86.36% Coverage. Moving on to SQL re-integration

This commit is contained in:
Rick Carlino 2015-04-28 06:40:36 -05:00
parent 36542501ea
commit 8fd8cf103f
11 changed files with 138 additions and 49 deletions

View file

@ -2,7 +2,7 @@ require_relative 'abstract_controller'
require_relative '../sequence_factory'
class ExecSequenceController < AbstractController
def call
sequence = SequenceFactory.run!(@message.payload["command"])
sequence = SequenceFactory.run!(Hash(@message.payload["command"]))
sequence.steps.each do |step|
step.call(bot)
end

View file

@ -7,10 +7,10 @@ require 'pry'
class FarmBotPi
attr_accessor :mesh, :bot, :credentials, :handler
def initialize(env = :development)
def initialize(bot: FB::Arduino.new)
@credentials = Credentials.new
@mesh = EM::MeshRuby.new(@credentials.uuid, @credentials.token)
@bot = FB::Arduino.new
@bot = bot
end
def start
@ -34,5 +34,7 @@ class FarmBotPi
bot.onclose { EM.stop }
end
rescue => error
binding.pry
end
end

View file

@ -1,7 +1,7 @@
require 'json'
require 'time'
require_relative 'mesh_message'
Dir["lib/controllers/*.rb"].each { |f| load(f) }
Dir["lib/controllers/**/*.rb"].each { |f| load(f) }
# Get the JSON command, received through skynet, and send it to the farmbot
# command queue Parses JSON messages received through SkyNet.

View file

@ -9,52 +9,60 @@ require 'ostruct'
# |__| \___/ |_____| \___/ |__| * Find a way to re-use single_command controller?
class SequenceStep < OpenStruct
attr_accessor :bot
def initialize(hash = nil)
super
self[:command] = OpenStruct.new(self[:command])
end
def call(bot)
botcmd, cmd = bot.commands, command
case message_type
when "move_relative"
coords = {x: (cmd.x || 0), y: (cmd.y || 0), z: (cmd.z || 0)}
bot.commands.move_relative coords
when "move_absolute"
coords = { x: cmd.x || bot.current_position.x,
y: cmd.y || bot.current_position.y,
z: cmd.z || bot.current_position.z }
botcmd.move_absolute(coords)
when "pin_write"
botcmd.pin_write(pin: cmd.pin, value: cmd.value, mode: cmd.mode)
else
bot.log "Unknown message #{message_type}"
@bot = bot
route_me = { "move_relative" => -> { move_relative },
"move_absolute" => -> { move_absolute },
"pin_write" => -> { pin_write }, }
route_me[message_type][] || bot.log("Unknown message #{message_type}")
end
def move_relative
coords = {x: (command.x || 0), y: (command.y || 0), z: (command.z || 0)}
bot.commands.move_relative coords
end
def move_absolute
coords = { x: command.x || bot.current_position.x,
y: command.y || bot.current_position.y,
z: command.z || bot.current_position.z, }
bot.commands.move_absolute(coords)
end
def pin_write
bot.commands.pin_write(pin: command.pin, value: command.value, mode: command.mode)
end
end
class StepValidator < Mutations::Command
COMMANDS = %w(emergency_stop home_all home_x home_y home_z move_absolute
move_relative pin_write read_parameter read_status write_parameter)
required do
string :message_type, in: COMMANDS
hash :command do
optional do
[:x, :y, :z, :speed, :pin, :value, :mode].each do |f|
integer f, default: nil
end
end
end
end
def execute
SequenceStep.new(inputs)
end
end
# Builds a validated sequence (and collection of steps)
class SequenceFactory < Mutations::Command
class StepValidator < Mutations::Command
COMMANDS = %w(emergency_stop home_all home_x home_y home_z move_absolute
move_relative pin_write read_parameter read_status write_parameter)
required do
string :message_type, in: COMMANDS
hash :command do
optional do
[:x, :y, :z, :speed, :pin, :value, :mode].each do |f|
integer f, default: nil
end
end
end
end
def execute
SequenceStep.new(inputs)
end
end
required do
string :name
array(:steps) { model :sequence_step, builder: StepValidator }

View file

@ -0,0 +1,37 @@
require 'spec_helper'
describe ExecSequenceController do
let(:bot) { FakeBot.new }
let(:mesh) { FakeMesh.new }
let(:example_hash) do
{
"message_type" => "exec_sequence",
"command" => {
"name" =>"Yowza!",
"steps" => [
{"message_type"=>"move_relative", "command"=> {"x"=>"500"}},
{"message_type"=>"move_absolute", "command"=> {"y"=>"1200"}},
{"message_type" => "pin_write",
"command" => {"pin" => 1, "value" => 1, "mode" => 0}},
]
}
}
end
let(:message) do
MeshMessage.new(from: '1234567890',
type: 'exec_sequence',
payload: example_hash)
end
let(:controller) { ExecSequenceController.new(message, bot, mesh) }
it "initializes" do
controller.call
expect(mesh.last.type).to eq("exec_sequence")
within_event_loop { bot.execute_command_next_tick }
results = bot.outbound_queue.map(&:to_s)
["F41 P1 V1 M0", "G0 X0 Y1200 Z0", "G0 X500 Y0 Z0"].each do |gcode|
expect(results).to include(gcode)
end
end
end

View file

@ -1,11 +1,12 @@
class FakeBot
require_relative "fake_logger"
require_relative "fake_serial_port"
require 'farmbot-serial'
class FakeBot < FB::Arduino
attr_reader :logs, :last_log
def initialize
def initialize(serial_port: FakeSerialPort.new, logger: FakeLogger.new)
@logs = []
end
def log(msg)
@last_log = @logs.push(msg).last
super
end
end

10
spec/fakes/fake_logger.rb Normal file
View file

@ -0,0 +1,10 @@
class FakeLogger < StringIO
def initialize(input = "")
super
end
def message
rewind
read.chomp
end
end

View file

@ -0,0 +1,12 @@
## SERIAL PORT SIMULATION
## **********************
class FakeSerialPort < StringIO
def initialize(*)
super("")
end
def message
rewind
read.chomp
end
end

11
spec/farmbot-pi_spec.rb Normal file
View file

@ -0,0 +1,11 @@
require 'spec_helper'
describe FarmBotPi do
let(:bot) { FakeBot.new }
it 'starts the event loop' do
pending 'Will circle back to this one later.
Possible issues in Farmbot-serial'.strip
expect(true).to be_falsey
end
end

View file

@ -18,8 +18,6 @@ describe Credentials do
expect(cred.credentials_file).to eq(temp_file)
end
it 'loads credentials'
it 'creates credentials' do
old_uuid = cred.uuid
old_token = cred.token

View file

@ -1,12 +1,13 @@
require 'simplecov'
SimpleCov.start do
add_filter "/spec/"
root = './lib'
end
require 'pry'
Dir['spec/fakes/**/*.rb'].each {|file| load file }
Dir['spec/fakes/**/*.rb'].each { |file| load file }
require_relative '../lib/farmbot-pi'
require_relative '../lib/controllers/exec_sequence_controller'
require_relative '../lib/sequence_factory.rb'
RSpec.configure do |config|
config.expect_with :rspec do |expectations|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
@ -16,3 +17,12 @@ RSpec.configure do |config|
mocks.verify_partial_doubles = true
end
end
# This is used for testing things that require an event loop. Once run, you can
# observe / make assertions on side effects.
def within_event_loop
EM.run do
EventMachine::PeriodicTimer.new(0.1) { EM.stop }
yield
end
end