/* * log - logging functions * * Copyright (C) 2009--2010 Enrico Zini * * 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 * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ using GLib; namespace zavai { namespace log { public class Waypoint : Object { public time_t ts; public double lat; public double lon; public Waypoint() { if (gps.gps.fix_status() != libgps.STATUS_NO_FIX) { lat = gps.gps.info().fix.latitude; lon = gps.gps.info().fix.longitude; ts = (time_t)gps.gps.info().fix.time; } else { // Use 1000 as missing values lat = 1000; lon = 1000; ts = time_t(); } } public void writeInside(FileStream outfd) { var t = Time.gm(ts); outfd.printf(" \n", t.format("%Y-%m-%dT%H:%M:%S%z")); } } public class LogEntry : Waypoint { public string msg; public LogEntry() { base(); } public void write(FileStream outfd) { outfd.printf(" \n", lat, lon); writeInside(outfd); outfd.printf(" %s\n", Markup.escape_text(msg)); outfd.puts(" \n"); } } public class TrackEntry : Waypoint { public TrackEntry() { base(); } public void write(FileStream outfd) { outfd.printf(" \n", lat, lon); writeInside(outfd); outfd.puts(" \n"); } } public class Log : Object { public string tag; public string title; public bool acked; public List entries; public List track; public Log(string tag, string title, bool acked=false) { this.tag = tag; this.title = title; this.acked = acked; entries = null; track = null; } public void add(string msg) { var entry = new LogEntry(); entry.msg = msg; entries.append(entry); } public void add_trackpoint() { track.append(new TrackEntry()); } public void save() { if (entries == null) return; // Directory where we save the log string dir; if (acked) dir = config.homedir + "/archive"; else dir = config.homedir + "/log"; DirUtils.create(dir, 0777); // First try with a plain name var t = Time.local(entries.data.ts); string basename = dir + "/" + t.format("%Y%m%d-%H%M%S") + "-" + tag; string pathname = basename + ".gpx"; // Find a pathname that does not exist already for (int i = 1; FileUtils.test(pathname, FileTest.EXISTS); ++i) pathname = "%s-%d.gpx".printf(basename, i); // Write out var outfd = FileStream.open(pathname, "w"); if (outfd == null) { zavai.log.error("opening " + pathname + ": " + strerror(errno)); return; } write(outfd); outfd.flush(); } public void dump() { write(stderr); } protected void writeTrack(FileStream outfd) { outfd.puts(" \n"); outfd.puts(" \n"); for (weak List i = track; i != null; i = i.next) i.data.write(outfd); outfd.puts(" \n"); outfd.puts(" \n"); } protected void writeEntries(FileStream outfd) { for (weak List i = entries; i != null; i = i.next) i.data.write(outfd); } protected void write(FileStream outfd) { outfd.puts("\n"); outfd.puts("\n"); outfd.puts(" \n"); outfd.printf(" %s\n", Markup.escape_text(title)); outfd.puts(" \n"); if (track != null) writeTrack(outfd); if (entries != null) writeEntries(outfd); outfd.puts(" \n"); } } enum LogParserState { NONE, METADATA, TRACK, WPT, } class LogParser: Object { const MarkupParser parser = { // It's a structure, not an object start,// when an element opens end, // when an element closes text, // when text is found null, // when comments are found null // when errors occur }; MarkupParseContext context = null; public Log result = null; LogParserState state = LogParserState.NONE; string cur_text = ""; LogEntry cur_logentry = null; TrackEntry cur_trackentry = null; construct { context = new MarkupParseContext( parser, // the structure with the callbacks 0, // MarkupParseFlags this, // extra argument for the callbacks, methods in this case destroy // when the parsing ends ); } void destroy() { cur_text = ""; cur_logentry = null; cur_trackentry = null; } public bool parse(string content, ssize_t len = -1) throws MarkupError { string oldtz = Environment.get_variable("TZ"); Environment.set_variable("TZ", "UTC", true); bool res = context.parse(content, len); if (oldtz == null) Environment.unset_variable("TZ"); else Environment.set_variable("TZ", oldtz, true); return res; } void parse_attrs(Waypoint w, string[] attr_names, string[] attr_values) { w.lat = 1000; w.lon = 1000; for (int i = 0; attr_names[i] != null; ++i) { if (attr_names[i] == "lat") w.lat = attr_values[i].to_double(); else if (attr_names[i] == "lon") w.lon = attr_values[i].to_double(); } } void start (MarkupParseContext context, string name, string[] attr_names, string[] attr_values) throws MarkupError { if (name == "gpx") { state = LogParserState.NONE; result = new Log("TODO:TAG", "TODO:TITLE"); } else if (name == "metadata") { state = LogParserState.METADATA; } else if (name == "wpt") { cur_logentry = new LogEntry(); parse_attrs(cur_logentry, attr_names, attr_values); result.entries.append(cur_logentry); state = LogParserState.WPT; } else if (name == "trkpt") { cur_trackentry = new TrackEntry(); parse_attrs(cur_trackentry, attr_names, attr_values); result.track.append(cur_trackentry); state = LogParserState.TRACK; } cur_text = ""; } void end (MarkupParseContext context, string name) throws MarkupError { if (name == "name") { switch (state) { case LogParserState.METADATA: result.title = cur_text; break; case LogParserState.WPT: cur_logentry.msg = cur_text; break; } } else if (name == "time") { Time t = Time(); t.strptime(cur_text, "%Y-%m-%dT%H:%M:%S%z"); if (state == LogParserState.WPT) cur_logentry.ts = t.mktime(); else if (state == LogParserState.TRACK) cur_trackentry.ts = t.mktime(); } } void text (MarkupParseContext context, string text, size_t text_len) throws MarkupError { cur_text += text; } } public class Logger : Resource, Object { protected List logs; public signal void entries_changed(); public Logger() { logs = null; zavai.registry.register(this); } protected void start_trace() { gps.gps.pos_changed += on_pos_changed; } protected void end_trace() { gps.gps.pos_changed -= on_pos_changed; } protected void on_pos_changed() { for (weak List i = logs; i != null; i = i.next) i.data.add_trackpoint(); } protected void pop(Log log) { for (weak List i = logs; i != null; i = i.next) if (i.data == log) logs.delete_link(i); } public Log start(string tag, string title) { bool was_empty = (logs == null); Log res = new Log(tag, title); logs.append(res); if (was_empty) start_trace(); return res; } public void end(Log log) { pop(log); log.save(); if (logs == null) end_trace(); entries_changed(); } public Log load(string fname) { string contents; size_t length; FileUtils.get_contents(fname, out contents, out length); LogParser parser = new LogParser(); parser.parse(contents, (ssize_t)length); return parser.result; } public delegate bool EntriesVisitor(string dir, string name); protected void list_dir(string dir, EntriesVisitor visitor) { var d = File.new_for_path(dir); var enumerator = d.enumerate_children(FILE_ATTRIBUTE_STANDARD_NAME, 0, null); FileInfo file_info; while ((file_info = enumerator.next_file(null)) != null) { if (!file_info.get_name().has_suffix(".gpx")) continue; if (!visitor(dir, file_info.get_name())) break; } } public void list_entries(EntriesVisitor visitor, bool only_unacked=true) { if (!only_unacked) list_dir(config.homedir + "/archive", visitor); list_dir(config.homedir + "/log", visitor); } public void instant(string tag, string msg) { var log = new Log(tag, msg); log.add(msg); log.save(); } public void shutdown() { bool had_logs = (logs != null); while (logs != null) { logs.data.save(); logs.delete_link(logs); } if (had_logs) end_trace(); } } public void error(string s) { stderr.printf("%s\n", s); } public void warning(string s) { stderr.printf("%s\n", s); } public void info(string s) { stderr.printf("%s\n", s); } public void debug(string s) { stderr.printf("%s\n", s); } Logger log = null; public void init() { log = new Logger(); } } }