Commit 4cc8642a authored by Cristina Cocho's avatar Cristina Cocho
Browse files

New SpectroPlot controller and new version of Dectris Detector controller

parent 0bdcb7a5
<module name="spectrometer">
<include path="$(NOMAD_HOME)/../NomadModules/src"/>
<controller class="spectrometer::QE65000Controller"/>
<include path="$(NOMAD_HOME)/../NomadModules/src" />
<controller class="spectrometer::QE65000Controller" />
<controller class="spectrometer::SpectroPlotController" />
</module>
\ No newline at end of file
/*
* Nomad Instrument Control Software
*
* Copyright 2011 Institut Laue-Langevin
*
* Licensed under the EUPL, Version 1.1 only (the "License");
* You may not use this work except in compliance with the Licence.
* You may obtain a copy of the Licence at:
*
* http://joinup.ec.europa.eu/software/page/eupl
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the Licence is distributed on an "AS IS" basis,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Licence for the specific language governing permissions and
* limitations under the Licence.
*/
#include "SpectroPlotController.h"
namespace spectrometer {
const std::string SpectroPlotController::TYPE = "spectro_plot";
SpectroPlotController::SpectroPlotController(const std::string& controllerName) :
ExperimentController(controllerName) {
xData0.init(this, NOSAVE, "x0_data");
yData0.init(this, NOSAVE, "y0_data");
xData1.init(this, NOSAVE, "x1_data");
yData1.init(this, NOSAVE, "y1_data");
// UVSpectro.init(this, "UV_spectro");
// FluoSpectro.init(this, "Fluo_spectro");
// Up to now the size is two
spectros.resize(2);
spectros[0].init(this, "UV_spectro");
spectros[1].init(this, "Fluo_spectro");
}
SpectroPlotController::~SpectroPlotController() {
}
void SpectroPlotController::postConfiguration() {
// spectros[0] = UVSpectro;
// spectros[1] = FluoSpectro;
registerUpdater(spectros[0]->yData, &SpectroPlotController::updateUVSpectroData, this);
registerUpdater(spectros[1]->yData, &SpectroPlotController::updateFluoSpectroData, this);
}
void SpectroPlotController::updateUVSpectroData() {
xData0.update(spectros[0]->xData());
xData0.setSize(spectros[0]->size());
xData0.sendEvent();
yData0.update(spectros[0]->yData());
yData0.setSize(spectros[0]->size());
yData0.sendEvent();
}
void SpectroPlotController::updateFluoSpectroData() {
xData1.update(spectros[1]->xData());
xData1.setSize(spectros[1]->size());
xData1.sendEvent();
yData1.update(spectros[1]->yData());
yData1.setSize(spectros[1]->size());
yData1.sendEvent();
}
}
/*
* Nomad Instrument Control Software
*
* Copyright 2011 Institut Laue-Langevin
*
* Licensed under the EUPL, Version 1.1 only (the "License");
* You may not use this work except in compliance with the Licence.
* You may obtain a copy of the Licence at:
*
* http://joinup.ec.europa.eu/software/page/eupl
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the Licence is distributed on an "AS IS" basis,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Licence for the specific language governing permissions and
* limitations under the Licence.
*/
#ifndef SRC_CONTROLLERS_SPECTROMETER_SPECTROPLOTCONTROLLER_H_
#define SRC_CONTROLLERS_SPECTROMETER_SPECTROPLOTCONTROLLER_H_
#include <Controller.h>
#include "QE65000Controller.h"
namespace spectrometer {
class SpectroPlotController : public ExperimentController {
public:
static const std::string TYPE;
SpectroPlotController(const std::string& controllerName);
virtual ~SpectroPlotController();
void postConfiguration();
ArrayProperty<float64> xData0;
ArrayProperty<float64> yData0;
ArrayProperty<float64> xData1;
ArrayProperty<float64> yData1;
private:
std::vector<ControllerPtr<QE65000Controller>> spectros;
// ControllerPtr<QE65000Controller> UVSpectro;
// ControllerPtr<QE65000Controller> FluoSpectro;
void updateUVSpectroData();
void updateFluoSpectroData();
};
}
#endif /* SRC_CONTROLLERS_SPECTROMETER_SPECTROPLOTCONTROLLER_H_ */
spectro_plot.UVPlotTitle=UV Plot
spectro_plot.UVPlotPrefix=UV Plot
spectro_plot.FluoPlotTitle=Fluo Plot
spectro_plot.FluoPlotPrefix=Fluo Plot
spectro_plot.plotXAxisLegend=Wavelenght
spectro_plot.plotYAxisLegend=
spectro_plot.UVPlotLegend=UV Plot
spectro_plot.FluoPlotLegend=Fluo Plot
\ No newline at end of file
<plotdatas>
<plotdata key="UV_data" legend_key="spectro_plot.UVPlotLegend" color="1F96C0" plugins="COMMAND|SETUP">
<dataX p_role="spectro_plot1" p_name="x0_data"/>
<dataY p_role="spectro_plot1" p_name="y0_data"/>
</plotdata>
<plotdata key="Fluo_data" legend_key="spectro_plot.FluoPlotLegend" color="1F96C0" plugins="COMMAND|SETUP">
<dataX p_role="spectro_plot1" p_name="x1_data"/>
<dataY p_role="spectro_plot1" p_name="y1_data"/>
</plotdata>
</plotdatas>
<controller_plugin_config type="spectro_plot">
<image key="SHUTTER"/>
<settings view="spectro_plotView.xml"/>
</controller_plugin_config>
<plugin>
<controller type="spectro_plot" role="spectro_plot1"/>
<plot_launcher role="spectro_plot1" data="UV_data" title="spectro_plot.UVPlotTitle" x_axis_title="spectro_plot.plotXAxisLegend" y_axis_title="spectro_plot.plotYAxisLegend" height="20"/>
<simple_label prefix="spectro_plot.UVPlotPrefix"/>
<newLine/>
<plot_launcher role="spectro_plot1" data="Fluo_data" title="spectro_plot.FluoPlotTitle" x_axis_title="spectro_plot.plotXAxisLegend" y_axis_title="spectro_plot.plotYAxisLegend" height="20"/>
<simple_label prefix="spectro_plot.FluoPlotPrefix"/>
<newLine/>
</plugin>
\ No newline at end of file
/*
* Nomad Instrument Control Software
*
* Copyright 2011 Institut Laue-Langevin
*
* Licensed under the EUPL, Version 1.1 only (the "License");
* You may not use this work except in compliance with the Licence.
* You may obtain a copy of the Licence at:
*
* http://joinup.ec.europa.eu/software/page/eupl
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the Licence is distributed on an "AS IS" basis,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Licence for the specific language governing permissions and
* limitations under the Licence.
*/
#include "DectrisDetector.h"
#include "drivers/dectris/EigerRDef.h"
#include "rapidjson/prettywriter.h"
#include "CImg.h"
namespace xraydetector {
const std::string DectrisDetector::TYPE = "dectris_detector";
DectrisDetector::DectrisDetector(const std::string& controllerName) :
ExperimentController(controllerName), controller::Stoppable(this) {
measureTime.init(this, SAVE | SPY, "measure_time", "Time");
detectorSum.init(this, NOSAVE | SPY, "detector_sum", "Sum");
countTime.init(this, SAVE, "actual_count_time", "wanted_count_time");
frameTime.init(this, SAVE, "actual_frame_time", "wanted_frame_time");
numImages.init(this, SAVE | SPY, "actual_n_images", "wanted_n_images", "num.Images");
fileId.init(this, SAVE | SPY, "file_id", "FileID"); // it is a number
useFileId.init(this, SAVE | SPY, "use_file_id", "Use FileID");
plotTitle.init(this, NOSAVE, "plot_title");
detectorXData.init(this, NOSAVE, "detector_x_data");
detectorYData.init(this, NOSAVE, "detector_y_data");
detectorZData.init(this, NOSAVE, "detector_z_data");
xSize.init(this, SAVE, "xsize");
ySize.init(this, SAVE, "ysize");
zSize.init(this, SAVE, "zsize");
m_driver.init(this, "driver");
ctx = nullptr;
receiverSocket = nullptr;
xData = nullptr;
yData = nullptr;
zData = nullptr;
sumDataPtr = nullptr;
output = nullptr;
isCancelled = false;
dataBlobSize = 0;
dataSize = 0;
mutex = new std::recursive_mutex();
}
DectrisDetector::~DectrisDetector() {
delete mutex;
}
void DectrisDetector::postConfiguration() {
registerRefresher(measureTime, &DectrisDetector::refreshMeasurementTime, this);
registerRefresher(frameTime.setpoint, &DectrisDetector::refreshFrameTime, this);
registerRefresher(fileId, &ExperimentController::copyValue<int32>, this, m_driver->fileId);
registerRefresher(useFileId, &ExperimentController::copyValue<bool>, this, m_driver->useFileId);
// New way of adding updaters when we basically copy the value
registerPropertyCopier(m_driver->frameTime, frameTime);
registerPropertyCopier(m_driver->numImages, numImages);
// Init values
detectorSum = 0;
}
void DectrisDetector::refreshMeasurementTime(float64 measureTime) {
// Calculate the number of images needed using a fixed frameTime value
int32 numImagesValue = ceil(measureTime / frameTime.setpoint()); // Round up the number of images
numImages.setpoint = numImagesValue;
m_driver->numImages.setpoint = numImages.setpoint();
}
void DectrisDetector::refreshFrameTime(float64 frameTime) {
// Calculate the number of images needed using a fixed measureTime value
int32 numImagesValue = ceil(measureTime() / frameTime); // Round up the number of images
numImages.setpoint = numImagesValue;
m_driver->numImages.setpoint = numImages.setpoint();
m_driver->frameTime.setpoint = frameTime;
}
/*
* The start procedure implies:
* - arm
* - read header
* - trigger
* - read stream
* - disarm
*/
void DectrisDetector::start() {
log(Level::s_Info) << " start in Dectris Detector controller, measureTime " << measureTime << " frameTime " << frameTime.setpoint() << " numImages " << numImages.setpoint() << endlog;
initStream();
reset();
if (isOK()) {
prepare();
}
if (isOK()) {
measure();
}
if (isOK()) {
disarm();
}
endStream();
}
void DectrisDetector::stop() {
cancel();
}
void DectrisDetector::initStream() {
ctx = new zmq::context_t(1);
receiverSocket = new zmq::socket_t(*ctx, zmq::socket_type::pull); // We will just receive data
receiverSocket->connect("tcp://10.42.41.10:9999");
}
void DectrisDetector::endStream() {
receiverSocket->disconnect("tcp://10.42.41.10:9999");
receiverSocket->close();
// Delete everything here
delete ctx;
delete receiverSocket;
ctx = nullptr;
receiverSocket = nullptr;
}
void DectrisDetector::reset() {
// reset local variables
isCancelled = false;
// reset sum
detectorSum.update(0.0);
// clear sum pointer
if (sumDataPtr != nullptr) {
delete sumDataPtr;
sumDataPtr = nullptr;
}
// Update number of fileId
if (useFileId()) {
fileId = fileId() + 1;
std::ostringstream title;
title << "SAXS Detector" << " : " << fileId();
plotTitle.update(title.str());
}
}
void DectrisDetector::prepare() {
if (isOK()) {
configure(); // See if we do it in a non systematic way
}
if (isOK()) {
arm();
}
if (isOK()) {
readHeader();
}
}
void DectrisDetector::measure() {
// We do not have another possibility than to execute two threads here
boost::thread_group group;
group.create_thread(boost::bind(&DectrisDetector::trigger, this));
group.create_thread(boost::bind(&DectrisDetector::readStream, this));
// Wait for the termination of the threads
group.join_all();
}
void DectrisDetector::readHeader() {
bool headerHasBeenRead = false;
do {
// To avoid blocking calls execute a poller before receiving data
if (doPollin()) {
log(Level::s_Info) << "Pollin in readHeader (part 1) OK" << endlog;
zmq::message_t msg;
receiverSocket->recv(&msg);
// Get data as a string due to its JSON format (again method to_string of message_t does not exist).
std::string str = std::string(static_cast<char*>(msg.data()), msg.size());
std::cout << "in readHeader content " << str << std::endl;
log(Level::s_Info) << "Header content of part 1 " << str << endlog;
rapidjson::Document document;
document.Parse(str.c_str());
if (document.HasMember(dectris::HTYPE) && std::string(document[dectris::HTYPE].GetString()) == dectris::DHEADER) {
// this is first part of a header message
std::string headerDetail = document[dectris::HEADER_DETAIL].GetString();
// read the rest of the header here I DO ASSUME THE MESSAGE IS BASIC
if (headerDetail == dectris::HEADER_DETAIL_BASIC) {
// message contains 1 part more to read
if (doPollin()) {
log(Level::s_Info) << "Pollin in readHeader (part 2) OK" << endlog;
receiverSocket->recv(&msg);
headerHasBeenRead = true;
} else {
log(Level::s_Info) << "Pollin in readHeader (part 2) ERROR" << endlog;
}
}
}
} else {
// Error in reading, cancel the execution
log(Level::s_Info) << "Pollin in readHeader (part 1) ERROR" << endlog;
cancel();
headerHasBeenRead = true; // exit
}
} while (!headerHasBeenRead);
}
void DectrisDetector::readStream() {
zmq::message_t msg;
for (int i = 0; i < 4; ++i) {
if (doPollin()) {
log(Level::s_Info) << "Pollin in readStream (part " << i << " ) OK" << endlog;
receiverSocket->recv(&msg); //ZMQ_DONTWAIT
// Read message in JSON format except for part 3 (which contains the detector image)
std::string str = std::string(static_cast<char*>(msg.data()), msg.size());
log(Level::s_Info) << "Stream content of part " << i << " : " << str << endlog;
rapidjson::Document document;
switch (i) {
case 0:
document.Parse(str.c_str());
if (document.HasMember(dectris::HTYPE) && std::string(document[dectris::HTYPE].GetString()) == dectris::DEND_SERIES) {
// endOfMessage = true;
i = 100; // exit for loop
} else {
readPartOneOfImageData(document);
}
break;
case 1:
document.Parse(str.c_str());
readPartTwoOfImageData(document);
break;
case 2:
readPartThreeOfImageData(str);
break;
case 3:
document.Parse(str.c_str());
readPartFourOfImageData(document);
getData();
break;
}
} else {
// Error in reading, cancel the execution
log(Level::s_Info) << "Pollin in readStream (part " << i << " ) ERROR" << endlog;
cancel();
break;
}
}
}
bool DectrisDetector::doPollin() {
bool ok = true;
// Create poller
zmq_pollitem_t items[1];
items[0].socket = static_cast<void *>(*receiverSocket);
items[0].fd = 0;
items[0].events = ZMQ_POLLIN;
items[0].revents = 0;
// Check polling here before receiving a message
int rc = zmq::poll(items, 1, 10000); // blocks until message received or timeout
if (rc == 0) {
ok = false;
}
return ok;
}
void DectrisDetector::readPartOneOfImageData(rapidjson::Document& document) {
if (document.HasMember(dectris::HTYPE) && std::string(document[dectris::HTYPE].GetString()) == dectris::DIMAGE) {
// TODO Get values of fields series, frame and hash
}
}
void DectrisDetector::readPartTwoOfImageData(rapidjson::Document& document) {
// Get values of fields shape, type, encoding and size
if (document.HasMember(dectris::HTYPE) && std::string(document[dectris::HTYPE].GetString()) == dectris::DIMAGE) {
// Shape
rapidjson::Document::Array array = document[dectris::SHAPE].GetArray();
if (array.Size() == 2) { // We could have three dimensions
int32 imageWidth = array[0].GetInt();
int32 imageHeight = array[1].GetInt();
// set properties here
xSize = imageWidth;
ySize = imageHeight;
zSize = 1; // Just for data file
}
// Data type
dataType = document[dectris::TYPE].GetString(); // either uint16 or uint32
// Encoding
encoding = document[dectris::ENCODING].GetString();
// Data Blob size
dataBlobSize = document[dectris::SIZE].GetInt(); // Size in bytes
}
}
void DectrisDetector::readPartThreeOfImageData(std::string& input) {
// Allocate the memory
if (dataType == "uint16") {
dataSize = 2;
} else if (dataType == "uint32") {
dataSize = 4;
} else {
// error
}
// Release memory. zData is a copy of output pointer.
if (output != nullptr) {
delete[] output;
}
int32 outputSize = xSize() * ySize() * dataSize;
output = new char[outputSize];
memset(output, 0, outputSize * sizeof(char));
int32 bytes_decompressed = LZ4_decompress_safe(input.c_str(), output, dataBlobSize, outputSize);
// bshuf_decompress_lz4();
if (bytes_decompressed < 0) {
std::cout << "ERROR in decompressing " << bytes_decompressed << std::endl;
} else {
// Fill property
zData = (int32 *) (output); // IMPORTANT! HERE I ASSUME THAT zData is of type int32 but it really depends on dataSize value!
cimg_library::CImg<int32> buf(zData, xSize(), ySize());
buf.permute_axes("yx");
buf.mirror("x");
memcpy(zData, buf.data(), xSize() * ySize() * sizeof(int32));
}
}
void DectrisDetector::readPartFourOfImageData(rapidjson::Document& document) {
// Get values of fields start_time, stop_time and real_time
if (document.HasMember(dectris::HTYPE) && std::string(document[dectris::HTYPE].GetString()) == dectris::DIMAGE) {
// TODO
}
}
void DectrisDetector::getData() {
log(Level::s_Info) << "Getting the data from stream" << endlog;
// Make safe the execution by using a mutex (do not think this is needed)
std::lock_guard<std::recursive_mutex> lock(*mutex);
if (xData != nullptr) {
delete[] xData;
}
if (yData != nullptr) {
delete[] yData;
}
xData = new int32[xSize()];
memset(xData, 0, xSize() * sizeof(int32));
for (int i = 0; i < xSize(); ++i) {
xData[i] = i;
}
yData = new int32[ySize()];
memset(yData, 0, ySize() * sizeof(int32));
for (int i = 0; i < ySize(); ++i) {
yData[i] = i;
}