source: trunk/Puzzlebox/Synapse/Server.py @ 292

Last change on this file since 292 was 292, checked in by sc, 10 years ago
  • Update to emit signals between Server and Interface instead of calling parent methods directly
  • Property svn:executable set to *
File size: 16.3 KB
Line 
1# -*- coding: utf-8 -*-
2
3# Copyright Puzzlebox Productions, LLC (2010-2011)
4#
5# This code is released under the GNU Pulic License (GPL) version 2
6# For more information please refer to http://www.gnu.org/copyleft/gpl.html
7
8# Old Class Name = puzzle_synapse_server_thinkgear
9
10__changelog__ = """\
11Last Update: 2011.08.15
12
13"""
14
15### IMPORTS ###
16
17import os, sys, time
18import signal
19import math
20
21import simplejson as json
22
23try:
24        import PySide as PyQt4
25        from PySide import QtCore, QtGui, QtNetwork
26except:
27        print "Using PyQt4 module"
28        from PyQt4 import QtCore, QtGui, QtNetwork
29else:
30        print "Using PySide module"
31
32#from PyQt4 import QtNetwork
33
34import Configuration as configuration
35import Protocol as serial_interface
36
37### GLOBALS ###
38
39DEBUG = 1
40
41SERVER_INTERFACE = configuration.THINKGEAR_SERVER_INTERFACE
42SERVER_PORT = configuration.THINKGEAR_SERVER_PORT
43THINKGEAR_DEVICE_SERIAL_PORT = configuration.THINKGEAR_DEVICE_SERIAL_PORT
44THINKGEAR_DEVICE_ID = configuration.THINKGEAR_DEVICE_ID
45
46CLIENT_NO_REPLY_WAIT = configuration.CLIENT_NO_REPLY_WAIT * 1000
47
48FLASH_POLICY_FILE_REQUEST = configuration.FLASH_POLICY_FILE_REQUEST
49FLASH_SOCKET_POLICY_FILE = configuration.FLASH_SOCKET_POLICY_FILE
50
51DELIMITER = configuration.THINKGEAR_DELIMITER
52
53MESSAGE_FREQUENCY_TIMER = 1 * 1000 # 1 Hz (1000 ms)
54
55ENABLE_SIMULATE_HEADSET_DATA = configuration.THINKGEAR_ENABLE_SIMULATE_HEADSET_DATA
56
57BLINK_FREQUENCY_TIMER = configuration.THINKGEAR_BLINK_FREQUENCY_TIMER
58
59DEFAULT_SAMPLE_WAVELENGTH = configuration.THINKGEAR_DEFAULT_SAMPLE_WAVELENGTH
60
61THINKGEAR_EMULATION_MAX_ESENSE_VALUE = \
62        configuration.THINKGEAR_EMULATION_MAX_ESENSE_VALUE
63THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE = \
64        configuration.THINKGEAR_EMULATION_MAX_EEG_POWER_VALUE
65
66THINKGEAR_ATTENTION_MULTIPLIER = configuration.THINKGEAR_ATTENTION_MULTIPLIER
67THINKGEAR_MEDITATION_MULTIPLIER = configuration.THINKGEAR_MEDITATION_MULTIPLIER
68
69THINKGEAR_EEG_POWER_MULTIPLIERS = configuration.THINKGEAR_EEG_POWER_MULTIPLIERS
70
71DEFAULT_AUTHORIZATION_MESSAGE = \
72        {"isAuthorized": True}
73                # Tells the client whether the server has authorized
74                # access to the user's headset data. The value is
75                # either true or false.
76
77DEFAULT_SIGNAL_LEVEL_MESSAGE = \
78        {"poorSignalLevel": 0}
79                # A quantifier of the quality of the brainwave signal.
80                # This is an integer value that is generally in the
81                # range of 0 to 200, with 0 indicating a
82                # good signal and 200 indicating an off-head state.
83
84DEFAULT_EEG_POWER_MESSAGE = \
85        {"eegPower": { \
86                'delta': 0, \
87                'theta': 0, \
88                'lowAlpha': 0, \
89                'highAlpha': 0, \
90                'lowBeta': 0, \
91                'highBeta': 0, \
92                'lowGamma': 0, \
93                'highGamma': 0, \
94                }, \
95        } # A container for the EEG powers. These may
96          # be either integer or floating-point values.
97          # Maximum values are undocumented but assumed to be 65535
98
99DEFAULT_ESENSE_MESSAGE = \
100        {"eSense": { \
101                'attention': 0, \
102                'meditation': 0, \
103                }, \
104        } # A container for the eSense™ attributes.
105          # These are integer values between 0 and 100,
106          # where 0 is perceived as a lack of that attribute
107          # and 100 is an excess of that attribute.
108
109DEFAULT_BLINK_MESSAGE = {"blinkStrength": 255}
110        # The strength of a detected blink. This is
111        # an integer in the range of 0-255.
112
113DEFAULT_RAWEEG_MESSAGE = {"rawEeg": 255}
114        # The raw data reading off the forehead sensor.
115        # This may be either an integer or a floating-point value.
116
117DEFAULT_PACKET = {}
118DEFAULT_PACKET.update(DEFAULT_EEG_POWER_MESSAGE)
119DEFAULT_PACKET.update(DEFAULT_SIGNAL_LEVEL_MESSAGE)
120DEFAULT_PACKET.update(DEFAULT_ESENSE_MESSAGE)
121
122DEFAULT_RESPONSE_MESSAGE = DEFAULT_SIGNAL_LEVEL_MESSAGE
123
124### CLASS MODULE ###
125
126class ThinkgearServer(QtCore.QThread):
127       
128        def __init__(self, log, \
129                          server_interface=SERVER_INTERFACE, \
130                          server_port=SERVER_PORT, \
131                          device_address=THINKGEAR_DEVICE_SERIAL_PORT, \
132                          device_id=THINKGEAR_DEVICE_ID, \
133                          emulate_headset_data=ENABLE_SIMULATE_HEADSET_DATA, \
134                          DEBUG=DEBUG, \
135                          parent=None):
136               
137                QtCore.QThread.__init__(self,parent)
138               
139                self.log = log
140                self.DEBUG = DEBUG
141                self.parent = parent
142               
143                self.server_interface = server_interface
144                self.server_port = server_port
145                self.device_address = device_address
146                self.device_id = device_id
147                self.emulate_headset_data = emulate_headset_data
148               
149                self.message_frequency_timer = MESSAGE_FREQUENCY_TIMER
150                self.blink_frequency_timer = BLINK_FREQUENCY_TIMER
151               
152                self.connection_timestamp = time.time()
153                self.blink_timestamp = time.time()
154               
155                self.connections = []
156                self.packet_queue = []
157               
158                self.serial_device = None
159                self.protocol = None
160               
161               
162                self.configureEEG()
163               
164                self.configureNetwork()
165               
166               
167                if (self.emulate_headset_data):
168                        self.emulationTimer = QtCore.QTimer()
169                        QtCore.QObject.connect(self.emulationTimer, \
170                                                    QtCore.SIGNAL("timeout()"), \
171                                                    self.emulationEvent)
172                        self.emulationTimer.start(MESSAGE_FREQUENCY_TIMER)
173       
174       
175        ##################################################################
176       
177        def configureEEG(self):
178               
179                if not self.emulate_headset_data:
180                       
181                        self.serial_device = \
182                                serial_interface.SerialDevice( \
183                                        self.log, \
184                                        device_address=self.device_address, \
185                                        DEBUG=self.DEBUG, \
186                                        parent=self)
187                       
188                        self.serial_device.start()
189               
190                else:
191                        self.serial_device = None
192               
193               
194                self.protocol = \
195                        serial_interface.ProtocolHandler( \
196                                self.log, \
197                                self.serial_device, \
198                                self.device_id, \
199                                DEBUG=self.DEBUG, \
200                                parent=self)
201               
202                self.protocol.start()
203       
204       
205        ##################################################################
206       
207        def emulationEvent(self):
208               
209                self.updateStatus()
210                #self.sendPacketQueue()
211                self.emitSendPacketSignal()
212       
213       
214        ##################################################################
215       
216        def configureNetwork(self):
217       
218                #self.blockSize = 0
219                self.socket = QtNetwork.QTcpServer()
220                self.socket.name = 'ThinkGear Server'
221               
222                if self.DEBUG:
223                        print "<---- [%s] Initializing server on %s:%i" % \
224                           (self.socket.name, self.server_interface, self.server_port)
225               
226               
227                if ((self.server_interface == '') or \
228                         (self.server_interface == '*')):
229                        address=QtNetwork.QHostAddress.Any
230                else:
231                        #address=self.server_interface
232                        address=QtNetwork.QHostAddress(self.server_interface)
233               
234               
235                result = self.socket.listen(address, self.server_port)
236               
237               
238                if not result:
239                        try:
240                                QtGui.QMessageBox.information( \
241                                self.parent, \
242                                self.socket.name, \
243                                "Unable to start the server on %s:%i" % \
244                                (self.server_interface, self.server_port))
245                        except:
246                                pass
247                       
248                        if self.DEBUG:
249                                print "ERROR [%s] Unable to start the server:" % self.socket.name,
250                                print self.socket.errorString()
251                       
252                        #self.parent.stopThinkGearConnectServer()
253                        #self.parent.pushButtonThinkGearConnect.nextCheckState()
254                        #self.parent.pushButtonThinkGearConnect.toggle()
255                        #self.parent.pushButtonThinkGearConnect.test.emit(QtCore.SIGNAL("clicked()"))
256                       
257                        self.socket.close()
258                        return
259               
260               
261                self.socket.newConnection.connect(self.processConnection)
262                #self.socket.error.connect(self.displayError)
263       
264       
265        ##################################################################
266       
267        def deleteDisconnected(self):
268               
269                connection_index = 0
270               
271                for connection in self.connections:
272                       
273                        try:
274                       
275                                if ((connection.state() != QtNetwork.QAbstractSocket.ConnectingState) and \
276                                        (connection.state() != QtNetwork.QAbstractSocket.ConnectedState)):
277                                       
278                                        if self.DEBUG:
279                                                print "- - [%s] Deleting disconnected socket" % self.socket.name
280                                       
281                                        connection.deleteLater()
282                                        # Delete references to disconnected sockets
283                                        del (self.connections[connection_index])
284                       
285                        except:
286                                # Delete references to sockets throwing exceptions
287                                del (self.connections[connection_index])
288                       
289                        connection_index += 1
290       
291       
292        ##################################################################
293       
294        def processConnection(self):
295               
296                clientConnection = self.socket.nextPendingConnection()
297                clientConnection.disconnected.connect(self.deleteDisconnected)
298               
299                self.connections.append(clientConnection)
300               
301                self.clientConnection = clientConnection
302               
303                # the next connected client to enter the readyRead state
304                # will be processed first
305                clientConnection.readyRead.connect(self.processClientConnection)
306       
307       
308        ##################################################################
309       
310        def processClientConnection(self):
311               
312                clientConnection = self.clientConnection
313               
314                socket_buffer = clientConnection.readAll()
315               
316                for packet in socket_buffer.split(DELIMITER):
317                       
318                        data_to_process = None
319                       
320                        if packet != '':
321                               
322                                try:
323                                        data_to_process = json.loads(packet.data())
324                               
325                                except Exception, e:
326                                       
327                                        # Special socket handling for Flash applications
328                                        if (packet == FLASH_POLICY_FILE_REQUEST):
329                                               
330                                                if self.DEBUG:
331                                                        print "--> [%s] Flash policy file requested" % self.socket.name
332                                               
333                                                data_to_process = packet.data()
334                                       
335                                       
336                                        else:
337                                               
338                                                if self.DEBUG:
339                                                        print "--> [ThinkGear Emulator] Partial data received (or error:",
340                                                        print e
341                                                        print ")."
342                                                       
343                                                        print "packet data:",
344                                                        print packet.data()
345                               
346                               
347                                else:
348                                       
349                                        if self.DEBUG:
350                                                print "--> [%s] Received:" % self.socket.name,
351                                                print data_to_process
352                               
353                               
354                                if (data_to_process != None):
355                                       
356                                        response = self.processData(data_to_process)
357                                       
358                                        if (response != None):
359                                               
360                                                self.sendResponse(clientConnection, response)
361       
362       
363        ##################################################################
364       
365        def sendResponse(self, connection, response, disconnect_after_sending=False):
366               
367                # Special socket handling for Flash applications
368                if (response == FLASH_SOCKET_POLICY_FILE):
369                        data = response
370                else:
371                        data = json.dumps(response)
372                        data = data + DELIMITER
373               
374                if connection.waitForConnected(CLIENT_NO_REPLY_WAIT):
375                       
376                        if self.DEBUG > 1:
377                                print "<-- [%s] Sending:" % self.socket.name,
378                                print data
379                       
380                        connection.write(data)
381                       
382                        connection.waitForBytesWritten(CLIENT_NO_REPLY_WAIT)
383                       
384                        if disconnect_after_sending:
385                                connection.disconnectFromHost()
386       
387       
388        ##################################################################
389       
390        def emitSendPacketSignal(self):
391       
392                self.emit( \
393                   QtCore.SIGNAL("sendPacket()"))
394       
395       
396        ##################################################################
397       
398        def sendPacketQueue(self):
399               
400                if self.DEBUG:
401                        print "sendPacketQueue called"
402               
403                if self.connections != []:
404                       
405                        while (len(self.packet_queue) > 0):
406                               
407                                packet = self.packet_queue[0]
408                                del self.packet_queue[0]
409                               
410                                for connection in self.connections:
411                                       
412                                        if connection.state() == QtNetwork.QAbstractSocket.ConnectedState:
413                                               
414                                                self.sendResponse(connection, packet)
415       
416       
417        ##################################################################
418       
419        def processData(self, data):
420               
421                response = None
422               
423                # Special socket handling for Flash applications
424                if (data == FLASH_POLICY_FILE_REQUEST):
425                       
426                        response = FLASH_SOCKET_POLICY_FILE
427                       
428                        #self.packet_queue.insert(0, FLASH_SOCKET_POLICY_FILE)
429               
430               
431                elif (type(data) == type({}) and \
432                      data.has_key('appName') and \
433                      data.has_key('appKey')):
434                        authorized = self.authorizeClient(data)
435                       
436                        response = {}
437                        response['isAuthorized'] = authorized
438                       
439                        #self.packet_queue.insert(0, response)
440               
441               
442                return(response)
443       
444       
445        ##################################################################
446       
447        def validateChecksum(self, checksum):
448               
449                '''The key used by the client application to identify
450itself. This must be 40 hexadecimal characters, ideally generated
451using an SHA-1 digest. The appKey is an identifier that is unique
452to each application, rather than each instance of an application.
453It is used by the server to bypass the authorization process if a
454user had previously authorized the requesting client. To reduce
455the chance of overlap with the appKey of other applications,
456the appKey should be generated using an SHA-1 digest.'''
457               
458                is_valid = True
459               
460                hexadecimal_characters = '0123456789abcdef'
461               
462                if len(checksum) != 40:
463                        is_valid = False
464                else:
465                        for character in checksum:
466                                if character not in hexadecimal_characters:
467                                        is_valid = False
468               
469                return(is_valid)
470       
471       
472        ##################################################################
473       
474        def authorizeClient(self, data):
475       
476                '''The client must initiate an authorization request
477and the server must authorize the client before the
478server will start transmitting any headset data.'''
479               
480                is_authorized = self.validateChecksum(data['appKey'])
481               
482                # A human-readable name identifying the client
483                # application. This can be a maximum of 255 characters.
484               
485                if len(data['appName']) > 255:
486                        is_authorized = False
487               
488               
489                return(is_authorized)
490       
491       
492        ##################################################################
493       
494        def calculateWavePoint(self, x, max_height=100, wave_length=10):
495               
496                # start at 0, increase to max value at half of one
497                # wavelength, decrease to 0 by end of wavelength
498                y = ( (max_height/2) * \
499                      math.sin ((x-1) * ( math.pi / (wave_length / 2)))) + \
500                      (max_height/2)
501               
502                # start at max value, decrease to 0 at half of one
503                # wavelegnth, increase to max by end of wavelength
504                #y = ( (max_height/2) * \
505                      #math.cos (x * ( math.pi / (wave_length / 2)))) + \
506                      #(max_height/2)
507               
508               
509                return(y)
510       
511       
512        ##################################################################
513       
514        def simulateHeadsetData(self):
515               
516                response = DEFAULT_PACKET
517               
518                response['timestamp'] = time.time()
519               
520                time_value = self.connection_timestamp - time.time()
521               
522                for key in response.keys():
523                       
524                        if key == 'poorSignalLevel':
525                                pass
526                       
527                        elif key == 'eSense':
528                                plot = self.calculateWavePoint( \
529                                        time_value, \
530                                        max_height=100, \
531                                        wave_length=DEFAULT_SAMPLE_WAVELENGTH)
532                               
533                                for each in response[key].keys():
534                                       
535                                        if ((each == 'attention') and \
536                                                 (THINKGEAR_ATTENTION_MULTIPLIER != None)):
537                                                value = plot * \
538                                                   THINKGEAR_ATTENTION_MULTIPLIER
539                                       
540                                        elif ((each == 'meditation') and \
541                                                   (THINKGEAR_MEDITATION_MULTIPLIER != None)):
542                                                value = plot * \
543                                                   THINKGEAR_MEDITATION_MULTIPLIER
544                                       
545                                        if value < 0:
546                                                value = 0
547                                        elif value > 100:
548                                                value = 100
549                                       
550                                        response[key][each] = value
551                       
552                       
553                        elif key == 'eegPower':
554                                plot = self.calculateWavePoint( \
555                                        time_value, \
556                                        max_height=65535, \
557                                        wave_length=DEFAULT_SAMPLE_WAVELENGTH)
558                               
559                                for each in response[key].keys():
560                                        if ((THINKGEAR_EEG_POWER_MULTIPLIERS != None) and \
561                                                 (each in THINKGEAR_EEG_POWER_MULTIPLIERS.keys())):
562                                                value = THINKGEAR_EEG_POWER_MULTIPLIERS[each] * plot
563                                        else:
564                                                value = plot
565                                        response[key][each] = value
566               
567               
568                return(response)
569       
570       
571        ##################################################################
572       
573        def processPacketThinkGear(self, packet):
574               
575                if self.DEBUG > 2:
576                        print packet
577               
578                if (packet != {}):
579                        self.packet_queue.append(packet)
580                        #self.sendPacketQueue()
581                        self.emitSendPacketSignal()
582               
583                #if (self.parent != None):
584                        #self.parent.processPacketThinkGear(self.protocol.data_packet)
585       
586       
587        ##################################################################
588       
589        def updateStatus(self):
590               
591                # Craft a simulated data packet
592                packet = self.simulateHeadsetData()
593               
594                self.packet_queue.append(packet)
595               
596                if (self.parent != None):
597                        self.parent.processPacketThinkGear(packet)
598               
599                # Include simulated blinks at desired frequency
600                if ((self.blink_frequency_timer != None) and \
601                                (self.blink_frequency_timer > 0) and \
602                                (time.time() - self.blink_timestamp > \
603                                self.blink_frequency_timer)):
604                       
605                        self.blink_timestamp = time.time()
606                       
607                        packet = DEFAULT_BLINK_MESSAGE
608                       
609                        packet['timestamp'] = self.blink_timestamp
610                       
611                        self.packet_queue.append(packet)
612       
613       
614        ##################################################################
615       
616        def run(self):
617               
618                if self.DEBUG:
619                        print "<---- [%s] Main thread running" % self.socket.name
620               
621                self.exec_()
622       
623               
624        ##################################################################
625       
626        def exitThread(self, callThreadQuit=True):
627               
628                try:
629                        self.emulationTimer.stop()
630                except:
631                        pass
632               
633                if self.serial_device != None:
634                        self.serial_device.exitThread()
635               
636                if self.protocol != None:
637                        self.protocol.exitThread()
638               
639                self.socket.close()
640               
641                if callThreadQuit:
642                        QtCore.QThread.quit(self)
643               
644                if self.parent == None:
645                        sys.exit()
646       
647       
648        ##################################################################
649       
650        def resetDevice(self):
651               
652                if self.serial_device != None:
653                        self.serial_device.exitThread()
654               
655                if self.protocol != None:
656                        self.protocol.exitThread()
657               
658                self.configureEEG()
659
Note: See TracBrowser for help on using the repository browser.