15#include <ChimeraTK/BackendFactory.h>
16#include <ChimeraTK/DeviceAccessVersion.h>
18#include <boost/tokenizer.hpp>
25typedef boost::tokenizer<boost::char_separator<char>>
tokenizer;
29 std::string address, std::map<std::string, std::string> parameters) {
45 prepareChannelAccess();
47 fillCatalogueFromMapFile(mapfile);
48 _catalogue_filled =
true;
57 void EpicsBackend::prepareChannelAccess() {
60 if(ca_current_context()) {
63 auto result = ca_context_create(ca_enable_preemptive_callback);
64 if(result != ECA_NORMAL) {
66 ss <<
"CA error " << ca_message(result) <<
"occurred while trying to start channel access.";
67 throw ChimeraTK::runtime_error(ss.str());
76 prepareChannelAccess();
83 _freshCreated =
false;
85 size_t n = default_ca_timeout / 0.1;
86 for(
size_t i = 0; i < n; i++) {
94 std::this_thread::sleep_for(std::chrono::milliseconds(100));
97 throw ChimeraTK::runtime_error(
"Failed to establish channel access connection.");
104 setOpenedAndClearException();
114 ca_context_destroy();
118 if(!isFunctional())
return;
125 size_t n = default_ca_timeout / 0.1;
126 bool allGood =
false;
127 for(
size_t i = 0; i < n; i++) {
132 std::this_thread::sleep_for(std::chrono::milliseconds(100));
135 std::cerr <<
"Failed to receive initial value for all subscriptions in activateAsyncRead()." << std::endl;
140 template<
typename UserType>
142 const RegisterPath& registerPathName,
size_t numberOfWords,
size_t wordOffsetInRegister, AccessModeFlags flags) {
143 RegisterPath path =
"EPICS://" + registerPathName;
147 if(numberOfWords + wordOffsetInRegister > info.
_nElements || (numberOfWords == 0 && wordOffsetInRegister > 0)) {
148 std::stringstream ss;
149 ss <<
"Requested number of words/elements ( " << numberOfWords <<
") with offset " << wordOffsetInRegister
150 <<
" exceeds the number of available words/elements: " << info.
_nElements;
151 throw ChimeraTK::logic_error(ss.str());
154 if(numberOfWords == 0) numberOfWords = info.
_nElements;
156 unsigned base_type = info.
_dbfType % (LAST_TYPE + 1);
157 if(info.
_dbfType == DBR_STSACK_STRING || info.
_dbfType == DBR_CLASS_NAME) base_type = DBR_STRING;
161 return boost::make_shared<EpicsBackendRegisterAccessor<dbr_string_t, dbr_time_string, UserType>>(
162 path, shared_from_this(), info, flags, numberOfWords, wordOffsetInRegister,
_asyncReadActivated);
165 return boost::make_shared<EpicsBackendRegisterAccessor<dbr_float_t, dbr_time_float, UserType>>(
166 path, shared_from_this(), info, flags, numberOfWords, wordOffsetInRegister,
_asyncReadActivated);
169 return boost::make_shared<EpicsBackendRegisterAccessor<dbr_double_t, dbr_time_double, UserType>>(
170 path, shared_from_this(), info, flags, numberOfWords, wordOffsetInRegister,
_asyncReadActivated);
173 return boost::make_shared<EpicsBackendRegisterAccessor<dbr_char_t, dbr_time_char, UserType>>(
174 path, shared_from_this(), info, flags, numberOfWords, wordOffsetInRegister,
_asyncReadActivated);
177 return boost::make_shared<EpicsBackendRegisterAccessor<dbr_short_t, dbr_time_short, UserType>>(
178 path, shared_from_this(), info, flags, numberOfWords, wordOffsetInRegister,
_asyncReadActivated);
181 return boost::make_shared<EpicsBackendRegisterAccessor<dbr_long_t, dbr_time_long, UserType>>(
182 path, shared_from_this(), info, flags, numberOfWords, wordOffsetInRegister,
_asyncReadActivated);
185 return boost::make_shared<EpicsBackendRegisterAccessor<dbr_enum_t, dbr_time_enum, UserType>>(
186 path, shared_from_this(), info, flags, numberOfWords, wordOffsetInRegister,
_asyncReadActivated);
199 throw ChimeraTK::runtime_error(std::string(
"Type ") + std::to_string(info._dbfType) +
" not implemented.");
206 std::cout <<
"BackendRegisterer: registered backend type epics" << std::endl;
210 std::string , std::map<std::string, std::string> parameters) {
211 if(parameters[
"map"].empty()) {
212 throw ChimeraTK::logic_error(
"No map file provided.");
214 return boost::shared_ptr<DeviceBackend>(
new EpicsBackend(parameters[
"map"]));
217 void EpicsBackend::addCatalogueEntry(RegisterPath path, std::shared_ptr<std::string> pvName) {
219 info._caName = std::string(*pvName.get());
224 catch(ChimeraTK::runtime_error& e) {
225 std::cerr << e.what() <<
". PV is not added to the catalog." << std::endl;
231 void EpicsBackend::configureChannel(EpicsBackendRegisterInfo& info) {
235 throw ChimeraTK::runtime_error(
"Trying to read an unconfigured channel.");
240 if(ca_read_access(
pv->
chid) != 1) info._isReadable =
false;
241 if(ca_write_access(
pv->
chid) != 1) info._isWritable =
false;
243 info._dataDescriptor = DataDescriptor(DataDescriptor::FundamentalType::string,
true,
true, 320, 300);
246 info._dataDescriptor = DataDescriptor(DataDescriptor::FundamentalType::numeric,
false,
true, 320, 300);
249 info._dataDescriptor = DataDescriptor(DataDescriptor::FundamentalType::numeric,
true,
true, 320, 300);
252 info._dataDescriptor = DataDescriptor(DataDescriptor::FundamentalType::boolean,
true,
true, 320, 300);
255 std::cerr <<
"Failed to data descriptor for node: " << info._caName <<
"." << std::endl;
257 info._accessModes.add(AccessMode::wait_for_new_data);
260 void EpicsBackend::fillCatalogueFromMapFile(
const std::string& mapfileName) {
261 boost::char_separator<char> sep{
"\t ",
"", boost::drop_empty_tokens};
263 std::ifstream mapfile(mapfileName);
264 if(mapfile.is_open()) {
265 while(std::getline(mapfile, line)) {
266 if(line.empty())
continue;
268 size_t nTokens = std::distance(tok.begin(), tok.end());
269 if(!(nTokens == 2)) {
270 std::cerr <<
"Wrong number of tokens (" << nTokens <<
") in mapfile " << mapfileName
271 <<
" line (-> line is ignored): \n " << line << std::endl;
274 auto it = tok.begin();
277 std::shared_ptr<std::string> pathStr = std::make_shared<std::string>(*it);
278 RegisterPath path(*(pathStr.get()));
280 std::shared_ptr<std::string> nodeName = std::make_shared<std::string>(*it);
281 addCatalogueEntry(path, nodeName);
283 catch(std::out_of_range& e) {
284 std::cerr <<
"Failed reading the following line from mapping file " << mapfileName <<
"\n " << line
291 throw ChimeraTK::runtime_error(std::string(
"Failed reading mapfile: ") + mapfileName);
295 throw ChimeraTK::runtime_error(
"No registers found in catalogue!");
298 auto result = ca_flush_io();
299 if(result == ECA_TIMEOUT) {
300 throw ChimeraTK::runtime_error(
"Channel setup failed.");
302 size_t n = default_ca_timeout / 0.1;
303 for(
size_t i = 0; i < n; i++) {
311 std::this_thread::sleep_for(std::chrono::milliseconds(100));
314 throw ChimeraTK::runtime_error(
"Failed to establish channel access connection.");
317 configureChannel(reg);
std::vector< std::string > ChimeraTK_DeviceAccess_sdmParameterNames
boost::tokenizer< boost::char_separator< char > > tokenizer
std::string ChimeraTK_DeviceAccess_version
boost::shared_ptr< ChimeraTK::DeviceBackend > ChimeraTK_DeviceAccess_createBackend(std::string address, std::map< std::string, std::string > parameters)
void setException(const std::string error)
Push exception to all accessors that are registered.
void cleanup()
Reset the map content.
void addChannel(const std::string name, EpicsBackend *backend)
Add channel to the map and open channel access.
std::shared_ptr< pv > getPV(const std::string &name)
Get pv pointer.
void resetConnectionState()
Reset configuration and connected state for all channels.
static ChannelManager & getInstance()
void activateChannels()
Activate all registered channels.
void addChannelsFromMap(EpicsBackend *backend)
Register all channels in the map and open channel access.
void deactivateChannels()
Deactivate subscription of all registered channels.
void activateAsyncRead() noexcept override
std::atomic< bool > _asyncReadActivated
static BackendRegisterer backendRegisterer
void setExceptionImpl() noexcept override
EpicsBackend(const std::string &mapfile="")
BackendRegisterCatalogue< EpicsBackendRegisterInfo > _catalogue_mutable
We need to make the catalog mutable, since we fill it within getRegisterCatalogue()
static boost::shared_ptr< DeviceBackend > createInstance(std::string address, std::map< std::string, std::string > parameters)
std::atomic< bool > _channelAccessUp
boost::shared_ptr< NDRegisterAccessor< UserType > > getRegisterAccessor_impl(const RegisterPath ®isterPathName, size_t numberOfWords, size_t wordOffsetInRegister, AccessModeFlags flags)
unsigned long nElems
True length of data in value.