source: remote_control/puzzlebox_brainstorms_client_interface.py @ 96

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

remote_control/puzzlebox_brainstorms_client_interface.py:

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