diff --git a/firmware/controllers/can/can_rx.cpp b/firmware/controllers/can/can_rx.cpp index 316d0e2fd9..70033517ac 100644 --- a/firmware/controllers/can/can_rx.cpp +++ b/firmware/controllers/can/can_rx.cpp @@ -10,6 +10,8 @@ #include "pch.h" +#include "rusefi_lua.h" + typedef float fsio_table_8x8_f32t_linear[FSIO_TABLE_8 * FSIO_TABLE_8]; bool acceptCanRx(int sid DECLARE_ENGINE_PARAMETER_SUFFIX) { @@ -156,7 +158,6 @@ static void processCanRxImu(const CANRxFrame& frame, efitick_t nowNt) { engine->sensors.accelerometer.z = accZ * MM5_10_ACC_QUANT; } } - } void processCanRxMessage(const CANRxFrame &frame, efitick_t nowNt) { @@ -173,6 +174,8 @@ void processCanRxMessage(const CANRxFrame &frame, efitick_t nowNt) { // todo: convert to CanListener or not? processCanRxImu(frame, nowNt); + processLuaCan(frame); + #if EFI_CANBUS_SLAVE if (CAN_EID(frame) == CONFIG(verboseCanBaseAddress) + CAN_SENSOR_1_OFFSET) { int16_t mapScaled = *reinterpret_cast(&frame.data8[0]); diff --git a/firmware/controllers/lua/lua.cpp b/firmware/controllers/lua/lua.cpp index ac2c7d14c0..f5eb9f45ce 100644 --- a/firmware/controllers/lua/lua.cpp +++ b/firmware/controllers/lua/lua.cpp @@ -116,43 +116,6 @@ static void* myAlloc(void* /*ud*/, void* ptr, size_t /*osize*/, size_t nsize) { } #endif // EFI_PROD_CODE -class LuaHandle final { -public: - LuaHandle() : LuaHandle(nullptr) { } - LuaHandle(lua_State* ptr) : m_ptr(ptr) { } - - // Don't allow copying! - LuaHandle(const LuaHandle&) = delete; - LuaHandle& operator=(const LuaHandle&) = delete; - - // Allow moving! - LuaHandle(LuaHandle&& rhs) { - m_ptr = rhs.m_ptr; - rhs.m_ptr = nullptr; - } - - // Move assignment operator - LuaHandle& operator=(LuaHandle&& rhs) { - m_ptr = rhs.m_ptr; - rhs.m_ptr = nullptr; - - return *this; - } - - // Destruction cleans up lua state - ~LuaHandle() { - if (m_ptr) { - efiPrintf("LUA: Tearing down instance..."); - lua_close(m_ptr); - } - } - - operator lua_State*() const { return m_ptr; } - -private: - lua_State* m_ptr; -}; - static int luaTickPeriodMs; static int lua_setTickRate(lua_State* l) { @@ -328,7 +291,12 @@ static bool runOneLua(lua_Alloc alloc, const char* script) { } while (!needsReset && !chThdShouldTerminateX()) { - // First, check if there is a pending interactive command entered by the user +#if EFI_CAN_SUPPORT + // First, process any pending can RX messages + doLuaCanRx(ls); +#endif // EFI_CAN_SUPPORT + + // Next, check if there is a pending interactive command entered by the user doInteractive(ls); invokeTick(ls); @@ -336,6 +304,10 @@ static bool runOneLua(lua_Alloc alloc, const char* script) { chThdSleepMilliseconds(luaTickPeriodMs); } +#if EFI_CAN_SUPPORT + resetLuaCanRx(); +#endif // EFI_CAN_SUPPORT + // De-init pins, they will reinit next start of the script. luaDeInitPins(); @@ -367,6 +339,10 @@ static LuaThread luaThread; void startLua() { #if LUA_USER_HEAP > 1 +#if EFI_CAN_SUPPORT + initLuaCanRx(); +#endif // EFI_CAN_SUPPORT + luaThread.Start(); addConsoleActionS("lua", [](const char* str){ diff --git a/firmware/controllers/lua/lua.mk b/firmware/controllers/lua/lua.mk index 911a94f36d..b530911ac0 100644 --- a/firmware/controllers/lua/lua.mk +++ b/firmware/controllers/lua/lua.mk @@ -4,6 +4,7 @@ LUA_EXT=$(PROJECT_DIR)/ext/lua ALLCPPSRC += $(LUA_DIR)/lua.cpp \ $(LUA_DIR)/lua_hooks.cpp \ $(LUA_DIR)/system_lua.cpp \ + $(LUA_DIR)/lua_can_rx.cpp \ ALLINC += $(LUA_DIR) $(LUA_DIR)/luaaa $(LUA_EXT) ALLCSRC += \ diff --git a/firmware/controllers/lua/lua_can_rx.cpp b/firmware/controllers/lua/lua_can_rx.cpp new file mode 100644 index 0000000000..9f4cc8d017 --- /dev/null +++ b/firmware/controllers/lua/lua_can_rx.cpp @@ -0,0 +1,141 @@ +#include "pch.h" + +#if EFI_CAN_SUPPORT + +#include "rusefi_lua.h" +#include "can.h" + +static constexpr size_t maxFilterCount = 16; + +size_t filterCount = 0; +int32_t luaCanRxIds[maxFilterCount] = {0}; + +static bool shouldRxCanFrame(const CANRxFrame& frame) { + for (size_t i = 0; i < filterCount; i++) { + if (CAN_EID(frame) == luaCanRxIds[i]) { + return true; + } + } + + return false; +} + +constexpr size_t canFrameCount = 32; +static CANRxFrame canFrames[canFrameCount]; +// CAN frame buffers that are not in use +chibios_rt::Mailbox freeBuffers; +// CAN frame buffers that are waiting to be processed by the lua thread +chibios_rt::Mailbox filledBuffers; + +void processLuaCan(const CANRxFrame& frame) { + // Filter the frame if we aren't listening for it + if (!shouldRxCanFrame(frame)) { + return; + } + + CANRxFrame* frameBuffer; + msg_t msg; + + { + // Acquire a buffer under lock + chibios_rt::CriticalSectionLocker csl; + msg = freeBuffers.fetchI(&frameBuffer); + } + + if (msg != MSG_OK) { + // all buffers are already in use, this frame will be dropped! + // TODO: warn the user + return; + } + + // Copy the frame in to the buffer + *frameBuffer = frame; + + { + // Push the frame in to the queue under lock + chibios_rt::CriticalSectionLocker csl; + filledBuffers.postI(frameBuffer); + } +} + +static void handleCanFrame(LuaHandle& ls, CANRxFrame* frame) { + lua_getglobal(ls, "onCanRx"); + if (lua_isnil(ls, -1)) { + // no rx function, ignore + efiPrintf("LUA CAN rx missing function onCanRx"); + lua_settop(ls, 0); + return; + } + + // TODO: push arguments to function! + UNUSED(frame); + + int status = lua_pcall(ls, 0, 0, 0); + + if (0 != status) { + // error calling CAN rx hook function + auto errMsg = lua_tostring(ls, -1); + efiPrintf("LUA CAN RX error %s", errMsg); + lua_pop(ls, 1); + } + + lua_settop(ls, 0); +} + +bool doOneLuaCanRx(LuaHandle& ls) { + CANRxFrame* frame; + + msg_t msg = filledBuffers.fetch(&frame, TIME_IMMEDIATE); + + if (msg == MSG_TIMEOUT) { + // No new CAN messages rx'd, nothing more to do. + return false; + } + + if (msg != MSG_OK) { + // Message was otherwise not OK + // TODO: what do here? + return false; + } + + // We've accepted the frame, process it in Lua. + handleCanFrame(ls, frame); + + // We're done, return this frame to the free list + msg = freeBuffers.post(frame, TIME_IMMEDIATE); + efiAssertVoid(OBD_PCM_Processor_Fault, msg == MSG_OK, "lua can post to free buffer fail"); + + // We processed a frame so we should check again + return true; +} + +void doLuaCanRx(LuaHandle& ls) { + // While it processed a frame, continue checking + while (doOneLuaCanRx(ls)) ; +} + +void initLuaCanRx() { + // Push all CAN frames in to the free buffer + for (size_t i = 0; i < canFrameCount; i++) { + freeBuffers.post(&canFrames[i], TIME_INFINITE); + } +} + +void resetLuaCanRx() { + // Clear all lua filters - reloading the script will reinit them + memset(luaCanRxIds, 0, sizeof(luaCanRxIds)); + filterCount = 0; +} + +void addLuaCanRxFilter(int32_t eid) { + if (filterCount >= maxFilterCount) { + firmwareError(OBD_PCM_Processor_Fault, "Too many Lua CAN RX filters"); + } + + efiPrintf("Added Lua CAN RX filter: %d", eid); + + luaCanRxIds[filterCount] = eid; + filterCount++; +} + +#endif // EFI_CAN_SUPPORT diff --git a/firmware/controllers/lua/lua_hooks.cpp b/firmware/controllers/lua/lua_hooks.cpp index 77631ebf8c..e1a4c23ce0 100644 --- a/firmware/controllers/lua/lua_hooks.cpp +++ b/firmware/controllers/lua/lua_hooks.cpp @@ -1,11 +1,10 @@ #include "pch.h" -#include "lua.hpp" +#include "rusefi_lua.h" #include "lua_hooks.h" #include "fuel_math.h" #include "airmass.h" -#include "utlist.h" #include "lua_airmass.h" #include "can_msg_tx.h" #include "settings.h" @@ -339,39 +338,17 @@ static int lua_setFuelMult(lua_State* l) { return 0; } + #endif // EFI_UNIT_TEST +#if EFI_CAN_SUPPORT +static int lua_canRxAdd(lua_State* l) { + auto eid = luaL_checkinteger(l, 1); + addLuaCanRxFilter(eid); -struct LuaCanReciever; - -// linked list of all CAN receivers -static LuaCanReciever *list; - -struct LuaCanReciever { - - LuaCanReciever *next; - -#if EFI_PROD_CODE - cyclic_buffer pending; -#endif // EFI_PROD_CODE - - ~LuaCanReciever() { - LuaCanReciever *current, *tmp; - // find self in list and remove self - LL_FOREACH_SAFE(list, current, tmp) - { - if (current == this) { - LL_DELETE(list, current); - } - } - } - - LuaCanReciever() { - LL_PREPEND(list, this); - } - - -}; + return 0; +} +#endif // EFI_CAN_SUPPORT struct LuaSensor : public StoredValueSensor { LuaSensor() : LuaSensor("Invalid") { } @@ -405,11 +382,6 @@ void configureRusefiLuaHooks(lua_State* l) { .fun("reset", static_cast(&Timer::reset )) .fun("getElapsedSeconds", static_cast(&Timer::getElapsedSeconds)); - LuaClass luaCanReciever(l, "CanReciever"); - luaCanReciever - .ctor() - ; - LuaClass luaSensor(l, "Sensor"); luaSensor .ctor() @@ -444,5 +416,9 @@ void configureRusefiLuaHooks(lua_State* l) { lua_register(l, "setFuelAdd", lua_setFuelAdd); lua_register(l, "setFuelMult", lua_setFuelMult); -#endif + +#if EFI_CAN_SUPPORT + lua_register(l, "canRxAdd", lua_canRxAdd); +#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 d288e1ec66..344d138a1b 100644 --- a/firmware/controllers/lua/rusefi_lua.h +++ b/firmware/controllers/lua/rusefi_lua.h @@ -2,6 +2,45 @@ #pragma once +#include "lua.hpp" + +class LuaHandle final { +public: + LuaHandle() : LuaHandle(nullptr) { } + LuaHandle(lua_State* ptr) : m_ptr(ptr) { } + + // Don't allow copying! + LuaHandle(const LuaHandle&) = delete; + LuaHandle& operator=(const LuaHandle&) = delete; + + // Allow moving! + LuaHandle(LuaHandle&& rhs) { + m_ptr = rhs.m_ptr; + rhs.m_ptr = nullptr; + } + + // Move assignment operator + LuaHandle& operator=(LuaHandle&& rhs) { + m_ptr = rhs.m_ptr; + rhs.m_ptr = nullptr; + + return *this; + } + + // Destruction cleans up lua state + ~LuaHandle() { + if (m_ptr) { + efiPrintf("LUA: Tearing down instance..."); + lua_close(m_ptr); + } + } + + operator lua_State*() const { return m_ptr; } + +private: + lua_State* m_ptr; +}; + void startLua(); #if EFI_UNIT_TEST @@ -12,3 +51,16 @@ float testLuaReturnsNumber(const char* script); int testLuaReturnsInteger(const char* script); void testLuaExecString(const char* script); #endif + +#if EFI_CAN_SUPPORT +// Lua CAN rx feature +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); +// 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 +void processLuaCan(const CANRxFrame& frame); +#endif // not EFI_CAN_SUPPORT diff --git a/firmware/util/loggingcentral.cpp b/firmware/util/loggingcentral.cpp index 6d1518723d..a6e51decd2 100644 --- a/firmware/util/loggingcentral.cpp +++ b/firmware/util/loggingcentral.cpp @@ -120,9 +120,9 @@ constexpr size_t lineBufferCount = 24; static LogLineBuffer lineBuffers[lineBufferCount]; // freeBuffers contains a queue of buffers that are not in use -chibios_rt::Mailbox freeBuffers; +static chibios_rt::Mailbox freeBuffers; // filledBuffers contains a queue of buffers currently waiting to be written to the output buffer -chibios_rt::Mailbox filledBuffers; +static chibios_rt::Mailbox filledBuffers; class LoggingBufferFlusher : public ThreadController<256> { public: diff --git a/unit_tests/tests/lua/test_lua_basic.cpp b/unit_tests/tests/lua/test_lua_basic.cpp index c5cb9e0898..70caca268a 100644 --- a/unit_tests/tests/lua/test_lua_basic.cpp +++ b/unit_tests/tests/lua/test_lua_basic.cpp @@ -1,3 +1,4 @@ +#include "pch.h" #include "rusefi_lua.h" #include