Skip to content

Commit 7940544

Browse files
authored
Merge pull request #1469 from FarmBot/staging
v14.8.1 Release
2 parents c60ae18 + a2e714a commit 7940544

File tree

13 files changed

+160
-153
lines changed

13 files changed

+160
-153
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
# 14.8.1
4+
5+
* Add `api()` helper to simplify API access in Lua.
6+
* Remove legacy logs relating to device updates.
7+
* Ability to execute raw CeleryScript from Lua.
8+
39
# 14.8.0
410

511
* Upgrade Elixir, Erlang and system deps.

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
14.8.0
1+
14.8.1

lib/celery.ex

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,20 @@ defmodule FarmbotOS.Celery do
1818
when k in @entrypoints do
1919
StepRunner.begin(caller, tag, ast)
2020
end
21+
22+
@doc "Lua VM calls CSVM"
23+
def execute_from_lua([input_ast], lua) do
24+
input_ast
25+
|> FarmbotOS.Lua.Util.lua_to_elixir()
26+
|> AST.decode()
27+
|> execute(make_ref(), self())
28+
29+
receive do
30+
{:csvm_done, _, :ok} ->
31+
{[true, nil], lua}
32+
33+
other ->
34+
{[false, inspect(other)], lua}
35+
end
36+
end
2137
end
Lines changed: 2 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
defimpl FarmbotOS.AssetWorker, for: FarmbotOS.Asset.Device do
2-
alias FarmbotOS.{Asset, Asset.Device}
3-
alias FarmbotOS.Celery.AST
2+
alias FarmbotOS.Asset.Device
43
use GenServer
54
require FarmbotOS.Logger
65

@@ -13,88 +12,18 @@ defimpl FarmbotOS.AssetWorker, for: FarmbotOS.Asset.Device do
1312
end
1413

1514
def init(%Device{} = device) do
16-
send(self(), :check_factory_reset)
1715
{:ok, %Device{} = device, 0}
1816
end
1917

2018
def handle_info(:timeout, %Device{} = device) do
2119
{:noreply, device}
2220
end
2321

24-
def handle_info(:check_factory_reset, %Device{needs_reset: true} = state) do
25-
ast =
26-
AST.Factory.new()
27-
|> AST.Factory.rpc_request("RESET_DEVICE_NOW")
28-
|> AST.Factory.factory_reset("farmbot_os")
29-
30-
:ok = FarmbotOS.Celery.execute(ast, make_ref())
31-
32-
{:noreply, state}
33-
end
34-
35-
def handle_info(:check_factory_reset, state) do
36-
{:noreply, state}
37-
end
38-
3922
def handle_info({:csvm_done, _ref, _}, state) do
4023
{:noreply, state}
4124
end
4225

43-
def handle_cast({:new_data, new_device}, old_device) do
44-
_ = log_changes(new_device, old_device)
45-
send(self(), :check_factory_reset)
26+
def handle_cast({:new_data, new_device}, _old_dev) do
4627
{:noreply, new_device}
4728
end
48-
49-
def log_changes(new_device, old_device) do
50-
interesting_params = [
51-
:ota_hour,
52-
:mounted_tool_id
53-
]
54-
55-
new_interesting_device =
56-
Map.take(new_device, interesting_params) |> MapSet.new()
57-
58-
old_interesting_device =
59-
Map.take(old_device, interesting_params) |> MapSet.new()
60-
61-
difference =
62-
MapSet.difference(new_interesting_device, old_interesting_device)
63-
64-
Enum.each(difference, fn
65-
{:ota_hour, nil} ->
66-
FarmbotOS.Logger.success(
67-
1,
68-
"Farmbot will apply updates as soon as possible"
69-
)
70-
71-
{:ota_hour, hour} ->
72-
FarmbotOS.Logger.success(
73-
1,
74-
"Farmbot will apply updates during the hour of #{hour}:00"
75-
)
76-
77-
{:mounted_tool_id, nil} ->
78-
if old_device.mounted_tool_id do
79-
if tool = Asset.get_tool(id: old_device.mounted_tool_id) do
80-
FarmbotOS.Logger.info(2, "Dismounted the #{tool.name}")
81-
else
82-
FarmbotOS.Logger.info(2, "Dismounted unknown tool")
83-
end
84-
else
85-
# no previously mounted tool
86-
:ok
87-
end
88-
89-
{:mounted_tool_id, id} ->
90-
if tool = Asset.get_tool(id: id) do
91-
FarmbotOS.Logger.info(2, "Mounted the #{tool.name}")
92-
else
93-
FarmbotOS.Logger.info(2, "Mounted unknown tool")
94-
end
95-
96-
{_key, _value} ->
97-
:noop
98-
end)
99-
end
10029
end

lib/ext/mqtt/rpc_handler.ex

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,6 @@ defmodule FarmbotOS.MQTT.RPCHandler do
4747
end
4848

4949
def handle_info({:csvm_done, ref, :ok}, state) do
50-
Logger.info("CeleryScript OK")
51-
5250
case state.rpc_requests[ref] do
5351
%{label: label, timer: timer} ->
5452
FarmbotOS.Time.cancel_timer(timer)

lib/os/lua.ex

Lines changed: 23 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -97,32 +97,29 @@ defmodule FarmbotOS.Lua do
9797

9898
def builtins() do
9999
%{
100-
base64: [
101-
{:decode, &DataManipulation.b64_decode/2},
102-
{:encode, &DataManipulation.b64_encode/2}
103-
],
104-
json: [
105-
{:decode, &DataManipulation.json_decode/2},
106-
{:encode, &DataManipulation.json_encode/2}
107-
],
108-
uart: [
109-
{:open, &FarmbotOS.Firmware.LuaUART.open/2},
110-
{:list, &FarmbotOS.Firmware.LuaUART.list/2}
111-
],
112100
# This flag can be compared agaist the last e-stop timestamp
113101
# to abort script execution (if E-Stop was called at any
114102
# point during Lua execution).
115103
__LUA_START_TIME: FarmbotOS.Time.system_time_ms(),
104+
__SERVER_PATH:
105+
FarmbotOS.Config.get_config_value(:string, "authorization", "server"),
116106
auth_token: &Info.auth_token/2,
107+
api: &DataManipulation.api/2,
108+
base64: [
109+
{:decode, &DataManipulation.b64_decode/2},
110+
{:encode, &DataManipulation.b64_encode/2}
111+
],
112+
calibrate_camera: execute_script("camera-calibration"),
117113
check_position: &Firmware.check_position/2,
118114
coordinate: &Firmware.coordinate/2,
115+
cs_eval: &FarmbotOS.Celery.execute_from_lua/2,
119116
current_hour: &Info.current_hour/2,
120117
current_minute: &Info.current_minute/2,
121118
current_month: &Info.current_month/2,
122119
current_second: &Info.current_second/2,
120+
detect_weeds: execute_script("plant-detection"),
123121
emergency_lock: &Firmware.emergency_lock/2,
124122
emergency_unlock: &Firmware.emergency_unlock/2,
125-
soft_stop: &Firmware.soft_stop/2,
126123
env: &DataManipulation.env/2,
127124
fbos_version: &Info.fbos_version/2,
128125
find_axis_length: &Firmware.calibrate/2,
@@ -132,45 +129,38 @@ defmodule FarmbotOS.Lua do
132129
get_device: &DataManipulation.get_device/2,
133130
get_fbos_config: &DataManipulation.get_fbos_config/2,
134131
get_firmware_config: &DataManipulation.get_firmware_config/2,
132+
get_job_progress: &Info.get_job_progress/2,
135133
get_position: &Firmware.get_position/2,
136134
go_to_home: &Firmware.go_to_home/2,
137135
http: &DataManipulation.http/2,
138136
inspect: &DataManipulation.json_encode/2,
137+
json: [
138+
{:decode, &DataManipulation.json_decode/2},
139+
{:encode, &DataManipulation.json_encode/2}
140+
],
141+
measure_soil_height: execute_script("Measure Soil Height"),
139142
move_absolute: &Firmware.move_absolute/2,
140143
new_sensor_reading: &DataManipulation.new_sensor_reading/2,
141144
photo_grid: &DataManipulation.photo_grid/2,
142145
read_pin: &Firmware.read_pin/2,
143146
read_status: &Info.read_status/2,
144147
send_message: &Info.send_message/2,
148+
set_job_progress: &Info.set_job_progress/2,
145149
set_pin_io_mode: &Firmware.set_pin_io_mode/2,
150+
soft_stop: &Firmware.soft_stop/2,
146151
soil_height: &DataManipulation.soil_height/2,
147152
take_photo_raw: &DataManipulation.take_photo_raw/2,
148153
take_photo: execute_script("take-photo"),
149-
calibrate_camera: execute_script("camera-calibration"),
150-
detect_weeds: execute_script("plant-detection"),
151-
measure_soil_height: execute_script("Measure Soil Height"),
154+
uart: [
155+
{:open, &FarmbotOS.Firmware.LuaUART.open/2},
156+
{:list, &FarmbotOS.Firmware.LuaUART.list/2}
157+
],
152158
update_device: &DataManipulation.update_device/2,
153159
update_fbos_config: &DataManipulation.update_fbos_config/2,
154160
update_firmware_config: &DataManipulation.update_firmware_config/2,
155161
wait: &Wait.wait/2,
156162
watch_pin: &PinWatcher.new/2,
157-
write_pin: &Firmware.write_pin/2,
158-
get_job_progress: fn [name], lua ->
159-
job = Map.get(FarmbotOS.BotState.fetch().jobs, name)
160-
{[job], lua}
161-
end,
162-
set_job_progress: fn [name, args], lua ->
163-
map = FarmbotOS.Lua.Util.lua_to_elixir(args)
164-
165-
job = %FarmbotOS.BotState.JobProgress.Percent{
166-
type: Map.get(map, "type") || "unknown",
167-
status: Map.get(map, "status") || "working",
168-
percent: Map.get(map, "percent") || 0
169-
}
170-
171-
FarmbotOS.BotState.set_job_progress(name, job)
172-
{[], lua}
173-
end
163+
write_pin: &Firmware.write_pin/2
174164
}
175165
end
176166
end

lib/os/lua/data_manipulation.ex

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -216,11 +216,18 @@ defmodule FarmbotOS.Lua.DataManipulation do
216216
end
217217
end
218218

219-
def photo_grid(_, lua) do
220-
lua_code = File.read!("#{:code.priv_dir(:farmbot)}/lua/photo_grid.lua")
221-
222-
with {:ok, result} <- Lua.raw_eval(lua, lua_code) do
223-
{result, lua}
219+
def photo_grid(args, lua), do: lua_extension(args, lua, "photo_grid")
220+
def api(args, lua), do: lua_extension(args, lua, "api")
221+
222+
defp lua_extension(args, lua, filename) do
223+
lua_code = File.read!("#{:code.priv_dir(:farmbot)}/lua/#{filename}.lua")
224+
225+
with {:ok, [result]} <- Lua.raw_eval(lua, lua_code) do
226+
if is_function(result) do
227+
{result.(args), lua}
228+
else
229+
{[result], lua}
230+
end
224231
else
225232
error ->
226233
{[nil, "ERROR: #{inspect(error)}"], lua}

lib/os/lua/info.ex

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,24 @@ defmodule FarmbotOS.Lua.Info do
9393
{[token], lua}
9494
end
9595

96+
def get_job_progress([name], lua) do
97+
job = Map.get(FarmbotOS.BotState.fetch().jobs, name)
98+
{[job], lua}
99+
end
100+
101+
def set_job_progress([name, args], lua) do
102+
map = FarmbotOS.Lua.Util.lua_to_elixir(args)
103+
104+
job = %FarmbotOS.BotState.JobProgress.Percent{
105+
type: Map.get(map, "type") || "unknown",
106+
status: Map.get(map, "status") || "working",
107+
percent: Map.get(map, "percent") || 0
108+
}
109+
110+
FarmbotOS.BotState.set_job_progress(name, job)
111+
{[], lua}
112+
end
113+
96114
defp do_send_message(kind, message, channels, lua) do
97115
result = SysCallGlue.send_message(kind, "#{message}", channels)
98116

priv/lua/api.lua

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
function merge(t1, t2)
2+
for k, v in ipairs(t2) do table.insert(t1, v) end
3+
return t1
4+
end
5+
6+
return function(input)
7+
params = {
8+
headers = {
9+
Authorization = ("bearer " .. auth_token()),
10+
Accept = "application/json"
11+
}
12+
}
13+
params.method = input.method or "GET"
14+
if input.url then
15+
params.url = __SERVER_PATH .. input.url
16+
else
17+
send_message("error", "Missing URL in HTTP request")
18+
return
19+
end
20+
21+
if input.body then params.body = json.encode(input.body) end
22+
23+
if input.headers then merge(params.headers, input.headers) end
24+
25+
local result, error = http(params)
26+
if error then
27+
send_message("error", "NETWORK ERROR: " .. inspect(error))
28+
return
29+
else
30+
if result.status > 299 then
31+
send_message("error", "HTTP ERROR: " .. inspect(result))
32+
return
33+
else
34+
return json.decode(result.body)
35+
end
36+
end
37+
end

test/asset_workers/device_worker_test.exs

Lines changed: 5 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,9 @@ defmodule FarmbotOS.DeviceWorkerTest do
33
use Mimic
44

55
alias Farmbot.TestSupport.AssetFixtures
6-
alias FarmbotOS.Celery.SysCallGlue.Stubs
76
alias FarmbotOS.Asset.Device
87
alias FarmbotOS.AssetWorker
98

10-
@im_so_sorry 300
11-
129
setup :set_mimic_global
1310
setup :verify_on_exit!
1411

@@ -18,36 +15,12 @@ defmodule FarmbotOS.DeviceWorkerTest do
1815
dev
1916
end
2017

21-
test "triggering of factory reset during init" do
22-
expect(Stubs, :factory_reset, fn _ ->
23-
:ok
24-
end)
25-
26-
dev = fresh_device()
27-
{:ok, _pid} = AssetWorker.start_link(dev, [])
28-
Process.sleep(@im_so_sorry)
29-
end
30-
31-
test "DO trigger factory reset during update" do
32-
dev = fresh_device(false)
33-
{:ok, pid} = AssetWorker.start_link(dev, [])
34-
35-
expect(Stubs, :factory_reset, 1, fn _pkg ->
36-
:ok
37-
end)
38-
39-
GenServer.cast(pid, {:new_data, %{dev | needs_reset: true}})
40-
Process.sleep(@im_so_sorry)
41-
end
42-
43-
test "DO NOT trigger factory reset during update" do
18+
test "initializes and runs noops" do
4419
dev = fresh_device(false)
4520
{:ok, _} = AssetWorker.start_link(dev, [])
46-
47-
stub(Stubs, :factory_reset, fn _pkg ->
48-
nooo = "SHOULD NOT HAPPEN!"
49-
flunk(nooo)
50-
raise nooo
51-
end)
21+
# Test noop functions:
22+
worker = FarmbotOS.AssetWorker.FarmbotOS.Asset.Device
23+
{:noreply, %{}} = worker.handle_info({:csvm_done, make_ref(), :ok}, %{})
24+
{:noreply, %{}} = worker.handle_cast({:new_data, %{}}, %{})
5225
end
5326
end

0 commit comments

Comments
 (0)