V4_0.java [src/java/m/rusle2] Revision:   Date:
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package m.rusle2;

import csip.Config;
import csip.annotations.Description;
import csip.annotations.Name;
import csip.annotations.Resource;
import static csip.annotations.ResourceType.ARCHIVE;
import static csip.annotations.ResourceType.FILE;
import static csip.annotations.ResourceType.REFERENCE;
import csip.annotations.State;
import static csip.annotations.State.DEVELOPMENT;
import csip.annotations.VersionInfo;
import csip.api.server.Executable;
import csip.api.server.PayloadMetaInfo;
import csip.api.server.ServiceException;
import csip.utils.Client;
import csip.utils.JSONUtils;
import database.ServiceResources;
import static database.ServiceResources.LMOD_ID;
import java.io.File;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import javax.ws.rs.Path;
import static m.rusle2.V3_0.OUT_JSON_TEMPLATE_FILENAME;
import static m.rusle2.V3_0.PYROME;
import static m.rusle2.V3_0.PYROMESRC;
import static m.rusle2.V3_0.PYTHON;
import static m.rusle2.V3_0.PYTHONZIP;
import static m.rusle2.V3_0.REPORT_JSON_TEMPLATE_FILENAME;
import static m.rusle2.V3_0.REPORT_PY_FILENAME;
import static m.rusle2.V3_0.ROMEDLL;
import static m.rusle2.V3_0.potResults;
import nodes.Management;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import soils.Component;
import soils.MapUnit;
import soils.db.SOILS_DATA;
import soils.db.SOILS_DB_Factory;
import soils.utils.EvalResult;
import static util.Constants.*;

/**
 *
 * @author Brad
 */
@Name("Rusle2_Ann")
@Description("Calls the ann service to run R2")
@VersionInfo("4.0")
@State(DEVELOPMENT)
@Path("m/rusle2/4.0")

@Resource(from = ServiceResources.class)

@Resource(type = FILE, file = "/bin/win-x86/2.6.8.4/RomeDLL.dll", id = ROMEDLL)
@Resource(type = FILE, file = "/bin/win-x86/2.6.8.4/pyrome.py", id = PYROMESRC)
@Resource(type = FILE, file = "/bin/win-x86/2.6.8.4/_pyrome.pyd", id = PYROME)
@Resource(type = ARCHIVE, file = "/bin/win-x86/Python34.zip", id = PYTHONZIP)
@Resource(type = REFERENCE, file = "${csip.dir}/bin/win-x86/Python34/python.exe", wine = true, id = PYTHON)

@Resource(type = FILE, file = "/bin/win-x86/rusle2_report.py", id = REPORT_PY_FILENAME)
@Resource(type = FILE, file = "/bin/win-x86/Rusle2_Report.json", id = REPORT_JSON_TEMPLATE_FILENAME)
@Resource(type = FILE, file = "/bin/win-x86/Rusle2_out.json", id = OUT_JSON_TEMPLATE_FILENAME)
public class V4_0 extends V3_0 {

  private Management man;
  private Component comp;
  private int contour = 0;
  private double ann_erosion;

  public static void checkValidResultRequest(PayloadMetaInfo metainfo, Collection<String> results) throws ServiceException {
    String[] j = metainfo.getStringArray(KEY_REQUEST_RESULTS);
    for (int i = 0; i < j.length; i++) {
      String name = j[i];
      if (!results.contains(name)) {
        throw new ServiceException("unknown result request :" + name);
      }
    }
  }

  @Override
  protected void preProcess() throws Exception {
    r2run.setLogger(LOG);
    try {
      reqResults = metainfo().getStringArray(KEY_REQUEST_RESULTS);
      LOG.info("\n" + metainfo().toString());
      checkValidResultRequest(metainfo(), potResults);
    } catch (ServiceException se) {
      LOG.severe("\n\n\nERROR!!!\n\n\n" + se.toString());
      LOG.warning("No Rusle2 return parameters requested! Will use defaults: SLOPE_DELIVERY, SLOPE_T_VALUE, SLOPE_DEGRAD");

      List<String> params = new LinkedList<>();
      params.add(RES_SLOPE_DELIVERY);
      params.add(RES_SLOPE_T_VALUE);
      params.add(RES_SLOPE_DEGRAD);

      // check if these are being set
      if (parameter().has(KEY_CONTOUR_SYSTEM_PTR)) {
        params.add("CONTOUR_SYSTEM_PTR");
      }
      if (parameter().has(KEY_STRIP_BARRIER_SYSTEM_PTR)) {
        params.add("STRIP_BARRIER_SYSTEM_PTR");
      }
      if (parameter().has(KEY_HYD_ELEM_SYSTEM_PTR)) {
        params.add("HYD_ELEM_SYSTEM_PTR");
      }
      reqResults = params.toArray(new String[0]);
    }
    parameter().require(KEY_SOILS, KEY_LENGTH, KEY_STEEPNESS, KEY_MGMTS);

    File r2script = new File(workspace().getDir(), R2_TMP_FILENAME + R2_TMP_FILEEXT);

    createInputFile(r2script, getParamMap());
    gatherAnnData();
  }

  protected void gatherAnnData() throws Exception {

    // get management data
    if (managements.length() > 1) {
      throw new ServiceException("R2 Ann does not handle multiple managements yet.");
    }

    try (Connection connection = resources().getJDBC(LMOD_ID)) {
      org.json.JSONObject managementCopy = new org.json.JSONObject(managements.getJSONObject(0).toString());
      man = new Management(connection, managementCopy);
    }

    // get soil data
    getSoilData(aSoils);

    if (!(contourSystem.equals("") || contourSystem.equals("(none)"))) {
      contour = 1;
    }
    getR_factor();
  }

  private void getSoilData(JSONArray cokeys) throws JSONException, Exception {
    String cokey;
    MapUnit mUnit;

    cokey = cokeys.getString(0);
    comp = new Component();
    comp.cokey(cokey);
    mUnit = getSoilMapUnit();
  }

  private MapUnit getSoilMapUnit() throws ServiceException, SQLException, Exception {
    MapUnit mapUnit = new MapUnit(comp);
    try (SOILS_DATA soilsDb = SOILS_DB_Factory.createEngine(getClass(), LOG, Config.getString("soils.gis.database.source"))) {
      soilsDb.findHorizonsForCokey(mapUnit, comp);
    }
    comp.slopelenusle_r(verifyValue(comp.slopelenusle_l(), comp.slopelenusle_r(), comp.slopelenusle_h()));
    comp.slope_r(verifyValue(comp.slope_l(), comp.slope_r(), comp.slope_h()));
    return mapUnit;
  }

  private double verifyValue(double low_c, double regular_c, double high_c) {
    double value;

    if (EvalResult.testDefaultDouble(low_c)) {
      low_c = 0; // this is an assumption about null values
    }

    if (EvalResult.testDefaultDouble(high_c)) {
      high_c = 0; // this is an assumption about null values
    }

    if (EvalResult.testDefaultDouble(regular_c)) {
      value = low_c + high_c / 2;
    } else {
      value = regular_c;
    }
    return value;
  }

  @Override
  protected void doProcess() throws Exception {
    try {
      Executable python = resources().getExe(V3_0.PYTHON);
      LOG.log(Level.INFO, "EXECUTING PYROME FROM doProcess()...");
      int result = r2run.executePyrome(new File(workspace().getDir(), R2_TMP_FILENAME + R2_TMP_FILEEXT), python, getSUID());
      callAnn();
    } catch (Exception e) {
      LOG.log(Level.SEVERE, "ERROR EXECUTING PYTHON-RUSLE2", e);
      throw e;
    }
  }

  private void callAnn() throws JSONException, Exception {
    JSONObject annRequest = new JSONObject();
    JSONObject meta = new JSONObject();
    JSONArray paramObj = new JSONArray();
    JSONObject jannName;
    JSONObject jslope;
    JSONObject jlength;
    JSONObject jstir;
    JSONObject jcontour;
    JSONObject jkfact;
    JSONObject jsand;
    JSONObject jsilt;
    JSONObject jclay;
    JSONObject jbiomass;
    JSONObject jr_factor;
    JSONObject annResponse;
    JSONArray resultArr;
    JSONObject jresult;
    JSONObject responseMeta;

    Double stir;

    annRequest.put(KEY_METAINFO, meta);
    annRequest.put(KEY_PARAMETER, paramObj);

    jannName = new JSONObject();
    jannName.put(KEY_NAME, ANNNAME);
    jannName.put(VALUE, Config.getString("ann.name"));
    paramObj.put(jannName);

    jslope = new JSONObject();
    jslope.put(KEY_NAME, SLOPE);
    jslope.put(VALUE, steepness);
    paramObj.put(jslope);

    jlength = new JSONObject();
    jlength.put(KEY_NAME, LENGTH);
    jlength.put(VALUE, length);
    paramObj.put(jlength);

    stir = man.calcAverageStir();
    if (stir > 110) {
      stir = 110.0;
    }

    jstir = new JSONObject();
    jstir.put(KEY_NAME, STIR);
    jstir.put(VALUE, stir);
    paramObj.put(jstir);

    jcontour = new JSONObject();
    jcontour.put(KEY_NAME, CONTOUR);
    jcontour.put(VALUE, contour);
    paramObj.put(jcontour);

    jkfact = new JSONObject();
    jkfact.put(KEY_NAME, KFFACT);
    jkfact.put(VALUE, comp.calculated_kffact());
    paramObj.put(jkfact);

    jsand = new JSONObject();
    jsand.put(KEY_NAME, SAND);
    jsand.put(VALUE, comp.calculated_sandtotal_r());
    paramObj.put(jsand);

    jsilt = new JSONObject();
    jsilt.put(KEY_NAME, SILT);
    jsilt.put(VALUE, comp.calculated_silttotal_r());
    paramObj.put(jsilt);

    jclay = new JSONObject();
    jclay.put(KEY_NAME, CLAY);
    jclay.put(VALUE, comp.calculated_claytotal_r());
    paramObj.put(jclay);

    jbiomass = new JSONObject();
    jbiomass.put(KEY_NAME, BIOMASS);
    jbiomass.put(VALUE, man.calcBiomass());
    paramObj.put(jbiomass);

    jr_factor = new JSONObject();
    jr_factor.put(KEY_NAME, R_FACTOR);
    jr_factor.put(VALUE, r_factor);
    paramObj.put(jr_factor);
    LOG.info("ANN Request: " + annRequest.toString());
    annResponse = new Client().doPOST(Config.getString("r2.ann", "http://csip.engr.colostate.edu:8088/csip-ann/m/run/1.0"), annRequest);
    LOG.info("ANN Response: " + annResponse.toString());
    responseMeta = annResponse.getJSONObject(KEY_METAINFO);
    if (!responseMeta.getString(KEY_STATUS).equalsIgnoreCase("Failed")) {
      resultArr = annResponse.getJSONArray(KEY_RESULT);
      jresult = resultArr.getJSONObject(0);
      ann_erosion = jresult.getDouble(VALUE);
    } else {
      String error = responseMeta.getString(ERROR);
      metainfo().appendWarning(error);
    }
  }

  @Override
  protected void postProcess() throws Exception {
    int errors = 0;
    for (int i = 0; i < operations.size(); i++) {
      String op = operations.get(i);
      if (r2run.isUrlReachable(op)) {
        results().put("OPERATION_" + (i + 1), op, "operation is valid");
      } else {
        results().put("OPERATION_INVALID_" + (i + 1), op, "OPERATION IS MISSING!");
        errors++;
      }
    }
    for (int i = 0; i < vegetations.size(); i++) {
      String vege = vegetations.get(i);
      if (r2run.isUrlReachable(vege)) {
        results().put("VEGETATION_" + (i + 1), vege, "vegetation is valid");
      } else {
        results().put("VEGETATION_INVALID_" + (i + 1), vege, "VEGETATION IS MISSING");
        errors++;
      }
    }
    for (int i = 0; i < residues.size(); i++) {
      String res = residues.get(i);
      if (r2run.isUrlReachable(res)) {
        results().put("RESIDUE_" + (i + 1), res, "residue is valid");
      } else {
        results().put("RESIDUE_INVALID_" + (i + 1), res, "RESIDUE IS MISSING");
        errors++;
      }
    }

    for (String r : reqResults) {
      if ((errors > 0) && (r.equals(RES_SLOPE_DEGRAD))) {
        results().put(r, r2run.getResultPyrome(r), R2_MISSING_XML_FILES_WARNING_MSG);
      } else {
        results().put(r, r2run.getResultPyrome(r));
      }
    }

    // Get erosion for segments
    if (JSONUtils.checkKeyExistsB(getParamMap(), KEY_TOPO_LENGTH)) {
      JSONArray aTopoLength = JSONUtils.getJSONArrayParam(getParamMap(), KEY_TOPO_LENGTH);
      if (aTopoLength.length() > 0) {
        JSONArray aSoilLoss = new JSONArray(r2run.getResultPyromeArray("SEG_SOIL_LOSS", false, false, true));
        results().put("SEG_SOIL_LOSS", aSoilLoss);
        results().put(KEY_CLIMATES, climate);
        LOG.info("\n\n\n\n\n\nSEG SOIL OUTPUT=" + r2run.getResultPyromeArray("SEG_SOIL", true, true, true) + "\n\n\n\n");
        LOG.info("\n\n\n\n\n\nSEG SOIL OUTPUT=" + r2run.getResultPyromeArray("SEG_MAN", true, true, true) + "\n\n\n\n");
        results().put(KEY_SOILS, new JSONArray(r2run.getResultPyromeArray("SEG_SOIL", true, true, true)));
        results().put(KEY_MANAGEMENTS, new JSONArray(r2run.getResultPyromeArray("SEG_MAN", true, true, true)));
        if (JSONUtils.checkKeyExistsB(getParamMap(), KEY_DIVERSIONS)) {
          results().put(KEY_DIVERSIONS, r2run.getResultPyromeArray("HYD_ELEM_SYS", true, true, true));
        }

        if (JSONUtils.checkKeyExistsB(getParamMap(), KEY_STRIP_BARRIER_SYSTEMS)) {
          results().put(KEY_STRIP_BARRIER_SYSTEMS, r2run.getResultPyromeArray("STRIP_BARRIER_SYS", true, true, true));
        }
      }
    } else {
      results().put(KEY_CLIMATES, climate);
      results().put(KEY_SOILS, new JSONArray(r2run.getResultPyromeArray("SEG_SOIL", true, true, true)));
      results().put(KEY_MANAGEMENTS, new JSONArray(r2run.getResultPyromeArray("SEG_MAN", true, true, true)));
    }
    results().put(KEY_ANN_EROSION, ann_erosion);
  }

  @Override
  protected Map<String, Object> getConfigInfo() {
    return new LinkedHashMap<String, Object>() {
      {
        put(ROMEDLL, resources().getResolved(ROMEDLL));
      }
    };
  }
}