PageRenderTime 27ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

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

http://atf2flightsim.googlecode.com/
C++ | 500 lines | 399 code | 26 blank | 75 comment | 50 complexity | bd830bb06e107eefe566be7d58e9c862 MD5 | raw file
Possible License(s): BSD-2-Clause, LGPL-2.0, IPL-1.0, BSD-3-Clause
  1. // Tools
  2. #include <MsgLogger.h>
  3. // Engine
  4. #include "EngineLocks.h"
  5. #include "ArchiveChannel.h"
  6. #include "GroupInfo.h"
  7. #include "SampleMechanismMonitored.h"
  8. #include "SampleMechanismGet.h"
  9. #include "SampleMechanismMonitoredGet.h"
  10. //#define DEBUG_ARCHIVE_CHANNEL
  11. ArchiveChannel::ArchiveChannel(EngineConfig &config,
  12. ProcessVariableContext &ctx,
  13. ScanList &scan_list,
  14. const char *channel_name,
  15. double scan_period, bool monitor)
  16. : NamedBase(channel_name),
  17. config(config),
  18. mutex("ArchiveChannel", EngineLocks::ArchiveChannel),
  19. scan_period(scan_period),
  20. monitor(monitor),
  21. sample_mechanism_busy(false),
  22. currently_disabling(false),
  23. disable_count(0)
  24. {
  25. sample_mechanism = createSampleMechanism(config, ctx, scan_list);
  26. LOG_ASSERT(sample_mechanism);
  27. }
  28. ArchiveChannel::~ArchiveChannel()
  29. {
  30. if (sample_mechanism_busy)
  31. LOG_MSG("ArchiveChannel '%s': sample mechanism busy in destructor\n",
  32. getName().c_str());
  33. if (sample_mechanism)
  34. {
  35. try
  36. {
  37. sample_mechanism->removeStateListener(this);
  38. bool can_disable;
  39. {
  40. Guard guard(__FILE__, __LINE__, *this);
  41. can_disable = canDisable(guard);
  42. }
  43. if (can_disable)
  44. sample_mechanism->removeValueListener(this);
  45. }
  46. catch (GenericException &e)
  47. {
  48. LOG_MSG("ArchiveChannel '%s': %s\n", getName().c_str(), e.what());
  49. }
  50. catch (...)
  51. {
  52. LOG_MSG("ArchiveChannel '%s': exception in destructor\n",
  53. getName().c_str());
  54. }
  55. }
  56. sample_mechanism = 0;
  57. }
  58. OrderedMutex &ArchiveChannel::getMutex()
  59. {
  60. return mutex;
  61. }
  62. // In order to allow as much concurrency with CA,
  63. // the ArchiveChannel unlocks whenever possible,
  64. // most important while writing.
  65. // But we must prevent changes to the sample_mechanism,
  66. // most important not change it in configure(),
  67. // while the sample_mechanism is used for start/stop/write.
  68. // So anything that uses the sample_mechanism sets
  69. // the sample_mechanism_busy flag, can then unlock the ArchiveChannel,
  70. // and finally resets the flag.
  71. // In short:
  72. // Lock ArchiveChannel, then use sample_mechanism: OK.
  73. // Lock ArchiveChannel, find sample_mechanism_busy=false,
  74. // then set sample_mechanism_busy, unlock, and use sample_mechanism: OK.
  75. // Find sample_mechanism_busy, unlock the ArchiveChannel,
  76. // then use the sample_mechanism: BAD.
  77. void ArchiveChannel::waitWhileSampleMechanismBusy(Guard &guard)
  78. {
  79. while (sample_mechanism_busy)
  80. {
  81. GuardRelease release(__FILE__, __LINE__, guard);
  82. LOG_MSG("ArchiveChannel '%s' waiting for busy sample_mechanism.\n",
  83. getName().c_str());
  84. epicsThreadSleep(1.0);
  85. }
  86. }
  87. void ArchiveChannel::configure(ProcessVariableContext &ctx,
  88. ScanList &scan_list,
  89. double scan_period, bool monitor)
  90. {
  91. # ifdef DEBUG_ARCHIVE_CHANNEL
  92. LOG_MSG("ArchiveChannel '%s' reconfig...\n",
  93. getName().c_str());
  94. # endif
  95. LOG_ASSERT(sample_mechanism);
  96. // Check args, stop old sample mechanism.
  97. Guard guard(__FILE__, __LINE__, *this);
  98. waitWhileSampleMechanismBusy(guard);
  99. LOG_ASSERT(!sample_mechanism_busy);
  100. {
  101. Guard sample_guard(__FILE__, __LINE__, *sample_mechanism);
  102. LOG_ASSERT(!sample_mechanism->isRunning(sample_guard));
  103. // If anybody wants to monitor, use monitor
  104. if (monitor)
  105. this->monitor = true;
  106. // Is scan_period initialized?
  107. if (this->scan_period <= 0.0)
  108. this->scan_period = scan_period;
  109. else if (this->scan_period > scan_period) // minimize
  110. this->scan_period = scan_period;
  111. }
  112. sample_mechanism->removeStateListener(this);
  113. if (canDisable(guard))
  114. sample_mechanism->removeValueListener(this);
  115. // Replace with new one
  116. sample_mechanism = createSampleMechanism(config, ctx, scan_list);
  117. LOG_ASSERT(sample_mechanism);
  118. if (canDisable(guard))
  119. sample_mechanism->addValueListener(this);
  120. }
  121. SampleMechanism *ArchiveChannel::createSampleMechanism(
  122. EngineConfig &config, ProcessVariableContext &ctx, ScanList &scan_list)
  123. {
  124. AutoPtr<SampleMechanism> new_mechanism;
  125. // Determine new mechanism
  126. if (monitor)
  127. new_mechanism = new SampleMechanismMonitored(config, ctx,
  128. getName().c_str(),
  129. scan_period);
  130. else if (scan_period >= config.getGetThreshold())
  131. new_mechanism = new SampleMechanismGet(config, ctx, scan_list,
  132. getName().c_str(),
  133. scan_period);
  134. else
  135. new_mechanism = new SampleMechanismMonitoredGet(config, ctx,
  136. getName().c_str(),
  137. scan_period);
  138. LOG_ASSERT(new_mechanism);
  139. new_mechanism->addStateListener(this);
  140. return new_mechanism.release();
  141. }
  142. #if 0
  143. // TODO: Alternatives to 'disable'.
  144. // Per group, as opposed to: all groups to which this channel belongs.
  145. enum group_mode
  146. {
  147. /** Plain group member. */
  148. gm_plain,
  149. /** Disable the group whenever our value is > 0. */
  150. gm_disabling,
  151. /** Disable the group unless connected and value is > 0. */
  152. gm_enabling,
  153. /** Trigger the group whenever we receive a value > 0. */
  154. gm_trigger
  155. };
  156. // Triggered sample modes:
  157. //
  158. // SampleModeTriggered : Simply save the most recent value?
  159. // SampleModeTriggeredCorrelator : Save the next correlated value?
  160. // Replace currently_disabling with 'disable_value',
  161. // since intepretation is now per group.
  162. #endif
  163. void ArchiveChannel::addToGroup(Guard &group_guard, GroupInfo *group,
  164. Guard &channel_guard, bool disabling)
  165. {
  166. channel_guard.check(__FILE__, __LINE__, mutex);
  167. LOG_ASSERT(!isRunning(channel_guard));
  168. stdList<GroupInfo *>::iterator i;
  169. // Add to the group list
  170. bool add = true;
  171. for (i=groups.begin(); i!=groups.end(); ++i)
  172. {
  173. if (*i == group)
  174. {
  175. add = false;
  176. break;
  177. }
  178. }
  179. if (add)
  180. {
  181. groups.push_back(group);
  182. // Is the group disabled?
  183. if (! group->isEnabled(group_guard))
  184. {
  185. LOG_MSG("Channel '%s': added to disabled group '%s'\n",
  186. getName().c_str(), group->getName().c_str());
  187. disable(channel_guard, epicsTime::getCurrent());
  188. }
  189. }
  190. // Add to the 'disable' groups?
  191. if (disabling)
  192. {
  193. // Is this the first time we become 'disabling',
  194. // i.e. not diabling, yet?
  195. // --> monitor values!
  196. if (! canDisable(channel_guard))
  197. sample_mechanism->addValueListener(this);
  198. // Add, but only once.
  199. add = true;
  200. for (i=disable_groups.begin(); i!=disable_groups.end(); ++i)
  201. {
  202. if (*i == group)
  203. {
  204. add = false;
  205. break;
  206. }
  207. }
  208. if (add)
  209. {
  210. disable_groups.push_back(group);
  211. // Is the channel already 'disabling'?
  212. // Then disable that new group right away.
  213. if (currently_disabling)
  214. {
  215. epicsTime when = epicsTime::getCurrent();
  216. LOG_MSG("Channel '%s' disables '%s' right away\n",
  217. getName().c_str(), group->getName().c_str());
  218. group->disable(group_guard, this, when);
  219. }
  220. }
  221. }
  222. }
  223. void ArchiveChannel::start(Guard &guard)
  224. {
  225. guard.check(__FILE__, __LINE__, mutex);
  226. if (isRunning(guard))
  227. return;
  228. Guard sample_guard(__FILE__, __LINE__, *sample_mechanism);
  229. sample_mechanism->start(sample_guard);
  230. }
  231. bool ArchiveChannel::isRunning(Guard &guard)
  232. {
  233. guard.check(__FILE__, __LINE__, mutex);
  234. LOG_ASSERT(sample_mechanism);
  235. Guard sample_guard(__FILE__, __LINE__, *sample_mechanism);
  236. return sample_mechanism->isRunning(sample_guard);
  237. }
  238. bool ArchiveChannel::isConnected(Guard &guard) const
  239. {
  240. guard.check(__FILE__, __LINE__, mutex);
  241. LOG_ASSERT(sample_mechanism);
  242. return sample_mechanism->getPVState() == ProcessVariable::CONNECTED;
  243. }
  244. // Called by group to disable channel.
  245. // Doesn't mean that this channel itself can disable,
  246. // that's handled in pvValue() callback!
  247. void ArchiveChannel::disable(Guard &guard, const epicsTime &when)
  248. {
  249. guard.check(__FILE__, __LINE__, mutex);
  250. ++disable_count;
  251. if (disable_count > groups.size())
  252. {
  253. LOG_MSG("ERROR: Channel '%s' disabled %zu times?\n",
  254. getName().c_str(), (size_t)disable_count);
  255. return;
  256. }
  257. // That handled the bookkeeping of being disabled or not.
  258. // In case this channel is itself capable of disabling,
  259. // it needs to stay enabled in order to stay informed
  260. // wether it should disable its groups or not.
  261. // It's also probably a good idea to see in the archive
  262. // what values the disabling channel had, so skip
  263. // the actual 'disable' for disabling channels.
  264. if (canDisable(guard))
  265. return;
  266. if (isDisabled(guard))
  267. {
  268. # ifdef DEBUG_ARCHIVE_CHANNEL
  269. LOG_MSG("Channel '%s' disabled\n", getName().c_str());
  270. # endif
  271. sample_mechanism->disable(when);
  272. if (config.getDisconnectOnDisable())
  273. stop(guard);
  274. }
  275. }
  276. // Called by group to re-enable channel.
  277. void ArchiveChannel::enable(Guard &guard, const epicsTime &when)
  278. {
  279. guard.check(__FILE__, __LINE__, mutex);
  280. if (disable_count <= 0)
  281. {
  282. LOG_MSG("ERROR: Channel '%s' enabled while not disabled?\n",
  283. getName().c_str());
  284. return;
  285. }
  286. --disable_count;
  287. if (canDisable(guard))
  288. return;
  289. if (!isDisabled(guard))
  290. {
  291. # ifdef DEBUG_ARCHIVE_CHANNEL
  292. LOG_MSG("Channel '%s' enabled\n", getName().c_str());
  293. # endif
  294. sample_mechanism->enable(when);
  295. if (config.getDisconnectOnDisable())
  296. start(guard);
  297. }
  298. }
  299. stdString ArchiveChannel::getSampleInfo(Guard &guard)
  300. {
  301. Guard sample_guard(__FILE__, __LINE__, *sample_mechanism);
  302. return sample_mechanism->getInfo(sample_guard);
  303. }
  304. void ArchiveChannel::stop(Guard &guard)
  305. {
  306. guard.check(__FILE__, __LINE__, mutex);
  307. if (!isRunning(guard))
  308. return;
  309. // Unlock so that CA client lib. can do whatever it needs to do
  310. // while it's stopping the channel.
  311. waitWhileSampleMechanismBusy(guard);
  312. sample_mechanism_busy = true;
  313. {
  314. GuardRelease release(__FILE__, __LINE__, guard);
  315. Guard sample_guard(__FILE__, __LINE__, *sample_mechanism);
  316. sample_mechanism->stop(sample_guard);
  317. }
  318. sample_mechanism_busy = false;
  319. }
  320. unsigned long ArchiveChannel::write(Guard &guard, Index &index)
  321. {
  322. // In order to allow CA callbacks whenever possible,
  323. // the ArchiveChannel unlocks while writing,
  324. // as will the SampleMechanism::write temporarily.
  325. // On the other hand, we must prevent changes to the sample_mechanism
  326. // while writing. The is_writing flag indicates this.
  327. guard.check(__FILE__, __LINE__, mutex);
  328. if (sample_mechanism_busy)
  329. {
  330. LOG_MSG("'%s' : cannot write because sample mechanism is busy\n",
  331. getName().c_str());
  332. return 0;
  333. }
  334. sample_mechanism_busy = true;
  335. unsigned long samples_written = 0;
  336. {
  337. GuardRelease release(__FILE__, __LINE__, guard);
  338. Guard sample_guard(__FILE__, __LINE__, *sample_mechanism);
  339. try
  340. {
  341. samples_written = sample_mechanism->write(sample_guard, index);
  342. }
  343. catch (GenericException &e)
  344. {
  345. LOG_MSG("ArchiveChannel '%s' write: %s\n",
  346. getName().c_str(), e.what());
  347. }
  348. catch (std::exception &e)
  349. {
  350. LOG_MSG("ArchiveChannel '%s' write: exception %s\n",
  351. getName().c_str(), e.what());
  352. }
  353. catch (...)
  354. {
  355. LOG_MSG("ArchiveChannel '%s' write: unknown exception\n",
  356. getName().c_str());
  357. }
  358. }
  359. sample_mechanism_busy = false;
  360. return samples_written;
  361. }
  362. // ArchiveChannel is StateListener to SampleMechanism (==PV)
  363. void ArchiveChannel::pvConnected(ProcessVariable &pv, const epicsTime &when)
  364. {
  365. #ifdef DEBUG_ARCHIVE_CHANNEL
  366. LOG_MSG("ArchiveChannel '%s' is connected\n", getName().c_str());
  367. #endif
  368. Guard guard(__FILE__, __LINE__, *this); // Lock order: only Channel
  369. // Notify groups
  370. stdList<GroupInfo *>::iterator gi;
  371. for (gi = groups.begin(); gi != groups.end(); ++gi)
  372. {
  373. GroupInfo *g = *gi;
  374. GuardRelease release(__FILE__, __LINE__, guard);
  375. {
  376. Guard group_guard(__FILE__, __LINE__, *g); // Lock Order: only Group
  377. g->incConnected(group_guard, *this);
  378. }
  379. }
  380. }
  381. // ArchiveChannel is StateListener to SampleMechanism (==PV)
  382. void ArchiveChannel::pvDisconnected(ProcessVariable &pv, const epicsTime &when)
  383. {
  384. #ifdef DEBUG_ARCHIVE_CHANNEL
  385. LOG_MSG("ArchiveChannel '%s' is disconnected\n", getName().c_str());
  386. #endif
  387. Guard guard(__FILE__, __LINE__, *this); // Lock order: Only Channel.
  388. // Notify groups
  389. stdList<GroupInfo *>::iterator gi;
  390. for (gi = groups.begin(); gi != groups.end(); ++gi)
  391. {
  392. GroupInfo *g = *gi;
  393. GuardRelease release(__FILE__, __LINE__, guard);
  394. {
  395. Guard group_guard(__FILE__, __LINE__, *g); // Lock order: Only Group.
  396. g->decConnected(group_guard, *this);
  397. }
  398. }
  399. }
  400. // ArchiveChannel is ValueListener to SampleMechanism (==PV) _IF_ disabling
  401. void ArchiveChannel::pvValue(ProcessVariable &pv, const RawValue::Data *data)
  402. {
  403. bool should_disable;
  404. {
  405. Guard pv_guard(__FILE__, __LINE__, pv);
  406. should_disable = RawValue::isAboveZero(pv.getDbrType(pv_guard), data);
  407. }
  408. Guard guard(__FILE__, __LINE__, *this); // Lock order: Only Channel
  409. if (!canDisable(guard))
  410. {
  411. LOG_MSG("ArchiveChannel '%s' got value for disable test "
  412. "but not configured to disable\n",
  413. getName().c_str());
  414. return;
  415. }
  416. if (should_disable)
  417. {
  418. //LOG_MSG("ArchiveChannel '%s' got disabling value\n",
  419. // getName().c_str());
  420. if (currently_disabling) // Was and still is disabling
  421. return;
  422. // Wasn't, but is now disabling.
  423. # ifdef DEBUG_ARCHIVE_CHANNEL
  424. LOG_MSG("ArchiveChannel '%s' disables its groups\n",
  425. getName().c_str());
  426. # endif
  427. currently_disabling = true;
  428. // Notify groups
  429. stdList<GroupInfo *>::iterator gi;
  430. epicsTime when = RawValue::getTime(data);
  431. for (gi = disable_groups.begin(); gi != disable_groups.end(); ++gi)
  432. {
  433. GroupInfo *g = *gi;
  434. GuardRelease release(__FILE__, __LINE__, guard);
  435. {
  436. Guard group_guard(__FILE__, __LINE__, *g); // Lock order: Only Group.
  437. g->disable(group_guard, this, when);
  438. }
  439. }
  440. }
  441. else
  442. {
  443. //LOG_MSG("ArchiveChannel '%s' got enabling value\n",
  444. // getName().c_str());
  445. if (! currently_disabling) // Wasn't and isn't disabling.
  446. return;
  447. // Re-enable groups.
  448. # ifdef DEBUG_ARCHIVE_CHANNEL
  449. LOG_MSG("ArchiveChannel '%s' enables its groups\n",
  450. getName().c_str());
  451. # endif
  452. currently_disabling = false;
  453. // Notify groups
  454. stdList<GroupInfo *>::iterator gi;
  455. epicsTime when = RawValue::getTime(data);
  456. for (gi = disable_groups.begin(); gi != disable_groups.end(); ++gi)
  457. {
  458. GroupInfo *g = *gi;
  459. GuardRelease release(__FILE__, __LINE__, guard);
  460. {
  461. Guard group_guard(__FILE__, __LINE__, *g); // Lock order: Only Group.
  462. g->enable(group_guard, this, when);
  463. }
  464. }
  465. }
  466. }
  467. void ArchiveChannel::addToFUX(Guard &guard, class FUX::Element *doc)
  468. {
  469. FUX::Element *channel = new FUX::Element(doc, "channel");
  470. new FUX::Element(channel, "name", getName());
  471. sample_mechanism->addToFUX(guard, channel);
  472. if (canDisable(guard))
  473. new FUX::Element(channel, "disable");
  474. }