source: remote_control/puzzlebox_brainstorms_client_interface.py @ 102

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

thinkgear_emulator/puzzlebox_thinkgear_server.py:

  • error message bugfix

remote_control/puzzlebox_brainstorms_client_interface.py:

  • To Do notes updated

remote_control/puzzlebox_brainstorms_client_interface_local.py:

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