Skip to content

Commit c782522

Browse files
committed
Module update bug, emit bug, compile add type and error
UI == - ObjectArray: show max 3 props in overview - Module: clear data before getting new data Server ===== - only emit to socket if clients! - Add Leds.h - WIP - Module.cpp: compareRecursive: changed bug - ModuleAnimation: include leds.h and create ledsModel. CompileAndRun: add type and error. Add error to model
1 parent 04619e2 commit c782522

File tree

9 files changed

+11357
-11290
lines changed

9 files changed

+11357
-11290
lines changed

docs/moonbase/modules.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ void setupDefinition(JsonArray root) override{
9292
9393
```cpp
9494
void loop1s() {
95+
if (!_socket->getConnectedClients()) return;
96+
9597
JsonDocument newData; //to only send updatedData
9698
9799
JsonArray scripts = newData["scripts"].to<JsonArray>(); //to: remove old array

interface/src/lib/components/custom/ObjectArray.svelte

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -153,15 +153,18 @@
153153
<div class="mask mask-hexagon bg-primary h-auto w-10 shrink-0">
154154
<Router class="text-primary-content h-auto w-full scale-75" />
155155
</div>
156+
{#each property.n.slice(0, 3) as propertyN}
157+
{#if propertyN.type != "array" && propertyN.type != "password"}
158+
<div>
159+
<div class="font-bold">{data[property.name][index][propertyN.name]}</div>
160+
</div>
161+
{/if}
162+
{/each}
156163
{#each property.n as propertyN}
157164
{#if propertyN.type == "array"}
158165
<div>
159166
<div class="font-bold">↓{nrofDetails(index, propertyN)}</div>
160167
</div>
161-
{:else if propertyN.type != "password"}
162-
<div>
163-
<div class="font-bold">{data[property.name][index][propertyN.name]}</div>
164-
</div>
165168
{/if}
166169
{/each}
167170
{#if !page.data.features.security || $user.admin}

interface/src/routes/+layout.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export const load = (async ({ fetch }) => {
1313
features: item,
1414
title: 'MoonBase',
1515
github: 'ewowi/MoonBase',
16-
copyright: '2025 MoonModules ⚖️GPLv3',
16+
copyright: '2025 MoonModules⚖️GPLv3',
1717
appName: 'MoonBase',
1818
instances: instancesJson
1919
};

interface/src/routes/moonbase/module/Module.svelte

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
3333
//workaround for let params = $state(page.url.searchParams)
3434
if (moduleName != oldName) {
35-
console.log("getState changed", moduleName);
35+
console.log("getState new module loaded", moduleName);
3636
if (oldName != "") {
3737
socketOff(oldName);
3838
}
@@ -67,6 +67,7 @@
6767
'Content-Type': 'application/json'
6868
}
6969
});
70+
data = {}; //clear the data of the old module
7071
handleState(await response.json())
7172
// console.log("data", data)
7273
} catch (error) {

lib/framework/WWWData.h

Lines changed: 11234 additions & 11231 deletions
Large diffs are not rendered by default.

src/custom/Leds.h

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/**
2+
@title MoonBase
3+
@file Leds.h
4+
@repo https://github.com/ewowi/MoonBase, submit changes to this file as PRs
5+
@Authors https://github.com/ewowi/MoonBase/commits/main
6+
@Doc https://ewowi.github.io/MoonBase/general/utilities/
7+
@Copyright © 2025 Github MoonBase Commit Authors
8+
@license GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007
9+
@license For non GPL-v3 usage, commercial licenses must be purchased. Contact [email protected]
10+
**/
11+
12+
#pragma once
13+
14+
#include <Arduino.h>
15+
#include "FastLED.h"
16+
17+
struct Coord3D {
18+
int x;
19+
int y;
20+
int z;
21+
};
22+
23+
class Node {
24+
25+
};
26+
27+
class EffectNode : public Node {
28+
29+
public:
30+
31+
Coord3D size = {8,8,1}; //not 0,0,0 to prevent div0 eg in Octopus2D
32+
33+
void fadeToBlackBy(const uint8_t fadeBy) {}
34+
void setPixelColor(const Coord3D &pixel, const CRGB& color) {}
35+
};
36+
37+
class LedsModel {
38+
39+
public:
40+
41+
void addPixel(uint16_t x, uint16_t y, uint16_t z) {
42+
ESP_LOGD(TAG, "addPixel %d,%d,%d", x, y, z);
43+
}
44+
45+
};

src/custom/Module.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,11 @@ bool ModuleState::compareRecursive(JsonString parent, JsonVariant stateData, Jso
7979

8080
for (int i = 0; i < max(oldArray.size(), newArray.size()); i++) { //compare each item in the array
8181
if (oldArray.size() < newArray.size()) oldArray.add<JsonObject>(); //add new row
82-
changed = changed || compareRecursive(key, oldArray[i], newArray[i], updatedItem, depth+1, i);
82+
changed = compareRecursive(key, oldArray[i], newArray[i], updatedItem, depth+1, i) || changed;
8383
if (oldArray.size() > newArray.size()) oldArray.remove(i); //remove old row
8484
}
8585
} else { // if property is key/value
86+
changed = true;
8687
updatedItem.name = key.c_str();
8788
updatedItem.oldValue = stateValue.as<String>();
8889
updatedItem.value = newValue;

src/custom/ModuleAnimations.h

Lines changed: 62 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#if FT_MOONLIGHT == 1
1616

1717
#include "Module.h"
18+
#include "Leds.h"
1819

1920
#undef TAG
2021
#define TAG "💫"
@@ -26,28 +27,12 @@
2627
#include "ESPLiveScript.h" //note: contains declarations AND definitions, therefore can only be included once!
2728
#endif
2829

29-
struct Coord3D {
30-
int x;
31-
int y;
32-
int z;
33-
};
34-
35-
class Node {
36-
37-
};
38-
39-
class EffectNode : public Node {
40-
41-
public:
42-
43-
Coord3D size = {8,8,1}; //not 0,0,0 to prevent div0 eg in Octopus2D
30+
class ModuleAnimations;
31+
static ModuleAnimations *gAnimations = nullptr;
4432

45-
void fadeToBlackBy(const uint8_t fadeBy) {}
46-
void setPixelColor(const Coord3D &pixel, const CRGB& color) {}
47-
};
33+
LedsModel ledsModel;
34+
static void _addPixel(uint16_t x, uint16_t y, uint16_t z) {ledsModel.addPixel(x, y, z);}
4835

49-
class ModuleAnimations;
50-
static ModuleAnimations *gLiveAnimation = nullptr;
5136

5237
class ModuleAnimations : public Module
5338
{
@@ -71,7 +56,7 @@ class ModuleAnimations : public Module
7156

7257
ESP_LOGD(TAG, "");
7358

74-
//create a handler which recompiles the animation when the file of the current animation changes
59+
//create a handler which recompiles the animation when the file of the current animation changes in the File Manager
7560
_filesService->addUpdateHandler([&](const String &originId)
7661
{
7762
ESP_LOGD(TAG, "FilesService::updateHandler %s", originId.c_str());
@@ -85,7 +70,7 @@ class ModuleAnimations : public Module
8570

8671
if (updatedItem == animation) {
8772
ESP_LOGD(TAG, "updateHandler updatedItem %s", updatedItem.c_str());
88-
compileAndRun(node["animation"]);
73+
compileAndRun(node["animation"], node["type"], node["error"]);
8974
}
9075
}
9176
}
@@ -95,10 +80,11 @@ class ModuleAnimations : public Module
9580
_socket->registerEvent("animationsRO");
9681
}
9782

83+
//define the data model
9884
void setupDefinition(JsonArray root) override {
9985
ESP_LOGD(TAG, "");
10086
JsonObject property; // state.data has one or more properties
101-
JsonArray details; // if a property is an array, this is the details of the array
87+
JsonArray details = root; // if a property is an array, this is the details of the array
10288
JsonArray values; // if a property is a select, this is the values of the select
10389

10490
property = root.add<JsonObject>(); property["name"] = "lightsOn"; property["type"] = "checkbox"; property["default"] = true;
@@ -132,6 +118,7 @@ class ModuleAnimations : public Module
132118
values.add("Modifier");
133119
values.add("Driver show");
134120
property = details.add<JsonObject>(); property["name"] = "size"; property["type"] = "number"; property["default"] = 85;
121+
property = details.add<JsonObject>(); property["name"] = "error"; property["type"] = "text"; property["ro"] = true;
135122
property = details.add<JsonObject>(); property["name"] = "controls"; property["type"] = "array"; details = property["n"].to<JsonArray>();
136123
{
137124
property = details.add<JsonObject>(); property["name"] = "name"; property["type"] = "text"; property["default"] = "speed";
@@ -154,6 +141,7 @@ class ModuleAnimations : public Module
154141
property = details.add<JsonObject>(); property["name"] = "handle"; property["type"] = "number"; property["ro"] = true;
155142
property = details.add<JsonObject>(); property["name"] = "binary_size"; property["type"] = "number"; property["ro"] = true;
156143
property = details.add<JsonObject>(); property["name"] = "data_size"; property["type"] = "number"; property["ro"] = true;
144+
property = details.add<JsonObject>(); property["name"] = "error"; property["type"] = "text"; property["ro"] = true;
157145
property = details.add<JsonObject>(); property["name"] = "kill"; property["type"] = "button";
158146
}
159147
}
@@ -170,6 +158,7 @@ class ModuleAnimations : public Module
170158
ESP_LOGD(TAG, "LEDs removed and FastLED configuration reset.");
171159
}
172160

161+
//implement business logic
173162
void onUpdate(UpdatedItem &updatedItem) override
174163
{
175164
if (equal(updatedItem.name, "pin")) {
@@ -194,15 +183,17 @@ class ModuleAnimations : public Module
194183
} else if (equal(updatedItem.name, "lightsOn") || equal(updatedItem.name, "brightness")) {
195184
ESP_LOGD(TAG, "handle %s = %s -> %s", updatedItem.name, updatedItem.oldValue.c_str(), updatedItem.value.as<String>().c_str());
196185
FastLED.setBrightness(_state.data["lightsOn"]?_state.data["brightness"]:0);
197-
} else if (equal(updatedItem.parent[0], "nodes") && equal(updatedItem.name, "animation")) {
186+
} else if (equal(updatedItem.parent[0], "nodes") && (equal(updatedItem.name, "animation") || equal(updatedItem.name, "type"))) {
187+
JsonVariant node = _state.data["nodes"][updatedItem.index[0]];
198188
ESP_LOGD(TAG, "handle %s = %s -> %s", updatedItem.name, updatedItem.oldValue.c_str(), updatedItem.value.as<String>().c_str());
199189
if (updatedItem.oldValue.length())
200-
ESP_LOGD(TAG, "delete %s ...", updatedItem.oldValue.c_str());
201-
if (updatedItem.value.as<String>().length())
202-
compileAndRun(updatedItem.value);
190+
ESP_LOGD(TAG, "delete %s %s ...", updatedItem.name, updatedItem.oldValue.c_str());
191+
if (!node["animation"].isNull() && !node["type"].isNull())
192+
compileAndRun(node["animation"], node["type"], node["error"]);
203193
} else if (equal(updatedItem.parent[0], "scripts") && equal(updatedItem.name, "kill")) {
204-
ESP_LOGD(TAG, "handle %s[%d].%s = %s -> %s", updatedItem.parent[0], updatedItem.index[0], updatedItem.name, updatedItem.oldValue.c_str(), updatedItem.value.as<String>().c_str());
205-
kill(updatedItem.index[0]);
194+
ESP_LOGD(TAG, "handle %s[%d].%s = %s -> %s (%d)", updatedItem.parent[0], updatedItem.index[0], updatedItem.name, updatedItem.oldValue.c_str(), updatedItem.value.as<String>().c_str(), updatedItem.oldValue.length());
195+
if (updatedItem.oldValue != "null") //do not run at boot!
196+
kill(updatedItem.index[0]);
206197
} else
207198
ESP_LOGD(TAG, "no handle for %s = %s -> %s", updatedItem.name, updatedItem.oldValue.c_str(), updatedItem.value.as<String>().c_str());
208199
}
@@ -227,6 +218,7 @@ class ModuleAnimations : public Module
227218
phase += speed;
228219
}
229220

221+
//run effects
230222
void loop()
231223
{
232224
bool showLeds = false;
@@ -292,6 +284,7 @@ class ModuleAnimations : public Module
292284
if (showLeds) driverShow();
293285
}
294286

287+
//update scripts / read only values in the UI
295288
void loop1s() {
296289
if (!_socket->getConnectedClients()) return;
297290

@@ -312,6 +305,7 @@ class ModuleAnimations : public Module
312305
object["handle"] = exec.__run_handle_index;
313306
object["binary_size"] = exeInfo.binary_size;
314307
object["data_size"] = exeInfo.data_size;
308+
object["error"] = exec.error.error_message;
315309
object["kill"] = 0;
316310
// ESP_LOGD(TAG, "scriptRuntime exec %s r:%d h:%d, e:%d h:%d b:%d + d:%d = %d", exec.name.c_str(), exec.isRunning(), exec.isHalted, exec.exeExist, exec.__run_handle_index, exeInfo.binary_size, exeInfo.data_size, exeInfo.total_size);
317311
}
@@ -328,27 +322,29 @@ class ModuleAnimations : public Module
328322
// ESP_LOGD(TAG, "livescripts %s", buffer);
329323
}
330324

331-
static void show()
332-
{
333-
delay(1); //to feed the watchdog (also if loopState == 0)
334-
335-
// gLiveAnimation->driverShow();
336-
}
337-
338325
void driverShow()
339326
{
340327
if (_state.data["driverOn"])
341328
FastLED.show();
342329
}
343330

344-
//ESPLiveScript
345-
void compileAndRun(JsonVariant animation) {
331+
//ESPLiveScript functions
332+
//=======================
333+
334+
static void show()
335+
{
336+
delay(1); //to feed the watchdog (also if loopState == 0)
337+
338+
// gAnimations->driverShow();
339+
}
340+
341+
void compileAndRun(const char * animation, const char * type, JsonVariant error) {
346342

347343
#if FT_LIVESCRIPT
348344

349-
ESP_LOGD(TAG, "animation %s", animation.as<String>().c_str());
345+
ESP_LOGD(TAG, "animation %s %s", animation, type);
350346

351-
if (animation.as<String>().c_str()[0] != '/') { //no sc script
347+
if (animation[0] != '/') { //no sc script
352348
return;
353349
}
354350

@@ -365,28 +361,43 @@ class ModuleAnimations : public Module
365361

366362
//run the recompile not in httpd but in main loopTask (otherwise we run out of stack space)
367363
// runInLoopTask.push_back([&, animation] {
368-
ESP_LOGD(TAG, "compileAndRun %s", animation.as<String>().c_str());
369-
File file = ESPFS.open(animation.as<String>().c_str());
364+
ESP_LOGD(TAG, "compileAndRun %s %s", animation, type);
365+
File file = ESPFS.open(animation);
370366
if (file) {
371367
std::string scScript = file.readString().c_str();
372368
// scScript += "void main(){setup();sync();}";
373369
file.close();
374370

375-
gLiveAnimation = this;
376-
addExternalVariable("leds", "CRGB *", "", (void *)leds);
377-
// addExternalFunction("fadeToBlackBy", "void", "uint8_t", (void *)fadeToBlackBy_static);
378-
uint16_t (*_random16)(uint16_t)=random16; //enforce specific random16 function
379-
addExternalFunction("random16", "uint16_t", "uint16_t", (void *)_random16);
380-
addExternalFunction("sin8", "uint8_t", "uint8_t", (void *)sin8);
371+
gAnimations = this;
372+
if (equal(type, "Effect")) {
373+
addExternalVariable("leds", "CRGB *", "", (void *)leds);
374+
// addExternalFunction("fadeToBlackBy", "void", "uint8_t", (void *)fadeToBlackBy_static);
375+
uint16_t (*_random16)(uint16_t)=random16; //enforce specific random16 function
376+
addExternalFunction("random16", "uint16_t", "uint16_t", (void *)_random16);
377+
addExternalFunction("sin8", "uint8_t", "uint8_t", (void *)sin8);
378+
} else if (equal(type, "Fixture definition")) {
379+
if (findLink("addPixel", externalType::function) == -1)
380+
addExternalFunction("addPixel", "void", "uint16_t,uint16_t,uint16_t", (void *)_addPixel);
381+
}
381382

382383
Executable executable = parser.parseScript(&scScript);
383-
executable.name = animation.as<string>();
384-
ESP_LOGD(TAG, "parsing %s done\n", animation.as<String>().c_str());
384+
executable.name = animation;
385+
ESP_LOGD(TAG, "parsing %s done\n", animation);
385386
scriptRuntime.addExe(executable); //if already exists, delete it first
386387
ESP_LOGD(TAG, "addExe success %s\n", executable.exeExist?"true":"false");
387388

388389
if (executable.exeExist)
389390
executable.execute("main"); //background task (async - vs sync)
391+
else
392+
ESP_LOGD(TAG, "error %s", executable.error.error_message.c_str());
393+
394+
for (asm_external el: external_links) {
395+
ESP_LOGD("", "el %s %s %d", el.shortname.c_str(), el.name.c_str(), el.type);
396+
}
397+
// ... send to client
398+
//send also error to client
399+
// error.set(executable.error.error_message); //String(executable.error.error_message.c_str());
400+
_state.data["nodes"][2]["error"] = executable.error.error_message;
390401

391402
}
392403
// });
@@ -395,7 +406,6 @@ class ModuleAnimations : public Module
395406
//stop UI spinner
396407
}
397408

398-
// ESPLiveScript
399409
void kill(int index) {
400410
ESP_LOGD(TAG, "kill %d", index);
401411
if (index < scriptRuntime._scExecutables.size()) {
@@ -404,7 +414,7 @@ class ModuleAnimations : public Module
404414
scriptRuntime.deleteExe(scriptRuntime._scExecutables[index].name);
405415
}
406416
}
407-
};
417+
}; // class ModuleAnimations
408418

409419
#endif
410420
#endif

src/custom/ModuleInstances.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ class ModuleInstances : public Module
114114
// char buffer[2048];
115115
// serializeJson(_state.data, buffer, sizeof(buffer));
116116
// ESP_LOGD(TAG, "data %s", buffer);
117+
if (!_socket->getConnectedClients()) return;
118+
117119
JsonObject instancesData = _state.data.as<JsonObject>();
118120
_socket->emitEvent("instances", instancesData);
119121
}

0 commit comments

Comments
 (0)