Berry 'tasmota.defer()' (#22976)

This commit is contained in:
s-hadinger 2025-02-09 17:56:38 +01:00 committed by GitHub
parent 5a6b219971
commit ff5b4956c8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 2049 additions and 1903 deletions

View File

@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file.
### Added
- Formatter `%_U` for `ext_snprintf_P()` to print uint64_t variable as decimal equivalent to `%llu`
- Support for RC-switch decoding of 64-bit received data
- Berry `tasmota.defer()`
### Breaking Changed

View File

@ -86,6 +86,7 @@ class be_class_tasmota (scope: global, name: Tasmota) {
_fl, var // list of active fast-loop object (faster than drivers)
_rules, var // list of active rules
_timers, var // list of active timers
_defer, var // list of deferred functions to be called at next millisecond
_crons, var // list of active crons
_ccmd, var // list of active Tasmota commands implemented in Berry
_drivers, var // list of active drivers
@ -173,6 +174,8 @@ class be_class_tasmota (scope: global, name: Tasmota) {
exec_rules, closure(class_Tasmota_exec_rules_closure)
exec_tele, closure(class_Tasmota_exec_tele_closure)
set_timer, closure(class_Tasmota_set_timer_closure)
run_timers, closure(class_Tasmota_run_timers_closure)
defer, closure(class_Tasmota_defer_closure)
run_deferred, closure(class_Tasmota_run_deferred_closure)
remove_timer, closure(class_Tasmota_remove_timer_closure)
add_cmd, closure(class_Tasmota_add_cmd_closure)

View File

@ -9,6 +9,7 @@ class Tasmota
var _fl # list of fast_loop registered closures
var _rules
var _timers # holds both timers and cron
var _defer # holds functions to be called at next millisecond
var _crons
var _ccmd
var _drivers
@ -273,13 +274,39 @@ class Tasmota
def set_timer(delay,f,id)
self.check_not_method(f)
if self._timers == nil
self._timers=[]
self._timers = []
end
self._timers.push(Trigger(self.millis(delay),f,id))
end
# run every 50ms tick
# special version to push a function that will be called immediately after
def defer(f)
if self._defer == nil
self._defer = []
end
self._defer.push(f)
tasmota.global.deferred_ready = 1
end
# run any immediate function
def run_deferred()
if self._defer
var sz = size(self._defer) # make sure to run only those present at first, and not those inserted in between
while sz > 0
var f = self._defer[0]
self._defer.remove(0)
sz -= 1
f()
end
if size(self._defer) == 0
tasmota.global.deferred_ready = 0
end
end
end
# run every 50ms tick
def run_timers()
self.run_deferred() # run immediate functions first
if self._timers
var i=0
while i < self._timers.size()
@ -699,7 +726,7 @@ class Tasmota
def event(event_type, cmd, idx, payload, raw)
import introspect
if event_type=='every_50ms'
self.run_deferred()
self.run_timers()
end #- first run deferred events -#
if event_type=='every_250ms'

View File

@ -383,6 +383,7 @@ struct TasmotaGlobal_t {
#endif // PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48_SECHEAP_SHARED
#ifdef USE_BERRY
bool berry_deferred_ready = false; // is there an deferred Berry function to be called at next millisecond
bool berry_fast_loop_enabled = false; // is Berry fast loop enabled, i.e. control is passed at each loop iteration
#endif // USE_BERRY
} TasmotaGlobal = { 0 };

View File

@ -33,10 +33,11 @@ extern "C" {
extern const be_ctypes_structure_t be_tasmota_global_struct = {
sizeof(TasmotaGlobal), /* size in bytes */
11, /* number of elements */
12, /* number of elements */
nullptr,
(const be_ctypes_structure_item_t[11]) {
(const be_ctypes_structure_item_t[12]) {
// Warning: fields below need to be in alphabetical order
{ "deferred_ready", offsetof(TasmotaGlobal_t, berry_deferred_ready), 0, 0, ctypes_u8, 0 },
{ "devices_present", offsetof(TasmotaGlobal_t, devices_present), 0, 0, ctypes_u8, 0 },
{ "energy_driver", offsetof(TasmotaGlobal_t, energy_driver), 0, 0, ctypes_u8, 0 },
{ "fast_loop_enabled", offsetof(TasmotaGlobal_t, berry_fast_loop_enabled), 0, 0, ctypes_u8, 0 },

View File

@ -176,6 +176,26 @@ int32_t callBerryEventDispatcher(const char *type, const char *cmd, int32_t idx,
return ret;
}
// simple wrapper to call `tasmota.<method_name>()`
static void callBerryTasmotaFunc(const char * method_name) {
bvm *vm = berry.vm;
if (be_getglobal(vm, "tasmota")) {
if (be_getmethod(vm, -1, method_name)) {
be_pushvalue(vm, -2); // add instance as first arg
BrTimeoutStart();
int32_t ret = be_pcall(vm, 1);
if (ret != 0) {
be_error_pop_all(berry.vm); // clear Berry stack
}
BrTimeoutReset();
be_pop(vm, 1);
}
be_pop(vm, 1); // remove method
}
be_pop(vm, 1); // remove instance object
be_pop(vm, be_top(vm)); // clean
}
// Simplified version of event loop. Just call `tasmota.fast_loop()`
// `every_5ms` is a flag to wait at least 5ms between calss to `tasmota.fast_loop()`
void callBerryFastLoop(bool every_5ms) {
@ -191,21 +211,13 @@ void callBerryFastLoop(bool every_5ms) {
fast_loop_last_call = now;
// TODO - can we make this dereferencing once for all?
if (be_getglobal(vm, "tasmota")) {
if (be_getmethod(vm, -1, "fast_loop")) {
be_pushvalue(vm, -2); // add instance as first arg
BrTimeoutStart();
int32_t ret = be_pcall(vm, 1);
if (ret != 0) {
be_error_pop_all(berry.vm); // clear Berry stack
}
BrTimeoutReset();
be_pop(vm, 1);
}
be_pop(vm, 1); // remove method
}
be_pop(vm, 1); // remove instance object
be_pop(vm, be_top(vm)); // clean
callBerryTasmotaFunc("fast_loop");
}
// call `tasmota.run_immediate()`
void callBerryRunDeferred(void) {
if (nullptr == berry.vm) { return; }
callBerryTasmotaFunc("run_deferred");
}
/*********************************************************************************************\
@ -888,6 +900,9 @@ bool Xdrv52(uint32_t function)
switch (function) {
case FUNC_SLEEP_LOOP:
if (TasmotaGlobal.berry_deferred_ready) { // there are immediate functions registered, call them first
callBerryRunDeferred(); // call `tasmota.run_immediate()`
}
if (TasmotaGlobal.berry_fast_loop_enabled) { // call only if enabled at global level
callBerryFastLoop(true); // call `tasmota.fast_loop()` optimized for minimal performance impact
}
@ -914,6 +929,9 @@ bool Xdrv52(uint32_t function)
}
#endif // USE_WEBSERVER
}
if (TasmotaGlobal.berry_deferred_ready) { // there are immediate functions registered, call them first
callBerryRunDeferred(); // call `tasmota.run_immediate()`
}
if (TasmotaGlobal.berry_fast_loop_enabled) { // call only if enabled at global level
callBerryFastLoop(false); // call `tasmota.fast_loop()` optimized for minimal performance impact
}