#!/usr/bin/env perl # -*- perl -*- #----------------------------------------------------------------------- # Module Name: $Source: /Users/goeke/programming/perl/crater/RCS/CCmd,v $ # Author: Robert F. Goeke (goeke@space.mit.edu # Copyright: Massachusetts Institute of Technology 2006 #----------------------------------------------------------------------- ######################################################################## # Initial Constants ######################################################################## require 5.6.0; use Socket; $Version = '$Id: CCmd,v 1.27 2008/12/11 19:56:21 goeke Exp goeke $'; $NSL = 'host'; # was "nslookup" $ToolsDir = ""; # set CRATERTOOLS if utility programs not in PATH # pdist connection port Info not publicly available! $Outport = 14100; $Inport = 14101; $Samples = 1000; # number of samples in statistics $SciPacket = "output 120 "; # we send this request to pdist $CmdPacket = "output 121 122 "; # NB pdist chops to kill a \n $HousePacket = "output 122 "; # in STDIN usage $StatPacket = "output 120 "; $AllPacket = "output 120 121 122 "; $State = "state "; $ArchiveOrder = 'rtlm $RTLM_flags -f $ArchiveName -p $Channel'; $XArchiveOrder = 'xterm -T Telemetry_Archive -j -sb -ut -geometry 40x4+20+250 -e rtlm $RTLM_flaqs -f $ArchiveName -p $Channel'; $CmdOrder = 'command.tcl -- $TCL_flags -p $Channel -m $CmdHost -q $CmdPort'; $DataOrder = 'rtlm $RTLM_flags -p $Channel'; $XDataOrder = 'rtlm $RTLM_flags -p $Channel'; $HouseOrder = 'house.tcl -- -p $Channel'; $PrintOrder = 'rtlm $RTLM_flags -a -t -l -p $Channel | lpr'; $PrintHouseOrder = 'rtlm $RTLM_flags -a -t -l -p $Channel | calcurve | expand -t "20 30 40 48" | lpr'; $StatOrder = 'statistics -m -p $Channel -n $Samples'; $WebOrder = 'rtlm $RTLM_flags -c -p $Channel | calcurve | c_monitor $Refresh'; # GSE command port info not publicly available! $CmdPort = 11402; # The default rtlm flag for CData; note trailing space $RTLM_flags = "-a -t "; $TCL_flags = ""; # A few of the more popular signals one might receive $SIG{'HUP'} = 'Cleanup'; $SIG{'INT'} = 'Cleanup'; $SIG{'QUIT'} = 'Cleanup'; $SIG{'KILL'} = 'Cleanup'; $SIG{'USR1'} = 'Cleanup'; $SIG{'USR2'} = 'Cleanup'; ######################################################################## # The actual program ######################################################################## &Setup; &Connect; &Launch; &Cleanup; exit 0; ######################################################################## # Close the data pipe # NB that all the external signals come here ######################################################################## sub Cleanup { &Diag("Signal @_ received") if (defined(@_)); socket(COMMAND, PF_INET, SOCK_DGRAM, getprotobyname('udp')) || die $!; $Portaddr = sockaddr_in($Outport,inet_aton("$Host")); die $! if (!defined(send(COMMAND,"close $Channel ",0,$Portaddr))); &Diag("Channel $Channel closed"); exit 0; } ######################################################################## # Go to remote, get a channel assignment, and close the process # The variable $Request specifies the action we want from pdist ######################################################################## sub Connect { socket(LISTEN, PF_INET, SOCK_DGRAM, getprotobyname('udp')) || die $!; bind(LISTEN, sockaddr_in($Inport,INADDR_ANY)) || die $!; &Diag("Listen for $Host info on port $Inport"); socket(COMMAND, PF_INET, SOCK_DGRAM, getprotobyname('udp')) || die $!; $Portaddr = sockaddr_in($Outport,inet_aton("$Host")); # select COMMAND; $| = 1; select STDOUT; $| = 1; &Diag("Sending >$Request< to $Host on port $Outport"); die $! if (!defined(send(COMMAND,$Request,0,$Portaddr))); $_ = ; &Diag("pdist says: $_"); die "pdist says ==>> $_" if (/^ERR|^Bad/); if ($Status) { # If a status request, just answer and exit while () { exit 0 if ($_ =~ /^\./); print; } } ($dis, $dis, $Channel) = split; # $dis is just a discard # we need to close the socket connection so that another process # may connect to tincan from this machine before we are done # with the current game close(LISTEN); close(COMMAND); } ######################################################################## # Diagnotic print statements ######################################################################## sub Diag { print STDERR ">> @_\n" if ($Verbose); } ######################################################################## # The usual help message -- does not return ######################################################################## sub Help { print " Usage $0 [-h] [-L] [-v] [crater_gse_host] [-f base] \\ [-t ##] [100|120|121|122|123|124|125] [-z flags] where -h prints full help message -L assumes localhost is the GSE -v provides verbosity -f optional basename for CArchive only -n optional sample size for CStatistics only -P increment port numbers for talking to pdist -t optional time limit for CArchive only (in minutes) optional refresh interval for CWeb -u displays time in UTC [local timezone] -z passes following flags on to rtlm (CArchive, CData only) "; exit 0 if (!$Verbose); print " The same program is invoked as one of CArchive -- starts an archive file CCmd -- starts the command.tcl window CData -- starts a primary science data stream to STDOUT CHouse -- starts the house.tcl window CPrint -- prints a single page of packet data CQuery -- queries GSE for list of all current users CStatistics -- displays statistics of primary science stream CWeb -- generates web display page of all but primary data Optional numeric arguments [120 121 122 123 124 125] explicitly choose the AppIds to be included in the data stream. The name of the CRaTER GSE is taken from the environment variable CRATER_GSE (currently set to \"$ENV{CRATER_GSE}\") or over-ridden by a name given in the command line. $Version "; exit; } ######################################################################## # Launch the useful process ######################################################################## sub Launch { # Now that we have all the data, we can dereference the variables $Execute =~ s/\${?(\w+)}?/${$1}/g; &Diag("Executing: $Execute"); if (defined($ATime)) { if ($pid = fork) { sleep $ATime*60; # Parent sleeps kill('HUP',$pid); # Kill the child } else { system("$Execute"); # Child works } } else { system("$Execute"); } } ######################################################################## # The usual command line parsing and initialization ######################################################################## sub Setup { my ($apps,$foo); $Host = $ENV{CRATER_GSE} if (defined($ENV{CRATER_GSE})); $Refresh = ''; if (!defined($ENV{CRATERTOOLS})) { my $path = $0; if ($path =~ /^\//) { $path =~ s/(.*\/)\w+/$1/; } else { $path = "."; } $ENV{CRATERTOOLS} = $path; } $ENV{PATH} = $ENV{PATH} . ':' . $ENV{CRATERTOOLS}; $0 =~ s@.*/(\w+)@$1@; # trim off abosolute path, if given if ($0 =~ /^CA/) { $Request = $AllPacket; $Execute = $ArchiveOrder; $RTLM_flags = ''; # cancel any preconceptions } elsif ($0 =~ /^CC/) { $Request = $CmdPacket; $Execute = $CmdOrder; } elsif ($0 =~ /^CD/) { $Request = $SciPacket; $Execute = $DataOrder; } elsif ($0 =~ /^CH/) { $Request = $HousePacket; $Execute = $HouseOrder; } elsif ($0 =~ /^CP/) { # this is default, but see below! $Request = $HousePacket; $Execute = $PrintHouseOrder; } elsif ($0 =~ /^CQ/) { $Request = $State; $Status++; } elsif ($0 =~ /^CS/) { $Request = $StatPacket; $Execute = $StatOrder; } elsif ($0 =~ /^CW/) { $Request = $AllPacket; $Execute = $WebOrder; } else { print STDERR "Unrecognized program name (linked to CCmd): $0\n"; exit 2; } while ($foo = shift(@ARGV)) { if ($foo =~ /^-[vV]/) { # verbose $Verbose++; next; } elsif ($foo =~ /^-[hH]/) { $Verbose++; &Help; } elsif ($foo =~ /^-[lL]/) { # localhost $Host = 'localhost'; next; } elsif ($foo =~ /^-[fF]/) { next if ($ArchiveName = shift(@ARGV)); print STDERR "$foo flag requires a following name"; &Help; } elsif ($foo =~ /^-[nN]/) { $Samples = shift(@ARGV); if (!defined($Samples) || $Samples =~ /[^0-9]/) { print STDERR "$foo flag requires a numberic argument\n"; &Help; } next; } elsif ($foo =~ /^-P/) { $Outport += 2; $Inport += 2; next; } elsif ($foo =~ /^-[tT]/) { $ATime = shift(@ARGV); if (!defined($ATime) || $ATime =~ /[^0-9]/) { print STDERR "$foo flag requires a numberic argument\n"; &Help; } if ($0 =~ /^CW/) { $Refresh = "-t $ATime"; undef($ATime); } next; } elsif ($foo =~ /^-[uU]/) { $RTLM_flags = $RTLM_flags . $foo . " "; # pass UTC flag on $TCL_flags = '-u'; next; } elsif ($foo =~ /^-[zZ]/) { if ($0 !~ /^CD/ && $0 !~ /^CA/) { print STDERR "$foo is not a valid flag for $0\n"; &Help; } $RTLM_flags = ''; # cancel any preconceptions while ($foo = shift(@ARGV)) { $RTLM_flags = $RTLM_flags . $foo . " "; } last; # all args have been scooped up! } elsif ($foo =~ /^-/) { # unknown flag, pass to rtlm print STDERR "Unknown flag: $foo"; &Help; } elsif ($foo !~ /[^0-9]/) { # only numbers if ($foo==(100)||$foo==(120)||$foo==(121)||$foo==(122)|| $foo==(123)||$foo==(124)||$foo==(125)) { # AppIds for CData $apps .= $foo . " "; next; } else { # just random numbers print STDERR "Bad argument: $foo"; &Help; } } else { $Host = $foo; } } if (!defined($Host)) { &Help; } # all this to catch typos in hostname # SERVES NO OTHER USEFUL PURPOSE # # hsst returns info in the form # space.mit.edu has address 18.75.0.10 # /or/ # Host foo not found # elsif (open(INFO,"$NSL $Host |")) { # die "Bad GSE host name given: $Host" if ( !~ /address/); # &Diag("$NSL validation suceeded"); # } $CmdHost = $Host; # Default assumes pdist on command machine if ($apps) { $Request = "output " . $apps; } # May need a fix for CPrint if ($0 =~ /^CP/ && $Request !~ /122/ ) { $Execute = $PrintOrder; } if (!$ArchiveName) { my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = localtime; $year -= 100; # we've past 2000, after all $mon++; # these guys start at zero $ArchiveName = sprintf("archive-%02d%02d%02d%02d%02d", $year, $mon, $mday, $hour, $min); } } ######################################################################## # Pod follows ######################################################################## =for html CRaTER -- CCmd =head2 NAME CArchive, CCmd, CData, CHouse, CStatus, CWeb -- Common High Level CRaTER Utilities CArchive -- starts an archive file CCmd -- starts the command.tcl window CData -- starts a primary science data stream to STDOUT CHouse -- starts the house.tcl window CPrint -- prints a single page of data (Analog Housekeeping by default) CQuery -- returns a list of all current user data streams CStatistics -- displays statistics of primary science stream CWeb -- generates a web page of SecScience and Housekeeping info =head2 USAGE CCmd [-h] [-L] [-v] [crater_gse_host] [-f base] \ [-n samples] [-t ##] [100|120|121|122|123|124|125] [-z flags] =head2 FLAGS -h prints full help message -L assumes localhost is the GSE -f optional basename for CArchive only -n set number of samples used by CStatistics -P increment port numbers to talk to pdist -t time limit for CArchive (in minutes) refresh interval for CWeb -u displays time in UTC [local timezone] -v provides verbosity -z passes following flags on to rtlm (CArchive, CData only) =head2 DESCRIPTION This program resides in the directory as B; all of the other program names are simply aliases. The name of the CRaTER GSE host can be specified on the command line, either explicitly or through use of the B<-L> flag. If neither of these are invoked, the program will look for the environment variable B. If none of these succeed, the program will exit with an error message. The program will contact the GSE host for a UDP channel allocation and use the returned number to start a instance of B. Optional numeric arguments [100 120 121 122 123 124 125] explicitly choose the AppIds to be included in the data stream. The B command will generate a time-stamped basename for the telemetry archive file if none is provided. All AppIds will be included in the archive by default. The B invokes B with the appropriate parameters. B itself starts an input pipeline of B followed by B. Its output is piped to B to send commands back to the GSE. The B command prints its ASCII data to STDOUT; by default all AppIds are processed. The B invokes B with the appropriate parameters. B itself starts an input pipeling of B followed by B. The B command reports back all current users of the GSE telemetry handler. The B command reports the mean, one sigma variation, min observed amplitude and max observed amplitude in each selected number of samples of the primary science events (1000 events by default). The B command starts an input pipeline of B and B to feed B, which does the work of actually generating the web page. Note that this HTML will appear as "index.html" in the directory from which B was invoked. A web browser -- which must be separately invoked (I) -- looking at this page will be instructed to refresh every 8 seconds unless the B<-t> flag is given. =head2 ENVIRONMENT perl5.6 Minimum version of Perl interpreter required CRATERTOOLS Environment variable containing path to CRaTER utilities CRATER_GSE Environment variable containing EGSE machine name =head2 BUGS We attempt on exit to contact the GSE and close the telemetry pipeline; if this process fails for some reason, the GSE will continue pumping out data on the assign UDP socket. =head2 SEE ALSO =head4 High Level programs CLog, CNoise, CQuery =head4 Low Level programs bcmd, calcurve, c_monitor, rtlm, tincan, statistics, verify, command.tcl, house.tcl =head2 AUTHOR Bob Goeke =head2 RCS Information $Id: CCmd,v 1.27 2008/12/11 19:56:21 goeke Exp goeke $ =cut ######################################################################## # history follows ######################################################################## # $Log: CCmd,v $ # Revision 1.27 2008/12/11 19:56:21 goeke # Minor fix to make pdist errors from Ballard box more useful # # Revision 1.26 2008/09/15 18:51:53 goeke # Add -u flag to pass to rtlm and command.tcl # # Revision 1.25 2008/08/22 15:33:19 goeke # Add -P flag to notch the i/o ports talking to pdist # # Revision 1.24 2008/07/14 13:56:55 goeke # Change CWeb request to pick up all packets (including s/c housekeeping) # # Revision 1.23 2008/06/30 14:22:14 goeke # Add AppId 100 # # Revision 1.22 2008/06/20 15:22:18 goeke # Add -u flag to pass UTC command to rtlm # # Revision 1.21 2008/05/01 18:28:20 goeke # Add -t flag for CWeb to control refresh rate # # Revision 1.20 2008/04/22 12:43:54 goeke # Added CWeb functionality # # Revision 1.19 2007/11/06 16:08:35 goeke # Take out all of the checks for IP naming; just getting in the way. # # Revision 1.18 2007/10/01 19:18:50 goeke # Fix bug in failing to clear RTLM_flags when invoked as CArchive # # Revision 1.17 2007/10/01 17:06:43 goeke # Allow passing additional flags through to rtlm in CArchive mode # # Revision 1.16 2007/08/17 16:06:18 goeke # Add POD description of CStatistics # # Revision 1.15 2007/08/17 13:54:01 goeke # Made compatible with CComment adding appids 123-125 # Took out references to CRATER_GSE_CMD # # Revision 1.14 2007/06/18 14:15:57 goeke # Added CPrint function # # Revision 1.13 2007/05/30 20:59:25 goeke # Add ability to set statistics sample with -n flag # # Revision 1.12 2007/05/04 13:05:24 goeke # Edits to &Help and POD # # Revision 1.11 2007/04/03 20:08:27 goeke # Added POD # # Revision 1.10 2007/03/30 16:41:57 goeke # Remove report of file name from CArchive # # Revision 1.9 2007/03/28 18:48:05 goeke # renamed CStatus->CQuery; added CStatistics # # Revision 1.9 2007/03/28 18:45:45 goeke # Added CStatus # # Revision 1.8 2007/03/05 13:37:43 goeke # Add -z flag to allow passage of flags to rtlm # # Revision 1.7 2007/02/26 15:09:23 goeke # Modify HELP message. # Allow all invocations to explicity choose AppIds. # Catch external signals and close channels on exit. # # Revision 1.6 2007/02/23 18:28:03 goeke # Added CStatus as program name # # Revision 1.5 2007/02/20 21:04:59 goeke # Added CData and CArchive functions # # Revision 1.4 2007/02/13 19:33:50 goeke # Add env variable CRATER_GSE_CMD; change out nslookup -> host # # Revision 1.3 2007/02/09 14:15:54 goeke # Shutdown wasn't closing socket; use close instead # # Revision 1.1 2007/02/02 20:24:06 goeke # Initial revision # #