From 04edd69b7afa291a54ffe70db146b068c22d2096 Mon Sep 17 00:00:00 2001 From: Klaus Schmidinger Date: Sun, 6 Sep 2015 09:14:53 +0200 Subject: [PATCH] Timers now have unique ids --- HISTORY | 5 ++++ svdrp.c | 74 +++++++++++++++++++++++++++++--------------------------- timers.c | 29 +++++++++++++++++++--- timers.h | 8 +++++- 4 files changed, 77 insertions(+), 39 deletions(-) diff --git a/HISTORY b/HISTORY index bacc5604..e22377f3 100644 --- a/HISTORY +++ b/HISTORY @@ -8784,3 +8784,8 @@ Video Disk Recorder Revision History communication printed to the console for debugging. - Added a missing 'const' to cReceiver::Receive(), to protect the given Data from being modified. +- The SVDRP commands that deal with timers (DELT, LSTT, MODT, NEWT, NEXT and UPDT) + as well as any log messages that refer to timers, now use a unique id for each + timer, which remains valid as long as this instance of VDR is running. This means + that timers are no longer continuously numbered from 1 to N in LSTT. There may be + gaps in the sequence, in case timers have been deleted. diff --git a/svdrp.c b/svdrp.c index 8d7fce7d..28813ccb 100644 --- a/svdrp.c +++ b/svdrp.c @@ -10,7 +10,7 @@ * and interact with the Video Disk Recorder - or write a full featured * graphical interface that sits on top of an SVDRP connection. * - * $Id: svdrp.c 4.3 2015/09/01 10:34:34 kls Exp $ + * $Id: svdrp.c 4.4 2015/09/06 09:14:53 kls Exp $ */ #include "svdrp.h" @@ -1226,7 +1226,7 @@ void cSVDRPServer::CmdDELC(const char *Option) Channels->SetExplicitModify(); if (cChannel *Channel = Channels->GetByNumber(strtol(Option, NULL, 10))) { if (const cTimer *Timer = Timers->UsesChannel(Channel)) { - Reply(550, "Channel \"%s\" is in use by timer %d", Option, Timer->Index() + 1); + Reply(550, "Channel \"%s\" is in use by timer %s", Option, *Timer->ToDescr()); return; } int CurrentChannelNr = cDevice::CurrentChannel(); @@ -1265,7 +1265,7 @@ static cString RecordingInUseMessage(int Reason, const char *RecordingId, cRecor { cRecordControl *rc; if ((Reason & ruTimer) != 0 && (rc = cRecordControls::GetRecordControl(Recording->FileName())) != NULL) - return cString::sprintf("Recording \"%s\" is in use by timer %d", RecordingId, rc->Timer()->Index() + 1); + return cString::sprintf("Recording \"%s\" is in use by timer %d", RecordingId, rc->Timer()->Id()); else if ((Reason & ruReplay) != 0) return cString::sprintf("Recording \"%s\" is being replayed", RecordingId); else if ((Reason & ruCut) != 0) @@ -1312,12 +1312,12 @@ void cSVDRPServer::CmdDELT(const char *Option) if (isnumber(Option)) { LOCK_TIMERS_WRITE; Timers->SetExplicitModify(); - cTimer *Timer = Timers->Get(strtol(Option, NULL, 10) - 1); + cTimer *Timer = Timers->GetById(strtol(Option, NULL, 10)); if (Timer && !Timer->Remote()) { if (!Timer->Recording()) { - isyslog("SVDRP < %s deleting timer %s", *connection, *Timer->ToDescr()); Timers->Del(Timer); Timers->SetModified(); + isyslog("SVDRP < %s deleted timer %s", *connection, *Timer->ToDescr()); Reply(250, "Timer \"%s\" deleted", Option); } else @@ -1745,8 +1745,8 @@ void cSVDRPServer::CmdLSTR(const char *Option) void cSVDRPServer::CmdLSTT(const char *Option) { - int Number = 0; - bool Id = false; + int Id = 0; + bool UseChannelId = false; if (*Option) { char buf[strlen(Option) + 1]; strcpy(buf, Option); @@ -1755,9 +1755,9 @@ void cSVDRPServer::CmdLSTT(const char *Option) char *p = strtok_r(buf, delim, &strtok_next); while (p) { if (isnumber(p)) - Number = strtol(p, NULL, 10); + Id = strtol(p, NULL, 10); else if (strcasecmp(p, "ID") == 0) - Id = true; + UseChannelId = true; else { Reply(501, "Unknown option: \"%s\"", p); return; @@ -1766,11 +1766,11 @@ void cSVDRPServer::CmdLSTT(const char *Option) } } LOCK_TIMERS_READ; - if (Number) { + if (Id) { for (const cTimer *Timer = Timers->First(); Timer; Timer = Timers->Next(Timer)) { if (!Timer->Remote()) { - if (Timer->Index() + 1 == Number) { - Reply(250, "%d %s", Timer->Index() + 1, *Timer->ToText(Id)); + if (Timer->Id() == Id) { + Reply(250, "%d %s", Timer->Id(), *Timer->ToText(UseChannelId)); return; } } @@ -1779,15 +1779,19 @@ void cSVDRPServer::CmdLSTT(const char *Option) return; } else { - cVector LocalTimers; - for (const cTimer *Timer = Timers->First(); Timer; Timer = Timers->Next(Timer)) { - if (!Timer->Remote()) - LocalTimers.Append(Timer); - } - if (LocalTimers.Size()) { - for (int i = 0; i < LocalTimers.Size(); i++) { - const cTimer *Timer = LocalTimers[i]; - Reply(i < LocalTimers.Size() - 1 ? -250 : 250, "%d %s", Timer->Index() + 1, *Timer->ToText(Id)); + const cTimer *LastLocalTimer = Timers->Last(); + while (LastLocalTimer) { + if (LastLocalTimer->Remote()) + LastLocalTimer = Timers->Prev(LastLocalTimer); + else + break; + } + if (LastLocalTimer) { + for (const cTimer *Timer = Timers->First(); Timer; Timer = Timers->Next(Timer)) { + if (!Timer->Remote()) + Reply(Timer != LastLocalTimer ? -250 : 250, "%d %s", Timer->Id(), *Timer->ToText(UseChannelId)); + if (Timer == LastLocalTimer) + break; } return; } @@ -1846,12 +1850,12 @@ void cSVDRPServer::CmdMODT(const char *Option) { if (*Option) { char *tail; - int n = strtol(Option, &tail, 10); + int Id = strtol(Option, &tail, 10); if (tail && tail != Option) { tail = skipspace(tail); LOCK_TIMERS_WRITE; Timers->SetExplicitModify(); - if (cTimer *Timer = Timers->Get(n - 1)) { + if (cTimer *Timer = Timers->GetById(Id)) { cTimer t = *Timer; if (strcasecmp(tail, "ON") == 0) t.SetFlags(tfActive); @@ -1863,11 +1867,11 @@ void cSVDRPServer::CmdMODT(const char *Option) } *Timer = t; Timers->SetModified(); - isyslog("SVDRP < %s timer %s modified (%s)", *connection, *Timer->ToDescr(), Timer->HasFlags(tfActive) ? "active" : "inactive"); - Reply(250, "%d %s", Timer->Index() + 1, *Timer->ToText()); + isyslog("SVDRP < %s modified timer %s (%s)", *connection, *Timer->ToDescr(), Timer->HasFlags(tfActive) ? "active" : "inactive"); + Reply(250, "%d %s", Timer->Id(), *Timer->ToText()); } else - Reply(501, "Timer \"%d\" not defined", n); + Reply(501, "Timer \"%d\" not defined", Id); } else Reply(501, "Error in timer number"); @@ -2007,8 +2011,8 @@ void cSVDRPServer::CmdNEWT(const char *Option) if (Timer->Parse(Option)) { LOCK_TIMERS_WRITE; Timers->Add(Timer); - isyslog("SVDRP < %s timer %s added", *connection, *Timer->ToDescr()); - Reply(250, "%d %s", Timer->Index() + 1, *Timer->ToText()); + isyslog("SVDRP < %s added timer %s", *connection, *Timer->ToDescr()); + Reply(250, "%d %s", Timer->Id(), *Timer->ToText()); return; } else @@ -2024,13 +2028,13 @@ void cSVDRPServer::CmdNEXT(const char *Option) LOCK_TIMERS_READ; if (const cTimer *t = Timers->GetNextActiveTimer()) { time_t Start = t->StartTime(); - int Number = t->Index() + 1; + int Id = t->Id(); if (!*Option) - Reply(250, "%d %s", Number, *TimeToString(Start)); + Reply(250, "%d %s", Id, *TimeToString(Start)); else if (strcasecmp(Option, "ABS") == 0) - Reply(250, "%d %ld", Number, Start); + Reply(250, "%d %ld", Id, Start); else if (strcasecmp(Option, "REL") == 0) - Reply(250, "%d %ld", Number, Start - time(NULL)); + Reply(250, "%d %ld", Id, Start - time(NULL)); else Reply(501, "Unknown option: \"%s\"", Option); } @@ -2261,13 +2265,13 @@ void cSVDRPServer::CmdUPDT(const char *Option) t->Parse(Option); delete Timer; Timer = t; - isyslog("SVDRP < %s timer %s updated", *connection, *Timer->ToDescr()); + isyslog("SVDRP < %s updated timer %s", *connection, *Timer->ToDescr()); } else { Timers->Add(Timer); - isyslog("SVDRP < %s timer %s added", *connection, *Timer->ToDescr()); + isyslog("SVDRP < %s added timer %s", *connection, *Timer->ToDescr()); } - Reply(250, "%d %s", Timer->Index() + 1, *Timer->ToText()); + Reply(250, "%d %s", Timer->Id(), *Timer->ToText()); return; } else diff --git a/timers.c b/timers.c index e124a1d5..0e5075fc 100644 --- a/timers.c +++ b/timers.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: timers.c 4.1 2015/08/31 10:45:13 kls Exp $ + * $Id: timers.c 4.2 2015/09/05 14:42:50 kls Exp $ */ #include "timers.h" @@ -25,6 +25,7 @@ cTimer::cTimer(bool Instant, bool Pause, const cChannel *Channel) { + id = 0; startTime = stopTime = 0; scheduleState = -1; deferred = 0; @@ -82,6 +83,7 @@ cTimer::cTimer(bool Instant, bool Pause, const cChannel *Channel) cTimer::cTimer(const cEvent *Event) { + id = 0; startTime = stopTime = 0; scheduleState = -1; deferred = 0; @@ -139,6 +141,7 @@ cTimer::~cTimer() cTimer& cTimer::operator= (const cTimer &Timer) { if (&Timer != this) { + id = Timer.id; uint OldFlags = flags & tfRecording; startTime = Timer.startTime; stopTime = Timer.stopTime; @@ -189,7 +192,7 @@ cString cTimer::ToText(bool UseChannelID) const cString cTimer::ToDescr(void) const { - return cString::sprintf("%d%s%s (%d %04d-%04d %s'%s')", Index() + 1, remote ? "@" : "", remote ? remote : "", Channel()->Number(), start, stop, HasFlags(tfVps) ? "VPS " : "", file); + return cString::sprintf("%d%s%s (%d %04d-%04d %s'%s')", Id(), remote ? "@" : "", remote ? remote : "", Channel()->Number(), start, stop, HasFlags(tfVps) ? "VPS " : "", file); } int cTimer::TimeToInt(int t) @@ -522,6 +525,11 @@ time_t cTimer::StopTime(void) const #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. +void cTimer::SetId(int Id) +{ + id = Id; +} + bool cTimer::SetEventFromSchedule(const cSchedules *Schedules) { const cSchedule *Schedule = Schedules->GetSchedule(Channel()); @@ -704,6 +712,7 @@ void cTimer::OnOff(void) // --- cTimers --------------------------------------------------------------- cTimers cTimers::timers; +int cTimers::lastTimerId = 0; cTimers::cTimers(void) :cConfig("Timers") @@ -717,6 +726,7 @@ bool cTimers::Load(const char *FileName) Timers->SetExplicitModify(); if (timers.cConfig::Load(FileName)) { for (cTimer *ti = timers.First(); ti; ti = timers.Next(ti)) { + ti->SetId(++lastTimerId); ti->ClrFlags(tfRecording); Timers->SetModified(); } @@ -725,6 +735,15 @@ bool cTimers::Load(const char *FileName) return false; } +const cTimer *cTimers::GetById(int Id) const +{ + for (const cTimer *ti = First(); ti; ti = Next(ti)) { + if (!ti->Remote() && ti->Id() == Id) + return ti; + } + return NULL; +} + cTimer *cTimers::GetTimer(cTimer *Timer) { for (cTimer *ti = First(); ti; ti = Next(ti)) { @@ -804,6 +823,8 @@ cTimers *cTimers::GetTimersWrite(cStateKey &StateKey, int TimeoutMs) void cTimers::Add(cTimer *Timer, cTimer *After) { + if (!Timer->Remote()) + Timer->SetId(++lastTimerId); cConfig::Add(Timer, After); cStatus::MsgTimerChange(Timer, tcAdd); } @@ -868,11 +889,13 @@ bool cTimers::GetRemoteTimers(const char *ServerName) int Code = Cmd.Code(s); if (Code == 250) { if (const char *v = Cmd.Value(s)) { + int Id = atoi(v); while (*v && *v != ' ') - v++; // skip number + v++; // skip id cTimer *Timer = new cTimer; if (Timer->Parse(v)) { Timer->SetRemote(ServerName); + Timer->SetId(Id); Add(Timer); Result = true; } diff --git a/timers.h b/timers.h index 88a06624..38eb9284 100644 --- a/timers.h +++ b/timers.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: timers.h 4.1 2015/08/31 10:42:53 kls Exp $ + * $Id: timers.h 4.2 2015/09/05 13:51:33 kls Exp $ */ #ifndef __TIMERS_H @@ -27,6 +27,7 @@ enum eTimerMatch { tmNone, tmPartial, tmFull }; class cTimer : public cListObject { friend class cMenuEditTimer; private: + int id; mutable time_t startTime, stopTime; int scheduleState; mutable time_t deferred; ///< Matches(time_t, ...) will return false if the current time is before this value @@ -50,6 +51,7 @@ public: virtual ~cTimer(); cTimer& operator= (const cTimer &Timer); virtual int Compare(const cListObject &ListObject) const; + int Id(void) const { return id; } bool Recording(void) const { return HasFlags(tfRecording); } bool Pending(void) const { return pending; } bool InVpsMargin(void) const { return inVpsMargin; } @@ -83,6 +85,7 @@ public: bool Expired(void) const; time_t StartTime(void) const; time_t StopTime(void) const; + void SetId(int Id); bool SetEventFromSchedule(const cSchedules *Schedules); bool SetEvent(const cEvent *Event); void SetRecording(bool Recording); @@ -112,6 +115,7 @@ public: class cTimers : public cConfig { private: static cTimers timers; + static int lastTimerId; time_t lastDeleteExpired; public: cTimers(void); @@ -162,6 +166,8 @@ public: ///< StateKey.Remove(); ///< } static bool Load(const char *FileName); + const cTimer *GetById(int Id) const; + cTimer *GetById(int Id) { return const_cast(static_cast(this)->GetById(Id)); }; cTimer *GetTimer(cTimer *Timer); const cTimer *GetMatch(time_t t) const; cTimer *GetMatch(time_t t) { return const_cast(static_cast(this)->GetMatch(t)); };