sending schedule to controller
parent
bd0ee43a79
commit
321eaf1a86
|
@ -0,0 +1,76 @@
|
|||
development:
|
||||
# Configure available database sessions. (required)
|
||||
sessions:
|
||||
# Defines the default session. (required)
|
||||
default:
|
||||
# Defines the name of the default database that Mongoid can connect to.
|
||||
# (required).
|
||||
database: farmbot_backend_development
|
||||
# Provides the hosts the default session can connect to. Must be an array
|
||||
# of host:port pairs. (required)
|
||||
hosts:
|
||||
- localhost:27017
|
||||
options:
|
||||
# Change the default write concern. (default = { w: 1 })
|
||||
# write:
|
||||
# w: 1
|
||||
|
||||
# Change the default consistency model to primary, secondary.
|
||||
# 'secondary' will send reads to secondaries, 'primary' sends everything
|
||||
# to master. (default: primary)
|
||||
# read: secondary_preferred
|
||||
|
||||
# How many times Moped should attempt to retry an operation after
|
||||
# failure. (default: 30)
|
||||
# max_retries: 30
|
||||
|
||||
# The time in seconds that Moped should wait before retrying an
|
||||
# operation on failure. (default: 1)
|
||||
# retry_interval: 1
|
||||
# Configure Mongoid specific options. (optional)
|
||||
options:
|
||||
# Enable the identity map, needed for eager loading. (default: false)
|
||||
# identity_map_enabled: false
|
||||
|
||||
# Includes the root model name in json serialization. (default: false)
|
||||
# include_root_in_json: false
|
||||
|
||||
# Include the _type field in serializaion. (default: false)
|
||||
# include_type_for_serialization: false
|
||||
|
||||
# Preload all models in development, needed when models use
|
||||
# inheritance. (default: false)
|
||||
# preload_models: false
|
||||
|
||||
# Protect id and type from mass assignment. (default: true)
|
||||
# protect_sensitive_fields: true
|
||||
|
||||
# Raise an error when performing a #find and the document is not found.
|
||||
# (default: true)
|
||||
# raise_not_found_error: true
|
||||
|
||||
# Raise an error when defining a scope with the same name as an
|
||||
# existing method. (default: false)
|
||||
# scope_overwrite_exception: false
|
||||
|
||||
# Skip the database version check, used when connecting to a db without
|
||||
# admin access. (default: false)
|
||||
# skip_version_check: false
|
||||
|
||||
# Use Active Support's time zone in conversions. (default: true)
|
||||
# use_activesupport_time_zone: true
|
||||
|
||||
# Ensure all times are UTC in the app side. (default: false)
|
||||
# use_utc: false
|
||||
test:
|
||||
sessions:
|
||||
default:
|
||||
database: farmbot_backend_test
|
||||
hosts:
|
||||
- localhost:27017
|
||||
options:
|
||||
read: primary
|
||||
# In the test environment we lower the retries and retry interval to
|
||||
# low amounts for fast failures.
|
||||
max_retries: 1
|
||||
retry_interval: 0
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
:uuid: 40b8cfdf-92e9-450d-b603-1e9efbbecf47
|
||||
:token: !binary |-
|
||||
MzIwMTc3M2IwZDA3N2NmM2ZlZmQ5ZDQwZjNiZGE1NmI=
|
|
@ -0,0 +1,71 @@
|
|||
|
||||
# ####################################
|
||||
# Send something to farmbot controller
|
||||
# ####################################
|
||||
|
||||
class FarmBotControllerComm
|
||||
|
||||
# send command to farmbot
|
||||
def send_single_command(action, x, y, z, amount, speed, delay)
|
||||
|
||||
$skynet.confirmed = false
|
||||
command =
|
||||
{
|
||||
:message_type => 'single_command',
|
||||
:time_stamp => Time.now.to_f.to_s,
|
||||
:command => {
|
||||
:action => action,
|
||||
:x => x,
|
||||
:y => y,
|
||||
:z => z,
|
||||
:speed => speed,
|
||||
:amount => amount,
|
||||
:delay => delay
|
||||
}}
|
||||
|
||||
|
||||
$skynet.send_message($farmbot_uuid, command)
|
||||
return wait_for_confirmation()
|
||||
|
||||
end
|
||||
|
||||
|
||||
# send schedule to farmbot
|
||||
def send_schedule(uuid, schedule)
|
||||
|
||||
$skynet.confirmed = false
|
||||
|
||||
sched_hash = schedule.to_hash
|
||||
sched_hash[:message_type] = 'crop_schedule_update'
|
||||
sched_hash[:time_stamp] = Time.now.to_f.to_s
|
||||
|
||||
puts sched_hash
|
||||
|
||||
$skynet.send_message(uuid, sched_hash)
|
||||
return wait_for_confirmation()
|
||||
|
||||
end
|
||||
|
||||
def wait_for_confirmation
|
||||
puts 'waiting for confirmation'
|
||||
count = 0
|
||||
while $skynet.confirmed != true and count < 10
|
||||
sleep 0.5
|
||||
print '.'
|
||||
count += 1
|
||||
end
|
||||
|
||||
puts ''
|
||||
|
||||
if $skynet.confirmed
|
||||
puts 'confirmation received'
|
||||
return true
|
||||
else
|
||||
puts 'confirmation timed out'
|
||||
sleep 2
|
||||
return false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,118 @@
|
|||
|
||||
#
|
||||
# Classes to make a schedule that can be transmitted to the bot
|
||||
#
|
||||
|
||||
class CropSchedule
|
||||
|
||||
attr_accessor :crop_id
|
||||
|
||||
def initialize
|
||||
@commands = Array.new
|
||||
end
|
||||
|
||||
def read_from_db(crop)
|
||||
@crop_id = crop.crop_id
|
||||
|
||||
crop.scheduled_commands.each do |dbcommand|
|
||||
|
||||
command = CropScheduleCommand.new
|
||||
command.read_from_db(dbcommand)
|
||||
add_command(command)
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
def to_hash
|
||||
command_nr = 0
|
||||
schedule_hash = {:crop_id => @crop_id, :commands => {} }
|
||||
commands = Hash.new
|
||||
@commands.each do |command|
|
||||
command_nr += 1
|
||||
commands["command_#{command_nr}"]= command.to_hash
|
||||
end
|
||||
schedule_hash[:commands] = commands
|
||||
return schedule_hash
|
||||
end
|
||||
|
||||
def add_command(command)
|
||||
|
||||
@commands << command
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class CropScheduleCommand
|
||||
|
||||
attr_accessor :scheduled_time
|
||||
|
||||
def initialize
|
||||
@command_lines = Array.new
|
||||
end
|
||||
|
||||
def read_from_db(command)
|
||||
puts command
|
||||
puts command.scheduled_time
|
||||
@scheduled_time = command.scheduled_time
|
||||
seq = 0
|
||||
|
||||
command.scheduled_command_lines.each do |dbline|
|
||||
|
||||
seq += 1
|
||||
line = CropScheduleCommandLine.new
|
||||
line.read_from_db(dbline, seq)
|
||||
add_command_line(line)
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
def to_hash
|
||||
sequence_nr = 0
|
||||
command_hash = {:scheduled_time => @scheduled_time, :command_lines => {} }
|
||||
command_lines = Hash.new
|
||||
@command_lines.each do |line|
|
||||
sequence_nr += 1
|
||||
line.sequence_nr = sequence_nr
|
||||
command_lines["command_line_#{sequence_nr}"]= line.to_hash
|
||||
end
|
||||
command_hash[:command_lines] = command_lines
|
||||
return command_hash
|
||||
end
|
||||
|
||||
def add_command_line(line)
|
||||
@command_lines << line
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class CropScheduleCommandLine
|
||||
|
||||
attr_accessor :action, :x, :y, :z, :amount, :speed, :sequence_nr
|
||||
|
||||
def read_from_db(line, sequence_nr)
|
||||
@action = line.action
|
||||
@x = line.coord_x
|
||||
@y = line.coord_y
|
||||
@z = line.coord_z
|
||||
@amount = line.amount
|
||||
@speed = line.speed
|
||||
@sequence_nr = sequence_nr
|
||||
end
|
||||
|
||||
def to_hash
|
||||
|
||||
line = {
|
||||
:action => @action ,
|
||||
:x => @x ,
|
||||
:y => @y ,
|
||||
:z => @z ,
|
||||
:amount => @amount ,
|
||||
:speed => @speed ,
|
||||
:sequence_nr => @sequence_nr
|
||||
}
|
||||
return line
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,157 @@
|
|||
require 'bson'
|
||||
require 'mongo'
|
||||
require 'mongoid'
|
||||
|
||||
# Data classes
|
||||
# This class is dedicated to retrieving and inserting commands into the schedule
|
||||
# queue for the farm bot Mongo is used as the database, Mongoid as the
|
||||
# databasemapper
|
||||
|
||||
class Command
|
||||
include Mongoid::Document
|
||||
|
||||
embeds_many :commandlines
|
||||
|
||||
field :plant_id
|
||||
field :crop_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
|
||||
|
||||
# Access class for the database
|
||||
|
||||
class DbAccess
|
||||
|
||||
def initialize
|
||||
Mongoid.load!("config/mongo.yml", :development)
|
||||
@last_command_retrieved = nil
|
||||
@refresh_value = 0
|
||||
@refresh_value_new = 0
|
||||
|
||||
@new_command = nil
|
||||
end
|
||||
|
||||
def test
|
||||
db_connection = Mongo::Connection.new
|
||||
db_farmbot = db_connection['farmbot_development']
|
||||
db_schedule = db_farmbot['schedule']
|
||||
|
||||
db_connection.database_names.each do |name|
|
||||
db = db_connection.db(name)
|
||||
db.collections.each do |collection|
|
||||
puts "#{name} - #{collection.name}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def create_new_command(scheduled_time, crop_id)
|
||||
@new_command = Command.new
|
||||
@new_command.scheduled_time = scheduled_time
|
||||
@new_command.crop_id = crop_id
|
||||
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 = 'scheduled'
|
||||
@new_command.save
|
||||
end
|
||||
increment_refresh
|
||||
end
|
||||
|
||||
def clear_schedule
|
||||
Command.where(
|
||||
:status => 'scheduled',
|
||||
:scheduled_time.ne => nil
|
||||
).order_by([:scheduled_time,:asc]).each do |command|
|
||||
|
||||
command.status = 'deleted'
|
||||
command.save
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
def clear_crop_schedule(crop_id)
|
||||
Command.where(
|
||||
:status => 'scheduled',
|
||||
:scheduled_time.ne => nil,
|
||||
:crop_id => crop_id
|
||||
).order_by([:scheduled_time,:asc]).each do |command|
|
||||
|
||||
command.status = 'deleted'
|
||||
command.save
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
def get_command_to_execute
|
||||
@last_command_retrieved = Command.where(
|
||||
:status => 'scheduled',
|
||||
: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
|
|
@ -0,0 +1,134 @@
|
|||
# This module holds our data definitions to store all basic plant and watering data
|
||||
# Mongo is used as the database, Mongoid as the databasemapper
|
||||
|
||||
require 'bson'
|
||||
require 'mongo'
|
||||
require 'mongoid'
|
||||
|
||||
#require 'bson_ext'
|
||||
|
||||
# The different farmbots are stored here
|
||||
|
||||
class FarmBot
|
||||
include Mongoid::Document
|
||||
|
||||
embeds_many :crops
|
||||
|
||||
field :active
|
||||
field :name
|
||||
field :environmental_coefficient
|
||||
field :uuid
|
||||
|
||||
# also needs user settings, security and whatsnot
|
||||
|
||||
end
|
||||
|
||||
# The list of crops tended by one farm bot. The crop is planted as a seed (age = 0) or when it has already sprouted.
|
||||
# Coordinates x, y and z are used to drive the robot to the right place
|
||||
# The age of maturing and harvesting should be customized to local conditions
|
||||
|
||||
class Crop
|
||||
include Mongoid::Document
|
||||
|
||||
embedded_in :farmbot
|
||||
embeds_many :grow_coefficients
|
||||
embeds_many :waterings
|
||||
embeds_many :historic_actions
|
||||
embeds_many :scheduled_commands
|
||||
|
||||
field :plant_type
|
||||
field :coord_x
|
||||
field :coord_y
|
||||
field :coord_z
|
||||
field :radius
|
||||
field :height
|
||||
field :status
|
||||
|
||||
field :date_at_planting
|
||||
field :age_at_planting
|
||||
field :age_at_fully_grown
|
||||
field :age_at_harvest
|
||||
|
||||
field :valid_data
|
||||
field :crop_id
|
||||
|
||||
end
|
||||
|
||||
# Coefficients are used by the evapotransporation system. It expresses the amount of water (mm/day) the plant needs for a good growth at a certain age
|
||||
# The values for the coefficient is the result of the local climate reference value multiplied with the
|
||||
# The age is represented as a precentage, where 100% is fully grown and 200% is ready for harvesting
|
||||
|
||||
# a typical curve for a crops. the Y axis is here a multiplication factor for the reference crops (fictional grass or alfalfa)
|
||||
#
|
||||
# 1.0 *****
|
||||
# *| **
|
||||
# * | ***
|
||||
# * | |
|
||||
# ** | |
|
||||
# 0.1 *** | |
|
||||
# | | |
|
||||
# 0% 100% 200%
|
||||
|
||||
class GrowCoefficient
|
||||
include Mongoid::Document
|
||||
|
||||
embedded_in :crop
|
||||
|
||||
field :age_in_percentage
|
||||
field :amount_water_manual
|
||||
|
||||
end
|
||||
|
||||
# These are the times when the robot is supposed to water the crop
|
||||
|
||||
class Watering
|
||||
include Mongoid::Document
|
||||
|
||||
embedded_in :crop
|
||||
|
||||
field :time
|
||||
field :percentage
|
||||
end
|
||||
|
||||
# A log of what happended to the plant. Waterings and rainfall are the most important probably
|
||||
|
||||
class HistoricAction
|
||||
include Mongoid::Document
|
||||
|
||||
embedded_in :crop
|
||||
|
||||
field :start_time
|
||||
field :stop_time
|
||||
field :action
|
||||
field :amount
|
||||
end
|
||||
|
||||
# This is the schedule for the next hours/days that the bot has to execute. This is synchronized to the bot.
|
||||
|
||||
class ScheduledCommand
|
||||
include Mongoid::Document
|
||||
|
||||
embedded_in :crop
|
||||
embeds_many :scheduled_command_lines
|
||||
|
||||
|
||||
field :crop_id
|
||||
field :schedule_id
|
||||
field :one_time_command
|
||||
field :scheduled_time
|
||||
field :command_id
|
||||
end
|
||||
|
||||
class ScheduledCommandLine
|
||||
include Mongoid::Document
|
||||
|
||||
embedded_in :scheduled_command
|
||||
|
||||
field :action
|
||||
field :coord_x
|
||||
field :coord_y
|
||||
field :coord_z
|
||||
field :speed
|
||||
field :amount
|
||||
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,201 @@
|
|||
require 'json'
|
||||
#require './lib/database/commandqueue.rb'
|
||||
require './lib/database/dbcommand.rb'
|
||||
require 'time'
|
||||
|
||||
# Get the JSON command, received through skynet, and send it to the farmbot
|
||||
# command queue Parses JSON messages received through SkyNet.
|
||||
class MessageHandler
|
||||
|
||||
attr_accessor :message
|
||||
|
||||
def initialize
|
||||
@dbaccess = DbAccess.new
|
||||
@last_time_stamp = ''
|
||||
end
|
||||
|
||||
# A list of MessageHandler methods (as strings) that a Skynet User may access.
|
||||
#
|
||||
def whitelist
|
||||
["single_command","crop_schedule_update"]
|
||||
end
|
||||
|
||||
# Main entry point for (Hash) commands coming in over SkyNet.
|
||||
# {
|
||||
# "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
|
||||
# }
|
||||
# }
|
||||
|
||||
def handle_message(message)
|
||||
|
||||
puts 'handle_message'
|
||||
#puts message
|
||||
#puts message['message']
|
||||
|
||||
@message = message['message']
|
||||
#fromUuid = message['fromUuid']
|
||||
#puts fromUuid
|
||||
|
||||
requested_command = message['message']["message_type"].to_s.downcase
|
||||
#puts requested_command
|
||||
|
||||
if whitelist.include?(requested_command)
|
||||
#puts 'sending'
|
||||
self.send(requested_command, message)
|
||||
else
|
||||
self.error(message)
|
||||
end
|
||||
end
|
||||
|
||||
# Handles an erorr (typically, an unauthorized or unknown message). Returns
|
||||
# Hash.
|
||||
def error
|
||||
return {error: ""}
|
||||
end
|
||||
|
||||
def single_command(message)
|
||||
|
||||
puts 'single_command'
|
||||
#puts message
|
||||
|
||||
time_stamp = message['message']['time_stamp']
|
||||
sender = message['fromUuid']
|
||||
|
||||
if time_stamp != @last_time_stamp
|
||||
@last_time_stamp = time_stamp
|
||||
|
||||
|
||||
# send the command to the queue
|
||||
delay = message['message']['command']['delay']
|
||||
action = message['message']['command']['action']
|
||||
x = message['message']['command']['x']
|
||||
y = message['message']['command']['y']
|
||||
z = message['message']['command']['z']
|
||||
speed = message['message']['command']['speed']
|
||||
amount = message['message']['command']['amount']
|
||||
delay = message['message']['command']['delay']
|
||||
|
||||
puts "[new command] received at #{Time.now} from #{sender}"
|
||||
puts "[#{action}] x: #{x}, y: #{y}, z: #{z}, speed: #{speed}, amount: #{amount} delay: #{delay}"
|
||||
|
||||
@dbaccess.create_new_command(Time.now + delay.to_i,'single_command')
|
||||
@dbaccess.add_command_line(action, x.to_i, y.to_i, z.to_i, speed.to_s, amount.to_i)
|
||||
@dbaccess.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(sender, command)
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
def crop_schedule_update(message)
|
||||
|
||||
puts 'crop_schedule_update'
|
||||
#puts message
|
||||
|
||||
time_stamp = message['message']['time_stamp']
|
||||
sender = message['fromUuid']
|
||||
|
||||
puts "time_stamp #{time_stamp}"
|
||||
puts "sender #{sender}"
|
||||
|
||||
if time_stamp != @last_time_stamp
|
||||
@last_time_stamp = time_stamp
|
||||
|
||||
|
||||
message_contents = message['message']
|
||||
#puts message_contents
|
||||
|
||||
crop_id = message_contents['crop_id']
|
||||
puts crop_id
|
||||
|
||||
puts 'removing old crop schedule'
|
||||
@dbaccess.clear_crop_schedule(crop_id)
|
||||
|
||||
message_contents['commands'].each do |command|
|
||||
|
||||
#puts command
|
||||
#puts command.class
|
||||
#puts command[0]
|
||||
#puts command[0].class
|
||||
#puts command[1]
|
||||
#puts command[1].class
|
||||
|
||||
scheduled_time = Time.parse(command[1]['scheduled_time'])
|
||||
|
||||
@dbaccess.create_new_command(scheduled_time, crop_id)
|
||||
#@dbaccess.create_new_command(Time.now, 'debug')
|
||||
puts scheduled_time
|
||||
puts Time.now
|
||||
|
||||
command[1]['command_lines'].each do |command_line|
|
||||
|
||||
action = command_line[1]['action']
|
||||
x = command_line[1]['x']
|
||||
y = command_line[1]['y']
|
||||
z = command_line[1]['z']
|
||||
speed = command_line[1]['speed']
|
||||
amount = command_line[1]['amount']
|
||||
|
||||
|
||||
puts "[#{action}] x: #{x}, y: #{y}, z: #{z}, speed: #{speed}, amount: #{amount}"
|
||||
@dbaccess.add_command_line(action, x.to_i, y.to_i, z.to_i, speed.to_s, amount.to_i)
|
||||
|
||||
end
|
||||
|
||||
@dbaccess.save_new_command
|
||||
|
||||
end
|
||||
|
||||
# send the command to the queue
|
||||
#delay = message['message']['command']['delay']
|
||||
#action = message['message']['command']['action']
|
||||
#x = message['message']['command']['x']
|
||||
#y = message['message']['command']['y']
|
||||
#z = message['message']['command']['z']
|
||||
#speed = message['message']['command']['speed']
|
||||
#amount = message['message']['command']['amount']
|
||||
#delay = message['message']['command']['delay']
|
||||
|
||||
#puts "[new command] received at #{Time.now} from #{sender}"
|
||||
#puts "[#{action}] x: #{x}, y: #{y}, z: #{z}, speed: #{speed}, amount: #{amount} delay: #{delay}"
|
||||
|
||||
#@dbaccess.create_new_command(Time.now + delay.to_i)
|
||||
#@dbaccess.add_command_line(action, x.to_i, y.to_i, z.to_i, speed.to_s, amount.to_i)
|
||||
#@dbaccess.save_new_command
|
||||
|
||||
puts 'sending comfirmation'
|
||||
|
||||
$skynet.confirmed = false
|
||||
|
||||
command =
|
||||
{
|
||||
:message_type => 'confirmation',
|
||||
:time_stamp => Time.now.to_f.to_s,
|
||||
:confirm_id => time_stamp
|
||||
}
|
||||
|
||||
$skynet.send_message(sender, command)
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
|
@ -0,0 +1,59 @@
|
|||
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'
|
||||
@confirmed = false
|
||||
|
||||
create_socket_events
|
||||
|
||||
puts "uuid: #{@uuid}"
|
||||
|
||||
@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)
|
||||
def handle_message(message)
|
||||
|
||||
puts "> message received at #{Time.now}"
|
||||
#puts message
|
||||
|
||||
if message.class.to_s == 'Hash'
|
||||
@message_handler.handle_message(message)
|
||||
end
|
||||
|
||||
if message.class.to_s == 'String'
|
||||
message_hash = JSON.parse(message)
|
||||
@message_handler.handle_message(message_hash)
|
||||
end
|
||||
|
||||
rescue
|
||||
raise "Runtime error while attempting to parse message: #{message}."
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,33 @@
|
|||
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
|
||||
@socket.on :message do |message|
|
||||
$skynet.handle_message(message)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,44 @@
|
|||
|
||||
$farmbot_uuid = "063df52b-0698-4e1c-b2bb-4c0890019782"
|
||||
|
||||
require_relative 'lib/skynet/skynet'
|
||||
require_relative 'lib/botcomm'
|
||||
require_relative 'lib/cropschedule'
|
||||
require_relative 'lib/database/dbcommand'
|
||||
require_relative 'lib/database/dbfarmbot'
|
||||
|
||||
puts '[FarmBot schedule transmit]'
|
||||
puts 'starting up'
|
||||
|
||||
# connecting to skynet framework
|
||||
$skynet = Skynet.new
|
||||
|
||||
$farmbot_comm = FarmBotControllerComm.new
|
||||
|
||||
while $skynet.identified != true
|
||||
sleep 0.5
|
||||
print '.'
|
||||
end
|
||||
puts ''
|
||||
|
||||
puts 'connecting to database'
|
||||
|
||||
Mongoid.load!("config/mongo.yml", :development)
|
||||
|
||||
puts 'checking list of bots'
|
||||
|
||||
FarmBot.where(:active => true).order_by([:name,:asc]).each do |farmbot|
|
||||
puts "checking bot #{farmbot.name} uuid #(farmbot.uuid)"
|
||||
|
||||
farmbot.crops.where(:valid_data => true).each do |crop|
|
||||
|
||||
puts "crop type=#{crop.plant_type} @ x=#{crop.coord_x} y=#{crop.coord_y}"
|
||||
|
||||
crop_schedule = CropSchedule.new
|
||||
crop_schedule.read_from_db( crop )
|
||||
|
||||
$farmbot_comm.send_schedule(farmbot.uuid,crop_schedule)
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,104 @@
|
|||
|
||||
$farmbot_uuid = "063df52b-0698-4e1c-b2bb-4c0890019782"
|
||||
|
||||
require_relative 'lib/device/device'
|
||||
require_relative 'lib/botcomm'
|
||||
|
||||
puts '[FarmBot Remote Control]'
|
||||
puts 'starting up'
|
||||
|
||||
# connecting to skynet framework
|
||||
$device = Device.new
|
||||
|
||||
$farmbot_comm = FarmBotControllerComm.new
|
||||
|
||||
while $device.identified != true
|
||||
sleep 0.5
|
||||
print '.'
|
||||
end
|
||||
puts ''
|
||||
|
||||
# send schedule to farmbot
|
||||
def send_singe_command(action, x, y, z, amount, speed, delay)
|
||||
|
||||
$farmbot_comm.send_single_command(action, x, y, z, amount, speed, delay)
|
||||
|
||||
end
|
||||
|
||||
|
||||
$shutdown = 0
|
||||
|
||||
# just a little menu for testing
|
||||
|
||||
$move_size = 10
|
||||
$command_delay = 0
|
||||
|
||||
while $shutdown == 0 do
|
||||
|
||||
system('cls')
|
||||
system('clear')
|
||||
|
||||
puts '[FarmBot Controller Menu]'
|
||||
puts ''
|
||||
puts 'p - stop'
|
||||
puts ''
|
||||
puts "move size = #{$move_size}"
|
||||
puts "command delay = #{$command_delay}"
|
||||
puts ''
|
||||
puts 'w - forward'
|
||||
puts 's - back'
|
||||
puts 'a - left'
|
||||
puts 'd - right'
|
||||
puts 'r - up'
|
||||
puts 'f - down'
|
||||
puts ''
|
||||
puts 'z - home z axis'
|
||||
puts 'x - home x axis'
|
||||
puts 'c - home y axis'
|
||||
puts ''
|
||||
puts 'y - dose water'
|
||||
puts ''
|
||||
puts 'q - step size'
|
||||
puts 'g - delay seconds'
|
||||
puts ''
|
||||
print 'command > '
|
||||
input = gets
|
||||
puts ''
|
||||
|
||||
case input.upcase[0]
|
||||
when "P" # Quit
|
||||
$shutdown = 1
|
||||
puts 'Shutting down...'
|
||||
when "O" # Get status
|
||||
puts 'Not implemented yet. Press \'Enter\' key to continue.'
|
||||
gets
|
||||
when "Q" # Set step size
|
||||
print 'Enter new step size > '
|
||||
move_size_temp = gets
|
||||
$move_size = move_size_temp.to_i if move_size_temp.to_i > 0
|
||||
when "G" # Set step delay (seconds)
|
||||
print 'Enter new delay in seconds > '
|
||||
command_delay_temp = gets
|
||||
$command_delay = command_delay_temp.to_i if command_delay_temp.to_i > 0
|
||||
when "Y" # Water
|
||||
send_single_command('DOSE WATER', 0, 0, 0, 15, 0, $command_delay)
|
||||
when "Z" # Move to home
|
||||
send_single_command('HOME Z', 0, 0, 0, 0, 0, $command_delay)
|
||||
when "X" # Move to home
|
||||
send_single_command('HOME X', 0, 0, 0, 0, 0, $command_delay)
|
||||
when "C" # Move to home
|
||||
send_single_command('HOME Y',0 ,0 ,-$move_size, 0, 0, $command_delay)
|
||||
when "W" # Move forward
|
||||
send_single_command('MOVE RELATIVE',0,$move_size, 0, 0, 0, $command_delay)
|
||||
when "S" # Move back
|
||||
send_single_command('MOVE RELATIVE',0,-$move_size, 0, 0, 0, $command_delay)
|
||||
when "A" # Move left
|
||||
send_single_command('MOVE RELATIVE', -$move_size, 0, 0, 0, 0, $command_delay)
|
||||
when "D" # Move right
|
||||
send_single_command('MOVE RELATIVE', $move_size, 0, 0, 0, 0, $command_delay)
|
||||
when "R" # Move up
|
||||
send_single_command('MOVE RELATIVE', 0, 0, $move_size, 0, 0, $command_delay)
|
||||
when "F" # Move down
|
||||
send_single_command("MOVE RELATIVE", 0, 0, -$move_size, 0, 0, $command_delay)
|
||||
end
|
||||
end
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 68e4f0cc61ce622cda65c79c2a3f13557077fb02
|
Loading…
Reference in New Issue