getGtfsRealtimeURIs() {
return gtfsRealtimeURIs.getValue();
}
private static StringListConfigValue gtfsRealtimeURIs =
- new StringListConfigValue("transitime.avl.gtfsRealtimeFeedURIs",
+ new StringListConfigValue("transitclock.avl.gtfsRealtimeFeedURIs",
null,
"Semicolon separated list of URIs of the GTFS-realtime data to read in");
@@ -58,7 +58,7 @@ private static String getCsvFileOutputDir() {
return csvFileOutputDir.getValue();
}
private static StringConfigValue csvFileOutputDir =
- new StringConfigValue("transitime.avl.csvFileOutputDir",
+ new StringConfigValue("transitclock.avl.csvFileOutputDir",
"/Users/Mike/gtfsRealtimeData/csv",
"Name of directory where output will be written");
@@ -69,7 +69,7 @@ private static String getTimeZoneStr() {
return timeZoneStr.getValue();
}
private static StringConfigValue timeZoneStr =
- new StringConfigValue("transitime.avl.timeZoneStr",
+ new StringConfigValue("transitclock.avl.timeZoneStr",
"",
"The timezone for the agency. In the form " +
"\"America/New_York\"");
@@ -78,7 +78,7 @@ private static boolean shouldOffsetForMapOfChina() {
return offsetForMapOfChina.getValue();
}
private static BooleanConfigValue offsetForMapOfChina =
- new BooleanConfigValue("transitime.avl.offsetForMapOfChina",
+ new BooleanConfigValue("transitclock.avl.offsetForMapOfChina",
false,
"If set to true then the latitudes/longitudes stored " +
"will be adjusted for the China map offset so that the " +
diff --git a/transitime/src/main/java/org/transitime/applications/Core.java b/transitclock/src/main/java/org/transitclock/applications/Core.java
old mode 100644
new mode 100755
similarity index 54%
rename from transitime/src/main/java/org/transitime/applications/Core.java
rename to transitclock/src/main/java/org/transitclock/applications/Core.java
index 21a7c61a3..ca21d7a27
--- a/transitime/src/main/java/org/transitime/applications/Core.java
+++ b/transitclock/src/main/java/org/transitclock/applications/Core.java
@@ -1,6 +1,6 @@
/*
* 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
@@ -14,91 +14,112 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.applications;
+package org.transitclock.applications;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import org.apache.commons.cli.*;
+import org.apache.commons.lang3.time.DateUtils;
+import org.hibernate.Criteria;
+import org.hibernate.Session;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.transitclock.config.ConfigFileReader;
+import org.transitclock.config.StringConfigValue;
+import org.transitclock.configData.AgencyConfig;
+import org.transitclock.configData.CoreConfig;
+import org.transitclock.core.ServiceUtils;
+import org.transitclock.core.TimeoutHandlerModule;
+import org.transitclock.core.dataCache.CacheTask;
+import org.transitclock.core.dataCache.DwellTimeModelCacheFactory;
+import org.transitclock.core.dataCache.ParallelProcessor;
+import org.transitclock.core.dataCache.PredictionDataCache;
+import org.transitclock.core.dataCache.StopArrivalDepartureCacheFactory;
+import org.transitclock.core.dataCache.TripDataHistoryCacheFactory;
+import org.transitclock.core.dataCache.VehicleDataCache;
+import org.transitclock.core.dataCache.ehcache.CacheManagerFactory;
+import org.transitclock.core.dataCache.frequency.FrequencyBasedHistoricalAverageCache;
+import org.transitclock.core.dataCache.scheduled.ScheduleBasedHistoricalAverageCache;
+import org.transitclock.core.predictiongenerator.scheduled.traveltime.kalman.TrafficManager;
+import org.transitclock.db.hibernate.DataDbLogger;
+import org.transitclock.db.hibernate.HibernateUtils;
+import org.transitclock.db.structs.ActiveRevisions;
+import org.transitclock.db.structs.Agency;
+import org.transitclock.db.structs.ArrivalDeparture;
+import org.transitclock.gtfs.DbConfig;
+import org.transitclock.guice.modules.ReportingModule;
+import org.transitclock.ipc.servers.*;
+import org.transitclock.modules.Module;
+import org.transitclock.monitoring.PidFile;
+import org.transitclock.utils.SettableSystemTime;
+import org.transitclock.utils.SystemCurrentTime;
+import org.transitclock.utils.SystemTime;
+import org.transitclock.utils.Time;
import java.io.PrintWriter;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
-import org.apache.commons.cli.BasicParser;
-import org.apache.commons.cli.CommandLine;
-import org.apache.commons.cli.CommandLineParser;
-import org.apache.commons.cli.HelpFormatter;
-import org.apache.commons.cli.OptionBuilder;
-import org.apache.commons.cli.Options;
-import org.apache.commons.cli.ParseException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.transitime.config.ConfigFileReader;
-import org.transitime.configData.AgencyConfig;
-import org.transitime.configData.CoreConfig;
-import org.transitime.core.ServiceUtils;
-import org.transitime.core.TimeoutHandlerModule;
-import org.transitime.core.dataCache.PredictionDataCache;
-import org.transitime.core.dataCache.VehicleDataCache;
-import org.transitime.db.hibernate.DataDbLogger;
-import org.transitime.db.hibernate.HibernateUtils;
-import org.transitime.db.structs.ActiveRevisions;
-import org.transitime.db.structs.Agency;
-import org.transitime.gtfs.DbConfig;
-import org.transitime.ipc.servers.CommandsServer;
-import org.transitime.ipc.servers.ConfigServer;
-import org.transitime.ipc.servers.PredictionsServer;
-import org.transitime.ipc.servers.ServerStatusServer;
-import org.transitime.ipc.servers.VehiclesServer;
-import org.transitime.modules.Module;
-import org.transitime.monitoring.PidFile;
-import org.transitime.utils.SettableSystemTime;
-import org.transitime.utils.SystemTime;
-import org.transitime.utils.SystemCurrentTime;
-import org.transitime.utils.Time;
+import static org.transitclock.core.dataCache.StopArrivalDepartureCacheInterface.createArrivalDeparturesCriteria;
/**
* The main class for running a Transitime Core real-time data processing
* system. Handles command line arguments and then initiates AVL feed.
- *
+ *
* @author SkiBu Smith
- *
+ *
*/
public class Core {
-
+
private static Core singleton = null;
-
+
// Contains the configuration data read from database
private final DbConfig configData;
-
+
// For logging data such as AVL reports and arrival times to database
private final DataDbLogger dataDbLogger;
private final TimeoutHandlerModule timeoutHandlerModule;
-
+
private final ServiceUtils service;
- private final Time time;
+ private Time time;
// So that can access the current time, even when in playback mode
private SystemTime systemTime = new SystemCurrentTime();
-
+
// Set by command line option. Specifies config rev to use if set
private static String configRevStr = null;
// Read in configuration files. This should be done statically before
// the logback LoggerFactory.getLogger() is called so that logback can
// also be configured using a transitime config file. The files are
- // specified using the java system property -Dtransitime.configFiles .
+ // specified using the java system property -Dtransitclock.configFiles .
static {
ConfigFileReader.processConfig();
}
-
- private static final Logger logger =
+
+ private static StringConfigValue cacheReloadStartTimeStr =
+ new StringConfigValue("transitclock.core.cacheReloadStartTimeStr",
+ "",
+ "Date and time of when to start reading arrivaldepartures to inform caches.");
+
+ private static StringConfigValue cacheReloadEndTimeStr =
+ new StringConfigValue("transitclock.core.cacheReloadEndTimeStr",
+ "",
+ "Date and time of when to end reading arrivaldepartures to inform caches.");
+ private static final Logger logger =
LoggerFactory.getLogger(Core.class);
-
+
/********************** Member Functions **************************/
/**
* Construct the Core object and read in the config data. This is private
* so that the createCore() factory method must be used.
- *
+ *
* @param agencyId
*/
private Core(String agencyId) {
@@ -107,48 +128,51 @@ private Core(String agencyId) {
int configRev;
if (configRevStr != null) {
// Use config rev from command line
+
+
+
configRev = Integer.parseInt(configRevStr);
} else {
// Read in config rev from ActiveRevisions table in db
ActiveRevisions activeRevisions = ActiveRevisions.get(agencyId);
-
+
// If config rev not set properly then simply log error.
- // Originally would also exit() but found that want system to
+ // Originally would also exit() but found that want system to
// work even without GTFS configuration so that can test AVL feed.
if (!activeRevisions.isValid()) {
logger.error("ActiveRevisions in database is not valid. The "
- + "configuration revs must be set to proper values. {}",
+ + "configuration revs must be set to proper values. {}",
activeRevisions);
}
configRev = activeRevisions.getConfigRev();
}
- // Set the timezone so that when dates are read from db or are logged
+ // Set the timezone so that when dates are read from db or are logged
// the time will be correct. Therefore this needs to be done right at
// the start of the application, before db is read.
TimeZone timeZone = Agency.getTimeZoneFromDb(agencyId);
TimeZone.setDefault(timeZone);
-
+
// Clears out the session factory so that a new one will be created for
// future db access. This way new db connections are made. This is
// useful for dealing with timezones and postgres. For that situation
- // want to be able to read in timezone from db so can set default
- // timezone. Problem with postgres is that once a factory is used to
- // generate sessions the database will continue to use the default
- // timezone that was configured at that time. This means that future
- // calls to the db will use the wrong timezone! Through this function
- // one can read in timezone from database, set the default timezone,
- // clear the factory so that future db connections will use the newly
+ // want to be able to read in timezone from db so can set default
+ // timezone. Problem with postgres is that once a factory is used to
+ // generate sessions the database will continue to use the default
+ // timezone that was configured at that time. This means that future
+ // calls to the db will use the wrong timezone! Through this function
+ // one can read in timezone from database, set the default timezone,
+ // clear the factory so that future db connections will use the newly
// configured timezone, and then successfully process dates.
HibernateUtils.clearSessionFactory();
-
+
// Read in all GTFS based config data from the database
configData = new DbConfig(agencyId);
configData.read(configRev);
-
+
// Create the DataDBLogger so that generated data can be stored
// to database via a robust queue. But don't actually log data
- // if in playback mode since then would be writing data again
+ // if in playback mode since then would be writing data again
// that was first written when predictor was run in real time.
// Note: DataDbLogger needs to be started after the timezone is set.
// Otherwise when running for a different timezone than what the
@@ -159,26 +183,26 @@ private Core(String agencyId) {
dataDbLogger = DataDbLogger.getDataDbLogger(agencyId,
CoreConfig.storeDataInDatabase(),
CoreConfig.pauseIfDbQueueFilling());
-
+
// Start mandatory modules
timeoutHandlerModule = new TimeoutHandlerModule(AgencyConfig.getAgencyId());
timeoutHandlerModule.start();
-
+
service = new ServiceUtils(configData);
time = new Time(configData);
}
-
+
/**
* Creates the Core object for the application. There can only be one Core
* object per application. Uses CoreConfig.getAgencyId() to determine the
* agencyId. This means it typically uses the agency ID specified by the
- * Java property -Dtransitime.core.agencyId .
+ * Java property -Dtransitclock.core.agencyId .
*
* Usually doesn't need to be called directly because can simply use
* Core.getInstance().
*
* Synchronized to ensure that don't create more than a single Core.
- *
+ *
* @return The Core singleton, or null if could not create it
*/
synchronized public static Core createCore() {
@@ -190,42 +214,59 @@ synchronized public static Core createCore() {
logger.error("No agencyId specified for when creating Core.");
return null;
}
-
+
// Make sure only can have a single Core object
if (Core.singleton != null) {
logger.error("Core singleton already created. Cannot create another one.");
return null;
}
-
+
+ Core core = new Core(agencyId);
+ Core.singleton = core;
+ return core;
+ }
+
+ /**
+ * For testing access. Not to be used in production!
+ * @param agencyId
+ * @return
+ */
+ synchronized public static Core createTestCore(String agencyId) {
Core core = new Core(agencyId);
Core.singleton = core;
return core;
}
-
/**
- * For obtaining singleton Core object
- *
+ * For obtaining singleton Core object.
+ * Synchronized to prevent race conditions if starting lots of optional modules.
+ *
* @returns the Core singleton object for this application, or null if it
* could not be created
*/
public static Core getInstance() {
- if (Core.singleton == null)
- createCore();
-
+ if (singleton == null) {
+ // only synchronize if we have to!
+ synchronized (cacheReloadStartTimeStr) {
+ if (singleton == null) {
+ createCore();
+ }
+ }
+ }
+
return singleton;
}
-
+
/**
* Returns true if core application. If GTFS processing or other application
* then not a Core application and should't try to read in data such as
* route names for a trip.
- *
+ *
* @return true if core application
*/
public static boolean isCoreApplication() {
return Core.singleton != null;
}
-
+
/**
* Makes the config data available to all
* @return
@@ -233,7 +274,7 @@ public static boolean isCoreApplication() {
public DbConfig getDbConfig() {
return configData;
}
-
+
/**
* Returns the ServiceUtils object that can be reused for efficiency.
* @return
@@ -241,7 +282,7 @@ public DbConfig getDbConfig() {
public ServiceUtils getServiceUtils() {
return service;
}
-
+
/**
* For when want to use methods in Time. This is important when need
* methods that access a Calendar a lot. By putting the Calendar in
@@ -251,40 +292,47 @@ public ServiceUtils getServiceUtils() {
public Time getTime() {
return time;
}
-
+
+ /**
+ * unit test access, otherwise this is constructed internally.
+ */
+ public void setTime(Time time) {
+ this.time = time;
+ }
+
/**
* For when need system time but might be in playback mode. If not in
* playback mode then the time will be the time of the system clock. But if
* in playback mode then will be using a SettableSystemTime and the time
* will be that of the last AVL report.
- *
+ *
* @return The system epoch time
*/
public long getSystemTime() {
return systemTime.get();
}
-
+
/**
* For when need system time but might be in playback mode. If not in
* playback mode then the time will be the time of the system clock. But if
* in playback mode then will be using a SettableSystemTime and the time
* will be that of the last AVL report.
- *
+ *
* @return The system epoch time
*/
public Date getSystemDate() {
return new Date(getSystemTime());
}
-
+
/**
* For setting the system time when in playback or batch mode.
- *
- * @param systemTime
+ *
+ * @param systemEpochTime
*/
public void setSystemTime(long systemEpochTime) {
this.systemTime = new SettableSystemTime(systemEpochTime);
}
-
+
/**
* Returns the Core logger so that each class doesn't need to create
* its own and have it be configured properly.
@@ -293,18 +341,18 @@ public void setSystemTime(long systemEpochTime) {
public static final Logger getLogger() {
return logger;
}
-
+
/**
- * This method logs status of the logger system to the console. Could
+ * This method logs status of the logger system to the console. Could
* be useful for seeing if there are problems with the logger config file.
*/
private static void outputLoggerStatus() {
// For debugging output current state of logger
// Commented out for now because not truly useful
// LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
-// StatusPrinter.print(lc);
+// StatusPrinter.print(lc);
}
-
+
/**
* Returns the DataDbLogger for logging data to db.
* @return
@@ -312,7 +360,7 @@ private static void outputLoggerStatus() {
public DataDbLogger getDbLogger() {
return dataDbLogger;
}
-
+
/**
* Returns the timeout handler module
* @return
@@ -320,18 +368,18 @@ public DataDbLogger getDbLogger() {
public TimeoutHandlerModule getTimeoutHandlerModule() {
return timeoutHandlerModule;
}
-
+
/**
* Processes all command line options using Apache CLI.
* Further info at http://commons.apache.org/proper/commons-cli/usage.html
*/
@SuppressWarnings("static-access") // Needed for using OptionBuilder
- private static void processCommandLineOptions(String[] args)
+ private static void processCommandLineOptions(String[] args)
throws ParseException {
// Specify the options
Options options = new Options();
- options.addOption("h", "help", false, "Display usage and help info.");
-
+ options.addOption("h", "help", false, "Display usage and help info.");
+
options.addOption(OptionBuilder.withArgName("configRev")
.hasArg()
.withDescription("Specifies optional configuration revision. "
@@ -339,11 +387,11 @@ private static void processCommandLineOptions(String[] args)
+ "from the ActiveRevisions table in the database.")
.create("configRev")
);
-
+
// Parse the options
CommandLineParser parser = new BasicParser();
CommandLine cmd = parser.parse( options, args);
-
+
// Handle optional config rev
if (cmd.hasOption("configRev")) {
configRevStr = cmd.getOptionValue("configRev");
@@ -352,7 +400,7 @@ private static void processCommandLineOptions(String[] args)
// Handle help option
if (cmd.hasOption("h")) {
// Display help
- final String commandLineSyntax = "java transitime.jar";
+ final String commandLineSyntax = "java transitclock.jar";
final PrintWriter writer = new PrintWriter(System.out);
final HelpFormatter helpFormatter = new HelpFormatter();
helpFormatter.printHelp(writer,
@@ -369,24 +417,141 @@ private static void processCommandLineOptions(String[] args)
}
}
+
+
+ /* populate one day at a time to avoid memory issue */
/**
* Start the RMI Servers so that clients can obtain data
* on predictions, vehicles locations, etc.
- *
+ *
* @param agencyId
*/
- private static void startRmiServers(String agencyId) {
+ public static void startRmiServers(String agencyId) {
// Start up all of the RMI servers
PredictionsServer.start(agencyId, PredictionDataCache.getInstance());
VehiclesServer.start(agencyId, VehicleDataCache.getInstance());
ConfigServer.start(agencyId);
ServerStatusServer.start(agencyId);
+ RevisionInformationServer.start(agencyId);
CommandsServer.start(agencyId);
+ CacheQueryServer.start(agencyId);
+ PredictionAnalysisServer.start(agencyId);
+ HoldingTimeServer.start(agencyId);
+
+ Injector injector = Guice.createInjector(new ReportingModule());
+ ReportingServer reportingServer = injector.getInstance(ReportingServer.class);
+ reportingServer.start(agencyId);
}
+ static private void populateCaches() throws Exception
+ {
+ Session session = HibernateUtils.getSession();
+ Date endDate=new Date(Time.getStartOfDay(Calendar.getInstance().getTime()));
+ endDate = DateUtils.addDays(endDate, 1); // go to end of day
+
+ if(cacheReloadStartTimeStr.getValue().length()>0&&cacheReloadEndTimeStr.getValue().length()>0)
+ {
+
+ Criteria criteria = session.createCriteria(ArrivalDeparture.class);
+ Date cacheStartDate = Time.parse(cacheReloadStartTimeStr.getValue());
+ Date cacheEndDate = Time.parse(cacheReloadEndTimeStr.getValue());
+ List results = createArrivalDeparturesCriteria(criteria, cacheStartDate, cacheEndDate);
+ if(TripDataHistoryCacheFactory.getInstance()!=null)
+ {
+ logger.info("Populating TripDataHistoryCache cache for period {} to {}",cacheReloadStartTimeStr.getValue(),cacheReloadEndTimeStr.getValue());
+ TripDataHistoryCacheFactory.getInstance().populateCacheFromDb(results);
+ }
+
+ if(FrequencyBasedHistoricalAverageCache.getInstance()!=null)
+ {
+ logger.info("Populating FrequencyBasedHistoricalAverageCache cache for period {} to {}",cacheReloadStartTimeStr.getValue(),cacheReloadEndTimeStr.getValue());
+ FrequencyBasedHistoricalAverageCache.getInstance().populateCacheFromDb(results);
+ }
+
+ if(StopArrivalDepartureCacheFactory.getInstance()!=null)
+ {
+ logger.info("Populating StopArrivalDepartureCache cache for period {} to {}",cacheReloadStartTimeStr.getValue(),cacheReloadEndTimeStr.getValue());
+ StopArrivalDepartureCacheFactory.getInstance().populateCacheFromDb(results);
+ }
+
+ if (TrafficManager.getInstance() != null) {
+ TrafficManager.getInstance().populateCacheFromDb(session, new Date(Time.parse(cacheReloadStartTimeStr.getValue()).getTime()), new Date(Time.parse(cacheReloadEndTimeStr.getValue()).getTime()));
+ }
+ }else
+ {
+ ParallelProcessor pp = new ParallelProcessor();
+ pp.startup();
+ for(int i=0;i defaultInput = createArrivalDeparturesCriteria(criteria, startDate, endDate);
+
+ logger.info("ParallelProcessor loaded {} to {}", startDate, endDate);
+
+
+ if(TripDataHistoryCacheFactory.getInstance()!=null)
+ {
+ CacheTask ct = new CacheTask(startDate, endDate, CacheTask.Type.TripDataHistoryCacheFactory, defaultInput);
+ pp.enqueue(ct);
+ }
+
+ // Only need to populate two days worth of stop arrival departure cache
+ if(i < 2 && StopArrivalDepartureCacheFactory.getInstance()!=null)
+ {
+ CacheTask ct = new CacheTask(startDate, endDate, CacheTask.Type.StopArrivalDepartureCacheFactory, defaultInput);
+ pp.enqueue(ct);
+ }
+
+ if(FrequencyBasedHistoricalAverageCache.getInstance()!=null)
+ {
+ CacheTask ct = new CacheTask(startDate, endDate, CacheTask.Type.FrequencyBasedHistoricalAverageCache, defaultInput);
+ pp.enqueue(ct);
+ }
+
+ if(ScheduleBasedHistoricalAverageCache.getInstance()!=null)
+ {
+ CacheTask ct = new CacheTask(startDate, endDate, CacheTask.Type.ScheduleBasedHistoricalAverageCache, defaultInput);
+ pp.enqueue(ct);
+ }
+
+ if(DwellTimeModelCacheFactory.getInstance() != null) {
+ CacheTask ct = new CacheTask(startDate, endDate, CacheTask.Type.DwellTimeModelCacheFactory, defaultInput);
+ pp.enqueue(ct);
+ }
+
+ if (i < 5 && TrafficManager.getInstance() != null && TrafficManager.getInstance().isEnabled()) {
+ CacheTask ct = new CacheTask(startDate, endDate, CacheTask.Type.TrafficDataHistoryCache, null);
+ pp.enqueue(ct);
+ }
+
+ endDate=startDate;
+ }
+ // don't continue until caches are ready!
+ while (!pp.isDone()) {
+ try {
+ logger.info("waiting on caching to complete with {} in run queue, {} in wait queue; {} running ", pp.getRunQueueSize(), pp.getWaitQueueSize(), pp.getDebugInfo() );
+ Thread.sleep(10 * 1000);
+ } catch (InterruptedException ie) {
+ return;
+ }
+ }
+ // clean up after ourselves -- releasing threads
+ pp.shutdown();
+ }
+
+ }
+
+ private static String getDateAsString(LocalDateTime date){
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern(Time.getDateTimePattern());
+ return date.format(formatter);
+ }
+
/**
* The main program that runs the entire Transitime application.!
- *
+ *
* @param args
*/
public static void main(String[] args) {
@@ -402,31 +567,62 @@ public static void main(String[] args) {
// or restart this application
PidFile.createPidFile(CoreConfig.getPidFileDirectory()
+ AgencyConfig.getAgencyId() + ".pid");
-
+
// For making sure logger configured properly
outputLoggerStatus();
-
- // Initialize the core now
+
+ // populate caches needs core!
+ // load now before its lazy-loaded under contention
createCore();
-
- // Start any optional modules.
+
+ if (CoreConfig.getFillHistoricalCaches()){
+ try {
+ populateCaches();
+ } catch (Exception e) {
+ logger.error("Failed to populate cache.", e);
+ }
+ }
+
+ // Close cache if shutting down.
+ Runtime.getRuntime().addShutdownHook(new Thread(new Runnable()
+ {
+ public void run()
+ {
+ try {
+ logger.info("Closing cache.");
+ CacheManagerFactory.getInstance().close();
+ logger.info("Cache closed.");
+ } catch (Exception e) {
+ logger.error("Cache close failed.");
+ logger.error(e.getMessage(),e);
+ }
+ System.exit(0);
+ }
+ }));
+
+
+ // Start any optional modules.
List optionalModuleNames = CoreConfig.getOptionalModules();
- if (optionalModuleNames.size() > 0)
- logger.info("Starting up optional modules specified via " +
- "transitime.modules.optionalModulesList param:");
- else
+ if (optionalModuleNames.size() > 0) {
+ logger.info("Starting up optional modules specified via " +
+ "transitclock.modules.optionalModulesList param:");
+ }
+ else {
logger.info("No optional modules to start up.");
+ }
+
for (String moduleName : optionalModuleNames) {
logger.info("Starting up optional module " + moduleName);
Module.start(moduleName);
- }
-
+ }
+
// Start the RMI Servers so that clients can obtain data
// on predictions, vehicles locations, etc.
- String agencyId = AgencyConfig.getAgencyId();
+ String agencyId = AgencyConfig.getAgencyId();
startRmiServers(agencyId);
} catch (Exception e) {
logger.error(e.getMessage(), e);
+ e.printStackTrace();
}
}
diff --git a/transitime/src/main/java/org/transitime/applications/CreateAPIKey.java b/transitclock/src/main/java/org/transitclock/applications/CreateAPIKey.java
similarity index 94%
rename from transitime/src/main/java/org/transitime/applications/CreateAPIKey.java
rename to transitclock/src/main/java/org/transitclock/applications/CreateAPIKey.java
index 6ee628a27..7e6fe6482 100644
--- a/transitime/src/main/java/org/transitime/applications/CreateAPIKey.java
+++ b/transitclock/src/main/java/org/transitclock/applications/CreateAPIKey.java
@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.applications;
+package org.transitclock.applications;
import java.io.PrintWriter;
@@ -24,9 +24,9 @@
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
-import org.transitime.config.ConfigFileReader;
-import org.transitime.db.webstructs.ApiKey;
-import org.transitime.db.webstructs.ApiKeyManager;
+import org.transitclock.config.ConfigFileReader;
+import org.transitclock.db.webstructs.ApiKey;
+import org.transitclock.db.webstructs.ApiKeyManager;
/**
* For creating a new API key.
diff --git a/transitclock/src/main/java/org/transitclock/applications/CreateWebAgency.java b/transitclock/src/main/java/org/transitclock/applications/CreateWebAgency.java
new file mode 100755
index 000000000..c84263e36
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/applications/CreateWebAgency.java
@@ -0,0 +1,57 @@
+/*
+ * 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 .
+ */
+
+package org.transitclock.applications;
+
+import org.transitclock.db.webstructs.WebAgency;
+
+public class CreateWebAgency {
+
+ /**
+ * For storing a web agency in the web database
+ *
+ * @param args
+ * agencyId = args[0]; hostName = args[1]; dbName = args[2];
+ * dbType = args[3]; dbHost = args[4]; dbUserName = args[5];
+ * dbPassword = args[6];
+ */
+ public static void main(String args[]) {
+ // Determine all the params
+ if (args.length <= 5) {
+ System.err.println("Specify params for the WebAgency: agencyId = args[0]; hostName = args[1]; dbName = args[1]; dbType = args[2]; dbHost = args[3]; dbUserName = args[4]; dbPassword = args[5];");
+ System.exit(-1);
+ }
+ String agencyId = args[0];
+ String hostName = args[1];
+ boolean active = true;
+ String dbName = args[2];
+ String dbType = args[3];
+ String dbHost = args[4];
+ String dbUserName = args[5];
+ String dbPassword = args[6];
+ // Name of database where to store the WebAgency object
+ String webAgencyDbName = "web";
+
+ // Create the WebAgency object
+ WebAgency webAgency = new WebAgency(agencyId, hostName, active, dbName,
+ dbType, dbHost, dbUserName, dbPassword);
+ System.out.println("Storing " + webAgency);
+
+ // Store the WebAgency
+ webAgency.store(webAgencyDbName);
+ }
+}
diff --git a/transitime/src/main/java/org/transitime/applications/DbTest.java b/transitclock/src/main/java/org/transitclock/applications/DbTest.java
similarity index 91%
rename from transitime/src/main/java/org/transitime/applications/DbTest.java
rename to transitclock/src/main/java/org/transitclock/applications/DbTest.java
index 0d7eb0aca..94b3659b9 100644
--- a/transitime/src/main/java/org/transitime/applications/DbTest.java
+++ b/transitclock/src/main/java/org/transitclock/applications/DbTest.java
@@ -15,15 +15,15 @@
* along with Transitime.org . If not, see .
*/
-package org.transitime.applications;
+package org.transitclock.applications;
import java.util.Date;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.transitime.configData.DbSetupConfig;
-import org.transitime.db.structs.AvlReport;
+import org.transitclock.configData.DbSetupConfig;
+import org.transitclock.db.structs.AvlReport;
/**
*
diff --git a/transitime/src/main/java/org/transitime/applications/GtfsFileProcessor.java b/transitclock/src/main/java/org/transitclock/applications/GtfsFileProcessor.java
similarity index 89%
rename from transitime/src/main/java/org/transitime/applications/GtfsFileProcessor.java
rename to transitclock/src/main/java/org/transitclock/applications/GtfsFileProcessor.java
index 160ee4da7..c9ce48a43 100644
--- a/transitime/src/main/java/org/transitime/applications/GtfsFileProcessor.java
+++ b/transitclock/src/main/java/org/transitclock/applications/GtfsFileProcessor.java
@@ -14,7 +14,20 @@
* You should have received a copy of the GNU General Public License along with
* Transitime.org . If not, see .
*/
-package org.transitime.applications;
+package org.transitclock.applications;
+
+import org.apache.commons.cli.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.transitclock.config.ConfigFileReader;
+import org.transitclock.configData.AgencyConfig;
+import org.transitclock.gtfs.GtfsData;
+import org.transitclock.gtfs.HttpGetGtfsFile;
+import org.transitclock.gtfs.TitleFormatter;
+import org.transitclock.gtfs.gtfsStructs.GtfsAgency;
+import org.transitclock.gtfs.readers.GtfsAgencyReader;
+import org.transitclock.utils.Time;
+import org.transitclock.utils.Zip;
import java.io.File;
import java.io.PrintWriter;
@@ -24,25 +37,6 @@
import java.util.List;
import java.util.TimeZone;
-import org.apache.commons.cli.BasicParser;
-import org.apache.commons.cli.CommandLine;
-import org.apache.commons.cli.CommandLineParser;
-import org.apache.commons.cli.HelpFormatter;
-import org.apache.commons.cli.OptionBuilder;
-import org.apache.commons.cli.Options;
-import org.apache.commons.cli.ParseException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.transitime.config.ConfigFileReader;
-import org.transitime.configData.AgencyConfig;
-import org.transitime.gtfs.GtfsData;
-import org.transitime.gtfs.HttpGetGtfsFile;
-import org.transitime.gtfs.TitleFormatter;
-import org.transitime.gtfs.gtfsStructs.GtfsAgency;
-import org.transitime.gtfs.readers.GtfsAgencyReader;
-import org.transitime.utils.Time;
-import org.transitime.utils.Zip;
-
/**
* Reads GTFS files, validates and cleans up the data, stores the data into Java
* objects, and then stores those objects into the database.
@@ -62,6 +56,7 @@ public class GtfsFileProcessor {
private Date zipFileLastModifiedTime;
private final String unzipSubdirectory;
private String gtfsDirectoryName;
+ private String feedVersion;
// Optional command line info used for GtfsData class
private final String supplementDir;
@@ -74,12 +69,13 @@ public class GtfsFileProcessor {
private final double maxTravelTimeSegmentLength;
private final int configRev;
private final boolean shouldStoreNewRevs;
+ private final boolean shouldDeleteRevs;
private final String notes;
private final boolean trimPathBeforeFirstStopOfTrip;
// Read in configuration files. This should be done statically before
// the logback LoggerFactory.getLogger() is called so that logback can
- // also be configured using a transitime config file.
+ // also be configured using a transitclock config file.
static {
ConfigFileReader.processConfig();
}
@@ -92,7 +88,7 @@ public class GtfsFileProcessor {
/**
* Simple constructor. Stores the configurable parameters for this class.
- * Declared private since only used internally by createGtfsFileProcessor().
+ * In main project, only used by createGtfsFileProcessor().
*
* @param configFile
* @param notes
@@ -114,7 +110,7 @@ public class GtfsFileProcessor {
* If true then will store the new config and travel times revs
* into ActiveRevisions table in db
*/
- private GtfsFileProcessor(String configFile, String notes, String gtfsUrl,
+ public GtfsFileProcessor(String configFile, String notes, String gtfsUrl,
String gtfsZipFileName, String unzipSubdirectory,
String gtfsDirectoryName, String supplementDir,
String regexReplaceListFileName, double pathOffsetDistance,
@@ -123,7 +119,7 @@ private GtfsFileProcessor(String configFile, String notes, String gtfsUrl,
int defaultWaitTimeAtStopMsec, double maxSpeedKph,
double maxTravelTimeSegmentLength,
int configRev,
- boolean shouldStoreNewRevs, boolean trimPathBeforeFirstStopOfTrip) {
+ boolean shouldStoreNewRevs, boolean shouldDeleteRevs, boolean trimPathBeforeFirstStopOfTrip) {
// Read in config params if command line option specified
if (configFile != null) {
try {
@@ -152,6 +148,7 @@ private GtfsFileProcessor(String configFile, String notes, String gtfsUrl,
this.configRev = configRev;
this.notes = notes;
this.shouldStoreNewRevs = shouldStoreNewRevs;
+ this.shouldDeleteRevs = shouldDeleteRevs;
this.trimPathBeforeFirstStopOfTrip = trimPathBeforeFirstStopOfTrip;
}
@@ -284,13 +281,15 @@ public void process() throws IllegalArgumentException {
// Process the GTFS data
GtfsData gtfsData =
new GtfsData(configRev, notes, zipFileLastModifiedTime,
- shouldStoreNewRevs, AgencyConfig.getAgencyId(),
+ shouldStoreNewRevs, shouldDeleteRevs,
+ AgencyConfig.getAgencyId(),
gtfsDirectoryName, supplementDir,
pathOffsetDistance, maxStopToPathDistance,
maxDistanceForEliminatingVertices,
defaultWaitTimeAtStopMsec, maxSpeedKph,
maxTravelTimeSegmentLength,
trimPathBeforeFirstStopOfTrip, titleFormatter);
+
gtfsData.processData();
// Log possibly useful info
@@ -307,7 +306,7 @@ public void process() throws IllegalArgumentException {
*/
private static void displayCommandLineOptions(Options options) {
// Display help
- final String commandLineSyntax = "java transitime.jar";
+ final String commandLineSyntax = "java transitclock.jar";
final PrintWriter writer = new PrintWriter(System.out);
final HelpFormatter helpFormatter = new HelpFormatter();
helpFormatter.printHelp(writer, 80, // printedRowWidth
@@ -416,6 +415,7 @@ private static GtfsFileProcessor createGtfsFileProcessor(
// Handle boolean command line options
boolean shouldStoreNewRevs = commandLineArgs.hasOption("storeNewRevs");
+ boolean shouldDeleteRevs = !commandLineArgs.hasOption("skipDeleteRevs");
boolean trimPathBeforeFirstStopOfTrip =
commandLineArgs.hasOption("trimPathBeforeFirstStopOfTrip");
@@ -429,7 +429,8 @@ private static GtfsFileProcessor createGtfsFileProcessor(
defaultWaitTimeAtStopMsec, maxSpeedKph,
maxTravelTimeSegmentLength,
configRev,
- shouldStoreNewRevs, trimPathBeforeFirstStopOfTrip);
+ shouldStoreNewRevs, shouldDeleteRevs,
+ trimPathBeforeFirstStopOfTrip);
return processor;
}
@@ -592,6 +593,9 @@ CommandLine processCommandLineOptions(String[] args) {
"Stores the config and travel time revs into ActiveRevisions "
+ "in database.");
+ options.addOption("skipDeleteRevs", false,
+ "Delete the rev to be created first just in case.");
+
options.addOption(
"trimPathBeforeFirstStopOfTrip",
false,
@@ -599,6 +603,11 @@ CommandLine processCommandLineOptions(String[] args) {
+ "stops of trips. Useful for when the shapes have problems "
+ "at the beginning, which is suprisingly common.");
+ options.addOption(
+ "integrationTest",
+ false,
+ "Flag to indicate whether import is being run as part of integration test");
+
// Parse the options
CommandLineParser parser = new BasicParser();
CommandLine cmd = null;
@@ -641,12 +650,19 @@ public static void main(String[] args) {
try {
processor.process();
} catch (Exception e) {
- logger.error(e.getMessage());
+ logger.error(e.getMessage(), e);
}
// Found that when running on AWS that program never terminates,
// probably because still have db threads running. Therefore
// using exit() to definitely end the process.
- System.exit(0);
+ String integrationTest = System.getProperty("transitclock.core.integrationTest");
+ if(integrationTest != null){
+ logger.info("GTFS import complete for integration test");
+ System.setProperty("transitclock.core.gtfsImported","true");
+ }else{
+ System.exit(0);
+ }
+
}
}
diff --git a/transitclock/src/main/java/org/transitclock/applications/LoadTrafficSensors.java b/transitclock/src/main/java/org/transitclock/applications/LoadTrafficSensors.java
new file mode 100644
index 000000000..0d6917242
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/applications/LoadTrafficSensors.java
@@ -0,0 +1,730 @@
+/*
+ * 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 .
+ */
+package org.transitclock.applications;
+
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.locationtech.jts.geom.Coordinate;
+import org.locationtech.jts.geom.LineSegment;
+import org.locationtech.jts.operation.overlay.snap.LineStringSnapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.transitclock.config.ConfigFileReader;
+import org.transitclock.config.DoubleConfigValue;
+import org.transitclock.config.IntegerConfigValue;
+import org.transitclock.config.StringConfigValue;
+import org.transitclock.configData.AgencyConfig;
+import org.transitclock.db.hibernate.HibernateUtils;
+import org.transitclock.db.structs.ActiveRevisions;
+import org.transitclock.db.structs.Location;
+import org.transitclock.db.structs.StopPath;
+import org.transitclock.db.structs.TrafficPath;
+import org.transitclock.db.structs.TrafficSensor;
+import org.transitclock.traffic.FeatureData;
+import org.transitclock.traffic.FeatureGeometry;
+import org.transitclock.traffic.TrafficWriter;
+import org.transitclock.utils.Geo;
+import org.transitclock.utils.JsonUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+
+/**
+ * Load in traffic sensors shapes, snap to stop paths,
+ * then flush to database as TrafficSensors and TrafficPaths.
+ */
+public class LoadTrafficSensors {
+
+ private static final StringConfigValue TRAFFIC_URL
+ = new StringConfigValue("transitclock.traffic.shapeUrl",
+ "https://pulse-io.blyncsy.com/geoservices/project_route_data/rest/services/81/FeatureServer/0/query?f=json&returnGeometry=true",
+ "URL of traffic sensor shapes");
+
+ // increase the tolerance to include the stops that are off centerline but included in stop shapes
+ private static final double DEFAULT_SNAP_TOLERANCE = 0.00006; /* times 110996 per degree = 6 * 1.109964513599842 m */
+ private static final DoubleConfigValue SNAP_TOLERANCE
+ = new DoubleConfigValue("transitclock.traffic.snapTolerance", DEFAULT_SNAP_TOLERANCE,
+ "Distance in degrees two shapes can be considered for snapping. Smaller is more precise");
+
+ protected static final double DEFAULT_MIN_SNAP_SCORE = 0.5; // 50% match of sensor to segment
+ protected static final DoubleConfigValue MIN_SNAP_SCORE = new DoubleConfigValue("transitclock.traffic.minSnapScore",
+ 0.5,
+ "percentage of snapped shape that needs to match before being considered an overall match");
+
+ private static final int DEFAULT_MAX_GAP_SIZE = 2;
+ private static final IntegerConfigValue MAX_GAP_SIZE = new IntegerConfigValue("transitclock.traffic.maxGapSize",
+ DEFAULT_MAX_GAP_SIZE,
+ "max difference in segment indexes of traffic shape, " +
+ "used to detect loops and other possible bad matches");
+
+ private static final int DEFAULT_MIN_GAP_SIZE = 0;
+ private static final IntegerConfigValue MIN_GAP_SIZE = new IntegerConfigValue("transitclock.traffic.minGapSize",
+ DEFAULT_MIN_GAP_SIZE,
+ "min difference in segment indexes of traffic shape");
+
+ private static final int DEFAULT_MIN_STOP_SEGMENT_LENGTH_FOR_GAP_CHECK = 4;
+ private static final IntegerConfigValue MIN_STOP_SEGMENT_LENGTH_FOR_GAP_CHECK
+ = new IntegerConfigValue("transitclock.traffic.MinStopSegmentLengthForGapCheck",
+ DEFAULT_MIN_STOP_SEGMENT_LENGTH_FOR_GAP_CHECK,
+ "Minimum number of segments in stop path to apply gap check algorithmn to");
+ private static final int MAX_OPPOSING_DEGREES = 120;
+
+ protected String getTrafficUrl() { return TRAFFIC_URL.getValue(); }
+ private double getSnapTolerance() { return SNAP_TOLERANCE.getValue(); }
+ private double getMinSnapScore() { return MIN_SNAP_SCORE.getValue();}
+ private int getMaxGapSize() { return MAX_GAP_SIZE.getValue(); }
+ private int getMinGapSize() { return MIN_GAP_SIZE.getValue(); }
+ private int getMinStopSegmentLengthForGapCheck() { return MIN_STOP_SEGMENT_LENGTH_FOR_GAP_CHECK.getValue();}
+
+ static {
+ ConfigFileReader.processConfig();
+ }
+
+ private static final Logger logger =
+ LoggerFactory.getLogger(LoadTrafficSensors.class);
+
+ private String agencyId = null;
+ private Integer configRev = null;
+ // maintain a list of sensors retrieved for later storage in database
+ private List sensors = new ArrayList<>();
+ // our database session
+ private Session session;
+ private Integer trafficRev = null;
+ // highest snapping score statistic
+ private double highScore = -1.0;
+ // count of most stopPats attached to a single traffic sensor
+ private int mostMatches = 0;
+ // track stop paths to traffic paths independent of shape
+ private Set hashOfJoins = new HashSet<>();
+
+ /**
+ * public entry point into object.
+ */
+ public void run() {
+ agencyId = AgencyConfig.getAgencyId();
+ String url = getTrafficUrl();
+ List featureDataList = null;
+ // carefully manage a single session OLD SCHOOL STYLE!
+ session = HibernateUtils.getSession(agencyId);
+ Transaction tx = null;
+
+ try {
+
+ // Put db access into a transaction
+ tx = session.beginTransaction();
+ TrafficWriter writer = new TrafficWriter(session, getTrafficRev());
+
+ // our traffic sensor data including shapes
+ featureDataList = loadFeatureDataFromURL(url);
+
+ // perform snapping of traffic sensor shapes to StopPaths
+ mapFeaturesToStopPaths(featureDataList);
+
+ // create database structs representing the above data
+ List trafficPaths = createTrafficPaths(featureDataList);
+
+ logger.info("writing {} sensors to database", sensors.size());
+ writer.writeSensors(sensors);
+
+ logger.info("writing {} traffic paths to database", trafficPaths.size());
+ writer.writeTrafficPaths(trafficPaths);
+
+ logger.info("Flushing data to database...");
+ session.flush();
+ logger.info("Done flushing");
+
+ logger.info("committing transaction....");
+ tx.commit();
+ logger.info("commit complete!");
+
+ } catch (Exception any) {
+ logger.error("run failed", any);
+ Throwable rootCause = HibernateUtils.getRootCause(any);
+ logger.error("root cause", rootCause);
+
+ if (tx != null)
+ tx.rollback();
+ } finally {
+ if (session != null) {
+ session.close();
+ }
+ }
+ }
+
+
+ // parse JSON into a POJO, later it will be become a TrafficSensor struct
+ private FeatureData parseFeatureData(JSONObject o) {
+ JSONObject a = o.getJSONObject("attributes");
+ long time = a.getLong("time");
+ String label = a.getString("label");
+ String externalId = String.valueOf(a.getInt("id"));
+ FeatureGeometry fg = parseFeatureGeometry(o.getJSONObject("geometry"));
+ FeatureData fd = new FeatureData();
+ fd.setTime(time);
+ fd.setLabel(label);
+ fd.setId(externalId);
+ fd.setFeatureGeometry(fg);
+ fd.setLength((float) calculateLengthInMeters(fg));
+ return fd;
+ }
+
+ // calculate the length of a shape
+ double calculateLengthInMeters(FeatureGeometry fg) {
+ Coordinate[] points = fg.getAsCoordinateArray();
+ return calculateLengthInMeters(points);
+ }
+
+ double calculateLengthInMeters(Coordinate[] points) {
+ double length = 0.0;
+ for (int i = 1; i< points.length; i++) {
+ length += Geo.distanceHaversine(toLocation(points[i-1]),
+ toLocation(points[i]));
+ }
+ return length;
+ }
+
+ // parse the shape into a POJO for later snapping
+ private FeatureGeometry parseFeatureGeometry(JSONObject geometry) {
+ JSONArray paths = geometry.getJSONArray("paths");
+ // we have an anonymous array -- we only support the first path
+ JSONArray perPaths = paths.getJSONArray(0);
+ FeatureGeometry fg = new FeatureGeometry();
+ for (int i = 0; i < perPaths.length(); i++) {
+ // another anonymous array
+ JSONArray coordinate = perPaths.getJSONArray(i);
+ double lat = coordinate.getDouble(1);
+ double lon = coordinate.getDouble(0);
+ fg.addLatLon(lat, lon);
+ }
+ return fg;
+ }
+
+ // call out to webservice and parse into POJOs
+ List loadFeatureDataFromURL(String urlStr)
+ throws Exception {
+ List elements = new ArrayList<>();
+
+ logger.info("loading feature data from {}", urlStr);
+ URL urlObj = new URL(urlStr);
+ URLConnection connection = urlObj.openConnection();
+ InputStream in = connection.getInputStream();
+ String jsonStr = getJsonString(in);
+
+ JSONObject descriptor = new JSONObject(jsonStr);
+ JSONArray features = (JSONArray) descriptor.get("features");
+
+ logger.info("constructing {} feature elements", features.length());
+ for (int i=0; i featureDataList) {
+ List allStopPaths = loadStopPaths();
+ if (allStopPaths == null) {
+ logger.error("no StopPaths loaded, bailing");
+ return;
+ }
+ logger.info("mapping {} features to {} StopPaths.", featureDataList.size(), allStopPaths.size());
+ for (FeatureData fd : featureDataList) {
+ logger.info("mapping \"{}\"", fd.getLabel());
+ mapFeatureToStopPaths(fd, allStopPaths);
+ }
+ }
+
+ /**
+ * load the current set of StopPath structs into memory.
+ * @return
+ */
+ private List loadStopPaths() {
+ List allStopPaths = null;
+ try {
+ logger.info("loading all StopPaths for configRev {}", getConfigRev());
+ allStopPaths = StopPath.getPaths(session,
+ getConfigRev());
+ } finally {
+ if (allStopPaths != null) {
+ logger.info("StopPath loading complete, loaded {} elements", allStopPaths.size());
+ } else {
+ logger.info("StopPath loading failed. No elements present");
+ }
+ }
+ return allStopPaths;
+ }
+
+ private String getAgencyId() {
+ return agencyId;
+ }
+
+ /**
+ * lazy load the current configRev from the database.
+ * @return
+ */
+ private int getConfigRev() {
+ if (configRev == null) {
+ configRev = ActiveRevisions.get(getAgencyId()).getConfigRev();
+ }
+ return configRev;
+ }
+
+
+ /**
+ * attempt to snap the FeatureGeometries to the SnapPaths based on
+ * the configured tolerance. Score the snapping, as shape matching
+ * is ultimately a compromise.
+ * @param fd
+ * @param allStopPaths
+ * @return the highest score of the match for unit testing.
+ */
+ Double mapFeatureToStopPaths(FeatureData fd, List allStopPaths) {
+ // this is where some magic happens
+ // we have a traffic segment feature path, and a stop path geometry
+ // if they snap to each other then we assign
+ // that stopPath to the feature
+ Coordinate[] sensorLineString = toLineString(fd);
+ // LineStringSnapper from JTS helps us with the GIS snapping
+ LineStringSnapper snapper = new LineStringSnapper(sensorLineString, getSnapTolerance());
+ int sensorBearing = getBearing(sensorLineString);
+
+ // check all stop paths against this feature
+ for (StopPath sp : allStopPaths) {
+
+ if (fd.getStopPaths().contains(sp)) {
+ // whoops, we've already been here!
+ logger.warn("fd {} already has sp {}",
+ fd.getLabel(), sp.getId());
+ continue;
+ }
+
+ try {
+ Coordinate[] stopPathLineString = toLineString(sp);
+ /*
+ * Snap the StopPath shape to the Sensor shape. This is accomplished by snapping
+ * both vertices and edges. Thus the Matches array may be a mix of both vertices and
+ * edges.
+ * Note that while this is computationally expensive, the current dataset is both small
+ * and in memory and execute quickly relative to database operations.
+ */
+ Coordinate[] matches = snapper.snapTo(stopPathLineString);
+ // matches is now the combination of sensor and stopPath line strings that overlayed
+ // for our purposes we only want the stopPath matches for scoring
+ // so remove the input sensor coordinates
+ matches = removeSourcePoints(sensorLineString, matches);
+ // score the result filtering out certain conditions along the way
+ double score = scoreOverlay(sensorLineString, stopPathLineString, matches);
+ if (score > highScore) {
+ // log some stats to confirm proper tolerance configuration
+ logger.info("new high score of {} for traffic sensor \"{}\" and stop path \"{}\"",
+ score, fd.getLabel(), sp.getId());
+ highScore = score;
+ }
+ int stopPathBearing = getBearing(stopPathLineString);
+ if (score > getMinSnapScore() && Math.abs(sensorBearing - stopPathBearing) < MAX_OPPOSING_DEGREES) {
+ logger.info("match score {} for sensor {} to stop path {}",
+ score, fd.getLabel(), sp.getId());
+ if (logger.isDebugEnabled()) {
+ debugMatch(sensorLineString, stopPathLineString, matches);
+ }
+ fd.addStopPath(sp);
+ int matchesCount = fd.getStopPaths().size();
+ if (matchesCount > 1) {
+ if (matchesCount > mostMatches) {
+ mostMatches = matchesCount;
+ // another stat to consider -- how many stop paths are attached to the
+ // most popular sensor
+ logger.info("most matches {} for sensor {}", matchesCount, fd.getLabel());
+ if (logger.isDebugEnabled()) {
+ debugMatch(sensorLineString, stopPathLineString, matches);
+ }
+ }
+ logger.debug("multiple matches={} for sensor {} ", matchesCount, fd.getLabel());
+ }
+ } else {
+ if (logger.isDebugEnabled()) {
+ logger.debug("rejected for score " + score);
+ debugMatch(sensorLineString, stopPathLineString, matches);
+ }
+ }
+ } catch (Exception any) {
+ logger.error("exception adding stop path {}", any, any);
+ }
+
+ }
+ return highScore;
+ }
+
+ private int getBearing(Coordinate[] stopPathLineString) {
+ if (stopPathLineString.length < 2) return 0;
+ int j = stopPathLineString.length -1;
+ LineSegment lastSegment = new LineSegment(stopPathLineString[0].x,
+ stopPathLineString[0].y,
+ stopPathLineString[j].x,
+ stopPathLineString[j].y);
+
+ return (int) (lastSegment.angle() * (180/Math.PI));
+
+ }
+
+ /**
+ * Often route shapes contain the stops they pass by. Remove them from the
+ * match as they are "noise".
+ * @param sensorLineString
+ * @param matches
+ * @return
+ */
+ private Coordinate[] removeSourcePoints(Coordinate[] sensorLineString, Coordinate[] matches) {
+ ArrayList filtered = new ArrayList<>();
+ List sourceLineString = Arrays.asList(sensorLineString);
+ for (Coordinate overlayCoordinate : matches) {
+ if (!sourceLineString.contains(overlayCoordinate)) {
+ filtered.add(overlayCoordinate);
+ }
+ }
+ return filtered.toArray(new Coordinate[filtered.size()]);
+ }
+
+ private void debugJoin(TrafficPath tp, StopPath sp) {
+ debugLink(toLineString(tp), toLineString(sp), null);
+ }
+
+ /**
+ * log some info about the match for debugging/verification
+ * @param featureLineString
+ * @param stopPathLineString
+ * @param matches
+ */
+ void debugMatch(Coordinate[] featureLineString, Coordinate[] stopPathLineString, Coordinate[] matches) {
+ debugLink(featureLineString, stopPathLineString, matches);
+ debugShape("feature", featureLineString);
+ debugShape("stop path", stopPathLineString);
+ debugShape("matches", matches);
+ }
+
+ /**
+ * debug the shape of the sensor against the matching stop path
+ * points via an externally hosted web page.
+ * @param featureLineString
+ * @param stopPathLineString
+ * @param matches
+ */
+ void debugLink(Coordinate[] featureLineString, Coordinate[] stopPathLineString, Coordinate[] matches) {
+ StringBuffer sb = new StringBuffer();
+ sb.append("http://developer.onebusaway.org/maps/debug.html?polyline=");
+ for (Coordinate c : featureLineString) {
+ sb.append(c.x).append("%2C").append(c.y).append("%20");
+ }
+ sb.append("&points=");
+ for (Coordinate c : stopPathLineString) {
+ sb.append(c.x).append("%2C").append(c.y).append("%20");
+ }
+
+ logger.info("\n" + sb.toString() + "\n");
+ }
+
+ /**
+ * utility function to log shape
+ * @param shapeName
+ * @param shape
+ */
+ void debugShape(String shapeName, Coordinate[] shape) {
+
+ logger.info("{}:", shapeName);
+ StringBuffer sb = new StringBuffer();
+ sb.append("\n");
+ for (Coordinate c : shape) {
+ sb.append(c.x).append(",").append(c.y).append('\n');
+ }
+ logger.info(sb.toString());
+ }
+
+ // based on the inputs and matches determine a percentage score, the higher the score the better
+ // as part of scoring, down-score some special circumstances we don't want to consider as matches
+ double scoreOverlay(Coordinate[] sensorShape, Coordinate[] stopSegmentShape, Coordinate[] matches) {
+ if (matches.length <= 0) return 0.0;
+ // simple percentage calculation -- if all stopSegmentShapes match we get 1.0
+ double score = (double)matches.length / stopSegmentShape.length;
+ if (score >= getMinSnapScore()) {
+ int gapSize = countGapsInShape(sensorShape, stopSegmentShape, matches);
+ // only consider gaps if the stop segment is large enough to have them
+ if (stopSegmentShape.length > getMinStopSegmentLengthForGapCheck()) {
+ if (gapSize > getMaxGapSize()) {
+ logger.info("disqualifying match due to gap of {}", gapSize);
+ if (logger.isDebugEnabled()) {
+ debugMatch(sensorShape, stopSegmentShape, matches);
+ }
+ return gapSize * -1;
+ } else if (gapSize < getMinGapSize()) {
+ logger.info("disqualifying match due to back matches of {}", gapSize * -1);
+ if (logger.isDebugEnabled()) {
+ debugMatch(sensorShape, stopSegmentShape, matches);
+ }
+ return gapSize;
+ }
+ }
+ }
+ return score;
+ }
+
+ /**
+ * Check for gaps in the overlay matching, and count how many points failed.
+ * Multiple point gaps might indicate a
+ * loop or otherwise mismatch in shape that should disqualify the
+ * overlay. Gaps on the ends are fine and expected.
+ * @param source
+ * @param test
+ * @param matches
+ * @return
+ */
+ int countGapsInShape(Coordinate[] source, Coordinate[] test, Coordinate[] matches) {
+ // for each value in test that's in match, keep track of the index
+ // return the largest gap that is not the start or end
+ ArrayList indices = new ArrayList<>();
+ for (int i = 0; i < matches.length; i++) {
+ indices.add(findMatchInArray(test, matches[i]));
+ }
+
+ ArrayList diffs = new ArrayList<>();
+ // not compute the gap sizes between elements
+ // throw out start and end gaps
+ // 0,1,2,3,4,5 (GOOD)
+ // 0, 0, 0, 1,...,2,7,8....,9,10 (BAD) = gap of 5 (2-7)
+ for (int i = 1; i < indices.size(); i++) {
+ diffs.add(indices.get(i) - indices.get(i-1));
+ }
+
+ int maxGap = 0;
+ int gapCount = 0;
+ for (int i = 0; i < diffs.size(); i++) {
+ if (diffs.get(i) > maxGap) {
+ maxGap = diffs.get(i);
+ } else if (diffs.get(i) < -1) {
+ // we backtracked on the shape by more than one point, record that as well
+ // we occasionally swap points as the shape climbs the stop
+ gapCount++;
+ }
+ }
+ if (gapCount > 1)
+ return gapCount * -1;
+ return maxGap;
+ }
+
+ /**
+ * test if the exact coordinate exists in the array. Used to remove
+ * stops from stop shapes that are noise to the snapping algorithm
+ * @param test
+ * @param match
+ * @return
+ */
+ private Integer findMatchInArray(Coordinate[] test, Coordinate match) {
+ double closestMatch = Double.POSITIVE_INFINITY;
+ int closestIndex = -1;
+
+ for (int i = 0; i < test.length; i++) {
+ double distanceAways = Geo.distanceHaversine(toLocation(match),
+ toLocation(test[i]));
+ if (distanceAways < closestMatch) {
+ closestMatch = distanceAways;
+ closestIndex = i;
+ }
+
+ }
+ return closestIndex;
+ }
+
+
+ private Location toLocation(Coordinate match) {
+ return new Location(match.x, match.y);
+ }
+
+ Coordinate[] toLineString(FeatureData fd) {
+ return fd.getFeatureGeometry().getAsCoordinateArray();
+ }
+
+ Coordinate[] toLineString(TrafficPath tp) {
+ ArrayList list = new ArrayList<>();
+
+ for (Location l : tp.getLocations()) {
+ list.add(new Coordinate(l.getLat(), l.getLon()));
+ }
+ return list.toArray(new Coordinate[list.size()]);
+ }
+
+ Coordinate[] toLineString(StopPath sp) {
+ ArrayList list = new ArrayList<>();
+
+ for (Location l : sp.getLocations()) {
+ list.add(new Coordinate(l.getLat(), l.getLon()));
+ }
+ return list.toArray(new Coordinate[list.size()]);
+ }
+
+ Coordinate[] toLineString(double[] trafficSensorLats, double[] trafficSensorLons) {
+ ArrayList list = new ArrayList<>();
+ if (trafficSensorLats == null || trafficSensorLons == null) return null;
+ if (trafficSensorLats.length != trafficSensorLons.length)
+ throw new IllegalStateException("unequal length inputs");
+
+ for (int i = 0; i createTrafficPaths(List featureDataList) {
+ List trafficPaths = new ArrayList<>();
+ for (FeatureData fd : featureDataList) {
+ TrafficPath path = createTrafficPath(fd);
+ trafficPaths.add(path);
+ createTrafficSensor(fd, path);
+ }
+ return trafficPaths;
+ }
+
+ private TrafficPath createTrafficPath(FeatureData fd) {
+ logger.info("creating TrafficPath({}, {}) for {}", fd.getId(),
+ getTrafficRev(),
+ fd.getLabel());
+ // current convention of using natural keys (not a great convention)
+ TrafficPath tp = new TrafficPath(fd.getId(),
+ getTrafficRev(),
+ fd.getLength()
+ );
+
+ tp.setLocations(new ArrayList());
+
+ for (Coordinate c :fd.getFeatureGeometry().getAsCoordinateArray()) {
+ tp.getLocations().add(new Location(c.x, c.y));
+ }
+
+ for (StopPath sp : fd.getStopPaths()) {
+ joinStopPathToTrafficPath(tp, sp);
+ }
+
+ return tp;
+ }
+
+
+ private void joinStopPathToTrafficPath(TrafficPath tp, StopPath sp) {
+ String hash = hash(tp, sp);
+ if (!hashOfJoins.contains(hash)) {
+ debugJoin(tp, sp);
+ hashOfJoins.add(hash);
+ }
+ logger.info("joining {}:{} to {}:{}:{}",
+ tp.getTrafficPathId(), tp.getTrafficRev(),
+ sp.getTripPatternId(),
+ sp.getId(),
+ sp.getConfigRev());
+ tp.getStopPaths().add(sp);
+ }
+
+ private String hash(TrafficPath tp, StopPath sp) {
+ return tp.getTrafficPathId() + "."
+ + tp.getTrafficRev() + "."
+ + sp.getId() + "."
+ + sp.getConfigRev();
+ }
+
+ private TrafficSensor createTrafficSensor(FeatureData featureData, TrafficPath trafficPath) {
+ logger.info("creating TrafficSensor({}, {}) for {}",
+ featureData.getId(),
+ getTrafficRev(),
+ featureData.getLabel());
+ TrafficSensor sensor = new TrafficSensor(featureData.getId(),
+ getTrafficRev(),
+ featureData.getId(),
+ trafficPath.getTrafficPathId());
+ sensor.setDescription(featureData.getLabel());
+ sensors.add(sensor);
+ return sensor;
+ }
+
+
+ /**
+ * lazy load (and increment) the trafficRev considering it may be null
+ * @return
+ */
+ private Integer getTrafficRev() {
+ if (trafficRev == null) {
+ ActiveRevisions activeRevisions = ActiveRevisions.get(session);
+ if (activeRevisions.getTrafficRev() == null) {
+ // first use, set it to 0
+ trafficRev = 0;
+ } else {
+ trafficRev = activeRevisions.getTrafficRev() + 1;
+ }
+ activeRevisions.setTrafficRev(trafficRev);
+ }
+
+ return trafficRev;
+ }
+
+
+ /**
+ * entry point into Application. Currently accepts no arguments
+ * as all configuration happens via config file infrastructure.
+ * @param args
+ */
+ public static void main(String[] args) {
+
+ LoadTrafficSensors loader = new LoadTrafficSensors();
+ loader.run();
+ logger.info("LoadTrafficSensors exiting!");
+ // daemon threads running in background, we need to
+ // exist in a less graceful fashion
+ System.exit(0);
+ }
+
+
+}
diff --git a/transitime/src/main/java/org/transitime/applications/OffsetGtfsLocsForChina.java b/transitclock/src/main/java/org/transitclock/applications/OffsetGtfsLocsForChina.java
similarity index 95%
rename from transitime/src/main/java/org/transitime/applications/OffsetGtfsLocsForChina.java
rename to transitclock/src/main/java/org/transitclock/applications/OffsetGtfsLocsForChina.java
index c53707e7e..b0b85d38b 100644
--- a/transitime/src/main/java/org/transitime/applications/OffsetGtfsLocsForChina.java
+++ b/transitclock/src/main/java/org/transitclock/applications/OffsetGtfsLocsForChina.java
@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.applications;
+package org.transitclock.applications;
import java.util.List;
import java.io.File;
@@ -26,13 +26,13 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.transitime.gtfs.gtfsStructs.GtfsShape;
-import org.transitime.gtfs.gtfsStructs.GtfsStop;
-import org.transitime.gtfs.readers.GtfsShapesReader;
-import org.transitime.gtfs.readers.GtfsStopsReader;
-import org.transitime.gtfs.writers.GtfsShapesWriter;
-import org.transitime.gtfs.writers.GtfsStopsWriter;
-import org.transitime.utils.ChinaGpsOffset;
+import org.transitclock.gtfs.gtfsStructs.GtfsShape;
+import org.transitclock.gtfs.gtfsStructs.GtfsStop;
+import org.transitclock.gtfs.readers.GtfsShapesReader;
+import org.transitclock.gtfs.readers.GtfsStopsReader;
+import org.transitclock.gtfs.writers.GtfsShapesWriter;
+import org.transitclock.gtfs.writers.GtfsStopsWriter;
+import org.transitclock.utils.ChinaGpsOffset;
/**
* Maps are offset for China such that when GTFS data is plotted it is not
diff --git a/transitimeApi/src/main/java/org/transitime/applications/RmiQuery.java b/transitclock/src/main/java/org/transitclock/applications/RmiQuery.java
similarity index 79%
rename from transitimeApi/src/main/java/org/transitime/applications/RmiQuery.java
rename to transitclock/src/main/java/org/transitclock/applications/RmiQuery.java
index b81173162..7b90ffeb3 100644
--- a/transitimeApi/src/main/java/org/transitime/applications/RmiQuery.java
+++ b/transitclock/src/main/java/org/transitclock/applications/RmiQuery.java
@@ -14,11 +14,8 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.applications;
+package org.transitclock.applications;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
import java.io.PrintWriter;
import java.rmi.RemoteException;
import java.util.ArrayList;
@@ -33,29 +30,27 @@
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
-import org.transitime.db.structs.Location;
-import org.transitime.api.gtfsRealtime.GtfsRtTripFeed;
-import org.transitime.api.gtfsRealtime.GtfsRtVehicleFeed;
-import org.transitime.feed.gtfsRt.OctalDecoder;
-import org.transitime.ipc.clients.ConfigInterfaceFactory;
-import org.transitime.ipc.clients.PredictionsInterfaceFactory;
-import org.transitime.ipc.clients.VehiclesInterfaceFactory;
-import org.transitime.ipc.data.IpcBlock;
-import org.transitime.ipc.data.IpcActiveBlock;
-import org.transitime.ipc.data.IpcVehicleComplete;
-import org.transitime.ipc.data.IpcPredictionsForRouteStopDest;
-import org.transitime.ipc.data.IpcRoute;
-import org.transitime.ipc.data.IpcRouteSummary;
-import org.transitime.ipc.data.IpcDirectionsForRoute;
-import org.transitime.ipc.data.IpcTrip;
-import org.transitime.ipc.data.IpcTripPattern;
-import org.transitime.ipc.data.IpcVehicle;
-import org.transitime.ipc.interfaces.ConfigInterface;
-import org.transitime.ipc.interfaces.PredictionsInterface;
-import org.transitime.ipc.interfaces.PredictionsInterface.RouteStop;
-import org.transitime.ipc.interfaces.VehiclesInterface;
-
-import com.google.transit.realtime.GtfsRealtime.FeedMessage;
+import org.transitclock.config.ConfigFileReader;
+import org.transitclock.db.structs.Location;
+import org.transitclock.ipc.clients.CommandsInterfaceFactory;
+import org.transitclock.ipc.clients.ConfigInterfaceFactory;
+import org.transitclock.ipc.clients.PredictionsInterfaceFactory;
+import org.transitclock.ipc.clients.VehiclesInterfaceFactory;
+import org.transitclock.ipc.data.IpcActiveBlock;
+import org.transitclock.ipc.data.IpcBlock;
+import org.transitclock.ipc.data.IpcDirectionsForRoute;
+import org.transitclock.ipc.data.IpcPredictionsForRouteStopDest;
+import org.transitclock.ipc.data.IpcRoute;
+import org.transitclock.ipc.data.IpcRouteSummary;
+import org.transitclock.ipc.data.IpcTrip;
+import org.transitclock.ipc.data.IpcTripPattern;
+import org.transitclock.ipc.data.IpcVehicle;
+import org.transitclock.ipc.data.IpcVehicleComplete;
+import org.transitclock.ipc.interfaces.CommandsInterface;
+import org.transitclock.ipc.interfaces.ConfigInterface;
+import org.transitclock.ipc.interfaces.PredictionsInterface;
+import org.transitclock.ipc.interfaces.VehiclesInterface;
+import org.transitclock.ipc.interfaces.PredictionsInterface.RouteStop;
/**
* A command line application that allows user to request data from the
@@ -78,9 +73,13 @@ public class RmiQuery {
private static String serviceId;
private static String tripId;
+ static {
+ ConfigFileReader.processConfig();
+ }
+
private static enum Command {NOT_SPECIFIED, GET_PREDICTIONS, GET_VEHICLES,
- GET_ROUTE_CONFIG, GET_CONFIG, GET_GTFS_RT_VEHICLES, GET_GTFS_RT_TRIPS,
- GET_ACTIVE_BLOCKS};
+ GET_ROUTE_CONFIG, GET_CONFIG,
+ GET_ACTIVE_BLOCKS, RESET_VEHICLE};
/********************** Member Functions **************************/
@@ -113,7 +112,7 @@ private static CommandLine processCommandLineOptions(String[] args) {
.isRequired()
.withDescription("Name of command to execute. Can be "
+ "\"preds\", \"vehicles\", \"routeConfig\", \"config\", "
- + "\"gtfsRtVehiclePositions\", \"gtfsRtTripUpdates\", "
+ + "\"resetVehicle\", "
+ "or \"activeBlocks\" .")
.create("c")
);
@@ -193,13 +192,11 @@ else if ("vehicles".equals(commandStr))
else if ("routeConfig".equals(commandStr))
command = Command.GET_ROUTE_CONFIG;
else if ("config".equals(commandStr))
- command = Command.GET_CONFIG;
- else if ("gtfsRtVehiclePositions".equals(commandStr))
- command = Command.GET_GTFS_RT_VEHICLES;
- else if ("gtfsRtTripUpdates".equals(commandStr))
- command = Command.GET_GTFS_RT_TRIPS;
+ command = Command.GET_CONFIG;
else if ("activeBlocks".equals(commandStr))
command = Command.GET_ACTIVE_BLOCKS;
+ else if ("resetVehicle".equals(commandStr))
+ command = Command.RESET_VEHICLE;
else {
System.out.println("Command \"" + commandStr + "\" is not valid.\n");
displayCommandLineOptionsAndExit(options);
@@ -234,7 +231,7 @@ else if ("activeBlocks".equals(commandStr))
*/
private static void displayCommandLineOptionsAndExit(Options options) {
// Display help
- final String commandLineSyntax = "java transitime.jar";
+ final String commandLineSyntax = "java transitclock.jar";
final PrintWriter writer = new PrintWriter(System.out);
writer.append(
"A command line application that allows user to request data\n" +
@@ -441,58 +438,9 @@ private static void getActiveBlocks() throws RemoteException {
}
}
- /**
- * Outputs in human readable format current snapshot of vehicle positions.
- */
- private static void getGtfsRtVehiclesPositions() {
- GtfsRtVehicleFeed feed = new GtfsRtVehicleFeed(agencyId);
- FeedMessage message = feed.createMessage();
-
- // Output data in human readable format. First, convert
- // the octal escaped message to regular UTF encoding.
- String decodedMessage =
- OctalDecoder.convertOctalEscapedString(message.toString());
-
- // Write message out to stdout
- System.out.println(decodedMessage);
-
- String fileName = "gtfsRtVehiclePositions";
- System.out.println("\nWriting GTFS-RT vehicle positions file to " + fileName);
- try {
- OutputStream outputStream = new FileOutputStream(fileName);
- message.writeTo(outputStream);
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- }
- /**
- * Outputs in human readable format current snapshot of trip updates,
- * which contains all the prediction information.
- */
- private static void getGtfsRtTripUpdates() {
- GtfsRtTripFeed feed = new GtfsRtTripFeed(agencyId);
- FeedMessage message = feed.createMessage();
-
- // Output data in human readable format. First, convert
- // the octal escaped message to regular UTF encoding.
- String decodedMessage =
- OctalDecoder.convertOctalEscapedString(message.toString());
-
- // Write message out to stdout
- System.out.println(decodedMessage);
-
- // Write data to binary file
- String fileName = "gtfsRtTripUpdate";
- System.out.println("\nWriting GTFS-RT trip update file to " + fileName);
- try {
- OutputStream outputStream = new FileOutputStream(fileName);
- message.writeTo(outputStream);
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
+
+
/**
* @param args
@@ -509,17 +457,30 @@ public static void main(String[] args) {
getRouteConfig();
} else if (command == Command.GET_CONFIG) {
getConfig();
- } else if (command == Command.GET_GTFS_RT_VEHICLES) {
- getGtfsRtVehiclesPositions();
- } else if (command == Command.GET_GTFS_RT_TRIPS) {
- getGtfsRtTripUpdates();
} else if (command == Command.GET_ACTIVE_BLOCKS) {
getActiveBlocks();
+ } else if(command == Command.RESET_VEHICLE) {
+ resetVehicles();
}
- } catch (RemoteException e) {
+ } catch (Exception e) {
// Output stack trace as error message
e.printStackTrace();
}
}
+ private static void resetVehicles() throws Exception {
+ CommandsInterface commandsInterface =
+ CommandsInterfaceFactory.get(agencyId);
+ if(commandsInterface != null)
+ {
+ for(String vehicleId:vehicleIds)
+ {
+ commandsInterface.setVehicleUnpredictable(vehicleId);
+ }
+ }else
+ {
+ throw new Exception("Cannot connect to Command server.");
+ }
+ }
+
}
diff --git a/transitime/src/main/java/org/transitime/applications/ScheduleGenerator.java b/transitclock/src/main/java/org/transitclock/applications/ScheduleGenerator.java
similarity index 93%
rename from transitime/src/main/java/org/transitime/applications/ScheduleGenerator.java
rename to transitclock/src/main/java/org/transitclock/applications/ScheduleGenerator.java
index 47bee840b..d7cf221fd 100644
--- a/transitime/src/main/java/org/transitime/applications/ScheduleGenerator.java
+++ b/transitclock/src/main/java/org/transitclock/applications/ScheduleGenerator.java
@@ -14,27 +14,21 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.applications;
+package org.transitclock.applications;
+
+import org.apache.commons.cli.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.transitclock.config.ConfigFileReader;
+import org.transitclock.configData.AgencyConfig;
+import org.transitclock.gtfs.readers.GtfsAgencyReader;
+import org.transitclock.statistics.ScheduleDataProcessor;
+import org.transitclock.utils.Time;
import java.io.PrintWriter;
import java.util.Date;
import java.util.TimeZone;
-import org.apache.commons.cli.BasicParser;
-import org.apache.commons.cli.CommandLine;
-import org.apache.commons.cli.CommandLineParser;
-import org.apache.commons.cli.HelpFormatter;
-import org.apache.commons.cli.OptionBuilder;
-import org.apache.commons.cli.Options;
-import org.apache.commons.cli.ParseException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.transitime.config.ConfigFileReader;
-import org.transitime.configData.AgencyConfig;
-import org.transitime.gtfs.readers.GtfsAgencyReader;
-import org.transitime.statistics.ScheduleDataProcessor;
-import org.transitime.utils.Time;
-
/**
* For generating more accurate schedule times for GTFS trips.txt file by
* using departure data obtained via GPS.
@@ -59,8 +53,8 @@ public class ScheduleGenerator {
// Read in configuration files. This should be done statically before
// the logback LoggerFactory.getLogger() is called so that logback can
- // also be configured using a transitime config file. The files are
- // specified using the java system property -Dtransitime.configFiles .
+ // also be configured using a transitclock config file. The files are
+ // specified using the java system property -Dtransitclock.configFiles .
static {
ConfigFileReader.processConfig();
}
@@ -78,7 +72,7 @@ public class ScheduleGenerator {
*/
private static void displayCommandLineOptions(Options options) {
// Display help
- final String commandLineSyntax = "java transitime.jar";
+ final String commandLineSyntax = "java transitclock.jar";
final PrintWriter writer = new PrintWriter(System.out);
final HelpFormatter helpFormatter = new HelpFormatter();
helpFormatter.printHelp(writer,
@@ -91,7 +85,7 @@ private static void displayCommandLineOptions(Options options) {
null, // footer
true); // displayUsage
writer.write("Also need to set VM parameters: \n" +
- " -Dtransitime.core.agencyId=\n");
+ " -Dtransitclock.core.agencyId=\n");
writer.close();
}
@@ -211,7 +205,7 @@ private static CommandLine processCommandLineOptions(String[] args) {
System.exit(0);
}
- // Get project ID from VM param transitime.core.agencyId
+ // Get project ID from VM param transitclock.core.agencyId
agencyId = AgencyConfig.getAgencyId();
if (agencyId == null) {
displayCommandLineOptions(options);
diff --git a/transitime/src/main/java/org/transitime/applications/SchemaGenerator.java b/transitclock/src/main/java/org/transitclock/applications/SchemaGenerator.java
similarity index 98%
rename from transitime/src/main/java/org/transitime/applications/SchemaGenerator.java
rename to transitclock/src/main/java/org/transitclock/applications/SchemaGenerator.java
index 5aa13e5f5..9954ccb7b 100644
--- a/transitime/src/main/java/org/transitime/applications/SchemaGenerator.java
+++ b/transitclock/src/main/java/org/transitclock/applications/SchemaGenerator.java
@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.applications;
+package org.transitclock.applications;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -265,7 +265,7 @@ private List getClasses(String packageName) throws Exception {
private static enum Dialect {
ORACLE("org.hibernate.dialect.Oracle10gDialect"),
// Note that using special ImprovedMySqlDialect
- MYSQL("org.transitime.applications.SchemaGenerator$ImprovedMySQLDialect"),
+ MYSQL("org.transitclock.applications.SchemaGenerator$ImprovedMySQLDialect"),
POSTGRES("org.hibernate.dialect.PostgreSQLDialect"),
HSQL("org.hibernate.dialect.HSQLDialect");
@@ -283,13 +283,13 @@ public String getDialectClass() {
/**
* Param args args[0] is the package name for the Hibernate annotated
* classes whose schema is to be exported such as
- * "org.transitime.db.structs". args[1] is optional output directory where
+ * "org.transitclock.db.structs". args[1] is optional output directory where
* the resulting files are to go. If the optional output directory is not
* specified then schema files written to local directory.
*
* The resulting files have the name "ddl_" plus dialect name such as mysql
* or oracle plus the first two components of the package name such as
- * org_transitime.
+ * org_transitclock.
*/
public static void main(String[] args) throws Exception {
// Handle the command line options
@@ -348,3 +348,4 @@ public static void main(String[] args) throws Exception {
}
+
diff --git a/transitclock/src/main/java/org/transitclock/applications/TravelTimesReport.java b/transitclock/src/main/java/org/transitclock/applications/TravelTimesReport.java
new file mode 100644
index 000000000..077507278
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/applications/TravelTimesReport.java
@@ -0,0 +1,275 @@
+package org.transitclock.applications;
+
+import com.google.common.collect.ArrayListMultimap;
+import org.hibernate.Criteria;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.criterion.Restrictions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.transitclock.config.ConfigFileReader;
+import org.transitclock.config.IntegerConfigValue;
+import org.transitclock.configData.AgencyConfig;
+import org.transitclock.db.hibernate.HibernateUtils;
+import org.transitclock.db.structs.ActiveRevisions;
+import org.transitclock.db.structs.TravelTimesForStopPath;
+import org.transitclock.db.structs.TravelTimesForTrip;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * output some statistics on travel times. Correct the most eggregiosu via -Drepair=true
+ */
+public class TravelTimesReport {
+
+ private static final Logger logger =
+ LoggerFactory.getLogger(TravelTimesReport.class);
+
+ public static final String AUTO_CORRECT = "repair";
+
+ private static final String ZERO_STOP_PATH_LENGTH = "Zero Stop Path Length";
+ private static final String NEGATIVE_STOP_PATH_LENGTH = "Negative Stop Path Length";
+ private static final String LARGE_STOP_PATH_LENGTH = "Large Stop Path Length";
+ private static final String NEGATIVE_STOP_TIME = "Negative Stop Time";
+ private static final String LARGE_STOP_TIME = "Large Stop Time";
+
+ private static IntegerConfigValue MAX_STOP_PATH_TRAVEL_TIME = new IntegerConfigValue("maxStopPathTravelTime",
+ 20 * 60 * 1000,
+ "Logical maximum time it can take to traverse a segment");
+
+ private static IntegerConfigValue MAX_STOP_PATH_LENGTH = new IntegerConfigValue("maxStopPathLength",
+ 300,
+ "max expected segment length in meters");
+
+ private static IntegerConfigValue MAX_STOP_TIME = new IntegerConfigValue("maxStopTime",
+ 20 * 60 * 1000,
+ "max expected stop time");
+
+ private static final String EMPTY_STOP_PATHS = "Empty Stop Paths";
+ private static final String NULL_STOP_PATH_TRAVEL_TIME = "Null Stop Path Travel Time";
+ private static final String ZERO_STOP_PATH_TRAVEL_TIME = "Zero Stop Path Travel Time";
+ private static final String NEGATIVE_STOP_PATH_TRAVEL_TIME = "Negative Stop Path Travel Time";
+ private static final String LARGE_STOP_PATH_TRAVEL_TIME = "Large Stop Path Travel Time";
+
+ private String agencyId;
+ private int configRev;
+ private int travelTimesRev;
+ private int stopPathCount = 0;
+
+ public void setAgencyId(String id) {
+ this.agencyId = id;
+ }
+
+ public void setConfigRev(int revision) {
+ this.configRev = revision;
+ }
+
+ public void setTravelTimesRev(int rev) {
+ this.travelTimesRev = rev;
+ }
+
+ ArrayListMultimap errorsByType = ArrayListMultimap.create();
+ ArrayListMultimap errorsById = ArrayListMultimap.create();
+
+ public TravelTimesReport() {
+
+ }
+
+ public void run() {
+ Session session = HibernateUtils.getSession(agencyId);
+ Transaction tx = null;
+ try {
+ tx = session.beginTransaction();
+ List travelTimesForTrips = loadTravelTimes(session);
+ addSystemStat("Total Travel Times", travelTimesForTrips.size());
+ boolean dirty = processTravelTimes(travelTimesForTrips);
+ addSystemStat("Total Stop Paths", stopPathCount);
+ //logger.info("processed {} stop paths", stopPathCount);
+ logResults();
+ if (dirty) {
+ logger.info("Flushing data to database...");
+ session.flush();
+ logger.info("Done flushing");
+ }
+ tx.commit();
+ } catch (Exception e) {
+ if (tx != null)
+ tx.rollback();
+ logger.error("exception with transaction", e);
+ } finally {
+ session.close();
+ HibernateUtils.clearSessionFactory();
+ }
+ }
+
+ private void addSystemStat(String key, int value) {
+ logger.info("{},{},{},{},{}", "STAT", travelTimesRev, "SYSTEM", key, value);
+ }
+
+ private void logResults() {
+ Set errorTypes = this.errorsById.asMap().keySet();
+ for (String errorType : errorTypes) {
+ String howSet = errorType.split("_")[0];
+ String type = errorType.split("_")[1];
+ logger.info("{},{},{},{},{}", "STAT", travelTimesRev, howSet, type, errorsById.get(errorType).size());
+ }
+ }
+
+ private boolean processTravelTimes(List travelTimesForTrips) {
+ boolean dirty = false;
+ for (TravelTimesForTrip travelTimesForTrip : travelTimesForTrips) {
+ String howSet = getHowSet(travelTimesForTrip);
+ if (travelTimesForTrip.getTravelTimesForStopPaths().isEmpty()) {
+ addError(EMPTY_STOP_PATHS, travelTimesForTrip.getId(), howSet,
+ travelTimesForTrip.getId(), travelTimesForTrip.getConfigRev(), travelTimesForTrip.getTravelTimeRev());
+ continue;
+ }
+
+ TravelTimesForStopPath firstStopPath = travelTimesForTrip.getTravelTimesForStopPath(0);
+
+ dirty |= validateStopPath(firstStopPath, howSet);
+ stopPathCount++;
+ for (int stopPathIndex = 1; stopPathIndex < travelTimesForTrip.getTravelTimesForStopPaths().size(); stopPathIndex++ ) {
+ validateStopPath(travelTimesForTrip.getTravelTimesForStopPath(stopPathIndex), howSet);
+ stopPathCount++;
+ }
+ }
+ return dirty;
+ }
+
+ private String getHowSet(TravelTimesForTrip travelTimesForTrip) {
+ if (travelTimesForTrip == null)
+ return "unknown";
+ if (travelTimesForTrip.getTravelTimesForStopPaths().isEmpty())
+ return "unknown";
+ return travelTimesForTrip.getTravelTimesForStopPath(0).getHowSet().toString();
+ }
+
+ private boolean validateStopPath(TravelTimesForStopPath stopPath, String howSet) {
+ boolean dirty = false;
+
+ int id = stopPath.getInternalId();
+ String stopPathId = stopPath.getStopPathId();
+ int configRev = stopPath.getConfigRev();
+ int ttrev = stopPath.getTravelTimesRev();
+
+ if (stopPath.getTravelTimeSegmentLength() == 0.0)
+ addError(ZERO_STOP_PATH_LENGTH, stopPathId, howSet, id, configRev, ttrev);
+
+ if (stopPath.getTravelTimeSegmentLength() < 0)
+ addError(NEGATIVE_STOP_PATH_LENGTH, stopPathId, howSet, id, configRev, ttrev);
+
+ if (stopPath.getTravelTimeSegmentLength() > MAX_STOP_PATH_LENGTH.getValue())
+ addError(LARGE_STOP_PATH_LENGTH, stopPathId, howSet, id, configRev, ttrev);
+
+ if (stopPath.getStopTimeMsec() < 0)
+ addError(NEGATIVE_STOP_TIME, stopPathId, howSet, id, configRev, ttrev);
+
+ if (stopPath.getStopTimeMsec() > MAX_STOP_TIME.getValue()) {
+ addError(LARGE_STOP_TIME, stopPathId, howSet, id, configRev, ttrev);
+ }
+
+
+ int ttmIndex = 0;
+ List invalidTravelTimesIndicies = new ArrayList<>();
+ for (Integer ttm : stopPath.getTravelTimesMsec()) {
+ if (ttm == null) {
+ addError(NULL_STOP_PATH_TRAVEL_TIME, stopPath.getStopPathId(), howSet, id, configRev, ttrev);
+ continue;
+ }
+ if (ttm == 0)
+ addError(ZERO_STOP_PATH_TRAVEL_TIME, stopPath.getStopPathId(), howSet, id, configRev, ttrev);
+
+ if (ttm < 0)
+ addError(NEGATIVE_STOP_PATH_TRAVEL_TIME, stopPath.getStopPathId(), howSet, id, configRev, ttrev);
+
+ if (ttm > MAX_STOP_PATH_TRAVEL_TIME.getValue()) {
+ addError(LARGE_STOP_PATH_TRAVEL_TIME, stopPath.getStopPathId(), howSet, id, configRev, ttrev);
+ if (repair) {
+ invalidTravelTimesIndicies.add(ttmIndex);
+ }
+
+ }
+ ttmIndex++;
+ }
+
+ // TravelTimesForStopPath is final -- the only repair we can make is to the list of travel times
+ if (repair() && !invalidTravelTimesIndicies.isEmpty()) {
+ logger.error("ACTION,stopPath,{},travelTimes post,{}", stopPath.getStopPathId(), stopPath.getTravelTimesMsec());
+ for (Integer badIndex : invalidTravelTimesIndicies) {
+ dirty = true;
+ stopPath.getTravelTimesMsec().set(badIndex, 0);
+ }
+ logger.error("ACTION,stopPath,{},travelTimes post,{}", stopPath.getStopPathId(), stopPath.getTravelTimesMsec());
+ }
+ return dirty;
+ }
+
+ private void addError(String errorType, Object natrualKey, String howSet, Integer id, Integer configRev, Integer ttrev) {
+ if (ttrev != null)
+ logger.error("DETAILS,"+ howSet + "_" + errorType + "," + natrualKey + "," + id
+ + "," + configRev + "," + ttrev);
+ this.errorsById.put(howSet + "_" + errorType, id);
+ }
+
+ private List loadTravelTimes(Session session) {
+ List allTravelTimes = session.createCriteria(TravelTimesForTrip.class)
+ .add(Restrictions.eq("travelTimesRev", travelTimesRev))
+ .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY).list();
+ return allTravelTimes;
+ }
+
+ Boolean repair = null;
+ public boolean repair() {
+ if (repair == null) {
+ repair = "true".equalsIgnoreCase(System.getProperty(AUTO_CORRECT));
+ }
+ return repair;
+ }
+
+ public static void main(String[] args) {
+ logger.info("Starting travel times report");
+
+ Integer startTravelTimesRev = null;
+ Integer maxTravelTimesRev = null;
+
+
+ if (args.length == 1) {
+ startTravelTimesRev = Integer.parseInt(args[0]);
+ maxTravelTimesRev = Integer.parseInt(args[0]);
+ logger.info("running travel times on rev {}", startTravelTimesRev);
+ } else if (args.length == 2) {
+ startTravelTimesRev = Integer.parseInt(args[0]);
+ maxTravelTimesRev = Integer.parseInt(args[1]);
+ logger.info("running travel times on revs {} through {}", startTravelTimesRev, maxTravelTimesRev);
+ }
+
+ ConfigFileReader.processConfig();
+
+ String agencyId = AgencyConfig.getAgencyId();
+ int configRev = ActiveRevisions.get(agencyId).getConfigRev();
+
+ if (startTravelTimesRev == null || maxTravelTimesRev == null) {
+ // run for last rev
+ startTravelTimesRev = ActiveRevisions.get(agencyId).getTravelTimesRev();
+ maxTravelTimesRev = ActiveRevisions.get(agencyId).getTravelTimesRev();
+ logger.info("running travel times on latest rev {}", startTravelTimesRev);
+ }
+
+ int i = startTravelTimesRev;
+ while (i <= maxTravelTimesRev) {
+ logger.info("loading travelTimeRev {}", i);
+ TravelTimesReport ttr = new TravelTimesReport();
+ ttr.setAgencyId(agencyId);
+ ttr.setConfigRev(configRev);
+ ttr.setTravelTimesRev(i);
+ ttr.run();
+ i++;
+ }
+ logger.info("travel times report complete!");
+ }
+
+
+}
diff --git a/transitime/src/main/java/org/transitime/applications/UpdateTravelTimes.java b/transitclock/src/main/java/org/transitclock/applications/UpdateTravelTimes.java
similarity index 74%
rename from transitime/src/main/java/org/transitime/applications/UpdateTravelTimes.java
rename to transitclock/src/main/java/org/transitclock/applications/UpdateTravelTimes.java
index d24405eda..d15a5abc9 100644
--- a/transitime/src/main/java/org/transitime/applications/UpdateTravelTimes.java
+++ b/transitclock/src/main/java/org/transitclock/applications/UpdateTravelTimes.java
@@ -15,32 +15,24 @@
* along with Transitime.org . If not, see .
*/
-package org.transitime.applications;
-
-import java.text.ParseException;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.TimeZone;
+package org.transitclock.applications;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.transitime.config.ConfigFileReader;
-import org.transitime.configData.AgencyConfig;
-import org.transitime.core.travelTimes.TravelTimeInfoMap;
-import org.transitime.core.travelTimes.TravelTimeInfoWithHowSet;
-import org.transitime.core.travelTimes.TravelTimesProcessor;
-import org.transitime.db.hibernate.HibernateUtils;
-import org.transitime.db.structs.ActiveRevisions;
-import org.transitime.db.structs.Agency;
-import org.transitime.db.structs.TravelTimesForStopPath;
-import org.transitime.db.structs.TravelTimesForTrip;
-import org.transitime.db.structs.Trip;
-import org.transitime.utils.IntervalTimer;
-import org.transitime.utils.Time;
+import org.transitclock.config.ConfigFileReader;
+import org.transitclock.configData.AgencyConfig;
+import org.transitclock.core.travelTimes.TravelTimeInfoMap;
+import org.transitclock.core.travelTimes.TravelTimeInfoWithHowSet;
+import org.transitclock.core.travelTimes.TravelTimesProcessor;
+import org.transitclock.db.hibernate.HibernateUtils;
+import org.transitclock.db.structs.*;
+import org.transitclock.utils.IntervalTimer;
+import org.transitclock.utils.Time;
+
+import java.text.ParseException;
+import java.util.*;
/**
* Uses AVL based data of arrival/departure times and matches from the database
@@ -57,11 +49,14 @@
*
*/
public class UpdateTravelTimes {
+
+ public static final int MAX_TRAVEL_TIME = 20 * Time.MS_PER_MIN;
+ public static final int MAX_STOP_TIME = 20 * Time.MS_PER_MIN;
// Read in configuration files. This should be done statically before
// the logback LoggerFactory.getLogger() is called so that logback can
- // also be configured using a transitime config file. The files are
- // specified using the java system property -Dtransitime.configFiles .
+ // also be configured using a transitclock config file. The files are
+ // specified using the java system property -Dtransitclock.configFiles .
static {
ConfigFileReader.processConfig();
}
@@ -84,8 +79,9 @@ public class UpdateTravelTimes {
* Map of all of the trips. Keyed on tripId.
* @param travelTimeInfoMap
* Contains travel times that are available by trip pattern ID
+ * @return the newly created travelTimesRev
*/
- private static void setTravelTimesForAllTrips(Session session,
+ private static int setTravelTimesForAllTrips(Session session,
Map tripMap, TravelTimeInfoMap travelTimeInfoMap) {
// For caching TravelTimesForTrip and TravelTimesForStopPaths that are
// created. This way won't store duplicate objects. Caching both
@@ -137,13 +133,15 @@ private static void setTravelTimesForAllTrips(Session session,
// for first stop of trip. For this case should use
// previous value.
List travelTimes;
- if (travelTimeInfo.areTravelTimesValid()) {
+ if (travelTimeInfo.areTravelTimesValid(MAX_TRAVEL_TIME)) {
travelTimes = travelTimeInfo.getTravelTimes();
} else {
// Travel times not valid so use old values
TravelTimesForStopPath originalTravelTimes =
trip.getTravelTimesForStopPath(stopIdx);
travelTimes = originalTravelTimes.getTravelTimesMsec();
+ logger.error("For trip={} stop={} invalid travel times from {} so falling back "
+ + "on old travel times from {}", trip, stopIdx, travelTimeInfo, originalTravelTimes);
}
// Determine stop time to use. There are situations where
@@ -153,13 +151,15 @@ private static void setTravelTimesForAllTrips(Session session,
// continue, stop getting AVL data for vehicle, etc. For
// this situation use the old stop time.
int stopTime;
- if (travelTimeInfo.isStopTimeValid()) {
+ if (travelTimeInfo.isStopTimeValid(MAX_STOP_TIME)) {
stopTime = travelTimeInfo.getStopTime();
} else {
// Stop time not valid so use old time
TravelTimesForStopPath originalTravelTimes =
trip.getTravelTimesForStopPath(stopIdx);
stopTime = originalTravelTimes.getStopTimeMsec();
+ logger.error("For trip={} stop={} invalid stop times from {} so falling back "
+ + "on old stop time from {}", trip, stopIdx, travelTimeInfo, originalTravelTimes);
}
// Create and add the travel time for this stop path
ttForStopPathToUse =
@@ -168,8 +168,8 @@ private static void setTravelTimesForAllTrips(Session session,
newTravelTimesRev,
trip.getStopPath(stopIdx).getId(),
travelTimeInfo.getTravelTimeSegLength(),
- travelTimes,
- stopTime,
+ clampTravelTimes(travelTimes),
+ clampStopTime(stopTime),
-1, // daysOfWeekOverride
travelTimeInfo.howSet(),
trip);
@@ -179,16 +179,20 @@ private static void setTravelTimesForAllTrips(Session session,
// Determine original travel times
TravelTimesForStopPath originalTravelTimes =
trip.getTravelTimesForStopPath(stopIdx);
+
+ logger.error("No historic data so using old travel times {}", originalTravelTimes);
// Create copy of the original travel times but update the
// travel time rev.
+ // while copying ensure the values are sane / prune out invalid values
ttForStopPathToUse =
- originalTravelTimes.clone(newTravelTimesRev);
+ originalTravelTimes.cloneAndClamp(newTravelTimesRev, MAX_STOP_TIME, MAX_TRAVEL_TIME);
}
// If already have created the exact same TravelTimesForStopPath
// then use the existing one so don't generate too many db
// objects.
+ // if we've clamped above this should return null
TravelTimesForStopPath cachedTTForStopPath =
ttForStopPathCache.get(ttForStopPathToUse);
if (cachedTTForStopPath == null) {
@@ -231,9 +235,31 @@ private static void setTravelTimesForAllTrips(Session session,
// Store the new travel times as part of the trip
trip.setTravelTimes(ttForTrip);
- } // End of for each trip that is configured
+ } // End of for each trip that is configured
+ return newTravelTimesRev;
}
-
+
+ // Make sure travelTimes aren't unreasonably large
+ private static List clampTravelTimes(List travelTimes) {
+ List clampedTravelTimes = new ArrayList<>(travelTimes.size());
+ for (Integer tt : travelTimes) {
+ if (tt > MAX_TRAVEL_TIME) {
+ clampedTravelTimes.add(0);
+ } else {
+ clampedTravelTimes.add(tt);
+ }
+ }
+ return clampedTravelTimes;
+ }
+
+ // Make sure stopTime isn't unreasonably large
+ private static int clampStopTime(int stopTime) {
+ if (stopTime > MAX_STOP_TIME) {
+ stopTime = 0;
+ }
+ return stopTime;
+ }
+
/**
* Writes the trips to the database.
*
@@ -269,13 +295,16 @@ private static void writeNewTripDataToDb(Session session,
*/
private static Map readTripsFromDb(String agencyId,
Session session) {
- ActiveRevisions activeRevisions = ActiveRevisions.get(session);
- IntervalTimer timer = new IntervalTimer();
- logger.info("Reading in trips from db...");
- Map tripMap =
- Trip.getTrips(session, activeRevisions.getConfigRev());
- logger.info("Reading in trips from db took {} msec", timer.elapsedMsec());
-
+ Map tripMap = new HashMap() ;
+ IntervalTimer timer = new IntervalTimer();
+ try {
+ ActiveRevisions activeRevisions = ActiveRevisions.get(session);
+ logger.info("Reading in trips from db...");
+ tripMap =
+ Trip.getTrips(session, activeRevisions.getConfigRev());
+ } finally {
+ logger.info("Reading in trips from db took {} msec", timer.elapsedMsec());
+ }
// Return results
return tripMap;
}
@@ -293,8 +322,9 @@ private static Map readTripsFromDb(String agencyId,
* Not fully implemented. Should therefore be null for now.
* @param beginTime
* @param endTime
+ * @return the newly created travelTimeRev
*/
- private static void processTravelTimes(Session session, String agencyId,
+ private static int processTravelTimes(Session session, String agencyId,
List specialDaysOfWeek, Date beginTime, Date endTime) {
// Read in historic data from db and put it into maps so that it can
// be processed.
@@ -302,19 +332,31 @@ private static void processTravelTimes(Session session, String agencyId,
processor.readAndProcessHistoricData(agencyId, specialDaysOfWeek,
beginTime, endTime);
+ if (processor.isEmpty()) {
+ logger.info("Exiting...");
+ return -1;
+ }
+
// Read in the current Trips. This is done after the historical data
// is read in so that less memory is used at once.
+ logger.info("reading trips...");
Map tripMap = readTripsFromDb(agencyId, session);
+ logger.info("processing travel times...");
// Process the historic data into a simple TravelTimeInfoMap
TravelTimeInfoMap travelTimeInfoMap =
processor.createTravelTimesFromMaps(tripMap);
+ logger.info("assigning travel times...");
// Update all the Trip objects with the new travel times
- setTravelTimesForAllTrips(session, tripMap, travelTimeInfoMap);
-
+ int travelTimesRev = setTravelTimesForAllTrips(session, tripMap, travelTimeInfoMap);
+
+ logger.info("saving travel times...");
// Write out the trip objects, which also writes out the travel times
- writeNewTripDataToDb(session, tripMap);
+ writeNewTripDataToDb(session, tripMap);
+
+ logger.info("committing....");
+ return travelTimesRev;
}
/**
@@ -331,8 +373,9 @@ private static void processTravelTimes(Session session, String agencyId,
* @param beginTime
* @param endTime
*/
- private static void manageSessionAndProcessTravelTimes(String agencyId,
+ public static void manageSessionAndProcessTravelTimes(String agencyId,
List specialDaysOfWeek, Date beginTime, Date endTime) {
+ int newTravelTimesRev = -2;
// Get a database session
Session session = HibernateUtils.getSession(agencyId);
Transaction tx = null;
@@ -341,7 +384,7 @@ private static void manageSessionAndProcessTravelTimes(String agencyId,
tx = session.beginTransaction();
// Actually do all the data processing
- processTravelTimes(session, agencyId, specialDaysOfWeek, beginTime,
+ newTravelTimesRev = processTravelTimes(session, agencyId, specialDaysOfWeek, beginTime,
endTime);
// Make sure that everything actually written out to db
@@ -357,7 +400,17 @@ private static void manageSessionAndProcessTravelTimes(String agencyId,
}
logger.info("Done processing travel times. Changes successfully "
- + "committed to database");
+ + "committed to database. Querying for metrics....");
+ HibernateUtils.clearSessionFactory();
+ Session statsSession = HibernateUtils.getSession(agencyId);
+ try {
+ TravelTimesProcessor processor = new TravelTimesProcessor();
+ Long inserts = processor.updateMetrics(statsSession, newTravelTimesRev);
+ logger.info("{} succesfully inserted for travelTimesRev={}", inserts, newTravelTimesRev);
+ } catch (Exception e) {
+ logger.error("exception querying for statistics for tavelTimesRev={}. Update most likely failed!", newTravelTimesRev, e);
+ }
+
}
/**
@@ -368,11 +421,15 @@ private static void manageSessionAndProcessTravelTimes(String agencyId,
* @param args
*/
public static void main(String[] args) {
+ logger.info("Starting update travel times");
// Determine the parameters
String agencyId = AgencyConfig.getAgencyId();
String startDateStr = args[0];
String endDateStr = args.length > 1 ? args[1] : startDateStr;
+
+ logger.info("Starting Date {}", startDateStr);
+ logger.info("End Date {}", endDateStr);
// Some params are hard coded simply to get things going
// List specialDaysOfWeek = new ArrayList();
@@ -382,6 +439,8 @@ public static void main(String[] args) {
// Set the timezone for the application. Must be done before
// determine begin and end time so that get the proper time of day.
int configRev = ActiveRevisions.get(agencyId).getConfigRev();
+
+ logger.info("Config Revision {}", configRev);
TimeZone timezone =
Agency.getAgencies(agencyId, configRev).get(0).getTimeZone();
TimeZone.setDefault(timezone);
@@ -390,10 +449,12 @@ public static void main(String[] args) {
Date beginTime = null;
Date endTime = null;
try {
+ logger.info("Parse Date");
beginTime = Time.parseDate(startDateStr);
endTime = new Date(Time.parseDate(endDateStr).getTime() +
Time.MS_PER_DAY);
} catch (ParseException e) {
+ logger.error("Problem parsing date", e);
e.printStackTrace();
System.exit(-1);
}
@@ -420,6 +481,7 @@ public static void main(String[] args) {
// tripList.add(trip5889634);
// tripList.add(trip5889635);
// DbWriter.writeTrips(session, tripList);
+
}
}
diff --git a/transitime/src/main/java/org/transitime/applications/package-info.java b/transitclock/src/main/java/org/transitclock/applications/package-info.java
similarity index 95%
rename from transitime/src/main/java/org/transitime/applications/package-info.java
rename to transitclock/src/main/java/org/transitclock/applications/package-info.java
index 10c2a3a7c..159092f0c 100644
--- a/transitime/src/main/java/org/transitime/applications/package-info.java
+++ b/transitclock/src/main/java/org/transitclock/applications/package-info.java
@@ -22,4 +22,4 @@
* @author SkiBu Smith
*
*/
-package org.transitime.applications;
\ No newline at end of file
+package org.transitclock.applications;
\ No newline at end of file
diff --git a/transitime/src/main/java/org/transitime/avl/AmigoCloudAvlModule.java b/transitclock/src/main/java/org/transitclock/avl/AmigoCloudAvlModule.java
similarity index 88%
rename from transitime/src/main/java/org/transitime/avl/AmigoCloudAvlModule.java
rename to transitclock/src/main/java/org/transitclock/avl/AmigoCloudAvlModule.java
index 25fd40ece..141670b95 100644
--- a/transitime/src/main/java/org/transitime/avl/AmigoCloudAvlModule.java
+++ b/transitclock/src/main/java/org/transitclock/avl/AmigoCloudAvlModule.java
@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License along with
* Transitime.org . If not, see .
*/
-package org.transitime.avl;
+package org.transitclock.avl;
import java.util.ArrayList;
import java.util.Collection;
@@ -27,16 +27,16 @@
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.transitime.avl.amigocloud.AmigoWebsocketListener;
-import org.transitime.avl.amigocloud.AmigoWebsockets;
-import org.transitime.config.LongConfigValue;
-import org.transitime.config.StringConfigValue;
-import org.transitime.configData.AgencyConfig;
-import org.transitime.db.structs.AvlReport;
-import org.transitime.logging.Markers;
-import org.transitime.modules.Module;
-import org.transitime.utils.Geo;
-import org.transitime.utils.Time;
+import org.transitclock.avl.amigocloud.AmigoWebsocketListener;
+import org.transitclock.avl.amigocloud.AmigoWebsockets;
+import org.transitclock.config.LongConfigValue;
+import org.transitclock.config.StringConfigValue;
+import org.transitclock.configData.AgencyConfig;
+import org.transitclock.db.structs.AvlReport;
+import org.transitclock.logging.Markers;
+import org.transitclock.modules.Module;
+import org.transitclock.utils.Geo;
+import org.transitclock.utils.Time;
/**
* AVL module for websocket based feed from AmigoCloud.
@@ -46,7 +46,7 @@
*/
public class AmigoCloudAvlModule extends AvlModule {
private static StringConfigValue feedUrl = new StringConfigValue(
- "transitime.avl.amigocloud.apiToken",
+ "transitclock.avl.amigocloud.apiToken",
"R:0pKSjytdXHfGMfWdRjY5xGGUU3FgaamySwDK0u",
"The API token obtained from AmigoCloud via "
+ "https://www.amigocloud.com/accounts/tokens that allows "
@@ -54,17 +54,17 @@ public class AmigoCloudAvlModule extends AvlModule {
private static LongConfigValue userId =
new LongConfigValue(
- "transitime.avl.amigocloud.userId",
+ "transitclock.avl.amigocloud.userId",
1194L,
"The ID of the client. Obtained using the amigocloud api "
+ "token via \"curl https://www.amigocloud.com/api/v1/me?token=API_TOKEN\"");
private static LongConfigValue projectId = new LongConfigValue(
- "transitime.avl.amigocloud.projectId", 661L,
+ "transitclock.avl.amigocloud.projectId", 661L,
"The amigocloud ID of the agency that getting AVL data " + "from.");
private static LongConfigValue datasetId = new LongConfigValue(
- "transitime.avl.amigocloud.datasetId", 12556L,
+ "transitclock.avl.amigocloud.datasetId", 12556L,
"The amigocloud ID of the dataset for the agency that "
+ "getting AVL data from.");
@@ -271,6 +271,6 @@ public void run() {
* @param args
*/
public static void main(String[] args) {
- Module.start("org.transitime.avl.AmigoCloudAvlModule");
+ Module.start("org.transitclock.avl.AmigoCloudAvlModule");
}
}
diff --git a/transitime/src/main/java/org/transitime/avl/AvlClient.java b/transitclock/src/main/java/org/transitclock/avl/AvlClient.java
similarity index 79%
rename from transitime/src/main/java/org/transitime/avl/AvlClient.java
rename to transitclock/src/main/java/org/transitclock/avl/AvlClient.java
index 5609faa52..3c4ae47f5 100644
--- a/transitime/src/main/java/org/transitime/avl/AvlClient.java
+++ b/transitclock/src/main/java/org/transitclock/avl/AvlClient.java
@@ -14,18 +14,19 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.avl;
+package org.transitclock.avl;
import java.util.HashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.transitime.configData.AgencyConfig;
-import org.transitime.configData.AvlConfig;
-import org.transitime.core.AvlProcessor;
-import org.transitime.db.structs.AvlReport;
-import org.transitime.logging.Markers;
-import org.transitime.utils.Time;
+import org.transitclock.configData.AgencyConfig;
+import org.transitclock.configData.AvlConfig;
+import org.transitclock.configData.CoreConfig;
+import org.transitclock.core.AvlProcessor;
+import org.transitclock.db.structs.AvlReport;
+import org.transitclock.logging.Markers;
+import org.transitclock.utils.Time;
/**
* Receives AVL data from the AvlExecutor or JMS, determines if AVL should be
@@ -97,12 +98,18 @@ public void run() {
if (previousReportForVehicle != null
&& avlReport.getTime() <= previousReportForVehicle
.getTime()) {
- logger.warn("Throwing away AVL report because it is same time "
- + "or older than the previous AVL report for the "
- + "vehicle. New AVL report is {}. Previous valid AVL "
- + "report is {}", avlReport,
- previousReportForVehicle);
- return;
+ if (avlReport.hasValidAssignment() && !previousReportForVehicle.hasValidAssignment()) {
+ // assignment records have higher priority then just lat/lon updates
+ logger.info("keeping older AVL report because it contains an assignment "
+ + "not present in cache {}", avlReport);
+ } else {
+ logger.warn("Throwing away AVL report because it is same time "
+ + "or older than the previous AVL report for the "
+ + "vehicle. New AVL report is {}. Previous valid AVL "
+ + "report is {}", avlReport,
+ previousReportForVehicle);
+ return;
+ }
}
// If previous report happened too recently then don't want to
@@ -133,7 +140,7 @@ public void run() {
logger.debug("Not processing AVL report because the new "
+ "report is too close in time to the previous AVL "
+ "report for the vehicle. "
- + "transitime.avl.minTimeBetweenAvlReportsSecs={} "
+ + "transitclock.avl.minTimeBetweenAvlReportsSecs={} "
+ "secs. New AVL report is {}. Previous valid AVL "
+ "report is {}",
AvlConfig.getMinTimeBetweenAvlReportsSecs(),
@@ -142,11 +149,15 @@ public void run() {
// But still want to update the vehicle cache with the
// latest report because doing so is cheap and it allows
// vehicles to move on map smoothly
- AvlProcessor.getInstance()
- .cacheAvlReportWithoutProcessing(avlReport);
+ if (avlReport.hasValidAssignment() && !previousReportForVehicle.hasValidAssignment()) {
+ logger.info("using discarded AVL report due to its assignment {}", avlReport);
+ } else {
+ AvlProcessor.getInstance()
+ .cacheAvlReportWithoutProcessing(avlReport);
+ // Done here since not processing this AVL report
+ return;
- // Done here since not processing this AVL report
- return;
+ }
}
}
@@ -161,6 +172,7 @@ public void run() {
Thread.currentThread().getName(), avlReport);
AvlProcessor.getInstance().processAvlReport(avlReport);
} catch (Exception e) {
+ e.printStackTrace();
// Catch unexpected exceptions so that can continue to use the same
// AVL thread even if there is an unexpected problem. Only let
// Errors, such as OutOfMemory errors, through.
diff --git a/transitime/src/main/java/org/transitime/avl/AvlCsvReader.java b/transitclock/src/main/java/org/transitclock/avl/AvlCsvReader.java
similarity index 90%
rename from transitime/src/main/java/org/transitime/avl/AvlCsvReader.java
rename to transitclock/src/main/java/org/transitclock/avl/AvlCsvReader.java
index 186012f2e..ca8c7c7ed 100644
--- a/transitime/src/main/java/org/transitime/avl/AvlCsvReader.java
+++ b/transitclock/src/main/java/org/transitclock/avl/AvlCsvReader.java
@@ -15,13 +15,13 @@
* along with Transitime.org . If not, see .
*/
-package org.transitime.avl;
+package org.transitclock.avl;
import java.text.ParseException;
import org.apache.commons.csv.CSVRecord;
-import org.transitime.db.structs.AvlReport;
-import org.transitime.utils.csv.CsvBaseReader;
+import org.transitclock.db.structs.AvlReport;
+import org.transitclock.utils.csv.CsvBaseReader;
/**
* For reading in AVL data from a CSV file.
diff --git a/transitime/src/main/java/org/transitime/avl/AvlCsvRecord.java b/transitclock/src/main/java/org/transitclock/avl/AvlCsvRecord.java
similarity index 92%
rename from transitime/src/main/java/org/transitime/avl/AvlCsvRecord.java
rename to transitclock/src/main/java/org/transitclock/avl/AvlCsvRecord.java
index cab5a8f7a..76f63a80b 100644
--- a/transitime/src/main/java/org/transitime/avl/AvlCsvRecord.java
+++ b/transitclock/src/main/java/org/transitclock/avl/AvlCsvRecord.java
@@ -15,13 +15,13 @@
* along with Transitime.org . If not, see .
*/
-package org.transitime.avl;
+package org.transitclock.avl;
import org.apache.commons.csv.CSVRecord;
-import org.transitime.db.structs.AvlReport;
-import org.transitime.db.structs.AvlReport.AssignmentType;
-import org.transitime.utils.Time;
-import org.transitime.utils.csv.CsvBase;
+import org.transitclock.db.structs.AvlReport;
+import org.transitclock.db.structs.AvlReport.AssignmentType;
+import org.transitclock.utils.Time;
+import org.transitclock.utils.csv.CsvBase;
import java.text.ParseException;
@@ -76,7 +76,13 @@ public static AvlReport getAvlReport(CSVRecord record, String fileName)
}
String latStr = avlCsvRecord.getRequiredValue(record, "latitude");
- double lat = Double.parseDouble(latStr);
+ double lat=Double.NaN;
+ try {
+ lat = Double.parseDouble(latStr);
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
String lonStr = avlCsvRecord.getRequiredValue(record, "longitude");
double lon = Double.parseDouble(lonStr);
diff --git a/transitime/src/main/java/org/transitime/avl/AvlCsvWriter.java b/transitclock/src/main/java/org/transitclock/avl/AvlCsvWriter.java
similarity index 89%
rename from transitime/src/main/java/org/transitime/avl/AvlCsvWriter.java
rename to transitclock/src/main/java/org/transitclock/avl/AvlCsvWriter.java
index b23bb5040..67d5ea2fe 100644
--- a/transitime/src/main/java/org/transitime/avl/AvlCsvWriter.java
+++ b/transitclock/src/main/java/org/transitclock/avl/AvlCsvWriter.java
@@ -15,19 +15,19 @@
* along with Transitime.org . If not, see .
*/
-package org.transitime.avl;
+package org.transitclock.avl;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.transitime.db.structs.AvlReport;
-import org.transitime.db.structs.AvlReport.AssignmentType;
-import org.transitime.utils.ChinaGpsOffset;
-import org.transitime.utils.Geo;
-import org.transitime.utils.Time;
-import org.transitime.utils.ChinaGpsOffset.LatLon;
-import org.transitime.utils.csv.CsvWriterBase;
+import org.transitclock.db.structs.AvlReport;
+import org.transitclock.db.structs.AvlReport.AssignmentType;
+import org.transitclock.utils.ChinaGpsOffset;
+import org.transitclock.utils.Geo;
+import org.transitclock.utils.Time;
+import org.transitclock.utils.ChinaGpsOffset.LatLon;
+import org.transitclock.utils.csv.CsvWriterBase;
/**
* For writing a CSV file containing AVL reports.
@@ -62,7 +62,7 @@ public AvlCsvWriter(String fileName, String timezoneStr) {
/* (non-Javadoc)
- * @see org.transitime.utils.csv.CsvWriterBase#writeHeader()
+ * @see org.transitclock.utils.csv.CsvWriterBase#writeHeader()
*/
@Override
protected void writeHeader() throws IOException {
diff --git a/transitime/src/main/java/org/transitime/avl/AvlExecutor.java b/transitclock/src/main/java/org/transitclock/avl/AvlExecutor.java
similarity index 85%
rename from transitime/src/main/java/org/transitime/avl/AvlExecutor.java
rename to transitclock/src/main/java/org/transitclock/avl/AvlExecutor.java
index 98f8da429..cee7d9ff3 100644
--- a/transitime/src/main/java/org/transitime/avl/AvlExecutor.java
+++ b/transitclock/src/main/java/org/transitclock/avl/AvlExecutor.java
@@ -14,28 +14,29 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.avl;
+package org.transitclock.avl;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.transitclock.config.IntegerConfigValue;
+import org.transitclock.configData.AgencyConfig;
+import org.transitclock.db.structs.AvlReport;
+import org.transitclock.logging.Markers;
+import org.transitclock.monitoring.MonitoringService;
+import org.transitclock.utils.Time;
+import org.transitclock.utils.threading.NamedThreadFactory;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.transitime.config.IntegerConfigValue;
-import org.transitime.configData.AgencyConfig;
-import org.transitime.db.structs.AvlReport;
-import org.transitime.logging.Markers;
-import org.transitime.utils.Time;
-import org.transitime.utils.threading.NamedThreadFactory;
-
/**
* A singleton thread executor for executing AVL reports. For when not using JMS
* to handle queue of AVL reports. One can dump AVL reports into this executor
* and then have them be executed, possibly using multiple threads. The number
- * of threads is specified using the Java property transitime.avl.numThreads .
- * The queue size is set using the Java property transitime.avl.queueSize .
+ * of threads is specified using the Java property transitclock.avl.numThreads .
+ * The queue size is set using the Java property transitclock.avl.queueSize .
*
* Causes AvlClient.run() to be called on each AvlReport, unless using test
* executor, in which case the AvlClientTester() is called.
@@ -47,7 +48,10 @@ public class AvlExecutor {
// The actual executor
ThreadPoolExecutor avlClientExecutor = null;
-
+ public int getQueueSize() {
+ return avlClientExecutor.getQueue().size();
+ }
+
// Singleton class
private static AvlExecutor singleton;
@@ -58,7 +62,7 @@ public class AvlExecutor {
private final static int MAX_THREADS = 25;
private static IntegerConfigValue avlQueueSize =
- new IntegerConfigValue("transitime.avl.queueSize", 2000,
+ new IntegerConfigValue("transitclock.avl.queueSize", 2000,
"How many items to go into the blocking AVL queue "
+ "before need to wait for queue to have space. Should "
+ "be approximately 50% more than the number of reports "
@@ -67,7 +71,7 @@ public class AvlExecutor {
+ "data will be rejected by the ThreadPoolExecutor. ");
private static IntegerConfigValue numAvlThreads =
- new IntegerConfigValue("transitime.avl.numThreads", 1,
+ new IntegerConfigValue("transitclock.avl.numThreads", 1,
"How many threads to be used for processing the AVL " +
"data. For most applications just using a single thread " +
"is probably sufficient and it makes the logging simpler " +
@@ -109,7 +113,6 @@ private AvlExecutor() {
maxAVLQueueSize, numberThreads);
// Start up the ThreadPoolExecutor
- int corePoolSize = 1;
int maximumPoolSize = numberThreads;
long keepAliveTime = 1; /* 1 hour */
BlockingQueue workQueue = new AvlQueue(maxAVLQueueSize);
@@ -134,9 +137,10 @@ public void rejectedExecution(Runnable arg0, ThreadPoolExecutor arg1) {
logger.error(message);
}
}};
-
+
+ // pool size does not expand as expected -- configure to have max threads available on creation
avlClientExecutor =
- new ThreadPoolExecutor(corePoolSize, maximumPoolSize,
+ new ThreadPoolExecutor(maximumPoolSize, maximumPoolSize,
keepAliveTime, TimeUnit.HOURS, workQueue,
avlClientThreadFactory,
rejectedHandler);
@@ -185,7 +189,13 @@ public void processAvlReport(AvlReport newAvlReport,
Runnable avlClient = !testing ?
new AvlClient(newAvlReport) : new AvlClientTester(newAvlReport);
- avlClientExecutor.execute(avlClient);
+ // monitor queue capacity
+ int size = this.avlClientExecutor.getQueue().size();
+ int capacity = avlQueueSize.getValue();
+ MonitoringService.getInstance().averageMetric("PredictionAvlWorkQueuePercentageLevel", (double) size / capacity);
+
+
+ avlClientExecutor.execute(avlClient);
}
/**
diff --git a/transitime/src/main/java/org/transitime/avl/AvlJmsClientModule.java b/transitclock/src/main/java/org/transitclock/avl/AvlJmsClientModule.java
similarity index 92%
rename from transitime/src/main/java/org/transitime/avl/AvlJmsClientModule.java
rename to transitclock/src/main/java/org/transitclock/avl/AvlJmsClientModule.java
index e3afaf2eb..fe5340ade 100644
--- a/transitime/src/main/java/org/transitime/avl/AvlJmsClientModule.java
+++ b/transitclock/src/main/java/org/transitclock/avl/AvlJmsClientModule.java
@@ -15,7 +15,7 @@
* along with Transitime.org . If not, see .
*/
-package org.transitime.avl;
+package org.transitclock.avl;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@@ -26,15 +26,15 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.transitime.config.IntegerConfigValue;
-import org.transitime.configData.AgencyConfig;
-import org.transitime.db.structs.AvlReport;
-import org.transitime.ipc.jms.JMSWrapper;
-import org.transitime.logging.Markers;
-import org.transitime.modules.Module;
-import org.transitime.utils.Time;
-import org.transitime.utils.threading.BoundedExecutor;
-import org.transitime.utils.threading.NamedThreadFactory;
+import org.transitclock.config.IntegerConfigValue;
+import org.transitclock.configData.AgencyConfig;
+import org.transitclock.db.structs.AvlReport;
+import org.transitclock.ipc.jms.JMSWrapper;
+import org.transitclock.logging.Markers;
+import org.transitclock.modules.Module;
+import org.transitclock.utils.Time;
+import org.transitclock.utils.threading.BoundedExecutor;
+import org.transitclock.utils.threading.NamedThreadFactory;
/**
* Reads AVL data from JMS topic and processes it. Can use
@@ -52,13 +52,13 @@ public class AvlJmsClientModule extends Module {
private final static int MAX_THREADS = 100;
private static IntegerConfigValue avlQueueSize =
- new IntegerConfigValue("transitime.avl.jmsQueueSize", 350,
+ new IntegerConfigValue("transitclock.avl.jmsQueueSize", 350,
"How many items to go into the blocking AVL queue "
+ "before need to wait for queue to have space. "
+ "Only for when JMS is used.");
private static IntegerConfigValue numAvlThreads =
- new IntegerConfigValue("transitime.avl.jmsNumThreads", 1,
+ new IntegerConfigValue("transitclock.avl.jmsNumThreads", 1,
"How many threads to be used for processing the AVL " +
"data. For most applications just using a single thread " +
"is probably sufficient and it makes the logging simpler " +
diff --git a/transitime/src/main/java/org/transitime/avl/AvlModule.java b/transitclock/src/main/java/org/transitclock/avl/AvlModule.java
similarity index 89%
rename from transitime/src/main/java/org/transitime/avl/AvlModule.java
rename to transitclock/src/main/java/org/transitclock/avl/AvlModule.java
index 20137c73f..5f3af5386 100644
--- a/transitime/src/main/java/org/transitime/avl/AvlModule.java
+++ b/transitclock/src/main/java/org/transitclock/avl/AvlModule.java
@@ -14,18 +14,21 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.avl;
+package org.transitclock.avl;
import java.util.Collection;
import javax.jms.JMSException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.transitime.configData.AvlConfig;
-import org.transitime.db.structs.AvlReport;
-import org.transitime.ipc.jms.JMSWrapper;
-import org.transitime.ipc.jms.RestartableMessageProducer;
-import org.transitime.modules.Module;
+import org.transitclock.configData.AvlConfig;
+import org.transitclock.db.structs.AvlReport;
+import org.transitclock.ipc.jms.JMSWrapper;
+import org.transitclock.ipc.jms.RestartableMessageProducer;
+import org.transitclock.modules.Module;
+
+import javax.jms.JMSException;
+import java.util.Collection;
/**
diff --git a/transitime/src/main/java/org/transitime/avl/AvlQueue.java b/transitclock/src/main/java/org/transitclock/avl/AvlQueue.java
similarity index 95%
rename from transitime/src/main/java/org/transitime/avl/AvlQueue.java
rename to transitclock/src/main/java/org/transitclock/avl/AvlQueue.java
index 94d4d72b2..b4d04dc1b 100644
--- a/transitime/src/main/java/org/transitime/avl/AvlQueue.java
+++ b/transitclock/src/main/java/org/transitclock/avl/AvlQueue.java
@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.avl;
+package org.transitclock.avl;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
@@ -23,7 +23,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.transitime.db.structs.AvlReport;
+import org.transitclock.db.structs.AvlReport;
/**
* A queue of AvlClient runnables that can be used with a ThreadPoolExecutor.
diff --git a/transitclock/src/main/java/org/transitclock/avl/BarefootPlaybackModule.java b/transitclock/src/main/java/org/transitclock/avl/BarefootPlaybackModule.java
new file mode 100644
index 000000000..b5da9bf97
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/avl/BarefootPlaybackModule.java
@@ -0,0 +1,164 @@
+package org.transitclock.avl;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.List;
+import java.util.TimeZone;
+
+import org.json.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.transitclock.applications.Core;
+import org.transitclock.db.structs.AvlReport;
+import org.transitclock.utils.IntervalTimer;
+import org.transitclock.utils.Time;
+
+import com.esri.core.geometry.Point;
+
+public class BarefootPlaybackModule extends PlaybackModule {
+ private static final Logger logger =
+ LoggerFactory.getLogger(PlaybackModule.class);
+
+ public BarefootPlaybackModule(String agencyId) {
+ super(agencyId);
+
+ }
+
+ @Override
+ public void run() {
+
+ IntervalTimer timer = new IntervalTimer();
+ // Keep running as long as not trying to access in the future.
+
+ while (dbReadBeginTime < System.currentTimeMillis() && (playbackEndTimeStr.getValue().length()==0 || dbReadBeginTime avlReports = getBatchOfAvlReportsFromDb();
+
+ // Process the AVL Reports read in.
+ long last_avl_time=-1;
+ for (AvlReport avlReport : avlReports) {
+ logger.info("Processing avlReport={}", avlReport);
+
+ // Update the Core SystemTime to use this AVL time
+ Core.getInstance().setSystemTime(avlReport.getTime());
+
+ DateFormat estFormat = new SimpleDateFormat("yyyyMMddHHmmss");
+ TimeZone estTime = TimeZone.getTimeZone("EST");
+ estFormat.setTimeZone(estTime);
+
+ TimeZone gmtTime = TimeZone.getTimeZone("GMT");
+ DateFormat gmtFormat = new SimpleDateFormat("yyyyMMddHHmmss");
+ gmtFormat.setTimeZone(gmtTime);
+
+ String estDate=estFormat.format(avlReport.getTime());
+ String gmtDate=gmtFormat.format(avlReport.getTime());
+
+
+ if(playbackRealtime.getValue()==true)
+ {
+ if(last_avl_time>-1)
+ {
+ try {
+ // only sleep if values less than 10 minutes. This is to allow it skip days/hours of missing data.
+ if((avlReport.getTime()-last_avl_time)<600000)
+ {
+ Thread.sleep(avlReport.getTime()-last_avl_time);
+ }
+ last_avl_time=avlReport.getTime();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }else
+ {
+ last_avl_time=avlReport.getTime();
+ }
+ }
+ // Send avl to barefoot server.
+ sendUpdate(avlReport);
+
+ }
+ }
+ // logging here as the rest is database access dependent.
+ logger.info("Processed AVL from playbackStartTimeStr:{} to playbackEndTimeStr:{} in {} secs.",playbackStartTimeStr,playbackEndTimeStr, Time.secondsStr(timer.elapsedMsec()));
+
+
+ // Wait for database queue to be emptied before exiting.
+ while(Core.getInstance().getDbLogger().queueSize()>0)
+ {
+ try {
+ Thread.sleep(10000);
+ } catch (InterruptedException e) {
+
+ }
+ }
+
+
+ logger.info("Read in AVL in playback mode all the way up to current " +
+ "time so done. Exiting.");
+
+ System.exit(0);
+ }
+ public void sendUpdate(AvlReport avlReport) {
+ try {
+ JSONObject report = new JSONObject();
+
+ InetAddress host = InetAddress.getLocalHost();
+
+ int port = 1234;
+
+ report.put("id", avlReport.getVehicleId());
+
+ SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ssZ");
+
+ report.put("time", df.format(avlReport.getDate()));
+
+ Point point = new Point();
+ point.setX(avlReport.getLon());
+ point.setY(avlReport.getLat());
+
+ report.put("point", "POINT(" + avlReport.getLon() + " " + avlReport.getLat() + ")");
+ // report.put("point", GeometryEngine.geometryToGeoJson(point));
+
+ sendBareFootSample(host, port, report);
+
+ } catch (Exception e) {
+ logger.error("Problem when sending samples to barefoot.", e);
+ }
+ }
+
+ private void sendBareFootSample(InetAddress host, int port, JSONObject sample) throws Exception {
+ int trials = 120;
+ int timeout = 500;
+ Socket client = null;
+ // TODO Will need to leave socket open.
+ while (client == null || !client.isConnected()) {
+ try {
+ client = new Socket(host, port);
+ } catch (IOException e) {
+ Thread.sleep(timeout);
+
+ if (trials == 0) {
+ logger.error(e.getMessage());
+ client.close();
+ throw new IOException();
+ } else {
+ trials -= 1;
+ }
+ }
+ }
+ PrintWriter writer = new PrintWriter(client.getOutputStream());
+ BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
+ writer.println(sample.toString());
+ writer.flush();
+
+ String code = reader.readLine();
+ if (!code.equals("SUCCESS")) {
+ throw new Exception("Barefoot server did not respond with SUCCESS. Code="+code);
+ }
+ }
+}
diff --git a/transitclock/src/main/java/org/transitclock/avl/BatchCsvAvlFeedModule.java b/transitclock/src/main/java/org/transitclock/avl/BatchCsvAvlFeedModule.java
new file mode 100644
index 000000000..2c5ae15ac
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/avl/BatchCsvAvlFeedModule.java
@@ -0,0 +1,154 @@
+/*
+ * 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 .
+ */
+
+package org.transitclock.avl;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.transitclock.applications.Core;
+import org.transitclock.config.BooleanConfigValue;
+import org.transitclock.config.StringConfigValue;
+import org.transitclock.core.AvlProcessor;
+import org.transitclock.db.structs.AvlReport;
+import org.transitclock.modules.Module;
+import org.transitclock.utils.Time;
+
+import java.util.List;
+
+/**
+ * For reading in a batch of AVL data in CSV format and processing it. It only
+ * reads a single batch of data, unlike the usual AVL modules that continuously
+ * read data. This module is useful for debugging because can relatively easily
+ * create a plain text CSV file of AVL data and see what the code does.
+ *
+ * CSV columns include vehicleId, time (in epoch msec or as date string as in
+ * "9-14-2015 12:53:01"), latitude, longitude, speed (optional), heading
+ * (optional), assignmentId, and assignmentType (optional, but can be BLOCK_ID,
+ * ROUTE_ID, TRIP_ID, or TRIP_SHORT_NAME).
+ *
+ * @author SkiBu Smith
+ *
+ */
+public class BatchCsvAvlFeedModule extends Module {
+
+ // For running in real time
+ private long lastAvlReportTimestamp = -1;
+
+ /*********** Configurable Parameters for this module ***********/
+ private static String getCsvAvlFeedFileName() {
+ return csvAvlFeedFileName.getValue();
+ }
+ private static StringConfigValue csvAvlFeedFileName =
+ new StringConfigValue("transitclock.avl.csvAvlFeedFileName",
+ "/Users/Mike/cvsAvlData/testAvlData.csv",
+ "The name of the CSV file containing AVL data to process.");
+
+
+ private static BooleanConfigValue processInRealTime =
+ new BooleanConfigValue("transitclock.avl.processInRealTime",
+ false,
+ "For when getting batch of AVL data from a CSV file. "
+ + "When true then when reading in do at the same speed as "
+ + "when the AVL was created. Set to false it you just want "
+ + "to read in as fast as possible.");
+
+ /****************** Logging **************************************/
+
+ private static final Logger logger = LoggerFactory
+ .getLogger(BatchCsvAvlFeedModule.class);
+
+ /********************** Member Functions **************************/
+
+ /**
+ * @param projectId
+ */
+ public BatchCsvAvlFeedModule(String projectId) {
+ super(projectId);
+ }
+
+ /**
+ * If configured to process data in real time them delay the appropriate
+ * amount of time
+ *
+ * @param avlReport
+ */
+ private void delayIfRunningInRealTime(AvlReport avlReport) {
+ if (processInRealTime.getValue()) {
+ long delayLength = 0;
+
+ if (lastAvlReportTimestamp > 0) {
+ delayLength = avlReport.getTime() - lastAvlReportTimestamp;
+ lastAvlReportTimestamp = avlReport.getTime();
+ } else {
+ lastAvlReportTimestamp = avlReport.getTime();
+ }
+
+ if (delayLength > 0)
+ Time.sleep(delayLength);
+ }
+ }
+
+ /*
+ * Reads in AVL reports from CSV file and processes them.
+ *
+ * (non-Javadoc)
+ * @see java.lang.Runnable#run()
+ */
+ @Override
+ public void run() {
+ List avlReports =
+ (new AvlCsvReader(getCsvAvlFeedFileName())).get();
+
+ // Process the AVL Reports read in.
+ for (AvlReport avlReport : avlReports) {
+
+ logger.info("Processing avlReport={}", avlReport);
+
+ // If configured to process data in real time them delay
+ // the appropriate amount of time
+ delayIfRunningInRealTime(avlReport);
+
+ // Use the AVL report time as the current system time
+ Core.getInstance().setSystemTime(avlReport.getTime());
+
+ // Actually process the AVL report
+ AvlProcessor.getInstance().processAvlReport(avlReport);
+
+ // Post process if neccessary
+ if (avlPostProcessor != null)
+ avlPostProcessor.postProcess(avlReport);
+ }
+
+ // Kill off the whole program because done processing the AVL data
+ String integrationTest = System.getProperty("transitclock.core.integrationTest");
+ if(integrationTest != null){
+ System.setProperty("transitclock.core.csvImported","true");
+ }else{
+ System.exit(0);
+ }
+ }
+
+ private AvlPostProcessor avlPostProcessor = null;
+
+ public void setAvlPostProcessor(AvlPostProcessor avlPostProcessor) {
+ this.avlPostProcessor = avlPostProcessor;
+ }
+
+ public interface AvlPostProcessor {
+ void postProcess(AvlReport avlReport);
+ }
+}
diff --git a/transitime/src/main/java/org/transitime/avl/BatchGtfsRealtimeModule.java b/transitclock/src/main/java/org/transitclock/avl/BatchGtfsRealtimeModule.java
similarity index 91%
rename from transitime/src/main/java/org/transitime/avl/BatchGtfsRealtimeModule.java
rename to transitclock/src/main/java/org/transitclock/avl/BatchGtfsRealtimeModule.java
index 7f813bc63..8f90f6d34 100644
--- a/transitime/src/main/java/org/transitime/avl/BatchGtfsRealtimeModule.java
+++ b/transitclock/src/main/java/org/transitclock/avl/BatchGtfsRealtimeModule.java
@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.avl;
+package org.transitclock.avl;
import java.util.ArrayList;
import java.util.Collection;
@@ -25,14 +25,14 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.transitime.applications.Core;
-import org.transitime.config.StringListConfigValue;
-import org.transitime.core.AvlProcessor;
-import org.transitime.db.structs.AvlReport;
-import org.transitime.db.structs.Location;
-import org.transitime.feed.gtfsRt.GtfsRtVehiclePositionsReader;
-import org.transitime.modules.Module;
-import org.transitime.utils.Time;
+import org.transitclock.applications.Core;
+import org.transitclock.config.StringListConfigValue;
+import org.transitclock.core.AvlProcessor;
+import org.transitclock.db.structs.AvlReport;
+import org.transitclock.db.structs.Location;
+import org.transitclock.feed.gtfsRt.GtfsRtVehiclePositionsReader;
+import org.transitclock.modules.Module;
+import org.transitclock.utils.Time;
/**
* For reading in a batch of GTFS-realtime data and processing it. It only
@@ -65,7 +65,7 @@ public static List getGtfsRealtimeURIs() {
return gtfsRealtimeURIs.getValue();
}
private static StringListConfigValue gtfsRealtimeURIs =
- new StringListConfigValue("transitime.avl.gtfsRealtimeFeedURIs",
+ new StringListConfigValue("transitclock.avl.gtfsRealtimeFeedURIs",
null,
"Semicolon separated list of URIs of the GTFS-realtime data to read in");
diff --git a/transitclock/src/main/java/org/transitclock/avl/CsvPollingAvlModule.java b/transitclock/src/main/java/org/transitclock/avl/CsvPollingAvlModule.java
new file mode 100644
index 000000000..3ea9e6b8c
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/avl/CsvPollingAvlModule.java
@@ -0,0 +1,159 @@
+package org.transitclock.avl;
+
+import org.apache.commons.csv.CSVFormat;
+import org.apache.commons.csv.CSVRecord;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.transitclock.config.BooleanConfigValue;
+import org.transitclock.config.StringConfigValue;
+import org.transitclock.db.structs.AvlReport;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Iterator;
+
+/**
+ * Map arbitrary CSV columns to an AVLRecord. Currently makes
+ * large assumptions about the columns but these can be improved as needed.
+ * Current columns required: vehicleId,Date,lat,lon,speed,heading
+ */
+public class CsvPollingAvlModule extends PollUrlAvlModule {
+
+ private static final float KILOMETERS_PER_MILE = 1.60934f;
+
+ private static StringConfigValue feedUrl =
+ new StringConfigValue("transitclock.avl.csv.url",
+ "http://localhost:8080/csv",
+ "The URL of the CSV feed.");
+
+ private static StringConfigValue vehicleParam =
+ new StringConfigValue("transitclock.avl.csv.vehicle_param",
+ "DeviceID",
+ "CSV header for vehicleId column");
+
+ private static StringConfigValue dateParam =
+ new StringConfigValue("transitclock.avl.csv.date_param",
+ "Date",
+ "CSV header for date column");
+
+ private static BooleanConfigValue isSeparateTimeParam =
+ new BooleanConfigValue("transitclock.avl.csv.need_time_param",
+ true,
+ "If time is separate from date");
+
+ private static StringConfigValue timeParam =
+ new StringConfigValue("transitclock.avl.csv.time_param",
+ "Time",
+ "CSV header for time column");
+
+ private static StringConfigValue dateFormatParam =
+ new StringConfigValue("transitclock.avl.csv.date_format_param",
+ "yyyy/MM/ddHH:mm:ss",
+ "SimpleDateFormat constructor for date parsing");
+
+ private static StringConfigValue latParam =
+ new StringConfigValue("transitclock.avl.csv.lat_param",
+ "Latitude",
+ "CSV header for latitude column");
+
+ private static StringConfigValue lonParam =
+ new StringConfigValue("transitclock.avl.csv.lon_param",
+ "Longitude",
+ "CSV header for longitude column");
+
+ private static StringConfigValue speedParam =
+ new StringConfigValue("transitclock.avl.csv.speed_param",
+ "Speed",
+ "CSV header for speed column");
+
+ private static BooleanConfigValue speedInMphParam =
+ new BooleanConfigValue("transitclock.avl.csv.speed_is_mph_param",
+ true,
+ "True if speed is in miles per hour");
+
+ private static StringConfigValue headingParam =
+ new StringConfigValue("transitclock.avl.csv.heading_param",
+ "Heading",
+ "CSV header for heading/bearing column");
+
+ private static final Logger logger = LoggerFactory.getLogger(CsvPollingAvlModule.class);
+
+
+ /**
+ * Constructor
+ *
+ * @param agencyId
+ */
+ public CsvPollingAvlModule(String agencyId) {
+ super(agencyId);
+ }
+
+ @Override
+ protected String getUrl() {
+ return feedUrl.getValue();
+ }
+
+ @Override
+ protected Collection processData(InputStream in) throws Exception {
+ // adapt an arbitrary csv file feed to an AVLRecord
+ Collection avlReportsReadIn = new ArrayList<>();
+ CSVRecord record = null;
+ CSVFormat formatter =
+ CSVFormat.DEFAULT.withHeader().withCommentMarker('-');
+
+ long start = System.currentTimeMillis();
+ // Parse the file
+ Iterable records = formatter.parse(new BufferedReader(new InputStreamReader(in)));
+ Iterator iterator = records.iterator();
+ while (iterator.hasNext()) {
+ record = iterator.next();
+ if (record.size() == 0)
+ continue;
+ try {
+ AvlReport report = handleRecord(record);
+ if (report != null) {
+ avlReportsReadIn.add(report);
+ }
+ } catch (Exception any) {
+ // bad record -- don't let it abort the entire file
+ logger.warn("bad record {} at row {}", record.toString(), record.getRecordNumber());
+ }
+ }
+ long stop = System.currentTimeMillis();
+ logger.warn("Parsed csv feed in {} ms", (stop-start)/1000);
+ return avlReportsReadIn;
+ }
+
+ private AvlReport handleRecord(CSVRecord record) throws Exception {
+ String vehicleId = record.get("DeviceID").trim();
+ String dateStr = record.get(dateParam.getValue()).trim();
+ if (isSeparateTimeParam.getValue()) {
+ dateStr += record.get(timeParam.getValue()).trim();
+ }
+ String latStr = record.get(latParam.getValue()).trim();
+ String lonStr = record.get(lonParam.getValue()).trim();
+ String speedStr = record.get(speedParam.getValue()).trim();
+ String headingStr = record.get(headingParam.getValue()).trim();
+
+ SimpleDateFormat sdf = new SimpleDateFormat(dateFormatParam.toString());
+ Date avlDate = sdf.parse(dateStr);
+ double lat = Double.parseDouble(latStr);
+ double lon = Double.parseDouble(lonStr);
+ float speed = Float.parseFloat(speedStr);
+ if (speedInMphParam.getValue()) {
+ speed = speed / (float)2.237; // mph to m/s
+
+ }
+ float heading = Float.parseFloat(headingStr);
+
+ AvlReport r = new AvlReport(vehicleId, avlDate.getTime(), lat, lon,
+ speed, heading, "OpenGTS");
+
+ return r;
+ }
+}
diff --git a/transitclock/src/main/java/org/transitclock/avl/GtfsRealtimeModule.java b/transitclock/src/main/java/org/transitclock/avl/GtfsRealtimeModule.java
new file mode 100644
index 000000000..3a378238d
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/avl/GtfsRealtimeModule.java
@@ -0,0 +1,126 @@
+/*
+ * 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 .
+ */
+package org.transitclock.avl;
+
+import java.io.InputStream;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.transitclock.config.StringConfigValue;
+import org.transitclock.db.structs.AvlReport;
+import org.transitclock.feed.gtfsRt.GtfsRtVehiclePositionsReader;
+import org.transitclock.modules.Module;
+import org.transitclock.monitoring.MonitoringService;
+
+import java.util.Collection;
+
+/**
+ * For reading in feed of GTFS-realtime AVL data. Is used for both realtime
+ * feeds and for when reading in a giant batch of data.
+ *
+ * @author SkiBu Smith
+ *
+ */
+public class GtfsRealtimeModule extends PollUrlAvlModule {
+
+
+
+ // If debugging feed and want to not actually process
+ // AVL reports to generate predictions and such then
+ // set shouldProcessAvl to false;
+ protected static boolean shouldProcessAvl = true;
+
+
+ private static final Logger logger = LoggerFactory
+ .getLogger(GtfsRealtimeModule.class);
+
+
+ /*********** Configurable Parameters for this module ***********/
+ public static String getGtfsRealtimeURI() {
+ return gtfsRealtimeURI.getValue();
+ }
+ private static StringConfigValue gtfsRealtimeURI =
+ new StringConfigValue("transitclock.avl.gtfsRealtimeFeedURI",
+ "file:///C:/Users/Mike/gtfsRealtimeData",
+ "The URI of the GTFS-realtime feed to use.");
+
+ /********************** Member Functions **************************/
+
+ /**
+ * @param projectId
+ */
+ public GtfsRealtimeModule(String projectId) {
+ super(projectId);
+ // GTFS-realtime is already binary so don't want to get compressed
+ // version since that would just be a waste.
+ useCompression = false;
+ }
+
+ /**
+ * Reads and processes the data. Called by AvlModule.run().
+ * Reading GTFS-realtime doesn't use InputSteram so overriding
+ * getAndProcessData().
+ */
+ @Override
+ protected void getAndProcessData() {
+
+ String[] urls = getGtfsRealtimeURI().split(",");
+
+ int assignments = 0;
+ int records = 0;
+ for (String urlStr : urls) {
+ try {
+ logger.info("reading {}", urlStr);
+ List avlReports = GtfsRtVehiclePositionsReader
+ .getAvlReports(urlStr);
+ logger.info("read complete");
+ for (AvlReport avlReport : avlReports) {
+ processAvlReport(avlReport);
+ records++;
+ if (avlReport.getAssignmentId() != null)
+ assignments++;
+ }
+ logger.info("processed {} reports for feed {}", avlReports.size(), urlStr);
+ } catch (Exception any) {
+ logger.error("issues processing feed {}:{}", urlStr, any, any);
+ }
+
+ }
+ MonitoringService.getInstance().averageMetric("PredictionAvlInputRecords", records);
+ MonitoringService.getInstance().averageMetric("PredictionAvlInputAssignments", assignments);
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.transitclock.avl.AvlModule#processData(java.io.InputStream)
+ */
+ @Override
+ protected Collection processData(InputStream inputStream)
+ throws Exception {
+ return null; // we've overriden getAndProcessData so this need not do anything
+ }
+
+ /**
+ * Just for debugging
+ */
+ public static void main(String[] args) {
+ // Create a GtfsRealtimeModule for testing
+ Module.start("org.transitclock.avl.GtfsRealtimeModule");
+ }
+
+}
diff --git a/transitclock/src/main/java/org/transitclock/avl/GtfsRealtimeTripUpdatesModule.java b/transitclock/src/main/java/org/transitclock/avl/GtfsRealtimeTripUpdatesModule.java
new file mode 100644
index 000000000..d30ec4e91
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/avl/GtfsRealtimeTripUpdatesModule.java
@@ -0,0 +1,74 @@
+/*
+ * 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 .
+ */
+package org.transitclock.avl;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.transitclock.feed.gtfsRt.GtfsRtTripUpdatesReader;
+import org.transitclock.modules.Module;
+import org.transitclock.trip.PollUrlTripModule;
+
+/**
+ * For reading in feed of GTFS-realtime AVL data. Is used for both realtime
+ * feeds and for when reading in a giant batch of data.
+ *
+ * @author SkiBu Smith
+ *
+ */
+public class GtfsRealtimeTripUpdatesModule extends PollUrlTripModule {
+
+
+ private static final Logger logger = LoggerFactory
+ .getLogger(GtfsRealtimeTripUpdatesModule.class);
+
+ private GtfsRtTripUpdatesReader reader;
+
+
+ /********************** Member Functions **************************/
+
+ /**
+ * @param projectId
+ */
+ public GtfsRealtimeTripUpdatesModule(String projectId) {
+ super(projectId);
+ // GTFS-realtime is already binary so don't want to get compressed
+ // version since that would just be a waste.
+ reader = new GtfsRtTripUpdatesReader();
+ }
+
+ /**
+ * Reads and processes the data. Called by AvlModule.run().
+ * Reading GTFS-realtime doesn't use InputSteram so overriding
+ * getAndProcessData().
+ */
+ @Override
+ protected void getAndProcessData(String url) {
+ logger.info("reading {}", url);
+ reader.process(url);
+ logger.info("read complete");
+ logger.info("processed feed {}", url);
+ }
+
+ /**
+ * Just for debugging
+ */
+ public static void main(String[] args) {
+ // Create a GtfsRealtimeModule for testing
+ Module.start("org.transitclock.avl.GtfsRealtimeTripUpdatesModule");
+ }
+
+}
diff --git a/transitime/src/main/java/org/transitime/avl/NextBusAvlModule.java b/transitclock/src/main/java/org/transitclock/avl/NextBusAvlModule.java
similarity index 88%
rename from transitime/src/main/java/org/transitime/avl/NextBusAvlModule.java
rename to transitclock/src/main/java/org/transitclock/avl/NextBusAvlModule.java
index 7f1c2f085..8741f2253 100644
--- a/transitime/src/main/java/org/transitime/avl/NextBusAvlModule.java
+++ b/transitclock/src/main/java/org/transitclock/avl/NextBusAvlModule.java
@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.avl;
+package org.transitclock.avl;
import java.util.ArrayList;
import java.util.Collection;
@@ -24,15 +24,15 @@
import org.jdom2.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.transitime.config.BooleanConfigValue;
-import org.transitime.config.IntegerConfigValue;
-import org.transitime.config.StringConfigValue;
-import org.transitime.db.structs.AvlReport;
-import org.transitime.db.structs.AvlReport.AssignmentType;
-import org.transitime.modules.Module;
-import org.transitime.utils.Geo;
-import org.transitime.utils.MathUtils;
-import org.transitime.utils.Time;
+import org.transitclock.config.BooleanConfigValue;
+import org.transitclock.config.IntegerConfigValue;
+import org.transitclock.config.StringConfigValue;
+import org.transitclock.db.structs.AvlReport;
+import org.transitclock.db.structs.AvlReport.AssignmentType;
+import org.transitclock.modules.Module;
+import org.transitclock.utils.Geo;
+import org.transitclock.utils.MathUtils;
+import org.transitclock.utils.Time;
/**
* Reads AVL data from a NextBus AVL feed and processes each AVL report.
@@ -44,7 +44,7 @@ public class NextBusAvlModule extends XmlPollingAvlModule {
// Parameter that specifies URL of the NextBus feed.
private static StringConfigValue nextBusFeedUrl =
- new StringConfigValue("transitime.avl.nextbus.url",
+ new StringConfigValue("transitclock.avl.nextbus.url",
"http://webservices.nextbus.com/service/publicXMLFeed",
"The URL of the NextBus feed to use.");
private static String getNextBusFeedUrl() {
@@ -54,9 +54,9 @@ private static String getNextBusFeedUrl() {
// Config param that specifies the agency name to use as part
// of the NextBus feed URL.
private static StringConfigValue agencyNameForFeed =
- new StringConfigValue("transitime.avl.nextbus.agencyNameForFeed",
+ new StringConfigValue("transitclock.avl.nextbus.agencyNameForFeed",
"If set then specifies the agency name to use for the "
- + "feed. If not set then the transitime.core.agencyId "
+ + "feed. If not set then the transitclock.core.agencyId "
+ "is used.");
protected String getAgencyNameForFeed() {
return agencyNameForFeed.getValue();
@@ -64,7 +64,7 @@ protected String getAgencyNameForFeed() {
private static BooleanConfigValue useTripShortNameForAssignment =
new BooleanConfigValue(
- "transitime.avl.nextbus.useTripShortNameForAssignment",
+ "transitclock.avl.nextbus.useTripShortNameForAssignment",
false,
"For some agencies the block info in the feed doesn't "
+ "match the GTFS data. For these can sometimes use the "
@@ -78,7 +78,7 @@ protected String getAgencyNameForFeed() {
// decreased.
private static IntegerConfigValue apiClockSkewMsecs =
new IntegerConfigValue(
- "transitime.avl.nextbus.apiClockSkewMsecs",
+ "transitclock.avl.nextbus.apiClockSkewMsecs",
0,
"Determining GPS time from API is kludgey. Only have "
+ "secsSinceReport attribute in API. Sometimes, probably "
@@ -92,7 +92,7 @@ protected String getAgencyNameForFeed() {
// So can just get data since last query. Initialize so when first called
// get data for last 10 minutes. Definitely don't want to use t=0 because
// then can end up with some really old reports.
- private long previousTime = System.currentTimeMillis() - 10*Time.MS_PER_MIN;
+ private long previousTime = System.currentTimeMillis() - 1*Time.MS_PER_MIN;
private static final Logger logger =
LoggerFactory.getLogger(NextBusAvlModule.class);
@@ -268,7 +268,7 @@ protected String processBlockId(String originalBlockIdFromFeed) {
*/
public static void main(String[] args) {
// Create a NextBusAvlModue for testing
- Module.start("org.transitime.avl.NextBusAvlModule");
+ Module.start("org.transitclock.avl.NextBusAvlModule");
}
}
diff --git a/transitclock/src/main/java/org/transitclock/avl/NextBusBarefootAvlModule.java b/transitclock/src/main/java/org/transitclock/avl/NextBusBarefootAvlModule.java
new file mode 100644
index 000000000..f4faf66ab
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/avl/NextBusBarefootAvlModule.java
@@ -0,0 +1,97 @@
+package org.transitclock.avl;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.text.SimpleDateFormat;
+import java.util.Collection;
+
+import org.json.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.transitclock.db.structs.AvlReport;
+
+import com.esri.core.geometry.Point;
+
+public class NextBusBarefootAvlModule extends NextBusAvlModule {
+ private static final Logger logger = LoggerFactory
+ .getLogger(NextBusBarefootAvlModule.class);
+ public NextBusBarefootAvlModule(String agencyId) {
+ super(agencyId);
+ }
+
+ @Override
+ protected void processAvlReports(Collection avlReports) {
+ forwardAvlReports(avlReports);
+ }
+
+
+ protected void forwardAvlReports(Collection avlReportsReadIn) {
+ for(AvlReport avlReport:avlReportsReadIn)
+ {
+ sendUpdate(avlReport);
+ }
+ }
+
+ public void sendUpdate(AvlReport avlReport) {
+ try {
+ JSONObject report = new JSONObject();
+
+ InetAddress host = InetAddress.getLocalHost();
+
+ int port = 1234;
+
+ report.put("id", avlReport.getVehicleId());
+
+ SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ssZ");
+
+ report.put("time", df.format(avlReport.getDate()));
+
+ Point point = new Point();
+ point.setX(avlReport.getLon());
+ point.setY(avlReport.getLat());
+
+ report.put("point", "POINT(" + avlReport.getLon() + " " + avlReport.getLat() + ")");
+ // report.put("point", GeometryEngine.geometryToGeoJson(point));
+
+ sendBareFootSample(host, port, report);
+
+ } catch (Exception e) {
+ logger.error("Problem when sending samples to barefoot.", e);
+ }
+ }
+
+ private void sendBareFootSample(InetAddress host, int port, JSONObject sample) throws Exception {
+ int trials = 120;
+ int timeout = 500;
+ Socket client = null;
+ // TODO Will need to leave socket open.
+ while (client == null || !client.isConnected()) {
+ try {
+ client = new Socket(host, port);
+ } catch (IOException e) {
+ Thread.sleep(timeout);
+
+ if (trials == 0) {
+ logger.error(e.getMessage());
+ client.close();
+ throw new IOException();
+ } else {
+ trials -= 1;
+ }
+ }
+ }
+ PrintWriter writer = new PrintWriter(client.getOutputStream());
+ BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
+ writer.println(sample.toString());
+ writer.flush();
+
+ String code = reader.readLine();
+ if (!code.equals("SUCCESS")) {
+ throw new Exception("Barefoot server did not respond with SUCCESS. Code="+code);
+ }
+ }
+}
diff --git a/transitime/src/main/java/org/transitime/avl/NmeaGpsLocation.java b/transitclock/src/main/java/org/transitclock/avl/NmeaGpsLocation.java
similarity index 99%
rename from transitime/src/main/java/org/transitime/avl/NmeaGpsLocation.java
rename to transitclock/src/main/java/org/transitclock/avl/NmeaGpsLocation.java
index 8102f858a..4fef93298 100644
--- a/transitime/src/main/java/org/transitime/avl/NmeaGpsLocation.java
+++ b/transitclock/src/main/java/org/transitclock/avl/NmeaGpsLocation.java
@@ -15,7 +15,7 @@
* along with Transitime.org . If not, see .
*/
-package org.transitime.avl;
+package org.transitclock.avl;
import java.text.DateFormat;
import java.text.ParseException;
@@ -25,7 +25,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.transitime.db.structs.Location;
+import org.transitclock.db.structs.Location;
/**
* For parsing a NMEA command into a GPS location.
diff --git a/transitclock/src/main/java/org/transitclock/avl/PlaybackModule.java b/transitclock/src/main/java/org/transitclock/avl/PlaybackModule.java
new file mode 100644
index 000000000..aacd593b9
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/avl/PlaybackModule.java
@@ -0,0 +1,288 @@
+/*
+ * 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 .
+ */
+package org.transitclock.avl;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.TimeZone;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.transitclock.applications.Core;
+import org.transitclock.config.BooleanConfigValue;
+import org.transitclock.config.IntegerConfigValue;
+import org.transitclock.config.StringConfigValue;
+import org.transitclock.core.AvlProcessor;
+import org.transitclock.db.structs.AvlReport;
+import org.transitclock.modules.Module;
+import org.transitclock.utils.IntervalTimer;
+import org.transitclock.utils.PlaybackIntervalTimer;
+import org.transitclock.utils.Time;
+
+/**
+ * For running the system in "playback mode" where AVL data is read from the
+ * database instead of from a realtime AVL feed. Useful for debugging the system
+ * software because can easily debug what is happening for a particular vehicle
+ * at a particular time.
+ *
+ * @author SkiBu Smith
+ *
+ */
+public class PlaybackModule extends Module {
+
+ // Get 5 minutes worth of AVL data at a time
+ private final static long DB_POLLING_TIME_MSEC = 5 * Time.MS_PER_MIN;
+
+ // For keeping track of beginning of timespan for doing query
+ protected long dbReadBeginTime;
+
+ /*********** Configurable Parameters for this module ***********/
+ private static String getPlaybackVehicleId() {
+ return playbackVehicleId.getValue();
+ }
+ private static StringConfigValue playbackVehicleId =
+ new StringConfigValue("transitclock.avl.playbackVehicleId",
+ "",
+ "ID of vehicle to playback.");
+
+ private static String getPlaybackStartTimeStr() {
+ return playbackStartTimeStr.getValue();
+ }
+ protected static StringConfigValue playbackStartTimeStr =
+ new StringConfigValue("transitclock.avl.playbackStartTime",
+ "",
+ "Date and time of when to start the playback.");
+
+ protected static StringConfigValue playbackEndTimeStr =
+ new StringConfigValue("transitclock.avl.playbackEndTime",
+ "",
+ "Date and time of when to end the playback.");
+
+ protected static BooleanConfigValue playbackRealtime =
+ new BooleanConfigValue("transitclock.avl.playbackRealtime",
+ false,
+ "Playback at normal time speed rather than as fast as possible.");
+
+ protected static IntegerConfigValue playbackSkipIntervalMinutes =
+ new IntegerConfigValue("transitclock.avl.playbackSkipIntervalMinutes",
+ 120,
+ "If no data for this amount of minutes skip forward in time.");
+
+ protected static IntegerConfigValue playbackStartDelayMinutes=
+ new IntegerConfigValue("transitclock.avl.playbackStartDelayMinutes",
+ 3,
+ "Time to sleep before starting. Gives some time to connect remote debugger.");
+
+ /********************* Logging **************************/
+ private static final Logger logger =
+ LoggerFactory.getLogger(PlaybackModule.class);
+
+
+ /********************** Member Functions **************************/
+
+ /**
+ * @param agencyId
+ */
+ public PlaybackModule(String agencyId) {
+ super(agencyId);
+
+ // Make sure params are set
+ if (getPlaybackVehicleId() == null
+ || getPlaybackVehicleId().isEmpty()
+ || getPlaybackStartTimeStr() == null
+ || getPlaybackStartTimeStr().isEmpty()) {
+ logger.warn("Parameters not set. See log file for details.");
+ }
+
+ // Initialize the dbReadBeingTime member
+ this.dbReadBeginTime = parsePlaybackStartTime(getPlaybackStartTimeStr());
+ }
+
+ private static long parsePlaybackStartTime(String playbackStartTimeStr) {
+ try {
+ long playbackStartTime = Time.parse(playbackStartTimeStr).getTime();
+
+ // If specified time is in the future then reject.
+ if (playbackStartTime > System.currentTimeMillis()) {
+ logger.error("Playback start time \"{}\" specified by " +
+ "transitclock.avl.playbackStartTime parameter is in " +
+ "the future and therefore invalid!",
+ playbackStartTimeStr);
+ System.exit(-1);
+ }
+
+ return playbackStartTime;
+ } catch (java.text.ParseException e) {
+ logger.error("Paramater -t \"{}\" specified by " +
+ "transitclock.avl.playbackStartTime parameter could not " +
+ "be parsed. Format must be \"MM-dd-yyyy HH:mm:ss\"",
+ playbackStartTimeStr);
+ System.exit(-1);
+
+ // Will never be reached because the above state exits program but
+ // needed so compiler doesn't complain.
+ return -1;
+ }
+ }
+ protected static long parsePlaybackEndTime(String playbackEndTimeStr) {
+ try {
+ long playbackEndTime = Time.parse(playbackEndTimeStr).getTime();
+
+ // If specified time is in the future then reject.
+ if (playbackEndTime > System.currentTimeMillis()) {
+ logger.error("Playback end time \"{}\" specified by " +
+ "transitclock.avl.playbackEndTime parameter is in " +
+ "the future and therefore invalid!",
+ playbackEndTimeStr);
+ System.exit(-1);
+ }
+
+ return playbackEndTime;
+ } catch (java.text.ParseException e) {
+ logger.error("Paramater -t \"{}\" specified by " +
+ "transitclock.avl.playbackEndTime parameter could not " +
+ "be parsed. Format must be \"MM-dd-yyyy HH:mm:ss\"",
+ playbackEndTimeStr);
+ System.exit(-1);
+
+ // Will never be reached because the above state exits program but
+ // needed so compiler doesn't complain.
+ return -1;
+ }
+ }
+ /**
+ * Gets a batch of AVl data from the database
+ * @return
+ */
+ protected List getBatchOfAvlReportsFromDb() {
+ // The times that should be reading data for
+ long start = dbReadBeginTime;
+ long end = dbReadBeginTime + DB_POLLING_TIME_MSEC;
+
+ logger.info("PlaybackModule getting batch of AVLReports for " +
+ "between beginTime={} and endTime={} " +
+ "and vehicleId={}",
+ Time.dateTimeStr(start),
+ Time.dateTimeStr(end),
+ playbackVehicleId);
+
+ List avlReports =
+ AvlReport.getAvlReportsFromDb(
+ new Date(start),
+ new Date(end),
+ getPlaybackVehicleId(),
+ "ORDER BY time");
+
+ logger.info("PlaybackModule read {} AVLReports.", avlReports.size());
+
+ // For next time this method is called.
+ dbReadBeginTime = end;
+
+ // Return results
+ return avlReports;
+ }
+
+ void sleepInIncrements(long clocktime, long sleep, long increment) throws InterruptedException
+ {
+ long counter=0;
+
+ while(counter avlReports = getBatchOfAvlReportsFromDb();
+
+ // Process the AVL Reports read in.
+ for (AvlReport avlReport : avlReports) {
+
+ if(playbackRealtime.getValue()==true)
+ {
+ if(last_avl_time>-1)
+ {
+ try {
+ // only sleep if values less than playbackSkipIntervalMinutes minutes. This is to allow it skip days/hours of missing data.
+ if((avlReport.getTime()-last_avl_time) < (playbackSkipIntervalMinutes.getValue()*Time.MS_PER_MIN))
+ {
+ //Thread.sleep(avlReport.getTime()-last_avl_time);
+ sleepInIncrements(Core.getInstance().getSystemTime(), avlReport.getTime()-last_avl_time, 10*Time.MS_PER_SEC);
+ }
+ last_avl_time=avlReport.getTime();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }else
+ {
+ last_avl_time=avlReport.getTime();
+ }
+ }
+ logger.info("Processing avlReport={}", avlReport);
+
+ // Update the Core SystemTime to use this AVL time
+ Core.getInstance().setSystemTime(avlReport.getTime());
+
+ // Do the actual processing of the AVL data
+ AvlProcessor.getInstance().processAvlReport(avlReport);
+
+ }
+ }
+ // logging here as the rest is database access dependent.
+ logger.info("Processed AVL from playbackStartTimeStr:{} to playbackEndTimeStr:{} in {} secs.",playbackStartTimeStr,playbackEndTimeStr, Time.secondsStr(timer.elapsedMsec()));
+
+
+ // Wait for database queue to be emptied before exiting.
+ while(Core.getInstance().getDbLogger().queueSize()>0)
+ {
+ try {
+ Thread.sleep(10000);
+ } catch (InterruptedException e) {
+
+ }
+ }
+
+
+ logger.info("Read in AVL in playback mode all the way up to current " +
+ "time so done. Exiting.");
+
+ System.exit(0);
+ }
+
+}
diff --git a/transitime/src/main/java/org/transitime/avl/PollUrlAvlModule.java b/transitclock/src/main/java/org/transitclock/avl/PollUrlAvlModule.java
similarity index 65%
rename from transitime/src/main/java/org/transitime/avl/PollUrlAvlModule.java
rename to transitclock/src/main/java/org/transitclock/avl/PollUrlAvlModule.java
index b0c4f2edf..72c28ec40 100644
--- a/transitime/src/main/java/org/transitime/avl/PollUrlAvlModule.java
+++ b/transitclock/src/main/java/org/transitclock/avl/PollUrlAvlModule.java
@@ -15,7 +15,21 @@
* along with Transitime.org . If not, see .
*/
-package org.transitime.avl;
+package org.transitclock.avl;
+
+import org.apache.commons.codec.binary.Base64;
+import org.json.JSONException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.transitclock.config.BooleanConfigValue;
+import org.transitclock.config.StringConfigValue;
+import org.transitclock.configData.AgencyConfig;
+import org.transitclock.configData.AvlConfig;
+import org.transitclock.db.structs.AvlReport;
+import org.transitclock.logging.Markers;
+import org.transitclock.utils.IntervalTimer;
+import org.transitclock.utils.JsonUtils;
+import org.transitclock.utils.Time;
import java.io.BufferedReader;
import java.io.IOException;
@@ -27,19 +41,6 @@
import java.util.Collection;
import java.util.zip.GZIPInputStream;
-import org.apache.commons.codec.binary.Base64;
-import org.json.JSONException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.transitime.config.BooleanConfigValue;
-import org.transitime.config.StringConfigValue;
-import org.transitime.configData.AgencyConfig;
-import org.transitime.configData.AvlConfig;
-import org.transitime.db.structs.AvlReport;
-import org.transitime.logging.Markers;
-import org.transitime.utils.IntervalTimer;
-import org.transitime.utils.Time;
-
/**
* Subclass of AvlModule to be used when reading AVL data from a feed. Calls the
* abstract method getAndProcessData() for the subclass to actually get data
@@ -49,27 +50,27 @@
* an AvlClient. If not in JMS mode then uses a BoundedExecutor with multiple
* threads to directly call AvlClient.run().
*
- * @author Michael Smith (michael@transitime.org)
+ * @author Michael Smith (michael@transitclock.org)
*
*/
public abstract class PollUrlAvlModule extends AvlModule {
private static StringConfigValue url =
- new StringConfigValue("transitime.avl.url",
+ new StringConfigValue("transitclock.avl.url",
"The URL of the AVL feed to poll.");
private static StringConfigValue authenticationUser =
- new StringConfigValue("transitime.avl.authenticationUser",
+ new StringConfigValue("transitclock.avl.authenticationUser",
"If authentication used for the feed then this specifies "
+ "the user.");
private static StringConfigValue authenticationPassword =
- new StringConfigValue("transitime.avl.authenticationPassword",
+ new StringConfigValue("transitclock.avl.authenticationPassword",
"If authentication used for the feed then this specifies "
+ "the password.");
private static BooleanConfigValue shouldProcessAvl =
- new BooleanConfigValue("transitime.avl.shouldProcessAvl",
+ new BooleanConfigValue("transitclock.avl.shouldProcessAvl",
true,
"Usually want to process the AVL data when it is read in "
+ "so that predictions and such are generated. But if "
@@ -139,17 +140,7 @@ protected abstract Collection processData(InputStream in)
*/
protected String getJsonString(InputStream in) throws IOException,
JSONException {
- BufferedReader streamReader =
- new BufferedReader(new InputStreamReader(in, "UTF-8"));
- StringBuilder responseStrBuilder = new StringBuilder();
-
- String inputStr;
- while ((inputStr = streamReader.readLine()) != null)
- responseStrBuilder.append(inputStr);
-
- String responseStr = responseStrBuilder.toString();
- logger.debug("JSON={}", responseStr);
- return responseStr;
+ return JsonUtils.getJsonString(in);
}
/**
@@ -170,63 +161,64 @@ protected void getAndProcessData() throws Exception {
IntervalTimer timer = new IntervalTimer();
// Get from the AVL feed subclass the URL to use for this feed
- String fullUrl = getUrl();
-
- // Log what is happening
- logger.info("Getting data from feed using url=" + fullUrl);
-
- // Create the connection
- URL url = new URL(fullUrl);
- URLConnection con = url.openConnection();
-
- // Set the timeout so don't wait forever
- int timeoutMsec = AvlConfig.getAvlFeedTimeoutInMSecs();
- con.setConnectTimeout(timeoutMsec);
- con.setReadTimeout(timeoutMsec);
-
- // Request compressed data to reduce bandwidth used
- if (useCompression)
- con.setRequestProperty("Accept-Encoding", "gzip,deflate");
-
- // If authentication being used then set user and password
- if (authenticationUser.getValue() != null
- && authenticationPassword.getValue() != null) {
- String authString =
- authenticationUser.getValue() + ":"
- + authenticationPassword.getValue();
- byte[] authEncBytes =
- Base64.encodeBase64(authString.getBytes());
- String authStringEnc = new String(authEncBytes);
- con.setRequestProperty("Authorization", "Basic "
- + authStringEnc);
- }
-
- // Set any additional AVL feed specific request headers
- setRequestHeaders(con);
-
- // Create appropriate input stream depending on whether content is
- // compressed or not
- InputStream in = con.getInputStream();
- if ("gzip".equals(con.getContentEncoding())) {
- in = new GZIPInputStream(in);
- logger.debug("Returned data is compressed");
- } else {
- logger.debug("Returned data is NOT compressed");
- }
+ String[] fullUrls = getUrl().split(",");
+ for (String fullUrl : fullUrls) {
+ // Log what is happening
+ logger.warn("Getting data from feed using url=" + fullUrl);
- // For debugging
- logger.debug("Time to access inputstream {} msec",
- timer.elapsedMsec());
-
- // Call the abstract method to actually process the data
- timer.resetTimer();
- Collection avlReportsReadIn = processData(in);
- in.close();
- logger.debug("Time to parse document {} msec", timer.elapsedMsec());
-
- // Process all the reports read in
- if (shouldProcessAvl.getValue())
- processAvlReports(avlReportsReadIn);
+ // Create the connection
+ URL url = new URL(fullUrl);
+ URLConnection con = url.openConnection();
+
+ // Set the timeout so don't wait forever
+ int timeoutMsec = AvlConfig.getAvlFeedTimeoutInMSecs();
+ con.setConnectTimeout(timeoutMsec);
+ con.setReadTimeout(timeoutMsec);
+
+ // Request compressed data to reduce bandwidth used
+ if (useCompression)
+ con.setRequestProperty("Accept-Encoding", "gzip,deflate");
+
+ // If authentication being used then set user and password
+ if (authenticationUser.getValue() != null
+ && authenticationPassword.getValue() != null) {
+ String authString =
+ authenticationUser.getValue() + ":"
+ + authenticationPassword.getValue();
+ byte[] authEncBytes =
+ Base64.encodeBase64(authString.getBytes());
+ String authStringEnc = new String(authEncBytes);
+ con.setRequestProperty("Authorization", "Basic "
+ + authStringEnc);
+ }
+
+ // Set any additional AVL feed specific request headers
+ setRequestHeaders(con);
+
+ // Create appropriate input stream depending on whether content is
+ // compressed or not
+ InputStream in = con.getInputStream();
+ if ("gzip".equals(con.getContentEncoding())) {
+ in = new GZIPInputStream(in);
+ logger.debug("Returned data is compressed");
+ } else {
+ logger.debug("Returned data is NOT compressed");
+ }
+
+ // For debugging
+ logger.warn("Time to access inputstream {} msec",
+ timer.elapsedMsec());
+
+ // Call the abstract method to actually process the data
+ timer.resetTimer();
+ Collection avlReportsReadIn = processData(in);
+ in.close();
+ logger.warn("Time to parse document {} msec", timer.elapsedMsec());
+
+ // Process all the reports read in
+ if (shouldProcessAvl.getValue())
+ processAvlReports(avlReportsReadIn);
+ }
}
/**
@@ -260,9 +252,9 @@ public void run() {
// Wait appropriate amount of time till poll again
long elapsedMsec = timer.elapsedMsec();
- long sleepTime =
+ long sleepTime = (long) (
AvlConfig.getSecondsBetweenAvlFeedPolling()*Time.MS_PER_SEC -
- elapsedMsec;
+ elapsedMsec);
if (sleepTime < 0) {
logger.warn("Supposed to have a polling rate of " +
AvlConfig.getSecondsBetweenAvlFeedPolling()*Time.MS_PER_SEC +
diff --git a/transitime/src/main/java/org/transitime/avl/TaipGpsLocation.java b/transitclock/src/main/java/org/transitclock/avl/TaipGpsLocation.java
similarity index 99%
rename from transitime/src/main/java/org/transitime/avl/TaipGpsLocation.java
rename to transitclock/src/main/java/org/transitclock/avl/TaipGpsLocation.java
index 8dca3c38a..00f2ae967 100644
--- a/transitime/src/main/java/org/transitime/avl/TaipGpsLocation.java
+++ b/transitclock/src/main/java/org/transitclock/avl/TaipGpsLocation.java
@@ -15,14 +15,14 @@
* along with Transitime.org . If not, see .
*/
-package org.transitime.avl;
+package org.transitclock.avl;
import java.util.Date;
import java.util.TimeZone;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.transitime.utils.Time;
+import org.transitclock.utils.Time;
/**
* For parsing Trimble TAIP format GPS commands into a Java object. It only
diff --git a/transitime/src/main/java/org/transitime/avl/TranslocAvlModule.java b/transitclock/src/main/java/org/transitclock/avl/TranslocAvlModule.java
similarity index 89%
rename from transitime/src/main/java/org/transitime/avl/TranslocAvlModule.java
rename to transitclock/src/main/java/org/transitclock/avl/TranslocAvlModule.java
index 6899d9561..fc4f320cd 100644
--- a/transitime/src/main/java/org/transitime/avl/TranslocAvlModule.java
+++ b/transitclock/src/main/java/org/transitclock/avl/TranslocAvlModule.java
@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.avl;
+package org.transitclock.avl;
import java.io.InputStream;
import java.net.URLConnection;
@@ -26,10 +26,10 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.transitime.config.StringConfigValue;
-import org.transitime.db.structs.AvlReport;
-import org.transitime.modules.Module;
-import org.transitime.utils.Geo;
+import org.transitclock.config.StringConfigValue;
+import org.transitclock.db.structs.AvlReport;
+import org.transitclock.modules.Module;
+import org.transitclock.utils.Geo;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -45,18 +45,18 @@
*/
public class TranslocAvlModule extends PollUrlAvlModule {
private static StringConfigValue feedUrl =
- new StringConfigValue("transitime.avl.transloc.url",
+ new StringConfigValue("transitclock.avl.transloc.url",
"https://transloc-api-1-2.p.mashape.com/",
"The URL of the Transloc API to use.");
private static StringConfigValue feedAgencyId =
- new StringConfigValue("transitime.avl.transloc.agencyId",
+ new StringConfigValue("transitclock.avl.transloc.agencyId",
"255",
"The number Transloc agency ID obtained using the Transloc "
+ "API agencies.json command.");
private static StringConfigValue apiKey =
- new StringConfigValue("transitime.avl.transloc.apiKey",
+ new StringConfigValue("transitclock.avl.transloc.apiKey",
"tXHlJmqevJmsh4q37xKuv3k2vfZ5p1VbAvPjsnwErq18jMCSmb",
"The API key for the Transloc API.");
@@ -172,7 +172,7 @@ protected Collection processData(InputStream in)
* Just for debugging
*/
public static void main(String[] args) {
- Module.start("org.transitime.avl.TranslocAvlModule");
+ Module.start("org.transitclock.avl.TranslocAvlModule");
}
}
diff --git a/transitime/src/main/java/org/transitime/avl/WorkwaveAvlModule.java b/transitclock/src/main/java/org/transitclock/avl/WorkwaveAvlModule.java
similarity index 92%
rename from transitime/src/main/java/org/transitime/avl/WorkwaveAvlModule.java
rename to transitclock/src/main/java/org/transitclock/avl/WorkwaveAvlModule.java
index 9428ee874..7313413d1 100644
--- a/transitime/src/main/java/org/transitime/avl/WorkwaveAvlModule.java
+++ b/transitclock/src/main/java/org/transitclock/avl/WorkwaveAvlModule.java
@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.avl;
+package org.transitclock.avl;
import java.io.InputStream;
import java.net.URL;
@@ -32,10 +32,10 @@
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.transitime.config.StringConfigValue;
-import org.transitime.configData.AvlConfig;
-import org.transitime.db.structs.AvlReport;
-import org.transitime.modules.Module;
+import org.transitclock.config.StringConfigValue;
+import org.transitclock.configData.AvlConfig;
+import org.transitclock.db.structs.AvlReport;
+import org.transitclock.modules.Module;
/**
* AVL module for Workwave AVL feed. Polls JSON feed. First gets a valid
@@ -56,7 +56,7 @@ public class WorkwaveAvlModule extends PollUrlAvlModule {
new SimpleDateFormat("MM/dd/yy hh:mm:ss a");
private static StringConfigValue apiKey =
- new StringConfigValue("transitime.avl.workwaveApiKey",
+ new StringConfigValue("transitclock.avl.workwaveApiKey",
"The API key to use when getting session ID for Workwave "
+ "AVL feed.");
@@ -203,7 +203,7 @@ protected Collection processData(InputStream in) throws Exception {
* Just for debugging
*/
public static void main(String[] args) {
- Module.start("org.transitime.avl.WorkwaveAvlModule");
+ Module.start("org.transitclock.avl.WorkwaveAvlModule");
}
-}
+}
diff --git a/transitime/src/main/java/org/transitime/avl/XmlPollingAvlModule.java b/transitclock/src/main/java/org/transitclock/avl/XmlPollingAvlModule.java
similarity index 93%
rename from transitime/src/main/java/org/transitime/avl/XmlPollingAvlModule.java
rename to transitclock/src/main/java/org/transitclock/avl/XmlPollingAvlModule.java
index bd57e6366..220f2ca9a 100644
--- a/transitime/src/main/java/org/transitime/avl/XmlPollingAvlModule.java
+++ b/transitclock/src/main/java/org/transitclock/avl/XmlPollingAvlModule.java
@@ -14,14 +14,14 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.avl;
+package org.transitclock.avl;
import java.io.InputStream;
import java.util.Collection;
import org.jdom2.Document;
import org.jdom2.input.SAXBuilder;
-import org.transitime.db.structs.AvlReport;
+import org.transitclock.db.structs.AvlReport;
/**
*
diff --git a/transitclock/src/main/java/org/transitclock/avl/ZeroMQAvlModule.java b/transitclock/src/main/java/org/transitclock/avl/ZeroMQAvlModule.java
new file mode 100644
index 000000000..67405ca52
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/avl/ZeroMQAvlModule.java
@@ -0,0 +1,117 @@
+package org.transitclock.avl;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.transitclock.config.IntegerConfigValue;
+import org.transitclock.config.StringConfigValue;
+import org.transitclock.db.structs.AvlReport;
+import org.transitclock.feed.zmq.ZmqQueueReaderFactory;
+import org.transitclock.modules.Module;
+import org.zeromq.ZMQ;
+
+import java.io.InputStream;
+import java.util.Collection;
+
+public class ZeroMQAvlModule extends PollUrlAvlModule {
+
+ private static final Logger logger = LoggerFactory
+ .getLogger(ZeroMQAvlModule.class);
+
+ protected ZMQ.Context _context = null;
+ protected ZMQ.Socket _socket = null;
+ protected ZMQ.Poller _poller = null;
+
+
+ public static StringConfigValue zeromqHost =
+ new StringConfigValue("transitclock.avl.zeromq.host",
+ "queue.dev.obanyc.com",
+ "The host of the ZeroMQ queue to use.");
+
+ public static IntegerConfigValue zeromqPort =
+ new IntegerConfigValue("transitclock.avl.zeromq.port",
+ 5567,
+ "The port of the ZeroMQ queue to use.");
+
+ public static StringConfigValue zeromqTopic =
+ new StringConfigValue("transitclock.avl.zeromq.topic",
+ "inference_queue",
+ "The topic of the ZeroMQ queue to use.");
+
+
+ // Initialize ZMQ with the given args
+ protected synchronized void initializeQueue(String host, String queueName,
+ Integer port) {
+ String bind = "tcp://" + host + ":" + port;
+ logger.warn("binding to " + bind + " with topic=" + queueName);
+
+ if (_context == null) {
+ _context = ZMQ.context(1);
+ _socket = _context.socket(ZMQ.SUB);
+ _poller = _context.poller(2);
+ _poller.register(_socket, ZMQ.Poller.POLLIN);
+ _socket.connect(bind);
+ _socket.subscribe(queueName.getBytes());
+ logger.warn("queue " + queueName + " is listening on " + bind);
+ }
+ }
+
+ public ZeroMQAvlModule(String agencyId){
+ super(agencyId);
+ initializeQueue(zeromqHost.getValue(), zeromqTopic.getValue(), zeromqPort.getValue());
+ }
+
+ /**
+ * Reads and processes the data. Called by AvlModule.run().
+ * Reading from ZeroMQ does use InputStream so overriding
+ * getAndProcessData().
+ */
+ @Override
+ protected void getAndProcessData() {
+
+ // prefer a java sleep to a native block
+ _poller.poll(0 * 1000); // microseconds for 2.2, milliseconds for 3.0
+ if (_poller.pollin(0)) {
+
+ String address = new String(_socket.recv(0));
+ byte[] buff = _socket.recv(0);
+
+ if(address == null || !address.equals(zeromqTopic.getValue())){
+ return;
+ }
+
+ try {
+ String contents = new String(buff);
+
+ // Convert to an AvlReport
+ AvlReport avlReport = ZmqQueueReaderFactory.getInstance().getAvlReport(zeromqTopic.getValue(), contents);
+
+ // Process the individual AVL Report
+ processAvlReport(avlReport);
+
+ } catch(Exception ex) {
+ logger.error("#####>>>>> processMessage() failed, exception was: " + ex.getMessage(), ex);
+ }
+
+ } else {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ logger.warn("exiting ZMQ thread (interrupted)");
+ return;
+ }
+ }
+ }
+
+ @Override
+ protected Collection processData(InputStream in) throws Exception {
+ return null;
+ }
+
+ /**
+ * Just for debugging
+ */
+ public static void main(String[] args) {
+ // Create a ZeroMQAvlModule for testing
+ Module.start("org.transitclock.avl.ZeroMQAvlModule");
+ }
+}
diff --git a/transitime/src/main/java/org/transitime/avl/ZonarAvlModule.java b/transitclock/src/main/java/org/transitclock/avl/ZonarAvlModule.java
similarity index 87%
rename from transitime/src/main/java/org/transitime/avl/ZonarAvlModule.java
rename to transitclock/src/main/java/org/transitclock/avl/ZonarAvlModule.java
index a34e08557..2a35b70d3 100644
--- a/transitime/src/main/java/org/transitime/avl/ZonarAvlModule.java
+++ b/transitclock/src/main/java/org/transitclock/avl/ZonarAvlModule.java
@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.avl;
+package org.transitclock.avl;
import java.util.ArrayList;
import java.util.Collection;
@@ -24,11 +24,11 @@
import org.jdom2.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.transitime.config.StringConfigValue;
-import org.transitime.db.structs.AvlReport;
-import org.transitime.modules.Module;
-import org.transitime.utils.MathUtils;
-import org.transitime.utils.Time;
+import org.transitclock.config.StringConfigValue;
+import org.transitclock.db.structs.AvlReport;
+import org.transitclock.modules.Module;
+import org.transitclock.utils.MathUtils;
+import org.transitclock.utils.Time;
/**
* Reads AVL data from Zonar XML AVL feed. Only GPS data is available. There is
@@ -40,17 +40,17 @@
public class ZonarAvlModule extends XmlPollingAvlModule {
private static StringConfigValue zonarAccount =
- new StringConfigValue("transitime.avl.zonarAccount",
+ new StringConfigValue("transitclock.avl.zonarAccount",
"The Zonar count name used as part of the domain name for "
+ "requests. Consists of 3 lower-case letters and 4 "
+ "digits, e.g. abc1234.");
private static StringConfigValue zonarUserName =
- new StringConfigValue("transitime.avl.zonarUserName",
+ new StringConfigValue("transitclock.avl.zonarUserName",
"The Zonar user name for API calls.");
private static StringConfigValue zonarPassword =
- new StringConfigValue("transitime.avl.zonarPassword",
+ new StringConfigValue("transitclock.avl.zonarPassword",
"The Zonar password for API calls.");
private static final double KM_PER_HOUR_TO_METERS_PER_SEC = 0.27777777777778;
@@ -142,7 +142,7 @@ protected String getUrl() {
*/
public static void main(String[] args) {
// Create a ZonarAvlModule for testing
- Module.start("org.transitime.avl.ZonarAvlModule");
+ Module.start("org.transitclock.avl.ZonarAvlModule");
}
}
diff --git a/transitime/src/main/java/org/transitime/avl/amigocloud/AmigoRest.java b/transitclock/src/main/java/org/transitclock/avl/amigocloud/AmigoRest.java
similarity index 95%
rename from transitime/src/main/java/org/transitime/avl/amigocloud/AmigoRest.java
rename to transitclock/src/main/java/org/transitclock/avl/amigocloud/AmigoRest.java
index db19ca9d1..87ee6081c 100644
--- a/transitime/src/main/java/org/transitime/avl/amigocloud/AmigoRest.java
+++ b/transitclock/src/main/java/org/transitclock/avl/amigocloud/AmigoRest.java
@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.avl.amigocloud;
+package org.transitclock.avl.amigocloud;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
diff --git a/transitime/src/main/java/org/transitime/avl/amigocloud/AmigoWebsocketListener.java b/transitclock/src/main/java/org/transitclock/avl/amigocloud/AmigoWebsocketListener.java
similarity index 93%
rename from transitime/src/main/java/org/transitime/avl/amigocloud/AmigoWebsocketListener.java
rename to transitclock/src/main/java/org/transitclock/avl/amigocloud/AmigoWebsocketListener.java
index 6a996a3e2..075713ab1 100644
--- a/transitime/src/main/java/org/transitime/avl/amigocloud/AmigoWebsocketListener.java
+++ b/transitclock/src/main/java/org/transitclock/avl/amigocloud/AmigoWebsocketListener.java
@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.avl.amigocloud;
+package org.transitclock.avl.amigocloud;
public interface AmigoWebsocketListener {
public void onMessage(String message);
@@ -27,4 +27,4 @@ public interface AmigoWebsocketListener {
* @param reason
*/
public void onClose(int code, String reason);
-}
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/transitime/src/main/java/org/transitime/avl/amigocloud/AmigoWebsockets.java b/transitclock/src/main/java/org/transitclock/avl/amigocloud/AmigoWebsockets.java
similarity index 94%
rename from transitime/src/main/java/org/transitime/avl/amigocloud/AmigoWebsockets.java
rename to transitclock/src/main/java/org/transitclock/avl/amigocloud/AmigoWebsockets.java
index aa3c0b3f2..ebd015393 100644
--- a/transitime/src/main/java/org/transitime/avl/amigocloud/AmigoWebsockets.java
+++ b/transitclock/src/main/java/org/transitclock/avl/amigocloud/AmigoWebsockets.java
@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License along with
* Transitime.org . If not, see .
*/
-package org.transitime.avl.amigocloud;
+package org.transitclock.avl.amigocloud;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft_10;
@@ -23,8 +23,8 @@
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.transitime.configData.AgencyConfig;
-import org.transitime.logging.Markers;
+import org.transitclock.configData.AgencyConfig;
+import org.transitclock.logging.Markers;
import java.net.URI;
import java.util.Random;
diff --git a/transitime/src/main/java/org/transitime/avl/calAmp/CalAmpAvlModule.java b/transitclock/src/main/java/org/transitclock/avl/calAmp/CalAmpAvlModule.java
similarity index 89%
rename from transitime/src/main/java/org/transitime/avl/calAmp/CalAmpAvlModule.java
rename to transitclock/src/main/java/org/transitclock/avl/calAmp/CalAmpAvlModule.java
index a1d5da859..fdc01df90 100644
--- a/transitime/src/main/java/org/transitime/avl/calAmp/CalAmpAvlModule.java
+++ b/transitclock/src/main/java/org/transitclock/avl/calAmp/CalAmpAvlModule.java
@@ -1,4 +1,4 @@
-package org.transitime.avl.calAmp;
+package org.transitclock.avl.calAmp;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
@@ -6,13 +6,13 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.transitime.avl.AvlModule;
-import org.transitime.config.IntegerConfigValue;
+import org.transitclock.avl.AvlModule;
+import org.transitclock.config.IntegerConfigValue;
public class CalAmpAvlModule extends AvlModule {
private static IntegerConfigValue calAmpFeedPort = new IntegerConfigValue(
- "transitime.avl.calAmpFeedPort", 20500,
+ "transitclock.avl.calAmpFeedPort", 20500,
"The port number for the UDP socket connection for the "
+ "CalAmp GPS tracker feed.");
diff --git a/transitime/src/main/java/org/transitime/avl/calAmp/MessageHeader.java b/transitclock/src/main/java/org/transitclock/avl/calAmp/MessageHeader.java
similarity index 95%
rename from transitime/src/main/java/org/transitime/avl/calAmp/MessageHeader.java
rename to transitclock/src/main/java/org/transitclock/avl/calAmp/MessageHeader.java
index 0d7ca609a..30c604cbf 100644
--- a/transitime/src/main/java/org/transitime/avl/calAmp/MessageHeader.java
+++ b/transitclock/src/main/java/org/transitclock/avl/calAmp/MessageHeader.java
@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.avl.calAmp;
+package org.transitclock.avl.calAmp;
/**
* For processing CalAmp message header.
diff --git a/transitime/src/main/java/org/transitime/avl/calAmp/MiniEventReport.java b/transitclock/src/main/java/org/transitclock/avl/calAmp/MiniEventReport.java
similarity index 92%
rename from transitime/src/main/java/org/transitime/avl/calAmp/MiniEventReport.java
rename to transitclock/src/main/java/org/transitclock/avl/calAmp/MiniEventReport.java
index cac7dc70f..2120a61bc 100644
--- a/transitime/src/main/java/org/transitime/avl/calAmp/MiniEventReport.java
+++ b/transitclock/src/main/java/org/transitclock/avl/calAmp/MiniEventReport.java
@@ -14,16 +14,16 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.avl.calAmp;
+package org.transitclock.avl.calAmp;
import java.util.Date;
-import org.transitime.avl.AvlExecutor;
-import org.transitime.core.dataCache.VehicleDataCache;
-import org.transitime.db.structs.AvlReport;
-import org.transitime.db.structs.VehicleConfig;
-import org.transitime.utils.Geo;
-import org.transitime.utils.Time;
+import org.transitclock.avl.AvlExecutor;
+import org.transitclock.core.dataCache.VehicleDataCache;
+import org.transitclock.db.structs.AvlReport;
+import org.transitclock.db.structs.VehicleConfig;
+import org.transitclock.utils.Geo;
+import org.transitclock.utils.Time;
/**
* Contains info for a CalAmp mini event report which is a simple GPS report.
diff --git a/transitime/src/main/java/org/transitime/avl/calAmp/OptionsHeader.java b/transitclock/src/main/java/org/transitclock/avl/calAmp/OptionsHeader.java
similarity index 95%
rename from transitime/src/main/java/org/transitime/avl/calAmp/OptionsHeader.java
rename to transitclock/src/main/java/org/transitclock/avl/calAmp/OptionsHeader.java
index 22ddae50f..926cb7b7c 100644
--- a/transitime/src/main/java/org/transitime/avl/calAmp/OptionsHeader.java
+++ b/transitclock/src/main/java/org/transitclock/avl/calAmp/OptionsHeader.java
@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.avl.calAmp;
+package org.transitclock.avl.calAmp;
/**
* For processing CalAmp options header.
diff --git a/transitime/src/main/java/org/transitime/avl/calAmp/Report.java b/transitclock/src/main/java/org/transitclock/avl/calAmp/Report.java
similarity index 96%
rename from transitime/src/main/java/org/transitime/avl/calAmp/Report.java
rename to transitclock/src/main/java/org/transitclock/avl/calAmp/Report.java
index 2a0738771..2423c9109 100644
--- a/transitime/src/main/java/org/transitime/avl/calAmp/Report.java
+++ b/transitclock/src/main/java/org/transitclock/avl/calAmp/Report.java
@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.avl.calAmp;
+package org.transitclock.avl.calAmp;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
@@ -208,4 +208,4 @@ public static void main(String args[]) {
}
}
-}
\ No newline at end of file
+}
diff --git a/transitime/src/main/java/org/transitime/avl/calAmp/package-info.java b/transitclock/src/main/java/org/transitclock/avl/calAmp/package-info.java
similarity index 93%
rename from transitime/src/main/java/org/transitime/avl/calAmp/package-info.java
rename to transitclock/src/main/java/org/transitclock/avl/calAmp/package-info.java
index 42912e2d0..7db7d7910 100644
--- a/transitime/src/main/java/org/transitime/avl/calAmp/package-info.java
+++ b/transitclock/src/main/java/org/transitclock/avl/calAmp/package-info.java
@@ -21,4 +21,4 @@
* @author SkiBu Smith
*
*/
-package org.transitime.avl.calAmp;
\ No newline at end of file
+package org.transitclock.avl.calAmp;
\ No newline at end of file
diff --git a/transitime/src/main/java/org/transitime/avl/package-info.java b/transitclock/src/main/java/org/transitclock/avl/package-info.java
similarity index 87%
rename from transitime/src/main/java/org/transitime/avl/package-info.java
rename to transitclock/src/main/java/org/transitclock/avl/package-info.java
index c0c4bb43f..9ec29e18a 100644
--- a/transitime/src/main/java/org/transitime/avl/package-info.java
+++ b/transitclock/src/main/java/org/transitclock/avl/package-info.java
@@ -45,21 +45,21 @@
* to update a configuration file when a new agency is dealt with. This
* makes maintaining JMS much simpler.
*
- * An AVL feed inherits from the org.transitime.modules.Module class. This
+ * An AVL feed inherits from the org.transitclock.modules.Module class. This
* means it is very easy to start. Also means that the Module.getProjectId()
* method is used specify which database the data is to be written to and
* the name of the JMS topic for the feed. This also means that the
- * command line param -Dtransitime.modules.optionalModulesList=XXX is
+ * command line param -Dtransitclock.modules.optionalModulesList=XXX is
* used to start up the desired AVL modules.
*
* Configuration parameters for AVL modules are listed in the AvlConfig.java
- * class. Important params include -Dtransitime.db.storeDataInDatabase=false
+ * class. Important params include -Dtransitclock.db.storeDataInDatabase=false
* if the generated data such as arrivals/departures should not be stored
* in the database. This is important for when in playback mode for
* debugging or such. Another important parameter is
- * -Dtransitime.avl.shouldUseJms=true if you want to use JMS. In that case you
+ * -Dtransitclock.avl.shouldUseJms=true if you want to use JMS. In that case you
* also need to specify both an AVL feed module and a JMS client module, such as
- * -Dtransitime.modules.optionalModulesList=org.transitime.avl.MuniNextBusAvlModule;org.transitime.avl.AvlJmsClientModule
+ * -Dtransitclock.modules.optionalModulesList=org.transitclock.avl.MuniNextBusAvlModule;org.transitclock.avl.AvlJmsClientModule
*/
-package org.transitime.avl;
+package org.transitclock.avl;
diff --git a/transitime/src/main/java/org/transitime/avl/socket/Client.java b/transitclock/src/main/java/org/transitclock/avl/socket/Client.java
similarity index 92%
rename from transitime/src/main/java/org/transitime/avl/socket/Client.java
rename to transitclock/src/main/java/org/transitclock/avl/socket/Client.java
index c6c3f5fd2..feb6a800e 100644
--- a/transitime/src/main/java/org/transitime/avl/socket/Client.java
+++ b/transitclock/src/main/java/org/transitclock/avl/socket/Client.java
@@ -1,4 +1,4 @@
-package org.transitime.avl.socket;
+package org.transitclock.avl.socket;
import java.io.IOException;
import java.io.PrintWriter;
diff --git a/transitime/src/main/java/org/transitime/avl/socket/Server.java b/transitclock/src/main/java/org/transitclock/avl/socket/Server.java
similarity index 92%
rename from transitime/src/main/java/org/transitime/avl/socket/Server.java
rename to transitclock/src/main/java/org/transitclock/avl/socket/Server.java
index d8c9d89c6..0d9c845b5 100644
--- a/transitime/src/main/java/org/transitime/avl/socket/Server.java
+++ b/transitclock/src/main/java/org/transitclock/avl/socket/Server.java
@@ -1,4 +1,4 @@
-package org.transitime.avl.socket;
+package org.transitclock.avl.socket;
import java.io.BufferedReader;
import java.io.IOException;
diff --git a/transitclock/src/main/java/org/transitclock/avl/via/AvlCsvReader.java b/transitclock/src/main/java/org/transitclock/avl/via/AvlCsvReader.java
new file mode 100644
index 000000000..ee3066480
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/avl/via/AvlCsvReader.java
@@ -0,0 +1,46 @@
+/*
+ * 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 .
+ */
+
+package org.transitclock.avl.via;
+
+import java.text.ParseException;
+
+import org.apache.commons.csv.CSVRecord;
+import org.transitclock.db.structs.AvlReport;
+import org.transitclock.utils.csv.CsvBaseReader;
+
+/**
+ * For reading in AVL data from a CSV file.
+ *
+ * @author SkiBu Smith
+ *
+ */
+public class AvlCsvReader extends CsvBaseReader {
+
+ /********************** Member Functions **************************/
+
+ public AvlCsvReader(String fileName) {
+ super(fileName);
+ }
+
+ @Override
+ public AvlReport handleRecord(CSVRecord record, boolean supplemental)
+ throws ParseException {
+ return AvlCsvRecord.getAvlReport(record, getFileName());
+ }
+
+}
diff --git a/transitclock/src/main/java/org/transitclock/avl/via/AvlCsvRecord.java b/transitclock/src/main/java/org/transitclock/avl/via/AvlCsvRecord.java
new file mode 100644
index 000000000..b0e021467
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/avl/via/AvlCsvRecord.java
@@ -0,0 +1,144 @@
+/*
+ * 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 .
+ */
+
+package org.transitclock.avl.via;
+
+import org.apache.commons.csv.CSVRecord;
+import org.transitclock.db.structs.AvlReport;
+import org.transitclock.db.structs.AvlReport.AssignmentType;
+import org.transitclock.utils.Time;
+import org.transitclock.utils.csv.CsvBase;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+
+/**
+ * Represents a single record in a CSV file containing AVL data.
+ *
+ * CSV columns include vehicleId, time (in epoch msec or as date string as in
+ * "9-14-2015 12:53:01"), latitude, longitude, speed (optional), heading
+ * (optional), assignmentId, and assignmentType (optional, but can be BLOCK_ID,
+ * ROUTE_ID, TRIP_ID, or TRIP_SHORT_NAME).
+ *
+ * @author SkiBu Smith
+ *
+ */
+public class AvlCsvRecord extends CsvBase {
+
+ /********************** Member Functions **************************/
+
+ private AvlCsvRecord(CSVRecord record, String fileName) {
+ super(record, false, fileName);
+ }
+
+ /**
+ * Returns AvlReport that the line in the CSV file represents.
+ *
+ * CSV columns include vehicleId, time (in epoch msec or as date string as
+ * in "9-14-2015 12:53:01"), latitude, longitude, speed (optional), heading
+ * (optional), assignmentId, and assignmentType (optional, but can be
+ * BLOCK_ID, ROUTE_ID, TRIP_ID, or TRIP_SHORT_NAME).
+ *
+ * @param record
+ * @param fileName
+ * @return AvlReport, or null if could not be parsed.
+ */
+ public static AvlReport getAvlReport(CSVRecord record, String fileName)
+ throws ParseException {
+ AvlCsvRecord avlCsvRecord = new AvlCsvRecord(record, fileName);
+
+ // Obtain the required values
+ String vehicleId = avlCsvRecord.getRequiredValue(record, "vehicleid");
+
+ String timeStr = avlCsvRecord.getRequiredValue(record, "time");
+
+ // 2017-01-09 02:19:46
+ SimpleDateFormat dateFormatter=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
+
+ // Process time
+ long time = 0L;
+
+ time=dateFormatter.parse(timeStr).getTime();
+
+ String latStr = avlCsvRecord.getRequiredValue(record, "latitude");
+
+ double lat=Double.NaN;
+ try {
+ lat = Double.parseDouble(latStr);
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ String lonStr = avlCsvRecord.getRequiredValue(record, "longitude");
+ double lon = Double.parseDouble(lonStr);
+
+ String speedStr = avlCsvRecord.getOptionalValue(record, "speed");
+ float speed = speedStr == null ? Float.NaN : Float.parseFloat(speedStr);
+
+ String headingStr = avlCsvRecord.getOptionalValue(record, "heading");
+ float heading = headingStr == null ?
+ Float.NaN : Float.parseFloat(headingStr);
+
+ // Obtain the optional values
+ String leadVehicleId = avlCsvRecord.getOptionalValue(record,
+ "leadVehicleId");
+ String driverId =
+ avlCsvRecord.getOptionalValue(record, "driverId");
+ String licensePlate = avlCsvRecord.getOptionalValue(record,
+ "licensePlate");
+
+ String passengerFullnessStr =
+ avlCsvRecord.getOptionalValue(record, "passengerFullness");
+ float passengerFullness = passengerFullnessStr==null ?
+ Float.NaN : Float.parseFloat(passengerFullnessStr);
+
+ String passengerCountStr =
+ avlCsvRecord.getOptionalValue(record, "passengerCount");
+ Integer passengerCount = passengerCountStr==null ?
+ null : Integer.parseInt(passengerCountStr);
+
+ // Create the avlReport
+ AvlReport avlReport =
+ new AvlReport(vehicleId, time, lat, lon, speed, heading, "CSV",
+ leadVehicleId, driverId, licensePlate, passengerCount,
+ passengerFullness);
+
+ // Assignment info
+ String assignmentId =
+ avlCsvRecord.getOptionalValue(record, "assignmentId");
+ String assignmentTypeStr =
+ avlCsvRecord.getOptionalValue(record, "assignmentType");
+ AssignmentType assignmentType;
+ if (assignmentId != null && assignmentTypeStr != null) {
+ if (assignmentTypeStr.equals("BLOCK_ID"))
+ assignmentType = AssignmentType.BLOCK_ID;
+ else if (assignmentTypeStr.equals("ROUTE_ID"))
+ assignmentType = AssignmentType.ROUTE_ID;
+ else if (assignmentTypeStr.equals("TRIP_ID"))
+ assignmentType = AssignmentType.TRIP_ID;
+ else if (assignmentTypeStr.equals("TRIP_SHORT_NAME"))
+ assignmentType = AssignmentType.TRIP_SHORT_NAME;
+ else
+ assignmentType = AssignmentType.UNSET;
+ avlReport.setAssignment(assignmentId, assignmentType);
+ }
+
+ return avlReport;
+ }
+
+}
diff --git a/transitime/src/main/java/org/transitime/avl/BatchCsvAvlFeedModule.java b/transitclock/src/main/java/org/transitclock/avl/via/BatchCsvAvlFeedModule.java
similarity index 89%
rename from transitime/src/main/java/org/transitime/avl/BatchCsvAvlFeedModule.java
rename to transitclock/src/main/java/org/transitclock/avl/via/BatchCsvAvlFeedModule.java
index d25c95e37..f424729b8 100644
--- a/transitime/src/main/java/org/transitime/avl/BatchCsvAvlFeedModule.java
+++ b/transitclock/src/main/java/org/transitclock/avl/via/BatchCsvAvlFeedModule.java
@@ -15,19 +15,19 @@
* along with Transitime.org . If not, see .
*/
-package org.transitime.avl;
+package org.transitclock.avl.via;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.transitime.applications.Core;
-import org.transitime.config.BooleanConfigValue;
-import org.transitime.config.StringConfigValue;
-import org.transitime.core.AvlProcessor;
-import org.transitime.db.structs.AvlReport;
-import org.transitime.modules.Module;
-import org.transitime.utils.Time;
+import org.transitclock.applications.Core;
+import org.transitclock.config.BooleanConfigValue;
+import org.transitclock.config.StringConfigValue;
+import org.transitclock.core.AvlProcessor;
+import org.transitclock.db.structs.AvlReport;
+import org.transitclock.modules.Module;
+import org.transitclock.utils.Time;
/**
* For reading in a batch of AVL data in CSV format and processing it. It only
@@ -53,13 +53,13 @@ private static String getCsvAvlFeedFileName() {
return csvAvlFeedFileName.getValue();
}
private static StringConfigValue csvAvlFeedFileName =
- new StringConfigValue("transitime.avl.csvAvlFeedFileName",
+ new StringConfigValue("transitclock.avl.csvAvlFeedFileName",
"/Users/Mike/cvsAvlData/testAvlData.csv",
"The name of the CSV file containing AVL data to process.");
private static BooleanConfigValue processInRealTime =
- new BooleanConfigValue("transitime.avl.processInRealTime",
+ new BooleanConfigValue("transitclock.avl.processInRealTime",
false,
"For when getting batch of AVL data from a CSV file. "
+ "When true then when reading in do at the same speed as "
diff --git a/transitclock/src/main/java/org/transitclock/avl/via/ViaRoute100Trial_GTFSRealtimeModule.java b/transitclock/src/main/java/org/transitclock/avl/via/ViaRoute100Trial_GTFSRealtimeModule.java
new file mode 100644
index 000000000..e04492911
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/avl/via/ViaRoute100Trial_GTFSRealtimeModule.java
@@ -0,0 +1,1584 @@
+package org.transitclock.avl.via;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+
+import org.transitclock.avl.GtfsRealtimeModule;
+import org.transitclock.core.VehicleState;
+import org.transitclock.core.dataCache.ArrivalDeparturesToProcessHoldingTimesFor;
+import org.transitclock.core.dataCache.HoldingTimeCache;
+import org.transitclock.core.dataCache.VehicleStateManager;
+import org.transitclock.core.holdingmethod.HoldingTimeGeneratorFactory;
+import org.transitclock.db.structs.ArrivalDeparture;
+import org.transitclock.db.structs.AvlReport;
+import org.transitclock.db.structs.HoldingTime;
+import org.transitclock.db.structs.AvlReport.AssignmentType;
+import org.transitclock.feed.gtfsRt.GtfsRtVehiclePositionsReader;
+
+public class ViaRoute100Trial_GTFSRealtimeModule extends GtfsRealtimeModule {
+
+ public ViaRoute100Trial_GTFSRealtimeModule(String projectId) {
+ super(projectId);
+ }
+
+ @Override
+ protected void getAndProcessData() {
+ Collection avlReports = GtfsRtVehiclePositionsReader
+ .getAvlReports(getGtfsRealtimeURI());
+ for (AvlReport avlReport : avlReports) {
+ if (shouldProcessAvl)
+ {
+ if(route100Trip(avlReport.getAssignmentId()))
+ {
+ // set to route as we are going to set this route up and frequency based with a single trip id.
+ avlReport.setAssignment("100", AssignmentType.ROUTE_ID);
+ processAvlReport(avlReport);
+ }
+ }
+ else
+ {
+ System.out.println(avlReport);
+ }
+ }
+
+ }
+
+ private static final String tripIds[]= {
+ "3231408",
+ "3231409",
+ "3231406",
+ "3231407",
+ "3231404",
+ "3231405",
+ "3231877",
+ "3231875",
+ "3231874",
+ "3231873",
+ "3231871",
+ "3231879",
+ "3231878",
+ "3231524",
+ "3231856",
+ "3231724",
+ "3163978",
+ "3163979",
+ "3232159",
+ "3162009",
+ "3162008",
+ "3162005",
+ "3162004",
+ "3162003",
+ "3162002",
+ "3162001",
+ "3162000",
+ "3232158",
+ "3231653",
+ "3231876",
+ "3231651",
+ "3231650",
+ "3231657",
+ "3231872",
+ "3231870",
+ "3231659",
+ "3231658",
+ "3161943",
+ "3161942",
+ "3161941",
+ "3161940",
+ "3161947",
+ "3161946",
+ "3161945",
+ "3161944",
+ "3161949",
+ "3161948",
+ "3232139",
+ "3232138",
+ "3232133",
+ "3232132",
+ "3232131",
+ "3232130",
+ "3232137",
+ "3232136",
+ "3232135",
+ "3232134",
+ "3231888",
+ "3231578",
+ "3231486",
+ "3231487",
+ "3231484",
+ "3231485",
+ "3231482",
+ "3231483",
+ "3231480",
+ "3231481",
+ "3231488",
+ "3231489",
+ "3231810",
+ "3231675",
+ "3231729",
+ "3231728",
+ "3231723",
+ "3231722",
+ "3231721",
+ "3231720",
+ "3231727",
+ "3231726",
+ "3164030",
+ "3164031",
+ "3164032",
+ "3164033",
+ "3164034",
+ "3164035",
+ "3164036",
+ "3164037",
+ "3164038",
+ "3164039",
+ "3231677",
+ "3231671",
+ "3231673",
+ "3163819",
+ "3163818",
+ "3163817",
+ "3163816",
+ "3231784",
+ "3231963",
+ "3231593",
+ "3231782",
+ "3162124",
+ "3162125",
+ "3162126",
+ "3162120",
+ "3162121",
+ "3162122",
+ "3162123",
+ "3163756",
+ "3163757",
+ "3163750",
+ "3163751",
+ "3163752",
+ "3163753",
+ "3163758",
+ "3163759",
+ "3161860",
+ "3161861",
+ "3161862",
+ "3161863",
+ "3161864",
+ "3161865",
+ "3161866",
+ "3161867",
+ "3161868",
+ "3161869",
+ "3231674",
+ "3231676",
+ "3231672",
+ "3232047",
+ "3232046",
+ "3232045",
+ "3232044",
+ "3232043",
+ "3232042",
+ "3232041",
+ "3232040",
+ "3232049",
+ "3232048",
+ "3231572",
+ "3231573",
+ "3231570",
+ "3231571",
+ "3231574",
+ "3231575",
+ "3163838",
+ "3163831",
+ "3163830",
+ "3163833",
+ "3163832",
+ "3163835",
+ "3163834",
+ "3163837",
+ "3231909",
+ "3231908",
+ "3231903",
+ "3231902",
+ "3231901",
+ "3231900",
+ "3231907",
+ "3231906",
+ "3231905",
+ "3231904",
+ "3161994",
+ "3161995",
+ "3161996",
+ "3161997",
+ "3161990",
+ "3161991",
+ "3161992",
+ "3161993",
+ "3161998",
+ "3161999",
+ "3163974",
+ "3163975",
+ "3163976",
+ "3163977",
+ "3163970",
+ "3163971",
+ "3163972",
+ "3162007",
+ "3162006",
+ "3231439",
+ "3231438",
+ "3232062",
+ "3163656",
+ "3163659",
+ "3163658",
+ "3231739",
+ "3231732",
+ "3231733",
+ "3231735",
+ "3161818",
+ "3161812",
+ "3231530",
+ "3231835",
+ "3231834",
+ "3231640",
+ "3231641",
+ "3231644",
+ "3231645",
+ "3231646",
+ "3231647",
+ "3231983",
+ "3231982",
+ "3231981",
+ "3231980",
+ "3231987",
+ "3231986",
+ "3231985",
+ "3231984",
+ "3231989",
+ "3231988",
+ "3161918",
+ "3161919",
+ "3161914",
+ "3161915",
+ "3161916",
+ "3161917",
+ "3161910",
+ "3161911",
+ "3232068",
+ "3231595",
+ "3231820",
+ "3231821",
+ "3231822",
+ "3231823",
+ "3161891",
+ "3161890",
+ "3161893",
+ "3161892",
+ "3161895",
+ "3161894",
+ "3161897",
+ "3161896",
+ "3161899",
+ "3161898",
+ "3231504",
+ "3231503",
+ "3231502",
+ "3231774",
+ "3231776",
+ "3231777",
+ "3231770",
+ "3231771",
+ "3231772",
+ "3231500",
+ "3231778",
+ "3164049",
+ "3164048",
+ "3164041",
+ "3164040",
+ "3164043",
+ "3164042",
+ "3164045",
+ "3164044",
+ "3164047",
+ "3164046",
+ "3163905",
+ "3163904",
+ "3163907",
+ "3163906",
+ "3163901",
+ "3163900",
+ "3163903",
+ "3163902",
+ "3163909",
+ "3163908",
+ "3162058",
+ "3162059",
+ "3163853",
+ "3163852",
+ "3163851",
+ "3163855",
+ "3162096",
+ "3231424",
+ "3231425",
+ "3161819",
+ "3161813",
+ "3161815",
+ "3161814",
+ "3161817",
+ "3161816",
+ "3231766",
+ "3231764",
+ "3231761",
+ "3231661",
+ "3163989",
+ "3163988",
+ "3163985",
+ "3163984",
+ "3163987",
+ "3163986",
+ "3163981",
+ "3163980",
+ "3163983",
+ "3163982",
+ "3231512",
+ "3163761",
+ "3163763",
+ "3163762",
+ "3163765",
+ "3163764",
+ "3163767",
+ "3163766",
+ "3231518",
+ "3231549",
+ "3231548",
+ "3231543",
+ "3231542",
+ "3231541",
+ "3231547",
+ "3231546",
+ "3231545",
+ "3231544",
+ "3162041",
+ "3231954",
+ "3231955",
+ "3231956",
+ "3231957",
+ "3231950",
+ "3231951",
+ "3231952",
+ "3231953",
+ "3231958",
+ "3231959",
+ "3163725",
+ "3163724",
+ "3163727",
+ "3163726",
+ "3163721",
+ "3163720",
+ "3163723",
+ "3163722",
+ "3163729",
+ "3163728",
+ "3231623",
+ "3231620",
+ "3231621",
+ "3231412",
+ "3231418",
+ "3231639",
+ "3231638",
+ "3231631",
+ "3231630",
+ "3231635",
+ "3231634",
+ "3231637",
+ "3231636",
+ "3163868",
+ "3163869",
+ "3163862",
+ "3163863",
+ "3163860",
+ "3163861",
+ "3163866",
+ "3163867",
+ "3163864",
+ "3163865",
+ "3231625",
+ "3231628",
+ "3231629",
+ "3231513",
+ "3231468",
+ "3231469",
+ "3231460",
+ "3231461",
+ "3231462",
+ "3231463",
+ "3231464",
+ "3231465",
+ "3231466",
+ "3231467",
+ "3232078",
+ "3232079",
+ "3232070",
+ "3231851",
+ "3231850",
+ "3231853",
+ "3231852",
+ "3231855",
+ "3231854",
+ "3231540",
+ "3166007",
+ "3166006",
+ "3164092",
+ "3164093",
+ "3164090",
+ "3164091",
+ "3164096",
+ "3164097",
+ "3164094",
+ "3164095",
+ "3164098",
+ "3164099",
+ "3232090",
+ "3232091",
+ "3232092",
+ "3232093",
+ "3232094",
+ "3232095",
+ "3232096",
+ "3232097",
+ "3232098",
+ "3232099",
+ "3163958",
+ "3163959",
+ "3163956",
+ "3163957",
+ "3163954",
+ "3163955",
+ "3163952",
+ "3163953",
+ "3163950",
+ "3163951",
+ "3162029",
+ "3162028",
+ "3231936",
+ "3161969",
+ "3161968",
+ "3161965",
+ "3161964",
+ "3161967",
+ "3161966",
+ "3161961",
+ "3161960",
+ "3161963",
+ "3161962",
+ "3232115",
+ "3232114",
+ "3232117",
+ "3232116",
+ "3232111",
+ "3232110",
+ "3232113",
+ "3232112",
+ "3232119",
+ "3232118",
+ "3231725",
+ "3232109",
+ "3163688",
+ "3163689",
+ "3163682",
+ "3163683",
+ "3163680",
+ "3163681",
+ "3163686",
+ "3163687",
+ "3163684",
+ "3163685",
+ "3232035",
+ "3231705",
+ "3231707",
+ "3231706",
+ "3231701",
+ "3231700",
+ "3231703",
+ "3231709",
+ "3231708",
+ "3164018",
+ "3164019",
+ "3164012",
+ "3164013",
+ "3164010",
+ "3164011",
+ "3164016",
+ "3164017",
+ "3164014",
+ "3164015",
+ "3232038",
+ "3231798",
+ "3231516",
+ "3162106",
+ "3162107",
+ "3162104",
+ "3162105",
+ "3162102",
+ "3162103",
+ "3162100",
+ "3162101",
+ "3162108",
+ "3162109",
+ "3163778",
+ "3163779",
+ "3163774",
+ "3163775",
+ "3163772",
+ "3163773",
+ "3163770",
+ "3163771",
+ "3161848",
+ "3161849",
+ "3161842",
+ "3161843",
+ "3161840",
+ "3161841",
+ "3161846",
+ "3161847",
+ "3161844",
+ "3161845",
+ "3231450",
+ "3231789",
+ "3231788",
+ "3231785",
+ "3231787",
+ "3231786",
+ "3231781",
+ "3231780",
+ "3231783",
+ "3161828",
+ "3232061",
+ "3232060",
+ "3232063",
+ "3232065",
+ "3232064",
+ "3232067",
+ "3232066",
+ "3232069",
+ "3231849",
+ "3231598",
+ "3231599",
+ "3231594",
+ "3231596",
+ "3231597",
+ "3231590",
+ "3231591",
+ "3231592",
+ "3231711",
+ "3163857",
+ "3163859",
+ "3163858",
+ "3231717",
+ "3231925",
+ "3231924",
+ "3231927",
+ "3231926",
+ "3231921",
+ "3231920",
+ "3231923",
+ "3231922",
+ "3231929",
+ "3231928",
+ "3231411",
+ "3231410",
+ "3231415",
+ "3231414",
+ "3231417",
+ "3231416",
+ "3232003",
+ "3232002",
+ "3232001",
+ "3163967",
+ "3163966",
+ "3163965",
+ "3163964",
+ "3163963",
+ "3163962",
+ "3163961",
+ "3163960",
+ "3163969",
+ "3163968",
+ "3231918",
+ "3231919",
+ "3231913",
+ "3231916",
+ "3231917",
+ "3231668",
+ "3231669",
+ "3231663",
+ "3231660",
+ "3231666",
+ "3231667",
+ "3231664",
+ "3231665",
+ "3161936",
+ "3161937",
+ "3161934",
+ "3161935",
+ "3161932",
+ "3161933",
+ "3161930",
+ "3161931",
+ "3161938",
+ "3161939",
+ "3232148",
+ "3232149",
+ "3232146",
+ "3232147",
+ "3232144",
+ "3232145",
+ "3232142",
+ "3232143",
+ "3232140",
+ "3232141",
+ "3231867",
+ "3231769",
+ "3231499",
+ "3231498",
+ "3231491",
+ "3231490",
+ "3231493",
+ "3231492",
+ "3231495",
+ "3231494",
+ "3231497",
+ "3231496",
+ "3231802",
+ "3231803",
+ "3231800",
+ "3231801",
+ "3231807",
+ "3231805",
+ "3231809",
+ "3231758",
+ "3231759",
+ "3231756",
+ "3231757",
+ "3231754",
+ "3231755",
+ "3231752",
+ "3231753",
+ "3231750",
+ "3231751",
+ "3164023",
+ "3164022",
+ "3164021",
+ "3164020",
+ "3164027",
+ "3164026",
+ "3164025",
+ "3164024",
+ "3164029",
+ "3164028",
+ "3162078",
+ "3162079",
+ "3162072",
+ "3162073",
+ "3162070",
+ "3162071",
+ "3162076",
+ "3162077",
+ "3162074",
+ "3162075",
+ "3163754",
+ "3163755",
+ "3231531",
+ "3163803",
+ "3163884",
+ "3163885",
+ "3163886",
+ "3163887",
+ "3163880",
+ "3163881",
+ "3163882",
+ "3163883",
+ "3163888",
+ "3163889",
+ "3231893",
+ "3231649",
+ "3163747",
+ "3163746",
+ "3163745",
+ "3163744",
+ "3163743",
+ "3163742",
+ "3163741",
+ "3163740",
+ "3163749",
+ "3163748",
+ "3232018",
+ "3232019",
+ "3232010",
+ "3232011",
+ "3232012",
+ "3232013",
+ "3232014",
+ "3232015",
+ "3232016",
+ "3232017",
+ "3161873",
+ "3161872",
+ "3161871",
+ "3161870",
+ "3161877",
+ "3161876",
+ "3161874",
+ "3161879",
+ "3161878",
+ "3231420",
+ "3231421",
+ "3231428",
+ "3231429",
+ "3231806",
+ "3231804",
+ "3231808",
+ "3231565",
+ "3231564",
+ "3231567",
+ "3231566",
+ "3231561",
+ "3231563",
+ "3231562",
+ "3231569",
+ "3231568",
+ "3163808",
+ "3163809",
+ "3163804",
+ "3163806",
+ "3163807",
+ "3163800",
+ "3163801",
+ "3163802",
+ "3231978",
+ "3231979",
+ "3231976",
+ "3231977",
+ "3231974",
+ "3231975",
+ "3231972",
+ "3231973",
+ "3231970",
+ "3231971",
+ "3232081",
+ "3161987",
+ "3161986",
+ "3161985",
+ "3161984",
+ "3161983",
+ "3161982",
+ "3161981",
+ "3161980",
+ "3161989",
+ "3161988",
+ "3231633",
+ "3231632",
+ "3231603",
+ "3163668",
+ "3163669",
+ "3231534",
+ "3231533",
+ "3231814",
+ "3161909",
+ "3161908",
+ "3161907",
+ "3161906",
+ "3161905",
+ "3161904",
+ "3161903",
+ "3161902",
+ "3161901",
+ "3161900",
+ "3232101",
+ "3232107",
+ "3231442",
+ "3231443",
+ "3231440",
+ "3231441",
+ "3231446",
+ "3231447",
+ "3231444",
+ "3231445",
+ "3231448",
+ "3231449",
+ "3231839",
+ "3231838",
+ "3231833",
+ "3231832",
+ "3231831",
+ "3231830",
+ "3231837",
+ "3231836",
+ "3231858",
+ "3231767",
+ "3231763",
+ "3231762",
+ "3231760",
+ "3163716",
+ "3163717",
+ "3164078",
+ "3164079",
+ "3164074",
+ "3164075",
+ "3164076",
+ "3164077",
+ "3164070",
+ "3164071",
+ "3164072",
+ "3164073",
+ "3163938",
+ "3163939",
+ "3163930",
+ "3163931",
+ "3163932",
+ "3163933",
+ "3163934",
+ "3163935",
+ "3163936",
+ "3163937",
+ "3162043",
+ "3162040",
+ "3162047",
+ "3162046",
+ "3162045",
+ "3162044",
+ "3162049",
+ "3162048",
+ "3231738",
+ "3163843",
+ "3161829",
+ "3161824",
+ "3161825",
+ "3161826",
+ "3161827",
+ "3161820",
+ "3161821",
+ "3161822",
+ "3161823",
+ "3231432",
+ "3231817",
+ "3231816",
+ "3231678",
+ "3231857",
+ "3231684",
+ "3231685",
+ "3231686",
+ "3231687",
+ "3231680",
+ "3231681",
+ "3231682",
+ "3231683",
+ "3231688",
+ "3231689",
+ "3231538",
+ "3231539",
+ "3231536",
+ "3231537",
+ "3231535",
+ "3231532",
+ "3231431",
+ "3231768",
+ "3231947",
+ "3231946",
+ "3231945",
+ "3231944",
+ "3231943",
+ "3231942",
+ "3231941",
+ "3231940",
+ "3231949",
+ "3231948",
+ "3231506",
+ "3231505",
+ "3163718",
+ "3163719",
+ "3163710",
+ "3163711",
+ "3163712",
+ "3231501",
+ "3163973",
+ "3231577",
+ "3231997",
+ "3231579",
+ "3231670",
+ "3163839",
+ "3163836",
+ "3232009",
+ "3232008",
+ "3162094",
+ "3162095",
+ "3162097",
+ "3162098",
+ "3162099",
+ "3232108",
+ "3231608",
+ "3231604",
+ "3231600",
+ "3231601",
+ "3231602",
+ "3232026",
+ "3232021",
+ "3232020",
+ "3232022",
+ "3232029",
+ "3232028",
+ "3163879",
+ "3163878",
+ "3163875",
+ "3163874",
+ "3163877",
+ "3163876",
+ "3163871",
+ "3163870",
+ "3163873",
+ "3163872",
+ "3163790",
+ "3163791",
+ "3163792",
+ "3163793",
+ "3163794",
+ "3163795",
+ "3163796",
+ "3163797",
+ "3163798",
+ "3163799",
+ "3231713",
+ "3231719",
+ "3232151",
+ "3232150",
+ "3232153",
+ "3232152",
+ "3232155",
+ "3232154",
+ "3232157",
+ "3232156",
+ "3231479",
+ "3231478",
+ "3231473",
+ "3231472",
+ "3231471",
+ "3231470",
+ "3231477",
+ "3231476",
+ "3231475",
+ "3231474",
+ "3231840",
+ "3231864",
+ "3231865",
+ "3231866",
+ "3231860",
+ "3231862",
+ "3231863",
+ "3231868",
+ "3231869",
+ "3162056",
+ "3166074",
+ "3166075",
+ "3231437",
+ "3231435",
+ "3164085",
+ "3164084",
+ "3164087",
+ "3164086",
+ "3164081",
+ "3164080",
+ "3164083",
+ "3164082",
+ "3164089",
+ "3164088",
+ "3232083",
+ "3232082",
+ "3232089",
+ "3232088",
+ "3163941",
+ "3163940",
+ "3163943",
+ "3163945",
+ "3163944",
+ "3163947",
+ "3163946",
+ "3163949",
+ "3163948",
+ "3162018",
+ "3162019",
+ "3162014",
+ "3162016",
+ "3162017",
+ "3162010",
+ "3162011",
+ "3162012",
+ "3162013",
+ "3231847",
+ "3231844",
+ "3231843",
+ "3231841",
+ "3161950",
+ "3161951",
+ "3161952",
+ "3161953",
+ "3161954",
+ "3161955",
+ "3161956",
+ "3161957",
+ "3161958",
+ "3161959",
+ "3231662",
+ "3231861",
+ "3232128",
+ "3232129",
+ "3232120",
+ "3232121",
+ "3232122",
+ "3232123",
+ "3232124",
+ "3232125",
+ "3232126",
+ "3232127",
+ "3232025",
+ "3162021",
+ "3162020",
+ "3162023",
+ "3162022",
+ "3162025",
+ "3162024",
+ "3162027",
+ "3162026",
+ "3232024",
+ "3232000",
+ "3232007",
+ "3232006",
+ "3232005",
+ "3232004",
+ "3162090",
+ "3162091",
+ "3162092",
+ "3162093",
+ "3163699",
+ "3163698",
+ "3163695",
+ "3163694",
+ "3163697",
+ "3163696",
+ "3163691",
+ "3163690",
+ "3163693",
+ "3163692",
+ "3231889",
+ "3231882",
+ "3231883",
+ "3231880",
+ "3231881",
+ "3231886",
+ "3231887",
+ "3231884",
+ "3231885",
+ "3231436",
+ "3231434",
+ "3231433",
+ "3231430",
+ "3231730",
+ "3231731",
+ "3231734",
+ "3231736",
+ "3231737",
+ "3164009",
+ "3164008",
+ "3164005",
+ "3164004",
+ "3164007",
+ "3164006",
+ "3164001",
+ "3164000",
+ "3164003",
+ "3164002",
+ "3231765",
+ "3231815",
+ "3231811",
+ "3231813",
+ "3231812",
+ "3231818",
+ "3232027",
+ "3231509",
+ "3231508",
+ "3162119",
+ "3162118",
+ "3162111",
+ "3162110",
+ "3162113",
+ "3162112",
+ "3162115",
+ "3162114",
+ "3162117",
+ "3162116",
+ "3232023",
+ "3231609",
+ "3231605",
+ "3231606",
+ "3231607",
+ "3163760",
+ "3163769",
+ "3163768",
+ "3161859",
+ "3161858",
+ "3161855",
+ "3161854",
+ "3161857",
+ "3161856",
+ "3161851",
+ "3161850",
+ "3161853",
+ "3161852",
+ "3163840",
+ "3231523",
+ "3231522",
+ "3231529",
+ "3231528",
+ "3231627",
+ "3231624",
+ "3231622",
+ "3231859",
+ "3163657",
+ "3232031",
+ "3232054",
+ "3232055",
+ "3232056",
+ "3232057",
+ "3232050",
+ "3232051",
+ "3232052",
+ "3232053",
+ "3232058",
+ "3232059",
+ "3231589",
+ "3231588",
+ "3231587",
+ "3231586",
+ "3231585",
+ "3231584",
+ "3231583",
+ "3231582",
+ "3231581",
+ "3231580",
+ "3163826",
+ "3163827",
+ "3163824",
+ "3163825",
+ "3163822",
+ "3163823",
+ "3163820",
+ "3163821",
+ "3163828",
+ "3163829",
+ "3231910",
+ "3231911",
+ "3231912",
+ "3231914",
+ "3231915",
+ "3232032",
+ "3232033",
+ "3232036",
+ "3232037",
+ "3232034",
+ "3231426",
+ "3231427",
+ "3231422",
+ "3231423",
+ "3231744",
+ "3163702",
+ "3163701",
+ "3163704",
+ "3231679",
+ "3231507",
+ "3231990",
+ "3231991",
+ "3231992",
+ "3231993",
+ "3231994",
+ "3231995",
+ "3231996",
+ "3231998",
+ "3231999",
+ "3161929",
+ "3161928",
+ "3161921",
+ "3161920",
+ "3161922",
+ "3161925",
+ "3161924",
+ "3161927",
+ "3161926",
+ "3231819",
+ "3161888",
+ "3161889",
+ "3161886",
+ "3161887",
+ "3161884",
+ "3161885",
+ "3161882",
+ "3161883",
+ "3161880",
+ "3161881",
+ "3231842",
+ "3231741",
+ "3231740",
+ "3231743",
+ "3231742",
+ "3231745",
+ "3231747",
+ "3231746",
+ "3231749",
+ "3231748",
+ "3164056",
+ "3164057",
+ "3164054",
+ "3164055",
+ "3164052",
+ "3164053",
+ "3164050",
+ "3164051",
+ "3164058",
+ "3164059",
+ "3163912",
+ "3163913",
+ "3163910",
+ "3163911",
+ "3163916",
+ "3163917",
+ "3163914",
+ "3163915",
+ "3163918",
+ "3163919",
+ "3162069",
+ "3162068",
+ "3162065",
+ "3162064",
+ "3162067",
+ "3162066",
+ "3162061",
+ "3162060",
+ "3162063",
+ "3162062",
+ "3161923",
+ "3231413",
+ "3231642",
+ "3231643",
+ "3231626",
+ "3163897",
+ "3163896",
+ "3163895",
+ "3163894",
+ "3163893",
+ "3163892",
+ "3163891",
+ "3163890",
+ "3163899",
+ "3163898",
+ "3231899",
+ "3163850",
+ "3163856",
+ "3163854",
+ "3231560",
+ "3231652",
+ "3163998",
+ "3163999",
+ "3163992",
+ "3163993",
+ "3163990",
+ "3163991",
+ "3163996",
+ "3163997",
+ "3163994",
+ "3163995",
+ "3231576",
+ "3163805",
+ "3161912",
+ "3161913",
+ "3231656",
+ "3231655",
+ "3231654",
+ "3231799",
+ "3231791",
+ "3231558",
+ "3231559",
+ "3231550",
+ "3231551",
+ "3231552",
+ "3231553",
+ "3231554",
+ "3231555",
+ "3231556",
+ "3231557",
+ "3163815",
+ "3163814",
+ "3163813",
+ "3163812",
+ "3163811",
+ "3163810",
+ "3231961",
+ "3231960",
+ "3231962",
+ "3231965",
+ "3231964",
+ "3231967",
+ "3231966",
+ "3231969",
+ "3231968",
+ "3163732",
+ "3163733",
+ "3163730",
+ "3163731",
+ "3163736",
+ "3163737",
+ "3163734",
+ "3163735",
+ "3163738",
+ "3163739",
+ "3231825",
+ "3231704",
+ "3231702",
+ "3232164",
+ "3232160",
+ "3232161",
+ "3232162",
+ "3232163",
+ "3163677",
+ "3163676",
+ "3163675",
+ "3163674",
+ "3163673",
+ "3163672",
+ "3163671",
+ "3163670",
+ "3163679",
+ "3163678",
+ "3163707",
+ "3232080",
+ "3232087",
+ "3232086",
+ "3232085",
+ "3232084",
+ "3163942",
+ "3162015",
+ "3231419",
+ "3231510",
+ "3231511",
+ "3231455",
+ "3231454",
+ "3231457",
+ "3231456",
+ "3231451",
+ "3231453",
+ "3231452",
+ "3231459",
+ "3231458",
+ "3231828",
+ "3231829",
+ "3232076",
+ "3232077",
+ "3232074",
+ "3232075",
+ "3232072",
+ "3232073",
+ "3232071",
+ "3231519",
+ "3231824",
+ "3231848",
+ "3231846",
+ "3231845",
+ "3231895",
+ "3231894",
+ "3231897",
+ "3231891",
+ "3231890",
+ "3164069",
+ "3164068",
+ "3164067",
+ "3164066",
+ "3164065",
+ "3164064",
+ "3164063",
+ "3164062",
+ "3164061",
+ "3164060",
+ "3163929",
+ "3163928",
+ "3163923",
+ "3163922",
+ "3163921",
+ "3163920",
+ "3163927",
+ "3163926",
+ "3163925",
+ "3163924",
+ "3162036",
+ "3162037",
+ "3162034",
+ "3162032",
+ "3162033",
+ "3162030",
+ "3162031",
+ "3162038",
+ "3162039",
+ "3231826",
+ "3231827",
+ "3162042",
+ "3161978",
+ "3161979",
+ "3161972",
+ "3161973",
+ "3161970",
+ "3161971",
+ "3161976",
+ "3161977",
+ "3161974",
+ "3161975",
+ "3232102",
+ "3232103",
+ "3232100",
+ "3232106",
+ "3232104",
+ "3232105",
+ "3161839",
+ "3161838",
+ "3161837",
+ "3161836",
+ "3161835",
+ "3161834",
+ "3161833",
+ "3161832",
+ "3161831",
+ "3161830",
+ "3163841",
+ "3163842",
+ "3163844",
+ "3163845",
+ "3163846",
+ "3231514",
+ "3231515",
+ "3231712",
+ "3163847",
+ "3231710",
+ "3231517",
+ "3231716",
+ "3231714",
+ "3231715",
+ "3231718",
+ "3163664",
+ "3163665",
+ "3163666",
+ "3163667",
+ "3163660",
+ "3163661",
+ "3163662",
+ "3163663",
+ "3231932",
+ "3231931",
+ "3231697",
+ "3231696",
+ "3231695",
+ "3231694",
+ "3231693",
+ "3231692",
+ "3231691",
+ "3231690",
+ "3231699",
+ "3231698",
+ "3231521",
+ "3231520",
+ "3231525",
+ "3231527",
+ "3231526",
+ "3231648",
+ "3163709",
+ "3163708",
+ "3163703",
+ "3163700",
+ "3163706",
+ "3163705",
+ "3163783",
+ "3163786",
+ "3163788",
+ "3231775",
+ "3231773",
+ "3231779",
+ "3231792",
+ "3231793",
+ "3231790",
+ "3231796",
+ "3231797",
+ "3231794",
+ "3231795",
+ "3163776",
+ "3163777",
+ "3162087",
+ "3162086",
+ "3162085",
+ "3162084",
+ "3162083",
+ "3162082",
+ "3162081",
+ "3162080",
+ "3162089",
+ "3162088",
+ "3231619",
+ "3231618",
+ "3231617",
+ "3231616",
+ "3231615",
+ "3231614",
+ "3231613",
+ "3231612",
+ "3231611",
+ "3231610",
+ "3162050",
+ "3162051",
+ "3162052",
+ "3162053",
+ "3162054",
+ "3162055",
+ "3162057",
+ "3163848",
+ "3163849",
+ "3231933",
+ "3231930",
+ "3231937",
+ "3231934",
+ "3231935",
+ "3231938",
+ "3231939",
+ "3232030",
+ "3163782",
+ "3163781",
+ "3163780",
+ "3163787",
+ "3163785",
+ "3163784",
+ "3163789",
+ "3232039",
+ "3231898",
+ "3163713",
+ "3163714",
+ "3163715",
+ "3231896",
+ "3231892"
+ };
+ private static final ArrayList list=new ArrayList(Arrays.asList(tripIds));
+ private boolean route100Trip(String assignmentId) {
+ if(list.contains(assignmentId))
+ {
+ return true;
+ }else
+ {
+ return false;
+ }
+ }
+
+}
diff --git a/transitime/src/main/java/org/transitime/config/BooleanConfigValue.java b/transitclock/src/main/java/org/transitclock/config/BooleanConfigValue.java
similarity index 98%
rename from transitime/src/main/java/org/transitime/config/BooleanConfigValue.java
rename to transitclock/src/main/java/org/transitclock/config/BooleanConfigValue.java
index 6905f2015..9879b527f 100644
--- a/transitime/src/main/java/org/transitime/config/BooleanConfigValue.java
+++ b/transitclock/src/main/java/org/transitclock/config/BooleanConfigValue.java
@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.config;
+package org.transitclock.config;
import java.util.List;
diff --git a/transitclock/src/main/java/org/transitclock/config/ClassConfigValue.java b/transitclock/src/main/java/org/transitclock/config/ClassConfigValue.java
new file mode 100644
index 000000000..6eaf55beb
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/config/ClassConfigValue.java
@@ -0,0 +1,26 @@
+package org.transitclock.config;
+
+import java.util.List;
+
+/**
+ * For injecting a class dependency via an input configuration parameter.
+ *
+ */
+public class ClassConfigValue extends ConfigValue {
+
+ public ClassConfigValue(String id, Class defaultValue, String description) {
+ super(id, defaultValue, description);
+ }
+
+ @Override
+ protected Class convertFromString(List dataStr) {
+ try {
+ return getClass().forName(dataStr.get(0).trim());
+ } catch (ClassNotFoundException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+}
diff --git a/transitime/src/main/java/org/transitime/config/ConfigFileReader.java b/transitclock/src/main/java/org/transitclock/config/ConfigFileReader.java
similarity index 89%
rename from transitime/src/main/java/org/transitime/config/ConfigFileReader.java
rename to transitclock/src/main/java/org/transitclock/config/ConfigFileReader.java
index 8dc138338..086b1dd33 100644
--- a/transitime/src/main/java/org/transitime/config/ConfigFileReader.java
+++ b/transitclock/src/main/java/org/transitclock/config/ConfigFileReader.java
@@ -14,16 +14,15 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.config;
+package org.transitclock.config;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.DocumentBuilder;
-
-import org.transitime.config.ConfigValue.ConfigParamException;
+import org.transitclock.config.ConfigValue.ConfigParamException;
import org.w3c.dom.Document;
-import org.w3c.dom.NodeList;
import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
@@ -57,14 +56,14 @@
*
* For each parameter configured in the file the property is written as a system
* property if that system property is not yet defined. This way all parameters
- * are available as system properties, even non-transitime ones such as for
+ * are available as system properties, even non-transitclock ones such as for
* hibernate or logback. Java properties set on the command line take precedence
* since the properties are not overwritten if already set.
*
* The params are configured in an XML or a properties file. The member
- * processConfig() first process a file named transitime.properties if one
+ * processConfig() first process a file named transitclock.properties if one
* exists in the classpath. Then files specified by the Java system property
- * transitime.configFiles . If the file has a .xml suffix then it will be
+ * transitclock.configFiles . If the file has a .xml suffix then it will be
* processed as XML. Otherwise it will be processed as a regular properties
* file. Multiple config files can be used. Each XML file should have a
* corresponding configuration class such as CoreConfig.java. In the
@@ -144,7 +143,7 @@ private static void processChildren(NodeList nodeList,
} else {
// Determine full name of parameter by appending node
// names together, separated by a period. So get something
- // like "transitime.predictor.radius".
+ // like "transitclock.predictor.radius".
StringBuilder propertyNameBuilder = new StringBuilder("");
for (int j=0; j
+ * Reads the specified XML file and stores the results into a HashMap
+ * configFileData. The file is expected to be very simple. Attributes are
+ * ignored. Only values are used. The keys for the resulting HashMap are
+ * based on the XML tags and their nesting. So if one is setting a value of
+ * 75 to a param with a key transitclock.predictor.allowableDistanceFromPath
+ * the XML would look like: {@code
+ *
*
*
* 75.0
*
*
- *
+ *
* }
* @param fileName
*/
@@ -298,8 +296,8 @@ private static void readPropertiesConfigFile(InputStream inputStream) {
* @param fileName Name of properties file
*/
private static void readPropertiesConfigFile(String fileName) {
- try {
- readPropertiesConfigFile(new FileInputStream(fileName));
+ try (FileInputStream file = new FileInputStream(fileName)) {
+ readPropertiesConfigFile(file);
} catch (IOException e) {
System.err.println("Exception occurred reading in fileName "
+ fileName + " . " + e.getMessage());
@@ -379,9 +377,9 @@ public static void processConfig(String fileName)
/**
* Process the configuration file specified by a file named
- * transitime.properties that is in the classpath. The processes
+ * transitclock.properties that is in the classpath. The processes
* configuration files specified by the Java system property
- * transitime.configFiles. The transitime.configFiles property is a ";"
+ * transitclock.configFiles. The transitclock.configFiles property is a ";"
* separated list so can specify multiple files. The files can be either in
* either XML or in Java properties format.
*
@@ -396,7 +394,7 @@ public static void processConfig(String fileName)
public static void processConfig() {
// Determine from the Java system property transitime.configFiles
// the configuration files to be read
- String configFilesStr = System.getProperty("transitime.configFiles");
+ String configFilesStr = System.getProperty("transitclock.configFiles");
if (configFilesStr == null)
return;
String configFiles[] = configFilesStr.split(";");
@@ -409,11 +407,11 @@ public static void processConfig() {
}
}
- // If the file transitime.properties exists in the classpath then
- // process it. This is done after the transitime.configFiles files
+ // If the file transitclock.properties exists in the classpath then
+ // process it. This is done after the transitclock.configFiles files
// are read in so that they have precedence over the file found
// in the classpath.
- String defaultPropertiesFileName = "transitime.properties";
+ String defaultPropertiesFileName = "transitclock.properties";
InputStream propertiesInput = ConfigFileReader.class.getClassLoader()
.getResourceAsStream(defaultPropertiesFileName);
if (propertiesInput != null) {
diff --git a/transitime/src/main/java/org/transitime/config/ConfigValue.java b/transitclock/src/main/java/org/transitclock/config/ConfigValue.java
similarity index 93%
rename from transitime/src/main/java/org/transitime/config/ConfigValue.java
rename to transitclock/src/main/java/org/transitclock/config/ConfigValue.java
index 9c7033183..c10992aa6 100644
--- a/transitime/src/main/java/org/transitime/config/ConfigValue.java
+++ b/transitclock/src/main/java/org/transitclock/config/ConfigValue.java
@@ -14,15 +14,15 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.config;
+package org.transitclock.config;
import java.util.Arrays;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.transitime.configData.AgencyConfig;
-import org.transitime.logging.Markers;
+import org.transitclock.configData.AgencyConfig;
+import org.transitclock.logging.Markers;
/**
* Abstract class for storing a single param. These params are read from the
@@ -36,7 +36,7 @@
*/
public abstract class ConfigValue {
// Name of the param. Also used as Java property name
- // (e.g. -Dtransitime.limit=2)
+ // (e.g. -Dtransitclock.limit=2)
protected final String id;
// Value to use if not specified in config file. Can be null.
@@ -199,9 +199,11 @@ public String getID() {
abstract protected T convertFromString(List dataStr);
/**
- * Reads value from the config data and stores it
+ * Reads value from the config data and stores it.
+ * Allow this to be public to allow re-interpretation of values for
+ * unit tests.
*/
- private void readValue()
+ public void readValue()
throws ConfigParamException {
List dataList = null;
diff --git a/transitime/src/main/java/org/transitime/config/DoubleConfigValue.java b/transitclock/src/main/java/org/transitclock/config/DoubleConfigValue.java
similarity index 97%
rename from transitime/src/main/java/org/transitime/config/DoubleConfigValue.java
rename to transitclock/src/main/java/org/transitclock/config/DoubleConfigValue.java
index 2fe75a1cd..f8d8d8965 100644
--- a/transitime/src/main/java/org/transitime/config/DoubleConfigValue.java
+++ b/transitclock/src/main/java/org/transitclock/config/DoubleConfigValue.java
@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.config;
+package org.transitclock.config;
import java.util.List;
diff --git a/transitime/src/main/java/org/transitime/config/FloatConfigValue.java b/transitclock/src/main/java/org/transitclock/config/FloatConfigValue.java
similarity index 97%
rename from transitime/src/main/java/org/transitime/config/FloatConfigValue.java
rename to transitclock/src/main/java/org/transitclock/config/FloatConfigValue.java
index 3e7212735..e1cc7e912 100644
--- a/transitime/src/main/java/org/transitime/config/FloatConfigValue.java
+++ b/transitclock/src/main/java/org/transitclock/config/FloatConfigValue.java
@@ -15,7 +15,7 @@
* along with Transitime.org . If not, see .
*/
-package org.transitime.config;
+package org.transitclock.config;
import java.util.List;
diff --git a/transitime/src/main/java/org/transitime/config/IntegerConfigValue.java b/transitclock/src/main/java/org/transitclock/config/IntegerConfigValue.java
similarity index 97%
rename from transitime/src/main/java/org/transitime/config/IntegerConfigValue.java
rename to transitclock/src/main/java/org/transitclock/config/IntegerConfigValue.java
index d1af693e5..b62cfd15d 100644
--- a/transitime/src/main/java/org/transitime/config/IntegerConfigValue.java
+++ b/transitclock/src/main/java/org/transitclock/config/IntegerConfigValue.java
@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.config;
+package org.transitclock.config;
import java.util.List;
diff --git a/transitime/src/main/java/org/transitime/config/LongConfigValue.java b/transitclock/src/main/java/org/transitclock/config/LongConfigValue.java
similarity index 97%
rename from transitime/src/main/java/org/transitime/config/LongConfigValue.java
rename to transitclock/src/main/java/org/transitclock/config/LongConfigValue.java
index 1c7c6f1ae..f0079222e 100644
--- a/transitime/src/main/java/org/transitime/config/LongConfigValue.java
+++ b/transitclock/src/main/java/org/transitclock/config/LongConfigValue.java
@@ -15,7 +15,7 @@
* along with Transitime.org . If not, see .
*/
-package org.transitime.config;
+package org.transitclock.config;
import java.util.List;
diff --git a/transitime/src/main/java/org/transitime/config/StringConfigValue.java b/transitclock/src/main/java/org/transitclock/config/StringConfigValue.java
similarity index 95%
rename from transitime/src/main/java/org/transitime/config/StringConfigValue.java
rename to transitclock/src/main/java/org/transitclock/config/StringConfigValue.java
index 5915c4c41..a9845380d 100644
--- a/transitime/src/main/java/org/transitime/config/StringConfigValue.java
+++ b/transitclock/src/main/java/org/transitclock/config/StringConfigValue.java
@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.config;
+package org.transitclock.config;
import java.util.List;
diff --git a/transitime/src/main/java/org/transitime/config/StringListConfigValue.java b/transitclock/src/main/java/org/transitclock/config/StringListConfigValue.java
similarity index 98%
rename from transitime/src/main/java/org/transitime/config/StringListConfigValue.java
rename to transitclock/src/main/java/org/transitclock/config/StringListConfigValue.java
index 1971aae91..5216cacd5 100644
--- a/transitime/src/main/java/org/transitime/config/StringListConfigValue.java
+++ b/transitclock/src/main/java/org/transitclock/config/StringListConfigValue.java
@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.config;
+package org.transitclock.config;
import java.util.List;
diff --git a/transitime/src/main/java/org/transitime/config/package-info.java b/transitclock/src/main/java/org/transitclock/config/package-info.java
similarity index 91%
rename from transitime/src/main/java/org/transitime/config/package-info.java
rename to transitclock/src/main/java/org/transitclock/config/package-info.java
index 276199a48..4c0efe697 100644
--- a/transitime/src/main/java/org/transitime/config/package-info.java
+++ b/transitclock/src/main/java/org/transitclock/config/package-info.java
@@ -28,21 +28,21 @@
* return projectId.getValue();
* }
* private static StringConfigValue projectId =
- * new StringConfigValue("transitime.core.projectId", "sfmta");
+ * new StringConfigValue("transitclock.core.projectId", "sfmta");
* }
*
*
* Such a parameter can be set either using a command line java property when the
* application is invoked, such as:
*
- * -Dtransitime.core.projectId=mbta
+ * -Dtransitclock.core.projectId=mbta
*
*
* Or in a configuration file:
*
* {@code
*
- *
+ *
*
*
* sfmta
@@ -51,7 +51,7 @@
* 120
*
*
- *
+ *
* }
*
*
@@ -63,4 +63,4 @@
* @author SkiBu Smith
*
*/
-package org.transitime.config;
\ No newline at end of file
+package org.transitclock.config;
\ No newline at end of file
diff --git a/transitime/src/main/java/org/transitime/configData/AgencyConfig.java b/transitclock/src/main/java/org/transitclock/configData/AgencyConfig.java
similarity index 91%
rename from transitime/src/main/java/org/transitime/configData/AgencyConfig.java
rename to transitclock/src/main/java/org/transitclock/configData/AgencyConfig.java
index 4aaf73a1c..2f52e2b51 100644
--- a/transitime/src/main/java/org/transitime/configData/AgencyConfig.java
+++ b/transitclock/src/main/java/org/transitclock/configData/AgencyConfig.java
@@ -15,9 +15,9 @@
* along with Transitime.org . If not, see .
*/
-package org.transitime.configData;
+package org.transitclock.configData;
-import org.transitime.config.StringConfigValue;
+import org.transitclock.config.StringConfigValue;
/**
* Configuration data commonly used for an agency. By splitting out these
@@ -39,7 +39,7 @@ public static String getAgencyId() {
return projectId.getValue();
}
private static StringConfigValue projectId =
- new StringConfigValue("transitime.core.agencyId",
+ new StringConfigValue("transitclock.core.agencyId",
null,
"Specifies the ID of the agency. Used for the database " +
"name and in the logback configuration to specify the " +
diff --git a/transitime/src/main/java/org/transitime/configData/AvlConfig.java b/transitclock/src/main/java/org/transitclock/configData/AvlConfig.java
similarity index 73%
rename from transitime/src/main/java/org/transitime/configData/AvlConfig.java
rename to transitclock/src/main/java/org/transitclock/configData/AvlConfig.java
index fb6cb6cd1..134c2339c 100644
--- a/transitime/src/main/java/org/transitime/configData/AvlConfig.java
+++ b/transitclock/src/main/java/org/transitclock/configData/AvlConfig.java
@@ -15,13 +15,9 @@
* along with Transitime.org . If not, see .
*/
-package org.transitime.configData;
+package org.transitclock.configData;
-import org.transitime.config.BooleanConfigValue;
-import org.transitime.config.DoubleConfigValue;
-import org.transitime.config.FloatConfigValue;
-import org.transitime.config.IntegerConfigValue;
-import org.transitime.config.StringConfigValue;
+import org.transitclock.config.*;
/**
* Handles the AVL configuration data.
@@ -39,7 +35,7 @@ public static boolean shouldUseJms() {
return shouldUseJms.getValue();
}
private static BooleanConfigValue shouldUseJms =
- new BooleanConfigValue("transitime.avl.shouldUseJms", false,
+ new BooleanConfigValue("transitclock.avl.shouldUseJms", false,
"Specifies whether should use JMS queue for handling " +
"AVL reports. Useful for if feed is read on one machine " +
"but processed on another.");
@@ -48,11 +44,11 @@ public static boolean shouldUseJms() {
* How frequently an AVL feed should be polled for new data.
* @return
*/
- public static int getSecondsBetweenAvlFeedPolling() {
+ public static float getSecondsBetweenAvlFeedPolling() {
return secondsBetweenAvlFeedPolling.getValue();
}
- private static IntegerConfigValue secondsBetweenAvlFeedPolling =
- new IntegerConfigValue("transitime.avl.feedPollingRateSecs", 5,
+ private static FloatConfigValue secondsBetweenAvlFeedPolling =
+ new FloatConfigValue("transitclock.avl.feedPollingRateSecs", 5f,
"How frequently an AVL feed should be polled for new data.");
/**
@@ -63,7 +59,7 @@ public static int getAvlFeedTimeoutInMSecs() {
return avlFeedTimeoutInMSecs.getValue();
}
private static IntegerConfigValue avlFeedTimeoutInMSecs =
- new IntegerConfigValue("transitime.avl.feedTimeoutInMSecs", 10000,
+ new IntegerConfigValue("transitclock.avl.feedTimeoutInMSecs", 10000,
"For when polling AVL XML feed. The feed logs error if "
+ "the timeout value is exceeded when performing the XML "
+ "request.");
@@ -76,11 +72,33 @@ public static double getMaxAvlSpeed() {
return maxAvlSpeed.getValue();
}
private static DoubleConfigValue maxAvlSpeed =
- new DoubleConfigValue("transitime.avl.maxSpeed",
+ new DoubleConfigValue("transitclock.avl.maxSpeed",
31.3, // 31.3m/s = 70mph
"Max speed between AVL reports for a vehicle. If this " +
"value is exceeded then the AVL report is ignored.");
+ private static DoubleConfigValue alternativeMaxSpeed = new DoubleConfigValue("transitclock.avl.alternativemaxspeed",
+ 15.0, // 31.3m/s = 70mph
+ "Alernative max speed between AVL reports for a vehicle. If this " +
+ "value is exceeded then the AVL report is ignored.");
+ public static DoubleConfigValue getAlternativeMaxSpeed() {
+ return alternativeMaxSpeed;
+ }
+ public static void setAlternativeMaxSpeed(DoubleConfigValue alternativeMaxSpeed) {
+ AvlConfig.alternativeMaxSpeed = alternativeMaxSpeed;
+ }
+ /**
+ * Maximum number of stopPaths to look ahead.
+ * @return max number
+ */
+ public static int getMaxStopPathsAhead()
+ {
+ return maxStopPathsAhead.getValue();
+ }
+ private static IntegerConfigValue maxStopPathsAhead =
+ new IntegerConfigValue("transitclock.avl.maxStopPathsAhead",
+ 999,
+ "Max stopPaths ahead to look for match.");
/**
* If AVL report speed is below this threshold then the heading is not
* considered valid.
@@ -90,7 +108,7 @@ public static double minSpeedForValidHeading() {
return minSpeedForValidHeading.getValue();
}
private static DoubleConfigValue minSpeedForValidHeading =
- new DoubleConfigValue("transitime.avl.minSpeedForValidHeading",
+ new DoubleConfigValue("transitclock.avl.minSpeedForValidHeading",
1.5, // 1.5m/s = .34mph
"If AVL report speed is below this threshold then the " +
"heading is not considered valid.");
@@ -110,7 +128,7 @@ public static String getMinAvlLatitudeParamName() {
return minAvlLatitude.getID();
}
private static FloatConfigValue minAvlLatitude =
- new FloatConfigValue("transitime.avl.minLatitude", 15.0f,
+ new FloatConfigValue("transitclock.avl.minLatitude", 15.0f,
"For filtering out bad AVL reports. The default values " +
"of latitude 15.0 to 55.0 and longitude of -135.0 to " +
"-60.0 are for North America, including Mexico and " +
@@ -124,7 +142,7 @@ public static String getMaxAvlLatitudeParamName() {
return maxAvlLatitude.getID();
}
private static FloatConfigValue maxAvlLatitude =
- new FloatConfigValue("transitime.avl.maxLatitude", 55.0f,
+ new FloatConfigValue("transitclock.avl.maxLatitude", 55.0f,
"For filtering out bad AVL reports. The default values " +
"of latitude 15.0 to 55.0 and longitude of -135.0 to " +
"-60.0 are for North America, including Mexico and " +
@@ -138,7 +156,7 @@ public static String getMinAvlLongitudeParamName() {
return minAvlLongitude.getID();
}
private static FloatConfigValue minAvlLongitude =
- new FloatConfigValue("transitime.avl.minLongitude", -135.0f,
+ new FloatConfigValue("transitclock.avl.minLongitude", -135.0f,
"For filtering out bad AVL reports. The default values " +
"of latitude 15.0 to 55.0 and longitude of -135.0 to " +
"-60.0 are for North America, including Mexico and " +
@@ -152,7 +170,7 @@ public static String getMaxAvlLongitudeParamName() {
return maxAvlLongitude.getID();
}
private static FloatConfigValue maxAvlLongitude =
- new FloatConfigValue("transitime.avl.maxLongitude", -60.0f,
+ new FloatConfigValue("transitclock.avl.maxLongitude", -60.0f,
"For filtering out bad AVL reports. The default values " +
"of latitude 15.0 to 55.0 and longitude of -135.0 to " +
"-60.0 are for North America, including Mexico and " +
@@ -163,19 +181,19 @@ public static String getMaxAvlLongitudeParamName() {
* So can filter out unpredictable assignments such as for training coaches,
* service vehicles, or simply vehicles that are not in service and should
* not be attempted to be made predictable. Returns empty string, the
- * default value if transitime.avl.unpredictableAssignmentsRegEx is not set.
+ * default value if transitclock.avl.unpredictableAssignmentsRegEx is not set.
*/
public static String getUnpredictableAssignmentsRegEx() {
return unpredictableAssignmentsRegEx.getValue();
}
private static StringConfigValue unpredictableAssignmentsRegEx =
- new StringConfigValue("transitime.avl.unpredictableAssignmentsRegEx",
+ new StringConfigValue("transitclock.avl.unpredictableAssignmentsRegEx",
"", // default value
"So can filter out unpredictable assignments such as for " +
"training coaches, service vehicles, or simply vehicles " +
"that are not in service and should not be attempted to " +
"be made predictable. Returns empty string, the default " +
- "value if transitime.avl.unpredictableAssignmentsRegEx " +
+ "value if transitclock.avl.unpredictableAssignmentsRegEx " +
"is not set.");
/**
@@ -189,7 +207,7 @@ public static int getMinTimeBetweenAvlReportsSecs() {
}
private static IntegerConfigValue minTimeBetweenAvlReportsSecs =
new IntegerConfigValue(
- "transitime.avl.minTimeBetweenAvlReportsSecs",
+ "transitclock.avl.minTimeBetweenAvlReportsSecs",
5,
"Minimum allowable time in seconds between AVL reports for "
+ "a vehicle. If get a report closer than this number of "
@@ -208,7 +226,14 @@ public static boolean shouldLogToStdOut() {
return shouldLogToStdOut.getValue();
}
private static BooleanConfigValue shouldLogToStdOut =
- new BooleanConfigValue("transitime.avl.shouldLogToStdOut", false,
+ new BooleanConfigValue("transitclock.avl.shouldLogToStdOut", false,
"For debugging. Logs each AVL report to stdout if set "
+ "to true. Default is false.");
+
+ public static boolean shouldTryTimeZoneCorrection() { return timeZoneCorrectionValue.getValue() != null; }
+ public static Integer getTimeZoneCorrection() { return timeZoneCorrectionValue.getValue(); }
+ private static IntegerConfigValue timeZoneCorrectionValue =
+ new IntegerConfigValue("transitclock.avl.tzCorrection",
+ null,
+ "Attempt to correct for a Time Zone issue by subtracting this value in seconds");
}
diff --git a/transitime/src/main/java/org/transitime/configData/CoreConfig.java b/transitclock/src/main/java/org/transitclock/configData/CoreConfig.java
old mode 100644
new mode 100755
similarity index 81%
rename from transitime/src/main/java/org/transitime/configData/CoreConfig.java
rename to transitclock/src/main/java/org/transitclock/configData/CoreConfig.java
index da90ace81..a5cd69076
--- a/transitime/src/main/java/org/transitime/configData/CoreConfig.java
+++ b/transitclock/src/main/java/org/transitclock/configData/CoreConfig.java
@@ -13,18 +13,18 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.configData;
+package org.transitclock.configData;
import java.util.ArrayList;
import java.util.List;
-import org.transitime.config.BooleanConfigValue;
-import org.transitime.config.DoubleConfigValue;
-import org.transitime.config.FloatConfigValue;
-import org.transitime.config.IntegerConfigValue;
-import org.transitime.config.StringConfigValue;
-import org.transitime.config.StringListConfigValue;
-import org.transitime.utils.Time;
+import org.transitclock.config.BooleanConfigValue;
+import org.transitclock.config.DoubleConfigValue;
+import org.transitclock.config.FloatConfigValue;
+import org.transitclock.config.IntegerConfigValue;
+import org.transitclock.config.StringConfigValue;
+import org.transitclock.config.StringListConfigValue;
+import org.transitclock.utils.Time;
/**
* Handles the core configuration data file. Allows parameters to be read in
@@ -39,7 +39,16 @@
public class CoreConfig {
// Database params
-
+ /**
+ * How many days data to look back at to fill cache
+ * @return
+ */
+ public static int getDaysPopulateHistoricalCache() {
+ return daysPopulateHistoricalCache.getValue();
+ }
+ private static IntegerConfigValue daysPopulateHistoricalCache =
+ new IntegerConfigValue("transitclock.cache.core.daysPopulateHistoricalCache", 0,
+ "How many days data to read in to populate historical cache on start up.");
/**
* When in playback mode or some other situations don't want to store
* generated data such as arrivals/departures, events, and such to the
@@ -51,19 +60,33 @@ public static boolean storeDataInDatabase() {
return storeDataInDatabase.getValue();
}
private static BooleanConfigValue storeDataInDatabase =
- new BooleanConfigValue("transitime.db.storeDataInDatabase",
+ new BooleanConfigValue("transitclock.db.storeDataInDatabase",
true,
"When in playback mode or some other situations don't " +
"want to store generated data such as arrivals/" +
"departures, events, and such to the database because " +
"only debugging.");
-
-
+
+ /**
+ * Prediction events represent debugging information that adds additional load
+ * to datbase queue subsystem. Enable for debugging purposes only or with caution.
+ * @return
+ */
+ public static boolean storePredictionEventsInDatabase() {
+ return storePredictionEventsInDatabase.getValue();
+ }
+ private static BooleanConfigValue storePredictionEventsInDatabase =
+ new BooleanConfigValue("transitclock.db.storePredictionEventsInDatabase",
+ false,
+ "PredictionEvents represent debugging information " +
+ "and can cause large load on database queues. " +
+ "Enable with caution.");
+
/**
* When batching large amount of AVL data through system to generate
* improved schedule time (as has been done for Zhengzhou) it takes huge
* amount of time to process everything. To speed things up you can set
- * -Dtransitime.core.onlyNeedArrivalDepartures=true such that the system
+ * -Dtransitclock.core.onlyNeedArrivalDepartures=true such that the system
* will be sped up by not generating nor logging predictions, not logging
* AVL data nor storing it in db, and not logging nor storing match data in
* db.
@@ -74,20 +97,20 @@ public static boolean onlyNeedArrivalDepartures() {
return onlyNeedArrivalDepartures.getValue();
}
private static BooleanConfigValue onlyNeedArrivalDepartures =
- new BooleanConfigValue("transitime.core.onlyNeedArrivalDepartures",
+ new BooleanConfigValue("transitclock.core.onlyNeedArrivalDepartures",
false,
"When batching large amount of AVL data through system " +
"to generate improved schedule time (as has been done " +
"for Zhengzhou) it takes huge amount of time to process " +
"everything. To speed things up you can set " +
- "-Dtransitime.core.onlyNeedArrivalDepartures=true such " +
+ "-Dtransitclock.core.onlyNeedArrivalDepartures=true such " +
"that the system will be sped up by not generating nor " +
"logging predictions, not logging AVL data nor storing " +
"it in db, and not logging nor storing match data in db.");
/**
* When in batch mode can flood db with lots of objects. If
- * transitime.core.pauseIfDbQueueFilling is set to true then when objects
+ * transitclock.core.pauseIfDbQueueFilling is set to true then when objects
* are put into the DataDbLogger queue the calling thread will be
* temporarily suspended so that the separate thread can run to write
* to the db and thereby empty out the queue.
@@ -97,10 +120,10 @@ public static boolean pauseIfDbQueueFilling() {
return pauseIfDbQueueFilling.getValue();
}
private static BooleanConfigValue pauseIfDbQueueFilling =
- new BooleanConfigValue("transitime.core.pauseIfDbQueueFilling",
+ new BooleanConfigValue("transitclock.core.pauseIfDbQueueFilling",
false,
"When in batch mode can flood db with lots of objects. If" +
- "transitime.core.pauseIfDbQueueFilling is set to true " +
+ "transitclock.core.pauseIfDbQueueFilling is set to true " +
"then when objects are put into the DataDbLogger queue " +
"the calling thread will be temporarily suspended so " +
"that the separate thread can run to write to the db and " +
@@ -118,10 +141,10 @@ public static List getOptionalModules() {
private static List optionalModulesDefaultList = new ArrayList();
static {
// Can add all the modules that should be started as default here
- //optionalModulesDefaultList.add("org.transitime.avl.NextBusAvlModule");
+ //optionalModulesDefaultList.add("org.transitclock.avl.NextBusAvlModule");
}
private static StringListConfigValue optionalModules =
- new StringListConfigValue("transitime.modules.optionalModulesList",
+ new StringListConfigValue("transitclock.modules.optionalModulesList",
optionalModulesDefaultList,
"The semicolon separated list of names of all of the " +
"modules that should be automatically started.");
@@ -139,13 +162,13 @@ public static double getMaxDistanceFromSegment() {
return maxDistanceFromSegment.getValue();
}
private static DoubleConfigValue maxDistanceFromSegment =
- new DoubleConfigValue("transitime.core.maxDistanceFromSegment",
+ new DoubleConfigValue("transitclock.core.maxDistanceFromSegment",
60.0,
"How far a location can be from a path segment and still "
+ "be considered a match. Can be overridden on a per route "
+ "basis via max_distance supplemental column of route GTFS "
+ "data. When auto assigning, the parameter "
- + "transitime.core.maxDistanceFromSegmentForAutoAssigning "
+ + "transitclock.core.maxDistanceFromSegmentForAutoAssigning "
+ "is used instead.");
/**
@@ -157,7 +180,7 @@ public static double getMaxDistanceFromSegmentForAutoAssigning() {
return maxDistanceFromSegmentForAutoAssigning.getValue();
}
private static DoubleConfigValue maxDistanceFromSegmentForAutoAssigning =
- new DoubleConfigValue("transitime.core.maxDistanceFromSegmentForAutoAssigning",
+ new DoubleConfigValue("transitclock.core.maxDistanceFromSegmentForAutoAssigning",
60.0,
"How far a location can be from a path segment and still "
+ "be considered a match when auto assigning a vehicle. Auto "
@@ -182,7 +205,7 @@ public static int getAllowableNumberOfBadMatches() {
return allowableNumberOfBadMatches.getValue();
}
private static IntegerConfigValue allowableNumberOfBadMatches =
- new IntegerConfigValue("transitime.core.allowableNumberOfBadMatches",
+ new IntegerConfigValue("transitclock.core.allowableNumberOfBadMatches",
2,
"How many bad spatial/temporal matches a predictable " +
"vehicle can have in a row before the vehicle is made " +
@@ -198,8 +221,8 @@ public static float getMaxHeadingOffsetFromSegment() {
return maxHeadingOffsetFromSegment.getValue();
}
private static FloatConfigValue maxHeadingOffsetFromSegment =
- new FloatConfigValue("transitime.core.maxHeadingOffsetFromSegment",
- 135.0f,
+ new FloatConfigValue("transitclock.core.maxHeadingOffsetFromSegment",
+ 360.0f,
"How far heading in degrees of vehicle can be away from " +
"path segment and still be considered a match. Needs to " +
"be pretty lenient because stopPaths and heading might " +
@@ -218,7 +241,7 @@ public static double getDistanceFromEndOfBlockForInitialMatching() {
}
private static DoubleConfigValue distanceFromEndOfBlockForInitialMatching =
new DoubleConfigValue(
- "transitime.core.distanceFromEndOfBlockForInitialMatching",
+ "transitclock.core.distanceFromEndOfBlockForInitialMatching",
250.0,
"For initial matching of vehicle to block assignment. If " +
"vehicle is closer than this distance from the end of " +
@@ -239,7 +262,7 @@ public static double getDistanceFromLastStopForEndMatching() {
}
private static DoubleConfigValue distanceFromLastStopForEndMatching =
new DoubleConfigValue(
- "transitime.core.distanceFromLastStopForEndMatching",
+ "transitclock.core.distanceFromLastStopForEndMatching",
250.0,
"How close vehicle needs to be from the last stop of the " +
"block such that the next AVL report should possibly be " +
@@ -263,7 +286,7 @@ public static float getDeadheadingShortVersusLongDistance() {
return deadheadingShortVersusLongDistance.getValue();
}
private static FloatConfigValue deadheadingShortVersusLongDistance =
- new FloatConfigValue("transitime.core.deadheadingShortVersusLongDistance",
+ new FloatConfigValue("transitclock.core.deadheadingShortVersusLongDistance",
1000.0f,
"For determining if enough time to deadhead to beginning " +
"of a trip. If vehicles are far away then they are more " +
@@ -280,7 +303,7 @@ public static float getShortDistanceDeadheadingSpeed() {
return shortDistanceDeadheadingSpeed.getValue();
}
private static FloatConfigValue shortDistanceDeadheadingSpeed =
- new FloatConfigValue("transitime.core.shortDistanceDeadheadingSpeed",
+ new FloatConfigValue("transitclock.core.shortDistanceDeadheadingSpeed",
4.0f, // 4.0m/s is about 8 mph
"Part of determining if enough time to deadhead to layover.");
@@ -288,7 +311,7 @@ public static float getLongDistanceDeadheadingSpeed() {
return longDistanceDeadheadingSpeed.getValue();
}
private static FloatConfigValue longDistanceDeadheadingSpeed =
- new FloatConfigValue("transitime.core.longDistanceDeadheadingSpeed",
+ new FloatConfigValue("transitclock.core.longDistanceDeadheadingSpeed",
10.0f, // 10.0m/s is about 20mph
"Part of determining if enough time to deadhead to layover.");
@@ -309,8 +332,8 @@ public static int getMaxPredictionsTimeForDbSecs() {
return maxPredictionsTimeForDbSecs.getValue();
}
private static IntegerConfigValue maxPredictionsTimeForDbSecs =
- new IntegerConfigValue("transitime.core.maxPredictionTimeForDbSecs",
- 0*Time.SEC_PER_MIN,
+ new IntegerConfigValue("transitclock.core.maxPredictionTimeForDbSecs",
+ 30*Time.SEC_PER_MIN,
"For determining if prediction should be stored in db. " +
"Set to 0 to never store predictions. A very large " +
"number of predictions is created so be careful with " +
@@ -330,7 +353,7 @@ public static int getAllowableEarlyForLayoverSeconds() {
return allowableEarlyForLayoverSeconds.getValue();
}
private static IntegerConfigValue allowableEarlyForLayoverSeconds =
- new IntegerConfigValue("transitime.core.allowableEarlyForLayoverSeconds",
+ new IntegerConfigValue("transitclock.core.allowableEarlyForLayoverSeconds",
60 * Time.SEC_PER_MIN,
"How early a vehicle can be and still be matched to a " +
"layover. Needs to be pretty large because sometimes " +
@@ -356,7 +379,7 @@ public static String getAllowableEarlySecondsId() {
return allowableEarlySeconds.getID();
}
private static IntegerConfigValue allowableEarlySeconds =
- new IntegerConfigValue("transitime.core.allowableEarlySeconds",
+ new IntegerConfigValue("transitclock.core.allowableEarlySeconds",
15 * Time.SEC_PER_MIN,
"How early a vehicle can be and still be matched to its " +
"block assignment. If when a new AVL report is received " +
@@ -379,7 +402,7 @@ public static String getAllowableLateSecondsId() {
return allowableLateSeconds.getID();
}
private static IntegerConfigValue allowableLateSeconds =
- new IntegerConfigValue("transitime.core.allowableLateSeconds",
+ new IntegerConfigValue("transitclock.core.allowableLateSeconds",
90 * Time.SEC_PER_MIN,
"How late a vehicle can be and still be matched to its " +
"block assignment. If when a new AVL report is received " +
@@ -396,7 +419,7 @@ public static int getAllowableEarlySecondsForInitialMatching() {
return allowableEarlySecondsForInitialMatching.getValue();
}
private static IntegerConfigValue allowableEarlySecondsForInitialMatching =
- new IntegerConfigValue("transitime.core.allowableEarlySecondsForInitialMatching",
+ new IntegerConfigValue("transitclock.core.allowableEarlySecondsForInitialMatching",
10 * Time.SEC_PER_MIN,
"How early a vehicle can be in seconds and still be " +
"matched to its block assignment.");
@@ -410,7 +433,7 @@ public static int getAllowableLateSecondsForInitialMatching() {
return allowableLateSecondsForInitialMatching.getValue();
}
private static IntegerConfigValue allowableLateSecondsForInitialMatching =
- new IntegerConfigValue("transitime.core.allowableLateSecondsForInitialMatching",
+ new IntegerConfigValue("transitclock.core.allowableLateSecondsForInitialMatching",
20 * Time.SEC_PER_MIN,
"How late a vehicle can be in seconds and still be " +
"matched to its block assignment.");
@@ -427,7 +450,7 @@ public static double getDistanceBetweenAvlsForInitialMatchingWithoutHeading() {
return distanceBetweenAvlsForInitialMatchingWithoutHeading.getValue();
}
private static DoubleConfigValue distanceBetweenAvlsForInitialMatchingWithoutHeading =
- new DoubleConfigValue("transitime.core.distanceBetweenAvlsForInitialMatchingWithoutHeading",
+ new DoubleConfigValue("transitclock.core.distanceBetweenAvlsForInitialMatchingWithoutHeading",
100.0,
"For initial matching vehicle to assignment when there " +
"isn't any heading information. In that case also want " +
@@ -450,7 +473,7 @@ public static double getDistanceFromLayoverForEarlyDeparture() {
return distanceFromLayoverForEarlyDeparture.getValue();
}
private static DoubleConfigValue distanceFromLayoverForEarlyDeparture =
- new DoubleConfigValue("transitime.core.distanceFromLayoverForEarlyDeparture",
+ new DoubleConfigValue("transitclock.core.distanceFromLayoverForEarlyDeparture",
180.0,
"How far along path past a layover stop a vehicle needs "
+ "to be in order for it to be considered an early "
@@ -472,7 +495,7 @@ public static double getLayoverDistance() {
return layoverDistance.getValue();
}
private static DoubleConfigValue layoverDistance =
- new DoubleConfigValue("transitime.core.layoverDistance",
+ new DoubleConfigValue("transitclock.core.layoverDistance",
2000.0,
"How far vehicle can be away from layover stop and still "
+ "match to it. For when not deadheading to a trip. This "
@@ -498,7 +521,7 @@ public static int getAllowableEarlyTimeForEarlyDepartureSecs() {
return allowableEarlyTimeForEarlyDepartureSecs.getValue();
}
private static IntegerConfigValue allowableEarlyTimeForEarlyDepartureSecs =
- new IntegerConfigValue("transitime.core.allowableEarlyTimeForEarlyDepartureSecs",
+ new IntegerConfigValue("transitclock.core.allowableEarlyTimeForEarlyDepartureSecs",
5*Time.SEC_PER_MIN,
"How early in seconds a vehicle can have left terminal and have it be considered "
+ "an early departure instead of just moving around within "
@@ -518,7 +541,7 @@ public static int getAllowableEarlyDepartureTimeForLoggingEvent() {
}
private static IntegerConfigValue allowableEarlyDepartureTimeForLoggingEvent =
new IntegerConfigValue(
- "transitime.core.allowableEarlyDepartureTimeForLoggingEvent",
+ "transitclock.core.allowableEarlyDepartureTimeForLoggingEvent",
60,
"How early in seconds a vehicle can depart a terminal "
+ "before it registers a VehicleEvent indicating a problem.");
@@ -534,7 +557,7 @@ public static int getAllowableLateDepartureTimeForLoggingEvent() {
}
private static IntegerConfigValue allowableLateDepartureTimeForLoggingEvent =
new IntegerConfigValue(
- "transitime.core.allowableLateDepartureTimeForLoggingEvent",
+ "transitclock.core.allowableLateDepartureTimeForLoggingEvent",
4*Time.SEC_PER_MIN,
"How late in seconds a vehicle can depart a terminal "
+ "before it registers a VehicleEvent indicating a problem.");
@@ -552,7 +575,7 @@ public static int getAllowableLateAtTerminalForLoggingEvent() {
}
private static IntegerConfigValue allowableLateAtTerminalForLoggingEvent =
new IntegerConfigValue(
- "transitime.core.allowableLateAtTerminalForLoggingEvent",
+ "transitclock.core.allowableLateAtTerminalForLoggingEvent",
1*Time.SEC_PER_MIN,
"If a vehicle is sitting at a terminal and provides "
+ "another GPS report indicating that it is more than this "
@@ -571,7 +594,7 @@ public static double getBeforeStopDistance() {
}
private static DoubleConfigValue beforeStopDistance =
new DoubleConfigValue(
- "transitime.core.beforeStopDistance",
+ "transitclock.core.beforeStopDistance",
50.0,
"How far a vehicle can be ahead of a stop in meters and " +
"be considered to have arrived.");
@@ -587,14 +610,13 @@ public static double getAfterStopDistance() {
}
private static DoubleConfigValue afterStopDistance =
new DoubleConfigValue(
- "transitime.core.afterStopDistance",
+ "transitclock.core.afterStopDistance",
50.0,
"How far a vehicle can be past a stop in meters and " +
"still be considered at the stop.");
/**
- * How far a vehicle can be past a stop in meters and still be considered at
- * the stop.
+ * Returns how long driver is expected to have a break for a stop.
*
* @return
*/
@@ -603,10 +625,9 @@ public static int getDefaultBreakTimeSec() {
}
private static IntegerConfigValue defaultBreakTimeSec =
new IntegerConfigValue(
- "transitime.core.defaultBreakTimeSec",
+ "transitclock.core.defaultBreakTimeSec",
0,
- "How far a vehicle can be past a stop in meters and " +
- "still be considered at the stop.");
+ "How long driver is expected to have a break for a stop.");
/**
* How much worse it is for a vehicle to be early as opposed to late when
@@ -619,7 +640,7 @@ public static double getEarlyToLateRatio() {
}
private static DoubleConfigValue earlyToLateRatio =
new DoubleConfigValue(
- "transitime.core.earlyToLateRatio",
+ "transitclock.core.earlyToLateRatio",
3.0,
"How much worse it is for a vehicle to be early as " +
"opposed to late when determining schedule adherence.");
@@ -637,7 +658,7 @@ public static boolean exclusiveBlockAssignments() {
}
private static BooleanConfigValue exclusiveBlockAssignments =
new BooleanConfigValue(
- "transitime.core.exclusiveBlockAssignments",
+ "transitclock.core.exclusiveBlockAssignments",
true,
"True if block assignments should be exclusive. If set to "
+ "true then when a vehicle is assigned to a block the "
@@ -654,7 +675,7 @@ public static int getTimeForDeterminingNoProgress() {
}
private static IntegerConfigValue timeForDeterminingNoProgress =
new IntegerConfigValue(
- "transitime.core.timeForDeterminingNoProgress",
+ "transitclock.core.timeForDeterminingNoProgress",
8 * Time.MS_PER_MIN,
"The interval in msec at which look at vehicle's history "
+ "to determine if it is not making any progress. A value"
@@ -667,7 +688,7 @@ public static double getMinDistanceForNoProgress() {
}
private static DoubleConfigValue minDistanceForNoProgress =
new DoubleConfigValue(
- "transitime.core.minDistanceForNoProgress",
+ "transitclock.core.minDistanceForNoProgress",
60.0,
"Minimum distance vehicle is expected to travel over "
+ "timeForDeterminingNoProgress to indicate vehicle is "
@@ -676,7 +697,7 @@ public static double getMinDistanceForNoProgress() {
+ "made unpredictable.");
/**
- * transitime.core.timeForDeterminingDelayedSecs
+ * transitclock.core.timeForDeterminingDelayedSecs
*
* @return
*/
@@ -685,13 +706,13 @@ public static int getTimeForDeterminingDelayedSecs() {
}
private static IntegerConfigValue timeForDeterminingDelayedSecs =
new IntegerConfigValue(
- "transitime.core.timeForDeterminingDelayedSecs",
+ "transitclock.core.timeForDeterminingDelayedSecs",
4 * Time.SEC_PER_MIN,
"The interval in msec at which look at vehicle's history "
+ "to determine if it is delayed.");
/**
- * transitime.core.minDistanceForDelayed
+ * transitclock.core.minDistanceForDelayed
*
* @return
*/
@@ -700,7 +721,7 @@ public static double getMinDistanceForDelayed() {
}
private static DoubleConfigValue minDistanceForDelayed =
new DoubleConfigValue(
- "transitime.core.minDistanceForDelayed",
+ "transitclock.core.minDistanceForDelayed",
60.0,
"Minimum distance vehicle is expected to travel over "
+ "timeForDeterminingDelayed to indicate vehicle is "
@@ -711,7 +732,7 @@ public static int getMatchHistoryMaxSize() {
}
private static IntegerConfigValue matchHistoryMaxSize =
new IntegerConfigValue(
- "transitime.core.matchHistoryMaxSize",
+ "transitclock.core.matchHistoryMaxSize",
20,
"How many matches are kept in history for vehicle so that "
+ "can can do things such as look back at history to "
@@ -725,7 +746,7 @@ public static int getAvlHistoryMaxSize() {
}
private static IntegerConfigValue avlHistoryMaxSize =
new IntegerConfigValue(
- "transitime.core.avlHistoryMaxSize",
+ "transitclock.core.avlHistoryMaxSize",
20,
"How many AVL reports are kept in history for vehicle so "
+ "that can can do things such as look back at history to "
@@ -734,15 +755,50 @@ public static int getAvlHistoryMaxSize() {
+ "timeForDeterminingNoProgress. If GPS rate is high then "
+ "this value will need to be high as well.");
+ private static IntegerConfigValue eventHistoryMaxSize =
+ new IntegerConfigValue(
+ "transitclock.core.eventHistoryMaxSize",
+ 20,
+ "How many arrival depature event reports are kept in history for vehicle so "
+ + "that can can do things such as look back at history");
+
+ public static int getEventHistoryMaxSize() {
+ return eventHistoryMaxSize.getValue();
+ }
public static String getPidFileDirectory() {
return pidFileDirectory.getValue();
}
private static StringConfigValue pidFileDirectory =
new StringConfigValue(
- "transitime.core.pidDirectory",
- "/home/ec2-user/pids/",
+ "transitclock.core.pidDirectory",
+ "/usr/local/transitclock/",
"Directory where pid file should be written. The pid file "
+ "can be used by monit to make sure that core process is "
+ "always running.");
+ /**
+ * Whether historical arrival/departure caches should be filled on
+ * Core start. These are used for some prediction generators, but
+ * not the default.
+ * @return
+ */
+ public static boolean getFillHistoricalCaches() {
+ return fillHistoricalCaches.getValue();
+ }
+ private static BooleanConfigValue fillHistoricalCaches =
+ new BooleanConfigValue(
+ "transitclock.core.fillHistoricalCaches",
+ false,
+ "whether historical caches should be filled on Core start.");
+
+ private static BooleanConfigValue strictTripAssignmentMatch =
+ new BooleanConfigValue(
+ "transitclock.core.strictTripAssignmentMatch",
+ false,
+ "AVL trip assignments are trusted, do not discard for block assignment. Retain the" +
+ "temporal implications of the trip assignment");
+
+ public static boolean tryForExactTripMatch() {
+ return strictTripAssignmentMatch.getValue();
+ }
}
diff --git a/transitime/src/main/java/org/transitime/configData/DbSetupConfig.java b/transitclock/src/main/java/org/transitclock/configData/DbSetupConfig.java
similarity index 78%
rename from transitime/src/main/java/org/transitime/configData/DbSetupConfig.java
rename to transitclock/src/main/java/org/transitclock/configData/DbSetupConfig.java
index 8af50da8b..8b985a14f 100644
--- a/transitime/src/main/java/org/transitime/configData/DbSetupConfig.java
+++ b/transitclock/src/main/java/org/transitclock/configData/DbSetupConfig.java
@@ -15,10 +15,10 @@
* along with Transitime.org . If not, see .
*/
-package org.transitime.configData;
+package org.transitclock.configData;
-import org.transitime.config.IntegerConfigValue;
-import org.transitime.config.StringConfigValue;
+import org.transitclock.config.IntegerConfigValue;
+import org.transitclock.config.StringConfigValue;
/**
* Config params for database
@@ -32,17 +32,17 @@ public static String getDbName() {
return dbName.getValue();
}
private static StringConfigValue dbName =
- new StringConfigValue("transitime.db.dbName",
+ new StringConfigValue("transitclock.db.dbName",
null, // Null as default to use the projectId
"Specifies the name of the database. If not set then the "
- + "transitime.core.agencyId will be used.");
+ + "transitclock.core.agencyId will be used.");
public static String getDbHost() {
return dbHost.getValue();
}
private static StringConfigValue dbHost =
- new StringConfigValue("transitime.db.dbHost",
+ new StringConfigValue("transitclock.db.dbHost",
null, // Null as default so can get it from hibernate config
"Specifies the name of the machine the database for the " +
"project resides on. Use null value to use values from " +
@@ -53,7 +53,7 @@ public static String getDbType() {
return dbType.getValue();
}
private static StringConfigValue dbType =
- new StringConfigValue("transitime.db.dbType",
+ new StringConfigValue("transitclock.db.dbType",
"mysql",
"Specifies type of database when creating the URL to "
+ "connect to the database. Can be mysql or postgresql. "
@@ -63,7 +63,7 @@ public static String getDbUserName() {
return dbUserName.getValue();
}
private static StringConfigValue dbUserName =
- new StringConfigValue("transitime.db.dbUserName",
+ new StringConfigValue("transitclock.db.dbUserName",
null,
"Specifies login for the project database. Use null " +
"value to use values from hibernate config file.");
@@ -72,7 +72,7 @@ public static String getDbPassword() {
return dbPassword.getValue();
}
private static StringConfigValue dbPassword =
- new StringConfigValue("transitime.db.dbPassword",
+ new StringConfigValue("transitclock.db.dbPassword",
null,
"Specifies password for the project database. Use null " +
"value to use values from hibernate config file.",
@@ -82,7 +82,7 @@ public static Integer getSocketTimeoutSec() {
return socketTimeoutSec.getValue();
}
public static IntegerConfigValue socketTimeoutSec =
- new IntegerConfigValue("transitime.db.socketTimeoutSec",
+ new IntegerConfigValue("transitclock.db.socketTimeoutSec",
60,
"So can set low-level socket timeout for JDBC connections. "
+ "Useful for when a session dies during a request, such as "
@@ -97,11 +97,19 @@ public static String getHibernateConfigFileName() {
return hibernateConfigFileName.getValue();
}
private static StringConfigValue hibernateConfigFileName =
- new StringConfigValue("transitime.hibernate.configFile",
- "mysql_hibernate.cfg.xml",
+ new StringConfigValue("transitclock.hibernate.configFile",
+ "hsql_hibernate.cfg.xml",
"Specifies the database dependent hibernate.cfg.xml file "
+ "to use to configure hibernate. The system will look both "
+ "on the file system and in the classpath. Can specify "
+ "mysql_hibernate.cfg.xml or postgres_hibernate.cfg.xml");
+ public static Integer getBatchSize() {
+ return batchSize.getValue();
+ }
+ private static IntegerConfigValue batchSize =
+ new IntegerConfigValue("transitclock.db.batchSize",
+ 100,
+ "Specifies the database batch size, defaults to 100");
+
}
diff --git a/transitclock/src/main/java/org/transitclock/configData/HeadwayConfig.java b/transitclock/src/main/java/org/transitclock/configData/HeadwayConfig.java
new file mode 100644
index 000000000..b40926418
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/configData/HeadwayConfig.java
@@ -0,0 +1,23 @@
+package org.transitclock.configData;
+
+import org.transitclock.config.BooleanConfigValue;
+
+public class HeadwayConfig {
+
+ public static Boolean matchByTripPattern() {
+ return matchByTripPattern.getValue();
+ }
+ private static BooleanConfigValue matchByTripPattern =
+ new BooleanConfigValue("transitclock.headway.matchByTripPattern",
+ true,
+ "Specifies whether to look for vehicles with matching tripPatterns when generating headways.");
+
+
+ public static Boolean calculateSystemVariance() {
+ return calculateSystemVariance.getValue();
+ }
+ private static BooleanConfigValue calculateSystemVariance =
+ new BooleanConfigValue("transitclock.headway.calculateSystemVariance",
+ false,
+ "Specifies whether to calculate and set system variance values for headways.");
+}
diff --git a/transitclock/src/main/java/org/transitclock/configData/ReportingConfig.java b/transitclock/src/main/java/org/transitclock/configData/ReportingConfig.java
new file mode 100644
index 000000000..54cf73fe2
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/configData/ReportingConfig.java
@@ -0,0 +1,41 @@
+package org.transitclock.configData;
+
+import org.transitclock.config.DoubleConfigValue;
+import org.transitclock.config.IntegerConfigValue;
+import org.transitclock.utils.Time;
+
+public class ReportingConfig {
+
+ public static int getMaxSchedAdh() {
+ return maxSchedAdhSec.getValue();
+ }
+ private static IntegerConfigValue maxSchedAdhSec =
+ new IntegerConfigValue(
+ "transitclock.speedMap.maxSchedAdhSec",
+ 30* Time.SEC_PER_MIN,
+ "Maximum allowable schedule adherence in sec");
+
+ public static double getMaxStopPathSpeedMps() {
+ return maxStopPathSpeedMps.getValue();
+ }
+ private static DoubleConfigValue maxStopPathSpeedMps =
+ new DoubleConfigValue("transitclock.speedMap.maxStopPathSpeed",
+ 27.0, // 27.0m/s = 60mph
+ "If a stop path is determined to have a higher "
+ + "speed than this value in meters/second then the speed "
+ + "will be decreased to meet this limit. Purpose is "
+ + "to make sure that don't get invalid speed values due to "
+ + "bad data.");
+
+ public static double getMinStopPathSpeedMps() {
+ return minStopPathSpeedMps.getValue();
+ }
+ private static DoubleConfigValue minStopPathSpeedMps =
+ new DoubleConfigValue("transitclock.speedMap.minStopPathSpeed",
+ 0.0,
+ "If a stop path is determined to have a lower "
+ + "speed than this value in meters/second then the speed "
+ + "will be decreased to meet this limit. Purpose is "
+ + "to make sure that don't get invalid speed values due to "
+ + "bad data.");
+}
diff --git a/transitime/src/main/java/org/transitime/configData/RmiConfig.java b/transitclock/src/main/java/org/transitclock/configData/RmiConfig.java
similarity index 90%
rename from transitime/src/main/java/org/transitime/configData/RmiConfig.java
rename to transitclock/src/main/java/org/transitclock/configData/RmiConfig.java
index a02fe1316..e99bb6dbe 100644
--- a/transitime/src/main/java/org/transitime/configData/RmiConfig.java
+++ b/transitclock/src/main/java/org/transitclock/configData/RmiConfig.java
@@ -15,10 +15,10 @@
* along with Transitime.org . If not, see .
*/
-package org.transitime.configData;
+package org.transitclock.configData;
-import org.transitime.config.IntegerConfigValue;
-import org.transitime.config.StringConfigValue;
+import org.transitclock.config.IntegerConfigValue;
+import org.transitclock.config.StringConfigValue;
/**
* Config params for RMI
@@ -40,7 +40,7 @@ public static String rmiHost() {
return rmiHost.getValue();
}
private static StringConfigValue rmiHost =
- new StringConfigValue("transitime.rmi.rmiHost",
+ new StringConfigValue("transitclock.rmi.rmiHost",
null,
"For a client that needs to connect to an agency server. "
+ "When null system gets RMI host name from the WebAgencies "
@@ -58,7 +58,7 @@ public static int rmiPort() {
return rmiPort.getValue();
}
private static IntegerConfigValue rmiPort =
- new IntegerConfigValue("transitime.rmi.rmiPort",
+ new IntegerConfigValue("transitclock.rmi.rmiPort",
2099,
"Which port to use for RMI calls. Usually RMI uses port "
+ "1099 but using default of 2099 to not interfere with "
@@ -78,7 +78,7 @@ public static int secondaryRmiPort() {
return secondaryRmiPort.getValue();
}
private static IntegerConfigValue secondaryRmiPort =
- new IntegerConfigValue("transitime.rmi.secondaryRmiPort",
+ new IntegerConfigValue("transitclock.rmi.secondaryRmiPort",
2098,
"Which secondary port to use for RMI calls, for once "
+ "initial communication has been established. Usually "
diff --git a/transitime/src/main/java/org/transitime/configData/package-info.java b/transitclock/src/main/java/org/transitclock/configData/package-info.java
similarity index 96%
rename from transitime/src/main/java/org/transitime/configData/package-info.java
rename to transitclock/src/main/java/org/transitclock/configData/package-info.java
index d91a379d2..c8dd28640 100644
--- a/transitime/src/main/java/org/transitime/configData/package-info.java
+++ b/transitclock/src/main/java/org/transitclock/configData/package-info.java
@@ -26,4 +26,4 @@
* @author SkiBu Smith
*
*/
-package org.transitime.configData;
\ No newline at end of file
+package org.transitclock.configData;
\ No newline at end of file
diff --git a/transitime/src/main/java/org/transitime/core/ArrivalDepartureGenerator.java b/transitclock/src/main/java/org/transitclock/core/ArrivalDepartureGenerator.java
similarity index 97%
rename from transitime/src/main/java/org/transitime/core/ArrivalDepartureGenerator.java
rename to transitclock/src/main/java/org/transitclock/core/ArrivalDepartureGenerator.java
index e685a8319..65724a2f0 100644
--- a/transitime/src/main/java/org/transitime/core/ArrivalDepartureGenerator.java
+++ b/transitclock/src/main/java/org/transitclock/core/ArrivalDepartureGenerator.java
@@ -15,7 +15,7 @@
* along with Transitime.org . If not, see .
*/
-package org.transitime.core;
+package org.transitclock.core;
/**
* An interface used for generating arrival/departure times. An interface is
diff --git a/transitime/src/main/java/org/transitime/core/ArrivalDepartureGeneratorDefaultImpl.java b/transitclock/src/main/java/org/transitclock/core/ArrivalDepartureGeneratorDefaultImpl.java
old mode 100644
new mode 100755
similarity index 71%
rename from transitime/src/main/java/org/transitime/core/ArrivalDepartureGeneratorDefaultImpl.java
rename to transitclock/src/main/java/org/transitclock/core/ArrivalDepartureGeneratorDefaultImpl.java
index b0639ad48..94a03c3f7
--- a/transitime/src/main/java/org/transitime/core/ArrivalDepartureGeneratorDefaultImpl.java
+++ b/transitclock/src/main/java/org/transitclock/core/ArrivalDepartureGeneratorDefaultImpl.java
@@ -1,6 +1,6 @@
/*
* 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
@@ -14,28 +14,35 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.core;
+package org.transitclock.core;
+import java.time.LocalDate;
+import java.util.ArrayList;
import java.util.Date;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.transitime.applications.Core;
-import org.transitime.config.IntegerConfigValue;
-import org.transitime.configData.AgencyConfig;
-import org.transitime.configData.CoreConfig;
-import org.transitime.core.predAccuracy.PredictionAccuracyModule;
-import org.transitime.db.structs.Arrival;
-import org.transitime.db.structs.ArrivalDeparture;
-import org.transitime.db.structs.AvlReport;
-import org.transitime.db.structs.Block;
-import org.transitime.db.structs.Departure;
-import org.transitime.db.structs.Route;
-import org.transitime.db.structs.Stop;
-import org.transitime.db.structs.Trip;
-import org.transitime.db.structs.VehicleEvent;
-import org.transitime.logging.Markers;
-import org.transitime.utils.Time;
+import org.transitclock.applications.Core;
+import org.transitclock.config.BooleanConfigValue;
+import org.transitclock.config.IntegerConfigValue;
+import org.transitclock.config.LongConfigValue;
+import org.transitclock.configData.AgencyConfig;
+import org.transitclock.configData.CoreConfig;
+import org.transitclock.core.dataCache.DwellTimeModelCacheFactory;
+import org.transitclock.core.dataCache.HoldingTimeCache;
+import org.transitclock.core.dataCache.StopArrivalDepartureCacheFactory;
+import org.transitclock.core.dataCache.TripDataHistoryCacheFactory;
+import org.transitclock.core.dataCache.VehicleStateManager;
+import org.transitclock.core.dataCache.frequency.FrequencyBasedHistoricalAverageCache;
+import org.transitclock.core.dataCache.scheduled.ScheduleBasedHistoricalAverageCache;
+import org.transitclock.core.holdingmethod.HoldingTimeGeneratorFactory;
+
+import org.transitclock.core.predAccuracy.PredictionAccuracyModule;
+import org.transitclock.db.structs.*;
+import org.transitclock.ipc.data.IpcArrivalDeparture;
+import org.transitclock.logging.Markers;
+import org.transitclock.utils.Geo;
+import org.transitclock.utils.Time;
/**
* For determining Arrival/Departure times based on a new GPS report and
@@ -70,16 +77,18 @@
* time actually stopped at the stop into account. Instead, need to use travel
* speed and distance to determine from the last AVL report when arrived or
* departed a stop.
- *
+ *
* @author SkiBu Smith
- *
+ *
*/
-public class ArrivalDepartureGeneratorDefaultImpl
+public class ArrivalDepartureGeneratorDefaultImpl
implements ArrivalDepartureGenerator {
-
- private static final Logger logger =
+
+ private static final Logger logger =
LoggerFactory.getLogger(ArrivalDepartureGeneratorDefaultImpl.class);
+
+
/********************** Config Params **************************/
/**
@@ -92,9 +101,9 @@ public class ArrivalDepartureGeneratorDefaultImpl
private static int getMaxStopsWhenNoPreviousMatch() {
return maxStopsWhenNoPreviousMatch.getValue();
}
- private static IntegerConfigValue maxStopsWhenNoPreviousMatch =
- new IntegerConfigValue(
- "transitime.arrivalsDepartures.maxStopsWhenNoPreviousMatch",
+ private static IntegerConfigValue maxStopsWhenNoPreviousMatch =
+ new IntegerConfigValue(
+ "transitclock.arrivalsDepartures.maxStopsWhenNoPreviousMatch",
1,
"If vehicle just became predictable as indicated by no " +
"previous match then still want to determine " +
@@ -113,9 +122,9 @@ private static int getMaxStopsWhenNoPreviousMatch() {
private static int getMaxStopsBetweenMatches() {
return maxStopsBetweenMatches.getValue();
}
- private static IntegerConfigValue maxStopsBetweenMatches =
- new IntegerConfigValue(
- "transitime.arrivalsDepartures.maxStopsBetweenMatches",
+ private static IntegerConfigValue maxStopsBetweenMatches =
+ new IntegerConfigValue(
+ "transitclock.arrivalsDepartures.maxStopsBetweenMatches",
12,
"If between AVL reports the vehicle appears to traverse " +
"many stops then something is likely wrong with the " +
@@ -123,14 +132,55 @@ private static int getMaxStopsBetweenMatches() {
"arrivals/departures are created between AVL reports.");
private static IntegerConfigValue allowableDifferenceBetweenAvlTimeSecs =
- new IntegerConfigValue("transitime.arrivalsDepartures.allowableDifferenceBetweenAvlTimeSecs",
- // Default is to only log problem if arrival time is more
+ new IntegerConfigValue("transitclock.arrivalsDepartures.allowableDifferenceBetweenAvlTimeSecs",
+ // Default is to only log problem if arrival time is more
// than a day off
- 1 * Time.SEC_PER_DAY,
+ 1 * Time.SEC_PER_DAY,
"If the time of a determine arrival/departure is really "
+ "different from the AVL time then something must be "
+ "wrong and the situation will be logged.");
-
+
+ /**
+ * Specify minimum allowable time in msec when calculating dwell time for departures.
+ * @return
+ */
+ private static long getMinAllowableDwellTime() {
+ return minAllowableDwellTime.getValue();
+ }
+ private static LongConfigValue minAllowableDwellTime =
+ new LongConfigValue(
+ "transitclock.arrivalsDepartures.minAllowableDwellTimeMsec",
+ 1l * Time.MS_PER_SEC,
+ "Specify minimum allowable time in msec when calculating dwell time for departures.");
+
+ /**
+ * Specifying the Max allowable time when calculating dwell time for departures.
+ * @return
+ */
+ private static long getMaxAllowableDwellTime() {
+ return maxAllowableDwellTime.getValue();
+ }
+ private static LongConfigValue maxAllowableDwellTime =
+ new LongConfigValue(
+ "transitclock.arrivalsDepartures.maxAllowableDwellTimeMsec",
+ 60l * Time.MS_PER_MIN,
+ "Specify maximum allowable time in msec when calculating dwell time for departures.");
+
+ /**
+ * Specifying the Max allowable time when calculating dwell time for departures.
+ * @return
+ */
+ private static long getDefaultArrivalDepartureBufferTime() {
+ return defaultArrivalDepartureBufferTime.getValue();
+ }
+ private static LongConfigValue defaultArrivalDepartureBufferTime =
+ new LongConfigValue(
+ "transitclock.arrivalsDepartures.defaultArrivalDepartureBufferTime",
+ 1l,
+ "Used when correcting situation where arrival time is after departure time. Default " +
+ "time to use when specifying amount of time between arrival and departure.");
+
+
/********************** Member Functions **************************/
/**
@@ -143,7 +193,7 @@ private static int getMaxStopsBetweenMatches() {
* such as the heading is not accurate causing the vehicle to match to the
* wrong direction, or if there is a software problem that causes an
* improper match.
- *
+ *
* @param oldMatch
* For determining how many stops traversed. Can be null
* @param newMatch
@@ -158,29 +208,29 @@ private boolean tooManyStopsTraversed(SpatialMatch oldMatch,
// If there is no old match then we are fine
if (oldMatch == null)
return false;
-
+
// If there is no old AVL report then we are fine
if (previousAvlReport == null)
return false;
-
+
// Determine how much time elapsed
- long avlTimeDeltaMsec =
+ long avlTimeDeltaMsec =
avlReport.getTime() - previousAvlReport.getTime();
-
+
// Determine number of stops traversed
Indices indices = oldMatch.getIndices();
Indices newMatchIndices = newMatch.getIndices();
int stopsTraversedCnt=0;
- while (!indices.pastEndOfBlock(avlReport.getTime())
+ while (!indices.pastEndOfBlock(avlReport.getTime())
&& indices.isEarlierStopPathThan(newMatchIndices)) {
indices.incrementStopPath(avlReport.getTime());
++stopsTraversedCnt;
}
-
- // If traversing more than a stop every 15 seconds then there must be
+
+ // If traversing more than a stop every 15 seconds then there must be
// a problem. Also use a minimum of 4 stops to make sure that don't get
// problems due to problematic GPS data causing issues
- if (stopsTraversedCnt >= 4 &&
+ if (stopsTraversedCnt >= 4 &&
stopsTraversedCnt > avlTimeDeltaMsec/15*Time.MS_PER_SEC) {
logger.error("vehicleId={} traversed {} stops in {} seconds " +
"which seems like too many stops for that amount of time. " +
@@ -193,11 +243,11 @@ private boolean tooManyStopsTraversed(SpatialMatch oldMatch,
} else
return false;
}
-
+
/**
* Determines if need to determine arrival/departure times due to vehicle
* having traversed a stop.
- *
+ *
* @param oldMatch
* The old match for the vehicle. Should be null if not previous
* match
@@ -205,11 +255,11 @@ private boolean tooManyStopsTraversed(SpatialMatch oldMatch,
* The new match for the vehicle.
* @return
*/
- private boolean shouldProcessArrivedOrDepartedStops(SpatialMatch oldMatch,
+ private boolean shouldProcessArrivedOrDepartedStops(SpatialMatch oldMatch,
SpatialMatch newMatch) {
- // If there is no old match at all then we likely finally got a
- // AVL report after vehicle had left terminal. Still want to
- // determine arrival/departure times for the first stops of the
+ // If there is no old match at all then we likely finally got a
+ // AVL report after vehicle had left terminal. Still want to
+ // determine arrival/departure times for the first stops of the
// block assignment. And this makes sure don't get a NPE in the
// next statements.
if (oldMatch == null)
@@ -219,29 +269,29 @@ private boolean shouldProcessArrivedOrDepartedStops(SpatialMatch oldMatch,
// matching to a very different part of the assignment. Since don't
// truly know what is going on it is best to not generate
// arrivals/departures for between the matches.
- int stopsTraversed =
+ int stopsTraversed =
SpatialMatch.numberStopsBetweenMatches(oldMatch, newMatch);
if (stopsTraversed > getMaxStopsBetweenMatches()) {
logger.error("Attempting to traverse {} stops between oldMatch " +
"and newMatch, which is more thanThere are more than " +
"MAX_STOPS_BETWEEN_MATCHES={}. Therefore not generating " +
"arrival/departure times. oldMatch={} newMatch={}",
- stopsTraversed, getMaxStopsBetweenMatches(),
+ stopsTraversed, getMaxStopsBetweenMatches(),
oldMatch, newMatch);
return false;
}
-
+
// Determine if should generate arrivals/departures
VehicleAtStopInfo oldStopInfo = oldMatch.getAtStop();
- VehicleAtStopInfo newStopInfo = newMatch.getAtStop();
+ VehicleAtStopInfo newStopInfo = newMatch.getAtStop();
if (oldStopInfo != null && newStopInfo != null) {
// Vehicle at stop for both old and new. Determine if they
// are different stops. If different then return true.
return oldStopInfo.getTripIndex() != newStopInfo.getTripIndex() ||
oldStopInfo.getStopPathIndex() != newStopInfo.getStopPathIndex();
} else if (oldStopInfo != null || newStopInfo != null) {
- // Just one (but not both) of the vehicle stop infos is null which
- // means they are different. Therefore must have arrived or departed
+ // Just one (but not both) of the vehicle stop infos is null which
+ // means they are different. Therefore must have arrived or departed
// stops.
return true;
} else {
@@ -251,34 +301,74 @@ stopsTraversed, getMaxStopsBetweenMatches(),
oldMatch.getStopPathIndex() != newMatch.getStopPathIndex();
}
}
-
+
/**
* Writes out departure time to database
- *
+ *
* @param vehicleState
* @param departureTime
* @param block
* @param tripIndex
* @param stopPathIndex
*/
- protected Departure createDepartureTime(VehicleState vehicleState,
- long departureTime, Block block, int tripIndex, int stopPathIndex) {
- // Store the departure in the database via the db logger
- Departure departure = new Departure(vehicleState.getVehicleId(),
- new Date(departureTime),
- vehicleState.getAvlReport().getDate(),
+ protected Departure createDepartureTime(VehicleState vehicleState, long departureTime, Block block,
+ int tripIndex, int stopPathIndex, Long dwellTime) {
+
+ Date freqStartDate=null;
+ if(vehicleState.getTripStartTime(vehicleState.getTripCounter())!=null)
+ {
+ freqStartDate = new Date(vehicleState.getTripStartTime(vehicleState.getTripCounter()));
+ }
+
+ String stopPathId = block.getStopPath(tripIndex, stopPathIndex).getId();
+ Date avlTime=vehicleState.getAvlReport().getDate();
+ Date time = new Date(departureTime);
+
+ Departure departure = new Departure(vehicleState.getVehicleId(),
+ time,
+ avlTime,
block,
tripIndex,
- stopPathIndex);
+ stopPathIndex,
+ freqStartDate,
+ dwellTime,
+ stopPathId);
+
+ departure = StopArrivalDepartureCacheFactory.getInstance().verifyDeparture(departure);
+ updateCache(vehicleState, departure);
+
logger.debug("Creating departure: {}", departure);
return departure;
}
+ /**
+ * Attempts to synchronize arrival and departure avl time.
+ * It seems like other parts of the code compare these values directly to the avl values therefore
+ * modifying here may create matching issues with caches and such.
+ * Avoid using for now.
+ *
+ * @param lastArrivalTime
+ * @param currentDepartureTime
+ * @return
+ */
+ private Date getDepartureAvlTime(Date lastArrivalTime, Date currentDepartureTime){
+ if(currentDepartureTime == null)
+ return lastArrivalTime;
+
+ if(lastArrivalTime != currentDepartureTime && lastArrivalTime != null){
+ if(Time.getTimeDifference(currentDepartureTime, lastArrivalTime) < 30 * Time.MS_PER_MIN){
+ return lastArrivalTime;
+ }
+ }
+
+ return currentDepartureTime;
+ }
+
/**
* Writes out arrival time to database. Also keeps track of the latest
* arrival time in VehicleState so that can make sure that subsequent
* departures are after the last arrival time.
- *
+ *
* @param vehicleState
* @param arrivalTime
* @param block
@@ -288,66 +378,187 @@ protected Departure createDepartureTime(VehicleState vehicleState,
protected Arrival createArrivalTime(VehicleState vehicleState,
long arrivalTime, Block block, int tripIndex, int stopPathIndex) {
// Store the arrival in the database via the db logger
- Arrival arrival = new Arrival(vehicleState.getVehicleId(),
+
+ Date freqStartDate=null;
+ if(vehicleState.getTripStartTime(vehicleState.getTripCounter())!=null)
+ {
+ freqStartDate = new Date(vehicleState.getTripStartTime(vehicleState.getTripCounter()));
+ }
+
+ String stopPathId = block.getStopPath(tripIndex, stopPathIndex).getId();
+
+ Arrival arrival = new Arrival(vehicleState.getVehicleId(),
new Date(arrivalTime),
vehicleState.getAvlReport().getDate(),
block,
tripIndex,
- stopPathIndex);
+ stopPathIndex,
+ freqStartDate,
+ stopPathId);
+
+ arrival = StopArrivalDepartureCacheFactory.getInstance().verifyArrival(arrival);
+ updateCache(vehicleState, arrival);
logger.debug("Creating arrival: {}", arrival);
-
+
// Remember this arrival time so that can make sure that subsequent
// departures are for after the arrival time.
- if (arrivalTime > vehicleState.getLastArrivalTime())
+ if (arrival.getTime() > vehicleState.getLastArrivalTime())
vehicleState.setLastArrivalTime(arrivalTime);
-
return arrival;
}
+ private void updateCache(VehicleState vehicleState, ArrivalDeparture arrivalDeparture)
+ {
+ if(TripDataHistoryCacheFactory.getInstance()!=null)
+ TripDataHistoryCacheFactory.getInstance().putArrivalDeparture(arrivalDeparture);
+
+ if(StopArrivalDepartureCacheFactory.getInstance()!=null)
+ {
+ StopArrivalDepartureCacheFactory.getInstance().putArrivalDeparture(arrivalDeparture);
+ }
+
+ if(DwellTimeModelCacheFactory.getInstance()!=null)
+ {
+ DwellTimeModelCacheFactory.getInstance().addSample(arrivalDeparture);
+ }
+
+ if(ScheduleBasedHistoricalAverageCache.getInstance()!=null)
+ {
+ try {
+ ScheduleBasedHistoricalAverageCache.getInstance().putArrivalDeparture(arrivalDeparture);
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ if(FrequencyBasedHistoricalAverageCache.getInstance()!=null)
+ try {
+ FrequencyBasedHistoricalAverageCache.getInstance().putArrivalDeparture(arrivalDeparture);
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ if(HoldingTimeGeneratorFactory.getInstance()!=null)
+ {
+ HoldingTime holdingTime;
+ try {
+ holdingTime = HoldingTimeGeneratorFactory.getInstance().generateHoldingTime(vehicleState, new IpcArrivalDeparture(arrivalDeparture));
+ if(holdingTime!=null)
+ {
+ HoldingTimeCache.getInstance().putHoldingTime(holdingTime);
+ vehicleState.setHoldingTime(holdingTime);
+
+ }
+ ArrayList N_List=new ArrayList();
+
+ HoldingTimeGeneratorFactory.getInstance().handleDeparture(vehicleState, arrivalDeparture);
+
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ }
+ /*
+ if(HoldingTimeGeneratorDefaultImpl.getOrderedListOfVehicles("66")!=null)
+ logger.info("ORDER:"+HoldingTimeGeneratorDefaultImpl.getOrderedListOfVehicles("66").toString());
+ */
+ /*
+ if(HoldingTimeGeneratorFactory.getInstance()!=null)
+ {
+ HoldingTimeCacheKey key=new HoldingTimeCacheKey(arrivalDeparture.getStopId(), arrivalDeparture.getVehicleId(), arrivalDeparture.getTripId());
+ if(arrivalDeparture.getVehicleId().equals("966"))
+ {
+ System.out.println("hello");
+ }
+
+
+
+ if(HoldingTimeCache.getInstance().getHoldingTime(key)!=null)
+ {
+ long sinceHoldingTimeGenerated=Math.abs(HoldingTimeCache.getInstance().getHoldingTime(key).getCreationTime().getTime()-arrivalDeparture.getAvlTime().getTime());
+
+ if((HoldingTimeCache.getInstance().getHoldingTime(key).isArrivalPredictionUsed()==false&&sinceHoldingTimeGenerated>1400000)||HoldingTimeCache.getInstance().getHoldingTime(key).isArrivalPredictionUsed()==true)
+ {
+ HoldingTime holdingTime = HoldingTimeGeneratorFactory.getInstance().generateHoldingTime(vehicleState, arrivalDeparture);
+ if(holdingTime!=null)
+ {
+ HoldingTimeCache.getInstance().putHoldingTime(holdingTime);
+ vehicleState.setHoldingTime(holdingTime);
+ }
+ }else
+ {
+ logger.debug("Don't generate holding time.");
+ }
+ }else
+ {
+ HoldingTime holdingTime = HoldingTimeGeneratorFactory.getInstance().generateHoldingTime(vehicleState, arrivalDeparture);
+ if(holdingTime!=null)
+ {
+ HoldingTimeCache.getInstance().putHoldingTime(holdingTime);
+ vehicleState.setHoldingTime(holdingTime);
+ }
+ }
+ HoldingTimeGeneratorFactory.getInstance().handleDeparture(vehicleState, arrivalDeparture);
+ }
+ */
+ }
/**
* For making sure that the arrival/departure time is reasonably close to
* the AVL time. Otherwise this indicates there was a problem determining
* the arrival/departure time.
- *
+ *
+ * @param avlTime
* @param time
- * @param avlReport
* @return true if arrival/departure time within 30 minutes of the AVL
* report time.
*/
- private boolean timeReasonable(ArrivalDeparture arrivalDeparture) {
- long delta = Math.abs(arrivalDeparture.getAvlTime().getTime()
- - arrivalDeparture.getDate().getTime());
- if (delta < allowableDifferenceBetweenAvlTimeSecs.getValue() * Time.MS_PER_SEC)
- return true;
- else {
- logger.error(Markers.email(),
- "For {} arrival or departure time of {} is more than "
- + "{} secs away from the AVL time of {}. Therefore not "
- + "storing this time. {}",
- AgencyConfig.getAgencyId(), arrivalDeparture.getDate(),
- allowableDifferenceBetweenAvlTimeSecs.getValue(),
- arrivalDeparture.getAvlTime(), arrivalDeparture);
- return false;
+ private boolean timeReasonable(Date avlTime, Date time){
+ if(avlTime != null && time != null) {
+ long delta = Math.abs(avlTime.getTime() - time.getTime());
+ if (delta < allowableDifferenceBetweenAvlTimeSecs.getValue() * Time.MS_PER_SEC)
+ return true;
}
+ return false;
}
+
/**
* Stores the specified ArrivalDeparture object into the db
* and log to the ArrivalsDeparatures log file that the
- * object was created.
+ * object was created.
*
- * Also generates corresponding prediction accuracy information
+ * Also generates corresponding prediction accuracy information
* if a corresponding prediction was found in memory.
- *
+ *
* @param arrivalDeparture
*/
protected void storeInDbAndLog(ArrivalDeparture arrivalDeparture) {
- // If arrival/departure time too far from the AVL time then something
- // must be wrong. For this situation don't store the arrival/departure
+
+ if (arrivalDeparture == null)
+ return;
+
+ // If arrival/departure time too far from the AVL time then something
+ // must be wrong. For this situation don't store the arrival/departure
// into db.
- if (!timeReasonable(arrivalDeparture))
+ Date avlTime = arrivalDeparture.getAvlTime();
+ Date time = arrivalDeparture.getDate();
+ if(!timeReasonable(avlTime, time)){
+ logger.error(Markers.email(),
+ "For {} arrival or departure time of {} is more than "
+ + "{} secs away from the AVL time of {}. Therefore not "
+ + "storing this time. {}",
+ AgencyConfig.getAgencyId(), time,
+ allowableDifferenceBetweenAvlTimeSecs.getValue(),
+ avlTime, arrivalDeparture.getBlockId());
return;
-
+ }
+
+
+
+
// Don't want to record arrival/departure time for last stop of a no
// schedule block/trip since the last stop is also the first stop of
// a non-schedule trip. We don't duplicate entries.
@@ -356,83 +567,90 @@ protected void storeInDbAndLog(ArrivalDeparture arrivalDeparture) {
arrivalDeparture.getBlock().getTrip(
arrivalDeparture.getTripIndex());
// If last stop in trip then don't do anything here
- if (arrivalDeparture.getStopPathIndex() ==
+ if (arrivalDeparture.getStopPathIndex() ==
trip.getNumberStopPaths() - 1)
return;
}
-
+
// Queue to store object into db
Core.getInstance().getDbLogger().add(arrivalDeparture);
-
+
// Log creation of ArrivalDeparture in ArrivalsDepartures.log file
arrivalDeparture.logCreation();
-
+
+ /* add event to vehicle state. Will increment tripCounter if the last arrival in a trip */
+ VehicleState vehicleState = VehicleStateManager.getInstance().getVehicleState(arrivalDeparture.getVehicleId());
+
+ vehicleState.incrementTripCounter(arrivalDeparture);
+
// Generate prediction accuracy info as appropriate
PredictionAccuracyModule.handleArrivalDeparture(arrivalDeparture);
+
+
}
-
+
/**
* If vehicle departs terminal too early or too late then log an event
* so that the problem is made more obvious.
- *
+ *
* @param vehicleState
- * @param arrivalDeparture
+ * @param departure
*/
- private void logEventIfVehicleDepartedEarlyOrLate(VehicleState vehicleState,
+ private void logEventIfVehicleDepartedEarlyOrLate(VehicleState vehicleState,
Departure departure) {
// If departure not for terminal then can ignore
- if (departure.getStopPathIndex() != 0)
+ if (departure.getStopPathIndex() != 0)
return;
-
+
// Determine schedule adherence. If no schedule adherence info available
// then can ignore.
TemporalDifference schAdh = departure.getScheduleAdherence();
if (schAdh == null)
return;
-
+
// If vehicle left too early then record an event
if (schAdh.isEarlierThan(CoreConfig.getAllowableEarlyDepartureTimeForLoggingEvent())) {
// Create description for VehicleEvent
Stop stop = Core.getInstance().getDbConfig().getStop(departure.getStopId());
Route route = Core.getInstance().getDbConfig().getRouteById(departure.getRouteId());
- String description = "Vehicle " + departure.getVehicleId()
+ String description = "Vehicle " + departure.getVehicleId()
+ " left stop " + departure.getStopId()
- + " \"" + stop.getName() + "\" for route \"" + route.getName()
- + "\" " + schAdh.toString() + ". Scheduled departure time was "
+ + " \"" + stop.getName() + "\" for route \"" + route.getName()
+ + "\" " + schAdh.toString() + ". Scheduled departure time was "
+ Time.timeStr(departure.getScheduledTime());
// Create, store in db, and log the VehicleEvent
VehicleEvent.create(vehicleState.getAvlReport(), vehicleState.getMatch(),
- VehicleEvent.LEFT_TERMINAL_EARLY,
- description,
+ VehicleEvent.LEFT_TERMINAL_EARLY,
+ description,
true, // predictable
- false, // becameUnpredictable
+ false, // becameUnpredictable
null); // supervisor
- }
-
+ }
+
// If vehicle left too late then record an event
if (schAdh.isLaterThan(CoreConfig.getAllowableLateDepartureTimeForLoggingEvent())) {
// Create description for VehicleEvent
Stop stop = Core.getInstance().getDbConfig().getStop(departure.getStopId());
Route route = Core.getInstance().getDbConfig().getRouteById(departure.getRouteId());
- String description = "Vehicle " + departure.getVehicleId()
+ String description = "Vehicle " + departure.getVehicleId()
+ " left stop " + departure.getStopId()
- + " \"" + stop.getName() + "\" for route \"" + route.getName()
- + "\" " + schAdh.toString() + ". Scheduled departure time was "
+ + " \"" + stop.getName() + "\" for route \"" + route.getName()
+ + "\" " + schAdh.toString() + ". Scheduled departure time was "
+ Time.timeStr(departure.getScheduledTime());
-
+
// Create, store in db, and log the VehicleEvent
VehicleEvent.create(vehicleState.getAvlReport(), vehicleState.getMatch(),
- VehicleEvent.LEFT_TERMINAL_LATE,
- description,
+ VehicleEvent.LEFT_TERMINAL_LATE,
+ description,
true, // predictable
- false, // becameUnpredictable
- null); // supervisor
+ false, // becameUnpredictable
+ null); // supervisor
}
}
-
+
/**
- * For when there is a new match but not an old match. This means that
+ * For when there is a new match but not an old match. This means that
* cannot interpolate the arrival/departure times. Instead need to
* look backwards and use travel and stop times to determine the
* arrival/departure times.
@@ -442,15 +660,15 @@ private void logEventIfVehicleDepartedEarlyOrLate(VehicleState vehicleState,
* vehicle might have actually started service mid-block, meaning that
* it didn't traverse the earlier stops and so shouldn't fake
* arrival/departure times for the earlier stops since there is a
- * good chance they never happened.
- *
+ * good chance they never happened.
+ *
* @param vehicleState
* @return List of ArrivalDepartures created
*/
private void estimateArrivalsDeparturesWithoutPreviousMatch(
VehicleState vehicleState) {
- // If vehicle got assigned to the same block as before then
- // there is likely a problem. In this case don't want to
+ // If vehicle got assigned to the same block as before then
+ // there is likely a problem. In this case don't want to
// estimate arrivals/departures because that would likely
// create duplicates.
if (vehicleState.vehicleNewlyAssignedToSameBlock()) {
@@ -461,16 +679,16 @@ private void estimateArrivalsDeparturesWithoutPreviousMatch(
"vehicle already had arrivals/departures for the stops. " +
"Therefore not estimating arrivals/departures for the " +
"early stops.",
- vehicleState.getVehicleId(),
+ vehicleState.getVehicleId(),
vehicleState.getBlock().getId());
return;
}
-
+
// Couple of convenience variables
SpatialMatch newMatch = vehicleState.getMatch();
String vehicleId = vehicleState.getVehicleId();
-
- if (newMatch.getTripIndex() == 0 &&
+
+ if (newMatch.getTripIndex() == 0 &&
newMatch.getStopPathIndex() > 0 &&
newMatch.getStopPathIndex() < getMaxStopsWhenNoPreviousMatch()) {
// Couple more convenience variables
@@ -478,27 +696,32 @@ private void estimateArrivalsDeparturesWithoutPreviousMatch(
Block block = newMatch.getBlock();
final int tripIndex = 0;
int stopPathIndex = 0;
-
+
// Determine departure time for first stop of trip
SpatialMatch beginningOfTrip = new SpatialMatch(0, block,
tripIndex, 0, 0, 0.0, 0.0);
long travelTimeFromFirstStopToMatch = TravelTimes.getInstance()
.expectedTravelTimeBetweenMatches(vehicleId, avlReportTime,
beginningOfTrip, newMatch);
- long departureTime =
+
+ // TODO - dwell time for first stop?
+ //Integer firstStopDwellTime = block.getPathStopTime(tripIndex, stopPathIndex);
+ Long firstStopDwellTime = recalculateDwellTimeUsingThresholds(null);
+
+ long departureTime =
avlReportTime.getTime() - travelTimeFromFirstStopToMatch;
-
- // Create departure time for first stop of trip if it has left that
+
+ // Create departure time for first stop of trip if it has left that
// stop
if (!newMatch.isAtStop(tripIndex, stopPathIndex)) {
storeInDbAndLog(createDepartureTime(vehicleState, departureTime,
- block, tripIndex, stopPathIndex));
+ block, tripIndex, stopPathIndex, firstStopDwellTime));
}
-
- // Go through remaining intermediate stops to determine
+
+ // Go through remaining intermediate stops to determine
// arrival/departure times
- for (stopPathIndex = 1;
- stopPathIndex < newMatch.getStopPathIndex();
+ for (stopPathIndex = 1;
+ stopPathIndex < newMatch.getStopPathIndex();
++stopPathIndex) {
// Create the arrival
long arrivalTime = departureTime
@@ -506,15 +729,18 @@ private void estimateArrivalsDeparturesWithoutPreviousMatch(
storeInDbAndLog(createArrivalTime(vehicleState, arrivalTime, block,
tripIndex, stopPathIndex));
+
+
// If the vehicle has left this stop then create the departure
if (!newMatch.isAtStop(tripIndex, stopPathIndex)) {
int stopTime = block.getPathStopTime(tripIndex, stopPathIndex);
departureTime = arrivalTime + stopTime;
+ Long dwellTime = recalculateDwellTimeUsingThresholds(departureTime - arrivalTime);
storeInDbAndLog(createDepartureTime(vehicleState, departureTime,
- block, tripIndex, stopPathIndex));
+ block, tripIndex, stopPathIndex, dwellTime));
}
}
-
+
// Need to add final arrival time if newMatch is at the
// stop for the match
if (newMatch.isAtStop(tripIndex, newMatch.getStopPathIndex())) {
@@ -525,39 +751,42 @@ private void estimateArrivalsDeparturesWithoutPreviousMatch(
} else {
logger.debug("For vehicleId={} no old match but the new " +
"match is too far along so not determining " +
- "arrival/departure times without previous match.",
+ "arrival/departure times without previous match.",
vehicleId);
}
}
-
+
/**
* Makes sure that the departure time is after the arrival time. Also
* handles the situation where couldn't store the previous arrival time
* because wasn't certain about it because it was determined to be after the
* associated AVL report.
- *
+ *
* @param departureTime
* @param departureTimeBasedOnNewMatch
* @param vehicleState
* @return
*/
- private long adjustDepartureSoAfterArrival(long departureTime,
- long departureTimeBasedOnNewMatch, VehicleState vehicleState) {
+ private Departure createDeparturePostArrival(VehicleState vehicleState, long departureTime, Block block,
+ int tripIndex, int stopPathIndex, long departureTimeBasedOnNewMatch) {
String vehicleId = vehicleState.getVehicleId();
AvlReport avlReport = vehicleState.getAvlReport();
- AvlReport previousAvlReport =
+ AvlReport previousAvlReport =
vehicleState.getPreviousAvlReportFromSuccessfulMatch();
+ Arrival arrivalToStoreInDb = vehicleState.getArrivalToStoreToDb();
+ long arrivalTime;
+
// Make sure departure time is after the previous arrival time since
// don't want arrival/departure times to ever go backwards. That of
// course looks really bad.
- Arrival arrivalToStoreInDb = vehicleState.getArrivalToStoreToDb();
if (arrivalToStoreInDb != null) {
+ arrivalTime = arrivalToStoreInDb.getTime();
// If the arrival time is a problem then adjust both the arrival
// time and the departure time so that they are as accurate as
// possible and that the arrival time comes before the departure
// time.
- if (arrivalToStoreInDb.getTime() >= departureTime) {
+ if (arrivalTime >= departureTime) {
long originalTimeBetweenOldAvlAndArrival = arrivalToStoreInDb
.getTime() - previousAvlReport.getTime();
// Note: don't want to subtract out departure time because
@@ -565,23 +794,23 @@ private long adjustDepartureSoAfterArrival(long departureTime,
// determine expected time for departure using the old
// AVL report for the arrival and the new AVL report for the
// the departure need to use departureTimeBasedOnNewMatch.
- long originalTimeBetweenDepartureAndAvl =
+ long originalTimeBetweenDepartureAndAvl =
avlReport.getTime() - departureTimeBasedOnNewMatch;
- long timeBetweenAvlReports =
+ long timeBetweenAvlReports =
avlReport.getTime() - previousAvlReport.getTime();
double ratio = (double) timeBetweenAvlReports /
- (originalTimeBetweenOldAvlAndArrival +
+ (originalTimeBetweenOldAvlAndArrival +
originalTimeBetweenDepartureAndAvl);
long newArrivalTime = previousAvlReport.getTime()
+ Math.round(ratio
* originalTimeBetweenOldAvlAndArrival);
- long newDepartureTime = newArrivalTime + 1;
-
+ long newDepartureTime = newArrivalTime + getDefaultArrivalDepartureBufferTime();
+
if (logger.isDebugEnabled()) {
logger.debug("vehicleId={} determined departure time was "
+ "{} which is less than or equal to the previous "
+ "arrival time of {}. Therefore the arrival time "
- + "adjusted to {} and departure adjusted to {}.",
+ + "adjusted to {} and departure adjusted to {}.",
vehicleId,
Time.dateTimeStrMsec(departureTime),
Time.dateTimeStrMsec(arrivalToStoreInDb.getTime()),
@@ -589,10 +818,17 @@ private long adjustDepartureSoAfterArrival(long departureTime,
Time.dateTimeStrMsec(newDepartureTime));
}
departureTime = newDepartureTime;
+ arrivalTime = newArrivalTime;
+ if (arrivalTime < vehicleState.getLastDepartureTime()) {
+ logger.error("vehicle={} generated illegal arrival time less than next departure {}"
+ + " but not greater than previous departure {}", vehicleId, Time.dateTimeStrMsec(departureTime),
+ Time.dateTimeStrMsec(vehicleState.getLastDepartureTime()));
+ // TODO: attempt to untangle out-of-order ADs
+ }
arrivalToStoreInDb = arrivalToStoreInDb
- .withUpdatedTime(new Date(newArrivalTime));
+ .withUpdatedTime(new Date(arrivalTime));
}
-
+
// Now that have the corrected arrival time store it in db
// and reset vehicleState to indicate that have dealt with it.
storeInDbAndLog(arrivalToStoreInDb);
@@ -604,21 +840,21 @@ private long adjustDepartureSoAfterArrival(long departureTime,
// arrival time no matter how it was created. This could happen
// if travel times indicate that the vehicle departed a long time
// ago.
- long lastArrivalTime = vehicleState.getLastArrivalTime();
- if (departureTime <= lastArrivalTime) {
+ arrivalTime = vehicleState.getLastArrivalTime();
+ if (departureTime <= arrivalTime) {
if (logger.isDebugEnabled()) {
logger.debug("vehicleId={} the determined departure was " +
"{} which is before the previous arrival time {}. " +
"Therefore adjusting the departure time to {}",
- vehicleId,
+ vehicleId,
Time.dateTimeStrMsec(departureTime),
- Time.dateTimeStrMsec(lastArrivalTime),
- Time.dateTimeStrMsec(lastArrivalTime+1));
+ Time.dateTimeStrMsec(arrivalTime),
+ Time.dateTimeStrMsec(arrivalTime+1));
}
- departureTime = lastArrivalTime + 1;
+ departureTime = arrivalTime + 1;
}
}
-
+
// If adjusting the departure time makes it after the AVL report
// then we have a problem. Can't do anything about it so just log
// the problem.
@@ -629,20 +865,25 @@ private long adjustDepartureSoAfterArrival(long departureTime,
"time of {} which is after the AVL time of {}. This " +
"is a problem because the match won't be between the " +
"departure and arrival time even though it should be.",
- vehicleId,
+ vehicleId,
Time.dateTimeStrMsec(departureTime),
Time.dateTimeStrMsec(avlReport.getTime()));
}
}
-
- // Return the possibly adjusted departure time
- return departureTime;
+
+ Long dwellTime = recalculateDwellTimeUsingThresholds(departureTime - arrivalTime);
+ Departure verifiedDeparture = createDepartureTime(vehicleState, departureTime, block, tripIndex, stopPathIndex, dwellTime);
+ // remember this departure time to ensure subsequent arrivals are in order
+ if (verifiedDeparture.getTime() > vehicleState.getLastDepartureTime()) {
+ vehicleState.setLastDepartureTime(verifiedDeparture.getTime());
+ }
+ return verifiedDeparture;
}
-
+
/**
* Handles the case where the old match indicates that vehicle has
* departed a stop. Determines the appropriate departure time.
- *
+ *
* @param vehicleState
* For obtaining match and AVL info
* @return The time to be used as the beginTime for determining
@@ -650,30 +891,30 @@ private long adjustDepartureSoAfterArrival(long departureTime,
* the new AVL report if vehicle is not at a stop. If it is at a
* stop then it will be the expected departure time at that stop.
*/
- private long handleVehicleDepartingStop(VehicleState vehicleState) {
+ private long handleVehicleDepartingStop(VehicleState vehicleState) {
String vehicleId = vehicleState.getVehicleId();
-
+
// If vehicle wasn't departing a stop then simply return the
// previous AVL time as the beginTime.
SpatialMatch oldMatch = vehicleState.getPreviousMatch();
VehicleAtStopInfo oldVehicleAtStopInfo = oldMatch.getAtStop();
- AvlReport previousAvlReport =
+ AvlReport previousAvlReport =
vehicleState.getPreviousAvlReportFromSuccessfulMatch();
if (oldVehicleAtStopInfo == null)
return previousAvlReport.getTime();
-
+
// Vehicle departed previous stop...
logger.debug("vehicleId={} was at stop {} previous AVL report " +
- "and departed so determining departure time",
+ "and departed so determining departure time",
vehicleId, oldVehicleAtStopInfo);
// Use match right at the departed stop. This way we are including the
// time it takes to get from the actual stop to the new match.
- SpatialMatch matchJustAfterStop =
+ SpatialMatch matchJustAfterStop =
oldMatch.getMatchAdjustedToBeginningOfPath();
-
+
// Determine departure info for the old stop by using the current
- // AVL report and subtracting the expected travel time to get from
+ // AVL report and subtracting the expected travel time to get from
// there to the new match.
SpatialMatch newMatch = vehicleState.getMatch();
int travelTimeToNewMatchMsec = TravelTimes.getInstance()
@@ -681,13 +922,13 @@ private long handleVehicleDepartingStop(VehicleState vehicleState) {
previousAvlReport.getDate(), matchJustAfterStop,
newMatch);
AvlReport avlReport = vehicleState.getAvlReport();
- long departureTimeBasedOnNewMatch =
+ long departureTimeBasedOnNewMatch =
avlReport.getTime() - travelTimeToNewMatchMsec;
-
- // Need to also look at departure time for the old stop by using the
- // previous AVL report and subtracting the expected travel time to get
- // from there. This will prevent us from using
- // departureTimeBasedOnNewMatch if that time is too early due to
+
+ // Need to also look at departure time for the old stop by using the
+ // previous AVL report and subtracting the expected travel time to get
+ // from there. This will prevent us from using
+ // departureTimeBasedOnNewMatch if that time is too early due to
// expected travel times being too long.
long departureTimeBasedOnOldMatch;
if (matchJustAfterStop.lessThanOrEqualTo(oldMatch)) {
@@ -702,7 +943,7 @@ private long handleVehicleDepartingStop(VehicleState vehicleState) {
} else {
// The oldMatch is before the stop so add the travel time from the
// oldMatch to the stop to the previous AVL report time.
- SpatialMatch matchJustBeforeStop =
+ SpatialMatch matchJustBeforeStop =
oldMatch.getMatchAdjustedToEndOfPath();
int travelTimeFromOldMatchToStopMsec = TravelTimes.getInstance()
.expectedTravelTimeBetweenMatches(vehicleId,
@@ -711,7 +952,7 @@ private long handleVehicleDepartingStop(VehicleState vehicleState) {
departureTimeBasedOnOldMatch = previousAvlReport.getTime()
+ travelTimeFromOldMatchToStopMsec;
}
-
+
// Determine actual departure time to use. If the old match departure
// time is greater than the new match time then we know that the
// vehicle was still at the stop at the old match departure time.
@@ -729,7 +970,7 @@ private long handleVehicleDepartingStop(VehicleState vehicleState) {
logger.debug("For vehicleId={} using departure time {} based "
+ "on old match because it is greater than the "
+ "earlier value " + "based on the new match of {}",
- vehicleId,
+ vehicleId,
Time.dateTimeStrMsec(departureTime),
Time.dateTimeStrMsec(departureTimeBasedOnNewMatch));
}
@@ -739,42 +980,42 @@ private long handleVehicleDepartingStop(VehicleState vehicleState) {
// This is important because the vehicle has gone beyond the stop and
// will be generating a match. Since the vehicle was determined to
// have left the stop the departure time must be before the AVL time.
- // This check makes sure that matches and arrivals/departures are in
+ // This check makes sure that matches and arrivals/departures are in
// the proper order for when determining historic travel times.
if (departureTime >= avlReport.getTime()) {
logger.debug("For vehicleId={} departure time determined to be " +
"{} but that is greater or equal to the AVL time " +
- "of {}. Therefore setting departure time to {}.",
+ "of {}. Therefore setting departure time to {}.",
vehicleId,
Time.dateTimeStrMsec(departureTime),
Time.dateTimeStrMsec(avlReport.getTime()),
Time.dateTimeStrMsec(avlReport.getTime() - 1));
departureTime = avlReport.getTime() - 1;
}
-
- // Make sure departure time is after arrival
- departureTime = adjustDepartureSoAfterArrival(departureTime,
- departureTimeBasedOnNewMatch, vehicleState);
- // Create and write out the departure time to db
- Departure departure = createDepartureTime(vehicleState,
+
+ // Make sure departure time is after arrival
+ Departure departure = createDeparturePostArrival(vehicleState,
departureTime, oldVehicleAtStopInfo.getBlock(),
oldVehicleAtStopInfo.getTripIndex(),
- oldVehicleAtStopInfo.getStopPathIndex());
+ oldVehicleAtStopInfo.getStopPathIndex(),
+ departureTimeBasedOnNewMatch);
+
+ // Create and write out the departure time to db
storeInDbAndLog(departure);
-
+
// Log event if vehicle left a terminal too early or too late
logEventIfVehicleDepartedEarlyOrLate(vehicleState, departure);
-
+
// The new beginTime to be used to determine arrival/departure
// times at intermediate stops
return departureTime;
}
-
+
/**
* Handles the case where the new match indicates that vehicle has
* arrived at a stop. Determines the appropriate arrival time.
- *
+ *
* @param vehicleState
* For obtaining match and AVL info
* @param beginTime
@@ -786,7 +1027,7 @@ private long handleVehicleDepartingStop(VehicleState vehicleState) {
* stop then it will be the expected arrival time at that stop.
*/
private long handleVehicleArrivingAtStop(VehicleState vehicleState,
- long beginTime) {
+ long beginTime) {
String vehicleId = vehicleState.getVehicleId();
// If vehicle hasn't arrived at a stop then simply return the
@@ -796,19 +1037,19 @@ private long handleVehicleArrivingAtStop(VehicleState vehicleState,
AvlReport avlReport = vehicleState.getAvlReport();
if (newVehicleAtStopInfo == null)
return avlReport.getTime();
-
+
// Vehicle has arrived at a stop...
logger.debug("vehicleId={} arrived at stop {} with new AVL " +
- "report so determining arrival time",
+ "report so determining arrival time",
vehicleId, newVehicleAtStopInfo);
// Use match right at the stop. This way we are including the
// time it takes to get from the new match to the actual
// stop and not just to some distance before the stop.
- SpatialMatch matchJustBeforeStop =
+ SpatialMatch matchJustBeforeStop =
newMatch.getMatchAdjustedToEndOfPath();
-
- // Determine arrival info for the new stop based on the
+
+ // Determine arrival info for the new stop based on the
// old AVL report. This will give us the proper time if
// the vehicle already arrived before the current AVL
// report
@@ -817,12 +1058,12 @@ private long handleVehicleArrivingAtStop(VehicleState vehicleState,
.expectedTravelTimeBetweenMatches(vehicleId,
avlReport.getDate(), oldMatch, matchJustBeforeStop);
// At first it appears that should use the time of the previous AVL
- // report plus the travel time. But since vehicle might have just
+ // report plus the travel time. But since vehicle might have just
// departed the previous stop should use that departure time instead.
// By using beginTime we are using the correct value.
- long arrivalTimeBasedOnOldMatch =
+ long arrivalTimeBasedOnOldMatch =
beginTime + travelTimeFromOldMatchMsec;
-
+
// Need to also look at arrival time based on the new match. This
// will prevent us from using arrivalTimeBasedOnOldMatch if that
// time is in the future due to the expected travel times incorrectly
@@ -835,55 +1076,55 @@ private long handleVehicleArrivingAtStop(VehicleState vehicleState,
int travelTimeFromNewMatchToStopMsec = TravelTimes.getInstance()
.expectedTravelTimeBetweenMatches(vehicleId,
avlReport.getDate(), newMatch, matchJustBeforeStop);
- arrivalTimeBasedOnNewMatch =
+ arrivalTimeBasedOnNewMatch =
avlReport.getTime() + travelTimeFromNewMatchToStopMsec;
} else {
// The new match is after the stop so subtract the travel time
// from the stop to the match from the AVL time to get the
// arrivalTimeBasedOnNewMatch.
- SpatialMatch matchJustAfterStop =
+ SpatialMatch matchJustAfterStop =
newMatch.getMatchAdjustedToBeginningOfPath();
int travelTimeFromStoptoNewMatchMsec = TravelTimes.getInstance()
.expectedTravelTimeBetweenMatches(vehicleId,
avlReport.getDate(), matchJustAfterStop, newMatch);
- arrivalTimeBasedOnNewMatch =
- avlReport.getTime() - travelTimeFromStoptoNewMatchMsec;
+ arrivalTimeBasedOnNewMatch =
+ avlReport.getTime() - travelTimeFromStoptoNewMatchMsec;
}
-
+
// Determine which arrival time to use. If the one based on the old
// match is greater than the one based on the new match it means that
// the vehicle traveled faster than expected. This is pretty common
// since the travel times can be based on the schedule, which is often
// not very accurate. For this case need to use the arrival time
// based on the new match since we know that the vehicle has arrived
- // at the stop by that time.
+ // at the stop by that time.
long arrivalTime = arrivalTimeBasedOnOldMatch;
if (arrivalTimeBasedOnNewMatch < arrivalTimeBasedOnOldMatch) {
// Use arrival time based on new match since we definitely know
// the vehicle has arrived at this time.
arrivalTime = arrivalTimeBasedOnNewMatch;
-
+
// Log what is going on
if (logger.isDebugEnabled()) {
logger.debug("For vehicleId={} using arrival time {} based " +
"on new match because it is less than the later value " +
- "based on the old match of {}",
- vehicleId,
+ "based on the old match of {}",
+ vehicleId,
Time.dateTimeStrMsec(arrivalTime),
Time.dateTimeStrMsec(arrivalTimeBasedOnOldMatch));
}
}
-
+
// Make sure the determined arrival time is greater than old AVL time.
- // This check makes sure that matches and arrivals/departures are in
+ // This check makes sure that matches and arrivals/departures are in
// the proper order for when determining historic travel times.
- AvlReport previousAvlReport =
+ AvlReport previousAvlReport =
vehicleState.getPreviousAvlReportFromSuccessfulMatch();
if (arrivalTime <= previousAvlReport.getTime()) {
logger.debug("For vehicleId={} arrival time determined to be " +
"{} but that is less than or equal to the previous AVL " +
- "time of {}. Therefore setting arrival time to {}.",
+ "time of {}. Therefore setting arrival time to {}.",
vehicleId,
Time.dateTimeStrMsec(arrivalTime),
Time.dateTimeStrMsec(previousAvlReport.getTime()),
@@ -891,22 +1132,33 @@ private long handleVehicleArrivingAtStop(VehicleState vehicleState,
arrivalTime = previousAvlReport.getTime() + 1;
}
+ if (arrivalTime < vehicleState.getLastDepartureTime()) {
+ // our time check above didn't catch an out-of-order arrivalTime
+ logger.debug("For vehicle={} arrival time determined to be " +
+ "{} but that is less than or equal to previous departure " +
+ "time of {}. Setting arrival time to {}.",
+ vehicleId,
+ Time.dateTimeStrMsec(arrivalTime),
+ Time.dateTimeStrMsec(vehicleState.getLastDepartureTime()));
+ arrivalTime = vehicleState.getLastArrivalTime() + 1;
+ }
+
// Create the arrival time
- Arrival arrival = createArrivalTime(vehicleState, arrivalTime,
- newVehicleAtStopInfo.getBlock(),
- newVehicleAtStopInfo.getTripIndex(),
+ Arrival arrival = createArrivalTime(vehicleState, arrivalTime,
+ newVehicleAtStopInfo.getBlock(),
+ newVehicleAtStopInfo.getTripIndex(),
newVehicleAtStopInfo.getStopPathIndex());
-
+
// If the arrival time is into the future then we don't want to store
// it right now because it might be so in the future that it could be
- // after the next AVL report, which of course hasn't happened yet.
+ // after the next AVL report, which of course hasn't happened yet.
// This would be a problem because then the store arrivals/departures
- // and matches could be out of sequence which would screw up all
+ // and matches could be out of sequence which would screw up all
// the systems that use that data. But if it is the last stop of the
// trip then should store it now because might not get another match
// for this trip and don't want to never store the arrival.
if (arrival.getTime() > avlReport.getTime()
- && newVehicleAtStopInfo.getStopPathIndex() !=
+ && newVehicleAtStopInfo.getStopPathIndex() !=
newMatch.getTrip().getNumberStopPaths() - 1) {
// Record the arrival to store into the db next time get a
// departure so that can make sure that arrival time is
@@ -917,18 +1169,18 @@ private long handleVehicleArrivingAtStop(VehicleState vehicleState,
vehicleState.setArrivalToStoreToDb(null);
storeInDbAndLog(arrival);
}
-
+
// The new endTime to be used to determine arrival/departure
// times at intermediate stops
return arrivalTime;
}
-
+
/**
* Determines number of travel times and wait times between oldMatch and
* newMatch that have zero travel or stop time, meaning that the
* arrival/departure times will need to be adjusted by 1msec to make sure
* that each time is unique for a vehicle.
- *
+ *
* @param oldMatch
* @param newMatch
* @return Number of zero travel or stop times
@@ -945,14 +1197,14 @@ private int numberOfZeroTravelOrStopTimes(SpatialMatch oldMatch,
++counter;
indices.incrementStopPath();
}
-
+
return counter;
}
-
+
/**
* Determine arrival/departure info for in the between stops between the
* previous and the current match.
- *
+ *
* @param vehicleState
* For obtaining match and AVL info
* @param beginTime
@@ -965,18 +1217,18 @@ private int numberOfZeroTravelOrStopTimes(SpatialMatch oldMatch,
private void handleIntermediateStops(VehicleState vehicleState,
long beginTime, long endTime) {
// Need to make sure that the arrival/departure times created for
- // intermediate stops do not have the same exact time as the
+ // intermediate stops do not have the same exact time as the
// departure of the previous stop or the arrival of the new
// stop. Otherwise this could happen if have travel and/or wait
// times of zero. The reason this is important is so that each
// arrival/departure for a vehicle have a different time and
- // ordered correctly time wise so that when one looks at the
+ // ordered correctly time wise so that when one looks at the
// arrival/departure times for a vehicle the order is correct.
// Otherwise the times being listed out of order could cause one
// to lose trust in the values.
++beginTime;
--endTime;
-
+
// Convenience variables
String vehicleId = vehicleState.getVehicleId();
SpatialMatch oldMatch = vehicleState.getPreviousMatch();
@@ -984,12 +1236,12 @@ private void handleIntermediateStops(VehicleState vehicleState,
Date previousAvlDate = vehicleState
.getPreviousAvlReportFromSuccessfulMatch().getDate();
Date avlDate = vehicleState.getAvlReport().getDate();
-
+
int numZeroTimes = numberOfZeroTravelOrStopTimes(oldMatch, newMatch);
-
- // Determine how fast vehicle was traveling compared to what is
- // expected. Then can use proportional travel and stop times to
- // determine the arrival and departure times. Note that this part of
+
+ // Determine how fast vehicle was traveling compared to what is
+ // expected. Then can use proportional travel and stop times to
+ // determine the arrival and departure times. Note that this part of
// the code doesn't need to be very efficient because usually will get
// frequent enough AVL reports such that there will be at most only
// a single stop that is crossed. Therefore it is OK to determine
@@ -998,62 +1250,62 @@ private void handleIntermediateStops(VehicleState vehicleState,
.expectedTravelTimeBetweenMatches(vehicleId, previousAvlDate,
oldMatch, newMatch);
long elapsedAvlTime = endTime - beginTime - numZeroTimes;
-
- // speedRatio is how much time vehicle took to travel compared to the
+
+ // speedRatio is how much time vehicle took to travel compared to the
// expected travel time. A value greater than 1.0 means that vehicle
// is taking longer than expected and the expected travel times should
- // therefore be increased accordingly. There are situations where
- // totalExpectedTravelTimeMsec can be zero or really small, such as
- // when using schedule based travel times and the schedule doesn't
+ // therefore be increased accordingly. There are situations where
+ // totalExpectedTravelTimeMsec can be zero or really small, such as
+ // when using schedule based travel times and the schedule doesn't
// provide enough time to even account for the 10 or seconds expected
// for wait time stops. Need to make sure that don't divide by zero
// for this situation, where expected travel time is 5 msec or less,
- // use a speedRatio of 1.0.
+ // use a speedRatio of 1.0.
double speedRatio;
if (totalExpectedTravelTimeMsec > 5)
speedRatio = (double) elapsedAvlTime / totalExpectedTravelTimeMsec;
else
speedRatio = 1.0;
-
- // To determine which path use the stopInfo if available since that
+
+ // To determine which path use the stopInfo if available since that
// way won't use the wrong path index if the vehicle is matching to
// just beyond the stop.
VehicleAtStopInfo oldVehicleAtStopInfo = oldMatch.getAtStop();
- Indices indices = oldVehicleAtStopInfo != null ?
- oldVehicleAtStopInfo.clone().incrementStopPath(endTime) :
+ Indices indices = oldVehicleAtStopInfo != null ?
+ oldVehicleAtStopInfo.clone().incrementStopPath(endTime) :
oldMatch.getIndices();
-
- VehicleAtStopInfo newVehicleAtStopInfo = newMatch.getAtStop();
+
+ VehicleAtStopInfo newVehicleAtStopInfo = newMatch.getAtStop();
Indices endIndices =
newVehicleAtStopInfo != null ?
newVehicleAtStopInfo.clone() : newMatch.getIndices();
-
+
// Determine time to first stop
SpatialMatch matchAtNextStop = oldMatch.getMatchAtJustBeforeNextStop();
long travelTimeToFirstStop = TravelTimes.getInstance()
.expectedTravelTimeBetweenMatches(vehicleId,
avlDate, oldMatch, matchAtNextStop);
double timeWithoutSpeedRatio = travelTimeToFirstStop;
- long arrivalTime =
+ long arrivalTime =
beginTime + Math.round(timeWithoutSpeedRatio * speedRatio);
// Go through each stop between the old match and the new match and
// determine the arrival and departure times...
logger.debug("For vehicleId={} determining if it traversed stops " +
- "in between the new and the old AVL report...",
+ "in between the new and the old AVL report...",
vehicleId);
Block block = indices.getBlock();
while (indices.isEarlierStopPathThan(endIndices)) {
// Determine arrival time for current stop
ArrivalDeparture arrival = createArrivalTime(vehicleState,
- arrivalTime,
- newMatch.getBlock(),
+ arrivalTime,
+ newMatch.getBlock(),
indices.getTripIndex(),
indices.getStopPathIndex());
storeInDbAndLog(arrival);
-
+
// Determine departure time for current stop
- double stopTime = block.getPathStopTime(indices.getTripIndex(),
+ double stopTime = block.getPathStopTime(indices.getTripIndex(),
indices.getStopPathIndex());
// Make sure that the departure time is different by at least
// 1 msec so that times will be ordered properly when querying
@@ -1061,39 +1313,61 @@ private void handleIntermediateStops(VehicleState vehicleState,
if (stopTime * speedRatio < 1.0)
stopTime = 1.0/speedRatio;
timeWithoutSpeedRatio += stopTime;
- long departureTime =
+ long departureTime =
beginTime + Math.round(timeWithoutSpeedRatio * speedRatio);
- ArrivalDeparture departure = createDepartureTime(vehicleState,
- departureTime,
- newMatch.getBlock(),
- indices.getTripIndex(),
- indices.getStopPathIndex());
+ Long dwellTime = recalculateDwellTimeUsingThresholds(departureTime - arrivalTime);
+ ArrivalDeparture departure = createDepartureTime(vehicleState,
+ departureTime,
+ newMatch.getBlock(),
+ indices.getTripIndex(),
+ indices.getStopPathIndex(),
+ dwellTime);
storeInDbAndLog(departure);
-
- // Determine travel time to next time for next time through
+
+ // Determine travel time to next time for next time through
// the while loop
indices.incrementStopPath();
- double pathTravelTime =
- block.getStopPathTravelTime(indices.getTripIndex(),
+ double pathTravelTime =
+ block.getStopPathTravelTime(indices.getTripIndex(),
indices.getStopPathIndex());
- if (pathTravelTime * speedRatio < 1.0 )
+ if (pathTravelTime * speedRatio < 1.0 )
pathTravelTime = 1.0/speedRatio;
timeWithoutSpeedRatio += pathTravelTime;
- arrivalTime =
+ arrivalTime =
beginTime + Math.round(timeWithoutSpeedRatio * speedRatio);
}
-
+
logger.debug("For vehicleId={} done determining if it traversed " +
- "stops in between the new and the old AVL report.",
+ "stops in between the new and the old AVL report.",
vehicleId);
}
-
+
+ /**
+ * Recalculate dwellTime using the Min and Max Allowable Dwell Time values.
+ * If DwellTime is below Min value, then Min value is returned.
+ * If DwellTime is above Max value, then NULL is returned since the value is likely an error.
+ *
+ * @param dwellTime
+ * Originally calculated dwellTime
+ */
+ private Long recalculateDwellTimeUsingThresholds(Long dwellTime){
+ if(dwellTime != null){
+ if(dwellTime < getMinAllowableDwellTime()){
+ return getMinAllowableDwellTime();
+ }
+ else if(dwellTime <= getMaxAllowableDwellTime()){
+ return dwellTime;
+ }
+ }
+ return null;
+ }
+
/**
* Processes updated vehicleState to generate associated arrival and
* departure times. Looks at both the previous match and the current
* match to determine which stops need to generate times for. Stores
* the resulting arrival/departure times into the database.
- *
+ *
* @param vehicleState
* @return List of generated ArrivalDeparture times
*/
@@ -1111,21 +1385,21 @@ public void generate(VehicleState vehicleState) {
logger.error("Vehicle was not matched when trying to process " +
"arrival/departure times. {}", vehicleState);
// Return empty arrivalDepartures list
- return;
+ return;
}
-
+
// If no old match then can determine the stops traversed between the
// old match and the new one. But this will frequently happen because
// sometimes won't get matches until vehicle has gone past the initial
// stop of the block due to not getting assignment right away or some
// kind of AVL issue. For this situation still want to estimate the
- // arrival/departure times for the previous stops.
+ // arrival/departure times for the previous stops.
SpatialMatch oldMatch = vehicleState.getPreviousMatch();
if (oldMatch == null) {
logger.debug("For vehicleId={} there was no previous match " +
"so seeing if can generate arrivals/departures for " +
"beginning of block", vehicleState.getVehicleId());
- // Don't have an oldMatch, but see if can estimate times anyways
+ // Don't have an oldMatch, but see if can estimate times anyways
estimateArrivalsDeparturesWithoutPreviousMatch(vehicleState);
return;
}
@@ -1135,61 +1409,61 @@ public void generate(VehicleState vehicleState) {
// arrival/departure times because the vehicles isn't or wasn't
// actually at the layover. This can happen because sometimes
// can jump the match ahead to the layover even though the
- // vehicle isn't actually there. Can't just use
+ // vehicle isn't actually there. Can't just use
// CoreConfig.getMaxDistanceFromSegment() since often for agencies
// like mbta the stops are not on the path which means that a layover
// match is likely to be greater than getMaxDistanceFromSegment() but
// still want to record the departure time.
- boolean oldMatchIsProblematic = oldMatch.isLayover()
- && (oldMatch.getDistanceToSegment() >
+ boolean oldMatchIsProblematic = oldMatch.isLayover()
+ && (oldMatch.getDistanceToSegment() >
CoreConfig.getLayoverDistance());
- boolean newMatchIsProblematic = newMatch.isLayover()
- && (newMatch.getDistanceToSegment() >
+ boolean newMatchIsProblematic = newMatch.isLayover()
+ && (newMatch.getDistanceToSegment() >
CoreConfig.getLayoverDistance());
-
+
if (oldMatchIsProblematic || newMatchIsProblematic) {
logger.warn("For vehicleId={} the old or the new match had a " +
"match distance greater than allowed. Therefore not " +
"generating arrival/departure times. " +
"Max allowed layoverDistance={}. oldMatch={} newMatch={}",
- vehicleState.getVehicleId(),
+ vehicleState.getVehicleId(),
CoreConfig.getLayoverDistance(), oldMatch, newMatch);
return;
}
-
+
// If too many stops were traversed given the AVL time then there must
// be something wrong so return
- AvlReport previousAvlReport =
+ AvlReport previousAvlReport =
vehicleState.getPreviousAvlReportFromSuccessfulMatch();
AvlReport avlReport = vehicleState.getAvlReport();
if (tooManyStopsTraversed(oldMatch, newMatch, previousAvlReport,
avlReport))
return;
-
+
// If no stops were traversed simply return
- if (!shouldProcessArrivedOrDepartedStops(oldMatch, newMatch))
+ if (!shouldProcessArrivedOrDepartedStops(oldMatch, newMatch))
return;
// Process the arrival/departure times since traversed at least one stop
logger.debug("vehicleId={} traversed at least one stop so " +
- "determining arrival/departure times. oldMatch={} newMatch={}",
+ "determining arrival/departure times. oldMatch={} newMatch={}",
vehicleState.getVehicleId(), oldMatch, newMatch);
-
+
// If vehicle was at a stop with the old match and has now departed
// then determine the departure time. Update the beginTime
// accordingly since it should be the departure time instead of
// the time of previous AVL report.
long beginTime = handleVehicleDepartingStop(vehicleState);
-
+
// If vehicle arrived at a stop then determine the arrival
// time. Update the endTime accordingly since should use the time
// that vehicle actually arrived instead of the AVL time.
long endTime = handleVehicleArrivingAtStop(vehicleState, beginTime);
-
+
// Determine arrival/departure info for in between stops. This needs to
// be called after handleVehicleArrivingAtStop() because need endTime
// from that method.
handleIntermediateStops(vehicleState, beginTime, endTime);
}
-
+
}
diff --git a/transitime/src/main/java/org/transitime/core/ArrivalDepartureGeneratorFactory.java b/transitclock/src/main/java/org/transitclock/core/ArrivalDepartureGeneratorFactory.java
similarity index 83%
rename from transitime/src/main/java/org/transitime/core/ArrivalDepartureGeneratorFactory.java
rename to transitclock/src/main/java/org/transitclock/core/ArrivalDepartureGeneratorFactory.java
index 386474962..9ea5163e1 100644
--- a/transitime/src/main/java/org/transitime/core/ArrivalDepartureGeneratorFactory.java
+++ b/transitclock/src/main/java/org/transitclock/core/ArrivalDepartureGeneratorFactory.java
@@ -15,16 +15,16 @@
* along with Transitime.org . If not, see .
*/
-package org.transitime.core;
+package org.transitclock.core;
-import org.transitime.config.StringConfigValue;
-import org.transitime.utils.ClassInstantiator;
+import org.transitclock.config.StringConfigValue;
+import org.transitclock.utils.ClassInstantiator;
/**
* For instantiating a ArrivalDepartureGenerator object that generates
* arrival/departure data when a new match is generated for a vehicle. The class
* to be instantiated can be set using the config variable
- * transitime.core.arrivalDepartureGeneratorClass
+ * transitclock.core.arrivalDepartureGeneratorClass
*
* @author SkiBu Smith
*
@@ -33,8 +33,8 @@ public class ArrivalDepartureGeneratorFactory {
// The name of the class to instantiate
private static StringConfigValue className =
- new StringConfigValue("transitime.core.arrivalDepartureGeneratorClass",
- "org.transitime.core.ArrivalDepartureGeneratorDefaultImpl",
+ new StringConfigValue("transitclock.core.arrivalDepartureGeneratorClass",
+ "org.transitclock.core.ArrivalDepartureGeneratorDefaultImpl",
"Specifies the name of the class used for generating " +
"arrival/departure data.");
diff --git a/transitime/src/main/java/org/transitime/core/AvlProcessor.java b/transitclock/src/main/java/org/transitclock/core/AvlProcessor.java
similarity index 85%
rename from transitime/src/main/java/org/transitime/core/AvlProcessor.java
rename to transitclock/src/main/java/org/transitclock/core/AvlProcessor.java
index 67b9da550..3bf484eba 100644
--- a/transitime/src/main/java/org/transitime/core/AvlProcessor.java
+++ b/transitclock/src/main/java/org/transitclock/core/AvlProcessor.java
@@ -14,41 +14,34 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.core;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+package org.transitclock.core;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.transitime.applications.Core;
-import org.transitime.config.BooleanConfigValue;
-import org.transitime.config.DoubleConfigValue;
-import org.transitime.config.IntegerConfigValue;
-import org.transitime.configData.AgencyConfig;
-import org.transitime.configData.AvlConfig;
-import org.transitime.configData.CoreConfig;
-import org.transitime.core.SpatialMatcher.MatchingType;
-import org.transitime.core.autoAssigner.AutoBlockAssigner;
-import org.transitime.core.dataCache.PredictionDataCache;
-import org.transitime.core.dataCache.VehicleDataCache;
-import org.transitime.core.dataCache.VehicleStateManager;
-import org.transitime.db.structs.AvlReport;
-import org.transitime.db.structs.Block;
-import org.transitime.db.structs.Location;
-import org.transitime.db.structs.Route;
-import org.transitime.db.structs.Stop;
-import org.transitime.db.structs.Trip;
-import org.transitime.db.structs.VehicleEvent;
-import org.transitime.db.structs.AvlReport.AssignmentType;
-import org.transitime.logging.Markers;
-import org.transitime.utils.Geo;
-import org.transitime.utils.IntervalTimer;
-import org.transitime.utils.StringUtils;
-import org.transitime.utils.Time;
+import org.transitclock.applications.Core;
+import org.transitclock.config.BooleanConfigValue;
+import org.transitclock.config.DoubleConfigValue;
+import org.transitclock.config.IntegerConfigValue;
+import org.transitclock.configData.AgencyConfig;
+import org.transitclock.configData.AvlConfig;
+import org.transitclock.configData.CoreConfig;
+import org.transitclock.core.SpatialMatcher.MatchingType;
+import org.transitclock.core.autoAssigner.AutoBlockAssigner;
+import org.transitclock.core.blockAssigner.BlockAssigner;
+import org.transitclock.core.dataCache.PredictionDataCache;
+import org.transitclock.core.dataCache.CanceledTripManager;
+import org.transitclock.core.dataCache.VehicleDataCache;
+import org.transitclock.core.dataCache.VehicleStateManager;
+import org.transitclock.db.structs.*;
+import org.transitclock.db.structs.AvlReport.AssignmentType;
+import org.transitclock.logging.Markers;
+import org.transitclock.monitoring.MonitoringService;
+import org.transitclock.utils.Geo;
+import org.transitclock.utils.IntervalTimer;
+import org.transitclock.utils.StringUtils;
+import org.transitclock.utils.Time;
+
+import java.util.*;
/**
* This is a very important high-level class. It takes the AVL data and
@@ -76,7 +69,7 @@ private static double getTerminalDistanceForRouteMatching() {
private static DoubleConfigValue terminalDistanceForRouteMatching =
new DoubleConfigValue(
- "transitime.core.terminalDistanceForRouteMatching",
+ "transitclock.core.terminalDistanceForRouteMatching",
100.0,
"How far vehicle must be away from the terminal before doing "
+ "initial matching. This is important because when vehicle is at "
@@ -85,7 +78,7 @@ private static double getTerminalDistanceForRouteMatching() {
private static IntegerConfigValue allowableBadAssignments =
new IntegerConfigValue(
- "transitime.core.allowableBadAssignments", 0,
+ "transitclock.core.allowableBadAssignments", 0,
"If get a bad assignment, such as no assignment, but no "
+ "more than allowableBadAssignments then will use the "
+ "previous assignment. Useful for when assignment part "
@@ -93,7 +86,7 @@ private static double getTerminalDistanceForRouteMatching() {
private static BooleanConfigValue emailMessagesWhenAssignmentGrabImproper =
new BooleanConfigValue(
- "transitime.core.emailMessagesWhenAssignmentGrabImproper",
+ "transitclock.core.emailMessagesWhenAssignmentGrabImproper",
false,
"When one vehicle gets assigned by AVL feed but another "
+ "vehicle already has that assignment then sometimes the "
@@ -106,12 +99,34 @@ private static double getTerminalDistanceForRouteMatching() {
private static DoubleConfigValue maxDistanceForAssignmentGrab =
new DoubleConfigValue(
- "transitime.core.maxDistanceForAssignmentGrab",
+ "transitclock.core.maxDistanceForAssignmentGrab",
10000.0,
"For when another vehicles gets assignment and needs to "
+ "grab it from another vehicle. The new vehicle must "
+ "match to route within maxDistanceForAssignmentGrab in "
+ "order to grab the assignment.");
+
+ private static DoubleConfigValue maxMatchDistanceFromAVLRecord =
+ new DoubleConfigValue(
+ "transitclock.core.maxMatchDistanceFromAVLRecord",
+ 500.0,
+ "For logging distance between spatial match and actual AVL assignment ");
+
+ private static BooleanConfigValue ignoreInactiveBlocks =
+ new BooleanConfigValue(
+ "transitclock.core.ignoreInactiveBlocks",
+ true,
+ "If the block isn't active at this time then ignore it. This way "
+ + "don't look at each trip to see if it is active which is important "
+ + "because looking at each trip means all the trip data including "
+ + "travel times needs to be lazy loaded, which can be slow.");
+
+
+ private double getMaxMatchDistanceFromAVLRecord() {
+ return maxMatchDistanceFromAVLRecord.getValue();
+ }
+
+
/************************** Logging *******************************/
@@ -220,11 +235,21 @@ public void makeVehicleUnpredictableAndGrabAssignment(
vehicleState.unsetBlock(BlockAssignmentMethod.ASSIGNMENT_GRABBED);
}
+ /**
+ * Removes the vehicle from the VehicleDataCache.
+ *
+ * @param vehicleId
+ * The vehicle to remove
+ */
+ public void removeFromVehicleDataCache(String vehicleId) {
+ VehicleDataCache.getInstance().removeVehicle(vehicleId);
+ }
+
/**
* Looks at the previous AVL reports to determine if vehicle is actually
* moving. If it is not moving then the vehicle is made unpredictable. Uses
- * the system properties transitime.core.timeForDeterminingNoProgress and
- * transitime.core.minDistanceForNoProgress
+ * the system properties transitclock.core.timeForDeterminingNoProgress and
+ * transitclock.core.minDistanceForNoProgress
*
* @param bestTemporalMatch
* @param vehicleState
@@ -294,8 +319,8 @@ private boolean handleIfVehicleNotMakingProgress(
* Looks at the previous AVL reports to determine if vehicle is actually
* moving. If it is not moving then the vehicle should be marked as being
* delayed. Uses the system properties
- * transitime.core.timeForDeterminingDelayed and
- * transitime.core.minDistanceForDelayed
+ * transitclock.core.timeForDeterminingDelayed and
+ * transitclock.core.minDistanceForDelayed
*
* @param vehicleState
* For providing the temporal match and the AVL history. It is
@@ -355,9 +380,9 @@ private boolean handlePossibleVehicleDelay(VehicleState vehicleState) {
+ "traveled only "
+ Geo.distanceFormat(distanceTraveled)
+ " while "
- + "transitime.core.timeForDeterminingDelayedSecs="
+ + "transitclock.core.timeForDeterminingDelayedSecs="
+ maxDelayedSecs + " and "
- + "transitime.core.minDistanceForDelayed="
+ + "transitclock.core.minDistanceForDelayed="
+ Geo.distanceFormat(minDistance);
// Log the event
@@ -402,6 +427,7 @@ public void matchNewFixForPredictableVehicle(VehicleState vehicleState) {
+ vehicleState);
}
+ logger.debug("STARTOFMATCHING");
logger.debug("Matching already predictable vehicle using new AVL "
+ "report. The old spatial match is {}", vehicleState);
@@ -413,9 +439,19 @@ public void matchNewFixForPredictableVehicle(VehicleState vehicleState) {
spatialMatches.size(), spatialMatches);
// Find best temporal match of the spatial matches
- TemporalMatch bestTemporalMatch = TemporalMatcher.getInstance()
- .getBestTemporalMatch(vehicleState, spatialMatches);
+ TemporalMatch bestTemporalMatch = null;
+ if (CoreConfig.tryForExactTripMatch()) {
+ //strict trip-level matching
+ TemporalMatcher.getInstance()
+ .getBestTemporalMatch(vehicleState, spatialMatches, true);
+ }
+ if (bestTemporalMatch == null) {
+ // match anything
+ bestTemporalMatch = TemporalMatcher.getInstance()
+ .getBestTemporalMatch(vehicleState, spatialMatches);
+ }
+
// Log this as info since matching is a significant milestone
logger.info("For vehicleId={} the best match is {}",
vehicleState.getVehicleId(), bestTemporalMatch);
@@ -464,12 +500,15 @@ public void matchNewFixForPredictableVehicle(VehicleState vehicleState) {
vehicleState.getVehicleId(),
vehicleState.numberOfBadMatches());
}
-
+
// If schedule adherence is bad then try matching vehicle to assignment
// again. This can make vehicle unpredictable if can't match vehicle to
// assignment.
if (vehicleState.isPredictable() && vehicleState.lastMatchIsValid())
verifyRealTimeSchAdh(vehicleState);
+
+ logger.debug("ENDOFMATCHING");
+
}
/**
@@ -559,9 +598,53 @@ private void updateVehicleStateFromAssignment(TemporalMatch bestMatch,
vehicleState.setMatch(bestMatch);
vehicleState.setBlock(block, blockAssignmentMethod, assignmentId,
predictable);
+
+ if (bestMatch != null) {
+ logConflictingSpatialAssigment(bestMatch, vehicleState);
+ }
}
/**
+ * compare the match to the avl location and log if they differ greatly.
+ * Note we just log this, we do not make the vehicle unpredictable.
+ * @param bestMatch
+ * @param vehicleState
+ */
+ private void logConflictingSpatialAssigment(TemporalMatch bestMatch,
+ VehicleState vehicleState) {
+ if (vehicleState == null || vehicleState.getAvlReport() == null) return;
+
+ // avl location
+ double avlLat = vehicleState.getAvlReport().getLat();
+ double avlLon = vehicleState.getAvlReport().getLon();
+ Location avlLocation = new Location(avlLat, avlLon);
+
+ // match location
+ VectorWithHeading segment = bestMatch.getIndices().getSegment();
+ double distanceAlongSegment = bestMatch.getDistanceAlongSegment();
+ Location matchLocation = segment.locAlongVector(distanceAlongSegment);
+
+ long tripStartTime = bestMatch.getTrip().getStartTime() * 1000 + Time.getStartOfDay(new Date());
+ // ignore future trips as we are deadheading
+ if (tripStartTime > Core.getInstance().getSystemTime())
+ return;
+
+ // difference
+ double deltaDistance = Math.abs(Geo.distance(avlLocation, matchLocation));
+
+ if (vehicleState.isPredictable() && deltaDistance > getMaxMatchDistanceFromAVLRecord()) {
+ String eventDescription = "Vehicle match conflict from AVL report of "
+ + Geo.distanceFormat(deltaDistance) + " from match " + matchLocation;
+
+ VehicleEvent.create(vehicleState.getAvlReport(), bestMatch, VehicleEvent.AVL_CONFLICT,
+ eventDescription, true, // predictable
+ false, // becameUnpredictable
+ null); // supervisor
+ }
+
+ }
+
+ /**
* Attempts to match vehicle to the specified route by finding appropriate
* block assignment. Updates the VehicleState with the new block assignment
* and match. These will be null if vehicle could not successfully be
@@ -609,7 +692,8 @@ private boolean matchVehicleToRouteAssignment(String routeId,
// don't look at each trip to see if it is active which is important
// because looking at each trip means all the trip data including
// travel times needs to be lazy loaded, which can be slow.
- if (!block.isActive(avlReport.getDate())) {
+ // Override by setting transitclock.core.ignoreInactiveBlocks to false
+ if (!block.isActive(avlReport.getDate()) && ignoreInactiveBlocks.getValue()) {
if (logger.isDebugEnabled()) {
logger.debug("For vehicleId={} ignoring block ID {} with "
+ "start_time={} and end_time={} because not "
@@ -702,11 +786,22 @@ private boolean matchVehicleToBlockAssignment(Block block,
avlReport.getVehicleId(), block.getId(), spatialMatches);
// Determine the best temporal match
- TemporalMatch bestMatch = TemporalMatcher.getInstance()
- .getBestTemporalMatchComparedToSchedule(avlReport,
- spatialMatches);
- logger.debug("Best temporal match for vehicleId={} is {}",
- avlReport.getVehicleId(), bestMatch);
+ TemporalMatch bestMatch = null;
+ if (CoreConfig.tryForExactTripMatch()) {
+ // strict trip matching is configured -- only consider matches
+ // that are same as assignment
+ bestMatch = TemporalMatcher.getInstance()
+ .getBestTemporalMatchComparedToSchedule(avlReport,
+ spatialMatches, true);
+ }
+ if (bestMatch == null) {
+ // try to match to any assignment
+ bestMatch = TemporalMatcher.getInstance()
+ .getBestTemporalMatchComparedToSchedule(avlReport,
+ spatialMatches);
+ logger.debug("Best temporal match for vehicleId={} is {}",
+ avlReport.getVehicleId(), bestMatch);
+ }
// If best match is a non-layover but cannot confirm that the heading
// is acceptable then don't consider this a match. Instead, wait till
@@ -881,7 +976,7 @@ private boolean matchProblematicDueOtherVehicleHavingAssignment(
/**
* For reducing e-mail logging messages when problem grabbing assignment.
- * Java property transitime.avl.emailMessagesWhenAssignmentGrabImproper must
+ * Java property transitclock.avl.emailMessagesWhenAssignmentGrabImproper must
* be true for e-mail to be sent when there is an error.
*
* @param vehicleId
@@ -942,7 +1037,6 @@ private void unassignOtherVehiclesFromBlock(Block block, String newVehicleId) {
* vehicle can be made predictable. The AvlReport is obtained from the
* vehicleState parameter.
*
- * @param avlReport
* @param vehicleState
* provides current AvlReport plus is updated by this method with
* the new state.
@@ -990,7 +1084,15 @@ public boolean matchVehicleToAssignment(VehicleState vehicleState) {
automaticalyMatchVehicleToAssignment(vehicleState);
if (autoAssigned)
return true;
-
+
+ if (AssignmentType.TRIP_ID.equals(avlReport.getAssignmentType())
+ && avlReport.getAssignmentId() != null) {
+ // given an assignment that didn't match that
+ // create an event for later forensics
+ logInvalidAssignment(vehicleState);
+ MonitoringService.getInstance().sumMetric("PredictionAvlInvalidMatch");
+ }
+
// There was no valid block or route assignment from AVL feed so can't
// do anything. But set the block assignment for the vehicle
// so it is up to date. This call also sets the vehicle state
@@ -1042,7 +1144,7 @@ private void handlePredictableVehicleWithoutAvlAssignment(
+ oldAssignment
+ " but received " + vehicleState.getBadAssignmentsInARow()
+ " null assignments in a row, which is configured by "
- + "transitime.core.allowableBadAssignments to be too many, "
+ + "transitclock.core.allowableBadAssignments to be too many, "
+ "so making vehicle unpredictable.";
makeVehicleUnpredictable(vehicleState.getVehicleId(),
eventDescription, VehicleEvent.ASSIGNMENT_CHANGED);
@@ -1228,16 +1330,9 @@ private void verifyRealTimeSchAdh(VehicleState vehicleState) {
String eventDescription = "Vehicle had schedule adherence of "
+ scheduleAdherence + " which is beyond acceptable "
+ "limits. Therefore vehicle made unpredictable.";
- VehicleEvent.create(vehicleState.getAvlReport(),
- vehicleState.getMatch(), VehicleEvent.NO_MATCH,
- eventDescription, false, // predictable,
- true, // becameUnpredictable
- null); // supervisor
-
- // Clear out match because it is no good! This is especially
- // important for when determining arrivals/departures because
- // that looks at previous match and current match.
- vehicleState.setMatch(null);
+
+ // Clear out match, make vehicle event, clear predictions.
+ makeVehicleUnpredictable(vehicleState.getVehicleId(), eventDescription, VehicleEvent.NO_MATCH);
// Schedule adherence not reasonable so match vehicle to assignment
// again.
@@ -1294,6 +1389,12 @@ private void lowLevelProcessAvlReport(AvlReport avlReport,
// Keep track of last AvlReport even if vehicle not predictable.
vehicleState.setAvlReport(avlReport);
+ // If asigned trip is canceled, do shouldn't be generating
+ // predictions.
+ if(isCanceled(vehicleState)){
+ return;
+ }
+
// If part of consist and shouldn't be generating predictions
// and such and shouldn't grab assignment the simply return
// not that the last AVL report has been set for the vehicle.
@@ -1396,8 +1497,8 @@ private void lowLevelProcessAvlReport(AvlReport avlReport,
// Write out current vehicle state to db so can join it with AVL
// data from db and get historical context of AVL report.
- org.transitime.db.structs.VehicleState dbVehicleState =
- new org.transitime.db.structs.VehicleState(vehicleState);
+ org.transitclock.db.structs.VehicleState dbVehicleState =
+ new org.transitclock.db.structs.VehicleState(vehicleState);
Core.getInstance().getDbLogger().add(dbVehicleState);
} // End of synchronizing on vehicleState }
}
@@ -1471,14 +1572,43 @@ public void cacheAvlReportWithoutProcessing(AvlReport avlReport) {
synchronized (vehicleState) {
// Update AVL report for cached VehicleState
vehicleState.setAvlReport(avlReport);
-
+
// Let vehicle data cache know that the vehicle state was updated
// so that new IPC vehicle data will be created and cached and
// made available to the API.
VehicleDataCache.getInstance().updateVehicle(vehicleState);
}
}
-
+
+ private boolean isCanceled(VehicleState vehicleState) {
+
+ AvlReport report = vehicleState.getAvlReport();
+ String vehicleId = report.getVehicleId();
+ String tripId = getTripId(report);
+
+ if(vehicleId != null && tripId != null){
+ return CanceledTripManager.getInstance().isCanceled(vehicleId, tripId);
+ }
+
+ return false;
+ }
+
+ private String getTripId(AvlReport report) {
+ if(report.getAssignmentType() == AssignmentType.TRIP_ID){
+ return report.getAssignmentId();
+ }
+ return null;
+ }
+
+ private void logInvalidAssignment(VehicleState vehicleState) {
+ final String description = "Assignment " + vehicleState.getAvlReport().getAssignmentId()
+ + " not valid";
+ VehicleEvent.create(vehicleState.getAvlReport(),
+ vehicleState.getMatch(), VehicleEvent.UNMATCHED_ASSIGNMENT,
+ description, false, // predictable
+ true, // becameUnpredictable
+ null); // supervisor
+ }
/**
* First does housekeeping for the AvlReport (stores it in db, logs it,
* etc). Processes the AVL report by matching to the assignment and
@@ -1498,11 +1628,20 @@ public void processAvlReport(AvlReport avlReport) {
if (AutoBlockAssigner.ignoreAvlAssignments()
&& !avlReport.isForSchedBasedPreds()) {
logger.debug("Removing assignment from AVL report because "
- + "transitime.autoBlockAssigner.ignoreAvlAssignments=true. {}",
+ + "transitclock.autoBlockAssigner.ignoreAvlAssignments=true. {}",
avlReport);
avlReport.setAssignment(null, AssignmentType.UNSET);
}
+ if (ExternalBlockAssigner.enabled()) {
+ // use the results of external AVL integration
+ ExternalBlockAssigner assigner = ExternalBlockAssigner.getInstance();
+ String assignmentId = assigner.getActiveAssignmentForVehicle(avlReport);
+ if (assignmentId != null) {
+ avlReport.setAssignment(assignmentId, AssignmentType.BLOCK_ID);
+ }
+ }
+
// The beginning of processing AVL data is an important milestone
// in processing data so log it as info.
logger.info("===================================================="
@@ -1540,8 +1679,9 @@ public void processAvlReport(AvlReport avlReport) {
// Do the low level work of matching vehicle and then generating results
lowLevelProcessAvlReport(avlReport, false);
-
logger.debug("Processing AVL report took {}msec", timer);
+ MonitoringService.getInstance().averageMetric("PredictionProcessingTimeInMillis", Double.valueOf(timer.elapsedMsec()));
+ MonitoringService.getInstance().averageMetric("PredictionTotalLatencyInMillis", Double.valueOf((System.currentTimeMillis() - avlReport.getTime())));
}
}
diff --git a/transitime/src/main/java/org/transitime/core/BlockAssignmentMethod.java b/transitclock/src/main/java/org/transitclock/core/BlockAssignmentMethod.java
similarity index 97%
rename from transitime/src/main/java/org/transitime/core/BlockAssignmentMethod.java
rename to transitclock/src/main/java/org/transitclock/core/BlockAssignmentMethod.java
index 6842bcf18..ad4940ede 100644
--- a/transitime/src/main/java/org/transitime/core/BlockAssignmentMethod.java
+++ b/transitclock/src/main/java/org/transitclock/core/BlockAssignmentMethod.java
@@ -15,7 +15,7 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.core;
+package org.transitclock.core;
/**
* Specifies the state of the block assignment for a vehicle.
diff --git a/transitime/src/main/java/org/transitime/core/BlocksInfo.java b/transitclock/src/main/java/org/transitclock/core/BlocksInfo.java
similarity index 88%
rename from transitime/src/main/java/org/transitime/core/BlocksInfo.java
rename to transitclock/src/main/java/org/transitclock/core/BlocksInfo.java
index 8617e0e4c..301b5022f 100644
--- a/transitime/src/main/java/org/transitime/core/BlocksInfo.java
+++ b/transitclock/src/main/java/org/transitclock/core/BlocksInfo.java
@@ -15,7 +15,7 @@
* along with Transitime.org . If not, see .
*/
-package org.transitime.core;
+package org.transitclock.core;
import java.util.ArrayList;
import java.util.Collection;
@@ -24,10 +24,11 @@
import java.util.List;
import java.util.Set;
-import org.transitime.applications.Core;
-import org.transitime.db.structs.Block;
-import org.transitime.gtfs.DbConfig;
-import org.transitime.utils.Time;
+import org.transitclock.applications.Core;
+import org.transitclock.config.IntegerConfigValue;
+import org.transitclock.db.structs.Block;
+import org.transitclock.gtfs.DbConfig;
+import org.transitclock.utils.Time;
/**
* Contains information on Blocks as a whole, such as which blocks are currently
@@ -37,6 +38,10 @@
*
*/
public class BlocksInfo {
+
+ private static IntegerConfigValue blockactiveForTimeBeforeSecs=new IntegerConfigValue("transitclock.core.blockactiveForTimeBeforeSecs", new Integer(0), "Now many seconds before the start of a block it will be considered active.");
+ private static IntegerConfigValue blockactiveForTimeAfterSecs=new IntegerConfigValue("transitclock.core.blockactiveForTimeAfterSecs", new Integer(-1), "Now many seconds after the end of a block it will be considered active.");
+
/********************** Member Functions **************************/
@@ -87,7 +92,7 @@ public static List getBlocksAboutToStart(int beforeStartTimeSecs) {
* @return List of currently active blocks. Will not be null.
*/
public static List getCurrentlyActiveBlocks() {
- return getCurrentlyActiveBlocks(null, null, 0, -1);
+ return getCurrentlyActiveBlocks(null, null,blockactiveForTimeBeforeSecs.getValue(), blockactiveForTimeAfterSecs.getValue());
}
/**
diff --git a/transitclock/src/main/java/org/transitclock/core/DwellTimeDetails.java b/transitclock/src/main/java/org/transitclock/core/DwellTimeDetails.java
new file mode 100644
index 000000000..c89ff92e6
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/core/DwellTimeDetails.java
@@ -0,0 +1,73 @@
+package org.transitclock.core;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.transitclock.config.IntegerConfigValue;
+import org.transitclock.db.structs.ArrivalDeparture;
+import org.transitclock.ipc.data.IpcArrivalDeparture;
+import org.transitclock.utils.Time;
+
+public class DwellTimeDetails {
+ private IpcArrivalDeparture departure;
+ private IpcArrivalDeparture arrival;
+ private static final Logger logger = LoggerFactory
+ .getLogger(DwellTimeDetails.class);
+ private static final IntegerConfigValue maxDwellTime =
+ new IntegerConfigValue(
+ "transitclock.core.maxDwellTime",
+ 10 * Time.MS_PER_MIN,
+ "This is a maximum dwell time at a stop to be taken into account for cache or prediction calculations.");
+
+
+ public IpcArrivalDeparture getDeparture() {
+ return departure;
+ }
+ public IpcArrivalDeparture getArrival() {
+ return arrival;
+ }
+ public DwellTimeDetails(IpcArrivalDeparture arrival, IpcArrivalDeparture departure) {
+ super();
+ this.arrival = arrival;
+ this.departure = departure;
+ }
+ public long getDwellTime()
+ {
+ if(this.arrival!=null && this.departure!=null&& arrival.isArrival() && departure.isDeparture())
+ {
+
+
+ if(sanityCheck())
+ {
+ long dwellTime=this.departure.getTime().getTime()-this.arrival.getTime().getTime();
+ return dwellTime;
+ }else
+ {
+ logger.warn("Outside bounds : {} ", this);
+ }
+ }
+ return -1;
+ }
+ public boolean sanityCheck()
+ {
+ if(this.arrival!=null && this.departure!=null&& arrival.isArrival() && departure.isDeparture())
+ {
+ long dwellTime=this.departure.getTime().getTime()-this.arrival.getTime().getTime();
+ if(dwellTime<0||dwellTime>maxDwellTime.getValue())
+ {
+ return false;
+ }else
+ {
+ return true;
+ }
+ }else
+ {
+ return false;
+ }
+ }
+ @Override
+ public String toString() {
+ return "DwellTimeDetails [departure=" + departure + ", arrival=" + arrival + ", getDwellTime()="
+ + getDwellTime() + ", sanityCheck()=" + sanityCheck() + "]";
+ }
+
+}
diff --git a/transitclock/src/main/java/org/transitclock/core/ExternalBlockAssigner.java b/transitclock/src/main/java/org/transitclock/core/ExternalBlockAssigner.java
new file mode 100644
index 000000000..a7e6c0ebf
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/core/ExternalBlockAssigner.java
@@ -0,0 +1,205 @@
+package org.transitclock.core;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.transitclock.applications.Core;
+import org.transitclock.config.BooleanConfigValue;
+import org.transitclock.config.IntegerConfigValue;
+import org.transitclock.config.StringConfigValue;
+import org.transitclock.configData.CoreConfig;
+import org.transitclock.core.blockAssigner.BlockAssignerCache;
+import org.transitclock.core.blockAssigner.BlockAssignerUpdater;
+import org.transitclock.db.structs.AvlReport;
+import org.transitclock.db.structs.Block;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Map;
+
+/**
+ * Enable external AVL management integration, such as via a CSV web service / file
+ */
+public class ExternalBlockAssigner {
+
+
+ static BooleanConfigValue externalAssignerEnabled =
+ new BooleanConfigValue(
+ "transitclock.externalAssignerEnabled",
+ false,
+ "Set to true to enable the manual assignment feature where "
+ + "the system tries to assign vehicle to an available block");
+
+ static StringConfigValue externalAssignerUrl =
+ new StringConfigValue("transitclock.externalAssignerUrl",
+ null,
+ "Set to the URL or file of the external AVL feed");
+
+ static StringConfigValue blockParam =
+ new StringConfigValue("transitclock.externalAssigner.block_param",
+ "block",
+ "CSV header for the block of the external AVL feed");
+
+
+ static StringConfigValue vehicleParam =
+ new StringConfigValue("transitclock.externalAssigner.vehicle_param",
+ "vehicle",
+ "CSV header for the vehicle of the external AVL feed");
+
+ static IntegerConfigValue cacheTTL =
+ new IntegerConfigValue("transitclock.externalAssigner.cacheTTL",
+ 60,
+ "time in seconds to cache feed");
+
+ private static final Logger logger = LoggerFactory
+ .getLogger(ExternalBlockAssigner.class);
+
+ ExternalBlockAssigner() {
+ // force singleton usage
+ }
+
+ /**
+ * Returns true if the externalBlockAssigner is actually enabled.
+ *
+ * @return true if enabled
+ */
+ public static boolean enabled() {
+ return externalAssignerEnabled.getValue();
+ }
+
+ private static ExternalBlockAssigner INSTANCE = new ExternalBlockAssigner();
+ private static BlockAssignerCache CACHE;
+ private static BlockAssignerUpdater UPDATER;
+
+
+ /**
+ * retrieve singleton instance of ExternalBlockAssigner
+ * @return
+ */
+ public static ExternalBlockAssigner getInstance() {
+ if (CACHE == null) {
+ // lazy instantiation of remaining members only if enabled
+ UPDATER = new BlockAssignerUpdater(externalAssignerUrl, blockParam, vehicleParam);
+ CACHE = new BlockAssignerCache(UPDATER, cacheTTL);
+ forceUpdate();
+ }
+ return INSTANCE;
+ }
+
+ /**
+ * reset internal state for unit tests.
+ */
+ static void reset() {
+ UPDATER = null;
+ CACHE = null;
+ INSTANCE = new ExternalBlockAssigner();
+ }
+
+ /**
+ * for the given avlReport, regardless of the current block assignment,
+ * check configured external BLOCK_FEED (\"transitclock.externalAssignerUrl\")
+ * and if block is present and active override the current assignment.
+ * @param avlReport
+ * @return
+ */
+ public String getActiveAssignmentForVehicle(AvlReport avlReport) {
+ int possibleAssignments = 0;
+ for (String assignmentId : blockMatch(avlReport.getVehicleId())) {
+ logger.info("possible assignment for vehicle {} = {}", avlReport.getVehicleId(), assignmentId);
+ possibleAssignments++;
+ if (assignmentId != null) {
+ int agencySeparator = assignmentId.lastIndexOf('_');
+ if (agencySeparator != -1) {
+ assignmentId = assignmentId.substring(agencySeparator+1);
+ }
+ Block requestedBlock = getActiveBlock(assignmentId, avlReport.getDate());
+ if (requestedBlock != null) {
+ logger.info("found active block {} for vehicle {}", assignmentId, avlReport.getVehicleId());
+ return assignmentId;
+ } else {
+ logger.info("block {} mismatch for vehicle {}", assignmentId, avlReport.getVehicleId());
+ }
+ }
+ }
+ logger.info("no active external assignment for vehicle {} with {} possible assignments and cache= {}",
+ avlReport.getVehicleId(), possibleAssignments, getBlockAssignmentsByVehicleIdMapFromCache().keySet());
+ return null;
+ }
+
+ /**
+ * if the blockId is active within window of service date / now retrieve the
+ * entire block.
+ * @param assignmentId
+ * @param avlReportDate
+ * @return
+ */
+ Block getActiveBlock(String assignmentId, Date avlReportDate) {
+ Collection dbBlocks =
+ Core.getInstance().getDbConfig().getBlocksForAllServiceIds(assignmentId);
+ if (dbBlocks == null) {
+ logger.warn("no block found for {}", assignmentId);
+ return null;
+ }
+ logger.info("getActiveBlock({}, {}) found {} potential blocks: {}",
+ assignmentId, avlReportDate, dbBlocks.size(), dbBlocks);
+ for (Block requestedBlock : dbBlocks) {
+ if (requestedBlock.isActive(avlReportDate,
+ CoreConfig.getAllowableEarlySeconds(),
+ -1 /* use endTime*/)) {
+ return requestedBlock;
+ } else {
+ logger.info("requestedBlock {} is not active on serviceDate {}", requestedBlock.getId(), avlReportDate);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * return a (potentially list) of blocks specified for given vehicleId.
+ * @param vehicleId
+ * @return
+ */
+ private ArrayList blockMatch(String vehicleId) {
+ // check web service for vehicle_id match
+ ArrayList blocks = getBlockAssignmentsByVehicleIdMapFromCache().get(vehicleId);
+ if (blocks != null)
+ return blocks;
+ // return empty list for easy inclusion in loop
+ return new ArrayList<>();
+ }
+
+
+ Map> getBlockAssignmentsByVehicleIdMapFromCache() {
+ return CACHE.getBlockAssignmentsByVehicleIdMap();
+ }
+
+ /**
+ * retrieve a list of block ids indexed on vehicleId. Does no caching.
+ * @return
+ */
+ Map> getBlockAssignmentsByVehicleIdMap() throws IOException {
+ return UPDATER.getBlockAssignmentsByVehicleIdMap();
+ }
+
+ /**
+ * retrieve the raw webservice feed, package private for unit tests.
+ */
+ InputStream getBlockAssignmentsByVehicleIdFeed() throws Exception {
+ if (enabled())
+ return UPDATER.getBlockAssignmentsByVehicleIdFeed();
+ return null;
+ }
+
+ /**
+ * Force a sycnhronous update of the cache. Managed internally, does not need to be
+ * explicitly called.
+ */
+ static void forceUpdate() {
+ CACHE.update();
+ }
+
+
+
+}
diff --git a/transitclock/src/main/java/org/transitclock/core/HeadwayDetails.java b/transitclock/src/main/java/org/transitclock/core/HeadwayDetails.java
new file mode 100644
index 000000000..9fd08ac81
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/core/HeadwayDetails.java
@@ -0,0 +1,81 @@
+package org.transitclock.core;
+
+import java.util.concurrent.TimeUnit;
+
+import org.transitclock.db.structs.ArrivalDeparture;
+import org.transitclock.ipc.data.IpcPrediction;
+
+/**
+ * @author Sean Óg Crudden
+ * Class to hold details of a single headway.
+ *
+ */
+public class HeadwayDetails {
+
+ private IpcPrediction vehicleAheadPrediction=null;
+ private ArrivalDeparture vehicleAheadArrival=null;
+ private IpcPrediction vehicleBehindPrediction=null;
+ Long arrivalPrediction=null;
+
+ public IpcPrediction getVehicleAheadPrediction() {
+ return vehicleAheadPrediction;
+ }
+
+ public IpcPrediction getVehicleBehindPrediction() {
+ return vehicleBehindPrediction;
+ }
+ public HeadwayDetails(Long arrivalPrediction, IpcPrediction vehicleAheadPrediction)
+ {
+ super();
+ this.vehicleAheadPrediction = vehicleAheadPrediction;
+ this.arrivalPrediction=arrivalPrediction;
+ }
+ public HeadwayDetails(Long arrivalPrediction, ArrivalDeparture vehicleAheadArrival)
+ {
+ super();
+ this.vehicleAheadArrival = vehicleAheadArrival;
+ this.arrivalPrediction=arrivalPrediction;
+ }
+
+
+
+
+
+
+
+
+ @Override
+ public String toString() {
+ return "HeadwayDetails [headway=" + TimeUnit.MILLISECONDS.toMinutes(getHeadway()) + " mins, vehicleId=" + getOtherVehicleId()
+ + ", prediction=" + basedOnPrediction() + "]";
+ }
+
+ public Long getHeadway() {
+
+ if(vehicleBehindPrediction!=null && vehicleAheadPrediction!=null)
+ return vehicleBehindPrediction.getPredictionTime() - vehicleAheadPrediction.getPredictionTime();
+ if(vehicleBehindPrediction!=null && vehicleAheadArrival!=null)
+ return vehicleBehindPrediction.getPredictionTime()-vehicleAheadArrival.getTime();
+ if(arrivalPrediction!=null && vehicleAheadArrival!=null)
+ return arrivalPrediction-vehicleAheadArrival.getTime();
+ if(arrivalPrediction!=null && vehicleAheadPrediction!=null)
+ return arrivalPrediction-vehicleAheadPrediction.getPredictionTime();
+
+ return null;
+ }
+ public String getOtherVehicleId()
+ {
+ if(vehicleAheadPrediction!=null)
+ return vehicleAheadPrediction.getVehicleId();
+ if(vehicleAheadArrival!=null)
+ return vehicleAheadArrival.getVehicleId();
+ return null;
+ }
+ public boolean basedOnPrediction()
+ {
+ if(vehicleAheadPrediction!=null)
+ return true;
+ else
+ return false;
+ }
+}
diff --git a/transitime/src/main/java/org/transitime/core/HeadwayGenerator.java b/transitclock/src/main/java/org/transitclock/core/HeadwayGenerator.java
similarity index 89%
rename from transitime/src/main/java/org/transitime/core/HeadwayGenerator.java
rename to transitclock/src/main/java/org/transitclock/core/HeadwayGenerator.java
index 61a50588e..fbb3ee726 100644
--- a/transitime/src/main/java/org/transitime/core/HeadwayGenerator.java
+++ b/transitclock/src/main/java/org/transitclock/core/HeadwayGenerator.java
@@ -15,7 +15,11 @@
* along with Transitime.org . If not, see .
*/
-package org.transitime.core;
+package org.transitclock.core;
+
+import java.util.List;
+
+import org.transitclock.db.structs.Headway;
/**
* Defines the interface for generating headway information. To create headway info using
@@ -34,5 +38,5 @@ public interface HeadwayGenerator {
*
* @param vehicleState
*/
- public void generate(VehicleState vehicleState);
+ public Headway generate(VehicleState vehicleState);
}
diff --git a/transitime/src/main/java/org/transitime/core/HeadwayGeneratorDefaultImpl.java b/transitclock/src/main/java/org/transitclock/core/HeadwayGeneratorDefaultImpl.java
similarity index 76%
rename from transitime/src/main/java/org/transitime/core/HeadwayGeneratorDefaultImpl.java
rename to transitclock/src/main/java/org/transitclock/core/HeadwayGeneratorDefaultImpl.java
index dddff8383..9478802ff 100644
--- a/transitime/src/main/java/org/transitime/core/HeadwayGeneratorDefaultImpl.java
+++ b/transitclock/src/main/java/org/transitclock/core/HeadwayGeneratorDefaultImpl.java
@@ -14,10 +14,14 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.core;
+package org.transitclock.core;
+
+import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.transitclock.db.structs.Headway;
+
/**
*
@@ -29,15 +33,19 @@ public class HeadwayGeneratorDefaultImpl implements HeadwayGenerator {
private static final Logger logger =
LoggerFactory.getLogger(HeadwayGeneratorDefaultImpl.class);
- /********************** Member Functions **************************/
+ /********************** Member Functions
+ * @return **************************/
/* (non-Javadoc)
- * @see org.transitime.core.HeadwayGenerator#generate(org.transitime.core.VehicleState)
+ * @see org.transitclock.core.HeadwayGenerator#generate(org.transitclock.core.VehicleState)
*/
@Override
- public void generate(VehicleState vehicleState) {
+ public Headway generate(VehicleState vehicleState) {
// FIXME Still needs to be implemented!!
logger.debug("HeadwayGeneratorDefaultImpl.generate() still needs to " +
"be implemented");
+ return null;
+
+
}
}
diff --git a/transitime/src/main/java/org/transitime/core/HeadwayGeneratorFactory.java b/transitclock/src/main/java/org/transitclock/core/HeadwayGeneratorFactory.java
similarity index 82%
rename from transitime/src/main/java/org/transitime/core/HeadwayGeneratorFactory.java
rename to transitclock/src/main/java/org/transitclock/core/HeadwayGeneratorFactory.java
index 48d81ca74..312cfbfa7 100644
--- a/transitime/src/main/java/org/transitime/core/HeadwayGeneratorFactory.java
+++ b/transitclock/src/main/java/org/transitclock/core/HeadwayGeneratorFactory.java
@@ -15,15 +15,15 @@
* along with Transitime.org . If not, see .
*/
-package org.transitime.core;
+package org.transitclock.core;
-import org.transitime.config.StringConfigValue;
-import org.transitime.utils.ClassInstantiator;
+import org.transitclock.config.StringConfigValue;
+import org.transitclock.utils.ClassInstantiator;
/**
* For instantiating a HeadwayGenerator object that generates headway info when
* a new match is generated for a vehicle. The class to be instantiated can be
- * set using the config variable transitime.core.headwayGeneratorClass
+ * set using the config variable transitclock.core.headwayGeneratorClass
*
* @author SkiBu Smith
*
@@ -32,8 +32,8 @@ public class HeadwayGeneratorFactory {
// The name of the class to instantiate
private static StringConfigValue className =
- new StringConfigValue("transitime.core.headwayGeneratorClass",
- "org.transitime.core.HeadwayGeneratorDefaultImpl",
+ new StringConfigValue("transitclock.core.headwayGeneratorClass",
+ "org.transitclock.core.HeadwayGeneratorDefaultImpl",
"Specifies the name of the class used for generating " +
"headway data.");
diff --git a/transitime/src/main/java/org/transitime/core/Indices.java b/transitclock/src/main/java/org/transitclock/core/Indices.java
similarity index 87%
rename from transitime/src/main/java/org/transitime/core/Indices.java
rename to transitclock/src/main/java/org/transitclock/core/Indices.java
index 85553d3d1..b3d180aa8 100644
--- a/transitime/src/main/java/org/transitime/core/Indices.java
+++ b/transitclock/src/main/java/org/transitclock/core/Indices.java
@@ -14,16 +14,21 @@
* You should have received a copy of the GNU General Public License along with
* Transitime.org . If not, see .
*/
-package org.transitime.core;
-
-import org.transitime.applications.Core;
-import org.transitime.db.structs.Block;
-import org.transitime.db.structs.StopPath;
-import org.transitime.db.structs.Route;
-import org.transitime.db.structs.ScheduleTime;
-import org.transitime.db.structs.Trip;
-import org.transitime.db.structs.TripPattern;
-import org.transitime.db.structs.VectorWithHeading;
+package org.transitclock.core;
+
+import java.io.Serializable;
+
+import org.transitclock.applications.Core;
+import org.transitclock.db.structs.ArrivalDeparture;
+import org.transitclock.db.structs.Block;
+import org.transitclock.db.structs.Route;
+import org.transitclock.db.structs.ScheduleTime;
+import org.transitclock.db.structs.StopPath;
+import org.transitclock.db.structs.Trip;
+import org.transitclock.db.structs.TripPattern;
+import org.transitclock.db.structs.VectorWithHeading;
+import org.transitclock.gtfs.DbConfig;
+import org.transitclock.ipc.data.IpcArrivalDeparture;
/**
* This private class is for keeping track of the trip, path, and segment
@@ -32,11 +37,11 @@
* @author SkiBu Smith
*
*/
-class Indices {
+public class Indices implements Serializable {
private Block block;
private int tripIndex;
private int stopPathIndex;
- private int segmentIndex;
+ private int segmentIndex;
/********************** Member Functions **************************/
@@ -59,20 +64,23 @@ public Indices(Block block, int tripIndex, int stopPathIndex,
// Make sure parameters are valid to avoid bugs. This could be
// considered a bit wasteful though.
- Trip trip = block.getTrip(tripIndex);
- if (trip == null)
- throw new IndexOutOfBoundsException("tripIndex " + tripIndex
- + " invalid for block " + block);
- StopPath stopPath = trip.getStopPath(stopPathIndex);
- if (stopPath == null)
- throw new IndexOutOfBoundsException("stopPathIndex "
- + stopPathIndex + " invalid for tripIndex " + tripIndex
- + " and block " + block);
- VectorWithHeading vector = stopPath.getSegmentVector(segmentIndex);
- if (vector == null)
- throw new IndexOutOfBoundsException("segmentIndex " + segmentIndex
- + " invalid for stopPathIndex " + stopPathIndex
- + " tripIndex " + tripIndex + " and block " + block);
+ if(block!=null)
+ {
+ Trip trip = block.getTrip(tripIndex);
+ if (trip == null)
+ throw new IndexOutOfBoundsException("tripIndex " + tripIndex
+ + " invalid for block " + block);
+ StopPath stopPath = trip.getStopPath(stopPathIndex);
+ if (stopPath == null)
+ throw new IndexOutOfBoundsException("stopPathIndex "
+ + stopPathIndex + " invalid for tripIndex " + tripIndex
+ + " and block " + block);
+ VectorWithHeading vector = stopPath.getSegmentVector(segmentIndex);
+ if (vector == null)
+ throw new IndexOutOfBoundsException("segmentIndex " + segmentIndex
+ + " invalid for stopPathIndex " + stopPathIndex
+ + " tripIndex " + tripIndex + " and block " + block);
+ }
}
/**
@@ -87,6 +95,37 @@ public Indices(SpatialMatch spatialMatch) {
this.segmentIndex = spatialMatch.getSegmentIndex();
}
+ public Indices(IpcArrivalDeparture event) {
+
+ Block block=null;
+
+ DbConfig dbConfig = Core.getInstance().getDbConfig();
+ block=dbConfig.getBlock(event.getServiceId(), event.getBlockId());
+
+ this.block=block;
+ this.tripIndex = event.getTripIndex();
+ this.stopPathIndex = event.getStopPathIndex();
+
+ }
+
+ public Indices(ArrivalDeparture event) {
+
+
+ Block block=null;
+ if(event.getBlock()==null)
+ {
+ DbConfig dbConfig = Core.getInstance().getDbConfig();
+ block=dbConfig.getBlock(event.getServiceId(), event.getBlockId());
+ }else
+ {
+ block=event.getBlock();
+ }
+ this.block=block;
+ this.tripIndex = event.getTripIndex();
+ this.stopPathIndex = event.getStopPathIndex();
+
+ }
+
/**
* Creates a copy of the Indices parameter. Useful if need to increment() or
* decrement() but don't want to affect the original object.
@@ -480,9 +519,9 @@ public int getTravelTimeForPath() {
@Override
public String toString() {
return "Indices ["
- + "blockId=" + block.getId()
+ + "blockId=" + (block!=null?block.getId():"NuLl")
// thought tripId not part of Indices nice to show for debugging
- + ", tripId=" + getTrip().getId()
+ + ", tripId=" + (getTrip()!=null?getTrip().getId():"NuLl")
+ ", tripIndex=" + tripIndex
+ ", stopPathIndex=" + stopPathIndex
+ ", segmentIndex=" + segmentIndex
@@ -590,4 +629,9 @@ public int getSegmentIndex() {
return segmentIndex;
}
+ public boolean controlPoint() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
}
diff --git a/transitime/src/main/java/org/transitime/core/MatchProcessor.java b/transitclock/src/main/java/org/transitclock/core/MatchProcessor.java
similarity index 79%
rename from transitime/src/main/java/org/transitime/core/MatchProcessor.java
rename to transitclock/src/main/java/org/transitclock/core/MatchProcessor.java
index 06643ede1..12fcaad11 100644
--- a/transitime/src/main/java/org/transitime/core/MatchProcessor.java
+++ b/transitclock/src/main/java/org/transitclock/core/MatchProcessor.java
@@ -14,19 +14,24 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.core;
+package org.transitclock.core;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.transitime.applications.Core;
-import org.transitime.configData.CoreConfig;
-import org.transitime.core.dataCache.PredictionDataCache;
-import org.transitime.db.structs.Prediction;
-import org.transitime.db.structs.Match;
-import org.transitime.ipc.data.IpcPrediction;
-import org.transitime.utils.Time;
+import org.transitclock.applications.Core;
+import org.transitclock.configData.CoreConfig;
+import org.transitclock.core.dataCache.HoldingTimeCache;
+import org.transitclock.core.dataCache.PredictionDataCache;
+import org.transitclock.core.holdingmethod.HoldingTimeGeneratorFactory;
+import org.transitclock.core.reporting.RunTimeGenerator;
+import org.transitclock.db.structs.Headway;
+import org.transitclock.db.structs.HoldingTime;
+import org.transitclock.db.structs.Match;
+import org.transitclock.db.structs.Prediction;
+import org.transitclock.ipc.data.IpcPrediction;
+import org.transitclock.utils.Time;
/**
* For generating predictions, arrival/departure times, headways etc. This class
@@ -79,11 +84,17 @@ private void processPredictions(VehicleState vehicleState) {
if (CoreConfig.getMaxPredictionsTimeForDbSecs() > 0) {
for (IpcPrediction prediction : newPredictions) {
// If prediction not too far into the future then ...
- if (prediction.getPredictionTime() - prediction.getAvlTime() < CoreConfig
- .getMaxPredictionsTimeForDbSecs() * Time.MS_PER_SEC) {
+ if (prediction.getPredictionTime() - prediction.getAvlTime() < (CoreConfig
+ .getMaxPredictionsTimeForDbSecs() * Time.MS_PER_SEC)) {
// Store the prediction into db
Prediction dbPrediction = new Prediction(prediction);
+
Core.getInstance().getDbLogger().add(dbPrediction);
+
+ }else
+ {
+ logger.debug("Difference in predictionTiem and AVLTime is {} and is greater than getMaxPredictionsTimeForDbSecs {}.", prediction.getPredictionTime() - prediction.getAvlTime(), CoreConfig
+ .getMaxPredictionsTimeForDbSecs() * Time.MS_PER_SEC);
}
}
}
@@ -96,6 +107,9 @@ private void processPredictions(VehicleState vehicleState) {
// Update predictions for vehicle
vehicleState.setPredictions(newPredictions);
+
+
+
}
/**
@@ -108,7 +122,13 @@ private void processHeadways(VehicleState vehicleState) {
logger.debug("Processing headways for vehicleId={}",
vehicleState.getVehicleId());
- HeadwayGeneratorFactory.getInstance().generate(vehicleState);
+ Headway headway = HeadwayGeneratorFactory.getInstance().generate(vehicleState);
+
+ if(headway!=null)
+ {
+ vehicleState.setHeadway(headway);
+ Core.getInstance().getDbLogger().add(headway);
+ }
}
/**
@@ -147,6 +167,13 @@ private void processSpatialMatch(VehicleState vehicleState) {
if (!match.isAtStop())
Core.getInstance().getDbLogger().add(match);
}
+
+ private void processRunTimes(VehicleState vehicleState) {
+ logger.debug("Processing runTimes for vehicleId={}",
+ vehicleState.getVehicleId());
+
+ boolean processedRunTime = RunTimesGeneratorFactory.getInstance().generate(vehicleState);
+ }
/**
* Called when vehicle is matched successfully. Generates predictions
@@ -183,5 +210,6 @@ public void generateResultsOfMatch(VehicleState vehicleState) {
processSpatialMatch(vehicleState);
}
processArrivalDepartures(vehicleState);
+ processRunTimes(vehicleState);
}
}
diff --git a/transitime/src/main/java/org/transitime/core/PredictionGenerator.java b/transitclock/src/main/java/org/transitclock/core/PredictionGenerator.java
similarity index 93%
rename from transitime/src/main/java/org/transitime/core/PredictionGenerator.java
rename to transitclock/src/main/java/org/transitclock/core/PredictionGenerator.java
index 1f72f2b53..eef1900a7 100644
--- a/transitime/src/main/java/org/transitime/core/PredictionGenerator.java
+++ b/transitclock/src/main/java/org/transitclock/core/PredictionGenerator.java
@@ -1,6 +1,6 @@
/*
* 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
@@ -15,20 +15,20 @@
* along with Transitime.org . If not, see .
*/
-package org.transitime.core;
+package org.transitclock.core;
-import java.util.List;
+import org.transitclock.ipc.data.IpcPrediction;
-import org.transitime.ipc.data.IpcPrediction;
+import java.util.List;
/**
* Defines the interface for generating predictions. To create predictions using
* an alternate method simply implement this interface and configure
* PredictionGeneratorFactory to instantiate the new class when a
* PredictionGenerator is needed.
- *
+ *
* @author SkiBu Smith
- *
+ *
*/
public interface PredictionGenerator {
diff --git a/transitime/src/main/java/org/transitime/core/PredictionGeneratorDefaultImpl.java b/transitclock/src/main/java/org/transitclock/core/PredictionGeneratorDefaultImpl.java
old mode 100644
new mode 100755
similarity index 52%
rename from transitime/src/main/java/org/transitime/core/PredictionGeneratorDefaultImpl.java
rename to transitclock/src/main/java/org/transitclock/core/PredictionGeneratorDefaultImpl.java
index d756fd681..eea9d9f49
--- a/transitime/src/main/java/org/transitime/core/PredictionGeneratorDefaultImpl.java
+++ b/transitclock/src/main/java/org/transitclock/core/PredictionGeneratorDefaultImpl.java
@@ -14,23 +14,29 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.core;
-
-import java.util.ArrayList;
-import java.util.List;
+package org.transitclock.core;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.transitime.applications.Core;
-import org.transitime.config.BooleanConfigValue;
-import org.transitime.config.IntegerConfigValue;
-import org.transitime.db.structs.AvlReport;
-import org.transitime.db.structs.StopPath;
-import org.transitime.db.structs.Trip;
-import org.transitime.ipc.data.IpcPrediction;
-import org.transitime.ipc.data.IpcPrediction.ArrivalOrDeparture;
-import org.transitime.utils.Geo;
-import org.transitime.utils.Time;
+import org.transitclock.applications.Core;
+import org.transitclock.config.BooleanConfigValue;
+import org.transitclock.config.IntegerConfigValue;
+import org.transitclock.config.LongConfigValue;
+import org.transitclock.core.dataCache.HoldingTimeCache;
+import org.transitclock.core.dataCache.StopPathPredictionCacheFactory;
+import org.transitclock.core.dataCache.VehicleStateManager;
+import org.transitclock.core.holdingmethod.HoldingTimeGeneratorFactory;
+import org.transitclock.core.predictiongenerator.PredictionComponentElementsGenerator;
+import org.transitclock.core.predictiongenerator.bias.BiasAdjuster;
+import org.transitclock.core.predictiongenerator.bias.BiasAdjusterFactory;
+import org.transitclock.db.structs.*;
+import org.transitclock.ipc.data.IpcPrediction;
+import org.transitclock.ipc.data.IpcPrediction.ArrivalOrDeparture;
+import org.transitclock.monitoring.MonitoringService;
+import org.transitclock.utils.Geo;
+import org.transitclock.utils.Time;
+
+import java.util.*;
/**
* When a new match based on AVL data is made for a vehicle the methods in this
@@ -58,25 +64,41 @@
* @author SkiBu Smith
*
*/
-public class PredictionGeneratorDefaultImpl implements PredictionGenerator {
+public class PredictionGeneratorDefaultImpl implements PredictionGenerator, PredictionComponentElementsGenerator{
+
+ private MonitoringService monitoring = null;
+
+ private static BooleanConfigValue terminatePredictionsAtTripEnd =
+ new BooleanConfigValue("transitclock.core.terminatePredictionsAtTripEnd",
+ false,
+ "Once maxPredictionsTimeSecs reached, continue until end of trip");
+ public static boolean getTerminatePredictionsAtTripEnd() {
+ return terminatePredictionsAtTripEnd.getValue();
+ }
private static IntegerConfigValue maxPredictionsTimeSecs =
- new IntegerConfigValue("transitime.core.maxPredictionsTimeSecs",
+ new IntegerConfigValue("transitclock.core.maxPredictionsTimeSecs",
45 * Time.SEC_PER_MIN,
"How far forward into the future should generate " +
"predictions for.");
public static int getMaxPredictionsTimeSecs() {
return maxPredictionsTimeSecs.getValue();
}
-
+
+ private static LongConfigValue generateHoldingTimeWhenPredictionWithin =
+ new LongConfigValue("transitclock.core.generateHoldingTimeWhenPredictionWithin",
+ 0L,
+ "If the prediction is less than this number of milliseconds from current time then use it to generate a holding time");
+
+
private static BooleanConfigValue useArrivalPredictionsForNormalStops =
- new BooleanConfigValue("transitime.core.useArrivalPredictionsForNormalStops",
+ new BooleanConfigValue("transitclock.core.useArrivalPredictionsForNormalStops",
true,
"For specifying whether to use arrival predictions or " +
"departure predictions for normal, non-wait time, stops.");
private static IntegerConfigValue maxLateCutoffPredsForNextTripsSecs =
- new IntegerConfigValue("transitime.core.maxLateCutoffPredsForNextTripsSecs",
+ new IntegerConfigValue("transitclock.core.maxLateCutoffPredsForNextTripsSecs",
Integer.MAX_VALUE,
"If a vehicle is further behind schedule than this amount "
+ "then predictions for subsequent trips will be marked as "
@@ -85,7 +107,7 @@ public static int getMaxPredictionsTimeSecs() {
+ "vehicle being late.");
private static BooleanConfigValue useExactSchedTimeForWaitStops =
- new BooleanConfigValue("transitime.core.useExactSchedTimeForWaitStops",
+ new BooleanConfigValue("transitclock.core.useExactSchedTimeForWaitStops",
true,
"The predicted time for wait stops includes the historic "
+ "wait stop time. This means it will be a bit after the "
@@ -96,6 +118,27 @@ public static int getMaxPredictionsTimeSecs() {
+ "time will be used. If false then the schedule time plus "
+ "the wait stop time will be used.");
+ protected static BooleanConfigValue storeTravelTimeStopPathPredictions = new BooleanConfigValue("transitclock.core.storeTravelTimeStopPathPredictions",
+ false,
+ "This is set to true to record all travelTime predictions for individual stopPaths generated. Useful for comparing performance of differant algorithms. (MAPE comparison). Not for normal use as will generate massive amounts of data.");
+
+
+ protected static BooleanConfigValue storeDwellTimeStopPathPredictions = new BooleanConfigValue("transitclock.core.storeDwellTimeStopPathPredictions",
+ false,
+ "This is set to true to record all travelTime predictions for individual dwell times generated. Useful for comparing performance of differant algorithms. (MAPE comparison). Not for normal use as will generate massive amounts of data.");
+
+
+ private static BooleanConfigValue useHoldingTimeInPrediction =
+ new BooleanConfigValue("useHoldingTimeInPrediction",
+ false,
+ "Add holding time to prediction.");
+
+ private static IntegerConfigValue maxAgeOfHistoricalPredictions =
+ new IntegerConfigValue("transitclock.core.maxAgeOfHistoricalPredictions",
+ 1,
+ "When holding on to historical predictions for future stops, how long " +
+ "to keep message before expiring. Value in minutes.");
+
private static final Logger logger =
LoggerFactory.getLogger(PredictionGeneratorDefaultImpl.class);
@@ -131,29 +174,52 @@ public static int getMaxPredictionsTimeSecs() {
* are less certain.
* @return The generated Prediction
*/
- private IpcPrediction generatePredictionForStop(AvlReport avlReport,
+ protected IpcPrediction generatePredictionForStop(AvlReport avlReport,
Indices indices, long predictionTime, boolean useArrivalTimes,
boolean affectedByWaitStop, boolean isDelayed,
- boolean lateSoMarkAsUncertain) {
- // Determine additional parameters for the prediction to be generated
+
+ boolean lateSoMarkAsUncertain, int tripCounter, Integer scheduleDeviation) {
+
+ getMonitoring().sumMetric("PredictionGenerationDefault");
+ // Determine additional parameters for the prediction to be generated
+
StopPath path = indices.getStopPath();
String stopId = path.getStopId();
int gtfsStopSeq = path.getGtfsStopSeq();
+
+ if(BiasAdjusterFactory.getInstance()!=null)
+ {
+ BiasAdjuster adjuster = BiasAdjusterFactory.getInstance();
+ predictionTime=avlReport.getTime()+adjuster.adjustPrediction(predictionTime-avlReport.getTime());
+ }
+
Trip trip = indices.getTrip();
- int expectedStopTimeMsec =
- TravelTimes.getInstance().expectedStopTimeForStopPath(indices);
-
+
+ long freqStartTime=-1;
+ VehicleState vehicleState = VehicleStateManager.getInstance().getVehicleState(avlReport.getVehicleId());
+ if(trip.isNoSchedule()) {
+ if(vehicleState.getTripStartTime(tripCounter)!=null)
+ freqStartTime=vehicleState.getTripStartTime(tripCounter).longValue();
+ }
+
// If should generate arrival time...
if ((indices.atEndOfTrip() || useArrivalTimes) && !indices.isWaitStop()) {
+ getMonitoring().sumMetric("PredictionGenerationStop");
// Create and return arrival time for this stop
- return new IpcPrediction(avlReport, stopId, gtfsStopSeq, trip,
+ return new IpcPrediction(avlReport, stopId, gtfsStopSeq, trip,
predictionTime, predictionTime, indices.atEndOfTrip(),
- affectedByWaitStop, isDelayed, lateSoMarkAsUncertain,
- ArrivalOrDeparture.ARRIVAL);
+ affectedByWaitStop, isDelayed, lateSoMarkAsUncertain, ArrivalOrDeparture.ARRIVAL,
+ scheduleDeviation, freqStartTime, tripCounter,vehicleState.isCanceled());
+
} else {
+
// Generate a departure time
+ int expectedStopTimeMsec =
+ (int) getStopTimeForPath(indices, avlReport, vehicleState);
// If at a wait stop then need to handle specially...
if (indices.isWaitStop()) {
+
+
logger.debug("For vehicleId={} the original arrival time " +
"for waitStop stopId={} is {}",
avlReport.getVehicleId(), path.getStopId(),
@@ -254,30 +320,35 @@ private IpcPrediction generatePredictionForStop(AvlReport avlReport,
long predictionForNextStopCalculation = expectedDepartureTime;
long predictionForUser = expectedDepartureTimeWithoutStopWaitTime;
+ getMonitoring().sumMetric("PredictionGenerationStop");
return new IpcPrediction(avlReport, stopId, gtfsStopSeq,
trip, predictionForUser,
predictionForNextStopCalculation,
indices.atEndOfTrip(), affectedByWaitStop,
- isDelayed, lateSoMarkAsUncertain,
- ArrivalOrDeparture.DEPARTURE);
+ isDelayed, lateSoMarkAsUncertain, ArrivalOrDeparture.DEPARTURE, scheduleDeviation,
+ freqStartTime, tripCounter,vehicleState.isCanceled());
+
} else {
+ getMonitoring().sumMetric("PredictionGenerationStop");
// Use the expected departure times, possibly adjusted for
// stop wait times
return new IpcPrediction(avlReport, stopId, gtfsStopSeq,
trip, expectedDepartureTime, expectedDepartureTime,
indices.atEndOfTrip(), affectedByWaitStop,
- isDelayed, lateSoMarkAsUncertain,
- ArrivalOrDeparture.DEPARTURE);
+ isDelayed, lateSoMarkAsUncertain, ArrivalOrDeparture.DEPARTURE,
+ scheduleDeviation, freqStartTime, tripCounter,vehicleState.isCanceled());
+
}
} else {
+ getMonitoring().sumMetric("PredictionGenerationStop");
// Create and return the departure prediction for this
// non-wait-stop stop
return new IpcPrediction(avlReport, stopId, gtfsStopSeq, trip,
predictionTime + expectedStopTimeMsec,
predictionTime + expectedStopTimeMsec,
- indices.atEndOfTrip(),
- affectedByWaitStop, isDelayed, lateSoMarkAsUncertain,
- ArrivalOrDeparture.DEPARTURE);
+ indices.atEndOfTrip(), affectedByWaitStop, isDelayed, lateSoMarkAsUncertain,
+ ArrivalOrDeparture.DEPARTURE, scheduleDeviation, freqStartTime,
+ tripCounter,vehicleState.isCanceled());
}
}
}
@@ -296,6 +367,8 @@ public List generate(VehicleState vehicleState) {
// departure time for anything else. But for non-layover stops
// can use either arrival or departure times, depending on what
// the agency wants. Therefore make this configurable.
+
+
boolean useArrivalPreds = useArrivalPredictionsForNormalStops.getValue();
// If prediction is based on scheduled departure time for a layover
@@ -310,16 +383,18 @@ public List generate(VehicleState vehicleState) {
TemporalMatch match = vehicleState.getMatch();
Indices indices = match.getIndices();
+
// Get info from the AVL report.
AvlReport avlReport = vehicleState.getAvlReport();
long avlTime = avlReport.getTime();
boolean schedBasedPreds = avlReport.isForSchedBasedPreds();
+ logger.debug("Calling prediction algorithm for {} with a match {}.", avlReport, match);
+
// Get time to end of first path and thereby determine prediction for
// first stop.
- TravelTimes travelTimes = TravelTimes.getInstance();
- long predictionTime = avlTime +
- travelTimes.expectedTravelTimeFromMatchToEndOfStopPath(match);
+
+ long predictionTime = avlTime + expectedTravelTimeFromMatchToEndOfStopPath(avlReport, match);
// Determine if vehicle is so late that predictions for subsequent
// trips should be marked as uncertain given that another vehicle
@@ -336,16 +411,26 @@ public List generate(VehicleState vehicleState) {
// For filtering out predictions that are before now, which can
// happen for schedule based predictions
long now = Core.getInstance().getSystemTime();
+
+ //indices.incrementStopPath(predictionTime);
+ Integer tripCounter = new Integer(vehicleState.getTripCounter());
+
+ Map filteredPredictions = new HashMap();
+
+
// Continue through block until end of block or limit on how far
// into the future should generate predictions reached.
- while (schedBasedPreds
- || predictionTime <
- avlTime + maxPredictionsTimeSecs.getValue() * Time.MS_PER_SEC) {
+ // also, if configured, once we exceed maxPredictionsTimeSec continue to end of trip
+ while (schedBasedPreds || isIndexWithinRange(indices, avlTime, predictionTime)) {
// Keep track of whether prediction is affected by layover
// scheduled departure time since those predictions might not
// be a accurate. Once a layover encountered then all subsequent
// predictions are affected by a layover.
+
+ // Increment indices so can generate predictions for next path
+
+
if (indices.isWaitStop())
affectedByWaitStop = true;
@@ -353,19 +438,41 @@ public List generate(VehicleState vehicleState) {
lateSoMarkSubsequentTripsAsUncertain
&& indices.getTripIndex() > currentTripIndex;
+
+ Integer delay = RealTimeSchedAdhProcessor.generateEffectiveScheduleDifference(vehicleState).getTemporalDifference()/1000;
+
// Determine the new prediction
IpcPrediction predictionForStop = generatePredictionForStop(avlReport,
indices, predictionTime,
useArrivalPreds, affectedByWaitStop,
- vehicleState.isDelayed(), lateSoMarkAsUncertain);
+ vehicleState.isDelayed(), lateSoMarkAsUncertain, tripCounter, delay);
+
+
+ if((predictionForStop.getPredictionTime()-Core.getInstance().getSystemTime())0)
+ {
+ if(HoldingTimeGeneratorFactory.getInstance()!=null)
+ {
+ HoldingTime holdingTime = HoldingTimeGeneratorFactory.getInstance().generateHoldingTime(vehicleState, predictionForStop);
+ if(holdingTime!=null)
+ {
+ //HoldingTimeCache.getInstance().putHoldingTimeExlusiveByStop(holdingTime, new Date(Core.getInstance().getSystemTime()));
+ HoldingTimeCache.getInstance().putHoldingTime(holdingTime);
+ vehicleState.setHoldingTime(holdingTime);
+ }
+ }
+ }
+
logger.debug("For vehicleId={} generated prediction {}",
vehicleState.getVehicleId(), predictionForStop);
+
// If prediction ended up being too far in the future (which can
// happen if it is a departure prediction where the time at the
// stop is added to the arrival time) then don't add the prediction
// and break out of the loop.
if (!schedBasedPreds
+ && !terminatePredictionsAtTripEnd.getValue()
&& predictionForStop.getPredictionTime() > avlTime
+ maxPredictionsTimeSecs.getValue() * Time.MS_PER_SEC)
break;
@@ -375,7 +482,17 @@ public List generate(VehicleState vehicleState) {
// first stop of the trip
boolean lastStopOfNonSchedBasedTrip =
indices.getBlock().isNoSchedule() && indices.atEndOfTrip();
+
+ // This is incremented each time the prediction starts a new trip.
+ // The first prediction for the start of a new trip is used as the
+ // start time for a frequency based service
+ if(lastStopOfNonSchedBasedTrip) {
+ tripCounter++;
+ vehicleState.putTripStartTime(tripCounter, predictionForStop.getPredictionTime());
+ //break;
+ }
+
// The prediction is not too far into the future. Add it to the
// list of predictions to be returned. But only do this if
// it is not last stop of non-schedule based trip since that is a
@@ -384,8 +501,34 @@ public List generate(VehicleState vehicleState) {
// Can get predictions in the past for schedule based predictions.
if (!lastStopOfNonSchedBasedTrip
&& predictionForStop.getPredictionTime() > now) {
- newPredictions.add(predictionForStop);
- logger.info("Generated prediction {}", predictionForStop);
+
+
+ logger.info("Generated prediction {} based on avlreport {}.", predictionForStop, avlReport);
+
+
+ if(indices.atEndOfTrip() || indices.atBeginningOfTrip()){
+ // Deals with case where a vehicle transitions from one trip to another and the lastStop then becomes the firstSTop
+ // This occassionally leads to duplicate predictions. This works around the problem by creating a hash of predictions
+ // that have the same Prediction information but different trips
+ int predictionKey = lastStopPredictionHash(predictionForStop);
+ if(filteredPredictions.containsKey(predictionKey) &&
+ filteredPredictions.get(predictionKey) != null){
+ if(predictionForStop.getTrip().getStartTime() > filteredPredictions.get(predictionKey).getTrip().getStartTime()){
+ logger.debug("Found multiple predictions for Prediction with routeId={}, stopId={}, and vehicleId={} ",
+ predictionForStop.getRouteId(), predictionForStop.getStopId(), predictionForStop.getVehicleId());
+ filteredPredictions.put(predictionKey, predictionForStop);
+ }
+ }
+ else{
+ filteredPredictions.put(predictionKey, predictionForStop);
+ }
+ }
+ else{
+ newPredictions.add(predictionForStop);
+ }
+
+ logger.info("Generated prediction {}", predictionForStop);
+
}
// Determine prediction time for the departure. For layovers
@@ -395,13 +538,27 @@ public List generate(VehicleState vehicleState) {
// getActualPredictionTime() instead of getPredictionTime() to
// handle situations where want to display to the user for wait
// stops schedule times instead of the calculated prediction time.
- predictionTime = predictionForStop.getActualPredictionTime();
+ predictionTime = predictionForStop.getActualPredictionTime();
if (predictionForStop.isArrival())
- predictionTime += indices.getStopTimeForPath();
-
- // Increment indices so can generate predictions for next path
- indices.incrementStopPath(predictionTime);
-
+ {
+ predictionTime += getStopTimeForPath(indices, avlReport, vehicleState);
+ /* TODO this is where we should take account of holding time */
+ if(useHoldingTimeInPrediction.getValue() && HoldingTimeGeneratorFactory.getInstance()!=null)
+ {
+ HoldingTime holdingTime = HoldingTimeGeneratorFactory.getInstance().generateHoldingTime(vehicleState, predictionForStop);
+
+ if(holdingTime!=null)
+ {
+ long holdingTimeMsec = holdingTime.getHoldingTime().getTime()-holdingTime.getArrivalTime().getTime();
+ if(holdingTimeMsec>indices.getStopTimeForPath())
+ {
+ predictionTime += holdingTime.getHoldingTime().getTime()-holdingTime.getArrivalTime().getTime();
+ }
+ }
+
+ }
+ }
+ indices.incrementStopPath(predictionTime);
// If reached end of block then done
if (indices.pastEndOfBlock(predictionTime)) {
logger.debug("For vehicleId={} reached end of block when " +
@@ -409,14 +566,165 @@ public List generate(VehicleState vehicleState) {
vehicleState.getVehicleId());
break;
}
-
+ boolean isCircuitRoute=true;
// Add in travel time for the next path to get to predicted
// arrival time of this stop
- predictionTime += indices.getTravelTimeForPath();
- }
+ if (!lastStopOfNonSchedBasedTrip && isCircuitRoute) {
+ predictionTime += getTravelTimeForPath(indices, avlReport, vehicleState);
+ }
+ } // end while loop
+ for(IpcPrediction prediction : filteredPredictions.values()){
+ newPredictions.add(prediction);
+
+
+ }
+
// Return the results
return newPredictions;
}
+
+ private boolean isIndexWithinRange(Indices indices, long avlTime, long predictionTime) {
+ boolean withinTimeBounds = predictionTime < avlTime + maxPredictionsTimeSecs.getValue() * Time.MS_PER_SEC;
+ if (getTerminatePredictionsAtTripEnd()) {
+ // per additional config, go to end of trip even if exceeded time range
+ if (!withinTimeBounds) {
+ if (indices.atEndOfTrip())
+ return false;
+ }
+ return true; // go to the end of the trip even though we have exceeded time
+ }
+
+ return withinTimeBounds;
+ }
+
+
+ public long getTravelTimeForPath(Indices indices, AvlReport avlReport, VehicleState vehicleState)
+ {
+ //logger.debug("Using transiTime default algorithm for travel time prediction : " + indices + " Value: "+indices.getTravelTimeForPath());
+ if(storeTravelTimeStopPathPredictions.getValue())
+ {
+ PredictionForStopPath predictionForStopPath=new PredictionForStopPath(vehicleState.getVehicleId(), new Date(Core.getInstance().getSystemTime()) , new Double(new Long(indices.getTravelTimeForPath()).intValue()), indices.getTrip().getId(), indices.getStopPathIndex(), "TRANSITIME DEFAULT", true, null);
+ Core.getInstance().getDbLogger().add(predictionForStopPath);
+ StopPathPredictionCacheFactory.getInstance().putPrediction(predictionForStopPath);
+ }
+ return indices.getTravelTimeForPath();
+ }
+
+
+ public long getStopTimeForPath(Indices indices, AvlReport avlReport, VehicleState vehicleState) {
+ long prediction=TravelTimes.getInstance().expectedStopTimeForStopPath(indices);
+ //logger.debug("Using transiTime default algorithm for stop time prediction : "+indices + " Value: "+prediction);
+ return prediction;
+ }
+
+ public long expectedTravelTimeFromMatchToEndOfStopPath(AvlReport avlReport, SpatialMatch match)
+ {
+ TravelTimes travelTimes = TravelTimes.getInstance();
+ return travelTimes.expectedTravelTimeFromMatchToEndOfStopPath(match);
+ }
+
+
+ private int lastStopPredictionHash(IpcPrediction prediction){
+ final int prime = 31;
+ int result = 1;
+
+ result = prime * result
+ + ((prediction.getBlockId() == null) ? 0 : prediction.getBlockId().hashCode());
+ result = prime * result
+ + ((prediction.getVehicleId() == null) ? 0 : prediction.getVehicleId().hashCode());
+ result = prime * result
+ + ((prediction.getStopId() == null) ? 0 : prediction.getStopId().hashCode());
+ result = prime * result
+ + ((prediction.getRouteId() == null) ? 0 : prediction.getRouteId().hashCode());
+ result = prime * result + Long.valueOf(prediction.getPredictionTime()).hashCode();
+
+ return result;
+ }
+
+ @Override
+ public boolean hasDataForPath(Indices indices, AvlReport avlReport) {
+ return true;
+ }
+
+ /**
+ * for the prediction calculate the scheduled arrival and return that
+ * as millseconds since epoch
+ * @param prediction
+ * @return
+ */
+ public static Long getScheduledArrivalTime(IpcPrediction prediction) {
+ long tripStartTime = prediction.getTripStartEpochTime();
+ long serviceDay = Time.getStartOfDay(new Date(tripStartTime));
+
+ int index = prediction.getGtfsStopSeq();
+ if (index < prediction.getTrip().getScheduleTimes().size()) {
+ long epochInMillis = serviceDay
+ + prediction.getTrip().getScheduleTimes().get(index).getTime()
+ * Time.MS_PER_SEC;
+ return epochInMillis;
+ }
+ return null; // we may be unscheduled or at end of trip
+ }
+
+ /**
+ * Test if vehicle is running early (negative schedule deviation)
+ * such that it has already served a stop that is scheduled in the future.
+ * GTFS-RT spec wants these prediction although per the codebase they are
+ * considered historical
+ * @param currentPrediction prediction to consider
+ * @param currentTime reference time for comparison
+ * @return true if prediction is in past and stop is scheduled in future
+ */
+ public static boolean isHistoricalPredictionForFutureStop(IpcPrediction currentPrediction,
+ long currentTime) {
+
+ // is prediction in past
+ if (currentPrediction.getPredictionTime() < currentTime) {
+ Long scheduledArrivalTime = getScheduledArrivalTime(currentPrediction);
+ // if we have a schedule associated with prediction
+ if (scheduledArrivalTime != null) {
+ long scheduleDeviation = currentPrediction.getPredictionTime() - scheduledArrivalTime;
+ long deltaArrivalFuture = scheduledArrivalTime - currentTime;
+ if (scheduleDeviation < 0 // bus running early
+ && deltaArrivalFuture > 0 // stop in future
+ && deltaArrivalFuture < 60 * Time.MS_PER_MIN) { // stop not too far in future
+ logger.debug("holding onto prediction for vehicle " + currentPrediction.getVehicleId()
+ + " on trip " + currentPrediction.getTripId()
+ + " with arrival " + new Date(scheduledArrivalTime)
+ + " and age " + Time.elapsedTimeStr(currentTime - currentPrediction.getAvlTime())
+ + " but prediction " + new Date(currentPrediction.getPredictionTime())
+ + "(" + currentPrediction.getPredictionTime() + ")");
+
+ long age = currentTime - currentPrediction.getAvlTime();
+ // special case: prune extremely old data relative to AVL age
+ if (age > maxAgeOfHistoricalPredictions.getValue() * Time.MS_PER_MIN // past config threshold
+ && currentPrediction.getDelay() != null // trip has schedule deviation
+ && age > currentPrediction.getDelay() * -Time.MS_PER_SEC) { // record is older than schedule deviation
+ logger.debug("expiring old prediction for vehicle "
+ + currentPrediction.getVehicleId() + " that is "
+ + Time.elapsedTimeStr(currentTime - currentPrediction.getAvlTime())
+ + " old");
+ return false;
+ } else {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+
+ /**
+ * lazy load Cloudwatch Monitoring service.
+ * @return
+ */
+ protected MonitoringService getMonitoring() {
+ if (monitoring == null)
+ monitoring = MonitoringService.getInstance();
+ return monitoring;
+ }
+
}
diff --git a/transitime/src/main/java/org/transitime/core/PredictionGeneratorFactory.java b/transitclock/src/main/java/org/transitclock/core/PredictionGeneratorFactory.java
similarity index 81%
rename from transitime/src/main/java/org/transitime/core/PredictionGeneratorFactory.java
rename to transitclock/src/main/java/org/transitclock/core/PredictionGeneratorFactory.java
index 01dc7d04e..e79e5a7e6 100644
--- a/transitime/src/main/java/org/transitime/core/PredictionGeneratorFactory.java
+++ b/transitclock/src/main/java/org/transitclock/core/PredictionGeneratorFactory.java
@@ -15,15 +15,15 @@
* along with Transitime.org . If not, see .
*/
-package org.transitime.core;
+package org.transitclock.core;
-import org.transitime.config.StringConfigValue;
-import org.transitime.utils.ClassInstantiator;
+import org.transitclock.config.StringConfigValue;
+import org.transitclock.utils.ClassInstantiator;
/**
* For instantiating a PredictionGenerator object that generates predictions
* when a new match is generated for a vehicle. The class to be instantiated can
- * be set using the config variable transitime.core.predictionGeneratorClass
+ * be set using the config variable transitclock.core.predictionGeneratorClass
*
* @author SkiBu Smith
*
@@ -32,8 +32,8 @@ public class PredictionGeneratorFactory {
// The name of the class to instantiate
private static StringConfigValue className =
- new StringConfigValue("transitime.core.predictionGeneratorClass",
- "org.transitime.core.PredictionGeneratorDefaultImpl",
+ new StringConfigValue("transitclock.core.predictionGeneratorClass",
+ "org.transitclock.core.PredictionGeneratorDefaultImpl",
"Specifies the name of the class used for generating " +
"prediction data.");
diff --git a/transitime/src/main/java/org/transitime/core/RealTimeSchedAdhProcessor.java b/transitclock/src/main/java/org/transitclock/core/RealTimeSchedAdhProcessor.java
similarity index 64%
rename from transitime/src/main/java/org/transitime/core/RealTimeSchedAdhProcessor.java
rename to transitclock/src/main/java/org/transitclock/core/RealTimeSchedAdhProcessor.java
index 245ab23ea..276ace35f 100644
--- a/transitime/src/main/java/org/transitime/core/RealTimeSchedAdhProcessor.java
+++ b/transitclock/src/main/java/org/transitclock/core/RealTimeSchedAdhProcessor.java
@@ -15,14 +15,18 @@
* along with Transitime.org . If not, see .
*/
-package org.transitime.core;
+package org.transitclock.core;
import java.util.Date;
+
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.transitime.applications.Core;
-import org.transitime.db.structs.ScheduleTime;
-import org.transitime.db.structs.Trip;
+import org.transitclock.applications.Core;
+import org.transitclock.db.structs.Block;
+import org.transitclock.db.structs.ScheduleTime;
+import org.transitclock.db.structs.StopPath;
+import org.transitclock.db.structs.Trip;
+import org.transitclock.utils.Time;
/**
* For determining the real-time schedule adherence for a predictable vehicle.
@@ -162,4 +166,83 @@ public static TemporalDifference generate(VehicleState vehicleState) {
vehicleId, scheduleAdherence, avlTime, scheduleTime);
return scheduleAdherence;
}
+
+ /**
+ * We define effective schedule time as where the bus currently falls in the schedule based on
+ * its current position.
+ */
+ public static TemporalDifference generateEffectiveScheduleDifference(VehicleState vehicleState) {
+ TemporalMatch match = vehicleState.getMatch();
+ Trip trip = match.getTrip();
+ long avlTime = match.getAvlTime();
+ String vehicleId = vehicleState.getVehicleId();
+
+ int nextStopPathIndex = match.getStopPathIndex();
+ int previousStopPathIndex = nextStopPathIndex -1;
+
+ if (previousStopPathIndex < 0) {
+ // we are either before the trip or at the first stop (layover)
+ Long departureEpoch = Core.getInstance().getTime().getEpochTime(trip.getScheduleTime(0).getTime(), avlTime);
+
+
+ // if trip has not started yet schedule difference = 0,
+ // unless previous trip is active in which case schedule difference=early
+ long difference = avlTime - departureEpoch;
+
+ if (difference < 0) {
+ difference = 0;
+
+ int tripIndex = match.getTripIndex();
+ if (tripIndex > 0) {
+ Trip prevTrip = match.getBlock().getTrip(tripIndex - 1);
+ Long epochEndTime = Core.getInstance().getTime().getEpochTime(prevTrip.getEndTime(), avlTime);
+
+ difference = Math.min(0, avlTime - epochEndTime);
+
+ logger.debug("vehicleId {} has schedDev before trip set by previous trip of {}",
+ vehicleId,
+ difference);
+ }
+ }
+
+
+ logger.debug("vehicleId {} has schedDev before trip start of {}",
+ vehicleId,
+ difference);
+
+ return new TemporalDifference(difference);
+ }
+ if (match.isAtStop()) {
+ // If at stop, nextStopPathIndex can be for current stop or next stop depending
+ // on match.atEndOfPathStop()
+ int departureSecs = match.getAtStop().getScheduleTime().getTime();
+ Long departureEpoch = Core.getInstance().getTime()
+ .getEpochTime(departureSecs, avlTime);
+ if (departureEpoch > avlTime) {
+ logger.debug("vehicleId {} has schedDev at stop of 0",
+ vehicleId);
+ }
+ logger.debug("vehicleId {} has schedDev at stop of {}",
+ vehicleId,
+ (avlTime - departureEpoch));
+ return new TemporalDifference(avlTime - departureEpoch);
+ }
+
+ // we must be between stops, interpolate effective schedule
+ long fromStopTimeSecs = trip.getScheduleTime(previousStopPathIndex).getTime();
+ long toStopTimeSecs = trip.getScheduleTime(nextStopPathIndex).getTime();
+ long pathTime = toStopTimeSecs - fromStopTimeSecs;
+
+ double ratio = match.getDistanceAlongStopPath() / match.getStopPath().getLength();
+
+ int effectiveStopTimeSec = (int) (fromStopTimeSecs + (pathTime * ratio));
+ Long effectiveScheduleTimeEpoch = Core.getInstance().getTime().getEpochTime(effectiveStopTimeSec, avlTime);
+
+ logger.debug("vehicleId {} has interpolated schedDev of {}, avlTime={}, effective={}",
+ vehicleId,
+ Time.elapsedTimeStr(avlTime - effectiveScheduleTimeEpoch),
+ Time.timeStr(avlTime),
+ Time.timeStr(effectiveScheduleTimeEpoch));
+ return new TemporalDifference(avlTime - effectiveScheduleTimeEpoch);
+ }
}
diff --git a/transitclock/src/main/java/org/transitclock/core/RunTimesGeneratorFactory.java b/transitclock/src/main/java/org/transitclock/core/RunTimesGeneratorFactory.java
new file mode 100644
index 000000000..8154d8c58
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/core/RunTimesGeneratorFactory.java
@@ -0,0 +1,55 @@
+/*
+ * 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 .
+ */
+
+package org.transitclock.core;
+
+import org.transitclock.config.StringConfigValue;
+import org.transitclock.core.reporting.RunTimeGenerator;
+import org.transitclock.utils.ClassInstantiator;
+
+/**
+ * For instantiating a RunTimeGenerator object that generates runtime info when
+ * a new match is generated for a vehicle. The class to be instantiated can be
+ * set using the config variable transitclock.core.runTimesGeneratorClass
+ *
+ * @author SkiBu Smith
+ *
+ */
+public class RunTimesGeneratorFactory {
+
+ // The name of the class to instantiate
+ private static StringConfigValue className =
+ new StringConfigValue("transitclock.core.runTimesGeneratorClass",
+ "org.transitclock.core.reporting.RunTimeGenerator",
+ "Specifies the name of the class used for generating " +
+ "runtimes data.");
+
+ private static RunTimeGenerator singleton = null;
+
+ /********************** Member Functions **************************/
+
+ public static RunTimeGenerator getInstance() {
+ // If the PredictionGenerator hasn't been created yet then do so now
+ if (singleton == null) {
+ singleton = ClassInstantiator.instantiate(className.getValue(),
+ RunTimeGenerator.class);
+ }
+
+ return singleton;
+ }
+
+}
diff --git a/transitclock/src/main/java/org/transitclock/core/ServiceType.java b/transitclock/src/main/java/org/transitclock/core/ServiceType.java
new file mode 100644
index 000000000..bb609bd48
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/core/ServiceType.java
@@ -0,0 +1,5 @@
+package org.transitclock.core;
+
+public enum ServiceType {
+ WEEKDAY, SATURDAY, SUNDAY;
+}
diff --git a/transitime/src/main/java/org/transitime/core/ServiceUtils.java b/transitclock/src/main/java/org/transitclock/core/ServiceUtils.java
old mode 100644
new mode 100755
similarity index 75%
rename from transitime/src/main/java/org/transitime/core/ServiceUtils.java
rename to transitclock/src/main/java/org/transitclock/core/ServiceUtils.java
index 1fe915341..b14a42f67
--- a/transitime/src/main/java/org/transitime/core/ServiceUtils.java
+++ b/transitclock/src/main/java/org/transitclock/core/ServiceUtils.java
@@ -14,25 +14,30 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.core;
+package org.transitclock.core;
+import java.time.DayOfWeek;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.GregorianCalendar;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
+import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.transitime.applications.Core;
-import org.transitime.config.IntegerConfigValue;
-import org.transitime.db.structs.Agency;
-import org.transitime.db.structs.Calendar;
-import org.transitime.db.structs.CalendarDate;
-import org.transitime.gtfs.DbConfig;
-import org.transitime.utils.Time;
+import org.transitclock.applications.Core;
+import org.transitclock.config.IntegerConfigValue;
+import org.transitclock.db.structs.*;
+import org.transitclock.gtfs.DbConfig;
+import org.transitclock.utils.Time;
/**
* For working with service types, such as determining serviceId or
@@ -49,7 +54,7 @@ public class ServiceUtils {
private static IntegerConfigValue minutesIntoMorningToIncludePreviousServiceIds =
new IntegerConfigValue(
- "transitime.service.minutesIntoMorningToIncludePreviousServiceIds",
+ "transitclock.service.minutesIntoMorningToIncludePreviousServiceIds",
4*Time.HOUR_IN_MINS,
"Early in the morning also want to include at service IDs "
+ "for previous day since a block might have started on "
@@ -69,11 +74,13 @@ public class ServiceUtils {
* @param timezoneName See http://en.wikipedia.org/wiki/List_of_tz_zones
*/
public ServiceUtils(DbConfig dbConfig) {
+
Agency agency = dbConfig.getFirstAgency();
this.calendar =
- agency != null ?
+ agency != null ?
new GregorianCalendar(agency.getTimeZone())
: new GregorianCalendar();
+
this.dbConfig = dbConfig;
}
@@ -164,6 +171,34 @@ private List getActiveCalendars(Date epochTime) {
return activeCalendarList;
}
+ Map> serviceIdsForDate = new HashMap>();
+ /**
+ * Caching version fo getServiceIdsForDay. Assumes epochTime can be distilled to
+ * a serviceDate. Note that boundary conditions may exist where serviceDate guess is wrong.
+ *
+ * TODO as is this cache will grow without bounds, but the data should be small
+ *
+ */
+ public List getServiceIdsForDay(Date epochTime) {
+ Date serviceDate = getStartOfDay(epochTime);
+ if (serviceIdsForDate.containsKey(serviceDate)) {
+ return serviceIdsForDate.get(serviceDate);
+ }
+ List serviceIds = getServiceIdsForDayNoCache(serviceDate);
+ serviceIdsForDate.put(serviceDate, serviceIds);
+ return serviceIds;
+ }
+
+ private Date getStartOfDay(Date epochTime) {
+ java.util.Calendar c = java.util.Calendar.getInstance();
+ c.setTime(epochTime);
+ c.set(java.util.Calendar.HOUR, 0);
+ c.set(java.util.Calendar.MINUTE, 0);
+ c.set(java.util.Calendar.SECOND, 0);
+ c.set(java.util.Calendar.MILLISECOND, 0);
+ return c.getTime();
+ }
+
/**
* Determines list of current service IDs for the specified time. These
* service IDs designate which block assignments are currently active.
@@ -175,7 +210,7 @@ private List getActiveCalendars(Date epochTime) {
* The current time that determining service IDs for
* @return List of service IDs that are active for the specified time.
*/
- public List getServiceIdsForDay(Date epochTime) {
+ public List getServiceIdsForDayNoCache(Date epochTime) {
List serviceIds = new ArrayList();
// Make sure haven't accidentally let all calendars expire
@@ -202,7 +237,7 @@ public List getServiceIdsForDay(Date epochTime) {
// Go through calendar_dates to see if there is special service for
// this date. Add or remove the special service.
- List calendarDatesForNow =
+ List calendarDatesForNow =
dbConfig.getCalendarDates(epochTime);
if (calendarDatesForNow != null) {
for (CalendarDate calendarDate : calendarDatesForNow) {
@@ -220,7 +255,8 @@ public List getServiceIdsForDay(Date epochTime) {
epochTime, serviceIds);
}
}
-
+ logger.info("Finished adding calendar dates");
+
// Return the results
return serviceIds;
}
@@ -323,4 +359,83 @@ public List getCurrentCalendars(long epochTime) {
// Return the results
return currentCalendars;
}
+
+ public ServiceType getServiceTypeForTrip(Date avlTime, Integer tripStartTime, String serviceId){
+ if(avlTime != null){
+ long updatedTime = avlTime.getTime();
+ if(tripStartTime != null){
+ if(tripStartTime < (TimeUnit.HOURS.toSeconds(6))){
+ updatedTime += TimeUnit.HOURS.toMillis(6);
+ } else if(tripStartTime > (TimeUnit.HOURS.toSeconds(18))){
+ updatedTime -= TimeUnit.HOURS.toMillis(6);
+ }
+ }
+ DayOfWeek dayOfWeek = Instant.ofEpochMilli(updatedTime)
+ .atZone(ZoneId.systemDefault())
+ .toLocalDate().getDayOfWeek();
+
+ ServiceType serviceType = getServiceType(dayOfWeek);
+
+ if(isServiceIdValidForDate(serviceType, serviceId, new Date(updatedTime))){
+ return serviceType;
+ }
+ }
+
+ return null;
+
+ }
+
+ private boolean isServiceIdValidForDate(ServiceType serviceType, String serviceId, Date date){
+ Calendar calendar = dbConfig.getCalendarByServiceId(serviceId);
+ if(isServiceTypeActiveForServiceCal(serviceType, calendar)){
+ return true;
+ } else{
+ List calendarDatesForNow = dbConfig.getCalendarDates(date);
+ if (calendarDatesForNow != null) {
+ for (CalendarDate calendarDate : calendarDatesForNow) {
+ // Handle special service for this date
+ if (calendarDate.addService() && calendarDate.getServiceId().equals(serviceId)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+
+ private ServiceType getServiceType(DayOfWeek dayOfWeek){
+ switch (dayOfWeek) {
+ case MONDAY:
+ case TUESDAY:
+ case WEDNESDAY:
+ case THURSDAY:
+ case FRIDAY:
+ return ServiceType.WEEKDAY;
+ case SATURDAY:
+ return ServiceType.SATURDAY;
+ case SUNDAY:
+ return ServiceType.SUNDAY;
+ default:
+ return null;
+ }
+ }
+
+ private boolean isServiceTypeActiveForServiceCal(ServiceType serviceType, Calendar calendar){
+ if(serviceType.equals(ServiceType.SUNDAY) && calendar.getSunday()){
+ return Boolean.TRUE;
+ } else if(serviceType.equals(ServiceType.SATURDAY) && calendar.getSaturday()) {
+ return Boolean.TRUE;
+ } else if(serviceType.equals(ServiceType.WEEKDAY) && (
+ calendar.getMonday() ||
+ calendar.getTuesday() ||
+ calendar.getWednesday() ||
+ calendar.getThursday() ||
+ calendar.getFriday()
+ )){
+ return Boolean.TRUE;
+ }
+ return Boolean.FALSE;
+ }
+
}
diff --git a/transitime/src/main/java/org/transitime/core/SpatialMatch.java b/transitclock/src/main/java/org/transitclock/core/SpatialMatch.java
similarity index 93%
rename from transitime/src/main/java/org/transitime/core/SpatialMatch.java
rename to transitclock/src/main/java/org/transitclock/core/SpatialMatch.java
index a2138d2da..c965d12d2 100644
--- a/transitime/src/main/java/org/transitime/core/SpatialMatch.java
+++ b/transitclock/src/main/java/org/transitclock/core/SpatialMatch.java
@@ -14,21 +14,23 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.core;
+package org.transitclock.core;
import java.util.List;
import org.slf4j.LoggerFactory;
+import org.transitclock.applications.Core;
+import org.transitclock.db.structs.Block;
+import org.transitclock.db.structs.Location;
+import org.transitclock.db.structs.Route;
+import org.transitclock.db.structs.ScheduleTime;
+import org.transitclock.db.structs.StopPath;
+import org.transitclock.db.structs.Trip;
+import org.transitclock.db.structs.Vector;
+import org.transitclock.db.structs.VectorWithHeading;
+import org.transitclock.utils.Geo;
+import org.transitclock.utils.Time;
import org.slf4j.Logger;
-import org.transitime.applications.Core;
-import org.transitime.db.structs.Block;
-import org.transitime.db.structs.Route;
-import org.transitime.db.structs.StopPath;
-import org.transitime.db.structs.ScheduleTime;
-import org.transitime.db.structs.Trip;
-import org.transitime.db.structs.Vector;
-import org.transitime.utils.Geo;
-import org.transitime.utils.Time;
/**
* Describes where an AVL report matches to an assignment spatially.
@@ -46,6 +48,7 @@ public class SpatialMatch {
protected final double distanceToSegment;
protected final double distanceAlongSegment;
protected final VehicleAtStopInfo atStop;
+ protected final Location predictedLocation;
private static final Logger logger =
LoggerFactory.getLogger(SpatialMatch.class);
@@ -66,6 +69,39 @@ public SpatialMatch(long avlTime, Block block,
// Determine whether at stop
this.atStop = atStop();
+ this.predictedLocation = computeLocation();
+ }
+
+ /**
+ * based on the current trip/stop path/sgement compute the predicted
+ * vehicle location.
+ */
+ private Location computeLocation() {
+ Trip trip = block.getTrip(tripIndex);
+ if (trip != null) {
+ StopPath stopPath = trip.getStopPath(stopPathIndex);
+ if (stopPath != null) {
+ VectorWithHeading vector = stopPath.getSegmentVector(segmentIndex);
+ if (vector != null) {
+ return vector.locAlongVector(distanceAlongSegment);
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * based on the indices and distance along segment compute the predicted
+ * vehicle location.
+ */
+ public Location computeLocation(Indices i, double distanceAlongSegment) {
+ if (i != null) {
+ VectorWithHeading segment = i.getSegment();
+ if (segment != null) {
+ return segment.locAlongVector(distanceAlongSegment);
+ }
+ }
+ return null;
}
/**
@@ -107,6 +143,8 @@ public SpatialMatch(SpatialMatch toCopy, Trip newTrip) {
this.tripIndex,
toCopy.atStop().getStopPathIndex());
}
+ // recomupte predictedLocation for above reasons as well
+ this.predictedLocation = toCopy.computeLocation(toCopy.getIndices(), toCopy.distanceAlongSegment);
}
/**
@@ -128,6 +166,7 @@ public SpatialMatch(SpatialMatch toCopy, Indices newIndices,
this.distanceToSegment = toCopy.distanceToSegment;
this.distanceAlongSegment = distanceAlongSegment;
this.atStop = toCopy.atStop;
+ this.predictedLocation = computeLocation(newIndices, distanceAlongSegment);
}
/**
@@ -144,6 +183,7 @@ protected SpatialMatch(SpatialMatch toCopy) {
this.distanceToSegment = toCopy.distanceToSegment;
this.distanceAlongSegment = toCopy.distanceAlongSegment;
this.atStop = toCopy.atStop;
+ this.predictedLocation = toCopy.predictedLocation;
}
/**
@@ -524,6 +564,7 @@ && getDistanceAlongStopPath() <
* @return true if this spatial match is for a layover
*/
public boolean isLayover() {
+ if (block.isNoSchedule()) return false; // Frequency-based unscheduled blocks can't have layovers
return block.isLayover(tripIndex, stopPathIndex);
}
@@ -548,10 +589,15 @@ public int getScheduledWaitStopTimeSecs() {
try {
ScheduleTime scheduleTime =
block.getScheduleTime(tripIndex, stopPathIndex);
+ if (scheduleTime == null) {
+ // prevent nullpointer
+ logger.error("no scheduled wait stop time for {} {}", tripIndex, stopPathIndex);
+ return -1;
+ }
return scheduleTime.getDepartureTime();
} catch (Exception e) {
logger.error("Tried to get wait stop time for a stop that didn't "
- + "have one. {}", this);
+ + "have one. {} {}", this, e, e);
return -1;
}
}
@@ -955,5 +1001,9 @@ public boolean isAtStop(int tripIndex, int stopPathIndex) {
public long getAvlTime() {
return avlTime;
}
+
+ public Location getLocation() {
+ return predictedLocation;
+ }
}
diff --git a/transitime/src/main/java/org/transitime/core/SpatialMatcher.java b/transitclock/src/main/java/org/transitclock/core/SpatialMatcher.java
similarity index 88%
rename from transitime/src/main/java/org/transitime/core/SpatialMatcher.java
rename to transitclock/src/main/java/org/transitclock/core/SpatialMatcher.java
index 9cee01eab..d5e74290d 100644
--- a/transitime/src/main/java/org/transitime/core/SpatialMatcher.java
+++ b/transitclock/src/main/java/org/transitclock/core/SpatialMatcher.java
@@ -14,8 +14,9 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.core;
+package org.transitclock.core;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
@@ -24,18 +25,19 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.transitime.configData.AvlConfig;
-import org.transitime.configData.CoreConfig;
-import org.transitime.db.structs.AvlReport;
-import org.transitime.db.structs.Block;
-import org.transitime.db.structs.Extent;
-import org.transitime.db.structs.Location;
-import org.transitime.db.structs.Route;
-import org.transitime.db.structs.StopPath;
-import org.transitime.db.structs.Trip;
-import org.transitime.db.structs.VectorWithHeading;
-import org.transitime.utils.Geo;
-import org.transitime.utils.Time;
+import org.transitclock.config.BooleanConfigValue;
+import org.transitclock.configData.AvlConfig;
+import org.transitclock.configData.CoreConfig;
+import org.transitclock.db.structs.AvlReport;
+import org.transitclock.db.structs.Block;
+import org.transitclock.db.structs.Extent;
+import org.transitclock.db.structs.Location;
+import org.transitclock.db.structs.Route;
+import org.transitclock.db.structs.StopPath;
+import org.transitclock.db.structs.Trip;
+import org.transitclock.db.structs.VectorWithHeading;
+import org.transitclock.utils.Geo;
+import org.transitclock.utils.Time;
/**
* For determining possible spatial matches. A spatial match is when the AVL
@@ -53,6 +55,8 @@ public class SpatialMatcher {
// For keeping track of whether getting closer or further away
private double previousDistanceToSegment = Double.MAX_VALUE;
+
+ private int previousSegmentIndex = -1;
// For keeping track of potential matches where heading and
// distance to segment are acceptable.
@@ -69,6 +73,7 @@ public enum MatchingType {STANDARD_MATCHING, AUTO_ASSIGNING_MATCHING};
private static final Logger logger =
LoggerFactory.getLogger(SpatialMatcher.class);
+ private static BooleanConfigValue spatialMatchToLayoversAllowedForAutoAssignment=new BooleanConfigValue("transitclock.core.spatialMatchToLayoversAllowedForAutoAssignment", false, "Allow auto assigner consider spatial matches to layovers. Experimental.");
/********************** Member Functions **************************/
/**
@@ -116,16 +121,6 @@ private List getSpatialMatchesForTrip(AvlReport avlReport,
// The matches to be returned
List spatialMatches = new ArrayList();
- // Looking at each stop path for a trip is pretty costly. So first
- // see if the AVL report is even within the trip pattern. If not then
- // can return right away.
- Extent tripPatternExtent = trip.getTripPattern().getExtent();
- double allowableDistance =
- getMaxAllowableDistanceFromSegment(trip.getRoute(), matchingType);
- if (!tripPatternExtent.isWithinDistance(avlReport.getLocation(),
- allowableDistance))
- return spatialMatches;
-
// Start looking for matches at the beginning of the trip.
Indices indices = new Indices(block, block.getTripIndex(trip),
0, // stopPathIndex
@@ -406,14 +401,14 @@ public static List getSpatialMatchesForAutoAssigning(
MatchingType.AUTO_ASSIGNING_MATCHING);
// Filter out the ones that are layovers
- List spatialMatchesWithoutLayovers =
+ List spatialMatches =
new ArrayList();
for (SpatialMatch spatialMatch : allSpatialMatches) {
- if (!spatialMatch.isLayover())
- spatialMatchesWithoutLayovers.add(spatialMatch);
+ if (!spatialMatch.isLayover() || spatialMatchToLayoversAllowedForAutoAssignment.getValue())
+ spatialMatches.add(spatialMatch);
}
- return spatialMatchesWithoutLayovers;
+ return spatialMatches;
}
/**
@@ -453,6 +448,8 @@ private double getMaxAllowableDistanceFromSegment(Route route,
*/
private double getMaxAllowableDistanceFromSegment(Indices indices,
MatchingType matchingType) {
+ if(indices.getStopPath().getMaxDistance()!=null)
+ return indices.getStopPath().getMaxDistance();
Route route = indices.getRoute();
return getMaxAllowableDistanceFromSegment(route, matchingType);
}
@@ -555,17 +552,18 @@ private void processPossiblePotentialMatch(AvlReport avlReport,
double distanceAlongSegment =
segmentVector.matchDistanceAlongVector(avlReport.getLocation());
boolean atLayover = potentialMatchIndices.isLayover();
-
+
// Make sure only searching starting from previous spatial match.
// Otherwise would screw up determination of arrivals/departures etc.
// But only do this for blocks that have a schedule since no-schedule
// blocks are loops where we don't really have the concept of
// before/after for indices.
- if (startSearchSpatialMatch != null
- && potentialMatchIndices.getBlock().hasSchedule()) {
+
+ //&& potentialMatchIndices.getBlock().hasSchedule()
+ if (startSearchSpatialMatch != null){
// If looking at previous index then something is really wrong.
// Don't need to see if this is a match.
- if (potentialMatchIndices.lessThan(startSearchSpatialMatch.getIndices())) {
+ if (potentialMatchIndices.lessThan(startSearchSpatialMatch.getIndices())&&!startSearchSpatialMatch.getIndices().atEndOfTrip()) {
logger.error("For vehicleId={} looking at segment that is " +
"before the segment of the previous match, which " +
"should not happen. potentialMatchIndices={} " +
@@ -581,19 +579,28 @@ private void processPossiblePotentialMatch(AvlReport avlReport,
if (potentialMatchIndices.equals(startSearchSpatialMatch.getIndices())
&& distanceAlongSegment <
startSearchSpatialMatch.getDistanceAlongSegment()) {
+
// The current match would be before the starting point so
// adjust it.
- logger.debug("For vehicleId={} the spatial match was before " +
+ logger.info("For vehicleId={} the spatial match was before " +
"the starting previous match so will use the previous " +
"match. original distanceAlongSegment={} and " +
"startSearchSpatialMatch={}",
avlReport.getVehicleId(),
Geo.distanceFormat(distanceAlongSegment),
startSearchSpatialMatch);
+
distanceAlongSegment =
startSearchSpatialMatch.getDistanceAlongSegment();
- distanceToSegment =
- startSearchSpatialMatch.getDistanceToSegment();
+
+ previousPotentialSpatialMatch = null;
+
+ // return;
+ // TODO CamSys version had this comment and did not set distance to Segment.
+ // Do not set distanceToSegment to startSearchSpatialMatch value:
+ // - Need to check that it is in bounds
+ // - Need accurate comparison with next match's distance.
+
}
}
@@ -618,12 +625,14 @@ private void processPossiblePotentialMatch(AvlReport avlReport,
// If the match is better than the previous one then it trending
// towards a minimum so keep track of it if heading and distance are OK.
- if (distanceToSegment <= previousDistanceToSegment) {
+ if (distanceToSegment < previousDistanceToSegment) {
boolean headingOK = segmentVector.headingOK(avlReport.getHeading(),
CoreConfig.getMaxHeadingOffsetFromSegment());
boolean distanceOK =
distanceToSegment < getMaxAllowableDistanceFromSegment(
potentialMatchIndices, matchingType);
+
+
if (headingOK && distanceOK) {
// Heading and distance OK so store this as a potential match
previousPotentialSpatialMatch = spatialMatch;
@@ -684,6 +693,7 @@ distanceToSegment < getMaxAllowableDistanceFromSegment(
// Remember the distance to the segment for when checking the
// next indices for spatial match.
previousDistanceToSegment = distanceToSegment;
+ previousSegmentIndex = potentialMatchIndices.getSegmentIndex();
// A layover is always a spatial match since the vehicle is allowed to
// be off of the route there. So always add layovers to the list of
@@ -736,20 +746,37 @@ && withinAllowableDistanceOfLayover(avlReport.getVehicleId(),
- vehicleState.getPreviousAvlReportFromSuccessfulMatch().getTime();
double distanceAlongPathToSearch = AvlConfig.getMaxAvlSpeed() * 1.2
* timeBetweenFixesMsec / Time.MS_PER_SEC + 200.0;
+
+ // This allows you override the default max distance used to calculate how far along the route to look for a match.
+ // This is set in an additional field (max_speed)in stop_times.txt.
+ if(previousMatch.getTrip().getStopPath(previousMatch.getIndices().getStopPathIndex()).getMaxSpeed()!=null)
+ {
+ timeBetweenFixesMsec = vehicleState.getAvlReport().getTime()
+ - vehicleState.getPreviousAvlReportFromSuccessfulMatch().getTime();
+ distanceAlongPathToSearch = previousMatch.getTrip().getStopPath(previousMatch.getIndices().getStopPathIndex()).getMaxSpeed()
+ * timeBetweenFixesMsec / Time.MS_PER_SEC;
+ logger.info("Using alternate max speed {} for vehicle {} on stop path index {} which results in a distance along segment to search of {}.", previousMatch.getTrip().getStopPath(previousMatch.getIndices().getStopPathIndex()).getMaxSpeed(),vehicleState.getVehicleId(),previousMatch.getIndices().getStopPathIndex(),distanceAlongPathToSearch);
+ }
// Since already traveled some along segment should start
// distanceSearched
// with minus that distance so that it will be determined correctly.
double distanceSearched = -previousMatch.getDistanceAlongSegment();
-
+
+
// Start at the previous match and search along the block for best
// spatial matches. Look ahead until distance spanned would mean
// that vehicle would have had to travel too fast or that end of
// block reached.
Indices indices = new Indices(previousMatch);
- spatialMatcher.setStartOfSearch(previousMatch);
- while (!indices.pastEndOfBlock(vehicleState.getAvlReport().getTime())
- && distanceSearched < distanceAlongPathToSearch) {
+
+
+ spatialMatcher.setStartOfSearch(previousMatch);
+
+ while (!indices.pastEndOfBlock(vehicleState.getAvlReport().getTime()) &&
+ (vehicleState.isLayover() || distanceSearched < distanceAlongPathToSearch)
+ && Math.abs(indices.getStopPathIndex()-previousMatch.getIndices().getStopPathIndex()) <= AvlConfig.getMaxStopPathsAhead()) {
+
spatialMatcher.processPossiblePotentialMatch(
vehicleState.getAvlReport(), indices, spatialMatches,
MatchingType.STANDARD_MATCHING);
@@ -772,19 +799,31 @@ && withinAllowableDistanceOfLayover(avlReport.getVehicleId(),
}
if (spatialMatches.size() > 0) {
- logger.debug("For vehicleId={} the match with the best " +
+ logger.debug("For vehicleId={} with search started at {} there where {} matches and the match with the best " +
"distance was {}",
vehicleState.getVehicleId(),
+ previousMatch,
+ spatialMatches.size(),
spatialMatcher.smallestDistanceSpatialMatch);
} else {
// There were no spatial matches so log this problem
- logger.warn("For vehicleId={} found no spatial matches within " +
- "allowable distance of segments. Best spatial match " +
- "distance was {} for spatial match {}",
- vehicleState.getVehicleId(),
- Geo.distanceFormat(spatialMatcher.
- smallestDistanceSpatialMatch.getDistanceToSegment()),
- spatialMatcher.smallestDistanceSpatialMatch);
+ if(spatialMatcher!=null && spatialMatcher.
+ smallestDistanceSpatialMatch!=null)
+ {
+ logger.warn("For vehicleId={} found no spatial matches within " +
+ "allowable distance of segments wtih search started at {}. Best spatial match " +
+ "distance was {} for spatial match {}",
+ vehicleState.getVehicleId(),
+ previousMatch,
+ Geo.distanceFormat(spatialMatcher.
+ smallestDistanceSpatialMatch.getDistanceToSegment()),
+ spatialMatcher.smallestDistanceSpatialMatch);
+ }else
+ {
+ logger.warn("For vehicleId={} found no spatial matches within " +
+ "allowable distance of segments. No best match.",
+ vehicleState.getVehicleId());
+ }
}
// Need to look at possibility that could match to end of the block if
diff --git a/transitime/src/main/java/org/transitime/core/TemporalDifference.java b/transitclock/src/main/java/org/transitclock/core/TemporalDifference.java
similarity index 98%
rename from transitime/src/main/java/org/transitime/core/TemporalDifference.java
rename to transitclock/src/main/java/org/transitclock/core/TemporalDifference.java
index c0980bdaa..7aec02f96 100644
--- a/transitime/src/main/java/org/transitime/core/TemporalDifference.java
+++ b/transitclock/src/main/java/org/transitclock/core/TemporalDifference.java
@@ -14,12 +14,12 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.core;
+package org.transitclock.core;
import java.io.Serializable;
-import org.transitime.configData.CoreConfig;
-import org.transitime.utils.Time;
+import org.transitclock.configData.CoreConfig;
+import org.transitclock.utils.Time;
/**
* For keeping track of how far off a vehicle is from the expected time. For
diff --git a/transitime/src/main/java/org/transitime/core/TemporalMatch.java b/transitclock/src/main/java/org/transitclock/core/TemporalMatch.java
similarity index 95%
rename from transitime/src/main/java/org/transitime/core/TemporalMatch.java
rename to transitclock/src/main/java/org/transitclock/core/TemporalMatch.java
index 250622dd5..ce37c04e3 100644
--- a/transitime/src/main/java/org/transitime/core/TemporalMatch.java
+++ b/transitclock/src/main/java/org/transitclock/core/TemporalMatch.java
@@ -14,10 +14,10 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.core;
+package org.transitclock.core;
-import org.transitime.utils.Geo;
-import org.transitime.utils.Time;
+import org.transitclock.utils.Geo;
+import org.transitclock.utils.Time;
/**
* Describes where an AVL report matches to an assignment temporally. It is a
diff --git a/transitime/src/main/java/org/transitime/core/TemporalMatcher.java b/transitclock/src/main/java/org/transitclock/core/TemporalMatcher.java
similarity index 83%
rename from transitime/src/main/java/org/transitime/core/TemporalMatcher.java
rename to transitclock/src/main/java/org/transitclock/core/TemporalMatcher.java
index df6a0a104..0bfccf8de 100644
--- a/transitime/src/main/java/org/transitime/core/TemporalMatcher.java
+++ b/transitclock/src/main/java/org/transitclock/core/TemporalMatcher.java
@@ -14,20 +14,20 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.core;
-
-import java.util.Date;
-import java.util.List;
+package org.transitclock.core;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.transitime.applications.Core;
-import org.transitime.configData.CoreConfig;
-import org.transitime.db.structs.AvlReport;
-import org.transitime.db.structs.Location;
-import org.transitime.db.structs.Trip;
-import org.transitime.utils.Geo;
-import org.transitime.utils.Time;
+import org.transitclock.applications.Core;
+import org.transitclock.configData.CoreConfig;
+import org.transitclock.db.structs.AvlReport;
+import org.transitclock.db.structs.Location;
+import org.transitclock.db.structs.Trip;
+import org.transitclock.utils.Geo;
+import org.transitclock.utils.Time;
+
+import java.util.Date;
+import java.util.List;
/**
* Singleton class that does the temporal matching to determine where
@@ -66,7 +66,7 @@ public static TemporalMatcher getInstance() {
* spatial match has the best temporal match. Intended to be used when first
* matching a vehicle to an assignment.
*
- * @param for logging messages
+ * @param vehicleId for logging messages
* @param date
* @param spatialMatch
* @param isFirstSpatialMatch
@@ -84,6 +84,14 @@ public static TemporalMatcher getInstance() {
private static TemporalDifference determineHowFarOffScheduledTime(
String vehicleId, Date date, SpatialMatch spatialMatch,
boolean isFirstSpatialMatch) {
+
+ // check to see if we are frequency based
+ if (spatialMatch.getTrip().isNoSchedule()) {
+ // if there is no schedule, we really can't be late!
+ logger.debug("frequency trip has no schedule adherence, using temporalDifference of 0 {}", spatialMatch.getTrip());
+ return new TemporalDifference(0);
+ }
+
// Determine how long it should take to travel along trip to the match.
// Can add this time to the trip scheduled start time to determine
// when the vehicle is predicted to be at the match.
@@ -165,7 +173,8 @@ private static TemporalDifference determineHowFarOffScheduledTime(
* since the layover time has already passed.
*
* @param vehicleState
- * @param spatialMatches
+ * @param spatialMatch
+ * @param expectedTravelTimeMsec
* @return
*/
private TemporalDifference temporalDifferenceForSpecialLayover(VehicleState vehicleState,
@@ -232,6 +241,7 @@ private TemporalDifference temporalDifferenceForSpecialLayover(VehicleState vehi
* @return True if current temporal match is better and should be used
*/
private static boolean currentMatchIsBetter(
+ AvlReport avlReport,
TemporalMatch bestTemporalMatchSoFar, SpatialMatch currentSpatialMatch,
TemporalDifference differenceFromExpectedTime) {
// If there is no current match then it can't be better
@@ -247,9 +257,18 @@ private static boolean currentMatchIsBetter(
// If the current match is definitely better than can return true
if (differenceFromExpectedTime.betterThanOrEqualTo(
bestTemporalMatchSoFar.getTemporalDifference())) {
+ if (CoreConfig.tryForExactTripMatch()) {
+ if (!tripMatches(avlReport.getAssignmentType(), avlReport.getAssignmentId(), currentSpatialMatch.getTrip().getId()))
+ {
+ logger.warn("DROPPING preferred assignment {} for better temporal assignment {} for vehicle {}",
+ bestTemporalMatchSoFar.getTrip().getId(),
+ currentSpatialMatch.getTrip().getId(),
+ avlReport.getVehicleId());
+ }
+ }
return true;
}
-
+
// The current match does not appear better than the old one. But
// should see if for the current match the vehicle left early. If it
// did leave early then want to use the current match even though
@@ -278,7 +297,7 @@ private static boolean currentMatchIsBetter(
* since system will expect a longer and longer travel time for vehicle to
* go back to layover and then to the spatial match. So could get incorrect
* layover match for quiet a while, until the layover distance
- * transitime.core.layoverDistance, which can be large, is exceeded.
+ * transitclock.core.layoverDistance, which can be large, is exceeded.
*
* Therefore need to filter out such problematic layover matches. This is
* done by seeing if past schedule time for the layover match (before the
@@ -337,21 +356,26 @@ private boolean isProblematicLayover(VehicleState vehicleState,
// Met all the conditions as a problem layover so return true
return true;
}
-
+
+ public TemporalMatch getBestTemporalMatch(VehicleState vehicleState,
+ List spatialMatches) {
+ return getBestTemporalMatch(vehicleState, spatialMatches, false);
+ }
/**
* For the spatial matches passed in determines the one that temporally
* makes the most sense. Compares the time elapsed between AVL reports and
* compares that to the expected travel time between the previous and the
* current spatial match. The spatial match which corresponds most to the
* expected travel time is returned as the best temporal match.
- *
+ *
* @param vehicleState
* @param spatialMatches
+ * @param tripIdMatchesOnly if set only consider trip-level assignment matches
* @return The best temporal match for the spatial matches passed in. If no
* valid temporal match found then returns null.
*/
public TemporalMatch getBestTemporalMatch(VehicleState vehicleState,
- List spatialMatches) {
+ List spatialMatches, boolean tripIdMatchesOnly) {
// Convenience variables
SpatialMatch previousMatch = vehicleState.getMatch();
Date previousAvlTime =
@@ -361,10 +385,26 @@ public TemporalMatch getBestTemporalMatch(VehicleState vehicleState,
long avlTimeDifferenceMsec =
avlTime.getTime() - previousAvlTime.getTime();
+ if (spatialMatches.size() == 1 && spatialMatches.get(0).getTrip().isNoSchedule()) {
+ // here we blindly trust avl assignment if its the only match
+ logger.debug("greedily matching to only spatial assigment for frequency based trip {}", spatialMatches.get(0).getTrip());
+ return new TemporalMatch(spatialMatches.get(0), new TemporalDifference(0));
+ }
+
// Find best temporal match of the spatial matches
TemporalMatch bestTemporalMatchSoFar = null;
for (int matchIdx = 0; matchIdx < spatialMatches.size(); ++matchIdx) {
SpatialMatch spatialMatch = spatialMatches.get(matchIdx);
+
+ if (tripIdMatchesOnly
+ && !tripMatches(vehicleState.getAvlReport().getAssignmentType(),
+ vehicleState.getAvlReport().getAssignmentId(),
+ spatialMatch.getTrip().getId())) {
+ // we are in strict trip matching mode and this spatial assignment doesn't match
+ // if we can't match strictly we will try again in lenient mode
+ continue;
+ }
+
logger.debug("Examining spatial match {}", spatialMatch);
// There is a complication with vehicles leaving a layover slightly
@@ -381,11 +421,21 @@ public TemporalMatch getBestTemporalMatch(VehicleState vehicleState,
// Determine how long would expect it to take to get from previous
// match to the new match.
- int expectedTravelTimeMsec =
+ int expectedTravelTimeMsecForward =
TravelTimes.getInstance().expectedTravelTimeBetweenMatches(
vehicleState.getVehicleId(),
previousAvlTime,
previousMatch, spatialMatch);
+ int expectedTravelTimeMsecBackward =
+ TravelTimes.getInstance().expectedTravelTimeBetweenMatches(
+ vehicleState.getVehicleId(),
+ previousAvlTime,
+ spatialMatch, previousMatch);
+
+ // TODO - Check why it has to look backwards. Useful for freq based trips. Currently breaks schedule based trips
+ //int expectedTravelTimeMsec = Math.min(expectedTravelTimeMsecForward, expectedTravelTimeMsecBackward);
+
+ int expectedTravelTimeMsec = expectedTravelTimeMsecForward;
// If looking at layover match and the match is different from
// the previous one then it means we expect that the vehicle has
@@ -429,6 +479,15 @@ public TemporalMatch getBestTemporalMatch(VehicleState vehicleState,
differenceFromExpectedTime = temporalDifferenceForSpecialLayover(
vehicleState, spatialMatch, expectedTravelTimeMsec);
}
+ // If it is before the last one just return the last one.
+ // Something is not right that it thinks it is going backwards.
+ if(new Indices(spatialMatch).isEarlierStopPathThan(new Indices(previousMatch)))
+ {
+ logger.debug("Match {} is before previousMatch {}.", spatialMatch, previousMatch);
+ //bestTemporalMatchSoFar=new TemporalMatch(previousMatch,
+ // differenceFromExpectedTime);
+ //break;
+ }
logger.debug("For vehicleId={} temporal match " +
"differenceFromExpectedTime={}",
@@ -449,7 +508,7 @@ public TemporalMatch getBestTemporalMatch(VehicleState vehicleState,
// If this temporal match is better than the previous best one
// then remember it.
- if (currentMatchIsBetter(bestTemporalMatchSoFar, spatialMatch,
+ if (currentMatchIsBetter(avlReport, bestTemporalMatchSoFar, spatialMatch,
differenceFromExpectedTime)) {
bestTemporalMatchSoFar = new TemporalMatch(spatialMatch,
differenceFromExpectedTime);
@@ -471,28 +530,55 @@ public TemporalMatch getBestTemporalMatch(VehicleState vehicleState,
logger.debug("For vehicleId={} best temporal match was found to be {}",
vehicleState.getVehicleId(), bestTemporalMatchSoFar);
+
+
// Return the best temporal match (if there is one)
return bestTemporalMatchSoFar;
}
-
+
+ public TemporalMatch getBestTemporalMatchComparedToSchedule(
+ AvlReport avlReport, List spatialMatches) {
+ return getBestTemporalMatchComparedToSchedule(avlReport, spatialMatches, false);
+ }
+
/**
* From the list of spatial matches passed in, determines which one has the
* best valid temporal match. Intended to be used when first matching a
* vehicle to an assignment. Does not use previous AVL report to determine
* if there is a match.
- *
+ *
+ * @param avlReport
* @param spatialMatches
* The spatial matches to examine
+ * @param tripIdMatchesOnly strict matching to assignment
* @return The match passed in that best matches temporally where vehicle
* should be. Returns null if no adequate temporal match.
*/
public TemporalMatch getBestTemporalMatchComparedToSchedule(
- AvlReport avlReport, List spatialMatches) {
+ AvlReport avlReport, List spatialMatches, boolean tripIdMatchesOnly) {
TemporalDifference bestDifferenceFromExpectedTime = null;
SpatialMatch bestSpatialMatch = null;
+
+ logger.debug("getBestTemporalMatchComparedToSchedule has spatialMatches {}", spatialMatches);
+
+ if (spatialMatches.size() == 1 && spatialMatches.get(0).getTrip().isNoSchedule()) {
+ // again we blindly trust avl assignment if its the only match
+ logger.debug("greedily matching to only scheduled spatial assigment for frequency based trip {}", spatialMatches.get(0).getTrip());
+ return new TemporalMatch(spatialMatches.get(0), new TemporalDifference(0));
+ }
+
for (int i=0; i.
*/
-package org.transitime.core;
+package org.transitclock.core;
import java.util.HashMap;
import java.util.Iterator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.transitime.applications.Core;
-import org.transitime.config.IntegerConfigValue;
-import org.transitime.configData.AgencyConfig;
-import org.transitime.core.dataCache.VehicleStateManager;
-import org.transitime.core.schedBasedPreds.SchedBasedPredsModule;
-import org.transitime.db.structs.AvlReport;
-import org.transitime.db.structs.VehicleEvent;
-import org.transitime.logging.Markers;
-import org.transitime.modules.Module;
-import org.transitime.utils.IntervalTimer;
-import org.transitime.utils.Time;
+import org.transitclock.applications.Core;
+import org.transitclock.config.BooleanConfigValue;
+import org.transitclock.config.IntegerConfigValue;
+import org.transitclock.configData.AgencyConfig;
+import org.transitclock.core.dataCache.VehicleStateManager;
+import org.transitclock.core.schedBasedPreds.SchedBasedPredsModule;
+import org.transitclock.db.structs.AvlReport;
+import org.transitclock.db.structs.VehicleEvent;
+import org.transitclock.logging.Markers;
+import org.transitclock.modules.Module;
+import org.transitclock.utils.IntervalTimer;
+import org.transitclock.utils.Time;
/**
* For handling when a vehicle doesn't report its position for too long. Makes
@@ -57,7 +58,7 @@ public class TimeoutHandlerModule extends Module {
private static IntegerConfigValue pollingRateSecs =
new IntegerConfigValue(
- "transitime.timeout.pollingRateSecs",
+ "transitclock.timeout.pollingRateSecs",
30,
"Specifies in seconds how frequently the TimeoutHandler "
+ "should actually look for timeouts. Don't want to do "
@@ -68,7 +69,7 @@ public class TimeoutHandlerModule extends Module {
private static IntegerConfigValue allowableNoAvlSecs =
new IntegerConfigValue(
- "transitime.timeout.allowableNoAvlSecs",
+ "transitclock.timeout.allowableNoAvlSecs",
6*Time.SEC_PER_MIN,
"For AVL timeouts. If don't get an AVL report for the "
+ "vehicle in this amount of time in seconds then the "
@@ -76,7 +77,7 @@ public class TimeoutHandlerModule extends Module {
private static IntegerConfigValue allowableNoAvlAfterSchedDepartSecs =
new IntegerConfigValue(
- "transitime.timeout.allowableNoAvlAfterSchedDepartSecs",
+ "transitclock.timeout.allowableNoAvlAfterSchedDepartSecs",
6 * Time.SEC_PER_MIN,
"If a vehicle is at a wait stop, such as "
+ "sitting at a terminal, and doesn't provide an AVL report "
@@ -87,6 +88,18 @@ public class TimeoutHandlerModule extends Module {
+ "for long after scheduled departure time if vehicle "
+ "taken out of service.");
+ private static BooleanConfigValue removeTimedOutVehiclesFromVehicleDataCache =
+ new BooleanConfigValue(
+ "transitclock.timeout.removeTimedOutVehiclesFromVehicleDataCache",
+ false,
+ "When timing out vehicles, the default behavior is to make "
+ + "the vehicle unpredictable but leave it in the "
+ + "VehicleDataCache. When set to true, a timeout will also "
+ + "remove the vehicle from the VehicleDataCache. This can "
+ + "be useful in situations where it is not desirable to "
+ + "include timed out vehicles in data feeds, e.g. the GTFS "
+ + "Realtime vehicle positions feed.");
+
/********************* Logging ************************************/
private static final Logger logger = LoggerFactory
@@ -115,6 +128,19 @@ public void storeAvlReport(AvlReport avlReport) {
avlReportsMap.put(avlReport.getVehicleId(), avlReport);
}
}
+
+ /**
+ * Removes the specified vehicle from the VehicleDataCache if configured to do so
+ *
+ * @param vehicleId
+ * Vehicle to remove
+ */
+ public void removeFromVehicleDataCache(String vehicleId) {
+ if (removeTimedOutVehiclesFromVehicleDataCache.getValue()) {
+ logger.info("Removing vehicleId={} from VehicleDataCache", vehicleId);
+ AvlProcessor.getInstance().removeFromVehicleDataCache(vehicleId);
+ }
+ }
/**
* For regular predictable vehicle that is not a schedule based prediction
@@ -147,20 +173,43 @@ private void handlePredictablePossibleTimeout(VehicleState vehicleState, long no
// Remove vehicle from map for next time looking for timeouts
mapIterator.remove();
-
+
+ // Remove vehicle from cache if configured to do so
+ removeFromVehicleDataCache(vehicleState.getVehicleId());
}
}
/**
- * Don't need to worry about vehicles that are not predictable. But might as
- * well remove vehicle from map so don't examine vehicle every
- * TimeoutHandleModule polling cycle
+ * For not predictable vehicle. If not removing vehicles from cache,
+ * removes the vehicle from the map to avoid looking at it again. If
+ * configured to remove timed out vehicles from cache, and haven't
+ * reported in too long, removes the vehicle from map and cache.
*
* @param mapIterator
* So can remove AVL report from map
*/
- private void handleNotPredictablePossibleTimeout(Iterator mapIterator) {
- mapIterator.remove();
+ private void handleNotPredictablePossibleTimeout(VehicleState vehicleState,
+ long now, Iterator mapIterator) {
+ if (!removeTimedOutVehiclesFromVehicleDataCache.getValue()) {
+ // Remove vehicle from map for next time looking for timeouts and return
+ mapIterator.remove();
+ return;
+ }
+
+ // If haven't reported in too long...
+ long maxNoAvl = allowableNoAvlSecs.getValue() * Time.MS_PER_SEC;
+ if (now > vehicleState.getAvlReport().getTime() + maxNoAvl) {
+
+ // Log the situation
+ logger.info("For not predictable vehicleId={} generated timeout "
+ + "event.", vehicleState.getVehicleId());
+
+ // Remove vehicle from map for next time looking for timeouts
+ mapIterator.remove();
+
+ // Remove vehicle from cache
+ removeFromVehicleDataCache(vehicleState.getVehicleId());
+ }
}
/**
@@ -190,7 +239,10 @@ private void handleSchedBasedPredsPossibleTimeout(VehicleState vehicleState,
vehicleState.getVehicleId(), shouldTimeoutEventDescription);
// Remove vehicle from map for next time looking for timeouts
- mapIterator.remove();
+ mapIterator.remove();
+
+ // Remove vehicle from cache if configured to do so
+ removeFromVehicleDataCache(vehicleState.getVehicleId());
}
}
@@ -203,9 +255,17 @@ private void handleSchedBasedPredsPossibleTimeout(VehicleState vehicleState,
* @param now
* @param mapIterator
*/
- private void handleWaitStopPossibleTimeout(VehicleState vehicleState,
- long now, Iterator mapIterator) {
- // If hasn't been too long between AVL reports then everything is fine
+ private void handleWaitStopPossibleTimeout(VehicleState vehicleState, long now,
+ Iterator mapIterator) {
+
+ // we can't easily determine wait stop time for frequency based trips
+ // so don't timeout based on stop info
+ if (vehicleState.getBlock().isNoSchedule()) {
+ logger.debug("not timing out frequency based assignment {}", vehicleState);
+ return;
+ }
+
+ // If hasn't been too long between AVL reports then everything is fine
// and simply return
long maxNoAvl = allowableNoAvlSecs.getValue() * Time.MS_PER_SEC;
if (now < vehicleState.getAvlReport().getTime() + maxNoAvl)
@@ -255,6 +315,9 @@ private void handleWaitStopPossibleTimeout(VehicleState vehicleState,
// Remove vehicle from map for next time looking for timeouts
mapIterator.remove();
+
+ // Remove vehicle from cache if configured to do so
+ removeFromVehicleDataCache(vehicleState.getVehicleId());
}
}
}
@@ -286,7 +349,8 @@ public void handlePossibleTimeouts() {
synchronized (vehicleState) {
if (!vehicleState.isPredictable()) {
// Vehicle is not predictable
- handleNotPredictablePossibleTimeout(mapIterator);
+ handleNotPredictablePossibleTimeout(vehicleState, now,
+ mapIterator);
} else if (vehicleState.isForSchedBasedPreds()) {
// Handle schedule based predictions vehicle
handleSchedBasedPredsPossibleTimeout(vehicleState, now,
diff --git a/transitclock/src/main/java/org/transitclock/core/TravelTimeDetails.java b/transitclock/src/main/java/org/transitclock/core/TravelTimeDetails.java
new file mode 100644
index 000000000..662cc2815
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/core/TravelTimeDetails.java
@@ -0,0 +1,77 @@
+package org.transitclock.core;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.transitclock.config.IntegerConfigValue;
+import org.transitclock.core.predictiongenerator.datafilter.TravelTimeDataFilter;
+import org.transitclock.core.predictiongenerator.datafilter.TravelTimeFilterFactory;
+import org.transitclock.db.structs.ArrivalDeparture;
+import org.transitclock.ipc.data.IpcArrivalDeparture;
+import org.transitclock.utils.Time;
+
+public class TravelTimeDetails {
+ private IpcArrivalDeparture departure;
+ private IpcArrivalDeparture arrival;
+
+ private static final IntegerConfigValue maxTravelTime =
+ new IntegerConfigValue(
+ "transitclock.core.maxTravelTime",
+ 30 * Time.MS_PER_MIN,
+ "This is a maximum allowed for travel between two stops. Used as a sanity check for cache and predictions.");
+
+ private static final Logger logger = LoggerFactory
+ .getLogger(TravelTimeDetails.class);
+
+ private static final TravelTimeDataFilter dataFilter=TravelTimeFilterFactory.getInstance();
+
+ public IpcArrivalDeparture getDeparture() {
+ return departure;
+ }
+ public IpcArrivalDeparture getArrival() {
+ return arrival;
+ }
+
+ public long getTravelTime()
+ {
+ if(this.arrival!=null && this.departure!=null && arrival.isArrival() && departure.isDeparture())
+ {
+ if(sanityCheck())
+ {
+ long travelTime=this.arrival.getTime().getTime()-this.getDeparture().getTime().getTime();
+ return travelTime;
+ }else
+ {
+ logger.warn("Outside bounds : {} ", this);
+ }
+ }
+ return -1;
+ }
+ public TravelTimeDetails(IpcArrivalDeparture departure, IpcArrivalDeparture arrival) {
+ super();
+ this.departure = departure;
+ this.arrival = arrival;
+ }
+
+ public boolean sanityCheck()
+ {
+ if(this.arrival!=null && this.departure!=null && arrival.isArrival() && departure.isDeparture())
+ {
+ if(dataFilter.filter(departure, arrival))
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+ }else
+ {
+ return false;
+ }
+ }
+ @Override
+ public String toString() {
+ return "TravelTimeDetails [departure=" + departure + ", arrival=" + arrival + ", getTravelTime()="
+ + getTravelTime() + ", sanityCheck()=" + sanityCheck() + "]";
+ }
+}
diff --git a/transitime/src/main/java/org/transitime/core/TravelTimes.java b/transitclock/src/main/java/org/transitclock/core/TravelTimes.java
similarity index 96%
rename from transitime/src/main/java/org/transitime/core/TravelTimes.java
rename to transitclock/src/main/java/org/transitclock/core/TravelTimes.java
index 60dd3e552..839d0691c 100644
--- a/transitime/src/main/java/org/transitime/core/TravelTimes.java
+++ b/transitclock/src/main/java/org/transitclock/core/TravelTimes.java
@@ -14,18 +14,18 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.core;
+package org.transitclock.core;
import java.util.Date;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.transitime.applications.Core;
-import org.transitime.configData.CoreConfig;
-import org.transitime.db.structs.Location;
-import org.transitime.db.structs.ScheduleTime;
-import org.transitime.db.structs.TravelTimesForStopPath;
-import org.transitime.utils.Time;
+import org.transitclock.applications.Core;
+import org.transitclock.configData.CoreConfig;
+import org.transitclock.db.structs.Location;
+import org.transitclock.db.structs.ScheduleTime;
+import org.transitclock.db.structs.TravelTimesForStopPath;
+import org.transitclock.utils.Time;
/**
* Singleton class that contains methods for determining how long a vehicle is
@@ -278,10 +278,12 @@ private TimeTravelInfo travelTimeInfoForPartialPath(SpatialMatch match) {
* @return Expected travel time in msec
*/
public int expectedTravelTimeFromMatchToEndOfStopPath(SpatialMatch match) {
+ StringBuffer sb = new StringBuffer();
// Get the travel times for this stop path
TravelTimesForStopPath travelTimesForStopPath =
match.getTrip().getTravelTimesForStopPath(match.getStopPathIndex());
-
+
+
// Determine how match corresponds to travel time segments
TimeTravelInfo timeTravelInfo = travelTimeInfoForPartialPath(match);
@@ -291,15 +293,17 @@ public int expectedTravelTimeFromMatchToEndOfStopPath(SpatialMatch match) {
.getTravelTimeSegmentMsec(timeTravelInfo.indexOfPartialSegment);
int travelTimeRemainingInPartialSegment = (int) (travelTimeForPartialSegment *
(1-timeTravelInfo.fractionCompleted));
-
+ sb.append("partial=" +travelTimeRemainingInPartialSegment);
// Sum up the travel times for the remaining full travel time segments
// in the path.
int travelTimeMsec = travelTimeRemainingInPartialSegment;
for (int i=timeTravelInfo.indexOfPartialSegment+1;
i.
*/
-package org.transitime.core;
+package org.transitclock.core;
-import java.util.Date;
-
-import org.transitime.applications.Core;
-import org.transitime.db.structs.Block;
+import org.transitclock.applications.Core;
+import org.transitclock.configData.CoreConfig;
+import org.transitclock.db.structs.Block;
+import org.transitclock.db.structs.Trip;
/**
* So that VehicleState can keep track of whether vehicle is matched to
@@ -87,11 +87,16 @@ public String toString() {
* @return true if vehicle at end of block
*/
@Override
- public boolean atEndOfBlock() {
+ public boolean atEndOfBlock() {
Block block = getBlock();
if (block.isNoSchedule()) {
- Date date = Core.getInstance().getSystemDate();
- return !block.isActive(date);
+ // frequency based blocks last until the last trip completes
+ Trip trip = getTrip();
+ int tripDuration = trip.getEndTime() - trip.getStartTime();
+ int blockDuration = block.getEndTime() - block.getStartTime();
+ int secondsBeforeTrip = CoreConfig.getAllowableEarlySeconds();
+ int secondsAfterTrip = CoreConfig.getAllowableLateSeconds();
+ return !block.isActive(Core.getInstance().getSystemDate(), secondsBeforeTrip, blockDuration + tripDuration + secondsAfterTrip);
} else {
return getTripIndex() == block.numTrips() - 1
&& getStopPathIndex() ==
diff --git a/transitime/src/main/java/org/transitime/core/VehicleState.java b/transitclock/src/main/java/org/transitclock/core/VehicleState.java
similarity index 82%
rename from transitime/src/main/java/org/transitime/core/VehicleState.java
rename to transitclock/src/main/java/org/transitclock/core/VehicleState.java
index a5187109c..2d66a55df 100644
--- a/transitime/src/main/java/org/transitime/core/VehicleState.java
+++ b/transitclock/src/main/java/org/transitclock/core/VehicleState.java
@@ -1,6 +1,6 @@
-/*
- * This file is part of Transitime.org
- *
+/*
+ * 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
@@ -14,34 +14,25 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.core;
-
-import java.util.Collections;
-import java.util.Date;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.NoSuchElementException;
-import java.util.Objects;
+package org.transitclock.core;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.transitime.configData.CoreConfig;
-import org.transitime.db.structs.Arrival;
-import org.transitime.db.structs.AvlReport;
-import org.transitime.db.structs.AvlReport.AssignmentType;
-import org.transitime.db.structs.Block;
-import org.transitime.db.structs.Location;
-import org.transitime.db.structs.StopPath;
-import org.transitime.db.structs.Trip;
-import org.transitime.db.structs.VectorWithHeading;
-import org.transitime.ipc.data.IpcPrediction;
-import org.transitime.utils.StringUtils;
-import org.transitime.utils.Time;
+import org.transitclock.configData.CoreConfig;
+import org.transitclock.core.blockAssigner.BlockAssigner;
+import org.transitclock.core.dataCache.VehicleStateManager;
+import org.transitclock.db.structs.*;
+import org.transitclock.db.structs.AvlReport.AssignmentType;
+import org.transitclock.ipc.data.IpcPrediction;
+import org.transitclock.utils.StringUtils;
+import org.transitclock.utils.Time;
+
+import java.util.*;
/**
* Keeps track of vehicle state including its block assignment, where it
* last matched to its assignment, and AVL reports.
- *
+ *
* @author SkiBu Smith
*
*/
@@ -53,39 +44,75 @@ public class VehicleState {
// Will be set to block ID (even if used trip to determine assignment) or route ID
private String assignmentId;
private Date assignmentTime;
-
+
private boolean predictable;
// First is most recent
- private LinkedList temporalMatchHistory =
+ private LinkedList temporalMatchHistory =
new LinkedList();
// First is most recent
private LinkedList avlReportHistory =
new LinkedList();
private List predictions;
private TemporalDifference realTimeSchedAdh;
-
+
+ // create a hashamp to store the trip start times. TODO change to LinkedList doesn't grow
+ HashMap tripStartTimesMap=new HashMap ();
+
// For keeping track of how many bad matches have been encountered.
// This way can ignore bad matches if only get a couple
private int numberOfBadMatches = 0;
-
+
// So can make sure that departure time is after the arrival time
private Arrival arrivalToStoreToDb;
private long lastArrivalTime = 0;
-
+ // verify new arrival is greater than last departure
+ private long lastDepartureTime = 0;
+ private Date lastAvlTime = null;
+
// So can keep track of whether assigning vehicle to same block that
// just got unassigned for. The unassignedTime member is the time when the
// vehicle was unassigned.
private Block previousBlockBeforeUnassigned = null;
private Date unassignedTime = null;
-
- // For determining if should use a previous assignment if the current
+
+ // For determining if should use a previous assignment if the current
// assignment is not valid.
private int badAssignmentsInARow = 0;
-
+
// For keeping track if vehicle delayed
private boolean isDelayed = false;
-
- private static final Logger logger =
+
+ private Integer tripCounter = 0;
+
+ private Headway headway=null;
+
+ private HoldingTime holdingTime=null;
+ //Used for schedPred AVL. Identify if trip is canceled.
+ private boolean isCanceled;
+
+ public Headway getHeadway() {
+ return headway;
+ }
+ public void setHeadway(Headway headway) {
+ this.headway = headway;
+ }
+
+
+
+ public HoldingTime getHoldingTime() {
+ return holdingTime;
+ }
+ public void setHoldingTime(HoldingTime holdingTime) {
+ this.holdingTime = holdingTime;
+ }
+ public Integer getTripCounter() {
+ return tripCounter;
+ }
+ public void incrementTripCounter() {
+ tripCounter=tripCounter+1;
+ }
+
+ private static final Logger logger =
LoggerFactory.getLogger(VehicleState.class);
/********************** Member Functions **************************/
@@ -93,11 +120,11 @@ public class VehicleState {
public VehicleState(String vehicleId) {
this.vehicleId = vehicleId;
}
-
+
/**
* Sets the block assignment for vehicle. Also, this is how it is specified
* whether a vehicle is predictable or not.
- *
+ *
* @param newBlock
* The current block assignment for the vehicle. Set to null if
* vehicle not assigned.
@@ -110,7 +137,7 @@ public VehicleState(String vehicleId) {
* @param predictable
* Whether vehicle is predictable
*/
- public void setBlock(Block newBlock, BlockAssignmentMethod assignmentMethod,
+ public void setBlock(Block newBlock, BlockAssignmentMethod assignmentMethod,
String assignmentId, boolean predictable) {
// When vehicle is made unpredictable remember the previous assignment
// so can tell if getting assigned to same block again (which could
@@ -124,24 +151,24 @@ public void setBlock(Block newBlock, BlockAssignmentMethod assignmentMethod,
this.assignmentMethod = assignmentMethod;
this.assignmentId = assignmentId;
this.predictable = predictable;
- this.assignmentTime = getAvlReport().getDate();
+ this.assignmentTime = getAvlReport().getDate();
}
-
+
/**
* Sets the block for this VehicleState to null. Also sets assignmentId
* to null and predictable to false.
- *
+ *
* @param assignmentMethod
* How vehicle was assigned (AVL feed, auto assigner, etc). Set
* to null if vehicle not assigned.
*/
public void unsetBlock(BlockAssignmentMethod assignmentMethod) {
setBlock(null, // newBlock
- assignmentMethod,
+ assignmentMethod,
null, // assignmentId
false); // predictable
}
-
+
/**
* Determines if vehicle is currently getting assigned and it is getting assigned
* back to the same block it was assigned to just a while ago. In other
@@ -149,60 +176,64 @@ public void unsetBlock(BlockAssignmentMethod assignmentMethod) {
* getting reassigned again. In this kind of situation don't want to for
* example determine arrivals/departures back to the beginning of the block
* because probably already did so when vehicle was previously assigned.
- *
+ *
* @return True if vehicle is being reassigned to the same block as before
*/
public boolean vehicleNewlyAssignedToSameBlock() {
- // If previously wasn't assigned but it is now then it is
+ // If previously wasn't assigned but it is now then it is
// newly assigned...
if (getPreviousMatch() == null && getMatch() != null) {
// If being assigned to same block it had previously...
if (previousBlockBeforeUnassigned == getBlock()) {
// If didn't get unassigned that long ago
- if (getAvlReport().getTime() <
+ if (getAvlReport().getTime() <
this.unassignedTime.getTime() + 20 * Time.MS_PER_MIN) {
- // It is being newly assigned to the same block it was
+ // It is being newly assigned to the same block it was
// recently unassigned from
return true;
}
}
}
-
+
// Vehicle not newly assigned
return false;
}
-
+
/**
* Sets the match for the vehicle into the history. If set to null then
* VehicleState.predictable is set to false. Also resets numberOfBadMatches
* to 0.
- *
+ *
* @param match
*/
public void setMatch(TemporalMatch match) {
+ TemporalMatch lastMatch = getMatch();
+ //To enable the vehicle if it was canceled and the trip is changed.
+ if(this.isCanceled &&(lastMatch==null || lastMatch.getTrip()==null || match==null || match.getTrip()==null || lastMatch.getTrip().getId().compareTo(match.getTrip().getId())!=0))
+ this.isCanceled=false;
// Add match to history
temporalMatchHistory.addFirst(match);
-
+
// Set predictability
if (match == null) {
predictable = false;
-
+
// Make sure that the arrival time buffer is cleared so that
// when get a new assignment won't try to use it since something
// peculiar might have happened.
setArrivalToStoreToDb(null);
}
-
+
// Reset numberOfBadMatches
numberOfBadMatches = 0;
-
+
// Truncate list if it has gotten too long
- while (temporalMatchHistory.size() >
+ while (temporalMatchHistory.size() >
CoreConfig.getMatchHistoryMaxSize()) {
temporalMatchHistory.removeLast();
}
}
-
+
/**
* Returns the last temporal match. Returns null if there isn't one.
* @return
@@ -219,7 +250,7 @@ public TemporalMatch getMatch() {
* Goes through the match history for the vehicle and returns match that is
* at least minimumAge old. If there isn't a match that old then returns
* null.
- *
+ *
* @param minimumAgeMsec
* The minimum age in msec of the match to be returned
* @return TemporalMatch for vehicle that is at least minimumAge old, or
@@ -230,21 +261,21 @@ public TemporalMatch getPreviousMatch(int minimumAgeMsec) {
AvlReport currentAvlReport = getAvlReport();
if (currentAvlReport == null)
return null;
-
+
// Go through math history to find one that is old enough
for (TemporalMatch match : temporalMatchHistory) {
// If the previous match was null then don't keep on
// looking because vehicle was not predictable at some
// point. Simply return null.
- if (match == null)
+ if (match == null)
return null;
-
+
// If found match in history that is old enough then use it
- if (match.getAvlTime() <
+ if (match.getAvlTime() <
currentAvlReport.getTime() - minimumAgeMsec)
return match;
}
-
+
// Went through all matches in history and didn't find one old enough.
// If the history wasn't full then simply don't have enough matches yet.
// But if history was full then it shows that the GPS reporting is so
@@ -257,9 +288,9 @@ public TemporalMatch getPreviousMatch(int minimumAgeMsec) {
+ "at least {} msec old but match history in VehicleState "
+ "had only {} entries which was not large enough. The oldest "
+ "match in history was for {} msec old. Likely "
- + "should increase transitime.core.matchHistoryMaxSize "
+ + "should increase transitclock.core.matchHistoryMaxSize "
+ "parameter to store more history or reduce "
- + "transitime.core.timeForDeterminingNoProgress to look "
+ + "transitclock.core.timeForDeterminingNoProgress to look "
+ "back less far.",
vehicleId,
minimumAgeMsec,
@@ -269,7 +300,7 @@ public TemporalMatch getPreviousMatch(int minimumAgeMsec) {
// Didn't have an old enough matches in history
return null;
}
-
+
/**
* To be called when predictable vehicle has no valid spatial/temporal
* match. Only allowed so many of these before vehicle is made
@@ -278,80 +309,118 @@ public TemporalMatch getPreviousMatch(int minimumAgeMsec) {
public void incrementNumberOfBadMatches() {
++numberOfBadMatches;
}
-
+
/**
* Returns if have exceeded the number of allowed bad matches. If so
* then vehicle should be made unpredictable.
- *
+ *
* @return
*/
public boolean overLimitOfBadMatches() {
return numberOfBadMatches > CoreConfig.getAllowableNumberOfBadMatches();
}
-
+
/**
* Returns the number of sequential bad spatial/temporal matches that
* occurred while vehicle was predictable.
- *
+ *
* @return current number of bad matches
*/
public int numberOfBadMatches() {
return numberOfBadMatches;
}
-
+
/**
* Returns true if the last AVL report was successfully matched to the
* assignment indicating that can generate predictions and arrival/departure
* times etc.
- *
+ *
* @return True if last match is valid
*/
public boolean lastMatchIsValid() {
return numberOfBadMatches == 0;
}
-
+
/**
* Stores the specified avlReport into the history for the vehicle.
* Makes sure that the AVL history doesn't exceed maximum size.
- *
+ *
* @param avlReport
*/
public void setAvlReport(AvlReport avlReport) {
// Add AVL report to history
avlReportHistory.addFirst(avlReport);
-
+
// Truncate list if it is too long or data in it is too old
while (avlReportHistory.size() > CoreConfig.getAvlHistoryMaxSize()) {
avlReportHistory.removeLast();
}
}
-
+
+ public void putTripStartTime(Integer tripCounter, Long date)
+ {
+ /* only add time once, as it is used as part of the GTFS trip descriptor for frequency based trips as is required to stay the same. */
+ if(this.tripStartTimesMap.get(tripCounter)==null && date > 0)
+ {
+ logger.debug("Setting start time for vehicle {} for trip counter {} to {}." , this.getVehicleId(), tripCounter, new Date(date));
+ this.tripStartTimesMap.put(tripCounter, date);
+ }
+ }
+ public Long getTripStartTime(Integer tripCounter)
+ {
+ return this.tripStartTimesMap.get(tripCounter);
+ }
+ /**
+ * Increment trip counter is event it is arrival first stop on trip. Used for frequency based services.
+ * @param event
+ */
+ public void incrementTripCounter(ArrivalDeparture event)
+ {
+ VehicleState vehicleState = VehicleStateManager.getInstance().getVehicleState(event.getVehicleId());
+
+ if(event.getStopPathIndex()==0)
+ {
+ if(event.isArrival())
+ {
+ vehicleState.incrementTripCounter();
+ logger.debug("Setting vehicle counter to : {} because of event : {}",vehicleState.getTripCounter(), event);
+ }else
+ {
+ logger.debug("Not arrival so not incrementing trip counter : {}", event);
+ }
+ }else
+ if(this.getPreviousMatch().getStopPathIndex()>event.getStopPathIndex())
+ {
+ vehicleState.incrementTripCounter();
+ logger.debug("Setting vehicle counter to : {} because of event : {}",vehicleState.getTripCounter(), event);
+ }
+ }
/**
* Returns the current Trip for the vehicle. Returns null if there is not
* current trip.
- *
+ *
* @return Trip or null.
*/
public Trip getTrip() {
TemporalMatch lastMatch = getMatch();
return lastMatch!=null ? lastMatch.getTrip() : null;
}
-
+
/**
* Returns the current route ID for the vehicle. Returns null if not
* currently associated with a trip.
- *
+ *
* @return Route ID or null.
*/
public String getRouteId() {
Trip trip = getTrip();
return trip!=null ? trip.getRouteId() : null;
}
-
+
/**
* Returns the current GTFS route_short_name for the vehicle. Returns null
* if not currently associated with a trip.
- *
+ *
* @return route short name or null
*/
public String getRouteShortName() {
@@ -363,19 +432,19 @@ public String getRouteShortName() {
* Returns the current name of the route that the vehicle is on. The route
* name is a combination of the GTFS route_short_name and the GTFS
* route_long_name so get a full name of something like "38 - Geary".
- *
+ *
* @return route name or null
*/
public String getRouteName() {
Trip trip = getTrip();
return trip != null ? trip.getRouteName() : null;
}
-
+
/**
* Returns true if last temporal match for vehicle indicates that it is at a
* layover. A layover stop is a wait stop where a vehicle can leave route path before
- * departing this stop at the scheduled time since the driver is taking a break.
- *
+ * departing this stop at the scheduled time since the driver is taking a break.
+ *
* @return true if at a layover
*/
public boolean isLayover() {
@@ -385,14 +454,14 @@ public boolean isLayover() {
else
return temporalMatch.isLayover();
}
-
+
/**
* Returns true if last temporal match for vehicle indicates that it is at a
* wait stop. A wait stop is when a vehicle is supposed to depart at a
* scheduled time. A layover is always a wait stop but you can have a wait
* stop that is not a layover due to the vehicle not being allowed to go
- * away from the stop.
- *
+ * away from the stop.
+ *
* @return true if at a wait stop
*/
public boolean isWaitStop() {
@@ -402,13 +471,13 @@ public boolean isWaitStop() {
else
return temporalMatch.isWaitStop();
}
-
+
/**
* Returns the next to last temporal match. Returns null if there isn't
* one. Useful for when need to compare the previous to last match with
* the last one, such as for determining if vehicle has crossed any
* stops.
- *
+ *
* @return
*/
public TemporalMatch getPreviousMatch() {
@@ -429,12 +498,14 @@ public AvlReport getAvlReport() {
return null;
}
}
-
+
+
+
/**
* Looks in the AvlReport history for the most recent AvlReport that is at
* least minDistanceFromCurrentReport from the current AvlReport. Also makes
* sure that previous report isn't too old (more than 20 minutes old).
- *
+ *
* @param minDistanceFromCurrentReport
* Distance in meters
* @return The previous AvlReport, or null if there isn't one that far away
@@ -449,7 +520,7 @@ public AvlReport getPreviousAvlReport(double minDistanceFromCurrentReport) {
// If the previous report is too old then return null
if (currentTime - previousAvlReport.getTime() > 20 * Time.MS_PER_MIN)
return null;
-
+
// If previous location far enough away from current location
// then return the previous AVL report.
Location previousLoc = previousAvlReport.getLocation();
@@ -457,7 +528,7 @@ public AvlReport getPreviousAvlReport(double minDistanceFromCurrentReport) {
return previousAvlReport;
}
}
-
+
// Didn't find a previous AvlReport in history that was far enough away
// so return null
return null;
@@ -467,7 +538,7 @@ public AvlReport getPreviousAvlReport(double minDistanceFromCurrentReport) {
* Goes through the AVL history for the vehicle and returns AVL report that
* is at least minimumAge old. If there isn't a match that old then returns
* null.
- *
+ *
* @param minimumAgeMsec
* The minimum age in msec of the AVL report to be returned
* @return AvlReport for vehicle that is at least minimumAge old, or null if
@@ -478,7 +549,7 @@ public AvlReport getPreviousAvlReport(int minimumAgeMsec) {
if (avlReport.getTime() < getAvlReport().getTime() - minimumAgeMsec)
return avlReport;
}
-
+
// Went through all AVL reports in history and didn't find one old enough.
// If the history wasn't full then simply don't have enough matches yet.
// But if history was full then it shows that the GPS reporting is so
@@ -488,7 +559,7 @@ public AvlReport getPreviousAvlReport(int minimumAgeMsec) {
logger.error("For vehicleId={} tried to retrieve AVL "
+ "at least {} msec old but AVL history in VehicleState "
+ "had only {} entries which was not large enough. Likely "
- + "should increase transitime.core.avlHistoryMaxSize "
+ + "should increase transitclock.core.avlHistoryMaxSize "
+ "parameter to store more history.",
vehicleId,
minimumAgeMsec,
@@ -497,7 +568,7 @@ public AvlReport getPreviousAvlReport(int minimumAgeMsec) {
// Didn't have an old enough AVL reports in history
return null;
}
-
+
/**
* Returns the next to last AvlReport where successfully matched the
* vehicle. This isn't necessarily simply the previous AvlReport since that
@@ -505,12 +576,12 @@ public AvlReport getPreviousAvlReport(int minimumAgeMsec) {
* the proper AvlReport when matching a vehicle or such because otherwise
* the elapsed time between the last successful match and the current match
* would be wrong.
- *
+ *
* @return The last successfully matched AvlReport, or null if no such
* report is available
*/
public AvlReport getPreviousAvlReportFromSuccessfulMatch() {
- if (avlReportHistory.size() >= 2+numberOfBadMatches)
+ if (avlReportHistory.size() >= 2+numberOfBadMatches)
return avlReportHistory.get(1+numberOfBadMatches);
else
return null;
@@ -525,7 +596,7 @@ public AvlReport getPreviousAvlReportFromSuccessfulMatch() {
* converted to a block assignment for doing the comparison. When using a
* route assignment then makes sure the route ID has not changed. For when
* reassigning a vehicle via the AVL feed.
- *
+ *
* @param avlReport
* For determining possibly new assignment
* @return True if new assignment such that vehicle needs to be matched to
@@ -537,8 +608,8 @@ public boolean hasNewAssignment(AvlReport avlReport) {
if (avlReport.getAssignmentType() == AssignmentType.UNSET)
return false;
- // If block assignment then simply check assignment ID. This is much
- // more straight forward than determining the new AVL block and
+ // If block assignment then simply check assignment ID. This is much
+ // more straight forward than determining the new AVL block and
// comparing the new AVL block to the old block since determining
// the new block is problematic given that there can be multiple
// service IDs active at any given time.
@@ -555,7 +626,7 @@ public boolean hasNewAssignment(AvlReport avlReport) {
BlockAssigner.getInstance().getBlockAssignment(avlReport);
return block != avlBlock;
}
-
+
// Not block or trip assignment so try route assignment
if (avlReport.isRouteIdAssignmentType()) {
String routeId =
@@ -567,14 +638,14 @@ public boolean hasNewAssignment(AvlReport avlReport) {
return !Objects.equals(assignmentId, newAssignment);
}
}
-
+
// Can't determine new assignment so return false. This shouldn't
// actually happen
- logger.error("Could not determine if vehicle has new assignment. {}",
+ logger.error("Could not determine if vehicle has new assignment. {}",
avlReport);
return false;
}
-
+
/**
* Returns true if previously the vehicle had the same assignment but that
* assignment was recently removed due to a problem where the vehicle
@@ -584,7 +655,7 @@ public boolean hasNewAssignment(AvlReport avlReport) {
* continue to get that assignment via the AVL feed don't want to reassign
* it to the problem assignment again.
*
- * BUT WHAT ABOUT A VEHICLE SIMPLY BECOMING UNPREDICTABLE BECAUSE IT
+ * BUT WHAT ABOUT A VEHICLE SIMPLY BECOMING UNPREDICTABLE BECAUSE IT
* TEMPORARILY WENT OFF ROUTE FOR 3 AVL REPORTS?? IN THAT CASE WANT VEHICLE
* TO MATCH ASSIGNMENT AGAIN. OR WHAT IF BLOCK SIMPLY ENDED??
* SEEMS THAT NEED TO REMEMBER IF VEHICLE WAS UNASSIGNED IN SUCH A WAY
@@ -592,7 +663,7 @@ public boolean hasNewAssignment(AvlReport avlReport) {
*
* An old assignment is considered recent if the unassignment happened
* within the last 2 hours.
- *
+ *
* @param avlReport
* @return True if vehicle already had the assignment but it was problematic
*/
@@ -602,35 +673,35 @@ public boolean previousAssignmentProblematic(AvlReport avlReport) {
if (assignmentMethod != BlockAssignmentMethod.ASSIGNMENT_GRABBED
&& assignmentMethod != BlockAssignmentMethod.ASSIGNMENT_TERMINATED)
return false;
-
+
// If the AVL report indicates a new assignment then don't have to
// worry about the old one being problematic
if (hasNewAssignment(avlReport))
return false;
-
+
// Got same assignment from AVL feed that was previously problematic.
// If the old problem assignment was somewhat recent then return true.
- return avlReport.getTime() - unassignedTime.getTime() <
- 2 * Time.MS_PER_HOUR;
+ return avlReport.getTime() - unassignedTime.getTime() <
+ 2 * Time.MS_PER_HOUR;
}
-
+
/********************** Getter methods ************************/
-
+
/**
* Returns an unmodifiable list of the match history. The most recent
* one is first. The size of the list will be not greater than
* MATCH_HISTORY_SIZE.
- *
+ *
* @return the match history
*/
public List getMatches() {
return Collections.unmodifiableList(temporalMatchHistory);
}
-
+
/**
* The current block assignment. But will be null if vehicle not currently
* assigned.
- *
+ *
* @return
*/
public Block getBlock() {
@@ -640,17 +711,17 @@ public Block getBlock() {
/**
* Can be the blockId, tripId, or tripShortName depending on the type of
* assignment received from the AVL feed.
- *
+ *
* @return blockId, tripId, or tripShortName or null if not assigned
*/
public String getAssignmentId() {
return assignmentId;
}
-
+
/**
* Indicates how the vehicle was assigned (via block assignment, route
* assignment, auto assignment, etc).
- *
+ *
* @return
*/
public BlockAssignmentMethod getAssignmentMethod() {
@@ -664,48 +735,48 @@ public Date getAssignmentTime() {
public String getVehicleId() {
return vehicleId;
}
-
+
public boolean isPredictable() {
return predictable;
}
-
+
/**
* Returns true if not a real vehicle but instead was created to produce
* schedule based predictions.
- *
+ *
* @return true if for schedule based predictions
*/
public boolean isForSchedBasedPreds() {
AvlReport avlReport = getAvlReport();
return avlReport != null && avlReport.isForSchedBasedPreds();
}
-
+
/**
* Records the specified arrival as one that still needs to be stored to the
* db. This is important because can generate arrivals into the future but
* need to make sure that the arrival is before the subsequent departure and
* can't do so until get additional AVL reports.
- *
+ *
* @param arrival
*/
public void setArrivalToStoreToDb(Arrival arrival) {
this.arrivalToStoreToDb = arrival;
}
-
+
public Arrival getArrivalToStoreToDb() {
return arrivalToStoreToDb;
}
-
+
/**
* Sets the current list of predictions for the vehicle to the
* predictions parameter.
- *
+ *
* @param predictions
*/
public void setPredictions(List predictions) {
this.predictions = predictions;
}
-
+
/**
* Gets the current list of predictions for the vehicle. Can be null.
* @return
@@ -713,21 +784,21 @@ public void setPredictions(List predictions) {
public List getPredictions() {
return predictions;
}
-
+
/**
* Stores the real-time schedule adherence for the vehicle.
- *
+ *
* @param realTimeSchedAdh
*/
public void setRealTimeSchedAdh(TemporalDifference realTimeSchedAdh) {
this.realTimeSchedAdh = realTimeSchedAdh;
}
-
+
/**
* Returns the current real-time schedule adherence for the vehicle, or null
* if schedule adherence not currently valid (vehicle is not predictable or
* running a non-schedule based assignment).
- *
+ *
* @return The TemporalDifference representing schedule adherence, or null
* if vehicle not currently predictable or the assignment doesn't
* have a schedule
@@ -738,12 +809,12 @@ public TemporalDifference getRealTimeSchedAdh() {
else
return null;
}
-
+
/**
* Determines the heading of the vector that defines the stop path segment
* that the vehicle is currently on. The heading will be between 0.0 and
* 360.0 degrees.
- *
+ *
* @return Heading of vehicle according to path segment. NaN if not
* currently matched or there is no heading for that segment.
*/
@@ -752,15 +823,15 @@ public float getPathHeading() {
SpatialMatch match = getMatch();
if (match == null)
return Float.NaN;
-
+
// If layover stop then the heading of the path isn't really valid
// since the vehicle might be deadheading to the stop.
StopPath stopPath = getTrip().getStopPath(match.getStopPathIndex());
if (stopPath.isLayoverStop())
return Float.NaN;
-
+
// Vehicle on non-layover path so return heading of that path.
- VectorWithHeading vector =
+ VectorWithHeading vector =
stopPath.getSegmentVector(match.getSegmentIndex());
return vector.getHeading();
}
@@ -775,7 +846,7 @@ public float getPathHeading() {
* if vehicle is actually within 200m of the layover. Otherwise the
* vehicle is deadheading and don't want to use the next path heading
* if vehicle is actually reasonably far away from the layover.
- *
+ *
* @return
*/
private float getNextPathHeadingIfAtLayover() {
@@ -787,15 +858,15 @@ private float getNextPathHeadingIfAtLayover() {
// This is only for layovers so if not at layover return NaN
if (!match.isLayover())
return Float.NaN;
-
+
// Determine if actually at the layover instead of deadheading. If more
- // than 200m away then don't use the next path heading. Instead
+ // than 200m away then don't use the next path heading. Instead
// return NaN.
double distanceToLayoverStop = match.getStopPath()
.getEndOfPathLocation().distance(getAvlReport().getLocation());
- if (distanceToLayoverStop > 200.0)
+ if (distanceToLayoverStop > 200.0)
return Float.NaN;
-
+
// Vehicle is reasonably close to the layover stop so determine the
// heading of the segment just after the layover.
// If already at end of trip can't go on to next stop path
@@ -805,10 +876,10 @@ private float getNextPathHeadingIfAtLayover() {
// Determine the next first segment vector of the next path
StopPath stopPath = getTrip().getStopPath(match.getStopPathIndex()+1);
VectorWithHeading vector = stopPath.getSegmentVector(0);
-
+
return vector.getHeading();
}
-
+
/**
* Looks in avlReportHistory and returns last valid heading. Will still
* return null in certain situations such as there not being a history, the
@@ -819,50 +890,50 @@ private float getNextPathHeadingIfAtLayover() {
* if the heading is valid. At layovers it is likely that a vehicle turned a
* corner a bit before the layover point and don't want to show the old
* heading before the turn.
- *
+ *
* @return
*/
private float recentValidHeading() {
long maxAge = System.currentTimeMillis() - 2 * Time.MS_PER_MIN;
-
+
for (AvlReport avlReport : avlReportHistory) {
// If report is too old then don't use it
if (avlReport.getTime() < maxAge)
return Float.NaN;
-
+
// If AVL has valid heading then use it
if (!Float.isNaN(avlReport.getHeading())) {
return avlReport.getHeading();
}
}
-
+
// No reports have a valid heading so return NaN
return Float.NaN;
}
-
+
/**
* Normally uses the heading from getPathHeading(). But if that returns NaN
* then uses recent valid heading from last AVL report, though that might be
* NaN as well. This can be better then always using heading from AVL report
* since that often won't line up with the path and can make vehicles be
* oriented in noticeably peculiar ways when drawn on an map.
- *
+ *
* @return The best heading for a vehicle
*/
public float getHeading() {
- // Try using path heading so that direction of vehicle will really
+ // Try using path heading so that direction of vehicle will really
// follow path. This make the maps look good.
float heading = getPathHeading();
if (!Float.isNaN(heading))
return heading;
-
+
// If vehicle not matched to path or if matched to layover then the
// heading from getPathHeading() is NaN. For this situation use the
// most recent valid GPS heading.
heading = recentValidHeading();
if (!Float.isNaN(heading))
return heading;
-
+
// Most recent heading wasn't valid either so as last shot try using
// path segment of next segment past layover.
heading = getNextPathHeadingIfAtLayover();
@@ -871,13 +942,13 @@ public float getHeading() {
@Override
public String toString() {
- return "VehicleState ["
- + "vehicleId=" + vehicleId
+ return "VehicleState ["
+ + "vehicleId=" + vehicleId
+ ", blockId=" + (block==null? null : block.getId())
+ ", assignmentId=" + assignmentId
+ ", assignmentMethod=" + assignmentMethod
- + ", assignmentTime=" + assignmentTime
- + ", predictable=" + predictable
+ + ", assignmentTime=" + assignmentTime
+ + ", predictable=" + predictable
+ ", realTimeSchedAdh=" + realTimeSchedAdh
+ (isDelayed() ? ", isDelayed=true" : "")
+ ", pathHeading=" + StringUtils.twoDigitFormat(getHeading())
@@ -885,27 +956,27 @@ public String toString() {
+ ", getAvlReport()=" + getAvlReport()
+ (arrivalToStoreToDb != null ? "\n arrivalToStoreToDb=" + arrivalToStoreToDb : "")
//+ ",\n block=" + block // Block info too verbose so commented out
- //+ ",\n temporalMatchHistory=" + temporalMatchHistory
- //+ ",\n avlReportHistory=" + avlReportHistory
+ //+ ",\n temporalMatchHistory=" + temporalMatchHistory
+ //+ ",\n avlReportHistory=" + avlReportHistory
+ "]";
}
public String toStringVerbose() {
- return "VehicleState ["
- + "vehicleId=" + vehicleId
+ return "VehicleState ["
+ + "vehicleId=" + vehicleId
+ ", blockId=" + (block==null? null : block.getId())
+ ", assignmentId=" + assignmentId
+ ", assignmentMethod=" + assignmentMethod
- + ", assignmentTime=" + assignmentTime
- + ", predictable=" + predictable
+ + ", assignmentTime=" + assignmentTime
+ + ", predictable=" + predictable
+ ", realTimeSchedAdh=" + realTimeSchedAdh
+ (isDelayed() ? ", isDelayed=true" : "")
+ ", pathHeading=" + StringUtils.twoDigitFormat(getHeading())
+ ", getMatch()=" + getMatch()
+ ", getAvlReport()=" + getAvlReport()
//+ ", \nblock=" + block // Block info too verbose so commented out
- + ",\n temporalMatchHistory=" + temporalMatchHistory
- + ",\n avlReportHistory=" + avlReportHistory
+ + ",\n temporalMatchHistory=" + temporalMatchHistory
+ + ",\n avlReportHistory=" + avlReportHistory
+ (arrivalToStoreToDb != null ? "\n arrivalToStoreToDb=" + arrivalToStoreToDb : "")
+ "]";
}
@@ -922,13 +993,36 @@ public void setLastArrivalTime(long arrivalTime) {
/**
* Returns the last stored arrival time so can make sure that departure
* times are after the arrival times.
- *
+ *
* @return
*/
public long getLastArrivalTime() {
return lastArrivalTime;
}
+ /**
+ * Stores the last departure time so we can verify subsequent arrivals
+ * are always increasing.
+ * @param departureTime
+ */
+ public void setLastDepartureTime(long departureTime) { lastDepartureTime = departureTime; }
+
+ /**
+ * Returns the last departure time so we can verify subsequent arrivals
+ * are always increasing.
+ * @return
+ */
+ public long getLastDepartureTime() {
+ return lastDepartureTime;
+ }
+ public Date getLastAvlTime() {
+ return lastAvlTime;
+ }
+
+ public void setLastAvlTime(Date lastAvlTime) {
+ this.lastAvlTime = lastAvlTime;
+ }
+
public int getBadAssignmentsInARow() {
return badAssignmentsInARow;
}
@@ -936,12 +1030,21 @@ public int getBadAssignmentsInARow() {
public void setBadAssignmentsInARow(int badAssignmentsInARow) {
this.badAssignmentsInARow = badAssignmentsInARow;
}
-
+
public void setIsDelayed(boolean isDelayed) {
this.isDelayed = isDelayed;
}
-
+
public boolean isDelayed() {
return isDelayed;
}
+
+ public void setCanceled(boolean isCanceled) {
+ this.isCanceled=isCanceled;
+
+ }
+ public boolean isCanceled() {
+ return isCanceled;
+ }
+
}
diff --git a/transitime/src/main/java/org/transitime/core/autoAssigner/AutoBlockAssigner.java b/transitclock/src/main/java/org/transitclock/core/autoAssigner/AutoBlockAssigner.java
similarity index 95%
rename from transitime/src/main/java/org/transitime/core/autoAssigner/AutoBlockAssigner.java
rename to transitclock/src/main/java/org/transitclock/core/autoAssigner/AutoBlockAssigner.java
index f312bd6ef..b67abb9f2 100644
--- a/transitime/src/main/java/org/transitime/core/autoAssigner/AutoBlockAssigner.java
+++ b/transitclock/src/main/java/org/transitclock/core/autoAssigner/AutoBlockAssigner.java
@@ -15,7 +15,7 @@
* along with Transitime.org . If not, see .
*/
-package org.transitime.core.autoAssigner;
+package org.transitclock.core.autoAssigner;
import java.util.ArrayList;
import java.util.Collection;
@@ -25,26 +25,26 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.transitime.applications.Core;
-import org.transitime.config.BooleanConfigValue;
-import org.transitime.config.DoubleConfigValue;
-import org.transitime.config.IntegerConfigValue;
-import org.transitime.configData.CoreConfig;
-import org.transitime.core.BlocksInfo;
-import org.transitime.core.SpatialMatch;
-import org.transitime.core.SpatialMatcher;
-import org.transitime.core.TemporalDifference;
-import org.transitime.core.TemporalMatch;
-import org.transitime.core.TemporalMatcher;
-import org.transitime.core.TravelTimes;
-import org.transitime.core.VehicleState;
-import org.transitime.core.dataCache.VehicleDataCache;
-import org.transitime.core.dataCache.VehicleStateManager;
-import org.transitime.db.structs.AvlReport;
-import org.transitime.db.structs.Block;
-import org.transitime.db.structs.Trip;
-import org.transitime.utils.IntervalTimer;
-import org.transitime.utils.Time;
+import org.transitclock.applications.Core;
+import org.transitclock.config.BooleanConfigValue;
+import org.transitclock.config.DoubleConfigValue;
+import org.transitclock.config.IntegerConfigValue;
+import org.transitclock.configData.CoreConfig;
+import org.transitclock.core.BlocksInfo;
+import org.transitclock.core.SpatialMatch;
+import org.transitclock.core.SpatialMatcher;
+import org.transitclock.core.TemporalDifference;
+import org.transitclock.core.TemporalMatch;
+import org.transitclock.core.TemporalMatcher;
+import org.transitclock.core.TravelTimes;
+import org.transitclock.core.VehicleState;
+import org.transitclock.core.dataCache.VehicleDataCache;
+import org.transitclock.core.dataCache.VehicleStateManager;
+import org.transitclock.db.structs.AvlReport;
+import org.transitclock.db.structs.Block;
+import org.transitclock.db.structs.Trip;
+import org.transitclock.utils.IntervalTimer;
+import org.transitclock.utils.Time;
/**
* For automatically assigning a vehicle to an available block by determining
@@ -83,14 +83,14 @@ public class AutoBlockAssigner {
private static BooleanConfigValue autoAssignerEnabled =
new BooleanConfigValue(
- "transitime.autoBlockAssigner.autoAssignerEnabled",
+ "transitclock.autoBlockAssigner.autoAssignerEnabled",
false,
"Set to true to enable the auto assignment feature where "
+ "the system tries to assign vehicle to an available block");
private static BooleanConfigValue ignoreAvlAssignments =
new BooleanConfigValue(
- "transitime.autoBlockAssigner.ignoreAvlAssignments",
+ "transitclock.autoBlockAssigner.ignoreAvlAssignments",
false,
"For when want to test automatic assignments. When set to "
+ "true then system ignores assignments from AVL feed so "
@@ -101,7 +101,7 @@ public static boolean ignoreAvlAssignments() {
private static DoubleConfigValue minDistanceFromCurrentReport =
new DoubleConfigValue(
- "transitime.autoBlockAssigner.minDistanceFromCurrentReport",
+ "transitclock.autoBlockAssigner.minDistanceFromCurrentReport",
100.0,
"AutoBlockAssigner looks at two AVL reports to match "
+ "vehicle. This parameter specifies how far away those "
@@ -111,21 +111,21 @@ public static boolean ignoreAvlAssignments() {
private static IntegerConfigValue allowableEarlySeconds =
new IntegerConfigValue(
- "transitime.autoBlockAssigner.allowableEarlySeconds",
+ "transitclock.autoBlockAssigner.allowableEarlySeconds",
3*Time.SEC_PER_MIN,
"How early a vehicle can be in seconds and still be "
+ "automatically assigned to a block");
private static IntegerConfigValue allowableLateSeconds =
new IntegerConfigValue(
- "transitime.autoBlockAssigner.allowableLateSeconds",
+ "transitclock.autoBlockAssigner.allowableLateSeconds",
5*Time.SEC_PER_MIN,
"How late a vehicle can be in seconds and still be "
+ "automatically assigned to a block");
private static IntegerConfigValue minTimeBetweenAutoAssigningSecs =
new IntegerConfigValue(
- "transitime.autoBlockAssigner.minTimeBetweenAutoAssigningSecs",
+ "transitclock.autoBlockAssigner.minTimeBetweenAutoAssigningSecs",
30,
"Minimum time per vehicle that can do auto assigning. Auto "
+ "assigning is computationally expensive, especially when "
@@ -714,7 +714,7 @@ private List determineTemporalMatches() {
/**
* Determines if the auto assigner is being called too recently, as specified by
- * the transitime.autoBlockAssigner.minTimeBetweenAutoAssigningSecs property. This
+ * the transitclock.autoBlockAssigner.minTimeBetweenAutoAssigningSecs property. This
* is important because auto assigning is quite costly for agencies with many available
* blocks. If have high reporting rate and many available blocks then the system can get bogged
* down just doing auto assigning.
diff --git a/transitime/src/main/java/org/transitime/core/autoAssigner/package-info.java b/transitclock/src/main/java/org/transitclock/core/autoAssigner/package-info.java
similarity index 95%
rename from transitime/src/main/java/org/transitime/core/autoAssigner/package-info.java
rename to transitclock/src/main/java/org/transitclock/core/autoAssigner/package-info.java
index b4d0ab164..0d6bb058c 100644
--- a/transitime/src/main/java/org/transitime/core/autoAssigner/package-info.java
+++ b/transitclock/src/main/java/org/transitclock/core/autoAssigner/package-info.java
@@ -23,4 +23,4 @@
* @author SkiBu Smith
*
*/
-package org.transitime.core.autoAssigner;
\ No newline at end of file
+package org.transitclock.core.autoAssigner;
\ No newline at end of file
diff --git a/transitclock/src/main/java/org/transitclock/core/blockAssigner/BlockAssigner.java b/transitclock/src/main/java/org/transitclock/core/blockAssigner/BlockAssigner.java
new file mode 100644
index 000000000..a2053551b
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/core/blockAssigner/BlockAssigner.java
@@ -0,0 +1,213 @@
+/*
+ * 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 .
+ */
+package org.transitclock.core.blockAssigner;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.transitclock.applications.Core;
+import org.transitclock.core.ServiceUtils;
+import org.transitclock.db.structs.AvlReport;
+import org.transitclock.db.structs.Block;
+import org.transitclock.db.structs.Trip;
+import org.transitclock.gtfs.DbConfig;
+import org.transitclock.utils.Time;
+
+import java.util.Collection;
+
+/**
+ * Singleton class that handles block assignments from AVL feed.
+ *
+ * @author SkiBu Smith
+ *
+ */
+public class BlockAssigner {
+
+ // Singleton class
+ private static BlockAssigner singleton = new BlockAssigner();
+
+ private static final Logger logger =
+ LoggerFactory.getLogger(BlockAssigner.class);
+
+ /********************** Member Functions **************************/
+
+ /**
+ * Constructor private since singleton class
+ */
+ private BlockAssigner() {}
+
+ /**
+ * Returns the BlockAssigner singleton
+ *
+ * @return
+ */
+ public static BlockAssigner getInstance() {
+ return singleton;
+ }
+
+ /**
+ * Gets the appropriate block associated with the AvlReport. If the
+ * assignment is a block assignment then first gets the proper serviceIds
+ * that are active for the AVL timestamp, and then determines the
+ * appropriate block using the serviceIds and the assignment from the AVL
+ * report.
+ *
+ * Works for block assignments, trip assignments, and trip short name
+ * assignments. If the assignment not specified in AVL data or the block
+ * could not be found for the serviceIds, it could not matched to the trip,
+ * or it was a route assignment then null will be returned
+ *
+ * @param avlReport
+ * So can determine the assignment ID, and the time so that so
+ * that can determine the proper service ID.
+ * @return Block corresponding to the time and blockId from AVL report, or
+ * null if could not determine block.
+ */
+ public Block getBlockAssignment(AvlReport avlReport) {
+ // If vehicle has assignment...
+ if (avlReport != null && avlReport.getAssignmentId() != null) {
+ DbConfig config = Core.getInstance().getDbConfig();
+
+ // If using block assignment...
+ if (avlReport.isBlockIdAssignmentType()) {
+ ServiceUtils serviceUtis = Core.getInstance().getServiceUtils();
+ Collection serviceIds =
+ serviceUtis.getServiceIds(avlReport.getDate());
+ // Go through all current service IDs to find the block
+ // that is currently active
+ Block activeBlock = null;
+ for (String serviceId : serviceIds) {
+ Block blockForServiceId = config.getBlock(serviceId,
+ avlReport.getAssignmentId());
+ // If there is a block for the current service ID
+ if (blockForServiceId != null) {
+ // If found a best match so far then remember it
+ if (activeBlock == null
+ || blockForServiceId.isActive(
+ avlReport.getTime(),
+ 90 * Time.MIN_IN_SECS)) {
+ activeBlock = blockForServiceId;
+ logger.debug("For vehicleId={} and serviceId={} "
+ + "the active block assignment from the "
+ + "AVL feed is blockId={}",
+ avlReport.getVehicleId(), serviceId,
+ activeBlock.getId());
+ }
+ }
+ }
+ if (activeBlock == null) {
+ logger.error("For vehicleId={} AVL report specifies "
+ + "blockId={} but block is not valid for "
+ + "serviceIds={}",
+ avlReport.getVehicleId(),
+ avlReport.getAssignmentId(),
+ serviceIds);
+ }
+ return activeBlock;
+ } else if (avlReport.isTripIdAssignmentType()) {
+ // Using trip ID
+ Trip trip = config.getTrip(avlReport.getAssignmentId());
+ if (trip != null && trip.getBlock() != null) {
+ Block block = trip.getBlock();
+ logger.debug("For vehicleId={} the trip assignment from "
+ + "the AVL feed is tripId={} which corresponds to "
+ + "blockId={}",
+ avlReport.getVehicleId(),
+ avlReport.getAssignmentId(), block.getId());
+ return block;
+ } else {
+ if (config.getServiceIdSuffix()) {
+ for (String serviceId : Core.getInstance().getDbConfig().getCurrentServiceIds()) {
+
+ Trip tripPrefix = getTripWithServiceIdSuffix(config, avlReport.getAssignmentId());
+
+ if (tripPrefix != null){
+ Block blockPrefix = tripPrefix.getBlock();
+ logger.debug("For vehicleId={} the trip assigngment from "
+ + "the AVL feed is tripId={} and serviceId={} which corresponds to "
+ + "blockId={}",
+ avlReport.getVehicleId(),
+ avlReport.getAssignmentId(),
+ serviceId,
+ blockPrefix.getId());
+ return blockPrefix;
+ }
+ }
+ }
+ logger.error("For vehicleId={} AVL report specifies " +
+ "assignment tripId={} but that trip is not valid.",
+ avlReport.getVehicleId(),
+ avlReport.getAssignmentId());
+ }
+ } else if (avlReport.isTripShortNameAssignmentType()) {
+ // Using trip short name
+ String tripShortName = avlReport.getAssignmentId();
+ Trip trip = config.getTripUsingTripShortName(tripShortName);
+ if (trip != null) {
+ Block block = trip.getBlock();
+ logger.debug("For vehicleId={} the trip assignment from "
+ + "the AVL feed is tripShortName={} which "
+ + "corresponds to blockId={}",
+ avlReport.getVehicleId(), tripShortName,
+ block.getId());
+ return block;
+ } else {
+ logger.error("For vehicleId={} AVL report specifies "
+ + "assignment tripShortName={} but that trip is not "
+ + "valid.",
+ avlReport.getVehicleId(), tripShortName);
+ }
+ }
+ }
+
+ // No valid block so return null
+ return null;
+ }
+
+ public Trip getTripWithServiceIdSuffix(DbConfig config, String assignmentId) {
+ for (String serviceId : Core.getInstance().getDbConfig().getCurrentServiceIds()) {
+ Trip tripPrefix = config.getTrip(assignmentId + "-" + serviceId);
+ int secondsIntoDay = 120 * Time.SEC_PER_MIN;
+ if (tripPrefix != null
+ && tripPrefix.getBlock() != null
+ && tripPrefix.getBlock()
+ .isActive( Core.getInstance().getSystemTime(), secondsIntoDay)){
+ return tripPrefix;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the route ID specified in the AVL feed. If no route ID then
+ * returns null.
+ *
+ * @param avlReport
+ * @return The route ID or null if none assigned
+ */
+ public String getRouteIdAssignment(AvlReport avlReport) {
+ if (avlReport != null
+ && avlReport.getAssignmentId() != null
+ && avlReport.isRouteIdAssignmentType()) {
+ // Route ID specified so return it
+ return avlReport.getAssignmentId();
+ } else {
+ // No route ID specified in AVL feed so return null
+ return null;
+ }
+
+ }
+}
diff --git a/transitclock/src/main/java/org/transitclock/core/blockAssigner/BlockAssignerCache.java b/transitclock/src/main/java/org/transitclock/core/blockAssigner/BlockAssignerCache.java
new file mode 100644
index 000000000..9cea2c6a1
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/core/blockAssigner/BlockAssignerCache.java
@@ -0,0 +1,96 @@
+package org.transitclock.core.blockAssigner;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.transitclock.config.IntegerConfigValue;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * Thread safe caching of external block assignments
+ */
+public class BlockAssignerCache {
+
+
+ private ConcurrentMap> _blockAssignmentsByVehicleId = new ConcurrentHashMap<>();
+ private IntegerConfigValue _cacheTTL = null;
+ private BlockAssignerUpdater _updater;
+
+ private static final Logger logger = LoggerFactory
+ .getLogger(BlockAssignerCache.class);
+
+ public BlockAssignerCache(BlockAssignerUpdater updater, IntegerConfigValue cacheTTL) {
+ this._updater = updater;
+ this._cacheTTL = cacheTTL;
+ }
+
+
+ /**
+ * retrieve a copy of the cache, potentially forcing a cache update AFTER the read
+ * for performance reasons.
+ * @return
+ */
+ public ConcurrentMap> getBlockAssignmentsByVehicleIdMap() {
+ // we only update the cache if we are in use
+ if (_updater.needsUpdate(_cacheTTL.getValue())) {
+ // mark cache as updated regardless of success to prevent multiple concurrent attempts
+ _updater.markUpdated();
+ asyncUpdate();
+ }
+ return _blockAssignmentsByVehicleId;
+ }
+
+ /**
+ * perform an asynchronous update of the cache. Managed internally, does not need to
+ * be explicitly called.
+ */
+ public void asyncUpdate() {
+ WorkerThread worker = new WorkerThread(_blockAssignmentsByVehicleId, _updater);
+ new Thread(worker).start();
+ }
+
+ /**
+ * perform a synchronous update of the cache. Managed internally, does not need to be
+ * explicitly called.
+ */
+ public void update() {
+ new WorkerThread(_blockAssignmentsByVehicleId, _updater).run();
+ }
+
+
+ /**
+ * Call the Updater on a separate thread.
+ */
+ private static class WorkerThread implements Runnable {
+
+ private ConcurrentMap> blockAssignmentsByVehicleIdMap = null;
+ private BlockAssignerUpdater updater = null;
+ public WorkerThread(ConcurrentMap> blockAssignments, BlockAssignerUpdater updater) {
+ this.blockAssignmentsByVehicleIdMap = blockAssignments;
+ this.updater = updater;
+ }
+
+ public void run() {
+ long start = System.currentTimeMillis();
+ try {
+ Map> newBlockAssignmentsByVehicleIdMap = updater.getBlockAssignmentsByVehicleIdMap();
+ if (newBlockAssignmentsByVehicleIdMap != null) {
+ // if the call fails or exception thrown preserve internal state so last value can be used
+ synchronized (blockAssignmentsByVehicleIdMap) {
+ blockAssignmentsByVehicleIdMap.clear();
+ blockAssignmentsByVehicleIdMap.putAll(newBlockAssignmentsByVehicleIdMap);
+ }
+ }
+ } catch (IOException ioe) {
+ logger.info("Exception retrieving external block assignments", ioe);
+ } finally {
+ long stop = System.currentTimeMillis();
+ logger.info("cache update in {}ms", (stop - start));
+ }
+ }
+ }
+}
diff --git a/transitclock/src/main/java/org/transitclock/core/blockAssigner/BlockAssignerUpdater.java b/transitclock/src/main/java/org/transitclock/core/blockAssigner/BlockAssignerUpdater.java
new file mode 100644
index 000000000..3358a0f61
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/core/blockAssigner/BlockAssignerUpdater.java
@@ -0,0 +1,166 @@
+package org.transitclock.core.blockAssigner;
+
+import org.apache.commons.csv.CSVFormat;
+import org.apache.commons.csv.CSVRecord;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.transitclock.config.StringConfigValue;
+import org.transitclock.configData.AvlConfig;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Asynchronous updates of external block assignments.
+ */
+public class BlockAssignerUpdater {
+
+ private StringConfigValue _url = null;
+ private StringConfigValue _blockParam = null;
+ private StringConfigValue _vehicleParam = null;
+
+ private AtomicLong _lastRefreshTimeInMillis = new AtomicLong(0);
+
+ private static final Logger logger = LoggerFactory
+ .getLogger(BlockAssignerUpdater.class);
+
+ public BlockAssignerUpdater(StringConfigValue externalAssignerUrl,
+ StringConfigValue blockParam,
+ StringConfigValue vehicleParam) {
+ this._url = externalAssignerUrl;
+ this._blockParam = blockParam;
+ this._vehicleParam = vehicleParam;
+ }
+
+ /**
+ * Read the web service in an assumed CSV format and return as a map
+ * @throws IOException on any communicaton issues
+ * @return a map, possibly empty, on successful retrieval and parsing of the external feed
+ */
+ public Map> getBlockAssignmentsByVehicleIdMap() throws IOException {
+ Map> blockAssignmentsByVehicleId = new HashMap<>();
+ CSVRecord record = null;
+ CSVFormat formatter = CSVFormat.DEFAULT.withHeader().withCommentMarker('-');
+ InputStream feed = null;
+ try {
+ feed = getBlockAssignmentsByVehicleIdFeed();
+ if (feed == null) {
+ logger.warn("In ExternalBlockAssigner but \"transitclock.externalAssignerUrl\" not defined!");
+ return blockAssignmentsByVehicleId;
+ }
+ Iterable records = formatter.parse(new BufferedReader(new InputStreamReader(feed)));
+ Iterator iterator = records.iterator();
+ while (iterator.hasNext()) {
+ record = iterator.next();
+ logger.info("record=|" + record.toString() + "|");
+ if (record.size() == 0)
+ continue;
+ try {
+ VehicleAssignment assignment = handleRecord(record);
+ if (assignment != null) {
+ insert(blockAssignmentsByVehicleId, assignment);
+ }
+ } catch (Exception any) {
+ logger.warn("bad record {}", record.toString());
+ }
+ }
+
+ } catch (Exception any) {
+ // we don't throw exception so that last valid value can be used
+ logger.error("Exception retrieving feed {} at {}", any, _url, any);
+ } finally {
+ if (feed != null) {
+ try {
+ feed.close();
+ } catch (Exception bury) {
+ // don't hide actual exception
+ }
+ }
+ }
+ return blockAssignmentsByVehicleId;
+ }
+
+
+ /**
+ * connect and retrieve as input stream the configured _url. Does no caching.
+ * @return
+ * @throws Exception
+ */
+ public InputStream getBlockAssignmentsByVehicleIdFeed() throws IOException {
+ if (_url.getValue() == null) return null; // we are not configured
+ URL url = new URL(_url.getValue());
+ URLConnection con = url.openConnection();
+ int timeoutMsec = AvlConfig.getAvlFeedTimeoutInMSecs();
+ con.setConnectTimeout(timeoutMsec);
+ con.setReadTimeout(timeoutMsec);
+ InputStream in = con.getInputStream();
+ return in;
+ }
+
+
+ /**
+ * test if cache is due for a refresh based on given TTL in seconds
+ * @param cacheTTL
+ * @return
+ */
+ public boolean needsUpdate(int cacheTTL) {
+ if ((System.currentTimeMillis() - _lastRefreshTimeInMillis.get()) / 1000 > cacheTTL) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * mark the update as successful
+ */
+ public void markUpdated() {
+ _lastRefreshTimeInMillis.set(System.currentTimeMillis());
+ }
+
+
+ /**
+ * convenience method to insert into the indexed map.
+ * @param blockAssignmentsByVehicleId
+ * @param assignment
+ */
+ private void insert(Map> blockAssignmentsByVehicleId, VehicleAssignment assignment) {
+ String vehicleId = assignment.getVehicleId();
+ String blockId = assignment.getBlockId();
+ ArrayList blocks;
+ if (!blockAssignmentsByVehicleId.containsKey(vehicleId)) {
+ blocks = new ArrayList<>();
+ } else {
+ blocks = blockAssignmentsByVehicleId.get(vehicleId);
+ }
+ blocks.add(blockId);
+ logger.debug("blockAssignmentsByVehicleId({})={}", vehicleId, blocks);
+ blockAssignmentsByVehicleId.put(vehicleId, blocks);
+ }
+
+ /**
+ * wrangle a CSVRecord into a blockId and a vehicleId based on configuration.
+ * @param record
+ * @return
+ */
+ private VehicleAssignment handleRecord(CSVRecord record) {
+ String vehicleId = record.get(_vehicleParam.getValue());
+ String blockId = record.get(_blockParam.getValue());
+ if (StringUtils.isNoneBlank(vehicleId) && StringUtils.isNotBlank(blockId)) {
+ return new VehicleAssignment(vehicleId, blockId);
+ }
+ logger.warn("discarding record {}", record.toString());
+ return null;
+ }
+
+
+}
diff --git a/transitclock/src/main/java/org/transitclock/core/blockAssigner/VehicleAssignment.java b/transitclock/src/main/java/org/transitclock/core/blockAssigner/VehicleAssignment.java
new file mode 100644
index 000000000..ee7e0e2bd
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/core/blockAssigner/VehicleAssignment.java
@@ -0,0 +1,23 @@
+package org.transitclock.core.blockAssigner;
+
+/**
+ * model class representing an external vehicle block assignment.
+ */
+public class VehicleAssignment {
+ private String _vehicleId;
+ private String _blockId;
+
+ public VehicleAssignment(String vehicleId, String blockId) {
+ this._vehicleId = vehicleId;
+ this._blockId = blockId;
+ }
+
+ public String getVehicleId() {
+ return _vehicleId;
+ }
+
+ public String getBlockId() {
+ return _blockId;
+ }
+}
+
diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/ArrivalDepartureComparator.java b/transitclock/src/main/java/org/transitclock/core/dataCache/ArrivalDepartureComparator.java
new file mode 100755
index 000000000..1e7d37652
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/core/dataCache/ArrivalDepartureComparator.java
@@ -0,0 +1,22 @@
+package org.transitclock.core.dataCache;
+
+import java.util.Comparator;
+
+import org.transitclock.db.structs.ArrivalDeparture;
+
+public class ArrivalDepartureComparator implements Comparator {
+
+
+ @Override
+ public int compare(ArrivalDeparture ad1, ArrivalDeparture ad2) {
+
+ if(ad1.getTime() ad2.getTime())
+ {
+ return -1;
+ }
+ return 0;
+ }
+}
diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/ArrivalDeparturesToProcessHoldingTimesFor.java b/transitclock/src/main/java/org/transitclock/core/dataCache/ArrivalDeparturesToProcessHoldingTimesFor.java
new file mode 100755
index 000000000..c44c3ece1
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/core/dataCache/ArrivalDeparturesToProcessHoldingTimesFor.java
@@ -0,0 +1,47 @@
+package org.transitclock.core.dataCache;
+
+import java.util.ArrayList;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.transitclock.db.structs.ArrivalDeparture;
+/**
+ * @author Sean Óg Crudden
+ */
+public class ArrivalDeparturesToProcessHoldingTimesFor {
+
+ private static ArrivalDeparturesToProcessHoldingTimesFor singleton = new ArrivalDeparturesToProcessHoldingTimesFor();
+
+ private static final Logger logger = LoggerFactory
+ .getLogger(ArrivalDeparturesToProcessHoldingTimesFor.class);
+
+
+
+
+ private final ArrayList m = new ArrayList();;
+
+ /**
+ * Gets the singleton instance of this class.
+ *
+ * @return
+ */
+ public static ArrivalDeparturesToProcessHoldingTimesFor getInstance() {
+ return singleton;
+ }
+
+
+ private ArrivalDeparturesToProcessHoldingTimesFor() {
+ }
+ public void empty()
+ {
+ m.clear();
+ }
+ public void add(ArrivalDeparture ad)
+ {
+ m.add(ad);
+ }
+ public ArrayList getList()
+ {
+ return m;
+ }
+
+}
diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/CacheTask.java b/transitclock/src/main/java/org/transitclock/core/dataCache/CacheTask.java
new file mode 100644
index 000000000..3d48efc04
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/core/dataCache/CacheTask.java
@@ -0,0 +1,99 @@
+package org.transitclock.core.dataCache;
+
+import org.hibernate.Criteria;
+import org.hibernate.Session;
+import org.hibernate.criterion.Restrictions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.transitclock.core.dataCache.frequency.FrequencyBasedHistoricalAverageCache;
+import org.transitclock.core.dataCache.scheduled.ScheduleBasedHistoricalAverageCache;
+import org.transitclock.core.predictiongenerator.scheduled.traveltime.kalman.TrafficManager;
+import org.transitclock.db.hibernate.HibernateUtils;
+import org.transitclock.db.structs.ArrivalDeparture;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * A task populating the cache on startup. Designed to be
+ * run in parallel.
+ */
+public class CacheTask implements ParallelTask {
+
+ private static final Logger logger =
+ LoggerFactory.getLogger(CacheTask.class);
+
+ /**
+ * type of cache we are dealing with
+ */
+ public enum Type {
+ TripDataHistoryCacheFactory,
+ StopArrivalDepartureCacheFactory,
+ FrequencyBasedHistoricalAverageCache,
+ ScheduleBasedHistoricalAverageCache,
+ TrafficDataHistoryCache,
+ DwellTimeModelCacheFactory
+ }
+
+ private Date startDate;
+ private Date endDate;
+ private Type type;
+ private List results;
+
+ public CacheTask(Date startDate, Date endDate, Type type, List defaultInput) {
+ this.startDate = startDate;
+ this.endDate = endDate;
+ this.type = type;
+ this.results = defaultInput;
+ }
+
+ @Override
+ public String toString() {
+ return type.name();
+ }
+
+ @Override
+ public void run() throws Exception {
+ Session session = null;
+ try {
+ session = HibernateUtils.getSession();
+ if (this.results == null) {
+ Criteria criteria = session.createCriteria(ArrivalDeparture.class);
+ results = criteria.add(Restrictions.between("time", startDate, endDate)).list();
+ }
+
+ logger.info("Populating {} cache for period {} to {}", type, startDate, endDate);
+ switch (type) {
+ case TripDataHistoryCacheFactory:
+ TripDataHistoryCacheFactory.getInstance().populateCacheFromDb(results);
+ break;
+ case StopArrivalDepartureCacheFactory:
+ StopArrivalDepartureCacheFactory.getInstance().populateCacheFromDb(results);
+ break;
+ case FrequencyBasedHistoricalAverageCache:
+ FrequencyBasedHistoricalAverageCache.getInstance().populateCacheFromDb(results);
+ break;
+ case ScheduleBasedHistoricalAverageCache:
+ ScheduleBasedHistoricalAverageCache.getInstance().populateCacheFromDb(results);
+ break;
+ case DwellTimeModelCacheFactory:
+ DwellTimeModelCacheFactory.getInstance().populateCacheFromDb(results);
+ break;
+ case TrafficDataHistoryCache:
+ TrafficManager.getInstance().populateCacheFromDb(session, startDate, endDate);
+ break;
+ default:
+ throw new IllegalArgumentException("unknown type=" + type);
+ }
+
+ } finally {
+ logger.info("Finished Populating {} cache for period {} to {}", type, startDate, endDate);
+ if (session != null) {
+ // this session is in a separate thread and needs to be reclaimed
+ // as it counts against the connection pool
+ session.close();
+ }
+ }
+ }
+
+}
diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/CanceledTripKey.java b/transitclock/src/main/java/org/transitclock/core/dataCache/CanceledTripKey.java
new file mode 100644
index 000000000..04168ee64
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/core/dataCache/CanceledTripKey.java
@@ -0,0 +1,67 @@
+package org.transitclock.core.dataCache;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+public class CanceledTripKey implements Serializable {
+
+ private final String vehicleId;
+ private final String tripId;
+
+
+ // Since hashCode() can be called a lot might as well cache the value
+ // since this object is immutable.
+ private int cachedHashCode;
+
+ public CanceledTripKey(String vehicleId, String tripId) {
+ this.vehicleId = vehicleId;
+ this.tripId = tripId;
+ this.cachedHashCode = createHashCode();
+ }
+
+ public String getVehicleId(){
+ return vehicleId;
+ }
+
+ public String getTripId(){
+ return tripId;
+ }
+
+ @Override
+ public String toString() {
+ return "CanceledTripKey [" + "vehicleId=" + vehicleId
+ + ", tripId=" + tripId + "]";
+ }
+
+
+ /**
+ * Using cached hash code for speed. Therefore this method is used by
+ * constructors to initialize the cached hash code.
+ *
+ * @return hash code to be cached
+ */
+ private int createHashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((vehicleId == null) ? 0 : vehicleId.hashCode());
+ result = prime * result + ((tripId == null) ? 0 : tripId.hashCode());
+ return result;
+ }
+
+ /**
+ * @return the cached hash code
+ */
+ @Override
+ public int hashCode() {
+ return cachedHashCode;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ CanceledTripKey that = (CanceledTripKey) o;
+ return Objects.equals(vehicleId, that.vehicleId) &&
+ Objects.equals(tripId, that.tripId);
+ }
+}
\ No newline at end of file
diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/CanceledTripManager.java b/transitclock/src/main/java/org/transitclock/core/dataCache/CanceledTripManager.java
new file mode 100644
index 000000000..229e6974a
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/core/dataCache/CanceledTripManager.java
@@ -0,0 +1,69 @@
+package org.transitclock.core.dataCache;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import org.transitclock.config.LongConfigValue;
+import org.transitclock.ipc.data.IpcCanceledTrip;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+public class CanceledTripManager {
+
+ private static LongConfigValue canceledTripCacheExpireSec =
+ new LongConfigValue("transitclock.avl.canceledTripCacheExpireSec", 60l,
+ "The amount of time to keep a trip schedule status in cache.");
+
+ private Cache canceledTripCache;
+
+ // This is a singleton class
+ private static CanceledTripManager singleton = new CanceledTripManager();
+
+ /********************** Member Functions **************************/
+
+ /**
+ * Constructor made private because this is singleton class where
+ * getInstance() should be used to get the VehicleStateManager.
+ */
+ private CanceledTripManager() {
+ canceledTripCache = CacheBuilder.newBuilder()
+ .expireAfterWrite(canceledTripCacheExpireSec.getValue(), TimeUnit.SECONDS)
+ .build();
+ }
+
+ /**
+ * Returns the singleton CanceledTripManager
+ * @return
+ */
+ public static CanceledTripManager getInstance() {
+ if(singleton == null){
+ synchronized (CanceledTripManager.class){
+ if(singleton == null){
+ singleton = new CanceledTripManager();
+ }
+ }
+ }
+ return singleton;
+ }
+
+ public boolean isCanceled(String vehicleId, String tripId){
+ CanceledTripKey key = new CanceledTripKey(vehicleId, tripId);
+ IpcCanceledTrip cachedTrip = canceledTripCache.getIfPresent(key);
+ if(cachedTrip != null && cachedTrip.getTripId() != null &&
+ tripId != null && cachedTrip.getTripId().equalsIgnoreCase(tripId)){
+ return true;
+ }
+ return false;
+ }
+
+ public synchronized void putAll(Map tripStatusMap){
+ canceledTripCache.putAll(tripStatusMap);
+ }
+
+ public synchronized HashMap getAll(){
+ HashMap canceledTripMap = new HashMap(canceledTripCache.asMap());
+ return canceledTripMap;
+ }
+
+}
diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/DwellTimeModelCacheFactory.java b/transitclock/src/main/java/org/transitclock/core/dataCache/DwellTimeModelCacheFactory.java
new file mode 100644
index 000000000..47a60f5d8
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/core/dataCache/DwellTimeModelCacheFactory.java
@@ -0,0 +1,27 @@
+package org.transitclock.core.dataCache;
+import org.transitclock.config.StringConfigValue;
+import org.transitclock.utils.ClassInstantiator;
+
+/**
+ * @author Sean Óg Crudden
+ * Factory that will provide cache to hold dwell time model class instances for each stop.
+ *
+ */
+public class DwellTimeModelCacheFactory {
+ private static StringConfigValue className =
+ new StringConfigValue("transitclock.core.cache.dwellTimeModelCache",
+ "org.transitclock.core.dataCache.ehcache.scheduled.DwellTimeModelCache",
+ "Specifies the class used to cache RLS data for a stop.");
+
+ private static DwellTimeModelCacheInterface singleton = null;
+
+ public static DwellTimeModelCacheInterface getInstance() {
+
+ if (singleton == null) {
+ singleton = ClassInstantiator.instantiate(className.getValue(),
+ DwellTimeModelCacheInterface.class);
+ }
+
+ return singleton;
+ }
+}
diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/DwellTimeModelCacheInterface.java b/transitclock/src/main/java/org/transitclock/core/dataCache/DwellTimeModelCacheInterface.java
new file mode 100644
index 000000000..525ee906c
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/core/dataCache/DwellTimeModelCacheInterface.java
@@ -0,0 +1,17 @@
+package org.transitclock.core.dataCache;
+
+import org.transitclock.db.structs.ArrivalDeparture;
+import org.transitclock.db.structs.Headway;
+
+import java.util.List;
+
+public interface DwellTimeModelCacheInterface {
+
+ void addSample(ArrivalDeparture event, Headway headway, long dwellTime);
+
+ void addSample(ArrivalDeparture departure);
+
+ Long predictDwellTime(StopPathCacheKey cacheKey, Headway headway);
+
+ void populateCacheFromDb(List results);
+}
diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/ErrorCache.java b/transitclock/src/main/java/org/transitclock/core/dataCache/ErrorCache.java
new file mode 100644
index 000000000..5ff3dbf2f
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/core/dataCache/ErrorCache.java
@@ -0,0 +1,22 @@
+package org.transitclock.core.dataCache;
+
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.transitclock.core.Indices;
+
+public interface ErrorCache {
+
+ KalmanError getErrorValue(Indices indices);
+
+ KalmanError getErrorValue(KalmanErrorCacheKey key);
+
+ void putErrorValue(Indices indices, Double value);
+
+ void putErrorValue(KalmanErrorCacheKey key, Double value);
+
+ List getKeys();
+
+
+
+}
\ No newline at end of file
diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/ErrorCacheFactory.java b/transitclock/src/main/java/org/transitclock/core/dataCache/ErrorCacheFactory.java
new file mode 100644
index 000000000..4d1e4b033
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/core/dataCache/ErrorCacheFactory.java
@@ -0,0 +1,27 @@
+package org.transitclock.core.dataCache;
+import org.transitclock.config.StringConfigValue;
+import org.transitclock.utils.ClassInstantiator;
+
+/**
+ * @author Sean Óg Crudden
+ * Factory that will provide cache to hold Kalman error values.
+ *
+ */
+public class ErrorCacheFactory {
+ private static StringConfigValue className =
+ new StringConfigValue("transitclock.core.cache.errorCacheClass",
+ "org.transitclock.core.dataCache.ehcache.KalmanErrorCache",
+ "Specifies the class used to cache the Kalamn error values.");
+
+ private static ErrorCache singleton = null;
+
+ public static ErrorCache getInstance() {
+
+ if (singleton == null) {
+ singleton = ClassInstantiator.instantiate(className.getValue(),
+ ErrorCache.class);
+ }
+
+ return singleton;
+ }
+}
diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/HistoricalAverage.java b/transitclock/src/main/java/org/transitclock/core/dataCache/HistoricalAverage.java
new file mode 100644
index 000000000..23e09f8f6
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/core/dataCache/HistoricalAverage.java
@@ -0,0 +1,50 @@
+package org.transitclock.core.dataCache;
+
+import java.io.Serializable;
+
+/**
+ * @author Sean Og Crudden
+ *
+ */
+public class HistoricalAverage implements Serializable{
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 2103092627208174290L;
+
+ @Override
+ public String toString() {
+ return "HistoricalAverage [count=" + count + ", average=" + average + "]";
+ }
+ public HistoricalAverage() {
+ super();
+ count=0;
+ average=0;
+ }
+
+ private int count;
+
+ double average;
+
+ public int getCount() {
+ return count;
+ }
+ public void setCount(int count) {
+ this.count = count;
+ }
+ public double getAverage() {
+ return average;
+ }
+ public void setAverage(double average) {
+ this.average = average;
+ }
+
+ public void update(double element)
+ {
+ average=((count*average)+element)/(count+1);
+ count=count+1;
+ }
+
+
+}
diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/HoldingTimeCache.java b/transitclock/src/main/java/org/transitclock/core/dataCache/HoldingTimeCache.java
new file mode 100755
index 000000000..db1812555
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/core/dataCache/HoldingTimeCache.java
@@ -0,0 +1,68 @@
+package org.transitclock.core.dataCache;
+
+import java.net.URL;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.transitclock.db.structs.HoldingTime;
+
+import org.ehcache.Cache;
+import org.ehcache.CacheManager;
+
+import org.ehcache.Status;
+import org.ehcache.config.builders.CacheManagerBuilder;
+import org.ehcache.xml.XmlConfiguration;
+/**
+ * @author Sean Óg Crudden
+ *
+ */
+public class HoldingTimeCache {
+ final private static String cacheName = "HoldingTimeCache";
+ private static HoldingTimeCache singleton = new HoldingTimeCache();
+ private static final Logger logger = LoggerFactory
+ .getLogger(HoldingTimeCache.class);
+ final URL xmlConfigUrl = getClass().getResource("/ehcache.xml");
+ private Cache cache = null;
+ /**
+ * Gets the singleton instance of this class.
+ *
+ * @return
+ */
+ public static HoldingTimeCache getInstance() {
+ return singleton;
+ }
+ private HoldingTimeCache() {
+ XmlConfiguration xmlConfig = new XmlConfiguration(xmlConfigUrl);
+
+ CacheManager cm = CacheManagerBuilder.newCacheManager(xmlConfig);
+
+ if(cm.getStatus().compareTo(Status.AVAILABLE)!=0)
+ cm.init();
+
+ cache = cm.getCache(cacheName, HoldingTimeCacheKey.class, HoldingTime.class);
+ }
+ public void logCache(Logger logger)
+ {
+ logger.debug("Cache content log. Not implemented");
+
+ }
+ public void putHoldingTime(HoldingTime holdingTime)
+ {
+ HoldingTimeCacheKey key=new HoldingTimeCacheKey(holdingTime);
+
+ cache.put(key, holdingTime);
+ }
+
+ public HoldingTime getHoldingTime(HoldingTimeCacheKey key)
+ {
+ return cache.get(key);
+
+
+ }
+ public List getKeys() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+}
diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/HoldingTimeCacheKey.java b/transitclock/src/main/java/org/transitclock/core/dataCache/HoldingTimeCacheKey.java
new file mode 100755
index 000000000..7995eb231
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/core/dataCache/HoldingTimeCacheKey.java
@@ -0,0 +1,88 @@
+package org.transitclock.core.dataCache;
+
+import java.util.Date;
+
+import org.transitclock.db.structs.HoldingTime;
+/**
+ * @author Sean Óg Crudden
+ *
+ */
+public class HoldingTimeCacheKey implements java.io.Serializable {
+ /**
+ *
+ */
+ private static final long serialVersionUID = 2621110737961919479L;
+
+ public HoldingTimeCacheKey(String stopid, String vehicleId, String tripId) {
+ super();
+ this.stopid = stopid;
+ this.vehicleId = vehicleId;
+ this.tripId = tripId;
+
+ }
+ public HoldingTimeCacheKey(HoldingTime holdTime)
+ {
+ this.stopid=holdTime.getStopId();
+ this.vehicleId=holdTime.getVehicleId();
+ this.tripId=holdTime.getTripId();
+ }
+ @Override
+ public String toString() {
+ return "HoldingTimeCacheKey [stopid=" + stopid + ", vehicleId=" + vehicleId + ", tripId=" + tripId + "]";
+ }
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((stopid == null) ? 0 : stopid.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;
+ HoldingTimeCacheKey other = (HoldingTimeCacheKey) obj;
+ if (stopid == null) {
+ if (other.stopid != null)
+ return false;
+ } else if (!stopid.equals(other.stopid))
+ return false;
+ if (vehicleId == null) {
+ if (other.vehicleId != null)
+ return false;
+ } else if (!vehicleId.equals(other.vehicleId))
+ return false;
+ return true;
+ }
+ public String getStopid() {
+ return stopid;
+ }
+ public void setStopid(String stopid) {
+ this.stopid = stopid;
+ }
+ public String getVehicleId() {
+ return vehicleId;
+ }
+ public void setVehicleId(String vehicleId) {
+ this.vehicleId = vehicleId;
+ }
+
+ private String stopid;
+ private String vehicleId;
+ private String tripId;
+
+
+ public String getTripId() {
+ return tripId;
+ }
+ public void setTripId(String tripId) {
+ this.tripId = tripId;
+ }
+
+}
diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/IpcArrivalDepartureComparator.java b/transitclock/src/main/java/org/transitclock/core/dataCache/IpcArrivalDepartureComparator.java
new file mode 100644
index 000000000..363fde8f7
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/core/dataCache/IpcArrivalDepartureComparator.java
@@ -0,0 +1,25 @@
+package org.transitclock.core.dataCache;
+
+import java.util.Comparator;
+
+import org.transitclock.ipc.data.IpcArrivalDeparture;
+
+
+
+public class IpcArrivalDepartureComparator implements Comparator {
+
+
+ @Override
+ public int compare(IpcArrivalDeparture ad1, IpcArrivalDeparture ad2) {
+
+ if(ad1.getTime().getTime() ad2.getTime().getTime())
+ {
+ return -1;
+ }
+ return 0;
+ }
+
+}
diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/KalmanError.java b/transitclock/src/main/java/org/transitclock/core/dataCache/KalmanError.java
new file mode 100644
index 000000000..c0e34de65
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/core/dataCache/KalmanError.java
@@ -0,0 +1,87 @@
+package org.transitclock.core.dataCache;
+
+import java.io.Serializable;
+
+public class KalmanError implements Serializable {
+ /**
+ *
+ */
+ private static final long serialVersionUID = 6732432800590510672L;
+
+ // This is the error values itself.
+ private Double error=Double.NaN;
+
+ //This is the number of times it has been updated.
+ private Integer updates=null;
+
+ public KalmanError(Double error) {
+ super();
+ setError(error);
+ }
+
+ public KalmanError() {
+ super();
+ // TODO Auto-generated constructor stub
+ }
+
+ public Double getError() {
+ return error;
+ }
+
+ public void setError(Double error) {
+
+ if(error != null && this.error != null && this.error.compareTo(error)!=0)
+ {
+ this.error = error;
+ incrementUpdates();
+ }
+ }
+
+ public Integer getUpdates() {
+ return updates;
+ }
+
+ private void incrementUpdates()
+ {
+ if(this.updates!=null)
+ this.updates=this.updates+1;
+ else
+ this.updates=new Integer(0);
+ }
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((error == null) ? 0 : error.hashCode());
+ result = prime * result + ((updates == null) ? 0 : updates.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;
+ KalmanError other = (KalmanError) obj;
+ if (error == null) {
+ if (other.error != null)
+ return false;
+ } else if (!error.equals(other.error))
+ return false;
+ if (updates == null) {
+ if (other.updates != null)
+ return false;
+ } else if (!updates.equals(other.updates))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "KalmanError [error=" + error + ", updates=" + updates + "]";
+ }
+
+}
diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/KalmanErrorCacheKey.java b/transitclock/src/main/java/org/transitclock/core/dataCache/KalmanErrorCacheKey.java
new file mode 100755
index 000000000..3cec5f392
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/core/dataCache/KalmanErrorCacheKey.java
@@ -0,0 +1,123 @@
+package org.transitclock.core.dataCache;
+
+import org.transitclock.core.Indices;
+
+/**
+ * @author Sean Og Crudden
+ * TODO This is the same as StopPathCacheKey but left seperate in case we might use block_id as well.
+ */
+public class KalmanErrorCacheKey implements java.io.Serializable {
+
+
+ public String getTripId() {
+ return tripId;
+ }
+
+ public void setTripId(String tripId) {
+ this.tripId = tripId;
+ }
+
+ public void setStopPathIndex(Integer stopPathIndex) {
+ this.stopPathIndex = stopPathIndex;
+ }
+
+ private String tripId;
+ private Integer stopPathIndex;
+
+ // The vehicleId is only used for debug purposed we know in log which vehicle set the error value
+ private String vehiceId;
+
+
+ public String getVehiceId() {
+ return vehiceId;
+ }
+
+ public void setVehiceId(String vehiceId) {
+ this.vehiceId = vehiceId;
+ }
+
+ /**
+ * Needs to be serializable to add to cache
+ */
+ private static final long serialVersionUID = 5029823633051153716L;
+
+
+ public KalmanErrorCacheKey(Indices indices, String vehicleId) {
+ super();
+
+ this.tripId=indices.getBlock().getTrip(indices.getTripIndex()).getId();
+ this.stopPathIndex=indices.getStopPathIndex();
+ this.vehiceId=vehicleId;
+
+ }
+ public KalmanErrorCacheKey(Indices indices) {
+ super();
+
+ this.tripId=indices.getBlock().getTrip(indices.getTripIndex()).getId();
+ this.stopPathIndex=indices.getStopPathIndex();
+
+
+ }
+ @Override
+ public String toString() {
+ return "KalmanErrorCacheKey [tripId=" + tripId + ", stopPathIndex=" + stopPathIndex + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((stopPathIndex == null) ? 0 : stopPathIndex.hashCode());
+ result = prime * result + ((tripId == null) ? 0 : tripId.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;
+ KalmanErrorCacheKey other = (KalmanErrorCacheKey) obj;
+ if (stopPathIndex == null) {
+ if (other.stopPathIndex != null)
+ return false;
+ } else if (!stopPathIndex.equals(other.stopPathIndex))
+ return false;
+ if (tripId == null) {
+ if (other.tripId != null)
+ return false;
+ } else if (!tripId.equals(other.tripId))
+ return false;
+ return true;
+ }
+
+ public KalmanErrorCacheKey(String tripId, Integer stopPathIndex) {
+ super();
+
+ this.tripId = tripId;
+ this.stopPathIndex = stopPathIndex;
+ }
+
+
+ /**
+ * @return the stopPathIndex
+ */
+ public int getStopPathIndex() {
+ return stopPathIndex;
+ }
+
+ /**
+ * @param stopPathIndex the stopPathIndex to set
+ */
+ public void setStopPathIndex(int stopPathIndex) {
+ this.stopPathIndex = stopPathIndex;
+ }
+
+}
+
+
+
+
diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/ParallelProcessor.java b/transitclock/src/main/java/org/transitclock/core/dataCache/ParallelProcessor.java
new file mode 100644
index 000000000..6841ff44c
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/core/dataCache/ParallelProcessor.java
@@ -0,0 +1,187 @@
+package org.transitclock.core.dataCache;
+
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.transitclock.config.IntegerConfigValue;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ArrayBlockingQueue;
+
+/**
+ * Processor for managing a thread queue of parallel active work. Currently used for
+ * Cache loading on startup.
+ */
+public class ParallelProcessor {
+
+ public static IntegerConfigValue parallelThreads = new IntegerConfigValue("transitclock.core.parallelThreads",
+ -1,
+ "Number of threads to run in parallel for cache loading. -1 is use all cpus, 1 is no parallelism at all");
+ private static final Logger logger = LoggerFactory
+ .getLogger(ParallelProcessor.class);
+
+ private List list = Collections.synchronizedList(new ArrayList());
+ private ArrayBlockingQueue runQueue;
+ private boolean shutDown = false;
+ private long startTime;
+
+ public ParallelProcessor() {
+ int parallelThreadCount = Runtime.getRuntime().availableProcessors();
+ if (parallelThreads.getValue() > 0)
+ parallelThreadCount = parallelThreads.getValue();
+ runQueue = new ArrayBlockingQueue(parallelThreadCount);
+ }
+
+ /**
+ * add a task to the queue.
+ * @param task
+ */
+ public void enqueue(ParallelTask task) {
+ list.add(task);
+ }
+
+ /**
+ * signal that running threads should exit.
+ */
+ public void shutdown() {
+ shutDown = true;
+ }
+
+ /**
+ * milliseconds that processor has been running for.
+ * @return
+ */
+ public long getRuntime() {
+ return System.currentTimeMillis() - startTime;
+ }
+
+ /**
+ * if all tasks are complete. An exception counts as complete.
+ * @return
+ */
+ public boolean isDone() {
+ return runQueue.isEmpty() && list.isEmpty();
+ }
+
+ /**
+ * how many threads are actively running.
+ * @return
+ */
+ public int getRunQueueSize() {
+ return runQueue.size();
+ }
+
+ /**
+ * how many threads are waiting to run.
+ * @return
+ */
+ public int getWaitQueueSize() {
+ return list.size();
+ }
+
+ /**
+ * start processing. Use shutdown() to cleanly shutdown.
+ */
+ public void startup() {
+ startTime = System.currentTimeMillis();
+ startRunThread();
+ }
+
+ private void startRunThread() {
+ RunThread rt = new RunThread(this);
+ new Thread(rt).start();
+ }
+
+
+ private void remove(TaskWrapper tw) {
+ runQueue.remove(tw);
+ }
+
+ public String getDebugInfo() {
+ TaskWrapper taskWrapper = runQueue.peek();
+ if (taskWrapper != null)
+ return taskWrapper.task.toString();
+ return "(none)";
+ }
+
+
+ /**
+ * add waiting jobs to the run queue, launching them in a new thread
+ * as the runqueue becomes available.
+ */
+ public static class RunThread implements Runnable {
+ private ParallelProcessor pp;
+ public RunThread(ParallelProcessor pp) {
+ this.pp = pp;
+ }
+
+ public void run() {
+ Thread.currentThread().setName("Parallel Processor Run Thread");
+ int taskCount = 0;
+ while (!pp.shutDown) {
+
+ if (!pp.list.isEmpty()) {
+ ParallelTask toRun = pp.list.remove(0);
+ TaskWrapper tw = new TaskWrapper(pp, toRun, taskCount);
+ boolean success = false;
+ while (!success) {
+ success = pp.runQueue.offer(tw);
+ if (!success) {
+ try {
+ logger.debug("waiting to offer task {} to run queue", taskCount);
+ Thread.sleep(1000);
+ } catch (InterruptedException ie) {
+ pp.shutDown = true;
+ return;
+ }
+ }
+ }
+ logger.debug("inserted running task {}...", taskCount);
+ new Thread(tw).start();
+ taskCount++;
+
+ }
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException ie) {
+ logger.error("exception {}", ie.toString(), ie);
+ }
+ }
+ logger.info("CACHE COMPLETE: exiting after {} s ", pp.getRuntime()/1000);
+ }
+ }
+
+ /**
+ * Wrapper around the task that can be safely run as a thread.
+ */
+ public static class TaskWrapper implements Runnable {
+ private ParallelProcessor pp;
+ private ParallelTask task;
+ private int taskNumber;
+ private boolean started = false;
+ private boolean done = false;
+
+ public TaskWrapper(ParallelProcessor pp, ParallelTask task, int taskNumber) {
+ this.pp = pp;
+ this.task = task;
+ this.taskNumber = taskNumber;
+ }
+
+ public void run() {
+ Thread.currentThread().setName(task.toString() + "-" + taskNumber);
+ logger.debug("starting task {}", taskNumber);
+ started = true;
+ try {
+ task.run();
+ } catch (Exception e) {
+ logger.error("task {} exited with {}", taskNumber, e.toString(), e);
+ } finally {
+ logger.debug("completing task {}", taskNumber);
+ done = true;
+ pp.remove(this);
+ }
+ }
+ }
+}
diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/ParallelTask.java b/transitclock/src/main/java/org/transitclock/core/dataCache/ParallelTask.java
new file mode 100644
index 000000000..c2e862d8f
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/core/dataCache/ParallelTask.java
@@ -0,0 +1,8 @@
+package org.transitclock.core.dataCache;
+
+/**
+ * Represents a Task that can be run in parallel. See ParallelProcessor.
+ */
+public interface ParallelTask {
+ void run() throws Exception;
+}
diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/PredictionComparator.java b/transitclock/src/main/java/org/transitclock/core/dataCache/PredictionComparator.java
new file mode 100644
index 000000000..30ff6ec9b
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/core/dataCache/PredictionComparator.java
@@ -0,0 +1,21 @@
+package org.transitclock.core.dataCache;
+
+import java.util.Comparator;
+
+import org.transitclock.ipc.data.IpcPrediction;
+public class PredictionComparator implements Comparator {
+
+
+ @Override
+ public int compare(IpcPrediction p1, IpcPrediction p2) {
+
+ if(p1.getPredictionTime()>p2.getPredictionTime())
+ {
+ return 1;
+ }else if(p1.getPredictionTime()< p2.getPredictionTime())
+ {
+ return -1;
+ }
+ return 0;
+ }
+}
diff --git a/transitime/src/main/java/org/transitime/core/dataCache/PredictionDataCache.java b/transitclock/src/main/java/org/transitclock/core/dataCache/PredictionDataCache.java
similarity index 93%
rename from transitime/src/main/java/org/transitime/core/dataCache/PredictionDataCache.java
rename to transitclock/src/main/java/org/transitclock/core/dataCache/PredictionDataCache.java
index 19d897562..334025d56 100644
--- a/transitime/src/main/java/org/transitime/core/dataCache/PredictionDataCache.java
+++ b/transitclock/src/main/java/org/transitclock/core/dataCache/PredictionDataCache.java
@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with Transitime.org . If not, see .
*/
-package org.transitime.core.dataCache;
+package org.transitclock.core.dataCache;
import java.util.ArrayList;
import java.util.Collection;
@@ -26,18 +26,21 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.transitime.applications.Core;
-import org.transitime.core.PredictionGeneratorDefaultImpl;
-import org.transitime.core.VehicleState;
-import org.transitime.db.structs.Route;
-import org.transitime.db.structs.Stop;
-import org.transitime.db.structs.Trip;
-import org.transitime.gtfs.DbConfig;
-import org.transitime.ipc.data.IpcPrediction;
-import org.transitime.ipc.data.IpcPredictionsForRouteStopDest;
-import org.transitime.ipc.interfaces.PredictionsInterface.RouteStop;
-import org.transitime.utils.MapKey;
-import org.transitime.utils.Time;
+import org.transitclock.applications.Core;
+import org.transitclock.config.BooleanConfigValue;
+import org.transitclock.core.PredictionGeneratorDefaultImpl;
+import org.transitclock.core.VehicleState;
+import org.transitclock.db.structs.Route;
+import org.transitclock.db.structs.Stop;
+import org.transitclock.db.structs.Trip;
+import org.transitclock.gtfs.DbConfig;
+import org.transitclock.ipc.data.IpcPrediction;
+import org.transitclock.ipc.data.IpcPredictionsForRouteStopDest;
+import org.transitclock.ipc.interfaces.PredictionsInterface.RouteStop;
+import org.transitclock.utils.MapKey;
+import org.transitclock.utils.Time;
+
+import static org.transitclock.core.PredictionGeneratorDefaultImpl.isHistoricalPredictionForFutureStop;
/**
* For storing and retrieving predictions by stop.
@@ -66,6 +69,10 @@ public class PredictionDataCache {
private static PredictionDataCache singleton =
new PredictionDataCache();
+ protected static BooleanConfigValue returnArrivalPredictionForEndOfTrip = new BooleanConfigValue("transitclock.prediction.returnArrivalPredictionForEndOfTrip",
+ false,
+ "This set to false will not return arrival predictions of the last stop on a trip.");
+
// Contains lists of predictions per route/stop. Also want to group
// predictions by destination/trip head sign together so that can
// show such predictions separately. This is important for routes
@@ -193,7 +200,7 @@ public List getPredictions(
// schedule based predictions then generating predictions far into the
// future.
long maxPredictionEpochTime =
- Core.getInstance().getSystemTime()
+ getSystemTime()
+ PredictionGeneratorDefaultImpl
.getMaxPredictionsTimeSecs()
* Time.SEC_IN_MSECS;
@@ -213,8 +220,9 @@ public List getPredictions(
nonEndOfTripPredFound = true;
}
}
+ /* Is this the best place to filter out predictions. Would it be better to allow the consumer filter? */
boolean shouldFilterOutEndOfTripPreds =
- endOfTripPredFound && nonEndOfTripPredFound;
+ (endOfTripPredFound && nonEndOfTripPredFound && !returnArrivalPredictionForEndOfTrip.getValue());
// Make a copy of the prediction objects so that they cannot be
// modified by another thread while they are being accessed. This
@@ -252,14 +260,15 @@ public List getPredictions(
// Direction ID is OK so clone prediction and add to list
IpcPredictionsForRouteStopDest clone =
predictions.getClone(maxPredictionsPerStop,
- maxPredictionEpochTime, distanceToStop);
+ maxPredictionEpochTime, PredictionGeneratorDefaultImpl.getTerminatePredictionsAtTripEnd(), distanceToStop);
clonedPredictions.add(clone);
}
// If no predictions should still return a IpcPredictionsForRouteStopDest
// object so that the client can get route, stop, and direction info to
// display in the UI.
- if (clonedPredictions.size() == 0) {
+ // except if no routeShortName - Ipc requires this!
+ if (clonedPredictions.size() == 0 && routeShortName != null) {
IpcPredictionsForRouteStopDest pred =
new IpcPredictionsForRouteStopDest(routeShortName,
directionId, stopIdOrCode, distanceToStop);
@@ -452,6 +461,9 @@ public List getAllPredictions(
predictionsMap.values();
for (List predictionsForRouteStop : predictionsByRouteStop) {
for (IpcPredictionsForRouteStopDest predictionForRouteStopDest : predictionsForRouteStop) {
+ // with the historical prediction / future stop checks we need to clean up the
+ // cache more often
+ predictionForRouteStopDest.removeExpiredPredictions(getSystemTime());
IpcPredictionsForRouteStopDest clonedPrediction =
predictionForRouteStopDest.getClone(
maxPredictionsPerStop, maxSystemTimeForPrediction);
@@ -524,13 +536,17 @@ public void updatePredictions(List oldPredictionsForVehicle,
oldPrediction.getStopId(),
oldPrediction.getTrip().getHeadsign());
if (newPredsForVehicleByRouteStopDestMap.get(key) == null) {
- // Remove the old prediction
- removePrediction(oldPrediction);
+ long currentTime = getSystemTime();
+ // here we hang onto old prediction if its scheduled arrival is in the future
+ if (!isHistoricalPredictionForFutureStop(oldPrediction, currentTime)) {
+ // Remove the old prediction
+ removePrediction(oldPrediction);
+ }
}
}
}
}
-
+
/**
* To be called when vehicle is being made unpredictable. Removes the
* predictions.
@@ -538,10 +554,10 @@ public void updatePredictions(List oldPredictionsForVehicle,
* @param vehicleState
*/
public void removePredictions(VehicleState vehicleState) {
- logger.info("Removing predictions for vehicleId={}",
+ logger.info("Removing predictions for vehicleId={}",
vehicleState.getVehicleId());
List oldPredictions = vehicleState.getPredictions();
-
+
updatePredictions(oldPredictions, null);
}
diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/SkippedStopsManager.java b/transitclock/src/main/java/org/transitclock/core/dataCache/SkippedStopsManager.java
new file mode 100644
index 000000000..55b0e1e0d
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/core/dataCache/SkippedStopsManager.java
@@ -0,0 +1,59 @@
+package org.transitclock.core.dataCache;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import org.transitclock.config.LongConfigValue;
+import org.transitclock.ipc.data.IpcSkippedStop;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+public class SkippedStopsManager {
+
+ private static LongConfigValue skippedStopsCacheExpireSec =
+ new LongConfigValue("transitclock.avl.skippedStopsCacheExpireSec", 60l,
+ "The amount of time to keep a trip schedule status in cache.");
+
+ private Cache> skippedStopsCache;
+
+ // This is a singleton class
+ private static SkippedStopsManager singleton = new SkippedStopsManager();
+
+ /********************** Member Functions **************************/
+
+ /**
+ * Constructor made private because this is singleton class where
+ * getInstance() should be used to get the VehicleStateManager.
+ */
+ private SkippedStopsManager() {
+ skippedStopsCache = CacheBuilder.newBuilder()
+ .expireAfterWrite(skippedStopsCacheExpireSec.getValue(), TimeUnit.SECONDS)
+ .build();
+ }
+
+ /**
+ * Returns the singleton SkippedStopsManager
+ * @return
+ */
+ public static SkippedStopsManager getInstance() {
+ if(singleton == null){
+ synchronized (SkippedStopsManager.class){
+ if(singleton == null){
+ singleton = new SkippedStopsManager();
+ }
+ }
+ }
+ return singleton;
+ }
+
+ public void putAll(Map> skippedStopByTripMap){
+ skippedStopsCache.putAll(skippedStopByTripMap);
+ }
+
+ public HashMap> getAll(){
+ HashMap> skippedStopMap = new HashMap(skippedStopsCache.asMap());
+ return skippedStopMap;
+ }
+}
diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/StopArrivalDepartureCacheFactory.java b/transitclock/src/main/java/org/transitclock/core/dataCache/StopArrivalDepartureCacheFactory.java
new file mode 100644
index 000000000..fc91495f6
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/core/dataCache/StopArrivalDepartureCacheFactory.java
@@ -0,0 +1,46 @@
+package org.transitclock.core.dataCache;
+import org.transitclock.config.BooleanConfigValue;
+import org.transitclock.config.StringConfigValue;
+import org.transitclock.utils.ClassInstantiator;
+
+/**
+ * @author Sean Óg Crudden
+ *
+ *
+ */
+public class StopArrivalDepartureCacheFactory {
+ private static StringConfigValue className =
+ new StringConfigValue("transitclock.core.cache.stopArrivalDepartureCache",
+ "org.transitclock.core.dataCache.ehcache.StopArrivalDepartureCache",
+ "Specifies the class used to cache the arrival and departures for a stop.");
+
+ private static BooleanConfigValue smoothArrivalDepartures =
+ new BooleanConfigValue("transitclock.core.cache.smoothStopArrivalDepartures",
+ false,
+ "Correct for non-linear arrivals and departures on reading");
+
+ private static BooleanConfigValue verifyArrivalDepartures =
+ new BooleanConfigValue("transitclock.core.cache.verifyStopArrivalDepartures",
+ true,
+ "Correct for non-linear arrivals and departures on writing");
+
+ public static boolean enableSmoothinng() {
+ return smoothArrivalDepartures.getValue();
+ }
+
+ public static boolean enableVerification() {
+ return verifyArrivalDepartures.getValue();
+ }
+
+ private static StopArrivalDepartureCacheInterface singleton = null;
+
+ public static StopArrivalDepartureCacheInterface getInstance() {
+
+ if (singleton == null) {
+ singleton = ClassInstantiator.instantiate(className.getValue(),
+ StopArrivalDepartureCacheInterface.class);
+ }
+
+ return singleton;
+ }
+}
diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/StopArrivalDepartureCacheInterface.java b/transitclock/src/main/java/org/transitclock/core/dataCache/StopArrivalDepartureCacheInterface.java
new file mode 100644
index 000000000..eeea0ef8a
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/core/dataCache/StopArrivalDepartureCacheInterface.java
@@ -0,0 +1,223 @@
+package org.transitclock.core.dataCache;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.hibernate.Criteria;
+import org.hibernate.criterion.Order;
+import org.hibernate.criterion.Restrictions;
+import org.transitclock.applications.Core;
+import org.transitclock.db.structs.Arrival;
+import org.transitclock.db.structs.ArrivalDeparture;
+import org.transitclock.db.structs.Block;
+import org.transitclock.db.structs.Departure;
+import org.transitclock.db.structs.StopPath;
+import org.transitclock.db.structs.Trip;
+import org.transitclock.ipc.data.IpcArrivalDeparture;
+import org.transitclock.utils.Time;
+
+public abstract class StopArrivalDepartureCacheInterface {
+
+ private static final Logger logger = LoggerFactory.getLogger(StopArrivalDepartureCacheInterface.class);
+
+ abstract public List getStopHistory(StopArrivalDepartureCacheKey key);
+
+ abstract public StopArrivalDepartureCacheKey putArrivalDeparture(ArrivalDeparture arrivalDeparture);
+
+ abstract public void populateCacheFromDb(List results);
+ public void defaultPopulateCacheFromDb(List results) {
+ try {
+ for (ArrivalDeparture result : results) {
+ this.putArrivalDeparture(result);
+ //TODO might be better with its own populateCacheFromdb
+ DwellTimeModelCacheFactory.getInstance().addSample(result);
+ }
+ } catch (Throwable t) {
+ logger.error("StopArrivalDepartureCacheInterface failed with {}", t, t);
+ }
+ }
+
+ /**
+ * Force departures to be later than arrivals.
+ * @param results
+ * @return
+ */
+ public static List smoothArrivalDepartures(List results) {
+ if (!StopArrivalDepartureCacheFactory.enableSmoothinng()) return results;
+
+ List filtered = new ArrayList<>(results.size());
+
+ int adjustedCount = 0;
+ int totalCount = 0;
+ String lastTripId = null;
+ Long lastTime = null;
+ for (ArrivalDeparture result : results) {
+ totalCount++;
+
+ // as we are adding A/Ds, smooth any negative arrivals
+ if (lastTime != null && lastTripId != null
+ && lastTripId.equals(result.getTripId())
+ && result.getTime() <= lastTime) {
+ int adjustment = 0; // departure can have same time as arrival
+ if (result.isArrival()) {
+ adjustment = 1; // arrival needs to be greater than last departure
+ }
+ filtered.add(createArrivalDeparture(result, lastTime + adjustment));
+ if (adjustment > 0)
+ adjustedCount++;
+ lastTime = lastTime + adjustment;
+ } else {
+ filtered.add(result);
+ lastTime = result.getTime();
+ }
+ if (lastTripId == null || !lastTripId.equals(result.getTripId())) {
+ lastTripId = result.getTripId();
+ }
+ }
+
+ logger.info("ADJUSTED {}% of {} entries", ((double)adjustedCount / totalCount)*100, totalCount);
+ return filtered;
+ }
+
+ /**
+ * modify the time of the
+ * @param ad
+ * @param lastTime
+ * @return
+ */
+ public static ArrivalDeparture createArrivalDeparture(ArrivalDeparture ad, Long lastTime) {
+ if (ad.isArrival()) {
+ return createArrival((Arrival)ad, lastTime);
+ }
+ return createDeparture((Departure) ad, lastTime);
+ }
+
+ public static Arrival createArrival(Arrival ad, Long lastTime) {
+ return ad.withUpdatedTime(new Date(lastTime));
+ }
+
+ public static Departure createDeparture(Departure ad, long lastTime) {
+ return ad.withUpdatedTime(new Date(lastTime));
+ }
+
+ public static List createArrivalDeparturesCriteria(Criteria criteria, Date startDate, Date endDate) {
+ @SuppressWarnings("unchecked")
+ List results = criteria.add(Restrictions.between("time", startDate, endDate))
+ .addOrder(Order.asc("tripId"))
+ .addOrder(Order.asc("stopPathIndex"))
+ .addOrder(Order.desc("isArrival"))
+ .list();
+ if (!StopArrivalDepartureCacheFactory.enableSmoothinng()) return results;
+ return smoothArrivalDepartures(results);
+ }
+
+ /**
+ * Use the existing cache to ensure the departure is not before
+ * the arrival
+ * @param departure
+ * @return a departure with a time greater than the arrival time
+ */
+ public Departure verifyDeparture(Departure departure) {
+ if (!StopArrivalDepartureCacheFactory.enableVerification()) return departure;
+
+ StopArrivalDepartureCacheKey key = new StopArrivalDepartureCacheKey(departure.getStopId(), new Date(Time.getStartOfDay(departure.getAvlTime())));
+ List stopHistory = getStopHistory(key);
+ ArrivalDeparture arrivalForDeparture = findArrivalForDeparture(stopHistory, departure);
+ if (arrivalForDeparture == null) {
+ logger.debug("no arrival found for departure {}", departure);
+ return departure;
+ }
+ if (arrivalForDeparture.getTime() >= departure.getTime()) {
+ logger.debug("adjusting departure time by {}", arrivalForDeparture.getTime() - departure.getTime() + 1);
+ return createDeparture(departure, arrivalForDeparture.getTime() + 1);
+ }
+ return departure;
+ }
+
+ public Arrival verifyArrival(Arrival arrival) {
+ if (!StopArrivalDepartureCacheFactory.enableVerification()) return arrival;
+
+ Trip trip = Core.getInstance().getDbConfig().getTrip(arrival.getTripId());
+ if (trip == null) {
+ return arrival;
+ }
+ // go back one to stop path to find last departure
+ StopPath stopPath = trip.getStopPath(arrival.getStopPathIndex() - 1);
+ if (stopPath == null) {
+ return arrival;
+ }
+ StopArrivalDepartureCacheKey key = new StopArrivalDepartureCacheKey(stopPath.getStopId(), new Date(Time.getStartOfDay(arrival.getAvlTime())));
+
+ List stopHistory = getStopHistory(key);
+ ArrivalDeparture lastDeparture = findLastDeparture(stopHistory, stopPath.getStopId(), arrival.getVehicleId(), arrival.getStopPathIndex());
+ if (lastDeparture == null) {
+ logger.debug("no previous departure for arrival {}", arrival);
+ return arrival;
+ }
+ if (arrival.getTime() <= lastDeparture.getTime()) {
+ logger.debug("adjusting arrival time by {}", arrival.getTime() - lastDeparture.getTime() + 1);
+ return createArrival(arrival, lastDeparture.getTime() + 1);
+ }
+ return arrival;
+ }
+
+ private ArrivalDeparture findLastDeparture(List stopHistory, String departureStopId, String arrivalVehicleId, int arrivalStopPathIndex) {
+ if (stopHistory == null) return null;
+ ArrivalDeparture lastArrivalDeparture = null;
+ for (IpcArrivalDeparture ad : stopHistory) {
+ if (ad.getVehicleId().equals(arrivalVehicleId)
+ && ad.getStopId().equals(departureStopId)
+ && ad.getStopPathIndex() < arrivalStopPathIndex) {
+ if (lastArrivalDeparture == null || ad.getStopPathIndex() > lastArrivalDeparture.getStopPathIndex()) {
+ lastArrivalDeparture = createArrivalDeparture(ad);
+ }
+ }
+ }
+ return lastArrivalDeparture;
+ }
+
+ private ArrivalDeparture findArrivalForDeparture(List stopHistory, Departure departure) {
+ if (stopHistory == null) return null;
+ for (IpcArrivalDeparture ad : stopHistory) {
+ if (ad.getVehicleId().equals(departure.getVehicleId())
+ && ad.getStopId().equals(departure.getStopId())
+ && ad.getTripId().equals(departure.getTripId())
+ && ad.getStopPathIndex() == departure.getStopPathIndex()) {
+ return createArrivalDeparture(ad);
+ }
+ }
+ return null;
+ }
+
+ private ArrivalDeparture createArrivalDeparture(IpcArrivalDeparture ad) {
+ Block block = Core.getInstance().getDbConfig().getBlock(ad.getServiceId(), ad.getBlockId());
+ Trip trip = block.getTrip(ad.getTripId());
+ int tripIndex = block.getTripIndex(trip);
+
+ if (ad.isArrival()) {
+ Arrival a = new Arrival(ad.getVehicleId(),
+ ad.getTime(),
+ ad.getAvlTime(),
+ block,
+ tripIndex,
+ ad.getStopPathIndex(),
+ ad.getFreqStartTime(),
+ null /* stopPathId not present */);
+ return a;
+ }
+ Departure d = new Departure(ad.getVehicleId(),
+ ad.getTime(),
+ ad.getAvlTime(),
+ block,
+ tripIndex,
+ ad.getStopPathIndex(),
+ ad.getFreqStartTime(),
+ ad.getDwellTime(),
+ null /* stopPathId not present */);
+ return d;
+ }
+
+}
\ No newline at end of file
diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/StopArrivalDepartureCacheKey.java b/transitclock/src/main/java/org/transitclock/core/dataCache/StopArrivalDepartureCacheKey.java
new file mode 100755
index 000000000..2feafca76
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/core/dataCache/StopArrivalDepartureCacheKey.java
@@ -0,0 +1,73 @@
+package org.transitclock.core.dataCache;
+
+import java.io.Serializable;
+import java.util.Calendar;
+import java.util.Date;
+/**
+ * @author Sean Og Crudden
+ *
+ */
+public class StopArrivalDepartureCacheKey implements Serializable {
+
+
+ private static final long serialVersionUID = 2466653739981305005L;
+ private String stopid;
+ private Date date;
+ public StopArrivalDepartureCacheKey(String stopid, Date date) {
+ super();
+ setDate(date);
+ this.stopid=stopid;
+ }
+ public String getStopid() {
+ return stopid;
+ }
+
+ public Date getDate() {
+ return date;
+ }
+ public void setDate(Date date) {
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTime(date);
+
+ calendar.set(Calendar.HOUR_OF_DAY, 0);
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.SECOND, 0);
+ calendar.set(Calendar.MILLISECOND, 0);
+
+ this.date = calendar.getTime();
+ }
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((date == null) ? 0 : date.hashCode());
+ result = prime * result + ((stopid == null) ? 0 : stopid.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;
+ StopArrivalDepartureCacheKey other = (StopArrivalDepartureCacheKey) obj;
+ if (date == null) {
+ if (other.date != null)
+ return false;
+ } else if (!date.equals(other.date))
+ return false;
+ if (stopid == null) {
+ if (other.stopid != null)
+ return false;
+ } else if (!stopid.equals(other.stopid))
+ return false;
+ return true;
+ }
+ @Override
+ public String toString() {
+ return "StopArrivalDepartureCacheKey [stopid=" + stopid + ", date=" + date + "]";
+ }
+
+}
diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/StopEvents.java b/transitclock/src/main/java/org/transitclock/core/dataCache/StopEvents.java
new file mode 100644
index 000000000..6b726432e
--- /dev/null
+++ b/transitclock/src/main/java/org/transitclock/core/dataCache/StopEvents.java
@@ -0,0 +1,71 @@
+package org.transitclock.core.dataCache;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.transitclock.ipc.data.IpcArrivalDeparture;
+public class StopEvents implements Serializable {
+ /**
+ *
+ */
+ private static final long serialVersionUID = 7968075904267156083L;
+ public List events;
+
+ public List getEvents() {
+ return events;
+ }
+
+ public void setEvents(List events) {
+ this.events = events;
+ Collections.sort(this.events, new IpcArrivalDepartureComparator());
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((events == null) ? 0 : events.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;
+ StopEvents other = (StopEvents) obj;
+ if (events == null) {
+ if (other.events != null)
+ return false;
+ } else if (!events.equals(other.events))
+ return false;
+ return true;
+ }
+
+ public StopEvents() {
+ super();
+ }
+
+ public StopEvents(List events) {
+ super();
+ this.events = events;
+
+ Collections.sort(this.events, new IpcArrivalDepartureComparator());
+
+ }
+
+ public void addEvent(IpcArrivalDeparture event)
+ {
+ if(this.events==null)
+ {
+ events=new ArrayList