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

Last change on this file since 297 was 297, checked in by sc, 10 years ago

Server:

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