Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

894 lines
22 KiB

  1. /* Copyright 1999 American Power Conversion, All Rights Reserverd
  2. *
  3. * Description:
  4. * The ApcMiniDriver class provides an interface that is
  5. * compatible with the MiniDriver interface for the Windows2000
  6. * UPS service.
  7. * The ApcMiniDriver makes use of a modified
  8. * PowerChute plus UPS service. This modified service has had
  9. * all of the networking, data logging, and flex manager code
  10. * removed. All that is left is the modeling and monitoring of
  11. * the connected UPS system. It is assumed that a "smart"
  12. * signalling UPS is connected.
  13. * The ApcMiniDriver class is also responsible for filling in
  14. * the advanced registry settings, battery replacement condition,
  15. * serial #, firmware rev, etc...
  16. *
  17. * Revision History:
  18. * mholly 14Apr1999 Created
  19. * mholly 16Apr1999 Convert data from UPS into wide characters
  20. * if UNICODE is defined
  21. * mholly 19Apr1999 remove registry updates of utility line state
  22. * mholly 20Apr1999 no longer updating model/vendor in registry
  23. * mholly 26Apr1999 convert RUN_TIME_REMAINING to minutes before
  24. * updating the registry - also only update the
  25. * runtime in the onRuntimeTimer method
  26. * mholly 12May1999 no longer taking aCommPort parameter in UPSInit
  27. *
  28. */
  29. #include "cdefine.h"
  30. #include <windows.h>
  31. #include <tchar.h>
  32. #include <stdio.h>
  33. #include <stdlib.h>
  34. #include "apcdrvr.h"
  35. #include "apcups.h"
  36. #include "ntsrvap.h"
  37. #include "event.h"
  38. #include "timerman.h"
  39. #include "codes.h"
  40. #include "tokenstr.h"
  41. extern "C"{
  42. #include "upsreg.h"
  43. }
  44. // Separator used for the shutdown delay allowed values
  45. #define SHUTDOWN_DELAY_SEPARATOR ","
  46. #define LOW_BATT_DURATION_SEPARATOR ","
  47. #define SECONDS_TO_MINUTES 60
  48. /**
  49. * ApcMiniDriver
  50. *
  51. * Description:
  52. * Constructor - initializes all data members
  53. *
  54. * Parameters:
  55. * None
  56. *
  57. * Returns:
  58. * N/A
  59. *
  60. */
  61. ApcMiniDriver::ApcMiniDriver()
  62. : theState(UPS_ONLINE),
  63. theStateChangedEvent(NULL),
  64. theReplaceBatteryState(UPS_BATTERYSTATUS_UNKNOWN),
  65. theUpsApp(NULL),
  66. theRunTimeTimer(0),
  67. theOnBatteryRuntime(-1),
  68. theBatteryCapacity(-1)
  69. {
  70. }
  71. /**
  72. * ~ApcMiniDriver
  73. *
  74. * Description:
  75. * Destructor - does nothing, must call
  76. * UPSStop prior to destructor
  77. *
  78. * Parameters:
  79. * None
  80. *
  81. * Returns:
  82. * N/A
  83. *
  84. */
  85. ApcMiniDriver::~ApcMiniDriver()
  86. {
  87. }
  88. /**
  89. * UPSInit
  90. *
  91. * Description:
  92. * Must be the first method called on the object
  93. * Failing to call UPSInit will result in an object
  94. * in an unstable state
  95. *
  96. * Parameters:
  97. * aCommPort: not used
  98. *
  99. * Returns:
  100. * UPS_INITOK: initalized successfully
  101. * UPS_INITUNKNOWNERROR: initialization failed
  102. *
  103. */
  104. DWORD ApcMiniDriver::UPSInit()
  105. {
  106. DWORD init_err = UPS_INITOK;
  107. theStateChangedEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  108. if (!theStateChangedEvent) {
  109. init_err = UPS_INITUNKNOWNERROR;
  110. }
  111. if ((UPS_INITOK == init_err) &&
  112. (ErrNO_ERROR != initalizeUpsApplication())) {
  113. init_err = UPS_INITUNKNOWNERROR;
  114. }
  115. if (UPS_INITOK != init_err) {
  116. UPSStop();
  117. }
  118. return init_err;
  119. }
  120. /**
  121. * UPSStop
  122. *
  123. * Description:
  124. * must be called to cleanup the object, after
  125. * this call completes the only valid method
  126. * call is UPSInit() or the destructor
  127. *
  128. * Parameters:
  129. * None
  130. *
  131. * Returns:
  132. * None
  133. *
  134. */
  135. void ApcMiniDriver::UPSStop()
  136. {
  137. UPSCancelWait();
  138. cleanupUpsApplication();
  139. if (theStateChangedEvent) {
  140. CloseHandle(theStateChangedEvent);
  141. theStateChangedEvent = NULL;
  142. }
  143. }
  144. /**
  145. * UPSWaitForStateChange
  146. *
  147. * Description:
  148. * Blocks until the state of the UPS differs
  149. * from the value passed in via aState or
  150. * anInterval milliseconds has expired. If
  151. * anInterval has a value of INFINITE this
  152. * function will never timeout
  153. *
  154. * Parameters:
  155. * aState: defines the state to wait for a change from,
  156. * possible values:
  157. * UPS_ONLINE
  158. * UPS_ONBATTERY
  159. * UPS_LOWBATTERY
  160. * UPS_NOCOMM
  161. *
  162. * anInterval: timeout in milliseconds, or INFINITE for
  163. * no timeout interval
  164. *
  165. * Returns:
  166. * None
  167. *
  168. */
  169. void ApcMiniDriver::UPSWaitForStateChange(DWORD aLastState, DWORD anInterval)
  170. {
  171. if (aLastState == theState) {
  172. //
  173. // wait for a state change from the UPS
  174. //
  175. if (theStateChangedEvent) {
  176. WaitForSingleObject(theStateChangedEvent, anInterval);
  177. }
  178. }
  179. }
  180. /**
  181. * UPSGetState
  182. *
  183. * Description:
  184. * returns the current state of the UPS
  185. *
  186. * Parameters:
  187. * None
  188. *
  189. * Returns:
  190. * possible values:
  191. * UPS_ONLINE
  192. * UPS_ONBATTERY
  193. * UPS_LOWBATTERY
  194. * UPS_NOCOMM
  195. *
  196. */
  197. DWORD ApcMiniDriver::UPSGetState()
  198. {
  199. return theState;
  200. }
  201. /**
  202. * UPSCancelWait
  203. *
  204. * Description:
  205. * interrupts pending calls to UPSWaitForStateChange
  206. * without regard to timout or state change
  207. *
  208. * Parameters:
  209. * None
  210. *
  211. * Returns:
  212. * None
  213. *
  214. */
  215. void ApcMiniDriver::UPSCancelWait()
  216. {
  217. if (theStateChangedEvent) {
  218. SetEvent(theStateChangedEvent);
  219. }
  220. }
  221. /**
  222. * UPSTurnOff
  223. *
  224. * Description:
  225. * Attempts to turn off the outlets on the UPS
  226. * after the specified delay. This method querries the
  227. * UPS for the allowed shutdown delays and sets the
  228. * delay to one of the following:
  229. * 1. aTurnOffDelay if it exactly matches one of the allowed values
  230. * 2. the next highest value after aTurnOffDelay, if one exists
  231. * 3. the highest allowed value, if aTurnOffDelay is larger than all of
  232. * the allowed values.
  233. * If no allowed values are returned, the Shutdown Delay will not be set.
  234. * Next the UPS is instructed to sleep until power is restored.
  235. *
  236. * Parameters:
  237. * aTurnOffDelay: the minimum amount of time to wait before
  238. * turning off the outlets on the UPS
  239. *
  240. * Returns:
  241. * None
  242. *
  243. */
  244. void ApcMiniDriver::UPSTurnOff(DWORD aTurnOffDelay)
  245. {
  246. if (theUpsApp) {
  247. char allowed_values[512];
  248. // Retrieve the allowed shutdown delays from the UPS
  249. theUpsApp->Get(ALLOWED_SHUTDOWN_DELAYS, allowed_values);
  250. TokenString token_str(allowed_values, SHUTDOWN_DELAY_SEPARATOR);
  251. PCHAR tok = token_str.GetCurrentToken();
  252. // Set the shutdown delay if there are allowed values
  253. if (tok) {
  254. // Initialize counters
  255. long requested_value = (long) aTurnOffDelay;
  256. long max = atol(tok);
  257. long last_closest = 0;
  258. // Cycle through the allowed values looking for a suitable value
  259. while (tok) {
  260. long value = atol(tok);
  261. // Check to see if this value is closest to the requested
  262. if ( ((value >= requested_value) && (value < last_closest))
  263. || (last_closest < requested_value)) {
  264. last_closest = value;
  265. }
  266. // Check to see if this value is the max value
  267. if (value > max) {
  268. max = value;
  269. }
  270. // Get the next value
  271. tok = token_str.GetCurrentToken();
  272. }
  273. long shutdown_delay = last_closest;
  274. if (last_closest < requested_value) {
  275. // The requested value is larger than all of the values, use the max
  276. shutdown_delay = max;
  277. }
  278. // Set the shutdown delay
  279. char shutdown_delay_str[4];
  280. sprintf(shutdown_delay_str, "%3.3u", shutdown_delay);
  281. theUpsApp->Set(SHUTDOWN_DELAY, shutdown_delay_str);
  282. }
  283. // Signal the UPS to sleep
  284. theUpsApp->UPSTurnOff();
  285. }
  286. }
  287. /**
  288. * Update
  289. *
  290. * Description:
  291. * Update is called when theUpsApp has
  292. * generated an Event for which this object
  293. * has registered.
  294. * Events are defined by a set of integer
  295. * 'codes' defined in the file 'codes.h'
  296. *
  297. * Parameters:
  298. * anEvent: a pointer to an Event object that
  299. * defines the generated event
  300. *
  301. * Returns:
  302. * ErrNO_ERROR
  303. *
  304. */
  305. INT ApcMiniDriver::Update(PEvent anEvent)
  306. {
  307. INT err = ErrNO_ERROR;
  308. if (!anEvent) {
  309. return err;
  310. }
  311. switch (anEvent->GetCode()) {
  312. case UTILITY_LINE_CONDITION:
  313. {
  314. err = onUtilityLineCondition(anEvent);
  315. }
  316. break;
  317. case BATTERY_REPLACEMENT_CONDITION:
  318. {
  319. err = onBatteryReplacementCondition(anEvent);
  320. }
  321. break;
  322. case BATTERY_CONDITION:
  323. {
  324. err = onBatteryCondition(anEvent);
  325. }
  326. break;
  327. case COMMUNICATION_STATE:
  328. {
  329. err = onCommunicationState(anEvent);
  330. }
  331. break;
  332. case TIMER_PULSE:
  333. {
  334. err = onTimerPulse(anEvent);
  335. }
  336. break;
  337. default:
  338. {
  339. err = UpdateObj::Update(anEvent);
  340. }
  341. break;
  342. }
  343. return err;
  344. }
  345. /**
  346. * onUtilityLineCondition
  347. *
  348. * Description:
  349. * Determines the current state of the power
  350. * and reports any changes to the registry and
  351. * to any threads pending on UPSWaitForStateChange
  352. *
  353. * Parameters:
  354. * anEvent: pointer to an Event object that
  355. * relates to a UTILITY_LINE_CONDITION event
  356. *
  357. * Returns:
  358. * ErrNO_ERROR
  359. *
  360. */
  361. INT ApcMiniDriver::onUtilityLineCondition(PEvent anEvent)
  362. {
  363. DWORD state = atoi(anEvent->GetValue());
  364. DWORD old_state = theState;
  365. if (LINE_BAD == state) {
  366. theState = UPS_ONBATTERY;
  367. }
  368. else {
  369. theState = UPS_ONLINE;
  370. }
  371. if (old_state != theState) {
  372. UPSCancelWait();
  373. }
  374. return ErrNO_ERROR;
  375. }
  376. /**
  377. * onBatteryReplacementCondition
  378. *
  379. * Description:
  380. * Determines the current replacement state of the
  381. * battery and reports any changes to the registry
  382. *
  383. * Parameters:
  384. * anEvent: pointer to an Event object that
  385. * relates to a BATTERY_REPLACEMENT_CONDITION event
  386. *
  387. * Returns:
  388. * ErrNO_ERROR
  389. *
  390. */
  391. INT ApcMiniDriver::onBatteryReplacementCondition(PEvent anEvent)
  392. {
  393. DWORD state = atoi(anEvent->GetValue());
  394. DWORD old_state = theReplaceBatteryState;
  395. if (BATTERY_NEEDS_REPLACING == state) {
  396. theReplaceBatteryState = UPS_BATTERYSTATUS_REPLACE;
  397. }
  398. else {
  399. theReplaceBatteryState = UPS_BATTERYSTATUS_GOOD;
  400. }
  401. if (old_state != theReplaceBatteryState) {
  402. InitUPSStatusBlock();
  403. SetUPSStatusBatteryStatus(theReplaceBatteryState);
  404. SaveUPSStatusBlock(FALSE);
  405. }
  406. return ErrNO_ERROR;
  407. }
  408. /**
  409. * onBatteryCondition
  410. *
  411. * Description:
  412. * Determines the current charge-state of the battery
  413. * and reports any changes to the registry and
  414. * to any threads pending on UPSWaitForStateChange
  415. *
  416. * Parameters:
  417. * anEvent: pointer to an Event object that
  418. * relates to a BATTERY_CONDITION event
  419. *
  420. * Returns:
  421. * ErrNO_ERROR
  422. *
  423. */
  424. INT ApcMiniDriver::onBatteryCondition(PEvent anEvent)
  425. {
  426. DWORD old_state = theState;
  427. DWORD state = atoi(anEvent->GetValue());
  428. //
  429. // get the current line condition
  430. // we only goto low battery if we
  431. // are also on battery
  432. //
  433. char value[256];
  434. theUpsApp->Get(UTILITY_LINE_CONDITION, value);
  435. DWORD line_state = atoi(value);
  436. if ((BATTERY_BAD == state) || (LOW_BATTERY == state)) {
  437. if (LINE_BAD == line_state) {
  438. theState = UPS_LOWBATTERY;
  439. }
  440. else {
  441. theState = UPS_ONLINE;
  442. }
  443. }
  444. else {
  445. if (LINE_BAD == line_state) {
  446. theState = UPS_ONBATTERY;
  447. }
  448. else {
  449. theState = UPS_ONLINE;
  450. }
  451. }
  452. if (old_state != theState) {
  453. UPSCancelWait();
  454. }
  455. return ErrNO_ERROR;
  456. }
  457. /**
  458. * onCommunicationState
  459. *
  460. * Description:
  461. * Determines the communication state to the UPS
  462. * If theState goes to UPS_NOCOMM we report the change
  463. * to threads pending on UPSWaitForStateChange
  464. * If the state is leaving UPS_NOCOMM, we
  465. * reinitialize the registry with advanced data
  466. * and then 'fake' power and battery condition
  467. * events
  468. *
  469. * Parameters:
  470. * anEvent: pointer to an Event object that
  471. * relates to a COMMUNICATION_STATE event
  472. *
  473. * Returns:
  474. * ErrNO_ERROR
  475. *
  476. */
  477. INT ApcMiniDriver::onCommunicationState(PEvent anEvent)
  478. {
  479. DWORD state = atoi(anEvent->GetValue());
  480. if ((COMMUNICATION_LOST == state) ||
  481. (COMMUNICATION_LOST_ON_BATTERY == state)) {
  482. if (UPS_NOCOMM != theState) {
  483. theState = UPS_NOCOMM;
  484. UPSCancelWait();
  485. }
  486. }
  487. else {
  488. if (UPS_NOCOMM == theState) {
  489. //
  490. // need to re-initialize the UPS data, since
  491. // when you lose comm, the user might plug in
  492. // a new UPS system
  493. //
  494. initalizeAdvancedUpsData();
  495. // Set the low battery warning threshold
  496. setLowBatteryDuration();
  497. //
  498. // here we just ask the service what it thinks
  499. // the current line/battery conditions are, and
  500. // 'fake' an event to ourselves. This allows
  501. // all line/battery state changes to be handled
  502. // consistently in the same methods
  503. //
  504. char value[256];
  505. theUpsApp->Get(UTILITY_LINE_CONDITION, value);
  506. Event ulc_evt(UTILITY_LINE_CONDITION, value);
  507. onUtilityLineCondition(&ulc_evt);
  508. theUpsApp->Get(BATTERY_CONDITION, value);
  509. Event batt_evt(BATTERY_CONDITION, value);
  510. onBatteryCondition(&batt_evt);
  511. }
  512. }
  513. return ErrNO_ERROR;
  514. }
  515. /**
  516. * onTimerPulse
  517. *
  518. * Description:
  519. * Retrieves the on-battery runtime and battery
  520. * capacity from the UPS and updates the registry
  521. * with the values if they differ from the last
  522. * time this method was called
  523. *
  524. * Parameters:
  525. * anEvent: pointer to an Event object that
  526. * relates to a TIMER_PULSE event
  527. *
  528. * Returns:
  529. * ErrNO_ERROR
  530. *
  531. */
  532. INT ApcMiniDriver::onTimerPulse(PEvent anEvent)
  533. {
  534. DWORD old_run_time = theOnBatteryRuntime;
  535. DWORD old_batt_cap = theBatteryCapacity;
  536. //
  537. // get the current on-battery runtime
  538. //
  539. CHAR data[100];
  540. if (theUpsApp->Get(RUN_TIME_REMAINING,data) == ErrNO_ERROR) {
  541. //
  542. // we get the RUN_TIME_REMAINING back in seconds
  543. // the UPS applet expects the value to be in
  544. // minutes -
  545. theOnBatteryRuntime = atol(data) / 60;
  546. }
  547. //
  548. // get the current battery capacity
  549. //
  550. if (theUpsApp->Get(BATTERY_CAPACITY,data) == ErrNO_ERROR) {
  551. // we get the battery capacity back as a percentage
  552. // the UPS applet expects only a whole number.
  553. theBatteryCapacity = (long) atof(data);
  554. }
  555. if ((old_run_time != theOnBatteryRuntime) || (old_batt_cap != theBatteryCapacity)){
  556. // One or more values changed, update the registry
  557. InitUPSStatusBlock();
  558. if (old_run_time != theOnBatteryRuntime) {
  559. // Update run time remaining
  560. SetUPSStatusRuntime(theOnBatteryRuntime);
  561. }
  562. if (old_batt_cap != theBatteryCapacity) {
  563. SetUPSStatusBatteryCapacity(theBatteryCapacity);
  564. }
  565. SaveUPSStatusBlock(FALSE);
  566. }
  567. //
  568. // must create another timer to update the
  569. // value again
  570. //
  571. theRunTimeTimer =
  572. _theTimerManager->SetTheTimer((ULONG)5,
  573. anEvent,
  574. this);
  575. return ErrNO_ERROR;
  576. }
  577. /**
  578. * initalizeAdvancedUpsData
  579. *
  580. * Description:
  581. * Retrieves the advanced data from theUpsApp
  582. * and forces an update to the registry with
  583. * the fresh data
  584. *
  585. * Parameters:
  586. * None
  587. *
  588. * Returns:
  589. * ErrNO_ERROR
  590. *
  591. */
  592. INT ApcMiniDriver::initalizeAdvancedUpsData()
  593. {
  594. // Initialize all static registry data
  595. InitUPSStatusBlock();
  596. CHAR data[100];
  597. TCHAR w_data[200];
  598. theUpsApp->Get(CURRENT_FIRMWARE_REV,data);
  599. #ifdef UNICODE
  600. MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, data, -1,
  601. w_data, (sizeof(w_data)/sizeof(TCHAR)));
  602. #else
  603. strcpy(w_data, data);
  604. #endif
  605. SetUPSStatusFirmRev(w_data);
  606. theUpsApp->Get(UPS_SERIAL_NUMBER,data);
  607. #ifdef UNICODE
  608. MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, data, -1,
  609. w_data, (sizeof(w_data)/sizeof(TCHAR)));
  610. #else
  611. strcpy(w_data, data);
  612. #endif
  613. SetUPSStatusSerialNum(w_data);
  614. // Default utility power and battery status to GOOD
  615. SetUPSStatusBatteryStatus(UPS_BATTERYSTATUS_GOOD);
  616. SetUPSStatusBatteryCapacity(0);
  617. SaveUPSStatusBlock(TRUE);
  618. //
  619. // dummy up a BATTERY_REPLACEMENT_CONDITION event,
  620. // just to make sure we initialize the registry with
  621. // valid data, since this is an event we will not
  622. // be notified unless the state changes
  623. //
  624. theUpsApp->Get(BATTERY_REPLACEMENT_CONDITION, data);
  625. Event replace_batt(BATTERY_REPLACEMENT_CONDITION, data);
  626. onBatteryReplacementCondition(&replace_batt);
  627. return ErrNO_ERROR;
  628. }
  629. /**
  630. * initalizeUpsApplication
  631. *
  632. * Description:
  633. * inits theUpsApp member with a new NTServerApplication
  634. * object.
  635. * Registers for the events that can be received
  636. * in the Update method
  637. * Initalizes the advanced UPS data in the registry,
  638. * and starts the repeating event that triggers updates
  639. * to get on-battery runtime data
  640. *
  641. * Parameters:
  642. * None
  643. *
  644. * Returns:
  645. * ErrNO_ERROR: init succeeded
  646. * ErrMEMORY: theUpsApp could not be created
  647. *
  648. */
  649. INT ApcMiniDriver::initalizeUpsApplication()
  650. {
  651. INT err = ErrNO_ERROR;
  652. //
  653. // assume that the UPS is not on battery
  654. // this matches the assumption made by
  655. // the NTServerApplication class
  656. //
  657. theState = UPS_ONLINE;
  658. theUpsApp = new NTServerApplication();
  659. if (!theUpsApp) {
  660. err = ErrMEMORY;
  661. }
  662. else {
  663. theUpsApp->RegisterEvent(UTILITY_LINE_CONDITION, this);
  664. theUpsApp->RegisterEvent(BATTERY_CONDITION, this);
  665. theUpsApp->RegisterEvent(BATTERY_REPLACEMENT_CONDITION, this);
  666. theUpsApp->RegisterEvent(COMMUNICATION_STATE, this);
  667. err = theUpsApp->Start();
  668. }
  669. if (ErrNO_ERROR == err) {
  670. initalizeAdvancedUpsData();
  671. // Start the polling of UPS run time
  672. if (!theRunTimeTimer) {
  673. Event retry_event(TIMER_PULSE, TIMER_PULSE);
  674. onTimerPulse(&retry_event);
  675. }
  676. }
  677. return err;
  678. }
  679. /**
  680. * cleanupUpsApplication
  681. *
  682. * Description:
  683. * cleans up and destructs theUpsApp object. Sets
  684. * member variables to the default values
  685. *
  686. *
  687. * Parameters:
  688. * None
  689. *
  690. * Returns:
  691. * None
  692. *
  693. */
  694. void ApcMiniDriver::cleanupUpsApplication()
  695. {
  696. theState = UPS_ONLINE;
  697. if (theUpsApp) {
  698. theUpsApp->Quit();
  699. delete theUpsApp;
  700. theUpsApp = NULL;
  701. }
  702. theRunTimeTimer = 0;
  703. theOnBatteryRuntime = -1;
  704. }
  705. /**
  706. * setLowBatteryDuration
  707. *
  708. * Description:
  709. * Sets the low battery duration to a value compatable with the
  710. * UPSTurnOffWait registry key. This method querries the
  711. * UPS for the allowed low battery durations and sets the
  712. * duration to one of the following:
  713. * 1. The value of UPSTurnOffWait if it exactly matches one of
  714. * the allowed values
  715. * 2. the next highest value after UPSTurnOffWait, if one exists
  716. * 3. the highest allowed value, if UPSTurnOffWait is larger than
  717. * all of the allowed values.
  718. * If the UPSTurnOffWait is not set or no allowed values are returned,
  719. * the low battery duration will not be set.
  720. *
  721. * Parameters:
  722. * None
  723. *
  724. * Returns:
  725. * None
  726. *
  727. */
  728. void ApcMiniDriver::setLowBatteryDuration() {
  729. DWORD turn_off_wait;
  730. InitUPSConfigBlock();
  731. if ((GetUPSConfigTurnOffWait(&turn_off_wait) == ERROR_SUCCESS) && (theUpsApp)) {
  732. char allowed_values[512];
  733. // Retrieve the allowed low battery durations from the UPS
  734. theUpsApp->Get(ALLOWED_LOW_BATTERY_DURATIONS, allowed_values);
  735. TokenString token_str(allowed_values, LOW_BATT_DURATION_SEPARATOR);
  736. PCHAR tok = token_str.GetCurrentToken();
  737. // Set the low battery duration if there are allowed values
  738. if (tok) {
  739. // Initialize counters
  740. long max = atol(tok);
  741. long last_closest = 0;
  742. long requested_value = 0;
  743. if (turn_off_wait > 0) {
  744. // Convert the value from seconds to minutes (rounded up)
  745. requested_value = turn_off_wait / SECONDS_TO_MINUTES;
  746. if ((turn_off_wait % SECONDS_TO_MINUTES) > 0) {
  747. ++requested_value;
  748. }
  749. }
  750. // Cycle through the allowed values looking for a suitable value
  751. while (tok) {
  752. long value = atol(tok);
  753. // Check to see if this value is closest to the requested
  754. if ( ((value >= requested_value) && (value < last_closest))
  755. || (last_closest < requested_value)) {
  756. last_closest = value;
  757. }
  758. // Check to see if this value is the max value
  759. if (value > max) {
  760. max = value;
  761. }
  762. // Get the next value
  763. tok = token_str.GetCurrentToken();
  764. }
  765. long low_batt_duration = last_closest;
  766. if (last_closest < requested_value) {
  767. // The requested value is larger than all of the values, use the max
  768. low_batt_duration = max;
  769. }
  770. // Set the shutdown delay
  771. char low_batt_duration_str[4];
  772. sprintf(low_batt_duration_str, "%2.2u", low_batt_duration);
  773. theUpsApp->Set(LOW_BATTERY_DURATION, low_batt_duration_str);
  774. }
  775. }
  776. }