/*
* VTVehicle - Copyright(c) 2014 Joe Pasqua
* Provided under the MIT License. See the LICENSE file for details.
* Created: Nov 20, 2014
*/
package org.noroomattheinn.visibletesla.vehicle;
import java.util.Map;
import java.util.prefs.Preferences;
import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import org.noroomattheinn.utils.TrackedObject;
import org.noroomattheinn.tesla.BaseState;
import org.noroomattheinn.tesla.ChargeState;
import org.noroomattheinn.tesla.DriveState;
import org.noroomattheinn.tesla.GUIState;
import org.noroomattheinn.tesla.HVACState;
import org.noroomattheinn.tesla.Options;
import org.noroomattheinn.tesla.StreamState;
import org.noroomattheinn.tesla.Vehicle;
import org.noroomattheinn.tesla.VehicleState;
import org.noroomattheinn.utils.RestHelper;
import org.noroomattheinn.utils.ThreadManager;
import org.noroomattheinn.utils.Utils;
import static org.noroomattheinn.tesla.Tesla.logger;
/**
* VTVehicle: These methods are logically an extension to the Vehicle object.
* If only we could dynamically subclass!
*
* @author Joe Pasqua <joe at NoRoomAtTheInn dot org>
*/
public class VTVehicle {
/*------------------------------------------------------------------------------
*
* Constants and Enums
*
*----------------------------------------------------------------------------*/
private static final Map<String,Options.RoofType> overrideRoof = Utils.newHashMap(
"Body Color", Options.RoofType.RFBC,
"Black", Options.RoofType.RFBK,
"Pano", Options.RoofType.RFPO);
private static final Map<String,Options.Model> overrideModel = Utils.newHashMap(
// RWD Standard Models
"S60", Options.Model.S60,
"S85", Options.Model.S85,
// RWD Performance Models
"P85", Options.Model.P85,
"P85+", Options.Model.P85Plus,
// RWD Standard & Performance Models
"S70D", Options.Model.S70D,
"S85D", Options.Model.S85D,
"P85D", Options.Model.P85D);
private static final Map<String,Options.PaintColor> overrideColor = Utils.newHashMap(
"White", Options.PaintColor.PBCW,
"Black", Options.PaintColor.PBSB,
"Brown", Options.PaintColor.PMAB,
"Blue", Options.PaintColor.PMMB,
"Green", Options.PaintColor.PMSG,
"Silver", Options.PaintColor.PMSS,
"Grey", Options.PaintColor.PMTG,
"Steel Grey", Options.PaintColor.PMNG,
"Red", Options.PaintColor.PPMR,
"Sig. Red", Options.PaintColor.PPSR,
"Pearl", Options.PaintColor.PPSW,
"Titanium", Options.PaintColor.PPTI,
"Obsidian Black", Options.PaintColor.PMBL,
"Deep Blue", Options.PaintColor.PPSB
);
private static final Map<String,Options.WheelType> overrideWheels = Utils.newHashMap(
"19\" Silver", Options.WheelType.WT19,
"19\" Aero", Options.WheelType.WTAE,
"19\" Turbine", Options.WheelType.WTTB,
"19\" Turbine Grey", Options.WheelType.WTTG,
"21\" Silver", Options.WheelType.WT21,
"21\" Grey", Options.WheelType.WTSP);
private static final Map<String,Utils.UnitType> overrideUnits = Utils.newHashMap(
"Imperial", Utils.UnitType.Imperial,
"Metric", Utils.UnitType.Metric);
/*------------------------------------------------------------------------------
*
* Internal State
*
*----------------------------------------------------------------------------*/
private final Overrides overrides;
/*==============================================================================
* ------- -------
* ------- Public Interface To This Class -------
* ------- -------
*============================================================================*/
public final TrackedObject<ChargeState> chargeState;
public final TrackedObject<DriveState> driveState;
public final TrackedObject<VehicleState> vehicleState;
public final TrackedObject<HVACState> hvacState;
public final TrackedObject<GUIState> guiState;
public final TrackedObject<StreamState> streamState;
public final TrackedObject<Vehicle> vehicle;
public VTVehicle(Overrides overrides) {
this.overrides = overrides;
this.chargeState = new TrackedObject<>(new ChargeState());
this.driveState = new TrackedObject<>(null);
this.guiState = new TrackedObject<>(null);
this.hvacState = new TrackedObject<>(null);
this.streamState = new TrackedObject<>(new StreamState());
this.vehicleState = new TrackedObject<>(null);
this.vehicle = new TrackedObject<>(null);
}
public static class Overrides {
public final StringProperty wheels, color, units, model, roof;
public final BooleanProperty doWheels, doColor, doUnits, doModel, doRoof;
public Overrides() {
this.wheels = new SimpleStringProperty();
this.doWheels = new SimpleBooleanProperty();
this.color = new SimpleStringProperty();
this.doColor = new SimpleBooleanProperty();
this.units = new SimpleStringProperty();
this.doUnits = new SimpleBooleanProperty();
this.model = new SimpleStringProperty();
this.doModel = new SimpleBooleanProperty();
this.roof = new SimpleStringProperty();
this.doRoof = new SimpleBooleanProperty();
}
public Overrides(
StringProperty overideWheelsTo, BooleanProperty overideWheelsActive,
StringProperty overideColorTo, BooleanProperty overideColorActive,
StringProperty overideUnitsTo, BooleanProperty overideUnitsActive,
StringProperty overideModelTo, BooleanProperty overideModelActive,
StringProperty overideRoofTo, BooleanProperty overideRoofActive) {
this.wheels = overideWheelsTo;
this.doWheels = overideWheelsActive;
this.color = overideColorTo;
this.doColor = overideColorActive;
this.units = overideUnitsTo;
this.doUnits = overideUnitsActive;
this.model = overideModelTo;
this.doModel = overideModelActive;
this.roof = overideRoofTo;
this.doRoof = overideRoofActive;
}
}
public void setVehicle(Vehicle v) {
vehicle.set(v);
}
public Vehicle getVehicle() { return vehicle.get(); }
public Options.Model model() {
Options.Model model = overrideModel.get(overrides.model.get());
if (overrides.doModel.get() && model != null) return model;
return (getVehicle().getOptions().model());
}
public Options.RoofType roofType() {
Options.RoofType roof = overrideRoof.get(overrides.roof.get());
if (overrides.doRoof.get() && roof != null) return roof;
return (getVehicle().getOptions().roofType());
}
public Options.PaintColor paintColor() {
Options.PaintColor color = overrideColor.get(overrides.color.get());
if (overrides.doColor.get() && color != null) return color;
return (getVehicle().getOptions().paintColor());
}
public boolean useDegreesF() {
Utils.UnitType units = overrideUnits.get(overrides.units.get());
if (overrides.doUnits.get() && units != null)
return units == Utils.UnitType.Imperial;
return guiState.get().temperatureUnits.equalsIgnoreCase("F");
}
public Utils.UnitType unitType() {
Utils.UnitType units = overrideUnits.get(overrides.units.get());
if (overrides.doUnits.get() && units != null) return units;
return guiState.get().distanceUnits.equalsIgnoreCase("mi/hr")
? Utils.UnitType.Imperial : Utils.UnitType.Metric;
}
public double inProperUnits(double val) {
if (unitType() == Utils.UnitType.Imperial) return val;
return Utils.milesToKm(val);
}
public Options.WheelType wheelType() {
Options.WheelType wt = overrideWheels.get(overrides.wheels.get());
if (overrides.doWheels.get() && wt != null) return wt;
wt = getVehicle().getOptions().wheelType();
VehicleState vs = vehicleState.get();
if (vs.wheelType != null) {
// Check for known override wheel types, right now that's just Aero19
switch (vs.wheelType) {
case "Aero19": wt = Options.WheelType.WTAE; break;
case "Base19": wt = Options.WheelType.WT19; break;
case "Super21Gray": wt = Options.WheelType.WTSG; break;
default: logger.info("Unknown WheelType: " + vs.wheelType); break;
}
}
return wt;
}
public void waitForWakeup(final Runnable r, final BooleanProperty forceWakeup) {
final long TestSleepInterval = 5 * 60 * 1000; // 5 Minutes
final Utils.Predicate p = new Utils.Predicate() {
@Override public boolean eval() {
return ThreadManager.get().shuttingDown() || (forceWakeup != null && forceWakeup.get());
}
};
Runnable poller = new Runnable() {
@Override public void run() {
while (getVehicle().isAsleep()) {
Utils.sleep(TestSleepInterval, p);
if (ThreadManager.get().shuttingDown()) return;
if (forceWakeup != null && forceWakeup.get()) {
forceWakeup.set(false);
if (r != null) Platform.runLater(r);
}
}
}
};
ThreadManager.get().launch(poller, "Wait For Wakeup");
}
public boolean forceWakeup() {
ChargeState charge;
for (int i = 0; i < 15; i++) {
if ((charge = getVehicle().queryCharge()).valid) {
noteUpdatedState(charge);
return true;
}
getVehicle().wakeUp();
ThreadManager.get().sleep(5 * 1000);
}
return false;
}
public void noteUpdatedState(final BaseState state) {
if (Platform.isFxApplicationThread()) {
noteUpdatedStateInternal(state);
} else {
Platform.runLater(new Runnable() {
@Override
public void run() {
noteUpdatedStateInternal(state);
}
});
}
}
public String carDetailsAsJSON() {
return CarInfo.carDetailsAsJSON(this);
}
public String carStateAsJSON() {
return CarInfo.carStateAsJSON(this);
}
public ChargeState lastSavedCS() {
Preferences persistentStore = Preferences.userNodeForPackage(this.getClass());
String json = persistentStore.get(this.vehicle.get().getVIN() + "_ChargeState", null);
if (json == null) return null;
return new ChargeState(RestHelper.newJSONObject(json));
}
public GUIState lastSavedGS() {
Preferences persistentStore = Preferences.userNodeForPackage(this.getClass());
String json = persistentStore.get(this.vehicle.get().getVIN() + "_GUIState", null);
if (json == null) return null;
return new GUIState(RestHelper.newJSONObject(json));
}
public VehicleState lastSavedVS() {
Preferences persistentStore = Preferences.userNodeForPackage(this.getClass());
String json = persistentStore.get(this.vehicle.get().getVIN() + "_VehicleState", null);
if (json == null) return null;
return new VehicleState(RestHelper.newJSONObject(json));
}
/*------------------------------------------------------------------------------
*
* Private utility methods
*
*----------------------------------------------------------------------------*/
private void noteUpdatedStateInternal(BaseState state) {
Preferences persistentStore = Preferences.userNodeForPackage(this.getClass());
String type = state.getClass().getSimpleName();
if (state instanceof ChargeState) {
chargeState.set((ChargeState) state);
} else if (state instanceof DriveState) {
driveState.set((DriveState) state);
} else if (state instanceof GUIState) {
guiState.set((GUIState) state);
} else if (state instanceof HVACState) {
hvacState.set((HVACState) state);
} else if (state instanceof VehicleState) {
vehicleState.set((VehicleState) state);
} else if (state instanceof StreamState) {
streamState.set((StreamState) state);
}
persistentStore.put(this.vehicle.get().getVIN() + "_" + type, state.rawState.toString());
}
}