Skip to content

Commit f2f6bba

Browse files
committed
Commented MenuLib
Finished commenting Missions Documentation done!
1 parent 1320bba commit f2f6bba

File tree

2 files changed

+259
-135
lines changed

2 files changed

+259
-135
lines changed

FLLMaster/MenuLib.py

Lines changed: 218 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,99 +1,219 @@
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()

FLLMaster/Missions.py

Lines changed: 41 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,42 @@
1-
if __name__ == "__main__":
2-
from DriveLibraries import *
3-
robot = Robot('robot.cfg')
4-
else:
5-
from MenuLib import robot
6-
7-
# Mission Names should be prefaced with a##, where ## is two numbers. These
8-
# numbers will not show up on the display, and tell the program what order the
9-
# missions should be in; a00 is first, followed by a01, and so on. It is
10-
# reccomended that spaces are left between the numbers (a00, a05, a10...), to
11-
# allow for future missions.
12-
13-
# Write Mission Programs Here ----------------------
14-
15-
def a00Star():
16-
# Drives a simple 4-pointed star, with arced sides
17-
robot.ArcTurn(-90, 30, 30)
18-
robot.ArcTurn(-90, 30, -30)
19-
robot.ArcTurn(-90, 30, 30)
20-
robot.ArcTurn(-90, 30, -30)
21-
22-
def a05LineFollow():
23-
# Follows a line for 50 cm, at 50% speed, stopping at end
24-
robot.LineFollow(50, 50, True)
25-
26-
def a10Command_Key():
27-
# Drives in a pattern similar to the Apple command key
28-
# o_o
29-
# | |
30-
# o‾o
31-
robot.DriveAtHeading(0, 50, 30, False)
32-
robot.ArcTurn(270, 10, 30)
33-
robot.DriveAtHeading(270, 50, 30, False)
34-
robot.ArcTurn(270, 10, 30)
35-
robot.DriveAtHeading(270 * 2, 50, 30, False)
36-
robot.ArcTurn(270, 10, 30)
37-
robot.DriveAtHeading(270 * 3, 50, 30, True)
1+
# Check if the program is running (rare) or if it is being used as a library
2+
if __name__ == "__main__":
3+
# If it is running, import everything from Drivelibraries and create a robot object.
4+
from DriveLibraries import *
5+
robot = Robot('robot.cfg')
6+
else:
7+
# If it is a library, import the robot object from MenuLib
8+
# (which would have called Missions in the first place).
9+
from MenuLib import robot
10+
11+
# Mission Names should be prefaced with a##, where ## is two numbers. These
12+
# numbers will not show up on the display, and tell the program what order the
13+
# missions should be in; a00 is first, followed by a01, and so on. It is
14+
# reccomended that spaces are left between the numbers (a00, a05, a10...), to
15+
# allow for future missions.
16+
17+
# Write Mission Programs Here ----------------------
18+
19+
def a05Star():
20+
# Drives a simple 4-pointed star, with arced sides
21+
robot.ArcTurn(-90, 30, 30)
22+
robot.ArcTurn(-90, 30, -30)
23+
robot.ArcTurn(-90, 30, 30)
24+
robot.ArcTurn(-90, 30, -30)
25+
26+
def a00LineFollow():
27+
# Follows a line for 50 cm, at 50% speed, stopping at end
28+
robot.LineFollow(50, 50, True)
29+
30+
def a10Command_Key():
31+
# Drives in a pattern similar to the Apple command key
32+
# o_o
33+
# | |
34+
# o‾o
35+
robot.DriveAtHeading(0, 50, 30, False)
36+
robot.ArcTurn(270, 10, 30)
37+
robot.DriveAtHeading(270, 50, 30, False)
38+
robot.ArcTurn(270, 10, 30)
39+
robot.DriveAtHeading(270 * 2, 50, 30, False)
40+
robot.ArcTurn(270, 10, 30)
41+
robot.DriveAtHeading(270 * 3, 50, 30, True)
3842
robot.GyroTurn(0)

0 commit comments

Comments
 (0)