1515#if FT_MOONLIGHT == 1
1616
1717#include " Module.h"
18+ #include " Leds.h"
1819
1920#undef TAG
2021#define TAG " 💫"
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
5237class 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
0 commit comments