From ea89f04a56d91c425b222dbaf2a21301b12b5e39 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Mon, 19 Dec 2022 11:58:35 +1100 Subject: [PATCH] AP_Scripting: script switching system Co-authored-by:Henry Wurzburg (hwurzburg@yahoo.com) --- libraries/AP_Scripting/AP_Scripting.cpp | 6 + libraries/AP_Scripting/AP_Scripting.h | 2 + .../applets/script_controller.lua | 153 ++++++++++++++++++ libraries/AP_Scripting/docs/docs.lua | 16 ++ .../generator/description/bindings.desc | 5 + libraries/AP_Scripting/lua_bindings.cpp | 38 +++++ libraries/AP_Scripting/lua_bindings.h | 2 + 7 files changed, 222 insertions(+) create mode 100644 libraries/AP_Scripting/applets/script_controller.lua diff --git a/libraries/AP_Scripting/AP_Scripting.cpp b/libraries/AP_Scripting/AP_Scripting.cpp index d9ba4f4436..e3cda78199 100644 --- a/libraries/AP_Scripting/AP_Scripting.cpp +++ b/libraries/AP_Scripting/AP_Scripting.cpp @@ -317,6 +317,12 @@ bool AP_Scripting::arming_checks(size_t buflen, char *buffer) const return true; } +void AP_Scripting::scripting_restart() +{ + _stop = true; + _restart = true; +} + AP_Scripting *AP_Scripting::_singleton = nullptr; namespace AP { diff --git a/libraries/AP_Scripting/AP_Scripting.h b/libraries/AP_Scripting/AP_Scripting.h index b75e373ba1..648da28c7b 100644 --- a/libraries/AP_Scripting/AP_Scripting.h +++ b/libraries/AP_Scripting/AP_Scripting.h @@ -50,6 +50,8 @@ public: void handle_mission_command(const class AP_Mission::Mission_Command& cmd); bool arming_checks(size_t buflen, char *buffer) const; + + void scripting_restart(void); // User parameters for inputs into scripts AP_Float _user[6]; diff --git a/libraries/AP_Scripting/applets/script_controller.lua b/libraries/AP_Scripting/applets/script_controller.lua new file mode 100644 index 0000000000..badfdcd261 --- /dev/null +++ b/libraries/AP_Scripting/applets/script_controller.lua @@ -0,0 +1,153 @@ +--[[ + a script to select other lua scripts using an auxillary switch +--]] + +local THIS_SCRIPT = "script_controller.lua" + +--[[ + copy file src to dest, return true on success +--]] +function file_copy(src, dest) + local block_size = 256 + local file1 = io.open(src, "rb") + if not file1 then + return false + end + local file2 = io.open(dest, "wb") + if not file2 then + file1:close() + return false + end + while true do + local block = file1:read(block_size) + if not block then + break + end + file2:write(block) + end + local ret = false + if file1:seek("end") == file2:seek("end") then + ret = true + end + file1:close() + file2:close() + return ret +end + +--[[ + compare two files, return true if they are the same +--]] +function file_compare(filename1, filename2) + local block_size = 256 + local file1 = io.open(filename1, "rb") + if not file1 then + return false + end + local file2 = io.open(filename2, "rb") + if not file2 then + file1:close() + return false + end + local ret = true + while true do + local block1 = file1:read(block_size) + local block2 = file2:read(block_size) + if block1 ~= block2 then + ret = false + break + end + if not block1 then + break + end + end + file1:close() + file2:close() + return ret +end + +--[[ + get the path to the scripts directory. This will be scripts/ on SITL + and APM/scripts on a ChibiOS board +--]] +function get_scripts_dir() + local dlist1 = dirlist("APM/scripts") + if dlist1 and #dlist1 > 0 then + return "APM/scripts" + end + -- otherwise assume scripts/ + return "scripts" +end + +function file_exists(fname) + local f = io.open(fname,"rb") + if not f then + return false + end + f:close() + return true +end + +--[[ + remove any lua scripts in the scripts directory that are not in the given subdir + returns true if any files were removed +--]] +function remove_scripts(subdir) + local sdir = get_scripts_dir() + local dlist = dirlist(sdir) + if not dlist then + return false + end + local ret = false + for k,v in ipairs(dlist) do + local suffix = v:sub(-4) + if suffix == ".lua" and v ~= THIS_SCRIPT then + if not file_exists(subdir .. "/" .. v) then + ret = true + remove(sdir .. "/" .. v) + end + end + end + return ret +end + +--[[ + copy scripts from a subdir to the main scripts directory + return true if any changes were made +--]] +function copy_scripts(subdir) + local dlist = dirlist(subdir) + if not dlist then + return false + end + local ret = false + local sdir = get_scripts_dir() + for k, v in ipairs(dlist) do + local suffix = v:sub(-4) + gcs:send_text(0, string.format("checking %s", v)) + if suffix == ".lua" and v ~= THIS_SCRIPT then + local src = subdir .. "/" .. v + local dest = sdir .. "/" .. v + if not file_compare(src, dest) then + ret = true + gcs:send_text(0, string.format("copying %s -> %s", src, dest)) + file_copy(src, dest) + else + gcs:send_text(0, string.format("same %s -> %s", src, dest)) + end + end + end + return ret +end + +--[[ + activate a scripting subdirectory +--]] +function activate_subdir(n) + gcs:send_text(0, string.format("Activating %s", n)) + -- step1, remove lua files from scripts/ that are not in the givem subdirectory + local subdir = get_scripts_dir() .. "/" .. n + local changes_made = remove_scripts(subdir) + changes_made = changes_made or copy_scripts(subdir) +end + +activate_subdir(1) diff --git a/libraries/AP_Scripting/docs/docs.lua b/libraries/AP_Scripting/docs/docs.lua index 3e240e6736..aceb7308c6 100644 --- a/libraries/AP_Scripting/docs/docs.lua +++ b/libraries/AP_Scripting/docs/docs.lua @@ -2361,3 +2361,19 @@ function follow:get_last_update_ms() end -- desc ---@return boolean function follow:have_target() end + +-- desc +---@class scripting +scripting = {} + +-- desc +function scripting:scripting_restart() end + +-- desc +--@param directoryname +--@return list of filenames +function dirlist(directoryname) end + +--desc +--@param filename +function remove(filename) end diff --git a/libraries/AP_Scripting/generator/description/bindings.desc b/libraries/AP_Scripting/generator/description/bindings.desc index f7f6ca5f3a..4dff1ac7e2 100644 --- a/libraries/AP_Scripting/generator/description/bindings.desc +++ b/libraries/AP_Scripting/generator/description/bindings.desc @@ -356,6 +356,9 @@ userdata Parameter method set_and_save boolean float'skip_check userdata Parameter method configured boolean userdata Parameter method set_default boolean float'skip_check +include AP_Scripting/AP_Scripting.h +singleton AP_Scripting rename scripting +singleton AP_Scripting method scripting_restart void include AP_Mission/AP_Mission.h singleton AP_Mission rename mission @@ -635,3 +638,5 @@ userdata uint32_t manual_operator __tostring uint32_t___tostring userdata uint32_t manual toint uint32_t_toint 0 userdata uint32_t manual tofloat uint32_t_tofloat 0 +global manual dirlist lua_dirlist 1 +global manual remove lua_removefile 1 diff --git a/libraries/AP_Scripting/lua_bindings.cpp b/libraries/AP_Scripting/lua_bindings.cpp index 463c2984f0..80df6b3330 100644 --- a/libraries/AP_Scripting/lua_bindings.cpp +++ b/libraries/AP_Scripting/lua_bindings.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include "lua_bindings.h" @@ -494,3 +495,40 @@ int lua_get_CAN_device2(lua_State *L) { return 1; } #endif // HAL_MAX_CAN_PROTOCOL_DRIVERS + +/* + directory listing, return table of files in a directory + */ +int lua_dirlist(lua_State *L) { + struct dirent *entry; + int i; + const char *path = luaL_checkstring(L, 1); + + /* open directory */ + auto dir = AP::FS().opendir(path); + if (dir == nullptr) { /* error opening the directory? */ + lua_pushnil(L); /* return nil and ... */ + lua_pushstring(L, strerror(errno)); /* error message */ + return 2; /* number of results */ + } + + /* create result table */ + lua_newtable(L); + i = 1; + while ((entry = AP::FS().readdir(dir)) != nullptr) { + lua_pushnumber(L, i++); /* push key */ + lua_pushstring(L, entry->d_name); /* push value */ + lua_settable(L, -3); + } + + AP::FS().closedir(dir); + return 1; /* table is already on top */ +} + +/* + remove a file + */ +int lua_removefile(lua_State *L) { + const char *filename = luaL_checkstring(L, 1); + return luaL_fileresult(L, remove(filename) == 0, filename); +} diff --git a/libraries/AP_Scripting/lua_bindings.h b/libraries/AP_Scripting/lua_bindings.h index c32930e36c..30f17ce7be 100644 --- a/libraries/AP_Scripting/lua_bindings.h +++ b/libraries/AP_Scripting/lua_bindings.h @@ -10,3 +10,5 @@ int lua_get_i2c_device(lua_State *L); int AP_HAL__I2CDevice_read_registers(lua_State *L); int lua_get_CAN_device(lua_State *L); int lua_get_CAN_device2(lua_State *L); +int lua_dirlist(lua_State *L); +int lua_removefile(lua_State *L);