WEPSServiceT.java [src/java/m/weps] Revision: fe6c776942e0dd8c9659e726c32855185a5303c7  Date: Tue Feb 09 18:19:52 MST 2016
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package m.weps;

import c.PostGIS;
import csip.Config;
import java.io.*;
import java.util.concurrent.Callable;
import java.util.logging.Level;
import javax.ws.rs.Path;
import oms3.annotations.Description;
import oms3.annotations.Name;
import oms3.annotations.VersionInfo;
import oms3.util.ProcessComponent;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.DefaultHttpClient;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import csip.ModelDataService;
import static csip.ModelDataService.EXEC_FAILED;
import static csip.ModelDataService.EXEC_OK;
import static csip.ModelDataService.KEY_METAINFO;
import static csip.ModelDataService.KEY_PARAMETER;
import csip.utils.Services;
import csip.annotations.Polling;
import csip.utils.Binaries;
import csip.utils.JSONUtils;
import static m.weps.SciEnergyParser.LOG;

/**
 * WEPS
 *
 * @author wlloyd, od
 */
@Name("WEPS")
@Description("WEPS")
@VersionInfo("1.0")
@Path("m/wepst/1.0")
@Polling(first = 25000, next = 2000)
public class WEPSServiceT extends ModelDataService {

    String wepsdb = "";
    static final String SOIL_FILE_EXT = ".ifc";

    @Override
    protected void preprocess() throws Exception {
        if (JSONUtils.checkKeyExistsB(getParamMap(), ("climate"))) {
            JSONUtils.checkFileExists(getParamMap().get("climate"), getWorkspaceDir());
        }
        JSONUtils.checkFileExists(getParamMap().get("management"), getWorkspaceDir());
        if (JSONUtils.checkKeyExistsB(getParamMap(), ("soils"))) {
            JSONUtils.checkFileExists(getParamMap().get("soils"), getWorkspaceDir());
        }
        if (JSONUtils.checkKeyExistsB(getParamMap(), ("wind"))) {
            JSONUtils.checkFileExists(getParamMap().get("wind"), getWorkspaceDir());
        }
        // enforce default value for wepsrun if not provided
        LOG.log(Level.INFO, "wepsrun key existence=" + JSONUtils.checkKeyExistsB(getParamMap(), "wepsrun"));
        if (!JSONUtils.checkKeyExistsB(getParamMap(), "wepsrun")) {
            getParamMap().put("wepsrun", JSONUtils.data("wepsrun", "weps.run"));
        }
    }

    @Override
    protected Callable<String> createCallable() throws Exception {
        return new Callable<String>() {
            @Override
            public String call() throws Exception {

                PostGIS db = PostGIS.singleton();
                WepsModelRun wmr = new WepsModelRun();
                String soilPtr = "";
                String soilFilename = "";
                String windDbPath = "";
                String sWindgenStation = "";

                wepsdb = Config.getString("weps.db", "http://oms-db.engr.colostate.edu/weps");

                try {
                    File workDir = getWorkspaceDir();
                    ProcessComponent pc = new ProcessComponent();
                    // /weps -W1 -u0 -I2 -t1 -P./ >stdout.txt 2>stderr.txt
                    String binDir = Config.getString("m.bin.dir", "/tmp/csip/bin");
                    pc.exe = Binaries.unpackResource("/bin/" + Binaries.getArch() + "/weps", new File(binDir)).toString();
                    pc.args = new String[]{"-W1", "-u0", "-I2", "-t1", "-P./"};
                    pc.working_dir = workDir.toString();

                    if (JSONUtils.checkKeyExistsB(getParamMap(), "latitude")) {
                        wmr.setLat(JSONUtils.getValue(getParamMap().get("latitude")));
                    }
                    if (JSONUtils.checkKeyExistsB(getParamMap(), "longitude")) {
                        wmr.setLongitude(JSONUtils.getValue(getParamMap().get("longitude")));
                    }

                    if ((JSONUtils.checkKeyExistsB(getParamMap(), "latitude"))
                            && (JSONUtils.checkKeyExistsB(getParamMap(), "longitude"))
                            && (!(JSONUtils.checkKeyExistsB(getParamMap(), ("climate"))))) {
                        if (db != null) {
                            // get climate station for this lat / long
                            PostGIS.StationResult cligenStation = db.findCligenStation(Double.parseDouble(wmr.getLat()), Double.parseDouble(wmr.getLongitude()));
                            // generate cli file using this station
                            ProcessComponent pcCliGen = new ProcessComponent();
                            pcCliGen.exe = Binaries.unpackResource("/bin/" + Binaries.getArch() + "/cligen", new File(binDir)).toString();
                            String dbpath = Binaries.unpackResource("/bin/" + Binaries.getArch() + "/upd_US_cligen_stations.par", new File(binDir)).toString();
                            pcCliGen.args = new String[]{"-S" + cligenStation.state, "-s" + cligenStation.stationId, "-i" + dbpath, "-t5", "-I3", "-F", "-b01", "-y150", "-o" + pc.working_dir + "/cligen.cli"};
                            LOG.log(Level.INFO, "cligen args=" + pcCliGen.args.toString());
                            pcCliGen.working_dir = workDir.toString();
                            pcCliGen.execute();
                            // prefer lat/long generated climates file for weps run
                            wmr.setCliFile("cligen.cli");
                        }
                    }

                    // If no wind file, use wind station(s) to generate wind data
                    if ((JSONUtils.checkKeyExistsB(getParamMap(), "latitude"))
                            && (JSONUtils.checkKeyExistsB(getParamMap(), "longitude"))
                            && (!(JSONUtils.checkKeyExistsB(getParamMap(), ("wind"))))) {
                        if (db != null) {
                            double latitude = Double.parseDouble(wmr.getLat());
                            double longitude = Double.parseDouble(wmr.getLongitude());

                            // check if in the interpolation boundary shape, if so, interpolate 3 nearest wind stations
                            // to generate an interpolated station
                            LOG.log(Level.INFO, "Check if lat/long is in interpolation boundary");
                            if (db.IsInInterpolateBoundary(latitude, longitude)) {
                                LOG.log(Level.INFO, "YES! lat/long is in interpolation boundary");
                                // Generate wind station interpolation

                                // Generate weights file
                                ProcessComponent pcInterpolate = new ProcessComponent();
                                String exepath = Binaries.unpackResource("/bin/" + Binaries.getArch() + "/interpolate", new File(binDir)).toString();
                                String dbpath = Binaries.unpackResource("/bin/" + Binaries.getArch() + "/wind_gen_his_upper_US_NRCS.idx", new File(binDir)).toString();
                                String polygon = Binaries.unpackResource("/bin/" + Binaries.getArch() + "/WINDGEN_DEC_13_2011.pol", new File(binDir)).toString();

                                // Generate shell script to invoke interpolate
                                File interpolatesh = new File(workDir.toString() + "/interpolate.sh");
                                FileWriter fw = new FileWriter(interpolatesh);
                                String invokeInterpolate = exepath + " -f " + dbpath + " -p " + polygon + " -lat " + latitude + " -long " + longitude;
                                fw.write(invokeInterpolate);
                                interpolatesh.setExecutable(true);
                                fw.close();

                                pcInterpolate.working_dir = workDir.toString();
                                pcInterpolate.exe = "./interpolate.sh";
                                pcInterpolate.args = new String[]{};
                                pcInterpolate.execute();

                                WeightsParser wp = new WeightsParser();
                                wp.weightsFile = pcInterpolate.stdout;
                                wp.exitValue = pcInterpolate.exitValue;
                                wp.weightsErr = pcInterpolate.stderr;

                                // must process this monstrosity 
                                wp.parse();

                                if ((wp.station1 > 0) & (wp.station2 > 0) && (wp.station3 > 0)) {
                                    // If there are wind stations then, generate interpolate wind station wdb file

                                    // Note the interp_wdb program is ancient Fortran and can not handle file paths of any length
                                    // therefore everything has to be extracted to the workdir and done locally there.
                                    // This is BAD (inefficient) because it is extra work to always extract wind station wdb's for every model run!
                                    ProcessComponent pcInterpWdb = new ProcessComponent();
                                    exepath = Binaries.unpackResource("/bin/" + Binaries.getArch() + "/interp_wdb", new File(binDir)).toString();
                                    String station1Db = Binaries.unpackResourceAbsolute("/bin/" + Binaries.getArch() + "/windstations/" + wp.station1 + ".wdb", workDir.toString() + "/" + wp.station1 + ".wdb").toString();
                                    String station2Db = Binaries.unpackResourceAbsolute("/bin/" + Binaries.getArch() + "/windstations/" + wp.station2 + ".wdb", workDir.toString() + "/" + wp.station2 + ".wdb").toString();
                                    String station3Db = Binaries.unpackResourceAbsolute("/bin/" + Binaries.getArch() + "/windstations/" + wp.station3 + ".wdb", workDir.toString() + "/" + wp.station3 + ".wdb").toString();

                                    // Generate shell script to invoke interpolate
                                    File interpwdbsh = new File(workDir.toString() + "/interpwdb.sh");
                                    fw = new FileWriter(interpwdbsh);
                                    String invokeInterpWdb = exepath + " test.wdb" + " " + wp.station1 + ".wdb " + wp.weight1 + " " + wp.station2 + ".wdb " + wp.weight2 + " " + wp.station3 + ".wdb " + wp.weight3;
                                    fw.write(invokeInterpWdb);
                                    interpwdbsh.setExecutable(true);
                                    fw.close();

                                    pcInterpWdb.working_dir = workDir.toString();
                                    pcInterpWdb.exe = "./interpwdb.sh";
                                    pcInterpWdb.args = new String[]{};
                                    pcInterpWdb.execute();
                                    windDbPath = "test.wdb";
                                    sWindgenStation = "999999";  // this is the magic number indicating its an interpolated wind station!
                                }
                            }

                            // otherwise, just use data from nearest wind station:

                            // If this is not using an interpolated wind station, then use the default one...
                            if (windDbPath.length() == 0) {
                                LOG.log(Level.INFO, "NO! lat/long is NOT in interpolation boundary");
                                // get wind generation station for this lat / long
                                PostGIS.StationResult windgenStation = db.findWindgenStation(Double.parseDouble(wmr.getLat()), Double.parseDouble(wmr.getLongitude()));
                                sWindgenStation = windgenStation.stationId.trim();

                                windDbPath = Binaries.unpackResource("/bin/" + Binaries.getArch() + "/wind_gen_his_upper_US.wdb", new File(binDir)).toString();
                            }

                            // generate win file using this station (or interpolated one)
                            ProcessComponent pcWindGen = new ProcessComponent();
                            String exepath = Binaries.unpackResource("/bin/" + Binaries.getArch() + "/wind_gen4", new File(binDir)).toString();
                            LOG.log(Level.INFO, "Using wind database:" + windDbPath);

                            // Generate shell script to invoke windgen
                            File windgensh = new File(workDir.toString() + "/windgen.sh");
                            FileWriter fw = new FileWriter(windgensh);
                            String invokeWinGen = exepath + " -f " + windDbPath + " -b 01 -y 150 -o thewind.win -s " + sWindgenStation;
                            fw.write(invokeWinGen);
                            windgensh.setExecutable(true);
                            fw.close();

                            pcWindGen.working_dir = workDir.toString();
                            String cmd = "./windgen.sh";
                            pcWindGen.args = new String[]{};
                            pcWindGen.exe = cmd;
                            pcWindGen.execute();

                            wmr.setWinFile("thewind.win");
                        }
                    }

                    // Get weps soils ifc file for lat & long
                    if ((JSONUtils.checkKeyExistsB(getParamMap(), "latitude"))
                            && (JSONUtils.checkKeyExistsB(getParamMap(), "longitude"))
                            && (!(JSONUtils.checkKeyExistsB(getParamMap(), ("soils"))))) {
                        if (db != null) {
                            PostGIS.FileQryResult soil = db.findSoilsWeps(Double.parseDouble(wmr.getLat()), Double.parseDouble(wmr.getLongitude()));
                            if (soil == null) {
                                LOG.log(Level.WARNING, "No soil for lat=" + wmr.getLat() + "\nfor long=" + wmr.getLongitude() + "\n");
                                soilPtr = "";
                            } else {
                                soilPtr = soil.file_path + "/" + soil.file_name + SOIL_FILE_EXT;
                                // Get soil file and write to temp space
                                String fileToGet = wepsdb + "/" + soilPtr;
                                LOG.log(Level.INFO, "soils file to get=" + fileToGet);
                                getFile(fileToGet, pc.working_dir, soil.file_name + SOIL_FILE_EXT);
                                // Prefer lat/long retrieved soils file 
                                wmr.setSoilFile(soil.file_name + SOIL_FILE_EXT);
                            }
                        }
                    }

                    // Generate Weps Run file from JSON parameters if not provided
                    try {
                        JSONUtils.checkFileExists(getParamMap().get("wepsrun"), getWorkspaceDir());
                    } catch (Exception e) {
                        LOG.log(Level.INFO, "wepsrun file does not exist, creating it.");
                        // Only generate weps.run file if it wasn't provided

                        // Override default weps run configuration parameters here with values from JSON object
                        if (JSONUtils.checkKeyExistsB(getParamMap(), ("climate"))) {
                            wmr.setCliFile(JSONUtils.getValue(getParamMap().get("climate")));
                        }
                        //wmr.setMgmtFile(JSONUtils.getValue(getParamMap().get("management")));
                        if (JSONUtils.checkKeyExistsB(getParamMap(), ("soils"))) {
                            wmr.setSoilFile(JSONUtils.getValue(getParamMap().get("soils")));
                        }
                        if (JSONUtils.checkKeyExistsB(getParamMap(), ("wind"))) {
                            wmr.setWinFile(JSONUtils.getValue(getParamMap().get("wind")));
                        }
                        String wepsrunFile = JSONUtils.getValue(getParamMap().get("wepsrun"));
                        LOG.log(Level.INFO, "weps run file name=" + wepsrunFile);
                        WepsRunFileGenerator.GenerateWepsRunFile(wmr, pc.working_dir, wepsrunFile);
                    }

                    System.out.println("exe " + pc.exe);
                    System.out.println("running.");
                    pc.execute();

                    System.out.println(pc.exitValue);

                    FileUtils.write(new File(workDir, "stdout.txt"), pc.stdout);
                    FileUtils.write(new File(workDir, "stderr.txt"), pc.stderr);

                    return pc.exitValue == 0 ? EXEC_OK : EXEC_FAILED;
                } catch (Exception e) {
                    LOG.log(Level.SEVERE, e.toString());
                    LOG.log(Level.SEVERE, e.getStackTrace().toString());
                    throw new Exception(e);
                    //return -1;
                }
            }
        };
    }

    private void getFile(String url, String destDir, String filename) {

        FileOutputStream fos = null;
        try {

            HttpClient client = new DefaultHttpClient();
            HttpGet httpget = new HttpGet(url);
            HttpResponse response = client.execute((HttpUriRequest) httpget);


            byte[] soilData = IOUtils.toByteArray(response.getEntity().getContent());
            fos = new FileOutputStream(new File(destDir + "/" + filename));
            fos.write(soilData);
            fos.close();
        } catch (IOException ie) {
            LOG.log(Level.SEVERE, "ERROR GETTING SOILS IFC FILE!:" + ie.toString());
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (Exception fe) {
                }
            }
        }
    }

    @Override
    protected File[] postprocess() throws Exception {
        return Services.toFiles(
            "stir_energy.out",
            "sci_energy.out",
            "stdout.txt",
            "stderr.txt");
    }

//    @Override
//    protected JSONObject describe() throws JSONException {
//
//        JSONArray p = new JSONArray();
//
////        p.put(JSONUtils.data(KEY_CLIMATES, ""));
////        p.put(JSONUtils.data(KEY_SOILS, ""));
////        p.put(JSONUtils.data(KEY_MANAGEMENTS, ""));
////        p.put(JSONUtils.data(KEY_STEEPNESS, 3.0));
////        p.put(JSONUtils.data(KEY_LENGTH, 10.0));
////        p.put(JSONUtils.data(KEY_RESOLVE_LOCATION, true));
////        p.put(JSONUtils.data(KEY_LATITUDE, 36.250515));
////        p.put(JSONUtils.data(KEY_LONGITUDE, -84.026264));
////
//        JSONObject metainfo = new JSONObject();
//////        metainfo.put(KEY_REQUEST_RESULTS, new JSONArray(potResults));
//
//        JSONObject model = new JSONObject();
//        model.put(KEY_PARAMETER, p);
//        model.put(KEY_METAINFO, metainfo);
//        return model;
//    }

}