/*
* clock - clock resource for zavai
*
- * Copyright (C) 2009 Enrico Zini <enrico@enricozini.org>
+ * Copyright (C) 2009--2010 Enrico Zini <enrico@enricozini.org>
*
* 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
public enum SourceType
{
- SYSTEM,
- GPS
+ SYSTEM,
+ GPS
}
/*
refresh_alarms()
{
- oldtime = next_alarm ? next_alarm.time : 0
- next_alarm = the first alarm from atq
- if (oldtime != next_alarm.time)
- {
- remove existing triggers
- (triggers can be skipped if we don't need to support non-zavai alarms)
- schedule a trigger calling refresh_alarms() at next_alarm.time + 30 seconds
- (triggers can be skipped if we don't need to support non-zavai alarms)
- }
+ oldtime = next_alarm ? next_alarm.time : 0
+ next_alarm = the first alarm from atq
+ if (oldtime != next_alarm.time)
+ {
+ remove existing triggers
+ (triggers can be skipped if we don't need to support non-zavai alarms)
+ schedule a trigger calling refresh_alarms() at next_alarm.time + 30 seconds
+ (triggers can be skipped if we don't need to support non-zavai alarms)
+ }
}
at clock constructor: refresh_alarms()
public class Alarm : Object
{
- // TODO old stuff
- // Notify of an alarm being triggered
- public signal void trigger(Alarm a);
-
- public time_t deadline;
- public string label;
-
- public Alarm(time_t deadline, string label)
- {
- this.deadline = deadline;
- this.label = label;
- }
-
-
- // TODO new stuff
-
- // Schedule with at
- public static void schedule(string timespec, string label) throws Error
- {
- string argv[4];
- argv[0] = "/usr/bin/at";
- argv[1] = timespec;
- argv[2] = null;
-
- Pid pid;
- int stdinfd;
-
- if (!Process.spawn_async_with_pipes("/", argv, null, SpawnFlags.STDERR_TO_DEV_NULL, null, out pid, out stdinfd, null, null))
- return;
-
- {
- FileStream fs = FileStream.fdopen(stdinfd, "w");
- fs.printf("# Zavai variables start here\n");
- fs.printf("ZAVAI_LABEL=\"%s\"\n", label.escape(""));
- fs.printf("# Zavai commands starts here\n");
- fs.printf("echo \"$ZAVAI_LABEL\" | zavai --notify");
- }
-
- Process.close_pid(pid);
- }
-
- // Get the label of the job with the given at ID
- public static string? getLabel(int atID)
- {
- string label = null;
- at.jobContents(atID, fd => {
- FileStream fs = FileStream.fdopen(fd, "r");
- while (true)
- {
- string? line = fs.read_line();
- if (line == null) break;
- if (line.has_prefix("ZAVAI_LABEL=\""))
- {
- size_t size = line.size();
- if (size < 15) continue;
- label = line.substring(13, (long)(size - 15));
- break;
- }
- }
- return true;
- });
- return label;
- }
+ public at.Event ev;
+ public string label;
+
+ // Schedule with at
+ public static void schedule(string timespec, string label) throws Error
+ {
+ string argv[5];
+ argv[0] = "/usr/bin/at";
+ argv[1] = "-q";
+ argv[2] = "z";
+ argv[3] = timespec;
+ argv[4] = null;
+
+ Pid pid;
+ int stdinfd;
+
+ if (!Process.spawn_async_with_pipes("/", argv, null, SpawnFlags.STDERR_TO_DEV_NULL, null, out pid, out stdinfd, null, null))
+ return;
+
+ {
+ FileStream fs = FileStream.fdopen(stdinfd, "w");
+ string display = GLib.Environment.get_variable("DISPLAY");
+ if (display != null)
+ fs.printf("DISPLAY=\"%s\"; export DISPLAY\n", display);
+ fs.printf("# Zavai variables start here\n");
+ fs.printf("ZAVAI_LABEL=\"%s\"\n", label.escape(""));
+ fs.printf("# Zavai commands starts here\n");
+ fs.printf("%s notify \"$ZAVAI_LABEL\"", zavai.config.argv0);
+ }
+
+ Process.close_pid(pid);
+ }
+
+ // Get the label of the job with the given at ID
+ public static string? getLabel(int atID)
+ {
+ string label = null;
+ at.jobContents(atID, fd => {
+ FileStream fs = FileStream.fdopen(fd, "r");
+ while (true)
+ {
+ string? line = fs.read_line();
+ if (line == null) break;
+ if (line.has_prefix("ZAVAI_LABEL=\""))
+ {
+ size_t size = line.size();
+ if (size < 15) continue;
+ label = line.substring(13, (long)(size - 14));
+ label = label.compress();
+ break;
+ }
+ }
+ return true;
+ });
+ return label;
+ }
}
-private int alarm_compare(void* a, void* b)
+[DBus (name = "org.enricozini.zavai.Alarm")]
+public class ZavaiClock : Object {
+ public void Notify (string label) {
+ clock.notify_alarm(label);
+ }
+}
+
+public class AlarmTriggerInfo
{
- return (int)(((Alarm*)a)->deadline - ((Alarm*)b)->deadline);
+ public uint id;
+ public string label;
+ public bool acked;
+ public bool canceled;
+
+ public AlarmTriggerInfo(string label)
+ {
+ id = 0;
+ this.label = label;
+ acked = false;
+ canceled = false;
+ }
}
-[DBus (name = "org.freesmartphone.Notification")]
-public class AlarmNotification : Object {
- public void Alarm () {
- clock.check_alarms();
- }
+public class AlarmTriggerQueue : zavai.Service
+{
+ protected List<AlarmTriggerInfo> queue;
+
+ public signal void triggered(AlarmTriggerInfo info);
+ public signal void acked(AlarmTriggerInfo info);
+ public signal void canceled(AlarmTriggerInfo info);
+
+ public AlarmTriggerQueue()
+ {
+ queue = new List<AlarmTriggerInfo>();
+ }
+
+ public uint enqueue_trigger(AlarmTriggerInfo info)
+ {
+ // Reuse IDs from the associated logger object
+ info.id = zavai.log.log.start("alarm", "Alarm " + info.label);
+ queue.append(info);
+ if (queue.data.id == info.id)
+ triggered(queue.data);
+ return info.id;
+ }
+
+ protected void done_with_first()
+ {
+ var first = queue.data;
+ queue.remove_link(queue);
+ if (queue != null)
+ triggered(queue.data);
+ }
+
+ public void ack(AlarmTriggerInfo info)
+ {
+ if (queue == null || info.id != queue.data.id) return;
+ if (!info.acked && !info.canceled)
+ {
+ info.acked = true;
+ acked(info);
+ zavai.log.log.add(info.id, "alarm acknowledged");
+ zavai.log.log.end(info.id);
+ }
+ done_with_first();
+ }
+
+ public void cancel(AlarmTriggerInfo info)
+ {
+ if (queue == null || info.id != queue.data.id) return;
+ if (!info.acked && !info.canceled)
+ {
+ info.canceled = true;
+ canceled(info);
+ zavai.log.log.add(info.id, "alarm canceled");
+ zavai.log.log.end(info.id);
+ }
+ done_with_first();
+ }
}
public class Clock: zavai.Service
{
- protected time_t last_gps_time;
- protected time_t last_gps_time_system_time;
- protected time_t last_system_time;
- protected dynamic DBus.Object gps_time;
- protected uint system_time_timeout;
- protected time_t last_minute;
- protected time_t chosen_time;
- protected SourceType chosen_type;
- protected AlarmNotification listener;
-
- protected dynamic DBus.Object otimed_alarm;
- protected dynamic DBus.Object rtc;
- protected SList<Alarm> alarms;
-
- // Ticks once a minute
- public signal void minute_changed(long time, SourceType source);
- public signal void schedule_changed();
-
- public Clock()
- {
- Object(name: "clock");
- alarms = null;
- listener = new AlarmNotification();
- last_minute = 0;
- last_gps_time = 0;
- last_gps_time_system_time = 0;
- last_system_time = time_t();
- chosen_time = last_system_time;
-
- gps_time = zavai.registry.sbus.get_object(
- "org.freesmartphone.ogpsd",
- "/org/freedesktop/Gypsy",
- "org.freedesktop.Gypsy.Time");
-
- // FSO alarm system
- otimed_alarm = zavai.registry.sbus.get_object(
- "org.freesmartphone.otimed",
- "/org/freesmartphone/Time/Alarm",
- "org.freesmartphone.Time.Alarm");
-
- rtc = zavai.registry.sbus.get_object(
- "org.freesmartphone.odeviced",
- "/org/freesmartphone/Device/RTC/0",
- "org.freesmartphone.Device.RealtimeClock");
-
- zavai.registry.sbus.register_object("/", listener);
-
- }
-
- public Alarm? next_alarm()
- {
- if (alarms == null)
- return null;
- return alarms.data;
- }
-
- public void schedule(Alarm a)
- {
- alarms.insert_sorted(a, alarm_compare);
- otimed_reschedule();
- }
-
- private void otimed_reschedule()
- {
- if (alarms != null)
- {
- zavai.log.info("Scheduling next alarm: " + alarms.data.label + " at " + Time.local(alarms.data.deadline).to_string());
- zavai.log.info("Scheduling at abs " + "%d".printf((int)alarms.data.deadline));
-
- try {
- otimed_alarm.ClearAlarm(zavai.registry.bus_name);
- } catch (Error e) {
- zavai.log.error("Cannot clear alarms: " + e.message);
- }
- try {
- otimed_alarm.SetAlarm(zavai.registry.bus_name, (int)alarms.data.deadline);
- } catch (Error e) {
- zavai.log.error("Cannot reschedule alarms: " + e.message);
- }
-
- int t = rtc.GetCurrentTime();
- stderr.printf("Current time: %d, RTC time: %d\n", (int)time_t(), t);
- t = rtc.GetWakeupTime();
- stderr.printf("Scheduled alarm: %d, RTC wakeup time: %d\n", (int)alarms.data.deadline, t);
- } else
- zavai.log.info("No alarms left to reschedule");
- schedule_changed();
- }
-
- public void check_alarms()
- {
- last_system_time = time_t();
- update_time();
- while (alarms != null && alarms.data.deadline <= chosen_time)
- {
- Alarm a = alarms.data;
- alarms.remove(a);
- zavai.log.info("Triggering " + a.label);
- a.trigger(a);
- }
-
- otimed_reschedule();
- }
-
- private void on_gps_time(dynamic DBus.Object pos, int t)
- {
- if (t == 0)
- {
- last_gps_time_system_time = 0;
- update_time();
- } else {
- last_gps_time = (time_t)t;
- last_gps_time_system_time = time_t();
- update_time();
- }
- }
-
- private bool on_system_time()
- {
- last_system_time = time_t();
- update_time();
- return true;
- }
-
- private void update_time()
- {
- if (last_gps_time_system_time + 10 > last_system_time)
- {
- chosen_time = last_gps_time;
- chosen_type = SourceType.GPS;
- }
- else
- {
- chosen_time = last_system_time;
- chosen_type = SourceType.SYSTEM;
- }
- if (chosen_time / 60 != last_minute)
- {
- last_minute = chosen_time / 60;
- minute_changed(chosen_time, chosen_type);
- }
- }
-
- /// Request GPS resource
- public override void start()
- {
- if (started) return;
-
- system_time_timeout = Timeout.add(5000, on_system_time);
- gps_time.TimeChanged += on_gps_time;
- last_system_time = time_t();
- update_time();
-
- base.start();
- }
-
- public override void stop()
- {
- if (!started) return;
-
- Source.remove(system_time_timeout);
- gps_time.TimeChanged -= on_gps_time;
-
- base.stop();
- }
+ protected time_t last_gps_time;
+ protected time_t last_gps_time_system_time;
+ protected time_t last_system_time;
+ protected uint system_time_timeout;
+ protected time_t last_minute;
+ protected time_t chosen_time;
+ protected SourceType chosen_type;
+ protected ZavaiClock dbusClock;
+
+ protected dynamic DBus.Object otimed_alarm;
+ protected dynamic DBus.Object rtc;
+ protected SList<Alarm> alarms;
+
+ // Ticks once a minute
+ public signal void minute_changed(long time, SourceType source);
+ public signal void schedule_changed(Alarm? next);
+
+ public Clock()
+ {
+ Object(name: "clock");
+ alarms = null;
+ dbusClock = new ZavaiClock();
+ last_minute = 0;
+ last_gps_time = 0;
+ last_gps_time_system_time = 0;
+ last_system_time = time_t();
+ chosen_time = last_system_time;
+
+ // FSO alarm system
+ otimed_alarm = zavai.registry.sbus.get_object(
+ "org.freesmartphone.otimed",
+ "/org/freesmartphone/Time/Alarm",
+ "org.freesmartphone.Time.Alarm");
+
+ rtc = zavai.registry.sbus.get_object(
+ "org.freesmartphone.odeviced",
+ "/org/freesmartphone/Device/RTC/0",
+ "org.freesmartphone.Device.RealtimeClock");
+
+ zavai.registry.sbus.register_object("/org/enricozini/Zavai/Clock", dbusClock);
+ }
+
+ public void notify_alarm(string label)
+ {
+ stderr.printf("Notifying %s\n", label);
+ AlarmTriggerInfo info = new AlarmTriggerInfo(label);
+ alarm_trigger_queue.enqueue_trigger(info);
+ schedule_changed(next_alarm());
+ }
+
+ public Alarm? next_alarm()
+ {
+ at.Event ev;
+ ev = at.earliestID("z");
+ if (ev.deadline == 0)
+ return null;
+ string label = Alarm.getLabel(ev.id);
+ Alarm res = new Alarm();
+ res.ev = ev;
+ res.label = label;
+ return res;
+ }
+
+ public void schedule(string timespec, string label) throws Error
+ {
+ Alarm.schedule(timespec, label);
+ schedule_changed(next_alarm());
+ }
+
+ private void on_gps_time(uint t)
+ {
+ if (t == 0)
+ {
+ last_gps_time_system_time = 0;
+ update_time();
+ } else {
+ last_gps_time = (time_t)t;
+ last_gps_time_system_time = time_t();
+ update_time();
+ }
+ }
+
+ private bool on_system_time()
+ {
+ last_system_time = time_t();
+ update_time();
+ return true;
+ }
+
+ private void update_time()
+ {
+ if (last_gps_time_system_time + 10 > last_system_time)
+ {
+ chosen_time = last_gps_time;
+ chosen_type = SourceType.GPS;
+ }
+ else
+ {
+ chosen_time = last_system_time;
+ chosen_type = SourceType.SYSTEM;
+ }
+ if (chosen_time / 60 != last_minute)
+ {
+ last_minute = chosen_time / 60;
+ minute_changed(chosen_time, chosen_type);
+ }
+ }
+
+ /// Request GPS resource
+ public override void start()
+ {
+ if (started) return;
+
+ system_time_timeout = Timeout.add(5000, on_system_time);
+ zavai.gps.gps.time_changed += on_gps_time;
+ last_system_time = time_t();
+ update_time();
+
+ base.start();
+ }
+
+ public override void stop()
+ {
+ if (!started) return;
+
+ Source.remove(system_time_timeout);
+ zavai.gps.gps.time_changed -= on_gps_time;
+
+ base.stop();
+ }
}
public Clock clock = null;
+public AlarmTriggerQueue alarm_trigger_queue = null;
public void init()
{
- clock = new Clock();
-
- zavai.registry.register_service(clock);
+ clock = new Clock();
+ alarm_trigger_queue = new AlarmTriggerQueue();
}
}