networking is almost working again, just need to rebuild the form in configuratory
parent
989dbac94c
commit
bcf1d036e6
|
@ -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...
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -12,6 +12,7 @@ This fork will have added packages for farmbot_os.
|
|||
* dnsmasq
|
||||
* avrdude
|
||||
* dropbear
|
||||
* ifup
|
||||
* BusyBox pacakges:
|
||||
* mkfs.ext*
|
||||
* fwup config:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -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
219
mix.exs
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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" -#
|
Loading…
Reference in New Issue