LDMX Software
EventBuilder.cxx
1#include "EventBuilder/EventBuilder.h"
2
3#include <algorithm>
4#include <chrono>
5#include <cstdint>
6#include <cstring>
7#include <fstream>
8#include <iomanip>
9#include <iostream>
10#include <set>
11#include <vector>
12
13#include "EventBuilder/Event/GenericDataBlock.h"
14#include "EventBuilder/Event/PhysicsEventData.h"
15#include "EventBuilder/Fragment.h"
17#include "Framework/Logger.h"
18#include "Packing/LDMXRoRHeader.h"
19#include "Packing/RawDataFile/SubsystemPacket.h"
20#include "Packing/RogueFrameHeader.h"
21
22using namespace eventbuilder;
23
24// Global flag to track if performance CSV header has been written
25static bool g_perf_csv_header_written = false;
26
27// Helper function to write performance metrics to CSV
28void writePerformanceMetric(const std::string& path, unsigned int event_id,
29 double build_time_ms, double cum_events_per_sec,
30 double cum_mb_per_sec, double window_events_per_sec,
31 double window_mb_per_sec, uint64_t total_events,
32 uint64_t total_bytes) {
33 if (path.empty()) return;
34 std::ofstream perf_csv(path, std::ios::app);
35 if (!perf_csv) return;
36
37 // Write header on first call
38 if (!g_perf_csv_header_written) {
39 perf_csv
40 << "event_id,event_build_time_ms,cum_events_per_sec,cum_mb_per_sec,"
41 << "window_events_per_sec,window_mb_per_sec,total_events,total_bytes_"
42 "mb\n";
43 g_perf_csv_header_written = true;
44 }
45
46 // Write data with consistent formatting
47 double total_mb = total_bytes / (1024.0 * 1024.0);
48 perf_csv << event_id << "," << std::fixed << std::setprecision(3)
49 << build_time_ms << "," << std::fixed << std::setprecision(2)
50 << cum_events_per_sec << "," << std::fixed << std::setprecision(3)
51 << cum_mb_per_sec << "," << std::fixed << std::setprecision(2)
52 << window_events_per_sec << "," << std::fixed << std::setprecision(3)
53 << window_mb_per_sec << "," << total_events << "," << std::fixed
54 << std::setprecision(2) << total_mb << "\n";
55 perf_csv.flush();
56}
57
59 if (ps.exists("verbose_parse")) {
60 m_verbose_parse_ = ps.get<bool>("verbose_parse");
61 }
62 if (ps.exists("dat_file")) {
63 m_input_file_ = ps.get<std::string>("dat_file");
64 } else {
65 const char* env_input = std::getenv("EVENTBUILDER_INPUT");
66 if (env_input) m_input_file_ = env_input;
67 }
68 if (ps.exists("output_name"))
69 m_output_name_ = ps.get<std::string>("output_name");
70 if (ps.exists("coherence_window_ns")) {
71 m_coherence_window_ns_ = ps.get<double>("coherence_window_ns");
72 }
73 if (ps.exists("min_subsystems")) {
74 m_min_subsystems_ = ps.get<int>("min_subsystems");
75 }
76 if (ps.exists("binary_file")) {
77 m_binary_file_ = ps.get<std::string>("binary_file");
78 }
79 if (ps.exists("performance_file")) {
80 m_performance_file_ = ps.get<std::string>("performance_file");
81 }
82
83 ldmx_log(info) << "configure(): dat_file='" << m_input_file_
84 << "' output_name='" << m_output_name_
85 << "' verbose_parse=" << (m_verbose_parse_ ? "true" : "false")
86 << " coherence_window_ns=" << m_coherence_window_ns_
87 << " min_subsystems=" << m_min_subsystems_;
88
89 if (!m_input_file_.empty()) {
90 ldmx_log(info) << "configure(): opening file '" << m_input_file_ << "'";
91 m_reader_.open(m_input_file_);
92 if (!m_reader_) {
93 ldmx_log(error) << "failed to open input file: '" << m_input_file_ << "'";
94 } else {
95 ldmx_log(info) << "configure(): file opened successfully";
96 }
97 } else {
98 ldmx_log(error) << "no input file specified";
99 }
100
101 // Initialize performance tracking
102 m_start_time_ = std::chrono::steady_clock::now();
103 m_total_bytes_read_ = 0;
104 m_total_events_built_ = 0;
105 m_events_since_last_report_ = 0;
106
107 // Initialize windowed metrics for real-time DAQ monitoring
108 m_window_start_time_ = m_start_time_;
109 m_window_events_count_ = 0;
110 m_window_bytes_read_ = 0;
111}
112
114 static int produce_call_count = 0;
115 produce_call_count++;
116 ldmx_log(debug) << "produce() called, count=" << produce_call_count;
117 ldmx_log(debug) << "verbose_parse=" << (m_verbose_parse_ ? "true" : "false");
118 if (m_verbose_parse_ || produce_call_count <= 3) {
119 ldmx_log(info) << "produce() call #" << produce_call_count;
120 }
121
122 // Start timing for this event
123 m_event_start_time_ = std::chrono::steady_clock::now();
124
125 // Track errors encountered during event assembly
126 uint32_t current_event_errors = 0;
127
128 while (m_reader_ && !m_reader_.eof()) {
129 // Try to read a RogueFrameHeader
130 packing::RogueFrameHeader frame_header;
131 frame_header.read(m_reader_);
132
133 // Store location of end-of-frame for potential recovery
134 const long long frame_end =
135 static_cast<long long>(m_reader_.tell()) + frame_header.size();
136
137 // Check if this is a data frame (channel 0) and not YAML
138 if (frame_header.channel() != 0 || frame_header.probablyYaml()) {
139 // Skip this frame
140 if (m_verbose_parse_)
141 ldmx_log(debug) << "skipping non-data frame (channel="
142 << frame_header.channel() << ")";
143 m_reader_.seek(frame_end);
144 continue;
145 }
146
147 // We have a valid data frame - try to parse it
148 DataFragment fragment{{}, {}, {.checksum_ = 0}};
149 bool parsed_ok = false;
150
151 // Try RoR format first
152 packing::LDMXRoRHeader ror_header;
153 int pos_before_ror = m_reader_.tell();
154
155 // Attempt to read as RoR header
156 try {
157 ror_header.read(m_reader_);
158 // Successfully parsed RoR header
159 fragment.header_.subsystem_id_ =
160 static_cast<uint64_t>(ror_header.subsystem());
161 fragment.header_.contributor_id_ =
162 static_cast<uint64_t>(ror_header.contributor());
163 fragment.header_.timestamp_ = ror_header.timestamp();
164
165 if (m_verbose_parse_) {
166 ldmx_log(debug) << "parsed RoR header subsys="
167 << (int)ror_header.subsystem()
168 << " contrib=" << (int)ror_header.contributor()
169 << " ts=" << ror_header.timestamp();
170 }
171
172 // Read remaining frame payload after RoR header
173 std::vector<uint8_t> payload_data;
174 long long payload_size = frame_end - m_reader_.tell();
175 if (payload_size > 0) {
176 payload_data.resize(payload_size);
177 m_reader_.read(reinterpret_cast<char*>(payload_data.data()),
178 payload_size);
179 }
180 fragment.payload_ = std::move(payload_data);
181 parsed_ok = true;
182 } catch (...) {
183 // RoR parse failed, try packing subsystem format
184 if (m_verbose_parse_)
185 ldmx_log(debug)
186 << "RoR header parse failed, trying packing subsystem format";
187 current_event_errors |= ldmx::EventSummary::ERROR_PARSE_FAILURE;
188 m_reader_.seek(pos_before_ror);
189 }
190
191 if (!parsed_ok) {
192 // Try packing subsystem format
194 try {
195 pkt.read(m_reader_);
196 fragment.header_.subsystem_id_ = static_cast<uint64_t>(pkt.id());
197 fragment.header_.contributor_id_ =
198 0; // Not available in packing subsystem format
199 fragment.header_.timestamp_ = static_cast<uint64_t>(pkt.header()[1]) *
200 1000000000ULL; // event number to ns
201
202 // Convert packet data to payload bytes
203 const auto& data = pkt.data();
204 fragment.payload_.resize(data.size() * 4);
205 std::memcpy(fragment.payload_.data(),
206 reinterpret_cast<const char*>(data.data()),
207 data.size() * 4);
208
209 if (m_verbose_parse_) {
210 ldmx_log(debug) << "parsed packing subsystem pkt subsys=" << pkt.id()
211 << " data_size=" << data.size();
212 }
213 parsed_ok = true;
214 } catch (...) {
215 // Both parse attempts failed, skip this frame
216 if (m_verbose_parse_)
217 ldmx_log(debug) << "failed to parse as either format, skipping frame";
218 current_event_errors |= ldmx::EventSummary::ERROR_PARSE_FAILURE;
219 m_reader_.seek(frame_end);
220 continue;
221 }
222 }
223
224 if (!parsed_ok) {
225 m_reader_.seek(frame_end);
226 continue;
227 }
228
229 if (m_verbose_parse_)
230 ldmx_log(debug) << "adding fragment subsys="
231 << fragment.header_.subsystem_id_
232 << " ts=" << fragment.header_.timestamp_
233 << " bytes=" << fragment.payload_.size();
234
235 // Check if this fragment is outside the coherence window of the current
236 // event batch If so, it means we should finalize the previous event before
237 // adding this one
238 long long fragment_ts = fragment.header_.timestamp_;
239 long long buffer_ref_time = m_event_buffer_.getReferenceTime();
240
241 if (buffer_ref_time > 0 &&
242 (fragment_ts < buffer_ref_time - m_coherence_window_ns_ ||
243 fragment_ts > buffer_ref_time + m_coherence_window_ns_)) {
244 // This fragment is outside the current window - try to build the previous
245 // event
246 std::vector<DataFragment> assembled_event_fragments;
247 if (m_event_buffer_.tryBuildEvent(m_coherence_window_ns_,
248 m_min_subsystems_,
249 assembled_event_fragments) &&
250 !assembled_event_fragments.empty()) {
251 // Output the complete event and return
252 ++m_event_id_;
253 // Add each subsystem's raw data as vector<uint8_t> directly
254 uint64_t event_timestamp = 0;
255 for (const auto& frag : assembled_event_fragments) {
256 std::string subsys_name = packing::LDMXRoRHeader::getSubsystemName(
257 static_cast<uint8_t>(frag.header_.subsystem_id_),
258 static_cast<uint8_t>(frag.header_.contributor_id_));
259 std::vector<uint8_t> payload_bytes(frag.payload_.begin(),
260 frag.payload_.end());
261 event.add(subsys_name, payload_bytes);
262 if (event_timestamp == 0) {
263 event_timestamp = frag.header_.timestamp_;
264 }
265 }
266 event.getEventHeader().setIntParameter(
267 "RoR Timestamp", static_cast<long long>(event_timestamp));
268 event.getEventHeader().setRealData();
269 // Still create PhysicsEventData for binary output file
270 PhysicsEventData final_event =
271 assemblePayload(assembled_event_fragments);
272 writeEventBinary(final_event, m_binary_file_);
273 ldmx_log(info) << "assembled event id=" << m_event_id_
274 << " timestamp=" << final_event.timestamp_
275 << " fragments=" << assembled_event_fragments.size()
276 << " systems=" << final_event.systems_readout_.size();
277
278 ldmx::EventSummary summary;
279 summary.setEventNumber(m_event_id_);
280 summary.setTimestampNs(static_cast<uint64_t>(final_event.timestamp_));
281 std::set<uint64_t> unique_sys, unique_sources;
282 uint64_t total_payload = 0;
283 for (const auto& f : assembled_event_fragments) {
284 unique_sys.insert(f.header_.subsystem_id_);
285 // a subsystem may have several contributors, a duplicate is the same
286 // pair twice
287 unique_sources.insert(f.header_.subsystem_id_ << 8 |
288 f.header_.contributor_id_);
289 total_payload += f.payload_.size();
290 }
291 // Check for duplicate subsystems
292 if (unique_sources.size() != assembled_event_fragments.size()) {
293 current_event_errors |= ldmx::EventSummary::ERROR_DUPLICATE_SUBSYSTEM;
294 }
295 summary.setNSystems(static_cast<uint32_t>(unique_sys.size()));
296 summary.setSystemIds(
297 std::vector<uint64_t>(unique_sys.begin(), unique_sys.end()));
298 summary.setPayloadSize(total_payload);
299 summary.setErrorFlags(current_event_errors);
300 event.add("EventSummary", summary);
301
302 // Update performance metrics
303 m_total_bytes_read_ += total_payload;
304 m_total_events_built_++;
305 m_events_since_last_report_++;
306 std::chrono::steady_clock::time_point now =
307 std::chrono::steady_clock::now();
308 std::chrono::milliseconds event_build_time_ms =
309 std::chrono::duration_cast<std::chrono::milliseconds>(
310 now - m_event_start_time_);
311 std::chrono::seconds total_elapsed_s =
312 std::chrono::duration_cast<std::chrono::seconds>(now -
313 m_start_time_);
314
315 double events_per_sec =
316 (total_elapsed_s.count() > 0)
317 ? static_cast<double>(m_total_events_built_) /
318 total_elapsed_s.count()
319 : 0.0;
320 double mb_per_sec = (total_elapsed_s.count() > 0)
321 ? static_cast<double>(m_total_bytes_read_) /
322 (1024.0 * 1024.0) /
323 total_elapsed_s.count()
324 : 0.0;
325
326 // Update windowed metrics
327 m_window_events_count_++;
328 m_window_bytes_read_ += total_payload;
329 std::chrono::seconds window_elapsed_s =
330 std::chrono::duration_cast<std::chrono::seconds>(
331 now - m_window_start_time_);
332
333 // Reset window if we've processed WINDOW_SIZE events
334 double window_events_per_sec = 0.0;
335 double window_mb_per_sec = 0.0;
336 if (m_window_events_count_ >= WINDOW_SIZE) {
337 double window_time_sec =
338 std::max(1.0, static_cast<double>(window_elapsed_s.count()));
339 window_events_per_sec =
340 static_cast<double>(m_window_events_count_) / window_time_sec;
341 window_mb_per_sec = static_cast<double>(m_window_bytes_read_) /
342 (1024.0 * 1024.0) / window_time_sec;
343
344 // Reset window
345 m_window_start_time_ = now;
346 m_window_events_count_ = 0;
347 m_window_bytes_read_ = 0;
348 }
349
350 // Write performance metrics to CSV
351 long long event_build_time_ms_val = event_build_time_ms.count();
352 writePerformanceMetric(m_performance_file_, m_event_id_,
353 static_cast<double>(event_build_time_ms_val),
354 events_per_sec, mb_per_sec,
355 window_events_per_sec, window_mb_per_sec,
356 m_total_events_built_, m_total_bytes_read_);
357
358 if (m_verbose_parse_ || m_events_since_last_report_ % 100 == 0) {
359 ldmx_log(info) << "Performance: "
360 << "total_events=" << m_total_events_built_ << ", "
361 << "events_per_sec=" << std::fixed
362 << std::setprecision(2) << events_per_sec << ", "
363 << "mb_per_sec=" << std::fixed << std::setprecision(3)
364 << mb_per_sec;
365 }
366
367 current_event_errors = 0; // Reset for next event
368 // The fragment that tripped the window-close belongs to the NEXT
369 // event, not the one we just emitted. Buffer it here (the buffer is
370 // empty post-build, so this also resets the reference time) instead
371 // of dropping it -- otherwise every event after the first loses its
372 // triggering subsystem.
373 m_event_buffer_.addFragment(std::move(fragment));
374 return; // Return the event to framework
375 }
376 }
377
378 // Add fragment to buffer (may start a new event batch if buffer was empty)
379 m_event_buffer_.addFragment(std::move(fragment));
380
381 if (m_verbose_parse_)
382 ldmx_log(debug) << "frame added to buffer, searching for more frames...";
383 }
384
385 // Reached EOF - flush any remaining events in the buffer
386 if (m_verbose_parse_)
387 ldmx_log(debug) << "reached EOF, flushing remaining events";
388
389 std::vector<DataFragment> assembled_event_fragments;
390 while (m_event_buffer_.tryBuildEvent(
391 m_coherence_window_ns_, m_min_subsystems_, assembled_event_fragments)) {
392 if (assembled_event_fragments.empty()) break;
393
394 // Mark truncated events (those flushed at EOF)
395 current_event_errors |= ldmx::EventSummary::ERROR_TRUNCATED_EVENT;
396
397 ++m_event_id_;
398 // Add each subsystem's raw data as vector<uint8_t> directly
399 uint64_t event_timestamp = 0;
400 for (const auto& frag : assembled_event_fragments) {
401 std::string subsys_name = packing::LDMXRoRHeader::getSubsystemName(
402 static_cast<uint8_t>(frag.header_.subsystem_id_),
403 static_cast<uint8_t>(frag.header_.contributor_id_));
404 std::vector<uint8_t> payload_bytes(frag.payload_.begin(),
405 frag.payload_.end());
406 event.add(subsys_name, payload_bytes);
407 if (event_timestamp == 0) {
408 event_timestamp = frag.header_.timestamp_;
409 }
410 }
411 event.getEventHeader().setIntParameter(
412 "RoR Timestamp", static_cast<long long>(event_timestamp));
413 event.getEventHeader().setRealData();
414 // Still create PhysicsEventData for binary output file
415 PhysicsEventData final_event = assemblePayload(assembled_event_fragments);
416 writeEventBinary(final_event, m_binary_file_);
417 ldmx_log(info) << "assembled event id=" << m_event_id_
418 << " timestamp=" << final_event.timestamp_
419 << " fragments=" << assembled_event_fragments.size()
420 << " systems=" << final_event.systems_readout_.size();
421
422 ldmx::EventSummary summary;
423 summary.setEventNumber(m_event_id_);
424 summary.setTimestampNs(static_cast<uint64_t>(final_event.timestamp_));
425 std::set<uint64_t> unique_sys, unique_sources;
426 uint64_t total_payload = 0;
427 for (const auto& f : assembled_event_fragments) {
428 unique_sys.insert(f.header_.subsystem_id_);
429 // a subsystem may have several contributors, a duplicate is the same pair
430 // twice
431 unique_sources.insert(f.header_.subsystem_id_ << 8 |
432 f.header_.contributor_id_);
433 total_payload += f.payload_.size();
434 }
435 // Check for duplicate subsystems
436 if (unique_sources.size() != assembled_event_fragments.size()) {
437 current_event_errors |= ldmx::EventSummary::ERROR_DUPLICATE_SUBSYSTEM;
438 }
439 summary.setNSystems(static_cast<uint32_t>(unique_sys.size()));
440 summary.setSystemIds(
441 std::vector<uint64_t>(unique_sys.begin(), unique_sys.end()));
442 summary.setPayloadSize(total_payload);
443 summary.setErrorFlags(current_event_errors);
444 event.add("EventSummary", summary);
445
446 // Update performance metrics
447 m_total_bytes_read_ += total_payload;
448 m_total_events_built_++;
449 m_events_since_last_report_++;
450 std::chrono::steady_clock::time_point now =
451 std::chrono::steady_clock::now();
452 std::chrono::milliseconds event_build_time_ms =
453 std::chrono::duration_cast<std::chrono::milliseconds>(
454 now - m_event_start_time_);
455 std::chrono::seconds total_elapsed_s =
456 std::chrono::duration_cast<std::chrono::seconds>(now - m_start_time_);
457
458 double events_per_sec = (total_elapsed_s.count() > 0)
459 ? static_cast<double>(m_total_events_built_) /
460 total_elapsed_s.count()
461 : 0.0;
462 double mb_per_sec = (total_elapsed_s.count() > 0)
463 ? static_cast<double>(m_total_bytes_read_) /
464 (1024.0 * 1024.0) / total_elapsed_s.count()
465 : 0.0;
466
467 // Update windowed metrics
468 m_window_events_count_++;
469 m_window_bytes_read_ += total_payload;
470 std::chrono::seconds window_elapsed_s =
471 std::chrono::duration_cast<std::chrono::seconds>(now -
472 m_window_start_time_);
473
474 // Calculate window metrics (for final event, may not have full window)
475 double window_events_per_sec = 0.0;
476 double window_mb_per_sec = 0.0;
477 if (m_window_events_count_ >= WINDOW_SIZE || window_elapsed_s.count() > 0) {
478 double window_time_sec =
479 std::max(1.0, static_cast<double>(window_elapsed_s.count()));
480 window_events_per_sec =
481 static_cast<double>(m_window_events_count_) / window_time_sec;
482 window_mb_per_sec = static_cast<double>(m_window_bytes_read_) /
483 (1024.0 * 1024.0) / window_time_sec;
484 }
485
486 // Write performance metrics to CSV
487 long long event_build_time_ms_val = event_build_time_ms.count();
488 writePerformanceMetric(m_performance_file_, m_event_id_,
489 static_cast<double>(event_build_time_ms_val),
490 events_per_sec, mb_per_sec, window_events_per_sec,
491 window_mb_per_sec, m_total_events_built_,
492 m_total_bytes_read_);
493
494 if (m_verbose_parse_ || m_events_since_last_report_ % 100 == 0) {
495 ldmx_log(info) << "Performance: "
496 << "total_events=" << m_total_events_built_ << ", "
497 << "events_per_sec=" << std::fixed << std::setprecision(2)
498 << events_per_sec << ", "
499 << "mb_per_sec=" << std::fixed << std::setprecision(3)
500 << mb_per_sec;
501 }
502
503 current_event_errors = 0; // Reset for next event
504
505 assembled_event_fragments.clear();
506 return;
507 }
508
509 // No more events - print final summary statistics
510 std::chrono::steady_clock::time_point final_time =
511 std::chrono::steady_clock::now();
512 std::chrono::seconds total_time_s =
513 std::chrono::duration_cast<std::chrono::seconds>(final_time -
514 m_start_time_);
515 double final_events_per_sec =
516 (total_time_s.count() > 0)
517 ? static_cast<double>(m_total_events_built_) / total_time_s.count()
518 : 0.0;
519 double final_mb_per_sec = (total_time_s.count() > 0)
520 ? static_cast<double>(m_total_bytes_read_) /
521 (1024.0 * 1024.0) / total_time_s.count()
522 : 0.0;
523
524 ldmx_log(info) << "\n===== FINAL STATISTICS =====";
525 ldmx_log(info) << "Total events built: " << m_total_events_built_;
526 ldmx_log(info) << "Total bytes read: "
527 << m_total_bytes_read_ / (1024.0 * 1024.0) << " MB";
528 ldmx_log(info) << "Total time: " << total_time_s.count() << " seconds";
529 ldmx_log(info) << "Average throughput: "
530 << "events_per_sec=" << std::fixed << std::setprecision(2)
531 << final_events_per_sec << ", "
532 << "mb_per_sec=" << std::fixed << std::setprecision(3)
533 << final_mb_per_sec;
534 ldmx_log(info) << "=============================\n";
535 ldmx_log(info) << "Event building complete";
536
537 abortEvent();
538}
539
540PhysicsEventData EventBuilder::assemblePayload(
541 const std::vector<DataFragment>& fragments) {
542 PhysicsEventData event_data;
543 if (fragments.empty()) return event_data;
544 event_data.event_id_ = m_event_id_;
545 event_data.timestamp_ = fragments.front().header_.timestamp_;
546 for (const auto& fragment : fragments) {
548 g.subsystem_id_ = fragment.header_.subsystem_id_;
549 g.timestamp_ns_ = fragment.header_.timestamp_;
550 g.data_ = fragment.payload_;
551 if (fragment.trailer_.checksum_ != 0)
552 g.checksum_ = fragment.trailer_.checksum_;
553 else
554 g.checksum_ = crc32(fragment.payload_);
555 event_data.blocks_.push_back(std::move(g));
556 event_data.systems_readout_.push_back(fragment.header_.subsystem_id_);
557 }
558 return event_data;
559}
560
561void EventBuilder::writeEventBinary(const PhysicsEventData& ev,
562 const std::string& path) {
563 static std::mutex g_out_mutex;
564 std::lock_guard<std::mutex> lg(g_out_mutex);
565 if (path.empty()) return;
566 std::ofstream ofs(path, std::ios::binary | std::ios::app);
567 if (!ofs) return;
568 uint64_t event_id_u = static_cast<uint64_t>(ev.event_id_);
569 uint64_t ts = static_cast<uint64_t>(ev.timestamp_);
570 uint32_t nblocks = static_cast<uint32_t>(ev.blocks_.size());
571 ofs.write(reinterpret_cast<const char*>(&event_id_u), sizeof(event_id_u));
572 ofs.write(reinterpret_cast<const char*>(&ts), sizeof(ts));
573 ofs.write(reinterpret_cast<const char*>(&nblocks), sizeof(nblocks));
574 for (const auto& b : ev.blocks_) {
575 uint64_t sid = b.subsystem_id_;
576 uint64_t bts = b.timestamp_ns_;
577 uint32_t psz = static_cast<uint32_t>(b.data_.size());
578 uint32_t csum = b.checksum_;
579 ofs.write(reinterpret_cast<const char*>(&sid), sizeof(sid));
580 ofs.write(reinterpret_cast<const char*>(&bts), sizeof(bts));
581 ofs.write(reinterpret_cast<const char*>(&psz), sizeof(psz));
582 ofs.write(reinterpret_cast<const char*>(&csum), sizeof(csum));
583 if (psz)
584 ofs.write(reinterpret_cast<const char*>(b.data_.data()),
585 static_cast<std::streamsize>(psz));
586 }
587 ofs.flush();
588}
589
590// Register producer with the framework factory
#define DECLARE_PRODUCER(CLASS)
Macro which allows the framework to construct a producer given its name during configuration.
Class that provides a summary of event assembly with metadata and error flags.
std::string m_performance_file_
performance metrics CSV, empty to disable
void produce(framework::Event &event) override
Process the event and put new data products into it.
void configure(framework::config::Parameters &ps) override
Callback for the EventProcessor to configure itself from the given set of parameters.
std::string m_binary_file_
binary event output, empty to disable
void abortEvent()
Abort the event immediately.
Implements an event buffer system for storing event data.
Definition Event.h:40
Class encapsulating parameters for configuring a processor.
Definition Parameters.h:26
const T & get(const std::string &name) const
Retrieve the parameter of the given name.
Definition Parameters.h:75
bool exists(const std::string &name) const
Check to see if a parameter exists.
Definition Parameters.h:60
Provides summary information about an assembled event, including error flags for data quality assessm...
void setSystemIds(const std::vector< uint64_t > &ids)
Set the system IDs.
void setNSystems(uint32_t ns)
Set the number of systems in this event.
void setPayloadSize(uint64_t size)
Set the total payload size.
void setTimestampNs(uint64_t ts)
Set the timestamp in nanoseconds.
void setEventNumber(uint64_t num)
Set the event number.
void setErrorFlags(uint32_t flags)
Set the error flags.
@ ERROR_PARSE_FAILURE
Frame parsing error.
@ ERROR_DUPLICATE_SUBSYSTEM
Duplicate subsystem in single event.
@ ERROR_TRUNCATED_EVENT
Event was truncated (EOF reached)
the header that the LDMX DAQ Firmware block includes in the output data stream at the beginning of ea...
static std::tuple< int, int > subsystem(const std::string &name)
Get the (subsystem, contributor) pair for the input subsystem name.
uint8_t contributor() const
ID number for contributor within subsystem (configured into firmware)
static std::string getSubsystemName(uint8_t subsystem_id, uint8_t contributor_id)
Get the subsystem name from subsystem and contributor IDs.
uint64_t timestamp() const
get timestamp of this RoR
utility::Reader & read(utility::Reader &r)
read the next LDMX RoR header into memory
the header that the Rogue StreamWriter puts includes at the beginning of each frame.
unsigned int size() const
get the size of the frame not including this header
bool probablyYaml() const
check if this frame is probably a yaml dump
int channel() const
get the channel this data was written to
utility::Reader & read(utility::Reader &r)
read the next rogue frame header into memory
std::vector< uint32_t > & data()
Get data.
utility::Reader & read(utility::Reader &r)
read the subsystem packet from the input reader
std::vector< uint32_t > header() const
get the header words
std::streampos tell()
Tell us where the reader is.
Definition Reader.h:88
void open(const std::string &file_name)
Open a file with this reader.
Definition Reader.h:36
bool eof()
check if file is done
Definition Reader.h:217
void seek(std::streampos off, std::ios_base::seekdir dir=std::ios::beg)
Go ("seek") a specific position in the file.
Definition Reader.h:62
Reader & read(WordType *w, std::size_t count)
Read the next 'count' words into the input handle.
Definition Reader.h:114