From 332d481e378a243ab300cb4e329d1abd1e33238b Mon Sep 17 00:00:00 2001 From: stan Date: Sun, 15 Dec 2002 14:09:53 +0000 Subject: [PATCH] Added logging, converted to .in, made server a package and added start command. git-svn-id: http://svn.zoneminder.com/svn/zm/trunk@197 e3e1d417-86f3-4887-817a-d78f3d33393f --- scripts/zmx10.pl.z | 772 +++++++++++++++++++++++---------------------- 1 file changed, 402 insertions(+), 370 deletions(-) diff --git a/scripts/zmx10.pl.z b/scripts/zmx10.pl.z index 01eceda99..347c6807d 100755 --- a/scripts/zmx10.pl.z +++ b/scripts/zmx10.pl.z @@ -39,6 +39,7 @@ use constant X10_DEVICE => '/dev/ttyS1'; use constant X10_HOUSE_CODE => 'K'; use constant X10_SOCK_FILE => '/tmp/zmx10.sock'; use constant X10_LOG_FILE => '/tmp/zmx10.log'; +use constant COMMAND_PATH => '@prefix@/bin/'; # ========================================================================== # @@ -48,11 +49,9 @@ use constant X10_LOG_FILE => '/tmp/zmx10.log'; use strict; use POSIX; -use DBI; use Socket; use Getopt::Long; use Data::Dumper; -use X10::ActiveHome; $| = 1; @@ -76,7 +75,13 @@ if ( !GetOptions( 'command=s'=>\$command, 'unit-code=i'=>\$unit_code ) ) } die( "No command given" ) unless( $command ); -die( "No unit code given" ) unless( $unit_code || ($command eq 'status' || $command eq 'shutdown') ); +die( "No unit code given" ) unless( $unit_code || ($command =~ /(?:start|status|shutdown)/) ); + +if ( $command eq "start" ) +{ + X10Server::runServer(); + exit(); +} socket( CLIENT, PF_UNIX, SOCK_STREAM, 0 ) or die( "Can't open socket: $!" ); @@ -99,373 +104,7 @@ if ( !connect( CLIENT, $saddr ) ) { setpgrp(); - open( LOG, '>>'.X10_LOG_FILE ) or die( "Can't open log file: $!" ); - select( LOG ); - $| = 1; - print( LOG "X10 server starting at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" ); - - socket( SERVER, PF_UNIX, SOCK_STREAM, 0 ) or die( "Can't open socket: $!" ); - unlink( X10_SOCK_FILE ); - bind( SERVER, $saddr ) or die( "Can't bind: $!" ); - listen( SERVER, SOMAXCONN ) or die( "Can't listen: $!" ); - - ( $ENV{PATH} ) = ( $ENV{PATH} =~ /^(.*)$/ ); - - my $dbh = DBI->connect( "DBI:mysql:".DB_NAME, DB_USER, DB_PASS ); - - my %device_hash; - my %pending_tasks; - - sub addToActivationList - { - my $unit_code = shift; - my $event = shift; - my $monitor = shift; - my $function = shift; - my $limit = shift; - - #print( "Adding to activation list, uc:$unit_code, ev:$event, mo:$monitor, fu:$function, li:$limit\n" ); - my $device = $device_hash{$unit_code}; - if ( !$device ) - { - $device = $device_hash{$unit_code} = { status=>'unknown' }; - } - - my $task = { monitor=>$monitor->{Id}, function=>$function }; - if ( $limit ) - { - $task->{limit} = $limit - } - - my $task_list = $device->{$event."_list"}; - if ( !$task_list ) - { - $task_list = $device->{$event."_list"} = []; - } - push( @$task_list, $task ); - } - - sub loadTasks - { - my %monitor_hash; - - #print( "Loading tasks\n" ); - # Clear out all old device task lists - foreach my $unit_code ( sort( keys(%device_hash) ) ) - { - my $device = $device_hash{$unit_code}; - $device->{ON_list} = []; - $device->{OFF_list} = []; - } - - my $sql = "select * from Monitors"; - my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() ); - while( my $monitor = $sth->fetchrow_hashref() ) - { - $monitor_hash{$monitor->{Id}} = $monitor; - if ( $monitor->{Function} eq 'X10' && $monitor->{X10Active} ) - { - #print( "$monitor->{Name} has active string '$monitor->{X10Active}'\n" ); - foreach my $code_string ( split( ',', $monitor->{X10Active} ) ) - { - #print( "Code string: $code_string\n" ); - my ( $invert, $unit_code, $modifier, $limit ) = ( $code_string =~ /^([!~])?(\d+)(?:([+-])(\d+)?)?$/ ); - $limit = 0 if ( !$limit ); - if ( $unit_code ) - { - if ( !$modifier || $modifier eq '+' ) - { - addToActivationList( $unit_code, "ON", $monitor, !$invert?"start_active":"stop_active", $limit ); - } - if ( !$modifier || $modifier eq '-' ) - { - addToActivationList( $unit_code, "OFF", $monitor, !$invert?"stop_active":"start_active", $limit ); - } - } - } - } - if ( $monitor->{X10Alarm} ) - { - #print( "$monitor->{Name} has alarm string '$monitor->{X10Alarm}'\n" ); - foreach my $code_string ( split( ',', $monitor->{X10Alarm} ) ) - { - #print( "Code string: $code_string\n" ); - my ( $invert, $unit_code, $modifier, $limit ) = ( $code_string =~ /^([!~])?(\d+)(?:([+-])(\d+)?)?$/ ); - $limit = 0 if ( !$limit ); - if ( $unit_code ) - { - if ( !$modifier || $modifier eq '+' ) - { - addToActivationList( $unit_code, "ON", $monitor, !$invert?"start_alarm":"stop_alarm", $limit ); - } - if ( !$modifier || $modifier eq '-' ) - { - addToActivationList( $unit_code, "OFF", $monitor, !$invert?"stop_alarm":"start_alarm", $limit ); - } - } - } - } - } - } - - sub addPendingTask - { - my $task = shift; - - # Check whether we are just extending a previous pending task - # and remove it if it's there - foreach my $activation_time ( sort(keys(%pending_tasks) ) ) - { - my $pending_list = $pending_tasks{$activation_time}; - my $new_pending_list = []; - foreach my $pending_task ( @$pending_list ) - { - if (( $task->{monitor}->{Id} != $pending_task->{monitor}->{Id} ) - || ( $task->{function} != $pending_task->{function} )) - { - push( @$new_pending_list, $pending_task ) - } - } - if ( @$new_pending_list ) - { - $pending_tasks{$activation_time} = $new_pending_list; - } - else - { - delete( $pending_tasks{$activation_time} ); - } - } - - my $end_time = time() + $task->{limit}; - my $pending_list = $pending_tasks{$end_time}; - if ( !$pending_list ) - { - $pending_list = $pending_tasks{$end_time} = []; - } - $task->{function} =~ s/start/stop/; - delete( $task->{limit} ); - push( @$pending_list, $task ); - } - - sub processTask - { - my $task = shift; - - my ( $instruction, $class ) = ( $task->{function} =~ /^(.+)_(.+)$/ ); - - my $command; - if ( $class eq "active" ) - { - if ( $instruction eq "start" ) - { - $command = "zmdc.pl start zma ".$task->{monitor}; - if ( $task->{limit} ) - { - addPendingTask( $task ); - } - } - elsif( $instruction eq "stop" ) - { - $command = "zmdc.pl stop zma ".$task->{monitor}; - } - } - elsif( $class eq "alarm" ) - { - if ( $instruction eq "start" ) - { - $command = "zmu --monitor ".$task->{monitor}." --alarm"; - if ( $task->{limit} ) - { - addPendingTask( $task ); - } - } - elsif( $instruction eq "stop" ) - { - $command = "zmu --monitor ".$task->{monitor}." --cancel"; - } - } - print( "Executing command '$command'\n" ); - if ( $command ) - { - qx( $command ); - } - } - - sub dprint - { - if ( fileno(CLIENT) ) - { - print CLIENT @_ - } - else - { - print @_; - } - } - - my $x10 = new X10::ActiveHome( port=>X10_DEVICE, house_code=>X10_HOUSE_CODE, debug=>1 ); - - loadTasks(); - - sub x10listen - { - foreach my $event ( @_ ) - { - #print( Dumper( $_ )."\n" ); - if ( $event->house_code() eq X10_HOUSE_CODE ) - { - my $unit_code = $event->unit_code(); - my $device = $device_hash{$unit_code}; - if ( !$device ) - { - $device = $device_hash{$unit_code} = { appliance=>$x10->Appliance( unit_code=>$unit_code ) }; - } - next if ( $event->func() !~ /(?:ON|OFF)/ ); - $device->{status} = $event->func(); - my $task_list = $device->{$event->func()."_list"}; - if ( $task_list ) - { - foreach my $task ( @$task_list ) - { - processTask( $task ); - } - } - } - print( LOG strftime( "%y/%m/%d %H:%M:%S", localtime() )." - ".$event->as_string()."\n" ); - } - } - - $x10->register_listener( \&x10listen ); - - my $rin = ''; - vec( $rin, fileno(SERVER),1) = 1; - vec( $rin, $x10->select_fds(),1) = 1; - my $timeout = 1; - #print( "F:".fileno(SERVER)."\n" ); - while( 1 ) - { - my $nfound = select( my $rout = $rin, undef, undef, $timeout ); - #print( "Off select, NF:$nfound, ER:$!\n" ); - #print( vec( $rout, fileno(SERVER),1)."\n" ); - #print( vec( $rout, $x10->select_fds(),1)."\n" ); - if ( $nfound > 0 ) - { - if ( vec( $rout, fileno(SERVER),1) ) - { - my $paddr = accept( CLIENT, SERVER ); - my $message = ; - - my ( $command, $unit_code ) = split( ';', $message ); - - my $device; - if ( defined($unit_code) ) - { - if ( $unit_code < 1 || $unit_code > 16 ) - { - dprint( "Error, invalid unit code '$unit_code'\n" ); - next; - } - - $device = $device_hash{$unit_code}; - if ( !$device ) - { - $device = $device_hash{$unit_code} = { appliance=>$x10->Appliance( unit_code=>$unit_code ), status=>'unknown' }; - } - } - - my $result; - if ( $command eq 'on' ) - { - $result = $device->{appliance}->on(); - } - elsif ( $command eq 'off' ) - { - $result = $device->{appliance}->off(); - } - #elsif ( $command eq 'dim' ) - #{ - #$result = $device->{appliance}->dim(); - #} - #elsif ( $command eq 'bright' ) - #{ - #$result = $device->{appliance}->bright(); - #} - elsif ( $command eq 'status' ) - { - if ( $device ) - { - dprint( $unit_code." ".$device->{status}."\n" ); - } - else - { - foreach my $unit_code ( sort( keys(%device_hash) ) ) - { - my $device = $device_hash{$unit_code}; - dprint( $unit_code." ".$device->{status}."\n" ); - } - } - } - elsif ( $command eq 'shutdown' ) - { - last; - } - else - { - dprint( "Error, invalid command '$command'\n" ); - } - if ( defined($result) ) - { - if ( 1 || $result ) - { - $device->{status} = $command; - dprint( $device->{appliance}->address()." $command, ok\n" ); - } - else - { - dprint( $device->{appliance}->address()." $command, failed\n" ); - } - } - close( CLIENT ); - } - elsif ( vec( $rout, $x10->select_fds(),1) ) - { - $x10->handle_input(); - } - else - { - die( "Bogus descriptor" ); - } - } - elsif ( $nfound < 0 ) - { - die( "Can't select: $!" ); - } - else - { - #print( "Select timed out\n" ); - # Check for pending tasks - my $now = time(); - foreach my $activation_time ( sort(keys(%pending_tasks) ) ) - { - last if ( $activation_time > $now ); - my $pending_list = $pending_tasks{$activation_time}; - foreach my $task ( @$pending_list ) - { - processTask( $task ); - } - delete( $pending_tasks{$activation_time} ); - } - if ( !($now%DB_RELOAD_INTERVAL) ) - { - loadTasks(); - } - } - } - print( LOG "X10 server exiting at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" ); - close( LOG ); - close( SERVER ); - exit(); + X10Server::runServer(); } else { @@ -486,3 +125,396 @@ while ( my $line = ) } close( CLIENT ); #print( "Finished writing, bye\n" ); +exit; + +# +# ========================================================================== +# +# This is the X10 Server package +# +# ========================================================================== +# +package X10Server; + +use strict; +use POSIX; +use DBI; +use Socket; +use X10::ActiveHome; + +our $dbh; +our $x10; + +our %device_hash; +our %pending_tasks; + +sub runServer +{ + open( LOG, '>>'.main::X10_LOG_FILE ) or die( "Can't open log file: $!" ); + select( LOG ); + $| = 1; + print( LOG "X10 server starting at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" ); + + socket( SERVER, PF_UNIX, SOCK_STREAM, 0 ) or die( "Can't open socket: $!" ); + unlink( main::X10_SOCK_FILE ); + my $saddr = sockaddr_un( main::X10_SOCK_FILE ); + bind( SERVER, $saddr ) or die( "Can't bind: $!" ); + listen( SERVER, SOMAXCONN ) or die( "Can't listen: $!" ); + + ( $ENV{PATH} ) = ( $ENV{PATH} =~ /^(.*)$/ ); + + $dbh = DBI->connect( "DBI:mysql:".main::DB_NAME, main::DB_USER, main::DB_PASS ); + + $x10 = new X10::ActiveHome( port=>main::X10_DEVICE, house_code=>main::X10_HOUSE_CODE, debug=>1 ); + + loadTasks(); + + $x10->register_listener( \&x10listen ); + + my $rin = ''; + vec( $rin, fileno(SERVER),1) = 1; + vec( $rin, $x10->select_fds(),1) = 1; + my $timeout = 1; + #print( "F:".fileno(SERVER)."\n" ); + while( 1 ) + { + my $nfound = select( my $rout = $rin, undef, undef, $timeout ); + #print( "Off select, NF:$nfound, ER:$!\n" ); + #print( vec( $rout, fileno(SERVER),1)."\n" ); + #print( vec( $rout, $x10->select_fds(),1)."\n" ); + if ( $nfound > 0 ) + { + if ( vec( $rout, fileno(SERVER),1) ) + { + my $paddr = accept( CLIENT, SERVER ); + my $message = ; + + my ( $command, $unit_code ) = split( ';', $message ); + + my $device; + if ( defined($unit_code) ) + { + if ( $unit_code < 1 || $unit_code > 16 ) + { + dprint( "Error, invalid unit code '$unit_code'\n" ); + next; + } + + $device = $device_hash{$unit_code}; + if ( !$device ) + { + $device = $device_hash{$unit_code} = { appliance=>$x10->Appliance( unit_code=>$unit_code ), status=>'unknown' }; + } + } + + my $result; + if ( $command eq 'on' ) + { + $result = $device->{appliance}->on(); + } + elsif ( $command eq 'off' ) + { + $result = $device->{appliance}->off(); + } + #elsif ( $command eq 'dim' ) + #{ + #$result = $device->{appliance}->dim(); + #} + #elsif ( $command eq 'bright' ) + #{ + #$result = $device->{appliance}->bright(); + #} + elsif ( $command eq 'status' ) + { + if ( $device ) + { + dprint( $unit_code." ".$device->{status}."\n" ); + } + else + { + foreach my $unit_code ( sort( keys(%device_hash) ) ) + { + my $device = $device_hash{$unit_code}; + dprint( $unit_code." ".$device->{status}."\n" ); + } + } + } + elsif ( $command eq 'shutdown' ) + { + last; + } + else + { + dprint( "Error, invalid command '$command'\n" ); + } + if ( defined($result) ) + { + if ( 1 || $result ) + { + $device->{status} = $command; + dprint( $device->{appliance}->address()." $command, ok\n" ); + } + else + { + dprint( $device->{appliance}->address()." $command, failed\n" ); + } + } + close( CLIENT ); + } + elsif ( vec( $rout, $x10->select_fds(),1) ) + { + $x10->handle_input(); + } + else + { + die( "Bogus descriptor" ); + } + } + elsif ( $nfound < 0 ) + { + die( "Can't select: $!" ); + } + else + { + #print( "Select timed out\n" ); + # Check for pending tasks + my $now = time(); + foreach my $activation_time ( sort(keys(%pending_tasks) ) ) + { + last if ( $activation_time > $now ); + my $pending_list = $pending_tasks{$activation_time}; + foreach my $task ( @$pending_list ) + { + processTask( $task ); + } + delete( $pending_tasks{$activation_time} ); + } + if ( !($now%main::DB_RELOAD_INTERVAL) ) + { + loadTasks(); + } + } + } + print( LOG "X10 server exiting at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" ); + close( LOG ); + close( SERVER ); + exit(); +} + +sub addToActivationList +{ + my $unit_code = shift; + my $event = shift; + my $monitor = shift; + my $function = shift; + my $limit = shift; + + #print( "Adding to activation list, uc:$unit_code, ev:$event, mo:$monitor, fu:$function, li:$limit\n" ); + my $device = $device_hash{$unit_code}; + if ( !$device ) + { + $device = $device_hash{$unit_code} = { status=>'unknown' }; + } + + my $task = { monitor=>$monitor->{Id}, function=>$function }; + if ( $limit ) + { + $task->{limit} = $limit + } + + my $task_list = $device->{$event."_list"}; + if ( !$task_list ) + { + $task_list = $device->{$event."_list"} = []; + } + push( @$task_list, $task ); +} + +sub loadTasks +{ + my %monitor_hash; + + #print( "Loading tasks\n" ); + # Clear out all old device task lists + foreach my $unit_code ( sort( keys(%device_hash) ) ) + { + my $device = $device_hash{$unit_code}; + $device->{ON_list} = []; + $device->{OFF_list} = []; + } + + my $sql = "select * from Monitors"; + my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() ); + while( my $monitor = $sth->fetchrow_hashref() ) + { + $monitor_hash{$monitor->{Id}} = $monitor; + if ( $monitor->{Function} eq 'X10' && $monitor->{X10Active} ) + { + #print( "$monitor->{Name} has active string '$monitor->{X10Active}'\n" ); + foreach my $code_string ( split( ',', $monitor->{X10Active} ) ) + { + #print( "Code string: $code_string\n" ); + my ( $invert, $unit_code, $modifier, $limit ) = ( $code_string =~ /^([!~])?(\d+)(?:([+-])(\d+)?)?$/ ); + $limit = 0 if ( !$limit ); + if ( $unit_code ) + { + if ( !$modifier || $modifier eq '+' ) + { + addToActivationList( $unit_code, "ON", $monitor, !$invert?"start_active":"stop_active", $limit ); + } + if ( !$modifier || $modifier eq '-' ) + { + addToActivationList( $unit_code, "OFF", $monitor, !$invert?"stop_active":"start_active", $limit ); + } + } + } + } + if ( $monitor->{X10Alarm} ) + { + #print( "$monitor->{Name} has alarm string '$monitor->{X10Alarm}'\n" ); + foreach my $code_string ( split( ',', $monitor->{X10Alarm} ) ) + { + #print( "Code string: $code_string\n" ); + my ( $invert, $unit_code, $modifier, $limit ) = ( $code_string =~ /^([!~])?(\d+)(?:([+-])(\d+)?)?$/ ); + $limit = 0 if ( !$limit ); + if ( $unit_code ) + { + if ( !$modifier || $modifier eq '+' ) + { + addToActivationList( $unit_code, "ON", $monitor, !$invert?"start_alarm":"stop_alarm", $limit ); + } + if ( !$modifier || $modifier eq '-' ) + { + addToActivationList( $unit_code, "OFF", $monitor, !$invert?"stop_alarm":"start_alarm", $limit ); + } + } + } + } + } +} + +sub addPendingTask +{ + my $task = shift; + + # Check whether we are just extending a previous pending task + # and remove it if it's there + foreach my $activation_time ( sort(keys(%pending_tasks) ) ) + { + my $pending_list = $pending_tasks{$activation_time}; + my $new_pending_list = []; + foreach my $pending_task ( @$pending_list ) + { + if (( $task->{monitor}->{Id} != $pending_task->{monitor}->{Id} ) + || ( $task->{function} != $pending_task->{function} )) + { + push( @$new_pending_list, $pending_task ) + } + } + if ( @$new_pending_list ) + { + $pending_tasks{$activation_time} = $new_pending_list; + } + else + { + delete( $pending_tasks{$activation_time} ); + } + } + + my $end_time = time() + $task->{limit}; + my $pending_list = $pending_tasks{$end_time}; + if ( !$pending_list ) + { + $pending_list = $pending_tasks{$end_time} = []; + } + $task->{function} =~ s/start/stop/; + delete( $task->{limit} ); + push( @$pending_list, $task ); +} + +sub processTask +{ + my $task = shift; + + my ( $instruction, $class ) = ( $task->{function} =~ /^(.+)_(.+)$/ ); + + my $command; + if ( $class eq "active" ) + { + if ( $instruction eq "start" ) + { + $command = COMMAND_PATH."zmdc.pl start zma ".$task->{monitor}; + if ( $task->{limit} ) + { + addPendingTask( $task ); + } + } + elsif( $instruction eq "stop" ) + { + $command = COMMAND_PATH."zmdc.pl stop zma ".$task->{monitor}; + } + } + elsif( $class eq "alarm" ) + { + if ( $instruction eq "start" ) + { + $command = COMMAND_PATH."zmu --monitor ".$task->{monitor}." --alarm"; + if ( $task->{limit} ) + { + addPendingTask( $task ); + } + } + elsif( $instruction eq "stop" ) + { + $command = COMMAND_PATH."zmu --monitor ".$task->{monitor}." --cancel"; + } + } + print( "Executing command '$command'\n" ); + if ( $command ) + { + qx( $command ); + } +} + +sub dprint +{ + if ( fileno(CLIENT) ) + { + print CLIENT @_ + } + else + { + print @_; + } +} + +sub x10listen +{ + foreach my $event ( @_ ) + { + #print( Dumper( $_ )."\n" ); + if ( $event->house_code() eq main::X10_HOUSE_CODE ) + { + my $unit_code = $event->unit_code(); + my $device = $device_hash{$unit_code}; + if ( !$device ) + { + $device = $device_hash{$unit_code} = { appliance=>$x10->Appliance( unit_code=>$unit_code ) }; + } + next if ( $event->func() !~ /(?:ON|OFF)/ ); + $device->{status} = $event->func(); + my $task_list = $device->{$event->func()."_list"}; + if ( $task_list ) + { + foreach my $task ( @$task_list ) + { + processTask( $task ); + } + } + } + print( LOG strftime( "%y/%m/%d %H:%M:%S", localtime() )." - ".$event->as_string()."\n" ); + } +} + +1;