349 lines
11 KiB
Elixir
349 lines
11 KiB
Elixir
defmodule Farmbot.Bootstrap.SettingsSync do
|
|
@moduledoc "Handles uploading and downloading of FBOS configs."
|
|
use Farmbot.Logger
|
|
import Farmbot.System.ConfigStorage,
|
|
only: [get_config_value: 3, update_config_value: 4, get_config_as_map: 0]
|
|
|
|
@fbos_keys [
|
|
"auto_sync",
|
|
"beta_opt_in",
|
|
"disable_factory_reset",
|
|
"firmware_output_log",
|
|
"firmware_input_log",
|
|
"sequence_body_log",
|
|
"sequence_complete_log",
|
|
"sequence_init_log",
|
|
"arduino_debug_messages",
|
|
"os_auto_update",
|
|
"firmware_hardware",
|
|
"network_not_found_timer"
|
|
]
|
|
|
|
@firmware_keys [
|
|
"param_version",
|
|
"param_test",
|
|
"param_config_ok",
|
|
"param_use_eeprom",
|
|
"param_e_stop_on_mov_err",
|
|
"param_mov_nr_retry",
|
|
"movement_timeout_x",
|
|
"movement_timeout_y",
|
|
"movement_timeout_z",
|
|
"movement_keep_active_x",
|
|
"movement_keep_active_y",
|
|
"movement_keep_active_z",
|
|
"movement_home_at_boot_x",
|
|
"movement_home_at_boot_y",
|
|
"movement_home_at_boot_z",
|
|
"movement_invert_endpoints_x",
|
|
"movement_invert_endpoints_y",
|
|
"movement_invert_endpoints_z",
|
|
"movement_enable_endpoints_x",
|
|
"movement_enable_endpoints_y",
|
|
"movement_enable_endpoints_z",
|
|
"movement_invert_motor_x",
|
|
"movement_invert_motor_y",
|
|
"movement_invert_motor_z",
|
|
"movement_secondary_motor_x",
|
|
"movement_secondary_motor_invert_x",
|
|
"movement_steps_acc_dec_x",
|
|
"movement_steps_acc_dec_y",
|
|
"movement_steps_acc_dec_z",
|
|
"movement_stop_at_home_x",
|
|
"movement_stop_at_home_y",
|
|
"movement_stop_at_home_z",
|
|
"movement_home_up_x",
|
|
"movement_home_up_y",
|
|
"movement_home_up_z",
|
|
"movement_step_per_mm_x",
|
|
"movement_step_per_mm_y",
|
|
"movement_step_per_mm_z",
|
|
"movement_min_spd_x",
|
|
"movement_min_spd_y",
|
|
"movement_min_spd_z",
|
|
"movement_home_spd_x",
|
|
"movement_home_spd_y",
|
|
"movement_home_spd_z",
|
|
"movement_max_spd_x",
|
|
"movement_max_spd_y",
|
|
"movement_max_spd_z",
|
|
"encoder_enabled_x",
|
|
"encoder_enabled_y",
|
|
"encoder_enabled_z",
|
|
"encoder_type_x",
|
|
"encoder_type_y",
|
|
"encoder_type_z",
|
|
"encoder_missed_steps_max_x",
|
|
"encoder_missed_steps_max_y",
|
|
"encoder_missed_steps_max_z",
|
|
"encoder_scaling_x",
|
|
"encoder_scaling_y",
|
|
"encoder_scaling_z",
|
|
"encoder_missed_steps_decay_x",
|
|
"encoder_missed_steps_decay_y",
|
|
"encoder_missed_steps_decay_z",
|
|
"encoder_use_for_pos_x",
|
|
"encoder_use_for_pos_y",
|
|
"encoder_use_for_pos_z",
|
|
"encoder_invert_x",
|
|
"encoder_invert_y",
|
|
"encoder_invert_z",
|
|
"movement_axis_nr_steps_x",
|
|
"movement_axis_nr_steps_y",
|
|
"movement_axis_nr_steps_z",
|
|
"movement_stop_at_max_x",
|
|
"movement_stop_at_max_y",
|
|
"movement_stop_at_max_z",
|
|
"pin_guard_1_pin_nr",
|
|
"pin_guard_1_time_out",
|
|
"pin_guard_1_active_state",
|
|
"pin_guard_2_pin_nr",
|
|
"pin_guard_2_time_out",
|
|
"pin_guard_2_active_state",
|
|
"pin_guard_3_pin_nr",
|
|
"pin_guard_3_time_out",
|
|
"pin_guard_3_active_state",
|
|
"pin_guard_4_pin_nr",
|
|
"pin_guard_4_time_out",
|
|
"pin_guard_4_active_state",
|
|
"pin_guard_5_pin_nr",
|
|
"pin_guard_5_time_out",
|
|
"pin_guard_5_active_state",
|
|
]
|
|
|
|
# TODO: This should be moved to ConfigStorage module maybe?
|
|
@bool_keys [
|
|
"auto_sync",
|
|
"beta_opt_in",
|
|
"disable_factory_reset",
|
|
"firmware_output_log",
|
|
"firmware_input_log",
|
|
"sequence_body_log",
|
|
"sequence_complete_log",
|
|
"sequence_init_log",
|
|
"arduino_debug_messages",
|
|
"os_auto_update"
|
|
]
|
|
|
|
@string_keys [
|
|
"firmware_hardware"
|
|
]
|
|
|
|
@float_keys @firmware_keys ++ [
|
|
"network_not_found_timer"
|
|
]
|
|
|
|
@doc false
|
|
def start_link() do
|
|
run()
|
|
:ignore
|
|
end
|
|
|
|
@doc false
|
|
def run() do
|
|
do_sync_fbos_configs()
|
|
do_sync_fw_configs()
|
|
Logger.debug 1, "Synced fbos and fw settings with API"
|
|
:ok
|
|
rescue
|
|
err ->
|
|
message = Exception.message(err)
|
|
err_msg = "#{message} #{inspect System.stacktrace()}"
|
|
Logger.error 1, "Error syncing settings: #{err_msg}"
|
|
update_config_value(:bool, "settings", "ignore_fbos_config", false)
|
|
{:error, message}
|
|
end
|
|
|
|
def apply_fbos_map(old_map, new_map) do
|
|
old_map = take_valid_fbos(old_map)
|
|
new_map = take_valid_fbos(new_map)
|
|
Map.new(new_map, fn({key, new_value}) ->
|
|
if old_map[key] != new_value do
|
|
Logger.debug 3, "Got new config update: #{key} => #{new_value}"
|
|
apply_to_config_storage key, new_value
|
|
end
|
|
{key, new_value}
|
|
end)
|
|
end
|
|
|
|
def apply_fw_map(old_map, new_map) do
|
|
old_map = take_valid_fw(old_map)
|
|
new_map = take_valid_fw(new_map)
|
|
new_stuff = Map.new(new_map, fn({key, new_value}) ->
|
|
if old_map[key] != new_value do
|
|
apply_to_config_storage key, new_value
|
|
end
|
|
{key, new_value}
|
|
end)
|
|
|
|
if Process.whereis(Farmbot.Firmware) do
|
|
for {param, new_value} <- new_stuff do
|
|
if old_map[param] != new_value do
|
|
Farmbot.Firmware.update_param(String.to_atom(param), new_value)
|
|
end
|
|
end
|
|
end
|
|
|
|
new_stuff
|
|
end
|
|
|
|
defp apply_to_config_storage(key, val)
|
|
when key in @bool_keys and (is_nil(val) or is_boolean(val)) do
|
|
Logger.success 2, "Updating: #{key} => #{inspect val}"
|
|
update_config_value(:bool, "settings", key, val)
|
|
end
|
|
|
|
defp apply_to_config_storage(key, val)
|
|
when key in @string_keys and (is_nil(val) or is_binary(val)) do
|
|
Logger.success 2, "Updating: #{key} => #{inspect val}"
|
|
update_config_value(:string, "settings", key, val)
|
|
end
|
|
|
|
defp apply_to_config_storage(key, val)
|
|
when key in @firmware_keys do
|
|
Logger.success 2, "Updating FW param: #{key} => #{inspect val}"
|
|
if val do
|
|
update_config_value(:float, "hardware_params", key, val / 1)
|
|
else
|
|
Logger.warn 2, "Not allowing #{key} to be set to null"
|
|
end
|
|
end
|
|
|
|
defp apply_to_config_storage(key, val)
|
|
when key in @float_keys and (is_nil(val) or is_number(val)) do
|
|
Logger.success 2, "Updating: #{key} => #{inspect val}"
|
|
if val do
|
|
update_config_value(:float, "settings", key, val / 1)
|
|
else
|
|
update_config_value(:float, "settings", key, val)
|
|
end
|
|
end
|
|
|
|
defp apply_to_config_storage(key, val) do
|
|
Logger.error 1, "Unknown pair: #{key} => #{inspect val}"
|
|
{:error, {:unknown_pair, {key, val}}}
|
|
end
|
|
|
|
@doc "Sync the settings related to the Firmware."
|
|
def do_sync_fw_configs do
|
|
with {:ok, %{body: body, status_code: 200}} <- Farmbot.HTTP.get("/api/firmware_config"),
|
|
{:ok, data} <- Poison.decode(body)
|
|
do
|
|
do_sync_fw_configs(data)
|
|
else
|
|
{:ok, status_code: code} ->
|
|
Logger.error 1, "HTTP error syncing settings: #{code}"
|
|
:ok
|
|
err ->
|
|
Logger.error 1, "Error syncing settings: #{inspect err}"
|
|
:ok
|
|
end
|
|
end
|
|
|
|
def do_sync_fw_configs(%{"api_migrated" => true} = api_data) do
|
|
Logger.info 3, "API is the source of truth for Firmware configs. Downloading data."
|
|
old_config = get_config_as_map()["hardware_params"]
|
|
apply_fw_map(old_config, api_data)
|
|
:ok
|
|
end
|
|
|
|
def do_sync_fw_configs(%{"api_migrated" => false} = api_data) do
|
|
if get_config_value(:bool, "settings", "firmware_needs_first_sync") do
|
|
Logger.info 3, "Firmware Settings have never been synced before, and settings have never been uploaded to the API."
|
|
old_config = get_config_as_map()["hardware_params"] |> Map.new(fn({key, _}) ->
|
|
{key, :hack}
|
|
end)
|
|
|
|
new = apply_fw_map(old_config, api_data)
|
|
payload = Map.put(new, "api_migrated", true) |> Poison.encode!()
|
|
Farmbot.HTTP.put!("/api/firmware_config", payload)
|
|
update_config_value(:bool, "settings", "firmware_needs_first_sync", false)
|
|
else
|
|
Logger.info 3, "FBOS is the source of truth for Firmware configs. Uploading data."
|
|
current = get_config_as_map()["hardware_params"]
|
|
payload = Map.put(current, "api_migrated", true) |> Poison.encode!()
|
|
Farmbot.HTTP.delete!("/api/firmware_config")
|
|
Farmbot.HTTP.put!("/api/firmware_config", payload)
|
|
end
|
|
|
|
:ok
|
|
end
|
|
|
|
def do_sync_fw_configs(_) do
|
|
Logger.error 1, "API is out of date. Not syncing firmware settings!"
|
|
:ok
|
|
end
|
|
|
|
@doc "Sync the settings related to FBOS"
|
|
def do_sync_fbos_configs do
|
|
with {:ok, %{body: body, status_code: 200}} <- Farmbot.HTTP.get("/api/fbos_config"),
|
|
{:ok, data} <- Poison.decode(body)
|
|
do
|
|
do_sync_fbos_configs(data)
|
|
else
|
|
{:ok, status_code: code} ->
|
|
Logger.error 1, "HTTP error syncing settings: #{code}"
|
|
:ok
|
|
err ->
|
|
Logger.error 1, "Error syncing settings: #{inspect err}"
|
|
:ok
|
|
end
|
|
end
|
|
|
|
def do_sync_fbos_configs(%{"api_migrated" => true} = api_data) do
|
|
Logger.info 3, "API is the source of truth for FBOS configs. Downloading data."
|
|
old_config = get_config_as_map()["settings"]
|
|
apply_fbos_map(old_config, api_data)
|
|
:ok
|
|
end
|
|
|
|
def do_sync_fbos_configs(_unimportant_data) do
|
|
Logger.info 3, "FBOS is the source of truth for FBOS configs. Uploading data."
|
|
update_config_value(:bool, "settings", "ignore_fbos_config", true)
|
|
auto_sync = get_config_value(:bool, "settings", "auto_sync")
|
|
beta_opt_in = get_config_value(:bool, "settings", "beta_opt_in")
|
|
disable_factory_reset = get_config_value(:bool, "settings", "disable_factory_reset")
|
|
firmware_output_log = get_config_value(:bool, "settings", "firmware_output_log")
|
|
firmware_input_log = get_config_value(:bool, "settings", "firmware_input_log")
|
|
sequence_body_log = get_config_value(:bool, "settings", "sequence_body_log")
|
|
sequence_complete_log = get_config_value(:bool, "settings", "sequence_complete_log")
|
|
sequence_init_log = get_config_value(:bool, "settings", "sequence_init_log")
|
|
arduino_debug_messages = get_config_value(:bool, "settings", "arduino_debug_messages")
|
|
os_auto_update = get_config_value(:bool, "settings", "os_auto_update")
|
|
firmware_hardware = get_config_value(:string, "settings", "firmware_hardware")
|
|
network_not_found_timer = get_config_value(:float, "settings", "network_not_found_timer")
|
|
payload = %{
|
|
api_migrated: true,
|
|
auto_sync: auto_sync,
|
|
beta_opt_in: beta_opt_in,
|
|
disable_factory_reset: disable_factory_reset,
|
|
firmware_output_log: firmware_output_log,
|
|
firmware_input_log: firmware_input_log,
|
|
sequence_body_log: sequence_body_log,
|
|
sequence_complete_log: sequence_complete_log,
|
|
sequence_init_log: sequence_init_log,
|
|
arduino_debug_messages: arduino_debug_messages,
|
|
os_auto_update: os_auto_update,
|
|
firmware_hardware: firmware_hardware,
|
|
network_not_found_timer: network_not_found_timer,
|
|
} |> Poison.encode!()
|
|
Farmbot.HTTP.delete!("/api/fbos_config")
|
|
Farmbot.HTTP.put!("/api/fbos_config", payload)
|
|
update_config_value(:bool, "settings", "ignore_fbos_config", false)
|
|
:ok
|
|
end
|
|
|
|
def take_valid_fbos(map) do
|
|
Map.take(map, @fbos_keys ++ Enum.map(@fbos_keys, &String.to_atom(&1)))
|
|
end
|
|
|
|
def take_valid_fw(%{param_config_ok: _} = atom_map) do
|
|
Map.new(atom_map, fn({key, val}) -> {to_string(key), val} end)
|
|
end
|
|
|
|
def take_valid_fw(map) do
|
|
Map.take(map, @firmware_keys ++ Enum.map(@firmware_keys, &String.to_atom(&1)))
|
|
|> Map.drop(["param_version", "param_test", "param_config_ok", "param_use_eeprom"])
|
|
end
|
|
end
|