vdr  1.7.27
timers.c
Go to the documentation of this file.
00001 /*
00002  * timers.c: Timer handling
00003  *
00004  * See the main source file 'vdr.c' for copyright information and
00005  * how to reach the author.
00006  *
00007  * $Id: timers.c 2.8 2012/02/27 09:38:41 kls Exp $
00008  */
00009 
00010 #include "timers.h"
00011 #include <ctype.h>
00012 #include "channels.h"
00013 #include "device.h"
00014 #include "i18n.h"
00015 #include "libsi/si.h"
00016 #include "recording.h"
00017 #include "remote.h"
00018 #include "status.h"
00019 
00020 #define VFAT_MAX_FILENAME 40 // same as MAX_SUBTITLE_LENGTH in recording.c
00021 
00022 // IMPORTANT NOTE: in the 'sscanf()' calls there is a blank after the '%d'
00023 // format characters in order to allow any number of blanks after a numeric
00024 // value!
00025 
00026 // --- cTimer ----------------------------------------------------------------
00027 
00028 cTimer::cTimer(bool Instant, bool Pause, cChannel *Channel)
00029 {
00030   startTime = stopTime = 0;
00031   lastSetEvent = 0;
00032   deferred = 0;
00033   recording = pending = inVpsMargin = false;
00034   flags = tfNone;
00035   if (Instant)
00036      SetFlags(tfActive | tfInstant);
00037   channel = Channel ? Channel : Channels.GetByNumber(cDevice::CurrentChannel());
00038   time_t t = time(NULL);
00039   struct tm tm_r;
00040   struct tm *now = localtime_r(&t, &tm_r);
00041   day = SetTime(t, 0);
00042   weekdays = 0;
00043   start = now->tm_hour * 100 + now->tm_min;
00044   stop = now->tm_hour * 60 + now->tm_min + Setup.InstantRecordTime;
00045   stop = (stop / 60) * 100 + (stop % 60);
00046   if (stop >= 2400)
00047      stop -= 2400;
00048   priority = Pause ? Setup.PausePriority : Setup.DefaultPriority;
00049   lifetime = Pause ? Setup.PauseLifetime : Setup.DefaultLifetime;
00050   *file = 0;
00051   aux = NULL;
00052   event = NULL;
00053   if (Instant && channel)
00054      snprintf(file, sizeof(file), "%s%s", Setup.MarkInstantRecord ? "@" : "", *Setup.NameInstantRecord ? Setup.NameInstantRecord : channel->Name());
00055   if (VfatFileSystem && (Utf8StrLen(file) > VFAT_MAX_FILENAME)) {
00056      dsyslog("timer file name too long for VFAT file system: '%s'", file);
00057      file[Utf8SymChars(file, VFAT_MAX_FILENAME)] = 0;
00058      dsyslog("timer file name truncated to '%s'", file);
00059      }
00060 }
00061 
00062 cTimer::cTimer(const cEvent *Event)
00063 {
00064   startTime = stopTime = 0;
00065   lastSetEvent = 0;
00066   deferred = 0;
00067   recording = pending = inVpsMargin = false;
00068   flags = tfActive;
00069   if (Event->Vps() && Setup.UseVps)
00070      SetFlags(tfVps);
00071   channel = Channels.GetByChannelID(Event->ChannelID(), true);
00072   time_t tstart = (flags & tfVps) ? Event->Vps() : Event->StartTime();
00073   time_t tstop = tstart + Event->Duration();
00074   if (!(HasFlags(tfVps))) {
00075      tstop  += Setup.MarginStop * 60;
00076      tstart -= Setup.MarginStart * 60;
00077      }
00078   struct tm tm_r;
00079   struct tm *time = localtime_r(&tstart, &tm_r);
00080   day = SetTime(tstart, 0);
00081   weekdays = 0;
00082   start = time->tm_hour * 100 + time->tm_min;
00083   time = localtime_r(&tstop, &tm_r);
00084   stop = time->tm_hour * 100 + time->tm_min;
00085   if (stop >= 2400)
00086      stop -= 2400;
00087   priority = Setup.DefaultPriority;
00088   lifetime = Setup.DefaultLifetime;
00089   *file = 0;
00090   const char *Title = Event->Title();
00091   if (!isempty(Title))
00092      Utf8Strn0Cpy(file, Event->Title(), sizeof(file));
00093   if (VfatFileSystem && (Utf8StrLen(file) > VFAT_MAX_FILENAME)) {
00094      dsyslog("timer file name too long for VFAT file system: '%s'", file);
00095      file[Utf8SymChars(file, VFAT_MAX_FILENAME)] = 0;
00096      dsyslog("timer file name truncated to '%s'", file);
00097      }
00098   aux = NULL;
00099   event = NULL; // let SetEvent() be called to get a log message
00100 }
00101 
00102 cTimer::cTimer(const cTimer &Timer)
00103 {
00104   channel = NULL;
00105   aux = NULL;
00106   event = NULL;
00107   flags = tfNone;
00108   *this = Timer;
00109 }
00110 
00111 cTimer::~cTimer()
00112 {
00113   free(aux);
00114 }
00115 
00116 cTimer& cTimer::operator= (const cTimer &Timer)
00117 {
00118   if (&Timer != this) {
00119      uint OldFlags = flags & tfRecording;
00120      startTime    = Timer.startTime;
00121      stopTime     = Timer.stopTime;
00122      lastSetEvent = 0;
00123      deferred = 0;
00124      recording    = Timer.recording;
00125      pending      = Timer.pending;
00126      inVpsMargin  = Timer.inVpsMargin;
00127      flags        = Timer.flags | OldFlags;
00128      channel      = Timer.channel;
00129      day          = Timer.day;
00130      weekdays     = Timer.weekdays;
00131      start        = Timer.start;
00132      stop         = Timer.stop;
00133      priority     = Timer.priority;
00134      lifetime     = Timer.lifetime;
00135      strncpy(file, Timer.file, sizeof(file));
00136      free(aux);
00137      aux = Timer.aux ? strdup(Timer.aux) : NULL;
00138      event = NULL;
00139      }
00140   return *this;
00141 }
00142 
00143 int cTimer::Compare(const cListObject &ListObject) const
00144 {
00145   cTimer *ti = (cTimer *)&ListObject;
00146   time_t t1 = StartTime();
00147   time_t t2 = ti->StartTime();
00148   int r = t1 - t2;
00149   if (r == 0)
00150      r = ti->priority - priority;
00151   return r;
00152 }
00153 
00154 cString cTimer::ToText(bool UseChannelID) const
00155 {
00156   strreplace(file, ':', '|');
00157   cString buffer = cString::sprintf("%u:%s:%s:%04d:%04d:%d:%d:%s:%s\n", flags, UseChannelID ? *Channel()->GetChannelID().ToString() : *itoa(Channel()->Number()), *PrintDay(day, weekdays, true), start, stop, priority, lifetime, file, aux ? aux : "");
00158   strreplace(file, '|', ':');
00159   return buffer;
00160 }
00161 
00162 cString cTimer::ToDescr(void) const
00163 {
00164   return cString::sprintf("%d (%d %04d-%04d %s'%s')", Index() + 1, Channel()->Number(), start, stop, HasFlags(tfVps) ? "VPS " : "", file);
00165 }
00166 
00167 int cTimer::TimeToInt(int t)
00168 {
00169   return (t / 100 * 60 + t % 100) * 60;
00170 }
00171 
00172 bool cTimer::ParseDay(const char *s, time_t &Day, int &WeekDays)
00173 {
00174   // possible formats are:
00175   // 19
00176   // 2005-03-19
00177   // MTWTFSS
00178   // MTWTFSS@19
00179   // MTWTFSS@2005-03-19
00180 
00181   Day = 0;
00182   WeekDays = 0;
00183   s = skipspace(s);
00184   if (!*s)
00185      return false;
00186   const char *a = strchr(s, '@');
00187   const char *d = a ? a + 1 : isdigit(*s) ? s : NULL;
00188   if (d) {
00189      if (strlen(d) == 10) {
00190         struct tm tm_r;
00191         if (3 == sscanf(d, "%d-%d-%d", &tm_r.tm_year, &tm_r.tm_mon, &tm_r.tm_mday)) {
00192            tm_r.tm_year -= 1900;
00193            tm_r.tm_mon--;
00194            tm_r.tm_hour = tm_r.tm_min = tm_r.tm_sec = 0;
00195            tm_r.tm_isdst = -1; // makes sure mktime() will determine the correct DST setting
00196            Day = mktime(&tm_r);
00197            }
00198         else
00199            return false;
00200         }
00201      else {
00202         // handle "day of month" for compatibility with older versions:
00203         char *tail = NULL;
00204         int day = strtol(d, &tail, 10);
00205         if (tail && *tail || day < 1 || day > 31)
00206            return false;
00207         time_t t = time(NULL);
00208         int DaysToCheck = 61; // 61 to handle months with 31/30/31
00209         for (int i = -1; i <= DaysToCheck; i++) {
00210             time_t t0 = IncDay(t, i);
00211             if (GetMDay(t0) == day) {
00212                Day = SetTime(t0, 0);
00213                break;
00214                }
00215             }
00216         }
00217      }
00218   if (a || !isdigit(*s)) {
00219      if ((a && a - s == 7) || strlen(s) == 7) {
00220         for (const char *p = s + 6; p >= s; p--) {
00221             WeekDays <<= 1;
00222             WeekDays |= (*p != '-');
00223             }
00224         }
00225      else
00226         return false;
00227      }
00228   return true;
00229 }
00230 
00231 cString cTimer::PrintDay(time_t Day, int WeekDays, bool SingleByteChars)
00232 {
00233 #define DAYBUFFERSIZE 64
00234   char buffer[DAYBUFFERSIZE];
00235   char *b = buffer;
00236   if (WeekDays) {
00237      // TRANSLATORS: the first character of each weekday, beginning with monday
00238      const char *w = trNOOP("MTWTFSS");
00239      if (!SingleByteChars)
00240         w = tr(w);
00241      while (*w) {
00242            int sl = Utf8CharLen(w);
00243            if (WeekDays & 1) {
00244               for (int i = 0; i < sl; i++)
00245                   b[i] = w[i];
00246               b += sl;
00247               }
00248            else
00249               *b++ = '-';
00250            WeekDays >>= 1;
00251            w += sl;
00252            }
00253      if (Day)
00254         *b++ = '@';
00255      }
00256   if (Day) {
00257      struct tm tm_r;
00258      localtime_r(&Day, &tm_r);
00259      b += strftime(b, DAYBUFFERSIZE - (b - buffer), "%Y-%m-%d", &tm_r);
00260      }
00261   *b = 0;
00262   return buffer;
00263 }
00264 
00265 cString cTimer::PrintFirstDay(void) const
00266 {
00267   if (weekdays) {
00268      cString s = PrintDay(day, weekdays, true);
00269      if (strlen(s) == 18)
00270         return *s + 8;
00271      }
00272   return ""; // not NULL, so the caller can always use the result
00273 }
00274 
00275 bool cTimer::Parse(const char *s)
00276 {
00277   char *channelbuffer = NULL;
00278   char *daybuffer = NULL;
00279   char *filebuffer = NULL;
00280   free(aux);
00281   aux = NULL;
00282   //XXX Apparently sscanf() doesn't work correctly if the last %a argument
00283   //XXX results in an empty string (this first occured when the EIT gathering
00284   //XXX was put into a separate thread - don't know why this happens...
00285   //XXX As a cure we copy the original string and add a blank.
00286   //XXX If anybody can shed some light on why sscanf() failes here, I'd love
00287   //XXX to hear about that!
00288   char *s2 = NULL;
00289   int l2 = strlen(s);
00290   while (l2 > 0 && isspace(s[l2 - 1]))
00291         l2--;
00292   if (s[l2 - 1] == ':') {
00293      s2 = MALLOC(char, l2 + 3);
00294      strcat(strn0cpy(s2, s, l2 + 1), " \n");
00295      s = s2;
00296      }
00297   bool result = false;
00298   if (8 <= sscanf(s, "%u :%a[^:]:%a[^:]:%d :%d :%d :%d :%a[^:\n]:%a[^\n]", &flags, &channelbuffer, &daybuffer, &start, &stop, &priority, &lifetime, &filebuffer, &aux)) {
00299      ClrFlags(tfRecording);
00300      if (aux && !*skipspace(aux)) {
00301         free(aux);
00302         aux = NULL;
00303         }
00304      //TODO add more plausibility checks
00305      result = ParseDay(daybuffer, day, weekdays);
00306      if (VfatFileSystem) {
00307         char *p = strrchr(filebuffer, FOLDERDELIMCHAR);
00308         if (p)
00309            p++;
00310         else
00311            p = filebuffer;
00312         if (Utf8StrLen(p) > VFAT_MAX_FILENAME) {
00313            dsyslog("timer file name too long for VFAT file system: '%s'", p);
00314            p[Utf8SymChars(p, VFAT_MAX_FILENAME)] = 0;
00315            dsyslog("timer file name truncated to '%s'", p);
00316            }
00317         }
00318      Utf8Strn0Cpy(file, filebuffer, sizeof(file));
00319      strreplace(file, '|', ':');
00320      if (isnumber(channelbuffer))
00321         channel = Channels.GetByNumber(atoi(channelbuffer));
00322      else
00323         channel = Channels.GetByChannelID(tChannelID::FromString(channelbuffer), true, true);
00324      if (!channel) {
00325         esyslog("ERROR: channel %s not defined", channelbuffer);
00326         result = false;
00327         }
00328      }
00329   free(channelbuffer);
00330   free(daybuffer);
00331   free(filebuffer);
00332   free(s2);
00333   return result;
00334 }
00335 
00336 bool cTimer::Save(FILE *f)
00337 {
00338   return fprintf(f, "%s", *ToText(true)) > 0;
00339 }
00340 
00341 bool cTimer::IsSingleEvent(void) const
00342 {
00343   return !weekdays;
00344 }
00345 
00346 int cTimer::GetMDay(time_t t)
00347 {
00348   struct tm tm_r;
00349   return localtime_r(&t, &tm_r)->tm_mday;
00350 }
00351 
00352 int cTimer::GetWDay(time_t t)
00353 {
00354   struct tm tm_r;
00355   int weekday = localtime_r(&t, &tm_r)->tm_wday;
00356   return weekday == 0 ? 6 : weekday - 1; // we start with Monday==0!
00357 }
00358 
00359 bool cTimer::DayMatches(time_t t) const
00360 {
00361   return IsSingleEvent() ? SetTime(t, 0) == day : (weekdays & (1 << GetWDay(t))) != 0;
00362 }
00363 
00364 time_t cTimer::IncDay(time_t t, int Days)
00365 {
00366   struct tm tm_r;
00367   tm tm = *localtime_r(&t, &tm_r);
00368   tm.tm_mday += Days; // now tm_mday may be out of its valid range
00369   int h = tm.tm_hour; // save original hour to compensate for DST change
00370   tm.tm_isdst = -1;   // makes sure mktime() will determine the correct DST setting
00371   t = mktime(&tm);    // normalize all values
00372   tm.tm_hour = h;     // compensate for DST change
00373   return mktime(&tm); // calculate final result
00374 }
00375 
00376 time_t cTimer::SetTime(time_t t, int SecondsFromMidnight)
00377 {
00378   struct tm tm_r;
00379   tm tm = *localtime_r(&t, &tm_r);
00380   tm.tm_hour = SecondsFromMidnight / 3600;
00381   tm.tm_min = (SecondsFromMidnight % 3600) / 60;
00382   tm.tm_sec =  SecondsFromMidnight % 60;
00383   tm.tm_isdst = -1; // makes sure mktime() will determine the correct DST setting
00384   return mktime(&tm);
00385 }
00386 
00387 void cTimer::SetFile(const char *File)
00388 {
00389   if (!isempty(File))
00390      Utf8Strn0Cpy(file, File, sizeof(file));
00391 }
00392 
00393 #define EITPRESENTFOLLOWINGRATE 10 // max. seconds between two occurrences of the "EIT present/following table for the actual multiplex" (2s by the standard, using some more for safety)
00394 
00395 bool cTimer::Matches(time_t t, bool Directly, int Margin) const
00396 {
00397   startTime = stopTime = 0;
00398   if (t == 0)
00399      t = time(NULL);
00400 
00401   int begin  = TimeToInt(start); // seconds from midnight
00402   int length = TimeToInt(stop) - begin;
00403   if (length < 0)
00404      length += SECSINDAY;
00405 
00406   if (IsSingleEvent()) {
00407      startTime = SetTime(day, begin);
00408      stopTime = startTime + length;
00409      }
00410   else {
00411      for (int i = -1; i <= 7; i++) {
00412          time_t t0 = IncDay(day ? max(day, t) : t, i);
00413          if (DayMatches(t0)) {
00414             time_t a = SetTime(t0, begin);
00415             time_t b = a + length;
00416             if ((!day || a >= day) && t < b) {
00417                startTime = a;
00418                stopTime = b;
00419                break;
00420                }
00421             }
00422          }
00423      if (!startTime)
00424         startTime = IncDay(t, 7); // just to have something that's more than a week in the future
00425      else if (!Directly && (t > startTime || t > day + SECSINDAY + 3600)) // +3600 in case of DST change
00426         day = 0;
00427      }
00428 
00429   if (t < deferred)
00430      return false;
00431   deferred = 0;
00432 
00433   if (HasFlags(tfActive)) {
00434      if (HasFlags(tfVps) && event && event->Vps()) {
00435         if (Margin || !Directly) {
00436            startTime = event->StartTime();
00437            stopTime = event->EndTime();
00438            if (!Margin) { // this is an actual check
00439               if (event->Schedule()->PresentSeenWithin(EITPRESENTFOLLOWINGRATE)) // VPS control can only work with up-to-date events...
00440                  return event->IsRunning(true);
00441               else
00442                  return startTime <= t && t < stopTime; // ...otherwise we fall back to normal timer handling
00443               }
00444            }
00445         }
00446      return startTime <= t + Margin && t < stopTime; // must stop *before* stopTime to allow adjacent timers
00447      }
00448   return false;
00449 }
00450 
00451 #define FULLMATCH 1000
00452 
00453 int cTimer::Matches(const cEvent *Event, int *Overlap) const
00454 {
00455   // Overlap is the percentage of the Event's duration that is covered by
00456   // this timer (based on FULLMATCH for finer granularity than just 100).
00457   // To make sure a VPS timer can be distinguished from a plain 100% overlap,
00458   // it gets an additional 100 added, and a VPS event that is actually running
00459   // gets 200 added to the FULLMATCH.
00460   if (HasFlags(tfActive) && channel->GetChannelID() == Event->ChannelID()) {
00461      bool UseVps = HasFlags(tfVps) && Event->Vps();
00462      Matches(UseVps ? Event->Vps() : Event->StartTime(), true);
00463      int overlap = 0;
00464      if (UseVps)
00465         overlap = (startTime == Event->Vps()) ? FULLMATCH + (Event->IsRunning() ? 200 : 100) : 0;
00466      if (!overlap) {
00467         if (startTime <= Event->StartTime() && Event->EndTime() <= stopTime)
00468            overlap = FULLMATCH;
00469         else if (stopTime <= Event->StartTime() || Event->EndTime() <= startTime)
00470            overlap = 0;
00471         else
00472            overlap = (min(stopTime, Event->EndTime()) - max(startTime, Event->StartTime())) * FULLMATCH / max(Event->Duration(), 1);
00473         }
00474      startTime = stopTime = 0;
00475      if (Overlap)
00476         *Overlap = overlap;
00477      if (UseVps)
00478         return overlap > FULLMATCH ? tmFull : tmNone;
00479      return overlap >= FULLMATCH ? tmFull : overlap > 0 ? tmPartial : tmNone;
00480      }
00481   return tmNone;
00482 }
00483 
00484 #define EXPIRELATENCY 60 // seconds (just in case there's a short glitch in the VPS signal)
00485 
00486 bool cTimer::Expired(void) const
00487 {
00488   return IsSingleEvent() && !Recording() && StopTime() + EXPIRELATENCY <= time(NULL) && (!HasFlags(tfVps) || !event || !event->Vps());
00489 }
00490 
00491 time_t cTimer::StartTime(void) const
00492 {
00493   if (!startTime)
00494      Matches();
00495   return startTime;
00496 }
00497 
00498 time_t cTimer::StopTime(void) const
00499 {
00500   if (!stopTime)
00501      Matches();
00502   return stopTime;
00503 }
00504 
00505 #define EPGLIMITBEFORE   (1 * 3600) // Time in seconds before a timer's start time and
00506 #define EPGLIMITAFTER    (1 * 3600) // after its stop time within which EPG events will be taken into consideration.
00507 
00508 void cTimer::SetEventFromSchedule(const cSchedules *Schedules)
00509 {
00510   cSchedulesLock SchedulesLock;
00511   if (!Schedules) {
00512      lastSetEvent = 0; // forces setting the event, even if the schedule hasn't been modified
00513      if (!(Schedules = cSchedules::Schedules(SchedulesLock)))
00514         return;
00515      }
00516   const cSchedule *Schedule = Schedules->GetSchedule(Channel());
00517   if (Schedule && Schedule->Events()->First()) {
00518      time_t now = time(NULL);
00519      if (!lastSetEvent || Schedule->Modified() >= lastSetEvent) {
00520         lastSetEvent = now;
00521         const cEvent *Event = NULL;
00522         if (HasFlags(tfVps) && Schedule->Events()->First()->Vps()) {
00523            if (event && Recording())
00524               return; // let the recording end first
00525            // VPS timers only match if their start time exactly matches the event's VPS time:
00526            for (const cEvent *e = Schedule->Events()->First(); e; e = Schedule->Events()->Next(e)) {
00527                if (e->StartTime() && e->RunningStatus() != SI::RunningStatusNotRunning) { // skip outdated events
00528                   int overlap = 0;
00529                   Matches(e, &overlap);
00530                   if (overlap > FULLMATCH) {
00531                      Event = e;
00532                      break; // take the first matching event
00533                      }
00534                   }
00535                }
00536            if (!Event && event && (now <= event->EndTime() || Matches(0, true)))
00537               return; // stay with the old event until the timer has completely expired
00538            }
00539         else {
00540            // Normal timers match the event they have the most overlap with:
00541            int Overlap = 0;
00542            // Set up the time frame within which to check events:
00543            Matches(0, true);
00544            time_t TimeFrameBegin = StartTime() - EPGLIMITBEFORE;
00545            time_t TimeFrameEnd   = StopTime()  + EPGLIMITAFTER;
00546            for (const cEvent *e = Schedule->Events()->First(); e; e = Schedule->Events()->Next(e)) {
00547                if (e->EndTime() < TimeFrameBegin)
00548                   continue; // skip events way before the timer starts
00549                if (e->StartTime() > TimeFrameEnd)
00550                   break; // the rest is way after the timer ends
00551                int overlap = 0;
00552                Matches(e, &overlap);
00553                if (overlap && overlap >= Overlap) {
00554                   if (Event && overlap == Overlap && e->Duration() <= Event->Duration())
00555                      continue; // if overlap is the same, we take the longer event
00556                   Overlap = overlap;
00557                   Event = e;
00558                   }
00559                }
00560            }
00561         SetEvent(Event);
00562         }
00563      }
00564 }
00565 
00566 void cTimer::SetEvent(const cEvent *Event)
00567 {
00568   if (event != Event) { //XXX TODO check event data, too???
00569      if (Event)
00570         isyslog("timer %s set to event %s", *ToDescr(), *Event->ToDescr());
00571      else
00572         isyslog("timer %s set to no event", *ToDescr());
00573      event = Event;
00574      }
00575 }
00576 
00577 void cTimer::SetRecording(bool Recording)
00578 {
00579   recording = Recording;
00580   if (recording)
00581      SetFlags(tfRecording);
00582   else
00583      ClrFlags(tfRecording);
00584   isyslog("timer %s %s", *ToDescr(), recording ? "start" : "stop");
00585 }
00586 
00587 void cTimer::SetPending(bool Pending)
00588 {
00589   pending = Pending;
00590 }
00591 
00592 void cTimer::SetInVpsMargin(bool InVpsMargin)
00593 {
00594   if (InVpsMargin && !inVpsMargin)
00595      isyslog("timer %s entered VPS margin", *ToDescr());
00596   inVpsMargin = InVpsMargin;
00597 }
00598 
00599 void cTimer::SetDay(time_t Day)
00600 {
00601   day = Day;
00602 }
00603 
00604 void cTimer::SetWeekDays(int WeekDays)
00605 {
00606   weekdays = WeekDays;
00607 }
00608 
00609 void cTimer::SetStart(int Start)
00610 {
00611   start = Start;
00612 }
00613 
00614 void cTimer::SetStop(int Stop)
00615 {
00616   stop = Stop;
00617 }
00618 
00619 void cTimer::SetPriority(int Priority)
00620 {
00621   priority = Priority;
00622 }
00623 
00624 void cTimer::SetLifetime(int Lifetime)
00625 {
00626   lifetime = Lifetime;
00627 }
00628 
00629 void cTimer::SetAux(const char *Aux)
00630 {
00631   free(aux);
00632   aux = strdup(Aux);
00633 }
00634 
00635 void cTimer::SetDeferred(int Seconds)
00636 {
00637   deferred = time(NULL) + Seconds;
00638   isyslog("timer %s deferred for %d seconds", *ToDescr(), Seconds);
00639 }
00640 
00641 void cTimer::SetFlags(uint Flags)
00642 {
00643   flags |= Flags;
00644 }
00645 
00646 void cTimer::ClrFlags(uint Flags)
00647 {
00648   flags &= ~Flags;
00649 }
00650 
00651 void cTimer::InvFlags(uint Flags)
00652 {
00653   flags ^= Flags;
00654 }
00655 
00656 bool cTimer::HasFlags(uint Flags) const
00657 {
00658   return (flags & Flags) == Flags;
00659 }
00660 
00661 void cTimer::Skip(void)
00662 {
00663   day = IncDay(SetTime(StartTime(), 0), 1);
00664   startTime = 0;
00665   SetEvent(NULL);
00666 }
00667 
00668 void cTimer::OnOff(void)
00669 {
00670   if (IsSingleEvent())
00671      InvFlags(tfActive);
00672   else if (day) {
00673      day = 0;
00674      ClrFlags(tfActive);
00675      }
00676   else if (HasFlags(tfActive))
00677      Skip();
00678   else
00679      SetFlags(tfActive);
00680   SetEvent(NULL);
00681   Matches(); // refresh start and end time
00682 }
00683 
00684 // --- cTimers ---------------------------------------------------------------
00685 
00686 cTimers Timers;
00687 
00688 cTimers::cTimers(void)
00689 {
00690   state = 0;
00691   beingEdited = 0;;
00692   lastSetEvents = 0;
00693   lastDeleteExpired = 0;
00694 }
00695 
00696 cTimer *cTimers::GetTimer(cTimer *Timer)
00697 {
00698   for (cTimer *ti = First(); ti; ti = Next(ti)) {
00699       if (ti->Channel() == Timer->Channel() &&
00700           (ti->WeekDays() && ti->WeekDays() == Timer->WeekDays() || !ti->WeekDays() && ti->Day() == Timer->Day()) &&
00701           ti->Start() == Timer->Start() &&
00702           ti->Stop() == Timer->Stop())
00703          return ti;
00704       }
00705   return NULL;
00706 }
00707 
00708 cTimer *cTimers::GetMatch(time_t t)
00709 {
00710   static int LastPending = -1;
00711   cTimer *t0 = NULL;
00712   for (cTimer *ti = First(); ti; ti = Next(ti)) {
00713       if (!ti->Recording() && ti->Matches(t)) {
00714          if (ti->Pending()) {
00715             if (ti->Index() > LastPending)
00716                LastPending = ti->Index();
00717             else
00718                continue;
00719             }
00720          if (!t0 || ti->Priority() > t0->Priority())
00721             t0 = ti;
00722          }
00723       }
00724   if (!t0)
00725      LastPending = -1;
00726   return t0;
00727 }
00728 
00729 cTimer *cTimers::GetMatch(const cEvent *Event, int *Match)
00730 {
00731   cTimer *t = NULL;
00732   int m = tmNone;
00733   for (cTimer *ti = First(); ti; ti = Next(ti)) {
00734       int tm = ti->Matches(Event);
00735       if (tm > m) {
00736          t = ti;
00737          m = tm;
00738          if (m == tmFull)
00739             break;
00740          }
00741       }
00742   if (Match)
00743      *Match = m;
00744   return t;
00745 }
00746 
00747 cTimer *cTimers::GetNextActiveTimer(void)
00748 {
00749   cTimer *t0 = NULL;
00750   for (cTimer *ti = First(); ti; ti = Next(ti)) {
00751       ti->Matches();
00752       if ((ti->HasFlags(tfActive)) && (!t0 || ti->StopTime() > time(NULL) && ti->Compare(*t0) < 0))
00753          t0 = ti;
00754       }
00755   return t0;
00756 }
00757 
00758 void cTimers::SetModified(void)
00759 {
00760   cStatus::MsgTimerChange(NULL, tcMod);
00761   state++;
00762 }
00763 
00764 void cTimers::Add(cTimer *Timer, cTimer *After)
00765 {
00766   cConfig<cTimer>::Add(Timer, After);
00767   cStatus::MsgTimerChange(Timer, tcAdd);
00768 }
00769 
00770 void cTimers::Ins(cTimer *Timer, cTimer *Before)
00771 {
00772   cConfig<cTimer>::Ins(Timer, Before);
00773   cStatus::MsgTimerChange(Timer, tcAdd);
00774 }
00775 
00776 void cTimers::Del(cTimer *Timer, bool DeleteObject)
00777 {
00778   cStatus::MsgTimerChange(Timer, tcDel);
00779   cConfig<cTimer>::Del(Timer, DeleteObject);
00780 }
00781 
00782 bool cTimers::Modified(int &State)
00783 {
00784   bool Result = state != State;
00785   State = state;
00786   return Result;
00787 }
00788 
00789 void cTimers::SetEvents(void)
00790 {
00791   if (time(NULL) - lastSetEvents < 5)
00792      return;
00793   cSchedulesLock SchedulesLock(false, 100);
00794   const cSchedules *Schedules = cSchedules::Schedules(SchedulesLock);
00795   if (Schedules) {
00796      if (!lastSetEvents || Schedules->Modified() >= lastSetEvents) {
00797         for (cTimer *ti = First(); ti; ti = Next(ti)) {
00798             if (cRemote::HasKeys())
00799                return; // react immediately on user input
00800             ti->SetEventFromSchedule(Schedules);
00801             }
00802         }
00803      }
00804   lastSetEvents = time(NULL);
00805 }
00806 
00807 void cTimers::DeleteExpired(void)
00808 {
00809   if (time(NULL) - lastDeleteExpired < 30)
00810      return;
00811   cTimer *ti = First();
00812   while (ti) {
00813         cTimer *next = Next(ti);
00814         if (ti->Expired()) {
00815            isyslog("deleting timer %s", *ti->ToDescr());
00816            Del(ti);
00817            SetModified();
00818            }
00819         ti = next;
00820         }
00821   lastDeleteExpired = time(NULL);
00822 }