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

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

Interface:

  • device_model support for handling NeuroSky? MindWave? auto-connect packets
  • 'Emit Signal' mode for Server communication support
  • fix for selecting correct default user path

Server:

  • device_model support for handling NeuroSky? MindWave? auto-connect packets
  • 'Emit Signal' mode for Server communication support

Protocol:

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