vdr  2.2.0
timers.c
Go to the documentation of this file.
1 /*
2  * timers.c: Timer handling
3  *
4  * See the main source file 'vdr.c' for copyright information and
5  * how to reach the author.
6  *
7  * $Id: timers.c 3.1 2013/12/28 11:33:08 kls Exp $
8  */
9 
10 #include "timers.h"
11 #include <ctype.h>
12 #include "channels.h"
13 #include "device.h"
14 #include "i18n.h"
15 #include "libsi/si.h"
16 #include "recording.h"
17 #include "remote.h"
18 #include "status.h"
19 
20 // IMPORTANT NOTE: in the 'sscanf()' calls there is a blank after the '%d'
21 // format characters in order to allow any number of blanks after a numeric
22 // value!
23 
24 // --- cTimer ----------------------------------------------------------------
25 
26 cTimer::cTimer(bool Instant, bool Pause, cChannel *Channel)
27 {
28  startTime = stopTime = 0;
29  lastSetEvent = 0;
30  deferred = 0;
31  recording = pending = inVpsMargin = false;
32  flags = tfNone;
33  *file = 0;
34  aux = NULL;
35  event = NULL;
36  if (Instant)
38  channel = Channel ? Channel : Channels.GetByNumber(cDevice::CurrentChannel());
39  time_t t = time(NULL);
40  struct tm tm_r;
41  struct tm *now = localtime_r(&t, &tm_r);
42  day = SetTime(t, 0);
43  weekdays = 0;
44  start = now->tm_hour * 100 + now->tm_min;
45  stop = 0;
46  if (!Setup.InstantRecordTime && channel && (Instant || Pause)) {
47  cSchedulesLock SchedulesLock;
48  if (const cSchedules *Schedules = cSchedules::Schedules(SchedulesLock)) {
49  if (const cSchedule *Schedule = Schedules->GetSchedule(channel)) {
50  if (const cEvent *Event = Schedule->GetPresentEvent()) {
51  time_t tstart = Event->StartTime();
52  time_t tstop = Event->EndTime();
53  if (Event->Vps() && Setup.UseVps) {
54  SetFlags(tfVps);
55  tstart = Event->Vps();
56  }
57  else {
58  tstop += Setup.MarginStop * 60;
59  tstart -= Setup.MarginStart * 60;
60  }
61  day = SetTime(tstart, 0);
62  struct tm *time = localtime_r(&tstart, &tm_r);
63  start = time->tm_hour * 100 + time->tm_min;
64  time = localtime_r(&tstop, &tm_r);
65  stop = time->tm_hour * 100 + time->tm_min;
66  SetEvent(Event);
67  }
68  }
69  }
70  }
71  if (!stop) {
72  stop = now->tm_hour * 60 + now->tm_min + (Setup.InstantRecordTime ? Setup.InstantRecordTime : DEFINSTRECTIME);
73  stop = (stop / 60) * 100 + (stop % 60);
74  }
75  if (stop >= 2400)
76  stop -= 2400;
79  if (Instant && channel)
80  snprintf(file, sizeof(file), "%s%s", Setup.MarkInstantRecord ? "@" : "", *Setup.NameInstantRecord ? Setup.NameInstantRecord : channel->Name());
81 }
82 
84 {
85  startTime = stopTime = 0;
86  lastSetEvent = 0;
87  deferred = 0;
88  recording = pending = inVpsMargin = false;
89  flags = tfActive;
90  *file = 0;
91  aux = NULL;
92  event = NULL;
93  if (Event->Vps() && Setup.UseVps)
94  SetFlags(tfVps);
95  channel = Channels.GetByChannelID(Event->ChannelID(), true);
96  time_t tstart = (flags & tfVps) ? Event->Vps() : Event->StartTime();
97  time_t tstop = tstart + Event->Duration();
98  if (!(HasFlags(tfVps))) {
99  tstop += Setup.MarginStop * 60;
100  tstart -= Setup.MarginStart * 60;
101  }
102  struct tm tm_r;
103  struct tm *time = localtime_r(&tstart, &tm_r);
104  day = SetTime(tstart, 0);
105  weekdays = 0;
106  start = time->tm_hour * 100 + time->tm_min;
107  time = localtime_r(&tstop, &tm_r);
108  stop = time->tm_hour * 100 + time->tm_min;
109  if (stop >= 2400)
110  stop -= 2400;
113  const char *Title = Event->Title();
114  if (!isempty(Title))
115  Utf8Strn0Cpy(file, Event->Title(), sizeof(file));
116  SetEvent(Event);
117 }
118 
119 cTimer::cTimer(const cTimer &Timer)
120 {
121  channel = NULL;
122  aux = NULL;
123  event = NULL;
124  flags = tfNone;
125  *this = Timer;
126 }
127 
129 {
130  free(aux);
131 }
132 
134 {
135  if (&Timer != this) {
136  uint OldFlags = flags & tfRecording;
137  startTime = Timer.startTime;
138  stopTime = Timer.stopTime;
139  lastSetEvent = 0;
140  deferred = 0;
141  recording = Timer.recording;
142  pending = Timer.pending;
143  inVpsMargin = Timer.inVpsMargin;
144  flags = Timer.flags | OldFlags;
145  channel = Timer.channel;
146  day = Timer.day;
147  weekdays = Timer.weekdays;
148  start = Timer.start;
149  stop = Timer.stop;
150  priority = Timer.priority;
151  lifetime = Timer.lifetime;
152  strncpy(file, Timer.file, sizeof(file));
153  free(aux);
154  aux = Timer.aux ? strdup(Timer.aux) : NULL;
155  event = NULL;
156  }
157  return *this;
158 }
159 
160 int cTimer::Compare(const cListObject &ListObject) const
161 {
162  const cTimer *ti = (const cTimer *)&ListObject;
163  time_t t1 = StartTime();
164  time_t t2 = ti->StartTime();
165  int r = t1 - t2;
166  if (r == 0)
167  r = ti->priority - priority;
168  return r;
169 }
170 
171 cString cTimer::ToText(bool UseChannelID) const
172 {
173  strreplace(file, ':', '|');
174  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 : "");
175  strreplace(file, '|', ':');
176  return buffer;
177 }
178 
180 {
181  return cString::sprintf("%d (%d %04d-%04d %s'%s')", Index() + 1, Channel()->Number(), start, stop, HasFlags(tfVps) ? "VPS " : "", file);
182 }
183 
185 {
186  return (t / 100 * 60 + t % 100) * 60;
187 }
188 
189 bool cTimer::ParseDay(const char *s, time_t &Day, int &WeekDays)
190 {
191  // possible formats are:
192  // 19
193  // 2005-03-19
194  // MTWTFSS
195  // MTWTFSS@19
196  // MTWTFSS@2005-03-19
197 
198  Day = 0;
199  WeekDays = 0;
200  s = skipspace(s);
201  if (!*s)
202  return false;
203  const char *a = strchr(s, '@');
204  const char *d = a ? a + 1 : isdigit(*s) ? s : NULL;
205  if (d) {
206  if (strlen(d) == 10) {
207  struct tm tm_r;
208  if (3 == sscanf(d, "%d-%d-%d", &tm_r.tm_year, &tm_r.tm_mon, &tm_r.tm_mday)) {
209  tm_r.tm_year -= 1900;
210  tm_r.tm_mon--;
211  tm_r.tm_hour = tm_r.tm_min = tm_r.tm_sec = 0;
212  tm_r.tm_isdst = -1; // makes sure mktime() will determine the correct DST setting
213  Day = mktime(&tm_r);
214  }
215  else
216  return false;
217  }
218  else {
219  // handle "day of month" for compatibility with older versions:
220  char *tail = NULL;
221  int day = strtol(d, &tail, 10);
222  if (tail && *tail || day < 1 || day > 31)
223  return false;
224  time_t t = time(NULL);
225  int DaysToCheck = 61; // 61 to handle months with 31/30/31
226  for (int i = -1; i <= DaysToCheck; i++) {
227  time_t t0 = IncDay(t, i);
228  if (GetMDay(t0) == day) {
229  Day = SetTime(t0, 0);
230  break;
231  }
232  }
233  }
234  }
235  if (a || !isdigit(*s)) {
236  if ((a && a - s == 7) || strlen(s) == 7) {
237  for (const char *p = s + 6; p >= s; p--) {
238  WeekDays <<= 1;
239  WeekDays |= (*p != '-');
240  }
241  }
242  else
243  return false;
244  }
245  return true;
246 }
247 
248 cString cTimer::PrintDay(time_t Day, int WeekDays, bool SingleByteChars)
249 {
250 #define DAYBUFFERSIZE 64
251  char buffer[DAYBUFFERSIZE];
252  char *b = buffer;
253  if (WeekDays) {
254  // TRANSLATORS: the first character of each weekday, beginning with monday
255  const char *w = trNOOP("MTWTFSS");
256  if (!SingleByteChars)
257  w = tr(w);
258  while (*w) {
259  int sl = Utf8CharLen(w);
260  if (WeekDays & 1) {
261  for (int i = 0; i < sl; i++)
262  b[i] = w[i];
263  b += sl;
264  }
265  else
266  *b++ = '-';
267  WeekDays >>= 1;
268  w += sl;
269  }
270  if (Day)
271  *b++ = '@';
272  }
273  if (Day) {
274  struct tm tm_r;
275  localtime_r(&Day, &tm_r);
276  b += strftime(b, DAYBUFFERSIZE - (b - buffer), "%Y-%m-%d", &tm_r);
277  }
278  *b = 0;
279  return buffer;
280 }
281 
283 {
284  if (weekdays) {
285  cString s = PrintDay(day, weekdays, true);
286  if (strlen(s) == 18)
287  return *s + 8;
288  }
289  return ""; // not NULL, so the caller can always use the result
290 }
291 
292 bool cTimer::Parse(const char *s)
293 {
294  char *channelbuffer = NULL;
295  char *daybuffer = NULL;
296  char *filebuffer = NULL;
297  free(aux);
298  aux = NULL;
299  //XXX Apparently sscanf() doesn't work correctly if the last %m argument
300  //XXX results in an empty string (this first occurred when the EIT gathering
301  //XXX was put into a separate thread - don't know why this happens...
302  //XXX As a cure we copy the original string and add a blank.
303  //XXX If anybody can shed some light on why sscanf() failes here, I'd love
304  //XXX to hear about that!
305  char *s2 = NULL;
306  int l2 = strlen(s);
307  while (l2 > 0 && isspace(s[l2 - 1]))
308  l2--;
309  if (s[l2 - 1] == ':') {
310  s2 = MALLOC(char, l2 + 3);
311  strcat(strn0cpy(s2, s, l2 + 1), " \n");
312  s = s2;
313  }
314  bool result = false;
315  if (8 <= sscanf(s, "%u :%m[^:]:%m[^:]:%d :%d :%d :%d :%m[^:\n]:%m[^\n]", &flags, &channelbuffer, &daybuffer, &start, &stop, &priority, &lifetime, &filebuffer, &aux)) {
317  if (aux && !*skipspace(aux)) {
318  free(aux);
319  aux = NULL;
320  }
321  //TODO add more plausibility checks
322  result = ParseDay(daybuffer, day, weekdays);
323  Utf8Strn0Cpy(file, filebuffer, sizeof(file));
324  strreplace(file, '|', ':');
325  if (isnumber(channelbuffer))
326  channel = Channels.GetByNumber(atoi(channelbuffer));
327  else
328  channel = Channels.GetByChannelID(tChannelID::FromString(channelbuffer), true, true);
329  if (!channel) {
330  esyslog("ERROR: channel %s not defined", channelbuffer);
331  result = false;
332  }
333  }
334  free(channelbuffer);
335  free(daybuffer);
336  free(filebuffer);
337  free(s2);
338  return result;
339 }
340 
341 bool cTimer::Save(FILE *f)
342 {
343  return fprintf(f, "%s", *ToText(true)) > 0;
344 }
345 
346 bool cTimer::IsSingleEvent(void) const
347 {
348  return !weekdays;
349 }
350 
351 int cTimer::GetMDay(time_t t)
352 {
353  struct tm tm_r;
354  return localtime_r(&t, &tm_r)->tm_mday;
355 }
356 
357 int cTimer::GetWDay(time_t t)
358 {
359  struct tm tm_r;
360  int weekday = localtime_r(&t, &tm_r)->tm_wday;
361  return weekday == 0 ? 6 : weekday - 1; // we start with Monday==0!
362 }
363 
364 bool cTimer::DayMatches(time_t t) const
365 {
366  return IsSingleEvent() ? SetTime(t, 0) == day : (weekdays & (1 << GetWDay(t))) != 0;
367 }
368 
369 time_t cTimer::IncDay(time_t t, int Days)
370 {
371  struct tm tm_r;
372  tm tm = *localtime_r(&t, &tm_r);
373  tm.tm_mday += Days; // now tm_mday may be out of its valid range
374  int h = tm.tm_hour; // save original hour to compensate for DST change
375  tm.tm_isdst = -1; // makes sure mktime() will determine the correct DST setting
376  t = mktime(&tm); // normalize all values
377  tm.tm_hour = h; // compensate for DST change
378  return mktime(&tm); // calculate final result
379 }
380 
381 time_t cTimer::SetTime(time_t t, int SecondsFromMidnight)
382 {
383  struct tm tm_r;
384  tm tm = *localtime_r(&t, &tm_r);
385  tm.tm_hour = SecondsFromMidnight / 3600;
386  tm.tm_min = (SecondsFromMidnight % 3600) / 60;
387  tm.tm_sec = SecondsFromMidnight % 60;
388  tm.tm_isdst = -1; // makes sure mktime() will determine the correct DST setting
389  return mktime(&tm);
390 }
391 
392 void cTimer::SetFile(const char *File)
393 {
394  if (!isempty(File))
395  Utf8Strn0Cpy(file, File, sizeof(file));
396 }
397 
398 #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)
399 
400 bool cTimer::Matches(time_t t, bool Directly, int Margin) const
401 {
402  startTime = stopTime = 0;
403  if (t == 0)
404  t = time(NULL);
405 
406  int begin = TimeToInt(start); // seconds from midnight
407  int length = TimeToInt(stop) - begin;
408  if (length < 0)
409  length += SECSINDAY;
410 
411  if (IsSingleEvent()) {
412  startTime = SetTime(day, begin);
413  stopTime = startTime + length;
414  }
415  else {
416  for (int i = -1; i <= 7; i++) {
417  time_t t0 = IncDay(day ? max(day, t) : t, i);
418  if (DayMatches(t0)) {
419  time_t a = SetTime(t0, begin);
420  time_t b = a + length;
421  if ((!day || a >= day) && t < b) {
422  startTime = a;
423  stopTime = b;
424  break;
425  }
426  }
427  }
428  if (!startTime)
429  startTime = IncDay(t, 7); // just to have something that's more than a week in the future
430  else if (!Directly && (t > startTime || t > day + SECSINDAY + 3600)) // +3600 in case of DST change
431  day = 0;
432  }
433 
434  if (t < deferred)
435  return false;
436  deferred = 0;
437 
438  if (HasFlags(tfActive)) {
439  if (HasFlags(tfVps) && event && event->Vps()) {
440  if (Margin || !Directly) {
441  startTime = event->StartTime();
442  stopTime = event->EndTime();
443  if (!Margin) { // this is an actual check
444  if (event->Schedule()->PresentSeenWithin(EITPRESENTFOLLOWINGRATE)) { // VPS control can only work with up-to-date events...
445  if (event->StartTime() > 0) // checks for "phased out" events
446  return event->IsRunning(true);
447  }
448  return startTime <= t && t < stopTime; // ...otherwise we fall back to normal timer handling
449  }
450  }
451  }
452  return startTime <= t + Margin && t < stopTime; // must stop *before* stopTime to allow adjacent timers
453  }
454  return false;
455 }
456 
457 #define FULLMATCH 1000
458 
459 eTimerMatch cTimer::Matches(const cEvent *Event, int *Overlap) const
460 {
461  // Overlap is the percentage of the Event's duration that is covered by
462  // this timer (based on FULLMATCH for finer granularity than just 100).
463  // To make sure a VPS timer can be distinguished from a plain 100% overlap,
464  // it gets an additional 100 added, and a VPS event that is actually running
465  // gets 200 added to the FULLMATCH.
466  if (HasFlags(tfActive) && channel->GetChannelID() == Event->ChannelID()) {
467  bool UseVps = HasFlags(tfVps) && Event->Vps();
468  Matches(UseVps ? Event->Vps() : Event->StartTime(), true);
469  int overlap = 0;
470  if (UseVps)
471  overlap = (startTime == Event->Vps()) ? FULLMATCH + (Event->IsRunning() ? 200 : 100) : 0;
472  if (!overlap) {
473  if (startTime <= Event->StartTime() && Event->EndTime() <= stopTime)
474  overlap = FULLMATCH;
475  else if (stopTime <= Event->StartTime() || Event->EndTime() <= startTime)
476  overlap = 0;
477  else
478  overlap = (min(stopTime, Event->EndTime()) - max(startTime, Event->StartTime())) * FULLMATCH / max(Event->Duration(), 1);
479  }
480  startTime = stopTime = 0;
481  if (Overlap)
482  *Overlap = overlap;
483  if (UseVps)
484  return overlap > FULLMATCH ? tmFull : tmNone;
485  return overlap >= FULLMATCH ? tmFull : overlap > 0 ? tmPartial : tmNone;
486  }
487  return tmNone;
488 }
489 
490 #define EXPIRELATENCY 60 // seconds (just in case there's a short glitch in the VPS signal)
491 
492 bool cTimer::Expired(void) const
493 {
494  return IsSingleEvent() && !Recording() && StopTime() + EXPIRELATENCY <= time(NULL) && (!HasFlags(tfVps) || !event || !event->Vps());
495 }
496 
497 time_t cTimer::StartTime(void) const
498 {
499  if (!startTime)
500  Matches();
501  return startTime;
502 }
503 
504 time_t cTimer::StopTime(void) const
505 {
506  if (!stopTime)
507  Matches();
508  return stopTime;
509 }
510 
511 #define EPGLIMITBEFORE (1 * 3600) // Time in seconds before a timer's start time and
512 #define EPGLIMITAFTER (1 * 3600) // after its stop time within which EPG events will be taken into consideration.
513 
515 {
516  cSchedulesLock SchedulesLock;
517  if (!Schedules) {
518  lastSetEvent = 0; // forces setting the event, even if the schedule hasn't been modified
519  if (!(Schedules = cSchedules::Schedules(SchedulesLock)))
520  return;
521  }
522  const cSchedule *Schedule = Schedules->GetSchedule(Channel());
523  if (Schedule && Schedule->Events()->First()) {
524  time_t now = time(NULL);
525  if (!lastSetEvent || Schedule->Modified() >= lastSetEvent) {
526  lastSetEvent = now;
527  const cEvent *Event = NULL;
528  if (HasFlags(tfVps) && Schedule->Events()->First()->Vps()) {
529  if (event && event->StartTime() > 0) { // checks for "phased out" events
530  if (Recording())
531  return; // let the recording end first
532  if (now <= event->EndTime() || Matches(0, true))
533  return; // stay with the old event until the timer has completely expired
534  }
535  // VPS timers only match if their start time exactly matches the event's VPS time:
536  for (const cEvent *e = Schedule->Events()->First(); e; e = Schedule->Events()->Next(e)) {
537  if (e->StartTime() && e->RunningStatus() != SI::RunningStatusNotRunning) { // skip outdated events
538  int overlap = 0;
539  Matches(e, &overlap);
540  if (overlap > FULLMATCH) {
541  Event = e;
542  break; // take the first matching event
543  }
544  }
545  }
546  }
547  else {
548  // Normal timers match the event they have the most overlap with:
549  int Overlap = 0;
550  // Set up the time frame within which to check events:
551  Matches(0, true);
552  time_t TimeFrameBegin = StartTime() - EPGLIMITBEFORE;
553  time_t TimeFrameEnd = StopTime() + EPGLIMITAFTER;
554  for (const cEvent *e = Schedule->Events()->First(); e; e = Schedule->Events()->Next(e)) {
555  if (e->EndTime() < TimeFrameBegin)
556  continue; // skip events way before the timer starts
557  if (e->StartTime() > TimeFrameEnd)
558  break; // the rest is way after the timer ends
559  int overlap = 0;
560  Matches(e, &overlap);
561  if (overlap && overlap >= Overlap) {
562  if (Event && overlap == Overlap && e->Duration() <= Event->Duration())
563  continue; // if overlap is the same, we take the longer event
564  Overlap = overlap;
565  Event = e;
566  }
567  }
568  }
569  SetEvent(Event);
570  }
571  }
572 }
573 
575 {
576  if (event != Event) { //XXX TODO check event data, too???
577  if (Event)
578  isyslog("timer %s set to event %s", *ToDescr(), *Event->ToDescr());
579  else
580  isyslog("timer %s set to no event", *ToDescr());
581  event = Event;
582  }
583 }
584 
586 {
588  if (recording)
590  else
592  isyslog("timer %s %s", *ToDescr(), recording ? "start" : "stop");
593 }
594 
596 {
597  pending = Pending;
598 }
599 
601 {
602  if (InVpsMargin && !inVpsMargin)
603  isyslog("timer %s entered VPS margin", *ToDescr());
605 }
606 
607 void cTimer::SetDay(time_t Day)
608 {
609  day = Day;
610 }
611 
613 {
614  weekdays = WeekDays;
615 }
616 
618 {
619  start = Start;
620 }
621 
623 {
624  stop = Stop;
625 }
626 
628 {
629  priority = Priority;
630 }
631 
633 {
634  lifetime = Lifetime;
635 }
636 
637 void cTimer::SetAux(const char *Aux)
638 {
639  free(aux);
640  aux = strdup(Aux);
641 }
642 
643 void cTimer::SetDeferred(int Seconds)
644 {
645  deferred = time(NULL) + Seconds;
646  isyslog("timer %s deferred for %d seconds", *ToDescr(), Seconds);
647 }
648 
650 {
651  flags |= Flags;
652 }
653 
655 {
656  flags &= ~Flags;
657 }
658 
660 {
661  flags ^= Flags;
662 }
663 
664 bool cTimer::HasFlags(uint Flags) const
665 {
666  return (flags & Flags) == Flags;
667 }
668 
669 void cTimer::Skip(void)
670 {
671  day = IncDay(SetTime(StartTime(), 0), 1);
672  startTime = 0;
673  SetEvent(NULL);
674 }
675 
676 void cTimer::OnOff(void)
677 {
678  if (IsSingleEvent())
680  else if (day) {
681  day = 0;
683  }
684  else if (HasFlags(tfActive))
685  Skip();
686  else
688  SetEvent(NULL);
689  Matches(); // refresh start and end time
690 }
691 
692 // --- cTimers ---------------------------------------------------------------
693 
695 
697 {
698  state = 0;
699  beingEdited = 0;;
700  lastSetEvents = 0;
701  lastDeleteExpired = 0;
702 }
703 
705 {
706  for (cTimer *ti = First(); ti; ti = Next(ti)) {
707  if (ti->Channel() == Timer->Channel() &&
708  (ti->WeekDays() && ti->WeekDays() == Timer->WeekDays() || !ti->WeekDays() && ti->Day() == Timer->Day()) &&
709  ti->Start() == Timer->Start() &&
710  ti->Stop() == Timer->Stop())
711  return ti;
712  }
713  return NULL;
714 }
715 
717 {
718  static int LastPending = -1;
719  cTimer *t0 = NULL;
720  for (cTimer *ti = First(); ti; ti = Next(ti)) {
721  if (!ti->Recording() && ti->Matches(t)) {
722  if (ti->Pending()) {
723  if (ti->Index() > LastPending) {
724  LastPending = ti->Index();
725  return ti;
726  }
727  else
728  continue;
729  }
730  if (!t0 || ti->Priority() > t0->Priority())
731  t0 = ti;
732  }
733  }
734  if (!t0)
735  LastPending = -1;
736  return t0;
737 }
738 
740 {
741  cTimer *t = NULL;
742  eTimerMatch m = tmNone;
743  for (cTimer *ti = First(); ti; ti = Next(ti)) {
744  eTimerMatch tm = ti->Matches(Event);
745  if (tm > m) {
746  t = ti;
747  m = tm;
748  if (m == tmFull)
749  break;
750  }
751  }
752  if (Match)
753  *Match = m;
754  return t;
755 }
756 
758 {
759  cTimer *t0 = NULL;
760  for (cTimer *ti = First(); ti; ti = Next(ti)) {
761  ti->Matches();
762  if ((ti->HasFlags(tfActive)) && (!t0 || ti->StopTime() > time(NULL) && ti->Compare(*t0) < 0))
763  t0 = ti;
764  }
765  return t0;
766 }
767 
769 {
771  state++;
772 }
773 
774 void cTimers::Add(cTimer *Timer, cTimer *After)
775 {
776  cConfig<cTimer>::Add(Timer, After);
778 }
779 
780 void cTimers::Ins(cTimer *Timer, cTimer *Before)
781 {
782  cConfig<cTimer>::Ins(Timer, Before);
784 }
785 
786 void cTimers::Del(cTimer *Timer, bool DeleteObject)
787 {
789  cConfig<cTimer>::Del(Timer, DeleteObject);
790 }
791 
792 bool cTimers::Modified(int &State)
793 {
794  bool Result = state != State;
795  State = state;
796  return Result;
797 }
798 
800 {
801  if (time(NULL) - lastSetEvents < 5)
802  return;
803  cSchedulesLock SchedulesLock(false, 100);
804  const cSchedules *Schedules = cSchedules::Schedules(SchedulesLock);
805  if (Schedules) {
806  if (!lastSetEvents || Schedules->Modified() >= lastSetEvents) {
807  for (cTimer *ti = First(); ti; ti = Next(ti)) {
808  if (cRemote::HasKeys())
809  return; // react immediately on user input
810  ti->SetEventFromSchedule(Schedules);
811  }
812  }
813  }
814  lastSetEvents = time(NULL);
815 }
816 
818 {
819  if (time(NULL) - lastDeleteExpired < 30)
820  return;
821  cTimer *ti = First();
822  while (ti) {
823  cTimer *next = Next(ti);
824  if (ti->Expired()) {
825  isyslog("deleting timer %s", *ti->ToDescr());
826  Del(ti);
827  SetModified();
828  }
829  ti = next;
830  }
831  lastDeleteExpired = time(NULL);
832 }
833 
834 // --- cSortedTimers ---------------------------------------------------------
835 
836 static int CompareTimers(const void *a, const void *b)
837 {
838  return (*(const cTimer **)a)->Compare(**(const cTimer **)b);
839 }
840 
842 :cVector<const cTimer *>(Timers.Count())
843 {
844  for (const cTimer *Timer = Timers.First(); Timer; Timer = Timers.Next(Timer))
845  Append(Timer);
847 }
cListObject * next
Definition: tools.h:456
cString itoa(int n)
Definition: tools.c:388
void SetEvents(void)
Definition: timers.c:799
time_t stopTime
Definition: timers.h:30
#define EPGLIMITAFTER
Definition: timers.c:512
void SetModified(void)
Definition: timers.c:768
Definition: epg.h:71
void SetWeekDays(int WeekDays)
Definition: timers.c:612
cChannels Channels
Definition: channels.c:864
bool isempty(const char *s)
Definition: tools.c:297
static tChannelID FromString(const char *s)
Definition: channels.c:26
int Index(void) const
Definition: tools.c:1989
bool isnumber(const char *s)
Definition: tools.c:312
void OnOff(void)
Definition: timers.c:676
time_t EndTime(void) const
Definition: epg.h:107
void SetRecording(bool Recording)
Definition: timers.c:585
void SetEvent(const cEvent *Event)
Definition: timers.c:574
char file[NAME_MAX *2+1]
Definition: timers.h:42
time_t Vps(void) const
Definition: epg.h:109
void Add(cListObject *Object, cListObject *After=NULL)
Definition: tools.c:2014
cString ToDescr(void) const
Definition: epg.c:238
int UseVps
Definition: config.h:305
int stop
Definition: timers.h:39
cSortedTimers(void)
Definition: timers.c:841
char * aux
Definition: timers.h:43
int WeekDays(void) const
Definition: timers.h:58
static int TimeToInt(int t)
Definition: timers.c:184
Definition: status.h:18
const cSchedule * Schedule(void) const
Definition: epg.h:95
#define EXPIRELATENCY
Definition: timers.c:490
int DefaultPriority
Definition: config.h:301
Definition: timers.h:18
bool Expired(void) const
Definition: timers.c:492
cTimers Timers
Definition: timers.c:694
void DeleteExpired(void)
Definition: timers.c:817
int start
Definition: timers.h:38
static cString sprintf(const char *fmt,...) __attribute__((format(printf
Definition: tools.c:1080
int PausePriority
Definition: config.h:302
virtual void Append(const cTimer * Data)
Definition: tools.h:571
#define EPGLIMITBEFORE
Definition: timers.c:511
bool DayMatches(time_t t) const
Definition: timers.c:364
cString PrintFirstDay(void) const
Definition: timers.c:282
cTimer * GetMatch(time_t t)
Definition: timers.c:716
const cEvent * Event(void) const
Definition: timers.h:69
#define esyslog(a...)
Definition: tools.h:34
int Duration(void) const
Definition: epg.h:108
void SetStop(int Stop)
Definition: timers.c:622
void Skip(void)
Definition: timers.c:669
char * strn0cpy(char *dest, const char *src, size_t n)
Definition: tools.c:131
bool Matches(time_t t=0, bool Directly=false, int Margin=0) const
Definition: timers.c:400
bool Parse(const char *s)
Definition: timers.c:292
void SetInVpsMargin(bool InVpsMargin)
Definition: timers.c:600
cChannel * channel
Definition: timers.h:35
#define EITPRESENTFOLLOWINGRATE
Definition: timers.c:398
T max(T a, T b)
Definition: tools.h:55
int PauseLifetime
Definition: config.h:302
int MarkInstantRecord
Definition: config.h:268
void SetPending(bool Pending)
Definition: timers.c:595
time_t StartTime(void) const
Definition: timers.c:497
int weekdays
bitmask, lowest bits: SSFTWTM (the &#39;M&#39; is the LSB)
Definition: timers.h:37
Definition: timers.h:25
time_t StartTime(void) const
Definition: epg.h:106
bool inVpsMargin
Definition: timers.h:33
void SetStart(int Start)
Definition: timers.c:617
T min(T a, T b)
Definition: tools.h:54
cTimer * GetNextActiveTimer(void)
Definition: timers.c:757
void Add(cTimer *Timer, cTimer *After=NULL)
Definition: timers.c:774
virtual ~cTimer()
Definition: timers.c:128
const cEvent * event
Definition: timers.h:44
bool PresentSeenWithin(int Seconds) const
Definition: epg.h:157
static const cSchedules * Schedules(cSchedulesLock &SchedulesLock)
Caller must provide a cSchedulesLock which has to survive the entire time the returned cSchedules is ...
Definition: epg.c:1201
#define MALLOC(type, size)
Definition: tools.h:46
const cChannel * Channel(void) const
Definition: timers.h:56
Definition: timers.h:27
eTimerMatch
Definition: timers.h:25
virtual int Compare(const cListObject &ListObject) const
Must return 0 if this object is equal to ListObject, a positive value if it is "greater", and a negative value if it is "smaller".
Definition: timers.c:160
static time_t SetTime(time_t t, int SecondsFromMidnight)
Definition: timers.c:381
int Lifetime(void) const
Definition: timers.h:62
void SetEventFromSchedule(const cSchedules *Schedules=NULL)
Definition: timers.c:514
Definition: tools.h:498
bool Recording(void) const
Definition: timers.h:52
Definition: status.h:18
cTimer * GetTimer(cTimer *Timer)
Definition: timers.c:704
static int CurrentChannel(void)
Returns the number of the current channel on the primary device.
Definition: device.h:323
const char * Name(void) const
Definition: channels.c:123
T * Next(const T *object) const
Definition: tools.h:495
cTimers(void)
Definition: timers.c:696
#define trNOOP(s)
Definition: i18n.h:88
bool Modified(int &State)
Returns true if any of the timers have been modified, which is detected by State being different than...
Definition: timers.c:792
uint Flags(void) const
Definition: timers.h:55
static time_t Modified(void)
Definition: epg.h:204
void Ins(cTimer *Timer, cTimer *Before=NULL)
Definition: timers.c:780
cListObject * Next(void) const
Definition: tools.h:468
#define DEFINSTRECTIME
Definition: config.h:45
tChannelID ChannelID(void) const
Definition: epg.c:147
static int GetMDay(time_t t)
Definition: timers.c:351
int lifetime
Definition: timers.h:41
bool HasFlags(uint Flags) const
Definition: timers.c:664
const cList< cEvent > * Events(void) const
Definition: epg.h:171
bool Pending(void) const
Definition: timers.h:53
void Ins(cListObject *Object, cListObject *Before=NULL)
Definition: tools.c:2030
void SetLifetime(int Lifetime)
Definition: timers.c:632
cSetup Setup
Definition: config.c:373
static int Utf8CharLen(const char *s)
Definition: si.c:417
tChannelID GetChannelID(void) const
Definition: channels.h:208
time_t Modified(void) const
Definition: epg.h:155
static bool HasKeys(void)
Definition: remote.c:175
time_t lastSetEvent
Definition: timers.h:31
void Sort(__compar_fn_t Compare)
Definition: tools.h:608
void SetAux(const char *Aux)
Definition: timers.c:637
cTimer & operator=(const cTimer &Timer)
Definition: timers.c:133
time_t day
midnight of the day this timer shall hit, or of the first day it shall hit in case of a repeating tim...
Definition: timers.h:36
static time_t IncDay(time_t t, int Days)
Definition: timers.c:369
bool InVpsMargin(void) const
Definition: timers.h:54
cChannel * GetByChannelID(tChannelID ChannelID, bool TryWithoutRid=false, bool TryWithoutPolarization=false)
Definition: channels.c:1023
bool recording
Definition: timers.h:33
static int GetWDay(time_t t)
Definition: timers.c:357
int InstantRecordTime
Definition: config.h:270
const char * Title(void) const
Definition: epg.h:100
void SetPriority(int Priority)
Definition: timers.c:627
time_t deferred
Matches(time_t, ...) will return false if the current time is before this value.
Definition: timers.h:32
int Stop(void) const
Definition: timers.h:60
const cSchedule * GetSchedule(tChannelID ChannelID) const
Definition: epg.c:1328
cString ToDescr(void) const
Definition: timers.c:179
uint flags
Definition: timers.h:34
T * First(void) const
Definition: tools.h:492
Definition: timers.h:25
void Del(cListObject *Object, bool DeleteObject=true)
Definition: tools.c:2046
int MarginStop
Definition: config.h:285
time_t StopTime(void) const
Definition: timers.c:504
void InvFlags(uint Flags)
Definition: timers.c:659
cChannel * GetByNumber(int Number, int SkipGap=0)
Definition: channels.c:995
void SetFlags(uint Flags)
Definition: timers.c:649
Definition: epg.h:143
void SetDeferred(int Seconds)
Definition: timers.c:643
bool pending
Definition: timers.h:33
Definition: timers.h:21
#define tr(s)
Definition: i18n.h:85
static void MsgTimerChange(const cTimer *Timer, eTimerChange Change)
Definition: status.c:32
const char * File(void) const
Definition: timers.h:63
time_t startTime
Definition: timers.h:30
bool IsSingleEvent(void) const
Definition: timers.c:346
char * skipspace(const char *s)
Definition: tools.h:200
#define SECSINDAY
Definition: tools.h:41
#define isyslog(a...)
Definition: tools.h:35
cTimer(bool Instant=false, bool Pause=false, cChannel *Channel=NULL)
Definition: timers.c:26
static bool ParseDay(const char *s, time_t &Day, int &WeekDays)
Definition: timers.c:189
bool Save(FILE *f)
Definition: timers.c:341
void ClrFlags(uint Flags)
Definition: timers.c:654
int priority
Definition: timers.h:40
time_t Day(void) const
Definition: timers.h:57
int Priority(void) const
Definition: timers.h:61
char NameInstantRecord[NAME_MAX+1]
Definition: config.h:269
bool IsRunning(bool OrAboutToStart=false) const
Definition: epg.c:255
cString ToText(bool UseChannelID=false) const
Definition: timers.c:171
static int CompareTimers(const void *a, const void *b)
Definition: timers.c:836
char * Utf8Strn0Cpy(char *Dest, const char *Src, int n)
Copies at most n character bytes from Src to Dest, making sure that the resulting copy ends with a co...
Definition: tools.c:845
int MarginStart
Definition: config.h:285
void Del(cTimer *Timer, bool DeleteObject=true)
Definition: timers.c:786
#define DAYBUFFERSIZE
const char * Aux(void) const
Definition: timers.h:65
void SetFile(const char *File)
Definition: timers.c:392
char * strreplace(char *s, char c1, char c2)
Definition: tools.c:139
void SetDay(time_t Day)
Definition: timers.c:607
int Start(void) const
Definition: timers.h:59
#define FULLMATCH
Definition: timers.c:457
Definition: status.h:18
Definition: tools.h:168
int DefaultLifetime
Definition: config.h:301
static cString PrintDay(time_t Day, int WeekDays, bool SingleByteChars)
Definition: timers.c:248