1
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-05-04 22:19:38 +00:00

10_ZWave.pm: rewrite command queueing (Forum #40594)

git-svn-id: https://svn.fhem.de/fhem/trunk/fhem@9204 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
rudolfkoenig 2015-09-05 20:09:56 +00:00
parent 5441734458
commit 69169f2bc4
2 changed files with 123 additions and 87 deletions

View File

@ -16,7 +16,7 @@ sub ZWDongle_Read($@);
sub ZWDongle_ReadAnswer($$$);
sub ZWDongle_Ready($);
sub ZWDongle_Write($$$);
sub ZWave_ProcessSendStack($);
sub ZWDongle_ProcessSendStack($);
# See also:
@ -193,8 +193,8 @@ ZWDongle_Initialize($)
$hash->{GetFn} = "ZWDongle_Get";
$hash->{AttrFn} = "ZWDongle_Attr";
$hash->{UndefFn} = "ZWDongle_Undef";
$hash->{AttrList}=
"do_not_notify:1,0 dummy:1,0 model:ZWDongle disable:0,1 homeId networkKey";
$hash->{AttrList}= "do_not_notify:1,0 dummy:1,0 model:ZWDongle disable:0,1 ".
"homeId networkKey delayNeeded:1,0";
}
#####################################
@ -513,32 +513,9 @@ ZWDongle_Write($$$)
$msg = sprintf("%02x%s", length($msg)/2+1, $msg);
$msg = "01$msg" . ZWDongle_CheckSum($msg);
# push message on stack
my $ss = $hash->{SendStack};
push @{$hash->{SendStack}}, $msg;
my $wNMIre = '01....13..028408';
if(@{$ss} && $ss->[0] =~ m/$wNMIre/) {
Log3 $hash, 2,
"ZWDongle_Write: command after wakeupNoMoreInformation dropped";
return;
}
push @{$ss}, $msg;
# assure that wakeupNoMoreInformation is the last message on the sendStack
if($msg =~ m/^01....13(..)/) {
my $wNMI;
my @s = grep { /^$wNMIre/ ? ($wNMI=$_,0):1 } @{$ss};
if($wNMI) {
Log3 $hash, 5, "ZWDongle_Write wakeupNoMoreInformation moved to the end"
if($wNMI ne $msg);
push @s, $wNMI;
$hash->{SendStack} = \@s;
}
}
ZWave_ProcessSendStack($hash);
ZWDongle_ProcessSendStack($hash);
}
sub
@ -547,7 +524,8 @@ ZWDongle_shiftSendStack($$$)
my ($hash, $level, $txt) = @_;
my $ss = $hash->{SendStack};
my $cmd = shift @{$ss};
Log3 $hash, $level, "$txt, removing $cmd from sendstack" if($txt && $cmd);
Log3 $hash, $level, "$txt, removing $cmd from dongle sendstack"
if($txt && $cmd);
$hash->{WaitForAck}=0;
$hash->{SendRetries}=0;
@ -555,11 +533,11 @@ ZWDongle_shiftSendStack($$$)
}
sub
ZWave_ProcessSendStack($)
ZWDongle_ProcessSendStack($)
{
my ($hash) = @_;
#Log3 $hash, 1, "ZWave_ProcessSendStack: ".@{$hash->{SendStack}}.
#Log3 $hash, 1, "ZWDongle_ProcessSendStack: ".@{$hash->{SendStack}}.
# " items on stack, waitForAck ".$hash->{WaitForAck};
RemoveInternalTimer($hash);
@ -568,12 +546,12 @@ ZWave_ProcessSendStack($)
if($hash->{WaitForAck}){
if($ts-$hash->{SendTime} >= 1){
Log3 $hash, 2, "ZWave_ProcessSendStack: no ACK, resending message";
Log3 $hash, 2, "ZWDongle_ProcessSendStack: no ACK, resending message";
$hash->{SendRetries}++;
$hash->{WaitForAck} = 0;
} else {
InternalTimer($ts+1, "ZWave_ProcessSendStack", $hash, 0);
InternalTimer($ts+1, "ZWDongle_ProcessSendStack", $hash, 0);
return;
}
@ -593,7 +571,7 @@ ZWave_ProcessSendStack($)
$hash->{WaitForAck} = 1;
$hash->{SendTime} = $ts;
InternalTimer($ts+1, "ZWave_ProcessSendStack", $hash, 0);
InternalTimer($ts+1, "ZWDongle_ProcessSendStack", $hash, 0);
}
#####################################
@ -693,7 +671,7 @@ ZWDongle_Read($@)
$hash->{PARTIAL} = $data;
# trigger sending of next message
ZWave_ProcessSendStack($hash) if(length($data) == 0);
ZWDongle_ProcessSendStack($hash) if(length($data) == 0);
return $msg if(defined($local));
return undef;
@ -944,6 +922,13 @@ ZWDongle_Ready($)
<li><a href="#homeId">homeId</a><br>
Stores the homeId of the dongle. Is a workaround for some buggy dongles,
wich sometimes report a wrong/nonexisten homeId (Forum #35126)</li>
<li><a href="#networkKey">networkKey</a><br>
Needed for secure inclusion, hex string with length of 32
</li>
<li><a href="#delayNeeded">delayNeeded</a><br>
If set to 0, no delay is needed between sending consecutive commands to
the same receiver. Default is 1 (delay is needed).
</li>
</ul>
<br>

View File

@ -9,11 +9,12 @@ use SetExtensions;
use Compress::Zlib;
use Time::HiRes qw( gettimeofday );
sub ZWave_Cmd($$@);
sub ZWave_Get($@);
sub ZWave_Parse($$@);
sub ZWave_Set($@);
sub ZWave_Get($@);
sub ZWave_Cmd($$@);
sub ZWave_SetClasses($$$$);
sub ZWave_addToSendStack($$);
use vars qw(%zw_func_id);
use vars qw(%zw_type6);
@ -425,7 +426,8 @@ use vars qw(%zwave_deviceSpecial);
init => { ORDER=>50, CMD => '"get $NAME zwavePlusInfo"' } } }
);
my $crypt_Rijndael = 0;
my $zwave_cryptRijndael = 0;
my $zwave_lastHashSent;
sub
ZWave_Initialize($)
@ -448,7 +450,7 @@ ZWave_Initialize($)
if($@) {
Log 3, "ZWave: cannot load Crypt::Rijndael, SECURITY class disabled";
} else {
$crypt_Rijndael = 1;
$zwave_cryptRijndael = 1;
}
}
@ -481,7 +483,6 @@ ZWave_Define($$)
AssignIoPort($hash); # FIXME: should take homeId into account
if(@a) { # Autocreate: set the classes, execute the init calls
$hash->{lastMsgTimestamp} = gettimeofday(); # device is awake.
ZWave_SetClasses($homeId, $id, undef, $a[0]);
}
return undef;
@ -617,8 +618,6 @@ ZWave_Cmd($$@)
my $cmdFmt = $cmdList{$cmd}{fmt};
my $cmdId = $cmdList{$cmd}{id};
# 0x05=AUTO_ROUTE+ACK, 0x20: ExplorerFrames
my $cmdEf = (AttrVal($name, "noExplorerFrames", 0) == 0 ? "25" : "05");
my $nArg = 0;
if($cmdFmt =~ m/%/) {
@ -679,7 +678,9 @@ ZWave_Cmd($$@)
} else {
my $len = sprintf("%02x", length($cmdFmt)/2+1);
my $cmdEf = (AttrVal($name, "noExplorerFrames", 0) == 0 ? "25" : "05");
$data = "13$id$len$cmdId${cmdFmt}$cmdEf"; # 13==SEND_DATA
}
$data .= $id; # callback=>id
@ -697,25 +698,11 @@ ZWave_Cmd($$@)
}
}
if($baseClasses =~ m/WAKE_UP/) {
if(!$baseHash->{WakeUp}) {
my @arr = ();
$baseHash->{WakeUp} = \@arr;
}
my $tdiff = gettimeofday() - $baseHash->{lastMsgTimestamp};
my $awake = ($baseHash->{lastMsgTimestamp} && $tdiff < 3);
if(!$awake && ($data !~ m/......9880.*/) ) {
Log3 $name, 5, "Device not awake (tdiff= $tdiff),".
" command ($data) stored in sendstack";
push @{$baseHash->{WakeUp}}, $data;
return (AttrVal($name,"verbose",3) > 2 ?
"Scheduled for sending after WAKEUP" : undef);
}
my $r = ZWave_addToSendStack($baseHash, $data);
if($r) {
return (AttrVal($name,"verbose",3) > 2 ? $r : undef);
}
IOWrite($hash, "00", $data);
my $val;
if($type eq "get") {
no strict "refs";
@ -1345,7 +1332,7 @@ ZWave_clockAdjust($$)
my ($hash, $d) = @_;
return $d if($d !~ m/^13(..)048104....$/);
my ($err, $nd) = ZWave_clockSet();
my $cmdEf = (AttrVal($hash->{NAME}, "noExplorerFrames", 0) == 0 ? "25" : "05");
my $cmdEf = (AttrVal($hash->{NAME},"noExplorerFrames",0) == 0 ? "25" : "05");
return "13${1}0481${nd}${cmdEf}${1}";
}
@ -2386,12 +2373,20 @@ ZWave_wakeupTimer($)
{
my ($hash) = @_;
my $now = gettimeofday();
if($now - $hash->{lastMsgTimestamp} > 1) { # wakeupNoMoreInformation
if($hash->{STATE} ne "TRANSMIT_NO_ACK") {
if(!$hash->{wakeupAlive}) {
$hash->{wakeupAlive} = 1;
$hash->{lastMsgSent} = $now;
InternalTimer($now+0.1, "ZWave_wakeupTimer", $hash, 0);
} elsif($now - $hash->{lastMsgSent} > 1) {
if(!$hash->{SendStack}) {
my $nodeId = $hash->{id};
my $cmdEf = (AttrVal($hash->{NAME},"noExplorerFrames",0)==0 ? "25":"05");
# wakeupNoMoreInformation
IOWrite($hash, "00", "13${nodeId}028408${cmdEf}$nodeId");
}
delete $hash->{wakeupAlive};
} else {
InternalTimer($now+0.1, "ZWave_wakeupTimer", $hash, 0);
@ -2400,19 +2395,58 @@ ZWave_wakeupTimer($)
}
sub
ZWave_sendWakeup($)
ZWave_processSendStack($$$)
{
my ($hash) = @_;
my ($hash, $now, $withDelay) = @_;
my $ss = $hash->{SendStack};
return if(!$ss);
my $wu = $hash->{WakeUp};
if($wu && @{$wu}) {
foreach my $wuCmd (@{$wu}) {
IOWrite($hash, "00", ZWave_clockAdjust($hash, $wuCmd));
Log3 $hash, 4, "Sending stored command: $wuCmd";
}
@{$hash->{WakeUp}}=();
InternalTimer(gettimeofday()+0.1, "ZWave_wakeupTimer", $hash, 0);
if($withDelay && AttrVal($hash->{IODev}{NAME}, "delayNeeded",1)) {
InternalTimer(gettimeofday()+0.3, sub {
ZWave_processSendStack($hash, $now, 0);
}, undef, 0);
return;
}
shift @{$ss} if(index($ss->[0],"sent") == 0);
if(@{$ss} == 0) {
delete $hash->{SendStack};
return;
}
IOWrite($hash, "00", $ss->[0]);
$ss->[0] = "sent:".$ss->[0];
$hash->{lastMsgSent} = ($now ? $now : gettimeofday());
$zwave_lastHashSent = $hash;
}
sub
ZWave_addToSendStack($$)
{
my ($hash, $cmd) = @_;
if(!$hash->{SendStack}) {
my @empty;
$hash->{SendStack} = \@empty;
}
my $ss = $hash->{SendStack};
push @{$ss}, $cmd;
my $now;
if(index(AttrVal($hash->{NAME}, "classes", ""), "WAKE_UP") >= 0) {
return "Scheduled for sending after WAKEUP" if(!$hash->{wakeupAlive});
} else { # clear commands without 0113 and 0013
$now = gettimeofday();
if(@{$ss} > 1 && $now-$hash->{lastMsgSent} > 10) {
Log3 $hash, 2,
"ERROR: $hash->{NAME}: cleaning commands without ack after 10s";
delete $hash->{SendStack};
return ZWave_addToSendStack($hash, $cmd);
}
}
ZWave_processSendStack($hash, $now, 0) if(@{$ss} == 1);
return undef;
}
@ -2432,11 +2466,21 @@ ZWave_Parse($$@)
return "";
}
if($msg =~ m/^01(..)(..*)/) { # 01==ANSWER
if($msg =~ m/^01(..)(..*)/) { # 01==ANSWER from the ZWDongle
my ($cmd, $arg) = ($1, $2);
$cmd = $zw_func_id{$cmd} if($zw_func_id{$cmd});
if($cmd eq "ZW_SEND_DATA") {
Log3 $ioName, 2, "ERROR: cannot SEND_DATA: $arg" if($arg != 1);
if($cmd eq "ZW_SEND_DATA") { # 011301: data was sent.
if($arg != 1) {
if($zwave_lastHashSent) {
my $hash = $zwave_lastHashSent;
readingsSingleUpdate($hash, "SEND_DATA", "failed:$arg", 1);
Log3 $ioName, 2, "ERROR: cannot SEND_DATA to $hash->{NAME}: $arg";
ZWave_processSendStack($hash, undef, 1);
} else {
Log3 $ioName, 2, "ERROR: cannot SEND_DATA: $arg (unknown device)";
}
}
return "";
}
if($cmd eq "SERIAL_API_SET_TIMEOUTS" && $arg =~ m/(..)(..)/) {
@ -2476,14 +2520,12 @@ ZWave_Parse($$@)
my $dh = $modules{ZWave}{defptr}{"$homeId $1"};
return "" if(!$dh);
$dh->{lastMsgTimestamp} = gettimeofday();
if($iodev->{addSecure}) {
readingsSingleUpdate($dh, "SECURITY",
"INITIALIZING (starting secure inclusion)", 0);
my $classes = AttrVal($dh->{NAME}, "classes", "");
if($classes =~ m/SECURITY/) {
if ($crypt_Rijndael == 1) {
if ($zwave_cryptRijndael == 1) {
my $key = AttrVal($ioName, "networkKey", "");
if($key) {
$iodev->{secInitName} = $dh->{NAME};
@ -2498,9 +2540,9 @@ ZWave_Parse($$@)
}
} else {
readingsSingleUpdate($dh, "SECURITY",
'DISABLED (Module Crypt_Rijndael not found)', 0);
'DISABLED (Module Crypt::Rijndael not found)', 0);
Log3 $dh->{NAME}, 1, "$dh->{NAME}: SECURITY disabled, module ".
"Crypt_Rijndael not found";
"Crypt::Rijndael not found";
}
} else {
readingsSingleUpdate($dh, "SECURITY",
@ -2519,8 +2561,11 @@ ZWave_Parse($$@)
my $hash = $modules{ZWave}{defptr}{"$homeId $id"};
if($hash) {
ZWave_sendWakeup($hash) if($hash);
$hash->{lastMsgTimestamp} = gettimeofday();
if(index(AttrVal($hash->{NAME}, "classes", ""), "WAKE_UP") >= 0) {
ZWave_wakeupTimer($hash);
ZWave_processSendStack($hash, undef, 0);
}
if(!$ret) {
readingsSingleUpdate($hash, "CMD", $cmd, 1); # forum:20884
return $hash->{NAME};
@ -2528,14 +2573,18 @@ ZWave_Parse($$@)
}
return $ret;
} elsif($cmd eq "ZW_SEND_DATA") {
} elsif($cmd eq "ZW_SEND_DATA") { # 0013cb00....
my $hash = $modules{ZWave}{defptr}{"$homeId $callbackid"};
my %msg = ('00'=>'OK', '01'=>'NO_ACK', '02'=>'FAIL',
'03'=>'NOT_IDLE', '04'=>'NOROUTE' );
my $msg = ($msg{$id} ? $msg{$id} : "UNKNOWN_ERROR");
if ($id eq "00") {
if($id eq "00") {
Log3 $ioName, 4, "$ioName transmit $msg for $callbackid";
readingsSingleUpdate($hash, "transmit", $msg, 0) if($hash);
if($hash) {
readingsSingleUpdate($hash, "transmit", $msg, 0);
ZWave_processSendStack($hash, undef, 1);
}
return "";
} else {
@ -2612,7 +2661,6 @@ ZWave_Parse($$@)
}
$baseHash->{lastMsgTimestamp} = gettimeofday();
my $name = $hash->{NAME};
my @event;
my @args = ($arg); # MULTI_CMD handling
@ -2667,7 +2715,10 @@ ZWave_Parse($$@)
push @event, "UNPARSED:$className $arg" if(!$matched);
}
ZWave_sendWakeup($baseHash) if($arg =~ m/^028407/);
if($arg =~ m/^028407/) { # wakeup:notification
ZWave_wakeupTimer($hash);
ZWave_processSendStack($hash, undef, 0);
}
return "" if(!@event);