EpicsConnection.cpp
Go to the documentation of this file.
1 #include <rms/provider/EpicsConnection.h>
2 #include <unistd.h>
3 
4 namespace gov {
5 
6 namespace fnal {
7 
8 namespace cd {
9 
10 namespace rms {
11 
12 namespace provider {
13 
14 /**
15  * The character that is used a delimiter is PV names.
16  */
18 
19 /**
20  * Create a new EpicsConnection given the application name and partition.
21  *
22  * @param applicationName The name of the application generating/receiving
23  * messages.
24  * @param applicationPartition The partition of the application that is
25  * generating/receiving messages.
26  */
28  const int applicationPartition) {
29  char hostname[20];
30 
31  ca_context_create(ca_enable_preemptive_callback);
32  _caCurrentContext = ca_current_context();
33 
34  _messageSource.setProperty(base::RmsDestination::TARGET_PROPERTY_NAME, applicationName);
35  _messageSource.setPartitionProperty(applicationPartition);
36 
37  if (gethostname(hostname, 20) == 0) {
38  _uuidGenerator = new util::UUIDGenerator(hostname);
39  }
40  else {
41  _uuidGenerator = new util::UUIDGenerator("C++EpicsConnection");
42  }
43 }
44 
45 /**
46  * Destructor, call close() and have it clean up
47  * everything.
48  */
50  close();
51 }
52 
53 /**
54  * Determine if the given destination can be reached with
55  * the EPICS provider.
56  *
57  * @param dest The destination to check to
58  * see if messages can be sent to it.
59  *
60  * @return True if the destination can be reached, false otherwise.
61  */
62 bool EpicsConnection::supportsDestination(const base::RmsDestination& dest) {
63  std::string destinationPV;
64  std::string monitoringOutboxPV;
65  EpicsMessenger *pvHandle;
66 
67  destinationPV = getDestinationPVFromDest(dest);
68  monitoringOutboxPV = getMonitoringPVFromDest(dest);
69 
70  pvHandle = getPVHandle(destinationPV);
71 
72  if (!pvHandle) {
73  return false;
74  }
75 
76  pvHandle = getPVHandle(monitoringOutboxPV);
77 
78  if (!pvHandle) {
79  return false;
80  }
81 
82  return true;
83 }
84 
85 /**
86  * Send a string to a particular destination.
87  *
88  * @param dest The destination that the message is going to
89  * @param messageString The string that is being sent
90  */
91 void EpicsConnection::sendString(const base::RmsDestination& dest,
92  const std::string& messageString) {
93  std::string destinationPV;
94  EpicsMessenger *pvHandle;
95 
96  destinationPV = getDestinationPVFromDest(dest);
97  pvHandle = getPVHandle(destinationPV);
98 
99  if (pvHandle) {
100  boost::mutex::scoped_lock lk(_epicsLock);
101  pvHandle->sendMessage(messageString);
102  }
103  else {
104  std::cout << "Unable to create PV handler for:" << std::endl;
105  std::cout << destinationPV << std::endl;
106  std::cout << "in EpicsConnection.sendMessage()" << std::endl;
107  }
108 
109  return;
110 }
111 
112 /**
113  * Send an RmsMessage to a particular destination.
114  *
115  * @param dest The destination that the message is going to
116  * @param message The RmsMessage that is being sent
117  */
118 void EpicsConnection::sendMessage(const base::RmsDestination& dest,
119  const base::RmsMessage& message) {
120  std::string destinationPV;
121  std::string monitoringOutboxPV;
122  EpicsMessenger *pvHandle;
123 
124  destinationPV = getDestinationPVFromDest(dest);
125  monitoringOutboxPV = getMonitoringPVFromDest(message.getReplyDestination());
126  std::string serializedMessage = message.serialize();
127 
128  pvHandle = getPVHandle(destinationPV);
129 
130  if (pvHandle) {
131  boost::mutex::scoped_lock lk(_epicsLock);
132  pvHandle->sendMessage(serializedMessage);
133  }
134  else {
135  std::cout << "Unable to create PV handler for:" << std::endl;
136  std::cout << destinationPV << std::endl;
137  std::cout << "in EpicsConnection.sendMessage()" << std::endl;
138  return;
139  }
140 
141  pvHandle = getPVHandle(monitoringOutboxPV);
142 
143  if (pvHandle) {
144  boost::mutex::scoped_lock lk(_epicsLock);
145  pvHandle->sendMessage(serializedMessage);
146  }
147  else {
148  std::cout << "Unable to create PV handler for:" << std::endl;
149  std::cout << monitoringOutboxPV << std::endl;
150  std::cout << "in EpicsConnection.sendMessage()" << std::endl;
151  return;
152  }
153 
154  return;
155 }
156 
157 /**
158  * Add a listener to a particular PV represented by a destination.
159  *
160  * @param dest The destination that we will be listening to.
161  * @param listener The listener that will be called when messages
162  * are posted to the PV.
163  */
164 void EpicsConnection::addListener(const base::RmsDestination& dest,
165  ProviderListener *listener) {
166  std::string destinationPV;
167  EpicsMessenger *pvHandle;
168 
169  /**
170  * If the target is empty, return the PV for the monitoring
171  * outbox.
172  */
173  if (dest.getProperty(base::RmsDestination::TARGET_PROPERTY_NAME) !=
174  std::string("")) {
175  destinationPV = getDestinationPVFromDest(dest);
176  }
177  else {
178  destinationPV = getMonitoringPVFromDest(dest);
179  }
180 
181  pvHandle = getPVHandle(destinationPV);
182 
183  if (pvHandle) {
184  boost::mutex::scoped_lock lk(_epicsLock);
185  pvHandle->addListener(listener);
186  }
187  else {
188  std::cout << "Unable to create PV handle for:" << std::endl;
189  std::cout << destinationPV << std::endl;
190  std::cout << "in EpicsConnection.addListener()" << std::endl;
191  }
192 
193  return;
194 }
195 
196 /**
197  * Remove a listener from the EpicsConnection so that it no
198  * longer receives messages.
199  *
200  * @param dest The destination to remove.
201  * @param listener The listener to remove.
202  */
203 void EpicsConnection::removeListener(const base::RmsDestination& dest, ProviderListener *listener) {
204  std::string destinationPV;
205  EpicsMessenger *pvHandle;
206 
207  /**
208  * If the target is empty, return the PV for the monitoring
209  * outbox.
210  */
211  if (dest.getProperty(base::RmsDestination::TARGET_PROPERTY_NAME) !=
212  std::string("")) {
213  destinationPV = getDestinationPVFromDest(dest);
214  }
215  else {
216  destinationPV = getMonitoringPVFromDest(dest);
217  }
218 
219  pvHandle = getPVHandle(destinationPV);
220 
221  if (pvHandle) {
222  boost::mutex::scoped_lock lk(_epicsLock);
223  pvHandle->removeListener(listener);
224  }
225  else {
226  std::cout << "Unable to create PV handle for:" << std::endl;
227  std::cout << destinationPV << std::endl;
228  std::cout << "in EpicsConnection.removeListener()" << std::endl;
229  }
230 
231  return;
232 }
233 
234 /**
235  * Try to connect to a PV to verify that it exists and is runing.
236  *
237  * @param pingDestination The destination that we are going to ping.
238  *
239  * @return True is the PV exists and is running, false otherwise.
240  */
241 bool EpicsConnection::ping(const base::RmsDestination& pingDestination) {
242  chid channelID;
243  int result;
244 
245  std::string destinationPV = getDestinationPVFromDest(pingDestination);
246 
247  boost::mutex::scoped_lock lk(_epicsLock);
248  ca_attach_context(_caCurrentContext);
249 
250  ca_create_channel(destinationPV.c_str(), NULL, NULL, 10, &channelID);
251  result = ca_pend_io(0.5);
252  ca_clear_channel(channelID);
253 
254  if (result != ECA_NORMAL) {
255  return false;
256  }
257 
258  return true;
259 }
260 
261 /**
262  * Free the memory allocated for the UUID generator and
263  * delete any instances of the EpicsMessenger class that
264  * were created. Finally, call ca_context_destroy().
265  */
267  std::map <std::string, EpicsMessenger *>::iterator cacheIterator;
268 
269  if (_uuidGenerator) {
270  delete _uuidGenerator;
271  _uuidGenerator = NULL;
272  }
273 
274  boost::mutex::scoped_lock lk(_epicsLock);
275 
276  cacheIterator = _cachedPVs.begin();
277 
278  while (cacheIterator != _cachedPVs.end()) {
279  delete cacheIterator->second;
280  cacheIterator++;
281  }
282 
283  _cachedPVs.clear();
284 
285  if (_caCurrentContext) {
286  ca_attach_context(_caCurrentContext);
287  ca_context_destroy();
288  _caCurrentContext = NULL;
289  }
290 }
291 
292 /**
293  * Translate a destination object into an Epics PV.
294  *
295  * @param dest The destination object that will be translated
296  * into a PV.
297  *
298  * @return The name of the PV for the location that the destination
299  * object represents.
300  */
302 EpicsConnection::getDestinationPVFromDest(const base::RmsDestination& dest) const {
303  std::string destinationPV;
304 
305  std::string destinationTarget =
307  std::string destinationChannel =
309  std::string destinationPartition =
311  std::string destinationEnvironment =
313 
314  /**
315  * The heartbeat channel is a special case. For each target, their will
316  * be one heartbeat channel. All heartbeat messages will be pushed out
317  * to the outbox.
318  */
319  if(destinationChannel == base::RmsDestination::HEARTBEAT_CHANNEL) {
320  if (destinationEnvironment != "") {
321  destinationPV.append(destinationEnvironment + "_");
322  }
323 
324  destinationPV.append(destinationTarget);
325  destinationPV.append(EpicsConnection::PV_DELIMITER);
326  destinationPV.append(destinationChannel);
327  destinationPV.append(EpicsConnection::PV_DELIMITER);
328  destinationPV.append("outbox");
329  return destinationPV;
330  }
331 
332  if (destinationEnvironment != "") {
333  destinationPV.append(destinationEnvironment + "_");
334  }
335 
336  destinationPV.append(destinationTarget);
337  destinationPV.append(EpicsConnection::PV_DELIMITER);
338  destinationPV.append(destinationPartition);
339  destinationPV.append(EpicsConnection::PV_DELIMITER);
340  destinationPV.append(destinationChannel);
341  destinationPV.append(EpicsConnection::PV_DELIMITER);
342  destinationPV.append("inbox");
343 
344  return destinationPV;
345 }
346 
347 /**
348  * Find the name of the monitoring outbox that a message should be
349  * posted to when sending to a particular destination.
350  *
351  * @param dest The destination that the message is going to
352  *
353  * @return This will return the name of the PV for the
354  * monitoring outbox that the message should be posted to.
355  */
357 EpicsConnection::getMonitoringPVFromDest(const base::RmsDestination& dest) const {
358  std::string targetName =
360  std::string destinationPartition =
362  std::string destinationChannel =
364 
365  /**
366  * There is no monitoring outbox for the heartbeat channel
367  */
368  if (destinationChannel == base::RmsDestination::HEARTBEAT_CHANNEL) {
369  return std::string("");
370  }
371 
372  if (targetName == std::string("")) {
373  targetName = dest.getProperty(base::RmsDestination::SOURCE_PROPERTY_NAME);
374  }
375 
376  targetName.append(EpicsConnection::PV_DELIMITER);
377  targetName.append(destinationPartition);
378  targetName.append(EpicsConnection::PV_DELIMITER);
379  targetName.append(destinationChannel);
380  targetName.append(EpicsConnection::PV_DELIMITER);
381  targetName.append("monitoringOutbox");
382 
383  std::string destinationEnvironment =
385 
386  if (destinationEnvironment != "") {
387  targetName.insert(0, destinationEnvironment + "_");
388  }
389 
390  return targetName;
391 }
392 
393 /**
394  * Obtain a handle to a particular PV. If the handle has already
395  * been created, find it in the _cachedPVs table and return it.
396  * Otherwise, create a new one and insert it into the _cachedPVs
397  * table.
398  *
399  * @param pvName The name of the PV to get a handle for.
400  *
401  * @return An EpicsMessenger for a particular PV.
402  */
404  EpicsMessenger *pvHandle;
405  std::map <std::string, EpicsMessenger *>::iterator cacheIterator;
406 
407  boost::mutex::scoped_lock lk(_epicsLock);
408 
409  cacheIterator = _cachedPVs.find(pvName);
410 
411  if (cacheIterator == _cachedPVs.end()) {
412  pvHandle =
413  new EpicsMessenger(pvName, _caCurrentContext,
414  1024, _uuidGenerator);
415  _cachedPVs.insert(std::pair<std::string, EpicsMessenger *>
416  (pvName, pvHandle));
417  }
418  else {
419  return cacheIterator->second;
420  }
421 
422  return pvHandle;
423 }
424 
425 }; // end of namespace provider
426 
427 }; // end of namespace rms
428 
429 }; // end of namespace cd
430 
431 }; // end of namespace fnal
432 
433 }; // end of namespace gov
bool ping(const base::RmsDestination &pingDestination)
void addListener(const base::RmsDestination &dest, ProviderListener *listener)
void sendMessage(const base::RmsDestination &dest, const base::RmsMessage &message)
static const std::string PARTITION_PROPERTY_NAME
std::map< std::string, EpicsMessenger * > _cachedPVs
static const std::string SOURCE_PROPERTY_NAME
struct ca_client_context * _caCurrentContext
EpicsConnection(const std::string applicationName, const int applicationPartition)
static const std::string HEARTBEAT_CHANNEL
void removeListener(ProviderListener *listener)
void addListener(ProviderListener *listener)
static const std::string CHANNEL_PROPERTY_NAME
bool supportsDestination(const base::RmsDestination &candidateDestination)
std::string getMonitoringPVFromDest(const base::RmsDestination &dest) const
Definition: fnal.py:1
void sendMessage(const std::string &messageText)
void sendString(const base::RmsDestination &dest, const std::string &messageString)
static const std::string ENVIRONMENT_PROPERTY_NAME
void removeListener(const base::RmsDestination &dest, ProviderListener *listener)
OStream cout
Definition: OStream.cxx:6
std::string getDestinationPVFromDest(const base::RmsDestination &dest) const
EpicsMessenger * getPVHandle(std::string pvName)
static const std::string TARGET_PROPERTY_NAME
c cd(1)
enum BeamMode string