lignite 0.1.2 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 69c552fbf1d67d88480d8d1ffd3909ea41eebadf
4
- data.tar.gz: c53de990e8d76b4960bfa8157f5aabb14c652fbf
3
+ metadata.gz: 4ffc33346a586d1761204e68f028b383303cc742
4
+ data.tar.gz: a57b880c420ef54dac5ea3b0023293867f5e9bdf
5
5
  SHA512:
6
- metadata.gz: fb091ed264555daa9ea360e59cd3c27b097710d4d0f33105445a83d2640d8259a19f83e40cdd9625dd52588606a36a47f721384e2511b3ccf92b2dc5202b0fb2
7
- data.tar.gz: 865cb7e2dadc5614708fde9aa728f7b9af07b78c5e0de4ac1642480c344026d986356a4500980ed24f15e449fefc534fb6aa69dd5b650c74f1c0da8c469a5aa8
6
+ metadata.gz: 41357f5c2e71a45aef56e9de73a36b2010462417210b18ea9fbd3cd654889a0103ff1aaf82ebb81f29571fa88f0f4bf43020e7dc56ac1f5862147d6b7278dd2e
7
+ data.tar.gz: 8a278113af2c9a7500d4ca7774dcb5e99a4ce163ad24e2446eb06e0b7ae4baddae676bbd736741b2d33491b3f52c75ec9f6b0bb2e8be05eff4386cf053292535
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --files NEWS.md
data/NEWS.md ADDED
@@ -0,0 +1,16 @@
1
+ # News
2
+
3
+ ## unreleased
4
+
5
+ - (none)
6
+
7
+ ## 0.2.0, 2018-02-26
8
+
9
+ - DirectCommand can handle replies, allowing to read sensor data
10
+ - Motors, for easy DirectCommands to motors
11
+ - with an example program for a [Tea Serving Robot][tea], which is a standard [Bobb3e][]
12
+ - Assembler can compile global variables
13
+ - More control flow: `if`, `loop_while_postcond`
14
+
15
+ [Bobb3e]: https://www.lego.com/mindstorms/build-a-robot/bobb3e
16
+ [tea]: https://www.youtube.com/watch?v=0KOEvz09kkA
data/README.md CHANGED
@@ -41,9 +41,13 @@ section of the
41
41
  Running the built-in demo program: Connect the brick with a USB cable. Run
42
42
 
43
43
  ```sh
44
- sudo ev3tool run ../prjs/BrkProg_SAVE/Demo.rpf
44
+ sudo ev3tool run BrkProg_SAVE/Demo.rpf
45
45
  ```
46
46
 
47
+ In the usual case where the folder and the program name are the same, a
48
+ shortcut works: `ev3tool run everstorm` is like `ev3tool run
49
+ everstorm/everstorm.rbf`.
50
+
47
51
  > The `sudo` is needed to access the USB device.
48
52
  > With udev you can configure the system to allow access to all users like this:
49
53
  >
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.2
1
+ 0.2.0
@@ -0,0 +1,211 @@
1
+ #!/usr/bin/ruby
2
+ # coding: utf-8
3
+ # https://www.lego.com/mindstorms/build-a-robot/bobb3e
4
+ # A Bobcat® like two-tracked forklift
5
+
6
+ require "lignite"
7
+
8
+ class Bobbee
9
+ # @return [Lignite::Motors]
10
+ attr :drive
11
+ # @return [Lignite::Motors]
12
+ attr :lift
13
+ attr :dc
14
+
15
+ def initialize(drive: Lignite::PORT_B | Lignite::PORT_C,
16
+ lift: Lignite::PORT_A,
17
+ dc: Lignite::DirectCommands.new)
18
+ layer = 0
19
+ @drive = Lignite::Motors.new(layer, drive, dc)
20
+ @lift = Lignite::Motors.new(layer, lift, dc)
21
+ @dc = dc
22
+ end
23
+
24
+ # @param turn [Integer] -200..200
25
+ def time_sync(speed, turn, ms, brake = Lignite::BRAKE, wait: true)
26
+ # 1500 ms is about 1 boost square at speed 100
27
+ drive.time_sync(-speed, turn, ms, brake)
28
+ end
29
+
30
+ # @param turn [Integer] -200..200
31
+ 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
+ drive.step_sync(-speed, turn, degrees, brake)
41
+ drive.ready if wait
42
+ end
43
+
44
+ LIFT_FULL = 220
45
+
46
+ def raise(wait: true)
47
+ lift.step_power(30, 10, LIFT_FULL - 20, 10)
48
+ lift.ready if wait
49
+ end
50
+
51
+ def third_raise(wait: true)
52
+ lift.step_power(30, 10, LIFT_FULL/3 - 20, 10)
53
+ lift.ready if wait
54
+
55
+ beep
56
+ sleep 3
57
+ end
58
+
59
+ def lower(wait: true)
60
+ lift.step_power(-1, 10, LIFT_FULL - 20, 10) #, Lignite::COAST)
61
+ lift.ready if wait
62
+
63
+ beep
64
+ sleep 3
65
+ end
66
+
67
+ def third_lower(wait: true)
68
+ lift.step_power(-1, 10, LIFT_FULL/3 - 20, 10)
69
+ lift.ready if wait
70
+ end
71
+
72
+ def beep(ms = 300)
73
+ dc.sound_tone(20, 1760, ms)
74
+ dc.sound_ready
75
+ end
76
+
77
+ SQUARE_STEPS = 920
78
+ def f(steps = SQUARE_STEPS)
79
+ step_sync(50, 0, steps)
80
+ beep
81
+ sleep 3
82
+ end
83
+
84
+ def b(steps = SQUARE_STEPS)
85
+ step_sync(-50, 0, back_factor(steps))
86
+ beep
87
+ sleep 3
88
+ end
89
+
90
+ # it moves faster when going backwards!?
91
+ def back_factor(n)
92
+ (n * 0.95).to_i
93
+ end
94
+
95
+ ROTATION_OFFSET = 350
96
+ def f1
97
+ f(ROTATION_OFFSET)
98
+ beep(100)
99
+ end
100
+
101
+ TURN_90_AT_200_STEPS = 600
102
+ def l2
103
+ step_sync(50, -200, TURN_90_AT_200_STEPS)
104
+ beep(100)
105
+ sleep 3
106
+ end
107
+
108
+ def r2
109
+ step_sync(50, 200, TURN_90_AT_200_STEPS)
110
+ beep(100)
111
+ sleep 3
112
+ end
113
+
114
+ def f3
115
+ b(ROTATION_OFFSET)
116
+ beep(100)
117
+ end
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
125
+ end
126
+
127
+ def r
128
+ # a 3 part maneuver: forward, right, and back-a-little
129
+ # to turn 1 BS right
130
+ f1
131
+ r2
132
+ f3
133
+ end
134
+ end
135
+
136
+ bb = Bobbee.new
137
+ # 1 BS
138
+ # bb.time_sync(100, 0, 1500)
139
+
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
143
+ def from_twins_to_fire(bb)
144
+ bb.instance_exec do
145
+ beep
146
+
147
+ third_raise
148
+ third_raise
149
+ r
150
+ f
151
+ third_raise
152
+ b
153
+ l
154
+ # we're at the starting position, but carrying the load
155
+
156
+ # move towards the recipient
157
+ f; f; r
158
+
159
+ # deliver and unload
160
+ f
161
+ third_lower
162
+ b
163
+
164
+ # resting position
165
+ l; b; b
166
+ third_lower
167
+ third_lower
168
+ end
169
+ end
170
+
171
+ def from_fire_to_twins(bb)
172
+ bb.instance_exec do
173
+ beep
174
+
175
+ third_raise
176
+ third_raise
177
+
178
+ # move towards the load
179
+ f; f; r
180
+
181
+ # load
182
+ f
183
+ third_raise
184
+ b
185
+
186
+ # starting position
187
+ l; b; b
188
+
189
+ # deliver
190
+ r
191
+ f
192
+ third_lower
193
+ b
194
+ l
195
+
196
+ # resting position
197
+ third_lower
198
+ third_lower
199
+ end
200
+ end
201
+
202
+ def calibrate_forward_and_back(bb)
203
+ bb.instance_exec do
204
+ f; f; f; f
205
+ b; b; b; b
206
+ end
207
+ end
208
+
209
+ # calibrate_forward_and_back(bb)
210
+ from_twins_to_fire(bb)
211
+ # from_fire_to_twins(bb)
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/ruby
2
+ require "lignite"
3
+
4
+ dc = Lignite::DirectCommands.new
5
+
6
+ # see 28/31 of Comm dev kit, read light sensor value on port 3
7
+ LAYER0 = 0
8
+ MODE = 0
9
+ count = 1
10
+ pct = dc.with_reply do
11
+ # global vars
12
+ dataf :light
13
+
14
+ # TODO: make the block optional if there are no locals. or #with_locals
15
+ block do
16
+ input_device_ready_si(LAYER0, Lignite::PORT_3, Lignite::TYPE_KEEP, MODE, count, :light)
17
+ end
18
+ end
19
+ puts "Light sensor percentage: #{pct}"
data/examples/motors.rb CHANGED
@@ -15,5 +15,5 @@ dc.output_step_speed(layer, nos, speed,
15
15
  dc.output_ready(layer, nos)
16
16
 
17
17
  speed = -100
18
- dc.output_step_speed(layer, nos, speed,
19
- step_begin, step_do, step_end, brake)
18
+ m = Lignite::Motors.new(layer, nos, dc)
19
+ m.step_speed(speed, step_begin, step_do, step_end)
data/lib/lignite.rb CHANGED
@@ -9,6 +9,7 @@ require "lignite/connection/usb"
9
9
  require "lignite/direct_commands"
10
10
  require "lignite/message"
11
11
  require "lignite/message_sender"
12
+ require "lignite/motors"
12
13
  require "lignite/op_compiler"
13
14
  require "lignite/rbf_object"
14
15
  require "lignite/system_commands"
@@ -20,6 +21,11 @@ module Lignite
20
21
  PORT_C = 4
21
22
  PORT_D = 8
22
23
 
24
+ PORT_1 = 0
25
+ PORT_2 = 1
26
+ PORT_3 = 2
27
+ PORT_4 = 3
28
+
23
29
  COAST = 0
24
30
  BRAKE = 1
25
31
 
@@ -1,4 +1,7 @@
1
+ require "lignite/variables"
2
+
1
3
  module Lignite
4
+ # Assemble a complete RBF program file.
2
5
  class Assembler
3
6
  include Bytes
4
7
  include Logger
@@ -9,12 +12,22 @@ module Lignite
9
12
  u32(global_bytes)
10
13
  end
11
14
 
12
- def assemble(rb_filename, rbf_filename)
15
+ # @return [Array<RbfObject>]
16
+ attr :objects
17
+ # @return [Variables]
18
+ attr :globals
19
+
20
+ # Assemble a complete RBF program file.
21
+ # (it is OK to reuse an Assembler and call this several times in a sequence)
22
+ # TODO: redesign for Assembler.new(rb_filename).assemble(rbf_filename)?
23
+ # @param rb_filename [String] input
24
+ # @param rbf_filename [String] output
25
+ def assemble(rb_filename, rbf_filename, version: 109)
13
26
  rb_text = File.read(rb_filename)
14
27
  @objects = []
15
- @global_bytes = 0
28
+ @globals = Variables.new
16
29
 
17
- instance_eval(rb_text, rb_filename, 1)
30
+ instance_eval(rb_text, rb_filename, 1) # 1 is the line number
18
31
 
19
32
  File.open(rbf_filename, "w") do |f|
20
33
  dummy_header = image_header(image_size:0, version: 0, object_count: 0, global_bytes: 0)
@@ -28,18 +41,24 @@ module Lignite
28
41
  size = f.tell
29
42
  f.pos = 0
30
43
  header = image_header(image_size: size,
31
- version: 109,
44
+ version: version,
32
45
  object_count: @objects.size,
33
- global_bytes: @global_bytes)
46
+ global_bytes: @globals.bytesize)
34
47
  f.write(header)
35
48
  end
36
49
  end
37
50
 
51
+ def variables
52
+ globals
53
+ end
54
+ include VariableDeclarer
55
+
38
56
  def vmthread(id, &body)
39
57
  @locals = Variables.new
40
- bodyc = BodyCompiler.new(@locals)
58
+ bodyc = BodyCompiler.new(@globals, @locals)
41
59
  bodyc.instance_exec(&body)
42
60
  bodyc.instance_exec { object_end }
61
+ # FIXME: id is not written?!
43
62
  logger.debug "VMTHREAD #{id}"
44
63
  logger.debug " size #{bodyc.bytes.bytesize}"
45
64
  logger.debug " " + hexdump(bodyc.bytes)
@@ -3,36 +3,49 @@ module Lignite
3
3
  # - variable declarations: {VariableDeclarer}
4
4
  # - high level flow control: {#loop}
5
5
  class BodyCompiler
6
+ # @return [ByteString]
7
+ attr_reader :bytes
8
+ # @return [Variables]
9
+ attr_reader :locals
6
10
 
7
- # {#locals} are {Variables}
8
- module VariableDeclarer
9
- def data32(id)
10
- locals.add(id, 4)
11
- end
12
-
13
- def datas(id, size)
14
- locals.add(id, size)
15
- end
11
+ def variables
12
+ locals
16
13
  end
17
-
18
14
  include VariableDeclarer
19
- attr_reader :bytes
20
- attr_reader :locals
21
15
 
22
- def initialize(locals)
16
+ def initialize(globals, locals)
23
17
  @bytes = ""
18
+ @globals = globals
24
19
  @locals = locals
25
- @op_compiler = OpCompiler.new(nil, @locals)
20
+ @op_compiler = OpCompiler.new(@globals, @locals)
21
+ end
22
+
23
+ def if(flag8, &body)
24
+ subc = BodyCompiler.new(@globals, @locals)
25
+ subc.instance_exec(&body)
26
+
27
+ jr_false(flag8, Complex(subc.bytes.bytesize, 2))
28
+ @bytes << subc.bytes
26
29
  end
27
30
 
28
31
  def loop(&body)
29
- subc = BodyCompiler.new(@locals)
32
+ subc = BodyCompiler.new(@globals, @locals)
30
33
  subc.instance_exec(&body)
31
34
  @bytes << subc.bytes
32
35
  # the jump takes up 4 bytes: JR, LC2, LO, HI
33
36
  jr(Complex(- (subc.bytes.bytesize + 4), 2))
34
37
  end
35
38
 
39
+ def loop_while_postcond(flag8, &body)
40
+ subc = BodyCompiler.new(@globals, @locals)
41
+ subc.instance_exec(&body)
42
+ @bytes << subc.bytes
43
+ # the jump takes up 4 bytes: JR_TRUE, LV0(flag8), LC2, LO, HI
44
+ jr_true(flag8, Complex(- (subc.bytes.bytesize + 5), 2))
45
+ end
46
+
47
+ # Delegate the ops to the {OpCompiler},
48
+ # but also aggregate the result in @bytes.
36
49
  def method_missing(name, *args)
37
50
  super unless @op_compiler.respond_to?(name)
38
51
 
data/lib/lignite/bytes.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  module Lignite
2
+ # Shortcut methods to convert between data and their byte representation
2
3
  module Bytes
3
4
  def u8(n)
4
5
  (n & 0xff).chr
@@ -28,6 +29,10 @@ module Lignite
28
29
  s.unpack("L<").first
29
30
  end
30
31
 
32
+ def unpack_f32(s)
33
+ s.unpack("e").first
34
+ end
35
+
31
36
  def hexdump(s)
32
37
  s.unpack("H*").first
33
38
  end
@@ -1,17 +1,37 @@
1
1
  module Lignite
2
- # FIXME: cannot handle replies
3
2
  class DirectCommands
4
3
  # @param conn [Connection]
5
4
  def initialize(conn = Connection.create)
6
5
  @op_compiler = OpCompiler.new
7
6
  @sender = MessageSender.new(conn)
7
+ @globals = nil
8
+ end
9
+
10
+ def variables
11
+ @globals
12
+ end
13
+ include VariableDeclarer
14
+
15
+ def with_reply(&body)
16
+ @globals = Variables.new
17
+ ret_bytes = instance_exec(&body)
18
+ ret = @globals.unpack(ret_bytes)
19
+ @globals = nil
20
+ ret # TODO decode according to type
8
21
  end
9
22
 
10
23
  def block(&body)
11
24
  locals = Variables.new
12
- bodyc = BodyCompiler.new(locals)
25
+ bodyc = BodyCompiler.new(@globals, locals)
13
26
  bodyc.instance_exec(&body)
14
- @sender.direct_command(bodyc.bytes, local_size: locals.bytesize)
27
+
28
+ bs = bodyc.bytes
29
+ lsize = locals.bytesize
30
+ if @globals
31
+ @sender.direct_command_with_reply(bs, global_size: @globals.bytesize, local_size: lsize)
32
+ else
33
+ @sender.direct_command(bs, global_size: 0, local_size: lsize)
34
+ end
15
35
  end
16
36
 
17
37
  def method_missing(name, *args)
@@ -85,7 +85,7 @@ module Lignite
85
85
  end
86
86
 
87
87
  class DirectReply < Message
88
- def new(msgid:, error:, body:)
88
+ def initialize(msgid:, error:, body:)
89
89
  @msgid = msgid
90
90
  @error = error
91
91
  @globals = body
@@ -31,7 +31,7 @@ module Lignite
31
31
  raise VMError # no details?
32
32
  end
33
33
 
34
- reply.data
34
+ reply.globals
35
35
  end
36
36
 
37
37
  def system_command_with_reply(instr_bytes)
@@ -0,0 +1,165 @@
1
+ module Lignite
2
+ # Send {DirectCommands} to motors.
3
+ # It's called Motors instead of Motor because some methods are designed
4
+ # to work on a pair if motors driving a vehicle.
5
+ class Motors
6
+ # do the DCs spawn independent threads??
7
+ # must run ready in the same block?
8
+
9
+ attr :layer
10
+ attr :nos
11
+ # @return [Lignite::DirectCommands]
12
+ attr :dc
13
+
14
+ # 0x02 | 0x04 | 0x08 -> [1, 2, 3]
15
+ def nos_as_indices
16
+ [0, 1, 2, 3].find_all do |n|
17
+ (nos & (1 << n)) != 0
18
+ end
19
+ end
20
+
21
+ def initialize(layer, nos, dc = Lignite::DirectCommands.new)
22
+ @layer = layer
23
+ @nos = nos
24
+ @dc = dc
25
+ end
26
+
27
+ # TODO filter out support: official: no
28
+
29
+ # the type is an OUT param so the VM SETs and we GET to learn the type?
30
+ def set_type
31
+ layer = @layer
32
+ nos_as_indices.map do |no|
33
+ type = dc.with_reply do
34
+ data8 :type
35
+ block do
36
+ output_set_type(layer, no, :type)
37
+ end
38
+ end
39
+ type
40
+ end
41
+ end
42
+
43
+ # TODO: direct commands can do outputs? not yet
44
+
45
+ # ATTR tacho, like degrees
46
+ def reset
47
+ dc.output_reset(lay, nos) # tacho counts
48
+ end
49
+
50
+ # ATTR running
51
+ def stop(brake = Lignite::BRAKE)
52
+ dc.output_stop(layer, nos, brake)
53
+ end
54
+
55
+ # ATTR power ~ speed ?
56
+ def power(power)
57
+ dc.output_power(layer, nos, power) # does this start them also?
58
+ end
59
+
60
+ def speed(speed)
61
+ dc.output_speed(layer, nos, speed) # does this start them also?
62
+ end
63
+
64
+ def start
65
+ dc.output_start(layer, nos) # apparently not
66
+ end
67
+
68
+ # ATTR ~polarity
69
+ def polarity(pol)
70
+ dc.output_polarity(layer, nos, pol)
71
+ end
72
+
73
+ def read
74
+ layer = @layer
75
+ nos_as_indices.map do |no|
76
+ speed_tacho_pair = dc.with_reply do
77
+ data32 :tacho
78
+ data8 :speed
79
+ block do
80
+ output_read(layer, no, :speed, :tacho)
81
+ end
82
+ end
83
+ speed_tacho_pair
84
+ end
85
+ end
86
+
87
+ # ATTR running?
88
+ def test
89
+ layer = @layer
90
+ nos_as_indices.map do |no|
91
+ busy = dc.with_reply do
92
+ data8 :busy
93
+ block do
94
+ output_test(layer, no, :busy)
95
+ end
96
+ end
97
+ busy
98
+ end
99
+ end
100
+
101
+ # which commands are affected? not output_start they say
102
+ def ready
103
+ dc.output_ready(layer, nos)
104
+ end
105
+
106
+ # @param power [Integer] -100..100
107
+ # TODO better param protocol?
108
+ def step_power(power, deg1, deg2, deg3, brake = Lignite::BRAKE)
109
+ dc.output_step_power(layer, nos, power, deg1, deg2, deg3, brake)
110
+ end
111
+
112
+ # @param power [Integer] -100..100
113
+ # msec
114
+ def time_power(power, ms1, ms2, ms3, brake = Lignite::BRAKE)
115
+ dc.output_time_power(layer, nos, power, ms1, ms2, ms3, brake)
116
+ end
117
+
118
+ # @param speed [Integer] -100..100
119
+ # tachos
120
+ def step_speed(speed, deg1, deg2, deg3, brake = Lignite::BRAKE)
121
+ dc.output_step_speed(layer, nos, speed, deg1, deg2, deg3, brake)
122
+ end
123
+
124
+ # @param speed [Integer] -100..100
125
+ # msec
126
+ def time_speed(speed, ms1, ms2, ms3, brake = Lignite::BRAKE)
127
+ dc.output_time_speed(layer, nos, speed, ms1, ms2, ms3, brake)
128
+ end
129
+
130
+ # @param speed [Integer] -100..100
131
+ # @param turn [Integer] -200..200:
132
+ # 0 straight, 100 stops the right motor, -100 stops the left motor
133
+ # > 100 reverses the right motor, < -100 reverses the left motor
134
+ def step_sync(speed, turn, tachos, brake = Lignite::BRAKE)
135
+ dc.output_step_sync(layer, nos, speed, turn, tachos, brake)
136
+ end
137
+
138
+ def time_sync(speed, turn, ms, brake = Lignite::BRAKE)
139
+ dc.output_time_sync(layer, nos, speed, turn, ms, brake)
140
+ end
141
+
142
+ # zero tachos, for use as sensor
143
+ def clr_count
144
+ dc.output_clr_count(layer, nos)
145
+ end
146
+
147
+ def get_count
148
+ layer = @layer
149
+ nos_as_indices.map do |no|
150
+ tachos = dc.with_reply do
151
+ data32 :tachos
152
+ block do
153
+ output_get_count(layer, no, :tachos)
154
+ end
155
+ end
156
+ tachos
157
+ end
158
+ end
159
+
160
+ # WTF?
161
+ def prg_stop
162
+ dc.output_prg_stop
163
+ end
164
+ end
165
+ end
@@ -1,11 +1,21 @@
1
1
  require "yaml"
2
2
 
3
3
  module Lignite
4
+ # Dynamically constructs methods for all the instructions in ev3.yml
5
+ # The methods return the {ByteString}s corresponding to the ops.
4
6
  class OpCompiler
7
+ # TODO: doing it dynamically
8
+ # - is slow
9
+ # - makes the implementation harder to understand
10
+ # - means we cannot use YARD to document the API
11
+ # Therefore we should generate (most of) op_compiler.rb statically from
12
+ # ev3.yml ahead of the time.
13
+
5
14
  include Bytes
6
15
  include Logger
7
16
  extend Logger
8
17
 
18
+ # A marker for features that are not implemented yet
9
19
  class TODO < StandardError
10
20
  end
11
21
 
@@ -118,6 +128,8 @@ module Lignite
118
128
  @loaded = true
119
129
  end
120
130
 
131
+ # @param globals [Variables,nil]
132
+ # @param locals [Variables,nil]
121
133
  def initialize(globals = nil, locals = nil)
122
134
  self.class.load_yml
123
135
  @globals = globals
@@ -190,6 +202,8 @@ module Lignite
190
202
  n & 0xff, (n >> 8) & 0xff, (n >> 16) & 0xff, (n >> 24) & 0xff]
191
203
  end
192
204
 
205
+ # Reference a variable.
206
+ # (For declaring, see {VariableDeclarer}.)
193
207
  def make_var(sym)
194
208
  raise "No variables declared, cannot process symbols" if @locals.nil? && @globals.nil?
195
209
  if @locals.key?(sym)
@@ -209,6 +223,7 @@ module Lignite
209
223
  when Integer
210
224
  make_lc(x).map(&:chr).join("")
211
225
  when Complex
226
+ # a Complex number: real: just like an Integer above, but imag tells how many bytes to use for it
212
227
  make_lc(x.real, x.imag).map(&:chr).join("")
213
228
  when String
214
229
  u8(0x80) + x + u8(0x00)
@@ -2,18 +2,24 @@ module Lignite
2
2
  # Part of an assembled RBF file
3
3
  class RbfObject
4
4
  include Bytes
5
+ # @return [RbfObject] make a Vmthread object
5
6
  def self.vmthread(body:, local_bytes:)
6
7
  new(owner: 0, triggers: 0, local_bytes: local_bytes, body: body)
7
8
  end
8
9
 
10
+ # @return [RbfObject] make a Subcall object
9
11
  def self.subcall(body:, local_bytes:)
10
12
  new(owner: 0, triggers: 1, local_bytes: local_bytes, body: body)
11
13
  end
12
14
 
15
+ # @return [RbfObject] make a Block object
13
16
  def self.block(owner:, triggers:, body:)
14
17
  new(owner: owner, triggers: triggers, local_bytes: 0, body: body)
15
18
  end
16
19
 
20
+ # @return [ByteString] an already assembled body of the object
21
+ attr :body
22
+
17
23
  def initialize(owner:, triggers:, local_bytes:, body:)
18
24
  @owner = owner
19
25
  @triggers = triggers
@@ -25,9 +31,5 @@ module Lignite
25
31
  u32(pos_before_header + 12) + # size of header
26
32
  u16(@owner) + u16(@triggers) + u32(@local_bytes)
27
33
  end
28
-
29
- def body
30
- @body
31
- end
32
34
  end
33
35
  end
@@ -1,27 +1,70 @@
1
1
  module Lignite
2
2
  # Allocate local or global variables
3
+ # FIXME: the user can make bad alignment, resulting in VMError at runtime
4
+ # bad: data8 :speed; data32 :tacho; output_read(...)
5
+ # good data32 :tacho; data8 :speed; output_read(...)
3
6
  class Variables
4
7
  def initialize
5
8
  @offset = 0
9
+ # for proper decoding of direct replies according to declared types
10
+ @unpacker = ""
6
11
  @vars = {}
7
12
  end
8
13
 
9
- def add(id, size)
14
+ # declare
15
+ def add(id, size, unpacker)
10
16
  raise "Duplicate variable #{id}" if @vars.key?(id)
11
17
  @vars[id] = {offset: @offset, size: size}
12
18
  @offset += size
19
+ @unpacker += unpacker
13
20
  end
14
21
 
15
- def bytesize
16
- @offset
17
- end
18
-
22
+ # use
19
23
  def key?(sym)
20
24
  @vars.key?(sym)
21
25
  end
22
26
 
27
+ # use
23
28
  def offset(sym)
24
29
  @vars[sym][:offset]
25
30
  end
31
+
32
+ # compile
33
+ def bytesize
34
+ @offset
35
+ end
36
+
37
+ # decode reply
38
+ def unpack(buf)
39
+ values = buf.unpack(@unpacker)
40
+ values.size == 1 ? values.first : values
41
+ end
42
+ end
43
+
44
+ # {#variables} are {Variables}
45
+ module VariableDeclarer
46
+ def data8(id)
47
+ variables.add(id, 1, "C")
48
+ end
49
+
50
+ def data16(id)
51
+ variables.add(id, 2, "S<")
52
+ end
53
+
54
+ def data32(id)
55
+ variables.add(id, 4, "L<")
56
+ end
57
+
58
+ def dataf(id)
59
+ variables.add(id, 4, "e")
60
+ end
61
+
62
+ def datas(id, size)
63
+ variables.add(id, size, "a#{size}")
64
+ end
65
+
66
+ def array8(id, count)
67
+ variables.add(id, count * 1, "C#{count}")
68
+ end
26
69
  end
27
70
  end
data/lignite.gemspec CHANGED
@@ -16,13 +16,15 @@ TXT
16
16
  s.homepage = "https://github.com/mvidner/lignite"
17
17
  s.license = "GPL-3.0-only"
18
18
 
19
- # ruby -e 'puts `git ls-files`.lines.map { |f| " %s,\n" % f.strip.inspect }'
19
+ # ruby -e 'puts `git ls-files`.lines.map { |f| " %s" % f.strip.inspect }.join ",\n"'
20
20
  s.files = [
21
21
  ".coveralls.yml",
22
22
  ".gitignore",
23
23
  ".travis.yml",
24
+ ".yardopts",
24
25
  "COPYING",
25
26
  "Gemfile",
27
+ "NEWS.md",
26
28
  "README.md",
27
29
  "Rakefile",
28
30
  "VERSION",
@@ -30,7 +32,9 @@ TXT
30
32
  "data/ev3.yml",
31
33
  "data/lignite-btaddr",
32
34
  "data/sysops.yml",
35
+ "examples/bobbee.rb",
33
36
  "examples/hello.rb",
37
+ "examples/light-sensor.rb",
34
38
  "examples/lights.rb",
35
39
  "examples/motors.rb",
36
40
  "examples/sound.rb",
@@ -46,6 +50,7 @@ TXT
46
50
  "lib/lignite/logger.rb",
47
51
  "lib/lignite/message.rb",
48
52
  "lib/lignite/message_sender.rb",
53
+ "lib/lignite/motors.rb",
49
54
  "lib/lignite/op_compiler.rb",
50
55
  "lib/lignite/rbf_object.rb",
51
56
  "lib/lignite/system_commands.rb",
@@ -53,11 +58,17 @@ TXT
53
58
  "lib/lignite/version.rb",
54
59
  "lignite.gemspec",
55
60
  "spec/assembler_spec.rb",
61
+ "spec/data/ColorReadout.lms",
62
+ "spec/data/ColorReadout.rb",
63
+ "spec/data/ColorReadout.rbf",
56
64
  "spec/data/HelloWorld-subop.rb",
57
65
  "spec/data/HelloWorld-subop.rbf",
58
66
  "spec/data/HelloWorld.lms",
59
67
  "spec/data/HelloWorld.rb",
60
68
  "spec/data/HelloWorld.rbf",
69
+ "spec/data/NoDebug.lms",
70
+ "spec/data/NoDebug.rb",
71
+ "spec/data/NoDebug.rbf",
61
72
  "spec/data/VernierReadout.lms",
62
73
  "spec/data/VernierReadout.rb",
63
74
  "spec/data/VernierReadout.rbf",
@@ -4,7 +4,7 @@ require "lignite"
4
4
  require "fileutils"
5
5
 
6
6
  describe Lignite::Assembler do
7
- shared_examples "compiles" do |base|
7
+ shared_examples "compiles" do |base, asm_version|
8
8
  let(:datadir) { File.expand_path("../data", __FILE__) }
9
9
 
10
10
  let(:in_rb) { "#{datadir}/#{base}.rb" }
@@ -12,13 +12,15 @@ describe Lignite::Assembler do
12
12
  let(:expected_rbf) { "#{datadir}/#{base}.rbf" }
13
13
 
14
14
  it "correctly assembles #{base}" do
15
- subject.assemble(in_rb, actual_rbf)
15
+ subject.assemble(in_rb, actual_rbf, version: asm_version)
16
16
  expect(FileUtils.compare_file(expected_rbf, actual_rbf)).to be_truthy
17
17
  FileUtils.rm actual_rbf
18
18
  end
19
19
  end
20
20
 
21
- include_examples "compiles", "HelloWorld"
22
- include_examples "compiles", "HelloWorld-subop"
23
- include_examples "compiles", "VernierReadout"
21
+ it_behaves_like "compiles", "HelloWorld", 109
22
+ it_behaves_like "compiles", "HelloWorld-subop", 109
23
+ it_behaves_like "compiles", "VernierReadout", 109
24
+ it_behaves_like "compiles", "ColorReadout", 106
25
+ it_behaves_like "compiles", "NoDebug", 109
24
26
  end
@@ -0,0 +1,75 @@
1
+ define PORT 1
2
+
3
+ DATA16 Cmd
4
+ DATA8 Type
5
+ DATA8 Mode
6
+ ARRAY8 Data 6
7
+
8
+ vmthread MAIN
9
+ {
10
+ DATA32 Timer
11
+ DATA32 Data1
12
+ DATA32 Data2
13
+ DATA32 Data3
14
+ DATAS String 8
15
+ DATA8 Mode
16
+ DATA8 State
17
+
18
+
19
+ MOVE8_8(3,Mode)
20
+
21
+ Loop:
22
+
23
+ UI_DRAW(FILLWINDOW,0x00,0,0)
24
+ UI_DRAW(SELECT_FONT,SMALL_FONT)
25
+ UI_DRAW(TEXT,FG_COLOR,0,10,' Color Readout V0.00 ')
26
+ UI_DRAW(TEXT,FG_COLOR,0,30,'Mode ')
27
+
28
+ UI_DRAW(TEXT,FG_COLOR,0,50,'Raw 1')
29
+ UI_DRAW(TEXT,FG_COLOR,0,65,'Raw 2')
30
+ UI_DRAW(TEXT,FG_COLOR,0,80,'Raw 3')
31
+ UI_DRAW(TEXT,FG_COLOR,0,118,'Left = 3 Right = 4')
32
+ UI_DRAW(SELECT_FONT,NORMAL_FONT)
33
+
34
+ MOVE8_32(Mode,Data1)
35
+ AND32(Data1,0xFF,Data1)
36
+ STRINGS(NUMBER_FORMATTED,Data1,'%1d',8,String)
37
+ UI_DRAW(TEXT,FG_COLOR,64,30,String)
38
+
39
+ INPUT_DEVICE(READY_RAW,0,PORT,0,Mode,3,Data1,Data2,Data3)
40
+
41
+ AND32(Data1,0xFFFF,Data1)
42
+ STRINGS(NUMBER_FORMATTED,Data1,'%-5d',8,String)
43
+ UI_DRAW(TEXT,FG_COLOR,64,50,String)
44
+
45
+ AND32(Data2,0xFFFF,Data2)
46
+ STRINGS(NUMBER_FORMATTED,Data2,'%-5d',8,String)
47
+ UI_DRAW(TEXT,FG_COLOR,64,65,String)
48
+
49
+ AND32(Data3,0xFFFF,Data3)
50
+ STRINGS(NUMBER_FORMATTED,Data3,'%-5d',8,String)
51
+ UI_DRAW(TEXT,FG_COLOR,64,80,String)
52
+
53
+ UI_DRAW(UPDATE)
54
+
55
+
56
+ UI_BUTTON(SHORTPRESS,LEFT_BUTTON,State)
57
+ JR_FALSE(State,Not3)
58
+
59
+ MOVE8_8(3,Mode)
60
+
61
+ Not3:
62
+
63
+ UI_BUTTON(SHORTPRESS,RIGHT_BUTTON,State)
64
+ JR_FALSE(State,Not4)
65
+
66
+ MOVE8_8(4,Mode)
67
+
68
+ Not4:
69
+
70
+ TIMER_WAIT(250,Timer)
71
+ TIMER_READY(Timer)
72
+
73
+ JR(Loop)
74
+ }
75
+
@@ -0,0 +1,67 @@
1
+ PORT = 1
2
+
3
+ # global vars - unused?!
4
+ data16 :cmd
5
+ data8 :type
6
+ data8 :mode
7
+ array8 :data, 6
8
+
9
+ vmthread :MAIN do
10
+ data32 :Timer
11
+ data32 :Data1
12
+ data32 :Data2
13
+ data32 :Data3
14
+ datas :String, 8
15
+ data8 :Mode
16
+ data8 :State
17
+
18
+
19
+ move8_8(3, :Mode)
20
+
21
+ self.loop do
22
+ ui_draw(FILLWINDOW, 0x00, 0, 0)
23
+ ui_draw(SELECT_FONT, SMALL_FONT)
24
+ ui_draw(TEXT, FG_COLOR, 0, 10, ' Color Readout V0.00 ')
25
+ ui_draw(TEXT, FG_COLOR, 0, 30, 'Mode ')
26
+
27
+ ui_draw(TEXT, FG_COLOR, 0, 50, 'Raw 1')
28
+ ui_draw(TEXT, FG_COLOR, 0, 65, 'Raw 2')
29
+ ui_draw(TEXT, FG_COLOR, 0, 80, 'Raw 3')
30
+ ui_draw(TEXT, FG_COLOR, 0, 118, 'Left = 3 Right = 4')
31
+ ui_draw(SELECT_FONT, NORMAL_FONT)
32
+
33
+ move8_32(:Mode, :Data1)
34
+ and32(:Data1, 0xFF, :Data1)
35
+ strings(NUMBER_FORMATTED, :Data1, '%1d', 8, :String)
36
+ ui_draw(TEXT, FG_COLOR, 64, 30, :String)
37
+
38
+ input_device(READY_RAW, 0, PORT, 0, :Mode, 3, :Data1, :Data2, :Data3)
39
+
40
+ and32(:Data1, 0xFFFF, :Data1)
41
+ strings(NUMBER_FORMATTED, :Data1, '%-5d', 8, :String)
42
+ ui_draw(TEXT, FG_COLOR, 64, 50, :String)
43
+
44
+ and32(:Data2, 0xFFFF, :Data2)
45
+ strings(NUMBER_FORMATTED, :Data2, '%-5d', 8, :String)
46
+ ui_draw(TEXT, FG_COLOR, 64, 65, :String)
47
+
48
+ and32(:Data3, 0xFFFF, :Data3)
49
+ strings(NUMBER_FORMATTED, :Data3, '%-5d', 8, :String)
50
+ ui_draw(TEXT, FG_COLOR, 64, 80, :String)
51
+
52
+ ui_draw(UPDATE)
53
+
54
+ ui_button(SHORTPRESS, LEFT_BUTTON, :State)
55
+ self.if(:State) do
56
+ move8_8(3, :Mode)
57
+ end
58
+
59
+ ui_button(SHORTPRESS, RIGHT_BUTTON, :State)
60
+ self.if(:State) do
61
+ move8_8(4, :Mode)
62
+ end
63
+
64
+ timer_wait(250, :Timer)
65
+ timer_ready(:Timer)
66
+ end
67
+ end
Binary file
@@ -0,0 +1,62 @@
1
+ /*
2
+ * LEGO® MINDSTORMS EV3
3
+ *
4
+ * Copyright (C) 2010-2013 The LEGO Group
5
+ *
6
+ * This program is free software; you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation; either version 2 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * This program is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with this program; if not, write to the Free Software
18
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
+ */
20
+
21
+
22
+ // TCP 08.04.2013
23
+ //! \page testnodebugappcode NoDebug Application
24
+ //!
25
+ //! <hr size="1"/>
26
+ //!
27
+ //! \verbatim
28
+ //**********************************************************************
29
+ define appv 'NoDebug V1.02' //
30
+ //**********************************************************************
31
+ //
32
+ //
33
+ vmthread MAIN // void MAIN(void)
34
+ { // {
35
+ DATA8 ShowVersion //
36
+ //
37
+ UI_BUTTON(PRESSED,RIGHT_BUTTON,ShowVersion) // UI_BUTTON(PRESSED,RIGHT_BUTTON,ShowVersion)
38
+ JR_FALSE(ShowVersion,DontShowVersion) // if (ShowVersion)
39
+ // {
40
+ UI_DRAW(FILLRECT,BG_COLOR,4,50,170,28) // UI_DRAW(FILLRECT,BG_COLOR,4,50,170,28)
41
+ UI_DRAW(RECT,FG_COLOR,6,52,166,24) // UI_DRAW(RECT,FG_COLOR,6,52,166,24)
42
+ UI_DRAW(TEXT,FG_COLOR,13,60,appv) // UI_DRAW(TEXT,FG_COLOR,13,60,appv)
43
+ UI_DRAW(UPDATE) // UI_DRAW(UPDATE)
44
+ //
45
+ ShowVersionWait: // do
46
+ // {
47
+ UI_BUTTON(PRESSED,RIGHT_BUTTON,ShowVersion) // UI_BUTTON(PRESSED,RIGHT_BUTTON,ShowVersion)
48
+ // }
49
+ JR_TRUE(ShowVersion,ShowVersionWait) // while (ShowVersion)
50
+ //
51
+ UI_BUTTON(FLUSH) // UI_BUTTON(FLUSH)
52
+ DontShowVersion: // }
53
+ //
54
+ UI_DRAW(RESTORE,0) // UI_DRAW(RESTORE,0)
55
+ UI_DRAW(TOPLINE,1) // UI_DRAW(TOPLINE,1)
56
+ UI_WRITE(LED,LED_GREEN) // UI_WRITE(LED,LED_GREEN)
57
+ //
58
+ UI_WRITE(TERMINAL,0) // UI_WRITE(TERMINAL,0)
59
+ } // }
60
+ //
61
+ //
62
+ //! \endverbatim
@@ -0,0 +1,25 @@
1
+ appv = 'NoDebug V1.02'
2
+
3
+ vmthread :MAIN do
4
+ data8 :ShowVersion
5
+
6
+ ui_button(PRESSED, RIGHT_BUTTON, :ShowVersion)
7
+ self.if :ShowVersion do
8
+ ui_draw(FILLRECT, BG_COLOR, 4, 50, 170, 28)
9
+ ui_draw(RECT, FG_COLOR, 6, 52, 166, 24)
10
+ ui_draw(TEXT, FG_COLOR, 13, 60, appv)
11
+ ui_draw(UPDATE)
12
+
13
+ loop_while_postcond(:ShowVersion) do
14
+ ui_button(PRESSED, RIGHT_BUTTON, :ShowVersion)
15
+ end
16
+
17
+ ui_button(FLUSH)
18
+ end
19
+
20
+ ui_draw(RESTORE, 0)
21
+ ui_draw(TOPLINE, 1)
22
+ ui_write(LED, LED_GREEN)
23
+
24
+ ui_write(TERMINAL, 0)
25
+ end
Binary file
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lignite
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martin Vidner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-02-20 00:00:00.000000000 Z
11
+ date: 2018-02-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: libusb
@@ -112,8 +112,10 @@ files:
112
112
  - ".coveralls.yml"
113
113
  - ".gitignore"
114
114
  - ".travis.yml"
115
+ - ".yardopts"
115
116
  - COPYING
116
117
  - Gemfile
118
+ - NEWS.md
117
119
  - README.md
118
120
  - Rakefile
119
121
  - VERSION
@@ -121,7 +123,9 @@ files:
121
123
  - data/ev3.yml
122
124
  - data/lignite-btaddr
123
125
  - data/sysops.yml
126
+ - examples/bobbee.rb
124
127
  - examples/hello.rb
128
+ - examples/light-sensor.rb
125
129
  - examples/lights.rb
126
130
  - examples/motors.rb
127
131
  - examples/sound.rb
@@ -137,6 +141,7 @@ files:
137
141
  - lib/lignite/logger.rb
138
142
  - lib/lignite/message.rb
139
143
  - lib/lignite/message_sender.rb
144
+ - lib/lignite/motors.rb
140
145
  - lib/lignite/op_compiler.rb
141
146
  - lib/lignite/rbf_object.rb
142
147
  - lib/lignite/system_commands.rb
@@ -144,11 +149,17 @@ files:
144
149
  - lib/lignite/version.rb
145
150
  - lignite.gemspec
146
151
  - spec/assembler_spec.rb
152
+ - spec/data/ColorReadout.lms
153
+ - spec/data/ColorReadout.rb
154
+ - spec/data/ColorReadout.rbf
147
155
  - spec/data/HelloWorld-subop.rb
148
156
  - spec/data/HelloWorld-subop.rbf
149
157
  - spec/data/HelloWorld.lms
150
158
  - spec/data/HelloWorld.rb
151
159
  - spec/data/HelloWorld.rbf
160
+ - spec/data/NoDebug.lms
161
+ - spec/data/NoDebug.rb
162
+ - spec/data/NoDebug.rbf
152
163
  - spec/data/VernierReadout.lms
153
164
  - spec/data/VernierReadout.rb
154
165
  - spec/data/VernierReadout.rbf