/* * RestMonitor.java - Copyright(c) 2013, 204 Joe Pasqua * Provided under the MIT License. See the LICENSE file for details. * Created: Oct 22, 2014 */ package org.noroomattheinn.visibletesla.data; import java.util.Calendar; import javafx.beans.property.BooleanProperty; import javafx.beans.property.ObjectProperty; import org.noroomattheinn.timeseries.Row; import org.noroomattheinn.timeseries.RowDescriptor; import org.noroomattheinn.utils.CalTime; import org.noroomattheinn.utils.TrackedObject; import org.noroomattheinn.visibletesla.vehicle.VTVehicle; /** * RestMonitor - Monitor and store data about Rest Cycles. * * @author Joe Pasqua <joe at NoRoomAtTheInn dot org> */ class RestMonitor { /*------------------------------------------------------------------------------ * * Constants and Enums * *----------------------------------------------------------------------------*/ private static final long MIN_REST_PERIOD = 60 * 60 * 1000; // 60 Minutes /*------------------------------------------------------------------------------ * * Internal State * *----------------------------------------------------------------------------*/ private final VTVehicle vtVehicle; private final TrackedObject<RestCycle> lastRestCycle; private final RowDescriptor schema; private final Calendar fromLimit, toLimit; private final boolean stradles; private final BooleanProperty limitEnabled; private RestCycle cycleInProgress = null; /*============================================================================== * ------- ------- * ------- Public Interface To This Class ------- * ------- ------- *============================================================================*/ RestMonitor(VTVehicle v, TrackedObject<RestCycle> lastRestCycle, BooleanProperty limitEnabled, ObjectProperty<CalTime> limitFrom, ObjectProperty<CalTime> limitTo) { this.vtVehicle = v; this.lastRestCycle = lastRestCycle; this.schema = VTData.schema; this.limitEnabled = limitEnabled; if (limitEnabled.get()) { fromLimit = limitFrom.get(); toLimit = limitTo.get(); stradles = (toLimit.before(fromLimit)); } else { fromLimit = toLimit = null; stradles = false; } this.cycleInProgress = null; vtVehicle.chargeState.addTracker(new Runnable() { @Override public void run() { handleNewData(StatsCollector.rowFromStates( vtVehicle.chargeState.get(), vtVehicle.streamState.get())); } }); } void handleNewData(Row r) { long timestamp = r.timestamp; if (outOfRange(timestamp)) { if (cycleInProgress != null) { completeCycle(r); } return; } double speed = r.get(schema, VTData.SpeedKey); double voltage = r.get(schema, VTData.VoltageKey); boolean idle = (speed == 0 && voltage < 100); if (cycleInProgress == null ) { // Not in a cycle if (idle) { startCycle(r); } } else { // In the middle of a cycle if (idle) { updateCycle(r); } else { completeCycle(r); } } } /*------------------------------------------------------------------------------ * * Private Utility Methods * *----------------------------------------------------------------------------*/ private void startCycle(Row r) { cycleInProgress = new RestCycle(); cycleInProgress.startTime = r.timestamp; cycleInProgress.startRange = r.get(schema, VTData.EstRangeKey); cycleInProgress.startSOC = r.get(schema, VTData.SOCKey); } private void updateCycle(Row r) { cycleInProgress.endTime = r.timestamp; cycleInProgress.endRange = r.get(schema, VTData.EstRangeKey); cycleInProgress.endSOC = r.get(schema, VTData.SOCKey); cycleInProgress.lat = r.get(schema, VTData.LatitudeKey); cycleInProgress.lng = r.get(schema, VTData.LongitudeKey); } private void completeCycle(Row r) { updateCycle(r); if (cycleInProgress.endTime - cycleInProgress.startTime > MIN_REST_PERIOD) { // OK, there's another odd situation to handle. If we start // a rest period and then stop getting data, we may miss a // charge. In that case the rest may look like we gained // power instead of losing power. In that case just toss // the rest period. It will skew the data. if (cycleInProgress.endRange <= cycleInProgress.startRange) { lastRestCycle.set(cycleInProgress); } } cycleInProgress = null; } private boolean outOfRange(long ts) { if (!limitEnabled.get()) return false; CalTime c = new CalTime(ts); if (stradles) { return (c.after(toLimit) && c.before(fromLimit)); } else { return c.after(toLimit) || c.before(fromLimit); } } }