/*
* Copyright 2014 Canonical Ltd.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3, as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranties of
* MERCHANTABILITY, SATISFACTORY QUALITY, 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, see .
*
* Authors:
* Charles Kerr
*/
#include
#include
namespace unity {
namespace indicator {
namespace datetime {
/***
**** Public API
***/
SimpleAlarmQueue::SimpleAlarmQueue(const std::shared_ptr& clock,
const std::shared_ptr& planner,
const std::shared_ptr& timer):
m_clock(clock),
m_planner(planner),
m_timer(timer),
m_datetime(clock->localtime())
{
m_planner->appointments().changed().connect([this](const std::vector&){
g_debug("AlarmQueue %p calling requeue() due to appointments changed", this);
requeue();
});
m_clock->minute_changed.connect([=]{
const auto now = m_clock->localtime();
constexpr auto skew_threshold_usec = int64_t{90} * G_USEC_PER_SEC;
const bool clock_jumped = std::abs(now - m_datetime) > skew_threshold_usec;
m_datetime = now;
if (clock_jumped) {
g_debug("AlarmQueue %p calling requeue() due to clock skew", this);
requeue();
}
});
m_timer->timeout().connect([this](){
g_debug("AlarmQueue %p calling requeue() due to timeout", this);
requeue();
});
requeue();
}
SimpleAlarmQueue::~SimpleAlarmQueue()
{
}
core::Signal& SimpleAlarmQueue::alarm_reached()
{
return m_alarm_reached;
}
/***
****
***/
void SimpleAlarmQueue::requeue()
{
// kick any current alarms
for (auto current : find_current_alarms())
{
const std::pair trig {current.uid, current.begin};
m_triggered.insert(trig);
m_alarm_reached(current);
}
// idle until the next alarm
Appointment next;
if (find_next_alarm(next))
{
g_debug ("setting timer to wake up for next appointment '%s' at %s",
next.summary.c_str(),
next.begin.format("%F %T").c_str());
m_timer->set_wakeup_time(next.begin);
}
}
// find the next alarm that will kick now or in the future
bool SimpleAlarmQueue::find_next_alarm(Appointment& setme) const
{
bool found = false;
Appointment tmp;
const auto now = m_clock->localtime();
const auto beginning_of_minute = now.add_full (0, 0, 0, 0, 0, -now.seconds());
const auto appointments = m_planner->appointments().get();
g_debug ("planner has %zu appointments in it", (size_t)appointments.size());
for(const auto& walk : appointments)
{
const std::pair trig {walk.uid, walk.begin};
if (m_triggered.count(trig))
continue;
if (walk.begin < beginning_of_minute) // has this one already passed?
continue;
if (found && (tmp.begin < walk.begin)) // do we already have a better match?
continue;
tmp = walk;
found = true;
}
if (found)
setme = tmp;
return found;
}
// find the alarm(s) that should kick right now
std::vector SimpleAlarmQueue::find_current_alarms() const
{
std::vector appointments;
const auto now = m_clock->localtime();
for(const auto& walk : m_planner->appointments().get())
{
const std::pair trig {walk.uid, walk.begin};
if (m_triggered.count(trig)) // did we already use this one?
continue;
if (!DateTime::is_same_minute(now, walk.begin))
continue;
appointments.push_back(walk);
}
return appointments;
}
/***
****
***/
} // namespace datetime
} // namespace indicator
} // namespace unity