NEWS
sendTo synchron verwenden
-
Liebe Community,
für ein paar mit dem SQL-Adapter geloggte Datenpunkte möchte ich den Mittelwert über eine definierte Zeitspanne berechnen. Das folgende Beispielscript berechnet die mittlere Anwesenheit (in %, wenn man meanval * 100 nimmt) während der letzten 48 Stunden und tut dies auch korrekt:
async function getMeanValFromSQL(datapoint, table, t_beg) { var Query = 'SELECT ts,val FROM iobroker.'+table+' WHERE id IN (SELECT id FROM iobroker.datapoints WHERE name="'+datapoint+'") and (ts>'+t_beg+' or ts = (SELECT MAX(ts) FROM iobroker.'+table+' WHERE id IN (SELECT id FROM iobroker.datapoints WHERE name="'+datapoint+'") AND ts <= '+t_beg+'))'; var t_end = Date.now(); var meanval = 0; sendTo('sql.0', 'query', Query, function (result) { if (result.error) { console.error(result.error); } else { // Berechne Mittelwert var rs = result.result; var timespan = t_end - t_beg; for(var i = 0; i<rs.length; i++) { var sec_cur = 0; if (i==0){ if(rs.lenght == 1){ sec_cur = t_end - t_beg; }else{ sec_cur = rs[i+1].ts - t_beg; } }else if(i == rs.length-1){ sec_cur = t_end - rs[i].ts; }else{ sec_cur = rs[i+1].ts - rs[i].ts; } meanval += rs[i].val*sec_cur/timespan; } log("Mittelwert nested: "+meanval); } } ); //await sleep(30) log("Mittelwert: "+meanval); } function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } log("Start"); getMeanValFromSQL("javascript.0.Eigene_Datenpunkte.Presence", "ts_bool", Date.now()-(2*24*60*60*1000)); log("Ende");Problem ist nur: Die (anonyme) callback-Funktion läuft natürlich asynchron. Deshalb kann die Hauptfunktion getMeanValFromSQL mit dem Mittelwert zunächst nicht weiterarbeiten, versucht das aber.
Logausgabe des obigen Scripts:
08:44:16.081 info javascript.0 (655) script.js.Test.DatabaseTest: Start 08:44:16.082 info javascript.0 (655) script.js.Test.DatabaseTest: Ende 08:44:16.082 info javascript.0 (655) script.js.Test.DatabaseTest: registered 0 subscriptions and 0 schedules 08:44:16.083 info javascript.0 (655) script.js.Test.DatabaseTest: Mittelwert: 0 08:44:16.131 info javascript.0 (655) script.js.Test.DatabaseTest: Mittelwert nested: 0.6609056365740742Wenn ich die sleep-Zeile (38) aktiviere, funktioniert es:
08:53:01.142 info javascript.0 (655) script.js.Test.DatabaseTest: Start 08:53:01.144 info javascript.0 (655) script.js.Test.DatabaseTest: Ende 08:53:01.144 info javascript.0 (655) script.js.Test.DatabaseTest: registered 0 subscriptions and 0 schedules 08:53:01.176 info javascript.0 (655) script.js.Test.DatabaseTest: Mittelwert nested: 0.6609056385364256 08:53:01.176 info javascript.0 (655) script.js.Test.DatabaseTest: Mittelwert: 0.6609056385364256Das ist natürlich keine Lösung, denn im Vorhinein weiß man nicht, wie lange die callback function benötigt, um ihr Ergebnis zu berechnen.
Deshalb möchte ich auf die Abarbeitung der callback function warten und dann in der Hauptfunktion weitermachen. Geht das irgendwie?
Einen Umweg über Datenpunkte will ich nicht gehen, um die Funktion universell und skalierbar zu halten (Später soll die Funktion aus einer Schleife heraus aufgerufen werden und Resultate für verschiedene Zeitspannen in ein Array speichern).
Viele Grüße und besten Dank
ceram
-
Liebe Community,
für ein paar mit dem SQL-Adapter geloggte Datenpunkte möchte ich den Mittelwert über eine definierte Zeitspanne berechnen. Das folgende Beispielscript berechnet die mittlere Anwesenheit (in %, wenn man meanval * 100 nimmt) während der letzten 48 Stunden und tut dies auch korrekt:
async function getMeanValFromSQL(datapoint, table, t_beg) { var Query = 'SELECT ts,val FROM iobroker.'+table+' WHERE id IN (SELECT id FROM iobroker.datapoints WHERE name="'+datapoint+'") and (ts>'+t_beg+' or ts = (SELECT MAX(ts) FROM iobroker.'+table+' WHERE id IN (SELECT id FROM iobroker.datapoints WHERE name="'+datapoint+'") AND ts <= '+t_beg+'))'; var t_end = Date.now(); var meanval = 0; sendTo('sql.0', 'query', Query, function (result) { if (result.error) { console.error(result.error); } else { // Berechne Mittelwert var rs = result.result; var timespan = t_end - t_beg; for(var i = 0; i<rs.length; i++) { var sec_cur = 0; if (i==0){ if(rs.lenght == 1){ sec_cur = t_end - t_beg; }else{ sec_cur = rs[i+1].ts - t_beg; } }else if(i == rs.length-1){ sec_cur = t_end - rs[i].ts; }else{ sec_cur = rs[i+1].ts - rs[i].ts; } meanval += rs[i].val*sec_cur/timespan; } log("Mittelwert nested: "+meanval); } } ); //await sleep(30) log("Mittelwert: "+meanval); } function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } log("Start"); getMeanValFromSQL("javascript.0.Eigene_Datenpunkte.Presence", "ts_bool", Date.now()-(2*24*60*60*1000)); log("Ende");Problem ist nur: Die (anonyme) callback-Funktion läuft natürlich asynchron. Deshalb kann die Hauptfunktion getMeanValFromSQL mit dem Mittelwert zunächst nicht weiterarbeiten, versucht das aber.
Logausgabe des obigen Scripts:
08:44:16.081 info javascript.0 (655) script.js.Test.DatabaseTest: Start 08:44:16.082 info javascript.0 (655) script.js.Test.DatabaseTest: Ende 08:44:16.082 info javascript.0 (655) script.js.Test.DatabaseTest: registered 0 subscriptions and 0 schedules 08:44:16.083 info javascript.0 (655) script.js.Test.DatabaseTest: Mittelwert: 0 08:44:16.131 info javascript.0 (655) script.js.Test.DatabaseTest: Mittelwert nested: 0.6609056365740742Wenn ich die sleep-Zeile (38) aktiviere, funktioniert es:
08:53:01.142 info javascript.0 (655) script.js.Test.DatabaseTest: Start 08:53:01.144 info javascript.0 (655) script.js.Test.DatabaseTest: Ende 08:53:01.144 info javascript.0 (655) script.js.Test.DatabaseTest: registered 0 subscriptions and 0 schedules 08:53:01.176 info javascript.0 (655) script.js.Test.DatabaseTest: Mittelwert nested: 0.6609056385364256 08:53:01.176 info javascript.0 (655) script.js.Test.DatabaseTest: Mittelwert: 0.6609056385364256Das ist natürlich keine Lösung, denn im Vorhinein weiß man nicht, wie lange die callback function benötigt, um ihr Ergebnis zu berechnen.
Deshalb möchte ich auf die Abarbeitung der callback function warten und dann in der Hauptfunktion weitermachen. Geht das irgendwie?
Einen Umweg über Datenpunkte will ich nicht gehen, um die Funktion universell und skalierbar zu halten (Später soll die Funktion aus einer Schleife heraus aufgerufen werden und Resultate für verschiedene Zeitspannen in ein Array speichern).
Viele Grüße und besten Dank
ceram
@ceram Du kannst grundsätzlich jede Funktion mit Callback promisifizieren, um sie anschließend per
awaitaufzurufen. Das Prinzip ist immer gleich, hier mal für deinen Fall:function sendToAsync(adapter, command, options) { return new Promise((resolve, reject) => { sendTo(adapter, command, options, result => { if (result.error) { reject(result.error); } else { resolve(result.result) } }); }); } // Nutzung in der aufrufenden Funktion: try { const result = await sendToAsync(...) // mit result weiterarbeiten } catch (e) { // Fehler behandeln }
Hey! Du scheinst an dieser Unterhaltung interessiert zu sein, hast aber noch kein Konto.
Hast du es satt, bei jedem Besuch durch die gleichen Beiträge zu scrollen? Wenn du dich für ein Konto anmeldest, kommst du immer genau dorthin zurück, wo du zuvor warst, und kannst dich über neue Antworten benachrichtigen lassen (entweder per E-Mail oder Push-Benachrichtigung). Du kannst auch Lesezeichen speichern und Beiträge positiv bewerten, um anderen Community-Mitgliedern deine Wertschätzung zu zeigen.
Mit deinem Input könnte dieser Beitrag noch besser werden 💗
Registrieren Anmelden