vdr
1.7.27
|
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 }