DevIo.pm: Add nonblocking connect for TCP (Forum #53309)

git-svn-id: https://svn.fhem.de/fhem/trunk@11700 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
rudolfkoenig 2016-06-20 20:46:10 +00:00
parent 011d76d457
commit 49d9886e5e
3 changed files with 105 additions and 57 deletions

View File

@ -59,7 +59,10 @@ FBAHA_Define($$)
$hash->{DeviceName} = $dev; $hash->{DeviceName} = $dev;
return undef if($dev eq "none"); # DEBUGGING return undef if($dev eq "none"); # DEBUGGING
my $ret = DevIo_OpenDev($hash, 0, "FBAHA_DoInit"); my $ret = DevIo_OpenDev($hash, 0, "FBAHA_DoInit", sub(){
my ($hash, $err) = @_;
Log3 $name, 2, "$name: $err" if($err);
});
return $ret; return $ret;
} }

View File

@ -5,7 +5,7 @@ package main;
sub DevIo_CloseDev($@); sub DevIo_CloseDev($@);
sub DevIo_Disconnected($); sub DevIo_Disconnected($);
sub DevIo_Expect($$$); sub DevIo_Expect($$$);
sub DevIo_OpenDev($$$); sub DevIo_OpenDev($$$;$);
sub DevIo_SetHwHandshake($); sub DevIo_SetHwHandshake($);
sub DevIo_SimpleRead($); sub DevIo_SimpleRead($);
sub DevIo_SimpleReadWithTimeout($$); sub DevIo_SimpleReadWithTimeout($$);
@ -188,16 +188,59 @@ DevIo_Expect($$$)
} }
######################## ########################
# callback is only meaningful for TCP/IP (Nonblocking connect), but can used in
# every cases. It will be called with $hash and a (potential) error message
sub sub
DevIo_OpenDev($$$) DevIo_OpenDev($$$;$)
{ {
my ($hash, $reopen, $initfn) = @_; my ($hash, $reopen, $initfn, $callback) = @_;
my $dev = $hash->{DeviceName}; my $dev = $hash->{DeviceName};
my $name = $hash->{NAME}; my $name = $hash->{NAME};
my $po; my $po;
my $baudrate; my $baudrate;
($dev, $baudrate) = split("@", $dev); ($dev, $baudrate) = split("@", $dev);
my ($databits, $parity, $stopbits) = (8, 'none', 1); my ($databits, $parity, $stopbits) = (8, 'none', 1);
sub
doCb($)
{
my ($r) = @_;
$callback->($hash,$r) if($callback);
return $r;
}
sub
doTailWork()
{
DevIo_setStates($hash, "opened");
my $ret;
if($initfn) {
my $hadFD = defined($hash->{FD});
$ret = &$initfn($hash);
if($ret) {
if($hadFD && !defined($hash->{FD})) { # Forum #54732 / ser2net
DevIo_Disconnected($hash);
$hash->{NEXT_OPEN} = time()+60;
} else {
DevIo_CloseDev($hash);
Log3 $name, 1, "Cannot init $dev, ignoring it ($name)";
}
}
}
if(!$ret) {
if($reopen) {
Log3 $name, 1, "$dev reappeared ($name)";
} else {
Log3 $name, 3, "$name device opened" if(!$hash->{DevioText});
}
}
DoTrigger($name, "CONNECTED") if($reopen && !$ret);
return undef;
}
if($baudrate =~ m/(\d+)(,([78])(,([NEO])(,([012]))?)?)?/) { if($baudrate =~ m/(\d+)(,([78])(,([NEO])(,([012]))?)?)?/) {
$baudrate = $1 if(defined($1)); $baudrate = $1 if(defined($1));
@ -209,7 +252,7 @@ DevIo_OpenDev($$$)
if($hash->{DevIoJustClosed}) { if($hash->{DevIoJustClosed}) {
delete $hash->{DevIoJustClosed}; delete $hash->{DevIoJustClosed};
return; return doCb(undef);
} }
$hash->{PARTIAL} = ""; $hash->{PARTIAL} = "";
@ -226,14 +269,14 @@ DevIo_OpenDev($$$)
}; };
if($@) { if($@) {
Log3 $name, 1, $@; Log3 $name, 1, $@;
return $@; return doCb($@);
} }
if(!$conn) { if(!$conn) {
Log3 $name, 3, "Can't connect to $dev: $!" if(!$reopen); Log3 $name, 3, "Can't connect to $dev: $!" if(!$reopen);
$readyfnlist{"$name.$dev"} = $hash; $readyfnlist{"$name.$dev"} = $hash;
DevIo_setStates($hash, "disconnected"); DevIo_setStates($hash, "disconnected");
return ""; return doCb("");
} }
$hash->{TCPDev} = $conn; $hash->{TCPDev} = $conn;
$hash->{FD} = $conn->fileno(); $hash->{FD} = $conn->fileno();
@ -250,11 +293,11 @@ DevIo_OpenDev($$$)
if (!CallFn($devName, "IOOpenFn", $hash)) { if (!CallFn($devName, "IOOpenFn", $hash)) {
Log3 $name, 3, "Can't open $dev!"; Log3 $name, 3, "Can't open $dev!";
DevIo_setStates($hash, "disconnected"); DevIo_setStates($hash, "disconnected");
return ""; return doCb("");
} }
} else { } else {
DevIo_setStates($hash, "disconnected"); DevIo_setStates($hash, "disconnected");
return ""; return doCb("");
} }
} elsif($dev =~ m/^(.+):([0-9]+)$/) { # host:port } elsif($dev =~ m/^(.+):([0-9]+)$/) { # host:port
@ -263,36 +306,63 @@ DevIo_OpenDev($$$)
# for non-existent devices has a delay of 3 sec, we are sitting all the # for non-existent devices has a delay of 3 sec, we are sitting all the
# time in this connect. NEXT_OPEN tries to avoid this problem. # time in this connect. NEXT_OPEN tries to avoid this problem.
if($hash->{NEXT_OPEN} && time() < $hash->{NEXT_OPEN}) { if($hash->{NEXT_OPEN} && time() < $hash->{NEXT_OPEN}) {
return; return doCb(undef);
} }
my $timeout = $hash->{TIMEOUT} ? $hash->{TIMEOUT} : 3; my $timeout = $hash->{TIMEOUT} ? $hash->{TIMEOUT} : 3;
my $conn = IO::Socket::INET->new(PeerAddr => $dev, Timeout => $timeout); sub
if($conn) { doTcpTail($)
delete($hash->{NEXT_OPEN}); {
$conn->setsockopt(SOL_SOCKET, SO_KEEPALIVE, 1) if(defined($conn)); my ($conn) = @_;
if($conn) {
delete($hash->{NEXT_OPEN});
$conn->setsockopt(SOL_SOCKET, SO_KEEPALIVE, 1) if(defined($conn));
} else { } else {
Log3 $name, 3, "Can't connect to $dev: $!" if(!$reopen); Log3 $name, 3, "Can't connect to $dev: $!" if(!$reopen);
$readyfnlist{"$name.$dev"} = $hash; $readyfnlist{"$name.$dev"} = $hash;
DevIo_setStates($hash, "disconnected"); DevIo_setStates($hash, "disconnected");
$hash->{NEXT_OPEN} = time()+60; $hash->{NEXT_OPEN} = time()+60;
return ""; return 0;
}
$hash->{TCPDev} = $conn;
$hash->{FD} = $conn->fileno();
delete($readyfnlist{"$name.$dev"});
$selectlist{"$name.$dev"} = $hash;
return 1;
} }
$hash->{TCPDev} = $conn; if($callback) {
$hash->{FD} = $conn->fileno(); use HttpUtils;
delete($readyfnlist{"$name.$dev"}); my $err = HttpUtils_Connect({ # Nonblocking
$selectlist{"$name.$dev"} = $hash; timeout => $timeout,
url => "http://$dev/", # not really http
NAME => $hash->{NAME},
noConn2 => 1,
callback=> sub() {
my ($h, $err, undef) = @_;
return $callback->($hash, $err) if($err);
return doCb("") if(!doTcpTail($h->{conn}));
return doCb(doTailWork());
}
});
return doCb($err) if($err);
return undef;
} else {
my $conn = IO::Socket::INET->new(PeerAddr => $dev, Timeout => $timeout);
return doCb("") if(!doTcpTail($conn));
}
} elsif($baudrate && lc($baudrate) eq "directio") { # w/o Device::SerialPort } elsif($baudrate && lc($baudrate) eq "directio") { # w/o Device::SerialPort
if(!open($po, "+<$dev")) { if(!open($po, "+<$dev")) {
return undef if($reopen); return doCb(undef) if($reopen);
Log3 $name, 3, "Can't open $dev: $!"; Log3 $name, 3, "Can't open $dev: $!";
$readyfnlist{"$name.$dev"} = $hash; $readyfnlist{"$name.$dev"} = $hash;
DevIo_setStates($hash, "disconnected"); DevIo_setStates($hash, "disconnected");
return ""; return doCb("");
} }
$hash->{DIODev} = $po; $hash->{DIODev} = $po;
@ -321,15 +391,15 @@ DevIo_OpenDev($$$)
} }
if($@) { if($@) {
Log3 $name, 1, $@; Log3 $name, 1, $@;
return $@; return doCb($@);
} }
if(!$po) { if(!$po) {
return undef if($reopen); return doCb(undef) if($reopen);
Log3 $name, 3, "Can't open $dev: $!"; Log3 $name, 3, "Can't open $dev: $!";
$readyfnlist{"$name.$dev"} = $hash; $readyfnlist{"$name.$dev"} = $hash;
DevIo_setStates($hash, "disconnected"); DevIo_setStates($hash, "disconnected");
return ""; return doCb("");
} }
$hash->{USBDev} = $po; $hash->{USBDev} = $po;
if( $^O =~ /Win/ ) { if( $^O =~ /Win/ ) {
@ -373,34 +443,7 @@ DevIo_OpenDev($$$)
$po->write_settings; $po->write_settings;
} }
DevIo_setStates($hash, "opened"); return doCb(doTailWork());
my $ret;
if($initfn) {
my $hadFD = defined($hash->{FD});
$ret = &$initfn($hash);
if($ret) {
if($hadFD && !defined($hash->{FD})) { # Forum #54732 / ser2net
DevIo_Disconnected($hash);
$hash->{NEXT_OPEN} = time()+60;
} else {
DevIo_CloseDev($hash);
Log3 $name, 1, "Cannot init $dev, ignoring it ($name)";
}
}
}
if(!$ret) {
if($reopen) {
Log3 $name, 1, "$dev reappeared ($name)";
} else {
Log3 $name, 3, "$name device opened" if(!$hash->{DevioText});
}
}
DoTrigger($name, "CONNECTED") if($reopen && !$ret);
return undef;
} }
sub sub

View File

@ -253,6 +253,8 @@ HttpUtils_Connect($)
return $hash->{callback}($hash, "$host: ".strerror($errno), "") return $hash->{callback}($hash, "$host: ".strerror($errno), "")
if($errno); if($errno);
return $hash->{callback}($hash,undef,undef) if($hash->{noConn2});
my $err = HttpUtils_Connect2($hash); my $err = HttpUtils_Connect2($hash);
$hash->{callback}($hash, $err, "") if($err); $hash->{callback}($hash, $err, "") if($err);
return $err; return $err;