1- from DriveLibraries import *
2- import multiprocessing
3- from time import sleep
4- Missions = None
5- mission = None
6- robot = None
7- programs = []
8- numPrograms = 0
9- count = 1
10- loopIndex = 0
11-
12- def init (confFile ):
13- global programs
14- global numPrograms
15- global robot
16- global Missions
17- robot = Robot (confFile )
18- import Missions
19- programs = dir (Missions )
20- i = 0
21- index = - 1
22-
23- length = len (programs )
24- while i < length :
25- if not ((programs [index ][0 ] == "a" ) and (programs [index ][1 ].isnumeric ()) and (programs [index ][2 ].isnumeric ())):
26- programs .pop (index )
27- else :
28- index -= 1
29- i += 1
30- numPrograms = len (programs )
31-
32- robot .disp .clear ()
33- robot .disp .text_grid (programs [0 ][3 :], font = robot .fonts .load ('timR24' ))
34-
35- def runCurrentMission ():
36- method_to_call = getattr (Missions , programs [count - 1 ])
37- method_to_call ()
38- robot .rm .off (brake = False )
39- robot .lm .off (brake = False )
40-
41- def initthread ():
42- global mission
43- mission = multiprocessing .Process (target = runCurrentMission )
44-
45- def run ():
46- global mission
47- mission = multiprocessing .Process (target = runCurrentMission )
48- robot .zeroGyro ()
49- mission .start ()
50-
51- def display ():
52- name = programs [count - 1 ][3 :]
53- robot .disp .text_grid (name , font = robot .fonts .load ('timR24' ))
54-
55- def abort ():
56- mission .terminate ()
57- mission .join ()
58- sleep (1 )
59- robot .lm .off (brake = False )
60- robot .rm .off (brake = False )
61- minusCount ()
62- display ()
63-
64- def addCount ():
65- global count
66- count = (count % numPrograms ) + 1
67-
68- def minusCount ():
69- global count
70- count = ((count + (numPrograms - 2 )) % numPrograms ) + 1
71-
72- def checkDrift ():
73- ar = robot .gs .angle_and_rate
74- rate = ar [1 ]
75- if rate < - 0.5 or rate > 0.5 :
76- robot .led .all_off ()
77- robot .led .set_color ('LEFT' , 'RED' )
78- robot .led .set_color ('RIGHT' , 'RED' )
79- else :
80- robot .led .reset ()
81-
82- def displaySensor ():
83- if loopIndex == 0 :
84- robot .disp .rectangle (False , 0 , 89 , 177 , 120 , 'white' , 'white' )
85- robot .disp .update
86- robot .cs ._ensure_mode (robot .cs .MODE_COL_REFLECT )
87- robot .disp .text_pixels ("P1, Color:" + str (robot .correctedRLI ), False , 40 , 90 , font = robot .fonts .load ('courR10' ))
88- robot .disp .text_pixels ("P2, Gyro:" + str (robot .correctedAngle ), False , 40 , 100 , font = robot .fonts .load ('courR10' ))
89- robot .disp .update ()
90-
91- def calibrate ():
92- robot .led .all_off ()
93- robot .led .set_color ('LEFT' , 'YELLOW' )
94- robot .led .set_color ('RIGHT' , 'YELLOW' )
95- robot .spkr .beep ('-f 369.99' )
96- robot .gs .calibrate ()
97- sleep (1 )
98- robot .spkr .beep ()
1+ # Import everything from DriveLibraries to allow interaction with the robot, and to create a Robot object
2+ from DriveLibraries import *
3+ # Used for running a mission in a killable way
4+ import multiprocessing
5+ # Some things need delays
6+ from time import sleep
7+ # Define variables with null values so they can be used as global variables later
8+ Missions = None
9+ mission = None
10+ robot = None
11+ programs = []
12+ numPrograms = 0
13+ loopIndex = 0
14+
15+ # count is used to determine which mission should be run, starts at 1 not 0
16+ count = 1
17+
18+ def init (confFile ):
19+ """
20+ Creates a ``Robot`` oject named ``robot`` using the filename given in ``confFile``, loads all mission functions
21+ from ``Missions.py`` into ``programs`` alphabetically, using the three-character prefix on all mission function names,
22+ and shows the first mission name on the display.
23+ """
24+ # Declare global variables
25+ global programs
26+ global numPrograms
27+ global robot
28+ global Missions
29+
30+ # Instantiate a Robot object named "robot"
31+ robot = Robot (confFile )
32+ # Now that a robot object exists, the mission programs, which need a robot object, can be imported
33+ import Missions
34+ # This is how the menu auto-adds missions. dir() returns a list of all objects contained in whatever it was given.
35+ # However, there are a lot more objects than just the mission functions in Missions.py, so the list needs to be pruned.
36+ programs = dir (Missions )
37+ # Setting up some varibles for pruning
38+ i = 0
39+ index = - 1
40+ length = len (programs )
41+
42+ # This will repeat the mission check on every element of the list of all objects in Missions.py
43+ while i < length :
44+ # This checks if the name of the object currently being checked starts with a##, like all missions should.
45+ if not ((programs [index ][0 ] == "a" ) and (programs [index ][1 ].isnumeric ()) and (programs [index ][2 ].isnumeric ())):
46+ # If it does not, the object will be removed from the programs list, as it is not a mission
47+ programs .pop (index )
48+ else :
49+ # If it is, the index varible wil be decreased by one to skip over the mission program the next time around
50+ # (the scan starts at the back of the list).
51+ index -= 1
52+ # Increment i by 1 every loop cycle
53+ i += 1
54+
55+ # Now that the only things left in programs are missions, the length of programs can be used as the number of missions
56+ # (used in AddCount and MinusCount, for rollover).
57+ numPrograms = len (programs )
58+
59+ # Clear the display
60+ robot .disp .clear ()
61+ # Dislpay the first mission name (the [3:] is a string split; it prevents the a## from being displayed)
62+ robot .disp .text_grid (programs [0 ][3 :], font = robot .fonts .load ('timR24' ))
63+
64+ def runCurrentMission ():
65+ """
66+ Gets the currently selected mission from ``Missions.py`` and runs it. Once the mission has completed,
67+ it stops the motors and allows them to glide to allow for manual robot positioning.
68+ """
69+ # Finds the object in Missions.py that has the same name as the currently selected mission
70+ # (count - 1 is used because count starts at 1 not 0), and creates the function pointer
71+ # method_to_call, which is the selected mission
72+ method_to_call = getattr (Missions , programs [count - 1 ])
73+ # Runs the selected mission
74+ method_to_call ()
75+ # Stops the drive motors and allows them to glide
76+ robot .rm .off (brake = False )
77+ robot .lm .off (brake = False )
78+ # Attempts to stop auxillary motors. If there are none, the try blocks will fail and the program will continue
79+ try :
80+ robot .m1 .off (brake = False )
81+ try :
82+ robot .m2 .off (brake = False )
83+
84+ def initthread ():
85+ """
86+ Creates the ``mission`` process that is used to run missions while being able to abort said mission.
87+ This is also done in ``run()``, but needs to be done before other things will work properly
88+ """
89+ # Declare mission as a global variable because it is being edited
90+ global mission
91+ # Create a process that will call runCurrentMission
92+ mission = multiprocessing .Process (target = runCurrentMission )
93+
94+ def run ():
95+ """
96+ Called when the center button is pressed, this creates a process
97+ to run the current mission, reset the gyro to zero, then start the mission.
98+ """
99+ # Declare mission as a global variable because it is being edited
100+ global mission
101+ # Create a process that will call runCurrentMission
102+ mission = multiprocessing .Process (target = runCurrentMission )
103+
104+ # Reset the gyro angle to zero
105+ robot .zeroGyro ()
106+ # Start the mission process
107+ mission .start ()
108+
109+ def display ():
110+ """
111+ Display the currently selected mission on the screen
112+ """
113+ # Read the currently selected mission from the programs list, using count
114+ # The [3:] is a string split so the a## prefix is not displayed, and 1 is subtracted
115+ # from count because count starts at one, while list indexes start at zero.
116+ name = programs [count - 1 ][3 :]
117+ # Display the mission name on the screen, in 24pt Times New Roman
118+ robot .disp .text_grid (name , font = robot .fonts .load ('timR24' ))
119+
120+ def abort ():
121+ """
122+ Kills the current mission process, stops the motors and allows them to glide, and undoes auto-advance
123+ """
124+ # Kill the mission process
125+ mission .terminate ()
126+ # Wait for it to completely finish
127+ mission .join ()
128+ # Stop the drive motors
129+ robot .lm .off (brake = False )
130+ robot .rm .off (brake = False )
131+ # Attempts to stop auxillary motors. If there are none, the try blocks will fail and the program will continue
132+ try :
133+ robot .m1 .off (brake = False )
134+ try :
135+ robot .m2 .off (brake = False )
136+ # Undo the auto-advance so the mission could easily be redone
137+ minusCount ()
138+ display ()
139+
140+ def addCount ():
141+ """
142+ Increases the ``count`` variable by one, resetting to one when numPrograms would have been ecxeeded
143+ """
144+ # Declare count as global because it is being edited
145+ global count
146+ # Take the modulo of count and numPrograms (to rollover), then add one
147+ count = (count % numPrograms ) + 1
148+
149+ def minusCount ():
150+ """
151+ Decreases the ``count`` variable by one, resetting to numPrograms when count would have gone below 1
152+ """
153+ # Declare count as global because it is being edited
154+ global count
155+ # (count + (numPrograms - 2)) % numPrograms is equal to two less than count, with the desired rollover behavior.
156+ # However, that is decreasing by two, and its range is zero to numPrograms - 1. Thus, 1 is added to the result to
157+ # correct the range and decrease by only one.
158+ count = ((count + (numPrograms - 2 )) % numPrograms ) + 1
159+
160+ def checkDrift ():
161+ """
162+ Turns the brick light green if the gyro reports a rate within one degree/second of zero, and red otherwise.
163+ This is used as a signal to the user that the gyro may be drifting.
164+ """
165+ # Read the gyro angle and rate. Angle and rate mode is consistantly used because switching modes resets the angle.
166+ ar = robot .gs .angle_and_rate
167+ # angle_and_rate returns a list; this function only needs rate
168+ rate = ar [1 ]
169+ # Check if the reported rate is outside of 1 degree/second from zero
170+ if rate < - 0.5 or rate > 0.5 :
171+ # If it is, turn both lights red (lights are turned off to clear)
172+ robot .led .all_off ()
173+ robot .led .set_color ('LEFT' , 'RED' )
174+ robot .led .set_color ('RIGHT' , 'RED' )
175+ else :
176+ # Otherwise, reset the lights to green
177+ robot .led .reset ()
178+
179+ def displaySensor ():
180+ """
181+ Periodically displays the color and gyro sensor values at the bottom of the screen.
182+ """
183+ # loopIndex is equal to zero every hundeth loop cycle, so this function only runs that frequently.
184+ # This is to prevent flickering of the printout.
185+ if loopIndex == 0 :
186+ # Draws a white rectangle where the sensor values will be. This effectively clears only
187+ # that section of the screen.
188+ robot .disp .rectangle (False , 0 , 89 , 177 , 120 , 'white' , 'white' )
189+ # Display commands do not take effect until update() is called
190+ robot .disp .update ()
191+ # Displays the current calibrated color sensor RLI reading in the correct place in 10pt Courier New
192+ robot .disp .text_pixels ("P1, Color:" + str (robot .correctedRLI ), False , 40 , 90 , font = robot .fonts .load ('courR10' ))
193+ # Displays the current gyro sensor angle reading
194+ robot .disp .text_pixels ("P2, Gyro:" + str (robot .correctedAngle ), False , 40 , 100 , font = robot .fonts .load ('courR10' ))
195+ # Apply pending changes
196+ robot .disp .update ()
197+
198+ def calibrate ():
199+ """
200+ Provides a UI wrapper for the built-in ``robot.gs.calibrate`` function to calibrate the gyro
201+
202+ Use: When called, the brick lights will turn yellow, the robot will emit a low pitched beep that is not used anywhere else,
203+ and when calibration is complete, the robot will emit a normal beep and the lights will turn green again. The robot should
204+ be completely still between the two beeps while the lights are yellow.
205+ """
206+ # Turn the lights yellow (clearing first)
207+ robot .led .all_off ()
208+ robot .led .set_color ('LEFT' , 'YELLOW' )
209+ robot .led .set_color ('RIGHT' , 'YELLOW' )
210+ # Unique low pitched beep (Don't touch the robot!)
211+ robot .spkr .beep ('-f 369.99' )
212+ # Calibrate the gyro
213+ robot .gs .calibrate ()
214+ # Ensure calibration has completely finished
215+ sleep (1 )
216+ # Normal beep (Calibration finished; safe to touch robot)
217+ robot .spkr .beep ()
218+ # Reset the lights to green
99219 robot .led .reset ()
0 commit comments