From d5ef4afceba2a74f87076a1243357b5c256f06af Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Wed, 3 Mar 2021 08:34:38 +0100 Subject: [PATCH] Berry improvements --- lib/libesp32/Berry-0.1.10/src/be_gc.c | 6 ++ lib/libesp32/Berry-0.1.10/src/be_vm.c | 10 ++++ lib/libesp32/Berry-0.1.10/src/be_vm.h | 3 + lib/libesp32/Berry-0.1.10/src/berry.h | 11 ++++ .../Berry-0.1.10/src/port/be_tasmotalib.c | 2 + .../Berry-0.1.10/src/port/be_wirelib.c | 3 + .../Berry-0.1.10/src/port/berry_conf.h | 6 ++ tasmota/xdrv_52_3_berry_native.ino | 29 ++++++++++ tasmota/xdrv_52_7_berry_embedded.ino | 25 ++------- tasmota/xdrv_52_9_berry.ino | 55 +++++++++++++++---- 10 files changed, 121 insertions(+), 29 deletions(-) diff --git a/lib/libesp32/Berry-0.1.10/src/be_gc.c b/lib/libesp32/Berry-0.1.10/src/be_gc.c index 37abd1f94..8993cbaf7 100644 --- a/lib/libesp32/Berry-0.1.10/src/be_gc.c +++ b/lib/libesp32/Berry-0.1.10/src/be_gc.c @@ -504,6 +504,9 @@ void be_gc_collect(bvm *vm) if (vm->gc.status & GC_HALT) { return; /* the GC cannot run for some reason */ } +#if BE_USE_OBSERVABILITY_HOOK + if (vm->obshook != NULL) (*vm->obshook)(vm, BE_OBS_GC_START, vm->gc.usage); +#endif /* step 1: set root-set reference objects to unscanned */ premark_internal(vm); /* object internal the VM */ premark_global(vm); /* global objects */ @@ -520,4 +523,7 @@ void be_gc_collect(bvm *vm) reset_fixedlist(vm); /* step 5: calculate the next GC threshold */ vm->gc.threshold = next_threshold(vm->gc); +#if BE_USE_OBSERVABILITY_HOOK + if (vm->obshook != NULL) (*vm->obshook)(vm, BE_OBS_GC_END, vm->gc.usage); +#endif } diff --git a/lib/libesp32/Berry-0.1.10/src/be_vm.c b/lib/libesp32/Berry-0.1.10/src/be_vm.c index 06b80cb7f..fd45cabfb 100644 --- a/lib/libesp32/Berry-0.1.10/src/be_vm.c +++ b/lib/libesp32/Berry-0.1.10/src/be_vm.c @@ -398,6 +398,9 @@ BERRY_API bvm* be_vm_new(void) be_globalvar_init(vm); be_gc_setpause(vm, 1); be_loadlibs(vm); +#if BE_USE_OBSERVABILITY_HOOK + vm->obshook = NULL; +#endif return vm; } @@ -1051,3 +1054,10 @@ void be_dofunc(bvm *vm, bvalue *v, int argc) default: call_error(vm, v); } } + +BERRY_API void be_set_obs_hook(bvm *vm, beobshook hook) +{ +#if BE_USE_OBSERVABILITY_HOOK + vm->obshook = hook; +#endif +} \ No newline at end of file diff --git a/lib/libesp32/Berry-0.1.10/src/be_vm.h b/lib/libesp32/Berry-0.1.10/src/be_vm.h index 9c806fd2d..562587100 100644 --- a/lib/libesp32/Berry-0.1.10/src/be_vm.h +++ b/lib/libesp32/Berry-0.1.10/src/be_vm.h @@ -86,6 +86,9 @@ struct bvm { bmap *ntvclass; /* native class table */ blist *registry; /* registry list */ struct bgc gc; +#if BE_USE_OBSERVABILITY_HOOK + beobshook obshook; +#endif #if BE_USE_DEBUG_HOOK bvalue hook; bbyte hookmask; diff --git a/lib/libesp32/Berry-0.1.10/src/berry.h b/lib/libesp32/Berry-0.1.10/src/berry.h index f3eb7cde6..40ceecab4 100644 --- a/lib/libesp32/Berry-0.1.10/src/berry.h +++ b/lib/libesp32/Berry-0.1.10/src/berry.h @@ -273,6 +273,14 @@ typedef void(*bntvhook)(bvm *vm, bhookinfo *info); #define be_assert(expr) ((void)0) #endif +/* Observability hook */ + +typedef void(*beobshook)(bvm *vm, int event, ...); +enum beobshookevents { + BE_OBS_GC_START, // start of GC, arg = allocated size + BE_OBS_GC_END, // end of GC, arg = allocated size +}; + /* FFI functions */ #define be_writestring(s) be_writebuffer((s), strlen(s)) #define be_writenewline() be_writebuffer("\n", 1) @@ -406,6 +414,9 @@ BERRY_API void be_regclass(bvm *vm, const char *name, const bnfuncinfo *lib); BERRY_API bvm* be_vm_new(void); BERRY_API void be_vm_delete(bvm *vm); +/* Observability hook */ +BERRY_API void be_set_obs_hook(bvm *vm, beobshook hook); + /* code load APIs */ BERRY_API int be_loadbuffer(bvm *vm, const char *name, const char *buffer, size_t length); diff --git a/lib/libesp32/Berry-0.1.10/src/port/be_tasmotalib.c b/lib/libesp32/Berry-0.1.10/src/port/be_tasmotalib.c index 098cb3844..bb02f19ca 100644 --- a/lib/libesp32/Berry-0.1.10/src/port/be_tasmotalib.c +++ b/lib/libesp32/Berry-0.1.10/src/port/be_tasmotalib.c @@ -18,6 +18,7 @@ extern int l_respCmndStr(bvm *vm); extern int l_respCmndDone(bvm *vm); extern int l_respCmndError(bvm *vm); extern int l_respCmndFailed(bvm *vm); +extern int l_resolveCmnd(bvm *vm); // #if !BE_USE_PRECOMPILED_OBJECT #if 1 // TODO we will do pre-compiled later @@ -36,6 +37,7 @@ be_native_module_attr_table(tasmota_ntv) { be_native_module_function("respcmnd_done", l_respCmndDone), be_native_module_function("respcmnd_error", l_respCmndError), be_native_module_function("respcmnd_failed", l_respCmndFailed), + be_native_module_function("resolvecmnd", l_resolveCmnd), be_native_module_str("_operators", "=<>!|"), }; diff --git a/lib/libesp32/Berry-0.1.10/src/port/be_wirelib.c b/lib/libesp32/Berry-0.1.10/src/port/be_wirelib.c index b52865710..0d3cd51f4 100644 --- a/lib/libesp32/Berry-0.1.10/src/port/be_wirelib.c +++ b/lib/libesp32/Berry-0.1.10/src/port/be_wirelib.c @@ -14,6 +14,8 @@ extern int b_wire_available(bvm *vm); extern int b_wire_write(bvm *vm); extern int b_wire_read(bvm *vm); +extern int b_wire_validread(bvm *vm); + // #if !BE_USE_PRECOMPILED_OBJECT #if 1 // TODO we will do pre-compiled later be_native_module_attr_table(wire) { @@ -23,6 +25,7 @@ be_native_module_attr_table(wire) { be_native_module_function("available", b_wire_available), be_native_module_function("write", b_wire_write), be_native_module_function("read", b_wire_read), + be_native_module_function("validread", b_wire_validread), }; be_define_native_module(wire, NULL); diff --git a/lib/libesp32/Berry-0.1.10/src/port/berry_conf.h b/lib/libesp32/Berry-0.1.10/src/port/berry_conf.h index 94f0c27b3..e3720b4d1 100644 --- a/lib/libesp32/Berry-0.1.10/src/port/berry_conf.h +++ b/lib/libesp32/Berry-0.1.10/src/port/berry_conf.h @@ -64,6 +64,12 @@ **/ #define BE_DEBUG_VAR_INFO 0 +/* Macro: BE_USE_OBSERVABILITY_HOOK + * Use the obshook function to report low-level actions. + * Default: 0 + **/ +#define BE_USE_OBSERVABILITY_HOOK 1 + /* Macro: BE_STACK_TOTAL_MAX * Set the maximum total stack size. * Default: 20000 diff --git a/tasmota/xdrv_52_3_berry_native.ino b/tasmota/xdrv_52_3_berry_native.ino index 22cf0b9f1..8897603a4 100644 --- a/tasmota/xdrv_52_3_berry_native.ino +++ b/tasmota/xdrv_52_3_berry_native.ino @@ -177,6 +177,17 @@ extern "C" { ResponseCmndFailed(); be_return_nil(vm); } + + // update XdrvMailbox.command with actual command + int32_t l_resolveCmnd(bvm *vm); + int32_t l_resolveCmnd(bvm *vm) { + int32_t top = be_top(vm); // Get the number of arguments + if (top == 1 && be_isstring(vm, 1)) { + const char *msg = be_tostring(vm, 1); + strlcpy(XdrvMailbox.command, msg, CMDSZ); + } + be_return_nil(vm); // Return nil when something goes wrong + } } /*********************************************************************************************\ @@ -277,6 +288,24 @@ extern "C" { be_return_nil(vm); // Return nil when something goes wrong } + // Berry: `validread(address:int, reg:int, size:int) -> int or nil` + int32_t b_wire_validread(struct bvm *vm); + int32_t b_wire_validread(struct bvm *vm) { + int32_t top = be_top(vm); // Get the number of arguments + if (top == 3 && be_isint(vm, 1) && be_isint(vm, 2) && be_isint(vm, 3)) { + uint8_t addr = be_toint(vm, 1); + uint8_t reg = be_toint(vm, 2); + uint8_t size = be_toint(vm, 3); + bool ok = I2cValidRead(addr, reg, size); + if (ok) { + be_pushint(vm, i2c_buffer); + } else { + be_pushnil(vm); + } + be_return(vm); // Return + } + be_return_nil(vm); // Return nil when something goes wrong + } } /*********************************************************************************************\ diff --git a/tasmota/xdrv_52_7_berry_embedded.ino b/tasmota/xdrv_52_7_berry_embedded.ino index 4904210d1..43b33652c 100644 --- a/tasmota/xdrv_52_7_berry_embedded.ino +++ b/tasmota/xdrv_52_7_berry_embedded.ino @@ -44,7 +44,7 @@ const char berry_prog[] = // Map all native functions to methods // Again, this will be eventually pre-compiled "var getfreeheap, publish, cmd, getoption, millis, timereached, yield " - "var respcmnd, respcmndstr, respcmnd_done, respcmnd_error, respcmnd_failed " + "var respcmnd, respcmndstr, respcmnd_done, respcmnd_error, respcmnd_failed, resolvecmnd " "def init_ntv() " "import tasmota_ntv " "self.getfreeheap = tasmota_ntv.getfreeheap " @@ -61,6 +61,7 @@ const char berry_prog[] = "self.respcmnd_done = tasmota_ntv.respcmnd_done " "self.respcmnd_error = tasmota_ntv.respcmnd_error " "self.respcmnd_failed = tasmota_ntv.respcmnd_failed " + "self.resolvecmnd = tasmota_ntv.resolvecmnd " "end " "def init() " @@ -207,8 +208,11 @@ const char berry_prog[] = "var payload_json = json.load(payload) " "var cmd_found = self.findkeyi(self._cmd, cmd) " "if cmd_found != nil " - "return self._cmd[cmd_found](cmd_found, idx, payload, payload_json) " + "self.resolvecmnd(cmd_found) " // set the command name in XdrvMailbox.command + "self._cmd[cmd_found](cmd_found, idx, payload, payload_json) " + "return true " "end " + "return false " "end " // Force gc and return allocated memory @@ -259,23 +263,6 @@ const char berry_prog[] = // "try compile('/autoexec.be','file')() except .. log('BRY: no /autoexec.bat file') end " // Wire - "wire.validread = def(addr, reg, size) " - "var ret = nil " - "for i:0..2 " - "wire.begintransmission(addr) " - "wire.write(reg) " - "if wire.endtransmission(false) == 0 " - "wire.requestfrom(addr, size) " - "if wire.available() == size " - "for j:0..size-1 " - "ret = ((ret != nil ? ret : 0) << 8) + wire.read() " - "end " - "return ret " - "end " - "end " - "end " - "wire.endtransmission() " - "end " ; #endif // USE_BERRY diff --git a/tasmota/xdrv_52_9_berry.ino b/tasmota/xdrv_52_9_berry.ino index 346572d9b..666b36818 100644 --- a/tasmota/xdrv_52_9_berry.ino +++ b/tasmota/xdrv_52_9_berry.ino @@ -102,7 +102,7 @@ bool callBerryRule(void) { } bool callBerryCommand(void) { - const char * command = nullptr; + bool serviced = false; checkBeTop(); be_getglobal(berry.vm, "_exec_cmd"); @@ -111,16 +111,21 @@ bool callBerryCommand(void) { be_pushint(berry.vm, XdrvMailbox.index); be_pushstring(berry.vm, XdrvMailbox.data); int ret = be_pcall(berry.vm, 3); - command = be_tostring(berry.vm, 3); - strlcpy(XdrvMailbox.topic, command, CMDSZ); - // AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_BERRY "Command (%s) serviced=%d"), XdrvMailbox.command, serviced); + // AddLog(LOG_LEVEL_INFO, "callBerryCommand: top=%d", be_top(berry.vm)); + // AddLog(LOG_LEVEL_INFO, "callBerryCommand: type(1)=%s", be_typename(berry.vm, 1)); + // AddLog(LOG_LEVEL_INFO, "callBerryCommand: type(2)=%s", be_typename(berry.vm, 2)); + // AddLog(LOG_LEVEL_INFO, "callBerryCommand: type(3)=%s", be_typename(berry.vm, 3)); + // AddLog(LOG_LEVEL_INFO, "callBerryCommand: type(4)=%s", be_typename(berry.vm, 4)); + // AddLog(LOG_LEVEL_INFO, "callBerryCommand: type(5)=%s", be_typename(berry.vm, 5)); + serviced = be_tobool(berry.vm, 1); // return value is in slot 1 + // AddLog(LOG_LEVEL_INFO, "callBerryCommand: serviced=%d", serviced); be_pop(berry.vm, 4); // remove function object } else { be_pop(berry.vm, 1); // remove nil object } checkBeTop(); - return command != nullptr; // TODO event not handled + return serviced; // TODO event not handled } size_t callBerryGC(void) { @@ -173,6 +178,35 @@ void callBerryFunctionVoid(const char * fname) { checkBeTop(); } +/*********************************************************************************************\ + * VM Observability +\*********************************************************************************************/ +void BerryObservability(bvm *vm, int32_t event...); +void BerryObservability(bvm *vm, int32_t event...) { + va_list param; + va_start(param, event); + static int32_t vm_usage = 0; + static uint32_t gc_time = 0; + + switch (event) { + case BE_OBS_GC_START: + { + gc_time = millis(); + vm_usage = va_arg(param, int32_t); + } + break; + case BE_OBS_GC_END: + { + int32_t vm_usage2 = va_arg(param, int32_t); + uint32_t gc_elapsed = millis() - gc_time; + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_BERRY "GC from %i to %i bytes (in %d ms)"), vm_usage, vm_usage2, gc_elapsed); + } + break; + default: break; + } + va_end(param); +} + /*********************************************************************************************\ * VM Init \*********************************************************************************************/ @@ -191,6 +225,7 @@ void BrReset(void) { bool berry_init_ok = false; do { berry.vm = be_vm_new(); /* create a virtual machine instance */ + be_set_obs_hook(berry.vm, &BerryObservability); // AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_BERRY "Berry VM created, RAM used=%u"), be_gc_memcount(berry.vm)); // Register functions @@ -264,12 +299,12 @@ void CmndBrRun(void) { } while (0); if (0 == ret_code) { + // AddLog(LOG_LEVEL_INFO, "run: top=%d", be_top(berry.vm)); + // AddLog(LOG_LEVEL_INFO, "run: type(1)=%s", be_typename(berry.vm, 1)); + // AddLog(LOG_LEVEL_INFO, "run: type(2)=%s", be_typename(berry.vm, 2)); + // code taken from REPL, look first at top, and if nil, look at return value - if (be_isnil(berry.vm, 0)) { - ret_val = be_tostring(berry.vm, -1); - } else { - ret_val = be_tostring(berry.vm, 0); - } + ret_val = be_tostring(berry.vm, 1); Response_P("{\"" D_PRFX_BR "\":\"%s\"}", ret_val); // can't use XdrvMailbox.command as it may have been overwritten by subcommand be_pop(berry.vm, 1); } else {