Autor Thema: ACK Verständnisfrage  (Gelesen 3887 mal)

Offline KarlHeinz2000

  • Full Member
  • ***
  • Beiträge: 183
ACK Verständnisfrage
« am: 30 September 2020, 15:16:27 »
Ich habe einen kleinen RS485 Aufbau mit GW und einem Node und teste das ACK, um meine Übertragung zuverlässiger zu machen.
Dabei ist mir aufgefallen, dass im Falle eines fehlenden ACK das heartbeat reading auf NACK gesetzt wird und nicht der state. Soll das so sein?
Heartbeat wird auch nur durch ein gesendetes Heartbeat wieder auf alive gesetzt. Ein erfolgreiches ACK bewirkt das nicht.
Wie wäre der angedachte Ablauf? Ich sende Daten zum Node mit ACK aktiv und warte dann, ob das entsprechende Reading innerhalb einer Zeit auf den neuen Wert geht? Wenn nicht-> nicht zugestellt. Oder wertet man das heartbeat reading bzgl NACK aus? Aber wie?

Offline Beta-User

  • Moderator
  • Hero Member
  • ***
  • Beiträge: 13683
  • "Developer"?!? Meistens doch eher "User"
Antw:ACK Verständnisfrage
« Antwort #1 am: 01 Oktober 2020, 08:08:23 »
Also, die story war die, dass heartbeat ursprünglich mal in state stattgefunden hat, weil das (scheinbar) gar nicht genutzt wurde.
Irgendwie war das aber nicht der richtige Ort, daher ist es insgesamt eben in ein eigenes Reading "heartbeat" gewandert, was dann nicht ganz optimal ist, wenn man es mit "state"-settern zu tun hat (also z.B. einem Relay, das man per setCommands nach "state" geschoben hat). Sowas ist aber die ziemliche Ausnahme, was man schon daran sieht, dass du nach Jahren der erste bist, der darüber grübelt ;D ...

Im Prinzip könnte man "dead" etc. ja auch wieder zurücksetzen, wenn "irgendwas" von der Node kommt. Das war mal in der Diskussion, hätte aber eine deutliche Erhöhung der Systemlast zur Folge gehabt, daher hatte ich das damals nicht umgesetzt. Du hat jetzt aber m.E. berechtigt einen "Sonderfall" aufgezeigt, werde mal schauen, wie man Antworten auf "Ack"-Anforderungen an der Stelle anders behandeln kann.

(Btw.: eine heartbeat-Message sollte nach meinem Codeverständnis eigentlich ein NACK nicht zurücksetzen, sondern erst eine zeitgerecht bestätigte (andere) ACK-Anforderung. Wenn wir das aber in der Ack-Verarbeitung berücksichtigen, kann das m.E. auch so bleiben, alles andere wäre unnötiger overhead.
Wobei man von ACK im engeren Sinne eigentlich nur noch bei nRF24 sprechen sollte, im MyS-Node-Code nennt sich das seit einiger Zeit bei anderen Transport-Layern "echo").
Server: HP-T620@Debian 10, aktuelles FHEM + ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | BT@OpenMQTTGateway
svn:MySensors, WeekdayTimer, RandomTimer, Twilight,  AttrTemplate => {mqtt2, mysensors, zwave}

Offline Beta-User

  • Moderator
  • Hero Member
  • ***
  • Beiträge: 13683
  • "Developer"?!? Meistens doch eher "User"
Antw:ACK Verständnisfrage
« Antwort #2 am: 01 Oktober 2020, 10:07:51 »
Mags du mal zum testen diese Routine in 10_MYSENSORS_DEVICE.pm tauschen:

sub onSetMessage {
    my $hash = shift;
    my $msg  = shift // return;
    my $name = $hash->{NAME};
    if (defined $msg->{payload}) {
      eval {
        my ($reading,$value) = rawToMappedReading($hash,$msg->{subType},$msg->{childId},$msg->{payload});
        readingsBeginUpdate($hash);
        readingsBulkUpdate($hash, $reading, $value);
        if ( defined ($hash->{setcommands}->{$value}) && $hash->{setcommands}->{$value}->{var} eq $reading ) { #$msg->{childId}
           if ($hash->{SetExtensionsCommand} && AttrVal($name, "setExtensionsEvent", undef)) {
             readingsBulkUpdate($hash,"state",$hash->{SetExtensionsCommand}) ; 
           } else {
             readingsBulkUpdate($hash,"state","$value");
             SetExtensionsCancel($hash) if !$msg->{ack};
           }
         }
      };
      readingsBulkUpdate($hash,"heartbeat","alive") if $msg->{ack} && ReadingsVal($hash,"heartbeat","dead") eq "NACK" and @{$hash->{IODev}->{messagesForRadioId}->{$hash->{radioId}}->{messages}} == 0;
      readingsEndUpdate( $hash, 1 );
      Log3 ($hash->{NAME}, 4, "MYSENSORS_DEVICE $hash->{NAME}: ignoring C_SET-message ".GP_Catch($@)) if $@;
    } else {
      Log3 ($hash->{NAME}, 5, "MYSENSORS_DEVICE $hash->{NAME}: ignoring C_SET-message without payload");
    }
    return;
}
Server: HP-T620@Debian 10, aktuelles FHEM + ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | BT@OpenMQTTGateway
svn:MySensors, WeekdayTimer, RandomTimer, Twilight,  AttrTemplate => {mqtt2, mysensors, zwave}

Offline KarlHeinz2000

  • Full Member
  • ***
  • Beiträge: 183
Antw:ACK Verständnisfrage
« Antwort #3 am: 01 Oktober 2020, 10:09:16 »
Alles klar. Mache ich heute Abend.
Danke.

Offline KarlHeinz2000

  • Full Member
  • ***
  • Beiträge: 183
Antw:ACK Verständnisfrage
« Antwort #4 am: 01 Oktober 2020, 17:16:05 »
Habe das kurz getestet. Keine Änderung. :(
Nach einem fehlenden ACK bleibt das heartbeat reading auf NACK. Egal ob die ACKs später wieder kommen. Auch einreboot des Node ändert nichts. Nur ein gesendeter heartbeat setzt das reading auf alive. Der Übergang von dead auf NACK funktioniert.

PS:
das requestAck Attribut lässt sich nur mit deleteattr löschen. Das Ändern über das drop down Feld geht nicht (es wird immer 1 angezeigt)

Offline Beta-User

  • Moderator
  • Hero Member
  • ***
  • Beiträge: 13683
  • "Developer"?!? Meistens doch eher "User"
Antw:ACK Verständnisfrage
« Antwort #5 am: 01 Oktober 2020, 17:34:02 »
Grummel, da war ich wohl etwas verpeilt, was das Ziel der ReadingsVal-Abfrage anging...

So sollte der Teil besser sein:
sub onSetMessage {
    my $hash = shift;
    my $msg  = shift // return;
    my $name = $hash->{NAME};
    if (defined $msg->{payload}) {
      eval {
        my ($reading,$value) = rawToMappedReading($hash,$msg->{subType},$msg->{childId},$msg->{payload});
        readingsBeginUpdate($hash);
        readingsBulkUpdate($hash, $reading, $value);
        if ( defined ($hash->{setcommands}->{$value}) && $hash->{setcommands}->{$value}->{var} eq $reading ) { #$msg->{childId}
           if ($hash->{SetExtensionsCommand} && AttrVal($name, "setExtensionsEvent", undef)) {
             readingsBulkUpdate($hash,"state",$hash->{SetExtensionsCommand}) ; 
           } else {
             readingsBulkUpdate($hash,"state","$value");
             SetExtensionsCancel($hash) if !$msg->{ack};
           }
         }
      };
      readingsBulkUpdate($hash,"heartbeat","alive") if $msg->{ack} && ReadingsVal($name,"heartbeat","dead") eq "NACK" and @{$hash->{IODev}->{messagesForRadioId}->{$hash->{radioId}}->{messages}} == 0;
      readingsEndUpdate( $hash, 1 );
      Log3 ($hash, 4, "MYSENSORS_DEVICE $name: ignoring C_SET-message ".GP_Catch($@)) if $@;
    } else {
      Log3 ($hash, 5, "MYSENSORS_DEVICE $name: ignoring C_SET-message without payload");
    }
    return;
}

Dafür finde ich es jetzt ganz und gar nicht mehr einleuchtend, dass eine heartbeat-Message das auf alive setzt, obwohl ggf. noch nicht alles zugestellt ist, was mit ACK-Anforderung versendet wurde (bzw. wiederholt dann auch versendet wird)... Da ist wohl auch noch was ändern ab Zeile 947:    if ($type == I_HEARTBEAT_RESPONSE) {
        readingsSingleUpdate($hash, "heartbeat", "alive",1) if !ReadingsVal($name,"heartbeat","alive") eq "NACK";

Ad PS:
Das mit dem "Wahr/Falsch"-Attribut ist auch in vielen anderen Fällen so, dass man das nur (auf 1) Setzen oder Löschen kann.
EDIT sagt: Es gibt jedenfalls nach meinem Verständnis sogar komische Ergebnisse, wenn man das via Kommandozeile auf "0" setzt, bitte mal ein list ansehen... (Wie gesagt, mit diesem Verhalten dürfte MYSENSORS_DEVICE kein Einzelfall sein ;) .)
« Letzte Änderung: 01 Oktober 2020, 17:52:38 von Beta-User »
Server: HP-T620@Debian 10, aktuelles FHEM + ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | BT@OpenMQTTGateway
svn:MySensors, WeekdayTimer, RandomTimer, Twilight,  AttrTemplate => {mqtt2, mysensors, zwave}

Offline KarlHeinz2000

  • Full Member
  • ***
  • Beiträge: 183
Antw:ACK Verständnisfrage
« Antwort #6 am: 01 Oktober 2020, 17:59:06 »
Jetzt funktioniert das ACK in der Art, dass das heartbeat reading nach Ablauf der timeoutAck Zeit auf NACK geht. Sobald die Nachricht zugestellt wurde geht es wieder auf alive. Passt, denke ich.
Allerdings ist das Alive jetzt ohne Funktion. Es gibt kein dead mehr. Das heartbeat reading bleibt immer auf alive. Nur wenn timeoutAlive geändert wird wird dead aktiv.

Offline Beta-User

  • Moderator
  • Hero Member
  • ***
  • Beiträge: 13683
  • "Developer"?!? Meistens doch eher "User"
Antw:ACK Verständnisfrage
« Antwort #7 am: 01 Oktober 2020, 18:10:25 »
Hmm, vielleicht sollte man die Abfrage einfacher schreiben:
     readingsSingleUpdate($hash, "heartbeat", "alive",1) if ReadingsVal($name,"heartbeat","alive") ne "NACK";
Danke für's Testen und Mitdenken!
Server: HP-T620@Debian 10, aktuelles FHEM + ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | BT@OpenMQTTGateway
svn:MySensors, WeekdayTimer, RandomTimer, Twilight,  AttrTemplate => {mqtt2, mysensors, zwave}

Offline KarlHeinz2000

  • Full Member
  • ***
  • Beiträge: 183
Antw:ACK Verständnisfrage
« Antwort #8 am: 01 Oktober 2020, 20:00:24 »
geht immer noch nicht. Das reading wechselt nicht von alive nach dead.

Offline Beta-User

  • Moderator
  • Hero Member
  • ***
  • Beiträge: 13683
  • "Developer"?!? Meistens doch eher "User"
Antw:ACK Verständnisfrage
« Antwort #9 am: 02 Oktober 2020, 08:17:08 »
Vermutlich habe ich was noch nicht richtig verstanden. Was genau meinst du mit
Allerdings ist das Alive jetzt ohne Funktion. Es gibt kein dead mehr. Das heartbeat reading bleibt immer auf alive. Nur wenn timeoutAlive geändert wird wird dead aktiv.

Grundsätzlich ist "dead" nur erreichbar, wenn timeoutAlive gesetzt ist. Du schreibst aber, dass das timeoutAlive geändert werden müßte. Dann war das gesetzt, allerdings muss der Timer wieder aktiviert werden, daher nochmaliges setzen.
(Dann hat das aber eher was mit der Änderung in onSetMessage() zu tun und nichts mit der  I_HEARTBEAT_RESPONSE-Geschichte? Das Verhalten müsste dann (fast) gleich sein, egal, was dort passiert... Grübel, werde mir das mit den Timern nochmal ansehen müssen, aber eine Klarstellung wäre ggf. hilfreich).
Server: HP-T620@Debian 10, aktuelles FHEM + ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | BT@OpenMQTTGateway
svn:MySensors, WeekdayTimer, RandomTimer, Twilight,  AttrTemplate => {mqtt2, mysensors, zwave}

Offline KarlHeinz2000

  • Full Member
  • ***
  • Beiträge: 183
Antw:ACK Verständnisfrage
« Antwort #10 am: 02 Oktober 2020, 09:22:30 »
Ich versuche mal das Verhalten zu beschreiben:

1)
requestAck 0
timeoutAlive 3

dead/alive funktioniert. Nach Ablauf timeoutAlive geht reading auf dead
- gesendetes heartbeat vom node: dead -> alive (->dead)
- ändern der timeoutAlive: dead->alive(->dead)

2)
requestAck 1
timeoutAck 2
timeoutAlive 3

RS485 Verbindung händisch unterbrochen.

Senden-> Reading geht auf NACK nach Ablauf der timeoutAck.
Ist die Verbindung wieder hergestellt, kommt nach einigen Sek ein erneuter/automatischer Sendeversuch. Dieser gelingt. Das reading wird von NACK->alive gesetzt (retry von FHEM oder GW?).
Es bleibt dann alive, außer:
- Wenn ein heartbeat vom node kommt, läuft die Zeit erneut ab und dead wird gesetzt.
- Die timeoutAlive Zeit wird geändert
- Übertragung ist gestört ->NACK


Offline Beta-User

  • Moderator
  • Hero Member
  • ***
  • Beiträge: 13683
  • "Developer"?!? Meistens doch eher "User"
Antw:ACK Verständnisfrage
« Antwort #11 am: 02 Oktober 2020, 09:34:13 »
OK, dann muss der Timer auch an der Stelle wieder in Gang gesetzt werden. Ergo sollte das hier helfen:

sub onSetMessage {
    my $hash = shift;
    my $msg  = shift // return;
    my $name = $hash->{NAME};
    if (defined $msg->{payload}) {
      readingsBeginUpdate($hash);
      eval {
        my ($reading,$value) = rawToMappedReading($hash,$msg->{subType},$msg->{childId},$msg->{payload});
     
        readingsBulkUpdate($hash, $reading, $value);
        if ( defined ($hash->{setcommands}->{$value}) && $hash->{setcommands}->{$value}->{var} eq $reading ) { #$msg->{childId}
           if ($hash->{SetExtensionsCommand} && AttrVal($name, "setExtensionsEvent", undef)) {
             readingsBulkUpdate($hash,"state",$hash->{SetExtensionsCommand}) ; 
           } else {
             readingsBulkUpdate($hash,"state","$value");
             SetExtensionsCancel($hash) if !$msg->{ack};
           }
         }
      };
      if ($msg->{ack} && ReadingsVal($name,"heartbeat","dead") eq "NACK" and @{$hash->{IODev}->{messagesForRadioId}->{$hash->{radioId}}->{messages}} == 0) {
         readingsBulkUpdate($hash,"heartbeat","alive") ;
         refreshInternalMySTimer($hash,"Alive") if $hash->{timeoutAlive};
      }
      readingsEndUpdate( $hash, 1 );
      Log3 ($hash, 4, "MYSENSORS_DEVICE $name: ignoring C_SET-message ".GP_Catch($@)) if $@;
    } else {
      Log3 ($hash, 5, "MYSENSORS_DEVICE $name: ignoring C_SET-message without payload");
    }
    return;
}
Server: HP-T620@Debian 10, aktuelles FHEM + ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | BT@OpenMQTTGateway
svn:MySensors, WeekdayTimer, RandomTimer, Twilight,  AttrTemplate => {mqtt2, mysensors, zwave}

Offline KarlHeinz2000

  • Full Member
  • ***
  • Beiträge: 183
Antw:ACK Verständnisfrage
« Antwort #12 am: 02 Oktober 2020, 12:05:44 »
Sieht gut aus!  :)
Soweit ich es testen konnte ist alles ok.

Ein eingehendes heartbeat vom Node generiert allerdings 2 alive events im monitor. Soll das so sein? War evtl auch vorher schon so...

Und jetzt geht es an die Umsetzung der eigentlichen Kommunikation mit ACK. Da habe ich auf FHEM Seite noch keine Idee.
Eine Sub schreiben, in der gesendet wird und gleichzeitig ein Timer läuft und auf das ACK wartet?
Wie kann ich sicherstellen, dass ein empfangenes ACK auch zu meiner gesendeten Nachricht gehört und nicht von einem anderen Node stammt? Fängt da Protokoll das ab?
Es gibt unterschiedliche events:
MYSENSORS_DEVICE MYSENSOR_100 var2 3
MYSENSORS_DEVICE MYSENSOR_100 var2: 3
Einmal mit und ohne ":"
"Ohne" ist das Senden mit angefordertem ACK
"Mit" ist das eingehende ACK
Bei timeout ACK kommt das NACK zwischendrin

2020-10-02 11:57:35 MYSENSORS_DEVICE MYSENSOR_100 var2 3
2020-10-02 11:57:37 MYSENSORS_DEVICE MYSENSOR_100 heartbeat: NACK
2020-10-02 11:57:49 MYSENSORS_DEVICE MYSENSOR_100 var2: 3

Wird ohne requestACK gesendet kommt nur:

MYSENSORS_DEVICE MYSENSOR_100 var2: 3

Macht ein Fifo Sinn, wenn eine Nachricht länger braucht und schon eine die nächste gesendet werden soll. Wo fängt man sowas ab?



Wenn es da Ideen gibt, bitte her damit  ;)

Offline Beta-User

  • Moderator
  • Hero Member
  • ***
  • Beiträge: 13683
  • "Developer"?!? Meistens doch eher "User"
Antw:ACK Verständnisfrage
« Antwort #13 am: 02 Oktober 2020, 12:24:40 »
Wg. des 2. Events: Ist vermutlich eine "Nebenwirkung" der Timer-Erneuerung, kommentiere mal in onSetEvent den Reading-Update aus:
if ($msg->{ack} && ReadingsVal($name,"heartbeat","dead") eq "NACK" and @{$hash->{IODev}->{messagesForRadioId}->{$hash->{radioId}}->{messages}} == 0) {
         #readingsBulkUpdate($hash,"heartbeat","alive") ;
         refreshInternalMySTimer($hash,"Alive") if $hash->{timeoutAlive};
      }

Was die "Warteschlange" angeht: die wird vom Modul verwaltet und der Reihe nach abgearbeitet. MAn. no action required. Ob da irgendeine bessere Timing-Verarbeitung erforderlich wäre: keine Ahnung, ein Teil der Nachrichten wird nämlich auch (zusätzlich) vom GW gepuffert, das hat einen buffer, der afaik sowohl eingehende wie ausgehende Messages enthalten kann (dunkel: ~15). Von daher würde ich das "Freischießen der Leitung" nach Möglichkeit auf dem GW belassen. Wer da richtig viel Traffic hat, muss ggf. eine andere MCU einsetzen und den Nachrichtenpuffer dort vergrößern?

Die Events mit und ohne Doppelpunkt dürften normal sein, ist einmal das Setzen vom User/von FHEM aus und einmal die Antwort, ohne Ack wird so getan, als "wäre das so".
Server: HP-T620@Debian 10, aktuelles FHEM + ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | BT@OpenMQTTGateway
svn:MySensors, WeekdayTimer, RandomTimer, Twilight,  AttrTemplate => {mqtt2, mysensors, zwave}

Offline KarlHeinz2000

  • Full Member
  • ***
  • Beiträge: 183
Antw:ACK Verständnisfrage
« Antwort #14 am: 02 Oktober 2020, 13:01:53 »
Die 2 alive sind immer noch im event monitor. Aber nur wenn das heartbeat vom Node kommt. Wenn ich nur den timeoutAlive ändere, kommt nur 1x alive.

Wie reagiere ich, wenn beim Senden etwas nicht ankommt. z.B: 3 Nachrichten sollen gesendet werden. Bei der 2. gibt es kein ACK. Wie bekomme ich das mit? Wird dann abgebrochen oder noch x-Mal probiert? Geht die 3. Nachricht noch raus?

 

decade-submarginal