source: trunk/Puzzlebox/Synapse/ThinkGear/Server.py @ 400

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