#!/usr/bin/env python 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()