App-FQStat

 view release on metacpan or  search on metacpan

script/fqstat.pl  view on Meta::CPAN

    my $arg = shift;
    return if not DEBUG;
    if (not $DEBUGFH) {
      $DEBUGFH = FileHandle->new();
      $DEBUGFH->open('fqstat.debug', '>>') or die $!;
      $DEBUGFH->autoflush(1);
      *DEBUGFH = $DEBUGFH;
    }
    chomp $arg;
    print $DEBUGFH $arg."\n";
  }

  debug("Setting up debug mode");
  open(STDERR, ">&DEBUGFH");
}

######################
# Record key constants
use constant {
  F_id => 0,
  F_prio => 1,
  F_name => 2,
  F_user => 3,
  F_status => 4,
  F_date => 5,
  F_time => 6,
  F_queue => 7,
};

use constant RECORD_KEY_CONSTANT => {
  id => F_id, prio => F_prio, name => F_name,
  user => F_user, status => F_status, date => F_date,
  'time' => F_time, queue => F_queue,  
};
use constant RECORD_CONSTANT_KEY => [
  qw/ id prio name user status date time queue /
];



################
# load local modules
use App::FQStat;
our $VERSION = $App::FQStat::VERSION;
use App::FQStat::Input qw/get_input_key/;
use App::FQStat::Drawing qw/printline/;
use App::FQStat::Debug;

use vars qw/%SIG/;
autoflush STDIN 1;
ReadMode 3;
$|=1;

##############
# Declare globals

# action & key globals
use constant KEY_POLL_INTERVAL => 0.2;  # blocking time for keyboard polling
our %Keys;                              # hash of key => action for main loop
our %ControlKeys;                       # hash of control key id => action for main loop
our %MenuKeys;                          # hash of key => action for menu 
our %MenuControlKeys;                   # hash of control key id => action for menu
our %SummaryKeys;
our %SummaryControlKeys;

# twiddly globals
use constant PROGRESS_INDICATORS => ['-', '\\', '|', '/']; # progress indicator states
our $ProgressIndicator = 0;             # progress indicator current state number

# displaying globals
our $Initialized = 0;
our @Termsize : shared;                 # holds terminal size
our $DisplayOffset : shared = 0;        # Offset of the first displayed job
our $SortField : shared;                # may hold name of sort field

our $Interval : shared;                 # Effective data refreshing interval. Do not change. Is set to $UserInterval below
our $HighlightUser;
{
  my $curuser = $ENV{USER};
  if (defined $curuser) {
    $HighlightUser = quotemeta($curuser);
  }
}

# application mode globals
our $MenuMode            = 0; # in menu or not
our $SummaryMode :shared = App::FQStat::Config::get("summary_mode") || 0; # in summary mode or not

# menu globals
our $MenuNumber      = 0; # which menu (see @App::FQStat::Menu::Menus)
our $MenuEntryNumber = 0; # in which entry of that menu


# Displayed column descriptions
our %Columns =  (
  prio   => { format => '%.5f',  width => 7,  name => 'Prio',  key => 'prio',  'index' => F_prio,  order => 'num_highlow' },
  name   => { format => '%-10s', width => 10, name => 'Name',  key => 'name',  'index' => F_name,  order => 'alpha'       },
  user   => { format => '%-12s', width => 12, name => 'Owner', key => 'user',  'index' => F_user,  order => 'alpha'       },
  id     => { format => '%7u',   width => 7,  name => 'Id',    key => 'id',    'index' => F_id,    order => 'num'         },
  date   => { format => '%-10s', width => 10, name => 'Date',  key => 'date',  'index' => F_date,  order => 'date'        },
  'time' => { format => '%-8s',  width => 8,  name => 'Time',  key => 'time',  'index' => F_time,  order => 'time'        },
  queue  => { format => '%30s',  width => 30, name => 'Queue', key => 'queue', 'index' => F_queue, order => 'alpha'       },
);
# Column order
our @Columns = qw(id name prio user date time queue);

# Summary Mode: Displayed column descriptions
our %SummaryColumns =  (
  user    => { format => '%-12s', width => 12, name => 'Owner',       key => 'user',    'index' => 0, order => 'alpha'       },
  name    => { format => '%-12s', width => 12, name => 'Name-Like',   key => 'name',    'index' => 1, order => 'alpha'       },
  n_run   => { format => '%-5u',  width => 5,  name => 'NRun',        key => 'nrun',    'index' => 2, order => 'num'         },
  n_err   => { format => '%-5u',  width => 5,  name => 'NErr',        key => 'nerr',    'index' => 3, order => 'num'         },
  n_hld   => { format => '%-5u',  width => 5,  name => 'NHold',       key => 'nhold',   'index' => 4, order => 'num'         },
  n_wait  => { format => '%-5u',  width => 5,  name => 'NWait',       key => 'nwait',   'index' => 5, order => 'num'         },
  prio    => { format => '%.6f',  width => 8,  name => 'AvrgPrio',    key => 'prio',    'index' => 6, order => 'num_highlow' },
  'time'  => { format => '%-11s', width => 11, name => 'AvrgRunTime', key => 'time',    'index' => 7, order => 'time'        },
  maxtime => { format => '%-11s', width => 11, name => 'MaxRunTime',  key => 'maxtime', 'index' => 9, order => 'time'        },
);
# Summary Mode: Column order
our @SummaryColumns = qw(user name n_run n_err n_hld n_wait prio time maxtime);


# Data structure to hold information about the current state of affairs
our $Records = [];
our $RecordsChanged : shared = 0;
our $RecordsReversed : shared = 0;
our $NoActiveNodes = 0;
our $Summary = [];

# scanner thread globals, see below.

##############
# Get Command line arguments
our $User : shared;
our $UserInterval = 30;
our $SlowRedraw = 0;
my $SSHCommand;
our $ResetConfig;
Getopt::Long::Configure("no_ignore_case");
GetOptions(
  'u|user=s' => \$User,
  'H|highlight=s' => \$HighlightUser,
  'i|interval=f' => \$UserInterval,
  's|slow' => \$SlowRedraw,
  'ssh=s' => \$SSHCommand,
  'resetconfig' => \$ResetConfig,
  'h|help|?' => sub {
    ReadMode 1;
    print RESET;
    print usage();
    thread_cleanup();

script/fqstat.pl  view on Meta::CPAN

# setup scanner thread
our $ScannerStartRun : shared = 0;
our $ScannerThread;# = threads->new(\&App::FQStat::Scanner::scanner_thread);

# thread exit handler
sub thread_cleanup {
  warnenter if ::DEBUG;
  if (defined $ScannerThread and $ScannerThread->is_running()) {
    print "Cleaning up polling threads...\n";
    $ScannerThread->kill('SIGKILL');
  }
}

# exit handler
sub cleanup_and_exit {
  warnenter if ::DEBUG;
  print RESET;
  Term::ANSIScreen::locate($Termsize[1],1);
  thread_cleanup();
  ReadMode 1;
  print "Have a nice day!\n" unless @_;
  exit();
}

$SIG{INT} = \&cleanup_and_exit;
$SIG{HUP} = \&cleanup_and_exit;
$SIG{TERM} = \&cleanup_and_exit;
$SIG{__DIE__} = sub{warn @_;ReadMode 1;exit(1);};

###########################
# RUN
GetTermSize();
cls();
App::FQStat::Drawing::update_display(1);

print_module_versions() if ::DEBUG;
%PAR::FileCache = %PAR::FileCache = () if exists $ENV{PAR_TEMP};
main_loop();
exit(0);

##############################
# Update @TermSize variable
sub GetTermSize {
  warnenter if ::DEBUG > 2;
  @Termsize = Term::ReadKey::GetTerminalSize();
  @Termsize = (80,25,0,0) if not @Termsize == 4;
}

####################
# MAIN LOOP

BEGIN {
  %ControlKeys = (
    'A'  => sub { App::FQStat::Actions::scroll_up(1); 1 },                # up
    'B'  => sub { App::FQStat::Actions::scroll_down(1); 1 },              # down
    '5'  => sub { App::FQStat::Actions::scroll_up($Termsize[1]-4); 1 },   # pgup
    '6'  => sub { App::FQStat::Actions::scroll_down($Termsize[1]-4); 1 }, # pgdown
    'H'  => sub { App::FQStat::Actions::scroll_up(1e9); 1 },              # pos1 (640kb ought to be enough for everyone!)
    'F'  => sub { App::FQStat::Actions::scroll_down(1e9); 1 },            # end (640kb ought to be enough for everyone!)
    '15' => sub { App::FQStat::Drawing::update_display(1) },              # F5
    '21' => sub { App::FQStat::Menu::toggle_menu() },                     # F10
  );

  %Keys = (
    'q' => \&cleanup_and_exit,
    'i' => \&App::FQStat::Actions::set_user_interval,
    'H' => \&App::FQStat::Actions::update_highlighted_user_name,
    'r' => \&App::FQStat::Actions::toggle_reverse_sort,
    's' => \&App::FQStat::Actions::select_sort_field,
    'u' => \&App::FQStat::Actions::update_user_name,
    'k' => \&App::FQStat::Actions::kill_jobs,
    'p' => \&App::FQStat::Actions::change_priority,
    'o' => \&App::FQStat::Actions::hold_jobs,
    'O' => \&App::FQStat::Actions::resume_jobs,
    'h' => \&App::FQStat::Actions::show_manual,
    '?' => \&App::FQStat::Actions::show_manual,
    'c' => \&App::FQStat::Actions::clear_job_error_state,
    'd' => \&App::FQStat::Actions::change_dependencies,
    ' ' => \&App::FQStat::Actions::show_job_details,
    "\n" => \&App::FQStat::Actions::show_job_details,
    'l' => \&App::FQStat::Actions::show_job_log,
    'S' => \&App::FQStat::Actions::toggle_summary_mode,
  );

  # copy of the key maps for the menu
  %MenuControlKeys = %ControlKeys;
  delete $MenuControlKeys{$_} foreach qw(5 6 H F); # pg-up, pg-down, home, end
  $MenuControlKeys{A}    = \&App::FQStat::Menu::menu_up,     # up-arrow
  $MenuControlKeys{B}    = \&App::FQStat::Menu::menu_down,   # down-arrow
  $MenuControlKeys{C}    = \&App::FQStat::Menu::menu_right,  # right-arrow
  $MenuControlKeys{D}    = \&App::FQStat::Menu::menu_left,   # left-arrow
  
  %MenuKeys = %Keys;
  $MenuKeys{"\n"} = \&App::FQStat::Menu::menu_select, # Enter
  $MenuKeys{" "}  = \&App::FQStat::Menu::menu_select, # space
  delete $MenuKeys{$_} foreach qw(S);

  %SummaryKeys = map {($_ => $Keys{$_})} qw(q i h S);
  $SummaryKeys{c} = \&App::FQStat::Actions::toggle_summary_name_clustering;
  $SummaryKeys{s} = $SummaryKeys{S};
  %SummaryControlKeys = map {($_ => $ControlKeys{$_})} qw(15 21);
}

my @OldTermSize = @Termsize;
sub main_loop {
  warnenter if ::DEBUG;
  my $Redraw = 1;
  my $RedrawTime = time();
  my $RedrawOffset = $DisplayOffset;
  while (1) {
    my $input = get_input_key();
    if (defined $input) {
      my ($KeysHash, $ControlKeysHash);
      if ($MenuMode) {
        $KeysHash = \%MenuKeys;
        $ControlKeysHash = \%MenuControlKeys;
      }
      elsif ($SummaryMode) {
        $KeysHash = \%SummaryKeys;
        $ControlKeysHash = \%SummaryControlKeys;
      }
      else {
        $KeysHash = \%Keys;
        $ControlKeysHash = \%ControlKeys;
      }

      #warn "-I->$input<---->".ord($input);
      if ($KeysHash->{$input}) {
        my $redraw = $KeysHash->{$input}->($input);
        $Redraw = 1 if $redraw;
      }
      
      elsif ($input eq '[') { # control-key!
        my $key = get_input_key(0.001);
        #warn "-K->$key<---->".ord($key);
        if (defined $key and exists $ControlKeysHash->{$key}) {
          my $redraw = $ControlKeysHash->{$key}->($key);
          $Redraw = 1 if $redraw;
        }
        elsif ($key eq '1' or $key eq '2') { # F-keys
          my $innerkey = get_input_key(0.001);
          #warn "-IK->$innerkey<---->".ord($innerkey);
          if (defined $innerkey and exists($ControlKeysHash->{"$key$innerkey"})) {
            my $redraw = $ControlKeysHash->{"$key$innerkey"}->("$key$innerkey");
            $Redraw = 1 if $redraw;
          }
        }
      } # end control keys
    } # end if defined input

    # Fetch new scanner results if applicable
    if (defined $ScannerThread and $ScannerThread->is_joinable()) {
      warnline "Scanner thread joinable in main loop. Joining" if ::DEBUG;
      my $return = $ScannerThread->join();
      ($Records, $NoActiveNodes) = @$return;
      $Initialized = 1;
      warnline "Scanner thread joined in main loop" if ::DEBUG;
      lock($RecordsChanged);
      $RecordsChanged = 1;
      $Summary = [];
    }

    my $startRun;
    {
      lock($ScannerStartRun);
      $startRun = $ScannerStartRun;
    }
    if ($startRun) {
      App::FQStat::Scanner::run_qstat();
    }

    {
      lock($RecordsChanged);
      lock($DisplayOffset);
      $Redraw = 1 if $RecordsChanged;
      $Redraw = 1 if $DisplayOffset != $RedrawOffset;



( run in 1.036 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )