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 .
*
2012-06-09 14:40:26 +02:00
* $ Id : timers . c 2.11 2012 / 06 / 09 14 : 37 : 24 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"
2006-03-26 14:38:46 +02:00
# include "libsi/si.h"
2008-02-15 15:46:07 +01:00
# include "recording.h"
2004-07-17 12:48:14 +02:00
# include "remote.h"
2008-02-16 15:04:49 +01:00
# include "status.h"
2002-10-20 12:28:55 +02:00
2008-02-15 15:46:07 +01:00
# define VFAT_MAX_FILENAME 40 // same as MAX_SUBTITLE_LENGTH in recording.c
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!
2008-02-10 14:11:47 +01:00
// --- cTimer ----------------------------------------------------------------
2002-10-20 12:28:55 +02:00
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 ;
2006-02-25 15:57:56 +01:00
lastSetEvent = 0 ;
2011-08-06 13:20:07 +02:00
deferred = 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 ;
2006-02-25 12:09:22 +01:00
aux = 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 ( ) ) ;
2009-08-09 12:45:36 +02:00
if ( VfatFileSystem & & ( Utf8StrLen ( file ) > VFAT_MAX_FILENAME ) ) {
dsyslog ( " timer file name too long for VFAT file system: '%s' " , file ) ;
file [ Utf8SymChars ( file , VFAT_MAX_FILENAME ) ] = 0 ;
dsyslog ( " timer file name truncated to '%s' " , file ) ;
}
2002-10-20 12:28:55 +02:00
}
2003-12-22 13:29:24 +01:00
cTimer : : cTimer ( const cEvent * Event )
2002-10-20 12:28:55 +02:00
{
startTime = stopTime = 0 ;
2006-02-26 10:52:51 +01:00
lastSetEvent = 0 ;
2011-08-06 13:20:07 +02:00
deferred = 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 ) )
2007-06-16 10:41:21 +02:00
Utf8Strn0Cpy ( file , Event - > Title ( ) , sizeof ( file ) ) ;
2009-08-09 12:45:36 +02:00
if ( VfatFileSystem & & ( Utf8StrLen ( file ) > VFAT_MAX_FILENAME ) ) {
dsyslog ( " timer file name too long for VFAT file system: '%s' " , file ) ;
file [ Utf8SymChars ( file , VFAT_MAX_FILENAME ) ] = 0 ;
dsyslog ( " timer file name truncated to '%s' " , file ) ;
}
2006-02-25 12:09:22 +01:00
aux = NULL ;
2006-02-26 10:52:51 +01:00
event = NULL ; // let SetEvent() be called to get a log message
2002-10-20 12:28:55 +02:00
}
2006-09-08 15:06:09 +02:00
cTimer : : cTimer ( const cTimer & Timer )
{
channel = NULL ;
aux = NULL ;
event = NULL ;
2008-04-13 12:45:15 +02:00
flags = tfNone ;
2006-09-08 15:06:09 +02:00
* this = Timer ;
}
2002-10-20 12:28:55 +02:00
cTimer : : ~ cTimer ( )
{
2006-02-25 12:09:22 +01:00
free ( aux ) ;
2002-10-20 12:28:55 +02:00
}
cTimer & cTimer : : operator = ( const cTimer & Timer )
{
2006-09-08 15:06:09 +02:00
if ( & Timer ! = this ) {
2008-02-10 12:25:52 +01:00
uint OldFlags = flags & tfRecording ;
2006-09-08 15:06:09 +02:00
startTime = Timer . startTime ;
stopTime = Timer . stopTime ;
lastSetEvent = 0 ;
2011-08-06 13:20:07 +02:00
deferred = 0 ;
2006-09-08 15:06:09 +02:00
recording = Timer . recording ;
pending = Timer . pending ;
inVpsMargin = Timer . inVpsMargin ;
2008-02-10 12:25:52 +01:00
flags = Timer . flags | OldFlags ;
2006-09-08 15:06:09 +02:00
channel = Timer . channel ;
day = Timer . day ;
weekdays = Timer . weekdays ;
start = Timer . start ;
stop = Timer . stop ;
priority = Timer . priority ;
lifetime = Timer . lifetime ;
strncpy ( file , Timer . file , sizeof ( file ) ) ;
free ( aux ) ;
aux = Timer . aux ? strdup ( Timer . aux ) : NULL ;
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
{
2012-06-02 12:10:36 +02:00
const cTimer * ti = ( const cTimer * ) & ListObject ;
2002-10-20 12:28:55 +02:00
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
}
2008-02-16 15:04:49 +01:00
cString cTimer : : ToText ( bool UseChannelID ) const
2002-10-20 12:28:55 +02:00
{
2002-11-10 15:50:21 +01:00
strreplace ( file , ' : ' , ' | ' ) ;
2008-02-15 14:57:48 +01:00
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 : " " ) ;
2002-11-10 15:50:21 +01:00
strreplace ( file , ' | ' , ' : ' ) ;
2008-02-15 14:57:48 +01:00
return buffer ;
2002-10-20 12:28:55 +02:00
}
2005-03-20 15:15:42 +01:00
cString cTimer : : ToDescr ( void ) const
{
2008-02-15 14:57:48 +01:00
return cString : : sprintf ( " %d (%d %04d-%04d %s'%s') " , Index ( ) + 1 , Channel ( ) - > Number ( ) , start , stop , HasFlags ( tfVps ) ? " VPS " : " " , file ) ;
2005-03-20 15:15:42 +01:00
}
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
}
2007-06-10 13:02:43 +02:00
cString cTimer : : PrintDay ( time_t Day , int WeekDays , bool SingleByteChars )
2002-10-20 12:28:55 +02:00
{
2007-06-10 13:02:43 +02:00
# define DAYBUFFERSIZE 64
2004-12-26 12:45:22 +01:00
char buffer [ DAYBUFFERSIZE ] ;
2005-03-19 15:38:43 +01:00
char * b = buffer ;
if ( WeekDays ) {
2007-08-11 12:39:06 +02:00
// TRANSLATORS: the first character of each weekday, beginning with monday
const char * w = trNOOP ( " MTWTFSS " ) ;
2007-06-10 13:02:43 +02:00
if ( ! SingleByteChars )
w = tr ( w ) ;
2002-10-20 12:28:55 +02:00
while ( * w ) {
2007-06-10 13:02:43 +02:00
int sl = Utf8CharLen ( w ) ;
if ( WeekDays & 1 ) {
for ( int i = 0 ; i < sl ; i + + )
b [ i ] = w [ i ] ;
b + = sl ;
}
else
* b + + = ' - ' ;
2005-03-19 15:38:43 +01:00
WeekDays > > = 1 ;
2007-06-10 13:02:43 +02:00
w + = sl ;
2002-10-20 12:28:55 +02:00
}
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 ) {
2007-06-10 13:02:43 +02:00
cString s = PrintDay ( day , weekdays , true ) ;
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 ;
2006-02-25 12:09:22 +01:00
free ( aux ) ;
aux = NULL ;
2002-10-20 12:28:55 +02:00
//XXX Apparently sscanf() doesn't work correctly if the last %a argument
2012-06-09 14:40:26 +02:00
//XXX results in an empty string (this first occurred when the EIT gathering
2002-10-20 12:28:55 +02:00
//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-02-25 12:09:22 +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 , & aux ) ) {
2006-01-27 15:41:41 +01:00
ClrFlags ( tfRecording ) ;
2006-02-25 12:09:22 +01:00
if ( aux & & ! * skipspace ( aux ) ) {
free ( aux ) ;
aux = NULL ;
2002-10-20 12:28:55 +02:00
}
//TODO add more plausibility checks
2005-03-19 15:38:43 +01:00
result = ParseDay ( daybuffer , day , weekdays ) ;
2008-02-15 15:46:07 +01:00
if ( VfatFileSystem ) {
2010-01-17 12:08:03 +01:00
char * p = strrchr ( filebuffer , FOLDERDELIMCHAR ) ;
2008-02-15 15:46:07 +01:00
if ( p )
p + + ;
else
p = filebuffer ;
2009-08-09 12:45:36 +02:00
if ( Utf8StrLen ( p ) > VFAT_MAX_FILENAME ) {
2008-02-15 15:46:07 +01:00
dsyslog ( " timer file name too long for VFAT file system: '%s' " , p ) ;
2009-08-09 12:45:36 +02:00
p [ Utf8SymChars ( p , VFAT_MAX_FILENAME ) ] = 0 ;
2008-02-15 15:46:07 +01:00
dsyslog ( " timer file name truncated to '%s' " , p ) ;
}
}
2009-08-09 12:45:36 +02:00
Utf8Strn0Cpy ( file , filebuffer , sizeof ( file ) ) ;
2002-10-20 12:28:55 +02:00
strreplace ( file , ' | ' , ' : ' ) ;
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 ;
2006-04-21 15:15:18 +02:00
return weekday = = 0 ? 6 : weekday - 1 ; // we start with Monday==0!
2002-10-20 12:28:55 +02:00
}
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 ) ;
}
2012-02-20 15:53:33 +01:00
void cTimer : : SetFile ( const char * File )
2002-10-20 12:28:55 +02:00
{
if ( ! isempty ( File ) )
2007-06-16 10:41:21 +02:00
Utf8Strn0Cpy ( file , File , sizeof ( file ) ) ;
2002-10-20 12:28:55 +02:00
}
2012-02-27 10:21:28 +01:00
# 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)
2006-04-09 09:12:47 +02:00
bool cTimer : : Matches ( time_t t , bool Directly , int Margin ) 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 + + ) {
2006-05-25 12:44:07 +02:00
time_t t0 = IncDay ( day ? max ( day , t ) : t , i ) ;
2005-03-19 15:38:43 +01:00
if ( DayMatches ( t0 ) ) {
time_t a = SetTime ( t0 , begin ) ;
time_t b = a + length ;
2006-08-05 12:06:11 +02:00
if ( ( ! day | | a > = day ) & & t < b ) {
2005-03-19 15:38:43 +01:00
startTime = a ;
stopTime = b ;
break ;
}
2002-10-20 12:28:55 +02:00
}
}
2005-03-19 15:38:43 +01:00
if ( ! startTime )
2006-05-25 12:44:07 +02:00
startTime = IncDay ( t , 7 ) ; // just to have something that's more than a week in the future
2005-03-19 15:38:43 +01:00
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
2011-08-06 13:20:07 +02:00
if ( t < deferred )
return false ;
deferred = 0 ;
2004-02-29 14:21:22 +01:00
if ( HasFlags ( tfActive ) ) {
2006-05-07 09:01:59 +02:00
if ( HasFlags ( tfVps ) & & event & & event - > Vps ( ) ) {
if ( Margin | | ! Directly ) {
startTime = event - > StartTime ( ) ;
stopTime = event - > EndTime ( ) ;
2012-02-27 10:21:28 +01:00
if ( ! Margin ) { // this is an actual check
if ( event - > Schedule ( ) - > PresentSeenWithin ( EITPRESENTFOLLOWINGRATE ) ) // VPS control can only work with up-to-date events...
return event - > IsRunning ( true ) ;
else
return startTime < = t & & t < stopTime ; // ...otherwise we fall back to normal timer handling
}
2006-05-07 09:01:59 +02:00
}
2004-02-29 14:21:22 +01:00
}
2006-04-09 09:12:47 +02:00
return startTime < = t + Margin & & t < stopTime ; // must stop *before* stopTime to allow adjacent timers
2004-02-29 14:21:22 +01:00
}
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 ;
2006-04-01 13:27:14 +02:00
if ( UseVps )
return overlap > FULLMATCH ? tmFull : tmNone ;
return overlap > = FULLMATCH ? 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
{
2009-06-21 14:08:51 +02:00
return IsSingleEvent ( ) & & ! Recording ( ) & & StopTime ( ) + EXPIRELATENCY < = time ( NULL ) & & ( ! HasFlags ( tfVps ) | | ! event | | ! event - > Vps ( ) ) ;
2005-03-20 11:19:36 +01:00
}
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 ;
}
2006-02-28 12:44:00 +01:00
# define EPGLIMITBEFORE (1 * 3600) // Time in seconds before a timer's start time and
# define EPGLIMITAFTER (1 * 3600) // after its stop time within which EPG events will be taken into consideration.
2006-02-25 15:57:56 +01:00
void cTimer : : SetEventFromSchedule ( const cSchedules * Schedules )
{
cSchedulesLock SchedulesLock ;
if ( ! Schedules ) {
lastSetEvent = 0 ; // forces setting the event, even if the schedule hasn't been modified
if ( ! ( Schedules = cSchedules : : Schedules ( SchedulesLock ) ) )
return ;
}
const cSchedule * Schedule = Schedules - > GetSchedule ( Channel ( ) ) ;
2006-04-01 13:27:14 +02:00
if ( Schedule & & Schedule - > Events ( ) - > First ( ) ) {
2006-02-28 12:44:00 +01:00
time_t now = time ( NULL ) ;
2006-02-25 15:57:56 +01:00
if ( ! lastSetEvent | | Schedule - > Modified ( ) > = lastSetEvent ) {
2006-04-09 09:12:47 +02:00
lastSetEvent = now ;
2006-02-25 15:57:56 +01:00
const cEvent * Event = NULL ;
2006-04-01 13:27:14 +02:00
if ( HasFlags ( tfVps ) & & Schedule - > Events ( ) - > First ( ) - > Vps ( ) ) {
2006-04-09 09:12:47 +02:00
if ( event & & Recording ( ) )
return ; // let the recording end first
2006-04-01 13:27:14 +02:00
// VPS timers only match if their start time exactly matches the event's VPS time:
for ( const cEvent * e = Schedule - > Events ( ) - > First ( ) ; e ; e = Schedule - > Events ( ) - > Next ( e ) ) {
2006-04-09 09:12:47 +02:00
if ( e - > StartTime ( ) & & e - > RunningStatus ( ) ! = SI : : RunningStatusNotRunning ) { // skip outdated events
int overlap = 0 ;
Matches ( e , & overlap ) ;
if ( overlap > FULLMATCH ) {
Event = e ;
break ; // take the first matching event
}
2006-04-01 13:27:14 +02:00
}
}
2006-04-09 09:12:47 +02:00
if ( ! Event & & event & & ( now < = event - > EndTime ( ) | | Matches ( 0 , true ) ) )
return ; // stay with the old event until the timer has completely expired
2006-04-01 13:27:14 +02:00
}
else {
// Normal timers match the event they have the most overlap with:
int Overlap = 0 ;
// Set up the time frame within which to check events:
Matches ( 0 , true ) ;
time_t TimeFrameBegin = StartTime ( ) - EPGLIMITBEFORE ;
time_t TimeFrameEnd = StopTime ( ) + EPGLIMITAFTER ;
for ( const cEvent * e = Schedule - > Events ( ) - > First ( ) ; e ; e = Schedule - > Events ( ) - > Next ( e ) ) {
2006-03-25 11:39:57 +01:00
if ( e - > EndTime ( ) < TimeFrameBegin )
continue ; // skip events way before the timer starts
if ( e - > StartTime ( ) > TimeFrameEnd )
break ; // the rest is way after the timer ends
2006-04-01 13:27:14 +02:00
int overlap = 0 ;
Matches ( e , & overlap ) ;
if ( overlap & & overlap > = Overlap ) {
if ( Event & & overlap = = Overlap & & e - > Duration ( ) < = Event - > Duration ( ) )
2006-02-25 15:57:56 +01:00
continue ; // if overlap is the same, we take the longer event
2006-04-01 13:27:14 +02:00
Overlap = overlap ;
Event = e ;
2006-02-25 15:57:56 +01:00
}
}
2006-04-01 13:27:14 +02:00
}
2006-02-25 15:57:56 +01:00
SetEvent ( Event ) ;
}
}
}
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???
2006-03-25 12:51:29 +01:00
if ( Event )
isyslog ( " timer %s set to event %s " , * ToDescr ( ) , * Event - > ToDescr ( ) ) ;
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 ;
}
2012-02-20 15:37:01 +01:00
void cTimer : : SetDay ( time_t Day )
{
day = Day ;
}
void cTimer : : SetWeekDays ( int WeekDays )
{
weekdays = WeekDays ;
}
void cTimer : : SetStart ( int Start )
{
start = Start ;
}
void cTimer : : SetStop ( int Stop )
{
stop = Stop ;
}
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
}
2012-02-20 15:37:01 +01:00
void cTimer : : SetLifetime ( int Lifetime )
{
lifetime = Lifetime ;
}
void cTimer : : SetAux ( const char * Aux )
{
free ( aux ) ;
aux = strdup ( Aux ) ;
}
2011-08-06 13:20:07 +02:00
void cTimer : : SetDeferred ( int Seconds )
{
deferred = time ( NULL ) + Seconds ;
isyslog ( " timer %s deferred for %d seconds " , * ToDescr ( ) , Seconds ) ;
}
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 ) ;
2006-05-25 14:38:04 +02:00
startTime = 0 ;
2006-02-25 15:57:56 +01:00
SetEvent ( 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 ) ;
2006-02-25 15:57:56 +01:00
SetEvent ( NULL ) ;
2002-10-20 12:28:55 +02:00
Matches ( ) ; // refresh start and end time
}
2008-02-10 14:11:47 +01:00
// --- cTimers ---------------------------------------------------------------
2002-10-20 12:28:55 +02:00
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 ;
2006-03-26 14:38:46 +02:00
lastDeleteExpired = 0 ;
2004-10-24 15:01:50 +02:00
}
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 )
{
2006-02-03 15:25:51 +01:00
static int LastPending = - 1 ;
2002-10-20 12:28:55 +02:00
cTimer * t0 = NULL ;
for ( cTimer * ti = First ( ) ; ti ; ti = Next ( ti ) ) {
if ( ! ti - > Recording ( ) & & ti - > Matches ( t ) ) {
2006-02-03 15:25:51 +01:00
if ( ti - > Pending ( ) ) {
if ( ti - > Index ( ) > LastPending )
LastPending = ti - > Index ( ) ;
else
continue ;
}
2002-10-20 12:28:55 +02:00
if ( ! t0 | | ti - > Priority ( ) > t0 - > Priority ( ) )
t0 = ti ;
}
}
2006-02-03 15:25:51 +01:00
if ( ! t0 )
LastPending = - 1 ;
2002-10-20 12:28:55 +02:00
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-08-05 12:06:11 +02:00
ti - > Matches ( ) ;
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 )
{
2008-02-16 15:04:49 +01:00
cStatus : : MsgTimerChange ( NULL , tcMod ) ;
2006-01-15 13:44:55 +01:00
state + + ;
2004-10-31 10:22:32 +01:00
}
2008-02-16 15:04:49 +01:00
void cTimers : : Add ( cTimer * Timer , cTimer * After )
{
cConfig < cTimer > : : Add ( Timer , After ) ;
cStatus : : MsgTimerChange ( Timer , tcAdd ) ;
}
void cTimers : : Ins ( cTimer * Timer , cTimer * Before )
{
cConfig < cTimer > : : Ins ( Timer , Before ) ;
cStatus : : MsgTimerChange ( Timer , tcAdd ) ;
}
void cTimers : : Del ( cTimer * Timer , bool DeleteObject )
{
cStatus : : MsgTimerChange ( Timer , tcDel ) ;
cConfig < cTimer > : : Del ( Timer , DeleteObject ) ;
}
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 ;
}
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-02-25 15:57:56 +01:00
if ( cRemote : : HasKeys ( ) )
return ; // react immediately on user input
ti - > SetEventFromSchedule ( Schedules ) ;
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 )
{
2006-03-26 14:38:46 +02:00
if ( time ( NULL ) - lastDeleteExpired < 30 )
return ;
2005-03-20 11:19:36 +01:00
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 ;
}
2006-03-26 14:38:46 +02:00
lastDeleteExpired = time ( NULL ) ;
2005-03-20 11:19:36 +01:00
}
2012-06-02 12:10:36 +02:00
// --- cSortedTimers ---------------------------------------------------------
static int CompareTimers ( const void * a , const void * b )
{
return ( * ( const cTimer * * ) a ) - > Compare ( * * ( const cTimer * * ) b ) ;
}
cSortedTimers : : cSortedTimers ( void )
2012-06-03 13:06:15 +02:00
: cVector < const cTimer * > ( Timers . Count ( ) )
2012-06-02 12:10:36 +02:00
{
for ( const cTimer * Timer = Timers . First ( ) ; Timer ; Timer = Timers . Next ( Timer ) )
Append ( Timer ) ;
Sort ( CompareTimers ) ;
}