diff options
Diffstat (limited to 'modules-available/rebootcontrol/inc/scheduler.inc.php')
-rw-r--r-- | modules-available/rebootcontrol/inc/scheduler.inc.php | 148 |
1 files changed, 68 insertions, 80 deletions
diff --git a/modules-available/rebootcontrol/inc/scheduler.inc.php b/modules-available/rebootcontrol/inc/scheduler.inc.php index 7a5c08dc..613fbbee 100644 --- a/modules-available/rebootcontrol/inc/scheduler.inc.php +++ b/modules-available/rebootcontrol/inc/scheduler.inc.php @@ -5,14 +5,22 @@ class Scheduler public static function updateSchedule($locationid, $options, $openingTimes) { - if ($openingTimes == '') { + if (empty($openingTimes)) { self::deleteSchedule($locationid); return false; } $nextexec = self::calculateNext($options, $openingTimes); - $json_options = json_encode($options); if ($nextexec !== false) { - self::upsert($locationid, $nextexec['action'], $nextexec['time'], $json_options); + $json_options = json_encode($options); + Database::exec("INSERT INTO `reboot_scheduler` (locationid, action, nextexecution, options) + VALUES (:lid, :act, :next, :opt) + ON DUPLICATE KEY UPDATE + action = VALUES(action), nextexecution = VALUES(nextexecution), options = VALUES(options)", [ + 'lid' => $locationid, + 'act' => $nextexec['action'], + 'next' => $nextexec['time'], + 'opt' => $json_options, + ]); } else { // All times are getting ignored because they are within 5 minutes of each other, delete possible db entries. self::deleteSchedule($locationid); @@ -26,106 +34,86 @@ class Scheduler WHERE locationid = :lid", ['lid' => $locationid]); } + private static function calculateTimestamp($now, $day, $time) + { + $ts = strtotime("$day $time"); + if ($ts < $now) { + $ts = strtotime("next $day $time"); + } + if ($ts < $now) { + EventLog::warning("Invalid params to calculateTimestamp(): 'next $day $time'"); + $ts = $now + 864000; + } + return $ts; + } + private static function calculateNext($options, $openingTimes) { + if (!$options['wol'] && !$options['sd']) + return false; $openingTimes = json_decode($openingTimes, true); $now = time(); - $openTimes = []; - $closeTimes = []; + $events = []; foreach ($openingTimes as $row) { - if (!$options['wol'] && !$options['sd']) - continue; - if ($options['wol']) { - $open = explode(':', $row['openingtime']); - $openTime = $open[0] * 60 + $open[1] - $options['wol-offset']; - } - if ($options['sd']) { - $close = explode(':', $row['closingtime']); - $closeTime = $close[0] * 60 + $close[1] + $options['sd-offset']; - } foreach ($row['days'] as $day) { if ($options['wol']) { - $nextOpen = strtotime(date('Y-m-d H:i', strtotime($day . ' ' . $openTime . ' minutes'))); - if ($nextOpen < $now) { - $openTimes[] = strtotime(date('Y-m-d H:i', strtotime('next ' . $day . ' ' . $openTime . ' minutes'))); - } else { - $openTimes[] = $nextOpen; - } + $events[] = ['action' => 'wol', + 'time' => self::calculateTimestamp($now, $day, $row['openingtime'])]; } if ($options['sd']) { - $nextClose = strtotime(date('Y-m-d H:i', strtotime($day . ' ' . $closeTime . ' minutes'))); - if ($nextClose < $now) { - $closeTimes[] = strtotime(date('Y-m-d H:i', strtotime('next ' . $day . ' ' . $closeTime . ' minutes'))); - } else { - $closeTimes[] = $nextClose; - } + $events[] = ['action' => 'sd', + 'time' => self::calculateTimestamp($now, $day, $row['closingtime'])]; } } } + $tmp = ArrayUtil::flattenByKey($events, 'time'); + array_multisort($tmp, SORT_NUMERIC | SORT_ASC, $events); - sort($openTimes); - sort($closeTimes); - $res = []; - - if ($options['wol'] && !$options['sd']) { - $res['action'] = 'wol'; - $res['time'] = $openTimes[0]; - return $res; - } else if ($options['sd'] && !$options['wol']) { - $res['action'] = 'sd'; - $res['time'] = $closeTimes[0]; - return $res; + // Only apply offsets now, so we can detect nonsensical overlap + $wolOffset = $options['wol-offset'] * 60; + $sdOffset = $options['sd-offset'] * 60; + $prev = PHP_INT_MAX; + for ($i = count($events) - 1; $i >= 0; --$i) { + $event =& $events[$i]; + if ($event['action'] === 'wol') { + $event['time'] -= $wolOffset; + } elseif ($event['action'] === 'sd') { + $event['time'] += $sdOffset; + } else { + error_log('BUG Unhandled event type ' . $event['action']); + } + if ($event['time'] >= $prev || $event['time'] < $now) { + // Overlap, delete this event + unset($events[$i]); + } else { + $prev = $event['time']; + } } + unset($event); + // Reset array keys + $events = array_values($events); - for ($i = 0; $i <= sizeof($openTimes); $i++) { - if (abs($openTimes[$i] - $closeTimes[$i]) < 300) { - // If difference is < 5 min, ignore both events. + // See which is the next suitable event to act upon + $lastEvent = count($events) - 1; + for ($i = 0; $i <= $lastEvent; $i++) { + $event =& $events[$i]; + $diff = ($i === $lastEvent ? PHP_INT_MAX : $events[$i + 1]['time'] - $event['time']); + if ($diff < 300 && $event['action'] !== $events[$i + 1]['action']) { + // If difference to next event is < 5 min, ignore. continue; - } elseif (abs($openTimes[$i] - $closeTimes[$i]) < 900) { - // If difference is < 15 min, reboot at the earlier time. + } + if ($diff < 900 && $event['action'] === 'sd' && $events[$i + 1]['action'] === 'wol') { + // If difference to next WOL is < 15 min and this is a shutdown, reboot instead. $res['action'] = 'rb'; - $res['time'] = $openTimes[$i] < $closeTimes[$i] ? $openTimes[$i] : $closeTimes[$i]; + $res['time'] = $event['time']; } else { // Use first event. - if ($openTimes[$i] < $closeTimes[$i]) { - $res['action'] = 'wol'; - $res['time'] = $openTimes[$i]; - } else { - $res['action'] = 'sd'; - $res['time'] = $closeTimes[$i]; - } + $res = $event; } return $res; } + unset($event); return false; } - private static function upsert($locationid, $action, $nextexec, $options) - { - $schedule = Database::queryFirst("SELECT locationid - FROM `reboot_scheduler` - WHERE locationid = :lid", [ - 'lid' => $locationid - ]); - if ($schedule === false) { - Database::exec("INSERT INTO `reboot_scheduler` (locationid, action, nextexecution, options) - VALUES (:lid, :act, :next, :opt)", [ - 'lid' => $locationid, - 'act' => $action, - 'next' => $nextexec, - 'opt' => $options - ]); - } else { - Database::exec("UPDATE `reboot_scheduler` - SET action = :act, nextexecution = :next, options = :opt - WHERE locationid = :lid", [ - 'act' => $action, - 'next' => $nextexec, - 'opt' => $options, - 'lid' => $locationid - ]); - } - return true; - } - }
\ No newline at end of file |