V3_0.java [src/java/m/rusle2] Revision: 2ad005e4d9ae0f317d03e0a18ddbcea8999bdf11  Date: Thu Feb 03 19:47:06 MST 2022
/*
 * $Id$
 *
 * This file is part of the Cloud Services Integration Platform (CSIP),
 * a Model-as-a-Service framework, API, and application suite.
 *
 * 2012-2017, OMSLab, Colorado State University.
 *
 * OMSLab licenses this file to you under the MIT license.
 * See the LICENSE file in the project root for more information.
 */
package m.rusle2;

import csip.utils.Client;
import csip.Config;
import csip.api.server.Executable;
import csip.ModelDataService;
import static csip.ModelDataService.ERROR;
import static csip.ModelDataService.KEY_METAINFO;
import static csip.ModelDataService.KEY_NAME;
import static csip.ModelDataService.KEY_PARAMETER;
import static csip.ModelDataService.KEY_REQUEST_RESULTS;
import static csip.ModelDataService.KEY_RESULT;
import static csip.ModelDataService.KEY_STATUS;
import static csip.ModelDataService.KEY_VALUE;
import static csip.ModelDataService.REPORT_DESC;
import static csip.ModelDataService.REPORT_DIM;
import static csip.ModelDataService.REPORT_NAME;
import static csip.ModelDataService.REPORT_TYPE;
import static csip.ModelDataService.REPORT_UNITS;
import static csip.ModelDataService.VALUE;
import csip.api.server.ServiceException;
import static csip.Utils.removeFirstLastChar;
import csip.annotations.*;
import static csip.annotations.ResourceType.ARCHIVE;
import static csip.annotations.ResourceType.FILE;
import static csip.annotations.ResourceType.REFERENCE;
import static csip.annotations.State.RELEASED;
import csip.utils.JSONUtils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.*;
import java.util.logging.Level;
import javax.ws.rs.Path;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import static m.rusle2.V3_0.*;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringEscapeUtils;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import static util.Constants.*;

/**
 * REST Web Service. Front end callable.
 *
 * @author wlloyd, od
 */
@Name("Rusle2")
@Description("IET / pyrome version of RomeDLL 2.6.8.4; references NRCS Soil Data Mart")
@VersionInfo("3.0")
@State(RELEASED)
@Path("m/rusle2/3.0")
@Polling(first = 1000, next = 1000)

@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)

public class V3_0 extends ModelDataService {

  static final boolean PRODUCTION_MODE = true;
  //
  static final String R2_TMP_FILENAME = "rusle2csip";
  static final String ROMEDLL = "RomeDLL.dll";
  static final String PYROMESRC = "pyrome.py";
  static final String PYROME = "_pyrome.pyd";
  static final String PYTHONZIP = "Python34.zip";
  static final String PYTHON = "python.exe";
  static final String R2_TMP_FILEEXT = ".py";
  static final String IO_TIMING_FILENAME = "/tmp/io-timing";
  static final String IO_TIMING_FILEEXT = ".txt";
  static final String REPORT_JSON_TEMPLATE_FILENAME = "Rusle2_Report.json";
  static final String REPORT_PY_FILENAME = "rusle2_report.py";
  static final String KEY_COKEY = "cokey";

  /*
     * The R2 Run
   */
  final R2Run r2run = new R2Run();
  static final List<String> potResults = Collections.unmodifiableList(Arrays.asList(
      RES_SLOPE_DELIVERY,
      RES_SLOPE_T_VALUE,
      RES_SLOPE_DEGRAD,
      RES_SLOPE_EQUIV_DIESEL_USE_PER_AREA,
      RES_SOIL_COND_INDEX_STIR_VAL,
      RES_SOIL_COND_INDEX_RESULT,
      RES_SEG_SIM_DAY_LIVE_BIOMASS,
      RES_SEG_SIM_DAY_COVER_MASS_SUM,
      RES_SEG_SIM_DAY_STAND_MASS_SUM,
      RES_SEG_SIM_DAY_CANOPY_COVER,
      RES_SEG_SIM_DAY_PERENN_VEG_LIVE_HEIGHT,
      RES_SLOPE_SIM_DAY_DEGRAD,
      RES_SURF_RES_OUTPUTS_SURF_COV_AT_OP,
      RES_SOIL_COND_INDEX_OM_SUBFACTOR,
      RES_SOIL_COND_INDEX_FO_SUBFACTOR,
      RES_SOIL_COND_INDEX_ER_SUBFACTOR));
  //
  String climate;
  String soil;
  String mgmt;
  List<String> operations;
  List<String> vegetations;
  List<String> residues;
  // moved these to globals so both R2 and ann can use them.
  protected double steepness;
  protected double length;
  protected String contourSystem;
  protected JSONArray managements;
  protected JSONArray aSoils;
  protected String climatePtr;

  String[] reqResults;

  //

  @Override
  protected void preProcess() throws Exception {
    r2run.setLogger(LOG);
    try {
      reqResults = metainfo().getStringArray(KEY_REQUEST_RESULTS);
      LOG.info("\n" + metainfo().toString());
      V4_0.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 (JSONUtils.checkKeyExistsB(getParamMap(), KEY_CONTOUR_SYSTEM_PTR)) {
        params.add("CONTOUR_SYSTEM_PTR");
      }

      if (JSONUtils.checkKeyExistsB(getParamMap(), KEY_STRIP_BARRIER_SYSTEM_PTR)) {
        params.add("STRIP_BARRIER_SYSTEM_PTR");
      }

      if (JSONUtils.checkKeyExistsB(getParamMap(), 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);

    if (!r2run.isUrlReachable("climates/default.xml")) {
      throw new ServiceException("R2 file server unreachable at: " + Config.getString("r2.db", "http://oms-db.engr.colostate.edu/r2"));
    }

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

  // protect the DLL?
  private static final Object romeLock = new Object();
  private static boolean lockRusle = Config.getBoolean("r2.3.0.lock", false);


  @Override
  protected void doProcess() throws Exception {
    try {
      Executable python = resources().getExe(V3_0.PYTHON);
      if (lockRusle) {
        synchronized (romeLock) {
          LOG.log(Level.INFO, "EXECUTING PYROME FROM doProcess()...");
          int result = r2run.executePyrome(new File(workspace().getDir(), R2_TMP_FILENAME + R2_TMP_FILEEXT), python, getSUID());
        }
      } else {
        LOG.log(Level.INFO, "EXECUTING PYROME FROM doProcess()...");
        int result = r2run.executePyrome(new File(workspace().getDir(), R2_TMP_FILENAME + R2_TMP_FILEEXT), python, getSUID());
      }

    } catch (Exception e) {
      LOG.log(Level.SEVERE, "ERROR EXECUTING PYTHON-RUSLE2", e);
      throw e;
    }
  }


  @Override
  protected void postProcess() throws Exception {
    int errors = 0;
    for (int i = 0; i < operations.size(); i++) {
      String op = operations.get(i);
      if (r2run.isFileUrlReachable(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.isFileUrlReachable(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.isFileUrlReachable(res)) {
        results().put("RESIDUE_" + (i + 1), res, "residue is valid");
      } else {
        results().put("RESIDUE_INVALID_" + (i + 1), res, "RESIDUE IS MISSING");
        errors++;
      }
    }

//      results.put(JSONUtils.data(KEY_SLOPE_DELIVERY, 3.0));
    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)));
    }
  }


  protected void createInputFile(File file, Map<String, JSONObject> param)
      throws Exception {

    steepness = JSONUtils.getDoubleParam(param, KEY_STEEPNESS, 0.01);
    if (steepness < 0.01)
      steepness = 0.01;

    length = JSONUtils.getDoubleParam(param, KEY_LENGTH, 0.0);
    boolean resolveLoc = JSONUtils.getBooleanParam(param, KEY_RESOLVE_LOCATION, false);
    double latitude = JSONUtils.getDoubleParam(param, KEY_LATITUDE, 0.0);
    double longitude = JSONUtils.getDoubleParam(param, KEY_LONGITUDE, 0.0);
    double simpleRockCoverPercent = JSONUtils.getDoubleParam(param, KEY_SIMPLE_ROCK_COVER, 0.0);

    LOG.info("Rock cover read by service is=" + simpleRockCoverPercent);

    JSONArray aRockCover = JSONUtils.getJSONArrayParam(param, KEY_SIMPLE_ROCK_COVER);
    for (int i = 0; i < aRockCover.length(); i++) {
      LOG.info("Rock cover [" + i + "]=" + aRockCover.getDouble(i));
      // this is temporary, since R2 will be using slope segments soon
      if (i == 0) {
        simpleRockCoverPercent = aRockCover.getDouble(i);
      }
    }

    climatePtr = JSONUtils.getStringParam(param, KEY_CLIMATES, "");

    aSoils = JSONUtils.getJSONArrayParam(param, KEY_SOILS);
    String soilPtr[] = new String[(aSoils.length())];
    for (int i = 0; i < aSoils.length(); i++) {
      LOG.info("soil [" + i + "]=" + aSoils.getInt(i));
    }

    contourSystem = "";
    String stripBarrierSystem = "";
    String hydElemSystem = "";

    // Get the Contour System if not default
    if (JSONUtils.checkKeyExistsB(param, KEY_CONTOUR_SYSTEM_PTR)) {
      contourSystem = JSONUtils.getStringParam(param, KEY_CONTOUR_SYSTEM_PTR, "");
    }

    // Get the Strip Barrier System
    if (JSONUtils.checkKeyExistsB(param, KEY_STRIP_BARRIER_SYSTEM_PTR)) {
      stripBarrierSystem = JSONUtils.getStringParam(param, KEY_STRIP_BARRIER_SYSTEM_PTR, "");
    }

    // Get the Hydraylic Element System Pointer
    if (JSONUtils.checkKeyExistsB(param, KEY_HYD_ELEM_SYSTEM_PTR)) {
      hydElemSystem = JSONUtils.getStringParam(param, KEY_HYD_ELEM_SYSTEM_PTR, "");
    }

    JSONArray aTopoLength = new JSONArray();
    JSONArray aTopoSteepness = new JSONArray();
    JSONArray aSoilIdx = new JSONArray();
    JSONArray aManIdx = new JSONArray();
    JSONArray aManLength = new JSONArray();
    if (JSONUtils.checkKeyExistsB(param, KEY_TOPO_LENGTH)) {
      aTopoLength = JSONUtils.getJSONArrayParam(param, KEY_TOPO_LENGTH);
      for (int i = 0; i < aTopoLength.length(); i++) {
        LOG.info("Topo Length [" + i + "]=" + aTopoLength.getDouble(i));
      }
    }

    if (JSONUtils.checkKeyExistsB(param, KEY_TOPO_STEEPNESS)) {
      aTopoSteepness = JSONUtils.getJSONArrayParam(param, KEY_TOPO_STEEPNESS);
      for (int i = 0; i < aTopoSteepness.length(); i++) {
        LOG.info("Topo Steepness [" + i + "]=" + aTopoSteepness.getDouble(i));
      }
    }

    if (JSONUtils.checkKeyExistsB(param, KEY_SOIL_INDEX)) {
      aSoilIdx = JSONUtils.getJSONArrayParam(param, KEY_SOIL_INDEX);
      for (int i = 0; i < aSoilIdx.length(); i++) {
        LOG.info("Soil Index [" + i + "]=" + aSoilIdx.getInt(i));
        //            if (i==0)
        //                steepness = aSoilIdx.getInt(i);
      }
    }

    if (JSONUtils.checkKeyExistsB(param, KEY_MAN_INDEX)) {
      aManIdx = JSONUtils.getJSONArrayParam(param, KEY_MAN_INDEX);
      for (int i = 0; i < aManIdx.length(); i++) {
        LOG.info("Man Index [" + i + "]=" + aManIdx.getInt(i));
        //            if (i==0)
        //                steepness = aTopoSteepness.getDouble(i);
      }
    }

    if (JSONUtils.checkKeyExistsB(param, KEY_MAN_LENGTH)) {
      aManLength = JSONUtils.getJSONArrayParam(param, KEY_MAN_LENGTH);
      for (int i = 0; i < aManLength.length(); i++) {
        LOG.info("Man Length [" + i + "]=" + aManLength.getInt(i));
      }
    }

    if (JSONUtils.checkKeyExistsB(param, KEY_TOPO_LENGTH)) {
      if (!allEqual(aTopoLength.length(), aTopoSteepness.length())) {
        throw new ServiceException("RUSLE2 SEGMENT RUN ERROR! SEGMENT ARRAYS FOR TOPO LENGTH AND TOPO STEEPNESS MUST BE OF EQUAL SIZE!");
      }
    }

    if (JSONUtils.checkKeyExistsB(param, KEY_MAN_INDEX)) {
      if (!allEqual(aManLength.length(), aManIdx.length())) {
        throw new ServiceException("RUSLE2 SEGMENT RUN ERROR! SEGMENT ARRAYS FOR MGMT LENGTH AND MGMT INDEX MUST BE OF EQUAL SIZE!");
      }
    }

    //soilPtr = JSONUtils.getStringParam(param, KEY_SOILS, "");
    String managementFormalName[] = new String[]{};

    // management conversion
    // Make file line separator unix compatible. ???? not sure if needed.
    System.setProperty("line.separator", "\n");
    //
    managements = JSONUtils.getJSONArrayParam(param, KEY_MGMTS);

    LOG.info("managements array=" + managements.toString());
    managementFormalName = new String[managements.length()];

    for (int i = 0; i < managements.length(); i++) {
      LOG.info("management #" + i);

      JSONObject lmod = (JSONObject) managements.get(i);
      LOG.info(lmod.toString());

      LOG.info("creating Jim's translator.");
      lmod2rusle2.Rusle2Translator translator = new lmod2rusle2.Rusle2Translator();
      LOG.info("trying to put in the lmod json");
      translator.readJsonString(lmod.toString());
      LOG.info("trying to translate=" + translator.Translate());

      Document r2_xml = translator.getDocument();

      LOG.info("R2 XML FILE from trhe translator:\n\n" + translator.getRusle2Xml());
      LOG.info("R2 XML FILE:\n\n" + translator.getRusle2Xml());
      //LOG.info("LMOD XML FILE:\n\n" + lmod_xml.getTextContent());

      if ((r2_xml != null) && (r2_xml.getElementsByTagName("Filename") != null)) {
        NodeList nl = r2_xml.getElementsByTagName("Filename");
        for (int ii = 0; ii < nl.getLength(); ii++) {
          LOG.info("filename node=" + nl.item(ii).getTextContent());
          managementFormalName[i] = nl.item(ii).getTextContent();
        }
      }
      translator.writeRusle2Xml(workspace().getDir().toString(), "lmod_file" + i + ".xml");
      operations = translator.getOperationFiles();
      vegetations = translator.getVegetationFiles();
      residues = translator.getResidueFiles();

      for (String operation : translator.getOperationFiles()) {
        System.out.println("operation=" + operation);
      }
      for (String vege : translator.getVegetationFiles()) {
        System.out.println("vegetation=" + vege);
      }
      for (String residue : translator.getResidueFiles()) {
        System.out.println("residue=" + residue);
      }
    }

    String altR2db = null;
    if (param.get(KEY_ALT_R2DB) != null) {
      altR2db = param.get(KEY_ALT_R2DB).getString(VALUE);
    }

    // If this is a Lat/Lng request, then we need to make a DB lookup
    // to obtain the Climate and Soil information and ignore whatever
    // may have been passed in
    LOG.info("Rulse 2 model request resolveLocation=" + resolveLoc);
    //if (resolveLoc) {

    climatePtr = getClimateFilePath(latitude, longitude);
    soilPtr = getSoilFiles(aSoils);

    FileOutputStream fos = new FileOutputStream(file);
    fos.write("import sys\n".getBytes());
    // could rewrite this line based on the OS, but python is interpretting it ok on windows
    fos.write(("sys.path.append('" + resources().getFile(PYROMESRC).getParent().replace('\\', '/') + "')\n").getBytes());

    fos.write("from pyrome import *\n".getBytes());
    fos.write("from time import sleep\n".getBytes());

    fos.write("def linearTest(save = False, openFlags = RX_FILESOPEN_TEMP):\n".getBytes());
    fos.write("    romeDLL = RomeInit('')\n".getBytes());
    fos.write("    files = RomeGetFiles(romeDLL)\n".getBytes());
    fos.write("    engine = RomeGetEngine(romeDLL)\n".getBytes());
    fos.write("    RomeEngineSetAutorun(engine,RX_FALSE)\n".getBytes());
    fos.write("    #Inputs\n".getBytes());

    if (aTopoLength.length() > 1) {
      String text = "    slopes=[";
      for (int i = 0; i < aTopoSteepness.length(); i++) {
        text += "'" + aTopoSteepness.getInt(i) + "'";
        text += (i == aTopoSteepness.length() - 1) ? "]\n" : ",";
      }
      fos.write(text.getBytes());
      text = "    lengths=[";
      for (int i = 0; i < aTopoLength.length(); i++) {
        text += "'" + aTopoLength.getInt(i) + "'";
        text += (i == aTopoLength.length() - 1) ? "]\n" : ",";
      }
      fos.write(text.getBytes());
      if ((aSoilIdx != null) && (aSoilIdx.length() > 0)) {
        text = "    soilIndex=[";
        for (int i = 0; i < aSoilIdx.length(); i++) {
          text += aSoilIdx.getInt(i) + "";
          text += (i == aSoilIdx.length() - 1) ? "]\n" : ",";
        }
        fos.write(text.getBytes());
      }
      text = "    manIndex=[";
      for (int i = 0; i < aManIdx.length(); i++) {
        text += aManIdx.getInt(i) + "";
        text += (i == aManIdx.length() - 1) ? "]\n" : ",";
      }
      fos.write(text.getBytes());

    } else {
      String text = "    slopes=['" + steepness + "']\n";
      fos.write(text.getBytes());
      text = "    lengths=['" + length + "']\n";
      fos.write(text.getBytes());
      fos.write("    soilIndex=[0]\n".getBytes());
      fos.write("    manIndex=[0]\n".getBytes());
    }

    fos.write("    results = list()\n".getBytes());

    fos.write("    #Run a simple profile\n".getBytes());
    fos.write("    profile = RomeFilesOpen(files,'profiles\\\\csippyrome',openFlags)\n".getBytes());

    // Open all of the mgmt files...
    for (int i = 0; i < managements.length(); i++) {
      String text = "    mgmt = RomeFilesOpen(files, '#XML:lmod_file" + i + ".xml',0)\n";
      fos.write(text.getBytes());
    }

    fos.write("    cli = RomeFilesOpen(files, '#XML:cli_file0.xml',0)\n".getBytes());

    for (int i = 0; i < aSoils.length(); i++) {
      String text = "    soil" + i + " = RomeFilesOpen(files, '#XML:soils_file" + i + ".xml',0)\n";
      fos.write(text.getBytes());
    }

    fos.write("    # SET SLOPE\n".getBytes());
    String text = "    RomeFileSetAttrValue(profile, 'SLOPE_HORIZ', '" + length + "', 0)\n";
    fos.write(text.getBytes());
    text = "    RomeFileSetAttrValue(profile, 'SLOPE_STEEP', '" + steepness + "', 0)\n";
    fos.write(text.getBytes());

    fos.write("    # SET CLIMATE_PTR\n".getBytes());
    fos.write("    RomeFileSetAttrValue(profile, 'CLIMATE_PTR','climates\\\\aaa',0)\n".getBytes());
    fos.write("    print('CLIMATE_PTR=%s' % RomeFileGetAttrValue(profile, 'CLIMATE_PTR', 0))\n".getBytes());

    fos.write("    # SET TOPO LAYER\n".getBytes());
    if (aTopoLength.length() > 0) {
      text = "    RomeFileSetAttrSize(profile, 'TOPO_LAYER', " + aTopoLength.length() + ")\n";
      fos.write(text.getBytes());
      for (int i = 0; i < aTopoLength.length(); i++) {
        text = "    RomeFileSetAttrValue(profile, 'TOPO_HORIZ', '" + aTopoLength.getString(i) + "', " + i + ")\n";
        fos.write(text.getBytes());
        String tstr = aTopoSteepness.getString(i);
        if (tstr.isEmpty() || Double.parseDouble(tstr) < 0.01)
          tstr = "0.01";
        text = "    RomeFileSetAttrValue(profile, 'TOPO_STEEP', '" + tstr + "', " + i + ")\n";
        fos.write(text.getBytes());
      }
    }

    // Presently we do not support multiple soils
    fos.write("    # SET SOIL LAYER\n".getBytes());
    if (aSoils.length() > 0) {
      text = "    RomeFileSetAttrSize(profile, 'SOIL_LAYER', " + aSoils.length() + ")\n";
      fos.write(text.getBytes());
      text = "    RomeFileSetAttrValue(profile, 'SOIL_HORIZ', '" + length + "', 0)\n";
      fos.write(text.getBytes());
      text = "    RomeFileSetAttrValue(profile, 'SOIL_PTR', '" + StringEscapeUtils.escapeJava(soilPtr[0]) + "', " + 0 + ")\n";
      fos.write(text.getBytes());
    }

    // Must be after set soils or the soils code resets it...sometimes
    if (JSONUtils.checkKeyExistsB(param, KEY_SIMPLE_ROCK_COVER)) {
      text = "    RomeFileSetAttrValue(profile, 'SIMPLE_ROCK_COVER', '" + simpleRockCoverPercent + "' ,0)\n";
      fos.write(text.getBytes());
    }

    fos.write("    # SET MAN LAYER\n".getBytes());
    if (aManIdx.length() > 0) {
      text = "    RomeFileSetAttrSize(profile, 'MAN_LAYER', " + aManIdx.length() + ")\n";
      fos.write(text.getBytes());
      for (int i = 0; i < aManIdx.length(); i++) {
        text = "    RomeFileSetAttrValue(profile, 'MAN_HORIZ', '" + aManLength.getString(i) + "', " + i + ")\n";
        fos.write(text.getBytes());
        LOG.info("********************--- raw Value is=" + managementFormalName[aManIdx.getInt(i)]);
        //text = "    RomeFileSetAttrValue(profile, 'MAN_PTR', '" + managementFormalName[aManIdx.getInt(i)] + "', " + i + ")\n";
        text = "    RomeFileSetAttrValue(profile, 'MAN_PTR', '" + R2Run.escapeJavaNoFwdSlash(managementFormalName[aManIdx.getInt(i)]) + "', " + i + ")\n";
        fos.write(text.getBytes());
      }
    } else {
      // IF no max_idx array provided, defaulting to single mgmt
      text = "    RomeFileSetAttrSize(profile, 'MAN_LAYER', 1)\n";
      fos.write(text.getBytes());
      text = "    RomeFileSetAttrValue(profile, 'MAN_HORIZ', '" + length + "', 0)\n";
      fos.write(text.getBytes());
      LOG.info("********************--- raw Value is=" + managementFormalName[0]);
      text = "    RomeFileSetAttrValue(profile, 'MAN_PTR', '" + R2Run.escapeJavaNoFwdSlash(managementFormalName[0]) + "', 0)\n";
      fos.write(text.getBytes());
    }

    //fos.write("    # Set SOIL_PTR\n".getBytes());
    //text = "    RomeFileSetAttrValue(profile, 'SOIL_PTR', '" + StringEscapeUtils.escapeJava(soilPtr[0]) + ".xml',0)\n";
    //fos.write(text.getBytes());
    fos.write("    print('SOIL_PTR=%s' % RomeFileGetAttrValue(profile, 'SOIL_PTR', 0))\n".getBytes());

    if ((contourSystem != null) && (contourSystem.length() > 1)) {
      // Contour Systems can simply point to the nginx XML file - no need to prefetch because of no special
      // characters in the file name
      text = "    RomeFileSetAttrValue(profile, 'CONTOUR_SYSTEM_PTR', '" + StringEscapeUtils.escapeJava(contourSystem) + "', 0)\n";
      fos.write(text.getBytes());
    }

    // To prevent issues with pyrome converting special chars in filenames to invalid chars for web server retrieval,
    // we prefetch the file, insert a dummy name, and point to it here
    if ((stripBarrierSystem != null) && (stripBarrierSystem.length() > 1)) {

      fos.write("    stripbarr = RomeFilesOpen(files, '#XML:stripbarr_file0.xml',0)\n".getBytes());
      text = "    RomeFileSetAttrValue(profile, 'STRIP_BARRIER_SYSTEM_PTR', 'strip-barrier-systems\\\\aaa', 0)\n";
      fos.write(text.getBytes());
    }

    // Runs with a Hydraulic Element System, use a hydraulic element flow path
    if ((hydElemSystem != null) && (hydElemSystem.length() > 1)) {
      // Load the hyd elem flow path into the workspace
      fos.write("    hydelem = RomeFilesOpen(files, '#XML:hydelemflowpath_file.xml',0)\n".getBytes());
      int numFlowPaths = r2run.determineNumberOfFlowPaths(hydElemSystem);
      text = "    RomeFileSetAttrSize(profile, 'NUM_FLOW_PATHS', " + numFlowPaths + ")\n";
      fos.write(text.getBytes());
      double offsets[] = r2run.determineFlowPathDistribution(hydElemSystem);
      for (int i = 0; i < numFlowPaths; i++) {
        text = "    RomeFileSetAttrValue(profile, 'FLOW_PATH_HORIZ', '" + length * offsets[i] + "', " + i + ")\n";
        fos.write(text.getBytes());
        if (i < (numFlowPaths - 1)) {
          text = "    RomeFileSetAttrValue(profile, 'HYD_ELEM_FLOW_PATH_PTR', 'hydraulic-element-flow-paths\\hydelemflowpath1', " + i + ")\n";
          fos.write(text.getBytes());
        } else {
          // last flow path is always the default
          text = "    RomeFileSetAttrValue(profile, 'HYD_ELEM_FLOW_PATH_PTR', 'hydraulic-element-flow-paths\\default', " + i + ")\n";
          fos.write(text.getBytes());
        }
      }
    }

    fos.write("    RomeFileSave(profile)\n".getBytes());
    fos.write("    RomeEngineRun(engine)\n".getBytes());

    // Request specific outputs from Rusle2
    for (String r : reqResults) {
      text = "    paramsize = RomeFileGetAttrSize(profile,'" + r + "')\n";
      fos.write(text.getBytes());
      text = "    if paramsize > 1:\n";
      fos.write(text.getBytes());
      text = "        text = '['\n";
      fos.write(text.getBytes());
      text = "        for x in range (paramsize):\n";
      fos.write(text.getBytes());
      text = "            text += RomeFileGetAttrValue(profile,'" + r + "',x)\n";
      fos.write(text.getBytes());
      text = "            if x < (paramsize-1):\n";
      fos.write(text.getBytes());
      text = "                text += ', '\n";
      fos.write(text.getBytes());
      text = "        text += ']'\n";
      fos.write(text.getBytes());
      text = "    else:\n";
      fos.write(text.getBytes());
      text = "        text = RomeFileGetAttrValue(profile, '" + r + "', 0)\n";
      fos.write(text.getBytes());
      text = "    results.append(RomeFileGetAttrValue(profile, '" + r + "', 0))\n";
      fos.write(text.getBytes());
      text = "    print('" + r + "=%s' % text)\n";
      fos.write(text.getBytes());
    }

    // Request output for segments (seg 0 for non segmented runs)
    fos.write("    numflowpaths = RomeFileGetAttrSize(profile, 'NUM_FLOW_PATHS')\n".getBytes());
    fos.write("    print('------------------------------------------------------------------------------')\n".getBytes());
    fos.write("    print('NUMBER OF FLOWPATHS=%d' % numflowpaths)\n".getBytes());
    fos.write("    for xx in range (numflowpaths):\n".getBytes());
    fos.write("        print('FLOW_PATH_HORIZ:%d=%s' % (xx, RomeFileGetAttrValue(profile, 'FLOW_PATH_HORIZ', xx)))\n".getBytes());
    fos.write("        print('HYD_ELEM_FLOW_PATH_PTR:%d=%s' % (xx, RomeFileGetAttrValue(profile, 'HYD_ELEM_FLOW_PATH_PTR', xx)))\n".getBytes());
    fos.write("    numsegs = RomeFileGetAttrSize(profile, 'SEGMENT')\n".getBytes());
    fos.write("    print('NUMBER OF SEGMENTS=%d' % numsegs)\n".getBytes());
    fos.write("    for x in range (numsegs):\n".getBytes());
    fos.write("        print('------------------------------------------------------------------------------')\n".getBytes());
    fos.write("        print('SEGMENT:%d=%s' % (x, RomeFileGetAttrValue(profile, 'SEGMENT', x)))\n".getBytes());
    fos.write("        print('SEG_HORIZ:%d=%s' % (x, RomeFileGetAttrValue(profile, 'SEG_HORIZ', x)))\n".getBytes());
    fos.write("        print('SEG_STEEP:%d=%s' % (x,RomeFileGetAttrValue(profile, 'SEG_STEEP', x)))\n".getBytes());
    fos.write("        print('SEG_MAN:%d=%s' % (x, RomeFileGetAttrValue(profile, 'MAN_PTR', x)))\n".getBytes());
    fos.write("        print('SEG_SOIL:%d=%s' % (x, RomeFileGetAttrValue(profile, 'SOIL_PTR', x)))\n".getBytes());
    fos.write("        results.append(RomeFileGetAttrValue(profile, 'SEG_SOIL_LOSS', x))\n".getBytes());
    fos.write("        print('SEG_SOIL_LOSS:%d=%s' % (x, RomeFileGetAttrValue(profile, 'SEG_SOIL_LOSS', x)))\n".getBytes());
    fos.write("        print('SEG_SOIL_LAYER %d=%s' % (x, RomeFileGetAttrValue(profile, 'SEG_SOIL_LAYER', x)))\n".getBytes());
    fos.write("        print('SEG_MAN_LAYER %d=%s' % (x, RomeFileGetAttrValue(profile, 'SEG_MAN_LAYER', x)))\n".getBytes());
    fos.write("        print('HYD_ELEM_SYS:%d=%s' % (x, RomeFileGetAttrValue(profile, 'HYD_ELEM_SYSTEM_PTR', x)))\n".getBytes());
    fos.write("        print('STRIP_BARRIER_SYS:%d=%s' % (x, RomeFileGetAttrValue(profile, 'STRIP_BARRIER_SYSTEM_PTR', x)))\n".getBytes());
    fos.write("        print('CONTOUR_SYS:%d=%s' % (x, RomeFileGetAttrValue(profile, 'CONTOUR_SYSTEM_PTR', x)))\n".getBytes());
    fos.write("    print('------------------------------------------------------------------------------')\n".getBytes());

    fos.write("    numsegs = RomeFileGetAttrSize(profile, 'SOIL_LAYER')\n".getBytes());
    fos.write("    print('NUMBER OF SOIL SEGMENTS=%d' % numsegs)\n".getBytes());
    fos.write("    for x in range (numsegs):\n".getBytes());
    fos.write("        print('------------------------------------------------------------------------------')\n".getBytes());
    fos.write("        print('SOIL_SEGMENT:%d=%s' % (x, RomeFileGetAttrValue(profile, 'SOIL_LAYER', x)))\n".getBytes());
    fos.write("        print('SOIL_HORIZ:%d=%s' % (x, RomeFileGetAttrValue(profile, 'SOIL_HORIZ', x)))\n".getBytes());
    fos.write("        print('SOIL_PTR:%d=%s' % (x, RomeFileGetAttrValue(profile, 'SOIL_PTR', x)))\n".getBytes());
    fos.write("    print('------------------------------------------------------------------------------')\n".getBytes());

    fos.write("    numsegs = RomeFileGetAttrSize(profile, 'MAN_LAYER')\n".getBytes());
    fos.write("    print('NUMBER OF MAN SEGMENTS=%d' % numsegs)\n".getBytes());
    fos.write("    for x in range (numsegs):\n".getBytes());
    fos.write("        print('------------------------------------------------------------------------------')\n".getBytes());
    fos.write("        print('MAN_SEGMENT:%d=%s' % (x, RomeFileGetAttrValue(profile, 'MAN_LAYER', x)))\n".getBytes());
    fos.write("        print('MAN_HORIZ:%d=%s' % (x, RomeFileGetAttrValue(profile, 'MAN_HORIZ', x)))\n".getBytes());
    fos.write("        print('MAN_PTR:%d=%s' % (x, RomeFileGetAttrValue(profile, 'MAN_PTR', x)))\n".getBytes());
    fos.write("    print('------------------------------------------------------------------------------')\n".getBytes());

    fos.write("    numsegs = RomeFileGetAttrSize(profile, 'TOPO_LAYER')\n".getBytes());
    fos.write("    print('NUMBER OF TOPO SEGMENTS=%d' % numsegs)\n".getBytes());
    fos.write("    for x in range (numsegs):\n".getBytes());
    fos.write("        print('------------------------------------------------------------------------------')\n".getBytes());
    fos.write("        print('TOPO_SEGMENT:%d=%s' % (x, RomeFileGetAttrValue(profile, 'TOPO_LAYER', x)))\n".getBytes());
    fos.write("        print('TOPO_HORIZ:%d=%s' % (x, RomeFileGetAttrValue(profile, 'TOPO_HORIZ', x)))\n".getBytes());
    fos.write("        print('TOPO_HORIZ_COMPOSITE:%d=%s' % (x, RomeFileGetAttrValue(profile, 'TOPO_HORIZ_COMPOSITE', x)))\n".getBytes());
    fos.write("        print('TOPO_STEEP:%d=%s' % (x, RomeFileGetAttrValue(profile, 'TOPO_STEEP', x)))\n".getBytes());
    fos.write("        print('TOPO_STEEP_COMPOSITE:%d=%s' % (x, RomeFileGetAttrValue(profile, 'TOPO_STEEP_COMPOSITE', x)))\n".getBytes());
    fos.write("    print('------------------------------------------------------------------------------')\n".getBytes());

    text = "    exec(open(\"" + REPORT_PY_FILENAME + "\").read())\n";
    fos.write(text.getBytes());
    fos.write("    RomeFileClose(profile)\n".getBytes());
    fos.write("    return results\n".getBytes());

    fos.write("if __name__ == \"__main__\":\n".getBytes());
    fos.write("    #inputs\n".getBytes());
    fos.write("    save = False\n".getBytes());
    fos.write("    #Run simulation\n".getBytes());
    fos.write(("    romeDLL = RomeInit('pyrome /DirRoot=" + resources().getFile(ROMEDLL).getParent().replace('\\', '/') + "/')\n").getBytes());
    fos.write("    database = RomeGetDatabase(romeDLL)\n".getBytes());

    // Specify the nginx server
    String r2db = Config.getString("r2.db", "http://oms-db.engr.colostate.edu/r2");
    if (altR2db == null) {
      text = "    RomeDatabaseOpen(database,'" + r2db + "')\n";
      fos.write(text.getBytes());
    } else {
      String altr2db = r2db.substring(0, r2db.lastIndexOf("/")) + "/model-data/" + altR2db;
      text = "    RomeDatabaseOpen(database,'" + altr2db + "')\n";
      fos.write(text.getBytes());
    }
    fos.write("    results = linearTest(save)\n".getBytes());
    fos.write("    RomeDatabaseClose(database)\n".getBytes());
    fos.write("    RomeExit(romeDLL)\n".getBytes());

    // prepare individual contour system, strip barrier system, hyd elem sys, if not provided in an array
    if (JSONUtils.checkKeyExistsB(param, KEY_CONTOUR_SYSTEM_PTR)) {
      String contourHttpPtr = r2db + "/" + contourSystem.replace("\\", "/") + ".xml";
      r2run.prepareFileJ(contourHttpPtr, new File(workspace().getDir(), "contour_file0.xml"), "", "contour-systems", "", true);
    }
    if (JSONUtils.checkKeyExistsB(param, KEY_STRIP_BARRIER_SYSTEM_PTR)) {
      String stripbarrHttpPtr = r2db + "/" + stripBarrierSystem.replace("\\", "/") + ".xml";
      r2run.prepareFileJ(stripbarrHttpPtr, new File(workspace().getDir(), "stripbarr_file0.xml"), "", "strip-barrier-systems", "", true);
    }
    if (JSONUtils.checkKeyExistsB(param, KEY_HYD_ELEM_SYSTEM_PTR)) {
      String hydelemHttpPtr = r2db + "/" + hydElemSystem.replace("\\", "/") + ".xml";
      r2run.prepareHydraulicElementFlowPathJ(hydelemHttpPtr, r2db, new File(workspace().getDir(), "hydelemflowpath_file.xml"));
    }

    r2run.prepareFileJ(climatePtr, new File(workspace().getDir(), "cli_file0.xml"), "CLIMATE", "climates", "", true);

    // to do
    // implement rusle 2 report in pyrome
    // Rusle2 report
    //fos.write("READ \"rusle2_report.rsh\"\n".getBytes());
    fos.close();

    // Unpack the report rsh file in the work space dir
    FileUtils.copyFileToDirectory(resources().getFile(REPORT_PY_FILENAME), workspace().getDir());

    climate = climatePtr;
    soil = soilPtr[0];
    mgmt = managementFormalName[0];

    LOG.info("R2 script: " + file.toString());
  }


  private String[] getSoilFiles(JSONArray cokeys) throws JSONException, Exception {
    String soilPtr[] = new String[(cokeys.length())];
    JSONObject soilsRequest = new JSONObject();
    JSONObject meta = new JSONObject();
    JSONArray paramObj = new JSONArray();
    JSONObject cokeyObj = new JSONObject();
    String cokey;
    JSONObject responseMeta;
    JSONArray resultArr;
    JSONObject soilObj;
    String soilFilePath;

    soilsRequest.put(KEY_METAINFO, meta);
    soilsRequest.put(KEY_PARAMETER, paramObj);
    paramObj.put(cokeyObj);

    for (int i = 0; i < cokeys.length(); i++) {
      cokey = cokeys.getString(i);
      paramObj.put(0, cokeyObj);
      cokeyObj.put(KEY_NAME, KEY_COKEY);
      cokeyObj.put(KEY_VALUE, cokey);
      LOG.info("SOILS Request: " + soilsRequest.toString());
      JSONObject soilsResponse = new Client().doPOST(Config.getString("r2.soils", "http://csip.engr.colostate.edu:8092/csip-soils/d/soilsXML/1.0"), soilsRequest);
      LOG.info("SOILS Response: " + soilsResponse.toString());
      responseMeta = soilsResponse.getJSONObject(KEY_METAINFO);
      if (!responseMeta.getString(KEY_STATUS).equalsIgnoreCase("Failed")) {
        resultArr = soilsResponse.getJSONArray(KEY_RESULT);
        soilObj = resultArr.getJSONObject(0);
        soilFilePath = soilObj.getString(KEY_VALUE);

        new Client().doGET(soilFilePath, workspace().getFile("soils_file" + i + ".xml"));

        soilPtr[i] = getSoilFilePath(workspace().getFile("soils_file" + i + ".xml")).replace("/", "\\");
        LOG.info("THE SOIL IS =" + soilPtr[i] + "\n\n\n\n");

      } else {
        String error = responseMeta.getString(ERROR);
        throw new ServiceException("Soil service error: " + error);
      }
    }
    return soilPtr;
  }


  private String getSoilFilePath(File soilFile) throws ParserConfigurationException, SAXException, IOException {
    String path = "";
    DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
    DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
    Document document = documentBuilder.parse(soilFile);
    path = document.getElementsByTagName("Filename").item(0).getTextContent();
    return path;
  }


  private String getClimateFilePath(double lat, double lon) throws JSONException, Exception {
    JSONObject climateReq = new JSONObject();
    JSONObject meta = new JSONObject();
    JSONArray param = new JSONArray();
    JSONObject jlat = new JSONObject();
    JSONObject jlong = new JSONObject();
    JSONArray resultArr;
    JSONObject responseMeta;
    JSONObject cliObj;
    String climatePath = "";

    climateReq.put(KEY_METAINFO, meta);
    climateReq.put(KEY_PARAMETER, param);

    param.put(jlat);
    param.put(jlong);

    jlat.put(KEY_NAME, KEY_LATITUDE);
    jlat.put(KEY_VALUE, lat);

    jlong.put(KEY_NAME, KEY_LONGITUDE);
    jlong.put(KEY_VALUE, lon);

    LOG.info("CLIMATE Request: " + climateReq.toString());
    JSONObject climateResponse = new Client().doPOST(Config.getString("r2.climate", "http://csip.engr.colostate.edu:8092/csip-misc/d/r2climate/2.0"), climateReq);
    LOG.info("CLIMATE Response: " + climateResponse.toString());
    responseMeta = climateResponse.getJSONObject(KEY_METAINFO);
    if (!responseMeta.getString(KEY_STATUS).equalsIgnoreCase("Failed")) {
      resultArr = climateResponse.getJSONArray(KEY_RESULT);
      cliObj = JSONUtils.preprocess(resultArr).get(KEY_CLIMATE_URL);
      climatePath = cliObj.getString(KEY_VALUE);
    }
    return climatePath;
  }


  private String getFilenumber(String filename) {
    return filename.substring(R2_TMP_FILENAME.length(), filename.length() - R2_TMP_FILEEXT.length());
  }


  @Override
  protected void doReport() throws Exception {
//        String sessionWorkDir = workspace().getDir().toString();
//        String reportJSON = Binaries.unpackResourceAbsolute("/bin/win-x86/" + REPORT_JSON_TEMPLATE_FILENAME, sessionWorkDir + "/" + REPORT_JSON_TEMPLATE_FILENAME).toString();
//        File reportTemplate = new File(sessionWorkDir + "/" + REPORT_JSON_TEMPLATE_FILENAME);
    File reportTemplate = resources().getFile(REPORT_JSON_TEMPLATE_FILENAME);

    String sReportJSON = FileUtils.readFileToString(reportTemplate);
    JSONArray reportItemsFromTemplate = new JSONArray(sReportJSON);

    for (int i = 0; i < reportItemsFromTemplate.length(); i++) {
      JSONObject obj = (JSONObject) reportItemsFromTemplate.get(i);
      String itemName = obj.getString(REPORT_NAME);
      String dimension = JSONUtils.getJSONString(obj, REPORT_DIM, null);
      String type = JSONUtils.getJSONString(obj, REPORT_TYPE, "");
      String units = JSONUtils.getJSONString(obj, REPORT_UNITS, null);
      String desc = JSONUtils.getJSONString(obj, REPORT_DESC, null);
      //LOG.info("Attempting to fetch results for:" + itemName);
      System.out.println("------------------------------------------------------------------------PROCESSING ELEMENT:");
      System.out.println("VAR NAME:" + itemName);
      System.out.println("-------------------------------------------------------------------------------------------");

      //String value = r2run.getResultPyrome(itemName);
      String value = r2run.getResult("\"" + itemName + "\"");
      LOG.info("the value='" + value + "'");
      if ((value != null) && (value.equals("null"))) {
        report().put(itemName, (String) null, desc, units);
        // if there is no value, then item should be removed from the array
      } else {
        if ((dimension != null) && (dimension.length() > 0) && (value != null) && (!value.equals("null"))) {
          try {
            report().put(itemName, new JSONArray(value), desc, units);
          } catch (JSONException je) {
            processReportElement(itemName, dimension, units, desc, type, value);
          }
        } else {
          processReportElement(itemName, dimension, units, desc, type, value);
        }

      }
      if (dimension != null) {
        report().putMetaInfo(itemName, REPORT_DIM, new JSONArray(dimension));
      }
    }
  }


  private void prepareHydraulicFlowElementFile() {
    // Given a Hydraulic Element Pointer, parse the XML and return a Hydraulic Flow Element
  }

  // This is a gross signiture but due to csip report changes this is the easiest way to change it without losing Wes's logic.

  private void processReportElement(String itemName, String dim, String units, String desc, String type, String value) throws Exception {
    System.out.println("------------------------------------------------------------------------PROCESSING ELEMENT:\n\n");
    System.out.println("TYPE:" + type);
    System.out.println("VALUE:" + value);
    System.out.println("-------------------------------------------------------------------------------------------\n\n");

    if ((type.equals("TEXT")) || (type.equals("FILENAME")) || (type.equals("DATE"))) {
      // because the RomeShell returns escaped strings, and the JSONObject in Java re-escapes them
      // we have to remove one level of "escaping" !!!

      String newString = StringEscapeUtils.unescapeJava(value);
      //String newString=value;
      report().put(itemName, removeFirstLastChar(newString), desc, units);
    }
    if (type.equals("INTEGER") && (value != null) && (!value.equals("null")) && (!value.equals("\"\""))) {
      // trim off quotes
      value = removeFirstLastChar(value);
      System.out.println("VALUE:" + value);
      report().put(itemName, new Double(value).intValue(), desc, units);
    }
    if (type.equals("REAL") && (value != null)) {
      if ((!value.equals("null")) && (!value.equals("\"\""))) {
        // trim off quotes
        value = removeFirstLastChar(value);
        System.out.println("VALUE:" + value);
        report().put(itemName, Double.parseDouble(value), desc, units);
      }
      if (value.equals("\"\"")) {
        report().put(itemName, new Double(0), desc, units);
      }
    }
    if (type.equals("")) {
      report().put(itemName, value, desc, units);
    }
  }


  private boolean allEqual(int... vals) {
    if (vals.length < 2) {
      return true;
    }
    int a = vals[0];
    for (int i = 0; i < vals.length; i++) {
      if (vals[i] != a) {
        return false;
      }
    }
    return true;
  }
}