lego-nxt 0.2.0 → 0.3.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 +5 -5
- data/README.md +16 -12
- data/Rakefile +9 -12
- data/lib/lego_nxt.rb +36 -0
- data/lib/nxt/commands/base.rb +56 -7
- data/lib/nxt/commands/input.rb +30 -12
- data/lib/nxt/commands/low_speed.rb +47 -0
- data/lib/nxt/commands/output.rb +9 -7
- data/lib/nxt/commands/program.rb +3 -0
- data/lib/nxt/commands/sound.rb +3 -0
- data/lib/nxt/commands/tone.rb +3 -0
- data/lib/nxt/connector/input/base.rb +9 -0
- data/lib/nxt/{connectors → connector}/input/color.rb +3 -0
- data/lib/nxt/{connectors → connector}/input/touch.rb +3 -0
- data/lib/nxt/connector/input/ultrasonic.rb +66 -0
- data/lib/nxt/connector/output/base.rb +9 -0
- data/lib/nxt/connector/output/motor.rb +117 -0
- data/lib/nxt/exceptions.rb +3 -1
- data/lib/nxt/interface/base.rb +18 -0
- data/lib/nxt/{interfaces → interface}/serial_port.rb +13 -10
- data/lib/nxt/{interfaces → interface}/usb.rb +10 -9
- data/lib/nxt/nxt_brick.rb +51 -45
- data/lib/nxt/patches/module.rb +5 -2
- data/lib/nxt/patches/string.rb +6 -17
- data/lib/nxt/protocols/i2c.rb +118 -0
- data/lib/nxt/utils/accessors.rb +8 -7
- data/lib/nxt/utils/assertions.rb +24 -0
- data/spec/matchers.rb +2 -0
- data/spec/nxt/connector/output/motor_spec.rb +52 -0
- data/spec/nxt/interface/serial_port_spec.rb +119 -0
- data/spec/nxt/nxt_brick_spec.rb +189 -120
- data/spec/spec_helper.rb +10 -1
- metadata +193 -59
- data/lib/nxt.rb +0 -27
- data/lib/nxt/connectors/input/ultrasonic.rb +0 -11
- data/lib/nxt/connectors/output/motor.rb +0 -116
- data/lib/nxt/interfaces/base.rb +0 -26
- data/spec/nxt/interfaces/serial_port_spec.rb +0 -73
data/lib/nxt/patches/string.rb
CHANGED
@@ -1,29 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Some patches that extend the default Ruby String class with some useful
|
4
|
+
# methods.
|
1
5
|
class String
|
2
6
|
# Convert the given string to a hexadecimal representation of the same data.
|
3
7
|
# This method is non-destructive, it will return a new copy of the string
|
4
8
|
# convered to hex.
|
5
9
|
def to_hex_str
|
6
10
|
str = ''
|
7
|
-
|
8
|
-
str
|
9
|
-
end
|
10
|
-
|
11
|
-
#
|
12
|
-
def from_hex_str_two
|
13
|
-
data = self.split(' ')
|
14
|
-
str = ''
|
15
|
-
data.each {|h| eval "str += '%c' % #{h}"}
|
11
|
+
each_byte { |b| str << format('0x%02x ', b) }
|
16
12
|
str
|
17
13
|
end
|
18
14
|
|
19
15
|
def from_hex_str
|
20
|
-
|
21
|
-
str = ''
|
22
|
-
|
23
|
-
data.each do |h|
|
24
|
-
str += '%c' % h
|
25
|
-
end
|
26
|
-
|
27
|
-
str
|
16
|
+
unpack('C*')
|
28
17
|
end
|
29
18
|
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NXT
|
4
|
+
module Protocols
|
5
|
+
# Communication to the NXT brick for digital sensors is done using the I2C
|
6
|
+
# protocol. This module implements communication in that fashion,
|
7
|
+
# abstracing the messages
|
8
|
+
# we wish to send from the underlying protocol for ease-of-use.
|
9
|
+
module I2C
|
10
|
+
COMMAND_IDENTIFIER = {
|
11
|
+
ls_get_status: 0x0E,
|
12
|
+
ls_write: 0x0F,
|
13
|
+
ls_read: 0x10
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
I2C_HEADER = 0x02
|
17
|
+
|
18
|
+
# Format is I2C address, followed by the number of bytes expected in the response.
|
19
|
+
I2C_CONSTANT_OPS = {
|
20
|
+
read_version: [0x00, 8],
|
21
|
+
read_product_id: [0x08, 8],
|
22
|
+
read_sensor_type: [0x10, 8],
|
23
|
+
read_factory_zero: [0x11, 1],
|
24
|
+
read_factory_scale_factor: [0x12, 1],
|
25
|
+
read_factory_scale_divisor: [0x13, 1],
|
26
|
+
read_measurement_units: [0x14, 7]
|
27
|
+
}.freeze
|
28
|
+
|
29
|
+
# Format is the I2C address (all variable operations expect a 1 byte reponse).
|
30
|
+
I2C_VARIABLE_OPS = {
|
31
|
+
read_continuous_measurements_interval: 0x40,
|
32
|
+
read_command_state: 0x41,
|
33
|
+
read_measurement_byte_zero: 0x42,
|
34
|
+
read_measurement_byte_one: 0x43,
|
35
|
+
read_measurement_byte_two: 0x44,
|
36
|
+
read_measurement_byte_three: 0x45,
|
37
|
+
read_measurement_byte_four: 0x46,
|
38
|
+
read_measurement_byte_five: 0x47,
|
39
|
+
read_measurement_byte_six: 0x48,
|
40
|
+
read_measurement_byte_seven: 0x49,
|
41
|
+
read_actual_zero: 0x50,
|
42
|
+
read_actual_scale_factor: 0x51,
|
43
|
+
read_actual_scale_divisor: 0x52
|
44
|
+
}.freeze
|
45
|
+
|
46
|
+
# Format is I2C address, followed by the command. If the array is only
|
47
|
+
# one member large, then the second byte is instead a value passed at
|
48
|
+
# run-time, eg. the length of the interval to run the continuous
|
49
|
+
# measuring on the ultrasonic sensor for.
|
50
|
+
I2C_COMMANDS = {
|
51
|
+
off_command: [0x41, 0x00],
|
52
|
+
single_shot_command: [0x41, 0x01],
|
53
|
+
continuous_measurement_command: [0x41, 0x02],
|
54
|
+
event_capture_command: [0x41, 0x03],
|
55
|
+
request_warm_reset: [0x41, 0x04],
|
56
|
+
set_continuous_measurement_interval: [0x40],
|
57
|
+
set_actual_zero: [0x50],
|
58
|
+
set_actual_scale_factor: [0x51],
|
59
|
+
set_actual_scale_divisor: [0x52]
|
60
|
+
}.freeze
|
61
|
+
|
62
|
+
def self.method_missing(name, *args)
|
63
|
+
if I2C_CONSTANT_OPS.key?(name)
|
64
|
+
run_constant_op(name)
|
65
|
+
elsif I2C_VARIABLE_OPS.key?(name)
|
66
|
+
run_variable_op(name)
|
67
|
+
elsif I2C_COMMANDS.key?(name)
|
68
|
+
run_command(name, *args)
|
69
|
+
else
|
70
|
+
super
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.respond_to_missing?(name, include_private = false)
|
75
|
+
I2C_CONSTANT_OPS.key?(name) ||
|
76
|
+
I2C_VARIABLE_OPS.key?(name) ||
|
77
|
+
I2C_COMMANDS.key?(name) ||
|
78
|
+
super
|
79
|
+
end
|
80
|
+
|
81
|
+
class << self
|
82
|
+
private
|
83
|
+
|
84
|
+
def run_constant_op(name)
|
85
|
+
op = I2C_CONSTANT_OPS[name]
|
86
|
+
addr = op[0]
|
87
|
+
rx_len = op[1]
|
88
|
+
|
89
|
+
[0, rx_len, I2C_HEADER, addr]
|
90
|
+
end
|
91
|
+
|
92
|
+
def run_variable_op(name)
|
93
|
+
op = I2C_VARIABLE_OPS[name]
|
94
|
+
addr = op
|
95
|
+
rx_len = 1
|
96
|
+
|
97
|
+
[0, rx_len, I2C_HEADER, addr]
|
98
|
+
end
|
99
|
+
|
100
|
+
def run_command(name, *args)
|
101
|
+
op = I2C_COMMANDS[name]
|
102
|
+
addr = op[0]
|
103
|
+
rx_len = 0
|
104
|
+
|
105
|
+
if op[1]
|
106
|
+
data = [op[1]]
|
107
|
+
elsif args[0]
|
108
|
+
data = [args[0]]
|
109
|
+
else
|
110
|
+
raise "Missing argument for command #{name}"
|
111
|
+
end
|
112
|
+
|
113
|
+
[data.size, rx_len, I2C_HEADER, addr] + data
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
data/lib/nxt/utils/accessors.rb
CHANGED
@@ -1,15 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module NXT
|
2
4
|
module Utils
|
5
|
+
# Utilities related to creating accessors that have some conditonal guards
|
6
|
+
# when setting values.
|
3
7
|
module Accessors
|
8
|
+
include NXT::Utils::Assertions
|
9
|
+
|
4
10
|
def attr_setter(name, options)
|
5
11
|
define_method("#{name}=") do |value|
|
6
|
-
if options.include?(:is)
|
7
|
-
|
8
|
-
end
|
9
|
-
|
10
|
-
if options.include?(:is_key_in) && !options[:is_key_in].include?(value)
|
11
|
-
raise TypeError.new("Expected value to be one of: :#{options[:is_key_in].keys.join(', :')}")
|
12
|
-
end
|
12
|
+
assert_type(name, value, options[:is]) if options.include?(:is)
|
13
|
+
assert_in(name, value, options[:is_key_in]) if options.include?(:is_key_in)
|
13
14
|
|
14
15
|
instance_variable_set("@#{name}", value)
|
15
16
|
self
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NXT
|
4
|
+
module Utils
|
5
|
+
# Utilities related to asserting values are what is expected.
|
6
|
+
module Assertions
|
7
|
+
def assert_in(name, value, values)
|
8
|
+
raise(TypeError, "Expected #{name} to be one of: :#{values.join(', :')}") unless values.include?(value)
|
9
|
+
end
|
10
|
+
|
11
|
+
def assert_type(name, value, type)
|
12
|
+
raise(TypeError, "Expected #{name} to be of type #{type}") unless value.is_a?(type)
|
13
|
+
end
|
14
|
+
|
15
|
+
def assert_responds_to(name, value, *methods)
|
16
|
+
valid = methods.all? do |method|
|
17
|
+
value.respond_to?(method)
|
18
|
+
end
|
19
|
+
|
20
|
+
raise(TypeError, "Expected #{name} to respond to: #{methods.join(', ')}") unless valid
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/spec/matchers.rb
CHANGED
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe NXT::Connector::Output::Motor do
|
6
|
+
subject(:motor) { described_class.new(port, interface) }
|
7
|
+
|
8
|
+
let(:port) { :a }
|
9
|
+
let(:interface) { nil }
|
10
|
+
|
11
|
+
describe 'constants' do
|
12
|
+
it 'has a DURATION_TYPE constant' do
|
13
|
+
expect(motor).to have_constant(:DURATION_TYPE)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'has a DURATION_AFTER constant' do
|
17
|
+
expect(motor).to have_constant(:DURATION_AFTER)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'has a DIRECTION constant' do
|
21
|
+
expect(motor).to have_constant(:DIRECTION)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe 'accessors' do
|
26
|
+
it 'has read accessor for @port' do
|
27
|
+
expect(motor).to respond_to(:port)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'has write accessor for @port' do
|
31
|
+
expect(motor).to respond_to(:port=)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'has read accessor for @interface' do
|
35
|
+
expect(motor).to respond_to(:interface)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'has write accessor for @interface' do
|
39
|
+
expect(motor).to respond_to(:interface=)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#initialize' do
|
44
|
+
it 'sets the port to the incomming argument' do
|
45
|
+
expect(motor.port).to equal(port)
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'sets the interface to the incomming argument' do
|
49
|
+
expect(motor.interface).to equal(interface)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe NXT::Interface::SerialPort do
|
6
|
+
subject(:serial_port) { described_class.new(device) }
|
7
|
+
|
8
|
+
let(:device) { '/dev/zero' }
|
9
|
+
|
10
|
+
describe 'constants' do
|
11
|
+
it 'has a BAUD_RATE constant' do
|
12
|
+
expect(serial_port).to have_constant(:BAUD_RATE)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'has a DATA_BITS constant' do
|
16
|
+
expect(serial_port).to have_constant(:DATA_BITS)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'has a STOP_BITS constant' do
|
20
|
+
expect(serial_port).to have_constant(:STOP_BITS)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'has a PARITY constant' do
|
24
|
+
expect(serial_port).to have_constant(:PARITY)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'has a READ_TIMEOUT constant' do
|
28
|
+
expect(serial_port).to have_constant(:READ_TIMEOUT)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe 'accessors' do
|
33
|
+
it 'has read accessor for @dev' do
|
34
|
+
expect(serial_port).to respond_to(:dev)
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'has write accessor for @dev' do
|
38
|
+
expect(serial_port).to respond_to(:dev=)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#initialize' do
|
43
|
+
it 'sets the device to the incomming argument' do
|
44
|
+
expect(serial_port.dev).to equal(device)
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'raises an exception when trying to connect to invalid dev files' do
|
48
|
+
expect do
|
49
|
+
serial_port.class.new('/dev/foobar')
|
50
|
+
end.to raise_exception(InvalidDeviceError)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe '#connect' do
|
55
|
+
it 'raises an exception when the SerialPort connection failed' do
|
56
|
+
expect do
|
57
|
+
serial_port.connect
|
58
|
+
end.to raise_exception(SerialPortConnectionError, "The #{device} device is not a valid SerialPort")
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'raises an exception when the SerialPort connection is nil' do
|
62
|
+
allow(::SerialPort).to receive(:new).and_return(nil)
|
63
|
+
expect do
|
64
|
+
serial_port.connect
|
65
|
+
end.to raise_exception(SerialPortConnectionError, "Could not establish a SerialPort connection to #{device}")
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'sets the flow control and read timeout when the connection is established' do
|
69
|
+
serial_port_stub = double
|
70
|
+
allow(serial_port_stub).to receive(:flow_control=).with(::SerialPort::HARD).once
|
71
|
+
allow(serial_port_stub).to receive(:read_timeout=).with(serial_port.class::READ_TIMEOUT).once
|
72
|
+
allow(::SerialPort).to receive(:new).and_return(serial_port_stub)
|
73
|
+
|
74
|
+
serial_port.connect
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe '#disconnect' do
|
79
|
+
let(:connection_klass) do
|
80
|
+
Class.new do
|
81
|
+
def initialize(closed)
|
82
|
+
@closed = closed
|
83
|
+
end
|
84
|
+
|
85
|
+
def closed?
|
86
|
+
@closed
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'closes the connection if connected' do
|
92
|
+
connection = connection_klass.new(false)
|
93
|
+
allow(connection).to receive(:close)
|
94
|
+
serial_port.instance_variable_set(:@connection, connection)
|
95
|
+
|
96
|
+
serial_port.disconnect
|
97
|
+
|
98
|
+
expect(connection).to have_received(:close)
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'does not close the connection if already disconnected' do
|
102
|
+
connection = connection_klass.new(true)
|
103
|
+
allow(connection).to receive(:close)
|
104
|
+
serial_port.instance_variable_set(:@connection, connection)
|
105
|
+
|
106
|
+
serial_port.disconnect
|
107
|
+
|
108
|
+
expect(connection).not_to have_received(:close)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# describe '#send' do
|
113
|
+
|
114
|
+
# end
|
115
|
+
|
116
|
+
# describe '#receive' do
|
117
|
+
|
118
|
+
# end
|
119
|
+
end
|
data/spec/nxt/nxt_brick_spec.rb
CHANGED
@@ -1,205 +1,274 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe NXTBrick do
|
4
|
-
|
5
|
-
@interface = stub(
|
6
|
-
is_a?: true
|
7
|
-
)
|
8
|
-
end
|
9
|
-
|
10
|
-
subject do
|
11
|
-
NXTBrick.new(@interface)
|
12
|
-
end
|
6
|
+
subject(:nxt_brick) { described_class.new(:usb) }
|
13
7
|
|
14
8
|
describe 'accessors' do
|
15
|
-
it '
|
16
|
-
|
17
|
-
|
9
|
+
it 'has a read accessor for @interface' do
|
10
|
+
expect(nxt_brick).to respond_to(:interface)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'has write accessor for @interface' do
|
14
|
+
expect(nxt_brick).to respond_to(:interface=)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'has a read accessor for @a' do
|
18
|
+
expect(nxt_brick).to respond_to(:a)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'has a write accessor for @a' do
|
22
|
+
expect(nxt_brick).not_to respond_to(:a=)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'has a read accessor for @b' do
|
26
|
+
expect(nxt_brick).to respond_to(:b)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'has a write accessor for @b' do
|
30
|
+
expect(nxt_brick).not_to respond_to(:b=)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'has a read accessor for @c' do
|
34
|
+
expect(nxt_brick).to respond_to(:c)
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'has a write accessor for @c' do
|
38
|
+
expect(nxt_brick).not_to respond_to(:c=)
|
18
39
|
end
|
19
40
|
|
20
|
-
it '
|
21
|
-
|
22
|
-
should respond_to(:options=)
|
41
|
+
it 'has a read accessor for @one' do
|
42
|
+
expect(nxt_brick).to respond_to(:one)
|
23
43
|
end
|
24
44
|
|
25
|
-
it '
|
26
|
-
|
27
|
-
should_not respond_to(:a=)
|
45
|
+
it 'has a write accessor for @one' do
|
46
|
+
expect(nxt_brick).not_to respond_to(:one=)
|
28
47
|
end
|
29
48
|
|
30
|
-
it '
|
31
|
-
|
32
|
-
should_not respond_to(:b=)
|
49
|
+
it 'has a read accessor for @two' do
|
50
|
+
expect(nxt_brick).to respond_to(:two)
|
33
51
|
end
|
34
52
|
|
35
|
-
it '
|
36
|
-
|
37
|
-
should_not respond_to(:c=)
|
53
|
+
it 'has a write accessor for @two' do
|
54
|
+
expect(nxt_brick).not_to respond_to(:two=)
|
38
55
|
end
|
39
56
|
|
40
|
-
it '
|
41
|
-
|
42
|
-
should_not respond_to(:one=)
|
57
|
+
it 'has a read accessor for @three' do
|
58
|
+
expect(nxt_brick).to respond_to(:three)
|
43
59
|
end
|
44
60
|
|
45
|
-
it '
|
46
|
-
|
47
|
-
should_not respond_to(:two=)
|
61
|
+
it 'has a write accessor for @three' do
|
62
|
+
expect(nxt_brick).not_to respond_to(:three=)
|
48
63
|
end
|
49
64
|
|
50
|
-
it '
|
51
|
-
|
52
|
-
should_not respond_to(:three=)
|
65
|
+
it 'has a read accessor for @four' do
|
66
|
+
expect(nxt_brick).to respond_to(:four)
|
53
67
|
end
|
54
68
|
|
55
|
-
it '
|
56
|
-
|
57
|
-
should_not respond_to(:four=)
|
69
|
+
it 'has a write accessor for @four' do
|
70
|
+
expect(nxt_brick).not_to respond_to(:four=)
|
58
71
|
end
|
59
72
|
|
60
|
-
it '
|
61
|
-
|
62
|
-
|
73
|
+
it 'has a read accessor for @port_identifiers' do
|
74
|
+
expect(nxt_brick).to respond_to(:port_identifiers)
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'has a write accessor for @port_identifiers' do
|
78
|
+
expect(nxt_brick).not_to respond_to(:port_identifiers=)
|
63
79
|
end
|
64
80
|
end
|
65
81
|
|
66
82
|
describe '#initialize' do
|
67
|
-
it '
|
68
|
-
interface_stub = stub()
|
69
|
-
interface_stub.should_receive(:is_a?).with(NXT::Interface::Base).once.and_return(false)
|
83
|
+
it 'raises an exception if an invalid type of interface is given' do
|
70
84
|
expect do
|
71
|
-
|
85
|
+
described_class.new(:foobar)
|
72
86
|
end.to raise_exception(InvalidInterfaceError)
|
73
87
|
end
|
74
88
|
|
75
|
-
it '
|
76
|
-
|
89
|
+
it 'sets the interface to the incomming argument' do
|
90
|
+
expect(nxt_brick.interface).to be_an_instance_of(NXT::Interface::Usb)
|
77
91
|
end
|
78
92
|
|
79
|
-
it '
|
80
|
-
options_stub = stub()
|
81
|
-
nxt = NXTBrick.new(@interface, options_stub)
|
82
|
-
nxt.options.should equal(options_stub)
|
83
|
-
end
|
84
|
-
|
85
|
-
it 'should call yield if given a block, passing self' do
|
93
|
+
it 'calls yield if given a block, passing self' do
|
86
94
|
block_called = false
|
87
95
|
|
88
|
-
|
96
|
+
# rubocop:disable RSpec/AnyInstance
|
97
|
+
allow_any_instance_of(NXT::Interface::Usb).to receive(:connect)
|
98
|
+
# rubocop:enable RSpec/AnyInstance
|
99
|
+
|
100
|
+
described_class.new(:usb) do |_nxt|
|
89
101
|
block_called = true
|
90
|
-
nxt.should be_an_instance_of(NXTBrick)
|
91
102
|
end
|
92
103
|
|
93
|
-
block_called.
|
104
|
+
expect(block_called).to be true
|
94
105
|
end
|
95
|
-
end
|
96
106
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
107
|
+
it 'passes self if given a block' do
|
108
|
+
# rubocop:disable RSpec/AnyInstance
|
109
|
+
allow_any_instance_of(NXT::Interface::Usb).to receive(:connect)
|
110
|
+
# rubocop:enable RSpec/AnyInstance
|
111
|
+
|
112
|
+
described_class.new(:usb) do |nxt|
|
113
|
+
expect(nxt).to be_an_instance_of(described_class)
|
114
|
+
end
|
102
115
|
end
|
116
|
+
end
|
103
117
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
118
|
+
describe '#connect' do
|
119
|
+
let(:interface) { Object.new }
|
120
|
+
|
121
|
+
before do
|
122
|
+
nxt_brick.instance_variable_set(:@interface, interface)
|
108
123
|
end
|
109
124
|
|
110
|
-
it '
|
111
|
-
|
112
|
-
|
113
|
-
|
125
|
+
it 'calls connect on the interface' do
|
126
|
+
allow(interface).to receive(:connect)
|
127
|
+
|
128
|
+
nxt_brick.connect
|
129
|
+
|
130
|
+
expect(interface).to have_received(:connect)
|
114
131
|
end
|
132
|
+
end
|
115
133
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
134
|
+
describe '#disconnect' do
|
135
|
+
let(:interface) { Object.new }
|
136
|
+
|
137
|
+
before do
|
138
|
+
nxt_brick.instance_variable_set(:@interface, interface)
|
120
139
|
end
|
121
140
|
|
122
|
-
it '
|
123
|
-
|
124
|
-
class_stub = Class.new
|
125
|
-
class_return_stub = stub()
|
141
|
+
it 'calls disconnect on the interface' do
|
142
|
+
allow(interface).to receive(:disconnect)
|
126
143
|
|
127
|
-
|
128
|
-
class_return_stub
|
129
|
-
end.with(port).once()
|
144
|
+
nxt_brick.disconnect
|
130
145
|
|
131
|
-
|
146
|
+
expect(interface).to have_received(:disconnect)
|
147
|
+
end
|
148
|
+
end
|
132
149
|
|
133
|
-
|
150
|
+
describe '#add' do
|
151
|
+
let(:port) { :a }
|
152
|
+
let(:identifier) { :hello }
|
153
|
+
let(:class_stub) do
|
154
|
+
Class.new do
|
155
|
+
def initialize(*_args)
|
156
|
+
nil
|
157
|
+
end
|
158
|
+
end
|
134
159
|
end
|
135
160
|
|
136
|
-
it '
|
137
|
-
|
161
|
+
it 'raises an exception if an invalid port number or letter is given' do
|
162
|
+
expect do
|
163
|
+
nxt_brick.add(:invalid_port, identifier, class_stub)
|
164
|
+
end.to raise_exception(TypeError, 'Expected port to be one of: :a, :b, :c, :one, :two, :three, :four')
|
165
|
+
end
|
138
166
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
167
|
+
it 'raises an exception if an invalid type of identifier is given' do
|
168
|
+
expect do
|
169
|
+
nxt_brick.add(port, 123, class_stub)
|
170
|
+
end.to raise_exception(TypeError, 'Expected identifier to respond to: to_sym')
|
171
|
+
end
|
143
172
|
|
173
|
+
it 'raises an exception if an invalid type of class is given' do
|
144
174
|
expect do
|
145
|
-
|
146
|
-
end.to
|
175
|
+
nxt_brick.add(port, identifier, 'not a class')
|
176
|
+
end.to raise_exception(TypeError, 'Expected klass to be of type Class')
|
147
177
|
end
|
148
178
|
|
149
|
-
it '
|
150
|
-
|
151
|
-
|
179
|
+
it 'raises an exception if trying to use an identifier that is the name of a defined method' do
|
180
|
+
expect do
|
181
|
+
nxt_brick.add(port, :add, class_stub)
|
182
|
+
end.to raise_error(
|
183
|
+
InvalidIdentifierError,
|
184
|
+
'Cannot use identifier add, a method on NXTBrick is already using it.'
|
185
|
+
)
|
186
|
+
end
|
152
187
|
|
153
|
-
|
154
|
-
|
155
|
-
subject.stub(identifier)
|
188
|
+
it 'raises an exception if the port given is already set' do
|
189
|
+
nxt_brick.add(port, identifier, class_stub)
|
156
190
|
|
157
191
|
expect do
|
158
|
-
|
159
|
-
end.to raise_error(
|
192
|
+
nxt_brick.add(port, identifier, class_stub)
|
193
|
+
end.to raise_error(PortTakenError, "Port #{port} is already set, call remove first")
|
160
194
|
end
|
161
195
|
|
162
|
-
it '
|
163
|
-
port
|
164
|
-
identifier = :hello_world
|
165
|
-
class_stub = Class.new
|
166
|
-
class_stub.stub(:new)
|
167
|
-
|
168
|
-
subject.add(port, identifier, class_stub)
|
196
|
+
it 'sets up the port if the given port is not alread in use' do
|
197
|
+
nxt_brick.add(port, identifier, class_stub)
|
169
198
|
|
170
|
-
|
199
|
+
expect(nxt_brick.send(port)).not_to eq(nil)
|
171
200
|
end
|
172
201
|
end
|
173
202
|
|
174
203
|
describe '#remove' do
|
175
|
-
it '
|
204
|
+
it 'raises an exception if an invalid type of identifier is given' do
|
176
205
|
expect do
|
177
|
-
|
178
|
-
end.to raise_exception(TypeError, 'Expected identifier to
|
206
|
+
nxt_brick.remove(123)
|
207
|
+
end.to raise_exception(TypeError, 'Expected identifier to respond to: to_sym')
|
208
|
+
end
|
209
|
+
|
210
|
+
it 'removes any matching identifiers' do
|
211
|
+
identifier = :hello_world
|
212
|
+
port_identifiers = {}
|
213
|
+
nxt_brick.instance_variable_set(:@port_identifiers, port_identifiers)
|
214
|
+
|
215
|
+
allow(port_identifiers).to receive(:delete)
|
216
|
+
|
217
|
+
nxt_brick.remove(identifier)
|
218
|
+
|
219
|
+
expect(port_identifiers).to have_received(:delete).with(identifier).once
|
179
220
|
end
|
180
221
|
|
181
|
-
it '
|
222
|
+
it 'returns a boolean true if it removed anything' do
|
182
223
|
identifier = :hello_world
|
183
224
|
port_identifiers = {}
|
184
|
-
|
225
|
+
port_identifiers[identifier] = true
|
226
|
+
nxt_brick.instance_variable_set(:@port_identifiers, port_identifiers)
|
185
227
|
|
186
|
-
|
187
|
-
|
228
|
+
return_value = nxt_brick.remove(identifier)
|
229
|
+
expect(return_value).to be true
|
188
230
|
end
|
189
231
|
|
190
|
-
it '
|
232
|
+
it 'removes the port if it did delete something' do
|
191
233
|
identifier = :hello_world
|
192
234
|
port_identifiers = {}
|
193
235
|
port_identifiers[identifier] = true
|
194
|
-
|
236
|
+
nxt_brick.instance_variable_set(:@port_identifiers, port_identifiers)
|
237
|
+
|
238
|
+
nxt_brick.remove(identifier)
|
239
|
+
|
240
|
+
expect(port_identifiers).not_to include(identifier)
|
241
|
+
end
|
195
242
|
|
196
|
-
|
197
|
-
return_value.
|
243
|
+
it 'returns a boolean false if it did not remove anything' do
|
244
|
+
return_value = nxt_brick.remove(:hello_world)
|
245
|
+
expect(return_value).to be false
|
246
|
+
end
|
247
|
+
end
|
198
248
|
|
199
|
-
|
249
|
+
describe '#define_port_handler_method' do
|
250
|
+
let(:port) { :a }
|
251
|
+
let(:identifier) { :hello }
|
252
|
+
let(:class_stub) do
|
253
|
+
Class.new do
|
254
|
+
def initialize(*_args)
|
255
|
+
nil
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
it 'stores it in the attribute for the given port' do
|
261
|
+
class_return_stub = double
|
262
|
+
allow(class_stub).to receive(:new) { class_return_stub }
|
263
|
+
|
264
|
+
nxt_brick.send(:define_port_handler_method, port, identifier, class_stub)
|
265
|
+
|
266
|
+
expect(nxt_brick.send(port)).to equal(class_return_stub)
|
267
|
+
end
|
200
268
|
|
201
|
-
|
202
|
-
|
269
|
+
it 'sets up the port identifiers correctly' do
|
270
|
+
nxt_brick.send(:define_port_handler_method, port, identifier, class_stub)
|
271
|
+
expect(nxt_brick.port_identifiers[identifier]).to equal(port)
|
203
272
|
end
|
204
273
|
end
|
205
274
|
end
|