viewer: download/upload ROIs to the broker
Add Download and "Upload to server" buttons to the ROI panel. The HTTP reader gains GetROIDefinitions (GET /config/roi) and UploadROIDefinitions (PUT /config/roi), converting between ROIDefinition and the generated Roi_definitions model (including the optional azimuthal phi sector), the same shapes OpenAPIConvert uses on the server. Download applies the fetched ROIs through SetROIDefinition; upload pushes the current ones. Both are no-ops unless the viewer is connected to a broker. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,8 @@
|
||||
#include "../broker/gen/model/Image_buffer_status.h"
|
||||
#include "../broker/gen/model/Plots.h"
|
||||
#include "../broker/gen/model/Broker_status.h"
|
||||
#include "../broker/gen/model/Roi_definitions.h"
|
||||
#include "../common/JFJochMath.h"
|
||||
#include "../image_analysis/bragg_integration/CalcISigma.h"
|
||||
|
||||
void JFJochHttpReader::Close() {
|
||||
@@ -402,6 +404,93 @@ void JFJochHttpReader::UploadUserMask(const std::vector<uint32_t>& mask) {
|
||||
"Server rejected user mask upload");
|
||||
}
|
||||
|
||||
ROIDefinition JFJochHttpReader::GetROIDefinitions() const {
|
||||
std::unique_lock ul(http_mutex);
|
||||
if (addr.empty())
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "HTTP address not set");
|
||||
|
||||
httplib::Client cli_cmd(addr);
|
||||
auto res = cli_cmd.Get("/config/roi");
|
||||
if (!res || res->status != httplib::StatusCode::OK_200)
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Could not get ROI definitions");
|
||||
|
||||
org::openapitools::server::model::Roi_definitions input = nlohmann::json::parse(res->body);
|
||||
ROIDefinition out;
|
||||
for (const auto &i : input.getBox().getRois())
|
||||
out.boxes.emplace_back(i.getName(), i.getMinXPxl(), i.getMaxXPxl(), i.getMinYPxl(), i.getMaxYPxl());
|
||||
for (const auto &i : input.getCircle().getRois())
|
||||
out.circles.emplace_back(i.getName(), i.getCenterXPxl(), i.getCenterYPxl(), i.getRadiusPxl());
|
||||
for (const auto &i : input.getAzim().getRois()) {
|
||||
float phi_min = 0, phi_max = 0;
|
||||
if (i.phiMinDegIsSet() && i.phiMaxDegIsSet()) {
|
||||
phi_min = i.getPhiMinDeg();
|
||||
phi_max = i.getPhiMaxDeg();
|
||||
}
|
||||
const float d_min = (i.getQMaxRecipA() == 0.0f) ? 0.0f : 2.0f * static_cast<float>(PI) / i.getQMaxRecipA();
|
||||
const float d_max = (i.getQMinRecipA() == 0.0f) ? 0.0f : 2.0f * static_cast<float>(PI) / i.getQMinRecipA();
|
||||
out.azimuthal.emplace_back(i.getName(), d_min, d_max, phi_min, phi_max);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
void JFJochHttpReader::UploadROIDefinitions(const ROIDefinition &rois) const {
|
||||
std::unique_lock ul(http_mutex);
|
||||
if (addr.empty())
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "HTTP address not set");
|
||||
|
||||
namespace model = org::openapitools::server::model;
|
||||
model::Roi_definitions out;
|
||||
|
||||
model::Roi_box_list bl;
|
||||
std::vector<model::Roi_box> boxes;
|
||||
for (const auto &b : rois.boxes) {
|
||||
model::Roi_box e;
|
||||
e.setName(b.GetName());
|
||||
e.setMinXPxl(b.GetXMin()); e.setMaxXPxl(b.GetXMax());
|
||||
e.setMinYPxl(b.GetYMin()); e.setMaxYPxl(b.GetYMax());
|
||||
boxes.push_back(e);
|
||||
}
|
||||
bl.setRois(boxes);
|
||||
out.setBox(bl);
|
||||
|
||||
model::Roi_circle_list cl;
|
||||
std::vector<model::Roi_circle> circles;
|
||||
for (const auto &c : rois.circles) {
|
||||
model::Roi_circle e;
|
||||
e.setName(c.GetName());
|
||||
e.setCenterXPxl(c.GetX()); e.setCenterYPxl(c.GetY());
|
||||
e.setRadiusPxl(c.GetRadius_pxl());
|
||||
circles.push_back(e);
|
||||
}
|
||||
cl.setRois(circles);
|
||||
out.setCircle(cl);
|
||||
|
||||
model::Roi_azim_list al;
|
||||
std::vector<model::Roi_azimuthal> azim;
|
||||
for (const auto &a : rois.azimuthal) {
|
||||
model::Roi_azimuthal e;
|
||||
e.setName(a.GetName());
|
||||
e.setQMinRecipA(a.GetQMin_recipA());
|
||||
e.setQMaxRecipA(a.GetQMax_recipA());
|
||||
if (a.HasPhi()) {
|
||||
e.setPhiMinDeg(a.GetPhiMin_deg());
|
||||
e.setPhiMaxDeg(a.GetPhiMax_deg());
|
||||
}
|
||||
azim.push_back(e);
|
||||
}
|
||||
al.setRois(azim);
|
||||
out.setAzim(al);
|
||||
|
||||
nlohmann::json j = out;
|
||||
httplib::Client cli_cmd(addr);
|
||||
auto res = cli_cmd.Put("/config/roi", j.dump(), "application/json");
|
||||
if (!res)
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
||||
"Failed to connect to server to upload ROIs");
|
||||
if (res->status != httplib::StatusCode::OK_200)
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Server rejected ROI upload");
|
||||
}
|
||||
|
||||
std::vector<SpotToSave> JFJochHttpReader::ReadSpots(int64_t image) const {
|
||||
std::unique_lock ul(http_mutex);
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "JFJochReader.h"
|
||||
#include "../common/BrokerStatus.h"
|
||||
#include "../common/ImageBuffer.h"
|
||||
#include "../common/ROIDefinition.h"
|
||||
|
||||
class JFJochHttpReader : public JFJochReader {
|
||||
mutable std::mutex http_mutex;
|
||||
@@ -36,6 +37,9 @@ public:
|
||||
|
||||
void UploadUserMask(const std::vector<uint32_t>& mask);
|
||||
|
||||
[[nodiscard]] ROIDefinition GetROIDefinitions() const; // GET /config/roi
|
||||
void UploadROIDefinitions(const ROIDefinition &rois) const; // PUT /config/roi
|
||||
|
||||
BrokerStatus GetBrokerStatus() const;
|
||||
|
||||
std::shared_ptr<JFJochReaderRawImage> GetRawImage(int64_t image_number) override;
|
||||
|
||||
@@ -600,6 +600,28 @@ void JFJochImageReadingWorker::SetROIDefinition(const ROIDefinition &rois) {
|
||||
SetROIDefinition_i(rois);
|
||||
}
|
||||
|
||||
void JFJochImageReadingWorker::DownloadROIsFromServer() {
|
||||
QMutexLocker locker(&m);
|
||||
if (!http_mode)
|
||||
return;
|
||||
try {
|
||||
SetROIDefinition_i(http_reader.GetROIDefinitions());
|
||||
} catch (const std::exception &e) {
|
||||
logger.Error("Download ROIs from server failed: {}", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void JFJochImageReadingWorker::UploadROIsToServer() {
|
||||
QMutexLocker locker(&m);
|
||||
if (!http_mode)
|
||||
return;
|
||||
try {
|
||||
http_reader.UploadROIDefinitions(curr_experiment.ROI().GetROIDefinition());
|
||||
} catch (const std::exception &e) {
|
||||
logger.Error("Upload ROIs to server failed: {}", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void JFJochImageReadingWorker::SetROIDefinition_i(const ROIDefinition &rois) {
|
||||
// The worker experiment is the source of truth; the analysis ROI engine is rebuilt
|
||||
// for it so newly loaded images pick up the change automatically. From now on the
|
||||
|
||||
@@ -171,6 +171,8 @@ public slots:
|
||||
void SubtractROIFromUserMask();
|
||||
|
||||
void SetROIDefinition(const ROIDefinition &rois);
|
||||
void DownloadROIsFromServer();
|
||||
void UploadROIsToServer();
|
||||
|
||||
void SaveUserMaskTIFF(QString filename);
|
||||
void UploadUserMask();
|
||||
|
||||
@@ -131,6 +131,8 @@ JFJochViewerSidePanel::JFJochViewerSidePanel(QWidget *parent) : QWidget(parent)
|
||||
connect(this, &JFJochViewerSidePanel::imageLoaded, roi_list, &JFJochViewerROIList::loadImage);
|
||||
connect(roi_list, &JFJochViewerROIList::roisChanged, this, &JFJochViewerSidePanel::roisChanged);
|
||||
connect(roi_list, &JFJochViewerROIList::selectedROIChanged, this, &JFJochViewerSidePanel::selectedROIChanged);
|
||||
connect(roi_list, &JFJochViewerROIList::downloadROIs, this, &JFJochViewerSidePanel::downloadROIs);
|
||||
connect(roi_list, &JFJochViewerROIList::uploadROIs, this, &JFJochViewerSidePanel::uploadROIs);
|
||||
|
||||
layout->addWidget(new TitleLabel("Data analysis", this));
|
||||
|
||||
|
||||
@@ -33,6 +33,8 @@ signals:
|
||||
void showROIFill(bool input);
|
||||
void roisChanged(ROIDefinition rois);
|
||||
void selectedROIChanged(QString name);
|
||||
void downloadROIs();
|
||||
void uploadROIs();
|
||||
void findBeamCenter(const UnitCell &input, bool guess);
|
||||
void analyze();
|
||||
|
||||
|
||||
@@ -197,6 +197,10 @@ JFJochViewerWindow::JFJochViewerWindow(QWidget *parent, bool dbus, const QString
|
||||
reading_worker, &JFJochImageReadingWorker::SetROIDefinition);
|
||||
connect(viewer, &JFJochDiffractionImage::roiSelected,
|
||||
side_panel, &JFJochViewerSidePanel::selectROIInList);
|
||||
connect(side_panel, &JFJochViewerSidePanel::downloadROIs,
|
||||
reading_worker, &JFJochImageReadingWorker::DownloadROIsFromServer);
|
||||
connect(side_panel, &JFJochViewerSidePanel::uploadROIs,
|
||||
reading_worker, &JFJochImageReadingWorker::UploadROIsToServer);
|
||||
|
||||
connect(menuBar, &JFJochViewerMenu::clearUserMaskSelected,
|
||||
reading_worker, &JFJochImageReadingWorker::ClearUserMask);
|
||||
|
||||
@@ -36,6 +36,15 @@ JFJochViewerROIList::JFJochViewerROIList(QWidget *parent) : QWidget(parent) {
|
||||
connect(ren, &QPushButton::clicked, this, &JFJochViewerROIList::OnRename);
|
||||
connect(del, &QPushButton::clicked, this, &JFJochViewerROIList::OnDelete);
|
||||
|
||||
auto *server = new QHBoxLayout();
|
||||
auto *download = new QPushButton("Download", this);
|
||||
auto *upload = new QPushButton("Upload to server", this);
|
||||
server->addWidget(download);
|
||||
server->addWidget(upload);
|
||||
layout->addLayout(server);
|
||||
connect(download, &QPushButton::clicked, this, &JFJochViewerROIList::downloadROIs);
|
||||
connect(upload, &QPushButton::clicked, this, &JFJochViewerROIList::uploadROIs);
|
||||
|
||||
auto *grid = new QGridLayout();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
flabel_[i] = new QLabel("", this);
|
||||
|
||||
@@ -50,4 +50,6 @@ private slots:
|
||||
signals:
|
||||
void roisChanged(ROIDefinition rois);
|
||||
void selectedROIChanged(QString name);
|
||||
void downloadROIs(); // fetch ROIs from the connected broker
|
||||
void uploadROIs(); // push ROIs to the connected broker
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user