/**
   * Read all PeMS data in the given time range and having a VDS ID in the given list.
   *
   * @see #read() if you want a transaction and logging around the operation.
   */
  public PeMSSet readSet(Interval interval, List<Long> vdsIds) throws DatabaseException {
    PeMSSet set = new PeMSSet();
    List<PeMSMap> mapList = set.getPemsMapList();

    PeMS pems;
    String query = null;

    try {
      query = runQuerySet(interval, vdsIds);
      org.joda.time.DateTime prevTime = null;
      PeMSMap map = null;

      while (null != (pems = pemsFromQueryRS(query))) {
        org.joda.time.DateTime curTime = pems.getJodaTimeMeasured();

        if (map == null || !prevTime.equals(curTime)) {
          map = new PeMSMap();
          mapList.add(map);
          prevTime = curTime;
        }

        map.getMap().put(pems.getVdsId().toString(), pems);
      }
    } finally {
      if (query != null) {
        dbr.psDestroy(query);
      }
    }

    return set;
  }
  /**
   * Read all PeMS aggregate data in the given time range, having a VDS ID in the given list, and at
   * the given aggregation level. List is sorted by time.
   */
  public List<PeMSStationAggregate> read(
      Interval interval, List<Long> vdsIds, PeMSAggregate.AggregationLevel level)
      throws DatabaseException {

    List<PeMSStationAggregate> list;

    String pemsIdStr =
        "pems.{vds_id=["
            + vdsIds.get(0)
            + ","
            + vdsIds.get(1)
            + ",...], interval="
            + interval
            + ", level="
            + level
            + "}";

    long timeBegin = System.nanoTime();

    try {
      dbr.transactionBegin();
      Monitor.debug("PeMS aggregate reader transaction beginning on " + pemsIdStr);

      list = readList(interval, vdsIds, level);

      dbr.transactionCommit();
      Monitor.debug("PeMS aggregate reader transaction committing on " + pemsIdStr);
    } catch (DatabaseException dbExc) {
      Monitor.err(dbExc);
      throw dbExc;
    } finally {
      try {
        dbr.transactionRollback();
        Monitor.debug("PeMS aggregate reader transaction rollback on " + pemsIdStr);
      } catch (Exception Exc) {
        // Do nothing.
      }
    }

    long timeCommit = System.nanoTime();
    if (list != null) {
      Monitor.duration("Read " + pemsIdStr, timeCommit - timeBegin);
    }

    return list;
  }
  /** Read all PeMS data in the given time range and having a VDS ID in the given list. */
  public PeMSSet read(Interval interval, List<Long> vdsIds) throws DatabaseException {
    PeMSSet set = null;

    String pemsIdStr =
        "pems.{vds_id=["
            + vdsIds.get(0)
            + ","
            + vdsIds.get(1)
            + ",...], interval="
            + interval
            + "}";

    long timeBegin = System.nanoTime();

    try {
      dbr.transactionBegin();
      Monitor.debug("PeMS reader transaction beginning on " + pemsIdStr);

      set = readSet(interval, vdsIds);

      dbr.transactionCommit();
      Monitor.debug("PeMS reader transaction committing on " + pemsIdStr);
    } catch (DatabaseException dbExc) {
      Monitor.err(dbExc);
      throw dbExc;
    } finally {
      try {
        dbr.transactionRollback();
        Monitor.debug("PeMS reader transaction rollback on " + pemsIdStr);
      } catch (Exception Exc) {
        // Do nothing.
      }
    }

    long timeCommit = System.nanoTime();
    if (set != null) {
      Monitor.duration("Read " + pemsIdStr, timeCommit - timeBegin);
    }

    return set;
  }
  /**
   * Read all PeMS aggregate data in the given time range, having a VDS ID in the given list, and at
   * the given aggregation level. List is sorted by time.
   *
   * @see #read() if you want a transaction and logging around the operation.
   */
  public List<PeMSStationAggregate> readList(
      Interval interval, List<Long> vdsIds, PeMSAggregate.AggregationLevel level)
      throws DatabaseException {

    List<PeMSStationAggregate> list = new ArrayList<PeMSStationAggregate>();
    PeMSStationAggregate sagg;

    String query = null;

    try {
      query = runQueryAggregates(interval, vdsIds, level);
      while (null != (sagg = aggregateFromQueryRS(query, level))) {
        list.add(sagg);
      }
    } finally {
      if (query != null) {
        dbr.psDestroy(query);
      }
    }

    return list;
  }
  /**
   * Instantiate and populate a pems aggregate object from the next item in the result set of a pems
   * aggregate query.
   *
   * @param query string
   * @return PeMSStationAggregate
   */
  protected PeMSStationAggregate aggregateFromQueryRS(
      String query, PeMSAggregate.AggregationLevel level) throws DatabaseException {

    PeMSStationAggregate sagg = null;

    if (dbr.psRSNext(query)) {
      // String columns = org.apache.commons.lang.StringUtils.join(dbr.psRSColumnNames(query), ",
      // ");
      // System.out.println("columns: [" + columns + "]");

      sagg = new PeMSStationAggregate();

      Long vdsId = dbr.psRSGetBigInt(query, "VDS_ID");
      edu.berkeley.path.model_elements.DateTime timeMeasured =
          new edu.berkeley.path.model_elements.DateTime(
              dbr.psRSGetTimestampMilliseconds(query, "MEASURE_DT"));

      sagg.setVdsId(vdsId);
      sagg.setTimeMeasured(timeMeasured);

      PeMSAggregate total = new PeMSAggregate();

      total.setSamples(dbr.psRSGetBigInt(query, "TOTAL_SAMPLES"));
      total.setObserved(dbr.psRSGetDouble(query, "PERCENT_OBSERVED"));
      total.setFlow(dbr.psRSGetDouble(query, "TOTAL_FLOW"));

      if (level == PeMSAggregate.AggregationLevel.PEMS_5MIN
          || level == PeMSAggregate.AggregationLevel.PEMS_1HOUR) {
        total.setAvgOccupancy(dbr.psRSGetDouble(query, "AVG_OCC"));
        total.setAvgSpeed(dbr.psRSGetDouble(query, "AVG_SPEED"));
      }

      sagg.setTotal(total);

      if (level == PeMSAggregate.AggregationLevel.PEMS_1HOUR
          || level == PeMSAggregate.AggregationLevel.PEMS_1DAY) {

        Map<CharSequence, Double> delay = new HashMap<CharSequence, Double>();

        for (Integer mph = 35; mph <= 60; mph += 5) {
          delay.put(mph.toString(), dbr.psRSGetDouble(query, "DELAY_VT_" + mph));
        }

        sagg.setDelay(delay);
      }

      if (level == PeMSAggregate.AggregationLevel.PEMS_5MIN
          || level == PeMSAggregate.AggregationLevel.PEMS_1HOUR) {

        List<PeMSAggregate> byLane = null;

        byLane = new ArrayList<PeMSAggregate>();
        byLane.add(null);

        for (int lane = 1; lane <= 8; lane++) {
          PeMSAggregate agg = new PeMSAggregate();

          String prefix = "LANE_" + lane + "_";

          agg.setFlow(dbr.psRSGetDouble(query, prefix + "FLOW"));
          agg.setAvgOccupancy(dbr.psRSGetDouble(query, prefix + "AVG_OCC"));
          agg.setAvgSpeed(dbr.psRSGetDouble(query, prefix + "AVG_SPEED"));

          if (level == PeMSAggregate.AggregationLevel.PEMS_5MIN) {
            agg.setSamples(dbr.psRSGetBigInt(query, prefix + "SAMPLES"));
            String obs = dbr.psRSGetVarChar(query, prefix + "OBSERVED");
            if (obs.equals("1")) {
              agg.setObserved(100.0);
            } else if (obs.equals("0")) {
              agg.setObserved(0.0);
            } else {
              System.out.println("Warning: unrecognized 'observed' value: '" + obs + "'.");
              // throw? log warning?
            }
          }

          byLane.add(agg);
        }

        sagg.setByLaneList(byLane);
      }
    }

    return sagg;
  }