/** * Copyright (C) 2013 - 2015 the enviroCar community * <p> * This file is part of the enviroCar app. * <p> * The enviroCar app 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. * <p> * The enviroCar app 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. * <p> * You should have received a copy of the GNU General Public License along * with the enviroCar app. If not, see http://www.gnu.org/licenses/. */ package org.envirocar.remote.serializer; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonArray; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import com.google.gson.JsonPrimitive; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; import com.google.gson.stream.JsonWriter; import org.envirocar.core.entity.Car; import org.envirocar.core.entity.Measurement; import org.envirocar.core.entity.Track; import org.envirocar.core.entity.TrackImpl; import org.envirocar.core.logging.Logger; import org.envirocar.core.util.FileWithMetadata; import org.envirocar.core.util.Util; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import static org.envirocar.core.entity.Measurement.PropertyKey.CALCULATED_MAF; import static org.envirocar.core.entity.Measurement.PropertyKey.CO2; import static org.envirocar.core.entity.Measurement.PropertyKey.CONSUMPTION; import static org.envirocar.core.entity.Measurement.PropertyKey.ENGINE_LOAD; import static org.envirocar.core.entity.Measurement.PropertyKey.FUEL_SYSTEM_LOOP; import static org.envirocar.core.entity.Measurement.PropertyKey.FUEL_SYSTEM_STATUS_CODE; import static org.envirocar.core.entity.Measurement.PropertyKey.GPS_ACCURACY; import static org.envirocar.core.entity.Measurement.PropertyKey.GPS_ALTITUDE; import static org.envirocar.core.entity.Measurement.PropertyKey.GPS_BEARING; import static org.envirocar.core.entity.Measurement.PropertyKey.GPS_HDOP; import static org.envirocar.core.entity.Measurement.PropertyKey.GPS_PDOP; import static org.envirocar.core.entity.Measurement.PropertyKey.GPS_SPEED; import static org.envirocar.core.entity.Measurement.PropertyKey.GPS_VDOP; import static org.envirocar.core.entity.Measurement.PropertyKey.INTAKE_PRESSURE; import static org.envirocar.core.entity.Measurement.PropertyKey.INTAKE_TEMPERATURE; import static org.envirocar.core.entity.Measurement.PropertyKey.LAMBDA_CURRENT; import static org.envirocar.core.entity.Measurement.PropertyKey.LAMBDA_CURRENT_ER; import static org.envirocar.core.entity.Measurement.PropertyKey.LAMBDA_VOLTAGE; import static org.envirocar.core.entity.Measurement.PropertyKey.LAMBDA_VOLTAGE_ER; import static org.envirocar.core.entity.Measurement.PropertyKey.LONG_TERM_TRIM_1; import static org.envirocar.core.entity.Measurement.PropertyKey.MAF; import static org.envirocar.core.entity.Measurement.PropertyKey.RPM; import static org.envirocar.core.entity.Measurement.PropertyKey.SHORT_TERM_TRIM_1; import static org.envirocar.core.entity.Measurement.PropertyKey.SPEED; import static org.envirocar.core.entity.Measurement.PropertyKey.THROTTLE_POSITON; /** * @author dewall */ public class TrackSerializer implements JsonSerializer<Track>, JsonDeserializer<Track> { private static final Logger LOG = Logger.getLogger(TrackSerializer.class); public static final Set<Measurement.PropertyKey> supportedPhenomenons = new HashSet<>(); static { supportedPhenomenons.add(CALCULATED_MAF); supportedPhenomenons.add(MAF); supportedPhenomenons.add(CO2); supportedPhenomenons.add(SPEED); supportedPhenomenons.add(RPM); supportedPhenomenons.add(INTAKE_PRESSURE); supportedPhenomenons.add(INTAKE_TEMPERATURE); supportedPhenomenons.add(CONSUMPTION); supportedPhenomenons.add(ENGINE_LOAD); supportedPhenomenons.add(THROTTLE_POSITON); supportedPhenomenons.add(GPS_ACCURACY); supportedPhenomenons.add(GPS_ALTITUDE); supportedPhenomenons.add(GPS_BEARING); supportedPhenomenons.add(GPS_HDOP); supportedPhenomenons.add(GPS_PDOP); supportedPhenomenons.add(GPS_VDOP); supportedPhenomenons.add(GPS_SPEED); supportedPhenomenons.add(SHORT_TERM_TRIM_1); supportedPhenomenons.add(LONG_TERM_TRIM_1); supportedPhenomenons.add(LAMBDA_CURRENT); supportedPhenomenons.add(LAMBDA_CURRENT_ER); supportedPhenomenons.add(LAMBDA_VOLTAGE); supportedPhenomenons.add(LAMBDA_VOLTAGE_ER); supportedPhenomenons.add(FUEL_SYSTEM_LOOP); supportedPhenomenons.add(FUEL_SYSTEM_STATUS_CODE); } @Override public JsonElement serialize(Track src, Type typeOfSrc, JsonSerializationContext context) { LOG.info("serialize() track"); // set the properties of the json object JsonObject trackProperties = new JsonObject(); trackProperties.addProperty(Track.KEY_TRACK_PROPERTIES_NAME, src.getName()); trackProperties.addProperty(Track.KEY_TRACK_PROPERTIES_DESCRIPTION, src.getDescription()); trackProperties.addProperty(Track.KEY_TRACK_PROPERTIES_SENSOR, src.getCar().getId()); try { if (src.getMetadata() != null) { JSONObject json = src.getMetadata().toJson(); JSONArray names = json.names(); for (int i = 0; i < names.length(); i++) { trackProperties.addProperty(names.get(i).toString(), json.getString(names.get(i).toString())); } } else { LOG.warn("The track does not provide metadata!"); } } catch (JSONException e) { LOG.severe("Error while parsing metadata of track", e); } // serialize the array of features. JsonArray trackFeatures = new JsonArray(); List<Measurement> measurements = src.getMeasurements(); if (measurements == null || measurements.isEmpty()) { LOG.severe("Track did not contain any non obfuscated measurements."); return null; } try { for (Measurement measurement : measurements) { JsonElement measurementJson = createMeasurementProperties( measurement, src.getCar()); trackFeatures.add(measurementJson); } } catch (JSONException e) { e.printStackTrace(); } JsonObject result = new JsonObject(); result.addProperty(Track.KEY_TRACK_TYPE, "FeatureCollection"); //TODO result.addProperty(Track.KEY_TRACK_PROPERTIES_LENGTH, src.getLengthOfTrack()); result.add(Track.KEY_TRACK_PROPERTIES, trackProperties); result.add(Track.KEY_TRACK_FEATURES, trackFeatures); return result; } private JsonElement createMeasurementProperties(Measurement src, Car car) throws JSONException { // Create the Geometry json object JsonObject geometryJsonObject = new JsonObject(); geometryJsonObject.addProperty(Track.KEY_TRACK_TYPE, "Point"); // Create the coordinates of the geometry json object JsonArray coordinatesArray = new JsonArray(); coordinatesArray.add(new JsonPrimitive(src.getLongitude())); coordinatesArray.add(new JsonPrimitive(src.getLatitude())); geometryJsonObject.add(Track.KEY_TRACK_FEATURES_GEOMETRY_COORDINATES, coordinatesArray); // Create measurement properties. JsonObject propertiesJson = new JsonObject(); propertiesJson.addProperty(Track.KEY_TRACK_FEATURES_PROPERTIES_TIME, Util.longToIsoDate(src.getTime())); propertiesJson.addProperty("sensor", car.getId()); // Add all measured phenomenons to this measurement. JsonObject phenomenons = createPhenomenons(src, car.getFuelType() == Car.FuelType.DIESEL); if (phenomenons != null) { propertiesJson.add(Track.KEY_TRACK_FEATURES_PROPERTIES_PHENOMENONS, phenomenons); } // Create the final json Measurement JsonObject result = new JsonObject(); result.addProperty("type", "Feature"); result.add(Track.KEY_TRACK_FEATURES_GEOMETRY, geometryJsonObject); result.add(Track.KEY_TRACK_FEATURES_PROPERTIES, propertiesJson); return result; } @Override public Track deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { LOG.info("deserialize()"); // Get the properties json object. JsonObject properties = json.getAsJsonObject() .get(Track.KEY_TRACK_PROPERTIES).getAsJsonObject(); // Parse the properties String id = properties.get(Track.KEY_TRACK_PROPERTIES_ID).getAsString(); String name = properties.has(Track.KEY_TRACK_PROPERTIES_NAME) ? properties.get(Track.KEY_TRACK_PROPERTIES_NAME).getAsString() : ("unnamed Track #" + id); String description = properties.has(Track.KEY_TRACK_PROPERTIES_DESCRIPTION) ? properties.get(Track.KEY_TRACK_PROPERTIES_DESCRIPTION).getAsString() : ""; // Parse the car object. JsonObject carObject = properties.get(Track.KEY_TRACK_PROPERTIES_SENSOR) .getAsJsonObject(); Car car = context.<Car>deserialize(carObject, Car.class); LOG.info("deserialize() measurements"); // Parse the measurements JsonArray measurementsJsonArray = json.getAsJsonObject().get(Track .KEY_TRACK_FEATURES).getAsJsonArray(); List<Measurement> measurements = new ArrayList<>(); for (int i = 0; i < measurementsJsonArray.size(); i++) { JsonObject measurementObject = measurementsJsonArray.get(i).getAsJsonObject(); measurements.add(context.<Measurement>deserialize( measurementObject, Measurement.class)); } LOG.info("deserialze(): storing measurements in database"); // Create the track Track track = new TrackImpl(Track.DownloadState.DOWNLOADED); track.setTrackStatus(Track.TrackStatus.FINISHED); track.setRemoteID(id); track.setName(name); track.setDescription(description); track.setCar(car); track.setMeasurements(measurements); // Storing happens here... return track; } private JsonObject createPhenomenons(Measurement measurement, boolean isDiesel) throws JSONException { if (measurement.getAllProperties().isEmpty()) { return null; } JsonObject result = new JsonObject(); Map<Measurement.PropertyKey, Double> props = measurement.getAllProperties(); for (Measurement.PropertyKey key : props.keySet()) { if (supportedPhenomenons.contains(key)) { if (isDiesel && (key == Measurement.PropertyKey.CO2 || key == Measurement.PropertyKey.CONSUMPTION) ){ // DO NOTHING TODO delete when necessary } else { result.add(key.toString(), createValue(props.get(key))); } } } return result; } public static JsonObject createValue(Double double1) throws JSONException { JsonObject result = new JsonObject(); result.addProperty("value", double1); return result; } public static FileWithMetadata exportTrack(Track track) throws IOException { File result = new File(Util.resolveExternalStorageBaseFolder(), "enviroCar-track-" + track.getTrackID() + ".json"); FileOutputStream out = new FileOutputStream(result); JsonWriter writer = new JsonWriter(new OutputStreamWriter(out, "UTF-8")); writer.setIndent(" "); Gson gson = new GsonBuilder() .registerTypeAdapter(Track.class, new TrackSerializer()) .create(); gson.toJson(track, Track.class, writer); writer.flush(); writer.close(); return new FileWithMetadata(result, false); } }