refactor: replace the TICK_MS global with app.setTickInterval()
A magic global that the runtime reads once at startup could not be changed later, gave no feedback when misspelled, and was a second spelling of a mechanism the sibling firmware already had. app.setTickInterval(ms) clamps to 33..3600000, takes 0 to stop, and errors when on_tick() is not defined -- by the time init() runs the chunk body has finished, so a missing callback is a typo rather than a race. Also drops the code comments pointing at the other repo. Where the two APIs agree or differ belongs in docs/lua-api-parity.md; a comment beside a constant explaining that another firmware picked the same number is noise a reader here cannot act on.
This commit is contained in:
@@ -1,10 +1,7 @@
|
||||
// HTTP client. Signatures match crosspoint-reader's `http` table exactly, so a script
|
||||
// that talks to a server runs on either device; see docs/lua-api-parity.md.
|
||||
//
|
||||
// Unlike crosspoint, certificates are verified against the root bundle already
|
||||
// embedded in the framework. Nothing here lets a script turn that off: the signatures
|
||||
// have no options table to hang it on, and the one caller that would want it most --
|
||||
// a firmware update -- is the one that can least afford an unauthenticated peer.
|
||||
// HTTP client. Certificates are verified against the root bundle already embedded in
|
||||
// the framework, and nothing here lets a script turn that off: the signatures have no
|
||||
// options table to hang it on, and the one caller that would want it most -- a firmware
|
||||
// update -- is the one that can least afford an unauthenticated peer.
|
||||
|
||||
#include <HTTPClient.h>
|
||||
#include <SD.h>
|
||||
@@ -19,7 +16,7 @@ extern const uint8_t rootca_crt_bundle_start[] asm("_binary_x509_crt_bundle_star
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr size_t MAX_RESPONSE = 50000; // matches crosspoint's cap, so limits behave alike
|
||||
constexpr size_t MAX_RESPONSE = 50000;
|
||||
constexpr uint32_t TIMEOUT_MS = 60000;
|
||||
constexpr int REDIRECT_LIMIT = 5;
|
||||
constexpr size_t DOWNLOAD_CHUNK = 2048;
|
||||
@@ -64,8 +61,8 @@ void applyHeaders(lua_State* L, int index, HTTPClient& http) {
|
||||
}
|
||||
|
||||
// get/head/delete take headers in slot 2; post/patch take a body there and headers in 3.
|
||||
// crosspoint reinterprets a string in slot 2 as a body for every method, which turns a
|
||||
// mistyped header table into a silent protocol error, so this rejects it instead.
|
||||
// A string in slot 2 of a bodyless method is rejected rather than reinterpreted, since
|
||||
// a mistyped headers table would otherwise become a silent protocol error.
|
||||
int request(lua_State* L, const char* method, bool bodyExpected) {
|
||||
const char* url = luaL_checkstring(L, 1);
|
||||
const char* body = "";
|
||||
@@ -122,8 +119,8 @@ int fail(lua_State* L, const char* message) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
// Only the keys crosspoint accepts, and rejecting the rest: a misspelled `sha256` that
|
||||
// was quietly ignored would report a verified download that verified nothing.
|
||||
// Unknown keys are rejected: a misspelled `sha256` that was quietly ignored would
|
||||
// report a verified download that verified nothing.
|
||||
bool readOptions(lua_State* L, int index, uint32_t& maxBytes, uint32_t& expectedSize,
|
||||
String& sha256, const char*& error) {
|
||||
luaL_checktype(L, index, LUA_TTABLE);
|
||||
|
||||
@@ -96,12 +96,32 @@ static int logAt(lua_State* L, const char* level) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Rejected rather than ignored when on_tick is absent: the chunk body has already run
|
||||
// by the time init() calls this, so a missing callback is a typo, not a race.
|
||||
static int l_app_setTickInterval(lua_State* L) {
|
||||
lua_Integer requested = luaL_checkinteger(L, 1);
|
||||
if (requested <= 0) {
|
||||
app(L)->setTickInterval(0);
|
||||
return 0;
|
||||
}
|
||||
lua_getglobal(L, "on_tick");
|
||||
bool hasTick = lua_isfunction(L, -1);
|
||||
lua_pop(L, 1);
|
||||
if (!hasTick) return luaL_error(L, "app.setTickInterval() requires on_tick()");
|
||||
|
||||
uint32_t interval = requested < (lua_Integer)LuaApp::MIN_TICK_MS ? LuaApp::MIN_TICK_MS
|
||||
: requested > (lua_Integer)LuaApp::MAX_TICK_MS ? LuaApp::MAX_TICK_MS
|
||||
: (uint32_t)requested;
|
||||
app(L)->setTickInterval(interval);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_log_debug(lua_State* L) { return logAt(L, "debug"); }
|
||||
static int l_log_info(lua_State* L) { return logAt(L, "info"); }
|
||||
static int l_log_error(lua_State* L) { return logAt(L, "error"); }
|
||||
|
||||
// Parity with crosspoint: sleeping is the one thing a script cannot express itself,
|
||||
// since the runtime owns the loop.
|
||||
// Sleeping is the one thing a script cannot express itself, since the runtime owns
|
||||
// the loop.
|
||||
static int l_sys_delay(lua_State* L) {
|
||||
delay(luaL_checkinteger(L, 1));
|
||||
return 0;
|
||||
@@ -155,7 +175,15 @@ void registerSys(lua_State* L) {
|
||||
luaL_newlib(L, lib);
|
||||
lua_setglobal(L, "sys");
|
||||
|
||||
// Too small to deserve its own translation unit.
|
||||
// These two are too small to deserve their own translation units.
|
||||
static const luaL_Reg appLib[] = {
|
||||
// --- Sets how often on_tick() runs. Errors when on_tick is not defined.
|
||||
// @param intervalMs integer 0 stops ticking; anything else is clamped to 33..3600000.
|
||||
{"setTickInterval", l_app_setTickInterval},
|
||||
{nullptr, nullptr}};
|
||||
luaL_newlib(L, appLib);
|
||||
lua_setglobal(L, "app");
|
||||
|
||||
static const luaL_Reg logLib[] = {
|
||||
// --- Writes a debug line to the serial log.
|
||||
// @param message string
|
||||
|
||||
@@ -85,8 +85,8 @@ static int l_wifi_status(lua_State* L) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Parity with crosspoint, whose wifi.status() returns a bare string and so needs these
|
||||
// as separate calls. Here they are shorthands for fields status() already carries.
|
||||
// Shorthands for two fields status() already carries, for scripts that want one answer
|
||||
// without unpacking a table.
|
||||
static int l_wifi_isConnected(lua_State* L) {
|
||||
lua_pushboolean(L, WiFi.status() == WL_CONNECTED);
|
||||
return 1;
|
||||
|
||||
+10
-14
@@ -91,6 +91,9 @@ bool LuaApp::takeFailure() {
|
||||
|
||||
bool LuaApp::load(const char* path) {
|
||||
closeState();
|
||||
// Cleared per app: the interval outlives the app that set it, so a ticking app
|
||||
// followed by one that never ticks would keep calling a nil global.
|
||||
tickIntervalMs = 0;
|
||||
// The launcher tap may still be down; swallow that gesture's release.
|
||||
lastTouched = true;
|
||||
ignoreRelease = true;
|
||||
@@ -109,29 +112,22 @@ bool LuaApp::load(const char* path) {
|
||||
return false;
|
||||
}
|
||||
if (!callGlobal(path)) return false; // run chunk body
|
||||
// Required, as on crosspoint-reader: an app whose entry point is misspelled would
|
||||
// otherwise start, draw nothing, and give no hint why.
|
||||
// An app whose entry point is misspelled would otherwise start, draw nothing, and
|
||||
// give no hint why.
|
||||
if (!hasGlobal("init")) {
|
||||
fail("Missing init()");
|
||||
return false;
|
||||
}
|
||||
lua_getglobal(state, "init");
|
||||
if (!callGlobal("init")) return false;
|
||||
// Cleared first: the interval outlives the app that set it, so a ticking app
|
||||
// followed by one without on_tick would otherwise keep calling a nil global.
|
||||
tickIntervalMs = 0;
|
||||
if (hasGlobal("on_tick")) {
|
||||
tickIntervalMs = DEFAULT_TICK_MS;
|
||||
lua_getglobal(state, "TICK_MS");
|
||||
if (lua_isinteger(state, -1)) {
|
||||
tickIntervalMs = std::max<uint32_t>(lua_tointeger(state, -1), DRAW_INTERVAL_MS);
|
||||
}
|
||||
lua_pop(state, 1);
|
||||
nextTickMs = millis() + tickIntervalMs;
|
||||
}
|
||||
return running();
|
||||
}
|
||||
|
||||
void LuaApp::setTickInterval(uint32_t intervalMs) {
|
||||
tickIntervalMs = intervalMs;
|
||||
nextTickMs = millis() + intervalMs;
|
||||
}
|
||||
|
||||
void LuaApp::registerBindings() {
|
||||
registerGui(state);
|
||||
registerSys(state);
|
||||
|
||||
+6
-1
@@ -28,6 +28,12 @@ class LuaApp {
|
||||
// can leave the message on screen long enough to read.
|
||||
bool takeFailure();
|
||||
|
||||
// Set from Lua by app.setTickInterval(); 0 stops the ticks.
|
||||
void setTickInterval(uint32_t intervalMs);
|
||||
|
||||
static constexpr uint32_t MIN_TICK_MS = 33; // no point ticking faster than a draw
|
||||
static constexpr uint32_t MAX_TICK_MS = 3600000; // an hour
|
||||
|
||||
// Panel geometry in its rotation-0 frame; calibration is stored in this space
|
||||
// so rotating the UI never needs a recalibration.
|
||||
static constexpr int16_t PANEL_W = 320;
|
||||
@@ -57,7 +63,6 @@ class LuaApp {
|
||||
uint32_t nextDrawMs = 0;
|
||||
|
||||
static constexpr uint32_t DRAW_INTERVAL_MS = 33;
|
||||
static constexpr uint32_t DEFAULT_TICK_MS = 100; // used when on_tick exists without TICK_MS
|
||||
|
||||
void registerBindings();
|
||||
bool callGlobal(const char* name, int nargs = 0);
|
||||
|
||||
Reference in New Issue
Block a user