Timers now have unique ids

This commit is contained in:
Klaus Schmidinger 2015-09-06 09:14:53 +02:00
parent 50d268538e
commit 04edd69b7a
4 changed files with 77 additions and 39 deletions

View File

@ -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.

74
svdrp.c
View File

@ -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<const cTimer *> 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

View File

@ -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<cTimer>("Timers")
@ -717,6 +726,7 @@ bool cTimers::Load(const char *FileName)
Timers->SetExplicitModify();
if (timers.cConfig<cTimer>::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<cTimer>::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;
}

View File

@ -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<cTimer> {
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<cTimer *>(static_cast<const cTimers *>(this)->GetById(Id)); };
cTimer *GetTimer(cTimer *Timer);
const cTimer *GetMatch(time_t t) const;
cTimer *GetMatch(time_t t) { return const_cast<cTimer *>(static_cast<const cTimers *>(this)->GetMatch(t)); };