source: trunk/synapse/puzzlebox_synapse_interface.py @ 161

Last change on this file since 161 was 160, checked in by sc, 11 years ago

Puzzlebox-Synapse.nsi:

  • 0.3.0 release

puzzlebox_synapse_client_thinkgear.py:

  • fixed localhost addressing issue

puzzlebox_synapse_interface.py:

  • fixed typo

setup_win32.py:

  • initial checkin
  • Property svn:executable set to *
File size: 17.8 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#
4# Puzzlebox - Synapse - Interface
5#
6# Copyright Puzzlebox Productions, LLC (2010)
7#
8# This code is released under the GNU Pulic License (GPL) version 2
9# For more information please refer to http://www.gnu.org/copyleft/gpl.html
10#
11# Last Update: 2010.08.12
12#
13#####################################################################
14# To Do:
15# - update configuration.ini file with settings entered into interface
16#####################################################################
17
18import os, sys
19
20import simplejson as json
21
22if (sys.platform != 'win32'):
23        import bluetooth
24        DEFAULT_IMAGE_PATH = '/usr/share/puzzlebox_synapse/images'
25else:
26        import serial
27        import re
28        import _winreg as winreg
29        import itertools
30        DEFAULT_IMAGE_PATH = 'images'
31
32try:
33        import PySide as PyQt4
34        from PySide import QtCore, QtGui
35except:
36        print "Using PyQt4 module"
37        from PyQt4 import QtCore, QtGui
38else:
39        print "Using PySide module"
40
41#from PyQt4 import QtCore, QtGui
42#from PySide import QtCore, QtGui
43
44from puzzlebox_synapse_interface_design import Ui_Form
45
46import puzzlebox_synapse_configuration as configuration
47import puzzlebox_synapse_server_thinkgear as synapse_server
48import puzzlebox_synapse_client_thinkgear as thinkgear_client
49#import puzzlebox_logger
50
51#####################################################################
52# Globals
53#####################################################################
54
55DEBUG = 1
56
57THINKGEAR_SERVER_HOST = configuration.THINKGEAR_SERVER_HOST
58THINKGEAR_SERVER_PORT = configuration.THINKGEAR_SERVER_PORT
59
60THINKGEAR_EEG_POWER_BAND_ORDER = configuration.THINKGEAR_EEG_POWER_BAND_ORDER
61
62THINKGEAR_EMULATION_MAX_ESENSE_VALUE = \
63        configuration.THINKGEAR_EMULATION_MAX_ESENSE_VALUE
64THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE = \
65        configuration.THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE
66
67PATH_TO_HCITOOL = '/usr/bin/hcitool'
68
69UPDATE_INTERFACE_VIA_TIMER = True # Alternative is to establish a
70                                  # ThinkGear Connect client which
71                                  # updates the interface on demand
72                                  # as packets are received
73
74INTERFACE_UPDATE_FREQUENCY = 1000 # ms
75
76#####################################################################
77# Classes
78#####################################################################
79
80class puzzlebox_synapse_interface(QtGui.QWidget, Ui_Form):
81       
82        def __init__(self, log, server=None, DEBUG=DEBUG, parent = None):
83               
84                self.log = log
85                self.DEBUG = DEBUG
86               
87                QtGui.QWidget.__init__(self, parent)
88                self.setupUi(self)
89               
90                self.configureSettings()
91                self.connectWidgets()
92               
93                self.name = "Synapse Interface"
94               
95                self.thinkGearConnectServer = None
96                self.thinkgearConnectClient = None
97               
98                self.maxEEGPower = THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE
99               
100                self.debug_console_buffer = ''
101               
102                if UPDATE_INTERFACE_VIA_TIMER:
103                        self.updateInterfaceTimer = QtCore.QTimer()
104                        QtCore.QObject.connect(self.updateInterfaceTimer, \
105                                                    QtCore.SIGNAL("timeout()"), \
106                                                    self.updateInterface)
107       
108       
109        ##################################################################
110       
111        def configureSettings(self):
112               
113                # Synapse Interface
114                image_path = "puzzlebox.ico"
115                if not os.path.exists(image_path):
116                        image_path = os.path.join(DEFAULT_IMAGE_PATH, image_path)
117               
118                if os.path.exists(image_path):
119                        icon = QtGui.QIcon()
120                        icon.addPixmap(QtGui.QPixmap(image_path), \
121                                            QtGui.QIcon.Normal, \
122                                            QtGui.QIcon.Off)
123                        self.setWindowIcon(icon)
124               
125                image_path = "puzzlebox_logo.png"
126                if not os.path.exists(image_path):
127                        image_path = os.path.join(DEFAULT_IMAGE_PATH, image_path)
128                if os.path.exists(image_path):
129                        self.labelPuzzleboxIcon.setPixmap(QtGui.QPixmap(image_path))
130               
131               
132                # ThinkGear Device
133                self.searchForThinkGearDevices()
134               
135               
136                # ThinkGear Connect Server
137                self.textLabelBluetoothStatus.setText("Status: Disconnected")
138               
139                # Display Host for ThinkGear Connect Socket Server
140                self.lineEditThinkGearHost.setText(THINKGEAR_SERVER_HOST)
141               
142                # Display Port for ThinkGear Connect Socket Server
143                self.lineEditThinkGearPort.setText('%i' % THINKGEAR_SERVER_PORT)
144               
145               
146                # ThinkgGear Progress Bars
147                self.progressBarEEGDelta.setMaximum(THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE)
148                self.progressBarEEGTheta.setMaximum(THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE)
149                self.progressBarEEGLowAlpha.setMaximum(THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE)
150                self.progressBarEEGHighAlpha.setMaximum(THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE)
151                self.progressBarEEGLowBeta.setMaximum(THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE)
152                self.progressBarEEGHighBeta.setMaximum(THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE)
153                self.progressBarEEGLowGamma.setMaximum(THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE)
154                self.progressBarEEGMidGamma.setMaximum(THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE)
155               
156                self.progressBarAttention.setMaximum(THINKGEAR_EMULATION_MAX_ESENSE_VALUE)
157                self.progressBarMeditation.setMaximum(THINKGEAR_EMULATION_MAX_ESENSE_VALUE)
158               
159                self.progressBarSignalContactQuality.setMaximum(200)
160       
161       
162        ##################################################################
163       
164        def connectWidgets(self):
165               
166                self.connect(self.pushButtonBluetoothSearch, \
167                                  QtCore.SIGNAL("clicked()"), \
168                                  self.searchForThinkGearDevices)
169               
170                self.connect(self.pushButtonBluetoothConnect, \
171                                  QtCore.SIGNAL("clicked()"), \
172                                  self.connectToThinkGearDevice)
173               
174                self.connect(self.pushButtonThinkGearConnect, \
175                                  QtCore.SIGNAL("clicked()"), \
176                                  self.startThinkGearConnectServer)
177       
178       
179        ##################################################################
180       
181        def connectToThinkGearDevice(self):
182               
183                device_selection = self.comboBoxDeviceSelect.currentText()
184               
185                self.disconnect(self.pushButtonBluetoothConnect, \
186                                     QtCore.SIGNAL("clicked()"), \
187                                     self.connectToThinkGearDevice)
188               
189                self.connect(self.pushButtonBluetoothConnect, \
190                                  QtCore.SIGNAL("clicked()"), \
191                                  self.disconnectFromThinkGearDevice)
192               
193                self.textLabelBluetoothStatus.setText("Status: Connected")
194               
195                self.pushButtonBluetoothSearch.setEnabled(False)
196               
197                self.pushButtonBluetoothConnect.setText('Disconnect')
198                self.pushButtonBluetoothConnect.setChecked(True)
199               
200                self.comboBoxDeviceSelect.setEnabled(False)
201       
202       
203        ##################################################################
204       
205        def disconnectFromThinkGearDevice(self):
206               
207                self.disconnect(self.pushButtonBluetoothConnect, \
208                                     QtCore.SIGNAL("clicked()"), \
209                                     self.disconnectFromThinkGearDevice)
210               
211                self.connect(self.pushButtonBluetoothConnect, \
212                                  QtCore.SIGNAL("clicked()"), \
213                                  self.connectToThinkGearDevice)
214               
215                self.textLabelBluetoothStatus.setText("Status: Disconnected")
216               
217                self.pushButtonBluetoothSearch.setEnabled(True)
218               
219                self.pushButtonBluetoothConnect.setText('Connect')
220                self.pushButtonBluetoothConnect.setChecked(False)
221               
222                self.comboBoxDeviceSelect.setEnabled(True)
223               
224               
225                self.progressBarEEGDelta.setValue(0)
226                self.progressBarEEGTheta.setValue(0)
227                self.progressBarEEGLowAlpha.setValue(0)
228                self.progressBarEEGHighAlpha.setValue(0)
229                self.progressBarEEGLowBeta.setValue(0)
230                self.progressBarEEGHighBeta.setValue(0)
231                self.progressBarEEGLowGamma.setValue(0)
232                self.progressBarEEGMidGamma.setValue(0)
233               
234                self.progressBarAttention.setValue(0)
235                self.progressBarMeditation.setValue(0)
236               
237                self.progressBarSignalContactQuality.setValue(0)
238               
239                self.maxEEGPower = THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE
240               
241                # In case the user connects to a MindSet, then disconnects
242                # and re-connects to a MindSet Emulator,
243                # we need to reset the max power values
244                self.progressBarEEGDelta.setMaximum(self.maxEEGPower)
245                self.progressBarEEGTheta.setMaximum(self.maxEEGPower)
246                self.progressBarEEGLowAlpha.setMaximum(self.maxEEGPower)
247                self.progressBarEEGHighAlpha.setMaximum(self.maxEEGPower)
248                self.progressBarEEGLowBeta.setMaximum(self.maxEEGPower)
249                self.progressBarEEGHighBeta.setMaximum(self.maxEEGPower)
250                self.progressBarEEGLowGamma.setMaximum(self.maxEEGPower)
251                self.progressBarEEGMidGamma.setMaximum(self.maxEEGPower)
252       
253       
254        ##################################################################
255       
256        def startThinkGearConnectServer(self):
257               
258                # Ensure EEG device is connected first
259               
260                if not self.pushButtonBluetoothConnect.isChecked():
261                        self.connectToThinkGearDevice()
262               
263               
264                self.pushButtonBluetoothSearch.setEnabled(False)
265                self.pushButtonBluetoothConnect.setEnabled(False)
266               
267                server_interface = str(self.lineEditThinkGearHost.text())
268                server_port = int(self.lineEditThinkGearPort.text())
269                device_address = str(self.comboBoxDeviceSelect.currentText())
270                emulate_headset_data = (device_address == 'MindSet Emulator')
271               
272               
273                self.thinkGearConnectServer = \
274                        synapse_server.puzzlebox_synapse_server_thinkgear( \
275                                log, \
276                                server_interface=server_interface, \
277                                server_port=server_port, \
278                                device_address=device_address, \
279                                emulate_headset_data=emulate_headset_data, \
280                                DEBUG=DEBUG, \
281                                parent=self)
282               
283                self.thinkGearConnectServer.start()
284               
285               
286                if UPDATE_INTERFACE_VIA_TIMER:
287                        self.updateInterfaceTimer.start(INTERFACE_UPDATE_FREQUENCY)
288               
289                else:
290                        self.thinkgearConnectClient = \
291                                thinkgear_client.puzzlebox_synapse_client_thinkgear( \
292                                        self.log, \
293                                        server_host=server_interface, \
294                                        server_port=server_port, \
295                                        DEBUG=0, \
296                                        parent=self)
297                       
298                        self.thinkgearConnectClient.start()
299               
300               
301                self.disconnect(self.pushButtonThinkGearConnect, \
302                                     QtCore.SIGNAL("clicked()"), \
303                                     self.startThinkGearConnectServer)
304               
305                self.connect(self.pushButtonThinkGearConnect, \
306                                  QtCore.SIGNAL("clicked()"), \
307                                  self.stopThinkGearConnectServer)
308               
309                self.lineEditThinkGearHost.setEnabled(False)
310                self.lineEditThinkGearPort.setEnabled(False)
311               
312                self.pushButtonThinkGearConnect.setText('Stop')
313       
314       
315        ##################################################################
316       
317        def stopThinkGearConnectServer(self):
318               
319                if UPDATE_INTERFACE_VIA_TIMER:
320                        self.updateInterfaceTimer.stop()
321                else:
322                        self.thinkgearConnectClient.disconnectFromHost()
323               
324                self.thinkGearConnectServer.exitThread()
325               
326                self.disconnect(self.pushButtonThinkGearConnect, \
327                                     QtCore.SIGNAL("clicked()"), \
328                                     self.stopThinkGearConnectServer)
329               
330                self.connect(self.pushButtonThinkGearConnect, \
331                                  QtCore.SIGNAL("clicked()"), \
332                                  self.startThinkGearConnectServer)
333               
334                self.lineEditThinkGearHost.setEnabled(True)
335                self.lineEditThinkGearPort.setEnabled(True)
336               
337                self.pushButtonThinkGearConnect.setText('Start')
338               
339                #self.pushButtonBluetoothSearch.setEnabled(True)
340                self.pushButtonBluetoothConnect.setEnabled(True)
341       
342       
343        ##################################################################
344       
345        def updateInterface(self):
346               
347                if not self.thinkGearConnectServer.emulate_headset_data:
348                        self.processPacketThinkGear( \
349                                self.thinkGearConnectServer.protocol.data_packet)
350       
351       
352        ##################################################################
353       
354        def processPacketThinkGear(self, packet):
355               
356                if self.DEBUG > 2:
357                        print packet
358               
359               
360                if ('poorSignalLevel' in packet.keys()):
361                        value = 200 - packet['poorSignalLevel']
362                        self.progressBarSignalContactQuality.setValue(value)
363                        self.textEditDebugConsole.append("")
364                        self.textEditDebugConsole.append("poorSignalLevel: %i" % \
365                                                              packet['poorSignalLevel'])
366               
367               
368                if ('eSense' in packet.keys()):
369                       
370                        if ('attention' in packet['eSense'].keys()):
371                                value = packet['eSense']['attention']
372                                self.progressBarAttention.setValue(value)
373                                self.textEditDebugConsole.append("eSense attention: %i" % value)
374                       
375                        if ('meditation' in packet['eSense'].keys()):
376                                value = packet['eSense']['meditation']
377                                self.progressBarMeditation.setValue(value)
378                                self.textEditDebugConsole.append("eSense meditation: %i" % value)
379               
380               
381                if ('eegPower' in packet.keys()):
382                       
383                        # If we are not emulating packets we'll set the maximum EEG Power value
384                        # threshold to the default (or maximum value found within this packet)
385                        if not self.thinkGearConnectServer.emulate_headset_data:
386                                self.maxEEGPower = THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE
387                       
388                        for value in packet['eegPower'].keys():
389                                if packet['eegPower'][value] > self.maxEEGPower:
390                                        self.maxEEGPower = packet['eegPower'][value]
391                       
392                       
393                        if ('delta' in packet['eegPower'].keys()):
394                                value = packet['eegPower']['delta']
395                                self.progressBarEEGDelta.setMaximum(self.maxEEGPower)
396                                self.progressBarEEGDelta.setValue(value)
397                                self.textEditDebugConsole.append("delta: %i" % value)
398                       
399                        if ('theta' in packet['eegPower'].keys()):
400                                value = packet['eegPower']['theta']
401                                self.progressBarEEGTheta.setMaximum(self.maxEEGPower)
402                                self.progressBarEEGTheta.setValue(value)
403                                self.textEditDebugConsole.append("theta: %i" % value)
404                       
405                        if ('lowAlpha' in packet['eegPower'].keys()):
406                                value = packet['eegPower']['lowAlpha']
407                                self.progressBarEEGLowAlpha.setMaximum(self.maxEEGPower)
408                                self.progressBarEEGLowAlpha.setValue(value)
409                                self.textEditDebugConsole.append("lowAlpha: %i" % value)
410                       
411                        if ('highAlpha' in packet['eegPower'].keys()):
412                                value = packet['eegPower']['highAlpha']
413                                self.progressBarEEGHighAlpha.setMaximum(self.maxEEGPower)
414                                self.progressBarEEGHighAlpha.setValue(value)
415                                self.textEditDebugConsole.append("highAlpha: %i" % value)
416                       
417                        if ('lowBeta' in packet['eegPower'].keys()):
418                                value = packet['eegPower']['lowBeta']
419                                self.progressBarEEGLowBeta.setMaximum(self.maxEEGPower)
420                                self.progressBarEEGLowBeta.setValue(value)
421                                self.textEditDebugConsole.append("lowBeta: %i" % value)
422                       
423                        if ('highBeta' in packet['eegPower'].keys()):
424                                value = packet['eegPower']['highBeta']
425                                self.progressBarEEGHighBeta.setMaximum(self.maxEEGPower)
426                                self.progressBarEEGHighBeta.setValue(value)
427                                self.textEditDebugConsole.append("highBeta: %i" % value)
428                       
429                        if ('lowGamma' in packet['eegPower'].keys()):
430                                value = packet['eegPower']['lowGamma']
431                                self.progressBarEEGLowGamma.setMaximum(self.maxEEGPower)
432                                self.progressBarEEGLowGamma.setValue(value)
433                                self.textEditDebugConsole.append("lowGamma: %i" % value)
434                       
435                        if ('highGamma' in packet['eegPower'].keys()):
436                                value = packet['eegPower']['highGamma']
437                                self.progressBarEEGMidGamma.setMaximum(self.maxEEGPower)
438                                self.progressBarEEGMidGamma.setValue(value)
439                                self.textEditDebugConsole.append("highGamma: %i" % value)
440       
441       
442        ##################################################################
443
444        def enumerateSerialPorts(self):
445               
446                """ Uses the Win32 registry to return an
447                iterator of serial (COM) ports
448                existing on this computer.
449
450                from http://eli.thegreenplace.net/2009/07/31/listing-all-serial-ports-on-windows-with-python/
451                """
452           
453                path = 'HARDWARE\\DEVICEMAP\\SERIALCOMM'
454                try:
455                        key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, path)
456                except WindowsError:
457                        raise IterationError
458
459                for i in itertools.count():
460                        try:
461                                val = winreg.EnumValue(key, i)
462                                yield str(val[1])
463                        except EnvironmentError:
464                                break
465
466
467        ##################################################################
468                       
469        def fullPortName(self, portname):
470               
471                """ Given a port-name (of the form COM7,
472                COM12, CNCA0, etc.) returns a full
473                name suitable for opening with the
474                Serial class.
475                """
476
477                m = re.match('^COM(\d+)$', portname)
478                if m and int(m.group(1)) < 10:
479                        return portname
480               
481                return '\\\\.\\' + portname
482
483       
484        ##################################################################
485       
486        def searchForThinkGearDevices(self):
487               
488                #self.pushButtonBluetoothSearch.setText('Searching')
489
490                mindset_devices = []
491               
492                if (sys.platform == 'win32'):
493
494                        for portname in self.enumerateSerialPorts():
495
496                                #portname = self.fullPortName(portname)
497                                mindset_devices.append(portname)
498
499
500                else:
501                       
502                        # Bluetooth module doesn't compile properly under Windows
503                       
504                        bluetooth_devices = []
505                       
506                        #bluetooth_devices = bluetooth.discover_devices( \
507                                                    #duration=5, \
508                                                    #flush_cache=True, \
509                                                    #lookup_names=True)
510                       
511                        command = '%s con' % PATH_TO_HCITOOL
512                       
513                        output = os.popen(command, 'r')
514                       
515                        for line in output.readlines():
516                                try:
517                                        address = line.split(' ')[2]
518                                except:
519                                        pass
520                                else:
521                                        bluetooth_devices.append(address)
522                       
523                        for address in bluetooth_devices:
524                                device_name = bluetooth.lookup_name(address)
525                                if device_name == 'MindSet':
526                                        mindset_devices.append(address)
527                       
528                       
529                        if self.DEBUG:
530                                print "Bluetooth MindSet devices found:",
531                                print mindset_devices
532                       
533                       
534                        self.comboBoxDeviceSelect.clear()
535                       
536                        self.comboBoxDeviceSelect.addItem('MindSet Emulator')
537                       
538                       
539                for mindset_device in mindset_devices:
540                        self.comboBoxDeviceSelect.addItem(mindset_device)
541               
542               
543                #self.pushButtonBluetoothSearch.setText('Search')
544       
545       
546        ##################################################################
547       
548        def closeEvent(self, event):
549               
550                quit_message = "Are you sure you want to exit the program?"
551               
552                reply = QtGui.QMessageBox.question( \
553                           self, \
554                          'Message', \
555                           quit_message, \
556                           QtGui.QMessageBox.Yes, \
557                           QtGui.QMessageBox.No)
558               
559                if reply == QtGui.QMessageBox.Yes:
560                       
561                        if UPDATE_INTERFACE_VIA_TIMER:
562                                self.updateInterfaceTimer.stop()
563                        else:
564                                if self.thinkgearConnectClient != None:
565                                        self.thinkgearConnectClient.disconnectFromHost()
566                       
567                        if self.thinkGearConnectServer != None:
568                                self.thinkGearConnectServer.exitThread()
569                       
570                        event.accept()
571               
572                else:
573                        event.ignore()
574
575
576#####################################################################
577# Functions
578#####################################################################
579
580#####################################################################
581# Main
582#####################################################################
583
584if __name__ == '__main__':
585       
586        #log = puzzlebox_logger.puzzlebox_logger(logfile='client_interface')
587        log = None
588       
589        app = QtGui.QApplication(sys.argv)
590       
591        window = puzzlebox_synapse_interface(log, DEBUG)
592        window.show()
593       
594        sys.exit(app.exec_())
595
Note: See TracBrowser for help on using the repository browser.