/*
* Vis5D system for visualizing five dimensional gridded data sets.
* Copyright (C) 1990 - 2000 Bill Hibbard, Johan Kellum, Brian Paul,
* Dave Santek, and Andre Battaiola.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* As a special exception to the terms of the GNU General Public
* License, you are permitted to link Vis5D with (and distribute the
* resulting source and executables) the LUI library (copyright by
* Stellar Computer Inc. and licensed for distribution with Vis5D),
* the McIDAS library, and/or the NetCDF library, where those
* libraries are governed by the terms of their own licenses.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "../config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <unistd.h>
#include "analysis.h"
#include "globals.h"
#include "grid.h"
#include "memory.h"
#include "proj.h"
#include "queue.h"
#include "socketio.h"
#define SOCK_NAME "/tmp/Vis5d_socket"
#define TEMP_FILE "/tmp/Vis5d_temp"
/*
* Search for external Fortan analysis functions and return a list of
* them.
*
* The search is done as follows:
* Get a directory listing of all files suffixed with .f
* For each file 'foo.f' in the directory {
* If there is a executable file named 'foo' add it to the list.
* }
* Input: path - directory path to search
* In/Out: FuncName - array of function names
* Return: Number of functions found.
*/
int find_analysis_functions( char *path, char FuncName[][1000] )
{
char command[1000];
char fname[1000], fname2[1000];
FILE *f, *g;
int len;
int numfuncs;
numfuncs = 0;
/* put a directory listing into a temporary file */
sprintf( command, "ls > %s %s/*.f", TEMP_FILE, path );
system( command );
f = fopen( TEMP_FILE, "r" );
if (f) {
while (fgets(fname, 1000, f) && numfuncs<MAX_FUNCS) {
/* truncate .f\n from each name */
len = strlen(fname);
if (len>3) {
fname[len-3] = '\0';
/* now see if executable function exists */
g = fopen( fname, "r" );
if (g) {
/* remove path prefix from name */
strcpy( fname2, fname+strlen(path)+1 );
strcpy( FuncName[numfuncs], fname2 );
numfuncs++;
fclose(g);
}
}
}
fclose(f);
}
/* rm the temp file */
unlink( TEMP_FILE );
return numfuncs;
}
/*** start_external_function ******************************************
Start a new process (i.e. begin executing the named program)
and return a socket number to use for communicating with it.
Input: progname - name of executable file to run.
Return: socket number to use for communication with the new
process or -1 if error.
**********************************************************************/
static int start_external_function( Context ctx, char *progname )
{
int s, sock, len;
struct sockaddr_un addr, from;
char command[1000];
int l;
/* first delete the socket's name if it exists */
unlink ( SOCK_NAME );
/* make the socket */
s = socket( PF_UNIX, SOCK_STREAM, 0 );
if (s<0) {
perror("External Function Error: Couldn't create socket:");
return -1;
}
/* bind the socket to its name */
strcpy( addr.sun_path, SOCK_NAME );
addr.sun_family = AF_UNIX;
if ( bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0 ) {
perror("External Function Error: Couldn't bind socket to name:");
return -1;
}
/* change permissions on the socket name in case it has to be rm'd later */
chmod( SOCK_NAME, 0666 ); /* allow read/write by all */
/* Start the external function via system() call */
sprintf( command, "%s %s &", progname, SOCK_NAME );
system( command );
/* wait for external function to connect */
l=listen( s, 20 );
if (l!=0) {
perror("External Function Error: Listen failed");
}
len = sizeof(from);
if ( (sock = accept( s, (struct sockaddr *) &from, &len )) < 0) {
perror("External Function Error: Accept failed");
return -1;
}
close(s); /* don't need this socket anymore */
return sock;
}
/*** stop_external_function *******************************************
Do any cleaning up that's necessary after the external function
is done such as deleting the socket name and closing the socket.
Input: progname - name of executable program.
sock - the socket used to communicate with ext program.
Return: nothing
**********************************************************************/
static int stop_external_function( char *progname, int sock )
{
if (unlink( SOCK_NAME )==-1) {
/*perror("unlink failed [2]");*/
};
close( sock );
return 0;
}
/*** compute_analysis_variable ****************************************
Make a new variable which is computed from an external analysis
function.
Input: var - number of the variable we're computing.
prognam - name of the executable program to call.
Return: 1 if success.
0 if error.
**********************************************************************/
int compute_analysis_variable( Context ctx, int var, char *progname )
{
int sock, ack, time, iv, np, t, i, instances;
float proj_args[100], vert_args[MAXLEVELS];
/* Clear the error variable */
ctx->ExtFuncErrorFlag = 0;
if (NumThreads<=1)
instances = 1;
else
instances = NumThreads - 1;
/* We start a copy of the external function for each background thread */
for (t=1;t<=instances;t++) {
printf("Starting instance %d of %s\n", t, progname );
sock = start_external_function( ctx, progname );
if (sock<0) {
/* there was an error, clean up and return 0 */
for (i=1;i<t;i++) {
stop_external_function( progname, ctx->ExtFuncSocket[i] );
}
return 0;
}
ctx->ExtFuncSocket[t] = sock;
/* wait for an acknowledgment */
receive_int( sock, &ack );
/*
* Send data which is invariant over all time steps...
*/
/* send number of timesteps */
send_int( sock, ctx->NumTimes );
/* Number of variables: all up to the one we're making */
np = var;
send_int( sock, np ); /* Number of variables */
/* Send size of grids */
send_int( sock, ctx->Nr );
send_int( sock, ctx->Nc );
/* Send the number of levels of every parameter */
for (iv=0; iv<np; iv++) {
send_int( sock, ctx->Nl[iv] );
}
/* Send the lowest level of every parameter */
for (iv=0; iv<np; iv++) {
send_int( sock, ctx->Variable[iv]->LowLev );
}
/* Send variable names */
for (iv=0;iv<np;iv++) {
send_str( sock, ctx->Variable[iv]->VarName );
}
/* Send map proj and vert coord sys info */
send_int( sock, ctx->Projection );
switch (ctx->Projection) {
case PROJ_GENERIC:
case PROJ_LINEAR:
proj_args[0] = ctx->NorthBound;
proj_args[1] = ctx->WestBound;
proj_args[2] = ctx->RowInc;
proj_args[3] = ctx->ColInc;
break;
case PROJ_MERCATOR:
proj_args[0] = ctx->CentralLat;
proj_args[1] = ctx->CentralLon;
proj_args[2] = ctx->RowIncKm;
proj_args[3] = ctx->ColIncKm;
case PROJ_ROTATED:
/* WLH 4-21-95 */
proj_args[0] = ctx->NorthBound;
proj_args[1] = ctx->WestBound;
proj_args[2] = ctx->RowInc;
proj_args[3] = ctx->ColInc;
proj_args[4] = ctx->CentralLat;
proj_args[5] = ctx->CentralLon;
proj_args[6] = ctx->Rotation;
break;
case PROJ_LAMBERT:
proj_args[0] = ctx->Lat1;
proj_args[1] = ctx->Lat2;
proj_args[2] = ctx->PoleRow;
proj_args[3] = ctx->PoleCol;
proj_args[4] = ctx->CentralLon;
proj_args[5] = ctx->ColInc;
break;
case PROJ_STEREO:
proj_args[0] = ctx->CentralLat;
proj_args[1] = ctx->CentralLon;
proj_args[2] = ctx->CentralRow;
proj_args[3] = ctx->CentralCol;
proj_args[4] = ctx->ColInc;
break;
}
for (i=0;i<100;i++) {
send_float( sock, proj_args[i] );
}
send_int( sock, ctx->VerticalSystem );
switch (ctx->VerticalSystem) {
case VERT_GENERIC:
case VERT_EQUAL_KM:
vert_args[0] = ctx->BottomBound;
vert_args[1] = ctx->LevInc;
break;
case VERT_NONEQUAL_KM:
case VERT_NONEQUAL_MB:
for (i=0;i<ctx->MaxNl;i++) {
vert_args[i] = ctx->Height[i];
}
break;
}
for (i=0;i<MAXLEVELS;i++) {
send_float( sock, vert_args[i] );
}
/* Send probe location */
{
float row, col, lev;
float lat, lon, hgt;
xyz_to_grid( ctx, ctx->dpy_ctx->CurTime, 0, ctx->dpy_ctx->CursorX, ctx->dpy_ctx->CursorY,
ctx->dpy_ctx->CursorZ, &row, &col, &lev);
xyz_to_geo( ctx, ctx->dpy_ctx->CurTime, 0, ctx->dpy_ctx->CursorX, ctx->dpy_ctx->CursorY,
ctx->dpy_ctx->CursorZ, &lat, &lon, &hgt );
send_float( sock, row );
send_float( sock, col );
send_float( sock, lev );
send_float( sock, lat );
send_float( sock, lon );
send_float( sock, hgt );
ctx->ProbeRow = row;
ctx->ProbeCol = col;
ctx->ProbeLev = lev;
}
/* Send user arguments */
/*
for (i=0;i<NUMARG;i++) {
send_float( sock, &Argument[i] );
}
*/
}
/*
* The external function(s) are now started. Put a request in the
* job queue to compute the external function for each time step.
*/
for (time=0;time<ctx->NumTimes;time++) {
request_ext_func( ctx, time, var );
}
/* Now wait for a signal that the grid for the last timestep
* has been computed.
*/
WAIT_SEM( ctx->ExtFuncDoneSem );
/* Stop the external function(s) */
for (t=1;t<=instances;t++) {
send_int( ctx->ExtFuncSocket[t], -1 ); /* send bad timestep number */
stop_external_function( progname, ctx->ExtFuncSocket[t] );
}
if (ctx->ExtFuncErrorFlag)
return 0;
else
return 1;
}
/*
* Call the currently running external function to compute a new grid
* for a particular time step. This function is called from work.c
*
* Input: time - which timestep
* var - which variable we're computing
* threadnum - thread number in [1..NumThreads-1]
* Return: 1 = ok, 0 = error.
*/
int calc_ext_func( Context ctx, int time, int var, int threadnum )
{
int sock, iv, np, error;
sock = ctx->ExtFuncSocket[threadnum];
send_int( sock, time );
/*printf("sending day: %d\n", ctx->DayStamp[time] );*/
send_int( sock, ctx->DayStamp[time] );
/*printf("sending time: %d\n", ctx->TimeStamp[time] );*/
send_int( sock, ctx->TimeStamp[time] );
/* Number of variables: all up to the one we're computing */
np = var;
/* Send Probe values */
for (iv=0;iv<np;iv++) {
float value = interpolate_grid_value( ctx, time, iv,
ctx->ProbeRow, ctx->ProbeCol, ctx->ProbeLev );
send_float( sock, value );
}
/*printf("np=%d\n", np );*/
for (iv=0;iv<np;iv++) {
/*printf("sending file number: %d\n", ctx->McFile[time][iv] );*/
send_int( sock, ctx->McFile[time][iv] );
/*printf("sending grid number: %d\n", ctx->McGrid[time][iv] );*/
send_int( sock, ctx->McGrid[time][iv] );
if (ctx->McFile[time][iv]==0 && ctx->McGrid[time][iv]==0) {
/* Original McIDAS file data not available, so send */
/* uncompressed data even though it's not too accurate */
float *g;
g = get_grid( ctx, time, iv );
/* Send variable sized grids */
send_data( sock, g, ctx->Nr*ctx->Nc*ctx->Nl[iv]*sizeof(float) );
release_grid( ctx, time, iv, g );
}
}
/* >>> At this point, the user's analysis function is running. <<< */
/*printf("[%d]Waiting for return code\n", threadnum );*/
receive_int( sock, &error ); /* wait for error code */
/*printf("[%d]Received return code: %d\n", threadnum, error );*/
if (error) {
/* Error, print an error message */
printf("External function failed: %d\n", error );
/*stop_external_function( progname, sock );*/
/* set the error flag */
ctx->ExtFuncErrorFlag = 1;
}
else {
/* No error, get the resulting grid */
float *grid;
int outNl, outLowLev;
int nbytes;
/* get number of levels in resulting grid */
receive_int(sock, &outNl);
receive_int(sock, &outLowLev);
/* printf("Received outNl=%d\n", outNl);*/
if (outNl>ctx->MaxNl) outNl = ctx->MaxNl;
ctx->Nl[var] = outNl;
ctx->Variable[var]->LowLev = outLowLev;
/* allocate space for resulting grid and receive the grid data */
nbytes = ctx->Nr * ctx->Nc * outNl * sizeof(float);
grid = (float *) allocate( ctx, nbytes );
receive_data( sock, grid, nbytes );
/* install the new grid */
install_new_grid( ctx, time, var, grid, outNl, outLowLev );
/* may discard data now */
deallocate( ctx, grid, nbytes );
}
if (time==ctx->NumTimes-1) {
/* Signal that we've computed the last time step */
/*printf("Signalling upon last time step\n");*/
SIGNAL_SEM( ctx->ExtFuncDoneSem );
}
if (error)
return 0;
else
return 1;
}
syntax highlighted by Code2HTML, v. 0.9.1