merged AppMatch.State.MINUTES/SECONDS to RUNNING + web-control for configMillis + re-implemented websocketEvent + configWrite delay
This commit is contained in:
parent
66a8474fca
commit
b7c2899f3f
@ -131,7 +131,7 @@
|
||||
segment.setAttribute("y", y + "vw");
|
||||
segment.setAttribute("width", S + "vw");
|
||||
segment.setAttribute("height", S + "vw");
|
||||
segment.setAttribute("stroke", "magenta");
|
||||
segment.setAttribute("stroke", "white");
|
||||
segment.setAttribute("fill", "none");
|
||||
segment.setAttribute("id", "segment" + segments.length);
|
||||
display.appendChild(segment);
|
||||
@ -155,9 +155,7 @@
|
||||
|
||||
function connect() {
|
||||
console.log("connecting websocket...");
|
||||
const url2 = url("ws", "/ws");
|
||||
console.log(url2);
|
||||
const socket = new WebSocket(url2);
|
||||
const socket = new WebSocket(url("ws", "/ws"));
|
||||
socket.timeout = 1;
|
||||
socket.addEventListener('open', _ => {
|
||||
console.log('websocket connected');
|
||||
@ -183,14 +181,14 @@
|
||||
console.log('websocket disconnected');
|
||||
segments.forEach(segment => {
|
||||
segment.setAttribute("fill", "none");
|
||||
segment.setAttribute("stroke", "magenta");
|
||||
segment.setAttribute("stroke", "white");
|
||||
});
|
||||
connect();
|
||||
setTimeout(connect, 1000);
|
||||
});
|
||||
}
|
||||
|
||||
drawDisplay(1, 1);
|
||||
connect();
|
||||
setTimeout(connect, 1000);
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
@ -6,6 +6,8 @@
|
||||
#include <core/log.h>
|
||||
#include <display/Display.h>
|
||||
|
||||
#define CONFIG_WRITE_DELAY_MILLIS (30 * 1000)
|
||||
|
||||
class App {
|
||||
|
||||
const char *name;
|
||||
@ -16,6 +18,8 @@ class App {
|
||||
|
||||
JsonObject config = configJsonDoc.to<JsonObject>();
|
||||
|
||||
unsigned long configDirty = 0;
|
||||
|
||||
bool dirty = true;
|
||||
|
||||
bool doForceNextHexBuffer = true;
|
||||
@ -36,7 +40,6 @@ public:
|
||||
void start() {
|
||||
configRead();
|
||||
_start();
|
||||
markDirty();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
@ -45,6 +48,10 @@ public:
|
||||
const auto dtMillis = now - lastMillis;
|
||||
lastMillis = now;
|
||||
_loop(dtMillis);
|
||||
if (configDirty != 0 && millis() - configDirty > CONFIG_WRITE_DELAY_MILLIS) {
|
||||
configDirty = 0;
|
||||
configWrite();
|
||||
}
|
||||
if (dirty) {
|
||||
dirty = false;
|
||||
draw();
|
||||
@ -98,6 +105,7 @@ protected:
|
||||
template<typename T>
|
||||
void configSet(const char *key, T value) {
|
||||
config[key] = value;
|
||||
configDirty = max(1UL, millis());
|
||||
}
|
||||
|
||||
virtual void _start() {
|
||||
@ -116,7 +124,7 @@ protected:
|
||||
//
|
||||
}
|
||||
|
||||
void markDirty(const bool forceNextHexBuffer = false) {
|
||||
void markDirty(const bool forceNextHexBuffer) {
|
||||
dirty = true;
|
||||
doForceNextHexBuffer = forceNextHexBuffer;
|
||||
}
|
||||
|
||||
@ -5,13 +5,23 @@
|
||||
|
||||
#include "App.h"
|
||||
|
||||
#define MS_PER_SEC (1000L)
|
||||
|
||||
#define APP_MATCH_NAME "match"
|
||||
#define MILLIS_STEP_UP_DOWN (60 * MS_PER_SEC)
|
||||
#define MILLIS_STEP_LEFT_RIGHT (MS_PER_SEC)
|
||||
#define MILLIS_MIN (MS_PER_SEC)
|
||||
#define MILLIS_MAX ((99 * 60 + 59) * MS_PER_SEC)
|
||||
|
||||
#define CONFIG_SECONDS_KEY "seconds"
|
||||
#define CONFIG_SECONDS_DEFAULT (6 * 60)
|
||||
|
||||
#define CONFIG_CENTIS_KEY "centis"
|
||||
#define CONFIG_CENTIS_DEFAULT (6 * 60)
|
||||
#define CONFIG_CENTIS_DEFAULT false
|
||||
|
||||
enum State {
|
||||
INITIAL, RUNNING, PAUSE, END
|
||||
};
|
||||
|
||||
class AppMatch final : public App {
|
||||
|
||||
@ -39,11 +49,7 @@ class AppMatch final : public App {
|
||||
|
||||
unsigned long updateSeconds = totalSeconds;
|
||||
|
||||
enum State {
|
||||
PAUSE, MINUTES, SECONDS, END
|
||||
};
|
||||
|
||||
State state = PAUSE;
|
||||
State state = INITIAL;
|
||||
|
||||
public:
|
||||
|
||||
@ -55,11 +61,10 @@ public:
|
||||
bool setConfig(const String& key, const String& valueStr) override {
|
||||
if (key.equals(CONFIG_SECONDS_KEY)) {
|
||||
const auto seconds = valueStr.toInt();
|
||||
const auto newMillis = seconds * 1000;
|
||||
const auto newMillis = seconds * MS_PER_SEC;
|
||||
if (seconds > 0 && configMillis != newMillis) {
|
||||
configMillis = newMillis;
|
||||
configSet(CONFIG_SECONDS_KEY, seconds);
|
||||
configWrite();
|
||||
return true;
|
||||
}
|
||||
} else if (key.equals(CONFIG_CENTIS_KEY)) {
|
||||
@ -67,50 +72,71 @@ public:
|
||||
if (configCentis != newCentis) {
|
||||
configCentis = newCentis;
|
||||
configSet(CONFIG_CENTIS_KEY, newCentis);
|
||||
configWrite();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void up() override {
|
||||
addConfigMillis(MILLIS_STEP_UP_DOWN);
|
||||
}
|
||||
|
||||
void down() override {
|
||||
addConfigMillis(-MILLIS_STEP_UP_DOWN);
|
||||
}
|
||||
|
||||
void right() override {
|
||||
addConfigMillis(MILLIS_STEP_LEFT_RIGHT);
|
||||
}
|
||||
|
||||
void left() override {
|
||||
addConfigMillis(-MILLIS_STEP_LEFT_RIGHT);
|
||||
}
|
||||
|
||||
void confirm() override {
|
||||
if (state == END) {
|
||||
return;
|
||||
}
|
||||
switch (state) {
|
||||
case INITIAL:
|
||||
case PAUSE:
|
||||
setState(RUNNING);
|
||||
break;
|
||||
case RUNNING:
|
||||
setState(PAUSE);
|
||||
break;
|
||||
case END:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void cancel() override {
|
||||
if (state == PAUSE || state == END) {
|
||||
_start();
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
void _start() override {
|
||||
configMillis = configGet<unsigned long>(CONFIG_SECONDS_KEY, CONFIG_SECONDS_DEFAULT) * 1000;
|
||||
configMillis = configGet<unsigned long>(CONFIG_SECONDS_KEY, CONFIG_SECONDS_DEFAULT) * MS_PER_SEC;
|
||||
configCentis = configGet<bool>(CONFIG_CENTIS_KEY, CONFIG_CENTIS_DEFAULT);
|
||||
|
||||
info("config:");
|
||||
info(" seconds = %ld", configMillis / 1000);
|
||||
info(" seconds = %ld", configMillis / MS_PER_SEC);
|
||||
info(" centis = %s", configCentis ? "true" : "false");
|
||||
|
||||
totalMillis = configMillis;
|
||||
setState(PAUSE, true);
|
||||
setTotalMillis(configMillis);
|
||||
setState(INITIAL, true);
|
||||
}
|
||||
|
||||
void _loop(const unsigned long dtMillis) override {
|
||||
if (state != PAUSE) {
|
||||
if (state == RUNNING) {
|
||||
if (totalMillis <= dtMillis) {
|
||||
totalMillis = 0;
|
||||
setTotalMillis(0);
|
||||
} else {
|
||||
totalMillis -= dtMillis;
|
||||
}
|
||||
}
|
||||
|
||||
totalCentis = totalMillis / 10;
|
||||
totalSeconds = totalCentis / 100;
|
||||
totalMinutes = totalSeconds / 60;
|
||||
|
||||
partCentis = totalCentis % 100;
|
||||
partSeconds = totalSeconds % 60;
|
||||
|
||||
if (state != PAUSE) {
|
||||
if (totalMinutes > 0) {
|
||||
setState(MINUTES);
|
||||
} else if (totalMillis > 0) {
|
||||
setState(SECONDS);
|
||||
} else {
|
||||
setState(END);
|
||||
setTotalMillis(totalMillis - dtMillis);
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,25 +145,31 @@ protected:
|
||||
if (now - blinkMillis > blinkIntervalMillis) {
|
||||
blinkMillis = now;
|
||||
blinkState = !blinkState;
|
||||
markDirty();
|
||||
markDirty(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (updateSeconds != totalSeconds) {
|
||||
updateSeconds = totalSeconds;
|
||||
markDirty();
|
||||
} else if (state == SECONDS && configCentis) {
|
||||
markDirty();
|
||||
if (state == RUNNING) {
|
||||
if (totalMinutes > 0 || !configCentis) {
|
||||
if (updateSeconds != totalSeconds) {
|
||||
updateSeconds = totalSeconds;
|
||||
markDirty(false);
|
||||
}
|
||||
} else if (configCentis) {
|
||||
markDirty(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void draw() override {
|
||||
display.clear();
|
||||
if (state == PAUSE) {
|
||||
if (state == INITIAL) {
|
||||
display.setColor(WHITE);
|
||||
} else if (state == PAUSE) {
|
||||
display.setColor(BLUE);
|
||||
} else if (totalMillis * 2 >= configMillis) {
|
||||
display.setColor(GREEN);
|
||||
} else if (totalMillis >= 10 * 1000) {
|
||||
} else if (totalMillis > 0) {
|
||||
display.setColor(YELLOW);
|
||||
} else {
|
||||
display.setColor(RED);
|
||||
@ -145,7 +177,6 @@ protected:
|
||||
if (blinkIntervalMillis == 0 || blinkState) {
|
||||
if (totalMinutes > 0) {
|
||||
display.printf("%2d:%02d", totalMinutes, partSeconds);
|
||||
info("%2d:%02d", totalMinutes, partSeconds);
|
||||
} else if (totalMillis > 0) {
|
||||
if (configCentis) {
|
||||
display.printf("%2d.%02d", partSeconds, partCentis);
|
||||
@ -158,60 +189,32 @@ protected:
|
||||
}
|
||||
}
|
||||
|
||||
void confirm() override {
|
||||
if (state == END) {
|
||||
return;
|
||||
}
|
||||
if (state == PAUSE) {
|
||||
setState(MINUTES);
|
||||
} else {
|
||||
setState(PAUSE);
|
||||
}
|
||||
}
|
||||
|
||||
void cancel() override {
|
||||
_start();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void blinkEnable(const unsigned long intervalMillis) {
|
||||
blinkState = false;
|
||||
blinkIntervalMillis = intervalMillis;
|
||||
blinkMillis = millis();
|
||||
}
|
||||
|
||||
void setState(const State newState, const bool force = false) {
|
||||
if (state == newState && !force) {
|
||||
return;
|
||||
}
|
||||
|
||||
state = newState;
|
||||
switch (state) {
|
||||
case PAUSE:
|
||||
blinkEnable(0);
|
||||
break;
|
||||
case MINUTES:
|
||||
updateSeconds = totalSeconds;
|
||||
blinkEnable(0);
|
||||
case SECONDS:
|
||||
blinkEnable(0);
|
||||
break;
|
||||
case END:
|
||||
blinkEnable(500);
|
||||
break;
|
||||
updateSeconds = totalSeconds;
|
||||
blinkEnable(0);
|
||||
if (state == END) {
|
||||
blinkEnable(500);
|
||||
}
|
||||
info("state changed to %s", getStateName());
|
||||
markDirty();
|
||||
|
||||
info("state = %s", getStateName());
|
||||
markDirty(true);
|
||||
}
|
||||
|
||||
const char *getStateName() const {
|
||||
switch (state) {
|
||||
case INITIAL:
|
||||
return "INITIAL";
|
||||
case PAUSE:
|
||||
return "PAUSE";
|
||||
case MINUTES:
|
||||
return "MINUTES";
|
||||
case SECONDS:
|
||||
return "SECONDS";
|
||||
case RUNNING:
|
||||
return "RUNNING";
|
||||
case END:
|
||||
return "END";
|
||||
default:
|
||||
@ -219,6 +222,44 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void blinkEnable(const unsigned long intervalMillis) {
|
||||
blinkState = false;
|
||||
blinkIntervalMillis = intervalMillis;
|
||||
blinkMillis = millis();
|
||||
}
|
||||
|
||||
void addConfigMillis(const long change) {
|
||||
if (state != INITIAL) {
|
||||
return;
|
||||
}
|
||||
const auto newConfigMillis = max(MILLIS_MIN, min(static_cast<long>(configMillis) + change, MILLIS_MAX));
|
||||
if (configMillis != newConfigMillis) {
|
||||
configMillis = newConfigMillis;
|
||||
configSet(CONFIG_SECONDS_KEY, configMillis / 1000);
|
||||
setTotalMillis(configMillis);
|
||||
markDirty(true);
|
||||
}
|
||||
}
|
||||
|
||||
void setTotalMillis(const unsigned long newTotalMillis) {
|
||||
if (totalMillis == newTotalMillis) {
|
||||
return;
|
||||
}
|
||||
|
||||
totalMillis = newTotalMillis;
|
||||
|
||||
if (totalMillis == 0) {
|
||||
setState(END);
|
||||
}
|
||||
|
||||
totalCentis = totalMillis / 10;
|
||||
totalSeconds = totalCentis / 100;
|
||||
totalMinutes = totalSeconds / 60;
|
||||
|
||||
partCentis = totalCentis % 100;
|
||||
partSeconds = totalSeconds % 60;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@ -81,24 +81,16 @@ void httpNotFound(AsyncWebServerRequest *request) {
|
||||
}
|
||||
}
|
||||
|
||||
const char *getWebsocketTypeName(AwsEventType type) {
|
||||
switch (type) {
|
||||
case WS_EVT_CONNECT:
|
||||
return "CONNECT";
|
||||
case WS_EVT_DISCONNECT:
|
||||
return "DISCONNECT";
|
||||
case WS_EVT_PONG:
|
||||
return "PONG";
|
||||
case WS_EVT_ERROR:
|
||||
return "ERROR";
|
||||
case WS_EVT_DATA:
|
||||
return "DATA";
|
||||
default:
|
||||
return "[???]";
|
||||
void websocketEvent(AsyncWebSocket *socket, AsyncWebSocketClient *client, const AwsEventType type, void *arg, unsigned char *message, const unsigned length) {
|
||||
if (type == WS_EVT_CONNECT) {
|
||||
display.sendHexBuffer(client);
|
||||
}
|
||||
}
|
||||
|
||||
void httpSetup() {
|
||||
ws.onEvent(websocketEvent);
|
||||
server.addHandler(&ws);
|
||||
|
||||
server.on("/action/left", HTTP_GET, httpActionLeft);
|
||||
server.on("/action/up", HTTP_GET, httpActionUp);
|
||||
server.on("/action/down", HTTP_GET, httpActionDown);
|
||||
@ -108,7 +100,6 @@ void httpSetup() {
|
||||
server.on("/app/config", HTTP_GET, httpAppConfig);
|
||||
server.serveStatic("/", LittleFS, "/http/", "max-age=86400").setDefaultFile("index.html");
|
||||
server.onNotFound(httpNotFound);
|
||||
server.addHandler(&ws);
|
||||
server.begin();
|
||||
|
||||
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*");
|
||||
|
||||
@ -32,6 +32,8 @@ class Display {
|
||||
|
||||
Color color = WHITE;
|
||||
|
||||
char hexBuffer[HEX_BUFFER_SIZE] = "";
|
||||
|
||||
public:
|
||||
|
||||
Display() /* : leds(PIXEL_COUNT, GPIO_NUM_13) */ {
|
||||
@ -61,7 +63,8 @@ public:
|
||||
static auto last = now;
|
||||
if (now - last >= HEX_BUFFER_MIN_WAIT_MS || forceNextHexBuffer) {
|
||||
last = now;
|
||||
sendHexBuffer();
|
||||
fillHexBuffer();
|
||||
websocketSendAll(hexBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,15 +146,17 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void sendHexBuffer(AsyncWebSocketClient *client) {
|
||||
client->text(hexBuffer);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void sendHexBuffer() {
|
||||
char hexBuffer[HEX_BUFFER_SIZE] = "";
|
||||
void fillHexBuffer() {
|
||||
auto b = hexBuffer;
|
||||
for (const auto& pixel: pixels) {
|
||||
b += snprintf(b, sizeof hexBuffer - (b - hexBuffer), "%X%X%X", pixel.r / 16, pixel.g / 16, pixel.b / 16);
|
||||
}
|
||||
websocketSendAll(hexBuffer);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user