Implement dump_info sys_call

pull/974/head
Connor Rigby 2019-04-18 09:32:34 -07:00
parent 4f74887990
commit 00003c2a96
No known key found for this signature in database
GPG Key ID: 29A88B24B70456E0
3 changed files with 113 additions and 8 deletions

View File

@ -31,6 +31,7 @@ defmodule FarmbotExt.AMQP.AutoSyncChannel do
@exchange "amq.topic"
@known_kinds ~w(
Device
DiagnosticDump
FarmEvent
FarmwareEnv
FarmwareInstallation
@ -122,7 +123,7 @@ defmodule FarmbotExt.AMQP.AutoSyncChannel do
handle_asset(asset_kind, label, id, params, state)
_ ->
Logger.info("ignoring router: #{key}")
Logger.info("ignoring route: #{key}")
json = JSON.encode!(%{args: %{label: label}, kind: "rpc_ok"})
:ok = Basic.publish(state.chan, @exchange, "bot.#{device}.from_device", json)
{:noreply, state}

View File

@ -37,7 +37,7 @@ defmodule FarmbotExt.API.DirtyWorker do
def handle_info(:timeout, %{module: module} = state) do
dirty = Private.list_dirty(module)
local = Private.list_local(module)
{:noreply, state, {:continue, dirty ++ local}}
{:noreply, state, {:continue, Enum.uniq(dirty ++ local)}}
end
@impl GenServer
@ -46,13 +46,23 @@ defmodule FarmbotExt.API.DirtyWorker do
end
def handle_continue([dirty | rest], %{module: module} = state) do
Logger.info("[#{module} #{dirty.local_id} #{inspect(self())}] Handling dirty data")
case http_request(dirty, state) do
# Valid data
{:ok, %{status: s, body: body}} when s > 199 and s < 300 ->
Logger.debug(
"[#{module} #{dirty.local_id} #{inspect(self())}] HTTP request complete: #{s} ok"
)
dirty |> module.changeset(body) |> handle_changeset(rest, state)
# Invalid data
{:ok, %{status: s, body: %{} = body}} when s > 399 and s < 500 ->
Logger.debug(
"[#{module} #{dirty.local_id} #{inspect(self())}] HTTP request complete: #{s} error+body"
)
changeset = module.changeset(dirty)
Enum.reduce(body, changeset, fn {key, val}, changeset ->
@ -62,13 +72,22 @@ defmodule FarmbotExt.API.DirtyWorker do
# Invalid data, but the API didn't say why
{:ok, %{status: s, body: _body}} when s > 399 and s < 500 ->
Logger.debug(
"[#{module} #{dirty.local_id} #{inspect(self())}] HTTP request complete: #{s} error"
)
module.changeset(dirty)
|> Map.put(:valid?, false)
|> handle_changeset(rest, state)
# HTTP Error. (500, network error, timeout etc.)
error ->
Logger.error("HTTP Error: #{state.module} #{inspect(error)}")
Logger.error(
"[#{module} #{dirty.local_id} #{inspect(self())}] HTTP Error: #{state.module} #{
inspect(error)
}"
)
{:noreply, state, @timeout}
end
end
@ -87,25 +106,26 @@ defmodule FarmbotExt.API.DirtyWorker do
# TODO(Connor) - Update the dirty field here, upload to rollbar?
def handle_changeset(%{valid?: false, data: data} = changeset, rest, state) do
message =
Enum.map(changeset.errors, fn {key, val} ->
"#{key}: #{val}"
Enum.map(changeset.errors, fn
{key, {msg, _meta}} when is_binary(key) -> "\t#{key}: #{msg}"
{key, msg} when is_binary(key) -> "\t#{key}: #{msg}"
end)
|> Enum.join("\n")
Logger.error("Failed to sync: #{state.module} #{message}", changeset: changeset)
Logger.error("Failed to sync: #{state.module} \n #{message}")
_ = Repo.delete!(data)
{:noreply, state, {:continue, rest}}
end
defp http_request(%{id: nil} = dirty, state) do
Logger.info("#{state.module} clean request (post)")
Logger.debug("#{state.module} clean request (post)")
path = state.module.path()
data = render(state.module, dirty)
API.post(API.client(), path, data)
end
defp http_request(dirty, state) do
Logger.info("#{state.module} dirty request (patch)")
Logger.debug("#{state.module} dirty request (patch)")
path = state.module.path()
data = render(state.module, dirty)
API.patch(API.client(), path, data)

View File

@ -0,0 +1,84 @@
defmodule FarmbotOS.SysCalls.DumpInfo do
@moduledoc false
require FarmbotCore.Logger
alias FarmbotCore.{Asset, Asset.Private, Config, Project}
def dump_info do
FarmbotCore.Logger.busy(1, "Recording diagnostic dump.")
ifname = get_network_config()
dmesg = dmesg()
fbos_commit = Project.commit()
fbos_version = Project.version()
fw_version = fw_version()
fw_commit = Project.arduino_commit()
fw_hardware = extract_fw_hardware(fw_version)
fw_data = fw_state(fw_version, fw_hardware)
params = %{
network_interface: ifname,
firmware_hardware: fw_hardware,
firmware_commit: fw_commit,
fbos_commit: fbos_commit,
fbos_version: fbos_version,
fbos_dmesg_dump: dmesg,
firmware_state: FarmbotCore.JSON.encode!(fw_data)
}
case Asset.new_diagnostic_dump(params) do
{:ok, diag} ->
_ = Private.mark_dirty!(diag, %{})
FarmbotCore.Logger.success(1, "Diagnostic dump recorded.")
:ok
{:error, changeset} ->
{:error, "error creating diagnostic dump: #{inspect(changeset)}"}
end
end
defp get_network_config do
case Config.get_all_network_configs() do
[%{name: ifname} | _] -> ifname
_ -> nil
end
end
defp dmesg do
{dmesg, _status} = System.cmd("dmesg", [])
dmesg
end
defp fw_version do
case FarmbotFirmware.request({:software_version_read, []}) do
{:ok, {_, {:report_software_version, [version]}}} -> version
_ -> nil
end
end
defp extract_fw_hardware(nil), do: nil
defp extract_fw_hardware(str) do
case String.split(str, ".") do
[_, _, _, "G"] -> "farmduino_k14"
[_, _, _, "F"] -> "farmduino"
[_, _, _, "R"] -> "arduino"
_ -> nil
end
end
defp fw_state(version, hardware) do
pid = Process.whereis(FarmbotFirmware)
if state = pid && :sys.get_state(pid) do
%{
firmware_hardware: hardware,
firmware_version: version,
busy: state.status == :busy,
serial_port: state.transport_args[:device],
locked: state.status == :emergency_lock,
current_command: inspect(state.command_queue)
}
else
%{error: "Firmware process is not running. Could not collect info."}
end
end
end