networking is almost working again, just need to rebuild the form in configuratory

pull/216/head
connor rigby 2016-12-20 13:12:11 -08:00
parent 989dbac94c
commit bcf1d036e6
27 changed files with 544 additions and 115 deletions

View File

@ -64,6 +64,12 @@ defmodule Farmbot.Configurator.EventHandler do
def handle_call(:sockets, sockets), do: {:ok, sockets, sockets}
def handle_call(_, sockets), do: {:ok, :unhandled, sockets}
def handle_info(info, sockets) do
Logger.debug ">> got some unhandled info in " <>
"socket event handler: #{inspect info}"
{:ok, sockets}
end
def terminate(_,_), do: :ok
# Probably a better way to do this...

View File

@ -0,0 +1,19 @@
defmodule Farmbot.Configurator.Logger do
@moduledoc """
Tiny logger backend to broadcast relevent logs to a connected configurator.
"""
alias Farmbot.Configurator.EventManager, as: EM
use GenEvent
def init(args), do: {:ok, args}
def handle_event({_l, _f, {Logger, ">>" <> message, _ts, _meta}}, socket) do
m = %{method: "log_message", id: nil, params: [%{message: "farmbot #{message}"}]}
|> Poison.encode!
|> broadcast
{:ok, socket}
end
def handle_event(_, socket), do: {:ok, socket}
def broadcast(message) when is_binary(message) do
EM.send_socket({:from_bot, message})
end
end

View File

@ -4,10 +4,12 @@ defmodule Farmbot.Configurator.Router do
"""
use Plug.Router
# this is so we can serve the bundle.js file.
plug Plug.Static, at: "/", from: :farmbot_configurator
plug :match
plug :dispatch
# anything that doesn't match a rest end point gets the index.
match _, do: conn |> send_resp(200, make_html)
def make_html do

View File

@ -13,6 +13,7 @@ defmodule Farmbot.Configurator.SocketHandler do
#Called on websocket connection initialization.
def websocket_init(_type, req, _options) do
Logger.debug ">> encountered a new local websocket connection."
Logger.add_backend(Farmbot.Configurator.Logger, [self])
:erlang.start_timer(1000, self, [])
# :ok = EH.start_link(self)
:ok = EH.add_socket(self)
@ -35,13 +36,13 @@ defmodule Farmbot.Configurator.SocketHandler do
end
def websocket_info(message, req, state) do
Logger.debug "got a info message: #{inspect message}"
Logger.debug ">> got an info message: #{inspect message}"
{:ok, req, state}
end
def websocket_terminate(_reason, _req, _state) do
Logger.debug ">> is closing a websocket connection."
# :ok = EH.stop_link(self)
Logger.remove_backend(Farmbot.Configurator.Logger,[])
:ok = EH.remove_socket(self)
:ok
end

View File

@ -22,7 +22,7 @@
"devDependencies": {},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack",
"build": "webpack --colors",
"watch": "webpack --watch --stdin --colors"
},
"repository": {

View File

@ -5,6 +5,11 @@
</head>
<body>
<font size=42>Welcome to the new web!
<marquee>CONFIGURE YOUR FARMBOT</marquee>
</font>
<div id="app"></div>
<script src="/bundle.js"></script>
</body>

View File

@ -35,6 +35,30 @@ export class Main extends React.Component<MainProps, {}> {
</div>
<button onClick={() => {
state.uploadConfigFile(this.props.ws);
} }>
Upload configuration!
</button>
<button onClick={() => {
state.uploadAppCredentials({
email: "admin@admin.com",
pass: "password123",
server: "http://192.168.29.167:3000"
}, this.props.ws);
} }>
Upload Web Credentials
</button>
<div>
<ul>
{state.logs.map((el, index) => {
return <li key={index}>{el}</li>
})}
</ul>
</div>
</div>
</div>
</div>

View File

@ -7,6 +7,7 @@ import {
BotConfigFile
} from "./interfaces";
import { infer } from "./jsonrpc";
import { uuid } from "./utils";
/** messages that need to be resolved by the bot. */
export interface RpcMessageDict { [propName: string]: RpcRequest | undefined; }
@ -89,7 +90,16 @@ export class MainState {
}
private handleNotification(data: RpcNotification): void {
return;
switch (data.method) {
case "log_message":
let message = data.params[0].message;
console.log("log_message: " + message);
this.logs.push(message);
return;
default:
console.log("could not handle: " + data.method);
return;
}
}
/** handles a response. Can possible return a notification if something went wrong */
@ -108,6 +118,12 @@ export class MainState {
// so does this: todo
state.networkInterfaces = JSON.parse(data.result);
break;
case "upload_config_file":
console.log("Config file uploaded!");
break;
case "web_app_creds":
console.log("Credentials uploaded!");
break;
default:
console.warn("unhandlled response: " + origin.method);
}
@ -131,6 +147,25 @@ export class MainState {
}
}
uploadAppCredentials(creds: { email: string, pass: string, server: string }, ws: WebSocket) {
console.log("Uploading web credentials");
this.makeRequest({
method: "web_app_creds",
params: [creds],
id: uuid()
}, ws);
}
uploadConfigFile(ws: WebSocket) {
let config = this.configuration;
console.dir(config);
this.makeRequest({
method: "upload_config_file",
params: [{ config: config }],
id: uuid()
}, ws);
}
@action
makeRequest(req: RpcRequest, ws: WebSocket) {
console.log("requesting: " + req.method);

View File

@ -54,6 +54,19 @@ defmodule Farmbot.FileSystem.ConfigStorage do
GenServer.call(__MODULE__, :read_config_file)
end
@doc """
Replace the configuration json with a new one.
BE CAREFUL IM NOT CHECKING THE FILE AT ALL
"""
def replace_config_file(config) do
Farmbot.FileSystem.transaction fn() ->
json = Poison.encode! config
File.write!(@config_file, json)
:ok
end
GenServer.stop(__MODULE__, :new_config)
end
def handle_call(:read_config_file, _, state) do
read = File.read(@config_file)
{:reply, read, state}
@ -84,6 +97,13 @@ defmodule Farmbot.FileSystem.ConfigStorage do
write! new_state
end
def terminate(:new_config, _state) do
Logger.debug ">> is loading a new config."
:ok
end
def terminate(_,_), do: nil
defp module_to_key(module),
do: module
|> Module.split

View File

@ -32,7 +32,7 @@ defmodule Farmbot.FileSystem do
GenServer.cast(__MODULE__, {:transaction, fun, self()})
receive do
{^fun, ret} -> ret
e -> raise "Bad return value for transaction: #{inspect e}"
e -> raise "Bad return value for filesystem transaction: #{inspect e}"
end
end

View File

@ -28,7 +28,7 @@ defmodule Module.concat([FileSystem, Utils, :prod, "rpi3"]) do
FileSystem access functions.
"""
@behaviour FileSystem.Utils
@state_path Application.get_env(:farmbot, :state_path)
@state_path Application.get_env(:farmbot_filesystem, :path)
@block_device "/dev/mmcblk0p3"
@fs_type "ext4"
@ro_options ["-t", @fs_type, "-o", "ro,remount", @block_device, @state_path]

View File

@ -12,6 +12,7 @@ This fork will have added packages for farmbot_os.
* dnsmasq
* avrdude
* dropbear
* ifup
* BusyBox pacakges:
* mkfs.ext*
* fwup config:

View File

@ -23,8 +23,16 @@ defmodule NervesSystemRpi3.Mixfile do
defp deps do
[{:nerves, "~> 0.4.0"},
{:nerves_toolchain_arm_unknown_linux_gnueabihf, "~> 0.8.0"},
{:nerves_system_br, "~> 0.8.1", override: true}]
{:nerves_toolchain_arm_unknown_linux_gnueabihf, "~> 0.8.0"}]
++ [find_nerves_system_br]
end
def find_nerves_system_br do
if File.exists?("../nerves_system_br") do
{:nerves_system_br, in_umbrella: true}
else
{:nerves_system_br, "~> 0.8.1"}
end
end
defp description do

View File

@ -57,6 +57,7 @@ BR2_PACKAGE_LIBMNL=y
BR2_PACKAGE_DNSMASQ=y
BR2_PACKAGE_DROPBEAR=y
BR2_PACKAGE_HOSTAPD=y
BR2_PACKAGE_IW=y
BR2_PACKAGE_WPA_SUPPLICANT=y
BR2_PACKAGE_WPA_SUPPLICANT_DEBUG_SYSLOG=y
BR2_TARGET_ROOTFS_SQUASHFS=y

View File

@ -2,7 +2,7 @@ defmodule Farmbot.BotState.Authorization do
@moduledoc """
Tracks authorization state.
"""
@data_path Application.get_env(:farmbot, :state_path)
@data_path Application.get_env(:farmbot_filesystem, :path)
require Logger
alias Farmbot.Auth
alias Farmbot.StateTracker

View File

@ -30,6 +30,11 @@ defmodule Module.concat([Farmbot, Network, Handler, "rpi3"]) do
GenServer.cast(state.parent, {:connected, interface, address})
{:ok, state}
end
def handle_event({:hostapd, data}, state) do
Logger.debug ">> got some hostapd data: #{data}"
{:ok, state}
end
# def handle_event(event, state) do
# Logger.warn "got event: #{inspect event}"
# {:ok, state}

View File

@ -12,7 +12,10 @@ defmodule Farmbot.Network.Hostapd do
@dnsmasq_conf_file "dnsmasq.conf"
@dnsmasq_pid_file "dnsmasq.pid"
@doc """
Example:
Iex> Hostapd.start_link ip_address: "192.168.24.1", manager: Farmbot.Network.Manager, interface: "wlan0"
"""
def start_link(
[interface: interface, ip_address: ip_addr, manager: manager])
do

View File

@ -8,8 +8,9 @@ defmodule Farmbot.Network do
defmodule Interface, do: defstruct [:ipv4_address, :pid]
defmodule State do
defstruct [connected?: false, interfaces: %{}]
@type t :: %__MODULE__{connected?: boolean, interfaces: %{}}
@enforce_keys [:manager, :hardware]
defstruct [connected?: false, interfaces: %{}, manager: nil, hardware: nil]
@type t :: %__MODULE__{connected?: boolean, interfaces: %{}, manager: pid}
end
#Nerves.InterimWiFi.setup "wlan0", ssid: ssid, key_mgmt: :"WPA-PSK", psk: pass
@ -21,8 +22,8 @@ defmodule Farmbot.Network do
def init(hardware) do
Process.flag :trap_exit, true
Logger.debug ">> is starting epmd."
System.cmd("epmd", ["-daemon"])
# Logger.debug ">> is starting epmd."
# System.cmd("epmd", ["-daemon"])
Logger.debug ">> is initializing networking on: #{inspect hardware}"
# this is from the json file yet to be defined.
{:ok, config} = get_config
@ -33,45 +34,121 @@ defmodule Farmbot.Network do
# add the handler. (probably change this to a mon handler)
GenEvent.add_handler(manager, handler, {self(), config})
blah = parse_config(config, hardware)
{:ok, %State{blah | connected?: false}}
{:ok, %State{connected?: false,
manager: manager,
hardware: hardware,
interfaces: parse_config(config, hardware)}}
end
def handle_cast({:connected, interface, ip}, state) do
Logger.debug ">>'s #{interface} is connected: #{ip}"
# I HATE THIS
GenServer.cast(Farmbot.BotState.Authorization, :try_log_in)
Farmbot.BotState.set_time
case Map.get(state.interfaces, interface) do
%Interface{} = thing ->
new_interface = %Interface{thing | ipv4_address: ip}
new_state =
%State{state | connected?: true, interfaces: Map.put(state.interfaces, interface, new_interface)}
%State{state | connected?: true,
interfaces: Map.put(state.interfaces, interface, new_interface)}
{:noreply, new_state}
t ->
Logger.warn(
">> encountered something weird updating #{interface} state: #{inspect t}")
">> encountered something weird updating #{interface} "
<> "state: #{inspect t}")
{:noreply, %State{state | connected?: true }}
end
end
def handle_info({:EXIT, pid, reason}, state) do
Logger.debug "something died: #{inspect pid}, #{inspect reason}"
Logger.debug "something in network died: #{inspect pid}, #{inspect reason}"
{:noreply, state}
end
def handle_call(:all_down, _, state) do
Enum.each(state.interfaces, fn({interface, config}) ->
Logger.debug ">> is stoping #{interface}"
if is_pid(config.pid) do
Process.exit(config.pid, :down)
end
end)
{:reply, :ok, %State{state | connected?: false, interfaces: %{}}}
end
def handle_call({:up, _iface, [host: true]}, _, state) do
{:reply, :todo, state}
end
def handle_call({:up, iface, settings }, _, state) do
{:ok, pid} = Nerves.InterimWiFi.setup(iface, settings)
new_interface = %Interface{pid: pid}
{:reply, pid, %State{state |
interfaces: Map.put(state.interfaces, iface, new_interface)}}
end
def handle_call({:down, iface}, _, state) do
iface = state.interfaces[iface]
if iface do
Process.exit(iface.pid, :down)
{:reply, :ok, %State{state |
interfaces: Map.delete(state.interfaces, iface)}}
else
Logger.debug ">> could not bring down #{iface}."
{:reply, :no_iface, state}
end
end
def handle_call(:restart, _, state) do
if Enum.empty?(state.interfaces) do
{:ok, new_state} = init(state.hardware)
{:reply, :ok, new_state}
else
Logger.debug ">> detected there are still some network interfaces up."
{:reply, {:error, :not_down}, state}
end
end
def handle_call(:manager, _, state), do: {:reply, state.manager, state}
def handle_call(:state, _, state), do: {:reply, state, state}
def state do
GenServer.call(__MODULE__, :state)
@doc """
Gets the entire state. Will probably go away.
"""
def state, do: GenServer.call(__MODULE__, :state)
@doc """
Brings down every interface that has been brought up.
"""
def all_down, do: GenServer.call(__MODULE__, :all_down)
@doc """
The pid of the manager that Network was started with.
"""
def manager, do: GenServer.call(__MODULE__, :manager)
@doc """
Bring an interface up with Nerves.InterimWiFi.
"""
def up(iface, settings),do: GenServer.call(__MODULE__, {:up, iface, settings})
@doc """
Bring an interface down.
"""
def down(iface), do: GenServer.call(__MODULE__, {:down, iface})
@doc """
Restarts networking, reloading the config file.
"""
def restart do
all_down
# just to make sure everything is ready
Logger.debug ">> is waiting for interfaces to come down."
Process.sleep 5000
GenServer.call(__MODULE__, :restart)
end
def terminate(_,_), do: :ok
def terminate(_reason,_state), do: :ok
defp get_config do
GenServer.call(FBConfigStorage, {:get, __MODULE__, :all})
end
defp get_config, do: GenServer.call(FBConfigStorage, {:get, __MODULE__, :all})
# Be very careful down here
defp parse_config(nil, _), do: %State{interfaces: %{}}
defp parse_config(nil, _),
do: %State{interfaces: %{}, manager: self, hardware: "development"}
defp parse_config(config, hardware) do
# {"wlan0", %{"type" => "hostapd"}}
# {"eth0", %{"type" => "ethernet", "ip" => %{"mode" => "dhcp"}}}
@ -105,7 +182,7 @@ defmodule Farmbot.Network do
{interface, %Interface{pid: pid}}
end
end)
%State{interfaces: something}
something
end
defp parse_ip_settings(ip_settings) do

View File

@ -3,13 +3,17 @@ defmodule Uh do
alias RPC.Spec.Request
alias RPC.Spec.Response
alias Farmbot.Configurator.EventManager, as: EM
alias Farmbot.FileSystem.ConfigStorage, as: CS
import RPC.Parser
use GenEvent
require Logger
import Farmbot.RPC.Requests
# GenEvent Stuff
def start_link, do: GenEvent.add_handler(EM, __MODULE__, [])
def start_link do
GenEvent.add_handler(EM, __MODULE__, [])
{:ok, self}
end
def stop_link, do: GenEvent.remove_handler(EM, __MODULE__, [])
def init([]), do: {:ok, []}
@ -34,6 +38,63 @@ defmodule Uh do
|> send_socket
end
def handle_socket(
%Request{id: id,
method: "get_network_interfaces",
params: _})
do
{hc, 0} = System.cmd("iw", ["wlan0", "scan", "ap-force"])
interfaces = [
%{name: "wlan0", type: "wireless", ssids: hc |> clean_ssid},
%{name: "eth0", type: "ethernet"}
]
%Response{id: id, result: Poison.encode!(interfaces), error: nil}
|> Poison.encode!
|> send_socket
end
def handle_socket(
%Request{id: id,
method: "upload_config_file",
params: [%{"config" => config}]})
do
# replace the old config file with the new one.
case CS.replace_config_file(config) do
:ok ->
%Response{id: id, result: "OK", error: nil}
|> Poison.encode!
|> send_socket
{:error, reason} ->
%Response{id: id, result: nil, error: "#{inspect reason}"}
|> Poison.encode!
|> send_socket
end
end
def handle_socket(
%Request{id: id,
method: "try_log_in",
params: _})
do
# Configurator is done configurating.
Logger.debug ">> has been configurated! going to try to log in."
%Response{id: id, result: "OK", error: nil}
|> Poison.encode!
|> send_socket
Farmbot.Network.restart
end
def handle_socket(
%Request{id: id,
method: "web_app_creds",
params: [%{"email" => email, "pass" => pass, "server" => server}]})
do
Farmbot.BotState.add_creds {email, pass, server}
%Response{id: id, result: "OK", error: nil}
|> Poison.encode!
|> send_socket
end
def handle_socket(%Request{} = request) do
handle_request(request.method, request.params) |> respond(request)
end
@ -64,4 +125,14 @@ defmodule Uh do
end
defp send_socket(json), do: EM.send_socket({:from_bot, json})
defp clean_ssid(hc) do
hc
|> String.replace("\t", "")
|> String.replace("\\x00", "")
|> String.split("\n")
|> Enum.filter(fn(s) -> String.contains?(s, "SSID") end)
|> Enum.map(fn(z) -> String.replace(z, "SSID: ", "") end)
|> Enum.filter(fn(z) -> String.length(z) != 0 end)
end
end

View File

@ -26,7 +26,8 @@ defmodule Farmbot.Supervisor do
# Handles Communication between the bot and frontend
supervisor(RPC.Supervisor, [[]], restart: :permanent),
worker(Farmbot.RPC.Handler, [[]], restart: :permanent)
worker(Farmbot.RPC.Handler, [[]], restart: :permanent),
worker(Uh, [], restart: :permanent)
]
opts = [strategy: :one_for_one, name: Farmbot.Supervisor]
supervise(children, opts)

View File

@ -0,0 +1,77 @@
defmodule Mix.Tasks.Farmbot.Upload do
use Mix.Task
@shortdoc "Uploads a file to a url"
def run(args) do
ip_address = List.first(args)
|| "192.168.29.186" # I get to do this because i own it.
curl_args = [
"-T", "_images/rpi3/farmbot.fw",
"http://#{ip_address}:8988/firmware",
"-H", "Content-Type: application/x-firmware",
"-H", "X-Reboot: true"]
IO.puts("Starting upload...")
Mix.Tasks.Farmbot.Curl.run(curl_args)
end
end
defmodule Mix.Tasks.Farmbot.Curl do
use Mix.Task
@shortdoc "Uploads an image to a development target"
def run(args) do
args = args ++
[ "-#" ] # CURL OPTIONS
Port.open({:spawn_executable, "/usr/bin/curl"},
[{:args, args},
:stream,
:binary,
:exit_status,
:hide,
:use_stdio,
:stderr_to_stdout])
handle_output
end
def handle_output do
receive do
info -> handle_info(info)
end
end
def handle_info({port, {:data, << <<35>>, _ :: size(568), " 100.0%">>}}) do # LAWLZ
IO.puts("\nDONE")
Port.close(port)
end
def handle_info({port, {:data, << "\r", <<35>>, _ :: size(568), " 100.0%">>}}) do # LAWLZ
IO.puts("\nDONE")
Port.close(port)
end
def handle_info({_port, {:data, << <<35>>, <<_ :: binary>> >>}}) do
IO.write("#")
handle_output
end
def handle_info({_port, {:data, << "\n", <<35>>, <<_ :: binary>> >>}}) do
IO.write("#")
handle_output
end
def handle_info({_port, {:data, << "\r", <<35>>, <<_ :: binary>> >>}}) do
IO.write("#")
handle_output
end
def handle_info({_port, {:data, _data}}) do
# IO.puts(data)
handle_output
end
def handle_info({_port, {:exit_status, 7}}) do
IO.puts("\nCOULD NOT CONNECT TO DEVICE!")
end
def handle_info({_port, {:exit_status, _status}}) do
IO.puts("\nDONE")
end
end

View File

@ -2,8 +2,7 @@ defmodule Mix.Tasks.Farmbot.Upload do
use Mix.Task
@shortdoc "Uploads a file to a url"
def run(args) do
ip_address = System.get_env("FARMBOT_IP")
|| List.first(args)
ip_address = List.first(args)
|| "192.168.29.186" # I get to do this because i own it.
curl_args = [
"-T", "_images/rpi3/farmbot.fw",

219
mix.exs
View File

@ -17,49 +17,15 @@ defmodule FarmbotOs.Mixfile do
defp target(_), do: System.get_env("NERVES_TARGET") || "development"
end
defmodule Mix.Tasks.Farmbot.Firmware do
use Mix.Task
@shortdoc "Builds firmware."
def run(args) do
case handle_args(args) do
true -> do_run(args)
_ -> :ok
end
end
def do_run(_args) do
if Mix.env == :dev do
IO.puts ">> BE CAREFUL BUILDING FIRMWARE IN DEVELOPMENT MODE, THINGS GET WEIRD. <<"
end
IO.puts "Building Farmbot firmware!"
defmodule Mix.Tasks.Farmbot do
def env_info do
IO.puts "[ NERVES_TARGET ]: #{Mix.Project.config[:target]}"
System.put_env("NERVES_TARGET", "#{Mix.Project.config[:target]}")
if System.get_env("NERVES_SYSTEM") do
IO.puts "[ NERVES_SYSTEM ]: #{System.get_env("NERVES_SYSTEM")}"
IO.puts "[ NERVES_SYSTEM ]: #{System.get_env("NERVES_SYSTEM")}"
end
IO.puts "[ ENVIRONMENT ]: #{Mix.env}"
IO.puts "[ GIT HASH ]: #{git_revision}"
if !File.exists?("./deps") do
fetch_deps
end
port = Port.open({:spawn, "bash ./apps/os/build.sh"},
[:stream,
:binary,
:exit_status,
:hide,
:use_stdio,
:stderr_to_stdout])
handle_port(port)
end
def handle_port(port) do
receive do
{^port, {:data, data}} -> IO.puts data; handle_port(port)
{^port, {:exit_status, 0}} -> IO.puts "Built firmware!"
{^port, {:exit_status, _}} -> IO.puts "Error building firmwre!"
stuff -> IO.puts "unexpected stuff: #{inspect stuff}"
end
end
defp git_revision do
@ -68,52 +34,141 @@ defmodule Mix.Tasks.Farmbot.Firmware do
res |> String.trim
end
defp fetch_deps do
IO.puts "Fetching dependencies."
Mix.Tasks.Deps.Get.run([])
true
defmodule System do
def run(_) do
Mix.Tasks.Farmbot.env_info
port = Port.open({:spawn, "bash ./scripts/clone_system.sh"},
[:stream,
:binary,
:exit_status,
:hide,
:use_stdio,
:stderr_to_stdout])
handle_port(port)
end
def handle_port(port) do
receive do
{^port, {:data, data}} -> IO.puts data; handle_port(port)
{^port, {:exit_status, 0}} -> IO.puts "Environment built!"
{^port, {:exit_status, _}} -> IO.puts "Error setting up environment"
stuff -> IO.puts "unexpected stuff: #{inspect stuff}"
end
end
end
def handle_args(args) do
Enum.all?(args, fn(arg) ->
do_thing(arg)
end)
end
# Because reasons.
def do_thing("-h"), do: do_thing("--help")
def do_thing("--help") do
IO.puts help_text
false
end
defmodule Firmware do
use Mix.Task
@shortdoc "Builds firmware."
def do_thing("--clean") do
IO.puts "cleaning environment!"
File.rm_rf("deps")
File.rm_rf("_build")
File.rm_rf("_images")
def run(args) do
Mix.Tasks.Farmbot.env_info
:ok = case handle_args(args) do
true -> do_run(args)
_ -> :ok
end
if Enum.find_value(args, fn(arg) -> arg == "--upload" end) do
if Elixir.System.get_env("BOT_IP_ADDR") == nil do
Elixir.System.put_env("BOT_IP_ADDR", "192.168.24.1")
end
ip = Elixir.System.get_env("BOT_IP_ADDR")
port = Port.open({:spawn, "bash ./scripts/upload.sh"},
[:stream,
:binary,
:exit_status,
:hide,
:use_stdio,
:stderr_to_stdout])
handle_port(port, "Uploaded!", "Failed to upload!")
end
end
def check_system_dir do
{:ok, dirs} = File.ls "./apps/"
Enum.find(dirs, fn(dir) ->
String.contains?(dir, "NERVES_SYSTEM")
end)
end
def do_run(_args) do
maybe_system_dir = check_system_dir
if maybe_system_dir && Elixir.System.get_env("NERVES_SYSTEM") == nil do
IO.puts "detected a system build: #{maybe_system_dir}, set the NERVES_SYSTEM env var to use it."
end
if !File.exists?("./deps") do
fetch_deps
end
port = Port.open({:spawn, "bash ./scripts/build.sh"},
[:stream,
:binary,
:exit_status,
:hide,
:use_stdio,
:stderr_to_stdout])
handle_port(port, "Built firmware!", "Error building firmware!")
end
def handle_port(port, success, err) do
receive do
{^port, {:data, data}} -> IO.puts data; handle_port(port, success, err)
{^port, {:exit_status, 0}} -> IO.puts success; :ok
{^port, {:exit_status, _}} -> IO.puts err; :error
stuff -> IO.puts "unexpected stuff: #{inspect stuff}"; :error
end
end
defp fetch_deps do
IO.puts "Fetching dependencies."
Mix.Tasks.Deps.Get.run([])
true
end
def handle_args(args) do
Enum.all?(args, fn(arg) ->
do_thing(arg)
end)
end
# Because reasons.
def do_thing("-h"), do: do_thing("--help")
def do_thing("--help") do
IO.puts help_text
false
end
def do_thing("--clean") do
IO.puts "cleaning environment!"
File.rm_rf("deps")
File.rm_rf("_build")
File.rm_rf("_images")
end
def do_thing("--nobuild"), do: false
def do_thing("--upload"), do: true
def do_thing("--burn"), do: true
def do_thing("--deps"), do: fetch_deps
def do_thing(other) do
IO.puts "bad argument: #{other}"
IO.puts help_text
false
end
defp help_text, do:
"""
Builds a farmbot firmware image.
Can be configured by various env vars.
* NERVES_TARGET (default: rpi3) - can be "rpi3", "qemu-arm" right now. More support coming soon.
* NERVES_SYSTEM (optional) - can be used to not use the default system for your NERVES_TARGET
* BOT_IP_ADDR (optional) - the ip address of a running farmbot. This is used in the upload task.
Can also be configured by several command line switches.
* --clean - will clean the environment before building again. (this can take a while)
* --upload - If the build succeeds, upload firmware to IP_ADDRESS
* --deps - force fetch dependencies.
* --nobuild - don't actually build the firmware.
* --burn - burn the image to an sdcard
* --help - display this message.
"""
end
def do_thing("--nobuild"), do: false
def do_thing("--deps"), do: fetch_deps
def do_thing(other) do
IO.puts "bad argument: #{other}"
IO.puts help_text
false
end
defp help_text, do:
"""
Builds a farmbot firmware image.
Can be configured by various env vars.
* NERVES_TARGET (default: rpi3) - can be "rpi3", "qemu-arm" right now. More support coming soon.
* NERVES_SYSTEM (optional) - can be used to not use the default system for your NERVES_TARGET
* IP_ADDR (optional) - the ip address of a running farmbot. This is used in the upload task.
Can also be configured by several command line switches.
* --clean - will clean the environment before building again. (this can take a while)
* --upload - If the build succeeds, upload firmware to IP_ADDRESS
* --deps - force fetch dependencies.
* --nobuild - don't actually build the firmware.
* --help - display this message.
"""
end

View File

@ -2,7 +2,7 @@
# yes. This is in fact a bash script that gets called
# from a mix task, that then in a wild chain of events, executes more mix tasks.
# don'nt you worry about a thing.
CWD=$PWD
CWD=$PWD # this should be from the root of the project
OS_DIR=$CWD/apps/os
CONFIGURATOR_DIR=$CWD/apps/farmbot_configurator

5
scripts/burn.sh 100644
View File

@ -0,0 +1,5 @@
#!/bin/bash
CWD=$PWD # this should be from the root of the project
OS_DIR=$CWD/apps/os
cd $OS_DIR
mix firmware.burn

View File

@ -0,0 +1,10 @@
#!/bin/bash
# sets up the environment for building the linux part of Farmbot.
CWD=$PWD # this should be from the root of the project
APPS_DIR=$CWD/apps
TARGET_DIR=$APPS_DIR/nerves_system_$NERVES_TARGET
BR_DIR=$CWD/deps/nerves_system_br
$BR_DIR/create-build.sh $TARGET_DIR/nerves_defconfig $APPS_DIR/NERVES_SYSTEM_$NERVES_TARGET
exit 0

View File

@ -0,0 +1,4 @@
#!/bin/bash
echo "uploading to ${BOT_IP_ADDR}"
/usr/bin/curl -T apps/os/_images/rpi3/farmbot.fw http://${BOT_IP_ADDR}:8988/firmware \
-H "Content-Type: application/x-firmware" -H "X-Reboot: true" -#