Commits (19)
......@@ -27,6 +27,7 @@ requirements:
build:
- git
- cmake
- make
- {{ compiler('cxx') }}
run:
- python {{ python }}
......
......@@ -19,6 +19,7 @@ build:
requirements:
build:
- cmake
- make
- git
- lima-core
run:
......
......@@ -11,6 +11,8 @@ Properties
Property name Mandatory Default value Description
==================== =============== =============== =========================================================================
detector_ip_address Yes N/A The ip address or the hostname of the detector computer interface
http_port No 80 The http port number for control API
stream_port No 9999 The port number for the data stream API
==================== =============== =============== =========================================================================
......@@ -20,12 +22,21 @@ Attributes
Attribute name RW Type Description
========================= ======= ======================= ======================================================================
auto_summation rw DevString If enable image depth is bpp32 and, if not image depth is bpp16 **(\*)**
cam_status ro DevString The internal camera status
compression_type rw DevString For data stream, supported compression are:
- NONE
- LZ4
- BSLZ4
countrate_correction rw DevString Enable or disable the countrate correction **(\*)**
efficency_correction rw DevString Enable the efficienty correction
flatfield_correction rw DevString Enable or disable the internal (vs. lima) flatfield correction **(\*)**
humidity ro DevFloat Return the humidity percentage
pixel_mask rw DevString Enable or disable the pixel mask correction **(\*)**
photon_energy rw DevFloat The photon energy,it should be set to the incoming beam energy. Actually it’s an helper which set the threshold
plugin_status ro DevString The camera plugin status
serie_id ro DevLong The current acquisition serie identifier
stream_last_info ro DevString Information on data stream, encoding, frame_dim and packed_size
stream_stats ro DevDouble ave_size, ave_time, ave_speed
threshold_energy rw DevFloat The threshold energy, it will set the camera detection threshold. This should be set between 50 to 60 % of the incoming beam energy.
temperature ro DevFloat The sensor temperature
virtual_pixel_correction rw DevString Enable or disable the virtual-pixel correction **(\*)**
......@@ -42,6 +53,11 @@ Commands
Command name Arg. in Arg. out Description
======================= =============== ======================= ===========================================
deleteMemoryFiles DevVoid DevVoid To remove the temporary mem. files
initialize DevVoid DevVoid To initialize the detector
latchStreamStatistics DevBoolean DevVarDoubleArray: If True, reset the statistics
- ave_size,
- ave_time,
- ave_speed
Init DevVoid DevVoid Do not use
State DevVoid DevLong Return the device state
Status DevVoid DevString Return the device state as a string
......
......@@ -73,7 +73,7 @@ class LIBEIGER Camera : public HwMaxImageSizeCallbackGen, public EventCallbackGe
enum Status { Initializing, Ready, Armed, Exposure, Fault };
enum CompressionType {NoCompression,LZ4,BSLZ4};
Camera(const std::string& detector_ip);
Camera(const std::string& host, int http_port=80, int stream_port=9999);
~Camera();
void initialize();
......@@ -122,6 +122,10 @@ class LIBEIGER Camera : public HwMaxImageSizeCallbackGen, public EventCallbackGe
void getTemperature(double&);
void getHumidity(double&);
void getHighVoltageState(std::string&);
void getHighVoltageMeasured(double&);
void getHighVoltageTarget(double&);
void resetHighVoltage();
void setCountrateCorrection(bool);
void getCountrateCorrection(bool&);
......@@ -131,6 +135,8 @@ class LIBEIGER Camera : public HwMaxImageSizeCallbackGen, public EventCallbackGe
void getAutoSummation(bool&);
void setEfficiencyCorrection(bool);
void getEfficiencyCorrection(bool& value);
void setRetrigger(bool);
void getRetrigger(bool &value);
void setPixelMask(bool);
void getPixelMask(bool&);
void setThresholdEnergy(double);
......@@ -159,7 +165,8 @@ class LIBEIGER Camera : public HwMaxImageSizeCallbackGen, public EventCallbackGe
void deleteMemoryFiles();
void disarm();
const std::string& getDetectorIp() const;
const std::string& getDetectorHost() const;
int getDetectorStreamPort() const;
private:
friend class Interface;
......@@ -177,7 +184,7 @@ class LIBEIGER Camera : public HwMaxImageSizeCallbackGen, public EventCallbackGe
Camera::Status _getStatus();
void _synchronize(); /// Used during plug-in initialization
void _trigger_finished(bool);
void _trigger_finished(bool ok, bool do_disarm);
void _initialization_finished(bool ok);
void _updateImageSize();
......@@ -249,7 +256,9 @@ class LIBEIGER Camera : public HwMaxImageSizeCallbackGen, public EventCallbackGe
double m_readout_time;
double m_x_pixelsize, m_y_pixelsize;
Cond m_cond;
std::string m_detector_ip;
std::string m_detector_host;
int m_detector_http_port;
int m_detector_stream_port;
double m_min_frame_time;
CompressionType m_compression_type;
};
......
......@@ -67,11 +67,15 @@ namespace eigerapi
void handle_result(CURLcode result);
void _status_changed();
bool check_http_response(const char *ptr, size_t size);
static void *_callback_thread_runFunc(void *data);
CURL* m_handle;
Status m_status;
std::string m_error_code;
int m_http_code;
std::string m_http_msg;
// Synchro
mutable pthread_mutex_t m_lock;
mutable pthread_cond_t m_cond;
......
......@@ -137,9 +137,12 @@ namespace eigerapi
typedef std::shared_ptr<Transfer> TransferReq;
enum COMMAND_NAME {INITIALIZE,ARM, DISARM,TRIGGER,CANCEL,ABORT,
FILEWRITER_CLEAR};
FILEWRITER_CLEAR, HV_RESET};
enum PARAM_NAME {TEMP,
HUMIDITY,
HVSTATE,
HVMEASURED,
HVTARGET,
DETECTOR_STATUS,
PIXELDEPTH,
X_PIXEL_SIZE,
......@@ -149,14 +152,15 @@ namespace eigerapi
DESCRIPTION,
DETECTOR_NUMBER,
DETECTOR_READOUT_TIME,
DATA_COLLECTION_DATE,
SOFTWARE_VERSION,
DATA_COLLECTION_DATE,
SOFTWARE_VERSION,
EXPOSURE,
FRAME_TIME,
TRIGGER_MODE,
COUNTRATE_CORRECTION,
FLATFIELD_CORRECTION,
EFFICIENCY_CORRECTION,
RETRIGGER,
PIXEL_MASK,
THRESHOLD_ENERGY,
VIRTUAL_PIXEL_CORRECTION,
......
......@@ -28,6 +28,8 @@
#include "eigerapi/EigerDefines.h"
#include "AutoMutex.h"
#include <regex>
using namespace eigerapi;
// Constant
......@@ -50,6 +52,7 @@ struct CURL_INIT
CurlLoop::FutureRequest::FutureRequest(const std::string& url) :
m_status(IDLE),
m_http_code(0),
m_cbk(NULL),
m_url(url)
{
......@@ -78,7 +81,7 @@ CurlLoop::FutureRequest::~FutureRequest()
inline void CurlLoop::FutureRequest::handle_result(CURLcode result)
{
Lock lock(&m_lock);
if(m_status != FutureRequest::CANCEL)
if(m_status == FutureRequest::RUNNING)
{
switch(result)
{
......@@ -97,6 +100,24 @@ inline void CurlLoop::FutureRequest::handle_result(CURLcode result)
_request_finished();
}
bool CurlLoop::FutureRequest::check_http_response(const char *ptr, size_t size)
{
static const std::regex re("([1-5][0-9]{2}) (.+)");
std::cmatch m;
if(!std::regex_match(ptr, ptr + size, m, re))
return false;
Lock lock(&m_lock);
m_http_code = std::stoi(m[1].str());
m_http_msg = m[2].str();
std::cout << "HTTP [" << m_url << "]: " << m[0].str() << std::endl;
if((m_status == FutureRequest::RUNNING) && (m_http_code >= 400)) {
m_status = FutureRequest::ERROR;
m_error_code = m_http_msg;
}
return true;
}
void CurlLoop::FutureRequest::wait(double timeout,bool lock_flag) const
{
Lock lock(&m_lock,lock_flag);
......
......@@ -41,6 +41,7 @@ typedef Requests::CurlReq CurlReq;
static const char* CSTR_EIGERCONFIG = "config";
static const char* CSTR_EIGERSTATUS = "status";
static const char* CSTR_EIGERSTATUS_BOARD = "status/board_000";
static const char* CSTR_EIGERSTATUS_HV = "status/high_voltage";
static const char* CSTR_EIGERCOMMAND = "command";
static const char* CSTR_EIGERFILES = "files";
static const char* CSTR_SUBSYSTEMFILEWRITER = "filewriter";
......@@ -83,6 +84,7 @@ static CommandIndex CommandsDescription[] = {
{Requests::CANCEL, {"cancel",CSTR_SUBSYSTEMDETECTOR,CSTR_EIGERCOMMAND}},
{Requests::ABORT, {"abort",CSTR_SUBSYSTEMDETECTOR,CSTR_EIGERCOMMAND}},
{Requests::FILEWRITER_CLEAR, {"clear",CSTR_SUBSYSTEMFILEWRITER,CSTR_EIGERCOMMAND}},
{Requests::HV_RESET, {"hv_reset",CSTR_SUBSYSTEMDETECTOR,CSTR_EIGERCOMMAND}},
};
const char* get_cmd_name(Requests::COMMAND_NAME cmd_name)
......@@ -105,6 +107,9 @@ ParamIndex ParamDescription[] = {
// Detector Read only values
{Requests::TEMP, {"th0_temp",CSTR_SUBSYSTEMDETECTOR,CSTR_EIGERSTATUS_BOARD}},
{Requests::HUMIDITY, {"th0_humidity",CSTR_SUBSYSTEMDETECTOR,CSTR_EIGERSTATUS_BOARD}},
{Requests::HVSTATE, {"state",CSTR_SUBSYSTEMDETECTOR,CSTR_EIGERSTATUS_HV}},
{Requests::HVMEASURED, {"measured",CSTR_SUBSYSTEMDETECTOR,CSTR_EIGERSTATUS_HV}},
{Requests::HVTARGET, {"target",CSTR_SUBSYSTEMDETECTOR,CSTR_EIGERSTATUS_HV}},
{Requests::DETECTOR_STATUS, {"state",CSTR_SUBSYSTEMDETECTOR,CSTR_EIGERSTATUS}},
{Requests::PIXELDEPTH, {"bit_depth_readout"}},
{Requests::X_PIXEL_SIZE, {"x_pixel_size"}},
......@@ -131,6 +136,7 @@ ParamIndex ParamDescription[] = {
{Requests::NIMAGES, {"nimages"}},
{Requests::NTRIGGER, {"ntrigger"}},
{Requests::AUTO_SUMMATION, {"auto_summation"}},
{Requests::RETRIGGER, {"retrigger"}},
// Filewriter settings
{Requests::FILEWRITER_MODE, {"mode",CSTR_SUBSYSTEMFILEWRITER}},
......@@ -405,7 +411,13 @@ void Requests::Command::_fill_request()
size_t Requests::Command::_write_callback(char *ptr,size_t size,
size_t nmemb,Requests::Command *cmd)
{
int size_to_copy = std::min(size * nmemb,sizeof(m_data));
size_t len = size * nmemb;
if (cmd->check_http_response(ptr, len))
return len;
int size_to_copy = std::min(len, sizeof(m_data) - 1);
if (size_to_copy < len)
std::cout << "Requests::Command: Warning: unexpected (long) message: "
<< std::string(ptr, len) << std::endl;
memcpy(cmd->m_data,ptr,size_to_copy);
cmd->m_data[size_to_copy] = '\0';
return size_to_copy;
......
......@@ -32,7 +32,7 @@ namespace Eiger
enum Status { Initializing, Ready, Armed, Exposure, Fault };
enum CompressionType {NoCompression,LZ4,BSLZ4};
Camera(const std::string& detector_ip);
Camera(const std::string& detector_ip, int http_port = 80, int stream_port = 9999) /KeywordArgs="Optional"/;
~Camera();
void initialize();
......@@ -81,6 +81,10 @@ namespace Eiger
void getTemperature(double& /Out/);
void getHumidity(double& /Out/);
void getHighVoltageState(std::string& /Out/);
void getHighVoltageMeasured(double& /Out/);
void getHighVoltageTarget(double& /Out/);
void resetHighVoltage();
void setCountrateCorrection(const bool);
void getCountrateCorrection(bool& /Out/);
......@@ -90,6 +94,8 @@ namespace Eiger
void getAutoSummation(bool& /Out/);
void setEfficiencyCorrection(const bool);
void getEfficiencyCorrection(bool& value /Out/);
void setRetrigger(bool);
void getRetrigger(bool& /Out/);
void setPixelMask(const bool);
void getPixelMask(bool& /Out/);
void setThresholdEnergy(const double);
......
......@@ -61,7 +61,10 @@ class Camera::TriggerCallback : public Callback
{
DEB_CLASS_NAMESPC(DebModCamera, "Camera::TriggerCallback", "Eiger");
public:
TriggerCallback(Camera& cam) : m_cam(cam) {}
TriggerCallback(Camera& cam, bool disarm, double duration)
: m_cam(cam), m_disarm(disarm), m_duration(duration),
m_start_ts(Timestamp::now())
{}
void status_changed(CurlLoop::FutureRequest::Status status,
std::string error)
......@@ -69,12 +72,27 @@ public:
DEB_MEMBER_FUNCT();
DEB_PARAM() << DEB_VAR2(status, error);
bool ok = (status == CurlLoop::FutureRequest::OK);
if (!ok) {
// the HTTP server can close the connection without completing
// trigger commands longer than 5 min. a retry (by libcurl) fails
const double timeout_limit = 5 * 60;
bool long_trigger = (m_duration > timeout_limit - 1);
// if the command lasted the expected duration, ignore the error
double elapsed = Timestamp::now() - m_start_ts;
if (long_trigger && (fabs(elapsed - m_duration) < 2)) {
DEB_ALWAYS() << "Ignoring trigger command error";
ok = true;
}
}
if (!ok)
DEB_ERROR() << DEB_VAR1(error);
m_cam._trigger_finished(ok);
m_cam._trigger_finished(ok, m_disarm);
}
private:
Camera& m_cam;
bool m_disarm;
double m_duration;
Timestamp m_start_ts;
};
class Camera::InitCallback : public Callback
......@@ -101,7 +119,7 @@ private:
//-----------------------------------------------------------------------------
/// Ctor
//-----------------------------------------------------------------------------
Camera::Camera(const std::string& detector_ip) ///< [in] Ip address of the detector server
Camera::Camera(const std::string& host, int http_port, int stream_port) ///< [in] Ip address of the detector server
: m_frames_triggered(0),
m_frames_acquired(0),
m_latency_time(0.),
......@@ -110,12 +128,16 @@ Camera::Camera(const std::string& detector_ip) ///< [in] Ip address of the dete
m_trigger_state(IDLE),
m_armed(false),
m_serie_id(0),
m_requests(new Requests(detector_ip)),
m_exp_time(1.),
m_detector_ip(detector_ip)
m_detector_host(host),
m_detector_http_port(http_port),
m_detector_stream_port(stream_port)
{
DEB_CONSTRUCTOR();
DEB_PARAM() << DEB_VAR1(detector_ip);
DEB_PARAM() << DEB_VAR1(host);
std::string http_address = host + ":" + std::to_string(http_port);
m_requests = new Requests(http_address);
// Detect EigerAPI version
std::string api_version = m_requests->get_api_version();
......@@ -271,12 +293,12 @@ void Camera::startAcq()
{
CommandReq trigger = m_requests->get_command(Requests::TRIGGER);
m_trigger_state = RUNNING;
m_frames_triggered += m_nb_triggers;
m_frames_triggered += m_nb_images;
bool disarm_at_end = (m_frames_triggered == m_nb_frames);
DEB_TRACE() << "Trigger start: " << DEB_VAR1(disarm_at_end);
double duration = m_nb_images * m_frame_time;
AutoMutexUnlock u(lock);
CallbackPtr cbk(new TriggerCallback(*this));
CallbackPtr cbk(new TriggerCallback(*this, disarm_at_end, duration));
trigger->register_callback(cbk, disarm_at_end);
}
......@@ -596,7 +618,7 @@ Camera::Status Camera::_getStatus()
status = Fault;
else if(m_trigger_state == RUNNING)
status = Exposure;
else if(m_armed)
else if(m_armed && (m_frames_triggered == 0))
status = Armed;
else if(m_initialize_state == RUNNING)
status = Initializing;
......@@ -715,7 +737,13 @@ void Camera::_synchronize()
param = Requests::FRAME_TIME;
m_min_frame_time = synchro[param]->get_min().data.double_val;
param = Requests::DETECTOR_READOUT_TIME;
m_readout_time = synchro[param]->get_min().data.double_val;
ParamReq req = synchro[param];
Requests::Param::Value value = req->get_min();
m_readout_time = value.data.double_val;
if (m_readout_time <= 0) {
value = req->get();
m_readout_time = value.data.double_val;
}
} catch (const EigerException& e) {
HANDLE_EIGERERROR(synchro[param], e);
}
......@@ -746,21 +774,20 @@ void Camera::_updateImageSize()
/*----------------------------------------------------------------------------
This method is called when the trigger is finished
----------------------------------------------------------------------------*/
void Camera::_trigger_finished(bool ok)
void Camera::_trigger_finished(bool ok, bool do_disarm)
{
DEB_MEMBER_FUNCT();
DEB_PARAM() << DEB_VAR1(ok);
AutoMutex lock(m_cond.mutex());
DEB_TRACE() << "Trigger end";
if(!ok) {
DEB_ERROR() << "Error in trigger command";
} else if(m_frames_triggered == m_nb_frames) {
AutoMutexUnlock u(lock);
} else if(do_disarm) {
try { disarm(); }
catch (...) { ok = false; }
}
AutoMutex lock(m_cond.mutex());
m_trigger_state = ok ? IDLE : ERROR;
}
......@@ -821,6 +848,37 @@ void Camera::getHumidity(double &humidity)
}
//-----------------------------------------------------------------------------
/// Returns the high voltage state
/*!
@return state string
*/
//-----------------------------------------------------------------------------
void Camera::getHighVoltageState(std::string &hvstate)
{
DEB_MEMBER_FUNCT();
getParam(Requests::HVSTATE,hvstate);
}
void Camera::resetHighVoltage()
{
DEB_MEMBER_FUNCT();
sendCommand(Requests::HV_RESET);
DEB_TRACE() << "reset HighVoltage";
}
void Camera::getHighVoltageMeasured(double &hvmeas)
{
DEB_MEMBER_FUNCT();
getParam(Requests::HVMEASURED, hvmeas);
}
void Camera::getHighVoltageTarget(double &hvtarget)
{
DEB_MEMBER_FUNCT();
getParam(Requests::HVTARGET, hvtarget);
}
//-----------------------------------------------------------------------------
/// Count rate correction setter
//-----------------------------------------------------------------------------
......@@ -860,6 +918,26 @@ void Camera::getFlatfieldCorrection(bool& value) ///< [out] true:enabled, false:
getParam(Requests::FLATFIELD_CORRECTION,value);
}
//-----------------------------------------------------------------------------
/// Retrigger mode setter
//-----------------------------------------------------------------------------
void Camera::setRetrigger(bool value) ///< [in] true:enabled, false:disabled
{
DEB_MEMBER_FUNCT();
setParam(Requests::RETRIGGER,value);
}
//-----------------------------------------------------------------------------
/// Retrigger getter
//-----------------------------------------------------------------------------
void Camera::getRetrigger(bool& value) ///< [out] true:enabled, false:disabled
{
DEB_MEMBER_FUNCT();
getParam(Requests::RETRIGGER,value);
}
//----------------------------------------------------------------------------
// Auto Summation setter
//----------------------------------------------------------------------------
......@@ -881,6 +959,8 @@ void Camera::getAutoSummation(bool& value)
getParam(Requests::AUTO_SUMMATION,value);
DEB_RETURN() << DEB_VAR1(value);
}
//-----------------------------------------------------------------------------
/// PixelMask setter
//-----------------------------------------------------------------------------
......@@ -1170,7 +1250,13 @@ void Camera::disarm()
sendCommand(Requests::DISARM);
}
const std::string& Camera::getDetectorIp() const
const std::string& Camera::getDetectorHost() const
{
return m_detector_ip;
return m_detector_host;
}
int Camera::getDetectorStreamPort() const
{
return m_detector_stream_port;
}
......@@ -131,18 +131,17 @@ void Interface::prepareAcq()
void Interface::startAcq()
{
DEB_MEMBER_FUNCT();
TrigMode trig_mode;
m_cam.getTrigMode(trig_mode);
int nb_trig_frames;
m_cam.getNbTriggeredFrames(nb_trig_frames);
// start data retrieval subsystems only in first call
if (getNbHwAcquiredFrames() == 0) {
if ((trig_mode != IntTrigMult) || (nb_trig_frames == 0)) {
// either we use eiger saving or the raw stream
if(m_saving->isActive())
m_saving->start();
else
m_stream->start();
} else {
TrigMode trig_mode;
m_cam.getTrigMode(trig_mode);
if (trig_mode != IntTrigMult)
DEB_WARNING() << "Unexpected start";
}
m_cam.startAcq();
......@@ -171,17 +170,11 @@ void Interface::getStatus(StatusType& status)
Eiger_status = m_cam.getStatus();
switch (Eiger_status)
{
case Camera::Armed:
case Camera::Ready:
{
bool mult_trig_in_progress = false;
TrigMode trig_mode;
m_cam.getTrigMode(trig_mode);
if ((Eiger_status == Camera::Armed) &&
((trig_mode == IntTrig) || (trig_mode == IntTrigMult))) {
status.set(HwInterface::StatusType::Ready);
break;
}
bool mult_trig_in_progress = false;
if (trig_mode == IntTrigMult) {
int tot_nb_frames, nb_trig_frames;
m_cam.getNbFrames(tot_nb_frames);
......@@ -214,6 +207,10 @@ void Interface::getStatus(StatusType& status)
status.set(HwInterface::StatusType::Exposure);
break;
case Camera::Armed:
status.set(HwInterface::StatusType::Ready);
break;
case Camera::Fault:
status.set(HwInterface::StatusType::Fault);
break;
......
......@@ -263,7 +263,8 @@ void Stream::_ZmqThread::_run_sequence()
char stream_endpoint[256];
snprintf(stream_endpoint,sizeof(stream_endpoint),
"tcp://%s:9999",cam.getDetectorIp().c_str());
"tcp://%s:%d",cam.getDetectorHost().c_str(),
cam.getDetectorStreamPort());
if(zmq_connect(stream_socket,stream_endpoint) != 0) {
char error_buffer[256];
const char *error_msg = strerror_r(errno,error_buffer,sizeof(error_buffer));
......@@ -298,7 +299,11 @@ void Stream::_ZmqThread::_run_sequence()
bool continue_flag = true;
while(continue_flag) { // reading loop
DEB_TRACE() << "Enter poll";
zmq_poll(items,2,-1);
long timeout_ms = m_stopped ? 2000 : -1;
if (zmq_poll(items,2,timeout_ms) <= 0) {
DEB_ERROR() << "No (end) message received after Abort";
break;
}
DEB_TRACE() << "Exit poll";
if(items[0].revents & ZMQ_POLLIN) { // reading synchro pipe
......
......@@ -81,12 +81,14 @@ class Eiger(PyTango.Device_4Impl):
'OFF':False}
self.__VirtualPixelCorrection = {'ON':True,
'OFF':False}
self.__Retrigger = {'ON':True,
'OFF':False}
self.__PixelMask = {'ON':True,
'OFF':False}
self.__CompressionType = {'NONE': EigerAcq.Camera.NoCompression,
'LZ4': EigerAcq.Camera.LZ4,
'BSLZ4': EigerAcq.Camera.BSLZ4}
self.__Status = {'INITIALIZING': EigerAcq.Camera.Initializing,
self.__PluginStatus = {'INITIALIZING': EigerAcq.Camera.Initializing,
'READY': EigerAcq.Camera.Ready,
'ARMED': EigerAcq.Camera.Armed,
'EXPOSURE': EigerAcq.Camera.Exposure,
......@@ -123,7 +125,8 @@ class Eiger(PyTango.Device_4Impl):
#==================================================================
def __getattr__(self,name) :
if name == 'read_plugin_status':
name = 'read_status'
func2call = getattr(_EigerCamera, "getStatus")
return AttrHelper.CallableReadEnum(self.__PluginStatus, func2call)
return AttrHelper.get_attr_4u(self,name,_EigerCamera)
#==================================================================
......@@ -180,6 +183,13 @@ class Eiger(PyTango.Device_4Impl):
stream_stats.ave_time(),
stream_stats.ave_speed()]
#----------------------------------------------------------------------------
# reset high voltage
#----------------------------------------------------------------------------
@Core.DEB_MEMBER_FUNCT
def resetHighVoltage(self):
_EigerCamera.resetHighVoltage()
#==================================================================
#
# EigerClass class definition
......@@ -197,6 +207,12 @@ class EigerClass(PyTango.DeviceClass):
'detector_ip_address':
[PyTango.DevString,
"Detector ip address",[]],
'http_port':
[PyTango.DevLong,
"HTTP port number",[]],
'stream_port':
[PyTango.DevLong,
"Stream port number",[]],
}
......@@ -214,6 +230,9 @@ class EigerClass(PyTango.DeviceClass):
'latchStreamStatistics':
[[PyTango.DevBoolean, "Reset statistics"],
[PyTango.DevVarDoubleArray, "[<ave_size>, <ave_time>, <ave_speed>]"]],
'resetHighVoltage':
[[PyTango.DevVoid, ""],
[PyTango.DevVoid, ""]],
}
......@@ -231,6 +250,18 @@ class EigerClass(PyTango.DeviceClass):
[[PyTango.DevFloat,
PyTango.SCALAR,
PyTango.READ]],
'high_voltage_state':
[[PyTango.DevString,
PyTango.SCALAR,
PyTango.READ]],
'high_voltage_measured':
[[PyTango.DevFloat,
PyTango.SCALAR,
PyTango.READ]],
'high_voltage_target':
[[PyTango.DevFloat,
PyTango.SCALAR,
PyTango.READ]],
'countrate_correction':
[[PyTango.DevString,
PyTango.SCALAR,
......@@ -251,6 +282,10 @@ class EigerClass(PyTango.DeviceClass):
[[PyTango.DevString,
PyTango.SCALAR,
PyTango.READ_WRITE]],
'retrigger':
[[PyTango.DevString,
PyTango.SCALAR,
PyTango.READ_WRITE]],
'pixel_mask':
[[PyTango.DevString,
PyTango.SCALAR,
......@@ -309,7 +344,12 @@ def get_control(detector_ip_address = "0", **keys) :
global _EigerInterface
global _EigerCamera
if _EigerInterface is None:
_EigerCamera = EigerAcq.Camera(detector_ip_address)
http_port = keys.pop('http_port', 80)
stream_port = keys.pop('stream_port', 9999)
_EigerCamera = EigerAcq.Camera(detector_ip_address,
http_port=http_port,
stream_port=stream_port)
_EigerInterface = EigerAcq.Interface(_EigerCamera)
return Core.CtControl(_EigerInterface)
......
from Lima import Core, Eiger
import time
import sys
import getopt
import numpy as np
from collections import namedtuple
from contextlib import contextmanager
from threading import Condition, Event
AcqPars = namedtuple('AcqPars',
['trig_mode', 'nb_frames', 'expo_time', 'lat_time'])
class TestIntTrig:
class Cb(Core.CtControl.ImageStatusCallback):
def __init__(self, frame_ts, end, acq_pars):
super().__init__()
self.last_acquired = -1
self.frame_ts = frame_ts
self.end = end
self.acq_pars = acq_pars
def imageStatusChanged(self, status):
if status.LastImageAcquired == self.last_acquired:
return
self.frame_ts.append(time.time())
self.last_acquired = status.LastImageAcquired
if self.last_acquired == self.acq_pars.nb_frames - 1:
self.end.set()
def __init__(self, hw_inter):
self.hw_inter = hw_inter
self.ct = Core.CtControl(self.hw_inter)
self.acq = self.ct.acquisition()
self.sync = self.hw_inter.getHwCtrlObj(Core.HwCap.Sync)
def run(self, acq_pars):
if acq_pars.trig_mode == Core.IntTrig:
self.runIntTrig(acq_pars)
else:
self.runIntTrigMult(acq_pars)
def prepare(self, acq_pars):
self.acq.setTriggerMode(acq_pars.trig_mode)
self.acq.setAcqNbFrames(acq_pars.nb_frames)
self.acq.setAcqExpoTime(acq_pars.expo_time)
self.acq.setLatencyTime(acq_pars.lat_time)
self.ct.prepareAcq()
ranges = self.sync.getValidRanges()
print("Valid ranges: ")
print(f" min_exp_time={ranges.min_exp_time:.7f},"
f" max_exp_time={ranges.max_exp_time:.7f}")
print(f" min_lat_time={ranges.min_lat_time:.7f},"
f" max_lat_time={ranges.max_lat_time:.7f}")
def runIntTrig(self, acq_pars):
frame_ts = []
end = Event()
cb = self.Cb(frame_ts, end, acq_pars)
cb.setRatePolicy(cb.RateAllFrames)
self.ct.registerImageStatusCallback(cb)
self.prepare(acq_pars)
print("Starting acquisition ...")
t0 = time.time()
self.ct.startAcq()
end.wait()
trigger_delay = time.time() - t0
while self.ct.getStatus().AcquisitionStatus != Core.AcqReady:
pass
acq_delay = time.time() - t0
if len(frame_ts) != acq_pars.nb_frames:
print(f"Missing triggers: {len(frame_ts)}, expected {acq_pars.nb_frames}")
bin_size = 5e-3
bins = self.splitTimeBins(frame_ts, acq_pars, bin_size)
print(f"Frame delay bins: {len(bins)} ({bin_size} size)")
for (ave, std), b in sorted(bins):
print(f" n={len(b):02d}, ave={ave:.6f}, std={std:.6f}")
first_trigger_delay = frame_ts[0] - t0
point_time = acq_pars.expo_time + acq_pars.lat_time
first_trigger_overhead = first_trigger_delay - point_time
acq_time = acq_pars.nb_frames * point_time
overhead = trigger_delay - acq_time
print(f"First trigger delay: {first_trigger_delay:.3f} "
f"({point_time:.3f} + {first_trigger_overhead:.3f})")
print(f"Trigger delay: {trigger_delay:.3f} "
f"({acq_time:.3f} + {overhead:.3f})")
print(f"Acquisition finished after {acq_delay:.3f} sec "
f"({trigger_delay:.3f} + {acq_delay - trigger_delay:.3f})")
def splitTimeBins(self, ts, acq_pars, bin_size):
deltas = [ts[i + 1] - ts[i] for i in range(len(ts) - 1)]
bins = []
for t in deltas:
for b in bins:
if abs(t - np.average(b)) <= bin_size:
b.append(t)
break
else:
bins.append([t])
return [((np.average(b), np.std(b)), b) for b in bins]
def runIntTrigMult(self, acq_pars):
self.prepare(acq_pars)
print("Starting acquisition ...")
trigger_delay = []
for i in range(nb_frames):
print(f"Sending start #{i}")
self.ct.startAcq()
t0 = time.time()
while self.hw_inter.getStatus().det != Core.DetIdle:
pass
trigger_delay.append(time.time() - t0)
t0 = time.time()
while self.ct.getStatus().AcquisitionStatus != Core.AcqReady:
pass
delay = time.time() - t0
last_delay = trigger_delay.pop(-1)
ave_trigger_delay = sum(trigger_delay) / len(trigger_delay)
overhead = ave_trigger_delay - expo_time
print(f"Average trigger_delay={ave_trigger_delay:.3f} "
f"({expo_time:.3f} + {overhead:.3f})")
delay += last_delay - ave_trigger_delay
print(f"Acquisition finished {delay:.3f} sec after trigger sequence")
if __name__ == '__main__':
verbose = False
nb_frames = 10
expo_time = 0.01
lat_time = 2e-3
trig_mode = 'IntTrigMult'
opts, args = getopt.getopt(sys.argv[1:], 'vn:e:l:t:')
for opt, val in opts:
if opt == '-v':
verbose = True
if opt == '-n':
nb_frames = int(val)
if opt == '-e':
expo_time = float(val)
if opt == '-l':
lat_time = float(val)
if opt == '-t':
trig_mode = val
host = args[0]
acq_pars = AcqPars(getattr(Core, trig_mode), nb_frames, expo_time, lat_time)
if verbose:
db = Core.DebParams
db.setTypeFlagsNameList(['Funct','Trace','Param','Return','Warning','Fatal'])
#db.setTypeFlagsNameList(['Funct', 'Trace','Fatal'])
#db.setModuleFlagsNameList(['Camera'])
cam = Eiger.Camera(host)
hw_inter = Eiger.Interface(cam)
test = TestIntTrig(hw_inter)
test.run(acq_pars)
stream_info = hw_inter.getLastStreamInfo()
print(f"Stream info: encoding={stream_info.encoding}, "
f"packed_size={stream_info.packed_size}")
stream_stats = hw_inter.latchStreamStatistics()
print(f"Stream stats: ave_speed={stream_stats.ave_speed() / 1e6:.3f} MB/s")