merging with skynet
parent
15bdb00301
commit
16fe103451
|
@ -0,0 +1,22 @@
|
|||
*.gem
|
||||
*.rbc
|
||||
/coverage/
|
||||
/InstalledFiles
|
||||
/pkg/
|
||||
/spec/reports/
|
||||
/test/tmp/
|
||||
/test/version_tmp/
|
||||
/tmp/
|
||||
/.yardoc/
|
||||
/_yardoc/
|
||||
/doc/
|
||||
/rdoc/
|
||||
/.bundle/
|
||||
/lib/bundler/man/
|
||||
Gemfile.lock
|
||||
.ruby-version
|
||||
.ruby-gemset
|
||||
snippets.rb
|
||||
snippet.coffee
|
||||
config.yml
|
||||
credentials.yml
|
|
@ -0,0 +1,19 @@
|
|||
source 'http://rubygems.org'
|
||||
|
||||
gem 'socket.io-client-simple'
|
||||
gem 'firmata'
|
||||
|
||||
group :test do
|
||||
gem 'simplecov'
|
||||
gem 'rspec'
|
||||
end
|
||||
|
||||
group :development do
|
||||
gem 'pry'
|
||||
end
|
||||
|
||||
# needed for schedule
|
||||
gem 'bson'
|
||||
gem 'mongo'
|
||||
gem 'mongoid'
|
||||
|
45
README.md
45
README.md
|
@ -29,13 +29,9 @@ sudo apt-get install ruby
|
|||
|
||||
sudo apt-get install arduino
|
||||
|
||||
gem install firmata
|
||||
|
||||
gem install bson
|
||||
|
||||
gem install mongo
|
||||
|
||||
gem install mongoid
|
||||
'bundle install --without test development' from the project directory
|
||||
OR
|
||||
'bundle install' (for developers)
|
||||
|
||||
|
||||
Mongo
|
||||
|
@ -76,14 +72,41 @@ use "git clone" to copy the code to the RPi
|
|||
Usage
|
||||
=====
|
||||
|
||||
Use "ruby sync.rb" to start the skybet communiation ans synchronisation with the farmbot back end
|
||||
|
||||
Use "ruby runtime.rb" to start the runtime part of the software. This will read the datbase and send commands to the hardware.
|
||||
|
||||
Use "ruby menu.rb" to start the interface. A menu will appear. Type the command needed and press enter. It is also possible to add a list of commands to the file 'testcommands.csv' and use the menu to execute the file.
|
||||
|
||||
Still to do
|
||||
===========
|
||||
Author
|
||||
------
|
||||
|
||||
* A lot of the settings for pins, sleep times, timeouts and motor inversions have to be moved to configuration.
|
||||
* Check if it works with the nema 17 motor from OpenBuilds
|
||||
* Rick Carlino
|
||||
|
||||
* Tim Evers
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2014 Farmbot Project
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
'Software'), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
gem install firmata
|
||||
|
||||
gem install bson
|
||||
|
||||
gem install mongo
|
||||
|
||||
gem install mongoid
|
||||
|
||||
gem install socket.io-client-simple
|
|
@ -0,0 +1,35 @@
|
|||
# data classes for the command queue
|
||||
|
||||
class Command
|
||||
include Mongoid::Document
|
||||
|
||||
embeds_many :commandlines
|
||||
|
||||
field :plant_id
|
||||
field :scheduled_time
|
||||
field :executed_time
|
||||
field :status
|
||||
|
||||
end
|
||||
|
||||
class Commandline
|
||||
include Mongoid::Document
|
||||
|
||||
embedded_in :command
|
||||
#belongs_to :command
|
||||
|
||||
field :action
|
||||
field :coord_x
|
||||
field :coord_y
|
||||
field :coord_z
|
||||
field :speed
|
||||
field :amount
|
||||
end
|
||||
|
||||
class Refresh
|
||||
include Mongoid::Document
|
||||
|
||||
field :name
|
||||
field :value
|
||||
end
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
# FarmBot Controller runtime control
|
||||
|
||||
# Get commands from the database and execute them on the hardware
|
||||
|
||||
puts '[FarmBot Controller]'
|
||||
puts 'starting up'
|
||||
|
||||
puts 'connecting to hardware'
|
||||
|
||||
require_relative 'lib/controller'
|
||||
require_relative "lib/hardware/ramps"
|
||||
|
||||
$bot_control = Controller.new
|
||||
$bot_hardware = HardwareInterface.new
|
||||
|
||||
$shutdown = 0
|
||||
|
||||
puts 'starting farmbot'
|
||||
$bot_control.runFarmBot
|
|
@ -1,66 +1,111 @@
|
|||
# FarmBot Controller
|
||||
|
||||
#require "Rubygems"
|
||||
#require "FarmBotDbAccess"
|
||||
#require "./hardware/ramps.rb"
|
||||
# This module executes the schedule. It reades the next command and sends it to the hardware implementation
|
||||
|
||||
require './lib/controlcommand.rb'
|
||||
require './lib/filehandler.rb'
|
||||
require 'date'
|
||||
|
||||
# This module ties the different components for the FarmBot together
|
||||
# It reads the next action from the database, executes it and reports back
|
||||
# and it will initiate the synchronization with the web systems
|
||||
require_relative 'database/dbaccess'
|
||||
|
||||
class Control
|
||||
class Controller
|
||||
|
||||
def initialize
|
||||
@inactiveCounter = 0
|
||||
@new_command = nil
|
||||
end
|
||||
|
||||
def setCommand( cmd )
|
||||
@new_command = cmd
|
||||
end
|
||||
# read command from schedule, wait for execution time
|
||||
|
||||
def runCycle
|
||||
def initialize
|
||||
|
||||
@bot_dbaccess = DbAccess.new
|
||||
end
|
||||
|
||||
#if $command_queue.empty? == false
|
||||
if @new_command != nil
|
||||
def runFarmBot
|
||||
|
||||
#command = $command_queue.pop
|
||||
#command = @new_command
|
||||
#@new_command = nil
|
||||
@new_command.commandlines.each do |command_line|
|
||||
#command.lines.each do |command_line|
|
||||
case command_line.action.upcase
|
||||
when "MOVE ABSOLUTE"
|
||||
$bot_hardware.moveAbsolute(command_line.coord_x, command_line.coord_y, command_line.coord_z)
|
||||
when "MOVE RELATIVE"
|
||||
$bot_hardware.moveRelative(command_line.coord_x, command_line.coord_y, command_line.coord_z)
|
||||
when "HOME X"
|
||||
$bot_hardware.moveHomeX
|
||||
when "HOME Y"
|
||||
$bot_hardware.moveHomeY
|
||||
when "HOME Z"
|
||||
$bot_hardware.moveHomeZ
|
||||
when "SET SPEED"
|
||||
$bot_hardware.setSpeed(command_line.speed)
|
||||
when "SHUTDOWN"
|
||||
puts "shutdown"
|
||||
$shutdown = 1
|
||||
end
|
||||
check = @bot_dbaccess.checkRefresh
|
||||
|
||||
end
|
||||
#$commandFinished << command if command.commandid != nil and command.commandid > 0
|
||||
else
|
||||
sleep 0.1
|
||||
end
|
||||
while $shutdown == 0 do
|
||||
|
||||
@new_command = nil
|
||||
|
||||
end
|
||||
end
|
||||
# keep checking the database for new data
|
||||
|
||||
#$bot_hardware = FarmBotControlInterface.new
|
||||
#$command_queue = Queue.new
|
||||
#$command_finished = Queue.new
|
||||
puts 'checking schedule'
|
||||
command = @bot_dbaccess.getCommandToExecute
|
||||
@bot_dbaccess.saveRefresh
|
||||
|
||||
if command != nil
|
||||
|
||||
puts "command retrieved is scheduled for #{command.scheduled_time}"
|
||||
puts "curent time is #{Time.now}"
|
||||
|
||||
#scheduled_time = command.scheduled_time
|
||||
|
||||
if command.scheduled_time <= Time.now or command.scheduled_time == nil
|
||||
|
||||
# execute the command now and set the status to done
|
||||
puts 'execute command'
|
||||
|
||||
#process_command( command )
|
||||
@bot_dbaccess.setCommandToExecuteStatus('FINISHED')
|
||||
|
||||
else
|
||||
|
||||
puts 'wait for scheduled time or refresh'
|
||||
|
||||
refresh_received = false
|
||||
|
||||
wait_start_time = Time.now
|
||||
|
||||
# wait until the scheduled time has arrived, or wait for a minute or until a refresh it set in the database as a sign new data has arrived
|
||||
|
||||
while Time.now < wait_start_time + 60 and command.scheduled_time > Time.now - 1 and refresh_received == false
|
||||
|
||||
sleep 1
|
||||
|
||||
refresh_received = @bot_dbaccess.checkRefresh
|
||||
puts 'refresh received' if refresh_received != false
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
puts 'no command found, wait'
|
||||
|
||||
refresh_received = false
|
||||
wait_start_time = Time.now
|
||||
|
||||
# wait for a minute or until a refresh it set in the database as a sign new data has arrived
|
||||
|
||||
while Time.now < wait_start_time + 60 and refresh_received == false
|
||||
|
||||
sleep 1
|
||||
|
||||
refresh_received = @bot_dbaccess.checkRefresh
|
||||
puts 'refresh received' if refresh_received != false
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def process_command( cmd )
|
||||
|
||||
if cmd != nil
|
||||
cmd.commandlines.each do |command_line|
|
||||
case command_line.action.upcase
|
||||
when "MOVE ABSOLUTE"
|
||||
$bot_hardware.moveAbsolute(command_line.coord_x, command_line.coord_y, command_line.coord_z)
|
||||
when "MOVE RELATIVE"
|
||||
$bot_hardware.moveRelative(command_line.coord_x, command_line.coord_y, command_line.coord_z)
|
||||
when "HOME X"
|
||||
$bot_hardware.moveHomeX
|
||||
when "HOME Y"
|
||||
$bot_hardware.moveHomeY
|
||||
when "HOME Z"
|
||||
$bot_hardware.moveHomeZ
|
||||
when "SET SPEED"
|
||||
$bot_hardware.setSpeed(command_line.speed)
|
||||
end
|
||||
end
|
||||
else
|
||||
sleep 0.1
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,78 @@
|
|||
# This class is dedicated to retrieving and inserting commands into the command queue for the farm bot
|
||||
|
||||
require 'bson'
|
||||
require 'mongo'
|
||||
require 'mongoid'
|
||||
|
||||
require './app/models/command.rb'
|
||||
|
||||
class CommandQueue
|
||||
|
||||
def initialize
|
||||
Mongoid.load!("config/mongo.yml", :development)
|
||||
@last_command_retrieved = nil
|
||||
@refresh_value = 0
|
||||
@refresh_value_new = 0
|
||||
|
||||
@new_command = nil
|
||||
end
|
||||
|
||||
def create_new_command(scheduled_time)
|
||||
@new_command = Command.new
|
||||
@new_command.scheduled_time = scheduled_time
|
||||
end
|
||||
|
||||
def add_command_line(action, x = 0, y = 0, z = 0, speed = 0, amount = 0)
|
||||
if @new_command != nil
|
||||
line = Commandline.new
|
||||
line.action = action
|
||||
line.coord_x = x
|
||||
line.coord_y = y
|
||||
line.coord_z = z
|
||||
line.speed = speed
|
||||
line.amount = amount
|
||||
if @new_command.commandlines == nil
|
||||
@new_command.commandlines = [ line ]
|
||||
else
|
||||
@new_command.commandlines << line
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def save_new_command
|
||||
if @new_command != nil
|
||||
@new_command.status = 'test'
|
||||
@new_command.save
|
||||
end
|
||||
increment_refresh
|
||||
end
|
||||
|
||||
def get_command_to_execute
|
||||
@last_command_retrieved = Command.where(:status => 'test', :scheduled_time.ne => nil).order_by([:scheduled_time,:asc]).first
|
||||
@last_command_retrieved
|
||||
end
|
||||
|
||||
def set_command_to_execute_status(new_status)
|
||||
if @last_command_retrieved != nil
|
||||
@last_command_retrieved.status = new_status
|
||||
@last_command_retrieved.save
|
||||
end
|
||||
end
|
||||
|
||||
def check_refresh
|
||||
r = Refresh.where(:name => 'FarmBotControllerSchedule').first_or_initialize
|
||||
@refresh_value_new = r.value.to_i
|
||||
return @refresh_value_new != @refresh_value
|
||||
end
|
||||
|
||||
def save_refresh
|
||||
@refresh_value = @refresh_value_new
|
||||
end
|
||||
|
||||
def increment_refresh
|
||||
r = Refresh.where(:name => 'FarmBotControllerSchedule').first_or_initialize
|
||||
r.value = r.value.to_i + 1
|
||||
r.save
|
||||
end
|
||||
|
||||
end
|
|
@ -124,47 +124,4 @@ class DbAccess
|
|||
r.save
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#sched = ScheduleManagement.new
|
||||
|
||||
|
||||
#c = FarmBotCommand.new
|
||||
#c.command_name = 'MOVE ABSOLUTE'
|
||||
#c.status = 'test'
|
||||
#c.save
|
||||
|
||||
#sched.test
|
||||
|
||||
#puts FarmBotCommand.first.inspect
|
||||
#puts FarmBotCommand.count
|
||||
|
||||
#Mongoid.load!("config/mongo.yml", :development)
|
||||
|
||||
|
||||
#puts FarmBotCommand.where(:status => 'test').count
|
||||
|
||||
#c = FarmBotCommand.where(:status => 'test').first
|
||||
|
||||
|
||||
|
||||
#c = Command.new
|
||||
#puts c.id
|
||||
#c.status = 'test'
|
||||
#c.scheduled_time = DateTime.now
|
||||
#c.save
|
||||
|
||||
#puts FarmBotCommand.where(:status => 'test').count
|
||||
|
||||
#c = FarmBotCommand.where(:status => 'test', :scheduled_time.ne => nil).order_by([:scheduled_time,:asc]).count
|
||||
#c = sched.getFirstCommand
|
||||
#if c == nil
|
||||
# puts 'niets gevonden'
|
||||
#else
|
||||
# puts 'gevonden'
|
||||
# puts c.id
|
||||
# puts c.scheduled_time
|
||||
#end
|
||||
#puts c
|
||||
|
||||
|
||||
end
|
|
@ -1,22 +0,0 @@
|
|||
require 'FarmBotControlInterface'
|
||||
|
||||
# specific implementation to control the bot using marlin software on arduino and G-Code
|
||||
|
||||
class FarmBotControlInterface < FarmBotControlInterfaceAbstract
|
||||
|
||||
def initialize
|
||||
#serialPort = ...
|
||||
end
|
||||
|
||||
def moveTo( X, Y, Z )
|
||||
#serialPort.Send("G21 X#{X} Y#{Y} Z#{Z}")
|
||||
end
|
||||
|
||||
def moveHome
|
||||
#serialPort.Send("G20")
|
||||
end
|
||||
|
||||
def setSpeed( speed )
|
||||
end
|
||||
end
|
||||
|
|
@ -3,337 +3,337 @@ require 'firmata'
|
|||
|
||||
class HardwareInterface
|
||||
|
||||
def initialize
|
||||
@pos_x = 0.0
|
||||
@pos_y = 0.0
|
||||
@pos_z = 0.0
|
||||
|
||||
# should come from configuration:
|
||||
@move_home_timeout = 3 # seconds after which home command is aborted
|
||||
@sleep_after_pin_set = 0.005
|
||||
@sleep_after_enable = 0.001
|
||||
def initialize
|
||||
@pos_x = 0.0
|
||||
@pos_y = 0.0
|
||||
@pos_z = 0.0
|
||||
|
||||
# should come from configuration:
|
||||
@move_home_timeout = 3 # seconds after which home command is aborted
|
||||
@sleep_after_pin_set = 0.005
|
||||
@sleep_after_enable = 0.001
|
||||
|
||||
@invert_axis_x = false
|
||||
@invert_axis_y = false
|
||||
@invert_axis_z = false
|
||||
@invert_axis_x = false
|
||||
@invert_axis_y = false
|
||||
@invert_axis_z = false
|
||||
|
||||
@steps_per_unit_x = 10 # steps per milimeter for example
|
||||
@steps_per_unit_y = 10
|
||||
@steps_per_unit_z = 10
|
||||
|
||||
@boardDevice = "/dev/ttyACM0"
|
||||
@steps_per_unit_x = 10 # steps per milimeter for example
|
||||
@steps_per_unit_y = 10
|
||||
@steps_per_unit_z = 10
|
||||
|
||||
@boardDevice = "/dev/ttyACM0"
|
||||
|
||||
@pin_led = 13
|
||||
@pin_led = 13
|
||||
|
||||
@pin_stp_x = 54
|
||||
@pin_dir_x = 55
|
||||
@pin_enb_x = 38
|
||||
@pin_stp_x = 54
|
||||
@pin_dir_x = 55
|
||||
@pin_enb_x = 38
|
||||
|
||||
@pin_stp_y = 60
|
||||
@pin_dir_y = 61
|
||||
@pin_enb_y = 56
|
||||
@pin_stp_y = 60
|
||||
@pin_dir_y = 61
|
||||
@pin_enb_y = 56
|
||||
|
||||
@pin_stp_z = 46
|
||||
@pin_dir_z = 48
|
||||
@pin_enb_z = 62
|
||||
@pin_stp_z = 46
|
||||
@pin_dir_z = 48
|
||||
@pin_enb_z = 62
|
||||
|
||||
@pin_min_x = 3
|
||||
@pin_max_x = 2
|
||||
@pin_min_x = 3
|
||||
@pin_max_x = 2
|
||||
|
||||
@pin_min_y = 14
|
||||
@pin_max_y = 15
|
||||
@pin_min_y = 14
|
||||
@pin_max_y = 15
|
||||
|
||||
@pin_min_z = 18
|
||||
@pin_max_z = 19
|
||||
|
||||
@board = Firmata::Board.new @boardDevice
|
||||
@board.connect
|
||||
|
||||
# set motor driver pins to output and set enables for the drivers to off
|
||||
|
||||
@board.set_pin_mode(@pin_enb_x, Firmata::Board::OUTPUT)
|
||||
@board.set_pin_mode(@pin_dir_x, Firmata::Board::OUTPUT)
|
||||
@board.set_pin_mode(@pin_stp_x, Firmata::Board::OUTPUT)
|
||||
@pin_min_z = 18
|
||||
@pin_max_z = 19
|
||||
|
||||
@board = Firmata::Board.new @boardDevice
|
||||
@board.connect
|
||||
|
||||
# set motor driver pins to output and set enables for the drivers to off
|
||||
|
||||
@board.set_pin_mode(@pin_enb_x, Firmata::Board::OUTPUT)
|
||||
@board.set_pin_mode(@pin_dir_x, Firmata::Board::OUTPUT)
|
||||
@board.set_pin_mode(@pin_stp_x, Firmata::Board::OUTPUT)
|
||||
|
||||
@board.set_pin_mode(@pin_enb_y, Firmata::Board::OUTPUT)
|
||||
@board.set_pin_mode(@pin_dir_y, Firmata::Board::OUTPUT)
|
||||
@board.set_pin_mode(@pin_stp_y, Firmata::Board::OUTPUT)
|
||||
@board.set_pin_mode(@pin_enb_y, Firmata::Board::OUTPUT)
|
||||
@board.set_pin_mode(@pin_dir_y, Firmata::Board::OUTPUT)
|
||||
@board.set_pin_mode(@pin_stp_y, Firmata::Board::OUTPUT)
|
||||
|
||||
@board.set_pin_mode(@pin_enb_z, Firmata::Board::OUTPUT)
|
||||
@board.set_pin_mode(@pin_dir_z, Firmata::Board::OUTPUT)
|
||||
@board.set_pin_mode(@pin_stp_z, Firmata::Board::OUTPUT)
|
||||
@board.set_pin_mode(@pin_enb_z, Firmata::Board::OUTPUT)
|
||||
@board.set_pin_mode(@pin_dir_z, Firmata::Board::OUTPUT)
|
||||
@board.set_pin_mode(@pin_stp_z, Firmata::Board::OUTPUT)
|
||||
|
||||
@board.digital_write(@pin_enb_x, Firmata::Board::HIGH)
|
||||
@board.digital_write(@pin_enb_y, Firmata::Board::HIGH)
|
||||
@board.digital_write(@pin_enb_z, Firmata::Board::HIGH)
|
||||
@board.digital_write(@pin_enb_x, Firmata::Board::HIGH)
|
||||
@board.digital_write(@pin_enb_y, Firmata::Board::HIGH)
|
||||
@board.digital_write(@pin_enb_z, Firmata::Board::HIGH)
|
||||
|
||||
# set the end stop pins to input
|
||||
# set the end stop pins to input
|
||||
|
||||
@board.set_pin_mode(@pin_min_x, Firmata::Board::INPUT)
|
||||
@board.set_pin_mode(@pin_min_y, Firmata::Board::INPUT)
|
||||
@board.set_pin_mode(@pin_min_z, Firmata::Board::INPUT)
|
||||
@board.set_pin_mode(@pin_min_x, Firmata::Board::INPUT)
|
||||
@board.set_pin_mode(@pin_min_y, Firmata::Board::INPUT)
|
||||
@board.set_pin_mode(@pin_min_z, Firmata::Board::INPUT)
|
||||
|
||||
@board.set_pin_mode(@pin_max_x, Firmata::Board::INPUT)
|
||||
@board.set_pin_mode(@pin_max_y, Firmata::Board::INPUT)
|
||||
@board.set_pin_mode(@pin_max_z, Firmata::Board::INPUT)
|
||||
@board.set_pin_mode(@pin_max_x, Firmata::Board::INPUT)
|
||||
@board.set_pin_mode(@pin_max_y, Firmata::Board::INPUT)
|
||||
@board.set_pin_mode(@pin_max_z, Firmata::Board::INPUT)
|
||||
|
||||
@board.toggle_pin_reporting(@pin_min_x)
|
||||
@board.toggle_pin_reporting(@pin_min_y)
|
||||
@board.toggle_pin_reporting(@pin_min_z)
|
||||
@board.toggle_pin_reporting(@pin_min_x)
|
||||
@board.toggle_pin_reporting(@pin_min_y)
|
||||
@board.toggle_pin_reporting(@pin_min_z)
|
||||
|
||||
@board.toggle_pin_reporting(@pin_max_x)
|
||||
@board.toggle_pin_reporting(@pin_max_y)
|
||||
@board.toggle_pin_reporting(@pin_max_z)
|
||||
@board.toggle_pin_reporting(@pin_max_x)
|
||||
@board.toggle_pin_reporting(@pin_max_y)
|
||||
@board.toggle_pin_reporting(@pin_max_z)
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
# move the bot to the home position
|
||||
# move the bot to the home position
|
||||
|
||||
def moveHomeX
|
||||
moveHome(@pin_enb_x, @pin_dir_x, @pin_stp_x, @pin_min_x, @invert_axis_x)
|
||||
@pos_x = 0
|
||||
end
|
||||
def moveHomeX
|
||||
moveHome(@pin_enb_x, @pin_dir_x, @pin_stp_x, @pin_min_x, @invert_axis_x)
|
||||
@pos_x = 0
|
||||
end
|
||||
|
||||
def moveHomeY
|
||||
moveHome(@pin_enb_y, @pin_dir_y, @pin_stp_y, @pin_min_y, @invert_axis_y)
|
||||
@pos_y = 0
|
||||
end
|
||||
def moveHomeY
|
||||
moveHome(@pin_enb_y, @pin_dir_y, @pin_stp_y, @pin_min_y, @invert_axis_y)
|
||||
@pos_y = 0
|
||||
end
|
||||
|
||||
def moveHomeZ
|
||||
moveHome(@pin_enb_z, @pin_dir_z, @pin_stp_z, @pin_min_z, @invert_axis_z)
|
||||
@pos_z = 0
|
||||
end
|
||||
def moveHomeZ
|
||||
moveHome(@pin_enb_z, @pin_dir_z, @pin_stp_z, @pin_min_z, @invert_axis_z)
|
||||
@pos_z = 0
|
||||
end
|
||||
|
||||
def setSpeed( speed )
|
||||
|
||||
end
|
||||
def setSpeed( speed )
|
||||
|
||||
end
|
||||
|
||||
def moveHome(pin_enb, pin_dir, pin_stp, pin_min, invert_axis)
|
||||
def moveHome(pin_enb, pin_dir, pin_stp, pin_min, invert_axis)
|
||||
|
||||
# set the direction and enable
|
||||
# set the direction and enable
|
||||
|
||||
@board.digital_write(pin_enb, Firmata::Board::LOW)
|
||||
sleep @sleep_after_enable
|
||||
@board.digital_write(pin_enb, Firmata::Board::LOW)
|
||||
sleep @sleep_after_enable
|
||||
|
||||
if invert_axis == false
|
||||
@board.digital_write(pin_dir, Firmata::Board::LOW)
|
||||
else
|
||||
@board.digital_write(pin_dir, Firmata::Board::HIGH)
|
||||
end
|
||||
sleep @sleep_after_pin_set
|
||||
if invert_axis == false
|
||||
@board.digital_write(pin_dir, Firmata::Board::LOW)
|
||||
else
|
||||
@board.digital_write(pin_dir, Firmata::Board::HIGH)
|
||||
end
|
||||
sleep @sleep_after_pin_set
|
||||
|
||||
start = Time.now
|
||||
home = 0
|
||||
start = Time.now
|
||||
home = 0
|
||||
|
||||
# keep setting pulses at the step pin until the end stop is reached of a time is reached
|
||||
# keep setting pulses at the step pin until the end stop is reached of a time is reached
|
||||
|
||||
while home == 0 do
|
||||
while home == 0 do
|
||||
|
||||
@board.read_and_process
|
||||
span = Time.now - start
|
||||
|
||||
if span > @move_home_timeout
|
||||
home = 1
|
||||
puts 'move home timed out'
|
||||
end
|
||||
@board.read_and_process
|
||||
span = Time.now - start
|
||||
|
||||
if span > @move_home_timeout
|
||||
home = 1
|
||||
puts 'move home timed out'
|
||||
end
|
||||
|
||||
if @board.pins[pin_min].value == 1
|
||||
home = 1
|
||||
puts 'end stop reached'
|
||||
end
|
||||
if @board.pins[pin_min].value == 1
|
||||
home = 1
|
||||
puts 'end stop reached'
|
||||
end
|
||||
|
||||
if home == 0
|
||||
@board.digital_write(pin_stp, Firmata::Board::HIGH)
|
||||
sleep @sleep_after_pin_set
|
||||
@board.digital_write(pin_stp, Firmata::Board::LOW)
|
||||
sleep @sleep_after_pin_set
|
||||
end
|
||||
end
|
||||
if home == 0
|
||||
@board.digital_write(pin_stp, Firmata::Board::HIGH)
|
||||
sleep @sleep_after_pin_set
|
||||
@board.digital_write(pin_stp, Firmata::Board::LOW)
|
||||
sleep @sleep_after_pin_set
|
||||
end
|
||||
end
|
||||
|
||||
# disavle motor driver
|
||||
@board.digital_write(pin_dir, Firmata::Board::LOW)
|
||||
# disavle motor driver
|
||||
@board.digital_write(pin_dir, Firmata::Board::LOW)
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# move the bot to the give coordinates
|
||||
# move the bot to the give coordinates
|
||||
|
||||
def moveAbsolute( coord_x, coord_y, coord_z)
|
||||
def moveAbsolute( coord_x, coord_y, coord_z)
|
||||
|
||||
puts '**move absolute **'
|
||||
puts '**move absolute **'
|
||||
|
||||
# calculate the number of steps for the motors to do
|
||||
# calculate the number of steps for the motors to do
|
||||
|
||||
steps_x = (coord_x - @pos_x) * @steps_per_unit_x
|
||||
steps_y = (coord_y - @pos_y) * @steps_per_unit_y
|
||||
steps_z = (coord_z - @pos_z) * @steps_per_unit_z
|
||||
steps_x = (coord_x - @pos_x) * @steps_per_unit_x
|
||||
steps_y = (coord_y - @pos_y) * @steps_per_unit_y
|
||||
steps_z = (coord_z - @pos_z) * @steps_per_unit_z
|
||||
|
||||
puts "x steps #{steps_x}"
|
||||
puts "y steps #{steps_y}"
|
||||
puts "z steps #{steps_z}"
|
||||
puts "x steps #{steps_x}"
|
||||
puts "y steps #{steps_y}"
|
||||
puts "z steps #{steps_z}"
|
||||
|
||||
moveSteps( steps_x, steps_y, steps_z )
|
||||
moveSteps( steps_x, steps_y, steps_z )
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
# move the bot a number of units starting from the current position
|
||||
# move the bot a number of units starting from the current position
|
||||
|
||||
def moveRelative( amount_x, amount_y, amount_z)
|
||||
def moveRelative( amount_x, amount_y, amount_z)
|
||||
|
||||
puts '**move relative **'
|
||||
puts '**move relative **'
|
||||
|
||||
# calculate the number of steps for the motors to do
|
||||
# calculate the number of steps for the motors to do
|
||||
|
||||
steps_x = amount_x * @steps_per_unit_x
|
||||
steps_y = amount_y * @steps_per_unit_y
|
||||
steps_z = amount_z * @steps_per_unit_z
|
||||
steps_x = amount_x * @steps_per_unit_x
|
||||
steps_y = amount_y * @steps_per_unit_y
|
||||
steps_z = amount_z * @steps_per_unit_z
|
||||
|
||||
puts "x steps #{steps_x}"
|
||||
puts "y steps #{steps_y}"
|
||||
puts "z steps #{steps_z}"
|
||||
|
||||
moveSteps( steps_x, steps_y, steps_z )
|
||||
puts "x steps #{steps_x}"
|
||||
puts "y steps #{steps_y}"
|
||||
puts "z steps #{steps_z}"
|
||||
|
||||
moveSteps( steps_x, steps_y, steps_z )
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
def moveSteps( steps_x, steps_y, steps_z)
|
||||
|
||||
puts '**move steps **'
|
||||
puts "x #{steps_x}"
|
||||
puts "y #{steps_y}"
|
||||
puts "z #{steps_z}"
|
||||
|
||||
# set the direction and the enable bit for the motor drivers
|
||||
|
||||
if (steps_x < 0 and @invert_axis_x == false) or (steps_x > 0 and @invert_axis_x == true)
|
||||
@board.digital_write(@pin_enb_x, Firmata::Board::LOW)
|
||||
@board.digital_write(@pin_dir_x, Firmata::Board::LOW)
|
||||
end
|
||||
|
||||
if (steps_x > 0 and @invert_axis_x == false) or (steps_x < 0 and @invert_axis_x == true)
|
||||
@board.digital_write(@pin_enb_x, Firmata::Board::LOW)
|
||||
@board.digital_write(@pin_dir_x, Firmata::Board::HIGH)
|
||||
end
|
||||
|
||||
if (steps_y < 0 and @invert_axis_y == false) or (steps_y > 0 and @invert_axis_y == true)
|
||||
@board.digital_write(@pin_enb_y, Firmata::Board::LOW)
|
||||
@sleep_after_enable
|
||||
@board.digital_write(@pin_dir_y, Firmata::Board::LOW)
|
||||
sleep @sleep_after_pin_set
|
||||
end
|
||||
|
||||
if (steps_y > 0 and @invert_axis_y == false) or (steps_y < 0 and @invert_axis_y == true)
|
||||
@board.digital_write(@pin_enb_y, Firmata::Board::LOW)
|
||||
@sleep_after_enable
|
||||
@board.digital_write(@pin_dir_y, Firmata::Board::HIGH)
|
||||
sleep @sleep_after_pin_set
|
||||
end
|
||||
|
||||
if (steps_z < 0 and @invert_axis_z == false) or (steps_z > 0 and @invert_axis_z == true)
|
||||
@board.digital_write(@pin_enb_z, Firmata::Board::LOW)
|
||||
@sleep_after_enable
|
||||
@board.digital_write(@pin_dir_z, Firmata::Board::LOW)
|
||||
@sleep_after_pin_set
|
||||
end
|
||||
|
||||
if (steps_z > 0 and @invert_axis_z == false) or (steps_z < 0 and @invert_axis_z == true)
|
||||
@board.digital_write(@pin_enb_z, Firmata::Board::LOW)
|
||||
@board.digital_write(@pin_dir_z, Firmata::Board::HIGH)
|
||||
@sleep_after_pin_set
|
||||
end
|
||||
|
||||
# make the steps positive numbers
|
||||
|
||||
nr_steps_x = steps_x.abs
|
||||
nr_steps_y = steps_y.abs
|
||||
nr_steps_z = steps_z.abs
|
||||
|
||||
# loop until all steps are done
|
||||
|
||||
while nr_steps_x > 0 or nr_steps_y > 0 or nr_steps_z > 0 do
|
||||
|
||||
# read all input pins and check the end stops
|
||||
|
||||
@board.read_and_process
|
||||
|
||||
#puts "x min = #{@board.pins[@pin_min_x].value} | x max = #{@board.pins[@pin_max_x].value} "
|
||||
#puts "y min = #{@board.pins[@pin_min_y].value} | y max = #{@board.pins[@pin_max_y].value} "
|
||||
#puts "z min = #{@board.pins[@pin_min_z].value} | z max = #{@board.pins[@pin_max_z].value} "
|
||||
|
||||
if @board.pins[@pin_min_x].value == 1 and steps_x < 0
|
||||
nr_steps_x = 0
|
||||
@pos_x = 0
|
||||
puts 'end stop min x'
|
||||
end
|
||||
|
||||
if @board.pins[@pin_max_x].value == 1 and steps_x > 0
|
||||
nr_steps_x = 0
|
||||
puts 'end stop max x'
|
||||
end
|
||||
|
||||
if @board.pins[@pin_min_y].value == 1 and steps_y < 0
|
||||
nr_steps_y = 0
|
||||
@pos_y = 0
|
||||
puts 'end stop min y'
|
||||
end
|
||||
|
||||
if @board.pins[@pin_max_y].value == 1 and steps_y > 0
|
||||
nr_steps_y = 0
|
||||
puts 'end stop max y'
|
||||
end
|
||||
|
||||
if @board.pins[@pin_min_z].value == 1 and steps_z < 0
|
||||
nr_steps_z = 0
|
||||
@pos_z = 0
|
||||
puts 'end stop min z'
|
||||
end
|
||||
|
||||
if @board.pins[@pin_max_z].value == 1 and steps_z > 0
|
||||
nr_steps_z = 0
|
||||
puts 'end stop max z'
|
||||
end
|
||||
|
||||
# send the step pulses to the motor drivers
|
||||
|
||||
if nr_steps_x > 0
|
||||
@board.digital_write(@pin_stp_x, Firmata::Board::HIGH)
|
||||
sleep @sleep_after_pin_set
|
||||
@board.digital_write(@pin_stp_x, Firmata::Board::LOW)
|
||||
sleep @sleep_after_pin_set
|
||||
@pos_x += 1 / @steps_per_unit_x
|
||||
nr_steps_x -= 1
|
||||
end
|
||||
|
||||
if nr_steps_y > 0
|
||||
@board.digital_write(@pin_stp_y, Firmata::Board::HIGH)
|
||||
sleep @sleep_after_pin_set
|
||||
@board.digital_write(@pin_stp_y, Firmata::Board::LOW)
|
||||
sleep @sleep_after_pin_set
|
||||
@pos_y += 1 / @steps_per_unit_y
|
||||
nr_steps_y -= 1
|
||||
|
||||
end
|
||||
|
||||
if nr_steps_z > 0
|
||||
@board.digital_write(@pin_stp_z, Firmata::Board::HIGH)
|
||||
sleep @sleep_after_pin_set
|
||||
@board.digital_write(@pin_stp_z, Firmata::Board::LOW)
|
||||
sleep @sleep_after_pin_set
|
||||
@pos_z += 1 / @steps_per_unit_z
|
||||
nr_steps_z -= 1
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# disable motor drivers
|
||||
|
||||
@board.digital_write(@pin_enb_x, Firmata::Board::HIGH)
|
||||
@board.digital_write(@pin_enb_y, Firmata::Board::HIGH)
|
||||
@board.digital_write(@pin_enb_z, Firmata::Board::HIGH)
|
||||
|
||||
#while (X - pos_X).abs < 1/steps_per_unit_X
|
||||
|
||||
puts '*move done*'
|
||||
|
||||
end
|
||||
def moveSteps( steps_x, steps_y, steps_z)
|
||||
|
||||
puts '**move steps **'
|
||||
puts "x #{steps_x}"
|
||||
puts "y #{steps_y}"
|
||||
puts "z #{steps_z}"
|
||||
|
||||
# set the direction and the enable bit for the motor drivers
|
||||
|
||||
if (steps_x < 0 and @invert_axis_x == false) or (steps_x > 0 and @invert_axis_x == true)
|
||||
@board.digital_write(@pin_enb_x, Firmata::Board::LOW)
|
||||
@board.digital_write(@pin_dir_x, Firmata::Board::LOW)
|
||||
end
|
||||
|
||||
if (steps_x > 0 and @invert_axis_x == false) or (steps_x < 0 and @invert_axis_x == true)
|
||||
@board.digital_write(@pin_enb_x, Firmata::Board::LOW)
|
||||
@board.digital_write(@pin_dir_x, Firmata::Board::HIGH)
|
||||
end
|
||||
|
||||
if (steps_y < 0 and @invert_axis_y == false) or (steps_y > 0 and @invert_axis_y == true)
|
||||
@board.digital_write(@pin_enb_y, Firmata::Board::LOW)
|
||||
@sleep_after_enable
|
||||
@board.digital_write(@pin_dir_y, Firmata::Board::LOW)
|
||||
sleep @sleep_after_pin_set
|
||||
end
|
||||
|
||||
if (steps_y > 0 and @invert_axis_y == false) or (steps_y < 0 and @invert_axis_y == true)
|
||||
@board.digital_write(@pin_enb_y, Firmata::Board::LOW)
|
||||
@sleep_after_enable
|
||||
@board.digital_write(@pin_dir_y, Firmata::Board::HIGH)
|
||||
sleep @sleep_after_pin_set
|
||||
end
|
||||
|
||||
if (steps_z < 0 and @invert_axis_z == false) or (steps_z > 0 and @invert_axis_z == true)
|
||||
@board.digital_write(@pin_enb_z, Firmata::Board::LOW)
|
||||
@sleep_after_enable
|
||||
@board.digital_write(@pin_dir_z, Firmata::Board::LOW)
|
||||
@sleep_after_pin_set
|
||||
end
|
||||
|
||||
if (steps_z > 0 and @invert_axis_z == false) or (steps_z < 0 and @invert_axis_z == true)
|
||||
@board.digital_write(@pin_enb_z, Firmata::Board::LOW)
|
||||
@board.digital_write(@pin_dir_z, Firmata::Board::HIGH)
|
||||
@sleep_after_pin_set
|
||||
end
|
||||
|
||||
# make the steps positive numbers
|
||||
|
||||
nr_steps_x = steps_x.abs
|
||||
nr_steps_y = steps_y.abs
|
||||
nr_steps_z = steps_z.abs
|
||||
|
||||
# loop until all steps are done
|
||||
|
||||
while nr_steps_x > 0 or nr_steps_y > 0 or nr_steps_z > 0 do
|
||||
|
||||
# read all input pins and check the end stops
|
||||
|
||||
@board.read_and_process
|
||||
|
||||
#puts "x min = #{@board.pins[@pin_min_x].value} | x max = #{@board.pins[@pin_max_x].value} "
|
||||
#puts "y min = #{@board.pins[@pin_min_y].value} | y max = #{@board.pins[@pin_max_y].value} "
|
||||
#puts "z min = #{@board.pins[@pin_min_z].value} | z max = #{@board.pins[@pin_max_z].value} "
|
||||
|
||||
if @board.pins[@pin_min_x].value == 1 and steps_x < 0
|
||||
nr_steps_x = 0
|
||||
@pos_x = 0
|
||||
puts 'end stop min x'
|
||||
end
|
||||
|
||||
if @board.pins[@pin_max_x].value == 1 and steps_x > 0
|
||||
nr_steps_x = 0
|
||||
puts 'end stop max x'
|
||||
end
|
||||
|
||||
if @board.pins[@pin_min_y].value == 1 and steps_y < 0
|
||||
nr_steps_y = 0
|
||||
@pos_y = 0
|
||||
puts 'end stop min y'
|
||||
end
|
||||
|
||||
if @board.pins[@pin_max_y].value == 1 and steps_y > 0
|
||||
nr_steps_y = 0
|
||||
puts 'end stop max y'
|
||||
end
|
||||
|
||||
if @board.pins[@pin_min_z].value == 1 and steps_z < 0
|
||||
nr_steps_z = 0
|
||||
@pos_z = 0
|
||||
puts 'end stop min z'
|
||||
end
|
||||
|
||||
if @board.pins[@pin_max_z].value == 1 and steps_z > 0
|
||||
nr_steps_z = 0
|
||||
puts 'end stop max z'
|
||||
end
|
||||
|
||||
# send the step pulses to the motor drivers
|
||||
|
||||
if nr_steps_x > 0
|
||||
@board.digital_write(@pin_stp_x, Firmata::Board::HIGH)
|
||||
sleep @sleep_after_pin_set
|
||||
@board.digital_write(@pin_stp_x, Firmata::Board::LOW)
|
||||
sleep @sleep_after_pin_set
|
||||
@pos_x += 1 / @steps_per_unit_x
|
||||
nr_steps_x -= 1
|
||||
end
|
||||
|
||||
if nr_steps_y > 0
|
||||
@board.digital_write(@pin_stp_y, Firmata::Board::HIGH)
|
||||
sleep @sleep_after_pin_set
|
||||
@board.digital_write(@pin_stp_y, Firmata::Board::LOW)
|
||||
sleep @sleep_after_pin_set
|
||||
@pos_y += 1 / @steps_per_unit_y
|
||||
nr_steps_y -= 1
|
||||
|
||||
end
|
||||
|
||||
if nr_steps_z > 0
|
||||
@board.digital_write(@pin_stp_z, Firmata::Board::HIGH)
|
||||
sleep @sleep_after_pin_set
|
||||
@board.digital_write(@pin_stp_z, Firmata::Board::LOW)
|
||||
sleep @sleep_after_pin_set
|
||||
@pos_z += 1 / @steps_per_unit_z
|
||||
nr_steps_z -= 1
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# disable motor drivers
|
||||
|
||||
@board.digital_write(@pin_enb_x, Firmata::Board::HIGH)
|
||||
@board.digital_write(@pin_enb_y, Firmata::Board::HIGH)
|
||||
@board.digital_write(@pin_enb_z, Firmata::Board::HIGH)
|
||||
|
||||
#while (X - pos_X).abs < 1/steps_per_unit_X
|
||||
|
||||
puts '*move done*'
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,83 +0,0 @@
|
|||
# FarmBot schedule keeper
|
||||
|
||||
# Get commands from the database and execute them on the hardware
|
||||
|
||||
require 'date'
|
||||
|
||||
#require './lib/dbaccess.rb'
|
||||
require './lib/controller.rb'
|
||||
#require './lib/filehandler.rb'
|
||||
#require "./lib/hardware/ramps.rb"
|
||||
|
||||
class Scheduler
|
||||
|
||||
def runFarmBot
|
||||
|
||||
check = $bot_dbaccess.checkRefresh
|
||||
|
||||
while $shutdown == 0 do
|
||||
|
||||
# keep checking the database for new data
|
||||
|
||||
puts 'checking schedule'
|
||||
command = $bot_dbaccess.getCommandToExecute
|
||||
$bot_dbaccess.saveRefresh
|
||||
|
||||
if command != nil
|
||||
|
||||
puts "command retrieved is scheduled for #{command.scheduled_time}"
|
||||
puts "curent time is #{Time.now}"
|
||||
|
||||
#scheduled_time = command.scheduled_time
|
||||
|
||||
if command.scheduled_time <= Time.now or command.scheduled_time == nil
|
||||
|
||||
# execute the command now and set the status to done
|
||||
puts 'execute command'
|
||||
|
||||
$bot_control.setCommand( command )
|
||||
$bot_control.runCycle
|
||||
$bot_dbaccess.setCommandToExecuteStatus('FINISHED')
|
||||
|
||||
else
|
||||
|
||||
puts 'wait for scheduled time or refresh'
|
||||
|
||||
refresh_received = false
|
||||
|
||||
wait_start_time = Time.now
|
||||
|
||||
# wait until the scheduled time has arrived, or wait for a minute or until a refresh it set in the database as a sign new data has arrived
|
||||
|
||||
while Time.now < wait_start_time + 60 and command.scheduled_time > Time.now - 1 and refresh_received == false
|
||||
|
||||
sleep 1
|
||||
|
||||
refresh_received = $bot_dbaccess.checkRefresh
|
||||
puts 'refresh received' if refresh_received != false
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
puts 'no command found, wait'
|
||||
|
||||
refresh_received = false
|
||||
wait_start_time = Time.now
|
||||
|
||||
# wait for a minute or until a refresh it set in the database as a sign new data has arrived
|
||||
|
||||
while Time.now < wait_start_time + 60 and refresh_received == false
|
||||
|
||||
sleep 1
|
||||
|
||||
refresh_received = $bot_dbaccess.checkRefresh
|
||||
puts 'refresh received' if refresh_received != false
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,15 @@
|
|||
|
||||
|
||||
require_relative 'skynet/skynet'
|
||||
|
||||
# The unfortunate use of globals in this project: The SocketIO library we use to
|
||||
# talk to skynet stores blocks as lambdas and calls them later under a different
|
||||
# context than that which they were defined. This means that even though we
|
||||
# define the .on() events within the `Device` class, self does NOT refer to the
|
||||
# device, but rather the current socket connection. Using a global is a quick
|
||||
# fix to ensure we always have easy access to the device. Pull requests welcome.
|
||||
|
||||
$skynet = Skynet.new
|
||||
|
||||
#TODO: Daemonize this script:
|
||||
#https://www.ruby-toolbox.com/categories/daemonizing
|
|
@ -0,0 +1,49 @@
|
|||
require 'securerandom'
|
||||
|
||||
module Credentials
|
||||
# Stores a references to the credentials yml file, which is used to persist
|
||||
# the user's skynet Token / ID across sessions. Returns String. parameterless
|
||||
def credentials_file
|
||||
'credentials.yml'
|
||||
end
|
||||
|
||||
# Returns Hash containing the a :uuid and :token key. Triggers the creation of
|
||||
# new credentials if the current ones are found to be invalid.
|
||||
def credentials
|
||||
if valid_credentials?
|
||||
return load_credentials
|
||||
else
|
||||
return create_credentials
|
||||
end
|
||||
end
|
||||
|
||||
# Validates that the credentials file has a :uuid and :token key. Returns Bool
|
||||
#
|
||||
def valid_credentials?
|
||||
if File.file?(credentials_file)
|
||||
cred = load_credentials
|
||||
return true if cred.has_key?(:uuid) && cred.has_key?(:token)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
# Uses the ruby securerandom library to make a new :uuid and :token. Also
|
||||
# registers with a new device :uuid and :token on skynet.im . Returns Hash
|
||||
# containing :uuid and :token key.
|
||||
def create_credentials
|
||||
hash = {
|
||||
uuid: (@uuid = SecureRandom.uuid),
|
||||
token: (@token = SecureRandom.hex)
|
||||
}
|
||||
`curl -s -X POST -d 'uuid=#{@uuid}&token=#{@token}&type=farmbot' \
|
||||
http://skynet.im/devices`
|
||||
File.open(credentials_file, 'w+') {|file| file.write(hash.to_yaml) }
|
||||
return hash
|
||||
end
|
||||
|
||||
### Loads the credentials file from disk and returns it as a ruby hash.
|
||||
def load_credentials
|
||||
return YAML.load(File.read(credentials_file))
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,75 @@
|
|||
# Get the JSON command, received through skynet, and send it to the farmbot command queue
|
||||
|
||||
require 'json'
|
||||
require './lib/database/commandqueue.rb'
|
||||
|
||||
class MessageHandler
|
||||
|
||||
def initialize
|
||||
@command_queue = CommandQueue.new
|
||||
@last_time_stamp = ''
|
||||
end
|
||||
|
||||
def handle_message(skynet, channel, message)
|
||||
|
||||
if message["message_type"].to_s.upcase == "SINGLE_COMMAND"
|
||||
|
||||
|
||||
# assuming a json message
|
||||
#{
|
||||
# "message_type" : "single_command",
|
||||
# "time_stamp" : 2001-01-01 01:01:01.001
|
||||
# "command" : {
|
||||
# "action" : "HOME X",
|
||||
# "x" : 1,
|
||||
# "y" : 2,
|
||||
# "z" : 3,
|
||||
# "speed" : "FAST",
|
||||
# "amount" : 5,
|
||||
# "delay" : 6
|
||||
# }
|
||||
#}
|
||||
|
||||
time_stamp = message['time_stamp']
|
||||
|
||||
if time_stamp != @last_time_stamp
|
||||
@last_time_stamp = time_stamp
|
||||
|
||||
# send the command to the queue
|
||||
delay = message['command']['delay']
|
||||
action = message['command']['action']
|
||||
x = message['command']['x']
|
||||
y = message['command']['y']
|
||||
z = message['command']['z']
|
||||
speed = message['command']['speed']
|
||||
amount = message['command']['amount']
|
||||
delay = message['command']['delay']
|
||||
|
||||
puts '[new command]'
|
||||
puts "received at #{Time.now}"
|
||||
puts "action = #{action}"
|
||||
puts "x = #{x}"
|
||||
puts "y = #{y}"
|
||||
puts "z = #{z}"
|
||||
puts "speed = #{speed}"
|
||||
puts "amount = #{amount}"
|
||||
puts "delay = #{delay}"
|
||||
|
||||
@command_queue.create_new_command(Time.now + delay.to_i)
|
||||
@command_queue.add_command_line(action, x.to_i, y.to_i, z.to_i, speed.to_s, amount.to_i)
|
||||
@command_queue.save_new_command
|
||||
|
||||
skynet.confirmed = false
|
||||
command =
|
||||
{
|
||||
:message_type => 'confirmation',
|
||||
:time_stamp => Time.now.to_f.to_s,
|
||||
:confirm_id => time_stamp
|
||||
}
|
||||
|
||||
skynet.send_message("44128811-8c59-11e3-b99a-11476114e05f", command)
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,50 @@
|
|||
require 'json'
|
||||
|
||||
require_relative 'credentials'
|
||||
require_relative 'web_socket'
|
||||
require_relative 'messagehandler.rb'
|
||||
|
||||
# The Device class is temporarily inheriting from Tim's HardwareInterface.
|
||||
# Eventually, we should merge the two projects, but this is good enough for now.
|
||||
class Skynet
|
||||
|
||||
include Credentials, WebSocket
|
||||
|
||||
attr_accessor :socket, :uuid, :token, :identified, :confirmed, :confirmation_id
|
||||
|
||||
# On instantiation #new sets the @uuid, @token variables, connects to skynet
|
||||
def initialize
|
||||
super
|
||||
identified = false
|
||||
creds = credentials
|
||||
@uuid = creds[:uuid]
|
||||
@token = creds[:token]
|
||||
@socket = SocketIO::Client::Simple.connect 'http://skynet.im:80'
|
||||
create_socket_events
|
||||
|
||||
@message_handler = MessageHandler.new
|
||||
end
|
||||
|
||||
def send_message(devices, message_hash )
|
||||
@socket.emit("message",{:devices => devices, :message => message_hash})
|
||||
end
|
||||
|
||||
# Acts as the entry point for message traffic captured from Skynet.im.
|
||||
# This method is a stub for now until I have time to merge into Tim's
|
||||
# controller code. Returns a MessageHandler object (a class yet created).
|
||||
def handle_message(channel, message)
|
||||
|
||||
if message.class.to_s == 'Hash'
|
||||
@message_handler.handle_message(self, channel, message)
|
||||
end
|
||||
|
||||
if message.class.to_s == 'String'
|
||||
message_hash = JSON.parse(message)
|
||||
@message_handler.handle_message(self, channel, message_hash)
|
||||
end
|
||||
|
||||
rescue
|
||||
raise "Runtime error while attempting to parse message: #{message}."
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,30 @@
|
|||
require 'socket.io-client-simple'
|
||||
|
||||
module WebSocket
|
||||
### Bootstraps all the events for skynet in the correct order. Returns Int.
|
||||
def create_socket_events
|
||||
#OTHER EVENTS: :identify, :identity, :ready, :disconnect, :message
|
||||
create_identify_event
|
||||
create_message_event
|
||||
end
|
||||
|
||||
#Handles self identification on skynet by responding to the :indentify with a
|
||||
#:identity event / credentials Hash.
|
||||
def create_identify_event
|
||||
@socket.on :identify do |data|
|
||||
self.emit :identity, {
|
||||
uuid: $skynet.uuid,
|
||||
token: $skynet.token,
|
||||
socketid: data['socketid']}
|
||||
$skynet.identified = true
|
||||
end
|
||||
end
|
||||
|
||||
### Routes all skynet messages to handle_event() for interpretation.
|
||||
def create_message_event
|
||||
@socket.on :message do |channel, message|
|
||||
$skynet.handle_message(channel, message)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
4
menu.rb
4
menu.rb
|
@ -1,8 +1,8 @@
|
|||
puts '[FarmBot Controller Menu]'
|
||||
puts 'starting up'
|
||||
|
||||
require './lib/dbaccess.rb'
|
||||
require './lib/filehandler.rb'
|
||||
require './lib/database/dbaccess.rb'
|
||||
require './lib/database/filehandler.rb'
|
||||
|
||||
#require './lib/controller.rb'
|
||||
#require "./lib/hardware/ramps.rb"
|
||||
|
|
32
runtime.rb
32
runtime.rb
|
@ -1,32 +0,0 @@
|
|||
# FarmBot Controller runtime control
|
||||
|
||||
# Get commands from the database and execute them on the hardware
|
||||
|
||||
puts '[FarmBot Controller]'
|
||||
puts 'starting up'
|
||||
|
||||
require 'date'
|
||||
|
||||
require './lib/dbaccess.rb'
|
||||
require './lib/controller.rb'
|
||||
require './lib/filehandler.rb'
|
||||
require "./lib/hardware/ramps.rb"
|
||||
require './lib/schedule.rb'
|
||||
|
||||
puts 'connecting to hardware'
|
||||
|
||||
$bot_control = Control.new
|
||||
$bot_hardware = HardwareInterface.new
|
||||
|
||||
puts 'connecting to database'
|
||||
|
||||
$bot_dbaccess = DbAccess.new
|
||||
$bot_schedule = Scheduler.new
|
||||
|
||||
$shutdown = 0
|
||||
|
||||
# controller loop
|
||||
|
||||
puts 'run'
|
||||
|
||||
$bot_schedule.runFarmBot
|
|
@ -0,0 +1,67 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Device do
|
||||
|
||||
let(:device) do
|
||||
Device.new
|
||||
end
|
||||
|
||||
describe '#credentials_file' do
|
||||
it 'returns name of the credentials YML file' do
|
||||
expect(device.credentials_file).to eq('credentials.yml')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#load_credentials' do
|
||||
it 'loads the credentials YML file' do
|
||||
expected = YAML.load(File.read(device.credentials_file))
|
||||
actual = device.load_credentials
|
||||
expect(expected).to eq(actual)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#create_credentials' do
|
||||
it 'creates device UUID' do
|
||||
old_uuid = device.uuid
|
||||
device.create_credentials
|
||||
new_uuid = device.uuid
|
||||
expect(old_uuid).to_not eq(new_uuid)
|
||||
end
|
||||
it 'creates device token' do
|
||||
old_token = device.token
|
||||
device.create_credentials
|
||||
new_token = device.token
|
||||
expect(old_token).to_not eq(new_token)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#valid_credentials?' do
|
||||
it 'ensures presence of UUID' do
|
||||
device.stub(:load_credentials) { {} }
|
||||
expect(device.load_credentials.key?(:uuid)).to be_false
|
||||
expect(device.valid_credentials?).to be_false
|
||||
end
|
||||
it 'ensures presence of token' do
|
||||
device.stub(:load_credentials) { {} }
|
||||
expect(device.load_credentials.key?(:token)).to be_false
|
||||
expect(device.valid_credentials?).to be_false
|
||||
end
|
||||
end
|
||||
|
||||
describe '#credentials' do
|
||||
it 'has a valid `UUID` key' do
|
||||
expect(device.credentials[:uuid]).to be_instance_of(String)
|
||||
end
|
||||
it 'has a valid `token` key' do
|
||||
expect(device.credentials[:token]).to be_instance_of(String)
|
||||
end
|
||||
|
||||
it 'creates a new set of credentials when invalid' do
|
||||
old_creds = device.credentials
|
||||
device.stub('valid_credentials?') { false }
|
||||
new_creds = device.credentials
|
||||
expect(old_creds).not_to eq(new_creds)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,26 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Device do
|
||||
|
||||
let(:device) do
|
||||
Device.new
|
||||
end
|
||||
|
||||
describe '#new' do
|
||||
it 'initializes a new Device instance' do
|
||||
expect(device).to be_kind_of(Device)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#uuid' do
|
||||
it 'returns a valid UUID' do
|
||||
expect(device.uuid).to be_kind_of(String)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#token' do
|
||||
it 'returns a valid token' do
|
||||
expect(device.token).to be_kind_of(String)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1 @@
|
|||
require File.expand_path('../spec_helper', __FILE__)
|
|
@ -0,0 +1,10 @@
|
|||
require 'simplecov'
|
||||
SimpleCov.start
|
||||
require 'rspec'
|
||||
|
||||
require File.expand_path('../../lib/farmbot-skynet', __FILE__)
|
||||
|
||||
RSpec.configure do |config|
|
||||
#
|
||||
end
|
||||
|
Loading…
Reference in New Issue