Files
Jungfraujoch/broker/JFJochBrokerParser.cpp
2024-06-30 17:48:35 +02:00

428 lines
18 KiB
C++

// Copyright (2019-2023) Paul Scherrer Institute
#include "JFJochBrokerParser.h"
#include "../common/NetworkAddressConvert.h"
#include "../frame_serialize/ZMQStream2Pusher.h"
#include "../frame_serialize/DumpCBORToFilePusher.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<int64_t>();
} catch (std::exception &e) {
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, tag + ": " + e.what());
}
} else
return def;
}
inline int32_t GET_I32(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<int32_t>();
} catch (std::exception &e) {
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, tag + ": " + e.what());
}
} else
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, tag + " is compulsory");
}
inline int32_t GET_I32(const nlohmann::json &j, const std::string& tag, int32_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<int32_t>();
} catch (std::exception &e) {
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, tag + ": " + e.what());
}
} else
return def;
}
inline float GET_FLOAT(const nlohmann::json &j, const std::string& tag, float def) {
if (j.contains(tag)) {
if (!j[tag].is_number())
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, tag + " must be float");
try {
return j[tag].get<float>();
} 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<bool>();
} 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, const std::string& def) {
if (j.contains(tag)) {
if (!j[tag].is_string())
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, tag + " must be string");
try {
return j[tag].get<std::string>();
} 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<std::string>();
} 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<int64_t>();
} catch (std::exception &e) {
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, tag + ": " + e.what());
}
} else
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, tag + " is compulsory");
}
inline std::vector<std::string> GET_STR_ARR(const nlohmann::json &j, const std::string& tag) {
std::vector<std::string> ret;
if (CHECK_ARRAY(j, tag)) {
try {
for (const auto &iter: j[tag])
ret.push_back(iter.get<std::string>());
} catch (std::exception &e) {
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, tag + ": " + e.what());
}
}
return ret;
}
inline std::vector<std::int64_t> GET_I64_ARR(const nlohmann::json &j, const std::string& tag) {
std::vector<std::int64_t> ret;
if (CHECK_ARRAY(j, tag)) {
try {
for (const auto &iter: j[tag])
ret.push_back(iter.get<int64_t>());
} 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 no units provided for time, this is always microsecond
if (j[tag].is_number())
return std::chrono::microseconds (std::lround(j[tag].get<double>() * 1000.0 * 1000.0));
else if (j[tag].is_string()) {
std::string::size_type pos;
auto text = j[tag].get<std::string>();
// Check if floating point
pos = text.find_first_of('.');
if (pos != std::string::npos)
throw JFJochException(JFJochExceptionCategory::WrongNumber,
"Time must be provided as <integer> {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 <integer> {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, bool mirror_y) {
std::vector<DetectorModuleGeometry> 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, mirror_y};
}
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"], GET_BOOL(j, "custom_geometry_mirror_y", false));
} 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");
DetectorType detector_type;
const std::string detector_type_str = GET_STR(j, "type", "jungfrau");
if (detector_type_str == "jungfrau")
detector_type = DetectorType::JUNGFRAU;
else if (detector_type_str == "eiger")
detector_type = DetectorType::EIGER;
else
throw JFJochException(JFJochExceptionCategory::JSON, "type must be \"jungfrau\" or \"eiger\" ");
DetectorSetup setup(geom, detector_type, description, module_hostname);
if (detector_type == DetectorType::JUNGFRAU) {
auto gain_files = GET_STR_ARR(j, "gain_files");
if (!gain_files.empty())
setup.LoadGain(gain_files);
} else if (detector_type == DetectorType::EIGER) {
auto trim_files = GET_STR_ARR(j, "trim_files");
if (!trim_files.empty())
setup.SetTrimFiles(trim_files);
}
if (j.contains("high_voltage"))
setup.HighVoltage(GET_I32(j,"high_voltage"));
setup.UDPInterfaceCount(GET_I64(j, "udp_interface_count", 2))
.SensorThickness_um(GET_FLOAT(j, "sensor_thickness_um", 320.0f))
.PixelSize_um(GET_FLOAT(j, "pixel_size_um", 75.0f))
.SensorMaterial(GET_STR(j, "sensor_material", "Si"))
.SerialNumber(GET_STR(j, "serial_number",""));
if (j.contains("tx_delay"))
setup.TxDelay(GET_I64_ARR(j, "tx_delay"));
return setup;
}
void ParseDetectorSetup(const nlohmann::json &j, const std::string& tag, JFJochBrokerHttp& 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 ParseImagePusher(const nlohmann::json &input, std::unique_ptr<ImagePusher> &image_pusher) {
std::string pusher_type = ParseString(input, "stream_type", "zmq");
if (pusher_type == "zmq") {
int32_t zmq_send_watermark = ParseInt32(input, "zmq_send_watermark", 100);
int32_t zmq_send_buffer_size = ParseInt32(input, "zmq_send_buffer_size", -1);
auto tmp = std::make_unique<ZMQStream2Pusher>(ParseStringArray(input, "zmq_image_addr"),
zmq_send_watermark,
zmq_send_buffer_size);
std::string preview_addr = ParseString(input, "zmq_preview_addr", "");
if (!preview_addr.empty())
tmp->PreviewSocket(preview_addr);
if (input.contains("zmq_preview_period"))
tmp->PreviewCounterPeriod(GET_TIME(input, "zmq_preview_period"));
std::string writer_notification_addr = ParseString(input, "zmq_writer_notification_addr", "");
if (!writer_notification_addr.empty())
tmp->WriterNotificationSocket(writer_notification_addr);
image_pusher = std::move(tmp);
} else if (pusher_type == "dump_cbor") {
image_pusher = std::make_unique<DumpCBORToFilePusher>();
} else
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
"stream_type allowed: zmq (default), dump_cbor");
}
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.SourceType(GET_STR(j, "source_type", ""));
experiment.InstrumentName(GET_STR(j, "instrument_name"));
experiment.InstrumentNameShort(GET_STR(j, "instrument_name_short"));
experiment.PulsedSource(GET_BOOL(j, "pulsed_source", false));
if (j.contains("rotation_axis")) {
if (j["rotation_axis"].is_array() && (j["rotation_axis"].size() == 3))
experiment.DefaultRotationAxis(Coord(j["rotation_axis"][0].get<float>(),
j["rotation_axis"][1].get<float>(),
j["rotation_axis"][2].get<float>()));
else
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
"rotation_axis must be float array of 3");
}
if (j.contains("pedestal_g0_frames"))
experiment.PedestalG0Frames(GET_I64(j, "pedestal_g0_frames"));
if (j.contains("pedestal_g1_frames"))
experiment.PedestalG1Frames(GET_I64(j, "pedestal_g1_frames"));
if (j.contains("pedestal_g2_frames"))
experiment.PedestalG2Frames(GET_I64(j, "pedestal_g2_frames"));
if (j.contains("detector_trigger_delay"))
experiment.DetectorDelay(GET_TIME(j, "detector_trigger_delay"));
experiment.FrameTime(GET_TIME(j, "frame_time"), GET_TIME(j, "count_time"));
experiment.UseInternalPacketGenerator(GET_BOOL(j, "internal_frame_generator", false));
if (experiment.IsUsingInternalPacketGen())
experiment.ConversionOnFPGA(false);
experiment.IPv4BaseAddr(GET_STR(j, "detector_ipv4"));
} else
throw JFJochException(JFJochExceptionCategory::JSON, "Default configuration not found");
}
void ParseAcquisitionDeviceGroup(const nlohmann::json &input, const std::string& tag,
AcquisitionDeviceGroup &aq_devices) {
if (CHECK_OBJECT(input, tag)) {
const auto &j = input[tag];
std::string dev_type = GET_STR(j, "type");
if (dev_type == "pcie") {
std::vector<std::string> devices = GET_STR_ARR(j, "devices");
if (devices.empty())
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
"At least one PCIe device must be provided");
for (auto &d: devices)
aq_devices.AddPCIeDevice(d);
} else if (dev_type == "hls") {
int64_t buffer_size = GET_I64(j, "buffer_size", 1024);
if ((buffer_size < 16) || (buffer_size > 65536))
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Buffer size must be in range 16-65536");
aq_devices.AddHLSDevice(buffer_size);
} else
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Device type unknown");
aq_devices.SetDefaultDataSource(AcquisitionDeviceSource::NETWORK);
std::vector<std::string> ipv4_addr = GET_STR_ARR(j, "ipv4_addr");
if (ipv4_addr.size() != aq_devices.size())
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
"Mismatch between number of devices and number of IPv4 addresses");
for (int i = 0; i < ipv4_addr.size(); i++)
aq_devices[i].SetIPv4Address(IPv4AddressFromStr(ipv4_addr[i]));
}
}
std::vector<std::string> ParseStringArray(const nlohmann::json &input, const std::string& tag) {
return GET_STR_ARR(input, tag);
}
std::string ParseString(const nlohmann::json &input, const std::string& tag, const std::string &def) {
return GET_STR(input, tag, def);
}
int64_t ParseInt64(const nlohmann::json &input, const std::string& tag, int64_t def) {
return GET_I64(input, tag, def);
}
int32_t ParseInt32(const nlohmann::json &input, const std::string& tag, int32_t def) {
return GET_I32(input, tag, def);
}