From ab9fba9fcf7fae14d8bb5a693931d1fc66a875f5 Mon Sep 17 00:00:00 2001 From: ntruchsess Date: Fri, 8 Jan 2016 22:55:41 +0000 Subject: [PATCH] 10_FRM: Fix Errors when TCP-connection is lost (changes by jensb) git-svn-id: svn://svn.code.sf.net/p/fhem/code/trunk@10416 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/FHEM/10_FRM.pm | 151 +++++++++++++++++++++++++++----------------- 1 file changed, 94 insertions(+), 57 deletions(-) diff --git a/fhem/FHEM/10_FRM.pm b/fhem/FHEM/10_FRM.pm index dec377b25..52b912189 100755 --- a/fhem/FHEM/10_FRM.pm +++ b/fhem/FHEM/10_FRM.pm @@ -162,6 +162,12 @@ sub FRM_Notify { } } +##################################### +sub FRM_is_firmata_connected { + my ($hash) = @_; + return defined($hash->{FirmataDevice}) && defined ($hash->{FirmataDevice}->{io}); +} + ##################################### sub FRM_Set($@) { my ($hash, @a) = @_; @@ -173,7 +179,7 @@ sub FRM_Set($@) { COMMAND_HANDLER: { $command eq "reset" and do { - return $hash->{NAME}." is not connected" unless (defined $hash->{FirmataDevice} and (defined $hash->{FD} or ($^O=~/Win/ and defined $hash->{USBDev}))); + return $hash->{NAME}." is not connected" unless (FRM_is_firmata_connected($hash) && (defined $hash->{FD} or ($^O=~/Win/ and defined $hash->{USBDev}))); $hash->{FirmataDevice}->system_reset(); if (defined $hash->{SERVERSOCKET}) { # dispose preexisting connections @@ -210,20 +216,20 @@ sub FRM_Get($@) { my $cmd = shift @a; ARGUMENT_HANDLER: { $cmd eq "firmware" and do { - if (defined $hash->{FirmataDevice}) { + if (FRM_is_firmata_connected($hash)) { return $hash->{FirmataDevice}->{metadata}->{firmware}; } else { return "not connected to FirmataDevice"; } - }; - $cmd eq "version" and do { - if (defined $hash->{FirmataDevice}) { - return $hash->{FirmataDevice}->{metadata}->{firmware_version}; - } else { - return "not connected to FirmataDevice"; - } - }; - } + }; + $cmd eq "version" and do { + if (FRM_is_firmata_connected($hash)) { + return $hash->{FirmataDevice}->{metadata}->{firmware_version}; + } else { + return "not connected to FirmataDevice"; + } + }; + } } ##################################### @@ -278,7 +284,10 @@ sub FRM_Tcp_Connection_Close($) { TcpServer_Close($hash); if ($hash->{SNAME}) { my $shash = $main::defs{$hash->{SNAME}}; - $hash->{SocketDevice} = undef if (defined $shash); + if (defined $shash) { + $shash->{STATE}="listening"; + delete $shash->{SocketDevice} if (defined $shash->{SocketDevice}); + } } my $dev = $hash->{DeviceName}; my $name = $hash->{NAME}; @@ -354,7 +363,7 @@ sub FRM_DoInit($) { my $name = $shash->{NAME}; - my $firmata_io = Firmata_IO->new($hash,$name); + my $firmata_io = Firmata_IO->new($hash,$name); my $device = Device::Firmata::Platform->attach($firmata_io) or return 1; $shash->{FirmataDevice} = $device; @@ -667,7 +676,8 @@ sub FRM_I2C_Write { my ($hash,$package) = @_; - if (defined (my $firmata = $hash->{FirmataDevice})) { + if (FRM_is_firmata_connected($hash)) { + my $firmata = $hash->{FirmataDevice}; COMMANDHANDLER: { $package->{direction} eq "i2cwrite" and do { if (defined $package->{reg}) { @@ -730,36 +740,36 @@ sub FRM_string_observer sub FRM_poll { - my ($hash) = @_; - if (defined $hash->{SocketDevice} and defined $hash->{SocketDevice}->{FD}) { - my ($rout, $rin) = ('', ''); - vec($rin, $hash->{SocketDevice}->{FD}, 1) = 1; - my $nfound = select($rout=$rin, undef, undef, 0.1); - my $mfound = vec($rout, $hash->{SocketDevice}->{FD}, 1); - if($mfound && defined $hash->{FirmataDevice}) { - $hash->{FirmataDevice}->poll(); - } - return $mfound; - } elsif (defined $hash->{FD}) { - my ($rout, $rin) = ('', ''); - vec($rin, $hash->{FD}, 1) = 1; - my $nfound = select($rout=$rin, undef, undef, 0.1); - my $mfound = vec($rout, $hash->{FD}, 1); - if($mfound && defined $hash->{FirmataDevice}) { - $hash->{FirmataDevice}->poll(); - } - return $mfound; - } else { - # This is relevant for windows/USB only - my $po = $hash->{USBDev}; - my ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags); - if($po) { - ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags) = $po->status; - } - if ($InBytes && $InBytes>0 && defined $hash->{FirmataDevice}) { - $hash->{FirmataDevice}->poll(); - } - } + my ($hash) = @_; + if (defined $hash->{SocketDevice} and defined $hash->{SocketDevice}->{FD}) { + my ($rout, $rin) = ('', ''); + vec($rin, $hash->{SocketDevice}->{FD}, 1) = 1; + my $nfound = select($rout=$rin, undef, undef, 0.1); + my $mfound = vec($rout, $hash->{SocketDevice}->{FD}, 1); + if($mfound && FRM_is_firmata_connected($hash)) { + $hash->{FirmataDevice}->poll(); + } + return $mfound; + } elsif (defined $hash->{FD}) { + my ($rout, $rin) = ('', ''); + vec($rin, $hash->{FD}, 1) = 1; + my $nfound = select($rout=$rin, undef, undef, 0.1); + my $mfound = vec($rout, $hash->{FD}, 1); + if($mfound && FRM_is_firmata_connected($hash)) { + $hash->{FirmataDevice}->poll(); + } + return $mfound; + } else { + # This is relevant for windows/USB only + my $po = $hash->{USBDev}; + my ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags); + if($po) { + ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags) = $po->status; + } + if ($InBytes && $InBytes>0 && FRM_is_firmata_connected($hash)) { + $hash->{FirmataDevice}->poll(); + } + } } ######### following is code to be called from OWX: ########## @@ -1019,6 +1029,24 @@ sub FRM_OWX_Discover ($) { 1; +=pod + CHANGES + + 18.12.2015 jensb + o added sub FRM_is_firmata_connected + - extended connection check including {FirmataDevice}->{io} (gets + deleted by FRM_FirmataDevice_Close on TCP disconnect while FHEM + has still a valid reference to {FirmataDevice} when calling + I2CWrtFn) + o modified sub FRM_Set, FRM_Get, FRM_I2C_Write, FRM_poll: + - use sub FRM_is_firmata_connected to check if Firmata is still + connected before performing IO operations (to prevent FHEM crash) + o modified sub FRM_Tcp_Connection_Close: + - set STATE to listening and delete SocketDevice (to present same + idle state as FRM_Start) + o help updated +=cut + =pod =begin html @@ -1079,8 +1107,7 @@ sub FRM_OWX_Discover ($) { The Arduino has to run either 'StandardFirmata' or 'ConfigurableFirmata'. StandardFirmata supports Digital and Analog-I/O, Servo and I2C. In addition - to that ConfigurableFirmata supports 1-Wire, Stepper-motors and allows to - connect via ethernet in client mode.

+ to that ConfigurableFirmata supports 1-Wire and Stepper-motors.

You can find StandardFirmata in the Arduino-IDE under 'Examples->Firmata->StandardFirmata

ConfigurableFirmata has to be installed manualy. See @@ -1089,14 +1116,20 @@ sub FRM_OWX_Discover ($) {
  • Network-connected devices:

    <port> specifies the port the FRM device listens on. If global is - specified the socket is bound to all local ip-addresses, otherwise to localhost - only.
    - The Arduino must ConfigurableFirmata. The connection is initiated by the arduino - in client-mode. Therefor the ip-address and port of the fhem-server has to be - configured an the arduino, so it knows where to connect to.
    + specified the socket is bound to all local IP addresses, otherwise to localhost + only.

    + + The Arduino has to run either 'StandardFirmataEthernet' or 'ConfigurableFirmata'. + StandardFirmataEthernet supports Digital and Analog-I/O, Servo and I2C. In addition + to that ConfigurableFirmata supports 1-Wire and Stepper-motors.

    + + The connection is initiated by the Arduino in client-mode. Therefore the IP address and port + of the fhem-server has to be configured in the Arduino, so it knows where to connect to.
    As of now only a single Arduino per FRM-device configured is supported. Multiple - Arduinos may connect to different FRM-devices configured for different ports.
    - ConfigurableFirmata has to be installed manualy. See
    + Arduinos may connect to different FRM-devices configured for different ports.

    + + You can find StandardFirmataEthernet in the Arduino-IDE under 'Examples->Firmata->StandardFirmataEthernet

    + ConfigurableFirmata has to be installed manually. See
    ConfigurableFirmata on GitHub or FHEM-Wiki

  • @@ -1125,14 +1158,18 @@ sub FRM_OWX_Discover ($) { Attributes