Moin,
ich bekomme seit einiger Zeit nach einem set dblog userCommand falsche Infos in userCommandResult. Der Command selbst wird einwandfrei ausgeführt. Aufgefallen ist es mir bei "insert into..." und "delete from..." Befehlen.
Hier mal das list von meinem logdb - Befehl und Result sind dort zu sehen:
Internals:
COLUMNS field length used for Device: 64, Type: 64, Event: 512, Reading: 64, Value: 128, Unit: 32
CONFIGURATION ./db.conf
DEF ./db.conf .*:.*
MODE synchronous
MODEL MYSQL
NAME logdb
NR 44
NTFY_ORDER 50-logdb
PID 10999
REGEXP .*:.*
STATE connected
TYPE DbLog
UTF8 0
VERSION 3.12.5
dbconn mysql:database=fhem;host=localhost;port=3306
dbuser pi
HELPER:
COLSET 1
DEVICECOL 64
EVENTCOL 512
OLDSTATE connected
READINGCOL 64
TYPECOL 64
UNITCOL 32
VALUECOL 128
READINGS:
2018-10-22 03:00:00 countCurrent 51
2018-10-22 03:00:00 countHistory 49200
2018-05-03 22:52:33 lastRowsDeleted 65913
2018-10-22 10:58:05 state connected
2018-10-22 10:58:05 userCommand insert into history (DEVICE, TYPE, EVENT, READING, VALUE, UNIT)
values ('bmw_mqtt_0001', 'MQTT_DEVICE', 'definition', 'DEF', 'leer', 'leer')
2018-10-22 10:58:05 userCommandResult DBD::mysql::db selectrow_array failed: fetch() without execute() at ./FHEM/93_DbLog.pm line 865.
cache:
index 0
Attributes:
DbLogSelectionMode Include
DbLogType Current/History
comment MySql - Datenbank Nutzung: Zur Sicherung von einzelnen Device- oder Zustandswerten
devStateIcon connected:general_ok .*:message_attention@red
event-on-change-reading countHistory
event-on-update-reading state,userCommandResult,userCommand
group MySQL
room -System
suppressAddLogV3 0
verbose 3
Habt ihr ne Idee was falsch sein könnte ?
Moin und schon mal vielen Dank für Eure Hilfe !
Bernd
Hallo Bernd,
du machst nichts falsch. Der Aufbau des userCommand im DbLog ist sehr einfach aufgebaut.
Bei Selects würde der ermittelte Wert zurück geliefert werden.
Verwende für deine Aufgabe besser das sqlCmd im DbRep. Das ist mehr ausgebaut und non-blocking.
Grüße,
Heiko
Moin Heiko,
danke für Deine Hilfe !! - wie immer: schnell und kompetent :)
Ich habe DbRep installiert und musste mich etwas einlesen und testen. Das ist ja wirklich ein mächtiges Modul.
Funktioiert alles - set <name> sqlCmd
insert into history (DEVICE, TYPE, EVENT, READING, VALUE, UNIT) values ('bmw_mqtt_0001', 'MQTT_DEVICE', 'definition', 'DEF', 'DbRep', 'hallo'
select MAX(TIMESTAMP) from history where device='bmw_mqtt_0001' and READING='DEF';
delete from history where device = 'bmw_mqtt_0001' and UNIT = 'hallo';
update history set UNIT='neu' where DEVICE='bmw_mqtt_0001' and TIMESTAMP = '2018-10-22 13:35:02';
Jetzt habe ich nur noch das Problem mit den Return-Werten, da die Befehle ja non-blocking ablaufen. Ich muss irgenwie auf die Return-Werte warten, ohne das ganze fhem lahm zu legen bzw. kurz per delay zu blockieren.
DANKE und moin
Bernd
Moin Bernd,
danke und freut mich dass du nun dein Ziel erreichen kannst. :D
Ja, ich gebe zu dass DbRep anfangs etwas gewöhnungsbedürftig ist. Aber es ist eben ein Datenbank-Frontend was
etwas anders als herkömmliche Module funktioniert.
Zitat
Jetzt habe ich nur noch das Problem mit den Return-Werten, da die Befehle ja non-blocking ablaufen. Ich muss irgenwie auf die Return-Werte warten, ohne das ganze fhem lahm zu legen bzw. kurz per delay zu blockieren.
Blockieren ist nicht nötig bzw. macht die non-blocking Vorteile ja zunichte. Um die Returnwerte in die eigenen Abläufe sequentiell einzubinden gibt es eine spezielle Schnittstelle die man mit dem Attribut "userExitFn" aktiviert.
Jetzt weiß ich nicht inwieweit du in der Perl-Programmierung drin steckst, aber wenn du mir genauer erläuterst wie du die Return-Werte weiter verarbeiten willst, können wir das ja mal zusammen machen und daraus eventuell gleich einen Wiki-Eintrag erstellen.
Nur heute Abend nicht mehr ;)
EDIT: Der Vollständigkeit halber ... es gibt noch das Kommando "get ... dbValue" welches es erlaubt wie sqlCmd benutzerspezifische SQL's abzuarbeiten, aber grundsätzlich
blockierend arbeitet. Du kannst also wählen was für deinen speziellen Anwendungsfall die beste Möglichkeit ist.
Grüße,
Heiko
Moin Heiko,
tja, also grundsätzlich möchte ich die Datenbank völlig unabhängig von irgentwelchen Devices oder Readings etc. in meiner 99_Utils nutzen. Ich denke, dass bei der hohen Flexibilität von fhem auch so ein flexibles, für individuelle Anforderungen nutzbares, DB-Tool ganz nützlich wäre und innovativ genutzt werden könnte.
Klar, entstanden sind DbLog und DbRep primär für das Logging und für Auswertungen.
Anstoss waren diese HM-LC-Bl1PBU-FM, die bei PowerOn beharrlich auf 50% stehen und sich die tatsächliche Lage der Jalousien angeblich nicht merken können und ggf. sogar noch Justierfahrten machen (da sollte eQ3 mal nachbessern). Wenn das nachts passiert gibt es Ärger mit der besten Ehefrau von allen :'( und ich finde das auch nicht lustig.
Oder der Heizungshauptschalter, der nach Stromausfall auf off geht, weil er nix anderes kann und damit wird die Bude kalt.
Oder ein harter Crash von fhem (das Problem befindet sich ja meist vor der Tastatur) und beim Restart sind dann alle Einstellungen
(active | inactive) der notify, at, dummy und Co und auch alle OldreadingsVal weg.
And so on..
Jedenfalls habe ich mal mein Konzept für ein PowerOn Recovery auf die DB gestützt, um möglichst viele verschiedene Ausfälle für möglichst viele Device-Typen absichern zu können.
Ich glaube, dass die DB einen Crash besser überlebt... vielleicht irre ich mich, ist aber bisher meine Erfahrung.
Grober Ablauf:
1. bei den fraglichen Devices wir per dbLogInclude je nach Typ der aktuelle state, pct oder level gespeichert
2. das Reading powerOn eines beliebigen Devices ruft per notify eine sub in meiner 99_Utils auf
3. in der sub suche ich in der Db mit select MAX(TIMESTAMP) den Timestamp des letzten powerOn (könnte man auch aus dem Reading lesen...)
4. in der sub suche ich in der Db den vorletzten Wert pct mit select VALUE from history where device='xxx' and READING='pct' and TIMESTAMP='...kleiner powerOn-time...';
5. dann in der sub: set device mit diesem vorletzten Wert für pct oder state oder was auch immer.
Dazu brauche ich natürlich jeweils direkt die Ergebnisse der Datenbankbefehle, zum Beispiel den Timestamp oder den Wert des Readings.
ZitatJetzt weiß ich nicht inwieweit du in der Perl-Programmierung drin steckst,
Meine Programmierkenntnisse stützen sich auf Kenntnisse aus längst vergangenen Zeiten.
Bin ein IT-Oldtimer :'( und damit ein Linux-Perl-Depp. Aber ich arbeite daran
ZitatDer Vollständigkeit halber ... es gibt noch das Kommando "get ... dbValue" welches es erlaubt wie sqlCmd benutzerspezifische SQL's abzuarbeiten, aber grundsätzlich blockierend arbeitet.
Ich habe jetzt viel getestet und das get dbValue scheint die Lösung zu sein !! Hier mal ein Auzug aus der Testroutine - geht super !
# MariaDB is the name of my DbRep-Device
#
# return_codes from CommandGet :
# operation OK -> return_code = number of affected lines
# operation NOK -> return_code empty
#
$return_code = CommandGet(undef,"MariaDB dbValue insert into history (DEVICE, TYPE, EVENT, READING, VALUE, UNIT)
values ('$device', '$model', 'definition', 'DEF', 'Hugo', 'Emma')");
Log3 $name, 5, "$name $sub_name: insert = $return_code";
$return_code = CommandGet(undef,"MariaDB dbValue select VALUE from history where device='$device' and READING='DEF' LIMIT 1;");
Log3 $name, 5, "$name $sub_name: select value = $return_code";
$return_code = CommandGet(undef,"MariaDB dbValue update history set UNIT='neu' where DEVICE='$device' and TIMESTAMP = '2018-10-23 23:18:37'");
Log3 $name, 5, "$name $sub_name: update = $return_code";
$return_code = CommandGet(undef,"MariaDB dbValue delete from history where device = '$device' and UNIT = 'Emma'; ");
Log3 $name, 5, "$name $sub_name: delete = $return_code";
Jetzt muss ich nur noch den alten Code umschreiben.
Schwachstelle ist im Konzept die Nummer 2, weil das Reading powerOn nicht zuverlässig gesetzt wird.
Danke für Deine Hilfe und Deine Anregungen !!!
Moin
Bernd
Moin Bernd,
na das hört sich doch schon gut an :)
Wenn ich morgen dazu komme und daran denke, gebe ich dir noch ein Beispiel wie man eine solche Verkettung mit dem non-blocking sqlCmd und einer 99_myUtils machen kann, also auch völlig unabhängig von anderen Devices.
Bin übrigens selbst auch so ein IT-Oldie und habe mir die Perl-Programmierung selbst beigebracht ;)
Lerne auch ständig immer wieder neu dazu.
LG,
Heiko
Hallo Bernd,
hier wie versprochen ein Beispiel einer Chain mit dem non-blocking setCmd.
Lege dir zunächst 4 identische DbRep-Devices an. Sie unterscheiden sich nur durch den Namen.
Einfach eins anlegen und dann nur noch kopieren (das DbLog-Device heißt im Beispiel "LogDB").
Die vier DbRep heißen:
Rep.insert, Rep.select, Rep.update, Rep.delete
define Rep.insert DbRep LogDB
attr Rep.insert allowDeletion 1
attr Rep.insert showproctime 1
attr Rep.insert userExitFn chain
In deine 99_myUtils fügst du dann diese Testroutine ein:
#############################################################################################
# sqlCmd Commandchain zur sequentiellen Abarbeitung von non-blocking DbRep-Befehlen
#
# genutzte werden vier DbRep-Devices: Rep.insert, Rep.select, Rep.update, Rep.delete
#
# Start der Chain im FHEMWEB mit: {chain("Rep.insert", "chainstart")}
#############################################################################################
sub chain {
my ($name,$reading,$value) = @_;
my $hash = $defs{$name};
my $device = "Testdevice";
my $model = "Testmodel";
my $rc;
if ($name eq "Rep.insert" && $reading eq "chainstart") {
# Start der 1. Operation (insert)
CommandSet(undef,"Rep.insert sqlCmd insert into history (DEVICE, TYPE, EVENT, READING, VALUE, UNIT)
values (\"$device\", \"$model\", \"definition\", \"DEF\", \"Hugo\", \"Emma\")");
Log3 $name, 1, "$name - Start chain with insert";
return;
}
if($reading eq "state" && $value eq "done") {
if ($name eq "Rep.insert") {
# Auswertung der 1. Operation (insert)
$rc = ReadingsVal($name, "SqlResultRow_1", "");
Log3 $name, 1, "$name - number of inserts: $rc";
# Start der 2. Operation (select)
CommandSet(undef,"Rep.select sqlCmd select VALUE from history where device=\"$device\" and READING=\"DEF\" LIMIT 1;");
}
if ($name eq "Rep.select") {
# Auswertung der 2. Operation (select)
$rc = ReadingsVal($name, "SqlResultRow_1", "");
Log3 $name, 1, "$name - return of select: $rc";
# Start der 3. Operation (update)
CommandSet(undef,"Rep.update sqlCmd update history set UNIT=\"neu\" where DEVICE=\"$device\";");
}
if ($name eq "Rep.update") {
# Auswertung der 3. Operation (update)
$rc = ReadingsVal($name, "SqlResultRow_1", "");
Log3 $name, 1, "$name - number of updates: $rc";
# Start der 4. Operation (delete)
CommandSet(undef,"Rep.delete sqlCmd delete from history where device = \"$device\" and UNIT = \"neu\";");
}
if ($name eq "Rep.delete") {
# Auswertung der 4. Operation (delete)
$rc = ReadingsVal($name, "SqlResultRow_1", "");
Log3 $name, 1, "$name - number of deletes: $rc";
}
}
return;
}
Die ganze Kette wird einfach mit dem Befehl gestartet:
chain("Rep.insert", "chainstart")
bzw. in der Kommandozeile im FHEMWEB mit:
{chain("Rep.insert", "chainstart")}
Im Log siehst du dann die Ergebnisse der Abarbeitungskette:
2018.10.24 23:57:37.602 1: Rep.insert - Start chain with insert
2018.10.24 23:57:37.815 1: Rep.insert - number of inserts: 1
2018.10.24 23:57:37.901 1: Rep.select - return of select: Hugo
2018.10.24 23:57:38.033 1: Rep.update - number of updates: 1
2018.10.24 23:57:38.176 1: Rep.delete - number of deletes: 1
Dieser Aufbau hat den Vorteil, dass dein FHEM nicht negativ beeinflusst wird wenn deine Datenbank mal nicht so schnell reagiert, oder nicht verfügbar ist (zum Beispiel wegen laufenden Backup o.ä.).
Grüße
Heiko
Moin Heiko,
ah, noch 'ne Nachteule :)
Ich glaube, dass ich das Pinzip vertanden habe, very tricky Dein Ablauf !! Die sub nach einer DB-Anforderung beenden, über den userExitFn mit Ergebnis wieder neu starten und mit den if dann den richtigen Entrypoint in der sub ansteuern.
Werde ich in Ruhe durchdenken - heute nicht mehr, bin müde... Schade, hab die nächsten Tage viel auf dem Zettel und ich kann mich wohl erst in einigen Tagen wieder melden.
Nochmals Danke ! und moin
Bernd
Habe soeben aus diesem Beispiel eine Wiki-Beitrag erstellt:
https://wiki.fhem.de/wiki/DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten#Abarbeitung_einer_sequentiellen_Befehlskette_mittels_non-blocking_sqlCmd-Kommandos
Grüße
Heiko