PageRenderTime 52ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/ATF2/control-software/epics-3.14.8/extensions/src/ChannelArchiver/Engine/ProcessVariable.cpp

http://atf2flightsim.googlecode.com/
C++ | 691 lines | 599 code | 23 blank | 69 comment | 73 complexity | 0b2c5f5acc5c03116c1b6c636c2170f1 MD5 | raw file
Possible License(s): BSD-2-Clause, LGPL-2.0, IPL-1.0, BSD-3-Clause
  1. // Tools
  2. #include <ToolsConfig.h>
  3. #include <MsgLogger.h>
  4. #include <ThrottledMsgLogger.h>
  5. #include <epicsTimeHelper.h>
  6. // Storage
  7. #include <RawValue.h>
  8. // Local
  9. #include "EngineLocks.h"
  10. #include "ProcessVariable.h"
  11. static ThrottledMsgLogger nulltime_throttle("Null-Timestamp", 60.0);
  12. static ThrottledMsgLogger nanosecond_throttle("Bad Nanoseconds", 60.0);
  13. //#define DEBUG_PV
  14. #define CHECK_EVID
  15. ProcessVariable::ProcessVariable(ProcessVariableContext &ctx, const char *name)
  16. : NamedBase(name),
  17. mutex(name, EngineLocks::ProcessVariable),
  18. ctx(ctx),
  19. state(INIT),
  20. id(0),
  21. ev_id(0),
  22. dbr_type(DBR_TIME_DOUBLE),
  23. dbr_count(1),
  24. outstanding_gets(0)
  25. {
  26. # ifdef DEBUG_PV
  27. printf("new ProcessVariable(%s)\n", getName().c_str());
  28. # endif
  29. {
  30. Guard ctx_guard(__FILE__, __LINE__, ctx);
  31. ctx.incRef(ctx_guard);
  32. }
  33. // Set some default info to match the default getDbrType/Count
  34. ctrl_info.setNumeric(0, "?", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
  35. }
  36. ProcessVariable::~ProcessVariable()
  37. {
  38. if (state != INIT)
  39. {
  40. LOG_MSG("ProcessVariable(%s) destroyed without stopping!\n",
  41. getName().c_str());
  42. // Bad situation.
  43. // If you don't clear the channel, CA might still invoke callbacks
  44. // for a ProcessVariable instance that is no more.
  45. // On the other hand, who knows if the 'id' is valid
  46. // and we won't crash in ca_clear_channel?
  47. // We assume that the id _is_ valid, so protect against further
  48. // callbacks. ca_clear_channel should remove all subscriptions.
  49. if (id != 0)
  50. {
  51. ca_clear_channel(id);
  52. id = 0;
  53. }
  54. }
  55. if (!state_listeners.isEmpty())
  56. {
  57. LOG_MSG("ProcessVariable(%s) still has %zu state listeners\n",
  58. getName().c_str(), (size_t)state_listeners.size());
  59. return;
  60. }
  61. if (!value_listeners.isEmpty())
  62. {
  63. LOG_MSG("ProcessVariable(%s) still has %zu value listeners\n",
  64. getName().c_str(), (size_t)value_listeners.size());
  65. return;
  66. }
  67. try
  68. {
  69. Guard ctx_guard(__FILE__, __LINE__, ctx);
  70. ctx.decRef(ctx_guard);
  71. }
  72. catch (GenericException &e)
  73. {
  74. LOG_MSG("ProcessVariable(%s) destructor: %s\n",
  75. getName().c_str(), e.what());
  76. }
  77. # ifdef DEBUG_PV
  78. printf("deleted ProcessVariable(%s)\n", getName().c_str());
  79. # endif
  80. }
  81. OrderedMutex &ProcessVariable::getMutex()
  82. {
  83. return mutex;
  84. }
  85. ProcessVariable::State ProcessVariable::getState(Guard &guard) const
  86. {
  87. guard.check(__FILE__, __LINE__, mutex);
  88. return state;
  89. }
  90. const char *ProcessVariable::getStateStr(Guard &guard) const
  91. {
  92. guard.check(__FILE__, __LINE__, mutex);
  93. switch (state)
  94. {
  95. case INIT: return "INIT";
  96. case DISCONNECTED: return "DISCONNECTED";
  97. case GETTING_INFO: return "GETTING_INFO";
  98. case CONNECTED: return "CONNECTED";
  99. default: return "<unknown>";
  100. }
  101. }
  102. const char *ProcessVariable::getCAStateStr(Guard &guard) const
  103. {
  104. guard.check(__FILE__, __LINE__, mutex);
  105. chid _id = id;
  106. if (_id == 0)
  107. return "Not Initialized";
  108. enum channel_state cs;
  109. { // Unlock while dealing with CAC.
  110. GuardRelease release(__FILE__, __LINE__, guard);
  111. {
  112. Guard ctx_guard(__FILE__, __LINE__, ctx);
  113. LOG_ASSERT(ctx.isAttached(ctx_guard));
  114. }
  115. cs = ca_state(_id);
  116. }
  117. switch (cs)
  118. {
  119. case cs_never_conn: return "Never Conn.";
  120. case cs_prev_conn: return "Prev. Conn.";
  121. case cs_conn: return "Connected";
  122. case cs_closed: return "Closed";
  123. default: return "unknown";
  124. }
  125. }
  126. void ProcessVariable::start(Guard &guard)
  127. {
  128. guard.check(__FILE__, __LINE__, mutex);
  129. # ifdef DEBUG_PV
  130. printf("start ProcessVariable(%s)\n", getName().c_str());
  131. # endif
  132. LOG_ASSERT(! isRunning(guard));
  133. LOG_ASSERT(state == INIT);
  134. state = DISCONNECTED;
  135. // Unlock around CA lib. calls to prevent deadlocks in callbacks.
  136. int status;
  137. chid _id;
  138. {
  139. GuardRelease release(__FILE__, __LINE__, guard);
  140. {
  141. try
  142. {
  143. status = ca_create_channel(getName().c_str(),
  144. connection_handler,
  145. this, CA_PRIORITY_ARCHIVE, &_id);
  146. }
  147. catch (std::exception &e)
  148. {
  149. LOG_MSG("ProcessVariable::start(%s): %s\n",
  150. getName().c_str(), e.what());
  151. }
  152. catch (...)
  153. {
  154. LOG_MSG("ProcessVariable::start(%s): Unknown Exception\n",
  155. getName().c_str());
  156. }
  157. Guard ctx_guard(__FILE__, __LINE__, ctx);
  158. ctx.requestFlush(ctx_guard);
  159. }
  160. }
  161. id = _id;
  162. if (status != ECA_NORMAL)
  163. LOG_MSG("'%s': ca_create_channel failed, status %s\n",
  164. getName().c_str(), ca_message(status));
  165. }
  166. bool ProcessVariable::isRunning(Guard &guard)
  167. {
  168. guard.check(__FILE__, __LINE__, mutex);
  169. return id != 0;
  170. }
  171. void ProcessVariable::getValue(Guard &guard)
  172. {
  173. guard.check(__FILE__, __LINE__, mutex);
  174. if (state != CONNECTED)
  175. return; // Can't get
  176. ++outstanding_gets;
  177. chid _id = id;
  178. GuardRelease release(__FILE__, __LINE__, guard); // Unlock while in CAC.
  179. {
  180. int status;
  181. try
  182. {
  183. status = ca_array_get_callback(dbr_type, dbr_count,
  184. _id, value_callback, this);
  185. }
  186. catch (std::exception &e)
  187. {
  188. LOG_MSG("ProcessVariable::getValue(%s): %s\n",
  189. getName().c_str(), e.what());
  190. }
  191. catch (...)
  192. {
  193. LOG_MSG("ProcessVariable::getValue(%s): Unknown Exception\n",
  194. getName().c_str());
  195. }
  196. if (status != ECA_NORMAL)
  197. {
  198. LOG_MSG("%s: ca_array_get_callback failed: %s\n",
  199. getName().c_str(), ca_message(status));
  200. return;
  201. }
  202. Guard ctx_guard(__FILE__, __LINE__, ctx);
  203. ctx.requestFlush(ctx_guard);
  204. }
  205. }
  206. #ifdef CHECK_EVID
  207. // Hack by reading oldSubscription definition from private CA headers,
  208. // to check if the ev_id points back to the correct PV.
  209. static void *peek_evid_userptr(evid ev_id)
  210. {
  211. return *((void **) (((char *)ev_id) + 4*sizeof(void *)));
  212. }
  213. #endif
  214. void ProcessVariable::subscribe(Guard &guard)
  215. {
  216. guard.check(__FILE__, __LINE__, mutex);
  217. if (dbr_type == 0)
  218. throw GenericException(__FILE__, __LINE__,
  219. "Cannot subscribe to %s, never connected",
  220. getName().c_str());
  221. // Prevent multiple subscriptions
  222. if (isSubscribed(guard))
  223. return;
  224. // While we were unlocked, a disconnect or stop() could have happend,
  225. // in which case we need to bail out.
  226. if (id == 0 || state != CONNECTED)
  227. {
  228. LOG_MSG("'%s': Skipped subscription, state %s, id 0x%lu.\n",
  229. getName().c_str(), getStateStr(guard), (unsigned long) id);
  230. return;
  231. }
  232. chid _id = id;
  233. evid _ev_id = 0;
  234. DbrType _type = dbr_type;
  235. DbrCount _count = dbr_count;
  236. { // Release around CA call??
  237. // -- GuardRelease release(__FILE__, __LINE__, guard);
  238. // Right now, could a stop() and ca_clear_channel(id) happen,
  239. // so that ca_create_subscription() uses an invalid id?
  240. //
  241. // Task A, CAC client:
  242. // control_callback, pvConnected, subscribe
  243. //
  244. // Task B, Engine or HTTPD:
  245. // stop, clear_channel
  246. //
  247. // LockTest.cpp indicates that the clear_channel() will wait
  248. // for the CAC library to leabe the control_callback.
  249. // So even though we unlock the ProcessVariable and somebody
  250. // could invoke stop() and set id=0, we have the copied _id,
  251. // and the ca_clear_channel(id) won't happen until we leave
  252. // the control_callback.
  253. // This of course only handles the use case of the engine
  254. // where subscribe is invoked from control_callback & pvConnected.
  255. // to be on the safe side, we keep the guard and prevent a stop(),
  256. // until we find a deadlock that forces us to reconsider....
  257. {
  258. int status;
  259. try
  260. {
  261. status = ca_create_subscription(_type, _count, _id,
  262. DBE_LOG | DBE_ALARM,
  263. value_callback, this, &_ev_id);
  264. }
  265. catch (std::exception &e)
  266. {
  267. LOG_MSG("ProcessVariable::subscribe(%s): %s\n",
  268. getName().c_str(), e.what());
  269. }
  270. catch (...)
  271. {
  272. LOG_MSG("ProcessVariable::subscribe(%s): Unknown Exception\n",
  273. getName().c_str());
  274. }
  275. if (status != ECA_NORMAL)
  276. {
  277. LOG_MSG("%s: ca_create_subscription failed: %s\n",
  278. getName().c_str(), ca_message(status));
  279. return;
  280. }
  281. Guard ctx_guard(__FILE__, __LINE__, ctx);
  282. ctx.requestFlush(ctx_guard);
  283. }
  284. }
  285. ev_id = _ev_id;
  286. LOG_ASSERT(ev_id != 0);
  287. #ifdef CHECK_EVID
  288. void *user = peek_evid_userptr(ev_id);
  289. LOG_ASSERT(user == this);
  290. #endif
  291. }
  292. void ProcessVariable::unsubscribe(Guard &guard)
  293. {
  294. guard.check(__FILE__, __LINE__, mutex);
  295. // See comments in stop(): this->id is already 0, state==INIT.
  296. if (isSubscribed(guard))
  297. {
  298. #ifdef CHECK_EVID
  299. void *user = peek_evid_userptr(ev_id);
  300. LOG_ASSERT(user == this);
  301. #endif
  302. evid _ev_id = ev_id;
  303. ev_id = 0;
  304. GuardRelease release(__FILE__, __LINE__, guard);
  305. {
  306. Guard ctx_guard(__FILE__, __LINE__, ctx);
  307. LOG_ASSERT(ctx.isAttached(ctx_guard));
  308. }
  309. try
  310. {
  311. ca_clear_subscription(_ev_id);
  312. }
  313. catch (std::exception &e)
  314. {
  315. LOG_MSG("ProcessVariable::unsubscribe(%s): %s\n",
  316. getName().c_str(), e.what());
  317. }
  318. catch (...)
  319. {
  320. LOG_MSG("ProcessVariable::unsubscribe(%s): Unknown Exception\n",
  321. getName().c_str());
  322. }
  323. }
  324. }
  325. void ProcessVariable::stop(Guard &guard)
  326. {
  327. # ifdef DEBUG_PV
  328. printf("stop ProcessVariable(%s)\n", getName().c_str());
  329. # endif
  330. guard.check(__FILE__, __LINE__, mutex);
  331. LOG_ASSERT(isRunning(guard));
  332. // We'll unlock in unsubscribe(), and then for the ca_clear_channel.
  333. // At those times, an ongoing connection could invoke the connection_handler,
  334. // control_callback or subscribe.
  335. // Setting all indicators back to INIT state will cause those to bail.
  336. chid _id = id;
  337. id = 0;
  338. bool was_connected = (state == CONNECTED);
  339. state = INIT;
  340. outstanding_gets = 0;
  341. unsubscribe(guard);
  342. // Unlock around CA lib. calls to prevent deadlocks.
  343. {
  344. GuardRelease release(__FILE__, __LINE__, guard);
  345. { // If we were subscribed, this was already checked in unsubscribe()...
  346. Guard ctx_guard(__FILE__, __LINE__, ctx);
  347. LOG_ASSERT(ctx.isAttached(ctx_guard));
  348. }
  349. try
  350. {
  351. ca_clear_channel(_id);
  352. }
  353. catch (std::exception &e)
  354. {
  355. LOG_MSG("ProcessVariable::stop(%s): %s\n",
  356. getName().c_str(), e.what());
  357. }
  358. catch (...)
  359. {
  360. LOG_MSG("ProcessVariable::stop(%s): Unknown Exception\n",
  361. getName().c_str());
  362. }
  363. // If there are listeners, tell them that we are disconnected.
  364. if (was_connected)
  365. firePvDisconnected();
  366. }
  367. }
  368. // Channel Access callback
  369. void ProcessVariable::connection_handler(struct connection_handler_args arg)
  370. {
  371. ProcessVariable *me = (ProcessVariable *) ca_puser(arg.chid);
  372. LOG_ASSERT(me != 0);
  373. try
  374. {
  375. if (arg.op == CA_OP_CONN_DOWN)
  376. { // Connection is down
  377. {
  378. Guard guard(__FILE__, __LINE__, *me);
  379. me->state = DISCONNECTED;
  380. }
  381. me->firePvDisconnected();
  382. return;
  383. }
  384. { // else: Connection is 'up'
  385. Guard guard(__FILE__, __LINE__, *me);
  386. if (me->id == 0)
  387. {
  388. LOG_MSG("ProcessVariable(%s) received "
  389. "unexpected connection_handler\n",
  390. me->getName().c_str());
  391. return;
  392. }
  393. # ifdef DEBUG_PV
  394. printf("ProcessVariable(%s) getting control info\n",
  395. me->getName().c_str());
  396. # endif
  397. me->state = GETTING_INFO;
  398. // Get control information for this channel.
  399. // Bug in (at least older) CA: Works only for 1 element,
  400. // even for array channels.
  401. // We are in CAC callback, and managed to lock ourself,
  402. // so it should be OK to issue another CAC call.
  403. try
  404. {
  405. int status = ca_array_get_callback(
  406. ca_field_type(me->id)+DBR_CTRL_STRING,
  407. 1 /* ca_element_count(me->ch_id) */,
  408. me->id, control_callback, me);
  409. if (status != ECA_NORMAL)
  410. {
  411. LOG_MSG("ProcessVariable('%s') connection_handler error %s\n",
  412. me->getName().c_str(), ca_message (status));
  413. return;
  414. }
  415. }
  416. catch (std::exception &e)
  417. {
  418. LOG_MSG("ProcessVariable::connection_handler(%s): %s\n",
  419. me->getName().c_str(), e.what());
  420. }
  421. catch (...)
  422. {
  423. LOG_MSG("ProcessVariable::connection_handler(%s): "
  424. "Unknown Exception\n", me->getName().c_str());
  425. }
  426. }
  427. Guard ctx_guard(__FILE__, __LINE__, me->ctx);
  428. me->ctx.requestFlush(ctx_guard);
  429. }
  430. catch (GenericException &e)
  431. {
  432. LOG_MSG("ProcessVariable(%s) connection_handler exception:\n%s\n",
  433. me->getName().c_str(), e.what());
  434. }
  435. }
  436. // Fill crtl_info from raw dbr_ctrl_xx data
  437. bool ProcessVariable::setup_ctrl_info(Guard &guard,
  438. DbrType type, const void *dbr_ctrl_xx)
  439. {
  440. switch (type)
  441. {
  442. case DBR_CTRL_DOUBLE:
  443. {
  444. struct dbr_ctrl_double *ctrl =
  445. (struct dbr_ctrl_double *)dbr_ctrl_xx;
  446. ctrl_info.setNumeric(ctrl->precision, ctrl->units,
  447. ctrl->lower_disp_limit,
  448. ctrl->upper_disp_limit,
  449. ctrl->lower_alarm_limit,
  450. ctrl->lower_warning_limit,
  451. ctrl->upper_warning_limit,
  452. ctrl->upper_alarm_limit);
  453. }
  454. return true;
  455. case DBR_CTRL_SHORT:
  456. {
  457. struct dbr_ctrl_int *ctrl = (struct dbr_ctrl_int *)dbr_ctrl_xx;
  458. ctrl_info.setNumeric(0, ctrl->units,
  459. ctrl->lower_disp_limit,
  460. ctrl->upper_disp_limit,
  461. ctrl->lower_alarm_limit,
  462. ctrl->lower_warning_limit,
  463. ctrl->upper_warning_limit,
  464. ctrl->upper_alarm_limit);
  465. }
  466. return true;
  467. case DBR_CTRL_FLOAT:
  468. {
  469. struct dbr_ctrl_float *ctrl = (struct dbr_ctrl_float *)dbr_ctrl_xx;
  470. ctrl_info.setNumeric(ctrl->precision, ctrl->units,
  471. ctrl->lower_disp_limit,
  472. ctrl->upper_disp_limit,
  473. ctrl->lower_alarm_limit,
  474. ctrl->lower_warning_limit,
  475. ctrl->upper_warning_limit,
  476. ctrl->upper_alarm_limit);
  477. }
  478. return true;
  479. case DBR_CTRL_CHAR:
  480. {
  481. struct dbr_ctrl_char *ctrl = (struct dbr_ctrl_char *)dbr_ctrl_xx;
  482. ctrl_info.setNumeric(0, ctrl->units,
  483. ctrl->lower_disp_limit,
  484. ctrl->upper_disp_limit,
  485. ctrl->lower_alarm_limit,
  486. ctrl->lower_warning_limit,
  487. ctrl->upper_warning_limit,
  488. ctrl->upper_alarm_limit);
  489. }
  490. return true;
  491. case DBR_CTRL_LONG:
  492. {
  493. struct dbr_ctrl_long *ctrl = (struct dbr_ctrl_long *)dbr_ctrl_xx;
  494. ctrl_info.setNumeric(0, ctrl->units,
  495. ctrl->lower_disp_limit,
  496. ctrl->upper_disp_limit,
  497. ctrl->lower_alarm_limit,
  498. ctrl->lower_warning_limit,
  499. ctrl->upper_warning_limit,
  500. ctrl->upper_alarm_limit);
  501. }
  502. return true;
  503. case DBR_CTRL_ENUM:
  504. {
  505. size_t i;
  506. struct dbr_ctrl_enum *ctrl = (struct dbr_ctrl_enum *)dbr_ctrl_xx;
  507. ctrl_info.allocEnumerated(ctrl->no_str,
  508. MAX_ENUM_STATES*MAX_ENUM_STRING_SIZE);
  509. for (i=0; i<(size_t)ctrl->no_str; ++i)
  510. ctrl_info.setEnumeratedString(i, ctrl->strs[i]);
  511. ctrl_info.calcEnumeratedSize();
  512. }
  513. return true;
  514. case DBR_CTRL_STRING: // no control information
  515. ctrl_info.setEnumerated(0, 0);
  516. return true;
  517. }
  518. LOG_MSG("ProcessVariable(%s) setup_ctrl_info cannot handle type %d\n",
  519. getName().c_str(), type);
  520. ctrl_info.setEnumerated(0, 0);
  521. return false;
  522. }
  523. // Channel Access callback
  524. // Should be invoked with the control info 'get' data,
  525. // requested in the connection handler.
  526. void ProcessVariable::control_callback(struct event_handler_args arg)
  527. {
  528. ProcessVariable *me = (ProcessVariable *) ca_puser(arg.chid);
  529. LOG_ASSERT(me != 0);
  530. if (arg.status != ECA_NORMAL)
  531. {
  532. LOG_MSG("ProcessVariable(%s) control_callback failed: %s\n",
  533. me->getName().c_str(), ca_message(arg.status));
  534. return;
  535. }
  536. try
  537. {
  538. {
  539. Guard guard(__FILE__, __LINE__, *me);
  540. if (me->state != GETTING_INFO || me->id == 0)
  541. {
  542. LOG_MSG("ProcessVariable(%s) received control_callback while %s, id=0x%lX\n",
  543. me->getName().c_str(), me->getStateStr(guard), (unsigned long) me->id);
  544. return;
  545. }
  546. // For native type DBR_xx, use DBR_TIME_xx, and native count:
  547. me->dbr_type = ca_field_type(me->id) + DBR_TIME_STRING;
  548. me->dbr_count = ca_element_count(me->id);
  549. me->state = CONNECTED;
  550. // Setup the PV info.
  551. if (!me->setup_ctrl_info(guard, arg.type, arg.dbr))
  552. return;
  553. }
  554. // Notify listeners that PV is now fully connected.
  555. me->firePvConnected();
  556. }
  557. catch (GenericException &e)
  558. {
  559. LOG_MSG("ProcessVariable(%s) control_callback exception:\n%s\n",
  560. me->getName().c_str(), e.what());
  561. }
  562. }
  563. // Channel Access callback
  564. // Each subscription monitor or 'get' callback goes here:
  565. void ProcessVariable::value_callback(struct event_handler_args arg)
  566. {
  567. ProcessVariable *me = (ProcessVariable *) ca_puser(arg.chid);
  568. LOG_ASSERT(me != 0);
  569. // Check if there is a useful value at all.
  570. if (arg.status != ECA_NORMAL)
  571. {
  572. LOG_MSG("ProcessVariable(%s) value_callback failed: %s\n",
  573. me->getName().c_str(), ca_message(arg.status));
  574. return;
  575. }
  576. const RawValue::Data *value = (const RawValue::Data *)arg.dbr;
  577. LOG_ASSERT(value != 0);
  578. // Catch records that never processed and give null times.
  579. if (value->stamp.secPastEpoch == 0)
  580. {
  581. nulltime_throttle.LOG_MSG("ProcessVariable::value_callback(%s) "
  582. "with invalid null timestamp\n", me->getName().c_str());
  583. return;
  584. }
  585. // Check the nanoseconds of each incoming value. Problems will arise later
  586. // unless they are normalized to less than one second.
  587. if (value->stamp.nsec >= 1000000000L)
  588. {
  589. epicsTimeStamp s = value->stamp;
  590. s.nsec = 0;
  591. epicsTime t = s;
  592. stdString txt;
  593. epicsTime2string(t, txt);
  594. nanosecond_throttle.LOG_MSG("ProcessVariable::value_callback(%s) "
  595. "with invalid secs/nsecs %zu, %zu: %s\n",
  596. me->getName().c_str(),
  597. (size_t) value->stamp.secPastEpoch,
  598. (size_t) value->stamp.nsec,
  599. txt.c_str());
  600. return;
  601. }
  602. try
  603. {
  604. {
  605. // Do we expect a value because of 'get' or subscription?
  606. Guard guard(__FILE__, __LINE__, *me);
  607. if (me->outstanding_gets > 0)
  608. --me->outstanding_gets;
  609. else if (me->isSubscribed(guard) == false)
  610. {
  611. LOG_MSG("ProcessVariable::value_callback(%s): not expected\n",
  612. me->getName().c_str());
  613. return;
  614. }
  615. # ifdef CHECK_EVID
  616. if (me->ev_id)
  617. {
  618. void *user = peek_evid_userptr(me->ev_id);
  619. LOG_ASSERT(user == me);
  620. }
  621. # endif
  622. if (me->state != CONNECTED)
  623. {
  624. // After a disconnect, this can happen in the GETTING_INFO state:
  625. // The CAC lib already sends us new monitors after the re-connect,
  626. // while we wait for the ctrl-info get_callback to finish.
  627. if (me->state == GETTING_INFO) // ignore
  628. return;
  629. LOG_MSG("ProcessVariable(%s) received value_callback while %s\n",
  630. me->getName().c_str(), me->getStateStr(guard));
  631. return;
  632. }
  633. }
  634. // Finally, notify listeners of new value.
  635. me->firePvValue(value);
  636. }
  637. catch (GenericException &e)
  638. {
  639. LOG_MSG("ProcessVariable(%s) value_callback exception:\n%s\n",
  640. me->getName().c_str(), e.what());
  641. }
  642. }
  643. void ProcessVariable::firePvConnected()
  644. {
  645. epicsTime now = epicsTime::getCurrent();
  646. ConcurrentListIterator<ProcessVariableStateListener>
  647. l(state_listeners.iterator());
  648. ProcessVariableStateListener *listener;
  649. while ((listener = l.next()) != 0)
  650. listener->pvConnected(*this, now);
  651. }
  652. void ProcessVariable::firePvDisconnected()
  653. {
  654. epicsTime now = epicsTime::getCurrent();
  655. ConcurrentListIterator<ProcessVariableStateListener>
  656. l(state_listeners.iterator());
  657. ProcessVariableStateListener *listener;
  658. while ((listener = l.next()) != 0)
  659. listener->pvDisconnected(*this, now);
  660. }
  661. void ProcessVariable::firePvValue(const RawValue::Data *value)
  662. {
  663. ConcurrentListIterator<ProcessVariableValueListener>
  664. l(value_listeners.iterator());
  665. ProcessVariableValueListener *listener;
  666. while ((listener = l.next()) != 0)
  667. listener->pvValue(*this, value);
  668. }