broker: /start takes a module placement (dataset_settings.module_geometry)
The module_geometry array of rugnux's <prefix>_panels.json can be given with /start. Each entry names its module by its box in the assembled image; the list has to name exactly the detector's modules (any order), else /start is refused. The image is still assembled at whole pixels as custom_geometry lays it out; the placement becomes the experiment's modules for the dataset, so it reaches the start message (detector_modules), the NXmx module_NN groups and the broker's own analysis. A /start without it goes back to the modules as assembled. Generated server model and frontend client regenerated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UqXYdJKPUFLoeAchjzv3x4
This commit is contained in:
1 parent
fd8704c00c
commit
3b60dace7b
15 files changed
+832
-94
No files matched your search
@@ -699,6 +699,23 @@ DatasetSettings Convert(const org::openapitools::server::model::Dataset_settings
|
||||
if (input.polarizationFactorIsSet())
|
||||
ret.PolarizationFactor(input.getPolarizationFactor());
|
||||
|
||||
std::vector<DetectorModule> modules;
|
||||
for (const auto &m: input.getModuleGeometry()) {
|
||||
DetectorModule d;
|
||||
d.x0 = m.getX0();
|
||||
d.y0 = m.getY0();
|
||||
d.width = m.getWidth();
|
||||
d.height = m.getHeight();
|
||||
d.ox = m.getOx();
|
||||
d.oy = m.getOy();
|
||||
d.fx = m.getFx();
|
||||
d.fy = m.getFy();
|
||||
d.sx = m.getSx();
|
||||
d.sy = m.getSy();
|
||||
modules.push_back(d);
|
||||
}
|
||||
ret.ModuleGeometry(modules);
|
||||
|
||||
ret.PoniRot1_rad(input.getPoniRot1Rad());
|
||||
ret.PoniRot2_rad(input.getPoniRot2Rad());
|
||||
ret.PoniRot3_rad(input.getPoniRot3Rad());
|
||||
|
||||
@@ -91,6 +91,7 @@ Dataset_settings::Dataset_settings()
|
||||
m_Detect_ice_ringsIsSet = false;
|
||||
m_Async_start = false;
|
||||
m_Async_startIsSet = false;
|
||||
m_Module_geometryIsSet = false;
|
||||
m_TokensIsSet = false;
|
||||
m_Xray_fluorescence_spectrumIsSet = false;
|
||||
|
||||
@@ -460,6 +461,27 @@ bool Dataset_settings::validate(std::stringstream& msg, const std::string& pathP
|
||||
|
||||
}
|
||||
|
||||
if (moduleGeometryIsSet())
|
||||
{
|
||||
const std::vector<org::openapitools::server::model::Detector_module_placement>& value = m_Module_geometry;
|
||||
const std::string currentValuePath = _pathPrefix + ".moduleGeometry";
|
||||
|
||||
|
||||
{ // Recursive validation of array elements
|
||||
const std::string oldValuePath = currentValuePath;
|
||||
int i = 0;
|
||||
for (const org::openapitools::server::model::Detector_module_placement& value : value)
|
||||
{
|
||||
const std::string currentValuePath = oldValuePath + "[" + std::to_string(i) + "]";
|
||||
|
||||
success = value.validate(msg, currentValuePath + ".moduleGeometry") && success;
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (tokensIsSet())
|
||||
{
|
||||
const std::vector<std::string>& value = m_Tokens;
|
||||
@@ -615,6 +637,9 @@ bool Dataset_settings::operator==(const Dataset_settings& rhs) const
|
||||
((!asyncStartIsSet() && !rhs.asyncStartIsSet()) || (asyncStartIsSet() && rhs.asyncStartIsSet() && isAsyncStart() == rhs.isAsyncStart())) &&
|
||||
|
||||
|
||||
((!moduleGeometryIsSet() && !rhs.moduleGeometryIsSet()) || (moduleGeometryIsSet() && rhs.moduleGeometryIsSet() && getModuleGeometry() == rhs.getModuleGeometry())) &&
|
||||
|
||||
|
||||
((!tokensIsSet() && !rhs.tokensIsSet()) || (tokensIsSet() && rhs.tokensIsSet() && getTokens() == rhs.getTokens())) &&
|
||||
|
||||
|
||||
@@ -707,6 +732,8 @@ void to_json(nlohmann::json& j, const Dataset_settings& o)
|
||||
j["detect_ice_rings"] = o.m_Detect_ice_rings;
|
||||
if(o.asyncStartIsSet())
|
||||
j["async_start"] = o.m_Async_start;
|
||||
if(o.moduleGeometryIsSet() || !o.m_Module_geometry.empty())
|
||||
j["module_geometry"] = o.m_Module_geometry;
|
||||
if(o.tokensIsSet() || !o.m_Tokens.empty())
|
||||
j["tokens"] = o.m_Tokens;
|
||||
if(o.xrayFluorescenceSpectrumIsSet())
|
||||
@@ -900,6 +927,11 @@ void from_json(const nlohmann::json& j, Dataset_settings& o)
|
||||
j.at("async_start").get_to(o.m_Async_start);
|
||||
o.m_Async_startIsSet = true;
|
||||
}
|
||||
if(j.find("module_geometry") != j.end())
|
||||
{
|
||||
j.at("module_geometry").get_to(o.m_Module_geometry);
|
||||
o.m_Module_geometryIsSet = true;
|
||||
}
|
||||
if(j.find("tokens") != j.end())
|
||||
{
|
||||
j.at("tokens").get_to(o.m_Tokens);
|
||||
@@ -1557,6 +1589,23 @@ void Dataset_settings::unsetAsync_start()
|
||||
{
|
||||
m_Async_startIsSet = false;
|
||||
}
|
||||
std::vector<org::openapitools::server::model::Detector_module_placement> Dataset_settings::getModuleGeometry() const
|
||||
{
|
||||
return m_Module_geometry;
|
||||
}
|
||||
void Dataset_settings::setModuleGeometry(std::vector<org::openapitools::server::model::Detector_module_placement> const& value)
|
||||
{
|
||||
m_Module_geometry = value;
|
||||
m_Module_geometryIsSet = true;
|
||||
}
|
||||
bool Dataset_settings::moduleGeometryIsSet() const
|
||||
{
|
||||
return m_Module_geometryIsSet;
|
||||
}
|
||||
void Dataset_settings::unsetModule_geometry()
|
||||
{
|
||||
m_Module_geometryIsSet = false;
|
||||
}
|
||||
std::vector<std::string> Dataset_settings::getTokens() const
|
||||
{
|
||||
return m_Tokens;
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "Grid_scan.h"
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "Dataset_settings_smargon.h"
|
||||
#include "Detector_module_placement.h"
|
||||
#include "Rotation_axis.h"
|
||||
#include "Unit_cell.h"
|
||||
#include <string>
|
||||
@@ -338,6 +339,13 @@ public:
|
||||
bool asyncStartIsSet() const;
|
||||
void unsetAsync_start();
|
||||
/// <summary>
|
||||
/// Where the detector's modules are for this dataset, as rugnux measures them (`rugnux --panel-refine` writes it as `<prefix>_panels.json`; the file's `module_geometry` array goes here). Each entry names its module by the module's box in the assembled image (`x0`, `y0`, `width`, `height`), in any order; every module must be named once, else `/start` fails with 400. The image is assembled as configured; the placement goes into the start message and the NXmx `module_NN` groups, and the analysis takes it. Omitted or empty: modules as assembled.
|
||||
/// </summary>
|
||||
std::vector<org::openapitools::server::model::Detector_module_placement> getModuleGeometry() const;
|
||||
void setModuleGeometry(std::vector<org::openapitools::server::model::Detector_module_placement> const& value);
|
||||
bool moduleGeometryIsSet() const;
|
||||
void unsetModule_geometry();
|
||||
/// <summary>
|
||||
/// Bearer tokens that protect this dataset: any number, all equivalent (e.g. a beamline secret and a dataset-specific user secret). While set, the endpoints marked with the `bearerAuth` security scheme require `Authorization: Bearer <token>` and answer 401 otherwise. Every accepted `/start` replaces the previous dataset's tokens; omitted or empty means no protection. Never returned by any endpoint.
|
||||
/// </summary>
|
||||
std::vector<std::string> getTokens() const;
|
||||
@@ -435,6 +443,8 @@ protected:
|
||||
bool m_Detect_ice_ringsIsSet;
|
||||
bool m_Async_start;
|
||||
bool m_Async_startIsSet;
|
||||
std::vector<org::openapitools::server::model::Detector_module_placement> m_Module_geometry;
|
||||
bool m_Module_geometryIsSet;
|
||||
std::vector<std::string> m_Tokens;
|
||||
bool m_TokensIsSet;
|
||||
org::openapitools::server::model::Dataset_settings_xray_fluorescence_spectrum m_Xray_fluorescence_spectrum;
|
||||
|
||||
@@ -0,0 +1,273 @@
|
||||
/**
|
||||
* Jungfraujoch
|
||||
* API to control Jungfraujoch developed by the Paul Scherrer Institute (Switzerland). Jungfraujoch is a data acquisition and analysis system for pixel array detectors, primarly PSI JUNGFRAU. Jungfraujoch uses FPGA boards to acquire data at high data rates. # License Clarification While this API definition is licensed under GPL-3.0, **the GPL copyleft provisions do not apply** when this file is used solely to generate OpenAPI clients or when implementing applications that interact with the API. Generated client code and applications using this API definition are not subject to the GPL license requirements and may be distributed under terms of your choosing. This exception is similar in spirit to the Linux Kernel's approach to userspace API headers and the GCC Runtime Library Exception. The Linux Kernel developers have explicitly stated that user programs that merely use the kernel interfaces (syscalls, ioctl definitions, etc.) are not derivative works of the kernel and are not subject to the terms of the GPL. This exception is intended to allow wider use of this API specification without imposing GPL requirements on applications that merely interact with the API, regardless of whether they communicate through network calls or other mechanisms.
|
||||
*
|
||||
* The version of the OpenAPI document: 1.0.0-rc.181
|
||||
* Contact: filip.leonarski@psi.ch
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
#include "Detector_module_placement.h"
|
||||
#include "Helpers.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace org::openapitools::server::model
|
||||
{
|
||||
|
||||
Detector_module_placement::Detector_module_placement()
|
||||
{
|
||||
m_X0 = 0L;
|
||||
m_Y0 = 0L;
|
||||
m_Width = 0L;
|
||||
m_Height = 0L;
|
||||
m_Ox = 0.0;
|
||||
m_Oy = 0.0;
|
||||
m_Fx = 0.0;
|
||||
m_Fy = 0.0;
|
||||
m_Sx = 0.0;
|
||||
m_Sy = 0.0;
|
||||
|
||||
}
|
||||
|
||||
void Detector_module_placement::validate() const
|
||||
{
|
||||
std::stringstream msg;
|
||||
if (!validate(msg))
|
||||
{
|
||||
throw org::openapitools::server::helpers::ValidationException(msg.str());
|
||||
}
|
||||
}
|
||||
|
||||
bool Detector_module_placement::validate(std::stringstream& msg) const
|
||||
{
|
||||
return validate(msg, "");
|
||||
}
|
||||
|
||||
bool Detector_module_placement::validate(std::stringstream& msg, const std::string& pathPrefix) const
|
||||
{
|
||||
bool success = true;
|
||||
const std::string _pathPrefix = pathPrefix.empty() ? "Detector_module_placement" : pathPrefix;
|
||||
|
||||
|
||||
|
||||
/* X0 */ {
|
||||
const int64_t& value = m_X0;
|
||||
const std::string currentValuePath = _pathPrefix + ".x0";
|
||||
|
||||
|
||||
if (value < 0ll)
|
||||
{
|
||||
success = false;
|
||||
msg << currentValuePath << ": must be greater than or equal to 0;";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Y0 */ {
|
||||
const int64_t& value = m_Y0;
|
||||
const std::string currentValuePath = _pathPrefix + ".y0";
|
||||
|
||||
|
||||
if (value < 0ll)
|
||||
{
|
||||
success = false;
|
||||
msg << currentValuePath << ": must be greater than or equal to 0;";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Width */ {
|
||||
const int64_t& value = m_Width;
|
||||
const std::string currentValuePath = _pathPrefix + ".width";
|
||||
|
||||
|
||||
if (value < 1ll)
|
||||
{
|
||||
success = false;
|
||||
msg << currentValuePath << ": must be greater than or equal to 1;";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Height */ {
|
||||
const int64_t& value = m_Height;
|
||||
const std::string currentValuePath = _pathPrefix + ".height";
|
||||
|
||||
|
||||
if (value < 1ll)
|
||||
{
|
||||
success = false;
|
||||
msg << currentValuePath << ": must be greater than or equal to 1;";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool Detector_module_placement::operator==(const Detector_module_placement& rhs) const
|
||||
{
|
||||
return
|
||||
|
||||
|
||||
(getX0() == rhs.getX0())
|
||||
&&
|
||||
|
||||
(getY0() == rhs.getY0())
|
||||
&&
|
||||
|
||||
(getWidth() == rhs.getWidth())
|
||||
&&
|
||||
|
||||
(getHeight() == rhs.getHeight())
|
||||
&&
|
||||
|
||||
(getOx() == rhs.getOx())
|
||||
&&
|
||||
|
||||
(getOy() == rhs.getOy())
|
||||
&&
|
||||
|
||||
(getFx() == rhs.getFx())
|
||||
&&
|
||||
|
||||
(getFy() == rhs.getFy())
|
||||
&&
|
||||
|
||||
(getSx() == rhs.getSx())
|
||||
&&
|
||||
|
||||
(getSy() == rhs.getSy())
|
||||
|
||||
|
||||
;
|
||||
}
|
||||
|
||||
bool Detector_module_placement::operator!=(const Detector_module_placement& rhs) const
|
||||
{
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json& j, const Detector_module_placement& o)
|
||||
{
|
||||
j = nlohmann::json::object();
|
||||
j["x0"] = o.m_X0;
|
||||
j["y0"] = o.m_Y0;
|
||||
j["width"] = o.m_Width;
|
||||
j["height"] = o.m_Height;
|
||||
j["ox"] = o.m_Ox;
|
||||
j["oy"] = o.m_Oy;
|
||||
j["fx"] = o.m_Fx;
|
||||
j["fy"] = o.m_Fy;
|
||||
j["sx"] = o.m_Sx;
|
||||
j["sy"] = o.m_Sy;
|
||||
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json& j, Detector_module_placement& o)
|
||||
{
|
||||
j.at("x0").get_to(o.m_X0);
|
||||
j.at("y0").get_to(o.m_Y0);
|
||||
j.at("width").get_to(o.m_Width);
|
||||
j.at("height").get_to(o.m_Height);
|
||||
j.at("ox").get_to(o.m_Ox);
|
||||
j.at("oy").get_to(o.m_Oy);
|
||||
j.at("fx").get_to(o.m_Fx);
|
||||
j.at("fy").get_to(o.m_Fy);
|
||||
j.at("sx").get_to(o.m_Sx);
|
||||
j.at("sy").get_to(o.m_Sy);
|
||||
|
||||
}
|
||||
|
||||
int64_t Detector_module_placement::getX0() const
|
||||
{
|
||||
return m_X0;
|
||||
}
|
||||
void Detector_module_placement::setX0(int64_t const value)
|
||||
{
|
||||
m_X0 = value;
|
||||
}
|
||||
int64_t Detector_module_placement::getY0() const
|
||||
{
|
||||
return m_Y0;
|
||||
}
|
||||
void Detector_module_placement::setY0(int64_t const value)
|
||||
{
|
||||
m_Y0 = value;
|
||||
}
|
||||
int64_t Detector_module_placement::getWidth() const
|
||||
{
|
||||
return m_Width;
|
||||
}
|
||||
void Detector_module_placement::setWidth(int64_t const value)
|
||||
{
|
||||
m_Width = value;
|
||||
}
|
||||
int64_t Detector_module_placement::getHeight() const
|
||||
{
|
||||
return m_Height;
|
||||
}
|
||||
void Detector_module_placement::setHeight(int64_t const value)
|
||||
{
|
||||
m_Height = value;
|
||||
}
|
||||
double Detector_module_placement::getOx() const
|
||||
{
|
||||
return m_Ox;
|
||||
}
|
||||
void Detector_module_placement::setOx(double const value)
|
||||
{
|
||||
m_Ox = value;
|
||||
}
|
||||
double Detector_module_placement::getOy() const
|
||||
{
|
||||
return m_Oy;
|
||||
}
|
||||
void Detector_module_placement::setOy(double const value)
|
||||
{
|
||||
m_Oy = value;
|
||||
}
|
||||
double Detector_module_placement::getFx() const
|
||||
{
|
||||
return m_Fx;
|
||||
}
|
||||
void Detector_module_placement::setFx(double const value)
|
||||
{
|
||||
m_Fx = value;
|
||||
}
|
||||
double Detector_module_placement::getFy() const
|
||||
{
|
||||
return m_Fy;
|
||||
}
|
||||
void Detector_module_placement::setFy(double const value)
|
||||
{
|
||||
m_Fy = value;
|
||||
}
|
||||
double Detector_module_placement::getSx() const
|
||||
{
|
||||
return m_Sx;
|
||||
}
|
||||
void Detector_module_placement::setSx(double const value)
|
||||
{
|
||||
m_Sx = value;
|
||||
}
|
||||
double Detector_module_placement::getSy() const
|
||||
{
|
||||
return m_Sy;
|
||||
}
|
||||
void Detector_module_placement::setSy(double const value)
|
||||
{
|
||||
m_Sy = value;
|
||||
}
|
||||
|
||||
|
||||
} // namespace org::openapitools::server::model
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* Jungfraujoch
|
||||
* API to control Jungfraujoch developed by the Paul Scherrer Institute (Switzerland). Jungfraujoch is a data acquisition and analysis system for pixel array detectors, primarly PSI JUNGFRAU. Jungfraujoch uses FPGA boards to acquire data at high data rates. # License Clarification While this API definition is licensed under GPL-3.0, **the GPL copyleft provisions do not apply** when this file is used solely to generate OpenAPI clients or when implementing applications that interact with the API. Generated client code and applications using this API definition are not subject to the GPL license requirements and may be distributed under terms of your choosing. This exception is similar in spirit to the Linux Kernel's approach to userspace API headers and the GCC Runtime Library Exception. The Linux Kernel developers have explicitly stated that user programs that merely use the kernel interfaces (syscalls, ioctl definitions, etc.) are not derivative works of the kernel and are not subject to the terms of the GPL. This exception is intended to allow wider use of this API specification without imposing GPL requirements on applications that merely interact with the API, regardless of whether they communicate through network calls or other mechanisms.
|
||||
*
|
||||
* The version of the OpenAPI document: 1.0.0-rc.181
|
||||
* Contact: filip.leonarski@psi.ch
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
/*
|
||||
* Detector_module_placement.h
|
||||
*
|
||||
* One module of the assembled image and where it lies on the detector plane, in pixels of the assembled image (a pixel's centre at its index). Image pixel (x, y) of the module, with x0 <= x < x0 + width and y0 <= y < y0 + height, lies on the plane at (ox, oy) + (x - x0) (fx, fy) + (y - y0) (sx, sy). As assembled: ox = x0, oy = y0, (fx, fy) = (1, 0), (sx, sy) = (0, 1).
|
||||
*/
|
||||
|
||||
#ifndef Detector_module_placement_H_
|
||||
#define Detector_module_placement_H_
|
||||
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
namespace org::openapitools::server::model
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// One module of the assembled image and where it lies on the detector plane, in pixels of the assembled image (a pixel's centre at its index). Image pixel (x, y) of the module, with x0 <= x < x0 + width and y0 <= y < y0 + height, lies on the plane at (ox, oy) + (x - x0) (fx, fy) + (y - y0) (sx, sy). As assembled: ox = x0, oy = y0, (fx, fy) = (1, 0), (sx, sy) = (0, 1).
|
||||
/// </summary>
|
||||
class Detector_module_placement
|
||||
{
|
||||
public:
|
||||
Detector_module_placement();
|
||||
virtual ~Detector_module_placement() = default;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Validate the current data in the model. Throws a ValidationException on failure.
|
||||
/// </summary>
|
||||
void validate() const;
|
||||
|
||||
/// <summary>
|
||||
/// Validate the current data in the model. Returns false on error and writes an error
|
||||
/// message into the given stringstream.
|
||||
/// </summary>
|
||||
bool validate(std::stringstream& msg) const;
|
||||
|
||||
/// <summary>
|
||||
/// Helper overload for validate. Used when one model stores another model and calls it's validate.
|
||||
/// Not meant to be called outside that case.
|
||||
/// </summary>
|
||||
bool validate(std::stringstream& msg, const std::string& pathPrefix) const;
|
||||
|
||||
bool operator==(const Detector_module_placement& rhs) const;
|
||||
bool operator!=(const Detector_module_placement& rhs) const;
|
||||
|
||||
/////////////////////////////////////////////
|
||||
/// Detector_module_placement members
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
int64_t getX0() const;
|
||||
void setX0(int64_t const value);
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
int64_t getY0() const;
|
||||
void setY0(int64_t const value);
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
int64_t getWidth() const;
|
||||
void setWidth(int64_t const value);
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
int64_t getHeight() const;
|
||||
void setHeight(int64_t const value);
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
double getOx() const;
|
||||
void setOx(double const value);
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
double getOy() const;
|
||||
void setOy(double const value);
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
double getFx() const;
|
||||
void setFx(double const value);
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
double getFy() const;
|
||||
void setFy(double const value);
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
double getSx() const;
|
||||
void setSx(double const value);
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
double getSy() const;
|
||||
void setSy(double const value);
|
||||
|
||||
friend void to_json(nlohmann::json& j, const Detector_module_placement& o);
|
||||
friend void from_json(const nlohmann::json& j, Detector_module_placement& o);
|
||||
protected:
|
||||
int64_t m_X0;
|
||||
|
||||
int64_t m_Y0;
|
||||
|
||||
int64_t m_Width;
|
||||
|
||||
int64_t m_Height;
|
||||
|
||||
double m_Ox;
|
||||
|
||||
double m_Oy;
|
||||
|
||||
double m_Fx;
|
||||
|
||||
double m_Fy;
|
||||
|
||||
double m_Sx;
|
||||
|
||||
double m_Sy;
|
||||
|
||||
|
||||
};
|
||||
|
||||
} // namespace org::openapitools::server::model
|
||||
|
||||
#endif /* Detector_module_placement_H_ */
|
||||
@@ -675,6 +675,17 @@ components:
|
||||
type: boolean
|
||||
default: false
|
||||
description: When set to true, `/start` will not wait for detector and Jungfraujoch to be ready for the measurement.
|
||||
module_geometry:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/detector_module_placement'
|
||||
description: |
|
||||
Where the detector's modules are for this dataset, as rugnux measures them (`rugnux --panel-refine`
|
||||
writes it as `<prefix>_panels.json`; the file's `module_geometry` array goes here).
|
||||
Each entry names its module by the module's box in the assembled image (`x0`, `y0`, `width`,
|
||||
`height`), in any order; every module must be named once, else `/start` fails with 400.
|
||||
The image is assembled as configured; the placement goes into the start message and the
|
||||
NXmx `module_NN` groups, and the analysis takes it. Omitted or empty: modules as assembled.
|
||||
tokens:
|
||||
type: array
|
||||
items:
|
||||
@@ -2392,6 +2403,54 @@ components:
|
||||
$ref: '#/components/schemas/detector_module_direction'
|
||||
slow_axis:
|
||||
$ref: '#/components/schemas/detector_module_direction'
|
||||
detector_module_placement:
|
||||
type: object
|
||||
description: |
|
||||
One module of the assembled image and where it lies on the detector plane, in pixels of the
|
||||
assembled image (a pixel's centre at its index). Image pixel (x, y) of the module, with
|
||||
x0 <= x < x0 + width and y0 <= y < y0 + height, lies on the plane at
|
||||
(ox, oy) + (x - x0) (fx, fy) + (y - y0) (sx, sy). As assembled: ox = x0, oy = y0, (fx, fy) = (1, 0),
|
||||
(sx, sy) = (0, 1).
|
||||
required:
|
||||
- x0
|
||||
- y0
|
||||
- width
|
||||
- height
|
||||
- ox
|
||||
- oy
|
||||
- fx
|
||||
- fy
|
||||
- sx
|
||||
- sy
|
||||
properties:
|
||||
x0:
|
||||
type: integer
|
||||
format: int64
|
||||
minimum: 0
|
||||
y0:
|
||||
type: integer
|
||||
format: int64
|
||||
minimum: 0
|
||||
width:
|
||||
type: integer
|
||||
format: int64
|
||||
minimum: 1
|
||||
height:
|
||||
type: integer
|
||||
format: int64
|
||||
minimum: 1
|
||||
ox:
|
||||
type: number
|
||||
oy:
|
||||
type: number
|
||||
fx:
|
||||
type: number
|
||||
fy:
|
||||
type: number
|
||||
sx:
|
||||
type: number
|
||||
sy:
|
||||
type: number
|
||||
detector:
|
||||
type: object
|
||||
required:
|
||||
|
||||
@@ -446,6 +446,15 @@ std::optional<float> DatasetSettings::GetBandwidthFWHM() const {
|
||||
return bandwidth_fwhm;
|
||||
}
|
||||
|
||||
DatasetSettings &DatasetSettings::ModuleGeometry(const std::vector<DetectorModule> &input) {
|
||||
module_geometry = input;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const std::vector<DetectorModule> &DatasetSettings::GetModuleGeometry() const {
|
||||
return module_geometry;
|
||||
}
|
||||
|
||||
float DatasetSettings::GetPoniRot3_rad() const {
|
||||
return poni_rot_3_rad;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "../compression/CompressionAlgorithmEnum.h"
|
||||
#include "JFJochMessages.h"
|
||||
#include "XrayFluorescenceSpectrum.h"
|
||||
#include "DetectorModules.h"
|
||||
|
||||
#include "gemmi/symmetry.hpp"
|
||||
|
||||
@@ -64,6 +65,9 @@ class DatasetSettings {
|
||||
|
||||
std::optional<float> polarization_factor;
|
||||
std::optional<float> bandwidth_fwhm; // relative X-ray bandwidth, FWHM of dlambda/lambda (e.g. 0.01 for 1%)
|
||||
// Where the modules are for this dataset (rugnux's <prefix>_modules.json); empty: as the detector
|
||||
// assembles them.
|
||||
std::vector<DetectorModule> module_geometry;
|
||||
float poni_rot_1_rad;
|
||||
float poni_rot_2_rad;
|
||||
float poni_rot_3_rad;
|
||||
@@ -114,6 +118,7 @@ public:
|
||||
DatasetSettings& PixelValueHighThreshold(const std::optional<int64_t> &input);
|
||||
DatasetSettings& PolarizationFactor(const std::optional<float> &input);
|
||||
DatasetSettings& BandwidthFWHM(const std::optional<float> &input);
|
||||
DatasetSettings& ModuleGeometry(const std::vector<DetectorModule> &input);
|
||||
DatasetSettings& PoniRot1_rad(float input);
|
||||
DatasetSettings& PoniRot2_rad(float input);
|
||||
DatasetSettings& PoniRot3_rad(float input);
|
||||
@@ -170,6 +175,7 @@ public:
|
||||
|
||||
std::optional<float> GetPolarizationFactor() const;
|
||||
std::optional<float> GetBandwidthFWHM() const;
|
||||
const std::vector<DetectorModule> &GetModuleGeometry() const;
|
||||
float GetPoniRot1_rad() const;
|
||||
float GetPoniRot2_rad() const;
|
||||
float GetPoniRot3_rad() const;
|
||||
|
||||
@@ -1149,6 +1149,21 @@ DiffractionExperiment &DiffractionExperiment::ImportDatasetSettings(const Datase
|
||||
"Number of images cannot exceed " + std::to_string(MAX_IMAGE_NUMBER));
|
||||
}
|
||||
|
||||
if (input.GetModuleGeometry().empty())
|
||||
dataset_modules = {};
|
||||
else {
|
||||
try {
|
||||
// The modules as this experiment has them: given, or a JUNGFRAU image it assembles itself.
|
||||
DetectorModules target = detector_modules;
|
||||
if (target.empty() && IsGeometryTransformed())
|
||||
target = ModulesAsImaged(detector.GetGeometry());
|
||||
dataset_modules = ModulesPlaced(target, input.GetModuleGeometry());
|
||||
} catch (...) {
|
||||
dataset = tmp;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -1459,7 +1474,7 @@ DiffractionExperiment &DiffractionExperiment::Modules(const DetectorModules &inp
|
||||
}
|
||||
|
||||
const DetectorModules &DiffractionExperiment::GetModules() const {
|
||||
return detector_modules;
|
||||
return dataset_modules.empty() ? detector_modules : dataset_modules;
|
||||
}
|
||||
|
||||
DiffractionExperiment &DiffractionExperiment::SaveCalibration(const std::optional<bool> &input) {
|
||||
|
||||
@@ -98,6 +98,8 @@ class DiffractionExperiment {
|
||||
// Where the detector's modules are on the plane the diffraction geometry describes (DetectorModules.h).
|
||||
// Empty: the image is one flat detector.
|
||||
DetectorModules detector_modules;
|
||||
// The same, placed by the dataset settings' module geometry; empty where they do not place them.
|
||||
DetectorModules dataset_modules;
|
||||
public:
|
||||
// Public methods are atomic
|
||||
DiffractionExperiment();
|
||||
|
||||
@@ -25,6 +25,13 @@ has any, its statistics (`/statistics/data_collection`, `/result/scan`), buffere
|
||||
and answer `401` otherwise; `/statistics` omits its `measurement` block instead. See
|
||||
[Security](SECURITY.md#3-authenticated-read-access--per-dataset-bearer-tokens).
|
||||
|
||||
`/start` also takes an optional `module_geometry`: where the JUNGFRAU modules are, as `rugnux
|
||||
--panel-refine` measures them on an earlier run (the `module_geometry` array of its `<prefix>_panels.json`).
|
||||
Each entry names its module by the module's box in the assembled image, so the order does not matter, and
|
||||
a list that does not name exactly the detector's modules is refused with `400`. The image is assembled as
|
||||
configured; the placement goes into the start message, the master file's `module_NN` groups and the
|
||||
broker's analysis. See [Module placement file](RUGNUX_FORMATS.md#module-placement-file-prefix_panelsjson).
|
||||
|
||||
## Broker configuration
|
||||
`jfjoch_broker` requires JSON configuration files. The file is described by OpenAPI structure `jfjoch_settings` defined in `jfjoch_api.yaml` file.
|
||||
It is recommended to go through example files in the `etc/`.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// This file is auto-generated by @hey-api/openapi-ts
|
||||
|
||||
export { deleteConfigRugnuxModel, getConfigAzimInt, getConfigBraggIntegration, getConfigDarkMask, getConfigDetector, getConfigFileWriter, getConfigImageFormat, getConfigIndexing, getConfigInstrument, getConfigMask, getConfigMaskTiff, getConfigRoi, getConfigRugnux, getConfigSelectDetector, getConfigSpotFinding, getConfigUserMask, getConfigUserMaskTiff, getConfigZeromqMetadata, getConfigZeromqPreview, getDetectorStatus, getFpgaStatus, getImageBufferImageCbor, getImageBufferImageJpeg, getImageBufferImageTiff, getImageBufferStartCbor, getImageBufferStatus, getImagePusherStatus, getLiveEvents, getPreviewPedestalTiff, getPreviewPlot, getPreviewPlotBin, getResultScan, getRugnuxMapCcp4, getRugnuxModelCif, getRugnuxStatus, getStatistics, getStatisticsCalibration, getStatisticsDataCollection, getStatus, getVersion, getXfelEventCode, getXfelPulseId, type Options, postCancel, postConfigImageFormatConversion, postConfigImageFormatRaw, postDeactivate, postImageBufferClear, postInitialize, postPedestal, postStart, postTrigger, postWaitTillDone, postWaitUntilRunning, putConfigAzimInt, putConfigBraggIntegration, putConfigDarkMask, putConfigDetector, putConfigFileWriter, putConfigImageFormat, putConfigIndexing, putConfigInstrument, putConfigInternalGeneratorImage, putConfigInternalGeneratorImageTiff, putConfigRoi, putConfigRugnux, putConfigRugnuxModel, putConfigSelectDetector, putConfigSpotFinding, putConfigUserMask, putConfigUserMaskTiff, putConfigZeromqMetadata, putConfigZeromqPreview } from './sdk.gen';
|
||||
export { type azim_int_settings, azint_unit, type background, type binning, type bragg_integration_settings, type broker_status, type calibration_statistics, type ClientOptions, color_scale, type crystal_lattice, type dark_mask_settings, type dataset_settings, type dataset_settingsWritable, type deleteConfigRugnuxModelData, type deleteConfigRugnuxModelError, type deleteConfigRugnuxModelErrors, type deleteConfigRugnuxModelResponses, type detector, type detector_list, type detector_list_element, type detector_module, detector_module_direction, detector_power_state, type detector_selection, type detector_settings, detector_state, type detector_status, detector_timing, detector_type, type error_message, type experimental_coord, file_writer_format, type file_writer_settings, type fill_value, type fpga_status, geom_refinement_algorithm, type getConfigAzimIntData, type getConfigAzimIntResponse, type getConfigAzimIntResponses, type getConfigBraggIntegrationData, type getConfigBraggIntegrationResponse, type getConfigBraggIntegrationResponses, type getConfigDarkMaskData, type getConfigDarkMaskResponse, type getConfigDarkMaskResponses, type getConfigDetectorData, type getConfigDetectorResponse, type getConfigDetectorResponses, type getConfigFileWriterData, type getConfigFileWriterResponse, type getConfigFileWriterResponses, type getConfigImageFormatData, type getConfigImageFormatResponse, type getConfigImageFormatResponses, type getConfigIndexingData, type getConfigIndexingResponse, type getConfigIndexingResponses, type getConfigInstrumentData, type getConfigInstrumentResponse, type getConfigInstrumentResponses, type getConfigMaskData, type getConfigMaskResponse, type getConfigMaskResponses, type getConfigMaskTiffData, type getConfigMaskTiffResponse, type getConfigMaskTiffResponses, type getConfigRoiData, type getConfigRoiResponse, type getConfigRoiResponses, type getConfigRugnuxData, type getConfigRugnuxResponse, type getConfigRugnuxResponses, type getConfigSelectDetectorData, type getConfigSelectDetectorResponse, type getConfigSelectDetectorResponses, type getConfigSpotFindingData, type getConfigSpotFindingResponse, type getConfigSpotFindingResponses, type getConfigUserMaskData, type getConfigUserMaskResponse, type getConfigUserMaskResponses, type getConfigUserMaskTiffData, type getConfigUserMaskTiffResponse, type getConfigUserMaskTiffResponses, type getConfigZeromqMetadataData, type getConfigZeromqMetadataResponse, type getConfigZeromqMetadataResponses, type getConfigZeromqPreviewData, type getConfigZeromqPreviewResponse, type getConfigZeromqPreviewResponses, type getDetectorStatusData, type getDetectorStatusError, type getDetectorStatusErrors, type getDetectorStatusResponse, type getDetectorStatusResponses, type getFpgaStatusData, type getFpgaStatusResponse, type getFpgaStatusResponses, type getImageBufferImageCborData, type getImageBufferImageCborError, type getImageBufferImageCborErrors, type getImageBufferImageCborResponse, type getImageBufferImageCborResponses, type getImageBufferImageJpegData, type getImageBufferImageJpegError, type getImageBufferImageJpegErrors, type getImageBufferImageJpegResponse, type getImageBufferImageJpegResponses, type getImageBufferImageTiffData, type getImageBufferImageTiffError, type getImageBufferImageTiffErrors, type getImageBufferImageTiffResponse, type getImageBufferImageTiffResponses, type getImageBufferStartCborData, type getImageBufferStartCborError, type getImageBufferStartCborErrors, type getImageBufferStartCborResponse, type getImageBufferStartCborResponses, type getImageBufferStatusData, type getImageBufferStatusError, type getImageBufferStatusErrors, type getImageBufferStatusResponse, type getImageBufferStatusResponses, type getImagePusherStatusData, type getImagePusherStatusError, type getImagePusherStatusErrors, type getImagePusherStatusResponse, type getImagePusherStatusResponses, type getLiveEventsData, type getLiveEventsError, type getLiveEventsErrors, type getLiveEventsResponse, type getLiveEventsResponses, type getPreviewPedestalTiffData, type getPreviewPedestalTiffErrors, type getPreviewPedestalTiffResponse, type getPreviewPedestalTiffResponses, type getPreviewPlotBinData, type getPreviewPlotBinError, type getPreviewPlotBinErrors, type getPreviewPlotBinResponse, type getPreviewPlotBinResponses, type getPreviewPlotData, type getPreviewPlotError, type getPreviewPlotErrors, type getPreviewPlotResponse, type getPreviewPlotResponses, type getResultScanData, type getResultScanError, type getResultScanErrors, type getResultScanResponse, type getResultScanResponses, type getRugnuxMapCcp4Data, type getRugnuxMapCcp4Error, type getRugnuxMapCcp4Errors, type getRugnuxMapCcp4Response, type getRugnuxMapCcp4Responses, type getRugnuxModelCifData, type getRugnuxModelCifError, type getRugnuxModelCifErrors, type getRugnuxModelCifResponse, type getRugnuxModelCifResponses, type getRugnuxStatusData, type getRugnuxStatusError, type getRugnuxStatusErrors, type getRugnuxStatusResponse,
Line truncated
|
||||
export { type azim_int_settings, azint_unit, type background, type binning, type bragg_integration_settings, type broker_status, type calibration_statistics, type ClientOptions, color_scale, type crystal_lattice, type dark_mask_settings, type dataset_settings, type dataset_settingsWritable, type deleteConfigRugnuxModelData, type deleteConfigRugnuxModelError, type deleteConfigRugnuxModelErrors, type deleteConfigRugnuxModelResponses, type detector, type detector_list, type detector_list_element, type detector_module, detector_module_direction, type detector_module_placement, detector_power_state, type detector_selection, type detector_settings, detector_state, type detector_status, detector_timing, detector_type, type error_message, type experimental_coord, file_writer_format, type file_writer_settings, type fill_value, type fpga_status, geom_refinement_algorithm, type getConfigAzimIntData, type getConfigAzimIntResponse, type getConfigAzimIntResponses, type getConfigBraggIntegrationData, type getConfigBraggIntegrationResponse, type getConfigBraggIntegrationResponses, type getConfigDarkMaskData, type getConfigDarkMaskResponse, type getConfigDarkMaskResponses, type getConfigDetectorData, type getConfigDetectorResponse, type getConfigDetectorResponses, type getConfigFileWriterData, type getConfigFileWriterResponse, type getConfigFileWriterResponses, type getConfigImageFormatData, type getConfigImageFormatResponse, type getConfigImageFormatResponses, type getConfigIndexingData, type getConfigIndexingResponse, type getConfigIndexingResponses, type getConfigInstrumentData, type getConfigInstrumentResponse, type getConfigInstrumentResponses, type getConfigMaskData, type getConfigMaskResponse, type getConfigMaskResponses, type getConfigMaskTiffData, type getConfigMaskTiffResponse, type getConfigMaskTiffResponses, type getConfigRoiData, type getConfigRoiResponse, type getConfigRoiResponses, type getConfigRugnuxData, type getConfigRugnuxResponse, type getConfigRugnuxResponses, type getConfigSelectDetectorData, type getConfigSelectDetectorResponse, type getConfigSelectDetectorResponses, type getConfigSpotFindingData, type getConfigSpotFindingResponse, type getConfigSpotFindingResponses, type getConfigUserMaskData, type getConfigUserMaskResponse, type getConfigUserMaskResponses, type getConfigUserMaskTiffData, type getConfigUserMaskTiffResponse, type getConfigUserMaskTiffResponses, type getConfigZeromqMetadataData, type getConfigZeromqMetadataResponse, type getConfigZeromqMetadataResponses, type getConfigZeromqPreviewData, type getConfigZeromqPreviewResponse, type getConfigZeromqPreviewResponses, type getDetectorStatusData, type getDetectorStatusError, type getDetectorStatusErrors, type getDetectorStatusResponse, type getDetectorStatusResponses, type getFpgaStatusData, type getFpgaStatusResponse, type getFpgaStatusResponses, type getImageBufferImageCborData, type getImageBufferImageCborError, type getImageBufferImageCborErrors, type getImageBufferImageCborResponse, type getImageBufferImageCborResponses, type getImageBufferImageJpegData, type getImageBufferImageJpegError, type getImageBufferImageJpegErrors, type getImageBufferImageJpegResponse, type getImageBufferImageJpegResponses, type getImageBufferImageTiffData, type getImageBufferImageTiffError, type getImageBufferImageTiffErrors, type getImageBufferImageTiffResponse, type getImageBufferImageTiffResponses, type getImageBufferStartCborData, type getImageBufferStartCborError, type getImageBufferStartCborErrors, type getImageBufferStartCborResponse, type getImageBufferStartCborResponses, type getImageBufferStatusData, type getImageBufferStatusError, type getImageBufferStatusErrors, type getImageBufferStatusResponse, type getImageBufferStatusResponses, type getImagePusherStatusData, type getImagePusherStatusError, type getImagePusherStatusErrors, type getImagePusherStatusResponse, type getImagePusherStatusResponses, type getLiveEventsData, type getLiveEventsError, type getLiveEventsErrors, type getLiveEventsResponse, type getLiveEventsResponses, type getPreviewPedestalTiffData, type getPreviewPedestalTiffErrors, type getPreviewPedestalTiffResponse, type getPreviewPedestalTiffResponses, type getPreviewPlotBinData, type getPreviewPlotBinError, type getPreviewPlotBinErrors, type getPreviewPlotBinResponse, type getPreviewPlotBinResponses, type getPreviewPlotData, type getPreviewPlotError, type getPreviewPlotErrors, type getPreviewPlotResponse, type getPreviewPlotResponses, type getResultScanData, type getResultScanError, type getResultScanErrors, type getResultScanResponse, type getResultScanResponses, type getRugnuxMapCcp4Data, type getRugnuxMapCcp4Error, type getRugnuxMapCcp4Errors, type getRugnuxMapCcp4Response, type getRugnuxMapCcp4Responses, type getRugnuxModelCifData, type getRugnuxModelCifError, type getRugnuxModelCifErrors, type getRugnuxModelCifResponse, type getRugnuxModelCifResponses, type getRugnuxStatusData, type getRugnuxStatusError, type getRugnuxStatusErrorsLine truncated
|
||||
@@ -393,6 +393,16 @@ export type dataset_settings = {
|
||||
* When set to true, `/start` will not wait for detector and Jungfraujoch to be ready for the measurement.
|
||||
*/
|
||||
async_start?: boolean;
|
||||
/**
|
||||
* Where the detector's modules are for this dataset, as rugnux measures them (`rugnux --panel-refine`
|
||||
* writes it as `<prefix>_panels.json`; the file's `module_geometry` array goes here).
|
||||
* Each entry names its module by the module's box in the assembled image (`x0`, `y0`, `width`,
|
||||
* `height`), in any order; every module must be named once, else `/start` fails with 400.
|
||||
* The image is assembled as configured; the placement goes into the start message and the
|
||||
* NXmx `module_NN` groups, and the analysis takes it. Omitted or empty: modules as assembled.
|
||||
*
|
||||
*/
|
||||
module_geometry?: Array<detector_module_placement>;
|
||||
xray_fluorescence_spectrum?: {
|
||||
/**
|
||||
* X-ray fluorescence scan energy for each measurement point
|
||||
@@ -1664,6 +1674,27 @@ export type detector_module = {
|
||||
slow_axis: detector_module_direction;
|
||||
};
|
||||
|
||||
/**
|
||||
* One module of the assembled image and where it lies on the detector plane, in pixels of the
|
||||
* assembled image (a pixel's centre at its index). Image pixel (x, y) of the module, with
|
||||
* x0 <= x < x0 + width and y0 <= y < y0 + height, lies on the plane at
|
||||
* (ox, oy) + (x - x0) (fx, fy) + (y - y0) (sx, sy). As assembled: ox = x0, oy = y0, (fx, fy) = (1, 0),
|
||||
* (sx, sy) = (0, 1).
|
||||
*
|
||||
*/
|
||||
export type detector_module_placement = {
|
||||
x0: number;
|
||||
y0: number;
|
||||
width: number;
|
||||
height: number;
|
||||
ox: number;
|
||||
oy: number;
|
||||
fx: number;
|
||||
fy: number;
|
||||
sx: number;
|
||||
sy: number;
|
||||
};
|
||||
|
||||
export type detector = {
|
||||
description: string;
|
||||
serial_number?: string;
|
||||
@@ -2485,6 +2516,16 @@ export type dataset_settingsWritable = {
|
||||
* When set to true, `/start` will not wait for detector and Jungfraujoch to be ready for the measurement.
|
||||
*/
|
||||
async_start?: boolean;
|
||||
/**
|
||||
* Where the detector's modules are for this dataset, as rugnux measures them (`rugnux --panel-refine`
|
||||
* writes it as `<prefix>_panels.json`; the file's `module_geometry` array goes here).
|
||||
* Each entry names its module by the module's box in the assembled image (`x0`, `y0`, `width`,
|
||||
* `height`), in any order; every module must be named once, else `/start` fails with 400.
|
||||
* The image is assembled as configured; the placement goes into the start message and the
|
||||
* NXmx `module_NN` groups, and the analysis takes it. Omitted or empty: modules as assembled.
|
||||
*
|
||||
*/
|
||||
module_geometry?: Array<detector_module_placement>;
|
||||
/**
|
||||
* Bearer tokens that protect this dataset: any number, all equivalent (e.g. a beamline
|
||||
* secret and a dataset-specific user secret). While set, the endpoints marked with the
|
||||
|
||||
+115
-92
@@ -107,80 +107,6 @@ export const zUnitCell = z.object({
|
||||
gamma: z.number().gte(0).lte(360)
|
||||
});
|
||||
|
||||
export const zDatasetSettings = z.object({
|
||||
images_per_trigger: z.coerce.bigint().gte(BigInt(1)).max(BigInt('9223372036854775807'), { error: 'Invalid value: Expected int64 to be <= 9223372036854775807' }).optional().default(BigInt(1)),
|
||||
ntrigger: z.coerce.bigint().gte(BigInt(1)).max(BigInt('9223372036854775807'), { error: 'Invalid value: Expected int64 to be <= 9223372036854775807' }).optional().default(BigInt(1)),
|
||||
image_time_us: z.coerce.bigint().gte(BigInt(0)).max(BigInt('9223372036854775807'), { error: 'Invalid value: Expected int64 to be <= 9223372036854775807' }).optional(),
|
||||
beam_x_pxl: z.number(),
|
||||
beam_y_pxl: z.number(),
|
||||
detector_distance_mm: z.number().gte(0),
|
||||
incident_energy_keV: z.number().gte(0.001).lte(500),
|
||||
file_prefix: z.string().optional().default(''),
|
||||
images_per_file: z.coerce.bigint().gte(BigInt(1)).max(BigInt('9223372036854775807'), { error: 'Invalid value: Expected int64 to be <= 9223372036854775807' }).optional(),
|
||||
space_group_number: z.coerce.bigint().gte(BigInt(1)).lte(BigInt(230)).optional(),
|
||||
sample_name: z.string().optional().default(''),
|
||||
compression: z.enum([
|
||||
'bslz4',
|
||||
'bszstd',
|
||||
'bszstd_rle',
|
||||
'bszstd_rlehuf',
|
||||
'none'
|
||||
]).optional().default('bslz4'),
|
||||
total_flux: z.number().optional(),
|
||||
transmission: z.number().gte(0).lte(1).optional(),
|
||||
beam_size_x_um: z.number().gte(0).optional(),
|
||||
beam_size_y_um: z.number().gte(0).optional(),
|
||||
goniometer: zRotationAxis.optional(),
|
||||
grid_scan: zGridScan.optional(),
|
||||
header_appendix: z.unknown().optional(),
|
||||
image_appendix: z.unknown().optional(),
|
||||
data_reduction_factor_serialmx: z.number().gte(0).lte(1).optional().default(1),
|
||||
pixel_value_low_threshold: z.coerce.bigint().gte(BigInt(0)).max(BigInt('9223372036854775807'), { error: 'Invalid value: Expected int64 to be <= 9223372036854775807' }).optional(),
|
||||
run_number: z.coerce.bigint().gte(BigInt(0)).max(BigInt('9223372036854775807'), { error: 'Invalid value: Expected int64 to be <= 9223372036854775807' }).optional(),
|
||||
run_name: z.string().optional(),
|
||||
experiment_group: z.string().optional(),
|
||||
poisson_compression: z.coerce.bigint().gte(BigInt(0)).lte(BigInt(16)).optional(),
|
||||
write_nxmx_hdf5_master: z.boolean().optional().default(true),
|
||||
save_calibration: z.boolean().optional(),
|
||||
polarization_factor: z.number().gte(-1).lte(1).optional(),
|
||||
ring_current_mA: z.number().gte(0).optional(),
|
||||
sample_temperature_K: z.number().gte(0).optional(),
|
||||
poni_rot1_rad: z.number().gte(-6.28318530718).lte(6.28318530718).optional().default(0),
|
||||
poni_rot2_rad: z.number().gte(-6.28318530718).lte(6.28318530718).optional().default(0),
|
||||
poni_rot3_rad: z.number().gte(-6.28318530718).lte(6.28318530718).optional().default(0),
|
||||
unit_cell: zUnitCell.optional(),
|
||||
spot_finding: z.boolean().optional().default(true),
|
||||
smargon: z.object({
|
||||
phi_deg: z.number(),
|
||||
chi_deg: z.number(),
|
||||
phi_axis: z.tuple([
|
||||
z.number(),
|
||||
z.number(),
|
||||
z.number()
|
||||
]).optional().default([
|
||||
1,
|
||||
0,
|
||||
0
|
||||
]),
|
||||
chi_axis: z.tuple([
|
||||
z.number(),
|
||||
z.number(),
|
||||
z.number()
|
||||
]).optional().default([
|
||||
0,
|
||||
0,
|
||||
1
|
||||
])
|
||||
}).optional(),
|
||||
max_spot_count: z.int().gte(10).lte(2000).optional().default(250),
|
||||
detect_ice_rings: z.boolean().optional(),
|
||||
async_start: z.boolean().optional().default(false),
|
||||
xray_fluorescence_spectrum: z.object({
|
||||
energy_eV: z.array(z.number()),
|
||||
data: z.array(z.number())
|
||||
}).optional()
|
||||
});
|
||||
|
||||
/**
|
||||
* NoFileWritten - no files are written at all
|
||||
* NXmxOnlyData - only data files are written, no master file
|
||||
@@ -517,24 +443,6 @@ export const zPowderCalibrationQuality = z.object({
|
||||
spot_cross_check: zPowderCalibrationSpotCheck.optional()
|
||||
});
|
||||
|
||||
/**
|
||||
* The result of a powder-ring detector calibration - what rugnux --mode calibration writes as
|
||||
* <prefix>.json, and what a calibration run over the image buffer would return.
|
||||
*
|
||||
* dataset_settings holds the geometry and nothing else, under the property names this API gives
|
||||
* them, so it can be POSTed or merged without translating a field. Its beam_x_pxl/beam_y_pxl is
|
||||
* the PONI, the foot of the perpendicular from the sample; where the beam actually lands is
|
||||
* calibration.direct_beam_x_pxl. The poni_rot*_rad are present whenever any of them is non-zero
|
||||
* and absent when all are zero, since a body omitting them states a FLAT detector rather than an
|
||||
* unstated one.
|
||||
*
|
||||
*/
|
||||
export const zPowderCalibrationOutput = z.object({
|
||||
dataset_settings: zDatasetSettings,
|
||||
calibration: zPowderCalibrationQuality.optional(),
|
||||
jfjoch_version: z.string().optional()
|
||||
});
|
||||
|
||||
export const zCalibrationStatistics = z.array(z.object({
|
||||
module_number: z.coerce.bigint().min(BigInt('-9223372036854775808'), { error: 'Invalid value: Expected int64 to be >= -9223372036854775808' }).max(BigInt('9223372036854775807'), { error: 'Invalid value: Expected int64 to be <= 9223372036854775807' }),
|
||||
storage_cell_number: z.coerce.bigint().min(BigInt('-9223372036854775808'), { error: 'Invalid value: Expected int64 to be >= -9223372036854775808' }).max(BigInt('9223372036854775807'), { error: 'Invalid value: Expected int64 to be <= 9223372036854775807' }),
|
||||
@@ -729,6 +637,120 @@ export const zDetectorModule = z.object({
|
||||
slow_axis: zDetectorModuleDirection
|
||||
});
|
||||
|
||||
/**
|
||||
* One module of the assembled image and where it lies on the detector plane, in pixels of the
|
||||
* assembled image (a pixel's centre at its index). Image pixel (x, y) of the module, with
|
||||
* x0 <= x < x0 + width and y0 <= y < y0 + height, lies on the plane at
|
||||
* (ox, oy) + (x - x0) (fx, fy) + (y - y0) (sx, sy). As assembled: ox = x0, oy = y0, (fx, fy) = (1, 0),
|
||||
* (sx, sy) = (0, 1).
|
||||
*
|
||||
*/
|
||||
export const zDetectorModulePlacement = z.object({
|
||||
x0: z.coerce.bigint().gte(BigInt(0)).max(BigInt('9223372036854775807'), { error: 'Invalid value: Expected int64 to be <= 9223372036854775807' }),
|
||||
y0: z.coerce.bigint().gte(BigInt(0)).max(BigInt('9223372036854775807'), { error: 'Invalid value: Expected int64 to be <= 9223372036854775807' }),
|
||||
width: z.coerce.bigint().gte(BigInt(1)).max(BigInt('9223372036854775807'), { error: 'Invalid value: Expected int64 to be <= 9223372036854775807' }),
|
||||
height: z.coerce.bigint().gte(BigInt(1)).max(BigInt('9223372036854775807'), { error: 'Invalid value: Expected int64 to be <= 9223372036854775807' }),
|
||||
ox: z.number(),
|
||||
oy: z.number(),
|
||||
fx: z.number(),
|
||||
fy: z.number(),
|
||||
sx: z.number(),
|
||||
sy: z.number()
|
||||
});
|
||||
|
||||
export const zDatasetSettings = z.object({
|
||||
images_per_trigger: z.coerce.bigint().gte(BigInt(1)).max(BigInt('9223372036854775807'), { error: 'Invalid value: Expected int64 to be <= 9223372036854775807' }).optional().default(BigInt(1)),
|
||||
ntrigger: z.coerce.bigint().gte(BigInt(1)).max(BigInt('9223372036854775807'), { error: 'Invalid value: Expected int64 to be <= 9223372036854775807' }).optional().default(BigInt(1)),
|
||||
image_time_us: z.coerce.bigint().gte(BigInt(0)).max(BigInt('9223372036854775807'), { error: 'Invalid value: Expected int64 to be <= 9223372036854775807' }).optional(),
|
||||
beam_x_pxl: z.number(),
|
||||
beam_y_pxl: z.number(),
|
||||
detector_distance_mm: z.number().gte(0),
|
||||
incident_energy_keV: z.number().gte(0.001).lte(500),
|
||||
file_prefix: z.string().optional().default(''),
|
||||
images_per_file: z.coerce.bigint().gte(BigInt(1)).max(BigInt('9223372036854775807'), { error: 'Invalid value: Expected int64 to be <= 9223372036854775807' }).optional(),
|
||||
space_group_number: z.coerce.bigint().gte(BigInt(1)).lte(BigInt(230)).optional(),
|
||||
sample_name: z.string().optional().default(''),
|
||||
compression: z.enum([
|
||||
'bslz4',
|
||||
'bszstd',
|
||||
'bszstd_rle',
|
||||
'bszstd_rlehuf',
|
||||
'none'
|
||||
]).optional().default('bslz4'),
|
||||
total_flux: z.number().optional(),
|
||||
transmission: z.number().gte(0).lte(1).optional(),
|
||||
beam_size_x_um: z.number().gte(0).optional(),
|
||||
beam_size_y_um: z.number().gte(0).optional(),
|
||||
goniometer: zRotationAxis.optional(),
|
||||
grid_scan: zGridScan.optional(),
|
||||
header_appendix: z.unknown().optional(),
|
||||
image_appendix: z.unknown().optional(),
|
||||
data_reduction_factor_serialmx: z.number().gte(0).lte(1).optional().default(1),
|
||||
pixel_value_low_threshold: z.coerce.bigint().gte(BigInt(0)).max(BigInt('9223372036854775807'), { error: 'Invalid value: Expected int64 to be <= 9223372036854775807' }).optional(),
|
||||
run_number: z.coerce.bigint().gte(BigInt(0)).max(BigInt('9223372036854775807'), { error: 'Invalid value: Expected int64 to be <= 9223372036854775807' }).optional(),
|
||||
run_name: z.string().optional(),
|
||||
experiment_group: z.string().optional(),
|
||||
poisson_compression: z.coerce.bigint().gte(BigInt(0)).lte(BigInt(16)).optional(),
|
||||
write_nxmx_hdf5_master: z.boolean().optional().default(true),
|
||||
save_calibration: z.boolean().optional(),
|
||||
polarization_factor: z.number().gte(-1).lte(1).optional(),
|
||||
ring_current_mA: z.number().gte(0).optional(),
|
||||
sample_temperature_K: z.number().gte(0).optional(),
|
||||
poni_rot1_rad: z.number().gte(-6.28318530718).lte(6.28318530718).optional().default(0),
|
||||
poni_rot2_rad: z.number().gte(-6.28318530718).lte(6.28318530718).optional().default(0),
|
||||
poni_rot3_rad: z.number().gte(-6.28318530718).lte(6.28318530718).optional().default(0),
|
||||
unit_cell: zUnitCell.optional(),
|
||||
spot_finding: z.boolean().optional().default(true),
|
||||
smargon: z.object({
|
||||
phi_deg: z.number(),
|
||||
chi_deg: z.number(),
|
||||
phi_axis: z.tuple([
|
||||
z.number(),
|
||||
z.number(),
|
||||
z.number()
|
||||
]).optional().default([
|
||||
1,
|
||||
0,
|
||||
0
|
||||
]),
|
||||
chi_axis: z.tuple([
|
||||
z.number(),
|
||||
z.number(),
|
||||
z.number()
|
||||
]).optional().default([
|
||||
0,
|
||||
0,
|
||||
1
|
||||
])
|
||||
}).optional(),
|
||||
max_spot_count: z.int().gte(10).lte(2000).optional().default(250),
|
||||
detect_ice_rings: z.boolean().optional(),
|
||||
async_start: z.boolean().optional().default(false),
|
||||
module_geometry: z.array(zDetectorModulePlacement).optional(),
|
||||
xray_fluorescence_spectrum: z.object({
|
||||
energy_eV: z.array(z.number()),
|
||||
data: z.array(z.number())
|
||||
}).optional()
|
||||
});
|
||||
|
||||
/**
|
||||
* The result of a powder-ring detector calibration - what rugnux --mode calibration writes as
|
||||
* <prefix>.json, and what a calibration run over the image buffer would return.
|
||||
*
|
||||
* dataset_settings holds the geometry and nothing else, under the property names this API gives
|
||||
* them, so it can be POSTed or merged without translating a field. Its beam_x_pxl/beam_y_pxl is
|
||||
* the PONI, the foot of the perpendicular from the sample; where the beam actually lands is
|
||||
* calibration.direct_beam_x_pxl. The poni_rot*_rad are present whenever any of them is non-zero
|
||||
* and absent when all are zero, since a body omitting them states a FLAT detector rather than an
|
||||
* unstated one.
|
||||
*
|
||||
*/
|
||||
export const zPowderCalibrationOutput = z.object({
|
||||
dataset_settings: zDatasetSettings,
|
||||
calibration: zPowderCalibrationQuality.optional(),
|
||||
jfjoch_version: z.string().optional()
|
||||
});
|
||||
|
||||
export const zDetector = z.object({
|
||||
description: z.string().min(1),
|
||||
serial_number: z.string().min(1).optional().default('Unknown'),
|
||||
@@ -1077,6 +1099,7 @@ export const zDatasetSettingsWritable = z.object({
|
||||
max_spot_count: z.int().gte(10).lte(2000).optional().default(250),
|
||||
detect_ice_rings: z.boolean().optional(),
|
||||
async_start: z.boolean().optional().default(false),
|
||||
module_geometry: z.array(zDetectorModulePlacement).optional(),
|
||||
tokens: z.array(z.string().min(1)).optional(),
|
||||
xray_fluorescence_spectrum: z.object({
|
||||
energy_eV: z.array(z.number()),
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
#include "../broker/JFJochStateMachine.h"
|
||||
#include "../acquisition_device/HLSSimulatedDevice.h"
|
||||
#include "../receiver/JFJochReceiverService.h"
|
||||
#include "../broker/OpenAPIConvert.h"
|
||||
#include "Dataset_settings.h"
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
@@ -625,3 +628,88 @@ TEST_CASE("JFJochStateMachine_DarkMaskCancelIsNotASuccess") {
|
||||
REQUIRE(status.message_severity == BrokerStatus::MessageSeverity::Error);
|
||||
REQUIRE_THAT(status.message.value_or(""), Catch::Matchers::ContainsSubstring("cancelled"));
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Keeps the start message of the run and refuses it, so nothing is collected.
|
||||
class StartMessageCapture : public ImagePusher {
|
||||
public:
|
||||
StartMessage start;
|
||||
void Preflight(StartMessage &message) override {
|
||||
start = message;
|
||||
throw JFJochException(JFJochExceptionCategory::FileWriteError, "captured");
|
||||
}
|
||||
void StartDataCollection(StartMessage &) override {}
|
||||
bool EndDataCollection(const EndMessage &) override { return true; }
|
||||
bool SendImage(const uint8_t *, size_t, int64_t) override { return true; }
|
||||
bool SendCalibration(const CompressedImage &) override { return true; }
|
||||
std::string PrintSetup() const override { return "StartMessageCapture"; }
|
||||
ImagePusherType GetType() const override { return ImagePusherType::Test; }
|
||||
};
|
||||
}
|
||||
|
||||
// A /start carrying a module placement (rugnux's <prefix>_panels.json) puts the modules where it says into
|
||||
// the start message - and so into the NXmx module groups - whatever order it lists them in; one naming a
|
||||
// module the detector does not have is refused.
|
||||
TEST_CASE("JFJochStateMachine_StartModuleGeometry") {
|
||||
Logger logger("JFJochStateMachine_StartModuleGeometry");
|
||||
|
||||
DiffractionExperiment experiment(DetJF(2));
|
||||
experiment.Conversion().PedestalG0Frames(0).NumTriggers(1).UseInternalPacketGenerator(true)
|
||||
.ImagesPerTrigger(4).IncidentEnergy_keV(12.4).FilePrefix("module_geometry");
|
||||
|
||||
AcquisitionDeviceGroup aq_devices;
|
||||
for (int i = 0; i < experiment.GetDataStreamsNum(); i++)
|
||||
aq_devices.Add(std::make_unique<HLSSimulatedDevice>(i, 64));
|
||||
StartMessageCapture pusher;
|
||||
JFJochReceiverService receiver_service(aq_devices, logger, pusher);
|
||||
JFJochServices services(logger);
|
||||
services.Receiver(&receiver_service);
|
||||
JFJochStateMachine state_machine(experiment, services, logger);
|
||||
state_machine.AddDetectorSetup(DetJF(2));
|
||||
state_machine.DebugOnly_SetState(JFJochState::Idle);
|
||||
|
||||
const auto assembled = ModulesAsImaged(experiment.GetDetectorSetup().GetGeometry()).Get();
|
||||
REQUIRE(assembled.size() == 2);
|
||||
// Module 1 moved by (+0.4, -1.3) px and turned slightly, module 0 as assembled - listed 1 first.
|
||||
const auto entry = [](const DetectorModule &m, double dx, double dy, double turn) {
|
||||
return nlohmann::json{{"x0", m.x0}, {"y0", m.y0}, {"width", m.width}, {"height", m.height},
|
||||
{"ox", m.ox + dx}, {"oy", m.oy + dy}, {"fx", std::cos(turn)}, {"fy", std::sin(turn)},
|
||||
{"sx", -std::sin(turn)}, {"sy", std::cos(turn)}};
|
||||
};
|
||||
nlohmann::json body = {{"beam_x_pxl", 500}, {"beam_y_pxl", 500}, {"detector_distance_mm", 100},
|
||||
{"incident_energy_keV", 12.4}, {"images_per_trigger", 4},
|
||||
{"file_prefix", "module_geometry"}};
|
||||
body["module_geometry"] = {entry(assembled[1], 0.4, -1.3, 0.001), entry(assembled[0], 0, 0, 0)};
|
||||
|
||||
org::openapitools::server::model::Dataset_settings model;
|
||||
from_json(body, model);
|
||||
std::stringstream msg;
|
||||
REQUIRE(model.validate(msg));
|
||||
|
||||
REQUIRE_THROWS_WITH(state_machine.Start(Convert(model)), Catch::Matchers::ContainsSubstring("captured"));
|
||||
const auto &modules = pusher.start.detector_modules;
|
||||
REQUIRE(modules.size() == 2);
|
||||
CHECK(modules[0].x0 == assembled[0].x0);
|
||||
CHECK(modules[0].ox == Catch::Approx(assembled[0].ox));
|
||||
CHECK(modules[1].x0 == assembled[1].x0);
|
||||
CHECK(modules[1].y0 == assembled[1].y0);
|
||||
CHECK(modules[1].ox == Catch::Approx(assembled[1].ox + 0.4));
|
||||
CHECK(modules[1].oy == Catch::Approx(assembled[1].oy - 1.3));
|
||||
CHECK(modules[1].fy == Catch::Approx(std::sin(0.001)));
|
||||
|
||||
// Without it, the modules as assembled.
|
||||
body.erase("module_geometry");
|
||||
model = {};
|
||||
from_json(body, model);
|
||||
REQUIRE_THROWS_WITH(state_machine.Start(Convert(model)), Catch::Matchers::ContainsSubstring("captured"));
|
||||
REQUIRE(pusher.start.detector_modules.size() == 2);
|
||||
CHECK(pusher.start.detector_modules[1].oy == Catch::Approx(assembled[1].oy));
|
||||
|
||||
// A module the detector does not have.
|
||||
DetectorModule elsewhere = assembled[1];
|
||||
elsewhere.y0 += 1;
|
||||
body["module_geometry"] = {entry(elsewhere, 0, 0, 0), entry(assembled[0], 0, 0, 0)};
|
||||
from_json(body, model);
|
||||
REQUIRE_THROWS_WITH(state_machine.Start(Convert(model)),
|
||||
Catch::Matchers::ContainsSubstring("is not a module of this detector"));
|
||||
}
|
||||
Reference in new issue
Block a user