Java tutorial
/* * This file is part of Transitime.org * * Transitime.org is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License (GPL) as published by * the Free Software Foundation, either version 3 of the License, or * any later version. * * Transitime.org 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 Transitime.org . If not, see <http://www.gnu.org/licenses/>. */ package org.transitime.db.structs; import java.io.Serializable; import java.util.Date; import java.util.List; import java.util.regex.Pattern; import javax.persistence.Column; import javax.persistence.Embedded; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; import javax.persistence.Id; import javax.persistence.Index; import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; import javax.persistence.Transient; import org.hibernate.HibernateException; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.annotations.DynamicUpdate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.transitime.applications.Core; import org.transitime.configData.AvlConfig; import org.transitime.db.hibernate.HibernateUtils; import org.transitime.ipc.data.IpcAvl; import org.transitime.utils.Geo; import org.transitime.utils.Time; import net.jcip.annotations.Immutable; /** * An AvlReport is a GPS report with some additional information, such as * vehicleId. * <p> * Serializable since Hibernate requires such. * * @author SkiBu Smith * */ @Immutable // From jcip.annoations @Entity @DynamicUpdate @Table(name = "AvlReports", indexes = { @Index(name = "AvlReportsTimeIndex", columnList = "time") }) public class AvlReport implements Serializable { // vehicleId is an @Id since might get multiple AVL reports // for different vehicles with the same time but need a unique // primary key. @Id @Column(length = HibernateUtils.DEFAULT_ID_SIZE) private final String vehicleId; // Need to use columnDefinition to explicitly specify that should use // fractional seconds. This column is an Id since shouldn't get two // AVL reports for the same vehicle for the same time. @Id @Column @Temporal(TemporalType.TIMESTAMP) private final Date time; // There is a delay between the time an AVL report is first generated // till the time it is actually processed. Therefore it is useful to // also keep track of the time it was processed so that can determine // latency. Will be null if AVL report not yet being processed. // Need to use columnDefinition to explicitly specify that should use // fractional seconds. @Column @Temporal(TemporalType.TIMESTAMP) private Date timeProcessed; @Embedded private final Location location; // Speed of vehicle in m/s. // Speed is an optional element since not always available // in an AVL feed. Internally it needs to be a Float and // be set to null when the value is not valid so that it can be stored // in the database. This is because Float.NaN doesn't work with JDBC // drivers. Externally though, such as when calling the constructor, // should use Float.NaN. It is converted to a null internally. @Column private final Float speed; // optional // Heading in degrees clockwise from north. // Heading is an optional element since not always available // in an AVL feed. It is in number of degrees clockwise from // north. Internally it needs to be a Float and // be set to null when the value is not valid so that it can be stored // in the database. This is because Float.NaN doesn't work with JDBC // drivers. Externally though, such as when calling the constructor, // should use Float.NaN. It is converted to a null internally. @Column private final Float heading; // optional // Optional text for describing the source of the AVL report @Column(length = SOURCE_LENGTH) private final String source; // Can be block, trip, or route ID @Column(length = HibernateUtils.DEFAULT_ID_SIZE) private String assignmentId; // optional // The type of the assignment received in the AVL feed public enum AssignmentType { UNSET, BLOCK_ID, // For when creating schedule based predictions BLOCK_FOR_SCHED_BASED_PREDS, ROUTE_ID, TRIP_ID, TRIP_SHORT_NAME, // For when get bad assignment info from AVL feed PREVIOUS }; @Column(length = 40) @Enumerated(EnumType.STRING) private AssignmentType assignmentType; // Optional. This value is transient because it is usually not set. // Initially only used for San Francisco Muni. Therefore not as // worthwhile for storing in the database. @Transient private final String leadVehicleId; // Optional @Column(length = HibernateUtils.DEFAULT_ID_SIZE) private final String driverId; // Optional @Column(length = 10) private final String licensePlate; // Optional. Set to null if passenger count info is not available @Column private final Integer passengerCount; // Optional. How full a bus is as a fraction. 0.0=empty, 1.0=at capacity. // This parameter is optional. Set to null if data not available. @Column(length = HibernateUtils.DEFAULT_ID_SIZE) private final Float passengerFullness; // Optional. For containing additional info for a particular feed. // Not declared final because setField1() is used to set values. @Column(length = HibernateUtils.DEFAULT_ID_SIZE) private String field1Name; // Optional. For containing additional info for a particular feed. // Not declared final because setField1() is used to set values. @Column(length = HibernateUtils.DEFAULT_ID_SIZE) String field1Value; // How long the AvlReport source field can be in db private static final int SOURCE_LENGTH = 10; private static final Logger logger = LoggerFactory.getLogger(AvlReport.class); // Needed because serializable so can transmit using JMS or RMI private static final long serialVersionUID = 92384928349823L; /********************** Member Functions **************************/ /** * Hibernate requires a no-args constructor for reading data. * So this is an experiment to see what can be done to satisfy * Hibernate but still have an object be immutable. Since * this constructor is only intended to be used by Hibernate * is is declared protected, since that still works. That way * others won't accidentally use this inappropriate constructor. * And yes, it is peculiar that even though the members in this * class are declared final that Hibernate can still create an * object using this no-args constructor and then set the fields. * Not quite as "final" as one might think. But at least it works. */ protected AvlReport() { vehicleId = null; time = null; location = null; speed = null; heading = null; source = null; assignmentId = null; assignmentType = AssignmentType.UNSET; leadVehicleId = null; driverId = null; licensePlate = null; timeProcessed = null; passengerCount = null; passengerFullness = null; field1Name = null; field1Value = null; } /** * Constructor for an AvlReport object that is not yet being processed. * Since not yet being processed timeProcessed is set to null. * * @param vehicleId * ID of the vehicle * @param time * Epoch time in msec of GPS report (not when processed) * @param lat * Latitude in decimal degrees * @param lon * Longitude in decimal degrees * @param speed * Speed of vehicle in m/s. Should be set to Float.NaN if speed * not available * @param heading * Heading of vehicle in degrees clockwise from north. Should be * set to Float.NaN if speed not available * @param source * Text describing the source of the report */ public AvlReport(String vehicleId, long time, double lat, double lon, float speed, float heading, String source) { // Store the values this.vehicleId = vehicleId; this.time = new Date(time); this.location = new Location(lat, lon); // DB requires null instead of NaN this.speed = Float.isNaN(speed) ? null : speed; this.heading = Float.isNaN(heading) ? null : heading; this.source = sized(source); this.assignmentId = null; this.assignmentType = AssignmentType.UNSET; this.leadVehicleId = null; this.driverId = null; this.licensePlate = null; this.passengerCount = null; this.passengerFullness = null; this.field1Name = null; this.field1Value = null; // Don't yet know when processed so set timeProcessed to null this.timeProcessed = null; } /** * Constructor for an AvlReport object that is not yet being processed. * Since not yet being processed timeProcessed is set to null. * * @param vehicleId * ID of the vehicle * @param time * Epoch time in msec of GPS report (not when processed) * @param location * @param speed * Speed of vehicle in m/s. Should be set to Float.NaN if speed * not available * @param heading * Heading of vehicle in degrees clockwise from north. Should be * set to Float.NaN if speed not available * @param source * Text describing the source of the report. Can only be * SOURCE_LENGTH (10) characters long */ public AvlReport(String vehicleId, long time, Location location, float speed, float heading, String source) { // Store the values this.vehicleId = vehicleId; this.time = new Date(time); this.location = location; // DB requires null instead of NaN this.speed = Float.isNaN(speed) ? null : speed; this.heading = Float.isNaN(heading) ? null : heading; this.source = sized(source); this.assignmentId = null; this.assignmentType = AssignmentType.UNSET; this.leadVehicleId = null; this.driverId = null; this.licensePlate = null; this.passengerCount = null; this.passengerFullness = null; this.field1Name = null; this.field1Value = null; // Don't yet know when processed so set timeProcessed to null this.timeProcessed = null; } /** * @param vehicleId * ID of the vehicle * @param time * Epoch time in msec of GPS report (not when processed) * For when speed and heading are not valid. They are set to Float.NaN . * Since not yet being processed timeProcessed is set to null. * @param vehicleId * @param time * @param lat * Latitude in decimal degrees * @param lon * Longitude in decimal degrees * @param source * Text describing the source of the report. Can only be * SOURCE_LENGTH (10) characters long */ public AvlReport(String vehicleId, long time, double lat, double lon, String source) { // Store the values this.vehicleId = vehicleId; this.time = new Date(time); ; this.location = new Location(lat, lon); this.speed = null; this.heading = null; this.source = sized(source); this.assignmentId = null; this.assignmentType = AssignmentType.UNSET; this.leadVehicleId = null; this.driverId = null; this.licensePlate = null; this.passengerCount = null; this.passengerFullness = null; this.field1Name = null; this.field1Value = null; // Don't yet know when processed so set timeProcessed to null this.timeProcessed = null; } /** * For when speed and heading are not valid. They are set to Float.NaN . * Since not yet being processed timeProcessed is set to null. * * @param vehicleId * @param time * @param location * @param source * Text describing the source of the report. Can only be * SOURCE_LENGTH (10) characters long */ public AvlReport(String vehicleId, long time, Location location, String source) { // Store the values this.vehicleId = vehicleId; this.time = new Date(time); this.location = location; this.speed = null; this.heading = null; this.source = sized(source); this.assignmentId = null; this.assignmentType = AssignmentType.UNSET; this.leadVehicleId = null; this.driverId = null; this.licensePlate = null; this.passengerCount = null; this.passengerFullness = null; this.field1Name = null; this.field1Value = null; // Don't yet know when processed so set timeProcessed to null timeProcessed = null; } /** * Constructor for an AvlReport object that is not yet being processed. * Since not yet being processed timeProcessed is set to null. * * @param vehicleId * identifier of vehicle * @param time * epoch time in msecs since 1970 * @param lat * latitude in decimal degrees * @param lon * longitude in decimal degrees * @param speed * Speed of vehicle in m/s. Should be set to Float.NaN if speed * not available * @param heading * Heading of vehicle in degrees clockwise from north. Should be * set to Float.NaN if speed not available * @param source * Text describing the source of the report. Can only be * SOURCE_LENGTH (10) characters long * @param leadVehicleId * Optional value. Set to null if not available. * @param driverId * Optional value. Set to null if not available. * @param licensePlate * Optional value. Set to null if not available. * @param passengerCount * Optional value. Set to the number of passengers on vehicles. * Set to null if not available. * @param passengerFullness * Optional Value. Fractional fullness of vehicle. 0.0=empty, * 1.0=full. Set to Float.NaN if data not available. */ public AvlReport(String vehicleId, long time, double lat, double lon, float speed, float heading, String source, String leadVehicleId, String driverId, String licensePlate, Integer passengerCount, float passengerFullness) { // Store the values this.vehicleId = vehicleId; this.time = new Date(time); this.location = new Location(lat, lon); // DB requires null instead of NaN this.speed = Float.isNaN(speed) ? null : speed; this.heading = Float.isNaN(heading) ? null : heading; this.source = sized(source); this.assignmentId = null; this.assignmentType = AssignmentType.UNSET; this.leadVehicleId = leadVehicleId; this.driverId = driverId; this.licensePlate = licensePlate; this.passengerCount = passengerCount; if (!Float.isNaN(passengerFullness)) this.passengerFullness = passengerFullness; else this.passengerFullness = null; this.field1Name = null; this.field1Value = null; // Don't yet know when processed so set timeProcessed to null this.timeProcessed = null; } /** * For converting a RMI IpcAvl object to a regular AvlReport. * * @param ipcAvl */ public AvlReport(IpcAvl ipcAvl) { this.vehicleId = ipcAvl.getVehicleId(); this.time = new Date(ipcAvl.getTime()); this.location = new Location(ipcAvl.getLatitude(), ipcAvl.getLongitude()); this.speed = Float.isNaN(ipcAvl.getSpeed()) ? null : ipcAvl.getSpeed(); this.heading = Float.isNaN(ipcAvl.getHeading()) ? null : ipcAvl.getHeading(); this.source = sized(ipcAvl.getSource()); this.assignmentId = ipcAvl.getAssignmentId(); this.assignmentType = ipcAvl.getAssignmentType(); this.leadVehicleId = null; this.driverId = null; this.licensePlate = null; this.passengerCount = null; this.passengerFullness = null; this.field1Name = null; this.field1Value = null; // Don't yet know when processed so set timeProcessed to null this.timeProcessed = null; } /** * Makes a copy of the AvlReport but uses the new time passed in. * Useful for creating a new AvlReport when AVL timeout occurs * when vehicle on a layover. * * @param toCopy * The AvlReport to copy (except for the AVL time) * @param newTime * The AVL time to use */ public AvlReport(AvlReport toCopy, Date newTime) { this.vehicleId = toCopy.vehicleId; this.time = newTime; this.location = toCopy.location; this.speed = toCopy.speed; this.heading = toCopy.heading; this.source = toCopy.source; this.assignmentId = toCopy.assignmentId; this.assignmentType = toCopy.assignmentType; this.leadVehicleId = toCopy.leadVehicleId; this.driverId = toCopy.driverId; this.licensePlate = toCopy.licensePlate; this.timeProcessed = toCopy.timeProcessed; this.passengerCount = toCopy.passengerCount; this.passengerFullness = toCopy.passengerFullness; this.field1Name = toCopy.field1Name; this.field1Value = toCopy.field1Value; } /** * Makes a copy of the AvlReport but uses the new assignment info passed in. * Useful for creating a new AvlReport when a bad assignment is received * since one can simply create a new AvlReport with the new assignment info. * * @param toCopy * The AvlReport to copy (except for the AVL time) * @param assignmentId * The assignment to use * @param assignmentType * How the assignment was done */ public AvlReport(AvlReport toCopy, String assignmentId, AssignmentType assignmentType) { this.vehicleId = toCopy.vehicleId; this.time = toCopy.time; this.location = toCopy.location; this.speed = toCopy.speed; this.heading = toCopy.heading; this.source = toCopy.source; this.assignmentId = assignmentId; this.assignmentType = assignmentType; this.leadVehicleId = toCopy.leadVehicleId; this.driverId = toCopy.driverId; this.licensePlate = toCopy.licensePlate; this.timeProcessed = toCopy.timeProcessed; this.passengerCount = toCopy.passengerCount; this.passengerFullness = toCopy.passengerFullness; this.field1Name = toCopy.field1Name; this.field1Value = toCopy.field1Value; } /** * For truncating the source member to size allowed in db. This way don't * later get an exception when trying to write an AvlReport to the db. * * @param source * @return The original source string, but truncated to SOURCE_LENGTH */ private static String sized(String source) { if (source == null || source.length() <= SOURCE_LENGTH) return source; return source.substring(0, SOURCE_LENGTH); } /** * Makes sure that the members of this class all have reasonable * values. * @return null if there are no problems. An error message if * there are problems with the data. */ public String validateData() { String errorMsg = ""; // Make sure vehicleId is set if (vehicleId == null) errorMsg += "VehicleId is null. "; else if (vehicleId.length() == 0) errorMsg += "VehicleId is empty string. "; // Make sure GPS time is OK long currentTime = System.currentTimeMillis(); if (time.getTime() < currentTime - 10 * Time.MS_PER_YEAR) errorMsg += "Time of " + Time.dateTimeStr(time) + " is more than 10 years old. "; if (time.getTime() > currentTime + 1 * Time.MS_PER_MIN) errorMsg += "Time of " + Time.dateTimeStr(time) + " is more than 1 minute into the future. "; // Make sure lat/lon is OK double lat = location.getLat(); double lon = location.getLon(); if (lat < AvlConfig.getMinAvlLatitude()) errorMsg += "Latitude of " + lat + " is less than the parameter " + AvlConfig.getMinAvlLatitudeParamName() + " which is set to " + AvlConfig.getMinAvlLatitude() + " . "; if (lat > AvlConfig.getMaxAvlLatitude()) errorMsg += "Latitude of " + lat + " is greater than the parameter " + AvlConfig.getMaxAvlLatitudeParamName() + " which is set to " + AvlConfig.getMaxAvlLatitude() + " . "; if (lon < AvlConfig.getMinAvlLongitude()) errorMsg += "Longitude of " + lon + " is less than the parameter " + AvlConfig.getMinAvlLongitudeParamName() + " which is set to " + AvlConfig.getMinAvlLongitude() + " . "; if (lon > AvlConfig.getMaxAvlLongitude()) errorMsg += "Longitude of " + lon + " is greater than the parameter " + AvlConfig.getMaxAvlLongitudeParamName() + " which is set to " + AvlConfig.getMaxAvlLongitude() + " . "; // Make sure speed is OK if (isSpeedValid()) { if (speed < 0.0f) errorMsg += "Speed of " + speed + " is less than zero. "; if (speed > AvlConfig.getMaxAvlSpeed()) { errorMsg += "Speed of " + speed + "m/s is greater than maximum allowable speed of " + AvlConfig.getMaxAvlSpeed() + "m/s. "; } } // Make sure heading is OK if (isHeadingValid()) { if (heading < 0.0f) errorMsg += "Heading of " + heading + " degrees is less than 0.0 degrees. "; if (heading > 360.0f) errorMsg += "Heading of " + heading + " degrees is greater than 360.0 degrees. "; } // Return the error message if any if (errorMsg.length() > 0) return errorMsg; else return null; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((assignmentId == null) ? 0 : assignmentId.hashCode()); result = prime * result + ((assignmentType == null) ? 0 : assignmentType.hashCode()); result = prime * result + ((driverId == null) ? 0 : driverId.hashCode()); result = prime * result + ((heading == null) ? 0 : heading.hashCode()); result = prime * result + ((licensePlate == null) ? 0 : licensePlate.hashCode()); result = prime * result + ((location == null) ? 0 : location.hashCode()); result = prime * result + ((speed == null) ? 0 : speed.hashCode()); result = prime * result + ((time == null) ? 0 : time.hashCode()); result = prime * result + ((timeProcessed == null) ? 0 : timeProcessed.hashCode()); result = prime * result + ((vehicleId == null) ? 0 : vehicleId.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; AvlReport other = (AvlReport) obj; if (assignmentId == null) { if (other.assignmentId != null) return false; } else if (!assignmentId.equals(other.assignmentId)) return false; if (assignmentType != other.assignmentType) return false; if (driverId == null) { if (other.driverId != null) return false; } else if (!driverId.equals(other.driverId)) return false; if (heading == null) { if (other.heading != null) return false; } else if (!heading.equals(other.heading)) return false; if (licensePlate == null) { if (other.licensePlate != null) return false; } else if (!licensePlate.equals(other.licensePlate)) return false; if (location == null) { if (other.location != null) return false; } else if (!location.equals(other.location)) return false; if (passengerCount == null) { if (other.passengerCount != null) return false; } else if (!passengerCount.equals(other.passengerCount)) return false; if (passengerFullness == null) { if (other.passengerFullness != null) return false; } else if (!passengerFullness.equals(other.passengerFullness)) return false; if (speed == null) { if (other.speed != null) return false; } else if (!speed.equals(other.speed)) return false; if (time == null) { if (other.time != null) return false; } else if (!time.equals(other.time)) return false; if (timeProcessed == null) { if (other.timeProcessed != null) return false; } else if (!timeProcessed.equals(other.timeProcessed)) return false; if (vehicleId == null) { if (other.vehicleId != null) return false; } else if (!vehicleId.equals(other.vehicleId)) return false; return true; } public String getVehicleId() { return vehicleId; } /** * @return The GPS time of the AVL report in msec epoch time */ public long getTime() { return time.getTime(); } /** * @return A Date object containing the GPS time */ public Date getDate() { return time; } /** * @return The time that the AVL report was received and processed. */ public long getTimeProcessed() { return timeProcessed.getTime(); } public Location getLocation() { return location; } public double getLat() { return location.getLat(); } public double getLon() { return location.getLon(); } /** * @return Speed of vehicle in meters per second. Returns Float.NaN if speed * is not valid. */ public float getSpeed() { return speed == null ? Float.NaN : speed; } /** * Heading of vehicles in degrees. The heading can sometimes * be invalid. Though internally an invalid heading is stored * as null it is returned by this method as NaN so that can * return a float. If speed is below * AvlConfig.minSpeedForValidHeading() then will also return * NaN. * * @return Heading of vehicles in degrees. If heading not set * or if speed below minimum then Float.NaN is returned. */ public float getHeading() { // If heading not available then return NaN if (heading == null) return Float.NaN; // The heading is valid. If there is a valid speed available and // but it is not high enough to make the heading valid // then return NaN. if (speed != null && speed < AvlConfig.minSpeedForValidHeading()) { return Float.NaN; } else { // Heading is valid so return it return heading; } } /** * The source of the AVL report * * @return */ public String getSource() { return source; } /** * Returns how many msec elapsed between the GPS fix was generated * to the time it was finally processed. Returns 0 if timeProcessed * was never set. * @return */ public long getLatency() { // If never processed then return 0. if (timeProcessed == null) return 0; return timeProcessed.getTime() - time.getTime(); } /** * For some AVL systems speed is not available and therefore cannot be used. * @return */ public boolean isSpeedValid() { return speed != null; } /** * For some AVL systems heading is not available and therefore cannot be used. * @return */ public boolean isHeadingValid() { return heading != null; } /** * Returns the assignment ID if it is set. If it is not set then * null is returned. * * @return */ public String getAssignmentId() { return assignmentId; } public AssignmentType getAssignmentType() { return assignmentType; } /** * Returns true if AVL report indicates that assignment is a block * assignment type such as AssignmentType.BLOCK_ID or * AssignmentType.BLOCK_FOR_SCHED_BASED_PREDS. * * @return true if block assignment */ public boolean isBlockIdAssignmentType() { return assignmentType == AssignmentType.BLOCK_ID || assignmentType == AssignmentType.BLOCK_FOR_SCHED_BASED_PREDS; } public boolean isTripIdAssignmentType() { return assignmentType == AssignmentType.TRIP_ID; } public boolean isTripShortNameAssignmentType() { return assignmentType == AssignmentType.TRIP_SHORT_NAME; } public boolean isRouteIdAssignmentType() { return assignmentType == AssignmentType.ROUTE_ID; } private static boolean unpredictableAssignmentsPatternInitialized = false; private static Pattern regExPattern = null; /** * Returns true if the assignment specified matches the regular expression * for unpredictable assignments. * * @param assignment * @return true if assignment matches regular expression */ public static boolean matchesUnpredictableAssignment(String assignment) { if (unpredictableAssignmentsPatternInitialized == false) { String regEx = AvlConfig.getUnpredictableAssignmentsRegEx(); if (regEx != null && !regEx.isEmpty()) { regExPattern = Pattern.compile(regEx); } unpredictableAssignmentsPatternInitialized = true; } if (regExPattern == null) return false; return regExPattern.matcher(assignment).matches(); } /** * Returns whether assignment information was set in the AVL data and that * assignment is valid. An assignment is not valid if it is configured to be * invalid. Examples of such include training vehicles, support vehicles, * and simply vehicles set to a special assignment such as 9999 for sfmta. * * @return true if has assignment and it is valid. Otherwise false. */ public boolean hasValidAssignment() { if (assignmentType != AssignmentType.UNSET && matchesUnpredictableAssignment(assignmentId)) logger.debug( "For vehicleId={} was assigned to \"{}\" but that " + "assignment is not considered valid due to " + "transitime.avl.unpredictableAssignmentsRegEx being set " + "to \"{}\"", vehicleId, assignmentId, AvlConfig.getUnpredictableAssignmentsRegEx()); return assignmentType != AssignmentType.UNSET && !matchesUnpredictableAssignment(assignmentId); } /** * Stores the assignment information as part of this AvlReport. If vehicle * is to not have an assignment need to set assignmentId to null and set * assignmentType to AssignmentType.UNSET. * * @param assignmentId * @param assignmentType */ public void setAssignment(String assignmentId, AssignmentType assignmentType) { // Make sure don't set to invalid values if (assignmentId == null && assignmentType != AssignmentType.UNSET) { logger.error("Tried to use setAssignment() to set assignment to " + "null without also setting assignmentType to UNSET"); return; } this.assignmentId = assignmentId; this.assignmentType = assignmentType; } /** * Returns the ID of the leading vehicle if this is an AVL report for a * non-lead vehicle in a multi-car consist. Otherwise returns null. * * @return */ public String getLeadVehicleId() { return leadVehicleId; } /** * For containing additional info as part of AVL feed that is * specific to a particular feed or a new element. * * @param name * @param value */ public void setField1(String name, String value) { field1Name = name; field1Value = value; } /** * If this is a vehicle in a multi-car consist and it is not the lead * vehicle then shouldn't generate redundant arrival/departure times, * predictions etc. * * @return True if non-lead car in multi-car consist */ public boolean ignoreBecauseInConsist() { return leadVehicleId != null; } public String getDriverId() { return driverId; } public String getLicensePlate() { return licensePlate; } /** * Returns the passenger count, as obtained from AVL feed. * If passenger count not available returns -1. * @return */ public int getPassengerCount() { if (passengerCount != null) return passengerCount; else return -1; } /** * Returns whether the passenger count is valid. * * @return true if count is valid. */ public boolean isPassengerCountValid() { return passengerCount != null; } /** * Fraction indicating how full a vehicle is. Returns NaN if info not * available. * * @return */ public float getPassengerFullness() { if (passengerFullness != null) return passengerFullness; else return Float.NaN; } /** * Returns whether the passenger fullness is valid. * @return true if passenger fullness is valid. */ public boolean isPassengerFullnessValid() { return passengerFullness != null; } /** * Updates the object to record the current time as the time that * the data was actually processed. */ public void setTimeProcessed() { timeProcessed = new Date(System.currentTimeMillis()); } public String getField1Name() { return field1Name; } public String getField1Value() { return field1Value; } /** * Returns true if the AVL report is configured to indicate that it was * created to generate schedule based predictions. * * @return true if for schedule based predictions */ public boolean isForSchedBasedPreds() { return assignmentType == AssignmentType.BLOCK_FOR_SCHED_BASED_PREDS; } @Override public String toString() { return "AvlReport [" + "vehicleId=" + vehicleId + ", time=" + Time.dateTimeStrMsec(time) + (timeProcessed == null ? "" : ", timeProcessed=" + Time.dateTimeStrMsec(timeProcessed)) + ", location=" + location + ", speed=" + Geo.speedFormat(getSpeed()) + ", heading=" + Geo.headingFormat(getHeading()) + ", source=" + source + ", assignmentId=" + assignmentId + ", assignmentType=" + assignmentType + (leadVehicleId == null ? "" : ", leadVehicleId=" + leadVehicleId) + (driverId == null ? "" : ", driverId=" + driverId) + (licensePlate == null ? "" : ", licensePlate=" + licensePlate) + (passengerCount == null ? "" : ", passengerCount=" + passengerCount) + (passengerFullness == null ? "" : ", passengerFullness=" + passengerFullness) + (field1Name == null ? "" : ", field1Name=" + field1Name) + (field1Value == null ? "" : ", field1Value=" + field1Value) + "]"; } /** * Gets list of AvlReports from database for the time span specified. * * @param beginTime * @param endTime * @param vehicleId * Optional. If not null then will only return results for that * vehicle * @param clause * Optional. If not null then the clause, such as "ORDER BY time" * will be added to the hql statement. * @return List of AvlReports or null if an exception is thrown */ public static List<AvlReport> getAvlReportsFromDb(Date beginTime, Date endTime, String vehicleId, String clause) { // Sessions are not threadsafe so need to create a new one each time. // They are supposed to be lightweight so this should be OK. Session session = HibernateUtils.getSession(); // Create the query. Table name is case sensitive! String hql = "FROM AvlReport " + " WHERE time >= :beginDate " + " AND time < :endDate"; if (vehicleId != null) hql += " AND vehicleId=:vehicleId"; if (clause != null) hql += " " + clause; Query query = session.createQuery(hql); // Set the parameters if (vehicleId != null) query.setString("vehicleId", vehicleId); query.setTimestamp("beginDate", beginTime); query.setTimestamp("endDate", endTime); try { @SuppressWarnings("unchecked") List<AvlReport> avlReports = query.list(); return avlReports; } catch (HibernateException e) { // Log error to the Core logger Core.getLogger().error(e.getMessage(), e); return null; } finally { // Clean things up. Not sure if this absolutely needed nor if // it might actually be detrimental and slow things down. session.close(); } } }