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

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