source: remote_control/puzzlebox_brainstorms_client_interface.py @ 95

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

remote_control/puzzlebox_brainstorms_configuration:

  • default detection/power thresholds tweaked

remote_control/puzzlebox_brainstorms_client_interface.py:

  • minimum speed settings adjusted
File size: 13.7 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#
4# Puzzlebox - Brainstorms - Client Interface - Qt
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.07.08
12#
13#####################################################################
14# To Do
15# - server reporting timeout on sendCommand calls from updateEvent
16# - disable autorepeating on shortcut keys
17#####################################################################
18
19import os, sys
20
21#try:
22        #from PySide import QtCore, QtGui, QtNetwork
23#except:
24        #print "Using PyQt4 module"
25        #from PyQt4 import QtCore, QtGui, QtNetwork
26#else:
27        #print "Using PySide module"
28
29from PyQt4 import QtCore, QtGui, QtNetwork
30#from PySide import QtCore, QtGui, QtNetwork
31
32sys.path.append(os.path.join(os.getcwd(), "interface"))
33from qt4_form import Ui_Form
34
35import simplejson as json
36
37import puzzlebox_brainstorms_configuration as configuration
38import puzzlebox_brainstorms_network_client as brainstorms_client
39import puzzlebox_brainstorms_network_client_thinkgear as thinkgear_client
40#import puzzlebox_logger
41
42#####################################################################
43# Globals
44#####################################################################
45
46DEBUG = 1
47
48THINKGEAR_POWER_THRESHOLDS = configuration.THINKGEAR_POWER_THRESHOLDS
49
50NXT_BLUETOOTH_DEVICE = configuration.NXT_BLUETOOTH_DEVICE
51
52DEFAULT_NXT_POWER_LEVEL = configuration.DEFAULT_NXT_POWER_LEVEL
53
54THINKGEAR_SERVER_HOST = configuration.THINKGEAR_SERVER_HOST
55THINKGEAR_SERVER_PORT = configuration.THINKGEAR_SERVER_PORT
56
57#####################################################################
58# Classes
59#####################################################################
60
61class puzzlebox_brainstorms_client_interface(QtGui.QWidget, Ui_Form):
62       
63        def __init__(self, log, DEBUG=DEBUG, parent = None):
64               
65                self.log = log
66                self.DEBUG = DEBUG
67               
68                QtGui.QWidget.__init__(self, parent)
69                self.setupUi(self)
70               
71                self.configureSettings()
72                self.configureNetworkBrainstorms()
73                self.connectWidgets()
74               
75                self.drive_state = 'stop_motors'
76                self.current_speed = 0
77       
78       
79        ##################################################################
80       
81        def configureSettings(self):
82               
83                # LEGO Mindstorms
84               
85                # NXT Bluetooth readout current not available
86                self.textLabelNXTStatus.setText("Status: N/A")
87               
88                # Display communication port for LEGO Mindstorms NXT device
89                self.lineEditNXTPort.setText(NXT_BLUETOOTH_DEVICE)
90                self.lineEditNXTPort.setEnabled(False)
91               
92                # NXT Bluetooth connection control not available
93                self.pushButtonNXTConnect.setEnabled(False)
94               
95               
96                # EEG Headset
97               
98                # Display Host for ThinkGear Connect Socket Server
99                self.lineEditThinkGearHost.setText(THINKGEAR_SERVER_HOST)
100                #self.lineEditThinkGearHost.setEnabled(False)
101               
102                # Display Port for ThinkGear Connect Socket Server
103                self.lineEditThinkGearPort.setText('%i' % THINKGEAR_SERVER_PORT)
104                #self.lineEditThinkGearPort.setEnabled(False)
105       
106       
107        ##################################################################
108       
109        def getMinimumThreshold(self, threshold):
110               
111                '''Return the minimum detection level which results
112                in a non-zero power setting'''
113               
114                minimum = 100
115               
116                threshold_keys = threshold.keys()
117                threshold_keys.sort()
118                threshold_keys.reverse()
119               
120                for key in threshold_keys:
121                       
122                        if ((threshold[key] < minimum) and \
123                                 (threshold[key] > 0)):
124                                minimum = key
125               
126               
127                return(minimum)
128       
129       
130        ##################################################################
131       
132        def configureNetworkBrainstorms(self):
133               
134                self.brainstormsClient = \
135                   brainstorms_client.puzzlebox_brainstorms_network_client(self.log, parent=self)
136       
137       
138        ##################################################################
139       
140        def connectToThinkGearHost(self):
141               
142                if self.DEBUG:
143                        print "Connecting to ThinkGear Host"
144               
145                #self.pushButtonThinkGearConnect.setText('Connecting')
146               
147                server_host = str(self.lineEditThinkGearHost.text())
148                server_port = int(self.lineEditThinkGearPort.text())
149               
150                self.thinkgearClient = \
151                   thinkgear_client.puzzlebox_brainstorms_network_client_thinkgear( \
152                           self.log, \
153                           server_host=server_host, \
154                           server_port=server_port, \
155                           DEBUG=0, \
156                           parent=self)
157               
158                if (self.thinkgearClient.socket.state() != QtNetwork.QAbstractSocket.ConnectedState):
159                        QtGui.QMessageBox.information(self, \
160                                                                self.thinkgearClient.socket.name, \
161                                           "Failed to connect to ThinkGear socket server")
162                       
163                        #self.pushButtonThinkGearConnect.setText('Connect')
164               
165                else:
166                        self.disconnect(self.pushButtonThinkGearConnect, \
167                                                         QtCore.SIGNAL("clicked()"), \
168                                                         self.connectToThinkGearHost)
169                       
170                        self.connect(self.pushButtonThinkGearConnect, \
171                                                         QtCore.SIGNAL("clicked()"), \
172                                                         self.diconnectFromThinkGearHost)
173                       
174                        self.pushButtonThinkGearConnect.setText('Disconnect')
175                       
176                        self.lineEditThinkGearHost.setEnabled(False)
177                        self.lineEditThinkGearPort.setEnabled(False)
178       
179       
180        ##################################################################
181       
182        def diconnectFromThinkGearHost(self):
183               
184                if self.DEBUG:
185                        print "Disconnecting from ThinkGear Host"
186               
187                self.thinkgearClient.disconnectFromHost()
188               
189                self.disconnect(self.pushButtonThinkGearConnect, \
190                                  QtCore.SIGNAL("clicked()"), \
191                                  self.diconnectFromThinkGearHost)
192               
193                self.connect(self.pushButtonThinkGearConnect, \
194                                  QtCore.SIGNAL("clicked()"), \
195                                  self.connectToThinkGearHost)
196               
197                self.pushButtonForward.emit(QtCore.SIGNAL("released()"))
198               
199                self.pushButtonThinkGearConnect.setText('Connect')
200               
201                self.lineEditThinkGearHost.setEnabled(True)
202                self.lineEditThinkGearPort.setEnabled(True)
203               
204                self.progressBarConcentration.setValue(0)
205                self.progressBarRelaxation.setValue(0)
206                self.progressBarSpeed.setValue(0)
207       
208       
209        ##################################################################
210       
211        def connectWidgets(self):
212               
213                self.connect(self.pushButtonTurnLeft, QtCore.SIGNAL("pressed()"), \
214                             self.turnLeft)
215                self.connect(self.pushButtonTurnLeft, QtCore.SIGNAL("released()"), \
216                             self.stopMotors)
217               
218                self.connect(self.pushButtonForward, QtCore.SIGNAL("pressed()"), \
219                             self.driveForward)
220                self.connect(self.pushButtonForward, QtCore.SIGNAL("released()"), \
221                             self.stopMotors)
222               
223                self.connect(self.pushButtonTurnRight, QtCore.SIGNAL("pressed()"), \
224                             self.turnRight)
225                self.connect(self.pushButtonTurnRight, QtCore.SIGNAL("released()"), \
226                             self.stopMotors)
227               
228                self.connect(self.pushButtonTurnLeftReverse, QtCore.SIGNAL("pressed()"), \
229                             self.turnLeftInReverse)
230                self.connect(self.pushButtonTurnLeftReverse, QtCore.SIGNAL("released()"), \
231                             self.stopMotors)
232               
233                self.connect(self.pushButtonReverse, QtCore.SIGNAL("pressed()"), \
234                             self.driveReverse)
235                self.connect(self.pushButtonReverse, QtCore.SIGNAL("released()"), \
236                             self.stopMotors)
237               
238                self.connect(self.pushButtonTurnRightReverse, QtCore.SIGNAL("pressed()"), \
239                             self.turnRightInReverse)
240                self.connect(self.pushButtonTurnRightReverse, QtCore.SIGNAL("released()"), \
241                             self.stopMotors)
242               
243               
244                self.connect(self.pushButtonThinkGearConnect, \
245                                  QtCore.SIGNAL("clicked()"), \
246                                  self.connectToThinkGearHost)
247       
248       
249                #shortcut = QtGui.QShortcut(self)
250                #shortcut.setKey(tr("Down"))
251                #self.connect(shortcut, QtCore.SIGNAL("pressed()"), self.driveReverse)
252               
253                action = QtGui.QAction(self)
254                action.setShortcut(QtGui.QKeySequence("W"))
255                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonForward, QtCore.SLOT("animateClick()"))
256                self.addAction(action)
257               
258                action = QtGui.QAction(self)
259                action.setShortcut(QtGui.QKeySequence("Up"))
260                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonForward, QtCore.SLOT("animateClick()"))
261                self.addAction(action)
262               
263               
264                action = QtGui.QAction(self)
265                action.setShortcut(QtGui.QKeySequence("Left"))
266                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonTurnLeft, QtCore.SLOT("animateClick()"))
267                self.addAction(action)
268               
269                action = QtGui.QAction(self)
270                action.setShortcut(QtGui.QKeySequence("A"))
271                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonTurnLeft, QtCore.SLOT("animateClick()"))
272                self.addAction(action)
273               
274               
275                action = QtGui.QAction(self)
276                action.setShortcut(QtGui.QKeySequence("S"))
277                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonReverse, QtCore.SLOT("animateClick()"))
278                self.addAction(action)
279               
280                action = QtGui.QAction(self)
281                action.setShortcut(QtGui.QKeySequence("Down"))
282                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonReverse, QtCore.SLOT("animateClick()"))
283                self.addAction(action)
284               
285               
286                action = QtGui.QAction(self)
287                action.setShortcut(QtGui.QKeySequence("D"))
288                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonTurnRight, QtCore.SLOT("animateClick()"))
289                self.addAction(action)
290               
291                action = QtGui.QAction(self)
292                action.setShortcut(QtGui.QKeySequence("Right"))
293                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonTurnRight, QtCore.SLOT("animateClick()"))
294                self.addAction(action)
295               
296               
297                action = QtGui.QAction(self)
298                action.setShortcut(QtGui.QKeySequence("Z"))
299                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonTurnLeftReverse, QtCore.SLOT("animateClick()"))
300                self.addAction(action)
301               
302               
303                action = QtGui.QAction(self)
304                action.setShortcut(QtGui.QKeySequence("C"))
305                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonTurnRightReverse, QtCore.SLOT("animateClick()"))
306                self.addAction(action)
307               
308               
309                self.pushButtonForward.setAutoRepeat(False)
310                self.pushButtonForward.setAutoRepeatDelay(0)
311                self.pushButtonForward.setAutoRepeatInterval(0)
312
313       
314        ##################################################################
315       
316        def turnLeft(self):
317                self.brainstormsClient.sendCommand('turn_left')
318                self.drive_state = 'turn_left'
319       
320        def driveForward(self):
321                #if self.DEBUG:
322                        #print "driveForward"
323                self.pushButtonForward.setDown(True)
324                if (self.drive_state != 'drive_forward'):
325                        self.updateSpeed(new_speed=DEFAULT_NXT_POWER_LEVEL)
326                self.brainstormsClient.sendCommand('drive_forward', power=self.current_speed)
327                self.drive_state = 'drive_forward'
328       
329        def turnRight(self):
330                self.brainstormsClient.sendCommand('turn_right')
331                self.drive_state = 'turn_right'
332       
333        def turnLeftInReverse(self):
334                self.brainstormsClient.sendCommand('turn_left_in_reverse')
335                self.drive_state = 'turn_left_in_reverse'
336       
337        def driveReverse(self):
338                self.brainstormsClient.sendCommand('drive_reverse')
339                self.drive_state = 'drive_reverse'
340       
341        def turnRightInReverse(self):
342                self.brainstormsClient.sendCommand('turn_right_in_reverse')
343                self.drive_state = 'turn_right_in_reverse'
344       
345        def stopMotors(self):
346                self.pushButtonForward.setDown(False)
347                if (self.current_speed != 0):
348                        self.updateSpeed(new_speed=0)
349                self.brainstormsClient.sendCommand('stop_motors')
350                self.drive_state = 'stop_motors'
351       
352       
353        ##################################################################
354       
355        def updateSpeed(self, new_speed=None):
356               
357                if new_speed == None:
358               
359                        concentration=self.progressBarConcentration.value()
360                        relaxation=self.progressBarRelaxation.value()
361                       
362                        new_speed = self.calculateSpeed(concentration, relaxation)
363               
364               
365                # Update GUI
366                self.progressBarSpeed.setValue(new_speed)
367               
368                # GUI button under "Speed" progress bar set to Enabled
369                speed_control = (self.pushButtonSpeedEnable.text() == 'Enabled')
370               
371                # If there is a change between the new and current speeds
372                # and either the robot is currently driving forward
373                # or the "speed control" button is enabled,
374                # then send the updated speed to the robot
375                if ((self.current_speed != new_speed) and \
376                         ((self.drive_state == 'drive_forward') or \
377                          (speed_control))):
378                       
379                        if (new_speed == 0):
380                                self.current_speed = new_speed
381                                self.stopMotors()
382                        else:
383                                self.pushButtonForward.setDown(True)
384                                self.brainstormsClient.sendCommand('drive_forward', power=new_speed)
385               
386               
387                self.current_speed = new_speed
388       
389       
390        ##################################################################
391       
392        def calculateSpeed(self, concentration, relaxation):
393               
394                speed = 0
395               
396                thresholds = THINKGEAR_POWER_THRESHOLDS
397               
398                match = int(concentration)
399               
400                while ((match not in thresholds['concentration'].keys()) and \
401                            (match >= 0)):
402                        match -= 1
403               
404               
405                if match in thresholds['concentration'].keys():
406                        speed = thresholds['concentration'][match]
407               
408               
409                match = int(relaxation)
410               
411                while ((match not in thresholds['relaxation'].keys()) and \
412                            (match >= 0)):
413                        match -= 1
414               
415                if match in thresholds['relaxation'].keys():
416                        speed = speed + thresholds['relaxation'][match]
417               
418               
419                # LEGO Mindstorms power settings cannot exceed 100
420                # and don't drive well with levels less than 50
421                if (speed > 100):
422                        speed = 100
423                elif (speed < 50):
424                        speed = 0
425               
426               
427                return(speed)
428       
429       
430        ##################################################################
431       
432        def processPacketThinkGear(self, packet):
433               
434                if ('eSense' in packet.keys()):
435                       
436                        if ('attention' in packet['eSense'].keys()):
437                                self.progressBarConcentration.setValue(packet['eSense']['attention'])
438                       
439                        if ('meditation' in packet['eSense'].keys()):
440                                self.progressBarRelaxation.setValue(packet['eSense']['meditation'])
441               
442               
443                self.updateSpeed()
444
445
446#####################################################################
447# Functions
448#####################################################################
449
450#####################################################################
451# Main
452#####################################################################
453
454if __name__ == '__main__':
455       
456        #log = puzzlebox_logger.puzzlebox_logger(logfile='client_interface')
457        log = None
458       
459        app = QtGui.QApplication(sys.argv)
460       
461        window = puzzlebox_brainstorms_client_interface(log, DEBUG)
462        window.show()
463       
464        sys.exit(app.exec_())
465
Note: See TracBrowser for help on using the repository browser.