diff options
Diffstat (limited to 'conflictcheck.c')
-rw-r--r-- | conflictcheck.c | 1158 |
1 files changed, 543 insertions, 615 deletions
diff --git a/conflictcheck.c b/conflictcheck.c index 8e6bd53..14ac636 100644 --- a/conflictcheck.c +++ b/conflictcheck.c @@ -47,8 +47,8 @@ cConflictCheckTimerObj::cConflictCheckTimerObj(cTimer* Timer, time_t Start, time cConflictCheckTimerObj::~cConflictCheckTimerObj() { - // conflict checks works on a copy of a timer, so delete it again - DELETENULL(timer); + // conflict checks works on a copy of a timer, so delete it again + DELETENULL(timer); } int cConflictCheckTimerObj::Compare(const cListObject &ListObject) const @@ -56,19 +56,18 @@ 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(); + diff = p->timer->Priority() - timer->Priority(); if (diff == 0) - diff = origIndex - p->origIndex; + diff = origIndex - p->origIndex; return diff; } const cEvent* cConflictCheckTimerObj::Event() { - if(timer->Event()) - return timer->Event(); - else - if (!event) - event = SetEventFromSchedule(); + if (timer->Event()) + return timer->Event(); + else if (!event) + event = SetEventFromSchedule(); return event; } @@ -76,86 +75,79 @@ const cEvent* cConflictCheckTimerObj::SetEventFromSchedule() { LOCK_SCHEDULES_READ; if (!Schedules) - return NULL; + 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; + 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 = (std::min(stop, Event->EndTime()) - std::max(start, Event->StartTime())) * FULLMATCH / std::max(Event->Duration(), 1); + // 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 = (std::min(stop, Event->EndTime()) - std::max(start, Event->StartTime())) * FULLMATCH / std::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; + if (Overlap) + *Overlap = overlap; + if (UseVps) + return overlap > FULLMATCH ? tmFull : tmNone; + return overlap >= FULLMATCH ? tmFull : overlap > 0 ? tmPartial : tmNone; + } + return tmNone; } // --- cConflictCheck ------------------------------------------------------- @@ -173,18 +165,16 @@ cConflictCheck::cConflictCheck() cConflictCheck::~cConflictCheck() { - if (evaltimeList) - { - evaltimeList->Clear(); - DELETENULL(evaltimeList); + if (evaltimeList) { + evaltimeList->Clear(); + DELETENULL(evaltimeList); } - if (timerList) - { - timerList->Clear(); - DELETENULL(timerList); + if (timerList) { + timerList->Clear(); + DELETENULL(timerList); } if (devices) - delete [] devices; + delete [] devices; } void cConflictCheck::InitDevicesInfo() @@ -195,15 +185,14 @@ void cConflictCheck::InitDevicesInfo() devices = new cConflictCheckDevice[MAXDEVICES]; #ifdef DEBUG_CONFL numDevices = 4; - for(int i=0; i<numDevices; i++) - { - devices[i].devicenr = i; - devices[i].device = NULL; + 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); + for (int i = 0; i < numDevices; i++) + devices[i].device = cDevice::GetDevice(i); #endif BondDevices(Setup.DeviceBondings); @@ -211,257 +200,231 @@ void cConflictCheck::InitDevicesInfo() void cConflictCheck::BondDevices(const char *Bondings) { - LogFile.Log(3, "Bond Devices"); - if (Bondings) { - cSatCableNumbers SatCableNumbers(MAXDEVICES, Bondings); - int* array = SatCableNumbers.Array(); - for (int i=0; i<SatCableNumbers.Size(); i++) { - for (int j=0; j<SatCableNumbers.Size(); j++) { - if (array[i] > 0 && array[i] == array[j] && i != j) { - LogFile.Log(3, "Bond devices %i and %i.", i+1, j+1); - devices[i].bondedDevices.push_back(&(devices[j])); - } - } + LogFile.Log(3, "Bond Devices"); + if (Bondings) { + cSatCableNumbers SatCableNumbers(MAXDEVICES, Bondings); + int* array = SatCableNumbers.Array(); + for (int i = 0; i < SatCableNumbers.Size(); i++) { + for (int j = 0; j < SatCableNumbers.Size(); j++) { + if (array[i] > 0 && array[i] == array[j] && i != j) { + LogFile.Log(3, "Bond devices %i and %i.", i + 1, j + 1); + devices[i].bondedDevices.push_back(&(devices[j])); + } + } + } } - } - LogFile.Log(3, "Bond Devices done."); + LogFile.Log(3, "Bond Devices done."); } void cConflictCheck::Check() { if (evaltimeList) - DELETENULL(evaltimeList); + DELETENULL(evaltimeList); if (timerList) - DELETENULL(timerList); + DELETENULL(timerList); - LogFile.Log(3, "check only local conflicts = %s",GetLocal()?"yes":"no"); + LogFile.Log(3, "check only local conflicts = %s", GetLocal() ? "yes" : "no"); timerList = CreateCurrentTimerList(); if (timerList) evaltimeList = CreateEvaluationTimeList(timerList); if (evaltimeList) failedList = CreateConflictList(evaltimeList, timerList); - if ((!localConflicts) && timerList) CreateRemoteConflictList(timerList,failedList); + if ((!localConflicts) && timerList) CreateRemoteConflictList(timerList, failedList); 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) - { - if (!localConflicts) - LogFile.Log(2,"timer '%s' (%s, channel %s) %s%s failed", (*it)->timer->File(), DAYDATETIME((*it)->timer->StartTime()), CHANNELNAME((*it)->timer->Channel()), (*it)->timer->Remote()?"@":"",(*it)->timer->Remote()?(*it)->timer->Remote():""); - else - LogFile.Log(2,"timer '%s' (%s, channel %s) failed", (*it)->timer->File(), DAYDATETIME((*it)->timer->StartTime()), CHANNELNAME((*it)->timer->Channel())); + 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) { + if (!localConflicts) + LogFile.Log(2, "timer '%s' (%s, channel %s) %s%s failed", (*it)->timer->File(), DAYDATETIME((*it)->timer->StartTime()), CHANNELNAME((*it)->timer->Channel()), (*it)->timer->Remote() ? "@" : "", (*it)->timer->Remote() ? (*it)->timer->Remote() : ""); + else + LogFile.Log(2, "timer '%s' (%s, channel %s) failed", (*it)->timer->File(), DAYDATETIME((*it)->timer->StartTime()), CHANNELNAME((*it)->timer->Channel())); + } } - } if (numConflicts > 0 && gl_timerStatusMonitor) - gl_timerStatusMonitor->SetConflictCheckAdvised(); + gl_timerStatusMonitor->SetConflictCheckAdvised(); } cList<cConflictCheckTimerObj>* cConflictCheck::CreateCurrentTimerList() { - LogFile.Log(3,"current timer list creation started"); + LogFile.Log(3, "current timer list creation started"); cList<cConflictCheckTimerObj>* CurrentTimerList = NULL; // collect single event timers time_t tMax = 0; LOCK_TIMERS_READ; const cTimer* ti = NULL; - for (ti = Timers->First(); ti; ti = Timers->Next(ti)) - { - tMax = std::max(tMax, ti->StartTime()); - if (localConflicts && ti->Remote()) continue; - if (!ti->IsSingleEvent()) continue; + for (ti = Timers->First(); ti; ti = Timers->Next(ti)) { + tMax = std::max(tMax, ti->StartTime()); + if (localConflicts && ti->Remote()) continue; + if (!ti->IsSingleEvent()) continue; // already recording? - int deviceNr = -1; - if (ti->Local()) // we check devices only for local timers - deviceNr = gl_recStatusMonitor->TimerRecDevice(ti)-1; - - // create a copy of this timer - cTimer* clone = new cTimer(*ti); - clone->SetEvent(ti->Event()); - - cConflictCheckTimerObj* timerObj = new cConflictCheckTimerObj(clone, ti->StartTime(), ti->StopTime(), deviceNr, ti->Id()); - 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); + int deviceNr = -1; + if (ti->Local()) // we check devices only for local timers + deviceNr = gl_recStatusMonitor->TimerRecDevice(ti) - 1; + + // create a copy of this timer + cTimer* clone = new cTimer(*ti); + clone->SetEvent(ti->Event()); + + cConflictCheckTimerObj* timerObj = new cConflictCheckTimerObj(clone, ti->StartTime(), ti->StopTime(), deviceNr, ti->Id()); + 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) + std::min(14,EPGSearchConfig.checkMaxDays) * SECSINDAY; + time_t maxCheck = time(NULL) + std::min(14, EPGSearchConfig.checkMaxDays) * SECSINDAY; tMax = std::max(tMax, maxCheck); - for (ti = Timers->First(); ti; ti = Timers->Next(ti)) - { - if (ti->IsSingleEvent()) continue; - if (localConflicts && ti->Remote()) continue; //JF??? - 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)) - { + for (ti = Timers->First(); ti; ti = Timers->Next(ti)) { + if (ti->IsSingleEvent()) continue; + if (localConflicts && ti->Remote()) continue; //JF??? + 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->Local() && ti->Recording()) - deviceNr = gl_recStatusMonitor->TimerRecDevice(ti)-1; + if (ti->Local() && ti->Recording()) + deviceNr = gl_recStatusMonitor->TimerRecDevice(ti) - 1; #else - if (Start + ti->StopTime() - ti->StartTime() > time(NULL)) - deviceNr = 0; + 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; - } - - // create a copy of this timer - cTimer* clone = new cTimer(*ti); - clone->SetEvent(ti->Event()); - - cConflictCheckTimerObj* timerObj = new cConflictCheckTimerObj(clone, Start, Start + ti->StopTime() - ti->StartTime(), deviceNr, ti->Id()); - 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 (deviceNr == -1) { // currently not recording, skip it + day += SECSINDAY; + continue; + } + } else if (Start < ti->StartTime()) { + day += SECSINDAY; + continue; + } + + // create a copy of this timer + cTimer* clone = new cTimer(*ti); + clone->SetEvent(ti->Event()); + + cConflictCheckTimerObj* timerObj = new cConflictCheckTimerObj(clone, Start, Start + ti->StopTime() - ti->StartTime(), deviceNr, ti->Id()); + 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(); - LogFile.Log(3,"current timer list created"); + LogFile.Log(3, "current timer list created"); 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"); + 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 (TimerObj->timer->Remote()) continue; // here we check local timers only - - 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); + for (cConflictCheckTimerObj* TimerObj = TimerList->First(); TimerObj; TimerObj = TimerList->Next(TimerObj)) { + if (!TimerObj->timer->HasFlags(tfActive)) continue; + + if (TimerObj->timer->Remote()) continue; // here we check local timers only + + 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(); + EvalTimeList->Sort(); - LogFile.Log(3,"create check time list - done"); + 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"); + LogFile.Log(3, "create conflict list"); relevantConflicts = 0; numConflicts = 0; maxCheck = time(NULL) + std::min(14, 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; - }; - } + 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.empty()) - 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 = std::min(nextRelevantConflictDate, checkTime->evaltime); - - relevantConflicts++; - allTimersIgnored = false; - break; - } - } - if (allTimersIgnored) - checkTime->ignore = true; - } - checkTime = checkTimeNext; + for (cConflictCheckTime* checkTime = EvalTimeList->First(); checkTime;) { // clear the list + cConflictCheckTime* checkTimeNext = EvalTimeList->Next(checkTime); + if (checkTime->failedTimers.empty()) + 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 = std::min(nextRelevantConflictDate, checkTime->evaltime); + + relevantConflicts++; + allTimersIgnored = false; + break; + } + } + if (allTimersIgnored) + checkTime->ignore = true; + } + checkTime = checkTimeNext; } // store for external access @@ -469,143 +432,121 @@ cList<cConflictCheckTime>* cConflictCheck::CreateConflictList(cList<cConflictChe cConflictCheckThread::m_cacheRelevantConflicts = relevantConflicts; cConflictCheckThread::m_cacheTotalConflicts = numConflicts; - LogFile.Log(3,"create conflict list - done"); + LogFile.Log(3, "create conflict list - done"); return EvalTimeList; } void cConflictCheck::CreateRemoteConflictList(cList<cConflictCheckTimerObj>* TimerList, cList<cConflictCheckTime>* failedList) { - LogFile.Log(3,"add remote conflicts to list"); - bool foundRemote = false; - cStringList RemoteHosts; - // check if we have any Remote timers - RemoteHosts.Clear(); - for(cConflictCheckTimerObj* TimerObj= TimerList->First(); TimerObj; TimerObj = TimerList->Next(TimerObj)) - { - if (!TimerObj->timer->HasFlags(tfActive)) continue; - - if (TimerObj->timer->Remote()) - { - if (RemoteHosts.Find(TimerObj->timer->Remote()) < 0) - { - foundRemote = true; - RemoteHosts.Append(strdup(TimerObj->timer->Remote())); - } - } - } - - if (!foundRemote) - { - LogFile.Log(3,"no remote timers to add"); - return; - } - - RemoteHosts.Sort(); - - cStringList Response; - // for all RemoteHosts - for (int i=0; i< RemoteHosts.Size(); i++) - { - Response.Clear(); - if (ExecSVDRPCommand(RemoteHosts[i], "PLUG epgsearch LSCC REL", &Response)) - { - for (int j = 0; j < Response.Size(); j++) - { - const char *s = Response[j]; - int Code = SVDRPCode(s); - if (Code == 901) - { - LogFile.Log(3,"conflictcheck %s no remote conflicts found",RemoteHosts[i]); - continue; - } else if (Code != 900) - { - LogFile.Log(2,"Invalid remote response %d %s", Code, - SVDRPValue(s)); - break; - } else if (const char* line = SVDRPValue(s)) - { - LogFile.Log(2,"remote conflictcheck line %s",line); - int Id,recPart; - char rest[256]; - time_t evaltime; - sscanf(line,"%ld:%d|%s",&evaltime,&Id,rest); - cConflictCheckTime* checkTime = new cConflictCheckTime(evaltime); - if (!failedList) - failedList = new cList<cConflictCheckTime>; - LogFile.Log(2,"added remote checkTime %s to failedList",DAYDATETIME(evaltime)); - failedList->Add(checkTime); - numConflicts++; - // find TimerObj with id Id in timerList - cConflictCheckTimerObj* failedTimer = NULL; - bool foundfT = false; - for(failedTimer = TimerList->First(); failedTimer; failedTimer = TimerList->Next(failedTimer)) - { - if (failedTimer->timer->Id() == Id) - { - foundfT = true; - break; - } - } - if (!foundfT) - { - LogFile.Log(2,"remote failed Timer disappeared"); - continue; - } - LogFile.Log(2,"create remote failedTimer with Id %d",Id); - failedTimer->conflCheckTime = checkTime; - failedTimer->origIndex = Id; - sscanf(rest,"%d|%s",&recPart,rest); - failedTimer->recDuration=((failedTimer->stop-failedTimer->start)* recPart / 100); - cConflictCheckTimerObj* concurrentTimer = NULL; - while (strlen(rest) > 0) - { - int n = sscanf(rest,"%d#%s",&Id,rest); - if (n < 2) - { - if (sscanf(rest,"%d",&Id) <= 0) - { - LogFile.Log(2,"error scanning rest of line %s",rest); - break; - } - *rest = 0; // TODO :<more timers> possible ?? - } - // find TimerObj itcc for with Id in timerList - bool foundcT = false; - for(concurrentTimer = TimerList->First(); concurrentTimer; concurrentTimer = TimerList->Next(concurrentTimer)) - { - if (concurrentTimer->timer->Id() == Id) - { - foundcT = true; - break; - } - } - if (!foundcT) - { - LogFile.Log(2,"remote concurrent Timer disappeared"); - continue; - } - if (!failedTimer->concurrentTimers) - failedTimer->concurrentTimers = new std::set<cConflictCheckTimerObj*,TimerObjSort>; - LogFile.Log(2,"insert remote Id %d into concurrentTimers",concurrentTimer->timer->Id()); - failedTimer->concurrentTimers->insert(concurrentTimer); - } // while concurrent Timers - LogFile.Log(2,"insert Id %d into checkTime->failedTimers",failedTimer->timer->Id()); - checkTime->failedTimers.insert(failedTimer); - relevantConflicts++; + LogFile.Log(3, "add remote conflicts to list"); + bool foundRemote = false; + cStringList RemoteHosts; + // check if we have any Remote timers + RemoteHosts.Clear(); + for (cConflictCheckTimerObj* TimerObj = TimerList->First(); TimerObj; TimerObj = TimerList->Next(TimerObj)) { + if (!TimerObj->timer->HasFlags(tfActive)) continue; + + if (TimerObj->timer->Remote()) { + if (RemoteHosts.Find(TimerObj->timer->Remote()) < 0) { + foundRemote = true; + RemoteHosts.Append(strdup(TimerObj->timer->Remote())); } - else - LogFile.Log(2,"got Code %d, but no Value from %s",Code,RemoteHosts[i]); - } // received response - } - else - { - LogFile.Log(2,"ExecSVDRPCommand failed for %s",RemoteHosts[i]); - } - } // for all RemoteHosts - cConflictCheckThread::m_cacheTotalConflicts = numConflicts; - cConflictCheckThread::m_cacheRelevantConflicts = relevantConflicts; - LogFile.Log(3,"add remote conflicts done"); + } + } + + if (!foundRemote) { + LogFile.Log(3, "no remote timers to add"); + return; + } + + RemoteHosts.Sort(); + + cStringList Response; + // for all RemoteHosts + for (int i = 0; i < RemoteHosts.Size(); i++) { + Response.Clear(); + if (ExecSVDRPCommand(RemoteHosts[i], "PLUG epgsearch LSCC REL", &Response)) { + for (int j = 0; j < Response.Size(); j++) { + const char *s = Response[j]; + int Code = SVDRPCode(s); + if (Code == 901) { + LogFile.Log(3, "conflictcheck %s no remote conflicts found", RemoteHosts[i]); + continue; + } else if (Code != 900) { + LogFile.Log(2, "Invalid remote response %d %s", Code, + SVDRPValue(s)); + break; + } else if (const char* line = SVDRPValue(s)) { + LogFile.Log(2, "remote conflictcheck line %s", line); + int Id, recPart; + char rest[256]; + time_t evaltime; + sscanf(line, "%ld:%d|%s", &evaltime, &Id, rest); + cConflictCheckTime* checkTime = new cConflictCheckTime(evaltime); + if (!failedList) + failedList = new cList<cConflictCheckTime>; + LogFile.Log(2, "added remote checkTime %s to failedList", DAYDATETIME(evaltime)); + failedList->Add(checkTime); + numConflicts++; + // find TimerObj with id Id in timerList + cConflictCheckTimerObj* failedTimer = NULL; + bool foundfT = false; + for (failedTimer = TimerList->First(); failedTimer; failedTimer = TimerList->Next(failedTimer)) { + if (failedTimer->timer->Id() == Id) { + foundfT = true; + break; + } + } + if (!foundfT) { + LogFile.Log(2, "remote failed Timer disappeared"); + continue; + } + LogFile.Log(2, "create remote failedTimer with Id %d", Id); + failedTimer->conflCheckTime = checkTime; + failedTimer->origIndex = Id; + sscanf(rest, "%d|%s", &recPart, rest); + failedTimer->recDuration = ((failedTimer->stop - failedTimer->start) * recPart / 100); + cConflictCheckTimerObj* concurrentTimer = NULL; + while (strlen(rest) > 0) { + int n = sscanf(rest, "%d#%s", &Id, rest); + if (n < 2) { + if (sscanf(rest, "%d", &Id) <= 0) { + LogFile.Log(2, "error scanning rest of line %s", rest); + break; + } + *rest = 0; // TODO :<more timers> possible ?? + } + // find TimerObj itcc for with Id in timerList + bool foundcT = false; + for (concurrentTimer = TimerList->First(); concurrentTimer; concurrentTimer = TimerList->Next(concurrentTimer)) { + if (concurrentTimer->timer->Id() == Id) { + foundcT = true; + break; + } + } + if (!foundcT) { + LogFile.Log(2, "remote concurrent Timer disappeared"); + continue; + } + if (!failedTimer->concurrentTimers) + failedTimer->concurrentTimers = new std::set<cConflictCheckTimerObj*, TimerObjSort>; + LogFile.Log(2, "insert remote Id %d into concurrentTimers", concurrentTimer->timer->Id()); + failedTimer->concurrentTimers->insert(concurrentTimer); + } // while concurrent Timers + LogFile.Log(2, "insert Id %d into checkTime->failedTimers", failedTimer->timer->Id()); + checkTime->failedTimers.insert(failedTimer); + relevantConflicts++; + } else + LogFile.Log(2, "got Code %d, but no Value from %s", Code, RemoteHosts[i]); + } // received response + } else { + LogFile.Log(2, "ExecSVDRPCommand failed for %s", RemoteHosts[i]); + } + } // for all RemoteHosts + cConflictCheckThread::m_cacheTotalConflicts = numConflicts; + cConflictCheckThread::m_cacheRelevantConflicts = relevantConflicts; + LogFile.Log(3, "add remote conflicts done"); } // checks for conflicts at one special time @@ -613,83 +554,74 @@ int cConflictCheck::ProcessCheckTime(cConflictCheckTime* checkTime) { if (!checkTime) return 0; - LogFile.Log(3,"check time %s", DAYDATETIME(checkTime->evaltime)); + LogFile.Log(3, "check time %s", DAYDATETIME(checkTime->evaltime)); - LogFile.Log(3,"detach stopping timers"); + LogFile.Log(3, "detach stopping timers"); int Conflicts = 0; // detach all stopping timers from their devices - std::set<cConflictCheckTimerObj*,TimerObjSort>::iterator it; + 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)+1, (*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 ((*it) && (*it)->device >= 0) { + LogFile.Log(3, "detach device %d from timer '%s' (%s, channel %s) at %s", ((*it)->device) + 1, (*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); + 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"); + 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+1); - 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+1); - } - else - { - AddConflict((*it), checkTime, pendingTimers); - Conflicts++; - } + 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 + 1); + 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 + 1); + } else { + AddConflict((*it), checkTime, pendingTimers); + Conflicts++; + } } - LogFile.Log(3,"check time %s - done", DAYDATETIME(checkTime->evaltime)); + LogFile.Log(3, "check time %s - done", DAYDATETIME(checkTime->evaltime)); return Conflicts; } eModuleStatus cConflictCheck::CamSlotModuleStatus(cCamSlot *CamSlot) { - if (!CamSlot) return msNone; - if ((int)camSlotStatusArray.size() != CamSlots.Count()) - for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) - camSlotStatusArray.push_back(CamSlot->ModuleStatus()); - if (CamSlot->Index() < (int)camSlotStatusArray.size()) - return camSlotStatusArray[CamSlot->Index()]; - else - return msNone; + if (!CamSlot) return msNone; + if ((int)camSlotStatusArray.size() != CamSlots.Count()) + for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) + camSlotStatusArray.push_back(CamSlot->ModuleStatus()); + if (CamSlot->Index() < (int)camSlotStatusArray.size()) + return camSlotStatusArray[CamSlot->Index()]; + else + return msNone; } int cConflictCheck::GetDevice(cConflictCheckTimerObj* TimerObj, bool* NeedsDetachReceivers) @@ -697,179 +629,175 @@ int cConflictCheck::GetDevice(cConflictCheckTimerObj* TimerObj, bool* NeedsDetac int Priority = TimerObj->timer->Priority(); const cChannel* Channel = TimerObj->timer->Channel(); - // Collect the current priorities of all CAM slots that can decrypt the 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 (CamSlotModuleStatus(CamSlot) == msReady) { - if (CamSlot->ProvidesCa(Channel->Caids())) { - if (!ChannelCamRelations.CamChecked(Channel->GetChannelID(), CamSlot->SlotNumber())) { - SlotPriority[CamSlot->Index()] = CamSlot->Priority(); - NumUsableSlots++; - } - } + for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) { + SlotPriority[CamSlot->Index()] = MAXPRIORITY + 1; // assumes it can't be used + if (CamSlotModuleStatus(CamSlot) == msReady) { + if (CamSlot->ProvidesCa(Channel->Caids())) { + if (!ChannelCamRelations.CamChecked(Channel->GetChannelID(), CamSlot->SlotNumber())) { + SlotPriority[CamSlot->Index()] = CamSlot->Priority(); + NumUsableSlots++; + } + } } - } + } #ifdef CFLC - int NumUsableSlots = 1; + int NumUsableSlots = 1; #endif - if (!NumUsableSlots) - return selDevice; // no CAM is able to decrypt this channel - } + if (!NumUsableSlots) + return selDevice; // no CAM is able to decrypt this channel + } if (NeedsDetachReceivers) - *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 basically 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; - // prefer the primary device for live viewing if we don't need to detach existing receivers - imp <<= 1; ; - // use receiving devices if we don't need to detach existing receivers - imp <<= 1; imp |= !devices[i].Receiving() || ndr; - // avoid devices that are receiving - imp <<= 1; imp |= devices[i].Receiving(); - // use the device with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used) - imp <<= 8; imp |= std::min(std::max(devices[i].Priority() + MAXPRIORITY, 0), 0xFF); - // use the CAM slot with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used) - imp <<= 8; imp |= std::min(std::max((NumUsableSlots ? SlotPriority[j] : 0) + MAXPRIORITY, 0), 0xFF); - // avoid devices if we need to detach existing receivers - imp <<= 1; imp |= ndr; - // avoid the primary device - imp <<= 1; imp |= devices[i].IsPrimaryDevice(); - // avoid cards with Common Interface for FTA channels - imp <<= 1; imp |= NumUsableSlots ? 0 : devices[i].HasCi(); - // avoid full featured cards - imp <<= 1; imp |= devices[i].HasDecoder(); - // prefer CAMs that are known to decrypt this channel - imp <<= 1; imp |= NumUsableSlots ? !ChannelCamRelations.CamDecrypt(Channel->GetChannelID(), j + 1) : 0; - if (imp < Impact) { - // This device has less impact than any previous one, so we take it. - Impact = imp; - selDevice = i; - if (NeedsDetachReceivers) - *NeedsDetachReceivers = ndr; + *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 basically 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; + // prefer the primary device for live viewing if we don't need to detach existing receivers + imp <<= 1; ; + // use receiving devices if we don't need to detach existing receivers + imp <<= 1; + imp |= !devices[i].Receiving() || ndr; + // avoid devices that are receiving + imp <<= 1; + imp |= devices[i].Receiving(); + // use the device with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used) + imp <<= 8; + imp |= std::min(std::max(devices[i].Priority() + MAXPRIORITY, 0), 0xFF); + // use the CAM slot with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used) + imp <<= 8; + imp |= std::min(std::max((NumUsableSlots ? SlotPriority[j] : 0) + MAXPRIORITY, 0), 0xFF); + // avoid devices if we need to detach existing receivers + imp <<= 1; + imp |= ndr; + // avoid the primary device + imp <<= 1; + imp |= devices[i].IsPrimaryDevice(); + // avoid cards with Common Interface for FTA channels + imp <<= 1; + imp |= NumUsableSlots ? 0 : devices[i].HasCi(); + // avoid full featured cards + imp <<= 1; + imp |= devices[i].HasDecoder(); + // prefer CAMs that are known to decrypt this channel + imp <<= 1; + imp |= NumUsableSlots ? !ChannelCamRelations.CamDecrypt(Channel->GetChannelID(), j + 1) : 0; + if (imp < Impact) { + // This device has less impact than any previous one, so we take it. + Impact = imp; + selDevice = i; + if (NeedsDetachReceivers) + *NeedsDetachReceivers = ndr; } - } - } - if (!NumUsableSlots) - break; // no CAM necessary, so just one loop over the devices - } - return selDevice; + } + } + if (!NumUsableSlots) + break; // no CAM necessary, so just one loop over the devices + } + return selDevice; } 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); + 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; + 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; + TimerObj->conflCheckTime = CheckTime; else - return; + 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())); + LogFile.Log(3, "conflict found for timer '%s' (%s, channel %s)", TimerObj->timer->File(), DAYDATETIME(TimerObj->start), CHANNELNAME(TimerObj->timer->Channel())); } bool cConflictCheck::TimerInConflict(const cTimer* timer) { if (!failedList) return false; - 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) - { - LOCK_TIMERS_READ; - for (it2 = (*it)->concurrentTimers->begin(); it2 != (*it)->concurrentTimers->end(); ++it2) - { - if ((*it2)->OrigTimer(Timers) == timer) - return true; - } - } - } - } + 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) { + LOCK_TIMERS_READ; + for (it2 = (*it)->concurrentTimers->begin(); it2 != (*it)->concurrentTimers->end(); ++it2) { + if ((*it2)->OrigTimer(Timers) == timer) + return true; + } + } + } + } } return false; } void cConflictCheck::EvaluateConflCheckCmd() { - if (strlen(EPGSearchConfig.conflCheckCmd) > 0) - { - LogFile.Log(2,"evaluating conflict check command '%s'", EPGSearchConfig.conflCheckCmd); - for(cConflictCheckTime* ct = failedList->First(); ct; ct = failedList->Next(ct)) - { - if (ct->ignore) continue; - std::set<cConflictCheckTimerObj*,TimerObjSort>::iterator it; - for (it = ct->failedTimers.begin(); it != ct->failedTimers.end(); ++it) - if ((*it) && !(*it)->ignore) - { - string result = EPGSearchConfig.conflCheckCmd; - LOCK_TIMERS_READ; - if (!(*it)->OrigTimer(Timers)) - { - LogFile.Log(3,"timer has disappeared meanwhile"); - continue; - } - else - LogFile.Log(3,"evaluating conflict check command for timer '%s' (%s, channel %s)", (*it)->timer->File(), DAYDATETIME((*it)->start), CHANNELNAME((*it)->timer->Channel())); - - if ((*it)->Event()) - { - cVarExpr varExprEvent(result); - result = varExprEvent.Evaluate((*it)->Event()); - } - cVarExpr varExprTimer(result); - result = varExprTimer.Evaluate((*it)->timer); - cVarExpr varExpr(result); - varExpr.Evaluate(); - } - } + if (strlen(EPGSearchConfig.conflCheckCmd) > 0) { + LogFile.Log(2, "evaluating conflict check command '%s'", EPGSearchConfig.conflCheckCmd); + for (cConflictCheckTime* ct = failedList->First(); ct; ct = failedList->Next(ct)) { + if (ct->ignore) continue; + std::set<cConflictCheckTimerObj*, TimerObjSort>::iterator it; + for (it = ct->failedTimers.begin(); it != ct->failedTimers.end(); ++it) + if ((*it) && !(*it)->ignore) { + string result = EPGSearchConfig.conflCheckCmd; + LOCK_TIMERS_READ; + if (!(*it)->OrigTimer(Timers)) { + LogFile.Log(3, "timer has disappeared meanwhile"); + continue; + } else + LogFile.Log(3, "evaluating conflict check command for timer '%s' (%s, channel %s)", (*it)->timer->File(), DAYDATETIME((*it)->start), CHANNELNAME((*it)->timer->Channel())); + + if ((*it)->Event()) { + cVarExpr varExprEvent(result); + result = varExprEvent.Evaluate((*it)->Event()); + } + cVarExpr varExprTimer(result); + result = varExprTimer.Evaluate((*it)->timer); + cVarExpr varExpr(result); + varExpr.Evaluate(); + } + } } } |