1
0
mirror of https://github.com/VDR4Arch/vdr.git synced 2023-10-10 13:36:52 +02:00

Implemented full handling of remote timers

This commit is contained in:
Klaus Schmidinger 2015-09-10 10:39:45 +02:00
parent 4e3325b7f7
commit bc0de5dbc5
10 changed files with 173 additions and 100 deletions

16
HISTORY
View File

@ -8669,8 +8669,10 @@ Video Disk Recorder Revision History
connections to keep them alive. connections to keep them alive.
- The new function GetSVDRPServerNames() can be used to get a list of all VDRs - The new function GetSVDRPServerNames() can be used to get a list of all VDRs
this VDR is connected to via SVDRP. this VDR is connected to via SVDRP.
- The new class cSVDRPCommand can be used to execute an SVDRP command on one of - The new function ExecSVDRPCommand() can be used to execute an SVDRP command on
the servers this VDR is connected to, and retrieve the result. one of the servers this VDR is connected to, and retrieve the result.
The helper functions SVDRPCode() and SVDRPValue() can be used to easily access
the codes and values returned by ExecSVDRPCommand().
- The cTimer class now has a new member named 'remote', which holds the name of the - The cTimer class now has a new member named 'remote', which holds the name of the
remote server this timer will record on. If this is NULL, it is a local timer. remote server this timer will record on. If this is NULL, it is a local timer.
- Timers from other VDRs that are connected to this VDR via SVDRP are now - Timers from other VDRs that are connected to this VDR via SVDRP are now
@ -8795,3 +8797,13 @@ Video Disk Recorder Revision History
".../SVDRP default host" can be used to configure automatic peering between VDRs ".../SVDRP default host" can be used to configure automatic peering between VDRs
in the same network. Peering is disabled by default and can be enabled by setting in the same network. Peering is disabled by default and can be enabled by setting
"SVDRP peering" to "yes". "SVDRP peering" to "yes".
- The function cTimer::ToText() no longer returns a newline character at the end of
the string. The newline is now added by the caller as necessary. This was changed
because cTimer::ToText() is now also needed in a context where the terminating
newline can't be used. Consequently, cChannel::ToText() and cMark::ToText() have
been modified accordingly.
- The "Edit timer" menu now has a new parameter "Record on", which can be used to
select the VDR on which this timer shall record. Timers can be freely moved
between connected VDRs by simply selecting the desired machine in this field.
- The SVDRP command DELT no longer checks whether the timer that shall be deleted
is currently recording.

3
MANUAL
View File

@ -498,6 +498,9 @@ Version 2.2
the name of the recording. the name of the recording.
First day: The date of the first day when this timer shall start recording First day: The date of the first day when this timer shall start recording
(only available for repeating timers). (only available for repeating timers).
Record on: The name of the remote VDR this timer shall record on (only available
if there are any remote VDRs connected to this VDR). If this field
is empty, the timer will record on the local VDR.
A timer can also be programmed by pressing the "Red" key on the "Schedule", A timer can also be programmed by pressing the "Red" key on the "Schedule",
"Now", "Next" or "Event" menus. "Now", "Next" or "Event" menus.

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: channels.c 4.2 2015/08/29 12:16:24 kls Exp $ * $Id: channels.c 4.3 2015/09/09 10:21:22 kls Exp $
*/ */
#include "channels.h" #include "channels.h"
@ -554,9 +554,9 @@ cString cChannel::ToText(const cChannel *Channel)
cString buffer; cString buffer;
if (Channel->groupSep) { if (Channel->groupSep) {
if (Channel->number) if (Channel->number)
buffer = cString::sprintf(":@%d %s\n", Channel->number, FullName); buffer = cString::sprintf(":@%d %s", Channel->number, FullName);
else else
buffer = cString::sprintf(":%s\n", FullName); buffer = cString::sprintf(":%s", FullName);
} }
else { else {
char vpidbuf[32]; char vpidbuf[32];
@ -588,7 +588,7 @@ cString cChannel::ToText(const cChannel *Channel)
q = caidbuf; q = caidbuf;
q += IntArrayToString(q, Channel->caids, 16); q += IntArrayToString(q, Channel->caids, 16);
*q = 0; *q = 0;
buffer = cString::sprintf("%s:%d:%s:%s:%d:%s:%s:%s:%s:%d:%d:%d:%d\n", FullName, Channel->frequency, *Channel->parameters, *cSource::ToString(Channel->source), Channel->srate, vpidbuf, apidbuf, tpidbuf, caidbuf, Channel->sid, Channel->nid, Channel->tid, Channel->rid); buffer = cString::sprintf("%s:%d:%s:%s:%d:%s:%s:%s:%s:%d:%d:%d:%d", FullName, Channel->frequency, *Channel->parameters, *cSource::ToString(Channel->source), Channel->srate, vpidbuf, apidbuf, tpidbuf, caidbuf, Channel->sid, Channel->nid, Channel->tid, Channel->rid);
} }
return buffer; return buffer;
} }
@ -806,7 +806,7 @@ bool cChannel::Parse(const char *s)
bool cChannel::Save(FILE *f) bool cChannel::Save(FILE *f)
{ {
return fprintf(f, "%s", *ToText()) > 0; return fprintf(f, "%s\n", *ToText()) > 0;
} }
// --- cChannelSorter -------------------------------------------------------- // --- cChannelSorter --------------------------------------------------------

100
menu.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: menu.c 4.5 2015/09/08 11:02:52 kls Exp $ * $Id: menu.c 4.6 2015/09/10 10:34:45 kls Exp $
*/ */
#include "menu.h" #include "menu.h"
@ -999,6 +999,15 @@ cMenuEditTimer::cMenuEditTimer(cTimer *Timer, bool New)
Add(new cMenuEditIntItem( tr("Lifetime"), &data.lifetime, 0, MAXLIFETIME)); Add(new cMenuEditIntItem( tr("Lifetime"), &data.lifetime, 0, MAXLIFETIME));
Add(file = new cMenuEditStrItem( tr("File"), data.file, sizeof(data.file))); Add(file = new cMenuEditStrItem( tr("File"), data.file, sizeof(data.file)));
SetFirstDayItem(); SetFirstDayItem();
if (data.remote)
strn0cpy(remote, data.remote, sizeof(remote));
else
*remote = 0;
if (GetSVDRPServerNames(&svdrpServerNames)) {
svdrpServerNames.Sort(true);
svdrpServerNames.Insert(strdup(""));
Add(new cMenuEditStrlItem(tr("Record on"), remote, sizeof(remote), &svdrpServerNames));
}
} }
SetHelpKeys(); SetHelpKeys();
} }
@ -1053,6 +1062,70 @@ eOSState cMenuEditTimer::SetFolder(void)
return CloseSubMenu(); return CloseSubMenu();
} }
static bool RemoteTimerError(const cTimer *Timer)
{
Skins.Message(mtError, cString::sprintf(tr("Error while accessing remote timer %d@%s!"), Timer->Id(), Timer->Remote()));
return false; // convenience return code
}
bool cMenuEditTimer::HandleRemoteModifications(cTimer *OldTimer, cTimer *NewTimer)
{
cStringList Response;
if (OldTimer->Local()) {
if (NewTimer->Local()) { // timer stays local, nothing to do
}
else { // timer is moved from local to remote
if (!ExecSVDRPCommand(NewTimer->Remote(), cString::sprintf("NEWT %s", *NewTimer->ToText(true)), &Response) || SVDRPCode(Response[0]) != 250)
return RemoteTimerError(NewTimer);
int RemoteId = atoi(SVDRPValue(Response[0]));
if (RemoteId <= 0)
return RemoteTimerError(NewTimer);
NewTimer->SetId(RemoteId);
isyslog("moved timer %s to %d@%s", *OldTimer->ToDescr(), NewTimer->Id(), NewTimer->Remote());
}
}
else if (NewTimer->Local()) { // timer is moved from remote to local
if (OldTimer->Id()) { // its an existing timer
if (!ExecSVDRPCommand(OldTimer->Remote(), cString::sprintf("DELT %d", OldTimer->Id()), &Response) || SVDRPCode(Response[0]) != 250)
return RemoteTimerError(OldTimer);
}
NewTimer->SetId(cTimers::NewTimerId());
isyslog("moved timer %d@%s to %s", OldTimer->Id(), OldTimer->Remote(), *NewTimer->ToDescr());
}
else if (strcmp(OldTimer->Remote(), NewTimer->Remote()) == 0) { // timer stays remote on same machine
if (OldTimer->Id()) { // its an existing timer
if (!ExecSVDRPCommand(OldTimer->Remote(), cString::sprintf("MODT %d %s", OldTimer->Id(), *NewTimer->ToText(true)), &Response) || SVDRPCode(Response[0]) != 250)
return RemoteTimerError(NewTimer);
isyslog("modified timer %s", *NewTimer->ToDescr());
}
else { // its a new timer
if (!ExecSVDRPCommand(NewTimer->Remote(), cString::sprintf("NEWT %s", *NewTimer->ToText(true)), &Response) || SVDRPCode(Response[0]) != 250)
return RemoteTimerError(NewTimer);
int RemoteId = atoi(SVDRPValue(Response[0]));
if (RemoteId <= 0)
return RemoteTimerError(NewTimer);
NewTimer->SetId(RemoteId);
isyslog("added timer %s", *NewTimer->ToDescr());
}
}
else { // timer is moved from one remote machine to an other
if (!ExecSVDRPCommand(NewTimer->Remote(), cString::sprintf("NEWT %s", *NewTimer->ToText(true)), &Response) || SVDRPCode(Response[0]) != 250)
return RemoteTimerError(NewTimer);
int RemoteId = atoi(SVDRPValue(Response[0]));
if (RemoteId <= 0)
return RemoteTimerError(NewTimer);
NewTimer->SetId(RemoteId);
if (OldTimer->Id()) { // its an existing timer
if (!ExecSVDRPCommand(OldTimer->Remote(), cString::sprintf("DELT %d", OldTimer->Id()), &Response) || SVDRPCode(Response[0]) != 250)
return RemoteTimerError(OldTimer);
isyslog("moved timer %d@%s to %s", OldTimer->Id(), OldTimer->Remote(), *NewTimer->ToDescr());
}
else // its a new timer
isyslog("added timer %s", *NewTimer->ToDescr());
}
return true;
}
eOSState cMenuEditTimer::ProcessKey(eKeys Key) eOSState cMenuEditTimer::ProcessKey(eKeys Key)
{ {
eOSState state = cOsdMenu::ProcessKey(Key); eOSState state = cOsdMenu::ProcessKey(Key);
@ -1074,6 +1147,9 @@ eOSState cMenuEditTimer::ProcessKey(eKeys Key)
} }
if (!*data.file) if (!*data.file)
strcpy(data.file, data.Channel()->ShortName(true)); strcpy(data.file, data.Channel()->ShortName(true));
data.SetRemote(*remote ? remote : NULL);
if (!HandleRemoteModifications(timer, &data))
return osContinue;
*timer = data; *timer = data;
if (addIfConfirmed) { if (addIfConfirmed) {
Timers->Add(timer); Timers->Add(timer);
@ -1215,7 +1291,7 @@ void cMenuTimers::Set(void)
for (const cTimer *Timer = Timers->First(); Timer; Timer = Timers->Next(Timer)) { for (const cTimer *Timer = Timers->First(); Timer; Timer = Timers->Next(Timer)) {
cMenuTimerItem *Item = new cMenuTimerItem(Timer); cMenuTimerItem *Item = new cMenuTimerItem(Timer);
Add(Item); Add(Item);
if (Timer == CurrentTimer) if (CurrentTimer && Timer->Id() == CurrentTimer->Id() && (!Timer->Remote() && !CurrentTimer->Remote() || Timer->Remote() && CurrentTimer->Remote() && strcmp(Timer->Remote(), CurrentTimer->Remote()) == 0))
CurrentItem = Item; CurrentItem = Item;
} }
Sort(); Sort();
@ -1255,6 +1331,11 @@ eOSState cMenuTimers::OnOff(void)
cTimer *Timer = GetTimer(); cTimer *Timer = GetTimer();
if (Timer) { if (Timer) {
Timer->OnOff(); Timer->OnOff();
if (Timer->Remote()) {
cStringList Response;
if (!ExecSVDRPCommand(Timer->Remote(), cString::sprintf("MODT %d %s", Timer->Id(), *Timer->ToText(true)), &Response) || SVDRPCode(Response[0]) != 250)
Skins.Message(mtError, cString::sprintf(tr("Error while accessing timer %d@%s!"), Timer->Id(), Timer->Remote()));
}
LOCK_SCHEDULES_READ; LOCK_SCHEDULES_READ;
Timer->SetEventFromSchedule(Schedules); Timer->SetEventFromSchedule(Schedules);
RefreshCurrent(); RefreshCurrent();
@ -1272,7 +1353,6 @@ eOSState cMenuTimers::Edit(void)
{ {
if (HasSubMenu() || Count() == 0) if (HasSubMenu() || Count() == 0)
return osContinue; return osContinue;
isyslog("editing timer %s", *GetTimer()->ToDescr());
return AddSubMenu(new cMenuEditTimer(GetTimer())); return AddSubMenu(new cMenuEditTimer(GetTimer()));
} }
@ -1292,17 +1372,24 @@ eOSState cMenuTimers::Delete(void)
if (Interface->Confirm(tr("Delete timer?"))) { if (Interface->Confirm(tr("Delete timer?"))) {
if (Timer->Recording()) { if (Timer->Recording()) {
if (Interface->Confirm(tr("Timer still recording - really delete?"))) { if (Interface->Confirm(tr("Timer still recording - really delete?"))) {
Timer->Skip(); if (!Timer->Remote()) {
cRecordControls::Process(Timers, time(NULL)); Timer->Skip();
cRecordControls::Process(Timers, time(NULL));
}
} }
else else
Timer = NULL; Timer = NULL;
} }
if (Timer) { if (Timer) {
isyslog("deleting timer %s", *Timer->ToDescr()); if (Timer->Remote()) {
cStringList Response;
if (!ExecSVDRPCommand(Timer->Remote(), cString::sprintf("DELT %d", Timer->Id()), &Response) || SVDRPCode(Response[0]) != 250)
Skins.Message(mtError, cString::sprintf(tr("Error while accessing timer %d@%s!"), Timer->Id(), Timer->Remote()));
}
Timers->Del(Timer); Timers->Del(Timer);
cOsdMenu::Del(Current()); cOsdMenu::Del(Current());
Display(); Display();
isyslog("deleted timer %s", *Timer->ToDescr());
} }
} }
} }
@ -3925,6 +4012,7 @@ void cMenuSetupMisc::Set(void)
Add(new cMenuEditStrItem( tr("Setup.Miscellaneous$SVDRP host name"), data.SVDRPHostName, sizeof(data.SVDRPHostName))); Add(new cMenuEditStrItem( tr("Setup.Miscellaneous$SVDRP host name"), data.SVDRPHostName, sizeof(data.SVDRPHostName)));
if (GetSVDRPServerNames(&svdrpServerNames)) { if (GetSVDRPServerNames(&svdrpServerNames)) {
svdrpServerNames.Sort(true); svdrpServerNames.Sort(true);
svdrpServerNames.Insert(strdup(""));
Add(new cMenuEditStrlItem(tr("Setup.Miscellaneous$SVDRP default host"), data.SVDRPDefaultHost, sizeof(data.SVDRPDefaultHost), &svdrpServerNames)); Add(new cMenuEditStrlItem(tr("Setup.Miscellaneous$SVDRP default host"), data.SVDRPDefaultHost, sizeof(data.SVDRPDefaultHost), &svdrpServerNames));
} }
} }

5
menu.h
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: menu.h 4.1 2015/08/31 13:34:12 kls Exp $ * $Id: menu.h 4.2 2015/09/08 14:04:27 kls Exp $
*/ */
#ifndef __MENU_H #ifndef __MENU_H
@ -77,12 +77,15 @@ private:
cTimer data; cTimer data;
int channel; int channel;
bool addIfConfirmed; bool addIfConfirmed;
cStringList svdrpServerNames;
char remote[HOST_NAME_MAX];
cMenuEditStrItem *file; cMenuEditStrItem *file;
cMenuEditDateItem *day; cMenuEditDateItem *day;
cMenuEditDateItem *firstday; cMenuEditDateItem *firstday;
eOSState SetFolder(void); eOSState SetFolder(void);
void SetFirstDayItem(void); void SetFirstDayItem(void);
void SetHelpKeys(void); void SetHelpKeys(void);
bool HandleRemoteModifications(cTimer *OldTimer, cTimer *NewTimer);
public: public:
cMenuEditTimer(cTimer *Timer, bool New = false); cMenuEditTimer(cTimer *Timer, bool New = false);
virtual ~cMenuEditTimer(); virtual ~cMenuEditTimer();

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: recording.c 4.3 2015/08/29 14:42:53 kls Exp $ * $Id: recording.c 4.4 2015/09/09 10:21:58 kls Exp $
*/ */
#include "recording.h" #include "recording.h"
@ -2018,7 +2018,7 @@ cMark::~cMark()
cString cMark::ToText(void) cString cMark::ToText(void)
{ {
return cString::sprintf("%s%s%s\n", *IndexToHMSF(position, true, framesPerSecond), Comment() ? " " : "", Comment() ? Comment() : ""); return cString::sprintf("%s%s%s", *IndexToHMSF(position, true, framesPerSecond), Comment() ? " " : "", Comment() ? Comment() : "");
} }
bool cMark::Parse(const char *s) bool cMark::Parse(const char *s)
@ -2037,7 +2037,7 @@ bool cMark::Parse(const char *s)
bool cMark::Save(FILE *f) bool cMark::Save(FILE *f)
{ {
return fprintf(f, "%s", *ToText()) > 0; return fprintf(f, "%s\n", *ToText()) > 0;
} }
// --- cMarks ---------------------------------------------------------------- // --- cMarks ----------------------------------------------------------------

37
svdrp.c
View File

@ -10,7 +10,7 @@
* and interact with the Video Disk Recorder - or write a full featured * and interact with the Video Disk Recorder - or write a full featured
* graphical interface that sits on top of an SVDRP connection. * graphical interface that sits on top of an SVDRP connection.
* *
* $Id: svdrp.c 4.6 2015/09/08 11:08:06 kls Exp $ * $Id: svdrp.c 4.7 2015/09/10 10:39:45 kls Exp $
*/ */
#include "svdrp.h" #include "svdrp.h"
@ -512,7 +512,7 @@ public:
cSVDRPClientHandler(int TcpPort, int UdpPort); cSVDRPClientHandler(int TcpPort, int UdpPort);
virtual ~cSVDRPClientHandler(); virtual ~cSVDRPClientHandler();
void SendDiscover(const char *Address = NULL); void SendDiscover(const char *Address = NULL);
bool Execute(const char *ServerName, const char *Command, cStringList *Response); bool Execute(const char *ServerName, const char *Command, cStringList *Response = NULL);
bool GetServerNames(cStringList *ServerNames, eSvdrpFetchFlags FetchFlags = sffNone); bool GetServerNames(cStringList *ServerNames, eSvdrpFetchFlags FetchFlags = sffNone);
bool TriggerFetchingTimers(const char *ServerName); bool TriggerFetchingTimers(const char *ServerName);
}; };
@ -1305,16 +1305,13 @@ void cSVDRPServer::CmdDELT(const char *Option)
if (isnumber(Option)) { if (isnumber(Option)) {
LOCK_TIMERS_WRITE; LOCK_TIMERS_WRITE;
Timers->SetExplicitModify(); Timers->SetExplicitModify();
cTimer *Timer = Timers->GetById(strtol(Option, NULL, 10)); if (cTimer *Timer = Timers->GetById(strtol(Option, NULL, 10))) {
if (Timer && !Timer->Remote()) { if (Timer->Recording())
if (!Timer->Recording()) { Timer->Skip();
Timers->Del(Timer); Timers->Del(Timer);
Timers->SetModified(); Timers->SetModified();
isyslog("SVDRP < %s deleted timer %s", *connection, *Timer->ToDescr()); isyslog("SVDRP < %s deleted timer %s", *connection, *Timer->ToDescr());
Reply(250, "Timer \"%s\" deleted", Option); Reply(250, "Timer \"%s\" deleted", Option);
}
else
Reply(550, "Timer \"%s\" is recording", Option);
} }
else else
Reply(501, "Timer \"%s\" not defined", Option); Reply(501, "Timer \"%s\" not defined", Option);
@ -2566,22 +2563,10 @@ bool GetSVDRPServerNames(cStringList *ServerNames, eSvdrpFetchFlags FetchFlag)
return false; return false;
} }
// --- cSVDRPCommand --------------------------------------------------------- bool ExecSVDRPCommand(const char *ServerName, const char *Command, cStringList *Response)
cSVDRPCommand::cSVDRPCommand(const char *ServerName, const char *Command)
{
serverName = ServerName;
command = Command;
}
cSVDRPCommand::~cSVDRPCommand()
{
}
bool cSVDRPCommand::Execute(void)
{ {
cMutexLock MutexLock(&SVDRPHandlerMutex); cMutexLock MutexLock(&SVDRPHandlerMutex);
if (SVDRPClientHandler) if (SVDRPClientHandler)
return SVDRPClientHandler->Execute(serverName, command, &response); return SVDRPClientHandler->Execute(ServerName, Command, Response);
return false; return false;
} }

58
svdrp.h
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: svdrp.h 4.4 2015/09/06 12:39:24 kls Exp $ * $Id: svdrp.h 4.5 2015/09/09 09:44:12 kls Exp $
*/ */
#ifndef __SVDRP_H #ifndef __SVDRP_H
@ -12,46 +12,6 @@
#include "tools.h" #include "tools.h"
class cSVDRPCommand {
protected:
cString serverName;
cString command;
cStringList response;
public:
cSVDRPCommand(const char *ServerName, const char *Command);
///< Sets up an SVDRP Command to be executed on the VDR with the given
///< ServerName. A list of all available servers can be retrieved by
///< calling GetSVDRPServerNames().
///< Command is one SVDRP command, followed by optional parameters,
///< just as it can be given in a normal SVDRP connection. It doesn't
///< need to be terminated with a newline.
virtual ~cSVDRPCommand();
bool Execute(void);
///< Sends the Command given in the constructor to the remote VDR
///< and collects all of the response strings.
///< Returns true if the data exchange was successful. Whether or
///< not the actual SVDRP command was successful depends on the
///< resulting strings from the remote VDR, which can be accessed
///< by calling Response(). Execute() can be called any number of
///< times. The list of response strings will be cleared before
///< the command is actually executed.
const cStringList *Response(void) const { return &response; }
///< Returns the list of strings the remote VDR has sent in response
///< to the command. The response strings are exactly as received,
///< with the leading three digit reply code and possible continuation
///< line indicator ('-') in place.
const char *Response(int Index) { return (Index >= 0 && Index < response.Size()) ? response[Index] : NULL; }
///< This is a convenience function for accessing the response strings.
///< Returns the string at the given Index, or NULL if Index is out
///< of range.
int Code(const char *s) { return s ? atoi(s) : 0; }
///< Returns the value of the three digit reply code of the given
///< response string.
const char *Value(const char *s) { return s && s[0] && s[1] && s[2] && s[3] ? s + 4 : NULL; }
///< Returns the actual value of the given response string, skipping
///< the three digit reply code and possible continuation line indicator.
};
enum eSvdrpFetchFlags { enum eSvdrpFetchFlags {
sffNone = 0b0000, sffNone = 0b0000,
sffTimers = 0b0001, sffTimers = 0b0001,
@ -72,5 +32,21 @@ bool GetSVDRPServerNames(cStringList *ServerNames, eSvdrpFetchFlags FetchFlag =
///< client has this flag set will be returned, and the client's flag ///< client has this flag set will be returned, and the client's flag
///< will be cleared. ///< will be cleared.
///< Returns true if the resulting list is not empty. ///< Returns true if the resulting list is not empty.
bool ExecSVDRPCommand(const char *ServerName, const char *Command, cStringList *Response = NULL);
///< Sends the given SVDRP Command string to the remote VDR identified
///< by ServerName and collects all of the response strings in Response.
///< If no Response parameter is given, the response from command execution
///< is ignored.
///< Returns true if the data exchange was successful. Whether or
///< not the actual SVDRP command was successful depends on the
///< resulting strings from the remote VDR, which can be accessed
///< through Response. If Response is given, it will be cleared before
///< the command is actually executed.
inline int SVDRPCode(const char *s) { return s ? atoi(s) : 0; }
///< Returns the value of the three digit reply code of the given
///< SVDRP response string.
inline const char *SVDRPValue(const char *s) { return s && s[0] && s[1] && s[2] && s[3] ? s + 4 : NULL; }
///< Returns the actual value of the given SVDRP response string, skipping
///< the three digit reply code and possible continuation line indicator.
#endif //__SVDRP_H #endif //__SVDRP_H

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: timers.c 4.3 2015/09/08 10:01:02 kls Exp $ * $Id: timers.c 4.4 2015/09/09 10:42:18 kls Exp $
*/ */
#include "timers.h" #include "timers.h"
@ -33,7 +33,7 @@ cTimer::cTimer(bool Instant, bool Pause, const cChannel *Channel)
flags = tfNone; flags = tfNone;
*file = 0; *file = 0;
aux = NULL; aux = NULL;
remote = NULL; remote = *Setup.SVDRPDefaultHost ? strdup(Setup.SVDRPDefaultHost) : NULL;
event = NULL; event = NULL;
if (Instant) if (Instant)
SetFlags(tfActive | tfInstant); SetFlags(tfActive | tfInstant);
@ -91,7 +91,7 @@ cTimer::cTimer(const cEvent *Event)
flags = tfActive; flags = tfActive;
*file = 0; *file = 0;
aux = NULL; aux = NULL;
remote = NULL; remote = *Setup.SVDRPDefaultHost ? strdup(Setup.SVDRPDefaultHost) : NULL;
event = NULL; event = NULL;
if (Event->Vps() && Setup.UseVps) if (Event->Vps() && Setup.UseVps)
SetFlags(tfVps); SetFlags(tfVps);
@ -185,7 +185,7 @@ int cTimer::Compare(const cListObject &ListObject) const
cString cTimer::ToText(bool UseChannelID) const cString cTimer::ToText(bool UseChannelID) const
{ {
strreplace(file, ':', '|'); strreplace(file, ':', '|');
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 : ""); cString buffer = cString::sprintf("%u:%s:%s:%04d:%04d:%d:%d:%s:%s", flags, UseChannelID ? *Channel()->GetChannelID().ToString() : *itoa(Channel()->Number()), *PrintDay(day, weekdays, true), start, stop, priority, lifetime, file, aux ? aux : "");
strreplace(file, '|', ':'); strreplace(file, '|', ':');
return buffer; return buffer;
} }
@ -355,7 +355,7 @@ bool cTimer::Parse(const char *s)
bool cTimer::Save(FILE *f) bool cTimer::Save(FILE *f)
{ {
if (!Remote()) if (!Remote())
return fprintf(f, "%s", *ToText(true)) > 0; return fprintf(f, "%s\n", *ToText(true)) > 0;
return true; return true;
} }
@ -726,7 +726,7 @@ bool cTimers::Load(const char *FileName)
Timers->SetExplicitModify(); Timers->SetExplicitModify();
if (timers.cConfig<cTimer>::Load(FileName)) { if (timers.cConfig<cTimer>::Load(FileName)) {
for (cTimer *ti = timers.First(); ti; ti = timers.Next(ti)) { for (cTimer *ti = timers.First(); ti; ti = timers.Next(ti)) {
ti->SetId(++lastTimerId); ti->SetId(NewTimerId());
ti->ClrFlags(tfRecording); ti->ClrFlags(tfRecording);
Timers->SetModified(); Timers->SetModified();
} }
@ -735,6 +735,11 @@ bool cTimers::Load(const char *FileName)
return false; return false;
} }
int cTimers::NewTimerId(void)
{
return ++lastTimerId; // no need for locking, the caller must have a lock on the global Timers list
}
const cTimer *cTimers::GetById(int Id) const const cTimer *cTimers::GetById(int Id) const
{ {
for (const cTimer *ti = First(); ti; ti = Next(ti)) { for (const cTimer *ti = First(); ti; ti = Next(ti)) {
@ -824,7 +829,7 @@ cTimers *cTimers::GetTimersWrite(cStateKey &StateKey, int TimeoutMs)
void cTimers::Add(cTimer *Timer, cTimer *After) void cTimers::Add(cTimer *Timer, cTimer *After)
{ {
if (!Timer->Remote()) if (!Timer->Remote())
Timer->SetId(++lastTimerId); Timer->SetId(NewTimerId());
cConfig<cTimer>::Add(Timer, After); cConfig<cTimer>::Add(Timer, After);
cStatus::MsgTimerChange(Timer, tcAdd); cStatus::MsgTimerChange(Timer, tcAdd);
} }
@ -882,13 +887,13 @@ bool cTimers::GetRemoteTimers(const char *ServerName)
bool Result = false; bool Result = false;
if (ServerName) { if (ServerName) {
Result = DelRemoteTimers(ServerName); Result = DelRemoteTimers(ServerName);
cSVDRPCommand Cmd(ServerName, "LSTT ID"); cStringList Response;
if (Cmd.Execute()) { if (ExecSVDRPCommand(ServerName, "LSTT ID", &Response)) {
const char *s; for (int i = 0; i < Response.Size(); i++) {
for (int i = 0; s = Cmd.Response(i); i++) { const char *s = Response[i];
int Code = Cmd.Code(s); int Code = SVDRPCode(s);
if (Code == 250) { if (Code == 250) {
if (const char *v = Cmd.Value(s)) { if (const char *v = SVDRPValue(s)) {
int Id = atoi(v); int Id = atoi(v);
while (*v && *v != ' ') while (*v && *v != ' ')
v++; // skip id v++; // skip id
@ -939,8 +944,7 @@ bool cTimers::DelRemoteTimers(const char *ServerName)
void cTimers::TriggerRemoteTimerPoll(const char *ServerName) void cTimers::TriggerRemoteTimerPoll(const char *ServerName)
{ {
if (ServerName) { if (ServerName) {
cSVDRPCommand Cmd(ServerName, cString::sprintf("POLL %s TIMERS", Setup.SVDRPHostName)); if (!ExecSVDRPCommand(ServerName, cString::sprintf("POLL %s TIMERS", Setup.SVDRPHostName)))
if (!Cmd.Execute())
esyslog("ERROR: can't send 'POLL %s TIMERS' to '%s'", Setup.SVDRPHostName, ServerName); esyslog("ERROR: can't send 'POLL %s TIMERS' to '%s'", Setup.SVDRPHostName, ServerName);
} }
else { else {

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: timers.h 4.2 2015/09/05 13:51:33 kls Exp $ * $Id: timers.h 4.3 2015/09/09 10:40:24 kls Exp $
*/ */
#ifndef __TIMERS_H #ifndef __TIMERS_H
@ -67,6 +67,7 @@ public:
time_t FirstDay(void) const { return weekdays ? day : 0; } time_t FirstDay(void) const { return weekdays ? day : 0; }
const char *Aux(void) const { return aux; } const char *Aux(void) const { return aux; }
const char *Remote(void) const { return remote; } const char *Remote(void) const { return remote; }
bool Local(void) const { return !remote; } // convenience
time_t Deferred(void) const { return deferred; } time_t Deferred(void) const { return deferred; }
cString ToText(bool UseChannelID = false) const; cString ToText(bool UseChannelID = false) const;
cString ToDescr(void) const; cString ToDescr(void) const;
@ -166,6 +167,7 @@ public:
///< StateKey.Remove(); ///< StateKey.Remove();
///< } ///< }
static bool Load(const char *FileName); static bool Load(const char *FileName);
static int NewTimerId(void);
const cTimer *GetById(int Id) const; const cTimer *GetById(int Id) const;
cTimer *GetById(int Id) { return const_cast<cTimer *>(static_cast<const cTimers *>(this)->GetById(Id)); }; cTimer *GetById(int Id) { return const_cast<cTimer *>(static_cast<const cTimers *>(this)->GetById(Id)); };
cTimer *GetTimer(cTimer *Timer); cTimer *GetTimer(cTimer *Timer);