source: remote_control/puzzlebox_brainstorms_client_interface.py @ 97

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

remote_control/puzzlebox_brainstorms_client_interface.py:

  • corrected handling of disconnected Brainstorms server

remote_control/puzzlebox_brainstorms_client_interface_local.py:

  • thinkgear emulator added in to test server/client threading issues
File size: 15.4 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.09
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.connectWidgets()
73               
74                self.brainstormsClient = None
75               
76                self.drive_state = 'stop_motors'
77                self.current_speed = 0
78       
79       
80        ##################################################################
81       
82        def configureSettings(self):
83               
84                # LEGO Mindstorms
85               
86                self.textLabelNXTStatus.setText("Status: Disconnected")
87               
88                # Display communication port for LEGO Mindstorms NXT device
89                self.lineEditNXTPort.setText(NXT_BLUETOOTH_DEVICE)
90               
91                # LEGO Mindstorms NXT COM port selection not yet available
92                self.lineEditNXTPort.setEnabled(False)
93               
94               
95                # EEG Headset
96               
97                # Display Host for ThinkGear Connect Socket Server
98                self.lineEditThinkGearHost.setText(THINKGEAR_SERVER_HOST)
99                #self.lineEditThinkGearHost.setEnabled(False)
100               
101                # Display Port for ThinkGear Connect Socket Server
102                self.lineEditThinkGearPort.setText('%i' % THINKGEAR_SERVER_PORT)
103                #self.lineEditThinkGearPort.setEnabled(False)
104       
105       
106        ##################################################################
107       
108        def getMinimumThreshold(self, threshold):
109               
110                '''Return the minimum detection level which results
111                in a non-zero power setting'''
112               
113                minimum = 100
114               
115                threshold_keys = threshold.keys()
116                threshold_keys.sort()
117                threshold_keys.reverse()
118               
119                for key in threshold_keys:
120                       
121                        if ((threshold[key] < minimum) and \
122                                 (threshold[key] > 0)):
123                                minimum = key
124               
125               
126                return(minimum)
127       
128       
129        ##################################################################
130       
131        def configureNetworkBrainstorms(self):
132               
133                self.brainstormsClient = \
134                   brainstorms_client.puzzlebox_brainstorms_network_client(self.log, parent=self)
135       
136       
137        ##################################################################
138       
139        def connectToBrainstormsServer(self):
140               
141                if self.DEBUG:
142                        print "Connecting to Brainstorms Server"
143               
144                #server_port = int(self.lineEditNXTPort.text())
145               
146                self.configureNetworkBrainstorms()
147               
148                #if (self.brainstormsClient.socket.state() != QtNetwork.QAbstractSocket.ConnectedState):
149                        #QtGui.QMessageBox.information(self, \
150                                                                #self.brainstormsClient.socket.name, \
151                                           #"Failed to connect to Brainstorms socket server")
152               
153                #else:
154                self.disconnect(self.pushButtonNXTConnect, \
155                                                        QtCore.SIGNAL("clicked()"), \
156                                                        self.connectToBrainstormsServer)
157               
158                self.connect(self.pushButtonNXTConnect, \
159                                                        QtCore.SIGNAL("clicked()"), \
160                                                        self.disconnectFromBrainstormsServer)
161               
162                self.textLabelNXTStatus.setText("Status: Connected")
163                self.pushButtonNXTConnect.setText('Disconnect')
164               
165                self.lineEditNXTPort.setEnabled(False)
166       
167       
168        ##################################################################
169       
170        def disconnectFromBrainstormsServer(self):
171               
172                if self.DEBUG:
173                        print "Disconnecting from Brainstorms Server"
174               
175                self.brainstormsClient.socket.disconnectFromHost()
176               
177                self.disconnect(self.pushButtonNXTConnect, \
178                                  QtCore.SIGNAL("clicked()"), \
179                                  self.disconnectFromBrainstormsServer)
180               
181                self.connect(self.pushButtonNXTConnect, \
182                                  QtCore.SIGNAL("clicked()"), \
183                                  self.connectToBrainstormsServer)
184               
185                self.textLabelNXTStatus.setText("Status: Disconnected")
186                self.pushButtonNXTConnect.setText('Connect')
187               
188                # LEGO Mindstorms NXT COM port selection not yet available
189                #self.lineEditNXTPort.setEnabled(True)
190               
191                self.brainstormsClient = None
192       
193       
194        ##################################################################
195       
196        def connectToThinkGearHost(self):
197               
198                if self.DEBUG:
199                        print "Connecting to ThinkGear Host"
200               
201                server_host = str(self.lineEditThinkGearHost.text())
202                server_port = int(self.lineEditThinkGearPort.text())
203               
204                self.thinkgearClient = \
205                   thinkgear_client.puzzlebox_brainstorms_network_client_thinkgear( \
206                           self.log, \
207                           server_host=server_host, \
208                           server_port=server_port, \
209                           DEBUG=0, \
210                           parent=self)
211               
212                if (self.thinkgearClient.socket.state() != QtNetwork.QAbstractSocket.ConnectedState):
213                        QtGui.QMessageBox.information(self, \
214                                                                self.thinkgearClient.socket.name, \
215                                           "Failed to connect to ThinkGear socket server")
216               
217                else:
218                        self.disconnect(self.pushButtonThinkGearConnect, \
219                                                         QtCore.SIGNAL("clicked()"), \
220                                                         self.connectToThinkGearHost)
221                       
222                        self.connect(self.pushButtonThinkGearConnect, \
223                                                         QtCore.SIGNAL("clicked()"), \
224                                                         self.disconnectFromThinkGearHost)
225                       
226                        self.pushButtonThinkGearConnect.setText('Disconnect')
227                       
228                        self.lineEditThinkGearHost.setEnabled(False)
229                        self.lineEditThinkGearPort.setEnabled(False)
230       
231       
232        ##################################################################
233       
234        def disconnectFromThinkGearHost(self):
235               
236                if self.DEBUG:
237                        print "Disconnecting from ThinkGear Host"
238               
239                self.thinkgearClient.disconnectFromHost()
240               
241                self.disconnect(self.pushButtonThinkGearConnect, \
242                                  QtCore.SIGNAL("clicked()"), \
243                                  self.disconnectFromThinkGearHost)
244               
245                self.connect(self.pushButtonThinkGearConnect, \
246                                  QtCore.SIGNAL("clicked()"), \
247                                  self.connectToThinkGearHost)
248               
249                self.pushButtonForward.emit(QtCore.SIGNAL("released()"))
250               
251                self.pushButtonThinkGearConnect.setText('Connect')
252               
253                self.lineEditThinkGearHost.setEnabled(True)
254                self.lineEditThinkGearPort.setEnabled(True)
255               
256                self.progressBarConcentration.setValue(0)
257                self.progressBarRelaxation.setValue(0)
258                self.progressBarSpeed.setValue(0)
259       
260       
261        ##################################################################
262       
263        def connectWidgets(self):
264               
265                self.connect(self.pushButtonTurnLeft, QtCore.SIGNAL("pressed()"), \
266                             self.turnLeft)
267                self.connect(self.pushButtonTurnLeft, QtCore.SIGNAL("released()"), \
268                             self.stopMotors)
269               
270                self.connect(self.pushButtonForward, QtCore.SIGNAL("pressed()"), \
271                             self.driveForward)
272                self.connect(self.pushButtonForward, QtCore.SIGNAL("released()"), \
273                             self.stopMotors)
274               
275                self.connect(self.pushButtonTurnRight, QtCore.SIGNAL("pressed()"), \
276                             self.turnRight)
277                self.connect(self.pushButtonTurnRight, QtCore.SIGNAL("released()"), \
278                             self.stopMotors)
279               
280                self.connect(self.pushButtonTurnLeftReverse, QtCore.SIGNAL("pressed()"), \
281                             self.turnLeftInReverse)
282                self.connect(self.pushButtonTurnLeftReverse, QtCore.SIGNAL("released()"), \
283                             self.stopMotors)
284               
285                self.connect(self.pushButtonReverse, QtCore.SIGNAL("pressed()"), \
286                             self.driveReverse)
287                self.connect(self.pushButtonReverse, QtCore.SIGNAL("released()"), \
288                             self.stopMotors)
289               
290                self.connect(self.pushButtonTurnRightReverse, QtCore.SIGNAL("pressed()"), \
291                             self.turnRightInReverse)
292                self.connect(self.pushButtonTurnRightReverse, QtCore.SIGNAL("released()"), \
293                             self.stopMotors)
294               
295               
296                self.connect(self.pushButtonNXTConnect, \
297                                  QtCore.SIGNAL("clicked()"), \
298                                  self.connectToBrainstormsServer)
299               
300                self.connect(self.pushButtonThinkGearConnect, \
301                                  QtCore.SIGNAL("clicked()"), \
302                                  self.connectToThinkGearHost)
303       
304       
305                #shortcut = QtGui.QShortcut(self)
306                #shortcut.setKey(tr("Down"))
307                #self.connect(shortcut, QtCore.SIGNAL("pressed()"), self.driveReverse)
308               
309                action = QtGui.QAction(self)
310                action.setShortcut(QtGui.QKeySequence("W"))
311                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonForward, QtCore.SLOT("animateClick()"))
312                self.addAction(action)
313               
314                action = QtGui.QAction(self)
315                action.setShortcut(QtGui.QKeySequence("Up"))
316                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonForward, QtCore.SLOT("animateClick()"))
317                self.addAction(action)
318               
319               
320                action = QtGui.QAction(self)
321                action.setShortcut(QtGui.QKeySequence("Left"))
322                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonTurnLeft, QtCore.SLOT("animateClick()"))
323                self.addAction(action)
324               
325                action = QtGui.QAction(self)
326                action.setShortcut(QtGui.QKeySequence("A"))
327                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonTurnLeft, QtCore.SLOT("animateClick()"))
328                self.addAction(action)
329               
330               
331                action = QtGui.QAction(self)
332                action.setShortcut(QtGui.QKeySequence("S"))
333                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonReverse, QtCore.SLOT("animateClick()"))
334                self.addAction(action)
335               
336                action = QtGui.QAction(self)
337                action.setShortcut(QtGui.QKeySequence("Down"))
338                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonReverse, QtCore.SLOT("animateClick()"))
339                self.addAction(action)
340               
341               
342                action = QtGui.QAction(self)
343                action.setShortcut(QtGui.QKeySequence("D"))
344                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonTurnRight, QtCore.SLOT("animateClick()"))
345                self.addAction(action)
346               
347                action = QtGui.QAction(self)
348                action.setShortcut(QtGui.QKeySequence("Right"))
349                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonTurnRight, QtCore.SLOT("animateClick()"))
350                self.addAction(action)
351               
352               
353                action = QtGui.QAction(self)
354                action.setShortcut(QtGui.QKeySequence("Z"))
355                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonTurnLeftReverse, QtCore.SLOT("animateClick()"))
356                self.addAction(action)
357               
358               
359                action = QtGui.QAction(self)
360                action.setShortcut(QtGui.QKeySequence("C"))
361                self.connect(action, QtCore.SIGNAL("activated()"), self.pushButtonTurnRightReverse, QtCore.SLOT("animateClick()"))
362                self.addAction(action)
363               
364               
365                self.pushButtonForward.setAutoRepeat(False)
366                self.pushButtonForward.setAutoRepeatDelay(0)
367                self.pushButtonForward.setAutoRepeatInterval(0)
368
369       
370        ##################################################################
371       
372        def turnLeft(self):
373                self.brainstormsClient.sendCommand('turn_left')
374                self.drive_state = 'turn_left'
375       
376        def driveForward(self):
377                #if self.DEBUG:
378                        #print "driveForward"
379                self.pushButtonForward.setDown(True)
380                if (self.drive_state != 'drive_forward'):
381                        self.updateSpeed(new_speed=DEFAULT_NXT_POWER_LEVEL)
382                self.brainstormsClient.sendCommand('drive_forward', power=self.current_speed)
383                self.drive_state = 'drive_forward'
384       
385        def turnRight(self):
386                self.brainstormsClient.sendCommand('turn_right')
387                self.drive_state = 'turn_right'
388       
389        def turnLeftInReverse(self):
390                self.brainstormsClient.sendCommand('turn_left_in_reverse')
391                self.drive_state = 'turn_left_in_reverse'
392       
393        def driveReverse(self):
394                self.brainstormsClient.sendCommand('drive_reverse')
395                self.drive_state = 'drive_reverse'
396       
397        def turnRightInReverse(self):
398                self.brainstormsClient.sendCommand('turn_right_in_reverse')
399                self.drive_state = 'turn_right_in_reverse'
400       
401        def stopMotors(self):
402                self.pushButtonForward.setDown(False)
403                if (self.current_speed != 0):
404                        self.updateSpeed(new_speed=0)
405                if self.brainstormsClient != None:
406                        self.brainstormsClient.sendCommand('stop_motors')
407                self.drive_state = 'stop_motors'
408       
409       
410        ##################################################################
411       
412        def updateSpeed(self, new_speed=None):
413               
414                if new_speed == None:
415               
416                        concentration=self.progressBarConcentration.value()
417                        relaxation=self.progressBarRelaxation.value()
418                       
419                        new_speed = self.calculateSpeed(concentration, relaxation)
420               
421               
422                # Update GUI
423                self.progressBarSpeed.setValue(new_speed)
424               
425                # GUI button under "Speed" progress bar set to Enabled
426                speed_control = (self.pushButtonSpeedEnable.text() == 'Enabled')
427               
428                # If there is a change between the new and current speeds
429                # and either the robot is currently driving forward
430                # or the "speed control" button is enabled,
431                # then send the updated speed to the robot
432                if ((self.current_speed != new_speed) and \
433                         ((self.drive_state == 'drive_forward') or \
434                          (speed_control))):
435                       
436                        if (new_speed == 0):
437                                self.current_speed = new_speed
438                                self.stopMotors()
439                        else:
440                                if self.brainstormsClient != None:
441                                        self.pushButtonForward.setDown(True)
442                                        self.brainstormsClient.sendCommand('drive_forward', power=new_speed)
443               
444               
445                self.current_speed = new_speed
446       
447       
448        ##################################################################
449       
450        def calculateSpeed(self, concentration, relaxation):
451               
452                speed = 0
453               
454                thresholds = THINKGEAR_POWER_THRESHOLDS
455               
456                match = int(concentration)
457               
458                while ((match not in thresholds['concentration'].keys()) and \
459                            (match >= 0)):
460                        match -= 1
461               
462               
463                if match in thresholds['concentration'].keys():
464                        speed = thresholds['concentration'][match]
465               
466               
467                match = int(relaxation)
468               
469                while ((match not in thresholds['relaxation'].keys()) and \
470                            (match >= 0)):
471                        match -= 1
472               
473                if match in thresholds['relaxation'].keys():
474                        speed = speed + thresholds['relaxation'][match]
475               
476               
477                # LEGO Mindstorms power settings cannot exceed 100
478                # and don't drive well with levels less than 50
479                if (speed > 100):
480                        speed = 100
481                elif (speed < 50):
482                        speed = 0
483               
484               
485                return(speed)
486       
487       
488        ##################################################################
489       
490        def processPacketThinkGear(self, packet):
491               
492                if ('eSense' in packet.keys()):
493                       
494                        if ('attention' in packet['eSense'].keys()):
495                                self.progressBarConcentration.setValue(packet['eSense']['attention'])
496                       
497                        if ('meditation' in packet['eSense'].keys()):
498                                self.progressBarRelaxation.setValue(packet['eSense']['meditation'])
499               
500               
501                self.updateSpeed()
502
503
504#####################################################################
505# Functions
506#####################################################################
507
508#####################################################################
509# Main
510#####################################################################
511
512if __name__ == '__main__':
513       
514        #log = puzzlebox_logger.puzzlebox_logger(logfile='client_interface')
515        log = None
516       
517        app = QtGui.QApplication(sys.argv)
518       
519        window = puzzlebox_brainstorms_client_interface(log, DEBUG)
520        window.show()
521       
522        sys.exit(app.exec_())
523
Note: See TracBrowser for help on using the repository browser.