#! perl -w
#==============================================================================
# @(#) $Id$
#------------------------------------------------------------------------------
##
## Name:
##
## cfx5control
##
## Purpose:
##
## Dynamic control of the ANSYS CFX Solver
##
## Synopsis:
#S
#S cfx5control <directory> -backup
#S cfx5control <directory> -edit-commands [-no-backup]
#S cfx5control <directory> -inject-commands <file> [-no-backup]
#S cfx5control <directory> -reset-priority <level>
#S cfx5control <directory> -stop
#S
## []  denotes an optional argument.
## |   separates mutually exclusive arguments. 
## ... following an argument indicates that it may be repeated.
## <>  denotes that substitution of a suitable value is required. All other
##     arguments are keywords, some of which have a short form.
##
## Arguments:
##
#A <directory>
##    Specifies a directory in which the ANSYS CFX solver is currently
##    running, such as StaticMixer_004.dir.
##
#A -backup
##    Instructs the ANSYS CFX solver running in the named directory to
##    write a backup file.
##
#A -edit-commands
##    Reads the current CFX Command Language in use by the run, and
##    starts the Command File Editor with it.  When the editing is
##    finished, the solver will re-read the updated Command Language.
##
#A -inject-commands <file>
##    Reads the CFX Command Language data from the named file, and
##    arranges for the ANSYS CFX Solver to read it in place of the
##    existing Command Language for that run.
##    Changes that affect the way the mesh is defined, or that affect the
##    way the physics CCL relates to the topology of the mesh stored
##    in the solver input file, cannot be made using this option.
##    For example, locators for applying physics cannot be modified using 
##    this option. Such changes can, however, be made in CFX-Pre.
##
#A -no-backup
##    When used with -edit-commands or -inject-commands, the solver
##    will not write a backup file before reading the new Command
##    Language.
##
## -reset-priority <level>
## -renice <level>
##    Resets the runtime priority of the ANSYS CFX Solver.  <level> should
##    be one of:
##      CFX Levels            Nice increment     Windows Priority
##     Idle (0)                   19               Low
##     Low (1)                     7               BelowNormal
##     Standard (2)                0               Normal
##     High (3)                    0               AboveNormal
##    This applies to all processes in a parallel run.  A numeric
##    setting is also accepted as shown in the CFX column.  If the
##    current priority level is the same as <level> then there
##    is no effect.  Administrative privileges are required to 
##    increase the priority from a lower level to a higher
##    level.
##
#A -stop
##    Tells the ANSYS CFX Solver running in the given directory to stop at
##    the end of the current timestep.
##
#U ****************************************************************************
#U *                                                                          *
#U *     The following features are supported only within CFX and are not     *
#U *     shown by the -help option when the command is run elsewhere.         *
#U *                                                                          *
#U ****************************************************************************
#U -merge-commands <file>
#U    Reads the CFX Command Language data from the named file, and
#U    merges it with the existing Command Language for the run.
#U
#------------------------------------------------------------------------------

# get the standard ANSYS CFX library directory
BEGIN {
 if ($0 =~ m!^([a-z]:)?[\\/]!i) {
    # it's an absolute path, use it as-is
    (my $nd=$0) =~ s!(perllib[\\/])?[^\\/]*$!!i;
    unshift @INC, "$nd/perllib";
  } else {
    # wrap this in an eval, and if it fails, pray ... for example,
    # this can fail on Windows if we're running off a share.
    eval {
      require FindBin;
      FindBin->import();
      unshift @INC, "$FindBin::Bin/perllib";
      my $dummy=$FindBin::Bin; # kill used only once warning
    };
    warn $@ if $@;
  }
  if (defined $ENV{CFX_LOCAL_PERLLIB}) {
     unshift @INC, $ENV{CFX_LOCAL_PERLLIB};
  }

}

use CFX5::Init;
CFX5::Init::init();

use vars qw($CVSVersion);
sub BEGIN {
  $CVSVersion='@(#) $Id$\n';
}

use strict;

use CFX5::Output qw(:all);
use CFX5::Config qw(%Config export_conf query_conf append_to_libpath);
#use CFX5::PrintConfig;
use CFX5::Help;
use CFX5::PathManip qw(join_file); 

use CFX5::CmdLine qw(:util parse_cmdline);

use CFX5::FileOps qw(get_file_contents);

#use CFX5::Commands qw(do_cmd handle_rv);

use CFX5::SolControl;

#use CFX5::MonFile;
#use CFX5::DefFile;

#use CFX5::CCLDiff;
#use CFX5::CCLI;

#
# read the configuration
#
CFX5::Config::read_config();

#
# parse the command line
#
my ($control,@mode,@inject_files,@merge_files,$nobackup,$outfile,$newpriority);

my %args=(
          '-backup'           => \@mode,
          '-edit-commands'    => \@mode,
          '-edit-cmds'        => '-edit-commands',
          '-inject-commands+' => sub { push @mode, $_[0]; push @inject_files, $_[1]; },
          '-inject-cmds+'     => '-inject-commands',
          '-merge-commands+'  => sub { push @mode, $_[0]; push @merge_files, $_[1]; },
          '-merge-cmds+'      => '-merge-commands',
          '-no-backup'        => \$nobackup,
          '-nobackup'         => '-no-backup',
          '-stop'             => \@mode,
          '-reset-priority+'  => sub {push @mode, $_[0]; $newpriority=$_[1];},
          '-renice+'          => '-reset-priority',
         );
my @nonopt=parse_cmdline
  ( \@ARGV,
    'INCLUDE_STD', 'ALLOW_NON_OPTION',
    %args,
  );

if (@nonopt and !defined $control) {
  my $dir=shift @nonopt;
  $control=new CFX5::SolControl($dir);
  # this should indicate errors with _error, but just in case, we have
  # a fallback
  if (!defined $control and not error_pending()) {
    _error("$dir is not a valid ANSYS CFX run directory.");
  }
} else {
  _error("You must specify a current ANSYS CFX Solver run directory.",
         CFX5::Help::usage_msg_once());
}

if (@nonopt) {
  _error("Unexpected argument(s)\n", @nonopt, "\nwithout an associated option were found on the command line.", CFX5::Help::usage_msg_once());
}

unless (@mode) {
  _error("You must give something to do!", CFX5::Help::usage_msg_once());
}

if (defined $outfile) {
  CFX5::FileTests::datafile_w($outfile);
}

exit_if_error();

if ($nobackup) {
  $control->suppress_backup($nobackup);
}

MODE_ARG: for my $mode (@mode) {
  # de-reference any alias arguments
 REFLEVEL: do {
    if (exists $args{$mode} and !ref($args{$mode})) {
      $mode=$args{$mode};
      redo;
    }
    if (exists $args{"$mode+"} and !ref($args{"$mode+"})) {
      $mode=$args{"$mode+"};
      redo;
    }
  };

  # Read Monitor Data
  if ($mode eq '-stop') {
    # errors are signalled with _error
    $control->stop();
    
    # Multi-configuration handling
    my $dir = $control->directory(); 
    local *CHECKDIR;
    unless (opendir CHECKDIR, $dir) {
      _error("Directory not found:\n\t'$dir'");
      return 0;
    }
    
    my $f;
    while (defined ($f=readdir(CHECKDIR))) {
      if ($f =~ m\.*(.dir)\) {
        my $dirConfig = join_file($dir,$f) ;
        my $controlConfig=new CFX5::SolControl($dirConfig);
        if (!defined $controlConfig and not error_pending()) {
          _error("$dirConfig is not a valid ANSYS CFX run directory.");
        }
        $controlConfig->stop(); 
        last; 
      }
    }

    closedir(CHECKDIR);

  } elsif ($mode eq '-backup') {
    # errors are signalled with _error
    $control->backup();
  } elsif ($mode eq '-reset-priority') {
    if ($newpriority =~ m!^[0123]$!) {
      my %map=(
               0 => 'Idle',
               1 => 'Low',
               2 => 'Standard',
               3 => 'High',
              );
     $newpriority=$map{$newpriority};
    }
    $control->renice($newpriority);
  } elsif ($mode =~ m/-inject-commands/) {
    my @newccl;
    for my $f (@inject_files) {
      push @newccl, split(m/\n\r*/, get_file_contents($f));
    }
    exit_if_error();
    # errors are signalled with _error
    $control->inject_ccl(@newccl);
  } elsif ($mode =~ m/-merge-commands/) {
    my @newccl;
    for my $f (@merge_files) {
      push @newccl, split(m/\n\r*/, get_file_contents($f));
    }
    exit_if_error();
    # errors are signalled with _error
    $control->merge_ccl(@newccl);
  } elsif ($mode eq '-edit-commands') {
    # errors are signalled with _error
    $control->edit_ccl();
  } else {
    internal_error("Argument $mode is unknown or unimplemented");
  }

  exit_if_error();

}

exit_if_error();

exit(0);

__END__

  if (0) {
    # these are easy, they just involve writing a file in the run
    # directory
    
    my @data=map{"$_\n"} CFX5::MonFile->read_from_res($deffile);
    if (@data) {
   if (defined $OutFile) {
     CFX5::FileOps::safe_set_file_contents($OutFile, @data);
   }else{
       print(@data);
   }
    }else{
   _warn("No monitor point data in file $deffile");
    }

  } elsif ($mode eq '-read-cmds') {
    my $cclds=CFX5::CCLI::getcclds($deffile);
    unless (ref $cclds) {
      _error("No CFX Command Language in file $deffile");
      next MODE_ARG;
    }

    my @data=map{"$_\n"} $cclds->data();
    if (@data) {
      if (defined $OutFile) {
        CFX5::FileOps::safe_set_file_contents($OutFile, @data);
      }else{
        print(@data);
      }
    }else{
      _error("No CFX Command Language in file $deffile");
    }

  } elsif ($mode eq '-read-pre-state') {
    my $def=CFX5::DefFile->new($deffile);
    unless (ref $def) {
      _error("Unable to open input file $deffile");
      next MODE_ARG;
    }
    my @dslist=$def->have_dataset(what => 'G/PRESTATE', 
                              when => 0);
    if (!@dslist) {
      _error("No CFX-Pre State Information in file $deffile");
      next MODE_ARG;
    }
    my $ds=$def->read_dataset(what => 'G/PRESTATE', 
                              when => 0);
    unless (ref $ds) {
      _error("No CFX-Pre State Information in file $deffile");
      next MODE_ARG;
    }

    my @data=map{"$_\n"} $ds->data();
    if (@data) {
      if (defined $OutFile) {
        CFX5::FileOps::safe_set_file_contents($OutFile, @data);
      }else{
        print(@data);
      }
    }else{
      _error("No CFX-Pre State Information in file $deffile");
    }

  # Compare the CCL in two DEF files
  } elsif ($mode eq '-compare') {

    CFX5::FileTests::datafile($compfile);
    exit_if_error();

    my @data = CFX5::CCLDiff->compareDefs($deffile,$compfile);
    if (@data) {
      if (defined $OutFile) {
        CFX5::FileOps::safe_set_file_contents($OutFile, @data);
      }else{
        print(@data);
      }
    }else{
   _warn("Unable to compare CCL in $deffile with $compfile");
    }
  }
