PageRenderTime 40ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/ATF2/control-software/epics-3.14.10/support/sncseq-2-0-12/src/pv/pvKtl.cc

http://atf2flightsim.googlecode.com/
C++ | 1418 lines | 733 code | 163 blank | 522 comment | 153 complexity | d378e1e2de47e86ede9fd7ec289ef9dd MD5 | raw file
Possible License(s): BSD-2-Clause, LGPL-2.0, IPL-1.0, BSD-3-Clause
  1. /* pvKtl.cc,v 1.2 2001/02/16 18:45:39 mrk Exp
  2. *
  3. * Implementation of EPICS sequencer KTL library (pvKtl)
  4. *
  5. * William Lupton, W. M. Keck Observatory
  6. */
  7. // ### check all object creation and call cantProceed on failure
  8. // ### review locking policy (need to be clear on why, how and when)
  9. #include <stdio.h>
  10. #include <string.h>
  11. #include <sys/time.h>
  12. #include <sys/types.h>
  13. #include "gpHash.h"
  14. #include "pvKtl.h"
  15. #include "pvKtlCnv.h"
  16. /* handlers */
  17. static void connectionHandler( ktlKeyword *variable, char *name,
  18. int connected );
  19. static void accessHandler( char *name, pvCallback *callback,
  20. KTL_ANYPOLY *ktlValue, KTL_CONTEXT *context );
  21. static void monitorHandler( char *name, ktlKeyword *keyword,
  22. KTL_ANYPOLY *ktlValue, KTL_CONTEXT *context );
  23. static void commonHandler( char *name, pvCallback *callback,
  24. KTL_ANYPOLY *ktlValue, KTL_CONTEXT *context );
  25. /* utilities */
  26. static char *serviceName( const char *name );
  27. /* service name from "srv.key" name */
  28. static char *keywordName( const char *name );
  29. /* keyword name from "srv.key" name */
  30. static pvSevr sevrFromKTL( int status );
  31. /* severity as pvSevr */
  32. static pvStat statFromKTL( int status );
  33. /* status as pvStat */
  34. static pvType typeFromKTL( int type );
  35. /* KTL type as pvType */
  36. static int copyFromKTL( int ktlFlags, KTL_DATATYPE ktlType, int ktlCount,
  37. const KTL_ANYPOLY *ktlValue, pvType type, int count,
  38. pvValue *value );
  39. /* copy KTL value to pvValue */
  40. static int copyToKTL( pvType type, int count, const pvValue *value,
  41. KTL_DATATYPE ktlType, int ktlCount, KTL_POLYMORPH *ktlValue );
  42. /* copy pvValue to KTL value */
  43. static int mallocKTL( KTL_DATATYPE ktlType, int ktlCount,
  44. KTL_POLYMORPH *ktlValue );
  45. /* alloc dynamic data ref'd by value */
  46. static void freeKTL( KTL_DATATYPE ktlType, KTL_POLYMORPH *ktlValue );
  47. /* free dynamic data ref'd by value */
  48. /* invoke KTL function and send error details to system or variable object */
  49. #define INVOKE(_function) \
  50. do { \
  51. int _status = _function; \
  52. if ( _status >= 0 ) { \
  53. setStatus( _status ); \
  54. setStat( pvStatOK ); \
  55. } \
  56. else \
  57. setError( _status, sevrFromKTL( _status ), \
  58. statFromKTL( _status ), ktl_get_errtxt() ); \
  59. } while ( FALSE )
  60. /* lock / unlock shorthands */
  61. #define LOCK getSystem()->lock()
  62. #define UNLOCK getSystem()->unlock()
  63. ////////////////////////////////////////////////////////////////////////////////
  64. /*+
  65. * Routine: ktlSystem::ktlSystem
  66. *
  67. * Purpose: ktlSystem constructor
  68. *
  69. * Description:
  70. */
  71. ktlSystem::ktlSystem( int debug ) :
  72. pvSystem( debug ),
  73. attach_( FALSE )
  74. {
  75. if ( getDebug() > 0 )
  76. printf( "%8p: ktlSystem::ktlSystem( %d )\n", this, debug );
  77. }
  78. /*+
  79. * Routine: ktlSystem::~ktlSystem
  80. *
  81. * Purpose: ktlSystem destructor
  82. *
  83. * Description:
  84. */
  85. ktlSystem::~ktlSystem()
  86. {
  87. if ( getDebug() > 0 )
  88. printf( "%8p: ktlSystem::~ktlSystem()\n", this );
  89. }
  90. /*+
  91. * Routine: ktlSystem::attach
  92. *
  93. * Purpose: ktlSystem attach routine
  94. *
  95. * Description:
  96. */
  97. pvStat ktlSystem::attach()
  98. {
  99. if ( getDebug() > 0 )
  100. printf( "%8p: ktlSystem::attach()\n", this );
  101. // attach all services that are already open
  102. for ( ktlService *service = ktlService::first(); service != NULL;
  103. service = service->next() )
  104. ( void ) ktl_ioctl( service->getHandle(), KTL_ATTACH );
  105. // attach all future services as they are opened
  106. attach_ = TRUE;
  107. return pvStatOK;
  108. }
  109. /*+
  110. * Routine: ktlSystem::flush
  111. *
  112. * Purpose: ktlSystem flush routine
  113. *
  114. * Description:
  115. */
  116. pvStat ktlSystem::flush()
  117. {
  118. if ( getDebug() > 1 )
  119. printf( "%8p: ktlSystem::flush()\n", this );
  120. // KTL has no way of deferring buffer flush
  121. return pvStatOK;
  122. }
  123. /*+
  124. * Routine: ktlSystem::pend
  125. *
  126. * Purpose: ktlSystem pend routine
  127. *
  128. * Description:
  129. */
  130. pvStat ktlSystem::pend( double seconds, int wait )
  131. {
  132. double secsAtStart; // OSI time at start
  133. double secsNow; // OSI time now
  134. double secsWaited; // number of seconds waited
  135. if ( getDebug() > 1 )
  136. printf( "%8p: ktlSystem::pend( %g, %d )\n", this, seconds, wait );
  137. // if wait==FALSE, should wait for all operations initiated via KTL_NOWAIT
  138. // (assuming that the keyword library really supports KTL_NOWAIT as
  139. // documented in KSD/28, which is very unlikely)
  140. // ### for now, ignore the above issue
  141. // utility macro for setting a timeval structure (not very portable)
  142. #define DOUBLE_TO_TIMEVAL(_t) { ( time_t ) (_t), \
  143. ( long ) ( 1e6 * ( (_t) - ( int ) (_t) ) ) }
  144. // loop waiting for activity and handling events until the requested
  145. // timeout has elapsed (always go through the loop at least once in case
  146. // timeout is zero, which is a poll; negative timeout means for ever)
  147. for ( pvTimeGetCurrentDouble( &secsAtStart ), secsWaited = 0.0;
  148. secsWaited == 0.0 || seconds < 0.0 || secsWaited < seconds;
  149. pvTimeGetCurrentDouble( &secsNow ),
  150. secsWaited = secsNow - secsAtStart ) {
  151. // collect fds from all services
  152. fd_set *readfds = ktlService::getFdsetsAll();
  153. // select (read fds only)
  154. int nfd;
  155. if ( seconds < 0.0 ) {
  156. nfd = select( FD_SETSIZE, readfds, NULL, NULL, NULL );
  157. } else {
  158. // ### hard-code 0.1s pending better solution
  159. //struct timeval timeout = DOUBLE_TO_TIMEVAL( seconds - secsWaited);
  160. struct timeval timeout = DOUBLE_TO_TIMEVAL( 0.1 );
  161. nfd = select( FD_SETSIZE, readfds, NULL, NULL, &timeout );
  162. }
  163. // ignore EINTR; fail on other errors
  164. if ( nfd < 0 ) {
  165. if ( errno == EINTR ) {
  166. continue;
  167. } else {
  168. setError( -errno, pvSevrMAJOR, pvStatERROR, "select failure" );
  169. return pvStatERROR;
  170. }
  171. }
  172. // continue or break on timeout (depending on "wait"; not an error)
  173. if ( nfd == 0 ) {
  174. if ( wait )
  175. continue;
  176. else
  177. break;
  178. }
  179. // otherwise, dispatch all open services
  180. LOCK;
  181. pvStat stat = ktlService::dispatchAll();
  182. UNLOCK;
  183. if ( stat != pvStatOK ) {
  184. return pvStatERROR;
  185. }
  186. }
  187. return pvStatOK;
  188. }
  189. /*+
  190. * Routine: ktlSystem::newVariable
  191. *
  192. * Purpose: ktlSystem variable creation routine
  193. *
  194. * Description:
  195. */
  196. pvVariable *ktlSystem::newVariable( const char *name, pvConnFunc func, void *priv,
  197. int debug )
  198. {
  199. if ( getDebug() > 0 )
  200. printf( "%8p: ktlSystem::newVariable( %s, %p, %p, %d )\n",
  201. this, name, func, priv, debug );
  202. return new ktlVariable( this, name, func, priv, debug );
  203. }
  204. ////////////////////////////////////////////////////////////////////////////////
  205. /*
  206. * list of ktlService objects (statically initialized)
  207. */
  208. ELLLIST ktlService::list_ = {{NULL,NULL}, 0};
  209. /*+
  210. * Routine: ktlService::ktlService
  211. *
  212. * Purpose: ktlService constructor
  213. *
  214. * Description:
  215. */
  216. ktlService::ktlService( ktlSystem *system, const char *name, int debug ) :
  217. debug_( debug ),
  218. name_( strdup( name ) ),
  219. flags_( 0 ),
  220. keys_( NULL ),
  221. status_( 0 ),
  222. sevr_( pvSevrNONE ),
  223. stat_( pvStatOK ),
  224. mess_( NULL )
  225. {
  226. if ( getDebug() > 0 )
  227. printf( "%8p: ktlService::ktlService( %s, %d )\n",
  228. this, name, debug );
  229. handle_ = NULL;
  230. INVOKE( ktl_open( name_, "keyword", 0, &handle_ ) );
  231. if ( system->getAttach() )
  232. ( void ) ktl_ioctl( handle_, KTL_ATTACH );
  233. // use KTL_SUPERSUPP to see whether KTL_SUPER is supported
  234. ( void ) ktl_ioctl( handle_, KTL_SUPERSUPP, &flags_ );
  235. flags_ = flags_ ? ( KTL_SUPER | KTL_STAMP ) : 0;
  236. // create keyword hash table (aborts on failure)
  237. gphInitPvt( &keys_, 256 );
  238. // ### not calling FDREG but perhaps should try to?
  239. // link into list of services
  240. node_.setData( this );
  241. ellAdd( &list_, ( ELLNODE * ) &node_ );
  242. }
  243. /*+
  244. * Routine: ktlService::~ktlService
  245. *
  246. * Purpose: ktlService destructor
  247. *
  248. * Description:
  249. */
  250. ktlService::~ktlService()
  251. {
  252. if ( getDebug() > 0 )
  253. printf( "%8p: ktlService::~ktlService()\n", this );
  254. // remove from list of services
  255. ellDelete( &list_, ( ELLNODE * ) &node_ );
  256. // free keyword hash table memory
  257. gphFreeMem( keys_ );
  258. // close KTL connection (ignore failure)
  259. INVOKE( ktl_close( handle_ ) );
  260. // free name
  261. if ( name_ != NULL )
  262. free( name_ );
  263. }
  264. /*+
  265. * Routine: ktlService::getService
  266. *
  267. * Purpose: ktlService static accessor routine
  268. *
  269. * Description:
  270. */
  271. ktlService *ktlService::getService( ktlSystem *system, char *name, int debug )
  272. {
  273. // search for service with this name; return if found; create if not
  274. ktlService *service;
  275. for ( service = ktlService::first(); service != NULL;
  276. service = service->next() ) {
  277. if ( strcmp( service->name_, name ) == 0 )
  278. break;
  279. }
  280. return ( service != NULL ) ? service : new ktlService( system, name, debug);
  281. }
  282. /*+
  283. * Routine: ktlService::first
  284. *
  285. * Purpose: ktlService first service routine
  286. *
  287. * Description:
  288. */
  289. ktlService *ktlService::first()
  290. {
  291. ktlNode *node = ( ktlNode * ) ellFirst( &list_ );
  292. return ( node != NULL ) ? ( ktlService * ) node->getData() : NULL;
  293. }
  294. /*+
  295. * Routine: ktlService::next
  296. *
  297. * Purpose: ktlService next service routine
  298. *
  299. * Description:
  300. */
  301. ktlService *ktlService::next() const
  302. {
  303. ktlNode *node = ( ktlNode * ) ellNext( ( ELLNODE * ) &node_ );
  304. return ( node != NULL ) ? ( ktlService * ) node->getData() : NULL;
  305. }
  306. /*+
  307. * Routine: ktlService::add
  308. *
  309. * Purpose: ktlService add keyword to hash table routine
  310. *
  311. * Description:
  312. */
  313. void ktlService::add( const ktlKeyword *keyword )
  314. {
  315. // ignore failure (means keyword had already been added)
  316. GPHENTRY *ent = gphAdd( keys_, keyword->getKeyName(), NULL );
  317. if ( ent != NULL ) ent->userPvt = ( void * ) keyword;
  318. }
  319. /*+
  320. * Routine: ktlService::remove
  321. *
  322. * Purpose: ktlService remove keyword from hash table routine
  323. *
  324. * Description:
  325. */
  326. void ktlService::remove( const ktlKeyword *keyword )
  327. {
  328. gphDelete( keys_, keyword->getKeyName(), NULL );
  329. }
  330. /*+
  331. * Routine: ktlService::find
  332. *
  333. * Purpose: ktlService find keyword in hash table routine
  334. *
  335. * Description:
  336. */
  337. ktlKeyword *ktlService::find( const char *keyName )
  338. {
  339. GPHENTRY *ent = gphFind( keys_, keyName, NULL );
  340. return ( ent != NULL ) ? ( ktlKeyword * ) ent->userPvt : NULL;
  341. }
  342. /*+
  343. * Routine: ktlService::getFdsetsAll
  344. *
  345. * Purpose: ktlService routine to return fd_set combining fds from all
  346. * open services
  347. *
  348. * Description:
  349. */
  350. fd_set *ktlService::getFdsetsAll()
  351. {
  352. static fd_set fdset;
  353. FD_ZERO( &fdset );
  354. for ( ktlService *service = ktlService::first(); service != NULL;
  355. service = service->next() ) {
  356. if ( service->handle_ == NULL )
  357. continue;
  358. fd_set temp;
  359. ( void ) ktl_ioctl( service->handle_, KTL_FDSET, &temp );
  360. for ( int fd = 0; fd < FD_SETSIZE; fd++ ) {
  361. if ( FD_ISSET( fd, &temp ) ) {
  362. FD_SET( fd, &fdset );
  363. }
  364. }
  365. }
  366. return &fdset;
  367. }
  368. /*+
  369. * Routine: ktlService::dispatchAll
  370. *
  371. * Purpose: ktlService routine to dispatch events on all open services
  372. *
  373. * Description:
  374. */
  375. pvStat ktlService::dispatchAll()
  376. {
  377. for ( ktlService *service = ktlService::first(); service != NULL;
  378. service = service->next() ) {
  379. if ( service->handle_ == NULL )
  380. continue;
  381. if ( ktl_dispatch( service->handle_ ) < 0 ) {
  382. service->setError( -1, pvSevrMAJOR, pvStatERROR,
  383. "dispatch failure" );
  384. return pvStatERROR;
  385. }
  386. }
  387. return pvStatOK;
  388. }
  389. /*+
  390. * Routine: ktlService::setError()
  391. *
  392. * Purpose: Copy error information
  393. *
  394. * Description:
  395. *
  396. * Function value:
  397. */
  398. void ktlService::setError( int status, pvSevr sevr, pvStat stat,
  399. const char *mess )
  400. {
  401. status_ = status;
  402. sevr_ = sevr;
  403. stat_ = stat;
  404. mess_ = Strdcpy( mess_, mess );
  405. }
  406. ////////////////////////////////////////////////////////////////////////////////
  407. /*+
  408. * Routine: ktlKeyword::ktlKeyword
  409. *
  410. * Purpose: ktlKeyword constructor
  411. *
  412. * Description:
  413. */
  414. ktlKeyword::ktlKeyword( ktlService *service, const char *keyName, int debug ) :
  415. debug_( debug ),
  416. service_( service ),
  417. keyName_( strdup( keyName ) ),
  418. monitored_( FALSE ),
  419. status_( 0 ),
  420. sevr_( pvSevrNONE ),
  421. stat_( pvStatOK ),
  422. mess_( NULL )
  423. {
  424. if ( getDebug() > 0 )
  425. printf( "%8p: ktlKeyword::ktlKeyword( %d )\n", this, debug );
  426. // add to service's hash table of keywords
  427. service->add( this );
  428. // initialize list of associated variables
  429. ellInit( &list_ );
  430. }
  431. /*+
  432. * Routine: ktlKeyword::~ktlKeyword
  433. *
  434. * Purpose: ktlKeyword destructor
  435. *
  436. * Description:
  437. */
  438. ktlKeyword::~ktlKeyword()
  439. {
  440. if ( getDebug() > 0 )
  441. printf( "%8p: ktlKeyword::~ktlKeyword()\n", this );
  442. // remove any remaining associated variables from list and free list
  443. for ( ktlVariable *variable = first(); variable != NULL;
  444. variable = next( variable ) ) {
  445. remove( variable );
  446. }
  447. ellFree( &list_ );
  448. // remove from service's hash table of keywords
  449. service_->remove( this );
  450. // free keyword name
  451. if ( keyName_ != NULL )
  452. free( ( char * ) keyName_ );
  453. }
  454. /*+
  455. * Routine: ktlKeyword::getKeyword
  456. *
  457. * Purpose: ktlKeyword static accessor routine
  458. *
  459. * Description:
  460. */
  461. ktlKeyword *ktlKeyword::getKeyword( ktlService *service, const char *keyName,
  462. int debug )
  463. {
  464. // search for keyword with this name; return if found; create if not
  465. ktlKeyword *keyword = service->find( keyName );
  466. return ( keyword != NULL ) ? keyword :
  467. new ktlKeyword( service, keyName, debug );
  468. }
  469. /*+
  470. * Routine: ktlKeyword::add
  471. *
  472. * Purpose: ktlKeyword add variable routine
  473. *
  474. * Description:
  475. */
  476. int ktlKeyword::add( ktlVariable *variable )
  477. {
  478. if ( getDebug() > 0 )
  479. printf( "%8p: ktlKeyword::add( %p )\n", this, variable );
  480. ktlNode *node = variable->getNode();
  481. node->setData( variable );
  482. ellAdd( &list_, ( ELLNODE * ) node );
  483. // attempt to enable connection handler
  484. INVOKE( ktl_ioctl( variable->getHandle(), KTL_KEYCONNREG,
  485. variable->getKeyName(), connectionHandler, this ) );
  486. return getStatus();
  487. }
  488. /*+
  489. * Routine: ktlKeyword::remove
  490. *
  491. * Purpose: ktlKeyword remove variable routine
  492. *
  493. * Description:
  494. */
  495. void ktlKeyword::remove( ktlVariable *variable )
  496. {
  497. if ( getDebug() > 0 )
  498. printf( "%8p: ktlKeyword::remove( %p )\n", this, variable );
  499. ( void ) monitorOff( variable );
  500. ellDelete( &list_, ( ELLNODE * ) variable->getNode() );
  501. }
  502. /*+
  503. * Routine: ktlKeyword::monitorOn
  504. *
  505. * Purpose: ktlKeyword monitor on variable routine
  506. *
  507. * Description:
  508. */
  509. int ktlKeyword::monitorOn( ktlVariable *variable )
  510. {
  511. if ( getDebug() > 0 )
  512. printf( "%8p: ktlKeyword::monitorOn( %p, %d )\n", this, variable,
  513. monitored_ );
  514. if ( ! monitored_++ ) {
  515. KTL_CONTEXT *context;
  516. INVOKE( ktl_context_create( variable->getHandle(), ( int(*)() )
  517. monitorHandler, NULL, NULL, &context ) );
  518. if ( getStat() == pvStatOK ) {
  519. int flags = KTL_ENABLEREADCONT | KTL_NOPRIME | variable->getFlags();
  520. INVOKE( ktl_read( variable->getHandle(), flags, variable->
  521. getKeyName(), ( void * ) this, NULL, context ) );
  522. ( void ) ktl_context_delete( context );
  523. }
  524. }
  525. return getStatus();
  526. }
  527. /*+
  528. * Routine: ktlKeyword::monitorOff
  529. *
  530. * Purpose: ktlKeyword monitor off variable routine
  531. *
  532. * Description:
  533. */
  534. int ktlKeyword::monitorOff( ktlVariable *variable )
  535. {
  536. if ( getDebug() > 0 )
  537. printf( "%8p: ktlKeyword::monitorOff( %p, %d )\n", this, variable,
  538. monitored_ );
  539. if ( ! --monitored_ ) {
  540. int flags = KTL_DISABLEREADCONT | variable->getFlags();
  541. INVOKE( ktl_read( variable->getHandle(), flags, variable->getKeyName(),
  542. NULL, NULL, NULL ) );
  543. }
  544. return getStatus();
  545. }
  546. /*+
  547. * Routine: ktlKeyword::first
  548. *
  549. * Purpose: ktlKeyword first variable routine
  550. *
  551. * Description:
  552. */
  553. ktlVariable *ktlKeyword::first()
  554. {
  555. ktlNode *node = ( ktlNode * ) ellFirst( &list_ );
  556. return ( node != NULL ) ? ( ktlVariable * ) node->getData() : NULL;
  557. }
  558. /*+
  559. * Routine: ktlKeyword::next
  560. *
  561. * Purpose: ktlKeyword next variable routine
  562. *
  563. * Description:
  564. */
  565. ktlVariable *ktlKeyword::next( ktlVariable *variable )
  566. {
  567. ktlNode *node = ( ktlNode * ) ellNext( ( ELLNODE * ) variable->getNode() );
  568. return ( node != NULL ) ? ( ktlVariable * ) node->getData() : NULL;
  569. }
  570. /*+
  571. * Routine: ktlKeyword::setError()
  572. *
  573. * Purpose: Copy error information
  574. *
  575. * Description:
  576. *
  577. * Function value:
  578. */
  579. void ktlKeyword::setError( int status, pvSevr sevr, pvStat stat,
  580. const char *mess )
  581. {
  582. status_ = status;
  583. sevr_ = sevr;
  584. stat_ = stat;
  585. mess_ = Strdcpy( mess_, mess );
  586. }
  587. ////////////////////////////////////////////////////////////////////////////////
  588. /*+
  589. * Routine: ktlVariable::ktlVariable
  590. *
  591. * Purpose: ktlVariable constructor
  592. *
  593. * Description:
  594. */
  595. ktlVariable::ktlVariable( ktlSystem *system, const char *name, pvConnFunc func,
  596. void *priv, int debug ) :
  597. pvVariable( system, name, func, priv, debug ),
  598. service_( ktlService::getService( system, serviceName( name ), getDebug())),
  599. keyName_( strdup( keywordName( name ) ) ),
  600. keyword_( ktlKeyword::getKeyword( service_, keyName_, getDebug() ) ),
  601. type_( KTL_DOUBLE ),
  602. count_( 1 ),
  603. readNot_( FALSE ),
  604. writeNot_( FALSE ),
  605. readCont_( FALSE ),
  606. connected_( FALSE ),
  607. monitor_( NULL )
  608. {
  609. if ( getDebug() > 0 )
  610. printf( "%8p: ktlVariable::ktlVariable( %s, %d )\n",
  611. this, name, debug );
  612. // use KTL_FLAGS to check whether keyword exists
  613. // ### ignored at the moment (don't want to abort part of constructor
  614. // because complicates destructor)
  615. int flags;
  616. INVOKE( ktl_ioctl( getHandle(), KTL_FLAGS, keyName_, &flags ) );
  617. // cache keyword attributes
  618. INVOKE( ktl_ioctl( getHandle(), KTL_TYPE | KTL_BINOUT, keyName_,
  619. &type_, &count_ ) );
  620. KTL_IMPL_CAPS impl_caps;
  621. INVOKE( ktl_ioctl( getHandle(), KTL_IMPLCAPS, &impl_caps ) );
  622. // ### will be FALSE (should have per keyword inquiry)
  623. readNot_ = impl_caps.read_notify;
  624. INVOKE( ktl_ioctl( getHandle(), KTL_CANWRITENOTIFY, keyName_,
  625. &writeNot_ ) );
  626. INVOKE( ktl_ioctl( getHandle(), KTL_CANREADCONTINUOUS, keyName_,
  627. &readCont_ ) );
  628. // monitor the keyword (won't result in any user callbacks being called
  629. // because no callback object has yet been allocated; is done to force
  630. // a connection request to be sent to the control system)
  631. INVOKE( keyword_->monitorOn( this ) );
  632. // add variable to keyword object's list; this enables the connection
  633. // handler (interpret error as meaning that KTL_KEYCONNREG is not
  634. // supported, so mark connected and invoke function, if supplied)
  635. INVOKE( keyword_->add( this ) );
  636. if ( getStat() != pvStatOK ) {
  637. connected_ = TRUE;
  638. if ( func != NULL )
  639. ( *func ) ( ( void * ) this, connected_ );
  640. }
  641. }
  642. /*+
  643. * Routine: ktlVariable::~ktlVariable
  644. *
  645. * Purpose: ktlVariable destructor
  646. *
  647. * Description:
  648. */
  649. ktlVariable::~ktlVariable()
  650. {
  651. if ( getDebug() > 0 )
  652. printf( "%8p: ktlVariable::~ktlVariable( %s )\n", this, keyName_ );
  653. // remove variable from keyword object's list
  654. keyword_->remove( this );
  655. // ### if this is last variable for this keyword, destroy keyword
  656. // ### if this is last variable for this service, destroy service
  657. // free keyword name
  658. if ( keyName_ != NULL )
  659. free( ( void * ) keyName_ );
  660. }
  661. /*+
  662. * Routine: ktlVariable::get
  663. *
  664. * Purpose: ktlVariable blocking get routine
  665. *
  666. * Description:
  667. */
  668. pvStat ktlVariable::get( pvType type, int count, pvValue *value )
  669. {
  670. if ( getDebug() > 0 )
  671. printf( "%8p: ktlVariable::get( %d, %d )\n", this, type, count );
  672. KTL_ANYPOLY ktlValue;
  673. LOCK;
  674. INVOKE( ktl_read( getHandle(), KTL_WAIT | getFlags() , keyName_, NULL,
  675. &ktlValue, NULL ) );
  676. UNLOCK;
  677. if ( getStat() == pvStatOK ) {
  678. INVOKE( copyFromKTL( getFlags(), type_, count_, &ktlValue,
  679. type, count, value ) );
  680. freeKTL( type_, KTL_POLYDATA( getFlags(), &ktlValue ) );
  681. }
  682. return getStat();
  683. }
  684. /*+
  685. * Routine: ktlVariable::getNoBlock
  686. *
  687. * Purpose: ktlVariable non-blocking get routine
  688. *
  689. * Description:
  690. */
  691. pvStat ktlVariable::getNoBlock( pvType type, int count, pvValue *value )
  692. {
  693. if ( getDebug() > 0 )
  694. printf( "%8p: ktlVariable::getNoBlock( %d, %d )\n", this,
  695. type, count );
  696. KTL_ANYPOLY ktlValue;
  697. LOCK;
  698. INVOKE( ktl_read( getHandle(), KTL_NOWAIT | getFlags(), keyName_, NULL,
  699. &ktlValue, NULL ) );
  700. UNLOCK;
  701. if ( getStat() == pvStatOK ) {
  702. INVOKE( copyFromKTL( getFlags(), type_, count_, &ktlValue,
  703. type, count, value ) );
  704. freeKTL( type_, KTL_POLYDATA( getFlags(), &ktlValue ) );
  705. }
  706. return getStat();
  707. }
  708. /*+
  709. * Routine: ktlVariable::getCallback
  710. *
  711. * Purpose: ktlVariable get with callback routine
  712. *
  713. * Description:
  714. */
  715. pvStat ktlVariable::getCallback( pvType type, int count,
  716. pvEventFunc func, void *arg )
  717. {
  718. if ( getDebug() > 0 )
  719. printf( "%8p: ktlVariable::getCallback( %d, %d, %p, %p )\n",
  720. this, type, count, func, arg );
  721. if ( !readNot_ ) {
  722. // ### larger than needed (status not checked)
  723. pvValue *value = new pvValue[count];
  724. pvStat stat = get( type, count, value );
  725. ( *func ) ( ( void * ) this, type, count, value, arg, stat );
  726. delete [] value;
  727. } else {
  728. pvCallback *callback = new pvCallback( this, type, count, func, arg,
  729. getDebug() );
  730. KTL_CONTEXT *context;
  731. INVOKE( ktl_context_create( getHandle(), ( int(*)() ) accessHandler,
  732. NULL, NULL, &context ) );
  733. if ( getStat() == pvStatOK ) {
  734. LOCK;
  735. INVOKE( ktl_read( getHandle(), KTL_NOTIFY | getFlags(), keyName_,
  736. ( void * ) callback, NULL, context ) );
  737. context->state = KTL_MESSAGE_STATE;
  738. UNLOCK;
  739. }
  740. }
  741. return getStat();
  742. }
  743. /*+
  744. * Routine: ktlVariable::put
  745. *
  746. * Purpose: ktlVariable blocking put routine
  747. *
  748. * Description:
  749. */
  750. pvStat ktlVariable::put( pvType type, int count, pvValue *value )
  751. {
  752. if ( getDebug() > 0 )
  753. printf( "%8p: ktlVariable::put( %d, %d )\n", this, type, count );
  754. KTL_POLYMORPH ktlValue;
  755. INVOKE( copyToKTL( type, count, value, type_, count_, &ktlValue ) );
  756. if ( getStat() == pvStatOK ) {
  757. LOCK;
  758. INVOKE( ktl_write( getHandle(), KTL_WAIT, keyName_, NULL, &ktlValue,
  759. NULL ) );
  760. UNLOCK;
  761. freeKTL( type_, &ktlValue );
  762. }
  763. return getStat();
  764. }
  765. /*+
  766. * Routine: ktlVariable::putNoBlock
  767. *
  768. * Purpose: ktlVariable non-blocking put routine
  769. *
  770. * Description:
  771. */
  772. pvStat ktlVariable::putNoBlock( pvType type, int count, pvValue *value )
  773. {
  774. if ( getDebug() > 0 )
  775. printf( "%8p: ktlVariable::putNoBlock( %d, %d )\n", this,
  776. type, count );
  777. KTL_POLYMORPH ktlValue;
  778. INVOKE( copyToKTL( type, count, value, type_, count_, &ktlValue ) );
  779. if ( getStat() == pvStatOK ) {
  780. LOCK;
  781. INVOKE( ktl_write( getHandle(), KTL_NOWAIT, keyName_, NULL, &ktlValue,
  782. NULL ) );
  783. UNLOCK;
  784. freeKTL( type_, &ktlValue );
  785. }
  786. return getStat();
  787. }
  788. /*+
  789. * Routine: ktlVariable::putCallback
  790. *
  791. * Purpose: ktlVariable put with callback routine
  792. *
  793. * Description:
  794. */
  795. pvStat ktlVariable::putCallback( pvType type, int count, pvValue *value,
  796. pvEventFunc func, void *arg )
  797. {
  798. if ( getDebug() > 0 )
  799. printf( "%8p: ktlVariable::putCallback( %d, %d )\n",
  800. this, type, count );
  801. if ( !writeNot_ ) {
  802. pvStat stat = put( type, count, value );
  803. ( *func ) ( ( void * ) this, type, count, value, arg, stat );
  804. } else {
  805. pvCallback *callback = new pvCallback( this, type, count, func, arg,
  806. getDebug() );
  807. KTL_POLYMORPH ktlValue;
  808. INVOKE( copyToKTL( type, count, value, type_, count_, &ktlValue ) );
  809. if ( getStat() == pvStatOK ) {
  810. KTL_CONTEXT *context;
  811. INVOKE( ktl_context_create( getHandle(), ( int(*)() ) accessHandler,
  812. NULL, NULL, &context ) );
  813. if ( getStat() == pvStatOK ) {
  814. LOCK;
  815. INVOKE( ktl_write( getHandle(), KTL_NOTIFY, keyName_,
  816. ( void * ) callback, &ktlValue, context ) );
  817. context->state = KTL_MESSAGE_STATE;
  818. UNLOCK;
  819. }
  820. }
  821. freeKTL( type_, &ktlValue );
  822. }
  823. return getStat();
  824. }
  825. /*+
  826. * Routine: ktlVariable::monitorOn
  827. *
  828. * Purpose: ktlVariable monitor enable routine
  829. *
  830. * Description:
  831. */
  832. pvStat ktlVariable::monitorOn( pvType type, int count, pvEventFunc func,
  833. void *arg, pvCallback **pCallback )
  834. {
  835. if ( getDebug() > 0 )
  836. printf( "%8p: ktlVariable::monitorOn( %s, %d, %d )\n",
  837. this, keyName_, type, count );
  838. if ( !readCont_ ) {
  839. setError( -1, pvSevrMAJOR, pvStatERROR, "monitoring not supported" );
  840. if ( pCallback != NULL )
  841. *pCallback = NULL;
  842. // ### can only have single monitor enabled per variable (can have
  843. // multiple variables per keyword though)
  844. } else if ( monitor_ != NULL ) {
  845. setError( -1, pvSevrMAJOR, pvStatERROR, "monitor already enabled" );
  846. if ( pCallback != NULL )
  847. *pCallback = NULL;
  848. } else {
  849. monitor_ = new pvCallback( this, type, count, func, arg, getDebug() );
  850. LOCK;
  851. INVOKE( keyword_->monitorOn( this ) );
  852. UNLOCK;
  853. if ( pCallback != NULL )
  854. *pCallback = monitor_;
  855. }
  856. return getStat();
  857. }
  858. /*+
  859. * Routine: ktlVariable::monitorOff
  860. *
  861. * Purpose: ktlVariable monitor disable routine
  862. *
  863. * Description:
  864. */
  865. pvStat ktlVariable::monitorOff( pvCallback *callback )
  866. {
  867. if ( getDebug() > 0 )
  868. printf( "%8p: ktlVariable::monitorOff()\n", this );
  869. LOCK;
  870. keyword_->monitorOff( this );
  871. UNLOCK;
  872. // ### are we sure that no further monitors can be delivered?
  873. if ( monitor_ != NULL ) {
  874. delete monitor_;
  875. monitor_ = NULL;
  876. }
  877. return getStat();
  878. }
  879. /*+
  880. * Routine: ktlVariable::getType
  881. *
  882. * Purpose: ktlVariable "what type are we?" routine
  883. *
  884. * Description:
  885. */
  886. pvType ktlVariable::getType() const
  887. {
  888. if ( getDebug() > 1 )
  889. printf( "%8p: ktlVariable::getType()\n", this );
  890. return typeFromKTL( type_ );
  891. }
  892. ////////////////////////////////////////////////////////////////////////////////
  893. /*+
  894. * Routine: connectionHandler
  895. *
  896. * Purpose: KTL connection handler
  897. *
  898. * Description:
  899. */
  900. static void connectionHandler( ktlKeyword *keyword, char *, int connected )
  901. {
  902. if ( keyword->getDebug() > 0 )
  903. printf( "%8p: ktlKeyword::connectionHandler( %d )\n", keyword,
  904. connected );
  905. for ( ktlVariable *variable = keyword->first(); variable != NULL;
  906. variable = keyword->next( variable ) ) {
  907. if ( variable->getDebug() > 0 )
  908. printf( "%8p: ktlVariable::connectionHandler( %s, %d )\n", variable,
  909. variable->getKeyName(), connected );
  910. variable->setConnected( connected );
  911. pvConnFunc func = variable->getFunc();
  912. if ( func != NULL )
  913. ( *func ) ( ( void * ) variable, connected );
  914. }
  915. }
  916. /*+
  917. * Routine: accessHandler
  918. *
  919. * Purpose: KTL get/put completion event handler
  920. *
  921. * Description:
  922. */
  923. static void accessHandler( char *keyName, pvCallback *callback,
  924. KTL_ANYPOLY *ktlValue, KTL_CONTEXT *context )
  925. {
  926. ktlVariable *variable = ( ktlVariable * ) callback->getVariable();
  927. int ktlFlags = variable->getFlags();
  928. if ( variable->getDebug() > 0 )
  929. printf( "%8p: ktlVariable::accessHandler( %s, %d )\n", variable,
  930. variable->getKeyName(), KTL_POLYDATA(ktlFlags,ktlValue)->i );
  931. commonHandler( keyName, callback, ktlValue, context );
  932. delete callback;
  933. }
  934. /*+
  935. * Routine: monitorHandler
  936. *
  937. * Purpose: KTL monitor event handler
  938. *
  939. * Description:
  940. */
  941. static void monitorHandler( char *keyName, ktlKeyword *keyword,
  942. KTL_ANYPOLY *ktlValue, KTL_CONTEXT *context )
  943. {
  944. int ktlFlags = keyword->getFlags();
  945. if ( keyword->getDebug() > 0 )
  946. printf( "%8p: ktlKeyword::monitorHandler( %d )\n", keyword,
  947. KTL_POLYDATA(ktlFlags,ktlValue)->i );
  948. for ( ktlVariable *variable = keyword->first(); variable != NULL;
  949. variable = keyword->next( variable ) ) {
  950. if ( variable->getDebug() > 0 )
  951. printf( "%8p: ktlVariable::monitorHandler( %s )\n", variable,
  952. variable->getKeyName() );
  953. pvCallback *monitor = variable->getMonitor();
  954. if ( monitor != NULL )
  955. commonHandler( keyName, monitor, ktlValue, context );
  956. }
  957. }
  958. /*+
  959. * Routine: commonHandler
  960. *
  961. * Purpose: KTL completion/monitor event handler (work routine)
  962. *
  963. * Description:
  964. */
  965. static void commonHandler( char *, pvCallback *callback,
  966. KTL_ANYPOLY *ktlValue, KTL_CONTEXT *context )
  967. {
  968. pvEventFunc func = callback->getFunc();
  969. ktlVariable *variable = ( ktlVariable * ) callback->getVariable();
  970. int ktlFlags = variable->getFlags();
  971. KTL_DATATYPE ktlType = variable->getKtlType();
  972. int ktlCount = variable->getCount();
  973. pvType type = callback->getType();
  974. int count = callback->getCount();
  975. void *arg = callback->getArg();
  976. // ### larger than needed (status not checked)
  977. pvValue *value = new pvValue[count];
  978. // ### function not called if copy from KTL failed
  979. if ( copyFromKTL( ktlFlags, ktlType, ktlCount, ktlValue,
  980. type, count, value ) >= 0 ) {
  981. ( *func ) ( ( void * ) variable, type, count, value, arg,
  982. statFromKTL( context->status ) );
  983. }
  984. delete [] value;
  985. }
  986. ////////////////////////////////////////////////////////////////////////////////
  987. /*+
  988. * Routine: serviceName()
  989. *
  990. * Purpose: service name from "srv.key" name
  991. *
  992. * Description:
  993. */
  994. static char *serviceName( const char *name )
  995. {
  996. static char temp[256];
  997. size_t len = strcspn( name, "." );
  998. strncpy( temp, name, len );
  999. temp[255] = '\0';
  1000. return temp;
  1001. }
  1002. /*+
  1003. * Routine: keywordName
  1004. *
  1005. * Purpose: keyword name from "srv.key" name
  1006. *
  1007. * Description:
  1008. */
  1009. static char *keywordName( const char *name )
  1010. {
  1011. static char temp[256];
  1012. size_t len = strcspn( name, "." );
  1013. strncpy( temp, name + len + 1, 256 - len - 1 );
  1014. temp[255] = '\0';
  1015. return temp;
  1016. }
  1017. /*+
  1018. * Routine: sevrFromKTL
  1019. *
  1020. * Purpose: extract pvSevr from KTL status
  1021. *
  1022. * Description:
  1023. */
  1024. static pvSevr sevrFromKTL( int status )
  1025. {
  1026. return ( status < 0 ) ? pvSevrERROR : pvSevrNONE;
  1027. }
  1028. /*+
  1029. * Routine: statFromKTL
  1030. *
  1031. * Purpose: extract pvStat from KTL status
  1032. *
  1033. * Description:
  1034. */
  1035. static pvStat statFromKTL( int status )
  1036. {
  1037. pvSevr sevr = sevrFromKTL( status );
  1038. return ( sevr == pvSevrNONE || sevr == pvSevrMINOR ) ?
  1039. pvStatOK : pvStatERROR;
  1040. }
  1041. /*+
  1042. * Routine: typeFromKTL
  1043. *
  1044. * Purpose: extract pvType from KTL type
  1045. *
  1046. * Description:
  1047. */
  1048. static pvType typeFromKTL( int type )
  1049. {
  1050. switch ( type ) {
  1051. case KTL_INT: return pvTypeLONG;
  1052. case KTL_BOOLEAN: return pvTypeLONG;
  1053. case KTL_ENUM: return pvTypeLONG;
  1054. case KTL_ENUMM: return pvTypeLONG;
  1055. case KTL_MASK: return pvTypeLONG;
  1056. case KTL_FLOAT: return pvTypeFLOAT;
  1057. case KTL_DOUBLE: return pvTypeDOUBLE;
  1058. case KTL_STRING: return pvTypeSTRING;
  1059. case KTL_INT_ARRAY: return pvTypeLONG;
  1060. case KTL_FLOAT_ARRAY: return pvTypeFLOAT;
  1061. case KTL_DOUBLE_ARRAY: return pvTypeDOUBLE;
  1062. default: return pvTypeERROR;
  1063. }
  1064. }
  1065. /*+
  1066. * Routine: copyFromKTL
  1067. *
  1068. * Purpose: copy KTL value to pvValue
  1069. *
  1070. * Description:
  1071. */
  1072. static int copyFromKTL( int ktlFlags, KTL_DATATYPE ktlType, int ktlCount,
  1073. const KTL_ANYPOLY *ktlValue, pvType type, int count,
  1074. pvValue *value )
  1075. {
  1076. // map types to ktlCnv's types (indices into conversion table)
  1077. ktlCnv::type inpType = ktlCnv::getType( ktlType );
  1078. ktlCnv::type outType = ktlCnv::getType( type );
  1079. // if type is not simple, copy status, severity and time stamp
  1080. // (note assumption that STAT and SEVR can be cast; this is in fact true)
  1081. if ( !PV_SIMPLE( type ) ) {
  1082. epicsTimeStamp stamp;
  1083. if ( ktlFlags & KTL_SUPER ) {
  1084. value->timeCharVal.status = ( pvStat ) ktlValue->super.stat;
  1085. value->timeCharVal.severity = ( pvSevr ) ktlValue->super.sevr;
  1086. if ( ktlFlags & KTL_STAMP ) {
  1087. epicsTimeFromTimeval( &value->timeCharVal.stamp,
  1088. &ktlValue->super.stamp );
  1089. }
  1090. else {
  1091. ( void ) epicsTimeGetCurrent( &stamp );
  1092. value->timeCharVal.stamp = stamp;
  1093. }
  1094. } else {
  1095. ( void ) epicsTimeGetCurrent( &stamp );
  1096. value->timeCharVal.status = pvStatOK;
  1097. value->timeCharVal.severity = pvSevrNONE;
  1098. value->timeCharVal.stamp = stamp;
  1099. }
  1100. }
  1101. // convert data
  1102. const KTL_POLYMORPH *ktlData = KTL_POLYDATA( ktlFlags, ktlValue );
  1103. return ktlCnv::convert( inpType, ktlCount, KTL_VALPTR( ktlType, ktlData ),
  1104. outType, count, PV_VALPTR( type, value ) );
  1105. }
  1106. /*+
  1107. * Routine: copyToKTL
  1108. *
  1109. * Purpose: copy pvValue to KTL value
  1110. *
  1111. * Description:
  1112. */
  1113. static int copyToKTL( pvType type, int count, const pvValue *value,
  1114. KTL_DATATYPE ktlType, int ktlCount, KTL_POLYMORPH *ktlValue )
  1115. {
  1116. // map types to ktlCnv's types (indices into conversion table)
  1117. ktlCnv::type inpType = ktlCnv::getType( type );
  1118. ktlCnv::type outType = ktlCnv::getType( ktlType );
  1119. // allocate memory for KTL array or string
  1120. if ( mallocKTL( ktlType, ktlCount, ktlValue ) < 0 )
  1121. return -1;
  1122. // convert data
  1123. return ktlCnv::convert( inpType, count, PV_VALPTR( type, value ),
  1124. outType, ktlCount, KTL_VALPTR( ktlType, ktlValue ));
  1125. }
  1126. /*+
  1127. * Routine: mallocKTL
  1128. *
  1129. * Purpose: allocate dynamic data referenced from a polymorph
  1130. *
  1131. * Description:
  1132. */
  1133. static int mallocKTL( KTL_DATATYPE ktlType, int ktlCount,
  1134. KTL_POLYMORPH *ktlValue )
  1135. {
  1136. switch ( ktlType ) {
  1137. case KTL_STRING:
  1138. ktlValue->s = new char[sizeof(pvString)];
  1139. if ( ktlValue->s == NULL ) goto error;
  1140. break;
  1141. case KTL_INT_ARRAY:
  1142. ktlValue->ia = new int[ktlCount];
  1143. if ( ktlValue->ia == NULL ) goto error;
  1144. break;
  1145. case KTL_FLOAT_ARRAY:
  1146. ktlValue->fa = new float[ktlCount];
  1147. if ( ktlValue->fa == NULL ) goto error;
  1148. break;
  1149. case KTL_DOUBLE_ARRAY:
  1150. ktlValue->da = new double[ktlCount];
  1151. if ( ktlValue->da == NULL ) goto error;
  1152. break;
  1153. default:
  1154. break;
  1155. }
  1156. return 0;
  1157. error:
  1158. ktl_set_errtxt( "memory allocation failure" );
  1159. return -1;
  1160. }
  1161. /*+
  1162. * Routine: freeKTL
  1163. *
  1164. * Purpose: free data referenced from a polymorph
  1165. *
  1166. * Description:
  1167. */
  1168. static void freeKTL( KTL_DATATYPE ktlType, KTL_POLYMORPH *ktlValue )
  1169. {
  1170. // do nothing if pointer (this is also ia, fa and da) is NULL
  1171. if ( ktlValue->s == NULL )
  1172. return;
  1173. switch ( ktlType ) {
  1174. case KTL_STRING:
  1175. delete [] ktlValue->s;
  1176. break;
  1177. case KTL_INT_ARRAY:
  1178. delete [] ktlValue->ia;
  1179. break;
  1180. case KTL_FLOAT_ARRAY:
  1181. delete [] ktlValue->fa;
  1182. break;
  1183. case KTL_DOUBLE_ARRAY:
  1184. delete [] ktlValue->da;
  1185. break;
  1186. default:
  1187. break;
  1188. }
  1189. }
  1190. /*
  1191. * pvKtl.cc,v
  1192. * Revision 1.2 2001/02/16 18:45:39 mrk
  1193. * changes for latest version of 3.14
  1194. *
  1195. * Revision 1.1.1.1 2000/04/04 03:22:14 wlupton
  1196. * first commit of seq-2-0-0
  1197. *
  1198. * Revision 1.19 2000/03/31 23:01:41 wlupton
  1199. * supported setStatus
  1200. *
  1201. * Revision 1.18 2000/03/29 23:28:05 wlupton
  1202. * corrected comment typo
  1203. *
  1204. * Revision 1.17 2000/03/29 02:00:45 wlupton
  1205. * monitor all keywords (forces connect request to be sent)
  1206. *
  1207. * Revision 1.16 2000/03/18 04:00:25 wlupton
  1208. * converted to use new configure scheme
  1209. *
  1210. * Revision 1.15 2000/03/16 02:11:29 wlupton
  1211. * supported KTL_ANYPOLY (plus misc other mods)
  1212. *
  1213. * Revision 1.14 2000/03/08 01:32:29 wlupton
  1214. * cut out some early error exits in constructors (to avoid crashes in destructors)
  1215. *
  1216. * Revision 1.13 2000/03/07 19:55:32 wlupton
  1217. * nearly sufficient tidying up
  1218. *
  1219. * Revision 1.12 2000/03/07 09:27:39 wlupton
  1220. * drastically reduced use of references
  1221. *
  1222. * Revision 1.11 2000/03/07 08:46:29 wlupton
  1223. * created ktlKeyword class (works but a bit messy)
  1224. *
  1225. * Revision 1.10 2000/03/06 19:21:04 wlupton
  1226. * misc error reporting and type conversion mods
  1227. *
  1228. * Revision 1.9 2000/03/01 02:07:14 wlupton
  1229. * converted to use new OSI library
  1230. *
  1231. * Revision 1.8 2000/02/19 01:12:22 wlupton
  1232. * misc tidy-up and bug fixes
  1233. *
  1234. * Revision 1.7 2000/02/14 21:33:07 wlupton
  1235. * fixed workshop 5.0 compilation error
  1236. *
  1237. * Revision 1.6 1999/10/12 02:53:13 wlupton
  1238. * fixed KTL_NOTIFY support (cannot have been tested)
  1239. *
  1240. * Revision 1.5 1999/09/07 20:43:21 epics
  1241. * increased debug level for pend() call
  1242. *
  1243. * Revision 1.4 1999/07/01 20:50:20 wlupton
  1244. * Working under VxWorks
  1245. *
  1246. * Revision 1.3 1999/06/29 01:56:25 wlupton
  1247. * always use 0.1s select() timeout because of startup problem seeing fds open
  1248. *
  1249. * Revision 1.2 1999/06/15 10:11:03 wlupton
  1250. * demo sequence mostly working with KTL
  1251. *
  1252. * Revision 1.1 1999/06/11 02:20:32 wlupton
  1253. * nearly working with KTL
  1254. *
  1255. */