Loops bei FHEM2FHEM vermeiden

Begonnen von Adimarantis, 01 August 2021, 10:55:49

Vorheriges Thema - Nächstes Thema

Adimarantis

Hallo,

ich probiere jetzt schon eine Weile wie man am besten Readings auf zwei Instanzen spiegelt (man darf sie also auf beiden Instanzen ändern und bekommt das auf der jeweils anderen mit).
Egal welches Modul (FHEM2FHEM, RFHEM) man da nimmt, läuft man hier immer in Gefahr eine Schleife zu bauen.

Ich hätte da einen Vorschlag zu FHEM2FHEM, wie man Schleifen unterbindet und damit potentiell auch ein solches Mirroring machen könnte.
Grundprinzip: Änderungen innerhalb eines gewissen Zeitraums werden unterdrückt und damit Schleifen durchbrochen. Sofern der Anwendungsfall nicht erfordert Änderungen im Sekundentakt oder schneller durchzuführen, sollte dies für den Anwender keine Einschränkung bedeuten. Theoretisch reicht es, dies auf einer Instanz zu machen (dann kriegt man halt immer noch ein "Echo", was aber harmlos ist).

In meiner Bastelversion von FHEM2FHEM habe ich folgende Funktion hinzugefügt:
sub FHEM2FHEM_updateReading(@)
{
my ($name,$dev,$reading,$value,$trigger) = @_;

my $age = ReadingsAge($dev->{NAME},$reading,undef);
my $threshold = AttrVal($name,"loopThreshold",0);

#Only execute if last change is older than Threshold
if (!defined($age) || $age>$threshold) {
readingsSingleUpdate($dev,$reading,$value, $trigger);
} else {
Log3 $name, 4, "No update due to possible loop for $dev->{NAME} $reading $value (age:$age, threshold:$threshold)";
}
return undef;
}


Das neue Attribut "loopThreshold" wird dann z.B. auf 2 Sekunden gesetzt.
In der FHEM2FHEM_Read werden entsprechend die readingsSingleUpdate ersetzt durch:
FHEM2FHEM_updateReading($name,$defs{$rname}, $1, $2, 1);

Ich hänge das geänderte Modul mal an.

Speziell die Fragen an Rudi:
- Könntest du dir so eine Änderung vorstellen?
- Es gibt noch einen Fall im Code wo ein "AnalyzeCommand" angestossen wird - diesen berücksichtige ich jetzt nicht, verstehe aber auch nicht ganz wann dieser Zweig getriggert wird

Gruß,
Jörg
Raspberry 4 + HM-MOD-RPI-PCB (pivCCU) + RfxTrx433XL + 2xRaspberry 1
Module: 50_Signalbot, 52_I2C_ADS1x1x , 58_RPI_1Wire, (50_SPI_MAX31865)

Adimarantis

Bei weiteren Tests ist mir aufgefallen, das FHEM2FHEM bei zu großzügerer Regexp Definition auch versucht für andere Befehle als "set" ein ReadingsSingleUpdate zu triggern (z.B. wenn man den Devicenamen in ".*device.*" einbettet).
Um solche Querschläger gleich abzufangen würde es sich anbieten, Nachrichten von "global" mit
if ($dev->{NAME} eq "global") {
Log3 $name, 4, "Ignoring admistrative message $reading $value";
return undef;}


gleich zu ignorieren.

Jörg
Raspberry 4 + HM-MOD-RPI-PCB (pivCCU) + RfxTrx433XL + 2xRaspberry 1
Module: 50_Signalbot, 52_I2C_ADS1x1x , 58_RPI_1Wire, (50_SPI_MAX31865)

rudolfkoenig

Das mag zwar in vielen Faellen funktionieren, aber z.Bsp. dann nicht, wenn ein Event eine Verarbeitung ausloest, was laenger als loopThreshold dauert.  Auch das Echo bzw. dass man es auf allen Seiten konfigurieren muss, ist unschoen => muss noch drueber nachdenken. Gibt es noch Andere, die das gleiche Problem haben?

Generell faende ich eine Loesung ueber MQTT besser, evtl. koennte ich MQTT2_CLIENT dafuer aufruesten.
Vmtl. geht das jetzt schon mit MQTT2_GENERIC_DEVICE, irgendwie.

Zitat- Es gibt noch einen Fall im Code wo ein "AnalyzeCommand" angestossen wird - diesen berücksichtige ich jetzt nicht, verstehe aber auch nicht ganz wann dieser Zweig getriggert wird
Wenn man eventOnly und setState nicht gesetzt hat, remote ein set ausgeloest wird, und lokal ein Geraet mit dem gleichen Namen existiert.

Adimarantis

Hi Rudi,

Zitat von: rudolfkoenig am 01 August 2021, 18:31:37
Das mag zwar in vielen Faellen funktionieren, aber z.Bsp. dann nicht, wenn ein Event eine Verarbeitung ausloest, was laenger als loopThreshold dauert.

Du meinst wenn die Loop aufgrund von Verzögerungen so lange dauert, dass sie nicht mehr erkannt wird. Naja, dann hätte man erstmal nichts verloren, da dies ja das aktuelle Verhalten ist - natürlich ungünstig für den Fall dass man sich drauf verlässt um den bidirektionalen Fall zu implementieren. Hier sollte aber helfen dann auf beiden Seiten ein loopThreshold zu haben - das Prozessing hat man selten auf beiden Seiten.

Insgesamt hat da der Push-Ansatz von RFHEM durchaus seinen Charme, allerdings müsste man die Befehle immer durch eine Remote Instanz (von RFHEM) kanalisieren, würde damit aber auch die Möglichkeit haben remote Kommandos abzusetzen, wozu manche Anwender anscheinend RFHEM parallel zu FHEM2FHEM betreiben (der readings update Teil von RFHEM ist aber unfertig und fehlerhaft - daher bin ich davon wieder abgekommen - siehe meine Diskussion mit Otto https://forum.fhem.de/index.php/topic,23638.msg1168650.html#new )

Wenn MQTT das abdecken kann ist das sicher eine Option. Habe mich damit noch nicht beschäftigt da mich auch gleich die komplexere Konfiguration und Notwendigkeit einer "Drittinstanz" (auch wenn die wieder auf FHEM laufen kann) abgeschreckt hat.

Jörg
Raspberry 4 + HM-MOD-RPI-PCB (pivCCU) + RfxTrx433XL + 2xRaspberry 1
Module: 50_Signalbot, 52_I2C_ADS1x1x , 58_RPI_1Wire, (50_SPI_MAX31865)

Beta-User

Zitat von: rudolfkoenig am 01 August 2021, 18:31:37
Generell faende ich eine Loesung ueber MQTT besser, evtl. koennte ich MQTT2_CLIENT dafuer aufruesten.
Vmtl. geht das jetzt schon mit [...]
Soweit ich das bisher überblicke, braucht es wohl keine Erweiterung für MQTT2_CLIENT, die mit MQTT2-IO's kompatiblen Module einschl. MQTT_GENERIC_BRIDGE müßten eigentlich alles mitbringen, um das stressfrei und ohne loops umzusetzen.

Voraussetzung ist aber - wie üblich -, dass das sauber gemacht wird.

Ich habe auch noch nicht ganz verstanden, um was es eigentlich geht. Falls es
Zitat von: Otto123 am 30 Juli 2021, 22:03:36
Einfach "blind" jede Änderung von a nach b und von b nach a replizieren.
sein sollte: Das ist immer nicht ganz easy, man muss sich jeweils sehr genau anschauen, was wo steht.
Zitat von: Adimarantis am 01 August 2021, 20:54:31
Wenn MQTT das abdecken kann ist das sicher eine Option. Habe mich damit noch nicht beschäftigt da mich auch gleich die komplexere Konfiguration und Notwendigkeit einer "Drittinstanz" (auch wenn die wieder auf FHEM laufen kann) abgeschreckt hat.
Na ja, für MQTT braucht es halt einen Server, aber mit MQTT2_SERVER auf einer Instanz sollte es in den meisten Fällen zu bewerkstelligen sein, und "über Kreuz" sollte eigentlich alles nach dem folgenden "Bauprinzip" abbildbar sein:
((Reales Device + MQTT_GENERIC_BRIDGE-Attribute) + (MQTT(2)-IO + MQTT_GENERIC_BRIDGE)) <= MQTT-Protokoll => (MQTT(2)-IO + MQTT2_DEVICE (ggf. via bridgeRegexp))

Das erfordert etwas Einarbeitung, aber dann läßt sich damit m.E. eigentlich recht einfach eine "eventsparende" und loopfreie Lösung basteln.

(Einen großen Bogen sollte man nur um diese "unselige" "SYS_MQTT-notify"-"Lösung" machen, die ihrerseits wieder unspezifische Events generiert...).

Demgegenüber sehe ich eine "timerbasiert aussortierende Pauschallösung" eher skeptisch (falls ich das vorgeschlagene Prinzip überhaupt korrekt erfasst haben sollte).
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

rudolfkoenig

Ich habe loopThreshold jetzt eingebaut, auch wenn das nicht perfekt ist, vielleicht hilft es jemandem.
Insb. in Zusammenhang mit den event-on-* Attributen sollte man aufpassen.

Adimarantis

Hallo Rudi,

Ich habe noch ein kleines Problem gefunden:
Wenn ein Reading nicht existiert, dann nimmst du es als "0" an, womit es immer ignoriert wird, wenn man ein threshold gesetzt hat.
D.h. man muss es erstmal mit "setreading" anlegen.
Habe gerade eine ganze Weile gesucht, warum ein neu angelegtes Device keine Werte bekommen hat.

Jörg
Raspberry 4 + HM-MOD-RPI-PCB (pivCCU) + RfxTrx433XL + 2xRaspberry 1
Module: 50_Signalbot, 52_I2C_ADS1x1x , 58_RPI_1Wire, (50_SPI_MAX31865)

rudolfkoenig

Hab was eingebaut, aber nur oberflaechlich getestet, kannst Du es bitte auch testen und Feedback geben?

Adimarantis

Hi Rudi,

ja das mit dem "undef" als Default klappt jetzt. Testweise neues Reading angelegt und das wurde anstandslos übernommen (und auch der Rest mit und ohne Thresholdunterschreitung funktioniert wie erwartet).

Danke & Gruß
Jörg
Raspberry 4 + HM-MOD-RPI-PCB (pivCCU) + RfxTrx433XL + 2xRaspberry 1
Module: 50_Signalbot, 52_I2C_ADS1x1x , 58_RPI_1Wire, (50_SPI_MAX31865)