]> ToastFreeware Gitweb - gregoa/zavai.git/blobdiff - src/clock.vala
Mark log entry as acked when acking alarms
[gregoa/zavai.git] / src / clock.vala
index 963638f4184a751937bf4a358a47309cb3757f68..b55d3b250c36589a60839ac5bd619d2124f21172 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * 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
@@ -29,29 +29,183 @@ public enum SourceType
     GPS
 }
 
+/*
+TODO: schedule alarms via at
+
+Uses the 'z' queue.
+
+atq -q z  can be used to list the jobs (for example, at startup, or after a job has run)
+at -c id  can be used to query a job, parsing its contents (which can have
+          comments or variables being set)
+zavai --notify ...  can be used to notify the job (and start zavai if it's not running)
+
+Alarm needs to be able to serialize itself to an at invocation and to
+deserialize itself from the output of at -c
+
+Alarm needs to deserialize also a job with no special markers whatsoever: a
+generic at job.
+
+
+
+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)
+    }
+}
+
+at clock constructor: refresh_alarms()
+inotifywait -e close /usr/bin/at -> refresh_alarms()  (shows when someone has just used at)
+  (can be skipped if we don't need to support non-zavai alarms)
+at alarm triggered through zavai: refresh_alarms()
+
+
+*/
+
+
 public class Alarm : Object
 {
-    public signal void trigger();
+    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);
+    }
 
-    public time_t deadline;
+    // 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;
+    }
+}
+
+[DBus (name = "org.enricozini.zavai.Alarm")]
+public class ZavaiClock : Object {
+    public void Notify (string label) {
+        clock.notify_alarm(label);
+    }
+}
+
+public class AlarmTriggerInfo
+{
+    public zavai.log.Log log;
     public string label;
+    public bool acked;
+    public bool canceled;
 
-    public Alarm(time_t deadline, string label)
+    public AlarmTriggerInfo(string label)
     {
-        this.deadline = deadline;
         this.label = label;
+        acked = false;
+        canceled = false;
     }
 }
 
-private int alarm_compare(void* a, void* b)
+public class AlarmTriggerQueue : zavai.Service
 {
-    return (int)(((Alarm*)a)->deadline - ((Alarm*)b)->deadline);
-}
+    protected List<AlarmTriggerInfo> queue;
+
+    public signal void triggered(AlarmTriggerInfo info);
+    public signal void acked(AlarmTriggerInfo info);
+    public signal void canceled(AlarmTriggerInfo info);
 
-[DBus (name = "org.freesmartphone.Notification")]
-public class TestAlarmListener : Object {
-    public void Alarm () {
-stderr.printf("GOT ALARMAMAMAMAM!\n");        
+    public AlarmTriggerQueue()
+    {
+        queue = new List<AlarmTriggerInfo>();
+    }
+
+    public void enqueue_trigger(AlarmTriggerInfo info)
+    {
+        // Reuse IDs from the associated logger object
+        info.log = zavai.log.log.start("alarm", "Alarm " + info.label);
+        queue.append(info);
+        if (queue.data == info)
+            triggered(queue.data);
+    }
+
+    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 != queue.data) return;
+        if (!info.acked && !info.canceled)
+        {
+            info.acked = true;
+            acked(info);
+            info.log.add("alarm acknowledged");
+            info.log.acked = true;
+            zavai.log.log.end(info.log);
+        }
+        done_with_first();
+    }
+
+    public void cancel(AlarmTriggerInfo info)
+    {
+        if (queue == null || info != queue.data) return;
+        if (!info.acked && !info.canceled)
+        {
+            info.canceled = true;
+            canceled(info);
+            info.log.add("alarm canceled");
+            zavai.log.log.end(info.log);
+        }
+        done_with_first();
     }
 }
 
@@ -60,101 +214,74 @@ 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 TestAlarmListener listener;
+    protected ZavaiClock dbusClock;
 
-       protected dynamic DBus.Object otimed_alarm;
-       protected dynamic DBus.Object notification;
+    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()
-       {
-               name = "clock";
+    public Clock()
+    {
+        Object(name: "clock");
         alarms = null;
-        listener = new TestAlarmListener();
+        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;
-        
-               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");
-        if (otimed_alarm == null)
-            zavai.log.error("ALARM IS NULL");
+        otimed_alarm = zavai.registry.sbus.get_object(
+                "org.freesmartphone.otimed",
+                "/org/freesmartphone/Time/Alarm",
+                "org.freesmartphone.Time.Alarm");
 
-        zavai.registry.sbus.register_object("/", listener);
+        rtc = zavai.registry.sbus.get_object(
+                "org.freesmartphone.odeviced",
+                "/org/freesmartphone/Device/RTC/0",
+                "org.freesmartphone.Device.RealtimeClock");
 
-        //notification = zavai.registry.sbus.get_object(
-        //    "org.freesmartphone",
-        //    "org/freesmartphone/Notification",
-        //    "org.freesmartphone.Notification");
-        //notification.Alarm += on_alarm;
-       }
-
-    public void schedule(Alarm a)
-    {
-        alarms.insert_sorted(a, alarm_compare);
-        zavai.log.info("Next alarm: " + alarms.data.label + " at " + Time.local(alarms.data.deadline).to_string());
-        otimed_reschedule();
+        zavai.registry.sbus.register_object("/org/enricozini/Zavai/Clock", dbusClock);
     }
 
-    private void otimed_reschedule()
+    public void notify_alarm(string label)
     {
-        stderr.printf("ALARM: %p BN %s\n", otimed_alarm, zavai.registry.bus_name);
-        if (alarms != null)
-        {
-            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);
-            }
-        }
+        stderr.printf("Notifying %s\n", label);
+        AlarmTriggerInfo info = new AlarmTriggerInfo(label);
+        alarm_trigger_queue.enqueue_trigger(info);
+        schedule_changed(next_alarm());
     }
 
-    private void on_alarm()
+    public Alarm? next_alarm()
     {
-stderr.printf("ON ALARM\n");
-        check_alarms();
+        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 check_alarms()
+    public void schedule(string timespec, string label) throws Error
     {
-        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();
-        }
-
-        otimed_reschedule();
+        Alarm.schedule(timespec, label);
+        schedule_changed(next_alarm());
     }
 
-       private void on_gps_time(dynamic DBus.Object pos, int t)
-       {
+    private void on_gps_time(uint t)
+    {
         if (t == 0)
         {
             last_gps_time_system_time = 0;
@@ -164,7 +291,7 @@ stderr.printf("ON ALARM\n");
             last_gps_time_system_time = time_t();
             update_time();
         }
-       }
+    }
 
     private bool on_system_time()
     {
@@ -192,37 +319,37 @@ stderr.printf("ON ALARM\n");
         }
     }
 
-       /// Request GPS resource
-       public override void start()
-       {
-               if (started) return;
+    /// 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;
+        zavai.gps.gps.time_changed += on_gps_time;
         last_system_time = time_t();
         update_time();
 
-               base.start();
-       }
+        base.start();
+    }
 
-       public override void stop()
-       {
-               if (!started) return;
+    public override void stop()
+    {
+        if (!started) return;
 
         Source.remove(system_time_timeout);
-               gps_time.TimeChanged -= on_gps_time;
+        zavai.gps.gps.time_changed -= on_gps_time;
 
-               base.stop();
-       }
+        base.stop();
+    }
 }
 
 public Clock clock = null;
+public AlarmTriggerQueue alarm_trigger_queue = null;
 
 public void init()
 {
     clock = new Clock();
-
-       zavai.registry.register_service(clock);
+    alarm_trigger_queue = new AlarmTriggerQueue();
 }
 
 }