source: trunk/brainstorms/Puzzlebox/Brainstorms/Helicopter_Control.py @ 234

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

Brainstorms/Helicopter_Control.py:

  • DEVICE_BUFFER_TIMER set to 21ms

Brainstorms/Interface?.py:

  • hover and fly_forward working
  • Property svn:executable set to *
File size: 19.8 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#
4# Puzzlebox - Brainstorms - Helicopter Control
5#
6# Copyright Puzzlebox Productions, LLC (2010)
7#
8# This code is released under the GNU Pulic License (GPL) version 2
9# For more information please refer to http://www.gnu.org/copyleft/gpl.html
10
11__changelog__ = """
12Last Update: 2010.11.22
13
14"""
15
16import sys, time
17import signal
18import serial
19
20try:
21        import PySide as PyQt4
22        from PySide import QtCore
23except:
24        print "Using PyQt4 module"
25        from PyQt4 import QtCore
26else:
27        print "Using PySide module"
28
29#import Configuration as configuration
30
31#import Puzzlebox.Synapse.Protocol as protocol
32#from Puzzlebox.Synapse import Protocol
33
34#####################################################################
35# Globals
36#####################################################################
37
38DEBUG = 2
39
40DEFAULT_COMMAND = 'dump_packets'
41
42SERIAL_DEVICE = '/dev/ttyUSB0'
43#SERIAL_DEVICE = '/dev/ttyACM0'
44#DEFAULT_SERIAL_BAUDRATE = 115200 # This is the closest "standard" baud rate the USB-to-Serial device will support
45#DEFAULT_SERIAL_BAUDRATE = 125000 # This is the speed reported by the forum post
46#DEFAULT_SERIAL_BAUDRATE = 128000 # This is the next closest somewhat commonly-found baud rate (though not supported by device)
47DEFAULT_SERIAL_BAUDRATE = 133333 # This is the speed reported by the logic analyzer
48#DEFAULT_SERIAL_BAUDRATE = 230400 # This is the next highest "standard" baud rate the USB-to-Serial device will support
49DEFAULT_MODE = 'read'
50
51PROTOCOL_SYNC_TIME = 5
52PROTOCOL_SYNC_HEAD1 = '\x00'
53PROTOCOL_SYNC_HEAD2 = '\x00'
54PROTOCOL_ADD_SYNC_TO_HEAD = False
55PROTOCOL_ADD_SYNC_TO_TAIL = False
56PACKET_LENGTH = 14
57PAYLOAD_MINIMUM_LENGTH = 12
58PACKET_READ_SIZE = 14
59ECHO_ON = False
60
61#DEVICE_BUFFER_TIMER = 22  # Frame cycle 22ms
62DEVICE_BUFFER_TIMER = 21  # Frame cycle 22ms
63
64COMMAND_PACKET = {
65        'neutral':        '\x00\x00\x00\xfa\x05\xc5\x09\xde\x0e\x0b\x13\x54\x14\xaa',  # default neutral setting to use for all commands
66        'no_thrust':      '\x00\x00\x00\x5a\x05\xc5\x09\xde\x0e\x0b\x13\x54\x14\xaa',  # lowest trim setting for throttle
67        'minimum_thrust': '\x00\x00\x00\xca\x05\xc5\x09\xde\x0e\x0b\x13\x54\x14\xaa',  # lowest trim level at which throttle kicks in
68        'minimum_thrust_minus_one': '\x00\x00\x00\xc6\x05\xc5\x09\xde\x0e\x0b\x13\x54\x14\xaa',  # lowest trim level at which throttle kicks in
69        'maximum_thrust': '\x00\x00\x03\x54\x05\xc5\x09\xde\x0e\x0b\x13\x54\x14\xaa',  # maximum possible throttle and trim
70        'fifty_percent_thrust': '\x00\x00\x01\x7d\x05\xc5\x09\xde\x0e\x0b\x13\x54\x14\xaa', # calculated 50% throttle
71        'test_packet':    '\x00\x00\x03\x54\x06\x15\x09\xca\x0e\x2f\x13\x54\x14\xaa', # test packet from saleae logic screenshot
72        'maximum_forward': '\x00\x00\x00\x5a\x05\xc5\x0b\x54\x0e\x0b\x13\x54\x14\xaa', # maximum possible elevator and trim
73            'fly_forward': '\x00\x00\x01\x7d\x05\xc5\x0a\xde\x0e\x0b\x13\x54\x14\xaa', \
74}
75
76DEFAULT_COMMAND_PACKET = COMMAND_PACKET['neutral']
77
78#####################################################################
79# Classes
80#####################################################################
81
82class puzzlebox_brainstorms_helicopter_control(QtCore.QThread):
83       
84        def __init__(self, \
85                     device_address=SERIAL_DEVICE, \
86                     command=DEFAULT_COMMAND, \
87                     DEBUG=DEBUG, \
88                     parent=None):
89               
90                QtCore.QThread.__init__(self, parent)
91               
92                self.log = None
93                self.DEBUG = DEBUG
94                self.parent = parent
95               
96                self.device_address = device_address
97                self.command = command
98                self.mode = DEFAULT_MODE
99               
100                self.serial_device = None
101                self.protocol = None
102               
103               
104                #self.configureRemote()
105       
106       
107        ##################################################################
108       
109        def configureRemote(self):
110               
111                self.serial_device = \
112                        SerialDevice( \
113                                self.log, \
114                                device_address=self.device_address, \
115                                mode=self.mode, \
116                                DEBUG=self.DEBUG, \
117                                parent=self)
118               
119                self.serial_device.start()
120               
121               
122                self.protocol = \
123                        ProtocolHandler( \
124                                self.log, \
125                                self.serial_device, \
126                                mode=self.mode, \
127                                DEBUG=self.DEBUG, \
128                                parent=self)
129               
130                self.protocol.start()
131       
132       
133        ##################################################################
134       
135        def processPacket(self, packet):
136               
137                if self.DEBUG:
138                        print "data_payload:",
139                        #print packet['data_payload']
140                        print packet['data_payload'].encode("hex")
141                       
142                        #if packet['data_payload'].encode("hex") == '80acdf22cdb08b8d54':
143                                #print True
144                                #import cPickle as pickle
145                                #file = open('packet.data', 'w')
146                                #pickle.dump(packet['data_payload'], file)
147                                #file.close()
148                                #sys.exit(app.exec_())
149               
150                #if (packet != {}):
151                        #self.packet_queue.append(packet)
152                        ###self.sendPacketQueue()
153               
154                if (self.parent != None):
155                        self.parent.processPacket(self.protocol.data_packet)
156       
157       
158        ##################################################################
159       
160        def dump_packets(self):
161               
162                pass
163       
164       
165        ##################################################################
166       
167        def neutral(self):
168               
169                if self.DEBUG:
170                        print "--> Command: neutral"
171               
172                #self.protocol.command_packet = COMMAND_PACKET['neutral']
173                self.protocol.command_packet = COMMAND_PACKET['no_thrust']
174       
175       
176        ##################################################################
177       
178        def test_packet(self):
179               
180                if self.DEBUG:
181                        print "--> Command: test_packet"
182               
183                self.protocol.command_packet = COMMAND_PACKET['test_packet']
184       
185       
186        ##################################################################
187       
188        def test_mode(self):
189               
190                if self.DEBUG:
191                        print "--> Command: test_mode"
192               
193                self.protocol.command_packet = COMMAND_PACKET['no_thrust']
194                QtCore.QThread.msleep(PROTOCOL_SYNC_TIME * 1000) # 4 seconds minimum to sync
195               
196                self.protocol.command_packet = COMMAND_PACKET['minimum_thrust']
197                QtCore.QThread.msleep(2 * 1000) # 1 second
198               
199                self.protocol.command_packet = COMMAND_PACKET['no_thrust']
200       
201       
202        ##################################################################
203       
204        def hover(self, duration=2):
205               
206                if self.DEBUG:
207                        print "--> Command: hover"
208               
209                self.protocol.command_packet = COMMAND_PACKET['no_thrust']
210                QtCore.QThread.msleep(PROTOCOL_SYNC_TIME * 1000) # 4 seconds minimum to sync
211               
212                self.protocol.command_packet = COMMAND_PACKET['fifty_percent_thrust']
213                QtCore.QThread.msleep(duration * 1000)
214               
215                self.protocol.command_packet = COMMAND_PACKET['no_thrust']
216       
217       
218        ##################################################################
219       
220        def fly_forward(self, duration=2):
221               
222                if self.DEBUG:
223                        print "--> Command: fly_forward"
224               
225                self.protocol.command_packet = COMMAND_PACKET['no_thrust']
226                QtCore.QThread.msleep(PROTOCOL_SYNC_TIME * 1000)
227               
228                self.protocol.command_packet = COMMAND_PACKET['fly_forward']
229                QtCore.QThread.msleep(duration * 1000)
230               
231                self.protocol.command_packet = COMMAND_PACKET['no_thrust']
232       
233       
234        ##################################################################
235       
236        def processCommand(self):
237               
238                if (self.command == 'dump_packets') or (self.command == 'read'):
239                        self.mode = 'read'
240                else:
241                        self.mode = 'write'
242               
243               
244                self.configureRemote()
245               
246               
247                if (self.command == 'dump_packets'):
248                        self.mode = 'read'
249                        self.dump_packets()
250               
251                elif (self.command == 'neutral'):
252                        self.mode = 'write'
253                        self.neutral()
254               
255                elif (self.command == 'test_packet'):
256                        self.mode = 'write'
257                        self.test_packet()
258               
259                elif (self.command == 'test_mode'):
260                        self.mode = 'write'
261                        self.test_mode()
262               
263                elif (self.command == 'hover'):
264                        self.mode = 'write'
265                        self.hover()
266               
267                elif (self.command == 'fly_forward'):
268                        self.mode = 'write'
269                        self.fly_forward(duration=2)
270       
271       
272        ##################################################################
273       
274        def stop(self):
275               
276                #self.connection.close()
277                pass
278       
279       
280        ##################################################################
281       
282        def run(self):
283               
284                if self.DEBUG:
285                        print "<---- [%s] Main thread running" % "Helicopter Remote"
286               
287               
288                self.processCommand()
289               
290                self.exec_()
291       
292       
293        ##################################################################
294       
295        def exitThread(self, callThreadQuit=True):
296               
297                try:
298                        self.emulationTimer.stop()
299                except:
300                        pass
301               
302                if self.serial_device != None:
303                        self.serial_device.exitThread()
304               
305                if self.protocol != None:
306                        self.protocol.exitThread()
307               
308                self.socket.close()
309               
310                if callThreadQuit:
311                        QtCore.QThread.quit(self)
312               
313                if self.parent == None:
314                        sys.exit()
315
316
317#####################################################################
318#####################################################################
319
320class ProtocolHandler(QtCore.QThread):
321       
322        def __init__(self, log, \
323                               serial_device, \
324                               mode=DEFAULT_MODE, \
325                               DEBUG=DEBUG, \
326                               parent=None):
327               
328                QtCore.QThread.__init__(self,parent)
329               
330                self.log = log
331                self.DEBUG = DEBUG
332                self.parent = parent
333               
334                self.device = None
335                self.mode = mode
336               
337                self.device = serial_device
338               
339                self.packet_count = 0
340                self.bad_packets = 0
341               
342                self.keep_running = True
343               
344                self.command_packet = DEFAULT_COMMAND_PACKET
345       
346       
347        ##################################################################
348       
349        def processDataPayload(self, data_payload, payload_timestamp):
350               
351                packet_update = {}
352                packet_update['data_payload'] = data_payload
353                packet_update['payload_timestamp'] = payload_timestamp
354               
355               
356                if (self.parent != None):
357                        self.parent.processPacket(packet_update)
358       
359       
360        ##################################################################
361       
362        def parseStream(self):
363               
364                # Loop forever, parsing one packet per loop
365                packet_count = 0
366               
367                while self.keep_running:
368                       
369                        # Synchronize on [SYNC] bytes
370                        byte = self.device.read()
371                        #print byte.encode("hex")
372                       
373                        #if (byte != PROTOCOL_SYNC):
374                        if (byte != PROTOCOL_SYNC_HEAD1):
375                                continue
376                       
377                        byte = self.device.read()
378                        if (byte != PROTOCOL_SYNC_HEAD2):
379                                continue
380                       
381                       
382                        payload_timestamp = time.time()
383                       
384                        data_payload = self.device.getBuffer()
385                        data_payload = "%s%s%s" % (PROTOCOL_SYNC_HEAD1, PROTOCOL_SYNC_HEAD2, data_payload)
386                       
387                       
388                        if len(data_payload) < PAYLOAD_MINIMUM_LENGTH:
389                        #if len(data_payload) != PACKET_LENGTH:
390                                continue
391                       
392                       
393                        self.processDataPayload(data_payload, payload_timestamp)
394                       
395                       
396                        #if self.DEBUG > 1:
397                                #packet_count += 1
398                                #if packet_count >= DEBUG_PACKET_COUNT:
399                                        #print "max debugging count reached, disconnecting"
400                                        #self.keep_running = False
401                                        #self.device.stop()
402                                        #QtCore.QThread.quit(self)
403                                        ##sys.exit()
404       
405       
406        ##################################################################
407       
408        def writeStream(self):
409               
410                # Loop forever, writing one packet per loop
411                packet_count = 0
412               
413                #import cPickle as pickle
414                #file = open('packet.data', 'r')
415                #packet = pickle.loads(file.read())
416                #file.close()
417               
418                while self.keep_running:
419                       
420                        # Preppend or Append [SYNC] bytes
421                        #if PROTOCOL_ADD_SYNC_TO_HEAD:
422                                #buffer = PROTOCOL_SYNC_HEAD1
423                                #buffer += PROTOCOL_SYNC_HEAD2
424                                #buffer += self.command_packet
425                       
426                        #if PROTOCOL_ADD_SYNC_TO_TAIL:
427                                #buffer = self.command_packet
428                                #buffer += PROTOCOL_SYNC_HEAD1
429                                #buffer += PROTOCOL_SYNC_HEAD2
430                               
431                        buffer = self.command_packet
432                        self.device.buffer = buffer
433                        #self.device.buffer = packet
434                        #print packet.encode("hex")
435                       
436                        # Sleep for 20 ms
437                        # Based on 50 Hz refresh rate of Blade MLP4DSM RC device
438                        # (1/50) * 1000 = 20
439                        QtCore.QThread.msleep(DEVICE_BUFFER_TIMER)
440       
441       
442        ##################################################################
443       
444        def run(self):
445               
446                self.packet_count = 0
447                self.bad_packets = 0
448                self.session_start_timestamp = time.time()
449               
450                if self.mode == 'read':
451                        self.parseStream()
452               
453                elif self.mode == 'write':
454                        self.writeStream()
455       
456       
457        ##################################################################
458       
459        def exitThread(self, callThreadQuit=True):
460               
461                try:
462                        self.device.stop()
463                except:
464                        pass
465               
466                #self.wait()
467                if callThreadQuit:
468                        QtCore.QThread.quit(self)
469
470
471#####################################################################
472#####################################################################
473
474class SerialDevice(QtCore.QThread):
475       
476        def __init__(self, log, \
477                               device_address=SERIAL_DEVICE, \
478                               mode=DEFAULT_MODE, \
479                               DEBUG=DEBUG, \
480                               parent=None):
481               
482                QtCore.QThread.__init__(self, parent)
483               
484                self.log = log
485                self.DEBUG = DEBUG
486               
487                self.device_address = device_address
488                self.mode = mode
489                self.device = None
490                self.buffer = ''
491               
492                if (self.device_address.count(':') == 5):
493                        # Device address is a Bluetooth MAC address
494                        self.device = self.initializeBluetoothDevice()
495                else:
496                        # Device address is a serial port address
497                        self.device = self.initializeSerialDevice()
498               
499                #self.buffer_check_timer = QtCore.QTimer()
500                #QtCore.QObject.connect(self.buffer_check_timer, \
501                                       #QtCore.SIGNAL("timeout()"), \
502                                       #self.checkBuffer)
503                #self.buffer_check_timer.start(DEVICE_BUFFER_TIMER)
504               
505                self.keep_running = True
506       
507       
508        ##################################################################
509       
510        #def initializeBluetoothDevice(self):
511               
512                #socket = bluetooth.BluetoothSocket( bluetooth.RFCOMM )
513               
514                #try:
515                        #socket.connect((self.device_address, THINKGEAR_DEVICE_BLUETOOTH_CHANNEL))
516               
517                #except Exception, e:
518                        #if self.DEBUG:
519                                #print "ERROR:",
520                                #print e
521                                #sys.exit()
522               
523               
524                #return socket
525       
526       
527        ###################################################################
528       
529        def initializeSerialDevice(self):
530               
531                baudrate = DEFAULT_SERIAL_BAUDRATE
532                bytesize = 8
533                parity = 'NONE'
534                stopbits = 1
535                software_flow_control = 'f'
536                rts_cts_flow_control = 'f'
537                #timeout = 15
538                timeout = 5
539               
540                # convert bytesize
541                if (bytesize == 5):
542                        init_byte_size = serial.FIVEBITS
543                elif (bytesize == 6):
544                        init_byte_size = serial.SIXBITS
545                elif (bytesize == 7):
546                        init_byte_size = serial.SEVENBITS
547                elif (bytesize == 8):
548                        init_byte_size = serial.EIGHTBITS
549                else:
550                        #self.log.perror("Invalid value for %s modem byte size! Using default (8)" % modem_type)
551                        init_byte_size = serial.EIGHTBITS
552               
553                # convert parity
554                if (parity == 'NONE'):
555                        init_parity = serial.PARITY_NONE
556                elif (parity == 'EVEN'):
557                        init_parity = serial.PARITY_EVEN
558                elif (parity == 'ODD'):
559                        init_parity = serial.PARITY_ODD
560                else:
561                        #self.log.perror("Invalid value for %s modem parity! Using default (NONE)" % modem_type)
562                        init_parity = serial.PARITY_NONE
563               
564                # convert stopbits
565                if (stopbits == 1):
566                        init_stopbits = serial.STOPBITS_ONE
567                elif (stopbits == 2):
568                        init_stopbits = serial.STOPBITS_TWO
569                else:
570                        #self.log.perror("Invalid value for %s modem stopbits! Using default (8)" % modem_type)
571                        init_byte_size = serial.STOPBITS_ONE
572               
573                # convert software flow control
574                if (software_flow_control == 't'):
575                        init_software_flow_control = 1
576                else:
577                        init_software_flow_control = 0
578               
579                # convert rts cts flow control
580                if (rts_cts_flow_control == 't'):
581                        init_rts_cts_flow_control = 1
582                else:
583                        init_rts_cts_flow_control = 0
584               
585               
586                try:
587                       
588                        device = serialWrapper(port = self.device_address, \
589                                                    baudrate = baudrate, \
590                                                    bytesize = init_byte_size, \
591                                                    parity = init_parity, \
592                                                    stopbits = init_stopbits, \
593                                                    xonxoff = init_software_flow_control, \
594                                                    rtscts = init_rts_cts_flow_control, \
595                                                    timeout = timeout)
596               
597                except Exception, e:
598                        if self.DEBUG:
599                                print "ERROR:",
600                                print e,
601                                print self.device_address
602                                sys.exit()
603               
604               
605                #device.flushInput()
606                ##device.flushOutput()
607               
608               
609                return(device)
610       
611       
612        ###################################################################
613       
614        #def checkBuffer(self):
615               
616                #if self.DEBUG > 1:
617                        #print "INFO: Buffer size check:",
618                        #print len(self.buffer),
619                        #print "(maximum before reset is %i)" % DEVICE_BUFFER_MAX_SIZE
620               
621                #if (DEVICE_BUFFER_MAX_SIZE <= len(self.buffer)):
622                       
623                        #if self.DEBUG:
624                                #print "ERROR: Buffer size has grown too large, resetting"
625                       
626                        #self.reset()
627       
628       
629        ###################################################################
630       
631        def getBuffer(self):
632               
633                data_payload = self.buffer
634               
635                self.resetBuffer()
636               
637               
638                return(data_payload)
639       
640       
641        ###################################################################
642       
643        def resetBuffer(self):
644               
645                self.buffer = ''
646       
647       
648        ###################################################################
649       
650        def read(self, length=1):
651               
652                # Sleep for 20 ms if buffer is empty
653                # Based on 50 Hz refresh rate of Blade MLP4DSM RC device
654                # (1/50) * 1000 = 20
655                while len(self.buffer) < length:
656                        QtCore.QThread.msleep(DEVICE_BUFFER_TIMER)
657                       
658                bytes = self.buffer[:length]
659               
660                self.buffer = self.buffer[length:]
661               
662                return(bytes)
663       
664       
665        ###################################################################
666       
667        def stop(self):
668               
669                #self.buffer_check_timer.stop()
670                self.keep_running = False
671       
672       
673        ###################################################################
674       
675        def exitThread(self, callThreadQuit=True):
676               
677                self.stop()
678                self.close()
679               
680                if callThreadQuit:
681                        QtCore.QThread.quit(self)
682       
683       
684        ###################################################################
685       
686        def close(self):
687               
688                self.device.close()
689       
690       
691        ###################################################################
692       
693        def readBuffer(self):
694       
695                self.buffer = ''
696               
697                while self.keep_running:
698                       
699                       
700                        # High-Speed Echo Mode
701                        if (self.DEBUG > 3) and ECHO_ON:
702                                byte = self.device.recv(PACKET_READ_SIZE)
703                                self.device.write(byte)
704                                continue
705                       
706                       
707                        try:
708                                #byte = self.device.read()
709                                byte = self.device.recv(PACKET_READ_SIZE)
710                               
711                                #if ECHO_ON:
712                                self.device.write(byte)
713                               
714                                if (len(byte) != 0):
715                                        if self.DEBUG > 2:
716                                                print "Device read:",
717                                                print byte,
718                                                if ECHO_ON:
719                                                        print byte.encode("hex"),
720                                                        print "wrote:",
721                                                print byte.encode("hex")
722                                               
723                                        self.buffer += byte
724                       
725                        except:
726                                if self.DEBUG:
727                                        print "ERROR: failed to read from serial device"
728                                break
729               
730               
731                self.exitThread()
732       
733       
734        ###################################################################
735       
736        def writeBuffer(self):
737       
738                self.buffer = ''
739                #beacon_timer = 0
740               
741                while self.keep_running:
742                       
743                        if (len(self.buffer) != 0):
744                                buffer = self.buffer
745                                self.buffer = ''
746                               
747                               
748                                #if beacon_timer >= 750:
749                                        #buffer += '\xaa' + buffer
750                                        #beacon_timer = 0
751                               
752                               
753                                try:
754                                        self.device.write(buffer)
755                                       
756                                        if self.DEBUG > 1:
757                                                print "Device wrote:",
758                                                #print buffer,
759                                                print buffer.encode("hex")
760                               
761                                except:
762                                        if self.DEBUG:
763                                                print "ERROR: failed to write to serial device"
764                                        break
765                       
766                       
767                        # Sleep for 20 ms if buffer is empty
768                        # Based on 50 Hz refresh rate of Blade MLP4DSM RC device
769                        # (1/50) * 1000 = 20
770                        QtCore.QThread.msleep(DEVICE_BUFFER_TIMER)
771                        #beacon_timer += DEVICE_BUFFER_TIMER
772               
773               
774                self.exitThread()
775       
776       
777        ###################################################################
778       
779        def run(self):
780               
781                if self.mode == 'read':
782                        self.readBuffer()
783               
784                elif self.mode == 'write':
785                        self.writeBuffer()
786
787
788#####################################################################
789#####################################################################
790
791class serialWrapper(serial.Serial):
792       
793        #__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)
794       
795        def recv(self, size=1):
796               
797                return(self.read(size))
798
799
800#####################################################################
801# Functions
802#####################################################################
803
804#####################################################################
805# Main
806#####################################################################
807
808if __name__ == '__main__':
809       
810        # Perform correct KeyboardInterrupt handling
811        signal.signal(signal.SIGINT, signal.SIG_DFL)
812       
813        # Collect default settings and command line parameters
814        device = SERIAL_DEVICE
815        command = DEFAULT_COMMAND
816       
817        for each in sys.argv:
818               
819                if each.startswith("--device="):
820                        device = each[ len("--device="): ]
821                elif each.startswith("--command="):
822                        command = each[ len("--command="): ]
823       
824       
825        app = QtCore.QCoreApplication(sys.argv)
826       
827        rc = puzzlebox_brainstorms_helicopter_control(device_address=device, command=command, DEBUG=DEBUG)
828       
829        rc.start()
830       
831        sys.exit(app.exec_())
832
Note: See TracBrowser for help on using the repository browser.