From 4229cafa63795c426b5e31d8eaba1db58f8a267f Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Fri, 25 Feb 2022 21:52:35 +0100 Subject: [PATCH] Berry fix cron --- lib/libesp32/berry/generate/be_const_strtab.h | 1 + .../berry/generate/be_const_strtab_def.h | 5 +- .../generate/be_fixed_be_class_ccronexpr.h | 8 +- lib/libesp32/berry_mapping/src/be_mapping.h | 11 +- .../berry_tasmota/src/be_cron_class.cpp | 31 ++++-- .../berry_tasmota/src/be_tasmotalib.c | 100 ++++++++--------- .../berry_tasmota/src/be_trigger_class.c | 104 ++++++++++++------ .../berry_tasmota/src/embedded/Tasmota.be | 38 ++++--- 8 files changed, 182 insertions(+), 116 deletions(-) diff --git a/lib/libesp32/berry/generate/be_const_strtab.h b/lib/libesp32/berry/generate/be_const_strtab.h index 5e5239093..684ebbdce 100644 --- a/lib/libesp32/berry/generate/be_const_strtab.h +++ b/lib/libesp32/berry/generate/be_const_strtab.h @@ -586,6 +586,7 @@ extern const bcstring be_const_str_next; extern const bcstring be_const_str_next_cron; extern const bcstring be_const_str_nil; extern const bcstring be_const_str_no_X20GPIO_X20specified_X20for_X20neopixelbus; +extern const bcstring be_const_str_now; extern const bcstring be_const_str_null_cb; extern const bcstring be_const_str_number; extern const bcstring be_const_str_o; diff --git a/lib/libesp32/berry/generate/be_const_strtab_def.h b/lib/libesp32/berry/generate/be_const_strtab_def.h index 62eab8f38..db2613da1 100644 --- a/lib/libesp32/berry/generate/be_const_strtab_def.h +++ b/lib/libesp32/berry/generate/be_const_strtab_def.h @@ -68,7 +68,7 @@ be_define_const_str(_X3D_X3C_X3E_X21, "=<>!", 2664470277u, 0, 4, &be_const_str_C be_define_const_str(_X3D_X3D, "==", 2431966415u, 0, 2, NULL); be_define_const_str(_X3E, ">", 990687777u, 0, 1, &be_const_str__def); be_define_const_str(_X3E_X3D, ">=", 284975636u, 0, 2, &be_const_str_AudioGeneratorWAV); -be_define_const_str(_X3F, "?", 973910158u, 0, 1, &be_const_str_web_add_handler); +be_define_const_str(_X3F, "?", 973910158u, 0, 1, &be_const_str_now); be_define_const_str(AES_GCM, "AES_GCM", 3832208678u, 0, 7, NULL); be_define_const_str(AXP192, "AXP192", 757230128u, 0, 6, &be_const_str_depower); be_define_const_str(Animate_X20pc_X20is_X20out_X20of_X20range, "Animate pc is out of range", 1854929421u, 0, 26, &be_const_str_debug); @@ -578,6 +578,7 @@ be_define_const_str(next, "next", 1555467752u, 0, 4, &be_const_str_resp_cmnd_str be_define_const_str(next_cron, "next_cron", 3260705337u, 0, 9, &be_const_str_seti); be_define_const_str(nil, "nil", 228849900u, 63, 3, NULL); be_define_const_str(no_X20GPIO_X20specified_X20for_X20neopixelbus, "no GPIO specified for neopixelbus", 42078528u, 0, 33, NULL); +be_define_const_str(now, "now", 682728183u, 0, 3, &be_const_str_web_add_handler); be_define_const_str(null_cb, "null_cb", 2333536460u, 0, 7, NULL); be_define_const_str(number, "number", 467038368u, 0, 6, &be_const_str_p1); be_define_const_str(o, "o", 3926667934u, 0, 1, NULL); @@ -1308,6 +1309,6 @@ static const bstring* const m_string_table[] = { static const struct bconststrtab m_const_string_table = { .size = 427, - .count = 877, + .count = 878, .table = m_string_table }; diff --git a/lib/libesp32/berry/generate/be_fixed_be_class_ccronexpr.h b/lib/libesp32/berry/generate/be_fixed_be_class_ccronexpr.h index 7c49e1b29..0a97a1df5 100644 --- a/lib/libesp32/berry/generate/be_fixed_be_class_ccronexpr.h +++ b/lib/libesp32/berry/generate/be_fixed_be_class_ccronexpr.h @@ -1,15 +1,17 @@ #include "be_constobj.h" static be_define_const_map_slots(be_class_ccronexpr_map) { - { be_const_key(deinit, 2), be_const_ctype_func(ccronexpr_init) }, + { be_const_key(now, -1), be_const_static_ctype_func(ccronexpr_now) }, { be_const_key(_X2Ep, -1), be_const_var(0) }, { be_const_key(next, -1), be_const_ctype_func(ccronexpr_next) }, - { be_const_key(init, 1), be_const_ctype_func(ccronexpr_init) }, + { be_const_key(init, 0), be_const_ctype_func(ccronexpr_init) }, + { be_const_key(deinit, -1), be_const_ctype_func(ccronexpr_init) }, + { be_const_key(time_reached, -1), be_const_static_ctype_func(ccronexpr_time_reached) }, }; static be_define_const_map( be_class_ccronexpr_map, - 4 + 6 ); BE_EXPORT_VARIABLE be_define_const_class( diff --git a/lib/libesp32/berry_mapping/src/be_mapping.h b/lib/libesp32/berry_mapping/src/be_mapping.h index 96ac67563..01aa2e17f 100644 --- a/lib/libesp32/berry_mapping/src/be_mapping.h +++ b/lib/libesp32/berry_mapping/src/be_mapping.h @@ -10,15 +10,24 @@ #ifdef __cplusplus #define be_const_ctype_func(_f) { \ - bvaldata((const void*) &ctype_func_def##_f), \ + bvaldata((const void*) &ctype_func_def##_f), \ BE_CTYPE_FUNC \ } + #define be_const_static_ctype_func(_f) { \ + bvaldata((const void*) &ctype_func_def##_f), \ + BE_CTYPE_FUNC | BE_STATIC \ + } #else // __cplusplus typedef const void* be_constptr; #define be_const_ctype_func(_f) { \ .v.nf = (const void*) &ctype_func_def##_f, \ .type = BE_CTYPE_FUNC \ } +typedef const void* be_constptr; + #define be_const_static_ctype_func(_f) { \ + .v.nf = (const void*) &ctype_func_def##_f, \ + .type = BE_CTYPE_FUNC | BE_STATIC \ + } #endif // __cplusplus #define BE_FUNC_CTYPE_DECLARE(_name, _ret_arg, _in_arg) \ diff --git a/lib/libesp32/berry_tasmota/src/be_cron_class.cpp b/lib/libesp32/berry_tasmota/src/be_cron_class.cpp index 9e87c4c23..a79d069ae 100644 --- a/lib/libesp32/berry_tasmota/src/be_cron_class.cpp +++ b/lib/libesp32/berry_tasmota/src/be_cron_class.cpp @@ -12,6 +12,9 @@ #include "ccronexpr.h" +extern uint32_t LocalTime(void); +static const uint32_t START_VALID_TIME = 1451602800; // Time is synced and after 2016-01-01 + // create static cron_expr* ccronexpr_init(struct bvm* vm, char* expr) { cron_expr* cron = new cron_expr(); @@ -33,16 +36,28 @@ BE_FUNC_CTYPE_DECLARE(ccronexpr_deinit, "", ".") // next -static uint32_t ccronexpr_next(cron_expr* cron, uint32_t date) { - return cron_next(cron, date); +// Returns the next trigger time, or 0 if rtc is invalid +static uint32_t ccronexpr_next(cron_expr* cron) { + uint32_t now_local = LocalTime(); + return now_local > START_VALID_TIME ? cron_next(cron, now_local) : 0; } -BE_FUNC_CTYPE_DECLARE(ccronexpr_next, "i", ".i") +BE_FUNC_CTYPE_DECLARE(ccronexpr_next, "i", ".") -// prev -static uint32_t ccronexpr_prev(cron_expr* cron, uint32_t date) { - return cron_prev(cron, date); +// time_reached +// Compares as uint32_t (Berry only handles int32_t) to avoid the 2038 bug +// Also prevents from triggering an event if the clock is not set (i.e. year is 1970) +static bool ccronexpr_time_reached(uint32_t date) { + uint32_t now_local = LocalTime(); + bool reached = (date <= now_local); + return now_local > START_VALID_TIME ? reached : false; } -BE_FUNC_CTYPE_DECLARE(ccronexpr_prev, "i", ".i") +BE_FUNC_CTYPE_DECLARE(ccronexpr_time_reached, "b", "i") + +// now (local time) +static uint32_t ccronexpr_now(void) { + return LocalTime(); +} +BE_FUNC_CTYPE_DECLARE(ccronexpr_now, "i", "") #include "be_fixed_be_class_ccronexpr.h" @@ -60,6 +75,8 @@ class be_class_ccronexpr (scope: global, name: ccronexpr) { deinit, ctype_func(ccronexpr_init) next, ctype_func(ccronexpr_next) + time_reached, static_ctype_func(ccronexpr_time_reached) + now, static_ctype_func(ccronexpr_now) } @const_object_info_end */ diff --git a/lib/libesp32/berry_tasmota/src/be_tasmotalib.c b/lib/libesp32/berry_tasmota/src/be_tasmotalib.c index e4c956035..130409c4e 100644 --- a/lib/libesp32/berry_tasmota/src/be_tasmotalib.c +++ b/lib/libesp32/berry_tasmota/src/be_tasmotalib.c @@ -2013,19 +2013,17 @@ be_local_closure(Tasmota_add_cron, /* name */ 0, /* has sup protos */ NULL, /* no sub protos */ 1, /* has constants */ - ( &(const bvalue[ 8]) { /* constants */ + ( &(const bvalue[ 6]) { /* constants */ /* K0 */ be_nested_str(check_not_method), /* K1 */ be_nested_str(_crons), /* K2 */ be_nested_str(ccronexpr), /* K3 */ be_nested_str(next), - /* K4 */ be_nested_str(rtc), - /* K5 */ be_nested_str(local), - /* K6 */ be_nested_str(push), - /* K7 */ be_nested_str(Trigger), + /* K4 */ be_nested_str(push), + /* K5 */ be_nested_str(Trigger), }), &be_const_str_add_cron, &be_const_str_solidified, - ( &(const binstruction[28]) { /* code */ + ( &(const binstruction[25]) { /* code */ 0x8C100100, // 0000 GETMET R4 R0 K0 0x5C180400, // 0001 MOVE R6 R2 0x7C100400, // 0002 CALL R4 2 @@ -2040,20 +2038,17 @@ be_local_closure(Tasmota_add_cron, /* name */ 0x7C140200, // 000B CALL R5 1 0x7C100200, // 000C CALL R4 1 0x8C140903, // 000D GETMET R5 R4 K3 - 0x8C1C0104, // 000E GETMET R7 R0 K4 - 0x7C1C0200, // 000F CALL R7 1 - 0x941C0F05, // 0010 GETIDX R7 R7 K5 - 0x7C140400, // 0011 CALL R5 2 - 0x88180101, // 0012 GETMBR R6 R0 K1 - 0x8C180D06, // 0013 GETMET R6 R6 K6 - 0xB8220E00, // 0014 GETNGBL R8 K7 - 0x5C240A00, // 0015 MOVE R9 R5 - 0x5C280400, // 0016 MOVE R10 R2 - 0x5C2C0600, // 0017 MOVE R11 R3 - 0x5C300800, // 0018 MOVE R12 R4 - 0x7C200800, // 0019 CALL R8 4 - 0x7C180400, // 001A CALL R6 2 - 0x80000000, // 001B RET 0 + 0x7C140200, // 000E CALL R5 1 + 0x88180101, // 000F GETMBR R6 R0 K1 + 0x8C180D04, // 0010 GETMET R6 R6 K4 + 0xB8220A00, // 0011 GETNGBL R8 K5 + 0x5C240A00, // 0012 MOVE R9 R5 + 0x5C280400, // 0013 MOVE R10 R2 + 0x5C2C0600, // 0014 MOVE R11 R3 + 0x5C300800, // 0015 MOVE R12 R4 + 0x7C200800, // 0016 CALL R8 4 + 0x7C180400, // 0017 CALL R6 2 + 0x80000000, // 0018 RET 0 }) ) ); @@ -2076,55 +2071,52 @@ be_local_closure(Tasmota_run_cron, /* name */ ( &(const bvalue[10]) { /* constants */ /* K0 */ be_nested_str(_crons), /* K1 */ be_const_int(0), - /* K2 */ be_nested_str(rtc), - /* K3 */ be_nested_str(local), + /* K2 */ be_nested_str(ccronexpr), + /* K3 */ be_nested_str(now), /* K4 */ be_nested_str(size), /* K5 */ be_nested_str(trig), - /* K6 */ be_nested_str(f), - /* K7 */ be_nested_str(next), - /* K8 */ be_nested_str(remove), + /* K6 */ be_nested_str(next), + /* K7 */ be_nested_str(time_reached), + /* K8 */ be_nested_str(f), /* K9 */ be_const_int(1), }), &be_const_str_run_cron, &be_const_str_solidified, - ( &(const binstruction[37]) { /* code */ + ( &(const binstruction[34]) { /* code */ 0x88040100, // 0000 GETMBR R1 R0 K0 - 0x78060021, // 0001 JMPF R1 #0024 + 0x7806001E, // 0001 JMPF R1 #0021 0x58040001, // 0002 LDCONST R1 K1 - 0x8C080102, // 0003 GETMET R2 R0 K2 - 0x7C080200, // 0004 CALL R2 1 - 0x94080503, // 0005 GETIDX R2 R2 K3 + 0xB80A0400, // 0003 GETNGBL R2 K2 + 0x8C080503, // 0004 GETMET R2 R2 K3 + 0x7C080200, // 0005 CALL R2 1 0x880C0100, // 0006 GETMBR R3 R0 K0 0x8C0C0704, // 0007 GETMET R3 R3 K4 0x7C0C0200, // 0008 CALL R3 1 0x140C0203, // 0009 LT R3 R1 R3 - 0x780E0018, // 000A JMPF R3 #0024 + 0x780E0015, // 000A JMPF R3 #0021 0x880C0100, // 000B GETMBR R3 R0 K0 0x940C0601, // 000C GETIDX R3 R3 R1 0x88100705, // 000D GETMBR R4 R3 K5 - 0x18100802, // 000E LE R4 R4 R2 - 0x78120011, // 000F JMPF R4 #0022 - 0x88100706, // 0010 GETMBR R4 R3 K6 - 0x8C140707, // 0011 GETMET R5 R3 K7 - 0x5C1C0400, // 0012 MOVE R7 R2 - 0x7C140400, // 0013 CALL R5 2 - 0x5C180A00, // 0014 MOVE R6 R5 - 0x741A0004, // 0015 JMPT R6 #001B - 0x88180100, // 0016 GETMBR R6 R0 K0 - 0x8C180D08, // 0017 GETMET R6 R6 K8 - 0x5C200200, // 0018 MOVE R8 R1 - 0x7C180400, // 0019 CALL R6 2 - 0x70020001, // 001A JMP #001D - 0x900E0A05, // 001B SETMBR R3 K5 R5 - 0x00040309, // 001C ADD R1 R1 K9 - 0x5C180800, // 001D MOVE R6 R4 - 0x5C1C0400, // 001E MOVE R7 R2 - 0x5C200A00, // 001F MOVE R8 R5 - 0x7C180400, // 0020 CALL R6 2 - 0x70020000, // 0021 JMP #0023 - 0x00040309, // 0022 ADD R1 R1 K9 - 0x7001FFE1, // 0023 JMP #0006 - 0x80000000, // 0024 RET 0 + 0x1C100901, // 000E EQ R4 R4 K1 + 0x78120003, // 000F JMPF R4 #0014 + 0x8C100706, // 0010 GETMET R4 R3 K6 + 0x7C100200, // 0011 CALL R4 1 + 0x900E0A04, // 0012 SETMBR R3 K5 R4 + 0x7002000A, // 0013 JMP #001F + 0x8C100707, // 0014 GETMET R4 R3 K7 + 0x7C100200, // 0015 CALL R4 1 + 0x78120007, // 0016 JMPF R4 #001F + 0x88100708, // 0017 GETMBR R4 R3 K8 + 0x8C140706, // 0018 GETMET R5 R3 K6 + 0x7C140200, // 0019 CALL R5 1 + 0x900E0A05, // 001A SETMBR R3 K5 R5 + 0x5C180800, // 001B MOVE R6 R4 + 0x5C1C0400, // 001C MOVE R7 R2 + 0x5C200A00, // 001D MOVE R8 R5 + 0x7C180400, // 001E CALL R6 2 + 0x00040309, // 001F ADD R1 R1 K9 + 0x7001FFE4, // 0020 JMP #0006 + 0x80000000, // 0021 RET 0 }) ) ); diff --git a/lib/libesp32/berry_tasmota/src/be_trigger_class.c b/lib/libesp32/berry_tasmota/src/be_trigger_class.c index b6340584b..b53fd074d 100644 --- a/lib/libesp32/berry_tasmota/src/be_trigger_class.c +++ b/lib/libesp32/berry_tasmota/src/be_trigger_class.c @@ -5,6 +5,39 @@ *******************************************************************/ #include "be_constobj.h" +/******************************************************************** +** Solidified function: init +********************************************************************/ +be_local_closure(Trigger_init, /* name */ + be_nested_proto( + 5, /* nstack */ + 5, /* argc */ + 2, /* varg */ + 0, /* has upvals */ + NULL, /* no upvals */ + 0, /* has sup protos */ + NULL, /* no sub protos */ + 1, /* has constants */ + ( &(const bvalue[ 4]) { /* constants */ + /* K0 */ be_nested_str(trig), + /* K1 */ be_nested_str(f), + /* K2 */ be_nested_str(id), + /* K3 */ be_nested_str(o), + }), + &be_const_str_init, + &be_const_str_solidified, + ( &(const binstruction[ 5]) { /* code */ + 0x90020001, // 0000 SETMBR R0 K0 R1 + 0x90020202, // 0001 SETMBR R0 K1 R2 + 0x90020403, // 0002 SETMBR R0 K2 R3 + 0x90020604, // 0003 SETMBR R0 K3 R4 + 0x80000000, // 0004 RET 0 + }) + ) +); +/*******************************************************************/ + + /******************************************************************** ** Solidified function: tostring ********************************************************************/ @@ -55,12 +88,12 @@ be_local_closure(Trigger_tostring, /* name */ /******************************************************************** -** Solidified function: init +** Solidified function: time_reached ********************************************************************/ -be_local_closure(Trigger_init, /* name */ +be_local_closure(Trigger_time_reached, /* name */ be_nested_proto( - 5, /* nstack */ - 5, /* argc */ + 4, /* nstack */ + 1, /* argc */ 2, /* varg */ 0, /* has upvals */ NULL, /* no upvals */ @@ -68,19 +101,26 @@ be_local_closure(Trigger_init, /* name */ NULL, /* no sub protos */ 1, /* has constants */ ( &(const bvalue[ 4]) { /* constants */ - /* K0 */ be_nested_str(trig), - /* K1 */ be_nested_str(f), - /* K2 */ be_nested_str(id), - /* K3 */ be_nested_str(o), + /* K0 */ be_nested_str(o), + /* K1 */ be_nested_str(trig), + /* K2 */ be_const_int(0), + /* K3 */ be_nested_str(time_reached), }), - &be_const_str_init, + &be_const_str_time_reached, &be_const_str_solidified, - ( &(const binstruction[ 5]) { /* code */ - 0x90020001, // 0000 SETMBR R0 K0 R1 - 0x90020202, // 0001 SETMBR R0 K1 R2 - 0x90020403, // 0002 SETMBR R0 K2 R3 - 0x90020604, // 0003 SETMBR R0 K3 R4 - 0x80000000, // 0004 RET 0 + ( &(const binstruction[12]) { /* code */ + 0x88040100, // 0000 GETMBR R1 R0 K0 + 0x78060007, // 0001 JMPF R1 #000A + 0x88040101, // 0002 GETMBR R1 R0 K1 + 0x24040302, // 0003 GT R1 R1 K2 + 0x78060004, // 0004 JMPF R1 #000A + 0x88040100, // 0005 GETMBR R1 R0 K0 + 0x8C040303, // 0006 GETMET R1 R1 K3 + 0x880C0101, // 0007 GETMBR R3 R0 K1 + 0x7C040400, // 0008 CALL R1 2 + 0x80040200, // 0009 RET 1 R1 + 0x50040000, // 000A LDBOOL R1 0 0 + 0x80040200, // 000B RET 1 R1 }) ) ); @@ -92,8 +132,8 @@ be_local_closure(Trigger_init, /* name */ ********************************************************************/ be_local_closure(Trigger_next, /* name */ be_nested_proto( - 5, /* nstack */ - 2, /* argc */ + 3, /* nstack */ + 1, /* argc */ 2, /* varg */ 0, /* has upvals */ NULL, /* no upvals */ @@ -106,15 +146,14 @@ be_local_closure(Trigger_next, /* name */ }), &be_const_str_next, &be_const_str_solidified, - ( &(const binstruction[ 8]) { /* code */ - 0x88080100, // 0000 GETMBR R2 R0 K0 - 0x780A0004, // 0001 JMPF R2 #0007 - 0x88080100, // 0002 GETMBR R2 R0 K0 - 0x8C080501, // 0003 GETMET R2 R2 K1 - 0x5C100200, // 0004 MOVE R4 R1 - 0x7C080400, // 0005 CALL R2 2 - 0x80040400, // 0006 RET 1 R2 - 0x80000000, // 0007 RET 0 + ( &(const binstruction[ 7]) { /* code */ + 0x88040100, // 0000 GETMBR R1 R0 K0 + 0x78060003, // 0001 JMPF R1 #0006 + 0x88040100, // 0002 GETMBR R1 R0 K0 + 0x8C040301, // 0003 GETMET R1 R1 K1 + 0x7C040200, // 0004 CALL R1 1 + 0x80040200, // 0005 RET 1 R1 + 0x80000000, // 0006 RET 0 }) ) ); @@ -127,15 +166,16 @@ be_local_closure(Trigger_next, /* name */ be_local_class(Trigger, 4, NULL, - be_nested_map(7, + be_nested_map(8, ( (struct bmapnode*) &(const bmapnode[]) { - { be_const_key(f, 1), be_const_var(1) }, + { be_const_key(id, 2), be_const_var(2) }, + { be_const_key(f, -1), be_const_var(1) }, + { be_const_key(next, -1), be_const_closure(Trigger_next_closure) }, + { be_const_key(trig, 7), be_const_var(0) }, + { be_const_key(time_reached, -1), be_const_closure(Trigger_time_reached_closure) }, + { be_const_key(tostring, 4), be_const_closure(Trigger_tostring_closure) }, { be_const_key(o, -1), be_const_var(3) }, { be_const_key(init, -1), be_const_closure(Trigger_init_closure) }, - { be_const_key(id, 4), be_const_var(2) }, - { be_const_key(tostring, -1), be_const_closure(Trigger_tostring_closure) }, - { be_const_key(trig, 2), be_const_var(0) }, - { be_const_key(next, -1), be_const_closure(Trigger_next_closure) }, })), be_str_literal("Trigger") ); diff --git a/lib/libesp32/berry_tasmota/src/embedded/Tasmota.be b/lib/libesp32/berry_tasmota/src/embedded/Tasmota.be index a6e561648..bae82eabb 100644 --- a/lib/libesp32/berry_tasmota/src/embedded/Tasmota.be +++ b/lib/libesp32/berry_tasmota/src/embedded/Tasmota.be @@ -15,12 +15,19 @@ class Trigger return string.format(" 0 + return self.o.time_reached(self.trig) + end + return false + end end tasmota = nil @@ -270,23 +277,19 @@ class Tasmota def run_cron() if self._crons var i=0 - var now = self.rtc()['local'] # now in epoch seconds + var now = ccronexpr.now() while i < self._crons.size() var trigger = self._crons[i] - if trigger.trig <= now + if trigger.trig == 0 # trigger was created when RTC was invalid, try to recalculate + trigger.trig = trigger.next() + elif trigger.time_reached() # time has come var f = trigger.f - var next_time = trigger.next(now) - if !next_time - self._crons.remove(i) # one shot event - else - trigger.trig = next_time # recurring event - i += 1 - end + var next_time = trigger.next() + trigger.trig = next_time # update to next time f(now, next_time) - else - i += 1 end + i += 1 end end end @@ -309,10 +312,11 @@ class Tasmota def add_cron(pattern,f,id) self.check_not_method(f) if !self._crons self._crons=[] end - var cron = ccronexpr(str(pattern)) # can fail, throwing an exception - var next_time = cron.next(self.rtc()['local']) - self._crons.push(Trigger(next_time, f, id, cron)) + var cron_obj = ccronexpr(str(pattern)) # can fail, throwing an exception + var next_time = cron_obj.next() + + self._crons.push(Trigger(next_time, f, id, cron_obj)) end # remove cron by id