nxt 0.3.0 → 0.5.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.
Files changed (109) hide show
  1. checksums.yaml +15 -0
  2. data/README.md +45 -0
  3. data/lib/communication/bluetooth_communication.rb +85 -0
  4. data/lib/communication/serial_port_profile.rb +71 -0
  5. data/lib/nxt.rb +111 -20
  6. data/lib/ruby-nxt.sublime-project +8 -0
  7. data/lib/ruby-nxt.sublime-workspace +288 -0
  8. data/lib/telegrams/commands/direct/get_battery_level.rb +8 -0
  9. data/lib/telegrams/commands/direct/get_current_program_name.rb +8 -0
  10. data/lib/{nxt/commands/sound.rb → telegrams/commands/direct/get_input_values.rb} +0 -0
  11. data/lib/{nxt/commands/tone.rb → telegrams/commands/direct/keep_alive.rb} +0 -0
  12. data/lib/telegrams/commands/direct/ls_get_status.rb +0 -0
  13. data/lib/telegrams/commands/direct/ls_read.rb +0 -0
  14. data/lib/telegrams/commands/direct/ls_write.rb +0 -0
  15. data/lib/telegrams/commands/direct/message_read.rb +0 -0
  16. data/lib/telegrams/commands/direct/message_write.rb +0 -0
  17. data/lib/telegrams/commands/direct/output_state.rb +229 -0
  18. data/lib/telegrams/commands/direct/play_sound_file.rb +38 -0
  19. data/lib/telegrams/commands/direct/play_tone.rb +34 -0
  20. data/lib/telegrams/commands/direct/replies/get_battery_level_reply.rb +28 -0
  21. data/lib/telegrams/commands/direct/replies/get_current_program_name_reply.rb +30 -0
  22. data/lib/telegrams/commands/direct/replies/play_sound_file_reply.rb +13 -0
  23. data/lib/telegrams/commands/direct/replies/play_tone_reply.rb +14 -0
  24. data/lib/telegrams/commands/direct/replies/reset_motor_position_reply.rb +13 -0
  25. data/lib/telegrams/commands/direct/replies/set_input_mode_reply.rb +11 -0
  26. data/lib/telegrams/commands/direct/replies/set_output_state_reply.rb +11 -0
  27. data/lib/telegrams/commands/direct/replies/start_program_reply.rb +13 -0
  28. data/lib/telegrams/commands/direct/replies/stop_program_reply.rb +13 -0
  29. data/lib/telegrams/commands/direct/replies/stop_sound_playback_reply.rb +13 -0
  30. data/lib/telegrams/commands/direct/reset_input_scaled_value.rb +0 -0
  31. data/lib/telegrams/commands/direct/reset_motor_position.rb +8 -0
  32. data/lib/telegrams/commands/direct/set_input_mode.rb +101 -0
  33. data/lib/telegrams/commands/direct/set_output_state.rb +29 -0
  34. data/lib/telegrams/commands/direct/start_program.rb +30 -0
  35. data/lib/telegrams/commands/direct/stop_program.rb +9 -0
  36. data/lib/telegrams/commands/direct/stop_sound_playback.rb +8 -0
  37. data/lib/telegrams/commands/direct_command.rb +8 -0
  38. data/lib/telegrams/commands/direct_command_reply.rb +10 -0
  39. data/lib/telegrams/commands/message_translator.rb +46 -0
  40. data/lib/telegrams/commands/system/get_device_info.rb +8 -0
  41. data/lib/telegrams/commands/system/replies/get_device_info_reply.rb +41 -0
  42. data/lib/telegrams/commands/system_command.rb +8 -0
  43. data/lib/telegrams/messages/error.rb +0 -0
  44. data/lib/telegrams/messages/message.rb +0 -0
  45. data/lib/telegrams/messages/success.rb +0 -0
  46. data/lib/telegrams/no_message_reply.rb +10 -0
  47. data/lib/telegrams/reply.rb +82 -0
  48. data/lib/telegrams/respondable_telegram.rb +29 -0
  49. data/lib/telegrams/telegram.rb +14 -0
  50. data/spec/communication/bluetooth_communication_spec.rb +170 -0
  51. data/spec/communication/serial_port_profile_spec.rb +139 -0
  52. data/spec/helper.rb +1 -0
  53. data/spec/nxt_spec.rb +438 -0
  54. data/spec/telegrams/commands/direct/get_battery_level_spec.rb +26 -0
  55. data/spec/telegrams/commands/direct/get_current_program_name_spec.rb +26 -0
  56. data/spec/telegrams/commands/direct/output_state_spec.rb +198 -0
  57. data/spec/telegrams/commands/direct/play_sound_file_spec.rb +75 -0
  58. data/spec/telegrams/commands/direct/play_tone_spec.rb +63 -0
  59. data/spec/telegrams/commands/direct/replies/get_battery_level_reply_spec.rb +40 -0
  60. data/spec/telegrams/commands/direct/replies/get_current_program_name_reply_spec.rb +33 -0
  61. data/spec/telegrams/commands/direct/replies/play_sound_file_reply_spec.rb +13 -0
  62. data/spec/telegrams/commands/direct/replies/play_tone_reply_spec.rb +14 -0
  63. data/spec/telegrams/commands/direct/replies/reset_motor_position_reply_spec.rb +13 -0
  64. data/spec/telegrams/commands/direct/replies/set_input_mode_reply_spec.rb +12 -0
  65. data/spec/telegrams/commands/direct/replies/set_output_state_reply_spec.rb +12 -0
  66. data/spec/telegrams/commands/direct/replies/start_program_reply_spec.rb +12 -0
  67. data/spec/telegrams/commands/direct/replies/stop_program_reply_spec.rb +13 -0
  68. data/spec/telegrams/commands/direct/replies/stop_sound_playback_reply_spec.rb +13 -0
  69. data/spec/telegrams/commands/direct/reset_motor_position_spec.rb +31 -0
  70. data/spec/telegrams/commands/direct/set_input_mode_spec.rb +122 -0
  71. data/spec/telegrams/commands/direct/set_output_state_spec.rb +72 -0
  72. data/spec/telegrams/commands/direct/start_program_spec.rb +58 -0
  73. data/spec/telegrams/commands/direct/stop_program_spec.rb +34 -0
  74. data/spec/telegrams/commands/direct/stop_sound_playback_spec.rb +34 -0
  75. data/spec/telegrams/commands/direct_command_reply_spec.rb +7 -0
  76. data/spec/telegrams/commands/direct_command_spec.rb +34 -0
  77. data/spec/telegrams/commands/system/get_device_info_spec.rb +16 -0
  78. data/spec/telegrams/commands/system/replies/get_device_info_reply_spec.rb +63 -0
  79. data/spec/telegrams/commands/system_command_spec.rb +26 -0
  80. data/spec/telegrams/no_message_reply_spec.rb +12 -0
  81. data/spec/telegrams/reply_spec.rb +63 -0
  82. data/spec/telegrams/respondable_telegram_spec.rb +66 -0
  83. data/spec/telegrams/telegram_spec.rb +38 -0
  84. metadata +97 -116
  85. data/README.markdown +0 -52
  86. data/Rakefile +0 -35
  87. data/lib/nxt/commands/base.rb +0 -51
  88. data/lib/nxt/commands/input.rb +0 -60
  89. data/lib/nxt/commands/output.rb +0 -105
  90. data/lib/nxt/commands/program.rb +0 -70
  91. data/lib/nxt/connectors/base.rb +0 -35
  92. data/lib/nxt/connectors/input/color.rb +0 -30
  93. data/lib/nxt/connectors/input/touch.rb +0 -11
  94. data/lib/nxt/connectors/input/ultrasonic.rb +0 -11
  95. data/lib/nxt/connectors/output/motor.rb +0 -114
  96. data/lib/nxt/errors.rb +0 -25
  97. data/lib/nxt/exceptions.rb +0 -26
  98. data/lib/nxt/interfaces/base.rb +0 -36
  99. data/lib/nxt/interfaces/serial_port.rb +0 -88
  100. data/lib/nxt/interfaces/usb.rb +0 -8
  101. data/lib/nxt/nxt_brick.rb +0 -167
  102. data/lib/nxt/patches/module.rb +0 -22
  103. data/lib/nxt/patches/string.rb +0 -29
  104. data/lib/nxt/utils/accessors.rb +0 -24
  105. data/spec/matchers.rb +0 -7
  106. data/spec/nxt/connectors/output/motor_spec.rb +0 -55
  107. data/spec/nxt/interfaces/serial_port_spec.rb +0 -73
  108. data/spec/nxt/nxt_brick_spec.rb +0 -199
  109. data/spec/spec_helper.rb +0 -4
@@ -1,11 +0,0 @@
1
- module NXT
2
- module Connector
3
- module Input
4
- class Touch
5
- def initialize(port)
6
- @port = port
7
- end
8
- end
9
- end
10
- end
11
- end
@@ -1,11 +0,0 @@
1
- module NXT
2
- module Connector
3
- module Input
4
- class Ultrasonic
5
- def initialize(port)
6
- @port = port
7
- end
8
- end
9
- end
10
- end
11
- end
@@ -1,114 +0,0 @@
1
- module NXT
2
- module Connector
3
- module Output
4
- class Motor
5
- include NXT::Command::Output
6
- extend NXT::Utils::Accessors
7
-
8
- DURATION_TYPE = [:seconds, :degrees, :rotations].freeze
9
- DURATION_AFTER = [:coast, :brake].freeze
10
- DIRECTION = [:forwards, :backwards].freeze
11
-
12
- attr_accessor :port, :interface
13
-
14
- attr_combined_accessor :duration, 0
15
- attr_combined_accessor :duration_type, :seconds
16
- attr_combined_accessor :duration_after, :brake
17
- attr_combined_accessor :direction, :forwards
18
-
19
- attr_setter :direction, is_in: DIRECTION
20
- attr_setter :duration_after, is_in: DURATION_AFTER
21
- attr_setter :duration_type, is_in: DURATION_TYPE
22
-
23
- def initialize(port, interface)
24
- @port = port
25
- @interface = interface
26
- end
27
-
28
- def duration=(duration, options = {})
29
- raise TypeError.new('Expected duration to be a number') unless duration.is_a?(Integer)
30
- @duration = duration
31
-
32
- self.duration_type = options[:type] if options.include?(:type)
33
-
34
- if options.include?(:after)
35
- raise TypeError.new('The after option is only available when the unit duration is in seconds.') if self.duration_type != :seconds
36
- self.duration_after = options[:after]
37
- end
38
-
39
- self
40
- end
41
-
42
- def forwards
43
- self.direction = :forwards
44
- self
45
- end
46
-
47
- def backwards
48
- self.direction = :backwards
49
- self
50
- end
51
-
52
- def stop(type = :coast)
53
- self.power = 0
54
- self.mode = :coast
55
-
56
- self.move
57
- end
58
-
59
- # takes block for response, or can return the response instead.
60
- def move
61
- response_required = (self.duration > 0 && self.duration_type != :seconds)
62
-
63
- set_motor_output_state(response_required)
64
-
65
- if self.duration > 0 && self.duration_type == :seconds
66
- sleep(self.duration)
67
- self.reset
68
- self.stop(self.duration_after)
69
- else
70
- self.reset
71
- end
72
- end
73
-
74
- def reset
75
- self.duration = 0
76
- self.direction = :forwards
77
- self.power = 75
78
- self.mode = :motor_on
79
- self.regulation_mode = :idle
80
- self.run_state = :running
81
- self.tacho_limit = 0
82
- end
83
-
84
-
85
- private
86
- def set_motor_output_state(response_required=true)
87
- original_tacho_limit = self.tacho_limit
88
- adjust_tacho_limit
89
-
90
- original_power = self.power
91
- adjust_power
92
-
93
- set_output_state(response_required)
94
-
95
- self.power = original_power
96
- self.tacho_limit = original_tacho_limit
97
- end
98
-
99
- def adjust_tacho_limit
100
- case self.duration_type
101
- when :rotations
102
- self.tacho_limit = self.duration * 360
103
- when :degrees
104
- self.tacho_limit = self.duration
105
- end
106
- end
107
-
108
- def adjust_power
109
- self.power *= -1 if self.direction == :backwards
110
- end
111
- end
112
- end
113
- end
114
- end
@@ -1,25 +0,0 @@
1
- module NXT
2
- module Errors
3
- SUCCESS = 0x00
4
- CODES = {
5
- SUCCESS => 'Success',
6
- 0x20 => 'Pending communication transaction in progress',
7
- 0x40 => 'Specified mailbox queue is empty',
8
- 0xBD => 'Request failed (i.e., specified file not found)',
9
- 0xBE => 'Unknown command opcode',
10
- 0xBF => 'Insane packet',
11
- 0xC0 => 'Data contains out-of-range values',
12
- 0xDD => 'Communication bus error',
13
- 0xDE => 'No free memory in communication buffer',
14
- 0xDF => 'Specified channel/connection is not valid',
15
- 0xE0 => 'Specified channel/connection not configured or busy',
16
- 0xEC => 'No active program',
17
- 0xED => 'Illegal size specified',
18
- 0xEE => 'Illegal mailbox queue ID specified',
19
- 0xEF => 'Attempted to access invalid field of a structure',
20
- 0xF0 => 'Bad input or output specified',
21
- 0xFB => 'Insufficient memory available',
22
- 0xFF => 'Bad arguments'
23
- }
24
- end
25
- end
@@ -1,26 +0,0 @@
1
- module NXT
2
- module Exceptions
3
- # Raised when a class has not implemented a method from the base class
4
- # that is required to be overriden.
5
- class InterfaceNotImplemented < NotImplementedError; end
6
-
7
- # Raised when an invalid interface is attempted to be constructed.
8
- class InvalidInterfaceError < TypeError; end
9
-
10
- # Raised when a port is attempted to be named when it already has been.
11
- class PortTakenError < TypeError; end
12
-
13
- # Raised when an invalid name is attempted to be given to a port.
14
- class InvalidIdentifierError < TypeError; end
15
-
16
- # Raised when the device file attempted to be used for communication is
17
- # for whatever reason does not exist or is not correct.
18
- class InvalidDeviceError < TypeError; end
19
-
20
- # Raised when communication with a Serial Port connection fails.
21
- class SerialPortConnectionError < RuntimeError; end
22
-
23
- # Raised when a command returns a reply message error
24
- class CommandError < RuntimeError; end
25
- end
26
- end
@@ -1,36 +0,0 @@
1
- require_relative '../commands/base' #TODO: bad code smell here
2
-
3
- module NXT
4
- module Interface
5
- class Base
6
- def send_and_receive(msg, response_required = true)
7
- if response_required
8
- msg[0] = msg[0] | (response_required ? 0x00 : 0x80)
9
- end
10
-
11
- self.send(msg)
12
-
13
- if response_required
14
- response = self.receive
15
- response
16
- end
17
- end
18
-
19
- def send
20
- raise InterfaceNotImplemented.new('The #send method must be implemented.')
21
- end
22
-
23
- def receive
24
- raise InterfaceNotImplemented.new('The #receive method must be implemented.')
25
- end
26
-
27
- def connect
28
- raise InterfaceNotImplemented.new('The #connect method must be implemented')
29
- end
30
-
31
- def disconnect
32
- raise InterfaceNotImplemented.new('The #disconnect method must be implemented')
33
- end
34
- end
35
- end
36
- end
@@ -1,88 +0,0 @@
1
- require 'serialport'
2
-
3
- module NXT
4
- module Interface
5
- class SerialPort < Base
6
- include NXT::Exceptions
7
-
8
- attr_reader :dev
9
-
10
- BAUD_RATE = 57600
11
- DATA_BITS = 8
12
- STOP_BITS = 1
13
- PARITY = ::SerialPort::NONE
14
- READ_TIMEOUT = 5000
15
-
16
- def initialize(dev)
17
- self.dev = (dev)
18
- end
19
-
20
- def dev=(dev)
21
- raise InvalidDeviceError unless File.exists?(dev)
22
- @dev = dev
23
- end
24
-
25
- def connect
26
- @connection = ::SerialPort.new(@dev, BAUD_RATE, DATA_BITS, STOP_BITS, PARITY)
27
-
28
- if !@connection.nil?
29
- @connection.flow_control = ::SerialPort::HARD
30
- @connection.read_timeout = READ_TIMEOUT
31
- else
32
- raise SerialPortConnectionError.new("Could not establish a SerialPort connection to #{dev}")
33
- end
34
-
35
- @connection
36
- rescue ArgumentError
37
- raise SerialPortConnectionError.new("The #{dev} device is not a valid SerialPort")
38
- end
39
-
40
- def disconnect
41
- @connection.close if @connection && !@connection.closed?
42
- end
43
-
44
- def connected?
45
- !@connection.closed?
46
- end
47
-
48
- def send(msg)
49
- # The expected data package structure for NXT Bluetooth communication is:
50
- #
51
- # [Length Byte 1, Length Byte 2, Command Type, Command, ...]
52
- #
53
- # So here we calculate the two leading length bytes, and rely on the
54
- # passed in argument to give us the rest of the message to send.
55
- #
56
- # Note that the length is stored in Little Endian ie. LSB -> MSB
57
- #
58
- # Reference: Appendix 1, Page 22
59
- msg = [(msg.length & 255), (msg.length >> 8)] + msg
60
-
61
- msg.each do |b|
62
- @connection.putc(b)
63
- end
64
- end
65
-
66
- def receive
67
- # This gets the length of the received data from the header that was sent
68
- # to us. We unpack it, as it's stored as a 16-bit Little Endian number.
69
- #
70
- # Reference: Appendix 1, Page 22
71
- length = @connection.sysread(2)
72
- actual_length = length.unpack('v')[0]
73
- response_bytes = @connection.sysread(actual_length)
74
- response_array = response_bytes.unpack('C*')
75
- response = {
76
- :reply => response_array[0] == 0x02,
77
- :for_command => response_array[1],
78
- :status => response_array[2]
79
- }
80
- response[:message] = NXT::Errors::CODES[response[:status]]
81
- response[:for_command_name] = NXT::Command::Base::COMMANDS[response[:for_command]]
82
- response[:data] = response_array[3..-1] if response_array.size > 3
83
-
84
- response
85
- end
86
- end
87
- end
88
- end
@@ -1,8 +0,0 @@
1
- require 'usb'
2
-
3
- module NXT
4
- module Interface
5
- class USB < Base
6
- end
7
- end
8
- end
@@ -1,167 +0,0 @@
1
- # This class is the entry point for end-users creating their own list of
2
- # commands to execute remotely on a Lego NXT brick.
3
- #
4
- # An instance of this class provides all the endpoints necessary to:
5
- #
6
- # * educate the API on the connected input and output devices; and,
7
- # * access these input and output devices and run commands using them.
8
- #
9
- # @example Creating an instance using a block, with one motor output.
10
- #
11
- # NXTBrick.new(interface) do |nxt|
12
- # nxt.add_motor_output(:a, :front_left)
13
- # end
14
- #
15
- # @example Creating an instance without a block, with one motor and one light sensor.
16
- #
17
- # nxt = NXTBrick.new(interface)
18
- # nxt.add_motor_output(:a, :front_left)
19
- # # ...
20
- # nxt.disconnect
21
- class NXTBrick
22
- include NXT::Exceptions
23
-
24
- # An enumeration of possible ports, both input and output, that the NXT brick
25
- # can have connectors attached to.
26
- PORTS = [:a, :b, :c, :one, :two, :three, :four]
27
-
28
- # Get the instance of the interface that this runner class is using to connect
29
- # to the NXT brick.
30
- attr_accessor :interface
31
-
32
- # Accessors for output ports on the NXT brick. These will be populated with
33
- # the appropriate instances of their respective output connectors.
34
- attr_reader :a, :b, :c
35
-
36
- # Accessors for input ports on the NXT brick. These will be populated with the
37
- # appropriate instances of their respective input connectors.
38
- attr_reader :one, :two, :three, :four
39
-
40
- # We mandate that all added port connections have an identifier associated
41
- # with it. This is so that code is not fragile when port swapping needs to
42
- # be done.
43
- attr_reader :port_identifiers
44
-
45
- def initialize(interface_type, *interface_args)
46
- @port_identifiers = {}
47
- interface_type = interface_type.to_s.classify
48
-
49
- validate_interface(interface_type)
50
- self.interface = NXT::Interface.const_get(interface_type).new(*interface_args)
51
-
52
- if block_given?
53
- begin
54
- self.connect
55
- yield(self)
56
- ensure
57
- self.disconnect
58
- end
59
- end
60
- end
61
-
62
- # Connect using the given interface to the NXT brick.
63
- def connect
64
- self.interface.connect
65
- end
66
-
67
- # Close the connection to the NXT brick, and dispose of any resources that
68
- # this instance of NXTBrick is using. Any commands run against this runner
69
- # after calling disconnect will fail.
70
- def disconnect
71
- self.interface.disconnect
72
- end
73
-
74
- # Add a new connector instance, binding a specific identifier to the given
75
- # port.
76
- #
77
- # If the given port already is bound, an exception will be thrown. The
78
- # instance given though can be of any class, presuming it talks the
79
- # correct language.
80
- #
81
- # @param Symbol port The port to bind to.
82
- # @param Symbol identifier The identifier to associate with this port.
83
- # @param Class klass The Class to instantiate as the instance of this
84
- # port. There is no limitation on what type this can
85
- # be, though it must be able to hook in correctly
86
- # with the NXT library.
87
- def add(port, identifier, klass)
88
- raise TypeError.new('Expected port to be a Symbol') unless port.is_a?(Symbol)
89
- raise TypeError.new('Expected identifier to be a Symbol') unless identifier.is_a?(Symbol)
90
- raise TypeError.new('Expected klass to be a Class') unless klass.is_a?(Class)
91
-
92
- unless PORTS.include?(port)
93
- raise TypeError.new("Expected port to be one of: :#{PORTS.join(', :')}")
94
- end
95
-
96
- port_variable = :"@#{port}"
97
-
98
- if !self.respond_to?(identifier)
99
- # Makes a new instance of the class and pushes it into our instance variable
100
- # for the given port.
101
- self.instance_variable_set(port_variable, klass.new(port, self.interface))
102
-
103
- # Given that that succeeded, all that remains is to add the identifier
104
- # to our lookup Hash. We'll use this Hash later on within method_missing.
105
- @port_identifiers[identifier] = port
106
-
107
- # Define a method on the eigenclass of this instance.
108
- (class << self; self; end).send(:define_method, identifier) do
109
- self.instance_variable_get(port_variable)
110
- end
111
- else
112
- if !self.instance_variable_get(port_variable).nil?
113
- raise PortTakenError.new("Port #{port} is already set, call remove first")
114
- else
115
- raise InvalidIdentifierError.new("Cannot use identifier #{identifier}, a method on #{self.class} is already using it.")
116
- end
117
- end
118
- end
119
-
120
- # Remove the assigned (if any) connector instance from the given
121
- # identifier.
122
- #
123
- # @param Symbol identifier The identifier to search for and remove.
124
- def remove(identifier)
125
- raise TypeError.new('Expected identifier to be a Symbol') unless identifier.is_a?(Symbol)
126
- !!@port_identifiers.delete(identifier)
127
- end
128
-
129
- # This will dynamically add methods like:
130
- #
131
- # * add_light_input
132
- # * add_motor_output
133
- # * add_ultrasonic_input
134
- #
135
- # This means they don't have to provide the class each and every time. For
136
- # connectors they have added themselves, it's likely best to use the
137
- # {#add} method.
138
- NXT::Connector.constants.each do |type_const|
139
- NXT::Connector.const_get(type_const).constants.each do |const|
140
- # We don't use a splat here for the args, because that way when
141
- # people don't pass in the correct number of params, it says helpfully
142
- # '1 of 2' args passed (or something similar).
143
- define_method("add_#{const.to_s.underscore}_#{type_const.to_s.underscore}") do |port, identifier|
144
- self.add(port, identifier, NXT::Connector.const_get(type_const).const_get(const))
145
- end
146
- end
147
- end
148
-
149
- def start_program(name)
150
- NXT::Command::Output::Program.start(self.interface, name)
151
- end
152
-
153
- def stop_program
154
- NXT::Command::Output::Program.stop(self.interface)
155
- end
156
-
157
- def running_program_name
158
- NXT::Command::Output::Program.get_running_name(self.interface)
159
- end
160
-
161
- private
162
- def validate_interface(interface_type_name_string)
163
- unless NXT::Interface.constants.include?(interface_type_name_string.to_sym)
164
- raise InvalidInterfaceError.new("There is no interface of type #{interface_type_name_string}.")
165
- end
166
- end
167
- end