3 # Copyright (C) 2010 Dmitry Marakasov
5 # This file is part of UberLogger.
7 # UberLogger is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
12 # UberLogger is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with UberLogger. If not, see <http://www.gnu.org/licenses/>.
21 from PyQt4.QtGui import *
22 from PyQt4.QtCore import SIGNAL, SLOT, Qt, QTimer, QThread
24 from time import sleep
25 from threading import Thread
26 from datetime import datetime
34 from nmea import GPSData
37 [ "00:0D:B5:38:9E:16", "BT-335" ],
38 [ "00:0D:B5:38:AF:C7", "BT-821" ],
42 logprefix = "/home/user/gps/"
44 # lets you get/set powered state of your bluetooth adapter
45 # code from http://tomch.com/wp/?p=132
46 def enable_bluetooth(enabled = None):
47 bus = dbus.SystemBus();
48 root = bus.get_object('org.bluez', '/')
49 manager = dbus.Interface(root, 'org.bluez.Manager')
50 defaultAdapter = manager.DefaultAdapter()
51 obj = bus.get_object('org.bluez', defaultAdapter)
52 adapter = dbus.Interface(obj, 'org.bluez.Adapter')
53 props = adapter.GetProperties()
56 return adapter.GetProperties()['Powered']
58 adapter.SetProperty('Powered', True)
60 adapter.SetProperty('Powered', False)
63 class GPSThread(QThread):
64 def __init__(self, addr, name, parent = None):
65 QThread.__init__(self, parent)
73 self.total_connects = 0
84 logname = os.path.join(logprefix, "%s.%s.nmea" % (datetime.today().strftime("%Y.%m.%d"), self.name))
86 enable_bluetooth(True)
87 self.logfile = open(logname, 'a')
90 if self.logfile is not None:
104 while not self.exiting and socket is None:
105 self.emit(SIGNAL("status_updated(QString)"), "Connecting...")
106 socket = bluetooth.BluetoothSocket()
107 socket.connect((self.addr, 1))
108 socket.settimeout(10)
109 self.total_connects += 1
111 self.emit(SIGNAL("status_updated(QString)"), "Connected")
114 while not self.exiting and socket is not None:
115 chunk = socket.recv(1024)
118 raise Exception("Zero read")
121 self.total_length += len(chunk)
122 self.logfile.write(chunk)
125 lines = buffer.split('\n')
129 gpsdata.parse_nmea_string(line)
131 self.total_lines += len(lines)
133 # update display info every 10k
134 if self.total_length - last_length > 512:
135 self.emit(SIGNAL("status_updated(QString)"), "Logged %d lines, %d bytes, %d connects" % (self.total_lines, self.total_length, self.total_connects))
136 last_length = self.total_length
138 self.emit(SIGNAL("data_updated(QString)"), gpsdata.dump())
141 error = "%s: %s" % ("Cannot connect" if socket is None else "Read error", str(e))
143 error = "%s: %s" % ("Cannot connect" if socket is None else "Read error", sys.exc_info())
145 if self.exiting or error is None: return
147 # process error: wait some time and retry
148 global reconnect_delay
149 count = reconnect_delay
150 while not self.exiting and count > 0:
151 self.emit(SIGNAL("status_updated(QString)"), "%s, retry in %d" % (error, count))
161 while not self.exiting:
164 self.emit(SIGNAL("status_updated(QString)"), "FATAL: %s" % str(e))
169 self.emit(SIGNAL("status_updated(QString)"), "FATAL: cleanup failed")
171 self.emit(SIGNAL("status_updated(QString)"), "stopped")
173 class ContainerWidget(QWidget):
174 def __init__(self, addr, name, parent=None):
175 QWidget.__init__(self, parent)
181 self.status = "stopped"
184 self.startbutton = QPushButton("Start")
185 self.startbutton.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
186 self.connect(self.startbutton, SIGNAL('clicked()'), self.start_thread)
188 self.stopbutton = QPushButton("Stop")
189 self.stopbutton.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
190 self.stopbutton.setEnabled(False)
191 self.connect(self.stopbutton, SIGNAL('clicked()'), self.stop_thread)
193 self.statuswidget = QLabel()
194 self.statuswidget.setWordWrap(True)
196 self.monitorwidget = QLabel()
198 header = QHBoxLayout()
199 header.addWidget(self.startbutton)
200 header.addWidget(self.stopbutton)
201 header.addWidget(self.statuswidget)
203 self.layout = QVBoxLayout()
204 self.layout.addLayout(header)
205 self.layout.addWidget(self.monitorwidget)
207 self.setLayout(self.layout)
215 def start_thread(self):
216 if self.thread is not None:
219 self.startbutton.setEnabled(False)
220 self.stopbutton.setEnabled(True)
222 self.thread = GPSThread(self.addr, self.name, self)
223 self.connect(self.thread, SIGNAL("status_updated(QString)"), self.update_status)
224 self.connect(self.thread, SIGNAL("data_updated(QString)"), self.update_monitor)
225 self.connect(self.thread, SIGNAL("finished()"), self.gc_thread)
228 def stop_thread(self):
229 if self.thread is None:
232 self.stopbutton.setEnabled(False)
236 self.thread = None # join
238 self.startbutton.setEnabled(True)
239 self.stopbutton.setEnabled(False)
242 def update_status(self, status = None):
243 if status is not None:
245 self.statuswidget.setText("%s: %s" % (self.name, self.status))
247 def update_monitor(self, data = None):
248 self.monitorwidget.setText(data)
250 class MainWindow(QWidget):
251 def __init__(self, parent=None):
252 QWidget.__init__(self, parent)
253 self.setWindowTitle("UberLogger")
255 layout = QVBoxLayout()
258 for addr, name in devices:
259 layout.addWidget(ContainerWidget(addr, name))
261 self.setLayout(layout)
264 app = QApplication(sys.argv)
266 window = MainWindow()
268 sys.exit(app.exec_())
270 if __name__ == "__main__":