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