package X10; use Data::Dumper; use File::Basename; use FileHandle; use POSIX; use strict; use X10::Macro; use X10::MacroProc; use X10::SchedEvent; use X10::Scheduler; use X10::Server; use vars qw($VERSION); $VERSION = 0.03; sub new { my $type = shift; my $self = bless { @_ }, $type; $self->{verbose} = 1 if $self->{debug}; $self->{logger} = sub { $self->syslog(@_) }; unless ($self->{controller_type}) { warn "Interface type must be specified\n"; return undef; } my $controller = $self->{controller_type}; if (eval "require $controller") { $controller->import; # just in case } else { die "Can't load module for $controller: ", $@; } $self->{controller} = $controller->new( port => $self->{controller_port}, debug => $self->{debug}, verbose => $self->{verbose}, logger => sub { $self->syslog(@_) }, ); $self->{controller}->register_listener( sub { $self->syslog('info', "Event: %s", $_[0]->as_string) } ); if (exists $self->{devices}) { # load device config } if (exists $self->{schedulerconfig}) { $self->{scheduler} = new X10::Scheduler( configfile => $self->{schedulerconfig}, controller => $self->{controller}, debug => $self->{debug}, verbose => $self->{verbose}, logger => sub { $self->syslog(@_) }, latitude => 38.74274, longitude => -90.560143, ); unless ($self->{scheduler}) { warn "Problem creating macro processor"; return undef; } } if (exists $self->{macroconfig}) { $self->{macrop} = new X10::MacroProc( configfile => $self->{macroconfig}, controller => $self->{controller}, debug => $self->{debug}, verbose => $self->{verbose}, logger => sub { $self->syslog(@_) }, ); unless ($self->{macrop}) { warn "Problem creating macro processor"; return undef; } } if (exists $self->{server_port}) { $self->{server} = new X10::Server( controller => $self->{controller}, debug => $self->{debug}, verbose => $self->{verbose}, server_port => $self->{server_port}, logger => sub { $self->syslog(@_) }, ); unless ($self->{server}) { warn "Problem creating network server"; return undef; } } return $self; } sub run { my $self = shift; # this method plans to never return... $self->{running} = 1; $SIG{'INT'} = sub { $self->{running} = 0; }; $SIG{'TERM'} = sub { $self->{running} = 0; }; $self->syslog('info', "%s service starting", $self->{controller_type}); my $next_wakeup = 0; X10RUNMAINLOOP: while ($self->{running}) { $self->{logger}->('info', "Entering mainloop") if $self->{debug}; my %fdindex; foreach my $module ( grep { exists $self->{$_} } qw(controller server macrop) ) { foreach my $fd ($self->{$module}->select_fds) { $fdindex{$fd} = $self->{$module}; } } $self->syslog('info', "All FDs are %s\n", join(', ', keys %fdindex)) if $self->{debug}; my $rfd = ''; foreach (keys %fdindex) { vec($rfd, $_, 1) = 1; } # done setting up FD array # figure out if we have to wake up at a certain time: my $timeout = undef; if ($self->{scheduler}) { my $next_event_time = $self->{scheduler}->next_event_time; if ($next_event_time) { $timeout = $next_event_time - int(time); $timeout = 0 if ($timeout < 0); } if ( (defined $timeout) && $next_event_time != $next_wakeup) { $next_wakeup = $next_event_time; $self->syslog('info', "Next Scheduled Event: %s (%s seconds away)", strftime("%a %b %e %H:%M %Y", localtime($next_event_time)), $timeout, ); } } # done calculating wakeup time my $readers; my $fdcount = select($readers=$rfd, undef, undef, $timeout); if ($fdcount > 0) { $self->{logger}->('info', "Got %s FDs to handle", $fdcount) if $self->{debug}; foreach (keys %fdindex) { if (vec($readers, $_, 1)) { $self->syslog('info', "Processing input on FD %s (%s)\n", $_, $fdindex{$_}) if $self->{debug}; $fdindex{$_}->handle_input; } } } elsif ($fdcount < 0 && $! != 4) # ignore Interrupted System Call { $self->{logger}->('info', "Error %d in select(): %s", $!, $!); } if ($self->{scheduler}) { $self->{scheduler}->run; } } $self->syslog('info', "%s service shutting down", $self->{controller_type}); } sub syslog { my $self = shift; my $level = shift; my $format = shift; my $message = sprintf($format, @_); my $facility = "local5"; my $tag = sprintf "%s[%s]", basename($0, ".pl"), $$, ; if ($self->{debug}) { printf "syslog message: %s\n", $message; } else { my $fh = new FileHandle; $fh->open("|/usr/bin/logger -p $facility.$level -t $tag"); $fh->print($message); $fh->close; } } 1;