From ada1ba6b5624d68a80c6ebd35d64b4bab3b2eae9 Mon Sep 17 00:00:00 2001 From: Matthew Kennedy Date: Wed, 27 Jul 2022 23:27:18 -0700 Subject: [PATCH] Lua rx callback (#4387) * s * mask, callback * support 8 different signatures * make the compiler happy about unrechable code * s * make the compiler happy about unreachable code * guard * changelog --- firmware/CHANGELOG.md | 1 + firmware/controllers/lua/lua_can_rx.cpp | 51 ++++++++--- firmware/controllers/lua/lua_hooks.cpp | 115 ++++++++++++++++++++++-- firmware/controllers/lua/rusefi_lua.h | 3 +- 4 files changed, 150 insertions(+), 20 deletions(-) diff --git a/firmware/CHANGELOG.md b/firmware/CHANGELOG.md index 64c204a02b..231e73213e 100644 --- a/firmware/CHANGELOG.md +++ b/firmware/CHANGELOG.md @@ -30,6 +30,7 @@ Release template (copy/paste this for new release): - microRusEFI and Proteus F4 have CAN OpenBLT bootloader #4199 #4230 - counter-noise measure for custom skipped trigger wheels #4375 - Write hard faults to backup ram, print on next boot #4324 + - Many more options for Lua CAN rx filters/callbacks #4387 ### Fixed - Lua CAN reception fixed for 11-bit IDs where the frame would be received, but a corrupt ID was passed to the handler function. #4321 diff --git a/firmware/controllers/lua/lua_can_rx.cpp b/firmware/controllers/lua/lua_can_rx.cpp index 4229813da4..a8daf84750 100644 --- a/firmware/controllers/lua/lua_can_rx.cpp +++ b/firmware/controllers/lua/lua_can_rx.cpp @@ -6,23 +6,35 @@ static constexpr size_t maxFilterCount = 48; -size_t filterCount = 0; -int32_t luaCanRxIds[maxFilterCount] = {0}; +struct Filter { + int32_t Id; + int32_t Mask; -static bool shouldRxCanFrame(const CANRxFrame& frame) { + int Bus; + int Callback; +}; + +size_t filterCount = 0; +Filter filters[maxFilterCount]; + +static Filter* getFilterForFrame(size_t busIndex, const CANRxFrame& frame) { for (size_t i = 0; i < filterCount; i++) { - int32_t id = luaCanRxIds[i]; - if (CAN_ID(frame) == id) { - return true; + auto& filter = filters[i]; + + if ((CAN_ID(frame) & filter.Mask) == filter.Id) { + if (filter.Bus == -1 || filter.Bus == busIndex) { + return &filter; + } } } - return false; + return nullptr; } // Stores information about one received CAN frame: which bus, plus the actual frame struct CanFrameData { uint8_t BusIndex; + int Callback; CANRxFrame Frame; }; @@ -34,8 +46,10 @@ chibios_rt::Mailbox freeBuffers; chibios_rt::Mailbox filledBuffers; void processLuaCan(const size_t busIndex, const CANRxFrame& frame) { + auto filter = getFilterForFrame(busIndex, frame); + // Filter the frame if we aren't listening for it - if (!shouldRxCanFrame(frame)) { + if (!filter) { return; } @@ -57,6 +71,7 @@ void processLuaCan(const size_t busIndex, const CANRxFrame& frame) { // Copy the frame in to the buffer frameBuffer->BusIndex = busIndex; frameBuffer->Frame = frame; + frameBuffer->Callback = filter->Callback; { // Push the frame in to the queue under lock @@ -66,7 +81,14 @@ void processLuaCan(const size_t busIndex, const CANRxFrame& frame) { } static void handleCanFrame(LuaHandle& ls, CanFrameData* data) { - lua_getglobal(ls, "onCanRx"); + if (data->Callback == -1) { + // No callback, use catch-all function + lua_getglobal(ls, "onCanRx"); + } else { + // Push the specified callback on to the stack + lua_rawgeti(ls, LUA_REGISTRYINDEX, data->Callback); + } + if (lua_isnil(ls, -1)) { // no rx function, ignore efiPrintf("LUA CAN rx missing function onCanRx"); @@ -144,18 +166,21 @@ void initLuaCanRx() { void resetLuaCanRx() { // Clear all lua filters - reloading the script will reinit them - memset(luaCanRxIds, 0, sizeof(luaCanRxIds)); filterCount = 0; } -void addLuaCanRxFilter(int32_t eid) { +void addLuaCanRxFilter(int32_t eid, uint32_t mask, int bus, int callback) { if (filterCount >= maxFilterCount) { firmwareError(OBD_PCM_Processor_Fault, "Too many Lua CAN RX filters"); } - efiPrintf("Added Lua CAN RX filter: %d", eid); + efiPrintf("Added Lua CAN RX filter id 0x%x mask 0x%x with%s custom function", eid, mask, (callback == -1 ? "out" : "")); + + filters[filterCount].Id = eid; + filters[filterCount].Mask = mask; + filters[filterCount].Bus = bus; + filters[filterCount].Callback = callback; - luaCanRxIds[filterCount] = eid; filterCount++; } diff --git a/firmware/controllers/lua/lua_hooks.cpp b/firmware/controllers/lua/lua_hooks.cpp index 625108279c..8d8f76649c 100644 --- a/firmware/controllers/lua/lua_hooks.cpp +++ b/firmware/controllers/lua/lua_hooks.cpp @@ -448,6 +448,113 @@ private: pid_s m_params; }; +static bool isFunction(lua_State* l, int idx) { + return lua_type(l, idx) == LUA_TFUNCTION; +} + +int getLuaFunc(lua_State* l) { + if (!isFunction(l, 1)) { + return luaL_error(l, "expected function"); + } else { + return luaL_ref(l, LUA_REGISTRYINDEX); + } +} + +#if EFI_CAN_SUPPORT +int lua_canRxAdd(lua_State* l) { + uint32_t eid; + + // defaults if not passed + int bus = -1; + int callback = -1; + + switch (lua_gettop(l)) { + case 1: + // handle canRxAdd(id) + eid = luaL_checkinteger(l, 1); + break; + + case 2: + if (isFunction(l, 2)) { + // handle canRxAdd(id, callback) + eid = luaL_checkinteger(l, 1); + lua_remove(l, 1); + callback = getLuaFunc(l); + } else { + // handle canRxAdd(bus, id) + bus = luaL_checkinteger(l, 1); + eid = luaL_checkinteger(l, 2); + } + + break; + case 3: + // handle canRxAdd(bus, id, callback) + bus = luaL_checkinteger(l, 1); + eid = luaL_checkinteger(l, 2); + lua_remove(l, 1); + lua_remove(l, 1); + callback = getLuaFunc(l); + break; + default: + return luaL_error(l, "Wrong number of arguments to canRxAdd. Got %d, expected 1, 2, or 3."); + } + + addLuaCanRxFilter(eid, 0x1FFFFFFF, bus, callback); + + return 0; +} + +int lua_canRxAddMask(lua_State* l) { + uint32_t eid; + uint32_t mask; + + // defaults if not passed + int bus = -1; + int callback = -1; + + switch (lua_gettop(l)) { + case 2: + // handle canRxAddMask(id, mask) + eid = luaL_checkinteger(l, 1); + mask = luaL_checkinteger(l, 2); + break; + + case 3: + if (isFunction(l, 3)) { + // handle canRxAddMask(id, mask, callback) + eid = luaL_checkinteger(l, 1); + mask = luaL_checkinteger(l, 2); + lua_remove(l, 1); + lua_remove(l, 1); + callback = getLuaFunc(l); + } else { + // handle canRxAddMask(bus, id, mask) + bus = luaL_checkinteger(l, 1); + eid = luaL_checkinteger(l, 2); + mask = luaL_checkinteger(l, 3); + } + + break; + case 4: + // handle canRxAddMask(bus, id, mask, callback) + bus = luaL_checkinteger(l, 1); + eid = luaL_checkinteger(l, 2); + mask = luaL_checkinteger(l, 3); + lua_remove(l, 1); + lua_remove(l, 1); + lua_remove(l, 1); + callback = getLuaFunc(l); + break; + default: + return luaL_error(l, "Wrong number of arguments to canRxAddMask. Got %d, expected 2, 3, or 4."); + } + + addLuaCanRxFilter(eid, mask, bus, callback); + + return 0; +} +#endif // EFI_CAN_SUPPORT + void configureRusefiLuaHooks(lua_State* l) { LuaClass luaTimer(l, "Timer"); luaTimer @@ -707,12 +814,8 @@ void configureRusefiLuaHooks(lua_State* l) { #if EFI_CAN_SUPPORT - lua_register(l, "canRxAdd", [](lua_State* l) { - auto eid = luaL_checkinteger(l, 1); - addLuaCanRxFilter(eid); - - return 0; - }); + lua_register(l, "canRxAdd", lua_canRxAdd); + lua_register(l, "canRxAddMask", lua_canRxAddMask); #endif // EFI_CAN_SUPPORT #endif // not EFI_UNIT_TEST } diff --git a/firmware/controllers/lua/rusefi_lua.h b/firmware/controllers/lua/rusefi_lua.h index cb056e4365..e50f7d2a64 100644 --- a/firmware/controllers/lua/rusefi_lua.h +++ b/firmware/controllers/lua/rusefi_lua.h @@ -61,7 +61,8 @@ void initLuaCanRx(); // Called when the user script is unloaded, resets any CAN rx filters void resetLuaCanRx(); // Adds a frame ID to listen to -void addLuaCanRxFilter(int32_t eid); +void addLuaCanRxFilter(int32_t eid, uint32_t mask, int bus, int callback); + // Called from the Lua loop to process any pending CAN frames void doLuaCanRx(LuaHandle& ls); // Called from the CAN RX thread to queue a frame for Lua consumption