2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-03-03 10:46:53 +00:00

HMCCU: Fixed RPC server start/stop

git-svn-id: https://svn.fhem.de/fhem/trunk@11059 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
fhemzap 2016-03-13 17:18:17 +00:00
parent 69545e89f7
commit 10e2e48faa

View File

@ -4,13 +4,13 @@
#
# $Id$
#
# Version 2.8
# Version 2.9
#
# (c) 2016 zap (zap01 <at> t-online <dot> de)
#
#########################################################################
#
# define <name> HMCCU <host_or_ip> [<read_interval>]
# define <name> HMCCU <hostname_or_ip_of_ccu>
#
# set <name> devstate <channel> <value> [...]
# set <name> datapoint {<device>|<channel>}.<datapoint> <value> [...]
@ -61,19 +61,22 @@ use warnings;
use SetExtensions;
use RPC::XML::Client;
# use File::Queue;
use Data::Dumper;
# use Data::Dumper;
use FindBin qw($Bin);
use lib "$Bin";
use RPCQueue;
# Initial interval for reading RPC queue
my $HMCCU_INIT_INTERVAL = 10;
# CCU Device names, key = CCU device address
my %HMCCU_Devices;
# CCU Device addresses, key = CCU device name
my %HMCCU_Addresses;
# Last update of device list
my $HMCCU_UpdateTime = 0;
# my $HMCCU_UpdateTime = 0;
# Last event from CCU
my $HMCCU_EventTime = 0;
# my $HMCCU_EventTime = 0;
# Flags for CCU object specification
my $HMCCU_FLAG_NAME = 1;
@ -199,6 +202,9 @@ sub HMCCU_Define ($$)
$hash->{NewDevices} = 0;
$hash->{DelDevices} = 0;
$hash->{RPCState} = "stopped";
$hash->{hmccu}{eventtime} = 0;
$hash->{hmccu}{updatetime} = 0;
return undef;
}
@ -213,11 +219,23 @@ sub HMCCU_Attr ($@)
my $hash = $defs{$name};
if (defined ($attrval) && $cmd eq "set") {
if ($attrname eq "rpcserver") {
if ($attrname eq "rpcport") {
foreach my $port (split (",", $attrval)) {
my $clkey = 'rpccl'.$port;
$hash->{hmccu}{$clkey}{cburl} = '';
}
}
elsif ($attrname eq 'rpcport') {
my @hm_pids;
my @ex_pids;
if (HMCCU_IsRPCServerRunning ($hash, \@hm_pids, \@ex_pids)) {
return "HMCCU: Restart RPC server for port changes to take effect";
}
}
elsif ($attrname eq "rpcserver") {
if ($attrval eq 'on') {
if (HMCCU_StartRPCServer ($hash)) {
InternalTimer (gettimeofday()+60,
'HMCCU_ReadRPCQueue', $hash, 0);
InternalTimer (gettimeofday()+$HMCCU_INIT_INTERVAL, 'HMCCU_ReadRPCQueue', $hash, 0);
}
}
elsif ($attrval eq 'off') {
@ -950,6 +968,7 @@ sub HMCCU_SubstRule ($$$)
sub HMCCU_UpdateClients ($$$$)
{
my ($hash, $devexp, $ccuget, $fromccu) = @_;
my $fhname = $hash->{NAME};
my $c_ok = 0;
my $c_err = 0;
@ -966,6 +985,7 @@ sub HMCCU_UpdateClients ($$$$)
my $rc = HMCCU_GetUpdate ($ch, $HMCCU_Addresses{$name}{address}, $ccuget);
if ($rc <= 0) {
Log3 $fhname, 1, "HMCCU: Update of device $name failed";
$c_err++;
}
else {
@ -1165,6 +1185,9 @@ sub HMCCU_StartRPCServer ($)
my $logfile = $modpath."/log/ccurpcd";
my $rpcqueue = AttrVal ($name, 'rpcqueue', '/tmp/ccuqueue');
my $rpcport = AttrVal ($name, 'rpcport', '2001');
my $serveraddr = $hash->{host};
my $localaddr = '';
my @hm_pids;
my @ex_pids;
@ -1184,12 +1207,36 @@ sub HMCCU_StartRPCServer ($)
Log3 $name, 1, "HMCCU: RPC server file ccurpcd.pl not found";
return 0;
}
# Clear RPC queues
HMCCU_ResetRPCQueue ($hash);
# Fork child process(es)
foreach my $port (split (',', $rpcport)) {
my $callbackport = 5400+$port;
# Detect local IP
if ($localaddr eq '') {
my $socket = IO::Socket::INET->new (PeerAddr => $serveraddr, PeerPort => $port);
if (!$socket) {
Log3 $name, 1, "Can't connect to CCU port $port";
next;
}
$localaddr = $socket->sockhost ();
close ($socket);
}
# Create RPC client
my $clkey = 'rpccl'.$port;
my $rpcclient = RPC::XML::Client->new ("http://$serveraddr:$port/");
# Check if RPC daemon on CCU is listening on port
my $resp = $rpcclient->send_request ('system.listMethods');
if (!ref($resp)) {
Log3 $name, 1, "No response from CCU on port $port. $resp";
next;
}
my $rpcqueueport = $rpcqueue."_".$port;
my $logfileport = $logfile."_".$port.".log";
@ -1200,15 +1247,27 @@ sub HMCCU_StartRPCServer ($)
}
if (!$pid) {
# Child process, replace it by RPC server
exec ($rpcserver." ".$hash->{host}." ".$port." ".$rpcqueueport." ".$logfileport);
# Child process. Replaced by RPC server
exec ($rpcserver." ".$serveraddr." ".$port." ".$rpcqueueport." ".$logfileport);
# When we reach this line start of RPC server failed and child
# process can exit.
# When we reach this line start of RPC server failed and child process can exit
die;
}
# Parent process
# Wait 2 seconds to ensure that RPC server is listening
sleep (2);
# Register callback in CCU
my $callbackurl = "http://".$localaddr.":".$callbackport."/fh".$port;
$hash->{hmccu}{$clkey}{cburl} = $callbackurl;
Log3 $name, 1, "HMCCU: Registering callback $callbackurl with ID CB".$port;
$rpcclient->send_request ("init", $callbackurl, "CB".$port);
Log3 $name, 1, "HMCCU: RPC callback with URL ".$callbackurl." initialized";
# Store PID
push (@hm_pids, $pid);
$hash->{hmccu}{$clkey}{pid} = $pid;
Log3 $name, 0, "HMCCU: RPC server started with pid ".$pid;
}
@ -1221,7 +1280,7 @@ sub HMCCU_StartRPCServer ($)
}
####################################################
# Stop RPC server / send SIGINT to process(es)
# Stop RPC server(s) / send SIGINT to process(es)
####################################################
sub HMCCU_StopRPCServer ($)
@ -1229,6 +1288,21 @@ sub HMCCU_StopRPCServer ($)
my ($hash) = @_;
my $name = $hash->{NAME};
my $rpcport = AttrVal ($name, 'rpcport', '2001');
my $serveraddr = $hash->{host};
# Deregister callback URLs in CCU
foreach my $port (split (',', $rpcport)) {
my $clkey = 'rpccl'.$port;
if (exists ($hash->{hmccu}{$clkey}{cburl}) && $hash->{hmccu}{$clkey}{cburl} ne '') {
my $rpcclient = RPC::XML::Client->new ("http://$serveraddr:$port/");
Log3 $name, 1, "HMCCU: Deregistering RPC server ".$hash->{hmccu}{$clkey}{cburl};
$rpcclient->send_request("init", $hash->{hmccu}{$clkey}{cburl});
$hash->{hmccu}{$clkey}{cburl} = '';
}
}
# Send signal SIGINT to RPC server processes
my @hm_pids;
my @ex_pids;
HMCCU_IsRPCServerRunning ($hash, \@hm_pids, \@ex_pids);
@ -1237,18 +1311,16 @@ sub HMCCU_StopRPCServer ($)
Log3 $name, 0, "HMCCU: Stopping RPC server with PID $pid";
kill ('INT', $pid);
}
# $hash->{RPCPID} = '0';
# $hash->{RPCPRC} = 'none';
$hash->{RPCState} = "stopping";
}
else {
Log3 $name, 1, "HMCCU: RPC server not running";
Log3 $name, 0, "HMCCU: RPC server not running";
return 0;
}
if (@ex_pids > 0) {
foreach my $pid (@ex_pids) {
Log3 $name, 1, "HMCCU: Externally launched RPC server detected. Kill process manually with command kill -SIGINT $pid";
Log3 $name, 0, "HMCCU: Externally launched RPC server detected. Kill process manually with command kill -SIGINT $pid";
}
return 0;
}
@ -1325,22 +1397,26 @@ sub HMCCU_CheckProcess ($$)
my $modpath = AttrVal ('global', 'modpath', '/opt/fhem');
my $rpcserver = $modpath."/FHEM/ccurpcd.pl";
my $pdump = `ps -ef | grep $rpcserver | grep -v grep`;
# Using BDS syntax. Supported by Debian, MacOS and FreeBSD
my $pdump = `ps ax | grep $rpcserver | grep -v grep`;
my @plist = split "\n", $pdump;
foreach my $proc (@plist) {
# Remove leading blanks, fix for MacOS. Thanks to mcdeck
$proc =~ s/^\s+//;
my @procattr = split /\s+/, $proc;
return $procattr[1] if ($procattr[1] != $$ && $procattr[7] =~ /perl$/ &&
$procattr[8] eq $rpcserver && $procattr[10] eq "$port");
# return $procattr[1] if ($procattr[1] != $$ && $procattr[7] =~ /perl$/ &&
# $procattr[8] eq $rpcserver && $procattr[10] eq "$port");
return $procattr[0] if ($procattr[0] != $$ && $procattr[4] =~ /perl$/ &&
$procattr[5] eq $rpcserver && $procattr[7] eq "$port");
}
return 0;
}
####################################################
# Get channel and datapoints of CCU device
# Get channels and datapoints of CCU device
####################################################
sub HMCCU_GetDeviceInfo ($$$)
@ -1423,7 +1499,7 @@ foreach(devid, root.Devices().EnumUsedIDs()) {
%HMCCU_Devices = ();
%HMCCU_Addresses = ();
$HMCCU_UpdateTime = time ();
$hash->{hmccu}{updatetime} = time ();
foreach my $hmdef (split /\n/,$response) {
my @hmdata = split /;/,$hmdef;
@ -1844,15 +1920,23 @@ sub HMCCU_ReadRPCQueue ($)
my $element = $queue->deq();
while ($element) {
$HMCCU_EventTime = time () if ($eventno == 0);
$hash->{hmccu}{eventtime} = time () if ($eventno == 0);
my @Tokens = split (/\|/, $element);
if ($Tokens[0] eq 'EV') {
#### Event ####
my ($add, $chn) = split (/:/, $Tokens[1]);
my $reading = HMCCU_GetReadingName ('', $add, $chn, $Tokens[2], '',
$ccureadingformat);
HMCCU_UpdateClientReading ($hash, $add, $chn, $reading, $Tokens[3], 'rpcevent');
$eventno++;
# Handle special events
if ($Tokens[1] eq 'CENTRAL' && $Tokens[2] eq 'PONG') {
Log3 $name, 2, "HMCCU: Received PONG event from ".$Tokens[3];
}
else {
# Standard event
my ($add, $chn) = split (/:/, $Tokens[1]);
my $reading = HMCCU_GetReadingName ('', $add, $chn, $Tokens[2], '',
$ccureadingformat);
HMCCU_UpdateClientReading ($hash, $add, $chn, $reading, $Tokens[3], 'rpcevent');
}
last if ($eventno == $maxevents);
}
elsif ($Tokens[0] eq 'ND') {
@ -1872,7 +1956,7 @@ sub HMCCU_ReadRPCQueue ($)
$hash->{RPCState} = "running";
HMCCU_SetState ($hash, "OK");
DoTrigger ($name, "RPC server running");
# my ($c_ok, $c_err) = HMCCU_UpdateClients ($hash, '.*', 'Attr');
my ($c_ok, $c_err) = HMCCU_UpdateClients ($hash, '.*', 'Attr', 0);
}
elsif ($Tokens[0] eq 'EX') {
#### RPC Server shut down ####
@ -1898,7 +1982,7 @@ sub HMCCU_ReadRPCQueue ($)
}
# Check if RPC server still running if events from CCU timed out
if ($HMCCU_EventTime > 0 && time()-$HMCCU_EventTime > $rpctimeout) {
if ($hash->{hmccu}{eventtime} > 0 && time()-$hash->{hmccu}{eventtime} > $rpctimeout) {
Log3 $name, 2, "HMCCU: Received no events from CCU since $rpctimeout seconds";
DoTrigger ($name, "No events from CCU since $rpctimeout seconds");
}
@ -1935,9 +2019,9 @@ sub HMCCU_ReadRPCQueue ($)
if ($f == 2 && $nhm_pids == 0) {
# All RPC servers terminated and restart flag set
$HMCCU_EventTime = 0;
$hash->{hmccu}{eventtime} = 0;
if (HMCCU_StartRPCServer ($hash)) {
InternalTimer (gettimeofday()+60, 'HMCCU_ReadRPCQueue', $hash, 0);
InternalTimer (gettimeofday()+$HMCCU_INIT_INTERVAL, 'HMCCU_ReadRPCQueue', $hash, 0);
return;
}
else {
@ -2249,9 +2333,9 @@ if (odev) {
my ($da, $cno) = HMCCU_SplitChnAddr ($cl_hash->{ccuaddr});
foreach my $dn (sort keys %defs) {
my $ch = $defs{$dn};
my @vdevs = split (",", $ch->{ccugroup});
next if ($ch->{TYPE} ne 'HMCCUDEV');
next if ($ch->{ccuif} ne "VirtualDevices" || !exists ($ch->{ccugroup}));
my @vdevs = split (",", $ch->{ccugroup});
if ($da ~~ @vdevs || ($cno ne '' && $cl_hash->{ccuaddr} ~~ @vdevs)) {
HMCCU_UpdateDeviceReadings ($ch, \@dpdef);
}
@ -2472,7 +2556,7 @@ sub HMCCU_RPCGetConfig ($$$$)
}
}
else {
return (-2, '');
return (-2, defined ($RPC::XML::ERROR) ? $RPC::XML::ERROR : '');
}
if ($mode eq 'getParamsetDescription') {
@ -2550,11 +2634,14 @@ sub HMCCU_RPCSetConfig ($$$)
Log3 $name, 1, "HMCCU: ".$res->{faultString};
return -2;
}
elsif ($res eq '') {
return -2;
else {
return 0;
}
}
else {
return -2;
}
return 0;
}