lignite 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +28 -0
- data/NEWS.md +6 -0
- data/Rakefile +22 -1
- data/VERSION +1 -1
- data/bin/ev3tool +2 -149
- data/examples/bobbee.rb +95 -73
- data/lib/lignite.rb +2 -0
- data/lib/lignite/assembler.rb +1 -1
- data/lib/lignite/bytes.rb +10 -2
- data/lib/lignite/connection.rb +13 -5
- data/lib/lignite/connection/bluetooth.rb +1 -0
- data/lib/lignite/connection/replay.rb +8 -10
- data/lib/lignite/connection/tap.rb +2 -8
- data/lib/lignite/connection/usb.rb +2 -0
- data/lib/lignite/defines.rb +244 -0
- data/lib/lignite/defines.rb.erb +14 -0
- data/lib/lignite/direct_commands.rb +1 -0
- data/lib/lignite/enums.rb +659 -0
- data/lib/lignite/enums.rb.erb +21 -0
- data/lib/lignite/ev3_ops.rb +9401 -0
- data/lib/lignite/ev3_tool.rb +175 -0
- data/lib/lignite/logger.rb +1 -0
- data/lib/lignite/message.rb +2 -0
- data/lib/lignite/motors.rb +1 -1
- data/lib/lignite/op_compiler.rb +14 -126
- data/lib/lignite/system_commands.rb +25 -26
- data/lignite.gemspec +17 -2
- data/spec/assembler_spec.rb +0 -2
- data/spec/data/ev3tool_download.yml +3 -0
- data/spec/data/ev3tool_list_files.yml +3 -0
- data/spec/data/ev3tool_start.yml +4 -0
- data/spec/data/ev3tool_stop.yml +2 -0
- data/spec/data/ev3tool_upload.yml +5 -0
- data/spec/data/everstorm.rbf +0 -0
- data/spec/direct_commands_spec.rb +1 -0
- data/spec/ev3_tool_spec.rb +71 -0
- data/spec/spec_helper.rb +6 -1
- data/tools/ops_from_yml +176 -0
- metadata +44 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ce4cca8022a372bfd1ee229725d3a538376c6d54
|
4
|
+
data.tar.gz: 7957e5d6aa35cf8551752d3a324f2cef141bd466
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fdf692daad988853107d65e5ebf31da38e0c6bd5d4bf6819e9b0e8e8ca5a140fe95bdf51d32db8c556f1e07298f3671aa389e8c994ff2293f087ae1c237cf1a7
|
7
|
+
data.tar.gz: c5a98aedfde6259e69cdadbed737ee7fa1eed93d505a42489ee99a2a64ff8dcadabbdd8c1f4d7bca0a7cb004fc18caaa4cc33f61275153789a63386404896d18
|
data/.rubocop.yml
CHANGED
@@ -2,11 +2,39 @@ inherit_from: rubocop-suse.yml
|
|
2
2
|
|
3
3
|
AllCops:
|
4
4
|
TargetRubyVersion: 2.1
|
5
|
+
Exclude:
|
6
|
+
- 'vendor/**/*'
|
7
|
+
- '.git/**/*'
|
8
|
+
- lib/lignite/ev3_ops.rb
|
5
9
|
|
6
10
|
Naming/FileName:
|
7
11
|
Exclude:
|
8
12
|
- spec/data/*.rb
|
9
13
|
|
10
14
|
Layout/IndentHeredoc:
|
15
|
+
Enabled: false
|
16
|
+
|
17
|
+
Performance/RedundantBlockCall:
|
18
|
+
Enabled: false
|
19
|
+
|
20
|
+
Style/SymbolArray:
|
21
|
+
Enabled: false
|
22
|
+
|
23
|
+
Metrics/BlockLength:
|
11
24
|
Exclude:
|
12
25
|
- lignite.gemspec
|
26
|
+
- lib/lignite/ev3_tool.rb
|
27
|
+
- spec/**/*.rb
|
28
|
+
|
29
|
+
Metrics/ClassLength:
|
30
|
+
Exclude:
|
31
|
+
- lib/lignite/ev3_tool.rb
|
32
|
+
- lib/lignite/motors.rb
|
33
|
+
|
34
|
+
Metrics/MethodLength:
|
35
|
+
Max: 25
|
36
|
+
|
37
|
+
Metrics/ModuleLength:
|
38
|
+
Exclude:
|
39
|
+
# generated
|
40
|
+
- lib/lignite/enums.rb
|
data/NEWS.md
CHANGED
@@ -2,6 +2,12 @@
|
|
2
2
|
|
3
3
|
## unreleased
|
4
4
|
|
5
|
+
## 0.4.0, 2018-03-04
|
6
|
+
|
7
|
+
- Generate Ev3Ops ahead of time, enabling YARD docs for them,
|
8
|
+
reducing startup time
|
9
|
+
- Fixed `ev3tool ls`, `ls-l`, `start`, all broken in 0.3.0
|
10
|
+
|
5
11
|
## 0.3.0, 2018-03-02
|
6
12
|
|
7
13
|
- Added Tap and Replay to simulate a real robot with YAML files for tests
|
data/Rakefile
CHANGED
@@ -1,6 +1,27 @@
|
|
1
|
-
task default: :test
|
1
|
+
task default: [:test, :rubocop, :yard]
|
2
2
|
|
3
3
|
desc "Run tests"
|
4
4
|
task :test do
|
5
5
|
system "rspec"
|
6
6
|
end
|
7
|
+
|
8
|
+
desc "Run RuboCop"
|
9
|
+
task :rubocop do
|
10
|
+
system "rubocop --display-cop-names"
|
11
|
+
end
|
12
|
+
|
13
|
+
desc "Generate YARD documentation"
|
14
|
+
task :yard do
|
15
|
+
system "yardoc --use-cache"
|
16
|
+
end
|
17
|
+
|
18
|
+
EV3_YML = "data/ev3.yml".freeze
|
19
|
+
GENERATOR = "tools/ops_from_yml".freeze
|
20
|
+
OPS = "lib/lignite/ev3_ops.rb".freeze
|
21
|
+
|
22
|
+
desc "Regenerate the EV3 assembler operations"
|
23
|
+
task ops: OPS
|
24
|
+
|
25
|
+
file OPS => [EV3_YML, GENERATOR] do |t|
|
26
|
+
sh GENERATOR, t.prerequisites.first, t.name
|
27
|
+
end
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.4.0
|
data/bin/ev3tool
CHANGED
@@ -1,151 +1,4 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
require "lignite"
|
3
|
-
require "thor"
|
4
|
-
require "fileutils"
|
5
|
-
include Lignite::Bytes
|
2
|
+
require "lignite/ev3_tool"
|
6
3
|
|
7
|
-
|
8
|
-
# which is not very useful. A better default is /home/root/lms2012/prjs
|
9
|
-
# which is displayed on the 2nd tab of the brick UI.
|
10
|
-
EV3TOOL_HOME = "../prjs".freeze
|
11
|
-
|
12
|
-
def phandles
|
13
|
-
handles = sc.list_open_handles
|
14
|
-
puts "OPEN HANDLES: ", hexdump(handles)
|
15
|
-
end
|
16
|
-
|
17
|
-
class Ev3Tool < Thor
|
18
|
-
desc "upload LOCAL_FILENAME [BRICK_FILENAME]", "upload a program or a file"
|
19
|
-
map "ul" => "upload"
|
20
|
-
def upload(local_filename, brick_filename = nil)
|
21
|
-
data = File.read(local_filename, encoding: Encoding::BINARY)
|
22
|
-
unless brick_filename
|
23
|
-
prj = File.basename(local_filename, ".rbf")
|
24
|
-
brick_filename = "#{EV3TOOL_HOME}/#{prj}/#{prj}.rbf"
|
25
|
-
end
|
26
|
-
handle = sc.begin_download(data.bytesize, brick_filename)
|
27
|
-
sc.continue_download(handle, data)
|
28
|
-
end
|
29
|
-
|
30
|
-
desc "download BRICK_FILENAME [LOCAL_FILENAME]", "download a file"
|
31
|
-
map "dl" => "download"
|
32
|
-
def download(brick_filename, local_filename = nil)
|
33
|
-
local_filename ||= File.basename(brick_filename)
|
34
|
-
fsize, handle, data = sc.begin_upload(4096, brick_filename)
|
35
|
-
File.open(local_filename, "w") do |f|
|
36
|
-
loop do
|
37
|
-
f.write(data)
|
38
|
-
fsize -= data.bytesize
|
39
|
-
break if fsize.zero?
|
40
|
-
handle, data = sc.continue_upload(handle, 4096)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
desc "list-files DIRNAME", "list DIRNAME in a long format"
|
46
|
-
map "ls-l" => "list-files"
|
47
|
-
def list_files(name)
|
48
|
-
name ||= EV3TOOL_HOME
|
49
|
-
name = "#{EV3TOOL_HOME}/#{name}" unless name.start_with?("/")
|
50
|
-
|
51
|
-
result = ""
|
52
|
-
fsize, handle, data = sc.list_files(4096, name)
|
53
|
-
loop do
|
54
|
-
result += data
|
55
|
-
fsize -= data.bytesize
|
56
|
-
break if fsize.zero?
|
57
|
-
handle, data = sc.continue_list_files(handle, 4096)
|
58
|
-
end
|
59
|
-
result
|
60
|
-
rescue Lignite::VMError
|
61
|
-
nil
|
62
|
-
end
|
63
|
-
|
64
|
-
desc "ls DIRNAME", "list DIRNAME in a short format"
|
65
|
-
def ls(name)
|
66
|
-
raw = list_files(name)
|
67
|
-
return nil if raw.nil?
|
68
|
-
|
69
|
-
raw.lines.map do |l|
|
70
|
-
l = l.chomp
|
71
|
-
next nil if ["./", "../"].include?(l)
|
72
|
-
next l if l.end_with?("/")
|
73
|
-
# skip checksum + space + size + space
|
74
|
-
l[32 + 1 + 8 + 1..-1]
|
75
|
-
end.compact
|
76
|
-
end
|
77
|
-
|
78
|
-
desc "start NAME", "start a program"
|
79
|
-
def start(name)
|
80
|
-
if name.include?("/")
|
81
|
-
name = "#{EV3TOOL_HOME}/#{name}" unless name.start_with?("/")
|
82
|
-
else
|
83
|
-
name = "#{EV3TOOL_HOME}/#{name}/#{name}.rbf"
|
84
|
-
end
|
85
|
-
|
86
|
-
unless file_exist?(name)
|
87
|
-
$stderr.puts "File #{name.inspect} not found"
|
88
|
-
exit 1
|
89
|
-
end
|
90
|
-
|
91
|
-
slot = Lignite::USER_SLOT
|
92
|
-
no_debug = 0
|
93
|
-
dc.block do
|
94
|
-
# these are local variables
|
95
|
-
data32 :size
|
96
|
-
data32 :ip
|
97
|
-
file_load_image(slot, name, :size, :ip)
|
98
|
-
program_start(slot, :size, :ip, no_debug)
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
desc "stop", "stop a running program"
|
103
|
-
def stop
|
104
|
-
dc.program_stop(Lignite::USER_SLOT)
|
105
|
-
end
|
106
|
-
|
107
|
-
no_commands do
|
108
|
-
def file_exist?(name)
|
109
|
-
dirname = File.dirname(name)
|
110
|
-
filename = File.basename(name)
|
111
|
-
files = ls(dirname) || []
|
112
|
-
files.include?(filename)
|
113
|
-
end
|
114
|
-
|
115
|
-
def assisted_connection
|
116
|
-
Lignite::Connection.create
|
117
|
-
rescue => e
|
118
|
-
fn = Lignite::Connection::Bluetooth.config_filename
|
119
|
-
$stderr.puts <<MSG
|
120
|
-
Could not connect to EV3.
|
121
|
-
Use a USB cable or configure a Bluetooth address in #{fn.inspect}.
|
122
|
-
Details:
|
123
|
-
#{e.message}
|
124
|
-
MSG
|
125
|
-
|
126
|
-
template = Lignite::Connection::Bluetooth.template_config_filename
|
127
|
-
if !File.exist?(fn) && File.exist?(template)
|
128
|
-
FileUtils.mkdir_p(File.dirname(fn))
|
129
|
-
FileUtils.install(template, fn)
|
130
|
-
$stderr.puts "(A template config file has been copied for your convenience)"
|
131
|
-
end
|
132
|
-
exit 1
|
133
|
-
end
|
134
|
-
|
135
|
-
def sc
|
136
|
-
return @sc if @sc
|
137
|
-
@sc = Lignite::SystemCommands.new(conn)
|
138
|
-
end
|
139
|
-
|
140
|
-
def dc
|
141
|
-
return @dc if @dc
|
142
|
-
@dc = Lignite::DirectCommands.new(conn)
|
143
|
-
end
|
144
|
-
|
145
|
-
def conn
|
146
|
-
@conn ||= assisted_connection
|
147
|
-
end
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
Ev3Tool.start(ARGV)
|
4
|
+
Lignite::Ev3Tool.start(ARGV)
|
data/examples/bobbee.rb
CHANGED
@@ -1,15 +1,17 @@
|
|
1
1
|
#!/usr/bin/ruby
|
2
|
-
|
3
|
-
# https://www.lego.com/mindstorms/build-a-robot/bobb3e
|
4
|
-
# A Bobcat® like two-tracked forklift
|
5
|
-
|
6
2
|
require "lignite"
|
7
3
|
|
4
|
+
# A Bobcat like two-tracked forklift.
|
5
|
+
# https://www.lego.com/mindstorms/build-a-robot/bobb3e
|
8
6
|
class Bobbee
|
7
|
+
# The pair of motors driving the tracks
|
9
8
|
# @return [Lignite::Motors]
|
10
9
|
attr_reader :drive
|
10
|
+
# The motor lifting the fork
|
11
11
|
# @return [Lignite::Motors]
|
12
12
|
attr_reader :lift
|
13
|
+
# Interface to all other commands
|
14
|
+
# @return [Lignite::DirectCommands]
|
13
15
|
attr_reader :dc
|
14
16
|
|
15
17
|
def initialize(drive: Lignite::PORT_B | Lignite::PORT_C,
|
@@ -21,33 +23,34 @@ class Bobbee
|
|
21
23
|
@dc = dc
|
22
24
|
end
|
23
25
|
|
26
|
+
# @param speed [Integer] -100..100
|
24
27
|
# @param turn [Integer] -200..200
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
#
|
28
|
+
# turn 0 (straight)
|
29
|
+
# - 900 (2.5 * 360) degrees is about 1 boost square
|
30
|
+
# turn 200 (full right)
|
31
|
+
# - 2400 degrees is a full turn, 600 deg is an about-right turn
|
32
|
+
# where the center of rotation is the battery-cover of the brick,
|
33
|
+
# or between the rear axles.
|
34
|
+
# turn 100 (semi right)
|
35
|
+
# - 4800 degrees is a full turn, about one of the rear axles
|
36
|
+
# @param degrees [Integer] degrees to turn the motors
|
37
|
+
# (complicated by gears and *turn*, which see)
|
38
|
+
# @param brake [BRAKE,COAST]
|
39
|
+
# @param wait [Boolean] work around my ignorance of motor readiness
|
31
40
|
def step_sync(speed, turn, degrees, brake = Lignite::BRAKE, wait: true)
|
32
|
-
# turn 0 (straight)
|
33
|
-
# - 900 (2.5 * 360) degrees is about 1 boost square
|
34
|
-
# turn 200 (full right)
|
35
|
-
# - 2400 degrees is a full turn, 600 deg is an about-right turn
|
36
|
-
# where the center of rotation is the battery-cover of the brick,
|
37
|
-
# or between the rear axles.
|
38
|
-
# turn 100 (semi right)
|
39
|
-
# - 4800 degrees is a full turn, about one of the rear axles
|
40
41
|
drive.step_sync(-speed, turn, degrees, brake)
|
41
42
|
drive.ready if wait
|
42
43
|
end
|
43
44
|
|
44
45
|
LIFT_FULL = 220
|
45
46
|
|
47
|
+
# Raise the fork from the ground up
|
46
48
|
def raise(wait: true)
|
47
49
|
lift.step_power(30, 10, LIFT_FULL - 20, 10)
|
48
50
|
lift.ready if wait
|
49
51
|
end
|
50
52
|
|
53
|
+
# Raise the fork one third of the way from the ground up
|
51
54
|
def third_raise(wait: true)
|
52
55
|
lift.step_power(30, 10, LIFT_FULL / 3 - 20, 10)
|
53
56
|
lift.ready if wait
|
@@ -56,6 +59,7 @@ class Bobbee
|
|
56
59
|
sleep 3
|
57
60
|
end
|
58
61
|
|
62
|
+
# Lower the fork from above to the ground
|
59
63
|
def lower(wait: true)
|
60
64
|
lift.step_power(-1, 10, LIFT_FULL - 20, 10) # , Lignite::COAST)
|
61
65
|
lift.ready if wait
|
@@ -64,107 +68,125 @@ class Bobbee
|
|
64
68
|
sleep 3
|
65
69
|
end
|
66
70
|
|
71
|
+
# Lower the fork one third of the way from above to the ground
|
67
72
|
def third_lower(wait: true)
|
68
73
|
lift.step_power(-1, 10, LIFT_FULL / 3 - 20, 10)
|
69
74
|
lift.ready if wait
|
70
75
|
end
|
71
76
|
|
77
|
+
# Beep
|
72
78
|
def beep(ms = 300)
|
73
79
|
dc.sound_tone(20, 1760, ms)
|
74
80
|
dc.sound_ready
|
75
81
|
end
|
76
82
|
|
83
|
+
# steps to move 1 square on the Boost mat
|
77
84
|
SQUARE_STEPS = 920
|
78
|
-
|
85
|
+
|
86
|
+
# Drive forward 1 square on the Boost mat
|
87
|
+
def forward(steps = SQUARE_STEPS)
|
79
88
|
step_sync(50, 0, steps)
|
80
89
|
beep
|
81
90
|
sleep 3
|
82
91
|
end
|
83
92
|
|
84
|
-
|
93
|
+
# Drive backward 1 square on the Boost mat
|
94
|
+
def back(steps = SQUARE_STEPS)
|
85
95
|
step_sync(-50, 0, back_factor(steps))
|
86
96
|
beep
|
87
97
|
sleep 3
|
88
98
|
end
|
89
99
|
|
90
|
-
#
|
100
|
+
# Compensation factor when moving backward
|
91
101
|
def back_factor(n)
|
102
|
+
# why? maybe the center of mass affects it
|
92
103
|
(n * 0.95).to_i
|
93
104
|
end
|
94
105
|
|
106
|
+
# If we want to turn inside a Boost mat square,
|
107
|
+
# we cannot simply make a turn since the center of rotation of the robot
|
108
|
+
# is between the rear axles which is not in the center of the square.
|
109
|
+
# So we need to move forward a bit, turn, and move back the same amount.
|
95
110
|
ROTATION_OFFSET = 350
|
96
|
-
|
97
|
-
|
111
|
+
|
112
|
+
def align_centers_for_turning(&turn_block)
|
113
|
+
forward(ROTATION_OFFSET)
|
114
|
+
beep(100)
|
115
|
+
|
116
|
+
turn_block.call
|
117
|
+
|
118
|
+
back(ROTATION_OFFSET)
|
98
119
|
beep(100)
|
99
120
|
end
|
100
121
|
|
122
|
+
# Motor degrees needed to turn the robot 90 degrees when using turn=200
|
101
123
|
TURN_90_AT_200_STEPS = 600
|
102
|
-
|
124
|
+
|
125
|
+
# Turn 90 degrees left, simply by moving tracks in opposite directions
|
126
|
+
def left_immediate
|
103
127
|
step_sync(50, -200, TURN_90_AT_200_STEPS)
|
104
128
|
beep(100)
|
105
129
|
sleep 3
|
106
130
|
end
|
107
131
|
|
108
|
-
|
132
|
+
# Turn 90 degrees right, simply by moving tracks in opposite directions
|
133
|
+
def right_immediate
|
109
134
|
step_sync(50, 200, TURN_90_AT_200_STEPS)
|
110
135
|
beep(100)
|
111
136
|
sleep 3
|
112
137
|
end
|
113
138
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
def l
|
120
|
-
# a 3 part maneuver: forward, left, and back-a-little
|
121
|
-
# to turn 1 BS left
|
122
|
-
f1
|
123
|
-
l2
|
124
|
-
f3
|
139
|
+
# Turn 90 degrees left, starting and ending inside a Boost mat square
|
140
|
+
def left
|
141
|
+
align_centers_for_turning do
|
142
|
+
left_immediate
|
143
|
+
end
|
125
144
|
end
|
126
145
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
f3
|
146
|
+
# Turn 90 degrees right, starting and ending inside a Boost mat square
|
147
|
+
def right
|
148
|
+
align_centers_for_turning do
|
149
|
+
right_immediate
|
150
|
+
end
|
133
151
|
end
|
134
152
|
end
|
135
153
|
|
136
154
|
bb = Bobbee.new
|
137
|
-
# 1 BS
|
138
|
-
# bb.time_sync(100, 0, 1500)
|
139
155
|
|
140
|
-
# Put Bobb3e on the blue arrow. Move it one square left, and forward.
|
141
|
-
# Put the container on a raised platform on the "twins" square.
|
142
|
-
# Put a raised platform on the "fire" square
|
156
|
+
# Put Bobb3e (B) on the blue arrow (^). Move it one square left, and forward.
|
157
|
+
# Put the container on a raised platform on the "twins" square (T).
|
158
|
+
# Put a raised platform on the "fire" square (F).
|
159
|
+
#
|
160
|
+
# ..F
|
161
|
+
# ...
|
162
|
+
# B.T
|
163
|
+
# .^.
|
143
164
|
def from_twins_to_fire(bb)
|
144
165
|
bb.instance_exec do
|
145
166
|
beep
|
146
167
|
|
168
|
+
2.times { third_raise }
|
169
|
+
|
170
|
+
right
|
171
|
+
forward
|
147
172
|
third_raise
|
148
|
-
|
149
|
-
|
150
|
-
f
|
151
|
-
third_raise
|
152
|
-
b
|
153
|
-
l
|
173
|
+
back
|
174
|
+
left
|
154
175
|
# we're at the starting position, but carrying the load
|
155
176
|
|
156
177
|
# move towards the recipient
|
157
|
-
|
178
|
+
2.times { forward }
|
179
|
+
right
|
158
180
|
|
159
181
|
# deliver and unload
|
160
|
-
|
182
|
+
forward
|
161
183
|
third_lower
|
162
|
-
|
184
|
+
back
|
163
185
|
|
164
186
|
# resting position
|
165
|
-
|
166
|
-
|
167
|
-
third_lower
|
187
|
+
left
|
188
|
+
2.times { back }
|
189
|
+
2.times { third_lower }
|
168
190
|
end
|
169
191
|
end
|
170
192
|
|
@@ -172,37 +194,37 @@ def from_fire_to_twins(bb)
|
|
172
194
|
bb.instance_exec do
|
173
195
|
beep
|
174
196
|
|
175
|
-
third_raise
|
176
|
-
third_raise
|
197
|
+
2.times { third_raise }
|
177
198
|
|
178
199
|
# move towards the load
|
179
|
-
|
200
|
+
2.times { forward }
|
201
|
+
right
|
180
202
|
|
181
203
|
# load
|
182
|
-
|
204
|
+
forward
|
183
205
|
third_raise
|
184
|
-
|
206
|
+
back
|
185
207
|
|
186
208
|
# starting position
|
187
|
-
|
209
|
+
left
|
210
|
+
2.times { back }
|
188
211
|
|
189
212
|
# deliver
|
190
|
-
|
191
|
-
|
213
|
+
right
|
214
|
+
forward
|
192
215
|
third_lower
|
193
|
-
|
194
|
-
|
216
|
+
back
|
217
|
+
left
|
195
218
|
|
196
219
|
# resting position
|
197
|
-
third_lower
|
198
|
-
third_lower
|
220
|
+
2.times { third_lower }
|
199
221
|
end
|
200
222
|
end
|
201
223
|
|
202
224
|
def calibrate_forward_and_back(bb)
|
203
225
|
bb.instance_exec do
|
204
|
-
|
205
|
-
|
226
|
+
4.times { forward }
|
227
|
+
4.times { back }
|
206
228
|
end
|
207
229
|
end
|
208
230
|
|