#!/usr/local/bin/perl -w

# matlab_method.pl

# Program to read in data formatted as Matlab binary files.

# All necessary run-time and configuration values are read in from
# 		$config_param{'send_mail_option'}
# a configuration file specified as the first argument in the command
# line.

# Assumptions:
#	1. At least one field name must be provided, as well as the 
# Matlab, binary, data file name.

$version="V1.03 May 16, 2001";

# May 16, 2001. V1.03  Use "batch" file to run matlab per the Mathworks
#	suggestion.  Clean up code that defines the various file names
#	so they are defined in one section, rather than 3.  rcg
# May 14, 2001. V1.02  Change matlab call to remove errors. Add quit to
#	end of m file.  rcg
# May 11, 2001. V1.01  Change data output to go to a file 
#	rather than unit 1.  Change output to use \t rather
#	than tab character directly.  Fix formatting and values
#	in date stamp.  rcg
# May 7, 2001.  V1.00  R. Groman

$| = 1;
$error = "&x";
$warning = "#";

$date = scalar localtime;
$day_today = (localtime)[3];
if ($day_today < 10 ) {$day_today = "0" . $day_today}
$month_today = (localtime)[4];
$month_today++;
if ($month_today < 10 ) {$month_today = "0" . $month_today}
$year_today = (localtime)[5];
if ($year_today < 1000) {$year_today = $year_today + 1900;}
$hour_today = (localtime)[2];
if ($hour_today < 10 ) {$hour_today = "0" . $hour_today;}
$minute_today = (localtime)[1];
if ($minute_today < 10 ) {$minute_today = "0" . $minute_today;}
$time_today = $hour_today . $minute_today;
$time_stamp = $year_today . $month_today . $day_today . $time_today;
$processid = getppid;

$configuration_file = $ARGV[0];

print STDOUT ("\n#Program $0 Version: $version\n");
print STDOUT ("#  configuration file is $configuration_file\n") if defined $configuration_file;
print STDOUT ("#  Date of run: ", $date, "\n\n");

@required = (
	"cshell",
	"field_name_1", 
	"matlab_command",
	"mat_file_names");

&read_configuration_file($configuration_file);

$debug = 'no';
if (exists $config_param{'debug'} and defined $config_param{'debug'} ) {
	if ($config_param{'debug'} =~ m/^y/i or 
	   $config_param{'debug'} =~ m/^1/ or 
	   $config_param{'debug'} =~ m/^t/i) {;
		$debug = "yes";
	}
}
&make_log_entry (">>>>>>>Begin Program=$0",
	"Date of run=$date\tVersion=$version",
	"Configuration file=$configuration_file");

$okay = "yes";
for ($i=0; $i<=$#required; $i++) {
	unless (exists $config_param{$required[$i]} ) { 
	   $okay="no"; 
	   &sendmessage ($error,
	   	"$required[$i] is missing from configuration ",
	   	"file=$configuration_file");
	   &make_log_entry ("$required[$i] is missing from configuration file",
	   	"file=$configuration_file");
	}
	else {
#		print STDOUT ("#  $required[$i]=$config_param{$required[$i]}\n");
		&make_log_entry ("$required[$i]=$config_param{$required[$i]}");
	}
}
if ( $okay eq "no") { 
	&sendmessage ($error,
		"One or more parameters are missing from the configuration file",
		"Cannot continue.");
	&make_log_entry (
		"One or more parameters missing from configuration file=$configuration_file");
	exit;
}


&process_data;

undef $error;
undef $warning;
undef $version;
undef $time_stamp;

&make_log_entry ("<<<<<<<End of $0");

exit;

#------------------------------------------------------------------------
sub read_configuration_file {

# Open and read configuration file specified as first passed parameter 
# $_[0]. Return the contents of the file as the hash array %config_param.
# Lines beginning with "#" are treated as comments.  It is assumed
# that the indirect file contains lines as

#	parameter = value

# and this information is stored as 

#	$config_param{"parameter"} = value

my $filename = $_[0];
my ($parameter, $value);

#print STDOUT ("#**debug, indirect filename=$filename\n");

unless (defined $filename ) {
	&sendmessage ($error,
		"Could not open configuration file",
		"File name not specified.  Cannot continue.");
	exit;
}
unless (open CONFIG_FILE, $filename) {
	&sendmessage ($error,
		"Could not open configuration file=$filename",
		"Error code=$!.  Cannot continue.");
	exit;
}
while (<CONFIG_FILE>) {
	chomp;
	s/\s//g;
	if (m/^#/) { next;}
	unless ( m/\S+/ ) {next;}
	unless ( m/:=/ ) {
		&sendmessage ($warning,
			"No ':=' in line of configuration file=$filename",
			"Line is=$_");
		next;
	}
#	print STDOUT ("***debug, config input line=$_\n");
	($parameter, $value) = split /:=/;
	$config_param{$parameter} = $value;
#	print STDOUT ("#**debug, config_param{$parameter}=",
#		$config_param{$parameter}, "\n");
}
close CONFIG_FILE;
}

#------------------------------------------------------------------------

sub sendmessage {

#Send a message to the user.
#The message sent will be in the strings $_[0] and $_[1]

my ( @args, $mailfile, $message0, $message1, $prefix, $who);
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst);

$prefix=$_[0];
$message0=$_[1];
$message1=$_[2];
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time);
if ($year >= 100 and $year <= 1000) {$year = $year + 1900}
if ($year < 1900) { $year = $year + 2000; }
$mon++;
if ($mon < 10) { $mon = "0" . $mon; }
if ($mday < 10) {$mday = "0" . $mday; }
if ($hour < 10) { $hour = "0" . $hour; }
if ($min < 10) { $min = "0" . $min; }
if ($sec < 10) { $sec = "0" . $sec; }
undef $wday;
undef $isdst;
$mailfile="/tmp/sendmess" . $year . $yday . $hour . $min . $sec . ".tmp";
if ( -e $mailfile) { unlink $mailfile }

if ( open TEMPFILE, ">$mailfile") {
	print TEMPFILE ("Message from $0\n");
	if ( exists $ENV{'REMOTE_HOST'} ) {$who=$ENV{'REMOTE_HOST'} ; }
	elsif (exists $ENV{'REMOTE_ADDR'} ) {$who=$ENV{'REMOTE_ADDR'} ; }
	else {$who="not available"; }
	print TEMPFILE (" Date of message: $year/$mon/$mday $hour:$min\n");
	print TEMPFILE (" From: $who\n");
	print TEMPFILE (" $message0\n");
	print TEMPFILE (" $message1\n");
	close TEMPFILE;
	`/bin/mail -s "Probem with $0" dmo\@globec.whoi.edu <$mailfile`;
	unlink $mailfile;
}

print STDOUT ($prefix," $message0\n");
print STDOUT ($prefix," $message1\n");
print STDOUT ($prefix," Above message from $0\n");
print STDOUT ($prefix," Date of message: $year/$mon/$mday $hour:$min\n");

return 0;
}

#----------------------------------------------------------------------------
sub process_data {

# Process the data from the file(s) specified in $config_param{'mat_file_names'}
# for the field names specified in $config_param{'field_name_<n>'}.

my (
	$batch_file,
	$count,
	@data_array, 
	$error1, $error2,
	@field_name, 
	@file, $file,
	$i, @junk,
	@input_structure,
	$last_value,
	$m_file,
	$output_of_batch_file,
	$output_data_file,
	@output_format,
	@input_position,
	$stuff, 
	);

# Define m file, batch file and output of batch file names

$m_file = 'matlab_m_file_' . $processid . '_' . $time_stamp . '.m';
$batch_file = 	'matlab_batch_' . $processid . '_' . $time_stamp . '.bat';
$output_of_batch_file = 'matlab_output_' . $processid . '_' . $time_stamp . '.out';
$output_data_file = 'matlab_data_' . $processid . '_' . $time_stamp . '.dat';

if (exists $config_param{'output_file_name_root'} and 
   defined $config_param{'output_file_name_root'} ) {
	$m_file = $config_param{'output_file_name_root'} . '/' .$m_file;
	$batch_file = $config_param{'output_file_name_root'} . '/' . $batch_file;
	$output_of_batch_file = $config_param{'output_file_name_root'} . '/' .
				$output_of_batch_file;
	$output_data_file = $config_param{'output_file_name_root'} . '/' .$output_data_file;
}
else {
	$m_file =  '/tmp/' .$m_file;
	$batch_file =  '/tmp/' . $batch_file;
	$output_of_batch_file = '/tmp/' . $output_of_batch_file;
	$output_data_file =  '/tmp/' .$output_data_file;
}

print STDOUT ("#***debug, m file=$m_file\n") if $debug eq "yes";
print STDOUT ("#***debug, batch_file=$batch_file\n") if $debug eq 'yes';
print STDOUT ("#***debug, output_of_batch_file=$output_of_batch_file\n") if $debug eq 'yes';
print STDOUT ("#***debug, output_data=$output_data_file\n") if $debug eq 'yes';

&make_log_entry("M file=$m_file",
		"Batch file=$batch_file",
		"Output of batch file=output_of_batch_file",
		"Output data file=$output_data_file");

unless (open BATCH_FILE, ">$batch_file") {
	&make_log_entry ("Could not open batch file=$batch_file");
	&sendmessage ($error, 
		"Could not open output of batch file $batch_file",
		"Cannot continue.");
	exit;
}

print BATCH_FILE ('#!', $config_param{'cshell'}, ' -f ', "\n");
print BATCH_FILE ("# Catch file to run Matlab\n",
	"# Clear the DISPLAY\n",
	"unsetenv DISPLAY\n");
print BATCH_FILE ("nohup ",$config_param{'matlab_command'}, ' < ',
	"$m_file", ' >&! ', "$output_of_batch_file", "\n");

close BATCH_FILE;
$count = chmod 0777, $batch_file;
unless ($count > 0 ) {
	&make_log_entry ("Could not change mod to execute for file=$batch_file");
	&sendmessage ($error, 
		"Could not change mode to execute for batch file $batch_file",
		"Cannot continue.");
	exit;
}

# Create new Matlab m file

unless (open M_FILE, ">$m_file") {
	$error1 = $!;
	&make_log_entry ("Could not open output Matlab file=$m_file", "Error=$error1");
	&sendmessage ($error, 
		"Could not open output Matlab file $m_file",
		"Error is $error1.  Cannot continue.");
	exit;
}

&make_log_entry("M file created is=$m_file");

# Get input file names and add LOAD command to m file.

@file = split /\s/, $config_param{'mat_file_names'};
print STDOUT ("#***debug,  file(s)=@file\n") if $debug eq "yes";

print M_FILE ("% Matlab method m file created $time_stamp\n");

print M_FILE ("outfile=fopen(\'$output_data_file\',\'w\');\n");


print M_FILE ("fprintf(outfile,\'%s\\n\',\'START OUTPUT NEXT LINE\');\n");

foreach $file (@file) {
	unless ( -e $file and -r $file) {
		&make_log_entry ("Cannot find or read input file=$file");
		&sendmessage ($error, "Cannot find or read input file=$file",
			"Cannot continue");
		exit;
	}
	print M_FILE ("load $file;\n");
}

# Add special commands to execute if in conf file as config_param{command_<n>}

$i=1;
while (exists $config_param{"command_$i"} and 
   defined $config_param{"command_$i"} ) {
   	print M_FILE ($config_param{"command_$i"},"\n");
   	$i++;
}

# Split up $config_param{'field_name_<n>'}

$i = 1;

while (exists $config_param{"field_name_$i"} and 
   defined $config_param{"field_name_$i"} ) {
	($data_array[$i], $field_name[$i], 
	   $input_position[$i], $input_structure[$i], $output_format[$i], 
	   $stuff) = split /,/, 
	   	$config_param{"field_name_$i"};
	unless (defined $data_array[$i] and defined $field_name[$i] and
	   defined $input_position[$i] and defined $input_structure[$i]
	   and defined $output_format[$i] ) {
	   	&make_log_entry ("Input data parameter not defined in field name=$1");
	   	&sendmessage ($error, "Input data parameter not defined.",
	   		"Check field name # $i");
	   	exit;
	}
	unless (lc $input_structure[$i] eq "array" or 
	  lc $input_structure[$i] eq "vector" ) {
		&make_log_entry ("Input structure not valid=$input_structure[$i]");
		&sendmessage ($error, "Input structure not valid as array or vector",
			"Specified as $input_structure[$i]");
		exit;
	}
	$last_value = $i;
	$i++;
}

# Write loop in output file

print M_FILE ("for i=1:length($data_array[1])\n");

for ($i=1; $i <= $last_value; $i++ ) {
	unless ( defined $output_format[$i] and 
	  defined $input_structure[$i]) {next }
	print M_FILE ("\tfprintf(outfile,\'$output_format[$i]\\t\',",
		"$data_array[$i](");
	if ( lc $input_structure[$i] eq "array" ) {
		print M_FILE ("i,$input_position[$i]));\n");
	}
	else {
		print M_FILE ('i))',";\n");
	}
	print STDOUT ("#***debug, input_structure[$i]=",
		$input_structure[$i], "\n") if $debug eq 'yes';
	print STDOUT ("#***debug, output_format[$i]=$output_format[$i]",
		"\n") if $debug eq 'yes';
	
}
print M_FILE ("\tfprintf(outfile,\'\\n\');","\nend\n");
print M_FILE ("fclose(outfile);\nquit;\n");
close M_FILE;

$error2 = system ($batch_file);
print STDERR ("#***debug, error2 (before division)=$error2\n") if $debug eq 'yes';
$error2 = $error2 / 256;
$error1 = chomp $?;
unless ($error2 == 0 and $error1 == 0 ) {
	&make_log_entry ("Could not open Matlab batch program with system call error=$error2",
		"Question error=$error1",
		"Batch file=$batch_file",
		"m file=$m_file");
	&sendmessage($error, "Could not open Matlab batch program with system call error=$error2",
		"Error ?=$error1. Batch file is $batch_file. Output file is $m_file.");
	exit;
}
&make_log_entry("Ran Matlab batch program to output data to file=$output_data_file",
	"Used batch file=$batch_file");

# Output data loop via reading matlab output.  Look first for special start
# string.

open MATLAB_IN, $output_data_file;
$error1 = chomp $!;
unless ($error1 == 0 ) {
	&make_log_entry ("Could not open Matlab output file=$output_data_file",
		"Error=$error1");
	&sendmessage($error,"Could not open Matlab output file=$output_data_file",
		"Error is $error1.  Cannot continue.");
	exit;
}

# Output field names to standard out
for ($i=1; $i <= $last_value; $i++) {
	if (defined $field_name[$i]) {
		print STDOUT ("$field_name[$i]\t");
	}
}
print STDOUT ("\n");

$ready = 'no';
while (<MATLAB_IN>) {
	if ($ready eq 'yes') {
		if (m/^>>/  ) { last}
		print STDOUT ($_);
	}
	else {
		if ( m/START OUTPUT NEXT LINE/ ) {
			$ready = 'yes';
		}
	}
}	

close MATLAB_IN	;
unlink $batch_file if -e $batch_file and $debug eq 'no';
unlink $m_file if -e $m_file and $debug eq 'no';
unlink $output_data_file if -e $output_data_file and $debug eq 'no';
unlink $output_of_batch_file if -e $output_of_batch_file and $debug eq 'no';


undef @junk;

return;
}

#-----------------------------------------------------------------------
sub make_log_entry {

# Make a log entry with @_ if the log file $config_param{"logfile"} 
# exists.  Each entry in @_ are separated by tabs.

my ($login, @month, $temp);
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst);

($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time);
if ($year >= 100 and $year <= 1000) {$year = $year + 1900}
if ($year < 1900) { $year = $year + 2000; }
$mon++;
if ($mon < 10) { $mon = "0" . $mon; }
if ($mday < 10) {$mday = "0" . $mday; }
if ($hour < 10) { $hour = "0" . $hour; }
if ($min < 10) { $min = "0" . $min; }
if ($sec < 10) { $sec = "0" . $sec; }

if (exists $config_param{"logfile"} ) {
	$temp = ">>" . $config_param{'logfile'};
	unless ( open LOGFILE, $temp ) {
		&sendmessage ($warning, "Could not open logfile=$temp",
			"Error code=$!");
	}
	else {
		$login = getlogin || (getpwuid($<))[0] || "Intruder!!";
		$login="\tusername=" . $login;
		print LOGFILE ($year, "/" . $mon . "/" . $mday . ":" . $hour . $min . "." . 
			$sec, $login);
		foreach (@_) {
			print LOGFILE ("\t", $_);
		}
		print LOGFILE ("\n");
		close LOGFILE;
	}
}
return;
}


#-----------------------------------------------------------------

sub mailmessage {

# Send an email message to $_[0].  

#	 $_[0] - address(es) of receiptiants
# 	 $_[1] - message (multiple lines of text)

my ( $mailfile, $message, $who);
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst);

$sendee = $_[0];
$message = $_[1];

($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time);
if ( $year < 1000 ) {$year = $year + 1900; }
$mon++;
if ($mon < 10) { $mon = "0" . $mon; }
if ($mday < 10) {$mday = "0" . $mday; }
if ($hour < 10) { $hour = "0" . $hour; }
if ($min < 10) { $min = "0" . $min; }
if ($sec < 10) { $sec = "0" . $sec; }
$mailfile=">/tmp/sendmess" . $year . $yday . $hour . $min . $sec . ".tmp";

if ( open TEMPFILE, $mailfile) {
	if ( exists $ENV{'REMOTE_HOST'} ) {$who=$ENV{'REMOTE_HOST'} ; }
	elsif (exists $ENV{'REMOTE_ADDR'} ) {$who=$ENV{'REMOTE_ADDR'} ; }
	else {$who="not available"; }
	print TEMPFILE ("Message from $0");
	print TEMPFILE (" Date of message: $year/$mon/$mday $hour:$min\n");
	print TEMPFILE (" From host: $who\n");
	print TEMPFILE (" Message text:\n$message\n");
	close TEMPFILE;
	$sendee =~ s/\@/\\\@/;
	`/usr/bin/mail -s \"FleetLink message\" $sendee <$mailfile`;
	unlink $mailfile;
}
return;
}

#----------------------------------------------------------------------
