summaryrefslogtreecommitdiff
path: root/conflictcheck.c
diff options
context:
space:
mode:
authorChristian Wieninger <winni@debian.(none)>2007-11-11 15:40:28 +0100
committerChristian Wieninger <winni@debian.(none)>2007-11-11 15:40:28 +0100
commit8d4f8607dc1558ce73eb4c376bdbf78ddb65da83 (patch)
treed0c5dde81a36ab2e8a2edc7c1e6922556518b312 /conflictcheck.c
downloadvdr-plugin-epgsearch-8d4f8607dc1558ce73eb4c376bdbf78ddb65da83.tar.gz
vdr-plugin-epgsearch-8d4f8607dc1558ce73eb4c376bdbf78ddb65da83.tar.bz2
Initial commit
Diffstat (limited to 'conflictcheck.c')
-rw-r--r--conflictcheck.c648
1 files changed, 648 insertions, 0 deletions
diff --git a/conflictcheck.c b/conflictcheck.c
new file mode 100644
index 0000000..d783f8a
--- /dev/null
+++ b/conflictcheck.c
@@ -0,0 +1,648 @@
+/*
+Copyright (C) 2004-2007 Christian Wieninger
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+
+The author can be reached at cwieninger@gmx.de
+
+The project's page is at http://winni.vdr-developer.org/epgsearch
+*/
+
+#include <vector>
+#include <algorithm>
+#include "conflictcheck.h"
+#include "epgsearchcfg.h"
+#include <libsi/si.h>
+#include "conflictcheck_thread.h"
+#include "recstatus.h"
+
+#define FULLMATCH 1000
+#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.
+
+// --- cConflictCheckTimerObj --------------------------------------------------------
+cConflictCheckTimerObj::cConflictCheckTimerObj(cTimer* Timer, time_t Start, time_t Stop, int Device) : cTimerObj(Timer), start(Start), stop(Stop), device(Device), conflCheckTime(NULL), concurrentTimers(NULL), ignore(false)
+{
+ event = Timer->Event();
+ recDuration = 0;
+ lastRecStart = 0;
+ lastRecStop = 0;
+}
+
+int cConflictCheckTimerObj::Compare(const cListObject &ListObject) const
+{
+ cConflictCheckTimerObj *p = (cConflictCheckTimerObj *)&ListObject;
+ long diff = start - p->start;
+ if (diff == 0)
+ diff = p->timer->Priority() - timer->Priority();
+ if (diff == 0)
+ diff = timer->Index() - p->timer->Index();
+ return diff;
+}
+
+const cEvent* cConflictCheckTimerObj::Event()
+{
+ if(timer->Event())
+ return timer->Event();
+ else
+ if (!event)
+ event = SetEventFromSchedule();
+ return event;
+}
+
+const cEvent* cConflictCheckTimerObj::SetEventFromSchedule()
+{
+ cSchedulesLock SchedulesLock;
+ const cSchedules* Schedules = NULL;
+ if (!(Schedules = cSchedules::Schedules(SchedulesLock)))
+ return NULL;
+
+ const cSchedule *Schedule = Schedules->GetSchedule(timer->Channel());
+ if (Schedule && Schedule->Events()->First())
+ {
+ const cEvent *Event = NULL;
+ if (timer->HasFlags(tfVps) && Schedule->Events()->First()->Vps())
+ {
+ // 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))
+ {
+ 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
+ }
+ }
+ }
+ }
+ 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:
+ timer->Matches(0, true);
+ time_t TimeFrameBegin = start - EPGLIMITBEFORE;
+ time_t TimeFrameEnd = stop + EPGLIMITAFTER;
+ for (const cEvent *e = Schedule->Events()->First(); e; e = Schedule->Events()->Next(e))
+ {
+ 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
+ int overlap = 0;
+ Matches(e, &overlap);
+ if (overlap && overlap >= Overlap)
+ {
+ if (Event && overlap == Overlap && e->Duration() <= Event->Duration())
+ continue; // if overlap is the same, we take the longer event
+ Overlap = overlap;
+ Event = e;
+ }
+ }
+ }
+ return Event;
+ }
+ return NULL;
+}
+
+int cConflictCheckTimerObj::Matches(const cEvent *Event, int *Overlap) const
+{
+ // 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.
+ if (timer->Channel()->GetChannelID() == Event->ChannelID()) {
+ bool UseVps = timer->HasFlags(tfVps) && Event->Vps();
+ timer->Matches(UseVps ? Event->Vps() : Event->StartTime(), true);
+ int overlap = 0;
+ if (UseVps)
+ overlap = (start == Event->Vps()) ? FULLMATCH + (Event->IsRunning() ? 200 : 100) : 0;
+ if (!overlap) {
+ if (start <= Event->StartTime() && Event->EndTime() <= stop)
+ overlap = FULLMATCH;
+ else if (stop <= Event->StartTime() || Event->EndTime() <= start)
+ overlap = 0;
+ else
+ overlap = (min(stop, Event->EndTime()) - max(start, Event->StartTime())) * FULLMATCH / max(Event->Duration(), 1);
+ }
+ if (Overlap)
+ *Overlap = overlap;
+ if (UseVps)
+ return overlap > FULLMATCH ? tmFull : tmNone;
+ return overlap >= FULLMATCH ? tmFull : overlap > 0 ? tmPartial : tmNone;
+ }
+ return tmNone;
+}
+
+// --- cConflictCheck -------------------------------------------------------
+cConflictCheck::cConflictCheck()
+{
+ evaltimeList = NULL;
+ timerList = NULL;
+ failedList = NULL;
+ relevantConflicts = 0;
+ numConflicts = 0;
+ devices = NULL;
+ InitDevicesInfo();
+}
+
+cConflictCheck::~cConflictCheck()
+{
+ if (evaltimeList)
+ {
+ evaltimeList->Clear();
+ DELETENULL(evaltimeList);
+ }
+ if (timerList)
+ {
+ timerList->Clear();
+ DELETENULL(timerList);
+ }
+ if (devices)
+ delete [] devices;
+}
+
+void cConflictCheck::InitDevicesInfo()
+{
+ devices = new cConflictCheckDevice[MAXDEVICES];
+#ifdef DEBUG_CONFL
+ numDevices = 2;
+ for(int i=0; i<numDevices; i++)
+ {
+ devices[i].devicenr = i;
+ devices[i].device = NULL;
+ }
+#else
+ numDevices = cDevice::NumDevices();
+ for(int i=0; i<numDevices; i++)
+ devices[i].device = cDevice::GetDevice(i);
+#endif
+}
+
+void cConflictCheck::Check()
+{
+ if (evaltimeList)
+ DELETENULL(evaltimeList);
+ if (timerList)
+ DELETENULL(timerList);
+
+ timerList = CreateCurrentTimerList();
+ if (timerList) evaltimeList = CreateEvaluationTimeList(timerList);
+ if (evaltimeList) failedList = CreateConflictList(evaltimeList, timerList);
+ if (failedList)
+ {
+ for(cConflictCheckTime* checkTime = failedList->First(); checkTime; checkTime = failedList->Next(checkTime))
+ {
+ LogFile.Log(2,"result of conflict check for %s:", DAYDATETIME(checkTime->evaltime));
+ std::set<cConflictCheckTimerObj*,TimerObjSort>::iterator it;
+ for (it = checkTime->failedTimers.begin(); it != checkTime->failedTimers.end(); it++)
+ LogFile.Log(2,"timer '%s' (%s, channel %s) failed", (*it)->timer->File(), DAYDATETIME((*it)->timer->StartTime()), CHANNELNAME((*it)->timer->Channel()));
+ }
+ }
+}
+
+cList<cConflictCheckTimerObj>* cConflictCheck::CreateCurrentTimerList()
+{
+ cList<cConflictCheckTimerObj>* CurrentTimerList = NULL;
+
+ // collect single event timers
+ time_t tMax = 0;
+ cTimer* ti = NULL;
+ for (ti = Timers.First(); ti; ti = Timers.Next(ti))
+ {
+ tMax = max(tMax, ti->StartTime());
+ if (!ti->IsSingleEvent()) continue;
+ // already recording?
+ int deviceNr = gl_recStatusMonitor->TimerRecDevice(ti)-1;
+ cConflictCheckTimerObj* timerObj = new cConflictCheckTimerObj(ti, ti->StartTime(), ti->StopTime(), deviceNr);
+ if (deviceNr >= 0)
+ {
+ devices[deviceNr].recTimers.insert(timerObj);
+ timerObj->lastRecStart = ti->StartTime();
+ }
+ LogFile.Log(3,"add timer '%s' (%s, channel %s) for conflict check", ti->File(), DAYDATETIME(ti->StartTime()), CHANNELNAME(ti->Channel()));
+ if (deviceNr >= 0)
+ LogFile.Log(3,"timer already recording since %s on device %d", DAYDATETIME(ti->StartTime()), deviceNr+1);
+
+ if (!CurrentTimerList) CurrentTimerList = new cList<cConflictCheckTimerObj>;
+ CurrentTimerList->Add(timerObj);
+ }
+
+ // collect repeating timers from now until the date of the timer with tMax
+ time_t maxCheck = time(NULL) + EPGSearchConfig.checkMaxDays * SECSINDAY;
+ tMax = max(tMax, maxCheck);
+ for (ti = Timers.First(); ti; ti = Timers.Next(ti))
+ {
+ if (ti->IsSingleEvent()) continue;
+ time_t day = time(NULL);
+ while(day < tMax)
+ {
+ if (ti->DayMatches(day))
+ {
+ time_t Start = cTimer::SetTime(day, cTimer::TimeToInt(ti->Start()));
+ int deviceNr = -1;
+ if (Start < time(NULL))
+ {
+#ifndef DEBUG_CONFL
+ if (ti->Recording())
+ deviceNr = gl_recStatusMonitor->TimerRecDevice(ti)-1;
+#else
+ if (Start + ti->StopTime() - ti->StartTime() > time(NULL))
+ deviceNr = 0;
+#endif
+ if (deviceNr == -1) // currently not recording, skip it
+ {
+ day += SECSINDAY;
+ continue;
+ }
+ }
+ else if (Start < ti->StartTime())
+ {
+ day += SECSINDAY;
+ continue;
+ }
+ cConflictCheckTimerObj* timerObj = new cConflictCheckTimerObj(ti, Start, Start + ti->StopTime() - ti->StartTime(), deviceNr);
+ LogFile.Log(3,"add timer '%s' (%s, channel %s) for conflict check", ti->File(), DAYDATETIME(Start), CHANNELNAME(ti->Channel()));
+ if (deviceNr >= 0)
+ {
+ LogFile.Log(3,"timer already recording since %s on device %d", DAYDATETIME(Start), deviceNr+1);
+ devices[deviceNr].recTimers.insert(timerObj);
+ timerObj->lastRecStart = Start;
+ }
+ if (!CurrentTimerList) CurrentTimerList = new cList<cConflictCheckTimerObj>;
+ CurrentTimerList->Add(timerObj);
+ }
+ day += SECSINDAY;
+ }
+ }
+
+ if (CurrentTimerList) CurrentTimerList->Sort();
+ return CurrentTimerList;
+}
+
+// create a list of all times that have to be checked
+cList<cConflictCheckTime>* cConflictCheck::CreateEvaluationTimeList(cList<cConflictCheckTimerObj>* TimerList)
+{
+ LogFile.Log(3,"create check time list");
+ cList<cConflictCheckTime>* EvalTimeList = NULL;
+ for(cConflictCheckTimerObj* TimerObj= TimerList->First(); TimerObj; TimerObj = TimerList->Next(TimerObj))
+ {
+ if (!TimerObj->timer->HasFlags(tfActive)) continue;
+
+ if (!EvalTimeList) EvalTimeList = new cList<cConflictCheckTime>;
+
+ cConflictCheckTime* checkTime = NULL;
+
+ // add all timer start times
+ for(cConflictCheckTime* checkTimeTest = EvalTimeList->First(); checkTimeTest; checkTimeTest = EvalTimeList->Next(checkTimeTest))
+ {
+ if (checkTimeTest->evaltime == TimerObj->start)
+ {
+ checkTime = checkTimeTest;
+ break;
+ }
+ }
+ if (!checkTime)
+ {
+ checkTime = new cConflictCheckTime(TimerObj->start);
+ EvalTimeList->Add(checkTime);
+ }
+ checkTime->startingTimers.insert(TimerObj);
+
+
+ // add all timer stop times
+ checkTime = NULL;
+ for(cConflictCheckTime* checkTimeTest = EvalTimeList->First(); checkTimeTest; checkTimeTest = EvalTimeList->Next(checkTimeTest))
+ {
+ if (checkTimeTest->evaltime == TimerObj->stop)
+ {
+ checkTime = checkTimeTest;
+ break;
+ }
+ }
+ if (!checkTime)
+ {
+ checkTime = new cConflictCheckTime(TimerObj->stop);
+ EvalTimeList->Add(checkTime);
+ }
+ checkTime->stoppingTimers.insert(TimerObj);
+ }
+ if (EvalTimeList)
+ EvalTimeList->Sort();
+
+ LogFile.Log(3,"create check time list - done");
+ return EvalTimeList;
+}
+
+// this return a list of all conflicts
+cList<cConflictCheckTime>* cConflictCheck::CreateConflictList(cList<cConflictCheckTime>* EvalTimeList, cList<cConflictCheckTimerObj>* TimerList)
+{
+ LogFile.Log(3,"create conflict list");
+ relevantConflicts = 0;
+ numConflicts = 0;
+ maxCheck = time(NULL) + EPGSearchConfig.checkMaxDays * SECSINDAY;
+
+ // check each time
+ for(cConflictCheckTime* checkTime = EvalTimeList->First(); checkTime; checkTime = EvalTimeList->Next(checkTime))
+ {
+ int Conflicts = ProcessCheckTime(checkTime);
+ if (Conflicts > 0) // if there were conflicts do a retry as VDR would do a few seconds after the conflict
+ {
+ LogFile.Log(3,"retry check time %s", DAYDATETIME(checkTime->evaltime));
+ int OldConflicts = Conflicts;
+ while(true)
+ {
+ Conflicts = ProcessCheckTime(checkTime);
+ if (Conflicts == OldConflicts) break; // no change after retry?
+ OldConflicts = Conflicts;
+ };
+ }
+ }
+
+ nextRelevantConflictDate = 0;
+ for(cConflictCheckTime* checkTime = EvalTimeList->First(); checkTime;) // clear the list
+ {
+ cConflictCheckTime* checkTimeNext = EvalTimeList->Next(checkTime);
+ if (checkTime->failedTimers.size() == 0)
+ EvalTimeList->Del(checkTime);
+ else
+ {
+ bool allTimersIgnored = true;
+ std::set<cConflictCheckTimerObj*,TimerObjSort>::iterator it;
+ for (it = checkTime->failedTimers.begin(); it != checkTime->failedTimers.end(); it++)
+ {
+ numConflicts++;
+ if (!(*it)->ignore)
+ {
+ if (!nextRelevantConflictDate)
+ nextRelevantConflictDate = checkTime->evaltime;
+ else
+ nextRelevantConflictDate = min(nextRelevantConflictDate, checkTime->evaltime);
+
+ relevantConflicts++;
+ allTimersIgnored = false;
+ break;
+ }
+ }
+ if (allTimersIgnored)
+ checkTime->ignore = true;
+ }
+ checkTime = checkTimeNext;
+ }
+
+ // store for external access
+ cConflictCheckThread::m_cacheNextConflict = nextRelevantConflictDate;
+ cConflictCheckThread::m_cacheRelevantConflicts = relevantConflicts;
+ cConflictCheckThread::m_cacheTotalConflicts = numConflicts;
+
+ LogFile.Log(3,"create conflict list - done");
+ return EvalTimeList;
+}
+
+// checks for conflicts at one special time
+int cConflictCheck::ProcessCheckTime(cConflictCheckTime* checkTime)
+{
+ if (!checkTime) return 0;
+ LogFile.Log(3,"check time %s", DAYDATETIME(checkTime->evaltime));
+
+ LogFile.Log(3,"detach stopping timers");
+ int Conflicts = 0;
+ // detach all stopping timers from their devices
+ std::set<cConflictCheckTimerObj*,TimerObjSort>::iterator it;
+ for (it = checkTime->stoppingTimers.begin(); it != checkTime->stoppingTimers.end(); it++)
+ if ((*it) && (*it)->device >= 0)
+ {
+ LogFile.Log(3,"detach device %d from timer '%s' (%s, channel %s) at %s", (*it)->device, (*it)->timer->File(), DAYDATETIME((*it)->start), CHANNELNAME((*it)->timer->Channel()), DAYDATETIME(checkTime->evaltime));
+ devices[(*it)->device].recTimers.erase(*it);
+ (*it)->lastRecStop = checkTime->evaltime;
+ if ((*it)->lastRecStart > 0 && (*it)->lastRecStart < (*it)->lastRecStop)
+ {
+ (*it)->recDuration += (*it)->lastRecStop - (*it)->lastRecStart;
+ (*it)->lastRecStart = 0;
+ if (((*it)->stop - (*it)->start - (*it)->recDuration) < EPGSearchConfig.checkMinDuration * 60)
+ (*it)->ignore = true;
+ }
+ }
+
+ LogFile.Log(3,"add pending timers");
+ // if we have pending timers add them to the current start list
+ for (it = pendingTimers.begin(); it != pendingTimers.end(); it++)
+ {
+ if ((*it) && (*it)->stop > checkTime->evaltime)
+ checkTime->startingTimers.insert(*it);
+ pendingTimers.erase(*it);
+ }
+
+ LogFile.Log(3,"attach starting timers");
+ // handle starting timers
+ for (it = checkTime->startingTimers.begin(); it != checkTime->startingTimers.end(); it++)
+ {
+ bool NeedsDetachReceivers = false;
+ if (!(*it) || (*it)->device >= 0) continue; // already has a device
+ int device = GetDevice(*it, &NeedsDetachReceivers);
+ if (device >= 0) // device will be attached?
+ {
+ if (NeedsDetachReceivers) // but needs to detach all others?
+ {
+ // disable running timers
+ std::set<cConflictCheckTimerObj*,TimerObjSort>::iterator it2 = devices[device].recTimers.begin();
+ for(; it2 != devices[device].recTimers.end(); it2++)
+ {
+ LogFile.Log(3,"stopping timer '%s' (%s, channel %s) at %s on device %d because of higher priority", (*it2)->timer->File(), DAYDATETIME((*it2)->start), CHANNELNAME((*it2)->timer->Channel()), DAYDATETIME(checkTime->evaltime), device);
+ AddConflict((*it2), checkTime, pendingTimers);
+ devices[device].recTimers.erase(*it2);
+ Conflicts++;
+ }
+ }
+ devices[device].recTimers.insert(*it);
+ (*it)->device = device;
+ (*it)->lastRecStart = checkTime->evaltime;
+
+ LogFile.Log(3,"recording timer '%s' (%s, channel %s) at %s on device %d", (*it)->timer->File(), DAYDATETIME((*it)->start), CHANNELNAME((*it)->timer->Channel()), DAYDATETIME(checkTime->evaltime), device);
+ }
+ else
+ {
+ AddConflict((*it), checkTime, pendingTimers);
+ Conflicts++;
+ }
+ }
+ LogFile.Log(3,"check time %s - done", DAYDATETIME(checkTime->evaltime));
+ return Conflicts;
+}
+
+#if APIVERSNUM >= 10500
+int cConflictCheck::GetDevice(cConflictCheckTimerObj* TimerObj, bool*)
+{
+ int Priority = TimerObj->timer->Priority();
+ const cChannel* Channel = TimerObj->timer->Channel();
+
+ // Collect the current priorities of all CAM slots that can decrypt the channel:
+ int selDevice = -1;
+ int NumCamSlots = CamSlots.Count();
+ int SlotPriority[NumCamSlots];
+ int NumUsableSlots = 0;
+ if (Channel->Ca() >= CA_ENCRYPTED_MIN) {
+ for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) {
+ SlotPriority[CamSlot->Index()] = MAXPRIORITY + 1; // assumes it can't be used
+ if (CamSlot->ModuleStatus() == msReady) {
+ if (CamSlot->ProvidesCa(Channel->Caids())) {
+ if (!ChannelCamRelations.CamChecked(Channel->GetChannelID(), CamSlot->SlotNumber())) {
+ SlotPriority[CamSlot->Index()] = CamSlot->Priority();
+ NumUsableSlots++;
+ }
+ }
+ }
+ }
+#ifdef CFLC
+ int NumUsableSlots = 1;
+#endif
+ if (!NumUsableSlots)
+ return selDevice; // no CAM is able to decrypt this channel
+ }
+
+ bool NeedsDetachReceivers = false;
+
+ uint32_t Impact = 0xFFFFFFFF; // we're looking for a device with the least impact
+ for (int j = 0; j < NumCamSlots || !NumUsableSlots; j++) {
+ if (NumUsableSlots && SlotPriority[j] > MAXPRIORITY)
+ continue; // there is no CAM available in this slot
+ for (int i = 0; i < numDevices; i++) {
+ if (Channel->Ca() && Channel->Ca() <= CA_DVB_MAX && Channel->Ca() != devices[i].CardIndex() + 1)
+ continue; // a specific card was requested, but not this one
+ if (NumUsableSlots && !CamSlots.Get(j)->Assign(devices[i].device, true))
+ continue; // CAM slot can't be used with this device
+ bool ndr;
+ if (devices[i].ProvidesChannel(Channel, Priority, &ndr)) { // this device is basicly able to do the job
+ if (NumUsableSlots && devices[i].CamSlot() && devices[i].CamSlot() != CamSlots.Get(j))
+ ndr = true; // using a different CAM slot requires detaching receivers
+ // Put together an integer number that reflects the "impact" using
+ // this device would have on the overall system. Each condition is represented
+ // by one bit in the number (or several bits, if the condition is actually
+ // a numeric value). The sequence in which the conditions are listed corresponds
+ // to their individual severity, where the one listed first will make the most
+ // difference, because it results in the most significant bit of the result.
+ uint32_t imp = 0;
+ imp <<= 1; imp |= 0; // prefer the primary device for live viewing if we don't need to detach existing receivers
+ imp <<= 1; imp |= !devices[i].Receiving() || ndr; // use receiving devices if we don't need to detach existing receivers
+ imp <<= 1; imp |= devices[i].Receiving(); // avoid devices that are receiving
+ imp <<= 8; imp |= min(max(devices[i].Priority() + MAXPRIORITY, 0), 0xFF); // use the device with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used)
+ imp <<= 8; imp |= min(max((NumUsableSlots ? SlotPriority[j] : 0) + MAXPRIORITY, 0), 0xFF); // use the CAM slot with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used)
+ imp <<= 1; imp |= ndr; // avoid devices if we need to detach existing receivers
+ imp <<= 1; imp |= devices[i].IsPrimaryDevice(); // avoid the primary device
+ imp <<= 1; imp |= devices[i].HasDecoder(); // avoid full featured cards
+ imp <<= 1; imp |= NumUsableSlots ? !ChannelCamRelations.CamDecrypt(Channel->GetChannelID(), j + 1) : 0; // prefer CAMs that are known to decrypt this channel
+ if (imp < Impact) {
+ // This device has less impact than any previous one, so we take it.
+ Impact = imp;
+ selDevice = i;
+ NeedsDetachReceivers = ndr;
+ }
+ }
+ }
+ if (!NumUsableSlots)
+ break; // no CAM necessary, so just one loop over the devices
+ }
+ return selDevice;
+}
+#else
+// retrieves a free device (nearly a copy of the original cDevice::GetDevice)
+int cConflictCheck::GetDevice(cConflictCheckTimerObj* TimerObj, bool *NeedsDetachReceivers)
+{
+ int selDevice = -1;
+ int Priority = TimerObj->timer->Priority();
+ const cChannel* Channel = TimerObj->timer->Channel();
+ uint Impact = 0xFFFFFFFF;
+ for (int i = 0; i < numDevices; i++) {
+ bool ndr;
+ if (devices[i].ProvidesChannel(Channel, Priority, &ndr)) { // this device is basicly able to do the job
+ uint imp = 0;
+ imp <<= 1; imp |= !devices[i].Receiving() || ndr;
+ imp <<= 1; imp |= devices[i].Receiving();
+ imp <<= 1; //imp |= devices[i] == ActualDevice(); // cannot be handled
+ imp <<= 1; imp |= devices[i].IsPrimaryDevice();
+ imp <<= 1; imp |= devices[i].HasDecoder();
+ imp <<= 8; imp |= min(max(devices[i].Priority() + MAXPRIORITY, 0), 0xFF);
+ imp <<= 8; imp |= min(max(devices[i].ProvidesCa(Channel), 0), 0xFF);
+ if (imp < Impact) {
+ Impact = imp;
+ selDevice = i;
+ if (NeedsDetachReceivers)
+ *NeedsDetachReceivers = ndr;
+ }
+ }
+ }
+ return selDevice;
+}
+#endif
+
+void cConflictCheck::AddConflict(cConflictCheckTimerObj* TimerObj, cConflictCheckTime* CheckTime, std::set<cConflictCheckTimerObj*>& pendingTimers)
+{
+ for(cConflictCheckTimerObj* concTimer= timerList->First(); concTimer; concTimer = timerList->Next(concTimer))
+ {
+ if (concTimer->start >= TimerObj->stop) continue;
+ if (concTimer->stop <= TimerObj->start) continue;
+ if (!TimerObj->concurrentTimers) TimerObj->concurrentTimers = new std::set<cConflictCheckTimerObj*,TimerObjSort>;
+ TimerObj->concurrentTimers->insert(concTimer);
+ }
+ TimerObj->ignore = (TimerObj->timer->Priority() < EPGSearchConfig.checkMinPriority) || TimerObj->start > maxCheck;
+ CheckTime->concurrentRecs.insert(TimerObj);
+ pendingTimers.insert(TimerObj);
+
+ TimerObj->lastRecStop = CheckTime->evaltime;
+ if (TimerObj->lastRecStart > 0 && TimerObj->lastRecStart < TimerObj->lastRecStop)
+ {
+ TimerObj->recDuration += TimerObj->lastRecStop - TimerObj->lastRecStart;
+ TimerObj->lastRecStart = 0;
+ if ((TimerObj->stop - TimerObj->start - TimerObj->recDuration) < EPGSearchConfig.checkMinDuration * 60)
+ TimerObj->ignore = true;
+ }
+
+ TimerObj->device = -1;
+ if (!TimerObj->conflCheckTime)
+ TimerObj->conflCheckTime = CheckTime;
+ else
+ return;
+ CheckTime->failedTimers.insert(TimerObj);
+
+ LogFile.Log(3,"conflict found for timer '%s' (%s, channel %s)", TimerObj->timer->File(), DAYDATETIME(TimerObj->start), CHANNELNAME(TimerObj->timer->Channel()));
+}
+
+bool cConflictCheck::TimerInConflict(cTimer* timer)
+{
+ for(cConflictCheckTime* checkTime = failedList->First(); checkTime; checkTime = failedList->Next(checkTime))
+ {
+ std::set<cConflictCheckTimerObj*,TimerObjSort>::iterator it;
+ for (it = checkTime->failedTimers.begin(); it != checkTime->failedTimers.end(); it++)
+ {
+ if (!(*it)->ignore)
+ {
+ std::set<cConflictCheckTimerObj*,TimerObjSort>::iterator it2;
+ if ((*it)->concurrentTimers)
+ {
+ for (it2 = (*it)->concurrentTimers->begin(); it2 != (*it)->concurrentTimers->end(); it2++)
+ {
+ if ((*it2)->timer == timer)
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+}