WvStreams
wvdailyevent.cc
00001 /*
00002  * Worldvisions Weaver Software:
00003  *   Copyright (C) 1997-2004 Net Integration Technologies, Inc.
00004  *
00005  * A simple class that can trigger an event on a timed basis.
00006  *   a) if given an hour, triggers once per day, on that hour.
00007  *   b) if given a number of times per day, triggers that many times per
00008  *      day, evenly, starting at the hour given in (a).  (Needed to get a
00009  *      Microbackup going every 15 minutes.)  
00010  *
00011  * Presently has a one-hour granularity in the first case, but that can be
00012  * extended one day when someone cares.
00013  *
00014  */
00015 #include "wvdailyevent.h"
00016 #include "wvstream.h"
00017 #include "wvtimeutils.h"
00018 
00019 #include <time.h>
00020 
00021 #ifndef _WIN32
00022 #include <sys/time.h>
00023 #include <unistd.h>
00024 #endif
00025 
00026 #define NUM_MINS_IN_DAY (24*60)
00027 #define NUM_SECS_IN_DAY (60*NUM_MINS_IN_DAY)
00028 
00029 WvDailyEvent::WvDailyEvent(int _first_hour, int _num_per_day, bool _skip_first)
00030     : prev(time(NULL))
00031 {
00032     need_reset = false;
00033     prev = wvstime().tv_sec;
00034     configure(_first_hour, _num_per_day, _skip_first);
00035 }
00036 
00037 
00038 // Compute the next time this stream should select()
00039 void WvDailyEvent::pre_select(SelectInfo &si)
00040 {
00041     WvStream::pre_select(si);
00042 
00043     if (num_per_day)
00044     {
00045         time_t now = wvstime().tv_sec;
00046         time_t next = next_event();
00047 
00048         assert(prev);
00049         assert(next);
00050         assert(prev > 100000);
00051         assert(next > 100000);
00052 
00053         //printf("%d %d %d\n", now, next, msecdiff(now, next));
00054         if (now < next)
00055             si.msec_timeout = msecdiff(now, next);
00056         else if (!need_reset)
00057         {
00058             need_reset = true;
00059             prev = next;
00060         }
00061     }
00062     if (need_reset)
00063         si.msec_timeout = 0;
00064     //printf("%p msd=%d\n", this, ret, si.msec_timeout);
00065 }
00066 
00067 
00068 // Test to see if the timer has gone off
00069 bool WvDailyEvent::post_select(SelectInfo& si)
00070 {
00071     bool timer_rang = false;
00072     WvTime next(next_event(), 0);
00073     if (next < wvtime())
00074     {
00075         timer_rang = true;
00076         prev = next;
00077     }
00078 
00079     return WvStream::post_select(si) || need_reset || timer_rang;
00080 }
00081 
00082 
00083 void WvDailyEvent::set_num_per_day(int _num_per_day) 
00084 {
00085     num_per_day = _num_per_day;
00086     if (num_per_day < 0)
00087         num_per_day = 1;
00088 
00089     if (num_per_day > NUM_SECS_IN_DAY)
00090         num_per_day = NUM_SECS_IN_DAY;
00091         
00092     time_t max = num_per_day ? NUM_SECS_IN_DAY/num_per_day : 6*60*60;
00093     if (max > 6*60*60)
00094         max = 6*60*60; // unless that's a very long time, 6 hrs
00095 
00096     // don't start until at least one period has gone by
00097     prev = wvstime().tv_sec;
00098     not_until = prev + max;
00099 }
00100 
00101 
00102 void WvDailyEvent::configure(int _first_hour, int _num_per_day, bool _skip_first)
00103 {
00104     first_hour = _first_hour;
00105     skip_first = _skip_first;
00106 
00107     // Don't let WvDailyEvents occur more than once a minute. -- use an alarm
00108     // instead
00109     if (_num_per_day > NUM_MINS_IN_DAY)
00110         _num_per_day = NUM_MINS_IN_DAY;
00111 
00112     set_num_per_day(_num_per_day);
00113 }
00114 
00115 // the daily event occurs each day at first_hour on the hour, or at
00116 // some multiple of the interval *after* that hour.
00117 time_t WvDailyEvent::next_event() const
00118 {
00119     if (!num_per_day) // disabled
00120         return 0;
00121 
00122     assert(prev);
00123     
00124     time_t interval = NUM_SECS_IN_DAY/num_per_day;
00125     time_t start = prev + interval;
00126    
00127     // find the time to start counting from (up to 24 hours in the past)
00128     struct tm *tm = localtime(&start);
00129     if (tm->tm_hour < first_hour)
00130     {
00131         start = prev - NUM_SECS_IN_DAY + 1; // this time yesterday
00132         tm = localtime(&start);
00133     }
00134     tm->tm_hour = first_hour; // always start at the given hour
00135     tm->tm_min = tm->tm_sec = 0; // right on the hour
00136     start = mktime(tm); // convert back into a time_t
00137 
00138     // find the next event after prev that's a multiple of 'interval'
00139     // since 'start'
00140     time_t next = prev + interval;
00141     if ((next - start)%interval != 0)
00142         next = start + (next - start)/interval * interval;
00143     
00144     assert(next);
00145     assert(next > 100000);
00146 
00147     while (skip_first && next < not_until)
00148         next += interval;
00149 
00150     return next;
00151 }