@Neubert: ECMD / ECMDDevice set cmd leer, postproc mit state + SetExtensions

Begonnen von Joe_D, 01 Januar 2020, 14:28:00

Vorheriges Thema - Nächstes Thema

Joe_D

Hallo Herr Neubert,

ist wohl in den anderen Foren untergegangen - mir sind an ECMD / ECMDDevice zwei Dinge aufgefallen:

1.) "set on cmd" möchte immer was senden, es geht einfach nicht "nichts" zu senden - weder mit undef noch mit "" (Leerstring).
2.) reading internal postproc kann zwar undef oder "" zurückgeben, es wird aber das state verändert, obwohl ich doch explizit state internal angegeben habe

Hier der vollständige Post: https://forum.fhem.de/index.php/topic,106399.0.html

Warum unterstützt ECMDDevice die SetExtensions nicht? Habe das mal ausprobiert und es geht (siehe https://forum.fhem.de/index.php/topic,65169.msg1002860.html#msg1002860).
Gibt es hier Gründe, warum das nicht implementiert ist?

Gruß

   Jochen

Dr. Boris Neubert

Hallo Jochen,

Danke für Deine Beschäftigung mit dem Modul. Ich habe mir Deine beiden Beiträge durchgelesen.

Sowohl wegen des Setzen von state als auch wegen der Einbindung der SetExtensions müsste ich mich erstmal wieder tief eindenken. Das schaffe ich gerade zeitlich nicht. Ich habe mir aber Deinen Beitrag auf meine Arbeitsliste für das Modul gesetzt und wenn ich wieder eine Programmiersitzung für FHEM mache, werde ich mir das anschauen.

Ich würde in Deinem Anwendungsfall "ein" nicht unmittelbar nach Absetzen des Befehls zum Einschalten setzen sondern tatsächlich abwarten, bis asynchron die Rückmeldung vom Device einläuft. Ich verstehe aber, dass Du wegen des Stromstoßrelais nicht zweimal "ein" senden darfst.

Viele Grüße
Boris
Globaler Moderator, Developer, aktives Mitglied des FHEM e.V. (Marketing, Verwaltung)
Bitte keine unaufgeforderten privaten Nachrichten!

Joe_D

Hallo Boris,

ich schreibe hier meine weiteren Beobachtungen, die ich gemacht habe.

Nachdem ich mich mit der Funktionsweise von ECMD / ECMDDevice und auch DevIO noch etwas näher beschäftigt habe ist mir folgendes aufgefallen:

In ECMD werden Daten sowohl asynchron als auch synchron ausgewertet, obwohl sich beides ins Gehege kommen kann.

Vor allem

1) get/set <commandname> expect

vs.

2) reading <reading> match "<regex>"

Bei 1 wird mit my $answer= DevIo_Expect($hash, $msg, $timeout ); in ECMD_SimpleExpect synchron auf eine Antwort gewartet, wobei eigentlich immer gleichzeitig mit my $buf = ECMD_SimpleRead($hash); Daten in ECMD_Read über die globale Loop abgeholt werden, die falls es passt an 2) weitergeleitet werden.

Interessanterweise funktioniert das, nur sauber ist das IMHO nicht. In ECMD_SimpleExpect werden einfach Antworten von der Quelle "abgefischt" und kommen so (glücklicherweise) nicht bei ECMD_Read an... Habe aber auch keine Idee wie das alles über einen Kanal, also ECMD_Read abzuwickeln wäre (vor allem die Unterscheidung Antwort auf set/get expect oder Spontanmeldung für reading expect)

Ich habe deshalb meine Classdef geändert, sämtliche set-Befehle haben nun kein expect/postproc mehr - dadurch werden Daten nur noch an einer Stelle geschrieben (ECMD_SimpleWrite) und der Empfang von Daten geschieht bei mir nur noch mit ECMD_Read...

params modnr pin
state internal

set on cmd { return "S%modnr:%pin\n" if (ReadingsVal("%NAME","internal","off") eq "off"); return undef; }
#set on expect "(ok\r\n)|(error:.*\r\n)"
#set on postproc { s/\r\n//; $_ }

set off cmd { return "S%modnr:%pin\n" if (ReadingsVal("%NAME","internal","on") eq "on"); return undef; }
#set off expect "(ok\r\n)|(error:.*\r\n)"
#set off postproc { s/\r\n//; $_ }

reading internal match "I%modnr:%pin=[01]\r\n"
reading internal postproc { my $oldstate=ReadingsVal("%NAME","internal",""); s/^I%modnr:%pin=1\r\n$/on/; my $newstate="$_" eq "on" ? "on" : "off"; if ($newstate ne $oldstate) { $_=$newstate; } else { $_= undef; } }


Wenn in der Classdef keine reading <reading> match "<regex>" definiert sind, könnte ECMD_Read ja eigentlich ignoriert werden?
Auf der anderen Seite könnte die Definition von  get/set <commandname> expect und reading <reading> match "<regex>" in einer Classdef als unzulässig definiert werden?


Was mir sonst noch aufgefallen ist, das unglaublich oft dispatch aufgerufen wird, meistens mit sehr wenigen Zeichen:
2020.01.06 17:30:36 5: IOServer: dispatch I
2020.01.06 17:30:36 5: IOServer: dispatch 0:1=1\r\n
2020.01.06 17:30:36 5: IOServer: dispatch I
2020.01.06 17:30:36 5: IOServer: dispatch 0:1=0\r\n
2020.01.06 17:30:37 5: IOServer: dispatch I
2020.01.06 17:30:37 5: IOServer: dispatch 0:1=1\r\n
2020.01.06 17:30:37 5: IOServer: dispatch I
2020.01.06 17:30:37 5: IOServer: dispatch 0:1=0\r\n
2020.01.06 17:30:37 5: IOServer: dispatch I
2020.01.06 17:30:37 5: IOServer: dispatch 0:1=1\r
2020.01.06 17:30:37 5: IOServer: dispatch \n
2020.01.06 17:30:37 5: IOServer: dispatch I
2020.01.06 17:30:37 5: IOServer: dispatch 0:1=0\r\n
2020.01.06 17:30:37 5: IOServer: dispatch I
2020.01.06 17:30:37 5: IOServer: dispatch 0:1=1\r\n
2020.01.06 17:30:38 5: IOServer: dispatch I0
2020.01.06 17:30:38 5: IOServer: dispatch :1=0\r\n
2020.01.06 17:30:38 5: IOServer: dispatch I
2020.01.06 17:30:38 5: IOServer: dispatch 0:1=1\r\n
2020.01.06 17:30:38 5: IOServer: dispatch I
2020.01.06 17:30:38 5: IOServer: dispatch 0:1=0\r\n
2020.01.06 17:30:38 5: IOServer: dispatch I
2020.01.06 17:30:38 5: IOServer: dispatch 0:1=1\r\n
2020.01.06 17:30:38 5: IOServer: dispatch I
2020.01.06 17:30:38 5: IOServer: dispatch 0:1=0\r\n

Zusammengeführt werden die Daten dann irgendwie im ECMDDevice, aber nur wenn das partial Attribut eine Zeitangabe enthält...

Nach Lektüre von DevIO habe ich dann mal die Funktion ECMD_Read etwas umgebaut, sodass bei Spontanempfang von Daten und der Definition eines responseSeparators die Daten schon bei ECMD_Read "richtig gestellt" werden. Den Aufruf von ECMD_SimpleRead könnte man nun auch noch ignorieren, falls keine readings definiert wurden, oder wird das noch für etwas anderes benötigt?
Zitat#####################################
# called from the global loop, when the select for hash->{FD} reports data
sub ECMD_Read($)
{
  my ($hash) = @_;

  return undef unless($hash->{STATE} eq "opened"); # avoid reading from closed device

  my $buf = ECMD_SimpleRead($hash);
  return unless(defined($buf));
  return if($buf eq "");

  my $responseSeparator= $hash->{fhem}{".responseSeparator"};
  if (defined($responseSeparator)) {
   
     my $buffer = $hash->{PARTIAL};
     # concat received data to $buffer
     $buffer .= $buf;

     # as long as the buffer contains newlines (complete datagramm)
     while($buffer =~ m/\n/)
     {
       my $msg;
   
       # extract the complete message ($msg), everything else is assigned to $buffer
       ($msg, $buffer) = split($responseSeparator, $buffer, 2);
   
       # remove trailing whitespaces
       chomp $msg;
       $msg=$msg.$responseSeparator;

       ECMD_Log $hash, 5,  "Spontaneously received " . dq($msg);
 
       # parse the extracted message
       Dispatch($hash, $msg, undef);
     }
 
     # update $hash->{PARTIAL} with the current buffer content
     $hash->{PARTIAL} = $buffer;     
  } else {

    ECMD_Log $hash, 5,  "Spontaneously received " . dq($buf);
    Dispatch($hash, $buf, undef);  # dispatch result to ECMDDevices
  }
}
Das ganze an den responseSeparator zu hängen (der ja ausschließlich für set/get expect definiert ist) ist eventuell nicht ganz geschickt?

Insgesamt sieht das Logfile schon viel aufgeräumter aus und es "matched" auch sofort (also bei I0:1=0\r\n):
2020.01.09 18:32:58 5: IOServer: dispatch error: timer running\r\n
2020.01.09 18:47:32 5: IOServer: dispatch I0:1=1\r\n
2020.01.09 18:47:42 5: IOServer: dispatch ok\r\n
2020.01.09 18:47:42 5: IOServer: dispatch I0:1=0\r\n
2020.01.09 18:47:50 5: IOServer: dispatch ok\r\n
2020.01.09 18:47:50 5: IOServer: dispatch I0:1=1\r\n
2020.01.09 18:47:51 5: IOServer: dispatch ok\r\n
2020.01.09 18:47:51 5: IOServer: dispatch I0:1=0\r\n
2020.01.09 18:47:52 5: IOServer: dispatch ok\r\n
2020.01.09 18:47:52 5: IOServer: dispatch I0:1=1\r\n
2020.01.09 18:47:53 5: IOServer: dispatch ok\r\n
2020.01.09 18:47:53 5: IOServer: dispatch I0:1=0\r\n
2020.01.09 18:47:55 5: IOServer: dispatch ok\r\n
2020.01.09 18:47:55 5: IOServer: dispatch I0:1=1\r\n


Die "ok\r\n" und "error: ...\r\n" sind Rückmeldungen vom AVR bei den set-Commands.

Hier hätte ich nun vier Möglichkeiten:


  • Ich programmier das aus meinem AVR raus, wäre aber schade denn dann bekomme ich nicht mehr direkt mit ob Kommandos ausgeführt werden.
  • Oder würde sowas wie "reading <reading> ignore "<regex>" bzw. reading <reading> expect "<regex>" funktionieren??? Denke nicht, denn "ok\r\n" und "error: ...\r\n" werden ja nicht ans ECMDDevice weitergegeben...
  • Ich markier' die Rückmeldungen eindeutig z.B. mir "R: ok\r\n" bzw. "R: error ...\r\n" - das könnte dann mittels Regex in einem Attribut in ECMD_Read ignoriert werden. Wäre dann aber global für das gesamte ECMD ...
  • Oder ich lasse die Rückmeldungen so wie sie sind und mache nichts ;)

Gruß

   Jochen

Dr. Boris Neubert

Hallo,

ich glaube, dass hier ein Missverständnis zur Funktion von get/set vs. reading vorliegt.

get und set senden ein Kommando an das Gerät und warten dann auf eine Antwort. Ein reading-Eintrag in der Klassendefinition dagegen reagiert auf spontane Aussendungen des Geräts. Das sind beides unterschiedliche Anwendungsfälle und das funktioniert absichtlich so.

Dispatch wird bei Empfang von Daten vom Gerät aufgerufen. Wenn das Gerät die volle Antwort in Stücken sendet, was bei sehr vielen Geräten der Fall ist, wird das Ergebnis über expect bei get/set bzw. über match bei reading solange zusammengebaut, bis es entweder einen Treffer beim regulären Ausdruck gibt oder der Timeout abgelaufen ist.

Viele Grüße
Boris
Globaler Moderator, Developer, aktives Mitglied des FHEM e.V. (Marketing, Verwaltung)
Bitte keine unaufgeforderten privaten Nachrichten!