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

Last change on this file since 291 was 291, checked in by sc, 10 years ago
  • Updated for new Puzzlebox Jigsaw plug-in model
  • Property svn:executable set to *
File size: 16.0 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       
212       
213        ##################################################################
214       
215        def configureNetwork(self):
216       
217                #self.blockSize = 0
218                self.socket = QtNetwork.QTcpServer()
219                self.socket.name = 'ThinkGear Server'
220               
221                if self.DEBUG:
222                        print "<---- [%s] Initializing server on %s:%i" % \
223                           (self.socket.name, self.server_interface, self.server_port)
224               
225               
226                if ((self.server_interface == '') or \
227                         (self.server_interface == '*')):
228                        address=QtNetwork.QHostAddress.Any
229                else:
230                        #address=self.server_interface
231                        address=QtNetwork.QHostAddress(self.server_interface)
232               
233               
234                result = self.socket.listen(address, self.server_port)
235               
236               
237                if not result:
238                        try:
239                                QtGui.QMessageBox.information( \
240                                self.parent, \
241                                self.socket.name, \
242                                "Unable to start the server on %s:%i" % \
243                                (self.server_interface, self.server_port))
244                        except:
245                                pass
246                       
247                        if self.DEBUG:
248                                print "ERROR [%s] Unable to start the server:" % self.socket.name,
249                                print self.socket.errorString()
250                       
251                        #self.parent.stopThinkGearConnectServer()
252                        #self.parent.pushButtonThinkGearConnect.nextCheckState()
253                        #self.parent.pushButtonThinkGearConnect.toggle()
254                        #self.parent.pushButtonThinkGearConnect.test.emit(QtCore.SIGNAL("clicked()"))
255                       
256                        self.socket.close()
257                        return
258               
259               
260                self.socket.newConnection.connect(self.processConnection)
261                #self.socket.error.connect(self.displayError)
262       
263       
264        ##################################################################
265       
266        def deleteDisconnected(self):
267               
268                connection_index = 0
269               
270                for connection in self.connections:
271                       
272                        try:
273                       
274                                if ((connection.state() != QtNetwork.QAbstractSocket.ConnectingState) and \
275                                        (connection.state() != QtNetwork.QAbstractSocket.ConnectedState)):
276                                       
277                                        if self.DEBUG:
278                                                print "- - [%s] Deleting disconnected socket" % self.socket.name
279                                       
280                                        connection.deleteLater()
281                                        # Delete references to disconnected sockets
282                                        del (self.connections[connection_index])
283                       
284                        except:
285                                # Delete references to sockets throwing exceptions
286                                del (self.connections[connection_index])
287                       
288                        connection_index += 1
289       
290       
291        ##################################################################
292       
293        def processConnection(self):
294               
295                clientConnection = self.socket.nextPendingConnection()
296                clientConnection.disconnected.connect(self.deleteDisconnected)
297               
298                self.connections.append(clientConnection)
299               
300                self.clientConnection = clientConnection
301               
302                # the next connected client to enter the readyRead state
303                # will be processed first
304                clientConnection.readyRead.connect(self.processClientConnection)
305       
306       
307        ##################################################################
308       
309        def processClientConnection(self):
310               
311                clientConnection = self.clientConnection
312               
313                socket_buffer = clientConnection.readAll()
314               
315                for packet in socket_buffer.split(DELIMITER):
316                       
317                        data_to_process = None
318                       
319                        if packet != '':
320                               
321                                try:
322                                        data_to_process = json.loads(packet.data())
323                               
324                                except Exception, e:
325                                       
326                                        # Special socket handling for Flash applications
327                                        if (packet == FLASH_POLICY_FILE_REQUEST):
328                                               
329                                                if self.DEBUG:
330                                                        print "--> [%s] Flash policy file requested" % self.socket.name
331                                               
332                                                data_to_process = packet.data()
333                                       
334                                       
335                                        else:
336                                               
337                                                if self.DEBUG:
338                                                        print "--> [ThinkGear Emulator] Partial data received (or error:",
339                                                        print e
340                                                        print ")."
341                                                       
342                                                        print "packet data:",
343                                                        print packet.data()
344                               
345                               
346                                else:
347                                       
348                                        if self.DEBUG:
349                                                print "--> [%s] Received:" % self.socket.name,
350                                                print data_to_process
351                               
352                               
353                                if (data_to_process != None):
354                                       
355                                        response = self.processData(data_to_process)
356                                       
357                                        if (response != None):
358                                               
359                                                self.sendResponse(clientConnection, response)
360       
361       
362        ##################################################################
363       
364        def sendResponse(self, connection, response, disconnect_after_sending=False):
365               
366                # Special socket handling for Flash applications
367                if (response == FLASH_SOCKET_POLICY_FILE):
368                        data = response
369                else:
370                        data = json.dumps(response)
371                        data = data + DELIMITER
372               
373                if connection.waitForConnected(CLIENT_NO_REPLY_WAIT):
374                       
375                        if self.DEBUG > 1:
376                                print "<-- [%s] Sending:" % self.socket.name,
377                                print data
378                       
379                        connection.write(data)
380                       
381                        connection.waitForBytesWritten(CLIENT_NO_REPLY_WAIT)
382                       
383                        if disconnect_after_sending:
384                                connection.disconnectFromHost()
385       
386       
387        ##################################################################
388       
389        def sendPacketQueue(self):
390               
391                if self.connections != []:
392                       
393                        while (len(self.packet_queue) > 0):
394                               
395                                packet = self.packet_queue[0]
396                                del self.packet_queue[0]
397                               
398                                for connection in self.connections:
399                                       
400                                        if connection.state() == QtNetwork.QAbstractSocket.ConnectedState:
401                                               
402                                                self.sendResponse(connection, packet)
403       
404       
405        ##################################################################
406       
407        def processData(self, data):
408               
409                response = None
410               
411                # Special socket handling for Flash applications
412                if (data == FLASH_POLICY_FILE_REQUEST):
413                       
414                        response = FLASH_SOCKET_POLICY_FILE
415                       
416                        #self.packet_queue.insert(0, FLASH_SOCKET_POLICY_FILE)
417               
418               
419                elif (type(data) == type({}) and \
420                      data.has_key('appName') and \
421                      data.has_key('appKey')):
422                        authorized = self.authorizeClient(data)
423                       
424                        response = {}
425                        response['isAuthorized'] = authorized
426                       
427                        #self.packet_queue.insert(0, response)
428               
429               
430                return(response)
431       
432       
433        ##################################################################
434       
435        def validateChecksum(self, checksum):
436               
437                '''The key used by the client application to identify
438itself. This must be 40 hexadecimal characters, ideally generated
439using an SHA-1 digest. The appKey is an identifier that is unique
440to each application, rather than each instance of an application.
441It is used by the server to bypass the authorization process if a
442user had previously authorized the requesting client. To reduce
443the chance of overlap with the appKey of other applications,
444the appKey should be generated using an SHA-1 digest.'''
445               
446                is_valid = True
447               
448                hexadecimal_characters = '0123456789abcdef'
449               
450                if len(checksum) != 40:
451                        is_valid = False
452                else:
453                        for character in checksum:
454                                if character not in hexadecimal_characters:
455                                        is_valid = False
456               
457                return(is_valid)
458       
459       
460        ##################################################################
461       
462        def authorizeClient(self, data):
463       
464                '''The client must initiate an authorization request
465and the server must authorize the client before the
466server will start transmitting any headset data.'''
467               
468                is_authorized = self.validateChecksum(data['appKey'])
469               
470                # A human-readable name identifying the client
471                # application. This can be a maximum of 255 characters.
472               
473                if len(data['appName']) > 255:
474                        is_authorized = False
475               
476               
477                return(is_authorized)
478       
479       
480        ##################################################################
481       
482        def calculateWavePoint(self, x, max_height=100, wave_length=10):
483               
484                # start at 0, increase to max value at half of one
485                # wavelength, decrease to 0 by end of wavelength
486                y = ( (max_height/2) * \
487                      math.sin ((x-1) * ( math.pi / (wave_length / 2)))) + \
488                      (max_height/2)
489               
490                # start at max value, decrease to 0 at half of one
491                # wavelegnth, increase to max by end of wavelength
492                #y = ( (max_height/2) * \
493                      #math.cos (x * ( math.pi / (wave_length / 2)))) + \
494                      #(max_height/2)
495               
496               
497                return(y)
498       
499       
500        ##################################################################
501       
502        def simulateHeadsetData(self):
503               
504                response = DEFAULT_PACKET
505               
506                response['timestamp'] = time.time()
507               
508                time_value = self.connection_timestamp - time.time()
509               
510                for key in response.keys():
511                       
512                        if key == 'poorSignalLevel':
513                                pass
514                       
515                        elif key == 'eSense':
516                                plot = self.calculateWavePoint( \
517                                        time_value, \
518                                        max_height=100, \
519                                        wave_length=DEFAULT_SAMPLE_WAVELENGTH)
520                               
521                                for each in response[key].keys():
522                                       
523                                        if ((each == 'attention') and \
524                                                 (THINKGEAR_ATTENTION_MULTIPLIER != None)):
525                                                value = plot * \
526                                                   THINKGEAR_ATTENTION_MULTIPLIER
527                                       
528                                        elif ((each == 'meditation') and \
529                                                   (THINKGEAR_MEDITATION_MULTIPLIER != None)):
530                                                value = plot * \
531                                                   THINKGEAR_MEDITATION_MULTIPLIER
532                                       
533                                        if value < 0:
534                                                value = 0
535                                        elif value > 100:
536                                                value = 100
537                                       
538                                        response[key][each] = value
539                       
540                       
541                        elif key == 'eegPower':
542                                plot = self.calculateWavePoint( \
543                                        time_value, \
544                                        max_height=65535, \
545                                        wave_length=DEFAULT_SAMPLE_WAVELENGTH)
546                               
547                                for each in response[key].keys():
548                                        if ((THINKGEAR_EEG_POWER_MULTIPLIERS != None) and \
549                                                 (each in THINKGEAR_EEG_POWER_MULTIPLIERS.keys())):
550                                                value = THINKGEAR_EEG_POWER_MULTIPLIERS[each] * plot
551                                        else:
552                                                value = plot
553                                        response[key][each] = value
554               
555               
556                return(response)
557       
558       
559        ##################################################################
560       
561        def processPacketThinkGear(self, packet):
562               
563                if self.DEBUG > 2:
564                        print packet
565               
566                if (packet != {}):
567                        self.packet_queue.append(packet)
568                        self.sendPacketQueue()
569               
570                #if (self.parent != None):
571                        #self.parent.processPacketThinkGear(self.protocol.data_packet)
572       
573       
574        ##################################################################
575       
576        def updateStatus(self):
577               
578                # Craft a simulated data packet
579                packet = self.simulateHeadsetData()
580               
581                self.packet_queue.append(packet)
582               
583                if (self.parent != None):
584                        self.parent.processPacketThinkGear(packet)
585               
586                # Include simulated blinks at desired frequency
587                if ((self.blink_frequency_timer != None) and \
588                                (self.blink_frequency_timer > 0) and \
589                                (time.time() - self.blink_timestamp > \
590                                self.blink_frequency_timer)):
591                       
592                        self.blink_timestamp = time.time()
593                       
594                        packet = DEFAULT_BLINK_MESSAGE
595                       
596                        packet['timestamp'] = self.blink_timestamp
597                       
598                        self.packet_queue.append(packet)
599       
600       
601        ##################################################################
602       
603        def run(self):
604               
605                if self.DEBUG:
606                        print "<---- [%s] Main thread running" % self.socket.name
607               
608                self.exec_()
609       
610               
611        ##################################################################
612       
613        def exitThread(self, callThreadQuit=True):
614               
615                try:
616                        self.emulationTimer.stop()
617                except:
618                        pass
619               
620                if self.serial_device != None:
621                        self.serial_device.exitThread()
622               
623                if self.protocol != None:
624                        self.protocol.exitThread()
625               
626                self.socket.close()
627               
628                if callThreadQuit:
629                        QtCore.QThread.quit(self)
630               
631                if self.parent == None:
632                        sys.exit()
633       
634       
635        ##################################################################
636       
637        def resetDevice(self):
638               
639                if self.serial_device != None:
640                        self.serial_device.exitThread()
641               
642                if self.protocol != None:
643                        self.protocol.exitThread()
644               
645                self.configureEEG()
646
Note: See TracBrowser for help on using the repository browser.