// Copyright (2019-2023) Paul Scherrer Institute #include "JFJochBrokerParser.h" #include "JFJochBroker.h" inline bool CHECK_ARRAY(const nlohmann::json &j, const std::string& tag) { if (j.contains(tag)) { if (!j[tag].is_array()) throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, tag + " must be array"); return true; } else return false; } inline bool CHECK_OBJECT(const nlohmann::json &j, const std::string& tag) { if (j.contains(tag)) { if (!j[tag].is_object()) throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, tag + " must be object"); return true; } else return false; } inline int64_t GET_I64(const nlohmann::json &j, const std::string& tag, int64_t def) { if (j.contains(tag)) { if (!j[tag].is_number_integer()) throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, tag + " must be integer"); try { return j[tag].get(); } catch (std::exception &e) { throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, tag + ": " + e.what()); } } else return def; } inline bool GET_BOOL(const nlohmann::json &j, const std::string& tag, bool def) { if (j.contains(tag)) { if (!j[tag].is_boolean()) throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, tag + " must be integer"); try { return j[tag].get(); } catch (std::exception &e) { throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, tag + ": " + e.what()); } } else return def; } inline std::string GET_STR(const nlohmann::json &j, const std::string& tag) { if (j.contains(tag)) { if (!j[tag].is_string()) throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, tag + " must be string"); try { return j[tag].get(); } catch (std::exception &e) { throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, tag + ": " + e.what()); } } else throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, tag + " is compulsory"); } inline int64_t GET_I64(const nlohmann::json &j, const std::string& tag) { if (j.contains(tag)) { if (!j[tag].is_number_integer()) throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, tag + " must be integer"); try { return j[tag].get(); } catch (std::exception &e) { throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, tag + ": " + e.what()); } } else throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, tag + " is compulsory"); } inline std::vector GET_STR_ARR(const nlohmann::json &j, const std::string& tag) { std::vector ret; if (CHECK_ARRAY(j, tag)) { try { for (const auto &iter: j[tag]) ret.push_back(iter.get()); } catch (std::exception &e) { throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, tag + ": " + e.what()); } } return ret; } DetectorModuleGeometry::Direction GET_DIRECTION(const nlohmann::json &j, const std::string& tag) { if (j.contains(tag)) { if (!j[tag].is_string()) throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, tag + " must be string"); if (j[tag] == "Xp") return DetectorModuleGeometry::Direction::Xpos; if (j[tag] == "Xn") return DetectorModuleGeometry::Direction::Xneg; if (j[tag] == "Yp") return DetectorModuleGeometry::Direction::Ypos; if (j[tag] == "Yn") return DetectorModuleGeometry::Direction::Yneg; else throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Allowed values for direction: Xp, Xn, Yp, Yn"); } else throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, tag + " is compulsory"); } inline int64_t TimeToUs(const std::string &unit) { if ((unit == "us") || unit.empty()) return 1L; else if (unit == "ms") return 1000L; else if ((unit == "s") || (unit == "sec")) return 1000*1000L; else throw JFJochException(JFJochExceptionCategory::WrongUnit, "Only us, ms and s (or sec) are viable units for time"); } inline std::chrono::microseconds GET_TIME(const nlohmann::json &j, const std::string& tag) { if (j.contains(tag)) { if (j[tag].is_number()) return std::chrono::microseconds (std::lround(j[tag].get() * 1000.0 * 1000.0)); else if (j[tag].is_string()) { std::string::size_type pos; auto text = j[tag].get(); // Check if floating point pos = text.find_first_of('.'); if (pos != std::string::npos) throw JFJochException(JFJochExceptionCategory::WrongNumber, "Time must be provided as {s|ms|us|Hz} - no floating point allowed"); // Convert integer part int64_t val = std::stol(text, &pos); if (pos == 0) throw JFJochException(JFJochExceptionCategory::WrongNumber, "Time must be provided as {s|ms|us|Hz}"); pos = text.find_first_not_of(' ', pos); if (pos != std::string::npos) { if (text.substr(pos) == "Hz") val = 1000*1000L / val; else val *= TimeToUs(text.substr(pos)); } return std::chrono::microseconds(val); } else throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, tag + " must be number in seconds or string with time units s|ms|us"); } else throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, tag + " is compulsory"); } DetectorGeometry ParseStandardDetectorGeometry(const nlohmann::json &j) { int32_t nmodules = GET_I64(j, "nmodules"); int32_t gap_x = GET_I64(j, "gap_x", 8); int32_t gap_y = GET_I64(j, "gap_y", 36); int32_t h_stacking = GET_I64(j, "horizontal_stacking", 2); bool mirror_y = GET_BOOL(j, "mirror_y", true); return {nmodules, h_stacking, gap_x, gap_y, mirror_y}; } DetectorGeometry ParseCustomDetectorGeometry(const nlohmann::json &j) { std::vector modules; for (const auto &iter: j) { if (!iter.is_object()) throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "custom_geometry element must be JSON object"); int64_t x0 = GET_I64(iter, "x0"); int64_t y0 = GET_I64(iter, "y0"); auto fast = GET_DIRECTION(iter, "fast_axis"); auto slow = GET_DIRECTION(iter, "slow_axis"); modules.emplace_back(x0, y0, fast, slow); } return {modules}; } DetectorGeometry ParseDetectorGeometry(const nlohmann::json &j) { if (CHECK_OBJECT(j, "standard_geometry")) return ParseStandardDetectorGeometry(j["standard_geometry"]); else if (CHECK_ARRAY(j, "custom_geometry")) return ParseCustomDetectorGeometry(j["custom_geometry"]); else throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "It is compulsory to have either standard_detector or custom_detector declared."); } DetectorSetup ParseDetectorSetup(const nlohmann::json &j) { if (!j.is_object()) throw JFJochException(JFJochExceptionCategory::JSON, "Expecting JSON object for detector setup"); DetectorGeometry geom = ParseDetectorGeometry(j); std::string description = GET_STR(j, "description"); auto module_hostname = GET_STR_ARR(j, "module_hostname"); auto gain_files = GET_STR_ARR(j, "gain_files"); DetectorSetup setup(geom, description, module_hostname); if (!gain_files.empty()) setup.LoadGain(gain_files); setup.UDPInterfaceCount(GET_I64(j, "udp_interface_count", 2)); return setup; } void ParseDetectorSetup(const nlohmann::json &j, const std::string& tag, JFJochBroker& broker) { if (CHECK_ARRAY(j, tag)) { for (const auto &iter : j[tag]) broker.AddDetectorSetup(ParseDetectorSetup(iter)); } else throw JFJochException(JFJochExceptionCategory::JSON, "Detector setup not found"); } void ParseFacilityConfiguration(const nlohmann::json &input, const std::string& tag, DiffractionExperiment &experiment) { if (CHECK_OBJECT(input, tag)) { auto j = input[tag]; experiment.SourceName(GET_STR(j, "source_name")); experiment.SourceNameShort(GET_STR(j, "source_name_short")); experiment.InstrumentName(GET_STR(j, "instrument_name")); experiment.InstrumentNameShort(GET_STR(j, "instrument_name_short")); experiment.DataStreams(GET_I64(j, "datastreams")); experiment.PedestalG0Frames(GET_I64(j, "pedestal_g0_frames")); experiment.PedestalG1Frames(GET_I64(j, "pedestal_g1_frames")); experiment.PedestalG2Frames(GET_I64(j, "pedestal_g2_frames")); experiment.FrameTime(GET_TIME(j, "frame_time_us"), GET_TIME(j, "count_time_us")); experiment.SpotFindingPeriod(GET_TIME(j, "spot_finding_period_us")); experiment.PreviewPeriod(GET_TIME(j, "preview_period_us")); experiment.IPv4BaseAddr(GET_STR(j, "detector_ipv4")); } else throw JFJochException(JFJochExceptionCategory::JSON, "Default configuration not found"); } void ParseBrokerConfiguration(const nlohmann::json &input, const std::string& tag, JFJochBroker& broker) { if (CHECK_OBJECT(input, tag)) { auto j = input[tag]; if (j.contains("receiver_addr")) broker.Services().Receiver(GET_STR(j, "receiver_addr")); if (CHECK_ARRAY(j, "writer")) { for (const auto &iter: j["writer"]) { broker.Services().Writer(GET_STR(iter, "addr_grpc"), GET_STR(iter, "addr_zmq")); } } if (j.contains("detector_addr")) broker.Services().Detector(GET_STR(j, "detector_addr")); } else throw JFJochException(JFJochExceptionCategory::JSON, "Service configuration not found"); }