TSCUL für CUL mit HM-Timestamp

Begonnen von noansi, 18 September 2016, 10:46:28

Vorheriges Thema - Nächstes Thema

rudolfkoenig

ZitatOder möchtest Du Dir mal mein Lösungsansatz zum Disconnect Busy Waiting näher anschauen und DevIo.pm anpassen?
Kann ich gerne machen, aber vorher moechte ich das Problem verstehen.
Ich frage mich inzwischen, ob du von jeder Datei eine TS Variante hast. :)

noansi

Hallo Rudolf,

ZitatIch frage mich inzwischen, ob du von jeder Datei eine TS Variante hast. :)
Ich arbeite daran.  ;)

In DevIo_SimpleRead($) nutzt Du

  ###########
  # Lets' try again: Some drivers return len(0) on the first read...
  if(defined($buf) && length($buf) == 0) {
    $buf = DevIo_SimpleReadWithTimeout($hash, 1);
  }


was in DevIo_SimpleReadWithTimeout in
  my $nfound = select($rin, undef, undef, $timeout);
bis zu 1 Sekunde Wartezeit erzeugt, wenn das device nicht antwortet.

Für HM muss aber in der Regel innerhalb von 120ms - IO Zeit eine Antwort an das HM IO Device geschickt werden. Das ist eine massive Diskrepanz. Insbesondere wenn FHEM versucht ein anderes nicht antwortendes Device zu neuen Antworten zu bewegen und open dabei nicht scheitert.

  # Lets' try again: Some drivers return len(0) on the first read...

ist mit einem
    $buf = DevIo_SimpleReadWithTimeout($hash, 0);
ebenfalls zu erschlagen.

Nun wollen wir noch wissen, ob das device tot ist.
Also kann man das gleiche auch mit einem Timeout erreichen, der dann bei späteren vergeblichen Aufrufen der Read Funktion zuschlägt und den Disconnected Status setzt. Also Zeitpunkt des ertmaligen vergeblichen zweiten Reads merken und bei "jetzt + x" erneutem vergeblichen Versuch Disconnected setzen oder wenn dann doch etwas kommt, den Timout Startzeitpunkt wieder löschen.

Gruß, Ansgar.

rudolfkoenig

Ich wuerde instinktiv 1 durch 0.01 ersetzen. SimpleRead sollte man nur aufrufen, wenn Select was geliefert hat. Wenn der Kommentar stimmt (weiss leider die genaue Ursache nicht mehr), dann duerfte egal ein, ob man 1s oder 0.01s wartet.

Ich habe das jetzt auch geaendert / eingecheckt.

noansi

#18
Hallo Rudolf,

die bisherigen Tests verlaufen recht gut. Ich  kann aber nur mit CUL im Stack testen

Ich sehe noch ein Problem mit STACKABLE_IOReadFn($), das gelegentlich zuschlägt.

Wenn zum Aufrufzeitpunkt schon asynchrone Daten (z.B. Empfangsdaten) anderer Stackteilnehmer unterwegs sind, dann bekommt der Aufrufer Daten geliefert, die nicht für ihn bestimmt sind (und nimmt sie dem richtigen Empfänger weg).
Das kommt in der Regel selten vor, da "synchronous get" normalerweise selten genutzt wird (IT fällt mir da als Ausnahme ein, wo auf die Rückmeldung zum Sendebefehl gewartet wird).
Beim bisherigen STACKABLE_CC und TSSTACKED habe ich das auch gelegentlich beobachtet, es aber vermutlich fälschlicherweise auf verlorene Zeichen auf der Schnittstelle zurückgeführt. Bei "get credit10ms" etc. habe ich es auch gelegentlich gesehen.

Außerdem können je nach IODev und Auslastung des FHEM Hosts mit einem DevIo_SimpleRead auch noch weitere Daten nach einem '\n' geliefert werden, die nicht in falsche Hände sollen und auch nicht verworfen werden sollten.

Folgender Ansatz zur Verbesserung:
#####################################
sub
STACKABLE_IOReadFn($) # used by synchronous get
{
  my ($hash) = @_;
  my $me = $hash->{IODev};
 
  my $rpf = AttrVal($me->{NAME},"readPrefix","\\*");
  my $srpf = AttrVal($hash->{STACKED},"readPrefix","\\*") if defined($hash->{STACKED});
  my $msrpf = $rpf.$srpf if defined($srpf);

  my $sg = $hash->{helper}{SyncGet};
 
  # we have also to consider asynchronous data from other devices in the stack e.g. received data
  # maybe there is allready partial data in real IODev
  # or complete data not for us
  my $ppart = \$me->{IODev}{PARTIAL}; # IODev has to have and use it!!!
  if (!defined($ppart)) { # but maybe it is not existing yet
    $me->{IODev}{PARTIAL} = "";
    $ppart = \$me->{IODev}{PARTIAL};
  }

  my $buf = ${$ppart};
  ${$ppart} = "";
  my $t;

  while(1) {
    # now we wait for our data or data for one of our stacked devices
    my $to = gettimeofday() + 2;
    $t = "";
    while($buf !~ m/\n/) {
      if (!defined($t) || (gettimeofday() > $to)) {
        ${$ppart} = $buf;
        $hash->{helper}{SyncGet} = 0; # just if client forgets it
        return undef; # undef on error
      }
      $t = DevIo_SimpleRead($me->{IODev}); # may block
      $buf .= $t if (defined($t));
    }

    if ($buf =~ m/^$rpf/) { # is it data for us or one of our stacked devices?

      if (!defined($srpf)) { # not someone stacked?
        # must be for us
        ($buf,$t) = split("\n", $buf, 2); # maybe something more arrived
        $buf .= "\n";
        ${$ppart} .= $t;                  # let IODev handle it later
        last;
      }

      if ($buf !~ m/^$msrpf/) { # not for one of our stacked devices?
        # must be for us
        if ($sg) { # are we the ones waiting for data?
          ($buf,$t) = split("\n", $buf, 2); # maybe something more arrived
          $buf .= "\n";
          ${$ppart} .= $t;                  # let IODev handle it later
          last;
        } else {
          ($t,$buf) = split("\n", $buf, 2); # complete data from IODev for us
          $t .= "\n"; ${$ppart} .= $t;      # let IODev handle it later
          next;
        }
      }

      # must be for one of our stacked devices
      if (!$sg) { # are we not the ones waiting for data?
        ($buf,$t) = split("\n", $buf, 2); # maybe something more arrived
        $buf .= "\n";
        ${$ppart} .= $t;                  # let IODev handle it later
        last;
      }
    }

    # must be for IODev
    ($t,$buf) = split("\n", $buf, 2); # complete data from IODev for stacked device
    $t .= "\n"; ${$ppart} .= $t;      # let IODev handle it later
  }

  $hash->{helper}{SyncGet} = 0; # just if client forgets it
  $buf =~ s/^.//; # Cut off prefix
  if(AttrVal($me->{NAME},"binary",0)) {
    $buf =~ s/[\r\n]//g;
    return pack("H*",$buf);
  } else {
    return $buf;
  }
}


Der Haken: leider muss der Client beim Aufruf sein Flag $hash->{helper}{SyncGet} setzen! Was besseres ist mir bisher leider auf die schnelle nicht eingefallen. Und jeder Client muß {PARTIAL} als Empfangspuffer nutzen (oder es müßte ein Funktion dafür her, diesen zu ermitteln).
Da die Funktion ja rekursiv aufgerufen wird, fehlt mir sonst die Info, ob Daten im Puffer bleiben müssen, damit sie später weiter verarbeitet werden oder geliefert werden müssen. Nur bei 2 Geräten im Stapel ist es eindeutig.
Bei CUL_ReadAnswer mußt Du das Flag vor dem Aufruf von DevIo_SimpleRead setzen. Bei CUL_Read nicht, da es ja nie zur Anwendung kommt bei gestapelten Geräten.
Eine Erweiterung von IOReadFn und DevIo_SimpleRead um ein solches Flag wäre vielleicht ein alterantiver Ansatz.

Zweiter Haken: ich habe selbst nur 2 Geräte im Stapel auf dem PI und kann daher nicht testen, ob mein Code bei mehr Geräten im Stack noch Probleme aufwirft. Kannst Du es testen?

Dritter Haken: es muss auch beim Empfang auf die STACKABLE Kennung geprüft werden. Dafür habe ich noch das Attribut readPrefix mit '*' als default ergänzt. Das ergibt maximale Flexibilität für einen generischen Ansatz.
Da bisher bei synchronous get nicht darauf geprüft wurde, sondern einfach das erste Zeichen entfernt wurde, sind Probleme auch nicht deutlich aufgefallen.

Und nein es ist kein Patch, da die Funktion ohnehin komplett anders ist, als in Deiner Version.  ;)
Ich hoffe, die Kommentare machen einigermaßen klar, wie es gedacht ist.
Mit dem rekursiven Aufruf ist es leider recht mühsam, sich die jeweilen Empfangsdaten und Pufferzustände vorzustellen, um es zu verstehen.

Falldaten sind:
1. Es sind noch komplette und/oder partielle Empfangsdaten im {PARTIAL} des IODev.
2. Während des Wartens auf die gewünschten Antwortdaten kommen noch Daten eines "tieferen" Stackteilnehmers an.
3. Während des Wartens auf die gewünschten Antwortdaten kommen noch Daten eines "höheren" Stackteilnehmers an.
4. Es ist nichts im IODev Empfangspuffer und nur das gewünschte Device sendet seine Antwortdaten. Davon ging die bisherige Implementation aus, was meißtens auch zutrifft.

Gruß, Ansgar.

rudolfkoenig

Kannst du mir das Problem bitte nachstellen? Ich will nicht viel Code einbauen, fuer etwas, was ich weder nachvollziehen, noch testen kann.

Ich habe einen SCC Simulator gebaut (contrib/CULsim.pl), da ich bis vor kurzem gar kein SCC Geraet hatte. Hier kann man im unteren Teil beliebige Antworte auf betimmte Befehle schreiben, es ist auch moeglich spontan irgendwelche Nachrichten zu liefern (siehe #timeout).

noansi

Hallo Rudolf,

mach ich. Den Simulator hatte ich noch nicht entdeckt, danke!

Um nicht auch damit so zufällig testen zu müssen, wie mit der Realität, müßte ich die "bösen" Fälle aber wohl hier bei Antworten einbauen.
      my $msg = "";
           if($cmd eq "V")   { $msg = "V 1.6".length($stars)." CUL868";
      } elsif($cmd eq "T01") { $msg = "0000";
      } elsif($cmd eq "?")   { $msg = "? (? is unknown) Use one of t u x";
      } elsif($cmd eq "t")   { $msg = sprintf("%08X", (time()%86400)*125);
      }


Z.B. für eine simulierte Antwort auf X, bei der ein z.B. *K3107020018\n vor und/oder hinter **211704\n geliefert wird.

Denn nur bei Antworten auf Befehle an SCC wird es problematisch.
Da ich mich mit der SCC Firmware bezüglich Stacking intensiv auseinander gesetzt habe, weiß ich das solche Fälle auftreten können, insbesondere, wenn der FHEM Host stark ausgelastet ist, so dass der Schnittstellenpuffer der seriellen Schnittstelle durch FHEM nicht Zeichen für Zeichen geleert wird.

Gruß, Ansgar

rudolfkoenig

ZitatUm nicht auch damit so zufällig testen zu müssen, wie mit der Realität, müßte ich die "bösen" Fälle aber wohl hier bei Antworten einbauen.
Genau. Ich habe eine Multi-Message Variante mit eingebauten Muell eingecheckt, (siehe Kommentar "Forum #57806")
Haeng hier die geaenderte Variante von CULsim.pl an, dann kann ich dein Problem damit nachstellen.

noansi

#22
Hallo Rudolf,

hier meine Abwandlung von CULSim.pl (auch im Anhang):
#!/usr/bin/perl

# Used for SCC testing.

use strict;
use warnings;
use IO::Socket;
use Time::HiRes qw(gettimeofday);

my $port = "12345";
my $serverSock = IO::Socket::INET->new(
   Listen    => 5,
   LocalAddr => 'localhost',
   LocalPort => $port,
   Proto     => 'tcp',
   ReuseAddr => 1
);

die "Can't open server port: $!" if(!$serverSock);
print "Opened port $port\n";

my %selectlist;
$selectlist{$serverSock->fileno()} = $serverSock;
my $cnt=0;
my $rcnt=0;
my $max_stars = -1; # or set to number of stacked devices to test
my $lstSndTm = gettimeofday();

for(;;) {
  my ($rout,$rin) = ('','');
  map { vec($rin, $_, 1) = 1; } keys %selectlist;
 
  my $now = gettimeofday();
  my $to = ($lstSndTm + ((175.5-0.12)/4)) - $now; # to send periodic messages
  $to = 0 if ($to < 0);
  my $nfound = select($rout=$rin, undef, undef, $to);
  $now = gettimeofday();
  die "select error: $!" if($nfound < 0);

  if(($nfound == 0) && ($max_stars >= 0)) { # timeout
    $cnt++;

    my $msg = "";
    my $n = $cnt % ($max_stars + 1);
    my $c = sprintf("%02X", 0x30-2*$n); # rssi -50 -n
    while($n--) {
      $msg .= '*';
    }
    $msg .= "K31070200" . $c; # by rssi checkable that it arrives at the correct destination
   
    $lstSndTm = $now;
    foreach my $fd (keys %selectlist) {
      if($fd != $serverSock->fileno()) {
        my $h = $selectlist{$fd};
        print "$h->{addr}:$h->{port}: snd >$msg<\n";
        syswrite($h->{sock}, $msg."\r\n");
      }
    }
  }

  foreach my $fd (keys %selectlist) {
    next if(!vec($rout, $fd, 1));
    my $h = $selectlist{$fd};

    if($fd == $serverSock->fileno()) {
      my @clientinfo = $h->accept();
      if(!@clientinfo) {
        print "Accept failed: $!\n";

      } else {
        my ($port, $iaddr) = sockaddr_in($clientinfo[1]);
        my %hash = ( port    => $port,
                     addr    => inet_ntoa($iaddr),
                     sock    => $clientinfo[0],
                     partial => "");
        print "$hash{addr}:$hash{port}: Connect\n";
        $selectlist{$clientinfo[0]->fileno()} = \%hash;
      }

      next;
    }

    my $buf;
    if(sysread($h->{sock}, $buf, 256) <= 0) {
      print "$h->{addr}:$h->{port}: left us\n";
      delete $selectlist{$fd};
      next;
    }

    $buf = $h->{partial} . $buf;
    while($buf =~ m/\n/) {
      $rcnt++;
      my ($cmd, $rest) = split("\n", $buf, 2);
      print "$h->{addr}:$h->{port}: $rcnt rcv >$cmd<\n";
      my $stars;
      $cmd =~ m/^(\**)(.*)$/;
      $stars = $1; $cmd = $2;
     
      my $nstars = length($stars);
      $max_stars = $nstars if ($max_stars < $nstars);
     

      my @msg;
      if($cmd eq "V")  {
        push @msg, "*E01015BE2940100B80B" if($rcnt > 10 &&  $max_stars >= 1); # Forum #57806
        push @msg, $stars."VTS 0.06"." SCCSIM".$nstars; #correct version for TSCUL and to check that it arrives at the correct destination

      } elsif($cmd eq "VH"){
        push @msg, $stars."CUL/SIMTS".$nstars;
        push @msg, "**E01015BE2940100B80B" if($rcnt > 10 &&  $max_stars >= 2);

      } elsif($cmd eq "T01"){
        push @msg, $stars."0000";

      } elsif($cmd eq "?") {
        push @msg, $stars."? (? is unknown) Use one of t u x";

      } elsif($cmd eq "t") {
        push @msg, "**K4107020011" if($rcnt > 10 &&  $max_stars >= 2); #simulate "simultanuous" message before
        push @msg, $stars.sprintf("%08X", (time()%86400)*125);
        push @msg, "*K4107020012" if($rcnt > 10 &&  $max_stars >= 1); #simulate "simultanuous" message after

      } elsif($cmd eq "X") {
        push @msg, "*K4107020013" if($rcnt > 10 &&  $max_stars >= 1); #simulate "simultanuous" message before
        push @msg, $stars.sprintf("21 170").$nstars;
        push @msg, "**K4107020014" if($rcnt > 10 &&  $max_stars >= 2); #simulate "simultanuous" message after

      }
      if(@msg) {
        print "$h->{addr}:$h->{port}:    =>".join(",",@msg)."<\n";
        syswrite($h->{sock}, join("\r\n",@msg)."\r\n"); # CUL sends \r\n
      }

      $buf = $rest;
    }
    $h->{partial} = $buf;

  }
}


Wenn ich mit STACKABLE damit FHEM neu starte, dann schaffe ich nur die Initialisierung von 2 gestapelten simulierten TSCULs bei einem Stapel von >2 TSCULs, weil V und VH nicht die richtigen Antworten zu sehen bekommen. Die oberen bleiben dann auf opened.

Ich habe die simulierten Daten auf K messages umgestellt, da ich in 00_TSCUL und 14_TSCUL_WS dafür eine Statistik eingebaut habe und so sehr schön sehen kann, ob alle Daten beim richtigen simulierten TSCUL ankommen. Mit "get dispSRFStat" kann ich mir die Statistik anschauen.

Gruß, Ansgar.

noansi

#23
Hallo Rudolf,

und hier die geänderte Version von STACKABLE_IOReadFn($), damit auch mehrere Nachrichten auf einmal im Puffer sein dürfen:

#####################################
sub
STACKABLE_IOReadFn($) # used by synchronous get
{
  my ($hash) = @_;
  my $me = $hash->{IODev};
  my $meIOh = $me->{IODev};

  my $rpf = AttrVal($me->{NAME},"readPrefix","\\*");

  my $buf;
 
  if (!defined($hash->{helper}{SyncGet})) { # for compatibility, but not well working
    $buf = "";
    while($buf !~ m/\n/) {
      $buf .= DevIo_SimpleRead($me->{IODev}); # may block
    }
    $buf =~ s/^.//; #cutoff prefix
    #may deliver not (only) the wanted data dependig on the data in the devices buffers
    if(AttrVal($me->{NAME},"binary",0)) {
      $buf =~ s/[\r\n]//g;
      return pack("H*",$buf);
    } else {
      return $buf;
    }
  }

  my $srpf = AttrVal($hash->{STACKED},"readPrefix","\\*") if defined($hash->{STACKED});
  my $msrpf = $rpf.$srpf if defined($srpf);

  my $sg = $hash->{helper}{SyncGet}; # set this flag by the client doing synchronous get
  $hash->{helper}{SyncGet} = 0; # just if client forgets it

  # we have also to consider asynchronous data from other devices in the stack e.g. received data
  # maybe there is allready partial data in real IODev
  # or complete data not for us
  my $ppart = \$meIOh->{PARTIAL}; # IODev has to have and use it!!!
  if (!defined($ppart)) { # but maybe it is not existing yet
    $meIOh->{PARTIAL} = "";
    $ppart = \$meIOh->{PARTIAL};
  }

  $buf = ${$ppart};
  $buf .= DevIo_SimpleRead($meIOh); # may block
  ${$ppart} = "";

  my $ret = "";

  # now check for our data or data for one of our stacked devices
  while($buf =~ m/\n/) {

    my $t;
    ($t,$buf) = split("\n", $buf, 2); # maybe something more arrived
   
    if ($t =~ m/^$rpf/) { # is it data for us or one of our stacked devices?

      if (!defined($srpf)) { # not someone stacked?
        # must be for us
        $t =~ s/^.//;             # Cut off prefix
        $ret .= $t . "\n";
        next;
      }

      if ($t !~ m/^$msrpf/) { # not for one of our stacked devices?
        # must be for us
        if ($sg) { # are we the ones waiting for data?
          $t =~ s/^.//;           # Cut off prefix
          $ret .= $t . "\n";
        } else {
          ${$ppart} .= $t . "\n"; # let IODev handle it later, when it receives new data.
                                  # How can we force a Read for IODev later to avoid the delay until next reception of data?
        }
        next;
      }

      # must be for one of our stacked devices
      if (!$sg) { # are we not the ones waiting for data?
        $t =~ s/^.//;             # Cut off prefix
        $ret .= $t . "\n";
        next;
      }
    }

    # must be for IODev
    ${$ppart} .= $t . "\n";       # let IODev handle it later, when it receives new data
                                  # How can we force a Read for IODev later to avoid the delay until next reception of data?
  }

  ${$ppart} .= $buf; # let IODev handle the remainig later, when it receives new data

  if (defined($meIOh->{helper}{ChkPart}) && (${$ppart} =~ m/\n/)) { # or try to do it now, if IODev supports it
    $meIOh->{helper}{ChkPart} = 1; #IODev needs to support it, this flag inhibits reading from device, just parse {PARTIAL}. IODev needs to clear the flag after!
    CallFn($meIOh->{NAME},"ReadFn",$meIOh); # Parse what arrived at IO
  }

  if(AttrVal($me->{NAME},"binary",0)) {
    $ret =~ s/[\r\n]//g;    #? is binary mode fully covered just here? More than one message can arrive here.
    return pack("H*",$ret); #? is binary mode fully covered just here?
  } else {
    return $ret;
  }
}


Weiterhin benötige ich ein Flag $hash->{helper}{SyncGet}, wie bei der letzten Version, um richtig "auszuschleusen". Damit klappt es und auch die ergänzten Befehle kommen richtig an, sowie auch die "störenden" gleichzeitigen asynchronen Daten.

Außerdem noch unschön, da die nicht benötigten Daten in {PARTIAL} der niedrigeren TSCULs (oder CULs) verbleiben, werden sie erst mit dem nächsten Eintreffen von Daten auch verarbeitet und damit verzögert. Für HM natürlich kontraproduktiv.
Aber leider kann ich Read vom niedrigsten CUL, z.B. mittels Timer, nicht zur Verarbeitung von {PARTIAL} aufrufen, weil das bei leerem Puffer einen Disconnect auslösen müßte ohne Daten im Puffer.

Für TSCUL habe ich folgende Lösung mittels des Flags $hash->{helper}{ChkPart} mit dem TSCUL_Read signalisiert wird, nur den {PARTIAL} zu parsen, aber zuvor nicht vom device zu lesen:

#####################################
# called from the global loop, when the select for hash->{FD} reports data
sub
TSCUL_Read($)
{
  my ($h) = @_;

  my $name = $h->{NAME};

  my $ppart = \$h->{PARTIAL};

  if (!$h->{helper}{ChkPart}) { # just parse {PARTIAL}, needed by STACKABLE
    my $buf = DevIo_SimpleRead($h);
    if(!defined($buf)) {
      TSCUL_condUpdate($h,253);
      Log3 $name, 1, "TSCUL/RAW $name: no data Read";
      return "";  # device disconnected
    }

    Log3 $name, 5, "TSCUL/RAW $name: ${$ppart}/$buf";
    ${$ppart} .= $buf;
  } else {
    $h->{helper}{ChkPart} = 0;
  }

  while(${$ppart} =~ m/\n/) {
    my $rmsg;
    ($rmsg,${$ppart}) = split("\n", ${$ppart}, 2);
    $rmsg =~ s/\r//g;
    TSCUL_Parse($h, $h, $name, $rmsg) if($rmsg);
  }
}


Damit werden dann in STACKABLE_IOReadFn($) erst die asynchronen Daten verarbeitet bevor synchrone Antworten geliefert werden.

Gruß, Ansgar.

noansi

#24
Hallo Rudolf,

hier ein diff zu 00_CUL.pm damit STACKABLE_IOReadFn($) im letzten Edit oben damit zusammenarbeiten kann. Damit habe ich keine Probleme mit der Simulation feststellen können (der Match für X war zu schwach um nicht verwechselt zu werden, daher diese zweite Änderung).

--- E:/tmp/old/00_CUL.pm Tue Mar 28 15:43:16 2017
+++ E:/tmp/new/00_CUL.pm Sun Apr 09 11:51:48 2017
@@ -26,7 +26,7 @@
   "uptime"   => ["t", '^[0-9A-F]{8}[\r\n]*$' ],
   "fhtbuf"   => ["T03", '^[0-9A-F]+[\r\n]*$' ],
   "cmds"     => ["?", '.*Use one of( .)*[\r\n]*$' ],
-  "credit10ms" => [ "X", '^.. *\d*[\r\n]*$' ],
+  "credit10ms" => [ "X", '^[0-9A-F]{2} +\d+[\r\n]*$'], #noansi: STACKABLE more save
);

my %sets = (
@@ -181,6 +181,8 @@
     Log3 undef, 2, $msg;
     return $msg;
   }

+  $hash->{helper}{ChkPart} = 0; # noansi: for STACKABLE

   DevIo_CloseDev($hash);

@@ -437,7 +439,7 @@
       $msg = sprintf("%d %02d:%02d:%02d",
         $msg/86400, ($msg%86400)/3600, ($msg%3600)/60, $msg%60);
     } elsif($a[1] eq "credit10ms") {
-      ($msg) = ($msg =~ /^.. *(\d*)[\r\n]*$/);
+      ($msg) = ($msg =~ /^[0-9A-F]{2} +(\d+)[\r\n]*$/); #noansi: STACKABLE more save
     }

     $msg =~ s/[\r\n]//g;
@@ -570,7 +572,9 @@
       }
       return ("Timeout reading answer for get $arg", undef)
         if($nfound == 0);
+      $ohash->{helper}{SyncGet} = 1; #noansi: STACKABLE
       $buf = DevIo_SimpleRead($hash);
+      $ohash->{helper}{SyncGet} = 0; #noansi: STACKABLE
       return ("No data", undef) if(!defined($buf));

     }
@@ -814,13 +818,20 @@
{
   my ($hash) = @_;

-  my $buf = DevIo_SimpleRead($hash);
-  return "" if(!defined($buf));
   my $name = $hash->{NAME};
-
-  my $culdata = $hash->{PARTIAL};
-  Log3 $name, 5, "CUL/RAW: $culdata/$buf";
-  $culdata .= $buf;
+  my $culdata;

+  if (!$hash->{helper}{ChkPart}) { #noansi: just parse {PARTIAL}, needed by STACKABLE
+    my $buf = DevIo_SimpleRead($hash);
+    return "" if(!defined($buf));
+
+    $culdata = $hash->{PARTIAL};
+    Log3 $name, 5, "CUL/RAW: $culdata/$buf";
+    $culdata .= $buf;
+  } else {                        #noansi: just parse {PARTIAL}, needed by STACKABLE
+    $hash->{helper}{ChkPart} = 0; #noansi: just parse {PARTIAL}, needed by STACKABLE
+    $culdata = $hash->{PARTIAL};
+  }                               #noansi: just parse {PARTIAL}, needed by STACKABLE

   while($culdata =~ m/\n/) {
     my $rmsg;

Auch als Datei im Anhang.

Wenn die beiden zusätzlichen Flags nicht definiert sind, dann fällt STACKABLE_IOReadFn($) in den vorherigen Funktionszustand mit seinen Einschränkungen zurück.

Nur die {PARTIAL} Nutzung ist seitens der Clients und des Basis devices Vorraussetzung.

Unsicher bin ich mir bezüglich des binary Modus, ob das so schon richtig funktioniert.

Was hältst Du von diesem Ansatz?
Hast Du einen besseren Vorschlag?

Gruß, Ansgar.

rudolfkoenig

Weiss nicht ob es besser ist, jedenfalls habe ich jetzt eine modifizierte Version von 16_STACKABLE.pm eingecheckt, der "Muell" bei ReadAnswer wegschmeisst, damit startet ein 4-fach CUL (mit 3-mal STACKABLE dazwischen) mit deiner Version von CULsim.pl problemlos. Es sind nur 5 neue Zeilen dabei, ich meine die Auswirkungen zu verstehen.

Den "Muell" richtig zu parsen benoetigt zu viel Aenderung, und das ist mir fuer diese seltenen Faelle zu risikoreich. Es sei denn jemand legt mir nahe, dass das keine seltenen Faelle sind. Als Muell definiere ich alles, was nicht von der bei ReadAnswer angefragten Ebene kommt.

rudolfkoenig

ZitatUnsicher bin ich mir bezüglich des binary Modus, ob das so schon richtig funktioniert.
Binary wird nur fuer die oberste Ebene unterstuetzt.
Es gibt z.Zt. auch nur ein Anwender dafuer mit einem EnOceanPi auf einem SCC. Theoretisch koennte auch ein Razberry auf einem (oder mehreren) SCCs funktioneren.
Da diese binary Platinen eine Weiterreichung nicht unterstuetzen, braucht STACKABLE das auch nicht zu tun.

noansi

#27
Hallo Rudolf,

hattest Du auch mal get credits10ms getestet?

Dafür war ein Teil des CUL patches gedacht.

Angehängt der reduzierte und noch etwas verfeinerte Patch (3600 max. also mind. 1 Leerzeichen), damit die credits richtig gefiltert werden und nicht mit gleichzeitig eintreffenden sonstigen zum alten match passenden Nachrichten verwechselt werden, wie ich es beobachtet habe.

Zitatder "Muell" bei ReadAnswer wegschmeisst,
SCC ist seriell angebunden. Es liefert also Daten Zeichen für Zeichen und nicht, wie der Simulator Zeilenweise.
Damit müssten auch abgeschnittene Nachrichten am realen SCC Stack auftauchen, die als PARTIAL im untersten CUL vorhanden sein können, bevor der SyncGet von oben startet.
Damit müßten beim untertersten CUL schon mal verstümmelte Nachrichten auftauchen.

Der Simulator müßte dafür modifiziert werden, um auch das als Input simulieren zu können.

Gruß, Ansgar.

noansi

Hallo Rudolf,

ich habe CULSim mal dahingehend abgewandelt, dass auch Teilmessages periodisch geliefert werden können, siehe Anhang. Was periodisch gesendet wird und wo gesplittet wird, kann einfach im Source eingestellt werden.
Was noch fehlt wäre eine Teilnachricht direkt im Anschluss an die gewünschte Antwort im selben Sendepuffer, die mit dem nächsten Sendepuffer vervollständigt wird.

Da Du {PARTIAL} beim untersten SCC nicht beim wegwerfen von "Müll" berücksichtigst, kann z.B. so was passieren:
Zitat2017.04.11 06:24:06.201 5: CUL/RAW: **K310/***K310

Beim Parsen muss das dann ausreichend genau gefiltert werden, damit es keine Probleme bereitet, insbesondere, wenn die neue Message keine * enthält und zudem als Kennbuchstabe eine Hex Ziffer gefolgt von weiteren Zahlen oder Hex Ziffern hat.
Das unangenehme ist, dass es im wahren Leben selten passieren wird und von daher schlecht nachvollziehbar ist.

Mit gelegentlichen Quatsch Nachrichten muss nach meinem Verständnis gerechnet werden.

Gruß, Ansgar.

rudolfkoenig

Bist du _ganz_ sicher, dass solche Nachrichten auftauchen koennen? Ich kenne den SCC nicht so genau, aber culfw sendet eine Nachricht erst, wenn es mit NL abgeschlossen ist, oder der Puffer voll ist. Und der Puffer beim SCC ist mit 512 Byte grosszuegig konfiguriert.