lignite 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -0
- data/NEWS.md +10 -0
- data/VERSION +1 -1
- data/examples/bobbee.rb +75 -29
- data/examples/gyro-sensor.rb +19 -0
- data/lib/lignite.rb +12 -0
- data/lib/lignite/assembler.rb +48 -5
- data/lib/lignite/body_compiler.rb +35 -37
- data/lib/lignite/condition.rb +116 -0
- data/lib/lignite/connection.rb +4 -0
- data/lib/lignite/ev3_ops.rb +104 -12
- data/lib/lignite/ev3_tool.rb +9 -2
- data/lib/lignite/jump_offset.rb +18 -0
- data/lib/lignite/motors.rb +17 -8
- data/lib/lignite/op_compiler.rb +52 -26
- data/lib/lignite/parameter_declarer.rb +15 -15
- data/lib/lignite/variables.rb +27 -15
- data/lignite.gemspec +3 -0
- data/spec/data/HelloWorld.rb +2 -2
- data/spec/data/Performance.rb +1 -1
- data/spec/data/Performance.rbf +0 -0
- data/spec/data/ev3tool_download.yml +4 -2
- data/spec/data/everstorm.rbf +0 -0
- data/spec/ev3_tool_spec.rb +4 -1
- data/tools/ops_from_yml +9 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3fef446a97c670acf0bfc1944d3287d80e40799a
|
4
|
+
data.tar.gz: 034b4f841c015e98af6b1776b1705a3f20905936
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fd4a8eaaf713aef7ca9081a4755ff953b24aaba900c4c22b6547d90392390c7563125fc874f926cc6179aecc2428e8209f17df5799285fefda7b500c35d33c6a
|
7
|
+
data.tar.gz: f257ff8520fd6b5b124eb824a0869aac56c94db788c7b245cd116284e09a7a4cee20d467f634215329ffc37a664d738d419e934d768202172c3c7a6022d161d0
|
data/.rubocop.yml
CHANGED
data/NEWS.md
CHANGED
@@ -2,6 +2,16 @@
|
|
2
2
|
|
3
3
|
## unreleased
|
4
4
|
|
5
|
+
## 0.6.0, 2018-03-25
|
6
|
+
|
7
|
+
- Fixed silent corruption of files obtained by `ev3tool download`
|
8
|
+
- Added `ev3tool asm foo.rb foo.rbf`
|
9
|
+
- Added SimpleAssembler
|
10
|
+
- Added BodyCompiler#if_else, #loop_until_pre
|
11
|
+
- Implemented array_init* (PARVALUES)
|
12
|
+
- Added JumpOffset, replacing the Complex hack
|
13
|
+
- Variables are now aligned automatically; subroutine argument alignment is checked
|
14
|
+
|
5
15
|
## 0.5.0, 2018-03-05
|
6
16
|
|
7
17
|
- Ev3Ops: added missing array_* ops (with PARV in the signature).
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.6.0
|
data/examples/bobbee.rb
CHANGED
@@ -13,14 +13,20 @@ class Bobbee
|
|
13
13
|
# Interface to all other commands
|
14
14
|
# @return [Lignite::DirectCommands]
|
15
15
|
attr_reader :dc
|
16
|
+
# The robot is built with a https://en.wikipedia.org/wiki/Worm_drive
|
17
|
+
# lift mechanism
|
18
|
+
# @return [Boolean]
|
19
|
+
attr_reader :worm_lift
|
16
20
|
|
17
21
|
def initialize(drive: Lignite::PORT_B | Lignite::PORT_C,
|
18
22
|
lift: Lignite::PORT_A,
|
19
|
-
dc: Lignite::DirectCommands.new
|
23
|
+
dc: Lignite::DirectCommands.new,
|
24
|
+
worm_lift: false)
|
20
25
|
layer = 0
|
21
26
|
@drive = Lignite::Motors.new(layer, drive, dc)
|
22
27
|
@lift = Lignite::Motors.new(layer, lift, dc)
|
23
28
|
@dc = dc
|
29
|
+
@worm_lift = worm_lift
|
24
30
|
end
|
25
31
|
|
26
32
|
# @param speed [Integer] -100..100
|
@@ -43,34 +49,51 @@ class Bobbee
|
|
43
49
|
end
|
44
50
|
|
45
51
|
LIFT_FULL = 220
|
52
|
+
WORM_LIFT_FULL = 2400
|
46
53
|
|
47
54
|
# Raise the fork from the ground up
|
48
55
|
def raise(wait: true)
|
49
|
-
|
56
|
+
if worm_lift
|
57
|
+
lift.step_power(-30, 10, WORM_LIFT_FULL - 20, 10)
|
58
|
+
else
|
59
|
+
lift.step_power(30, 10, LIFT_FULL - 20, 10)
|
60
|
+
end
|
50
61
|
lift.ready if wait
|
51
62
|
end
|
52
63
|
|
53
64
|
# Raise the fork one third of the way from the ground up
|
54
65
|
def third_raise(wait: true)
|
55
|
-
|
66
|
+
if worm_lift
|
67
|
+
lift.step_power(-30, 10, WORM_LIFT_FULL / 3 - 20, 10)
|
68
|
+
else
|
69
|
+
lift.step_power(30, 10, LIFT_FULL / 3 - 20, 10)
|
70
|
+
end
|
56
71
|
lift.ready if wait
|
57
72
|
|
58
73
|
beep
|
59
|
-
|
74
|
+
ready
|
60
75
|
end
|
61
76
|
|
62
77
|
# Lower the fork from above to the ground
|
63
78
|
def lower(wait: true)
|
64
|
-
|
79
|
+
if worm_lift
|
80
|
+
lift.step_power(30, 10, WORM_LIFT_FULL - 20, 10)
|
81
|
+
else
|
82
|
+
lift.step_power(-1, 10, LIFT_FULL - 20, 10)
|
83
|
+
end
|
65
84
|
lift.ready if wait
|
66
85
|
|
67
86
|
beep
|
68
|
-
|
87
|
+
ready
|
69
88
|
end
|
70
89
|
|
71
90
|
# Lower the fork one third of the way from above to the ground
|
72
91
|
def third_lower(wait: true)
|
73
|
-
|
92
|
+
if worm_lift
|
93
|
+
lift.step_power(30, 10, WORM_LIFT_FULL / 3 - 20, 10)
|
94
|
+
else
|
95
|
+
lift.step_power(-1, 10, LIFT_FULL / 3 - 20, 10)
|
96
|
+
end
|
74
97
|
lift.ready if wait
|
75
98
|
end
|
76
99
|
|
@@ -87,14 +110,14 @@ class Bobbee
|
|
87
110
|
def forward(steps = SQUARE_STEPS)
|
88
111
|
step_sync(50, 0, steps)
|
89
112
|
beep
|
90
|
-
|
113
|
+
ready
|
91
114
|
end
|
92
115
|
|
93
116
|
# Drive backward 1 square on the Boost mat
|
94
117
|
def back(steps = SQUARE_STEPS)
|
95
118
|
step_sync(-50, 0, back_factor(steps))
|
96
119
|
beep
|
97
|
-
|
120
|
+
ready
|
98
121
|
end
|
99
122
|
|
100
123
|
# Compensation factor when moving backward
|
@@ -121,37 +144,49 @@ class Bobbee
|
|
121
144
|
|
122
145
|
# Motor degrees needed to turn the robot 90 degrees when using turn=200
|
123
146
|
TURN_90_AT_200_STEPS = 600
|
147
|
+
# Similar, but when we're carrying 100 grams, turning becomes harder!
|
148
|
+
LOADED_TURN_90_AT_200_STEPS = 750
|
124
149
|
|
125
150
|
# Turn 90 degrees left, simply by moving tracks in opposite directions
|
126
|
-
def left_immediate
|
127
|
-
|
151
|
+
def left_immediate(loaded: false)
|
152
|
+
steps = loaded ? LOADED_TURN_90_AT_200_STEPS : TURN_90_AT_200_STEPS
|
153
|
+
step_sync(50, -200, steps)
|
128
154
|
beep(100)
|
129
|
-
|
155
|
+
ready
|
130
156
|
end
|
131
157
|
|
132
158
|
# Turn 90 degrees right, simply by moving tracks in opposite directions
|
133
|
-
def right_immediate
|
134
|
-
|
159
|
+
def right_immediate(loaded: false)
|
160
|
+
steps = loaded ? LOADED_TURN_90_AT_200_STEPS : TURN_90_AT_200_STEPS
|
161
|
+
step_sync(50, 200, steps)
|
135
162
|
beep(100)
|
136
|
-
|
163
|
+
ready
|
137
164
|
end
|
138
165
|
|
139
166
|
# Turn 90 degrees left, starting and ending inside a Boost mat square
|
140
|
-
def left
|
167
|
+
def left(loaded: false)
|
141
168
|
align_centers_for_turning do
|
142
|
-
left_immediate
|
169
|
+
left_immediate(loaded: loaded)
|
143
170
|
end
|
144
171
|
end
|
145
172
|
|
146
173
|
# Turn 90 degrees right, starting and ending inside a Boost mat square
|
147
|
-
def right
|
174
|
+
def right(loaded: false)
|
148
175
|
align_centers_for_turning do
|
149
|
-
right_immediate
|
176
|
+
right_immediate(loaded: loaded)
|
150
177
|
end
|
151
178
|
end
|
152
|
-
end
|
153
179
|
|
154
|
-
|
180
|
+
# Wait until the previous motor movements are complete
|
181
|
+
def ready
|
182
|
+
return unless @dc.is_a? Lignite::DirectCommands
|
183
|
+
|
184
|
+
print "Ready... "
|
185
|
+
drive.test
|
186
|
+
lift.test
|
187
|
+
puts "OK"
|
188
|
+
end
|
189
|
+
end
|
155
190
|
|
156
191
|
# Put Bobb3e (B) on the blue arrow (^). Move it one square left, and forward.
|
157
192
|
# Put the container on a raised platform on the "twins" square (T).
|
@@ -169,18 +204,18 @@ def from_twins_to_fire(bb)
|
|
169
204
|
|
170
205
|
right
|
171
206
|
forward
|
172
|
-
third_raise
|
207
|
+
2.times { third_raise }
|
173
208
|
back
|
174
|
-
left
|
209
|
+
left(loaded: true)
|
175
210
|
# we're at the starting position, but carrying the load
|
176
211
|
|
177
212
|
# move towards the recipient
|
178
213
|
2.times { forward }
|
179
|
-
right
|
214
|
+
right(loaded: true)
|
180
215
|
|
181
216
|
# deliver and unload
|
182
217
|
forward
|
183
|
-
third_lower
|
218
|
+
2.times { third_lower }
|
184
219
|
back
|
185
220
|
|
186
221
|
# resting position
|
@@ -202,17 +237,17 @@ def from_fire_to_twins(bb)
|
|
202
237
|
|
203
238
|
# load
|
204
239
|
forward
|
205
|
-
third_raise
|
240
|
+
2.times { third_raise }
|
206
241
|
back
|
207
242
|
|
208
243
|
# starting position
|
209
|
-
left
|
244
|
+
left(loaded: true)
|
210
245
|
2.times { back }
|
211
246
|
|
212
247
|
# deliver
|
213
|
-
right
|
248
|
+
right(loaded: true)
|
214
249
|
forward
|
215
|
-
third_lower
|
250
|
+
2.times { third_lower }
|
216
251
|
back
|
217
252
|
left
|
218
253
|
|
@@ -228,6 +263,17 @@ def calibrate_forward_and_back(bb)
|
|
228
263
|
end
|
229
264
|
end
|
230
265
|
|
266
|
+
mode = ARGV.first || "direct"
|
267
|
+
dc = if mode == "rbf"
|
268
|
+
Lignite::SimpleAssembler.new
|
269
|
+
else
|
270
|
+
Lignite::DirectCommands.new
|
271
|
+
end
|
272
|
+
|
273
|
+
bb = Bobbee.new(dc: dc, worm_lift: true)
|
274
|
+
|
231
275
|
# calibrate_forward_and_back(bb)
|
232
276
|
from_twins_to_fire(bb)
|
233
277
|
# from_fire_to_twins(bb)
|
278
|
+
|
279
|
+
dc.write("bobbee.rbf") if mode == "rbf"
|
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
require "lignite"
|
3
|
+
|
4
|
+
dc = Lignite::DirectCommands.new
|
5
|
+
|
6
|
+
# Read gyro sensor angle on port 2
|
7
|
+
LAYER0 = 0
|
8
|
+
MODE = 0
|
9
|
+
pct = dc.with_reply do
|
10
|
+
# global vars
|
11
|
+
dataf :angle
|
12
|
+
|
13
|
+
block do
|
14
|
+
input_device_ready_si(LAYER0, Lignite::PORT_2, Lignite::TYPE_GYRO, MODE, :angle)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
puts "Gyro sensor angle: #{pct}"
|
18
|
+
|
19
|
+
dc.close
|
data/lib/lignite.rb
CHANGED
@@ -4,11 +4,13 @@ require "lignite/logger"
|
|
4
4
|
require "lignite/assembler"
|
5
5
|
require "lignite/body_compiler"
|
6
6
|
require "lignite/connection"
|
7
|
+
require "lignite/condition"
|
7
8
|
require "lignite/connection/bluetooth"
|
8
9
|
require "lignite/connection/replay"
|
9
10
|
require "lignite/connection/tap"
|
10
11
|
require "lignite/connection/usb"
|
11
12
|
require "lignite/direct_commands"
|
13
|
+
require "lignite/jump_offset"
|
12
14
|
require "lignite/message"
|
13
15
|
require "lignite/motors"
|
14
16
|
require "lignite/op_compiler"
|
@@ -17,7 +19,10 @@ require "lignite/system_commands"
|
|
17
19
|
require "lignite/variables"
|
18
20
|
require "lignite/version"
|
19
21
|
|
22
|
+
# The main namespace
|
20
23
|
module Lignite
|
24
|
+
LAYER_0 = 0
|
25
|
+
|
21
26
|
PORT_A = 1
|
22
27
|
PORT_B = 2
|
23
28
|
PORT_C = 4
|
@@ -39,4 +44,11 @@ module Lignite
|
|
39
44
|
# Represents an error returned by the robot
|
40
45
|
class VMError < RuntimeError
|
41
46
|
end
|
47
|
+
|
48
|
+
def program(&block)
|
49
|
+
p = Assembler.new
|
50
|
+
p.compile(&block)
|
51
|
+
p
|
52
|
+
end
|
53
|
+
module_function :program
|
42
54
|
end
|
data/lib/lignite/assembler.rb
CHANGED
@@ -17,6 +17,7 @@ module Lignite
|
|
17
17
|
class Assembler
|
18
18
|
include Bytes
|
19
19
|
include Logger
|
20
|
+
include Lignite # for constants
|
20
21
|
|
21
22
|
HEADER_SIZE = 16
|
22
23
|
SIGNATURE = "LEGO".freeze
|
@@ -26,9 +27,14 @@ module Lignite
|
|
26
27
|
end
|
27
28
|
|
28
29
|
# @return [Array<RbfObject>]
|
29
|
-
|
30
|
+
attr_accessor :objects
|
30
31
|
# @return [Variables]
|
31
|
-
|
32
|
+
attr_accessor :globals
|
33
|
+
|
34
|
+
def initialize
|
35
|
+
@objects = []
|
36
|
+
@globals = Variables.new
|
37
|
+
end
|
32
38
|
|
33
39
|
# Assemble a complete RBF program file.
|
34
40
|
# (it is OK to reuse an Assembler and call this several times in a sequence)
|
@@ -36,9 +42,8 @@ module Lignite
|
|
36
42
|
# @param rb_filename [String] input
|
37
43
|
# @param rbf_filename [String] output
|
38
44
|
def assemble(rb_filename, rbf_filename, version: 109)
|
45
|
+
initialize
|
39
46
|
rb_text = File.read(rb_filename)
|
40
|
-
@objects = []
|
41
|
-
@globals = Variables.new
|
42
47
|
|
43
48
|
@declarer = RbfDeclarer.new
|
44
49
|
@declarer.instance_eval(rb_text, rb_filename, 1) # 1 is the line number
|
@@ -47,7 +52,13 @@ module Lignite
|
|
47
52
|
write(rbf_filename, version)
|
48
53
|
end
|
49
54
|
|
50
|
-
def
|
55
|
+
def compile(&block)
|
56
|
+
@declarer = RbfDeclarer.new
|
57
|
+
@declarer.instance_exec(&block)
|
58
|
+
instance_exec(&block)
|
59
|
+
end
|
60
|
+
|
61
|
+
def write(rbf_filename, version = 109)
|
51
62
|
image_size = HEADER_SIZE + @objects.map(&:size).reduce(0, :+)
|
52
63
|
|
53
64
|
File.open(rbf_filename, "w") do |f|
|
@@ -99,4 +110,36 @@ module Lignite
|
|
99
110
|
local_bytes: @locals.bytesize)
|
100
111
|
end
|
101
112
|
end
|
113
|
+
|
114
|
+
# Acts like DirectCommands but instead of executing, assembles them to a RBF file
|
115
|
+
class SimpleAssembler
|
116
|
+
def initialize
|
117
|
+
@globals = Variables.new
|
118
|
+
@locals = Variables.new
|
119
|
+
@declarer = RbfDeclarer::Dummy.new
|
120
|
+
|
121
|
+
@interp = BodyCompiler.new(@globals, @locals, @declarer)
|
122
|
+
end
|
123
|
+
|
124
|
+
def write(rbf_filename)
|
125
|
+
@interp.object_end
|
126
|
+
vmthread = RbfObject.vmthread(body: @interp.bytes, local_bytes: @locals.bytesize)
|
127
|
+
|
128
|
+
asm = Assembler.new
|
129
|
+
asm.objects = [vmthread]
|
130
|
+
asm.globals = @globals
|
131
|
+
asm.write(rbf_filename)
|
132
|
+
end
|
133
|
+
|
134
|
+
# Delegate the ops to the {BodyCompiler},
|
135
|
+
def method_missing(name, *args, &block)
|
136
|
+
super unless @interp.respond_to?(name)
|
137
|
+
|
138
|
+
@interp.public_send(name, *args, &block)
|
139
|
+
end
|
140
|
+
|
141
|
+
def respond_to_missing?(name, _include_private)
|
142
|
+
@interp.respond_to?(name) || super
|
143
|
+
end
|
144
|
+
end
|
102
145
|
end
|
@@ -1,30 +1,4 @@
|
|
1
1
|
module Lignite
|
2
|
-
# Less-than (32 bit)
|
3
|
-
class Lt32 # < Condition
|
4
|
-
def initialize(a, b)
|
5
|
-
@a = a
|
6
|
-
@b = b
|
7
|
-
end
|
8
|
-
|
9
|
-
def not
|
10
|
-
Ge32.new(@a, @b)
|
11
|
-
end
|
12
|
-
|
13
|
-
def jump_forward(compiler, body_size)
|
14
|
-
compiler.jr_lt32(@a, @b, Complex(body_size, 2))
|
15
|
-
end
|
16
|
-
|
17
|
-
def jump_back(compiler, body_size, self_size = nil)
|
18
|
-
if self_size.nil?
|
19
|
-
fake = compiler.clone_context
|
20
|
-
jump_back(fake, body_size, 0)
|
21
|
-
self_size = fake.bytes.bytesize
|
22
|
-
end
|
23
|
-
|
24
|
-
compiler.jr_lt32(@a, @b, Complex(- (body_size + self_size), 2))
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
2
|
# Extends {OpCompiler} by
|
29
3
|
# - variable declarations: {VariableDeclarer}
|
30
4
|
# - high level flow control: {#loop}
|
@@ -62,28 +36,39 @@ module Lignite
|
|
62
36
|
BodyCompiler.new(@globals, @locals, @declared_objects)
|
63
37
|
end
|
64
38
|
|
65
|
-
def if(
|
66
|
-
|
39
|
+
def if(cond, &body)
|
40
|
+
cond = Flag.new(cond) unless cond.is_a? Condition
|
41
|
+
|
42
|
+
subc = clone_context
|
67
43
|
subc.instance_exec(&body)
|
68
44
|
|
69
|
-
|
45
|
+
cond.not.jump_forward(self, subc.bytes.bytesize)
|
70
46
|
@bytes << subc.bytes
|
71
47
|
end
|
72
48
|
|
49
|
+
def if_else(flag8, body_true, body_false)
|
50
|
+
truec = clone_context
|
51
|
+
falsec = clone_context
|
52
|
+
truec.instance_exec(&body_true)
|
53
|
+
falsec.instance_exec(&body_false)
|
54
|
+
|
55
|
+
# 4 is the unconditional jump size
|
56
|
+
jr_false(flag8, JumpOffset.new(truec.bytes.bytesize + 4))
|
57
|
+
@bytes << truec.bytes
|
58
|
+
jr(JumpOffset.new(falsec.bytes.bytesize))
|
59
|
+
@bytes << falsec.bytes
|
60
|
+
end
|
61
|
+
|
73
62
|
def loop(&body)
|
74
|
-
subc =
|
63
|
+
subc = clone_context
|
75
64
|
subc.instance_exec(&body)
|
76
65
|
@bytes << subc.bytes
|
77
66
|
# the jump takes up 4 bytes: JR, LC2, LO, HI
|
78
|
-
jr(
|
67
|
+
jr(JumpOffset.new(- (subc.bytes.bytesize + 4)))
|
79
68
|
end
|
80
69
|
|
81
70
|
def loop_while_postcond(flag8, &body)
|
82
|
-
|
83
|
-
subc.instance_exec(&body)
|
84
|
-
@bytes << subc.bytes
|
85
|
-
# the jump takes up 5 bytes: JR_TRUE, LV0(flag8), LC2, LO, HI
|
86
|
-
jr_true(flag8, Complex(- (subc.bytes.bytesize + 5), 2))
|
71
|
+
loop_while(body, Flag.new(flag8))
|
87
72
|
end
|
88
73
|
|
89
74
|
def loop_while(a, b)
|
@@ -95,18 +80,31 @@ module Lignite
|
|
95
80
|
end
|
96
81
|
|
97
82
|
def loop_while_post(condition, &body)
|
98
|
-
subc =
|
83
|
+
subc = clone_context
|
99
84
|
subc.instance_exec(&body)
|
100
85
|
@bytes << subc.bytes
|
101
86
|
body_size = subc.bytes.bytesize
|
102
87
|
condition.jump_back(self, body_size)
|
103
88
|
end
|
104
89
|
|
90
|
+
def loop_until_pre(condition, &body)
|
91
|
+
subc = clone_context
|
92
|
+
subc.instance_exec(&body)
|
93
|
+
ofs1 = @bytes.bytesize
|
94
|
+
condition.jump_forward(self, subc.bytes.bytesize + 4)
|
95
|
+
ofs2 = @bytes.bytesize
|
96
|
+
fw_jump_size = ofs2 - ofs1
|
97
|
+
@bytes << subc.bytes
|
98
|
+
jr(JumpOffset.new(-(fw_jump_size + subc.bytes.bytesize + 4)))
|
99
|
+
end
|
100
|
+
|
105
101
|
def call(name, *args)
|
106
102
|
obj_id = declared_objects.index_of(name)
|
107
103
|
raise "Name #{name} not found" if obj_id.nil?
|
108
104
|
|
109
105
|
# TODO: check that args match their declaration
|
106
|
+
# In particular, mixing up data32 with dataf passes the VM validity check
|
107
|
+
# but then misinterprets the bits.
|
110
108
|
super(obj_id, *args) # Ev3Ops::call
|
111
109
|
end
|
112
110
|
|