Log alarms using new GPX logger
[gregoa/zavai.git] / src / log.vala
1 /*
2  * log - logging functions
3  *
4  * Copyright (C) 2009--2010  Enrico Zini <enrico@enricozini.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 using GLib;
22
23 namespace zavai {
24 namespace log {
25
26 public class Waypoint : Object
27 {
28     public time_t ts;
29     public double lat;
30     public double lon;
31
32     public Waypoint()
33     {
34         if (gps.gps.fix_status() != libgps.STATUS_NO_FIX)
35         {
36             lat = gps.gps.info().fix.latitude;
37             lon = gps.gps.info().fix.longitude;
38             ts = (time_t)gps.gps.info().fix.time;
39         } else {
40             // Use 1000 as missing values
41             lat = 1000;
42             lon = 1000;
43             ts = time_t();
44         }
45     }
46
47     public void writeInside(FileStream outfd)
48     {
49         var t = Time.gm(ts);
50         outfd.printf("   <time>%s</time>\n", t.format("%Y-%m-%dT%H:%M:%SZ"));
51     }
52 }
53
54
55 public class LogEntry : Waypoint
56 {
57     public string msg;
58
59     public LogEntry()
60     {
61         Object();
62     }
63
64     public void write(FileStream outfd)
65     {
66         outfd.printf("   <wpt lat=\"%f\" lon=\"%f\">\n", lat, lon);
67         writeInside(outfd);
68         outfd.puts("   </wpt>\n");
69     }
70 }
71
72 public class TrackEntry : Waypoint
73 {
74     public TrackEntry()
75     {
76         Object();
77     }
78
79     public void write(FileStream outfd)
80     {
81         outfd.printf("   <trkpt lat=\"%f\" lon=\"%f\">\n", lat, lon);
82         writeInside(outfd);
83         outfd.puts("   </trkpt>\n");
84     }
85 }
86
87
88 public class Log : Object
89 {
90     public uint id;
91     public string tag;
92     public List<LogEntry> entries;
93     public List<TrackEntry> track;
94
95     public Log(uint id, string tag)
96     {
97         this.id = id;
98         this.tag = tag;
99         entries = null;
100         track = null;
101     }
102
103     public void add(string msg)
104     {
105         var entry = new LogEntry();
106         entry.msg = msg;
107         entries.append(entry);
108     }
109
110     public void save()
111     {
112         if (entries == null) return;
113
114         // Directory where we save the log
115         string dir = config.homedir + "/log-" + tag;
116         DirUtils.create(dir, 0777);
117
118         // First try with a plain name
119         var t = Time.local(entries.data.ts);
120         string basename = dir + "/" + t.format("%Y%m%d-%H%M%S");
121
122         string pathname = basename + ".gpx";
123
124         // Find a pathname that does not exist already
125         for (int i = 1; FileUtils.test(pathname, FileTest.EXISTS); ++i)
126             pathname = "%s-%d.gpx".printf(basename, i);
127
128 stderr.printf("WRITING TO %s\n", pathname);
129
130         // Write out
131         var outfd = FileStream.open(pathname, "w");
132         if (outfd == null)
133         {
134             zavai.log.error("opening " + pathname + ": " + strerror(errno));
135             return;
136         }
137
138         write(outfd);
139         outfd.flush();
140     }
141
142     protected void writeTrack(FileStream outfd)
143     {
144         outfd.puts("   <trk>\n");
145         outfd.puts("     <trkseg>\n");
146         for (weak List<TrackEntry> i = track; i != null; i = i.next)
147             i.data.write(outfd);
148         outfd.puts("     </trkseg>\n");
149         outfd.puts("   </trk>\n");
150     }
151
152     protected void writeEntries(FileStream outfd)
153     {
154         outfd.puts("   <wpt>\n");
155         for (weak List<LogEntry> i = entries; i != null; i = i.next)
156             i.data.write(outfd);
157         outfd.puts("   </wpt>\n");
158     }
159
160     protected void write(FileStream outfd)
161     {
162         outfd.puts("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
163         outfd.puts("<gpx version=\"1.0\"\n");
164         outfd.printf("     creator=\"zavai %s\"\n", zavai.config.version);
165         outfd.puts("     xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n");
166         outfd.puts("     xmlns=\"http://www.topografix.com/GPX/1/0\"\n");
167         outfd.puts("     xsi:schemaLocation=\"http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd\">\n");
168         if (track != null) writeTrack(outfd);
169         if (entries != null) writeEntries(outfd);
170         outfd.puts(" </gpx>\n");
171     }
172 }
173
174 public class Logger : Resource, Object
175 {
176     protected List<Log> logs;
177     protected uint seq;
178
179     public Logger()
180     {
181         logs = null;
182         seq = 0;
183
184         zavai.registry.register(this);
185     }
186
187     protected uint gen_seq()
188     {
189         // Increase avoiding 0 on rollover
190         while (true)
191         {
192             if (++seq == 0) ++seq;
193             bool found = false;
194             for (weak List<Log> i = logs; i != null; i = i.next)
195             {
196                 if (i.data.id == seq)
197                 {
198                     found = true;
199                     break;
200                 }
201             }
202             if (!found) break;
203         }
204         return seq;
205     }
206
207     protected weak Log? find(uint id)
208     {
209         for (weak List<Log> i = logs; i != null; i = i.next)
210             if (i.data.id == id)
211                 return i.data;
212         return null;
213     }
214
215     protected Log? pop(uint id)
216     {
217         for (weak List<Log> i = logs; i != null; i = i.next)
218             if (i.data.id == id)
219             {
220                 Log res = i.data;
221                 logs.delete_link(i);
222                 return res;
223             }
224         return null;
225     }
226
227     public uint start(string tag)
228     {
229         uint id = gen_seq();
230         logs.append(new Log(id, tag));
231         return id;
232     }
233
234     public void add(uint id, string msg)
235     {
236         Log log = find(id);
237         if (log == null) return;
238         log.add(msg);
239     }
240
241     public void end(uint id)
242     {
243         Log log = pop(id);
244         log.save();
245     }
246
247     public void instant(string tag, string msg)
248     {
249         var log = new Log(0, tag);
250         log.add(msg);
251         log.save();
252     }
253
254     public void shutdown()
255     {
256         while (logs != null)
257         {
258             var log = pop(logs.data.id);
259             log.save();
260         }
261     }
262 }
263
264 public void error(string s)
265 {
266         stderr.printf("%s\n", s);
267 }
268 public void warning(string s)
269 {
270         stderr.printf("%s\n", s);
271 }
272 public void info(string s)
273 {
274         stderr.printf("%s\n", s);
275 }
276 public void debug(string s)
277 {
278         stderr.printf("%s\n", s);
279 }
280
281 Logger log = null;
282
283 public void init()
284 {
285     log = new Logger();
286 }
287
288 }
289 }