2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-04-30 12:07:09 +00:00

98_ArduCounter: devio_getState for readyFn to solve reconnect issues

git-svn-id: https://svn.fhem.de/fhem/trunk@26790 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
StefanStrobel 2022-12-05 19:48:59 +00:00
parent b09a4fdbaf
commit f340880227

View File

@ -21,9 +21,7 @@
# #
# #
# ideas / todo: # ideas / todo:
# - use DoClose / SetStates -> Utils?
# - check integration of device none and write tests # - check integration of device none and write tests
#
# - DevIo_IsOpen instead of checking fd # - DevIo_IsOpen instead of checking fd
# - "static" ports that do not count but report every change or an analog value to Fhem # - "static" ports that do not count but report every change or an analog value to Fhem
# - check reply from device after sending a command # - check reply from device after sending a command
@ -31,6 +29,7 @@
# - max time for interpolation as attribute # - max time for interpolation as attribute
# - detect level thresholds automatically for analog input, track drift # - detect level thresholds automatically for analog input, track drift
# - timeMissed # - timeMissed
# - rename openRetries to helloRetries
# #
# #
@ -110,6 +109,8 @@ BEGIN {
DevIo_SimpleRead DevIo_SimpleRead
DevIo_CloseDev DevIo_CloseDev
DevIo_Disconnected DevIo_Disconnected
DevIo_setStates
DevIo_getState
SetExtensions SetExtensions
HttpUtils_NonblockingGet HttpUtils_NonblockingGet
@ -126,7 +127,7 @@ BEGIN {
}; };
my $Module_version = '8.00 - 21.10.2021'; my $Module_version = '8.01 - 04.12.2022';
my %SetHash = ( my %SetHash = (
@ -256,7 +257,9 @@ sub DefineFn {
return 'wrong syntax: define <name> ArduCounter devicename@speed or ipAdr:port' return 'wrong syntax: define <name> ArduCounter devicename@speed or ipAdr:port'
if ( @a < 3 ); if ( @a < 3 );
DevIo_CloseDev($hash); $hash->{devioNoSTATE} = 1; # let stateFormat do its thing, just set the reading 'state', not the Internal STATE
DevIo_CloseDev($hash); # make sure it is initially closed
DevIo_setStates($hash, 'disconnected'); # initial value, no triggers
my $name = $a[0]; my $name = $a[0];
my $dev = $a[2]; my $dev = $a[2];
@ -279,8 +282,7 @@ sub DefineFn {
} }
$hash->{DeviceName} = $dev; $hash->{DeviceName} = $dev;
$hash->{VersionModule} = $Module_version; $hash->{VersionModule} = $Module_version;
$hash->{NOTIFYDEV} = "global"; # NotifyFn nur aufrufen wenn global events (INITIALIZED) $hash->{NOTIFYDEV} = 'global'; # NotifyFn nur aufrufen wenn global events (INITIALIZED)
$hash->{STATE} = "disconnected";
delete $hash->{Initialized}; # device might not be initialized - wait for hello / setup before cmds delete $hash->{Initialized}; # device might not be initialized - wait for hello / setup before cmds
@ -309,7 +311,7 @@ sub SetDisconnected {
RemoveInternalTimer ("alive:$name"); # no timeout if waiting for keepalive response RemoveInternalTimer ("alive:$name"); # no timeout if waiting for keepalive response
RemoveInternalTimer ("keepAlive:$name"); # don't send keepalive messages anymore RemoveInternalTimer ("keepAlive:$name"); # don't send keepalive messages anymore
RemoveInternalTimer ("sendHello:$name"); RemoveInternalTimer ("sendHello:$name");
DevIo_Disconnected($hash); # close, add to readyFnList so _Ready is called to reopen DevIo_Disconnected($hash); # close, set state and add to readyFnList so _Ready is called to reopen
return; return;
} }
@ -327,6 +329,7 @@ sub OpenCallback {
delete $hash->{BUSY_OPENDEV}; delete $hash->{BUSY_OPENDEV};
if ($hash->{FD}) { if ($hash->{FD}) {
Log3 $name, 5, "$name: DoOpen succeeded in callback"; Log3 $name, 5, "$name: DoOpen succeeded in callback";
DevIo_setStates($hash, 'opened');
my $hdl = AttrVal($name, "helloSendDelay", 4); my $hdl = AttrVal($name, "helloSendDelay", 4);
# send hello if device doesn't say "Started" withing $hdl seconds # send hello if device doesn't say "Started" withing $hdl seconds
RemoveInternalTimer ("sendHello:$name"); RemoveInternalTimer ("sendHello:$name");
@ -382,7 +385,8 @@ sub DoOpen {
} }
if (!$reopen) { # not called from _Ready if (!$reopen) { # not called from _Ready
DevIo_CloseDev($hash); DevIo_CloseDev($hash); # doesn't change state reading
DevIo_setStates($hash, 'disconnected'); # initial value, no triggers
delete $hash->{NEXT_OPEN}; delete $hash->{NEXT_OPEN};
delete $hash->{DevIoJustClosed}; delete $hash->{DevIoJustClosed};
} }
@ -400,6 +404,7 @@ sub DoOpen {
delete $hash->{TIMEOUT}; delete $hash->{TIMEOUT};
if ($hash->{FD}) { if ($hash->{FD}) {
Log3 $name, 5, "$name: DoOpen succeeded immediately" if (!$reopen); Log3 $name, 5, "$name: DoOpen succeeded immediately" if (!$reopen);
DevIo_setStates($hash, 'opened');
} else { } else {
Log3 $name, 5, "$name: DoOpen waiting for callback" if (!$reopen); Log3 $name, 5, "$name: DoOpen waiting for callback" if (!$reopen);
} }
@ -407,32 +412,6 @@ sub DoOpen {
} }
##################################################
# close connection
# $hash is physical or both (connection over TCP)
sub DoClose {
my ($hash, $noState, $noDelete) = @_;
my $name = $hash->{NAME};
Log3 $name, 5, "$name: Close called from " . FhemCaller() .
($noState || $noDelete ? ' with ' : '') . ($noState ? 'noState' : '') . # set state?
($noState && $noDelete ? ' and ' : '') . ($noDelete ? 'noDelete' : ''); # command delete on connection device?
delete $hash->{LASTOPEN}; # reset so next open will actually call OpenDev
if ($hash->{DeviceName} eq 'none') {
Log3 $name, 4, "$name: Simulate closing connection to none";
}
else {
Log3 $name, 4, "$name: Close connection with DevIo_CloseDev";
# close even if it was not open yet but on ready list (need to remove entry from readylist)
DevIo_CloseDev($hash);
}
SetStates($hash, 'disconnected') if (!$noState);
return;
}
################################################################# #################################################################
# set state Reading and STATE internal # set state Reading and STATE internal
# call instead of setting STATE directly and when inactive / disconnected # call instead of setting STATE directly and when inactive / disconnected
@ -443,7 +422,7 @@ sub SetStates {
my $newState = $state; my $newState = $state;
Log3 $name, 5, "$name: SetState called from " . FhemCaller() . " with $state sets state and STATE to $newState"; Log3 $name, 5, "$name: SetState called from " . FhemCaller() . " with $state sets state and STATE to $newState";
$hash->{STATE} = $newState; #$hash->{STATE} = $newState; # leave to be set via stateFormat when reading 'state' changes
return if ($newState eq ReadingsVal($name, 'state', '')); return if ($newState eq ReadingsVal($name, 'state', ''));
readingsSingleUpdate($hash, 'state', $newState, 1); readingsSingleUpdate($hash, 'state', $newState, 1);
return; return;
@ -456,13 +435,14 @@ sub ReadyFn {
my $hash = shift; my $hash = shift;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
if($hash->{STATE} eq "disconnected") { if(DevIo_getState($hash) eq 'disconnected') {
RemoveInternalTimer ("alive:$name"); # no timeout if waiting for keepalive response RemoveInternalTimer ("alive:$name"); # no timeout if waiting for keepalive response
RemoveInternalTimer ("keepAlive:$name"); # don't send keepalive messages anymore RemoveInternalTimer ("keepAlive:$name"); # don't send keepalive messages anymore
delete $hash->{Initialized}; # when reconnecting wait for setup / hello before further action delete $hash->{Initialized}; # when reconnecting wait for setup / hello before further action
if (IsDisabled($name)) { if (IsDisabled($name)) {
Log3 $name, 3, "$name: _Ready: $name is disabled - don't try to reconnect"; Log3 $name, 3, "$name: _Ready: $name is disabled - don't try to reconnect";
DevIo_CloseDev($hash); # close, remove from readyfnlist so _ready is not called again DevIo_CloseDev($hash); # close, remove from readyfnlist so _ready is not called again
DevIo_setStates($hash, 'disconnected'); # no triggers
return; return;
} }
DoOpen($hash, 1); # reopen, don't call DevIoClose before reopening DoOpen($hash, 1); # reopen, don't call DevIoClose before reopening
@ -1148,6 +1128,7 @@ sub AttrFn {
Log3 $name, 3, "$name: value too big in attr $name $aName $aVal"; Log3 $name, 3, "$name: value too big in attr $name $aName $aVal";
return "Value too big: $aVal"; return "Value too big: $aVal";
} }
# todo: set new keepalive delay and communicate to device
} }
else { else {
Log3 $name, 3, "$name: Invalid value in attr $name $aName $aVal"; Log3 $name, 3, "$name: Invalid value in attr $name $aName $aVal";
@ -1157,8 +1138,16 @@ sub AttrFn {
elsif ($aName eq 'disable') { elsif ($aName eq 'disable') {
if ($aVal) { if ($aVal) {
Log3 $name, 5, "$name: disable attribute set"; Log3 $name, 5, "$name: disable attribute set";
SetDisconnected($hash); # set to disconnected and remove timers if ($hash->{DeviceName} eq 'none') {
Log3 $name, 4, "$name: Simulate closing connection to none";
}
else {
DevIo_CloseDev($hash); # really close and remove from readyFnList again DevIo_CloseDev($hash); # really close and remove from readyFnList again
DevIo_setStates($hash, 'disconnected'); # initial value, no triggers
RemoveInternalTimer ("alive:$name"); # no timeout if waiting for keepalive response
RemoveInternalTimer ("keepAlive:$name"); # don't send keepalive messages anymore
RemoveInternalTimer ("sendHello:$name");
}
return; return;
} else { } else {
Log3 $name, 3, "$name: disable attribute cleared"; Log3 $name, 3, "$name: disable attribute cleared";
@ -1240,7 +1229,7 @@ sub DoFlash {
$ip = $1; $ip = $1;
$port = $1; $port = $1;
} }
return if ($hash->{DeviceName} eq 'none');
my $hexFile = shift @args; my $hexFile = shift @args;
my $netPort = 0; my $netPort = 0;
@ -1313,9 +1302,8 @@ sub DoFlash {
if (-e $logFile) { if (-e $logFile) {
unlink $logFile; unlink $logFile;
} }
DevIo_Disconnected($hash); # close, add to readyFnList so _Ready is called to reopen, set state
SetDisconnected($hash);
DevIo_CloseDev($hash);
$log .= "$name closed\n"; $log .= "$name closed\n";
$flashCommand =~ s/\Q[PORT]\E/$port/g; $flashCommand =~ s/\Q[PORT]\E/$port/g;
@ -1439,9 +1427,15 @@ sub SetFn {
} }
elsif ($setName eq "reconnect") { elsif ($setName eq "reconnect") {
Log3 $name, 4, "$name: set reconnect called"; Log3 $name, 4, "$name: set reconnect called";
if ($hash->{DeviceName} eq 'none') {
Log3 $name, 4, "$name: Simulate closing / reopening connection to none";
}
else {
DevIo_CloseDev($hash); DevIo_CloseDev($hash);
DevIo_setStates($hash, 'disconnected'); # no triggers
delete $hash->{OpenRetries}; delete $hash->{OpenRetries};
DoOpen($hash); DoOpen($hash);
}
return; return;
} }
elsif ($setName eq "clearLevels") { elsif ($setName eq "clearLevels") {
@ -1502,8 +1496,14 @@ sub SetFn {
if (DoWrite($hash, "r")) { if (DoWrite($hash, "r")) {
delete $hash->{Initialized}; delete $hash->{Initialized};
} }
if ($hash->{DeviceName} eq 'none') {
Log3 $name, 4, "$name: Simulate closing and reopening connection to none";
}
else {
DevIo_CloseDev($hash); DevIo_CloseDev($hash);
DevIo_setStates($hash, 'disconnected'); # no triggers
DoOpen($hash); DoOpen($hash);
}
return "sent (r)eset command to device - waiting for its setup message"; return "sent (r)eset command to device - waiting for its setup message";
} }
elsif ($setName eq "resetWifi") { elsif ($setName eq "resetWifi") {
@ -1984,7 +1984,7 @@ sub HandleRunTime {
#Log3 $name, 5, "$name: HandleRunTime: devices list is @devices"; #Log3 $name, 5, "$name: HandleRunTime: devices list is @devices";
DEVICELOOP: DEVICELOOP:
foreach my $d (@devices) { foreach my $d (@devices) {
my $state = (ReadingsVal($d, "state", "")); my $state = (ReadingsVal($d, 'state', ''));
#Log3 $name, 5, "$name: HandleRunTime: check $d with state $state"; #Log3 $name, 5, "$name: HandleRunTime: check $d with state $state";
if ($state =~ /1|on|open|BI/) { if ($state =~ /1|on|open|BI/) {
$doIgnore = 1; $doIgnore = 1;
@ -2210,7 +2210,14 @@ sub Parse {
my $delay = AttrVal($name, "nextOpenDelay", 60); my $delay = AttrVal($name, "nextOpenDelay", 60);
Log3 $name, 4, "$name: _Parse: primary tcp connection seems busy - delay next open"; Log3 $name, 4, "$name: _Parse: primary tcp connection seems busy - delay next open";
SetDisconnected($hash); # set to disconnected (state), remove timers SetDisconnected($hash); # set to disconnected (state), remove timers
if ($hash->{DeviceName} eq 'none') {
Log3 $name, 4, "$name: Simulate closing connection to none";
}
else {
DevIo_CloseDev($hash); # close, remove from readyfnlist so _ready is not called again DevIo_CloseDev($hash); # close, remove from readyfnlist so _ready is not called again
DevIo_setStates($hash, 'disconnected'); # no triggers
}
RemoveInternalTimer ("delayedopen:$name"); RemoveInternalTimer ("delayedopen:$name");
InternalTimer($now+$delay, "ArduCounter::DelayedOpen", "delayedopen:$name", 0); InternalTimer($now+$delay, "ArduCounter::DelayedOpen", "delayedopen:$name", 0);
# todo: the level reports should be recorded separately per pin # todo: the level reports should be recorded separately per pin