Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 25 additions & 11 deletions gemc/gstreamer/factories/JSON/event/event.cc
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ bool GstreamerJsonFactory::startEventImpl(const std::shared_ptr<GEventDataCollec
current_event_has_header = false;
current_event_has_any_detector = false;
current_event_has_generated = false;
current_event_digitized_entries.clear();

// Cache the event number early so it is available to the root event object.
event_number = event_data->getHeader()->getG4LocalEvn();
Expand All @@ -47,24 +48,37 @@ bool GstreamerJsonFactory::endEventImpl(const std::shared_ptr<GEventDataCollecti
return false;
}

// If the caller skipped the header step, emit a minimal fallback so the JSON remains valid.
// If the caller skipped the header step, emit a minimal fallback and close the header
// object (startEventImpl opened it; publishEventHeaderImpl normally writes its fields
// and closes it).
if (!current_event_has_header) {
current_event << "\"timestamp\": \"\", \"thread_id\": -1";
current_event << "\"timestamp\": \"\", \"thread_id\": -1}";
}

// Detector publishers leave the "detectors" object open so digitized data
// can be appended after true-information banks without rewriting strings.
if (current_event_has_any_detector) {
current_event << "}";
// Ensure a "detectors" object exists so the schema stays predictable and any buffered
// digitized data has a place to live, even when no true-information banks were published.
bool detectors_has_content = current_event_has_any_detector;
if (!current_event_has_any_detector) {
current_event << ", \"detectors\": {";
current_event_has_any_detector = true;
}
current_event << "}";

// Keep the event schema predictable even when no detector banks were published.
if (!current_event_has_any_detector) {
current_event << ", \"detectors\": {}";
// Emit all buffered digitized detector arrays as a single, well-formed
// "digitized_by_detector" object nested inside "detectors".
if (!current_event_digitized_entries.empty()) {
if (detectors_has_content) { current_event << ", "; }
current_event << "\"digitized_by_detector\": {";
bool first = true;
for (const auto& entry : current_event_digitized_entries) {
if (!first) { current_event << ", "; }
first = false;
current_event << entry;
}
current_event << "}";
}

current_event << "}";
current_event << "}"; // close "detectors"
current_event << "}"; // close the event object

writeTopLevelEntry(current_event.str());

Expand Down
1 change: 1 addition & 0 deletions gemc/gstreamer/factories/JSON/event/eventHeader.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ bool GstreamerJsonFactory::publishEventHeaderImpl(const std::unique_ptr<GEventHe
current_event << "\"timestamp\": \"" << jsonEscape(timestamp) << "\""
<< ", \"thread_id\": " << thread_id
<< ", \"g4local_event\": " << gevent_header->getG4LocalEvn();
current_event << "}"; // close the "header" object opened in startEventImpl

current_event_has_header = true;
return true;
Expand Down
62 changes: 18 additions & 44 deletions gemc/gstreamer/factories/JSON/event/publishDigitized.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,80 +17,54 @@ bool GstreamerJsonFactory::publishEventDigitizedDataImpl(const std::string&
return false;
}

// The current design appends digitized content into a reserved object nested under
// "detectors". This avoids rewriting previously emitted detector JSON.
// Build this detector's digitized array into a standalone entry and buffer it.
// endEventImpl emits all buffered entries together as the "digitized_by_detector"
// object, which keeps the JSON valid regardless of how true-info and digitized
// publish calls interleave across detectors.
if (digitizedData.empty()) return true;

static const char* marker = "\"digitized_by_detector\": {";
const std::string assembled = current_event.str();

const bool has_digitized_container = (assembled.find(marker) != std::string::npos);

if (!has_digitized_container) {
// Ensure the event already has a detectors object before reserving a nested map
// for digitized collections keyed by detector name.
if (!current_event_has_any_detector) {
current_event << ", \"detectors\": {";
current_event_has_any_detector = true;
}
else {
current_event << ", ";
}

current_event << "\"digitized_by_detector\": {";
}

// If the reserved object already contains one detector entry, append a comma
// before adding the next one.
const std::string updated = current_event.str();
if (!updated.empty()) {
char last = updated.back();
if (last != '{') current_event << ", ";
}

current_event << "\"" << jsonEscape(detectorName) << "\": [";
std::ostringstream entry;
entry << "\"" << jsonEscape(detectorName) << "\": [";

bool wrote_first_hit = false;
for (const auto* hit : digitizedData) {
if (!hit) continue;

if (wrote_first_hit) current_event << ", ";
if (wrote_first_hit) entry << ", ";
wrote_first_hit = true;

current_event << "{";
entry << "{";

auto addr = getIdentityString(hit->getIdentity());

current_event << "\"address\": \"" << jsonEscape(addr) << "\"";
entry << "\"address\": \"" << jsonEscape(addr) << "\"";

current_event << ", \"vars\": {";
entry << ", \"vars\": {";

bool wrote_first_var = false;

// Integer observables:
// the argument 0 means "do not include SRO variables".
for (const auto& [name, value] : hit->getIntObservablesMap(0)) {
if (wrote_first_var) current_event << ", ";
if (wrote_first_var) entry << ", ";
wrote_first_var = true;
current_event << "\"" << jsonEscape(name) << "\": " << value;
entry << "\"" << jsonEscape(name) << "\": " << value;
}

// Floating-point observables.
for (const auto& [name, value] : hit->getDblObservablesMap(0)) {
if (wrote_first_var) current_event << ", ";
if (wrote_first_var) entry << ", ";
wrote_first_var = true;
current_event << "\"" << jsonEscape(name) << "\": " << value;
entry << "\"" << jsonEscape(name) << "\": " << value;
}

current_event << "}";
current_event << "}";
entry << "}";
entry << "}";
}

current_event << "]";
entry << "]";

// Close the temporary digitized_by_detector object immediately so the enclosing
// event remains structurally valid after this call.
current_event << "}";
current_event_digitized_entries.push_back(entry.str());

return true;
}
5 changes: 5 additions & 0 deletions gemc/gstreamer/factories/JSON/gstreamerJSONFactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,11 @@ class GstreamerJsonFactory : public GStreamer {
/// \brief Tracks whether the current event already contains generated-particle banks.
bool current_event_has_generated = false;

/// \brief Buffered digitized detector arrays for the current event. endEventImpl emits
/// them together as the "digitized_by_detector" object, which keeps the JSON valid
/// regardless of how true-info and digitized publish calls interleave across detectors.
std::vector<std::string> current_event_digitized_entries;

/// \brief Tracks whether the plugin is currently assembling a frame object.
bool is_building_frame = false;

Expand Down