V1_0.java [src/java/org/rti/rivertrak] Revision: default  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 org.rti.rivertrak;

import csip.Config;
import csip.api.server.Executable;
import csip.ModelDataService;
import csip.api.server.ServiceException;
import csip.annotations.Category;
import csip.annotations.Description;
import csip.annotations.Documentation;
import csip.annotations.Name;
import csip.annotations.Resource;
import static csip.annotations.ResourceType.*;
import csip.annotations.State;
import csip.annotations.VersionInfo;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.logging.Level;
import javax.ws.rs.Path;
import org.apache.commons.io.FileUtils;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
import org.codehaus.jettison.json.JSONObject;
import static org.rti.rivertrak.V1_0.*;

/**
 * Rivertrak model.
 *
 * @author Olaf David, Michael Thieman
 */
@Name("Rivertrak")
@Description("Dynamic Flood Inundation, real-time floodplain mapping system.")
@Documentation("https://www.rivertrak.com/system")
@State(State.STABLE)
@VersionInfo("$Id$")
@Category("Hydrology")
@Category("Flood forecasting")

@Path("m/rivertrak/3.2")
@Resource(file = "/bin/OpTableAppLight.exe", id = RIVERTRAK, wine = true, type = EXECUTABLE)
@Resource(file = "/bin/OpTableAppLight_Mar2019.exe", id = RIVERTRAK_MAR19, wine = true, type = EXECUTABLE)
@Resource(file = "/config/RiverTrak_Dimensions.csv", id = CONFIG_DIM)
@Resource(file = "/config/RiverTrak_Units.csv", id = CONFIG_UNITS)
@Resource(file = "/config/RiverTrak_DataTypes.csv", id = CONFIG_DATATYPES)
@Resource(file = "/config/RiverTrak_Properties.csv", id = CONFIG_PROPERTIES)
@Resource(file = "OpTableAppLight.log TimeSeries/* ", type = OUTPUT)
public class V1_0 extends ModelDataService {

  static VelocityEngine velocity = new VelocityEngine();

  public static final String RIVERTRAK = "rivertrak";
  public static final String RIVERTRAK_MAR19 = "rivertrak_Mar19";

  static final String RIVERTRAK_CFG = "rivertrak.cfg";
  static final String CONFIG_DIM = "dim";
  static final String CONFIG_UNITS = "units";
  static final String CONFIG_DATATYPES = "datatypes";
  static final String CONFIG_PROPERTIES = "properties";

  static final String RIVERTRAK_CONF_VM = "org/rti/rivertrak/config.vm";

  static final String FORMKEY_TSINPUT = "ts_input";
  static final String METAKEY_MODELNAME = "model_name";
  static final String PARAMKEY_OPTABLE = "OpTable";
  static final String PARAMKEY_RUNSTARTDATE = "RunStartDate";
  static final String PARAMKEY_RUNENDDATE = "RunEndDate";
  static final String PARAMKEY_STATESWRITEMODE = "StatesWriteMode";

  // config properties
  static final String DRIVE = Config.getString("win.drive", "Z:");

  // payload
  int enableDebug;
  int debugLevel;
  int statusLevel;
  int warningLevel;
  int refreshCount;
  String opTable = "Models.optable";

  private static class JSONKeys implements Iterable<String> {

    JSONObject json;


    JSONKeys(JSONObject json) {
      this.json = json;
    }


    @Override
    public Iterator<String> iterator() {
      return json.keys();
    }

  }


  @Override
  protected void doProcess() throws Exception {

    parameter().require(PARAMKEY_OPTABLE)
        .require(PARAMKEY_RUNSTARTDATE)
        .require(PARAMKEY_RUNENDDATE)
        .require(PARAMKEY_STATESWRITEMODE);
//    formdata().require(FORMKEY_TSINPUT);

    // config settings with defaults
    enableDebug = parameter().getInt("EnableDebug", 0);
    debugLevel = parameter().getInt("DebugLevel", 1);
    statusLevel = parameter().getInt("StatusLevel", 1);
    warningLevel = parameter().getInt("WarningLevel", 1);
    refreshCount = parameter().getInt("RefreshCount", 10);

    workspace().getFile("States").mkdirs();

    if (!workspace().getFile("TimeSeries").exists()) {
      throw new ServiceException("No Time series data.");
    }

    File modelFolder = workspace().getFile("Models");
    if (!modelFolder.exists()) {
      modelFolder.mkdirs();
    }

    try {
      // assume an embedded JSONObject for optable
      JSONObject j = parameter().getJSON(PARAMKEY_OPTABLE);
      for (String modelName : new JSONKeys(j)) {
        // append to opTable
        FileUtils.write(new File(modelFolder, opTable), modelName + "\n", true);
        try (PrintWriter w = new PrintWriter(new File(modelFolder, modelName))) {
          JSONObject modelDefs = j.getJSONObject(modelName);
          for (String section : new JSONKeys(modelDefs)) {
            w.println("#" + section);
            JSONObject kv = modelDefs.getJSONObject(section);
            for (String key : new JSONKeys(kv)) {
              w.println(key + "=" + kv.get(key).toString());
            }
          }
        }
      }
    } catch (ServiceException E) {
      // carry on
      opTable = parameter().getString(PARAMKEY_OPTABLE);
    }

    File opTableFile = new File(modelFolder, opTable);
    if (!opTableFile.exists()) {
      throw new ServiceException("OpTable file not found: " + opTable);
    }

    String modelName = RIVERTRAK;
    if (metainfo().hasName(METAKEY_MODELNAME)) {
      modelName = metainfo().getString(METAKEY_MODELNAME);
    }

    Executable rt = resources().getExe(modelName);
    rt.addArguments("-Config", createConfig(),
        "-RunStartDate", parameter().getString(PARAMKEY_RUNSTARTDATE),
        "-RunEndDate", parameter().getString(PARAMKEY_RUNENDDATE),
        "-StatesWriteMode", parameter().getString(PARAMKEY_STATESWRITEMODE, "WRITE")
    );

    int ret = rt.exec();
    if (ret != 0) {
      throw new ServiceException("Error running Rivertrak: " + ret);
    }
  }


  /// public properties for Velocity access
  public String getWorkspace() {
    return toWin(workspace().getDir());
  }


  public String getDrive() {
    return DRIVE;
  }


  public int getEnableDebug() {
    return enableDebug;
  }


  public int getDebugLevel() {
    return debugLevel;
  }


  public String getOpTable() {
    return opTable;
  }


  public int getStatusLevel() {
    return statusLevel;
  }


  public int getWarningLevel() {
    return warningLevel;
  }


  public int getRefreshCount() {
    return refreshCount;
  }


  public String getConfigDimensions() throws ServiceException {
    return toWin(resources().getFile(CONFIG_DIM));
  }


  public String getConfigUnits() throws ServiceException {
    return toWin(resources().getFile(CONFIG_UNITS));
  }


  public String getConfigDatatypes() throws ServiceException {
    return toWin(resources().getFile(CONFIG_DATATYPES));
  }


  public String getConfigProperties() throws ServiceException {
    return toWin(resources().getFile(CONFIG_PROPERTIES));
  }


  private static String toWin(File f) {
    return f.toString().replace('/', '\\');
  }


  private String createConfig() throws IOException {
    VelocityContext context = new VelocityContext();
    context.put("service", this);

    FileWriter w = new FileWriter(workspace().getFile(RIVERTRAK_CFG));
    velocity.getTemplate(RIVERTRAK_CONF_VM, "utf-8").merge(context, w);
    w.close();

    if (LOG.isLoggable(Level.INFO)) {
      LOG.info("Created: " + RIVERTRAK_CFG);
      LOG.info("\n" + FileUtils.readFileToString(workspace().getFile(RIVERTRAK_CFG)));
    }
    return RIVERTRAK_CFG;
  }


  public static void onContextInit() {
    velocity.setProperty("file.resource.loader.class",
        ClasspathResourceLoader.class.getName());
    velocity.init();
  }
}