Draft gpx-based logging module
[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>", 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\">", lat, lon);
67         writeInside(outfd);
68         outfd.puts("   </wpt>");
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\">", lat, lon);
82         writeInside(outfd);
83         outfd.puts("   </trkpt>");
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
117         // First try with a plain name
118         var t = Time.local(entries.data.ts);
119         string basename = t.format("%Y%m%d-%H%M%S");
120
121         string name = basename + ".gpx";
122
123         // Find a pathname that does not exist already
124         string pathname = dir + "/" + name;
125         for (int i = 1; FileUtils.test(dir + "/" + name, FileTest.EXISTS); ++i)
126             name = "%s-%d.gpx".printf(basename, i);
127
128         // Write out
129         var outfd = FileStream.open(pathname, "w");
130         write(outfd);
131         outfd.flush();
132     }
133
134     protected void writeTrack(FileStream outfd)
135     {
136         outfd.puts("   <trk>");
137         outfd.puts("     <trkseg>");
138         for (weak List<TrackEntry> i = track; i != null; i = i.next)
139             i.data.write(outfd);
140         outfd.puts("     </trkseg>");
141         outfd.puts("   </trk>");
142     }
143
144     protected void writeEntries(FileStream outfd)
145     {
146         outfd.puts("   <wpt>");
147         for (weak List<LogEntry> i = entries; i != null; i = i.next)
148             i.data.write(outfd);
149         outfd.puts("   </wpt>");
150     }
151
152     protected void write(FileStream outfd)
153     {
154         outfd.puts("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
155         outfd.puts(" <gpx");
156         outfd.puts("     version=\"1.0\"");
157         outfd.printf("     creator=\"zavai %s\"\n", zavai.config.version);
158         outfd.puts("     xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
159         outfd.puts("     xmlns=\"http://www.topografix.com/GPX/1/0\"");
160         outfd.puts("     xsi:schemaLocation=\"http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd\">");
161         if (track != null) writeTrack(outfd);
162         if (entries != null) writeEntries(outfd);
163         outfd.puts(" </gpx>");
164     }
165 }
166
167 public class Logger : Resource, Object
168 {
169     protected List<Log> logs;
170     protected uint seq;
171
172     public Logger()
173     {
174         logs = null;
175         seq = 0;
176
177         zavai.registry.register(this);
178     }
179
180     protected uint gen_seq()
181     {
182         // Increase avoiding 0 on rollover
183         while (true)
184         {
185             if (++seq == 0) ++seq;
186             bool found = false;
187             for (weak List<Log> i = logs; i != null; i = i.next)
188             {
189                 if (i.data.id == seq)
190                 {
191                     found = true;
192                     break;
193                 }
194             }
195             if (!found) break;
196         }
197         return seq;
198     }
199
200     protected weak Log? find(uint id)
201     {
202         for (weak List<Log> i = logs; i != null; i = i.next)
203             if (i.data.id == id)
204                 return i.data;
205         return null;
206     }
207
208     protected Log? pop(uint id)
209     {
210         for (weak List<Log> i = logs; i != null; i = i.next)
211             if (i.data.id == id)
212             {
213                 Log res = i.data;
214                 logs.delete_link(i);
215                 return res;
216             }
217         return null;
218     }
219
220     public uint start(string tag)
221     {
222         uint id = gen_seq();
223         logs.append(new Log(id, tag));
224         return id;
225     }
226
227     public void add(uint id, string msg)
228     {
229         Log log = find(id);
230         if (log == null) return;
231         log.add(msg);
232     }
233
234     public void end(uint id)
235     {
236         Log log = pop(id);
237         log.save();
238     }
239
240     public void instant(string tag, string msg)
241     {
242         var log = new Log(0, tag);
243         log.add(msg);
244         log.save();
245     }
246
247     public void shutdown()
248     {
249         while (logs != null)
250         {
251             var log = pop(logs.data.id);
252             log.save();
253         }
254     }
255 }
256
257 public void error(string s)
258 {
259         stderr.printf("%s\n", s);
260 }
261 public void warning(string s)
262 {
263         stderr.printf("%s\n", s);
264 }
265 public void info(string s)
266 {
267         stderr.printf("%s\n", s);
268 }
269 public void debug(string s)
270 {
271         stderr.printf("%s\n", s);
272 }
273
274 Logger log = null;
275
276 public void init()
277 {
278     log = new Logger();
279 }
280
281 }
282 }