-
Martin Christoph Hierholzer authoredMartin Christoph Hierholzer authored
DoocsProcessArray.h 4.18 KiB
#ifndef __DOOCS_PROCESS_ARRAY_H__
#define __DOOCS_PROCESS_ARRAY_H__
#include <D_spectrum.h>
#include <boost/noncopyable.hpp>
#include <ChimeraTK/OneDRegisterAccessor.h>
#include <ChimeraTK/ControlSystemAdapter/ProcessVariableListener.h>
#include <ChimeraTK/ControlSystemAdapter/ControlSystemSynchronizationUtility.h>
#include "splitStringAtFirstSlash.h"
#include <eq_fct.h>
namespace ChimeraTK {
template<typename DOOCS_T, typename DOOCS_PRIMITIVE_T>
class DoocsProcessArray : public DOOCS_T, public boost::noncopyable {
public:
DoocsProcessArray(EqFct* eqFct, std::string const& doocsPropertyName,
boost::shared_ptr<ChimeraTK::NDRegisterAccessor<DOOCS_PRIMITIVE_T>> const& processArray, DoocsUpdater& updater)
: DOOCS_T(doocsPropertyName.c_str(), processArray->getNumberOfSamples(), eqFct), _processArray(processArray) {
if(processArray->isReadable()) {
updater.addVariable(ChimeraTK::OneDRegisterAccessor<DOOCS_PRIMITIVE_T>(processArray), eqFct,
std::bind(&DoocsProcessArray<DOOCS_T, DOOCS_PRIMITIVE_T>::updateDoocsBuffer, this));
}
// Check if the array length exceeds the maximum allowed length by DOOCS. DOOCS does not report this as an
// error and instead silently truncates the array length.
if(processArray->getNumberOfSamples() != static_cast<size_t>(this->length())) {
std::stringstream s;
s << "Error: The selected DOOCS data type for the variable '" << processArray->getName() << "' "
<< "(mapped to the DOOCS name '" << doocsPropertyName << "') seems not to support the requested length of "
<< processArray->getNumberOfSamples() << " since the DOOCS property has a length of " << this->length()
<< ". Try selectin a different DOOCS type in the mappng XML file, e.g. a D_spectrum!";
throw ChimeraTK::logic_error(s.str());
}
}
/**
* Overload the set function which is called by DOOCS to inject sending to the device.
*/
void set(EqAdr* eqAdr, EqData* data1, EqData* data2, EqFct* eqFct) override {
DOOCS_T::set(eqAdr, data1, data2, eqFct);
sendToDevice();
}
/**
* Override the Doocs auto_init() method, which is called after initialising the value of
* the property from the config file.
*/
void auto_init(void) override {
DOOCS_T::auto_init();
// send the current value to the device
if(_processArray->isWriteable()) {
sendToDevice();
}
}
void updateDoocsBuffer() {
// Note: we already own the location lock by specification of the DoocsUpdater
auto& processVector = _processArray->accessChannel(0);
// We have to cast the pointer to the correct underlying DOOCS type. This cast never does anything, the only
// reason is to "convert" from int64_t to long long int (which are different types!)
typedef typename std::result_of<decltype (&DOOCS_T::value)(DOOCS_T, int)>::type THE_DOOCS_TYPE;
static_assert(std::is_same<THE_DOOCS_TYPE, DOOCS_PRIMITIVE_T>::value ||
(std::is_same<DOOCS_PRIMITIVE_T, int64_t>::value && std::is_same<THE_DOOCS_TYPE, long long int>::value),
"Bad type casting.");
auto dataPtr = reinterpret_cast<THE_DOOCS_TYPE*>(processVector.data());
this->fill_array(dataPtr, processVector.size());
if(publishZMQ) {
dmsg_info info;
memset(&info, 0, sizeof(info));
this->send(&info);
}
}
void publishZeroMQ() { publishZMQ = true; }
protected:
boost::shared_ptr<ChimeraTK::NDRegisterAccessor<DOOCS_PRIMITIVE_T>> _processArray;
bool publishZMQ{false};
// Internal function which copies the content from the DOOCS container into the
// ChimeraTK ProcessArray and calls the send method. Factored out to allow unit testing.
void sendToDevice() {
// Brute force implementation with a loop. Works for all data types.
// always get a fresh reference
auto& processVector = _processArray->accessChannel(0);
size_t arraySize = processVector.size();
for(size_t i = 0; i < arraySize; ++i) {
processVector[i] = this->value(i);
}
_processArray->write();
}
};
} // namespace ChimeraTK
#endif // __DOOCS_PROCESS_ARRAY_H__