Browse Source

fix (filebrowser): fixed blocking, when file list is reated

- created a single used task for file indexing process
 - added more reliable and user friendly design for the user interface
 - added escaping for css in processPython.py script
master
Mario Lukas 5 years ago
parent
commit
f940a2ee67
  1. 45
      html/website.html
  2. 6
      processHtml.py
  3. 70
      src/main.cpp
  4. 49
      src/websiteMgmt.h

45
html/website.html

@ -56,13 +56,18 @@
font-size: 0.8em; font-size: 0.8em;
} }
.refreshAction:hover{
cursor: pointer;
color: darkgray;
}
.overlay { .overlay {
z-index: 9; z-index: 9;
opacity: 0.8; opacity: 0.8;
background: #1a1919; background: #1a1919;
height: 200px; height: 200px;
display: none; display: none;
width: 32em;
width: 100%;
} }
</style> </style>
</head> </head>
@ -145,7 +150,8 @@
</div> </div>
<div id="indexing-progress" class="indexing-progress overlay"> <div id="indexing-progress" class="indexing-progress overlay">
<div style="text-align: center; color:white; margin-top:2em;"> <div style="text-align: center; color:white; margin-top:2em;">
<p><i class="fas fa-sync fa-spin fa-2x"></i> <br><br> Der Prozess kann mehrere Minuten dauern...</p>
<div><i class="fas fa-sync fa-spin fa-2x"></i> <br><br> Der Prozess kann mehrere Minuten dauern...</div>
<div id="currentProcessedFile"></div>
</div> </div>
</div> </div>
</div> </div>
@ -429,7 +435,18 @@
} }
}); });
function showFileIndexingState(){
$("#indexing-progress").show();
$("#refreshAction").hide();
}
function hideFileIndexingState(){
$("#indexing-progress").hide();
$("#refreshAction").show();
}
var socket = undefined; var socket = undefined;
var tm;
function connect() { function connect() {
socket = new WebSocket("ws://" + host + "/ws"); socket = new WebSocket("ws://" + host + "/ws");
@ -440,9 +457,7 @@
socket.onclose = function (e) { socket.onclose = function (e) {
console.log('Socket is closed. Reconnect will be attempted in 1 second.', e.reason); console.log('Socket is closed. Reconnect will be attempted in 1 second.', e.reason);
toastr.error('Die Websocket Verbindung wurde vom Tonuino unterbrochen.');
socket = null; socket = null;
setTimeout(function () { setTimeout(function () {
connect(); connect();
}, 5000); }, 5000);
@ -450,8 +465,6 @@
socket.onerror = function (err) { socket.onerror = function (err) {
console.error('Socket encountered error: ', err.message, 'Closing socket'); console.error('Socket encountered error: ', err.message, 'Closing socket');
toastr.error('Es gab einen Fehler bei der Websocket Verbindung');
socket.close();
}; };
socket.onmessage = function(event) { socket.onmessage = function(event) {
@ -465,24 +478,25 @@
$("#rfidIdMusic").effect("highlight", {color:"#abf5af"}, 3000); $("#rfidIdMusic").effect("highlight", {color:"#abf5af"}, 3000);
$("#rfidIdMod").effect("highlight", {color:"#abf5af"}, 3000); $("#rfidIdMod").effect("highlight", {color:"#abf5af"}, 3000);
} if (socketMsg.status != null) {
if (socketMsg.status == 'ok') {
} if ("status" in socketMsg) {
if (socketMsg.status == "ok") {
toastr.success("Aktion erfolgreich ausgeführt." ); toastr.success("Aktion erfolgreich ausgeführt." );
} else {
toastr.error("Es ist ein Fehler aufgetreten." );
} }
} if (socketMsg.pong != null) {
} if ("pong" in socketMsg) {
if (socketMsg.pong == 'pong') { if (socketMsg.pong == 'pong') {
pong(); pong();
} }
} if ("refreshFileList" in socketMsg){ } if ("refreshFileList" in socketMsg){
hideFileIndexingState();
toastr.info("Die Datei Liste wurde neu erzeugt!"); toastr.info("Die Datei Liste wurde neu erzeugt!");
$('#filetree').jstree(true).refresh(); $('#filetree').jstree(true).refresh();
$("#indexing-progress").hide();
$("#refreshAction").show();
} }
if ("indexingState" in socketMsg){ if ("indexingState" in socketMsg){
console.log(socketMsg.indexingState);
if(socketMsg.indexingState != null) {
$("#currentProcessedFile").text(socketMsg.indexingState);
console.log(socketMsg.indexingState);
}
} }
}; };
} }
@ -512,8 +526,7 @@
var myJSON = JSON.stringify(myObj); var myJSON = JSON.stringify(myObj);
$("#refreshAction").hide(); $("#refreshAction").hide();
socket.send(myJSON); socket.send(myJSON);
$("#indexing-progress").show();
$("#refreshAction").hide();
showFileIndexingState();
}; };
function genSettings(clickedId) { function genSettings(clickedId) {

6
processHtml.py

@ -1,16 +1,20 @@
#!/usr/bin/python #!/usr/bin/python
import re
content = '' content = ''
content2 = '' content2 = ''
contentEN = '' contentEN = ''
content2EN = '' content2EN = ''
# TODO: Add a JS Minifier python lib
with open('html/website.html', 'r') as r: with open('html/website.html', 'r') as r:
data = r.read().replace('\n', '\\\n')
data = r.read()
data = data.replace('\n', '\\\n')
data = data.replace('\"', '\\"') data = data.replace('\"', '\\"')
data = data.replace('\\d', '\\\d') data = data.replace('\\d', '\\\d')
data = data.replace('\\.', '\\\.') data = data.replace('\\.', '\\\.')
data = data.replace('\\^', '\\\\^') data = data.replace('\\^', '\\\\^')
data = data.replace('%;', '%%;')
content += data content += data
with open('src/websiteMgmt.h', 'w') as w: with open('src/websiteMgmt.h', 'w') as w:

70
src/main.cpp

@ -367,7 +367,6 @@ static const char restartWebsite[] PROGMEM = "<p>Der Tonuino wird neu gestartet.
SPIClass spiSD(HSPI); SPIClass spiSD(HSPI);
TaskHandle_t mp3Play; TaskHandle_t mp3Play;
TaskHandle_t rfid; TaskHandle_t rfid;
TaskHandle_t fileTaskHandle;
#ifdef NEOPIXEL_ENABLE #ifdef NEOPIXEL_ENABLE
TaskHandle_t LED; TaskHandle_t LED;
#endif #endif
@ -649,12 +648,8 @@ bool pathValid(const char *_fileItem) {
* @param levels * @param levels
*/ */
char fileNameBuf[255]; char fileNameBuf[255];
bool notifyOverWebsocket = true;
//FIXME: This function blocks the websocket connection
void parseSDFileList(fs::FS &fs, const char * dirname, const char * parent, uint8_t levels){ void parseSDFileList(fs::FS &fs, const char * dirname, const char * parent, uint8_t levels){
// i/o is timing critical keep all stuff running
esp_task_wdt_reset(); esp_task_wdt_reset();
yield(); yield();
@ -687,11 +682,13 @@ void parseSDFileList(fs::FS &fs, const char * dirname, const char * parent, uint
} }
strncpy(fileNameBuf, (char *) file.name(), sizeof(fileNameBuf) / sizeof(fileNameBuf[0])); strncpy(fileNameBuf, (char *) file.name(), sizeof(fileNameBuf) / sizeof(fileNameBuf[0]));
// we have a folder // we have a folder
if(file.isDirectory()){ if(file.isDirectory()){
esp_task_wdt_reset(); esp_task_wdt_reset();
if (pathValid(fileNameBuf)){ if (pathValid(fileNameBuf)){
sendWebsocketData(0, 31);
appendNodeToJSONFile(SD, DIRECTORY_INDEX_FILE, fileNameBuf, parent, "folder" ); appendNodeToJSONFile(SD, DIRECTORY_INDEX_FILE, fileNameBuf, parent, "folder" );
// check for next subfolder // check for next subfolder
@ -699,21 +696,21 @@ void parseSDFileList(fs::FS &fs, const char * dirname, const char * parent, uint
parseSDFileList(fs, fileNameBuf, root.name(), levels -1); parseSDFileList(fs, fileNameBuf, root.name(), levels -1);
} }
} }
// we have a file
// we have a file
} else { } else {
if (fileValid(fileNameBuf)){ if (fileValid(fileNameBuf)){
appendNodeToJSONFile(SD, DIRECTORY_INDEX_FILE, fileNameBuf, parent, "file" ); appendNodeToJSONFile(SD, DIRECTORY_INDEX_FILE, fileNameBuf, parent, "file" );
} }
} }
vTaskDelay(portTICK_PERIOD_MS*50);
file = root.openNextFile(); file = root.openNextFile();
// i/o is timing critical keep all stuff running // i/o is timing critical keep all stuff running
esp_task_wdt_reset(); esp_task_wdt_reset();
} }
} }
// TODO: maybe this is not save with asyncWebserver
uint8_t runFileIndexing = 0;
uint8_t fileIndexingDone = 0;
/** /**
* Public function for creating file index json on SD-Card. * Public function for creating file index json on SD-Card.
* It notifies the user client via websockets when the indexing * It notifies the user client via websockets when the indexing
@ -724,22 +721,14 @@ void createJSONFileList(){
parseSDFileList(SD, "/", NULL, FS_DEPTH); parseSDFileList(SD, "/", NULL, FS_DEPTH);
appendToFile(SD, DIRECTORY_INDEX_FILE, "]"); appendToFile(SD, DIRECTORY_INDEX_FILE, "]");
isFirstJSONtNode = true; isFirstJSONtNode = true;
sendWebsocketData(0, 30);
sendWebsocketData(0,30);
} }
void fileHandlingTask(void *arguments){
while(true) {
if(runFileIndexing){
runFileIndexing = 0;
//createJSONFileList();
fileIndexingDone = 1;
}
esp_task_wdt_reset();
vTaskDelay(portTICK_PERIOD_MS*150);
}
void fileHandlingTask(void *arguments){
createJSONFileList();
esp_task_wdt_reset();
vTaskDelete( NULL );
} }
// Measures voltage of a battery as per interval or after bootup (after allowing a few seconds to settle down) // Measures voltage of a battery as per interval or after bootup (after allowing a few seconds to settle down)
@ -3028,6 +3017,7 @@ bool getWifiEnableStatusFromNVS(void) {
prefsSettings.putUInt("enableWifi", 1); prefsSettings.putUInt("enableWifi", 1);
wifiStatus = 1; wifiStatus = 1;
} }
return wifiStatus; return wifiStatus;
} }
@ -3315,9 +3305,17 @@ bool processJsonRequest(char *_serialJson) {
sendWebsocketData(0, 20); sendWebsocketData(0, 20);
return false; return false;
} else if (doc.containsKey("refreshFileList")) { } else if (doc.containsKey("refreshFileList")) {
//createJSONFileList();
runFileIndexing = 1;
createJSONFileList();
//TODO: we need a semaphore or mutex here to prevent
// a call when the task is still running
xTaskCreate(
fileHandlingTask, /* Task function. */
"TaskTwo", /* String with name of task. */
10000, /* Stack size in bytes. */
NULL, /* Parameter passed as input of the task */
1, /* Priority of the task. */
NULL); /* Task handle. */
} }
return true; return true;
@ -3342,7 +3340,6 @@ void sendWebsocketData(uint32_t client, uint8_t code) {
object["refreshFileList"] = "ready"; object["refreshFileList"] = "ready";
}else if (code == 31){ }else if (code == 31){
object["indexingState"] = fileNameBuf; object["indexingState"] = fileNameBuf;
esp_task_wdt_reset();
} }
char jBuf[255]; char jBuf[255];
@ -3353,7 +3350,6 @@ void sendWebsocketData(uint32_t client, uint8_t code) {
} else { } else {
ws.printf(client, jBuf); ws.printf(client, jBuf);
} }
notifyOverWebsocket = true;
} }
@ -3929,18 +3925,6 @@ void setup() {
1 /* Core where the task should run */ 1 /* Core where the task should run */
); );
/**
* SD Card File Indexing Task
*/
xTaskCreatePinnedToCore(
fileHandlingTask, /* Function to implement the task */
"fileHandlingTask", /* Name of the task */
2000, /* Stack size in words */
NULL, /* Task input parameter */
3, /* Priority of the task */
&fileTaskHandle, /* Task handle. */
1 /* Core where the task should run */
);
//esp_sleep_enable_ext0_wakeup((gpio_num_t) DREHENCODER_BUTTON, 0); //esp_sleep_enable_ext0_wakeup((gpio_num_t) DREHENCODER_BUTTON, 0);
@ -3979,7 +3963,6 @@ void setup() {
Serial.print(F("Free heap: ")); Serial.print(F("Free heap: "));
Serial.println(ESP.getFreeHeap()); Serial.println(ESP.getFreeHeap());
} }
@ -3997,13 +3980,6 @@ void loop() {
sleepHandler(); sleepHandler();
deepSleepManager(); deepSleepManager();
rfidPreferenceLookupHandler(); rfidPreferenceLookupHandler();
/*
if(fileIndexingDone){
fileIndexingDone = 0;
sendWebsocketData(0,30);
}*/
if (wifiManager() == WL_CONNECTED) { if (wifiManager() == WL_CONNECTED) {
#ifdef MQTT_ENABLE #ifdef MQTT_ENABLE
if (enableMqtt) { if (enableMqtt) {

49
src/websiteMgmt.h

@ -42,8 +42,8 @@ static const char mgtWebsite[] PROGMEM = "<!DOCTYPE html>\
}\ }\
\ \
.indexing-progress {\ .indexing-progress {\
width: 100%;\
height: 100%;\
width: 100%%;\
height: 100%%;\
position: absolute;\ position: absolute;\
top: 0;\ top: 0;\
left: 0;\ left: 0;\
@ -55,6 +55,11 @@ static const char mgtWebsite[] PROGMEM = "<!DOCTYPE html>\
text-align: right;\ text-align: right;\
font-size: 0.8em;\ font-size: 0.8em;\
}\ }\
\
.refreshAction:hover{\
cursor: pointer;\
color: darkgray;\
}\
\ \
.overlay {\ .overlay {\
z-index: 9;\ z-index: 9;\
@ -62,7 +67,7 @@ static const char mgtWebsite[] PROGMEM = "<!DOCTYPE html>\
background: #1a1919;\ background: #1a1919;\
height: 200px;\ height: 200px;\
display: none;\ display: none;\
width: 32em;\
width: 100%%;\
}\ }\
</style>\ </style>\
</head>\ </head>\
@ -145,7 +150,8 @@ static const char mgtWebsite[] PROGMEM = "<!DOCTYPE html>\
</div>\ </div>\
<div id=\"indexing-progress\" class=\"indexing-progress overlay\">\ <div id=\"indexing-progress\" class=\"indexing-progress overlay\">\
<div style=\"text-align: center; color:white; margin-top:2em;\">\ <div style=\"text-align: center; color:white; margin-top:2em;\">\
<p><i class=\"fas fa-sync fa-spin fa-2x\"></i> <br><br> Der Prozess kann mehrere Minuten dauern...</p>\
<div><i class=\"fas fa-sync fa-spin fa-2x\"></i> <br><br> Der Prozess kann mehrere Minuten dauern...</div>\
<div id=\"currentProcessedFile\"></div>\
</div>\ </div>\
</div>\ </div>\
</div>\ </div>\
@ -428,8 +434,19 @@ static const char mgtWebsite[] PROGMEM = "<!DOCTYPE html>\
$('#filebrowser').slideDown();\ $('#filebrowser').slideDown();\
}\ }\
});\ });\
\
function showFileIndexingState(){\
$(\"#indexing-progress\").show();\
$(\"#refreshAction\").hide();\
}\
\
function hideFileIndexingState(){\
$(\"#indexing-progress\").hide();\
$(\"#refreshAction\").show();\
}\
\ \
var socket = undefined;\ var socket = undefined;\
var tm;\
\ \
function connect() {\ function connect() {\
socket = new WebSocket(\"ws://\" + host + \"/ws\");\ socket = new WebSocket(\"ws://\" + host + \"/ws\");\
@ -440,9 +457,7 @@ static const char mgtWebsite[] PROGMEM = "<!DOCTYPE html>\
\ \
socket.onclose = function (e) {\ socket.onclose = function (e) {\
console.log('Socket is closed. Reconnect will be attempted in 1 second.', e.reason);\ console.log('Socket is closed. Reconnect will be attempted in 1 second.', e.reason);\
toastr.error('Die Websocket Verbindung wurde vom Tonuino unterbrochen.');\
socket = null;\ socket = null;\
\
setTimeout(function () {\ setTimeout(function () {\
connect();\ connect();\
}, 5000);\ }, 5000);\
@ -450,8 +465,6 @@ static const char mgtWebsite[] PROGMEM = "<!DOCTYPE html>\
\ \
socket.onerror = function (err) {\ socket.onerror = function (err) {\
console.error('Socket encountered error: ', err.message, 'Closing socket');\ console.error('Socket encountered error: ', err.message, 'Closing socket');\
toastr.error('Es gab einen Fehler bei der Websocket Verbindung');\
socket.close();\
};\ };\
\ \
socket.onmessage = function(event) {\ socket.onmessage = function(event) {\
@ -465,24 +478,25 @@ static const char mgtWebsite[] PROGMEM = "<!DOCTYPE html>\
$(\"#rfidIdMusic\").effect(\"highlight\", {color:\"#abf5af\"}, 3000);\ $(\"#rfidIdMusic\").effect(\"highlight\", {color:\"#abf5af\"}, 3000);\
$(\"#rfidIdMod\").effect(\"highlight\", {color:\"#abf5af\"}, 3000);\ $(\"#rfidIdMod\").effect(\"highlight\", {color:\"#abf5af\"}, 3000);\
\ \
} if (socketMsg.status != null) {\
if (socketMsg.status == 'ok') {\
} if (\"status\" in socketMsg) {\
if (socketMsg.status == \"ok\") {\
toastr.success(\"Aktion erfolgreich ausgeführt.\" );\ toastr.success(\"Aktion erfolgreich ausgeführt.\" );\
} else {\
toastr.error(\"Es ist ein Fehler aufgetreten.\" );\
}\ }\
} if (socketMsg.pong != null) {\
} if (\"pong\" in socketMsg) {\
if (socketMsg.pong == 'pong') {\ if (socketMsg.pong == 'pong') {\
pong();\ pong();\
}\ }\
} if (\"refreshFileList\" in socketMsg){\ } if (\"refreshFileList\" in socketMsg){\
hideFileIndexingState();\
toastr.info(\"Die Datei Liste wurde neu erzeugt!\");\ toastr.info(\"Die Datei Liste wurde neu erzeugt!\");\
$('#filetree').jstree(true).refresh();\ $('#filetree').jstree(true).refresh();\
$(\"#indexing-progress\").hide();\
$(\"#refreshAction\").show();\
\
}\ }\
if (\"indexingState\" in socketMsg){\ if (\"indexingState\" in socketMsg){\
console.log(socketMsg.indexingState);\
if(socketMsg.indexingState != null) {\
$(\"#currentProcessedFile\").text(socketMsg.indexingState);\
console.log(socketMsg.indexingState);\
}\
}\ }\
};\ };\
}\ }\
@ -512,8 +526,7 @@ static const char mgtWebsite[] PROGMEM = "<!DOCTYPE html>\
var myJSON = JSON.stringify(myObj);\ var myJSON = JSON.stringify(myObj);\
$(\"#refreshAction\").hide();\ $(\"#refreshAction\").hide();\
socket.send(myJSON);\ socket.send(myJSON);\
$(\"#indexing-progress\").show();\
$(\"#refreshAction\").hide();\
showFileIndexingState();\
};\ };\
\ \
function genSettings(clickedId) {\ function genSettings(clickedId) {\

Loading…
Cancel
Save