Files
Jungfraujoch/broker/jfjoch_broker.cpp
T
leonarski_f afbe844b20
Build Packages / Create release (push) Successful in 17s
Build Packages / build:portable:macos-arm64:nocuda (push) Successful in 4m41s
Build Packages / HDF5 consumer tests (DIALS, XDS) (push) Successful in 20m49s
Build Packages / build:portable:windows-x86_64:nocuda (push) Successful in 13m6s
Build Packages / build:portable:windows-x86_64:cuda (push) Successful in 23m44s
Build Packages / build:rugnux:linux-aarch64:cuda (push) Successful in 7m28s
Build Packages / build:portable:linux-x86_64:nocuda (push) Successful in 11m50s
Build Packages / build:portable:linux-x86_64:cuda (push) Successful in 14m9s
Build Packages / build:jfjoch:rocky8:nocuda (push) Successful in 16m25s
Build Packages / build:jfjoch:ubuntu2204:nocuda (push) Successful in 15m42s
Build Packages / build:jfjoch:rocky9:nocuda (push) Successful in 20m50s
Build Packages / build:jfjoch:ubuntu2404:nocuda (push) Successful in 20m36s
Build Packages / build:jfjoch:rocky8:cuda-sls9 (push) Successful in 22m18s
Build Packages / build:jfjoch:rocky9:cuda-sls9 (push) Successful in 23m29s
Build Packages / Generate python client (push) Successful in 30s
Build Packages / Build documentation (push) Successful in 1m10s
Build Packages / build:jfjoch:rocky8:cuda (push) Successful in 21m38s
Build Packages / build:jfjoch:ubuntu2204:cuda (push) Successful in 17m8s
Build Packages / build:jfjoch:rocky9:cuda (push) Successful in 23m10s
Build Packages / build:jfjoch:ubuntu2404:cuda (push) Successful in 19m16s
Build Packages / Unit tests (push) Successful in 2h8m4s
Build Packages / Validation Battery (push) Successful in 40m53s
v1.0.0-rc.178 (#88)
* Rugnux: Processes serial crystallography data from XFELs - SACLA (MPCCD), PAL-XFEL and European XFEL (CXI), LCLS (XTC) and SwissFEL Alvra (JUNGFRAU with a CrystFEL module geometry) - with several files or Jungfraujoch master files merged in one run (`rugnux *_master.h5`).
* Rugnux: Stills processing takes a known unit cell and space group as a starting point, finds the beam centre (and, with a cell, the detector distance) from the images, detects the saturation level in other facilities' files and refines the placement of individual detector panels.
* Rugnux: The stills report gives R_split and CC1/2 from frame half-sets, comparable with CrystFEL.
* jfjoch_viewer: Opens the same XFEL file formats.
* Validation: A stills battery checks Rugnux against deposited structures from SACLA, PAL-XFEL, LCLS, European XFEL and SwissFEL.
* Rugnux: More reliable convergence of rotation scaling, and live molecular replacement starts as soon as the model is uploaded.
* jfjoch_broker: Initialize clears the previous dataset's bearer tokens, image buffer and live Rugnux status, and a slow CUDA JIT cache is reported at start.

Reviewed-on: #88
Co-authored-by: Filip Leonarski <filip.leonarski@psi.ch>
2026-10-21 11:20:01 +02:00

158 lines
5.5 KiB
C++

// SPDX-FileCopyrightText: 2024 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
// Using OpenAPI licensed with Apache License 2.0
#include <csignal>
#include <fstream>
#include <iostream>
#include <vector>
#include <nlohmann/json.hpp>
#include "../common/CudaJitCache.h"
#include "../common/GitInfo.h"
#include "../common/Logger.h"
#include "../common/print_license.h"
#include "../detector_control/DectrisSimplonClient.h"
#include "../image_analysis/indexing/IndexerThreadPool.h"
#include "../writer/HDF5Objects.h"
#include "JFJochBrokerHttp.h"
#include "JFJochBrokerParser.h"
static httplib::Server *httpServer = nullptr;
static void sigHandler [[noreturn]] (int sig) {
switch (sig) {
case SIGINT:
case SIGQUIT:
case SIGTERM:
case SIGHUP:
default:
if (httpServer)
httpServer->stop();
break;
}
std::exit(0);
}
static void setUpUnixSignals(std::vector<int> quitSignals) {
sigset_t blocking_mask;
sigemptyset(&blocking_mask);
for (auto sig : quitSignals)
sigaddset(&blocking_mask, sig);
struct sigaction sa;
sa.sa_handler = sigHandler;
sa.sa_mask = blocking_mask;
sa.sa_flags = 0;
for (auto sig : quitSignals)
sigaction(sig, &sa, nullptr);
}
int main(int argc, char **argv) {
UsePrivateCudaJitCacheIfNeeded();
RegisterHDF5Filter();
print_license("jfjoch_broker");
if ((argc == 1) || (argc > 3)) {
std::cout << "Usage ./jfjoch_broker <JSON config> {<TCP http port>}" << std::endl;
return EXIT_FAILURE;
}
uint16_t http_port = 5232;
if (argc >= 3) http_port = static_cast<uint16_t>(std::atoi(argv[2]));
Logger logger("jfjoch_broker");
logger.Info("jfjoch_broker {} (git {}), built with {}", jfjoch_version(), jfjoch_git_sha1().substr(0, 6),
jfjoch_build_info(true));
// cuFFT's initialisation, and card 0's context, ahead of the first collection; the plan it times shows
// whether the CUDA JIT cache works - without one, every plan the analysis makes is compiled again.
LogCudaJitCache(logger, WarmUpCuFFT());
org::openapitools::server::model::Jfjoch_settings settings;
std::ifstream file(argv[1]);
try {
nlohmann::json input = nlohmann::json::parse(file);
settings = input;
settings.validate();
logger.Info("JSON configuration file read properly");
} catch (const std::exception &e) {
logger.Error("Error reading JSON configuration file: " + std::string(e.what()));
return EXIT_FAILURE;
}
if (settings.numaPolicyIsSet())
logger.Warning("NUMA policy is deprecated and ignored - processed are split over all the CPUs/GPUs in the system");
const bool verbose = settings.verboseIsSet() && settings.isVerbose();
logger.Verbose(verbose);
Logger::ServiceLevel("JFJochBroker", verbose ? spdlog::level::debug : spdlog::level::info);
// What rugnux logs as it processes is its result, and that reaches the user through the live status,
// the end message and the written files: the journal keeps its errors only, and all of it when verbose.
for (const auto &service: {"Rugnux", "ScaleOnTheFly"})
Logger::ServiceLevel(service, verbose ? spdlog::level::debug : spdlog::level::err);
std::unique_ptr<ImagePusher> image_pusher = ParseImagePusher(settings);
if (image_pusher)
logger.Info(image_pusher->PrintSetup());
std::vector<DetectorSetup> det_setup;
for (const auto &d: settings.getDetector())
det_setup.push_back(ParseDetectorSetup(d));
if (det_setup.empty()) {
logger.Error("At least one detector need to be defined in the config file");
return EXIT_FAILURE;
}
DiffractionExperiment experiment(det_setup[0]);
ParseFacilityConfiguration(settings, experiment);
SpotFindingSettings spot_finding_settings = ParseSpotFindingSettings(settings);
AcquisitionDeviceGroup aq_devices;
ParseAcquisitionDeviceGroup(settings, aq_devices);
experiment.DataStreams(aq_devices.size());
int32_t send_buffer_size_MiB = settings.getImageBufferMiB();
std::unique_ptr<JFJochReceiverService> receiver =
std::make_unique<JFJochReceiverService>(aq_devices, logger, *image_pusher, send_buffer_size_MiB);
ParseReceiverSettings(settings, *receiver);
JFJochBrokerHttp broker(experiment, spot_finding_settings);
broker.FrontendDirectory(settings.getFrontendDirectory());
for (const auto &d: det_setup)
broker.AddDetectorSetup(d);
if (receiver)
broker.Services().Receiver(receiver.get());
ParseRugnuxSettings(settings, broker.Services());
httplib::Server server;
httpServer = &server;
// Set here rather than through httplib's compile-time default: a reader following a collection
// holds one thread for the whole run (/live/events), beside the web frontend's polling.
server.new_task_queue = [] { return new httplib::ThreadPool(16, 64); };
server.Get("/", [](const httplib::Request &req, httplib::Response &res) {
res.status = 302; // Found (temporary redirect)
res.set_header("Location", "/frontend");
});
server.set_mount_point("/frontend", settings.getFrontendDirectory());
signal(SIGPIPE, SIG_IGN);
std::vector<int> sigs{SIGQUIT, SIGINT, SIGTERM, SIGHUP};
setUpUnixSignals(sigs);
broker.attach(server);
if (!server.listen("0.0.0.0", http_port)) {
logger.Error("Failed to start HTTP server");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}