McStas Readout Master 0.8.0
Loading...
Searching...
No Matches
reader.h
1#pragma once
2
3#include <algorithm>
4#include <cstdint>
5#include <cstring>
6#include <map>
7#include <memory>
8#include <optional>
9#include <set>
10#include <sstream>
11#include <stdexcept>
12#include <string>
13#include <vector>
14
15#include "ReadoutClass.h"
16#include "CollectorClass.h"
17
18
19#ifdef WIN32
20// Export symbols if compile flags "READOUT_SHARED" and "READOUT_EXPORT" are set on Windows.
21 #ifdef READOUT_SHARED
22 #ifdef READOUT_EXPORT
23 #define RL_API __declspec(dllexport)
24 #else
25 #define RL_API __declspec(dllimport)
26 #endif
27 #else
28 // Disable definition if linking statically.
29 #define RL_API
30 #endif
31#else
32// Disable definition for non-Win32 systems.
33#define RL_API
34#endif
35
38 std::string name;
39 HighFive::DataType datatype;
40 std::optional<std::string> unit;
41 std::optional<std::string> description;
42};
43
50class Reader {
51 std::shared_ptr<HighFive::File> file_;
52 std::string group_name_;
53 std::optional<HighFive::Group> group_;
54 std::optional<HighFive::DataSet> readouts_, cues_, weights_, normalizations_;
55 std::vector<uint32_t> cue_values_;
56 std::vector<uint64_t> normalization_values_;
57 std::optional<DetectorType> detector_{std::nullopt};
58 std::optional<ReadoutType> sendable_{std::nullopt};
59 std::optional<std::string> description_{std::nullopt};
60 std::optional<std::string> efu_address_{std::nullopt};
61 std::optional<uint16_t> efu_port_{std::nullopt};
62
63 [[nodiscard]] std::pair<size_t, size_t> point_bounds(const size_t point) const {
64 if (point >= cue_values_.size()) {
65 throw std::runtime_error("Out of bounds point requested");
66 }
67 const auto end = static_cast<size_t>(cue_values_.at(point));
68 const auto start = point > 0 ? static_cast<size_t>(cue_values_.at(point - 1)) : 0u;
69 if (end < start || end > size()) {
70 throw std::runtime_error("Invalid cue values in collector group");
71 }
72 return {start, end};
73 }
74
75public:
76 RL_API explicit Reader(std::shared_ptr<HighFive::File> file, const std::string & collector_name): file_{std::move(file)}, group_name_{collector_name} {
77 if (!file_) {
78 throw std::runtime_error("Reader requires an open file");
79 }
80 if (!file_->exist(group_name_)) {
81 std::stringstream s;
82 s << "Collector group \"" << group_name_ << "\" not found in file";
83 throw std::runtime_error(s.str());
84 }
85 group_ = file_->getGroup(group_name_);
86 if (const auto result = validate_collector_group(group_.value()); !result.empty()) {
87 std::stringstream s;
88 s << "Collector group \"" << group_name_ << "\" is invalid: " << result;
89 throw std::runtime_error(s.str());
90 }
91
92 readouts_ = group_->getDataSet(CollectorSink::readout_dataset_name());
93 cues_ = group_->getDataSet(CollectorSink::cue_dataset_name());
94 weights_ = group_->getDataSet(CollectorSink::weight_dataset_name());
95 normalizations_ = group_->getDataSet(CollectorSink::normalization_dataset_name());
96
97 // The detector identity (the ESS packet-type byte at replay) is a group
98 // attribute, next to the EFU routing attributes; absent for user-described
99 // record groups with no EFU destiny. The record layout has no attribute at
100 // all: it is the dataset's own compound datatype.
101 if (group_->hasAttribute(CollectorSink::detector_attribute_name())) {
102 detector_ = detectorType_from_name(group_->getAttribute(CollectorSink::detector_attribute_name()).read<std::string>());
103 }
104 if (readouts_->hasAttribute(CollectorSink::parameter_description_attribute_name())) {
105 description_ = readouts_->getAttribute(CollectorSink::parameter_description_attribute_name()).read<std::string>();
106 }
107 // EFU-sendability is decided by the stored datatype matching a registry compound type,
108 // not by the attributes: an attribute cannot lie about the record layout
109 const auto stored = readouts_->getDataType();
110 for (const auto rt : {ReadoutType::CAEN, ReadoutType::CDT, ReadoutType::VMM3,
111 ReadoutType::BM0, ReadoutType::BM2, ReadoutType::BMI}) {
112 if (stored == hdf_compound_type(rt)) {
113 sendable_ = rt;
114 break;
115 }
116 }
117
118 if (group_->hasAttribute(CollectorSink::efu_address_attribute_name())) {
119 efu_address_ = group_->getAttribute(CollectorSink::efu_address_attribute_name()).read<std::string>();
120 }
121 if (group_->hasAttribute(CollectorSink::efu_port_attribute_name())) {
122 const auto port = group_->getAttribute(CollectorSink::efu_port_attribute_name()).read<int>();
123 if (port > 0 && port <= 65535) {
124 efu_port_ = static_cast<uint16_t>(port);
125 }
126 }
127
128 cues_->read(cue_values_);
129 if (cue_values_.empty()) {
130 throw std::runtime_error("Collector cues dataset is empty");
131 }
132 if (!std::ranges::is_sorted(cue_values_)) {
133 throw std::runtime_error("Collector cues dataset must be monotonically non-decreasing");
134 }
135 if (cue_values_.back() != size()) {
136 throw std::runtime_error("Collector cues end value does not match readout dataset size");
137 }
138 normalizations_->read(normalization_values_);
139 if (weights_->getDimensions().back() != cue_values_.size() || normalization_values_.size() != cue_values_.size()) {
140 throw std::runtime_error("Collector point datasets (cues, weights, normalizations) have mismatched dimensions");
141 }
142 }
143
144 RL_API ~Reader() = default;
145
146 RL_API [[nodiscard]] const std::string & collector_name() const { return group_name_; }
148 RL_API [[nodiscard]] DetectorType detector_type() const { return detector_.value_or(DetectorType::Reserved); }
150 RL_API [[nodiscard]] ReadoutType readout_type() const {
151 if (sendable_.has_value()) { return sendable_.value(); }
152 throw std::runtime_error("Collector group has no readout type: it stores user-described records");
153 }
156 RL_API [[nodiscard]] std::optional<ReadoutType> sendable_readout_type() const { return sendable_; }
158 RL_API [[nodiscard]] const std::optional<std::string> & type_description() const { return description_; }
159 RL_API [[nodiscard]] size_t size() const { return readouts_->getDimensions().back(); }
160 RL_API [[nodiscard]] size_t points() const { return cue_values_.size(); }
161 RL_API [[nodiscard]] HighFive::DataType datatype() const { return readouts_->getDataType(); }
162 RL_API [[nodiscard]] size_t record_size() const { return readouts_->getDataType().getSize(); }
163
166 RL_API [[nodiscard]] double point_weight(const size_t point) const {
167 if (point >= cue_values_.size()) {
168 throw std::runtime_error("Out of bounds point requested");
169 }
170 return weights_->select({point}, {1}).read<double>();
171 }
172
175 RL_API [[nodiscard]] uint64_t point_normalization(const size_t point) const {
176 if (point >= normalization_values_.size()) {
177 throw std::runtime_error("Out of bounds point requested");
178 }
179 return normalization_values_[point];
180 }
181
184 RL_API [[nodiscard]] std::vector<uint8_t> get_raw(const size_t index, const size_t count) const {
185 if (index >= size() || index + count > size()) { throw std::runtime_error("Out of bounds record requested"); }
186 const auto datatype = readouts_->getDataType();
187 std::vector<uint8_t> buffer(count * datatype.getSize());
188 readouts_->select({index}, {count}).read_raw(buffer.data(), datatype);
189 return buffer;
190 }
191
193 RL_API [[nodiscard]] std::optional<std::string> efu_address() const { return efu_address_; }
195 RL_API [[nodiscard]] std::optional<uint16_t> efu_port() const { return efu_port_; }
196
197 RL_API [[nodiscard]] size_t point_offset(const size_t point) const {
198 return point_bounds(point).first;
199 }
200 RL_API [[nodiscard]] size_t point_size(const size_t point) const {
201 const auto [start, end] = point_bounds(point);
202 return end - start;
203 }
204
205 RL_API auto get_CAEN(const size_t index, const size_t count) const {
206 if (sendable_ != ReadoutType::CAEN){ throw std::runtime_error("Non CAEN readout type"); }
207 if (index >= size() || index + count > size()) { throw std::runtime_error("Out of bounds event requested");}
208 std::vector<CAEN_event> event(count);
209 const auto datatype = readouts_->getDataType();
210 readouts_->select({index}, {count}).read_raw(event.data(), datatype);
211 return event;
212 }
213 RL_API auto get_VMM3(const size_t index, const size_t count) const{
214 if (sendable_ != ReadoutType::VMM3) { throw std::runtime_error("Non VMM3 readout type"); }
215 if (index >= size() || index + count > size()) { throw std::runtime_error("Out of bounds event requested");}
216 std::vector<VMM3_event> event(count);
217 const auto datatype = readouts_->getDataType();
218 readouts_->select({index}, {count}).read_raw(event.data(), datatype);
219 return event;
220 }
221 RL_API auto get_CDT(const size_t index, const size_t count) const{
222 if (sendable_ != ReadoutType::CDT) { throw std::runtime_error("Non CDT readout type"); }
223 if (index >= size() || index + count > size()) { throw std::runtime_error("Out of bounds event requested");}
224 std::vector<CDT_event> event(count);
225 const auto datatype = readouts_->getDataType();
226 readouts_->select({index}, {count}).read_raw(event.data(), datatype);
227 return event;
228 }
229 RL_API auto get_BM0(const size_t index, const size_t count) const{
230 if (sendable_ != ReadoutType::BM0) { throw std::runtime_error("Non BM0 readout type"); }
231 if (index >= size() || index + count > size()) { throw std::runtime_error("Out of bounds event requested");}
232 std::vector<BM0_event> event(count);
233 const auto datatype = readouts_->getDataType();
234 readouts_->select({index}, {count}).read_raw(event.data(), datatype);
235 return event;
236 }
237 RL_API auto get_BM2(const size_t index, const size_t count) const{
238 if (sendable_ != ReadoutType::BM2) { throw std::runtime_error("Non BM2 readout type"); }
239 if (index >= size() || index + count > size()) { throw std::runtime_error("Out of bounds event requested");}
240 std::vector<BM2_event> event(count);
241 const auto datatype = readouts_->getDataType();
242 readouts_->select({index}, {count}).read_raw(event.data(), datatype);
243 return event;
244 }
245 RL_API auto get_BMI(const size_t index, const size_t count) const {
246 if (sendable_ != ReadoutType::BMI) { throw std::runtime_error("Non BMI readout type"); }
247 if (index >= size() || index + count > size()) { throw std::runtime_error("Out of bounds event requested");}
248 std::vector<BMI_event> event(count);
249 const auto datatype = readouts_->getDataType();
250 readouts_->select({index}, {count}).read_raw(event.data(), datatype);
251 return event;
252 }
253
254 RL_API auto get_point_CAEN(const size_t point) const {
255 const auto [start, end] = point_bounds(point);
256 return get_CAEN(start, end - start);
257 }
258 RL_API auto get_point_VMM3(const size_t point) const {
259 const auto [start, end] = point_bounds(point);
260 return get_VMM3(start, end - start);
261 }
262 RL_API auto get_point_CDT(const size_t point) const {
263 const auto [start, end] = point_bounds(point);
264 return get_CDT(start, end - start);
265 }
266 RL_API auto get_point_BM0(const size_t point) const {
267 const auto [start, end] = point_bounds(point);
268 return get_BM0(start, end - start);
269 }
270 RL_API auto get_point_BM2(const size_t point) const {
271 const auto [start, end] = point_bounds(point);
272 return get_BM2(start, end - start);
273 }
274 RL_API auto get_point_BMI(const size_t point) const {
275 const auto [start, end] = point_bounds(point);
276 return get_BMI(start, end - start);
277 }
278};
279
286 std::string filename_;
287 std::shared_ptr<HighFive::File> file_;
288 std::optional<HighFive::Group> parameters_;
289 std::vector<Reader> readers_;
290 std::vector<std::string> parameter_names_;
291 std::vector<ParameterDatasetView> parameter_views_;
292 size_t points_{0};
293
294 static bool is_parameter_group(const HighFive::Group & group) {
295 return group.hasAttribute(CollectorSink::type_attribute())
296 && group.getAttribute(CollectorSink::type_attribute()).read<std::string>() == CollectorSink::parameter_group_type();
297 }
298
299 static bool is_collector_group(const HighFive::Group & group) {
300 return group.hasAttribute(CollectorSink::type_attribute())
301 && group.getAttribute(CollectorSink::type_attribute()).read<std::string>() == CollectorSink::collector_group_type();
302 }
303
304public:
307 RL_API explicit ReaderSource(const std::string& filename, const bool warn_build = true): filename_{filename} {
308 try {
309 file_ = std::make_shared<HighFive::File>(filename, HighFive::File::ReadOnly);
310 } catch (HighFive::Exception & ex) {
311 std::stringstream s;
312 s << "Error opening file " << filename << ":\n" << ex.what();
313 throw std::runtime_error(s.str());
314 }
315
316 std::string program;
317 if (file_->hasAttribute(CollectorSink::program_attribute_name())) {
318 file_->getAttribute(CollectorSink::program_attribute_name()).read(program);
319 }
320 if (program != CollectorSink::program_attribute_value()) {
321 throw std::runtime_error("The provided HDF file was not produced using libreadout Collector output");
322 }
323 if (!file_->hasAttribute(CollectorSink::version_attribute_name()) || !file_->hasAttribute(CollectorSink::revision_attribute_name())) {
324 throw std::runtime_error("Collector file is missing required version attributes");
325 }
326
327 if (warn_build) {
328 warn_about_another_build(*file_, filename);
329 }
330
331 const auto root = file_->getGroup("/");
332 std::vector<std::string> collector_names;
333 for (size_t i=0; i<root.getNumberObjects(); ++i) {
334 const auto name = root.getObjectName(i);
335 if (root.getObjectType(name) != HighFive::ObjectType::Group) {
336 continue;
337 }
338 const auto group = root.getGroup(name);
339 if (name == CollectorSink::parameter_group_name() || is_parameter_group(group)) {
340 parameters_ = group;
341 } else if (is_collector_group(group)) {
342 collector_names.push_back(name);
343 }
344 }
345
346 if (collector_names.empty()) {
347 throw std::runtime_error("Collector file has no collector groups. Legacy flat readout files are not supported.");
348 }
349 std::ranges::sort(collector_names);
350 readers_.reserve(collector_names.size());
351 for (const auto & name : collector_names) {
352 readers_.emplace_back(file_, name);
353 }
354
355 points_ = readers_.front().points();
356 for (const auto & reader : readers_) {
357 if (reader.points() != points_) {
358 throw std::runtime_error("Collector groups have inconsistent point counts");
359 }
360 }
361
362 if (parameters_.has_value()) {
363 for (size_t i=0; i<parameters_->getNumberObjects(); ++i) {
364 const auto name = parameters_->getObjectName(i);
365 if (parameters_->getObjectType(name) != HighFive::ObjectType::Dataset) {
366 continue;
367 }
368 const auto ds = parameters_->getDataSet(name);
369 if (const auto dims = ds.getDimensions(); dims.size() != 1u || dims.back() != points_) {
370 throw std::runtime_error("Parameter datasets must be 1-D and match collector point count");
371 }
372 parameter_names_.push_back(name);
373 ParameterDatasetView view{name, ds.getDataType(), std::nullopt, std::nullopt};
374 if (ds.hasAttribute(CollectorSink::parameter_unit_attribute_name())) {
375 view.unit = ds.getAttribute(CollectorSink::parameter_unit_attribute_name()).read<std::string>();
376 }
377 if (ds.hasAttribute(CollectorSink::parameter_description_attribute_name())) {
378 view.description = ds.getAttribute(CollectorSink::parameter_description_attribute_name()).read<std::string>();
379 }
380 parameter_views_.push_back(std::move(view));
381 }
382 std::ranges::sort(parameter_names_);
383 std::ranges::sort(parameter_views_, [](const auto & a, const auto & b){ return a.name < b.name; });
384 }
385 }
386
387 RL_API [[nodiscard]] size_t points() const { return points_; }
388 RL_API [[nodiscard]] const std::vector<Reader> & readers() const { return readers_; }
389 RL_API [[nodiscard]] bool has_parameters() const { return parameters_.has_value(); }
390 RL_API [[nodiscard]] const std::vector<std::string> & parameter_names() const { return parameter_names_; }
391 RL_API [[nodiscard]] const std::vector<ParameterDatasetView> & parameter_views() const { return parameter_views_; }
392
393 RL_API [[nodiscard]] const Reader & reader(const std::string & collector_name) const {
394 for (const auto & reader : readers_) {
395 if (reader.collector_name() == collector_name) {
396 return reader;
397 }
398 }
399 throw std::runtime_error("Requested collector reader does not exist");
400 }
401
402 RL_API [[nodiscard]] std::vector<uint8_t> parameter_value(const std::string & name, const size_t point) const {
403 if (!parameters_.has_value()) {
404 throw std::runtime_error("Collector file does not contain a parameters group");
405 }
406 if (point >= points_) {
407 throw std::runtime_error("Out of bounds parameter point requested");
408 }
409 if (!parameters_->exist(name) || parameters_->getObjectType(name) != HighFive::ObjectType::Dataset) {
410 throw std::runtime_error("Requested parameter dataset does not exist");
411 }
412 const auto ds = parameters_->getDataSet(name);
413 const auto type = ds.getDataType();
414 std::vector<uint8_t> buffer(type.getSize());
415 ds.select({point}, {1}).read_raw(buffer.data(), type);
416 return buffer;
417 }
418
419 RL_API [[nodiscard]] bool parameter_is_double(const std::string & name) const {
420 for (const auto & parameter_view : parameter_views_) {
421 if (parameter_view.name == name) {
422 return parameter_view.datatype == HighFive::AtomicType<double>();
423 }
424 }
425 return false;
426 }
427
428 RL_API [[nodiscard]] double parameter_double_value(const std::string & name, const size_t point) const {
429 if (!parameters_.has_value()) {
430 throw std::runtime_error("Collector file does not contain a parameters group");
431 }
432 if (point >= points_) {
433 throw std::runtime_error("Out of bounds parameter point requested");
434 }
435 if (!parameters_->exist(name) || parameters_->getObjectType(name) != HighFive::ObjectType::Dataset) {
436 throw std::runtime_error("Requested parameter dataset does not exist");
437 }
438 const auto ds = parameters_->getDataSet(name);
439 const auto type = ds.getDataType();
440 if (type != HighFive::AtomicType<double>()) {
441 throw std::runtime_error("Requested parameter dataset does not have type double");
442 }
443 return ds.select({point}, {1}).read<double>();
444 }
445
446 RL_API [[nodiscard]] bool parameter_is_float(const std::string & name) const {
447 for (const auto & parameter_view : parameter_views_) {
448 if (parameter_view.name == name) {
449 return parameter_view.datatype == HighFive::AtomicType<float>();
450 }
451 }
452 return false;
453 }
454 RL_API [[nodiscard]] float parameter_float_value(const std::string & name, const size_t point) const {
455 if (!parameters_.has_value()) {
456 throw std::runtime_error("Collector file does not contain a parameters group");
457 }
458 if (point >= points_) {
459 throw std::runtime_error("Out of bounds parameter point requested");
460 }
461 if (!parameters_->exist(name) || parameters_->getObjectType(name) != HighFive::ObjectType::Dataset) {
462 throw std::runtime_error("Requested parameter dataset does not exist");
463 }
464 const auto ds = parameters_->getDataSet(name);
465 const auto type = ds.getDataType();
466 if (type != HighFive::AtomicType<float>()) {
467 throw std::runtime_error("Requested parameter dataset does not have type float");
468 }
469 return ds.select({point}, {1}).read<float>();
470 }
471
472
473 RL_API [[nodiscard]] bool parameter_is_char(const std::string & name) const {
474 for (const auto & parameter_view : parameter_views_) {
475 if (parameter_view.name == name) {
476 return parameter_view.datatype == HighFive::AtomicType<char>();
477 }
478 }
479 return false;
480 }
481 RL_API [[nodiscard]] char parameter_char_value(const std::string & name, const size_t point) const {
482 if (!parameters_.has_value()) {
483 throw std::runtime_error("Collector file does not contain a parameters group");
484 }
485 if (point >= points_) {
486 throw std::runtime_error("Out of bounds parameter point requested");
487 }
488 if (!parameters_->exist(name) || parameters_->getObjectType(name) != HighFive::ObjectType::Dataset) {
489 throw std::runtime_error("Requested parameter dataset does not exist");
490 }
491 const auto ds = parameters_->getDataSet(name);
492 const auto type = ds.getDataType();
493 if (type != HighFive::AtomicType<char>()) {
494 throw std::runtime_error("Requested parameter dataset does not have type char");
495 }
496 return ds.select({point}, {1}).read<char>();
497 }
498
499 RL_API [[nodiscard]] bool parameter_is_int(const std::string & name) const {
500 for (const auto & parameter_view : parameter_views_) {
501 if (parameter_view.name == name) {
502 return parameter_view.datatype == HighFive::AtomicType<int>();
503 }
504 }
505 return false;
506 }
507 RL_API [[nodiscard]] int parameter_int_value(const std::string & name, const size_t point) const {
508 if (!parameters_.has_value()) {
509 throw std::runtime_error("Collector file does not contain a parameters group");
510 }
511 if (point >= points_) {
512 throw std::runtime_error("Out of bounds parameter point requested");
513 }
514 if (!parameters_->exist(name) || parameters_->getObjectType(name) != HighFive::ObjectType::Dataset) {
515 throw std::runtime_error("Requested parameter dataset does not exist");
516 }
517 const auto ds = parameters_->getDataSet(name);
518 const auto type = ds.getDataType();
519 if (type != HighFive::AtomicType<int>()) {
520 throw std::runtime_error("Requested parameter dataset does not have type int");
521 }
522 return ds.select({point}, {1}).read<int>();
523 }
524
525 RL_API [[nodiscard]] bool parameter_is_int32(const std::string & name) const {
526 for (const auto & parameter_view : parameter_views_) {
527 if (parameter_view.name == name) {
528 return parameter_view.datatype == HighFive::AtomicType<int32_t>();
529 }
530 }
531 return false;
532 }
533 RL_API [[nodiscard]] int32_t parameter_int32_value(const std::string & name, const size_t point) const {
534 if (!parameters_.has_value()) {
535 throw std::runtime_error("Collector file does not contain a parameters group");
536 }
537 if (point >= points_) {
538 throw std::runtime_error("Out of bounds parameter point requested");
539 }
540 if (!parameters_->exist(name) || parameters_->getObjectType(name) != HighFive::ObjectType::Dataset) {
541 throw std::runtime_error("Requested parameter dataset does not exist");
542 }
543 const auto ds = parameters_->getDataSet(name);
544 const auto type = ds.getDataType();
545 if (type != HighFive::AtomicType<int32_t>()) {
546 throw std::runtime_error("Requested parameter dataset does not have type int32_t");
547 }
548 return ds.select({point}, {1}).read<int32_t>();
549 }
550
551 RL_API [[nodiscard]] bool parameter_is_int64(const std::string & name) const {
552 for (const auto & parameter_view : parameter_views_) {
553 if (parameter_view.name == name) {
554 return parameter_view.datatype == HighFive::AtomicType<int64_t>();
555 }
556 }
557 return false;
558 }
559 RL_API [[nodiscard]] int64_t parameter_int64_value(const std::string & name, const size_t point) const {
560 if (!parameters_.has_value()) {
561 throw std::runtime_error("Collector file does not contain a parameters group");
562 }
563 if (point >= points_) {
564 throw std::runtime_error("Out of bounds parameter point requested");
565 }
566 if (!parameters_->exist(name) || parameters_->getObjectType(name) != HighFive::ObjectType::Dataset) {
567 throw std::runtime_error("Requested parameter dataset does not exist");
568 }
569 const auto ds = parameters_->getDataSet(name);
570 const auto type = ds.getDataType();
571 if (type != HighFive::AtomicType<int64_t>()) {
572 throw std::runtime_error("Requested parameter dataset does not have type int64_t");
573 }
574 return ds.select({point}, {1}).read<int64_t>();
575 }
576
577 RL_API [[nodiscard]] bool parameter_is_uint(const std::string & name) const {
578 for (const auto & parameter_view : parameter_views_) {
579 if (parameter_view.name == name) {
580 return parameter_view.datatype == HighFive::AtomicType<unsigned int>();
581 }
582 }
583 return false;
584 }
585 RL_API [[nodiscard]] unsigned int parameter_uint_value(const std::string & name, const size_t point) const {
586 if (!parameters_.has_value()) {
587 throw std::runtime_error("Collector file does not contain a parameters group");
588 }
589 if (point >= points_) {
590 throw std::runtime_error("Out of bounds parameter point requested");
591 }
592 if (!parameters_->exist(name) || parameters_->getObjectType(name) != HighFive::ObjectType::Dataset) {
593 throw std::runtime_error("Requested parameter dataset does not exist");
594 }
595 const auto ds = parameters_->getDataSet(name);
596 const auto type = ds.getDataType();
597 if (type != HighFive::AtomicType<unsigned int>()) {
598 throw std::runtime_error("Requested parameter dataset does not have type unsigned int");
599 }
600 return ds.select({point}, {1}).read<unsigned int>();
601 }
602
603 RL_API [[nodiscard]] bool parameter_is_uint32(const std::string & name) const {
604 for (const auto & parameter_view : parameter_views_) {
605 if (parameter_view.name == name) {
606 return parameter_view.datatype == HighFive::AtomicType<uint32_t>();
607 }
608 }
609 return false;
610 }
611 RL_API [[nodiscard]] uint32_t parameter_uint32_value(const std::string & name, const size_t point) const {
612 if (!parameters_.has_value()) {
613 throw std::runtime_error("Collector file does not contain a parameters group");
614 }
615 if (point >= points_) {
616 throw std::runtime_error("Out of bounds parameter point requested");
617 }
618 if (!parameters_->exist(name) || parameters_->getObjectType(name) != HighFive::ObjectType::Dataset) {
619 throw std::runtime_error("Requested parameter dataset does not exist");
620 }
621 const auto ds = parameters_->getDataSet(name);
622 const auto type = ds.getDataType();
623 if (type != HighFive::AtomicType<uint32_t>()) {
624 throw std::runtime_error("Requested parameter dataset does not have type uint32_t");
625 }
626 return ds.select({point}, {1}).read<uint32_t>();
627 }
628
629 RL_API [[nodiscard]] bool parameter_is_uint64(const std::string & name) const {
630 for (const auto & parameter_view : parameter_views_) {
631 if (parameter_view.name == name) {
632 return parameter_view.datatype == HighFive::AtomicType<uint64_t>();
633 }
634 }
635 return false;
636 }
637 RL_API [[nodiscard]] uint64_t parameter_uint64_value(const std::string & name, const size_t point) const {
638 if (!parameters_.has_value()) {
639 throw std::runtime_error("Collector file does not contain a parameters group");
640 }
641 if (point >= points_) {
642 throw std::runtime_error("Out of bounds parameter point requested");
643 }
644 if (!parameters_->exist(name) || parameters_->getObjectType(name) != HighFive::ObjectType::Dataset) {
645 throw std::runtime_error("Requested parameter dataset does not exist");
646 }
647 const auto ds = parameters_->getDataSet(name);
648 const auto type = ds.getDataType();
649 if (type != HighFive::AtomicType<uint64_t>()) {
650 throw std::runtime_error("Requested parameter dataset does not have type uint64_t");
651 }
652 return ds.select({point}, {1}).read<uint64_t>();
653 }
654
655 RL_API [[nodiscard]] bool parameter_is_string(const std::string & name) const {
656 for (const auto & parameter_view : parameter_views_) {
657 if (parameter_view.name == name) {
658 return parameter_view.datatype.isFixedLenStr() || parameter_view.datatype.isVariableStr();
659 }
660 }
661 return false;
662 }
663 RL_API [[nodiscard]] std::string parameter_string_value(const std::string & name, const size_t point) const {
664 if (!parameters_.has_value()) {
665 throw std::runtime_error("Collector file does not contain a parameters group");
666 }
667 if (point >= points_) {
668 throw std::runtime_error("Out of bounds parameter point requested");
669 }
670 if (!parameters_->exist(name) || parameters_->getObjectType(name) != HighFive::ObjectType::Dataset) {
671 throw std::runtime_error("Requested parameter dataset does not exist");
672 }
673 const auto ds = parameters_->getDataSet(name);
674 const auto type = ds.getDataType();
675 if (!type.isFixedLenStr() && !type.isVariableStr()) {
676 throw std::runtime_error("Requested parameter dataset does not have a string type");
677 }
678 return ds.select({point}, {1}).read<std::string>();
679 }
680
681};
UDP readout collector class.
RL_API bool warn_about_another_build(const HighFive::File &file, const std::string &filename)
Warn on stderr if a collector file was written by another libreadout build.
UDP readout generator class.
static const std::string & detector_attribute_name()
The detector identity is a GROUP attribute, alongside the EFU routing attributes: together they say w...
Opens one collector file and discovers all collector groups and parameters.
Definition reader.h:285
RL_API ReaderSource(const std::string &filename, const bool warn_build=true)
Definition reader.h:307
Typed accessor for one collector group inside a validated collector file.
Definition reader.h:50
RL_API uint64_t point_normalization(const size_t point) const
The accumulated simulated-ray count of one point; grows when files are appended, so stored-weight / n...
Definition reader.h:175
RL_API DetectorType detector_type() const
The detector named by the group's attribute, or Reserved for user-described groups.
Definition reader.h:148
RL_API std::optional< uint16_t > efu_port() const
Returns the EFU UDP port embedded in the file, if any.
Definition reader.h:195
RL_API std::vector< uint8_t > get_raw(const size_t index, const size_t count) const
Raw access to stored records for user-described groups: count records starting at index,...
Definition reader.h:184
RL_API double point_weight(const size_t point) const
The accumulated rate-weight of one point, in stored (ncount-scaled) units: divide by point_normalizat...
Definition reader.h:166
RL_API std::optional< std::string > efu_address() const
Returns the EFU IP address embedded in the file, if any.
Definition reader.h:193
RL_API ReadoutType readout_type() const
The readout type implied by the stored compound datatype.
Definition reader.h:150
RL_API std::optional< ReadoutType > sendable_readout_type() const
The registry readout type whose compound datatype exactly matches the stored records,...
Definition reader.h:156
RL_API const std::optional< std::string > & type_description() const
The original type-description string for user-described groups, if recorded.
Definition reader.h:158
Metadata describing one dataset in the optional root "parameters" group.
Definition reader.h:37