source: trunk/synapse/Puzzlebox/Synapse/Protocol.py @ 191

Last change on this file since 191 was 191, checked in by sc, 11 years ago

synapse/Puzzlebox/Synapse/Protocol.py:

  • bad packet tracking added
  • Property svn:executable set to *
File size: 27.5 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 Classes:
9#       puzzlebox_synapse_protocol_thinkgear = ProtocolHandler
10#       puzzlebox_synapse_protocol_thinkgear_serial_device = SerialDevice
11
12__changelog__ = """\
13Last Update: 2010.09.21
14
15"""
16
17__doc__ = """\
18Puzzlebox.Synapse.Protocol
19
20usage:
21  from Puzzlebox.Synapse import Protocol
22
23Modules:
24  Protocol.ProtocolHandler()
25  Protocol.SerialDevice()
26
27SPEC:
28
29 CODE Definitions Table
30 Single-Byte CODEs
31 Extended             (Byte)
32 Code Level   [CODE] [LENGTH] Data Value Meaning
33 ----------   ------ -------- ------------------
34           0    0x02        - POOR_SIGNAL Quality (0-255)
35           0    0x04        - ATTENTION eSense (0 to 100)
36           0    0x05        - MEDITATION eSense (0 to 100)
37           0    0x16        - Blink Strength. (0-255) Sent only
38                              when Blink event occurs.
39 Multi-Byte CODEs
40 Extended             (Byte)
41 Code Level   [CODE] [LENGTH] Data Value Meaning
42 ----------   ------ -------- ------------------
43           0    0x80        2 RAW Wave Value: a single big-endian
44                                16-bit two's-compliment signed value
45                                (high-order byte followed by
46                                low-order byte) (-32768 to 32767)
47           0    0x83       24 ASIC_EEG_POWER: eight big-endian
48                                3-byte unsigned integer values
49                                representing delta, theta, low-alpha
50                                high-alpha, low-beta, high-beta,
51                                low-gamma, and mid-gamma EEG band
52                                power values
53         Any    0x55        - NEVER USED (reserved for [EXCODE])
54         Any    0xAA        - NEVER USED (reserved for [SYNC])
55
56
57Linux Bluetooth serial protocol profile example:
58    rfcomm connect rfcomm0 00:13:EF:00:1B:FE 3
59
60TODO:
61 - needs to handle:
62   serial.serialutil.SerialException:
63   could not open port /dev/rfcomm0:
64   [Errno 16] Device or resource busy: '/dev/rfcomm0'
65
66"""
67
68### IMPORTS ###
69import sys
70import signal
71import serial
72
73if (sys.platform != 'win32'):
74        import bluetooth
75
76try:
77        import PySide as PyQt4
78        from PySide import QtCore
79except:
80        print "Using PyQt4 module"
81        from PyQt4 import QtCore
82else:
83        print "Using PySide module"
84
85import Configuration as configuration
86
87### GLOBALS ###
88
89DEBUG = 2
90
91THINKGEAR_DEVICE_SERIAL_PORT = configuration.THINKGEAR_DEVICE_SERIAL_PORT
92#THINKGEAR_DEVICE_BLUETOOTH_ADDRESS = \
93        #configuration.THINKGEAR_DEVICE_BLUETOOTH_ADDRESS
94
95DEFAULT_SERIAL_BAUDRATE = 57600
96
97#THINKGEAR_DEVICE_BLUETOOTH_ADDRESS = '00:13:EF:00:1B:FE'
98THINKGEAR_DEVICE_BLUETOOTH_CHANNEL = 3
99
100PROTOCOL_SYNC = '\xAA'
101PROTOCOL_EXCODE = '\x55'
102
103EEG_POWER_BAND_ORDER = configuration.THINKGEAR_EEG_POWER_BAND_ORDER
104
105DEVICE_BUFFER_CHECK_TIMER = 60 * 1000 # Check buffer size once every minute
106DEVICE_BUFFER_MAX_SIZE = 180 # Reset buffer if it grow this large
107                             # as this would indicate the processing
108                             # algorithm is not keeping up with the device
109                             # According to protocol specification,
110                             # "...a complete, valid Packet is ... a maximum
111                             # of 173 bytes long (possible if the Data Payload
112                             # is the maximum 169 bytes long)."
113                             # Therefore we reset if our buffer has grown longer
114                             # than the maximum packet length as this means
115                             # the processing algorthim is at least one full
116                             # packet behind.
117
118DEBUG_BYTE_COUNT = 819200
119DEBUG_PACKET_COUNT = 1024
120
121### CLASSES ###
122
123class ProtocolHandler(QtCore.QThread):
124       
125        def __init__(self, log, \
126                               serial_device, \
127                               DEBUG=DEBUG, \
128                               parent=None):
129               
130                QtCore.QThread.__init__(self,parent)
131               
132                self.log = log
133                self.DEBUG = DEBUG
134                self.parent = parent
135               
136                self.device = None
137                self.buffer = ''
138               
139                self.device = serial_device
140               
141                self.data_packet = {}
142                self.data_packet['eegPower'] = {}
143                self.data_packet['eSense'] = {}
144               
145                self.bad_packets = 0
146               
147                self.keep_running = True
148       
149       
150        ##################################################################
151       
152        def communicateWithHandsfreeProfile(self):
153               
154                #"AT+CKPD=200" - Indicates a Bluetooth button press
155                #"AT+VGM=" - Indicates a microphone volume change
156                #"AT+VGS=" - Indicates a speakerphone volume change
157                #"AT+BRSF=" - The Headset is asking what features are supported
158                #"AT+CIND?" - The Headset is asking about the indicators that are signaled
159                #"AT+CIND=?" - The Headset is asking about the test indicators
160                #"AT+CMER=" - The Headset is asking which indicates are registered for updates
161                #"ATA" - When an incoming call has been answered, usually a Bluetooth button press
162                #"AT+CHUP" - When a call has been hung up, usually a Bluetooth button press
163                #"ATD>" - The Headset is requesting the local device to perform a memory dial
164                #"ATD" - The Headset is requesting to dial the number
165                #"AT+BLDN" - The Headset is requesting to perform last number dialed
166                #"AT+CCWA=" - The Headset has enabled call waiting
167                #"AT+CLIP=" - The Headset has enabled CLI (Calling Line Identification)
168                #"AT+VTS=" - The Headset is asking to send DTMF digits
169                #"AT+CHLD=" - The Headset is asking to put the call on Hold
170                #"AT+BVRA=" - The Headset is requesting voice recognition
171                #"ATH" - Call hang-up
172               
173                #self.device.write('\x29')
174                #self.device.write('AT+BRSF=24\r\n')
175               
176                buffer = ''
177               
178                while True:
179                        reply = self.device.read()
180                       
181                        if (len(reply) != 0):
182                                if DEBUG > 1:
183                                        print reply
184                                buffer += reply
185                       
186                        if buffer == "AT+BRSF=24\r":
187                                print "--> Received:",
188                                print buffer
189                                response = '\r\nOK\r\n'
190                                print "<-- Sending:",
191                                print response.replace('\r\n', '')
192                                self.device.write(response)
193                                buffer = ''
194                       
195                        elif buffer == 'AT+CIND=?\r':
196                                print "--> Received:",
197                                print buffer
198                                # first field indicates that we have cellular service [0-1]
199                                # second field indicates that we're in a call (0 for false) [0-1]
200                                # third field indicates the current call setup (0 for idle) [0-3]
201                                response = '\r\n+CIND: 1,0,0\r\n'
202                                print "<-- Sending:",
203                                print response.replace('\r\n', '')
204                                self.device.write(response)
205                                response = '\r\nOK\r\n'
206                                print "<-- Sending:",
207                                print response.replace('\r\n', '')
208                                self.device.write(response)
209                                buffer = ''
210                       
211                        elif buffer == 'AT+CMER=3, 0, 0, 1\r':
212                                print "--> Received:",
213                                print buffer
214                                response = '\r\nOK\r\n'
215                                print "<-- Sending:",
216                                print response.replace('\r\n', '')
217                                self.device.write(response)
218                                response = '\r\n+CIEV:2,1\r\n'
219                                print "<-- Sending:",
220                                print response.replace('\r\n', '')
221                                self.device.write(response)
222                                response = '\r\n+CIEV:3,0\r\n'
223                                print "<-- Sending:",
224                                print response.replace('\r\n', '')
225                                self.device.write(response)
226                                buffer = ''
227                       
228                        elif buffer == 'AT+VGS=15\r':
229                                print "--> Received:",
230                                print buffer
231                                response = '\r\nOK\r\n'
232                                print "<-- Sending:",
233                                print response.replace('\r\n', '')
234                                self.device.write(response)
235                                buffer = ''
236                       
237                        elif buffer == 'AT+VGM=08\r':
238                                print "--> Received:",
239                                print buffer
240                                response = '\r\nOK\r\n'
241                                print "<-- Sending:",
242                                print response.replace('\r\n', '')
243                                self.device.write(response)
244                                buffer = ''
245                               
246                               
247                                self.exitThread()
248                                #self.keep_running = False
249                                #self.device.stop()
250                                #QtCore.QThread.quit(self)
251                                #sys.exit()
252       
253       
254        ##################################################################
255       
256        def hexStringEndianSwap(self, theString):
257                """Rearranges character-couples in a little endian hex string to
258                convert it into a big endian hex string and vice-versa. i.e. 'A3F2'
259                is converted to 'F2A3'
260               
261                @param theString: The string to swap character-couples in
262                @return: A hex string with swapped character-couples. -1 on error.
263               
264                Taken from http://bytes.com/topic/python/answers/652429-convert-little-endian-hex-string-number#post2588668"""
265               
266                # We can't swap character couples in a string that has an odd number
267                # of characters.
268                if len(theString)%2 != 0:
269                        return -1
270               
271                # Swap the couples
272                swapList = []
273                for i in range(0, len(theString), 2):
274                        swapList.insert(0, theString[i:i+2])
275               
276                # Combine everything into one string. Don't use a delimeter.
277                return ''.join(swapList)
278       
279       
280        ##################################################################
281       
282        def processRawEEGValue(self, data_values):
283               
284                '''SPEC: This Data Value consists of two bytes, and represents a
285                single raw wave sample. Its value is a signed 16-bit integer that
286                ranges from -32768 to 32767. The first byte of the Value represents
287                the high-order bits of the twos-compliment value, while the second
288                byte represents the low-order bits. To reconstruct the full raw
289                wave value, simply shift the rst byte left by 8 bits, and
290                bitwise-or with the second byte:
291               
292                short raw = (Value[0]<<8) | Value[1];
293               
294                where Value[0] is the high-order byte, and Value[1] is the
295                low-order byte. In systems or languages where bit operations are
296                inconvenient, the following arithmetic operations may be
297                substituted instead:
298               
299                raw = Value[0]*256 + Value[1];
300                if( raw >= 32768 ) raw = raw - 65536;
301               
302                where raw is of any signed number type in the language that can
303                represent all the numbers from -32768 to 32767.
304               
305                Each ThinkGear model reports its raw wave information in only
306                certain areas of the full -32768 to 32767 range. For example,
307                MindSet reports raw waves that fall between approximately -2048 to
308                2047. By default, output of this Data Value is enabled, and is
309                outputed 512 times a second, or approximately once every 2ms.'''
310               
311                high_order = data_values[0:2]
312                low_order = data_values[2:4]
313               
314                #high_order = high_order.encode("hex")
315                high_order = int(high_order, 16)
316               
317                #low_order = low_order.encode("hex")
318                low_order = int(low_order, 16)
319
320                raw = high_order * 256 + low_order
321               
322                if (raw >= 32768):
323                        raw = raw - 65536
324               
325               
326                return (raw)
327       
328       
329        ##################################################################
330       
331        def processAsicEegPower(self, data_values):
332               
333                '''SPEC: This Data Value represents the current magnitude of 8
334                commonly-recognized types of EEG (brain-waves). This Data Value
335                is output as a series of eight 3-byte unsigned integers in
336                little-endian format.
337                The eight EEG powers are output in the following order:
338                delta (0.5 - 2.75Hz),
339                theta (3.5 - 6.75Hz),
340                low-alpha (7.5 - 9.25Hz),
341                high-alpha (10 - 11.75Hz),
342                low-beta (13 - 16.75Hz),
343                high-beta (18 - 29.75Hz),
344                low-gamma (31 - 39.75Hz), and
345                mid-gamma (41 - 49.75Hz).
346                These values have no units and therefore are only meaningful compared
347                to each other and to themselves, to consider relative quantity and
348                temporal uctuations. By default, output of this Data Value is enabled,
349                and is typically output once a second.'''
350               
351                eegPower = {}
352               
353                eegPower['delta'] = data_values[0:6]
354                eegPower['theta'] = data_values[6:12]
355                eegPower['lowAlpha'] = data_values[12:18]
356                eegPower['highAlpha'] = data_values[18:24]
357                eegPower['lowBeta'] = data_values[24:30]
358                eegPower['highBeta'] = data_values[30:36]
359                eegPower['lowGamma'] = data_values[36:42]
360                eegPower['highGamma'] = data_values[42:48]
361               
362                for key in eegPower.keys():
363                        eegPower[key] = self.hexStringEndianSwap(eegPower[key])
364                        #eegPower[key] = eegPower[key].encode("hex")
365                        eegPower[key] = int(eegPower[key], 16)
366               
367               
368                return(eegPower)
369       
370       
371        ##################################################################
372       
373        def processDataRow(self, extended_code_level, code, length, data_values):
374               
375                '''CODE Definitions Table
376                   Single-Byte CODEs
377                   Extended             (Byte)
378                   Code Level   [CODE] [LENGTH] Data Value Meaning
379                   ----------   ------ -------- ------------------
380                             0    0x02        - POOR_SIGNAL Quality (0-255)
381                             0    0x04        - ATTENTION eSense (0 to 100)
382                             0    0x05        - MEDITATION eSense (0 to 100)
383                             0    0x16        - Blink Strength. (0-255) Sent only
384                                                when Blink event occurs.
385                   Multi-Byte CODEs
386                   Extended             (Byte)
387                   Code Level   [CODE] [LENGTH] Data Value Meaning
388                   ----------   ------ -------- ------------------
389                             0    0x80        2 RAW Wave Value: a single big-endian
390                                                  16-bit two's-compliment signed value
391                                                  (high-order byte followed by
392                                                  low-order byte) (-32768 to 32767)
393                             0    0x83       24 ASIC_EEG_POWER: eight big-endian
394                                                  3-byte unsigned integer values
395                                                  representing delta, theta, low-alpha
396                                                  high-alpha, low-beta, high-beta,
397                                                  low-gamma, and mid-gamma EEG band
398                                                  power values
399                           Any    0x55        - NEVER USED (reserved for [EXCODE])
400                           Any    0xAA        - NEVER USED (reserved for [SYNC])'''
401               
402                packet_update = {}
403               
404                if extended_code_level == 0:
405                       
406                        if code == '02':
407                                poor_signal_quality = int(data_values, 16)
408                                if self.DEBUG > 1:
409                                        print # Empty line at the beginning of most packets
410                                        print "poorSignalLevel:",
411                                        print poor_signal_quality
412                               
413                                packet_update['poorSignalLevel'] = poor_signal_quality
414                       
415                       
416                        elif code == '04':
417                                attention = int(data_values, 16)
418                                if self.DEBUG > 1:
419                                        print "attention:",
420                                        print attention
421                               
422                                packet_update['eSense'] = {}
423                                packet_update['eSense']['attention'] = attention
424                       
425                       
426                        elif code == '05':
427                                meditation = int(data_values, 16)
428                                if self.DEBUG > 1:
429                                        print "meditation:",
430                                        print meditation
431                               
432                                packet_update['eSense'] = {}
433                                packet_update['eSense']['meditation'] = meditation
434                       
435                       
436                        elif code == '16':
437                                blink_strength = int(data_values, 16)
438                                if self.DEBUG > 1:
439                                        print "blinkStrength:",
440                                        print blink_strength
441                               
442                                packet_update['blinkStrength'] = blink_strength
443                       
444                       
445                        elif code == '80':
446                                raw_wave_value = data_values
447                                if self.DEBUG > 3:
448                                        print "Raw EEG:",
449                                        print raw_wave_value
450                                raw_eeg_value = self.processRawEEGValue(data_values)
451                                if self.DEBUG > 2:
452                                        print "Raw EEG Value:",
453                                        print raw_eeg_value
454                               
455                                packet_update['rawEeg'] = raw_eeg_value
456                       
457                       
458                        elif code == '83':
459                                asic_eeg_power = data_values
460                                if self.DEBUG > 2:
461                                        print "ASIC_EEG_POWER:",
462                                        print asic_eeg_power
463                                eegPower = self.processAsicEegPower(data_values)
464                                if self.DEBUG > 1:
465                                        for key in EEG_POWER_BAND_ORDER:
466                                                print "%s: %i" % (key, eegPower[key])
467                               
468                                packet_update['eegPower'] = {}
469                                for key in eegPower.keys():
470                                        packet_update['eegPower'][key] = eegPower[key]
471                       
472                       
473                        else:
474                                if self.DEBUG:
475                                        print "ERROR: data payload row code not matched"
476               
477               
478                return(packet_update)
479       
480       
481        ##################################################################
482       
483        def processDataPayload(self, data_payload):
484               
485                '''A DataRow consists of bytes in the following format:
486                ([EXCODE]...) [CODE] ([VLENGTH])   [VALUE...]
487                ____________________ ____________ ___________
488                ^^^^(Value Type)^^^^ ^^(length)^^ ^^(value)^^'''
489               
490               
491                if self.DEBUG > 3:
492                        print "data payload:",
493                        for byte in data_payload:
494                                print byte.encode("hex"),
495                        print
496               
497                byte_index = 0
498               
499                # Parse the extended_code_level, code, and length
500                while (byte_index < len(data_payload)):
501                        extended_code_level = 0
502                       
503                        # 1. Parse and count the number of [EXCODE] (0x55)
504                        #    bytes that may be at the beginning of the
505                        #    current DataRow.
506                        while (data_payload[byte_index] == PROTOCOL_EXCODE):
507                                extended_code_level += 1
508                                byte_index += 1
509                       
510                        # 2. Parse the [CODE] byte for the current DataRow.
511                        code = data_payload[byte_index]
512                        byte_index += 1
513                        code = code.encode("hex")
514                       
515                        # 3. If [CODE] >= 0x80, parse the next byte as the
516                        #    [VLENGTH] byte for the current DataRow.
517                        if (code > '\x7f'.encode("hex")):
518                                length = data_payload[byte_index]
519                                byte_index += 1
520                                length = length.encode("hex")
521                                length = int(length, 16)
522                        else:
523                                length = 1
524                       
525                       
526                        if self.DEBUG > 3:
527                                print "EXCODE level:",
528                                print extended_code_level,
529                                print " CODE:",
530                                print code,
531                                print " length:",
532                                print length
533                                #print type(code)
534                       
535                        data_values = ''
536                        value_index = 0
537                       
538                        # 4. Parse and handle the [VALUE...] byte(s) of the current
539                        #    DataRow, based on the DataRow's [EXCODE] level, [CODE],
540                        #    and [VLENGTH] (refer to the Code De nitions Table).
541                        while value_index < length:
542                                # Uh-oh more C mojo
543                                value = data_payload[(byte_index + value_index)] # & 0xFF
544                                data_values += value.encode("hex")
545                                value_index += 1
546                       
547                        if self.DEBUG > 3:
548                                print "Data Values:",
549                                print data_values
550                                print
551                       
552                        packet_update = self.processDataRow(extended_code_level, \
553                                                            code, \
554                                                            length, \
555                                                            data_values)
556                       
557                        self.updateDataPacket(packet_update)
558                       
559                       
560                        byte_index += length
561                       
562                        # 5. If not all bytes have been parsed from the payload[] array,
563                        # return to step 1. to continue parsing the next DataRow.
564       
565       
566        ##################################################################
567       
568        def parseStream(self):
569               
570                '''Each Packet begins with its Header, followed by its Data Payload,
571                and ends with the Payload's Check-sum Byte, as follows:
572                [SYNC] [SYNC] [PLENGTH]      [PAYLOAD...]         [CHKSUM]
573                _______________________      _____________     ____________
574                ^^^^^^^^(Header)^^^^^^^      ^^(Payload)^^     ^(Checksum)^'''
575               
576                # Loop forever, parsing one Packet per loop...
577                packet_count = 0
578               
579                while self.keep_running:
580                       
581                        # Synchronize on [SYNC] bytes
582                        # Read from stream until two consecutive [SYNC] bytes are found
583                        byte = self.device.read()
584                        if (byte != PROTOCOL_SYNC):
585                                continue
586                       
587                        byte = self.device.read()
588                        if (byte != PROTOCOL_SYNC):
589                                continue
590                       
591                       
592                        # Parse [PLENGTH] byte
593                       
594                        # SPEC: [PLENGTH] byte indicates the length, in bytes, of the
595                        # Packet's Data Payload [PAYLOAD...] section, and may be any value
596                        # from 0 up to 169. Any higher value indicates an error
597                        # (PLENGTH TOO LARGE). Be sure to note that [PLENGTH] is the length
598                        # of the Packet's Data Payload, NOT of the entire Packet.
599                        # The Packet's complete length will always be [PLENGTH] + 4.
600                       
601                        byte = self.device.read()
602                        packet_length = byte.encode("hex")
603                        packet_length = int(packet_length, 16)
604                       
605                        if (packet_length > 170):
606                                self.bad_packets += 1
607                                if self.DEBUG:
608                                        print "ERROR: packet length bad"
609                                        continue
610                       
611                       
612                        # Collect [PAYLOAD...] bytes
613                        data_payload = self.device.read(packet_length)
614                       
615                       
616                        # Calculate [PAYLOAD...] checksum
617                       
618                        # SPEC: The [CHKSUM] Byte must be used to verify the integrity of the
619                        # Packet's Data Payload. The Payload's Checksum is defined as:
620                        #  1. summing all the bytes of the Packet's Data Payload
621                        #  2. taking the lowest 8 bits of the sum
622                        #  3. performing the bit inverse (one's compliment inverse)
623                        #     on those lowest 8 bits
624                       
625                        payload_checksum = 0
626                        for byte in data_payload:
627                                value = byte.encode("hex")
628                                value = int(value, 16)
629                                payload_checksum += value
630                       
631                       
632                        # Take the lowest 8 bits of the calculated payload_checksum
633                        # and invert them. Serious C code mojo follows.
634                        payload_checksum &= 0xff
635                        payload_checksum = ~payload_checksum & 0xff
636                       
637                       
638                        # Parse [CKSUM] byte
639                        packet_checksum = self.device.read()
640                        packet_checksum = packet_checksum.encode("hex")
641                        packet_checksum = int(packet_checksum, 16)
642                       
643                       
644                        # Verify [CKSUM] byte against calculated [PAYLOAD...] checksum
645                        if packet_checksum != payload_checksum:
646                                if self.DEBUG > 1:
647                                        print "ERROR: packet checksum does not match"
648                                        print "       packet_checksum:",
649                                        print packet_checksum
650                                        print "       payload_checksum:",
651                                        print payload_checksum
652                                       
653                                        #self.device.checkBuffer()
654                               
655                                continue
656                       
657                       
658                        else:
659                                # Since [CKSUM] is OK, parse the Data Payload
660                                if self.DEBUG > 3:
661                                        print "packet checksum correct"
662                               
663                               
664                                self.processDataPayload(data_payload)
665                               
666                               
667                                #if self.DEBUG > 1:
668                                        #packet_count += 1
669                                        #if packet_count >= DEBUG_PACKET_COUNT:
670                                                #print "max debugging count reached, disconnecting"
671                                                #self.keep_running = False
672                                                #self.device.stop()
673                                                #QtCore.QThread.quit(self)
674                                                ##sys.exit()
675       
676       
677        ##################################################################
678       
679        def updateDataPacket(self, packet_update):
680               
681                if 'eSense' in packet_update.keys():
682                        process_packet = {'eSense': {}}
683                        for key in packet_update['eSense'].keys():
684                                self.data_packet['eSense'][key] = packet_update['eSense'][key]
685                                process_packet['eSense'][key] = packet_update['eSense'][key]
686               
687                else:
688                        self.data_packet.update(packet_update)
689                        process_packet = packet_update
690               
691               
692                if self.DEBUG > 3:
693                        print self.data_packet
694               
695               
696                if (self.parent != None):
697                        self.parent.processPacketThinkGear(process_packet)
698       
699       
700        ##################################################################
701       
702        def run(self):
703               
704                self.bad_packets = 0
705               
706                self.parseStream()
707       
708       
709        ##################################################################
710       
711        def exitThread(self, callThreadQuit=True):
712               
713                try:
714                        self.device.stop()
715                except:
716                        pass
717               
718                #self.wait()
719                if callThreadQuit:
720                        QtCore.QThread.quit(self)
721
722
723#####################################################################
724#####################################################################
725
726class SerialDevice(QtCore.QThread):
727       
728        def __init__(self, log, \
729                               device_address=THINKGEAR_DEVICE_SERIAL_PORT, \
730                               DEBUG=DEBUG, \
731                               parent=None):
732               
733                QtCore.QThread.__init__(self,parent)
734               
735                self.log = log
736                self.DEBUG = DEBUG
737               
738                self.device_address = device_address
739                self.device = None
740                self.buffer = ''
741               
742                if (self.device_address.count(':') == 5):
743                        # Device address is a Bluetooth MAC address
744                        self.device = self.initializeBluetoothDevice()
745                else:
746                        # Device address is a serial port address
747                        self.device = self.initializeSerialDevice()
748               
749                self.buffer_check_timer = QtCore.QTimer()
750                QtCore.QObject.connect(self.buffer_check_timer, \
751                                       QtCore.SIGNAL("timeout()"), \
752                                       self.checkBuffer)
753                self.buffer_check_timer.start(DEVICE_BUFFER_CHECK_TIMER)
754               
755                self.keep_running = True
756       
757       
758        ##################################################################
759       
760        def initializeBluetoothDevice(self):
761               
762                socket = bluetooth.BluetoothSocket( bluetooth.RFCOMM )
763               
764                try:
765                        socket.connect((self.device_address, THINKGEAR_DEVICE_BLUETOOTH_CHANNEL))
766               
767                except Exception, e:
768                        if self.DEBUG:
769                                print "ERROR:",
770                                print e
771                                sys.exit()
772               
773               
774                return socket
775       
776       
777        ##################################################################
778       
779        def initializeSerialDevice(self):
780               
781                baudrate = DEFAULT_SERIAL_BAUDRATE
782                bytesize = 8
783                parity = 'NONE'
784                stopbits = 1
785                software_flow_control = 'f'
786                rts_cts_flow_control = 'f'
787                #timeout = 15
788                timeout = 5
789               
790                # convert bytesize
791                if (bytesize == 5):
792                        init_byte_size = serial.FIVEBITS
793                elif (bytesize == 6):
794                        init_byte_size = serial.SIXBITS
795                elif (bytesize == 7):
796                        init_byte_size = serial.SEVENBITS
797                elif (bytesize == 8):
798                        init_byte_size = serial.EIGHTBITS
799                else:
800                        #self.log.perror("Invalid value for %s modem byte size! Using default (8)" % modem_type)
801                        init_byte_size = serial.EIGHTBITS
802               
803                # convert parity
804                if (parity == 'NONE'):
805                        init_parity = serial.PARITY_NONE
806                elif (parity == 'EVEN'):
807                        init_parity = serial.PARITY_EVEN
808                elif (parity == 'ODD'):
809                        init_parity = serial.PARITY_ODD
810                else:
811                        #self.log.perror("Invalid value for %s modem parity! Using default (NONE)" % modem_type)
812                        init_parity = serial.PARITY_NONE
813               
814                # convert stopbits
815                if (stopbits == 1):
816                        init_stopbits = serial.STOPBITS_ONE
817                elif (stopbits == 2):
818                        init_stopbits = serial.STOPBITS_TWO
819                else:
820                        #self.log.perror("Invalid value for %s modem stopbits! Using default (8)" % modem_type)
821                        init_byte_size = serial.STOPBITS_ONE
822               
823                # convert software flow control
824                if (software_flow_control == 't'):
825                        init_software_flow_control = 1
826                else:
827                        init_software_flow_control = 0
828               
829                # convert rts cts flow control
830                if (rts_cts_flow_control == 't'):
831                        init_rts_cts_flow_control = 1
832                else:
833                        init_rts_cts_flow_control = 0
834               
835               
836                try:
837##                      device = serial.Serial(port = self.device_address, \
838##                                                  baudrate = baudrate, \
839##                                                  bytesize = init_byte_size, \
840##                                                  parity = init_parity, \
841##                                                  stopbits = init_stopbits, \
842##                                                  xonxoff = init_software_flow_control, \
843##                                                  rtscts = init_rts_cts_flow_control, \
844##                                                  timeout = timeout)
845                       
846                        device = serialWrapper(port = self.device_address, \
847                                                    baudrate = baudrate, \
848                                                    bytesize = init_byte_size, \
849                                                    parity = init_parity, \
850                                                    stopbits = init_stopbits, \
851                                                    xonxoff = init_software_flow_control, \
852                                                    rtscts = init_rts_cts_flow_control, \
853                                                    timeout = timeout)
854               
855                except Exception, e:
856                        if self.DEBUG:
857                                print "ERROR:",
858                                print e,
859                                print self.device_address
860                                sys.exit()
861               
862               
863                device.flushInput()
864                #device.flushOutput()
865               
866               
867                return(device)
868       
869       
870        ##################################################################
871       
872        def checkBuffer(self):
873               
874                if self.DEBUG > 1:
875                        print "INFO: Buffer size check:",
876                        print len(self.buffer),
877                        print "(maximum before reset is %i)" % DEVICE_BUFFER_MAX_SIZE
878               
879                if (DEVICE_BUFFER_MAX_SIZE <= len(self.buffer)):
880                       
881                        if self.DEBUG:
882                                print "ERROR: Buffer size has grown too large, resetting"
883                       
884                        self.reset()
885       
886       
887        ##################################################################
888       
889        def reset(self):
890               
891                self.buffer = ''
892       
893       
894        ##################################################################
895       
896        def read(self, length=1):
897               
898                # Sleep for 2 ms if buffer is empty
899                # Based on 512 Hz refresh rate of NeuroSky MindSet device
900                # (1/512) * 1000 = 1.9531250
901                while len(self.buffer) < length:
902                        QtCore.QThread.msleep(2)
903                       
904                bytes = self.buffer[:length]
905               
906                self.buffer = self.buffer[length:]
907               
908                return(bytes)
909       
910       
911        ##################################################################
912       
913        def stop(self):
914               
915                self.keep_running = False
916                self.buffer_check_timer.stop()
917       
918       
919        ##################################################################
920       
921        def exitThread(self, callThreadQuit=True):
922               
923                self.stop()
924                self.close()
925               
926                if callThreadQuit:
927                        QtCore.QThread.quit(self)
928       
929       
930        ##################################################################
931       
932        def close(self):
933               
934                self.device.close()
935       
936       
937        ##################################################################
938       
939        def run(self):
940               
941                self.buffer = ''
942               
943                while self.keep_running:
944                       
945                        try:
946                                #byte = self.device.read()
947                                byte = self.device.recv(1)
948                               
949                                if (len(byte) != 0):
950                                        if self.DEBUG > 2:
951                                                print "Device read:",
952                                                print byte
953                                               
954                                        self.buffer += byte
955                       
956                        except:
957                                if self.DEBUG:
958                                        print "ERROR: failed to read from serial device"
959                                break
960               
961               
962                self.exitThread()
963
964
965#####################################################################
966#####################################################################
967
968class serialWrapper(serial.Serial):
969
970        #__init__(port=None, baudrate=9600, bytesize=EIGHTBITS, parity=PARITY_NONE, stopbits=STOPBITS_ONE, timeout=None, xonxoff=False, rtscts=False, writeTimeout=None, dsrdtr=False, interCharTimeout=None)
971
972        def recv(self, size=1):
973
974                return(self.read(size))
975
Note: See TracBrowser for help on using the repository browser.