source: trunk/synapse/Puzzlebox/Synapse/Server.py @ 189

Last change on this file since 189 was 189, checked in by sc, 12 years ago

Synapse:

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