2018-10-31 19:43:23 -03:00
|
|
|
/*
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2023-06-07 02:56:50 -03:00
|
|
|
#include "AP_Scripting_config.h"
|
|
|
|
|
|
|
|
#if AP_SCRIPTING_ENABLED
|
|
|
|
|
2018-10-31 19:43:23 -03:00
|
|
|
#include "lua_scripts.h"
|
|
|
|
#include <AP_HAL/AP_HAL.h>
|
2019-10-15 04:02:08 -03:00
|
|
|
#include "AP_Scripting.h"
|
2022-03-03 23:29:47 -04:00
|
|
|
#include <AP_Logger/AP_Logger.h>
|
2018-10-31 19:43:23 -03:00
|
|
|
|
2020-05-10 06:37:10 -03:00
|
|
|
#include <AP_Scripting/lua_generated_bindings.h>
|
2019-03-14 04:38:12 -03:00
|
|
|
|
2022-01-31 19:37:04 -04:00
|
|
|
#define DISABLE_INTERRUPTS_FOR_SCRIPT_RUN 0
|
|
|
|
|
2018-10-31 19:43:23 -03:00
|
|
|
extern const AP_HAL::HAL& hal;
|
2023-03-09 00:47:50 -04:00
|
|
|
#define ENABLE_DEBUG_MODULE 0
|
2018-10-31 19:43:23 -03:00
|
|
|
|
|
|
|
bool lua_scripts::overtime;
|
2018-11-14 19:44:15 -04:00
|
|
|
jmp_buf lua_scripts::panic_jmp;
|
2021-08-25 21:29:41 -03:00
|
|
|
char *lua_scripts::error_msg_buf;
|
2022-08-30 17:47:36 -03:00
|
|
|
HAL_Semaphore lua_scripts::error_msg_buf_sem;
|
2021-08-25 21:29:41 -03:00
|
|
|
uint8_t lua_scripts::print_error_count;
|
|
|
|
uint32_t lua_scripts::last_print_ms;
|
2018-10-31 19:43:23 -03:00
|
|
|
|
2023-11-25 13:18:37 -04:00
|
|
|
uint32_t lua_scripts::loaded_checksum;
|
|
|
|
uint32_t lua_scripts::running_checksum;
|
|
|
|
HAL_Semaphore lua_scripts::crc_sem;
|
|
|
|
|
2021-11-13 13:09:09 -04:00
|
|
|
lua_scripts::lua_scripts(const AP_Int32 &vm_steps, const AP_Int32 &heap_size, const AP_Int8 &debug_options, struct AP_Scripting::terminal_s &_terminal)
|
2019-03-22 12:19:59 -03:00
|
|
|
: _vm_steps(vm_steps),
|
2021-11-13 13:09:09 -04:00
|
|
|
_debug_options(debug_options),
|
2022-12-21 18:36:11 -04:00
|
|
|
terminal(_terminal)
|
|
|
|
{
|
|
|
|
_heap.create(heap_size, 4);
|
2018-10-31 19:43:23 -03:00
|
|
|
}
|
|
|
|
|
2022-04-19 20:34:23 -03:00
|
|
|
lua_scripts::~lua_scripts() {
|
2022-12-21 18:36:11 -04:00
|
|
|
_heap.destroy();
|
2022-04-19 20:34:23 -03:00
|
|
|
}
|
|
|
|
|
2018-10-31 19:43:23 -03:00
|
|
|
void lua_scripts::hook(lua_State *L, lua_Debug *ar) {
|
|
|
|
lua_scripts::overtime = true;
|
|
|
|
|
|
|
|
// we need to aggressively bail out as we are over time
|
|
|
|
// so we will aggressively trap errors until we clear out
|
|
|
|
lua_sethook(L, hook, LUA_MASKCOUNT, 1);
|
|
|
|
|
|
|
|
luaL_error(L, "Exceeded CPU time");
|
|
|
|
}
|
|
|
|
|
2021-08-25 21:29:41 -03:00
|
|
|
void lua_scripts::print_error(MAV_SEVERITY severity) {
|
2022-08-30 17:47:36 -03:00
|
|
|
error_msg_buf_sem.take_blocking();
|
2021-08-25 21:29:41 -03:00
|
|
|
if (error_msg_buf == nullptr) {
|
2022-08-30 17:47:36 -03:00
|
|
|
error_msg_buf_sem.give();
|
2021-08-25 21:29:41 -03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
last_print_ms = AP_HAL::millis();
|
|
|
|
GCS_SEND_TEXT(severity, "Lua: %s", error_msg_buf);
|
2022-08-30 17:47:36 -03:00
|
|
|
error_msg_buf_sem.give();
|
2021-08-25 21:29:41 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
void lua_scripts::set_and_print_new_error_message(MAV_SEVERITY severity, const char *fmt, ...) {
|
2022-08-30 17:47:36 -03:00
|
|
|
error_msg_buf_sem.take_blocking();
|
|
|
|
|
2021-08-25 21:29:41 -03:00
|
|
|
// reset buffer and print count
|
|
|
|
print_error_count = 0;
|
|
|
|
if (error_msg_buf) {
|
2022-12-21 18:36:11 -04:00
|
|
|
_heap.deallocate(error_msg_buf);
|
2021-08-25 21:29:41 -03:00
|
|
|
error_msg_buf = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// generate va_list and create a copy
|
|
|
|
va_list arg_list, arg_list_copy;
|
|
|
|
va_start(arg_list, fmt);
|
|
|
|
va_copy(arg_list_copy, arg_list);
|
|
|
|
|
|
|
|
// dry run to work out the required length
|
2022-05-04 12:52:13 -03:00
|
|
|
int len = hal.util->vsnprintf(nullptr, 0, fmt, arg_list_copy);
|
2021-08-25 21:29:41 -03:00
|
|
|
|
|
|
|
// finished with copy
|
|
|
|
va_end(arg_list_copy);
|
|
|
|
|
|
|
|
if (len <= 0) {
|
|
|
|
// nothing to print, something has gone wrong
|
|
|
|
va_end(arg_list);
|
2022-08-30 17:47:36 -03:00
|
|
|
error_msg_buf_sem.give();
|
2021-08-25 21:29:41 -03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// allocate buffer on scripting heap
|
2022-12-21 18:36:11 -04:00
|
|
|
error_msg_buf = (char *)_heap.allocate(len+1);
|
2021-08-25 21:29:41 -03:00
|
|
|
if (!error_msg_buf) {
|
|
|
|
// allocation failed
|
|
|
|
va_end(arg_list);
|
2022-08-30 17:47:36 -03:00
|
|
|
error_msg_buf_sem.give();
|
2021-08-25 21:29:41 -03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// do actual print to buffer and clear va list
|
|
|
|
hal.util->vsnprintf(error_msg_buf, len+1, fmt, arg_list);
|
|
|
|
va_end(arg_list);
|
|
|
|
|
|
|
|
// print to cosole and GCS
|
2022-03-21 06:40:15 -03:00
|
|
|
DEV_PRINTF("Lua: %s\n", error_msg_buf);
|
2022-08-30 17:47:36 -03:00
|
|
|
|
|
|
|
error_msg_buf_sem.give();
|
2021-08-25 21:29:41 -03:00
|
|
|
print_error(severity);
|
|
|
|
}
|
|
|
|
|
2018-11-14 19:44:15 -04:00
|
|
|
int lua_scripts::atpanic(lua_State *L) {
|
2021-08-25 21:29:41 -03:00
|
|
|
set_and_print_new_error_message(MAV_SEVERITY_CRITICAL, "Panic: %s", lua_tostring(L, -1));
|
2018-11-14 19:44:15 -04:00
|
|
|
longjmp(panic_jmp, 1);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-10-30 21:16:57 -03:00
|
|
|
// helper for print and log of runtime stats
|
|
|
|
void lua_scripts::update_stats(const char *name, uint32_t run_time, int total_mem, int run_mem)
|
|
|
|
{
|
|
|
|
if ((_debug_options.get() & uint8_t(DebugLevel::RUNTIME_MSG)) != 0) {
|
2023-09-21 22:24:30 -03:00
|
|
|
GCS_SEND_TEXT(MAV_SEVERITY_DEBUG, "Lua: Time: %u Mem: %d + %d",
|
2022-10-30 21:16:57 -03:00
|
|
|
(unsigned int)run_time,
|
|
|
|
(int)total_mem,
|
|
|
|
(int)run_mem);
|
|
|
|
}
|
2023-10-04 13:29:34 -03:00
|
|
|
#if HAL_LOGGING_ENABLED
|
2022-10-30 21:16:57 -03:00
|
|
|
if ((_debug_options.get() & uint8_t(DebugLevel::LOG_RUNTIME)) != 0) {
|
|
|
|
struct log_Scripting pkt {
|
|
|
|
LOG_PACKET_HEADER_INIT(LOG_SCRIPTING_MSG),
|
|
|
|
time_us : AP_HAL::micros64(),
|
|
|
|
name : {},
|
|
|
|
run_time : run_time,
|
|
|
|
total_mem : total_mem,
|
|
|
|
run_mem : run_mem
|
|
|
|
};
|
|
|
|
const char * name_short = strrchr(name, '/');
|
|
|
|
if ((strlen(name) > sizeof(pkt.name)) && (name_short != nullptr)) {
|
|
|
|
strncpy_noterm(pkt.name, name_short+1, sizeof(pkt.name));
|
|
|
|
} else {
|
|
|
|
strncpy_noterm(pkt.name, name, sizeof(pkt.name));
|
|
|
|
}
|
|
|
|
AP::logger().WriteBlock(&pkt, sizeof(pkt));
|
|
|
|
}
|
2023-10-04 13:29:34 -03:00
|
|
|
#endif // HAL_LOGGING_ENABLED
|
2022-10-30 21:16:57 -03:00
|
|
|
}
|
|
|
|
|
2018-11-01 01:08:37 -03:00
|
|
|
lua_scripts::script_info *lua_scripts::load_script(lua_State *L, char *filename) {
|
2018-10-31 19:43:23 -03:00
|
|
|
if (int error = luaL_loadfile(L, filename)) {
|
|
|
|
switch (error) {
|
|
|
|
case LUA_ERRSYNTAX:
|
2021-08-25 21:29:41 -03:00
|
|
|
set_and_print_new_error_message(MAV_SEVERITY_CRITICAL, "Error: %s", lua_tostring(L, -1));
|
2019-07-17 01:27:25 -03:00
|
|
|
lua_pop(L, lua_gettop(L));
|
2018-10-31 19:43:23 -03:00
|
|
|
return nullptr;
|
|
|
|
case LUA_ERRMEM:
|
2021-08-25 21:29:41 -03:00
|
|
|
set_and_print_new_error_message(MAV_SEVERITY_CRITICAL, "Insufficent memory loading %s", filename);
|
2019-07-17 01:27:25 -03:00
|
|
|
lua_pop(L, lua_gettop(L));
|
2018-10-31 19:43:23 -03:00
|
|
|
return nullptr;
|
2018-11-14 19:44:15 -04:00
|
|
|
case LUA_ERRFILE:
|
2021-08-25 21:29:41 -03:00
|
|
|
set_and_print_new_error_message(MAV_SEVERITY_CRITICAL, "Unable to load the file: %s", lua_tostring(L, -1));
|
2019-07-17 01:27:25 -03:00
|
|
|
lua_pop(L, lua_gettop(L));
|
2018-11-14 19:44:15 -04:00
|
|
|
return nullptr;
|
2018-10-31 19:43:23 -03:00
|
|
|
default:
|
2021-08-25 21:29:41 -03:00
|
|
|
set_and_print_new_error_message(MAV_SEVERITY_CRITICAL, "Unknown error (%d) loading %s", error, filename);
|
2019-07-17 01:27:25 -03:00
|
|
|
lua_pop(L, lua_gettop(L));
|
2018-10-31 19:43:23 -03:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-30 21:16:57 -03:00
|
|
|
const int loadMem = lua_gc(L, LUA_GCCOUNT, 0) * 1024 + lua_gc(L, LUA_GCCOUNTB, 0);
|
|
|
|
const uint32_t loadStart = AP_HAL::micros();
|
|
|
|
|
2022-12-21 18:36:11 -04:00
|
|
|
script_info *new_script = (script_info *)_heap.allocate(sizeof(script_info));
|
2018-10-31 19:43:23 -03:00
|
|
|
if (new_script == nullptr) {
|
|
|
|
// No memory, shouldn't happen, we even attempted to do a GC
|
2021-08-25 21:29:41 -03:00
|
|
|
set_and_print_new_error_message(MAV_SEVERITY_CRITICAL, "Insufficent memory loading %s", filename);
|
2018-10-31 19:43:23 -03:00
|
|
|
lua_pop(L, 1); // we can't use the function we just loaded, so ditch it
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-02-05 18:58:33 -04:00
|
|
|
create_sandbox(L);
|
|
|
|
lua_setupvalue(L, -2, 1);
|
|
|
|
|
2022-10-30 21:16:57 -03:00
|
|
|
const uint32_t loadEnd = AP_HAL::micros();
|
|
|
|
const int endMem = lua_gc(L, LUA_GCCOUNT, 0) * 1024 + lua_gc(L, LUA_GCCOUNTB, 0);
|
|
|
|
|
|
|
|
update_stats(filename, loadEnd-loadStart, endMem, loadMem);
|
|
|
|
|
|
|
|
new_script->name = filename;
|
2020-02-05 18:58:33 -04:00
|
|
|
new_script->lua_ref = luaL_ref(L, LUA_REGISTRYINDEX); // cache the reference
|
|
|
|
new_script->next_run_ms = AP_HAL::millis64() - 1; // force the script to be stale
|
2018-10-31 19:43:23 -03:00
|
|
|
|
2023-11-25 13:18:37 -04:00
|
|
|
// Get checksum of file
|
|
|
|
uint32_t crc = 0;
|
|
|
|
if (AP::FS().crc32(filename, crc)) {
|
|
|
|
// Record crc of this script
|
|
|
|
new_script->crc = crc;
|
|
|
|
{
|
|
|
|
// Apply crc to checksum of all scripts
|
|
|
|
WITH_SEMAPHORE(crc_sem);
|
|
|
|
loaded_checksum ^= crc;
|
|
|
|
running_checksum ^= crc;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-05 18:58:33 -04:00
|
|
|
return new_script;
|
|
|
|
}
|
|
|
|
|
|
|
|
void lua_scripts::create_sandbox(lua_State *L) {
|
2020-02-13 16:50:17 -04:00
|
|
|
lua_newtable(L);
|
|
|
|
luaopen_base_sandbox(L);
|
2023-03-09 00:47:50 -04:00
|
|
|
|
|
|
|
#if ENABLE_DEBUG_MODULE
|
|
|
|
lua_pushstring(L, "debug");
|
|
|
|
luaopen_debug(L);
|
|
|
|
lua_settable(L, -3);
|
|
|
|
#endif
|
2020-02-13 16:50:17 -04:00
|
|
|
lua_pushstring(L, "math");
|
|
|
|
luaopen_math(L);
|
|
|
|
lua_settable(L, -3);
|
|
|
|
lua_pushstring(L, "table");
|
|
|
|
luaopen_table(L);
|
|
|
|
lua_settable(L, -3);
|
|
|
|
lua_pushstring(L, "string");
|
|
|
|
luaopen_string(L);
|
|
|
|
lua_settable(L, -3);
|
|
|
|
lua_pushstring(L, "io");
|
|
|
|
luaopen_io(L);
|
|
|
|
lua_settable(L, -3);
|
|
|
|
lua_pushstring(L, "utf8");
|
|
|
|
luaopen_utf8(L);
|
|
|
|
lua_settable(L, -3);
|
2023-03-09 00:47:50 -04:00
|
|
|
lua_pushstring(L, "package");
|
|
|
|
luaopen_package(L);
|
|
|
|
lua_settable(L, -3);
|
2018-10-31 19:43:23 -03:00
|
|
|
|
2023-03-09 00:47:50 -04:00
|
|
|
load_generated_sandbox(L);
|
2018-10-31 19:43:23 -03:00
|
|
|
}
|
|
|
|
|
2018-11-01 01:08:37 -03:00
|
|
|
void lua_scripts::load_all_scripts_in_dir(lua_State *L, const char *dirname) {
|
|
|
|
if (dirname == nullptr) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-03-11 22:58:41 -03:00
|
|
|
auto *d = AP::FS().opendir(dirname);
|
2018-11-01 01:08:37 -03:00
|
|
|
if (d == nullptr) {
|
2023-09-22 21:29:05 -03:00
|
|
|
GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Lua: open directory (%s) failed", dirname);
|
2018-11-01 01:08:37 -03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// load anything that ends in .lua
|
2019-08-01 07:55:03 -03:00
|
|
|
for (struct dirent *de=AP::FS().readdir(d); de; de=AP::FS().readdir(d)) {
|
2018-11-01 01:08:37 -03:00
|
|
|
uint8_t length = strlen(de->d_name);
|
|
|
|
if (length < 5) {
|
|
|
|
// not long enough
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strncmp(&de->d_name[length-4], ".lua", 4)) {
|
|
|
|
// doesn't end in .lua
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME: because chunk name fetching is not working we are allocating and storing an extra string we shouldn't need to
|
|
|
|
size_t size = strlen(dirname) + strlen(de->d_name) + 2;
|
2022-12-21 18:36:11 -04:00
|
|
|
char * filename = (char *) _heap.allocate(size);
|
2018-11-01 01:08:37 -03:00
|
|
|
if (filename == nullptr) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
snprintf(filename, size, "%s/%s", dirname, de->d_name);
|
|
|
|
|
|
|
|
// we have something that looks like a lua file, attempt to load it
|
|
|
|
script_info * script = load_script(L, filename);
|
|
|
|
if (script == nullptr) {
|
2022-12-21 18:36:11 -04:00
|
|
|
_heap.deallocate(filename);
|
2018-11-01 01:08:37 -03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
reschedule_script(script);
|
|
|
|
|
2021-12-06 00:11:29 -04:00
|
|
|
#if HAL_LOGGER_FILE_CONTENTS_ENABLED
|
2021-11-13 13:42:49 -04:00
|
|
|
if ((_debug_options.get() & uint8_t(DebugLevel::SUPPRESS_SCRIPT_LOG)) == 0) {
|
|
|
|
AP::logger().log_file_content(filename);
|
|
|
|
}
|
2021-12-06 00:11:29 -04:00
|
|
|
#endif
|
2018-11-01 01:08:37 -03:00
|
|
|
}
|
2019-08-01 07:55:03 -03:00
|
|
|
AP::FS().closedir(d);
|
2018-11-01 01:08:37 -03:00
|
|
|
}
|
|
|
|
|
2020-02-05 18:58:33 -04:00
|
|
|
void lua_scripts::reset_loop_overtime(lua_State *L) {
|
|
|
|
overtime = false;
|
|
|
|
// reset the hook to clear the counter
|
|
|
|
const int32_t vm_steps = MAX(_vm_steps, 1000);
|
|
|
|
lua_sethook(L, hook, LUA_MASKCOUNT, vm_steps);
|
|
|
|
}
|
|
|
|
|
2018-10-31 19:43:23 -03:00
|
|
|
void lua_scripts::run_next_script(lua_State *L) {
|
|
|
|
if (scripts == nullptr) {
|
2018-11-03 18:59:45 -03:00
|
|
|
#if defined(AP_SCRIPTING_CHECKS) && AP_SCRIPTING_CHECKS >= 1
|
2018-10-31 19:43:23 -03:00
|
|
|
AP_HAL::panic("Lua: Attempted to run a script without any scripts queued");
|
2018-11-03 18:59:45 -03:00
|
|
|
#endif // defined(AP_SCRIPTING_CHECKS) && AP_SCRIPTING_CHECKS >= 1
|
2018-10-31 19:43:23 -03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
AP_Scripting: Fix the lua scheduling rate to be referenced from the
start of the update
This allows specifying a return value like "return update, 10" to run
at a near perfect 100Hz, where as before it would be run 10 ms after the
script had completed it's loop, which can be highly variable as the
script experiences interupts from the system, as well as needing the
script author to take responsibility for calculating the desired update
rate at the end. This was always intended to be fixed, but I pushed it
back during the initial development, however people are begining to run
scripts that have enough processing, or are rate sensitive enough that
we are now needing to start correcting this, or scripts will have to do
their best to guess the time, which will be inferior to us providing it.
As a note if you exceeded the time expected we will be rescheduling the
script immediately, thus it will have a schedule time in the past and
will be slotted in. This can't indefinetly starve other scripts as they
will still be slotted in, but if you request an update in 1 ms, but took
100ms to run we will simply slide you back into the queue 1ms after when
you started running.
2020-11-14 06:07:07 -04:00
|
|
|
uint64_t start_time_ms = AP_HAL::millis64();
|
2018-10-31 19:43:23 -03:00
|
|
|
// strip the selected script out of the list
|
|
|
|
script_info *script = scripts;
|
|
|
|
scripts = script->next;
|
|
|
|
|
|
|
|
// reset the hook to clear the counter
|
2020-02-05 18:58:33 -04:00
|
|
|
reset_loop_overtime(L);
|
2018-10-31 19:43:23 -03:00
|
|
|
|
|
|
|
// store top of stack so we can calculate the number of return values
|
|
|
|
int stack_top = lua_gettop(L);
|
|
|
|
|
|
|
|
// pop the function to the top of the stack
|
|
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, script->lua_ref);
|
2023-03-09 00:47:50 -04:00
|
|
|
AP::scripting()->set_current_ref(script->lua_ref);
|
2018-10-31 19:43:23 -03:00
|
|
|
|
|
|
|
if(lua_pcall(L, 0, LUA_MULTRET, 0)) {
|
|
|
|
if (overtime) {
|
|
|
|
// script has consumed an excessive amount of CPU time
|
2021-08-25 21:29:41 -03:00
|
|
|
set_and_print_new_error_message(MAV_SEVERITY_CRITICAL, "%s exceeded time limit", script->name);
|
2018-10-31 19:43:23 -03:00
|
|
|
} else {
|
2023-09-22 21:29:05 -03:00
|
|
|
set_and_print_new_error_message(MAV_SEVERITY_CRITICAL, "%s", lua_tostring(L, -1));
|
2018-10-31 19:43:23 -03:00
|
|
|
}
|
2021-08-25 21:29:41 -03:00
|
|
|
remove_script(L, script);
|
2018-10-31 19:43:23 -03:00
|
|
|
lua_pop(L, 1);
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
int returned = lua_gettop(L) - stack_top;
|
|
|
|
switch (returned) {
|
|
|
|
case 0:
|
|
|
|
// no time to reschedule so bail out
|
|
|
|
remove_script(L, script);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
{
|
2021-08-25 21:29:41 -03:00
|
|
|
// sanity check the return types
|
|
|
|
if (lua_type(L, -1) != LUA_TNUMBER) {
|
|
|
|
set_and_print_new_error_message(MAV_SEVERITY_CRITICAL, "%s did not return a delay (0x%d)", script->name, lua_type(L, -1));
|
|
|
|
lua_pop(L, 2);
|
|
|
|
remove_script(L, script);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (lua_type(L, -2) != LUA_TFUNCTION) {
|
|
|
|
set_and_print_new_error_message(MAV_SEVERITY_CRITICAL, "%s did not return a function (0x%d)", script->name, lua_type(L, -2));
|
|
|
|
lua_pop(L, 2);
|
|
|
|
remove_script(L, script);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// types match the expectations, go ahead and reschedule
|
|
|
|
script->next_run_ms = start_time_ms + (uint64_t)luaL_checknumber(L, -1);
|
|
|
|
lua_pop(L, 1);
|
|
|
|
int old_ref = script->lua_ref;
|
|
|
|
script->lua_ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
|
|
|
luaL_unref(L, LUA_REGISTRYINDEX, old_ref);
|
|
|
|
reschedule_script(script);
|
|
|
|
break;
|
2018-10-31 19:43:23 -03:00
|
|
|
}
|
|
|
|
default:
|
|
|
|
{
|
2021-08-25 21:29:41 -03:00
|
|
|
set_and_print_new_error_message(MAV_SEVERITY_CRITICAL, "%s returned bad result count (%d)", script->name, returned);
|
2018-10-31 19:43:23 -03:00
|
|
|
remove_script(L, script);
|
|
|
|
// pop all the results we got that we didn't expect
|
|
|
|
lua_pop(L, returned);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void lua_scripts::remove_script(lua_State *L, script_info *script) {
|
2018-11-14 19:44:15 -04:00
|
|
|
if (script == nullptr) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-10-31 19:43:23 -03:00
|
|
|
// ensure that the script isn't in the loaded list for any reason
|
|
|
|
if (scripts == nullptr) {
|
|
|
|
// nothing to do, already not in the list
|
|
|
|
} else if (scripts == script) {
|
|
|
|
scripts = script->next;
|
|
|
|
} else {
|
|
|
|
for(script_info * current = scripts; current->next != nullptr; current = current->next) {
|
|
|
|
if (current->next == script) {
|
|
|
|
current->next = script->next;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-28 18:45:38 -04:00
|
|
|
{
|
|
|
|
// Remove from running checksum
|
|
|
|
WITH_SEMAPHORE(crc_sem);
|
|
|
|
running_checksum ^= script->crc;
|
|
|
|
}
|
|
|
|
|
2018-11-14 19:44:15 -04:00
|
|
|
if (L != nullptr) {
|
|
|
|
// state could be null if we are force killing all scripts
|
|
|
|
luaL_unref(L, LUA_REGISTRYINDEX, script->lua_ref);
|
|
|
|
}
|
2022-12-21 18:36:11 -04:00
|
|
|
_heap.deallocate(script->name);
|
|
|
|
_heap.deallocate(script);
|
2018-10-31 19:43:23 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
void lua_scripts::reschedule_script(script_info *script) {
|
|
|
|
if (script == nullptr) {
|
2018-11-03 18:59:45 -03:00
|
|
|
#if defined(AP_SCRIPTING_CHECKS) && AP_SCRIPTING_CHECKS >= 1
|
2018-10-31 19:43:23 -03:00
|
|
|
AP_HAL::panic("Lua: Attempted to schedule a null pointer");
|
2018-11-03 18:59:45 -03:00
|
|
|
#endif // defined(AP_SCRIPTING_CHECKS) && AP_SCRIPTING_CHECKS >= 1
|
2018-10-31 19:43:23 -03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
script->next = nullptr;
|
|
|
|
if (scripts == nullptr) {
|
|
|
|
scripts = script;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t next_run_ms = script->next_run_ms;
|
|
|
|
|
|
|
|
if (scripts->next_run_ms > next_run_ms) {
|
|
|
|
script->next = scripts;
|
|
|
|
scripts = script;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
script_info *previous = scripts;
|
|
|
|
while (previous->next != nullptr) {
|
|
|
|
if (previous->next->next_run_ms > next_run_ms) {
|
|
|
|
script->next = previous->next;
|
|
|
|
previous->next = script;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
previous = previous->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
previous->next = script;
|
|
|
|
}
|
|
|
|
|
2022-12-21 18:36:11 -04:00
|
|
|
MultiHeap lua_scripts::_heap;
|
2018-12-08 22:35:08 -04:00
|
|
|
|
|
|
|
void *lua_scripts::alloc(void *ud, void *ptr, size_t osize, size_t nsize) {
|
2022-12-21 18:36:11 -04:00
|
|
|
(void)ud; /* not used */
|
|
|
|
return _heap.change_size(ptr, osize, nsize);
|
2018-12-08 22:35:08 -04:00
|
|
|
}
|
|
|
|
|
2020-02-05 18:58:33 -04:00
|
|
|
void lua_scripts::repl_cleanup (void) {
|
|
|
|
if (terminal.session) {
|
|
|
|
terminal.session = false;
|
|
|
|
if (terminal.output_fd != -1) {
|
|
|
|
AP::FS().close(terminal.output_fd);
|
|
|
|
terminal.output_fd = -1;
|
|
|
|
AP::FS().unlink(REPL_DIRECTORY "/in");
|
|
|
|
AP::FS().unlink(REPL_DIRECTORY "/out");
|
|
|
|
AP::FS().unlink(REPL_DIRECTORY);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-31 19:43:23 -03:00
|
|
|
void lua_scripts::run(void) {
|
2019-12-05 02:17:06 -04:00
|
|
|
bool succeeded_initial_load = false;
|
|
|
|
|
2022-12-21 18:36:11 -04:00
|
|
|
if (!_heap.available()) {
|
2023-09-22 21:29:05 -03:00
|
|
|
GCS_SEND_TEXT(MAV_SEVERITY_CRITICAL, "Lua: Unable to allocate a heap");
|
2018-12-08 22:35:08 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-11-14 19:44:15 -04:00
|
|
|
// panic should be hooked first
|
|
|
|
if (setjmp(panic_jmp)) {
|
2019-12-05 02:17:06 -04:00
|
|
|
if (!succeeded_initial_load) {
|
|
|
|
return;
|
|
|
|
}
|
2018-11-14 19:44:15 -04:00
|
|
|
if (lua_state != nullptr) {
|
|
|
|
lua_close(lua_state); // shutdown the old state
|
|
|
|
}
|
|
|
|
// remove all the old scheduled scripts
|
|
|
|
for (script_info *script = scripts; script != nullptr; script = scripts) {
|
|
|
|
remove_script(nullptr, script);
|
|
|
|
}
|
|
|
|
scripts = nullptr;
|
|
|
|
overtime = false;
|
2020-02-05 18:58:33 -04:00
|
|
|
// end any open REPL sessions
|
|
|
|
repl_cleanup();
|
2018-11-14 19:44:15 -04:00
|
|
|
}
|
|
|
|
|
2018-12-08 22:35:08 -04:00
|
|
|
lua_state = lua_newstate(alloc, NULL);
|
2018-11-14 19:44:15 -04:00
|
|
|
lua_State *L = lua_state;
|
|
|
|
if (L == nullptr) {
|
2023-09-21 22:24:30 -03:00
|
|
|
GCS_SEND_TEXT(MAV_SEVERITY_CRITICAL, "Lua: Couldn't allocate a lua state");
|
2018-11-14 19:44:15 -04:00
|
|
|
return;
|
|
|
|
}
|
2022-10-30 21:18:16 -03:00
|
|
|
|
|
|
|
#ifndef HAL_CONSOLE_DISABLED
|
|
|
|
const int inital_mem = lua_gc(L, LUA_GCCOUNT, 0) * 1024 + lua_gc(L, LUA_GCCOUNTB, 0);
|
|
|
|
#endif
|
|
|
|
|
2018-11-14 19:44:15 -04:00
|
|
|
lua_atpanic(L, atpanic);
|
2020-02-13 16:50:17 -04:00
|
|
|
load_generated_bindings(L);
|
2018-10-31 19:43:23 -03:00
|
|
|
|
2022-10-30 21:18:16 -03:00
|
|
|
#ifndef HAL_CONSOLE_DISABLED
|
|
|
|
const int loaded_mem = lua_gc(L, LUA_GCCOUNT, 0) * 1024 + lua_gc(L, LUA_GCCOUNTB, 0);
|
|
|
|
DEV_PRINTF("Lua: State memory usage: %i + %i\n", inital_mem, loaded_mem - inital_mem);
|
|
|
|
#endif
|
|
|
|
|
2018-11-01 01:08:37 -03:00
|
|
|
// Scan the filesystem in an appropriate manner and autostart scripts
|
2020-10-26 22:08:14 -03:00
|
|
|
// Skip those directores disabled with SCR_DIR_DISABLE param
|
|
|
|
uint16_t dir_disable = AP_Scripting::get_singleton()->get_disabled_dir();
|
|
|
|
bool loaded = false;
|
|
|
|
if ((dir_disable & uint16_t(AP_Scripting::SCR_DIR::SCRIPTS)) == 0) {
|
|
|
|
load_all_scripts_in_dir(L, SCRIPTING_DIRECTORY);
|
|
|
|
loaded = true;
|
|
|
|
}
|
|
|
|
if ((dir_disable & uint16_t(AP_Scripting::SCR_DIR::ROMFS)) == 0) {
|
|
|
|
load_all_scripts_in_dir(L, "@ROMFS/scripts");
|
|
|
|
loaded = true;
|
|
|
|
}
|
|
|
|
if (!loaded) {
|
2023-09-21 22:24:30 -03:00
|
|
|
GCS_SEND_TEXT(MAV_SEVERITY_CRITICAL, "Lua: All directory's disabled see SCR_DIR_DISABLE");
|
2020-10-26 22:08:14 -03:00
|
|
|
}
|
2018-10-31 19:43:23 -03:00
|
|
|
|
2020-03-28 00:12:06 -03:00
|
|
|
#ifndef __clang_analyzer__
|
2019-12-05 02:17:06 -04:00
|
|
|
succeeded_initial_load = true;
|
2020-03-28 00:12:06 -03:00
|
|
|
#endif // __clang_analyzer__
|
2019-12-05 02:17:06 -04:00
|
|
|
|
2021-08-04 13:49:17 -03:00
|
|
|
while (AP_Scripting::get_singleton()->should_run()) {
|
2020-02-05 18:58:33 -04:00
|
|
|
// handle terminal data if we have any
|
|
|
|
if (terminal.session) {
|
|
|
|
doREPL(L);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-11-14 19:44:15 -04:00
|
|
|
#if defined(AP_SCRIPTING_CHECKS) && AP_SCRIPTING_CHECKS >= 1
|
|
|
|
if (lua_gettop(L) != 0) {
|
|
|
|
AP_HAL::panic("Lua: Stack should be empty before running scripts");
|
|
|
|
}
|
|
|
|
#endif // defined(AP_SCRIPTING_CHECKS) && AP_SCRIPTING_CHECKS >= 1
|
|
|
|
|
2018-10-31 19:43:23 -03:00
|
|
|
if (scripts != nullptr) {
|
2018-11-03 18:59:45 -03:00
|
|
|
#if defined(AP_SCRIPTING_CHECKS) && AP_SCRIPTING_CHECKS >= 1
|
|
|
|
// Sanity check that the scripts list is ordered correctly
|
|
|
|
script_info *sanity = scripts;
|
|
|
|
while (sanity->next != nullptr) {
|
|
|
|
if (sanity->next_run_ms > sanity->next->next_run_ms) {
|
|
|
|
AP_HAL::panic("Lua: Script tasking order has been violated");
|
|
|
|
}
|
|
|
|
sanity = sanity->next;
|
|
|
|
}
|
|
|
|
#endif // defined(AP_SCRIPTING_CHECKS) && AP_SCRIPTING_CHECKS >= 1
|
|
|
|
|
2018-10-31 19:43:23 -03:00
|
|
|
// compute delay time
|
|
|
|
uint64_t now_ms = AP_HAL::millis64();
|
|
|
|
if (now_ms < scripts->next_run_ms) {
|
|
|
|
hal.scheduler->delay(scripts->next_run_ms - now_ms);
|
|
|
|
}
|
|
|
|
|
2021-11-13 13:09:09 -04:00
|
|
|
if ((_debug_options.get() & uint8_t(DebugLevel::RUNTIME_MSG)) != 0) {
|
2023-09-21 22:24:30 -03:00
|
|
|
GCS_SEND_TEXT(MAV_SEVERITY_DEBUG, "Lua: Running %s", scripts->name);
|
2019-03-22 12:19:59 -03:00
|
|
|
}
|
2021-11-13 13:42:49 -04:00
|
|
|
// copy name for logging, cant do it after as script reschedule moves the pointers
|
|
|
|
const char * script_name = scripts->name;
|
2018-10-31 19:43:23 -03:00
|
|
|
|
2022-01-31 19:37:04 -04:00
|
|
|
#if DISABLE_INTERRUPTS_FOR_SCRIPT_RUN
|
|
|
|
void *istate = hal.scheduler->disable_interrupts_save();
|
|
|
|
#endif
|
|
|
|
|
2019-07-26 23:10:26 -03:00
|
|
|
const int startMem = lua_gc(L, LUA_GCCOUNT, 0) * 1024 + lua_gc(L, LUA_GCCOUNTB, 0);
|
2018-10-31 19:43:23 -03:00
|
|
|
const uint32_t loadEnd = AP_HAL::micros();
|
|
|
|
|
|
|
|
run_next_script(L);
|
|
|
|
|
|
|
|
const uint32_t runEnd = AP_HAL::micros();
|
2019-07-26 23:10:26 -03:00
|
|
|
const int endMem = lua_gc(L, LUA_GCCOUNT, 0) * 1024 + lua_gc(L, LUA_GCCOUNTB, 0);
|
2022-01-31 19:37:04 -04:00
|
|
|
|
|
|
|
#if DISABLE_INTERRUPTS_FOR_SCRIPT_RUN
|
|
|
|
hal.scheduler->restore_interrupts(istate);
|
|
|
|
#endif
|
|
|
|
|
2022-10-30 21:16:57 -03:00
|
|
|
update_stats(script_name, runEnd - loadEnd, endMem, endMem - startMem);
|
2021-11-13 13:42:49 -04:00
|
|
|
|
2018-10-31 19:43:23 -03:00
|
|
|
|
2019-08-26 21:25:23 -03:00
|
|
|
// garbage collect after each script, this shouldn't matter, but seems to resolve a memory leak
|
|
|
|
lua_gc(L, LUA_GCCOLLECT, 0);
|
|
|
|
|
2018-10-31 19:43:23 -03:00
|
|
|
} else {
|
2021-11-13 13:09:09 -04:00
|
|
|
if ((_debug_options.get() & uint8_t(DebugLevel::NO_SCRIPTS_TO_RUN)) != 0) {
|
2023-09-21 22:24:30 -03:00
|
|
|
GCS_SEND_TEXT(MAV_SEVERITY_DEBUG, "Lua: No scripts to run");
|
2020-02-18 21:10:24 -04:00
|
|
|
}
|
|
|
|
hal.scheduler->delay(1000);
|
2018-10-31 19:43:23 -03:00
|
|
|
}
|
|
|
|
|
2021-08-25 21:29:41 -03:00
|
|
|
// re-print the latest error message every 10 seconds 10 times
|
|
|
|
const uint8_t error_prints = 10;
|
|
|
|
if ((print_error_count < error_prints) && (AP_HAL::millis() - last_print_ms > 10000)) {
|
2022-08-28 16:24:21 -03:00
|
|
|
// note that we do not clear the buffer after we have finished printing, this allows it to be used for a pre-arm check
|
2021-08-25 21:29:41 -03:00
|
|
|
print_error(MAV_SEVERITY_DEBUG);
|
|
|
|
print_error_count++;
|
|
|
|
}
|
2018-10-31 19:43:23 -03:00
|
|
|
}
|
2022-04-19 20:34:23 -03:00
|
|
|
|
|
|
|
// make sure all scripts have been removed
|
|
|
|
while (scripts != nullptr) {
|
|
|
|
remove_script(lua_state, scripts);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lua_state != nullptr) {
|
|
|
|
lua_close(lua_state); // shutdown the old state
|
|
|
|
lua_state = nullptr;
|
|
|
|
}
|
|
|
|
|
2022-08-30 17:47:36 -03:00
|
|
|
error_msg_buf_sem.take_blocking();
|
2022-04-19 20:34:23 -03:00
|
|
|
if (error_msg_buf != nullptr) {
|
2022-12-21 18:36:11 -04:00
|
|
|
_heap.deallocate(error_msg_buf);
|
2022-04-19 20:34:23 -03:00
|
|
|
error_msg_buf = nullptr;
|
|
|
|
}
|
2022-08-30 17:47:36 -03:00
|
|
|
error_msg_buf_sem.give();
|
2018-10-31 19:43:23 -03:00
|
|
|
}
|
2023-06-07 02:56:50 -03:00
|
|
|
|
2023-11-25 13:18:37 -04:00
|
|
|
// Return the file checksums of running and loaded scripts
|
|
|
|
uint32_t lua_scripts::get_loaded_checksum()
|
|
|
|
{
|
|
|
|
WITH_SEMAPHORE(crc_sem);
|
|
|
|
return loaded_checksum;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t lua_scripts::get_running_checksum()
|
|
|
|
{
|
|
|
|
WITH_SEMAPHORE(crc_sem);
|
|
|
|
return running_checksum;
|
|
|
|
}
|
|
|
|
|
2023-06-07 02:56:50 -03:00
|
|
|
#endif // AP_SCRIPTING_ENABLED
|