2002-10-20 12:28:55 +02:00
/*
* timers . c : Timer handling
*
* See the main source file ' vdr . c ' for copyright information and
* how to reach the author .
*
2006-01-28 15:10:27 +01:00
* $ Id : timers . c 1.45 2006 / 01 / 28 15 : 09 : 05 kls Exp $
2002-10-20 12:28:55 +02:00
*/
# include "timers.h"
# include <ctype.h>
# include "channels.h"
2005-09-04 14:48:39 +02:00
# include "device.h"
2002-10-20 12:28:55 +02:00
# include "i18n.h"
2004-07-17 12:48:14 +02:00
# include "remote.h"
2002-10-20 12:28:55 +02:00
// IMPORTANT NOTE: in the 'sscanf()' calls there is a blank after the '%d'
// format characters in order to allow any number of blanks after a numeric
// value!
// -- cTimer -----------------------------------------------------------------
2006-01-01 15:44:29 +01:00
cTimer : : cTimer ( bool Instant , bool Pause , cChannel * Channel )
2002-10-20 12:28:55 +02:00
{
startTime = stopTime = 0 ;
2004-02-29 14:21:22 +01:00
recording = pending = inVpsMargin = false ;
flags = tfNone ;
if ( Instant )
SetFlags ( tfActive | tfInstant ) ;
2006-01-01 15:44:29 +01:00
channel = Channel ? Channel : Channels . GetByNumber ( cDevice : : CurrentChannel ( ) ) ;
2002-10-20 12:28:55 +02:00
time_t t = time ( NULL ) ;
struct tm tm_r ;
struct tm * now = localtime_r ( & t , & tm_r ) ;
2005-03-19 15:38:43 +01:00
day = SetTime ( t , 0 ) ;
weekdays = 0 ;
2002-10-20 12:28:55 +02:00
start = now - > tm_hour * 100 + now - > tm_min ;
stop = now - > tm_hour * 60 + now - > tm_min + Setup . InstantRecordTime ;
stop = ( stop / 60 ) * 100 + ( stop % 60 ) ;
if ( stop > = 2400 )
stop - = 2400 ;
2003-05-11 14:10:00 +02:00
priority = Pause ? Setup . PausePriority : Setup . DefaultPriority ;
lifetime = Pause ? Setup . PauseLifetime : Setup . DefaultLifetime ;
2002-10-20 12:28:55 +02:00
* file = 0 ;
summary = NULL ;
2004-02-29 14:21:22 +01:00
event = NULL ;
2002-10-20 12:28:55 +02:00
if ( Instant & & channel )
snprintf ( file , sizeof ( file ) , " %s%s " , Setup . MarkInstantRecord ? " @ " : " " , * Setup . NameInstantRecord ? Setup . NameInstantRecord : channel - > Name ( ) ) ;
}
2003-12-22 13:29:24 +01:00
cTimer : : cTimer ( const cEvent * Event )
2002-10-20 12:28:55 +02:00
{
startTime = stopTime = 0 ;
2004-02-29 14:21:22 +01:00
recording = pending = inVpsMargin = false ;
flags = tfActive ;
if ( Event - > Vps ( ) & & Setup . UseVps )
SetFlags ( tfVps ) ;
2003-12-22 13:29:24 +01:00
channel = Channels . GetByChannelID ( Event - > ChannelID ( ) , true ) ;
2004-02-29 14:21:22 +01:00
time_t tstart = ( flags & tfVps ) ? Event - > Vps ( ) : Event - > StartTime ( ) ;
time_t tstop = tstart + Event - > Duration ( ) ;
if ( ! ( HasFlags ( tfVps ) ) ) {
tstop + = Setup . MarginStop * 60 ;
tstart - = Setup . MarginStart * 60 ;
}
2002-10-20 12:28:55 +02:00
struct tm tm_r ;
struct tm * time = localtime_r ( & tstart , & tm_r ) ;
2005-03-19 15:38:43 +01:00
day = SetTime ( tstart , 0 ) ;
weekdays = 0 ;
2002-10-20 12:28:55 +02:00
start = time - > tm_hour * 100 + time - > tm_min ;
time = localtime_r ( & tstop , & tm_r ) ;
stop = time - > tm_hour * 100 + time - > tm_min ;
if ( stop > = 2400 )
stop - = 2400 ;
priority = Setup . DefaultPriority ;
lifetime = Setup . DefaultLifetime ;
* file = 0 ;
2003-12-22 13:29:24 +01:00
const char * Title = Event - > Title ( ) ;
2002-10-20 12:28:55 +02:00
if ( ! isempty ( Title ) )
2003-12-22 13:29:24 +01:00
strn0cpy ( file , Event - > Title ( ) , sizeof ( file ) ) ;
2002-10-20 12:28:55 +02:00
summary = NULL ;
2004-02-29 14:21:22 +01:00
event = Event ;
2002-10-20 12:28:55 +02:00
}
cTimer : : ~ cTimer ( )
{
free ( summary ) ;
}
cTimer & cTimer : : operator = ( const cTimer & Timer )
{
memcpy ( this , & Timer , sizeof ( * this ) ) ;
if ( summary )
summary = strdup ( summary ) ;
2004-02-29 14:21:22 +01:00
event = NULL ;
2002-10-20 12:28:55 +02:00
return * this ;
}
2004-11-01 10:40:38 +01:00
int cTimer : : Compare ( const cListObject & ListObject ) const
2002-10-20 12:28:55 +02:00
{
cTimer * ti = ( cTimer * ) & ListObject ;
time_t t1 = StartTime ( ) ;
time_t t2 = ti - > StartTime ( ) ;
2004-11-01 10:40:38 +01:00
int r = t1 - t2 ;
if ( r = = 0 )
r = ti - > priority - priority ;
return r ;
2002-10-20 12:28:55 +02:00
}
2004-12-26 12:45:22 +01:00
cString cTimer : : ToText ( bool UseChannelID )
2002-10-20 12:28:55 +02:00
{
2004-12-26 12:45:22 +01:00
char * buffer ;
2002-11-10 15:50:21 +01:00
strreplace ( file , ' : ' , ' | ' ) ;
strreplace ( summary , ' \n ' , ' | ' ) ;
2006-01-06 14:31:57 +01:00
asprintf ( & buffer , " %u:%s:%s:%04d:%04d:%d:%d:%s:%s \n " , flags , UseChannelID ? * Channel ( ) - > GetChannelID ( ) . ToString ( ) : * itoa ( Channel ( ) - > Number ( ) ) , * PrintDay ( day , weekdays ) , start , stop , priority , lifetime , file , summary ? summary : " " ) ;
2002-11-10 15:50:21 +01:00
strreplace ( summary , ' | ' , ' \n ' ) ;
strreplace ( file , ' | ' , ' : ' ) ;
2005-02-06 09:48:08 +01:00
return cString ( buffer , true ) ;
2002-10-20 12:28:55 +02:00
}
2005-03-20 15:15:42 +01:00
cString cTimer : : ToDescr ( void ) const
{
char * buffer ;
asprintf ( & buffer , " %d (%d %04d-%04d '%s') " , Index ( ) + 1 , Channel ( ) - > Number ( ) , start , stop , file ) ;
return cString ( buffer , true ) ;
}
2002-10-20 12:28:55 +02:00
int cTimer : : TimeToInt ( int t )
{
return ( t / 100 * 60 + t % 100 ) * 60 ;
}
2005-03-19 15:38:43 +01:00
bool cTimer : : ParseDay ( const char * s , time_t & Day , int & WeekDays )
{
// possible formats are:
// 19
// 2005-03-19
// MTWTFSS
// MTWTFSS@19
// MTWTFSS@2005-03-19
Day = 0 ;
WeekDays = 0 ;
s = skipspace ( s ) ;
if ( ! * s )
return false ;
const char * a = strchr ( s , ' @ ' ) ;
const char * d = a ? a + 1 : isdigit ( * s ) ? s : NULL ;
if ( d ) {
if ( strlen ( d ) = = 10 ) {
struct tm tm_r ;
if ( 3 = = sscanf ( d , " %d-%d-%d " , & tm_r . tm_year , & tm_r . tm_mon , & tm_r . tm_mday ) ) {
tm_r . tm_year - = 1900 ;
tm_r . tm_mon - - ;
tm_r . tm_hour = tm_r . tm_min = tm_r . tm_sec = 0 ;
tm_r . tm_isdst = - 1 ; // makes sure mktime() will determine the correct DST setting
Day = mktime ( & tm_r ) ;
2002-10-20 12:28:55 +02:00
}
2005-03-19 15:38:43 +01:00
else
return false ;
}
else {
// handle "day of month" for compatibility with older versions:
char * tail = NULL ;
2005-06-11 14:30:26 +02:00
int day = strtol ( d , & tail , 10 ) ;
2005-03-19 15:38:43 +01:00
if ( tail & & * tail | | day < 1 | | day > 31 )
return false ;
time_t t = time ( NULL ) ;
int DaysToCheck = 61 ; // 61 to handle months with 31/30/31
for ( int i = - 1 ; i < = DaysToCheck ; i + + ) {
time_t t0 = IncDay ( t , i ) ;
if ( GetMDay ( t0 ) = = day ) {
Day = SetTime ( t0 , 0 ) ;
break ;
}
}
2002-10-20 12:28:55 +02:00
}
}
2005-03-19 15:38:43 +01:00
if ( a | | ! isdigit ( * s ) ) {
if ( ( a & & a - s = = 7 ) | | strlen ( s ) = = 7 ) {
for ( const char * p = s + 6 ; p > = s ; p - - ) {
WeekDays < < = 1 ;
WeekDays | = ( * p ! = ' - ' ) ;
}
}
else
return false ;
}
return true ;
2002-10-20 12:28:55 +02:00
}
2005-03-19 15:38:43 +01:00
cString cTimer : : PrintDay ( time_t Day , int WeekDays )
2002-10-20 12:28:55 +02:00
{
# define DAYBUFFERSIZE 32
2004-12-26 12:45:22 +01:00
char buffer [ DAYBUFFERSIZE ] ;
2005-03-19 15:38:43 +01:00
char * b = buffer ;
if ( WeekDays ) {
2002-10-20 12:28:55 +02:00
const char * w = tr ( " MTWTFSS " ) ;
while ( * w ) {
2005-03-19 15:38:43 +01:00
* b + + = ( WeekDays & 1 ) ? * w : ' - ' ;
WeekDays > > = 1 ;
2002-10-20 12:28:55 +02:00
w + + ;
}
2005-03-19 15:38:43 +01:00
if ( Day )
* b + + = ' @ ' ;
}
if ( Day ) {
struct tm tm_r ;
localtime_r ( & Day , & tm_r ) ;
b + = strftime ( b , DAYBUFFERSIZE - ( b - buffer ) , " %Y-%m-%d " , & tm_r ) ;
2002-10-20 12:28:55 +02:00
}
2005-03-19 15:38:43 +01:00
* b = 0 ;
2002-10-20 12:28:55 +02:00
return buffer ;
}
2005-03-20 15:15:42 +01:00
cString cTimer : : PrintFirstDay ( void ) const
2002-10-20 12:28:55 +02:00
{
2005-03-19 15:38:43 +01:00
if ( weekdays ) {
cString s = PrintDay ( day , weekdays ) ;
2002-10-20 12:28:55 +02:00
if ( strlen ( s ) = = 18 )
2004-12-26 12:45:22 +01:00
return * s + 8 ;
2002-10-20 12:28:55 +02:00
}
return " " ; // not NULL, so the caller can always use the result
}
bool cTimer : : Parse ( const char * s )
{
2002-11-10 15:50:21 +01:00
char * channelbuffer = NULL ;
char * daybuffer = NULL ;
char * filebuffer = NULL ;
2002-10-20 12:28:55 +02:00
free ( summary ) ;
summary = NULL ;
//XXX Apparently sscanf() doesn't work correctly if the last %a argument
//XXX results in an empty string (this first occured when the EIT gathering
//XXX was put into a separate thread - don't know why this happens...
//XXX As a cure we copy the original string and add a blank.
//XXX If anybody can shed some light on why sscanf() failes here, I'd love
//XXX to hear about that!
char * s2 = NULL ;
int l2 = strlen ( s ) ;
while ( l2 > 0 & & isspace ( s [ l2 - 1 ] ) )
l2 - - ;
if ( s [ l2 - 1 ] = = ' : ' ) {
s2 = MALLOC ( char , l2 + 3 ) ;
strcat ( strn0cpy ( s2 , s , l2 + 1 ) , " \n " ) ;
s = s2 ;
}
2002-11-10 15:50:21 +01:00
bool result = false ;
2006-01-06 14:31:57 +01:00
if ( 8 < = sscanf ( s , " %u :%a[^:]:%a[^:]:%d :%d :%d :%d :%a[^: \n ]:%a[^ \n ] " , & flags , & channelbuffer , & daybuffer , & start , & stop , & priority , & lifetime , & filebuffer , & summary ) ) {
2006-01-27 15:41:41 +01:00
ClrFlags ( tfRecording ) ;
2002-10-20 12:28:55 +02:00
if ( summary & & ! * skipspace ( summary ) ) {
free ( summary ) ;
summary = NULL ;
}
//TODO add more plausibility checks
2005-03-19 15:38:43 +01:00
result = ParseDay ( daybuffer , day , weekdays ) ;
2002-11-10 15:50:21 +01:00
strn0cpy ( file , filebuffer , MaxFileName ) ;
2002-10-20 12:28:55 +02:00
strreplace ( file , ' | ' , ' : ' ) ;
strreplace ( summary , ' | ' , ' \n ' ) ;
2004-01-04 12:30:00 +01:00
if ( isnumber ( channelbuffer ) )
channel = Channels . GetByNumber ( atoi ( channelbuffer ) ) ;
else
2004-02-13 15:50:26 +01:00
channel = Channels . GetByChannelID ( tChannelID : : FromString ( channelbuffer ) , true , true ) ;
2002-10-20 12:28:55 +02:00
if ( ! channel ) {
2002-11-10 15:50:21 +01:00
esyslog ( " ERROR: channel %s not defined " , channelbuffer ) ;
result = false ;
2002-10-20 12:28:55 +02:00
}
}
2002-11-10 15:50:21 +01:00
free ( channelbuffer ) ;
free ( daybuffer ) ;
free ( filebuffer ) ;
2002-10-20 12:28:55 +02:00
free ( s2 ) ;
2002-11-10 15:50:21 +01:00
return result ;
2002-10-20 12:28:55 +02:00
}
bool cTimer : : Save ( FILE * f )
{
2004-12-26 12:45:22 +01:00
return fprintf ( f , " %s " , * ToText ( true ) ) > 0 ;
2002-10-20 12:28:55 +02:00
}
2004-11-01 10:40:38 +01:00
bool cTimer : : IsSingleEvent ( void ) const
2002-10-20 12:28:55 +02:00
{
2005-03-19 15:38:43 +01:00
return ! weekdays ;
2002-10-20 12:28:55 +02:00
}
2004-11-14 16:27:27 +01:00
int cTimer : : GetMDay ( time_t t )
2002-10-20 12:28:55 +02:00
{
struct tm tm_r ;
return localtime_r ( & t , & tm_r ) - > tm_mday ;
}
2004-11-14 16:27:27 +01:00
int cTimer : : GetWDay ( time_t t )
2002-10-20 12:28:55 +02:00
{
struct tm tm_r ;
int weekday = localtime_r ( & t , & tm_r ) - > tm_wday ;
return weekday = = 0 ? 6 : weekday - 1 ; // we start with monday==0!
}
2004-11-01 10:40:38 +01:00
bool cTimer : : DayMatches ( time_t t ) const
2002-10-20 12:28:55 +02:00
{
2005-03-19 15:38:43 +01:00
return IsSingleEvent ( ) ? SetTime ( t , 0 ) = = day : ( weekdays & ( 1 < < GetWDay ( t ) ) ) ! = 0 ;
2002-10-20 12:28:55 +02:00
}
time_t cTimer : : IncDay ( time_t t , int Days )
{
struct tm tm_r ;
tm tm = * localtime_r ( & t , & tm_r ) ;
tm . tm_mday + = Days ; // now tm_mday may be out of its valid range
int h = tm . tm_hour ; // save original hour to compensate for DST change
tm . tm_isdst = - 1 ; // makes sure mktime() will determine the correct DST setting
t = mktime ( & tm ) ; // normalize all values
tm . tm_hour = h ; // compensate for DST change
return mktime ( & tm ) ; // calculate final result
}
time_t cTimer : : SetTime ( time_t t , int SecondsFromMidnight )
{
struct tm tm_r ;
tm tm = * localtime_r ( & t , & tm_r ) ;
tm . tm_hour = SecondsFromMidnight / 3600 ;
tm . tm_min = ( SecondsFromMidnight % 3600 ) / 60 ;
tm . tm_sec = SecondsFromMidnight % 60 ;
tm . tm_isdst = - 1 ; // makes sure mktime() will determine the correct DST setting
return mktime ( & tm ) ;
}
char * cTimer : : SetFile ( const char * File )
{
if ( ! isempty ( File ) )
strn0cpy ( file , File , sizeof ( file ) ) ;
return file ;
}
2004-11-01 10:40:38 +01:00
bool cTimer : : Matches ( time_t t , bool Directly ) const
2002-10-20 12:28:55 +02:00
{
startTime = stopTime = 0 ;
if ( t = = 0 )
t = time ( NULL ) ;
int begin = TimeToInt ( start ) ; // seconds from midnight
int length = TimeToInt ( stop ) - begin ;
if ( length < 0 )
length + = SECSINDAY ;
2005-03-19 15:38:43 +01:00
if ( IsSingleEvent ( ) ) {
startTime = SetTime ( day , begin ) ;
stopTime = startTime + length ;
}
else {
for ( int i = - 1 ; i < = 7 ; i + + ) {
time_t t0 = IncDay ( t , i ) ;
if ( DayMatches ( t0 ) ) {
time_t a = SetTime ( t0 , begin ) ;
time_t b = a + length ;
if ( ( ! day | | a > = day ) & & t < = b ) {
startTime = a ;
stopTime = b ;
break ;
}
2002-10-20 12:28:55 +02:00
}
}
2005-03-19 15:38:43 +01:00
if ( ! startTime )
startTime = day ; // just to have something that's more than a week in the future
else if ( ! Directly & & ( t > startTime | | t > day + SECSINDAY + 3600 ) ) // +3600 in case of DST change
day = 0 ;
}
2004-02-29 14:21:22 +01:00
if ( HasFlags ( tfActive ) ) {
2005-12-27 14:39:14 +01:00
if ( HasFlags ( tfVps ) & & ! Directly & & event & & event - > Vps ( ) & & event - > Schedule ( ) & & event - > Schedule ( ) - > PresentSeenWithin ( 30 ) ) {
2004-02-29 14:21:22 +01:00
startTime = event - > StartTime ( ) ;
2004-03-06 11:27:08 +01:00
stopTime = event - > EndTime ( ) ;
return event - > IsRunning ( true ) ;
2004-02-29 14:21:22 +01:00
}
return startTime < = t & & t < stopTime ; // must stop *before* stopTime to allow adjacent timers
}
return false ;
}
2005-03-13 11:42:59 +01:00
# define FULLMATCH 1000
2005-03-20 15:15:42 +01:00
int cTimer : : Matches ( const cEvent * Event , int * Overlap ) const
2004-02-29 14:21:22 +01:00
{
2005-03-20 13:13:02 +01:00
// Overlap is the percentage of the Event's duration that is covered by
// this timer (based on FULLMATCH for finer granularity than just 100).
// To make sure a VPS timer can be distinguished from a plain 100% overlap,
// it gets an additional 100 added, and a VPS event that is actually running
// gets 200 added to the FULLMATCH.
2005-03-13 11:42:59 +01:00
if ( HasFlags ( tfActive ) & & channel - > GetChannelID ( ) = = Event - > ChannelID ( ) ) {
2004-02-29 14:21:22 +01:00
bool UseVps = HasFlags ( tfVps ) & & Event - > Vps ( ) ;
2005-03-13 11:42:59 +01:00
Matches ( UseVps ? Event - > Vps ( ) : Event - > StartTime ( ) , true ) ;
2005-06-18 13:32:03 +02:00
int overlap = 0 ;
2005-03-13 11:42:59 +01:00
if ( UseVps )
overlap = ( startTime = = Event - > Vps ( ) ) ? FULLMATCH + ( Event - > IsRunning ( ) ? 200 : 100 ) : 0 ;
2005-06-18 13:32:03 +02:00
if ( ! overlap ) {
if ( startTime < = Event - > StartTime ( ) & & Event - > EndTime ( ) < = stopTime )
overlap = FULLMATCH ;
else if ( stopTime < = Event - > StartTime ( ) | | Event - > EndTime ( ) < = startTime )
overlap = 0 ;
else
overlap = ( min ( stopTime , Event - > EndTime ( ) ) - max ( startTime , Event - > StartTime ( ) ) ) * FULLMATCH / max ( Event - > Duration ( ) , 1 ) ;
}
2004-02-29 14:21:22 +01:00
startTime = stopTime = 0 ;
2005-03-13 11:42:59 +01:00
if ( Overlap )
* Overlap = overlap ;
return overlap > = 1000 ? tmFull : overlap > 0 ? tmPartial : tmNone ;
2004-02-29 14:21:22 +01:00
}
return tmNone ;
2002-10-20 12:28:55 +02:00
}
2005-03-20 11:19:36 +01:00
# define EXPIRELATENCY 60 // seconds (just in case there's a short glitch in the VPS signal)
2005-03-20 15:15:42 +01:00
bool cTimer : : Expired ( void ) const
2005-03-20 11:19:36 +01:00
{
return IsSingleEvent ( ) & & ! Recording ( ) & & StopTime ( ) + EXPIRELATENCY < = time ( NULL ) ;
}
2004-11-01 10:40:38 +01:00
time_t cTimer : : StartTime ( void ) const
2002-10-20 12:28:55 +02:00
{
if ( ! startTime )
Matches ( ) ;
return startTime ;
}
2004-11-01 10:40:38 +01:00
time_t cTimer : : StopTime ( void ) const
2002-10-20 12:28:55 +02:00
{
if ( ! stopTime )
Matches ( ) ;
return stopTime ;
}
2005-12-27 14:39:14 +01:00
void cTimer : : SetEvent ( const cEvent * Event )
2004-02-29 14:21:22 +01:00
{
if ( event ! = Event ) { //XXX TODO check event data, too???
if ( Event ) {
char vpsbuf [ 64 ] = " " ;
if ( Event - > Vps ( ) )
2004-12-26 12:45:22 +01:00
sprintf ( vpsbuf , " (VPS: %s) " , * Event - > GetVpsString ( ) ) ;
2005-03-20 15:15:42 +01:00
isyslog ( " timer %s set to event %s %s-%s %s'%s' " , * ToDescr ( ) , * Event - > GetDateString ( ) , * Event - > GetTimeString ( ) , * Event - > GetEndTimeString ( ) , vpsbuf , Event - > Title ( ) ) ;
2004-02-29 14:21:22 +01:00
}
2004-03-06 11:27:08 +01:00
else
2005-03-20 15:15:42 +01:00
isyslog ( " timer %s set to no event " , * ToDescr ( ) ) ;
2004-02-29 14:21:22 +01:00
event = Event ;
}
}
2002-10-20 12:28:55 +02:00
void cTimer : : SetRecording ( bool Recording )
{
recording = Recording ;
2005-05-07 11:10:56 +02:00
if ( recording )
SetFlags ( tfRecording ) ;
else
ClrFlags ( tfRecording ) ;
2005-03-20 15:15:42 +01:00
isyslog ( " timer %s %s " , * ToDescr ( ) , recording ? " start " : " stop " ) ;
2002-10-20 12:28:55 +02:00
}
void cTimer : : SetPending ( bool Pending )
{
pending = Pending ;
}
2004-02-29 14:21:22 +01:00
void cTimer : : SetInVpsMargin ( bool InVpsMargin )
{
if ( InVpsMargin & & ! inVpsMargin )
2005-03-20 15:15:42 +01:00
isyslog ( " timer %s entered VPS margin " , * ToDescr ( ) ) ;
2004-02-29 14:21:22 +01:00
inVpsMargin = InVpsMargin ;
}
2006-01-03 11:46:57 +01:00
void cTimer : : SetPriority ( int Priority )
{
2006-01-08 11:44:37 +01:00
priority = Priority ;
2006-01-03 11:46:57 +01:00
}
2006-01-06 14:31:57 +01:00
void cTimer : : SetFlags ( uint Flags )
2004-02-29 14:21:22 +01:00
{
flags | = Flags ;
}
2006-01-06 14:31:57 +01:00
void cTimer : : ClrFlags ( uint Flags )
2002-10-20 12:28:55 +02:00
{
2004-02-29 14:21:22 +01:00
flags & = ~ Flags ;
}
2006-01-06 14:31:57 +01:00
void cTimer : : InvFlags ( uint Flags )
2004-02-29 14:21:22 +01:00
{
flags ^ = Flags ;
}
2006-01-06 14:31:57 +01:00
bool cTimer : : HasFlags ( uint Flags ) const
2004-02-29 14:21:22 +01:00
{
return ( flags & Flags ) = = Flags ;
2002-10-20 12:28:55 +02:00
}
void cTimer : : Skip ( void )
{
2005-03-19 15:38:43 +01:00
day = IncDay ( SetTime ( StartTime ( ) , 0 ) , 1 ) ;
2004-02-29 14:21:22 +01:00
event = NULL ;
2002-10-20 12:28:55 +02:00
}
void cTimer : : OnOff ( void )
{
if ( IsSingleEvent ( ) )
2004-02-29 14:21:22 +01:00
InvFlags ( tfActive ) ;
2005-03-19 15:38:43 +01:00
else if ( day ) {
day = 0 ;
2004-02-29 14:21:22 +01:00
ClrFlags ( tfActive ) ;
2002-10-20 12:28:55 +02:00
}
2004-02-29 14:21:22 +01:00
else if ( HasFlags ( tfActive ) )
2002-10-20 12:28:55 +02:00
Skip ( ) ;
else
2004-02-29 14:21:22 +01:00
SetFlags ( tfActive ) ;
event = NULL ;
2002-10-20 12:28:55 +02:00
Matches ( ) ; // refresh start and end time
}
// -- cTimers ----------------------------------------------------------------
cTimers Timers ;
2004-10-24 15:01:50 +02:00
cTimers : : cTimers ( void )
{
2006-01-15 13:44:55 +01:00
state = 0 ;
2004-10-24 15:01:50 +02:00
beingEdited = 0 ; ;
lastSetEvents = 0 ;
}
2002-10-20 12:28:55 +02:00
cTimer * cTimers : : GetTimer ( cTimer * Timer )
{
for ( cTimer * ti = First ( ) ; ti ; ti = Next ( ti ) ) {
2005-09-09 15:28:26 +02:00
if ( ti - > Channel ( ) = = Timer - > Channel ( ) & &
( ti - > WeekDays ( ) & & ti - > WeekDays ( ) = = Timer - > WeekDays ( ) | | ! ti - > WeekDays ( ) & & ti - > Day ( ) = = Timer - > Day ( ) ) & &
ti - > Start ( ) = = Timer - > Start ( ) & &
ti - > Stop ( ) = = Timer - > Stop ( ) )
2002-10-20 12:28:55 +02:00
return ti ;
}
return NULL ;
}
cTimer * cTimers : : GetMatch ( time_t t )
{
cTimer * t0 = NULL ;
for ( cTimer * ti = First ( ) ; ti ; ti = Next ( ti ) ) {
if ( ! ti - > Recording ( ) & & ti - > Matches ( t ) ) {
if ( ! t0 | | ti - > Priority ( ) > t0 - > Priority ( ) )
t0 = ti ;
}
}
return t0 ;
}
2004-02-29 14:21:22 +01:00
cTimer * cTimers : : GetMatch ( const cEvent * Event , int * Match )
{
cTimer * t = NULL ;
int m = tmNone ;
for ( cTimer * ti = First ( ) ; ti ; ti = Next ( ti ) ) {
int tm = ti - > Matches ( Event ) ;
if ( tm > m ) {
t = ti ;
m = tm ;
if ( m = = tmFull )
break ;
}
}
if ( Match )
* Match = m ;
return t ;
}
2002-10-20 12:28:55 +02:00
cTimer * cTimers : : GetNextActiveTimer ( void )
{
cTimer * t0 = NULL ;
for ( cTimer * ti = First ( ) ; ti ; ti = Next ( ti ) ) {
2006-01-28 15:10:27 +01:00
if ( ( ti - > HasFlags ( tfActive ) ) & & ( ! t0 | | ti - > StopTime ( ) > time ( NULL ) & & ti - > Compare ( * t0 ) < 0 ) )
2002-10-20 12:28:55 +02:00
t0 = ti ;
}
return t0 ;
}
2004-02-29 14:21:22 +01:00
2004-10-31 10:22:32 +01:00
void cTimers : : SetModified ( void )
{
2006-01-15 13:44:55 +01:00
state + + ;
2004-10-31 10:22:32 +01:00
}
2006-01-15 13:44:55 +01:00
bool cTimers : : Modified ( int & State )
2004-10-31 10:22:32 +01:00
{
2006-01-15 13:44:55 +01:00
bool Result = state ! = State ;
State = state ;
2004-10-31 10:22:32 +01:00
return Result ;
}
2005-03-20 13:13:02 +01:00
# define EPGLIMITPAST (2 * 3600) // time in seconds around now, within which EPG events will be taken into consideration
# define EPGLIMITFUTURE (4 * 3600)
2005-03-13 11:42:59 +01:00
2004-02-29 14:21:22 +01:00
void cTimers : : SetEvents ( void )
{
2004-10-24 15:01:50 +02:00
if ( time ( NULL ) - lastSetEvents < 5 )
return ;
2004-03-14 13:27:57 +01:00
cSchedulesLock SchedulesLock ( false , 100 ) ;
2004-02-29 14:21:22 +01:00
const cSchedules * Schedules = cSchedules : : Schedules ( SchedulesLock ) ;
if ( Schedules ) {
2004-10-24 15:01:50 +02:00
if ( ! lastSetEvents | | Schedules - > Modified ( ) > = lastSetEvents ) {
for ( cTimer * ti = First ( ) ; ti ; ti = Next ( ti ) ) {
2006-01-14 15:52:40 +01:00
const cSchedule * Schedule = Schedules - > GetSchedule ( ti - > Channel ( ) ) ;
2004-10-24 15:01:50 +02:00
if ( Schedule ) {
if ( ! lastSetEvents | | Schedule - > Modified ( ) > = lastSetEvents ) {
const cEvent * Event = NULL ;
2005-03-13 11:42:59 +01:00
int Overlap = 0 ;
int Distance = INT_MIN ;
time_t now = time ( NULL ) ;
2004-10-24 15:01:50 +02:00
for ( const cEvent * e = Schedule - > Events ( ) - > First ( ) ; e ; e = Schedule - > Events ( ) - > Next ( e ) ) {
if ( cRemote : : HasKeys ( ) )
return ; // react immediately on user input
2005-03-20 13:13:02 +01:00
if ( e - > EndTime ( ) < now - EPGLIMITPAST )
2005-03-13 11:42:59 +01:00
continue ; // skip old events
2005-03-20 13:13:02 +01:00
if ( e - > StartTime ( ) > now + EPGLIMITFUTURE )
2005-03-13 11:42:59 +01:00
break ; // no need to process events too far in the future
int overlap = 0 ;
ti - > Matches ( e , & overlap ) ;
if ( overlap & & overlap > = Overlap ) {
int distance = 0 ;
if ( now < e - > StartTime ( ) )
distance = e - > StartTime ( ) - now ;
else if ( now > e - > EndTime ( ) )
distance = e - > EndTime ( ) - now ;
if ( Event & & overlap = = Overlap ) {
if ( Overlap > FULLMATCH ) { // this means VPS
2006-01-08 11:44:37 +01:00
if ( abs ( Distance ) < abs ( distance ) )
2005-03-13 11:42:59 +01:00
break ; // we've already found the closest VPS event
}
else if ( e - > Duration ( ) < = Event - > Duration ( ) )
continue ; // if overlap is the same, we take the longer event
}
Overlap = overlap ;
Distance = distance ;
2004-10-24 15:01:50 +02:00
Event = e ;
}
}
2005-03-20 14:18:04 +01:00
if ( Event & & Event - > EndTime ( ) < now - EXPIRELATENCY & & ! Event - > IsRunning ( ) )
Event = NULL ;
2005-12-27 14:39:14 +01:00
ti - > SetEvent ( Event ) ;
2004-10-24 15:01:50 +02:00
}
}
2004-02-29 14:21:22 +01:00
}
2004-10-24 15:01:50 +02:00
}
2004-02-29 14:21:22 +01:00
}
2004-10-24 15:01:50 +02:00
lastSetEvents = time ( NULL ) ;
2004-02-29 14:21:22 +01:00
}
2005-03-20 11:19:36 +01:00
void cTimers : : DeleteExpired ( void )
{
cTimer * ti = First ( ) ;
while ( ti ) {
cTimer * next = Next ( ti ) ;
if ( ti - > Expired ( ) ) {
2005-03-20 15:15:42 +01:00
isyslog ( " deleting timer %s " , * ti - > ToDescr ( ) ) ;
2005-03-20 11:19:36 +01:00
Del ( ti ) ;
SetModified ( ) ;
}
ti = next ;
}
}