X-Git-Url: http://vcs.maemo.org/git/?a=blobdiff_plain;f=uberlogger.py;fp=uberlogger.py;h=a656749fe8103b2d4d3268cb0091f7e7f5eced6b;hb=3c670edbe915745ad644a65955dc5ebf6754c7f9;hp=0000000000000000000000000000000000000000;hpb=2c1cebb5f11b486b6b7d8e5777a1a306a58c870f;p=uberlogger diff --git a/uberlogger.py b/uberlogger.py new file mode 100755 index 0000000..a656749 --- /dev/null +++ b/uberlogger.py @@ -0,0 +1,271 @@ +#!/usr/bin/env python +# +# Copyright (C) 2010 Dmitry Marakasov +# +# This file is part of UberLogger. +# +# UberLogger 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 3 of the License, or +# (at your option) any later version. +# +# UberLogger 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 UberLogger. If not, see . +# + +from PyQt4.QtGui import * +from PyQt4.QtCore import SIGNAL, SLOT, Qt, QTimer, QThread + +from time import sleep +from threading import Thread +from datetime import datetime + +import bluetooth +import os +import socket +import sys +import dbus + +from nmea import GPSData + +devices = [ + [ "00:0D:B5:38:9E:16", "BT-335" ], + [ "00:0D:B5:38:AF:C7", "BT-821" ], +] + +reconnect_delay = 10 +logprefix = "/home/user/gps/" + +# lets you get/set powered state of your bluetooth adapter +# code from http://tomch.com/wp/?p=132 +def enable_bluetooth(enabled = None): + bus = dbus.SystemBus(); + root = bus.get_object('org.bluez', '/') + manager = dbus.Interface(root, 'org.bluez.Manager') + defaultAdapter = manager.DefaultAdapter() + obj = bus.get_object('org.bluez', defaultAdapter) + adapter = dbus.Interface(obj, 'org.bluez.Adapter') + props = adapter.GetProperties() + + if enabled is None: + return adapter.GetProperties()['Powered'] + elif enabled: + adapter.SetProperty('Powered', True) + else: + adapter.SetProperty('Powered', False) + + +class GPSThread(QThread): + def __init__(self, addr, name, parent = None): + QThread.__init__(self, parent) + + self.addr = addr + self.name = name + self.exiting = False + self.logfile = None + + self.total_length = 0 + self.total_connects = 0 + self.total_lines = 0 + + def __del__(self): + self.exiting = True + self.wait() + + def stop(self): + self.exiting = True + + def init(self): + logname = os.path.join(logprefix, "%s.%s.nmea" % (datetime.today().strftime("%Y.%m.%d"), self.name)) + + enable_bluetooth(True) + self.logfile = open(logname, 'a') + + def cleanup(self): + if self.logfile is not None: + self.logfile.close() + self.logfile = None + + def main_loop(self): + error = None + buffer = "" + last_length = 0 + socket = None + + gpsdata = GPSData() + + try: + # connect + while not self.exiting and socket is None: + self.emit(SIGNAL("status_updated(QString)"), "Connecting...") + socket = bluetooth.BluetoothSocket() + socket.connect((self.addr, 1)) + socket.settimeout(10) + self.total_connects += 1 + + self.emit(SIGNAL("status_updated(QString)"), "Connected") + + # read + while not self.exiting and socket is not None: + chunk = socket.recv(1024) + + if len(chunk) == 0: + raise Exception("Zero read") + + buffer += chunk + self.total_length += len(chunk) + self.logfile.write(chunk) + + # parse lines + lines = buffer.split('\n') + buffer = lines.pop() + + for line in lines: + gpsdata.parse_nmea_string(line) + + self.total_lines += len(lines) + + # update display info every 10k + if self.total_length - last_length > 512: + self.emit(SIGNAL("status_updated(QString)"), "Logged %d lines, %d bytes, %d connects" % (self.total_lines, self.total_length, self.total_connects)) + last_length = self.total_length + + self.emit(SIGNAL("data_updated(QString)"), gpsdata.dump()) + + except IOError, e: + error = "%s: %s" % ("Cannot connect" if socket is None else "Read error", str(e)) + except: + error = "%s: %s" % ("Cannot connect" if socket is None else "Read error", sys.exc_info()) + + if self.exiting or error is None: return + + # process error: wait some time and retry + global reconnect_delay + count = reconnect_delay + while not self.exiting and count > 0: + self.emit(SIGNAL("status_updated(QString)"), "%s, retry in %d" % (error, count)) + sleep(1) + count -= 1 + + socket = None + + def run(self): + try: + self.init() + + while not self.exiting: + self.main_loop() + except Exception, e: + self.emit(SIGNAL("status_updated(QString)"), "FATAL: %s" % str(e)) + + try: + self.cleanup() + except: + self.emit(SIGNAL("status_updated(QString)"), "FATAL: cleanup failed") + + self.emit(SIGNAL("status_updated(QString)"), "stopped") + +class ContainerWidget(QWidget): + def __init__(self, addr, name, parent=None): + QWidget.__init__(self, parent) + + # data + self.addr = addr + self.name = name + self.thread = None + self.status = "stopped" + + # UI: header + self.startbutton = QPushButton("Start") + self.startbutton.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) + self.connect(self.startbutton, SIGNAL('clicked()'), self.start_thread) + + self.stopbutton = QPushButton("Stop") + self.stopbutton.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) + self.stopbutton.setEnabled(False) + self.connect(self.stopbutton, SIGNAL('clicked()'), self.stop_thread) + + self.statuswidget = QLabel() + self.statuswidget.setWordWrap(True) + + self.monitorwidget = QLabel() + + header = QHBoxLayout() + header.addWidget(self.startbutton) + header.addWidget(self.stopbutton) + header.addWidget(self.statuswidget) + + self.layout = QVBoxLayout() + self.layout.addLayout(header) + self.layout.addWidget(self.monitorwidget) + + self.setLayout(self.layout) + + # done + self.update_status() + + def __del__(self): + self.thread = None + + def start_thread(self): + if self.thread is not None: + return + + self.startbutton.setEnabled(False) + self.stopbutton.setEnabled(True) + + self.thread = GPSThread(self.addr, self.name, self) + self.connect(self.thread, SIGNAL("status_updated(QString)"), self.update_status) + self.connect(self.thread, SIGNAL("data_updated(QString)"), self.update_monitor) + self.connect(self.thread, SIGNAL("finished()"), self.gc_thread) + self.thread.start() + + def stop_thread(self): + if self.thread is None: + return + + self.stopbutton.setEnabled(False) + self.thread.stop() + + def gc_thread(self): + self.thread = None # join + + self.startbutton.setEnabled(True) + self.stopbutton.setEnabled(False) + self.update_status() + + def update_status(self, status = None): + if status is not None: + self.status = status + self.statuswidget.setText("%s: %s" % (self.name, self.status)) + + def update_monitor(self, data = None): + self.monitorwidget.setText(data) + +class MainWindow(QWidget): + def __init__(self, parent=None): + QWidget.__init__(self, parent) + self.setWindowTitle("UberLogger") + + layout = QVBoxLayout() + + global devices + for addr, name in devices: + layout.addWidget(ContainerWidget(addr, name)) + + self.setLayout(layout) + +def main(): + app = QApplication(sys.argv) + + window = MainWindow() + window.show() + sys.exit(app.exec_()) + +if __name__ == "__main__": + main()