From 88d7b80f9d9dd8862a9960ae07cce307671fa482 Mon Sep 17 00:00:00 2001 From: Rick Carlino Date: Mon, 30 Dec 2019 13:17:08 -0600 Subject: [PATCH 01/16] Release v9.0.1-rc1 --- CHANGELOG.md | 3 +++ VERSION | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c227c1df..d40b1bc29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +# 9.0.1 + * Routine token updates on Circle CI. + # 9.0.0 * Run updates on Nerves systems. * Updates to the way `set_servo_angle` is handled. diff --git a/VERSION b/VERSION index f7ee06693..ee3ea8073 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -9.0.0 +9.0.1-rc1 From 77337a6bb6d50357a2bc55fddc274b0c3c0a45e7 Mon Sep 17 00:00:00 2001 From: Gabriel Burnworth Date: Mon, 30 Dec 2019 11:38:20 -0800 Subject: [PATCH 02/16] Update FEATURE_MIN_VERSIONS.json [skip ci] --- FEATURE_MIN_VERSIONS.json | 1 - 1 file changed, 1 deletion(-) diff --git a/FEATURE_MIN_VERSIONS.json b/FEATURE_MIN_VERSIONS.json index 54212e392..057eb51d7 100644 --- a/FEATURE_MIN_VERSIONS.json +++ b/FEATURE_MIN_VERSIONS.json @@ -4,7 +4,6 @@ "api_pin_bindings": "6.4.4", "assertion_block": "8.0.0", "backscheduled_regimens": "6.4.0", - "boot_sequence": "8.3.0", "change_ownership": "6.3.0", "diagnostic_dumps": "6.4.4", "endstop_invert": "6.4.1", From 872ee5a6c6e57b0942009defcadda2f98efab694 Mon Sep 17 00:00:00 2001 From: Connor Rigby Date: Tue, 31 Dec 2019 08:31:44 -0800 Subject: [PATCH 03/16] Update deps for farmbot_os otp app --- farmbot_os/mix.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/farmbot_os/mix.lock b/farmbot_os/mix.lock index 63300b207..a87ed6557 100644 --- a/farmbot_os/mix.lock +++ b/farmbot_os/mix.lock @@ -13,7 +13,7 @@ "cowlib": {:hex, :cowlib, "2.7.3", "a7ffcd0917e6d50b4d5fb28e9e2085a0ceb3c97dea310505f7460ff5ed764ce9", [:rebar3], [], "hexpm"}, "credentials_obfuscation": {:hex, :credentials_obfuscation, "1.1.0", "513793cc20c18afc9e03e584b436192a751a8344890e03a8741c65c8d6866fab", [:rebar3], [], "hexpm"}, "db_connection": {:hex, :db_connection, "1.1.3", "89b30ca1ef0a3b469b1c779579590688561d586694a3ce8792985d4d7e575a61", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"}, - "decimal": {:hex, :decimal, "1.8.0", "ca462e0d885f09a1c5a342dbd7c1dcf27ea63548c65a65e67334f4b61803822e", [:mix], [], "hexpm"}, + "decimal": {:hex, :decimal, "1.8.1", "a4ef3f5f3428bdbc0d35374029ffcf4ede8533536fa79896dd450168d9acdf3c", [:mix], [], "hexpm"}, "dialyxir": {:hex, :dialyxir, "1.0.0-rc.6", "78e97d9c0ff1b5521dd68041193891aebebce52fc3b93463c0a6806874557d7d", [:mix], [{:erlex, "~> 0.2.1", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm"}, "dns": {:hex, :dns, "2.1.2", "81c46d39f7934f0e73368355126e4266762cf227ba61d5889635d83b2d64a493", [:mix], [{:socket, "~> 0.3.13", [hex: :socket, repo: "hexpm", optional: false]}], "hexpm"}, "earmark": {:hex, :earmark, "1.4.3", "364ca2e9710f6bff494117dbbd53880d84bebb692dafc3a78eb50aa3183f2bfd", [:mix], [], "hexpm"}, @@ -59,10 +59,10 @@ "nerves_toolchain_ctng": {:hex, :nerves_toolchain_ctng, "1.6.0", "452f8589c1a58ac787477caab20a8cfc6671e345837ccc19beefe49ae35ba983", [:mix], [{:nerves, "~> 1.0", [hex: :nerves, repo: "hexpm", optional: false]}], "hexpm"}, "nimble_csv": {:hex, :nimble_csv, "0.6.0", "a3673f26d41f986774fe6060e309615343d3cb83a6d435754d8b1fdbd5764879", [:mix], [], "hexpm"}, "nimble_parsec": {:hex, :nimble_parsec, "0.5.3", "def21c10a9ed70ce22754fdeea0810dafd53c2db3219a0cd54cf5526377af1c6", [:mix], [], "hexpm"}, - "one_dhcpd": {:hex, :one_dhcpd, "0.2.3", "753f1495a5f1b29d24b6e1f1e4e9c30c54242daae236bed6baff32027165559e", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm"}, + "one_dhcpd": {:hex, :one_dhcpd, "0.2.4", "2664f2e1ac72cbae0474a88d1a3d55019ccc3ee582ded382589511627a8ed516", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm"}, "parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"}, "pbcs": {:hex, :pbcs, "0.1.1", "199c7fd4af3351758378355909145a2d187c565555ed16bde30b5055114652ed", [:mix], [], "hexpm"}, - "phoenix_client": {:hex, :phoenix_client, "0.9.0", "973fdcadfd4e540558efa12a8dba028ba393712bece36ab523f2c218d5a1ae86", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:websocket_client, "~> 1.3", [hex: :websocket_client, repo: "hexpm", optional: true]}], "hexpm"}, + "phoenix_client": {:hex, :phoenix_client, "0.10.0", "a77ace5495c400001808e96980673dd3b97b1048f296fd032991c52e8f5fe93d", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:websocket_client, "~> 1.3", [hex: :websocket_client, repo: "hexpm", optional: true]}], "hexpm"}, "phoenix_html": {:hex, :phoenix_html, "2.13.3", "850e292ff6e204257f5f9c4c54a8cb1f6fbc16ed53d360c2b780a3d0ba333867", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, "plug": {:hex, :plug, "1.8.3", "12d5f9796dc72e8ac9614e94bda5e51c4c028d0d428e9297650d09e15a684478", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm"}, "plug_cowboy": {:hex, :plug_cowboy, "2.1.0", "b75768153c3a8a9e8039d4b25bb9b14efbc58e9c4a6e6a270abff1cd30cbe320", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, @@ -88,10 +88,10 @@ "uboot_env": {:hex, :uboot_env, "0.1.1", "b01e3ec0973e99473234f27839e29e63b5b81eba6a136a18a78d049d4813d6c5", [:mix], [], "hexpm"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"}, "uuid": {:hex, :uuid, "1.1.8", "e22fc04499de0de3ed1116b770c7737779f226ceefa0badb3592e64d5cfb4eb9", [:mix], [], "hexpm"}, - "vintage_net": {:hex, :vintage_net, "0.7.0", "31f5f9df881968572597aaab18ab0aaa13b0383805316d34a00f79fc16bbde0d", [:make, :mix], [{:busybox, "~> 0.1.4", [hex: :busybox, repo: "hexpm", optional: true]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:gen_state_machine, "~> 2.0.0", [hex: :gen_state_machine, repo: "hexpm", optional: false]}, {:muontrap, "~> 0.5.0", [hex: :muontrap, repo: "hexpm", optional: false]}], "hexpm"}, + "vintage_net": {:hex, :vintage_net, "0.7.2", "b14468e29704156f133a9342fd41c8cfc9bae0849bfa20a6859b29ac9e28166e", [:make, :mix], [{:busybox, "~> 0.1.4", [hex: :busybox, repo: "hexpm", optional: true]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:gen_state_machine, "~> 2.0.0", [hex: :gen_state_machine, repo: "hexpm", optional: false]}, {:muontrap, "~> 0.5.0", [hex: :muontrap, repo: "hexpm", optional: false]}], "hexpm"}, "vintage_net_direct": {:hex, :vintage_net_direct, "0.7.0", "6a8d95432fc2fb9a9e0225bf1a6dbb7c1ba097bb509a989e947ad3f44d2b0f28", [:mix], [{:one_dhcpd, "~> 0.2.3", [hex: :one_dhcpd, repo: "hexpm", optional: false]}, {:vintage_net, "~> 0.7.0", [hex: :vintage_net, repo: "hexpm", optional: false]}], "hexpm"}, "vintage_net_ethernet": {:hex, :vintage_net_ethernet, "0.7.0", "b66a4da0846b4a55a471d15d8ab2bedbbef9c75c18f4e511c43777f7cfeffa09", [:mix], [{:vintage_net, "~> 0.7.0", [hex: :vintage_net, repo: "hexpm", optional: false]}], "hexpm"}, "vintage_net_wifi": {:hex, :vintage_net_wifi, "0.7.0", "be3d8275fa40ba357159033523606c74c8f612dbd60801840df60835453a6906", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:vintage_net, "~> 0.7.0", [hex: :vintage_net, repo: "hexpm", optional: false]}], "hexpm"}, "websocket_client": {:hex, :websocket_client, "1.3.0", "2275d7daaa1cdacebf2068891c9844b15f4fdc3de3ec2602420c2fb486db59b6", [:rebar3], [], "hexpm"}, - "x509": {:hex, :x509, "0.7.0", "001b762cd99e1a33bc876bb090bd34d9cadc66eb7df3deb76d9f4d8a37b89612", [:mix], [], "hexpm"}, + "x509": {:hex, :x509, "0.8.0", "b286b9dbb32801f76f48bdea476304d280c64ce172ea330c23d8df7ea9e75ce6", [:mix], [], "hexpm"}, } From 6fbb0c6ac85079dcfba21e535783a79f97312388 Mon Sep 17 00:00:00 2001 From: Connor Rigby Date: Tue, 31 Dec 2019 11:05:06 -0800 Subject: [PATCH 04/16] Elaborate on how firmware updates work --- .../releasing_target_firmware.md | 78 ++++++++++++++++--- 1 file changed, 69 insertions(+), 9 deletions(-) diff --git a/docs/target_development/releasing_target_firmware.md b/docs/target_development/releasing_target_firmware.md index 63360ab07..5b3202937 100644 --- a/docs/target_development/releasing_target_firmware.md +++ b/docs/target_development/releasing_target_firmware.md @@ -1,14 +1,62 @@ # Publishing OTAs -## Beta +## Beta OTA channel -Publish an OTA to the `beta` channel can be done by: +Beta updates are simply tags matching the following semver string: + +``` +vMajor.Minor.Tiny-rcRC +``` + +for example: + +``` +v10.5.6-rc30 +``` + +To publish an OTA, just tag a release matching that +string. + +```bash +cd $FARMBOT_OS_ROOT_DIRECTORY +git checkout staging +git fetch --all && git reset --hard origin/staging +echo 10.5.6-rc30 > VERSION +# update the CHANGELOG, but DO NOT put the `rc` +# on the semver string. +$EDITOR CHANGELOG.md +git add CHANGELOG.md VERSION +git commit -m "Release v10.5.6-rc30" +git tag v$(cat VERSION) +git push origin staging v$(cat VERSION) +``` + +or call the helper script: +`./scripts/release_candidate.sh` + +### NOTE about release candidate script + +the helper script only **increments** the +RC version. Calling the `release-candidate` script +from a non rc version will fail. Example: + +This will fail: + +```bash +$ cat VERSION +10.5.6 +./scripts/release_candidate.sh +``` + +This will succeed: ```bash +$ cat VERSION +10.5.6-rc44 ./scripts/release_candidate.sh ``` -## QA +## QA OTA channel Publish an OTA to the `qa` channel can be done by pushing a new branch to github with `qa/` prefix. @@ -18,17 +66,29 @@ git checkout -b qa/ git push origin qa/ ``` -## Production +or to build a QA image from an existing branch: + +```bash +git checkout -b some-feature +git commit -am "build out some feature" +git push origin some-feature some-feature:qa/some-featuer +``` + +## Stable OTA channel -Publish an OTA to the `stable` channel can be done by: +Publish an OTA to the `stable` OTA channel can be +done by pushing anything to the master branch: ```bash -git checkout -b rel- # update VERSION +echo $NEW_VERSION > VERSION # update CHANGELOG.md -# update README.md -git commit -am "Release v" -git push origin rel- +$EDITOR CHANGELOG.md +# update the download link in the readme +$EDITOR README.md +git checkout -b rel-$(cat VERSION) +git commit -am 'Release v$(cat VERSION)' +git push origin rel-$(cat VERSION) # open pull request # merge pull request # publish release once CI has completed From 57cc8a8b37652c169831be739f85f2b5a1dd3ecf Mon Sep 17 00:00:00 2001 From: Rick Carlino Date: Wed, 8 Jan 2020 10:37:53 -0600 Subject: [PATCH 05/16] Tests for AVRDude / MuonTrap usage --- .../lib/farmbot_core/farmware_runtime.ex | 41 +++++++++++-------- farmbot_os/config/host/test.exs | 1 + farmbot_os/lib/avrdude.ex | 8 +++- farmbot_os/lib/avrdude/muon_trap_adapter.ex | 17 ++++++++ .../lib/avrdude/muon_trap_default_adapter.ex | 6 +++ .../lib/avrdude/muon_trap_test_adapter.ex | 8 ++++ .../test/farmbot_os/avrdude/avrdude_test.exs | 40 ++++++++++++++++++ .../test/support/muon_trap_test_adapter.ex | 8 ++++ farmbot_os/test/test_helper.exs | 1 + 9 files changed, 111 insertions(+), 19 deletions(-) create mode 100644 farmbot_os/lib/avrdude/muon_trap_adapter.ex create mode 100644 farmbot_os/lib/avrdude/muon_trap_default_adapter.ex create mode 100644 farmbot_os/lib/avrdude/muon_trap_test_adapter.ex create mode 100644 farmbot_os/test/farmbot_os/avrdude/avrdude_test.exs create mode 100644 farmbot_os/test/support/muon_trap_test_adapter.ex diff --git a/farmbot_core/lib/farmbot_core/farmware_runtime.ex b/farmbot_core/lib/farmbot_core/farmware_runtime.ex index 87419a0c4..cf1d14155 100644 --- a/farmbot_core/lib/farmbot_core/farmware_runtime.ex +++ b/farmbot_core/lib/farmbot_core/farmware_runtime.ex @@ -3,11 +3,12 @@ defmodule FarmbotCore.FarmwareRuntime do Handles execution of Farmware plugins. """ + alias Avrdude.MuonTrapAdapter alias FarmbotCeleryScript.AST - alias FarmbotCore.FarmwareRuntime.PipeWorker - alias FarmbotCore.AssetWorker.FarmbotCore.Asset.FarmwareInstallation alias FarmbotCore.Asset.FarmwareInstallation.Manifest + alias FarmbotCore.AssetWorker.FarmbotCore.Asset.FarmwareInstallation alias FarmbotCore.BotState.FileSystem + alias FarmbotCore.FarmwareRuntime.PipeWorker alias FarmbotCore.Project import FarmwareInstallation, only: [install_dir: 1] @@ -67,7 +68,8 @@ defmodule FarmbotCore.FarmwareRuntime do @doc "Stop a farmware" def stop(pid) do - Logger.info "Terminating farmware process" + Logger.info("Terminating farmware process") + if Process.alive?(pid) do GenServer.stop(pid, :normal) end @@ -75,7 +77,7 @@ defmodule FarmbotCore.FarmwareRuntime do def init([manifest, env, caller]) do package = manifest.package - <> = Ecto.UUID.generate() + <> = Ecto.UUID.generate() request_pipe = Path.join([ @@ -109,8 +111,10 @@ defmodule FarmbotCore.FarmwareRuntime do ) # Start the plugin. - Logger.debug "spawning farmware: #{exec} #{manifest.args}" - {cmd, _} = spawn_monitor(MuonTrap, :cmd, ["sh", ["-c", "#{exec} #{manifest.args}"], opts]) + Logger.debug("spawning farmware: #{exec} #{manifest.args}") + + {cmd, _} = + spawn_monitor(MuonTrapAdapter, :cmd, ["sh", ["-c", "#{exec} #{manifest.args}"], opts]) state = %State{ caller: caller, @@ -125,7 +129,7 @@ defmodule FarmbotCore.FarmwareRuntime do response_pipe_handle: resp } - send self(), :timeout + send(self(), :timeout) {:ok, state} end @@ -142,12 +146,12 @@ defmodule FarmbotCore.FarmwareRuntime do end def handle_info(msg, %{context: :error} = state) do - Logger.warn "unhandled message in error state: #{inspect(msg)}" + Logger.warn("unhandled message in error state: #{inspect(msg)}") {:noreply, state} end def handle_info({:step_complete, ref, {:error, reason}}, %{scheduler_ref: ref} = state) do - send state.caller, {:error, reason} + send(state.caller, {:error, reason}) {:noreply, %{state | ref: nil, context: :error}} end @@ -159,7 +163,7 @@ defmodule FarmbotCore.FarmwareRuntime do _reply = PipeWorker.write(state.response_pipe_handle, ipc) # Make sure to `timeout` after this one to go back to the # get_header context. This will cause another rpc to be processed. - send self(), :timeout + send(self(), :timeout) {:noreply, %{state | rpc: nil, context: :get_header}} end @@ -174,14 +178,14 @@ defmodule FarmbotCore.FarmwareRuntime do # didn't pick up the scheduled AST in a reasonable amount of time. def handle_info(:timeout, %{context: :process_request} = state) do Logger.error("Timeout waiting for #{inspect(state.rpc)} to be processed") - send state.caller, {:error, :rpc_timeout} + send(state.caller, {:error, :rpc_timeout}) {:noreply, %{state | context: :error}} end # farmware exit def handle_info({:DOWN, _ref, :process, _pid, _reason}, %{cmd: _cmd_pid} = state) do Logger.debug("Farmware exit") - send state.caller, {:error, :farmware_exit} + send(state.caller, {:error, :farmware_exit}) {:noreply, %{state | context: :error}} end @@ -200,14 +204,14 @@ defmodule FarmbotCore.FarmwareRuntime do # error result of an io:read/2 in :get_header context def handle_info({PipeWorker, _ref, {:ok, data}}, %{context: :get_header} = state) do Logger.error("Bad header: #{inspect(data, base: :hex, limit: :infinity)}") - send state.caller, {:error, {:unhandled_packet, data}} + send(state.caller, {:error, {:unhandled_packet, data}}) {:noreply, %{state | context: :error}} end # error result of an io:read/2 in :get_header context def handle_info({PipeWorker, _ref, error}, %{context: :get_header} = state) do Logger.error("Bad header: #{inspect(error)}") - send state.caller, {:error, :bad_packet_header} + send(state.caller, {:error, :bad_packet_header}) {:noreply, %{state | context: :error}} end @@ -219,7 +223,7 @@ defmodule FarmbotCore.FarmwareRuntime do # error result of an io:read/2 in :get_header context def handle_info({PipeWorker, _ref, error}, %{context: :get_payload} = state) do Logger.error("Bad payload: #{inspect(error)}") - send state.caller, {:error, :bad_packet_payload} + send(state.caller, {:error, :bad_packet_payload}) {:noreply, %{state | context: :error}} end @@ -247,10 +251,12 @@ defmodule FarmbotCore.FarmwareRuntime do Logger.debug("executing rpc from farmware: #{inspect(rpc)}") # todo(connor) replace this with StepRunner? FarmbotCeleryScript.execute(rpc, ref) - {:noreply, %{state | rpc: rpc, scheduler_ref: ref, context: :process_request}, @error_timeout_ms} + + {:noreply, %{state | rpc: rpc, scheduler_ref: ref, context: :process_request}, + @error_timeout_ms} else {:error, reason} -> - send state.caller, {:error, reason} + send(state.caller, {:error, reason}) {:noreply, %{state | context: :error}} end end @@ -300,6 +306,7 @@ defmodule FarmbotCore.FarmwareRuntime do header = <<@packet_header_token::size(16)>> <> :binary.copy(<<0x00>>, 4) <> <> + header <> payload end end diff --git a/farmbot_os/config/host/test.exs b/farmbot_os/config/host/test.exs index 903988f55..7437d2405 100644 --- a/farmbot_os/config/host/test.exs +++ b/farmbot_os/config/host/test.exs @@ -39,3 +39,4 @@ config :farmbot_core, FarmbotCore.AssetWorker.FarmbotCore.Asset.FbosConfig, firmware_flash_attempt_threshold: 0 config :plug, :validate_header_keys_during_test, true +config :farmbot, :muon_trap_adapter, Avrdude.MuonTrapTestAdapter diff --git a/farmbot_os/lib/avrdude.ex b/farmbot_os/lib/avrdude.ex index 07eb0d2f0..f6569e762 100644 --- a/farmbot_os/lib/avrdude.ex +++ b/farmbot_os/lib/avrdude.ex @@ -30,7 +30,11 @@ defmodule Avrdude do # call the function for resetting the line before executing avrdude. call_reset_fun(reset_fun) - MuonTrap.cmd("avrdude", args, into: IO.stream(:stdio, :line), stderr_to_stdout: true) + + Avrdude.MuonTrapAdapter.cmd("avrdude", args, + into: IO.stream(:stdio, :line), + stderr_to_stdout: true + ) end def call_reset_fun(reset_fun) do @@ -39,7 +43,7 @@ defmodule Avrdude do catch error_type, error -> FarmbotCore.Logger.error(1, """ - Error calling reset function: #{inspect(reset_fun)} + Error calling reset function: #{inspect(reset_fun)} error type: #{error_type} error: #{inspect(error)} """) diff --git a/farmbot_os/lib/avrdude/muon_trap_adapter.ex b/farmbot_os/lib/avrdude/muon_trap_adapter.ex new file mode 100644 index 000000000..504f87bda --- /dev/null +++ b/farmbot_os/lib/avrdude/muon_trap_adapter.ex @@ -0,0 +1,17 @@ +defmodule Avrdude.MuonTrapAdapter do + @type exe :: String.t() + @type args :: [String.t()] + @type io_stream :: Enumerable.t() + @type option :: {:into, io_stream()} | {:stderr_to_stdout, boolean()} + + @callback cmd(exe, args, list(option)) :: {String.t(), non_neg_integer} + + @doc false + def adapter do + Application.get_env(:farmbot, :muon_trap_adapter, Avrdude.MuonTrapDefaultAdapter) + end + + def cmd(exe, args, options) do + adapter().cmd(exe, args, options) + end +end diff --git a/farmbot_os/lib/avrdude/muon_trap_default_adapter.ex b/farmbot_os/lib/avrdude/muon_trap_default_adapter.ex new file mode 100644 index 000000000..4e4b94e10 --- /dev/null +++ b/farmbot_os/lib/avrdude/muon_trap_default_adapter.ex @@ -0,0 +1,6 @@ +defmodule Avrdude.MuonTrapDefaultAdapter do + @behaviour Avrdude.MuonTrapAdapter + + @impl Avrdude.MuonTrapAdapter + defdelegate cmd(exe, args, options), to: MuonTrap, as: :cmd +end diff --git a/farmbot_os/lib/avrdude/muon_trap_test_adapter.ex b/farmbot_os/lib/avrdude/muon_trap_test_adapter.ex new file mode 100644 index 000000000..6502277cc --- /dev/null +++ b/farmbot_os/lib/avrdude/muon_trap_test_adapter.ex @@ -0,0 +1,8 @@ +defmodule Avrdude.MuonTrapTestAdapter do + @behaviour Avrdude.MuonTrapAdapter + + @impl Avrdude.MuonTrapAdapter + def cmd(exe, args, options) do + {exe, args, options} + end +end diff --git a/farmbot_os/test/farmbot_os/avrdude/avrdude_test.exs b/farmbot_os/test/farmbot_os/avrdude/avrdude_test.exs new file mode 100644 index 000000000..1f069d551 --- /dev/null +++ b/farmbot_os/test/farmbot_os/avrdude/avrdude_test.exs @@ -0,0 +1,40 @@ +Mox.defmock(Avrdude.MuonTrapAdapter, for: Avrdude.MuonTrapAdapter) + +defmodule FarmbotOs.AvrdudeTest do + use ExUnit.Case + + import Mox + + setup [:verify_on_exit!] + + test "works" do + File.touch("/tmp/wow") + + expect(Avrdude.MuonTrapAdapter, :cmd, fn cmd, args, opts -> + assert cmd == "avrdude" + + assert args == [ + "-patmega2560", + "-cwiring", + "-P/dev/null", + "-b115200", + "-D", + "-V", + "-Uflash:w:/tmp/wow:i" + ] + + assert opts == [ + into: %IO.Stream{ + device: :standard_io, + line_or_bytes: :line, + raw: false + }, + stderr_to_stdout: true + ] + end) + + Avrdude.flash("/tmp/wow", "null", fn -> + "YOLO" + end) + end +end diff --git a/farmbot_os/test/support/muon_trap_test_adapter.ex b/farmbot_os/test/support/muon_trap_test_adapter.ex new file mode 100644 index 000000000..6502277cc --- /dev/null +++ b/farmbot_os/test/support/muon_trap_test_adapter.ex @@ -0,0 +1,8 @@ +defmodule Avrdude.MuonTrapTestAdapter do + @behaviour Avrdude.MuonTrapAdapter + + @impl Avrdude.MuonTrapAdapter + def cmd(exe, args, options) do + {exe, args, options} + end +end diff --git a/farmbot_os/test/test_helper.exs b/farmbot_os/test/test_helper.exs index 869559e70..87b7a727e 100644 --- a/farmbot_os/test/test_helper.exs +++ b/farmbot_os/test/test_helper.exs @@ -1 +1,2 @@ +Application.ensure_all_started(:mox) ExUnit.start() From f7582e89223148a1b2bbe8644bc5fa728c41493f Mon Sep 17 00:00:00 2001 From: Rick Carlino Date: Wed, 8 Jan 2020 10:42:14 -0600 Subject: [PATCH 06/16] Remove duplicate file --- farmbot_os/lib/avrdude/muon_trap_test_adapter.ex | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 farmbot_os/lib/avrdude/muon_trap_test_adapter.ex diff --git a/farmbot_os/lib/avrdude/muon_trap_test_adapter.ex b/farmbot_os/lib/avrdude/muon_trap_test_adapter.ex deleted file mode 100644 index 6502277cc..000000000 --- a/farmbot_os/lib/avrdude/muon_trap_test_adapter.ex +++ /dev/null @@ -1,8 +0,0 @@ -defmodule Avrdude.MuonTrapTestAdapter do - @behaviour Avrdude.MuonTrapAdapter - - @impl Avrdude.MuonTrapAdapter - def cmd(exe, args, options) do - {exe, args, options} - end -end From e2548b3af4b8feafbb1c281d99a1f52b90a5da0b Mon Sep 17 00:00:00 2001 From: Rick Carlino Date: Wed, 8 Jan 2020 13:53:18 -0600 Subject: [PATCH 07/16] TEST RELEASE; PLEASE IGNORE. --- farmbot_firmware/lib/farmbot_firmware.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/farmbot_firmware/lib/farmbot_firmware.ex b/farmbot_firmware/lib/farmbot_firmware.ex index 516241d94..3293942b7 100644 --- a/farmbot_firmware/lib/farmbot_firmware.ex +++ b/farmbot_firmware/lib/farmbot_firmware.ex @@ -241,7 +241,7 @@ defmodule FarmbotFirmware do args = Keyword.merge(args, global) transport = Keyword.fetch!(args, :transport) side_effects = Keyword.get(args, :side_effects) - reset = Keyword.fetch!(args, :reset) + reset = Keyword.get(args, :reset) || __MODULE__ vcr_fd = case Keyword.get(args, :vcr_path) do From 2f640c5d13e981a37195291e20024142131ce30d Mon Sep 17 00:00:00 2001 From: Rick Carlino Date: Wed, 8 Jan 2020 14:14:48 -0600 Subject: [PATCH 08/16] If the process has already started, consider firmware reboot complete --- farmbot_firmware/lib/farmbot_firmware.ex | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/farmbot_firmware/lib/farmbot_firmware.ex b/farmbot_firmware/lib/farmbot_firmware.ex index 3293942b7..d16916d01 100644 --- a/farmbot_firmware/lib/farmbot_firmware.ex +++ b/farmbot_firmware/lib/farmbot_firmware.ex @@ -290,6 +290,11 @@ defmodule FarmbotFirmware do Logger.debug("Firmware reset #{state.reset} started. #{inspect(state.transport_args)}") {:noreply, %{state | reset_pid: pid}} + # TODO(Rick): I have no idea what's going on here. + {:error, {:already_started, pid}} -> + Logger.debug("Firmware reset complete. #{inspect(state.transport_args)}") + {:noreply, %{state | reset_pid: pid}} + error -> Logger.error("Error starting Firmware Reset: #{inspect(error)}") Process.send_after(self(), :timeout, @transport_init_error_retry_ms) From d9ba9f25ca8b76a08c8d2e332e9931d8b3b19f07 Mon Sep 17 00:00:00 2001 From: Rick Carlino Date: Wed, 8 Jan 2020 14:44:15 -0600 Subject: [PATCH 09/16] It appears that the `:reset` config is not passed in under all circumstances...? --- .../lib/farmbot_firmware/transports/uart_transport.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/farmbot_firmware/lib/farmbot_firmware/transports/uart_transport.ex b/farmbot_firmware/lib/farmbot_firmware/transports/uart_transport.ex index 0c29c0795..0616433ca 100644 --- a/farmbot_firmware/lib/farmbot_firmware/transports/uart_transport.ex +++ b/farmbot_firmware/lib/farmbot_firmware/transports/uart_transport.ex @@ -14,7 +14,7 @@ defmodule FarmbotFirmware.UARTTransport do def init(args) do device = Keyword.fetch!(args, :device) handle_gcode = Keyword.fetch!(args, :handle_gcode) - reset = Keyword.fetch!(args, :reset) + reset = Keyword.get(args, :reset) {:ok, uart} = UART.start_link() {:ok, %{uart: uart, device: device, open: false, handle_gcode: handle_gcode, reset: reset}, 0} end From 0d549379460bd289cc87a28ae85e545d409014a1 Mon Sep 17 00:00:00 2001 From: Rick Carlino Date: Thu, 9 Jan 2020 11:59:02 -0600 Subject: [PATCH 10/16] Pattern matching problems? --- .../lib/farmbot_core/firmware_side_effects.ex | 13 ++++++++++--- farmbot_ext/lib/farmbot_ext/jwt.ex | 2 +- farmbot_firmware/lib/farmbot_firmware.ex | 4 ++-- farmbot_firmware/lib/farmbot_firmware/command.ex | 2 +- .../lib/farmbot_firmware/gcode/decoder.ex | 4 ++++ .../lib/farmbot_firmware/gcode/encoder.ex | 6 +++++- farmbot_firmware/lib/farmbot_firmware/request.ex | 2 +- 7 files changed, 24 insertions(+), 9 deletions(-) diff --git a/farmbot_core/lib/farmbot_core/firmware_side_effects.ex b/farmbot_core/lib/farmbot_core/firmware_side_effects.ex index 93374fc9f..6e45d00eb 100644 --- a/farmbot_core/lib/farmbot_core/firmware_side_effects.ex +++ b/farmbot_core/lib/farmbot_core/firmware_side_effects.ex @@ -27,7 +27,7 @@ defmodule FarmbotCore.FirmwareSideEffects do @impl FarmbotFirmware.SideEffects def handle_axis_timeout(axis) do - FarmbotCore.Logger.error 1, "Axis #{axis} timed out waiting for movement to complete" + FarmbotCore.Logger.error(1, "Axis #{axis} timed out waiting for movement to complete") :noop end @@ -48,7 +48,7 @@ defmodule FarmbotCore.FirmwareSideEffects do # this is a bug in the firmware code i think def handle_encoders_scaled([]), do: :noop - + @impl FarmbotFirmware.SideEffects def handle_encoders_raw(x: x, y: y, z: z) do :ok = BotState.set_encoders_raw(x, y, z) @@ -64,6 +64,7 @@ defmodule FarmbotCore.FirmwareSideEffects do %{param => value} |> Asset.update_firmware_config!() |> Asset.Private.mark_dirty!(%{}) + :ok end @@ -75,27 +76,33 @@ defmodule FarmbotCore.FirmwareSideEffects do @impl FarmbotFirmware.SideEffects def handle_software_version([version]) do :ok = BotState.set_firmware_version(version) + case String.split(version, ".") do # Ramps [_, _, _, "R"] -> _ = Leds.red(:solid) :ok = BotState.set_firmware_hardware("arduino") + # Farmduino [_, _, _, "F"] -> _ = Leds.red(:solid) :ok = BotState.set_firmware_hardware("farmduino") + # Farmduino V14 [_, _, _, "G"] -> _ = Leds.red(:solid) :ok = BotState.set_firmware_hardware("farmduino_k14") + # Farmduino V15 [_, _, _, "H"] -> _ = Leds.red(:solid) :ok = BotState.set_firmware_hardware("farmduino_k15") + # Express V10 [_, _, _, "E"] -> _ = Leds.red(:solid) :ok = BotState.set_firmware_hardware("express_k10") + [_, _, _, "S"] -> _ = Leds.red(:slow_blink) :ok = BotState.set_firmware_version("none") @@ -159,7 +166,7 @@ defmodule FarmbotCore.FirmwareSideEffects do @impl FarmbotFirmware.SideEffects def handle_debug_message([message]) do fbos_config = Asset.fbos_config() - should_log? = fbos_config.firmware_debug_log || fbos_config.arduino_debug_messages + should_log? = fbos_config.firmware_debug_log || fbos_config.arduino_debug_messages should_log? && FarmbotCore.Logger.debug(3, "Firmware debug message: " <> message) end diff --git a/farmbot_ext/lib/farmbot_ext/jwt.ex b/farmbot_ext/lib/farmbot_ext/jwt.ex index 306562463..7b432eaba 100644 --- a/farmbot_ext/lib/farmbot_ext/jwt.ex +++ b/farmbot_ext/lib/farmbot_ext/jwt.ex @@ -37,7 +37,7 @@ defmodule FarmbotExt.JWT do {:ok, jwt} else :error -> {:error, "base64_decode_fail"} - {:error, _resson} -> {:error, "json_decode_error"} + {:error, _reason} -> {:error, "json_decode_error"} end end diff --git a/farmbot_firmware/lib/farmbot_firmware.ex b/farmbot_firmware/lib/farmbot_firmware.ex index d16916d01..37836f7b8 100644 --- a/farmbot_firmware/lib/farmbot_firmware.ex +++ b/farmbot_firmware/lib/farmbot_firmware.ex @@ -636,7 +636,7 @@ defmodule FarmbotFirmware do {:noreply, goto(state, :busy)} end - def handle_report({:report_error, []} = code, %{status: :configuration} = state) do + def handle_report({:report_error, _} = code, %{status: :configuration} = state) do if state.caller_pid, do: send(state.caller_pid, {state.tag, code}) for {pid, _code} <- state.command_queue, do: send(pid, {state.tag, {:report_busy, []}}) @@ -644,7 +644,7 @@ defmodule FarmbotFirmware do {:stop, {:error, state.current}, state} end - def handle_report({:report_error, []} = code, state) do + def handle_report({:report_error, _} = code, state) do if state.caller_pid, do: send(state.caller_pid, {state.tag, code}) for {pid, _code} <- state.command_queue, do: send(pid, {state.tag, {:report_busy, []}}) diff --git a/farmbot_firmware/lib/farmbot_firmware/command.ex b/farmbot_firmware/lib/farmbot_firmware/command.ex index 40eae1ea5..c39dee098 100644 --- a/farmbot_firmware/lib/farmbot_firmware/command.ex +++ b/farmbot_firmware/lib/farmbot_firmware/command.ex @@ -46,7 +46,7 @@ defmodule FarmbotFirmware.Command do debug_log("#{GCODE.encode(code)} position change") wait_for_command_result(tag, code, retries, error) - {_, {:report_error, []}} -> + {_, {:report_error, _}} -> debug_log("#{GCODE.encode(code)} firmware error") if err, do: {:error, err}, else: {:error, :firmware_error} diff --git a/farmbot_firmware/lib/farmbot_firmware/gcode/decoder.ex b/farmbot_firmware/lib/farmbot_firmware/gcode/decoder.ex index 6ad605b86..a3ddf01d8 100644 --- a/farmbot_firmware/lib/farmbot_firmware/gcode/decoder.ex +++ b/farmbot_firmware/lib/farmbot_firmware/gcode/decoder.ex @@ -91,6 +91,10 @@ defmodule FarmbotFirmware.GCODE.Decoder do def decode_error(["V14"]), do: [:invalid_command] def decode_error(["V15"]), do: [:no_config] def decode_error([unk]), do: [unknown_error: unk] + # TODO(Rick): This is a guess. Can be removed if + # better solution is found. + # FarmBot/farmbot_os/issues/1105#issuecomment-572381069 + def decode_error(unk), do: [unknown_error: [unk]] defp decode_floats(list, acc \\ []) diff --git a/farmbot_firmware/lib/farmbot_firmware/gcode/encoder.ex b/farmbot_firmware/lib/farmbot_firmware/gcode/encoder.ex index 88cd4c41c..cb56bd0fb 100644 --- a/farmbot_firmware/lib/farmbot_firmware/gcode/encoder.ex +++ b/farmbot_firmware/lib/farmbot_firmware/gcode/encoder.ex @@ -8,7 +8,10 @@ defmodule FarmbotFirmware.GCODE.Encoder do def do_encode(:report_idle, []), do: "R00" def do_encode(:report_begin, []), do: "R01" def do_encode(:report_success, []), do: "R02" - def do_encode(:report_error, []), do: "R03" + # TODO(Rick): Why are some `:report_error`s sending + # tuples instead of lists?? + # https://github.com/FarmBot/farmbot_os/issues/1105#issuecomment-572381069 + def do_encode(:report_error, _), do: "R03" def do_encode(:report_busy, []), do: "R04" def do_encode(:report_axis_state, xyz), do: "R05 " <> encode_axis_state(xyz) @@ -84,6 +87,7 @@ defmodule FarmbotFirmware.GCODE.Encoder do def do_encode(:command_emergency_unlock, _), do: "F09" def do_encode(:command_emergency_lock, _), do: "E" + def do_encode(), do: "R03" @spec encode_floats([{Param.t(), float()}]) :: binary() defp encode_floats(args) do diff --git a/farmbot_firmware/lib/farmbot_firmware/request.ex b/farmbot_firmware/lib/farmbot_firmware/request.ex index 0b7c8d5e1..272f20425 100644 --- a/farmbot_firmware/lib/farmbot_firmware/request.ex +++ b/farmbot_firmware/lib/farmbot_firmware/request.ex @@ -65,7 +65,7 @@ defmodule FarmbotFirmware.Request do do: {:ok, {tag, result}}, else: wait_for_request_result(tag, code, result) - {_, {:report_error, []}} -> + {_, {:report_error, _}} -> {:error, :firmware_error} {_, {:report_invalid, []}} -> From 5c04f54cd85fedb3b61b048d9e243cf105b871b3 Mon Sep 17 00:00:00 2001 From: Rick Carlino Date: Thu, 9 Jan 2020 13:32:45 -0600 Subject: [PATCH 11/16] v9.0.1-rc2 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index ee3ea8073..e14105e1c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -9.0.1-rc1 +9.0.1-rc2 From bd86cfe7a727af11a28d3444448392e062f3cdc8 Mon Sep 17 00:00:00 2001 From: Rick Carlino Date: Thu, 9 Jan 2020 13:49:46 -0600 Subject: [PATCH 12/16] Release v9.0.1-rc3 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index e14105e1c..a2a1729fc 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -9.0.1-rc2 +9.0.1-rc3 \ No newline at end of file From 958d5c8f09b9f6a139cd246d06c584367992ab3d Mon Sep 17 00:00:00 2001 From: Rick Carlino Date: Thu, 9 Jan 2020 16:44:02 -0600 Subject: [PATCH 13/16] v9.0.1 --- CHANGELOG.md | 1 + VERSION | 2 +- docs/target_development/provisioning_ota_system.md | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d40b1bc29..62145cbd1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ # 9.0.1 * Routine token updates on Circle CI. + * Fix bugs that were causing devices to erroneously factory reset under some circumstances. # 9.0.0 * Run updates on Nerves systems. diff --git a/VERSION b/VERSION index a2a1729fc..37ad5c8b1 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -9.0.1-rc3 \ No newline at end of file +9.0.1 diff --git a/docs/target_development/provisioning_ota_system.md b/docs/target_development/provisioning_ota_system.md index d67b9ab7c..49202a016 100644 --- a/docs/target_development/provisioning_ota_system.md +++ b/docs/target_development/provisioning_ota_system.md @@ -10,7 +10,7 @@ Publishing a FarmBotOS release requires coordination of a few different systems. ## Legacy Release System -The legacy system is somewhat simpiler. It goes as follows: +The legacy system is somewhat simpler. It goes as follows: ### Pull request into `master` branch @@ -54,7 +54,7 @@ Beta releases are constructed by creating a tag off of the `staging` branch. ## NervesHub System -The NervesHub system is simpiler to use, but more complex to setup. +The NervesHub system is simpler to use, but more complex to setup. ### User registration From 6ad51907e6e06dc03a8fc11bc17ad18d2c87441c Mon Sep 17 00:00:00 2001 From: Rick Carlino Date: Thu, 9 Jan 2020 16:44:36 -0600 Subject: [PATCH 14/16] Release v9.0.1 From 10ea92805f32a6d8fa3ea1aa6f155dad72b2ab66 Mon Sep 17 00:00:00 2001 From: Rick Carlino Date: Thu, 9 Jan 2020 16:48:34 -0600 Subject: [PATCH 15/16] Release v9.0.1 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 09f99fb1d..c18e601ee 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Download the version of FarmBot OS that corresponds to the FarmBot kit and compu | FarmBot Kit | Computer | Download Link | | --- | --- | --- | -| Genesis v1.2, Genesis v1.3, Genesis v1.4, Genesis XL v1.4 | Raspberry Pi 3 | [Download FBOS](https://github.com/FarmBot/farmbot_os/releases/download/v9.0.0/farmbot-rpi3-9.0.0.img) | +| Genesis v1.2, Genesis v1.3, Genesis v1.4, Genesis XL v1.4 | Raspberry Pi 3 | [Download FBOS](https://github.com/FarmBot/farmbot_os/releases/download/v9.0.1/farmbot-rpi3-9.0.1.img) | | Express v1.0, Express XL v1.0 | Raspberry Pi Zero W | Coming soon | --- From 82fc90c0ecc8f6fcbbc430034014f47ba6db0dec Mon Sep 17 00:00:00 2001 From: Rick Carlino Date: Thu, 9 Jan 2020 16:58:39 -0600 Subject: [PATCH 16/16] Release v9.0.2 --- CHANGELOG.md | 3 +++ README.md | 2 +- VERSION | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 62145cbd1..3672641ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +# 9.0.2 + * See notes for 9.0.1. + # 9.0.1 * Routine token updates on Circle CI. * Fix bugs that were causing devices to erroneously factory reset under some circumstances. diff --git a/README.md b/README.md index c18e601ee..08d46b71e 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Download the version of FarmBot OS that corresponds to the FarmBot kit and compu | FarmBot Kit | Computer | Download Link | | --- | --- | --- | -| Genesis v1.2, Genesis v1.3, Genesis v1.4, Genesis XL v1.4 | Raspberry Pi 3 | [Download FBOS](https://github.com/FarmBot/farmbot_os/releases/download/v9.0.1/farmbot-rpi3-9.0.1.img) | +| Genesis v1.2, Genesis v1.3, Genesis v1.4, Genesis XL v1.4 | Raspberry Pi 3 | [Download FBOS](https://github.com/FarmBot/farmbot_os/releases/download/v9.0.2/farmbot-rpi3-9.0.2.img) | | Express v1.0, Express XL v1.0 | Raspberry Pi Zero W | Coming soon | --- diff --git a/VERSION b/VERSION index 37ad5c8b1..3beeadd42 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -9.0.1 +9.0.2