/** * DepositoryValueServerResource.java This file is part of WattDepot. * * Copyright (C) 2013 Cam Moore * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.wattdepot.server.http.api; import java.text.ParseException; import java.util.Date; import java.util.logging.Level; import javax.xml.datatype.DatatypeConfigurationException; import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics; import org.restlet.data.Status; import org.restlet.resource.ResourceException; import org.wattdepot.common.domainmodel.Depository; import org.wattdepot.common.domainmodel.Labels; import org.wattdepot.common.domainmodel.InterpolatedValue; import org.wattdepot.common.domainmodel.Sensor; import org.wattdepot.common.domainmodel.SensorGroup; import org.wattdepot.common.exception.IdNotFoundException; import org.wattdepot.common.exception.MeasurementGapException; import org.wattdepot.common.exception.MisMatchedOwnerException; import org.wattdepot.common.exception.NoMeasurementException; import org.wattdepot.common.util.DateConvert; import org.wattdepot.server.ServerProperties; /** * DepositoryValueServerResource - ServerResouce that handles the GET * /wattdepot/{org-id}/depository/{depository-id}/value/ response. * * @author Cam Moore * */ public class DepositoryValueServer extends WattDepotServerResource { private String depositoryId; private String sensorId; private String start; private String end; private String timestamp; private String latest; private String earliest; private String gapSeconds; private static DescriptiveStatistics averageGetTime = new DescriptiveStatistics(); /* * (non-Javadoc) * * @see org.restlet.resource.Resource#doInit() */ @Override protected void doInit() throws ResourceException { super.doInit(); this.sensorId = getQuery().getValues(Labels.SENSOR); this.start = getQuery().getValues(Labels.START); this.end = getQuery().getValues(Labels.END); this.timestamp = getQuery().getValues(Labels.TIMESTAMP); this.latest = getQuery().getValues(Labels.LATEST); this.earliest = getQuery().getValues(Labels.EARLIEST); this.orgId = getAttribute(Labels.ORGANIZATION_ID); this.depositoryId = getAttribute(Labels.DEPOSITORY_ID); this.gapSeconds = getQuery().getValues(Labels.GAP); } /** * retrieve the depository value for a sensor. * * @return measured value. */ public InterpolatedValue doRetrieve() { getLogger().log( Level.INFO, "GET /wattdepot/{" + orgId + "}/depository/{" + depositoryId + "}/value/?sensor={" + sensorId + "}&start={" + start + "}&end={" + end + "}×tamp={" + timestamp + "}&latest={" + latest + "}&earliest={" + earliest + "}&gap={" + gapSeconds + "}"); Long startTime = null; if (depot.getServerProperties().get(ServerProperties.SERVER_TIMING_KEY) .equals(ServerProperties.TRUE)) { startTime = System.nanoTime(); } if (isInRole(orgId)) { try { Depository deposit = depot.getDepository(depositoryId, orgId, true); if (deposit != null) { try { depot.getSensor(sensorId, orgId, true); InterpolatedValue val = calculateValue(sensorId, start, end, timestamp, earliest, latest); if (depot.getServerProperties().get(ServerProperties.SERVER_TIMING_KEY) .equals(ServerProperties.TRUE)) { Long endTime = System.nanoTime(); Long diff = endTime - startTime; averageGetTime.addValue(diff / 1E9); getLogger().log( Level.SEVERE, "GET /wattdepot/{" + orgId + "}/depository/{" + depositoryId + "}/value/?sensor={" + sensorId + "}&start={" + start + "}&end={" + end + "}×tamp={" + timestamp + "}&latest={" + latest + "}&earliest={" + earliest + "}&gap={" + gapSeconds + "} took " + (diff / 1E9) + " seconds. Running average is " + averageGetTime.getMean()); } return val; } catch (IdNotFoundException inf) { try { SensorGroup group = depot.getSensorGroup(sensorId, orgId, true); InterpolatedValue val = null; // this wont work for earliest or latest. if (earliest != null) { // find the last 'earliest' time Date time = null; for (String s : group.getSensors()) { InterpolatedValue v = calculateValue(s, start, end, timestamp, earliest, latest); if (time == null) { time = v.getStart(); } else if (time.before(v.getStart())) { time = v.getStart(); } } // have the time get the value at time for (String s : group.getSensors()) { if (val == null) { val = calculateValue(s, null, null, DateConvert.convertDate(time).toString(), null, null); } else { val.setValue(val.getValue() + calculateValue(s, null, null, DateConvert.convertDate(time).toString(), null, null).getValue()); } } } else if (latest != null) { // find the first 'latest' time Date time = null; for (String s : group.getSensors()) { InterpolatedValue v = calculateValue(s, start, end, timestamp, earliest, latest); if (time == null) { time = v.getEnd(); } else if (time.after(v.getEnd())) { time = v.getEnd(); } } // have the time get the value at time for (String s : group.getSensors()) { if (val == null) { val = calculateValue(s, null, null, DateConvert.convertDate(time).toString(), null, null); } else { val.setValue(val.getValue() + calculateValue(s, null, null, DateConvert.convertDate(time).toString(), null, null).getValue()); } } } else { for (String s : group.getSensors()) { if (val == null) { val = calculateValue(s, start, end, timestamp, earliest, latest); } else { val.setValue(val.getValue() + calculateValue(s, start, end, timestamp, earliest, latest).getValue()); } } } if (depot.getServerProperties().get(ServerProperties.SERVER_TIMING_KEY) .equals(ServerProperties.TRUE)) { Long endTime = System.nanoTime(); getLogger().log( Level.SEVERE, "GET /wattdepot/{" + orgId + "}/depository/{" + depositoryId + "}/value/?sensor={" + sensorId + "}&start={" + start + "}&end={" + end + "}×tamp={" + timestamp + "}&latest={" + latest + "}&earliest={" + earliest + "}&gap={" + gapSeconds + "} took " + ((endTime - startTime) / 1E9) + " seconds."); } return val; } catch (IdNotFoundException inf1) { setStatus(Status.CLIENT_ERROR_BAD_REQUEST, "Could not find sensor " + sensorId); } } } else { setStatus(Status.CLIENT_ERROR_BAD_REQUEST, "Could not find depository " + depositoryId); } } catch (MisMatchedOwnerException e) { setStatus(Status.CLIENT_ERROR_BAD_REQUEST, e.getMessage()); } catch (NoMeasurementException e1) { setStatus(Status.SERVER_ERROR_INTERNAL, e1.getMessage()); } catch (NumberFormatException e) { setStatus(Status.SERVER_ERROR_INTERNAL, e.getMessage()); } catch (MeasurementGapException e) { setStatus(Status.CLIENT_ERROR_BAD_REQUEST, e.getMessage()); } catch (ParseException e) { setStatus(Status.SERVER_ERROR_INTERNAL, e.getMessage()); } catch (DatatypeConfigurationException e) { setStatus(Status.SERVER_ERROR_INTERNAL, e.getMessage()); } catch (IdNotFoundException e) { setStatus(Status.CLIENT_ERROR_BAD_REQUEST, e.getMessage()); } return null; } else { setStatus(Status.CLIENT_ERROR_FORBIDDEN, "Bad credentials."); return null; } } /** * @param sensorId the Sensor's id. * @param start The start time. Optional, must have end if not null. * @param end The end time. Optional, must exist if start not null. * @param timestamp The time of the value. Optional, must be null if start or * end. * @param earliest if not null will get earliest value. Optional, must be null * if start or end or latest or timestamp. * @param latest if not null will get latest value. Optional, must be null if * start or end or earliest or timestamp. * @return The interpolated value for the sensorId and time(s). * @throws IdNotFoundException if sensorId is not defined. * @throws MisMatchedOwnerException if sensorId is not in orgId. * @throws NoMeasurementException if there aren't measurements around the * time(s). * @throws ParseException if the times are not valid Date strings. * @throws DatatypeConfigurationException if there is a server problem. * @throws NumberFormatException if there is a problem. * @throws MeasurementGapException if the measurements are too far apart. */ private InterpolatedValue calculateValue(String sensorId, String start, String end, String timestamp, String earliest, String latest) throws IdNotFoundException, MisMatchedOwnerException, NoMeasurementException, ParseException, DatatypeConfigurationException, NumberFormatException, MeasurementGapException { Depository deposit = depot.getDepository(depositoryId, orgId, true); Sensor sensor = depot.getSensor(sensorId, orgId, true); Double value = null; Date startDate = null; Date endDate = null; Date time = null; InterpolatedValue val = null; if (earliest != null) { val = depot.getEarliestMeasuredValue(depositoryId, orgId, sensorId, true); val.addDefinedSensor(sensorId); val.addReportingSensor(sensorId); return val; } else if (latest != null) { val = depot.getLatestMeasuredValue(depositoryId, orgId, sensorId, true); val.addDefinedSensor(sensorId); val.addReportingSensor(sensorId); return val; } else if (timestamp != null) { time = DateConvert.parseCalStringToDate(timestamp); if (gapSeconds != null) { value = depot.getValue(depositoryId, orgId, sensor.getId(), time, Long.parseLong(gapSeconds), true); } else { value = depot.getValue(depositoryId, orgId, sensor.getId(), time, true); } val = new InterpolatedValue(sensorId, value, deposit.getMeasurementType(), time); } else if (start != null && end != null) { startDate = DateConvert.parseCalStringToDate(start); endDate = DateConvert.parseCalStringToDate(end); if (gapSeconds != null) { value = depot.getValue(depositoryId, orgId, sensor.getId(), startDate, endDate, Long.parseLong(gapSeconds), true); } else { value = depot.getValue(depositoryId, orgId, sensor.getId(), startDate, endDate, true); } val = new InterpolatedValue(sensorId, value, deposit.getMeasurementType(), startDate, endDate); } if (val != null) { val.addDefinedSensor(sensorId); if (value != null) { val.addReportingSensor(sensorId); } } return val; } }