diff --git a/.gitignore b/.gitignore index 4dacc6c7f..5bf5bca9e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,47 +1,20 @@ *.class +*.project +*.classpath *.iml .idea/ - # Package Files # *.jar *.war *.ear +*.log /bin/ -/bin/ -/bin/ -/bin/ -/bin/ -/bin/ -/bin/ -/bin/ -/bin/ -/bin/ -/bin/ -/bin/ -/bin/ -/bin/ -/bin/ -/bin/ -/bin/ -/bin/ -/bin/ -/bin/ -/bin/ -/bin/ -/bin/ -/bin/ -/bin/ -/bin/ -/bin/ -/bin/ -/bin/ -/bin/ -/bin/ -/bin/ -/bin/ -/bin/ -/bin/ -/bin/ -/bin/ -/bin/ -/bin/ +/.settings/ +.DS_Store + + +transitime/target/ + +transitimeApi/target/ + +transitimeWebapp/target/ diff --git a/.project b/.project deleted file mode 100644 index 9c0727c3d..000000000 --- a/.project +++ /dev/null @@ -1,42 +0,0 @@ - - - api - - - - - - org.eclipse.wst.jsdt.core.javascriptValidator - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.wst.common.project.facet.core.builder - - - - - org.eclipse.wst.validation.validationbuilder - - - - - org.eclipse.m2e.core.maven2Builder - - - - - - org.eclipse.m2e.core.maven2Nature - org.eclipse.jem.workbench.JavaEMFNature - org.eclipse.wst.common.modulecore.ModuleCoreNature - org.eclipse.wst.common.project.facet.core.nature - org.eclipse.jdt.core.javanature - org.eclipse.wst.jsdt.core.jsNature - - diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..68a107639 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,27 @@ +language: java +jdk: openjdk8 +cache: + directories: + - "$HOME/.m2" +script: mvn install -DskipTests +deploy: + provider: releases + api_key: + secure: gT/9kJv4oLzhXUsVOpASZi41BTiXRDohrryYg7o8txZG6caeki2B5lsLCYQlONe09IR3Mv2BwY6hcPX+GJlJa1Npvfn3Q9Th5IbYLAglRQoqDN/d3iyY8WhEY/ZqbUZ7vJXdpPNjwcyoA0dd2aA8N9pOC8QaFK43n47LCDAe+F16uGyxQN72TX4jCIANOvagVq8iUZtJUByFyubIGEHv0boEQNdODu5NdwzHsd0anCSd+Z/8zIu/rV5Ck1J5ecslqVJFSCs0LRrA3aFu31tf87KgT4+HomC79WHeo3hqrzCZ+PaqPJGrxYQ5j02Lw7rDBX3C8U9SqiP8piKKTXoHflNTWp4rrBT7Z/KehRChfXPvj3l+Oo6Nlz8e49du9HGpMbDRF6IUA5NiCrUotMR+MRMPrCmJcyLwaupPPr4Ha0+Tiy72T7Rp4oYpByuBvIrp8lzEZjKgKv4kpIkSiYhep7N3SoU5S98y6/S/FPWJvLceJrusi/LNFGCcMSY9Ntn4q1qKNt426eqksymcSy5G36DYopRDYg/z3vlTX1lyzPEWEkQv7PPlZu9MFJh0+7pUpadxuRLSiIpL9PtJ1sDPUvGVYSKGnBH2H64Wd8D13tnfPbtqPAsT+9bLFBk0mUbEGqXp93vMSs/Q9n+LC5+29rSdsO2IIR7gbLEquH/9rGo= + file: + - "transitclock/target/Core.jar" + - "transitclock/target/CreateAPIKey.jar" + - "transitclock/target/CreateWebAgency.jar" + - "transitclock/target/GtfsFileProcessor.jar" + - "transitclock/target/RmiQuery.jar" + - "transitclock/target/ScheduleGenerator.jar" + - "transitclock/target/SchemaGenerator.jar" + - "transitclock/target/transitclockCore-2.0.0-SNAPSHOT.jar" + - "transitclock/target/UpdateTravelTimes.jar" + - "transitclockWebapp/target/web.war" + - "transitclockApi/target/api.war" + skip_cleanup: true + on: + repo: TheTransitClock/transitime + tags: false + branch: develop diff --git a/README.md b/README.md index 18346aeb5..a53b074ab 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -core +core [![Build Status](https://travis-ci.org/TheTransitClock/transitime.svg?branch=develop)](https://travis-ci.org/TheTransitClock/transitime) ==== The complete core Java software for the Transitime real-time transit information project. Transitime is a project created and managed by Swiftly, Inc. (www.goswift.ly). @@ -23,6 +23,8 @@ GTFSFileProcessor.java will read a GTFS file into this database structure.
Core.java is as the name implies is the workhorse of the system.
RmiQuery.java allows you make queries to the server run in core from the command line.
CreateAPIKey.java a test app to allow you create test/demo key to access REST api webapp.
+CreateWebAgency.java is used to create and agency that will work in transitimeWebapp.
+UpdateTravelTimes.java is used to update travel time estimates based on historical data in the system.
Details on how to run each of these and their respective parameters are in the README for the transitime module. @@ -30,3 +32,5 @@ Once this is set up the next step is to set up the transitimeApi which is a REST The transitimeWebapp in turn is a web application which uses the transitTimeAPI to provided a user interface. This is a war file which can be deployed into Tomcat. This connects to the database and the connection information is configured in hibernate.cfg.xml in the src/main/resources directory. Currently this needs to be deployed on the same server as the API. +The transitimeQuickStart can be built with mvn install and ran using java -jar transitimeQuickStart it is currently a work in progress but the gui elements can be seen. + diff --git a/pom.xml b/pom.xml index d6a5786f5..fd468a5b9 100644 --- a/pom.xml +++ b/pom.xml @@ -1,15 +1,86 @@ - - 4.0.0 - transitime - transitime-all - 0.0.1-SNAPSHOT - pom - transitime-all - http://www.transitime.org// - - transitime - transitimeApi - transitimeWebapp - + 4.0.0 + TheTransitClock + transitclock + 2.0.49-cs-SNAPSHOT + pom + transitclock + http://www.transitclock.org// + + transitclock + transitclockApi + transitclockWebapp + transitclockTraccarClient + transitclockBarefootClient + + + + + + skip-integration-tests + + true + + + + include-integration-tests + + transitclockIntegration + transitclock + transitclockApi + transitclockWebapp + transitclockQuickStart + transitclockTraccarClient + transitclockBarefootClient + + + + + scm:git:http://github.com/sheldonabrown/core.git + scm:git:ssh://git@github.com/sheldonabrown/core.git + http://github.com/sheldonabrown/core + HEAD + + + + releases-camsys-public-repo + Camsys Public Release Repo + s3://repo.camsys-apps.com/releases/ + + + snapshots-camsys-public-repo + Camsys Public Snapshot Repo + s3://repo.camsys-apps.com/snapshots/ + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.10.3 + + -Xdoclint:none + + + + + attach-javadocs + + jar + + + + + + + + com.github.platform-team + aws-maven + 6.0.0 + + + diff --git a/transitclock/.gitignore b/transitclock/.gitignore new file mode 100644 index 000000000..a2604ae61 --- /dev/null +++ b/transitclock/.gitignore @@ -0,0 +1,12 @@ +*.class +*.project +*.classpath +*.iml +.idea/ +# Package Files # +*.jar +*.war +*.ear +/target/ +/bin/ +/.settings/ \ No newline at end of file diff --git a/transitclock/README.md b/transitclock/README.md new file mode 100755 index 000000000..572ab39b0 --- /dev/null +++ b/transitclock/README.md @@ -0,0 +1,242 @@ +There are several main classes which are used in the set up of the system. These can be run directly by specifying the class to run or by using the executable jar in the target directory. + +The steps to set up the system are + + + +generateDatabaseSchema.jar -- Main class: org.transitclock.applications.SchemaGenerator +================================= +ISSUE: skip to ISSUE below for the moment as there is a classloader issue when using onejar. +
+The jar generateDatabaseSchema.jar can be used to re-generate the SQL required to create the database structures required to run transiTime. It generates three files in the specified directory. A file is generated for each supported database type. (Postgres, Oracle, Mysql). The script generated will drop tables that already exist. +
+ + + +``` +usage: + java -jar generateDatabaseSchema.jar
+ -o,--outputDirectory This is the directory to output the sql
+ -p,--hibernatePackagePath This is the path to the package + containing the hibernate annotated java
+ classes
+``` + + +``` +example: + java -jar generateDatabaseSchema.jar -o c:\temp\ -p org.transitclock.db.structs +``` +To create all tables require you to support the core and the webapp you could run + +``` + java -jar generateDatabaseSchema.jar -o c:\temp\core\ -p org.transitclock.db.structs + java -jar generateDatabaseSchema.jar -o c:\temp\web\ -p org.transitclock.db.webstructs +``` + +Once these commands have been run you should run the sql created in the files in the core and web directory in your database. + +ISSUE: This works in eclipse by executing the class but not on command line using the executable jar. It is an issue with the ClassLoader and onejar. Maybe better to create using mvn exec plugin. + +The following will can be run from the transitclock directory under core and will place the required SQL in the target directory. +``` +mvn exec:java -Dexec.mainClass="org.transitclock.applications.SchemaGenerator" -Dexec.args="-o target -p org.transitclock.db.structs" +mvn exec:java -Dexec.mainClass="org.transitclock.applications.SchemaGenerator" -Dexec.args="-o target -p org.transitclock.db.webstructs" +```` + +processGTFSFile.jar -- Main class: org.transitclock.applications.GTFSFileProcessor +================================= +This class the usage can be got from specifying the -h option on its own. + +``` +usage: java GTFSFileProcessor.jar [-c ] [-combineRouteNames] + [-defaultWaitTimeAtStopMsec ] [-gtfsDirectoryName ] + [-gtfsUrl ] [-gtfsZipFileName ] [-h] + [-maxDistanceForEliminatingVertices ] [-maxSpeedKph ] + [-maxStopToPathDistance ] [-maxTravelTimeSegmentLength ] + [-n ] [-pathOffsetDistance ] [-regexReplaceFile + ] [-storeNewRevs] [-supplementDir ] + [-trimPathBeforeFirstStopOfTrip] [-unzipSubdirectory ] +args: + -c,--config Specifies configuration file to + read in. Needed for specifying + how to connect to database. + -combineRouteNames Combines short and long route + names to create full name. + -defaultWaitTimeAtStopMsec For initial travel times before + AVL data used to refine them. + Specifies how long vehicle is + expected to wait at the stop. + Default is 10,000 msec (10 + seconds). + -gtfsDirectoryName Directory where unzipped GTFS + file are. Can be used if already + have current version of GTFS data + and it is already unzipped. + -gtfsUrl URL where to get GTFS zip file + from. It will be copied over, + unzipped, and processed. + -gtfsZipFileName Local file name where the GTFS + zip file is. It will be unzipped + and processed. + -h Display usage and help info. + -maxDistanceForEliminatingVertices For consolidating vertices for a + path. If have short segments that + line up then might as combine + them. If a vertex is off the rest + of the path by only the distance + specified then the vertex will be + removed, thereby simplifying the + path. Value is in meters. Default + is 0.0m, which means that no + vertices will be eliminated. + -maxSpeedKph For initial travel times before + AVL data used to refine them. + Specifies maximum speed a vehicle + can go between stops when + determining schedule based travel + times. Default is 97kph (60mph). + -maxStopToPathDistance How far a stop can be away from + the stopPaths. If the stop is + further away from the distance + then a warning message will be + output and the path will be + modified to include the stop. + -maxTravelTimeSegmentLength For determining how many travel + time segments should have between + a pair of stops. Default is + 200.0m, which means that many + stop stopPaths will have only a + single travel time segment + between stops. + -n,--notes Description of why processing the + GTFS data + -pathOffsetDistance When set then the shapes from + shapes.txt are offset to the + right by this distance in meters. + Useful for when shapes.txt is + street centerline data. By + offsetting the shapes then the + stopPaths for the two directions + won't overlap when zoomed in on + the map. Can use a negative + distance to adjust stopPaths to + the left instead of right, which + could be useful for countries + where one drives on the left side + of the road. + -regexReplaceFile File that contains pairs or regex + and replacement text. The names + in the GTFS files are processed + using these replacements to fix + up spelling mistakes, + capitalization, etc. + -storeNewRevs Stores the config and travel time + revs into ActiveRevisions in + database. + -supplementDir Directory where supplemental GTFS + files can be found. These files + are combined with the regular + GTFS files. Useful for additing + additional info such as + routeorder and hidden. + -trimPathBeforeFirstStopOfTrip For trimming off path from + shapes.txt for before the first + stops of trips. Useful for when + the shapes have problems at the + beginning, which is suprisingly + common. + -unzipSubdirectory For when unzipping GTFS files. If + set then the resulting files go + into this subdirectory. +``` + +``` +example: + java -Xmx1000M -Dtransitclock.core.agencyId=02 -jar GTFSFileProcessor.jar -c d:/transiTime/transiTimeConfig.xml -gtfsDirectoryName d:/transiTime/updated_google_transit_irishrail/ -storeNewRevs -maxTravelTimeSegmentLength 1000 +``` + + +WORK IN PROGRESS........................ +Improving Predictions +================================= +UpdateTravelTimes.java is a main application which looks at historical data in the system and updates the estimated times where there is relavent historical data. + +This takes one or two date arguments. It is intended to process one days data and update the travel times in the database based on this data. + +If two dates supplied it processes all data within the date range. If a single date provided it processes all data for that day. + +Date in is format MM-dd-yyyy + +Example using maven to execute +```` + + +mvn exec:java -Dtransitclock.configFiles=/home/scrudden/workspace/transitimeconfig/transiTimeConfig.xml -Dtransitclock.logging.dir=/home/scrudden/workspace/core/logs/ -Dexec.mainClass="org.transitclock.applications.UpdateTravelTimes" -Dexec.args="08-24-2015" +```` +Configuration File for core.java +============================== +Core can read its configuration from an xml configuration file. The xml file is not based on a schema but on nested tags that match the hierachy specified in the names in the source. The main work is done by the modules which are configured in the semi colon delimited list in the optionModuleList tag. The choice of module and the their individual configuration is a complex task which will be specific to each transit agency. + +The database and hibernate config file are specified in this file. + +/home/scrudden/workspace/transitimeconfig/transiTimeConfig.xml +```` + + + + + + org.transitclock.custom.irishrail.NexalaAvlModule + + + + 1200 + 1200 + 10000 + 7200 + 02 + + + + http://0.0.0.0:8092/vehiclePositions + http://0.0.0.0:8091/vehiclePositions + -10.725 + -5.35 + 51.35 + 55.45 + 60 + + 62.6 + + + transitime + 127.0.0.1:5432 + postgresql + ogcrudden + password + + + /home/ogcrudden/workspace/transitimeconfig/postgres_hibernate.cfg.xml + + +```` diff --git a/transitclock/pom.xml b/transitclock/pom.xml new file mode 100755 index 000000000..cc7836589 --- /dev/null +++ b/transitclock/pom.xml @@ -0,0 +1,616 @@ + + 4.0.0 + + TheTransitClock + transitclock + 2.0.49-cs-SNAPSHOT + + transitclockCore + transitclockCore + + UTF-8 + + + + + + false + + bintray-kevinlee-maven + bintray + http://dl.bintray.com/kevinlee/maven + + + + + repo.obaweb.org + http://repo.camsys-apps.com/releases + + + + + TheTransitClock + transitclockBarefootClient + 2.0.49-cs-SNAPSHOT + + + TheTransitClock + transitclockTraccarClient + 2.0.49-cs-SNAPSHOT + + + com.esri.geometry + esri-geometry-api + 1.1 + + + + com.simontuffs + one-jar-boot + 0.97.3 + + + + mysql + mysql-connector-java + 5.1.49 + + + org.postgresql + postgresql + 9.3-1103-jdbc41 + + + org.hibernate + hibernate-core + 4.3.9.Final + + + org.hibernate.common + hibernate-commons-annotations + 4.0.5.Final + + + org.hibernate + hibernate-c3p0 + 4.3.9.Final + + + + xml-apis + xml-apis + 1.4.01 + + + + ch.qos.logback + logback-classic + 1.2.3 + + + org.hsqldb + hsqldb + 2.3.2 + + + + com.amazonaws + aws-java-sdk + 1.10.16 + + + + org.apache.commons + commons-lang3 + 3.3.2 + + + + org.jdom + jdom + 2.0.2 + + + + com.google.transit + gtfs-realtime-bindings + 0.0.4 + + + + commons-cli + commons-cli + 1.2 + + + + + org.hornetq + hornetq-core-client + 2.3.25.Final + + + org.hornetq + hornetq-jms-client + 2.3.25.Final + + + + org.java-websocket + Java-WebSocket + 1.3.0 + + + + org.json + json + 20140107 + + + + org.apache.commons + commons-csv + 1.1 + + + + net.jcip + jcip-annotations + 1.0 + + + + org.jasypt + jasypt + 1.9.2 + + + + javax.mail + mail + 1.4.7 + + + commons-beanutils + commons-beanutils + 1.9.2 + + + + org.locationtech.jts + jts-core + 1.18.0 + + + + junit + junit + 4.11 + test + + + org.mockito + mockito-core + 3.2.4 + test + + + + com.google.guava + guava + 18.0 + + + org.ehcache + ehcache + 3.4.0 + + + org.slf4j + slf4j-api + + + + + + com.esotericsoftware + kryo + 4.0.0 + + + + org.apache.commons + commons-jcs-core + 2.2 + + + + javax.servlet + javax.servlet-api + 3.0.1 + provided + + + + com.github.haifengl + smile-core + 1.5.1 + + + + com.j256.cloudwatchlogbackappender + cloudwatchlogbackappender + 1.11 + + + + com.github.haifengl + smile-core + 1.5.1 + + + + net.spy + spymemcached + 2.12.3 + + + com.google.inject + guice + 4.1.0 + + + + + + maven-compiler-plugin + 2.5.1 + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-shade-plugin + + + shade-UpdateTravelTimes + package + + shade + + + + + org.transitclock.applications.UpdateTravelTimes + + + true + UpdateTravelTimes + + + + shade-Core + package + + shade + + + + + org.transitclock.applications.Core + + + true + Core + + + + shade-SchemaGenerator + package + + shade + + + + + org.transitclock.applications.SchemaGenerator + + + true + SchemaGenerator + + + + shade-ScheduleGenerator + package + + shade + + + + + org.transitclock.applications.ScheduleGenerator + + + true + ScheduleGenerator + + + + shade-GtfsFileProcessor + package + + shade + + + + + org.transitclock.applications.GtfsFileProcessor + + + true + GtfsFileProcessor + + + + shade-RmiQuery + package + + shade + + + + + org.transitclock.applications.RmiQuery + + + true + RmiQuery + + + + shade-CreateAPIKey + package + + shade + + + + + org.transitclock.applications.CreateAPIKey + + + true + CreateAPIKey + + + + shade-CreateWebAgency + package + + shade + + + + + org.transitclock.applications.CreateWebAgency + + + true + CreateWebAgency + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + make-assembly + + package + + + single + + + + jar-with-dependencies + + + + org.transitclock.applications.Core + + + + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.5 + + + + **/*IntegrationTest.java + + + + log4j.configuration + log4j-stdout.xml + + + ${argLine} + + + + + integration-tests + integration-test + + test + + + false + + none + + + **/*IntegrationTest.java + + + false + + + + + + + + + + runCore + + + + org.codehaus.mojo + exec-maven-plugin + 1.1.1 + + + test + + java + + + org.transitclock.applications.Core + + arg0 + arg1 + + + + + + + + + + dbTest + + + + org.codehaus.mojo + exec-maven-plugin + 1.1.1 + + + test + + java + + + org.transitclock.applications.DbTest + + arg0 + arg1 + + + + + + + + + + PredictionsAccuracyIntegrationTest + + + + org.codehaus.mojo + truezip-maven-plugin + 1.2 + + + unzip-database + + copy + + test + + true + + ${project.basedir}/src/test/resources/database/transitime_test.zip + ${project.basedir}/src/test/resources/database/data + + + + + + + org.onebusaway.plugins + maven-hsqldb-plugin + + 1.0.1 + + + user-database-start + test + + run + + + true + ${project.basedir}/src/test/resources/database/data/transitime_test + 9001 + + + + + + org.codehaus.mojo + exec-maven-plugin + 1.1.1 + + + test + test-case-execution + + java + + + org.transitclock.applications.PredictionsAccuracyIntegrationTest + -c transitclock/src/test/resources/transiTimeConfigIntegrationTest.xml -gtfsDirectoryName transitclock/src/test/resources/wmata_gtfs -storeNewRevs -maxTravelTimeSegmentLength 1000 + + + + + + + + + + diff --git a/transitclock/src/main/java/org/transitclock/DoNotDelete.java b/transitclock/src/main/java/org/transitclock/DoNotDelete.java new file mode 100644 index 000000000..466b571ee --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/DoNotDelete.java @@ -0,0 +1,5 @@ +package org.transitclock; + +public class DoNotDelete { + +} diff --git a/transitime/src/main/java/org/transitime/applications/AvlReportsFromDbToCsv.java b/transitclock/src/main/java/org/transitclock/applications/AvlReportsFromDbToCsv.java similarity index 89% rename from transitime/src/main/java/org/transitime/applications/AvlReportsFromDbToCsv.java rename to transitclock/src/main/java/org/transitclock/applications/AvlReportsFromDbToCsv.java index a9fca5e98..352afa918 100644 --- a/transitime/src/main/java/org/transitime/applications/AvlReportsFromDbToCsv.java +++ b/transitclock/src/main/java/org/transitclock/applications/AvlReportsFromDbToCsv.java @@ -14,15 +14,16 @@ * 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.text.ParseException; import java.util.Date; import java.util.List; -import org.transitime.avl.AvlCsvWriter; -import org.transitime.db.structs.AvlReport; -import org.transitime.utils.Time; -import org.transitime.utils.TimeZoneSetter; + +import org.transitclock.avl.AvlCsvWriter; +import org.transitclock.db.structs.AvlReport; +import org.transitclock.utils.Time; +import org.transitclock.utils.TimeZoneSetter; /** * For reading AvlReport data from database and writing it to a CSV file. Useful diff --git a/transitime/src/main/java/org/transitime/applications/ConvertGtfsRtToCsvFile.java b/transitclock/src/main/java/org/transitclock/applications/ConvertGtfsRtToCsvFile.java similarity index 90% rename from transitime/src/main/java/org/transitime/applications/ConvertGtfsRtToCsvFile.java rename to transitclock/src/main/java/org/transitclock/applications/ConvertGtfsRtToCsvFile.java index b36409637..3b5b866b9 100644 --- a/transitime/src/main/java/org/transitime/applications/ConvertGtfsRtToCsvFile.java +++ b/transitclock/src/main/java/org/transitclock/applications/ConvertGtfsRtToCsvFile.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.applications; +package org.transitclock.applications; import java.util.ArrayList; import java.util.Collection; @@ -26,12 +26,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.avl.AvlCsvWriter; -import org.transitime.config.BooleanConfigValue; -import org.transitime.config.StringConfigValue; -import org.transitime.config.StringListConfigValue; -import org.transitime.db.structs.AvlReport; -import org.transitime.feed.gtfsRt.GtfsRtVehiclePositionsReader; +import org.transitclock.avl.AvlCsvWriter; +import org.transitclock.config.BooleanConfigValue; +import org.transitclock.config.StringConfigValue; +import org.transitclock.config.StringListConfigValue; +import org.transitclock.db.structs.AvlReport; +import org.transitclock.feed.gtfsRt.GtfsRtVehiclePositionsReader; /** * Reads in a GTFS-realtime file for a Vehicle Positions feed and converts it @@ -50,7 +50,7 @@ private 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"); @@ -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(); + } + events.add(event); + Collections.sort(this.events, new IpcArrivalDepartureComparator()); + } + +} diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/StopEventsKyroSerializer.java b/transitclock/src/main/java/org/transitclock/core/dataCache/StopEventsKyroSerializer.java new file mode 100644 index 000000000..26fe3b057 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/dataCache/StopEventsKyroSerializer.java @@ -0,0 +1,40 @@ +package org.transitclock.core.dataCache; + +import java.io.ByteArrayOutputStream; +import java.nio.ByteBuffer; + +import org.ehcache.core.spi.service.FileBasedPersistenceContext; +import org.ehcache.spi.serialization.Serializer; +import org.ehcache.spi.serialization.SerializerException; +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.io.ByteBufferInputStream; +import com.esotericsoftware.kryo.io.Input; +import com.esotericsoftware.kryo.io.Output; + +public class StopEventsKyroSerializer implements Serializer { + + private static final Kryo kryo = new Kryo(); + public StopEventsKyroSerializer(ClassLoader classLoader) { + } + public StopEventsKyroSerializer(ClassLoader classLoader, FileBasedPersistenceContext persistenceContext) { + } + @Override + public boolean equals(StopEvents arg0, ByteBuffer arg1) throws ClassNotFoundException, SerializerException { + return arg0.equals(read(arg1)); + } + + @Override + public StopEvents read(ByteBuffer arg0) throws ClassNotFoundException, SerializerException { + Input input = new Input(new ByteBufferInputStream(arg0)) ; + return kryo.readObject(input, StopEvents.class); + } + + @Override + public ByteBuffer serialize(StopEvents arg0) throws SerializerException { + Output output = new Output(new ByteArrayOutputStream()); + kryo.writeObject(output, arg0); + output.close(); + return ByteBuffer.wrap(output.getBuffer()); + } + +} diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/StopPathCacheKey.java b/transitclock/src/main/java/org/transitclock/core/dataCache/StopPathCacheKey.java new file mode 100755 index 000000000..2e42b06b0 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/dataCache/StopPathCacheKey.java @@ -0,0 +1,154 @@ +package org.transitclock.core.dataCache; + +import java.util.Date; + +import org.transitclock.core.Indices; +/** + * @author Sean Óg Crudden + * + */ +public class StopPathCacheKey implements java.io.Serializable { + + + /** + * + */ + private static final long serialVersionUID = 9119654046491298858L; + private String tripId; + private Integer stopPathIndex; + + /* this is only set for frequency based trips otherwise null. This is seconds from midnight */ + private Long startTime = null ; + + private boolean travelTime=true; + + public boolean isTravelTime() { + return travelTime; + } + + public StopPathCacheKey(String tripId, Integer stopPathIndex) + { + super(); + + this.tripId = tripId; + this.stopPathIndex = stopPathIndex; + this.travelTime=true; + this.startTime=null; + } + + public StopPathCacheKey(String tripId, Integer stopPathIndex, boolean travelTime) { + super(); + + this.tripId = tripId; + this.stopPathIndex = stopPathIndex; + this.travelTime=travelTime; + this.startTime = null; + } + public StopPathCacheKey(String tripId, Integer stopPathIndex, boolean travelTime, Long startTime) { + super(); + + this.tripId = tripId; + this.stopPathIndex = stopPathIndex; + this.travelTime=travelTime; + this.startTime = startTime; + } + + public StopPathCacheKey(StopPathCacheKey key) { + + this.stopPathIndex=new Integer(key.getStopPathIndex()); + + this.tripId=new String(key.getTripId()); + + this.travelTime=key.travelTime; + + this.startTime=new Long(key.getStartTime()); + } + + + public StopPathCacheKey(Indices indices) { + super(); + + this.stopPathIndex=indices.getStopPathIndex(); + + int tripIndex = indices.getTripIndex(); + + this.tripId=indices.getBlock().getTrip(tripIndex).getId(); + + this.travelTime=true; + } + + public String getTripId() { + return tripId; + } + + public Long getStartTime() { + return startTime; + } + + public void setTripId(String tripId) { + this.tripId = tripId; + } + + + public Integer getStopPathIndex() { + return stopPathIndex; + } + + + public void setStopPathIndex(Integer stopPathIndex) { + this.stopPathIndex = stopPathIndex; + } + + @Override + public String toString() { + return "StopPathCacheKey [tripId=" + tripId + ", stopPathIndex=" + stopPathIndex + ", startTime=" + startTime + + ", travelTime=" + travelTime + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((startTime == null) ? 0 : startTime.hashCode()); + result = prime * result + ((stopPathIndex == null) ? 0 : stopPathIndex.hashCode()); + result = prime * result + (travelTime ? 1231 : 1237); + 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; + StopPathCacheKey other = (StopPathCacheKey) obj; + if (startTime == null) { + if (other.startTime != null) + return false; + } else if (!startTime.equals(other.startTime)) + return false; + if (stopPathIndex == null) { + if (other.stopPathIndex != null) + return false; + } else if (!stopPathIndex.equals(other.stopPathIndex)) + return false; + if (travelTime != other.travelTime) + return false; + if (tripId == null) { + if (other.tripId != null) + return false; + } else if (!tripId.equals(other.tripId)) + return false; + return true; + } + + + +} + + + + diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/StopPathPredictionCache.java b/transitclock/src/main/java/org/transitclock/core/dataCache/StopPathPredictionCache.java new file mode 100755 index 000000000..7ce48bc9c --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/dataCache/StopPathPredictionCache.java @@ -0,0 +1,86 @@ +package org.transitclock.core.dataCache; + +import org.ehcache.Cache; +import org.ehcache.CacheManager; +import org.ehcache.Status; +import org.ehcache.config.builders.CacheManagerBuilder; +import org.ehcache.xml.XmlConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.db.structs.PredictionForStopPath; + +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +public class StopPathPredictionCache implements StopPathPredictionCacheInterface{ + final private static String cacheName = "StopPathPredictionCache"; + private static StopPathPredictionCache singleton = new StopPathPredictionCache(); + private static final Logger logger = LoggerFactory + .getLogger(StopPathPredictionCache.class); + + private Cache cache = null; + final URL xmlConfigUrl = getClass().getResource("/ehcache.xml"); + + public static StopPathPredictionCache getInstance() { + return singleton; + } + + public StopPathPredictionCache() { + + XmlConfiguration xmlConfig = new XmlConfiguration(xmlConfigUrl); + + CacheManager cm = CacheManagerBuilder.newCacheManager(xmlConfig); + + if(cm.getStatus().compareTo(Status.AVAILABLE)!=0) + cm.init(); + + cache = cm.getCache(cacheName, StopPathCacheKey.class, StopPredictions.class); + } + + @Override + public void logCache(Logger logger) + { + // no-op + //logger.debug("Cache content log. Not implemented."); + + } + @SuppressWarnings("unchecked") + @Override + public List getPredictions(StopPathCacheKey key) { + + StopPredictions result = cache.get(key); + logCache(logger); + if(result==null) + return null; + else + return (List) result.getPredictions(); + } + + @Override + public void putPrediction(PredictionForStopPath prediction) + { + StopPathCacheKey key=new StopPathCacheKey(prediction.getTripId(), prediction.getStopPathIndex()); + putPrediction(key,prediction); + } + + @SuppressWarnings("unchecked") + @Override + public void putPrediction(StopPathCacheKey key, PredictionForStopPath prediction) { + + StopPredictions emptySp = new StopPredictions(); + emptySp.addPrediction(prediction); + + synchronized (cache) { + StopPredictions element = cache.get(key); + if (element == null) { + cache.put(key, emptySp); + } else { + element.addPrediction(prediction); + cache.put(key, element); + } + + } + } + +} diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/StopPathPredictionCacheFactory.java b/transitclock/src/main/java/org/transitclock/core/dataCache/StopPathPredictionCacheFactory.java new file mode 100644 index 000000000..941819283 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/dataCache/StopPathPredictionCacheFactory.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 stop path model class instances for each stop. + * + */ +public class StopPathPredictionCacheFactory { + private static StringConfigValue className = + new StringConfigValue("transitclock.core.cache.stopPathPredictionCache", + "org.transitclock.core.dataCache.StopPathPredictionCache", + "Specifies the class used to cache RLS data for a stop."); + + private static StopPathPredictionCacheInterface singleton = null; + + public static StopPathPredictionCacheInterface getInstance() { + + if (singleton == null) { + singleton = ClassInstantiator.instantiate(className.getValue(), + StopPathPredictionCacheInterface.class); + } + + return singleton; + } +} diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/StopPathPredictionCacheInterface.java b/transitclock/src/main/java/org/transitclock/core/dataCache/StopPathPredictionCacheInterface.java new file mode 100644 index 000000000..cbbd1436c --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/dataCache/StopPathPredictionCacheInterface.java @@ -0,0 +1,19 @@ +package org.transitclock.core.dataCache; + +import org.slf4j.Logger; +import org.transitclock.db.structs.PredictionForStopPath; + +import java.util.List; + +public interface StopPathPredictionCacheInterface { + + void logCache(Logger logger); + + @SuppressWarnings("unchecked") + List getPredictions(StopPathCacheKey key); + + void putPrediction(PredictionForStopPath prediction); + + @SuppressWarnings("unchecked") + void putPrediction(StopPathCacheKey key, PredictionForStopPath prediction); +} diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/StopPredictions.java b/transitclock/src/main/java/org/transitclock/core/dataCache/StopPredictions.java new file mode 100644 index 000000000..f2b5f210f --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/dataCache/StopPredictions.java @@ -0,0 +1,46 @@ +package org.transitclock.core.dataCache; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +import org.transitclock.db.structs.PredictionForStopPath; +public class StopPredictions implements Serializable { + /** + * + */ + + private static final long serialVersionUID = -6487148805894879790L; + + public List predictions = null; + + + public StopPredictions(List predictions) { + super(); + this.predictions = predictions; + } + + + public List getPredictions() { + return predictions; + } + + + public void setPredictions(List predictions) { + this.predictions = predictions; + } + + public void addPrediction(PredictionForStopPath prediction) + { + if(this.predictions==null) + { + predictions=new ArrayList(); + } + predictions.add(prediction); + } + + + public StopPredictions() { + super(); + } + +} diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/TripDataHistoryCacheFactory.java b/transitclock/src/main/java/org/transitclock/core/dataCache/TripDataHistoryCacheFactory.java new file mode 100644 index 000000000..0d9cb0f69 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/dataCache/TripDataHistoryCacheFactory.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 arrival and departures for a trip. + * + */ +public class TripDataHistoryCacheFactory { + private static StringConfigValue className = + new StringConfigValue("transitclock.core.cache.tripDataHistoryCache", + "org.transitclock.core.dataCache.ehcache.frequency.TripDataHistoryCache", + "Specifies the class used to cache the arrival and departures for a trip."); + + public static TripDataHistoryCacheInterface singleton = null; + + public static TripDataHistoryCacheInterface getInstance() { + + if (singleton == null) { + singleton = ClassInstantiator.instantiate(className.getValue(), + TripDataHistoryCacheInterface.class); + } + + return singleton; + } +} diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/TripDataHistoryCacheInterface.java b/transitclock/src/main/java/org/transitclock/core/dataCache/TripDataHistoryCacheInterface.java new file mode 100644 index 000000000..e15fb9c0c --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/dataCache/TripDataHistoryCacheInterface.java @@ -0,0 +1,25 @@ +package org.transitclock.core.dataCache; + +import java.util.Date; +import java.util.List; + +import org.hibernate.Session; +import org.slf4j.Logger; +import org.transitclock.db.structs.ArrivalDeparture; +import org.transitclock.ipc.data.IpcArrivalDeparture; + +public interface TripDataHistoryCacheInterface { + + + List getTripHistory(TripKey tripKey); + + TripKey putArrivalDeparture(ArrivalDeparture arrivalDeparture); + + void populateCacheFromDb(List results); + + IpcArrivalDeparture findPreviousArrivalEvent(List arrivalDepartures, IpcArrivalDeparture current); + + IpcArrivalDeparture findPreviousDepartureEvent(List arrivalDepartures, IpcArrivalDeparture current); + + List getKeys(); +} \ No newline at end of file diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/TripEvents.java b/transitclock/src/main/java/org/transitclock/core/dataCache/TripEvents.java new file mode 100644 index 000000000..eb199061b --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/dataCache/TripEvents.java @@ -0,0 +1,77 @@ +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 TripEvents implements Serializable { + /** + * + */ + private static final long serialVersionUID = -510989387398784934L; + + + public List events = new ArrayList<>(); + + public List getEvents() { + return events; + } + + @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; + TripEvents other = (TripEvents) obj; + if (events == null) { + if (other.events != null) + return false; + } else if (!events.equals(other.events)) + return false; + return true; + } + + public TripEvents() { + super(); + } + + /** + * make immuntable for Thread safe usage + * @param event + */ + public TripEvents(IpcArrivalDeparture event) { + super(); + synchronized (this.events) { + this.events.add(event); + Collections.sort(this.events, new IpcArrivalDepartureComparator()); + } + } + + public TripEvents(List events) { + super(); + synchronized (this.events) { + this.events.addAll(events); + Collections.sort(this.events, new IpcArrivalDepartureComparator()); + } + } + + public TripEvents copyAdd(IpcArrivalDeparture additional) { + List newEvents = new ArrayList<>(this.events); + newEvents.add(additional); + TripEvents newEvent = new TripEvents(newEvents); + return newEvent; + } + +} diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/TripEventsKyroSerializer.java b/transitclock/src/main/java/org/transitclock/core/dataCache/TripEventsKyroSerializer.java new file mode 100644 index 000000000..219894c43 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/dataCache/TripEventsKyroSerializer.java @@ -0,0 +1,40 @@ +package org.transitclock.core.dataCache; + +import java.io.ByteArrayOutputStream; +import java.nio.ByteBuffer; + +import org.ehcache.core.spi.service.FileBasedPersistenceContext; +import org.ehcache.spi.serialization.Serializer; +import org.ehcache.spi.serialization.SerializerException; +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.io.ByteBufferInputStream; +import com.esotericsoftware.kryo.io.Input; +import com.esotericsoftware.kryo.io.Output; + +public class TripEventsKyroSerializer implements Serializer { + + private static final Kryo kryo = new Kryo(); + public TripEventsKyroSerializer(ClassLoader classLoader) { + } + public TripEventsKyroSerializer(ClassLoader classLoader, FileBasedPersistenceContext persistenceContext) { + } + @Override + public boolean equals(TripEvents arg0, ByteBuffer arg1) throws ClassNotFoundException, SerializerException { + return arg0.equals(read(arg1)); + } + + @Override + public TripEvents read(ByteBuffer arg0) throws ClassNotFoundException, SerializerException { + Input input = new Input(new ByteBufferInputStream(arg0)) ; + return kryo.readObject(input, TripEvents.class); + } + + @Override + public ByteBuffer serialize(TripEvents arg0) throws SerializerException { + Output output = new Output(new ByteArrayOutputStream()); + kryo.writeObject(output, arg0); + output.close(); + return ByteBuffer.wrap(output.getBuffer()); + } + +} diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/TripKey.java b/transitclock/src/main/java/org/transitclock/core/dataCache/TripKey.java new file mode 100755 index 000000000..cfa1ff89b --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/dataCache/TripKey.java @@ -0,0 +1,98 @@ +package org.transitclock.core.dataCache; + +import java.util.Date; +/** + * @author Sean Og Crudden + * + */ +public class TripKey implements java.io.Serializable { + /** + * Needs to be serializable to add to cache + */ + private static final long serialVersionUID = 5029823633051153715L; + private String tripId; + + private Date tripStartDate; + private Integer startTime; + + + /** + * @return the tripId + */ + public String getTripId() { + return tripId; + } + + /** + * @return the tripStartDate + */ + public Date getTripStartDate() { + return tripStartDate; + } + /** + * @return the startTime + */ + public Integer getStartTime() { + return startTime; + } + public TripKey(String tripId, Date tripStartDate, + Integer startTime) { + super(); + this.tripId = tripId; + + this.tripStartDate = tripStartDate; + this.startTime = startTime; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((startTime == null) ? 0 : startTime.hashCode()); + result = prime * result + ((tripId == null) ? 0 : tripId.hashCode()); + result = prime * result + ((tripStartDate == null) ? 0 : tripStartDate.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; + TripKey other = (TripKey) obj; + if (startTime == null) { + if (other.startTime != null) + return false; + } else if (!startTime.equals(other.startTime)) + return false; + if (tripId == null) { + if (other.tripId != null) + return false; + } else if (!tripId.equals(other.tripId)) + return false; + if (tripStartDate == null) { + if (other.tripStartDate != null) + return false; + } else if (!tripStartDate.equals(other.tripStartDate)) + return false; + return true; + } + + @Override + public String toString() { + return "TripKey [tripId=" + tripId + ", tripStartDate=" + tripStartDate + ", startTime=" + startTime + "]"; + } + + public void setStartTime(Integer time) { + // TODO Auto-generated method stub + this.startTime=time; + + } + + + + +} diff --git a/transitime/src/main/java/org/transitime/core/dataCache/VehicleDataCache.java b/transitclock/src/main/java/org/transitclock/core/dataCache/VehicleDataCache.java similarity index 92% rename from transitime/src/main/java/org/transitime/core/dataCache/VehicleDataCache.java rename to transitclock/src/main/java/org/transitclock/core/dataCache/VehicleDataCache.java index 661176b68..f45e5181f 100644 --- a/transitime/src/main/java/org/transitime/core/dataCache/VehicleDataCache.java +++ b/transitclock/src/main/java/org/transitclock/core/dataCache/VehicleDataCache.java @@ -14,31 +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.dataCache; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; +package org.transitclock.core.dataCache; import org.hibernate.HibernateException; import org.hibernate.Session; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.applications.Core; -import org.transitime.configData.AgencyConfig; -import org.transitime.core.VehicleState; -import org.transitime.db.hibernate.HibernateUtils; -import org.transitime.db.structs.AvlReport; -import org.transitime.db.structs.Route; -import org.transitime.db.structs.VehicleConfig; -import org.transitime.ipc.data.IpcVehicleComplete; -import org.transitime.utils.ConcurrentHashMapNullKeyOk; -import org.transitime.utils.Time; +import org.transitclock.applications.Core; +import org.transitclock.configData.AgencyConfig; +import org.transitclock.core.VehicleState; +import org.transitclock.db.hibernate.HibernateUtils; +import org.transitclock.db.structs.AvlReport; +import org.transitclock.db.structs.Route; +import org.transitclock.db.structs.VehicleConfig; +import org.transitclock.ipc.data.IpcVehicleComplete; +import org.transitclock.utils.ConcurrentHashMapNullKeyOk; +import org.transitclock.utils.Time; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; /** * For storing and retrieving vehicle information that can be used by clients. @@ -63,7 +57,7 @@ public class VehicleDataCache { // Keyed by route_short_name. Key is null for vehicles that have not // been successfully associated with a route. For each route there is a // submap that is keyed by vehicle. - private Map> vehiclesByRouteMap = + private Map> vehiclesByRouteMap = new ConcurrentHashMapNullKeyOk>(); // So can determine vehicles associated with a block ID. Keyed on @@ -77,8 +71,8 @@ public class VehicleDataCache { // in AVL feed then this map is updated and the new VehicleConfig is also // written to the database. Using HashMap instead of ConcurrentHashMap // since synchronizing puts anyways. - private Map vehicleConfigsMap = - new HashMap(); + private ConcurrentHashMap vehicleConfigsMap = + new ConcurrentHashMap(); // So can quickly look up vehicle config using tracker ID private Map vehicleConfigByTrackerIdMap = @@ -178,23 +172,16 @@ public void cacheVehicleConfig(AvlReport avlReport) { // Make sure go initial data from database readVehicleConfigFromDbIfNeedTo(); - // Synchronize on the map since separately testing for object - // and adding object - synchronized (vehicleConfigsMap) { - // If new vehicle... - String vehicleId = avlReport.getVehicleId(); - if (!vehicleConfigsMap.containsKey(vehicleId)) { - logger.info("Encountered new vehicle where vehicleId={} so " - + "updating vehicle cache and writing the " - + "VehicleConfig to database.", vehicleId); - - // Add vehicle to cache and update database - VehicleConfig vehicleConfig = new VehicleConfig(vehicleId); - vehicleConfigsMap.put(vehicleId, vehicleConfig); - - // Write the vehicle to the database - Core.getInstance().getDbLogger().add(vehicleConfig); - } + // If new vehicle... + String vehicleId = avlReport.getVehicleId(); + VehicleConfig vehicleConfig = new VehicleConfig(vehicleId); + VehicleConfig absent = vehicleConfigsMap.putIfAbsent(vehicleId, vehicleConfig); + if (absent == null) { + logger.info("Encountered new vehicle where vehicleId={} so " + + "updating vehicle cache and writing the " + + "VehicleConfig to database.", vehicleId); + // Write the vehicle to the database + Core.getInstance().getDbLogger().add(vehicleConfig); } } @@ -517,7 +504,7 @@ private void updateVehiclesByRouteMap(IpcVehicleComplete originalVehicle, * @param vehicle */ private void updateVehiclesMap(IpcVehicleComplete vehicle) { - if (!vehicle.isForSchedBasedPred() || vehicle.isPredictable()) { + if (!vehicle.isForSchedBasedPred() || vehicle.isPredictable() || vehicle.isCanceled()) { // Normal situation. Add vehicle to vehiclesMap vehiclesMap.put(vehicle.getId(), vehicle); } else { @@ -546,4 +533,15 @@ public void updateVehicle(VehicleState vehicleState) { updateVehicleIdsByBlockMap(originalVehicle, vehicle); updateVehiclesMap(vehicle); } + + /** + * Removes a vehicle from the vehiclesMap + * + * @param vehicleId + * The id of the vehicle to remove from the vehiclesMap + */ + public void removeVehicle(String vehicleId) { + logger.debug("Removing from VehicleDataCache vehiclesMap vehicleId={}", vehicleId); + vehiclesMap.remove(vehicleId); + } } diff --git a/transitime/src/main/java/org/transitime/core/dataCache/VehicleStateManager.java b/transitclock/src/main/java/org/transitclock/core/dataCache/VehicleStateManager.java similarity index 97% rename from transitime/src/main/java/org/transitime/core/dataCache/VehicleStateManager.java rename to transitclock/src/main/java/org/transitclock/core/dataCache/VehicleStateManager.java index 8abcb2ec8..c42258f53 100644 --- a/transitime/src/main/java/org/transitime/core/dataCache/VehicleStateManager.java +++ b/transitclock/src/main/java/org/transitclock/core/dataCache/VehicleStateManager.java @@ -14,13 +14,13 @@ * 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.Collection; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import org.transitime.core.VehicleState; +import org.transitclock.core.VehicleState; /** * For keeping track of vehicle state. This is used by the main predictor code, diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/ehcache/CacheManagerFactory.java b/transitclock/src/main/java/org/transitclock/core/dataCache/ehcache/CacheManagerFactory.java new file mode 100644 index 000000000..60c5ced66 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/dataCache/ehcache/CacheManagerFactory.java @@ -0,0 +1,54 @@ +package org.transitclock.core.dataCache.ehcache; + +import org.ehcache.CacheManager; +import org.ehcache.config.builders.CacheManagerBuilder; +import org.ehcache.xml.XmlConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.config.StringConfigValue; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; + +public class CacheManagerFactory { + + public static CacheManager singleton = null; + + private static final String DEFAULT_CONFIG = "ehcache.xml"; + + private static final Logger logger = + LoggerFactory.getLogger(CacheManagerFactory.class); + + private static StringConfigValue ehcacheConfigFile = + new StringConfigValue("transitclock.cache.ehcacheConfigFile", DEFAULT_CONFIG, + "Specifies the ehcache config file location."); + + public static CacheManager getInstance() { + + if (singleton == null) { + URL xmlConfigUrl = getConfigPath(); + XmlConfiguration xmlConfig = new XmlConfiguration(xmlConfigUrl); + + singleton = CacheManagerBuilder.newCacheManager(xmlConfig); + singleton.init(); + } + + return singleton; + } + + private static URL getConfigPath() { + URL defaultConfig = CacheManagerFactory.class.getClassLoader().getResource(ehcacheConfigFile.getValue()); + if(ehcacheConfigFile.getValue().equalsIgnoreCase(DEFAULT_CONFIG)){ + return defaultConfig; + } else { + try { + File file = new File(ehcacheConfigFile.getValue()); + return file.toURI().toURL(); + } catch (MalformedURLException e) { + logger.error("Unable to load ehcache config file: " + ehcacheConfigFile.getValue(), e); + return defaultConfig; + } + } + } +} diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/ehcache/KalmanErrorCache.java b/transitclock/src/main/java/org/transitclock/core/dataCache/ehcache/KalmanErrorCache.java new file mode 100644 index 000000000..e70572539 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/dataCache/ehcache/KalmanErrorCache.java @@ -0,0 +1,106 @@ +package org.transitclock.core.dataCache.ehcache; +import org.ehcache.Cache; +import org.ehcache.CacheManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.core.Indices; +import org.transitclock.core.dataCache.ErrorCache; +import org.transitclock.core.dataCache.KalmanError; +import org.transitclock.core.dataCache.KalmanErrorCacheKey; + +import java.util.List; +/** + * @author Sean Óg Crudden + * + */ +public class KalmanErrorCache implements ErrorCache { + final private static String cacheName = "KalmanErrorCache"; + + private static final Logger logger = LoggerFactory + .getLogger(KalmanErrorCache.class); + + private Cache cache = null; + /** + * Gets the singleton instance of this class. + * + * @return + */ + + public KalmanErrorCache() { + + CacheManager cm = CacheManagerFactory.getInstance(); + + cache = cm.getCache(cacheName, KalmanErrorCacheKey.class, KalmanError.class); + } + + public void logCache(Logger logger) + { + logger.debug("Cache content log. Not implemented."); + + } + + /* (non-Javadoc) + * @see org.transitclock.core.dataCache.ErrorCache#getErrorValue(org.transitclock.core.Indices) + */ + @Override + @SuppressWarnings("unchecked") + synchronized public KalmanError getErrorValue(Indices indices) { + + KalmanErrorCacheKey key=new KalmanErrorCacheKey(indices); + + KalmanError result = (KalmanError)cache.get(key); + + if(result==null) + return null; + else + return result; + } + /* (non-Javadoc) + * @see org.transitclock.core.dataCache.ErrorCache#getErrorValue(org.transitclock.core.dataCache.KalmanErrorCacheKey) + */ + @Override + @SuppressWarnings("unchecked") + synchronized public KalmanError getErrorValue(KalmanErrorCacheKey key) { + + KalmanError result = (KalmanError)cache.get(key); + + if(result==null) + return null; + else + return result; + } + /* (non-Javadoc) + * @see org.transitclock.core.dataCache.ErrorCache#putErrorValue(org.transitclock.core.Indices, java.lang.Double) + */ + @Override + synchronized public void putErrorValue(Indices indices, Double value) { + + KalmanErrorCacheKey key=new KalmanErrorCacheKey(indices); + putErrorValue(key,value); + } + + @Override + public void putErrorValue(KalmanErrorCacheKey key, Double value) { + + KalmanError error= (KalmanError)cache.get(key); + + if(error==null) + { + error=new KalmanError(value); + }else + { + error.setError(value); + } + + + cache.put(key,error); + } + + @Override + public List getKeys() { + // TODO Auto-generated method stub + return null; + } + + +} diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/ehcache/StopArrivalDepartureCache.java b/transitclock/src/main/java/org/transitclock/core/dataCache/ehcache/StopArrivalDepartureCache.java new file mode 100644 index 000000000..2b2230f9d --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/dataCache/ehcache/StopArrivalDepartureCache.java @@ -0,0 +1,149 @@ +/** + * + */ +package org.transitclock.core.dataCache.ehcache; + +import org.ehcache.Cache; +import org.ehcache.CacheManager; +import org.hibernate.Criteria; +import org.hibernate.Session; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.core.dataCache.*; +import org.transitclock.db.structs.ArrivalDeparture; +import org.transitclock.ipc.data.IpcArrivalDeparture; +import org.transitclock.utils.Time; + +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +/** + * @author Sean Og Crudden This is a Cache to hold a sorted list of all arrival departure events + * for each stop in a cache. We can use this to look up all event for a + * stop for a day. The date used in the key should be the start of the + * day concerned. + * + * TODO this could do with an interface, factory class, and alternative + * implementations, perhaps using Infinispan. + */ +public class StopArrivalDepartureCache extends StopArrivalDepartureCacheInterface { + + + private static boolean debug = false; + + final private static String cacheByStop = "arrivalDeparturesByStop"; + + private static final Logger logger = LoggerFactory.getLogger(StopArrivalDepartureCache.class); + + private Cache cache = null; + /** + * Default is 4 as we need 3 days worth for Kalman Filter implementation + */ + private static final IntegerConfigValue tripDataCacheMaxAgeSec = new IntegerConfigValue( + "transitclock.tripdatacache.tripDataCacheMaxAgeSec", 4 * Time.SEC_PER_DAY, + "How old an arrivaldeparture has to be before it is removed from the cache "); + + + public StopArrivalDepartureCache() { + CacheManager cm = CacheManagerFactory.getInstance(); + + cache = cm.getCache(cacheByStop, StopArrivalDepartureCacheKey.class, StopEvents.class); + } + + public void logCache(Logger logger) { + logger.debug("Cache content log. Not implemented."); + } + + /* (non-Javadoc) + * @see org.transitclock.core.dataCache.ehcache.StopArrivalDepartureCacheInterface#getStopHistory(org.transitclock.core.dataCache.StopArrivalDepartureCacheKey) + */ + + @SuppressWarnings("unchecked") + public List getStopHistory(StopArrivalDepartureCacheKey key) { + + //logger.debug(cache.toString()); + Calendar date = Calendar.getInstance(); + date.setTime(key.getDate()); + + date.set(Calendar.HOUR_OF_DAY, 0); + date.set(Calendar.MINUTE, 0); + date.set(Calendar.SECOND, 0); + date.set(Calendar.MILLISECOND, 0); + key.setDate(date.getTime()); + // cache retrieval should be synchronized as Kyro is not thread safe + synchronized (cache) { + StopEvents result = cache.get(key); + + if (result != null) { + return (List) result.getEvents(); + } else { + return null; + } + } + } + + /* (non-Javadoc) + * @see org.transitclock.core.dataCache.ehcache.StopArrivalDepartureCacheInterface#putArrivalDeparture(org.transitclock.db.structs.ArrivalDeparture) + */ + @SuppressWarnings("unchecked") + public StopArrivalDepartureCacheKey putArrivalDeparture(ArrivalDeparture arrivalDeparture) { + + logger.trace("Putting :" + arrivalDeparture.toString() + " in StopArrivalDepartureCache cache."); + + Calendar date = Calendar.getInstance(); + date.setTime(arrivalDeparture.getDate()); + + date.set(Calendar.HOUR_OF_DAY, 0); + date.set(Calendar.MINUTE, 0); + date.set(Calendar.SECOND, 0); + date.set(Calendar.MILLISECOND, 0); + if(arrivalDeparture.getStop() == null) return null; + StopArrivalDepartureCacheKey key = new StopArrivalDepartureCacheKey(arrivalDeparture.getStop().getId(), + date.getTime()); + IpcArrivalDeparture ipc; + StopEvents empty = new StopEvents(); + try { + ipc = new IpcArrivalDeparture(arrivalDeparture); + } catch (Exception e) { + logger.error("Exception adding " + arrivalDeparture.toString() + " event to StopArrivalDepartureCache.", e); + return key; + } + empty.addEvent(ipc); + + synchronized (cache) { + StopEvents element = cache.get(key); + if (element == null) { + cache.put(key, empty); + } else { + element.addEvent(ipc); + cache.put(key, element); + } + return key; + } + } + + private static Iterable emptyIfNull(Iterable iterable) { + return iterable == null ? Collections. emptyList() : iterable; + } + + public void populateCacheFromDb(List results) { + try { + int counter = 0; + + for (ArrivalDeparture result : results) { + if (counter % 1000 == 0) { + logger.info("{} out of {} Stop Arrival Departure Records ({}%)", counter, results.size(), (int) ((counter * 100.0f) / results.size())); + } + putArrivalDeparture(result); + counter++; + } + } catch (Throwable t) { + logger.error("Exception in populateCacheFromDb {}", t, t); + } + } + + +} diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/ehcache/dummy/DummyDwellTimeModelCache.java b/transitclock/src/main/java/org/transitclock/core/dataCache/ehcache/dummy/DummyDwellTimeModelCache.java new file mode 100644 index 000000000..3c0484aef --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/dataCache/ehcache/dummy/DummyDwellTimeModelCache.java @@ -0,0 +1,37 @@ +package org.transitclock.core.dataCache.ehcache.dummy; + + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.core.dataCache.DwellTimeModelCacheInterface; +import org.transitclock.core.dataCache.StopPathCacheKey; +import org.transitclock.db.structs.ArrivalDeparture; +import org.transitclock.db.structs.Headway; + +import java.util.List; + +public class DummyDwellTimeModelCache implements DwellTimeModelCacheInterface { + + private static final Logger logger = LoggerFactory.getLogger(DummyDwellTimeModelCache.class); + + + @Override + public void addSample(ArrivalDeparture event, Headway headway, long dwellTime) { + return; + } + + @Override + public void addSample(ArrivalDeparture departure) { + return; + } + + @Override + public Long predictDwellTime(StopPathCacheKey cacheKey, Headway headway) { + return null; + } + + @Override + public void populateCacheFromDb(List results) { + + } +} diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/ehcache/dummy/DummyStopPathPredictionCache.java b/transitclock/src/main/java/org/transitclock/core/dataCache/ehcache/dummy/DummyStopPathPredictionCache.java new file mode 100644 index 000000000..4a63d5218 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/dataCache/ehcache/dummy/DummyStopPathPredictionCache.java @@ -0,0 +1,30 @@ +package org.transitclock.core.dataCache.ehcache.dummy; + +import org.slf4j.Logger; +import org.transitclock.core.dataCache.StopPathCacheKey; +import org.transitclock.core.dataCache.StopPathPredictionCacheInterface; +import org.transitclock.db.structs.PredictionForStopPath; + +import java.util.List; + +public class DummyStopPathPredictionCache implements StopPathPredictionCacheInterface { + @Override + public void logCache(Logger logger) { + return; + } + + @Override + public List getPredictions(StopPathCacheKey key) { + return null; + } + + @Override + public void putPrediction(PredictionForStopPath prediction) { + return; + } + + @Override + public void putPrediction(StopPathCacheKey key, PredictionForStopPath prediction) { + return; + } +} diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/ehcache/frequency/TripDataHistoryCache.java b/transitclock/src/main/java/org/transitclock/core/dataCache/ehcache/frequency/TripDataHistoryCache.java new file mode 100755 index 000000000..e636aa7ce --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/dataCache/ehcache/frequency/TripDataHistoryCache.java @@ -0,0 +1,245 @@ +/** + * + */ +package org.transitclock.core.dataCache.ehcache.frequency; + +import org.apache.commons.lang3.time.DateUtils; +import org.ehcache.Cache; +import org.ehcache.CacheManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.applications.Core; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.core.dataCache.*; +import org.transitclock.core.dataCache.ehcache.CacheManagerFactory; +import org.transitclock.core.dataCache.frequency.FrequencyBasedHistoricalAverageCache; +import org.transitclock.db.structs.ArrivalDeparture; +import org.transitclock.db.structs.Block; +import org.transitclock.db.structs.Trip; +import org.transitclock.gtfs.DbConfig; +import org.transitclock.gtfs.GtfsData; +import org.transitclock.ipc.data.IpcArrivalDeparture; +import org.transitclock.utils.Time; + +import java.util.*; + +/** + * @author Sean Og Crudden + * This is a Cache to hold historical arrival departure data for trips. It + * is intended to look up a trips historical data when a trip starts and + * place in cache for use in generating predictions based on a Kalman + * filter. Uses Ehcache for caching rather than just using a concurrent + * hashmap. This approach to holding data in memory for transitclock needs + * to be proven. + * + * TODO this could do with an interface, factory class, and alternative implementations, perhaps using Infinispan. + */ +public class TripDataHistoryCache implements TripDataHistoryCacheInterface{ + private static TripDataHistoryCacheInterface singleton = new TripDataHistoryCache(); + + private static boolean debug = false; + + final private static String cacheByTrip = "arrivalDeparturesByTrip"; + + private static final Logger logger = LoggerFactory + .getLogger(TripDataHistoryCache.class); + + private Cache cache = null; + + /** + * Default is 4 as we need 3 days worth for Kalman Filter implementation + */ + private static final IntegerConfigValue tripDataCacheMaxAgeSec = new IntegerConfigValue( + "transitclock.tripdatacache.tripDataCacheMaxAgeSec", + 15 * Time.SEC_PER_DAY, + "How old an arrivaldeparture has to be before it is removed from the cache "); + + /** + * Gets the singleton instance of this class. + * + * @return + */ + public static TripDataHistoryCacheInterface getInstance() { + return singleton; + } + + public TripDataHistoryCache() { + CacheManager cm = CacheManagerFactory.getInstance(); + + + + cache = cm.getCache(cacheByTrip, TripKey.class, TripEvents.class); + } + + + public void logCache(Logger logger) + { + logger.debug("Cache content log. Not implemented."); + + } + + /* (non-Javadoc) + * @see org.transitclock.core.dataCache.TripDataHistoryCacheInterface#getTripHistory(org.transitclock.core.dataCache.TripKey) + */ + @Override + @SuppressWarnings("unchecked") + public List getTripHistory(TripKey tripKey) { + + //logger.debug(cache.toString()); + logger.debug("Looking for TripDataHistoryCache cache element using key {}.", tripKey); + + TripEvents result = cache.get(tripKey); + + if(result!=null) + { + logger.debug("Found TripDataHistoryCache cache element using key {}.", tripKey); + return (List) result.getEvents(); + } + else + { + return null; + } + } + + /* (non-Javadoc) + * @see org.transitclock.core.dataCache.TripDataHistoryCacheInterface#putArrivalDeparture(org.transitclock.db.structs.ArrivalDeparture) + */ + @Override + @SuppressWarnings("unchecked") + public TripKey putArrivalDeparture(ArrivalDeparture arrivalDeparture) { + + Block block=null; + if(arrivalDeparture.getBlock()==null) + { + DbConfig dbConfig = Core.getInstance().getDbConfig(); + block=dbConfig.getBlock(arrivalDeparture.getServiceId(), arrivalDeparture.getBlockId()); + }else + { + block=arrivalDeparture.getBlock(); + } + + /* just put todays time in for last three days to aid development. This means it will kick in in 1 days rather than 3. Perhaps be a good way to start rather than using default transiTime method but I doubt it. */ + int days_back=1; + if(debug) + days_back=3; + TripKey tripKey=null; + + for(int i=0;i < days_back;i++) { + Date nearestDay = DateUtils.truncate(new Date(arrivalDeparture.getTime()), Calendar.DAY_OF_MONTH); + + nearestDay=DateUtils.addDays(nearestDay, i*-1); + + DbConfig dbConfig = Core.getInstance().getDbConfig(); + + Trip trip=dbConfig.getTrip(arrivalDeparture.getTripId()); + + // TODO need to set start time based on start of bucket + if(arrivalDeparture.getFreqStartTime()!=null) { + Integer time = FrequencyBasedHistoricalAverageCache.secondsFromMidnight(arrivalDeparture.getFreqStartTime(), 2); + + time = FrequencyBasedHistoricalAverageCache.round(time, FrequencyBasedHistoricalAverageCache.getCacheIncrementsForFrequencyService()); + + if (trip != null) { + tripKey = new TripKey(arrivalDeparture.getTripId(), + nearestDay, + time); + + logger.debug("Putting :{} in TripDataHistoryCache cache using key {}.", arrivalDeparture, tripKey); + IpcArrivalDeparture ipcad = null; + try { + ipcad = new IpcArrivalDeparture(arrivalDeparture); + } catch (Exception e) { + logger.error("Exception creating IpcArrivalDeparture {}", e, e); + return tripKey; + } + TripEvents empty = new TripEvents(ipcad); + + synchronized (cache) { + TripEvents element = cache.get(tripKey); + if (element == null) { + cache.put(tripKey, empty); + } else { + // immutable object for thread safety + element = element.copyAdd(ipcad); + cache.put(tripKey, element); + } + } + } + } else { + logger.error("Cannot add event to TripDataHistoryCache as it has no freqStartTime set. {}", arrivalDeparture); + } + } + return tripKey; + } + + /* (non-Javadoc) + * @see org.transitclock.core.dataCache.TripDataHistoryCacheInterface#populateCacheFromDb(org.hibernate.Session, java.util.Date, java.util.Date) + */ + + @Override + public void populateCacheFromDb(List results) + { + try { + for (ArrivalDeparture result : results) { + // TODO this might be better done in the database. + if (GtfsData.routeNotFiltered(result.getRouteId())) { + putArrivalDeparture(result); + } + } + } catch (Throwable t) { + logger.error("Exception in populateCacheFromDb {}", t, t); + } + } + + /* (non-Javadoc) + * @see org.transitclock.core.dataCache.ehcache.test#findPreviousArrivalEvent(java.util.List, org.transitclock.db.structs.ArrivalDeparture) + */ + @Override + public IpcArrivalDeparture findPreviousArrivalEvent(List arrivalDepartures,IpcArrivalDeparture current) + { + Collections.sort(arrivalDepartures, new IpcArrivalDepartureComparator()); + for (IpcArrivalDeparture tocheck : emptyIfNull(arrivalDepartures)) + { + if(tocheck.getStopId().equals(current.getStopId()) && (current.isDeparture() && tocheck.isArrival())) + { + return tocheck; + } + } + return null; + } + /* (non-Javadoc) + * @see org.transitclock.core.dataCache.ehcache.test#findPreviousDepartureEvent(java.util.List, org.transitclock.db.structs.ArrivalDeparture) + */ + @Override + public IpcArrivalDeparture findPreviousDepartureEvent(List arrivalDepartures,IpcArrivalDeparture current) + { + Collections.sort(arrivalDepartures, new IpcArrivalDepartureComparator()); + for (IpcArrivalDeparture tocheck : emptyIfNull(arrivalDepartures)) + { + try { + + if(tocheck.getStopPathIndex()==(current.getStopPathIndex()-1) + && (current.isArrival() && tocheck.isDeparture()) + && current.getFreqStartTime().equals(tocheck.getFreqStartTime())) + { + return tocheck; + } + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + return null; + } + + private static Iterable emptyIfNull(Iterable iterable) { + return iterable == null ? Collections. emptyList() : iterable; + } + + @Override + public List getKeys() { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/ehcache/scheduled/DwellTimeModelCache.java b/transitclock/src/main/java/org/transitclock/core/dataCache/ehcache/scheduled/DwellTimeModelCache.java new file mode 100644 index 000000000..7a11cb8c8 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/dataCache/ehcache/scheduled/DwellTimeModelCache.java @@ -0,0 +1,222 @@ +package org.transitclock.core.dataCache.ehcache.scheduled; + +import org.ehcache.Cache; +import org.ehcache.CacheManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.config.LongConfigValue; +import org.transitclock.core.dataCache.StopArrivalDepartureCacheFactory; +import org.transitclock.core.dataCache.StopArrivalDepartureCacheKey; +import org.transitclock.core.dataCache.StopPathCacheKey; +import org.transitclock.core.dataCache.ehcache.CacheManagerFactory; +import org.transitclock.core.predictiongenerator.scheduled.dwell.DwellModel; +import org.transitclock.core.predictiongenerator.scheduled.dwell.DwellTimeModelFactory; +import org.transitclock.db.structs.ArrivalDeparture; +import org.transitclock.db.structs.Headway; +import org.transitclock.ipc.data.IpcArrivalDeparture; +import org.transitclock.utils.Time; + +import java.io.IOException; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +/** + * + * @author scrudden + * This stores DwellModel instances in the cache. TODO We should abstract the anomaly detection as per TODO in code below. + */ +public class DwellTimeModelCache implements org.transitclock.core.dataCache.DwellTimeModelCacheInterface { + + private static LongConfigValue maxDwellTimeAllowedInModel = new LongConfigValue("transitclock.prediction.dwell.maxDwellTimeAllowedInModel", (long) (2 * Time.MS_PER_MIN), "Max dwell time to be considered in dwell RLS algotithm."); + private static LongConfigValue minDwellTimeAllowedInModel = new LongConfigValue("transitclock.prediction.dwell.minDwellTimeAllowedInModel", (long) 1000, "Min dwell time to be considered in dwell RLS algotithm."); + private static LongConfigValue maxHeadwayAllowedInModel = new LongConfigValue("transitclock.prediction.dwell.maxHeadwayAllowedInModel", 1*Time.MS_PER_HOUR, "Max headway to be considered in dwell RLS algotithm."); + private static LongConfigValue minHeadwayAllowedInModel = new LongConfigValue("transitclock.prediction.dwell.minHeadwayAllowedInModel", (long) 1000, "Min headway to be considered in dwell RLS algotithm."); + private static IntegerConfigValue minSceheduleAdherence = new IntegerConfigValue("transitclock.prediction.dwell.minSceheduleAdherence", (int) (10 * Time.SEC_PER_MIN), "If schedule adherence of vehicle is outside this then not considerd in dwell RLS algorithm."); + private static IntegerConfigValue maxSceheduleAdherence = new IntegerConfigValue("transitclock.prediction.dwell.maxSceheduleAdherence", (int) (10 * Time.SEC_PER_MIN), "If schedule adherence of vehicle is outside this then not considerd in dwell RLS algorithm."); + + + final private static String cacheName= "dwellTimeModelCache"; + + private static final Logger logger = LoggerFactory.getLogger(DwellTimeModelCache.class); + + + private Cache cache = null; + + public DwellTimeModelCache() throws IOException { + + CacheManager cm = CacheManagerFactory.getInstance(); + + cache = cm.getCache(cacheName, StopPathCacheKey.class, DwellModel.class); + } + @Override + public void addSample(ArrivalDeparture event, Headway headway, long dwellTime) { + + StopPathCacheKey key=new StopPathCacheKey(headway.getTripId(), event.getStopPathIndex(), false); + DwellModel empty = DwellTimeModelFactory.getInstance(); + empty.putSample((int) dwellTime, (int) headway.getHeadway(), null); + + synchronized (cache) { + DwellModel model = cache.get(key); + if (model == null) { + cache.put(key, empty); + } else { + model.putSample((int) dwellTime, (int) headway.getHeadway(), null); + cache.put(key, model); + } + } + } + + @Override + public void addSample(ArrivalDeparture departure) { + try { + if(departure!=null && !departure.isArrival()) + { + StopArrivalDepartureCacheKey key= new StopArrivalDepartureCacheKey(departure.getStopId(), departure.getDate()); + List stopData = StopArrivalDepartureCacheFactory.getInstance().getStopHistory(key); + + if(stopData!=null && stopData.size()>1) + { + IpcArrivalDeparture arrival=findArrival(stopData, new IpcArrivalDeparture(departure)); + + if(arrival!=null) + { + IpcArrivalDeparture previousArrival=findPreviousArrival(stopData, arrival); + if(arrival!=null&&previousArrival!=null) + { + Headway headway=new Headway(); + headway.setHeadway(arrival.getTime().getTime()-previousArrival.getTime().getTime()); + long dwelltime=departure.getTime()-arrival.getTime().getTime(); + headway.setTripId(arrival.getTripId()); + + /* Leave out silly values as they are most likely errors or unusual circumstance. */ + /* TODO Should abstract this behind an anomaly detention interface/Factory */ + + if(departure.getScheduleAdherence()!=null && departure.getScheduleAdherence().isWithinBounds(minSceheduleAdherence.getValue(),maxSceheduleAdherence.getValue())) + { + + if((departure.getStop().isWaitStop()==null||!departure.getStop().isWaitStop()) + &&(departure.getStop().isLayoverStop()==null || !departure.getStop().isLayoverStop())) + { + // Arrival schedule adherence appears not to be set much. So only stop if set and outside range. + if(previousArrival.getScheduledAdherence()==null || previousArrival.getScheduledAdherence().isWithinBounds(minSceheduleAdherence.getValue(),maxSceheduleAdherence.getValue())) + { + if(dwelltime minDwellTimeAllowedInModel.getValue()) + { + if(headway.getHeadway() < maxHeadwayAllowedInModel.getValue() + && headway.getHeadway() > minHeadwayAllowedInModel.getValue()) + { + addSample(departure,headway,dwelltime); + }else + { + logger.warn("Headway outside allowable range . {}", headway); + } + }else + { + logger.warn("Dwell time {} outside allowable range for {}.", dwelltime, departure); + } + }else + { + logger.warn("Schedule adherence outside allowable range. "+previousArrival.getScheduledAdherence()); + } + }else + { + logger.warn("This is a wait stop or layover so not being included in model as dwell time is affected by if vehicle is early or late to the stop."); + } + }else + { + logger.warn("Schedule adherence outside allowable range. "+departure.getScheduleAdherence()); + } + } + } + } + } + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + private IpcArrivalDeparture findPreviousArrival(List stopData, IpcArrivalDeparture arrival) { + for(IpcArrivalDeparture event:stopData) + { + if(event.isArrival()) + { + if(!event.getVehicleId().equals(arrival.getVehicleId())) + { + if(!event.getTripId().equals(arrival.getTripId())) + { + if(event.getStopId().equals(arrival.getStopId())) + { + if(event.getTime().getTime() stopData, IpcArrivalDeparture departure) { + + for(IpcArrivalDeparture event:stopData) + { + if(event.isArrival()) + { + if(event.getStopId().equals(departure.getStopId())) + { + if(event.getVehicleId().equals(departure.getVehicleId())) + { + if(event.getTripId().equals(departure.getTripId())) + { + return event; + } + } + } + } + } + return null; + } + @Override + public Long predictDwellTime(StopPathCacheKey cacheKey, Headway headway) { + + DwellModel model=(DwellModel) cache.get(cacheKey); + if(model==null||headway==null) + return null; + + if(model.predict((int)headway.getHeadway(), null)!=null) + return new Long(model.predict((int)headway.getHeadway(), null)); + + return null; + } + + @Override + public void populateCacheFromDb(List results) { + logger.info("running populateCacheFromDb..."); + synchronized (cache) { + for (ArrivalDeparture result : results) { + try { + addSample(result); + } catch (Exception e) { + logger.error("Exception caching {}", e, e); + } + } + } + logger.info("finished populateCacheFromDb..."); + } + + +} diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/ehcache/scheduled/TripDataHistoryCache.java b/transitclock/src/main/java/org/transitclock/core/dataCache/ehcache/scheduled/TripDataHistoryCache.java new file mode 100755 index 000000000..84b5b1a1b --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/dataCache/ehcache/scheduled/TripDataHistoryCache.java @@ -0,0 +1,221 @@ +/** + * + */ +package org.transitclock.core.dataCache.ehcache.scheduled; + +import org.apache.commons.lang3.time.DateUtils; +import org.ehcache.Cache; +import org.ehcache.CacheManager; +import org.hibernate.Criteria; +import org.hibernate.Session; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.applications.Core; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.core.dataCache.*; +import org.transitclock.core.dataCache.ehcache.CacheManagerFactory; +import org.transitclock.db.structs.ArrivalDeparture; +import org.transitclock.db.structs.Trip; +import org.transitclock.gtfs.DbConfig; +import org.transitclock.gtfs.GtfsData; +import org.transitclock.ipc.data.IpcArrivalDeparture; +import org.transitclock.utils.Time; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +/** + * @author Sean Og Crudden + * This is a Cache to hold historical arrival departure data for schedule based trips. It + * is intended to look up a trips historical data when a trip starts and + * place in cache for use in generating predictions based on a Kalman + * filter. + * + */ +public class TripDataHistoryCache implements TripDataHistoryCacheInterface{ + private static TripDataHistoryCacheInterface singleton = new TripDataHistoryCache(); + + private static boolean debug = false; + + final private static String cacheByTrip = "arrivalDeparturesByTrip"; + + private static final Logger logger = LoggerFactory + .getLogger(TripDataHistoryCache.class); + + private Cache cache = null; + + /** + * Default is 4 as we need 3 days worth for Kalman Filter implementation + */ + private static final IntegerConfigValue tripDataCacheMaxAgeSec = new IntegerConfigValue( + "transitclock.tripdatacache.tripDataCacheMaxAgeSec", + 15 * Time.SEC_PER_DAY, + "How old an arrivaldeparture has to be before it is removed from the cache "); + + /** + * Gets the singleton instance of this class. + * + * @return + */ + public static TripDataHistoryCacheInterface getInstance() { + return singleton; + } + + public TripDataHistoryCache() { + CacheManager cm = CacheManagerFactory.getInstance(); + cache = cm.getCache(cacheByTrip, TripKey.class, TripEvents.class); + } + + + /* (non-Javadoc) + * @see org.transitclock.core.dataCache.TripDataHistoryCacheInterface#getTripHistory(org.transitclock.core.dataCache.TripKey) + */ + @Override + public List getTripHistory(TripKey tripKey) { + TripEvents result = null; + synchronized (cache) { + // we need to protected the deserializer from modifications + result = cache.get(tripKey); + } + + if (result != null) { + // TripEvent is immutable so this call is now threadsafe + return result.getEvents(); + } else { + return null; + } + } + + /* (non-Javadoc) + * @see org.transitclock.core.dataCache.TripDataHistoryCacheInterface#putArrivalDeparture(org.transitclock.db.structs.ArrivalDeparture) + */ + @Override + public TripKey putArrivalDeparture(ArrivalDeparture arrivalDeparture) { + + logger.trace("Putting :"+arrivalDeparture.toString() + " in TripDataHistoryCache cache."); + /* just put todays time in for last three days to aid development. This means it will kick in in 1 days rather than 3. Perhaps be a good way to start rather than using default transiTime method but I doubt it. */ + int days_back=1; + if(debug) + days_back=3; + TripKey tripKey=null; + + for(int i=0;i < days_back;i++) + { + Date nearestDay = DateUtils.truncate(new Date(arrivalDeparture.getTime()), Calendar.DAY_OF_MONTH); + + nearestDay=DateUtils.addDays(nearestDay, i*-1); + + DbConfig dbConfig = Core.getInstance().getDbConfig(); + + Trip trip=dbConfig.getTrip(arrivalDeparture.getTripId()); + Integer tripStartTime = null; + + if (trip != null) { + tripStartTime = trip.getStartTime(); + } + + tripKey = new TripKey(arrivalDeparture.getTripId(), + nearestDay, + tripStartTime); + + IpcArrivalDeparture ipcad = null; + try { + ipcad = new IpcArrivalDeparture(arrivalDeparture); + } catch (Exception e) { + logger.error("Error adding "+arrivalDeparture.toString()+" event to TripDataHistoryCache.", e); + } + TripEvents empty = new TripEvents(ipcad); + + synchronized (cache) { + TripEvents result = cache.get(tripKey); + if (result == null) { + cache.put(tripKey, empty); + } else { + // update TripEvents in a threadsafe way + // object needs to be immutable for ehcache to store + result = result.copyAdd(ipcad); + cache.put(tripKey, result); + } + } + } + return tripKey; + } + + /* (non-Javadoc) + * @see org.transitclock.core.dataCache.TripDataHistoryCacheInterface#populateCacheFromDb(org.hibernate.Session, java.util.Date, java.util.Date) + */ + + @Override + public void populateCacheFromDb(List results) + { + try { + int counter = 0; + for (ArrivalDeparture result : results) { + if (counter % 1000 == 0) { + logger.info("{} out of {} Trip Data History Records ({}%)", counter, results.size(), (int) ((counter * 100.0f) / results.size())); + } + // TODO this might be better done in the database. + if (GtfsData.routeNotFiltered(result.getRouteId())) { + try { + putArrivalDeparture(result); + } catch (Throwable tt) { + logger.error("TripDataHistoryCache Excepiton buried"); + } + } + counter++; + } + } catch (Throwable t) { + logger.error("Exception in populateCacheFromDb {}", t, t); + } + } + + /* (non-Javadoc) + * @see org.transitclock.core.dataCache.ehcache.test#findPreviousArrivalEvent(java.util.List, org.transitclock.db.structs.ArrivalDeparture) + */ + @Override + public IpcArrivalDeparture findPreviousArrivalEvent(List arrivalDepartures,IpcArrivalDeparture current) + { + if (arrivalDepartures == null) return null; + Collections.sort(arrivalDepartures, new IpcArrivalDepartureComparator()); + for (IpcArrivalDeparture tocheck : emptyIfNull(arrivalDepartures)) { + if (tocheck.getStopId().equals(current.getStopId()) && (current.isDeparture() && tocheck.isArrival())) { + return tocheck; + } + } + return null; + } + /* (non-Javadoc) + * @see org.transitclock.core.dataCache.ehcache.test#findPreviousDepartureEvent(java.util.List, org.transitclock.db.structs.ArrivalDeparture) + */ + @Override + public IpcArrivalDeparture findPreviousDepartureEvent(List arrivalDeparturesUnsafe,IpcArrivalDeparture current) + { + if (arrivalDeparturesUnsafe == null) return null; + List arrivalDepartures = new ArrayList<>(arrivalDeparturesUnsafe); + Collections.sort(arrivalDepartures, new IpcArrivalDepartureComparator()); + for (IpcArrivalDeparture tocheck : emptyIfNull(arrivalDepartures)) { + try { + if (tocheck.getStopPathIndex() == (current.getStopPathIndex() - 1) && (current.isArrival() && tocheck.isDeparture())) { + return tocheck; + } + } catch (Exception e) { + logger.error("Exception searching {}", e, e); + } + } + return null; + } + + private static Iterable emptyIfNull(Iterable iterable) { + return iterable == null ? Collections. emptyList() : iterable; + } + + @Override + public List getKeys() { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/ehcache/serializers/TripEventKyroSerializer.java b/transitclock/src/main/java/org/transitclock/core/dataCache/ehcache/serializers/TripEventKyroSerializer.java new file mode 100644 index 000000000..d94968546 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/dataCache/ehcache/serializers/TripEventKyroSerializer.java @@ -0,0 +1,47 @@ +package org.transitclock.core.dataCache.ehcache.serializers; + +import java.nio.ByteBuffer; + +import org.ehcache.spi.serialization.Serializer; +import org.ehcache.spi.serialization.SerializerException; +import org.transitclock.core.dataCache.TripEvents; +import org.transitclock.ipc.data.IpcArrivalDeparture; + +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.io.ByteBufferInputStream; +import com.esotericsoftware.kryo.io.Input; +import com.esotericsoftware.kryo.io.Output; +import com.esotericsoftware.kryo.serializers.FieldSerializer; + +public class TripEventKyroSerializer implements Serializer { + + private static final Kryo kryo = new Kryo(); + + public TripEventKyroSerializer(ClassLoader loader) { + //no-op + kryo.register(IpcArrivalDeparture.class); + + FieldSerializer serializer = new FieldSerializer(kryo, IpcArrivalDeparture.class); + serializer.setCopyTransient(false); + kryo.register(IpcArrivalDeparture.class, serializer); + } + + @Override + public ByteBuffer serialize(final TripEvents object) throws SerializerException { + Output output = new Output(4096,-1); + kryo.writeObject(output, object); + return ByteBuffer.wrap(output.getBuffer()); + } + + @Override + public TripEvents read(final ByteBuffer binary) throws ClassNotFoundException, SerializerException { + Input input = new Input(new ByteBufferInputStream(binary)) ; + return kryo.readObject(input, TripEvents.class); + } + + @Override + public boolean equals(final TripEvents object, final ByteBuffer binary) throws ClassNotFoundException, SerializerException { + return object.equals(read(binary)); + } + + } diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/frequency/FrequencyBasedHistoricalAverageCache.java b/transitclock/src/main/java/org/transitclock/core/dataCache/frequency/FrequencyBasedHistoricalAverageCache.java new file mode 100755 index 000000000..ba023f82b --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/dataCache/frequency/FrequencyBasedHistoricalAverageCache.java @@ -0,0 +1,449 @@ +package org.transitclock.core.dataCache.frequency; + +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.applications.Core; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.core.dataCache.*; +import org.transitclock.db.structs.ArrivalDeparture; +import org.transitclock.db.structs.Trip; +import org.transitclock.gtfs.DbConfig; +import org.transitclock.gtfs.GtfsData; +import org.transitclock.ipc.data.IpcArrivalDeparture; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +/** + * @author Sean Óg Crudden + * This class is to hold the historical average for frequency based services. It puts them in buckets that represent increments of time. The start time of the trip is used to decide which + * bucket to apply the data to or which average to retrieve. + */ +public class FrequencyBasedHistoricalAverageCache { + + private static FrequencyBasedHistoricalAverageCache singleton = new FrequencyBasedHistoricalAverageCache(); + + private static final Logger logger = LoggerFactory + .getLogger(FrequencyBasedHistoricalAverageCache.class); + + + + private static IntegerConfigValue minTravelTimeFilterValue= new IntegerConfigValue( + "transitclock.core.frequency.minTravelTimeFilterValue", + 0, + "The value to be included in average calculation for Travel times must exceed this value."); + + private static IntegerConfigValue maxTravelTimeFilterValue = new IntegerConfigValue( + "transitclock.core.frequency.maxTravelTimeFilterValue", + 600000, + "The value to be included in average calculation for Travel times must be less than this value."); + + private static IntegerConfigValue minDwellTimeFilterValue = new IntegerConfigValue( + "transitclock.core.frequency.minDwellTimeFilterValue", + 0, + "The value to be included in average calculation for dwell time must exceed this value."); + + private static IntegerConfigValue maxDwellTimeFilterValue = new IntegerConfigValue( + "transitclock.core.frequency.maxDwellTimeFilterValue", + 600000, + "The value to be included in average calculation for dwell time must be less this value."); + + private static IntegerConfigValue cacheIncrementsForFrequencyService = + new IntegerConfigValue( + "transitclock.core.frequency.cacheIncrementsForFrequencyService", + 180*60, + "This is the intervals size of the day that the average is applied to. "); + + public static int getCacheIncrementsForFrequencyService() { + return cacheIncrementsForFrequencyService.getValue(); + } + + + private final ConcurrentHashMap> m = new ConcurrentHashMap>(); + + /** + * Gets the singleton instance of this class. + * + * @return + */ + public static FrequencyBasedHistoricalAverageCache getInstance() { + return singleton; + } + + + private FrequencyBasedHistoricalAverageCache() { + } + public String toString() + { + + String totalsString=new String(); + for(StopPathKey key:m.keySet()) + { + TreeMap values=new TreeMap(); + + TreeMap map = m.get(key); + Set times = map.keySet(); + for(Long time:times) + { + values.put(time, map.get(time)); + + totalsString=totalsString+"\n"+key.tripId+","+key.stopPathIndex+","+key.travelTime+","+time+","+map.get(time).getCount()+","+map.get(time).getAverage(); + } + + } + + return totalsString+"\nDetails\n"+m.toString(); + } + public HistoricalAverage getAverage(StopPathCacheKey key) { + + logger.debug("Looking for average for : {} in FrequencyBasedHistoricalAverageCache cache.", key); + TreeMap result = m.get(new StopPathKey(key)); + if(result!=null) + { + logger.debug("Found average buckets for {}. ", key); + if(key.getStartTime()!=null) + { + SortedMap subresult = result.subMap(key.getStartTime(), key.getStartTime()+getCacheIncrementsForFrequencyService()); + + if(subresult.size()==1) + { + logger.debug("Found average for : {} in FrequencyBasedHistoricalAverageCache cache with a value : {}", key, subresult.get(subresult.lastKey())); + return subresult.get(subresult.lastKey()); + }else + { + logger.debug("No historical data within time range ({} to {}) for this trip {} in FrequencyBasedHistoricalAverageCache cache.",key.getStartTime(), key.getStartTime()+getCacheIncrementsForFrequencyService(), key); + } + } + } + logger.debug("No average found in FrequencyBasedHistoricalAverageCache cache for : {}",key); + return null; + } + private void putAverage(StopPathCacheKey key, HistoricalAverage average) { + + StopPathKey stopPathKey = new StopPathKey(key); + synchronized (m) { + TreeMap result = m.get(stopPathKey); + + if (result == null) { + result = new TreeMap(); + } + result.put(key.getStartTime(), average); + //logger.debug("Putting: {} for start time: {} in FrequencyBasedHistoricalAverageCache with value : {}",new StopPathKey(key), key.getStartTime(), average); + m.put(stopPathKey, result); + } + } + public void putArrivalDeparture(ArrivalDeparture arrivalDeparture) throws Exception + { + DbConfig dbConfig = Core.getInstance().getDbConfig(); + + Trip trip=dbConfig.getTrip(arrivalDeparture.getTripId()); + + if(trip!=null && trip.isNoSchedule()) + { + Integer time=secondsFromMidnight(arrivalDeparture.getDate(), 2); + + /* this is what puts the trip into the buckets (time slots) */ + time=round(time, getCacheIncrementsForFrequencyService()); + + TravelTimeResult pathDuration=getLastPathDuration(new IpcArrivalDeparture(arrivalDeparture), trip); + + if(pathDuration!=null && pathDuration.getDuration() > minTravelTimeFilterValue.getValue() && pathDuration.getDuration() < maxTravelTimeFilterValue.getValue()) + { + if(trip.isNoSchedule()) + { + StopPathCacheKey historicalAverageCacheKey=new StopPathCacheKey(trip.getId(), pathDuration.getArrival().getStopPathIndex(), true,new Long(time)); + + synchronized (m) { + HistoricalAverage average = FrequencyBasedHistoricalAverageCache.getInstance().getAverage(historicalAverageCacheKey); + + if (average == null) + average = new HistoricalAverage(); + + average.update(pathDuration.getDuration()); + + logger.debug("Putting : {} in FrequencyBasedHistoricalAverageCache cache for key : {} which results in : {}.", pathDuration, historicalAverageCacheKey, average); + + putAverage(historicalAverageCacheKey, average); + } + } + } + DwellTimeResult stopDuration=getLastStopDuration(new IpcArrivalDeparture(arrivalDeparture), trip); + if(stopDuration!=null && stopDuration.getDuration() > minDwellTimeFilterValue.getValue() && stopDuration.getDuration() < maxDwellTimeFilterValue.getValue()) + { + StopPathCacheKey historicalAverageCacheKey=new StopPathCacheKey(trip.getId(), stopDuration.getDeparture().getStopPathIndex(), false, new Long(time)); + + synchronized (m) { + HistoricalAverage average = getAverage(historicalAverageCacheKey); + + if (average == null) + average = new HistoricalAverage(); + + average.update(stopDuration.getDuration()); + + logger.debug("Putting : {} in FrequencyBasedHistoricalAverageCache cache for key : {} which results in : {}.", stopDuration, historicalAverageCacheKey, average); + + putAverage(historicalAverageCacheKey, average); + } + } + if(stopDuration==null && pathDuration==null) + { + logger.debug("Cannot add to FrequencyBasedHistoricalAverageCache as cannot calculate stopDuration or pathDuration. : {}", arrivalDeparture); + } + if(pathDuration!=null && (pathDuration.getDuration() < minTravelTimeFilterValue.getValue() || pathDuration.getDuration() > maxTravelTimeFilterValue.getValue())) + { + logger.debug("Cannot add to FrequencyBasedHistoricalAverageCache as pathDuration: {} is outside parameters. : {}",pathDuration, arrivalDeparture); + } + if(stopDuration!=null && (stopDuration.getDuration() < minDwellTimeFilterValue.getValue() || stopDuration.getDuration() > maxDwellTimeFilterValue.getValue())) + { + logger.debug("Cannot add to FrequencyBasedHistoricalAverageCache as stopDuration: {} is outside parameters. : {}",stopDuration, arrivalDeparture); + } + }else + { + logger.debug("Cannot add to FrequencyBasedHistoricalAverageCache as no start time set : {}", arrivalDeparture); + } + } + public IpcArrivalDeparture findPreviousDepartureEvent(List arrivalDepartures,IpcArrivalDeparture current) + { + Collections.sort(arrivalDepartures, new IpcArrivalDepartureComparator()); + for (IpcArrivalDeparture tocheck : emptyIfNull(arrivalDepartures)) + { + if(current.getFreqStartTime()!=null && tocheck.getFreqStartTime()!=null&&tocheck.getFreqStartTime().equals(current.getFreqStartTime())) + { + if(tocheck.getStopPathIndex()==(current.getStopPathIndex()-1) && (current.isArrival() && tocheck.isDeparture()) && current.getVehicleId().equals(tocheck.getVehicleId())) + { + return tocheck; + } + } + } + return null; + } + public IpcArrivalDeparture findPreviousArrivalEvent(List arrivalDepartures,IpcArrivalDeparture current) + { + Collections.sort(arrivalDepartures, new IpcArrivalDepartureComparator()); + for (IpcArrivalDeparture tocheck : emptyIfNull(arrivalDepartures)) + { + if(current.getFreqStartTime()!=null && tocheck.getFreqStartTime()!=null&&tocheck.getFreqStartTime().equals(current.getFreqStartTime())) + { + if(tocheck.getStopId().equals(current.getStopId()) && (current.isDeparture() && tocheck.isArrival()) && current.getVehicleId().equals(tocheck.getVehicleId())) + { + return tocheck; + } + } + } + + return null; + } + private TravelTimeResult getLastPathDuration(IpcArrivalDeparture arrivalDeparture, Trip trip) + { + Date nearestDay = DateUtils.truncate(new Date(arrivalDeparture.getTime().getTime()), Calendar.DAY_OF_MONTH); + TripKey tripKey = new TripKey(arrivalDeparture.getTripId(), + nearestDay, + trip.getStartTime()); + + List arrivalDepartures=(List) TripDataHistoryCacheFactory.getInstance().getTripHistory(tripKey); + + if(arrivalDepartures!=null && arrivalDepartures.size()>0 && arrivalDeparture.isArrival()) + { + IpcArrivalDeparture previousEvent = findPreviousDepartureEvent(arrivalDepartures, arrivalDeparture); + + if(previousEvent!=null && arrivalDeparture!=null && previousEvent.isDeparture()) + { + TravelTimeResult travelTimeResult=new TravelTimeResult(previousEvent,arrivalDeparture); + return travelTimeResult; + } + + } + + return null; + } + private DwellTimeResult getLastStopDuration(IpcArrivalDeparture arrivalDeparture, Trip trip) + { + Date nearestDay = DateUtils.truncate(new Date(arrivalDeparture.getTime().getTime()), Calendar.DAY_OF_MONTH); + TripKey tripKey = new TripKey(arrivalDeparture.getTripId(), + nearestDay, + trip.getStartTime()); + + List arrivalDepartures=(List) TripDataHistoryCacheFactory.getInstance().getTripHistory(tripKey); + + if(arrivalDepartures!=null && arrivalDepartures.size()>0 && arrivalDeparture.isDeparture()) + { + IpcArrivalDeparture previousEvent = findPreviousArrivalEvent(arrivalDepartures, arrivalDeparture); + + if(previousEvent!=null && arrivalDeparture!=null && previousEvent.isArrival()) + { + DwellTimeResult dwellTimeResult=new DwellTimeResult(previousEvent, arrivalDeparture); + return dwellTimeResult; + } + } + return null; + } + public void populateCacheFromDb(List resultsUnsafe) throws Exception + { + try { + if (resultsUnsafe == null) return; + List results = new ArrayList<>(resultsUnsafe); + Collections.sort(results, new ArrivalDepartureComparator()); + + int counter = 0; + for (ArrivalDeparture result : results) { + if (counter % 1000 == 0) { + logger.info("{} out of {} Frequency Based Historical Records for period ({}%)", counter, results.size(), (int) ((counter * 100.0f) / results.size())); + } + // TODO this might be better done in the database. + if (GtfsData.routeNotFiltered(result.getRouteId())) { + putArrivalDeparture(result); + } + counter++; + } + } catch (Throwable t) { + logger.error("Exception in populateCacheFromDb {}", t, t); + } + } + public static int round(double i, int v){ + return (int) (Math.floor(i/v) * v); + } + public static int secondsFromMidnight(Date date, int startHour) + { + Calendar c = Calendar.getInstance(); + c.setTimeInMillis(date.getTime()); + long now = c.getTimeInMillis(); + c.set(Calendar.HOUR_OF_DAY, startHour); + c.set(Calendar.MINUTE, 0); + c.set(Calendar.SECOND, 0); + c.set(Calendar.MILLISECOND, 0); + long passed = now - c.getTimeInMillis(); + long secondsPassed = passed / 1000; + + return (int)secondsPassed; + } + private static Iterable emptyIfNull(Iterable iterable) { + return iterable == null ? Collections. emptyList() : iterable; + } + private class StopPathKey + { + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + getOuterType().hashCode(); + result = prime * result + ((stopPathIndex == null) ? 0 : stopPathIndex.hashCode()); + result = prime * result + (travelTime ? 1231 : 1237); + 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; + StopPathKey other = (StopPathKey) obj; + if (!getOuterType().equals(other.getOuterType())) + return false; + if (stopPathIndex == null) { + if (other.stopPathIndex != null) + return false; + } else if (!stopPathIndex.equals(other.stopPathIndex)) + return false; + if (travelTime != other.travelTime) + return false; + if (tripId == null) { + if (other.tripId != null) + return false; + } else if (!tripId.equals(other.tripId)) + return false; + return true; + } + + + + @Override + public String toString() { + return "StopPathKey [tripId=" + tripId + ", stopPathIndex=" + stopPathIndex + ", travelTime=" + travelTime + + "]"; + } + + String tripId; + Integer stopPathIndex; + + private boolean travelTime=true; + + public boolean isTravelTime() { + return travelTime; + } + + public StopPathKey(StopPathCacheKey stopPathCacheKey) + { + this.tripId= stopPathCacheKey.getTripId(); + this.stopPathIndex = stopPathCacheKey.getStopPathIndex(); + this.travelTime = stopPathCacheKey.isTravelTime(); + } + + private FrequencyBasedHistoricalAverageCache getOuterType() { + return FrequencyBasedHistoricalAverageCache.this; + } + + + } + private class TravelTimeResult { + @Override + public String toString() { + return "TravelTimeResult [departure=" + departure + ", arrival=" + arrival + ", duration="+getDuration()+"]"; + } + + + + public TravelTimeResult(IpcArrivalDeparture previousEvent, IpcArrivalDeparture arrivalDeparture) { + // TODO Auto-generated constructor stub + } + + public ArrivalDeparture getArrival() { + return arrival; + } + + public ArrivalDeparture getDeparture() { + return departure; + } + public double getDuration() + { + return Math.abs(arrival.getTime()-departure.getTime()); + } + private ArrivalDeparture arrival=null; + private ArrivalDeparture departure=null; + + } + private class DwellTimeResult { + @Override + public String toString() { + return "DwellTimeResult [arrival=" + arrival + ", departure=" + departure + ", duration="+getDuration()+"]"; + } + + public DwellTimeResult(IpcArrivalDeparture arrival, IpcArrivalDeparture departure) { + super(); + this.arrival = arrival; + this.departure = departure; + } + + public IpcArrivalDeparture getArrival() { + return arrival; + } + + public IpcArrivalDeparture getDeparture() { + return departure; + } + public double getDuration() + { + return Math.abs(departure.getTime().getTime()-arrival.getTime().getTime()); + } + private IpcArrivalDeparture arrival=null; + private IpcArrivalDeparture departure=null; + } +} diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/frequency/StopPathCacheKeyStartTimeComparator.java b/transitclock/src/main/java/org/transitclock/core/dataCache/frequency/StopPathCacheKeyStartTimeComparator.java new file mode 100755 index 000000000..9be9b5138 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/dataCache/frequency/StopPathCacheKeyStartTimeComparator.java @@ -0,0 +1,14 @@ +package org.transitclock.core.dataCache.frequency; + +import java.util.Comparator; + +import org.transitclock.core.dataCache.StopPathCacheKey; + +public class StopPathCacheKeyStartTimeComparator implements Comparator{ + + @Override + public int compare(StopPathCacheKey key1, StopPathCacheKey key2) { + return key1.getStartTime().compareTo(key2.getStartTime()); + } + +} diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/jcs/KalmanErrorCache.java b/transitclock/src/main/java/org/transitclock/core/dataCache/jcs/KalmanErrorCache.java new file mode 100644 index 000000000..b11ade670 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/dataCache/jcs/KalmanErrorCache.java @@ -0,0 +1,105 @@ +package org.transitclock.core.dataCache.jcs; + +import org.apache.commons.jcs.JCS; +import org.apache.commons.jcs.access.CacheAccess; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.core.Indices; +import org.transitclock.core.dataCache.ErrorCache; +import org.transitclock.core.dataCache.KalmanError; +import org.transitclock.core.dataCache.KalmanErrorCacheKey; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +/** + * @author Sean Og Crudden + * + */ +public class KalmanErrorCache implements ErrorCache { + final private static String cacheName = "KalmanErrorCache"; + + private static final Logger logger = LoggerFactory + .getLogger(KalmanErrorCache.class); + + private CacheAccess cache = null; + + public KalmanErrorCache() { + cache = JCS.getInstance(cacheName); + + } + + /* (non-Javadoc) + * @see org.transitclock.core.dataCache.ErrorCache#getErrorValue(org.transitclock.core.Indices) + */ + @Override + @SuppressWarnings("unchecked") + synchronized public KalmanError getErrorValue(Indices indices) { + + KalmanErrorCacheKey key=new KalmanErrorCacheKey(indices); + + KalmanError result = cache.get(key); + + return result; + + } + /* (non-Javadoc) + * @see org.transitclock.core.dataCache.ErrorCache#getErrorValue(org.transitclock.core.dataCache.KalmanErrorCacheKey) + */ + @Override + @SuppressWarnings("unchecked") + synchronized public KalmanError getErrorValue(KalmanErrorCacheKey key) { + System.out.println(cache.getStats().toString()); + + KalmanError result = cache.get(key); + + return result; + } + /* (non-Javadoc) + * @see org.transitclock.core.dataCache.ErrorCache#putErrorValue(org.transitclock.core.Indices, java.lang.Double) + */ + @Override + @SuppressWarnings("unchecked") + synchronized public void putErrorValue(Indices indices, Double value) { + + KalmanErrorCacheKey key=new KalmanErrorCacheKey(indices); + + putErrorValue(key,value); + + } + + @Override + public void putErrorValue(KalmanErrorCacheKey key, Double value) { + + + KalmanError error= (KalmanError)cache.get(key); + + if(error==null) + { + error=new KalmanError(value); + }else + { + error.setError(value); + } + + + cache.put(key,error); + } + + + public List getKeys() { + ArrayList fulllist=new ArrayList(); + Set names = JCS.getGroupCacheInstance(cacheName).getGroupNames(); + + for(String name:names) + { + Set keys = JCS.getGroupCacheInstance(cacheName).getGroupKeys(name); + + for(Object key:keys) + { + fulllist.add((KalmanErrorCacheKey)key); + } + } + return fulllist; + } +} diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/jcs/frequency/DwellTimeModelCache.java b/transitclock/src/main/java/org/transitclock/core/dataCache/jcs/frequency/DwellTimeModelCache.java new file mode 100644 index 000000000..31b5cd0f4 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/dataCache/jcs/frequency/DwellTimeModelCache.java @@ -0,0 +1,242 @@ +package org.transitclock.core.dataCache.jcs.frequency; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.jcs.JCS; +import org.apache.commons.jcs.access.CacheAccess; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.applications.Core; +import org.transitclock.config.DoubleConfigValue; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.config.LongConfigValue; +import org.transitclock.core.Indices; +import org.transitclock.core.dataCache.StopArrivalDepartureCacheFactory; +import org.transitclock.core.dataCache.StopArrivalDepartureCacheKey; +import org.transitclock.core.dataCache.StopPathCacheKey; +import org.transitclock.core.dataCache.frequency.FrequencyBasedHistoricalAverageCache; +import org.transitclock.core.predictiongenerator.scheduled.dwell.rls.TransitClockRLS; +import org.transitclock.db.structs.ArrivalDeparture; +import org.transitclock.db.structs.Block; +import org.transitclock.db.structs.Headway; +import org.transitclock.gtfs.DbConfig; +import org.transitclock.ipc.data.IpcArrivalDeparture; +import org.transitclock.utils.Time; + +public class DwellTimeModelCache implements org.transitclock.core.dataCache.DwellTimeModelCacheInterface { + + final private static String cacheName = "DwellTimeModelCache"; + + private static IntegerConfigValue maxDwellTimeAllowedInModel = new IntegerConfigValue("org.transitclock.core.dataCache.jcs.maxDwellTimeAllowedInModel", 2 * Time.MS_PER_MIN, "Max dwell time to be considered in dwell RLS algotithm."); + private static LongConfigValue maxHeadwayAllowedInModel = new LongConfigValue("org.transitclock.core.dataCache.jcs.maxHeadwayAllowedInModel", 1*Time.MS_PER_HOUR, "Max headway to be considered in dwell RLS algotithm."); + + private static DoubleConfigValue lambda = new DoubleConfigValue("org.transitclock.core.dataCache.jcs.lambda", 0.75, "This sets the rate at which the RLS algorithm forgets old values. Value are between 0 and 1. With 0 being the most forgetful."); + + private CacheAccess cache = null; + + private static final Logger logger = LoggerFactory.getLogger(DwellTimeModelCache.class); + + public DwellTimeModelCache() { + cache = JCS.getInstance(cacheName); + } + @Override + synchronized public void addSample(ArrivalDeparture event, Headway headway, long dwellTime) { + + Integer time=FrequencyBasedHistoricalAverageCache.secondsFromMidnight(event.getFreqStartTime(),2); + + time=FrequencyBasedHistoricalAverageCache.round(time, FrequencyBasedHistoricalAverageCache.getCacheIncrementsForFrequencyService()); + + StopPathCacheKey key=new StopPathCacheKey(event.getTripId(), event.getStopPathIndex(), false, new Long(time)); + + TransitClockRLS rls = null; + if(cache.get(key)!=null) + { + rls=cache.get(key); + + double[] x = new double[1]; + x[0]=headway.getHeadway(); + + double y = Math.log10(dwellTime); + + + double[] arg0 = new double[1]; + arg0[0]=headway.getHeadway(); + if(rls.getRls()!=null) + { + double prediction = Math.pow(10,rls.getRls().predict(arg0)); + + logger.debug("Predicted dwell: "+prediction + " for: "+key + " based on headway: "+TimeUnit.MILLISECONDS.toMinutes((long) headway.getHeadway())+" mins"); + + logger.debug("Actual dwell: "+ dwellTime + " for: "+key + " based on headway: "+TimeUnit.MILLISECONDS.toMinutes((long) headway.getHeadway())+" mins"); + } + + rls.addSample(headway.getHeadway(), Math.log10(dwellTime)); + if(rls.getRls()!=null) + { + double prediction = Math.pow(10,rls.getRls().predict(arg0)); + + logger.debug("Predicted dwell after: "+ prediction + " for: "+key+ " with samples: "+rls.numSamples()); + } + }else + { + + rls=new TransitClockRLS(lambda.getValue()); + rls.addSample(headway.getHeadway(), Math.log10(dwellTime)); + } + cache.put(key,rls); + } + + @Override + public void addSample(ArrivalDeparture departure) { + try { + if(departure!=null && !departure.isArrival()) + { + Block block=null; + if(departure.getBlock()==null) + { + DbConfig dbConfig = Core.getInstance().getDbConfig(); + block=dbConfig.getBlock(departure.getServiceId(), departure.getBlockId()); + }else + { + block=departure.getBlock(); + } + + Indices indices = new Indices(departure); + StopArrivalDepartureCacheKey key= new StopArrivalDepartureCacheKey(departure.getStopId(), departure.getDate()); + List stopData = StopArrivalDepartureCacheFactory.getInstance().getStopHistory(key); + + if(stopData!=null && stopData.size()>1) + { + IpcArrivalDeparture arrival=findArrival(stopData, new IpcArrivalDeparture(departure)); + if(arrival!=null) + { + IpcArrivalDeparture previousArrival=findPreviousArrival(stopData, arrival); + if(arrival!=null&&previousArrival!=null) + { + Headway headway=new Headway(); + headway.setHeadway(arrival.getTime().getTime()-previousArrival.getTime().getTime()); + long dwelltime=departure.getTime()-arrival.getTime().getTime(); + headway.setTripId(arrival.getTripId()); + + // Negative dwell times are errors in data so do not include. + // TODO not sure if it should ignore zero values. + if(dwelltime>=0) + { + /* Leave out silly values as they are most likely errors or unusual circumstance. */ + if(dwelltime stopData, IpcArrivalDeparture arrival) { + for(IpcArrivalDeparture event:stopData) + { + if(event.isArrival()) + { + if(!event.getVehicleId().equals(arrival.getVehicleId())) + { + if(!event.getTripId().equals(arrival.getTripId())) + { + if(event.getStopId().equals(arrival.getStopId())) + { + if(event.getTime().getTime() stopData, IpcArrivalDeparture departure) { + + for(IpcArrivalDeparture event:stopData) + { + if(event.isArrival()) + { + if(event.getStopId().equals(departure.getStopId())) + { + if(event.getVehicleId().equals(departure.getVehicleId())) + { + if(event.getTripId().equals(departure.getTripId())) + { + return event; + } + } + } + } + } + return null; + } + @Override + public Long predictDwellTime(StopPathCacheKey cacheKey, Headway headway) { + + TransitClockRLS rls=cache.get(cacheKey); + if(rls!=null&&rls.getRls()!=null) + { + double[] arg0 = new double[1]; + arg0[0]=headway.getHeadway(); + rls.getRls().predict(arg0); + long prediction = (long) Math.pow(10, rls.getRls().predict(arg0)); + + // If silly values returned then need to reset model and allow it use the super prediction. + if(prediction>maxDwellTimeAllowedInModel.getValue()) + { + cache.remove(cacheKey); + return null; + } + return prediction; + }else + { + return null; + } + } + + @Override + public void populateCacheFromDb(List results) { + synchronized (cache) { + for (ArrivalDeparture result : results) { + addSample(result); + } + } + } + + public static void main(String[] args) + { + double startvalue=1000; + double result1 = Math.log10(startvalue); + double result2 = Math.pow(10, result1); + if(startvalue==result2) + System.out.println("As expected they are the same."); + } + +} diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/jcs/frequency/TripDataHistoryCache.java b/transitclock/src/main/java/org/transitclock/core/dataCache/jcs/frequency/TripDataHistoryCache.java new file mode 100644 index 000000000..f37722bd3 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/dataCache/jcs/frequency/TripDataHistoryCache.java @@ -0,0 +1,186 @@ +package org.transitclock.core.dataCache.jcs.frequency; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Set; + +import org.apache.commons.jcs.JCS; +import org.apache.commons.jcs.access.CacheAccess; +import org.apache.commons.lang3.time.DateUtils; +import org.hibernate.Criteria; +import org.hibernate.Session; +import org.hibernate.criterion.Restrictions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.applications.Core; +import org.transitclock.core.dataCache.ArrivalDepartureComparator; +import org.transitclock.core.dataCache.IpcArrivalDepartureComparator; +import org.transitclock.core.dataCache.TripDataHistoryCacheFactory; +import org.transitclock.core.dataCache.TripDataHistoryCacheInterface; +import org.transitclock.core.dataCache.TripKey; +import org.transitclock.core.dataCache.frequency.FrequencyBasedHistoricalAverageCache; +import org.transitclock.db.structs.ArrivalDeparture; +import org.transitclock.db.structs.Trip; +import org.transitclock.gtfs.DbConfig; +import org.transitclock.gtfs.GtfsData; +import org.transitclock.ipc.data.IpcArrivalDeparture; + + + +public class TripDataHistoryCache implements TripDataHistoryCacheInterface { + final private static String cacheName = "FrequencyTripDataHistoryCache"; + + private static final Logger logger = LoggerFactory + .getLogger(TripDataHistoryCache.class); + + private CacheAccess> cache = null; + + + public List getKeys() { + ArrayList fulllist=new ArrayList(); + Set names = JCS.getGroupCacheInstance(cacheName).getGroupNames(); + + for(String name:names) + { + Set keys = JCS.getGroupCacheInstance(cacheName).getGroupKeys(name); + + for(Object key:keys) + { + fulllist.add((TripKey)key); + } + } + return fulllist; + } + + public TripDataHistoryCache() { + cache = JCS.getInstance(cacheName); + } + + + public void logCache(Logger logger) { + + logger.debug("Cache content log. Not implemented."); + } + + @Override + public List getTripHistory(TripKey tripKey) { + + /* this is what gets the trip from the buckets */ + int time = FrequencyBasedHistoricalAverageCache.round(tripKey.getStartTime(), FrequencyBasedHistoricalAverageCache.getCacheIncrementsForFrequencyService()); + + tripKey.setStartTime(time); + + return cache.get(tripKey); + } + + @Override + synchronized public TripKey putArrivalDeparture(ArrivalDeparture arrivalDeparture) { + logger.debug("Putting :"+arrivalDeparture.toString() + " in TripDataHistoryCache cache."); + /* just put todays time in for last three days to aid development. This means it will kick in in 1 days rather than 3. Perhaps be a good way to start rather than using default transiTime method but I doubt it. */ + int days_back=1; + + TripKey tripKey=null; + + for(int i=0;i < days_back;i++) + { + Date nearestDay = DateUtils.truncate(new Date(arrivalDeparture.getTime()), Calendar.DAY_OF_MONTH); + + nearestDay=DateUtils.addDays(nearestDay, i*-1); + + DbConfig dbConfig = Core.getInstance().getDbConfig(); + + Trip trip=dbConfig.getTrip(arrivalDeparture.getTripId()); + + if(trip!=null) + { + Integer time=FrequencyBasedHistoricalAverageCache.secondsFromMidnight(arrivalDeparture.getDate(),2); + + /* this is what gets the trip from the buckets */ + time=FrequencyBasedHistoricalAverageCache.round(time, FrequencyBasedHistoricalAverageCache.getCacheIncrementsForFrequencyService()); + + tripKey = new TripKey(arrivalDeparture.getTripId(), + nearestDay, + time); + + List list = cache.get(tripKey); + + if(list==null) + list = new ArrayList(); + + try { + list.add(new IpcArrivalDeparture(arrivalDeparture)); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + cache.put(tripKey, Collections.synchronizedList(list)); + } + } + return tripKey; + + } + + /* (non-Javadoc) + * @see org.transitclock.core.dataCache.TripDataHistoryCacheInterface#populateCacheFromDb(org.hibernate.Session, java.util.Date, java.util.Date) + */ + + @Override + public void populateCacheFromDb(List results) + { + + for(ArrivalDeparture result : results) + { + // TODO this might be better done in the database. + if(GtfsData.routeNotFiltered(result.getRouteId())) + { + TripDataHistoryCacheFactory.getInstance().putArrivalDeparture(result); + } + } + } + + /* (non-Javadoc) + * @see org.transitclock.core.dataCache.ehcache.test#findPreviousArrivalEvent(java.util.List, org.transitclock.db.structs.ArrivalDeparture) + */ + @Override + public IpcArrivalDeparture findPreviousArrivalEvent(List arrivalDepartures,IpcArrivalDeparture current) + { + Collections.sort(arrivalDepartures, new IpcArrivalDepartureComparator()); + for (IpcArrivalDeparture tocheck : emptyIfNull(arrivalDepartures)) + { + if(tocheck.getStopId().equals(current.getStopId()) && (current.isDeparture() && tocheck.isArrival())) + { + return tocheck; + } + } + return null; + } + /* (non-Javadoc) + * @see org.transitclock.core.dataCache.ehcache.test#findPreviousDepartureEvent(java.util.List, org.transitclock.db.structs.ArrivalDeparture) + */ + @Override + public IpcArrivalDeparture findPreviousDepartureEvent(List arrivalDepartures, IpcArrivalDeparture current) + { + Collections.sort(arrivalDepartures, new IpcArrivalDepartureComparator()); + for (IpcArrivalDeparture tocheck : emptyIfNull(arrivalDepartures)) + { + try { + if(tocheck.getStopPathIndex()==(current.getStopPathIndex()-1) + && (current.isArrival() && tocheck.isDeparture()) + && current.getFreqStartTime().equals(tocheck.getFreqStartTime())) + { + return tocheck; + } + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + return null; + } + private static Iterable emptyIfNull(Iterable iterable) { + return iterable == null ? Collections. emptyList() : iterable; + } +} diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/jcs/scheduled/DwellTimeModelCache.java b/transitclock/src/main/java/org/transitclock/core/dataCache/jcs/scheduled/DwellTimeModelCache.java new file mode 100644 index 000000000..5e6cf7aee --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/dataCache/jcs/scheduled/DwellTimeModelCache.java @@ -0,0 +1,248 @@ +package org.transitclock.core.dataCache.jcs.scheduled; + +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.jcs.JCS; +import org.apache.commons.jcs.access.CacheAccess; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.applications.Core; +import org.transitclock.config.DoubleConfigValue; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.config.LongConfigValue; +import org.transitclock.core.Indices; +import org.transitclock.core.dataCache.StopArrivalDepartureCacheFactory; +import org.transitclock.core.dataCache.StopArrivalDepartureCacheKey; +import org.transitclock.core.dataCache.StopPathCacheKey; +import org.transitclock.core.predictiongenerator.scheduled.dwell.rls.TransitClockRLS; +import org.transitclock.db.structs.ArrivalDeparture; +import org.transitclock.db.structs.Block; +import org.transitclock.db.structs.Headway; +import org.transitclock.gtfs.DbConfig; +import org.transitclock.ipc.data.IpcArrivalDeparture; +import org.transitclock.utils.Time; + +public class DwellTimeModelCache implements org.transitclock.core.dataCache.DwellTimeModelCacheInterface { + + final private static String cacheName = "DwellTimeModelCache"; + + + private static LongConfigValue maxDwellTimeAllowedInModel = new LongConfigValue("transitclock.prediction.rls.maxDwellTimeAllowedInModel", (long) (2 * Time.MS_PER_MIN), "Max dwell time to be considered in dwell RLS algotithm."); + private static LongConfigValue minDwellTimeAllowedInModel = new LongConfigValue("transitclock.prediction.rls.minDwellTimeAllowedInModel", (long) 1000, "Min dwell time to be considered in dwell RLS algotithm."); + private static LongConfigValue maxHeadwayAllowedInModel = new LongConfigValue("transitclock.prediction.rls.maxHeadwayAllowedInModel", 1*Time.MS_PER_HOUR, "Max headway to be considered in dwell RLS algotithm."); + private static LongConfigValue minHeadwayAllowedInModel = new LongConfigValue("transitclock.prediction.rls.minHeadwayAllowedInModel", (long) 1000, "Min headway to be considered in dwell RLS algotithm."); + private static IntegerConfigValue minSceheduleAdherence = new IntegerConfigValue("transitclock.prediction.rls.minSceheduleAdherence", (int) (10 * Time.SEC_PER_MIN), "If schedule adherence of vehicle is outside this then not considerd in dwell RLS algorithm."); + private static IntegerConfigValue maxSceheduleAdherence = new IntegerConfigValue("transitclock.prediction.rls.maxSceheduleAdherence", (int) (10 * Time.SEC_PER_MIN), "If schedule adherence of vehicle is outside this then not considerd in dwell RLS algorithm."); + + + + + private static DoubleConfigValue lambda = new DoubleConfigValue("transitclock.prediction.rls.lambda", 0.75, "This sets the rate at which the RLS algorithm forgets old values. Value are between 0 and 1. With 0 being the most forgetful."); + + private CacheAccess cache = null; + + private static final Logger logger = LoggerFactory.getLogger(DwellTimeModelCache.class); + + public DwellTimeModelCache() { + cache = JCS.getInstance(cacheName); + } + @Override + synchronized public void addSample(ArrivalDeparture event, Headway headway, long dwellTime) { + + StopPathCacheKey key=new StopPathCacheKey(headway.getTripId(), event.getStopPathIndex()); + + + TransitClockRLS rls = null; + if(cache.get(key)!=null) + { + rls=cache.get(key); + + double[] x = new double[1]; + x[0]=headway.getHeadway(); + + double y = Math.log10(dwellTime); + + + double[] arg0 = new double[1]; + arg0[0]=headway.getHeadway(); + if(rls.getRls()!=null) + { + double prediction = Math.pow(10,rls.getRls().predict(arg0)); + + logger.debug("Predicted dwell: "+prediction + " for: "+key + " based on headway: "+TimeUnit.MILLISECONDS.toMinutes((long) headway.getHeadway())+" mins"); + + logger.debug("Actual dwell: "+ dwellTime + " for: "+key + " based on headway: "+TimeUnit.MILLISECONDS.toMinutes((long) headway.getHeadway())+" mins"); + } + + rls.addSample(headway.getHeadway(), Math.log10(dwellTime)); + if(rls.getRls()!=null) + { + double prediction = Math.pow(10,rls.getRls().predict(arg0)); + + logger.debug("Predicted dwell after: "+ prediction + " for: "+key+ " with samples: "+rls.numSamples()); + } + }else + { + + rls=new TransitClockRLS(lambda.getValue()); + rls.addSample(headway.getHeadway(), Math.log10(dwellTime)); + } + cache.put(key,rls); + } + + @Override + public void addSample(ArrivalDeparture departure) { + + try { + if(departure!=null && !departure.isArrival()) + { + StopArrivalDepartureCacheKey key= new StopArrivalDepartureCacheKey(departure.getStopId(), departure.getDate()); + List stopData = StopArrivalDepartureCacheFactory.getInstance().getStopHistory(key); + + if(stopData!=null && stopData.size()>1) + { + IpcArrivalDeparture arrival=findArrival(stopData, new IpcArrivalDeparture(departure)); + if(arrival!=null) + { + IpcArrivalDeparture previousArrival=findPreviousArrival(stopData, arrival); + if(arrival!=null&&previousArrival!=null) + { + Headway headway=new Headway(); + headway.setHeadway(arrival.getTime().getTime()-previousArrival.getTime().getTime()); + long dwelltime=departure.getTime()-arrival.getTime().getTime(); + headway.setTripId(arrival.getTripId()); + + /* Leave out silly values as they are most likely errors or unusual circumstance. */ + /* TODO Should abstract this behind an anomaly detention interface/Factory */ + + if(departure.getScheduleAdherence()!=null && departure.getScheduleAdherence().isWithinBounds(minSceheduleAdherence.getValue(),maxSceheduleAdherence.getValue())) + { + + // Arrival schedule adherence appears not to be set alot. So only stop if set and outside range. + if(previousArrival.getScheduledAdherence()==null || previousArrival.getScheduledAdherence().isWithinBounds(minSceheduleAdherence.getValue(),maxSceheduleAdherence.getValue())) + { + if(dwelltime minDwellTimeAllowedInModel.getValue()) + { + if(headway.getHeadway() < maxHeadwayAllowedInModel.getValue() + && headway.getHeadway() > minHeadwayAllowedInModel.getValue()) + { + addSample(departure,headway,dwelltime); + }else + { + + } + }else + { + logger.info("Dwell time {} outside allowable range for {}.", dwelltime, departure); + } + }else + { + logger.info("Schedule adherence outside allowable range. "+previousArrival.getScheduledAdherence()); + } + }else + { + logger.info("Schedule adherence outside allowable range. "+departure.getScheduleAdherence()); + } + + } + } + } + } + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + + private IpcArrivalDeparture findPreviousArrival(List stopData, IpcArrivalDeparture arrival) { + for(IpcArrivalDeparture event:stopData) + { + if(event.isArrival()) + { + if(!event.getVehicleId().equals(arrival.getVehicleId())) + { + if(!event.getTripId().equals(arrival.getTripId())) + { + if(event.getStopId().equals(arrival.getStopId())) + { + if(event.getTime().getTime() stopData, IpcArrivalDeparture departure) { + + for(IpcArrivalDeparture event:stopData) + { + if(event.isArrival()) + { + if(event.getStopId().equals(departure.getStopId())) + { + if(event.getVehicleId().equals(departure.getVehicleId())) + { + if(event.getTripId().equals(departure.getTripId())) + { + return event; + } + } + } + } + } + return null; + } + @Override + public Long predictDwellTime(StopPathCacheKey cacheKey, Headway headway) { + + TransitClockRLS rls=cache.get(cacheKey); + if(rls!=null&&rls.getRls()!=null) + { + double[] arg0 = new double[1]; + arg0[0]=headway.getHeadway(); + rls.getRls().predict(arg0); + return (long) Math.pow(10, rls.getRls().predict(arg0)); + }else + { + return null; + } + } + + @Override + public void populateCacheFromDb(List results) { + synchronized (cache) { + for (ArrivalDeparture result : results) { + addSample(result); + } + } + } + + public static void main(String[] args) + { + double startvalue=1000; + double result1 = Math.log10(startvalue); + double result2 = Math.pow(10, result1); + if(startvalue==result2) + System.out.println("As expected they are the same."); + } + +} diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/jcs/scheduled/TripDataHistoryCache.java b/transitclock/src/main/java/org/transitclock/core/dataCache/jcs/scheduled/TripDataHistoryCache.java new file mode 100644 index 000000000..d67a3dfa3 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/dataCache/jcs/scheduled/TripDataHistoryCache.java @@ -0,0 +1,172 @@ +package org.transitclock.core.dataCache.jcs.scheduled; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Set; + +import org.apache.commons.jcs.JCS; +import org.apache.commons.jcs.access.CacheAccess; +import org.apache.commons.lang3.time.DateUtils; +import org.hibernate.Criteria; +import org.hibernate.Session; +import org.hibernate.criterion.Restrictions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.applications.Core; +import org.transitclock.core.dataCache.ArrivalDepartureComparator; +import org.transitclock.core.dataCache.IpcArrivalDepartureComparator; +import org.transitclock.core.dataCache.KalmanErrorCacheKey; +import org.transitclock.core.dataCache.TripDataHistoryCacheFactory; +import org.transitclock.core.dataCache.TripDataHistoryCacheInterface; +import org.transitclock.core.dataCache.TripKey; +import org.transitclock.db.structs.ArrivalDeparture; +import org.transitclock.db.structs.Trip; +import org.transitclock.gtfs.DbConfig; +import org.transitclock.gtfs.GtfsData; +import org.transitclock.ipc.data.IpcArrivalDeparture; + + + +public class TripDataHistoryCache implements TripDataHistoryCacheInterface { + final private static String cacheName = "TripDataHistoryCache"; + + private static final Logger logger = LoggerFactory + .getLogger(TripDataHistoryCache.class); + + private CacheAccess> cache = null; + + + public List getKeys() { + ArrayList fulllist=new ArrayList(); + Set names = JCS.getGroupCacheInstance(cacheName).getGroupNames(); + + for(String name:names) + { + Set keys = JCS.getGroupCacheInstance(cacheName).getGroupKeys(name); + + for(Object key:keys) + { + fulllist.add((TripKey)key); + } + } + return fulllist; + } + + public TripDataHistoryCache() { + cache = JCS.getInstance(cacheName); + } + + + public void logCache(Logger logger) { + + logger.debug("Cache content log. Not implemented."); + } + + @Override + public List getTripHistory(TripKey tripKey) { + return cache.get(tripKey); + } + + @Override + synchronized public TripKey putArrivalDeparture(ArrivalDeparture arrivalDeparture) { + logger.debug("Putting :"+arrivalDeparture.toString() + " in TripDataHistoryCache cache."); + /* just put todays time in for last three days to aid development. This means it will kick in in 1 days rather than 3. Perhaps be a good way to start rather than using default transiTime method but I doubt it. */ + int days_back=1; + + TripKey tripKey=null; + + for(int i=0;i < days_back;i++) + { + Date nearestDay = DateUtils.truncate(new Date(arrivalDeparture.getTime()), Calendar.DAY_OF_MONTH); + + nearestDay=DateUtils.addDays(nearestDay, i*-1); + + DbConfig dbConfig = Core.getInstance().getDbConfig(); + + Trip trip=dbConfig.getTrip(arrivalDeparture.getTripId()); + + if(trip!=null) + { + tripKey = new TripKey(arrivalDeparture.getTripId(), + nearestDay, + trip.getStartTime()); + + List list = cache.get(tripKey); + + if(list==null) + list = new ArrayList(); + + try { + list.add(new IpcArrivalDeparture(arrivalDeparture)); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + cache.put(tripKey, Collections.synchronizedList(list)); + } + } + return tripKey; + + } + + /* (non-Javadoc) + * @see org.transitclock.core.dataCache.TripDataHistoryCacheInterface#populateCacheFromDb(org.hibernate.Session, java.util.Date, java.util.Date) + */ + + @Override + public void populateCacheFromDb(List results) + { + for(ArrivalDeparture result : results) + { + // TODO this might be better done in the database. + if(GtfsData.routeNotFiltered(result.getRouteId())) + { + TripDataHistoryCacheFactory.getInstance().putArrivalDeparture(result); + } + } + } + + /* (non-Javadoc) + * @see org.transitclock.core.dataCache.ehcache.test#findPreviousArrivalEvent(java.util.List, org.transitclock.db.structs.ArrivalDeparture) + */ + @Override + public IpcArrivalDeparture findPreviousArrivalEvent(List arrivalDepartures,IpcArrivalDeparture current) + { + Collections.sort(arrivalDepartures, new IpcArrivalDepartureComparator()); + for (IpcArrivalDeparture tocheck : emptyIfNull(arrivalDepartures)) + { + if(tocheck.getStopId().equals(current.getStopId()) && (current.isDeparture() && tocheck.isArrival())) + { + return tocheck; + } + } + return null; + } + /* (non-Javadoc) + * @see org.transitclock.core.dataCache.ehcache.test#findPreviousDepartureEvent(java.util.List, org.transitclock.db.structs.ArrivalDeparture) + */ + @Override + public IpcArrivalDeparture findPreviousDepartureEvent(List arrivalDepartures,IpcArrivalDeparture current) + { + Collections.sort(arrivalDepartures, new IpcArrivalDepartureComparator()); + for (IpcArrivalDeparture tocheck : emptyIfNull(arrivalDepartures)) + { + try { + if(tocheck.getStopPathIndex()==(current.getStopPathIndex()-1) && (current.isArrival() && tocheck.isDeparture())) + { + return tocheck; + } + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + return null; + } + private static Iterable emptyIfNull(Iterable iterable) { + return iterable == null ? Collections. emptyList() : iterable; + } +} diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/memcached/scheduled/DwellTimeModelCache.java b/transitclock/src/main/java/org/transitclock/core/dataCache/memcached/scheduled/DwellTimeModelCache.java new file mode 100644 index 000000000..902e791e1 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/dataCache/memcached/scheduled/DwellTimeModelCache.java @@ -0,0 +1,204 @@ +package org.transitclock.core.dataCache.memcached.scheduled; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.config.LongConfigValue; +import org.transitclock.config.StringConfigValue; +import org.transitclock.core.dataCache.StopArrivalDepartureCacheFactory; +import org.transitclock.core.dataCache.StopArrivalDepartureCacheKey; +import org.transitclock.core.dataCache.StopPathCacheKey; +import org.transitclock.core.predictiongenerator.scheduled.dwell.DwellTimeModelFactory; +import org.transitclock.core.predictiongenerator.datafilter.DwellTimeDataFilter; +import org.transitclock.core.predictiongenerator.datafilter.DwellTimeFilterFactory; +import org.transitclock.core.predictiongenerator.scheduled.dwell.DwellModel; +import org.transitclock.db.structs.ArrivalDeparture; +import org.transitclock.db.structs.Headway; +import org.transitclock.ipc.data.IpcArrivalDeparture; +import org.transitclock.utils.Time; + +import net.spy.memcached.MemcachedClient; +/** + * + * @author scrudden + * This stores DwellModel instances in the cache. TODO We should abstract the anomaly detection as per TODO in code below. + */ +public class DwellTimeModelCache implements org.transitclock.core.dataCache.DwellTimeModelCacheInterface { + + private static LongConfigValue maxHeadwayAllowedInModel = new LongConfigValue("transitclock.prediction.dwell.maxHeadwayAllowedInModel", 1*Time.MS_PER_HOUR, "Max headway to be considered in dwell RLS algotithm."); + private static LongConfigValue minHeadwayAllowedInModel = new LongConfigValue("transitclock.prediction.dwell.minHeadwayAllowedInModel", (long) 1000, "Min headway to be considered in dwell RLS algotithm."); + + + private static StringConfigValue memcachedHost = new StringConfigValue("transitclock.cache.memcached.host", "127.0.0.1", + "Specifies the host machine that memcache is running on."); + + private static IntegerConfigValue memcachedPort = new IntegerConfigValue("transitclock.cache.memcached.port", 11211, + "Specifies the port that memcache is running on."); + + + private static final Logger logger = LoggerFactory.getLogger(DwellTimeModelCache.class); + MemcachedClient memcachedClient = null; + private static String keystub = "DWELLTIMEMODEL_"; + Integer expiryDuration=Time.SEC_PER_DAY*7; + public DwellTimeModelCache() throws IOException { + memcachedClient = new MemcachedClient( + new InetSocketAddress(memcachedHost.getValue(), memcachedPort.getValue().intValue())); + } + @Override + synchronized public void addSample(ArrivalDeparture event, Headway headway, long dwellTime) { + + StopPathCacheKey key=new StopPathCacheKey(headway.getTripId(), event.getStopPathIndex()); + + DwellModel model = null; + + if(memcachedClient.get(createKey(key))!=null) + { + model=(DwellModel) memcachedClient.get(createKey(key)); + + model.putSample((int)dwellTime, (int)headway.getHeadway(),null); + }else + { + model=DwellTimeModelFactory.getInstance(); + } + model.putSample((int)dwellTime, (int)headway.getHeadway(),null); + memcachedClient.set(createKey(key),expiryDuration, model); + } + + @Override + public void addSample(ArrivalDeparture departure) { + try { + if(departure!=null && !departure.isArrival()) + { + StopArrivalDepartureCacheKey key= new StopArrivalDepartureCacheKey(departure.getStopId(), departure.getDate()); + List stopData = StopArrivalDepartureCacheFactory.getInstance().getStopHistory(key); + + if(stopData!=null && stopData.size()>1) + { + IpcArrivalDeparture arrival=findArrival(stopData, new IpcArrivalDeparture(departure)); + if(arrival!=null) + { + IpcArrivalDeparture previousArrival=findPreviousArrival(stopData, arrival); + if(arrival!=null&&previousArrival!=null) + { + Headway headway=new Headway(); + headway.setHeadway(arrival.getTime().getTime()-previousArrival.getTime().getTime()); + long dwelltime=departure.getTime()-arrival.getTime().getTime(); + headway.setTripId(arrival.getTripId()); + + /* Leave out silly values as they are most likely errors or unusual circumstance. */ + DwellTimeDataFilter datafilter = DwellTimeFilterFactory.getInstance(); + + if(!datafilter.filter(arrival, new IpcArrivalDeparture(departure))) + { + + /* TODO Should also abstract behind an anomaly detention interface/Factory */ + if(headway.getHeadway() < maxHeadwayAllowedInModel.getValue() + && headway.getHeadway() > minHeadwayAllowedInModel.getValue()) + { + addSample(departure,headway,dwelltime); + }else + { + logger.warn("Headway outside allowable range . {}", headway); + } + } + } + } + } + } + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + private IpcArrivalDeparture findPreviousArrival(List stopData, IpcArrivalDeparture arrival) { + for(IpcArrivalDeparture event:stopData) + { + if(event.isArrival()) + { + if(!event.getVehicleId().equals(arrival.getVehicleId())) + { + if(!event.getTripId().equals(arrival.getTripId())) + { + if(event.getStopId().equals(arrival.getStopId())) + { + if(event.getTime().getTime() stopData, IpcArrivalDeparture departure) { + + for(IpcArrivalDeparture event:stopData) + { + if(event.isArrival()) + { + if(event.getStopId().equals(departure.getStopId())) + { + if(event.getVehicleId().equals(departure.getVehicleId())) + { + if(event.getTripId().equals(departure.getTripId())) + { + return event; + } + } + } + } + } + return null; + } + @Override + public Long predictDwellTime(StopPathCacheKey cacheKey, Headway headway) { + + DwellModel model=(DwellModel) memcachedClient.get(createKey(cacheKey)); + if(model==null||headway==null) + return null; + + if(model.predict((int)headway.getHeadway(), null)!=null) + return new Long(model.predict((int)headway.getHeadway(), null)); + + return null; + } + + @Override + public void populateCacheFromDb(List results) { + for (ArrivalDeparture result: results) { + addSample(result); + } + } + + public static void main(String[] args) + { + double startvalue=1000; + double result1 = Math.log10(startvalue); + double result2 = Math.pow(10, result1); + if(startvalue==result2) + System.out.println("As expected they are the same."); + } + private String createKey(StopPathCacheKey key) + { + return keystub + key.getTripId() + "_" + key.getStopPathIndex(); + } + +} diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/memcached/scheduled/KalmanErrorCache.java b/transitclock/src/main/java/org/transitclock/core/dataCache/memcached/scheduled/KalmanErrorCache.java new file mode 100644 index 000000000..3bbd8009c --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/dataCache/memcached/scheduled/KalmanErrorCache.java @@ -0,0 +1,83 @@ +package org.transitclock.core.dataCache.memcached.scheduled; + +import net.spy.memcached.MemcachedClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.config.StringConfigValue; +import org.transitclock.core.Indices; +import org.transitclock.core.dataCache.ErrorCache; +import org.transitclock.core.dataCache.KalmanError; +import org.transitclock.core.dataCache.KalmanErrorCacheKey; +import org.transitclock.utils.Time; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.List; + +public class KalmanErrorCache implements ErrorCache { + + private static StringConfigValue memcachedHost = new StringConfigValue("transitclock.cache.memcached.host", "127.0.0.1", + "Specifies the host machine that memcache is running on."); + + private static IntegerConfigValue memcachedPort = new IntegerConfigValue("transitclock.cache.memcached.port", 11211, + "Specifies the port that memcache is running on."); + + MemcachedClient memcachedClient = null; + private static String keystub = "KALMANERROR_"; + Integer expiryDuration=Time.SEC_PER_DAY*28; + + private static final Logger logger = LoggerFactory + .getLogger(KalmanErrorCache.class); + + public KalmanErrorCache() throws IOException { + memcachedClient = new MemcachedClient( + new InetSocketAddress(memcachedHost.getValue(), memcachedPort.getValue().intValue())); + } + + @Override + public KalmanError getErrorValue(Indices indices) { + KalmanErrorCacheKey key=new KalmanErrorCacheKey(indices); + + return getErrorValue(key); + } + + @Override + public KalmanError getErrorValue(KalmanErrorCacheKey key) { + + Double errorValue = (Double) memcachedClient.get(createKey(key)); + if (errorValue == null || errorValue.isNaN()) { + return null; + } + return new KalmanError(errorValue); + } + + @Override + public void putErrorValue(Indices indices, Double value) { + + + KalmanErrorCacheKey key=new KalmanErrorCacheKey(indices); + + putErrorValue(key, value); + } + + @Override + public void putErrorValue(KalmanErrorCacheKey key, Double value) { + + memcachedClient.set(createKey(key), expiryDuration, value); + + } + + + public List getKeys() { + + logger.info("Not implemented for memecached."); + return null; + } + + private String createKey(KalmanErrorCacheKey key) { + return keystub + key.getTripId() + "_" + key.getStopPathIndex(); + + } + +} diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/memcached/scheduled/StopArrivalDepartureCache.java b/transitclock/src/main/java/org/transitclock/core/dataCache/memcached/scheduled/StopArrivalDepartureCache.java new file mode 100644 index 000000000..be27f2874 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/dataCache/memcached/scheduled/StopArrivalDepartureCache.java @@ -0,0 +1,113 @@ +package org.transitclock.core.dataCache.memcached.scheduled; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collections; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.config.StringConfigValue; + +import org.transitclock.core.dataCache.IpcArrivalDepartureComparator; +import org.transitclock.core.dataCache.StopArrivalDepartureCacheInterface; +import org.transitclock.core.dataCache.StopArrivalDepartureCacheKey; +import org.transitclock.db.structs.ArrivalDeparture; +import org.transitclock.ipc.data.IpcArrivalDeparture; +import org.transitclock.utils.Time; + +import net.spy.memcached.MemcachedClient; + +public class StopArrivalDepartureCache extends StopArrivalDepartureCacheInterface { + + private static StringConfigValue memcachedHost = new StringConfigValue("transitclock.cache.memcached.host", "127.0.0.1", + "Specifies the host machine that memcache is running on."); + + private static IntegerConfigValue memcachedPort = new IntegerConfigValue("transitclock.cache.memcached.port", 11211, + "Specifies the port that memcache is running on."); + + MemcachedClient memcachedClient = null; + Integer expiryDuration=Time.SEC_PER_DAY; + private static String keystub = "STOPAD_"; + + private static final Logger logger = LoggerFactory.getLogger(StopArrivalDepartureCache.class); + + @Override + public void populateCacheFromDb(List results) { + // make it obvious we are using the base implementation + defaultPopulateCacheFromDb(results); + } + + @SuppressWarnings("unchecked") + @Override + public List getStopHistory(StopArrivalDepartureCacheKey key) { + + Calendar date = Calendar.getInstance(); + date.setTime(key.getDate()); + + date.set(Calendar.HOUR_OF_DAY, 0); + date.set(Calendar.MINUTE, 0); + date.set(Calendar.SECOND, 0); + date.set(Calendar.MILLISECOND, 0); + key.setDate(date.getTime()); + List result = (List) memcachedClient.get(createKey(key)); + + return result; + } + + @Override + public StopArrivalDepartureCacheKey putArrivalDeparture(ArrivalDeparture arrivalDeparture) { + + Calendar date = Calendar.getInstance(); + date.setTime(arrivalDeparture.getDate()); + + date.set(Calendar.HOUR_OF_DAY, 0); + date.set(Calendar.MINUTE, 0); + date.set(Calendar.SECOND, 0); + date.set(Calendar.MILLISECOND, 0); + + StopArrivalDepartureCacheKey key = new StopArrivalDepartureCacheKey(arrivalDeparture.getStop().getId(), + date.getTime()); + + List list = getStopHistory(key); + + if (list == null) + list = new ArrayList(); + + try { + list.add(new IpcArrivalDeparture(arrivalDeparture)); + Collections.sort(list, new IpcArrivalDepartureComparator()); + memcachedClient.set(createKey(key), expiryDuration, list); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return key; + } + + public StopArrivalDepartureCache() throws IOException { + super(); + memcachedClient = new MemcachedClient( + new InetSocketAddress(memcachedHost.getValue(), memcachedPort.getValue().intValue())); + } + + private String createKey(StopArrivalDepartureCacheKey key) { + + Calendar date = Calendar.getInstance(); + date.setTime(key.getDate()); + + date.set(Calendar.HOUR_OF_DAY, 0); + date.set(Calendar.MINUTE, 0); + date.set(Calendar.SECOND, 0); + date.set(Calendar.MILLISECOND, 0); + key.setDate(date.getTime()); + + SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd"); + return keystub + key.getStopid() + "_" + formatter.format(key.getDate()); + } + +} diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/memcached/scheduled/TripDataHistoryCache.java b/transitclock/src/main/java/org/transitclock/core/dataCache/memcached/scheduled/TripDataHistoryCache.java new file mode 100644 index 000000000..c031c8186 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/dataCache/memcached/scheduled/TripDataHistoryCache.java @@ -0,0 +1,155 @@ +package org.transitclock.core.dataCache.memcached.scheduled; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +import org.apache.commons.lang3.time.DateUtils; +import org.hibernate.Criteria; +import org.hibernate.Session; +import org.slf4j.Logger; +import org.transitclock.applications.Core; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.config.StringConfigValue; +import org.transitclock.core.dataCache.IpcArrivalDepartureComparator; +import org.transitclock.core.dataCache.TripDataHistoryCacheFactory; +import org.transitclock.core.dataCache.TripDataHistoryCacheInterface; +import org.transitclock.core.dataCache.TripKey; +import org.transitclock.db.structs.ArrivalDeparture; +import org.transitclock.db.structs.Trip; +import org.transitclock.gtfs.DbConfig; +import org.transitclock.gtfs.GtfsData; +import org.transitclock.ipc.data.IpcArrivalDeparture; +import org.transitclock.utils.Time; + +import net.spy.memcached.MemcachedClient; + +public class TripDataHistoryCache implements TripDataHistoryCacheInterface { + + private static StringConfigValue memcachedHost = new StringConfigValue("transitclock.cache.memcached.host", "127.0.0.1", + "Specifies the host machine that memcache is running on."); + + private static IntegerConfigValue memcachedPort = new IntegerConfigValue("transitclock.cache.memcached.port", 11211, + "Specifies the port that memcache is running on."); + + MemcachedClient memcachedClient = null; + + private static String keystub = "TRIPHISTORY_"; + Integer expiryDuration=Time.SEC_PER_DAY*28; + public TripDataHistoryCache() throws IOException { + + memcachedClient = new MemcachedClient( + new InetSocketAddress(memcachedHost.getValue(), memcachedPort.getValue().intValue())); + } + + + public List getKeys() { + // TODO Auto-generated method stub + return null; + } + + + public void logCache(Logger logger) { + // TODO Auto-generated method stub + + } + + @SuppressWarnings("unchecked") + @Override + public List getTripHistory(TripKey tripKey) { + + Object value = memcachedClient.get(createKey(tripKey)); + if (value instanceof List) + return (List) value; + return null; + + } + + @Override + public TripKey putArrivalDeparture(ArrivalDeparture arrivalDeparture) { + + Date nearestDay = DateUtils.truncate(new Date(arrivalDeparture.getTime()), Calendar.DAY_OF_MONTH); + + DbConfig dbConfig = Core.getInstance().getDbConfig(); + + Trip trip = dbConfig.getTrip(arrivalDeparture.getTripId()); + if (trip != null) { + TripKey tripKey = new TripKey(arrivalDeparture.getTripId(), nearestDay, trip.getStartTime()); + + List list = this.getTripHistory(tripKey); + + if (list == null) + list = new ArrayList(); + + try { + list.add(new IpcArrivalDeparture(arrivalDeparture)); + } catch (Exception e) { + + e.printStackTrace(); + } + memcachedClient.set(createKey(tripKey), expiryDuration, list); + } + + return null; + } + + @Override + public void populateCacheFromDb(List results) { + + for(ArrivalDeparture result : results) + { + // TODO this might be better done in the database. + if(GtfsData.routeNotFiltered(result.getRouteId())) + { + TripDataHistoryCacheFactory.getInstance().putArrivalDeparture(result); + } + } + + } + + @Override + public IpcArrivalDeparture findPreviousArrivalEvent(List arrivalDepartures, + IpcArrivalDeparture current) { + Collections.sort(arrivalDepartures, new IpcArrivalDepartureComparator()); + for (IpcArrivalDeparture tocheck : emptyIfNull(arrivalDepartures)) + { + if(tocheck.getStopId().equals(current.getStopId()) && (current.isDeparture() && tocheck.isArrival())) + { + return tocheck; + } + } + return null; + } + + @Override + public IpcArrivalDeparture findPreviousDepartureEvent(List arrivalDepartures, + IpcArrivalDeparture current) { + Collections.sort(arrivalDepartures, new IpcArrivalDepartureComparator()); + for (IpcArrivalDeparture tocheck : emptyIfNull(arrivalDepartures)) + { + try { + if(tocheck.getStopPathIndex()==(current.getStopPathIndex()-1) && (current.isArrival() && tocheck.isDeparture())) + { + return tocheck; + } + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + return null; + } + + private String createKey(TripKey tripKey) { + SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd"); + return keystub + tripKey.getTripId() + "_" + formatter.format(tripKey.getTripStartDate()); + } + private static Iterable emptyIfNull(Iterable iterable) { + return iterable == null ? Collections. emptyList() : iterable; + } +} diff --git a/transitime/src/main/java/org/transitime/core/dataCache/package-info.java b/transitclock/src/main/java/org/transitclock/core/dataCache/package-info.java similarity index 95% rename from transitime/src/main/java/org/transitime/core/dataCache/package-info.java rename to transitclock/src/main/java/org/transitclock/core/dataCache/package-info.java index 07f51c55d..11794b4c8 100644 --- a/transitime/src/main/java/org/transitime/core/dataCache/package-info.java +++ b/transitclock/src/main/java/org/transitclock/core/dataCache/package-info.java @@ -22,4 +22,4 @@ * @author SkiBu Smith * */ -package org.transitime.core.dataCache; \ No newline at end of file +package org.transitclock.core.dataCache; \ No newline at end of file diff --git a/transitclock/src/main/java/org/transitclock/core/dataCache/scheduled/ScheduleBasedHistoricalAverageCache.java b/transitclock/src/main/java/org/transitclock/core/dataCache/scheduled/ScheduleBasedHistoricalAverageCache.java new file mode 100644 index 000000000..641c9e0b7 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/dataCache/scheduled/ScheduleBasedHistoricalAverageCache.java @@ -0,0 +1,200 @@ +package org.transitclock.core.dataCache.scheduled; + +import org.apache.commons.lang3.time.DateUtils; +import org.ehcache.Cache; +import org.ehcache.CacheManager; +import org.hibernate.Criteria; +import org.hibernate.Session; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.applications.Core; +import org.transitclock.core.DwellTimeDetails; +import org.transitclock.core.TravelTimeDetails; +import org.transitclock.core.dataCache.*; +import org.transitclock.core.dataCache.ehcache.CacheManagerFactory; +import org.transitclock.db.structs.ArrivalDeparture; +import org.transitclock.db.structs.Trip; +import org.transitclock.gtfs.DbConfig; +import org.transitclock.ipc.data.IpcArrivalDeparture; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.List; +/** + * @author Sean Óg Crudden + * + */ +public class ScheduleBasedHistoricalAverageCache { + final private static String cacheName = "HistoricalAverageCache"; + private static ScheduleBasedHistoricalAverageCache singleton = new ScheduleBasedHistoricalAverageCache(); + private static final Logger logger = LoggerFactory + .getLogger(ScheduleBasedHistoricalAverageCache.class); + private Cache cache = null; + /** + * Gets the singleton instance of this class. + * + * @return + */ + public static ScheduleBasedHistoricalAverageCache getInstance() { + return singleton; + } + + private ScheduleBasedHistoricalAverageCache() { + CacheManager cm = CacheManagerFactory.getInstance(); + + cache = cm.getCache(cacheName, StopPathCacheKey.class, HistoricalAverage.class); + } + + public void logCache(Logger logger) + { + logger.debug("Cache content log. Not implemented."); + + } + public void logCacheSize(Logger logger) + { + logger.debug("Log cache size. Not implemented."); + } + + public HistoricalAverage getAverage(StopPathCacheKey key) { + + return cache.get(key); + } + public void putAverage(StopPathCacheKey key, HistoricalAverage average) { + + logger.debug("Putting: {} in cache with values : {}", key.toString(), average); + + cache.put(key, average); + // logCache(logger); + } + public void putArrivalDeparture(ArrivalDeparture arrivalDeparture) throws Exception + { + DbConfig dbConfig = Core.getInstance().getDbConfig(); + + Trip trip=dbConfig.getTrip(arrivalDeparture.getTripId()); + + if(trip!=null && !trip.isNoSchedule()) + { + logger.debug("Putting :"+arrivalDeparture.toString() + " in HistoricalAverageCache cache."); + + TravelTimeDetails travelTimeDetails=getLastTravelTimeDetails(new IpcArrivalDeparture(arrivalDeparture), trip); + + if(travelTimeDetails!=null&&travelTimeDetails.sanityCheck()) + { + if(!trip.isNoSchedule()) + { + StopPathCacheKey historicalAverageCacheKey=new StopPathCacheKey(trip.getId(), arrivalDeparture.getStopPathIndex(), true); + + HistoricalAverage empty = new HistoricalAverage(); + empty.update(travelTimeDetails.getTravelTime()); + synchronized (cache) { + HistoricalAverage average = getAverage(historicalAverageCacheKey); + + if (average == null) { + putAverage(historicalAverageCacheKey, empty); + } else{ + average.update(travelTimeDetails.getTravelTime()); + putAverage(historicalAverageCacheKey, average); + } + } + } + } + + DwellTimeDetails dwellTimeDetails=getLastDwellTimeDetails(new IpcArrivalDeparture(arrivalDeparture), trip); + if(dwellTimeDetails!=null&&dwellTimeDetails.sanityCheck()) + { + StopPathCacheKey historicalAverageCacheKey=new StopPathCacheKey(trip.getId(), arrivalDeparture.getStopPathIndex(), false); + + HistoricalAverage empty = new HistoricalAverage(); + empty.update(dwellTimeDetails.getDwellTime()); + synchronized (cache) { + HistoricalAverage average = getAverage(historicalAverageCacheKey); + + if (average == null) { + putAverage(historicalAverageCacheKey, empty); + } else { + logger.trace("Updating historical averege for : {} with {}", historicalAverageCacheKey, dwellTimeDetails); + average.update(dwellTimeDetails.getDwellTime()); + putAverage(historicalAverageCacheKey, average); + } + } + } + } + } + private TravelTimeDetails getLastTravelTimeDetails(IpcArrivalDeparture arrivalDeparture, Trip trip) + { + Date nearestDay = DateUtils.truncate(new Date(arrivalDeparture.getTime().getTime()), Calendar.DAY_OF_MONTH); + TripKey tripKey = new TripKey(arrivalDeparture.getTripId(), + nearestDay, + trip.getStartTime()); + + + List arrivalDepartures = new ArrayList<>(emptyIfNull(TripDataHistoryCacheFactory.getInstance().getTripHistory(tripKey))); + + if(arrivalDepartures!=null && arrivalDepartures.size()>0 && arrivalDeparture.isArrival()) + { + IpcArrivalDeparture previousEvent = TripDataHistoryCacheFactory.getInstance().findPreviousDepartureEvent(arrivalDepartures, arrivalDeparture); + + if(previousEvent!=null && arrivalDeparture!=null && previousEvent.isDeparture()) + { + return new TravelTimeDetails(previousEvent, arrivalDeparture); + } + } + + return null; + } + + private DwellTimeDetails getLastDwellTimeDetails(IpcArrivalDeparture arrivalDeparture, Trip trip) + { + Date nearestDay = DateUtils.truncate(new Date(arrivalDeparture.getTime().getTime()), Calendar.DAY_OF_MONTH); + TripKey tripKey = new TripKey(arrivalDeparture.getTripId(), + nearestDay, + trip.getStartTime()); + + // copy this list in case it changes underneath us + List arrivalDepartures = new ArrayList<>(emptyIfNull(TripDataHistoryCacheFactory.getInstance().getTripHistory(tripKey))); + + if(arrivalDepartures.size() > 0 && arrivalDeparture.isDeparture()) + { + IpcArrivalDeparture previousEvent = TripDataHistoryCacheFactory.getInstance().findPreviousArrivalEvent(arrivalDepartures, arrivalDeparture); + + if(previousEvent!=null && arrivalDeparture!=null && previousEvent.isArrival()) + { + return new DwellTimeDetails(previousEvent, arrivalDeparture); + + } + } + return null; + } + + private List emptyIfNull(List tripHistory) { + if (tripHistory == null) return new ArrayList<>(); + return tripHistory; + } + + public void populateCacheFromDb(List resultsUnsafe) throws Exception + { + try { + if (resultsUnsafe == null) return; + List results = new ArrayList<>(resultsUnsafe); + Collections.sort(results, new ArrivalDepartureComparator()); + + int counter = 0; + for (ArrivalDeparture result : results) { + if (counter % 1000 == 0) { + logger.info("{} out of {} ScheduleBased Historical Records ({}%)", counter, results.size(), (int) ((counter * 100.0f) / results.size())); + } + putArrivalDeparture(result); + counter++; + } + } catch (Throwable t) { + logger.error("Exception in populateCacheFromDb {}", t, t); + } + } + + public List getKeys() { + // TODO Auto-generated method stub + return null; + } +} diff --git a/transitclock/src/main/java/org/transitclock/core/headwaygenerator/AbstractHeadwayGenerator.java b/transitclock/src/main/java/org/transitclock/core/headwaygenerator/AbstractHeadwayGenerator.java new file mode 100644 index 000000000..cc531f327 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/headwaygenerator/AbstractHeadwayGenerator.java @@ -0,0 +1,141 @@ +package org.transitclock.core.headwaygenerator; + +import org.transitclock.configData.HeadwayConfig; +import org.transitclock.core.HeadwayGenerator; +import org.transitclock.core.VehicleState; +import org.transitclock.core.dataCache.StopArrivalDepartureCacheFactory; +import org.transitclock.core.dataCache.StopArrivalDepartureCacheKey; +import org.transitclock.core.dataCache.VehicleDataCache; +import org.transitclock.core.dataCache.VehicleStateManager; +import org.transitclock.db.structs.Headway; +import org.transitclock.ipc.data.IpcArrivalDeparture; +import org.transitclock.ipc.data.IpcVehicleComplete; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +public abstract class AbstractHeadwayGenerator implements HeadwayGenerator { + @Override + public abstract Headway generate(VehicleState vehicleState); + + public List getRecentArrivalDeparturesForStop(String stopId, long vehicleMatchAvlTime){ + StopArrivalDepartureCacheKey key=new StopArrivalDepartureCacheKey(stopId, new Date(vehicleMatchAvlTime)); + + return StopArrivalDepartureCacheFactory.getInstance().getStopHistory(key); + } + + public int[] getLastStopAndPrevVehicleArrivalDepartureIndex(VehicleState vehicleState, + String vehicleId, + String stopId, + List arrivalDeparturesForStop, + boolean useArrival){ + int lastStopArrivalIndex = -1; + int previousVehicleArrivalIndex = -1; + + for(int i=0;i-1 && + correctArrivalOrDeparture && + arrivalDepature.getStopId().equals(stopId) && + !arrivalDepature.getVehicleId().equals(vehicleId) && + (!HeadwayConfig.matchByTripPattern() || + arrivalDepature.getTripPatternId().equals(vehicleState.getTrip().getTripPattern().getId())) && + (vehicleState.getTrip().getDirectionId()==null || + vehicleState.getTrip().getDirectionId().equals(arrivalDepature.getDirectionId()))) + { + previousVehicleArrivalIndex = i; + } + } + return new int[]{lastStopArrivalIndex, previousVehicleArrivalIndex}; + } + + public static long calculateHeadway(IpcArrivalDeparture prevStopArrival, IpcArrivalDeparture prevStopArrivalForPrevVehicle){ + return Math.abs(prevStopArrival.getTime().getTime() - prevStopArrivalForPrevVehicle.getTime().getTime()); + } + + public static Long calculateScheduledHeadway(IpcArrivalDeparture prevStopArrival, + IpcArrivalDeparture prevStopArrivalForPrevVehicle) { + if(prevStopArrival !=null && prevStopArrival.getScheduledDate() != null && + prevStopArrivalForPrevVehicle != null && prevStopArrivalForPrevVehicle.getScheduledDate() != null){ + return Math.abs(prevStopArrival.getScheduledDate().getTime() - prevStopArrivalForPrevVehicle.getScheduledDate().getTime()); + } + return null; + } + + public void setSystemVariance(Headway headway) + { + ArrayList headways=new ArrayList(); + + int total_with_headway=0; + int total_vehicles=0; + boolean error=false; + for(IpcVehicleComplete currentVehicle: VehicleDataCache.getInstance().getVehicles()) + { + VehicleState vehicleState = VehicleStateManager.getInstance().getVehicleState(currentVehicle.getId()); + if(vehicleState.getHeadway()!=null) + { + headways.add(vehicleState.getHeadway()); + total_with_headway++; + } + total_vehicles++; + } + // ONLY SET IF HAVE VALES FOR ALL VEHICLES ON ROUTE. + if(VehicleDataCache.getInstance().getVehicles().size()==headways.size()&&total_vehicles==total_with_headway) + { + headway.setAverage(average(headways)); + headway.setVariance(variance(headways)); + headway.setCoefficientOfVariation(coefficientOfVariance(headways)); + headway.setNumVehicles(headways.size()); + }else + { + headway.setAverage(-1); + headway.setVariance(-1); + headway.setCoefficientOfVariation(-1); + headway.setNumVehicles(total_with_headway); + } + } + + private double average(List headways) + { + double total=0; + for(Headway headway:headways) + { + total=total+headway.getHeadway(); + } + return total/headways.size(); + } + + private double variance(List headways) + { + double topline=0; + double average = average(headways); + for(Headway headway:headways) + { + topline=topline+((headway.getHeadway()-average)*(headway.getHeadway()-average)); + } + return topline/headways.size(); + } + + private double coefficientOfVariance(List headways) + { + double variance = variance(headways);; + double average = average(headways); + + return variance/(average*average); + } + +} diff --git a/transitclock/src/main/java/org/transitclock/core/headwaygenerator/LastArrivalsHeadwayGenerator.java b/transitclock/src/main/java/org/transitclock/core/headwaygenerator/LastArrivalsHeadwayGenerator.java new file mode 100755 index 000000000..b84b2d65b --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/headwaygenerator/LastArrivalsHeadwayGenerator.java @@ -0,0 +1,97 @@ +package org.transitclock.core.headwaygenerator; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.applications.Core; +import org.transitclock.configData.HeadwayConfig; +import org.transitclock.core.HeadwayGenerator; +import org.transitclock.core.VehicleState; +import org.transitclock.core.dataCache.StopArrivalDepartureCacheFactory; +import org.transitclock.core.dataCache.StopArrivalDepartureCacheKey; +import org.transitclock.core.dataCache.VehicleDataCache; +import org.transitclock.core.dataCache.VehicleStateManager; +import org.transitclock.core.dataCache.ehcache.StopArrivalDepartureCache; +import org.transitclock.db.structs.ArrivalDeparture; +import org.transitclock.db.structs.Headway; +import org.transitclock.ipc.data.IpcArrivalDeparture; +import org.transitclock.ipc.data.IpcVehicleComplete; + +import static org.transitclock.configData.HeadwayConfig.calculateSystemVariance; + +/** + * + * @author Sean Óg Crudden + * + * This is a first pass at generating a Headway value. It will find the last arrival time at the last stop for the vehicle and then get the vehicle ahead of it and check when it arrived at the same stop. The difference will be used as the headway. + * + * This is a WIP + * + * Maybe should be a list and have a predicted headway at each stop along the route. So key for headway could be (stop, vehicle, trip, start_time). + */ +public class LastArrivalsHeadwayGenerator extends AbstractHeadwayGenerator { + + private static final Logger logger = + LoggerFactory.getLogger(LastArrivalsHeadwayGenerator.class); + + @Override + public Headway generate(VehicleState vehicleState) { + + try { + + if(vehicleState.getMatch().getMatchAtPreviousStop()==null) + return null; + + String stopId = vehicleState.getMatch().getMatchAtPreviousStop().getAtStop().getStopId(); + long vehicleMatchAvlTime = vehicleState.getMatch().getAvlTime(); + String vehicleId=vehicleState.getVehicleId(); + + List arrivalDeparturesForStop = getRecentArrivalDeparturesForStop(stopId, vehicleMatchAvlTime); + + int lastStopArrivalIndex; + int previousVehicleArrivalIndex; + + if(arrivalDeparturesForStop!=null) + { + boolean useArrivals = true; + int[] arrivalIndexes = getLastStopAndPrevVehicleArrivalDepartureIndex(vehicleState, vehicleId, stopId, + arrivalDeparturesForStop, useArrivals); + lastStopArrivalIndex = arrivalIndexes[0]; + previousVehicleArrivalIndex = arrivalIndexes [1]; + + if(previousVehicleArrivalIndex!=-1 && lastStopArrivalIndex!=-1) + { + IpcArrivalDeparture lastStopDeparture = arrivalDeparturesForStop.get(lastStopArrivalIndex); + IpcArrivalDeparture lastStopPreviousVehicleDeparture = arrivalDeparturesForStop.get(previousVehicleArrivalIndex); + + long headwayTime= calculateHeadway(lastStopDeparture, lastStopPreviousVehicleDeparture); + Long scheduledHeadwayTime = calculateScheduledHeadway(lastStopDeparture, lastStopPreviousVehicleDeparture); + + Headway headway=new Headway(headwayTime, + scheduledHeadwayTime, + new Date(vehicleMatchAvlTime), + vehicleId, + arrivalDeparturesForStop.get(previousVehicleArrivalIndex).getVehicleId(), + stopId, + vehicleState.getTrip().getId(), + vehicleState.getTrip().getRouteId(), + new Date(arrivalDeparturesForStop.get(lastStopArrivalIndex).getTime().getTime()), + new Date(arrivalDeparturesForStop.get(previousVehicleArrivalIndex).getTime().getTime()), + new Date(vehicleState.getAvlReport().getTime())); + + if(calculateSystemVariance()){ + setSystemVariance(headway); + } + + return headway; + } + } + } catch (Exception e) { + logger.error("Exception when processing headway", e); + } + return null; + } +} diff --git a/transitclock/src/main/java/org/transitclock/core/headwaygenerator/LastDepartureHeadwayGenerator.java b/transitclock/src/main/java/org/transitclock/core/headwaygenerator/LastDepartureHeadwayGenerator.java new file mode 100644 index 000000000..caf2c38c7 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/headwaygenerator/LastDepartureHeadwayGenerator.java @@ -0,0 +1,113 @@ +package org.transitclock.core.headwaygenerator; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.applications.Core; +import org.transitclock.config.StringConfigValue; +import org.transitclock.configData.HeadwayConfig; +import org.transitclock.core.HeadwayGenerator; +import org.transitclock.core.MatchProcessor; +import org.transitclock.core.VehicleState; +import org.transitclock.core.dataCache.PredictionDataCache; +import org.transitclock.core.dataCache.StopArrivalDepartureCacheFactory; +import org.transitclock.core.dataCache.StopArrivalDepartureCacheKey; +import org.transitclock.core.dataCache.VehicleDataCache; +import org.transitclock.core.dataCache.VehicleStateManager; +import org.transitclock.core.dataCache.ehcache.StopArrivalDepartureCache; +import org.transitclock.db.structs.ArrivalDeparture; +import org.transitclock.db.structs.Headway; +import org.transitclock.ipc.data.IpcArrivalDeparture; +import org.transitclock.ipc.data.IpcVehicleComplete; + +import static org.transitclock.configData.HeadwayConfig.calculateSystemVariance; + +/** + * + * @author Sean Óg Crudden + * + * This is a first pass at generating a Headway value. It will find the last departure time at the last stop for the vehicle and then get the vehicle ahead of it and check when it departed the same stop. The difference will be used as the headway. + * + * This is a WIP + * + * Maybe should be a list and have a predicted headway at each stop along the route. So key for headway could be (stop, vehicle, trip, start_time). + */ +public class LastDepartureHeadwayGenerator extends AbstractHeadwayGenerator { + + private static final Logger logger = + LoggerFactory.getLogger(LastDepartureHeadwayGenerator.class); + + @Override + public Headway generate(VehicleState vehicleState) { + + try { + + if(vehicleState.getMatch().getMatchAtPreviousStop()==null) + return null; + + String stopId = vehicleState.getMatch().getMatchAtPreviousStop().getAtStop().getStopId(); + long vehicleMatchAvlTime = vehicleState.getMatch().getAvlTime(); + String vehicleId=vehicleState.getVehicleId(); + + List arrivalDeparturesForStop = getRecentArrivalDeparturesForStop(stopId, vehicleMatchAvlTime); + + int lastStopArrivalIndex; + int previousVehicleArrivalIndex; + + if(arrivalDeparturesForStop!=null) + { + boolean useArrivals = false; + int[] arrivalIndexes = getLastStopAndPrevVehicleArrivalDepartureIndex(vehicleState, vehicleId, stopId, + arrivalDeparturesForStop, useArrivals); + lastStopArrivalIndex = arrivalIndexes[0]; + previousVehicleArrivalIndex = arrivalIndexes [1]; + + if(previousVehicleArrivalIndex!=-1 && lastStopArrivalIndex!=-1) { + + IpcArrivalDeparture lastStopDeparture = arrivalDeparturesForStop.get(lastStopArrivalIndex); + IpcArrivalDeparture lastStopPreviousVehicleDeparture = arrivalDeparturesForStop.get(previousVehicleArrivalIndex); + + long headwayTime= calculateHeadway(lastStopDeparture, lastStopPreviousVehicleDeparture); + Long scheduledHeadwayTime = calculateScheduledHeadway(lastStopDeparture, lastStopPreviousVehicleDeparture); + + Headway headway=new Headway(headwayTime, + scheduledHeadwayTime, + new Date(vehicleMatchAvlTime), + vehicleId, + arrivalDeparturesForStop.get(previousVehicleArrivalIndex).getVehicleId(), + stopId, + vehicleState.getTrip().getId(), + vehicleState.getTrip().getRouteId(), + new Date(arrivalDeparturesForStop.get(lastStopArrivalIndex).getTime().getTime()), + new Date(arrivalDeparturesForStop.get(previousVehicleArrivalIndex).getTime().getTime()), + new Date(vehicleState.getAvlReport().getTimeProcessed())); + + if(isInvalidHeadway(headway, lastStopArrivalIndex)) + { + return null; + } + + if(calculateSystemVariance()){ + setSystemVariance(headway); + } + + return headway; + } + } + } catch (Exception e) { + logger.error("Exception when processing headway", e); + } + return null; + } + + private boolean isInvalidHeadway(Headway headway, int lastStopArrivalIndex){ + if(Math.abs(headway.getCreationTime().getTime()-headway.getFirstDeparture().getTime())>1200000||lastStopArrivalIndex>5) + { + return true; + } + return false; + } +} diff --git a/transitclock/src/main/java/org/transitclock/core/holdingmethod/ControlStop.java b/transitclock/src/main/java/org/transitclock/core/holdingmethod/ControlStop.java new file mode 100755 index 000000000..d63c2fb9e --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/holdingmethod/ControlStop.java @@ -0,0 +1,64 @@ +package org.transitclock.core.holdingmethod; + +public class ControlStop { + String stopPathIndex; + String stopId; + public ControlStop(String combined) { + if(combined.contains(":")) + { + String splits[]=combined.split(":"); + stopPathIndex=splits[1]; + stopId=splits[0]; + }else + { + stopId=combined; + stopPathIndex=null; + } + } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((stopId == null) ? 0 : stopId.hashCode()); + result = prime * result + ((stopPathIndex == null) ? 0 : stopPathIndex.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; + ControlStop other = (ControlStop) obj; + if (stopId == null) { + if (other.stopId != null) + return false; + } else if (!stopId.equals(other.stopId)) + return false; + if (stopPathIndex == null) { + if (other.stopPathIndex != null) + return false; + } else if (!stopPathIndex.equals(other.stopPathIndex)) + return false; + return true; + } + public ControlStop(String stopPathIndex, String stopId) { + super(); + this.stopPathIndex = stopPathIndex; + this.stopId = stopId; + } + public String getStopPathIndex() { + return stopPathIndex; + } + public void setStopPathIndex(String stopPathIndex) { + this.stopPathIndex = stopPathIndex; + } + public String getStopId() { + return stopId; + } + public void setStopId(String stopId) { + this.stopId = stopId; + } +} diff --git a/transitclock/src/main/java/org/transitclock/core/holdingmethod/HoldingTimeGenerator.java b/transitclock/src/main/java/org/transitclock/core/holdingmethod/HoldingTimeGenerator.java new file mode 100755 index 000000000..cb79a9e04 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/holdingmethod/HoldingTimeGenerator.java @@ -0,0 +1,21 @@ +package org.transitclock.core.holdingmethod; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.transitclock.core.VehicleState; +import org.transitclock.db.structs.ArrivalDeparture; +import org.transitclock.db.structs.HoldingTime; +import org.transitclock.ipc.data.IpcArrivalDeparture; +import org.transitclock.ipc.data.IpcPrediction; +/** + * @author Sean Óg Crudden + */ +public interface HoldingTimeGenerator { + public List getControlPointStops(); + public HoldingTime generateHoldingTime(VehicleState vehicleState, IpcArrivalDeparture event); + public HoldingTime generateHoldingTime(VehicleState vehicleState, IpcPrediction arrivalPrediction); + + public void handleDeparture(VehicleState vehicleState, ArrivalDeparture arrivalDeparture); +} diff --git a/transitclock/src/main/java/org/transitclock/core/holdingmethod/HoldingTimeGeneratorDefaultImpl.java b/transitclock/src/main/java/org/transitclock/core/holdingmethod/HoldingTimeGeneratorDefaultImpl.java new file mode 100755 index 000000000..97f401324 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/holdingmethod/HoldingTimeGeneratorDefaultImpl.java @@ -0,0 +1,793 @@ +package org.transitclock.core.holdingmethod; + +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.StringListConfigValue; +import org.transitclock.core.VehicleState; +import org.transitclock.core.dataCache.ArrivalDepartureComparator; +import org.transitclock.core.dataCache.HoldingTimeCache; +import org.transitclock.core.dataCache.IpcArrivalDepartureComparator; +import org.transitclock.core.dataCache.PredictionDataCache; +import org.transitclock.core.dataCache.StopArrivalDepartureCacheFactory; +import org.transitclock.core.dataCache.StopArrivalDepartureCacheKey; +import org.transitclock.core.dataCache.VehicleDataCache; +import org.transitclock.core.dataCache.VehicleStateManager; +import org.transitclock.core.dataCache.ehcache.StopArrivalDepartureCache; +import org.transitclock.db.structs.ArrivalDeparture; +import org.transitclock.db.structs.HoldingTime; +import org.transitclock.ipc.data.IpcArrivalDeparture; +import org.transitclock.ipc.data.IpcPrediction; +import org.transitclock.ipc.data.IpcPredictionsForRouteStopDest; +import org.transitclock.ipc.data.IpcVehicleComplete; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +/** + * @author Sean Óg Crudden + * This is a default implementation of the holding time generator and is an implementation of the approach proposed by Simon Berrebi et al. + * + * http://www.worldtransitresearch.info/research/5644/ + * + * The theory is to use the time generated as the holding time for the vehicle at the control point. This is in an attempt to manage the headway between vehicles along the route. + * + * This is a WIP. + */ +public class HoldingTimeGeneratorDefaultImpl implements HoldingTimeGenerator { + private static final Logger logger = + LoggerFactory.getLogger(HoldingTimeGeneratorDefaultImpl.class); + + protected static BooleanConfigValue storeHoldingTimes = new BooleanConfigValue("transitclock.holding.storeHoldingTimes", + true, + "This is set to true to record all holding times."); + + protected static IntegerConfigValue maxPredictionsForHoldingTimeCalculation = new IntegerConfigValue("transitclock.holding.maxPredictionsForHoldingTimeCalculation", + 3, + "This is the maximim number of arrival predictions to include in holding time calculation"); + + protected static BooleanConfigValue useArrivalEvents = new BooleanConfigValue("transitclock.holding.usearrivalevents", + true, + "Generate a holding time on arrival events."); + + protected static BooleanConfigValue useArrivalPredictions = new BooleanConfigValue("transitclock.holding.usearrivalpredictions", + true, + "Generate a holding time on arrival predictions."); + + protected static BooleanConfigValue regenerateOnDeparture = new BooleanConfigValue("transitclock.holding.regenerateondeparture", + false, + "Regenerate a holding time for all vehicles at control point when a vehicle departs the control point."); + + protected static IntegerConfigValue plannedHeadwayMsec = new IntegerConfigValue("transitclock.holding.plannedHeadwayMsec", 60*1000*9, "Planned Headway"); + protected static StringListConfigValue controlStopList = new StringListConfigValue("transitclock.holding.controlStops", null, "This is a list of stops to generate holding times for."); + + public HoldingTime generateHoldingTime(VehicleState vehicleState, IpcArrivalDeparture event) { + + PredictionDataCache predictionCache = PredictionDataCache.getInstance(); + + if(!useArrivalEvents.getValue()) + { + return null; + } + + if(event.isArrival() && isControlStop(event.getStopId())) + { + + List predictions = new ArrayList(); + + for(IpcVehicleComplete currentVehicle:VehicleDataCache.getInstance().getVehicles()) + { + if(predictionCache.getPredictionForVehicle(currentVehicle.getId(), event.getRouteId(), event.getStopId())!=null) + { + predictions.add(predictionCache.getPredictionForVehicle(currentVehicle.getId(), event.getRouteId(), event.getStopId())); + } + } + Collections.sort(predictions, new PredictionTimeComparator()); + + logger.debug("Calling Holding Generator for event : {} using predictions : {}", event.toString(),predictions.toString()); + + //This is to remove a prediction for the current vehicle and stop. Belt and braces. + if(predictions.size()>0 && predictions.get(0).getVehicleId().equals(event.getVehicleId())) + { + predictions.remove(0); + } + + logger.debug("Have {} predictions for stops {}.", predictions.size(), event.getStopId()); + + IpcArrivalDeparture lastVehicleDeparture = getLastVehicleDepartureTime(event.getVehicleId(), event.getTripId(), event.getStopId(), new Date(event.getTime().getTime())); + + /* Now get check if there is current holdingTie for the stop and get the next vehicle scheduled to leave and use its holding time as the departure time for calculation. */ + HoldingTime lastVehicleDepartureByHoldingTime=getNextDepartureByHoldingTime(event.getVehicleId(), getCurrentHoldingTimesForStop(event.getStopId())); + + // IF ARRIVAL OF HOLDING VEHICLE IS AFTER CURRENT DO NOT CONSIDER AS DEPARTURE + /*if(lastVehicleDepartureByHoldingTime.getArrivalTime().getTime()1) + { + Long N[]=null; + + int counter=0; + + N=predictionsToLongArray(predictions); + + for(int i=0;i1) + { + Long N[]=null; + + int counter=0; + + N=predictionsToLongArray(predictions); + + for(int i=0;i getOrderedListOfVehicles(String routeId) + { + int count=0; + boolean canorder=true; + List unordered=new ArrayList(); + List ordered=null; + for(VehicleState currentVehicleState:VehicleStateManager.getInstance().getVehiclesState()) + { + if(currentVehicleState.getTrip()!=null&¤tVehicleState.getTrip().getRoute().getId().equals(routeId)&¤tVehicleState.isPredictable()) + { + count++; + unordered.add(currentVehicleState); + if(currentVehicleState.getHeadway()==null) + { + canorder=false; + + } + } + } + if(canorder) + { + ordered=new ArrayList(); + + while(((count+1) > 0)&&unordered.size() > 0) + { + if(ordered.size()==0) + { + String first=unordered.get(0).getVehicleId(); + String second=unordered.get(0).getHeadway().getOtherVehicleId(); + + ordered.add(first); + count--; + ordered.add(second); + count--; + }else + { + ordered.add(VehicleStateManager.getInstance().getVehicleState(ordered.get(ordered.size()-1)).getHeadway().getOtherVehicleId()); + count--; + } + + } + // check first vehicle equals last vehicle + if(ordered.size()>1) + { + if(!ordered.get(ordered.size()-1).equals(ordered.get(0))) + { + return null; + } + } + } + return ordered; + } + protected ArrayList getCurrentHoldingTimesForStop(String stopId) + { + ArrayList currentHoldingTimes=new ArrayList(); + + for(IpcVehicleComplete currentVehicle:VehicleDataCache.getInstance().getVehicles()) + { + VehicleState vehicleState = VehicleStateManager.getInstance().getVehicleState(currentVehicle.getId()); + if(vehicleState.getHoldingTime()!=null) + { + if(vehicleState.getHoldingTime().getStopId().equals(stopId)) + { + currentHoldingTimes.add(vehicleState.getHoldingTime()); + } + } + } + return currentHoldingTimes; + } + protected HoldingTime getNextDepartureByHoldingTime(String currentVehicleId, List holdingTimes) + { + logger.debug("Looking for next departure by holding time for vehicle {}.", currentVehicleId ); + + + HoldingTime nextDeparture=null; + for(HoldingTime holdingTime:holdingTimes) + { + + if(!holdingTime.getVehicleId().equals(currentVehicleId) && holdingTime.isArrivalPredictionUsed()==false) + { + // Ignore predicted holding times in calculation + if(holdingTime.isArrivalPredictionUsed()==false) + { + if(nextDeparture==null||holdingTime.getHoldingTime().before(nextDeparture.getHoldingTime())) + { + nextDeparture=holdingTime; + } + }else + { + logger.debug("Holding time not considered as it is a predicted holding time. {}", holdingTime); + } + } + } + return nextDeparture; + } + private IpcArrivalDeparture getLastVehicleDepartureTime(String currentVehicleId, String tripId, String stopId, Date time) + { + StopArrivalDepartureCacheKey currentStopKey=new StopArrivalDepartureCacheKey(stopId,time); + + List currentStopList = StopArrivalDepartureCacheFactory.getInstance().getStopHistory(currentStopKey); + + IpcArrivalDeparture closestDepartureEvent=null; + if(currentStopList!=null) + { + /* These are sorted when placed in cache */ + for(IpcArrivalDeparture event:currentStopList) + { + //if(event.isDeparture() && event.getTripId().equals(tripId)) TODO This is because of the GTFS config of Atlanta Streetcar. Could we use route_id instead? + if(!event.getVehicleId().equals(currentVehicleId)) + { + if(event.isDeparture() ) + { + if(closestDepartureEvent==null) + closestDepartureEvent=event; + else if (time.getTime()-event.getTime().getTime() < time.getTime()-closestDepartureEvent.getTime().getTime()) + { + closestDepartureEvent=event; + } + } + } + } + } + return closestDepartureEvent; + } + + private ArrayList getOtherVehiclesAtStop(String stopId, String currentVehicleId) + { + ArrayList alsoAtStop=new ArrayList(); + + for(IpcVehicleComplete currentVehicle:VehicleDataCache.getInstance().getVehicles()) + { + if(currentVehicle.isAtStop() && currentVehicle.getAtOrNextStopId().equals(stopId) && !currentVehicle.getId().equals(currentVehicleId)) + { + alsoAtStop.add(currentVehicle.getId()); + } + } + + return alsoAtStop; + } + + private IpcArrivalDeparture getLastVehicleArrivalEvent(String stopid, String vehicleid, Date time) + { + StopArrivalDepartureCacheKey currentStopKey=new StopArrivalDepartureCacheKey(stopid,time); + + List currentStopList = StopArrivalDepartureCacheFactory.getInstance().getStopHistory(currentStopKey); + + IpcArrivalDeparture closestArrivalEvent=null; + + if(currentStopList!=null) + { + for(IpcArrivalDeparture event:currentStopList) + { + if(event.getVehicleId().equals(vehicleid)) + { + // if it arrives after the current time + if(event.isArrival() && event.getStopId().equals(stopid) ) + { + if(event.getTime().getTime()>time.getTime()) + { + if(closestArrivalEvent==null ) + { + closestArrivalEvent=event; + } + else if (Math.abs(time.getTime()-event.getTime().getTime()) < Math.abs(time.getTime()-closestArrivalEvent.getTime().getTime())) + { + closestArrivalEvent=event; + } + } + } + } + } + } + return closestArrivalEvent; + } + public ArrayList addArrivalTimesForVehiclesAtStop(String stopid, String vehicleid, Date time, ArrayList arrivalTimes) + { + + ArrayList events=new ArrayList(); + for(String othervehicle:getOtherVehiclesAtStop(stopid, vehicleid)) + { + IpcArrivalDeparture event = getLastVehicleArrivalEvent(stopid, othervehicle, time); + + if(event!=null) + { + arrivalTimes.add(event.getTime().getTime()); + events.add(event); + } + } + Collections.sort(arrivalTimes); + Collections.sort(events, new IpcArrivalDepartureComparator()); + return events; + } + + private static Long calculateHoldingTime(Long current_vehicle_arrival_time, Long last_vehicle_departure_time, Long N[], int max_predictions) + { + long max_value=-1; + + for(int i=0;imax_value) + { + max_value=value; + } + } + return Math.max(max_value-(current_vehicle_arrival_time-last_vehicle_departure_time),0); + } + + private long calculateHoldingTime(long current_vehicle_arrival_time, long last_vehicle_departure_time) + { + // TODO to be implemented as per google doc. + + //HoldingTime= max(0,PlannedHeadway - TimeSinceLastDeparture) + long holdingTime; + + holdingTime=Math.max(plannedHeadwayMsec.getValue().longValue()-Math.abs(current_vehicle_arrival_time-last_vehicle_departure_time) , 0); + + return holdingTime; + } + @Override + public HoldingTime generateHoldingTime(VehicleState vehicleState, IpcPrediction arrivalPrediction) { + + if(!useArrivalPredictions.getValue()) + { + return null; + } + + HoldingTime holdingTime = null; + if(arrivalPrediction.isArrival() && isControlStop(arrivalPrediction.getStopId())) + { + logger.debug("Calling Holding Generator for prediction : {}", arrivalPrediction.toString()); + + /* TODO We will also need a holding time based on predictions to inform predictions for later stops. We can swap to arrival one once we have arrival event for vehicle and set a definite hold time.*/ + IpcPrediction forwardDeparturePrediction=getForwardVehicleDeparturePrediction(arrivalPrediction); + if(forwardDeparturePrediction==null) + logger.debug("Cannot find last departure prediction for : {}", arrivalPrediction.toString()); + else + logger.debug("Found last vehicle predicted departure event: {}", forwardDeparturePrediction.toString()); + + IpcArrivalDeparture lastDeparture = getLastVehicleDepartureTime(arrivalPrediction.getVehicleId(),arrivalPrediction.getTripId(), arrivalPrediction.getStopId(), new Date(arrivalPrediction.getPredictionTime())); + if(lastDeparture==null) + logger.debug("Cannot find last departure for : {}", arrivalPrediction.toString()); + else + logger.debug("Found last vehicle departure event: {}", lastDeparture.toString()); + + List backwardArrivalPredictions=getBackwardArrivalPredictions(arrivalPrediction); + if(backwardArrivalPredictions!=null) + logger.debug("Found {} arrival predictions for stop {}.", backwardArrivalPredictions.size(),arrivalPrediction.getStopId()); + + + if((forwardDeparturePrediction != null || lastDeparture!=null) && backwardArrivalPredictions!=null && backwardArrivalPredictions.size()>0) + { + for(int i=0;iforwardDeparturePrediction.getPredictionTime()) + { + long holdingTimeValue=calculateHoldingTime(arrivalPrediction.getPredictionTime(), forwardDeparturePrediction.getPredictionTime() ,N, maxPredictionsForHoldingTimeCalculation.getValue()); + + holdingTime=new HoldingTime(new Date(arrivalPrediction.getPredictionTime()+holdingTimeValue), new Date(Core.getInstance().getSystemTime()), arrivalPrediction.getVehicleId(), arrivalPrediction.getStopId(), arrivalPrediction.getTripId(), + arrivalPrediction.getRouteId(),true, false, new Date(arrivalPrediction.getPredictionTime()), true, N.length); + + logger.debug("Holding time for : {} is {}.", arrivalPrediction.toString(), holdingTime); + if(storeHoldingTimes.getValue()) + Core.getInstance().getDbLogger().add(holdingTime); + + }else if(lastDeparture.getTime().getTime()<=forwardDeparturePrediction.getPredictionTime()) + { + long holdingTimeValue=calculateHoldingTime(arrivalPrediction.getPredictionTime(), lastDeparture.getTime().getTime() ,N, maxPredictionsForHoldingTimeCalculation.getValue()); + + holdingTime=new HoldingTime(new Date(arrivalPrediction.getPredictionTime()+holdingTimeValue), new Date(Core.getInstance().getSystemTime()), arrivalPrediction.getVehicleId(), arrivalPrediction.getStopId(), arrivalPrediction.getTripId(), + arrivalPrediction.getRouteId(),true, false, new Date(arrivalPrediction.getPredictionTime()), true, N.length); + + logger.debug("Holding time for : {} is {}.", arrivalPrediction.toString(), holdingTime); + if(storeHoldingTimes.getValue()) + Core.getInstance().getDbLogger().add(holdingTime); + + } + }else if(forwardDeparturePrediction!=null) + { + long holdingTimeValue=calculateHoldingTime(arrivalPrediction.getPredictionTime(), forwardDeparturePrediction.getPredictionTime() ,N, maxPredictionsForHoldingTimeCalculation.getValue()); + + holdingTime=new HoldingTime(new Date(arrivalPrediction.getPredictionTime()+holdingTimeValue), new Date(Core.getInstance().getSystemTime()), arrivalPrediction.getVehicleId(), arrivalPrediction.getStopId(), arrivalPrediction.getTripId(), + arrivalPrediction.getRouteId(), true, false, new Date(arrivalPrediction.getPredictionTime()), true, N.length); + logger.debug("Holding time for : {} is {}.", arrivalPrediction.toString(), holdingTime); + if(storeHoldingTimes.getValue()) + Core.getInstance().getDbLogger().add(holdingTime); + + }else if(lastDeparture!=null) + { + long holdingTimeValue=calculateHoldingTime(arrivalPrediction.getPredictionTime(), lastDeparture.getTime().getTime() ,N, maxPredictionsForHoldingTimeCalculation.getValue()); + + + + holdingTime=new HoldingTime(new Date(arrivalPrediction.getPredictionTime()+holdingTimeValue), new Date(Core.getInstance().getSystemTime()), arrivalPrediction.getVehicleId(), arrivalPrediction.getStopId(), arrivalPrediction.getTripId(), + arrivalPrediction.getRouteId(), true, false, new Date(arrivalPrediction.getPredictionTime()), true, N.length); + logger.debug("Holding time for : {} is {}.", arrivalPrediction.toString(), holdingTime); + if(storeHoldingTimes.getValue()) + Core.getInstance().getDbLogger().add(holdingTime); + } + + }else + { + if(forwardDeparturePrediction != null || lastDeparture!=null) + { + Long holdingTimeValue=null; + if(lastDeparture!=null&&forwardDeparturePrediction!=null) + { + holdingTimeValue=calculateHoldingTime(Math.min(lastDeparture.getTime().getTime(), forwardDeparturePrediction.getPredictionTime()), lastDeparture.getTime().getTime()); + }else + if(lastDeparture!=null&&forwardDeparturePrediction==null) + { + holdingTimeValue=calculateHoldingTime(arrivalPrediction.getPredictionTime(), lastDeparture.getTime().getTime() ); + }else + if(lastDeparture==null&&forwardDeparturePrediction!=null) + { + holdingTimeValue=calculateHoldingTime(arrivalPrediction.getPredictionTime(), forwardDeparturePrediction.getPredictionTime() ); + } + if(holdingTimeValue!=null) + { + holdingTime=new HoldingTime(new Date(arrivalPrediction.getPredictionTime()+holdingTimeValue), new Date(Core.getInstance().getSystemTime()), arrivalPrediction.getVehicleId(), arrivalPrediction.getStopId(), arrivalPrediction.getTripId(), + arrivalPrediction.getRouteId(), true, false, new Date(arrivalPrediction.getPredictionTime()), true, 0); + + logger.debug("Holding time for : {} is {}.", arrivalPrediction.toString(), holdingTime); + + if(storeHoldingTimes.getValue()) + Core.getInstance().getDbLogger().add(holdingTime); + + return holdingTime; + }else + { + logger.debug("Did not calucate holding time for some strange reason.",arrivalPrediction.getStopId()); + holdingTime=new HoldingTime(new Date(arrivalPrediction.getPredictionTime()), new Date(Core.getInstance().getSystemTime()), arrivalPrediction.getVehicleId(), arrivalPrediction.getStopId(), arrivalPrediction.getTripId(), + arrivalPrediction.getRouteId(), true, true, new Date(arrivalPrediction.getPredictionTime()), false, 0); + + if(storeHoldingTimes.getValue()) + Core.getInstance().getDbLogger().add(holdingTime); + + return holdingTime; + } + }else + { + logger.debug("Did not find last vehicle departure for stop {}. This is required to calculate holding time.",arrivalPrediction.getStopId()); + long current_vehicle_arrival_time=arrivalPrediction.getPredictionTime(); + holdingTime=new HoldingTime(new Date(current_vehicle_arrival_time), new Date(Core.getInstance().getSystemTime()), arrivalPrediction.getVehicleId(), arrivalPrediction.getStopId(), arrivalPrediction.getTripId(), + arrivalPrediction.getRouteId(), true, true, new Date(arrivalPrediction.getPredictionTime()), false, 0); + + if(storeHoldingTimes.getValue()) + Core.getInstance().getDbLogger().add(holdingTime); + + return holdingTime; + } + } + } + return holdingTime; + } + protected IpcPrediction getForwardVehicleDeparturePrediction(IpcPrediction predictionEvent) + { + PredictionDataCache predictionCache = PredictionDataCache.getInstance(); + + List predictions = new ArrayList(); + + List predictionsForRouteStopDests = predictionCache.getPredictions(predictionEvent.getRouteId(), predictionEvent.getStopId()); + + for(IpcPredictionsForRouteStopDest predictionForRouteStopDest: predictionsForRouteStopDests) + { + for(IpcPrediction prediction:predictionForRouteStopDest.getPredictionsForRouteStop()) + { + predictions.add(prediction); + } + } + Collections.sort(predictions, new PredictionTimeComparator()); + + int found=-1; + IpcPrediction closestPrediction=null; + for(int i=0;ifound) + { + if(predictions.get(i).isArrival()==false + && predictions.get(i).getStopId().equals(predictionEvent.getStopId()) + && !predictions.get(i).getTripId().equals(predictionEvent.getTripId())) + { + closestPrediction=predictions.get(i); + + return predictions.get(i); + } + } + } + + return null; + } + + + protected List getBackwardArrivalPredictions(IpcPrediction predictionEvent) + { + PredictionDataCache predictionCache = PredictionDataCache.getInstance(); + + List predictions = new ArrayList(); + + List predictionsForRouteStopDests = predictionCache.getPredictions(predictionEvent.getRouteId(), predictionEvent.getStopId()); + + for(IpcPredictionsForRouteStopDest predictionForRouteStopDest: predictionsForRouteStopDests) + { + for(IpcPrediction prediction:predictionForRouteStopDest.getPredictionsForRouteStop()) + { + //logger.debug(prediction.toString()); + //TODO if(prediction.getPredictionTime()>predictionEvent.getPredictionTime()&&prediction.isArrival()) + // The end of the route it seems to only generate departure predictions which points towards an issue. + //if(prediction.getPredictionTime()>predictionEvent.getPredictionTime()) + if(prediction.getPredictionTime()>predictionEvent.getPredictionTime()&&prediction.isArrival()&&!predictionEvent.getVehicleId().equals(prediction.getVehicleId())) + predictions.add(prediction); + } + } + Collections.sort(predictions, new PredictionTimeComparator()); + /* TODO get the first two of the same type */ + return predictions; + } + @Override + public List getControlPointStops() { + + if(controlStopList.getValue() == null){ + return null; + } + + ArrayList controlStops=new ArrayList(); + + for(String stopEntry: controlStopList.getValue()) + { + controlStops.add(new ControlStop(stopEntry)); + } + return controlStops; + } + + + private Long[] predictionsToLongArray(List predictions) + { + Long[] list=new Long[predictions.size()]; + + if(predictions!=null) + { + int i=0; + for(IpcPrediction prediction:predictions) + { + list[i]=new Long(prediction.getPredictionTime()); + i++; + } + } + return list; + } + ArrayList predictionsToLongArrayList(List predictions, ArrayList list) + { + + if(predictions!=null) + { + for(IpcPrediction prediction:predictions) + { + list.add(new Long(prediction.getPredictionTime())); + } + } + Collections.sort(list); + return list; + } + private boolean isControlStop(String stopId) + { + ControlStop controlStop=new ControlStop( null, stopId); + if(getControlPointStops()!=null) + { + for(ControlStop controlStopInList:getControlPointStops()) + { + if(controlStopInList.getStopId().equals(controlStop.getStopId())) + { + return true; + } + } + } + return false; + } + public static void main(String[] args) { + { + Long N[]={new Long(11),new Long(18),new Long(28)}; + + Long result=calculateHoldingTime(5L, 0L, N, maxPredictionsForHoldingTimeCalculation.getValue()); + + System.out.println("Holding time: " + result); + } + { + Long N[]={9L,21L,26L}; + + Long result=calculateHoldingTime(5L, 0L, N, maxPredictionsForHoldingTimeCalculation.getValue()); + + System.out.println("Holding time: " + result); + } + { + Long N[]={10L}; + + Long result=calculateHoldingTime(4L, 0L, N, maxPredictionsForHoldingTimeCalculation.getValue()); + + System.out.println("Holding time: " + result); + + } + + { + Long N[]={12L,18L,25L}; + + Long result=calculateHoldingTime(7L, 0L, N, maxPredictionsForHoldingTimeCalculation.getValue()); + + System.out.println("Holding time: " + result); + + } + { + Long N[]={10L,14L,19L}; + + Long result=calculateHoldingTime(3L, 0L, N, maxPredictionsForHoldingTimeCalculation.getValue()); + + System.out.println("Holding time: " + result); + + } + { + Long N[]={13L,20L,28L}; + + Long result=calculateHoldingTime(4L, 0L, N, maxPredictionsForHoldingTimeCalculation.getValue()); + + System.out.println("Holding time: " + result); + + } + } + + @Override + public void handleDeparture(VehicleState vehicleState, ArrivalDeparture arrivalDeparture) { + /* if it is a departure from a control stop */ + if(arrivalDeparture.isDeparture() && isControlStop(arrivalDeparture.getStopId())) + { + /* remove holding time once vehicle has left control point. */ + + logger.debug("Removing holding time {} due to departure {}.",vehicleState.getHoldingTime(), arrivalDeparture); + vehicleState.setHoldingTime(null); + + if(regenerateOnDeparture.getValue()) + { + for(HoldingTime holdingTime:getCurrentHoldingTimesForStop(arrivalDeparture.getStopId())) + { + VehicleState otherState = VehicleStateManager.getInstance().getVehicleState(holdingTime.getVehicleId()); + + IpcArrivalDeparture lastArrival = getLastVehicleArrivalEvent(arrivalDeparture.getStopId(), otherState.getVehicleId(), arrivalDeparture.getAvlTime()); + + HoldingTime otherHoldingTime = HoldingTimeGeneratorFactory.getInstance().generateHoldingTime(otherState, lastArrival); + + HoldingTimeCache.getInstance().putHoldingTime(otherHoldingTime); + + otherState.setHoldingTime(otherHoldingTime); + } + } + } + } + +} diff --git a/transitclock/src/main/java/org/transitclock/core/holdingmethod/HoldingTimeGeneratorFactory.java b/transitclock/src/main/java/org/transitclock/core/holdingmethod/HoldingTimeGeneratorFactory.java new file mode 100755 index 000000000..b7eab69e2 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/holdingmethod/HoldingTimeGeneratorFactory.java @@ -0,0 +1,31 @@ +package org.transitclock.core.holdingmethod; + +import org.transitclock.config.StringConfigValue; +import org.transitclock.core.holdingmethod.HoldingTimeGenerator; +import org.transitclock.utils.ClassInstantiator; +/** + * @author Sean Óg Crudden + */ +public class HoldingTimeGeneratorFactory { + // The name of the class to instantiate + private static StringConfigValue className = + new StringConfigValue("transitclock.core.holdingTimeGeneratorClass", + null, + "Specifies the name of the class used for generating " + + "holding times."); + + private static HoldingTimeGenerator singleton = null; + + /********************** Member Functions **************************/ + + public static HoldingTimeGenerator getInstance() { + // If the HoldingTimeGenerator hasn't been created yet then do so now + if (singleton == null) { + if(className.getValue()!=null&&className.getValue().length()>0) + singleton = ClassInstantiator.instantiate(className.getValue(), + HoldingTimeGenerator.class); + } + + return singleton; + } +} diff --git a/transitclock/src/main/java/org/transitclock/core/holdingmethod/PredictionTimeComparator.java b/transitclock/src/main/java/org/transitclock/core/holdingmethod/PredictionTimeComparator.java new file mode 100755 index 000000000..9f10fb63e --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/holdingmethod/PredictionTimeComparator.java @@ -0,0 +1,23 @@ +package org.transitclock.core.holdingmethod; + +import java.util.Comparator; + +import org.transitclock.ipc.data.IpcPrediction; +/** + * @author Sean Óg Crudden + * Compare the time of two predictions. Used to put predictions in order. + */ +public class PredictionTimeComparator implements Comparator { + + @Override + public int compare(IpcPrediction o1, IpcPrediction o2) { + + + if(o1.getPredictionTime()==o2.getPredictionTime()) + return 0; + else if(o1.getPredictionTime()>o2.getPredictionTime()) + return 1; + else + return -1; + } +} diff --git a/transitclock/src/main/java/org/transitclock/core/holdingmethod/SimpleHoldingTimeGeneratorImpl.java b/transitclock/src/main/java/org/transitclock/core/holdingmethod/SimpleHoldingTimeGeneratorImpl.java new file mode 100644 index 000000000..4300b80f4 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/holdingmethod/SimpleHoldingTimeGeneratorImpl.java @@ -0,0 +1,254 @@ +package org.transitclock.core.holdingmethod; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; + +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.StringListConfigValue; +import org.transitclock.core.ArrivalDepartureGeneratorDefaultImpl; +import org.transitclock.core.Indices; +import org.transitclock.core.VehicleState; +import org.transitclock.core.dataCache.PredictionDataCache; +import org.transitclock.core.dataCache.StopArrivalDepartureCacheFactory; +import org.transitclock.core.dataCache.StopArrivalDepartureCacheKey; +import org.transitclock.core.dataCache.VehicleDataCache; +import org.transitclock.core.dataCache.VehicleStateManager; +import org.transitclock.core.dataCache.ehcache.StopArrivalDepartureCache; +import org.transitclock.db.structs.ArrivalDeparture; +import org.transitclock.db.structs.HoldingTime; +import org.transitclock.db.structs.Prediction; +import org.transitclock.db.structs.Stop; +import org.transitclock.ipc.data.IpcArrivalDeparture; +import org.transitclock.ipc.data.IpcPrediction; +import org.transitclock.ipc.data.IpcPredictionsForRouteStopDest; +import org.transitclock.ipc.data.IpcVehicleComplete; + +/** + * @author Sean Óg Crudden + * Simple holding time generator. + */ +public class SimpleHoldingTimeGeneratorImpl implements HoldingTimeGenerator { + private static final Logger logger = + LoggerFactory.getLogger(SimpleHoldingTimeGeneratorImpl.class); + + protected static BooleanConfigValue storeHoldingTimes = new BooleanConfigValue("transitclock.holding.storeHoldingTimes", + true, + "This is set to true to record all holding times."); + + protected static IntegerConfigValue plannedHeadwayMsec = new IntegerConfigValue("transitclock.holding.plannedHeadwayMsec", 60*1000*9, "Planned Headway"); + protected static StringListConfigValue controlStopList = new StringListConfigValue("transitclock.holding.controlStops", null, "This is a list of stops to generate holding times for."); + + public HoldingTime generateHoldingTime(VehicleState vehicleState, IpcArrivalDeparture event) { + + if(event.isArrival() && isControlStop(event.getStopId(), event.getStopPathIndex())) + { + logger.debug("Calling Simple Holding Generator for event : {}", event.toString()); + + IpcArrivalDeparture lastVehicleDeparture = getLastVehicleDepartureTime(event.getTripId(), event.getStopId(), new Date(event.getTime().getTime())); + + if(lastVehicleDeparture!=null) + { + logger.debug("Found last vehicle departure event: {}", lastVehicleDeparture.toString()); + + long current_vehicle_arrival_time=event.getTime().getTime(); + + long holdingTimeValue=calculateHoldingTime(current_vehicle_arrival_time, lastVehicleDeparture.getTime().getTime()); + + logger.debug("Holding time for : {} is {}.", event.toString(), holdingTimeValue); + + HoldingTime holdingTime=new HoldingTime(new Date(current_vehicle_arrival_time+holdingTimeValue), new Date(Core.getInstance().getSystemTime()), event.getVehicleId(), event.getStopId(), event.getTripId(), + event.getRouteId(), false, true, new Date(event.getTime().getTime()), true, 0); + + if(storeHoldingTimes.getValue()) + Core.getInstance().getDbLogger().add(holdingTime); + + return holdingTime; + + }else + { + logger.debug("Did not find last vehicle departure for stop {}. This is required to calculate holding time.",event.getStopId() ); + + long current_vehicle_arrival_time=event.getTime().getTime(); + HoldingTime holdingTime=new HoldingTime(new Date(current_vehicle_arrival_time), new Date(Core.getInstance().getSystemTime()), event.getVehicleId(), event.getStopId(), event.getTripId(), + event.getRouteId(), false, true, new Date(event.getTime().getTime()), false, 0); + return holdingTime; + } + + } + // Return null so has no effect. + return null; + } + + private IpcArrivalDeparture getLastVehicleDepartureTime(String tripId, String stopId, Date time) + { + StopArrivalDepartureCacheKey currentStopKey=new StopArrivalDepartureCacheKey(stopId,time); + + List currentStopList = StopArrivalDepartureCacheFactory.getInstance().getStopHistory(currentStopKey); + + IpcArrivalDeparture closestDepartureEvent=null; + if(currentStopList!=null) + { + /* These are sorted when placed in cache */ + for(IpcArrivalDeparture event:currentStopList) + { + //if(event.isDeparture() && event.getTripId().equals(tripId)) TODO This is because of the GTFS config of Atlanta Streetcar. Could we use route_id instead? + if(event.isDeparture() ) + { + if(closestDepartureEvent==null) + closestDepartureEvent=event; + else if (time.getTime()-event.getTime().getTime() < time.getTime()-closestDepartureEvent.getTime().getTime()) + { + closestDepartureEvent=event; + } + } + } + } + return closestDepartureEvent; + } + private long calculateHoldingTime(long current_vehicle_arrival_time, long last_vehicle_departure_time, long N_1, long N_2) + { + //G16=MAX(IF((E18-H15)/3>(F17-H15)/2,((E18-H15)/3-(B16-H15))/1.5,((F17-H15)/2-(B16-H15))/2),0) + + /*Vehicle Z departure time at this stop. (H15) + One vehicle arrive at control point at 3:20. (vehicle A) (B16) + The next + 1 vehicle arrives at control point at 3:29 (vehicle B) (F17) + The next + 2 vehicle arrives at control point at 3:42 (vehicle C) (E18) */ + + long holdingTime; + long E18=N_2; + long F17=N_1; + long B16=current_vehicle_arrival_time; + long H15=last_vehicle_departure_time; + + if(((E18-H15)/3)>((F17-H15)/2)) + { + holdingTime=(long) (((E18-H15)/3-(B16-H15))); + }else + { + holdingTime=((F17-H15)/2-(B16-H15)); + } + return Math.max(holdingTime, 0); + + } + + + + private long calculateHoldingTime(long current_vehicle_arrival_time, long last_vehicle_departure_time) + { + // TODO to be implemented as per google doc. + + //HoldingTime= max(0,PlannedHeadway - TimeSinceLastDeparture) + long holdingTime; + + holdingTime=Math.max(plannedHeadwayMsec.getValue().longValue()-Math.abs(current_vehicle_arrival_time-last_vehicle_departure_time) , 0); + + return holdingTime; + } + @Override + public HoldingTime generateHoldingTime(VehicleState vehicleState,IpcPrediction arrivalPrediction) { + + HoldingTime holdingTime = null; + + return holdingTime; + } + protected IpcPrediction getForwardVehicleDeparturePrediction(IpcPrediction predictionEvent) + { + PredictionDataCache predictionCache = PredictionDataCache.getInstance(); + + List predictions = new ArrayList(); + + List predictionsForRouteStopDests = predictionCache.getPredictions(predictionEvent.getRouteId(), predictionEvent.getStopId()); + + for(IpcPredictionsForRouteStopDest predictionForRouteStopDest: predictionsForRouteStopDests) + { + for(IpcPrediction prediction:predictionForRouteStopDest.getPredictionsForRouteStop()) + { + predictions.add(prediction); + } + } + Collections.sort(predictions, new PredictionTimeComparator()); + + int found=-1; + + for(int i=0;ifound) + { + if(predictions.get(i).isArrival()==false + && predictions.get(i).getStopId().equals(predictionEvent.getStopId()) + && !predictions.get(i).getTripId().equals(predictionEvent.getTripId())) + { + return predictions.get(i); + } + } + } + return null; + } + protected List getBackwardArrivalPredictions(IpcPrediction predictionEvent) + { + PredictionDataCache predictionCache = PredictionDataCache.getInstance(); + + List predictions = new ArrayList(); + + List predictionsForRouteStopDests = predictionCache.getPredictions(predictionEvent.getRouteId(), predictionEvent.getStopId()); + + for(IpcPredictionsForRouteStopDest predictionForRouteStopDest: predictionsForRouteStopDests) + { + for(IpcPrediction prediction:predictionForRouteStopDest.getPredictionsForRouteStop()) + { + logger.debug(prediction.toString()); + //TODO if(prediction.getPredictionTime()>predictionEvent.getPredictionTime()&&prediction.isArrival()) + // The end of the route it seems to only generate departure predictions which points towards an issue. + //if(prediction.getPredictionTime()>predictionEvent.getPredictionTime()) + if(prediction.getPredictionTime()>predictionEvent.getPredictionTime()&&prediction.isArrival()) + predictions.add(prediction); + } + } + Collections.sort(predictions, new PredictionTimeComparator()); + /* TODO get the first two of the same type */ + return predictions; + } + @Override + public List getControlPointStops() { + + ArrayList controlStops=new ArrayList(); + + for(String stopEntry: controlStopList.getValue()) + { + controlStops.add(new ControlStop(stopEntry)); + } + return controlStops; + } + + private boolean isControlStop(String stopId, int stopPathIndex) + { + ControlStop controlStop=new ControlStop(""+stopPathIndex, stopId); + if(getControlPointStops()!=null) + return getControlPointStops().contains(controlStop); + else + return false; + } + + @Override + public void handleDeparture(VehicleState vehicleState, ArrivalDeparture arrivalDeparture) { + // TODO Auto-generated method stub + + } + + +} diff --git a/transitime/src/main/java/org/transitime/core/package-info.java b/transitclock/src/main/java/org/transitclock/core/package-info.java similarity index 97% rename from transitime/src/main/java/org/transitime/core/package-info.java rename to transitclock/src/main/java/org/transitclock/core/package-info.java index a986a34ac..cf65b2fb4 100644 --- a/transitime/src/main/java/org/transitime/core/package-info.java +++ b/transitclock/src/main/java/org/transitclock/core/package-info.java @@ -24,4 +24,4 @@ * @author SkiBu Smith * */ -package org.transitime.core; \ No newline at end of file +package org.transitclock.core; \ No newline at end of file diff --git a/transitime/src/main/java/org/transitime/core/predAccuracy/NextBusPredictionAccuracyModule.java b/transitclock/src/main/java/org/transitclock/core/predAccuracy/NextBusPredictionAccuracyModule.java old mode 100644 new mode 100755 similarity index 94% rename from transitime/src/main/java/org/transitime/core/predAccuracy/NextBusPredictionAccuracyModule.java rename to transitclock/src/main/java/org/transitclock/core/predAccuracy/NextBusPredictionAccuracyModule.java index ca2d47136..ee160a1cf --- a/transitime/src/main/java/org/transitime/core/predAccuracy/NextBusPredictionAccuracyModule.java +++ b/transitclock/src/main/java/org/transitclock/core/predAccuracy/NextBusPredictionAccuracyModule.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.core.predAccuracy; +package org.transitclock.core.predAccuracy; import java.io.IOException; import java.io.InputStream; @@ -33,12 +33,12 @@ import org.jdom2.input.SAXBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.applications.Core; -import org.transitime.config.StringConfigValue; -import org.transitime.db.structs.Route; -import org.transitime.db.structs.Trip; -import org.transitime.gtfs.DbConfig; -import org.transitime.modules.Module; +import org.transitclock.applications.Core; +import org.transitclock.config.StringConfigValue; +import org.transitclock.db.structs.Route; +import org.transitclock.db.structs.Trip; +import org.transitclock.gtfs.DbConfig; +import org.transitclock.modules.Module; /** * Reads in external prediction data from NextBus feed and internal Transitime @@ -60,7 +60,7 @@ public class NextBusPredictionAccuracyModule extends PredictionAccuracyModule { /********************** Config Params **************************/ private static final StringConfigValue externalPredictionApiUrl = - new StringConfigValue("transitime.predAccuracy.externalPredictionApiUrl", + new StringConfigValue("transitclock.predAccuracy.externalPredictionApiUrl", "http://webservices.nextbus.com/service/publicXMLFeed?", "URL to access to obtain external predictions."); @@ -69,7 +69,7 @@ private static String getExternalPredictionApiUrl() { } private static final StringConfigValue nextBusAgencyIdForApi = - new StringConfigValue("transitime.predAccuracy.nextBusAgencyIdForApi", + new StringConfigValue("transitclock.predAccuracy.nextBusAgencyIdForApi", null, "Name of agency for API"); @@ -170,6 +170,7 @@ private Document getExternalPredictionsForRoute( SAXBuilder builder = new SAXBuilder(); Document doc = builder.build(in); + in.close(); return doc; } catch (IOException | JDOMException e) { @@ -249,7 +250,7 @@ private void processExternalPredictionsForRoute( PredAccuracyPrediction pred = new PredAccuracyPrediction( routeId, directionId, stopId, tripId, vehicleId, predictedTime, predictionsReadTime, isArrival, - affectedByWaitStop, "NextBus"); + affectedByWaitStop, "NextBus",null,null); storePrediction(pred); } } @@ -292,7 +293,7 @@ public static void main(String[] args) { Core.createCore(); // Create a NextBusAvlModue for testing - Module.start("org.transitime.core.predAccuracy.NextBusPredictionAccuracyModule"); + Module.start("org.transitclock.core.predAccuracy.NextBusPredictionAccuracyModule"); } } diff --git a/transitclock/src/main/java/org/transitclock/core/predAccuracy/PlaybackPredictionAccuracyModule.java b/transitclock/src/main/java/org/transitclock/core/predAccuracy/PlaybackPredictionAccuracyModule.java new file mode 100644 index 000000000..b6c3a77ea --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predAccuracy/PlaybackPredictionAccuracyModule.java @@ -0,0 +1,48 @@ +package org.transitclock.core.predAccuracy; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.applications.Core; +import org.transitclock.utils.PlaybackIntervalTimer; + +public class PlaybackPredictionAccuracyModule extends PredictionAccuracyModule { + private static final Logger logger = LoggerFactory + .getLogger(PredictionAccuracyModule.class); + public PlaybackPredictionAccuracyModule(String agencyId) { + super(agencyId); + + } + + @Override + public void run() { + // TODO Auto-generated method stub + // Log that module successfully started + logger.info("Started module {} for agencyId={}", + getClass().getName(), getAgencyId()); + + // Run forever + PlaybackIntervalTimer timer = new PlaybackIntervalTimer(); + while (true) { + // No need to run at startup since internal predictions won't be + // generated yet. So sleep a bit first. + + // Time.sleep(5000); + if( timer.elapsedMsec() > getTimeBetweenPollingPredictionsMsec()) + { + try { + // Process data + getAndProcessData(getRoutesAndStops(Core.getInstance().getDbConfig().getRoutes()), Core.getInstance().getSystemDate()); + + // Make sure old predictions that were never matched to an + // arrival/departure don't stick around taking up memory. + clearStalePredictions(); + } catch (Exception e) { + e.printStackTrace(); + logger.error("Error accessing predictions feed : "+ e.getMessage(), e); + } + timer.resetTimer(); + } + } + } + +} diff --git a/transitime/src/main/java/org/transitime/core/predAccuracy/PredAccuracyPrediction.java b/transitclock/src/main/java/org/transitclock/core/predAccuracy/PredAccuracyPrediction.java similarity index 87% rename from transitime/src/main/java/org/transitime/core/predAccuracy/PredAccuracyPrediction.java rename to transitclock/src/main/java/org/transitclock/core/predAccuracy/PredAccuracyPrediction.java index 759758426..be090395d 100644 --- a/transitime/src/main/java/org/transitime/core/predAccuracy/PredAccuracyPrediction.java +++ b/transitclock/src/main/java/org/transitclock/core/predAccuracy/PredAccuracyPrediction.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.core.predAccuracy; +package org.transitclock.core.predAccuracy; import java.util.Date; @@ -39,10 +39,12 @@ public class PredAccuracyPrediction { // The time the prediction was read. This allows us to determine // how far out into the future the prediction is for. private final Date predictionReadTime; + private final String scheduledTime; private final boolean isArrival; // affectedByWaitStop is a Boolean so that null can represent "don't know" private final Boolean affectedByWaitStop; private final String source; + private final String algorithm; /********************** Member Functions **************************/ @@ -66,11 +68,13 @@ public class PredAccuracyPrediction { * @param source * Description of the feed, especially useful if have couple of * sources. Can be a value such as "MBTA_epoch" or "NextBus". + * @param algorithm + * This is the algorithm used to generate prediction. */ public PredAccuracyPrediction(String routeId, String directionId, String stopId, String tripId, String vehicleId, Date predictedTime, Date predictionReadTime, boolean isArrival, - Boolean affectedByWaitStop, String source) { + Boolean affectedByWaitStop, String source, String algorithm, String scheduledTime) { super(); this.routeId = routeId; this.directionId = directionId; @@ -82,8 +86,14 @@ public PredAccuracyPrediction(String routeId, String directionId, this.isArrival = isArrival; this.affectedByWaitStop = affectedByWaitStop; this.source = source; + this.scheduledTime = scheduledTime; + this.algorithm = algorithm; } + public String getAlgorithm() { + return algorithm; + } + public String getRouteId() { return routeId; } @@ -143,8 +153,15 @@ public String toString() { (predictedTime.getTime() - predictionReadTime.getTime()) + ", isArrival=" + isArrival + ", affectedByWaitStop=" + affectedByWaitStop - + ", source=" + source + + ", source=" + source + + ", algorithm=" + algorithm + + ", scheduleTime=" + scheduledTime + "]"; } + + public String getScheduledTime() { + return scheduledTime; + } + } diff --git a/transitime/src/main/java/org/transitime/core/predAccuracy/PredictionAccuracyModule.java b/transitclock/src/main/java/org/transitclock/core/predAccuracy/PredictionAccuracyModule.java old mode 100644 new mode 100755 similarity index 69% rename from transitime/src/main/java/org/transitime/core/predAccuracy/PredictionAccuracyModule.java rename to transitclock/src/main/java/org/transitclock/core/predAccuracy/PredictionAccuracyModule.java index c55705d10..e3cbd5d1d --- a/transitime/src/main/java/org/transitime/core/predAccuracy/PredictionAccuracyModule.java +++ b/transitclock/src/main/java/org/transitclock/core/predAccuracy/PredictionAccuracyModule.java @@ -15,37 +15,30 @@ * along with Transitime.org . If not, see . */ -package org.transitime.core.predAccuracy; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; +package org.transitclock.core.predAccuracy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.applications.Core; -import org.transitime.config.IntegerConfigValue; -import org.transitime.core.dataCache.PredictionDataCache; -import org.transitime.db.structs.ArrivalDeparture; -import org.transitime.db.structs.PredictionAccuracy; -import org.transitime.db.structs.Route; -import org.transitime.db.structs.TripPattern; -import org.transitime.ipc.data.IpcPrediction; -import org.transitime.ipc.data.IpcPredictionsForRouteStopDest; -import org.transitime.modules.Module; -import org.transitime.utils.IntervalTimer; -import org.transitime.utils.MapKey; -import org.transitime.utils.Time; +import org.transitclock.applications.Core; +import org.transitclock.config.BooleanConfigValue; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.core.dataCache.PredictionDataCache; +import org.transitclock.db.structs.ArrivalDeparture; +import org.transitclock.db.structs.PredictionAccuracy; +import org.transitclock.db.structs.Route; +import org.transitclock.db.structs.TripPattern; +import org.transitclock.ipc.data.IpcPrediction; +import org.transitclock.ipc.data.IpcPredictionsForRouteStopDest; +import org.transitclock.modules.Module; +import org.transitclock.utils.IntervalTimer; +import org.transitclock.utils.MapKey; +import org.transitclock.utils.Time; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; /** - * Reads internal predictions every transitime.predAccuracy.pollingRateMsec and + * Reads internal predictions every transitclock.predAccuracy.pollingRateMsec and * stores the predictions into memory. Then when arrivals/departures occur the * prediction accuracy can be determined and stored. * @@ -69,17 +62,17 @@ public class PredictionAccuracyModule extends Module { /********************** Config Params **************************/ private static final IntegerConfigValue timeBetweenPollingPredictionsMsec = - new IntegerConfigValue("transitime.predAccuracy.pollingRateMsec", + new IntegerConfigValue("transitclock.predAccuracy.pollingRateMsec", 4 * Time.MS_PER_MIN, "How frequently to query predictions for determining " + "prediction accuracy."); - private static int getTimeBetweenPollingPredictionsMsec() { + protected static int getTimeBetweenPollingPredictionsMsec() { return timeBetweenPollingPredictionsMsec.getValue(); } private static final IntegerConfigValue maxPredTimeMinutes = - new IntegerConfigValue("transitime.predAccuracy.maxPredTimeMinutes", + new IntegerConfigValue("transitclock.predAccuracy.maxPredTimeMinutes", 15, "Maximum time into the future for a pediction for it to " + "be stored in memory for prediction accuracy analysis."); @@ -89,7 +82,7 @@ private static int getMaxPredTimeMinutes() { } private static final IntegerConfigValue maxPredStalenessMinutes = - new IntegerConfigValue("transitime.predAccuracy.maxPredStalenessMinutes", + new IntegerConfigValue("transitclock.predAccuracy.maxPredStalenessMinutes", 15, "Maximum time in minutes a prediction cam be into the " + "past before it is removed from memory because no " @@ -100,17 +93,33 @@ private static int getMaxPredStalenessMinutes() { } private static final IntegerConfigValue stopsPerTrip = - new IntegerConfigValue("transitime.predAccuracy.stopsPerTrip", + new IntegerConfigValue("transitclock.predAccuracy.stopsPerTrip", 5, "Number of stops per trip pattern that should collect " + "prediction data for each polling cycle."); - + private static int getStopsPerTrip() { return stopsPerTrip.getValue(); } + private static final BooleanConfigValue includeStopsFromAllTripPatterns = + new BooleanConfigValue("transitclock.predAccuracy.includeStopsFromAllTripPatterns", + false, + "Include unique stops for all trip patterns when sampling instead of just using stops " + + "for the longest trip pattern per route/direction"); + + private static boolean getIncludeStopsFromAllTripPatterns() { + return includeStopsFromAllTripPatterns.getValue(); + } + + private static final IntegerConfigValue maxRandomStopSelectionsPerTrip = + new IntegerConfigValue("transitclock.predAccuracy.maxRandomStopSelectionsPerTrip", + 100, + "Max number of random stops to look at to get the stopsPerTrip."); + + private static final IntegerConfigValue maxLatenessComparedToPredictionMsec = - new IntegerConfigValue("transitime.predAccuracy.maxLatenessComparedToPredictionMsec", + new IntegerConfigValue("transitclock.predAccuracy.maxLatenessComparedToPredictionMsec", 25 * Time.MS_PER_MIN, "How late in msec a vehicle can arrive/departure a stop " + "compared to the prediction and still have the prediction " @@ -121,7 +130,7 @@ private static int getMaxLatenessComparedToPredictionMsec() { } private static final IntegerConfigValue maxEarlynessComparedToPredictionMsec = - new IntegerConfigValue("transitime.predAccuracy.maxEarlynessComparedToPredictionMsec", + new IntegerConfigValue("transitclock.predAccuracy.maxEarlynessComparedToPredictionMsec", 15 * Time.MS_PER_MIN, "How early in msec a vehicle can arrive/departure a stop " + "compared to the prediction and still have the prediction " @@ -192,24 +201,38 @@ public void run() { // Run forever while (true) { - IntervalTimer timer = new IntervalTimer(); - - try { + IntervalTimer timer = null; + + try { + timer = new IntervalTimer(); // Process data - getAndProcessData(getRoutesAndStops(), new Date()); - + + logger.info("processing prediction accuracy...."); + if(getIncludeStopsFromAllTripPatterns()){ + getAndProcessData(getAllRoutesAndStops(Core.getInstance().getDbConfig().getRoutes()), Core.getInstance().getSystemDate()); + } else { + getAndProcessData(getRoutesAndStops(Core.getInstance().getDbConfig().getRoutes()), Core.getInstance().getSystemDate()); + } + logger.info("processing prediction accuracy complete."); + // Make sure old predictions that were never matched to an // arrival/departure don't stick around taking up memory. clearStalePredictions(); + } catch (Exception e) { - logger.error("Error accessing predictions feed", e); - } finally { - // Wait appropriate amount of time till poll again - long elapsedMsec = timer.elapsedMsec(); - long sleepTime = - getTimeBetweenPollingPredictionsMsec() - elapsedMsec; - if (sleepTime > 0) - Time.sleep(sleepTime); + + logger.error("Error accessing predictions feed {}", e, e); + logger.debug("execption details {}", e, e); + } catch (Throwable t) { + logger.error("possible sql exception {}", t, t); + } finally { + // if we have an exception, we still need to wait to be nice to the cpu + // Wait appropriate amount of time till poll again + long elapsedMsec = timer.elapsedMsec(); + long sleepTime = + getTimeBetweenPollingPredictionsMsec() - elapsedMsec; + if (sleepTime > 0) + Time.sleep(sleepTime); } } } @@ -217,23 +240,23 @@ public void run() { /** * Returns the routes and stops that should store predictions in memory for. * Usually will be all routes for an agency, with a sampling of stops. - * + * Only Includes stops for the longest trip pattern on the route. + * * @return */ - protected List getRoutesAndStops() { + public List getRoutesAndStops(List routes) { // The value to be returned List list = new ArrayList(); // For each route... - List routes = Core.getInstance().getDbConfig().getRoutes(); for (Route route : routes) { RouteAndStops routeStopInfo = new RouteAndStops(); list.add(routeStopInfo); routeStopInfo.routeId = route.getId(); - + // For each direction for the route... - List tripPatterns = + List tripPatterns = route.getLongestTripPatternForEachDirection(); for (TripPattern tripPattern : tripPatterns) { List stopIdsForTripPattern = tripPattern.getStopIds(); @@ -246,14 +269,16 @@ protected List getRoutesAndStops() { } else { // Get stops for direction randomly Set stopsSet = new HashSet(); - while (stopsSet.size() < getStopsPerTrip()) { - // Randomly get a stop ID for the trip pattern - int index = (int) (stopIdsForTripPattern.size() * - Math.random()); - String stopId = stopIdsForTripPattern.get(index); - if (!stopsSet.contains(stopId)) { - stopsSet.add(stopId); + Collections.shuffle(stopIdsForTripPattern); + int index=0; + while (index < stopIdsForTripPattern.size() + && index < maxRandomStopSelectionsPerTrip.getValue() + && stopsSet.size() < getStopsPerTrip()) { + String shuffledStopId = stopIdsForTripPattern.get(index); + if (!stopsSet.contains(shuffledStopId)) { + stopsSet.add(shuffledStopId); } + index++; } routeStopInfo.stopIds.put(tripPattern.getDirectionId(), stopsSet); @@ -266,11 +291,67 @@ protected List getRoutesAndStops() { logger.debug("getRoutesAndStops() returning {}", list); return list; } + + /** + * Returns the routes and stops that should store predictions in memory for. + * Usually will be all routes for an agency, with a sampling of stops. + * Includes all stops for all trip patterns. + * + * @return + */ + public List getAllRoutesAndStops(List routes) { + // The value to be returned + List list = new ArrayList(); + + // For each route... + for (Route route : routes) { + RouteAndStops routeStopInfo = new RouteAndStops(); + list.add(routeStopInfo); + + routeStopInfo.routeId = route.getId(); + + // For each direction for the route... + Map> stopsByDirection = route.getUnorderedUniqueStopsByDirection(); + + for (Map.Entry> entry : stopsByDirection.entrySet()) { + + String directionId = entry.getKey(); + List stopIdsForDirection = entry.getValue(); + + // If not that many stops for the trip then use all of them. + if (getStopsPerTrip() >= stopIdsForDirection.size()) { + // Use all stops for this trip pattern + routeStopInfo.stopIds.put(directionId, stopIdsForDirection); + } else { + // Get stops for direction randomly + Set stopsSet = new HashSet(); + Collections.shuffle(stopIdsForDirection); + int index=0; + while (index < stopIdsForDirection.size() + && index < maxRandomStopSelectionsPerTrip.getValue() + && stopsSet.size() < getStopsPerTrip()) { + String shuffledStopId = stopIdsForDirection.get(index); + if (!stopsSet.contains(shuffledStopId)) { + stopsSet.add(shuffledStopId); + } + index++; + } + routeStopInfo.stopIds.put(directionId, + stopsSet); + } + } + } + + // Return the routes/stops that predictions should be stored in + // memory for + logger.debug("getAllRoutesAndStops() returning {}", list); + return list; + } /** * Stores prediction in memory so that when arrival/departure generated * can compare with the stored prediction. Will only store prediction - * if it is less then transitime.predAccuracy.maxPredTimeMinutes into + * if it is less then transitclock.predAccuracy.maxPredTimeMinutes into * the future. * * @param pred @@ -280,7 +361,7 @@ protected void storePrediction(PredAccuracyPrediction pred) { // memory. This is important because need to limit how much // memory is used for prediction accuracy data collecting. if (pred.getPredictedTime().getTime() > - System.currentTimeMillis() + getMaxPredTimeMinutes()*Time.MS_PER_MIN) { + Core.getInstance().getSystemTime() + getMaxPredTimeMinutes()*Time.MS_PER_MIN) { logger.debug("Prediction is too far into future so not storing " + "it in memory for prediction accuracy analysis. {}", pred); @@ -306,8 +387,12 @@ protected void storePrediction(PredAccuracyPrediction pred) { * needed because sometimes a vehicle will never arrive at a stop and so * will not be removed from memory. In order to prevent memory use from * building up need to clear out the old predictions. + * + * Synchronized as multiple subclasses exist. */ - private void clearStalePredictions() { + + protected synchronized void clearStalePredictions() { + int numPredictionsInMemory = 0; int numPredictionsRemoved = 0; @@ -319,7 +404,7 @@ private void clearStalePredictions() { while (iter.hasNext()) { PredAccuracyPrediction pred = iter.next(); if (pred.getPredictedTime().getTime() < - System.currentTimeMillis() - + Core.getInstance().getSystemTime() - getMaxPredStalenessMinutes()*Time.MS_PER_MIN) { // Prediction was too old so remove it from memory ++numPredictionsRemoved; @@ -332,7 +417,7 @@ private void clearStalePredictions() { storePredictionAccuracyInfo(pred, null); } else { ++numPredictionsInMemory; - logger.debug("Prediction currently held in memory. {}"+pred.toString()); + logger.debug("Prediction currently held in memory. {}",pred); } } } @@ -357,7 +442,7 @@ private void clearStalePredictions() { * same time can easily see from data in db which internal and * external predictions are associated with each other. */ - protected void getAndProcessData(List routesAndStops, + protected synchronized void getAndProcessData(List routesAndStops, Date predictionsReadTime) { logger.debug("Calling PredictionReaderModule.getAndProcessData() " + "to process internal prediction."); @@ -387,7 +472,7 @@ protected void getAndProcessData(List routesAndStops, predictionsReadTime, pred.isArrival(), pred.isAffectedByWaitStop(), - "Transitime"); + "TransitClock",null,null); storePrediction(accuracyPred); predictionsFound = true; } @@ -403,7 +488,30 @@ protected void getAndProcessData(List routesAndStops, } } } - + private static void printPredictionsMap(ConcurrentHashMap> predictionMap, ArrivalDeparture arrivalDeparture) + { + + logger.debug("Looking for match : " + arrivalDeparture.toString() ); + for (PredictionKey key: predictionMap.keySet()) + { + List value = predictionMap.get(key); + for(PredAccuracyPrediction pred:value) + { + boolean keyprinted=false; + if(pred.getVehicleId().equals(arrivalDeparture.getVehicleId())) + { + if(!keyprinted) + { + logger.debug(key.toString()); + keyprinted=true; + } + logger.debug(pred.toString()); + } + } + + + } + } /** * Looks for corresponding prediction in memory. If found then prediction * accuracy information for that prediction is stored in the database. @@ -491,7 +599,7 @@ private static void storePredictionAccuracyInfo( pred.getRouteId(), pred.getDirectionId(), pred.getStopId(), pred.getTripId(), arrivalDepartureTime, pred.getPredictedTime(), pred.getPredictionReadTime(), - pred.getSource(), pred.getVehicleId(), pred.isAffectedByWaitStop()); + pred.getSource(),pred.getAlgorithm(), pred.getVehicleId(), pred.isAffectedByWaitStop()); // Add the prediction accuracy object to the db logger so that // it gets written to database diff --git a/transitclock/src/main/java/org/transitclock/core/predAccuracy/gtfsrt/GTFSRealtimePredictionAccuracyModule.java b/transitclock/src/main/java/org/transitclock/core/predAccuracy/gtfsrt/GTFSRealtimePredictionAccuracyModule.java new file mode 100755 index 000000000..bb5206fa4 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predAccuracy/gtfsrt/GTFSRealtimePredictionAccuracyModule.java @@ -0,0 +1,643 @@ +/* + * 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.predAccuracy.gtfsrt; + +import com.google.protobuf.CodedInputStream; +import com.google.transit.realtime.GtfsRealtime; +import com.google.transit.realtime.GtfsRealtime.FeedEntity; +import com.google.transit.realtime.GtfsRealtime.FeedMessage; +import com.google.transit.realtime.GtfsRealtime.TripUpdate; +import com.google.transit.realtime.GtfsRealtime.TripUpdate.StopTimeUpdate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.applications.Core; +import org.transitclock.config.BooleanConfigValue; +import org.transitclock.config.ClassConfigValue; +import org.transitclock.config.StringConfigValue; +import org.transitclock.core.blockAssigner.BlockAssigner; +import org.transitclock.core.predAccuracy.PredAccuracyPrediction; +import org.transitclock.core.predAccuracy.PredictionAccuracyModule; +import org.transitclock.db.structs.*; +import org.transitclock.gtfs.DbConfig; + +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.time.Instant; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.*; +import java.util.Calendar; + +/** + * Reads in external prediction data from a GTFS realtime trip updates feed and + * stores the data in memory. Then when arrivals/departures occur the prediction + * accuracy can be determined and stored. + * + * @author Sean Og Crudden + * + */ +public class GTFSRealtimePredictionAccuracyModule extends PredictionAccuracyModule { + + private static final Logger logger = LoggerFactory.getLogger(GTFSRealtimePredictionAccuracyModule.class); + + /********************** Config Params **************************/ + + + private static final StringConfigValue gtfsTripUpdateUrl = new StringConfigValue( + "transitclock.predAccuracy.gtfsTripUpdateUrl", "http://127.0.0.1:8091/trip-updates", + "URL to access gtfs-rt trip updates."); + + private static StringConfigValue gtfsRealtimeHeaderKey = + new StringConfigValue("transitclock.predictionAccuracy.apiKeyHeader", + null, + "api key header value if necessary, null if not needed"); + + private static StringConfigValue gtfsRealtimeHeaderValue = + new StringConfigValue("transitclock.predictionAccuracy.apiKeyValue", + null, + "api key value if necessary, null if not needed"); + + + private static ClassConfigValue translatorConfig = + new ClassConfigValue("transitclock.predAccuracy.RtTranslator", null, + "Implementation of GTFSRealtimeTranslator to perform " + + "the translation of stopIds and other rt quirks"); + + // if stopIds needs optional parsing/translation + private GTFSRealtimeTranslator translator = null; + + /** + * @return the gtfstripupdateurl + */ + public static StringConfigValue getGtfstripupdateurl() { + return gtfsTripUpdateUrl; + } + + /********************** Member Functions **************************/ + + /** + * @param agencyId + */ + public GTFSRealtimePredictionAccuracyModule(String agencyId) { + super(agencyId); + } + + /** + * Gets GTFS realtime feed all routes from URL and return FeedMessage + * + * @return the FeedMessage to be processed + */ + + private FeedMessage getExternalPredictions() { + + // Will just read all data from gtfs-rt url + URL url = null; + logger.info("Getting predictions from API using URL={}", getGtfstripupdateurl().getValue()); + + try { + + // Create the connection + url = new URL(getGtfstripupdateurl().getValue()); + + HttpURLConnection + connection = (HttpURLConnection) url.openConnection(); + + if (gtfsRealtimeHeaderKey.getValue() != null && + gtfsRealtimeHeaderValue.getValue() != null) { + connection.addRequestProperty(gtfsRealtimeHeaderKey.getValue(), gtfsRealtimeHeaderValue.getValue()); + connection.addRequestProperty("Cache-Control", "no-cache"); + } + + + // Create a CodedInputStream instead of just a regular InputStream + // so that can change the size limit. Otherwise if file is greater + // than 64MB get an exception. + InputStream inputStream = connection.getInputStream(); + CodedInputStream codedStream = + CodedInputStream.newInstance(inputStream); + // What to use instead of default 64MB limit + final int GTFS_SIZE_LIMIT = 200000000; + codedStream.setSizeLimit(GTFS_SIZE_LIMIT); + + FeedMessage feed = FeedMessage.parseFrom(codedStream); + logger.info("Prediction read successfully from URL={}", getGtfstripupdateurl().getValue()); + + return feed; + } catch (Exception e) { + logger.error("Problem when getting data from GTFS realtime trip updates URL={}", url, e); + return null; + } + } + + /** + * Takes data from Protocol Buffer object and processes it and calls + * storePrediction() on the predictions. + * + * @param feed + * @param dbConfig + */ + public void processExternalPredictions(FeedMessage feed, DbConfig dbConfig) { + + // If couldn't read data from feed then can't process it + if (feed == null) + return; + + // So can look up direction in database + + TimeZone timeZone = dbConfig.getFirstAgency().getTimeZone(); + + logger.info("Processing GTFS-rt feed....."); + + for (FeedEntity entity : feed.getEntityList()) { + if (entity.hasTripUpdate()) { + TripUpdate update = entity.getTripUpdate(); + + Date eventReadTime; + if (update.hasTimestamp() && update.getTimestamp() <= feed.getHeader().getTimestamp()) { + /* + * this is the best option as it is specific to each trip + * update + */ + eventReadTime = new Date(update.getTimestamp() * 1000); + } else { + /* + * can't do anything else? TODO Except maybe look to see if + * this has changed and then take the time it has changed as + * the read time + */ + eventReadTime = new Date(feed.getHeader().getTimestamp() * 1000); + } + + GtfsRealtime.TripDescriptor tripDescriptor = update.getTrip(); + Trip gtfsTrip; + String direction; + + + if (tripDescriptor != null) { + String tripId = getTripId(dbConfig, tripDescriptor); + logger.debug("Trip Descriptor: {}", tripDescriptor); + gtfsTrip = dbConfig.getTrip(tripId); + + if (gtfsTrip != null) { + direction = gtfsTrip.getDirectionId(); + + List tripUpdateStopTimes; + + tripUpdateStopTimes = update.getStopTimeUpdateList(); + + Integer lastStopTimeDelay = null; + + // Loop through trip update stop times + for (int i = 0; i < tripUpdateStopTimes.size(); i++) { + + StopTimeUpdate stopTimeUpdate = tripUpdateStopTimes.get(i); + + // Confirm stopTime update has arrival or departure + if (stopTimeUpdate.hasArrival() || stopTimeUpdate.hasDeparture()) { + + // Get stop path index for stop time update + int stopPathIndex = getStopPathIndex(stopTimeUpdate, gtfsTrip); + + if(stopPathIndex >= 0){ + logger.debug("StopPathIndex : " + stopPathIndex); + int stopPathIndexIncrement = 0; + StopTimeUpdate nextStopTime = getNextStopTime(i, tripUpdateStopTimes); + boolean isLastStopTimeUpdate = nextStopTime == null; + int totalStopPaths = gtfsTrip.getStopPaths().size(); + + // Check if it is last stopTime for trip + if(isLastStopTimeUpdate){ + // Loop through remaining stop paths in case they are not in the feed + while(stopPathIndex + stopPathIndexIncrement < totalStopPaths){ + String stopId = gtfsTrip.getStopPath(stopPathIndex + stopPathIndexIncrement).getStopId(); + ScheduleTime scheduledTime = gtfsTrip.getScheduleTime(stopPathIndex + stopPathIndexIncrement); + StopPath stopPath = gtfsTrip.getStopPath(stopPathIndex + stopPathIndexIncrement); + boolean stopTimeMatchesSchedule = false; + + if(stopTimeMatchesStopPath(stopTimeUpdate, stopPath)){ + lastStopTimeDelay = getStopTimeDelay(stopTimeUpdate, scheduledTime, timeZone.toZoneId()); + stopTimeMatchesSchedule = true; + } + + processPrediction(scheduledTime, stopTimeUpdate, update, gtfsTrip, direction, + stopId, eventReadTime, lastStopTimeDelay, stopTimeMatchesSchedule); + + stopPathIndexIncrement++; + + } + } + else{ + // Look for stopPaths that not be included in the feed and process them + while(stopPathIndex + stopPathIndexIncrement < totalStopPaths && + !stopTimeMatchesStopPath(nextStopTime, gtfsTrip.getStopPath(stopPathIndex + stopPathIndexIncrement))){ + + String stopId = gtfsTrip.getStopPath(stopPathIndex + stopPathIndexIncrement).getStopId(); + ScheduleTime scheduledTime = gtfsTrip.getScheduleTime(stopPathIndex + stopPathIndexIncrement); + boolean stopTimeMatchesSchedule = stopTimeMatchesStopPath(stopTimeUpdate, gtfsTrip.getStopPath(stopPathIndex + stopPathIndexIncrement)); + + if(stopTimeMatchesSchedule){ + lastStopTimeDelay = getStopTimeDelay(stopTimeUpdate, scheduledTime, timeZone.toZoneId()); + } + + processPrediction(scheduledTime, stopTimeUpdate, update, gtfsTrip, direction, + stopId, eventReadTime, lastStopTimeDelay, stopTimeMatchesSchedule); + + stopPathIndexIncrement++; + } + } + } + } + else { + logger.debug("No predictions for vehicleId={} for stop={}", update.getVehicle().getId(), + stopTimeUpdate.getStopId()); + } + } + + } + else { + logger.warn("Got tripId={} but no such trip in the active GTFS", tripId); + } + + } + } + } + } + + private void processPrediction(ScheduleTime scheduledTime, StopTimeUpdate stopTimeUpdate, TripUpdate update, + Trip gtfsTrip, String direction, String stopId, Date eventReadTime, + Integer stopTimeDelay, boolean stopTimeMatchesSchedule){ + if (scheduledTime != null) { + Date eventTime; + if (stopTimeUpdate.hasArrival()) { + eventTime = getEventTime(update, stopTimeUpdate, scheduledTime, true, stopTimeDelay, + stopTimeMatchesSchedule, eventReadTime); + if (eventTime != null) { + processRecord(gtfsTrip.getRouteId(), direction, stopId, gtfsTrip.getId(), + update.getVehicle().getId(), eventTime, eventReadTime, + true, "GTFS-RT (Arrival)", scheduledTime.toString()); + } + } + if (stopTimeUpdate.hasDeparture()) { + eventTime = getEventTime(update, stopTimeUpdate, scheduledTime, false, stopTimeDelay, + stopTimeMatchesSchedule, eventReadTime); + if (eventTime != null) { + processRecord(gtfsTrip.getRouteId(), direction, stopId, gtfsTrip.getId(), + update.getVehicle().getId(), eventTime, eventReadTime, + false, "GTFS-RT (Departure)", scheduledTime.toString()); + } + } + + } else { + logger.debug("No scheduled time found for trip {}, stopId {}, stopPathIndex {}", + gtfsTrip.getId(), stopId); + } + } + + private Integer getStopTimeDelay(StopTimeUpdate tripUpdateStopTime, ScheduleTime scheduleTime, ZoneId zoneId) { + if(tripUpdateStopTime != null && scheduleTime != null){ + int scheduledArrivalTime = scheduleTime.getArrivalTime() != null ? scheduleTime.getArrivalTime() : scheduleTime.getDepartureTime(); + int scheduledDepartureTime = scheduleTime.getDepartureTime() != null ? scheduleTime.getDepartureTime() : scheduleTime.getArrivalTime(); + if(tripUpdateStopTime.hasArrival() && tripUpdateStopTime.getArrival().hasTime()){ + int stopTimeSeconds = getSecondsIntoDay(tripUpdateStopTime.getArrival().getTime() * 1000l, zoneId); + return stopTimeSeconds - scheduledArrivalTime; + } + else if(tripUpdateStopTime.hasDeparture() && tripUpdateStopTime.getDeparture().hasDelay()){ + int stopTimeSeconds = getSecondsIntoDay(tripUpdateStopTime.getDeparture().getTime() * 1000l, zoneId); + return stopTimeSeconds - scheduledDepartureTime; + } + } + return null; + } + + private void processRecord( String routeId, + String direction, + String stopId, + String tripId, + String vehicleId, + Date eventTime, + Date eventReadTime, + boolean isArrival, + String source, + String scheduledTime){ + if (eventTime.after(eventReadTime)) { + + logger.info( + "Storing external prediction routeId={}, " + + "directionId={}, tripId={}, vehicleId={}, " + + "stopId={}, prediction={}, isArrival={}, scheduledTime={}, readTime={}", + routeId, direction, tripId, vehicleId, stopId, eventTime, true, scheduledTime, + eventReadTime.toString()); + + logger.info("Prediction in milliseconds is {} and converted is {}", + eventTime.getTime(), eventTime); + + PredAccuracyPrediction pred = new PredAccuracyPrediction( + routeId, + direction, + stopId, + tripId, + vehicleId, + eventTime, + eventReadTime, + isArrival, + false, + source, + null, + scheduledTime + ); + if(isArrival){ + storeArrivalPrediction(pred); + } else{ + storeDeparturePrediction(pred); + } + } else { + logger.info( + "Discarding as prediction after event. routeId={}, " + + "directionId={}, tripId={}, vehicleId={}, " + + "stopId={}, prediction={}, isArrival={}, scheduledTime={}, readTime={}", + routeId, direction, tripId, vehicleId, + stopId, eventTime, isArrival, scheduledTime, + eventReadTime.toString()); + } + } + + public void storeArrivalPrediction(PredAccuracyPrediction pred){ + storePrediction(pred); + } + + public void storeDeparturePrediction(PredAccuracyPrediction pred){ + storePrediction(pred); + } + + private StopTimeUpdate getNextStopTime(int i, List stopTimes) { + if (i + 1 < stopTimes.size()) { + return stopTimes.get(i + 1); + } + return null; + } + + private Date getEventTime(TripUpdate tripUpdate, + StopTimeUpdate stopTime, + ScheduleTime scheduledTime, + boolean isArrival, + Integer stopTimeDelay, + boolean stopTimeMatchesSchedule, + Date eventReadTime){ + Date eventTime = null; + + if (isArrival && stopTime.getArrival().hasTime() && stopTimeMatchesSchedule) { + eventTime = new Date(stopTime.getArrival().getTime() * 1000); + } + else if(!isArrival && stopTime.getDeparture().hasTime() && stopTimeMatchesSchedule){ + eventTime = new Date(stopTime.getDeparture().getTime() * 1000); + } + else if ((isArrival && stopTime.getArrival().hasDelay()) || (!isArrival && stopTime.getDeparture().hasDelay())) { + + int timeInSeconds = isArrival ? stopTime.getArrival().getDelay() : stopTime.getDeparture().getDelay(); + timeInSeconds = getArrivalDepartureTimeInSeconds(timeInSeconds, scheduledTime, isArrival); + + eventTime = getDateForSecondsFromMidnight(eventReadTime, timeInSeconds); + logger.debug("Event Time : " + eventTime); + logger.debug("Time in seconds :" + timeInSeconds); + + } else if (tripUpdate.hasDelay()) { + + int timeInSeconds = tripUpdate.getDelay(); + timeInSeconds = getArrivalDepartureTimeInSeconds(timeInSeconds, scheduledTime, isArrival); + + eventTime = getDateForSecondsFromMidnight(eventReadTime, timeInSeconds); + logger.debug("Event Time : " + eventTime); + logger.debug("Time in seconds :" + timeInSeconds); + } + else if(stopTimeDelay != null){ + int timeInSeconds = stopTimeDelay; + timeInSeconds = getArrivalDepartureTimeInSeconds(timeInSeconds, scheduledTime, isArrival); + + eventTime = getDateForSecondsFromMidnight(eventReadTime, timeInSeconds); + logger.debug("Event Time : " + eventTime); + logger.debug("Time in seconds :" + timeInSeconds); + } + + return eventTime; + } + + private Integer getArrivalDepartureTimeInSeconds(int timeInSeconds, ScheduleTime scheduledTime, boolean isArrival){ + if(isArrival){ + if (scheduledTime.getArrivalTime() != null) { + timeInSeconds = timeInSeconds + scheduledTime.getArrivalTime(); + } + else if (scheduledTime.getDepartureTime() != null) { + timeInSeconds = timeInSeconds + scheduledTime.getDepartureTime(); + } + } else { + if (scheduledTime.getDepartureTime() != null) { + timeInSeconds = timeInSeconds + scheduledTime.getDepartureTime(); + } + else if (scheduledTime.getArrivalTime() != null) { + timeInSeconds = timeInSeconds + scheduledTime.getArrivalTime(); + } + } + return timeInSeconds; + } + + Date getDateForSecondsFromMidnight(Date epochTime, int secondsFromMidnight) { + + Calendar calendar = Calendar.getInstance(); + calendar.setTime(epochTime); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + + calendar.add(Calendar.SECOND, secondsFromMidnight); + + return calendar.getTime(); + + } + + public int getSecondsIntoDay(long epochTime, ZoneId zoneId) { + LocalTime localTime = Instant.ofEpochMilli(epochTime).atZone(zoneId).toLocalTime(); + return localTime.toSecondOfDay(); + } + + private String getTripId(DbConfig config, GtfsRealtime.TripDescriptor tripDescriptor) { + String tripId = tripDescriptor.getTripId(); + + if(config.getServiceIdSuffix()){ + Trip trip = BlockAssigner.getInstance().getTripWithServiceIdSuffix(config,tripId); + if(trip != null) { + tripId = trip.getId(); + } else { + logger.warn("No matching trip found for tripId {}", tripId); + } + } + return tripId; + } + + private boolean stopTimeMatchesStopPath(StopTimeUpdate stopTimeUpdate, StopPath stopPath) { + if (stopTimeUpdate.hasStopSequence()) { + if (stopTimeUpdate.getStopSequence() == stopPath.getGtfsStopSeq()) + return true; + else + return false; + } else if (stopTimeUpdate.hasStopId()) { + if (stopTimeUpdate.getStopId().equals(stopPath.getStopId())) + return true; + else + return false; + } else + return false; + } + + private int getStopPathIndex(StopTimeUpdate tripUpdateStopTime, Trip gtfsTrip){ + int stopPathIndex = -1; + + // Try to get valid stopPathIndex from StopTime + if (tripUpdateStopTime.hasStopSequence()) { + try { + stopPathIndex = getStopPathIndexForStopSequence(gtfsTrip, tripUpdateStopTime.getStopSequence()); + } catch (Exception e) { + logger.error("Not valid stop sequence {} for trip {}.", tripUpdateStopTime.getStopSequence(), + gtfsTrip.getId()); + } + } + else if (tripUpdateStopTime.hasStopId() && !tripUpdateStopTime.hasStopSequence()) { + StopPath stopPath = gtfsTrip.getStopPath(tripUpdateStopTime.getStopId()); + if (stopPath != null) + logger.debug(stopPath.toString()); + + stopPathIndex = getStopPathIndexForStopPath(gtfsTrip, stopPath); + + } + else { + logger.warn("StopTimeUpdate must have stop id or stop sequence set:" + tripUpdateStopTime.toString()); + } + return stopPathIndex; + } + + private int getStopPathIndexForStopSequence(Trip gtfsTrip, int gtfsStopSequence) { + int index = 0; + for (StopPath path : gtfsTrip.getStopPaths()) { + if (path.getGtfsStopSeq() == gtfsStopSequence) { + return index; + } + index++; + } + return -1; + } + + private int getStopPathIndexForStopPath(Trip gtfsTrip, StopPath stopPath) { + + if (gtfsTrip != null && stopPath != null) { + for (int i = 0; i < gtfsTrip.getNumberStopPaths(); i++) { + if (gtfsTrip.getStopPath(i).getStopPathId().equals(stopPath.getStopPathId())) { + return i; + } + } + } + return -1; + } + + /** + + * Processes both the internal and external predictions + * + * @param routesAndStops + * @param predictionsReadTime + * For keeping track of when the predictions read in. Used for + * determining length of predictions. Should be the same for all + * predictions read in during a polling cycle even if the + * predictions are read at slightly different times. By using the + * same time can easily see from data in db which internal and + * external predictions are associated with each other. + */ + @Override + protected void getAndProcessData(List routesAndStops, Date predictionsReadTime) { + // Process internal predictions + + // super.getAndProcessData(routesAndStops, predictionsReadTime); + + logger.info("Calling GTFSRealtimePredictionAccuracyModule." + "getAndProcessData()"); + + // Get data for all items in the GTFS-RT trip updates feed + FeedMessage feed = getExternalPredictions(); + DbConfig dbConfig = Core.getInstance().getDbConfig(); + + processExternalPredictions(feed, dbConfig); + + } + + class PredictionReadTimeKey { + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + getOuterType().hashCode(); + result = prime * result + (int) (predictedTime ^ (predictedTime >>> 32)); + 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; + PredictionReadTimeKey other = (PredictionReadTimeKey) obj; + if (!getOuterType().equals(other.getOuterType())) + return false; + if (predictedTime != other.predictedTime) + return false; + 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 PredictionReadTimeKey(String stopId, String vehicleId, long predictedTime) { + super(); + this.stopId = stopId; + this.vehicleId = vehicleId; + this.predictedTime = predictedTime; + } + + String stopId; + String vehicleId; + long predictedTime; + + private GTFSRealtimePredictionAccuracyModule getOuterType() { + return GTFSRealtimePredictionAccuracyModule.this; + } + } + + static HashMap readTimesMap = new HashMap(); + + +} diff --git a/transitclock/src/main/java/org/transitclock/core/predAccuracy/gtfsrt/GTFSRealtimeTranslator.java b/transitclock/src/main/java/org/transitclock/core/predAccuracy/gtfsrt/GTFSRealtimeTranslator.java new file mode 100644 index 000000000..3b446a9f5 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predAccuracy/gtfsrt/GTFSRealtimeTranslator.java @@ -0,0 +1,11 @@ +package org.transitclock.core.predAccuracy.gtfsrt; + +import java.util.Date; + +import com.google.transit.realtime.GtfsRealtime.FeedHeader; + +public interface GTFSRealtimeTranslator { + String parseStopId(String inputStopId); + + Date parseFeedHeaderTimestamp(FeedHeader header); +} diff --git a/transitclock/src/main/java/org/transitclock/core/predAccuracy/gtfsrt/KCMTranslator.java b/transitclock/src/main/java/org/transitclock/core/predAccuracy/gtfsrt/KCMTranslator.java new file mode 100644 index 000000000..7a4ae9915 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predAccuracy/gtfsrt/KCMTranslator.java @@ -0,0 +1,46 @@ +package org.transitclock.core.predAccuracy.gtfsrt; + +import java.util.Date; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.transit.realtime.GtfsRealtime.FeedHeader; + +/** + * King County Metro has a bug in there GTFS-RT feed that prepends + * digits to stopIds. Below is a quick-and-dirty method to strip them. + * + */ +public class KCMTranslator implements GTFSRealtimeTranslator { + + private static final Logger logger = LoggerFactory + .getLogger(KCMTranslator.class); + + @Override + public String parseStopId(String stopId) { + if (stopId.startsWith("100000")) + return stopId.substring("100000".length(), stopId.length()); + if (stopId.startsWith("10000")) + return stopId.substring("10000".length(), stopId.length()); + if (stopId.startsWith("1000")) + return stopId.substring("1000".length(), stopId.length()); + if (stopId.startsWith("100")) + return stopId.substring("100".length(), stopId.length()); + return stopId; + } + + @Override + public Date parseFeedHeaderTimestamp(FeedHeader header) { + // KCM feed is three hours behind! + Date feedDate = new Date(header.getTimestamp()*1000 + (3 * 60 * 60 * 1000)); + if (Math.abs(System.currentTimeMillis() - feedDate.getTime()) > 1 * 60 * 1000) { + // if the feed is reporting a date of more than a minute ago, we have significant clock skew + // this will ruin reports, so pretend the feed is up-to-date + logger.error("Feed has clock skew of {} mins, time {}", (System.currentTimeMillis() - feedDate.getTime())/60000, feedDate); + return new Date(); + } + return feedDate; + } + +} diff --git a/transitime/src/main/java/org/transitime/core/predAccuracy/package-info.java b/transitclock/src/main/java/org/transitclock/core/predAccuracy/package-info.java similarity index 97% rename from transitime/src/main/java/org/transitime/core/predAccuracy/package-info.java rename to transitclock/src/main/java/org/transitclock/core/predAccuracy/package-info.java index b0a7f13c5..1d3e46009 100644 --- a/transitime/src/main/java/org/transitime/core/predAccuracy/package-info.java +++ b/transitclock/src/main/java/org/transitclock/core/predAccuracy/package-info.java @@ -31,4 +31,4 @@ * @author SkiBu Smith * */ -package org.transitime.core.predAccuracy; \ No newline at end of file +package org.transitclock.core.predAccuracy; \ No newline at end of file diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/CascadingPredictionGenerator.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/CascadingPredictionGenerator.java new file mode 100644 index 000000000..609142557 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/CascadingPredictionGenerator.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.core.predictiongenerator; + +import org.transitclock.config.StringConfigValue; +import org.transitclock.core.Indices; +import org.transitclock.core.PredictionGeneratorDefaultImpl; +import org.transitclock.core.VehicleState; +import org.transitclock.db.structs.AvlReport; +import org.transitclock.utils.ClassInstantiator; + +import java.util.ArrayList; +import java.util.List; + +public class CascadingPredictionGenerator extends PredictionGeneratorDefaultImpl implements PredictionComponentElementsGenerator { + + private static StringConfigValue classNames = + new StringConfigValue("transitclock.core.predictionGeneratorClasses", + "org.transitclock.core.predictiongenerator.kalman.KalmanPredictionGeneratorImpl," + + "org.transitclock.core.predictiongenerator.average.HistoricalAveragePredictionGeneratorImpl," + + "org.transitclock.core.predictiongenerator.lastvehicle.LastVehiclePredictionGeneratorImpl", + "Specifies, in order, the names of the classes used for generating " + + "prediction data."); + + private PredictionComponentElementsGenerator defaultGenerator = new PredictionGeneratorDefaultImpl(); + + private List generators; + + public CascadingPredictionGenerator() { + generators = new ArrayList(); + for (String name : classNames.getValue().split(",")) { + generators.add(getInstance(name)); + } + } + + @Override + public long getTravelTimeForPath(Indices indices, AvlReport avlReport, VehicleState vehicleState) { + for (PredictionComponentElementsGenerator generator : generators) { + if (generator.hasDataForPath(indices, avlReport)) { + return generator.getTravelTimeForPath(indices, avlReport, vehicleState); + } + } + return defaultGenerator.getTravelTimeForPath(indices, avlReport,vehicleState); + } + + @Override + public long getStopTimeForPath(Indices indices, AvlReport avlReport, VehicleState vehicleState) { + for (PredictionComponentElementsGenerator generator : generators) { + if (generator.hasDataForPath(indices, avlReport)) { + return generator.getStopTimeForPath(indices, avlReport, vehicleState); + } + } + return defaultGenerator.getStopTimeForPath(indices, avlReport, vehicleState); + } + + private static PredictionComponentElementsGenerator getInstance(String name) { + return ClassInstantiator.instantiate(name, PredictionComponentElementsGenerator.class); + } +} diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/HistoricalPredictionLibrary.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/HistoricalPredictionLibrary.java new file mode 100644 index 000000000..e3aab5813 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/HistoricalPredictionLibrary.java @@ -0,0 +1,343 @@ +/* + * 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.predictiongenerator; + +import org.apache.commons.lang3.time.DateUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.applications.Core; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.core.Indices; +import org.transitclock.core.TravelTimeDetails; +import org.transitclock.core.VehicleState; +import org.transitclock.core.dataCache.*; +import org.transitclock.core.predictiongenerator.datafilter.TravelTimeDataFilter; +import org.transitclock.core.predictiongenerator.datafilter.TravelTimeFilterFactory; +import org.transitclock.db.structs.ArrivalDeparture; +import org.transitclock.db.structs.Block; +import org.transitclock.db.structs.PredictionEvent; +import org.transitclock.gtfs.DbConfig; +import org.transitclock.ipc.data.IpcArrivalDeparture; +import org.transitclock.monitoring.MonitoringService; + +import java.util.*; + +/** + * Commonly-used methods for PredictionGenerators that use historical cached data. + */ +public class HistoricalPredictionLibrary { + + private static MonitoringService monitoring = null; + + private static final IntegerConfigValue closestVehicleStopsAhead = new IntegerConfigValue( + "transitclock.prediction.closestvehiclestopsahead", new Integer(2), + "Num stops ahead a vehicle must be to be considers in the closest vehicle calculation"); + + private static final Logger logger = LoggerFactory.getLogger(HistoricalPredictionLibrary.class); + + public static TravelTimeDetails getLastVehicleTravelTime(VehicleState currentVehicleState, Indices indices) throws Exception { + + // NOTE: direction is relative to index, not vehicleState! + // We may be on a future trip in a reverse direction! + String currentDirection = indices.getTrip().getDirectionId(); + StopArrivalDepartureCacheKey nextStopKey = new StopArrivalDepartureCacheKey( + indices.getStopPath().getStopId(), + new Date(currentVehicleState.getMatch().getAvlTime())); + + /* TODO how do we handle the the first stop path. Where do we get the first stop id. */ + if(!indices.atBeginningOfTrip()) + { + String currentStopId = indices.getPreviousStopPath().getStopId(); + + StopArrivalDepartureCacheKey currentStopKey = new StopArrivalDepartureCacheKey(currentStopId, + new Date(currentVehicleState.getMatch().getAvlTime())); + + List currentStopList = StopArrivalDepartureCacheFactory.getInstance().getStopHistory(currentStopKey); + getMonitoring().rateMetric("PredictionStopADCurrentHit", (currentStopList==null||currentStopList.isEmpty()?false:true)); + + List nextStopList = StopArrivalDepartureCacheFactory.getInstance().getStopHistory(nextStopKey); + getMonitoring().rateMetric("PredictionStopADNextHit", (nextStopList==null||nextStopList.isEmpty()?false:true)); + + if (currentStopList != null && nextStopList != null) { + // lists are already sorted when put into cache. + for (IpcArrivalDeparture currentArrivalDeparture : currentStopList) { + + if(currentArrivalDeparture.isDeparture() + && !currentArrivalDeparture.getVehicleId().equals(currentVehicleState.getVehicleId()) + && (currentDirection==null || currentDirection.equals(currentArrivalDeparture.getDirectionId()))) + { + // this appears bound by percentage of service filled + getMonitoring().rateMetric("PredictionStopADVehicleHit", true); + IpcArrivalDeparture found; + // for this departure find the next arrival + if ((found = findMatchInList(nextStopList, currentArrivalDeparture)) != null) { + getMonitoring().rateMetric("PredictionStopADTTHit", true); + // NOTE: this constructor is departure, arrival!!!! + TravelTimeDetails travelTimeDetails=new TravelTimeDetails(currentArrivalDeparture, found); + if(travelTimeDetails.getTravelTime()>0) + { + getMonitoring().rateMetric("PredictionStopADHit", true); + getMonitoring().rateMetric("PredictionStopADValidTTHit", true); + return travelTimeDetails; + + }else + { + getMonitoring().rateMetric("PredictionStopADValidTTHit", false); + String description=found + " : " + currentArrivalDeparture; + PredictionEvent.create(currentVehicleState.getAvlReport(), currentVehicleState.getMatch(), PredictionEvent.TRAVELTIME_EXCEPTION, + description, + travelTimeDetails.getArrival().getStopId(), + travelTimeDetails.getDeparture().getStopId(), + travelTimeDetails.getArrival().getVehicleId(), + travelTimeDetails.getArrival().getTime(), + travelTimeDetails.getDeparture().getTime() + ); + getMonitoring().rateMetric("PredictionStopADHit", false); + return null; + } + }else + { + // match not found + getMonitoring().rateMetric("PredictionStopADHit", false); + getMonitoring().rateMetric("PredictionStopADTTHit", false); + return null; + } + } else { + logger.debug("vehicle/direction did not match {} != {}", + currentArrivalDeparture.getVehicleId(), currentVehicleState.getVehicleId()); + } + } + getMonitoring().rateMetric("PredictionStopADVehicleHit", false); + logger.debug("fall through"); + } else { + logger.debug("stop lists not populated"); + } + } + getMonitoring().rateMetric("PredictionStopADHit", false); + return null; + } + + public static Indices getLastVehicleIndices(VehicleState currentVehicleState, Indices indices) { + + StopArrivalDepartureCacheKey nextStopKey = new StopArrivalDepartureCacheKey( + indices.getStopPath().getStopId(), + new Date(currentVehicleState.getMatch().getAvlTime())); + + /* TODO how do we handle the the first stop path. Where do we get the first stop id. */ + if(!indices.atBeginningOfTrip()) + { + String currentStopId = indices.getPreviousStopPath().getStopId(); + + StopArrivalDepartureCacheKey currentStopKey = new StopArrivalDepartureCacheKey(currentStopId, + new Date(currentVehicleState.getMatch().getAvlTime())); + + List currentStopList = StopArrivalDepartureCacheFactory.getInstance().getStopHistory(currentStopKey); + + List nextStopList = StopArrivalDepartureCacheFactory.getInstance().getStopHistory(nextStopKey); + + if (currentStopList != null && nextStopList != null) { + // lists are already sorted when put into cache. + for (IpcArrivalDeparture currentArrivalDeparture : currentStopList) { + + if(currentArrivalDeparture.isDeparture() && !currentArrivalDeparture.getVehicleId().equals(currentVehicleState.getVehicleId()) + && (currentVehicleState.getTrip().getDirectionId()==null || currentVehicleState.getTrip().getDirectionId().equals(currentArrivalDeparture.getDirectionId()))) + { + IpcArrivalDeparture found; + + if ((found = findMatchInList(nextStopList, currentArrivalDeparture)) != null) { + if(found.getTime().getTime() - currentArrivalDeparture.getTime().getTime()>0) + { + Block currentBlock=null; + /* block is transient in arrival departure so when read from database need to get from dbconfig. */ + + DbConfig dbConfig = Core.getInstance().getDbConfig(); + + currentBlock=dbConfig.getBlock(currentArrivalDeparture.getServiceId(), currentArrivalDeparture.getBlockId()); + + if(currentBlock!=null) + return new Indices(currentBlock, currentArrivalDeparture.getTripIndex(), found.getStopPathIndex(), 0); + }else + { + // must be going backwards + return null; + } + }else + { + return null; // not matched in list + } + } + } + } + } + return null; + } + /* TODO could also make it a requirement that it is on the same route as the one we are generating prediction for */ + private static IpcArrivalDeparture findMatchInList(List nextStopList, + IpcArrivalDeparture currentArrivalDeparture) { + for (IpcArrivalDeparture nextStopArrivalDeparture : nextStopList) { + if (currentArrivalDeparture.getVehicleId().equals(nextStopArrivalDeparture.getVehicleId()) + && currentArrivalDeparture.getTripId().equals(nextStopArrivalDeparture.getTripId()) + && currentArrivalDeparture.isDeparture() && nextStopArrivalDeparture.isArrival() ) { + return nextStopArrivalDeparture; + } + } + return null; + } + + private static VehicleState getClosetVechicle(List vehiclesOnRoute, Indices indices, + VehicleState currentVehicleState) { + + Map> stopsByDirection = currentVehicleState.getTrip().getRoute() + .getOrderedStopsByDirection(); + + List routeStops = stopsByDirection.get(currentVehicleState.getTrip().getDirectionId()); + + Integer closest = 100; + + VehicleState result = null; + + for (VehicleState vehicle : vehiclesOnRoute) { + + Integer numAfter = numAfter(routeStops, vehicle.getMatch().getStopPath().getStopId(), + currentVehicleState.getMatch().getStopPath().getStopId()); + if (numAfter != null && numAfter > closestVehicleStopsAhead.getValue() && numAfter < closest) { + closest = numAfter; + result = vehicle; + } + } + return result; + } + + private static boolean isAfter(List stops, String stop1, String stop2) { + if (stops != null && stop1 != null && stop2 != null) { + if (stops.contains(stop1) && stops.contains(stop2)) { + if (stops.indexOf(stop1) > stops.indexOf(stop2)) + return true; + else + return false; + } + } + return false; + } + + private static Integer numAfter(List stops, String stop1, String stop2) { + if (stops != null && stop1 != null && stop2 != null) + if (stops.contains(stop1) && stops.contains(stop2)) + return stops.indexOf(stop1) - stops.indexOf(stop2); + + return null; + } + + public static List getHistoricalTravelTimes(TripDataHistoryCacheInterface cache, + String tripId, + String direction, + int stopPathIndex, + Date startDate, + Integer startTime, + int num_days_look_back, + int num_days) { + + List times = new ArrayList(); + List results = null; + int num_found = 0; + /* + * TODO This could be smarter about the dates it looks at by looking at + * which services use this trip and only 1ook on day service is + * running + */ + for (int i = 0; i < num_days_look_back && num_found < num_days; i++) { + + Date nearestDay = DateUtils.truncate(DateUtils.addDays(startDate, (i + 1) * -1), Calendar.DAY_OF_MONTH); + + TripKey tripKey = new TripKey(tripId, nearestDay, startTime); + + results = cache.getTripHistory(tripKey); + + if (results != null) { + + IpcArrivalDeparture arrival = getArrival(stopPathIndex, results); + + if(arrival!=null) + { + IpcArrivalDeparture departure = TripDataHistoryCacheFactory.getInstance().findPreviousDepartureEvent(results, arrival); + + if (arrival != null && departure != null) { + + TravelTimeDetails travelTimeDetails=new TravelTimeDetails(departure, arrival); + + if(travelTimeDetails.getTravelTime()!=-1) + { + TravelTimeDataFilter travelTimefilter = TravelTimeFilterFactory.getInstance(); + if(!travelTimefilter.filter(travelTimeDetails.getDeparture(),travelTimeDetails.getArrival())) + { + times.add(travelTimeDetails); + num_found++; + } + } else { + // invalid travel time + logger.debug("invalid travel time {}", travelTimeDetails.getTravelTime()); + } + } else { + //departure is null + logger.debug("departure is null for arrival {} ", arrival); + } + } else { + // arrival null + logger.debug("arrival is null for trip {}", tripKey); + } + } else { + // tripHistory null + logger.debug("history is empty for trip {}" + tripKey); + } + } + return times; + } + + private static IpcArrivalDeparture getArrival(int stopPathIndex, List results) + { + for(IpcArrivalDeparture result:results) + { + if(result.isArrival()&&result.getStopPathIndex()==stopPathIndex) + { + return result; + } + } + return null; + } + + private static long timeBetweenStops(ArrivalDeparture ad1, ArrivalDeparture ad2) { + + return Math.abs(ad2.getTime() - ad1.getTime()); + } + + public static Iterable emptyIfNull(Iterable iterable) { + return iterable == null ? Collections. emptyList() : iterable; + } + + /** + * lazy load Cloudwatch Monitoring service. + * @return + */ + protected static MonitoringService getMonitoring() { + if (monitoring == null) + monitoring = MonitoringService.getInstance(); + return monitoring; + } + + +} diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/PredictionComponentElementsGenerator.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/PredictionComponentElementsGenerator.java new file mode 100755 index 000000000..b42d2478a --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/PredictionComponentElementsGenerator.java @@ -0,0 +1,21 @@ +package org.transitclock.core.predictiongenerator; + + +import org.transitclock.core.HeadwayDetails; +import org.transitclock.core.Indices; +import org.transitclock.core.SpatialMatch; +import org.transitclock.core.VehicleState; +import org.transitclock.db.structs.AvlReport; +import org.transitclock.ipc.data.IpcPrediction; + +public interface PredictionComponentElementsGenerator { + /* this generates a prediction for travel time between stops */ + long getTravelTimeForPath(Indices indices, AvlReport avlReport, VehicleState vehicleState); + + long getStopTimeForPath(Indices indices, AvlReport avlReport, VehicleState vehicleState); + + long expectedTravelTimeFromMatchToEndOfStopPath(AvlReport avlReport, SpatialMatch match); + + boolean hasDataForPath(Indices indices, AvlReport avlReport); + +} \ No newline at end of file diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/bias/BiasAdjuster.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/bias/BiasAdjuster.java new file mode 100644 index 000000000..c69b35916 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/bias/BiasAdjuster.java @@ -0,0 +1,10 @@ +package org.transitclock.core.predictiongenerator.bias; + +/** + * @author scrudden + * Implement this to add bias adjustment if needed. + * + */ +public interface BiasAdjuster { + public long adjustPrediction(long prediction); +} diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/bias/BiasAdjusterFactory.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/bias/BiasAdjusterFactory.java new file mode 100644 index 000000000..aee9cacf9 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/bias/BiasAdjusterFactory.java @@ -0,0 +1,27 @@ +package org.transitclock.core.predictiongenerator.bias; + +import org.transitclock.config.StringConfigValue; +import org.transitclock.utils.ClassInstantiator; + +public class BiasAdjusterFactory { + private static BiasAdjuster singleton=null; + + // The name of the class to instantiate + private static StringConfigValue className = new StringConfigValue("transitclock.core.predictiongenerator.biasadjuster", + "org.transitclock.core.predictiongenerator.bias.ExponentialBiasAdjuster", + "Specifies the name of the class used to adjust the bias of a predction."); + + public static BiasAdjuster getInstance() + { + if(className!=null && className.getValue()!=null && className.getValue().length()>0) + { + if(singleton==null) + singleton=ClassInstantiator.instantiate(className.getValue(), BiasAdjuster.class); + return singleton; + + }else + { + return null; + } + } +} diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/bias/ExponentialBiasAdjuster.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/bias/ExponentialBiasAdjuster.java new file mode 100644 index 000000000..20cc80b3e --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/bias/ExponentialBiasAdjuster.java @@ -0,0 +1,83 @@ +package org.transitclock.core.predictiongenerator.bias; + +import org.transitclock.config.DoubleConfigValue; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.utils.Time; + +/** + * @author scrudden + * This will adjust a prediction based on a percentage which increases exponentially as the horizon gets bigger. + */ +public class ExponentialBiasAdjuster implements BiasAdjuster { + private double percentage=Double.NaN; + + private double number=Double.NaN; + + /* + * y=a(b^x)+c + * + * + */ + + private static DoubleConfigValue baseNumber = new DoubleConfigValue( + "org.transitclock.core.predictiongenerator.bias.exponential.b", 1.1, + "Base number to be raised to the power of the horizon minutes. y=a(b^x)+c."); + + private static DoubleConfigValue multiple = new DoubleConfigValue( + "org.transitclock.core.predictiongenerator.bias.exponential.a", 0.5, + "Multiple.y=a(b^x)+c."); + + private static DoubleConfigValue constant = new DoubleConfigValue( + "org.transitclock.core.predictiongenerator.bias.exponential.c", -0.5, + "Constant. y=a(b^x)+c."); + + private static IntegerConfigValue updown = new IntegerConfigValue( + "org.transitclock.core.predictiongenerator.bias.exponential.updown", -1, + "Is the adjustment up or down? Set +1 or -1."); + @Override + public long adjustPrediction(long prediction) { + + + double tothepower=(prediction/1000)/60; + percentage = ((Math.pow(number, tothepower))*multiple.getValue())-constant.getValue(); + + double new_prediction = prediction + (updown.getValue()*(((percentage/100)*prediction))); + return (long) new_prediction; + } + public ExponentialBiasAdjuster() { + super(); + this.number=baseNumber.getValue(); + } + public static void main(String [ ] args) + { + ExponentialBiasAdjuster adjuster=new ExponentialBiasAdjuster(); + long result = adjuster.adjustPrediction( 20*Time.MS_PER_MIN); + System.out.println("Percentage is :"+adjuster.getPercentage() +" giving a result to :"+Math.round(result/Time.MS_PER_SEC)); + + result=adjuster.adjustPrediction( 15*Time.MS_PER_MIN); + System.out.println("Percentage is :"+adjuster.getPercentage() +" giving a result to :"+Math.round(result/Time.MS_PER_SEC)); + + result=adjuster.adjustPrediction( 10*Time.MS_PER_MIN); + System.out.println("Percentage is :"+adjuster.getPercentage() +" giving a result to :"+Math.round(result/Time.MS_PER_SEC)); + + result=adjuster.adjustPrediction( 5*Time.MS_PER_MIN); + System.out.println("Percentage is :"+adjuster.getPercentage() +" giving a result to :"+Math.round(result/Time.MS_PER_SEC)); + + result=adjuster.adjustPrediction( 1*Time.MS_PER_MIN); + System.out.println("Percentage is :"+adjuster.getPercentage() +" giving a result to :"+Math.round(result/Time.MS_PER_SEC)); + + } + public double getPercentage() { + return percentage; + } + + + public double getNumber() { + return number; + } + @Override + public String toString() { + return "ExponentialBiasAdjuster [percentage=" + percentage + ", number=" + number + "]"; + } + +} diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/bias/LinearBiasAdjuster.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/bias/LinearBiasAdjuster.java new file mode 100644 index 000000000..cc70dee3a --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/bias/LinearBiasAdjuster.java @@ -0,0 +1,82 @@ +package org.transitclock.core.predictiongenerator.bias; + +import org.transitclock.config.DoubleConfigValue; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.utils.Time; + +/** + * @author scrudden + * + * This will adjust a prediction based on a percentage which increases linearly as the horizon gets bigger. + * + * The rate of increase of the percentage can be set using the constructor. + * + */ +public class LinearBiasAdjuster implements BiasAdjuster { + + private double rate=-Double.NaN; + + + private static DoubleConfigValue rateChangePercentage = new DoubleConfigValue( + "org.transitclock.core.predictiongenerator.bias.linear.rate", 0.0006, + "Rate at which percentage adjustment changes with horizon."); + private static IntegerConfigValue updown = new IntegerConfigValue( + "org.transitclock.core.predictiongenerator.bias.linear.updown", -1, + "Is the adjustment up or down? Set +1 or -1."); + + public LinearBiasAdjuster() { + super(); + this.rate=rateChangePercentage.getValue(); + } + public LinearBiasAdjuster(double rateChangePercentage) { + super(); + this.rate = rateChangePercentage; + } + private double percentage=Double.NaN; + + /* going to adjust by a larger percentage as horizon gets bigger.*/ + + @Override + public long adjustPrediction(long prediction) { + + + percentage = (prediction/100)*rate; + + double new_prediction = prediction+(((percentage/100)*prediction)*updown.getValue()); + return (long) new_prediction; + } + public double getRate() { + return rate; + } + + @Override + public String toString() { + return "LinearBiasAdjuster [rate=" + rate + ", percentage=" + percentage + "]"; + } + public double getPercentage() { + return percentage; + } + + public static void main(String [ ] args) + { + LinearBiasAdjuster adjuster=new LinearBiasAdjuster(0.0006); + long result = adjuster.adjustPrediction( 20*Time.MS_PER_MIN); + System.out.println("Percentage is :"+adjuster.getPercentage() +" giving a result to :"+Math.round(result/Time.MS_PER_SEC)); + + result=adjuster.adjustPrediction( 15*Time.MS_PER_MIN); + System.out.println("Percentage is :"+adjuster.getPercentage() +" giving a result to :"+Math.round(result/Time.MS_PER_SEC)); + + result=adjuster.adjustPrediction( 10*Time.MS_PER_MIN); + System.out.println("Percentage is :"+adjuster.getPercentage() +" giving a result to :"+Math.round(result/Time.MS_PER_SEC)); + + result=adjuster.adjustPrediction( 5*Time.MS_PER_MIN); + System.out.println("Percentage is :"+adjuster.getPercentage() +" giving a result to :"+Math.round(result/Time.MS_PER_SEC)); + + result=adjuster.adjustPrediction( 1*Time.MS_PER_MIN); + System.out.println("Percentage is :"+adjuster.getPercentage() +" giving a result to :"+Math.round(result/Time.MS_PER_SEC)); + + } + + + +} diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/datafilter/DwellTimeDataFilter.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/datafilter/DwellTimeDataFilter.java new file mode 100644 index 000000000..63cfe698d --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/datafilter/DwellTimeDataFilter.java @@ -0,0 +1,14 @@ +package org.transitclock.core.predictiongenerator.datafilter; + +import org.transitclock.ipc.data.IpcArrivalDeparture; +/** + * + * @author scrudden + * Interface to implement to filter out unwanted dwell time data. + */ + +public interface DwellTimeDataFilter { + + public boolean filter(IpcArrivalDeparture arrival, IpcArrivalDeparture departure); + +} diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/datafilter/DwellTimeDataFilterImpl.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/datafilter/DwellTimeDataFilterImpl.java new file mode 100644 index 000000000..e0df34bf0 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/datafilter/DwellTimeDataFilterImpl.java @@ -0,0 +1,62 @@ +package org.transitclock.core.predictiongenerator.datafilter; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.config.LongConfigValue; + +import org.transitclock.ipc.data.IpcArrivalDeparture; +import org.transitclock.utils.Time; +/** + * + * @author scrudden + * Filter by + * schedule adherence min and max + * dwell tiime min and max + * + */ +public class DwellTimeDataFilterImpl implements DwellTimeDataFilter { + private static LongConfigValue maxDwellTimeAllowedInModel = new LongConfigValue( + "transitclock.prediction.dwell.maxDwellTimeAllowedInModel", (long) (2 * Time.MS_PER_MIN), + "Max dwell time to be considered in algotithm."); + private static LongConfigValue minDwellTimeAllowedInModel = new LongConfigValue( + "transitclock.prediction.dwell.minDwellTimeAllowedInModel", (long) 0, + "Min dwell time to be considered in algotithm."); + private static IntegerConfigValue minSceheduleAdherence = new IntegerConfigValue( + "transitclock.prediction.dwell.minSceheduleAdherence", (int) (10 * Time.SEC_PER_MIN), + "If schedule adherence of vehicle is outside this then not considerd in dwell algorithm."); + private static IntegerConfigValue maxSceheduleAdherence = new IntegerConfigValue( + "transitclock.prediction.dwell.maxSceheduleAdherence", (int) (10 * Time.SEC_PER_MIN), + "If schedule adherence of vehicle is outside this then not considerd in dwell algorithm."); + private static final Logger logger = LoggerFactory.getLogger(DwellTimeDataFilterImpl.class); + + @Override + public boolean filter(IpcArrivalDeparture arrival, IpcArrivalDeparture departure) { + if (arrival != null && departure != null) { + long dwelltime = departure.getTime().getTime() - arrival.getTime().getTime(); + + if (departure.getScheduledAdherence() != null && departure.getScheduledAdherence() + .isWithinBounds(minSceheduleAdherence.getValue(), maxSceheduleAdherence.getValue())) { + // TODO Arrival schedule adherence appears not to be set much. So + // only stop if set and outside range. + if (arrival.getScheduledAdherence() == null || arrival.getScheduledAdherence() + .isWithinBounds(minSceheduleAdherence.getValue(), maxSceheduleAdherence.getValue())) { + if (dwelltime < maxDwellTimeAllowedInModel.getValue() + && dwelltime > minDwellTimeAllowedInModel.getValue()) { + return false; + } else { + logger.warn("Dwell time {} outside allowable range for {}.", dwelltime, departure); + } + } else { + logger.warn("Schedule adherence outside allowable range. " + arrival); + } + } else { + logger.warn("Schedule adherence outside allowable range. " + departure); + } + } else { + logger.warn("Arrival and/or departure not set."); + } + return true; + } + +} diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/datafilter/DwellTimeFilterFactory.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/datafilter/DwellTimeFilterFactory.java new file mode 100644 index 000000000..bfd275a18 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/datafilter/DwellTimeFilterFactory.java @@ -0,0 +1,29 @@ +package org.transitclock.core.predictiongenerator.datafilter; + +import org.transitclock.config.StringConfigValue; +import org.transitclock.utils.ClassInstantiator; + +public class DwellTimeFilterFactory { + + /** + * + * @author scrudden Returns the filter that is used to exclude bad dwell time + * data. + * + */ + + private static DwellTimeDataFilter singleton = null; + + // The name of the class to instantiate + private static StringConfigValue className = new StringConfigValue("transitclock.core.predictiongenerator.datafilter.dwelltime", + "org.transitclock.core.predictiongenerator.datafilter.DwellTimeDataFilterImpl", + "Specifies the name of the class used to filter dwell times."); + + public static DwellTimeDataFilter getInstance() { + + if(singleton==null) + singleton=ClassInstantiator.instantiate(className.getValue(), DwellTimeDataFilter.class); + return singleton; + } + +} diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/datafilter/TravelTimeDataFilter.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/datafilter/TravelTimeDataFilter.java new file mode 100644 index 000000000..67a9d4ea3 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/datafilter/TravelTimeDataFilter.java @@ -0,0 +1,11 @@ +package org.transitclock.core.predictiongenerator.datafilter; + +import org.transitclock.ipc.data.IpcArrivalDeparture; +/** + * + * @author scrudden + * Interface to implement to filter out unwanted travel time data. + */ +public interface TravelTimeDataFilter { + public boolean filter(IpcArrivalDeparture departure, IpcArrivalDeparture arrival); +} diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/datafilter/TravelTimeDataFilterImpl.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/datafilter/TravelTimeDataFilterImpl.java new file mode 100644 index 000000000..6b3c2e98d --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/datafilter/TravelTimeDataFilterImpl.java @@ -0,0 +1,64 @@ +package org.transitclock.core.predictiongenerator.datafilter; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.config.LongConfigValue; +import org.transitclock.db.structs.PredictionEvent; +import org.transitclock.ipc.data.IpcArrivalDeparture; +import org.transitclock.utils.Time; +/** + * + * @author scrudden + * Filter by + * schedule adherence min and max + * travel time min and max + */ + +public class TravelTimeDataFilterImpl implements TravelTimeDataFilter { + + private static LongConfigValue maxTravelTimeAllowedInModel = new LongConfigValue( + "transitclock.prediction.travel.maxTravelTimeAllowedInModel", (long) (20 * Time.MS_PER_MIN), + "Max travel time to be considered in algorithm. Milliseconds."); + private static LongConfigValue minTravelTimeAllowedInModel = new LongConfigValue( + "transitclock.prediction.travel.minTravelTimeAllowedInModel", (long) 100, + "Min travel time to be considered in algorithm. Milliseconds."); + private static IntegerConfigValue minSceheduleAdherence = new IntegerConfigValue( + "transitclock.prediction.travel.minSceheduleAdherence", (int) (10 * Time.SEC_PER_MIN), + "If schedule adherence of vehicle is outside this then not considerd in travel time algorithm."); + private static IntegerConfigValue maxSceheduleAdherence = new IntegerConfigValue( + "transitclock.prediction.travel.maxSceheduleAdherence", (int) (10 * Time.SEC_PER_MIN), + "If schedule adherence of vehicle is outside this then not considerd in travel time algorithm."); + + private static final Logger logger = LoggerFactory.getLogger(DwellTimeDataFilterImpl.class); + + @Override + public boolean filter(IpcArrivalDeparture departure, IpcArrivalDeparture arrival) { + if (arrival != null && departure != null) { + long traveltime = arrival .getTime().getTime() - departure.getTime().getTime(); + + if (departure.getScheduledAdherence() == null || departure.getScheduledAdherence() + .isWithinBounds(minSceheduleAdherence.getValue(), maxSceheduleAdherence.getValue())) { + if (arrival.getScheduledAdherence() == null || arrival.getScheduledAdherence() + .isWithinBounds(minSceheduleAdherence.getValue(), maxSceheduleAdherence.getValue())) { + if (traveltime < maxTravelTimeAllowedInModel.getValue() + && traveltime > minTravelTimeAllowedInModel.getValue()) { + return false; + + } else { + logger.warn("Travel time {} outside allowable range for {} to {}.", traveltime, departure, arrival); + } + } else { + logger.warn("Schedule adherence outside allowable range. " + arrival); + } + } else { + logger.warn("Schedule adherence outside allowable range. " + departure); + } + } else { + logger.warn("Arrival and/or departure not set."); + } + + return true; + } + +} diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/datafilter/TravelTimeFilterFactory.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/datafilter/TravelTimeFilterFactory.java new file mode 100644 index 000000000..7c97e8204 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/datafilter/TravelTimeFilterFactory.java @@ -0,0 +1,30 @@ +package org.transitclock.core.predictiongenerator.datafilter; + +import org.transitclock.config.StringConfigValue; +import org.transitclock.utils.ClassInstantiator; + +public class TravelTimeFilterFactory { + + /** + * + * @author scrudden + * Returns the filter that is used to exclude bad travel time data. + * + */ + + private static TravelTimeDataFilter singleton = null; + + // The name of the class to instantiate + private static StringConfigValue className = new StringConfigValue("transitclock.core.predictiongenerator.datafilter.traveltime", + "org.transitclock.core.predictiongenerator.datafilter.TravelTimeDataFilterImpl", + "Specifies the name of the class used to filter travel times."); + + public static TravelTimeDataFilter getInstance() { + + if (singleton == null) { + singleton=ClassInstantiator.instantiate(className.getValue(), TravelTimeDataFilter.class); + } + return singleton; + } + +} diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/frequency/dwell/rls/DwellTimePredictionGeneratorImpl.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/frequency/dwell/rls/DwellTimePredictionGeneratorImpl.java new file mode 100644 index 000000000..5c829b3af --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/frequency/dwell/rls/DwellTimePredictionGeneratorImpl.java @@ -0,0 +1,92 @@ +package org.transitclock.core.predictiongenerator.frequency.dwell.rls; + +import java.util.Date; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.core.Indices; +import org.transitclock.core.VehicleState; +import org.transitclock.core.dataCache.DwellTimeModelCacheFactory; +import org.transitclock.core.dataCache.StopPathCacheKey; +import org.transitclock.core.dataCache.frequency.FrequencyBasedHistoricalAverageCache; +import org.transitclock.core.predictiongenerator.frequency.traveltime.kalman.KalmanPredictionGeneratorImpl; +import org.transitclock.db.structs.AvlReport; +import org.transitclock.db.structs.Headway; + +/** + * @author Sean Og Crudden + * + * This is an experiment to see if headway can be used to better predict dwell time. Most of what + * I have read tells me it can but in conjunction with APC data and estimation of demand at stops. + * + * This is for frequency based services. + * + * + * + */ +public class DwellTimePredictionGeneratorImpl extends KalmanPredictionGeneratorImpl { + + private static final Logger logger = LoggerFactory.getLogger(DwellTimePredictionGeneratorImpl.class); + + @Override + public long getStopTimeForPath(Indices indices, AvlReport avlReport, VehicleState vehicleState) { + Long result=null; + try { + Headway headway = vehicleState.getHeadway(); + + if(headway!=null) + { + logger.debug("Headway at {} based on avl {} is {}.",indices, avlReport, headway); + + /* Change approach to use a RLS model. + */ + if(super.getStopTimeForPath(indices, avlReport, vehicleState)>0) + { + // TODO Would be more correct to use the start time of the trip. + Integer time=FrequencyBasedHistoricalAverageCache.secondsFromMidnight(new Date(avlReport.getTime()),2); + + time=FrequencyBasedHistoricalAverageCache.round(time, FrequencyBasedHistoricalAverageCache.getCacheIncrementsForFrequencyService()); + + StopPathCacheKey cacheKey=new StopPathCacheKey(indices.getTrip().getId(), indices.getStopPathIndex(), false, new Long(time)); + + result = DwellTimeModelCacheFactory.getInstance().predictDwellTime(cacheKey, headway); + + if(result==null) + { + logger.debug("Using scheduled value for dwell time as no RLS data available for {}.", indices); + result = super.getStopTimeForPath(indices, avlReport, vehicleState); + } + + + /* should never have a negative dwell time */ + if(result<0) + { + logger.debug("Predicted negative dwell time {} for {}.", result, indices); + result=0L; + } + + }else + { + logger.debug("Scheduled dwell time is less than 0 for {}.", indices); + result = super.getStopTimeForPath(indices, avlReport, vehicleState); + } + + + } + else + { + result = super.getStopTimeForPath(indices, avlReport, vehicleState); + logger.debug("Using dwell time {} for {} instead of {}. No headway.",result,indices, super.getStopTimeForPath(indices ,avlReport, vehicleState)); + } + + } catch (Exception e) { + + logger.error(e.getMessage(),e); + e.printStackTrace(); + + } + + return result; + } + +} diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/frequency/traveltime/average/HistoricalAveragePredictionGeneratorImpl.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/frequency/traveltime/average/HistoricalAveragePredictionGeneratorImpl.java new file mode 100755 index 000000000..dd853c2de --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/frequency/traveltime/average/HistoricalAveragePredictionGeneratorImpl.java @@ -0,0 +1,141 @@ +package org.transitclock.core.predictiongenerator.frequency.traveltime.average; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.applications.Core; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.core.Indices; +import org.transitclock.core.SpatialMatch; +import org.transitclock.core.VehicleState; +import org.transitclock.core.dataCache.HistoricalAverage; +import org.transitclock.core.dataCache.StopPathCacheKey; +import org.transitclock.core.dataCache.StopPathPredictionCacheFactory; +import org.transitclock.core.dataCache.frequency.FrequencyBasedHistoricalAverageCache; +import org.transitclock.core.predictiongenerator.PredictionComponentElementsGenerator; +import org.transitclock.core.predictiongenerator.lastvehicle.LastVehiclePredictionGeneratorImpl; +import org.transitclock.db.structs.AvlReport; +import org.transitclock.db.structs.PredictionForStopPath; + +import java.util.Date; + +/** + * @author Sean Óg Crudden + * This provides a prediction based on the average of historical data for frequency based services. + * The average will be based on a trip id and a start time. The average will be based on time segments and previous trips during this time segment on previous days.. Each segment will be the duration of a single trip. + * + */ +public class HistoricalAveragePredictionGeneratorImpl extends +LastVehiclePredictionGeneratorImpl implements PredictionComponentElementsGenerator { + private String alternative="LastVehiclePredictionGeneratorImpl"; + + private static final IntegerConfigValue minDays = new IntegerConfigValue( + "transitclock.prediction.data.average.mindays", + new Integer(1), + "Min number of days trip data that needs to be available before historical average prediciton is used instead of default transiTime prediction."); + + + private static final Logger logger = LoggerFactory + .getLogger(HistoricalAveragePredictionGeneratorImpl.class); + + /* (non-Javadoc) + * @see org.transitclock.core.predictiongenerator.KalmanPredictionGeneratorImpl#getTravelTimeForPath(org.transitclock.core.Indices, org.transitclock.db.structs.AvlReport) + */ + @Override + public long getTravelTimeForPath(Indices indices, AvlReport avlReport, VehicleState vehicleState) { + + /* + * if we have enough data start using historical average otherwise + * revert to default. This does not mean that this method of + * prediction is better than the default. + */ + if(vehicleState.getTripStartTime(vehicleState.getTripCounter())!=null) + { + + Integer time=FrequencyBasedHistoricalAverageCache.secondsFromMidnight(avlReport.getDate(),2); + + /* this is what gets the trip from the buckets */ + time=FrequencyBasedHistoricalAverageCache.round(time, FrequencyBasedHistoricalAverageCache.getCacheIncrementsForFrequencyService()); + + StopPathCacheKey historicalAverageCacheKey=new StopPathCacheKey(indices.getTrip().getId(), indices.getStopPathIndex(), true, time.longValue()); + + HistoricalAverage average = FrequencyBasedHistoricalAverageCache.getInstance().getAverage(historicalAverageCacheKey); + + if(average!=null && average.getCount()>=minDays.getValue()) + { + if(storeTravelTimeStopPathPredictions.getValue()) + { + PredictionForStopPath predictionForStopPath=new PredictionForStopPath(vehicleState.getVehicleId(), new Date(Core.getInstance().getSystemTime()), average.getAverage(), indices.getTrip().getId(), indices.getStopPathIndex(), "HISTORICAL AVERAGE", true, time); + Core.getInstance().getDbLogger().add(predictionForStopPath); + StopPathPredictionCacheFactory.getInstance().putPrediction(predictionForStopPath); + } + + logger.debug("Using historical average algorithm for prediction : " +average.toString() + " for : " + indices.toString()); + //logger.debug("Instead of transitclock value : " + super.getTravelTimeForPath(indices, avlReport)); + return (long)average.getAverage(); + } + } + //logger.debug("No historical average found, generating prediction using lastvehicle algorithm: " + historicalAverageCacheKey.toString()); + /* default to parent method if not enough data. This will be based on schedule if UpdateTravelTimes has not been called. */ + return super.getTravelTimeForPath(indices, avlReport,vehicleState); + } + + @Override + public long expectedTravelTimeFromMatchToEndOfStopPath(AvlReport avlReport,SpatialMatch match) { + + Indices indices = match.getIndices(); + Integer time=FrequencyBasedHistoricalAverageCache.secondsFromMidnight(new Date(match.getAvlTime()),2); + + /* this is what gets the trip from the buckets */ + time=FrequencyBasedHistoricalAverageCache.round(time, FrequencyBasedHistoricalAverageCache.getCacheIncrementsForFrequencyService()); + + StopPathCacheKey historicalAverageCacheKey=new StopPathCacheKey(indices.getTrip().getId(), indices.getStopPathIndex(), true, time.longValue()); + + HistoricalAverage average = FrequencyBasedHistoricalAverageCache.getInstance().getAverage(historicalAverageCacheKey); + + if(average!=null && average.getCount()>=minDays.getValue()) + { + double fractionofstoppathlefttotravel=(match.getStopPath().getLength()-match.getDistanceAlongStopPath())/match.getStopPath().getLength(); + double value = (double)(average.getAverage() * fractionofstoppathlefttotravel); + if(storeTravelTimeStopPathPredictions.getValue()) + { + PredictionForStopPath predictionForStopPath=new PredictionForStopPath(avlReport.getVehicleId(), new Date(Core.getInstance().getSystemTime()), value, indices.getTrip().getId(), indices.getStopPathIndex(), "PARTIAL HISTORICAL AVERAGE", true, time); + Core.getInstance().getDbLogger().add(predictionForStopPath); + StopPathPredictionCacheFactory.getInstance().putPrediction(predictionForStopPath); + } + return (long)value; + }else + { + return super.expectedTravelTimeFromMatchToEndOfStopPath(avlReport, match); + } + } + + @Override + public long getStopTimeForPath(Indices indices, AvlReport avlReport, VehicleState vehicleState) { + + if(vehicleState.getTripStartTime(vehicleState.getTripCounter())!=null) + { + Integer time=FrequencyBasedHistoricalAverageCache.secondsFromMidnight(avlReport.getDate(),2); + + /* this is what gets the trip from the buckets */ + time=FrequencyBasedHistoricalAverageCache.round(time, FrequencyBasedHistoricalAverageCache.getCacheIncrementsForFrequencyService()); + + StopPathCacheKey historicalAverageCacheKey=new StopPathCacheKey(indices.getTrip().getId(), indices.getStopPathIndex(), false, time.longValue()); + + HistoricalAverage average = FrequencyBasedHistoricalAverageCache.getInstance().getAverage(historicalAverageCacheKey); + + if(average!=null && average.getCount()>=minDays.getValue()) + { + if(storeDwellTimeStopPathPredictions.getValue()) + { + PredictionForStopPath predictionForStopPath=new PredictionForStopPath(vehicleState.getVehicleId(), new Date(Core.getInstance().getSystemTime()), average.getAverage(), indices.getTrip().getId(), indices.getStopPathIndex(), "HISTORICAL AVERAGE", false, time); + Core.getInstance().getDbLogger().add(predictionForStopPath); + StopPathPredictionCacheFactory.getInstance().putPrediction(predictionForStopPath); + } + + logger.debug("Using historical average alogrithm for dwell time : "+average.toString() + " for : " + indices.toString()); + return (long)average.getAverage(); + } + } + return super.getStopTimeForPath(indices, avlReport, vehicleState); + } +} diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/frequency/traveltime/kalman/KalmanPredictionGeneratorImpl.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/frequency/traveltime/kalman/KalmanPredictionGeneratorImpl.java new file mode 100755 index 000000000..c31cb1e67 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/frequency/traveltime/kalman/KalmanPredictionGeneratorImpl.java @@ -0,0 +1,340 @@ +package org.transitclock.core.predictiongenerator.frequency.traveltime.kalman; + +import org.apache.commons.lang3.time.DateUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.applications.Core; +import org.transitclock.config.BooleanConfigValue; +import org.transitclock.config.DoubleConfigValue; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.core.Indices; +import org.transitclock.core.SpatialMatch; +import org.transitclock.core.TravelTimeDetails; +import org.transitclock.core.VehicleState; +import org.transitclock.core.dataCache.*; +import org.transitclock.core.dataCache.frequency.FrequencyBasedHistoricalAverageCache; +import org.transitclock.core.predictiongenerator.HistoricalPredictionLibrary; +import org.transitclock.core.predictiongenerator.PredictionComponentElementsGenerator; +import org.transitclock.core.predictiongenerator.frequency.traveltime.average.HistoricalAveragePredictionGeneratorImpl; +import org.transitclock.core.predictiongenerator.kalman.*; +import org.transitclock.db.structs.AvlReport; +import org.transitclock.db.structs.PredictionEvent; +import org.transitclock.db.structs.PredictionForStopPath; + +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +/** + * This is a prediction generator that uses a Kalman + * filter to provide predictions for a frequency based service. + * + * @see KalmanPrediction for the research paper and description of the terms + * @author Sean Óg Crudden + * @author sheldonabrown + */ +public class KalmanPredictionGeneratorImpl extends HistoricalAveragePredictionGeneratorImpl + implements PredictionComponentElementsGenerator { + + + private String alternative="LastVehiclePredictionGeneratorImpl"; + + private static final IntegerConfigValue minKalmanDays = new IntegerConfigValue( + "transitclock.prediction.data.kalman.mindays", new Integer(3), + "Min number of days trip data that needs to be available before Kalman prediciton is used instead of default transiTime prediction."); + + private static final IntegerConfigValue maxKalmanDays = new IntegerConfigValue( + "transitclock.prediction.data.kalman.maxdays", new Integer(3), + "Max number of historical days trips to include in Kalman prediction calculation."); + + private static final IntegerConfigValue maxKalmanDaysToSearch = new IntegerConfigValue( + "transitclock.prediction.data.kalman.maxdaystosearch", new Integer(30), + "Max number of days to look back for data. This will also be effected by how old the data in the cache is."); + + private static final DoubleConfigValue initialErrorValue = new DoubleConfigValue( + "transitclock.prediction.data.kalman.initialerrorvalue", new Double(100), + "Initial Kalman error value to use to start filter."); + + /* May be better to use the default implementation as it splits things down into segments. */ + private static final BooleanConfigValue useKalmanForPartialStopPaths = new BooleanConfigValue ( + "transitclock.prediction.data.kalman.usekalmanforpartialstoppaths", new Boolean(true), + "Will use Kalman prediction to get to first stop of prediction." + ); + + private static final IntegerConfigValue percentagePredictionMethodDifferenceEventLog =new IntegerConfigValue( + "transitclock.prediction.data.kalman.percentagePredictionMethodDifference", new Integer(50), + "If the difference in prediction method estimates is greater than this percentage log a Vehicle Event"); + + private static final IntegerConfigValue tresholdForDifferenceEventLog=new IntegerConfigValue( + "transitclock.prediction.data.kalman.tresholdForDifferenceEventLog", new Integer(60000), + "This is the threshold in milliseconds that the difference has to be over before it will consider the percentage difference."); + + private static final Logger logger = LoggerFactory.getLogger(KalmanPredictionGeneratorImpl.class); + + /* + * return a prediction for the travel of the given vehicle. If enough data is present this + * will be a kalman weighted prediction, otherwise it will default to the basic prediction + * algorithm. + * + * @see + * org.transitclock.core.PredictionGeneratorDefaultImpl#getTravelTimeForPath + * (org.transitclock.core.Indices, org.transitclock.db.structs.AvlReport) + */ + @Override + public long getTravelTimeForPath(Indices indices, AvlReport avlReport, VehicleState currentVehicleState) { + + logger.debug("Calling frequency based Kalman prediction algorithm for : "+indices.toString()); + long alternatePrediction = super.getTravelTimeForPath(indices, avlReport, currentVehicleState); + + Integer time = FrequencyBasedHistoricalAverageCache.secondsFromMidnight(avlReport.getDate(),2); + + time = FrequencyBasedHistoricalAverageCache.round( + time, + FrequencyBasedHistoricalAverageCache.getCacheIncrementsForFrequencyService()); + + + try { + TravelTimeDetails headwayTravelTimes = getLastVehicleTravelTime(currentVehicleState, indices); + + /* + * The first vehicle of the day should use schedule or historic data to + * make prediction. Cannot use Kalman as yesterdays vehicle will have + * little to say about todays. + */ + if (headwayTravelTimes!=null) { + getMonitoring().rateMetric("PredictionKalmanHeadwayHit", true); + logger.debug("Kalman has last vehicle info for : " +indices.toString()+ " : "+headwayTravelTimes); + + List historicalTravelTimes = getHistoricalTravelTimes(avlReport, indices, currentVehicleState, time); + /* + * if we have enough data start using Kalman filter otherwise revert + * to extended class for prediction. + */ + if (historicalTravelTimes != null && historicalTravelTimes.size() >= minKalmanDays.getValue().intValue()) { + getMonitoring().rateMetric("PredictionKalmanHistoryHit", true); + getMonitoring().averageMetric("PredictionKalmanHistorySize", historicalTravelTimes.size()); + logger.debug("Generating Kalman prediction for : "+indices.toString()); + + try { + KalmanPrediction kalmanPrediction = new KalmanPrediction(); + LinkTravelTimes linkTravelTimes = generateLinkTravelTimes(avlReport, historicalTravelTimes, headwayTravelTimes, indices); + Indices headwayVehicleIndices = new Indices(headwayTravelTimes.getArrival()); + KalmanError headwayError = lastVehiclePredictionError(getKalmanErrorCache(), headwayVehicleIndices); + logger.debug("Using error value: " + headwayError +" found with vehicle id "+headwayTravelTimes.getArrival().getVehicleId()+ " from: "+new KalmanErrorCacheKey(headwayVehicleIndices).toString()); + + KalmanPredictionResult kalmanPredictionResult = kalmanPrediction.predict(linkTravelTimes.getLastVehicleSegment(), + linkTravelTimes.getHistoricalSegments(), + headwayError.getError()); + + long predictionTime = (long) kalmanPredictionResult.getResult(); + logger.debug("Setting Kalman error value: " + kalmanPredictionResult.getFilterError() + " for : "+ new KalmanErrorCacheKey(indices).toString()); + getKalmanErrorCache().putErrorValue(indices, kalmanPredictionResult.getFilterError()); + logPredictionEvent(avlReport, headwayTravelTimes, currentVehicleState, predictionTime, alternatePrediction); + + logger.debug("Using Kalman prediction: " + predictionTime + " instead of "+alternative+" prediction: " + + alternatePrediction +" for : " + indices.toString()); + + double percentageDifferecence = 100 * ((predictionTime - alternatePrediction) / (double)alternatePrediction); + + storePrediction(currentVehicleState, indices, predictionTime); + + // instrument kalman hit + getMonitoring().rateMetric("PredictionKalmanHit", true); + getMonitoring().sumMetric("PredictionGenerationKalman"); + return predictionTime; + + } catch (Exception e) { + logger.error(e.getMessage(), e); + } + } else { + getMonitoring().rateMetric("PredictionKalmanHistoryHit", false); + if (historicalTravelTimes == null) + getMonitoring().averageMetric("PredictionKalmanHistorySize", 0.0); + else + getMonitoring().averageMetric("PredictionKalmanHistorySize", historicalTravelTimes.size()); + } + } else { + getMonitoring().rateMetric("PredictionKalmanHeadwayHit", false); + } + } catch (Exception e) { + logger.error("kalman prediction error", e); + } + // instrument kalman miss + getMonitoring().rateMetric("PredictionKalmanHit", false); + return alternatePrediction; + } + + @Override + public long expectedTravelTimeFromMatchToEndOfStopPath(AvlReport avlReport, SpatialMatch match) { + + if(useKalmanForPartialStopPaths.getValue().booleanValue()) + { + VehicleStateManager vehicleStateManager = VehicleStateManager.getInstance(); + + VehicleState currentVehicleState = vehicleStateManager.getVehicleState(avlReport.getVehicleId()); + + long fulltime = this.getTravelTimeForPath(match.getIndices(), avlReport, currentVehicleState); + + double distanceAlongStopPath = match.getDistanceAlongStopPath(); + + double stopPathLength = + match.getStopPath().getLength(); + + long remainingtime = (long) (fulltime * ((stopPathLength-distanceAlongStopPath)/stopPathLength)); + + logger.debug("Using Kalman for first stop path {} with value {} instead of {}.", match.getIndices(), remainingtime, super.expectedTravelTimeFromMatchToEndOfStopPath(avlReport, match)); + + return remainingtime; + }else + { + return super.expectedTravelTimeFromMatchToEndOfStopPath(avlReport, match); + } + } + + + + private KalmanError lastVehiclePredictionError(ErrorCache cache, Indices indices) { + + KalmanError result = cache.getErrorValue(indices); + if(result==null) + { + logger.debug("Kalman Error value set to default: "+initialErrorValue.getValue() +" for key: "+new KalmanErrorCacheKey(indices).toString()); + result=new KalmanError(initialErrorValue.getValue()); + } + return result; + } + + @Override + public long getStopTimeForPath(Indices indices, AvlReport avlReport, VehicleState vehicleState) { + long result=super.getStopTimeForPath(indices, avlReport, vehicleState); + + return result; + + } + + /** + * protected for unit test overrides. + * @return + */ + protected TripDataHistoryCacheInterface getTripCache() { + return TripDataHistoryCacheFactory.getInstance(); + } + + /** + * protected for unit test overrides. + * @return + */ + protected ErrorCache getKalmanErrorCache() { + return ErrorCacheFactory.getInstance(); + } + + /** + * protected for unit test overrides. + * @return + */ + protected TravelTimeDetails getLastVehicleTravelTime(VehicleState currentVehicleState, Indices indices) + throws Exception { + return HistoricalPredictionLibrary.getLastVehicleTravelTime(currentVehicleState, indices); + } + + protected List getHistoricalTravelTimes(AvlReport avlReport, + Indices indices, + VehicleState currentVehicleState, + Integer time) { + Date nearestDay = DateUtils.truncate(avlReport.getDate(), Calendar.DAY_OF_MONTH); + List historicalTravelTimes = + HistoricalPredictionLibrary.getHistoricalTravelTimes( + getTripCache(), + currentVehicleState.getTrip().getId(), + currentVehicleState.getTrip().getDirectionId(), + indices.getStopPathIndex(), + nearestDay, + time, + maxKalmanDaysToSearch.getValue(), + maxKalmanDays.getValue()); + if(historicalTravelTimes!=null) { + logger.debug("Kalman has " +historicalTravelTimes.size()+ " historical values for : " +indices.toString()); + } + + return historicalTravelTimes; + } + + private void storePrediction(VehicleState vehicleState, Indices indices, long predictionTime) { + if(storeTravelTimeStopPathPredictions.getValue()) + { + PredictionForStopPath predictionForStopPath=new PredictionForStopPath( + vehicleState.getVehicleId(), + new Date(Core.getInstance().getSystemTime()), + new Double(new Long(predictionTime).intValue()), + indices.getTrip().getId(), + indices.getStopPathIndex(), + "KALMAN", + true, + null); + Core.getInstance().getDbLogger().add(predictionForStopPath); + StopPathPredictionCacheFactory.getInstance().putPrediction(predictionForStopPath); + } + + } + + private LinkTravelTimes generateLinkTravelTimes(AvlReport avlReport, + List lastDaysTimes, + TravelTimeDetails travelTimeDetails, + Indices indices) { + + Vehicle vehicle = new Vehicle(avlReport.getVehicleId()); + VehicleStopDetail originDetail = new VehicleStopDetail(null, 0, vehicle); + TripSegment[] historical_segments_k = new TripSegment[lastDaysTimes.size()]; + for (int i = 0; i < lastDaysTimes.size() && i < maxKalmanDays.getValue(); i++) { + logger.debug("Kalman is using historical value : "+lastDaysTimes.get(i) +" for : " + indices.toString()); + VehicleStopDetail destinationDetail = new VehicleStopDetail(null, lastDaysTimes.get(i).getTravelTime(), + vehicle); + // NOTE: schedule version inserts into array in reverse order + historical_segments_k[i] = new TripSegment(originDetail, destinationDetail); + } + VehicleStopDetail destinationDetail_0_k_1 = new VehicleStopDetail(null, travelTimeDetails.getTravelTime(), vehicle); + TripSegment ts_day_0_k_1 = new TripSegment(originDetail, destinationDetail_0_k_1); + TripSegment last_vehicle_segment = ts_day_0_k_1; + return new LinkTravelTimes(last_vehicle_segment, historical_segments_k); + } + + private void logPredictionEvent(AvlReport avlReport, TravelTimeDetails travelTimeDetails, VehicleState vehicleState, long predictionTime, long alternatePrediction) { + + double percentageDifferecence = Math.abs(100 * ((predictionTime - alternatePrediction) / (double)alternatePrediction)); + + if (!Double.isInfinite(percentageDifferecence)) + getMonitoring().averageMetric("PredictionKalmanAverageDifference", Math.abs(percentageDifferecence)); + + if(((percentageDifferecence * alternatePrediction)/100) > tresholdForDifferenceEventLog.getValue()) + { + if(percentageDifferecence > percentagePredictionMethodDifferenceEventLog.getValue()) + { + String description="Kalman predicts : "+predictionTime+" Super predicts : "+alternatePrediction; + + logger.warn(description); + + PredictionEvent.create(avlReport, vehicleState.getMatch(), PredictionEvent.PREDICTION_VARIATION, description, + travelTimeDetails.getArrival().getStopId(), + travelTimeDetails.getDeparture().getStopId(), + travelTimeDetails.getArrival().getVehicleId(), + travelTimeDetails.getArrival().getTime(), + travelTimeDetails.getDeparture().getTime()); + } + } + + } + + /** + * Current and historical link travel times. + */ + private static class LinkTravelTimes { + private TripSegment lastVehicleSegment; + private TripSegment[] historicalSegments; + public LinkTravelTimes(TripSegment last_vehicle_segment, TripSegment[] historical_segments_k) { + this.lastVehicleSegment = last_vehicle_segment; + this.historicalSegments = historical_segments_k; + } + public TripSegment getLastVehicleSegment() { return lastVehicleSegment; } + public TripSegment[] getHistoricalSegments() { return historicalSegments; } + } +} diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/kalman/KalmanPrediction.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/kalman/KalmanPrediction.java new file mode 100755 index 000000000..3d6b71b8a --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/kalman/KalmanPrediction.java @@ -0,0 +1,198 @@ +package org.transitclock.core.predictiongenerator.kalman; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.config.BooleanConfigValue; + +/** + * The theory behind the Kalman Filter application to link travel times is provided + * https://scholarcommons.usf.edu/cgi/viewcontent.cgi?article=1342&context=jpt + * + * From the above paper we need to understand the following terminology: + * * "g" (gain) equals the filter gain + * * "a" (loopGain) is the loop gain + * * "e" (error/lastPredictionError) represents filter error + * * "p" (prediction) equals prediction + * * art(k) (lastVehicleDuration) is actual running time of the previous bus at instant (k) + * * art1(k+1) (historicalDuration) is actual running time of the previous day at instant (k+1) (we use average here instead to dampen) + * * VAR[data out] equals the prediction variance + * * VAR[data in] is the last three days “art3(k+1), art2(k+1) and art1(k+1)†variance + * + * With that, the predict method below implements P(k +1) according to: + * + * gain equation: + * g(k+1) = (e(k) + VAR[local]) / (e(k) + 2 * VAR(local)) + * as code: + * gain = (lastPredictionError + variance) / (lastPredictionError + ( 2 * variance )) + * + * loop gain equation: + * a(k + 1) = 1 – g(k + 1) + * as code: + * loopGain = 1 - lastPredictionError + * + * error equation: + * e(k + 1) = VAR[datain] * g(k + 1) + * as code: + * filterError = variance * loopGain + * + * prediction equation: + * P(k + 1) = a(k+1) * art(k) + g(k+1) * art1(k + 1) + * as code: + * prediction = (loopGain * lastVehicleDuration)+(gain * historicalDuration) + * + * + * @author Sean Óg Crudden + * + */ +public class KalmanPrediction { + + private static final Logger logger = LoggerFactory.getLogger(KalmanPrediction.class); + + private static final BooleanConfigValue useAverage = new BooleanConfigValue ( + "transitclock.prediction.kalman.useaverage", new Boolean(true), + "Will use average travel time as opposed to last historical vehicle in Kalman prediction calculation." + ); + + /** + * @param lastVehicleSegment The last vehicle info for the time taken to cover the same segment + * @param historicalSegments The last 3 days for info relating to the time taken for the vehicle handling the same service/trip + * @param lastPredictionError From the previous segments calculation result + * @return KalmanPredictionResult contains the predicted time and the lastPredictionError to be used in the next prediction calculation + * @throws Exception + */ + public KalmanPredictionResult predict(TripSegment lastVehicleSegment, + TripSegment historicalSegments[], + double lastPredictionError) throws Exception { + double average = historicalAverage(historicalSegments); + double variance = historicalVariance(historicalSegments, average); + double gain = gain(average, variance, lastPredictionError); + double loopGain = 1 - gain; + + return new KalmanPredictionResult( + prediction( + gain, + loopGain, + historicalSegments, + lastVehicleSegment, + average), + filterError(variance, gain)); + } + + private double historicalAverage(TripSegment historicalSegments[]) throws Exception { + if (historicalSegments.length>0) { + long total=0; + for(int i=0;i 355 && result.getResult() < 356) && (result.getFilterError()>149 && result.getFilterError()<150)) + { + System.out.println("Successful Kalman Filter Prediction."); + } else { + System.out.println("UnSuccessful Kalman Filter Prediction."); + } + } else { + System.out.println("No result."); + } + } catch (Exception e) { + System.out.println("Whoops"); + e.printStackTrace(); + } + } +} diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/kalman/KalmanPredictionResult.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/kalman/KalmanPredictionResult.java new file mode 100755 index 000000000..70d193edc --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/kalman/KalmanPredictionResult.java @@ -0,0 +1,40 @@ +package org.transitclock.core.predictiongenerator.kalman; + +public class KalmanPredictionResult { + @Override + public String toString() { + return "KalmanPredictionResult [duration=" + duration + ", filterError=" + filterError + "]"; + } + double duration; + double filterError; + public KalmanPredictionResult(double result, double filterError) { + super(); + this.duration = result; + this.filterError = filterError; + } + /** + * @return the result + */ + public double getResult() { + return duration; + } + /** + * @param result the result to set + */ + public void setResult(double result) { + this.duration = result; + } + /** + * @return the filterError + */ + public double getFilterError() { + return filterError; + } + /** + * @param filterError the filterError to set + */ + public void setFilterError(double filterError) { + this.filterError = filterError; + } + +} diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/kalman/TripSegment.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/kalman/TripSegment.java new file mode 100755 index 000000000..561f8522c --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/kalman/TripSegment.java @@ -0,0 +1,70 @@ +package org.transitclock.core.predictiongenerator.kalman; + +public class TripSegment { + VehicleStopDetail origin; + VehicleStopDetail destination; + + + public TripSegment(VehicleStopDetail origin, VehicleStopDetail destination) { + super(); + this.origin = origin; + this.destination = destination; + } + public long getDuration() + { + if(this.origin!=null && this.getDestination()!=null ) + return destination.getTime()-origin.getTime(); + else + return -1; + + } + /** + * @return the origin + */ + public VehicleStopDetail getOrigin() { + return origin; + } + + /** + * @return the destination + */ + public VehicleStopDetail getDestination() { + return destination; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((destination == null) ? 0 : destination.hashCode()); + result = prime * result + ((origin == null) ? 0 : origin.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; + TripSegment other = (TripSegment) obj; + if (destination == null) { + if (other.destination != null) + return false; + } else if (!destination.equals(other.destination)) + return false; + if (origin == null) { + if (other.origin != null) + return false; + } else if (!origin.equals(other.origin)) + return false; + return true; + } + @Override + public String toString() { + return "TripSegment [origin=" + origin + ", destination=" + destination + ", duration="+(destination.getTime()-origin.getTime())+"]"; + } + + +} diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/kalman/Vehicle.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/kalman/Vehicle.java new file mode 100755 index 000000000..ea635cea0 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/kalman/Vehicle.java @@ -0,0 +1,54 @@ +package org.transitclock.core.predictiongenerator.kalman; + +public class Vehicle { + @Override + public String toString() { + return "Vehicle [licence=" + licence + "]"; + } + + private String licence=null; + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((licence == null) ? 0 : licence.hashCode()); + return result; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Vehicle other = (Vehicle) obj; + if (licence == null) { + if (other.licence != null) + return false; + } else if (!licence.equals(other.licence)) + return false; + return true; + } + + /** + * @return the licence + */ + public String getLicence() { + return licence; + } + + public Vehicle(String licence) { + super(); + this.licence = licence; + } + +} diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/kalman/VehicleStopDetail.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/kalman/VehicleStopDetail.java new file mode 100755 index 000000000..5986b9fd8 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/kalman/VehicleStopDetail.java @@ -0,0 +1,108 @@ +package org.transitclock.core.predictiongenerator.kalman; + +import org.transitclock.db.structs.Stop; + +public class VehicleStopDetail { + @Override + public String toString() { + return "VehicleStopDetail [stop=" + stop + ", time=" + time + ", vehicle=" + vehicle + "]"; + } + + protected Stop stop; + protected long time=-1L; + protected Vehicle vehicle; + protected Long trafficTime = null; + + public VehicleStopDetail(Stop stop, long time, Vehicle vehicle) { + this.stop = stop; + this.time = time; + this.vehicle = vehicle; + } + public VehicleStopDetail(Stop stop, long time, Long trafficTime, Vehicle vehicle) { + this.stop = stop; + this.time = time; + this.trafficTime = trafficTime; + this.vehicle = vehicle; + } + + /** + * @return the stop + */ +public Stop getStop() { + return stop; +} +/** + * @param stop the stop to set + */ +public void setStop(Stop stop) { + this.stop = stop; +} +/* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ +@Override +public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((stop == null) ? 0 : stop.hashCode()); + result = prime * result + (int) (time ^ (time >>> 32)); + result = prime * result + ((vehicle == null) ? 0 : vehicle.hashCode()); + return result; +} +/* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ +@Override +public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + VehicleStopDetail other = (VehicleStopDetail) obj; + if (stop == null) { + if (other.stop != null) + return false; + } else if (!stop.equals(other.stop)) + return false; + if (time != other.time) + return false; + if (vehicle == null) { + if (other.vehicle != null) + return false; + } else if (!vehicle.equals(other.vehicle)) + return false; + return true; +} +/** + * @return the time + */ +public long getTime() { + return time; +} +/** + * @param time the time to set + */ +public void setTime(long time) { + this.time = time; +} +/** + * @return the vehicle + */ +public Vehicle getVehicle() { + return vehicle; +} +/** + * @param vehicle the vehicle to set + */ +public void setVehicle(Vehicle vehicle) { + this.vehicle = vehicle; +} + +public Long getTrafficTime() { + return trafficTime; +} +public void setTrafficTime(long trafficTime) { this.trafficTime = trafficTime; } + +} diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/lastvehicle/LastVehiclePredictionGeneratorImpl.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/lastvehicle/LastVehiclePredictionGeneratorImpl.java new file mode 100755 index 000000000..e6656c3b6 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/lastvehicle/LastVehiclePredictionGeneratorImpl.java @@ -0,0 +1,105 @@ +package org.transitclock.core.predictiongenerator.lastvehicle; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.applications.Core; +import org.transitclock.core.Indices; +import org.transitclock.core.PredictionGeneratorDefaultImpl; +import org.transitclock.core.TravelTimeDetails; +import org.transitclock.core.VehicleState; +import org.transitclock.core.dataCache.StopPathPredictionCacheFactory; +import org.transitclock.core.dataCache.VehicleDataCache; +import org.transitclock.core.dataCache.VehicleStateManager; +import org.transitclock.core.predictiongenerator.HistoricalPredictionLibrary; +import org.transitclock.core.predictiongenerator.PredictionComponentElementsGenerator; +import org.transitclock.db.structs.AvlReport; +import org.transitclock.db.structs.PredictionForStopPath; +import org.transitclock.ipc.data.IpcPrediction; +import org.transitclock.ipc.data.IpcVehicleComplete; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * @author Sean Óg Crudden + * This provides a prediction based on the time it took the previous vehicle on the same route to cover the same ground. This is another step to get to Kalman implementation. + * + * TODO Debug as this has yet to be tried and tested. + * Could do a combination with historical average so that it improves quickly rather than just waiting on having enough data to support average or Kalman. + * So do a progression from LastVehicle --> Historical Average --> Kalman. Might be interesting to look at the rate of improvement of prediction as well as the end result. + * + * Does this by changing which class each extends. How can we make configurable? + * + * This works for both schedules based and frequency based services out of the box. Not so for historical average or Kalman filter. + */ +public class LastVehiclePredictionGeneratorImpl extends + PredictionGeneratorDefaultImpl implements PredictionComponentElementsGenerator { + @Override + protected IpcPrediction generatePredictionForStop(AvlReport avlReport, Indices indices, long predictionTime, + boolean useArrivalTimes, boolean affectedByWaitStop, boolean isDelayed, boolean lateSoMarkAsUncertain, + int tripCounter, Integer scheduleDeviation) { + // TODO Auto-generated method stub + return super.generatePredictionForStop(avlReport, indices, predictionTime, useArrivalTimes, affectedByWaitStop, + isDelayed, lateSoMarkAsUncertain, tripCounter, scheduleDeviation); + } + private String alternative="PredictionGeneratorDefaultImpl"; + + private static final Logger logger = LoggerFactory + .getLogger(LastVehiclePredictionGeneratorImpl.class); + + /* (non-Javadoc) + * @see org.transitclock.core.predictiongenerator.KalmanPredictionGeneratorImpl#getTravelTimeForPath(org.transitclock.core.Indices, org.transitclock.db.structs.AvlReport) + */ + @Override + public long getTravelTimeForPath(Indices indices, AvlReport avlReport, VehicleState vehicleState) { + + VehicleDataCache vehicleCache = VehicleDataCache.getInstance(); + + List vehiclesOnRoute = new ArrayList(); + + VehicleStateManager vehicleStateManager = VehicleStateManager + .getInstance(); + + VehicleState currentVehicleState = vehicleStateManager + .getVehicleState(avlReport.getVehicleId()); + + for (IpcVehicleComplete vehicle : HistoricalPredictionLibrary.emptyIfNull(vehicleCache + .getVehiclesForRoute(currentVehicleState.getRouteId()))) { + VehicleState vehicleOnRouteState = vehicleStateManager + .getVehicleState(vehicle.getId()); + vehiclesOnRoute.add(vehicleOnRouteState); + } + + try { + TravelTimeDetails travelTimeDetails = null; + if((travelTimeDetails=HistoricalPredictionLibrary.getLastVehicleTravelTime(currentVehicleState, indices))!=null) + { + logger.debug("Using last vehicle algorithm for prediction : " + travelTimeDetails.toString() + " for : " + indices.toString()); + + if(storeTravelTimeStopPathPredictions.getValue()) + { + PredictionForStopPath predictionForStopPath=new PredictionForStopPath(vehicleState.getVehicleId(), new Date(Core.getInstance().getSystemTime()), new Double(new Long(travelTimeDetails.getTravelTime()).intValue()), indices.getTrip().getId(), indices.getStopPathIndex(), "LAST VEHICLE", true, null); + + Core.getInstance().getDbLogger().add(predictionForStopPath); + StopPathPredictionCacheFactory.getInstance().putPrediction(predictionForStopPath); + } + + return travelTimeDetails.getTravelTime(); + } + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + //logger.debug("No last vehicle data found, generating default prediction : " + indices.toString()); + /* default to parent method if not enough data. This will be based on schedule if UpdateTravelTimes has not been called. */ + return super.getTravelTimeForPath(indices, avlReport, currentVehicleState); + } + @Override + public long getStopTimeForPath(Indices indices, AvlReport avlReport, VehicleState vehicleState) { + // Looking at last vehicle value would be a bad idea for dwell time, so no implementation here. + + return super.getStopTimeForPath(indices, avlReport, vehicleState); + } +} diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/performance/MeanAbsolutePercentageError.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/performance/MeanAbsolutePercentageError.java new file mode 100755 index 000000000..4549a11a4 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/performance/MeanAbsolutePercentageError.java @@ -0,0 +1,34 @@ +package org.transitclock.core.predictiongenerator.performance; + +import java.util.List; + +/** + * @author Sean Og Crudden + * https://en.wikipedia.org/wiki/Mean_absolute_percentage_error + */ +public class MeanAbsolutePercentageError { + private Long actualDuration = null; + private List predictions = null; + + public MeanAbsolutePercentageError(Long actualDuration, List predictions) { + this.actualDuration=actualDuration; + this.predictions=predictions; + + } + public Long getMAPE() throws Exception + { + if(actualDuration !=null && predictions!=null && predictions.size()>0 ) + { + Long totalDifference= 0L; + + for(Long prediction:predictions) + { + totalDifference=totalDifference+Math.abs(actualDuration-prediction); + } + return totalDifference/predictions.size(); + }else + { + throw new Exception("All paramters must be set."); + } + } +} diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/average/HistoricalAveragePredictionGeneratorImpl.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/average/HistoricalAveragePredictionGeneratorImpl.java new file mode 100755 index 000000000..c41840816 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/average/HistoricalAveragePredictionGeneratorImpl.java @@ -0,0 +1,100 @@ +package org.transitclock.core.predictiongenerator.scheduled.average; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.applications.Core; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.core.Indices; +import org.transitclock.core.PredictionGeneratorDefaultImpl; +import org.transitclock.core.VehicleState; +import org.transitclock.core.dataCache.HistoricalAverage; +import org.transitclock.core.dataCache.StopPathCacheKey; +import org.transitclock.core.dataCache.StopPathPredictionCacheFactory; +import org.transitclock.core.dataCache.scheduled.ScheduleBasedHistoricalAverageCache; +import org.transitclock.core.predictiongenerator.PredictionComponentElementsGenerator; +import org.transitclock.db.structs.AvlReport; +import org.transitclock.db.structs.PredictionForStopPath; + +import java.util.Date; + +/** + * @author Sean Óg Crudden + * This provides a prediction based on the average of historical data for schedules based services. The average is taken from the HistoricalAverageCache which is + * populated each time an arrival/departure event occurs. The HistoricalAverageCache is updated using data from the TripDataHistory cache. + */ +public class HistoricalAveragePredictionGeneratorImpl extends + PredictionGeneratorDefaultImpl implements PredictionComponentElementsGenerator { + private String alternative="LastVehiclePredictionGeneratorImpl"; + + + + private static final IntegerConfigValue minDays = new IntegerConfigValue( + "transitclock.prediction.data.average.mindays", + new Integer(1), + "Min number of days trip data that needs to be available before historical average prediciton is used instead of default transiTime prediction."); + + + private static final Logger logger = LoggerFactory + .getLogger(HistoricalAveragePredictionGeneratorImpl.class); + + /* (non-Javadoc) + * @see org.transitclock.core.predictiongenerator.KalmanPredictionGeneratorImpl#getTravelTimeForPath(org.transitclock.core.Indices, org.transitclock.db.structs.AvlReport) + */ + @Override + public long getTravelTimeForPath(Indices indices, AvlReport avlReport, VehicleState vehicleState) { + + logger.debug("Calling historical average algorithm : "+indices.toString()); + /* + * if we have enough data start using historical average otherwise + * revert to default. This does not mean that this method of + * prediction is better than the default. + */ + StopPathCacheKey historicalAverageCacheKey=new StopPathCacheKey(indices.getTrip().getId(), indices.getStopPathIndex()); + + HistoricalAverage average = ScheduleBasedHistoricalAverageCache.getInstance().getAverage(historicalAverageCacheKey); + + if(average!=null && average.getCount()>=minDays.getValue()) + { + if(storeTravelTimeStopPathPredictions.getValue()) + { + PredictionForStopPath predictionForStopPath=new PredictionForStopPath(vehicleState.getVehicleId(), new Date(Core.getInstance().getSystemTime()), average.getAverage(), indices.getTrip().getId(), indices.getStopPathIndex(), "HISTORICAL AVERAGE",true, null); + Core.getInstance().getDbLogger().add(predictionForStopPath); + StopPathPredictionCacheFactory.getInstance().putPrediction(predictionForStopPath); + } + + logger.debug("Using historical average algorithm for prediction : " +average.toString() + " instead of "+alternative+" prediction: " + + super.getTravelTimeForPath(indices, avlReport,vehicleState) +" for : " + indices.toString()); + //logger.debug("Instead of transitclock value : " + super.getTravelTimeForPath(indices, avlReport)); + return (long)average.getAverage(); + } + + //logger.debug("No historical average found, generating prediction using lastvehicle algorithm: " + historicalAverageCacheKey.toString()); + /* default to parent method if not enough data. This will be based on schedule if UpdateTravelTimes has not been called. */ + return super.getTravelTimeForPath(indices, avlReport, vehicleState); + } + + @Override + public long getStopTimeForPath(Indices indices, AvlReport avlReport, VehicleState vehicleState) { + + StopPathCacheKey historicalAverageCacheKey=new StopPathCacheKey(indices.getTrip().getId(), indices.getStopPathIndex(),false); + + HistoricalAverage average = ScheduleBasedHistoricalAverageCache.getInstance().getAverage(historicalAverageCacheKey); + + if(average!=null && average.getCount()>=minDays.getValue()) + { + logger.debug("Using historical average alogrithm for dwell time prediction : "+average.toString() + " instead of "+alternative+" prediction: " + + super.getStopTimeForPath(indices, avlReport, vehicleState) +" for : " + indices.toString()); + return (long)average.getAverage(); + } + + return super.getStopTimeForPath(indices, avlReport, vehicleState); + } + + @Override + public boolean hasDataForPath(Indices indices, AvlReport avlReport) { + StopPathCacheKey historicalAverageCacheKey = new StopPathCacheKey(indices.getTrip().getId(), indices.getStopPathIndex()); + HistoricalAverage average = ScheduleBasedHistoricalAverageCache.getInstance().getAverage(historicalAverageCacheKey); + + return (average!=null && average.getCount()>=minDays.getValue()); + } +} diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/dwell/DwellAverage.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/dwell/DwellAverage.java new file mode 100644 index 000000000..48415f7ae --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/dwell/DwellAverage.java @@ -0,0 +1,83 @@ +package org.transitclock.core.predictiongenerator.scheduled.dwell; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.transitclock.config.DoubleConfigValue; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.statistics.Statistics; +/** + * + * @author scrudden + * This is a running average for dwell times. + */ +public class DwellAverage implements DwellModel, Serializable { + /** + * + */ + private static final long serialVersionUID = 7514794134817837972L; + + private static IntegerConfigValue samplesize= new IntegerConfigValue("transitclock.prediction.dwell.average.samplesize", 5 , "Max number of samples to keep for mean calculation."); + private static DoubleConfigValue fractionLimitForStopTimes=new DoubleConfigValue("transitclock.prediction.dwell.average.fractionlimit", 0.7, "For when determining stop times. Throws out outliers if they are less than 0.7 or greater than 1/0.7 of the average."); + + private List values=new ArrayList(); + // For this model headway or demand is not taken into account. + @Override + public void putSample(Integer value, Integer headway, Integer demand) + { + + if(values.size() < samplesize.getValue()) + { + values.add(value); + Collections.rotate(values,1); + }else + { + Collections.rotate(values,1); + values.set(0, value); + } + } + + @Override + public Integer predict(Integer headway, Integer demand) { + + return Statistics + .filteredMean(values, + fractionLimitForStopTimes.getValue()); + } + public static void main(String[] args) + { + DwellAverage average=new DwellAverage(); + average.putSample(new Integer(1), null, null); + average.putSample(new Integer(2), null, null); + average.putSample(new Integer(3), null, null); + average.putSample(new Integer(4), null, null); + average.putSample(new Integer(5), null, null); + average.putSample(new Integer(6), null, null); + average.putSample(new Integer(7), null, null); + average.putSample(new Integer(8), null, null); + average.putSample(new Integer(9), null, null); + average.putSample(new Integer(10), null, null); + average.putSample(new Integer(11), null, null); + average.putSample(new Integer(12), null, null); + + System.out.println(average.predict(null, null)); + + average.putSample(new Integer(2), null, null); + average.putSample(new Integer(2), null, null); + average.putSample(new Integer(2), null, null); + average.putSample(new Integer(2), null, null); + average.putSample(new Integer(2), null, null); + average.putSample(new Integer(9), null, null); + average.putSample(new Integer(2), null, null); + average.putSample(new Integer(2), null, null); + average.putSample(new Integer(11), null, null); + average.putSample(new Integer(2), null, null); + average.putSample(new Integer(2), null, null); + average.putSample(new Integer(2), null, null); + + System.out.println(average.predict(null, null)); + } + +} diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/dwell/DwellModel.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/dwell/DwellModel.java new file mode 100644 index 000000000..817066a30 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/dwell/DwellModel.java @@ -0,0 +1,15 @@ +package org.transitclock.core.predictiongenerator.scheduled.dwell; + +import java.io.Serializable; + +/** + * + * @author scrudden + * To be implemented if you want to create an algorithm to predict dwell time. + * The possible inputs to any such algorithms are dwelltime, headway and demand. + * + */ +public interface DwellModel extends Serializable { + public Integer predict(Integer headway, Integer demand); + public void putSample(Integer dwelltime, Integer headway, Integer demand); +} diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/dwell/DwellRLS.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/dwell/DwellRLS.java new file mode 100644 index 000000000..6738eb991 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/dwell/DwellRLS.java @@ -0,0 +1,54 @@ +package org.transitclock.core.predictiongenerator.scheduled.dwell; + +import java.io.Serializable; + +import org.transitclock.config.DoubleConfigValue; +import org.transitclock.core.predictiongenerator.scheduled.dwell.rls.TransitClockRLS; +/** + * + * @author scrudden + * + * + */ +public class DwellRLS implements DwellModel,Serializable { + + /** + * + */ + private static final long serialVersionUID = -9082591970192068672L; + + private TransitClockRLS rls = null; + + public TransitClockRLS getRls() { + return rls; + } + + public void setRls(TransitClockRLS rls) { + this.rls = rls; + } + + private static DoubleConfigValue lambda = new DoubleConfigValue("transitclock.prediction.rls.lambda", 0.75, "This sets the rate at which the RLS algorithm forgets old values. Value are between 0 and 1. With 0 being the most forgetful."); + + + public DwellRLS() { + super(); + rls=new TransitClockRLS(lambda.getValue()); + } + + @Override + public Integer predict(Integer headway, Integer demand) { + double[] arg0 = new double[1]; + arg0[0]=headway; + if(rls.getRls()!=null) + return (int) Math.pow(10, rls.getRls().predict(arg0)); + else + return null; + } + + @Override + public void putSample(Integer dwelltime, Integer headway, Integer demand) { + + rls.addSample(headway, Math.log10(dwelltime)); + } + +} diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/dwell/DwellTimeModelFactory.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/dwell/DwellTimeModelFactory.java new file mode 100644 index 000000000..423c32e40 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/dwell/DwellTimeModelFactory.java @@ -0,0 +1,25 @@ +package org.transitclock.core.predictiongenerator.scheduled.dwell; + +import org.transitclock.config.StringConfigValue; +import org.transitclock.utils.ClassInstantiator; +/** + * + * @author scrudden + * Returns the model that is to be used to estimate dwell time for a stop. + * + */ +public class DwellTimeModelFactory { + // The name of the class to instantiate + private static StringConfigValue className = + new StringConfigValue("transitclock.core.dwelltime.model", + "org.transitclock.core.predictiongenerator.scheduled.dwell.DwellAverage", + "Specifies the name of the class used to predict dwell."); + + + /********************** Member Functions **************************/ + public static DwellModel getInstance() { + + return ClassInstantiator.instantiate(className.getValue(), + DwellModel.class); + } +} diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/dwell/DwellTimePredictionGeneratorImpl.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/dwell/DwellTimePredictionGeneratorImpl.java new file mode 100644 index 000000000..8f4d723f0 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/dwell/DwellTimePredictionGeneratorImpl.java @@ -0,0 +1,86 @@ +package org.transitclock.core.predictiongenerator.scheduled.dwell; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.core.Indices; +import org.transitclock.core.VehicleState; +import org.transitclock.core.dataCache.DwellTimeModelCacheFactory; +import org.transitclock.core.dataCache.StopPathCacheKey; +import org.transitclock.core.predictiongenerator.scheduled.traveltime.kalman.KalmanPredictionGeneratorImpl; +import org.transitclock.db.structs.AvlReport; +import org.transitclock.db.structs.Headway; + +/** + * @author Sean Og Crudden + * + * This is an experiment to see if headway can be used to better predict dwell time. Most of what + * I have read tells me it can but in conjunction with APC data and estimation of demand at stops. + * + * I do wonder if headway alone is enough to at least improve things beyond using the schedule? + * + * This has now been changed to work with any DwellModel implementation. + * + */ +public class DwellTimePredictionGeneratorImpl extends KalmanPredictionGeneratorImpl { + + private static final Logger logger = LoggerFactory.getLogger(DwellTimePredictionGeneratorImpl.class); + + @Override + public long getStopTimeForPath(Indices indices, AvlReport avlReport, VehicleState vehicleState) { + Long result=null; + try { + Headway headway = vehicleState.getHeadway(); + + if(headway!=null) + { + logger.debug("Headway at {} based on avl {} is {}.",indices, avlReport, headway); + + /* Change approach to use a RLS model. + */ + if(super.getStopTimeForPath(indices, avlReport, vehicleState)>0) + { + + StopPathCacheKey cacheKey=new StopPathCacheKey(indices.getTrip().getId(), indices.getStopPathIndex(), false); + + + result = DwellTimeModelCacheFactory.getInstance().predictDwellTime(cacheKey, headway); + + if(result==null) + { + logger.debug("Using scheduled value for dwell time as no model available for {}.", indices); + result = super.getStopTimeForPath(indices, avlReport, vehicleState); + } + + + /* should never have a negative dwell time */ + if(result<0) + { + logger.debug("Predicted negative dwell time {} for {}.", result, indices); + result=0L; + } + + }else + { + logger.debug("Scheduled dwell time is less than 0 for {}.", indices); + result = super.getStopTimeForPath(indices, avlReport, vehicleState); + } + + logger.debug("Using dwell time {} for {} instead of {}. Headway for vehicle {} is {}",result,indices, super.getStopTimeForPath(indices, avlReport, vehicleState), vehicleState.getVehicleId(),headway ); + } + else + { + result = super.getStopTimeForPath(indices, avlReport, vehicleState); + logger.debug("Using dwell time {} for {} instead of {}. No headway.",result,indices, super.getStopTimeForPath(indices ,avlReport, vehicleState)); + } + + } catch (Exception e) { + + logger.error(e.getMessage(),e); + e.printStackTrace(); + + } + + return result; + } + +} diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/dwell/rls/TransitClockRLS.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/dwell/rls/TransitClockRLS.java new file mode 100644 index 000000000..1d7da2cf5 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/dwell/rls/TransitClockRLS.java @@ -0,0 +1,77 @@ +package org.transitclock.core.predictiongenerator.scheduled.dwell.rls; + +import java.io.Serializable; + +import org.transitclock.config.DoubleConfigValue; + +import smile.regression.RLS; + +public class TransitClockRLS implements Serializable { + + + /** + * @param lambda + * + */ + public TransitClockRLS(double lambda) { + super(); + this.lambda=lambda; + } + + public RLS getRls() { + return rls; + } + private double lambda; + Double firstx=null; + Double firsty=null; + private Integer samples=0; + RLS rls=null; + + private static final long serialVersionUID = -5863984357400905560L; + + + + public void addSample(double d, double e) + { + + samples++; + + if(firstx==null&&firsty==null) + { + this.firstx=d; + this.firsty=e; + }else + { + + + + if(rls==null) + { + double samplex[][]=new double[2][1]; + double sampley[]=new double[2]; + + samplex[0][0]=firstx; + sampley[0]=firsty; + + samplex[1][0]=d; + sampley[1]=e; + rls=new RLS(samplex, sampley, lambda); + } + else + { + double samplex[][]=new double[1][1]; + double sampley[]=new double[1]; + + samplex[0][0]=d; + sampley[0]=e; + + rls.learn(samplex, sampley); + } + } + } + public Integer numSamples() + { + return samples; + } + +} diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/KalmanPredictionGeneratorImpl.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/KalmanPredictionGeneratorImpl.java new file mode 100755 index 000000000..5e09bed91 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/KalmanPredictionGeneratorImpl.java @@ -0,0 +1,381 @@ +package org.transitclock.core.predictiongenerator.scheduled.traveltime.kalman; + +import org.apache.commons.lang3.time.DateUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.applications.Core; +import org.transitclock.config.BooleanConfigValue; +import org.transitclock.config.DoubleConfigValue; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.core.*; +import org.transitclock.core.dataCache.*; +import org.transitclock.core.predictiongenerator.HistoricalPredictionLibrary; +import org.transitclock.core.predictiongenerator.PredictionComponentElementsGenerator; +import org.transitclock.core.predictiongenerator.kalman.*; +import org.transitclock.db.structs.AvlReport; +import org.transitclock.db.structs.PredictionEvent; +import org.transitclock.db.structs.PredictionForStopPath; +import org.transitclock.db.structs.TrafficSensorData; + +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +/** + * This is a prediction generator that uses a Kalman filter to provide predictions. + * It uses historical average in combination with the a headway vehicle when + * enough data is present to support a Kalman filter. + * + * @see KalmanPrediction for the research paper and description of the terms + * + * + * @author Sean Óg Crudden + * @author sheldonabrown + */ +public class KalmanPredictionGeneratorImpl extends PredictionGeneratorDefaultImpl + implements PredictionComponentElementsGenerator { + + private String alternative="PredictionGeneratorDefaultImpl"; + + private static final IntegerConfigValue minKalmanDays = new IntegerConfigValue( + "transitclock.prediction.data.kalman.mindays", new Integer(3), + "Min number of days trip data that needs to be available before Kalman prediction is used instead of default transitClock prediction."); + + private static final IntegerConfigValue maxKalmanDays = new IntegerConfigValue( + "transitclock.prediction.data.kalman.maxdays", new Integer(3), + "Max number of historical days trips to include in Kalman prediction calculation."); + + private static final IntegerConfigValue maxKalmanDaysToSearch = new IntegerConfigValue( + "transitclock.prediction.data.kalman.maxdaystosearch", new Integer(21), + "Max number of days to look back for data. This will also be effected by how old the data in the cache is."); + + private static final DoubleConfigValue initialErrorValue = new DoubleConfigValue( + "transitclock.prediction.data.kalman.initialerrorvalue", new Double(100), + "Initial Kalman error value to use to start filter."); + + /* May be better to use the default implementation as it splits things down into segments. */ + private static final BooleanConfigValue useKalmanForPartialStopPaths = new BooleanConfigValue ( + "transitclock.prediction.data.kalman.usekalmanforpartialstoppaths", new Boolean(true), + "Will use Kalman prediction to get to first stop of prediction." + ); + + private static final IntegerConfigValue percentagePredictionMethodDifferenceEventLog =new IntegerConfigValue( + "transitclock.prediction.data.kalman.percentagePredictionMethodDifference", new Integer(50), + "If the difference in prediction method estimates is greater than this percentage log a Vehicle Event"); + + private static final IntegerConfigValue tresholdForDifferenceEventLog=new IntegerConfigValue( + "transitclock.prediction.data.kalman.tresholdForDifferenceEventLog", new Integer(60000), + "This is the threshold in milliseconds that the difference has to be over before it will consider the percentage difference."); + + private static final Logger logger = LoggerFactory.getLogger(KalmanPredictionGeneratorImpl.class); + + /* + * return a prediction for the travel of the given vehicle. If enough data is present this + * will be a kalman weighted prediction, otherwise it will default to the basic prediction + * algorithm. + * + * @see + * org.transitclock.core.PredictionGeneratorDefaultImpl#getTravelTimeForPath + * (org.transitclock.core.Indices, org.transitclock.db.structs.AvlReport) + */ + @Override + public long getTravelTimeForPath(Indices indices, AvlReport avlReport, VehicleState currentVehicleState) { + + logger.debug("Calling Kalman prediction algorithm for : "+indices.toString()); + long alternatePrediction = super.getTravelTimeForPath(indices, avlReport, currentVehicleState); + + try { + // travel times of vehicle one (or more) headways in front of us on this segment + TravelTimeDetails headwayTravelTimes = getLastVehicleTravelTime(currentVehicleState, indices); + + /* + * The first vehicle of the day should use schedule or historic data to + * make prediction. Cannot use Kalman as yesterdays vehicle will have + * little to say about today's. + */ + if (headwayTravelTimes!=null) { + getMonitoring().rateMetric("PredictionKalmanHeadwayHit", true); + logger.debug("Kalman has last vehicle info for : " +indices.toString()+ " : "+headwayTravelTimes); + + // lookup historical travel times for this trip independent of vehicle + List historicalTravelTimes = getHistoricalTravelTimes(avlReport, indices, currentVehicleState); + /* + * if we have enough data start using Kalman filter otherwise revert + * to base class for prediction. + */ + if (historicalTravelTimes != null && historicalTravelTimes.size() >= minKalmanDays.getValue().intValue()) { + getMonitoring().rateMetric("PredictionKalmanHistoryHit", true); + getMonitoring().averageMetric("PredictionKalmanHistorySize", historicalTravelTimes.size()); + logger.debug("Generating Kalman prediction for : "+indices.toString()); + + try { + KalmanPrediction kalmanPrediction = new KalmanPrediction(); + LinkTravelTimes linkTravelTimes = generateLinkTravelTimes(avlReport, historicalTravelTimes, headwayTravelTimes, indices); + Indices headwayVehicleIndices = new Indices(headwayTravelTimes.getArrival()); + KalmanError headwayError = getKalmanErrorForIndices(getKalmanErrorCache(), headwayVehicleIndices); + + // perform the adjustment based on the history retrieved and the headway as the realtime input + KalmanPredictionResult kalmanPredictionResult = kalmanPrediction.predict(linkTravelTimes.getLastVehicleSegment(), + linkTravelTimes.getHistoricalSegments(), + headwayError.getError()); + + long predictionTime = (long) kalmanPredictionResult.getResult(); + + getKalmanErrorCache().putErrorValue(indices, kalmanPredictionResult.getFilterError()); + logPredictionEvent(avlReport, headwayTravelTimes, currentVehicleState, predictionTime, alternatePrediction); + + logger.debug("Using Kalman prediction: " + predictionTime + " instead of "+alternative+" prediction: " + + alternatePrediction +" for : " + indices.toString()); + + storePrediction(currentVehicleState, indices, predictionTime); + + getMonitoring().rateMetric("PredictionKalmanHit", true); + getMonitoring().sumMetric("PredictionGenerationKalman"); + return predictionTime; + + } catch (Exception e) { + logger.error("Exception {}", e.toString(), e); + } + } else { + getMonitoring().rateMetric("PredictionKalmanHistoryHit", false); + if (historicalTravelTimes == null) + getMonitoring().averageMetric("PredictionKalmanHistorySize", 0.0); + else + getMonitoring().averageMetric("PredictionKalmanHistorySize", historicalTravelTimes.size()); + } + } else { + // no travel time + logger.debug("no travel times for trip {}", currentVehicleState.getTrip().getId()); + getMonitoring().rateMetric("PredictionKalmanHeadwayHit", false); + } + } catch (Exception e) { + logger.error("kalman prediction error", e); + } + // instrument kalman miss + getMonitoring().rateMetric("PredictionKalmanHit", false); + return alternatePrediction; + } + + @Override + public long expectedTravelTimeFromMatchToEndOfStopPath(AvlReport avlReport, SpatialMatch match) { + + if(useKalmanForPartialStopPaths.getValue().booleanValue()) + { + + VehicleState currentVehicleState = getVehicleStateManager().getVehicleState(avlReport.getVehicleId()); + + long fulltime = this.getTravelTimeForPath(match.getIndices(), avlReport, currentVehicleState); + + double distanceAlongStopPath = match.getDistanceAlongStopPath(); + + double stopPathLength = + match.getStopPath().getLength(); + + long remainingtime = (long) (fulltime * ((stopPathLength-distanceAlongStopPath)/stopPathLength)); + + logger.debug("Using Kalman for first stop path {} with value {} instead of {}.", match.getIndices(), remainingtime, super.expectedTravelTimeFromMatchToEndOfStopPath(avlReport, match)); + + return remainingtime; + }else + { + return super.expectedTravelTimeFromMatchToEndOfStopPath(avlReport, match); + } + } + + protected List getHistoricalTravelTimes(AvlReport avlReport, Indices indices, VehicleState currentVehicleState) { + Date nearestDay = DateUtils.truncate(avlReport.getDate(), Calendar.DAY_OF_MONTH); + List historicalTravelTimes = + HistoricalPredictionLibrary.getHistoricalTravelTimes( + getTripCache(), + currentVehicleState.getTrip().getId(), + currentVehicleState.getTrip().getDirectionId(), + indices.getStopPathIndex(), + nearestDay, + currentVehicleState.getTrip().getStartTime(), + maxKalmanDaysToSearch.getValue(), + maxKalmanDays.getValue()); + if(historicalTravelTimes!=null) { + logger.debug("Kalman has " +historicalTravelTimes.size()+ " historical values for : " +indices.toString()); + } + + return historicalTravelTimes; + } + + private void storePrediction(VehicleState vehicleState, Indices indices, long predictionTime) { + if(storeTravelTimeStopPathPredictions.getValue()) + { + PredictionForStopPath predictionForStopPath=new PredictionForStopPath( + vehicleState.getVehicleId(), + new Date(Core.getInstance().getSystemTime()), + new Double(new Long(predictionTime).intValue()), + indices.getTrip().getId(), + indices.getStopPathIndex(), + "KALMAN", + true, + null); + Core.getInstance().getDbLogger().add(predictionForStopPath); + StopPathPredictionCacheFactory.getInstance().putPrediction(predictionForStopPath); + } + + } + + private void logPredictionEvent(AvlReport avlReport, TravelTimeDetails travelTimeDetails, VehicleState vehicleState, long predictionTime, long alternatePrediction) { + + double percentageDifferecence = Math.abs(100 * ((predictionTime - alternatePrediction) / (double)alternatePrediction)); + + if (!Double.isInfinite(percentageDifferecence)) + getMonitoring().averageMetric("PredictionKalmanAverageDifference", Math.abs(percentageDifferecence)); + + if(((percentageDifferecence * alternatePrediction)/100) > tresholdForDifferenceEventLog.getValue()) + { + if(percentageDifferecence > percentagePredictionMethodDifferenceEventLog.getValue()) + { + String description="Kalman predicts : "+predictionTime+" Super predicts : "+alternatePrediction; + + logger.warn(description); + + PredictionEvent.create(avlReport, vehicleState.getMatch(), PredictionEvent.PREDICTION_VARIATION, description, + travelTimeDetails.getArrival().getStopId(), + travelTimeDetails.getDeparture().getStopId(), + travelTimeDetails.getArrival().getVehicleId(), + travelTimeDetails.getArrival().getTime(), + travelTimeDetails.getDeparture().getTime()); + } + } + + } + + private LinkTravelTimes generateLinkTravelTimes(AvlReport avlReport, + List lastDaysTimes, + TravelTimeDetails travelTimeDetails, + Indices indices) { + Vehicle vehicle = new Vehicle(avlReport.getVehicleId()); + + Long trafficTravelTime = getTrafficForIndices(indices); + if (logger.isInfoEnabled() && trafficTravelTime != null) { + TrafficSensorData sensorData = TrafficManager.getInstance().getTrafficSensorDataForStopPath(indices.getStopPath()); + double length = indices.getStopPath().getLength(); + long busTravelTime = lastDaysTimes.get(0).getTravelTime(); + Double trafficSpeedInMPH = sensorData.getSpeed() * 2.237 /* m/s to mph */; + Double busSpeedInMPH = length / busTravelTime * 1000 * 2.237 /* m/s to mph */; + + if (trafficTravelTime != null && trafficTravelTime > 0) { + logger.info("traffic adjusted speed {}mph, bus speed {}mph, {}% diff", + trafficSpeedInMPH, + busSpeedInMPH, + (trafficSpeedInMPH - busSpeedInMPH)/busSpeedInMPH); + } + } + + VehicleStopDetail originDetail = new VehicleStopDetail(null, 0, 0l, vehicle); + TripSegment[] historical_segments_k = new TripSegment[lastDaysTimes.size()]; + for (int i = 0; i < lastDaysTimes.size() && i < maxKalmanDays.getValue(); i++) { + // We don't have historical AVL times so guess at it based on now + Long historicalAvlTime = DateUtils.addDays(new Date(avlReport.getTime()), -1 * i).getTime(); + logger.debug("Kalman is using historical value : "+lastDaysTimes.get(i) +" for : " + indices.toString()); + VehicleStopDetail destinationDetail = new VehicleStopDetail(null, lastDaysTimes.get(i).getTravelTime(), + getTrafficHistory(indices, historicalAvlTime), + vehicle); + // TODO: why do we insert into array in reverse order? + historical_segments_k[lastDaysTimes.size()-i-1] = new TripSegment(originDetail, destinationDetail); + } + VehicleStopDetail destinationDetail_0_k_1 = new VehicleStopDetail(null, travelTimeDetails.getTravelTime(), trafficTravelTime, vehicle); + TripSegment ts_day_0_k_1 = new TripSegment(originDetail, destinationDetail_0_k_1); + TripSegment last_vehicle_segment = ts_day_0_k_1; + return new LinkTravelTimes(last_vehicle_segment, historical_segments_k); + } + + private Long getTrafficHistory(Indices indices, Long historicalTime) { + if (!isTrafficDataEnabled() || historicalTime == null) return null; + if (TrafficManager.getInstance().hasTrafficData(indices.getStopPath())) { + return TrafficManager.getInstance().getHistoricalTravelTime(indices.getStopPath(), historicalTime); + } + return null; + } + + private boolean isTrafficDataEnabled() { + return TrafficManager.trafficDataEnabled.getValue(); + } + + private Long getTrafficForIndices(Indices indices) { + if (!isTrafficDataEnabled()) return null; + // find stopPath, see if there is a traffic path for it + // if so, retrieve the traffic travel time for that segment + if (TrafficManager.getInstance().hasTrafficData(indices.getStopPath())) { + return TrafficManager.getInstance().getTravelTime(indices.getStopPath()); + } + return null; + } + + private KalmanError getKalmanErrorForIndices(ErrorCache cache, Indices indices) { + + KalmanError result; + try { + result = cache.getErrorValue(indices); + if(result==null) + { + logger.debug("Kalman Error value set to default: "+initialErrorValue.getValue() +" for key: "+new KalmanErrorCacheKey(indices).toString()); + result=new KalmanError(initialErrorValue.getValue()); + } + return result; + } catch (Exception e) { + logger.error("error retrieving KalmanError:", e); + } + return new KalmanError(initialErrorValue.getValue()); + } + + @Override + public long getStopTimeForPath(Indices indices, AvlReport avlReport, VehicleState vehicleState) { + long result=super.getStopTimeForPath(indices, avlReport, vehicleState); + + return result; + + } + + /** + * protected for unit test overrides. + * @return + */ + protected VehicleStateManager getVehicleStateManager() { + return VehicleStateManager.getInstance(); + } + + /** + * protected for unit test overrides. + * @return + */ + protected TripDataHistoryCacheInterface getTripCache() { + return TripDataHistoryCacheFactory.getInstance(); + } + + /** + * protected for unit test overrides. + * @return + */ + protected ErrorCache getKalmanErrorCache() { + return ErrorCacheFactory.getInstance(); + } + + /** + * protected for unit test overrides. + * @return + */ + protected TravelTimeDetails getLastVehicleTravelTime(VehicleState currentVehicleState, Indices indices) + throws Exception { + return HistoricalPredictionLibrary.getLastVehicleTravelTime(currentVehicleState, indices); + } + + /** + * Current and historical link travel times. + */ + private static class LinkTravelTimes { + private TripSegment lastVehicleSegment; + private TripSegment[] historicalSegments; + public LinkTravelTimes(TripSegment last_vehicle_segment, TripSegment[] historical_segments_k) { + this.lastVehicleSegment = last_vehicle_segment; + this.historicalSegments = historical_segments_k; + } + public TripSegment getLastVehicleSegment() { return lastVehicleSegment; } + public TripSegment[] getHistoricalSegments() { return historicalSegments; } + } +} diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/TrafficDataCache.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/TrafficDataCache.java new file mode 100644 index 000000000..43daba1b8 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/TrafficDataCache.java @@ -0,0 +1,309 @@ +/* + * 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.predictiongenerator.scheduled.traveltime.kalman; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.locationtech.jts.geom.Coordinate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.applications.Core; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.config.StringConfigValue; +import org.transitclock.configData.CoreConfig; +import org.transitclock.db.structs.Location; +import org.transitclock.db.structs.TrafficPath; +import org.transitclock.db.structs.TrafficSensorData; +import org.transitclock.traffic.FeatureGeometry; +import org.transitclock.utils.Geo; +import org.transitclock.utils.IntervalTimer; +import org.transitclock.utils.JsonUtils; +import org.transitclock.utils.Time; +import org.transitclock.utils.Timer; + +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * Poll and cache traffic sensor data. Access via TrafficManager. + */ +public class TrafficDataCache { + + 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=false", + "URL of traffic sensor shapes"); + + private static final StringConfigValue TRAFFIC_URL_WITH_FEATURES + = 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"); + + private static final IntegerConfigValue MAX_TRAFFIC_LATENCY_MINUTES + = new IntegerConfigValue("transitclock.traffic.maxLatency", + 5, + "Max age of traffic data to be considered valid in minuntes"); + + private static final IntegerConfigValue TRAFFIC_REFRESH_RATE_SECONDS + = new IntegerConfigValue("transitclock.traffic.refreshRate", + 60, + "Refresh rate in seconds to poll traffic service"); + + private int getRefreshRateInSeconds() { + return TRAFFIC_REFRESH_RATE_SECONDS.getValue(); + } + + private long getMaxTrafficLatencyMillis() { + return MAX_TRAFFIC_LATENCY_MINUTES.getValue() * Time.MS_PER_MIN; + } + + private static final Logger logger = + LoggerFactory.getLogger(TrafficDataCache.class); + + private Map trafficSensorDataCache = new HashMap<>(); + private TrafficDataMapper mapper; + private Integer trafficRev; + private ScheduledThreadPoolExecutor refreshTimer = Timer.get(); + private TrafficDataHistoricalCache historicalCache; + private boolean enableArchiving = true; + + public TrafficDataCache(TrafficDataHistoricalCache historicalCache, TrafficDataMapper mapper, Integer trafficRev) { + this.historicalCache = historicalCache; + this.mapper = mapper; + this.trafficRev = trafficRev; + if (!TrafficManager.trafficDataEnabled.getValue()) { + // not enabled, don't setup timer thread + logger.info("Traffic data polling disabled"); + return; + } + logger.info("Traffic data polling every {} seconds", + getRefreshRateInSeconds()); + + refreshTimer.scheduleAtFixedRate( + new Runnable() { + @Override + public void run() { + refresh(); + } + } + , 0, getRefreshRateInSeconds(), TimeUnit.SECONDS); + } + + // for testing. + public void setArchiving(boolean flag) { + this.enableArchiving = flag; + } + + /** + * add traffic sensor data to the cache. + * @param mapSensorToStopPath + * @param data + */ + public void cache(TrafficPath mapSensorToStopPath, TrafficSensorData data) { + trafficSensorDataCache.put(mapSensorToStopPath, data); + } + + /** + * retrieve traffic sensor data from the cache. + * @param trafficPath + * @return + */ + public TrafficSensorData get(TrafficPath trafficPath) { + synchronized (trafficSensorDataCache) { + TrafficSensorData data = trafficSensorDataCache.get(trafficPath); + if (data != null && isValid(data)) { + return data; + } + return null; + } + } + + private boolean isValid(TrafficSensorData data) { + return data.getSpeed() != null && !isLatent(data); + } + + boolean isLatent(TrafficSensorData data) { + logger.info(" Oldest acceptable data {} > data date {}", + new Date(System.currentTimeMillis() - getMaxTrafficLatencyMillis()), + new Date(data.getTime().getTime())); + + if (System.currentTimeMillis() - getMaxTrafficLatencyMillis() > data.getTime().getTime()) + return true; // data is too old + return false; + } + + /** + * update the cache on a configurable interval. + */ + private void refresh() { + List sensorData = null; + IntervalTimer loadTimer = new IntervalTimer(); + try { + sensorData = loadData(); + archive(sensorData); + } catch (Exception e) { + logger.error("loading error:", e); + // something went wrong -- no sense in updating our cache + return; + } finally { + logger.info("Traffic data loading and archiving complete in {} msec.", + loadTimer.elapsedMsec()); + } + + IntervalTimer updateTimer = new IntervalTimer(); + synchronized (trafficSensorDataCache) { + updateCache(sensorData); + } + logger.info("Traffic data cache update complete in {} msec, total in {} msec", + updateTimer.elapsedMsec(), + loadTimer.elapsedMsec()); + } + + void updateCache(List sensorData) { + trafficSensorDataCache.clear(); + int mapped = 0; + for (TrafficSensorData data : sensorData) { + TrafficPath trafficPath = mapper.getTrafficPath(data); + if (trafficPath != null) { + mapped++; + cache(trafficPath, data); + } + } + } + + /** + * retrieve the data from the traffic sensors. + * @return + * @throws Exception + */ + List loadData() throws Exception { + List elements = new ArrayList<>(); + + URL urlObj = new URL(TRAFFIC_URL_WITH_FEATURES.getValue()); + URLConnection connection = urlObj.openConnection(); + InputStream in = connection.getInputStream(); + String jsonStr = JsonUtils.getJsonString(in); + + JSONObject descriptor = new JSONObject(jsonStr); + JSONArray features = (JSONArray) descriptor.get("features"); + for (int i = 0; i < features.length(); i++) { + try { + TrafficSensorData data = parseData(features.getJSONObject(i), trafficRev); + if (data != null) { + elements.add(data); + } + } catch (Exception any) { + try { + logger.warn("exception parsing feature {}, {}", features.getJSONObject(i), any, any); + } catch (Exception bury) {} + } + } + return elements; + } + + /** + * parse out data into TTC structs. + * @param o + * @param trafficRev + * @return + */ + private TrafficSensorData parseData(JSONObject o, int trafficRev) { + JSONObject a = o.getJSONObject("attributes"); + if (!a.has("mph")) + return null; + long time = a.getLong("time"); + String externalId = String.valueOf(a.getInt("id")); + double speed = toMetersPerSecond(a.getDouble("mph")); + double delayMillis = a.getDouble("delaySeconds") * 1000; + Integer travelTimeMillis = a.getInt("travelTime"); + double confidence = a.getDouble("confidence"); + Coordinate[] shape = getShape(o.getJSONObject("geometry")); + Double length = getLength(shape); + TrafficSensorData data = new TrafficSensorData( + externalId, + trafficRev, + new Date(time), + speed, + delayMillis, + confidence, + travelTimeMillis, + length + ); + return data; + } + + private Double getLength(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; + } + + private Location toLocation(Coordinate point) { + return new Location(point.x, point.y); + } + + private Coordinate[] getShape(JSONObject geometry) { + List list = new ArrayList<>(); + 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++) { + JSONArray coordinate = perPaths.getJSONArray(i); + list.add(new Coordinate(coordinate.getDouble(1), coordinate.getDouble(0))); + } + return list.toArray(new Coordinate[list.size()]); + } + + private double toMetersPerSecond(double mph) { + return (mph / 2.237); + } + + /** + * The data we retrieve needs to be archived to the database + * (if configured) and copied into the historical cache. + * @param sensorData + */ + void archive(List sensorData) { + if (historicalCache != null) { + // update historical cache + historicalCache.put(sensorData); + } + + if (!enableArchiving) return; + if (!CoreConfig.storeDataInDatabase()) return; + + // flush to the database + for (TrafficSensorData data : sensorData) { + Core.getInstance().getDbLogger().add(data); + } + + } + + +} diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/TrafficDataHistoricalCache.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/TrafficDataHistoricalCache.java new file mode 100644 index 000000000..cfd3fddd7 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/TrafficDataHistoricalCache.java @@ -0,0 +1,119 @@ +/* + * 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.predictiongenerator.scheduled.traveltime.kalman; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.db.structs.StopPath; +import org.transitclock.db.structs.TrafficPath; +import org.transitclock.db.structs.TrafficSensorData; +import org.transitclock.utils.Time; + +import java.util.HashMap; +import java.util.List; + +/** + * Historical cache of traffic sensor data. Access + * via TrafficManager. + */ +public class TrafficDataHistoricalCache { + + private static final Logger logger = + LoggerFactory.getLogger(TrafficDataHistoricalCache.class); + private TrafficDataMapper mapper; + + // TODO this needs to purge based on configuration + // consider guava or equiv impl here + private HashMap cache; + + public TrafficDataHistoricalCache(TrafficDataMapper mapper, Integer trafficRev) { + this.mapper = mapper; + this.cache= new HashMap<>(); // TODO setup expiry here + } + + /** + * Accept traffic data en masse for storage. + * @param data + * @return + */ + public boolean put(List data) { + int mapped = 0; + for (TrafficSensorData element : data) { + boolean success = put(element); + if (success) mapped++; + } + logger.info("mapped {} sensors of {}", + mapped, data.size()); + return mapped > 0; + } + + /** + * Accept traffic for storage. + * @param element + * @return + */ + public boolean put(TrafficSensorData element) { + // we spend the effort now to make the retrieval easy + TrafficDataKey key = hash(element); + if (key != null) { + cache.put(key, element); + return true; + } + return false; + } + /** + * create a key of the data element for fast retrieval. + * @param data + * @return + */ + private TrafficDataKey hash(TrafficSensorData data) { + TrafficPath trafficPath = mapper.getTrafficPath(data); + if (trafficPath == null) return null; + StopPath stopPath = mapper.reverseLookup(trafficPath); + if (stopPath == null) return null; + return new TrafficDataKey(stopPath, data.getTime().getTime()); + } + + /** + * Retrieve traffic sensor data relating to the given StopPath + * at the time. Time is implicitly shifted to the appropriate + * precision. + * @param stopPath + * @param time + * @return + */ + public Long getHistoricalTravelTime(StopPath stopPath, long time) { + TrafficDataKey cacheKey = new TrafficDataKey(stopPath, time); + TrafficSensorData trafficSensorData = cache.get(cacheKey); + if (trafficSensorData == null) { + // try once more for the previous minute + time = time - Time.MS_PER_MIN; + cacheKey = new TrafficDataKey(stopPath, time); + trafficSensorData = cache.get(cacheKey); + } + if (trafficSensorData != null) { + double speed = trafficSensorData.getSpeed(); + // time = velocity * length + return new Double(stopPath.getLength() * speed).longValue(); + } + return null; + + } + + + +} diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/TrafficDataKey.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/TrafficDataKey.java new file mode 100644 index 000000000..03adfc399 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/TrafficDataKey.java @@ -0,0 +1,76 @@ +/* + * 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.predictiongenerator.scheduled.traveltime.kalman; + +import org.transitclock.db.structs.StopPath; +import org.transitclock.utils.Time; + +import java.util.Objects; + +/** + * Cache key for TrafficSensorData. + */ +public class TrafficDataKey { + private StopPath stopPath; + private Long time; + + public TrafficDataKey(StopPath stopPath, + Long time) { + this.stopPath = stopPath; + this.time = shave(time); + } + + public Long getTime() { + return time; + } + + public StopPath getStopPath() { + return stopPath; + } + + // shave precision to the minute + Long shave(Long time) { + long seconds = time / Time.MS_PER_SEC; + long minutes = seconds / Time.SEC_PER_MIN; + + return minutes * Time.MS_PER_MIN; + } + + @Override + public boolean equals(Object otherObject) { + if (this == otherObject) { + return true; + } + if (!(otherObject instanceof TrafficDataKey)) { + return false; + } + + TrafficDataKey otherKey = (TrafficDataKey) otherObject; + return Objects.equals(this.stopPath, otherKey.stopPath) + && Objects.equals(this.time, otherKey.time); + } + + + @Override + public int hashCode() { + int result = 17; + result = 37 * result + Objects.hashCode(stopPath); + result = 37 * result + Objects.hashCode(time); + + return result; + } +} diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/TrafficDataMapper.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/TrafficDataMapper.java new file mode 100644 index 000000000..d034f2b77 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/TrafficDataMapper.java @@ -0,0 +1,58 @@ +/* + * 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.predictiongenerator.scheduled.traveltime.kalman; + +import org.transitclock.db.structs.StopPath; +import org.transitclock.db.structs.TrafficPath; +import org.transitclock.db.structs.TrafficSensorData; + +import java.util.HashMap; +import java.util.Map; + +/** + * Maintain a mapping of Traffic Sensor data to TrafficPaths and + * StopPaths for fast retrieval. + */ +public class TrafficDataMapper { + + private Map stopPathTrafficPathMap = new HashMap<>(); + private Map trafficPathStopPathMap = new HashMap<>(); + private Map sensorIdToTrafficPath = new HashMap<>(); + + public void addTrafficPath(TrafficPath trafficPath) { + this.sensorIdToTrafficPath.put(trafficPath.getTrafficPathId(), trafficPath); + } + + public TrafficPath getTrafficPath(TrafficSensorData data) { + return sensorIdToTrafficPath.get(data.getTrafficSensorId()); + } + + public boolean has(StopPath stopPath) { + return stopPathTrafficPathMap.containsKey(stopPath); + } + + public void mapStopPath(StopPath stopPath, TrafficPath trafficPath) { + this.stopPathTrafficPathMap.put(stopPath, trafficPath); + this.trafficPathStopPathMap.put(trafficPath, stopPath); + } + + public TrafficPath get(StopPath stopPath) { + return stopPathTrafficPathMap.get(stopPath); + } + + public StopPath reverseLookup(TrafficPath trafficPath) { return trafficPathStopPathMap.get(trafficPath); } +} diff --git a/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/TrafficManager.java b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/TrafficManager.java new file mode 100644 index 000000000..a4800a62a --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/TrafficManager.java @@ -0,0 +1,228 @@ +/* + * 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.predictiongenerator.scheduled.traveltime.kalman; + +import org.hibernate.Session; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.applications.Core; +import org.transitclock.config.BooleanConfigValue; +import org.transitclock.db.structs.ActiveRevisions; +import org.transitclock.db.structs.StopPath; +import org.transitclock.db.structs.TrafficPath; +import org.transitclock.db.structs.TrafficSensorData; + +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +/** + * Manage traffic data access, retrieval, and storage; + */ +public class TrafficManager { + + public static final BooleanConfigValue trafficDataEnabled + = new BooleanConfigValue("transitclock.traffic.enabled", + false, + "Enable Traffic Data integration"); + + private static final Logger logger = + LoggerFactory.getLogger(TrafficManager.class); + + private static TrafficManager INSTANCE; + private TrafficDataMapper mapper; + private TrafficDataCache cache; + private TrafficDataHistoricalCache historicalCache; + + private Integer trafficRev = null; + + + /** + * Static access via getInstance() as is the convention with TTC. + */ + protected TrafficManager() { + } + + public static TrafficManager getInstance() { + if (INSTANCE == null) { + INSTANCE = new TrafficManager(); + INSTANCE.init(); + } + return INSTANCE; + } + + private boolean testingEnable = false; + public void setEnabled(boolean b) { + testingEnable = true; + } + public boolean isEnabled() { + return testingEnable || trafficDataEnabled.getValue(); + } + + /** + * for testing. + * @param mapper + */ + public void setMapper(TrafficDataMapper mapper) { + this.mapper = mapper; + } + /** + * for testing. + * @param trafficRev + */ + public void setTrafficRev(int trafficRev) { + this.trafficRev = trafficRev; + } + // for testing. + public void setCache(TrafficDataCache cache) { + this.cache = cache; + } + // for testing. + public void setHistoricalCache(TrafficDataHistoricalCache historicalCache) { + this.historicalCache = historicalCache; + } + + /** + * load internal state and setup the caches. + */ + private void init() { + Session session = Core.getInstance().getDbConfig().getGlobalSession(); + ActiveRevisions activeRevisions = ActiveRevisions.get(session); + + trafficRev = activeRevisions.getTrafficRev(); + + mapper = new TrafficDataMapper(); + if (trafficRev == null) { + // no traffic data -- nothing to do + return; + } + + // setup mapping for fast retrieval + List allTrafficPaths = TrafficPath.getTrafficPaths(session, trafficRev); + for (TrafficPath trafficPath : allTrafficPaths) { + mapper.addTrafficPath(trafficPath); + for (StopPath stopPath : trafficPath.getStopPaths()) { + if (mapper.has(stopPath)){ + logger.info("ignoring duplicate stopPath {} to tripPath {} mapping", + stopPath, trafficPath); + } else { + mapper.mapStopPath(stopPath, trafficPath); + } + } + } + + // initialize our caches + historicalCache = new TrafficDataHistoricalCache(mapper, trafficRev); + cache = new TrafficDataCache(historicalCache, mapper, trafficRev); + + } + + + public boolean hasTrafficData(StopPath stopPath) { + if (!isEnabled()) return false; + return mapper.has(stopPath); + } + + /** + * Retrieve the current travel time in millis for the given + * StopPath. + * @param stopPath + * @return + */ + public Long getTravelTime(StopPath stopPath) { + if (!isEnabled()) return null; + TrafficPath trafficPath = mapper.get(stopPath); + TrafficSensorData data = getTrafficSensorData(trafficPath); + if (data == null) return null; + if (data.getSpeed() == null) return null; + // time = distance(m) / velocity (m/s) + return new Double(stopPath.getLength() + / data.getSpeed()).longValue() * 1000; + } + + public TrafficSensorData getTrafficSensorDataForStopPath(StopPath stopPath) { + if (!isEnabled()) return null; + TrafficPath trafficPath = mapper.get(stopPath); + return getTrafficSensorData(trafficPath); + } + + public Double getSpeed(StopPath stopPath) { + if (!isEnabled()) return null; + TrafficPath trafficPath = mapper.get(stopPath); + TrafficSensorData data = getTrafficSensorData(trafficPath); + if (data == null) return null; + return data.getSpeed(); + } + + /** + * estimated length of traffic sensor. + * @param stopPath + * @return + */ + public Double getLength(StopPath stopPath) { + if (!isEnabled()) return null; + TrafficPath trafficPath = mapper.get(stopPath); + TrafficSensorData data = getTrafficSensorData(trafficPath); + if (data == null) return null; + return data.getLength(); + } + + public Integer getTotalTravelTime(StopPath stopPath) { + if (!isEnabled()) return null; + TrafficPath trafficPath = mapper.get(stopPath); + TrafficSensorData data = getTrafficSensorData(trafficPath); + if (data == null) return null; + return data.getTravelTimeMillis(); + } + + private TrafficSensorData getTrafficSensorData(TrafficPath trafficPath) { + if (!isEnabled()) return null; + return cache.get(trafficPath); + } + + /** + * Retrieve the travel time in millis for the StopPath at the given + * time period, if it exists. The precision of thetime parameter is implicitly + * shifted to the appropriate value. + * @param stopPath + * @param time + * @return + */ + public Long getHistoricalTravelTime (StopPath stopPath, long time) { + if (!isEnabled()) return null; + return historicalCache.getHistoricalTravelTime(stopPath, time); + } + +/** + * load data on startup populating internal caches. + */ + public void populateCacheFromDb(Session session, Date startDate, Date endDate) { + + List list = TrafficSensorData.getTrafficSensorDataFromDb(session, startDate, endDate); + Iterator iterator = list.iterator(); + + int i = 0; + while (iterator.hasNext()) { + i++; + if (i % 1000 == 0) { + logger.info("loaded {} traffic sensors for {}", i, startDate); + } + TrafficSensorData element = iterator.next(); + historicalCache.put(element); + } + } +} diff --git a/transitclock/src/main/java/org/transitclock/core/reporting/RunTimeGenerator.java b/transitclock/src/main/java/org/transitclock/core/reporting/RunTimeGenerator.java new file mode 100755 index 000000000..7d8d1521c --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/core/reporting/RunTimeGenerator.java @@ -0,0 +1,196 @@ +package org.transitclock.core.reporting; + +import org.apache.commons.lang3.time.DateUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.applications.Core; +import org.transitclock.core.ServiceType; +import org.transitclock.core.ServiceUtils; +import org.transitclock.core.SpatialMatch; +import org.transitclock.core.VehicleState; +import org.transitclock.core.dataCache.*; +import org.transitclock.db.structs.*; +import org.transitclock.ipc.data.IpcArrivalDeparture; +import org.transitclock.utils.IntervalTimer; + +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +public class RunTimeGenerator { + + private static final Logger logger = + LoggerFactory.getLogger(RunTimeGenerator.class); + + public boolean generate(VehicleState vehicleState) { + + try { + SpatialMatch matchAtPreviousStop = vehicleState.getMatch().getMatchAtPreviousStop(); + Integer lastStopIndex; + if(matchAtPreviousStop==null || (lastStopIndex = atEndOfTrip(matchAtPreviousStop)) == null) { + return false; + } + + String tripId = matchAtPreviousStop.getAtStop().getTrip().getId(); + long vehicleMatchAvlTime = vehicleState.getMatch().getAvlTime(); + String vehicleId= vehicleState.getVehicleId(); + Integer tripStartTime = matchAtPreviousStop.getTrip().getStartTime(); + + + List arrivalDeparturesForStop = getArrivalDeparturesForTrip(tripId, + vehicleMatchAvlTime, tripStartTime); + + if(arrivalDeparturesForStop!=null) + { + boolean isValid = processRunTimesForEndOfTrip( vehicleId, + arrivalDeparturesForStop, + matchAtPreviousStop, + lastStopIndex + ); + return isValid; + } + } catch (Exception e) { + logger.error("Exception when processing run times", e); + } + return false; + } + + public List getArrivalDeparturesForTrip(String tripId, long vehicleMatchAvlTime, Integer startTime){ + Date nearestDay = DateUtils.truncate(new Date(vehicleMatchAvlTime), Calendar.DAY_OF_MONTH); + TripKey key=new TripKey(tripId, nearestDay, startTime); + return TripDataHistoryCacheFactory.getInstance().getTripHistory(key); + } + + public boolean processRunTimesForEndOfTrip( String vehicleId, + List arrivalDeparturesForStop, + SpatialMatch matchAtPreviousStop, + Integer lastStopIndex){ + + + + if(!isRunTimeValid(arrivalDeparturesForStop, lastStopIndex)){ + return false; + } + + Trip trip = matchAtPreviousStop.getTrip(); + int dwellTimeCount = lastStopIndex; + Long totalDwellTime = null; + Date finalStopArrivalTime = null; + + IntervalTimer timer = new IntervalTimer(); + for(int i=0;i arrivalDeparturesForStop, int lastStopIndex){ + if(arrivalDeparturesForStop == null || + arrivalDeparturesForStop.size() < 2 || + arrivalDeparturesForStop.get(0).getStopPathIndex() != lastStopIndex || + arrivalDeparturesForStop.get(arrivalDeparturesForStop.size()-1).getStopPathIndex() != 0){ + return false; + } + return true; + } + + private boolean isSpatialMatchAndArrivalDepartureMatch(IpcArrivalDeparture arrivalDeparture, + SpatialMatch matchAtPreviousStop, + String vehicleId){ + return arrivalDeparture.getTripId().equals(matchAtPreviousStop.getTrip().getId()) && + arrivalDeparture.getVehicleId().equals(vehicleId); + //matchAtPreviousStop.getTripIndex() == arrivalDeparture.getTripIndex(); + } + + private boolean isLastStopForTrip(Integer currentStopPathIndex, Integer lastStopIndex){ + return currentStopPathIndex == lastStopIndex; + } + + private boolean isMiddleStopForTrip(Integer currentStopPathIndex, Integer lastStopIndex){ + return currentStopPathIndex > 0 && currentStopPathIndex < lastStopIndex; + } + + private boolean isFirstStopForTrip(Integer currentStopPathIndex){ + return currentStopPathIndex == 0; + } + + public Integer atEndOfTrip(SpatialMatch matchAtPreviousStop) { + Block block = matchAtPreviousStop.getBlock(); + int tripIndex = matchAtPreviousStop.getTripIndex(); + int stopPathIndex = matchAtPreviousStop.getStopPathIndex(); + if(stopPathIndex == block.numStopPaths(tripIndex) - 1){ + return stopPathIndex; + } + return null; + } + + public Integer getNextTripStartTime(Trip trip){ + if(trip.getBlock().getTrip(trip.getIndexInBlock() + 1) != null){ + return trip.getStartTime(); + } + return null; + } +} diff --git a/transitime/src/main/java/org/transitime/core/schedBasedPreds/SchedBasedPredsModule.java b/transitclock/src/main/java/org/transitclock/core/schedBasedPreds/SchedBasedPredsModule.java similarity index 78% rename from transitime/src/main/java/org/transitime/core/schedBasedPreds/SchedBasedPredsModule.java rename to transitclock/src/main/java/org/transitclock/core/schedBasedPreds/SchedBasedPredsModule.java index 1b155bbf3..3a954d208 100644 --- a/transitime/src/main/java/org/transitime/core/schedBasedPreds/SchedBasedPredsModule.java +++ b/transitclock/src/main/java/org/transitclock/core/schedBasedPreds/SchedBasedPredsModule.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.core.schedBasedPreds; +package org.transitclock.core.schedBasedPreds; import java.util.Collection; import java.util.HashSet; @@ -24,24 +24,24 @@ 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.configData.AgencyConfig; -import org.transitime.core.AvlProcessor; -import org.transitime.core.BlocksInfo; -import org.transitime.core.VehicleState; -import org.transitime.core.dataCache.VehicleDataCache; -import org.transitime.db.structs.AvlReport; -import org.transitime.db.structs.Block; -import org.transitime.db.structs.Location; -import org.transitime.db.structs.AvlReport.AssignmentType; -import org.transitime.ipc.data.IpcVehicle; -import org.transitime.ipc.data.IpcVehicleComplete; -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.AvlProcessor; +import org.transitclock.core.BlocksInfo; +import org.transitclock.core.VehicleState; +import org.transitclock.core.dataCache.VehicleDataCache; +import org.transitclock.db.structs.AvlReport; +import org.transitclock.db.structs.Block; +import org.transitclock.db.structs.Location; +import org.transitclock.db.structs.AvlReport.AssignmentType; +import org.transitclock.ipc.data.IpcVehicle; +import org.transitclock.ipc.data.IpcVehicleComplete; +import org.transitclock.logging.Markers; +import org.transitclock.modules.Module; +import org.transitclock.utils.IntervalTimer; +import org.transitclock.utils.Time; /** * The schedule based predictions module runs in the background. Every few @@ -58,7 +58,7 @@ *

* Schedule based predictions are removed once a regular vehicle is assigned to * the block or the schedule based vehicle is timed out via TimeoutHandlerModule - * due to it being transitime.timeout.allowableNoAvlForSchedBasedPredictions + * due to it being transitclock.timeout.allowableNoAvlForSchedBasedPredictions * after the scheduled departure time for the assignment. * * @author SkiBu Smith @@ -73,13 +73,13 @@ public class SchedBasedPredsModule extends Module { private static final IntegerConfigValue timeBetweenPollingMsec = new IntegerConfigValue( - "transitime.schedBasedPreds.pollingRateMsec", + "transitclock.schedBasedPreds.pollingRateMsec", 4 * Time.MS_PER_MIN, "How frequently to look for blocks that do not have " + "associated vehicle."); private static final BooleanConfigValue processImmediatelyAtStartup = - new BooleanConfigValue("transitime.schedBasedPreds.processImmediatelyAtStartup", + new BooleanConfigValue("transitclock.schedBasedPreds.processImmediatelyAtStartup", false, "Whether should start creating schedule based predictions " + "right at startup. Usually want to give AVL data a " @@ -90,13 +90,13 @@ public class SchedBasedPredsModule extends Module { private static final IntegerConfigValue beforeStartTimeMinutes = new IntegerConfigValue( - "transitime.schedBasedPreds.beforeStartTimeMinutes", 60, + "transitclock.schedBasedPreds.beforeStartTimeMinutes", 60, "How many minutes before a block start time should create " + "a schedule based vehicle for that block."); private static IntegerConfigValue afterStartTimeMinutes = new IntegerConfigValue( - "transitime.schedBasedPreds.afterStartTimeMinutes", + "transitclock.schedBasedPreds.afterStartTimeMinutes", 8, // Can take a while to automatically assign a vehicle "If predictions created for a block based on the schedule " + "will remove those predictions this specified " @@ -111,7 +111,14 @@ public class SchedBasedPredsModule extends Module { + "important when using the automatic assignment method " + "because it can take a few minutes."); - + //ADD IN ORDER TO MANAGE CACELLED TRIPS + private static final BooleanConfigValue cancelTripOnTimeout = + new BooleanConfigValue("transitclock.schedBasedPreds.cancelTripOnTimeout", + true, + "Whether should mark a ScheduleBasePred as canceled." + + "This won't remove a trip after afterStartTimeMinutes. Instead it will" + + " change the state to cancelled."); + /********************** Member Functions **************************/ /** @@ -168,21 +175,24 @@ private void createSchedBasedPredsAsNecessary() { .getEpochTime(block.getStartTime(), referenceTime); Location location = block.getStartLoc(); - AvlReport avlReport = - new AvlReport(vehicleId, blockStartEpochTime, location, - "Schedule"); - - // Set the block assignment for the AVL report and indicate - // that it is for creating scheduled based predictions - avlReport.setAssignment(block.getId(), - AssignmentType.BLOCK_FOR_SCHED_BASED_PREDS); - - logger.info("Creating a schedule based vehicle for blockId={}. " - + "The fake AVL report is {}. The block is {}", - block.getId(), avlReport, block.toShortString()); - - // Process that AVL report to generate predictions and such - AvlProcessor.getInstance().processAvlReport(avlReport); + if(location!=null) + { + AvlReport avlReport = + new AvlReport(vehicleId, blockStartEpochTime, location, + "Schedule"); + + // Set the block assignment for the AVL report and indicate + // that it is for creating scheduled based predictions + avlReport.setAssignment(block.getId(), + AssignmentType.BLOCK_FOR_SCHED_BASED_PREDS); + + logger.info("Creating a schedule based vehicle for blockId={}. " + + "The fake AVL report is {}. The block is {}", + block.getId(), avlReport, block.toShortString()); + + // Process that AVL report to generate predictions and such + AvlProcessor.getInstance().processAvlReport(avlReport); + } } } } @@ -252,7 +262,18 @@ public static String shouldTimeoutVehicle(VehicleState vehicleState, long now) { + Time.dateTimeStr(scheduledDepartureTime) + " while allowable time without an AVL report is " + Time.elapsedTimeStr(maxNoAvl) + "."; - return shouldTimeoutEventDescription; + if(!cancelTripOnTimeout.getValue()) + return shouldTimeoutEventDescription; + else if(!vehicleState.isCanceled() && cancelTripOnTimeout.getValue())//TODO: Check if it works on state changed + { + logger.info("Canceling trip..."); + vehicleState.setCanceled(true); + VehicleDataCache.getInstance().updateVehicle(vehicleState); + AvlReport avlReport=vehicleState.getAvlReport(); + AvlProcessor.getInstance().processAvlReport(avlReport); + + + } } } } diff --git a/transitime/src/main/java/org/transitime/core/schedBasedPreds/package-info.java b/transitclock/src/main/java/org/transitclock/core/schedBasedPreds/package-info.java similarity index 97% rename from transitime/src/main/java/org/transitime/core/schedBasedPreds/package-info.java rename to transitclock/src/main/java/org/transitclock/core/schedBasedPreds/package-info.java index e1f62b4cf..ad61468ae 100644 --- a/transitime/src/main/java/org/transitime/core/schedBasedPreds/package-info.java +++ b/transitclock/src/main/java/org/transitclock/core/schedBasedPreds/package-info.java @@ -36,4 +36,4 @@ * @author SkiBu Smith * */ -package org.transitime.core.schedBasedPreds; \ No newline at end of file +package org.transitclock.core.schedBasedPreds; \ No newline at end of file diff --git a/transitime/src/main/java/org/transitime/core/travelTimes/DataFetcher.java b/transitclock/src/main/java/org/transitclock/core/travelTimes/DataFetcher.java similarity index 67% rename from transitime/src/main/java/org/transitime/core/travelTimes/DataFetcher.java rename to transitclock/src/main/java/org/transitclock/core/travelTimes/DataFetcher.java index baafe7db0..392064e2c 100644 --- a/transitime/src/main/java/org/transitime/core/travelTimes/DataFetcher.java +++ b/transitclock/src/main/java/org/transitclock/core/travelTimes/DataFetcher.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.core.travelTimes; +package org.transitclock.core.travelTimes; import java.util.ArrayList; import java.util.Date; @@ -27,13 +27,15 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.db.structs.ActiveRevisions; -import org.transitime.db.structs.Agency; -import org.transitime.db.structs.ArrivalDeparture; -import org.transitime.db.structs.Match; -import org.transitime.utils.IntervalTimer; -import org.transitime.utils.MapKey; -import org.transitime.utils.Time; +import org.transitclock.config.BooleanConfigValue; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.db.structs.ActiveRevisions; +import org.transitclock.db.structs.Agency; +import org.transitclock.db.structs.ArrivalDeparture; +import org.transitclock.db.structs.Match; +import org.transitclock.utils.IntervalTimer; +import org.transitclock.utils.MapKey; +import org.transitclock.utils.Time; /** * For retrieving historic AVL based data from database so that travel times can @@ -44,6 +46,23 @@ */ public class DataFetcher { + private static boolean pageDbReads() { + return pageDbReads.getValue(); + } + private static BooleanConfigValue pageDbReads = + new BooleanConfigValue("transitclock.updates.pageDbReads", + true, + "page database reads to break up long reads. " + + "It may impact performance on MySql" + ); + private static Integer pageSize() { + return pageSize.getValue(); + } + private static IntegerConfigValue pageSize = + new IntegerConfigValue("transitclock.updates.pageSize", + 50000, + "Number of records to read in at a time"); + // The data ends up in arrivalDepartureMap and matchesMap. // It is keyed by DbDataMapKey which means that data is grouped // per vehicle trip. This way can later subsequent arrivals/departures @@ -234,32 +253,67 @@ public Map> readArrivalsDepartures( // Batch size of 50k found to be significantly faster than 10k, // by about a factor of 2. Since sometimes using really large // batches of data using 500k - int batchSize = 500000; // Also known as maxResults + int batchSize = pageSize.getValue(); // Also known as maxResults // The temporary list for the loop that contains a batch of results + + logger.info("counting arrival/departures"); + Long count = ArrivalDeparture.getArrivalsDeparturesCountFromDb(dbName, beginTime, endTime, null, false); + logger.info("retrieving {} arrival/departures", count); List arrDepBatchList; - // Read in batch of 50k rows of data and process it - do { - arrDepBatchList = ArrivalDeparture.getArrivalsDeparturesFromDb( - dbName, - beginTime, endTime, - // Order results by time so that process them in the same - // way that a vehicle travels. - "ORDER BY time", // SQL clause - firstResult, batchSize, - null); // arrivalOrDeparture. Null means read in both - - // Add arrivals/departures to map - for (ArrivalDeparture arrDep : arrDepBatchList) { - addArrivalDepartureToMap(resultsMap, arrDep); - } - - logger.info("Read in total of {} arrival/departures", - firstResult+arrDepBatchList.size()); - - // Update firstResult for reading next batch of data - firstResult += batchSize; - } while (arrDepBatchList.size() == batchSize); + + if (!pageDbReads()) { + // page by day for MySql -- its batch impl falls down on large data + Date pageBeginTime = beginTime; + Date pageEndTime = new Date(beginTime.getTime() + Time.MS_PER_DAY); + int runningCount = 0; + do { + logger.info("querying a/d for between {} and {}", pageBeginTime, pageEndTime); + arrDepBatchList = ArrivalDeparture.getArrivalsDeparturesFromDb( + dbName, + pageBeginTime, pageEndTime, + // Order results by time so that process them in the same + // way that a vehicle travels. + "ORDER BY time", // SQL clause + null, null, + null, // arrivalOrDeparture. Null means read in both + false); + + // Add arrivals/departures to map + for (ArrivalDeparture arrDep : arrDepBatchList) { + addArrivalDepartureToMap(resultsMap, arrDep); + } + runningCount += arrDepBatchList.size(); + logger.info("Read in total of {} a/ds of {} {}%", + runningCount, count, (0.0+runningCount)/count*100); + pageBeginTime = pageEndTime; + pageEndTime = new Date(Math.min(pageBeginTime.getTime() + Time.MS_PER_DAY, endTime.getTime())); + + } while (pageEndTime.before(endTime)); + } else { + // Read in batch of 50k rows of data and process it + do { + arrDepBatchList = ArrivalDeparture.getArrivalsDeparturesFromDb( + dbName, + beginTime, endTime, + // Order results by time so that process them in the same + // way that a vehicle travels. + "ORDER BY time", // SQL clause + firstResult, batchSize, + null, // arrivalOrDeparture. Null means read in both + false); + // Add arrivals/departures to map + for (ArrivalDeparture arrDep : arrDepBatchList) { + addArrivalDepartureToMap(resultsMap, arrDep); + } + + logger.info("Read in total of {} arrival/departures of {} {}%", + firstResult+arrDepBatchList.size(), count, (0.0+firstResult+arrDepBatchList.size())/count*100); + + // Update firstResult for reading next batch of data + firstResult += batchSize; + } while (arrDepBatchList.size() == batchSize); + } logger.info("Reading arrival/departures took {} msec", timer.elapsedMsec()); @@ -305,33 +359,60 @@ private Map> readMatches( // Batch size of 50k found to be significantly faster than 10k, // by about a factor of 2. Since sometimes using really large // batches of data using 500k - int batchSize = 500000; // Also known as maxResults + int batchSize = pageSize.getValue(); // Also known as maxResults + String sqlClause = "AND atStop = false ORDER BY avlTime"; + logger.info("counting matches..."); + Long count = Match.getMatchesCountFromDb(projectId, beginTime, endTime, "AND atStop = false"); + logger.info("found {} matches", count); + // The temporary list for the loop that contains a batch of results List matchBatchList; + + if (!pageDbReads()) { + // page by day for MySql -- its batch impl falls down on large data + Date pageBeginTime = beginTime; + Date pageEndTime = new Date(beginTime.getTime() + Time.MS_PER_DAY); + int runningCount = 0; + do { + logger.info("querying matches for between {} and {}", pageBeginTime, pageEndTime); + matchBatchList = Match.getMatchesFromDb(projectId, pageBeginTime, pageEndTime, sqlClause, null, null); + // Add arrivals/departures to map + for (Match match : matchBatchList) { + addMatchToMap(resultsMap, match); + } + runningCount += matchBatchList.size(); + logger.info("Read in total of {} matches of {} {}%", + runningCount, count, (0.0+runningCount)/count*100); + + pageBeginTime = pageEndTime; + pageEndTime = new Date(Math.min(pageBeginTime.getTime() + Time.MS_PER_DAY, endTime.getTime())); + + } while (pageEndTime.before(endTime)); + } else { // Read in batch of 50k rows of data and process it - do { - matchBatchList = Match.getMatchesFromDb( - projectId, - beginTime, endTime, - // Only want matches that are not at a stop since for that - // situation instead using arrivals/departures. - // Order results by time so that process them in the same - // way that a vehicle travels. - "AND atStop = false ORDER BY avlTime", // SQL clause - firstResult, batchSize); - - // Add arrivals/departures to map - for (Match match : matchBatchList) { - addMatchToMap(resultsMap, match); + do { + matchBatchList = Match.getMatchesFromDb( + projectId, + beginTime, endTime, + // Only want matches that are not at a stop since for that + // situation instead using arrivals/departures. + // Order results by time so that process them in the same + // way that a vehicle travels. + sqlClause, // SQL clause + firstResult, batchSize); + + // Add arrivals/departures to map + for (Match match : matchBatchList) { + addMatchToMap(resultsMap, match); + } + + logger.info("Read in total of {} matches of {} {}%", + firstResult+matchBatchList.size(), count, (0.0+firstResult+matchBatchList.size())/count*100); + + // Update firstResult for reading next batch of data + firstResult += batchSize; + } while (matchBatchList.size() == batchSize); } - - logger.info("Read in total of {} matches", - firstResult+matchBatchList.size()); - - // Update firstResult for reading next batch of data - firstResult += batchSize; - } while (matchBatchList.size() == batchSize); - logger.info("Reading matches took {} msec", timer.elapsedMsec()); // Return the resulting map of arrivals/departures @@ -351,6 +432,10 @@ public void readData(String agencyId, Date beginTime, // Read in arrival/departure times and matches from db logger.info("Reading historic data from db..."); matchesMap = readMatches(agencyId, beginTime, endTime); + if (matchesMap == null || matchesMap.isEmpty()) { + logger.info("No Matches present in db"); + return; + } arrivalDepartureMap = readArrivalsDepartures(agencyId, beginTime, endTime); } diff --git a/transitime/src/main/java/org/transitime/core/travelTimes/TravelTimeInfo.java b/transitclock/src/main/java/org/transitclock/core/travelTimes/TravelTimeInfo.java similarity index 78% rename from transitime/src/main/java/org/transitime/core/travelTimes/TravelTimeInfo.java rename to transitclock/src/main/java/org/transitclock/core/travelTimes/TravelTimeInfo.java index 774e9e3b3..4c320c7c8 100644 --- a/transitime/src/main/java/org/transitime/core/travelTimes/TravelTimeInfo.java +++ b/transitclock/src/main/java/org/transitclock/core/travelTimes/TravelTimeInfo.java @@ -15,12 +15,12 @@ * along with Transitime.org . If not, see . */ -package org.transitime.core.travelTimes; +package org.transitclock.core.travelTimes; import java.util.List; -import org.transitime.db.structs.Trip; -import org.transitime.utils.Geo; +import org.transitclock.db.structs.Trip; +import org.transitclock.utils.Geo; /** * For holding the GPS based travel times for each trip/stop path. Contains @@ -89,13 +89,32 @@ public int getStopTime() { } public boolean isStopTimeValid() { - return stopTime != STOP_TIME_NOT_VALID; + return stopTime != STOP_TIME_NOT_VALID && stopTime >= 0; } - + + public boolean isStopTimeValid(int maxStopTime) { + return stopTime != STOP_TIME_NOT_VALID && stopTime >= 0 && stopTime < maxStopTime; + } + public boolean areTravelTimesValid() { - return travelTimes != null && !travelTimes.isEmpty(); + if (travelTimes == null || travelTimes.isEmpty()) + return false; + for (int time : travelTimes) + if (time < 0) + return false; + return true; } - + + public boolean areTravelTimesValid(int maxTravelTime) { + if (travelTimes == null || travelTimes.isEmpty()) + return false; + for (int time : travelTimes) + if (time < 0 || time > maxTravelTime) + return false; + return true; + } + + public double getTravelTimeSegLength() { return travelTimeSegLength; } diff --git a/transitime/src/main/java/org/transitime/core/travelTimes/TravelTimeInfoMap.java b/transitclock/src/main/java/org/transitclock/core/travelTimes/TravelTimeInfoMap.java similarity index 97% rename from transitime/src/main/java/org/transitime/core/travelTimes/TravelTimeInfoMap.java rename to transitclock/src/main/java/org/transitclock/core/travelTimes/TravelTimeInfoMap.java index 24b338b3a..bd7fe4cec 100644 --- a/transitime/src/main/java/org/transitime/core/travelTimes/TravelTimeInfoMap.java +++ b/transitclock/src/main/java/org/transitclock/core/travelTimes/TravelTimeInfoMap.java @@ -15,16 +15,16 @@ * along with Transitime.org . If not, see . */ -package org.transitime.core.travelTimes; +package org.transitclock.core.travelTimes; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import org.transitime.db.structs.TravelTimesForStopPath.HowSet; -import org.transitime.db.structs.Trip; -import org.transitime.utils.Time; +import org.transitclock.db.structs.Trip; +import org.transitclock.db.structs.TravelTimesForStopPath.HowSet; +import org.transitclock.utils.Time; /** * For keeping track of the historic data such that if no data is available for diff --git a/transitime/src/main/java/org/transitime/core/travelTimes/TravelTimeInfoWithHowSet.java b/transitclock/src/main/java/org/transitclock/core/travelTimes/TravelTimeInfoWithHowSet.java similarity index 92% rename from transitime/src/main/java/org/transitime/core/travelTimes/TravelTimeInfoWithHowSet.java rename to transitclock/src/main/java/org/transitclock/core/travelTimes/TravelTimeInfoWithHowSet.java index 7af0d70c7..a9548cc96 100644 --- a/transitime/src/main/java/org/transitime/core/travelTimes/TravelTimeInfoWithHowSet.java +++ b/transitclock/src/main/java/org/transitclock/core/travelTimes/TravelTimeInfoWithHowSet.java @@ -15,12 +15,12 @@ * along with Transitime.org . If not, see . */ -package org.transitime.core.travelTimes; +package org.transitclock.core.travelTimes; import java.util.List; -import org.transitime.db.structs.Trip; -import org.transitime.db.structs.TravelTimesForStopPath.HowSet; +import org.transitclock.db.structs.Trip; +import org.transitclock.db.structs.TravelTimesForStopPath.HowSet; /** * Extends TravelTimeInfo class but adds how the travel time was set. diff --git a/transitime/src/main/java/org/transitime/core/travelTimes/TravelTimesProcessor.java b/transitclock/src/main/java/org/transitclock/core/travelTimes/TravelTimesProcessor.java old mode 100644 new mode 100755 similarity index 88% rename from transitime/src/main/java/org/transitime/core/travelTimes/TravelTimesProcessor.java rename to transitclock/src/main/java/org/transitclock/core/travelTimes/TravelTimesProcessor.java index 31ef1dd5b..bd8b3f83b --- a/transitime/src/main/java/org/transitime/core/travelTimes/TravelTimesProcessor.java +++ b/transitclock/src/main/java/org/transitclock/core/travelTimes/TravelTimesProcessor.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.core.travelTimes; +package org.transitclock.core.travelTimes; import java.util.ArrayList; import java.util.Collection; @@ -26,22 +26,24 @@ import java.util.Map; import java.util.Set; +import org.hibernate.Session; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.config.BooleanConfigValue; -import org.transitime.config.DoubleConfigValue; -import org.transitime.core.TemporalDifference; -import org.transitime.core.travelTimes.DataFetcher.DbDataMapKey; -import org.transitime.db.structs.ArrivalDeparture; -import org.transitime.db.structs.Match; -import org.transitime.db.structs.StopPath; -import org.transitime.db.structs.Trip; -import org.transitime.statistics.Statistics; -import org.transitime.utils.Geo; -import org.transitime.utils.IntervalTimer; -import org.transitime.utils.MapKey; -import org.transitime.utils.StringUtils; -import org.transitime.utils.Time; +import org.transitclock.config.BooleanConfigValue; +import org.transitclock.config.DoubleConfigValue; +import org.transitclock.core.TemporalDifference; +import org.transitclock.core.travelTimes.DataFetcher.DbDataMapKey; +import org.transitclock.db.structs.ArrivalDeparture; +import org.transitclock.db.structs.Match; +import org.transitclock.db.structs.StopPath; +import org.transitclock.db.structs.Trip; +import org.transitclock.monitoring.MonitoringService; +import org.transitclock.statistics.Statistics; +import org.transitclock.utils.Geo; +import org.transitclock.utils.IntervalTimer; +import org.transitclock.utils.MapKey; +import org.transitclock.utils.StringUtils; +import org.transitclock.utils.Time; import com.amazonaws.services.importexport.model.InvalidParameterException; @@ -63,16 +65,7 @@ */ public class TravelTimesProcessor { - // For when determining travel times for segments. Throws out - // outliers if they are less than 0.7 or greater than 1/0.7 - // of the average. - private final static double FRACTION_LIMIT_FOR_SEGMENT_TIMES = 0.7; - - // For when determining stop times. Throws out - // outliers if they are less than 0.7 or greater than 1/0.7 - // of the average. - private final static double FRACTION_LIMIT_FOR_STOP_TIMES = 0.7; - + // Used when determining stop time for first stop of trip. A value // of 1.0 means that will use a stop time that is within 1.0 // standard deviation of the mean or higher, which is, @@ -89,17 +82,33 @@ public class TravelTimesProcessor { // because if vehicle is really late then perhaps it is matched to // wrong trip or such. At the very minimum it is an anomaly. In such a // case the data would only skew the stop time. - private final static int MAX_SCHED_ADH_FOR_FIRST_STOP_TIME = + public final static int MAX_SCHED_ADH_FOR_FIRST_STOP_TIME = 10*Time.MS_PER_MIN; private final static int MAX_SCHED_ADH_SECS = 30*Time.SEC_PER_MIN; - private static boolean shouldResetEarlyTerminalDepartures() { + public static boolean shouldResetEarlyTerminalDepartures() { return resetEarlyTerminalDepartures.getValue(); } + + // For when determining stop times. Throws out + // outliers if they are less than 0.7 or greater than 1/0.7 + // of the average. + private static DoubleConfigValue fractionLimitForStopTimes = new DoubleConfigValue("transitclock.traveltimes.fractionLimitForStopTimes", + 0.7, + "For when determining stop times. Throws out outliers."); + + + // For when determining travel times for segments. Throws out + // outliers if they are less than 0.7 or greater than 1/0.7 + // of the average. + private static DoubleConfigValue fractionLimitForTravelTimes = new DoubleConfigValue("transitclock.traveltimes.fractionLimitForTravelTimes", + 0.7, + "For when determining travel times. Throws out outliers."); + private static BooleanConfigValue resetEarlyTerminalDepartures = - new BooleanConfigValue("transitime.travelTimes.resetEarlyTerminalDepartures", + new BooleanConfigValue("transitclock.travelTimes.resetEarlyTerminalDepartures", true, "For some agencies vehicles won't be departing terminal " + "early. If an early departure is detected for such an " @@ -110,7 +119,7 @@ private static double getMaxTravelTimeSegmentLength() { return maxTravelTimeSegmentLength.getValue(); } private static DoubleConfigValue maxTravelTimeSegmentLength = - new DoubleConfigValue("transitime.traveltimes.maxTravelTimeSegmentLength", + new DoubleConfigValue("transitclock.traveltimes.maxTravelTimeSegmentLength", 250.0, "The longest a travel time segment can be. If a stop path " + "is longer than this distance then it will be divided " @@ -120,7 +129,7 @@ private static double getMinSegmentSpeedMps() { return minSegmentSpeedMps.getValue(); } private static DoubleConfigValue minSegmentSpeedMps = - new DoubleConfigValue("transitime.traveltimes.minSegmentSpeedMps", + new DoubleConfigValue("transitclock.traveltimes.minSegmentSpeedMps", 0.0, "If a travel time segment is determined to have a lower " + "speed than this value in meters/sec then the travel time" @@ -129,7 +138,7 @@ private static double getMinSegmentSpeedMps() { + "bad data."); private static DoubleConfigValue maxSegmentSpeedMps = - new DoubleConfigValue("transitime.traveltimes.maxSegmentSpeedMps", + new DoubleConfigValue("transitclock.traveltimes.maxSegmentSpeedMps", 27.0, // 27.0m/s = 60mph "If a travel time segment is determined to have a higher " + "speed than this value in meters/second then the travel " @@ -157,6 +166,18 @@ private static double getMinSegmentSpeedMps() { private static final Logger logger = LoggerFactory.getLogger(TravelTimesProcessor.class); + private MonitoringService monitoringService; + + private boolean isEmpty = true; + + public boolean isEmpty() { + return isEmpty; + } + + public TravelTimesProcessor() { + monitoringService = MonitoringService.getInstance(); + } + /********************** Member Functions **************************/ /** @@ -169,7 +190,7 @@ private static double getMinSegmentSpeedMps() { * added to the trip. */ public static class ProcessedDataMapKey extends MapKey { - private ProcessedDataMapKey(String tripId, int stopPathIndex, + public ProcessedDataMapKey(String tripId, int stopPathIndex, String stopId) { super(tripId, stopPathIndex, stopId); } @@ -271,7 +292,7 @@ private static void processFirstStopOfTrip(ArrivalDeparture arrDep) { // If schedule adherence is really far off then ignore the data // point because it would skew the results. - if (Math.abs(lateTimeMsec) > MAX_SCHED_ADH_FOR_FIRST_STOP_TIME) + if (Math.abs(lateTimeMsec) > MAX_SCHED_ADH_FOR_FIRST_STOP_TIME) return; // If configured to not use early departures then reset lateTimeMsec @@ -280,7 +301,7 @@ private static void processFirstStopOfTrip(ArrivalDeparture arrDep) { // departures indicates a problem with travel times. if (shouldResetEarlyTerminalDepartures() && lateTimeMsec < 0) lateTimeMsec = 0; - + // Get the MapKey so can put stop time into map ProcessedDataMapKey mapKeyForTravelTimes = getKey(arrDep.getTripId(), arrDep.getStopPathIndex(), @@ -515,7 +536,7 @@ private List determineTravelTimesForStopPath( && arrDep1.getTime() < arrDep1.getScheduledTime()) { logger.debug("Note: for {} using scheduled departure time instead " + "of the calculated departure time since " - + "transitime.travelTimes.resetEarlyTerminalDepartures is " + + "transitclock.travelTimes.resetEarlyTerminalDepartures is " + "true and the departure time was (likely incorrectly) " + "calculated to be before the scheduled departure time", arrDep1); @@ -618,7 +639,16 @@ private List determineTravelTimesForStopPath( // A low speed indicates a problem with the data. double segmentSpeedMps = travelTimeSegmentLength * Time.MS_PER_SEC / segmentTime; - if (segmentSpeedMps < getMinSegmentSpeedMps()) { + if (segmentSpeedMps < 0.0) { + // arrival / departure were switched, clamp to 0 travel time + logger.error("For segmentIdx={} segment speed of {}m/s is " + + "negative" + + "Therefore it is being reset to zero. " + + "arrDep1={} arrDep2={}", + i, StringUtils.twoDigitFormat(segmentSpeedMps), + arrDep1, arrDep2); + segmentTime = 0; + } else if (segmentSpeedMps < getMinSegmentSpeedMps()) { logger.error("For segmentIdx={} segment speed of {}m/s is " + "below the limit of minSegmentSpeedMps={}m/s. " + "Therefore it is being reset to min segment speed. " @@ -692,8 +722,14 @@ private void processDataBetweenTwoArrivalDepartures( int dwellTimeMsec = (int) (arrDep2.getTime() - arrDep1.getTime()); // Add this stop time to map so it can be averaged - addStopTimeToMap(mapKeyForTravelTimes, dwellTimeMsec); - + if (dwellTimeMsec >= 0) + addStopTimeToMap(mapKeyForTravelTimes, dwellTimeMsec); + else + logger.error("Ignoring negative dwell time={} for stop path " + + "at arrival/departures {} and {} (key = {})", + dwellTimeMsec, arrDep1, arrDep2, + mapKeyForTravelTimes); + return; } @@ -706,6 +742,21 @@ private void processDataBetweenTwoArrivalDepartures( List travelTimesForStopPath = determineTravelTimesForStopPath(dataFetcher, arrDep1, arrDep2); + + // Ignore a stop path if any segment travel time is negative. Nulls will + // be ignored downstream anyway so can also ignore those. + if (travelTimesForStopPath == null) + return; + for (Integer travelTimeForSegment : travelTimesForStopPath) { + if (travelTimeForSegment < 0) { + logger.error("Ignoring negative travel times={} for stop path " + + "between arrival/departures {} and {} (key = {})", + travelTimesForStopPath, arrDep1, arrDep2, + mapKeyForTravelTimes); + return; + } + } + addTravelTimesToMap(mapKeyForTravelTimes, travelTimesForStopPath); return; @@ -840,10 +891,15 @@ public TravelTimeInfoMap createTravelTimesFromMaps( new HashSet(); combinedKeySet.addAll(travelTimesMap.keySet()); combinedKeySet.addAll(stopTimesMap.keySet()); + int setSize = 0; + int unmatched = 0; + int matched = 0; + int invalid = 0; // For each trip/stop path that had historical arrivals/departures and // or matches in the database... for (ProcessedDataMapKey mapKey : combinedKeySet) { + setSize++; // Determine the associated Trip object for the data Trip trip = tripMap.get(mapKey.getTripId()); if (trip == null) { @@ -851,6 +907,7 @@ public TravelTimeInfoMap createTravelTimesFromMaps( "configuration data even though historic data was " + "found for it.", mapKey.getTripId()); + invalid ++; continue; } @@ -862,6 +919,7 @@ public TravelTimeInfoMap createTravelTimesFromMaps( + "The stopPathIndex from the historical data {} is " + "greater than the number of stop paths for {}", mapKey.getStopPathIndex(), trip); + invalid++; continue; } String stopIdFromTrip = @@ -873,9 +931,9 @@ public TravelTimeInfoMap createTravelTimesFromMaps( + "stopId={}. {}", mapKey.getStopPathIndex(), mapKey.getStopId(), stopIdFromTrip, trip); + invalid++; continue; } - // Determine average travel times for this trip/stop path List> travelTimesForStopPathForTrip = travelTimesMap.get(mapKey); @@ -894,7 +952,7 @@ public TravelTimeInfoMap createTravelTimesFromMaps( travelTimesBySegment) { int averageTravelTimeForSegment = Statistics .filteredMean(travelTimesByTripForSegment, - FRACTION_LIMIT_FOR_SEGMENT_TIMES); + fractionLimitForTravelTimes.getValue()); averageTravelTimes.add(averageTravelTimeForSegment); } } @@ -917,18 +975,18 @@ public TravelTimeInfoMap createTravelTimesFromMaps( averagedStopTime = Statistics.biasedFilteredMean( stopTimesForStopPathForTrip, - FRACTION_LIMIT_FOR_STOP_TIMES, + fractionLimitForStopTimes.getValue(), STD_DEV_BIAS_FOR_FIRST_STOP); // So far have determine when vehicle has departed. But should add // a bit of a bias since passengers have to get on a few seconds // before doors shut and vehicle starts moving. - averagedStopTime -= STOP_TIME_BIAS_FOR_FIRST_STOP; + averagedStopTime = Math.max(0, averagedStopTime - STOP_TIME_BIAS_FOR_FIRST_STOP); } else { // Not first stop of trip averagedStopTime = Statistics.filteredMean( stopTimesForStopPathForTrip, - FRACTION_LIMIT_FOR_STOP_TIMES); + fractionLimitForStopTimes.getValue()); } } else { // No arrival and corresponding departure time for the stop. @@ -940,6 +998,8 @@ public TravelTimeInfoMap createTravelTimesFromMaps( if (mapKey.getStopPathIndex() != trip.getNumberStopPaths()-1) { logger.debug("No stop times for {} even though there are " + "travel times for that map key", mapKey); + } else { + unmatched++; } } @@ -954,17 +1014,18 @@ public TravelTimeInfoMap createTravelTimesFromMaps( mapKey.getStopPathIndex(), averagedStopTime, averageTravelTimes, travelTimeSegLength); travelTimeInfoMap.add(travelTimeInfo); + matched++; } // Nice to log how long things took so can see progress and bottle necks - logger.info("Processing data into a TravelTimeInfoMap took {} msec.", - intervalTimer.elapsedMsec()); - + logger.info("Processing data (total={} matched={} unmatched={} invalid={}) into a TravelTimeInfoMap took {} msec.", + setSize, matched, unmatched, invalid, intervalTimer.elapsedMsec()); + reportStatus(setSize, matched, unmatched, invalid); // Return the map with all the processed travel time data in it return travelTimeInfoMap; } - /** + /** * Reads in the Matches and the ArrivalDepartures from the database for the * time specified. Puts the data into the stopTimesMap and the travelTimesMap * for further processing. @@ -980,6 +1041,16 @@ public void readAndProcessHistoricData(String projectId, DataFetcher dataFetcher = new DataFetcher(projectId, specialDaysOfWeek); dataFetcher.readData(projectId, beginTime, endTime); + // exit here if no matches are present + // no further work can be done! + if (dataFetcher.getMatchesMap()== null || dataFetcher.getMatchesMap().isEmpty()) { + logger.error("No Matches: Nothing to do!"); + isEmpty = true; + reportStatus(0, 0, 0, 0); + return; + } + isEmpty = false; + // Process all the historic data read from the database. Puts // resulting data into stopTimesMap and travelTimesMap. logger.info("Processing data into travel time maps..."); @@ -996,7 +1067,28 @@ public void readAndProcessHistoricData(String projectId, "times map took {} msec.", intervalTimer.elapsedMsec()); } + + public Long updateMetrics(Session session, int travelTimesRev) { + Long count = Trip.countTravelTimesForTrips(session, travelTimesRev); + monitoringService.averageMetric("PredictionLatestTravelTimeRev", travelTimesRev*1.0); + if (count != null) { + monitoringService.averageMetric("PredictionTravelTimesForTripsCount", count*1.0); + } else { + monitoringService.averageMetric("PredictionTravelTimesForTripsCount", -1.0); + } + monitoringService.flush(); + return count; + } + + + // cloudwatch reporting/monitoring + private void reportStatus(int setSize, int matched, int unmatched, int invalid) { + monitoringService.averageMetric("TravelTimeTotal", setSize * 1.0); + monitoringService.averageMetric("TravelTimeMatched", matched * 1.0); + monitoringService.averageMetric("TravelTimeUnmatched", unmatched * 1.0); + monitoringService.averageMetric("TravelTimeInvalid", invalid * 1.0); + } /* * Just for debugging diff --git a/transitime/src/main/java/org/transitime/core/travelTimes/package-info.java b/transitclock/src/main/java/org/transitclock/core/travelTimes/package-info.java similarity index 95% rename from transitime/src/main/java/org/transitime/core/travelTimes/package-info.java rename to transitclock/src/main/java/org/transitclock/core/travelTimes/package-info.java index ef1fe14ad..0cd1c82e7 100644 --- a/transitime/src/main/java/org/transitime/core/travelTimes/package-info.java +++ b/transitclock/src/main/java/org/transitclock/core/travelTimes/package-info.java @@ -21,4 +21,4 @@ * @author SkiBu Smith * */ -package org.transitime.core.travelTimes; \ No newline at end of file +package org.transitclock.core.travelTimes; \ No newline at end of file diff --git a/transitclock/src/main/java/org/transitclock/custom/DbAvlReader.java b/transitclock/src/main/java/org/transitclock/custom/DbAvlReader.java new file mode 100644 index 000000000..52d289d7d --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/custom/DbAvlReader.java @@ -0,0 +1,160 @@ +package org.transitclock.custom; + +import org.transitclock.avl.AvlClient; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.config.StringConfigValue; +import org.transitclock.db.structs.AvlReport; +import org.transitclock.modules.Module; +import org.transitclock.utils.Time; +import org.transitclock.utils.threading.BoundedExecutor; +import org.transitclock.utils.threading.NamedThreadFactory; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.Statement; +import java.util.*; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +public class DbAvlReader extends Module { + + public static final String DB_URL = "url"; + private static StringConfigValue dbUrl = + new StringConfigValue("transitclock.avl.dbUrl", null, "The jdbc url of the avl database"); + private static StringConfigValue dbQuery = + new StringConfigValue("transitclock.avl.dbQuery", "select ID as Id, BusID as busId, " + + " CAST(CONCAT(RptDate, ' ', RptTime) AS DATETIME) AS rptDateTime, " + + "LatDD as lat, LonDD as lon, LogonRoute as logonRoute, LogonTrip as logonTrip, " + + "RptDate as reportDate from tblbuses;", "The query to execute to receive AVL data"); + + private static StringConfigValue validationQuery = + new StringConfigValue("transitclock.avl.validationQuery", "select 1"); + private static IntegerConfigValue numAvlThreads = + 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 " + + "since the messages will not be interleaved. But for " + + "large systems with lots of vehicles then should use " + + "multiple threads, such as 3-5 so that more of the cores " + + "are used. Only for when JMS is used."); + private static IntegerConfigValue avlQueueSize = + 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 pollingRateMsec = + new IntegerConfigValue("transitclock.avl.pollingRateMsec", 30000, + "Milliseconds between AVL database polls"); + + private final BoundedExecutor _avlClientExecutor; + private Connection connection = null; + + public DbAvlReader(String agencyId) throws Exception { + super(agencyId); + int numberThreads = numAvlThreads.getValue(); + int maxAVLQueueSize = avlQueueSize.getValue(); + // Create the executor that actually processes the AVL data + NamedThreadFactory avlClientThreadFactory = new NamedThreadFactory( + "avlClient"); + Executor executor = Executors.newFixedThreadPool(numberThreads, + avlClientThreadFactory); + _avlClientExecutor = new BoundedExecutor(executor, maxAVLQueueSize); + } + + protected List getAvlReports(Connection connection) throws Exception { + ResultSet rs = null; + Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); + rs = statement.executeQuery(dbQuery.getValue()); + return map(rs); + } + + private List map(ResultSet rs) throws Exception { + ArrayList data = new ArrayList(); + while (rs.next()) { + AvlReport avl = readRow(rs); + if (avl != null) { + data.add(avl); + } + } + return data; + } + + private AvlReport readRow(ResultSet rs) throws Exception { + Integer id = rs.getInt(1); + String vehicleId = rs.getString(2); + Date reportedTime = rs.getTimestamp(3); + Double lat = rs.getDouble(4); + Double lon = rs.getDouble(5); + String source = "db"; + AvlReport avl = new AvlReport(vehicleId, reportedTime.getTime(), lat, lon, source); + return avl; + } + + private Connection getAndValidateConnection() throws Exception { + if (connection == null) { + connection = getConnection(getConnectionProperties()); + } + if (!isValid(connection)) { + connection = getConnection(getConnectionProperties()); + } + return connection; + } + + private boolean isValid(Connection connection) { + if (connection == null ) return false; + try { + Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); + statement.executeQuery(validationQuery.getValue()); + logger.info("validation succeeded"); + return true; + } catch (Throwable t) { + return false; + } + } + + Map getConnectionProperties() { + HashMap properties = new HashMap(); + properties.put(DB_URL, dbUrl.getValue()); + return properties; + } + + Connection getConnection(Map properties) throws Exception { + /* here we load the driver by hand -- the avl database may (will!) be + * completely different from the transitclock database. + */ + return DriverManager.getConnection(properties.get(DB_URL)); + } + + @Override + public void run() { + + if (dbUrl.getValue() == null) { + logger.error("no dbUrl configured for reader, exiting"); + return; + } + + while (!Thread.interrupted()) { + + try { + Connection conn = getAndValidateConnection(); + if (conn != null) { + logger.info("poll"); + List reports = getAvlReports(conn); + logger.info("found {} reports: {}", reports.size(), reports); + for (AvlReport report : reports) { + Runnable avlClient = new AvlClient(report); + _avlClientExecutor.execute(avlClient); + } + } else { + logger.error("no connection available for {}", dbUrl.getValue()); + } + Time.sleep(pollingRateMsec.getValue()); + } catch (Exception any) { + logger.error("issue with avl {}", any); + } + } + } + +} diff --git a/transitclock/src/main/java/org/transitclock/custom/SiriLikeModule.java b/transitclock/src/main/java/org/transitclock/custom/SiriLikeModule.java new file mode 100644 index 000000000..2845181ad --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/custom/SiriLikeModule.java @@ -0,0 +1,478 @@ +package org.transitclock.custom; + +import java.net.URL; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpression; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.avl.AvlClient; +import org.transitclock.custom.aws.AvlSqsClientModule; +import org.transitclock.db.structs.AvlReport; +import org.transitclock.db.structs.AvlReport.AssignmentType; +import org.transitclock.modules.Module; +import org.transitclock.utils.threading.BoundedExecutor; +import org.transitclock.utils.threading.NamedThreadFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +public class SiriLikeModule extends Module { + + private static final Logger logger = + LoggerFactory.getLogger(AvlSqsClientModule.class); + + private static SimpleDateFormat ISO_DATE_SHORT_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX"); + private static SimpleDateFormat ISO_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSXXX"); + + public List _routeList = new ArrayList(); + private final BoundedExecutor _avlClientExecutor; + private XPathExpression mvjExpression; + private XPathExpression tripIdExpression; + private XPathExpression recordedAtExpression; + private XPathExpression vehicleIdExpression; + private XPathExpression latExpression; + private XPathExpression lonExpression; + private XPathExpression speedExpression; + private XPathExpression bearingExpression; + private DocumentBuilderFactory factory; + private DocumentBuilder builder; + private String _apiKey; + private String _baseUrl; + + public SiriLikeModule(String agencyId) { + super(agencyId); + // todo move this to config + addRoutes(); + + _baseUrl = "http://api.rideuta.com/SIRI/SIRI.svc/VehicleMonitor/ByRoute"; + _apiKey = "UPBML0P0ZO0"; + + ISO_DATE_FORMAT.setLenient(false); + ISO_DATE_SHORT_FORMAT.setLenient(false); + factory = DocumentBuilderFactory.newInstance(); + try { + builder = factory.newDocumentBuilder(); + + XPathFactory xPathfactory = XPathFactory.newInstance(); + XPath xpath = xPathfactory.newXPath(); + mvjExpression = xpath.compile("/Siri/VehicleMonitoringDelivery/VehicleActivity/MonitoredVehicleJourney"); + recordedAtExpression = xpath.compile("/Siri/VehicleMonitoringDelivery/VehicleActivity/RecordedAtTime/text()"); + tripIdExpression = xpath.compile("FramedVehicleJourneyRef/DatedVehicleJourneyRef/text()"); + vehicleIdExpression = xpath.compile("VehicleRef/text()"); + latExpression = xpath.compile("VehicleLocation/Latitude/text()"); + lonExpression = xpath.compile("VehicleLocation/Longitude/text()"); + speedExpression = xpath.compile("Extensions/Speed/text()"); + bearingExpression = xpath.compile("Extensions/Bearing/text()"); + } catch (ParserConfigurationException | XPathExpressionException e) { + logger.error("invalid config:", e); + } + + // Create the executor that actually processes the AVL data + NamedThreadFactory avlClientThreadFactory = new NamedThreadFactory( + "avlClient"); + Executor executor = Executors.newFixedThreadPool(1, + avlClientThreadFactory); + _avlClientExecutor = new BoundedExecutor(executor, 100); + } + + + + public String getApiKey() { + return _apiKey; + } + + public String getUrl() { + return _baseUrl; + } + + public void setRoutes(List routes) { + _routeList = routes; + } + + public List getRoutes() { + return _routeList; + } + + @Override + public void run() { + while (!Thread.interrupted()) { + try { + refresh(); + Thread.sleep(1000); + } catch (Exception e) { + logger.error("issue processing data", e); + } + } + } + + private void refresh() throws Exception { + for (String route : getRoutes()) { + NodesAndTimestamp nodesAndTimestamp = parseVehicles(new URL(constructUrl(route, getApiKey(), getUrl()))); + for (Node n : nodesAndTimestamp.getNodes()) { + AvlReport avl = parse(n, nodesAndTimestamp.getTimestamp()); + if (avl != null) { + Runnable avlClient = new AvlClient(avl); + _avlClientExecutor.execute(avlClient); + } + } + } + } + + + private AvlReport parse(Node node, long timestamp) throws Exception { + String tripId = (String) tripIdExpression.evaluate(node, XPathConstants.STRING); + if (tripId == null) { + logger.error("no trip for node=" + node); + return null; + } + String vehicleId = (String)vehicleIdExpression.evaluate(node, XPathConstants.STRING); + double lat = asDouble(latExpression.evaluate(node, XPathConstants.STRING)); + double lon = asDouble(lonExpression.evaluate(node, XPathConstants.STRING)); + float speed = asFloat(speedExpression.evaluate(node, XPathConstants.STRING)); + float bearing = asFloat(bearingExpression.evaluate(node, XPathConstants.STRING)); + + return toAvlReport(vehicleId, timestamp, lat, lon, speed, bearing, tripId); + } + + private NodesAndTimestamp parseVehicles(URL url) throws Exception { + List vehicles = new ArrayList(); + Document doc = builder.parse(url.toString()); + String recordedAtStr = (String)recordedAtExpression.evaluate(doc, XPathConstants.STRING); + long timestamp = parseDate(recordedAtStr).getTime(); + NodeList nl = (NodeList) this.mvjExpression.evaluate(doc, XPathConstants.NODESET); + if (nl ==null || nl.getLength() == 0) { + logger.error("no nodes found"); + return new NodesAndTimestamp(vehicles, timestamp); + } + + for (int i = 0; i < nl.getLength(); i++) { + vehicles.add(nl.item(i)); + } + return new NodesAndTimestamp(vehicles, timestamp); + } + + private AvlReport toAvlReport(String vehicleId, + long timestamp, + double lat, double lon, + float speed, + float bearing, + String tripId) { + AvlReport a = new AvlReport(vehicleId, timestamp, lat, lon, speed, bearing, "UTASIRI"); + a.setAssignment(tripId, AssignmentType.TRIP_ID); + return a; + } + private String constructUrl(String route, String apiKey, String url) { + return url + "?route=" + route + "&usertoken=" + apiKey; + } + + public Date parseShortDate(String s) throws Exception { + return ISO_DATE_SHORT_FORMAT.parse(s); + } + + public Date parseDate(String s) throws Exception { + int endPos = "yyyy-MM-ddTHH:mm:ss.SSS".length(); + // we can't convince Java's Simple Date to parse millisecond to 7 digit precision + s = s.substring(0, endPos-1) + s.substring((s.length() - 7), s.length()); + + return ISO_DATE_FORMAT.parse(s); + } + + private double asDouble(Object obj) { + String s = (String) obj; + return Double.parseDouble(s); + } + + private float asFloat(Object obj) { + String s = (String) obj; + return Float.parseFloat(s); + } + + public static class NodesAndTimestamp { + private List _nodes; + private long _timestamp; + public NodesAndTimestamp(List nodes, long timestamp) { + this._nodes = nodes; + this._timestamp = timestamp; + } + public List getNodes() { + return _nodes; + } + public long getTimestamp() { + return _timestamp; + } + } + private void addRoutes() { + _routeList.add("2X"); + _routeList.add("2"); + _routeList.add("320"); + _routeList.add("33"); + _routeList.add("240"); + _routeList.add("248"); + _routeList.add("228"); + _routeList.add("232"); + _routeList.add("307"); + _routeList.add("313"); + + _routeList.add("3"); + _routeList.add("920"); + _routeList.add("919"); + _routeList.add("9"); + _routeList.add("863"); + _routeList.add("880"); + _routeList.add("850"); + _routeList.add("862"); + _routeList.add("35M"); + _routeList.add("39"); + _routeList.add("35"); + _routeList.add("354"); + _routeList.add("320"); + _routeList.add("33"); + _routeList.add("307"); + _routeList.add("313"); + _routeList.add("41"); + _routeList.add("45"); + _routeList.add("960"); + _routeList.add("920"); + _routeList.add("F522"); + _routeList.add("990"); + _routeList.add("F546"); + _routeList.add("F534"); + _routeList.add("F556"); + _routeList.add("F547"); + _routeList.add("F618"); + _routeList.add("F638"); + _routeList.add("451"); + _routeList.add("205"); + _routeList.add("455"); + _routeList.add("454"); + _routeList.add("453"); + _routeList.add("201"); + _routeList.add("462"); + _routeList.add("461"); + _routeList.add("460"); + _routeList.add("456"); + _routeList.add("47"); + _routeList.add("463"); + _routeList.add("248"); + _routeList.add("841"); + _routeList.add("842"); + _routeList.add("838"); + _routeList.add("840"); + _routeList.add("863"); + _routeList.add("9"); + _routeList.add("850"); + _routeList.add("862"); + _routeList.add("902"); + _routeList.add("919"); + _routeList.add("F590"); + _routeList.add("F638"); + _routeList.add("F618"); + _routeList.add("516"); + _routeList.add("519"); + _routeList.add("477"); + _routeList.add("500"); + _routeList.add("509"); + _routeList.add("513"); + _routeList.add("470"); + _routeList.add("471"); + _routeList.add("472"); + _routeList.add("473"); + _routeList.add("F94"); + _routeList.add("836"); + _routeList.add("835"); + _routeList.add("834"); + _routeList.add("833"); + _routeList.add("832"); + _routeList.add("831"); + _routeList.add("830"); + _routeList.add("822"); + _routeList.add("821"); + _routeList.add("811"); + _routeList.add("990"); + _routeList.add("667"); + _routeList.add("665"); + _routeList.add("F400"); + _routeList.add("645"); + _routeList.add("640"); + _routeList.add("664"); + _routeList.add("650"); + _routeList.add("626"); + _routeList.add("625"); + _routeList.add("630"); + _routeList.add("627"); + _routeList.add("F94"); + _routeList.add("608"); + _routeList.add("606"); + _routeList.add("525"); + _routeList.add("520"); + _routeList.add("54"); + _routeList.add("526"); + _routeList.add("6"); + _routeList.add("551"); + _routeList.add("604"); + _routeList.add("603"); + _routeList.add("806"); + _routeList.add("807"); + _routeList.add("701"); + _routeList.add("703"); + _routeList.add("704"); + _routeList.add("72"); + _routeList.add("720"); + _routeList.add("750"); + _routeList.add("805"); + _routeList.add("616"); + _routeList.add("62"); + _routeList.add("612"); + _routeList.add("613"); + _routeList.add("606"); + _routeList.add("608"); + _routeList.add("603"); + _routeList.add("604"); + _routeList.add("551"); + _routeList.add("6"); + _routeList.add("217"); + _routeList.add("213"); + _routeList.add("200"); + + _routeList.add("17"); + _routeList.add("11"); + _routeList.add("21"); + _routeList.add("209"); + _routeList.add("205"); + _routeList.add("201"); + _routeList.add("F514"); + _routeList.add("640"); + _routeList.add("645"); + _routeList.add("F504"); + _routeList.add("616"); + _routeList.add("62"); + _routeList.add("612"); + _routeList.add("613"); + _routeList.add("627"); + _routeList.add("630"); + _routeList.add("625"); + _routeList.add("626"); + _routeList.add("F518"); + _routeList.add("902"); + _routeList.add("54"); + _routeList.add("526"); + _routeList.add("880"); + _routeList.add("901"); + _routeList.add("513"); + _routeList.add("509"); + _routeList.add("500"); + _routeList.add("477"); + _routeList.add("525"); + _routeList.add("520"); + _routeList.add("519"); + _routeList.add("516"); + _routeList.add("703"); + _routeList.add("F578"); + _routeList.add("675"); + _routeList.add("674"); + _routeList.add("667"); + _routeList.add("665"); + _routeList.add("664"); + _routeList.add("F570"); + _routeList.add("F402"); + _routeList.add("F401"); + _routeList.add("704"); + _routeList.add("675"); + _routeList.add("674"); + _routeList.add("223"); + _routeList.add("F400"); + _routeList.add("F402"); + _routeList.add("F401"); + _routeList.add("3"); + _routeList.add("2X"); + _routeList.add("72"); + _routeList.add("720"); + _routeList.add("750"); + _routeList.add("805"); + _routeList.add("806"); + _routeList.add("807"); + _routeList.add("811"); + _routeList.add("821"); + _routeList.add("822"); + _routeList.add("830"); + _routeList.add("962"); + _routeList.add("992"); + _routeList.add("240"); + _routeList.add("951"); + _routeList.add("952"); + _routeList.add("953"); + _routeList.add("954"); + _routeList.add("227"); + _routeList.add("223"); + _routeList.add("F504"); + _routeList.add("F514"); + _routeList.add("21"); + _routeList.add("209"); + _routeList.add("F518"); + _routeList.add("F522"); + _routeList.add("220"); + _routeList.add("218"); + _routeList.add("217"); + _routeList.add("213"); + _routeList.add("455"); + _routeList.add("454"); + _routeList.add("650"); + _routeList.add("354"); + _routeList.add("35"); + _routeList.add("39"); + _routeList.add("35M"); + _routeList.add("45"); + _routeList.add("41"); + _routeList.add("453"); + _routeList.add("451"); + _routeList.add("841"); + _routeList.add("840"); + _routeList.add("838"); + _routeList.add("832"); + _routeList.add("831"); + _routeList.add("834"); + _routeList.add("833"); + _routeList.add("232"); + _routeList.add("228"); + _routeList.add("11"); + _routeList.add("17"); + _routeList.add("220"); + _routeList.add("218"); + _routeList.add("227"); + _routeList.add("701"); + _routeList.add("472"); + _routeList.add("2"); + _routeList.add("200"); + _routeList.add("473"); + _routeList.add("463"); + _routeList.add("47"); + _routeList.add("470"); + _routeList.add("471"); + _routeList.add("456"); + _routeList.add("460"); + _routeList.add("461"); + _routeList.add("462"); + _routeList.add("F590"); + _routeList.add("F578"); + _routeList.add("F570"); + _routeList.add("F556"); + _routeList.add("F547"); + _routeList.add("F546"); + _routeList.add("F534"); + + } +} diff --git a/transitclock/src/main/java/org/transitclock/custom/aws/AvlReportWrapper.java b/transitclock/src/main/java/org/transitclock/custom/aws/AvlReportWrapper.java new file mode 100644 index 000000000..02e68c441 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/custom/aws/AvlReportWrapper.java @@ -0,0 +1,47 @@ +package org.transitclock.custom.aws; + +import org.transitclock.db.structs.AvlReport; + +public class AvlReportWrapper { + private AvlReport _report; + private Long _totalLatency = null; + private Long _avlLatency = null; + private Long _sqsLatency = null; + private Long _forwarderProcessingLatency = null; + private Long _forwarderSendLatency = null; + + public AvlReportWrapper(AvlReport report, Long avlLatency, Long forwarderProcessingLatency, Long sqsLatency, Long totalLatency) { + _report = report; + _avlLatency = avlLatency; + _sqsLatency = sqsLatency; + _totalLatency = totalLatency; + _forwarderProcessingLatency = forwarderProcessingLatency; + if (sqsLatency != null && forwarderProcessingLatency != null) { + _forwarderSendLatency = sqsLatency - forwarderProcessingLatency; + } + } + + public AvlReport getReport() { + return _report; + } + + public Long getTotalLatency() { + return _totalLatency; + } + + public Long getAvlLatency() { + return _avlLatency; + } + + public Long getSqsLatency() { + return _sqsLatency; + } + + public Long getForwarderProcessingLatency() { + return _forwarderProcessingLatency; + } + + public Long getForwarderSendLatency() { + return _forwarderSendLatency; + } +} diff --git a/transitclock/src/main/java/org/transitclock/custom/aws/AvlSqsClientModule.java b/transitclock/src/main/java/org/transitclock/custom/aws/AvlSqsClientModule.java new file mode 100644 index 000000000..4c4ec1246 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/custom/aws/AvlSqsClientModule.java @@ -0,0 +1,459 @@ +package org.transitclock.custom.aws; + +import com.amazonaws.auth.AWSCredentials; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.regions.Region; +import com.amazonaws.regions.Regions; +import com.amazonaws.services.sns.AmazonSNSClient; +import com.amazonaws.services.sns.model.PublishRequest; +import com.amazonaws.services.sqs.AmazonSQS; +import com.amazonaws.services.sqs.AmazonSQSClient; +import com.amazonaws.services.sqs.model.DeleteMessageRequest; +import com.amazonaws.services.sqs.model.Message; +import com.amazonaws.services.sqs.model.ReceiveMessageRequest; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.avl.AvlClient; +import org.transitclock.config.ClassConfigValue; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.config.StringConfigValue; +import org.transitclock.core.AvlProcessor; +import org.transitclock.modules.Module; +import org.transitclock.monitoring.MonitoringService; +import org.transitclock.utils.threading.BoundedExecutor; +import org.transitclock.utils.threading.NamedThreadFactory; + +import java.util.List; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +/** + * Reads AVL data from AWS SQS topic, deserializes it, and process it + * following the patter established by AvlJmsClientModule. + * + */ +public class AvlSqsClientModule extends Module { + private static final Logger logger = + LoggerFactory.getLogger(AvlSqsClientModule.class); + + private final BoundedExecutor _avlClientExecutor; + private AWSCredentials _sqsCredentials; + private AmazonSQS _sqs; + private AmazonSNSClient _sns = null; + private String _url = null; + private SqsMessageUnmarshaller _messageUnmarshaller; + private int _messageCount = 0; + private long _messageStart = System.currentTimeMillis(); + private ArrayBlockingQueue _receiveQueue; + private ArrayBlockingQueue _deserializeQueue; + private ArrayBlockingQueue _acknowledgeQueue; + private ArrayBlockingQueue _archiveQueue; + private MonitoringService monitoring; + + private final static int MAX_THREADS = 100; + + private static final int DEFAULT_MESSAGE_LOG_FREQUENCY = 10000; + + + private static IntegerConfigValue avlQueueSize = + 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("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 " + + "since the messages will not be interleaved. But for " + + "large systems with lots of vehicles then should use " + + "multiple threads, such as 3-5 so that more of the cores " + + "are used. Only for when JMS is used."); + + private static IntegerConfigValue jmsPauseTimeInSeconds = + new IntegerConfigValue("transitclock.avl.jmsPauseTimeInSeconds", 20, + "How long to block for the next message."); + + + private static IntegerConfigValue messageLogFrequency = + new IntegerConfigValue("transitclock.avl.messageLogFrequency", + DEFAULT_MESSAGE_LOG_FREQUENCY, + "How often (in count of message) a log message is output " + + "confirming messages have been received"); + + private static StringConfigValue avlUrl = + new StringConfigValue("transitclock.avl.sqsUrl", null, "The SQS URL from AWS"); + + private static StringConfigValue sqsKey = + new StringConfigValue("transitclock.avl.sqsKey", null, "The AWS Key with SQS read access"); + + private static StringConfigValue sqsSecret = + new StringConfigValue("transitclock.avl.sqsSecret", null, "The AWS Secret with SQS read access"); + + + private static StringConfigValue snsKey = + new StringConfigValue("transitclock.avl.snsKey", null, "The AWS Key with SNS write access"); + + private static StringConfigValue snsSecret = + new StringConfigValue("transitclock.avl.snsSecret", null, "The AWS Secret with SNS write access"); + + private static StringConfigValue snsArn = + new StringConfigValue("transitclock.avl.snsArn", null, "The AWS SNS ARN to write to"); + + private static ClassConfigValue unmarshallerConfig = + new ClassConfigValue("transitclock.avl.unmarshaller", WmataAvlTypeUnmarshaller.class, + "Implementation of SqsMessageUnmarshaller to perform " + + "the deserialization of SQS Message objects into AVLReport objects"); + + private static StringConfigValue applicableList = + new StringConfigValue("transitclock.avl.filterVehicleIdList", null, "List of vehicle Ids to filter on"); + + public AvlSqsClientModule(String agencyId) throws Exception { + super(agencyId); + monitoring = MonitoringService.getInstance(); + logger.info("loading AWS SQS credentials from environment"); + _sqsCredentials = new BasicAWSCredentials(sqsKey.getValue(), sqsSecret.getValue()); + connect(); + + int maxAVLQueueSize = avlQueueSize.getValue(); + int numberThreads = numAvlThreads.getValue(); + _url = avlUrl.getValue(); + + logger.info("Starting AvlClient for agencyId={} with " + + "maxAVLQueueSize={}, numberThreads={} and url={}", agencyId, + maxAVLQueueSize, numberThreads, _url); + + // Make sure that numberThreads is reasonable + if (numberThreads < 1) { + logger.error("Number of threads must be at least 1 but {} was " + + "specified. Therefore using 1 thread.", numberThreads); + numberThreads = 1; + } + if (numberThreads > MAX_THREADS) { + logger.error("Number of threads must be no greater than {} but " + + "{} was specified. Therefore using {} threads.", + MAX_THREADS, numberThreads, MAX_THREADS); + numberThreads = MAX_THREADS; + } + + + // Create the executor that actually processes the AVL data + NamedThreadFactory avlClientThreadFactory = new NamedThreadFactory( + "avlClient"); + Executor executor = Executors.newFixedThreadPool(numberThreads, + avlClientThreadFactory); + _avlClientExecutor = new BoundedExecutor(executor, maxAVLQueueSize); + _receiveQueue = new ArrayBlockingQueue(maxAVLQueueSize); + _deserializeQueue = new ArrayBlockingQueue(maxAVLQueueSize*10); + _acknowledgeQueue = new ArrayBlockingQueue(maxAVLQueueSize*10); + _archiveQueue = new ArrayBlockingQueue(maxAVLQueueSize*1000); + + logger.info("starting {} threads for queues.", numberThreads); + for (int i = 0; i <= numberThreads; i++) { + // todo this really should be executors + new Thread(new ReceiveTask()).start(); + new Thread(new DeserailzeTask()).start(); + new Thread(new AcknowledgeTask()).start(); + new Thread(new ArchiveTask()).start(); + } + + new Thread(new StatusTask()).start(); + + // create an instance of the SQS message unmarshaller + _messageUnmarshaller = (SqsMessageUnmarshaller) unmarshallerConfig.getValue().newInstance(); + + if (!StringUtils.isEmpty(snsKey.getValue()) + && !StringUtils.isEmpty(snsSecret.getValue()) + && !StringUtils.isEmpty(snsArn.getValue())) { + try { + logger.info("creating sns connection for archiving to ARN {}", snsArn.getValue()); + _sns = new AmazonSNSClient(new BasicAWSCredentials(snsKey.getValue(), snsSecret.getValue())); + } catch (Exception any) { + // SNS topic failure is non-fatal + logger.error("failed to create sns client: {}", any); + _sns = null; + } + } else { + logger.info("sns configuration not set, skipping."); + } + } + + + + private synchronized void connect() { + _sqs = new AmazonSQSClient(_sqsCredentials); + Region usEast1 = Region.getRegion(Regions.US_EAST_1); + _sqs.setRegion(usEast1); + } + + @Override + public void run() { + while (!Thread.interrupted()) { + try { + processAVLDataFromSQS(); + } catch (Exception e) { + logger.error("issue processing data:", e); + } + } + } + + private void processAVLDataFromSQS() { + int logFrequency = messageLogFrequency.getValue(); + logger.info("logFrequency={}", logFrequency); + + while (!Thread.interrupted()) { + try { + + Message message = _receiveQueue.poll(250, TimeUnit.MILLISECONDS); + if (message == null) continue; + + try { + _messageCount++; + _deserializeQueue.add(message); + _archiveQueue.add(message); + } catch (IllegalStateException ise) { + logger.error("dropping message {} as queue is full. deseralize size={}, archive size={}: ", message, ise, _deserializeQueue.size(), _archiveQueue.size()); + } catch (Exception any) { + logger.error("exception deserializing mesage={}: ", message, any); + } + + } catch (Exception e) { + logger.error("issue receiving request", e); + } + + try { + // put out a log message to show progress every so often + if (_messageCount % logFrequency == 0) { + long delta = (System.currentTimeMillis() - _messageStart)/1000; + long rate = 0; + if (delta != 0) { + rate = _messageCount / delta; + } + logger.info("received " + _messageCount + " messages in " + + delta + " seconds (" + rate + "/s) receive size=" + _deserializeQueue.size() + + ", archive size=" + _archiveQueue.size() + ", ack size=" + + _acknowledgeQueue.size()); + _messageStart = System.currentTimeMillis(); + _messageCount = 0; + } + } catch (Exception e) { + logger.error("status message exception: ", e); + } + } + } + + + private void archive(Message message) { + if (message == null || _sns == null) return; + // currently AWS does not support batch publishing to SNS + try { + PublishRequest request = new PublishRequest(); + request.setTopicArn(snsArn.getValue()); + String content = _messageUnmarshaller.toString(message); + logger.debug("archiving content {}", content); + request.setMessage(content); + _sns.publish(request); + } catch (Exception any) { + logger.error("issue archving message {}: ", message, any); + } + } + + + private void acknowledge(Message message) { + // let SQS know we processed the messages + if (message != null) { + // only acknowledge receipt of the transmission, not of each message + String messageReceiptHandle = message.getReceiptHandle(); + try { + logger.trace("ack message"); + _sqs.deleteMessage(new DeleteMessageRequest(_url, messageReceiptHandle)); + } catch (Exception e) { + logger.error("unable to mark message as received: ", e); + } + } + } + + private class ReceiveTask implements Runnable { + + @Override + public void run() { + try { + while (!Thread.interrupted()) { + try { + ReceiveMessageRequest request = new ReceiveMessageRequest(_url); + request.setWaitTimeSeconds(jmsPauseTimeInSeconds.getValue()); + List messages = _sqs.receiveMessage(request).getMessages(); + try { + _receiveQueue.addAll(messages); + } catch (IllegalStateException ise) { + logger.error("dropping receive {} as queue is full: ", messages, ise); + } + + if (!messages.isEmpty()) { + try { + // we only need to ack receipt of the request, not each message + _acknowledgeQueue.add(messages.get(0)); + } catch (IllegalStateException ise) { + logger.error("dropping ack {} as queue is full: ", messages, ise); + } + } + } catch (Exception any) { + logger.error("exception receiving: ", any); + } + } + } finally { + logger.error("ReceiveTask exiting!"); + } + } + } + + private class DeserailzeTask implements Runnable { + + @Override + public void run() { + int logFrequency = messageLogFrequency.getValue(); + int recordCount = 0; + long recordStart = System.currentTimeMillis(); + try { + while (!Thread.interrupted()) { + try { + Message message = _deserializeQueue.poll(250, TimeUnit.MILLISECONDS); + if (message == null) continue; + List avlReports = null; + try { + avlReports = _messageUnmarshaller.toAvlReports(message); + for (AvlReportWrapper avlReport : avlReports) { + recordCount++; + if (avlReport != null) { + if (avlReport.getTotalLatency() != null) { + monitoring.averageMetric("PredictionTotalQueueLatencyInMillis", new Double(avlReport.getTotalLatency())); + } + if (avlReport.getSqsLatency() != null) { + monitoring.averageMetric("PredictionSQSQueueLatencyInMillis", new Double(avlReport.getSqsLatency())); + } + if (avlReport.getAvlLatency() != null) { + monitoring.averageMetric("PredictionAvlQueueLatencyInMillis", new Double(avlReport.getAvlLatency())); + } + if (avlReport.getForwarderProcessingLatency() != null) { + monitoring.averageMetric("PredictionForwarderProcessingLatencyInMillis", new Double(avlReport.getForwarderProcessingLatency())); + } + if (avlReport.getForwarderSendLatency() != null) { + monitoring.averageMetric("PredictionForwarderSendLatencyInMillis", new Double(avlReport.getForwarderSendLatency())); + } + } + + if (avlReport != null) { + if (applicableList.getValue() == null || isApplicable(avlReport)) { + Runnable avlClient = new AvlClient(avlReport.getReport()); + _avlClientExecutor.execute(avlClient); + } + } + if (recordCount % logFrequency == 0) { + long delta = (System.currentTimeMillis() - recordStart)/1000; + long rate = 0; + if (delta != 0) { + rate = recordCount / delta; + } + logger.info("deserialized " + recordCount + " messages in " + + delta + " seconds (" + rate + "/s) receive size=" + _deserializeQueue.size() + + ", archive size=" + _archiveQueue.size() + ", ack size=" + + _acknowledgeQueue.size()); + recordStart = System.currentTimeMillis(); + recordCount = 0; + } + } + } catch (Exception any) { + logger.error("exception deserializing message {}", message, any); + } + } catch (Exception any) { + logger.error("unexpected exception: ", any); + } + } + + } finally { + logger.error("DeserializeTask exiting!"); + } + } + + private boolean isApplicable(AvlReportWrapper avlReport) { + for (String s : applicableList.getValue().split(",")) { + if (avlReport.getReport().getVehicleId().equals(s.trim())) { + return true; + } + } + return false; + } + } + + private class AcknowledgeTask implements Runnable { + + @Override + public void run() { + try { + while (!Thread.interrupted()) { + try { + Message message = _acknowledgeQueue.poll(250, TimeUnit.MILLISECONDS); + if (message == null) continue; + acknowledge(message); + } catch (Exception any) { + logger.error("exception acking: ", any); + } + } + } finally { + logger.error("AcknowledgeTask exiting!"); + } + } + } + + private class ArchiveTask implements Runnable { + + @Override + public void run() { + try { + while (!Thread.interrupted()) { + try { + Message message = _archiveQueue.poll(250, TimeUnit.MILLISECONDS); + if (message == null) continue; + archive(message); + } catch (Exception any) { + logger.error("exception archiving: ", any); + } + } + } finally { + logger.error("ArchiveTask exiting!"); + } + } + } + + private class StatusTask implements Runnable { + private static final int STATUS_FREQUENCY_SECONDS = 60; + + @Override + public void run() { + while (!Thread.interrupted()) { + try { + long lastAvlReportTime = (System.currentTimeMillis() - AvlProcessor.getInstance().lastAvlReportTime())/1000; + logger.info("Queue Size Report: AVL last report {}s, recieve={}, deserialize={}, ack={}, archive={}", + lastAvlReportTime, + _receiveQueue.size(), + _deserializeQueue.size(), + _acknowledgeQueue.size(), + _archiveQueue.size()); + // lastAvlReportTime is already reported as LatestAvlReportAgeInSeconds + monitoring.averageMetric("PredictionReceiveQueueSize", new Double(_receiveQueue.size())); + monitoring.averageMetric("PredictionDeserializeQueueSize", new Double(_deserializeQueue.size())); + monitoring.averageMetric("PredictionAckQueueSize", new Double(_acknowledgeQueue.size())); + monitoring.averageMetric("PredictionArchiveQueueSize", new Double(_archiveQueue.size())); + Thread.sleep(STATUS_FREQUENCY_SECONDS * 1000); + } catch (Exception any) { + logger.error("exception with status: ", any); + } + } + } + } + +} diff --git a/transitclock/src/main/java/org/transitclock/custom/aws/DashAvlTypeUnmarshaller.java b/transitclock/src/main/java/org/transitclock/custom/aws/DashAvlTypeUnmarshaller.java new file mode 100644 index 000000000..563fb53bd --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/custom/aws/DashAvlTypeUnmarshaller.java @@ -0,0 +1,156 @@ +package org.transitclock.custom.aws; + +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.db.structs.AvlReport; +import org.transitclock.db.structs.AvlReport.AssignmentType; + +import com.amazonaws.services.sqs.model.Message; + +/** + * Implementation of SqsMessageUnmarshaller for DASH data. + * + */ +public class DashAvlTypeUnmarshaller implements SqsMessageUnmarshaller { + + private static final Logger logger = + LoggerFactory.getLogger(WmataAvlTypeUnmarshaller.class); + + @Override + public List toAvlReports(Message message) { + List reports = new ArrayList(); + String body = message.getBody(); + + if (body.startsWith("[")) { + reports.addAll(toAvlReportsFromJsonArray(body)); + } else if (body.contains("SigningCertURL")) { + // SNS to SQS leaves some artifacts + // TODO find out why this happens + try { + JSONObject jsonObj = new JSONObject(message.getBody()); + String content = jsonObj.getString("Message"); + reports.addAll(toAvlReportsFromJsonArray(content)); + } catch (JSONException e) { + // try one last time + reports.add(toAvlReport(message)); + } + } + else { + // not an array, try to deserialize as is + reports.add(toAvlReport(message)); + } + return reports; + } + + + private List toAvlReportsFromJsonArray(String body) { + List reports = new ArrayList(); + if (body == null) return reports; + // we have an array + JSONArray jsonArray = new JSONArray(body); + for (int i=0; i toAvlReports(Message message); + +} diff --git a/transitclock/src/main/java/org/transitclock/custom/aws/WmataAvlTypeUnmarshaller.java b/transitclock/src/main/java/org/transitclock/custom/aws/WmataAvlTypeUnmarshaller.java new file mode 100644 index 000000000..3be90c130 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/custom/aws/WmataAvlTypeUnmarshaller.java @@ -0,0 +1,156 @@ +package org.transitclock.custom.aws; + +import java.util.ArrayList; +import java.util.List; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.db.structs.AvlReport; +import org.transitclock.db.structs.AvlReport.AssignmentType; + +import com.amazonaws.services.sqs.model.Message; + +/** + * Implementation of SqsMessageUnmarshaller for WMATA data. + * + */ +public class WmataAvlTypeUnmarshaller implements SqsMessageUnmarshaller { + + private static final Logger logger = + LoggerFactory.getLogger(WmataAvlTypeUnmarshaller.class); + + @Override + public List toAvlReports(Message message) { + List reports = new ArrayList(); + String body = message.getBody(); + + if (body.startsWith("[")) { + reports.addAll(toAvlReportsFromJsonArray(body)); + } else if (body.contains("SigningCertURL")) { + // SNS to SQS leaves some artifacts + // TODO find out why this happens + try { + JSONObject jsonObj = new JSONObject(message.getBody()); + String content = jsonObj.getString("Message"); + reports.addAll(toAvlReportsFromJsonArray(content)); + } catch (JSONException e) { + // try one last time + reports.add(toAvlReport(message)); + } + } + else { + // not an array, try to deserialize as is + reports.add(toAvlReport(message)); + } + return reports; + } + + + private List toAvlReportsFromJsonArray(String body) { + List reports = new ArrayList(); + if (body == null) return reports; + // we have an array + JSONArray jsonArray = new JSONArray(body); + for (int i=0; i. + */ +package org.transitclock.custom.barefoot; + +import java.io.InputStream; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; + +import org.transitclock.avl.NextBusBarefootAvlModule; +import org.transitclock.avl.PollUrlAvlModule; +import org.transitclock.config.DoubleConfigValue; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.config.StringConfigValue; +import org.transitclock.db.structs.AvlReport; +import org.zeromq.ZMQ; +import org.zeromq.ZMQ.Context; +import org.zeromq.ZMQ.Socket; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.db.structs.Location; +import com.esri.core.geometry.GeometryEngine; +import com.esri.core.geometry.Point; +import com.esri.core.geometry.WktImportFlags; +import com.esri.core.geometry.Geometry.Type; + +/** + * @author Sean Óg Crudden + * + * This module subscribes to a barefoot server to get map matched GPS + * data. + * + * + */ +public class BarefootAVLModule extends PollUrlAvlModule { + + private static IntegerConfigValue barefootPort = new IntegerConfigValue("transitclock.avl.barefoot.subscribe.port", + 1235, "This is the port of the barefoot server to subscribe to."); + + private static StringConfigValue barefootServer = new StringConfigValue( + "transitclock.avl.barefoot.subscribe.server", "127.0.0.1", + "This is the server that is running the barefoot map matching service."); + + private static DoubleConfigValue minProbability = new DoubleConfigValue( + "transitclock.avl.barefoot.subscribe.minprobability", 0.1, + "Only use values that have a probability of being correct higher than this."); + + private static final Logger logger = LoggerFactory.getLogger(BarefootAVLModule.class); + + private static HashMap locations = new HashMap(); + + public BarefootAVLModule(String agencyId) { + super(agencyId); + + } + + @Override + protected void getAndProcessData() throws Exception { + + // Prepare our context and subscriber + + Context context = ZMQ.context(1); + + Socket subscriber = context.socket(ZMQ.SUB); + + subscriber.connect("tcp://" + barefootServer.getValue() + ":" + barefootPort.getValue()); + + subscriber.subscribe("".getBytes()); + + while (!Thread.currentThread().isInterrupted()) { + + String contents = subscriber.recvStr(); + logger.debug("Got content from barefoot : " + contents); + + JSONObject update = new JSONObject(contents); + + String vehicleId = update.getString("id"); + + Long time = update.getLong("time"); + Location location = getAdjustedLocation(vehicleId, update); + AvlReport avlReport = null; + if (location != null) { + avlReport = new AvlReport(vehicleId, time, location.getLat(), location.getLon(), "Barefoot"); + logger.debug("AVL from barefoot to be processed : " + avlReport); + processAvlReport(avlReport); + } + } + subscriber.close(); + context.term(); + } + + @Override + protected Collection processData(InputStream in) throws Exception { + // TODO Auto-generated method stub + return null; + } + + private Location getAdjustedLocation(String vehicleId, JSONObject state) { + + Location result = null; + try { + + JSONArray candidatesArray = state.getJSONArray("candidates"); + + Double maxProbability = 0.0; + + for (int i = 0; i < candidatesArray.length(); i++) { + double probabililty = candidatesArray.getJSONObject(i).getDouble("prob"); + + String geoPoint = candidatesArray.getJSONObject(i).getString("point"); + + Point point = (Point) GeometryEngine.geometryFromWkt(geoPoint, WktImportFlags.wktImportDefaults, + Type.Point); + + // Only consider new or modified GPS values + if ((locations.get(vehicleId) != null && !locations.get(vehicleId).equals(point)) + || locations.get(vehicleId) == null) { + if (probabililty > minProbability.getValue()) { + + maxProbability = probabililty; + + result = new Location(point.getY(), point.getX()); + + locations.put(vehicleId, point); + } + } + } + } catch (Exception e) { + logger.error("Failed to adjust location with barefoot. Reason: " + e.getMessage()); + e.printStackTrace(); + } + return result; + } + + protected double bearing(double lat1, double lon1, double lat2, double lon2) { + double longitude1 = lon1; + double longitude2 = lon2; + double latitude1 = Math.toRadians(lat1); + double latitude2 = Math.toRadians(lat2); + double longDiff = Math.toRadians(longitude2 - longitude1); + double y = Math.sin(longDiff) * Math.cos(latitude2); + double x = Math.cos(latitude1) * Math.sin(latitude2) + - Math.sin(latitude1) * Math.cos(latitude2) * Math.cos(longDiff); + + return (Math.toDegrees(Math.atan2(y, x)) + 360) % 360; + } +} diff --git a/transitclock/src/main/java/org/transitclock/custom/bullrunner/BullrunnerPlaybackModule.java b/transitclock/src/main/java/org/transitclock/custom/bullrunner/BullrunnerPlaybackModule.java new file mode 100644 index 000000000..49f4d3dff --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/custom/bullrunner/BullrunnerPlaybackModule.java @@ -0,0 +1,191 @@ +package org.transitclock.custom.bullrunner; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.applications.Core; +import org.transitclock.avl.PollUrlAvlModule; +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.Time; + +/** + * @author scrudden + * Throw away module. Just used to read in archived Bullrunner data to work on Kalman predictions for frequency based services. + * + */ +public class BullrunnerPlaybackModule extends PollUrlAvlModule { + + private static StringConfigValue bullrunnerbasePlaybackUrl = + new StringConfigValue("transitclock.avl.bullrunnerbasePlaybackUrl", "http://transit.jadorno.com/bullrunner/gtfsrt/vehicle_positions", + "The URL of the historical json feed to use."); + + private static String getPlaybackStartTimeStr() { + return playbackStartTimeStr.getValue(); + } + + private static StringConfigValue playbackStartTimeStr = + new StringConfigValue("transitclock.avl.bullrunner.playbackStartTime", + "08-06-2018 12:00:00", + "Date and time of when to start the playback."); + + private static StringConfigValue playbackEndTimeStr = + new StringConfigValue("transitclock.avl.bullrunner.playbackEndTime", + "08-09-2018 23:00:00", + "Date and time of when to end the playback."); + + + + private static StringConfigValue runnerTestRoute = + new StringConfigValue("transitclock.avl.testroute", null, + "Route to test against."); + + private static final Logger logger = LoggerFactory + .getLogger(BullrunnerPlaybackModule.class); + + public BullrunnerPlaybackModule(String agencyId) { + super(agencyId); + + if(latesttime==null) + latesttime=parsePlaybackStartTime(playbackStartTimeStr.getValue()); + + } + Long latesttime=null; + + // If debugging feed and want to not actually process + // AVL reports to generate predictions and such then + // set shouldProcessAvl to false; + private static boolean shouldProcessAvl = true; + @Override + protected String getUrl() { + + return bullrunnerbasePlaybackUrl.getValue()+"?timestamp="+latesttime; + } + + public static void main(String[] args) { + // TODO Auto-generated method stub + Module.start("org.transitclock.custom.bullrunner.BullrunnerPlaybackModule"); + } + + @Override + protected Collection processData(InputStream in) throws Exception { + + String jsonStr = getJsonString(in); + Collection avlReportsReadIn = new ArrayList(); + try { + // Convert JSON string to a JSON object + JSONObject jsonObj = new JSONObject(jsonStr); + + JSONArray entities=(JSONArray) jsonObj.get("entity"); + JSONObject header=(JSONObject) jsonObj.get("header"); + + //JSONArray vehicles = entityObj.getJSONArray("vehicle"); + + for(int i=0;i(); + + } + + } + + + 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.bullrunner.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.bullrunner.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; + } + } + private 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; + } + } +} diff --git a/transitime/src/main/java/org/transitime/custom/georgiaTech/GeorgiaTechAvlModule.java b/transitclock/src/main/java/org/transitclock/custom/georgiaTech/GeorgiaTechAvlModule.java similarity index 88% rename from transitime/src/main/java/org/transitime/custom/georgiaTech/GeorgiaTechAvlModule.java rename to transitclock/src/main/java/org/transitclock/custom/georgiaTech/GeorgiaTechAvlModule.java index 24aebab77..9332ca394 100644 --- a/transitime/src/main/java/org/transitime/custom/georgiaTech/GeorgiaTechAvlModule.java +++ b/transitclock/src/main/java/org/transitclock/custom/georgiaTech/GeorgiaTechAvlModule.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.custom.georgiaTech; +package org.transitclock.custom.georgiaTech; import java.io.BufferedReader; import java.io.DataInputStream; @@ -32,14 +32,14 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.avl.AvlModule; -import org.transitime.avl.NmeaGpsLocation; -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.Time; +import org.transitclock.avl.AvlModule; +import org.transitclock.avl.NmeaGpsLocation; +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.Time; /** * Gets GPS data from the Georgia Tech AVL feed. This feed is different from @@ -52,12 +52,12 @@ public class GeorgiaTechAvlModule extends AvlModule { private static StringConfigValue georgiaTechFeedDomainName = - new StringConfigValue("transitime.avl.georgiaTechFeedDomainName", + new StringConfigValue("transitclock.avl.georgiaTechFeedDomainName", "The domain name for the socket connection for the " + "Georgia Tech AVLfeed."); private static IntegerConfigValue georgiaTechFeedPort = - new IntegerConfigValue("transitime.avl.georgiaTechFeedPort", + new IntegerConfigValue("transitclock.avl.georgiaTechFeedPort", "The port number for the socket connection for the " + "Georgia Tech AVLfeed."); @@ -238,7 +238,7 @@ public static void main(String[] args) { shouldProcessAvl = false; // Create a NextBusAvlModue for testing - Module.start("org.transitime.custom.georgiaTech.GeorgiaTechAvlModule"); + Module.start("org.transitclock.custom.georgiaTech.GeorgiaTechAvlModule"); } } diff --git a/transitime/src/main/java/org/transitime/custom/georgiaTech/package-info.java b/transitclock/src/main/java/org/transitclock/custom/georgiaTech/package-info.java similarity index 94% rename from transitime/src/main/java/org/transitime/custom/georgiaTech/package-info.java rename to transitclock/src/main/java/org/transitclock/custom/georgiaTech/package-info.java index e74724862..87f5bb186 100644 --- a/transitime/src/main/java/org/transitime/custom/georgiaTech/package-info.java +++ b/transitclock/src/main/java/org/transitclock/custom/georgiaTech/package-info.java @@ -21,4 +21,4 @@ * @author SkiBu Smith * */ -package org.transitime.custom.georgiaTech; \ No newline at end of file +package org.transitclock.custom.georgiaTech; \ No newline at end of file diff --git a/transitclock/src/main/java/org/transitclock/custom/gtt/GTTAvlModule.java b/transitclock/src/main/java/org/transitclock/custom/gtt/GTTAvlModule.java new file mode 100755 index 000000000..85c7b7ae1 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/custom/gtt/GTTAvlModule.java @@ -0,0 +1,125 @@ +package org.transitclock.custom.gtt; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.transitclock.avl.PollUrlAvlModule; +import org.transitclock.config.StringListConfigValue; +import org.transitclock.db.structs.AvlReport; +import org.transitclock.db.structs.AvlReport.AssignmentType; +import org.transitclock.modules.Module; + +public class GTTAvlModule extends PollUrlAvlModule { + + private static String avlURL="http://m.gatech.edu/api/buses/position"; + HashMap avlreports = new HashMap(); + + protected static StringListConfigValue vehiclesonredroute = new StringListConfigValue("transitclock.gtt.vehiclesonredroute",null, "List of vehicles on read route for HoldingTime trial."); + + public GTTAvlModule(String agencyId) { + super(agencyId); + } + + @Override + protected String getUrl() { + + return avlURL; + } + + @Override + protected Collection processData(InputStream in) throws Exception { + + String json=this.getJsonString(in); + // The return value for the method + Collection avlReportsReadIn = new ArrayList(); + + JSONArray array = new JSONArray(json); + + for (int i=0; i 0) { + delayLength = avlReport.getTime() - lastAvlReportTimestamp; + lastAvlReportTimestamp = avlReport.getTime(); + } else { + lastAvlReportTimestamp = avlReport.getTime(); + } + + if (delayLength > 0) + Time.sleep(delayLength); + } + } + +} diff --git a/transitclock/src/main/java/org/transitclock/custom/irishrail/GtfsRtAvlModule.java b/transitclock/src/main/java/org/transitclock/custom/irishrail/GtfsRtAvlModule.java new file mode 100755 index 000000000..7f91b901e --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/custom/irishrail/GtfsRtAvlModule.java @@ -0,0 +1,107 @@ +/* + * 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.custom.irishrail; + +import java.io.InputStream; +import java.util.Collection; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.avl.GtfsRealtimeModule; +import org.transitclock.config.StringConfigValue; +import org.transitclock.db.structs.AvlReport; +import org.transitclock.feed.gtfsRt.GtfsRtVehiclePositionsReader; + + +/** + * This is a seperate module to read the GTFS realtime realtime data form the GTFS-RT vehicle location url. + * This is to allow for the less accurate realtime info be read from else where. + * + * @author Sean Og Crudden + * + */ +public class GtfsRtAvlModule extends GtfsRealtimeModule { + + private static final Logger logger = LoggerFactory + .getLogger(GtfsRtAvlModule.class); + + /*********** Configurable Parameters for this module ***********/ + public static String getGtfsRealtimeURI() { + return gtfsRealtimeFeedURI.getValue(); + } + + private static StringConfigValue gtfsRealtimeFeedURI = + new StringConfigValue("transitclock.avl.gtfsRealtimeFeedURI", + "http://developer.onebusaway.org/wmata-gtfsr/vehiclePositions", + "The URI of the GTFS-realtime feed generated form the gtfs-realtime data."); + + /********************** Member Functions **************************/ + + /** + * @param projectId + */ + public GtfsRtAvlModule(String projectId) { + super(projectId); + } + + /** + * 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(","); + + for (String urlStr : urls) { + try { + logger.info("reading {}", urlStr); + List avlReports = GtfsRtVehiclePositionsReader + .getAvlReports(urlStr); + logger.info("read complete"); + for (AvlReport avlReport : avlReports) { + avlReport.setSource("gtfsrt"); + processAvlReport(avlReport); + } + logger.info("processed {} avl reports for feed {}", avlReports.size(), urlStr); + } catch (Exception any) { + logger.error("issues processing feed {}:{}", urlStr, any, any); + } + } + } + + /* (non-Javadoc) + * @see org.transitclock.avl.AvlModule#getUrl() + */ + @Override + protected String getUrl() { + return getGtfsRealtimeURI(); + } + + /* Not needed since all processing for this class is done in + * the overridden getAndProcessData(). + * (non-Javadoc) + * @see org.transitclock.avl.AvlModule#processData(java.io.InputStream) + */ + @Override + protected Collection processData(InputStream in) throws Exception { + return null; // we've override getAndProcessData so this need not do anything + } +} diff --git a/transitclock/src/main/java/org/transitclock/custom/lametro/LametroNextBusAvlModule.java b/transitclock/src/main/java/org/transitclock/custom/lametro/LametroNextBusAvlModule.java new file mode 100644 index 000000000..d93886496 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/custom/lametro/LametroNextBusAvlModule.java @@ -0,0 +1,163 @@ +/* + * 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.custom.lametro; + +import org.jdom2.Document; +import org.jdom2.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.avl.NextBusAvlModule; +import org.transitclock.config.StringConfigValue; +import org.transitclock.db.structs.AvlReport; +import org.transitclock.utils.Geo; +import org.transitclock.utils.MathUtils; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.TimeZone; + +/** + * For lametro agency the block assignments from the feed don't + * match to the GTFS data. Therefore this module must be used + * for the sfmta AVL feed. + * + * @author SkiBu Smith + * + */ +public class LametroNextBusAvlModule extends NextBusAvlModule { + + private static final Logger logger = + LoggerFactory.getLogger(LametroNextBusAvlModule.class); + + private static StringConfigValue xmlFeedURI = new StringConfigValue("transitclock.avl.xmlFeedURI", + "http://example.com", + "nextbus avl feed URI"); + + /** + * @param agencyId + */ + public LametroNextBusAvlModule(String agencyId) { + super(agencyId); + } + + @Override + protected String getUrl() { + return xmlFeedURI.getValue(); + //return "http://data.nextbus.com/service/customerfeed/lametro/avl?command=lastdata"; + } + + @Override + protected Collection extractAvlData(Document doc) + throws NumberFormatException { + try { + logger.info("Extracting data from xml file..."); + // Get root of doc + Element rootNode = doc.getRootElement(); + + // Handle any error message + Element error = rootNode.getChild("Error"); + if (error != null) { + String errorStr = error.getTextNormalize(); + logger.error("While processing AVL data in NextBusAvlModule: " + errorStr); + return new ArrayList(); + } + + // The return value for the method + Collection avlReportsReadIn = new ArrayList(); + // Handle getting vehicle location data + Element vehiclesNode = rootNode.getChild("vehicles"); + List vehicles = vehiclesNode.getChildren(); + for (Element vehicle : vehicles) { + String vehicleId = vehicle.getChild("id").getValue(); + String dateStr = vehicle.getChild("date").getValue(); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + Date date = null; + try { + if (dateStr != null) { + sdf.setTimeZone(TimeZone.getTimeZone("UTC")); + date = sdf.parse(dateStr); + logger.info("created date {}", date); + } else { + logger.info("missing date for {}", vehicleId); + continue; + } + } catch (ParseException e) { + logger.error("invalid date {}, skipping", dateStr); + continue; + } + if (date == null) { + logger.error("missing(1) date for {}", vehicleId); + continue; + } + float lat = Float.parseFloat(vehicle.getChild("lat").getValue()); + float lon = Float.parseFloat(vehicle.getChild("lon").getValue()); + String speedStr = null; + if (vehicle.getChild("speed") != null) + speedStr = vehicle.getChild("speed").getValue(); // appears to be in kmh + float speed = 0.0f; + if (speedStr != null) + speed = Geo.converKmPerHrToMetersPerSecond(Integer.parseInt(speedStr)); + Integer bearing = Integer.parseInt(vehicle.getChild("direction").getValue()); + String blockId = null; + if (vehicle.getChild("block") != null) + blockId = vehicle.getChild("block").getValue(); + String operator = null; + if (vehicle.getChild("driver") != null) + operator = vehicle.getChild("driver").getValue(); + + AvlReport avlReport = new AvlReport(vehicleId, date.getTime(), + MathUtils.round(lat, 5), MathUtils.round(lon, 5), + speed, bearing, "NextBus", null, operator, + null, + null, + Float.NaN); + avlReport.setAssignment(blockId, AvlReport.AssignmentType.BLOCK_ID); + avlReportsReadIn.add(avlReport); + } + logger.info("processed {} records", avlReportsReadIn.size()); + return avlReportsReadIn; + } catch (Throwable t) { + logger.info("horrible mistake: ", t); + return new ArrayList(); + } finally { + logger.info("exiting xml data extraction"); + } + } + + /** + * At least for sfmta agency they don't use a leading 0 in the block ID in + * the GTFS data. Therefore to match strip out leading zeros from the block + * ID here. + * + * @param originalBlockIdFromFeed + * the block ID to be modified + * @return the modified block ID that corresponds to the GTFS data + */ + @Override + protected String processBlockId(String originalBlockIdFromFeed) { + String block = originalBlockIdFromFeed; + while (block != null && block.startsWith("0")) + block = block.substring(1); + return block; + } + +} diff --git a/transitime/src/main/java/org/transitime/custom/lametro/LametroRailNextBusAvlModule.java b/transitclock/src/main/java/org/transitclock/custom/lametro/LametroRailNextBusAvlModule.java similarity index 71% rename from transitime/src/main/java/org/transitime/custom/lametro/LametroRailNextBusAvlModule.java rename to transitclock/src/main/java/org/transitclock/custom/lametro/LametroRailNextBusAvlModule.java index 6dcd29994..f4965d2d7 100644 --- a/transitime/src/main/java/org/transitime/custom/lametro/LametroRailNextBusAvlModule.java +++ b/transitclock/src/main/java/org/transitclock/custom/lametro/LametroRailNextBusAvlModule.java @@ -14,10 +14,16 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.custom.lametro; +package org.transitclock.custom.lametro; -import org.transitime.avl.NextBusAvlModule; -import org.transitime.config.StringConfigValue; +import org.jdom2.Document; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.avl.NextBusAvlModule; +import org.transitclock.config.StringConfigValue; +import org.transitclock.db.structs.AvlReport; + +import java.util.Collection; /** * For lametro agency using two separate AVL feeds so need a @@ -29,9 +35,9 @@ public class LametroRailNextBusAvlModule extends NextBusAvlModule { private static StringConfigValue agencyNameForFeed = - new StringConfigValue("transitime.custom.lametro.agencyNameForLametroRailFeed", + new StringConfigValue("transitclock.custom.lametro.agencyNameForLametroRailFeed", "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."); @Override protected String getAgencyNameForFeed() { @@ -44,6 +50,6 @@ protected String getAgencyNameForFeed() { public LametroRailNextBusAvlModule(String agencyId) { super(agencyId); } - + } diff --git a/transitime/src/main/java/org/transitime/custom/lametro/package-info.java b/transitclock/src/main/java/org/transitclock/custom/lametro/package-info.java similarity index 60% rename from transitime/src/main/java/org/transitime/custom/lametro/package-info.java rename to transitclock/src/main/java/org/transitclock/custom/lametro/package-info.java index a41161922..8f45f0586 100644 --- a/transitime/src/main/java/org/transitime/custom/lametro/package-info.java +++ b/transitclock/src/main/java/org/transitclock/custom/lametro/package-info.java @@ -5,4 +5,4 @@ * @author Michael * */ -package org.transitime.custom.lametro; \ No newline at end of file +package org.transitclock.custom.lametro; \ No newline at end of file diff --git a/transitclock/src/main/java/org/transitclock/custom/mbta/BusLocAvlModule.java b/transitclock/src/main/java/org/transitclock/custom/mbta/BusLocAvlModule.java new file mode 100644 index 000000000..805a7d566 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/custom/mbta/BusLocAvlModule.java @@ -0,0 +1,152 @@ +/* + * This file is part of transitclock.org + * + * transitclock.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. + * + * transitclock.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.custom.mbta; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.avl.AvlModule; +import org.transitclock.avl.PollUrlAvlModule; +import org.transitclock.config.StringConfigValue; +import org.transitclock.db.structs.AvlReport; +import org.transitclock.db.structs.AvlReport.AssignmentType; +import org.transitclock.modules.Module; + +/** + * AVL module for reading AVL data from MBTA BusLoc feed. + * + * @author Sean Óg Crudden + * + */ +public class BusLocAvlModule extends PollUrlAvlModule { + + private static StringConfigValue mbtaBusLocFeedUrl = + new StringConfigValue("transitclock.avl.mbtaBusLocFeedUrl", "https://s3.amazonaws.com/mbta-busloc-s3/VehiclePositions_enhanced.json", + "The URL of the BusLoc json feed to use."); + + + private static StringConfigValue mbtaTestRoute = + new StringConfigValue("transitclock.avl.testroute", "77", + "Route to test against."); + + + // If debugging feed and want to not actually process + // AVL reports to generate predictions and such then + // set shouldProcessAvl to false; + private static boolean shouldProcessAvl = true; + + + + // For logging use AvlModule class so that will end up in the AVL log file + private static final Logger logger = LoggerFactory + .getLogger(AvlModule.class); + + /********************** Member Functions **************************/ + + /** + * Constructor + * + * @param agencyId + */ + public BusLocAvlModule(String agencyId) { + super(agencyId); + } + + /** + * Returns URL to use + */ + @Override + protected String getUrl() { + return mbtaBusLocFeedUrl.getValue(); + } + + /** + * Called when AVL data is read from URL. Processes the JSON data and calls + * processAvlReport() for each AVL report. + */ + @Override + protected Collection processData(InputStream in) throws Exception { + // Get the JSON string containing the AVL data + String jsonStr = getJsonString(in); + Collection avlReportsReadIn = new ArrayList(); + try { + // Convert JSON string to a JSON object + JSONObject jsonObj = new JSONObject(jsonStr); + + JSONArray entities=(JSONArray) jsonObj.get("entity"); + + //JSONArray vehicles = entityObj.getJSONArray("vehicle"); + + for(int i=0;i(); + + } + + } + + /** + * Just for debugging + */ + public static void main(String[] args) { + // For debugging turn off the actual processing of the AVL data. + // This way the AVL data is logged, but that is all. + shouldProcessAvl = false; + + // Create a BusLocAvlModule for testing + Module.start("org.transitclock.custom.mbta.BusLocAvlModule"); + } + +} diff --git a/transitime/src/main/java/org/transitime/custom/mbta/GenerateMbtaBlockInfo.java b/transitclock/src/main/java/org/transitclock/custom/mbta/GenerateMbtaBlockInfo.java similarity index 97% rename from transitime/src/main/java/org/transitime/custom/mbta/GenerateMbtaBlockInfo.java rename to transitclock/src/main/java/org/transitclock/custom/mbta/GenerateMbtaBlockInfo.java index e9aba606b..e632b8c4b 100644 --- a/transitime/src/main/java/org/transitime/custom/mbta/GenerateMbtaBlockInfo.java +++ b/transitclock/src/main/java/org/transitclock/custom/mbta/GenerateMbtaBlockInfo.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.custom.mbta; +package org.transitclock.custom.mbta; import java.text.ParseException; import java.util.ArrayList; @@ -31,13 +31,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.db.structs.AvlReport; -import org.transitime.gtfs.gtfsStructs.GtfsStopTime; -import org.transitime.gtfs.gtfsStructs.GtfsTrip; -import org.transitime.gtfs.readers.GtfsStopTimesReader; -import org.transitime.gtfs.readers.GtfsTripsReader; -import org.transitime.gtfs.writers.GtfsTripsWriter; -import org.transitime.utils.Time; +import org.transitclock.db.structs.AvlReport; +import org.transitclock.gtfs.gtfsStructs.GtfsStopTime; +import org.transitclock.gtfs.gtfsStructs.GtfsTrip; +import org.transitclock.gtfs.readers.GtfsStopTimesReader; +import org.transitclock.gtfs.readers.GtfsTripsReader; +import org.transitclock.gtfs.writers.GtfsTripsWriter; +import org.transitclock.utils.Time; /** * Processes MBTA AVL feed data one day at a time to determine the which block a diff --git a/transitclock/src/main/java/org/transitclock/custom/mbta/HighFreqAvlModule.java b/transitclock/src/main/java/org/transitclock/custom/mbta/HighFreqAvlModule.java new file mode 100644 index 000000000..006d972cd --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/custom/mbta/HighFreqAvlModule.java @@ -0,0 +1,167 @@ +/* + * This file is part of transitclock.org + * + * transitclock.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. + * + * transitclock.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.custom.mbta; + +import java.io.InputStream; +import java.text.SimpleDateFormat; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.avl.AvlModule; +import org.transitclock.avl.PollUrlAvlModule; +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.Time; + +/** + * AVL module for reading AVL data from MBTA High freq feed. + * + * @author Sean Óg Crudden + * + */ +public class HighFreqAvlModule extends PollUrlAvlModule { + + private static StringConfigValue feedUrl = + new StringConfigValue("transitclock.avl.feedUrl", "https://api-v3.mbta.com/vehicles/?filter%5Bid%5D=y1696,y1708,y1709,y1710,y1717,y1718&sort=-updated_at", + "The URL of the BusLoc json feed to use."); + + + private static StringConfigValue mbtaTestRoute = + new StringConfigValue("transitclock.avl.testroute", null, + "Route to test against."); + + + // If debugging feed and want to not actually process + // AVL reports to generate predictions and such then + // set shouldProcessAvl to false; + private static boolean shouldProcessAvl = true; + + + + // For logging use AvlModule class so that will end up in the AVL log file + private static final Logger logger = LoggerFactory + .getLogger(AvlModule.class); + + /********************** Member Functions **************************/ + + /** + * Constructor + * + * @param agencyId + */ + public HighFreqAvlModule(String agencyId) { + super(agencyId); + } + + /** + * Returns URL to use + */ + @Override + protected String getUrl() { + return feedUrl.getValue(); + } + + /** + * Called when AVL data is read from URL. Processes the JSON data and calls + * processAvlReport() for each AVL report. + */ + @Override + protected Collection processData(InputStream in) throws Exception { + // Get the JSON string containing the AVL data + String jsonStr = getJsonString(in); + Collection avlReportsReadIn = new ArrayList(); + try { + // Convert JSON string to a JSON object + JSONObject jsonObj = new JSONObject(jsonStr); + + + JSONArray entities=(JSONArray) jsonObj.get("data"); + + //JSONArray vehicles = entityObj.getJSONArray("vehicle"); + + for(int i=0;i(); + + } + + } + + /** + * Just for debugging + */ + public static void main(String[] args) { + // For debugging turn off the actual processing of the AVL data. + // This way the AVL data is logged, but that is all. + shouldProcessAvl = false; + + // Create a BusLocAvlModule for testing + Module.start("org.transitclock.custom.mbta.HighFreqAvlModule"); + } + +} diff --git a/transitime/src/main/java/org/transitime/custom/mbta/KeolisAvlModule.java b/transitclock/src/main/java/org/transitclock/custom/mbta/KeolisAvlModule.java similarity index 85% rename from transitime/src/main/java/org/transitime/custom/mbta/KeolisAvlModule.java rename to transitclock/src/main/java/org/transitclock/custom/mbta/KeolisAvlModule.java index cd8a5137c..92680bdae 100644 --- a/transitime/src/main/java/org/transitime/custom/mbta/KeolisAvlModule.java +++ b/transitclock/src/main/java/org/transitclock/custom/mbta/KeolisAvlModule.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.custom.mbta; +package org.transitclock.custom.mbta; import java.io.InputStream; import java.util.ArrayList; @@ -24,30 +24,30 @@ import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.avl.AvlModule; -import org.transitime.avl.PollUrlAvlModule; -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.Time; +import org.transitclock.avl.AvlModule; +import org.transitclock.avl.PollUrlAvlModule; +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.Time; /** * AVL module for reading AVL data from Keolis feed. * - * @author Michael Smith (michael@transitime.org) + * @author Michael Smith (michael@transitclock.org) * */ public class KeolisAvlModule extends PollUrlAvlModule { private static StringConfigValue mbtaCommuterRailFeedUrl = - new StringConfigValue("transitime.avl.mbtaCommuterRailFeedUrl", + new StringConfigValue("transitclock.avl.mbtaCommuterRailFeedUrl", "The URL of the Keolis feed to use."); private static IntegerConfigValue keolisFeedAvlTimeOffset = - new IntegerConfigValue("transitime.mbta.keolisFeedAvlTimeOffset", + new IntegerConfigValue("transitclock.mbta.keolisFeedAvlTimeOffset", 5, "The GPS time from the Keolis feed has a strange 4 or 5 " + "hour offset. It appears that it is 4 hours during " @@ -157,6 +157,7 @@ protected Collection processData(InputStream in) throws Exception { logger.error("Error parsing JSON. {}. {}", e.getMessage(), jsonStr, e); return new ArrayList(); + } } @@ -170,7 +171,7 @@ public static void main(String[] args) { shouldProcessAvl = false; // Create a NextBusAvlModue for testing - Module.start("org.transitime.custom.mbta.KeolisAvlModule"); + Module.start("org.transitclock.custom.mbta.KeolisAvlModule"); } } diff --git a/transitime/src/main/java/org/transitime/custom/mbta/MbtaCommuterRailAvlModule.java b/transitclock/src/main/java/org/transitclock/custom/mbta/MbtaCommuterRailAvlModule.java similarity index 86% rename from transitime/src/main/java/org/transitime/custom/mbta/MbtaCommuterRailAvlModule.java rename to transitclock/src/main/java/org/transitclock/custom/mbta/MbtaCommuterRailAvlModule.java index a2da20945..7c94c7b69 100644 --- a/transitime/src/main/java/org/transitime/custom/mbta/MbtaCommuterRailAvlModule.java +++ b/transitclock/src/main/java/org/transitclock/custom/mbta/MbtaCommuterRailAvlModule.java @@ -15,7 +15,7 @@ * Transitime.org . If not, see . */ -package org.transitime.custom.mbta; +package org.transitclock.custom.mbta; import java.io.BufferedReader; import java.io.InputStream; @@ -29,13 +29,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.avl.AvlModule; -import org.transitime.avl.PollUrlAvlModule; -import org.transitime.avl.TaipGpsLocation; -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.transitclock.avl.AvlModule; +import org.transitclock.avl.PollUrlAvlModule; +import org.transitclock.avl.TaipGpsLocation; +import org.transitclock.config.StringConfigValue; +import org.transitclock.db.structs.AvlReport; +import org.transitclock.db.structs.AvlReport.AssignmentType; +import org.transitclock.modules.Module; /** * Gets GPS data from the MBTA Commuter Rail AVL feed. The data comes from a URL @@ -58,7 +58,7 @@ public class MbtaCommuterRailAvlModule extends PollUrlAvlModule { /********************** Member Functions **************************/ private static StringConfigValue mbtaCommuterRailFeedUrl = - new StringConfigValue("transitime.avl.mbtaCommuterRailFeedUrl", + new StringConfigValue("transitclock.avl.mbtaCommuterRailFeedUrl", "The URL of the MBTA commuter rail feed to use."); private static String getMbtaCommuterRailFeedUrl() { @@ -77,7 +77,7 @@ public MbtaCommuterRailAvlModule(String agencyId) { /* * (non-Javadoc) * - * @see org.transitime.avl.AvlModule#getUrl() + * @see org.transitclock.avl.AvlModule#getUrl() */ @Override protected String getUrl() { @@ -87,7 +87,7 @@ protected String getUrl() { /* * (non-Javadoc) * - * @see org.transitime.avl.AvlModule#processData(java.io.InputStream) + * @see org.transitclock.avl.AvlModule#processData(java.io.InputStream) */ @Override protected Collection processData(InputStream in) throws Exception { @@ -162,6 +162,22 @@ else if (assignmentFromFeed.length() == 4 // Assignment is OK as is so return it return assignmentFromFeed; +// +// // Handle special null case +// if (assignmentFromFeed == null) +// return assignmentFromFeed; +// +// // Assignment is too short so pad it +// if (assignmentFromFeed.length() == 1) +// return "00" + assignmentFromFeed; +// else if (assignmentFromFeed.length() == 2) +// return "0" + assignmentFromFeed; +// else if (assignmentFromFeed.length() == 4 +// && assignmentFromFeed.endsWith("00")) +// return assignmentFromFeed.substring(0, 2); +// +// // Assignment is OK as is so return it +// return assignmentFromFeed; } // The following are determining the proper place in the @@ -277,7 +293,7 @@ public static void main(String[] args) { shouldProcessAvl = false; // Create a NextBusAvlModue for testing - Module.start("org.transitime.custom.mbta.MbtaCommuterRailAvlModule"); + Module.start("org.transitclock.custom.mbta.MbtaCommuterRailAvlModule"); } } diff --git a/transitime/src/main/java/org/transitime/custom/mbta/MbtaNextBusAvlModule.java b/transitclock/src/main/java/org/transitclock/custom/mbta/MbtaNextBusAvlModule.java similarity index 94% rename from transitime/src/main/java/org/transitime/custom/mbta/MbtaNextBusAvlModule.java rename to transitclock/src/main/java/org/transitclock/custom/mbta/MbtaNextBusAvlModule.java index a222fecaf..ab929ddc7 100644 --- a/transitime/src/main/java/org/transitime/custom/mbta/MbtaNextBusAvlModule.java +++ b/transitclock/src/main/java/org/transitclock/custom/mbta/MbtaNextBusAvlModule.java @@ -15,9 +15,9 @@ * along with Transitime.org . If not, see . */ -package org.transitime.custom.mbta; +package org.transitclock.custom.mbta; -import org.transitime.avl.NextBusAvlModule; +import org.transitclock.avl.NextBusAvlModule; /** * For mbta the block assignments from the feed don't diff --git a/transitime/src/main/java/org/transitime/custom/mbta/MbtaPredictionAccuracyModule.java b/transitclock/src/main/java/org/transitclock/custom/mbta/MbtaPredictionAccuracyModule.java similarity index 93% rename from transitime/src/main/java/org/transitime/custom/mbta/MbtaPredictionAccuracyModule.java rename to transitclock/src/main/java/org/transitclock/custom/mbta/MbtaPredictionAccuracyModule.java index 43fda2817..ec3735fac 100644 --- a/transitime/src/main/java/org/transitime/custom/mbta/MbtaPredictionAccuracyModule.java +++ b/transitclock/src/main/java/org/transitclock/custom/mbta/MbtaPredictionAccuracyModule.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.custom.mbta; +package org.transitclock.custom.mbta; import java.io.IOException; import java.io.InputStream; @@ -33,13 +33,13 @@ import org.jdom2.input.SAXBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.applications.Core; -import org.transitime.config.StringConfigValue; -import org.transitime.core.predAccuracy.PredAccuracyPrediction; -import org.transitime.core.predAccuracy.PredictionAccuracyModule; -import org.transitime.db.structs.StopPath; -import org.transitime.db.structs.Trip; -import org.transitime.modules.Module; +import org.transitclock.applications.Core; +import org.transitclock.config.StringConfigValue; +import org.transitclock.core.predAccuracy.PredAccuracyPrediction; +import org.transitclock.core.predAccuracy.PredictionAccuracyModule; +import org.transitclock.db.structs.StopPath; +import org.transitclock.db.structs.Trip; +import org.transitclock.modules.Module; /** * Reads in external prediction data from MBTA feed and stores the data in @@ -62,7 +62,7 @@ public class MbtaPredictionAccuracyModule extends PredictionAccuracyModule { /********************** Config Params **************************/ private static final StringConfigValue externalPredictionApiUrl = - new StringConfigValue("transitime.predAccuracy.externalPredictionApiUrl", + new StringConfigValue("transitclock.predAccuracy.externalPredictionApiUrl", "http://realtime.mbta.com/developer/api/v2/predictionsbyroute?", "URL to access to obtain external predictions."); @@ -71,7 +71,7 @@ private static String getExternalPredictionApiUrl() { } private static final StringConfigValue apiKey = - new StringConfigValue("transitime.predAccuracy.apiKey", + new StringConfigValue("transitclock.predAccuracy.apiKey", "wX9NwuHnZU2ToO7GmGR9uw", "The API key to use when accessing the external prediction " + "feed. The default value is the public MBTA key, which " @@ -275,7 +275,7 @@ private void processExternalPredictionsForRoute( PredAccuracyPrediction pred = new PredAccuracyPrediction( routeId, directionId, stopId, tripId, vehicleId, predictedTime, predictionsReadTime, isArrival, - null, "MBTA_epoch"); + null, "MBTA_epoch",null, null); storePrediction(pred); } } @@ -315,7 +315,7 @@ protected void getAndProcessData(List routesAndStops, */ public static void main(String[] args) { // Create a MbtaPredictionReaderModule for testing - Module.start("org.transitime.core.predAccuracy.MbtaPredictionReaderModule"); + Module.start("org.transitclock.core.predAccuracy.MbtaPredictionReaderModule"); } } diff --git a/transitime/src/main/java/org/transitime/custom/mbta/package-info.java b/transitclock/src/main/java/org/transitclock/custom/mbta/package-info.java similarity index 95% rename from transitime/src/main/java/org/transitime/custom/mbta/package-info.java rename to transitclock/src/main/java/org/transitclock/custom/mbta/package-info.java index c61a972ff..08d10fcd7 100644 --- a/transitime/src/main/java/org/transitime/custom/mbta/package-info.java +++ b/transitclock/src/main/java/org/transitclock/custom/mbta/package-info.java @@ -21,4 +21,4 @@ * @author SkiBu Smith * */ -package org.transitime.custom.mbta; \ No newline at end of file +package org.transitclock.custom.mbta; \ No newline at end of file diff --git a/transitime/src/main/java/org/transitime/custom/missionBay/ArrDepGeneratorMissionBayImpl.java b/transitclock/src/main/java/org/transitclock/custom/missionBay/ArrDepGeneratorMissionBayImpl.java similarity index 91% rename from transitime/src/main/java/org/transitime/custom/missionBay/ArrDepGeneratorMissionBayImpl.java rename to transitclock/src/main/java/org/transitclock/custom/missionBay/ArrDepGeneratorMissionBayImpl.java index 8edf9243c..807b61e88 100644 --- a/transitime/src/main/java/org/transitime/custom/missionBay/ArrDepGeneratorMissionBayImpl.java +++ b/transitclock/src/main/java/org/transitclock/custom/missionBay/ArrDepGeneratorMissionBayImpl.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.custom.missionBay; +package org.transitclock.custom.missionBay; import java.util.ArrayList; import java.util.HashMap; @@ -25,13 +25,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.core.ArrivalDepartureGeneratorDefaultImpl; -import org.transitime.core.VehicleState; -import org.transitime.db.structs.Arrival; -import org.transitime.db.structs.Block; -import org.transitime.db.structs.Departure; -import org.transitime.db.structs.Location; -import org.transitime.utils.Time; +import org.transitclock.core.ArrivalDepartureGeneratorDefaultImpl; +import org.transitclock.core.VehicleState; +import org.transitclock.db.structs.Arrival; +import org.transitclock.db.structs.Block; +import org.transitclock.db.structs.Departure; +import org.transitclock.db.structs.Location; +import org.transitclock.utils.Time; /** * Handles generation of Arrivals and Departures by extending the @@ -79,7 +79,7 @@ public ArrDepGeneratorMissionBayImpl() { * API. */ protected Arrival createArrivalTime(VehicleState vehicleState, - long arrivalTime, Block block, int tripIndex, int stopPathIndex) { + long arrivalTime, Block block, int tripIndex, int stopPathIndex, String stopPathId) { // Call parent class createArrivalTime() Arrival arrival = super.createArrivalTime(vehicleState, arrivalTime, block, tripIndex, stopPathIndex); @@ -99,10 +99,10 @@ protected Arrival createArrivalTime(VehicleState vehicleState, * written to the SFMTA API. */ protected Departure createDepartureTime(VehicleState vehicleState, - long departureTime, Block block, int tripIndex, int stopPathIndex) { + long departureTime, Block block, int tripIndex, int stopPathIndex, Long dwellTime) { // Call parent class createDepartureTime() Departure departure = super.createDepartureTime(vehicleState, departureTime, block, - tripIndex, stopPathIndex); + tripIndex, stopPathIndex, dwellTime); // If special SFMTA stop then write arrival/departure time to SFMTA API if (sfmtaStopMap.containsKey(departure.getStopId())) { diff --git a/transitime/src/main/java/org/transitime/custom/missionBay/GtfsFromNextBus.java b/transitclock/src/main/java/org/transitclock/custom/missionBay/GtfsFromNextBus.java similarity index 93% rename from transitime/src/main/java/org/transitime/custom/missionBay/GtfsFromNextBus.java rename to transitclock/src/main/java/org/transitclock/custom/missionBay/GtfsFromNextBus.java index 0ef4240ce..9fa5ea76f 100644 --- a/transitime/src/main/java/org/transitime/custom/missionBay/GtfsFromNextBus.java +++ b/transitclock/src/main/java/org/transitclock/custom/missionBay/GtfsFromNextBus.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.custom.missionBay; +package org.transitclock.custom.missionBay; import java.io.IOException; import java.io.InputStream; @@ -36,20 +36,20 @@ import org.jdom2.input.SAXBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.config.StringConfigValue; -import org.transitime.config.StringListConfigValue; -import org.transitime.db.structs.Location; -import org.transitime.gtfs.gtfsStructs.GtfsRoute; -import org.transitime.gtfs.gtfsStructs.GtfsShape; -import org.transitime.gtfs.gtfsStructs.GtfsStop; -import org.transitime.gtfs.gtfsStructs.GtfsStopTime; -import org.transitime.gtfs.gtfsStructs.GtfsTrip; -import org.transitime.gtfs.writers.GtfsRoutesWriter; -import org.transitime.gtfs.writers.GtfsShapesWriter; -import org.transitime.gtfs.writers.GtfsStopTimesWriter; -import org.transitime.gtfs.writers.GtfsStopsWriter; -import org.transitime.gtfs.writers.GtfsTripsWriter; -import org.transitime.utils.Time; +import org.transitclock.config.StringConfigValue; +import org.transitclock.config.StringListConfigValue; +import org.transitclock.db.structs.Location; +import org.transitclock.gtfs.gtfsStructs.GtfsRoute; +import org.transitclock.gtfs.gtfsStructs.GtfsShape; +import org.transitclock.gtfs.gtfsStructs.GtfsStop; +import org.transitclock.gtfs.gtfsStructs.GtfsStopTime; +import org.transitclock.gtfs.gtfsStructs.GtfsTrip; +import org.transitclock.gtfs.writers.GtfsRoutesWriter; +import org.transitclock.gtfs.writers.GtfsShapesWriter; +import org.transitclock.gtfs.writers.GtfsStopTimesWriter; +import org.transitclock.gtfs.writers.GtfsStopsWriter; +import org.transitclock.gtfs.writers.GtfsTripsWriter; +import org.transitclock.utils.Time; /** * For generating some of the GTFS files for an agency by reading data from the @@ -63,39 +63,39 @@ public class GtfsFromNextBus { /******************* Parameters *************************/ private static StringConfigValue agencyId = - new StringConfigValue("transitime.gtfs.agencyId", + new StringConfigValue("transitclock.gtfs.agencyId", "missionBay", "Agency name used in resulting GTFS files."); private static StringConfigValue nextBusAgencyId = - new StringConfigValue("transitime.gtfs.nextBusAgencyId", + new StringConfigValue("transitclock.gtfs.nextBusAgencyId", "sf-mission-bay", "Agency name that NextBus uses."); private static StringConfigValue nextBusFeedUrl = - new StringConfigValue("transitime.gtfs.nextbusFeedUrl", + new StringConfigValue("transitclock.gtfs.nextbusFeedUrl", "http://webservices.nextbus.com/service/publicXMLFeed", "The URL of the NextBus feed to use."); private static StringConfigValue gtfsDirectory = - new StringConfigValue("transitime.gtfs.gtfsDirectory", + new StringConfigValue("transitclock.gtfs.gtfsDirectory", "C:/GTFS/", "Directory where resulting GTFS files are to be written."); private static StringConfigValue gtfsRouteType = - new StringConfigValue("transitime.gtfs.gtfsRouteType", + new StringConfigValue("transitclock.gtfs.gtfsRouteType", "3", "GTFS definition of the route type. 1=subway 2=rail " + "3=buses."); private static StringConfigValue serviceId = - new StringConfigValue("transitime.gtfs.serviceId", + new StringConfigValue("transitclock.gtfs.serviceId", "wkd", "The service_id to use for the trips.txt file. Currently " + "only can handle a single service ID."); private static StringListConfigValue validBlockIds = - new StringListConfigValue("transitime.gtfs.validBlockIds", + new StringListConfigValue("transitclock.gtfs.validBlockIds", "Only the block IDs from this list will be process from " + "the schedule from the NextBus API. Useful since NextBus " + "doesn't always clean out obsolete blocks");; diff --git a/transitime/src/main/java/org/transitime/custom/missionBay/MissionBayAvlModule.java b/transitclock/src/main/java/org/transitclock/custom/missionBay/MissionBayAvlModule.java similarity index 91% rename from transitime/src/main/java/org/transitime/custom/missionBay/MissionBayAvlModule.java rename to transitclock/src/main/java/org/transitclock/custom/missionBay/MissionBayAvlModule.java index baadd8964..32a7d8cea 100644 --- a/transitime/src/main/java/org/transitime/custom/missionBay/MissionBayAvlModule.java +++ b/transitclock/src/main/java/org/transitclock/custom/missionBay/MissionBayAvlModule.java @@ -15,12 +15,12 @@ * along with Transitime.org . If not, see . */ -package org.transitime.custom.missionBay; +package org.transitclock.custom.missionBay; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.avl.NextBusAvlModule; -import org.transitime.db.structs.AvlReport; +import org.transitclock.avl.NextBusAvlModule; +import org.transitclock.db.structs.AvlReport; /** * diff --git a/transitime/src/main/java/org/transitime/custom/missionBay/MissionBayConfig.java b/transitclock/src/main/java/org/transitclock/custom/missionBay/MissionBayConfig.java similarity index 96% rename from transitime/src/main/java/org/transitime/custom/missionBay/MissionBayConfig.java rename to transitclock/src/main/java/org/transitclock/custom/missionBay/MissionBayConfig.java index 694de17fc..19a244b99 100644 --- a/transitime/src/main/java/org/transitime/custom/missionBay/MissionBayConfig.java +++ b/transitclock/src/main/java/org/transitclock/custom/missionBay/MissionBayConfig.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.custom.missionBay; +package org.transitclock.custom.missionBay; import java.util.ArrayList; import java.util.Arrays; @@ -23,7 +23,7 @@ import java.util.List; import java.util.Map; -import org.transitime.custom.missionBay.GtfsFromNextBus.Dir; +import org.transitclock.custom.missionBay.GtfsFromNextBus.Dir; /** * Contains GTFS config information of stop and paths for the directions. Wanted diff --git a/transitime/src/main/java/org/transitime/custom/missionBay/SfmtaApiCaller.java b/transitclock/src/main/java/org/transitclock/custom/missionBay/SfmtaApiCaller.java similarity index 95% rename from transitime/src/main/java/org/transitime/custom/missionBay/SfmtaApiCaller.java rename to transitclock/src/main/java/org/transitclock/custom/missionBay/SfmtaApiCaller.java index 40f60ee3b..e506a084b 100644 --- a/transitime/src/main/java/org/transitime/custom/missionBay/SfmtaApiCaller.java +++ b/transitclock/src/main/java/org/transitclock/custom/missionBay/SfmtaApiCaller.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.custom.missionBay; +package org.transitclock.custom.missionBay; import java.io.BufferedReader; import java.io.DataOutputStream; @@ -35,9 +35,9 @@ import org.apache.commons.codec.binary.Base64; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.config.IntegerConfigValue; -import org.transitime.config.StringConfigValue; -import org.transitime.db.structs.AvlReport; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.config.StringConfigValue; +import org.transitclock.db.structs.AvlReport; /** * @@ -48,43 +48,43 @@ public class SfmtaApiCaller { private static StringConfigValue telemetryUrl = - new StringConfigValue("transitime.sfmta.telemetryUrl", + new StringConfigValue("transitclock.sfmta.telemetryUrl", "https://services.sfmta.com/shuttle/api/Telemetries/", "The telemetry URL for the SFMTA API"); private static StringConfigValue stopsUrl = - new StringConfigValue("transitime.sfmta.telemetryUrl", + new StringConfigValue("transitclock.sfmta.telemetryUrl", "https://services.sfmta.com/shuttle/api/StopEvents/", "The stops URL for the SFMTA API"); private static StringConfigValue login = - new StringConfigValue("transitime.sfmta.login", + new StringConfigValue("transitclock.sfmta.login", "TransitTimeApiUser", "Login for basic authentication."); private static StringConfigValue password = - new StringConfigValue("transitime.sfmta.password", + new StringConfigValue("transitclock.sfmta.password", "SFMTA$hutt1e$3G", "Password for basic authentication."); private static StringConfigValue techProviderId = - new StringConfigValue("transitime.sfmta.techProviderId", + new StringConfigValue("transitclock.sfmta.techProviderId", "248", "The TechProviderId for the SFMTA API"); private static StringConfigValue shuttleCompanyId = - new StringConfigValue("transitime.sfmta.shuttleCompanyId", + new StringConfigValue("transitclock.sfmta.shuttleCompanyId", "11", "The ShuttleCompanyId for the SFMTA API"); private static IntegerConfigValue timeout = - new IntegerConfigValue("transitime.sfmta.timeout", + new IntegerConfigValue("transitclock.sfmta.timeout", 10000, "Timeout in msec for API calls. A timeout of 0 is " + "interpreted as an infinit timeout."); private static IntegerConfigValue avlReportsBatchSize = - new IntegerConfigValue("transitime.sfmta.avlReportsBatchSize", + new IntegerConfigValue("transitclock.sfmta.avlReportsBatchSize", 10, "Won't actually post AVL reports to SFMTA API until this " + "many have been received."); diff --git a/transitime/src/main/java/org/transitime/custom/missionBay/package-info.java b/transitclock/src/main/java/org/transitclock/custom/missionBay/package-info.java similarity index 94% rename from transitime/src/main/java/org/transitime/custom/missionBay/package-info.java rename to transitclock/src/main/java/org/transitclock/custom/missionBay/package-info.java index 84b417b4e..c9e8f21bf 100644 --- a/transitime/src/main/java/org/transitime/custom/missionBay/package-info.java +++ b/transitclock/src/main/java/org/transitclock/custom/missionBay/package-info.java @@ -21,4 +21,4 @@ * @author SkiBu Smith * */ -package org.transitime.custom.missionBay; \ No newline at end of file +package org.transitclock.custom.missionBay; \ No newline at end of file diff --git a/transitime/src/main/java/org/transitime/custom/nyc/BusTimeSiriAvlModule.java b/transitclock/src/main/java/org/transitclock/custom/nyc/BusTimeSiriAvlModule.java similarity index 95% rename from transitime/src/main/java/org/transitime/custom/nyc/BusTimeSiriAvlModule.java rename to transitclock/src/main/java/org/transitclock/custom/nyc/BusTimeSiriAvlModule.java index 4e511044b..f0a41f415 100644 --- a/transitime/src/main/java/org/transitime/custom/nyc/BusTimeSiriAvlModule.java +++ b/transitclock/src/main/java/org/transitclock/custom/nyc/BusTimeSiriAvlModule.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.custom.nyc; +package org.transitclock.custom.nyc; import java.text.DateFormat; import java.text.ParseException; @@ -28,11 +28,11 @@ import org.jdom2.Namespace; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.avl.XmlPollingAvlModule; -import org.transitime.db.structs.AvlReport; -import org.transitime.db.structs.AvlReport.AssignmentType; -import org.transitime.modules.Module; -import org.transitime.utils.Time; +import org.transitclock.avl.XmlPollingAvlModule; +import org.transitclock.db.structs.AvlReport; +import org.transitclock.db.structs.AvlReport.AssignmentType; +import org.transitclock.modules.Module; +import org.transitclock.utils.Time; /** * Reads AVL data from the NYC MTA BusTime Siri AVL feed and publishes it to @@ -198,7 +198,7 @@ protected Collection extractAvlData(Document doc) */ public static void main(String[] args) { // Create a BustimeSiriAvlModule for testing - Module.start("org.transitime.avl.BusTimeSiriAvlModule"); + Module.start("org.transitclock.avl.BusTimeSiriAvlModule"); } } diff --git a/transitime/src/main/java/org/transitime/custom/nyc/package-info.java b/transitclock/src/main/java/org/transitclock/custom/nyc/package-info.java similarity index 95% rename from transitime/src/main/java/org/transitime/custom/nyc/package-info.java rename to transitclock/src/main/java/org/transitclock/custom/nyc/package-info.java index 8cb292546..6f3d2bb9a 100644 --- a/transitime/src/main/java/org/transitime/custom/nyc/package-info.java +++ b/transitclock/src/main/java/org/transitclock/custom/nyc/package-info.java @@ -21,4 +21,4 @@ * @author SkiBu Smith * */ -package org.transitime.custom.nyc; \ No newline at end of file +package org.transitclock.custom.nyc; \ No newline at end of file diff --git a/transitime/src/main/java/org/transitime/custom/package-info.java b/transitclock/src/main/java/org/transitclock/custom/package-info.java similarity index 96% rename from transitime/src/main/java/org/transitime/custom/package-info.java rename to transitclock/src/main/java/org/transitclock/custom/package-info.java index c60e314a3..b12883e3b 100644 --- a/transitime/src/main/java/org/transitime/custom/package-info.java +++ b/transitclock/src/main/java/org/transitclock/custom/package-info.java @@ -21,4 +21,4 @@ * @author SkiBu Smith * */ -package org.transitime.custom; \ No newline at end of file +package org.transitclock.custom; \ No newline at end of file diff --git a/transitime/src/main/java/org/transitime/custom/sfmta/MuniNextBusAvlModule.java b/transitclock/src/main/java/org/transitclock/custom/sfmta/MuniNextBusAvlModule.java similarity index 94% rename from transitime/src/main/java/org/transitime/custom/sfmta/MuniNextBusAvlModule.java rename to transitclock/src/main/java/org/transitclock/custom/sfmta/MuniNextBusAvlModule.java index 487743dbd..af8363f46 100644 --- a/transitime/src/main/java/org/transitime/custom/sfmta/MuniNextBusAvlModule.java +++ b/transitclock/src/main/java/org/transitclock/custom/sfmta/MuniNextBusAvlModule.java @@ -15,9 +15,9 @@ * along with Transitime.org . If not, see . */ -package org.transitime.custom.sfmta; +package org.transitclock.custom.sfmta; -import org.transitime.avl.NextBusAvlModule; +import org.transitclock.avl.NextBusAvlModule; /** * For sfmta agency the block assignments from the feed don't diff --git a/transitime/src/main/java/org/transitime/custom/sfmta/delayTimes/Intersection.java b/transitclock/src/main/java/org/transitclock/custom/sfmta/delayTimes/Intersection.java similarity index 96% rename from transitime/src/main/java/org/transitime/custom/sfmta/delayTimes/Intersection.java rename to transitclock/src/main/java/org/transitclock/custom/sfmta/delayTimes/Intersection.java index 32c64d373..786c6ba40 100644 --- a/transitime/src/main/java/org/transitime/custom/sfmta/delayTimes/Intersection.java +++ b/transitclock/src/main/java/org/transitclock/custom/sfmta/delayTimes/Intersection.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.custom.sfmta.delayTimes; +package org.transitclock.custom.sfmta.delayTimes; import java.io.BufferedReader; import java.io.FileInputStream; @@ -29,9 +29,9 @@ import org.apache.commons.csv.CSVRecord; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.db.structs.Location; -import org.transitime.db.structs.Vector; -import org.transitime.utils.Geo; +import org.transitclock.db.structs.Location; +import org.transitclock.db.structs.Vector; +import org.transitclock.utils.Geo; /** * Defines the two vectors that define an intersection. The vector segment diff --git a/transitime/src/main/java/org/transitime/custom/sfmta/delayTimes/IntersectionMatcher.java b/transitclock/src/main/java/org/transitclock/custom/sfmta/delayTimes/IntersectionMatcher.java similarity index 90% rename from transitime/src/main/java/org/transitime/custom/sfmta/delayTimes/IntersectionMatcher.java rename to transitclock/src/main/java/org/transitclock/custom/sfmta/delayTimes/IntersectionMatcher.java index 8c1e35b78..89d16ca1c 100644 --- a/transitime/src/main/java/org/transitime/custom/sfmta/delayTimes/IntersectionMatcher.java +++ b/transitclock/src/main/java/org/transitclock/custom/sfmta/delayTimes/IntersectionMatcher.java @@ -15,13 +15,13 @@ * along with Transitime.org . If not, see . */ -package org.transitime.custom.sfmta.delayTimes; +package org.transitclock.custom.sfmta.delayTimes; import java.util.List; -import org.transitime.db.structs.Location; -import org.transitime.db.structs.Vector; -import org.transitime.utils.Geo; +import org.transitclock.db.structs.Location; +import org.transitclock.db.structs.Vector; +import org.transitclock.utils.Geo; /** * diff --git a/transitime/src/main/java/org/transitime/custom/sfmta/delayTimes/Loc.java b/transitclock/src/main/java/org/transitclock/custom/sfmta/delayTimes/Loc.java similarity index 96% rename from transitime/src/main/java/org/transitime/custom/sfmta/delayTimes/Loc.java rename to transitclock/src/main/java/org/transitclock/custom/sfmta/delayTimes/Loc.java index 5c1dd0f2d..f5fb55c13 100644 --- a/transitime/src/main/java/org/transitime/custom/sfmta/delayTimes/Loc.java +++ b/transitclock/src/main/java/org/transitclock/custom/sfmta/delayTimes/Loc.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.custom.sfmta.delayTimes; +package org.transitclock.custom.sfmta.delayTimes; import java.io.BufferedReader; import java.io.FileInputStream; @@ -30,8 +30,8 @@ import org.apache.commons.csv.CSVRecord; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.db.structs.Location; -import org.transitime.utils.StringUtils; +import org.transitclock.db.structs.Location; +import org.transitclock.utils.StringUtils; /** * A structure that represents a location datapoint diff --git a/transitime/src/main/java/org/transitime/custom/sfmta/delayTimes/SfmtaTrackerDataProcessor.java b/transitclock/src/main/java/org/transitclock/custom/sfmta/delayTimes/SfmtaTrackerDataProcessor.java similarity index 98% rename from transitime/src/main/java/org/transitime/custom/sfmta/delayTimes/SfmtaTrackerDataProcessor.java rename to transitclock/src/main/java/org/transitclock/custom/sfmta/delayTimes/SfmtaTrackerDataProcessor.java index efb6c03df..347e875e0 100644 --- a/transitime/src/main/java/org/transitime/custom/sfmta/delayTimes/SfmtaTrackerDataProcessor.java +++ b/transitclock/src/main/java/org/transitclock/custom/sfmta/delayTimes/SfmtaTrackerDataProcessor.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.custom.sfmta.delayTimes; +package org.transitclock.custom.sfmta.delayTimes; import java.util.ArrayList; import java.util.Date; @@ -23,10 +23,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.db.structs.Location; -import org.transitime.utils.Geo; -import org.transitime.utils.StringUtils; -import org.transitime.utils.Time; +import org.transitclock.db.structs.Location; +import org.transitclock.utils.Geo; +import org.transitclock.utils.StringUtils; +import org.transitclock.utils.Time; /** * An experiment for determining using GPS how much time is lost diff --git a/transitime/src/main/java/org/transitime/custom/sfmta/delayTimes/package-info.java b/transitclock/src/main/java/org/transitclock/custom/sfmta/delayTimes/package-info.java similarity index 94% rename from transitime/src/main/java/org/transitime/custom/sfmta/delayTimes/package-info.java rename to transitclock/src/main/java/org/transitclock/custom/sfmta/delayTimes/package-info.java index c0e7a566a..15b04ddbb 100644 --- a/transitime/src/main/java/org/transitime/custom/sfmta/delayTimes/package-info.java +++ b/transitclock/src/main/java/org/transitclock/custom/sfmta/delayTimes/package-info.java @@ -22,4 +22,4 @@ * @author SkiBu Smith * */ -package org.transitime.custom.sfmta.delayTimes; \ No newline at end of file +package org.transitclock.custom.sfmta.delayTimes; \ No newline at end of file diff --git a/transitime/src/main/java/org/transitime/custom/sfmta/package-info.java b/transitclock/src/main/java/org/transitclock/custom/sfmta/package-info.java similarity index 95% rename from transitime/src/main/java/org/transitime/custom/sfmta/package-info.java rename to transitclock/src/main/java/org/transitclock/custom/sfmta/package-info.java index 73c2a9a16..7389a1090 100644 --- a/transitime/src/main/java/org/transitime/custom/sfmta/package-info.java +++ b/transitclock/src/main/java/org/transitclock/custom/sfmta/package-info.java @@ -21,4 +21,4 @@ * @author SkiBu Smith * */ -package org.transitime.custom.sfmta; \ No newline at end of file +package org.transitclock.custom.sfmta; \ No newline at end of file diff --git a/transitclock/src/main/java/org/transitclock/custom/swordsexpress/SwordsExpressAvlModule.java b/transitclock/src/main/java/org/transitclock/custom/swordsexpress/SwordsExpressAvlModule.java new file mode 100755 index 000000000..5ce1aa2a8 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/custom/swordsexpress/SwordsExpressAvlModule.java @@ -0,0 +1,70 @@ +package org.transitclock.custom.swordsexpress; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.text.SimpleDateFormat; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.transitclock.avl.PollUrlAvlModule; +import org.transitclock.db.structs.AvlReport; +import org.transitclock.modules.Module; + +public class SwordsExpressAvlModule extends PollUrlAvlModule { + + private static String avlURL="http://www.swordsexpress.com/latlong.php?id=100774022844716920000"; + + public SwordsExpressAvlModule(String agencyId) { + super(agencyId); + } + + @Override + protected String getUrl() { + + return avlURL; + } + + @Override + protected Collection processData(InputStream in) throws Exception { + + Collection reports = new ArrayList(); + + String json=this.getJsonString(in); + Collection avlReportsReadIn = new ArrayList(); + JSONArray array = new JSONArray(json); + SimpleDateFormat dateformater=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + for (int i=0; i2) + { + //["07G2339","53.348110","-6.248768","2016-09-07 22:35:53","1","30 kmph","w"] + String vehicleId = entryarray.getString(0); + Double latitude=new Double(entryarray.getString(1)); + Double longitude=new Double(entryarray.getString(2)); + + Date timestamp=dateformater.parse(entryarray.getString(3)); + float heading=Float.NaN; + + float speed=Float.NaN; + + AvlReport avlReport = + new AvlReport(vehicleId, timestamp.getTime(), latitude, + longitude, heading, speed, "Swords Express"); + + + avlReportsReadIn.add(avlReport); + } + } + return avlReportsReadIn; + } + /** + * Just for debugging + */ + public static void main(String[] args) { + // Create a WexfordCoachAvlModule for testing + Module.start("org.transitclock.custom.swordsexpress.SwordsExpressAvlModule"); + } +} diff --git a/transitclock/src/main/java/org/transitclock/custom/traccar/TraccarAVLModule.java b/transitclock/src/main/java/org/transitclock/custom/traccar/TraccarAVLModule.java new file mode 100644 index 000000000..467007890 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/custom/traccar/TraccarAVLModule.java @@ -0,0 +1,109 @@ +/* + * This file is part of thetransitclock.org + * + * thetransitclock.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. + * + * thetransitclock.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 thetransitclock.org . If not, see . + */ +package org.transitclock.custom.traccar; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.avl.AmigoCloudAvlModule; +import org.transitclock.avl.PollUrlAvlModule; +import org.transitclock.config.StringConfigValue; +import org.transitclock.db.structs.AvlReport; +import org.transitclock.db.structs.AvlReport.AssignmentType; +import io.swagger.client.ApiClient; +import io.swagger.client.api.DefaultApi; +import io.swagger.client.model.Position; +import io.swagger.client.model.User; + +/** + * @author Sean Óg Crudden This module integrates TheTransitClock with the API of a traccar + * server to get vehicle locations. + * + * See http://www.traccar.org + * + * It uses classes that where generated using the swagger file provided + * with traccar. + * + */ +public class TraccarAVLModule extends PollUrlAvlModule { + + private User user = null; + private DefaultApi api = null; + + private static StringConfigValue traccarEmail = new StringConfigValue("transitclock.avl.traccar.email", "admin", + "This is the username for the traccar server api."); + + private static StringConfigValue traccarPassword = new StringConfigValue("transitclock.avl.traccar.password", + "admin", "This is the password for the traccar server api"); + + private static StringConfigValue traccarBaseUrl = new StringConfigValue("transitclock.avl.traccar.baseurl", + "http://127.0.0.1:8082/api", "This is the url for the traccar server api."); + + private static StringConfigValue traccarSource = new StringConfigValue("transitclock.avl.traccar.source", + "This is the value recorded in the source for the AVL Report." + "is used.", "Traccar"); + + private static final Logger logger = LoggerFactory.getLogger(TraccarAVLModule.class); + + public TraccarAVLModule(String agencyId) throws Throwable { + super(agencyId); + api = new DefaultApi(); + ApiClient client = new ApiClient(); + client.setBasePath(traccarBaseUrl.getValue()); + client.setUsername(traccarEmail.getValue()); + client.setPassword(traccarPassword.getValue()); + api.setApiClient(client); + user = api.sessionPost(traccarEmail.getValue(), traccarPassword.getValue()); + if (user != null) + logger.debug("Traccar login succeeded."); + } + + @Override + protected void getAndProcessData() throws Exception { + + Collection avlReportsReadIn = new ArrayList(); + if (api != null && user != null) { + List results = api.positionsGet(null, null, null, user.getId()); + for (Position result : results) { + logger.debug(result.toString()); + + AvlReport avlReport = new AvlReport(result.getDeviceId().toString(), + result.getDeviceTime().toDate().getTime(), result.getLatitude().doubleValue(), + result.getLongitude().doubleValue(), traccarSource.toString()); + + avlReportsReadIn.add(avlReport); + } + + forwardAvlReports(avlReportsReadIn); + } + } + + @Override + protected Collection processData(InputStream in) throws Exception { + // TODO Auto-generated method stub + return null; + } + + protected void forwardAvlReports(Collection avlReportsReadIn) + { + processAvlReports(avlReportsReadIn); + } + +} \ No newline at end of file diff --git a/transitclock/src/main/java/org/transitclock/custom/traccar/TraccarBarefootAVLModule.java b/transitclock/src/main/java/org/transitclock/custom/traccar/TraccarBarefootAVLModule.java new file mode 100644 index 000000000..04c789b57 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/custom/traccar/TraccarBarefootAVLModule.java @@ -0,0 +1,124 @@ +/* + * This file is part of thetransitclock.org + * + * thetransitclock.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. + * + * thetransitclock.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 thetransitclock.org . If not, see . + */ +package org.transitclock.custom.traccar; + +import java.util.Collection; + +import org.transitclock.db.structs.AvlReport; + +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.config.StringConfigValue; +import com.esri.core.geometry.Point; +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; + +/** + * @author Sean Óg Crudden + * + * This module takes data from traccar and forwards it to a barefoot + * server instances to have it map matched to a GTFS route. + * + * The BarefootAVLModule should be configured with this module to read + * the adjusted AVL from the barefoot server for processing by + * TheTransitClock. + */ +public class TraccarBarefootAVLModule extends TraccarAVLModule { + private static final Logger logger = LoggerFactory.getLogger(TraccarBarefootAVLModule.class); + + private static IntegerConfigValue barefootPort = new IntegerConfigValue("transitclock.avl.barefoot.port", 1235, + "This is the port of the barefoot server to send samples to."); + + private static StringConfigValue barefootServer = new StringConfigValue("transitclock.avl.barefoot.server", + "127.0.0.1", "This is the server that is running the barefoot service."); + + public TraccarBarefootAVLModule(String agencyId) throws Throwable { + super(agencyId); + } + @Override + protected void forwardAvlReports(Collection avlReportsReadIn) { + for(AvlReport avlReport:avlReportsReadIn) + { + sendUpdate(avlReport); + } + } + + public void sendUpdate(AvlReport avlReport) { + try { + JSONObject report = new JSONObject(); + + InetAddress host = InetAddress.getByName(barefootServer.getValue()); + + 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, barefootPort.getValue(), 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"); + } + } +} diff --git a/transitime/src/main/java/org/transitime/custom/vta/VtaAcsAvlModule.java b/transitclock/src/main/java/org/transitclock/custom/vta/VtaAcsAvlModule.java similarity index 83% rename from transitime/src/main/java/org/transitime/custom/vta/VtaAcsAvlModule.java rename to transitclock/src/main/java/org/transitclock/custom/vta/VtaAcsAvlModule.java index 6ca19c973..2126ce051 100644 --- a/transitime/src/main/java/org/transitime/custom/vta/VtaAcsAvlModule.java +++ b/transitclock/src/main/java/org/transitclock/custom/vta/VtaAcsAvlModule.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.custom.vta; +package org.transitclock.custom.vta; import java.io.BufferedReader; import java.io.InputStream; @@ -25,12 +25,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.avl.PollUrlAvlModule; -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.Time; +import org.transitclock.avl.PollUrlAvlModule; +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.Time; /** * For VTA Orbital ACS system. Reads in a text file containing the GPS info. @@ -41,7 +41,7 @@ public class VtaAcsAvlModule extends PollUrlAvlModule { private static StringConfigValue feedUrl = - new StringConfigValue("transitime.avl.vta.url", + new StringConfigValue("transitclock.avl.vta.url", "The URL of the ACS feed to use."); private static final Logger logger = LoggerFactory @@ -99,7 +99,7 @@ protected Collection processData(InputStream in) throws Exception { * Just for debugging */ public static void main(String[] args) { - Module.start("org.transitime.custom.vta.VtaAcsAvlModule"); + Module.start("org.transitclock.custom.vta.VtaAcsAvlModule"); } } diff --git a/transitime/src/main/java/org/transitime/custom/vta/package-info.java b/transitclock/src/main/java/org/transitclock/custom/vta/package-info.java similarity index 64% rename from transitime/src/main/java/org/transitime/custom/vta/package-info.java rename to transitclock/src/main/java/org/transitclock/custom/vta/package-info.java index 25f3940b3..e3333d256 100644 --- a/transitime/src/main/java/org/transitime/custom/vta/package-info.java +++ b/transitclock/src/main/java/org/transitclock/custom/vta/package-info.java @@ -5,4 +5,4 @@ * @author Michael Smith * */ -package org.transitime.custom.vta; \ No newline at end of file +package org.transitclock.custom.vta; \ No newline at end of file diff --git a/transitclock/src/main/java/org/transitclock/custom/wexfordcoach/WexfordCoachAvlModule.java b/transitclock/src/main/java/org/transitclock/custom/wexfordcoach/WexfordCoachAvlModule.java new file mode 100755 index 000000000..bff61cb80 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/custom/wexfordcoach/WexfordCoachAvlModule.java @@ -0,0 +1,70 @@ +package org.transitclock.custom.wexfordcoach; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.text.SimpleDateFormat; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.transitclock.avl.PollUrlAvlModule; +import org.transitclock.db.structs.AvlReport; +import org.transitclock.modules.Module; + +public class WexfordCoachAvlModule extends PollUrlAvlModule { + + private static String avlURL="http://www.textmemybus.com/live_bus_positions/bus_positions.json"; + + public WexfordCoachAvlModule(String agencyId) { + super(agencyId); + } + + @Override + protected String getUrl() { + + return avlURL; + } + + @Override + protected Collection processData(InputStream in) throws Exception { + + Collection reports = new ArrayList(); + + String json=this.getJsonString(in); + // The return value for the method + Collection avlReportsReadIn = new ArrayList(); + JSONArray array = new JSONArray(json); + + for (int i=0; i. */ -package org.transitime.db; +package org.transitclock.db; import java.sql.SQLException; import java.util.List; @@ -49,7 +49,7 @@ private GenericCsvQuery(String agencyId) throws SQLException { } /* (non-Javadoc) - * @see org.transitime.db.GenericQuery#addColumn(java.lang.String, int) + * @see org.transitclock.db.GenericQuery#addColumn(java.lang.String, int) */ @Override protected void addColumn(String columnName, int type) { @@ -59,7 +59,7 @@ protected void addColumn(String columnName, int type) { } /* (non-Javadoc) - * @see org.transitime.db.GenericQuery#doneWithColumns() + * @see org.transitclock.db.GenericQuery#doneWithColumns() */ @Override protected void doneWithColumns() { @@ -67,7 +67,7 @@ protected void doneWithColumns() { } /* (non-Javadoc) - * @see org.transitime.db.GenericQuery#addRow(java.util.List) + * @see org.transitclock.db.GenericQuery#addRow(java.util.List) */ @Override protected void addRow(List values) { @@ -100,9 +100,9 @@ protected void addRow(List values) { * @return CVS string of results from query * @throws SQLException */ - public static String getCsvString(String agencyId, String sql) throws SQLException { + public static String getCsvString(String agencyId, String sql, Object... parameters) throws SQLException { GenericCsvQuery query = new GenericCsvQuery(agencyId); - query.doQuery(sql); + query.doQuery(sql,parameters); return query.sb.toString(); } diff --git a/transitime/src/main/java/org/transitime/db/GenericQuery.java b/transitclock/src/main/java/org/transitclock/db/GenericQuery.java old mode 100644 new mode 100755 similarity index 89% rename from transitime/src/main/java/org/transitime/db/GenericQuery.java rename to transitclock/src/main/java/org/transitclock/db/GenericQuery.java index a43eb2ef6..c707a8895 --- a/transitime/src/main/java/org/transitime/db/GenericQuery.java +++ b/transitclock/src/main/java/org/transitclock/db/GenericQuery.java @@ -15,23 +15,25 @@ * along with Transitime.org . If not, see . */ -package org.transitime.db; +package org.transitclock.db; import java.sql.Connection; import java.sql.DriverManager; +import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; +import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; import java.util.Properties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.db.webstructs.WebAgency; -import org.transitime.utils.IntervalTimer; -import org.transitime.utils.Time; +import org.transitclock.db.webstructs.WebAgency; +import org.transitclock.utils.IntervalTimer; +import org.transitclock.utils.Time; /** * For doing a query without using Hibernate. By using regular JDBC and avoiding @@ -136,13 +138,26 @@ public static Connection getConnection(String dbType, String dbHost, * (instead of null) * @throws SQLException */ - public void doQuery(String sql) throws SQLException { - Statement statement = null; + + protected void doQuery(String sql, Object...parameters) throws SQLException { + PreparedStatement statement = null; + IntervalTimer timer = new IntervalTimer(); - try { - statement = connection.createStatement(); - ResultSet rs = statement.executeQuery(sql); + try { + + statement = connection.prepareStatement(sql); + + // TODO Deal with dates for the moment + for (int i=0;i. */ -package org.transitime.db.hibernate; +package org.transitclock.db.hibernate; import org.hibernate.cfg.Configuration; -import org.transitime.db.structs.ActiveRevisions; -import org.transitime.db.structs.Arrival; -import org.transitime.db.structs.AvlReport; -import org.transitime.db.structs.Agency; -import org.transitime.db.structs.Block; -import org.transitime.db.structs.Calendar; -import org.transitime.db.structs.CalendarDate; -import org.transitime.db.structs.ConfigRevision; -import org.transitime.db.structs.DbTest; -import org.transitime.db.structs.MeasuredArrivalTime; -import org.transitime.db.structs.Prediction; -import org.transitime.db.structs.Departure; -import org.transitime.db.structs.FareAttribute; -import org.transitime.db.structs.Frequency; -import org.transitime.db.structs.Match; -import org.transitime.db.structs.MonitoringEvent; -import org.transitime.db.structs.PredictionAccuracy; -import org.transitime.db.structs.Route; -import org.transitime.db.structs.Stop; -import org.transitime.db.structs.Transfer; -import org.transitime.db.structs.FareRule; -import org.transitime.db.structs.StopPath; -import org.transitime.db.structs.TravelTimesForStopPath; -import org.transitime.db.structs.TravelTimesForTrip; -import org.transitime.db.structs.Trip; -import org.transitime.db.structs.TripPattern; -import org.transitime.db.structs.VehicleConfig; -import org.transitime.db.structs.VehicleEvent; -import org.transitime.db.structs.VehicleState; -import org.transitime.db.webstructs.ApiKey; -import org.transitime.db.webstructs.WebAgency; +import org.transitclock.db.structs.*; +import org.transitclock.db.webstructs.ApiKey; +import org.transitclock.db.webstructs.WebAgency; /** * @@ -83,26 +55,36 @@ public class AnnotatedClassesList { Departure.class, FareAttribute.class, FareRule.class, + FeedInfo.class, Frequency.class, + Location.class, Match.class, MeasuredArrivalTime.class, MonitoringEvent.class, PredictionAccuracy.class, Route.class, + RunTimesForRoutes.class, Stop.class, StopPath.class, Transfer.class, TravelTimesForStopPath.class, + PredictionForStopPath.class, TravelTimesForTrip.class, Trip.class, TripPattern.class, VehicleEvent.class, + PredictionEvent.class, VehicleConfig.class, VehicleState.class, - + HoldingTime.class, + Headway.class, // For website ApiKey.class, WebAgency.class, + // for traffic data + TrafficSensor.class, + TrafficPath.class, + TrafficSensorData.class }; /********************** Member Functions **************************/ diff --git a/transitclock/src/main/java/org/transitclock/db/hibernate/DataDbLogger.java b/transitclock/src/main/java/org/transitclock/db/hibernate/DataDbLogger.java new file mode 100644 index 000000000..09657794c --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/db/hibernate/DataDbLogger.java @@ -0,0 +1,414 @@ +/* + * 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.db.hibernate; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.configData.CoreConfig; +import org.transitclock.configData.DbSetupConfig; +import org.transitclock.db.structs.ArrivalDeparture; +import org.transitclock.db.structs.AvlReport; +import org.transitclock.db.structs.Headway; +import org.transitclock.db.structs.Match; +import org.transitclock.db.structs.MonitoringEvent; +import org.transitclock.db.structs.Prediction; +import org.transitclock.db.structs.PredictionAccuracy; +import org.transitclock.db.structs.PredictionEvent; +import org.transitclock.db.structs.RunTimesForRoutes; +import org.transitclock.db.structs.TrafficSensorData; +import org.transitclock.db.structs.VehicleConfig; +import org.transitclock.db.structs.VehicleEvent; +import org.transitclock.db.structs.VehicleState; + +/** + * DataDbLogger is for storing to the db a stream of data objects. It is intended + * for example for storing AVL reports, vehicle matches, arrivals, departures, etc. + * There can be quite a large volume of this type of data. + * + * The database might not always be available. It could be vacuumed, restarted, + * moved, etc. When this happens don't want the data to be lost and don't want + * to tie up the core predictor. Therefore a queue is used to log objects. + * This makes the application far more robust with respect to database issues. + * The application simply calls add(Object o) to add the object to be stored + * to the queue. + * + * A separate thread is used to read from the queue and write the data to the + * database. If the queue starts filling up then error messages are e-mailed + * to users alerting them that there is a problem. E-mail messages are also + * sent out when the queue level is going down again. + * + * A goal with this class was to make the writing to the database is + * efficient as possible. Therefore the objects are written in batches. + * This reduces network traffic as well as database load. But this did + * make handling exceptions more complicated. If there is an exception + * with a batch then each item is individually written so that don't + * lose any data. + * + * When in playback mode then don't want to store the data because it would + * interfere with data stored when the application was run in real time. + * Therefore when running in playback mode set shouldStoreToDb to true + * when calling getDataDbLogger(). + * + * @author SkiBu Smith + * + */ +public class DataDbLogger { + + + // This is a singleton class that only returns a single object per agencyId. + private static Map dataDbLoggerMap = + new HashMap(1); + + private DbQueue arrivalDepartureQueue; + private DbQueue avlReportQueue; + private DbQueue vehicleConfigQueue; + private DbQueue predictionQueue; + private DbQueue matchQueue; + private DbQueue predictionAccuracyQueue; + private DbQueue monitoringEventQueue; + private DbQueue vehicleEventQueue; + private DbQueue predictionEventQueue; + private DbQueue vehicleStateQueue; + private DbQueue trafficSensorDataQueue; + private DbQueue headwayQueue; + private DbQueue runTimesForRoutesQueue; + private DbQueue genericQueue; + + private static final int QUEUE_CAPACITY = 5000000; + + // The queue capacity levels when an error message should be e-mailed out. + // The max value should be 1.0. + private final double levels[] = { 0.5, 0.8, 1.00 }; + + // The queue that objects to be stored are placed in + private BlockingQueue queue = new LinkedBlockingQueue(QUEUE_CAPACITY); + + // When running in playback mode where getting AVLReports from database + // instead of from an AVL feed, then debugging and don't want to store + // derived data into the database because that would interfere with the + // derived data that was already stored in real time. For that situation + // shouldStoreToDb should be set to false. + private final boolean shouldStoreToDb; + + // Used by add(). If queue filling up to 25% and shouldPauseToReduceQueue is + // true then will pause the calling thread for a few seconds so that more + // objects can be written out and not have the queue fill up. + private final boolean shouldPauseToReduceQueue; + + // For keeping track of index into levels, which level of capacity of + // queue being used. When level changes then an e-mail is sent out warning + // the operators. + private double indexOfLevelWhenMessageLogged = 0; + + // For keeping track of maximum capacity of queue that was used. + // Used for logging when queue use is going down. + private double maxQueueLevel = 0.0; + + // So can access agencyId for logging messages + private String agencyId; + + // keep track of primary key values to reduce database duplicate exceptions + private Map vehicleToPrimayKeyMap = new HashMap<>(); + + private static final Logger logger = + LoggerFactory.getLogger(DataDbLogger.class); + + /********************** Member Functions **************************/ + + /** + * Factory method. Returns the singleton db logger for the specified + * agencyId. + * + * @param agencyId + * Id of database to be written to + * @param shouldStoreToDb + * Specifies whether data should actually be written to db. If in + * playback mode and shouldn't write data to db then set to + * false. + * @param shouldPauseToReduceQueue + * Specifies if should pause the thread calling add() if the + * queue is filling up. Useful for when in batch mode and dumping + * a whole bunch of data to the db really quickly. + * @return The DataDbLogger for the specified agencyId + */ + public static DataDbLogger getDataDbLogger(String agencyId, + boolean shouldStoreToDb, boolean shouldPauseToReduceQueue) { + synchronized (dataDbLoggerMap) { + DataDbLogger logger = dataDbLoggerMap.get(agencyId); + if (logger == null) { + logger = new DataDbLogger(agencyId, shouldStoreToDb, + shouldPauseToReduceQueue); + dataDbLoggerMap.put(agencyId, logger); + } + return logger; + } + } + + /** + * Constructor. Private so that factory method getDataDbLogger() has to be + * used. Starts up separate thread that actually reads from queue and stores + * the data. + * + * @param agencyId + * Id of database to be written to + * @param shouldStoreToDb + * Specifies whether data should actually be written to db. If in + * playback mode and shouldn't write data to db then set to + * false. + * @param shouldPauseToReduceQueue + * Specifies if should pause the thread calling add() if the + * queue is filling up. Useful for when in batch mode and dumping + * a whole bunch of data to the db really quickly. + */ + private DataDbLogger(String agencyId, boolean shouldStoreToDb, + boolean shouldPauseToReduceQueue) { + this.agencyId = agencyId; + this.shouldStoreToDb = shouldStoreToDb; + this.shouldPauseToReduceQueue = shouldPauseToReduceQueue; + arrivalDepartureQueue = new DbQueue(agencyId, shouldStoreToDb, shouldPauseToReduceQueue, ArrivalDeparture.class.getSimpleName()); + avlReportQueue = new DbQueue(agencyId, shouldStoreToDb, shouldPauseToReduceQueue, AvlReport.class.getSimpleName()); + vehicleConfigQueue = new DbQueue(agencyId, shouldStoreToDb, shouldPauseToReduceQueue, VehicleConfig.class.getSimpleName()); + predictionQueue = new DbQueue(agencyId, shouldStoreToDb, shouldPauseToReduceQueue, Prediction.class.getSimpleName()); + matchQueue = new DbQueue(agencyId, shouldStoreToDb, shouldPauseToReduceQueue, Match.class.getSimpleName()); + predictionAccuracyQueue = new DbQueue(agencyId, shouldStoreToDb, shouldPauseToReduceQueue, PredictionAccuracy.class.getSimpleName()); + monitoringEventQueue = new DbQueue(agencyId, shouldStoreToDb, shouldPauseToReduceQueue, MonitoringEvent.class.getSimpleName()); + vehicleEventQueue = new DbQueue(agencyId, shouldStoreToDb, shouldPauseToReduceQueue, VehicleEvent.class.getSimpleName()); + predictionEventQueue = new DbQueue(agencyId, shouldStoreToDb, shouldPauseToReduceQueue, PredictionEvent.class.getSimpleName()); + vehicleStateQueue = new DbQueue(agencyId, shouldStoreToDb, shouldPauseToReduceQueue, VehicleState.class.getSimpleName()); + trafficSensorDataQueue = new DbQueue(agencyId, shouldStoreToDb, shouldPauseToReduceQueue, TrafficSensorData.class.getSimpleName()); + headwayQueue = new DbQueue(agencyId, shouldStoreToDb, shouldPauseToReduceQueue, Headway.class.getSimpleName()); + runTimesForRoutesQueue = new DbQueue(agencyId, shouldStoreToDb, shouldPauseToReduceQueue, RunTimesForRoutes.class.getSimpleName()); + genericQueue = new DbQueue(agencyId, shouldStoreToDb, shouldPauseToReduceQueue, Object.class.getSimpleName()); + + } + + public boolean add(ArrivalDeparture ad) { + String key = "ad_" + ad.getVehicleId(); + String hash = vehicleToPrimayKeyMap.get(key); + if (hash != null && hash.equals(hashArrivalDeparture(ad))) { + // we already have this value, prevent sql exception + return false; + } + vehicleToPrimayKeyMap.put(key, hash); + return arrivalDepartureQueue.add(ad); + } + public boolean add(AvlReport ar) { + String key = "ar_" + ar.getVehicleId(); + String hash = vehicleToPrimayKeyMap.get(key); + if (hash != null && hash.equals(hashAvl(ar))) { + // we already have this value, prevent sql exception + return false; + } + vehicleToPrimayKeyMap.put(key, hash); + return avlReportQueue.add(ar); + } + public boolean add(VehicleConfig vc) { + return vehicleConfigQueue.add(vc); + } + public boolean add(Prediction p) { + return predictionQueue.add(p); + } + public boolean add(Match m) { + return matchQueue.add(m); + } + public boolean add(PredictionAccuracy pa) { + if (pa != null && pa.getArrivalDepartureTime() != null) { + return predictionAccuracyQueue.add(pa); + } + // reject null arrival/departure time + return false; + } + public boolean add(MonitoringEvent me) { + return monitoringEventQueue.add(me); + } + public boolean add(VehicleEvent ve) { + return vehicleEventQueue.add(ve); + } + public boolean add(PredictionEvent pe) { + if (CoreConfig.storePredictionEventsInDatabase()) { + return predictionEventQueue.add(pe); + } + return false; + } + public boolean add(VehicleState vs) { + String key = "vs_" + vs.getVehicleId(); + String hash = vehicleToPrimayKeyMap.get(key); + if (hash != null && hash.equals(hashVehicleState(vs))) { + // we already have this value, prevent sql exception + return false; + } + vehicleToPrimayKeyMap.put(key, hash); + return vehicleStateQueue.add(vs); + } + + public boolean add(TrafficSensorData data) { + return trafficSensorDataQueue.add(data); + } + + public boolean add(Headway data) { + return headwayQueue.add(data); + } + + public boolean add(RunTimesForRoutes data) { + return runTimesForRoutesQueue.add(data); + } + + + /** + * Determines set of class names in the queue. Useful for logging + * error message when queue getting filled up so know what kind of + * objects are backing the system up. + * + * @return Map of class names and their count of the objects in the queue + */ + private Map getClassNamesInQueue() { + Map classNamesMap = new HashMap(); + for (Object o : queue) { + String className = o.getClass().getName(); + Integer count = classNamesMap.get(className); + if (count == null) { + count = new Integer(0); + classNamesMap.put(className, count); + } + ++count; + } + return classNamesMap; + } + + /** + * Adds an object to be saved in the database to the queue. If queue is + * getting filled up then an e-mail will be sent out indicating there is a + * problem. The queue levels at which an e-mail is sent out is specified by + * levels. If queue has reached capacity then an error message is logged. + * + * @param o + * The object that should be logged to the database + * @return True if OK (object added to queue or logging disabled). False if + * queue was full. + */ + public boolean add(Object o) { + // this is now a catch-all -- most objects have an argument overriden method + return genericQueue.add(o); + } + + + // as a summary of overall queue behaviour, return the highest queue level + public double queueLevel() { + Double[] queueLevelsArray = { + arrivalDepartureQueue.queueLevel(), + avlReportQueue.queueLevel(), + vehicleConfigQueue.queueLevel(), + predictionQueue.queueLevel(), + matchQueue.queueLevel(), + predictionAccuracyQueue.queueLevel(), + monitoringEventQueue.queueLevel(), + vehicleEventQueue.queueLevel(), + predictionEventQueue.queueLevel(), + vehicleStateQueue.queueLevel(), + trafficSensorDataQueue.queueLevel(), + headwayQueue.queueLevel(), + runTimesForRoutesQueue.queueLevel(), + genericQueue.queueLevel() + }; + + List levels = Arrays.asList(queueLevelsArray); + Collections.sort(levels); + return levels.get(levels.size()-1); + } + + // as a summary of queue sizes, return the largest queue size + public int queueSize() { + Integer[] sizesArray = { + arrivalDepartureQueue.queueSize(), + avlReportQueue.queueSize(), + vehicleConfigQueue.queueSize(), + predictionQueue.queueSize(), + matchQueue.queueSize(), + predictionAccuracyQueue.queueSize(), + monitoringEventQueue.queueSize(), + vehicleEventQueue.queueSize(), + predictionEventQueue.queueSize(), + vehicleStateQueue.queueSize(), + trafficSensorDataQueue.queueSize(), + headwayQueue.queueSize(), + runTimesForRoutesQueue.queueSize(), + genericQueue.queueSize() + }; + + List sizes = Arrays.asList(sizesArray); + Collections.sort(sizes); + return sizes.get(sizes.size()-1); + } + + private String hashAvl(AvlReport ar) { + // primary keys minus vehicleId + DateFormat simple = + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + return simple.format(ar.getDate()); + } + + private String hashArrivalDeparture(ArrivalDeparture ad) { + // primary keys minus vehicleId + return + ad.getTripId() + "_" + + ad.getTime() + "_" + + ad.getStopId() + "_" + + ad.isArrival() + "_" + + ad.getGtfsStopSequence(); + } + + private String hashVehicleState(VehicleState vs) { + // primary keys minus vehicleId + DateFormat simple = + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + return simple.format(vs.getAvlTime()); + + } + + /** + * Just for doing some testing + * + * @param args + */ + public static void main(String[] args) { + DataDbLogger logger = getDataDbLogger("test", false, false); + + long initialTime = (System.currentTimeMillis() / 1000) * 1000; + + for (int i=0; i<25; ++i) + logger.add(new AvlReport("test", initialTime + i, 1.23, 4.56, null)); + + // This one should cause constraint problem with the second batch. + // Need to not retry for such an exception + logger.add(new AvlReport("test", initialTime, 1.23, 4.56, null)); + + for (int i=DbSetupConfig.getBatchSize(); i<2*DbSetupConfig.getBatchSize();++i) + logger.add(new AvlReport("test", initialTime+i, 1.23, 4.56, null)); + + } + +} diff --git a/transitclock/src/main/java/org/transitclock/db/hibernate/DbQueue.java b/transitclock/src/main/java/org/transitclock/db/hibernate/DbQueue.java new file mode 100644 index 000000000..7dbcc8d56 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/db/hibernate/DbQueue.java @@ -0,0 +1,533 @@ +package org.transitclock.db.hibernate; + +import java.net.SocketException; +import java.net.SocketTimeoutException; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; + +import org.hibernate.HibernateException; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.Transaction; +import org.hibernate.exception.GenericJDBCException; +import org.hibernate.exception.JDBCConnectionException; +import org.hibernate.exception.SQLGrammarException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.configData.DbSetupConfig; +import org.transitclock.logging.Markers; +import org.transitclock.monitoring.MonitoringService; +import org.transitclock.utils.IntervalTimer; +import org.transitclock.utils.Time; +import org.transitclock.utils.threading.NamedThreadFactory; + +/** + * Encapsulate the queuing operations of the database. Make generic so + * db-side batching is more effective. + */ +public class DbQueue { + + private static final Logger logger = + LoggerFactory.getLogger(DbQueue.class); + + // For when cannot connect to data the length of time in msec between retries + private static final long TIME_BETWEEN_RETRIES = 1 * 1000; //msec + + private static final int QUEUE_CAPACITY = 500000; + + // The queue that objects to be stored are placed in + private BlockingQueue queue = new LinkedBlockingQueue(QUEUE_CAPACITY); + + + // When running in playback mode where getting AVLReports from database + // instead of from an AVL feed, then debugging and don't want to store + // derived data into the database because that would interfere with the + // derived data that was already stored in real time. For that situation + // shouldStoreToDb should be set to false. + private final boolean shouldStoreToDb; + + // Used by add(). If queue filling up to 25% and shouldPauseToReduceQueue is + // true then will pause the calling thread for a few seconds so that more + // objects can be written out and not have the queue fill up. + private final boolean shouldPauseToReduceQueue; + + // The queue capacity levels when an error message should be e-mailed out. + // The max value should be 1.0. + private final double levels[] = { 0.5, 0.8, 1.00 }; + + // For keeping track of index into levels, which level of capacity of + // queue being used. When level changes then an e-mail is sent out warning + // the operators. + private double indexOfLevelWhenMessageLogged = 0; + + // For keeping track of maximum capacity of queue that was used. + // Used for logging when queue use is going down. + private double maxQueueLevel = 0.0; + + + // So can access projectId for logging messages + private String projectId; + + // The Session for writing data to db + private SessionFactory sessionFactory; + + // collect some statistics on how the db is performing + private long throughputCount = 0; + private long throughputTimestamp = System.currentTimeMillis(); + private String shortType; + + public DbQueue(String projectId, boolean shouldStoreToDb, + boolean shouldPauseToReduceQueue, String shortType) { + this.projectId = projectId; + this.shouldStoreToDb = shouldStoreToDb; + this.shouldPauseToReduceQueue = shouldPauseToReduceQueue; + this.shortType = shortType; + + + // Create the reusable heavy weight session factory + sessionFactory = HibernateUtils.getSessionFactory(projectId); + + // Start up separate thread that reads from the queue and + // actually stores the data + NamedThreadFactory threadFactory = new NamedThreadFactory(getClass().getSimpleName()); + ExecutorService executor = Executors.newSingleThreadExecutor(threadFactory); + executor.execute(new Runnable() { + public void run() { + processData(); + } + }); + ThroughputMonitor tm = new ThroughputMonitor(); + new Thread(tm).start(); + + } + + public boolean add(T t) { + // If in playback mode then don't want to store the + // derived data because it would interfere with the + // derived data already stored when was running in real time. + if (!shouldStoreToDb) + return true; + + // Add the object to the queue + boolean success = queue.offer(t); + + double level = queueLevel(); + int levelIndex = indexOfLevel(level); + // If reached a new level then output message e-mail to warn users + if (levelIndex > indexOfLevelWhenMessageLogged) { + indexOfLevelWhenMessageLogged = levelIndex; + String message = success ? + "DataDbLogger queue filling up " + + " for projectId=" + projectId +" and type " + shortType + ". It is now at " + + String.format("%.1f", level*100) + "% capacity with " + + queue.size() + " elements already in the queue." + : + "DataDbLogger queue is now completely full for projectId=" + + projectId + ". LOSING DATA!!!"; + logger.error(Markers.email(), message); + } + + // If losing data then log such + if (!success) { + logger.error("DataDbLogger queue is now completely full for " + + "projectId=" + projectId + "and type " + shortType + ". LOSING DATA!!! Failed to " + + "store object=[" + t + "]"); + } + + // Keep track of max queue level so can log it when queue level + // is decreasing again. + if (level > maxQueueLevel) + maxQueueLevel = level; + + // If shouldPauseToReduceQueue (because in batch mode or such) and + // if queue is starting to get more full then pause the calling + // thread for 10 seconds so that separate thread can clear out + // queue a bit. + if (shouldPauseToReduceQueue && level > 0.2) { + logger.info("Pausing thread adding data to DataDbLogger queue " + + "so that queue can be cleared out. Level={}%, type=", + level*100.0, shortType); + Time.sleep(10 * Time.MS_PER_SEC); + } + + // Return whether was successful in adding object to queue + return success; + + } + + private List drain() { + // Get the next object from the head of the queue + ArrayList buff = new ArrayList(DbSetupConfig.getBatchSize()); + int count = 0; + do { + buff.clear(); + count = queue.drainTo(buff, DbSetupConfig.getBatchSize()); + throughputCount += count; + if (count == 0) + try { + Thread.sleep(TIME_BETWEEN_RETRIES); + } catch (InterruptedException e) { + } + } while (buff.isEmpty()); + logger.debug("drained {} elements", count); + // Log if went below a capacity level + // See if queue dropped to 10% less than the previously logged level. + // Use a margin of 10% so that don't get flood of messages if queue + // oscillating around a level. + double level = queueLevel(); + int levelIndexIncludingMargin = indexOfLevel(level + 0.10); + if (levelIndexIncludingMargin < indexOfLevelWhenMessageLogged) { + logger.error(Markers.email(), "DataDbLogger queue emptying out somewhat " + + " for projectId=" + projectId + " and type " + shortType + ". It is now at " + + String.format("%.1f", level*100) + "% capacity with " + queue.size() + + " elements already in the queue. The maximum capacity was " + + String.format("%.1f", maxQueueLevel*100) + "%."); + indexOfLevelWhenMessageLogged = levelIndexIncludingMargin; + + // Reset the maxQueueLevel so can determine what next peak is + maxQueueLevel = level; + } + + // Return the result + return buff; + } + + /** + * Process a batch of data, as specified by BATCH_SIZE member. The goal + * is to batch a few db writes together to reduce load on network and on + * db machines. There this method will try to store multiple objects + * from the queue at once, up to the BATCH_SIZE. + * + * If there is an exception with an object being written then the + * batch of objects will be written individually so that all of the + * good data will still be stored. + * + * When looked at Hibernate documentation on batch writing there is + * mention of using: + * if (++batchingCounter % BATCH_SIZE == 0) { + * session.flush(); + * session.clear(); + * } + * But the above doesn't commit the data to the db until the transaction + * commit is done. Therefore the need here isn't true Hibernate batch + * processing. Instead, need to use a transaction for each batch. + */ + public void processBatchOfData() { + // Create an array for holding what is being written to db. If there + // is an exception with one of the objects, such as a constraint violation, + // then can try to write the objects one at a time to make sure that the + // the good ones are written. This way don't lose any good data even if + // an exception occurs while batching data. + List objectsForThisBatch = new ArrayList(DbSetupConfig.getBatchSize()); + + Transaction tx = null; + Session session = null; + + try { + session = sessionFactory.openSession(); + tx = session.beginTransaction(); + + // Get the objects to be stored from the queue + List objectsToBeStored = drain(); + + objectsForThisBatch.addAll(objectsToBeStored); + for (Object objectToBeStored : objectsForThisBatch) { + // Write the data to the session. This doesn't yet + // actually write the data to the db though. That is only + // done when the session is flushed or committed. + logger.debug("DataDbLogger batch saving object={}", + objectToBeStored); + session.save(objectToBeStored); + } + + // Sometimes useful for debugging via the console + //System.err.println(new Date() + " Committing " + // + objectsForThisBatch.size() + " objects. " + queueSize() + // + " objects still in queue."); + logger.debug("Committing {} objects. {} objects still in queue.", + objectsForThisBatch.size(), queueSize()); + IntervalTimer timer = new IntervalTimer(); + + // Actually do the commit + tx.commit(); + + // Sometimes useful for debugging via the console + //System.err.println(new Date() + " Done committing. Took " + // + timer.elapsedMsec() + " msec"); + logger.debug("Done committing. Took {} msec", timer.elapsedMsec()); + + session.close(); + } catch (HibernateException e) { + e.printStackTrace(); + + // If there was a connection problem then create a whole session + // factory so that get new connections. + Throwable rootCause = HibernateUtils.getRootCause(e); + + if (rootCause instanceof SocketTimeoutException || rootCause instanceof SocketException + || (rootCause instanceof SQLException + && rootCause.getMessage().contains("statement closed"))) { + logger.error(Markers.email(), + "Had a connection problem to the database. Likely " + + "means that the db was rebooted or that the " + + "connection to it was lost. Therefore creating a new " + + "SessionFactory so get new connections."); + HibernateUtils.clearSessionFactory(); + sessionFactory = HibernateUtils.getSessionFactory(projectId); + } else { + // Rollback the transaction since it likely was not committed. + // Otherwise can get an error when using Postgres "ERROR: + // current transaction is aborted, commands ignored until end of + // transaction block". + try { + if (tx != null) + tx.rollback(); + } catch (HibernateException e2) { + logger.error( + "Error rolling back transaction after processing " + + "batch of data via DataDbLogger.", e2); + } + + // Close session here so that can process the objects + // individually + // using a new session. + try { + if (session != null) + session.close(); + } catch (HibernateException e2) { + logger.error("Error closing session after processing " + + "batch of data via DataDbLogger.", e2); + } + + // If it is a SQLGrammarException then also log the SQL to + // help in debugging. + String additionaInfo = e instanceof SQLGrammarException ? + " SQL=\"" + ((SQLGrammarException) e).getSQL() + "\"" + : ""; + Throwable cause = HibernateUtils.getRootCause(e); + logger.error("{} for database for project={} when batch writing " + + "objects: {}. Will try to write each object " + + "from batch individually. {}", + e.getClass().getSimpleName(), projectId, + cause.getMessage(), additionaInfo); + } + + // Write each object individually so that the valid ones will be + // successfully written. + for (Object o : objectsForThisBatch) { + boolean shouldKeepTrying = false; + do { + try { + processSingleObject(o); + shouldKeepTrying = false; + } catch (HibernateException e2) { + // Need to know if it is a problem with the database not + // being accessible or if there is a problem with the SQL/data. + // If there is a problem accessibility of the database then + // want to keep trying writing the old data. But if it is + // a problem with the SQL/data then only want to try to write + // the good data from the batch a single time to make sure + // all good data is written. + if (shouldKeepTryingBecauseConnectionException(e2)) { + shouldKeepTrying = true; + logger.error("Encountered database connection " + + "exception so will sleep for {} msec and " + + "will then try again.", TIME_BETWEEN_RETRIES); + Time.sleep(TIME_BETWEEN_RETRIES); + } + + // Output message on what is going on + Throwable cause2 = HibernateUtils.getRootCause(e2); + logger.error(e2.getClass().getSimpleName() + " when individually writing object " + + o + ". " + + (shouldKeepTrying?"Will keep trying. " : "") + + "msg=" + cause2.getMessage()); + } + } while (shouldKeepTrying); + } + } + } + + + /** + * This is the main method for processing data. It simply keeps on calling + * processBatchOfData() so that data is batched as efficiently as possible. + * Exceptions are caught such that this method will continue to run + * indefinitely. + */ + public void processData() { + while (true) { + try { + logger.debug("DataDbLogger.processData() processing batch of " + + "data to be stored in database."); + processBatchOfData(); + } catch (Exception e) { + logger.error("Error writing data to database via DataDbLogger. " + + "Look for ERROR in log file to see if the database classes " + + "were configured correctly. Error: " + + e); + + if (queueSize() == 0) { + // avoid a tight loop if nothing to do + Time.sleep(TIME_BETWEEN_RETRIES); + } + } + } + } + + + + + /** + * Returns how much capacity of the queue is being used up. + * + * @return a value between 0.0 and 1.0 indicating how much of queue being used + */ + public double queueLevel() { + int remainingCapacity = queue.remainingCapacity(); + int totalCapacity = queue.size() + remainingCapacity; + double level = 1.0 - (double) remainingCapacity / totalCapacity; + return level; + } + + /** + * Returns how many items are in queue to be processed + * @return items in queue + */ + public int queueSize() { + return queue.size(); + } + + /** + * Returns the index into levels that the queue capacity is at. + * For determining if should send e-mail warning message. + * + * @param queueLevel + * @return + */ + private int indexOfLevel(double queueLevel) { + for (int i=0; i. */ -package org.transitime.db.hibernate; +package org.transitclock.db.hibernate; import java.io.ByteArrayOutputStream; import java.io.File; @@ -31,7 +31,8 @@ import org.hibernate.service.ServiceRegistry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.configData.DbSetupConfig; +import org.transitclock.configData.DbSetupConfig; +import org.hibernate.service.ServiceRegistryBuilder; /** * Utilities for dealing with Hibernate issues such as sessions. @@ -43,7 +44,7 @@ public class HibernateUtils { // Should be set to what is used in hibernate.cfg.xml where the // batch_size is set, e.g. 25 - public static final int BATCH_SIZE = 25; + public static final int BATCH_SIZE = 100; // When Using @Column for route, stop, etc IDs don't need the default of // 255 characters. Therefore can use shorter fields. @@ -67,27 +68,68 @@ public class HibernateUtils { */ private static SessionFactory createSessionFactory(String dbName) throws HibernateException { + return createSessionFactory(dbName, false); + } + private static SessionFactory createSessionFactory(String dbName, boolean readOnly) + throws HibernateException { logger.debug("Creating new Hibernate SessionFactory for dbName={}", dbName); // Create a Hibernate configuration based on customized config file - Configuration config = new Configuration(); + Configuration config = getConfiguration(dbName); + // Add the annotated classes so that they can be used + AnnotatedClassesList.addAnnotatedClasses(config); + + String dbUrl = getDbUrl(config, dbName, readOnly); + if(dbUrl != null) { + config.setProperty("hibernate.connection.url", dbUrl); + } + + String dbUserName = getDbUser(config); + if(dbUserName != null) { + config.setProperty("hibernate.connection.username", dbUserName); + } + + String dbPassword = getDbPassword(); + if (dbPassword != null) { + config.setProperty("hibernate.connection.password", dbPassword); + } + + // Log info, but don't log password. This can just be debug logging + // even though it is important because the C3P0 connector logs the info. + logger.info("For Hibernate factory project dbName={} " + + "using url={} username={}, and configured password", + dbName, dbUrl, dbUserName); + + // Get the session factory for persistence + Properties properties = config.getProperties(); + ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(properties).build(); + SessionFactory sessionFactory = + config.buildSessionFactory(serviceRegistry); + + // Return the factory + return sessionFactory; + } + + public static Configuration getConfiguration(String dbName){ + // Create a Hibernate configuration based on customized config file + Configuration config = new Configuration(); + // Want to be able to specify a configuration file for now // since developing in Eclipse and want all config files - // to be in same place. But the Config.configure(String) + // to be in same place. But the Config.configure(String) // method can't seem to work with a Windows directory name such - // as C:/users/Mike/software/hibernate.cfg.xml . Therefore create + // as C:/hibernate.cfg.xml . Therefore create // a File object for that file name and pass in the File object // to configure(). String fileName = DbSetupConfig.getHibernateConfigFileName(); - logger.info("Configuring Hibernate for dbName={} using config file={}", - dbName, fileName); + logger.info("Configuring Hibernate for dbName={} using config file={}", dbName, fileName); + File f = new File(fileName); if (!f.exists()) { - logger.info("The Hibernate file {} doesn't exist as a regular file " - + "so seeing if it is in classpath.", fileName); - + logger.info("The Hibernate file {} doesn't exist as a regular file so seeing if it is in classpath.", fileName); + // Couldn't find file directly so look in classpath for it ClassLoader classLoader = HibernateUtils.class.getClassLoader(); URL url = classLoader.getResource(fileName); @@ -99,60 +141,69 @@ private static SessionFactory createSessionFactory(String dbName) else { logger.error("Could not load in hibernate config file {}", fileName); } - - // Add the annotated classes so that they can be used - AnnotatedClassesList.addAnnotatedClasses(config); - // Set the db info for the URL, user name, and password. Uses the - // property hibernate.connection.url if it is set so that everything - // can be overwritten in a standard way. If that property not set then - // uses values from DbSetupConfig if set. If they are not set then the - // values will be obtained from the hibernate.cfg.xml config file. + return config; + } + + public static String getDbName(){ + return DbSetupConfig.getDbName(); + } + + /** + * Get the db info for the URL, user name, and password. Uses the + * property hibernate.connection.url if it is set so that everything + * can be overwritten in a standard way. If that property not set then + * uses values from DbSetupConfig if set. If they are not set then the + * values will be obtained from the hibernate.cfg.xml config file. + * + * @param config + * @param dbName + * @param readOnly + * @return + */ + public static String getDbUrl(Configuration config, String dbName, boolean readOnly){ + String dbUrl = config.getProperty("hibernate.connection.url"); + if (readOnly && config.getProperty("hibernate.ro.connection.url") != null) { + dbUrl = config.getProperty("hibernate.ro.connection.url"); + // override the configured url so its picked up by the driver + config.setProperty("hibernate.connection.url", dbUrl); + logger.info("using read only connection url {}", dbUrl); + } if (dbUrl == null || dbUrl.isEmpty()) { dbUrl = "jdbc:" + DbSetupConfig.getDbType() + "://" + DbSetupConfig.getDbHost() + "/" + dbName; - + // If socket timeout specified then add that to the URL Integer timeout = DbSetupConfig.getSocketTimeoutSec(); if (timeout != null && timeout != 0) { // If mysql then timeout specified in msec instead of secs if (DbSetupConfig.getDbType().equals("mysql")) timeout *= 1000; - + dbUrl += "?connectTimeout=" + timeout + "&socketTimeout=" + timeout; } - config.setProperty("hibernate.connection.url", dbUrl); } - + return dbUrl; + } + + public static String getDbUser(Configuration config){ String dbUserName = DbSetupConfig.getDbUserName(); - if (dbUserName != null) { - config.setProperty("hibernate.connection.username", dbUserName); - } else { + if (dbUserName == null) { dbUserName = config.getProperty("hibernate.connection.username"); } - - if (DbSetupConfig.getDbPassword() != null) - config.setProperty("hibernate.connection.password", - DbSetupConfig.getDbPassword()); - - // Log info, but don't log password. This can just be debug logging - // even though it is important because the C3P0 connector logs the info. - logger.info("For Hibernate factory project dbName={} " + - "using url={} username={}, and configured password", - dbName, dbUrl, dbUserName); - - // Get the session factory for persistence - Properties properties = config.getProperties(); - ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder() - .applySettings(properties).build(); - SessionFactory sessionFactory = - config.buildSessionFactory(serviceRegistry); + return dbUserName; + } - // Return the factory - return sessionFactory; + public static String getDbPassword(){ + return DbSetupConfig.getDbPassword(); + } + + public static String getDbType(){ + return DbSetupConfig.getDbType(); } + /** * Returns a cached Hibernate SessionFactory. Returns null if there is a @@ -160,26 +211,35 @@ private static SessionFactory createSessionFactory(String dbName) * * @param agencyId * Used as the database name if the property - * transitime.db.dbName is not set + * transitclock.db.dbName is not set * @return */ public static SessionFactory getSessionFactory(String agencyId) throws HibernateException{ + return getSessionFactory(agencyId, false); + } + + public static SessionFactory getSessionFactory(String agencyId, boolean readOnly) + throws HibernateException{ // Determine the database name to use. Will usually use the // projectId since each project has a database. But this might - // be overridden by the transitime.core.dbName property. + // be overridden by the transitclock.core.dbName property. String dbName = DbSetupConfig.getDbName(); if (dbName == null) dbName = agencyId; + if (readOnly) { + dbName = dbName + "-ro"; + } SessionFactory factory; synchronized(sessionFactoryCache) { factory = sessionFactoryCache.get(dbName); // If factory not yet created for this projectId then create it - if (factory == null) { + if (factory == null || factory.isClosed()) { try { - factory = createSessionFactory(dbName); + logger.info("creating new session factory with readOnly={}", readOnly); + factory = createSessionFactory(dbName, readOnly); sessionFactoryCache.put(dbName, factory); } catch (Exception e) { logger.error("Could not create SessionFactory for " @@ -217,21 +277,25 @@ public static void clearSessionFactory() { * * @param agencyId * Used as the database name if the property - * transitime.core.dbName is not set + * transitclock.core.dbName is not set * @return The Session. Make sure you close it when done because system only * gets limited number of open sessions. * @throws HibernateException */ public static Session getSession(String agencyId) throws HibernateException { + return getSession(agencyId, false); + } + + public static Session getSession(String agencyId, boolean readOnly) throws HibernateException { SessionFactory sessionFactory = - HibernateUtils.getSessionFactory(agencyId); + HibernateUtils.getSessionFactory(agencyId, readOnly); Session session = sessionFactory.openSession(); return session; } /** * Returns the session for the database name specified by the - * transitime.db.dbName Java property. + * transitclock.db.dbName Java property. *

* NOTE: Make sure you close the session after the query!! Use a try/catch * around the query and close the session in a finally block to make sure it @@ -241,8 +305,12 @@ public static Session getSession(String agencyId) throws HibernateException { * gets limited number of open sessions. */ public static Session getSession() { + return getSession(false); + } + + public static Session getSession(boolean readOnly) { SessionFactory sessionFactory = - HibernateUtils.getSessionFactory(DbSetupConfig.getDbName()); + HibernateUtils.getSessionFactory(DbSetupConfig.getDbName(), readOnly); Session session = sessionFactory.openSession(); return session; } diff --git a/transitime/src/main/java/org/transitime/db/hibernate/PrecisionTimestampType.java b/transitclock/src/main/java/org/transitclock/db/hibernate/PrecisionTimestampType.java similarity index 98% rename from transitime/src/main/java/org/transitime/db/hibernate/PrecisionTimestampType.java rename to transitclock/src/main/java/org/transitclock/db/hibernate/PrecisionTimestampType.java index 3abd5dbf2..837300416 100644 --- a/transitime/src/main/java/org/transitime/db/hibernate/PrecisionTimestampType.java +++ b/transitclock/src/main/java/org/transitclock/db/hibernate/PrecisionTimestampType.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.db.hibernate; +package org.transitclock.db.hibernate; import java.io.Serializable; import java.sql.PreparedStatement; @@ -39,7 +39,7 @@ * the SQL parameters. * * To enable use - * @Type (type="org.transitime.db.hibernate.PrecisionTimestampType") + * @Type (type="org.transitclock.db.hibernate.PrecisionTimestampType") * instead of @Temporal(TemporalType.TIMESTAMP) when declaring a Java * object that is to be persisted. * diff --git a/transitime/src/main/java/org/transitime/db/hibernate/package-info.java b/transitclock/src/main/java/org/transitclock/db/hibernate/package-info.java similarity index 99% rename from transitime/src/main/java/org/transitime/db/hibernate/package-info.java rename to transitclock/src/main/java/org/transitclock/db/hibernate/package-info.java index bce7bff51..50457190b 100644 --- a/transitime/src/main/java/org/transitime/db/hibernate/package-info.java +++ b/transitclock/src/main/java/org/transitclock/db/hibernate/package-info.java @@ -89,4 +89,4 @@ * @author SkiBu Smith * */ -package org.transitime.db.hibernate; \ No newline at end of file +package org.transitclock.db.hibernate; \ No newline at end of file diff --git a/transitime/src/main/java/org/transitime/db/package-info.java b/transitclock/src/main/java/org/transitclock/db/package-info.java similarity index 96% rename from transitime/src/main/java/org/transitime/db/package-info.java rename to transitclock/src/main/java/org/transitclock/db/package-info.java index 59e681577..75dc18f1c 100644 --- a/transitime/src/main/java/org/transitime/db/package-info.java +++ b/transitclock/src/main/java/org/transitclock/db/package-info.java @@ -23,4 +23,4 @@ * @author SkiBu Smith * */ -package org.transitime.db; \ No newline at end of file +package org.transitclock.db; \ No newline at end of file diff --git a/transitclock/src/main/java/org/transitclock/db/query/ArrivalDepartureQuery.java b/transitclock/src/main/java/org/transitclock/db/query/ArrivalDepartureQuery.java new file mode 100644 index 000000000..aa342b266 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/db/query/ArrivalDepartureQuery.java @@ -0,0 +1,241 @@ +package org.transitclock.db.query; + +import org.transitclock.core.ServiceType; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.List; +import java.util.Set; + +public class ArrivalDepartureQuery { + private LocalDate beginDate; + private LocalDate endDate; + private LocalTime beginTime; + private LocalTime endTime; + private String routeShortName; + private String headsign; + private String startStop; + private String endStop; + private String tripPatternId; + private Set tripIds; + private ServiceType serviceType; + private boolean timePointsOnly; + private boolean scheduledTimesOnly; + private boolean dwellTimeOnly; + private boolean includeTrip; + private boolean includeStop; + private boolean includeStopPath; + private boolean readOnly; + + private ArrivalDepartureQuery(Builder builder){ + this.beginDate = builder.beginDate; + this.endDate = builder.endDate; + this.beginTime = builder.beginTime; + this.endTime = builder.endTime; + this.routeShortName = builder.routeShortName; + this.headsign = builder.headsign; + this.startStop = builder.startStop; + this.endStop = builder.endStop; + this.tripPatternId = builder.tripPatternId; + this.tripIds = builder.tripIds; + this.serviceType = builder.serviceType; + this.timePointsOnly = builder.timePointsOnly; + this.scheduledTimesOnly = builder.scheduledTimesOnly; + this.dwellTimeOnly = builder.dwellTimeOnly; + this.includeTrip = builder.includeTrip; + this.includeStop = builder.includeStop; + this.includeStopPath = builder.includeStopPath; + this.readOnly = builder.readOnly; + } + + public LocalDate getBeginDate() { + return beginDate; + } + + public LocalDate getEndDate() { + return endDate; + } + + public LocalTime getBeginTime() { + return beginTime; + } + + public LocalTime getEndTime() { + return endTime; + } + + public String getRouteShortName() { + return routeShortName; + } + + public String getHeadsign() { + return headsign; + } + + public String getStartStop() { + return startStop; + } + + public String getEndStop() { + return endStop; + } + + public String getTripPatternId() { + return tripPatternId; + } + + public Set getTripIds() { + return tripIds; + } + + public ServiceType getServiceType() { + return serviceType; + } + + public boolean isTimePointsOnly() { + return timePointsOnly; + } + + public boolean isScheduledTimesOnly() { + return scheduledTimesOnly; + } + + public boolean isDwellTimeOnly() { + return dwellTimeOnly; + } + + public boolean isIncludeTrip() { + return includeTrip; + } + + public boolean isIncludeStop() { + return includeStop; + } + + public boolean isIncludeStopPath() { + return includeStopPath; + } + + public boolean isReadOnly() { + return readOnly; + } + + public static class Builder{ + + private LocalDate beginDate; + private LocalDate endDate; + private LocalTime beginTime; + private LocalTime endTime; + private String routeShortName; + private String headsign; + private String startStop; + private String endStop; + private String tripPatternId; + private Set tripIds; + private ServiceType serviceType; + private boolean timePointsOnly = false; + private boolean scheduledTimesOnly = false; + private boolean dwellTimeOnly = false; + private boolean includeTrip = false; + private boolean includeStop = false; + private boolean includeStopPath = false; + private boolean readOnly = false; + + public Builder(){} + + public Builder beginDate(LocalDate beginDate) { + this.beginDate = beginDate; + return this; + } + + public Builder endDate(LocalDate endDate) { + this.endDate = endDate; + return this; + } + + public Builder beginTime(LocalTime beginTime) { + this.beginTime = beginTime; + return this; + } + + public Builder endTime(LocalTime endTime) { + this.endTime = endTime; + return this; + } + + public Builder routeShortName(String routeShortName) { + this.routeShortName = routeShortName; + return this; + } + + public Builder headsign(String headsign) { + this.headsign = headsign; + return this; + } + + public Builder startStop(String startStop) { + this.startStop = startStop; + return this; + } + + public Builder endStop(String endStop) { + this.endStop = endStop; + return this; + } + + public Builder tripPatternId(String tripPatternId){ + this.tripPatternId = tripPatternId; + return this; + } + + public Builder tripIds(Set tripIds){ + this.tripIds = tripIds; + return this; + } + + public Builder serviceType(ServiceType serviceType) { + this.serviceType = serviceType; + return this; + } + + public Builder timePointsOnly(boolean timePointsOnly) { + this.timePointsOnly = timePointsOnly; + return this; + } + + public Builder scheduledTimesOnly(boolean scheduledTimesOnly) { + this.scheduledTimesOnly = scheduledTimesOnly; + return this; + } + + public Builder dwellTimeOnly(boolean dwellTimeOnly) { + this.dwellTimeOnly = dwellTimeOnly; + return this; + } + + public Builder includeTrip(boolean includeTrip) { + this.includeTrip = includeTrip; + return this; + } + + public Builder includeStop(boolean includeStop) { + this.includeStop = includeStop; + return this; + } + + public Builder includeStopPath(boolean includeStopPath) { + this.includeStopPath = includeStopPath; + return this; + } + + public Builder readOnly(boolean readOnly) { + this.readOnly = readOnly; + return this; + } + + public ArrivalDepartureQuery build(){ + ArrivalDepartureQuery query = new ArrivalDepartureQuery(this); + return query; + } + } +} diff --git a/transitclock/src/main/java/org/transitclock/db/query/RunTimeForRouteQuery.java b/transitclock/src/main/java/org/transitclock/db/query/RunTimeForRouteQuery.java new file mode 100644 index 000000000..682a2d8ea --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/db/query/RunTimeForRouteQuery.java @@ -0,0 +1,132 @@ +package org.transitclock.db.query; + +import org.transitclock.core.ServiceType; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.Set; + +public class RunTimeForRouteQuery { + private LocalDate beginDate; + private LocalDate endDate; + private Integer beginTime; + private Integer endTime; + private String routeShortName; + private ServiceType serviceType; + private boolean timePointsOnly; + private boolean scheduledTimesOnly; + private boolean readOnly; + + private RunTimeForRouteQuery(Builder builder){ + this.beginDate = builder.beginDate; + this.endDate = builder.endDate; + this.beginTime = builder.beginTime; + this.endTime = builder.endTime; + this.routeShortName = builder.routeShortName; + this.serviceType = builder.serviceType; + this.timePointsOnly = builder.timePointsOnly; + this.scheduledTimesOnly = builder.scheduledTimesOnly; + this.readOnly = builder.readOnly; + } + + public LocalDate getBeginDate() { + return beginDate; + } + + public LocalDate getEndDate() { + return endDate; + } + + public Integer getBeginTime() { + return beginTime; + } + + public Integer getEndTime() { + return endTime; + } + + public String getRouteShortName() { + return routeShortName; + } + + public ServiceType getServiceType() { + return serviceType; + } + + public boolean isTimePointsOnly() { + return timePointsOnly; + } + + public boolean isScheduledTimesOnly() { + return scheduledTimesOnly; + } + + public boolean isReadOnly() { + return readOnly; + } + + public static class Builder{ + + private LocalDate beginDate; + private LocalDate endDate; + private Integer beginTime; + private Integer endTime; + private String routeShortName; + private ServiceType serviceType; + private boolean timePointsOnly = false; + private boolean scheduledTimesOnly = false; + private boolean readOnly = false; + + public Builder(){} + + public Builder beginDate(LocalDate beginDate) { + this.beginDate = beginDate; + return this; + } + + public Builder endDate(LocalDate endDate) { + this.endDate = endDate; + return this; + } + + public Builder beginTime(Integer beginTime) { + this.beginTime = beginTime; + return this; + } + + public Builder endTime(Integer endTime) { + this.endTime = endTime; + return this; + } + + public Builder routeShortName(String routeShortName) { + this.routeShortName = routeShortName; + return this; + } + + public Builder serviceType(ServiceType serviceType) { + this.serviceType = serviceType; + return this; + } + + public Builder timePointsOnly(boolean timePointsOnly) { + this.timePointsOnly = timePointsOnly; + return this; + } + + public Builder scheduledTimesOnly(boolean scheduledTimesOnly) { + this.scheduledTimesOnly = scheduledTimesOnly; + return this; + } + + public Builder readOnly(boolean readOnly) { + this.readOnly = readOnly; + return this; + } + + public RunTimeForRouteQuery build(){ + RunTimeForRouteQuery query = new RunTimeForRouteQuery(this); + return query; + } + } +} diff --git a/transitclock/src/main/java/org/transitclock/db/query/TripQuery.java b/transitclock/src/main/java/org/transitclock/db/query/TripQuery.java new file mode 100644 index 000000000..fec00dd4c --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/db/query/TripQuery.java @@ -0,0 +1,104 @@ +package org.transitclock.db.query; + +import java.time.LocalTime; +import java.util.Set; + +public class TripQuery { + + private final String routeShortName; + private final String headsign; + private final String direction; + private final Integer firstStartTime; + private final Integer lastStartTime; + private final Set configRevs; + private final boolean readOnly; + + private TripQuery(Builder builder){ + this.routeShortName = builder.routeShortName; + this.headsign = builder.headsign; + this.direction = builder.direction; + this.configRevs = builder.configRevs; + this.firstStartTime = builder.firstStartTime; + this.lastStartTime = builder.lastStartTime; + this.readOnly = builder.readOnly; + } + + public String getRouteShortName() { + return routeShortName; + } + + public String getHeadsign() { + return headsign; + } + + public String getDirection() { + return direction; + } + + public Set getConfigRevs() { + return configRevs; + } + + public Integer getFirstStartTime() { + return firstStartTime; + } + + public Integer getLastStartTime() { + return lastStartTime; + } + + public boolean isReadOnly() { + return readOnly; + } + + + public static class Builder{ + + private String routeShortName; + private String headsign; + private String direction; + private Integer firstStartTime; + private Integer lastStartTime; + private Set configRevs; + private boolean readOnly = false; + + public Builder(String routeShortName, Set configRevs){ + this.routeShortName = routeShortName; + this.configRevs = configRevs; + } + + public Builder headsign(String headsign) { + this.headsign = headsign; + return this; + } + + public Builder direction(String direction){ + this.direction = direction; + return this; + } + + public Builder firstStartTime(LocalTime firstStartTime){ + if(firstStartTime != null){ + this.firstStartTime = firstStartTime.toSecondOfDay(); + } + return this; + } + + public Builder lastStartTime(LocalTime lastStartTime){ + if(lastStartTime != null){ + this.lastStartTime = lastStartTime.toSecondOfDay(); + } + return this; + } + + public Builder readOnly(boolean readOnly) { + this.readOnly = readOnly; + return this; + } + + public TripQuery build(){ + TripQuery query = new TripQuery(this); + return query; + } + } +} diff --git a/transitime/src/main/java/org/transitime/db/structs/ActiveRevisions.java b/transitclock/src/main/java/org/transitclock/db/structs/ActiveRevisions.java similarity index 91% rename from transitime/src/main/java/org/transitime/db/structs/ActiveRevisions.java rename to transitclock/src/main/java/org/transitclock/db/structs/ActiveRevisions.java index addf72ea7..c463760ec 100644 --- a/transitime/src/main/java/org/transitime/db/structs/ActiveRevisions.java +++ b/transitclock/src/main/java/org/transitclock/db/structs/ActiveRevisions.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.db.structs; +package org.transitclock.db.structs; import javax.persistence.Column; import javax.persistence.Entity; @@ -28,7 +28,7 @@ import org.hibernate.annotations.DynamicUpdate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.db.hibernate.HibernateUtils; +import org.transitclock.db.hibernate.HibernateUtils; /** * For keeping track of current revisions. This table should only have a single @@ -61,7 +61,13 @@ public class ActiveRevisions { @Column private int travelTimesRev; - private static final Logger logger = + + // For the traffic configuration data. Updated independently of + // configRev and travelTimesRev. + @Column + private Integer trafficRev; + + private static final Logger logger = LoggerFactory.getLogger(ActiveRevisions.class); /********************** Member Functions **************************/ @@ -72,6 +78,7 @@ public class ActiveRevisions { public ActiveRevisions() { configRev = -1; travelTimesRev = -1; + trafficRev = null; } /** @@ -184,6 +191,12 @@ public int getConfigRev() { return configRev; } + public Integer getTrafficRev() { return trafficRev; } + + public void setTrafficRev(Integer trafficRev) { + this.trafficRev = trafficRev; + } + public int getTravelTimesRev() { return travelTimesRev; } @@ -199,7 +212,8 @@ public boolean isValid() { public String toString() { return "ActiveRevisions [" + "configRev=" + configRev - + ", travelTimesRev=" + travelTimesRev + + ", travelTimesRev=" + travelTimesRev + + ", trafficRev=" + trafficRev + "]"; } diff --git a/transitime/src/main/java/org/transitime/db/structs/Agency.java b/transitclock/src/main/java/org/transitclock/db/structs/Agency.java similarity index 98% rename from transitime/src/main/java/org/transitime/db/structs/Agency.java rename to transitclock/src/main/java/org/transitclock/db/structs/Agency.java index ca573d6d6..aab61664c 100644 --- a/transitime/src/main/java/org/transitime/db/structs/Agency.java +++ b/transitclock/src/main/java/org/transitclock/db/structs/Agency.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.db.structs; +package org.transitclock.db.structs; import java.io.Serializable; import java.util.List; @@ -31,9 +31,9 @@ import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.annotations.DynamicUpdate; -import org.transitime.db.hibernate.HibernateUtils; -import org.transitime.gtfs.gtfsStructs.GtfsAgency; -import org.transitime.utils.Time; +import org.transitclock.db.hibernate.HibernateUtils; +import org.transitclock.gtfs.gtfsStructs.GtfsAgency; +import org.transitclock.utils.Time; /** * Contains data from the agency.txt GTFS file. This class is diff --git a/transitime/src/main/java/org/transitime/db/structs/Arrival.java b/transitclock/src/main/java/org/transitclock/db/structs/Arrival.java similarity index 77% rename from transitime/src/main/java/org/transitime/db/structs/Arrival.java rename to transitclock/src/main/java/org/transitclock/db/structs/Arrival.java index 0edf45f1a..b2845af03 100644 --- a/transitime/src/main/java/org/transitime/db/structs/Arrival.java +++ b/transitclock/src/main/java/org/transitclock/db/structs/Arrival.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.db.structs; +package org.transitclock.db.structs; import java.util.Date; @@ -44,14 +44,20 @@ public class Arrival extends ArrivalDeparture { * So can match arrival to the AVL report that generated it * @param block * @param tripIndex - * @param stopPathIndex + * @param pathIndex + * @param freqStartTime */ public Arrival(String vehicleId, Date time, Date avlTime, Block block, - int tripIndex, int pathIndex) { + int tripIndex, int pathIndex, Date freqStartTime, String stopPathId) { super(vehicleId, time, avlTime, block, tripIndex, pathIndex, - true); // isArrival + true, freqStartTime, null, stopPathId); // isArrival + } + + public Arrival(int configRev, String vehicleId, Date time, Date avlTime, Block block, + int tripIndex, int pathIndex, Date freqStartTime, String stopPathId) { + super(configRev, vehicleId, time, avlTime, block, tripIndex, pathIndex, + true, freqStartTime, null, stopPathId); // isArrival } - /** * Hibernate always wants a no-arg constructor. Made private since * it shouldn't normally be used. @@ -71,7 +77,7 @@ private Arrival() { */ public Arrival withUpdatedTime(Date newTime) { return new Arrival(getVehicleId(), newTime, getAvlTime(), getBlock(), - getTripIndex(), getStopPathIndex()); + getTripIndex(), getStopPathIndex(), this.getFreqStartTime(), this.getStopPathId()); } } diff --git a/transitime/src/main/java/org/transitime/db/structs/ArrivalDeparture.java b/transitclock/src/main/java/org/transitclock/db/structs/ArrivalDeparture.java old mode 100644 new mode 100755 similarity index 52% rename from transitime/src/main/java/org/transitime/db/structs/ArrivalDeparture.java rename to transitclock/src/main/java/org/transitclock/db/structs/ArrivalDeparture.java index b6de28392..2a5eca4f3 --- a/transitime/src/main/java/org/transitime/db/structs/ArrivalDeparture.java +++ b/transitclock/src/main/java/org/transitclock/db/structs/ArrivalDeparture.java @@ -14,39 +14,38 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.db.structs; +package org.transitclock.db.structs; -import java.io.Serializable; -import java.util.Date; -import java.util.Iterator; -import java.util.List; - -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; -import javax.persistence.Transient; - -import org.hibernate.CallbackException; -import org.hibernate.HibernateException; +import org.apache.commons.lang3.StringUtils; import org.hibernate.Query; -import org.hibernate.Session; -import org.hibernate.annotations.DynamicUpdate; +import org.hibernate.*; +import org.hibernate.annotations.*; import org.hibernate.classic.Lifecycle; +import org.hibernate.criterion.Restrictions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.applications.Core; -import org.transitime.configData.AgencyConfig; -import org.transitime.configData.DbSetupConfig; -import org.transitime.core.TemporalDifference; -import org.transitime.db.hibernate.HibernateUtils; -import org.transitime.logging.Markers; -import org.transitime.utils.Geo; -import org.transitime.utils.IntervalTimer; -import org.transitime.utils.Time; +import org.transitclock.applications.Core; +import org.transitclock.configData.AgencyConfig; +import org.transitclock.configData.DbSetupConfig; +import org.transitclock.core.ServiceType; +import org.transitclock.core.TemporalDifference; +import org.transitclock.db.hibernate.HibernateUtils; +import org.transitclock.db.query.ArrivalDepartureQuery; +import org.transitclock.logging.Markers; +import org.transitclock.utils.Geo; +import org.transitclock.utils.IntervalTimer; +import org.transitclock.utils.Time; + +import javax.persistence.*; +import javax.persistence.Entity; +import javax.persistence.Index; +import javax.persistence.Table; +import java.io.Serializable; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.util.*; /** * For persisting an Arrival or a Departure time. Should use Arrival or @@ -61,13 +60,16 @@ * * @author SkiBu Smith */ + @Entity @DynamicUpdate @Table(name="ArrivalsDepartures", indexes = { @Index(name="ArrivalsDeparturesTimeIndex", columnList="time" ), @Index(name="ArrivalsDeparturesRouteTimeIndex", - columnList="routeShortName, time" )} ) + columnList="routeShortName, time" ), + @Index(name="ArrivalsDeparturesTripPatternIdIndex", + columnList="tripPatternId" )} ) public class ArrivalDeparture implements Lifecycle, Serializable { @Id @@ -157,6 +159,11 @@ public class ArrivalDeparture implements Lifecycle, Serializable { @Column private final int tripIndex; + /* this is required for frequenecy based services */ + @Column + @Temporal(TemporalType.TIMESTAMP) + private final Date freqStartTime; + // The index of which stop path this is within the trip. // Different from the GTFS gtfsStopSeq. The stopPathIndex starts // at 0 and increments by one for every stop. The GTFS gtfsStopSeq @@ -186,16 +193,54 @@ public class ArrivalDeparture implements Lifecycle, Serializable { // So can easily create copy constructor withUpdatedTime() @Transient private final Block block; - - // Needed because some methods need to know if dealing with arrivals or - // departures. + + // Record of dwell time for departures + @Column + private final Long dwellTime; + + @Column(length=TripPattern.TRIP_PATTERN_ID_LENGTH) + private String tripPatternId; + + @Column(length=2*HibernateUtils.DEFAULT_ID_SIZE) + private String stopPathId; + + @ManyToOne(fetch=FetchType.LAZY) + @JoinColumns( + { + @JoinColumn(updatable=false,insertable=false, name="stopId", referencedColumnName="id"), + @JoinColumn(updatable=false,insertable=false, name="configRev", referencedColumnName="configRev") + } + ) + private Stop stop; + + // Fetches first Trip that matches tripId and configRev + // Does NOT take frequencyTime into consideration + // TODO - add trip startTime as seconds to ArrivalDeparture and join on that + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumnsOrFormulas({ + @JoinColumnOrFormula(column = @JoinColumn(updatable=false,insertable=false, name="tripId", referencedColumnName="tripId")), + @JoinColumnOrFormula(column = @JoinColumn(updatable=false,insertable=false, name="configRev", referencedColumnName="configRev")), + @JoinColumnOrFormula(formula = @JoinFormula(value="(SELECT t.startTime FROM Trips t WHERE t.tripId = tripId AND t.configRev = configRev LIMIT 1)", referencedColumnName="startTime")) + }) + private Trip trip; + + @ManyToOne(fetch=FetchType.LAZY) + @JoinColumns({ + @JoinColumn(updatable=false,insertable=false, name="stopPathId", referencedColumnName="stopPathId"), + @JoinColumn(updatable=false,insertable=false, name="tripPatternId", referencedColumnName="tripPatternId"), + @JoinColumn(updatable=false,insertable=false, name="configRev", referencedColumnName="configRev") + }) + private StopPath stopPath; + public enum ArrivalsOrDepartures {ARRIVALS, DEPARTURES}; private static final Logger logger = LoggerFactory.getLogger(ArrivalDeparture.class); + private static DateTimeFormatter isoDateTimeFormat = DateTimeFormatter.ISO_DATE_TIME; + // Needed because Hibernate objects must be serializable - private static final long serialVersionUID = 6511713704337986699L; + private static final long serialVersionUID = -2186334947521763886L; /********************** Member Functions **************************/ @@ -211,8 +256,9 @@ public enum ArrivalsOrDepartures {ARRIVALS, DEPARTURES}; * @param stopPathIndex * @param isArrival */ - protected ArrivalDeparture(String vehicleId, Date time, Date avlTime, Block block, - int tripIndex, int stopPathIndex, boolean isArrival) { + protected ArrivalDeparture(int configRev, String vehicleId, Date time, Date avlTime, Block block, + int tripIndex, int stopPathIndex, boolean isArrival, Date freqStartTime, Long dwellTime, + String stopPathId) { this.vehicleId = vehicleId; this.time = time; this.avlTime = avlTime; @@ -220,54 +266,82 @@ protected ArrivalDeparture(String vehicleId, Date time, Date avlTime, Block bloc this.tripIndex = tripIndex; this.stopPathIndex = stopPathIndex; this.isArrival = isArrival; - this.configRev = Core.getInstance().getDbConfig().getConfigRev(); + this.configRev = configRev; + this.freqStartTime = freqStartTime; + this.dwellTime = dwellTime; + this.stopPathId = stopPathId; // Some useful convenience variables - Trip trip = block.getTrip(tripIndex); - StopPath stopPath = trip.getStopPath(stopPathIndex); - String stopId = stopPath.getStopId(); - - // Determine and store stop order - this.stopOrder = + + if(block!=null) + { + Trip trip = block.getTrip(tripIndex); + StopPath stopPath = trip.getStopPath(stopPathIndex); + this.tripPatternId = stopPath.getTripPatternId(); + String stopId = stopPath.getStopId(); + // Determine and store stop order + this.stopOrder = trip.getRoute().getStopOrder(trip.getDirectionId(), stopId, stopPathIndex); - // Determine the schedule time, which is a bit complicated. - // Of course, only do this for schedule based assignments. - // The schedule time will only be set if the schedule info was available - // from the GTFS data and it is the proper type of arrival or departure - // stop (there is an arrival schedule time and this is the last stop for - // a trip and and this is an arrival time OR there is a departure schedule - // time and this is not the last stop for a trip and this is a departure - // time. - Date scheduledEpochTime = null; - if (!trip.isNoSchedule()) { - ScheduleTime scheduleTime = trip.getScheduleTime(stopPathIndex); - if (stopPath.isLastStopInTrip() && scheduleTime.getArrivalTime() != null - && isArrival) { - long epochTime = Core.getInstance().getTime() - .getEpochTime(scheduleTime.getArrivalTime(), time); - scheduledEpochTime = new Date(epochTime); - } else if (!stopPath.isLastStopInTrip() - && scheduleTime.getDepartureTime() != null && !isArrival) { - long epochTime = Core.getInstance().getTime() - .getEpochTime(scheduleTime.getDepartureTime(), time); - scheduledEpochTime = new Date(epochTime); + // Determine the schedule time, which is a bit complicated. + // Of course, only do this for schedule based assignments. + // The schedule time will only be set if the schedule info was available + // from the GTFS data and it is the proper type of arrival or departure + // stop (there is an arrival schedule time and this is the last stop for + // a trip and and this is an arrival time OR there is a departure schedule + // time and this is not the last stop for a trip and this is a departure + // time. + Date scheduledEpochTime = null; + if (!trip.isNoSchedule()) { + ScheduleTime scheduleTime = trip.getScheduleTime(stopPathIndex); + if (stopPath.isLastStopInTrip() && scheduleTime.getArrivalTime() != null + && isArrival) { + long epochTime = Core.getInstance().getTime() + .getEpochTime(scheduleTime.getArrivalTime(), time); + scheduledEpochTime = new Date(epochTime); + } else if (!stopPath.isLastStopInTrip() + && scheduleTime.getDepartureTime() != null && !isArrival) { + long epochTime = Core.getInstance().getTime() + .getEpochTime(scheduleTime.getDepartureTime(), time); + scheduledEpochTime = new Date(epochTime); + } + } + this.scheduledTime = scheduledEpochTime; + + this.blockId = block.getId(); + this.tripId = trip.getId(); + this.directionId = trip.getDirectionId(); + this.stopId = stopId; + this.gtfsStopSeq = stopPath.getGtfsStopSeq(); + this.stopPathLength = (float) stopPath.getLength(); + this.routeId = trip.getRouteId(); + this.routeShortName = trip.getRouteShortName(); + this.serviceId = block.getServiceId(); + }else + { + /* have to do this as they are final */ + this.stopPathLength=0; + this.gtfsStopSeq=0; + this.scheduledTime=null; + this.tripId=""; + this.stopId=""; + this.serviceId = ""; + this.stopOrder=0; + this.tripPatternId = null; } - this.scheduledTime = scheduledEpochTime; + } + protected ArrivalDeparture(String vehicleId, Date time, Date avlTime, Block block, + int tripIndex, int stopPathIndex, boolean isArrival, Date freqStartTime, Long dwellTime, + String stopPathId) { - this.blockId = block.getId(); - this.tripId = trip.getId(); - this.directionId = trip.getDirectionId(); - this.stopId = stopId; - this.gtfsStopSeq = stopPath.getGtfsStopSeq(); - this.stopPathLength = (float) stopPath.getLength(); - this.routeId = trip.getRouteId(); - this.routeShortName = trip.getRouteShortName(); - this.serviceId = block.getServiceId(); + this(Core.getInstance().getDbConfig().getConfigRev(),vehicleId, time, avlTime, block, + tripIndex, stopPathIndex, isArrival, freqStartTime, dwellTime, stopPathId); + } + public Date getFreqStartTime() { + return freqStartTime; } - /** * Hibernate requires a no-arg constructor for reading objects * from database. @@ -292,6 +366,10 @@ protected ArrivalDeparture() { this.routeId = null; this.routeShortName = null; this.serviceId = null; + this.freqStartTime = null; + this.dwellTime = null; + this.tripPatternId = null; + this.stopPathId = null; } /** @@ -316,6 +394,10 @@ public void onLoad(Session s, Serializable id) throws CallbackException { serviceId = serviceId.intern(); if (directionId != null) directionId= directionId.intern(); + if (tripPatternId != null) + tripPatternId= tripPatternId.intern(); + if (stopPathId != null) + stopPathId= stopPathId.intern(); } /** @@ -352,10 +434,10 @@ public boolean onDelete(Session s) throws CallbackException { public void logCreation() { logger.info(this.toString()); } - + /** * Because using a composite Id Hibernate wants this member. - */ + */ @Override public int hashCode() { final int prime = 31; @@ -395,12 +477,21 @@ public int hashCode() { result = prime * result + ((vehicleId == null) ? 0 : vehicleId.hashCode()); + result = + prime * result + + ((dwellTime == null) ? 0 : dwellTime.hashCode()); + result = + prime * result + + ((tripPatternId == null) ? 0 : tripPatternId.hashCode()); + result = + prime * result + + ((stopPathId == null) ? 0 : stopPathId.hashCode()); return result; } /** * Because using a composite Id Hibernate wants this member. - */ + */ @Override public boolean equals(Object obj) { if (this == obj) @@ -488,6 +579,21 @@ public boolean equals(Object obj) { return false; } else if (!vehicleId.equals(other.vehicleId)) return false; + if (dwellTime == null) { + if (other.dwellTime != null) + return false; + } else if (!dwellTime.equals(other.dwellTime)) + return false; + if (tripPatternId == null) { + if (other.tripPatternId != null) + return false; + } else if (!tripPatternId.equals(other.tripPatternId)) + return false; + if (stopPathId == null) { + if (other.stopPathId != null) + return false; + } else if (!stopPathId.equals(other.stopPathId)) + return false; return true; } @@ -502,11 +608,14 @@ public String toString() { + ", directionId=" + directionId + ", stop=" + stopId + ", gtfsStopSeq=" + gtfsStopSeq - + ", stopIdx=" + stopPathIndex + + ", stopIdx=" + stopPathIndex + + ", stopPathId=" + stopPathId + + ", freqStartTime=" + freqStartTime + ", stopOrder=" + stopOrder + ", avlTime=" + Time.timeStrMsec(avlTime) - + ", trip=" + tripId - + ", tripIdx=" + tripIndex + + ", trip=" + tripId + + ", tripIdx=" + tripIndex + + ", tripPatternId=" + tripPatternId + ", block=" + blockId + ", srv=" + serviceId + ", cfg=" + configRev @@ -516,6 +625,7 @@ public String toString() { + (scheduledTime != null ? ", schedAdh=" + new TemporalDifference( scheduledTime.getTime() - time.getTime()) : "") + + (dwellTime != null ? ", dwellTime=" + dwellTime : "") + "]"; } @@ -560,7 +670,6 @@ public static Iterator getArrivalsDeparturesDbIterator( /** * Read in arrivals and departures for a vehicle, over a time range. * - * @param projectId * @param beginTime * @param endTime * @param vehicleId @@ -571,13 +680,88 @@ public static List getArrivalsDeparturesFromDb( // Call in standard getArrivalsDeparturesFromDb() but pass in // sql clause return getArrivalsDeparturesFromDb( - null, // Use db specified by transitime.db.dbName + null, // Use db specified by transitclock.db.dbName beginTime, endTime, "AND vehicleId='" + vehicleId + "'", 0, 0, // Don't use batching - null); // Read both arrivals and departures + null, // Read both arrivals and departures + false); } + /** + * Reads in arrivals and departures for a particular trip and service. Create session and uses it + * + * @param beginTime + * @param endTime + * @param tripId + * @param serviceId + * @return + */ + public static List getArrivalsDeparturesFromDb(Date beginTime, Date endTime, String tripId, String serviceId) + { + Session session = HibernateUtils.getSession(); + + return ArrivalDeparture.getArrivalsDeparturesFromDb(session, beginTime, endTime, tripId, serviceId); + } + + /** + * Reads in arrivals and departures for a particular trip and service. Uses session provided + * + * @paran session + * @param beginTime + * @param endTime + * @param tripId + * @param serviceId + * @return + */ + public static List getArrivalsDeparturesFromDb(Session session, Date beginTime, Date endTime, String tripId, String serviceId) + { + Criteria criteria = session.createCriteria(ArrivalDeparture.class); + + criteria.add(Restrictions.eq( "tripId",tripId )); + criteria.add(Restrictions.gt("time", beginTime)); + criteria.add(Restrictions.lt("time",endTime)).list(); + + if(serviceId!=null) + criteria.add(Restrictions.eq( "serviceId",serviceId )); + + @SuppressWarnings("unchecked") + List arrivalsDeparatures=criteria.list(); + return arrivalsDeparatures; + + } + /** + * Reads in arrivals and departures for a particular stopPathIndex of a trip between two dates. Uses session provided + * + * @paran session + * @param beginTime + * @param endTime + * @param tripId + * @param stopPathIndex + * @return + */ + public static List getArrivalsDeparturesFromDb(Session session, Date beginTime, Date endTime, String tripId, Integer stopPathIndex) + { + Criteria criteria = session.createCriteria(ArrivalDeparture.class); + + if(tripId!=null) + { + criteria.add(Restrictions.eq( "tripId",tripId )); + + if(stopPathIndex!=null) + criteria.add(Restrictions.eq( "stopPathIndex",stopPathIndex )); + } + + criteria.add(Restrictions.gt("time", beginTime)); + criteria.add(Restrictions.lt("time",endTime)).list(); + + + + @SuppressWarnings("unchecked") + List arrivalsDeparatures=criteria.list(); + return arrivalsDeparatures; + + } /** * Reads the arrivals/departures for the timespan specified. All of the * data is read in at once so could present memory issue if reading @@ -639,7 +823,7 @@ public static List getArrivalsDeparturesFromDb( * @param dbName * Name of the database to retrieve data from. If set to null * then will use db name configured by Java property - * transitime.db.dbName + * transitclock.db.dbName * @param beginTime * @param endTime * @param sqlClause @@ -659,18 +843,18 @@ public static List getArrivalsDeparturesFromDb( public static List getArrivalsDeparturesFromDb( String dbName, Date beginTime, Date endTime, String sqlClause, - final int firstResult, final int maxResults, - ArrivalsOrDepartures arrivalOrDeparture) { + final Integer firstResult, final Integer maxResults, + ArrivalsOrDepartures arrivalOrDeparture, boolean readOnly) { IntervalTimer timer = new IntervalTimer(); // Get the database session. This is supposed to be pretty light weight - Session session = dbName != null ? HibernateUtils.getSession(dbName) : HibernateUtils.getSession(); + Session session = dbName != null ? HibernateUtils.getSession(dbName, readOnly) : HibernateUtils.getSession(true); // Create the query. Table name is case sensitive and needs to be the // class name instead of the name of the db table. String hql = "FROM ArrivalDeparture " + - " WHERE time >= :beginDate " + - " AND time < :endDate"; + " WHERE time between :beginDate " + + " AND :endDate"; if (arrivalOrDeparture != null) { if (arrivalOrDeparture == ArrivalsOrDepartures.ARRIVALS) hql += " AND isArrival = true"; @@ -686,9 +870,12 @@ public static List getArrivalsDeparturesFromDb( query.setTimestamp("endDate", endTime); // Only get a batch of data at a time if maxResults specified - query.setFirstResult(firstResult); - if (maxResults > 0) + if (firstResult != null) { + query.setFirstResult(firstResult); + } + if (maxResults != null && maxResults > 0) { query.setMaxResults(maxResults); + } try { @SuppressWarnings("unchecked") @@ -708,9 +895,54 @@ public static List getArrivalsDeparturesFromDb( } + public static Long getArrivalsDeparturesCountFromDb( + String dbName, Date beginTime, Date endTime, + ArrivalsOrDepartures arrivalOrDeparture, boolean readOnly) { + IntervalTimer timer = new IntervalTimer(); + Long count = null; + // Get the database session. This is supposed to be pretty light weight + Session session = dbName != null ? HibernateUtils.getSession(dbName, false) : HibernateUtils.getSession(true); + + // Create the query. Table name is case sensitive and needs to be the + // class name instead of the name of the db table. + String hql = "select count(*) FROM ArrivalDeparture " + + " WHERE time >= :beginDate " + + " AND time < :endDate"; + if (arrivalOrDeparture != null) { + if (arrivalOrDeparture == ArrivalsOrDepartures.ARRIVALS) + hql += " AND isArrival = true"; + else + hql += " AND isArrival = false"; + } + + Query query = session.createQuery(hql); + + // Set the parameters for the query + query.setTimestamp("beginDate", beginTime); + query.setTimestamp("endDate", endTime); + + + try { + count = (Long) query.uniqueResult(); + logger.debug("Getting arrival/departures from database took {} msec", + timer.elapsedMsec()); + return count; + } catch (HibernateException e) { + // Log error to the Core logger + Core.getLogger().error(e.getMessage(), e); + return null; + } finally { + // Clean things up. Not sure if this absolutely needed nor if + // it might actually be detrimental and slow things down. + session.close(); + } + + } + + /** * Same as other getArrivalsDeparturesFromDb() but uses - * -Dtransitime.db.dbName Java property to specify the name of the database. + * -Dtransitclock.db.dbName Java property to specify the name of the database. * * @param beginTime * @param endTime @@ -725,7 +957,328 @@ public static List getArrivalsDeparturesFromDb( final int firstResult, final int maxResults, ArrivalsOrDepartures arrivalOrDeparture) { return getArrivalsDeparturesFromDb(DbSetupConfig.getDbName(), beginTime, - endTime, sqlClause, firstResult, maxResults, arrivalOrDeparture); + endTime, sqlClause, firstResult, maxResults, arrivalOrDeparture, false); + } + + public static List getArrivalsDeparturesFromDb(LocalDate beginDate, LocalDate endDate, + LocalTime beginTime, LocalTime endTime, + String routeShortName, String headsign, + ServiceType serviceType, boolean timePointsOnly, + boolean scheduledTimesOnly, boolean dwellTimeOnly, + boolean includeTrip, boolean includeStop, + boolean includeStopPath, boolean readOnly) throws Exception { + return getArrivalsDeparturesFromDb(beginDate, endDate, beginTime, endTime, routeShortName, headsign, + null, null, serviceType, timePointsOnly, scheduledTimesOnly, dwellTimeOnly, + includeTrip, includeStop, includeStopPath, readOnly); + } + + + /** + * Reads the arrivals/departures for the timespan and routeId specified + * Can specify whether you want to retrieve the data from a readOnly db + * + * @param beginTime + * @param endTime + * @param routeShortName + * @param serviceType + * @param timePointsOnly + * @param readOnly + * @return + */ + public static List getArrivalsDeparturesFromDb(LocalDate beginDate, LocalDate endDate, + LocalTime beginTime, LocalTime endTime, + String routeShortName, String headsign, + String startStop, String endStop, + ServiceType serviceType, boolean timePointsOnly, + boolean scheduledTimesOnly, boolean dwellTimeOnly, + boolean includeTrip, boolean includeStop, + boolean includeStopPath, boolean readOnly) throws Exception { + IntervalTimer timer = new IntervalTimer(); + + // Get the database session. This is supposed to be pretty light weight + Session session = HibernateUtils.getSession(readOnly); + + // Create the query. Table name is case sensitive and needs to be the + // class name instead of the name of the db table. + + String hql = "SELECT " + + "ad " + + "FROM " + + "ArrivalDeparture ad " + + getTimePointsJoin(timePointsOnly) + + getServiceTypeJoin(serviceType) + + getStopsJoin(includeStop) + + getTripsJoin(headsign, includeTrip) + + getStopPathsJoin(includeStopPath) + + "WHERE " + + getArrivalDepartureTimeWhere(beginDate, endDate, beginTime, endTime) + + getRouteWhere(routeShortName) + + getTripPatternWhere(null) + + getScheduledTimesWhere(scheduledTimesOnly) + + getTimePointsWhere(timePointsOnly) + + getServiceTypeWhere(serviceType) + + getTripsWhere(headsign, includeTrip) + + getStopsWhere(includeStop) + + getStopPathsWhere(includeStopPath) + + getDwellTimesWhere(dwellTimeOnly) + + "ORDER BY ad.time, ad.stopPathIndex, ad.isArrival DESC"; + + try { + Query query = session.createQuery(hql); + + List results = query.list(); + + logger.debug("Getting arrival/departures from database took {} msec", + timer.elapsedMsec()); + + return results; + + } catch (HibernateException e) { + // Log error to the Core logger + Core.getLogger().error("Unable to retrieve arrival departures", e); + return null; + } finally { + // Clean things up. Not sure if this absolutely needed nor if + // it might actually be detrimental and slow things down. + session.close(); + } + } + + + /** + * Reads the arrivals/departures for the timespan and routeId specified + * Can specify whether you want to retrieve the data from a readOnly db + * + * @param adQuery {@link org.transitclock.db.query.ArrivalDepartureQuery} + * @return List + */ + public static List getArrivalsDeparturesFromDb(ArrivalDepartureQuery adQuery) throws Exception { + IntervalTimer timer = new IntervalTimer(); + + // Get the database session. This is supposed to be pretty light weight + Session session = HibernateUtils.getSession(adQuery.isReadOnly()); + + // Create the query. Table name is case sensitive and needs to be the + // class name instead of the name of the db table. + + String hql = "SELECT " + + "ad " + + "FROM " + + "ArrivalDeparture ad " + + getTimePointsJoin(adQuery.isTimePointsOnly()) + + getServiceTypeJoin(adQuery.getServiceType()) + + getStopsJoin(adQuery.isIncludeStop()) + + getTripsJoin(adQuery.getHeadsign(), adQuery.isIncludeTrip()) + + getStopPathsJoin(adQuery.isIncludeStopPath()) + + "WHERE " + + getArrivalDepartureTimeWhere(adQuery.getBeginDate(), adQuery.getEndDate(), adQuery.getBeginTime(), adQuery.getEndTime()) + + getRouteWhere(adQuery.getRouteShortName()) + + getTripPatternWhere(adQuery.getTripPatternId()) + + getTripIdsWhere(adQuery.getTripIds()) + + getScheduledTimesWhere(adQuery.isScheduledTimesOnly()) + + getTimePointsWhere(adQuery.isTimePointsOnly()) + + getServiceTypeWhere(adQuery.getServiceType()) + + getTripsWhere(adQuery.getHeadsign(), adQuery.isIncludeTrip()) + + getStopsWhere(adQuery.isIncludeStop()) + + getStopPathsWhere(adQuery.isIncludeStopPath()) + + getDwellTimesWhere(adQuery.isDwellTimeOnly()) + + "ORDER BY ad.time, ad.stopPathIndex, ad.isArrival DESC"; + + try { + Query query = session.createQuery(hql); + if(adQuery.getTripIds() != null) { + query.setParameterList("tripIds", adQuery.getTripIds()); + } + + List results = query.list(); + + logger.debug("Getting arrival/departures from database took {} msec", + timer.elapsedMsec()); + + return results; + + } catch (HibernateException e) { + // Log error to the Core logger + Core.getLogger().error("Unable to retrieve arrival departures", e); + return null; + } finally { + // Clean things up. Not sure if this absolutely needed nor if + // it might actually be detrimental and slow things down. + session.close(); + } + } + + /** + * Helper HQL methods for getArrivalsDeparturesFromDb + * Broken down into JOIN methods and WHERE methods + * / + + /* + * JOIN HQL STATEMENTS + */ + private static String getTimePointsJoin(boolean timePointsOnly){ + if(!timePointsOnly){ + return ""; + } + return ", StopPath sp "; + } + + private static String getServiceTypeJoin(ServiceType serviceType){ + if(serviceType == null){ + return ""; + } + return ", Calendar c "; + } + + private static String getStopsJoin(boolean includeStop){ + if(includeStop){ + return "JOIN FETCH ad.stop s "; + } + return ""; + } + + private static String getTripsJoin(String headsign, boolean includeTrip){ + if(includeTrip){ + return " JOIN FETCH ad.trip t "; + }else if(StringUtils.isNotBlank(headsign)){ + return ", Trip t "; + } + + return ""; + } + + private static String getStopPathsJoin(boolean includeStopPath) { + if(includeStopPath){ + return "JOIN FETCH ad.stopPath sp "; + } + return ""; + } + + /* + * WHERE HQL STATEMENTS + */ + public static String getArrivalDepartureTimeWhere(LocalDate beginDate, LocalDate endDate, LocalTime beginTime, LocalTime endTime) { + String hql = ""; + + if(beginTime != null && endTime != null) { + List dates = new ArrayList<>(); + while (!beginDate.isAfter(endDate)) { + dates.add(beginDate); + beginDate = beginDate.plusDays(1); + } + + for (int i = 0; i < dates.size(); i++) { + if (i == 0) { + hql += " ("; + + } else { + hql += " OR "; + } + LocalDateTime startDateTime = LocalDateTime.of(dates.get(i), beginTime); + LocalDateTime endDateTime = LocalDateTime.of(dates.get(i), endTime); + hql += String.format(" ad.time between '%s' AND '%s' ", + startDateTime.format(isoDateTimeFormat), endDateTime.format(isoDateTimeFormat)); + if (i == dates.size() - 1) { + hql += ") "; + } + } + } + else if(!beginDate.isAfter(endDate)) { + LocalDateTime startDateTime = beginDate.atStartOfDay(); + LocalDateTime endDateTime = endDate.atTime(LocalTime.MAX); + hql += String.format(" ad.time between '%s' AND '%s' ", + startDateTime.format(isoDateTimeFormat), endDateTime.format(isoDateTimeFormat)); + } + else{ + LocalDateTime startDateTime = beginDate.atStartOfDay(); + LocalDateTime endDateTime = beginDate.atTime(LocalTime.MAX); + hql += String.format(" ad.time between '%s' AND '%s' ", + startDateTime.format(isoDateTimeFormat), endDateTime.format(isoDateTimeFormat)); + } + return hql; + } + + private static String getRouteWhere(String routeShortName){ + if(StringUtils.isNotBlank(routeShortName) && routeShortName !=null) { + return String.format("AND ad.routeShortName = '%s' ", routeShortName); + } + return ""; + } + + private static String getTripPatternWhere(String tripPatternId){ + if(StringUtils.isNotBlank(tripPatternId)) { + return String.format("AND ad.tripPatternId = '%s' ", tripPatternId); + } + return ""; + } + + private static String getTripIdsWhere(Set tripIds){ + if(tripIds != null) { + return "AND ad.tripId IN (:tripIds)"; + } + return ""; + } + + private static String getScheduledTimesWhere(boolean scheduledTimesOnly){ + if(scheduledTimesOnly){ + return "AND ad.scheduledTime IS NOT NULL "; + } + return ""; + } + + private static String getTimePointsWhere(boolean timePointsOnly){ + if(timePointsOnly){ + return "AND ad.configRev = sp.configRev AND ad.stopId = sp.stopId AND ad.tripPatternId = sp.tripPatternId AND sp.scheduleAdherenceStop = true "; + } + return ""; + } + + private static String getServiceTypeWhere(ServiceType serviceType){ + if(serviceType != null) { + String query = "AND ad.serviceId = c.serviceId AND ad.configRev = c.configRev "; + if (serviceType.equals(ServiceType.WEEKDAY)) { + query += "AND (c.monday = true OR c.tuesday = true OR c.wednesday = true OR c.thursday = true OR c.friday = true)"; + } else if (serviceType.equals(ServiceType.SATURDAY)) { + query += "AND c.saturday = true "; + } else if (serviceType.equals(ServiceType.SUNDAY)) { + query += "AND c.sunday = true "; + } + return query; + } + return ""; + } + + private static String getTripsWhere(String headsign, boolean includeTrip){ + String tripsWhere = ""; + boolean includeHeadsign = StringUtils.isNotBlank(headsign); + if(includeTrip || includeHeadsign) { + tripsWhere = "AND ad.configRev = t.configRev AND ad.tripId = t.tripId "; + if(includeHeadsign){ + tripsWhere += String.format("AND t.headsign = '%s' ", headsign); + } + } + return tripsWhere; + } + + private static String getStopsWhere(boolean includeStop){ + if(includeStop){ + return "AND ad.configRev = s.configRev AND ad.stopId = s.id "; + } + return ""; + } + + private static String getStopPathsWhere(boolean includeStopPaths){ + if(includeStopPaths){ + return "AND ad.configRev = sp.configRev AND ad.stopPathId = sp.stopPathId AND ad.tripPatternId = sp.tripPatternId "; + } + return ""; + } + + private static String getDwellTimesWhere(boolean dwellTimesOnly){ + if(dwellTimesOnly){ + return "AND ad.dwellTime != null "; + } + return ""; } public String getVehicleId() { @@ -846,7 +1399,7 @@ public Block getBlock() { public Date getScheduledDate() { return scheduledTime; } - + /** * Same as getScheduledDate() but returns long epoch time. * @return @@ -854,7 +1407,7 @@ public Date getScheduledDate() { public long getScheduledTime() { return scheduledTime.getTime(); } - + /** * Returns the schedule adherence for the stop if there was a schedule * time. Otherwise returns null. @@ -880,4 +1433,31 @@ public TemporalDifference getScheduleAdherence() { public Stop getStop() { return Core.getInstance().getDbConfig().getStop(stopId); } + + /** + * @return the gtfsStopSequence associated with the arrival/departure + */ + public int getGtfsStopSequence() { + return gtfsStopSeq; + } + + public Long getDwellTime() { + return dwellTime; + } + + public String getStopPathId() { + return stopPathId; + } + + public String getTripPatternId() { + return tripPatternId; + } + + public Stop getStopFromDb() { + return stop; + } + + public Trip getTripFromDb() { return trip; } + + public StopPath getStopPathFromDb() { return stopPath; } } diff --git a/transitime/src/main/java/org/transitime/db/structs/AvlReport.java b/transitclock/src/main/java/org/transitclock/db/structs/AvlReport.java similarity index 95% rename from transitime/src/main/java/org/transitime/db/structs/AvlReport.java rename to transitclock/src/main/java/org/transitclock/db/structs/AvlReport.java index e552a7006..00e9bd796 100644 --- a/transitime/src/main/java/org/transitime/db/structs/AvlReport.java +++ b/transitclock/src/main/java/org/transitclock/db/structs/AvlReport.java @@ -14,24 +14,14 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.db.structs; +package org.transitclock.db.structs; import java.io.Serializable; import java.util.Date; import java.util.List; import java.util.regex.Pattern; -import javax.persistence.Column; -import javax.persistence.Embedded; -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; -import javax.persistence.Transient; +import javax.persistence.*; import org.hibernate.HibernateException; import org.hibernate.Query; @@ -39,12 +29,12 @@ import org.hibernate.annotations.DynamicUpdate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.applications.Core; -import org.transitime.configData.AvlConfig; -import org.transitime.db.hibernate.HibernateUtils; -import org.transitime.ipc.data.IpcAvl; -import org.transitime.utils.Geo; -import org.transitime.utils.Time; +import org.transitclock.applications.Core; +import org.transitclock.configData.AvlConfig; +import org.transitclock.db.hibernate.HibernateUtils; +import org.transitclock.ipc.data.IpcAvl; +import org.transitclock.utils.Geo; +import org.transitclock.utils.Time; import net.jcip.annotations.Immutable; @@ -114,7 +104,7 @@ public class AvlReport implements Serializable { // Optional text for describing the source of the AVL report @Column(length=SOURCE_LENGTH) - private final String source; + private String source; // Can be block, trip, or route ID @Column(length=HibernateUtils.DEFAULT_ID_SIZE) @@ -563,8 +553,21 @@ else if (vehicleId.length() == 0) long currentTime = System.currentTimeMillis(); if (time.getTime() < currentTime - 10 * Time.MS_PER_YEAR) errorMsg += "Time of " + Time.dateTimeStr(time) + " is more than 10 years old. "; - if (time.getTime() > currentTime + 1 * Time.MS_PER_MIN) - errorMsg += "Time of " + Time.dateTimeStr(time) + " is more than 1 minute into the future. "; + if (time.getTime() > currentTime + 1 * Time.MS_PER_MIN) { + if (AvlConfig.shouldTryTimeZoneCorrection()) { + if (time.getTime() - (AvlConfig.getTimeZoneCorrection() * Time.MS_PER_SEC) + > currentTime + 1 * Time.MS_PER_MIN) { + errorMsg += "Time of " + Time.dateTimeStr(time) + + " is more than 1 minute into the future even after correction. Delta={ " + + Time.elapsedTimeStr(time.getTime() - currentTime) + "}"; + } else { + time.setTime(time.getTime() - (AvlConfig.getTimeZoneCorrection() * Time.MS_PER_SEC)); + logger.info("correcting timestamp for vehicle {} to {}", vehicleId, time); + } + } else { + errorMsg += "Time of " + Time.dateTimeStr(time) + " is more than 1 minute into the future. "; + } + } // Make sure lat/lon is OK double lat = location.getLat(); @@ -788,6 +791,13 @@ public String getSource() { return source; } + + /** + * Override the source set by constructor + */ + public void setSource(String source) { + this.source=sized(source); + } /** * Returns how many msec elapsed between the GPS fix was generated * to the time it was finally processed. Returns 0 if timeProcessed @@ -894,7 +904,7 @@ public boolean hasValidAssignment() { matchesUnpredictableAssignment(assignmentId)) logger.debug("For vehicleId={} was assigned to \"{}\" but that " + "assignment is not considered valid due to " + - "transitime.avl.unpredictableAssignmentsRegEx being set " + + "transitclock.avl.unpredictableAssignmentsRegEx being set " + "to \"{}\"", vehicleId, assignmentId, AvlConfig.getUnpredictableAssignmentsRegEx()); @@ -1012,7 +1022,8 @@ public boolean isPassengerFullnessValid() { * the data was actually processed. */ public void setTimeProcessed() { - timeProcessed = new Date(System.currentTimeMillis()); + timeProcessed = new Date(Core.getInstance().getSystemTime()); + } public String getField1Name() { @@ -1082,14 +1093,14 @@ public static List getAvlReportsFromDb( String hql = "FROM AvlReport " + " WHERE time >= :beginDate " + " AND time < :endDate"; - if (vehicleId != null) + if (vehicleId != null && !vehicleId.isEmpty()) hql += " AND vehicleId=:vehicleId"; if (clause != null) hql += " " + clause; Query query = session.createQuery(hql); // Set the parameters - if (vehicleId != null) + if (vehicleId != null && !vehicleId.isEmpty()) query.setString("vehicleId", vehicleId); query.setTimestamp("beginDate", beginTime); query.setTimestamp("endDate", endTime); diff --git a/transitime/src/main/java/org/transitime/db/structs/Block.java b/transitclock/src/main/java/org/transitclock/db/structs/Block.java similarity index 84% rename from transitime/src/main/java/org/transitime/db/structs/Block.java rename to transitclock/src/main/java/org/transitclock/db/structs/Block.java index d308a0149..ec2f08610 100644 --- a/transitime/src/main/java/org/transitime/db/structs/Block.java +++ b/transitclock/src/main/java/org/transitclock/db/structs/Block.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.db.structs; +package org.transitclock.db.structs; import java.io.Serializable; import java.net.SocketException; @@ -53,15 +53,17 @@ import org.postgresql.util.PSQLException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.applications.Core; -import org.transitime.configData.AgencyConfig; -import org.transitime.configData.CoreConfig; -import org.transitime.core.SpatialMatch; -import org.transitime.db.hibernate.HibernateUtils; -import org.transitime.gtfs.DbConfig; -import org.transitime.logging.Markers; -import org.transitime.utils.IntervalTimer; -import org.transitime.utils.Time; +import org.transitclock.applications.Core; +import org.transitclock.config.BooleanConfigValue; +import org.transitclock.config.StringConfigValue; +import org.transitclock.configData.AgencyConfig; +import org.transitclock.configData.CoreConfig; +import org.transitclock.core.SpatialMatch; +import org.transitclock.db.hibernate.HibernateUtils; +import org.transitclock.gtfs.DbConfig; +import org.transitclock.logging.Markers; +import org.transitclock.utils.IntervalTimer; +import org.transitclock.utils.Time; /** * Represents assignment for a vehicle for a day. Obtained by combining @@ -114,11 +116,9 @@ public final class Block implements Serializable { // Use CascadeType.SAVE_UPDATE so that when the TripPattern is stored // the Paths are automatically stored. // - // Use FetchType.LAZY so that don't read in all trip data at once since - // that in turn reads in trip pattern and travel time info, which can - // be voluminous and therefore slow. The trips will be read in when - // getTrips() is called. - @ManyToMany(fetch=FetchType.LAZY) + // Fetch EAGERly to avoid AVLExecutor threading issues + // take the hit to load blocks on startup for later concurrency/throughput + @ManyToMany(fetch=FetchType.EAGER) @JoinTable(name="Block_to_Trip_joinTable") @OrderColumn(name="listIndex") @Cascade({CascadeType.SAVE_UPDATE}) @@ -141,6 +141,9 @@ public final class Block implements Serializable { // Hibernate requires class to be serializable because has composite Id private static final long serialVersionUID = 6511242755235485004L; + + private static BooleanConfigValue blockLoading = + new BooleanConfigValue("transitclock.blockLoading.agressive", false, "Set true to eagerly fetch all blocks into memory on startup"); private static final Logger logger = LoggerFactory.getLogger(Block.class); @@ -201,13 +204,44 @@ private Block() { @SuppressWarnings("unchecked") public static List getBlocks(Session session, int configRev) throws HibernateException { - String hql = "FROM Blocks " + - " WHERE configRev = :configRev"; - Query query = session.createQuery(hql); - query.setInteger("configRev", configRev); - return query.list(); + try { + + if (Boolean.TRUE.equals(blockLoading.getValue())) { + logger.warn("caching blocks aggressively...."); + return getBlocksAgressively(session, configRev); + } + logger.warn("caching blocks passively...."); + return getBlocksPassive(session, configRev); + } finally { + logger.warn("caching complete"); + } } + private static List getBlocksPassive(Session session, int configRev) + throws HibernateException { + String hql = "FROM Blocks b " + + "WHERE b.configRev = :configRev"; + Query query = session.createQuery(hql); + query.setInteger("configRev", configRev); + return query.list(); + } + + private static List getBlocksAgressively(Session session, int configRev) + throws HibernateException { + String hql = "FROM Blocks as b " + + "join fetch b.trips t " + + "join fetch t.travelTimes tt " +// + "join fetch tt.travelTimesForStopPaths tsp " + + "join fetch t.tripPattern tp " + + "join fetch tp.stopPaths sp " + /*+ "join fetch sp.locations "*/ //this makes the resultset REALLY big + + "WHERE b.configRev = :configRev"; + Query query = session.createQuery(hql); + query.setInteger("configRev", configRev); + return query.list(); + } + + /** * Deletes rev from the Blocks, Trips, and Block_to_Trip_joinTable * @@ -277,9 +311,8 @@ public String toString() { + ", serviceId=" + serviceId + ", startTime=" + Time.timeOfDayStr(startTime) + ", endTime=" + Time.timeOfDayStr(endTime) - // Use getTrips() instead of trips to deal with possible lazy - // initialization issues - + ", trips=" + getTrips() + // Removing to avoid issues with lazy instantiation + //+ ", trips=" + getTrips() + "]"; } @@ -538,7 +571,7 @@ public boolean isActive(Date date, int allowableBeforeTimeSecs, * if the service associated with the block is active. Only looks at time of * day. * - * @param date + * @param epochTime * The time checking to see whether block is active for * @param allowableBeforeTimeSecs * Block considered active if within this number of seconds @@ -643,7 +676,7 @@ private static boolean addTripIfActive(String vehicleId, int allowableEarlyTimeSecs = CoreConfig.getAllowableEarlyForLayoverSeconds(); if (secsInDayForAvlReport > startTime - allowableEarlyTimeSecs - && secsInDayForAvlReport < endTime) { + && secsInDayForAvlReport < endTime + CoreConfig.getAllowableLateSeconds()) { tripsThatMatchTime.add(trip); if (logger.isDebugEnabled()) { @@ -667,7 +700,10 @@ private static boolean addTripIfActive(String vehicleId, return true; } - // Not a match so return false + if (logger.isDebugEnabled()) + logger.debug("block {} is not active for vehicleId {}", trip.getBlock().getId(), vehicleId); + + // Not a match so return false return false; } @@ -819,91 +855,99 @@ public List getTrips() { + "globalLazyLoadSession.", getId(), session == null ? null : session.hashCode(), globalLazyLoadSession.hashCode()); - - globalLazyLoadSession.update(this); + try { + globalLazyLoadSession.update(this); + } catch(Exception e){ + logger.warn("Unable to load lazy session", e); + } } } else { - // trips member must always be a PersistentList - logger.error("Blocks.trips member is not a PersistentList!?!?. " - + "Exiting!"); - System.exit(-1); + logger.error("Blocks.trips member is not a PersistentList!?!?. "); + // not exiting here.... } // Actually lazy-load the trips trips.get(0); } catch (JDBCException e) { - // Really shouldn't get an exception unless there was a problem - // communicating with the db. If connection to db lost then need - // to create a new one. It likely means that the db was rebooted - // or such. For this situation need to use a new session. - // Originally tried to only use a new session if the root cause - // of the exception was a SocketException or - // SocketTimeoutException. But then found that other exceptions - // can occur. So best to use a new session when any exception - // occurs. + // TODO this is an anti-pattern + // If root cause of exception is a SocketTimeoutException + // then somehow lost connection to the database. Might have + // been rebooted or such. For this situation need to attach + // object to new session. Throwable rootCause = HibernateUtils.getRootCause(e); - logger.error( - "Socket exception in getTrips() for " - + "blockId={}. Database might have been " - + "rebooted. Creating a new session if " - + "necessary. Root cause is {}.", - this.getId(), rootCause, e); + if (rootCause instanceof SocketTimeoutException || rootCause instanceof SocketException) { + logger.error("Socket timeout in getTrips() for " + + "blockId={}. Database might have been " + + "rebooted. Creating a new session.", + this.getId(), e); - if (!(rootCause instanceof SocketException - || rootCause instanceof SocketTimeoutException - || rootCause instanceof PSQLException)) { - logger.error(Markers.email(), - "For agencyId={} in Blocks.getTrips() for " - + "blockId={} encountered exception whose root " - + "cause was not a SocketException, " - + "SocketTimeoutException, or PSQLException," - + "which therefore is unexpected. Therefore should " - + "investigate. Root cause is {}.", - AgencyConfig.getAgencyId(), this.getId(), - rootCause, e); - } - - // Even though there was a timeout meaning that the - // session is no longer any good the Block object - // might still be associated with the old session. - // In order to attach the Block to a newly created - // session need to first close the old session or else - // system will complain that trying to add a object - // to two live sessions. Tried using session.evict(this) - // but still got exception "Illegal attempt to associate - // a collection with two open sessions" - PersistentList persistentListTrips = (PersistentList) trips; - SessionImplementor originalSessionImpl = - persistentListTrips.getSession(); - SessionImpl originalSession = (SessionImpl) originalSessionImpl; - if (!originalSession.isClosed()) { - try { - // Note: this causes a stack trace to be output - // to stdout by Hibernate. Seems that this - // cannot be avoided since need to close the - // session. - originalSession.close(); - } catch (HibernateException e1) { - logger.error("Exception occurred when trying " - + "to close session when lazy loading " - + "data after socket timeout occurred.", e1); + if (!(rootCause instanceof SocketException + || rootCause instanceof SocketTimeoutException + || rootCause instanceof PSQLException)) { + logger.error(Markers.email(), + "For agencyId={} in Blocks.getTrips() for " + + "blockId={} encountered exception whose root " + + "cause was not a SocketException, " + + "SocketTimeoutException, or PSQLException," + + "which therefore is unexpected. Therefore should " + + "investigate. Root cause is {}.", + AgencyConfig.getAgencyId(), this.getId(), + rootCause, e); + } + + // Even though there was a timeout meaning that the + // session is no longer any good the Block object + // might still be associated with the old session. + // In order to attach the Block to a newly created + // session need to first close the old session or else + // system will complain that trying to add a object + // to two live sessions. Tried using session.evict(this) + // but still got exception "Illegal attempt to associate + // a collection with two open sessions" + PersistentList persistentListTrips = (PersistentList) trips; + SessionImplementor sessionImpl = + persistentListTrips.getSession(); + SessionImpl session = (SessionImpl) sessionImpl; + if (!session.isClosed()) { + try { + // Note: this causes a stack trace to be output + // to stdout by Hibernate. Seems that this + // cannot be avoided since need to close the + // session. + session.close(); + } catch (HibernateException e1) { + logger.error("Exception occurred when trying " + + "to close session when lazy loading " + + "data after socket timeout occurred.", e1); + } } - } - // Get new session, update object to use it, and try again. - // Note: before calling get(0) to load the data first made - // sure that the session used for the Block.trips is the same - // as the current session. Therefore if made it here then it - // means that definitely need to create new session. - DbConfig dbConfig = Core.getInstance().getDbConfig(); - dbConfig.createNewGlobalSession(); - Session globalLazyLoadSession = dbConfig.getGlobalSession(); - globalLazyLoadSession.update(this); + // Get new session, update object to use it, and try again. + // Note: before calling get(0) to load the data first made + // sure that the session used for the Block.trips is the same + // as the current session. Therefore if made it here then it + // means that definitely need to create new session. + DbConfig dbConfig = Core.getInstance().getDbConfig(); + logger.info("CREATING NEW SESSION"); + dbConfig.createNewGlobalSession(); + Session globalLazyLoadSession = dbConfig.getGlobalSession(); + globalLazyLoadSession.update(this); + + // Now that have attached a new session lazy load the trips + // data + trips.get(0); + } else { + // Not a socket timeout. Therefore don't know handle + // to handle so just log and throw the exception + logger.error("In Block.getTrips() got JDBCException. " + + "SQL=\"{}\" msg={}", e.getSQL(), e + .getSQLException().getMessage(), e); + throw e; + } - // Now that have attached a new session lazy try loading the - // trips data + // Actually lazy-load the trips trips.get(0); - } + } logger.debug("Finished lazy load for trips data for " + "blockId={} serviceId={}. Took {} msec", blockId, @@ -912,6 +956,25 @@ public List getTrips() { return Collections.unmodifiableList(trips); } + + public List getTripsFromDb(boolean readOnly) { + Session session = HibernateUtils.getSession(readOnly); + try { + session.update(this); + trips.get(0); + } catch (HibernateException e) { + // Log error to the Core logger + Core.getLogger().error("Unable to retrieve trips", e); + return null; + } finally { + // Clean things up. Not sure if this absolutely needed nor if + // it might actually be detrimental and slow things down. + session.close(); + } + + return Collections.unmodifiableList(trips); + + } /** * So can sync up loading of trip and trip pattern data when trips are all @@ -957,6 +1020,16 @@ public Trip getTrip(int tripIndex) { // Return the specified trip return getTrips().get(tripIndex); } + + public Trip getTripFromDb(int tripIndex, boolean readOnly) { + List trips = getTripsFromDb(readOnly); + // If index out of range return null + if (tripIndex < 0 || tripIndex >= trips.size()) { + return null; + } + // Return the specified trip + return trips.get(tripIndex); + } /** * Returns the trip for the block as specified by the tripId parameter @@ -1187,7 +1260,10 @@ public StopPath getStopPath(int tripIndex, int stopPathIndex) { */ public Location getStartLoc() { StopPath firstStopPath = getStopPath(0, 0); - return firstStopPath.getEndOfPathLocation(); + if(firstStopPath!=null) + return firstStopPath.getEndOfPathLocation(); + else + return null; } /** diff --git a/transitime/src/main/java/org/transitime/db/structs/Calendar.java b/transitclock/src/main/java/org/transitclock/db/structs/Calendar.java similarity index 98% rename from transitime/src/main/java/org/transitime/db/structs/Calendar.java rename to transitclock/src/main/java/org/transitclock/db/structs/Calendar.java index 697247893..03e530ad0 100644 --- a/transitime/src/main/java/org/transitime/db/structs/Calendar.java +++ b/transitclock/src/main/java/org/transitclock/db/structs/Calendar.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.db.structs; +package org.transitclock.db.structs; import java.io.Serializable; import java.text.DateFormat; @@ -40,9 +40,9 @@ import org.hibernate.annotations.DynamicUpdate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.db.hibernate.HibernateUtils; -import org.transitime.gtfs.gtfsStructs.GtfsCalendar; -import org.transitime.utils.Time; +import org.transitclock.db.hibernate.HibernateUtils; +import org.transitclock.gtfs.gtfsStructs.GtfsCalendar; +import org.transitclock.utils.Time; /** * Contains data from the calendar.txt GTFS file. This class is diff --git a/transitime/src/main/java/org/transitime/db/structs/CalendarDate.java b/transitclock/src/main/java/org/transitclock/db/structs/CalendarDate.java similarity index 97% rename from transitime/src/main/java/org/transitime/db/structs/CalendarDate.java rename to transitclock/src/main/java/org/transitclock/db/structs/CalendarDate.java index 08cdbdc0e..62b56af11 100644 --- a/transitime/src/main/java/org/transitime/db/structs/CalendarDate.java +++ b/transitclock/src/main/java/org/transitclock/db/structs/CalendarDate.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.db.structs; +package org.transitclock.db.structs; import java.io.Serializable; import java.text.DateFormat; @@ -35,8 +35,8 @@ import org.hibernate.annotations.DynamicUpdate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.db.hibernate.HibernateUtils; -import org.transitime.gtfs.gtfsStructs.GtfsCalendarDate; +import org.transitclock.db.hibernate.HibernateUtils; +import org.transitclock.gtfs.gtfsStructs.GtfsCalendarDate; /** * Contains data from the calendardates.txt GTFS file. This class is diff --git a/transitime/src/main/java/org/transitime/db/structs/ConfigRevision.java b/transitclock/src/main/java/org/transitclock/db/structs/ConfigRevision.java similarity index 62% rename from transitime/src/main/java/org/transitime/db/structs/ConfigRevision.java rename to transitclock/src/main/java/org/transitclock/db/structs/ConfigRevision.java index b48b2eab2..5f15e7c3a 100644 --- a/transitime/src/main/java/org/transitime/db/structs/ConfigRevision.java +++ b/transitclock/src/main/java/org/transitclock/db/structs/ConfigRevision.java @@ -15,30 +15,29 @@ * along with Transitime.org . If not, see . */ -package org.transitime.db.structs; - -import java.util.Date; - -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; +package org.transitclock.db.structs; import org.hibernate.HibernateException; +import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.Transaction; import org.hibernate.annotations.DynamicUpdate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.db.hibernate.HibernateUtils; +import org.transitclock.db.hibernate.HibernateUtils; + +import javax.persistence.*; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Date; +import java.util.List; /** * For keeping track of information having to do with a configuration revision. * This way can keep track of reason for processing config, when it was run, * etc. * - * @author Michael Smith (michael@transitime.org) + * @author Michael Smith (michael@transitclock.org) * */ @Entity @DynamicUpdate @@ -59,6 +58,8 @@ public class ConfigRevision { @Column(length=512) private final String notes; + private static DateTimeFormatter isoDateTimeFormat = DateTimeFormatter.ISO_DATE_TIME; + // Logging public static final Logger logger = LoggerFactory.getLogger(ConfigRevision.class); @@ -86,7 +87,7 @@ private ConfigRevision() { this.configRev = -1; this.processedTime = null; this.zipFileLastModifiedTime = null; - this.notes = null; + this.notes = null; } @Override @@ -142,4 +143,47 @@ public String getNotes() { return notes; } + public static List getConfigRevisions(Session session, int configRev) throws HibernateException { + String hql = "From ConfigRevision c ORDER by configRev"; + Query query = session.createQuery(hql); + return query.list(); + } + + public static List getConfigRevisionsForDateRange(LocalDateTime startTime, + LocalDateTime endTime, + boolean readOnly) throws HibernateException { + + + String hql = "FROM ConfigRevision c " + + "WHERE c.processedTime between " + + "(" + + "SELECT MAX(c2.procssedTime) " + + "FROM ConfigRevision c2 " + + "WHERE c2.processedTime < :start " + + ") " + + "AND :end " + + "ORDER BY c.processedTime DESC"; + + Session session = HibernateUtils.getSession(readOnly); + Query query = session.createQuery(hql); + query.setParameter("start", startTime); + query.setParameter("end", endTime); + return query.list(); + } + + public static List getConfigRevisionsForMaxDate( LocalDateTime endTime, + boolean readOnly) throws HibernateException { + + + String hql = "FROM ConfigRevision c " + + "WHERE c.processedTime < :end " + + "ORDER BY c.processedTime DESC"; + + Session session = HibernateUtils.getSession(readOnly); + Query query = session.createQuery(hql); + query.setParameter("end", java.sql.Timestamp.valueOf(endTime)); + query.setMaxResults(1); + return query.list(); + } + } diff --git a/transitime/src/main/java/org/transitime/db/structs/DbTest.java b/transitclock/src/main/java/org/transitclock/db/structs/DbTest.java similarity index 98% rename from transitime/src/main/java/org/transitime/db/structs/DbTest.java rename to transitclock/src/main/java/org/transitclock/db/structs/DbTest.java index f7b0bc50e..356d605a3 100644 --- a/transitime/src/main/java/org/transitime/db/structs/DbTest.java +++ b/transitclock/src/main/java/org/transitclock/db/structs/DbTest.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.db.structs; +package org.transitclock.db.structs; import java.util.List; @@ -30,7 +30,7 @@ import org.hibernate.annotations.DynamicUpdate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.db.hibernate.HibernateUtils; +import org.transitclock.db.hibernate.HibernateUtils; /** * For testing ability to read and write to and from the db. diff --git a/transitime/src/main/java/org/transitime/db/structs/Departure.java b/transitclock/src/main/java/org/transitclock/db/structs/Departure.java similarity index 60% rename from transitime/src/main/java/org/transitime/db/structs/Departure.java rename to transitclock/src/main/java/org/transitclock/db/structs/Departure.java index fbcdeca2b..a6dac8b4d 100644 --- a/transitime/src/main/java/org/transitime/db/structs/Departure.java +++ b/transitclock/src/main/java/org/transitclock/db/structs/Departure.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.db.structs; +package org.transitclock.db.structs; import java.util.Date; @@ -41,18 +41,22 @@ public class Departure extends ArrivalDeparture { * @param time * @param avlTime * So can match arrival to the AVL report that generated it - * @param blockId - * @param stopId - * @param tripId + * @param block * @param tripIndex * @param stopPathIndex + * @param freqStartTime + * @param dwellTime */ public Departure(String vehicleId, Date time, Date avlTime, Block block, - int tripIndex, int stopPathIndex) { + int tripIndex, int stopPathIndex, Date freqStartTime, Long dwellTime, String stopPathId) { super(vehicleId, time, avlTime, block, tripIndex, stopPathIndex, - false); // isArrival + false, freqStartTime, dwellTime, stopPathId); + } + public Departure(int configRev, String vehicleId, Date time, Date avlTime, Block block, + int tripIndex, int stopPathIndex, Date freqStartTime, Long dwellTime, String stopPathId) { + super(configRev, vehicleId, time, avlTime, block, tripIndex, stopPathIndex, + false, freqStartTime, dwellTime, stopPathId); } - /** * Hibernate always wants a no-arg constructor. Made private since * it shouldn't normally be used. @@ -61,4 +65,18 @@ public Departure(String vehicleId, Date time, Date avlTime, Block block, private Departure() { super(); } + + /** + * A copy constructor that creates a new Departure by copying this + * one but using the newTime. This method is needed to update a time given + * that the class has all final elements. + * + * @param newTime + * @return The newly constructed Arrival with the new time. + */ + public Departure withUpdatedTime(Date newTime) { + return new Departure(getVehicleId(), newTime, getAvlTime(), getBlock(), + getTripIndex(), getStopPathIndex(), this.getFreqStartTime(), this.getDwellTime(), this.getStopPathId()); + } + } diff --git a/transitime/src/main/java/org/transitime/db/structs/Extent.java b/transitclock/src/main/java/org/transitclock/db/structs/Extent.java similarity index 98% rename from transitime/src/main/java/org/transitime/db/structs/Extent.java rename to transitclock/src/main/java/org/transitclock/db/structs/Extent.java index 375bc1244..c70fdaafa 100644 --- a/transitime/src/main/java/org/transitime/db/structs/Extent.java +++ b/transitclock/src/main/java/org/transitclock/db/structs/Extent.java @@ -14,15 +14,16 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.db.structs; +package org.transitclock.db.structs; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.Embeddable; -import net.jcip.annotations.Immutable; -import org.transitime.utils.Geo; +import org.transitclock.utils.Geo; + +import net.jcip.annotations.Immutable; /** diff --git a/transitime/src/main/java/org/transitime/db/structs/FareAttribute.java b/transitclock/src/main/java/org/transitclock/db/structs/FareAttribute.java similarity index 97% rename from transitime/src/main/java/org/transitime/db/structs/FareAttribute.java rename to transitclock/src/main/java/org/transitclock/db/structs/FareAttribute.java index cbfab6968..e143c89e9 100644 --- a/transitime/src/main/java/org/transitime/db/structs/FareAttribute.java +++ b/transitclock/src/main/java/org/transitclock/db/structs/FareAttribute.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.db.structs; +package org.transitclock.db.structs; import java.io.Serializable; import java.util.List; @@ -29,8 +29,8 @@ import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.annotations.DynamicUpdate; -import org.transitime.db.hibernate.HibernateUtils; -import org.transitime.gtfs.gtfsStructs.GtfsFareAttribute; +import org.transitclock.db.hibernate.HibernateUtils; +import org.transitclock.gtfs.gtfsStructs.GtfsFareAttribute; /** * Contains data from the fareattributes.txt GTFS file. This class is diff --git a/transitime/src/main/java/org/transitime/db/structs/FareRule.java b/transitclock/src/main/java/org/transitclock/db/structs/FareRule.java similarity index 97% rename from transitime/src/main/java/org/transitime/db/structs/FareRule.java rename to transitclock/src/main/java/org/transitclock/db/structs/FareRule.java index e1bea0c73..ec076032c 100644 --- a/transitime/src/main/java/org/transitime/db/structs/FareRule.java +++ b/transitclock/src/main/java/org/transitclock/db/structs/FareRule.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.db.structs; +package org.transitclock.db.structs; import java.io.Serializable; import java.util.List; @@ -28,8 +28,8 @@ import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.annotations.DynamicUpdate; -import org.transitime.db.hibernate.HibernateUtils; -import org.transitime.gtfs.gtfsStructs.GtfsFareRule; +import org.transitclock.db.hibernate.HibernateUtils; +import org.transitclock.gtfs.gtfsStructs.GtfsFareRule; /** * Contains data from the fare_rules.txt GTFS file. This class is @@ -94,7 +94,7 @@ public FareRule(int configRev, GtfsFareRule gfr, String properRouteId) { this.routeId = routeIdToUse; this.originId = gfr.getOriginId()==null?"":gfr.getOriginId(); this.destinationId = gfr.getDestinationId()==null?"":gfr.getDestinationId(); - this.containsId = gfr.getContainsId(); + this.containsId = gfr.getContainsId()==null?"":gfr.getContainsId();; } /** diff --git a/transitclock/src/main/java/org/transitclock/db/structs/FeedInfo.java b/transitclock/src/main/java/org/transitclock/db/structs/FeedInfo.java new file mode 100644 index 000000000..eecd993e8 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/db/structs/FeedInfo.java @@ -0,0 +1,214 @@ +/* + * 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.db.structs; + +import org.hibernate.HibernateException; +import org.hibernate.Query; +import org.hibernate.Session; +import org.hibernate.annotations.DynamicUpdate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.gtfs.gtfsStructs.GtfsFeedInfo; + +import javax.persistence.*; +import java.io.Serializable; +import java.text.DateFormat; +import java.text.ParseException; +import java.util.Date; +import java.util.List; +import java.util.Objects; + +/** + * Contains data from the transfers.txt GTFS file. This class is + * for reading/writing that data to the db. + * + * @author SkiBu Smith + * + */ +@Entity @DynamicUpdate @Table(name="FeedInfo") +public class FeedInfo implements Serializable { + + // Because Hibernate requires objects with composite Ids to be Serializable + private static final long serialVersionUID = 7068954782064701311L; + + @Column + @Id + private final int configRev; + + @Column(length=120) + private final String feedVersion; + + @Column + @Id + private final String feedPublisherName; + + @Column(length=512) + private final String feedPublisherUrl; + + @Column(length=15) + private final String feedLanguage; + + @Column @Temporal(TemporalType.DATE) + private final Date feedStartDate; + + @Column @Temporal(TemporalType.DATE) + private final Date feedEndDate; + + // Logging + public static final Logger logger = + LoggerFactory.getLogger(FeedInfo.class); + + /********************** Member Functions **************************/ + + /** + * Constructor + * + * @param configRev + * @param gtfsFeedInfo + */ + public FeedInfo(int configRev, GtfsFeedInfo gtfsFeedInfo, DateFormat dateFormat) { + this.configRev = configRev; + this.feedVersion = gtfsFeedInfo.getFeedVersion(); + this.feedPublisherName = gtfsFeedInfo.getFeedPublisherName(); + this.feedPublisherUrl = gtfsFeedInfo.getFeedPublisherUrl(); + this.feedLanguage = gtfsFeedInfo.getFeedLang(); + + + Date tempStartDate = null; + if (gtfsFeedInfo.getFeedStartDate() != null){ + try { + tempStartDate = dateFormat.parse(gtfsFeedInfo.getFeedStartDate()); + } catch (ParseException e) { + logger.error("Could not parse Feed Start Date \"{}\" from " + + "line #{} from file {}", + gtfsFeedInfo.getFeedStartDate(), + gtfsFeedInfo.getLineNumber(), + gtfsFeedInfo.getFileName()); + } + } + this.feedStartDate = tempStartDate; + + Date tempEndDate= null; + if(gtfsFeedInfo.getFeedEndDate() != null) { + try { + tempEndDate = dateFormat.parse(gtfsFeedInfo.getFeedEndDate()); + } catch (ParseException e) { + logger.error("Could not parse Feed End Date \"{}\" from " + + "line #{} from file {}", + gtfsFeedInfo.getFeedEndDate(), + gtfsFeedInfo.getLineNumber(), + gtfsFeedInfo.getFileName()); + } + } + this.feedEndDate = tempEndDate; + } + + /** + * Needed because no-arg constructor required by Hibernate + */ + @SuppressWarnings("unused") + private FeedInfo() { + this.configRev = -1; + this.feedVersion = null; + this.feedPublisherName = null; + this.feedPublisherUrl = null; + this.feedLanguage = null; + this.feedStartDate = null; + this.feedEndDate = null; + } + + /** + * Deletes rev 0 from the Transfers table + * + * @param session + * @param configRev + * @return Number of rows deleted + * @throws HibernateException + */ + public static int deleteFromRev(Session session, int configRev) + throws HibernateException { + // Note that hql uses class name, not the table name + String hql = "DELETE FeedInfo WHERE configRev=" + configRev; + int numUpdates = session.createQuery(hql).executeUpdate(); + return numUpdates; + } + + /** + * Returns List of Transfer objects for the specified database revision. + * + * @param session + * @param configRev + * @return + * @throws HibernateException + */ + @SuppressWarnings("unchecked") + public static List getFeedInfo(Session session, int configRev) + throws HibernateException { + String hql = "FROM FeedInfo " + + " WHERE configRev = :configRev"; + Query query = session.createQuery(hql); + query.setInteger("configRev", configRev); + return query.list(); + } + + public int getConfigRev() { + return configRev; + } + + public String getFeedVersion() { + return feedVersion; + } + + public String getFeedPublisherName() { + return feedPublisherName; + } + + public String getFeedPublisherUrl() { + return feedPublisherUrl; + } + + public String getFeedLanguage() { + return feedLanguage; + } + + public Date getFeedStartDate() { + return feedStartDate; + } + + public Date getFeedEndDate() { + return feedEndDate; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + FeedInfo feedInfo = (FeedInfo) o; + return configRev == feedInfo.configRev && + Objects.equals(feedVersion, feedInfo.feedVersion) && + feedPublisherName.equals(feedInfo.feedPublisherName) && + feedPublisherUrl.equals(feedInfo.feedPublisherUrl) && + feedLanguage.equals(feedInfo.feedLanguage) && + Objects.equals(feedStartDate, feedInfo.feedStartDate) && + Objects.equals(feedEndDate, feedInfo.feedEndDate); + } + + @Override + public int hashCode() { + return Objects.hash(configRev, feedVersion, feedPublisherName, feedPublisherUrl, feedLanguage, feedStartDate, feedEndDate); + } +} diff --git a/transitime/src/main/java/org/transitime/db/structs/Frequency.java b/transitclock/src/main/java/org/transitclock/db/structs/Frequency.java similarity index 97% rename from transitime/src/main/java/org/transitime/db/structs/Frequency.java rename to transitclock/src/main/java/org/transitclock/db/structs/Frequency.java index b87fe0645..bef660ee4 100644 --- a/transitime/src/main/java/org/transitime/db/structs/Frequency.java +++ b/transitclock/src/main/java/org/transitclock/db/structs/Frequency.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.db.structs; +package org.transitclock.db.structs; import java.io.Serializable; import java.util.List; @@ -28,8 +28,8 @@ import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.annotations.DynamicUpdate; -import org.transitime.db.hibernate.HibernateUtils; -import org.transitime.gtfs.gtfsStructs.GtfsFrequency; +import org.transitclock.db.hibernate.HibernateUtils; +import org.transitclock.gtfs.gtfsStructs.GtfsFrequency; /** diff --git a/transitclock/src/main/java/org/transitclock/db/structs/Headway.java b/transitclock/src/main/java/org/transitclock/db/structs/Headway.java new file mode 100755 index 000000000..291ae907f --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/db/structs/Headway.java @@ -0,0 +1,298 @@ +package org.transitclock.db.structs; + +import java.io.Serializable; +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Index; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; + +import org.hibernate.annotations.DynamicUpdate; +import org.transitclock.applications.Core; +import org.transitclock.db.hibernate.HibernateUtils; +@Entity @DynamicUpdate +@Table(name="Headway", + indexes = { @Index(name="HeadwayIndex", + columnList="creationTime" ) } ) +public class Headway implements Serializable { + public Headway() { + } + + /** + * + */ + private static final long serialVersionUID = -4561111910398287801L; + + @Id + @GeneratedValue(strategy=GenerationType.AUTO) + private long id; + + // The revision of the configuration data that was being used + @Column + private int configRev; + + @Column + private double headway; + + @Column + private Double scheduledHeadway; + + @Column + private double average; + + @Column + private double variance; + + @Column + private double coefficientOfVariation; + + @Column + private int numVehicles; + + // The time the AVL data was processed and the headway was created. + @Column + @Temporal(TemporalType.TIMESTAMP) + private Date creationTime; + + @Column(length=HibernateUtils.DEFAULT_ID_SIZE) + private String vehicleId; + + @Column(length=HibernateUtils.DEFAULT_ID_SIZE) + private String otherVehicleId; + + @Column(length=HibernateUtils.DEFAULT_ID_SIZE) + private String stopId; + + @Column(length=HibernateUtils.DEFAULT_ID_SIZE) + private String tripId; + + @Column(length=HibernateUtils.DEFAULT_ID_SIZE) + private String routeId; + + @Column + @Temporal(TemporalType.TIMESTAMP) + private Date firstDeparture; + + @Column + @Temporal(TemporalType.TIMESTAMP) + private Date secondDeparture; + + + + + public Headway(long headway, Long scheduledHeadway, Date creationTime, String vehicleId, String otherVehicleId, String stopId, String tripId, + String routeId, Date firstDeparture, Date secondDeparture, Date avlTime) { + + this.configRev = Core.getInstance().getDbConfig().getConfigRev(); + this.headway = headway; + this.scheduledHeadway = scheduledHeadway !=null ? scheduledHeadway.doubleValue() : null; + this.creationTime = creationTime; + this.vehicleId = vehicleId; + this.stopId = stopId; + this.tripId = tripId; + this.routeId = routeId; + this.average=0; + this.variance=0; + this.coefficientOfVariation=0; + this.numVehicles = 0; + this.otherVehicleId=otherVehicleId; + this.firstDeparture=firstDeparture; + this.secondDeparture=secondDeparture; + } + + public String getOtherVehicleId() { + return otherVehicleId; + } + + public Date getFirstDeparture() { + return firstDeparture; + } + + public Date getSecondDeparture() { + return secondDeparture; + } + + public static long getSerialversionuid() { + return serialVersionUID; + } + + public long getId() { + return id; + } + + public int getConfigRev() { + return configRev; + } + + public Date getCreationTime() { + return creationTime; + } + + public String getVehicleId() { + return vehicleId; + } + + public String getStopId() { + return stopId; + } + + public String getTripId() { + return tripId; + } + + public String getRouteId() { + return routeId; + } + + public int getNumVehicles() { + return numVehicles; + } + + public void setNumVehicles(int numVehicles) { + this.numVehicles = numVehicles; + } + public double getAverage() { + return average; + } + + public void setAverage(double average) { + this.average = average; + } + + public double getVariance() { + return variance; + } + + public void setVariance(double variance) { + this.variance = variance; + } + + public double getCoefficientOfVariation() { + return coefficientOfVariation; + } + + public void setCoefficientOfVariation(double coefficientOfVariation) { + this.coefficientOfVariation = coefficientOfVariation; + } + + public double getHeadway() { + return headway; + } + public void setConfigRev(int configRev) { + this.configRev = configRev; + } + + public void setHeadway(double headway) { + this.headway = headway; + } + + public void setCreationTime(Date creationTime) { + this.creationTime = creationTime; + } + + public void setVehicleId(String vehicleId) { + this.vehicleId = vehicleId; + } + + public void setOtherVehicleId(String otherVehicleId) { + this.otherVehicleId = otherVehicleId; + } + + public void setStopId(String stopId) { + this.stopId = stopId; + } + + public void setTripId(String tripId) { + this.tripId = tripId; + } + + public void setRouteId(String routeId) { + this.routeId = routeId; + } + + public void setFirstDeparture(Date firstDeparture) { + this.firstDeparture = firstDeparture; + } + + public void setSecondDeparture(Date secondDeparture) { + this.secondDeparture = secondDeparture; + } + + + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + long headwayTemp = Double.doubleToLongBits(headway); + long scheduledHeadwayTemp = Double.doubleToLongBits(scheduledHeadway); + result = prime * result + (int) (headwayTemp ^ (headwayTemp >>> 32)); + result = prime * result + (int) (scheduledHeadwayTemp ^ (scheduledHeadwayTemp >>> 32)); + result = prime * result + ((otherVehicleId == null) ? 0 : otherVehicleId.hashCode()); + result = prime * result + ((routeId == null) ? 0 : routeId.hashCode()); + result = prime * result + ((stopId == null) ? 0 : stopId.hashCode()); + result = prime * result + ((tripId == null) ? 0 : tripId.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; + Headway other = (Headway) obj; + if (Double.doubleToLongBits(headway) != Double.doubleToLongBits(other.headway)) + return false; + if (Double.doubleToLongBits(scheduledHeadway) != Double.doubleToLongBits(other.scheduledHeadway)) + return false; + if (otherVehicleId == null) { + if (other.otherVehicleId != null) + return false; + } else if (!otherVehicleId.equals(other.otherVehicleId)) + return false; + if (routeId == null) { + if (other.routeId != null) + return false; + } else if (!routeId.equals(other.routeId)) + return false; + if (stopId == null) { + if (other.stopId != null) + return false; + } else if (!stopId.equals(other.stopId)) + return false; + if (tripId == null) { + if (other.tripId != null) + return false; + } else if (!tripId.equals(other.tripId)) + return false; + if (vehicleId == null) { + if (other.vehicleId != null) + return false; + } else if (!vehicleId.equals(other.vehicleId)) + return false; + return true; + } + + @Override + public String toString() { + return "Headway [id=" + id + ", configRev=" + configRev + ", headway=" + headway + ", scheduledHeadway=" + scheduledHeadway + + ", average=" + average + ", variance=" + variance + ", coefficientOfVariation=" + + coefficientOfVariation + ", numVehicles=" + numVehicles + ", creationTime=" + creationTime + + ", vehicleId=" + vehicleId + ", otherVehicleId=" + otherVehicleId + ", stopId=" + stopId + + ", tripId=" + tripId + ", routeId=" + routeId + ", firstDeparture=" + firstDeparture + + ", secondDeparture=" + secondDeparture + "]"; + } + + + +} diff --git a/transitclock/src/main/java/org/transitclock/db/structs/HoldingTime.java b/transitclock/src/main/java/org/transitclock/db/structs/HoldingTime.java new file mode 100755 index 000000000..a8969c742 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/db/structs/HoldingTime.java @@ -0,0 +1,277 @@ +package org.transitclock.db.structs; + +import java.io.Serializable; +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Index; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; +import javax.persistence.Transient; + +import org.hibernate.annotations.DynamicUpdate; +import org.transitclock.applications.Core; +import org.transitclock.db.hibernate.HibernateUtils; +@Entity @DynamicUpdate +@Table(name="HoldingTimes", + indexes = { @Index(name="HoldingTimeIndex", + columnList="creationTime" ) } ) + +/** + * For persisting a holding time recommendation. + * + * @author Sean Óg Crudden + * + */ +public class HoldingTime implements Serializable { + + @Override + public String toString() { + return "HoldingTime [id=" + id + ", configRev=" + configRev + ", holdingTime=" + holdingTime + ", creationTime=" + + creationTime + ", vehicleId=" + vehicleId + ", stopId=" + stopId + ", tripId=" + tripId + ", routeId=" + + routeId + ", arrivalTime=" + arrivalTime + ", arrivalPredictionUsed=" + arrivalPredictionUsed + + ", arrivalUsed=" + arrivalUsed + ", hasD1=" + hasD1 + ", numberPredictionsUsed=" + + numberPredictionsUsed + "]"; + } + + public int getNumberPredictionsUsed() { + return numberPredictionsUsed; + } + + public void setNumberPredictionsUsed(int numberPredictionsUsed) { + this.numberPredictionsUsed = numberPredictionsUsed; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (arrivalPredictionUsed ? 1231 : 1237); + result = prime * result + ((arrivalTime == null) ? 0 : arrivalTime.hashCode()); + result = prime * result + (arrivalUsed ? 1231 : 1237); + result = prime * result + configRev; + result = prime * result + ((creationTime == null) ? 0 : creationTime.hashCode()); + result = prime * result + ((holdingTime == null) ? 0 : holdingTime.hashCode()); + result = prime * result + ((routeId == null) ? 0 : routeId.hashCode()); + result = prime * result + ((stopId == null) ? 0 : stopId.hashCode()); + result = prime * result + ((tripId == null) ? 0 : tripId.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; + HoldingTime other = (HoldingTime) obj; + if (arrivalPredictionUsed != other.arrivalPredictionUsed) + return false; + if (arrivalTime == null) { + if (other.arrivalTime != null) + return false; + } else if (!arrivalTime.equals(other.arrivalTime)) + return false; + if (arrivalUsed != other.arrivalUsed) + return false; + if (configRev != other.configRev) + return false; + if (creationTime == null) { + if (other.creationTime != null) + return false; + } else if (!creationTime.equals(other.creationTime)) + return false; + if (holdingTime == null) { + if (other.holdingTime != null) + return false; + } else if (!holdingTime.equals(other.holdingTime)) + return false; + if (routeId == null) { + if (other.routeId != null) + return false; + } else if (!routeId.equals(other.routeId)) + return false; + if (stopId == null) { + if (other.stopId != null) + return false; + } else if (!stopId.equals(other.stopId)) + return false; + if (tripId == null) { + if (other.tripId != null) + return false; + } else if (!tripId.equals(other.tripId)) + return false; + if (vehicleId == null) { + if (other.vehicleId != null) + return false; + } else if (!vehicleId.equals(other.vehicleId)) + return false; + return true; + } + + private static final long serialVersionUID = -8000018800462712153L; + + @Id + @GeneratedValue(strategy=GenerationType.AUTO) + private long id; + + // The revision of the configuration data that was being used + @Column + private final int configRev; + + @Column + @Temporal(TemporalType.TIMESTAMP) + private final Date holdingTime; + + // The time the AVL data was processed and the prediction was created. + @Column + @Temporal(TemporalType.TIMESTAMP) + private final Date creationTime; + + @Column(length=HibernateUtils.DEFAULT_ID_SIZE) + private final String vehicleId; + + @Column(length=HibernateUtils.DEFAULT_ID_SIZE) + private final String stopId; + + @Column(length=HibernateUtils.DEFAULT_ID_SIZE) + private final String tripId; + + @Column(length=HibernateUtils.DEFAULT_ID_SIZE) + private final String routeId; + + @Column + @Temporal(TemporalType.TIMESTAMP) + private final Date arrivalTime; + + public Date getArrivalTime() { + return arrivalTime; + } + + @Column + private boolean arrivalPredictionUsed; + + @Column + private boolean arrivalUsed; + + @Column + private boolean hasD1; + + @Column + int numberPredictionsUsed; + + + public boolean isHasD1() { + return hasD1; + } + + public void setHasD1(boolean hasD1) { + this.hasD1 = hasD1; + } + + public Date getHoldingTime() { + return holdingTime; + } + + public Date getCreationTime() { + return creationTime; + } + + public String getVehicleId() { + return vehicleId; + } + + public String getStopId() { + return stopId; + } + + public String getTripId() { + return tripId; + } + + public String getRouteId() { + return routeId; + } + + public boolean isArrivalPredictionUsed() { + return arrivalPredictionUsed; + } + + public boolean isArrivalUsed() { + return arrivalUsed; + } + public HoldingTime() { + this.configRev = -1; + this.holdingTime = null; + this.creationTime = null; + this.vehicleId = null; + this.stopId = null; + this.tripId = null; + this.routeId = null; + arrivalPredictionUsed=false; + arrivalUsed=false; + arrivalTime = null; + hasD1 = false; + numberPredictionsUsed=-1; + } + + public HoldingTime(Date holdingTime, Date creationTime, String vehicleId, String stopId, String tripId, + String routeId, boolean arrivalPredictionUsed, boolean arrivalUsed, Date arrivalTime, boolean hasD1, int numberPredictionsUsed) { + this.configRev = Core.getInstance().getDbConfig().getConfigRev(); + this.holdingTime = holdingTime; + this.creationTime = creationTime; + this.vehicleId = vehicleId; + this.stopId = stopId; + this.tripId = tripId; + this.routeId = routeId; + this.arrivalPredictionUsed=arrivalPredictionUsed; + this.arrivalTime=arrivalTime; + this.arrivalUsed=arrivalUsed; + this.hasD1=hasD1; + this.numberPredictionsUsed=numberPredictionsUsed; + + } + public HoldingTime(HoldingTime holdingTimeToCopy, Date holdingTime) { + this.configRev = Core.getInstance().getDbConfig().getConfigRev(); + this.holdingTime =holdingTime; + this.creationTime = holdingTimeToCopy.creationTime; + this.vehicleId = holdingTimeToCopy.vehicleId; + this.stopId = holdingTimeToCopy.stopId; + this.tripId = holdingTimeToCopy.tripId; + this.routeId = holdingTimeToCopy.routeId; + this.arrivalPredictionUsed=holdingTimeToCopy.arrivalPredictionUsed; + this.arrivalTime=holdingTimeToCopy.arrivalTime; + this.arrivalUsed=holdingTimeToCopy.arrivalUsed; + this.hasD1=holdingTimeToCopy.hasD1; + this.numberPredictionsUsed=holdingTimeToCopy.numberPredictionsUsed; + } + public Date getTimeToLeave(Date currentTime) + { + if(currentTime.after(holdingTime)) + { + return currentTime; + }else + { + return holdingTime; + } + } + public boolean leaveStop(Date currentTime) + { + if(holdingTime.before(currentTime)) + { + return true; + }else + { + return false; + } + } + +} diff --git a/transitime/src/main/java/org/transitime/db/structs/Location.java b/transitclock/src/main/java/org/transitclock/db/structs/Location.java similarity index 98% rename from transitime/src/main/java/org/transitime/db/structs/Location.java rename to transitclock/src/main/java/org/transitclock/db/structs/Location.java index 16b15cdd6..393603dae 100644 --- a/transitime/src/main/java/org/transitime/db/structs/Location.java +++ b/transitclock/src/main/java/org/transitclock/db/structs/Location.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.db.structs; +package org.transitclock.db.structs; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.Embeddable; -import org.transitime.utils.Geo; +import org.transitclock.utils.Geo; import net.jcip.annotations.Immutable; diff --git a/transitime/src/main/java/org/transitime/db/structs/Match.java b/transitclock/src/main/java/org/transitclock/db/structs/Match.java similarity index 87% rename from transitime/src/main/java/org/transitime/db/structs/Match.java rename to transitclock/src/main/java/org/transitclock/db/structs/Match.java index ad76da863..590781d0f 100644 --- a/transitime/src/main/java/org/transitime/db/structs/Match.java +++ b/transitclock/src/main/java/org/transitclock/db/structs/Match.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.db.structs; +package org.transitclock.db.structs; import java.io.Serializable; import java.util.Date; @@ -38,12 +38,12 @@ import org.hibernate.classic.Lifecycle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.applications.Core; -import org.transitime.core.TemporalMatch; -import org.transitime.core.VehicleState; -import org.transitime.db.hibernate.HibernateUtils; -import org.transitime.utils.Geo; -import org.transitime.utils.IntervalTimer; +import org.transitclock.applications.Core; +import org.transitclock.core.TemporalMatch; +import org.transitclock.core.VehicleState; +import org.transitclock.db.hibernate.HibernateUtils; +import org.transitclock.utils.Geo; +import org.transitclock.utils.IntervalTimer; /** * For persisting the match for the vehicle. This data is later used @@ -302,17 +302,17 @@ public String toString() { public static List getMatchesFromDb( String projectId, Date beginTime, Date endTime, String sqlClause, - final int firstResult, final int maxResults) { + final Integer firstResult, final Integer maxResults) { IntervalTimer timer = new IntervalTimer(); // Get the database session. This is supposed to be pretty light weight - Session session = HibernateUtils.getSession(projectId); + Session session = HibernateUtils.getSession(projectId, false); // Create the query. Table name is case sensitive and needs to be the // class name instead of the name of the db table. String hql = "FROM Match " + - " WHERE avlTime >= :beginDate " + - " AND avlTime < :endDate"; + " WHERE avlTime between :beginDate " + + " AND :endDate"; if (sqlClause != null) hql += " " + sqlClause; Query query = session.createQuery(hql); @@ -321,9 +321,13 @@ public static List getMatchesFromDb( query.setTimestamp("beginDate", beginTime); query.setTimestamp("endDate", endTime); + if (firstResult != null) { // Only get a batch of data at a time - query.setFirstResult(firstResult); - query.setMaxResults(maxResults); + query.setFirstResult(firstResult); + } + if (maxResults != null) { + query.setMaxResults(maxResults); + } try { @SuppressWarnings("unchecked") @@ -342,7 +346,46 @@ public static List getMatchesFromDb( } } + public static Long getMatchesCountFromDb( + String projectId, Date beginTime, Date endTime, + String sqlClause) { + IntervalTimer timer = new IntervalTimer(); + + // Get the database session. This is supposed to be pretty light weight + Session session = HibernateUtils.getSession(projectId, false); + + // Create the query. Table name is case sensitive and needs to be the + // class name instead of the name of the db table. + String hql = "Select count(*) FROM Match " + + " WHERE avlTime >= :beginDate " + + " AND avlTime < :endDate"; + if (sqlClause != null) + hql += " " + sqlClause; + Query query = session.createQuery(hql); + + // Set the parameters for the query + query.setTimestamp("beginDate", beginTime); + query.setTimestamp("endDate", endTime); + + Long count = null; + + try { + count = (Long) query.uniqueResult(); + logger.debug("Getting matches from database took {} msec", + timer.elapsedMsec()); + return count; + } catch (HibernateException e) { + // Log error to the Core logger + Core.getLogger().error(e.getMessage(), e); + return null; + } finally { + // Clean things up. Not sure if this absolutely needed nor if + // it might actually be detrimental and slow things down. + session.close(); + } + } + public String getVehicleId() { return vehicleId; } diff --git a/transitime/src/main/java/org/transitime/db/structs/MeasuredArrivalTime.java b/transitclock/src/main/java/org/transitclock/db/structs/MeasuredArrivalTime.java similarity index 94% rename from transitime/src/main/java/org/transitime/db/structs/MeasuredArrivalTime.java rename to transitclock/src/main/java/org/transitclock/db/structs/MeasuredArrivalTime.java index 2b2195432..c4770c8ce 100644 --- a/transitime/src/main/java/org/transitime/db/structs/MeasuredArrivalTime.java +++ b/transitclock/src/main/java/org/transitclock/db/structs/MeasuredArrivalTime.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.db.structs; +package org.transitclock.db.structs; import java.io.Serializable; import java.util.Date; @@ -28,7 +28,7 @@ import javax.persistence.TemporalType; import org.hibernate.annotations.DynamicUpdate; -import org.transitime.db.hibernate.HibernateUtils; +import org.transitclock.db.hibernate.HibernateUtils; /** * For storing a measured arrival time so that can see if measured arrival time diff --git a/transitime/src/main/java/org/transitime/db/structs/MonitoringEvent.java b/transitclock/src/main/java/org/transitclock/db/structs/MonitoringEvent.java similarity index 97% rename from transitime/src/main/java/org/transitime/db/structs/MonitoringEvent.java rename to transitclock/src/main/java/org/transitclock/db/structs/MonitoringEvent.java index e7a89388e..061248a56 100644 --- a/transitime/src/main/java/org/transitime/db/structs/MonitoringEvent.java +++ b/transitclock/src/main/java/org/transitclock/db/structs/MonitoringEvent.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.db.structs; +package org.transitclock.db.structs; import java.io.Serializable; import java.util.Date; @@ -37,9 +37,9 @@ import org.hibernate.annotations.DynamicUpdate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.applications.Core; -import org.transitime.db.hibernate.HibernateUtils; -import org.transitime.utils.IntervalTimer; +import org.transitclock.applications.Core; +import org.transitclock.db.hibernate.HibernateUtils; +import org.transitclock.utils.IntervalTimer; /** * For storing monitoring events into database. By storing the monitoring diff --git a/transitime/src/main/java/org/transitime/db/structs/Prediction.java b/transitclock/src/main/java/org/transitclock/db/structs/Prediction.java similarity index 94% rename from transitime/src/main/java/org/transitime/db/structs/Prediction.java rename to transitclock/src/main/java/org/transitclock/db/structs/Prediction.java index 7c6a6cc5b..3ef082bc6 100644 --- a/transitime/src/main/java/org/transitime/db/structs/Prediction.java +++ b/transitclock/src/main/java/org/transitclock/db/structs/Prediction.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.db.structs; +package org.transitclock.db.structs; import java.io.Serializable; import java.util.Date; @@ -31,9 +31,12 @@ import javax.persistence.TemporalType; import org.hibernate.annotations.DynamicUpdate; -import org.transitime.applications.Core; -import org.transitime.db.hibernate.HibernateUtils; -import org.transitime.ipc.data.IpcPrediction; +import org.transitclock.applications.Core; +import org.transitclock.db.hibernate.HibernateUtils; +import org.transitclock.ipc.data.IpcPrediction; + +import java.io.Serializable; +import java.util.Date; /** * For persisting a prediction. @@ -92,6 +95,9 @@ public class Prediction implements Serializable { @Column private final boolean schedBasedPred; + + @Column + private final int gtfsStopSeq; // Needed because Hibernate objects must be serializable private static final long serialVersionUID = 3966430062434375435L; @@ -113,7 +119,8 @@ public class Prediction implements Serializable { */ public Prediction(long predictionTime, long avlTime, long creationTime, String vehicleId, String stopId, String tripId, String routeId, - boolean affectedByWaitStop, boolean isArrival, boolean schedBasedPred) { + boolean affectedByWaitStop, boolean isArrival, boolean schedBasedPred, + int gtfsStopSeq) { this.configRev = Core.getInstance().getDbConfig().getConfigRev(); this.predictionTime = new Date(predictionTime); this.avlTime = new Date(avlTime); @@ -125,6 +132,7 @@ public Prediction(long predictionTime, long avlTime, long creationTime, this.affectedByWaitStop = affectedByWaitStop; this.isArrival = isArrival; this.schedBasedPred = schedBasedPred; + this.gtfsStopSeq = gtfsStopSeq; } public Prediction(IpcPrediction prediction) { @@ -139,6 +147,7 @@ public Prediction(IpcPrediction prediction) { this.affectedByWaitStop = prediction.isAffectedByWaitStop(); this.isArrival = prediction.isArrival(); this.schedBasedPred = prediction.isSchedBasedPred(); + this.gtfsStopSeq = prediction.getGtfsStopSeq(); } /** @@ -157,6 +166,7 @@ protected Prediction() { this.affectedByWaitStop = false; this.isArrival = false; this.schedBasedPred= false; + this.gtfsStopSeq = -1; } /** @@ -300,4 +310,8 @@ public boolean isArrival() { public boolean isSchedBasedPred() { return schedBasedPred; } + + public int getGtfsStopSeq() { + return gtfsStopSeq; + } } diff --git a/transitime/src/main/java/org/transitime/db/structs/PredictionAccuracy.java b/transitclock/src/main/java/org/transitclock/db/structs/PredictionAccuracy.java similarity index 93% rename from transitime/src/main/java/org/transitime/db/structs/PredictionAccuracy.java rename to transitclock/src/main/java/org/transitclock/db/structs/PredictionAccuracy.java index 66d5a67b7..6dbf56425 100644 --- a/transitime/src/main/java/org/transitime/db/structs/PredictionAccuracy.java +++ b/transitclock/src/main/java/org/transitclock/db/structs/PredictionAccuracy.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.db.structs; +package org.transitclock.db.structs; import java.io.Serializable; import java.util.Date; @@ -29,13 +29,14 @@ import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; +import javax.persistence.Transient; import org.hibernate.CallbackException; import org.hibernate.Session; import org.hibernate.annotations.DynamicUpdate; import org.hibernate.classic.Lifecycle; -import org.transitime.applications.Core; -import org.transitime.db.hibernate.HibernateUtils; +import org.transitclock.applications.Core; +import org.transitclock.db.hibernate.HibernateUtils; /** * A database object for persisting information on how accurate a prediction was @@ -115,6 +116,11 @@ public class PredictionAccuracy implements Lifecycle, Serializable { @Column(length=HibernateUtils.DEFAULT_ID_SIZE) private String predictionSource; + /* TODO */ + //@Column(length=HibernateUtils.DEFAULT_ID_SIZE) + @Transient + private String predictionAlgorithm; + @Column(length=HibernateUtils.DEFAULT_ID_SIZE) private String vehicleId; @@ -137,12 +143,13 @@ public class PredictionAccuracy implements Lifecycle, Serializable { * The time the vehicle was predicted to arrive at the stop * @param predictionReadTime * @param predictionSource + * @param predictionAlgorithm * @param vehicleId */ public PredictionAccuracy(String routeId, String directionId, String stopId, String tripId, Date arrivalDepartureTime, Date predictedTime, Date predictionReadTime, - String predictionSource, String vehicleId, + String predictionSource,String predictionAlgorithm, String vehicleId, Boolean affectedByWaitStop) { super(); this.routeId = routeId; @@ -160,6 +167,11 @@ public PredictionAccuracy(String routeId, String directionId, this.predictionSource = predictionSource; this.vehicleId = vehicleId; this.affectedByWaitStop = affectedByWaitStop; + this.predictionAlgorithm=predictionAlgorithm; + } + + public String getPredictionAlgorithm() { + return predictionAlgorithm; } /** @@ -180,6 +192,7 @@ protected PredictionAccuracy() { this.predictionSource = null; this.vehicleId = null; this.affectedByWaitStop = null; + this.predictionAlgorithm = null; } @Override @@ -273,6 +286,11 @@ public boolean equals(Object obj) { return false; } else if (!predictionSource.equals(other.predictionSource)) return false; + if (predictionAlgorithm == null) { + if (other.predictionAlgorithm != null) + return false; + } else if (!predictionAlgorithm.equals(other.predictionAlgorithm)) + return false; if (routeId == null) { if (other.routeId != null) return false; @@ -315,6 +333,7 @@ public String toString() { + ", predictionLengthMsecs=" + getPredictionLengthMsecs() + ", predictionAccuracyMsecs=" + predictionAccuracyMsecs + ", predictionSource=" + predictionSource + + ", predictionAlgorithm=" + predictionAlgorithm + ", vehicleId=" + vehicleId + ", affectedByWaitStop=" + affectedByWaitStop + "]"; @@ -380,7 +399,7 @@ public Boolean isAffectedByWaitStop() { /** * Callback due to implementing Lifecycle interface. Used to compact - * string members by interning them. + * string members by them. */ @Override public void onLoad(Session s, Serializable id) throws CallbackException { @@ -396,6 +415,8 @@ public void onLoad(Session s, Serializable id) throws CallbackException { tripId = tripId.intern(); if (predictionSource != null) predictionSource = predictionSource.intern(); + if (predictionAlgorithm != null) + predictionAlgorithm = predictionAlgorithm.intern(); if (vehicleId != null) vehicleId = vehicleId.intern(); } diff --git a/transitclock/src/main/java/org/transitclock/db/structs/PredictionEvent.java b/transitclock/src/main/java/org/transitclock/db/structs/PredictionEvent.java new file mode 100644 index 000000000..c988dd9f5 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/db/structs/PredictionEvent.java @@ -0,0 +1,502 @@ +/* + * 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.db.structs; + +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +import javax.persistence.Column; +import javax.persistence.Embedded; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Index; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; + +import net.jcip.annotations.Immutable; + +import org.hibernate.HibernateException; +import org.hibernate.Query; +import org.hibernate.Session; +import org.hibernate.annotations.DynamicUpdate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.applications.Core; +import org.transitclock.core.TemporalMatch; +import org.transitclock.db.hibernate.HibernateUtils; +import org.transitclock.utils.IntervalTimer; +import org.transitclock.utils.Time; + +/** + * For storing events associated with predictions into log file and into database. + * The resulting information can be mapped out for finding + * problems in prediction methods. Based on VehicleEvents class. + * + * @author SkiBu Smith, Sean Óg Crudden + * + */ +@Immutable // From jcip.annoations +@Entity @DynamicUpdate +@Table(name="PredictionEvents", + indexes = { @Index(name="PredictionEventsTimeIndex", + columnList="time" ) } ) +public class PredictionEvent implements Serializable { + + // System time of the event. + @Id + @Column + @Temporal(TemporalType.TIMESTAMP) + private final Date time; + + // Important for understanding context of issue + @Id + @Column(length=HibernateUtils.DEFAULT_ID_SIZE) + private final String vehicleId; + + // Short descriptor of event. Not using an enumerator because don't + // want to have to change this code every time a new event type is + // created. It is an @Id because several events for a vehicle might + // happen with the same exact timestamp. + @Id + @Column(length=HibernateUtils.DEFAULT_ID_SIZE) + private final String eventType; + + // AVL time of the event. Should correspond to last AVL report time so that + // can join with AVL report to get more info if necessary. + @Column + @Temporal(TemporalType.TIMESTAMP) + private final Date avlTime; + + // A more verbose textual description of the event + private final static int MAX_DESCRIPTION_LENGTH = 500; + @Column(length=MAX_DESCRIPTION_LENGTH) + private final String description; + + + // Latitude/longitude of vehicle when event occurred. Though this could + // be obtained by joining with corresponding AvlReport from db having + // the lat/lon here makes it much easier to do things like display + // events on a map using only a simple query. + @Embedded + private final Location location; + + // Nice for providing context. Allows for query so can see all events + // for a route. + @Column(length=HibernateUtils.DEFAULT_ID_SIZE) + private final String routeId; + + // Nice for providing context. + // routeShortName is included because for some agencies the + // route_id changes when there are schedule updates. But the + // routeShortName is more likely to stay consistent. Therefore + // it is better for when querying for arrival/departure data + // over a timespan. + @Column(length=HibernateUtils.DEFAULT_ID_SIZE) + private final String routeShortName; + + // Nice for providing context. + @Column(length=HibernateUtils.DEFAULT_ID_SIZE) + private final String blockId; + + // Nice for providing context. + @Column(length=HibernateUtils.DEFAULT_ID_SIZE) + private final String serviceId; + + // Nice for providing context. + @Column(length=HibernateUtils.DEFAULT_ID_SIZE) + private final String tripId; + + // Nice for providing context. + @Column(length=HibernateUtils.DEFAULT_ID_SIZE) + private final String stopId; + + @Column(length=HibernateUtils.DEFAULT_ID_SIZE) + private final String arrivalstopid; + + @Column(length=HibernateUtils.DEFAULT_ID_SIZE) + private final String departurestopid; + + @Column(length=HibernateUtils.DEFAULT_ID_SIZE) + private final String referenceVehicleId; + + @Column + @Temporal(TemporalType.TIMESTAMP) + private final Date arrivalTime; + + @Column + @Temporal(TemporalType.TIMESTAMP) + private final Date departureTime; + + // Some standard prediciton event types + public static final String PREDICTION_VARIATION = "Prediction variation"; + public static final String TRAVELTIME_EXCEPTION = "Travel time exception"; + + // Hibernate requires class to be Serializable + private static final long serialVersionUID = -763445348557811925L; + + private static final Logger logger = + LoggerFactory.getLogger(PredictionEvent.class); + + + + /********************** Member Functions **************************/ + + /** + * Simple constructor. Declared private because should be only accessed by + * the create() method so that can make sure that do things like log each + * creation of a predictionEvent. + * + * @param time + * @param avlTime + * @param vehicleId + * @param eventType + * @param description + * @param predictable + * @param becameUnpredictable + * @param supervisor + * @param location + * @param routeId + * @param routeShortName + * @param blockId + * @param serviceId + * @param tripId + * @param stopId + */ + private PredictionEvent(Date time, Date avlTime, String vehicleId, + String eventType, String description, + Location location, + String routeId, String routeShortName, String blockId, + String serviceId, String tripId, String stopId, String arrivalStopId, String departureStopId, String referenceVehicleId, Date arrivalTime, Date departureTime) { + super(); + this.time = time; + this.avlTime = avlTime; + this.vehicleId = vehicleId; + this.eventType = eventType; + this.description = description.length() <= MAX_DESCRIPTION_LENGTH ? + description : description.substring(0, MAX_DESCRIPTION_LENGTH); + this.location = location; + this.routeId = routeId; + this.routeShortName = routeShortName; + this.blockId = blockId; + this.serviceId = serviceId; + this.tripId = tripId; + this.stopId = stopId; + this.arrivalstopid = arrivalStopId; + this.departurestopid = departureStopId; + this.referenceVehicleId = referenceVehicleId; + this.arrivalTime = arrivalTime; + this.departureTime= departureTime; + + } + + /** + * Constructs a predictionEvent event and logs it and queues it to be stored in + * database. + * + * @param time + * @param avlTime + * @param vehicleId + * @param eventType + * @param description + * @param predictable + * @param becameUnpredictable + * @param supervisor + * @param location + * @param routeId + * @param routeShortName + * @param blockId + * @param serviceId + * @param tripId + * @param stopId + * @return The predictionEvent constructed + */ + public static PredictionEvent create(Date time, Date avlTime, + String vehicleId, String eventType, String description, + Location location, String routeId, + String routeShortName, String blockId, String serviceId, + String tripId, String stopId, String arrivalStopId, String departureStopId, String referenceVehicleId, Date arrivalTime, Date departureTime) { + PredictionEvent predictionEvent = + new PredictionEvent(time, avlTime, vehicleId, eventType, + description, location, routeId, routeShortName, blockId, + serviceId, tripId, stopId, arrivalStopId, departureStopId, referenceVehicleId, arrivalTime, departureTime); + + // Log predictionEvent in log file + logger.info(predictionEvent.toString()); + + // Queue to write object to database + Core.getInstance().getDbLogger().add(predictionEvent); + + // Return new predictionEvent + return predictionEvent; + } + + /** + * A simpler way to create a VehicleEvent that gets a lot of its info from + * the avlReport and match params. This also logs it and queues it to be + * stored in database. The match param can be null. + * + * @param avlReport + * @param match + * @param eventType + * @param description + * @param predictable + * @param becameUnpredictable + * @param supervisor + * @return The VehicleEvent constructed + */ + public static PredictionEvent create(AvlReport avlReport, TemporalMatch match, + String eventType, String description, String arrivalStopId, String departureStopId , String referenceVehicleId, Date arrivalTime, Date departureTime) { + // Get a log of the info from the possibly null match param + String routeId = match==null ? null : match.getTrip().getRouteId(); + String routeShortName = + match==null ? null : match.getTrip().getRouteShortName(); + String blockId = match==null ? null : match.getBlock().getId(); + String serviceId = match==null ? null : match.getBlock().getServiceId(); + String tripId = match==null ? null : match.getTrip().getId(); + String stopId = match==null ? null : match.getStopPath().getStopId(); + + // Create and return the VehicleEvent + return create(Core.getInstance().getSystemDate(), avlReport.getDate(), + avlReport.getVehicleId(), eventType, description, avlReport.getLocation(), + routeId, routeShortName, blockId, serviceId, tripId, stopId, arrivalStopId, departureStopId, referenceVehicleId, arrivalTime, departureTime); + } + + /** + * Hibernate requires a no-args constructor for reading data. + * So this is an experiment to see what can be done to satisfy + * Hibernate but still have an object be immutable. Since + * this constructor is only intended to be used by Hibernate + * is is declared protected, since that still works. That way + * others won't accidentally use this inappropriate constructor. + * And yes, it is peculiar that even though the members in this + * class are declared final that Hibernate can still create an + * object using this no-args constructor and then set the fields. + * Not quite as "final" as one might think. But at least it works. + */ + protected PredictionEvent() { + this.time = null; + this.avlTime = null; + this.vehicleId = null; + this.eventType = null; + this.description = null; + this.location = null; + this.routeId = null; + this.routeShortName = null; + this.blockId = null; + this.serviceId = null; + this.tripId = null; + this.stopId = null; + this.arrivalstopid = null; + this.departurestopid = null; + this.referenceVehicleId = null; + this.departureTime = null; + this.arrivalTime = null; + } + + public String getArrivalstopid() { + return arrivalstopid; + } + + public String getDeparturestopid() { + return departurestopid; + } + + public Date getTime() { + return time; + } + + public String getVehicleId() { + return vehicleId; + } + + public String getEventType() { + return eventType; + } + + public Date getAvlTime() { + return avlTime; + } + + public String getDescription() { + return description; + } + + public Location getLocation() { + return location; + } + + public String getRouteId() { + return routeId; + } + + public String getRouteShortName() { + return routeShortName; + } + + public String getBlockId() { + return blockId; + } + + public String getServiceId() { + return serviceId; + } + + public String getTripId() { + return tripId; + } + + public String getStopId() { + return stopId; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((arrivalTime == null) ? 0 : arrivalTime.hashCode()); + result = prime * result + ((arrivalstopid == null) ? 0 : arrivalstopid.hashCode()); + result = prime * result + ((avlTime == null) ? 0 : avlTime.hashCode()); + result = prime * result + ((blockId == null) ? 0 : blockId.hashCode()); + result = prime * result + ((departureTime == null) ? 0 : departureTime.hashCode()); + result = prime * result + ((departurestopid == null) ? 0 : departurestopid.hashCode()); + result = prime * result + ((description == null) ? 0 : description.hashCode()); + result = prime * result + ((eventType == null) ? 0 : eventType.hashCode()); + result = prime * result + ((location == null) ? 0 : location.hashCode()); + result = prime * result + ((referenceVehicleId == null) ? 0 : referenceVehicleId.hashCode()); + result = prime * result + ((routeId == null) ? 0 : routeId.hashCode()); + result = prime * result + ((routeShortName == null) ? 0 : routeShortName.hashCode()); + result = prime * result + ((serviceId == null) ? 0 : serviceId.hashCode()); + result = prime * result + ((stopId == null) ? 0 : stopId.hashCode()); + result = prime * result + ((time == null) ? 0 : time.hashCode()); + result = prime * result + ((tripId == null) ? 0 : tripId.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; + PredictionEvent other = (PredictionEvent) obj; + if (arrivalTime == null) { + if (other.arrivalTime != null) + return false; + } else if (!arrivalTime.equals(other.arrivalTime)) + return false; + if (arrivalstopid == null) { + if (other.arrivalstopid != null) + return false; + } else if (!arrivalstopid.equals(other.arrivalstopid)) + return false; + if (avlTime == null) { + if (other.avlTime != null) + return false; + } else if (!avlTime.equals(other.avlTime)) + return false; + if (blockId == null) { + if (other.blockId != null) + return false; + } else if (!blockId.equals(other.blockId)) + return false; + if (departureTime == null) { + if (other.departureTime != null) + return false; + } else if (!departureTime.equals(other.departureTime)) + return false; + if (departurestopid == null) { + if (other.departurestopid != null) + return false; + } else if (!departurestopid.equals(other.departurestopid)) + return false; + if (description == null) { + if (other.description != null) + return false; + } else if (!description.equals(other.description)) + return false; + if (eventType == null) { + if (other.eventType != null) + return false; + } else if (!eventType.equals(other.eventType)) + return false; + if (location == null) { + if (other.location != null) + return false; + } else if (!location.equals(other.location)) + return false; + if (referenceVehicleId == null) { + if (other.referenceVehicleId != null) + return false; + } else if (!referenceVehicleId.equals(other.referenceVehicleId)) + return false; + if (routeId == null) { + if (other.routeId != null) + return false; + } else if (!routeId.equals(other.routeId)) + return false; + if (routeShortName == null) { + if (other.routeShortName != null) + return false; + } else if (!routeShortName.equals(other.routeShortName)) + return false; + if (serviceId == null) { + if (other.serviceId != null) + return false; + } else if (!serviceId.equals(other.serviceId)) + return false; + if (stopId == null) { + if (other.stopId != null) + return false; + } else if (!stopId.equals(other.stopId)) + return false; + if (time == null) { + if (other.time != null) + return false; + } else if (!time.equals(other.time)) + return false; + if (tripId == null) { + if (other.tripId != null) + return false; + } else if (!tripId.equals(other.tripId)) + return false; + if (vehicleId == null) { + if (other.vehicleId != null) + return false; + } else if (!vehicleId.equals(other.vehicleId)) + return false; + return true; + } + + @Override + public String toString() { + return "PredictionEvent [time=" + time + ", vehicleId=" + vehicleId + ", eventType=" + eventType + ", avlTime=" + + avlTime + ", description=" + description + ", location=" + location + ", routeId=" + routeId + + ", routeShortName=" + routeShortName + ", blockId=" + blockId + ", serviceId=" + serviceId + + ", tripId=" + tripId + ", stopId=" + stopId + ", arrivalstopid=" + arrivalstopid + + ", departurestopid=" + departurestopid + ", referenceVehicleId=" + referenceVehicleId + + ", arrivalTime=" + arrivalTime + ", departureTime=" + departureTime + "]"; + } + + + +} diff --git a/transitclock/src/main/java/org/transitclock/db/structs/PredictionForStopPath.java b/transitclock/src/main/java/org/transitclock/db/structs/PredictionForStopPath.java new file mode 100755 index 000000000..287a4db30 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/db/structs/PredictionForStopPath.java @@ -0,0 +1,253 @@ +package org.transitclock.db.structs; + +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Index; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; + +import org.hibernate.Criteria; +import org.hibernate.Session; +import org.hibernate.annotations.Cache; +import org.hibernate.annotations.CacheConcurrencyStrategy; +import org.hibernate.annotations.DynamicUpdate; +import org.hibernate.criterion.Restrictions; +import org.transitclock.db.hibernate.HibernateUtils; +/** + * @author Sean Og Crudden + * Store the travel time prediction for a stopPath. + */ +@Entity @DynamicUpdate +@Table(name="StopPathPredictions", + indexes = { @Index(name="StopPathPredictionTimeIndex", + columnList="tripId, stopPathIndex" ) } ) +public class PredictionForStopPath implements Serializable{ + + + private static final long serialVersionUID = -6409934486927225387L; + + @Id + @GeneratedValue(strategy=GenerationType.AUTO) + private long id; + + @Column + @Temporal(TemporalType.TIMESTAMP) + private Date creationTime; + + @Column + private Double predictionTime; + + @Column(length=HibernateUtils.DEFAULT_ID_SIZE) + private String tripId; + + @Column + private Integer startTime; + + @Column + private String algorithm; + + @Column + private Integer stopPathIndex; + + @Column + private String vehicleId; + + @Column + private boolean travelTime; + + + + public boolean isTravelTime() { + return travelTime; + } + + public void setTravelTime(boolean travelTime) { + this.travelTime = travelTime; + } + + public PredictionForStopPath(String vehicleId, Date creationTime, Double predictionTimeMilliseconds, String tripId, Integer stopPathIndex, String algorithm, boolean travelTime, Integer startTime) { + super(); + this.creationTime = creationTime; + this.predictionTime = predictionTimeMilliseconds; + this.tripId = tripId; + this.stopPathIndex = stopPathIndex; + this.algorithm=algorithm; + this.vehicleId=vehicleId; + this.travelTime = travelTime; + this.startTime = startTime; + } + + public String getVehicleId() { + return vehicleId; + } + + public void setVehicleId(String vehicleId) { + this.vehicleId = vehicleId; + } + public void setCreationTime(Date creationTime) { + this.creationTime = creationTime; + } + + public void setPredictionTime(Double predictionTime) { + this.predictionTime = predictionTime; + } + + public void setTripId(String tripId) { + this.tripId = tripId; + } + + public void setAlgorithm(String algorithm) { + this.algorithm = algorithm; + } + + public void setStopPathIndex(Integer stopPathIndex) { + this.stopPathIndex = stopPathIndex; + } + + public Integer getStopPathIndex() { + return stopPathIndex; + } + + public Date getCreationTime() { + return creationTime; + } + + public Double getPredictionTime() { + return predictionTime; + } + + public String getTripId() { + return tripId; + } + + public String getAlgorithm() { + return algorithm; + } + + + + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((algorithm == null) ? 0 : algorithm.hashCode()); + result = prime * result + ((creationTime == null) ? 0 : creationTime.hashCode()); + result = prime * result + (int) (id ^ (id >>> 32)); + result = prime * result + ((predictionTime == null) ? 0 : predictionTime.hashCode()); + result = prime * result + ((startTime == null) ? 0 : startTime.hashCode()); + result = prime * result + ((stopPathIndex == null) ? 0 : stopPathIndex.hashCode()); + result = prime * result + (travelTime ? 1231 : 1237); + result = prime * result + ((tripId == null) ? 0 : tripId.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; + PredictionForStopPath other = (PredictionForStopPath) obj; + if (algorithm == null) { + if (other.algorithm != null) + return false; + } else if (!algorithm.equals(other.algorithm)) + return false; + if (creationTime == null) { + if (other.creationTime != null) + return false; + } else if (!creationTime.equals(other.creationTime)) + return false; + if (id != other.id) + return false; + if (predictionTime == null) { + if (other.predictionTime != null) + return false; + } else if (!predictionTime.equals(other.predictionTime)) + return false; + if (startTime == null) { + if (other.startTime != null) + return false; + } else if (!startTime.equals(other.startTime)) + return false; + if (stopPathIndex == null) { + if (other.stopPathIndex != null) + return false; + } else if (!stopPathIndex.equals(other.stopPathIndex)) + return false; + if (travelTime != other.travelTime) + return false; + if (tripId == null) { + if (other.tripId != null) + return false; + } else if (!tripId.equals(other.tripId)) + return false; + if (vehicleId == null) { + if (other.vehicleId != null) + return false; + } else if (!vehicleId.equals(other.vehicleId)) + return false; + return true; + } + + @SuppressWarnings("unchecked") + public static List getPredictionForStopPathFromDB ( + Date beginTime, + Date endTime, + String algorithm, + String tripId, + Integer stopPathIndex) + { + Session session = HibernateUtils.getSession(); + Criteria criteria = session.createCriteria(PredictionForStopPath.class); + + if(algorithm!=null&&algorithm.length()>0) + criteria.add(Restrictions.eq("algorithm", algorithm)); + if(tripId!=null) + criteria.add(Restrictions.eq("tripId", tripId)); + if(stopPathIndex!=null) + criteria.add(Restrictions.eq("stopPathIndex", stopPathIndex)); + if(beginTime!=null) + criteria.add(Restrictions.gt("creationTime", beginTime)); + if(endTime!=null) + criteria.add(Restrictions.lt("creationTime", endTime)); + List results=criteria.list(); + if(results.size()>0) + { + System.out.println("Got some results"); + } + return results; + } + + public PredictionForStopPath() { + super(); + this.creationTime = null; + this.predictionTime = null; + this.tripId = null; + this.stopPathIndex = null; + this.algorithm=null; + this.travelTime = true; + this.startTime = null; + } + + public Integer getStartTime() { + return startTime; + } + + public void setStartTime(Integer startTime) { + this.startTime = startTime; + } + +} diff --git a/transitime/src/main/java/org/transitime/db/structs/Route.java b/transitclock/src/main/java/org/transitclock/db/structs/Route.java similarity index 85% rename from transitime/src/main/java/org/transitime/db/structs/Route.java rename to transitclock/src/main/java/org/transitclock/db/structs/Route.java index 257102114..ff70333a8 100644 --- a/transitime/src/main/java/org/transitime/db/structs/Route.java +++ b/transitclock/src/main/java/org/transitclock/db/structs/Route.java @@ -14,23 +14,7 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.db.structs; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -import javax.persistence.Column; -import javax.persistence.Embedded; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Table; -import javax.persistence.Transient; +package org.transitclock.db.structs; import org.hibernate.HibernateException; import org.hibernate.Query; @@ -38,12 +22,16 @@ import org.hibernate.annotations.DynamicUpdate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.applications.Core; -import org.transitime.db.hibernate.HibernateUtils; -import org.transitime.gtfs.TitleFormatter; -import org.transitime.gtfs.gtfsStructs.GtfsRoute; -import org.transitime.utils.OrderedCollection; -import org.transitime.utils.StringUtils; +import org.transitclock.applications.Core; +import org.transitclock.db.hibernate.HibernateUtils; +import org.transitclock.gtfs.TitleFormatter; +import org.transitclock.gtfs.gtfsStructs.GtfsRoute; +import org.transitclock.utils.OrderedCollection; +import org.transitclock.utils.StringUtils; + +import javax.persistence.*; +import java.io.Serializable; +import java.util.*; /** @@ -82,19 +70,19 @@ public class Route implements Serializable { @Column(length=2) private final String type; - @Column + @Column(length=1024) private final String description; // Directly from GTFS data - @Column(length=80) + @Column(length=255) private final String shortName; // Directly from GTFS data - @Column(length=80) + @Column(length=255) private final String longName; // Processed name combing the GTFS route_short_name and route_long_name - @Column(length=80) + @Column(length=255) private final String name; @Embedded @@ -107,7 +95,7 @@ public class Route implements Serializable { @Transient // Later will probably want to store this in database, // but not yet sure. This means it is not available to application! - private final List tripPatternsForRoute; + private List tripPatternsForRoute; // For getStops() @Transient @@ -121,13 +109,22 @@ public class Route implements Serializable { @Transient private Map> orderedStopsPerDirectionMap = null; + @Transient + private Map> unorderedUniqueStopsPerDirectionMap = null; + // For getStopOrder(). // Keeps track of stop order for each direction. Keyed on direction id. // The submap is keyed on stop id and contains list of stop orders. // Need a list since a stop can be in a trip multiple times. @Transient private Map>> stopOrderByDirectionMap = null; - + + @Transient + private final Object unorderedStopsLock = new Object(); + + @Transient + private List directionIds = null; + // Because Hibernate requires objects with composite Ids to be Serializable private static final long serialVersionUID = 9037023420649883860L; @@ -198,7 +195,7 @@ public Route(int configRev, GtfsRoute gtfsRoute, this.name = titleFormatter.processTitle(this.shortName); } - this.tripPatternsForRoute = tripPatternsForRoute; + this.tripPatternsForRoute = tripPatternsForRoute; this.maxDistance = gtfsRoute.getMaxDistance(); // Determine the extent of the route by looking at the extent @@ -354,7 +351,7 @@ public String toString() { String tripPatternIds = "not set"; if (tripPatternsForRoute != null) { tripPatternIds = "["; - for (TripPattern tp : tripPatternsForRoute) + for (TripPattern tp : tripPatternsForRoute) tripPatternIds += tp.toShortString() + ", "; tripPatternIds += "]"; } @@ -391,7 +388,7 @@ public int hashCode() { result = prime * result + (hidden ? 1231 : 1237); result = prime * result + ((id == null) ? 0 : id.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); - result = prime * result + routeOrder; + result = prime * result + ((routeOrder == null) ? 0 : routeOrder);; result = prime * result + ((shortName == null) ? 0 : shortName.hashCode()); result = prime * result @@ -586,16 +583,31 @@ public List getLongestTripPatternForEachDirection() { * @return */ public List getTripPatterns(String directionId) { - List tripPatternsForRoute = Core.getInstance() - .getDbConfig().getTripPatternsForRoute(getId()); - List tripPatternsForDir = new ArrayList(); - for (TripPattern tripPattern : tripPatternsForRoute) { - if (Objects.equals(tripPattern.getDirectionId(), directionId)) - tripPatternsForDir.add(tripPattern); + List tripPatternsForRoute = getTripPatterns(); + List tripPatternsForDir = new ArrayList(); + for (TripPattern tripPattern : tripPatternsForRoute) { + if (Objects.equals(tripPattern.getDirectionId(), directionId)) + tripPatternsForDir.add(tripPattern); } - + return tripPatternsForDir; } + + public List getTripPatterns() { + if (tripPatternsForRoute == null) { + tripPatternsForRoute = Core.getInstance() + .getDbConfig().getTripPatternsForRoute(getId()); + } + return tripPatternsForRoute; + } + + /** + * unit test access. + * @param patterns + */ + public void setTripPatterns(List patterns) { + this.tripPatternsForRoute = patterns; + } /** * Returns list of direction IDs for the route. @@ -603,17 +615,28 @@ public List getTripPatterns(String directionId) { * @return */ public List getDirectionIds() { - List directionIds = new ArrayList(); - List tripPatternsForRoute = Core.getInstance() - .getDbConfig().getTripPatternsForRoute(getId()); - for (TripPattern tripPattern : tripPatternsForRoute) { - String directionId = tripPattern.getDirectionId(); - if (!directionIds.contains(directionId)) - directionIds.add(directionId); + if (directionIds == null) { + directionIds = new ArrayList(); + List tripPatternsForRoute = Core.getInstance() + .getDbConfig().getTripPatternsForRoute(getId()); + if (tripPatternsForRoute == null) return directionIds; + for (TripPattern tripPattern : tripPatternsForRoute) { + String directionId = tripPattern.getDirectionId(); + if (!directionIds.contains(directionId)) + directionIds.add(directionId); + } } return directionIds; } + /** + * for unit testing access. + * @param ids + */ + public void setDirectionIds(List ids) { + this.directionIds = ids; + } + /** * Returns unordered collection of path vectors associated with route * @return @@ -656,6 +679,40 @@ public synchronized Collection getPathSegments() { // Return the newly created collection of stop paths return stopPaths; } + + /** + * For each GTFS direction ID returns list of unordered stops for the direction. + * Groups all stops together without accounting for trip pattern order. + * Synchronized block since can be access through multiple threads. + * + * @return Map keyed by direction ID and value of List of ordered stop IDs. + */ + public Map> getUnorderedUniqueStopsByDirection() { + synchronized (unorderedStopsLock) { + // If already determined the stops return the cached map + if (unorderedUniqueStopsPerDirectionMap != null) { + return unorderedUniqueStopsPerDirectionMap; + } + + // Haven't yet determined ordered stops so do so now + unorderedUniqueStopsPerDirectionMap = new HashMap<>(); + + // For each direction + for (String directionId : getDirectionIds()) { + // Determine ordered collection of stops for direction + Set stopIdsForTripPatternList = new HashSet<>(); + List tripPatternsForDir = getTripPatterns(directionId); + for (TripPattern tripPattern : tripPatternsForDir) { + List stopIdsForTripPattern = tripPattern.getStopIds(); + for (String stopId : stopIdsForTripPattern) { + stopIdsForTripPatternList.add(stopId); + } + } + unorderedUniqueStopsPerDirectionMap.put(directionId, new ArrayList<>(stopIdsForTripPatternList)); + } + return unorderedUniqueStopsPerDirectionMap; + } + } /** * For each GTFS direction ID returns list of stops that in the appropriate diff --git a/transitclock/src/main/java/org/transitclock/db/structs/RunTimesForRoutes.java b/transitclock/src/main/java/org/transitclock/db/structs/RunTimesForRoutes.java new file mode 100644 index 000000000..9d5fe9d79 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/db/structs/RunTimesForRoutes.java @@ -0,0 +1,274 @@ +package org.transitclock.db.structs; + +import com.google.common.base.Objects; +import org.hibernate.annotations.DynamicUpdate; +import org.transitclock.core.ServiceType; +import org.transitclock.db.hibernate.HibernateUtils; + +import javax.persistence.*; +import java.io.Serializable; +import java.util.Date; + +@Entity +@DynamicUpdate +@Table(name="RunTimesForRoutes", + indexes = { @Index(name="RunTimesForRoutesTimeIndex", + columnList="startTime" ), + @Index(name="RunTimesForRoutesRouteNameIndex", + columnList="routeShortName" ), + @Index(name="RunTimesForRoutesTripIdIndex", + columnList="tripId" ), + @Index(name="RunTimesForRoutesVehicleIdIndex", + columnList="vehicleId" ), + @Index(name="RunTimesForRoutesServiceTypeIndex", + columnList="serviceType" ) + } ) + +public class RunTimesForRoutes implements Serializable { + + @Id + @Column + private int configRev; + + @Column(length= HibernateUtils.DEFAULT_ID_SIZE) + private String serviceId; + + @Column(length=HibernateUtils.DEFAULT_ID_SIZE) + private String directionId; + + @Column(length=HibernateUtils.DEFAULT_ID_SIZE) + private String routeShortName; + + @Column(length=TripPattern.TRIP_PATTERN_ID_LENGTH) + private String tripPatternId; + + @Id + @Column(length=HibernateUtils.DEFAULT_ID_SIZE) + private String tripId; + + @Column(length=TripPattern.HEADSIGN_LENGTH) + private String headsign; + + @Id + @Column + @Temporal(TemporalType.TIMESTAMP) + private Date startTime; + + @Column + @Temporal(TemporalType.TIMESTAMP) + private Date endTime; + + @Column + private Integer scheduledStartTime; + + @Column + private Integer scheduledEndTime; + + @Column + private Integer nextTripStartTime; + + @Id + @Column(length=HibernateUtils.DEFAULT_ID_SIZE) + private String vehicleId; + + @Column(length=8) + @Enumerated(EnumType.STRING) + private ServiceType serviceType; + + @Column + private Long dwellTime; + + public RunTimesForRoutes() { + } + + public RunTimesForRoutes(int configRev, String serviceId, String directionId, String routeShortName, + String tripPatternId, String tripId, String headsign, Date startTime, Date endTime, + Integer scheduledStartTime, Integer scheduledEndTime, Integer nextTripStartTime, + String vehicleId, ServiceType serviceType, Long dwellTime) { + this.configRev = configRev; + this.serviceId = serviceId; + this.directionId = directionId; + this.routeShortName = routeShortName; + this.tripPatternId = tripPatternId; + this.tripId = tripId; + this.headsign = headsign; + this.startTime = startTime; + this.endTime = endTime; + this.scheduledStartTime = scheduledStartTime; + this.scheduledEndTime = scheduledEndTime; + this.nextTripStartTime = nextTripStartTime; + this.vehicleId = vehicleId; + this.serviceType = serviceType; + this.dwellTime = dwellTime; + } + + public int getConfigRev() { + return configRev; + } + + public void setConfigRev(int configRev) { + this.configRev = configRev; + } + + public String getServiceId() { + return serviceId; + } + + public void setServiceId(String serviceId) { + this.serviceId = serviceId; + } + + public String getDirectionId() { + return directionId; + } + + public void setDirectionId(String directionId) { + this.directionId = directionId; + } + + public String getRouteShortName() { + return routeShortName; + } + + public void setRouteShortName(String routeShortName) { + this.routeShortName = routeShortName; + } + + public String getTripPatternId() { + return tripPatternId; + } + + public void setTripPatternId(String tripPatternId) { + this.tripPatternId = tripPatternId; + } + + public String getTripId() { + return tripId; + } + + public void setTripId(String tripId) { + this.tripId = tripId; + } + + public String getHeadsign() { + return headsign; + } + + public void setHeadsign(String headsign) { + this.headsign = headsign; + } + + public Date getStartTime() { + return startTime; + } + + public void setStartTime(Date startTime) { + this.startTime = startTime; + } + + public Date getEndTime() { + return endTime; + } + + public void setEndTime(Date endTime) { + this.endTime = endTime; + } + + public Integer getScheduledStartTime() { + return scheduledStartTime; + } + + public void setScheduledStartTime(Integer scheduledStartTime) { + this.scheduledStartTime = scheduledStartTime; + } + + public Integer getScheduledEndTime() { + return scheduledEndTime; + } + + public void setScheduledEndTime(Integer scheduledEndTime) { + this.scheduledEndTime = scheduledEndTime; + } + + public Integer getNextTripStartTime() { + return nextTripStartTime; + } + + public void setNextTripStartTime(Integer nextTripStartTime) { + this.nextTripStartTime = nextTripStartTime; + } + + public String getVehicleId() { + return vehicleId; + } + + public void setVehicleId(String vehicleId) { + this.vehicleId = vehicleId; + } + + public ServiceType getServiceType() { + return serviceType; + } + + public void setServiceType(ServiceType serviceType) { + this.serviceType = serviceType; + } + + public Long getDwellTime() { + return dwellTime; + } + + public void setDwellTime(Long dwellTime) { + this.dwellTime = dwellTime; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + RunTimesForRoutes that = (RunTimesForRoutes) o; + return configRev == that.configRev && + Objects.equal(serviceId, that.serviceId) && + Objects.equal(directionId, that.directionId) && + Objects.equal(routeShortName, that.routeShortName) && + Objects.equal(tripPatternId, that.tripPatternId) && + Objects.equal(tripId, that.tripId) && + Objects.equal(headsign, that.headsign) && + Objects.equal(startTime, that.startTime) && + Objects.equal(endTime, that.endTime) && + Objects.equal(scheduledStartTime, that.scheduledStartTime) && + Objects.equal(scheduledEndTime, that.scheduledEndTime) && + Objects.equal(nextTripStartTime, that.nextTripStartTime) && + Objects.equal(vehicleId, that.vehicleId) && + serviceType == that.serviceType && + Objects.equal(dwellTime, that.dwellTime); + } + + @Override + public int hashCode() { + return Objects.hashCode(configRev, serviceId, directionId, routeShortName, tripPatternId, tripId, headsign, + startTime, endTime, scheduledStartTime, scheduledEndTime, nextTripStartTime, vehicleId, serviceType, + dwellTime); + } + + @Override + public String toString() { + return "RunTimesForRoutes{" + + "configRev=" + configRev + + ", serviceId='" + serviceId + '\'' + + ", directionId='" + directionId + '\'' + + ", routeShortName='" + routeShortName + '\'' + + ", tripPatternId='" + tripPatternId + '\'' + + ", tripId='" + tripId + '\'' + + ", headsign='" + headsign + '\'' + + ", startTime=" + startTime + + ", endTime=" + endTime + + ", scheduledStartTime=" + scheduledStartTime + + ", scheduledEndTime=" + scheduledEndTime + + ", nextTripStartTime=" + nextTripStartTime + + ", vehicleId='" + vehicleId + '\'' + + ", serviceType=" + serviceType + + ", dwellTime=" + dwellTime + + '}'; + } +} diff --git a/transitime/src/main/java/org/transitime/db/structs/ScheduleTime.java b/transitclock/src/main/java/org/transitclock/db/structs/ScheduleTime.java similarity index 88% rename from transitime/src/main/java/org/transitime/db/structs/ScheduleTime.java rename to transitclock/src/main/java/org/transitclock/db/structs/ScheduleTime.java index 3058cb101..8d5578bf7 100644 --- a/transitime/src/main/java/org/transitime/db/structs/ScheduleTime.java +++ b/transitclock/src/main/java/org/transitclock/db/structs/ScheduleTime.java @@ -14,11 +14,13 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.db.structs; +package org.transitclock.db.structs; -import java.io.Serializable; +import javax.persistence.Embeddable; + +import org.transitclock.utils.Time; -import org.transitime.utils.Time; +import java.io.Serializable; /** @@ -27,6 +29,7 @@ * * @author SkiBu Smith */ +@Embeddable public class ScheduleTime implements Serializable { // Times are in seconds. arrivalTime only set for last @@ -44,8 +47,13 @@ public ScheduleTime(Integer arrivalTime, Integer departureTime) { this.arrivalTime = arrivalTime; this.departureTime = departureTime; } - - /** + + protected ScheduleTime() { + arrivalTime = null; + departureTime = null; + } + + /** * Returns departure time if there is one. Otherwise returns arrival time if * there is one. Otherwise returns null. * diff --git a/transitime/src/main/java/org/transitime/db/structs/Stop.java b/transitclock/src/main/java/org/transitclock/db/structs/Stop.java similarity index 91% rename from transitime/src/main/java/org/transitime/db/structs/Stop.java rename to transitclock/src/main/java/org/transitclock/db/structs/Stop.java index ae808c0cb..f344cc0a0 100644 --- a/transitime/src/main/java/org/transitime/db/structs/Stop.java +++ b/transitclock/src/main/java/org/transitclock/db/structs/Stop.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.db.structs; +package org.transitclock.db.structs; import java.io.Serializable; import java.util.List; @@ -29,9 +29,9 @@ import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.annotations.DynamicUpdate; -import org.transitime.db.hibernate.HibernateUtils; -import org.transitime.gtfs.TitleFormatter; -import org.transitime.gtfs.gtfsStructs.GtfsStop; +import org.transitclock.db.hibernate.HibernateUtils; +import org.transitclock.gtfs.TitleFormatter; +import org.transitclock.gtfs.gtfsStructs.GtfsStop; /** @@ -138,7 +138,7 @@ public Stop(int configRev, GtfsStop gtfsStop, Integer stopCodeBaseValue, * Needed because Hibernate requires no-arg constructor */ @SuppressWarnings("unused") - private Stop() { + public Stop() { configRev = -1; id = null; code = null; @@ -209,15 +209,17 @@ public static List getStops(Session session, int configRev) public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + (timepointStop ? 1231 : 1237); - result = prime * result + ((code == null) ? 0 : code.hashCode()); - result = prime * result + configRev; - result = prime * result + (hidden ? 1231 : 1237); - result = prime * result + ((id == null) ? 0 : id.hashCode()); - result = prime * result + (layoverStop ? 1231 : 1237); - result = prime * result + ((loc == null) ? 0 : loc.hashCode()); - result = prime * result + ((name == null) ? 0 : name.hashCode()); - result = prime * result + (waitStop ? 1231 : 1237); + + result = prime * result + (timepointStop ? 1231 : 1237); + result = prime * result + ((code == null) ? 0 : code.hashCode()); + result = prime * result + configRev; + result = prime * result + (hidden ? 1231 : 1237); + result = prime * result + ((id == null) ? 0 : id.hashCode()); + result = prime * result + ((layoverStop != null ? layoverStop : false) ? 1231 : 1237); + result = prime * result + ((loc == null) ? 0 : loc.hashCode()); + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((waitStop != null ? waitStop : false) ? 1231 : 1237); + return result; } diff --git a/transitime/src/main/java/org/transitime/db/structs/StopPath.java b/transitclock/src/main/java/org/transitclock/db/structs/StopPath.java similarity index 91% rename from transitime/src/main/java/org/transitime/db/structs/StopPath.java rename to transitclock/src/main/java/org/transitclock/db/structs/StopPath.java index e52c07ef2..1471fdced 100644 --- a/transitime/src/main/java/org/transitime/db/structs/StopPath.java +++ b/transitclock/src/main/java/org/transitclock/db/structs/StopPath.java @@ -1,5 +1,5 @@ /* - * 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 @@ -14,15 +14,18 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.db.structs; +package org.transitclock.db.structs; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import javax.persistence.Column; +import javax.persistence.ElementCollection; import javax.persistence.Entity; +import javax.persistence.FetchType; import javax.persistence.Id; +import javax.persistence.OrderColumn; import javax.persistence.Table; import javax.persistence.Transient; @@ -34,10 +37,11 @@ import org.hibernate.classic.Lifecycle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.applications.Core; -import org.transitime.configData.CoreConfig; -import org.transitime.db.hibernate.HibernateUtils; -import org.transitime.utils.Geo; +import org.transitclock.applications.Core; +import org.transitclock.configData.CoreConfig; +import org.transitclock.db.hibernate.HibernateUtils; +import org.transitclock.utils.Geo; + /** @@ -106,13 +110,11 @@ public class StopPath implements Serializable, Lifecycle { @Column private final Integer breakTime; - // NOTE: since trying to use serialization need to use ArrayList<> instead - // of List<> since List<> doesn't implement Serializable. Serialization - // makes the data not readable in the db using regular SQL but it means - // that don't need separate table and the data can be read and written - // much faster. - @Column(length=1000)// @ElementCollection @OrderColumn - private ArrayList locations; + // sacrifice performance for reportability -- use a child table instead of java serialization + @ElementCollection(fetch=FetchType.LAZY) + @OrderColumn + private List locations; + // Having the path length readily accessible via the database is handy // since that way can easily do queries to determine travel speeds and @@ -120,6 +122,12 @@ public class StopPath implements Serializable, Lifecycle { @Column private double pathLength; + + @Column + private Double maxDistance; + + @Column + private Double maxSpeed; // So can have easy access to vectors representing the segments so // can easily determine heading. Declared transient because this // info is generated by other members after the object has been @@ -139,7 +147,7 @@ public class StopPath implements Serializable, Lifecycle { * Simple constructor * * @param configRev - * @param pathId + * @param stopPathId * @param stopId * @param gtfsStopSeq * @param lastStopInTrip @@ -150,7 +158,7 @@ public class StopPath implements Serializable, Lifecycle { * @param breakTime */ public StopPath(int configRev, - String pathId, + String stopPathId, String stopId, int gtfsStopSeq, boolean lastStopInTrip, @@ -158,9 +166,11 @@ public StopPath(int configRev, boolean layoverStop, boolean waitStop, boolean scheduleAdherenceStop, - Integer breakTime) { + Integer breakTime, + Double maxDistance, + Double maxSpeed) { this.configRev = configRev; - this.stopPathId = pathId; + this.stopPathId = stopPathId; this.stopId = stopId; this.gtfsStopSeq = gtfsStopSeq; this.lastStopInTrip = lastStopInTrip; @@ -172,13 +182,15 @@ public StopPath(int configRev, // If valid break time was passed in then use it. Otherwise // use the default value. this.breakTime = breakTime; + this.maxDistance = maxDistance; + this.maxSpeed = maxSpeed; } /** * Needed because Hibernate requires no-arg constructor */ @SuppressWarnings("unused") - private StopPath() { + public StopPath() { this.configRev = -1; this.stopPathId = null; this.stopId = null; @@ -191,6 +203,9 @@ private StopPath() { this.waitStop = false; this.scheduleAdherenceStop = false; this.breakTime = null; + this.maxDistance = null; + this.maxSpeed = null; + } /** @@ -266,10 +281,30 @@ public String toString() { + ", layoverStop=" + layoverStop + ", waitStop=" + waitStop + ", scheduleAdherenceStop=" + scheduleAdherenceStop + + ", maxDistance=" + maxDistance + + ", maxSpeed=" + maxSpeed + (breakTime == null ? "" : ", breakTime=" + breakTime) + "]"; } + + + public Double getMaxSpeed() { + return maxSpeed; + } + + public void setMaxSpeed(Double maxSpeed) { + this.maxSpeed = maxSpeed; + } + + public Double getMaxDistance() { + return maxDistance; + } + + public void setMaxDistance(Double maxDistance) { + this.maxDistance = maxDistance; + } + /** * Needed because have a composite ID for Hibernate storage */ @@ -609,6 +644,10 @@ public int getBreakTimeSec() { } } + public Integer getBreakTime() { + return breakTime; + } + /** * Indicates that vehicle is not supposed to depart the stop until the * scheduled departure time. @@ -665,13 +704,20 @@ public void onLoad(Session arg0, Serializable arg1) { vectors = new ArrayList(locations.size()-1); for (int segmentIndex=0; segmentIndex. + */ +package org.transitclock.db.structs; + +import org.hibernate.Query; +import org.hibernate.Session; +import org.hibernate.annotations.Cascade; +import org.hibernate.annotations.CascadeType; +import org.hibernate.annotations.DynamicUpdate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.db.hibernate.HibernateUtils; + +import javax.persistence.Column; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.JoinTable; +import javax.persistence.OneToMany; +import javax.persistence.OrderColumn; +import javax.persistence.Table; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * Mirroring the conventions in StopPath, a TrafficPath is a set of points + * that defines the shape of a traffic sensor. StopPaths are then mapped + * to these TrafficPaths where overlap occurs. + */ +@Entity(name="TrafficPath") +@DynamicUpdate +@Table(name="TrafficPaths") +public class TrafficPath implements Serializable { + + @Column(length=2* HibernateUtils.DEFAULT_ID_SIZE) + @Id + private final String trafficPathId; + + @Column + @Id + private final Integer trafficRev; + + @ElementCollection(fetch=FetchType.LAZY) + @OrderColumn + private List locations; + + @OneToMany(fetch=FetchType.LAZY) + @Cascade({CascadeType.SAVE_UPDATE}) + @JoinTable(name="TrafficPath_to_StopPath_joinTable") + @OrderColumn( name="listIndex") + final protected List stopPaths; + + @Column + private float pathLength; + + private static long serialVersionUID = 1; + + private static final Logger logger = + LoggerFactory.getLogger(TrafficPath.class); + + /** + * Public constructor. + * + * @param trafficPathId + * @param trafficRev + * @param pathLength + */ + public TrafficPath(String trafficPathId, + int trafficRev, + float pathLength) { + this.trafficPathId = trafficPathId; + this.trafficRev = trafficRev; + this.pathLength = pathLength; + stopPaths = new ArrayList<>(); + } + + /** + * Needed because Hibernate requires no-arg constructor + */ + public TrafficPath() { + this.trafficPathId = null; + this.trafficRev = null; + this.pathLength = -1.0f; + stopPaths = new ArrayList<>(); + } + + public static List getTrafficPaths(Session session, Integer trafficRev) { + String hql = "FROM TrafficPath " + + " WHERE trafficRev = :trafficRev"; + Query query = session.createQuery(hql); + query.setInteger("trafficRev", trafficRev); + return query.list(); + } + + + public void setLocations(ArrayList locations) { + this.locations = locations; + } + public List getLocations() { + return locations; + } + + public List getStopPaths() { + return stopPaths; + } + + public String getTrafficPathId() { + return trafficPathId; + } + + public int getTrafficRev() { + return trafficRev; + } + +} diff --git a/transitclock/src/main/java/org/transitclock/db/structs/TrafficSensor.java b/transitclock/src/main/java/org/transitclock/db/structs/TrafficSensor.java new file mode 100644 index 000000000..a331b94dc --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/db/structs/TrafficSensor.java @@ -0,0 +1,83 @@ +/* + * 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.db.structs; + +import org.hibernate.annotations.DynamicUpdate; +import org.transitclock.db.hibernate.HibernateUtils; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import java.io.Serializable; + +/** + * Represents the sensor specific data from an external system. + */ +@Entity(name="TrafficSensor") +@DynamicUpdate +@Table(name="TrafficSensor") +public class TrafficSensor implements Serializable { + + @Column(length=HibernateUtils.DEFAULT_ID_SIZE) + @Id + private final String id; + + @Column + @Id + private final Integer trafficRev; + + @Column(length=HibernateUtils.DEFAULT_ID_SIZE) + private String externalId; + + @Column(length=2*HibernateUtils.DEFAULT_ID_SIZE) + private String trafficPathId; + + @Column + private String description; + + /** + * Simple constructor + * @param id + * @param trafficRev + * @param externalId + */ + public TrafficSensor(String id, + int trafficRev, + String externalId, + String trafficPathId) { + this.id = id; + this.trafficRev = trafficRev; + this.externalId = externalId; + this.trafficPathId = trafficPathId; + } + + /** + * Needed because Hibernate requires no-arg constructor + */ + public TrafficSensor() { + this.id = null; + this.trafficRev = null; + this.externalId = null; + this.trafficPathId = null; + } + + public void setDescription(String s) { + this.description = s; + } + +} diff --git a/transitclock/src/main/java/org/transitclock/db/structs/TrafficSensorData.java b/transitclock/src/main/java/org/transitclock/db/structs/TrafficSensorData.java new file mode 100644 index 000000000..3899271af --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/db/structs/TrafficSensorData.java @@ -0,0 +1,168 @@ +/* + * 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.db.structs; + +import org.hibernate.Query; +import org.hibernate.Session; +import org.hibernate.annotations.DynamicUpdate; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import java.io.Serializable; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +/** + * Represents one sample of traffic data from a single traffic sensor. + */ +@Entity(name="TrafficSensorData") +@DynamicUpdate +@Table(name="TrafficSensorData") +public class TrafficSensorData implements Serializable { + + @Column + @Id + private final String trafficSensorId; + + @Column + @Id + private final int trafficRev; + + @Column + @Id + private final Date time; + + @Column + private Double speed; // in meters per second! + + @Column + private Double delayMillis; + + @Column + private Double confidence; + + @Column + private Integer travelTimeMillis; + + @Column + /** + * An estimated length of the traffic sensor + * based on the geometry. This is NOT accuracte + * enough for travel time calculation. + */ + private Double length; + + /** + * Simple constructor + * @param trafficSensorId + * @param trafficRev + * @param time + * @param speed + * @param delayMillis + * @param confidence + * @param travelTimeMillis + * @param length + */ + public TrafficSensorData(String trafficSensorId, + int trafficRev, + Date time, + Double speed, + Double delayMillis, + Double confidence, + Integer travelTimeMillis, + Double length) { + this.trafficSensorId = trafficSensorId; + this.trafficRev = trafficRev; + this.time = time; + this.speed = speed; + this.delayMillis = delayMillis; + this.confidence = confidence; + this.travelTimeMillis = travelTimeMillis; + this.length = length; + } + + /** + * Hibernate requires no-arg constructor + */ + public TrafficSensorData() { + trafficSensorId = null; + trafficRev = -1; + time = null; + speed = null; + delayMillis = null; + confidence = null; + travelTimeMillis = null; + length = null; + } + + public String getTrafficSensorId() { return trafficSensorId; } + + /** + * speed in meters per second. + * @return + */ + public Double getSpeed() { + return speed; + } + public Integer getTravelTimeMillis() { + return travelTimeMillis; + } + + /** + * estimated length of the traffic sensor shape. + * @return + */ + public double getLength() { + return length; + } + public Date getTime() { + return time; + } + + /** + * do bulk load of data. + * @param session + * @param startDate + * @param endDate + * @return + */ + public static Iterator getTrafficSensorDataIteratorFromDb(Session session, Date startDate, Date endDate) { + String hql = "FROM TrafficSensorData " + + " WHERE time >= :beginDate " + + " AND time < :endDate"; + Query query = session.createQuery(hql); + query.setTimestamp("beginDate", startDate); + query.setTimestamp("endDate", endDate); + //iterator performance on mysql is poor! + return query.iterate(); + } + + public static List getTrafficSensorDataFromDb(Session session, Date startDate, Date endDate) { + String hql = "FROM TrafficSensorData " + + " WHERE time >= :beginDate " + + " AND time < :endDate"; + Query query = session.createQuery(hql); + query.setTimestamp("beginDate", startDate); + query.setTimestamp("endDate", endDate); + + return query.list(); + } + +} diff --git a/transitime/src/main/java/org/transitime/db/structs/Transfer.java b/transitclock/src/main/java/org/transitclock/db/structs/Transfer.java similarity index 97% rename from transitime/src/main/java/org/transitime/db/structs/Transfer.java rename to transitclock/src/main/java/org/transitclock/db/structs/Transfer.java index fe53f870b..46d7af26f 100644 --- a/transitime/src/main/java/org/transitime/db/structs/Transfer.java +++ b/transitclock/src/main/java/org/transitclock/db/structs/Transfer.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.db.structs; +package org.transitclock.db.structs; import java.io.Serializable; import java.util.List; @@ -28,8 +28,8 @@ import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.annotations.DynamicUpdate; -import org.transitime.db.hibernate.HibernateUtils; -import org.transitime.gtfs.gtfsStructs.GtfsTransfer; +import org.transitclock.db.hibernate.HibernateUtils; +import org.transitclock.gtfs.gtfsStructs.GtfsTransfer; /** * Contains data from the transfers.txt GTFS file. This class is diff --git a/transitime/src/main/java/org/transitime/db/structs/TravelTimesForStopPath.java b/transitclock/src/main/java/org/transitclock/db/structs/TravelTimesForStopPath.java old mode 100644 new mode 100755 similarity index 88% rename from transitime/src/main/java/org/transitime/db/structs/TravelTimesForStopPath.java rename to transitclock/src/main/java/org/transitclock/db/structs/TravelTimesForStopPath.java index a34ac652d..46d492f61 --- a/transitime/src/main/java/org/transitime/db/structs/TravelTimesForStopPath.java +++ b/transitclock/src/main/java/org/transitclock/db/structs/TravelTimesForStopPath.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.db.structs; +package org.transitclock.db.structs; import java.io.Serializable; import java.util.ArrayList; @@ -28,6 +28,7 @@ import javax.persistence.Id; import javax.persistence.Table; +import org.apache.commons.collections.CollectionUtils; import org.hibernate.HibernateException; import org.hibernate.Query; import org.hibernate.Session; @@ -35,8 +36,8 @@ import org.hibernate.annotations.DynamicUpdate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.db.hibernate.HibernateUtils; -import org.transitime.utils.Geo; +import org.transitclock.db.hibernate.HibernateUtils; +import org.transitclock.utils.Geo; /** @@ -109,7 +110,8 @@ public class TravelTimesForStopPath implements Serializable { // it can make things so much more efficient want to try using it. // NOTE: since trying to use serialization need to use ArrayList<> instead // of List<> since List<> doesn't implement Serializable. - private static final int travelTimesMaxBytes = 2000; + private static final int travelTimesMaxBytes = 100000; + @Column(length=travelTimesMaxBytes) private final ArrayList travelTimesMsec; @@ -273,7 +275,26 @@ public TravelTimesForStopPath clone(int newTravelTimesRev) { stopPathId, travelTimeSegmentLength, travelTimesMsec, stopTimeMsec, daysOfWeekOverride, howSet, null); } - + + public TravelTimesForStopPath cloneAndClamp(int newTravelTimesRev, int maxStopTimeMsec, int maxTravelTimeMsec) { + int clampedStopTimeMsec = Math.min(this.stopTimeMsec, maxStopTimeMsec); + List clampedTravelTimesMsec = new ArrayList<>(this.travelTimesMsec.size()); + for (Integer tt : this.travelTimesMsec) { + clampedTravelTimesMsec.add(Math.min(tt, maxTravelTimeMsec)); + } + // if not dirty, return original list so strict equals comparison still works + if (CollectionUtils.isEqualCollection(this.travelTimesMsec, clampedTravelTimesMsec)) { + return new TravelTimesForStopPath(configRev, newTravelTimesRev, + stopPathId, travelTimeSegmentLength, travelTimesMsec, + clampedStopTimeMsec, daysOfWeekOverride, howSet, null); + } else { + return new TravelTimesForStopPath(configRev, newTravelTimesRev, + stopPathId, travelTimeSegmentLength, clampedTravelTimesMsec, + clampedStopTimeMsec, daysOfWeekOverride, howSet, null); + } + } + + /* (non-Javadoc) * @see java.lang.Object#toString() */ @@ -313,6 +334,8 @@ public String toStringEmphasizeTravelTimes() { /************************ Getter Methods *************************/ + public int getInternalId() { return id; } + public int getConfigRev() { return configRev; } @@ -438,6 +461,21 @@ public static List getTravelTimes(SessionFactory session session.close(); } } + + /** + * Returns true if all travel times and dwell time are nonnegative. + */ + public boolean isValid() { + if (travelTimesMsec != null) { + for (int time : travelTimesMsec) { + if (time < 0) + return false; + } + } + if (stopTimeMsec < 0) + return false; + return true; + } /** * Defined so can use as key in map diff --git a/transitime/src/main/java/org/transitime/db/structs/TravelTimesForTrip.java b/transitclock/src/main/java/org/transitclock/db/structs/TravelTimesForTrip.java similarity index 94% rename from transitime/src/main/java/org/transitime/db/structs/TravelTimesForTrip.java rename to transitclock/src/main/java/org/transitclock/db/structs/TravelTimesForTrip.java index a18c1cfa7..c3df8a61b 100644 --- a/transitime/src/main/java/org/transitime/db/structs/TravelTimesForTrip.java +++ b/transitclock/src/main/java/org/transitclock/db/structs/TravelTimesForTrip.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.db.structs; +package org.transitclock.db.structs; import java.io.Serializable; import java.util.ArrayList; @@ -33,15 +33,16 @@ import javax.persistence.OrderColumn; import javax.persistence.Table; +import org.hibernate.Criteria; import org.hibernate.HibernateException; -import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.annotations.Cascade; import org.hibernate.annotations.CascadeType; import org.hibernate.annotations.DynamicUpdate; +import org.hibernate.criterion.Restrictions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.db.hibernate.HibernateUtils; +import org.transitclock.db.hibernate.HibernateUtils; /** @@ -68,7 +69,9 @@ public class TravelTimesForTrip implements Serializable { @Id @GeneratedValue private Integer id; - + public Integer getId() { + return id; + } // Need configRev for the configuration so that when old configurations // cleaned out can also easily get rid of old travel times. Note: at one // point tried making configRevan @Id so that the config rev is part of @@ -97,7 +100,8 @@ public class TravelTimesForTrip implements Serializable { // on schedule. @Column(length=HibernateUtils.DEFAULT_ID_SIZE) private final String tripCreatedForId; - + + // Load EAGERly for AVLExecutor thread safety/concurrency performance @ManyToMany(fetch=FetchType.EAGER) @JoinTable(name="TravelTimesForTrip_to_TravelTimesForPath_joinTable") @Cascade({CascadeType.SAVE_UPDATE}) @@ -221,19 +225,9 @@ public static Map> getTravelTimesForTrips( logger.info("Reading TravelTimesForTrips for travelTimesRev={} ...", travelTimesRev); - // Get List of all TravelTimesForTrip for the specified rev - String hql = "FROM TravelTimesForTrip " + - " WHERE travelTimesRev = :travelTimesRev"; - Query query = session.createQuery(hql); - query.setInteger("travelTimesRev", travelTimesRev); - List allTravelTimes; - try { - allTravelTimes = query.list(); - } catch (Exception e) { - logger.error("Exception in getTravelTimesForTrips(). {}", - e.getMessage()); - throw e; - } + List allTravelTimes = session.createCriteria(TravelTimesForTrip.class) + .add(Restrictions.eq("travelTimesRev", travelTimesRev)) + .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY).list(); logger.info("Putting travel times into map..."); @@ -274,6 +268,16 @@ public boolean purelyScheduleBased() { return true; } + /** + * Returns true if all stop paths are valid. + */ + public boolean isValid() { + for (TravelTimesForStopPath times : travelTimesForStopPaths) + if (!times.isValid()) + return false; + return true; + } + /** * Needed because of Hibernate and also because want to cache * TravelTimesForTrip and to do so need to store the objects @@ -294,7 +298,7 @@ public int hashCode() { result = prime * result + travelTimesRev; result = prime * result + tripPatternId.hashCode(); // result = prime * result + tripCreatedForId.hashCode(); - result = prime * result + travelTimesForStopPaths.hashCode(); +// result = prime * result + travelTimesForStopPaths.hashCode(); // Stack Overflow with lots of data return result; } diff --git a/transitime/src/main/java/org/transitime/db/structs/Trip.java b/transitclock/src/main/java/org/transitclock/db/structs/Trip.java similarity index 85% rename from transitime/src/main/java/org/transitime/db/structs/Trip.java rename to transitclock/src/main/java/org/transitclock/db/structs/Trip.java index b9502f70d..92b20e767 100644 --- a/transitime/src/main/java/org/transitime/db/structs/Trip.java +++ b/transitclock/src/main/java/org/transitclock/db/structs/Trip.java @@ -14,22 +14,15 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.db.structs; +package org.transitclock.db.structs; import java.io.Serializable; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.math.BigInteger; +import java.util.*; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.Id; -import javax.persistence.ManyToOne; -import javax.persistence.Table; -import javax.persistence.Transient; +import javax.persistence.*; +import org.apache.commons.lang3.StringUtils; import org.hibernate.CallbackException; import org.hibernate.HibernateException; import org.hibernate.Query; @@ -37,15 +30,19 @@ import org.hibernate.annotations.Cascade; import org.hibernate.annotations.CascadeType; import org.hibernate.annotations.DynamicUpdate; +import org.hibernate.collection.internal.PersistentList; +import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.classic.Lifecycle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.applications.Core; -import org.transitime.db.hibernate.HibernateUtils; -import org.transitime.gtfs.DbConfig; -import org.transitime.gtfs.TitleFormatter; -import org.transitime.gtfs.gtfsStructs.GtfsTrip; -import org.transitime.utils.Time; +import org.transitclock.applications.Core; +import org.transitclock.db.hibernate.HibernateUtils; +import org.transitclock.db.query.TripQuery; +import org.transitclock.gtfs.DbConfig; +import org.transitclock.gtfs.TitleFormatter; +import org.transitclock.gtfs.gtfsStructs.GtfsTrip; +import org.transitclock.utils.IntervalTimer; +import org.transitclock.utils.Time; /** @@ -123,16 +120,10 @@ public class Trip implements Lifecycle, Serializable { // Contains schedule time for each stop as obtained from GTFS // stop_times.txt file. Useful for determining schedule adherence. - // NOTE: trying to use serialization. Serialization - // makes the data not readable in the db using regular SQL but it means - // that don't need separate table and the data can be read and written - // much faster. - // scheduleTimesMaxBytes set to 4000 because for sfmta route 91 there - // is a trip with 91 schedule times. - private static final int scheduleTimesMaxBytes = 4000; - @Column(length=scheduleTimesMaxBytes) - private final ArrayList scheduledTimesList = - new ArrayList(); + @ElementCollection + @OrderColumn + private final List scheduledTimesList = + new ArrayList(); // For non-scheduled blocks where vehicle runs a trip as a continuous loop @Column @@ -161,6 +152,9 @@ public class Trip implements Lifecycle, Serializable { @Column(length=HibernateUtils.DEFAULT_ID_SIZE) private String shapeId; + @ManyToMany(mappedBy = "trips") + private List blocks; + @Transient private Route route; @@ -338,7 +332,7 @@ public Trip(Trip tripFromStopTimes, int frequenciesBasedStartTime, * Hibernate requires no-arg constructor */ @SuppressWarnings("unused") - private Trip() { + public Trip() { configRev = -1; tripId = null; tripShortName = null; @@ -421,22 +415,22 @@ public void addScheduleTimes(List newScheduledTimesList) { // If resulting map takes up too much memory throw an exception. // Only bother checking if have at least a few schedule times. - if (scheduledTimesList.size() > 5) { + /*if (scheduledTimesList.size() > 5) { int serializedSize = HibernateUtils.sizeof(scheduledTimesList); if (serializedSize > scheduleTimesMaxBytes) { String msg = "Too many elements in " + "scheduledTimesMap when constructing a " + "Trip. Have " + scheduledTimesList.size() - + " schedule times taking up " + serializedSize - + " bytes but only have " + scheduleTimesMaxBytes - + " bytes allocated for the data. Trip=" + + " schedule times taking up " + serializedSize + + " bytes but only have " + scheduleTimesMaxBytes + + " bytes allocated for the data. Trip=" + this.toShortString(); logger.error(msg); - + // Since this could be a really problematic issue, throw an error throw new ArrayIndexOutOfBoundsException(msg); } - } + }*/ } /** @@ -498,8 +492,10 @@ public static Map getTrips(Session session, int configRev) public static Trip getTrip(Session session, int configRev, String tripId) throws HibernateException { // Setup the query - String hql = "FROM Trip " + - " WHERE configRev = :configRev" + + String hql = "FROM Trip t " + + " left join fetch t.scheduledTimesList " + + " left join fetch t.travelTimes " + + " WHERE t.configRev = :configRev" + " AND tripId = :tripId"; Query query = session.createQuery(hql); query.setInteger("configRev", configRev); @@ -525,9 +521,11 @@ public static Trip getTrip(Session session, int configRev, String tripId) public static List getTripByShortName(Session session, int configRev, String tripShortName) throws HibernateException { // Setup the query - String hql = "FROM Trip " + - " WHERE configRev = :configRev" + - " AND tripShortName = :tripShortName"; + String hql = "FROM Trip t " + + " left join fetch t.scheduledTimesList " + + " left join fetch t.travelTimes " + + " WHERE t.configRev = :configRev" + + " AND t.tripShortName = :tripShortName"; Query query = session.createQuery(hql); query.setInteger("configRev", configRev); query.setString("tripShortName", tripShortName); @@ -582,7 +580,7 @@ public String toString() { + ", serviceId=" + serviceId + ", blockId=" + blockId + ", shapeId=" + shapeId - + ", scheduledTimesList=" + scheduledTimesList +// + ", scheduledTimesList=" + scheduledTimesList + "]"; } @@ -877,7 +875,15 @@ public Route getRoute() { } return route; } - + + /** + * Setter for unit tests. + * @param route + */ + public void setRoute(Route route) { + this.route = route; + } + /** * Returns route name. Gets it from the Core database configuration. If Core * database configuration not available such as when processing GTFS data @@ -1016,6 +1022,17 @@ public TripPattern getTripPattern() { * @return */ public ScheduleTime getScheduleTime(int stopPathIndex) { + if (scheduledTimesList instanceof PersistentList) { + // TODO this is an anti-pattern + // instead find a way to manage sessions more consistently + PersistentList persistentListTimes = (PersistentList)scheduledTimesList; + SessionImplementor session = + persistentListTimes.getSession(); + if (session == null) { + Session globalLazyLoadSession = Core.getInstance().getDbConfig().getGlobalSession(); + globalLazyLoadSession.update(this); + } + } return scheduledTimesList.get(stopPathIndex); } @@ -1153,5 +1170,130 @@ public boolean onUpdate(Session s) throws CallbackException { public boolean onDelete(Session s) throws CallbackException { return Lifecycle.NO_VETO; } - + + /** + * Query how many travel times for trips entries exist for a given + * travelTimesRev. Used for metrics. + * @param session + * @param travelTimesRev + * @return + */ + public static Long countTravelTimesForTrips(Session session, + int travelTimesRev) { + String sql = "Select count(*) from TravelTimesForTrips where travelTimesRev=:rev"; + + Query query = session.createSQLQuery(sql); + query.setInteger("rev", travelTimesRev); + Long count = null; + try { + + Integer bcount; + if(query.uniqueResult() instanceof BigInteger) + { + bcount = ((BigInteger)query.uniqueResult()).intValue(); + }else + { + bcount = (Integer) query.uniqueResult(); + } + + if (bcount != null) + count = bcount.longValue(); + } catch (HibernateException e) { + Core.getLogger().error("exception querying for metrics", e); + } + return count; + } + + public static List getTripsFromDb(TripQuery tripQuery) { + IntervalTimer timer = new IntervalTimer(); + + // Get the database session. This is supposed to be pretty light weight + Session session = HibernateUtils.getSession(tripQuery.isReadOnly()); + + // Create the query. Table name is case sensitive and needs to be the + // class name instead of the name of the db table. + + String hql = "SELECT " + + "t " + + "FROM " + + "Trip t " + + "WHERE t.routeShortName = :routeShortName " + + "AND t.configRev IN (:configRevs) " + + getHeadsignWhere(tripQuery) + + getDirectionWhere(tripQuery) + + getStartTimeWhere(tripQuery) + + "ORDER BY t.startTime"; + try { + Query query = session.createQuery(hql); + query.setString("routeShortName", tripQuery.getRouteShortName()); + query.setParameterList("configRevs", tripQuery.getConfigRevs()); + + List results = query.list(); + + logger.debug("Getting trips from database took {} msec", + timer.elapsedMsec()); + + return results; + + } catch (HibernateException e) { + // Log error to the Core logger + Core.getLogger().error("Unable to retrieve trips", e); + return null; + } finally { + // Clean things up. Not sure if this absolutely needed nor if + // it might actually be detrimental and slow things down. + session.close(); + } + } + + + + private static String getHeadsignWhere(TripQuery tripQuery){ + if(StringUtils.isNotBlank(tripQuery.getHeadsign())){ + return String.format("AND t.headsign = '%s' ", tripQuery.getHeadsign()); + } + return ""; + } + + private static String getDirectionWhere(TripQuery tripQuery){ + if(StringUtils.isNotBlank(tripQuery.getDirection())){ + return String.format("AND t.directionId = '%s' ", tripQuery.getDirection()); + } + return ""; + } + + private static String getStartTimeWhere(TripQuery tripQuery){ + String startTime = ""; + if(tripQuery.getFirstStartTime() != null + && (tripQuery.getLastStartTime() == null || tripQuery.getFirstStartTime() < tripQuery.getLastStartTime())){ + startTime += String.format("AND t.startTime >= %s ", tripQuery.getFirstStartTime()); + } + if(tripQuery.getLastStartTime() != null + && (tripQuery.getFirstStartTime() == null || tripQuery.getFirstStartTime() < tripQuery.getLastStartTime())){ + startTime += String.format("AND t.startTime <= %s ", tripQuery.getLastStartTime()); + } + return startTime; + } + + + /** + * Assumes only one block for Trip. + * TODO - Fix this to return + * @param readOnly + * @return Block + */ + public Block getBlockFromDb(boolean readOnly){ + Session session = HibernateUtils.getSession(readOnly); + try { + session.update(this); + return blocks.get(0); + } + catch (Exception e) { + Core.getLogger().error("Unable to retrieve block", e); + return null; + } finally { + session.close(); + } + } + } diff --git a/transitime/src/main/java/org/transitime/db/structs/TripPattern.java b/transitclock/src/main/java/org/transitclock/db/structs/TripPattern.java similarity index 94% rename from transitime/src/main/java/org/transitime/db/structs/TripPattern.java rename to transitclock/src/main/java/org/transitclock/db/structs/TripPattern.java index 48699019b..0aa859fab 100644 --- a/transitime/src/main/java/org/transitime/db/structs/TripPattern.java +++ b/transitclock/src/main/java/org/transitclock/db/structs/TripPattern.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.db.structs; +package org.transitclock.db.structs; import java.io.Serializable; import java.util.ArrayList; @@ -41,9 +41,9 @@ import org.hibernate.annotations.CascadeType; import org.hibernate.annotations.DynamicUpdate; import org.hibernate.classic.Lifecycle; -import org.transitime.db.hibernate.HibernateUtils; -import org.transitime.gtfs.GtfsData; -import org.transitime.gtfs.gtfsStructs.GtfsRoute; +import org.transitclock.db.hibernate.HibernateUtils; +import org.transitclock.gtfs.GtfsData; +import org.transitclock.gtfs.gtfsStructs.GtfsRoute; /** @@ -77,7 +77,7 @@ public class TripPattern implements Serializable, Lifecycle { // are accessed with the default LAZY loading. And use // CascadeType.SAVE_UPDATE so that when the TripPattern is stored the // Paths are automatically stored. - @OneToMany(fetch=FetchType.EAGER) + @OneToMany(fetch=FetchType.LAZY) @Cascade({CascadeType.SAVE_UPDATE}) @JoinTable(name="TripPattern_to_Path_joinTable") @OrderColumn( name="listIndex") @@ -374,9 +374,15 @@ private static String generateTripPatternId(String shapeId, } // Add the from and to stop IDs - StopPath path1 = stopPaths.get(0); - StopPath path2 = stopPaths.get(stopPaths.size()-1); - tripPatternId += path1.getStopId() + "_to_" + path2.getStopId() + "_"; + if (stopPaths != null && stopPaths.size() > 1) { + StopPath path1 = stopPaths.get(0); + StopPath path2 = stopPaths.get(stopPaths.size()-1); + tripPatternId += path1.getStopId() + "_to_" + path2.getStopId() + "_"; + } else { + GtfsData.logger.info("There was an issue with creating trip " + + "pattern for tripId={} for routeId={}, it is missing stops "); + return tripPatternId; + } // Determine hash in hexadecimal format of list of stops StringBuilder sb = new StringBuilder(); @@ -517,7 +523,9 @@ public int hashCode() { result = prime * result + ((id == null) ? 0 : id.hashCode()); result = prime * result + ((headsign == null) ? 0 : headsign.hashCode()); result = prime * result + ((routeId == null) ? 0 : routeId.hashCode()); - result = prime * result + ((trips == null) ? 0 : trips.hashCode()); + +// result = prime * result + ((trips == null) ? 0 : trips.hashCode()); // stack overflow with large db + return result; } diff --git a/transitime/src/main/java/org/transitime/db/structs/TripPatternKey.java b/transitclock/src/main/java/org/transitclock/db/structs/TripPatternKey.java similarity index 99% rename from transitime/src/main/java/org/transitime/db/structs/TripPatternKey.java rename to transitclock/src/main/java/org/transitclock/db/structs/TripPatternKey.java index 980d8dc8b..cb32a1103 100644 --- a/transitime/src/main/java/org/transitime/db/structs/TripPatternKey.java +++ b/transitclock/src/main/java/org/transitclock/db/structs/TripPatternKey.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.db.structs; +package org.transitclock.db.structs; import java.util.List; diff --git a/transitime/src/main/java/org/transitime/db/structs/Vector.java b/transitclock/src/main/java/org/transitclock/db/structs/Vector.java similarity index 98% rename from transitime/src/main/java/org/transitime/db/structs/Vector.java rename to transitclock/src/main/java/org/transitclock/db/structs/Vector.java index 10b050f09..6cfd7e5c0 100644 --- a/transitime/src/main/java/org/transitime/db/structs/Vector.java +++ b/transitclock/src/main/java/org/transitclock/db/structs/Vector.java @@ -14,11 +14,11 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.db.structs; +package org.transitclock.db.structs; import java.io.Serializable; -import org.transitime.utils.Geo; +import org.transitclock.utils.Geo; /** * Simple vector that contains two locations. diff --git a/transitime/src/main/java/org/transitime/db/structs/VectorWithHeading.java b/transitclock/src/main/java/org/transitclock/db/structs/VectorWithHeading.java similarity index 97% rename from transitime/src/main/java/org/transitime/db/structs/VectorWithHeading.java rename to transitclock/src/main/java/org/transitclock/db/structs/VectorWithHeading.java index cc6a596f2..288cf4bfa 100644 --- a/transitime/src/main/java/org/transitime/db/structs/VectorWithHeading.java +++ b/transitclock/src/main/java/org/transitclock/db/structs/VectorWithHeading.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.db.structs; +package org.transitclock.db.structs; -import org.transitime.utils.Geo; +import org.transitclock.utils.Geo; /** * Inherits from Vector but automatically calculates the heading. Useful diff --git a/transitime/src/main/java/org/transitime/db/structs/VehicleConfig.java b/transitclock/src/main/java/org/transitclock/db/structs/VehicleConfig.java similarity index 98% rename from transitime/src/main/java/org/transitime/db/structs/VehicleConfig.java rename to transitclock/src/main/java/org/transitclock/db/structs/VehicleConfig.java index 5d9036824..8c80c2c8f 100644 --- a/transitime/src/main/java/org/transitime/db/structs/VehicleConfig.java +++ b/transitclock/src/main/java/org/transitclock/db/structs/VehicleConfig.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.db.structs; +package org.transitclock.db.structs; import java.util.List; @@ -28,7 +28,7 @@ import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.annotations.DynamicUpdate; -import org.transitime.db.hibernate.HibernateUtils; +import org.transitclock.db.hibernate.HibernateUtils; /** * For storing static configuration information for a vehicle. diff --git a/transitime/src/main/java/org/transitime/db/structs/VehicleEvent.java b/transitclock/src/main/java/org/transitclock/db/structs/VehicleEvent.java similarity index 97% rename from transitime/src/main/java/org/transitime/db/structs/VehicleEvent.java rename to transitclock/src/main/java/org/transitclock/db/structs/VehicleEvent.java index 368cfba82..7350da6d6 100644 --- a/transitime/src/main/java/org/transitime/db/structs/VehicleEvent.java +++ b/transitclock/src/main/java/org/transitclock/db/structs/VehicleEvent.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.db.structs; +package org.transitclock.db.structs; import java.io.Serializable; import java.util.Date; @@ -38,11 +38,11 @@ import org.hibernate.annotations.DynamicUpdate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.applications.Core; -import org.transitime.core.TemporalMatch; -import org.transitime.db.hibernate.HibernateUtils; -import org.transitime.utils.IntervalTimer; -import org.transitime.utils.Time; +import org.transitclock.applications.Core; +import org.transitclock.core.TemporalMatch; +import org.transitclock.db.hibernate.HibernateUtils; +import org.transitclock.utils.IntervalTimer; +import org.transitclock.utils.Time; /** * For storing events associated with vehicles into log file and into database. @@ -161,6 +161,9 @@ public class VehicleEvent implements Serializable { public static final String NOT_LEAVING_TERMINAL = "Not leaving terminal"; public static final String ASSIGNMENT_GRABBED = "Assignment Grabbed"; public static final String ASSIGNMENT_CHANGED = "Assignment Changed"; + public static final String AVL_CONFLICT ="AVL Conflict"; + public static final String PREDICTION_VARIATION = "Prediction variation"; + public static final String UNMATCHED_ASSIGNMENT = "Unmatched Assignment"; // Hibernate requires class to be Serializable private static final long serialVersionUID = -763445348557811925L; @@ -168,6 +171,8 @@ public class VehicleEvent implements Serializable { private static final Logger logger = LoggerFactory.getLogger(VehicleEvent.class); + + /********************** Member Functions **************************/ /** diff --git a/transitime/src/main/java/org/transitime/db/structs/VehicleState.java b/transitclock/src/main/java/org/transitclock/db/structs/VehicleState.java similarity index 95% rename from transitime/src/main/java/org/transitime/db/structs/VehicleState.java rename to transitclock/src/main/java/org/transitclock/db/structs/VehicleState.java index 5eb8b85c6..42c0adc58 100644 --- a/transitime/src/main/java/org/transitime/db/structs/VehicleState.java +++ b/transitclock/src/main/java/org/transitclock/db/structs/VehicleState.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.db.structs; +package org.transitclock.db.structs; import java.io.Serializable; import java.util.Date; @@ -31,7 +31,7 @@ import net.jcip.annotations.Immutable; import org.hibernate.annotations.DynamicUpdate; -import org.transitime.db.hibernate.HibernateUtils; +import org.transitclock.db.hibernate.HibernateUtils; /** * For persisting the vehicle state for the vehicle. Can be joined with @@ -115,7 +115,7 @@ public class VehicleState implements Serializable { * * @param vehicleState */ - public VehicleState(org.transitime.core.VehicleState vs) { + public VehicleState(org.transitclock.core.VehicleState vs) { this.vehicleId = truncate(vs.getVehicleId(), HibernateUtils.DEFAULT_ID_SIZE); this.avlTime = diff --git a/transitime/src/main/java/org/transitime/db/structs/package-info.java b/transitclock/src/main/java/org/transitclock/db/structs/package-info.java similarity index 96% rename from transitime/src/main/java/org/transitime/db/structs/package-info.java rename to transitclock/src/main/java/org/transitclock/db/structs/package-info.java index b0edd6103..8c2f8c57f 100644 --- a/transitime/src/main/java/org/transitime/db/structs/package-info.java +++ b/transitclock/src/main/java/org/transitclock/db/structs/package-info.java @@ -23,4 +23,4 @@ * @author SkiBu Smith * */ -package org.transitime.db.structs; \ No newline at end of file +package org.transitclock.db.structs; \ No newline at end of file diff --git a/transitime/src/main/java/org/transitime/db/webstructs/ApiKey.java b/transitclock/src/main/java/org/transitclock/db/webstructs/ApiKey.java similarity index 98% rename from transitime/src/main/java/org/transitime/db/webstructs/ApiKey.java rename to transitclock/src/main/java/org/transitclock/db/webstructs/ApiKey.java index e6e72c1ef..7674febbe 100644 --- a/transitime/src/main/java/org/transitime/db/webstructs/ApiKey.java +++ b/transitclock/src/main/java/org/transitclock/db/webstructs/ApiKey.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.db.webstructs; +package org.transitclock.db.webstructs; import java.io.Serializable; import java.util.List; @@ -32,7 +32,7 @@ import org.hibernate.annotations.DynamicUpdate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.db.hibernate.HibernateUtils; +import org.transitclock.db.hibernate.HibernateUtils; /** * Database class for storing keys and related info for the API. diff --git a/transitime/src/main/java/org/transitime/db/webstructs/ApiKeyManager.java b/transitclock/src/main/java/org/transitclock/db/webstructs/ApiKeyManager.java similarity index 88% rename from transitime/src/main/java/org/transitime/db/webstructs/ApiKeyManager.java rename to transitclock/src/main/java/org/transitclock/db/webstructs/ApiKeyManager.java index d1cb33f44..483335b04 100644 --- a/transitime/src/main/java/org/transitime/db/webstructs/ApiKeyManager.java +++ b/transitclock/src/main/java/org/transitclock/db/webstructs/ApiKeyManager.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.db.webstructs; +package org.transitclock.db.webstructs; import java.util.HashMap; import java.util.List; @@ -25,10 +25,11 @@ import org.hibernate.Session; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.configData.DbSetupConfig; -import org.transitime.db.hibernate.HibernateUtils; -import org.transitime.db.webstructs.ApiKey; -import org.transitime.utils.Time; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.configData.DbSetupConfig; +import org.transitclock.db.hibernate.HibernateUtils; +import org.transitclock.db.webstructs.ApiKey; +import org.transitclock.utils.Time; /** * Manages the ApiKeys. Caches them so API can quickly determine if key is @@ -49,6 +50,10 @@ public class ApiKeyManager { // For preventing too frequent db reads private long lastTimeKeysReadIntoCache = 0; + private static IntegerConfigValue lastTimeKeysReadLimitSec = new IntegerConfigValue( + "transitclock.api.apiKeyLastUpdateLimitSec", 3, + "Amount of time to wait in sec before updating the apiKeyCache"); + // This is a singleton class private static ApiKeyManager singleton = new ApiKeyManager(); @@ -100,10 +105,10 @@ public synchronized boolean isKeyValid(String key) { // Want to make sure a user doesn't overwhelm the system by // repeatedly trying to use an invalid key. So if the cache was - // just updated a few (3) seconds ago then don't update it again + // just updated a few x seconds ago then don't update it again // right now. Simply return false. - if (System.currentTimeMillis() < lastTimeKeysReadIntoCache + 3 - * Time.MS_PER_SEC) + if (System.currentTimeMillis() < lastTimeKeysReadIntoCache + + lastTimeKeysReadLimitSec.getValue() * Time.MS_PER_SEC) return false; lastTimeKeysReadIntoCache = System.currentTimeMillis(); @@ -127,7 +132,7 @@ public synchronized boolean isKeyValid(String key) { * CoreConfig.getDbHost(), CoreConfig.getDbUserName(), and * CoreConfig.getDbPassword(). The db host, user name, and password can also * be set in the hibernate.cfg.xml file if the parameter - * transitime.hibernate.configFile in the CoreConfig is set. + * transitclock.hibernate.configFile in the CoreConfig is set. * * @return */ diff --git a/transitime/src/main/java/org/transitime/db/webstructs/WebAgency.java b/transitclock/src/main/java/org/transitclock/db/webstructs/WebAgency.java old mode 100644 new mode 100755 similarity index 93% rename from transitime/src/main/java/org/transitime/db/webstructs/WebAgency.java rename to transitclock/src/main/java/org/transitclock/db/webstructs/WebAgency.java index 5ca2a5683..0fa7cce84 --- a/transitime/src/main/java/org/transitime/db/webstructs/WebAgency.java +++ b/transitclock/src/main/java/org/transitclock/db/webstructs/WebAgency.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.db.webstructs; +package org.transitclock.db.webstructs; import java.rmi.RemoteException; import java.util.ArrayList; @@ -38,14 +38,14 @@ import org.hibernate.annotations.DynamicUpdate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.configData.DbSetupConfig; -import org.transitime.db.hibernate.HibernateUtils; -import org.transitime.db.structs.Agency; -import org.transitime.ipc.clients.ConfigInterfaceFactory; -import org.transitime.ipc.interfaces.ConfigInterface; -import org.transitime.utils.Encryption; -import org.transitime.utils.IntervalTimer; -import org.transitime.utils.Time; +import org.transitclock.configData.DbSetupConfig; +import org.transitclock.db.hibernate.HibernateUtils; +import org.transitclock.db.structs.Agency; +import org.transitclock.ipc.clients.ConfigInterfaceFactory; +import org.transitclock.ipc.interfaces.ConfigInterface; +import org.transitclock.utils.Encryption; +import org.transitclock.utils.IntervalTimer; +import org.transitclock.utils.Time; /** * For keeping track of agency data for a website. Contains info such as the @@ -227,7 +227,7 @@ public String getAgencyName() { /** * Specifies name of database to use for reading in the WebAgency objects. - * Currently using the command line option transitime.core.agencyId . + * Currently using the command line option transitclock.core.agencyId . * * @return Name of db to retrieve WebAgency objects from */ @@ -488,7 +488,9 @@ public String getDbPassword() { 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]; dbType = args[2]; dbHost = args[3]; dbUserName = args[4]; dbPassword = args[5];"); + + System.err.println("Specify params for the WebAgency: agencyId = args[0]; hostName = args[1]; dbName = args[2] ;dbType = args[3]; dbHost = args[4]; dbUserName = args[5]; dbPassword = args[6];"); + System.exit(-1); } String agencyId = args[0]; diff --git a/transitime/src/main/java/org/transitime/db/webstructs/package-info.java b/transitclock/src/main/java/org/transitclock/db/webstructs/package-info.java similarity index 95% rename from transitime/src/main/java/org/transitime/db/webstructs/package-info.java rename to transitclock/src/main/java/org/transitclock/db/webstructs/package-info.java index 67fae8e95..98fecb131 100644 --- a/transitime/src/main/java/org/transitime/db/webstructs/package-info.java +++ b/transitclock/src/main/java/org/transitclock/db/webstructs/package-info.java @@ -21,4 +21,4 @@ * @author SkiBu Smith * */ -package org.transitime.db.webstructs; \ No newline at end of file +package org.transitclock.db.webstructs; \ No newline at end of file diff --git a/transitclock/src/main/java/org/transitclock/exceptions/InvalidRouteException.java b/transitclock/src/main/java/org/transitclock/exceptions/InvalidRouteException.java new file mode 100644 index 000000000..9024258db --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/exceptions/InvalidRouteException.java @@ -0,0 +1,7 @@ +package org.transitclock.exceptions; + +public class InvalidRouteException extends Exception{ + public InvalidRouteException(String route){ + super("Invalid route " + route); + } +} diff --git a/transitclock/src/main/java/org/transitclock/feed/gtfsRt/GtfsRtTripUpdatesReader.java b/transitclock/src/main/java/org/transitclock/feed/gtfsRt/GtfsRtTripUpdatesReader.java new file mode 100644 index 000000000..bf629cb58 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/feed/gtfsRt/GtfsRtTripUpdatesReader.java @@ -0,0 +1,21 @@ +package org.transitclock.feed.gtfsRt; + +import com.google.transit.realtime.GtfsRealtime.TripDescriptor; +import org.transitclock.core.dataCache.CanceledTripManager; + +public class GtfsRtTripUpdatesReader extends GtfsRtTripUpdatesReaderBase{ + + /********************** Member Functions **************************/ + private CanceledTripManager canceledTripManager; + + + public GtfsRtTripUpdatesReader() { + canceledTripManager = CanceledTripManager.getInstance(); + } + + @Override + public void handleTrip(TripDescriptor trip) { + return; + // Empty + } +} \ No newline at end of file diff --git a/transitclock/src/main/java/org/transitclock/feed/gtfsRt/GtfsRtTripUpdatesReaderBase.java b/transitclock/src/main/java/org/transitclock/feed/gtfsRt/GtfsRtTripUpdatesReaderBase.java new file mode 100644 index 000000000..c6ffd2d94 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/feed/gtfsRt/GtfsRtTripUpdatesReaderBase.java @@ -0,0 +1,195 @@ +package org.transitclock.feed.gtfsRt; + +import com.google.protobuf.CodedInputStream; +import com.google.transit.realtime.GtfsRealtime; +import com.google.transit.realtime.GtfsRealtime.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.applications.Core; +import org.transitclock.core.blockAssigner.BlockAssigner; +import org.transitclock.core.dataCache.CanceledTripKey; +import org.transitclock.core.dataCache.CanceledTripManager; +import org.transitclock.core.dataCache.SkippedStopsManager; +import org.transitclock.db.structs.Trip; +import org.transitclock.gtfs.DbConfig; +import org.transitclock.ipc.data.IpcCanceledTrip; +import org.transitclock.ipc.data.IpcSkippedStop; +import org.transitclock.utils.IntervalTimer; + +import java.io.InputStream; +import java.net.URI; +import java.net.URL; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; + +public abstract class GtfsRtTripUpdatesReaderBase { + + private static final Logger logger = LoggerFactory + .getLogger(GtfsRtTripUpdatesReaderBase.class); + + /********************** Member Functions **************************/ + + public GtfsRtTripUpdatesReaderBase() { } + + /** + * Actually processes the GTFS-realtime file and calls handleAvlReport() + * for each AvlReport. + */ + public synchronized void process(String urlString) { + try { + logger.info("Getting GTFS-realtime AVL data from URL={} ...", + urlString); + IntervalTimer timer = new IntervalTimer(); + + URI uri = new URI(urlString); + URL url = uri.toURL(); + + // Create a CodedInputStream instead of just a regular InputStream + // so that can change the size limit. Otherwise if file is greater + // than 64MB get an exception. + InputStream inputStream = url.openStream(); + CodedInputStream codedStream = + CodedInputStream.newInstance(inputStream); + // What to use instead of default 64MB limit + final int GTFS_SIZE_LIMIT = 200000000; + codedStream.setSizeLimit(GTFS_SIZE_LIMIT); + + // Actual read in the data into a protobuffer FeedMessage object. + // Would prefer to do this one VehiclePosition at a time using + // something like VehiclePosition.parseFrom(codedStream) so that + // wouldn't have to load entire protobuffer file into memory. But + // it never seemed to complete, even for just a single call to + // parseFrom(). Therefore loading in entire file at once. + GtfsRealtime.FeedMessage feed = GtfsRealtime.FeedMessage.parseFrom(codedStream); + logger.info("Parsing GTFS-realtime file into a FeedMessage took " + + "{} msec", timer.elapsedMsec()); + + // Process each individual VehiclePostions message + processMessage(feed); + inputStream.close(); + } catch (Exception e) { + logger.error("Exception when reading GTFS-realtime data from " + + "URL {}", + urlString, e); + } + } + + + /** + * Goes through each entity and passes TripDescriptor to + * + * @param message + * Contains all of the VehiclePosition objects + * @return List of AvlReports + */ + private void processMessage(FeedMessage message) { + logger.info("Processing each individual AvlReport..."); + IntervalTimer timer = new IntervalTimer(); + + Map> skippedStopsMap = new HashMap<>(); + Map cancelledTripsMap = new HashMap<>(); + + // For each entity/vehicle process the data + int counter = 0; + for (FeedEntity entity : message.getEntityList()) { + // If no trip in the entity then nothing to process + if (!entity.hasTripUpdate()) + continue; + + // Get the object describing the trip + TripUpdate tripUpdate = entity.getTripUpdate(); + + TripDescriptor tripDescriptor = getTrip(tripUpdate); + VehicleDescriptor vehicleDescriptor = tripUpdate.getVehicle(); + + if(tripDescriptor == null) + continue; + + if (tripDescriptor.hasTripId() && + tripDescriptor.hasScheduleRelationship() && + tripDescriptor.getScheduleRelationship() == TripDescriptor.ScheduleRelationship.CANCELED) { + + String tripId = getTripId(tripDescriptor); + if (tripId == null) + continue; + IpcCanceledTrip canceledTrip = new IpcCanceledTrip(tripId, + tripDescriptor.getRouteId(), tripDescriptor.getStartDate(), tripUpdate.getTimestamp()); + + cancelledTripsMap.put(getCanceledTripKey(vehicleDescriptor, tripId), canceledTrip); + logger.debug("Adding canceledTrip to map {}", canceledTrip); + + } + + HashSet skippedStops = new HashSet<>(); + String skippedTripId = null; + boolean skippedTripAlreadyChecked = false; + for(TripUpdate.StopTimeUpdate stopTimeUpdate : tripUpdate.getStopTimeUpdateList()){ + if(stopTimeUpdate.hasScheduleRelationship() && + stopTimeUpdate.getScheduleRelationship() == TripUpdate.StopTimeUpdate.ScheduleRelationship.SKIPPED){ + if(skippedTripId == null && !skippedTripAlreadyChecked){ + skippedTripId = getTripId(tripDescriptor); + skippedTripAlreadyChecked = true; + } + if (skippedTripId == null) + continue; + IpcSkippedStop skippedStop = new IpcSkippedStop(vehicleDescriptor.getId(), stopTimeUpdate.getStopId(), stopTimeUpdate.getStopSequence()); + skippedStops.add(skippedStop); + logger.debug("Adding skipped stop to map {}", skippedStop); + skippedStopsMap.put(skippedTripId, skippedStops); + } + } + + // The callback for each TripDescriptor + handleTrip(tripDescriptor); + + ++counter; + } + + CanceledTripManager.getInstance().putAll(cancelledTripsMap); + SkippedStopsManager.getInstance().putAll(skippedStopsMap); + + logger.info("Successfully processed {} Trips from " + + "GTFS-realtime Trip Updates feed in {} msec", + counter, timer.elapsedMsec()); + + + } + + private String getTripId(TripDescriptor tripDescriptor) { + DbConfig config = Core.getInstance().getDbConfig(); + String tripId = tripDescriptor.getTripId(); + + if(config.getServiceIdSuffix()){ + Trip trip = BlockAssigner.getInstance().getTripWithServiceIdSuffix(config,tripId); + if (trip == null) { + logger.info("missing trip {}", tripId); + return null; + } + tripId = trip.getId(); + } + return tripId; + } + + private CanceledTripKey getCanceledTripKey(VehicleDescriptor vehicleDescriptor, String tripId){ + if(vehicleDescriptor.hasId()){ + return new CanceledTripKey(vehicleDescriptor.getId(), tripId); + } + return new CanceledTripKey(null, tripId); + } + + /** + * To be overridden by superclass. Is called each time an + * AvlReport is handled. + * + * @param tripDescriptor + */ + protected abstract void handleTrip(TripDescriptor tripDescriptor); + + private static TripDescriptor getTrip(TripUpdate tripUpdate){ + if (!tripUpdate.hasTrip()) { + return null; + } + return tripUpdate.getTrip(); + } +} \ No newline at end of file diff --git a/transitclock/src/main/java/org/transitclock/feed/gtfsRt/GtfsRtVehiclePositionsReader.java b/transitclock/src/main/java/org/transitclock/feed/gtfsRt/GtfsRtVehiclePositionsReader.java new file mode 100644 index 000000000..501f5efd0 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/feed/gtfsRt/GtfsRtVehiclePositionsReader.java @@ -0,0 +1,75 @@ +/* + * 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.feed.gtfsRt; + +import java.util.ArrayList; +import java.util.List; + +import org.transitclock.db.structs.AvlReport; + +/** + * Reads in GTFS-realtime Vehicle Positions file and converts them into List of + * AvlReport objects. This class should be inherited from such that + * handleAvlReport() of the superclass will process the AVL data one report at a + * time. This way don't have to fill up memory with a giant list of AvlReports. + * + * @author SkiBu Smith + * + */ +public class GtfsRtVehiclePositionsReader extends +GtfsRtVehiclePositionsReaderBase { + + private List avlReports = new ArrayList(); + + /********************** Member Functions **************************/ + + /** + * Simple constructor. Protected because should access this class through + * getAvlReports(). + * + * @param urlString + */ + protected GtfsRtVehiclePositionsReader(String urlString) { + super(urlString); + } + + /** + * Called for each AvlReport processed. Adds the report to the list of + * AvlReports. + */ + @Override + protected void handleAvlReport(AvlReport avlReport) { + avlReports.add(avlReport); + } + + /** + * Returns list of AvlReports read from GTFS-realtime file + * specified by urlString. + * + * @param urlString URL of GTFS-realtime file + * @return List of AvlReports + */ + public static List getAvlReports(String urlString) { + GtfsRtVehiclePositionsReader reader = + new GtfsRtVehiclePositionsReader(urlString); + + reader.process(); + + return reader.avlReports; + } +} diff --git a/transitime/src/main/java/org/transitime/feed/gtfsRt/GtfsRtVehiclePositionsReader.java b/transitclock/src/main/java/org/transitclock/feed/gtfsRt/GtfsRtVehiclePositionsReaderBase.java similarity index 51% rename from transitime/src/main/java/org/transitime/feed/gtfsRt/GtfsRtVehiclePositionsReader.java rename to transitclock/src/main/java/org/transitclock/feed/gtfsRt/GtfsRtVehiclePositionsReaderBase.java index 09275c6ec..a0eabc24c 100644 --- a/transitime/src/main/java/org/transitime/feed/gtfsRt/GtfsRtVehiclePositionsReader.java +++ b/transitclock/src/main/java/org/transitclock/feed/gtfsRt/GtfsRtVehiclePositionsReaderBase.java @@ -15,31 +15,24 @@ * along with Transitime.org . If not, see . */ -package org.transitime.feed.gtfsRt; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; -import java.net.URL; -import java.net.URLConnection; -import java.util.ArrayList; -import java.util.Collection; +package org.transitclock.feed.gtfsRt; +import com.google.protobuf.CodedInputStream; +import com.google.transit.realtime.GtfsRealtime.*; 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.IntervalTimer; -import org.transitime.utils.MathUtils; -import org.transitime.utils.Time; +import org.transitclock.avl.AvlExecutor; +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.utils.IntervalTimer; +import org.transitclock.utils.MathUtils; -import com.google.protobuf.CodedInputStream; -import com.google.transit.realtime.GtfsRealtime.FeedEntity; -import com.google.transit.realtime.GtfsRealtime.FeedMessage; -import com.google.transit.realtime.GtfsRealtime.Position; -import com.google.transit.realtime.GtfsRealtime.TripDescriptor; -import com.google.transit.realtime.GtfsRealtime.VehicleDescriptor; -import com.google.transit.realtime.GtfsRealtime.VehiclePosition; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URL; /** * Reads in GTFS-realtime Vehicle Positions file and converts them into List of @@ -50,43 +43,58 @@ * @author SkiBu Smith * */ -public class GtfsRtVehiclePositionsReader { +public abstract class GtfsRtVehiclePositionsReaderBase { + + private static StringConfigValue gtfsRealtimeHeaderKey = + new StringConfigValue("transitclock.avl.apiKeyHeader", + null, + "api key header value if necessary, null if not needed"); + + private static StringConfigValue gtfsRealtimeHeaderValue = + new StringConfigValue("transitclock.avl.apiKeyValue", + null, + "api key value if necessary, null if not needed"); + private static IntegerConfigValue minQueueSizeForRefresh = + new IntegerConfigValue("transitclock.avl.minQueueSizeForRefresh", + 0, + "beyond this queue size refreshes will block waiting for queue to empty"); + + private final String urlString; + private static final Logger logger = LoggerFactory - .getLogger(GtfsRtVehiclePositionsReader.class); + .getLogger(GtfsRtVehiclePositionsReaderBase.class); /********************** Member Functions **************************/ + + public GtfsRtVehiclePositionsReaderBase(String urlString) { + this.urlString = urlString; + } /** - * Returns the vehicleID. If vehicle ID not available then returns vehicle - * label. Returns null if no VehicleDescription associated with the vehicle - * or if no ID or label associated with the VehicleDescription. + * Returns the vehicleID. Returns null if no VehicleDescription associated + * with the vehicle or if no ID associated with the VehicleDescription. * * @param vehicle - * @return vehicle ID or label or null if there isn't one + * @return vehicle ID or null if there isn't one */ private static String getVehicleId(VehiclePosition vehicle) { if (!vehicle.hasVehicle()) { return null; } VehicleDescriptor desc = vehicle.getVehicle(); - - // Return vehicle ID if there is one - if (desc.hasId()) - return desc.getId(); - - // No vehicle ID so return vehicle label if there is one - if (desc.hasLabel()) - return desc.getLabel(); - - // No ID nor label so return null - return null; + if (!desc.hasId()) { + return null; + } + return desc.getId(); } /** * Returns the vehicleID. Returns null if no VehicleDescription associated * with the vehicle or if no ID associated with the VehicleDescription. * + * If not vehicleId try label (VIA San Antonio Feed). + * * @param vehicle * @return vehicle ID or null if there isn't one */ @@ -96,10 +104,21 @@ private static String getLicensePlate(VehiclePosition vehicle) { } VehicleDescriptor desc = vehicle.getVehicle(); if (!desc.hasLicensePlate()) { + + if(desc.hasLabel()) + return desc.getLabel(); return null; - } + } return desc.getLicensePlate(); } + + /** + * To be overridden by superclass. Is called each time an + * AvlReport is handled. + * + * @param avlReport + */ + protected abstract void handleAvlReport(AvlReport avlReport); /** * For each vehicle in the GTFS-realtime message put AvlReport into list. @@ -108,14 +127,12 @@ private static String getLicensePlate(VehiclePosition vehicle) { * Contains all of the VehiclePosition objects * @return List of AvlReports */ - private static Collection processMessage(FeedMessage message) { + private void processMessage(FeedMessage message) { logger.info("Processing each individual AvlReport..."); IntervalTimer timer = new IntervalTimer(); - - // The return value for the method - Collection avlReportsReadIn = new ArrayList(); - + // For each entity/vehicle process the data + int counter = 0; for (FeedEntity entity : message.getEntityList()) { // If no vehicles in the entity then nothing to process if (!entity.hasVehicle()) @@ -126,7 +143,13 @@ private static Collection processMessage(FeedMessage message) { // Determine vehicle ID. If no vehicle ID then can't handle it. String vehicleId = getVehicleId(vehicle); - if (vehicleId == null) + + String vehicleLabel = getVehicleLabel(vehicle); + + if (vehicleId == null && vehicleLabel!=null) + vehicleId=vehicleLabel; + + if(vehicleId == null) continue; // Determine the GPS time. If time is not available then use the @@ -134,13 +157,14 @@ private static Collection processMessage(FeedMessage message) { // latency will be quite large, resulting in inaccurate predictions // and arrival times. But better than not having a time at all. long gpsTime; - if (vehicle.hasTimestamp()) - gpsTime = vehicle.getTimestamp()*Time.MS_PER_SEC; - else { - logger.warn("For vehicleId={} GPS time not available in " - + "GTFS-realtime feed so using system time, which is " - + "not accurate!", - vehicleId); + + if (vehicle.hasTimestamp()) { + gpsTime = vehicle.getTimestamp(); + if (gpsTime < 14396727760l) { // TODO if too small to be milli second epoch + gpsTime = gpsTime * 1000; + } + } else + { gpsTime = System.currentTimeMillis(); } @@ -162,19 +186,16 @@ private static Collection processMessage(FeedMessage message) { float heading = Float.NaN; if (position.hasBearing()) { heading = position.getBearing(); - - // rtd-denver at least sets bearing to 65535.0 when vehicle - // not moving. For this special case reset heading to NaN. - if (heading == 65535.0) - heading = Float.NaN; } // Create the core AVL object. The feed can provide a silly amount // of precision so round to just 5 decimal places. + // AvlReport is expecting time in ms while the proto provides it in // seconds AvlReport avlReport = new AvlReport(vehicleId, gpsTime, + MathUtils.round(lat, 5), MathUtils.round(lon, 5), speed, heading, "GTFS-rt", @@ -184,90 +205,111 @@ private static Collection processMessage(FeedMessage message) { null, // passengerCount Float.NaN); // passengerFullness - // Determine vehicle assignment information. Trip assignments - // are more useful than route assignments so check trip - // assignment first. + // Determine vehicle assignment information if (vehicle.hasTrip()) { TripDescriptor tripDescriptor = vehicle.getTrip(); + if (tripDescriptor.hasTripId()) { - avlReport.setAssignment(tripDescriptor.getTripId(), + avlReport.setAssignment(tripDescriptor.getTripId(), AssignmentType.TRIP_ID); - } else if (tripDescriptor.hasRouteId()) { - avlReport.setAssignment(tripDescriptor.getRouteId(), + } + else if (tripDescriptor.hasRouteId()) { + avlReport.setAssignment(tripDescriptor.getRouteId(), AssignmentType.ROUTE_ID); } - } - avlReportsReadIn.add(avlReport); + } logger.debug("Processed {}", avlReport); - } - + + // The callback for each AvlReport + handleAvlReport(avlReport); + + ++counter; + } + logger.info("Successfully processed {} AVL reports from " + "GTFS-realtime feed in {} msec", - avlReportsReadIn.size(), timer.elapsedMsec()); - - return avlReportsReadIn; + counter, timer.elapsedMsec()); } - /** - * Actually processes the GTFS-realtime file and calls handleAvlReport() - * for each AvlReport. - */ - public static Collection process(InputStream inputStream) { - IntervalTimer timer = new IntervalTimer(); - - // Create a CodedInputStream instead of just a regular InputStream - // so that can change the size limit. Otherwise if file is greater - // than 64MB get an exception. - CodedInputStream codedStream = - CodedInputStream.newInstance(inputStream); - // What to use instead of default 64MB limit - final int GTFS_SIZE_LIMIT = 200000000; - codedStream.setSizeLimit(GTFS_SIZE_LIMIT); - - // Actual read in the data into a protobuffer FeedMessage object. - // Would prefer to do this one VehiclePosition at a time using - // something like VehiclePosition.parseFrom(codedStream) so that - // wouldn't have to load entire protobuffer file into memory. But - // it never seemed to complete, even for just a single call to - // parseFrom(). Therefore loading in entire file at once. - FeedMessage feedMessage; - try { - feedMessage = FeedMessage.parseFrom(codedStream); - logger.info("Parsing GTFS-realtime file into a FeedMessage took " + - "{} msec", timer.elapsedMsec()); - return processMessage(feedMessage); - } catch (IOException e) { - logger.error("Exception when reading GTFS-realtime data from " + - "input stream.", e); - return new ArrayList(); - } + + private String getVehicleLabel(VehiclePosition vehicle) { + return vehicle.getVehicle().getLabel(); } - + + /** * Actually processes the GTFS-realtime file and calls handleAvlReport() * for each AvlReport. */ - public static Collection getAvlReports(String urlString) { + public void process() { try { logger.info("Getting GTFS-realtime AVL data from URL={} ...", urlString); - + IntervalTimer timer = new IntervalTimer(); + URI uri = new URI(urlString); - URL url = uri.toURL(); - URLConnection con = url.openConnection(); + URL url = uri.toURL(); + + HttpURLConnection + connection = (HttpURLConnection) url.openConnection(); + + if (gtfsRealtimeHeaderKey.getValue() != null && + gtfsRealtimeHeaderValue.getValue() != null) { + connection.addRequestProperty(gtfsRealtimeHeaderKey.getValue(), gtfsRealtimeHeaderValue.getValue()); + connection.addRequestProperty("Cache-Control", "no-cache"); + } + + + // Create a CodedInputStream instead of just a regular InputStream + // so that can change the size limit. Otherwise if file is greater + // than 64MB get an exception. + InputStream inputStream = connection.getInputStream(); + CodedInputStream codedStream = + CodedInputStream.newInstance(inputStream); + // What to use instead of default 64MB limit + final int GTFS_SIZE_LIMIT = 200000000; + codedStream.setSizeLimit(GTFS_SIZE_LIMIT); + - InputStream inputStream = con.getInputStream(); + // Actual read in the data into a protobuffer FeedMessage object. + // Would prefer to do this one VehiclePosition at a time using + // something like VehiclePosition.parseFrom(codedStream) so that + // wouldn't have to load entire protobuffer file into memory. But + // it never seemed to complete, even for just a single call to + // parseFrom(). Therefore loading in entire file at once. + FeedMessage feed = FeedMessage.parseFrom(codedStream); + logger.info("Parsing GTFS-realtime file into a FeedMessage took " + + "{} msec", timer.elapsedMsec()); + + // Process each individual VehiclePositions message + processMessage(feed); + inputStream.close(); + + int size = AvlExecutor.getInstance().getQueueSize(); + while (size > minQueueSizeForRefresh.getValue()) { + // if configured, block until avl queue empties out + // the prevents queue starvation + Thread.sleep(100); + size = AvlExecutor.getInstance().getQueueSize(); + } + logger.info("Processing GTFS-realtime complete in {} msec", timer.elapsedMsec()); - return process(inputStream); } catch (Exception e) { logger.error("Exception when reading GTFS-realtime data from " + "URL {}", urlString, e); - return new ArrayList(); } } + /** + * Returns the URL that this class is reading the GTFS-realtime data from. + * + * @return The URL being used. + */ + public String getUrlString() { + return urlString; + } } diff --git a/transitime/src/main/java/org/transitime/feed/gtfsRt/OctalDecoder.java b/transitclock/src/main/java/org/transitclock/feed/gtfsRt/OctalDecoder.java similarity index 99% rename from transitime/src/main/java/org/transitime/feed/gtfsRt/OctalDecoder.java rename to transitclock/src/main/java/org/transitclock/feed/gtfsRt/OctalDecoder.java index 559e85afe..eb864fd1d 100644 --- a/transitime/src/main/java/org/transitime/feed/gtfsRt/OctalDecoder.java +++ b/transitclock/src/main/java/org/transitclock/feed/gtfsRt/OctalDecoder.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.feed.gtfsRt; +package org.transitclock.feed.gtfsRt; /** * GTFS-RT doesn't handle UTF-8 characters well when outputing human readable diff --git a/transitime/src/main/java/org/transitime/feed/gtfsRt/package-info.java b/transitclock/src/main/java/org/transitclock/feed/gtfsRt/package-info.java similarity index 83% rename from transitime/src/main/java/org/transitime/feed/gtfsRt/package-info.java rename to transitclock/src/main/java/org/transitclock/feed/gtfsRt/package-info.java index 2ee1a40c1..b982ba931 100644 --- a/transitime/src/main/java/org/transitime/feed/gtfsRt/package-info.java +++ b/transitclock/src/main/java/org/transitclock/feed/gtfsRt/package-info.java @@ -17,11 +17,11 @@ /** * Contains the classes used for providing a GTFS-realtime feed. This package is - * part of the transitime project so that it can be accessed by the RmiQuery + * part of the transitclock project so that it can be accessed by the RmiQuery * application. At some point it could make more sense to put this package along - * with RmiQuery class in the transitimeApi package. + * with RmiQuery class in the transitclockApi package. * * @author SkiBu Smith * */ -package org.transitime.feed.gtfsRt; \ No newline at end of file +package org.transitclock.feed.gtfsRt; \ No newline at end of file diff --git a/transitime/src/main/java/org/transitime/feed/package-info.java b/transitclock/src/main/java/org/transitclock/feed/package-info.java similarity index 96% rename from transitime/src/main/java/org/transitime/feed/package-info.java rename to transitclock/src/main/java/org/transitclock/feed/package-info.java index 09c4c71ec..a8025adcc 100644 --- a/transitime/src/main/java/org/transitime/feed/package-info.java +++ b/transitclock/src/main/java/org/transitclock/feed/package-info.java @@ -23,4 +23,4 @@ * @author SkiBu Smith * */ -package org.transitime.feed; \ No newline at end of file +package org.transitclock.feed; \ No newline at end of file diff --git a/transitclock/src/main/java/org/transitclock/feed/zmq/ZmqQueueBeanReader.java b/transitclock/src/main/java/org/transitclock/feed/zmq/ZmqQueueBeanReader.java new file mode 100644 index 000000000..d3d39b443 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/feed/zmq/ZmqQueueBeanReader.java @@ -0,0 +1,7 @@ +package org.transitclock.feed.zmq; + +import org.transitclock.db.structs.AvlReport; + +public interface ZmqQueueBeanReader { + AvlReport getAvlReport(String topic, String contents) throws Exception; +} diff --git a/transitclock/src/main/java/org/transitclock/feed/zmq/ZmqQueueReaderFactory.java b/transitclock/src/main/java/org/transitclock/feed/zmq/ZmqQueueReaderFactory.java new file mode 100644 index 000000000..09af3e2d9 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/feed/zmq/ZmqQueueReaderFactory.java @@ -0,0 +1,23 @@ +package org.transitclock.feed.zmq; + +import org.transitclock.config.StringConfigValue; +import org.transitclock.utils.ClassInstantiator; + +public class ZmqQueueReaderFactory { + private static StringConfigValue className = + new StringConfigValue("transitclock.avl.feed.zmq.reader", + "org.transitclock.feed.zmq.oba.NycQueueInferredLocationBeanReader", + "Specifies the class used to process queue inferred location beans."); + + public static ZmqQueueBeanReader singleton = null; + + public static ZmqQueueBeanReader getInstance() { + + if (singleton == null) { + singleton = ClassInstantiator.instantiate(className.getValue(), + ZmqQueueBeanReader.class); + } + + return singleton; + } +} diff --git a/transitclock/src/main/java/org/transitclock/feed/zmq/oba/NycQueueInferredLocationBeanReader.java b/transitclock/src/main/java/org/transitclock/feed/zmq/oba/NycQueueInferredLocationBeanReader.java new file mode 100644 index 000000000..5586e7883 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/feed/zmq/oba/NycQueueInferredLocationBeanReader.java @@ -0,0 +1,170 @@ +package org.transitclock.feed.zmq.oba; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectReader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.avl.ZeroMQAvlModule; +import org.transitclock.config.StringConfigValue; +import org.transitclock.db.structs.AvlReport; +import org.transitclock.feed.zmq.ZmqQueueBeanReader; +import org.transitclock.utils.MathUtils; + +import java.util.Date; +import java.util.HashSet; +import java.util.Set; + +/** + * Contains method to convert JSON into NycQueueInferredLocationBean and + * to convert NycQueueInferredLocationBean into an AvlReport. + * + * @author carabalb + * + */ +public class NycQueueInferredLocationBeanReader implements ZmqQueueBeanReader { + + private static final Logger logger = LoggerFactory + .getLogger(ZeroMQAvlModule.class); + private static final ObjectMapper _mapper = new ObjectMapper(); + private static final ObjectReader _reader = _mapper.readerFor(NycQueueInferredLocationBeanReader.class); + + Date markTimestamp = new Date(); + private int processedCount = 0; + private int acceptableProcessedCount = 0; + private int avlReportProcessedCount = 0; + private static final int COUNT_INTERVAL = 10000; + + public static StringConfigValue zeromqAvlFilter = + new StringConfigValue("transitclock.avl.zeromqAllowedRoutesFilter", + "*", + "List of acceptable routes for incoming avl data. Defaults to * which allows all."); + + private static Set routeFilterList = new HashSet<>(); + + public NycQueueInferredLocationBeanReader(){ + initializeRouteFilter(); + } + + /********************** Member Functions **************************/ + + private void initializeRouteFilter(){ + try { + String[] routesToFilter = zeromqAvlFilter.getValue().split("(;|,| +)"); + for(String routeToFilter: routesToFilter){ + routeFilterList.add(routeToFilter.toUpperCase()); + } + }catch (Exception e){ + routeFilterList.clear(); + routeFilterList.add("*"); + } + } + + + @Override + public AvlReport getAvlReport(String topic, String contents) throws Exception { + // Get NycQueuedInferredLocationBean from ZMQ + NycQueuedInferredLocationBean inferredLocationBean = processMessage(contents); + processedCount++; + + // Check for data issues and/or filter out data + if(!acceptableResult(inferredLocationBean)){ + logCounts(topic); + return null; + } else { + acceptableProcessedCount++; + } + + AvlReport avlReport = convertInferredLocationBeanToAvlReport(inferredLocationBean); + + return avlReport; + + } + + private NycQueuedInferredLocationBean processMessage(String contents) throws Exception { + try { + NycQueuedInferredLocationBean inferredResult = _reader.readValue(contents); + return inferredResult; + } catch (Exception e) { + logger.warn("Received corrupted message from queue; discarding: " + e.getMessage(), e); + logger.warn("Contents=" + contents); + throw e; + } + } + + private boolean acceptableResult(NycQueuedInferredLocationBean inferredLocationBean){ + String routeId = inferredLocationBean.getInferredRouteId(); + + if(routeId == null || routeFilterList.isEmpty() || (routeFilterList.size() > 0 && routeFilterList.contains("*"))) + return true; + try { + String route = routeId.split("_")[1].toUpperCase(); // formerly AgencyAndId + if (routeFilterList.contains(route)) { + return true; + } else { + logger.debug("NycQueuedInferredLocationBean {} not filtered", inferredLocationBean); + return false; + } + } catch (Exception e){ + logger.error("Error processing NycQueuedInferredLocationBean {}", inferredLocationBean, e); + return false; + } + } + + private AvlReport convertInferredLocationBeanToAvlReport(NycQueuedInferredLocationBean inferredLocationBean){ + + String vehicleId = inferredLocationBean.getVehicleId(); + long gpsTime = inferredLocationBean.getRecordTimestamp(); + Double lat = inferredLocationBean.getInferredLatitude(); + Double lon = inferredLocationBean.getInferredLongitude(); + float speed = Float.NaN; + float heading = (float) inferredLocationBean.getBearing(); + + // AvlReport is expecting time in ms while the proto provides it in + // seconds + AvlReport avlReport = new AvlReport( + vehicleId, + gpsTime, + MathUtils.round(lat, 5), + MathUtils.round(lon, 5), + speed, + heading, + "ZMQ", + null, // leadingVehicleId, + null, // driverId + null, + null, // passengerCount + Float.NaN // passengerFullness + ); + + if(inferredLocationBean.getInferredTripId() != null){ + avlReport.setAssignment(inferredLocationBean.getInferredTripId(), + AvlReport.AssignmentType.TRIP_ID); + } else if(inferredLocationBean.getInferredBlockId() != null) { + avlReport.setAssignment(inferredLocationBean.getInferredBlockId(), AvlReport.AssignmentType.BLOCK_ID); + } else if(inferredLocationBean.getInferredRouteId() != null){ + avlReport.setAssignment(inferredLocationBean.getInferredRouteId(), AvlReport.AssignmentType.ROUTE_ID); + } + return avlReport; + } + + private void logCounts(String topic){ + if (processedCount > COUNT_INTERVAL) { + long timeInterval = (new Date().getTime() - markTimestamp.getTime()); + + logger.info("{} input queue: processed {} messages in {} seconds. ({}) records/second", + topic, COUNT_INTERVAL, timeInterval/1000, 1000.0 * processedCount/timeInterval); + + logger.info("{} input queue: processed {} accepted messages in {} seconds. ({}) records/second", + topic, acceptableProcessedCount, timeInterval/1000, 1000.0 * acceptableProcessedCount/timeInterval); + + logger.info("processed {} avl report records in {} seconds. ({}) records/second", + avlReportProcessedCount, timeInterval/1000, 1000.0 * avlReportProcessedCount/timeInterval); + + + markTimestamp = new Date(); + processedCount = 0; + acceptableProcessedCount = 0; + avlReportProcessedCount = 0; + } + } +} diff --git a/transitclock/src/main/java/org/transitclock/feed/zmq/oba/NycQueuedInferredLocationBean.java b/transitclock/src/main/java/org/transitclock/feed/zmq/oba/NycQueuedInferredLocationBean.java new file mode 100644 index 000000000..8e36ece31 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/feed/zmq/oba/NycQueuedInferredLocationBean.java @@ -0,0 +1,359 @@ +/** + * Copyright (C) 2011 Metropolitan Transportation Authority + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * The code below comes courtesy of OBA, but paired down to data fields only. Its a single bean that + * acts as an interface between systems. + */ +package org.transitclock.feed.zmq.oba; + +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * An "over the wire", queued inferred location result--gets passed between the inference + * engine and the TDF/TDS running on all front-end notes, plus the archiver and other inference + * data consumers. + * + * @author jmaki + * + */ +public class NycQueuedInferredLocationBean implements Serializable { + + private static final long serialVersionUID = 1L; + + private final int DECIMAL_PLACES = 6; + + // the timestamp applied to the record when received by the inference engine + private Long recordTimestamp; + + private String vehicleId; + + // service date of trip/block + private Long serviceDate; + + private Integer scheduleDeviation; + + private String blockId; + + private String tripId; + + private Double distanceAlongBlock; + + private Double distanceAlongTrip; + + // snapped lat/long of vehicle to route shape + private Double inferredLatitude; + + private Double inferredLongitude; + + // raw lat/long of vehicle as reported by BHS. + private Double observedLatitude; + + private Double observedLongitude; + + // inferred operational status/phase + private String phase; + + private String status; + + // inference engine telemetry + private NycVehicleManagementStatusBean managementRecord; + + private String runId; + + private String routeId; + + private double bearing; + + private BigDecimal speed; + + // Fields from TDS + + // Stop ID of next scheduled stop + private String nextScheduledStopId; + + // Distance to next scheduled stop + private Double nextScheduledStopDistance; + + // Stop ID from previous scheduled stop + private String previousScheduledStopId; + + // Distance from previous scheduled stop + private Double previousScheduledStopDistance; + + private String inferredBlockId; + + private String inferredTripId; + + private String inferredRouteId; + + private String inferredDirectionId; + + private Long lastLocationUpdateTime; + + private String assignedBlockId; + + + public NycQueuedInferredLocationBean() {} + + public Long getRecordTimestamp() { + return recordTimestamp; + } + + public void setRecordTimestamp(Long recordTimestamp) { + this.recordTimestamp = recordTimestamp; + } + + public String getVehicleId() { + return vehicleId; + } + + public void setVehicleId(String vehicleId) { + this.vehicleId = vehicleId; + } + + public Long getServiceDate() { + return serviceDate; + } + + public void setServiceDate(Long serviceDate) { + this.serviceDate = serviceDate; + } + + public Integer getScheduleDeviation() { + return scheduleDeviation; + } + + public void setScheduleDeviation(Integer scheduleDeviation) { + this.scheduleDeviation = scheduleDeviation; + } + + public String getBlockId() { + return blockId; + } + + public void setBlockId(String blockId) { + this.blockId = blockId; + } + + public String getTripId() { + return tripId; + } + + public void setTripId(String tripId) { + this.tripId = tripId; + } + + public Double getDistanceAlongBlock() { + return distanceAlongBlock; + } + + public void setDistanceAlongBlock(Double distanceAlongBlock) { + this.distanceAlongBlock = distanceAlongBlock; + } + + public Double getDistanceAlongTrip() { + return distanceAlongTrip; + } + + public void setDistanceAlongTrip(Double distanceAlongTrip) { + this.distanceAlongTrip = distanceAlongTrip; + } + + public Double getInferredLatitude() { + return inferredLatitude; + } + + public void setInferredLatitude(Double inferredLatitude) { + this.inferredLatitude = inferredLatitude; + } + + public Double getInferredLongitude() { + return inferredLongitude; + } + + public void setInferredLongitude(Double inferredLongitude) { + this.inferredLongitude = inferredLongitude; + } + + public Double getObservedLatitude() { + return observedLatitude; + } + + public void setObservedLatitude(Double observedLatitude) { + this.observedLatitude = observedLatitude; + } + + public Double getObservedLongitude() { + return observedLongitude; + } + + public void setObservedLongitude(Double observedLongitude) { + this.observedLongitude = observedLongitude; + } + + public String getPhase() { + return phase; + } + + public void setPhase(String phase) { + this.phase = phase; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public NycVehicleManagementStatusBean getManagementRecord() { + return managementRecord; + } + + public void setManagementRecord(NycVehicleManagementStatusBean managementRecord) { + this.managementRecord = managementRecord; + } + + public void setRunId(String runId) { + this.runId = runId; + } + + public String getRunId() { + return runId; + } + + public void setRouteId(String routeId) { + this.routeId = routeId; + } + + public String getRouteId() { + return routeId; + } + + public void setBearing(double bearing) { + this.bearing = bearing; + } + + public double getBearing() { + return bearing; + } + + // Properties from TDS + + public void setNextScheduledStopId(String nextScheduledStopId) { + this.nextScheduledStopId = nextScheduledStopId; + } + + public String getNextScheduledStopId() { + return nextScheduledStopId; + } + + public void setNextScheduledStopDistance(Double distance) { + this.nextScheduledStopDistance = distance; + } + + public Double getNextScheduledStopDistance() { + return nextScheduledStopDistance; + } + + public String getPreviousScheduledStopId() { + return previousScheduledStopId; + } + + public void setPreviousScheduledStopId(String previousScheduledStopId) { + this.previousScheduledStopId = previousScheduledStopId; + } + + public Double getPreviousScheduledStopDistance() { + return previousScheduledStopDistance; + } + + public void setPreviousScheduledStopDistance( + Double previousScheduledStopDistance) { + this.previousScheduledStopDistance = previousScheduledStopDistance; + } + + public String getInferredBlockId() { + return inferredBlockId; + } + + public void setInferredBlockId(String inferredBlockId) { + this.inferredBlockId = inferredBlockId; + } + + public String getInferredTripId() { + return inferredTripId; + } + + public void setInferredTripId(String inferredTripId) { + this.inferredTripId = inferredTripId; + } + + public String getInferredRouteId() { + return inferredRouteId; + } + + public void setInferredRouteId(String inferredRouteId) { + this.inferredRouteId = inferredRouteId; + } + + public String getInferredDirectionId() { + return inferredDirectionId; + } + + public void setInferredDirectionId(String inferredDirectionId) { + this.inferredDirectionId = inferredDirectionId; + } + + public Long getLastLocationUpdateTime() { + return lastLocationUpdateTime; + } + + public void setLastLocationUpdateTime(Long lastLocationUpdateTime) { + this.lastLocationUpdateTime = lastLocationUpdateTime; + } + + public BigDecimal getSpeed() { + return speed; + } + + public void setSpeed(BigDecimal speed) { + this.speed = speed; + } + + + + + private Double scaleDouble(Double doubleVal, int decimalPlaces){ + if(doubleVal == null || doubleVal.isNaN()) + return doubleVal; + + return new BigDecimal(doubleVal).setScale(decimalPlaces, BigDecimal.ROUND_HALF_UP).doubleValue(); + + } + + public void setAssignedBlockId(String assignedBlockId) { + this.assignedBlockId = assignedBlockId; + } + + public String getAssignedBlockId() { + return assignedBlockId; + } + +} + diff --git a/transitclock/src/main/java/org/transitclock/feed/zmq/oba/NycVehicleManagementStatusBean.java b/transitclock/src/main/java/org/transitclock/feed/zmq/oba/NycVehicleManagementStatusBean.java new file mode 100644 index 000000000..639684f76 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/feed/zmq/oba/NycVehicleManagementStatusBean.java @@ -0,0 +1,218 @@ +/** + * Copyright (C) 2011 Metropolitan Transportation Authority + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.transitclock.feed.zmq.oba; + +import java.io.Serializable; + +/** + * An over the wire management record that is sent by the inference engine and received + * by the TDF. These records are passed to the VehicleTrackingManagementService for processing. + * + * @author jmaki + * + */ +public class NycVehicleManagementStatusBean implements Serializable { + + private static final long serialVersionUID = 1L; + + // record identifier + private String uuid; + + // is inference enabled? + private boolean inferenceIsEnabled; + + // last filter update time for this vehicle + private Long lastUpdateTime; + + // last time a valid (non 0) location update was provided + private Long lastLocationUpdateTime; + + // last latitude observed + private Double lastObservedLatitude; + + // lat longitude observed + private Double lastObservedLongitude; + + // most recent DSC from the bus + private String mostRecentObservedDestinationSignCode; + + // most recent inferred DSC + private String lastInferredDestinationSignCode; + + // is this inference engine the primary? + private boolean inferenceEngineIsPrimary; + + // the bundle ID this result was generated with + private String activeBundleId; + + // is inference formal? e.g. comes from an assigned run? + private boolean inferenceIsFormal; + + // the depot this vehicle is assigned to in the vehicle assignment service + private String depotId; + + // the bus' in-emergency flag. + private boolean emergencyFlag; + + // the last observed operator ID from the bus. + private String lastInferredOperatorId; + + // the run ID calculated by the system + private String inferredRunId; + + // the run ID provided by the operator assignment service + private String assignedRunId; + + private String assignedBlockId; + + public String getUUID() { + return uuid; + } + + public void setUUID(String uuid) { + this.uuid = uuid; + } + + public boolean isInferenceIsEnabled() { + return inferenceIsEnabled; + } + + public void setInferenceIsEnabled(boolean enabled) { + this.inferenceIsEnabled = enabled; + } + + public long getLastUpdateTime() { + return lastUpdateTime; + } + + public void setLastUpdateTime(long lastUpdateTime) { + this.lastUpdateTime = lastUpdateTime; + } + + public long getLastLocationUpdateTime() { + return lastLocationUpdateTime; + } + + public void setLastLocationUpdateTime(long lastGpsTime) { + this.lastLocationUpdateTime = lastGpsTime; + } + + public double getLastObservedLatitude() { + return lastObservedLatitude; + } + + public void setLastObservedLatitude(double lastGpsLat) { + this.lastObservedLatitude = lastGpsLat; + } + + public double getLastObservedLongitude() { + return lastObservedLongitude; + } + + public void setLastObservedLongitude(double lastGpsLon) { + this.lastObservedLongitude = lastGpsLon; + } + + public String getMostRecentObservedDestinationSignCode() { + return mostRecentObservedDestinationSignCode; + } + + public void setMostRecentObservedDestinationSignCode(String mostRecentDestinationSignCode) { + this.mostRecentObservedDestinationSignCode = mostRecentDestinationSignCode; + } + + public String getLastInferredDestinationSignCode() { + return lastInferredDestinationSignCode; + } + + public void setLastInferredDestinationSignCode(String inferredDestinationSignCode) { + this.lastInferredDestinationSignCode = inferredDestinationSignCode; + } + + public boolean isInferenceIsFormal() { + return inferenceIsFormal; + } + + public void setInferenceIsFormal(boolean inferenceIsFormal) { + this.inferenceIsFormal = inferenceIsFormal; + } + + public boolean isInferenceEngineIsPrimary() { + return inferenceEngineIsPrimary; + } + + public void setInferenceEngineIsPrimary(boolean inferenceEngineIsPrimary) { + this.inferenceEngineIsPrimary = inferenceEngineIsPrimary; + } + + public String getActiveBundleId() { + return activeBundleId; + } + + public void setActiveBundleId(String activeBundleId) { + this.activeBundleId = activeBundleId; + } + + public void setDepotId(String depotId) { + this.depotId = depotId; + } + + public String getDepotId() { + return depotId; + } + + public boolean isEmergencyFlag() { + return emergencyFlag; + } + + public void setEmergencyFlag(boolean emergencyFlag) { + this.emergencyFlag = emergencyFlag; + } + + public void setLastInferredOperatorId(String operatorId) { + this.lastInferredOperatorId = operatorId; + } + + public String getLastInferredOperatorId() { + return lastInferredOperatorId; + } + + public void setInferredRunId(String inferredRunId) { + this.inferredRunId = inferredRunId; + } + + public String getInferredRunId() { + return inferredRunId; + } + + public String getAssignedRunId() { + return assignedRunId; + } + + public void setAssignedRunId(String assignedRunId) { + this.assignedRunId = assignedRunId; + } + + public String getAssignedBlockId() { + return assignedBlockId; + } + + public void setAssignedBlockId(String assignedBlockId) { + this.assignedBlockId = assignedBlockId; + } + +} + diff --git a/transitime/src/main/java/org/transitime/gtfs/BlocksProcessor.java b/transitclock/src/main/java/org/transitclock/gtfs/BlocksProcessor.java similarity index 92% rename from transitime/src/main/java/org/transitime/gtfs/BlocksProcessor.java rename to transitclock/src/main/java/org/transitclock/gtfs/BlocksProcessor.java index 714ce8c52..fbed9feb4 100644 --- a/transitime/src/main/java/org/transitime/gtfs/BlocksProcessor.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/BlocksProcessor.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.gtfs; +package org.transitclock.gtfs; import java.util.ArrayList; import java.util.Collection; @@ -27,10 +27,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.db.structs.Block; -import org.transitime.db.structs.Trip; -import org.transitime.db.structs.TripPattern; -import org.transitime.gtfs.gtfsStructs.GtfsRoute; +import org.transitclock.db.structs.Block; +import org.transitclock.db.structs.Trip; +import org.transitclock.db.structs.TripPattern; +import org.transitclock.gtfs.gtfsStructs.GtfsRoute; /** * Goes through all the Trip objects and creates corresponding Block @@ -210,6 +210,9 @@ public List process(int configRev) { new Comparator() { @Override public int compare(Trip arg0, Trip arg1) { + if (arg0 == arg1) return 0; + if (arg0 == null || arg0.getStartTime() == null) return -1; + if (arg1 == null || arg1.getStartTime() == null) return 1; return arg0.getStartTime(). compareTo(arg1.getStartTime()); } @@ -217,6 +220,8 @@ public int compare(Trip arg0, Trip arg1) { // Determine start time for block from the first trip. Trip firstTripForBlock = tripsListForBlock.get(0); + if (firstTripForBlock == null || firstTripForBlock.getStartTime() == null) + continue; int startTimeForBlock = firstTripForBlock.getStartTime(); // Determine end time for block from the last trip. diff --git a/transitime/src/main/java/org/transitime/gtfs/DbConfig.java b/transitclock/src/main/java/org/transitclock/gtfs/DbConfig.java similarity index 81% rename from transitime/src/main/java/org/transitime/gtfs/DbConfig.java rename to transitclock/src/main/java/org/transitclock/gtfs/DbConfig.java index 2df55db72..6a3582839 100644 --- a/transitime/src/main/java/org/transitime/gtfs/DbConfig.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/DbConfig.java @@ -14,43 +14,27 @@ * You should have received a copy of the GNU General Public License along with * Transitime.org . If not, see . */ -package org.transitime.gtfs; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; +package org.transitclock.gtfs; import org.hibernate.HibernateException; +import org.hibernate.Query; +import org.hibernate.SQLQuery; import org.hibernate.Session; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.applications.Core; -import org.transitime.core.ServiceUtils; -import org.transitime.db.hibernate.HibernateUtils; -import org.transitime.db.structs.ActiveRevisions; -import org.transitime.db.structs.Agency; -import org.transitime.db.structs.Block; -import org.transitime.db.structs.Calendar; -import org.transitime.db.structs.CalendarDate; -import org.transitime.db.structs.FareAttribute; -import org.transitime.db.structs.FareRule; -import org.transitime.db.structs.Frequency; -import org.transitime.db.structs.StopPath; -import org.transitime.db.structs.Route; -import org.transitime.db.structs.Stop; -import org.transitime.db.structs.Transfer; -import org.transitime.db.structs.TravelTimesForStopPath; -import org.transitime.db.structs.TravelTimesForTrip; -import org.transitime.db.structs.Trip; -import org.transitime.db.structs.TripPattern; -import org.transitime.utils.IntervalTimer; -import org.transitime.utils.MapKey; -import org.transitime.utils.Time; +import org.transitclock.applications.Core; +import org.transitclock.config.BooleanConfigValue; +import org.transitclock.config.StringConfigValue; +import org.transitclock.core.ServiceUtils; +import org.transitclock.db.hibernate.HibernateUtils; +import org.transitclock.db.structs.Calendar; +import org.transitclock.db.structs.*; +import org.transitclock.utils.IntervalTimer; +import org.transitclock.utils.MapKey; +import org.transitclock.utils.Time; + +import java.util.*; +import java.util.stream.Collectors; /** * Reads all the configuration data from the database. The data is based on GTFS @@ -75,10 +59,31 @@ public class DbConfig { // Following is for all the data read from the database private List blocks; + /** + * cache of known "configRev" ids. ActiveRevision will be the + * currently loaded one. + */ + private List configRevisions; + // So can access blocks by service ID and block ID easily. // Keyed on serviceId. Submap keyed on blockId private Map> blocksByServiceMap = null; + /** + * For unit tests populate blocksByServiceMap. + * @param serviceId + * @param blockId + * @param block + */ + public void addBlockToServiceMap(String serviceId, String blockId, Block block) { + Map blockMap = blocksByServiceMap.get(serviceId); + if (blockMap == null) { + blockMap = new HashMap<>(); + blocksByServiceMap.put(serviceId, blockMap); + } + blockMap.put(blockId, block); + } + // So can access blocks by service ID and route ID easily private Map> blocksByRouteMap = null; @@ -102,15 +107,24 @@ public class DbConfig { private Map> individualTripsByShortNameMap = new HashMap>(); + // cache of all tripIds to prevent queries for nonexistant trips + private Set tripIdSet = null; + // cache of all tripNames to prevent queries for nonexistent trips + private Set tripNameSet = null; + + private List agencies; private List calendars; private List calendarDates; // So can efficiently look up calendar dates private Map> calendarDatesMap; + private Map calendarByServiceIdMap; private List fareAttributes; private List fareRules; private List frequencies; private List transfers; + private List feedInfo; + // Keyed by stop_id. private Map stopsMap; @@ -125,6 +139,19 @@ public class DbConfig { private static final Logger logger = LoggerFactory .getLogger(DbConfig.class); + private StringConfigValue validateTestQuery + = new StringConfigValue("transitclock.db.validateQuery", + "SELECT 1", + "query to validate database connection"); + + public String getValidateTestQuery() { + return validateTestQuery.getValue(); + } + + private BooleanConfigValue serviceIdSuffix = new BooleanConfigValue("transitclock.avl.serviceIdSuffix", + false,"suffix tripId with serviceId"); + public boolean getServiceIdSuffix() { return serviceIdSuffix.getValue(); } + /********************** Member Functions **************************/ /** @@ -134,6 +161,7 @@ public class DbConfig { */ public DbConfig(String agencyId) { this.agencyId = agencyId; + new Thread(new ValidateSessionThread(this)).start(); } /** @@ -464,6 +492,30 @@ public List getTripPatternsForRoute(String routeId) { return tripPatternsByRouteMap.get(routeId); } + /** + * Returns the list of trip patterns associated with the specified route and headsign. + * Reads the trip patterns from the database and stores them in cache so + * that subsequent calls get them directly from the cache. The first time + * this is called it can take a few seconds. Therefore this is not done at + * startup since want startup to be quick. + * + * @param routeId + * @return List of TripPatterns for the route, or null if no such route + */ + public List getTripPatternsForRouteAndHeadSign(String routeId, String headSign) { + // If haven't read in the trip pattern data yet, do so now and cache it + if (tripPatternsByRouteMap == null) { + logger.error("tripPatternsByRouteMap not set when " + + "getTripPatternsForRoute() called. Exiting!"); + System.exit(-1); + } + + // Return cached trip pattern data + return tripPatternsByRouteMap.get(routeId).stream() + .filter(tp -> tp.getHeadsign().equalsIgnoreCase(headSign)) + .collect(Collectors.toList()); + } + /** * Returns cached map of all Trips. Can be slow first time accessed because * it can take a while to read in all trips including all sub-data. @@ -507,6 +559,16 @@ public Trip getTrip(String tripIdOrShortName) { // If trip not read in yet, do so now if (trip == null) { + + // make sure this trip really exists before we try to load + if (!getTripNameSet().contains(tripIdOrShortName)) { + if (!getTripIdSet().contains(tripIdOrShortName)) { + // perhaps it was a previous configRev + logger.debug("requested {} trip no longer exists", tripIdOrShortName); + return null; + } + } + logger.debug("Trip for tripIdOrShortName={} not read from db yet " + "so reading it now.", tripIdOrShortName); @@ -539,6 +601,55 @@ public Trip getTrip(String tripIdOrShortName) { return trip; } + /** + * cache of all trip names for this configRev. + * @return Set of tripNames + */ + private Set getTripNameSet() { + if (tripNameSet == null) { + synchronized (Block.getLazyLoadingSyncObject()) { + // check to see if we won the lock + if (tripNameSet != null) return tripNameSet; + IntervalTimer tick = new IntervalTimer(); + logger.info("loading tripShortName Cache...."); + String hql = "select tripShortName FROM Trip t " + + " WHERE t.configRev = :configRev"; + Query query = globalSession.createQuery(hql); + query.setInteger("configRev", configRev); + + // Actually perform the query + tripNameSet = new HashSet(query.list()); + logger.info("tripShortName cache loaded in {}", tick.elapsedMsec()); + } + } + return tripNameSet; + } + + /** + * cache of all trip ids for this configRev. + * @return Set of tripIds + */ + + private Set getTripIdSet() { + if (tripIdSet == null) { + synchronized (Block.getLazyLoadingSyncObject()) { + // check to see if we won the lock + if (tripIdSet != null) return tripIdSet; + IntervalTimer tick = new IntervalTimer(); + logger.info("loading tripId Cache...."); + String hql = "select tripId FROM Trip t " + + " WHERE t.configRev = :configRev"; + Query query = globalSession.createQuery(hql); + query.setInteger("configRev", configRev); + + // Actually perform the query + tripIdSet = new HashSet(query.list()); + logger.info("tripId cache loaded in {}", tick.elapsedMsec()); + } + } + return tripIdSet; + } + /** * Looks through the trips passed in and returns the one that has * a service ID that is currently valid. @@ -589,7 +700,14 @@ public Trip getTripUsingTripShortName(String tripShortName) { } } - logger.info("FIXME tripShortName={} not yet read from db so reading it in now", tripShortName); + // if we got this far, its possible the trip doesn't exist. Check cache + // before making expensive call to database + if (!getTripNameSet().contains(tripShortName)) { + logger.debug("request for non-existant trip {}", tripShortName); + return null; + } + logger.info("FIXME tripShortName={} not yet read from db so reading it in now ", + tripShortName); // Trips for the short name not read in yet, do so now // Need to sync such that block data, which includes trip @@ -684,6 +802,7 @@ private void actuallyReadData(int configRev) { timer = new IntervalTimer(); blocks = Block.getBlocks(globalSession, configRev); + configRevisions = ConfigRevision.getConfigRevisions(globalSession, configRev); blocksByServiceMap = putBlocksIntoMap(blocks); blocksByRouteMap = putBlocksIntoMapByRoute(blocks); logger.debug("Reading blocks took {} msec", timer.elapsedMsec()); @@ -707,8 +826,17 @@ private void actuallyReadData(int configRev) { agencies = Agency.getAgencies(globalSession, configRev); calendars = Calendar.getCalendars(globalSession, configRev); calendarDates = CalendarDate.getCalendarDates(globalSession, configRev); + + calendarByServiceIdMap = new HashMap<>(); + for (Calendar calendar : calendars) { + if(calendarByServiceIdMap.get(calendar.getServiceId()) == null){ + calendarByServiceIdMap.put(calendar.getServiceId(), calendar); + } else{ + logger.warn("Duplicate Service Id {} in Calendar", calendar.getServiceId()); + } + } - calendarDatesMap = new HashMap>(); + calendarDatesMap = new HashMap<>(); for (CalendarDate calendarDate : calendarDates) { Long time = calendarDate.getTime(); List calendarDatesForDate = calendarDatesMap.get(time); @@ -724,6 +852,8 @@ private void actuallyReadData(int configRev) { fareRules = FareRule.getFareRules(globalSession, configRev); frequencies = Frequency.getFrequencies(globalSession, configRev); transfers = Transfer.getTransfers(globalSession, configRev); + feedInfo = FeedInfo.getFeedInfo(globalSession, configRev); + logger.debug("Reading everything else took {} msec", timer.elapsedMsec()); @@ -776,6 +906,15 @@ public Block getBlock(String serviceId, String blockId) { } + public int getBlockCount(){ + int blockCount = 0; + for(String serviceId : blocksByServiceMap.keySet()){ + blockCount += (blocksByServiceMap.get(serviceId) != null + ? blocksByServiceMap.get(serviceId).size() : 0); + } + return blockCount; + } + /** * Returns blocks for the specified blockId for all service IDs. * @@ -806,7 +945,7 @@ public Collection getBlocksForAllServiceIds(String blockId) { */ public Collection getBlocks(String serviceId) { Map blocksForServiceMap = - blocksByServiceMap.get(serviceId); + blocksByServiceMap.get(serviceId); if (blocksForServiceMap != null) { Collection blocksForService = blocksForServiceMap.values(); return Collections.unmodifiableCollection(blocksForService); @@ -824,6 +963,14 @@ public List getBlocks() { return Collections.unmodifiableList(blocks); } + /** + * Expose metadata about data loaded. + * @return + */ + public List getConfigRevisions() { + return Collections.unmodifiableList(configRevisions); + } + /** * Returns Map of routesMap keyed on the routeId. * @@ -971,7 +1118,11 @@ public List getCurrentServiceIds() { } return serviceIds; } - + + public Calendar getCalendarByServiceId(String serviceId) { + return calendarByServiceIdMap.get(serviceId); + } + /** * There can be multiple agencies but usually there will be just one. For * getting timezone and such want to be able to easily access the main @@ -1012,13 +1163,43 @@ private static void outputCollection(String name, Collection list) { } } + @SuppressWarnings("unused") + private static class ValidateSessionThread implements Runnable { + + private DbConfig service; + public ValidateSessionThread(DbConfig service) { + this.service = service; + } + + @Override + public void run() { + DbConfig dbConfig = Core.getInstance().getDbConfig(); + + while (!Thread.interrupted()) { + Time.sleep(60 * 1000); + try { + SQLQuery query = service.getGlobalSession().createSQLQuery(dbConfig.getValidateTestQuery()); + query.list(); + logger.debug("session test success"); + } catch (Throwable t) { + logger.error("session test failure: {} {}", t, t); + // the only reason this validate query should fail is if + // our db connnection is invalid + // log the issue for now + // eventually flush connection pool or give other hints + } + + } + } + } + /** * For debugging. * * @param args */ public static void main(String args[]) { - String projectId = "sfmta"; + String projectId = "1"; int configRev = ActiveRevisions.get(projectId).getConfigRev(); @@ -1044,5 +1225,6 @@ public static void main(String args[]) { outputCollection("FareRules", dbConfig.fareRules); outputCollection("Frequencies", dbConfig.frequencies); outputCollection("Transfers", dbConfig.transfers); + outputCollection("FeedInfo", dbConfig.feedInfo); } } diff --git a/transitime/src/main/java/org/transitime/gtfs/DbWriter.java b/transitclock/src/main/java/org/transitclock/gtfs/DbWriter.java similarity index 66% rename from transitime/src/main/java/org/transitime/gtfs/DbWriter.java rename to transitclock/src/main/java/org/transitclock/gtfs/DbWriter.java index 2db8e7a92..fe9fccc18 100644 --- a/transitime/src/main/java/org/transitime/gtfs/DbWriter.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/DbWriter.java @@ -15,28 +15,16 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.gtfs; +package org.transitclock.gtfs; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.Transaction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.db.hibernate.HibernateUtils; -import org.transitime.db.structs.Agency; -import org.transitime.db.structs.Block; -import org.transitime.db.structs.Calendar; -import org.transitime.db.structs.CalendarDate; -import org.transitime.db.structs.FareAttribute; -import org.transitime.db.structs.FareRule; -import org.transitime.db.structs.Frequency; -import org.transitime.db.structs.Route; -import org.transitime.db.structs.Stop; -import org.transitime.db.structs.Transfer; -import org.transitime.db.structs.TravelTimesForTrip; -import org.transitime.db.structs.Trip; -import org.transitime.db.structs.TripPattern; -import org.transitime.utils.IntervalTimer; +import org.transitclock.configData.DbSetupConfig; +import org.transitclock.db.structs.*; +import org.transitclock.utils.IntervalTimer; /** * Writes the GTFS data contained in a GtfsData object to the database. @@ -63,14 +51,24 @@ public DbWriter(GtfsData gtfsData) { * @param object */ private void writeObject(Session session, Object object) { - session.saveOrUpdate(object); + writeObject(session, object, true); + } + + private void writeObject(Session session, Object object, boolean checkForUpdate) { + if (checkForUpdate) { + session.saveOrUpdate(object); + } else { + session.save(object); + } // Since can writing large amount of data should use Hibernate // batching to make sure don't run out memory. counter++; - if (counter % HibernateUtils.BATCH_SIZE == 0) { + if (counter % DbSetupConfig.getBatchSize() == 0) { + logger.info("flushing with " + counter + " % " + DbSetupConfig.getBatchSize()); session.flush(); session.clear(); + logger.info("flushed with " + counter + " % " + DbSetupConfig.getBatchSize()); } } @@ -80,30 +78,32 @@ private void writeObject(Session session, Object object) { * * @param configRev */ - private void actuallyWriteData(Session session, int configRev) { - // Get rid of old data. Getting rid of trips, trip patterns, and blocks - // is a bit complicated. Need to delete them in proper order because - // of the foreign keys. Because appear to need to use plain SQL - // to do so successfully (without reading in objects and then - // deleting them, which takes too much time and memory). Therefore - // deleting of this data is done here before writing the data. - logger.info("Deleting old blocks and associated trips from rev {} of " - + "database...", configRev); - Block.deleteFromRev(session, configRev); - - logger.info("Deleting old trips from rev {} of database...", - configRev); - Trip.deleteFromRev(session, configRev); - - logger.info("Deleting old trip patterns from rev {} of database...", - configRev); - TripPattern.deleteFromRev(session, configRev); - - // Get rid of travel times that are associated with the rev being - // deleted - logger.info("Deleting old travel times from rev {} of database...", - configRev); - TravelTimesForTrip.deleteFromRev(session, configRev); + private void actuallyWriteData(Session session, int configRev, boolean cleanupRevs) { + if (cleanupRevs) { + // Get rid of old data. Getting rid of trips, trip patterns, and blocks + // is a bit complicated. Need to delete them in proper order because + // of the foreign keys. Because appear to need to use plain SQL + // to do so successfully (without reading in objects and then + // deleting them, which takes too much time and memory). Therefore + // deleting of this data is done here before writing the data. + logger.info("Deleting old blocks and associated trips from rev {} of " + + "database...", configRev); + Block.deleteFromRev(session, configRev); + + logger.info("Deleting old trips from rev {} of database...", + configRev); + Trip.deleteFromRev(session, configRev); + + logger.info("Deleting old trip patterns from rev {} of database...", + configRev); + TripPattern.deleteFromRev(session, configRev); + + // Get rid of travel times that are associated with the rev being + // deleted + logger.info("Deleting old travel times from rev {} of database...", + configRev); + TravelTimesForTrip.deleteFromRev(session, configRev); + } // Now write the data to the database. // First write the Blocks. This will also write the Trips, TripPatterns, @@ -112,10 +112,14 @@ private void actuallyWriteData(Session session, int configRev) { logger.info("Saving {} blocks (plus associated trips) to database...", gtfsData.getBlocks().size()); int c = 0; + long startTime = System.currentTimeMillis(); for (Block block : gtfsData.getBlocks()) { logger.info("Saving block #{} with blockId={} serviceId={} blockId={}", ++c, block.getId(), block.getServiceId(), block.getId()); - writeObject(session, block); + writeObject(session, block, false); + if (c % 1000 == 0) { + logger.info("wrote " + c + " blocks in " + (System.currentTimeMillis()-startTime)/1000 + "s"); + } } logger.info("Saving routes to database..."); @@ -171,6 +175,12 @@ private void actuallyWriteData(Session session, int configRev) { for (Transfer transfer : gtfsData.getTransfers()) { writeObject(session, transfer); } + + logger.info("Saving feedinfo to database..."); + FeedInfo.deleteFromRev(session, configRev); + for (FeedInfo feedInfo : gtfsData.getFeedInfo()) { + writeObject(session, feedInfo); + } // Write out the ConfigRevision data writeObject(session, gtfsData.getConfigRevision()); @@ -184,8 +194,14 @@ private void actuallyWriteData(Session session, int configRev) { * @param configRev So can delete old data for the rev * @throws HibernateException when problem with database */ + public void write(Session session, int configRev) throws HibernateException { + write(session, configRev, true); + } + + public void write(Session session, int configRev, boolean cleanupRevs) + throws HibernateException { // For logging how long things take IntervalTimer timer = new IntervalTimer(); @@ -196,7 +212,7 @@ public void write(Session session, int configRev) // Do the low-level processing try { - actuallyWriteData(session, configRev); + actuallyWriteData(session, configRev, cleanupRevs); // Done writing data so commit it tx.commit(); diff --git a/transitime/src/main/java/org/transitime/gtfs/GtfsData.java b/transitclock/src/main/java/org/transitclock/gtfs/GtfsData.java similarity index 90% rename from transitime/src/main/java/org/transitime/gtfs/GtfsData.java rename to transitclock/src/main/java/org/transitclock/gtfs/GtfsData.java index e294bdfa1..453300a36 100644 --- a/transitime/src/main/java/org/transitime/gtfs/GtfsData.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/GtfsData.java @@ -14,86 +14,29 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.gtfs; - -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TimeZone; -import java.util.concurrent.ConcurrentHashMap; -import java.util.regex.Pattern; +package org.transitclock.gtfs; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.config.DoubleConfigValue; -import org.transitime.config.IntegerConfigValue; -import org.transitime.config.StringConfigValue; -import org.transitime.db.hibernate.HibernateUtils; -import org.transitime.db.structs.ActiveRevisions; -import org.transitime.db.structs.Agency; -import org.transitime.db.structs.Block; -import org.transitime.db.structs.Calendar; -import org.transitime.db.structs.CalendarDate; -import org.transitime.db.structs.ConfigRevision; -import org.transitime.db.structs.FareAttribute; -import org.transitime.db.structs.FareRule; -import org.transitime.db.structs.Frequency; -import org.transitime.db.structs.Location; -import org.transitime.db.structs.StopPath; -import org.transitime.db.structs.Route; -import org.transitime.db.structs.ScheduleTime; -import org.transitime.db.structs.Stop; -import org.transitime.db.structs.Transfer; -import org.transitime.db.structs.Trip; -import org.transitime.db.structs.TripPattern; -import org.transitime.db.structs.TripPatternKey; -import org.transitime.gtfs.gtfsStructs.GtfsAgency; -import org.transitime.gtfs.gtfsStructs.GtfsCalendar; -import org.transitime.gtfs.gtfsStructs.GtfsCalendarDate; -import org.transitime.gtfs.gtfsStructs.GtfsFareAttribute; -import org.transitime.gtfs.gtfsStructs.GtfsFareRule; -import org.transitime.gtfs.gtfsStructs.GtfsFrequency; -import org.transitime.gtfs.gtfsStructs.GtfsRoute; -import org.transitime.gtfs.gtfsStructs.GtfsShape; -import org.transitime.gtfs.gtfsStructs.GtfsStop; -import org.transitime.gtfs.gtfsStructs.GtfsStopTime; -import org.transitime.gtfs.gtfsStructs.GtfsTransfer; -import org.transitime.gtfs.gtfsStructs.GtfsTrip; -import org.transitime.gtfs.readers.GtfsAgenciesSupplementReader; -import org.transitime.gtfs.readers.GtfsAgencyReader; -import org.transitime.gtfs.readers.GtfsCalendarDatesReader; -import org.transitime.gtfs.readers.GtfsCalendarReader; -import org.transitime.gtfs.readers.GtfsFareAttributesReader; -import org.transitime.gtfs.readers.GtfsFareRulesReader; -import org.transitime.gtfs.readers.GtfsFrequenciesReader; -import org.transitime.gtfs.readers.GtfsRoutesReader; -import org.transitime.gtfs.readers.GtfsRoutesSupplementReader; -import org.transitime.gtfs.readers.GtfsShapesReader; -import org.transitime.gtfs.readers.GtfsShapesSupplementReader; -import org.transitime.gtfs.readers.GtfsStopTimesReader; -import org.transitime.gtfs.readers.GtfsStopTimesSupplementReader; -import org.transitime.gtfs.readers.GtfsStopsReader; -import org.transitime.gtfs.readers.GtfsStopsSupplementReader; -import org.transitime.gtfs.readers.GtfsTransfersReader; -import org.transitime.gtfs.readers.GtfsTripsReader; -import org.transitime.gtfs.readers.GtfsTripsSupplementReader; -import org.transitime.utils.Geo; -import org.transitime.utils.IntervalTimer; -import org.transitime.utils.MapKey; -import org.transitime.utils.StringUtils; -import org.transitime.utils.Time; +import org.transitclock.config.DoubleConfigValue; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.config.StringConfigValue; +import org.transitclock.db.hibernate.HibernateUtils; +import org.transitclock.db.structs.Calendar; +import org.transitclock.db.structs.*; +import org.transitclock.gtfs.gtfsStructs.*; +import org.transitclock.gtfs.readers.*; +import org.transitclock.monitoring.MonitoringService; +import org.transitclock.utils.*; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Pattern; /** * Contains all the GTFS data processed into Java lists and such. Also combines @@ -111,6 +54,9 @@ public class GtfsData { // The session used throughout the class private final Session session; + // log metrics + private MonitoringService monitoringService; + // Various params set by constructor private final ActiveRevisions revs; private final String notes; @@ -125,6 +71,7 @@ public class GtfsData { private final double maxSpeedKph; private final double maxTravelTimeSegmentLength; private final boolean trimPathBeforeFirstStopOfTrip; + private final boolean cleanupRevs; // So can make the titles more readable private final TitleFormatter titleFormatter; @@ -199,7 +146,8 @@ public class GtfsData { private List fareAttributes; private List fareRules; private List transfers; - + private List feedInfoList; + // This is the format that dates are in for CSV. Should // be accessed only through getDateFormatter() to make // sure that it is initialized. @@ -210,7 +158,7 @@ public class GtfsData { // for details on how to filter out matches as opposed to specifying // which ones want to keep. private static StringConfigValue routeIdFilterRegEx = new StringConfigValue( - "transitime.gtfs.routeIdFilterRegEx", + "transitclock.gtfs.routeIdFilterRegEx", null, // Default of null means don't do any filtering "Route is included only if route_id matches the this regular " + "expression. If only want routes with \"SPECIAL\" in the id then " @@ -224,7 +172,7 @@ public class GtfsData { // So can process only trips that match a regular expression. // Default of null means don't do any filtering private static StringConfigValue tripIdFilterRegEx = new StringConfigValue( - "transitime.gtfs.tripIdFilterRegEx", + "transitclock.gtfs.tripIdFilterRegEx", null, // Default of null means don't do any filtering "Trip is included only if trip_id matches the this regular " + "expression. If only want trips with \"SPECIAL\" in the id then " @@ -236,18 +184,26 @@ public class GtfsData { private static Pattern tripIdFilterRegExPattern = null; private static IntegerConfigValue stopCodeBaseValue = - new IntegerConfigValue("transitime.gtfs.stopCodeBaseValue", + new IntegerConfigValue("transitclock.gtfs.stopCodeBaseValue", "If agency doesn't specify stop codes but simply wants to " + "have them be a based number plus the stop ID then this " + "parameter can specify the base value. "); private static DoubleConfigValue minDistanceBetweenStopsToDisambiguateHeadsigns = - new DoubleConfigValue("transitime.gtfs.minDistanceBetweenStopsToDisambiguateHeadsigns", + new DoubleConfigValue("transitclock.gtfs.minDistanceBetweenStopsToDisambiguateHeadsigns", 1000.0, "When disambiguating headsigns by appending the too stop " + "name of the last stop, won't disambiguate if the last " + "stops for the trips with the same headsign differ by " + "less than this amount."); + + private static StringConfigValue outputPathsAndStopsForGraphingRouteIds = + new StringConfigValue("transitclock.gtfs.outputPathsAndStopsForGraphingRouteIds", + null, // Default of null means don't output any routes + "Outputs data for specified routes grouped by trip pattern." + + "The resulting data can be visualized on a map by cutting" + + "and pasting it in to http://www.gpsvisualizer.com/map_input" + + "Separate multiple route ids with commas"); // Logging public static final Logger logger = @@ -278,6 +234,7 @@ public GtfsData(int configRev, String notes, Date zipFileLastModifiedTime, boolean shouldStoreNewRevs, + boolean shouldDeleteRevs, String projectId, String gtfsDirectoryName, String supplementDir, @@ -309,6 +266,7 @@ public GtfsData(int configRev, HibernateUtils.getSessionFactory(getAgencyId()); session = sessionFactory.openSession(); + monitoringService = MonitoringService.getInstance(); // Deal with the ActiveRevisions. First, store the original travel times // rev since need it to read in old travel time data. ActiveRevisions originalRevs = ActiveRevisions.get(session); @@ -327,7 +285,7 @@ public GtfsData(int configRev, // Don't need to store new revs in db so use a transient object revs = new ActiveRevisions(); } - + cleanupRevs = shouldDeleteRevs; // If particular configuration rev specified then use it. This way // can write over existing configuration revisions. if (configRev >= 0) { @@ -829,6 +787,7 @@ private List processStopTimesForTrip( // Therefore filter out such stops. Note that only filtering // out such stops if they are the first stops in the trip. GtfsStop gtfsStop = getGtfsStop(gtfsStopTime.getStopId()); + if (gtfsStop == null) continue; if (gtfsStop.getDeleteFromRoutesStr() != null || (firstStopInTrip && gtfsStop .getDeleteFirstStopFromRoutesStr() != null)) { @@ -1207,22 +1166,31 @@ private List getScheduleTimesForTrip(Trip trip) { // Determine if layover stop. If trip doesn't have schedule then // it definitely can't be a layover. boolean layoverStop = false; - if (depTime != null && !trip.isNoSchedule()) { - if (stop.isLayoverStop() == null) { - layoverStop = firstStopInTrip; - } else { - layoverStop = stop.isLayoverStop(); + + if(!isTripFrequencyBasedWithoutExactTimes(trip.getId())) + { + if (depTime != null && !trip.isNoSchedule()) { + if (stop.isLayoverStop() == null) { + layoverStop = firstStopInTrip; + } else { + layoverStop = stop.isLayoverStop(); + } } } // Determine if it is a waitStop. If trip doesn't have schedule then // it definitely can't be a waitStop. boolean waitStop = false; - if (depTime != null && !trip.isNoSchedule()) { - if (stop.isWaitStop() == null) { - waitStop = firstStopInTrip || gtfsStopTime.isWaitStop(); - } else { - waitStop = stop.isWaitStop(); + + + if(!isTripFrequencyBasedWithoutExactTimes(trip.getId())) + { + if (depTime != null && !trip.isNoSchedule()) { + if (stop.isWaitStop() == null) { + waitStop = firstStopInTrip || gtfsStopTime.isWaitStop(); + } else { + waitStop = stop.isWaitStop(); + } } } @@ -1230,12 +1198,17 @@ private List getScheduleTimesForTrip(Trip trip) { // if there is an associated time and it is configured to be such. // But should also be true if there is associated time and it is // first or last stop of the trip. - boolean scheduleAdherenceStop = - (depTime != null - && (firstStopInTrip - || gtfsStopTime.isTimepointStop() - || stop.isTimepointStop())) - || (arrTime != null && lastStopInTrip); + boolean scheduleAdherenceStop = false; + + if(!isTripFrequencyBasedWithoutExactTimes(trip.getId())) + { + scheduleAdherenceStop = + (depTime != null + && (firstStopInTrip + || gtfsStopTime.isTimepointStop() + || stop.isTimepointStop())) + || (arrTime != null && lastStopInTrip); + } // Determine the pathId. Make sure that use a unique path ID by // appending "_loop" if looping over the same stops @@ -1252,7 +1225,7 @@ private List getScheduleTimesForTrip(Trip trip) { StopPath path = new StopPath(revs.getConfigRev(), pathId, stopId, gtfsStopTime.getStopSequence(), lastStopInTrip, trip.getRouteId(), layoverStop, waitStop, - scheduleAdherenceStop, gtfsRoute.getBreakTime()); + scheduleAdherenceStop, gtfsRoute.getBreakTime(), gtfsStopTime.getMaxDistance(), gtfsStopTime.getMaxSpeed()); paths.add(path); previousStopId = stopId; @@ -1446,6 +1419,12 @@ private void createTripsAndTripPatterns( // to have getScheduleTimesForTrip() update an already existing // Trip object. List scheduleTimesList = getScheduleTimesForTrip(trip); + + if (scheduleTimesList.size() < 2) { + logger.warn("trip_id={} has zero or one stoptimes and will be discarded.", tripId); + continue; + } + trip.addScheduleTimes(scheduleTimesList); if (isTripFrequencyBasedWithExactTimes(tripId)) { @@ -1590,7 +1569,7 @@ private void makeHeadsignsUniqueIfDifferentLastStop() { logger.warn("Modifying headsign \"{}\" to \"{}\" " + "since it has a different last stop {} away " + "which is further away than " - + "transitime.gtfs.minDistanceBetweenStopsToDisambiguateHeadsigns of {}. " + + "transitclock.gtfs.minDistanceBetweenStopsToDisambiguateHeadsigns of {}. " + "TripPattern {}. Other TripPattern {}", firstTripPatternForHeadsign.getHeadsign(), modifiedHeadsign, @@ -1615,7 +1594,7 @@ private void makeHeadsignsUniqueIfDifferentLastStop() { logger.warn("Modifying headsign \"{}\" to \"{}\" " + "since it has a different last stop {} away " + "which is further away than " - + "transitime.gtfs.minDistanceBetweenStopsToDisambiguateHeadsigns of {}. " + + "transitclock.gtfs.minDistanceBetweenStopsToDisambiguateHeadsigns of {}. " + "TripPattern {}. Other TripPattern {}", tripPattern.getHeadsign(), modifiedHeadsign, @@ -1992,14 +1971,36 @@ private static boolean isCalendarDateActiveInTheFuture(CalendarDate calendarDate private void processCalendars() { // Let user know what is going on logger.info("Processing calendar.txt data..."); - - // Create the map where the data is going to go + + // Create the map where the data is going to go calendars = new ArrayList(); // Read in the calendar.txt GTFS data from file GtfsCalendarReader calendarReader = new GtfsCalendarReader(gtfsDirectoryName); List gtfsCalendars = calendarReader.get(); + + if(gtfsCalendars.size() < 1){ + logger.info("calendar.txt not found, will generate calendars and assume all services are always available..."); + SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd"); + java.util.Calendar cal = java.util.Calendar.getInstance(); + String start = format.format(cal.getTime()); + cal.add(java.util.Calendar.MONTH, 6); + String end = format.format(cal.getTime()); + GtfsTripsReader tripsReader = new GtfsTripsReader(gtfsDirectoryName); + List gtfsTrips = tripsReader.get(); + Set serviceIds = new HashSet<>(); + for(GtfsTrip gtfsTrip :gtfsTrips){ + serviceIds.add(gtfsTrip.getServiceId()); + } + for(String serviceId : serviceIds){ + GtfsCalendar gtfsCalendar = new GtfsCalendar( + serviceId, "1", "1", "1", "1", "1", "1", "1", start, end + ); + gtfsCalendars.add(gtfsCalendar); + } + } + for (GtfsCalendar gtfsCalendar : gtfsCalendars) { // Create the Calendar object and put it into the array) @@ -2019,7 +2020,7 @@ private void processCalendars() { private void processCalendarDates() { // Let user know what is going on logger.info("Processing calendar_dates.txt data..."); - + // Create the map where the data is going to go calendarDates = new ArrayList(); @@ -2208,12 +2209,33 @@ private void processTransfers() { // Create the CalendarDate object and put it into the array Transfer transfer = new Transfer(revs.getConfigRev(), gtfsTransfer); transfers.add(transfer); - } + } // Let user know what is going on logger.info("Finished processing transfers.txt data. "); } + /** + * Reads feed_info.txt file and puts feed_version into ConfigRevision + */ + private void processFeedInfo() { + // Let user know what is going on + logger.info("Processing feed_info.txt data..."); + feedInfoList = new ArrayList(); + // Read in the feed_info.txt GTFS data from file + GtfsFeedInfosReader feedInfosReader = + new GtfsFeedInfosReader(gtfsDirectoryName); + List gtfsFeedInfos = feedInfosReader.get(); + + for (GtfsFeedInfo gtfsFeedInfo : gtfsFeedInfos) { + FeedInfo feedInfo = new FeedInfo(revs.getConfigRev(), gtfsFeedInfo, getDateFormatter()); + feedInfoList.add(feedInfo); + } + + // Let user know what is going on + logger.info("Finished processing feed_info.txt data. "); + } + /******************** Getter Methods ****************************/ /** @@ -2481,7 +2503,12 @@ public List getFareRules() { public List getTransfers() { return transfers; } - + + public List getFeedInfo() { + return feedInfoList; + } + + /** * Returns information about the current revision. * @@ -2494,6 +2521,16 @@ public ConfigRevision getConfigRevision() { /*************************** Main Public Methods **********************/ + public void outputRoutesForGraphing() { + if (outputPathsAndStopsForGraphingRouteIds.getValue() == null) + return; + + String[] routeIds = outputPathsAndStopsForGraphingRouteIds.getValue().split(","); + for (String routeId : routeIds) { + outputPathsAndStopsForGraphing(routeId); + } + } + /** * Outputs data for specified route grouped by trip pattern. * The resulting data can be visualized on a map by cutting @@ -2566,7 +2603,7 @@ public void outputPathsAndStopsForGraphing(String routeId) { /** * Returns true if the tripId isn't supposed to be filtered out, as - * specified by the transitime.gtfs.tripIdRegExPattern property. + * specified by the transitclock.gtfs.tripIdRegExPattern property. * * @param tripId * @return True if trip not to be filtered out @@ -2585,7 +2622,7 @@ public static boolean tripNotFiltered(String tripId) { /** * Returns true if the routeId isn't supposed to be filtered out, as - * specified by the transitime.gtfs.routeIdRegExPattern property. + * specified by the transitclock.gtfs.routeIdRegExPattern property. * * @param routeId * @return True if route not to be filtered out @@ -2634,6 +2671,7 @@ public void processData() { processFareAttributes(); processFareRules(); processTransfers(); + processFeedInfo(); // Sometimes will be using a partial configuration. For example, for // MBTA commuter rail only want to use the trips defined for @@ -2649,8 +2687,8 @@ public void processData() { // MBTA commuter rail. trimCalendars(); - // debugging - //outputPathsAndStopsForGraphing("8699"); + // Optionally output routes for debug graphing + outputRoutesForGraphing(); // Now process travel times and update the Trip objects. TravelTimesProcessorForGtfsUpdates travelTimesProcesssor = @@ -2659,28 +2697,31 @@ public void processData() { defaultWaitTimeAtStopMsec, maxSpeedKph); travelTimesProcesssor.process(session, this); - // Try allowing garbage collector to free up some memory since - // don't need the GTFS structures anymore. - gtfsRoutesMap = null; - gtfsTripsMap = null; - gtfsStopTimesForTripMap = null; - - // Now that have read in all the data into collections output it - // to database. + // Try allowing garbage collector to free up some memory since + // don't need the GTFS structures anymore. + gtfsRoutesMap = null; + gtfsTripsMap = null; + gtfsStopTimesForTripMap = null; + int originalNumberOfTravelTimes = travelTimesProcesssor.getOriginalNumberOfTravelTimes(); + int numberOfTravelTimes = travelTimesProcesssor.getNumberOfTravelTimes(); + int configRev = revs.getConfigRev(); + int travelTimesRev= revs.getTravelTimesRev(); try { - DbWriter dbWriter = new DbWriter(this); - dbWriter.write(session, revs.getConfigRev()); - - // Finish things up by closing the session - session.close(); - - // Let user know what is going on - logger.info("Finished processing GTFS data from {} . Took {} msec.", - gtfsDirectoryName, timer.elapsedMsec()); - } catch (HibernateException e) { - logger.error("Exception when writing data to db", e); - throw e; - } + DbWriter dbWriter = new DbWriter(this); + dbWriter.write(session, revs.getConfigRev(), cleanupRevs); + // Finish things up by closing the session + session.close(); + + // Let user know what is going on + logger.info("Finished processing GTFS data from {} . Took {} msec.", + gtfsDirectoryName, timer.elapsedMsec()); + } catch (HibernateException e) { + logger.error("Exception when writing data to db", e); + throw e; + } + updateMetrics(originalNumberOfTravelTimes, numberOfTravelTimes, configRev, travelTimesRev); + + // just for debugging // GtfsLoggingAppender.outputMessagesToSysErr(); @@ -2715,4 +2756,26 @@ public void processData() { // System.err.println(" " + r); // } } + + public Long updateMetrics(int originalTravelTimesCount, int expectedTravelTimesCount, int configRev, int travelTimesRev) { + HibernateUtils.clearSessionFactory(); + SessionFactory sessionFactory = + HibernateUtils.getSessionFactory(getAgencyId()); + Session statsSession = sessionFactory.openSession(); + monitoringService.averageMetric("PredictionLatestConfigRev", configRev*1.0); + monitoringService.averageMetric("PredictionLatestTravelTimesRev", travelTimesRev*1.0); + Long count = Trip.countTravelTimesForTrips(statsSession, travelTimesRev); + if (count != null) { + monitoringService.averageMetric("PredictionTravelTimesForTripsCount", count*1.0); + } else { + monitoringService.averageMetric("PredictionTravelTimesForTripsCount", -1.0); + } + monitoringService.averageMetric("PredictionTravelTimesForTripsExpectedCount", expectedTravelTimesCount*1.0); + monitoringService.averageMetric("PredictionTravelTimesForTripsOriginalCount", originalTravelTimesCount*1.0); + monitoringService.flush(); + logger.info("Found {} TravelTimesForTrips for {}:{} with expected={}, orginal={}", + count, configRev, travelTimesRev, expectedTravelTimesCount, originalTravelTimesCount); + return count; + + } } diff --git a/transitime/src/main/java/org/transitime/gtfs/GtfsLoggingAppender.java b/transitclock/src/main/java/org/transitclock/gtfs/GtfsLoggingAppender.java similarity index 93% rename from transitime/src/main/java/org/transitime/gtfs/GtfsLoggingAppender.java rename to transitclock/src/main/java/org/transitclock/gtfs/GtfsLoggingAppender.java index fd80f3c3b..7c5f8c857 100644 --- a/transitime/src/main/java/org/transitime/gtfs/GtfsLoggingAppender.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/GtfsLoggingAppender.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.gtfs; +package org.transitclock.gtfs; import java.util.ArrayList; import java.util.List; @@ -34,10 +34,10 @@ * hopefully causing them to fix the configuration problems. * * To use create a logbackGtfs.xml file with the following: - * + * * * - * + * * * * diff --git a/transitime/src/main/java/org/transitime/gtfs/GtfsUpdatedModule.java b/transitclock/src/main/java/org/transitclock/gtfs/GtfsUpdatedModule.java similarity index 89% rename from transitime/src/main/java/org/transitime/gtfs/GtfsUpdatedModule.java rename to transitclock/src/main/java/org/transitclock/gtfs/GtfsUpdatedModule.java index c3e2078c4..318d14a00 100644 --- a/transitime/src/main/java/org/transitime/gtfs/GtfsUpdatedModule.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/GtfsUpdatedModule.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.gtfs; +package org.transitclock.gtfs; import java.io.File; import java.io.IOException; @@ -30,15 +30,15 @@ import org.apache.http.HttpStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.config.LongConfigValue; -import org.transitime.config.StringConfigValue; -import org.transitime.configData.AgencyConfig; -import org.transitime.logging.Markers; -import org.transitime.modules.Module; -import org.transitime.monitoring.MonitorBase; -import org.transitime.utils.EmailSender; -import org.transitime.utils.HttpGetFile; -import org.transitime.utils.Time; +import org.transitclock.config.LongConfigValue; +import org.transitclock.config.StringConfigValue; +import org.transitclock.configData.AgencyConfig; +import org.transitclock.logging.Markers; +import org.transitclock.modules.Module; +import org.transitclock.monitoring.MonitorBase; +import org.transitclock.utils.EmailSender; +import org.transitclock.utils.HttpGetFile; +import org.transitclock.utils.Time; /** * Downloads GTFS file from web server if it has been updated and notifies @@ -46,7 +46,7 @@ * by an agency. *

* When a GTFS file is downloaded then this module also e-mails recipients - * specified by the parameter transitime.monitoring.emailRecipients + * specified by the parameter transitclock.monitoring.emailRecipients * * @author SkiBu Smith * @@ -56,17 +56,17 @@ public class GtfsUpdatedModule extends Module { // Configuration parameters private static StringConfigValue url = new StringConfigValue( - "transitime.gtfs.url", + "transitclock.gtfs.url", "URL where to retrieve the GTFS file."); private static StringConfigValue dirName = new StringConfigValue( - "transitime.gtfs.dirName", + "transitclock.gtfs.dirName", "Directory on agency server where to place the GTFS file."); private static LongConfigValue intervalMsec = new LongConfigValue( - "transitime.gtfs.intervalMsec", + "transitclock.gtfs.intervalMsec", // Low cost unless file actually downloaded so do pretty // frequently so get updates as soon as possible 4 * Time.MS_PER_HOUR, @@ -251,7 +251,7 @@ public void run() { */ public static void main(String[] args) { // Start the module - start("org.transitime.gtfs.GtfsUpdatedModule"); + start("org.transitclock.gtfs.GtfsUpdatedModule"); } } diff --git a/transitime/src/main/java/org/transitime/gtfs/HttpGetGtfsFile.java b/transitclock/src/main/java/org/transitclock/gtfs/HttpGetGtfsFile.java similarity index 96% rename from transitime/src/main/java/org/transitime/gtfs/HttpGetGtfsFile.java rename to transitclock/src/main/java/org/transitclock/gtfs/HttpGetGtfsFile.java index 1ed4a5df1..11a257d1d 100644 --- a/transitime/src/main/java/org/transitime/gtfs/HttpGetGtfsFile.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/HttpGetGtfsFile.java @@ -15,10 +15,10 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.gtfs; +package org.transitclock.gtfs; -import org.transitime.utils.HttpGetFile; -import org.transitime.utils.Time; +import org.transitclock.utils.HttpGetFile; +import org.transitclock.utils.Time; /** diff --git a/transitime/src/main/java/org/transitime/gtfs/StopPathProcessor.java b/transitclock/src/main/java/org/transitclock/gtfs/StopPathProcessor.java similarity index 98% rename from transitime/src/main/java/org/transitime/gtfs/StopPathProcessor.java rename to transitclock/src/main/java/org/transitclock/gtfs/StopPathProcessor.java index a320d6bb5..812d5d2b2 100644 --- a/transitime/src/main/java/org/transitime/gtfs/StopPathProcessor.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/StopPathProcessor.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.gtfs; +package org.transitclock.gtfs; import java.util.ArrayList; import java.util.Collection; @@ -25,14 +25,14 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.db.structs.Location; -import org.transitime.db.structs.StopPath; -import org.transitime.db.structs.Stop; -import org.transitime.db.structs.TripPattern; -import org.transitime.db.structs.Vector; -import org.transitime.gtfs.gtfsStructs.GtfsShape; -import org.transitime.utils.Geo; -import org.transitime.utils.IntervalTimer; +import org.transitclock.db.structs.Location; +import org.transitclock.db.structs.Stop; +import org.transitclock.db.structs.StopPath; +import org.transitclock.db.structs.TripPattern; +import org.transitclock.db.structs.Vector; +import org.transitclock.gtfs.gtfsStructs.GtfsShape; +import org.transitclock.utils.Geo; +import org.transitclock.utils.IntervalTimer; /** * Part of GtfsData class. Processes the shapes.txt data and converts diff --git a/transitime/src/main/java/org/transitime/gtfs/StopsByLoc.java b/transitclock/src/main/java/org/transitclock/gtfs/StopsByLoc.java similarity index 95% rename from transitime/src/main/java/org/transitime/gtfs/StopsByLoc.java rename to transitclock/src/main/java/org/transitclock/gtfs/StopsByLoc.java index 9034ca0dc..20cab52aa 100644 --- a/transitime/src/main/java/org/transitime/gtfs/StopsByLoc.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/StopsByLoc.java @@ -15,19 +15,19 @@ * along with Transitime.org . If not, see . */ -package org.transitime.gtfs; +package org.transitclock.gtfs; import java.util.ArrayList; import java.util.List; -import org.transitime.applications.Core; -import org.transitime.core.dataCache.PredictionDataCache; -import org.transitime.db.structs.Location; -import org.transitime.db.structs.Route; -import org.transitime.db.structs.StopPath; -import org.transitime.db.structs.TripPattern; -import org.transitime.ipc.data.IpcPredictionsForRouteStopDest; -import org.transitime.utils.Geo; +import org.transitclock.applications.Core; +import org.transitclock.core.dataCache.PredictionDataCache; +import org.transitclock.db.structs.Location; +import org.transitclock.db.structs.Route; +import org.transitclock.db.structs.StopPath; +import org.transitclock.db.structs.TripPattern; +import org.transitclock.ipc.data.IpcPredictionsForRouteStopDest; +import org.transitclock.utils.Geo; /** * For determining which stops are near a location. This information diff --git a/transitime/src/main/java/org/transitime/gtfs/Test.java b/transitclock/src/main/java/org/transitclock/gtfs/Test.java similarity index 77% rename from transitime/src/main/java/org/transitime/gtfs/Test.java rename to transitclock/src/main/java/org/transitclock/gtfs/Test.java index e399dc0d7..a6f04d051 100644 --- a/transitime/src/main/java/org/transitime/gtfs/Test.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/Test.java @@ -15,37 +15,37 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.gtfs; +package org.transitclock.gtfs; import java.util.List; -import org.transitime.gtfs.gtfsStructs.GtfsAgency; -import org.transitime.gtfs.gtfsStructs.GtfsCalendar; -import org.transitime.gtfs.gtfsStructs.GtfsCalendarDate; -import org.transitime.gtfs.gtfsStructs.GtfsFareAttribute; -import org.transitime.gtfs.gtfsStructs.GtfsFareRule; -import org.transitime.gtfs.gtfsStructs.GtfsFeedInfo; -import org.transitime.gtfs.gtfsStructs.GtfsFrequency; -import org.transitime.gtfs.gtfsStructs.GtfsRoute; -import org.transitime.gtfs.gtfsStructs.GtfsShape; -import org.transitime.gtfs.gtfsStructs.GtfsStop; -import org.transitime.gtfs.gtfsStructs.GtfsStopTime; -import org.transitime.gtfs.gtfsStructs.GtfsTransfer; -import org.transitime.gtfs.gtfsStructs.GtfsTrip; -import org.transitime.gtfs.readers.GtfsAgencyReader; -import org.transitime.gtfs.readers.GtfsCalendarDatesReader; -import org.transitime.gtfs.readers.GtfsCalendarReader; -import org.transitime.gtfs.readers.GtfsFareAttributesReader; -import org.transitime.gtfs.readers.GtfsFareRulesReader; -import org.transitime.gtfs.readers.GtfsFeedInfosReader; -import org.transitime.gtfs.readers.GtfsFrequenciesReader; -import org.transitime.gtfs.readers.GtfsRoutesReader; -import org.transitime.gtfs.readers.GtfsRoutesSupplementReader; -import org.transitime.gtfs.readers.GtfsShapesReader; -import org.transitime.gtfs.readers.GtfsStopTimesReader; -import org.transitime.gtfs.readers.GtfsStopsReader; -import org.transitime.gtfs.readers.GtfsTransfersReader; -import org.transitime.gtfs.readers.GtfsTripsReader; +import org.transitclock.gtfs.gtfsStructs.GtfsAgency; +import org.transitclock.gtfs.gtfsStructs.GtfsCalendar; +import org.transitclock.gtfs.gtfsStructs.GtfsCalendarDate; +import org.transitclock.gtfs.gtfsStructs.GtfsFareAttribute; +import org.transitclock.gtfs.gtfsStructs.GtfsFareRule; +import org.transitclock.gtfs.gtfsStructs.GtfsFeedInfo; +import org.transitclock.gtfs.gtfsStructs.GtfsFrequency; +import org.transitclock.gtfs.gtfsStructs.GtfsRoute; +import org.transitclock.gtfs.gtfsStructs.GtfsShape; +import org.transitclock.gtfs.gtfsStructs.GtfsStop; +import org.transitclock.gtfs.gtfsStructs.GtfsStopTime; +import org.transitclock.gtfs.gtfsStructs.GtfsTransfer; +import org.transitclock.gtfs.gtfsStructs.GtfsTrip; +import org.transitclock.gtfs.readers.GtfsAgencyReader; +import org.transitclock.gtfs.readers.GtfsCalendarDatesReader; +import org.transitclock.gtfs.readers.GtfsCalendarReader; +import org.transitclock.gtfs.readers.GtfsFareAttributesReader; +import org.transitclock.gtfs.readers.GtfsFareRulesReader; +import org.transitclock.gtfs.readers.GtfsFeedInfosReader; +import org.transitclock.gtfs.readers.GtfsFrequenciesReader; +import org.transitclock.gtfs.readers.GtfsRoutesReader; +import org.transitclock.gtfs.readers.GtfsRoutesSupplementReader; +import org.transitclock.gtfs.readers.GtfsShapesReader; +import org.transitclock.gtfs.readers.GtfsStopTimesReader; +import org.transitclock.gtfs.readers.GtfsStopsReader; +import org.transitclock.gtfs.readers.GtfsTransfersReader; +import org.transitclock.gtfs.readers.GtfsTripsReader; /** * diff --git a/transitime/src/main/java/org/transitime/gtfs/TitleFormatter.java b/transitclock/src/main/java/org/transitclock/gtfs/TitleFormatter.java similarity index 95% rename from transitime/src/main/java/org/transitime/gtfs/TitleFormatter.java rename to transitclock/src/main/java/org/transitclock/gtfs/TitleFormatter.java index 6ae17c66d..53674ef1a 100644 --- a/transitime/src/main/java/org/transitime/gtfs/TitleFormatter.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/TitleFormatter.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.gtfs; +package org.transitclock.gtfs; import java.io.BufferedReader; import java.io.FileInputStream; @@ -28,7 +28,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.config.BooleanConfigValue; +import org.transitclock.config.BooleanConfigValue; /** @@ -99,7 +99,7 @@ public RegexInfo(String regex, String replace) { new ArrayList(); private static final BooleanConfigValue capitalize = - new BooleanConfigValue("transitime.gtfs.capitalize", + new BooleanConfigValue("transitclock.gtfs.capitalize", false, "Sometimes GTFS titles have all capital letters or other " + "capitalization issues. If set to true then will properly " diff --git a/transitime/src/main/java/org/transitime/gtfs/TravelTimesProcessorForGtfsUpdates.java b/transitclock/src/main/java/org/transitclock/gtfs/TravelTimesProcessorForGtfsUpdates.java similarity index 88% rename from transitime/src/main/java/org/transitime/gtfs/TravelTimesProcessorForGtfsUpdates.java rename to transitclock/src/main/java/org/transitclock/gtfs/TravelTimesProcessorForGtfsUpdates.java index 46aa1171a..795cee406 100644 --- a/transitime/src/main/java/org/transitime/gtfs/TravelTimesProcessorForGtfsUpdates.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/TravelTimesProcessorForGtfsUpdates.java @@ -14,28 +14,30 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.gtfs; +package org.transitclock.gtfs; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import org.hibernate.HibernateException; import org.hibernate.Session; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.db.structs.ActiveRevisions; -import org.transitime.db.structs.StopPath; -import org.transitime.db.structs.ScheduleTime; -import org.transitime.db.structs.TravelTimesForStopPath; -import org.transitime.db.structs.TravelTimesForStopPath.HowSet; -import org.transitime.db.structs.TravelTimesForTrip; -import org.transitime.db.structs.Trip; -import org.transitime.db.structs.TripPattern; -import org.transitime.gtfs.gtfsStructs.GtfsStopTime; -import org.transitime.utils.Geo; -import org.transitime.utils.IntervalTimer; -import org.transitime.utils.Time; +import org.transitclock.db.structs.ActiveRevisions; +import org.transitclock.db.structs.ScheduleTime; +import org.transitclock.db.structs.StopPath; +import org.transitclock.db.structs.TravelTimesForStopPath; +import org.transitclock.db.structs.TravelTimesForTrip; +import org.transitclock.db.structs.Trip; +import org.transitclock.db.structs.TripPattern; +import org.transitclock.db.structs.TravelTimesForStopPath.HowSet; +import org.transitclock.gtfs.gtfsStructs.GtfsStopTime; +import org.transitclock.utils.Geo; +import org.transitclock.utils.IntervalTimer; +import org.transitclock.utils.Time; /** * For setting travel times when processing GTFS configuration. Tries to use @@ -107,21 +109,16 @@ public TravelTimesProcessorForGtfsUpdates(ActiveRevisions activeRevisions, * @return */ private ScheduleTime getGtfsScheduleTime(String tripId, - TripPattern tripPattern, int stopPathIndex, GtfsData gtfsData) { - String stopId = tripPattern.getStopId(stopPathIndex); + TripPattern tripPattern, int gtfsStopSequence, GtfsData gtfsData) { // Go through list of stops for the tripId. - // Note: bit tricky handling trips where stop is encountered twice. - // But at least can start the for loop at 1 so that don't - // look at all the stop times. At least this will properly handle the - // situations where the first and last stop for a trip are the same. List gtfsStopTimesList = gtfsData.getGtfsStopTimesForTrip(tripId); - for (int stopTimeIdx = stopPathIndex>0 ? 1 : 0; + for (int stopTimeIdx = 0; stopTimeIdx < gtfsStopTimesList.size(); ++stopTimeIdx) { GtfsStopTime gtfsStopTime = gtfsStopTimesList.get(stopTimeIdx); - if (gtfsStopTime.getStopId().equals(stopId)) { + if (gtfsStopTime.getStopSequence() == gtfsStopSequence) { // Found the stop. Integer arr = gtfsStopTime.getArrivalTimeSecs(); Integer dep = gtfsStopTime.getDepartureTimeSecs(); @@ -159,6 +156,10 @@ private TravelTimesForTrip determineTravelTimesBasedOnSchedule(Trip trip, firstPathTravelTimesMsec.add(0); StopPath firstPath = trip.getStopPath(0); + if (firstPath == null) { + logger.error(" trip {} is missing stop paths, skipping travel times", trip.getId()); + return travelTimes; + } TravelTimesForStopPath firstPathTravelTimesForPath = new TravelTimesForStopPath( activeRevisions.getConfigRev(), @@ -175,7 +176,7 @@ private TravelTimesForTrip determineTravelTimesBasedOnSchedule(Trip trip, // Start at index 1 since the first stub path is a special case int previousStopPathWithScheduleTimeIndex = 0; ScheduleTime previousScheduleTime = getGtfsScheduleTime(trip.getId(), - tripPattern, 0, gtfsData); + tripPattern, firstPath.getGtfsStopSeq(), gtfsData); int numberOfPaths = trip.getTripPattern().getNumberStopPaths(); for (int stopPathWithScheduleTimeIndex = 1; stopPathWithScheduleTimeIndex < numberOfPaths; @@ -184,8 +185,9 @@ private TravelTimesForTrip determineTravelTimesBasedOnSchedule(Trip trip, // Can't use the trip stop times because those can be filtered to just // be for schedule adherence stops, which could be a small subset of the // schedule times from the stop_times.txt GTFS file. + int stopSeq = trip.getStopPath(stopPathWithScheduleTimeIndex).getGtfsStopSeq(); ScheduleTime scheduleTime = getGtfsScheduleTime(trip.getId(), - tripPattern, stopPathWithScheduleTimeIndex, gtfsData); + tripPattern, stopSeq, gtfsData); if (scheduleTime == null) continue; @@ -218,6 +220,7 @@ private TravelTimesForTrip determineTravelTimesBasedOnSchedule(Trip trip, msecSpentStopped = elapsedScheduleTimeInSecs*1000; int msecForTravelBetweenScheduleTimes = elapsedScheduleTimeInSecs*1000 - msecSpentStopped; + // Make sure that speed is reasonable, taking default wait // stop times into account. An agency might // mistakenly only provide a few seconds of travel time and @@ -453,7 +456,13 @@ private TravelTimesForTrip travelTimesForTripFromDb(Trip trip, // Found good match of travel times from db logger.debug("Found exact travel time match for " + "tripId={} from database.", trip.getId()); - return ttForTripFromDb; + + if (ttForTripFromDb.isValid()) + return ttForTripFromDb; + + logger.error("Found invalid travel times for " + + "tripId={} from database: {}", trip.getId(), + ttForTripFromDb); } } } @@ -496,7 +505,14 @@ private TravelTimesForTrip travelTimesForTripPatternFromDb(Trip trip, "will use the old one created for tripId={}", trip.getId(), trip.getTripPattern().getId(), ttForTripFromDb.getTripCreatedForId()); - return ttForTripFromDb; + + if (ttForTripFromDb.isValid()) + return ttForTripFromDb; + + logger.error("Found invalid travel time match for " + + "tripId={}, tripPatternId={} from database: {}", + trip.getId(), trip.getTripPattern().getId(), + ttForTripFromDb); } } @@ -542,6 +558,10 @@ private TravelTimesForTrip scheduleBasedTravelTimes(Trip trip, ttForTripFromDbList); } ttForTripFromDbList.add(scheduleBasedTravelTimes); + + // This would indicate a bug in the schedule-based travel time calculation code + if (!scheduleBasedTravelTimes.isValid()) + logger.error("Schedule based travel time is invalid: {}", scheduleBasedTravelTimes); return scheduleBasedTravelTimes; } @@ -610,10 +630,14 @@ private boolean scheduleCloseEnough(Trip trip, * @param gtfsData * @param travelTimesFromDbMap * Map keyed by tripPatternId of Lists of TripPatterns + * @return the number of distinct traveltimes referenced for the travelTimesRev * @throws HibernateException */ - private void processTrips(GtfsData gtfsData, + private Integer processTrips(GtfsData gtfsData, Map> travelTimesFromDbMap) { + // keep a set of travel times for trips for metrics + Set travelTimesForTripIds = new HashSet(); + // For trip read from GTFS data.. for (Trip trip : gtfsData.getTrips()) { TripPattern tripPattern = trip.getTripPattern(); @@ -667,11 +691,14 @@ private void processTrips(GtfsData gtfsData, ttForTripFromDbList, travelTimesFromDbMap); } + // were he keep a count of travel times for later metrics + travelTimesForTripIds.add(travelTimesToUse.getId()); // Set the resulting TravelTimesForTrip for the Trip so travel times // will be stored as part of the trip when the trip is stored to // the database. trip.setTravelTimes(travelTimesToUse); } + return travelTimesForTripIds.size(); } /** @@ -727,21 +754,38 @@ public void process(Session session, GtfsData gtfsData) { TravelTimesForTrip.getTravelTimesForTrips(session, originalTravelTimesRev); - int originalNumberTravelTimes = - numberOfTravelTimes(travelTimesFromDbMap); + setOriginalNumberOfTravelTimes( + numberOfTravelTimes(travelTimesFromDbMap)); // Do the low-level processing - processTrips(gtfsData, travelTimesFromDbMap); + setNumberOfTravelTimes(processTrips(gtfsData, travelTimesFromDbMap)); // Let user know what is going on logger.info("Finished processing travel time data. " + "Number of travel times read from db={}. " + "Total number of travel times needed to cover each trip={}. " + "Total number of trips={}. Took {} msec.", - originalNumberTravelTimes, + getOriginalNumberOfTravelTimes(), numberOfTravelTimes(travelTimesFromDbMap), gtfsData.getTrips().size(), timer.elapsedMsec()); } + int numberOfTravelTimes = 0; + public int getNumberOfTravelTimes() { + return numberOfTravelTimes; + } + public void setNumberOfTravelTimes(Integer numberOfTravelTimes) { + if (numberOfTravelTimes != null) + this.numberOfTravelTimes = numberOfTravelTimes; + } + + int originalNumberOfTravelTimes = 0; + public int getOriginalNumberOfTravelTimes() { + return originalNumberOfTravelTimes; + } + public void setOriginalNumberOfTravelTimes(int numberOfTravelTimes) { + originalNumberOfTravelTimes = numberOfTravelTimes; + } + } diff --git a/transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsAgency.java b/transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsAgency.java similarity index 94% rename from transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsAgency.java rename to transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsAgency.java index 995ca1912..9436388b5 100644 --- a/transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsAgency.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsAgency.java @@ -15,10 +15,10 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.gtfs.gtfsStructs; +package org.transitclock.gtfs.gtfsStructs; import org.apache.commons.csv.CSVRecord; -import org.transitime.utils.csv.CsvBase; +import org.transitclock.utils.csv.CsvBase; /** * diff --git a/transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsCalendar.java b/transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsCalendar.java similarity index 83% rename from transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsCalendar.java rename to transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsCalendar.java index 4372ac717..6f7c23808 100644 --- a/transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsCalendar.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsCalendar.java @@ -15,11 +15,12 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.gtfs.gtfsStructs; +package org.transitclock.gtfs.gtfsStructs; -import java.text.ParseException; import org.apache.commons.csv.CSVRecord; -import org.transitime.utils.csv.CsvBase; +import org.transitclock.utils.csv.CsvBase; + +import java.text.ParseException; /** @@ -66,6 +67,19 @@ public GtfsCalendar(CSVRecord record, boolean supplemental, String fileName) endDate = getRequiredValue(record, "end_date"); } + public GtfsCalendar(String serviceId, String monday, String tuesday, String wednesday, String thursday, String friday, String saturday, String sunday, String startDate, String endDate){ + this.serviceId = serviceId; + this.monday = monday; + this.tuesday = tuesday; + this.wednesday = wednesday; + this.thursday = thursday; + this.friday = friday; + this.saturday = saturday; + this.sunday = sunday; + this.startDate = startDate; + this.endDate = endDate; + } + public String getServiceId() { return serviceId; } diff --git a/transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsCalendarDate.java b/transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsCalendarDate.java similarity index 95% rename from transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsCalendarDate.java rename to transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsCalendarDate.java index e2ac07a60..7d2bfbfb9 100644 --- a/transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsCalendarDate.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsCalendarDate.java @@ -15,11 +15,11 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.gtfs.gtfsStructs; +package org.transitclock.gtfs.gtfsStructs; import java.text.ParseException; import org.apache.commons.csv.CSVRecord; -import org.transitime.utils.csv.CsvBase; +import org.transitclock.utils.csv.CsvBase; /** diff --git a/transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsExtendedStopTime.java b/transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsExtendedStopTime.java similarity index 97% rename from transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsExtendedStopTime.java rename to transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsExtendedStopTime.java index 3aacc4912..06f492ce0 100644 --- a/transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsExtendedStopTime.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsExtendedStopTime.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.gtfs.gtfsStructs; +package org.transitclock.gtfs.gtfsStructs; -import org.transitime.statistics.ScheduleStatistics; +import org.transitclock.statistics.ScheduleStatistics; /** * For when generating more accurate schedule times using AVL data. diff --git a/transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsFareAttribute.java b/transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsFareAttribute.java similarity index 96% rename from transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsFareAttribute.java rename to transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsFareAttribute.java index ca61c3e59..af52218ca 100644 --- a/transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsFareAttribute.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsFareAttribute.java @@ -15,10 +15,10 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.gtfs.gtfsStructs; +package org.transitclock.gtfs.gtfsStructs; import org.apache.commons.csv.CSVRecord; -import org.transitime.utils.csv.CsvBase; +import org.transitclock.utils.csv.CsvBase; /** * A GTFS fare_attributes object. diff --git a/transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsFareRule.java b/transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsFareRule.java similarity index 97% rename from transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsFareRule.java rename to transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsFareRule.java index 32d3c3f01..26a4bba70 100644 --- a/transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsFareRule.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsFareRule.java @@ -15,10 +15,10 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.gtfs.gtfsStructs; +package org.transitclock.gtfs.gtfsStructs; import org.apache.commons.csv.CSVRecord; -import org.transitime.utils.csv.CsvBase; +import org.transitclock.utils.csv.CsvBase; /** * A GTFS fare_rules object. diff --git a/transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsFeedInfo.java b/transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsFeedInfo.java similarity index 96% rename from transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsFeedInfo.java rename to transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsFeedInfo.java index 1ebd318d3..3a0a7ba06 100644 --- a/transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsFeedInfo.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsFeedInfo.java @@ -15,11 +15,11 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.gtfs.gtfsStructs; +package org.transitclock.gtfs.gtfsStructs; import java.text.ParseException; import org.apache.commons.csv.CSVRecord; -import org.transitime.utils.csv.CsvBase; +import org.transitclock.utils.csv.CsvBase; /** diff --git a/transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsFrequency.java b/transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsFrequency.java similarity index 95% rename from transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsFrequency.java rename to transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsFrequency.java index a90e6291c..3f97a02d6 100644 --- a/transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsFrequency.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsFrequency.java @@ -15,11 +15,11 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.gtfs.gtfsStructs; +package org.transitclock.gtfs.gtfsStructs; import org.apache.commons.csv.CSVRecord; -import org.transitime.utils.Time; -import org.transitime.utils.csv.CsvBase; +import org.transitclock.utils.Time; +import org.transitclock.utils.csv.CsvBase; /** * A GTFS frequencies object. diff --git a/transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsRoute.java b/transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsRoute.java similarity index 95% rename from transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsRoute.java rename to transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsRoute.java index 46364b0fb..9e0410aab 100644 --- a/transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsRoute.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsRoute.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.gtfs.gtfsStructs; +package org.transitclock.gtfs.gtfsStructs; import net.jcip.annotations.Immutable; import org.apache.commons.csv.CSVRecord; -import org.transitime.utils.csv.CsvBase; +import org.transitclock.utils.csv.CsvBase; /** diff --git a/transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsShape.java b/transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsShape.java similarity index 97% rename from transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsShape.java rename to transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsShape.java index 4d4f6491b..469c91280 100644 --- a/transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsShape.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsShape.java @@ -14,11 +14,11 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.gtfs.gtfsStructs; +package org.transitclock.gtfs.gtfsStructs; import org.apache.commons.csv.CSVRecord; -import org.transitime.db.structs.Location; -import org.transitime.utils.csv.CsvBase; +import org.transitclock.db.structs.Location; +import org.transitclock.utils.csv.CsvBase; /** diff --git a/transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsStop.java b/transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsStop.java similarity index 99% rename from transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsStop.java rename to transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsStop.java index 460fd8bd7..fada3c521 100644 --- a/transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsStop.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsStop.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.gtfs.gtfsStructs; +package org.transitclock.gtfs.gtfsStructs; import org.apache.commons.csv.CSVRecord; -import org.transitime.utils.csv.CsvBase; +import org.transitclock.utils.csv.CsvBase; /** * A GTFS stops object. diff --git a/transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsStopTime.java b/transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsStopTime.java similarity index 91% rename from transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsStopTime.java rename to transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsStopTime.java index 4d002a79c..660801877 100644 --- a/transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsStopTime.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsStopTime.java @@ -14,11 +14,11 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.gtfs.gtfsStructs; +package org.transitclock.gtfs.gtfsStructs; import org.apache.commons.csv.CSVRecord; -import org.transitime.utils.Time; -import org.transitime.utils.csv.CsvBase; +import org.transitclock.utils.Time; +import org.transitclock.utils.csv.CsvBase; /** * A GTFS stop_times object @@ -48,6 +48,12 @@ public class GtfsStopTime extends CsvBase implements Comparable { // For deleting a stop time via a supplemental stop_times.txt file private final Boolean delete; + + private final Double maxDistance; + + /* This is the max speed for using in calculating how far the spatial matcher + * is to look along the route when the vehicle is at this point. */ + private final Double maxSpeed; /********************** Member Functions **************************/ @@ -86,6 +92,10 @@ public GtfsStopTime(String tripId, String arrivalTimeStr, this.delete = false; this.isWaitStop = null; + + this.maxDistance = null; + this.maxSpeed = null; + } /** @@ -134,6 +144,12 @@ public GtfsStopTime(CSVRecord record, boolean supplemental, String fileName) delete = getOptionalBooleanValue(record, "delete"); isWaitStop = null; + + maxDistance = getOptionalDoubleValue(record, "max_distance"); + + maxSpeed= getOptionalDoubleValue(record, "max_speed"); + + } /** @@ -183,6 +199,8 @@ protected GtfsStopTime(GtfsStopTime originalValues, Integer newArrivalTime, delete = false; isWaitStop = null; + maxDistance=originalValues.maxDistance; + maxSpeed=originalValues.maxSpeed; } /** @@ -214,6 +232,8 @@ public GtfsStopTime(GtfsStopTime originalValues, Integer newArrivalTimeSecs) { // Handle the special members arrivalTimeSecs = newArrivalTimeSecs; isWaitStop = true; + maxDistance=originalValues.maxDistance; + maxSpeed=originalValues.maxSpeed; } /** @@ -232,6 +252,14 @@ public GtfsStopTime(GtfsStopTime originalStopTime, GtfsStopTime supplementStopTi GtfsStopTime s = supplementStopTime; this.tripId = o.tripId; + this.maxDistance = + s.maxDistance == null ? o.maxDistance + : s.maxDistance; + + this.maxSpeed = + s.maxSpeed == null ? o.maxSpeed + : s.maxSpeed; + this.arrivalTimeSecs = s.arrivalTimeSecs == null ? o.arrivalTimeSecs : s.arrivalTimeSecs; @@ -255,6 +283,10 @@ public GtfsStopTime(GtfsStopTime originalStopTime, GtfsStopTime supplementStopTi this.isWaitStop = s.isWaitStop == null ? o.isWaitStop : s.isWaitStop; } + public Double getMaxDistance() { + return maxDistance; + } + public String getTripId() { return tripId; } @@ -339,9 +371,15 @@ public String toString() { + ", ": "") + "delete=" + delete + ", " + (isWaitStop != null ? "isWaitStop=" + isWaitStop : "") + + (maxDistance != null ? "maxDistance=" + maxDistance : "") + + (maxSpeed != null ? "maxSpeed=" + maxSpeed : "") + "]"; } + public Double getMaxSpeed() { + return maxSpeed; + } + /** * So can use Collections.sort() to sort an Array of GtfsStopTime objects by * stop sequence. diff --git a/transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsTransfer.java b/transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsTransfer.java similarity index 96% rename from transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsTransfer.java rename to transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsTransfer.java index 23ece17d6..be9098d2b 100644 --- a/transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsTransfer.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsTransfer.java @@ -15,10 +15,10 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.gtfs.gtfsStructs; +package org.transitclock.gtfs.gtfsStructs; import org.apache.commons.csv.CSVRecord; -import org.transitime.utils.csv.CsvBase; +import org.transitclock.utils.csv.CsvBase; /** * diff --git a/transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsTrip.java b/transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsTrip.java similarity index 94% rename from transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsTrip.java rename to transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsTrip.java index 2ac7be0e1..e5724902e 100644 --- a/transitime/src/main/java/org/transitime/gtfs/gtfsStructs/GtfsTrip.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/GtfsTrip.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.gtfs.gtfsStructs; +package org.transitclock.gtfs.gtfsStructs; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.csv.CSVRecord; -import org.transitime.config.StringConfigValue; -import org.transitime.utils.csv.CsvBase; +import org.transitclock.config.StringConfigValue; +import org.transitclock.utils.csv.CsvBase; /** * A GTFS trip object as read from the trips.txt GTFS file. @@ -46,7 +46,7 @@ public class GtfsTrip extends CsvBase { // trip_short_name is not specified in GTFS file. // Default of null means simply use trip_id without any modification. private static StringConfigValue tripShortNameRegEx = new StringConfigValue( - "transitime.gtfs.tripShortNameRegEx", + "transitclock.gtfs.tripShortNameRegEx", null, "For agencies where trip short name not specified can use this " + "regular expression to determine the short name from the trip " @@ -57,7 +57,7 @@ public class GtfsTrip extends CsvBase { // For determining proper block_id that corresponds to AVL feed // Default of null means simply use block_id without any modification. private static StringConfigValue blockIdRegEx = new StringConfigValue( - "transitime.gtfs.blockIdRegEx", + "transitclock.gtfs.blockIdRegEx", null, "For agencies where block ID from GTFS datda needs to be modified " + "to match that of the AVL feed. Can use this " @@ -213,7 +213,7 @@ public GtfsTrip(String tripShortName, String blockId) { /** * Many agencies don't specify a trip_short_name. For these use the trip_id - * or if the transitime.gtfs.tripShortNameRegEx is set to determine a group + * or if the transitclock.gtfs.tripShortNameRegEx is set to determine a group * then use that group in the tripId. For example, if tripId is * "345-long unneeded description" and the regex is set to "(.*?)-" then * returned trip short name will be 345. @@ -254,7 +254,7 @@ private static String getTripShortName(String tripShortName, String tripId) { /** * In case block IDs from GTFS needs to be modified to match block IDs from - * AVL feed. Uses the property transitime.gtfs.blockIdRegEx if it is not + * AVL feed. Uses the property transitclock.gtfs.blockIdRegEx if it is not * null. * * @param originalBlockId diff --git a/transitime/src/main/java/org/transitime/gtfs/gtfsStructs/package-info.java b/transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/package-info.java similarity index 95% rename from transitime/src/main/java/org/transitime/gtfs/gtfsStructs/package-info.java rename to transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/package-info.java index 185737820..d3288e376 100644 --- a/transitime/src/main/java/org/transitime/gtfs/gtfsStructs/package-info.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/gtfsStructs/package-info.java @@ -21,4 +21,4 @@ * @author SkiBu Smith * */ -package org.transitime.gtfs.gtfsStructs; \ No newline at end of file +package org.transitclock.gtfs.gtfsStructs; \ No newline at end of file diff --git a/transitime/src/main/java/org/transitime/gtfs/package-info.java b/transitclock/src/main/java/org/transitclock/gtfs/package-info.java similarity index 96% rename from transitime/src/main/java/org/transitime/gtfs/package-info.java rename to transitclock/src/main/java/org/transitclock/gtfs/package-info.java index e5fd44f04..defd520fc 100644 --- a/transitime/src/main/java/org/transitime/gtfs/package-info.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/package-info.java @@ -21,4 +21,4 @@ * @author SkiBu Smith * */ -package org.transitime.gtfs; \ No newline at end of file +package org.transitclock.gtfs; \ No newline at end of file diff --git a/transitime/src/main/java/org/transitime/gtfs/readers/GtfsAgenciesSupplementReader.java b/transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsAgenciesSupplementReader.java similarity index 86% rename from transitime/src/main/java/org/transitime/gtfs/readers/GtfsAgenciesSupplementReader.java rename to transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsAgenciesSupplementReader.java index d49e24e02..e2dbda0d5 100644 --- a/transitime/src/main/java/org/transitime/gtfs/readers/GtfsAgenciesSupplementReader.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsAgenciesSupplementReader.java @@ -14,13 +14,13 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.gtfs.readers; +package org.transitclock.gtfs.readers; import java.text.ParseException; import org.apache.commons.csv.CSVRecord; -import org.transitime.gtfs.gtfsStructs.GtfsAgency; -import org.transitime.utils.csv.CsvBaseReader; +import org.transitclock.gtfs.gtfsStructs.GtfsAgency; +import org.transitclock.utils.csv.CsvBaseReader; /** * GTFS reader for supplemental agency.txt file diff --git a/transitime/src/main/java/org/transitime/gtfs/readers/GtfsAgencyReader.java b/transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsAgencyReader.java similarity index 93% rename from transitime/src/main/java/org/transitime/gtfs/readers/GtfsAgencyReader.java rename to transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsAgencyReader.java index 4d9ec1a26..a240dd383 100644 --- a/transitime/src/main/java/org/transitime/gtfs/readers/GtfsAgencyReader.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsAgencyReader.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.gtfs.readers; +package org.transitclock.gtfs.readers; import java.text.ParseException; import java.util.List; import org.apache.commons.csv.CSVRecord; -import org.transitime.gtfs.gtfsStructs.GtfsAgency; -import org.transitime.utils.csv.CsvBaseReader; +import org.transitclock.gtfs.gtfsStructs.GtfsAgency; +import org.transitclock.utils.csv.CsvBaseReader; /** diff --git a/transitime/src/main/java/org/transitime/gtfs/readers/GtfsCalendarDatesReader.java b/transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsCalendarDatesReader.java similarity index 89% rename from transitime/src/main/java/org/transitime/gtfs/readers/GtfsCalendarDatesReader.java rename to transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsCalendarDatesReader.java index 0aefee03f..5161f392e 100644 --- a/transitime/src/main/java/org/transitime/gtfs/readers/GtfsCalendarDatesReader.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsCalendarDatesReader.java @@ -14,13 +14,13 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.gtfs.readers; +package org.transitclock.gtfs.readers; import java.text.ParseException; import org.apache.commons.csv.CSVRecord; -import org.transitime.gtfs.gtfsStructs.GtfsCalendarDate; -import org.transitime.utils.csv.CsvBaseReader; +import org.transitclock.gtfs.gtfsStructs.GtfsCalendarDate; +import org.transitclock.utils.csv.CsvBaseReader; /** * GTFS reader for the calendar_dates.txt file diff --git a/transitime/src/main/java/org/transitime/gtfs/readers/GtfsCalendarReader.java b/transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsCalendarReader.java similarity index 89% rename from transitime/src/main/java/org/transitime/gtfs/readers/GtfsCalendarReader.java rename to transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsCalendarReader.java index 14cc0c563..129e59b01 100644 --- a/transitime/src/main/java/org/transitime/gtfs/readers/GtfsCalendarReader.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsCalendarReader.java @@ -14,13 +14,13 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.gtfs.readers; +package org.transitclock.gtfs.readers; import java.text.ParseException; import org.apache.commons.csv.CSVRecord; -import org.transitime.gtfs.gtfsStructs.GtfsCalendar; -import org.transitime.utils.csv.CsvBaseReader; +import org.transitclock.gtfs.gtfsStructs.GtfsCalendar; +import org.transitclock.utils.csv.CsvBaseReader; /** * diff --git a/transitime/src/main/java/org/transitime/gtfs/readers/GtfsFareAttributesReader.java b/transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsFareAttributesReader.java similarity index 89% rename from transitime/src/main/java/org/transitime/gtfs/readers/GtfsFareAttributesReader.java rename to transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsFareAttributesReader.java index 94dc6ac9f..50594b338 100644 --- a/transitime/src/main/java/org/transitime/gtfs/readers/GtfsFareAttributesReader.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsFareAttributesReader.java @@ -14,13 +14,13 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.gtfs.readers; +package org.transitclock.gtfs.readers; import java.text.ParseException; import org.apache.commons.csv.CSVRecord; -import org.transitime.gtfs.gtfsStructs.GtfsFareAttribute; -import org.transitime.utils.csv.CsvBaseReader; +import org.transitclock.gtfs.gtfsStructs.GtfsFareAttribute; +import org.transitclock.utils.csv.CsvBaseReader; /** diff --git a/transitime/src/main/java/org/transitime/gtfs/readers/GtfsFareRulesReader.java b/transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsFareRulesReader.java similarity index 89% rename from transitime/src/main/java/org/transitime/gtfs/readers/GtfsFareRulesReader.java rename to transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsFareRulesReader.java index e9c954c0a..3890f2289 100644 --- a/transitime/src/main/java/org/transitime/gtfs/readers/GtfsFareRulesReader.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsFareRulesReader.java @@ -14,13 +14,13 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.gtfs.readers; +package org.transitclock.gtfs.readers; import java.text.ParseException; import org.apache.commons.csv.CSVRecord; -import org.transitime.gtfs.gtfsStructs.GtfsFareRule; -import org.transitime.utils.csv.CsvBaseReader; +import org.transitclock.gtfs.gtfsStructs.GtfsFareRule; +import org.transitclock.utils.csv.CsvBaseReader; /** * GTFS reader for the fare_rules.txt file diff --git a/transitime/src/main/java/org/transitime/gtfs/readers/GtfsFeedInfosReader.java b/transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsFeedInfosReader.java similarity index 89% rename from transitime/src/main/java/org/transitime/gtfs/readers/GtfsFeedInfosReader.java rename to transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsFeedInfosReader.java index 8aba11b33..a562277c0 100644 --- a/transitime/src/main/java/org/transitime/gtfs/readers/GtfsFeedInfosReader.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsFeedInfosReader.java @@ -14,13 +14,13 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.gtfs.readers; +package org.transitclock.gtfs.readers; import java.text.ParseException; import org.apache.commons.csv.CSVRecord; -import org.transitime.gtfs.gtfsStructs.GtfsFeedInfo; -import org.transitime.utils.csv.CsvBaseReader; +import org.transitclock.gtfs.gtfsStructs.GtfsFeedInfo; +import org.transitclock.utils.csv.CsvBaseReader; /** * GTFS reader for the feed_info.txt file diff --git a/transitime/src/main/java/org/transitime/gtfs/readers/GtfsFrequenciesReader.java b/transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsFrequenciesReader.java similarity index 84% rename from transitime/src/main/java/org/transitime/gtfs/readers/GtfsFrequenciesReader.java rename to transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsFrequenciesReader.java index 277635973..bbfba1843 100644 --- a/transitime/src/main/java/org/transitime/gtfs/readers/GtfsFrequenciesReader.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsFrequenciesReader.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.gtfs.readers; +package org.transitclock.gtfs.readers; import java.text.ParseException; import org.apache.commons.csv.CSVRecord; -import org.transitime.gtfs.GtfsData; -import org.transitime.gtfs.gtfsStructs.GtfsFrequency; -import org.transitime.utils.csv.CsvBaseReader; +import org.transitclock.gtfs.GtfsData; +import org.transitclock.gtfs.gtfsStructs.GtfsFrequency; +import org.transitclock.utils.csv.CsvBaseReader; /** * GTFS reader for the frequencies.txt file diff --git a/transitime/src/main/java/org/transitime/gtfs/readers/GtfsRoutesReader.java b/transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsRoutesReader.java similarity index 84% rename from transitime/src/main/java/org/transitime/gtfs/readers/GtfsRoutesReader.java rename to transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsRoutesReader.java index 14d8177f6..e88a931d3 100644 --- a/transitime/src/main/java/org/transitime/gtfs/readers/GtfsRoutesReader.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsRoutesReader.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.gtfs.readers; +package org.transitclock.gtfs.readers; import java.text.ParseException; import org.apache.commons.csv.CSVRecord; -import org.transitime.gtfs.GtfsData; -import org.transitime.gtfs.gtfsStructs.GtfsRoute; -import org.transitime.utils.csv.CsvBaseReader; +import org.transitclock.gtfs.GtfsData; +import org.transitclock.gtfs.gtfsStructs.GtfsRoute; +import org.transitclock.utils.csv.CsvBaseReader; /** * GTFS reader for the route.txt file diff --git a/transitime/src/main/java/org/transitime/gtfs/readers/GtfsRoutesSupplementReader.java b/transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsRoutesSupplementReader.java similarity index 89% rename from transitime/src/main/java/org/transitime/gtfs/readers/GtfsRoutesSupplementReader.java rename to transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsRoutesSupplementReader.java index fed6bf1b9..0252458ee 100644 --- a/transitime/src/main/java/org/transitime/gtfs/readers/GtfsRoutesSupplementReader.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsRoutesSupplementReader.java @@ -14,13 +14,13 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.gtfs.readers; +package org.transitclock.gtfs.readers; import java.text.ParseException; import org.apache.commons.csv.CSVRecord; -import org.transitime.gtfs.gtfsStructs.GtfsRoute; -import org.transitime.utils.csv.CsvBaseReader; +import org.transitclock.gtfs.gtfsStructs.GtfsRoute; +import org.transitclock.utils.csv.CsvBaseReader; /** * GTFS reader for the routeSupplement.txt file diff --git a/transitime/src/main/java/org/transitime/gtfs/readers/GtfsShapesReader.java b/transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsShapesReader.java similarity index 89% rename from transitime/src/main/java/org/transitime/gtfs/readers/GtfsShapesReader.java rename to transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsShapesReader.java index ff38ff19b..ed34b0063 100644 --- a/transitime/src/main/java/org/transitime/gtfs/readers/GtfsShapesReader.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsShapesReader.java @@ -14,13 +14,13 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.gtfs.readers; +package org.transitclock.gtfs.readers; import java.text.ParseException; import org.apache.commons.csv.CSVRecord; -import org.transitime.gtfs.gtfsStructs.GtfsShape; -import org.transitime.utils.csv.CsvBaseReader; +import org.transitclock.gtfs.gtfsStructs.GtfsShape; +import org.transitclock.utils.csv.CsvBaseReader; /** diff --git a/transitime/src/main/java/org/transitime/gtfs/readers/GtfsShapesSupplementReader.java b/transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsShapesSupplementReader.java similarity index 87% rename from transitime/src/main/java/org/transitime/gtfs/readers/GtfsShapesSupplementReader.java rename to transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsShapesSupplementReader.java index c937b05e6..b64732474 100644 --- a/transitime/src/main/java/org/transitime/gtfs/readers/GtfsShapesSupplementReader.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsShapesSupplementReader.java @@ -14,13 +14,13 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.gtfs.readers; +package org.transitclock.gtfs.readers; import java.text.ParseException; import org.apache.commons.csv.CSVRecord; -import org.transitime.gtfs.gtfsStructs.GtfsShape; -import org.transitime.utils.csv.CsvBaseReader; +import org.transitclock.gtfs.gtfsStructs.GtfsShape; +import org.transitclock.utils.csv.CsvBaseReader; /** * GTFS reader for supplemental shapes.txt file. Useful for moving or deleting diff --git a/transitime/src/main/java/org/transitime/gtfs/readers/GtfsStopTimesReader.java b/transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsStopTimesReader.java similarity index 84% rename from transitime/src/main/java/org/transitime/gtfs/readers/GtfsStopTimesReader.java rename to transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsStopTimesReader.java index 7f9996f85..bd007d20b 100644 --- a/transitime/src/main/java/org/transitime/gtfs/readers/GtfsStopTimesReader.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsStopTimesReader.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.gtfs.readers; +package org.transitclock.gtfs.readers; import java.text.ParseException; import org.apache.commons.csv.CSVRecord; -import org.transitime.gtfs.GtfsData; -import org.transitime.gtfs.gtfsStructs.GtfsStopTime; -import org.transitime.utils.csv.CsvBaseReader; +import org.transitclock.gtfs.GtfsData; +import org.transitclock.gtfs.gtfsStructs.GtfsStopTime; +import org.transitclock.utils.csv.CsvBaseReader; /** * GTFS reader for the stop_times.txt file diff --git a/transitime/src/main/java/org/transitime/gtfs/readers/GtfsStopTimesSupplementReader.java b/transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsStopTimesSupplementReader.java similarity index 86% rename from transitime/src/main/java/org/transitime/gtfs/readers/GtfsStopTimesSupplementReader.java rename to transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsStopTimesSupplementReader.java index 08067c0b0..2800e7058 100644 --- a/transitime/src/main/java/org/transitime/gtfs/readers/GtfsStopTimesSupplementReader.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsStopTimesSupplementReader.java @@ -14,13 +14,13 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.gtfs.readers; +package org.transitclock.gtfs.readers; import java.text.ParseException; import org.apache.commons.csv.CSVRecord; -import org.transitime.gtfs.gtfsStructs.GtfsStopTime; -import org.transitime.utils.csv.CsvBaseReader; +import org.transitclock.gtfs.gtfsStructs.GtfsStopTime; +import org.transitclock.utils.csv.CsvBaseReader; /** * GTFS reader for supplemental stop_times.txt file diff --git a/transitime/src/main/java/org/transitime/gtfs/readers/GtfsStopsReader.java b/transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsStopsReader.java similarity index 89% rename from transitime/src/main/java/org/transitime/gtfs/readers/GtfsStopsReader.java rename to transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsStopsReader.java index cff78951b..480cd27b1 100644 --- a/transitime/src/main/java/org/transitime/gtfs/readers/GtfsStopsReader.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsStopsReader.java @@ -14,13 +14,13 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.gtfs.readers; +package org.transitclock.gtfs.readers; import java.text.ParseException; import org.apache.commons.csv.CSVRecord; -import org.transitime.gtfs.gtfsStructs.GtfsStop; -import org.transitime.utils.csv.CsvBaseReader; +import org.transitclock.gtfs.gtfsStructs.GtfsStop; +import org.transitclock.utils.csv.CsvBaseReader; /** diff --git a/transitime/src/main/java/org/transitime/gtfs/readers/GtfsStopsSupplementReader.java b/transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsStopsSupplementReader.java similarity index 89% rename from transitime/src/main/java/org/transitime/gtfs/readers/GtfsStopsSupplementReader.java rename to transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsStopsSupplementReader.java index b968a482f..61830f675 100644 --- a/transitime/src/main/java/org/transitime/gtfs/readers/GtfsStopsSupplementReader.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsStopsSupplementReader.java @@ -14,13 +14,13 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.gtfs.readers; +package org.transitclock.gtfs.readers; import java.text.ParseException; import org.apache.commons.csv.CSVRecord; -import org.transitime.gtfs.gtfsStructs.GtfsStop; -import org.transitime.utils.csv.CsvBaseReader; +import org.transitclock.gtfs.gtfsStructs.GtfsStop; +import org.transitclock.utils.csv.CsvBaseReader; /** diff --git a/transitime/src/main/java/org/transitime/gtfs/readers/GtfsTransfersReader.java b/transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsTransfersReader.java similarity index 89% rename from transitime/src/main/java/org/transitime/gtfs/readers/GtfsTransfersReader.java rename to transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsTransfersReader.java index 2e0edc99b..0d2428ecf 100644 --- a/transitime/src/main/java/org/transitime/gtfs/readers/GtfsTransfersReader.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsTransfersReader.java @@ -14,13 +14,13 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.gtfs.readers; +package org.transitclock.gtfs.readers; import java.text.ParseException; import org.apache.commons.csv.CSVRecord; -import org.transitime.gtfs.gtfsStructs.GtfsTransfer; -import org.transitime.utils.csv.CsvBaseReader; +import org.transitclock.gtfs.gtfsStructs.GtfsTransfer; +import org.transitclock.utils.csv.CsvBaseReader; /** diff --git a/transitime/src/main/java/org/transitime/gtfs/readers/GtfsTripsReader.java b/transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsTripsReader.java similarity index 85% rename from transitime/src/main/java/org/transitime/gtfs/readers/GtfsTripsReader.java rename to transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsTripsReader.java index ab6378f3f..b47f463dc 100644 --- a/transitime/src/main/java/org/transitime/gtfs/readers/GtfsTripsReader.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsTripsReader.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.gtfs.readers; +package org.transitclock.gtfs.readers; import java.text.ParseException; import org.apache.commons.csv.CSVRecord; -import org.transitime.gtfs.GtfsData; -import org.transitime.gtfs.gtfsStructs.GtfsTrip; -import org.transitime.utils.csv.CsvBaseReader; +import org.transitclock.gtfs.GtfsData; +import org.transitclock.gtfs.gtfsStructs.GtfsTrip; +import org.transitclock.utils.csv.CsvBaseReader; /** * GTFS reader for the trips.txt file diff --git a/transitime/src/main/java/org/transitime/gtfs/readers/GtfsTripsSupplementReader.java b/transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsTripsSupplementReader.java similarity index 89% rename from transitime/src/main/java/org/transitime/gtfs/readers/GtfsTripsSupplementReader.java rename to transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsTripsSupplementReader.java index 4d1cd7dc5..7434b2257 100644 --- a/transitime/src/main/java/org/transitime/gtfs/readers/GtfsTripsSupplementReader.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/readers/GtfsTripsSupplementReader.java @@ -15,13 +15,13 @@ * along with Transitime.org . If not, see . */ -package org.transitime.gtfs.readers; +package org.transitclock.gtfs.readers; import java.text.ParseException; import org.apache.commons.csv.CSVRecord; -import org.transitime.gtfs.gtfsStructs.GtfsTrip; -import org.transitime.utils.csv.CsvBaseReader; +import org.transitclock.gtfs.gtfsStructs.GtfsTrip; +import org.transitclock.utils.csv.CsvBaseReader; /** * GTFS reader for supplemental trips.txt file diff --git a/transitime/src/main/java/org/transitime/gtfs/readers/package-info.java b/transitclock/src/main/java/org/transitclock/gtfs/readers/package-info.java similarity index 94% rename from transitime/src/main/java/org/transitime/gtfs/readers/package-info.java rename to transitclock/src/main/java/org/transitclock/gtfs/readers/package-info.java index ef0f62fc2..d23eef335 100644 --- a/transitime/src/main/java/org/transitime/gtfs/readers/package-info.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/readers/package-info.java @@ -17,7 +17,7 @@ /** * For reading in a GTFS file in CSV format. These classes extend - * org.transitime.utils.csv.CsvBaseReader class which uses + * org.transitclock.utils.csv.CsvBaseReader class which uses * the org.apache.commons.csv package for actually processing CSV files. *

* A key feature of these readers is that they handle additional columns @@ -42,4 +42,4 @@ * @author SkiBu Smith * */ -package org.transitime.gtfs.readers; +package org.transitclock.gtfs.readers; diff --git a/transitime/src/main/java/org/transitime/gtfs/writers/GtfsExtendedStopTimesWriter.java b/transitclock/src/main/java/org/transitclock/gtfs/writers/GtfsExtendedStopTimesWriter.java similarity index 96% rename from transitime/src/main/java/org/transitime/gtfs/writers/GtfsExtendedStopTimesWriter.java rename to transitclock/src/main/java/org/transitclock/gtfs/writers/GtfsExtendedStopTimesWriter.java index 1ed041df8..5a1757556 100644 --- a/transitime/src/main/java/org/transitime/gtfs/writers/GtfsExtendedStopTimesWriter.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/writers/GtfsExtendedStopTimesWriter.java @@ -14,11 +14,12 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.gtfs.writers; +package org.transitclock.gtfs.writers; import java.io.IOException; -import org.transitime.gtfs.gtfsStructs.GtfsExtendedStopTime; -import org.transitime.utils.StringUtils; + +import org.transitclock.gtfs.gtfsStructs.GtfsExtendedStopTime; +import org.transitclock.utils.StringUtils; /** * For writing out extended GTFS stop_times.txt file that contains additional diff --git a/transitime/src/main/java/org/transitime/gtfs/writers/GtfsRoutesWriter.java b/transitclock/src/main/java/org/transitclock/gtfs/writers/GtfsRoutesWriter.java similarity index 93% rename from transitime/src/main/java/org/transitime/gtfs/writers/GtfsRoutesWriter.java rename to transitclock/src/main/java/org/transitclock/gtfs/writers/GtfsRoutesWriter.java index d8c22a8c3..c00788c89 100644 --- a/transitime/src/main/java/org/transitime/gtfs/writers/GtfsRoutesWriter.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/writers/GtfsRoutesWriter.java @@ -15,12 +15,12 @@ * along with Transitime.org . If not, see . */ -package org.transitime.gtfs.writers; +package org.transitclock.gtfs.writers; import java.io.IOException; -import org.transitime.gtfs.gtfsStructs.GtfsRoute; -import org.transitime.utils.csv.CsvWriterBase; +import org.transitclock.gtfs.gtfsStructs.GtfsRoute; +import org.transitclock.utils.csv.CsvWriterBase; /** * For writing out the routes.txt GTFS file. Useful for when need to modify the diff --git a/transitime/src/main/java/org/transitime/gtfs/writers/GtfsShapesWriter.java b/transitclock/src/main/java/org/transitclock/gtfs/writers/GtfsShapesWriter.java similarity index 94% rename from transitime/src/main/java/org/transitime/gtfs/writers/GtfsShapesWriter.java rename to transitclock/src/main/java/org/transitclock/gtfs/writers/GtfsShapesWriter.java index 6d26d5b8a..be1709b42 100644 --- a/transitime/src/main/java/org/transitime/gtfs/writers/GtfsShapesWriter.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/writers/GtfsShapesWriter.java @@ -14,13 +14,13 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.gtfs.writers; +package org.transitclock.gtfs.writers; import java.io.IOException; import java.text.DecimalFormat; -import org.transitime.gtfs.gtfsStructs.GtfsShape; -import org.transitime.utils.csv.CsvWriterBase; +import org.transitclock.gtfs.gtfsStructs.GtfsShape; +import org.transitclock.utils.csv.CsvWriterBase; /** * Writes out a GTFS shapes.txt file. Useful for when need to modify the file, diff --git a/transitime/src/main/java/org/transitime/gtfs/writers/GtfsStopTimesWriter.java b/transitclock/src/main/java/org/transitclock/gtfs/writers/GtfsStopTimesWriter.java similarity index 94% rename from transitime/src/main/java/org/transitime/gtfs/writers/GtfsStopTimesWriter.java rename to transitclock/src/main/java/org/transitclock/gtfs/writers/GtfsStopTimesWriter.java index afd45f5de..0bd91b84d 100644 --- a/transitime/src/main/java/org/transitime/gtfs/writers/GtfsStopTimesWriter.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/writers/GtfsStopTimesWriter.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.gtfs.writers; +package org.transitclock.gtfs.writers; import java.io.IOException; import java.io.Writer; -import org.transitime.gtfs.gtfsStructs.GtfsStopTime; -import org.transitime.utils.StringUtils; -import org.transitime.utils.Time; -import org.transitime.utils.csv.CsvWriterBase; +import org.transitclock.gtfs.gtfsStructs.GtfsStopTime; +import org.transitclock.utils.StringUtils; +import org.transitclock.utils.Time; +import org.transitclock.utils.csv.CsvWriterBase; /** * For writing a GTFS stop_times.txt file. This class is useful when updating diff --git a/transitime/src/main/java/org/transitime/gtfs/writers/GtfsStopsWriter.java b/transitclock/src/main/java/org/transitclock/gtfs/writers/GtfsStopsWriter.java similarity index 95% rename from transitime/src/main/java/org/transitime/gtfs/writers/GtfsStopsWriter.java rename to transitclock/src/main/java/org/transitclock/gtfs/writers/GtfsStopsWriter.java index 1c2385361..f1860f948 100644 --- a/transitime/src/main/java/org/transitime/gtfs/writers/GtfsStopsWriter.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/writers/GtfsStopsWriter.java @@ -14,13 +14,13 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.gtfs.writers; +package org.transitclock.gtfs.writers; import java.io.IOException; import java.text.DecimalFormat; -import org.transitime.gtfs.gtfsStructs.GtfsStop; -import org.transitime.utils.csv.CsvWriterBase; +import org.transitclock.gtfs.gtfsStructs.GtfsStop; +import org.transitclock.utils.csv.CsvWriterBase; /** diff --git a/transitime/src/main/java/org/transitime/gtfs/writers/GtfsTripsWriter.java b/transitclock/src/main/java/org/transitclock/gtfs/writers/GtfsTripsWriter.java similarity index 94% rename from transitime/src/main/java/org/transitime/gtfs/writers/GtfsTripsWriter.java rename to transitclock/src/main/java/org/transitclock/gtfs/writers/GtfsTripsWriter.java index cff4d131a..713237a5d 100644 --- a/transitime/src/main/java/org/transitime/gtfs/writers/GtfsTripsWriter.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/writers/GtfsTripsWriter.java @@ -15,12 +15,12 @@ * along with Transitime.org . If not, see . */ -package org.transitime.gtfs.writers; +package org.transitclock.gtfs.writers; import java.io.IOException; -import org.transitime.gtfs.gtfsStructs.GtfsTrip; -import org.transitime.utils.csv.CsvWriterBase; +import org.transitclock.gtfs.gtfsStructs.GtfsTrip; +import org.transitclock.utils.csv.CsvWriterBase; /** * For writing out the trips.txt GTFS file. Useful for when need to modify the diff --git a/transitime/src/main/java/org/transitime/gtfs/writers/package-info.java b/transitclock/src/main/java/org/transitclock/gtfs/writers/package-info.java similarity index 95% rename from transitime/src/main/java/org/transitime/gtfs/writers/package-info.java rename to transitclock/src/main/java/org/transitclock/gtfs/writers/package-info.java index 7d9c5abc1..6a93c5091 100644 --- a/transitime/src/main/java/org/transitime/gtfs/writers/package-info.java +++ b/transitclock/src/main/java/org/transitclock/gtfs/writers/package-info.java @@ -21,4 +21,4 @@ * @author SkiBu Smith * */ -package org.transitime.gtfs.writers; \ No newline at end of file +package org.transitclock.gtfs.writers; \ No newline at end of file diff --git a/transitclock/src/main/java/org/transitclock/guice/modules/ReportingModule.java b/transitclock/src/main/java/org/transitclock/guice/modules/ReportingModule.java new file mode 100644 index 000000000..62b83ba26 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/guice/modules/ReportingModule.java @@ -0,0 +1,18 @@ +package org.transitclock.guice.modules; + +import com.google.inject.AbstractModule; +import com.google.inject.Scopes; +import org.transitclock.reporting.dao.RunTimeRoutesDao; +import org.transitclock.reporting.service.OnTimePerformanceService; +import org.transitclock.reporting.service.RunTimeService; +import org.transitclock.reporting.service.SpeedMapService; + +public class ReportingModule extends AbstractModule { + @Override + protected void configure() { + bind(RunTimeRoutesDao.class).in(Scopes.SINGLETON); + bind(RunTimeService.class).in(Scopes.SINGLETON); + bind(OnTimePerformanceService.class).in(Scopes.SINGLETON); + bind(SpeedMapService.class).in(Scopes.SINGLETON); + } +} diff --git a/transitime/src/main/java/org/transitime/ipc/clients/AgencyMonitorClient.java b/transitclock/src/main/java/org/transitclock/ipc/clients/AgencyMonitorClient.java similarity index 96% rename from transitime/src/main/java/org/transitime/ipc/clients/AgencyMonitorClient.java rename to transitclock/src/main/java/org/transitclock/ipc/clients/AgencyMonitorClient.java index 3868e56f0..fbeb86d37 100644 --- a/transitime/src/main/java/org/transitime/ipc/clients/AgencyMonitorClient.java +++ b/transitclock/src/main/java/org/transitclock/ipc/clients/AgencyMonitorClient.java @@ -15,16 +15,16 @@ * along with Transitime.org . If not, see . */ -package org.transitime.ipc.clients; +package org.transitclock.ipc.clients; import java.rmi.RemoteException; import java.util.Collection; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.db.webstructs.WebAgency; -import org.transitime.ipc.interfaces.ConfigInterface; -import org.transitime.ipc.interfaces.ServerStatusInterface; +import org.transitclock.db.webstructs.WebAgency; +import org.transitclock.ipc.interfaces.ConfigInterface; +import org.transitclock.ipc.interfaces.ServerStatusInterface; /** * Makes the ServerStatusInterface.monitor() RMI call easy to access. diff --git a/transitclock/src/main/java/org/transitclock/ipc/clients/CacheQueryInterfaceFactory.java b/transitclock/src/main/java/org/transitclock/ipc/clients/CacheQueryInterfaceFactory.java new file mode 100755 index 000000000..2ff57bf95 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/clients/CacheQueryInterfaceFactory.java @@ -0,0 +1,58 @@ +/* + * 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.ipc.clients; + +import java.util.HashMap; +import java.util.Map; + +import org.transitclock.ipc.interfaces.CacheQueryInterface; +import org.transitclock.ipc.interfaces.CommandsInterface; +import org.transitclock.ipc.rmi.ClientFactory; + +/** + * Provides a CacheQueryInterface client that can be sent cache queries. + * + * @author Sean Og Crudden + * + */ +public class CacheQueryInterfaceFactory { + + // Keyed by agencyId + private static Map cachequeryInterfaceMap = + new HashMap(); + + /********************** Member Functions **************************/ + + /** + * Gets the singleton instance. + * + * @param agencyId + * @return + */ + public static CacheQueryInterface get(String agencyId) { + CacheQueryInterface cachequeryInterface = + cachequeryInterfaceMap.get(agencyId); + if (cachequeryInterface == null) { + cachequeryInterface = + ClientFactory.getInstance(agencyId, CacheQueryInterface.class); + cachequeryInterfaceMap.put(agencyId, cachequeryInterface); + } + + return cachequeryInterface; + } + +} diff --git a/transitime/src/main/java/org/transitime/ipc/clients/CommandsInterfaceFactory.java b/transitclock/src/main/java/org/transitclock/ipc/clients/CommandsInterfaceFactory.java similarity index 88% rename from transitime/src/main/java/org/transitime/ipc/clients/CommandsInterfaceFactory.java rename to transitclock/src/main/java/org/transitclock/ipc/clients/CommandsInterfaceFactory.java index b2905cef6..738b3a9de 100644 --- a/transitime/src/main/java/org/transitime/ipc/clients/CommandsInterfaceFactory.java +++ b/transitclock/src/main/java/org/transitclock/ipc/clients/CommandsInterfaceFactory.java @@ -14,13 +14,13 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.ipc.clients; +package org.transitclock.ipc.clients; import java.util.HashMap; import java.util.Map; -import org.transitime.ipc.interfaces.CommandsInterface; -import org.transitime.ipc.rmi.ClientFactory; +import org.transitclock.ipc.interfaces.CommandsInterface; +import org.transitclock.ipc.rmi.ClientFactory; /** * Provides a CommandsInterface client that can be sent commands. diff --git a/transitime/src/main/java/org/transitime/ipc/clients/ConfigInterfaceFactory.java b/transitclock/src/main/java/org/transitclock/ipc/clients/ConfigInterfaceFactory.java similarity index 91% rename from transitime/src/main/java/org/transitime/ipc/clients/ConfigInterfaceFactory.java rename to transitclock/src/main/java/org/transitclock/ipc/clients/ConfigInterfaceFactory.java index 64a671492..5235ce3e2 100644 --- a/transitime/src/main/java/org/transitime/ipc/clients/ConfigInterfaceFactory.java +++ b/transitclock/src/main/java/org/transitclock/ipc/clients/ConfigInterfaceFactory.java @@ -15,13 +15,13 @@ * along with Transitime.org . If not, see . */ -package org.transitime.ipc.clients; +package org.transitclock.ipc.clients; import java.util.HashMap; import java.util.Map; -import org.transitime.ipc.interfaces.ConfigInterface; -import org.transitime.ipc.rmi.ClientFactory; +import org.transitclock.ipc.interfaces.ConfigInterface; +import org.transitclock.ipc.rmi.ClientFactory; /** * Provides a ConfigInterface client that can be queried for diff --git a/transitclock/src/main/java/org/transitclock/ipc/clients/HoldingTimeInterfaceFactory.java b/transitclock/src/main/java/org/transitclock/ipc/clients/HoldingTimeInterfaceFactory.java new file mode 100755 index 000000000..bd8dc8b52 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/clients/HoldingTimeInterfaceFactory.java @@ -0,0 +1,61 @@ +/* + * 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.ipc.clients; + +import java.util.HashMap; +import java.util.Map; + +import org.transitclock.ipc.interfaces.CacheQueryInterface; +import org.transitclock.ipc.interfaces.CommandsInterface; +import org.transitclock.ipc.interfaces.HoldingTimeInterface; +import org.transitclock.ipc.rmi.ClientFactory; + +/** + * Provides a HoldingTimeInterface client that can be sent holding time queries. + * + * @author Sean Og Crudden + * + */ +public class HoldingTimeInterfaceFactory { + + // Keyed by agencyId + private static Map holdingtimeInterfaceMap = + new HashMap(); + + /********************** Member Functions **************************/ + + /** + * Gets the singleton instance. + * + * @param agencyId + * @return + */ + public static HoldingTimeInterface get(String agencyId) { + HoldingTimeInterface holdingTimeInterface = + holdingtimeInterfaceMap.get(agencyId); + if (holdingTimeInterface == null) { + + holdingTimeInterface = ClientFactory.getInstance(agencyId, HoldingTimeInterface.class); + + holdingtimeInterfaceMap.put(agencyId, holdingTimeInterface); + + } + + return holdingTimeInterface; + } + +} diff --git a/transitclock/src/main/java/org/transitclock/ipc/clients/PredictionAnalysisInterfaceFactory.java b/transitclock/src/main/java/org/transitclock/ipc/clients/PredictionAnalysisInterfaceFactory.java new file mode 100755 index 000000000..3a44938ad --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/clients/PredictionAnalysisInterfaceFactory.java @@ -0,0 +1,59 @@ +/* + * 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.ipc.clients; + +import java.util.HashMap; +import java.util.Map; + +import org.transitclock.ipc.interfaces.PredictionAnalysisInterface; +import org.transitclock.ipc.interfaces.PredictionsInterface; +import org.transitclock.ipc.rmi.ClientFactory; + +/** + * Provides a PredictionsInterface client that can be queried for + * predictions. + * + * @author Sean Og Crudden + * + */ +public class PredictionAnalysisInterfaceFactory { + + // Keyed by agencyId + private static Map predictionAnalysisInterfaceMap = + new HashMap(); + + /********************** Member Functions **************************/ + + /** + * Gets the PredictionAnalysisInterface for the specified projectId. There is one + * interface per agencyId. + * + * @param agencyId + * @return + */ + public static PredictionAnalysisInterface get(String agencyId) { + PredictionAnalysisInterface predictionAnalysisInterface = + predictionAnalysisInterfaceMap.get(agencyId); + if (predictionAnalysisInterface == null) { + predictionAnalysisInterface = + ClientFactory.getInstance(agencyId, PredictionAnalysisInterface.class); + predictionAnalysisInterfaceMap.put(agencyId, predictionAnalysisInterface); + } + return predictionAnalysisInterface; + } + +} diff --git a/transitime/src/main/java/org/transitime/ipc/clients/PredictionsInterfaceFactory.java b/transitclock/src/main/java/org/transitclock/ipc/clients/PredictionsInterfaceFactory.java similarity index 91% rename from transitime/src/main/java/org/transitime/ipc/clients/PredictionsInterfaceFactory.java rename to transitclock/src/main/java/org/transitclock/ipc/clients/PredictionsInterfaceFactory.java index 771b3e66d..5c9f09bec 100644 --- a/transitime/src/main/java/org/transitime/ipc/clients/PredictionsInterfaceFactory.java +++ b/transitclock/src/main/java/org/transitclock/ipc/clients/PredictionsInterfaceFactory.java @@ -14,13 +14,13 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.ipc.clients; +package org.transitclock.ipc.clients; import java.util.HashMap; import java.util.Map; -import org.transitime.ipc.interfaces.PredictionsInterface; -import org.transitime.ipc.rmi.ClientFactory; +import org.transitclock.ipc.interfaces.PredictionsInterface; +import org.transitclock.ipc.rmi.ClientFactory; /** * Provides a PredictionsInterface client that can be queried for diff --git a/transitclock/src/main/java/org/transitclock/ipc/clients/RevisionInterfaceFactory.java b/transitclock/src/main/java/org/transitclock/ipc/clients/RevisionInterfaceFactory.java new file mode 100644 index 000000000..f1f97eb8d --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/clients/RevisionInterfaceFactory.java @@ -0,0 +1,26 @@ +package org.transitclock.ipc.clients; + +import org.transitclock.ipc.interfaces.RevisionInformationInterface; +import org.transitclock.ipc.rmi.ClientFactory; + +import java.util.HashMap; +import java.util.Map; + +/** + * Factory class for instantiating RevisionInformationInterface. + */ +public class RevisionInterfaceFactory { + + private static Map revisionInformationInterfaceMap = + new HashMap(); + + public static RevisionInformationInterface get(String agencyId) { + RevisionInformationInterface revisionInformationInterface = + revisionInformationInterfaceMap.get(agencyId); + if (revisionInformationInterface == null) { + revisionInformationInterface = ClientFactory.getInstance(agencyId, RevisionInformationInterface.class); + revisionInformationInterfaceMap.put(agencyId, revisionInformationInterface); + } + return revisionInformationInterface; + } +} diff --git a/transitclock/src/main/java/org/transitclock/ipc/clients/ScheduleAdherenceInterfaceFactory.java b/transitclock/src/main/java/org/transitclock/ipc/clients/ScheduleAdherenceInterfaceFactory.java new file mode 100755 index 000000000..81d2b0524 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/clients/ScheduleAdherenceInterfaceFactory.java @@ -0,0 +1,59 @@ +/* + * 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.ipc.clients; + +import org.transitclock.ipc.interfaces.ReportingInterface; +import org.transitclock.ipc.rmi.ClientFactory; + +import java.util.HashMap; +import java.util.Map; + +/** + * Provides a ScheduleAdherenceInterface client + * + * @author carabalb + * + */ +public class ScheduleAdherenceInterfaceFactory { + + // Keyed by agencyId + private static Map scheduleAdherenceInterfaceMap = + new HashMap(); + + /********************** Member Functions **************************/ + + /** + * Gets the singleton instance. + * + * @param agencyId + * @return + */ + public static ReportingInterface get(String agencyId) { + ReportingInterface reportingInterface = + scheduleAdherenceInterfaceMap.get(agencyId); + if (reportingInterface == null) { + + reportingInterface = ClientFactory.getInstance(agencyId, ReportingInterface.class); + + scheduleAdherenceInterfaceMap.put(agencyId, reportingInterface); + + } + + return reportingInterface; + } + +} diff --git a/transitime/src/main/java/org/transitime/ipc/clients/ServerStatusInterfaceFactory.java b/transitclock/src/main/java/org/transitclock/ipc/clients/ServerStatusInterfaceFactory.java similarity index 91% rename from transitime/src/main/java/org/transitime/ipc/clients/ServerStatusInterfaceFactory.java rename to transitclock/src/main/java/org/transitclock/ipc/clients/ServerStatusInterfaceFactory.java index 66659e0f6..ba46c3bac 100644 --- a/transitime/src/main/java/org/transitime/ipc/clients/ServerStatusInterfaceFactory.java +++ b/transitclock/src/main/java/org/transitclock/ipc/clients/ServerStatusInterfaceFactory.java @@ -15,13 +15,13 @@ * along with Transitime.org . If not, see . */ -package org.transitime.ipc.clients; +package org.transitclock.ipc.clients; import java.util.HashMap; import java.util.Map; -import org.transitime.ipc.interfaces.ServerStatusInterface; -import org.transitime.ipc.rmi.ClientFactory; +import org.transitclock.ipc.interfaces.ServerStatusInterface; +import org.transitclock.ipc.rmi.ClientFactory; /** * Provides a ServerStatusInterface client that can be queried for diff --git a/transitime/src/main/java/org/transitime/ipc/clients/VehiclesInterfaceFactory.java b/transitclock/src/main/java/org/transitclock/ipc/clients/VehiclesInterfaceFactory.java similarity index 91% rename from transitime/src/main/java/org/transitime/ipc/clients/VehiclesInterfaceFactory.java rename to transitclock/src/main/java/org/transitclock/ipc/clients/VehiclesInterfaceFactory.java index 22841f65d..94780013e 100644 --- a/transitime/src/main/java/org/transitime/ipc/clients/VehiclesInterfaceFactory.java +++ b/transitclock/src/main/java/org/transitclock/ipc/clients/VehiclesInterfaceFactory.java @@ -15,13 +15,13 @@ * along with Transitime.org . If not, see . */ -package org.transitime.ipc.clients; +package org.transitclock.ipc.clients; import java.util.HashMap; import java.util.Map; -import org.transitime.ipc.interfaces.VehiclesInterface; -import org.transitime.ipc.rmi.ClientFactory; +import org.transitclock.ipc.interfaces.VehiclesInterface; +import org.transitclock.ipc.rmi.ClientFactory; /** * Provides a VehiclesInterface client that can be queried for diff --git a/transitime/src/main/java/org/transitime/ipc/clients/package-info.java b/transitclock/src/main/java/org/transitclock/ipc/clients/package-info.java similarity index 95% rename from transitime/src/main/java/org/transitime/ipc/clients/package-info.java rename to transitclock/src/main/java/org/transitclock/ipc/clients/package-info.java index eb15fd9e0..6b57f593b 100644 --- a/transitime/src/main/java/org/transitime/ipc/clients/package-info.java +++ b/transitclock/src/main/java/org/transitclock/ipc/clients/package-info.java @@ -22,4 +22,4 @@ * @author SkiBu Smith * */ -package org.transitime.ipc.clients; \ No newline at end of file +package org.transitclock.ipc.clients; \ No newline at end of file diff --git a/transitime/src/main/java/org/transitime/ipc/data/IpcActiveBlock.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcActiveBlock.java similarity index 88% rename from transitime/src/main/java/org/transitime/ipc/data/IpcActiveBlock.java rename to transitclock/src/main/java/org/transitclock/ipc/data/IpcActiveBlock.java index bd59911db..15f600eb6 100644 --- a/transitime/src/main/java/org/transitime/ipc/data/IpcActiveBlock.java +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcActiveBlock.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.ipc.data; +package org.transitclock.ipc.data; import java.io.Serializable; import java.util.Collection; @@ -23,7 +23,7 @@ import java.util.Comparator; import java.util.List; -import org.transitime.db.structs.Trip; +import org.transitclock.db.structs.Trip; /** * For IPC for obtaining currently active blocks. Contains both block @@ -104,6 +104,13 @@ private Trip getTripForSorting() { */ @Override public int compare(IpcActiveBlock b1, IpcActiveBlock b2) { + if (b1 == null && b2 == null) + return 0; + if (b1 == null || b1.getTripForSorting() == null || b1.getTripForSorting().getRoute() == null || b1.getTripForSorting().getRoute().getRouteOrder() == null) + return -1; + if (b2 == null || b2.getTripForSorting() == null || b2.getTripForSorting().getRoute() == null || b2.getTripForSorting().getRoute().getRouteOrder() == null) + return 1; + int routeOrder1 = b1.getTripForSorting().getRoute().getRouteOrder(); int routeOrder2 = diff --git a/transitclock/src/main/java/org/transitclock/ipc/data/IpcArrivalDeparture.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcArrivalDeparture.java new file mode 100755 index 000000000..78c414fcd --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcArrivalDeparture.java @@ -0,0 +1,359 @@ +package org.transitclock.ipc.data; + +import org.transitclock.core.TemporalDifference; +import org.transitclock.db.structs.ArrivalDeparture; + +import javax.xml.bind.annotation.XmlAttribute; +import java.io.Serializable; +import java.util.Date; +/** + * For IPC for obtaining arrival and departure events for a stop that are in the cache. + * + * @author Sean Og Crudden + * + */ +public class IpcArrivalDeparture implements Serializable { + + + /** + * + */ + private static final long serialVersionUID = 8916143683528781201L; + + @XmlAttribute + private String vehicleId; + @XmlAttribute + private Date time; + @XmlAttribute + private String stopId; + @XmlAttribute + private int gtfsStopSeq; + @XmlAttribute + private boolean isArrival; + @XmlAttribute + private String tripId; + @XmlAttribute + private transient Date avlTime; + @XmlAttribute + private transient TemporalDifference scheduledAdherence; + @XmlAttribute + private String blockId; + @XmlAttribute + private transient String routeId; + @XmlAttribute + private transient String routeShortName; + @XmlAttribute + private transient String serviceId; + @XmlAttribute + private String directionId; + @XmlAttribute + private transient int tripIndex; + @XmlAttribute + private int stopPathIndex; + @XmlAttribute + private transient float stopPathLength; + @XmlAttribute + private Date freqStartTime; + @XmlAttribute + private Long dwellTime; + @XmlAttribute + private String tripPatternId; + @XmlAttribute + private Date scheduledDate; + + protected IpcArrivalDeparture(){} + + public IpcArrivalDeparture(ArrivalDeparture arrivalDepature) throws Exception { + + this.vehicleId=arrivalDepature.getVehicleId(); + this.time=new Date(arrivalDepature.getTime()); + this.avlTime=arrivalDepature.getAvlTime(); + this.routeId=arrivalDepature.getRouteId(); + this.tripId=arrivalDepature.getTripId(); + this.isArrival=arrivalDepature.isArrival(); + this.stopId=arrivalDepature.getStopId(); + this.stopPathIndex=arrivalDepature.getStopPathIndex(); + + this.scheduledAdherence=arrivalDepature.getScheduleAdherence(); + this.freqStartTime=arrivalDepature.getFreqStartTime(); + this.directionId=arrivalDepature.getDirectionId(); + this.blockId=arrivalDepature.getBlockId(); + this.serviceId=arrivalDepature.getServiceId(); + this.dwellTime=arrivalDepature.getDwellTime(); + this.tripPatternId=arrivalDepature.getTripPatternId(); + this.scheduledDate=arrivalDepature.getScheduledDate(); + } + + + + + public String getVehicleId() { + return vehicleId; + } + public void setVehicleId(String vehicleId) { + this.vehicleId = vehicleId; + } + public Date getTime() { + return time; + } + public void setTime(Date time) { + this.time = time; + } + public String getStopId() { + return stopId; + } + public void setStopId(String stopId) { + this.stopId = stopId; + } + public int getGtfsStopSeq() { + return gtfsStopSeq; + } + public void setGtfsStopSeq(int gtfsStopSeq) { + this.gtfsStopSeq = gtfsStopSeq; + } + public boolean isArrival() { + return isArrival; + } + public void setArrival(boolean isArrival) { + this.isArrival = isArrival; + } + public String getTripId() { + return tripId; + } + public void setTripId(String tripId) { + this.tripId = tripId; + } + public Date getAvlTime() { + return avlTime; + } + public void setAvlTime(Date avlTime) { + this.avlTime = avlTime; + } + + public String getBlockId() { + return blockId; + } + public void setBlockId(String blockId) { + this.blockId = blockId; + } + public String getRouteId() { + return routeId; + } + public void setRouteId(String routeId) { + this.routeId = routeId; + } + public String getRouteShortName() { + return routeShortName; + } + public void setRouteShortName(String routeShortName) { + this.routeShortName = routeShortName; + } + public String getServiceId() { + return serviceId; + } + public void setServiceId(String serviceId) { + this.serviceId = serviceId; + } + public String getDirectionId() { + return directionId; + } + public void setDirectionId(String directionId) { + this.directionId = directionId; + } + public int getTripIndex() { + return tripIndex; + } + public void setTripIndex(int tripIndex) { + this.tripIndex = tripIndex; + } + public int getStopPathIndex() { + return stopPathIndex; + } + public void setStopPathIndex(int stopPathIndex) { + this.stopPathIndex = stopPathIndex; + } + public float getStopPathLength() { + return stopPathLength; + } + public void setStopPathLength(float stopPathLength) { + this.stopPathLength = stopPathLength; + } + + public boolean isDeparture() { + return !isArrival; + } + + public Date getFreqStartTime() { + return freqStartTime; + } + + public void setFreqStartTime(Date freqStartTime) { + this.freqStartTime = freqStartTime; + } + + + + + public TemporalDifference getScheduledAdherence() { + return scheduledAdherence; + } + + + + + public void setScheduledAdherence(TemporalDifference scheduledAdherence) { + this.scheduledAdherence = scheduledAdherence; + } + + public Long getDwellTime() { + return dwellTime; + } + + public void setDwellTime(Long dwellTime) { + this.dwellTime = dwellTime; + } + + public String getTripPatternId() { + return tripPatternId; + } + + public Date getScheduledDate() { + return scheduledDate; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((avlTime == null) ? 0 : avlTime.hashCode()); + result = prime * result + ((blockId == null) ? 0 : blockId.hashCode()); + result = prime * result + ((directionId == null) ? 0 : directionId.hashCode()); + result = prime * result + ((freqStartTime == null) ? 0 : freqStartTime.hashCode()); + result = prime * result + gtfsStopSeq; + result = prime * result + (isArrival ? 1231 : 1237); + result = prime * result + ((routeId == null) ? 0 : routeId.hashCode()); + result = prime * result + ((routeShortName == null) ? 0 : routeShortName.hashCode()); + result = prime * result + ((serviceId == null) ? 0 : serviceId.hashCode()); + result = prime * result + ((stopId == null) ? 0 : stopId.hashCode()); + result = prime * result + stopPathIndex; + result = prime * result + Float.floatToIntBits(stopPathLength); + result = prime * result + ((time == null) ? 0 : time.hashCode()); + result = prime * result + ((tripId == null) ? 0 : tripId.hashCode()); + result = prime * result + tripIndex; + result = prime * result + ((vehicleId == null) ? 0 : vehicleId.hashCode()); + result = prime * result + ((dwellTime == null) ? 0 : dwellTime.hashCode()); + result = prime * result + ((scheduledDate == null) ? 0 : scheduledDate.hashCode()); + result = prime * result + ((tripPatternId == null) ? 0 : tripPatternId.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; + IpcArrivalDeparture other = (IpcArrivalDeparture) obj; + if (avlTime == null) { + if (other.avlTime != null) + return false; + } else if (!avlTime.equals(other.avlTime)) + return false; + if (blockId == null) { + if (other.blockId != null) + return false; + } else if (!blockId.equals(other.blockId)) + return false; + if (directionId == null) { + if (other.directionId != null) + return false; + } else if (!directionId.equals(other.directionId)) + return false; + if (freqStartTime == null) { + if (other.freqStartTime != null) + return false; + } else if (!freqStartTime.equals(other.freqStartTime)) + return false; + if (gtfsStopSeq != other.gtfsStopSeq) + return false; + if (isArrival != other.isArrival) + return false; + if (routeId == null) { + if (other.routeId != null) + return false; + } else if (!routeId.equals(other.routeId)) + return false; + if (routeShortName == null) { + if (other.routeShortName != null) + return false; + } else if (!routeShortName.equals(other.routeShortName)) + return false; + if (serviceId == null) { + if (other.serviceId != null) + return false; + } else if (!serviceId.equals(other.serviceId)) + return false; + if (stopId == null) { + if (other.stopId != null) + return false; + } else if (!stopId.equals(other.stopId)) + return false; + if (stopPathIndex != other.stopPathIndex) + return false; + if (Float.floatToIntBits(stopPathLength) != Float.floatToIntBits(other.stopPathLength)) + return false; + if (time == null) { + if (other.time != null) + return false; + } else if (!time.equals(other.time)) + return false; + if (tripId == null) { + if (other.tripId != null) + return false; + } else if (!tripId.equals(other.tripId)) + return false; + if (tripIndex != other.tripIndex) + return false; + if (vehicleId == null) { + if (other.vehicleId != null) + return false; + } else if (!vehicleId.equals(other.vehicleId)) + return false; + if (dwellTime == null) { + if (other.dwellTime != null) + return false; + } else if (!dwellTime.equals(other.dwellTime)) + return false; + if (scheduledDate == null) { + if (other.scheduledDate != null) + return false; + } else if (!scheduledDate.equals(other.scheduledDate)) + return false; + if (tripPatternId == null) { + if (other.tripPatternId != null) + return false; + } else if (!tripPatternId.equals(other.tripPatternId)) + return false; + return true; + } + + @Override + public String toString() { + return "IpcArrivalDeparture [vehicleId=" + vehicleId + ", time=" + time + ", stopId=" + stopId + + ", gtfsStopSeq=" + gtfsStopSeq + ", isArrival=" + isArrival + ", tripId=" + tripId + ", avlTime=" + + avlTime + ", scheduledAdherence=" + scheduledAdherence + ", blockId=" + blockId + ", routeId=" + + routeId + ", routeShortName=" + routeShortName + ", serviceId=" + serviceId + ", directionId=" + + directionId + ", tripIndex=" + tripIndex + ", stopPathIndex=" + stopPathIndex + ", stopPathLength=" + + stopPathLength + ", freqStartTime=" + freqStartTime + (dwellTime != null ? ", dwellTime=" + dwellTime : "") + + ", tripPatternId=" + tripPatternId + (scheduledDate != null ? ", scheduledDate=" + scheduledDate : "") + "]"; + } + + + + +} diff --git a/transitclock/src/main/java/org/transitclock/ipc/data/IpcArrivalDepartureScheduleAdherence.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcArrivalDepartureScheduleAdherence.java new file mode 100644 index 000000000..6f5b566fe --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcArrivalDepartureScheduleAdherence.java @@ -0,0 +1,57 @@ +package org.transitclock.ipc.data; + +import org.transitclock.core.TemporalDifference; +import org.transitclock.db.structs.ArrivalDeparture; + +import javax.xml.bind.annotation.XmlAttribute; +import java.util.Objects; + +public class IpcArrivalDepartureScheduleAdherence extends IpcArrivalDeparture { + + @XmlAttribute + private TemporalDifference scheduledAdherence; + + @XmlAttribute + private boolean isTimePoint; + + private IpcArrivalDepartureScheduleAdherence(){ + super(); + } + + public IpcArrivalDepartureScheduleAdherence(ArrivalDeparture arrivalDepature) throws Exception { + super(arrivalDepature); + this.scheduledAdherence = arrivalDepature.getScheduleAdherence(); + } + + @Override + public TemporalDifference getScheduledAdherence() { + return scheduledAdherence; + } + + @Override + public void setScheduledAdherence(TemporalDifference scheduledAdherence) { + this.scheduledAdherence = scheduledAdherence; + } + + public boolean isTimePoint() { + return isTimePoint; + } + + public void setTimePoint(boolean timePoint) { + isTimePoint = timePoint; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + IpcArrivalDepartureScheduleAdherence that = (IpcArrivalDepartureScheduleAdherence) o; + return isTimePoint == that.isTimePoint; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), isTimePoint); + } +} diff --git a/transitime/src/main/java/org/transitime/ipc/data/IpcAvl.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcAvl.java similarity index 97% rename from transitime/src/main/java/org/transitime/ipc/data/IpcAvl.java rename to transitclock/src/main/java/org/transitclock/ipc/data/IpcAvl.java index 8b4ecb516..6298cbab5 100644 --- a/transitime/src/main/java/org/transitime/ipc/data/IpcAvl.java +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcAvl.java @@ -15,17 +15,17 @@ * along with Transitime.org . If not, see . */ -package org.transitime.ipc.data; +package org.transitclock.ipc.data; import java.io.IOException; import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.Serializable; -import org.transitime.db.structs.AvlReport; -import org.transitime.db.structs.AvlReport.AssignmentType; -import org.transitime.utils.Geo; -import org.transitime.utils.Time; +import org.transitclock.db.structs.AvlReport; +import org.transitclock.db.structs.AvlReport.AssignmentType; +import org.transitclock.utils.Geo; +import org.transitclock.utils.Time; /** * A serializable object used by RMI to transfer AVL data to client. diff --git a/transitime/src/main/java/org/transitime/ipc/data/IpcBlock.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcBlock.java similarity index 91% rename from transitime/src/main/java/org/transitime/ipc/data/IpcBlock.java rename to transitclock/src/main/java/org/transitclock/ipc/data/IpcBlock.java index b61a23e4d..77987266f 100644 --- a/transitime/src/main/java/org/transitime/ipc/data/IpcBlock.java +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcBlock.java @@ -15,17 +15,17 @@ * along with Transitime.org . If not, see . */ -package org.transitime.ipc.data; +package org.transitclock.ipc.data; import java.io.Serializable; import java.util.ArrayList; import java.util.List; -import org.transitime.applications.Core; -import org.transitime.db.structs.Block; -import org.transitime.db.structs.Route; -import org.transitime.db.structs.Trip; -import org.transitime.utils.Time; +import org.transitclock.applications.Core; +import org.transitclock.db.structs.Block; +import org.transitclock.db.structs.Route; +import org.transitclock.db.structs.Trip; +import org.transitclock.utils.Time; /** * Configuration information for a Block for IPC. diff --git a/transitime/src/main/java/org/transitime/ipc/data/IpcCalendar.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcCalendar.java similarity index 93% rename from transitime/src/main/java/org/transitime/ipc/data/IpcCalendar.java rename to transitclock/src/main/java/org/transitclock/ipc/data/IpcCalendar.java index 7b10c09c1..c34d24d64 100644 --- a/transitime/src/main/java/org/transitime/ipc/data/IpcCalendar.java +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcCalendar.java @@ -14,10 +14,11 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.ipc.data; +package org.transitclock.ipc.data; import java.io.Serializable; -import org.transitime.db.structs.Calendar; + +import org.transitclock.db.structs.Calendar; /** * A calendar object for IPC via RMI diff --git a/transitclock/src/main/java/org/transitclock/ipc/data/IpcCanceledTrip.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcCanceledTrip.java new file mode 100644 index 000000000..caf7f6048 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcCanceledTrip.java @@ -0,0 +1,101 @@ +package org.transitclock.ipc.data; + +import java.io.Serializable; +import java.util.Objects; + +public class IpcCanceledTrip implements Serializable { + + private static final long serialVersionUID = 1L; + + private String tripId; + private String routeId; + private String tripStartDate; + private long timeStamp; + private float latitude; + private float longitude; + + public IpcCanceledTrip(String tripId, String routeId, String tripStartTime, long timeStamp){ + this.tripId = tripId; + this.routeId = routeId; + this.tripStartDate = tripStartTime; + this.timeStamp = timeStamp; + } + + public String getTripId() { + return tripId; + } + + public void setTripId(String tripId) { + this.tripId = tripId; + } + + public String getRouteId() { + return routeId; + } + + public void setRouteId(String routeId) { + this.routeId = routeId; + } + + public String getTripStartDate() { + return tripStartDate; + } + + public void setTripStartDate(String tripStartDate) { + this.tripStartDate = tripStartDate; + } + + public long getTimeStamp() { + return timeStamp; + } + + public void setTimeStamp(long timeStamp) { + this.timeStamp = timeStamp; + } + + public float getLatitude() { + return latitude; + } + + public void setLatitude(float latitude) { + this.latitude = latitude; + } + + public float getLongitude() { + return longitude; + } + + public void setLongitude(float longitude) { + this.longitude = longitude; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + IpcCanceledTrip that = (IpcCanceledTrip) o; + return timeStamp == that.timeStamp && + Float.compare(that.latitude, latitude) == 0 && + Float.compare(that.longitude, longitude) == 0 && + Objects.equals(tripId, that.tripId) && + Objects.equals(routeId, that.routeId) && + Objects.equals(tripStartDate, that.tripStartDate); + } + + @Override + public int hashCode() { + return Objects.hash(tripId, routeId, tripStartDate, timeStamp, latitude, longitude); + } + + @Override + public String toString() { + return "IpcCanceledTrip{" + + "tripId='" + tripId + '\'' + + ", routeId='" + routeId + '\'' + + ", tripStartDate='" + tripStartDate + '\'' + + ", timeStamp=" + timeStamp + + ", latitude=" + latitude + + ", longitude=" + longitude + + '}'; + } +} diff --git a/transitime/src/main/java/org/transitime/ipc/data/IpcDirection.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcDirection.java similarity index 93% rename from transitime/src/main/java/org/transitime/ipc/data/IpcDirection.java rename to transitclock/src/main/java/org/transitclock/ipc/data/IpcDirection.java index 8ea1b7cdb..817c300d9 100644 --- a/transitime/src/main/java/org/transitime/ipc/data/IpcDirection.java +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcDirection.java @@ -15,17 +15,17 @@ * along with Transitime.org . If not, see . */ -package org.transitime.ipc.data; +package org.transitclock.ipc.data; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.List; -import org.transitime.applications.Core; -import org.transitime.db.structs.Route; -import org.transitime.db.structs.Stop; -import org.transitime.db.structs.TripPattern; +import org.transitclock.applications.Core; +import org.transitclock.db.structs.Route; +import org.transitclock.db.structs.Stop; +import org.transitclock.db.structs.TripPattern; /** * diff --git a/transitime/src/main/java/org/transitime/ipc/data/IpcDirectionsForRoute.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcDirectionsForRoute.java similarity index 84% rename from transitime/src/main/java/org/transitime/ipc/data/IpcDirectionsForRoute.java rename to transitclock/src/main/java/org/transitclock/ipc/data/IpcDirectionsForRoute.java index f6564b0b7..f750a3daa 100644 --- a/transitime/src/main/java/org/transitime/ipc/data/IpcDirectionsForRoute.java +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcDirectionsForRoute.java @@ -15,13 +15,13 @@ * along with Transitime.org . If not, see . */ -package org.transitime.ipc.data; +package org.transitclock.ipc.data; import java.io.Serializable; import java.util.ArrayList; import java.util.List; -import org.transitime.db.structs.Route; +import org.transitclock.db.structs.Route; /** * Contains each direction for route, along with each stop for each direction. @@ -31,6 +31,10 @@ */ public class IpcDirectionsForRoute implements Serializable { + private String routeId; + + private String routeShortName; + private List directions; private static final long serialVersionUID = -3112277760645758349L; @@ -38,6 +42,9 @@ public class IpcDirectionsForRoute implements Serializable { /********************** Member Functions **************************/ public IpcDirectionsForRoute(Route dbRoute) { + this.routeId = dbRoute.getId(); + this.routeShortName = dbRoute.getShortName(); + directions = new ArrayList(); // Determine the directions @@ -65,4 +72,11 @@ public List getDirections() { return directions; } + public String getRouteId() { + return routeId; + } + + public String getRouteShortName() { + return routeShortName; + } } diff --git a/transitclock/src/main/java/org/transitclock/ipc/data/IpcDoubleSummaryStatistics.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcDoubleSummaryStatistics.java new file mode 100644 index 000000000..37c3239d3 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcDoubleSummaryStatistics.java @@ -0,0 +1,42 @@ +package org.transitclock.ipc.data; + +import java.io.Serializable; +import java.util.DoubleSummaryStatistics; + +public class IpcDoubleSummaryStatistics implements Serializable { + private static final long serialVersionUID = 6366506370164076626L; + + private final long count; + private final double sum; + private final double min; + private final double max; + private final double average; + + public IpcDoubleSummaryStatistics(DoubleSummaryStatistics doubleSummaryStatistics){ + this.count = doubleSummaryStatistics.getCount(); + this.sum = doubleSummaryStatistics.getSum(); + this.min = doubleSummaryStatistics.getMin(); + this.max = doubleSummaryStatistics.getMax(); + this.average = doubleSummaryStatistics.getAverage(); + } + + public long getCount() { + return count; + } + + public double getSum() { + return sum; + } + + public double getMin() { + return min; + } + + public double getMax() { + return max; + } + + public double getAverage() { + return average; + } +} diff --git a/transitclock/src/main/java/org/transitclock/ipc/data/IpcHistoricalAverage.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcHistoricalAverage.java new file mode 100755 index 000000000..1568ace04 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcHistoricalAverage.java @@ -0,0 +1,53 @@ +package org.transitclock.ipc.data; + +import java.io.Serializable; + +import org.transitclock.core.dataCache.HistoricalAverage; + +/** + * @author Sean Og Crudden + * Represents an historical average. + * + */ +public class IpcHistoricalAverage implements Serializable{ + + + public IpcHistoricalAverage(HistoricalAverage historicalAverage) { + super(); + + if(historicalAverage!=null) + { + this.count = historicalAverage.getCount(); + this.average = historicalAverage.getAverage(); + } + } + + + public Integer getCount() { + return count; + } + + + public void setCount(Integer count) { + this.count = count; + } + + + public Double getAverage() { + return average; + } + + + public void setAverage(Double average) { + this.average = average; + } + + + private static final long serialVersionUID = -1285357644186049157L; + + + private Integer count=0; + + + private Double average=0.0; +} diff --git a/transitclock/src/main/java/org/transitclock/ipc/data/IpcHistoricalAverageCacheKey.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcHistoricalAverageCacheKey.java new file mode 100755 index 000000000..b79a13e6e --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcHistoricalAverageCacheKey.java @@ -0,0 +1,32 @@ +package org.transitclock.ipc.data; + +import java.io.Serializable; + +import org.transitclock.core.dataCache.StopPathCacheKey; + +public class IpcHistoricalAverageCacheKey implements Serializable +{ + private static final long serialVersionUID = 8712981814295653998L; + + + private String tripId; + private Integer stopPathIndex; + + public IpcHistoricalAverageCacheKey(StopPathCacheKey key) { + super(); + this.tripId=key.getTripId(); + this.stopPathIndex=key.getStopPathIndex(); + } + public String getTripId() { + return tripId; + } + public void setTripId(String tripId) { + this.tripId = tripId; + } + public Integer getStopPathIndex() { + return stopPathIndex; + } + public void setStopPathIndex(Integer stopPathIndex) { + this.stopPathIndex = stopPathIndex; + } +} diff --git a/transitclock/src/main/java/org/transitclock/ipc/data/IpcHoldingTime.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcHoldingTime.java new file mode 100755 index 000000000..7fe08d9fc --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcHoldingTime.java @@ -0,0 +1,138 @@ +package org.transitclock.ipc.data; + +import java.io.Serializable; +import java.util.Calendar; +import java.util.Date; + +import org.transitclock.db.structs.HoldingTime; + +public class IpcHoldingTime implements Serializable{ + + /** + * + */ + private static final long serialVersionUID = 2869001113239169554L; + + private final Date holdingTime; + + private final Date creationTime; + + private Date currentTime; + + public void setCurrentTime(Date currentTime) { + this.currentTime = currentTime; + } + + private final String vehicleId; + + private final String stopId; + + private final String tripId; + + private final String routeId; + + private boolean arrivalPredictionUsed; + + private boolean arrivalUsed; + + private final Date arrivalTime; + + private boolean hasD1; + + private int numberPredictionsUsed; + + + public boolean isHasD1() { + return hasD1; + } + public void setHasD1(boolean hasD1) { + this.hasD1 = hasD1; + } + + public IpcHoldingTime(Date holdingTime, Date creationTime, String vehicleId, String stopId, String tripId, + String routeId, boolean arrivalPredictionUsed , boolean arrivalUsed, Date arrivalTime, boolean hasD1, int numberPredictionsUsed) { + super(); + this.holdingTime = holdingTime; + this.creationTime = creationTime; + this.vehicleId = vehicleId; + this.stopId = stopId; + this.tripId = tripId; + this.routeId = routeId; + this.currentTime = Calendar.getInstance().getTime(); + this.arrivalTime=arrivalTime; + this.hasD1=hasD1; + this.numberPredictionsUsed=numberPredictionsUsed; + + } + public IpcHoldingTime(HoldingTime holdingTime) { + + this.holdingTime = holdingTime.getHoldingTime(); + this.creationTime = holdingTime.getCreationTime(); + this.vehicleId = holdingTime.getVehicleId(); + this.stopId = holdingTime.getStopId(); + this.tripId = holdingTime.getTripId(); + this.routeId = holdingTime.getRouteId(); + this.arrivalPredictionUsed = holdingTime.isArrivalPredictionUsed(); + this.arrivalUsed = holdingTime.isArrivalUsed(); + this.currentTime = Calendar.getInstance().getTime(); + this.arrivalTime=holdingTime.getArrivalTime(); + this.hasD1=holdingTime.isHasD1(); + this.numberPredictionsUsed=holdingTime.getNumberPredictionsUsed(); + } + + public int getNumberPredictionsUsed() { + return numberPredictionsUsed; + } + public void setNumberPredictionsUsed(int numberPredictionsUsed) { + this.numberPredictionsUsed = numberPredictionsUsed; + } + public Date getArrivalTime() { + return arrivalTime; + } + public void setArrivalPredictionUsed(boolean arrivalPredictionUsed) { + this.arrivalPredictionUsed = arrivalPredictionUsed; + } + public void setArrivalUsed(boolean arrivalUsed) { + this.arrivalUsed = arrivalUsed; + } + public Date getCurrentTime() { + return currentTime; + } + public Date getHoldingTime() { + return holdingTime; + } + + + public Date getCreationTime() { + return creationTime; + } + + + public String getVehicleId() { + return vehicleId; + } + + + public String getStopId() { + return stopId; + } + + + public String getTripId() { + return tripId; + } + + + public String getRouteId() { + return routeId; + } + + public boolean isArrivalPredictionUsed() { + return arrivalPredictionUsed; + } + + public boolean isArrivalUsed() { + return arrivalUsed; + } + +} diff --git a/transitclock/src/main/java/org/transitclock/ipc/data/IpcHoldingTimeCacheKey.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcHoldingTimeCacheKey.java new file mode 100755 index 000000000..81dd9b5a7 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcHoldingTimeCacheKey.java @@ -0,0 +1,39 @@ +package org.transitclock.ipc.data; + +import java.io.Serializable; + +import org.transitclock.core.dataCache.HoldingTimeCacheKey; + +public class IpcHoldingTimeCacheKey implements Serializable { + + /** + * + */ + private static final long serialVersionUID = -2625236742413645110L; + private String stopid; + private String vehicleId; + private String tripId; + public IpcHoldingTimeCacheKey(HoldingTimeCacheKey holdingTimeCacheKey) { + this.stopid=holdingTimeCacheKey.getStopid(); + this.vehicleId=holdingTimeCacheKey.getVehicleId(); + this.setTripId(holdingTimeCacheKey.getTripId()); + } + 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; + } + public String getTripId() { + return tripId; + } + public void setTripId(String tripId) { + this.tripId = tripId; + } +} diff --git a/transitclock/src/main/java/org/transitclock/ipc/data/IpcKalmanErrorCacheKey.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcKalmanErrorCacheKey.java new file mode 100755 index 000000000..5b569059e --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcKalmanErrorCacheKey.java @@ -0,0 +1,32 @@ +package org.transitclock.ipc.data; + +import java.io.Serializable; + +import org.transitclock.core.dataCache.KalmanErrorCacheKey; +import org.transitclock.core.dataCache.StopPathCacheKey; + +public class IpcKalmanErrorCacheKey implements Serializable +{ + + private static final long serialVersionUID = -748336688312487489L; + private String tripId; + private Integer stopPathIndex; + + public IpcKalmanErrorCacheKey(KalmanErrorCacheKey key) { + super(); + this.tripId=key.getTripId(); + this.stopPathIndex=key.getStopPathIndex(); + } + public String getTripId() { + return tripId; + } + public void setTripId(String tripId) { + this.tripId = tripId; + } + public Integer getStopPathIndex() { + return stopPathIndex; + } + public void setStopPathIndex(Integer stopPathIndex) { + this.stopPathIndex = stopPathIndex; + } +} diff --git a/transitime/src/main/java/org/transitime/ipc/data/IpcPrediction.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcPrediction.java similarity index 79% rename from transitime/src/main/java/org/transitime/ipc/data/IpcPrediction.java rename to transitclock/src/main/java/org/transitclock/ipc/data/IpcPrediction.java index c51eb1f98..9b6920d2c 100644 --- a/transitime/src/main/java/org/transitime/ipc/data/IpcPrediction.java +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcPrediction.java @@ -14,7 +14,13 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.ipc.data; +package org.transitclock.ipc.data; + +import org.transitclock.applications.Core; +import org.transitclock.db.structs.AvlReport; +import org.transitclock.db.structs.Trip; +import org.transitclock.utils.StringUtils; +import org.transitclock.utils.Time; import java.io.IOException; import java.io.InvalidObjectException; @@ -22,12 +28,6 @@ import java.io.Serializable; import java.util.Date; -import org.transitime.applications.Core; -import org.transitime.db.structs.AvlReport; -import org.transitime.db.structs.Trip; -import org.transitime.utils.StringUtils; -import org.transitime.utils.Time; - /** * Contains information on a single prediction. For providing info to client. *

@@ -51,6 +51,7 @@ public class IpcPrediction implements Serializable { private final int gtfsStopSeq; private final String tripId; private final String tripPatternId; + private final boolean isTripUnscheduled; private final String blockId; // The prediction to present to the user. Can be different from // actualPredictionTime in that for wait stops might want to show @@ -77,7 +78,15 @@ public class IpcPrediction implements Serializable { private final boolean isDelayed; private final boolean lateAndSubsequentTripSoMarkAsUncertain; private final boolean isArrival; - + private final Integer delay; + private boolean isCanceled; + + public boolean isCanceled() { + return isCanceled; + } + + private final long freqStartTime; + private final int tripCounter; // Want to store trip on server side so that can determine route info // when creating PredictionsForRouteStop object. private final Trip trip; @@ -85,6 +94,8 @@ public class IpcPrediction implements Serializable { private static final long serialVersionUID = 7264507678733060173L; public enum ArrivalOrDeparture {ARRIVAL, DEPARTURE}; + + /********************** Member Functions **************************/ @@ -114,43 +125,53 @@ public enum ArrivalOrDeparture {ARRIVAL, DEPARTURE}; * @param isDelayed * @param lateAndSubsequentTripSoMarkAsUncertain * @param arrivalOrDeparture + * @param delay scheduleDeviation or null + * @param freqStartTime + * @param tripCounter + * */ public IpcPrediction(AvlReport avlReport, String stopId, int gtfsStopSeq, - Trip trip, long predictionTime, long actualPredictionTime, - boolean atEndOfTrip, boolean predictionAffectedByWaitStop, - boolean isDelayed, boolean lateAndSubsequentTripSoMarkAsUncertain, - ArrivalOrDeparture arrivalOrDeparture) { + Trip trip, long predictionTime, long actualPredictionTime, + boolean atEndOfTrip, boolean affectedByWaitStop, + boolean isDelayed, boolean lateAndSubsequentTripSoMarkAsUncertain, + ArrivalOrDeparture arrivalOrDeparture, Integer delay, long freqStartTime, + int tripCounter,boolean isCanceled) { this.vehicleId = avlReport.getVehicleId(); - this.routeId = trip.getRouteId(); - this.stopId = stopId; - this.gtfsStopSeq = gtfsStopSeq; - this.trip = trip; - // For when trip is null use "" instead of null for the tripId - // so that when getting all predictions code for telling when - // tripId changes will still work when debugging. - this.tripId = trip != null ? trip.getId() : ""; - this.tripPatternId = trip != null ? trip.getTripPattern().getId() : ""; - this.blockId = trip != null ? trip.getBlockId() : null; - this.predictionTime = predictionTime; - this.actualPredictionTime = actualPredictionTime; - this.atEndOfTrip = atEndOfTrip; - this.schedBasedPred = avlReport.isForSchedBasedPreds(); - this.avlTime = avlReport.getTime(); - this.creationTime = avlReport.getTimeProcessed(); - - Date currentTime = Core.getInstance().getSystemDate(); - this.tripStartEpochTime = - Core.getInstance().getTime() - .getEpochTime(trip.getStartTime(), currentTime); - - this.affectedByWaitStop = predictionAffectedByWaitStop; - this.driverId = avlReport.getDriverId(); - this.passengerCount = (short) avlReport.getPassengerCount(); - this.passengerFullness = avlReport.getPassengerFullness(); - this.isDelayed = isDelayed; - this.lateAndSubsequentTripSoMarkAsUncertain = - lateAndSubsequentTripSoMarkAsUncertain; - this.isArrival = arrivalOrDeparture == ArrivalOrDeparture.ARRIVAL; + this.routeId = trip.getRouteId(); + this.stopId = stopId; + this.gtfsStopSeq = gtfsStopSeq; + this.trip = trip; + // For when trip is null use "" instead of null for the tripId + // so that when getting all predictions code for telling when + // tripId changes will still work when debugging. + this.tripId = trip != null ? trip.getId() : ""; + this.tripPatternId = trip != null ? trip.getTripPattern().getId() : ""; + this.blockId = trip != null ? trip.getBlockId() : null; + this.isTripUnscheduled = trip != null && trip.isNoSchedule() && !trip.isExactTimesHeadway(); + this.predictionTime = predictionTime; + this.actualPredictionTime = actualPredictionTime; + this.atEndOfTrip = atEndOfTrip; + this.schedBasedPred = avlReport.isForSchedBasedPreds(); + this.avlTime = avlReport.getTime(); + this.creationTime = avlReport.getTimeProcessed(); + + Date currentTime = Core.getInstance().getSystemDate(); + this.tripStartEpochTime = + Core.getInstance().getTime() + .getEpochTime(trip.getStartTime(), currentTime); + + this.affectedByWaitStop = affectedByWaitStop; + this.driverId = avlReport.getDriverId(); + this.passengerCount = (short) avlReport.getPassengerCount(); + this.passengerFullness = avlReport.getPassengerFullness(); + this.isDelayed = isDelayed; + this.lateAndSubsequentTripSoMarkAsUncertain = + lateAndSubsequentTripSoMarkAsUncertain; + this.isArrival = arrivalOrDeparture == ArrivalOrDeparture.ARRIVAL; + this.delay = delay; + this.freqStartTime = freqStartTime; + this.tripCounter = tripCounter; + this.isCanceled=isCanceled; } /** @@ -158,13 +179,15 @@ public IpcPrediction(AvlReport avlReport, String stopId, int gtfsStopSeq, * because only used internally by the proxy class. */ private IpcPrediction(String vehicleId, String routeId, String stopId, - int gtfsStopSeq, String tripId, String tripPatternId, + int gtfsStopSeq, String tripId, String tripPatternId, boolean isTripUnscheduled, String blockId, long predictionTime, long actualPredictionTime, boolean atEndOfTrip, boolean schedBasedPred, long avlTime, long creationTime, long tripStartEpochTime, boolean affectedByWaitStop, String driverId, short passengerCount, float passengerFullness, boolean isDelayed, - boolean lateAndSubsequentTripSoMarkAsUncertain, boolean isArrival) { + boolean lateAndSubsequentTripSoMarkAsUncertain, boolean isArrival, Integer delay, + Long freqStartTime, int tripCounter,boolean isCanceled) { + this.vehicleId = vehicleId; this.routeId = routeId; this.stopId = stopId; @@ -173,6 +196,7 @@ private IpcPrediction(String vehicleId, String routeId, String stopId, this.trip = null; this.tripId = tripId; this.tripPatternId = tripPatternId; + this.isTripUnscheduled = isTripUnscheduled; this.blockId = blockId; this.predictionTime = predictionTime; this.actualPredictionTime = actualPredictionTime; @@ -189,8 +213,15 @@ private IpcPrediction(String vehicleId, String routeId, String stopId, this.lateAndSubsequentTripSoMarkAsUncertain = lateAndSubsequentTripSoMarkAsUncertain; this.isArrival = isArrival; + + this.freqStartTime = freqStartTime; + this.tripCounter = tripCounter; + + this.delay = delay; + this.isCanceled=isCanceled; } + /** * SerializationProxy is used so that this class can be immutable and so * that can do versioning of objects. @@ -203,6 +234,7 @@ private static class SerializationProxy implements Serializable { private int gtfsStopSeq; private String tripId; private String tripPatternId; + private boolean isTripUnscheduled; private String blockId; private long predictionTime; private boolean atEndOfTrip; @@ -218,7 +250,13 @@ private static class SerializationProxy implements Serializable { private boolean lateAndSubsequentTripSoMarkAsUncertain; private boolean isArrival; - private static final long serialVersionUID = -8585283691951746718L; + private long freqStartTime; + private int tripCounter; + + private Integer delay; + private boolean isCanceled; + + private static final long serialVersionUID = -8585283691951746719L; private static final short currentSerializationVersion = 0; /* @@ -231,6 +269,7 @@ private SerializationProxy(IpcPrediction p) { this.gtfsStopSeq = p.gtfsStopSeq; this.tripId = p.tripId; this.tripPatternId = p.tripPatternId; + this.isTripUnscheduled = p.isTripUnscheduled; this.blockId = p.blockId; this.predictionTime = p.predictionTime; this.atEndOfTrip = p.atEndOfTrip; @@ -246,6 +285,12 @@ private SerializationProxy(IpcPrediction p) { this.lateAndSubsequentTripSoMarkAsUncertain = p.lateAndSubsequentTripSoMarkAsUncertain; this.isArrival = p.isArrival; + + this.freqStartTime = p.freqStartTime; + this.tripCounter = p.tripCounter; + + this.delay = p.delay; + this.isCanceled=p.isCanceled; } /* @@ -264,6 +309,7 @@ private void writeObject(java.io.ObjectOutputStream stream) stream.writeInt(gtfsStopSeq); stream.writeObject(tripId); stream.writeObject(tripPatternId); + stream.writeBoolean(isTripUnscheduled); stream.writeObject(blockId); stream.writeLong(predictionTime); stream.writeBoolean(atEndOfTrip); @@ -278,6 +324,12 @@ private void writeObject(java.io.ObjectOutputStream stream) stream.writeBoolean(isArrival); stream.writeBoolean(isDelayed); stream.writeBoolean(lateAndSubsequentTripSoMarkAsUncertain); + + stream.writeLong(freqStartTime); + stream.writeInt(tripCounter); + + stream.writeObject(delay); + stream.writeBoolean(isCanceled); } /* @@ -303,6 +355,7 @@ private void readObject(java.io.ObjectInputStream stream) gtfsStopSeq = stream.readInt(); tripId = (String) stream.readObject(); tripPatternId = (String) stream.readObject(); + isTripUnscheduled = stream.readBoolean(); blockId = (String) stream.readObject(); predictionTime = stream.readLong(); atEndOfTrip = stream.readBoolean(); @@ -317,6 +370,12 @@ private void readObject(java.io.ObjectInputStream stream) isArrival = stream.readBoolean(); isDelayed = stream.readBoolean(); lateAndSubsequentTripSoMarkAsUncertain = stream.readBoolean(); + + freqStartTime=stream.readLong(); + tripCounter=stream.readInt(); + + delay = (Integer) stream.readObject(); + isCanceled=stream.readBoolean(); } /* @@ -327,11 +386,12 @@ private void readObject(java.io.ObjectInputStream stream) */ private Object readResolve() { return new IpcPrediction(vehicleId, routeId, stopId, gtfsStopSeq, - tripId, tripPatternId, blockId, predictionTime, 0, + tripId, tripPatternId, isTripUnscheduled, blockId, predictionTime, 0, atEndOfTrip, schedBasedPred, avlTime, creationTime, tripStartEpochTime, affectedByWaitStop, driverId, - passengerCount, passengerFullness, isDelayed, - lateAndSubsequentTripSoMarkAsUncertain, isArrival); + passengerCount, passengerFullness, isDelayed, lateAndSubsequentTripSoMarkAsUncertain, + isArrival, delay, freqStartTime, tripCounter,isCanceled); + } } @@ -366,6 +426,7 @@ public String toString() { // + (stopName!=null ? ", stopNm=\"" + stopName + "\"" : "") + ", gtfsStopSeq=" + gtfsStopSeq + ", tripId=" + tripId + + ", freqStartTime=" + Time.timeStrMsecNoTimeZone(freqStartTime) + ", tripPatternId=" + tripPatternId + ", blockId=" + blockId + ", avlTime=" + Time.timeStrMsecNoTimeZone(avlTime) @@ -382,6 +443,7 @@ public String toString() { : "") + (!Float.isNaN(passengerFullness) ? ", psngrFullness=" + StringUtils.twoDigitFormat(passengerFullness) : "") + + ("isCanceled= "+isCanceled) + "]"; } @@ -412,6 +474,10 @@ public String getTripPatternId() { return tripPatternId; } + public boolean isTripUnscheduled() { + return isTripUnscheduled; + } + public String getBlockId() { return blockId; } @@ -518,4 +584,13 @@ public String getRouteShortName() { return null; return trip.getRouteShortName(); } -} + + public Integer getDelay() { + return delay; + } + + public long getFreqStartTime() { + return freqStartTime; + } + +} \ No newline at end of file diff --git a/transitclock/src/main/java/org/transitclock/ipc/data/IpcPredictionForStopPath.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcPredictionForStopPath.java new file mode 100755 index 000000000..5b5753ebe --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcPredictionForStopPath.java @@ -0,0 +1,114 @@ +package org.transitclock.ipc.data; + +import java.io.Serializable; +import java.util.Date; + +import org.transitclock.db.structs.PredictionForStopPath; + +public class IpcPredictionForStopPath implements Serializable { + + /** + * + */ + private static final long serialVersionUID = 4379439564376344615L; + + + private final Date creationTime; + + + private final Double predictionTime; + + + private final String tripId; + + + private final String algorithm; + + + private final Integer stopPathIndex; + + public IpcPredictionForStopPath(PredictionForStopPath predictionForStopPath) { + this.tripId=predictionForStopPath.getTripId(); + this.algorithm=predictionForStopPath.getAlgorithm(); + this.stopPathIndex=predictionForStopPath.getStopPathIndex(); + this.predictionTime=predictionForStopPath.getPredictionTime(); + this.creationTime=predictionForStopPath.getCreationTime(); + + } + + @Override + public String toString() { + return "IpcPredictionForStopPath [creationTime=" + creationTime + ", predictionTime=" + predictionTime + + ", tripId=" + tripId + ", algorithm=" + algorithm + ", stopPathIndex=" + stopPathIndex + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((algorithm == null) ? 0 : algorithm.hashCode()); + result = prime * result + ((creationTime == null) ? 0 : creationTime.hashCode()); + result = prime * result + ((predictionTime == null) ? 0 : predictionTime.hashCode()); + 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; + IpcPredictionForStopPath other = (IpcPredictionForStopPath) obj; + if (algorithm == null) { + if (other.algorithm != null) + return false; + } else if (!algorithm.equals(other.algorithm)) + return false; + if (creationTime == null) { + if (other.creationTime != null) + return false; + } else if (!creationTime.equals(other.creationTime)) + return false; + if (predictionTime == null) { + if (other.predictionTime != null) + return false; + } else if (!predictionTime.equals(other.predictionTime)) + return false; + 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 Date getCreationTime() { + return creationTime; + } + + public Double getPredictionTime() { + return predictionTime; + } + + public String getTripId() { + return tripId; + } + + public String getAlgorithm() { + return algorithm; + } + + public Integer getStopPathIndex() { + return stopPathIndex; + } + +} diff --git a/transitime/src/main/java/org/transitime/ipc/data/IpcPredictionsForRouteStopDest.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcPredictionsForRouteStopDest.java similarity index 87% rename from transitime/src/main/java/org/transitime/ipc/data/IpcPredictionsForRouteStopDest.java rename to transitclock/src/main/java/org/transitclock/ipc/data/IpcPredictionsForRouteStopDest.java index 7d7e8122e..b77c755dd 100644 --- a/transitime/src/main/java/org/transitime/ipc/data/IpcPredictionsForRouteStopDest.java +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcPredictionsForRouteStopDest.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.ipc.data; +package org.transitclock.ipc.data; import java.io.IOException; import java.io.InvalidObjectException; @@ -25,13 +25,16 @@ import java.util.Iterator; import java.util.List; -import org.transitime.applications.Core; -import org.transitime.db.structs.Route; -import org.transitime.db.structs.Stop; -import org.transitime.db.structs.Trip; -import org.transitime.db.structs.TripPattern; -import org.transitime.utils.Geo; -import org.transitime.utils.TrimmableArrayList; +import org.transitclock.applications.Core; +import org.transitclock.db.structs.Route; +import org.transitclock.db.structs.Stop; +import org.transitclock.db.structs.Trip; +import org.transitclock.db.structs.TripPattern; +import org.transitclock.utils.Geo; + +import org.transitclock.utils.TrimmableArrayList; + +import static org.transitclock.core.PredictionGeneratorDefaultImpl.isHistoricalPredictionForFutureStop; /** * Contains list of predictions for a route/stop/destination. @@ -142,12 +145,16 @@ public IpcPredictionsForRouteStopDest(TripPattern tripPattern, String stopId, * @param maxSystemTimeForPrediction * Max point in future want predictions for. This way can limit * predictions when requesting a large number of them. - * @param distanceFromStop + * @param terminatePredictionsAtEndOfTrip + * if set continue after maxSystemTimeForPrediction to serve + * predictions for the entirety of the trip + * @param distanceToStop * For when getting predictions by location */ private IpcPredictionsForRouteStopDest( IpcPredictionsForRouteStopDest toClone, - int maxPredictionsPerStop, long maxSystemTimeForPrediction, + int maxPredictionsPerStop, long maxSystemTimeForPrediction, + boolean terminatePredictionsAtEndOfTrip, double distanceToStop) { this.routeId = toClone.routeId; this.routeShortName = toClone.routeShortName; @@ -168,8 +175,11 @@ private IpcPredictionsForRouteStopDest( this.predictionsForRouteStopDest = new ArrayList(size); for (int i=0; i maxSystemTimeForPrediction) + /* If prediction exceeds max time then done + * EXCEPT if terminatePredictionsAtEndOfTrip, in which case we assume + * we have exactly the amount of predictions expected (we don't filter) + */ + if (!terminatePredictionsAtEndOfTrip && prediction.getPredictionTime() > maxSystemTimeForPrediction) break; this.predictionsForRouteStopDest.add(i, prediction); } @@ -375,9 +385,9 @@ private void readObject(ObjectInputStream stream) * Gets a copy of this object. This is done with the object being * copied synchronized so that the predictions remain coherent. Limits * number of predictions to maxPredictionsPerStop. - * + * * @param maxPredictionsPerStop - * @param distanceFromStop + * @param distanceToStop * For when getting predictions by location * @return */ @@ -388,7 +398,7 @@ public IpcPredictionsForRouteStopDest getClone(int maxPredictionsPerStop, // Integer.MAX_VALUE and currentTime set to 0L because it // doesn't matter. IpcPredictionsForRouteStopDest clone = new IpcPredictionsForRouteStopDest(this, - maxPredictionsPerStop, Long.MAX_VALUE, distanceToStop); + maxPredictionsPerStop, Long.MAX_VALUE, false, distanceToStop); return clone; } @@ -402,14 +412,36 @@ public IpcPredictionsForRouteStopDest getClone(int maxPredictionsPerStop, * @param maxSystemTimeForPrediction * Max point in future want predictions for. This way can limit * predictions when requesting a large number of them. + * @param terminatePredictionsAtEndOfTrip + * if set continue after maxSystemTimeForPrediction to complete + * predictions for the trip. * @param distanceToStop * For when getting predictions by location * @return */ public IpcPredictionsForRouteStopDest getClone(int maxPredictionsPerStop, - long maxSystemTimeForPrediction, double distanceToStop) { + long maxSystemTimeForPrediction, boolean terminatePredictionsAtEndOfTrip, double distanceToStop) { IpcPredictionsForRouteStopDest clone = new IpcPredictionsForRouteStopDest( - this, maxPredictionsPerStop, maxSystemTimeForPrediction, distanceToStop); + this, maxPredictionsPerStop, maxSystemTimeForPrediction, terminatePredictionsAtEndOfTrip, distanceToStop); + return clone; + } + + /** + * Gets a copy of this object. This is done with the object being copied + * synchronized so that the predictions remain coherent. Limits number of + * predictions to maxPredictionsPerStop. + * + * @param maxPredictionsPerStop + * Won't copy more then this number of predictions + * @param maxSystemTimeForPrediction + * Max point in future want predictions for. This way can limit + * predictions when requesting a large number of them. + * @return + */ + public IpcPredictionsForRouteStopDest getClone(int maxPredictionsPerStop, + long maxSystemTimeForPrediction, boolean terminatePredictionsAtEndOfTrip) { + IpcPredictionsForRouteStopDest clone = getClone(maxPredictionsPerStop, + maxSystemTimeForPrediction, terminatePredictionsAtEndOfTrip, Double.NaN); return clone; } @@ -440,21 +472,19 @@ public synchronized void removeExpiredPredictions(long currentTime) { while (iterator.hasNext()) { IpcPrediction currentPrediction = iterator.next(); - // Remove predictions that are expired. It makes sense to do this + + // Remove predictions that are expired. It makes sense to do this // here when adding predictions since only need to take out // predictions if more are being added. if (currentPrediction.getPredictionTime() < currentTime) { - iterator.remove(); - } else { - // The subsequent predictions are later so if this one is - // into the future then the remaining ones are too. - // Therefore done. - return; + // per the spec, we need to serve predictions until the scheduled time has past + if (!isHistoricalPredictionForFutureStop(currentPrediction, currentTime)) { + iterator.remove(); + } } } - } - + /** * Updates the predictions for this object with the new predictions for a * vehicle. @@ -486,16 +516,19 @@ public synchronized void updatePredictionsForVehicle( // Remove existing predictions for this vehicle if (currentPrediction.getVehicleId().equals(vehicleId)) { - iterator.remove(); - continue; + // hold on to past prediction for future stop + if (!isHistoricalPredictionForFutureStop(currentPrediction, currentTime)) { + iterator.remove(); + continue; + } } // Remove predictions that are expired. It makes sense to do this // here when adding predictions since only need to take out // predictions if more are being added. if (currentPrediction.getPredictionTime() < currentTime) { - iterator.remove(); - continue; + iterator.remove(); + continue; } } @@ -610,4 +643,5 @@ public List getPredictionsForRouteStop() { public int getRouteOrder() { return routeOrder; } + } diff --git a/transitclock/src/main/java/org/transitclock/ipc/data/IpcRevisionInformation.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcRevisionInformation.java new file mode 100644 index 000000000..796835c79 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcRevisionInformation.java @@ -0,0 +1,52 @@ +package org.transitclock.ipc.data; + +import java.io.Serializable; +import java.util.Date; + +/** + * Expose schedule metadata to API tier. + */ +public class IpcRevisionInformation implements Serializable { + + private int activeRev; + private int configRev; + private Date processedTime; + private String note; + private Date zipFileTime; + + public int getActiveRev() { + return activeRev; + } + public void setActiveRev(int activeRev) { + this.activeRev = activeRev; + } + + public int getLastConfigRev() { + return configRev; + } + public void setLastConfigRev(int configRev) { + this.configRev = configRev; + } + + public Date getLastProcessedTime() { + return processedTime; + } + public void setLastProcessedTime(Date processedTime) { + this.processedTime = processedTime; + } + + public String getNote() { + return note; + } + + public void setLastNote(String note) { + this.note = note; + } + + public Date getZipFileLastModifiedTime() { + return zipFileTime; + } + public void setZipFileLastModifiedTime(Date zipFileLastModifiedTime) { + this.zipFileTime = zipFileLastModifiedTime; + } +} diff --git a/transitime/src/main/java/org/transitime/ipc/data/IpcRoute.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcRoute.java similarity index 91% rename from transitime/src/main/java/org/transitime/ipc/data/IpcRoute.java rename to transitclock/src/main/java/org/transitclock/ipc/data/IpcRoute.java index 7d0a413c1..49c77fedd 100644 --- a/transitime/src/main/java/org/transitime/ipc/data/IpcRoute.java +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcRoute.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.ipc.data; +package org.transitclock.ipc.data; import java.util.ArrayList; import java.util.Collection; @@ -24,14 +24,14 @@ import java.util.Map; import java.util.Set; -import org.transitime.applications.Core; -import org.transitime.core.dataCache.PredictionDataCache; -import org.transitime.core.dataCache.VehicleDataCache; -import org.transitime.db.structs.Location; -import org.transitime.db.structs.Route; -import org.transitime.db.structs.Stop; -import org.transitime.db.structs.StopPath; -import org.transitime.db.structs.TripPattern; +import org.transitclock.applications.Core; +import org.transitclock.core.dataCache.PredictionDataCache; +import org.transitclock.core.dataCache.VehicleDataCache; +import org.transitclock.db.structs.Location; +import org.transitclock.db.structs.Route; +import org.transitclock.db.structs.Stop; +import org.transitclock.db.structs.StopPath; +import org.transitclock.db.structs.TripPattern; /** * Describes a route such that it can e displayed in a UI. Consists of all the @@ -217,24 +217,40 @@ private static IpcDirectionsForRoute createStops(Route dbRoute, // Determine if UI stop. It is a UI stop if the stopId parameter // specified and the current stop is after the stopId for a UI // trip pattern. + TripPattern currentTripPattern=null; boolean isUiStop = true; if (stopId != null) { isUiStop = false; for (TripPattern tripPattern : uiTripPatterns) { - if (tripPattern.isStopAtOrAfterStop(stopId, - currentStopId)) { + if((tripPattern.getDirectionId()==null && currentDirectionId==null) || tripPattern.getDirectionId().compareTo(currentDirectionId)==0) + currentTripPattern=tripPattern; + if (tripPattern.isStopAtOrAfterStop(stopId, currentStopId)) { isUiStop = true; break; } } } - + else + { + for (TripPattern tripPattern : uiTripPatterns) { + if((tripPattern.getDirectionId()==null && currentDirectionId==null) || tripPattern.getDirectionId().compareTo(currentDirectionId)==0) + currentTripPattern=tripPattern; + } + } + Double stopPathLength=null; + if(currentTripPattern!=null) + { + + StopPath stopPath = currentTripPattern.getStopPath(currentStopId); + if(stopPath!=null) + stopPathLength=stopPath.getLength(); + } // Create the IpcStop and add it to the list of stops for the // current direction Stop stop = Core.getInstance().getDbConfig().getStop(currentStopId); IpcStop ipcStop = - new IpcStop(stop, isUiStop, currentDirectionId); + new IpcStop(stop, isUiStop, currentDirectionId,stopPathLength); ipcStopsForDirection.add(ipcStop); } ipcDirections.add(new IpcDirection(dbRoute, currentDirectionId, diff --git a/transitime/src/main/java/org/transitime/ipc/data/IpcRouteSummary.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcRouteSummary.java similarity index 96% rename from transitime/src/main/java/org/transitime/ipc/data/IpcRouteSummary.java rename to transitclock/src/main/java/org/transitclock/ipc/data/IpcRouteSummary.java index 7b296cdbf..9f905307d 100644 --- a/transitime/src/main/java/org/transitime/ipc/data/IpcRouteSummary.java +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcRouteSummary.java @@ -15,12 +15,12 @@ * along with Transitime.org . If not, see . */ -package org.transitime.ipc.data; +package org.transitclock.ipc.data; import java.io.Serializable; -import org.transitime.db.structs.Extent; -import org.transitime.db.structs.Route; +import org.transitclock.db.structs.Extent; +import org.transitclock.db.structs.Route; /** * Contains configuration information for a single route. For providing info to diff --git a/transitclock/src/main/java/org/transitclock/ipc/data/IpcRunTime.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcRunTime.java new file mode 100644 index 000000000..67b9d892b --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcRunTime.java @@ -0,0 +1,63 @@ +package org.transitclock.ipc.data; + +import com.google.common.base.Objects; + +import java.io.Serializable; + +public class IpcRunTime implements Serializable { + private final Double avgRunTime; + private final Double fixed; + private final Double variable; + private final Double dwell; + + + + public IpcRunTime(Double avgRunTime, Double fixed, Double variable, Double dwell){ + this.avgRunTime = avgRunTime; + this.fixed = fixed; + this.variable = variable; + this.dwell = dwell; + } + + public Double getAvgRunTime() { + return avgRunTime; + } + + public Double getFixed() { + return fixed; + } + + public Double getVariable() { + return variable; + } + + public Double getDwell() { + return dwell; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + IpcRunTime that = (IpcRunTime) o; + return Objects.equal(avgRunTime, that.avgRunTime) && + Objects.equal(fixed, that.fixed) && + Objects.equal(variable, that.variable) && + Objects.equal(dwell, that.dwell); + } + + @Override + public int hashCode() { + return Objects.hashCode(avgRunTime, fixed, variable, dwell); + } + + @Override + public String toString() { + return "IpcRunTime{" + + "avgRunTime=" + avgRunTime + + ", fixed=" + fixed + + ", variable=" + variable + + ", dwell=" + dwell + + '}'; + } +} diff --git a/transitclock/src/main/java/org/transitclock/ipc/data/IpcRunTimeForRoute.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcRunTimeForRoute.java new file mode 100644 index 000000000..9953ef9e4 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcRunTimeForRoute.java @@ -0,0 +1,77 @@ +package org.transitclock.ipc.data; + +import com.google.common.base.Objects; +import java.io.Serializable; + +public class IpcRunTimeForRoute implements Serializable { + + private String routeShortName; + private Integer earlyCount; + private Integer onTimeCount; + private Integer lateCount; + + public IpcRunTimeForRoute(String routeShortName, Integer earlyCount, Integer onTimeCount, Integer lateCount) { + this.routeShortName = routeShortName; + this.earlyCount = earlyCount; + this.onTimeCount = onTimeCount; + this.lateCount = lateCount; + } + + public String getRouteShortName() { + return routeShortName; + } + + public void setRouteShortName(String routeShortName) { + this.routeShortName = routeShortName; + } + + public Integer getEarlyCount() { + return earlyCount; + } + + public void setEarlyCount(Integer earlyCount) { + this.earlyCount = earlyCount; + } + + public Integer getOnTimeCount() { + return onTimeCount; + } + + public void setOnTimeCount(Integer onTimeCount) { + this.onTimeCount = onTimeCount; + } + + public Integer getLateCount() { + return lateCount; + } + + public void setLateCount(Integer lateCount) { + this.lateCount = lateCount; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + IpcRunTimeForRoute that = (IpcRunTimeForRoute) o; + return Objects.equal(routeShortName, that.routeShortName) && + Objects.equal(earlyCount, that.earlyCount) && + Objects.equal(onTimeCount, that.onTimeCount) && + Objects.equal(lateCount, that.lateCount); + } + + @Override + public int hashCode() { + return Objects.hashCode(routeShortName, earlyCount, onTimeCount, lateCount); + } + + @Override + public String toString() { + return "IpcRunTimeForRoute{" + + "routeShortName='" + routeShortName + '\'' + + ", earlyCount=" + earlyCount + + ", onTimeCount=" + onTimeCount + + ", lateCount=" + lateCount + + '}'; + } +} diff --git a/transitclock/src/main/java/org/transitclock/ipc/data/IpcRunTimeForStopPath.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcRunTimeForStopPath.java new file mode 100644 index 000000000..75afb6992 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcRunTimeForStopPath.java @@ -0,0 +1,79 @@ +package org.transitclock.ipc.data; + +import com.google.common.base.Objects; + +public class IpcRunTimeForStopPath extends IpcRunTime { + private String stopPathId; + private Integer stopPathIndex; + private Integer prevStopSchDepartureTime; + private Integer currentStopSchDepartureTime; + private Double scheduledCompletionTime; + + public IpcRunTimeForStopPath(String stopPathId, Integer stopPathIndex, Integer prevStopSchDepartureTime, + Integer currentStopSchDepartureTime, Double avgRunTime, Double fixed, Double variable, + Double dwell){ + super(avgRunTime, fixed, variable, dwell); + this.stopPathId = stopPathId; + this.stopPathIndex = stopPathIndex; + this.prevStopSchDepartureTime = prevStopSchDepartureTime; + this.currentStopSchDepartureTime = currentStopSchDepartureTime; + + + if(prevStopSchDepartureTime != null && currentStopSchDepartureTime != null) { + double schTimeLengthMsec = (currentStopSchDepartureTime - prevStopSchDepartureTime) * 1000; + this.scheduledCompletionTime = schTimeLengthMsec; + } + + } + + public String getStopPathId() { + return stopPathId; + } + + public Integer getStopPathIndex() { + return stopPathIndex; + } + + public Integer getPrevStopSchDepartureTime() { + return prevStopSchDepartureTime; + } + + public Integer getCurrentStopSchDepartureTime() { + return currentStopSchDepartureTime; + } + + public Double getScheduledCompletionTime() { + return scheduledCompletionTime; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + IpcRunTimeForStopPath that = (IpcRunTimeForStopPath) o; + return Objects.equal(stopPathId, that.stopPathId) && + Objects.equal(prevStopSchDepartureTime, that.prevStopSchDepartureTime) && + Objects.equal(currentStopSchDepartureTime, that.currentStopSchDepartureTime) && + Objects.equal(scheduledCompletionTime, that.scheduledCompletionTime); + } + + @Override + public int hashCode() { + return Objects.hashCode(super.hashCode(), stopPathId, prevStopSchDepartureTime, currentStopSchDepartureTime, scheduledCompletionTime); + } + + @Override + public String toString() { + return "IpcRunTimeForStopPath{" + + "stopPathId='" + stopPathId + '\'' + + ", prevStopSchDepartureTime=" + prevStopSchDepartureTime + + ", currentStopSchDepartureTime=" + currentStopSchDepartureTime + + ", scheduledCompletionTime=" + scheduledCompletionTime + + ", avgRunTime=" + getAvgRunTime() + + ", fixed=" + getFixed() + + ", variable=" + getVariable() + + ", dwell=" + getDwell() + + '}'; + } +} diff --git a/transitclock/src/main/java/org/transitclock/ipc/data/IpcRunTimeForTrip.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcRunTimeForTrip.java new file mode 100644 index 000000000..d0086024b --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcRunTimeForTrip.java @@ -0,0 +1,82 @@ +package org.transitclock.ipc.data; + +import com.google.common.base.Objects; + +public class IpcRunTimeForTrip extends IpcRunTime { + private final String tripId; + private Integer scheduledTripStartTime; + private Double scheduledTripCompletionTime; + private Double nextScheduledTripStartTime; + + public IpcRunTimeForTrip(String tripId, + Integer tripSchStartTime, + Integer tripSchEndTime, + Integer nextTripSchStartTime, + Double avgRunTime, + Double fixed, + Double variable, + Double dwell){ + + super(avgRunTime, fixed, variable, dwell); + + this.tripId = tripId; + this.scheduledTripStartTime = tripSchStartTime; + + if(tripSchStartTime != null && tripSchEndTime != null) { + double tripSchTimeLengthMsec = (tripSchEndTime - tripSchStartTime) * 1000; + this.scheduledTripCompletionTime = tripSchTimeLengthMsec; + } + + if(tripSchStartTime != null && nextTripSchStartTime != null) { + double nextTripSchTripLengthMsec = (nextTripSchStartTime - tripSchStartTime) * 1000; + this.nextScheduledTripStartTime = nextTripSchTripLengthMsec; + } + } + + public String getTripId() { + return tripId; + } + + public Integer getScheduledTripStartTime() { + return scheduledTripStartTime; + } + + public Double getScheduledTripCompletionTime() { + return scheduledTripCompletionTime; + } + + public Double getNextScheduledTripStartTime() { + return nextScheduledTripStartTime; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + IpcRunTimeForTrip that = (IpcRunTimeForTrip) o; + return Objects.equal(tripId, that.tripId) && + Objects.equal(scheduledTripStartTime, that.scheduledTripStartTime) && + Objects.equal(scheduledTripCompletionTime, that.scheduledTripCompletionTime) && + Objects.equal(nextScheduledTripStartTime, that.nextScheduledTripStartTime); + } + + @Override + public int hashCode() { + return Objects.hashCode(super.hashCode(), tripId, scheduledTripStartTime, scheduledTripCompletionTime, nextScheduledTripStartTime); + } + + @Override + public String toString() { + return "IpcRunTimeForTrip{" + + "tripId=" + tripId + + ", scheduledTripStartTime=" + scheduledTripStartTime + + ", scheduledTripCompletionTime=" + scheduledTripCompletionTime + + ", nextScheduledTripStartTime=" + nextScheduledTripStartTime + + ", avgRunTime=" + getAvgRunTime() + + ", fixed=" + getFixed() + + ", variable=" + getVariable() + + ", dwell=" + getDwell() + + '}'; + } +} diff --git a/transitime/src/main/java/org/transitime/ipc/data/IpcSchedTime.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcSchedTime.java similarity index 97% rename from transitime/src/main/java/org/transitime/ipc/data/IpcSchedTime.java rename to transitclock/src/main/java/org/transitclock/ipc/data/IpcSchedTime.java index eba65bb84..9127e85af 100644 --- a/transitime/src/main/java/org/transitime/ipc/data/IpcSchedTime.java +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcSchedTime.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.ipc.data; +package org.transitclock.ipc.data; import java.io.Serializable; diff --git a/transitime/src/main/java/org/transitime/ipc/data/IpcSchedTimes.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcSchedTimes.java similarity index 90% rename from transitime/src/main/java/org/transitime/ipc/data/IpcSchedTimes.java rename to transitclock/src/main/java/org/transitclock/ipc/data/IpcSchedTimes.java index b8a2a873a..e3f443a97 100644 --- a/transitime/src/main/java/org/transitime/ipc/data/IpcSchedTimes.java +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcSchedTimes.java @@ -15,14 +15,14 @@ * along with Transitime.org . If not, see . */ -package org.transitime.ipc.data; +package org.transitclock.ipc.data; import java.io.Serializable; -import org.transitime.applications.Core; -import org.transitime.db.structs.ScheduleTime; -import org.transitime.db.structs.Stop; -import org.transitime.utils.Time; +import org.transitclock.applications.Core; +import org.transitclock.db.structs.ScheduleTime; +import org.transitclock.db.structs.Stop; +import org.transitclock.utils.Time; /** * Configuration information for a schedule times for IPC. diff --git a/transitime/src/main/java/org/transitime/ipc/data/IpcSchedTrip.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcSchedTrip.java similarity index 94% rename from transitime/src/main/java/org/transitime/ipc/data/IpcSchedTrip.java rename to transitclock/src/main/java/org/transitclock/ipc/data/IpcSchedTrip.java index a2797deed..e06f710f6 100644 --- a/transitime/src/main/java/org/transitime/ipc/data/IpcSchedTrip.java +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcSchedTrip.java @@ -15,16 +15,16 @@ * along with Transitime.org . If not, see . */ -package org.transitime.ipc.data; +package org.transitclock.ipc.data; import java.io.Serializable; import java.util.ArrayList; import java.util.List; -import org.transitime.applications.Core; -import org.transitime.db.structs.ScheduleTime; -import org.transitime.db.structs.StopPath; -import org.transitime.db.structs.Trip; +import org.transitclock.applications.Core; +import org.transitclock.db.structs.ScheduleTime; +import org.transitclock.db.structs.StopPath; +import org.transitclock.db.structs.Trip; /** * For describing a trip as part of a schedule diff --git a/transitime/src/main/java/org/transitime/ipc/data/IpcSchedule.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcSchedule.java similarity index 91% rename from transitime/src/main/java/org/transitime/ipc/data/IpcSchedule.java rename to transitclock/src/main/java/org/transitclock/ipc/data/IpcSchedule.java index fcaa1ee10..59999dd6d 100644 --- a/transitime/src/main/java/org/transitime/ipc/data/IpcSchedule.java +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcSchedule.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.ipc.data; +package org.transitclock.ipc.data; import java.io.IOException; import java.io.ObjectInputStream; @@ -28,11 +28,11 @@ import java.util.List; import java.util.Map; -import org.transitime.db.structs.Block; -import org.transitime.db.structs.Route; -import org.transitime.db.structs.ScheduleTime; -import org.transitime.db.structs.Trip; -import org.transitime.utils.MapKey; +import org.transitclock.db.structs.Block; +import org.transitclock.db.structs.Route; +import org.transitclock.db.structs.ScheduleTime; +import org.transitclock.db.structs.Trip; +import org.transitclock.utils.MapKey; /** * Used by RMI to transfer data for a schedule for a route/direction/service @@ -319,7 +319,8 @@ public static List createSchedules(Route route, } // Add current trip to the IpcSchedule - ipcSchedule.trips.add(trip); + if (!ipcSchedule.trips.contains(trip)) + ipcSchedule.trips.add(trip); } } @@ -337,6 +338,36 @@ public static List createSchedules(Route route, // Return the sorted schedules return scheduleList; } + + /** + * Creates a IpcSchedule for a trip + * + * @param trip + * @return + */ + public static List createScheduleForTrip(Trip trip) { + String serviceId = trip.getServiceId(); + String serviceName = serviceId; + + String directionId = trip.getDirectionId(); + String directionName = trip.getHeadsign(); + Route route = trip.getRoute(); + + IpcSchedule ipcSchedule = new IpcSchedule(route, directionId, + directionName, serviceId, serviceName); + + ipcSchedule.trips.add(trip); + ipcSchedule.processTrips(); + + // Convert to a list of IpcSchedules and sort them + List scheduleList = + new ArrayList(1); + + scheduleList.add(ipcSchedule); + + // Return the sorted schedules + return scheduleList; + } @Override public String toString() { diff --git a/transitime/src/main/java/org/transitime/ipc/data/IpcServerStatus.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcServerStatus.java similarity index 94% rename from transitime/src/main/java/org/transitime/ipc/data/IpcServerStatus.java rename to transitclock/src/main/java/org/transitclock/ipc/data/IpcServerStatus.java index 932e101a5..07b135d08 100644 --- a/transitime/src/main/java/org/transitime/ipc/data/IpcServerStatus.java +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcServerStatus.java @@ -15,12 +15,12 @@ * along with Transitime.org . If not, see . */ -package org.transitime.ipc.data; +package org.transitclock.ipc.data; import java.io.Serializable; import java.util.List; -import org.transitime.monitoring.MonitorResult; +import org.transitclock.monitoring.MonitorResult; /** * Represents server status for Inter Process Communication (IPC) @@ -51,4 +51,6 @@ public List getMonitorResults() { return monitorResults; } + + } diff --git a/transitime/src/main/java/org/transitime/ipc/data/IpcShape.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcShape.java similarity index 85% rename from transitime/src/main/java/org/transitime/ipc/data/IpcShape.java rename to transitclock/src/main/java/org/transitclock/ipc/data/IpcShape.java index bc7d39b97..627ccabba 100644 --- a/transitime/src/main/java/org/transitime/ipc/data/IpcShape.java +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcShape.java @@ -15,15 +15,15 @@ * along with Transitime.org . If not, see . */ -package org.transitime.ipc.data; +package org.transitclock.ipc.data; import java.io.Serializable; import java.util.ArrayList; import java.util.List; -import org.transitime.db.structs.Location; -import org.transitime.db.structs.TripPattern; -import org.transitime.db.structs.Vector; +import org.transitclock.db.structs.Location; +import org.transitclock.db.structs.TripPattern; +import org.transitclock.db.structs.Vector; /** * Represents a shape for Inter Process Communication (IPC) @@ -37,7 +37,25 @@ public class IpcShape implements Serializable { private String headsign; private List locations; private boolean isUiShape; + private double length; + private String directionId; + public String getDirectionId() { + return directionId; + } + + public void setDirectionId(String directionId) { + this.directionId = directionId; + } + + public double getLength() { + return length; + } + + public void setLength(double length) { + this.length = length; + } + // For determining if segment from previous stop path can be // combined with segment from new stop path. private static final double MAX_VERTEX_DISTANCE = 3.0; @@ -48,6 +66,8 @@ public class IpcShape implements Serializable { IpcShape(TripPattern tripPattern, boolean isUiShape) { this.tripPatternId = tripPattern.getId(); + this.length=tripPattern.getLength(); + this.directionId=tripPattern.getDirectionId(); this.headsign = tripPattern.getHeadsign(); this.locations = new ArrayList(); this.isUiShape = isUiShape; diff --git a/transitclock/src/main/java/org/transitclock/ipc/data/IpcSkippedStop.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcSkippedStop.java new file mode 100644 index 000000000..1b6d71915 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcSkippedStop.java @@ -0,0 +1,67 @@ +package org.transitclock.ipc.data; + +import java.io.Serializable; +import java.util.Objects; + +public class IpcSkippedStop implements Serializable { + + private static final long serialVersionUID = 1L; + + private String vehicleId; + private String stopId; + private int stopSequence; + + public IpcSkippedStop(String vehicleId, String stopId, int stopSequence){ + this.vehicleId = vehicleId; + this.stopId = stopId; + this.stopSequence = stopSequence; + } + + public String getStopId() { + return stopId; + } + + public void setStopId(String stopId) { + this.stopId = stopId; + } + + public int getStopSequence() { + return stopSequence; + } + + public void setStopSequence(int stopSequence) { + this.stopSequence = stopSequence; + } + + public String getVehicleId() { + return vehicleId; + } + + public void setVehicleId(String vehicleId) { + this.vehicleId = vehicleId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + IpcSkippedStop that = (IpcSkippedStop) o; + return stopSequence == that.stopSequence && + Objects.equals(vehicleId, that.vehicleId) && + Objects.equals(stopId, that.stopId); + } + + @Override + public int hashCode() { + return Objects.hash(vehicleId, stopId, stopSequence); + } + + @Override + public String toString() { + return "IpcSkippedStop{" + + "vehicleId='" + vehicleId + '\'' + + ", stopId='" + stopId + '\'' + + ", stopSequence=" + stopSequence + + '}'; + } +} diff --git a/transitime/src/main/java/org/transitime/ipc/data/IpcStop.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcStop.java similarity index 69% rename from transitime/src/main/java/org/transitime/ipc/data/IpcStop.java rename to transitclock/src/main/java/org/transitclock/ipc/data/IpcStop.java index 7ca923c42..a8bb97203 100644 --- a/transitime/src/main/java/org/transitime/ipc/data/IpcStop.java +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcStop.java @@ -15,12 +15,13 @@ * along with Transitime.org . If not, see . */ -package org.transitime.ipc.data; +package org.transitclock.ipc.data; import java.io.Serializable; -import org.transitime.db.structs.Location; -import org.transitime.db.structs.Stop; +import com.google.common.base.Objects; +import org.transitclock.db.structs.Location; +import org.transitclock.db.structs.Stop; /** * Contains information for a single route. Since Stop objects do @@ -38,18 +39,28 @@ public class IpcStop implements Serializable { private final Location loc; private final boolean isUiStop; private final String directionId; + private Double stopPathLength; + public Double getStopPathLength() { + return stopPathLength; + } + + public void setStopPathLength(Double stopPathLength) { + this.stopPathLength = stopPathLength; + } + private static final long serialVersionUID = 8964112532327897125L; /********************** Member Functions **************************/ - public IpcStop(Stop dbStop, boolean aUiStop, String directionId) { + public IpcStop(Stop dbStop, boolean aUiStop, String directionId, Double stopPathLength) { this.id = dbStop.getId(); this.name = dbStop.getName(); this.code = dbStop.getCode(); this.loc = dbStop.getLoc(); this.isUiStop = aUiStop; this.directionId = directionId; + this.stopPathLength=stopPathLength; } /** @@ -101,4 +112,23 @@ public boolean isUiStop() { public String getDirectionId() { return directionId; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + IpcStop ipcStop = (IpcStop) o; + return isUiStop == ipcStop.isUiStop && + Objects.equal(id, ipcStop.id) && + Objects.equal(name, ipcStop.name) && + Objects.equal(code, ipcStop.code) && + Objects.equal(loc, ipcStop.loc) && + Objects.equal(directionId, ipcStop.directionId) && + Objects.equal(stopPathLength, ipcStop.stopPathLength); + } + + @Override + public int hashCode() { + return Objects.hashCode(id, name, code, loc, isUiStop, directionId, stopPathLength); + } } diff --git a/transitime/src/main/java/org/transitime/ipc/data/IpcStopPath.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcStopPath.java similarity index 90% rename from transitime/src/main/java/org/transitime/ipc/data/IpcStopPath.java rename to transitclock/src/main/java/org/transitclock/ipc/data/IpcStopPath.java index b545eba70..8fb92772f 100644 --- a/transitime/src/main/java/org/transitime/ipc/data/IpcStopPath.java +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcStopPath.java @@ -15,16 +15,16 @@ * along with Transitime.org . If not, see . */ -package org.transitime.ipc.data; +package org.transitclock.ipc.data; import java.io.Serializable; import java.util.List; -import org.transitime.applications.Core; -import org.transitime.db.structs.Location; -import org.transitime.db.structs.Stop; -import org.transitime.db.structs.StopPath; -import org.transitime.utils.Geo; +import org.transitclock.applications.Core; +import org.transitclock.db.structs.Location; +import org.transitclock.db.structs.Stop; +import org.transitclock.db.structs.StopPath; +import org.transitclock.utils.Geo; /** * Configuration information for a StopPath for IPC. @@ -56,7 +56,11 @@ public IpcStopPath(StopPath dbStopPath) { this.stopId = dbStopPath.getStopId(); Stop stop = Core.getInstance().getDbConfig() .getStop(dbStopPath.getStopId()); - this.stopName = stop.getName(); + if(stop != null){ + this.stopName = stop.getName(); + } else { + this.stopName = ""; + } this.gtfsStopSeq = dbStopPath.getGtfsStopSeq(); this.layoverStop = dbStopPath.isLayoverStop(); this.waitStop = dbStopPath.isWaitStop(); diff --git a/transitclock/src/main/java/org/transitclock/ipc/data/IpcStopPathWithSpeed.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcStopPathWithSpeed.java new file mode 100644 index 000000000..ccf2ebab7 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcStopPathWithSpeed.java @@ -0,0 +1,17 @@ +package org.transitclock.ipc.data; + +import org.transitclock.db.structs.StopPath; + +public class IpcStopPathWithSpeed extends IpcStopPath{ + + private final Double speed; + + public IpcStopPathWithSpeed(StopPath stopPath, Double speed) { + super(stopPath); + this.speed = speed; + } + + public Double getSpeed() { + return speed; + } +} diff --git a/transitclock/src/main/java/org/transitclock/ipc/data/IpcStopWithDwellTime.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcStopWithDwellTime.java new file mode 100644 index 000000000..00c520677 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcStopWithDwellTime.java @@ -0,0 +1,50 @@ +package org.transitclock.ipc.data; + +import org.transitclock.db.structs.Stop; + +import java.io.Serializable; + +public class IpcStopWithDwellTime extends IpcStop implements Serializable { + + private final Long dwellTime; + + private final int configRev; + + public IpcStopWithDwellTime(Stop dbStop, boolean aUiStop, String directionId, Double stopPathLength, Long dwellTime) { + super(dbStop, aUiStop, directionId, stopPathLength); + this.dwellTime = dwellTime; + this.configRev = dbStop.getConfigRev(); + } + + /** + * Constructs a stop and sets isUiStop to true. + * + * @param dbStop + */ + public IpcStopWithDwellTime(Stop dbStop, String directionId, Long dwellTime) { + super(dbStop,directionId); + this.dwellTime = dwellTime; + this.configRev = dbStop.getConfigRev(); + } + + public Long getDwellTime() { + return dwellTime; + } + + public int getConfigRev() { + return configRev; + } + + @Override + public String toString() { + return "IpcStopWithDwellTime{" + + "id=" + getId() + + ", name=" + getName() + + ", code=" + getCode() + + ", loc=" + getLoc() + + ", isUiStop=" + isUiStop() + + ", directionId" + getDirectionId() + + ", dwellTime=" + dwellTime + + '}'; + } +} diff --git a/transitime/src/main/java/org/transitime/ipc/data/IpcTrip.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcTrip.java similarity index 96% rename from transitime/src/main/java/org/transitime/ipc/data/IpcTrip.java rename to transitclock/src/main/java/org/transitclock/ipc/data/IpcTrip.java index f0cf7e20b..334a5a4be 100644 --- a/transitime/src/main/java/org/transitime/ipc/data/IpcTrip.java +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcTrip.java @@ -15,15 +15,15 @@ * along with Transitime.org . If not, see . */ -package org.transitime.ipc.data; +package org.transitclock.ipc.data; import java.io.Serializable; import java.util.ArrayList; import java.util.List; -import org.transitime.db.structs.TravelTimesForTrip; -import org.transitime.db.structs.Trip; -import org.transitime.utils.Time; +import org.transitclock.db.structs.TravelTimesForTrip; +import org.transitclock.db.structs.Trip; +import org.transitclock.utils.Time; /** * Configuration information for a Trip for IPC. diff --git a/transitime/src/main/java/org/transitime/ipc/data/IpcTripPattern.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcTripPattern.java similarity index 84% rename from transitime/src/main/java/org/transitime/ipc/data/IpcTripPattern.java rename to transitclock/src/main/java/org/transitclock/ipc/data/IpcTripPattern.java index 592a137ab..54f9903d4 100644 --- a/transitime/src/main/java/org/transitime/ipc/data/IpcTripPattern.java +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcTripPattern.java @@ -15,15 +15,15 @@ * along with Transitime.org . If not, see . */ -package org.transitime.ipc.data; +package org.transitclock.ipc.data; import java.io.Serializable; import java.util.ArrayList; import java.util.List; -import org.transitime.db.structs.Extent; -import org.transitime.db.structs.StopPath; -import org.transitime.db.structs.TripPattern; +import org.transitclock.db.structs.Extent; +import org.transitclock.db.structs.StopPath; +import org.transitclock.db.structs.TripPattern; /** * Configuration information for a TripPattern. For IPC. @@ -41,6 +41,8 @@ public class IpcTripPattern implements Serializable { private final String routeShortName; private final Extent extent; private final String shapeId; + private final String firstStopName; + private final String lastStopName; private final List stopPaths; private static final long serialVersionUID = 5631162916487757340L; @@ -56,6 +58,9 @@ public IpcTripPattern(TripPattern dbTripPattern) { this.routeShortName = dbTripPattern.getRouteShortName(); this.extent = dbTripPattern.getExtent(); this.shapeId = dbTripPattern.getShapeId(); + this.firstStopName = dbTripPattern.getStopName(0); + this.lastStopName = dbTripPattern.getStopName(dbTripPattern.getNumberStopPaths() - 1); + this.stopPaths = new ArrayList(); for (StopPath stopPath : dbTripPattern.getStopPaths()) @@ -109,6 +114,14 @@ public String getShapeId() { return shapeId; } + public String getFirstStopName() { + return firstStopName; + } + + public String getLastStopName() { + return lastStopName; + } + public List getStopPaths() { return stopPaths; } diff --git a/transitime/src/main/java/org/transitime/ipc/data/IpcVehicle.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcVehicle.java similarity index 82% rename from transitime/src/main/java/org/transitime/ipc/data/IpcVehicle.java rename to transitclock/src/main/java/org/transitclock/ipc/data/IpcVehicle.java index 57d841858..422da6ca1 100644 --- a/transitime/src/main/java/org/transitime/ipc/data/IpcVehicle.java +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcVehicle.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.ipc.data; +package org.transitclock.ipc.data; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -24,15 +24,18 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; - -import org.transitime.core.BlockAssignmentMethod; -import org.transitime.core.SpatialMatch; -import org.transitime.core.TemporalDifference; -import org.transitime.core.VehicleState; -import org.transitime.core.dataCache.PredictionDataCache; -import org.transitime.db.structs.AvlReport.AssignmentType; -import org.transitime.db.structs.Trip; -import org.transitime.utils.Time; +import java.util.Date; + +import org.transitclock.applications.Core; +import org.transitclock.core.BlockAssignmentMethod; +import org.transitclock.core.SpatialMatch; +import org.transitclock.core.TemporalDifference; +import org.transitclock.core.VehicleState; +import org.transitclock.core.dataCache.PredictionDataCache; +import org.transitclock.db.structs.Location; +import org.transitclock.db.structs.Trip; +import org.transitclock.db.structs.AvlReport.AssignmentType; +import org.transitclock.utils.Time; import net.jcip.annotations.Immutable; @@ -46,6 +49,10 @@ @Immutable public class IpcVehicle implements Serializable { + public boolean isAtStop() { + return isAtStop; + } + private final String blockId; private final BlockAssignmentMethod blockAssignmentMethod; private final IpcAvl avl; @@ -70,8 +77,16 @@ public class IpcVehicle implements Serializable { private final String nextStopId; private final String nextStopName; private final String vehicleType; + + private long freqStartTime; + private final boolean isAtStop; + private final IpcHoldingTime holdingTime; + + private final double predictedLatitude; + private final double predictedLongitude; + - private static final long serialVersionUID = -1744566765456572042L; + private static final long serialVersionUID = -1744566765456572041L; /********************** Member Functions **************************/ @@ -126,6 +141,12 @@ public IpcVehicle(VehicleState vs) { this.nextStopName = match.getStopPath().getStopName(); this.vehicleType = match.getRoute().getType(); + + if(vs.getTripCounter()!=null && vs.getTripStartTime(vs.getTripCounter())!=null) + { + this.freqStartTime = vs.getTripStartTime(vs.getTripCounter()); + } + } else { // Vehicle not assigned to trip so null out parameters this.blockId = null; @@ -143,6 +164,31 @@ public IpcVehicle(VehicleState vs) { this.schedBasedPred = vs.isForSchedBasedPreds(); this.realTimeSchedAdh = vs.getRealTimeSchedAdh(); this.isDelayed = vs.isDelayed(); + + + if(vs.getMatch()!=null) + this.isAtStop = vs.getMatch().isAtStop(); + else + this.isAtStop = false; + + if(vs.getHoldingTime()!=null) + { + this.holdingTime = new IpcHoldingTime(vs.getHoldingTime()); + this.holdingTime.setCurrentTime(new Date(Core.getInstance().getSystemTime())); + } + else + { + this.holdingTime = null; + } + + if (vs.getMatch() != null && vs.getMatch().getLocation() != null) { + Location matchLocation = vs.getMatch().getLocation(); + this.predictedLatitude = matchLocation.getLat(); + this.predictedLongitude = matchLocation.getLon(); + } else { + this.predictedLatitude = 0.0; + this.predictedLongitude = 0.0; + } } /** @@ -170,6 +216,7 @@ public IpcVehicle(VehicleState vs) { * @param nextStopId * @param nextStopName * @param vehicleType + * @param freqStartTime */ protected IpcVehicle(String blockId, BlockAssignmentMethod blockAssignmentMethod, IpcAvl avl, @@ -178,7 +225,9 @@ protected IpcVehicle(String blockId, String directionId, String headsign, boolean predictable, boolean schedBasedPred, TemporalDifference realTimeSchdAdh, boolean isDelayed, boolean isLayover, long layoverDepartureTime, - String nextStopId, String nextStopName, String vehicleType) { + + String nextStopId, String nextStopName, String vehicleType, long freqStartTime, boolean isAtStop, IpcHoldingTime holdingTime, double predictedLatitude, double predictedLongitude) { + this.blockId = blockId; this.blockAssignmentMethod = blockAssignmentMethod; this.avl = avl; @@ -199,6 +248,14 @@ protected IpcVehicle(String blockId, this.nextStopId = nextStopId; this.nextStopName = nextStopName; this.vehicleType = vehicleType; + + this.freqStartTime = freqStartTime; + this.isAtStop = isAtStop; + this.holdingTime = holdingTime; + + this.predictedLatitude = predictedLatitude; + this.predictedLongitude = predictedLongitude; + } /* @@ -228,7 +285,15 @@ protected static class SerializationProxy implements Serializable { protected String nextStopName; protected String vehicleType; - private static final long serialVersionUID = -4996254752417270043L; + protected long freqStartTime; + protected boolean isAtStop; + protected IpcHoldingTime holdingTime; + + protected double predictedLatitude; + protected double predictedLongitude; + + + private static final long serialVersionUID = -4996254752417270041L; private static final short currentSerializationVersion = 0; /* @@ -255,6 +320,14 @@ protected SerializationProxy(IpcVehicle v) { this.nextStopId = v.nextStopId; this.nextStopName = v.nextStopName; this.vehicleType = v.vehicleType; + + this.freqStartTime = v.freqStartTime; + this.isAtStop = v.isAtStop; + this.holdingTime = v.holdingTime; + + this.predictedLatitude = v.predictedLatitude; + this.predictedLongitude = v.predictedLongitude; + } /* @@ -277,16 +350,24 @@ protected void writeObject(java.io.ObjectOutputStream stream) stream.writeObject(tripId); stream.writeObject(tripPatternId); stream.writeObject(directionId); - stream.writeObject(headsign); + stream.writeObject(headsign); stream.writeBoolean(predictable); stream.writeBoolean(schedBasedPred); stream.writeObject(realTimeSchdAdh); stream.writeBoolean(isDelayed); + stream.writeBoolean(isLayover); stream.writeLong(layoverDepartureTime); stream.writeObject(nextStopId); stream.writeObject(nextStopName); stream.writeObject(vehicleType); + stream.writeObject(freqStartTime); + stream.writeBoolean(isAtStop); + stream.writeObject(holdingTime); + + stream.writeDouble(predictedLatitude); + stream.writeDouble(predictedLongitude); + } /* @@ -326,6 +407,12 @@ protected void readObject(java.io.ObjectInputStream stream) nextStopId = (String) stream.readObject(); nextStopName = (String) stream.readObject(); vehicleType = (String) stream.readObject(); + freqStartTime = stream.readLong(); + isAtStop = stream.readBoolean(); + holdingTime = (IpcHoldingTime)stream.readObject(); + predictedLatitude = stream.readDouble(); + predictedLongitude = stream.readDouble(); + } /* @@ -339,7 +426,8 @@ private Object readResolve() { routeId, routeShortName, routeName, tripId, tripPatternId, directionId, headsign, predictable, schedBasedPred, realTimeSchdAdh, isDelayed, isLayover, layoverDepartureTime, - nextStopId, nextStopName, vehicleType); + nextStopId, nextStopName, vehicleType, freqStartTime, isAtStop, holdingTime, predictedLatitude, predictedLongitude); + } } // End of SerializationProxy class @@ -389,6 +477,10 @@ public float getHeading() { return heading; } + public IpcHoldingTime getHoldingTime() { + return holdingTime; + } + /** * @return Speed of vehicle, or Float.NaN if speed not defined. */ @@ -403,6 +495,14 @@ public float getLatitude() { public float getLongitude() { return avl.getLongitude(); } + + public double getPredictedLatitude() { + return predictedLatitude; + } + + public double getPredictedLongitude() { + return predictedLongitude; + } public String getLicensePlate() { return avl.getLicensePlate(); @@ -515,6 +615,7 @@ public String toString() { + ", avl=" + avl + ", heading=" + heading + ", vehicleType=" + vehicleType + + ", holdingTime=" + holdingTime + "]"; } @@ -530,8 +631,9 @@ public static void main(String args[]) { new IpcVehicle("blockId", BlockAssignmentMethod.AVL_FEED_BLOCK_ASSIGNMENT, avl, 123.456f, "routeId", "routeShortName", "routeName", - "tripId", "tripPatternId", "dirId", "headsign", true, - false, null, false, false, 0, null, null, null); + "tripId", "tripPatternId", "dirId", "headsign", true, + false, null, false, false, 0, null, null, null, -1, false, null, 0.0, 0.0); + try { FileOutputStream fileOut = new FileOutputStream("foo.ser"); ObjectOutputStream outStream = new ObjectOutputStream(fileOut); @@ -558,4 +660,8 @@ public static void main(String args[]) { } } + public long getFreqStartTime() { + return freqStartTime; + } + } diff --git a/transitime/src/main/java/org/transitime/ipc/data/IpcVehicleComplete.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcVehicleComplete.java similarity index 73% rename from transitime/src/main/java/org/transitime/ipc/data/IpcVehicleComplete.java rename to transitclock/src/main/java/org/transitclock/ipc/data/IpcVehicleComplete.java index c0636e2d7..1735115b3 100644 --- a/transitime/src/main/java/org/transitime/ipc/data/IpcVehicleComplete.java +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcVehicleComplete.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,7 +15,15 @@ * along with Transitime.org . If not, see . */ -package org.transitime.ipc.data; +package org.transitclock.ipc.data; + +import org.transitclock.core.BlockAssignmentMethod; +import org.transitclock.core.SpatialMatch; +import org.transitclock.core.TemporalDifference; +import org.transitclock.core.VehicleState; +import org.transitclock.db.structs.Trip; +import org.transitclock.utils.Geo; +import org.transitclock.utils.Time; import java.io.IOException; import java.io.InvalidObjectException; @@ -23,14 +31,6 @@ import java.util.Date; import java.util.List; -import org.transitime.core.BlockAssignmentMethod; -import org.transitime.core.SpatialMatch; -import org.transitime.core.TemporalDifference; -import org.transitime.core.VehicleState; -import org.transitime.db.structs.Trip; -import org.transitime.utils.Geo; -import org.transitime.utils.Time; - /** * Extension of IpcVehicle class so that all info describing vehicle is * included. All this info is put into the VehicleDataCache so that can get @@ -47,32 +47,33 @@ public class IpcVehicleComplete extends IpcVehicleGtfsRealtime { private final String originStopId; private final String destinationId; - private final double distanceToNextStop; - private final double distanceOfNextStopFromTripStart; - private final double distanceAlongTrip; - + private final Double distanceToNextStop; + private final Double distanceOfNextStopFromTripStart; + private final Double distanceAlongTrip; + private double headway; + private static final long serialVersionUID = 8154105842499551461L; /********************** Member Functions **************************/ /** * The constructor. - * + * * @param vs The current vehicle state. Must not be null. */ public IpcVehicleComplete(VehicleState vs) { super(vs); - + // If vehicle assigned then can set the parameters Trip trip = vs.getTrip(); if (trip != null) { - List stopIdsForTrip = + List stopIdsForTrip = vs.getTrip().getTripPattern().getStopIds(); this.originStopId = stopIdsForTrip.get(0); this.destinationId = stopIdsForTrip.get(stopIdsForTrip.size()-1); - + // Get the match. If match is just after a stop then adjust - // it to just before the stop so that can determine proper + // it to just before the stop so that can determine proper // stop ID and such. SpatialMatch match = vs.getMatch().getMatchBeforeStopIfAtStop(); // If vehicle is at a stop then "next" stop will actually be @@ -85,20 +86,26 @@ public IpcVehicleComplete(VehicleState vs) { sumOfStopPathLengths += trip.getStopPath(spIdx).length(); } this.distanceOfNextStopFromTripStart = sumOfStopPathLengths; - this.distanceAlongTrip = - sumOfStopPathLengths - this.distanceToNextStop; + this.distanceAlongTrip = + sumOfStopPathLengths - this.distanceToNextStop; + if(vs.getHeadway()!=null) + { + this.headway=vs.getHeadway().getHeadway(); + } + else + this.headway=-1; } else { // Vehicle not assigned to trip so null out parameters this.originStopId = null; this.destinationId = null; - this.distanceToNextStop = Double.NaN; - this.distanceOfNextStopFromTripStart = Double.NaN; - this.distanceAlongTrip = Double.NaN; + this.distanceToNextStop =null; //Double.NaN; + this.distanceOfNextStopFromTripStart =null;// Double.NaN; + this.distanceAlongTrip =null; // Double.NaN; } } - + /** - * Constructor used for when deserializing a proxy object. + * Constructor used for when deserializing a proxy object. * * @param blockId * @param blockAssignmentMethod @@ -109,6 +116,7 @@ public IpcVehicleComplete(VehicleState vs) { * @param routeName * @param tripId * @param tripPatternId + * @param isTripUnscheduled * @param directionId * @param headsign * @param predictable @@ -132,44 +140,49 @@ public IpcVehicleComplete(VehicleState vs) { private IpcVehicleComplete(String blockId, BlockAssignmentMethod blockAssignmentMethod, IpcAvl avl, float pathHeading, String routeId, String routeShortName, - String routeName, String tripId, String tripPatternId, + String routeName, String tripId, String tripPatternId, boolean isTripUnscheduled, String directionId, String headsign, boolean predictable, boolean schedBasedPred, TemporalDifference realTimeSchdAdh, boolean isDelayed, boolean isLayover, long layoverDepartureTime, String nextStopId, String nextStopName, String vehicleType, long tripStartEpochTime, boolean atStop, String atOrNextStopId, Integer atOrNextGtfsStopSeq, String originStopId, - String destinationId, double distanceToNextStop, - double distanceOfNextStopFromTripStart, double distanceAlongTrip) { + String destinationId, Double distanceToNextStop, + + Double distanceOfNextStopFromTripStart, Double distanceAlongTrip, long freqStartTime, IpcHoldingTime holdingTime, double predictedLatitude, double predictedLongitude,boolean isCanceled, + double headway) { + super(blockId, blockAssignmentMethod, avl, pathHeading, routeId, - routeShortName, routeName, tripId, tripPatternId, directionId, headsign, + routeShortName, routeName, tripId, tripPatternId, isTripUnscheduled, directionId, headsign, predictable, schedBasedPred, realTimeSchdAdh, isDelayed, isLayover, layoverDepartureTime, nextStopId, nextStopName, vehicleType, tripStartEpochTime, atStop, atOrNextStopId, - atOrNextGtfsStopSeq); + atOrNextGtfsStopSeq, freqStartTime, holdingTime, predictedLatitude, predictedLongitude,isCanceled); + this.originStopId = originStopId; this.destinationId = destinationId; this.distanceToNextStop = distanceToNextStop; this.distanceOfNextStopFromTripStart = distanceOfNextStopFromTripStart; this.distanceAlongTrip = distanceAlongTrip; + this.headway=headway; } - + /* * SerializationProxy is used so that this class can be immutable and so * that can do versioning of objects. */ - protected static class CompleteVehicleSerializationProxy + protected static class CompleteVehicleSerializationProxy extends GtfsRealtimeVehicleSerializationProxy { // Exact copy of fields of IpcCompleteVehicle enclosing class object private String originStopId; private String destinationId; - private double distanceToNextStop; - private double distanceOfNextStopFromTripStart; - private double distanceAlongTrip; - + private Double distanceToNextStop; + private Double distanceOfNextStopFromTripStart; + private Double distanceAlongTrip; + private double headway; private static final short currentSerializationVersion = 0; - + private static final long serialVersionUID = 6982458672576764027L; private CompleteVehicleSerializationProxy(IpcVehicleComplete v) { @@ -179,8 +192,9 @@ private CompleteVehicleSerializationProxy(IpcVehicleComplete v) { this.distanceToNextStop = v.distanceToNextStop; this.distanceOfNextStopFromTripStart = v.distanceOfNextStopFromTripStart; this.distanceAlongTrip = v.distanceAlongTrip; + this.headway=v.headway; } - + /* * When object is serialized writeReplace() causes this * SerializationProxy object to be written. Write it in a custom way @@ -191,15 +205,16 @@ protected void writeObject(java.io.ObjectOutputStream stream) throws IOException { // Write the data for IpcGtfsRealtimeVehicle super class super.writeObject(stream); - + // Write the data for this class stream.writeShort(currentSerializationVersion); - + stream.writeObject(originStopId); stream.writeObject(destinationId); - stream.writeDouble(distanceToNextStop); - stream.writeDouble(distanceOfNextStopFromTripStart); - stream.writeDouble(distanceAlongTrip); + stream.writeObject(distanceToNextStop); + stream.writeObject(distanceOfNextStopFromTripStart); + stream.writeObject(distanceAlongTrip); + stream.writeDouble(headway); } /* @@ -212,23 +227,25 @@ protected void readObject(java.io.ObjectInputStream stream) // If reading from a newer version of protocol then don't // know how to handle it so throw exception - short readVersion = stream.readShort(); + short readVersion = stream.readShort(); if (currentSerializationVersion < readVersion) { throw new IOException("Serialization error when reading " + getClass().getSimpleName() - + " object. Read version=" + readVersion - + " but currently using software version=" + + " object. Read version=" + readVersion + + " but currently using software version=" + currentSerializationVersion); } // Read in data for this class originStopId = (String) stream.readObject(); destinationId = (String) stream.readObject(); - distanceToNextStop = stream.readDouble(); - distanceOfNextStopFromTripStart = stream.readDouble(); - distanceAlongTrip = stream.readDouble(); + distanceToNextStop = (Double)stream.readObject(); + distanceOfNextStopFromTripStart = (Double)stream.readObject(); + distanceAlongTrip =(Double) stream.readObject(); + isCanceled=stream.readBoolean(); + headway=stream.readDouble(); } - + /* * When an object is read in it will be a SerializatProxy object due to * writeReplace() being used by the enclosing class. When such an object @@ -238,19 +255,22 @@ protected void readObject(java.io.ObjectInputStream stream) private Object readResolve() { return new IpcVehicleComplete(blockId, blockAssignmentMethod, avl, heading, routeId, routeShortName, routeName, tripId, - tripPatternId, directionId, headsign, predictable, + tripPatternId, isTripUnscheduled, directionId, headsign, predictable, schedBasedPred, realTimeSchdAdh, isDelayed, isLayover, layoverDepartureTime, nextStopId, nextStopName, vehicleType, tripStartEpochTime, atStop, atOrNextStopId, atOrNextGtfsStopSeq, originStopId, destinationId, distanceToNextStop, distanceOfNextStopFromTripStart, - distanceAlongTrip); + + distanceAlongTrip, freqStartTime, holdingTime, predictedLatitude, predictedLongitude,isCanceled, + headway); + } } // End of class SiriVehicleSerializationProxy - + /* - * Needed as part of using a SerializationProxy. When IpcSerialVehicle + * Needed as part of using a SerializationProxy. When IpcSerialVehicle * object is serialized the SerializationProxy will instead be used. */ private Object writeReplace() { @@ -275,56 +295,62 @@ public String getDestinationId() { return destinationId; } - public double getDistanceToNextStop() { + public Double getDistanceToNextStop() { return distanceToNextStop; } - public double getDistanceOfNextStopFromTripStart() { + public Double getDistanceOfNextStopFromTripStart() { return distanceOfNextStopFromTripStart; } - - public double getDistanceAlongTrip() { + + public Double getDistanceAlongTrip() { return distanceAlongTrip; } + public double getHeadway() + { + return headway; + } @Override public String toString() { - return "IpcExtVehicle [" + return "IpcExtVehicle [" + "vehicleId=" + getId() - + ", blockId=" + getBlockId() + + ", blockId=" + getBlockId() + ", blockAssignmentMethod=" + getBlockAssignmentMethod() + ", routeId=" + getRouteId() + ", routeShortName=" + getRouteShortName() - + ", routeName=" + getRouteName() + + ", routeName=" + getRouteName() + ", tripId=" + getTripId() + ", tripPatternId=" + getTripPatternId() + + ", isTripUnscheduled=" + isTripUnscheduled() + ", directionId=" + getDirectionId() + ", headsign=" + getHeadsign() + ", predictable=" + isPredictable() + ", schedBasedPred=" + isForSchedBasedPred() - + ", realTimeSchedAdh=" + getRealTimeSchedAdh() + + ", realTimeSchedAdh=" + getRealTimeSchedAdh() + ", isDelayed=" + isDelayed() + ", isLayover=" + isLayover() - + ", layoverDepartureTime=" + + ", layoverDepartureTime=" + Time.timeStrNoTimeZone(getLayoverDepartureTime()) - + ", nextStopId=" + getNextStopId() + + ", nextStopId=" + getNextStopId() + ", avl=" + getAvl() - + ", heading=" + getHeading() + + ", heading=" + getHeading() + ", vehicleType=" + getVehicleType() + ", atStop=" + isAtStop() + ", atOrNextStopId=" + getAtOrNextStopId() + ", atOrNextGtfsStopSeq=" + getAtOrNextGtfsStopSeq() + ", tripStartEpochTime=" + getTripStartEpochTime() + ", tripStartEpochTime=" + new Date(getTripStartEpochTime()) - + ", originStopId=" + originStopId - + ", destinationId=" + destinationId - + ", distanceToNextStop=" + + ", isCanceled=" + isCanceled() + + ", originStopId=" + originStopId + + ", headway=" + headway + + ", distanceToNextStop=" + Geo.distanceFormat(distanceToNextStop) - + ", distanceOfNextStopFromTripStart=" + + ", distanceOfNextStopFromTripStart=" + Geo.distanceFormat(distanceOfNextStopFromTripStart) - + ", distanceAlongTrip=" - + Geo.distanceFormat(distanceAlongTrip) + + ", distanceAlongTrip=" + + Geo.distanceFormat(distanceAlongTrip) + "]"; } -} +} \ No newline at end of file diff --git a/transitime/src/main/java/org/transitime/ipc/data/IpcVehicleConfig.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcVehicleConfig.java similarity index 96% rename from transitime/src/main/java/org/transitime/ipc/data/IpcVehicleConfig.java rename to transitclock/src/main/java/org/transitclock/ipc/data/IpcVehicleConfig.java index 138826dcd..5aca83639 100644 --- a/transitime/src/main/java/org/transitime/ipc/data/IpcVehicleConfig.java +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcVehicleConfig.java @@ -15,11 +15,11 @@ * along with Transitime.org . If not, see . */ -package org.transitime.ipc.data; +package org.transitclock.ipc.data; import java.io.Serializable; -import org.transitime.db.structs.VehicleConfig; +import org.transitclock.db.structs.VehicleConfig; /** * For transmitting via Interprocess Communication vehicle configuration info. diff --git a/transitime/src/main/java/org/transitime/ipc/data/IpcVehicleGtfsRealtime.java b/transitclock/src/main/java/org/transitclock/ipc/data/IpcVehicleGtfsRealtime.java similarity index 83% rename from transitime/src/main/java/org/transitime/ipc/data/IpcVehicleGtfsRealtime.java rename to transitclock/src/main/java/org/transitclock/ipc/data/IpcVehicleGtfsRealtime.java index ac6e9b044..28fafa4e2 100644 --- a/transitime/src/main/java/org/transitime/ipc/data/IpcVehicleGtfsRealtime.java +++ b/transitclock/src/main/java/org/transitclock/ipc/data/IpcVehicleGtfsRealtime.java @@ -15,20 +15,17 @@ * along with Transitime.org . If not, see . */ -package org.transitime.ipc.data; +package org.transitclock.ipc.data; + +import org.transitclock.applications.Core; +import org.transitclock.core.*; +import org.transitclock.db.structs.StopPath; +import org.transitclock.db.structs.Trip; +import org.transitclock.utils.Time; import java.io.IOException; import java.util.Date; -import org.transitime.applications.Core; -import org.transitime.core.BlockAssignmentMethod; -import org.transitime.core.SpatialMatch; -import org.transitime.core.TemporalDifference; -import org.transitime.core.TemporalMatch; -import org.transitime.core.VehicleState; -import org.transitime.db.structs.StopPath; -import org.transitime.utils.Time; - /** * Extension of IpcVehicle class so that additional info can be provided for @@ -53,7 +50,22 @@ public class IpcVehicleGtfsRealtime extends IpcVehicle { private final Integer atOrNextGtfsStopSeq; // For GTFS-rt to disambiguate trips - private final long tripStartEpochTime; + private final long tripStartEpochTime; + + + private boolean isCanceled; + + public boolean isCanceled() { + return isCanceled; + } + + public void setCanceled(boolean isCanceled) { + this.isCanceled = isCanceled; + } + + // For GTFS-rt to set scheduled relationship + private final boolean isTripUnscheduled; + private static final long serialVersionUID = -6611046660260490100L; @@ -81,7 +93,7 @@ public IpcVehicleGtfsRealtime(VehicleState vs) { match.getAtStop().getStopPath() : match.getStopPath(); atOrNextStopId = stopPath.getStopId(); atOrNextGtfsStopSeq = stopPath.getGtfsStopSeq(); - + this.isCanceled =vs.isCanceled(); // Note: the trip start date is created on server side so that // proper timezone is used. This unfortunately is a bit expensive. int time = vs.getTrip().getStartTime(); @@ -89,11 +101,14 @@ public IpcVehicleGtfsRealtime(VehicleState vs) { this.tripStartEpochTime = Core.getInstance().getTime() .getEpochTime(time, currentTime); + Trip trip = vs.getTrip(); + this.isTripUnscheduled = trip != null && trip.isNoSchedule() && !trip.isExactTimesHeadway(); } else { atStop = false; atOrNextStopId = null; atOrNextGtfsStopSeq = null; tripStartEpochTime = 0; + isTripUnscheduled = false; } } @@ -110,6 +125,7 @@ public IpcVehicleGtfsRealtime(VehicleState vs) { * @param tripId * @param tripStartDateStr * @param tripPatternId + * @param isTripUnscheduled * @param directionId * @param headsign * @param predictable @@ -124,26 +140,36 @@ public IpcVehicleGtfsRealtime(VehicleState vs) { * @param atStopId * @param atOrNextStopId * @param atOrNextGtfsStopSeq + * @param holdingTime + * @param isCanceled */ protected IpcVehicleGtfsRealtime(String blockId, BlockAssignmentMethod blockAssignmentMethod, IpcAvl avl, float pathHeading, String routeId, String routeShortName, - String routeName, String tripId, String tripPatternId, + String routeName, String tripId, String tripPatternId, boolean isTripUnscheduled, String directionId, String headsign, boolean predictable, boolean schedBasedPred, TemporalDifference realTimeSchdAdh, boolean isDelayed, boolean isLayover, long layoverDepartureTime, String nextStopId, String nextStopName, String vehicleType, long tripStartEpochTime, boolean atStop, String atOrNextStopId, - Integer atOrNextGtfsStopSeq) { + + Integer atOrNextGtfsStopSeq, long freqStartTime, IpcHoldingTime holdingTime, double predictedLatitude, + double predictedLongitude,boolean isCanceled) { + super(blockId, blockAssignmentMethod, avl, pathHeading, routeId, routeShortName, routeName, tripId, tripPatternId, directionId, headsign, predictable, schedBasedPred, realTimeSchdAdh, isDelayed, isLayover, layoverDepartureTime, nextStopId, nextStopName, - vehicleType); + + vehicleType, freqStartTime ,atStop, holdingTime, predictedLatitude, predictedLongitude); + this.atStop = atStop; this.atOrNextStopId = atOrNextStopId; this.atOrNextGtfsStopSeq = atOrNextGtfsStopSeq; this.tripStartEpochTime = tripStartEpochTime; + this.isCanceled=isCanceled; + this.isTripUnscheduled = isTripUnscheduled; + } /* @@ -156,7 +182,8 @@ protected static class GtfsRealtimeVehicleSerializationProxy protected String atOrNextStopId; protected Integer atOrNextGtfsStopSeq; protected long tripStartEpochTime; - + protected boolean isCanceled; + protected boolean isTripUnscheduled; private static final short currentSerializationVersion = 0; private static final long serialVersionUID = 5804716921925188073L; @@ -166,6 +193,8 @@ protected GtfsRealtimeVehicleSerializationProxy(IpcVehicleGtfsRealtime v) { this.atOrNextStopId = v.atOrNextStopId; this.atOrNextGtfsStopSeq = v.atOrNextGtfsStopSeq; this.tripStartEpochTime = v.tripStartEpochTime; + this.isCanceled=v.isCanceled; + this.isTripUnscheduled = v.isTripUnscheduled; } /* @@ -186,6 +215,8 @@ protected void writeObject(java.io.ObjectOutputStream stream) stream.writeObject(atOrNextStopId); stream.writeObject(atOrNextGtfsStopSeq); stream.writeLong(tripStartEpochTime); + stream.writeBoolean(isCanceled); + stream.writeBoolean(isTripUnscheduled); } /* @@ -212,6 +243,8 @@ protected void readObject(java.io.ObjectInputStream stream) atOrNextStopId = (String) stream.readObject(); atOrNextGtfsStopSeq = (Integer) stream.readObject(); tripStartEpochTime = stream.readLong(); + isCanceled=stream.readBoolean(); + isTripUnscheduled = stream.readBoolean(); } /* @@ -223,11 +256,13 @@ protected void readObject(java.io.ObjectInputStream stream) private Object readResolve() { return new IpcVehicleGtfsRealtime(blockId, blockAssignmentMethod, avl, heading, routeId, routeShortName, routeName, tripId, - tripPatternId, directionId, headsign, predictable, + tripPatternId, isTripUnscheduled, directionId, headsign, predictable, schedBasedPred, realTimeSchdAdh, isDelayed, isLayover, layoverDepartureTime, nextStopId, nextStopName, vehicleType, tripStartEpochTime, atStop, atOrNextStopId, - atOrNextGtfsStopSeq); + + atOrNextGtfsStopSeq, freqStartTime, holdingTime, predictedLongitude, predictedLatitude,isCanceled); + } } // End of class GtfsRealtimeVehicleSerializationProxy @@ -236,6 +271,10 @@ public long getTripStartEpochTime() { return tripStartEpochTime; } + public boolean isTripUnscheduled() { + return isTripUnscheduled; + } + /** * Returns true if vehicle at a stop * @return true if at stop @@ -275,6 +314,7 @@ public String toString() { + ", routeShortName=" + getRouteShortName() + ", tripId=" + getTripId() + ", tripPatternId=" + getTripPatternId() + + ", isTripUnscheduled=" + isTripUnscheduled() + ", directionId=" + getDirectionId() + ", headsign=" + getHeadsign() + ", predictable=" + isPredictable() @@ -294,8 +334,10 @@ public String toString() { + ", atOrNextGtfsStopSeq=" + atOrNextGtfsStopSeq + ", tripStartEpochTime=" + tripStartEpochTime + ", tripStartEpochTime=" + new Date(tripStartEpochTime) + + ", isCanceled=" +isCanceled + + ", isTripUnscheduled" +isTripUnscheduled + "]"; } -} +} \ No newline at end of file diff --git a/transitime/src/main/java/org/transitime/ipc/data/package-info.java b/transitclock/src/main/java/org/transitclock/ipc/data/package-info.java similarity index 93% rename from transitime/src/main/java/org/transitime/ipc/data/package-info.java rename to transitclock/src/main/java/org/transitclock/ipc/data/package-info.java index 08ea3dfb2..0b7c5cf24 100644 --- a/transitime/src/main/java/org/transitime/ipc/data/package-info.java +++ b/transitclock/src/main/java/org/transitclock/ipc/data/package-info.java @@ -31,7 +31,7 @@ *

* The way to achieve the goals is to use a inner "SerializationProxy" * class as described by Joshua Bloch in "Effective Java 2nd Edition" in - * item 78. See the org.transitime.ipc.data.Vehicle class for an example. + * item 78. See the org.transitclock.ipc.data.Vehicle class for an example. * Yes, this is more complicated then using the default serialization but * the default is simply not adequate because some of the structures will * change over time and the default serialization cannot handle all such @@ -40,4 +40,4 @@ * @author SkiBu Smith * */ -package org.transitime.ipc.data; \ No newline at end of file +package org.transitclock.ipc.data; \ No newline at end of file diff --git a/transitclock/src/main/java/org/transitclock/ipc/interfaces/CacheQueryInterface.java b/transitclock/src/main/java/org/transitclock/ipc/interfaces/CacheQueryInterface.java new file mode 100755 index 000000000..4ada258c3 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/interfaces/CacheQueryInterface.java @@ -0,0 +1,122 @@ +/* + * 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.ipc.interfaces; + +import java.rmi.Remote; +import java.rmi.RemoteException; +import java.time.LocalDate; +import java.util.Date; +import java.util.List; + +import org.transitclock.ipc.data.IpcArrivalDeparture; +import org.transitclock.ipc.data.IpcHistoricalAverage; +import org.transitclock.ipc.data.IpcHistoricalAverageCacheKey; +import org.transitclock.ipc.data.IpcHoldingTimeCacheKey; +import org.transitclock.ipc.data.IpcKalmanErrorCacheKey; + +/** + * Defines the RMI interface used for obtaining cache runtime information. + * + * @author Sean Og Crudden + * + */ +public interface CacheQueryInterface extends Remote { + + /** + * Returns a list of current arrival or departure events for a specified stop that are in the cache. + * + * @param stopId + * @return List of IpcArrivalDeparture objects for the stop, one + * for each event. + * @throws RemoteException + */ + public List getStopArrivalDepartures( + String stopId) + throws RemoteException; + + /** + * + * Returns the number of entries in the cacheName cache + * @param cacheName + * @return + * @throws RemoteException + */ + public Integer entriesInCache(String cacheName) + throws RemoteException; + + /** + * Returns the historical average value for the trip stopPathIndex that is held in the HistoricalAverageCache + * @param tripId + * @param stopPathIndex + * @return IpcHistoricalAverage + * @throws RemoteException + */ + public IpcHistoricalAverage getHistoricalAverage(String tripId, Integer stopPathIndex) + throws RemoteException; + + + /** + * Return the arrivals and departures for a trip on a specific day and start time + * @param tripId + * @param date + * @param starttime + * @return + * @throws RemoteException + */ + public List getTripArrivalDepartures(String tripId, LocalDate date, Integer starttime) + throws RemoteException; + + + /** + * @return a list of the keys that have values in the historical average cache for schedules based services + * @throws RemoteException + */ + public List getScheduledBasedHistoricalAverageCacheKeys() + throws RemoteException; + + /** + * @return a list of the keys that have values in the historical average cache for frequency based services. + * @throws RemoteException + */ + public List getFrequencyBasedHistoricalAverageCacheKeys() + throws RemoteException; + + /** + * @return a list of the keys that have values in the Kalman error value cache + * @throws RemoteException + */ + public List getKalmanErrorCacheKeys() + throws RemoteException; + + /** + * @return a list of the keys for the holding times in the cache + * @throws RemoteException + */ + public List getHoldingTimeCacheKeys() + throws RemoteException; + + + /** + * Return the latest Kalman error value for a the stop path of a trip. + * @param tripId + * @param stopPathIndex + * @return + * @throws RemoteException + */ + public Double getKalmanErrorValue(String tripId, Integer stopPathIndex) throws RemoteException; + +} diff --git a/transitime/src/main/java/org/transitime/ipc/interfaces/CommandsInterface.java b/transitclock/src/main/java/org/transitclock/ipc/interfaces/CommandsInterface.java similarity index 65% rename from transitime/src/main/java/org/transitime/ipc/interfaces/CommandsInterface.java rename to transitclock/src/main/java/org/transitclock/ipc/interfaces/CommandsInterface.java index 81f66efec..a208ea721 100644 --- a/transitime/src/main/java/org/transitime/ipc/interfaces/CommandsInterface.java +++ b/transitclock/src/main/java/org/transitclock/ipc/interfaces/CommandsInterface.java @@ -14,13 +14,14 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.ipc.interfaces; +package org.transitclock.ipc.interfaces; import java.rmi.Remote; import java.rmi.RemoteException; +import java.time.LocalDateTime; import java.util.Collection; -import org.transitime.ipc.data.IpcAvl; +import org.transitclock.ipc.data.IpcAvl; /** * Defines the RMI interface for sending commands or data to the server (as @@ -48,4 +49,21 @@ public interface CommandsInterface extends Remote { * @throws RemoteException */ public String pushAvl(Collection avlData) throws RemoteException; + + /* + * WIP This is to give a means of manually setting a vehicle unpredictable and unassigned so it will be reassigned quickly. + */ + public void setVehicleUnpredictable(String vehicleId) throws RemoteException; + + /* + * Cancel a trip. It should exists in current predictions. + * Retruns null on success + */ + public String cancelTrip(String tripId,LocalDateTime at) throws RemoteException; + + /* + * Enable a canceled trip. It should exists in current predictions. + * Retruns null on success + */ + String reenableTrip(String tripId, LocalDateTime startTripTime) throws RemoteException; } diff --git a/transitime/src/main/java/org/transitime/ipc/interfaces/ConfigInterface.java b/transitclock/src/main/java/org/transitclock/ipc/interfaces/ConfigInterface.java similarity index 84% rename from transitime/src/main/java/org/transitime/ipc/interfaces/ConfigInterface.java rename to transitclock/src/main/java/org/transitclock/ipc/interfaces/ConfigInterface.java index 0ee4dc030..65e16dc0b 100644 --- a/transitime/src/main/java/org/transitime/ipc/interfaces/ConfigInterface.java +++ b/transitclock/src/main/java/org/transitclock/ipc/interfaces/ConfigInterface.java @@ -15,23 +15,16 @@ * along with Transitime.org . If not, see . */ -package org.transitime.ipc.interfaces; +package org.transitclock.ipc.interfaces; + +import org.transitclock.db.structs.Agency; +import org.transitclock.ipc.data.*; import java.rmi.Remote; import java.rmi.RemoteException; import java.util.Collection; import java.util.List; -import org.transitime.db.structs.Agency; -import org.transitime.ipc.data.IpcBlock; -import org.transitime.ipc.data.IpcCalendar; -import org.transitime.ipc.data.IpcRoute; -import org.transitime.ipc.data.IpcRouteSummary; -import org.transitime.ipc.data.IpcDirectionsForRoute; -import org.transitime.ipc.data.IpcSchedule; -import org.transitime.ipc.data.IpcTrip; -import org.transitime.ipc.data.IpcTripPattern; - /** * Defines the RMI interface for getting configuration data. * @@ -78,12 +71,17 @@ public IpcRoute getRoute(String routeIdOrShortName, String directionId, /** * Obtains ordered list of route details - * @param routeIdOrShortName + * @param routeIdsOrShortNames * @return * @throws RemoteException */ public List getRoutes(List routeIdsOrShortNames) throws RemoteException; - + + /* (non-Javadoc) + * @see org.transitclock.ipc.interfaces.ConfigInterface#getRoutesForStop(java.lang.String) + */ + public List getRoutesForStop(String stopId) throws RemoteException; + /** * Returns stops for each direction for a route. * @@ -96,7 +94,13 @@ public IpcRoute getRoute(String routeIdOrShortName, String directionId, */ public IpcDirectionsForRoute getStops(String routeIdOrShortName) throws RemoteException; - + + /* (non-Javadoc) + * @see org.transitclock.ipc.interfaces.ConfigInterface#getStops(java.util.List) + */ + List getStops(List routeIdOrShortNames) + throws RemoteException; + /** * Returns block info for specified blockId and serviceId. Includes all trip * and trip pattern info associated with the block. @@ -152,14 +156,23 @@ public List getTripPatterns(String routeIdOrShortName) */ public List getSchedules(String routeIdOrShortName) throws RemoteException; - - /** + + List getTripPatterns(String routeIdOrShortName, String headSign) + throws RemoteException; + + /** * Returns list of Agency objects containing data from GTFS agency.txt file * @return * @throws RemoteException */ public List getAgencies() throws RemoteException; - + + /* (non-Javadoc) + * @see org.transitclock.ipc.interfaces.ConfigInterface#getSchedulesForTrip(java.lang.String) + */ + List getSchedulesForTrip(String tripId) + throws RemoteException; + /** * Returns list of Calendar objects that are currently active. * @return @@ -222,6 +235,16 @@ public List getSchedules(String routeIdOrShortName) * @throws RemoteException */ public List getBlockIds(String serviceId) - throws RemoteException; + throws RemoteException; + + public List getServiceIdsForDay(Long currentTimeMillis) throws RemoteException; + + /** + * Returns serviceId suffix config param + * + * @return boolean + * @throws RemoteException + */ + public boolean getServiceIdSuffix() throws RemoteException; } diff --git a/transitclock/src/main/java/org/transitclock/ipc/interfaces/HoldingTimeInterface.java b/transitclock/src/main/java/org/transitclock/ipc/interfaces/HoldingTimeInterface.java new file mode 100755 index 000000000..c2e9ff48a --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/interfaces/HoldingTimeInterface.java @@ -0,0 +1,43 @@ +/* + * 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.ipc.interfaces; + +import java.rmi.Remote; +import java.rmi.RemoteException; +import java.time.LocalDate; +import java.util.Date; +import java.util.List; + +import org.transitclock.ipc.data.IpcArrivalDeparture; +import org.transitclock.ipc.data.IpcHistoricalAverage; +import org.transitclock.ipc.data.IpcHistoricalAverageCacheKey; +import org.transitclock.ipc.data.IpcHoldingTime; +import org.transitclock.ipc.data.IpcHoldingTimeCacheKey; +import org.transitclock.ipc.data.IpcKalmanErrorCacheKey; + +/** + * Defines the RMI interface used for obtaining holding time information. + * + * @author Sean Og Crudden + * + */ +public interface HoldingTimeInterface extends Remote { + + public IpcHoldingTime getHoldTime(String stopId, String vehicleId, String tripId) throws RemoteException; + public IpcHoldingTime getHoldTime(String stopId, String vehicleId) throws RemoteException; + + } diff --git a/transitclock/src/main/java/org/transitclock/ipc/interfaces/PredictionAnalysisInterface.java b/transitclock/src/main/java/org/transitclock/ipc/interfaces/PredictionAnalysisInterface.java new file mode 100755 index 000000000..298151b0d --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/interfaces/PredictionAnalysisInterface.java @@ -0,0 +1,42 @@ +/* + * 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.ipc.interfaces; + +import java.rmi.Remote; +import java.rmi.RemoteException; +import java.util.Date; +import java.util.List; + +import org.transitclock.db.structs.PredictionForStopPath; +import org.transitclock.ipc.data.IpcArrivalDeparture; +import org.transitclock.ipc.data.IpcHistoricalAverage; +import org.transitclock.ipc.data.IpcHistoricalAverageCacheKey; +import org.transitclock.ipc.data.IpcKalmanErrorCacheKey; +import org.transitclock.ipc.data.IpcPredictionForStopPath; + +/** + * Defines the RMI interface used for obtaining data required to look at the quality of predictions. + * + * @author Sean Og Crudden + * + */ +public interface PredictionAnalysisInterface extends Remote { + public List getRecordedTravelTimePredictions(String tripId, Integer stopPathIndex, Date startdate, Date enddate, String algorithm) + throws RemoteException; + public List getCachedTravelTimePredictions(String tripId, Integer stopPathIndex, Date startdate, Date enddate, String algorithm) + throws RemoteException; +} diff --git a/transitime/src/main/java/org/transitime/ipc/interfaces/PredictionsInterface.java b/transitclock/src/main/java/org/transitclock/ipc/interfaces/PredictionsInterface.java similarity index 85% rename from transitime/src/main/java/org/transitime/ipc/interfaces/PredictionsInterface.java rename to transitclock/src/main/java/org/transitclock/ipc/interfaces/PredictionsInterface.java index c772bd60b..891960537 100644 --- a/transitime/src/main/java/org/transitime/ipc/interfaces/PredictionsInterface.java +++ b/transitclock/src/main/java/org/transitclock/ipc/interfaces/PredictionsInterface.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.ipc.interfaces; +package org.transitclock.ipc.interfaces; + +import org.transitclock.core.dataCache.CanceledTripKey; +import org.transitclock.db.structs.Location; +import org.transitclock.ipc.data.IpcCanceledTrip; +import org.transitclock.ipc.data.IpcPredictionsForRouteStopDest; +import org.transitclock.ipc.data.IpcSkippedStop; import java.io.Serializable; import java.rmi.Remote; import java.rmi.RemoteException; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; -import org.transitime.db.structs.Location; -import org.transitime.ipc.data.IpcPredictionsForRouteStopDest; - /** * Defines the RMI interface used for obtaining predictions. * @@ -98,7 +103,18 @@ public List get( public List get( List routeStops, int predictionsPerStop) throws RemoteException; - + + /** + * Returns a list of the latest canceled trips by vehicleId + */ + public HashMap getAllCanceledTrips() throws RemoteException; + + + /** + * Returns a list of the latest skipped stops by tripId + */ + HashMap> getAllSkippedStops() throws RemoteException; + /** * Returns predictions based on the specified location. * diff --git a/transitclock/src/main/java/org/transitclock/ipc/interfaces/ReportingInterface.java b/transitclock/src/main/java/org/transitclock/ipc/interfaces/ReportingInterface.java new file mode 100644 index 000000000..4c88af5ba --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/interfaces/ReportingInterface.java @@ -0,0 +1,65 @@ +package org.transitclock.ipc.interfaces; + +import org.transitclock.core.ServiceType; +import org.transitclock.ipc.data.*; + +import java.rmi.Remote; +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.List; + +/** + * Defines the RMI interface used for obtaining schedule adherence information. + * + * @author carabalb + * + */ +public interface ReportingInterface extends Remote { + List getArrivalsDeparturesForOtp(LocalDate beginDate, LocalDate endDate, + LocalTime beginTime, LocalTime endTime, + String routeIdOrShortName, ServiceType serviceType, + boolean timePointsOnly, String headsign) throws Exception; + + List getArrivalsDeparturesForOtp(LocalDate beginDate, LocalDate endDate, + LocalTime beginTime, LocalTime endTime, + String routeId, ServiceType serviceType, + boolean timePointsOnly, String headsign, + boolean readOnly) throws Exception; + + List getStopsWithAvgDwellTimes(LocalDate beginDate, LocalDate endDate, + LocalTime beginTime, LocalTime endTime, String routeIdOrShortName, + ServiceType serviceType, boolean timePointsOnly, String headsign, + boolean readOnly) throws Exception; + + List getStopPathsWithSpeed(LocalDate beginDate, LocalDate endDate, + LocalTime beginTime, LocalTime endTime, + String routeIdOrShortName, ServiceType serviceType, + String headsign, boolean readOnly) throws Exception; + + IpcDoubleSummaryStatistics getAverageRunTime(LocalDate beginDate, LocalDate endDate, + LocalTime beginTime, LocalTime endTime, String routeIdOrShortName, + ServiceType serviceType, boolean timePointsOnly, + String headsign, boolean readOnly) throws Exception; + + IpcRunTime getRunTimeSummary(LocalDate beginDate, LocalDate endDate, + LocalTime beginTime, LocalTime endTime, + String routeIdOrShortName, String headsign, + String startStop, String endStop, + ServiceType serviceType, boolean timePointsOnly, + boolean currentTripsOnly, boolean readOnly) throws Exception; + + List getRunTimeForTrips(LocalDate beginDate, LocalDate endDate, + LocalTime beginTime, LocalTime endTime, + String routeIdOrShortName, String headsign, + String tripPatternId, ServiceType serviceType, boolean timePointsOnly, + boolean currentTripsOnly, boolean readOnly) throws Exception; + + List getRunTimeForStopPaths(LocalDate beginDate, LocalDate endDate, + LocalTime beginTime, LocalTime endTime, + String routeIdOrShortName, String tripId, ServiceType serviceType, + boolean timePointsOnly, boolean readOnly) throws Exception; + + List getRunTimeForRoutes(LocalDate beginDate, LocalDate endDate, + LocalTime beginTime, LocalTime endTime, ServiceType serviceType, + Integer earlyThreshold, Integer lateThreshold, boolean readOnly) throws Exception; +} \ No newline at end of file diff --git a/transitclock/src/main/java/org/transitclock/ipc/interfaces/RevisionInformationInterface.java b/transitclock/src/main/java/org/transitclock/ipc/interfaces/RevisionInformationInterface.java new file mode 100644 index 000000000..a9fa4ea26 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/interfaces/RevisionInformationInterface.java @@ -0,0 +1,16 @@ +package org.transitclock.ipc.interfaces; + +import org.transitclock.ipc.data.IpcRevisionInformation; + +import java.rmi.Remote; +import java.rmi.RemoteException; + +/** + * Interface for exposing schedule metadata to API tier. + */ +public interface RevisionInformationInterface extends Remote { + + IpcRevisionInformation get() throws RemoteException; + + +} diff --git a/transitime/src/main/java/org/transitime/ipc/interfaces/ServerStatusInterface.java b/transitclock/src/main/java/org/transitclock/ipc/interfaces/ServerStatusInterface.java similarity index 84% rename from transitime/src/main/java/org/transitime/ipc/interfaces/ServerStatusInterface.java rename to transitclock/src/main/java/org/transitclock/ipc/interfaces/ServerStatusInterface.java index 5f64e4029..0775cb71b 100644 --- a/transitime/src/main/java/org/transitime/ipc/interfaces/ServerStatusInterface.java +++ b/transitclock/src/main/java/org/transitclock/ipc/interfaces/ServerStatusInterface.java @@ -15,12 +15,13 @@ * along with Transitime.org . If not, see . */ -package org.transitime.ipc.interfaces; +package org.transitclock.ipc.interfaces; import java.rmi.Remote; import java.rmi.RemoteException; +import java.util.Date; -import org.transitime.ipc.data.IpcServerStatus; +import org.transitclock.ipc.data.IpcServerStatus; /** * RMI interface for determining the status of the server. @@ -49,4 +50,13 @@ public interface ServerStatusInterface extends Remote { */ public String monitor() throws RemoteException; + + /** + * Gets current server time. + * + * + * @return + * @throws RemoteException + */ + public Date getCurrentServerTime() throws RemoteException; } diff --git a/transitime/src/main/java/org/transitime/ipc/interfaces/VehiclesInterface.java b/transitclock/src/main/java/org/transitclock/ipc/interfaces/VehiclesInterface.java similarity index 61% rename from transitime/src/main/java/org/transitime/ipc/interfaces/VehiclesInterface.java rename to transitclock/src/main/java/org/transitclock/ipc/interfaces/VehiclesInterface.java index 7d1640260..ea6d87cfc 100644 --- a/transitime/src/main/java/org/transitime/ipc/interfaces/VehiclesInterface.java +++ b/transitclock/src/main/java/org/transitclock/ipc/interfaces/VehiclesInterface.java @@ -15,17 +15,17 @@ * along with Transitime.org . If not, see . */ -package org.transitime.ipc.interfaces; +package org.transitclock.ipc.interfaces; import java.rmi.Remote; import java.rmi.RemoteException; import java.util.Collection; -import org.transitime.ipc.data.IpcActiveBlock; -import org.transitime.ipc.data.IpcVehicleComplete; -import org.transitime.ipc.data.IpcVehicleGtfsRealtime; -import org.transitime.ipc.data.IpcVehicle; -import org.transitime.ipc.data.IpcVehicleConfig; +import org.transitclock.ipc.data.IpcActiveBlock; +import org.transitclock.ipc.data.IpcVehicle; +import org.transitclock.ipc.data.IpcVehicleComplete; +import org.transitclock.ipc.data.IpcVehicleConfig; +import org.transitclock.ipc.data.IpcVehicleGtfsRealtime; /** * Defines the RMI interface used for obtaining vehicle information. @@ -35,7 +35,55 @@ */ public interface VehiclesInterface extends Remote { - /** + /** + * Gets from the server IpcActiveBlocks for blocks that are currently + * active without vehicle data. + * + * @param routeIds + * List of routes that want data for. Can also be null or empty. + * @param allowableBeforeTimeSecs + * How much before the block time the block is considered to be + * active + * @return Collection of blocks that are active + * @throws RemoteException + */ + public Collection getActiveBlocksWithoutVehicles( + Collection routeIds, int allowableBeforeTimeSecs) + throws RemoteException; + + /** + * Gets from the server IpcActiveBlocks for blocks that are currently + * active with vehicle data for a particular route. + * + * @param routeId + * Route that want data for. Can also be null or empty. + * @param allowableBeforeTimeSecs + * How much before the block time the block is considered to be + * active + * @return Collection of blocks that are active + * @throws RemoteException + */ + public Collection getActiveBlocksAndVehiclesByRouteId( + String routeId, int allowableBeforeTimeSecs) + throws RemoteException; + + /** + * Gets from the server IpcActiveBlocks for blocks that are currently + * active with vehicle data for all routes with given route name. + * + * @param routeName + * Route name that want data for. Can also be null or empty. + * @param allowableBeforeTimeSecs + * How much before the block time the block is considered to be + * active + * @return Collection of blocks that are active + * @throws RemoteException + */ + public Collection getActiveBlocksAndVehiclesByRouteName( + String routeName, int allowableBeforeTimeSecs) + throws RemoteException; + + /** * For getting configuration information for all vehicles. Useful for * determining IDs of all vehicles in system * @@ -116,7 +164,7 @@ public Collection getComplete( Collection vehicleIds) throws RemoteException; /** - * Gets from server IpcVehicle info for all vehicles currently. associated + * Gets from server IpcVehicle info for all vehicles currently associated * with route. * * @param routeIdOrShortName @@ -126,6 +174,16 @@ public Collection getComplete( */ public Collection getForRoute(String routeIdOrShortName) throws RemoteException; + + /** + * Gets from server IpcVehicle info for all vehicles currently associated + * with a block. Each block should have a IpcVehicle in the returned collection. + * + * @return Collection of Vehicle objects + * @throws RemoteException + */ + public Collection getVehiclesForBlocks() + throws RemoteException; /** * Gets from server IpcCompleteVehicle info for all vehicles currently. @@ -178,4 +236,21 @@ public Collection getCompleteForRoute( public Collection getActiveBlocks( Collection routeIds, int allowableBeforeTimeSecs) throws RemoteException; + + /** + * Gets from the server the number of blocks that are currently active. + * + * @param routeIds + * List of routes that want data for. Can also be null or empty. + * @param allowableBeforeTimeSecs + * How much before the block time the block is considered to be + * active + * @return Number of blocks that are active. + * @throws RemoteException + */ + public int getNumActiveBlocks( + Collection routeIds, int allowableBeforeTimeSecs) + throws RemoteException; + + } diff --git a/transitime/src/main/java/org/transitime/ipc/interfaces/package-info.java b/transitclock/src/main/java/org/transitclock/ipc/interfaces/package-info.java similarity index 95% rename from transitime/src/main/java/org/transitime/ipc/interfaces/package-info.java rename to transitclock/src/main/java/org/transitclock/ipc/interfaces/package-info.java index 62d763e22..5a5e77f0a 100644 --- a/transitime/src/main/java/org/transitime/ipc/interfaces/package-info.java +++ b/transitclock/src/main/java/org/transitclock/ipc/interfaces/package-info.java @@ -22,4 +22,4 @@ * @author SkiBu Smith * */ -package org.transitime.ipc.interfaces; \ No newline at end of file +package org.transitclock.ipc.interfaces; \ No newline at end of file diff --git a/transitime/src/main/java/org/transitime/ipc/jms/JMSTest.java b/transitclock/src/main/java/org/transitclock/ipc/jms/JMSTest.java similarity index 97% rename from transitime/src/main/java/org/transitime/ipc/jms/JMSTest.java rename to transitclock/src/main/java/org/transitclock/ipc/jms/JMSTest.java index e6afefd82..480124af1 100644 --- a/transitime/src/main/java/org/transitime/ipc/jms/JMSTest.java +++ b/transitclock/src/main/java/org/transitclock/ipc/jms/JMSTest.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.ipc.jms; +package org.transitclock.ipc.jms; import javax.jms.JMSException; import javax.jms.MessageConsumer; diff --git a/transitime/src/main/java/org/transitime/ipc/jms/JMSWrapper.java b/transitclock/src/main/java/org/transitclock/ipc/jms/JMSWrapper.java similarity index 98% rename from transitime/src/main/java/org/transitime/ipc/jms/JMSWrapper.java rename to transitclock/src/main/java/org/transitclock/ipc/jms/JMSWrapper.java index 48f136571..4fb29e4b9 100644 --- a/transitime/src/main/java/org/transitime/ipc/jms/JMSWrapper.java +++ b/transitclock/src/main/java/org/transitclock/ipc/jms/JMSWrapper.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.ipc.jms; +package org.transitclock.ipc.jms; import java.io.Serializable; @@ -40,7 +40,7 @@ import org.hornetq.api.jms.management.JMSManagementHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.config.StringConfigValue; +import org.transitclock.config.StringConfigValue; /** * This class is a intended to make JMS easy to use. This class @@ -77,7 +77,7 @@ public class JMSWrapper { // Parameter that specifies URL of where to find the hornetq server private static StringConfigValue hornetqServerURL = - new StringConfigValue("transitime.ipc.hornetqServerURL", + new StringConfigValue("transitclock.ipc.hornetqServerURL", "jnp://localhost:1099", "The URL of the Hornet JMS service to use."); public static String getHornetqServerURL() { diff --git a/transitime/src/main/java/org/transitime/ipc/jms/RestartableMessageProducer.java b/transitclock/src/main/java/org/transitclock/ipc/jms/RestartableMessageProducer.java similarity index 99% rename from transitime/src/main/java/org/transitime/ipc/jms/RestartableMessageProducer.java rename to transitclock/src/main/java/org/transitclock/ipc/jms/RestartableMessageProducer.java index 6eb9cb0d4..ca07c0e52 100644 --- a/transitime/src/main/java/org/transitime/ipc/jms/RestartableMessageProducer.java +++ b/transitclock/src/main/java/org/transitclock/ipc/jms/RestartableMessageProducer.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.ipc.jms; +package org.transitclock.ipc.jms; import java.io.Serializable; diff --git a/transitime/src/main/java/org/transitime/ipc/jms/package-info.java b/transitclock/src/main/java/org/transitclock/ipc/jms/package-info.java similarity index 96% rename from transitime/src/main/java/org/transitime/ipc/jms/package-info.java rename to transitclock/src/main/java/org/transitclock/ipc/jms/package-info.java index 723493f7d..351a1b089 100644 --- a/transitime/src/main/java/org/transitime/ipc/jms/package-info.java +++ b/transitclock/src/main/java/org/transitclock/ipc/jms/package-info.java @@ -22,4 +22,4 @@ * @author SkiBu Smith * */ -package org.transitime.ipc.jms; \ No newline at end of file +package org.transitclock.ipc.jms; \ No newline at end of file diff --git a/transitime/src/main/java/org/transitime/ipc/package-info.java b/transitclock/src/main/java/org/transitclock/ipc/package-info.java similarity index 97% rename from transitime/src/main/java/org/transitime/ipc/package-info.java rename to transitclock/src/main/java/org/transitclock/ipc/package-info.java index fd9669a3f..fa3709db9 100644 --- a/transitime/src/main/java/org/transitime/ipc/package-info.java +++ b/transitclock/src/main/java/org/transitclock/ipc/package-info.java @@ -29,4 +29,4 @@ * @author SkiBu Smith * */ -package org.transitime.ipc; \ No newline at end of file +package org.transitclock.ipc; \ No newline at end of file diff --git a/transitime/src/main/java/org/transitime/ipc/rmi/AbstractServer.java b/transitclock/src/main/java/org/transitclock/ipc/rmi/AbstractServer.java similarity index 90% rename from transitime/src/main/java/org/transitime/ipc/rmi/AbstractServer.java rename to transitclock/src/main/java/org/transitclock/ipc/rmi/AbstractServer.java index 36a09cd28..b14551233 100644 --- a/transitime/src/main/java/org/transitime/ipc/rmi/AbstractServer.java +++ b/transitclock/src/main/java/org/transitclock/ipc/rmi/AbstractServer.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.ipc.rmi; +package org.transitclock.ipc.rmi; import java.net.UnknownHostException; import java.rmi.Remote; @@ -26,9 +26,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.configData.AgencyConfig; -import org.transitime.logging.Markers; -import org.transitime.utils.Timer; +import org.transitclock.configData.AgencyConfig; +import org.transitclock.logging.Markers; +import org.transitclock.utils.Timer; /** * This class does all of the work on the server side for an RMI object. A @@ -62,7 +62,7 @@ public abstract class AbstractServer { // Need to store this so can rebind private String bindName; - private final String agencyId; + private String agencyId; // Need to store this so can rebind private Remote stub; @@ -118,20 +118,26 @@ public abstract class AbstractServer { * something like ClassName.class.getSimpleName() */ protected AbstractServer(String agencyId, String objectName) { + start(agencyId, objectName); + } + + protected AbstractServer(){} + + protected void start(String agencyId, String objectName){ this.agencyId = agencyId; - + try { // First make sure that this object is a subclass of Remote // since this class is only intended to be a parent class // of Remote. if (!(this instanceof Remote)) { logger.error("Class {} is not a subclass of Remote. Therefore " - + "it cannot be used with {}", + + "it cannot be used with {}", this.getClass().getName(), getClass().getSimpleName()); return; } Remote remoteThis = (Remote) this; - + logger.info("Setting up AbstractServer for RMI using secondary " + "port={}", RmiParams.getSecondaryRmiPort()); // Export the RMI stub. Specify that should use special port for @@ -142,27 +148,27 @@ protected AbstractServer(String agencyId, String objectName) { // Make sure the registry exists if (registry == null) { try { - // Start up the RMI registry. If it has already been - // started manually or by another process then will + // Start up the RMI registry. If it has already been + // started manually or by another process then will // get an exception, which is fine. LocateRegistry.createRegistry(RmiParams.getRmiPort()); } catch (Exception e) { // Most likely registry was already running logger.debug("Exception occurred when trying to create " + - "RMI registry. Most likely the registry was " + - "already running, which is fine. Message={}", + "RMI registry. Most likely the registry was " + + "already running, which is fine. Message={}", e.getMessage()); } registry = LocateRegistry.getRegistry(RmiParams.getRmiPort()); } - + // Bind the stub to the RMI registry so that it can be accessed by // name by the client. bindName = getBindName(agencyId, objectName); - - // Bind the stub to the RMI registry in a loop so that even if + + // Bind the stub to the RMI registry in a loop so that even if // rmiregistry is restarted the stub will quickly get bound to it. - // rebind() is called immediately and then again every + // rebind() is called immediately and then again every // REBIND_RATE_SEC seconds. rebindTimer.scheduleAtFixedRate( // Call rebind() using anonymous class @@ -171,14 +177,14 @@ public void run() { rebind(); } }, 0, REBIND_RATE_SEC, TimeUnit.SECONDS); - + constructed = true; } catch (Exception e) { // Log the error. Since RMI is critical send out e-mail as well so // that the issue is taken care of. - logger.error(Markers.email(), + logger.error(Markers.email(), "For agencyId={} error occurred when constructing a RMI {}", - AgencyConfig.getAgencyId(), getClass().getSimpleName(), + AgencyConfig.getAgencyId(), getClass().getSimpleName(), e); } } diff --git a/transitime/src/main/java/org/transitime/ipc/rmi/ClientFactory.java b/transitclock/src/main/java/org/transitclock/ipc/rmi/ClientFactory.java similarity index 87% rename from transitime/src/main/java/org/transitime/ipc/rmi/ClientFactory.java rename to transitclock/src/main/java/org/transitclock/ipc/rmi/ClientFactory.java index cbfef133c..092d3e865 100644 --- a/transitime/src/main/java/org/transitime/ipc/rmi/ClientFactory.java +++ b/transitclock/src/main/java/org/transitclock/ipc/rmi/ClientFactory.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.ipc.rmi; +package org.transitclock.ipc.rmi; import java.io.IOException; import java.lang.reflect.Proxy; @@ -30,9 +30,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.config.IntegerConfigValue; -import org.transitime.ipc.rmi.Hello; -import org.transitime.utils.Time; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.config.StringConfigValue; +import org.transitclock.ipc.rmi.Hello; +import org.transitclock.utils.Time; /** * For clients to access RMI based method calls for a remote object simply need @@ -48,11 +49,16 @@ public class ClientFactory { private static Boolean rmiTimeoutEnabled = false; private static IntegerConfigValue timeoutSec = new IntegerConfigValue( - "transitime.rmi.timeoutSec", 4, + "transitclock.rmi.timeoutSec", 4, "Specifies the timeout time in seconds for RMI calls. Note " + "that when an RMI failure occurs a second try is done " + "so total timeout time is twice what is specified here."); + private static StringConfigValue debugRmiServerHost = new StringConfigValue( + "transtime.rmi.debug.rmi.server", + null, + "The RMI server to connect to when in debug mode"); + private static final Logger logger = LoggerFactory .getLogger(ClientFactory.class); @@ -90,7 +96,7 @@ public static T getInstance(String agencyId, // Get the RMI stub. Don't update host name since there is no // indication of a problem with the cached version. Instead, // just use the cached version to reduce db access. - boolean updateHostName = false; + boolean updateHostName = true; T rmiStub = getRmiStub(info, updateHostName); logger.debug("Getting proxy instance..."); @@ -139,9 +145,13 @@ public static T getRmiStub(RmiStubInfo info, String hostName = updateHostName ? info.getHostNameViaUpdatedCache() : info.getHostName(); - logger.debug("Getting RMI registry for hostname={} port={} ...", - hostName, RmiParams.getRmiPort()); + if (debugRmiServerHost.getValue() != null) { + logger.info("using debug RMI server value of {}", debugRmiServerHost.getValue()); + hostName = debugRmiServerHost.getValue(); + } + logger.debug("Getting RMI registry for hostname={} port={} ...", + hostName, RmiParams.getRmiPort()); // Get the registry Registry registry = LocateRegistry.getRegistry(hostName, RmiParams.getRmiPort()); @@ -188,6 +198,10 @@ public Socket createSocket(String host, int port) timeoutSec.getValue() * Time.MS_PER_SEC; socket.setSoTimeout(timeoutMillis); socket.setSoLinger(false, 0); + + if (debugRmiServerHost.getValue() != null) { + host = debugRmiServerHost.getValue(); + } socket.connect(new InetSocketAddress(host, port), timeoutMillis); return socket; diff --git a/transitime/src/main/java/org/transitime/ipc/rmi/Hello.java b/transitclock/src/main/java/org/transitclock/ipc/rmi/Hello.java similarity index 96% rename from transitime/src/main/java/org/transitime/ipc/rmi/Hello.java rename to transitclock/src/main/java/org/transitclock/ipc/rmi/Hello.java index 4e34272c7..8e1606642 100644 --- a/transitime/src/main/java/org/transitime/ipc/rmi/Hello.java +++ b/transitclock/src/main/java/org/transitclock/ipc/rmi/Hello.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.ipc.rmi; +package org.transitclock.ipc.rmi; import java.rmi.Remote; import java.rmi.RemoteException; diff --git a/transitime/src/main/java/org/transitime/ipc/rmi/HelloClient.java b/transitclock/src/main/java/org/transitclock/ipc/rmi/HelloClient.java similarity index 94% rename from transitime/src/main/java/org/transitime/ipc/rmi/HelloClient.java rename to transitclock/src/main/java/org/transitclock/ipc/rmi/HelloClient.java index 67431168a..c5684127c 100644 --- a/transitime/src/main/java/org/transitime/ipc/rmi/HelloClient.java +++ b/transitclock/src/main/java/org/transitclock/ipc/rmi/HelloClient.java @@ -14,12 +14,13 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.ipc.rmi; +package org.transitclock.ipc.rmi; import java.rmi.RemoteException; import java.util.concurrent.ScheduledThreadPoolExecutor; -import org.transitime.utils.Time; -import org.transitime.utils.threading.NamedThreadFactory; + +import org.transitclock.utils.Time; +import org.transitclock.utils.threading.NamedThreadFactory; /** * Sample test program to show how a client can do RMI calls. diff --git a/transitime/src/main/java/org/transitime/ipc/rmi/HelloServer.java b/transitclock/src/main/java/org/transitclock/ipc/rmi/HelloServer.java similarity index 92% rename from transitime/src/main/java/org/transitime/ipc/rmi/HelloServer.java rename to transitclock/src/main/java/org/transitclock/ipc/rmi/HelloServer.java index 8dce8da39..d574b1b35 100644 --- a/transitime/src/main/java/org/transitime/ipc/rmi/HelloServer.java +++ b/transitclock/src/main/java/org/transitclock/ipc/rmi/HelloServer.java @@ -14,11 +14,11 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.ipc.rmi; +package org.transitclock.ipc.rmi; import java.rmi.RemoteException; -import org.transitime.utils.Time; +import org.transitclock.utils.Time; /** * A sample RMI server class. Shows how RMI server class can be implemented. @@ -51,7 +51,7 @@ static public HelloServer getInstance(String projectId) { } /* (non-Javadoc) - * @see org.transitime.rmi.Hello#concat(java.lang.String, java.lang.String) + * @see org.transitclock.rmi.Hello#concat(java.lang.String, java.lang.String) */ @Override public String concat(String s1, String s2) throws RemoteException { diff --git a/transitime/src/main/java/org/transitime/ipc/rmi/RmiCallInvocationHandler.java b/transitclock/src/main/java/org/transitclock/ipc/rmi/RmiCallInvocationHandler.java similarity index 90% rename from transitime/src/main/java/org/transitime/ipc/rmi/RmiCallInvocationHandler.java rename to transitclock/src/main/java/org/transitclock/ipc/rmi/RmiCallInvocationHandler.java index f21cbc069..bb8bb15e3 100644 --- a/transitime/src/main/java/org/transitime/ipc/rmi/RmiCallInvocationHandler.java +++ b/transitclock/src/main/java/org/transitclock/ipc/rmi/RmiCallInvocationHandler.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.ipc.rmi; +package org.transitclock.ipc.rmi; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; @@ -23,9 +23,13 @@ import java.rmi.RemoteException; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.utils.IntervalTimer; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.utils.IntervalTimer; /** * This code is called on the client side when RMI object is accessed. @@ -52,11 +56,11 @@ public class RmiCallInvocationHandler implements InvocationHandler { private final RmiStubInfo info; // For keeping track of number of total and current RMI calls. - // Don't actually need to use Atomic variables since - // not doing a change & get. + // Java's increment does a change & get behind the scenes so + // we need to worry about thread safety here! private static class Counts { - long total = 0L; - int current = 0; + AtomicLong total = new AtomicLong(0L); + AtomicInteger current = new AtomicInteger(0); } // For limiting how many RMI calls are in process for a particular @@ -67,8 +71,10 @@ private static class Counts { private static final ConcurrentHashMap currentCallsByAgencyMap = new ConcurrentHashMap(); - // Set default value to 25 - private static int maxConcurrentCallsPerProject = 25; + private static IntegerConfigValue maxConcurrentCallsPerProjectConfig = new IntegerConfigValue( + "transitclock.usage.maxRmiCalls", 100000, + "Maximum number of concurrent RMI calls to allow"); + private static int maxConcurrentCallsPerProject = maxConcurrentCallsPerProjectConfig.getValue().intValue(); // Logging private static final Logger logger = @@ -125,7 +131,7 @@ public static Set getAgencies() { * @return */ public static int getCount(String agencyId) { - return getAccessCounter(agencyId).current; + return getAccessCounter(agencyId).current.get(); } /** @@ -136,7 +142,7 @@ public static int getCount(String agencyId) { * @return */ public static long getTotalCount(String agencyId) { - return getAccessCounter(agencyId).total; + return getAccessCounter(agencyId).total.get(); } /** @@ -264,7 +270,7 @@ public Object invoke(Object proxy, Method method, Object[] args) + "Is the Java system property java.rmi.server.hostname " + "set to the proper host name? " + "Is the Java system " - + "property transitime.rmi.timeoutSec timeout time of " + + "property transitclock.rmi.timeoutSec timeout time of " + ClientFactory.getTimeoutSec() + " seconds adequate?"; logger.error(message); @@ -305,8 +311,8 @@ private Object invokeIfNotTooMuchConcurrentAccess(Method method, Object[] args) // burden the project even more with additional calls. // Therefore when behind want to return as quickly as possible. Counts accessCounter = getAccessCounter(info.getAgencyId()); - accessCounter.total++; - if (accessCounter.current >= getMaxConcurrentCallsPerProject()) { + accessCounter.total.incrementAndGet(); + if (accessCounter.current.get() >= getMaxConcurrentCallsPerProject()) { // Currently too many RMI calls is progress so log error // and throw exception String message = "Reached MAX_CURRENT_CALLS_PER_PROJECT=" @@ -320,14 +326,14 @@ private Object invokeIfNotTooMuchConcurrentAccess(Method method, Object[] args) } else { try { // Keep track that another RMI call is being initiated - accessCounter.current++; + accessCounter.current.incrementAndGet(); // Actually make the RMI call Object result = lowLevelInvoke(method, args); return result; } finally { // Make sure that access counter decrement no matter what - accessCounter.current--; + accessCounter.current.decrementAndGet(); } } } diff --git a/transitime/src/main/java/org/transitime/ipc/rmi/RmiParams.java b/transitclock/src/main/java/org/transitclock/ipc/rmi/RmiParams.java similarity index 94% rename from transitime/src/main/java/org/transitime/ipc/rmi/RmiParams.java rename to transitclock/src/main/java/org/transitclock/ipc/rmi/RmiParams.java index 79b6ef9c8..e6bc076e0 100644 --- a/transitime/src/main/java/org/transitime/ipc/rmi/RmiParams.java +++ b/transitclock/src/main/java/org/transitclock/ipc/rmi/RmiParams.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.ipc.rmi; +package org.transitclock.ipc.rmi; -import org.transitime.configData.RmiConfig; +import org.transitclock.configData.RmiConfig; /** * For defining port numbers to be used for RMI diff --git a/transitime/src/main/java/org/transitime/ipc/rmi/RmiStubInfo.java b/transitclock/src/main/java/org/transitclock/ipc/rmi/RmiStubInfo.java similarity index 88% rename from transitime/src/main/java/org/transitime/ipc/rmi/RmiStubInfo.java rename to transitclock/src/main/java/org/transitclock/ipc/rmi/RmiStubInfo.java index 5a42ff567..824597753 100644 --- a/transitime/src/main/java/org/transitime/ipc/rmi/RmiStubInfo.java +++ b/transitclock/src/main/java/org/transitclock/ipc/rmi/RmiStubInfo.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.ipc.rmi; +package org.transitclock.ipc.rmi; -import org.transitime.configData.RmiConfig; -import org.transitime.db.webstructs.WebAgency; -import org.transitime.utils.Time; +import org.transitclock.configData.RmiConfig; +import org.transitclock.db.webstructs.WebAgency; +import org.transitclock.utils.Time; /** * Contains the info needed for creating an RMI stub, including the agency ID * and the agency host name. The hostname is obtained from the Java property - * transitime.rmi.rmiHost if set. Otherwise gets the host names from the + * transitclock.rmi.rmiHost if set. Otherwise gets the host names from the * WebAgency database. *

* This information exists on the client side. @@ -53,7 +53,7 @@ public String getClassName() { /** * Returns the RMI hostname. Will use command line parameter - * -Dtransitime.core.rmiHost if it is set. If not set then looks in + * -Dtransitclock.core.rmiHost if it is set. If not set then looks in * WebAgencies table in the web database. Returns null if not configured. * * @param rereadIfOlderThanMsecs @@ -84,7 +84,7 @@ private String getHostName(int rereadIfOlderThanMsecs) { /** * Returns the RMI hostname that is cached. Will not update the cache even * if agency not in cache and cache not updated for a long time. Will use - * command line parameter -Dtransitime.core.rmiHost if it is set. If not set + * command line parameter -Dtransitclock.core.rmiHost if it is set. If not set * then looks in cached version of WebAgencies table from the web database. * Returns null if agency not configured. * @@ -98,7 +98,7 @@ public String getHostName() { * Intended for when communication was working but now is not in that this * method will get updated hostname data from db if cache is somewhat old. * Returns the RMI hostname. Will use command line parameter - * -Dtransitime.core.rmiHost if it is set. If not set then looks in cached + * -Dtransitclock.core.rmiHost if it is set. If not set then looks in cached * version of WebAgencies table from the web database. If cached data is * more than 30 seconds old then will reread WebAgency data from db. Returns * null if agency not configured. diff --git a/transitime/src/main/java/org/transitime/ipc/rmi/package-info.java b/transitclock/src/main/java/org/transitclock/ipc/rmi/package-info.java similarity index 94% rename from transitime/src/main/java/org/transitime/ipc/rmi/package-info.java rename to transitclock/src/main/java/org/transitclock/ipc/rmi/package-info.java index 3ea74d422..02674e0c8 100644 --- a/transitime/src/main/java/org/transitime/ipc/rmi/package-info.java +++ b/transitclock/src/main/java/org/transitclock/ipc/rmi/package-info.java @@ -16,7 +16,7 @@ */ /** - The org.transitime.ipc.rmi package is for making it easy to have remote RMI + The org.transitclock.ipc.rmi package is for making it easy to have remote RMI access to objects on the server. In particular, it is intended for "command" type of objects that do not change. They are simply used for changing state of a server or reading information from it. @@ -30,13 +30,13 @@ C:/Program Files/Java/jdk1.7.0_25/bin/rmiregistry.exe . With changes to the RMI system with version 1.7_20 of Java you need to specify the codebase if you run the rmiregistry manually. It would be started using something like: - /usr/lib/jvm/jre/bin/rmiregistry -J-Djava.rmi.server.codebase=file:/home/ec2-user/jars/transitime.jar 2099 & + /usr/lib/jvm/jre/bin/rmiregistry -J-Djava.rmi.server.codebase=file:/home/ec2-user/jars/transitclock.jar 2099 &

AbstractServer configures RMI to not use port 0, basically a random port, when establishing communication (the main port 2099 is used to establish communication but then communication is handed off to a separate port). Instead of using port 0 for actually sending commands to a server, RMI is configured to use a - special port via the Java property -Dtransitime.rmi.secondaryRmiPort. + special port via the Java property -Dtransitclock.rmi.secondaryRmiPort. The default value is 2098. In this way the firewalls for the servers can be configured to be open just for the ports that are actually used, which is much safer than having to completely @@ -44,7 +44,7 @@ establishing communication (the main port 2099 is used to establish communicatio secondary port for communication. This means that if you have multiple core systems running on a server only one can use the default secondary RMI port. The other core systems will need to have a different port specified by - -Dtransitime.rmi.secondaryRmiPort . + -Dtransitclock.rmi.secondaryRmiPort .

An important issue is that when running on AWS the RMI server needs to be run using Java property -Djava.rmi.server.hostname=PUBLIC_DOMAIN.amazonaws.com . @@ -92,4 +92,4 @@

  • For the client create an object such as (for the "Hello" interface class in @author SkiBu Smith */ -package org.transitime.ipc.rmi; \ No newline at end of file +package org.transitclock.ipc.rmi; \ No newline at end of file diff --git a/transitclock/src/main/java/org/transitclock/ipc/servers/CacheQueryServer.java b/transitclock/src/main/java/org/transitclock/ipc/servers/CacheQueryServer.java new file mode 100755 index 000000000..3bdb48572 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/servers/CacheQueryServer.java @@ -0,0 +1,222 @@ +/** + * + */ +package org.transitclock.ipc.servers; + +import java.rmi.RemoteException; +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.core.dataCache.ErrorCacheFactory; +import org.transitclock.core.dataCache.HistoricalAverage; +import org.transitclock.core.dataCache.HoldingTimeCache; +import org.transitclock.core.dataCache.HoldingTimeCacheKey; +import org.transitclock.core.dataCache.IpcArrivalDepartureComparator; +import org.transitclock.core.dataCache.KalmanErrorCacheKey; +import org.transitclock.core.dataCache.StopArrivalDepartureCacheFactory; +import org.transitclock.core.dataCache.StopArrivalDepartureCacheKey; +import org.transitclock.core.dataCache.TripKey; +import org.transitclock.core.dataCache.frequency.FrequencyBasedHistoricalAverageCache; +import org.transitclock.core.dataCache.scheduled.ScheduleBasedHistoricalAverageCache; +import org.transitclock.core.dataCache.StopPathCacheKey; +import org.transitclock.core.dataCache.TripDataHistoryCacheFactory; +import org.transitclock.ipc.data.IpcArrivalDeparture; +import org.transitclock.ipc.data.IpcHistoricalAverage; +import org.transitclock.ipc.data.IpcHistoricalAverageCacheKey; +import org.transitclock.ipc.data.IpcHoldingTimeCacheKey; +import org.transitclock.ipc.data.IpcKalmanErrorCacheKey; +import org.transitclock.ipc.interfaces.CacheQueryInterface; +import org.transitclock.ipc.rmi.AbstractServer; + +/** + * @author Sean Og Crudden Server to allow cache content to be queried. + */ +public class CacheQueryServer extends AbstractServer implements CacheQueryInterface { + // Should only be accessed as singleton class + private static CacheQueryServer singleton; + + private static final Logger logger = LoggerFactory.getLogger(CacheQueryServer.class); + + protected CacheQueryServer(String agencyId) { + super(agencyId, CacheQueryInterface.class.getSimpleName()); + + } + + /** + * Starts up the CacheQueryServer so that RMI calls can be used to query + * cache. This will automatically cause the object to continue to run and + * serve requests. + * + * @param agencyId + * @return the singleton CacheQueryServer object. Usually does not need to + * used since the server will be fully running. + */ + public static CacheQueryServer start(String agencyId) { + if (singleton == null) { + singleton = new CacheQueryServer(agencyId); + } + + if (!singleton.getAgencyId().equals(agencyId)) { + logger.error( + "Tried calling CacheQueryServer.start() for " + + "agencyId={} but the singleton was created for agencyId={}", + agencyId, singleton.getAgencyId()); + return null; + } + + return singleton; + } + + /* + * (non-Javadoc) + * + * @see org.transitclock.ipc.interfaces.CacheQueryInterface# + * getStopArrivalDepartures(java.lang.String) + */ + @Override + public List getStopArrivalDepartures(String stopId) throws RemoteException { + + try { + StopArrivalDepartureCacheKey nextStopKey = new StopArrivalDepartureCacheKey(stopId, + Calendar.getInstance().getTime()); + + List result = StopArrivalDepartureCacheFactory.getInstance().getStopHistory(nextStopKey); + + return result; + + } catch (Exception e) { + + throw new RemoteException(e.toString(),e); + } + } + + @Override + public Integer entriesInCache(String cacheName) throws RemoteException { + + // TODO Auto-generated method stub + return -1; + + } + + @Override + public IpcHistoricalAverage getHistoricalAverage(String tripId, Integer stopPathIndex) throws RemoteException { + StopPathCacheKey key = new StopPathCacheKey(tripId, stopPathIndex); + + HistoricalAverage average = ScheduleBasedHistoricalAverageCache.getInstance().getAverage(key); + return new IpcHistoricalAverage(average); + } + + @Override + public List getTripArrivalDepartures(String tripId, LocalDate localDate, Integer starttime) + throws RemoteException { + + try { + List result = new ArrayList(); + + if(tripId!=null && localDate!=null && starttime!=null){ + + Date date = Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant()); + TripKey tripKey = new TripKey(tripId, date, starttime); + + result = TripDataHistoryCacheFactory.getInstance().getTripHistory(tripKey); + } + else if(tripId!=null && localDate!=null && starttime==null) + { + Date date = Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant()); + for(TripKey key:TripDataHistoryCacheFactory.getInstance().getKeys()) + { + if(key.getTripId().equals(tripId) && date.compareTo(key.getTripStartDate())==0) + { + result.addAll(TripDataHistoryCacheFactory.getInstance().getTripHistory(key)); + } + } + }else if(tripId!=null && localDate==null && starttime==null) + { + for(TripKey key:TripDataHistoryCacheFactory.getInstance().getKeys()) + { + if(key.getTripId().equals(tripId)) + { + result.addAll(TripDataHistoryCacheFactory.getInstance().getTripHistory(key)); + } + } + } + else if(tripId==null && localDate!=null && starttime==null) + { + Date date = Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant()); + for(TripKey key:TripDataHistoryCacheFactory.getInstance().getKeys()) + { + if(date.compareTo(key.getTripStartDate())==0) + { + result.addAll(TripDataHistoryCacheFactory.getInstance().getTripHistory(key)); + } + } + } + + Collections.sort(result, new IpcArrivalDepartureComparator()); + + return result; + + } catch (Exception e) { + + throw new RemoteException(e.toString(), e); + } + } + + @Override + public List getScheduledBasedHistoricalAverageCacheKeys() throws RemoteException { + + List keys = ScheduleBasedHistoricalAverageCache.getInstance().getKeys(); + List ipcResultList = new ArrayList(); + + for(StopPathCacheKey key:keys) + { + ipcResultList.add(new IpcHistoricalAverageCacheKey(key)); + } + return ipcResultList; + } + + @Override + public Double getKalmanErrorValue(String tripId, Integer stopPathIndex) throws RemoteException { + KalmanErrorCacheKey key=new KalmanErrorCacheKey(tripId, stopPathIndex); + Double result = ErrorCacheFactory.getInstance().getErrorValue(key).getError(); + return result; + } + + @Override + public List getKalmanErrorCacheKeys() throws RemoteException { + List keys = ErrorCacheFactory.getInstance().getKeys(); + List ipcResultList = new ArrayList(); + + for(KalmanErrorCacheKey key:keys) + { + ipcResultList.add(new IpcKalmanErrorCacheKey(key)); + } + return ipcResultList; + } + + @Override + public List getHoldingTimeCacheKeys() throws RemoteException { + List keys = HoldingTimeCache.getInstance().getKeys(); + List ipcResultList = new ArrayList(); + + for(HoldingTimeCacheKey key:keys) + { + ipcResultList.add(new IpcHoldingTimeCacheKey(key)); + } + return ipcResultList; + } + + @Override + public List getFrequencyBasedHistoricalAverageCacheKeys() throws RemoteException { + // TODO Auto-generated method stub + FrequencyBasedHistoricalAverageCache.getInstance(); + return null; + } +} diff --git a/transitclock/src/main/java/org/transitclock/ipc/servers/CommandsServer.java b/transitclock/src/main/java/org/transitclock/ipc/servers/CommandsServer.java new file mode 100644 index 000000000..9b4277091 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/servers/CommandsServer.java @@ -0,0 +1,216 @@ +package org.transitclock.ipc.servers; + +import java.rmi.RemoteException; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.Collection; +import java.util.Date; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.avl.AvlExecutor; +import org.transitclock.core.AvlProcessor; +import org.transitclock.core.TemporalMatch; +import org.transitclock.core.VehicleState; +import org.transitclock.core.dataCache.PredictionDataCache; +import org.transitclock.core.dataCache.VehicleDataCache; +import org.transitclock.core.dataCache.VehicleStateManager; +import org.transitclock.db.structs.AvlReport; +import org.transitclock.db.structs.VehicleEvent; +import org.transitclock.ipc.data.IpcAvl; +import org.transitclock.ipc.data.IpcVehicleComplete; +import org.transitclock.ipc.interfaces.CommandsInterface; +import org.transitclock.ipc.rmi.AbstractServer; + +public class CommandsServer extends AbstractServer + implements CommandsInterface { + + // Should only be accessed as singleton class + private static CommandsServer singleton; + + private static final Logger logger = + LoggerFactory.getLogger(CommandsServer.class); + + /********************** Member Functions **************************/ + + /** + * Starts up the CommandsServer so that RMI calls can be used to control the + * server. This will automatically cause the object to continue to run and + * serve requests. + * + * @param agencyId + * @return the singleton CommandsServer object. Usually does not need to + * used since the server will be fully running. + */ + public static CommandsServer start(String agencyId) { + if (singleton == null) { + singleton = new CommandsServer(agencyId); + } + + if (!singleton.getAgencyId().equals(agencyId)) { + logger.error("Tried calling CommandsServer.start() for " + + "agencyId={} but the singleton was created for agencyId={}", + agencyId, singleton.getAgencyId()); + return null; + } + + return singleton; + } + + /** + * Constructor. Made private so that can only be instantiated by + * get(). Doesn't actually do anything since all the work is done in + * the superclass constructor. + * + * @param agencyId + * for registering this object with the rmiregistry + */ + private CommandsServer(String agencyId) { + super(agencyId, CommandsInterface.class.getSimpleName()); + } + + /** + * Called on server side via RMI when AVL data is to be processed + * + * @param avlData + * AVL data sent to server + * @return Null if OK, otherwise an error message + */ + @Override + public String pushAvl(IpcAvl avlData) throws RemoteException { + // Use AvlExecutor to actually process the data using a thread executor + AvlReport avlReport = new AvlReport(avlData); + logger.debug("Processing AVL report {}", avlReport); + AvlExecutor.getInstance().processAvlReport(avlReport); + + // Return that was successful + return null; + } + + /** + * Called on server side via RMI when AVL data is to be processed + * + * @param avlDataCollection + * AVL data sent to server + * @return Null if OK, otherwise an error message + */ + @Override + public String pushAvl(Collection avlDataCollection) throws RemoteException { + for (IpcAvl avlData : avlDataCollection) { + // Use AvlExecutor to actually process the data using a thread executor + AvlReport avlReport = new AvlReport(avlData); + logger.debug("Processing AVL report {}", avlReport); + AvlExecutor.getInstance().processAvlReport(avlReport); + } + + // Return that was successful + return null; + } + + @Override + public void setVehicleUnpredictable(String vehicleId) throws RemoteException { + + VehicleState vehicleState = VehicleStateManager.getInstance() + .getVehicleState(vehicleId); + + // Create a VehicleEvent to record what happened + AvlReport avlReport = vehicleState.getAvlReport(); + TemporalMatch lastMatch = vehicleState.getMatch(); + boolean wasPredictable = vehicleState.isPredictable(); + + String vehicleEvent="Command called to make vehicleId unpredicable. "; + String eventDescription="Command called to make vehicleId unpredicable. ";; + VehicleEvent.create(avlReport, lastMatch, vehicleEvent, + eventDescription, false, // predictable + wasPredictable, // becameUnpredictable + null); // supervisor + + // Update the state of the vehicle + vehicleState.setMatch(null); + + // Remove the predictions that were generated by the vehicle + PredictionDataCache.getInstance().removePredictions(vehicleState); + + // Update VehicleDataCache with the new state for the vehicle + VehicleDataCache.getInstance().updateVehicle(vehicleState); + + } + private VehicleState getVehicleStateForTrip(String tripId, LocalDateTime _startTripTime) + { + /** + * The startTripTime parameter should not be null if noSchedule + */ + long startTripTime=0; + if(_startTripTime!=null) + startTripTime=_startTripTime.atZone(ZoneId.systemDefault()).toEpochSecond()*1000L; + /** + * Get the vehicle assosiated to the tripId. + * Is it possible to have more than 1 bus with the same tripId?? + */ + Collection ipcVehicleCompletList = VehicleDataCache.getInstance().getVehiclesIncludingSchedBasedOnes(); + VehicleState vehicleState=null; + for(IpcVehicleComplete _ipcVehicle:ipcVehicleCompletList) + { + + + if(_ipcVehicle.getTripId()!=null && _ipcVehicle.getTripId().compareTo(tripId)==0) + { + VehicleState _vehicleState = VehicleStateManager.getInstance() + .getVehicleState(_ipcVehicle.getId()); + boolean noSchedule=_vehicleState.getTrip().isNoSchedule(); + if(!noSchedule) + { + vehicleState=_vehicleState; + break; + } + else if(noSchedule && _ipcVehicle.getTripStartEpochTime()==startTripTime) + { + vehicleState=_vehicleState; + break; + } + + } + } + return vehicleState; + } + @Override + public String cancelTrip(String tripId, LocalDateTime startTripTime) { + + //String vehicleId= "block_" + blockId + "_schedBasedVehicle"; + VehicleState vehicleState=this.getVehicleStateForTrip(tripId, startTripTime); + if(vehicleState==null) + return "TripId id is not currently available"; + + AvlReport avlReport=vehicleState.getAvlReport(); + if(avlReport!=null) + { + vehicleState.setCanceled(true); + VehicleDataCache.getInstance().updateVehicle(vehicleState); + AvlProcessor.getInstance().processAvlReport(avlReport); + return null; + } + else + return "vehicle with this trip id does not have avl report"; + + + } + @Override + public String reenableTrip(String tripId, LocalDateTime startTripTime) { + + //String vehicleId= "block_" + blockId + "_schedBasedVehicle"; + VehicleState vehicleState=this.getVehicleStateForTrip(tripId, startTripTime); + if(vehicleState==null) + return "TripId id is not currently available"; + AvlReport avlReport=vehicleState.getAvlReport(); + if(avlReport!=null) + { + vehicleState.setCanceled(false); + VehicleDataCache.getInstance().updateVehicle(vehicleState); + AvlProcessor.getInstance().processAvlReport(avlReport); + return null; + } + else + return "vehicle with this trip id does not have avl report"; + } + +} diff --git a/transitime/src/main/java/org/transitime/ipc/servers/ConfigServer.java b/transitclock/src/main/java/org/transitclock/ipc/servers/ConfigServer.java similarity index 69% rename from transitime/src/main/java/org/transitime/ipc/servers/ConfigServer.java rename to transitclock/src/main/java/org/transitclock/ipc/servers/ConfigServer.java index 38bfd50d8..c736eab08 100644 --- a/transitime/src/main/java/org/transitime/ipc/servers/ConfigServer.java +++ b/transitclock/src/main/java/org/transitclock/ipc/servers/ConfigServer.java @@ -15,7 +15,17 @@ * along with Transitime.org . If not, see . */ -package org.transitime.ipc.servers; +package org.transitclock.ipc.servers; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.applications.Core; +import org.transitclock.core.dataCache.VehicleDataCache; +import org.transitclock.db.structs.*; +import org.transitclock.gtfs.DbConfig; +import org.transitclock.ipc.data.*; +import org.transitclock.ipc.interfaces.ConfigInterface; +import org.transitclock.ipc.rmi.AbstractServer; import java.rmi.RemoteException; import java.util.ArrayList; @@ -23,29 +33,6 @@ import java.util.HashSet; import java.util.List; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.transitime.applications.Core; -import org.transitime.core.dataCache.VehicleDataCache; -import org.transitime.db.structs.Agency; -import org.transitime.db.structs.Block; -import org.transitime.db.structs.Calendar; -import org.transitime.db.structs.Route; -import org.transitime.db.structs.Trip; -import org.transitime.db.structs.TripPattern; -import org.transitime.db.structs.VehicleConfig; -import org.transitime.gtfs.DbConfig; -import org.transitime.ipc.data.IpcBlock; -import org.transitime.ipc.data.IpcCalendar; -import org.transitime.ipc.data.IpcRoute; -import org.transitime.ipc.data.IpcRouteSummary; -import org.transitime.ipc.data.IpcDirectionsForRoute; -import org.transitime.ipc.data.IpcSchedule; -import org.transitime.ipc.data.IpcTrip; -import org.transitime.ipc.data.IpcTripPattern; -import org.transitime.ipc.interfaces.ConfigInterface; -import org.transitime.ipc.rmi.AbstractServer; - /** * Implements ConfigInterface to serve up configuration information to RMI * clients. @@ -120,19 +107,19 @@ private Route getRoute(String routeIdOrShortName) { } /* (non-Javadoc) - * @see org.transitime.ipc.interfaces.ConfigInterface#getRoutes() + * @see org.transitclock.ipc.interfaces.ConfigInterface#getRoutes() */ @Override public Collection getRoutes() throws RemoteException { // Get the db route info DbConfig dbConfig = Core.getInstance().getDbConfig(); - Collection dbRoutes = + Collection dbRoutes = dbConfig.getRoutes(); // Convert the db routes into ipc routes Collection ipcRoutes = new ArrayList(dbRoutes.size()); - for (org.transitime.db.structs.Route dbRoute : dbRoutes) { + for (org.transitclock.db.structs.Route dbRoute : dbRoutes) { IpcRouteSummary ipcRoute = new IpcRouteSummary(dbRoute); ipcRoutes.add(ipcRoute); } @@ -141,8 +128,9 @@ public Collection getRoutes() throws RemoteException { return ipcRoutes; } + /* (non-Javadoc) - * @see org.transitime.ipc.interfaces.ConfigInterface#getRoute(java.lang.String) + * @see org.transitclock.ipc.interfaces.ConfigInterface#getRoute(java.lang.String) */ @Override public IpcRoute getRoute(String routeIdOrShortName, String directionId, @@ -160,7 +148,7 @@ public IpcRoute getRoute(String routeIdOrShortName, String directionId, /* * (non-Javadoc) - * @see org.transitime.ipc.interfaces.ConfigInterface#getRoutes(java.util.List) + * @see org.transitclock.ipc.interfaces.ConfigInterface#getRoutes(java.util.List) */ @Override public List getRoutes(List routeIdsOrShortNames) @@ -170,7 +158,7 @@ public List getRoutes(List routeIdsOrShortNames) // If no route specified then return data for all routes if (routeIdsOrShortNames == null || routeIdsOrShortNames.isEmpty()) { DbConfig dbConfig = Core.getInstance().getDbConfig(); - List dbRoutes = + List dbRoutes = dbConfig.getRoutes(); for (Route dbRoute : dbRoutes) { IpcRoute ipcRoute = new IpcRoute(dbRoute, null, null, null); @@ -193,7 +181,23 @@ public List getRoutes(List routeIdsOrShortNames) } /* (non-Javadoc) - * @see org.transitime.ipc.interfaces.ConfigInterface#getStops(java.lang.String) + * @see org.transitclock.ipc.interfaces.ConfigInterface#getRoutesForStop(java.lang.String) + */ + @Override + public List getRoutesForStop(String stopId) throws RemoteException{ + List routes = new ArrayList<>(); + DbConfig dbConfig = Core.getInstance().getDbConfig(); + Collection routesForStop = dbConfig.getRoutesForStop(stopId); + if(routesForStop != null){ + for(Route route : routesForStop){ + routes.add(new IpcRoute(route, null, null, null)); + } + } + return routes; + } + + /* (non-Javadoc) + * @see org.transitclock.ipc.interfaces.ConfigInterface#getStops(java.lang.String) */ @Override public IpcDirectionsForRoute getStops(String routeIdOrShortName) @@ -211,7 +215,45 @@ public IpcDirectionsForRoute getStops(String routeIdOrShortName) } /* (non-Javadoc) - * @see org.transitime.ipc.interfaces.ConfigInterface#getBlock(java.lang.String, java.lang.String) + * @see org.transitclock.ipc.interfaces.ConfigInterface#getStops(java.util.List) + */ + @Override + public List getStops(List routeIdsOrShortNames) + throws RemoteException{ + List routes = new ArrayList<>(); + + // If no route specified then return data for all routes + if (routeIdsOrShortNames == null || routeIdsOrShortNames.isEmpty()) { + DbConfig dbConfig = Core.getInstance().getDbConfig(); + List dbRoutes = + dbConfig.getRoutes(); + for (Route dbRoute : dbRoutes) { + routes.add(dbRoute); + } + } else { + // Routes specified so return data for those routes + for (String routeIdOrShortName : routeIdsOrShortNames) { + // Determine the route + Route dbRoute = getRoute(routeIdOrShortName); + if (dbRoute == null) + continue; + routes.add(dbRoute); + } + } + + List ipcStopsForRoutes = new ArrayList<>(); + + // Convert db route into an ipc route + for(Route dbRoute : routes){ + ipcStopsForRoutes.add(new IpcDirectionsForRoute(dbRoute)); + } + + // Return the ipc routes list + return ipcStopsForRoutes; + } + + /* (non-Javadoc) + * @see org.transitclock.ipc.interfaces.ConfigInterface#getBlock(java.lang.String, java.lang.String) */ @Override public IpcBlock getBlock(String blockId, String serviceId) @@ -227,7 +269,7 @@ public IpcBlock getBlock(String blockId, String serviceId) } /* (non-Javadoc) - * @see org.transitime.ipc.interfaces.ConfigInterface#getBlocks(java.lang.String) + * @see org.transitclock.ipc.interfaces.ConfigInterface#getBlocks(java.lang.String) */ @Override public Collection getBlocks(String blockId) @@ -249,7 +291,7 @@ public Collection getBlocks(String blockId) } /* (non-Javadoc) - * @see org.transitime.ipc.interfaces.ConfigInterface#getTrip(java.lang.String) + * @see org.transitclock.ipc.interfaces.ConfigInterface#getTrip(java.lang.String) */ @Override public IpcTrip getTrip(String tripId) throws RemoteException { @@ -271,7 +313,7 @@ public IpcTrip getTrip(String tripId) throws RemoteException { } /* (non-Javadoc) - * @see org.transitime.ipc.interfaces.ConfigInterface#getTripPattern(java.lang.String) + * @see org.transitclock.ipc.interfaces.ConfigInterface#getTripPattern(java.lang.String) */ @Override public List getTripPatterns(String routeIdOrShortName) @@ -294,8 +336,29 @@ public List getTripPatterns(String routeIdOrShortName) return tripPatterns; } + @Override + public List getTripPatterns(String routeIdOrShortName, String headSign) + throws RemoteException { + DbConfig dbConfig = Core.getInstance().getDbConfig(); + + Route dbRoute = getRoute(routeIdOrShortName); + if (dbRoute == null) + return null; + + List dbTripPatterns = + dbConfig.getTripPatternsForRouteAndHeadSign(dbRoute.getId(), headSign); + if (dbTripPatterns == null) + return null; + + List tripPatterns = new ArrayList(); + for (TripPattern dbTripPattern : dbTripPatterns) { + tripPatterns.add(new IpcTripPattern(dbTripPattern)); + } + return tripPatterns; + } + /* (non-Javadoc) - * @see org.transitime.ipc.interfaces.ConfigInterface#getAgencies() + * @see org.transitclock.ipc.interfaces.ConfigInterface#getAgencies() */ @Override public List getAgencies() throws RemoteException { @@ -303,7 +366,7 @@ public List getAgencies() throws RemoteException { } /* (non-Javadoc) - * @see org.transitime.ipc.interfaces.ConfigInterface#getSchedules(java.lang.String) + * @see org.transitclock.ipc.interfaces.ConfigInterface#getSchedules(java.lang.String) */ @Override public List getSchedules(String routeIdOrShortName) @@ -322,9 +385,27 @@ public List getSchedules(String routeIdOrShortName) IpcSchedule.createSchedules(dbRoute, blocksForRoute); return ipcSchedules; } + + /* (non-Javadoc) + * @see org.transitclock.ipc.interfaces.ConfigInterface#getSchedulesForTrip(java.lang.String) + */ + @Override + public List getSchedulesForTrip(String tripId) + throws RemoteException { + // Determine the trip + Trip trip = Core.getInstance().getDbConfig() + .getTrip(tripId); + if (trip == null) + return null; + + // Convert trip to list of IpcSchedule objects and return + List ipcSchedules = + IpcSchedule.createScheduleForTrip(trip); + return ipcSchedules; + } /* (non-Javadoc) - * @see org.transitime.ipc.interfaces.ConfigInterface#getCurrentCalendars() + * @see org.transitclock.ipc.interfaces.ConfigInterface#getCurrentCalendars() */ @Override public List getCurrentCalendars() { @@ -342,7 +423,7 @@ public List getCurrentCalendars() { } /* (non-Javadoc) - * @see org.transitime.ipc.interfaces.ConfigInterface#getAllCalendars() + * @see org.transitclock.ipc.interfaces.ConfigInterface#getAllCalendars() */ @Override public List getAllCalendars() { @@ -360,7 +441,7 @@ public List getAllCalendars() { } /* (non-Javadoc) - * @see org.transitime.ipc.interfaces.ConfigInterface#getVehicleIds() + * @see org.transitclock.ipc.interfaces.ConfigInterface#getVehicleIds() */ @Override public List getVehicleIds() throws RemoteException { @@ -372,7 +453,7 @@ public List getVehicleIds() throws RemoteException { } /* (non-Javadoc) - * @see org.transitime.ipc.interfaces.ConfigInterface#getServiceIds() + * @see org.transitclock.ipc.interfaces.ConfigInterface#getServiceIds() */ @Override public List getServiceIds() throws RemoteException { @@ -383,7 +464,7 @@ public List getServiceIds() throws RemoteException { } /* (non-Javadoc) - * @see org.transitime.ipc.interfaces.ConfigInterface#getCurrentServiceIds() + * @see org.transitclock.ipc.interfaces.ConfigInterface#getCurrentServiceIds() */ @Override public List getCurrentServiceIds() throws RemoteException { @@ -394,7 +475,7 @@ public List getCurrentServiceIds() throws RemoteException { } /* (non-Javadoc) - * @see org.transitime.ipc.interfaces.ConfigInterface#getTripIds() + * @see org.transitclock.ipc.interfaces.ConfigInterface#getTripIds() */ @Override public List getTripIds() throws RemoteException { @@ -407,7 +488,7 @@ public List getTripIds() throws RemoteException { } /* (non-Javadoc) - * @see org.transitime.ipc.interfaces.ConfigInterface#getBlockIds() + * @see org.transitclock.ipc.interfaces.ConfigInterface#getBlockIds() */ @Override public List getBlockIds() throws RemoteException { @@ -419,7 +500,7 @@ public List getBlockIds() throws RemoteException { } /* (non-Javadoc) - * @see org.transitime.ipc.interfaces.ConfigInterface#getBlockIds() + * @see org.transitclock.ipc.interfaces.ConfigInterface#getBlockIds() */ @Override public List getBlockIds(String serviceId) @@ -436,4 +517,12 @@ public List getBlockIds(String serviceId) return blockIds; } + public List getServiceIdsForDay(Long day) { + return Core.getInstance().getServiceUtils().getServiceIdsForDay(day); + } + + @Override + public boolean getServiceIdSuffix() throws RemoteException { + return Core.getInstance().getDbConfig().getServiceIdSuffix(); + } } diff --git a/transitclock/src/main/java/org/transitclock/ipc/servers/HoldingTimeServer.java b/transitclock/src/main/java/org/transitclock/ipc/servers/HoldingTimeServer.java new file mode 100755 index 000000000..4d1c02b54 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/servers/HoldingTimeServer.java @@ -0,0 +1,98 @@ +/** + * + */ +package org.transitclock.ipc.servers; + +import java.rmi.RemoteException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.core.dataCache.HoldingTimeCache; +import org.transitclock.core.dataCache.HoldingTimeCacheKey; +import org.transitclock.core.dataCache.VehicleDataCache; +import org.transitclock.db.structs.HoldingTime; +import org.transitclock.ipc.data.IpcHoldingTime; +import org.transitclock.ipc.interfaces.HoldingTimeInterface; +import org.transitclock.ipc.interfaces.PredictionAnalysisInterface; +import org.transitclock.ipc.rmi.AbstractServer; + +/** + * @author Sean Og Crudden Server to allow stored travel time predictions to be queried. + * TODO May not be set to run by default as really only for analysis of predictions. + */ +public class HoldingTimeServer extends AbstractServer implements HoldingTimeInterface { + // Should only be accessed as singleton class + private static HoldingTimeServer singleton; + + private static final Logger logger = LoggerFactory.getLogger(HoldingTimeServer.class); + + protected HoldingTimeServer(String agencyId) { + super(agencyId, HoldingTimeInterface.class.getSimpleName()); + + } + + /** + * Starts up the HoldingTimeServer so that RMI calls can be used to query + * holding times stored in he cache. This will automatically cause the object to continue to run and + * serve requests. + * + * @param agencyId + * @return the singleton PredictionAnalysisServer object. Usually does not need to + * used since the server will be fully running. + */ + public static HoldingTimeServer start(String agencyId) { + if (singleton == null) { + singleton = new HoldingTimeServer(agencyId); + } + if (!singleton.getAgencyId().equals(agencyId)) { + logger.error( + "Tried calling HoldingTimeServer.start() for " + + "agencyId={} but the singleton was created for agencyId={}", + agencyId, singleton.getAgencyId()); + return null; + } + return singleton; + } + + @Override + public IpcHoldingTime getHoldTime(String stopId, String vehicleId, String tripId) throws RemoteException { + + if(tripId==null) + { + if(VehicleDataCache.getInstance().getVehicle(vehicleId)!=null) + { + tripId=VehicleDataCache.getInstance().getVehicle(vehicleId).getTripId(); + } + } + if(stopId!=null && vehicleId!=null && tripId!=null) + { + HoldingTimeCacheKey key=new HoldingTimeCacheKey(stopId,vehicleId, tripId ); + HoldingTime result = HoldingTimeCache.getInstance().getHoldingTime(key); + if(result!=null) + return new IpcHoldingTime(result); + } + return null; + } + + @Override + public IpcHoldingTime getHoldTime(String stopId, String vehicleId) throws RemoteException { + + String tripId=null; + if(VehicleDataCache.getInstance().getVehicle(vehicleId)!=null) + { + tripId=VehicleDataCache.getInstance().getVehicle(vehicleId).getTripId(); + } + if(stopId!=null && vehicleId!=null && tripId!=null) + { + HoldingTimeCacheKey key=new HoldingTimeCacheKey(stopId,vehicleId, tripId ); + HoldingTime result = HoldingTimeCache.getInstance().getHoldingTime(key); + if(result!=null) + return new IpcHoldingTime(result); + } + return null; + } + + + + + +} diff --git a/transitclock/src/main/java/org/transitclock/ipc/servers/PredictionAnalysisServer.java b/transitclock/src/main/java/org/transitclock/ipc/servers/PredictionAnalysisServer.java new file mode 100755 index 000000000..a03ea4fcc --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/servers/PredictionAnalysisServer.java @@ -0,0 +1,103 @@ +/** + * + */ +package org.transitclock.ipc.servers; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.core.dataCache.StopPathCacheKey; +import org.transitclock.core.dataCache.StopPathPredictionCacheFactory; +import org.transitclock.db.structs.PredictionForStopPath; +import org.transitclock.ipc.data.IpcPredictionForStopPath; +import org.transitclock.ipc.interfaces.PredictionAnalysisInterface; +import org.transitclock.ipc.rmi.AbstractServer; + +import java.rmi.RemoteException; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * @author Sean Óg Crudden + * Server to allow stored travel time predictions to be queried. + * TODO May not be set to run by default as really only for analysis of predictions. + * TODO This needs to be changed to also work with frequency based services. + */ +public class PredictionAnalysisServer extends AbstractServer implements PredictionAnalysisInterface { + // Should only be accessed as singleton class + private static PredictionAnalysisServer singleton; + + private static final Logger logger = LoggerFactory.getLogger(PredictionAnalysisServer.class); + + protected PredictionAnalysisServer(String agencyId) { + super(agencyId, PredictionAnalysisInterface.class.getSimpleName()); + + } + + /** + * Starts up the PredictionAnalysisServer so that RMI calls can be used to query + * travel times prediction stored. This will automatically cause the object to continue to run and + * serve requests. + * + * @param agencyId + * @return the singleton PredictionAnalysisServer object. Usually does not need to + * used since the server will be fully running. + */ + public static PredictionAnalysisServer start(String agencyId) { + if (singleton == null) { + singleton = new PredictionAnalysisServer(agencyId); + } + if (!singleton.getAgencyId().equals(agencyId)) { + logger.error( + "Tried calling PredictionAnalysisInterface.start() for " + + "agencyId={} but the singleton was created for agencyId={}", + agencyId, singleton.getAgencyId()); + return null; + } + return singleton; + } + + + + @Override + public List getRecordedTravelTimePredictions(String tripId, Integer stopPathIndex, + Date startdate, Date enddate, String algorithm) throws RemoteException { + List result = PredictionForStopPath.getPredictionForStopPathFromDB(startdate, enddate, algorithm, tripId, stopPathIndex); + List results=new ArrayList(); + for(PredictionForStopPath prediction:result) + { + IpcPredictionForStopPath ipcPrediction=new IpcPredictionForStopPath(prediction); + results.add(ipcPrediction); + } + + + return results; + } + + @Override + public List getCachedTravelTimePredictions(String tripId, Integer stopPathIndex, + Date startdate, Date enddate, String algorithm) throws RemoteException { + StopPathCacheKey key=new StopPathCacheKey(tripId,stopPathIndex,true); + List predictions = StopPathPredictionCacheFactory.getInstance().getPredictions(key); + List results=new ArrayList(); + if(predictions!=null) + { + for(PredictionForStopPath prediction:predictions) + { + IpcPredictionForStopPath ipcPrediction=new IpcPredictionForStopPath(prediction); + if(algorithm!=null&&algorithm.length()>0) + { + if(algorithm.equals(prediction.getAlgorithm())) + { + results.add(ipcPrediction); + } + }else + { + results.add(ipcPrediction); + } + } + } + return results; + } + +} diff --git a/transitime/src/main/java/org/transitime/ipc/servers/PredictionsServer.java b/transitclock/src/main/java/org/transitclock/ipc/servers/PredictionsServer.java similarity index 80% rename from transitime/src/main/java/org/transitime/ipc/servers/PredictionsServer.java rename to transitclock/src/main/java/org/transitclock/ipc/servers/PredictionsServer.java index 01119b955..8a7913f5a 100644 --- a/transitime/src/main/java/org/transitime/ipc/servers/PredictionsServer.java +++ b/transitclock/src/main/java/org/transitclock/ipc/servers/PredictionsServer.java @@ -14,32 +14,34 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.ipc.servers; - -import java.rmi.RemoteException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; +package org.transitclock.ipc.servers; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.applications.Core; -import org.transitime.core.dataCache.PredictionDataCache; -import org.transitime.db.structs.Location; -import org.transitime.gtfs.StopsByLoc; -import org.transitime.gtfs.StopsByLoc.StopInfo; -import org.transitime.ipc.data.IpcPredictionsForRouteStopDest; -import org.transitime.ipc.interfaces.PredictionsInterface; -import org.transitime.ipc.rmi.AbstractServer; -import org.transitime.utils.IntervalTimer; -import org.transitime.utils.Time; +import org.transitclock.applications.Core; +import org.transitclock.core.dataCache.CanceledTripKey; +import org.transitclock.core.dataCache.SkippedStopsManager; +import org.transitclock.ipc.data.IpcCanceledTrip; +import org.transitclock.core.dataCache.CanceledTripManager; +import org.transitclock.core.dataCache.PredictionDataCache; +import org.transitclock.db.structs.Location; +import org.transitclock.gtfs.StopsByLoc; +import org.transitclock.gtfs.StopsByLoc.StopInfo; +import org.transitclock.ipc.data.IpcPredictionsForRouteStopDest; +import org.transitclock.ipc.data.IpcSkippedStop; +import org.transitclock.ipc.interfaces.PredictionsInterface; +import org.transitclock.ipc.rmi.AbstractServer; +import org.transitclock.utils.IntervalTimer; +import org.transitclock.utils.Time; + +import java.rmi.RemoteException; +import java.util.*; /** * Implements the PredictionsInterface interface on the server side such that a * PredictionsInterfaceFactory can make RMI calls in order to obtain prediction * information. The prediction information is provided using - * org.transitime.ipc.data.Prediction objects. + * org.transitclock.ipc.data.Prediction objects. * * @author SkiBu Smith * @@ -98,7 +100,7 @@ private PredictionsServer(String projectId) { } /* (non-Javadoc) - * @see org.transitime.ipc.interfaces.PredictionsInterface#get(java.lang.String, java.lang.String, int) + * @see org.transitclock.ipc.interfaces.PredictionsInterface#get(java.lang.String, java.lang.String, int) */ @Override public List get(String routeIdOrShortName, @@ -108,7 +110,7 @@ public List get(String routeIdOrShortName, } /* (non-Javadoc) - * @see org.transitime.ipc.interfaces.PredictionsInterface#get(java.util.List, int) + * @see org.transitclock.ipc.interfaces.PredictionsInterface#get(java.util.List, int) */ @Override public List get(List routeStops, @@ -117,7 +119,7 @@ public List get(List routeStops, } /* (non-Javadoc) - * @see org.transitime.ipc.interfaces.PredictionsInterface#getPredictionsByVehicle() + * @see org.transitclock.ipc.interfaces.PredictionsInterface#getPredictionsByVehicle() */ @Override public List getAllPredictions( @@ -130,6 +132,23 @@ public List getAllPredictions( maxSystemTimeForPrediction); } + /* (non-Javadoc) + * @see org.transitclock.ipc.interfaces.PredictionsInterface#getAllCanceledTrips() + */ + @Override + public HashMap getAllCanceledTrips(){ + return CanceledTripManager.getInstance().getAll(); + } + + /* (non-Javadoc) + * @see org.transitclock.ipc.interfaces.PredictionsInterface#getAllSkippedStops() + */ + @Override + public HashMap> getAllSkippedStops(){ + return SkippedStopsManager.getInstance().getAll(); + } + + // If stops are relatively close then should order routes based on route // order instead of distance. private static double DISTANCE_AT_WHICH_ROUTES_GROUPED = 80.0; @@ -172,7 +191,7 @@ public int compare(IpcPredictionsForRouteStopDest pred1, }; /* (non-Javadoc) - * @see org.transitime.ipc.interfaces.PredictionsInterface#get(org.transitime.db.structs.Location, double, int) + * @see org.transitclock.ipc.interfaces.PredictionsInterface#get(org.transitclock.db.structs.Location, double, int) */ @Override public List get(Location loc, diff --git a/transitclock/src/main/java/org/transitclock/ipc/servers/ReportingServer.java b/transitclock/src/main/java/org/transitclock/ipc/servers/ReportingServer.java new file mode 100644 index 000000000..02db31925 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/servers/ReportingServer.java @@ -0,0 +1,179 @@ +package org.transitclock.ipc.servers; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.core.ServiceType; +import org.transitclock.ipc.data.*; +import org.transitclock.ipc.interfaces.ReportingInterface; +import org.transitclock.ipc.rmi.AbstractServer; +import org.transitclock.reporting.service.OnTimePerformanceService; +import org.transitclock.reporting.service.RunTimeService; +import org.transitclock.reporting.service.SpeedMapService; + +import javax.inject.Inject; +import java.time.*; +import java.util.*; + +import static java.util.stream.Collectors.groupingBy; + +/** + * @author carabalb + * Server to allow Reporting information to be queried. + */ +public class ReportingServer extends AbstractServer implements ReportingInterface { + + // Should only be accessed as singleton class + private static ReportingServer singleton; + + @Inject + private SpeedMapService speedMapService; + + @Inject + private RunTimeService runTimeService; + + @Inject + private OnTimePerformanceService onTimePerformanceService; + + private static final Logger logger = + LoggerFactory.getLogger(ReportingServer.class); + + + public void start(String agencyId){ + start(agencyId, ReportingInterface.class.getSimpleName()); + } + + // Speed Map Reports + @Override + public List getStopPathsWithSpeed(LocalDate beginDate, + LocalDate endDate, + LocalTime beginTime, + LocalTime endTime, + String routeIdOrShortName, + ServiceType serviceType, + String headsign, + boolean readOnly) throws Exception{ + + return speedMapService.getStopPathsWithSpeed(beginDate, endDate, beginTime, endTime, routeIdOrShortName, serviceType, + headsign, getAgencyId(), readOnly); + } + + @Override + public List getStopsWithAvgDwellTimes(LocalDate beginDate, + LocalDate endDate, + LocalTime beginTime, + LocalTime endTime, + String routeIdOrShortName, + ServiceType serviceType, + boolean timePointsOnly, + String headsign, + boolean readOnly) throws Exception { + + return speedMapService.getStopsWithAvgDwellTimes(beginDate, endDate, beginTime, endTime, routeIdOrShortName, + serviceType, timePointsOnly, headsign, readOnly); + } + + @Override + public IpcDoubleSummaryStatistics getAverageRunTime(LocalDate beginDate, + LocalDate endDate, + LocalTime beginTime, + LocalTime endTime, + String routeIdOrShortName, + ServiceType serviceType, + boolean timePointsOnly, + String headsign, + boolean readOnly) throws Exception { + + return runTimeService.getAverageRunTime(beginDate, endDate, beginTime, endTime, routeIdOrShortName, + serviceType, timePointsOnly, headsign, readOnly); + } + + // Run Times Reports + @Override + public IpcRunTime getRunTimeSummary(LocalDate beginDate, + LocalDate endDate, + LocalTime beginTime, + LocalTime endTime, + String routeIdOrShortName, + String headsign, + String startStop, + String endStop, + ServiceType serviceType, + boolean timePointsOnly, + boolean currentTripsOnly, + boolean readOnly) throws Exception { + + return runTimeService.getRunTimeSummary(beginDate, endDate, beginTime, endTime, routeIdOrShortName, headsign, + startStop, endStop, serviceType, timePointsOnly, currentTripsOnly, this.getAgencyId(), readOnly); + } + + @Override + public List getRunTimeForTrips(LocalDate beginDate, + LocalDate endDate, + LocalTime beginTime, + LocalTime endTime, + String routeIdOrShortName, + String headsign, + String tripPatternId, + ServiceType serviceType, + boolean timePointsOnly, + boolean currentTripsOnly, + boolean readOnly) throws Exception { + + return runTimeService.getRunTimeForTrips(beginDate, endDate, beginTime, endTime, routeIdOrShortName, headsign, + tripPatternId, serviceType, timePointsOnly, currentTripsOnly, this.getAgencyId(), readOnly); + } + + @Override + public List getRunTimeForStopPaths(LocalDate beginDate, + LocalDate endDate, + LocalTime beginTime, + LocalTime endTime, + String routeIdOrShortName, + String tripId, + ServiceType serviceType, + boolean timePointsOnly, + boolean readOnly) throws Exception { + return runTimeService.getRunTimeForStopPaths(beginDate, endDate, beginTime, endTime, routeIdOrShortName, + tripId, serviceType, timePointsOnly, this.getAgencyId(), readOnly); + } + + // On Time Performance Reports + @Override + public List getArrivalsDeparturesForOtp(LocalDate beginDate, + LocalDate endDate, + LocalTime beginTime, + LocalTime endTime, + String routeIdOrShortName, + ServiceType serviceType, + boolean timePointsOnly, + String headsign) throws Exception{ + + return onTimePerformanceService.getArrivalsDeparturesForOtp(beginDate, endDate, beginTime, endTime, + routeIdOrShortName, serviceType, timePointsOnly, headsign, false); + } + + @Override + public List getArrivalsDeparturesForOtp(LocalDate beginDate, + LocalDate endDate, + LocalTime beginTime, + LocalTime endTime, + String routeIdOrShortName, + ServiceType serviceType, + boolean timePointsOnly, + String headsign, + boolean readOnly) throws Exception{ + + return onTimePerformanceService.getArrivalsDeparturesForOtp(beginDate, endDate, beginTime, endTime, + routeIdOrShortName, serviceType, timePointsOnly, headsign, readOnly); + } + + @Override + public List getRunTimeForRoutes(LocalDate beginDate, LocalDate endDate, + LocalTime beginTime, LocalTime endTime, ServiceType serviceType, + Integer earlyThreshold, Integer lateThreshold, + boolean readOnly) throws Exception{ + return runTimeService.getRunTimeForRoutes(beginDate, endDate, beginTime, endTime, serviceType, + earlyThreshold, lateThreshold, readOnly); + } + +} diff --git a/transitclock/src/main/java/org/transitclock/ipc/servers/RevisionInformationServer.java b/transitclock/src/main/java/org/transitclock/ipc/servers/RevisionInformationServer.java new file mode 100644 index 000000000..7a3d91906 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/servers/RevisionInformationServer.java @@ -0,0 +1,53 @@ +package org.transitclock.ipc.servers; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.applications.Core; +import org.transitclock.db.structs.ConfigRevision; +import org.transitclock.ipc.data.IpcRevisionInformation; +import org.transitclock.ipc.interfaces.RevisionInformationInterface; +import org.transitclock.ipc.rmi.AbstractServer; + +import java.rmi.RemoteException; + +/** + * RMI server for exposing internal schedule metadata. + */ +public class RevisionInformationServer extends AbstractServer implements RevisionInformationInterface { + + private static RevisionInformationServer singleton; + + private static final Logger logger = + LoggerFactory.getLogger(RevisionInformationServer.class); + + public static RevisionInformationServer start(String agencyId) { + if (singleton == null) { + singleton = new RevisionInformationServer(agencyId); + } + if (!singleton.getAgencyId().equals(agencyId)) { + logger.error("Tried calling ServerStatusServer.start() for " + + "agencyId={} but the singleton was created for projectId={}", + agencyId, singleton.getAgencyId()); + return null; + } + return singleton; + } + + private RevisionInformationServer(String projectId) { + super(projectId, RevisionInformationInterface.class.getSimpleName()); + } + + @Override + public IpcRevisionInformation get() throws RemoteException { + int activeRev = Core.getInstance().getDbConfig().getConfigRev(); + int lastIndex = Core.getInstance().getDbConfig().getConfigRevisions().size()-1; + ConfigRevision lastConfigRevision = Core.getInstance().getDbConfig().getConfigRevisions().get(lastIndex); + IpcRevisionInformation ipcRevisionInformation = new IpcRevisionInformation(); + ipcRevisionInformation.setActiveRev(activeRev); + ipcRevisionInformation.setLastConfigRev(lastConfigRevision.getConfigRev()); + ipcRevisionInformation.setLastProcessedTime(lastConfigRevision.getProcessedTime()); + ipcRevisionInformation.setLastNote(lastConfigRevision.getNotes()); + ipcRevisionInformation.setZipFileLastModifiedTime(lastConfigRevision.getZipFileLastModifiedTime()); + return ipcRevisionInformation; + } +} diff --git a/transitime/src/main/java/org/transitime/ipc/servers/ServerStatusServer.java b/transitclock/src/main/java/org/transitclock/ipc/servers/ServerStatusServer.java similarity index 81% rename from transitime/src/main/java/org/transitime/ipc/servers/ServerStatusServer.java rename to transitclock/src/main/java/org/transitclock/ipc/servers/ServerStatusServer.java index c9fad9022..ca4a53a02 100644 --- a/transitime/src/main/java/org/transitime/ipc/servers/ServerStatusServer.java +++ b/transitclock/src/main/java/org/transitclock/ipc/servers/ServerStatusServer.java @@ -15,16 +15,18 @@ * along with Transitime.org . If not, see . */ -package org.transitime.ipc.servers; +package org.transitclock.ipc.servers; import java.rmi.RemoteException; +import java.util.Date; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.ipc.data.IpcServerStatus; -import org.transitime.ipc.interfaces.ServerStatusInterface; -import org.transitime.ipc.rmi.AbstractServer; -import org.transitime.monitoring.AgencyMonitor; +import org.transitclock.applications.Core; +import org.transitclock.ipc.data.IpcServerStatus; +import org.transitclock.ipc.interfaces.ServerStatusInterface; +import org.transitclock.ipc.rmi.AbstractServer; +import org.transitclock.monitoring.AgencyMonitor; /** * Runs on the server side and receives IPC calls and returns results. @@ -74,7 +76,7 @@ private ServerStatusServer(String projectId) { } /* (non-Javadoc) - * @see org.transitime.ipc.interfaces.ServerStatusInterface#get() + * @see org.transitclock.ipc.interfaces.ServerStatusInterface#get() */ @Override public IpcServerStatus get() throws RemoteException { @@ -83,7 +85,7 @@ public IpcServerStatus get() throws RemoteException { } /* (non-Javadoc) - * @see org.transitime.ipc.interfaces.ServerStatusInterface#monitor() + * @see org.transitclock.ipc.interfaces.ServerStatusInterface#monitor() */ @Override public String monitor() throws RemoteException { @@ -96,4 +98,9 @@ public String monitor() throws RemoteException { return resultStr; } + @Override + public Date getCurrentServerTime() throws RemoteException { + return new Date(Core.getInstance().getSystemTime()); + } + } diff --git a/transitime/src/main/java/org/transitime/ipc/servers/VehiclesServer.java b/transitclock/src/main/java/org/transitclock/ipc/servers/VehiclesServer.java similarity index 52% rename from transitime/src/main/java/org/transitime/ipc/servers/VehiclesServer.java rename to transitclock/src/main/java/org/transitclock/ipc/servers/VehiclesServer.java index ebd7cf83d..d6e852a83 100644 --- a/transitime/src/main/java/org/transitime/ipc/servers/VehiclesServer.java +++ b/transitclock/src/main/java/org/transitclock/ipc/servers/VehiclesServer.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.ipc.servers; +package org.transitclock.ipc.servers; import java.io.Serializable; import java.rmi.RemoteException; @@ -24,26 +24,32 @@ import java.util.Date; import java.util.List; +import org.hibernate.Criteria; +import org.hibernate.Session; +import org.hibernate.criterion.Projections; +import org.hibernate.criterion.Restrictions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.core.BlocksInfo; -import org.transitime.core.dataCache.VehicleDataCache; -import org.transitime.db.structs.Block; -import org.transitime.db.structs.Trip; -import org.transitime.db.structs.VehicleConfig; -import org.transitime.ipc.data.IpcBlock; -import org.transitime.ipc.data.IpcVehicleComplete; -import org.transitime.ipc.data.IpcVehicleGtfsRealtime; -import org.transitime.ipc.data.IpcVehicle; -import org.transitime.ipc.data.IpcActiveBlock; -import org.transitime.ipc.data.IpcVehicleConfig; -import org.transitime.ipc.interfaces.VehiclesInterface; -import org.transitime.ipc.rmi.AbstractServer; +import org.transitclock.core.BlocksInfo; +import org.transitclock.core.dataCache.VehicleDataCache; +import org.transitclock.db.hibernate.HibernateUtils; +import org.transitclock.db.structs.Block; +import org.transitclock.db.structs.Route; +import org.transitclock.db.structs.Trip; +import org.transitclock.db.structs.VehicleConfig; +import org.transitclock.ipc.data.IpcActiveBlock; +import org.transitclock.ipc.data.IpcBlock; +import org.transitclock.ipc.data.IpcVehicle; +import org.transitclock.ipc.data.IpcVehicleComplete; +import org.transitclock.ipc.data.IpcVehicleConfig; +import org.transitclock.ipc.data.IpcVehicleGtfsRealtime; +import org.transitclock.ipc.interfaces.VehiclesInterface; +import org.transitclock.ipc.rmi.AbstractServer; /** * Implements the VehiclesInterface interface on the server side such that a * VehiclessClient can make RMI calls in order to obtain vehicle information. - * The vehicle information is provided using org.transitime.ipc.data.Vehicle + * The vehicle information is provided using org.transitclock.ipc.data.Vehicle * objects. * * @author SkiBu Smith @@ -102,7 +108,7 @@ private VehiclesServer(String projectId) { } /* (non-Javadoc) - * @see org.transitime.ipc.interfaces.VehiclesInterface#get() + * @see org.transitclock.ipc.interfaces.VehiclesInterface#get() */ @Override public Collection get() throws RemoteException { @@ -110,7 +116,7 @@ public Collection get() throws RemoteException { } /* (non-Javadoc) - * @see org.transitime.ipc.interfaces.VehiclesInterface#getComplete() + * @see org.transitclock.ipc.interfaces.VehiclesInterface#getComplete() */ @Override public Collection getComplete() throws RemoteException { @@ -118,7 +124,7 @@ public Collection getComplete() throws RemoteException { } /* (non-Javadoc) - * @see org.transitime.ipc.interfaces.VehiclesInterface#getGtfsRealtime() + * @see org.transitclock.ipc.interfaces.VehiclesInterface#getGtfsRealtime() */ @Override public Collection getGtfsRealtime() @@ -128,7 +134,7 @@ public Collection getGtfsRealtime() /* (non-Javadoc) - * @see org.transitime.ipc.interfaces.VehiclesInterface#get(java.lang.String) + * @see org.transitclock.ipc.interfaces.VehiclesInterface#get(java.lang.String) */ @Override public IpcVehicle get(String vehicleId) throws RemoteException { @@ -136,7 +142,7 @@ public IpcVehicle get(String vehicleId) throws RemoteException { } /* (non-Javadoc) - * @see org.transitime.ipc.interfaces.VehiclesInterface#get(java.lang.String) + * @see org.transitclock.ipc.interfaces.VehiclesInterface#get(java.lang.String) */ @Override public IpcVehicleComplete getComplete(String vehicleId) throws RemoteException { @@ -144,7 +150,7 @@ public IpcVehicleComplete getComplete(String vehicleId) throws RemoteException { } /* (non-Javadoc) - * @see org.transitime.ipc.interfaces.VehiclesInterface#get(java.util.List) + * @see org.transitclock.ipc.interfaces.VehiclesInterface#get(java.util.List) */ @Override public Collection get(Collection vehicleIds) @@ -154,7 +160,7 @@ public Collection get(Collection vehicleIds) } /* (non-Javadoc) - * @see org.transitime.ipc.interfaces.VehiclesInterface#get(java.util.List) + * @see org.transitclock.ipc.interfaces.VehiclesInterface#get(java.util.List) */ @Override public Collection getComplete(Collection vehicleIds) @@ -164,7 +170,7 @@ public Collection getComplete(Collection vehicleIds) } /* (non-Javadoc) - * @see org.transitime.ipc.interfaces.VehiclesInterface#getForRoute(java.lang.String) + * @see org.transitclock.ipc.interfaces.VehiclesInterface#getForRoute(java.lang.String) */ @Override public Collection getForRoute(String routeIdOrShortName) @@ -174,7 +180,7 @@ public Collection getForRoute(String routeIdOrShortName) } /* (non-Javadoc) - * @see org.transitime.ipc.interfaces.VehiclesInterface#getForRoute(java.lang.String) + * @see org.transitclock.ipc.interfaces.VehiclesInterface#getForRoute(java.lang.String) */ @Override public Collection getCompleteForRoute(String routeIdOrShortName) @@ -184,7 +190,7 @@ public Collection getCompleteForRoute(String routeIdOrShortN } /* (non-Javadoc) - * @see org.transitime.ipc.interfaces.VehiclesInterface#getForRoute(java.util.Collection) + * @see org.transitclock.ipc.interfaces.VehiclesInterface#getForRoute(java.util.Collection) */ @Override public Collection getForRoute( @@ -194,7 +200,7 @@ public Collection getForRoute( } /* (non-Javadoc) - * @see org.transitime.ipc.interfaces.VehiclesInterface#getForRoute(java.util.Collection) + * @see org.transitclock.ipc.interfaces.VehiclesInterface#getForRoute(java.util.Collection) */ @Override public Collection getCompleteForRoute( @@ -265,7 +271,7 @@ private Collection getCompleteSerializableCollection( } /* (non-Javadoc) - * @see org.transitime.ipc.interfaces.VehiclesInterface#getActiveBlocks() + * @see org.transitclock.ipc.interfaces.VehiclesInterface#getActiveBlocks() */ @Override public Collection getActiveBlocks( @@ -274,12 +280,10 @@ public Collection getActiveBlocks( // List of data to be returned List results = new ArrayList(); - // Determine all the active blocks List blocks = BlocksInfo.getCurrentlyActiveBlocks(routeIds, null, allowableBeforeTimeSecs, -1); - // For each active block determine associated vehicle for (Block block : blocks) { IpcBlock ipcBlock = new IpcBlock(block); @@ -304,7 +308,6 @@ public Collection getActiveBlocks( ipcVehiclesForBlock, tripForSorting); results.add(ipcBlockAndVehicle); } - // Sort the results so that ordered by route and then block start time IpcActiveBlock.sort(results); @@ -313,7 +316,140 @@ public Collection getActiveBlocks( } /* (non-Javadoc) - * @see org.transitime.ipc.interfaces.VehiclesInterface#getVehicleConfigs() + * @see org.transitclock.ipc.interfaces.VehiclesInterface#getActiveBlocks() + */ + @Override + public int getNumActiveBlocks( + Collection routeIds, int allowableBeforeTimeSecs) + throws RemoteException { + // Determine all the active blocks + List blocks = + BlocksInfo.getCurrentlyActiveBlocks(routeIds, null, + allowableBeforeTimeSecs, -1); + + return blocks.size(); + } + + /* (non-Javadoc) + * @see org.transitclock.ipc.interfaces.VehiclesInterface#getActiveBlocks() + */ + @Override + public Collection getActiveBlocksWithoutVehicles( + Collection routeIds, int allowableBeforeTimeSecs) + throws RemoteException { + // List of data to be returned + List results = + new ArrayList(); + // Determine all the active blocks + List blocks = + BlocksInfo.getCurrentlyActiveBlocks(routeIds, null, + allowableBeforeTimeSecs, -1); + // For each active block determine associated vehicle + for (Block block : blocks) { + try{ + IpcBlock ipcBlock = new IpcBlock(block); + int activeTripIndex = block.activeTripIndex(new Date(), + allowableBeforeTimeSecs); + + + // Create and add the IpcActiveBlock, skipping the slow vehicle fetching + Trip tripForSorting = block.getTrip(activeTripIndex); + IpcActiveBlock ipcBlockAndVehicle = + new IpcActiveBlock(ipcBlock, activeTripIndex, + new ArrayList(), tripForSorting); + results.add(ipcBlockAndVehicle); + }catch (Exception e){ + logger.warn("Error while fecthing active blocks data (probably hibernate still loading data): " + e.getMessage()); + } + } + // Sort the results so that ordered by route and then block start time + IpcActiveBlock.sort(results); + + // Return results + return results; + } + + + /* (non-Javadoc) + * @see org.transitclock.ipc.interfaces.VehiclesInterface#getActiveBlocksAndVehiclesByRouteId() + */ + @Override + public Collection getActiveBlocksAndVehiclesByRouteId( + String routeId, int allowableBeforeTimeSecs) + throws RemoteException { + + Collection routeIds = new ArrayList<>(); + routeIds.add(routeId); + return getActiveBlocksAndVehiclesByRouteId(routeIds, allowableBeforeTimeSecs); + } + + /* (non-Javadoc) + * @see org.transitclock.ipc.interfaces.VehiclesInterface#getActiveBlocksAndVehiclesByRouteName() + */ + @Override + public Collection getActiveBlocksAndVehiclesByRouteName( + String routeName, int allowableBeforeTimeSecs) + throws RemoteException { + + Session session = HibernateUtils.getSession(); + Criteria criteria = session.createCriteria(Route.class) + .add(Restrictions.eq("name", routeName)) + .setProjection(Projections.groupProperty("id")); + List routeIds = criteria.list(); + session.close(); + + return getActiveBlocksAndVehiclesByRouteId(routeIds, allowableBeforeTimeSecs); + } + + private Collection getActiveBlocksAndVehiclesByRouteId( + Collection routeIds, int allowableBeforeTimeSecs) + throws RemoteException { + + // List of data to be returned + List results = + new ArrayList(); + // Determine all the active blocks + List blocks = + BlocksInfo.getCurrentlyActiveBlocks(routeIds, null, + allowableBeforeTimeSecs, -1); + // For each active block determine associated vehicle + for (Block block : blocks) { + IpcBlock ipcBlock = new IpcBlock(block); + // If a block doesn't have a vehicle associated with it need + // to determine which route a block is currently associated with + // since can't get that info from the vehicle. This way the block + // can be properly grouped with the associated route even when it + // doesn't have a vehicle assigned. + int activeTripIndex = block.activeTripIndex(new Date(), + allowableBeforeTimeSecs); + + Trip tripForSorting = block.getTrip(activeTripIndex); + + // Check that block's active trip is for the specified route + // (Otherwise, could be a past or future trip) + if (routeIds != null && !routeIds.isEmpty() && !routeIds.contains(tripForSorting.getRouteId())) + continue; + + // Determine vehicles associated with the block if there are any + Collection vehicleIdsForBlock = VehicleDataCache + .getInstance().getVehiclesByBlockId(block.getId()); + Collection ipcVehiclesForBlock = get(vehicleIdsForBlock); + + // Create and add the IpcActiveBlock + IpcActiveBlock ipcBlockAndVehicle = + new IpcActiveBlock(ipcBlock, activeTripIndex, + ipcVehiclesForBlock, tripForSorting); + results.add(ipcBlockAndVehicle); + } + // Sort the results so that ordered by route and then block start time + IpcActiveBlock.sort(results); + + // Return results + return results; + } + + /* (non-Javadoc) + * @see org.transitclock.ipc.interfaces.VehiclesInterface#getVehicleConfigs() */ @Override public Collection getVehicleConfigs() @@ -327,4 +463,18 @@ public Collection getVehicleConfigs() return result; } + /* (non-Javadoc) + * @see org.transitclock.ipc.interfaces.VehiclesInterface#getVehiclesForBlocks() + */ + @Override + public Collection getVehiclesForBlocks() throws RemoteException { + List vehicleIds = new ArrayList(); + List blocks = BlocksInfo.getCurrentlyActiveBlocks(); + for (Block block : blocks) { + Collection vehicleIdsForBlock = VehicleDataCache + .getInstance().getVehiclesByBlockId(block.getId()); + vehicleIds.addAll(vehicleIdsForBlock); + } + return get(vehicleIds); + } } diff --git a/transitime/src/main/java/org/transitime/ipc/servers/package-info.java b/transitclock/src/main/java/org/transitclock/ipc/servers/package-info.java similarity index 96% rename from transitime/src/main/java/org/transitime/ipc/servers/package-info.java rename to transitclock/src/main/java/org/transitclock/ipc/servers/package-info.java index 153ee0ad8..78576598c 100644 --- a/transitime/src/main/java/org/transitime/ipc/servers/package-info.java +++ b/transitclock/src/main/java/org/transitclock/ipc/servers/package-info.java @@ -26,4 +26,4 @@ * @author SkiBu Smith * */ -package org.transitime.ipc.servers; \ No newline at end of file +package org.transitclock.ipc.servers; \ No newline at end of file diff --git a/transitclock/src/main/java/org/transitclock/ipc/util/GtfsDbDataUtil.java b/transitclock/src/main/java/org/transitclock/ipc/util/GtfsDbDataUtil.java new file mode 100644 index 000000000..51a5bacbf --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/util/GtfsDbDataUtil.java @@ -0,0 +1,36 @@ +package org.transitclock.ipc.util; + +import org.apache.commons.lang3.StringUtils; +import org.transitclock.applications.Core; +import org.transitclock.db.structs.Route; +import org.transitclock.exceptions.InvalidRouteException; +import org.transitclock.gtfs.DbConfig; + +public class GtfsDbDataUtil { + /** + * For getting route from routeIdOrShortName. Tries using + * routeIdOrShortName as first a route short name to see if there is such a + * route. If not, then uses routeIdOrShortName as a routeId. + * + * @param routeIdOrShortName + * @return The Route, or null if no such route + */ + public static String getRouteShortName(String routeIdOrShortName) throws InvalidRouteException { + if(StringUtils.isNotBlank(routeIdOrShortName)){ + DbConfig dbConfig = Core.getInstance().getDbConfig(); + Route dbRoute = + dbConfig.getRouteByShortName(routeIdOrShortName); + if (dbRoute == null) + dbRoute = dbConfig.getRouteById(routeIdOrShortName); + if (dbRoute != null){ + String route = dbRoute.getShortName(); + if(StringUtils.isNotBlank(route)){ + return route; + } + } + } else { + return ""; + } + throw new InvalidRouteException(routeIdOrShortName); + } +} diff --git a/transitclock/src/main/java/org/transitclock/ipc/util/GtfsTimeUtil.java b/transitclock/src/main/java/org/transitclock/ipc/util/GtfsTimeUtil.java new file mode 100644 index 000000000..d663a6c25 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/ipc/util/GtfsTimeUtil.java @@ -0,0 +1,17 @@ +package org.transitclock.ipc.util; + +import org.transitclock.utils.Time; + +import java.time.ZoneId; +import java.util.Date; + +public class GtfsTimeUtil { + + public static int dayOfYearForTrip(Date date, ZoneId zone) { + // Adjust date by three hours so if get a time such as 2:30 am + // it will be adjusted back to the previous day. This way can handle + // trips that span midnight. But this doesn't work for trips that + // span 3am. + return date.toInstant().minusMillis(3* Time.MS_PER_HOUR).atZone(zone).toLocalDate().getDayOfYear(); + } +} diff --git a/transitime/src/main/java/org/transitime/logging/LoggingTest.java b/transitclock/src/main/java/org/transitclock/logging/LoggingTest.java similarity index 95% rename from transitime/src/main/java/org/transitime/logging/LoggingTest.java rename to transitclock/src/main/java/org/transitclock/logging/LoggingTest.java index bcb62f567..1fff97d20 100644 --- a/transitime/src/main/java/org/transitime/logging/LoggingTest.java +++ b/transitclock/src/main/java/org/transitclock/logging/LoggingTest.java @@ -14,11 +14,11 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.logging; +package org.transitclock.logging; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.applications.Core; +import org.transitclock.applications.Core; /** * Just for debugging logging diff --git a/transitime/src/main/java/org/transitime/logging/Markers.java b/transitclock/src/main/java/org/transitclock/logging/Markers.java similarity index 97% rename from transitime/src/main/java/org/transitime/logging/Markers.java rename to transitclock/src/main/java/org/transitclock/logging/Markers.java index 475b84fda..81d18444a 100644 --- a/transitime/src/main/java/org/transitime/logging/Markers.java +++ b/transitclock/src/main/java/org/transitclock/logging/Markers.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.logging; +package org.transitclock.logging; import org.slf4j.Marker; import org.slf4j.MarkerFactory; diff --git a/transitime/src/main/java/org/transitime/logging/package-info.java b/transitclock/src/main/java/org/transitclock/logging/package-info.java similarity index 97% rename from transitime/src/main/java/org/transitime/logging/package-info.java rename to transitclock/src/main/java/org/transitclock/logging/package-info.java index dd4dbbbd2..eb641d5ec 100644 --- a/transitime/src/main/java/org/transitime/logging/package-info.java +++ b/transitclock/src/main/java/org/transitclock/logging/package-info.java @@ -31,4 +31,4 @@ * @author SkiBu Smith * */ -package org.transitime.logging; \ No newline at end of file +package org.transitclock.logging; \ No newline at end of file diff --git a/transitime/src/main/java/org/transitime/maintenance/ArchiveOldFiles.java b/transitclock/src/main/java/org/transitclock/maintenance/ArchiveOldFiles.java similarity index 95% rename from transitime/src/main/java/org/transitime/maintenance/ArchiveOldFiles.java rename to transitclock/src/main/java/org/transitclock/maintenance/ArchiveOldFiles.java index beaa558ff..4dc438df2 100644 --- a/transitime/src/main/java/org/transitime/maintenance/ArchiveOldFiles.java +++ b/transitclock/src/main/java/org/transitclock/maintenance/ArchiveOldFiles.java @@ -15,15 +15,15 @@ * along with Transitime.org . If not, see . */ -package org.transitime.maintenance; +package org.transitclock.maintenance; import java.io.File; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.utils.Gzip; -import org.transitime.utils.Time; -import org.transitime.utils.Zip; +import org.transitclock.utils.Gzip; +import org.transitclock.utils.Time; +import org.transitclock.utils.Zip; /** * For finding old files and archiving them. Used for log files. @@ -130,7 +130,7 @@ private File gzipFile(File file) { * * (non-Javadoc) * - * @see org.transitime.maintenance.OldFileFinder#handleOldFile(java.io.File) + * @see org.transitclock.maintenance.OldFileFinder#handleOldFile(java.io.File) */ @Override public void handleOldFile(File file) { @@ -247,7 +247,7 @@ private static void deleteFiles(File directory) { * * (non-Javadoc) * - * @see org.transitime.maintenance.OldFileFinder#handleDirectoryWithOldFile(java.io.File) + * @see org.transitclock.maintenance.OldFileFinder#handleDirectoryWithOldFile(java.io.File) */ @Override protected void handleDirectoryWithOldFile(File directory) { @@ -277,7 +277,7 @@ protected void handleDirectoryWithOldFile(File directory) { * * (non-Javadoc) * - * @see org.transitime.maintenance.OldFileFinder#handleDirectory(java.io.File) + * @see org.transitclock.maintenance.OldFileFinder#handleDirectory(java.io.File) */ @Override protected void handleDirectory(File directory) { diff --git a/transitime/src/main/java/org/transitime/maintenance/ArchiveOldFilesModule.java b/transitclock/src/main/java/org/transitclock/maintenance/ArchiveOldFilesModule.java similarity index 86% rename from transitime/src/main/java/org/transitime/maintenance/ArchiveOldFilesModule.java rename to transitclock/src/main/java/org/transitclock/maintenance/ArchiveOldFilesModule.java index 80c3ab447..5319a160c 100644 --- a/transitime/src/main/java/org/transitime/maintenance/ArchiveOldFilesModule.java +++ b/transitclock/src/main/java/org/transitclock/maintenance/ArchiveOldFilesModule.java @@ -15,19 +15,19 @@ * along with Transitime.org . If not, see . */ -package org.transitime.maintenance; +package org.transitclock.maintenance; 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.config.StringConfigValue; -import org.transitime.configData.AgencyConfig; -import org.transitime.logging.Markers; -import org.transitime.modules.Module; -import org.transitime.utils.Time; +import org.transitclock.applications.Core; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.config.StringConfigValue; +import org.transitclock.configData.AgencyConfig; +import org.transitclock.logging.Markers; +import org.transitclock.modules.Module; +import org.transitclock.utils.Time; /** * A module that runs in a separate thread that archives old log files as @@ -41,27 +41,27 @@ public class ArchiveOldFilesModule extends Module { /******************** Parameters ************************************/ private static StringConfigValue timeToArchive = - new StringConfigValue("transitime.maintenance.timeToArchive", + new StringConfigValue("transitclock.maintenance.timeToArchive", "01:00:00", // 1 am "Specified time of day that should archive files. Should " + "usually be run in middle of night when load is less. " + "Time is in the format HH:MM:SS"); private static StringConfigValue awsRegion = - new StringConfigValue("transitime.maintenance.awsRegion", + new StringConfigValue("transitclock.maintenance.awsRegion", AwsGlacier.OREGON_REGION, "The region for Amazon AWS where files should be archive " + "to."); private static StringConfigValue logDirForInventoryFile = - new StringConfigValue("transitime.maintenance.logDirForInventoryFile", + new StringConfigValue("transitclock.maintenance.logDirForInventoryFile", "Directory where to write the inventory file that lists " + "the archiveIds and other info for the data written " + "to the specified vault. Should be something like " + "D:/Logs/mbta"); private static StringConfigValue logFileBaseDir = - new StringConfigValue("transitime.maintenance.logFileBaseDir", + new StringConfigValue("transitclock.maintenance.logFileBaseDir", "Where to find the log files. This part of the directory " + "name is not included in the file description for each " + "file in the archived zip file. This way the file names " @@ -70,39 +70,39 @@ public class ArchiveOldFilesModule extends Module { + "D:/Logs/mbta/core/2014/12/20."); private static StringConfigValue awsVaultName = - new StringConfigValue("transitime.maintenance.awsVaultName", + new StringConfigValue("transitclock.maintenance.awsVaultName", "For creating the vault name for archiving files to AWS " + "Glacier. Should contain the agency name such that will " + "be something like mbta-core"); private static StringConfigValue awsVaultName2 = - new StringConfigValue("transitime.maintenance.awsVaultName2", + new StringConfigValue("transitclock.maintenance.awsVaultName2", null, "For when making two separate archives. Can use two " + "archives if some files should be archived sooner " + "that others. Null means not using second archive."); private static StringConfigValue logFileSubDirectory = - new StringConfigValue("transitime.maintenance.logFileSubDir", + new StringConfigValue("transitclock.maintenance.logFileSubDir", "The subdirectory beyond the base directory. Specifies " + "where to find the log files to be archived. This part " + "of the file names is included in the file descriptions " + "in the zip file. Should be something like mbta/core"); private static StringConfigValue logFileSubDirectory2 = - new StringConfigValue("transitime.maintenance.logFileSubDir2", + new StringConfigValue("transitclock.maintenance.logFileSubDir2", null, "For when making two separate archives. Can use two " + "archives if some files should be archived sooner " + "that others. Null means not using second archive."); private static IntegerConfigValue daysTillFilesArchived = - new IntegerConfigValue("transitime.maintenance.daysTillFilesArchived", + new IntegerConfigValue("transitclock.maintenance.daysTillFilesArchived", 90, "How many days old files can be online before they are archived."); private static IntegerConfigValue daysTillFilesArchived2 = - new IntegerConfigValue("transitime.maintenance.daysTillFilesArchived2", + new IntegerConfigValue("transitclock.maintenance.daysTillFilesArchived2", 90, "For when making two separate archives. Can use two " + "archives if some files should be archived sooner " diff --git a/transitime/src/main/java/org/transitime/maintenance/ArchiverInterface.java b/transitclock/src/main/java/org/transitclock/maintenance/ArchiverInterface.java similarity index 97% rename from transitime/src/main/java/org/transitime/maintenance/ArchiverInterface.java rename to transitclock/src/main/java/org/transitclock/maintenance/ArchiverInterface.java index 5cf58aa43..2fb77c398 100644 --- a/transitime/src/main/java/org/transitime/maintenance/ArchiverInterface.java +++ b/transitclock/src/main/java/org/transitclock/maintenance/ArchiverInterface.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.maintenance; +package org.transitclock.maintenance; /** diff --git a/transitime/src/main/java/org/transitime/maintenance/AwsGlacier.java b/transitclock/src/main/java/org/transitclock/maintenance/AwsGlacier.java similarity index 98% rename from transitime/src/main/java/org/transitime/maintenance/AwsGlacier.java rename to transitclock/src/main/java/org/transitclock/maintenance/AwsGlacier.java index 9124551b8..826f245d8 100644 --- a/transitime/src/main/java/org/transitime/maintenance/AwsGlacier.java +++ b/transitclock/src/main/java/org/transitclock/maintenance/AwsGlacier.java @@ -15,14 +15,14 @@ * along with Transitime.org . If not, see . */ -package org.transitime.maintenance; +package org.transitclock.maintenance; import java.io.File; import java.io.FileNotFoundException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.utils.IntervalTimer; -import org.transitime.utils.Time; +import org.transitclock.utils.IntervalTimer; +import org.transitclock.utils.Time; import com.amazonaws.AmazonClientException; import com.amazonaws.auth.AWSCredentials; diff --git a/transitime/src/main/java/org/transitime/maintenance/AwsGlacierArchiver.java b/transitclock/src/main/java/org/transitclock/maintenance/AwsGlacierArchiver.java similarity index 96% rename from transitime/src/main/java/org/transitime/maintenance/AwsGlacierArchiver.java rename to transitclock/src/main/java/org/transitclock/maintenance/AwsGlacierArchiver.java index adc049b8b..1a6cc91f7 100644 --- a/transitime/src/main/java/org/transitime/maintenance/AwsGlacierArchiver.java +++ b/transitclock/src/main/java/org/transitclock/maintenance/AwsGlacierArchiver.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.maintenance; +package org.transitclock.maintenance; import java.io.File; import java.io.FileWriter; @@ -88,7 +88,7 @@ private void addToArchiveLog(String description, String archiveId) { } /* (non-Javadoc) - * @see org.transitime.maintenance.ArchiverInterface#upload(java.lang.String, java.lang.String) + * @see org.transitclock.maintenance.ArchiverInterface#upload(java.lang.String, java.lang.String) */ @Override public String upload(String fileName, String description) { diff --git a/transitime/src/main/java/org/transitime/maintenance/AwsGlacierInventoryRetriever.java b/transitclock/src/main/java/org/transitclock/maintenance/AwsGlacierInventoryRetriever.java similarity index 99% rename from transitime/src/main/java/org/transitime/maintenance/AwsGlacierInventoryRetriever.java rename to transitclock/src/main/java/org/transitclock/maintenance/AwsGlacierInventoryRetriever.java index 75101bfef..72b3b463f 100644 --- a/transitime/src/main/java/org/transitime/maintenance/AwsGlacierInventoryRetriever.java +++ b/transitclock/src/main/java/org/transitclock/maintenance/AwsGlacierInventoryRetriever.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.maintenance; +package org.transitclock.maintenance; import java.io.BufferedReader; import java.io.BufferedWriter; @@ -28,8 +28,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.utils.IntervalTimer; -import org.transitime.utils.Time; +import org.transitclock.utils.IntervalTimer; +import org.transitclock.utils.Time; import com.amazonaws.AmazonClientException; import com.amazonaws.auth.AWSCredentials; diff --git a/transitime/src/main/java/org/transitime/maintenance/OldFileFinder.java b/transitclock/src/main/java/org/transitclock/maintenance/OldFileFinder.java similarity index 98% rename from transitime/src/main/java/org/transitime/maintenance/OldFileFinder.java rename to transitclock/src/main/java/org/transitclock/maintenance/OldFileFinder.java index 34257fb73..d63e3acfa 100644 --- a/transitime/src/main/java/org/transitime/maintenance/OldFileFinder.java +++ b/transitclock/src/main/java/org/transitclock/maintenance/OldFileFinder.java @@ -15,14 +15,14 @@ * along with Transitime.org . If not, see . */ -package org.transitime.maintenance; +package org.transitclock.maintenance; import java.io.File; import java.io.IOException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.utils.Time; +import org.transitclock.utils.Time; /** * Abstract class for finding files older than specified number of days. The diff --git a/transitime/src/main/java/org/transitime/maintenance/package-info.java b/transitclock/src/main/java/org/transitclock/maintenance/package-info.java similarity index 95% rename from transitime/src/main/java/org/transitime/maintenance/package-info.java rename to transitclock/src/main/java/org/transitclock/maintenance/package-info.java index 846769c34..e123e3cf9 100644 --- a/transitime/src/main/java/org/transitime/maintenance/package-info.java +++ b/transitclock/src/main/java/org/transitclock/maintenance/package-info.java @@ -22,4 +22,4 @@ * @author SkiBu Smith * */ -package org.transitime.maintenance; \ No newline at end of file +package org.transitclock.maintenance; \ No newline at end of file diff --git a/transitime/src/main/java/org/transitime/misc/HibernateTest.java b/transitclock/src/main/java/org/transitclock/misc/HibernateTest.java similarity index 95% rename from transitime/src/main/java/org/transitime/misc/HibernateTest.java rename to transitclock/src/main/java/org/transitclock/misc/HibernateTest.java index 8beaecea2..d0829f021 100644 --- a/transitime/src/main/java/org/transitime/misc/HibernateTest.java +++ b/transitclock/src/main/java/org/transitclock/misc/HibernateTest.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.misc; +package org.transitclock.misc; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.StatelessSession; import org.hibernate.Transaction; -import org.transitime.db.hibernate.HibernateUtils; -import org.transitime.db.structs.AvlReport; -import org.transitime.utils.IntervalTimer; +import org.transitclock.db.hibernate.HibernateUtils; +import org.transitclock.db.structs.AvlReport; +import org.transitclock.utils.IntervalTimer; /** * For testing writing data to database. Compares batch to stateless processing. diff --git a/transitime/src/main/java/org/transitime/misc/Test.java b/transitclock/src/main/java/org/transitclock/misc/Test.java similarity index 96% rename from transitime/src/main/java/org/transitime/misc/Test.java rename to transitclock/src/main/java/org/transitclock/misc/Test.java index 7a9d3cbf1..9c8c8abdb 100644 --- a/transitime/src/main/java/org/transitime/misc/Test.java +++ b/transitclock/src/main/java/org/transitclock/misc/Test.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.misc; +package org.transitclock.misc; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/transitime/src/main/java/org/transitime/misc/package-info.java b/transitclock/src/main/java/org/transitclock/misc/package-info.java similarity index 96% rename from transitime/src/main/java/org/transitime/misc/package-info.java rename to transitclock/src/main/java/org/transitclock/misc/package-info.java index 789d9f6f5..89e8146f7 100644 --- a/transitime/src/main/java/org/transitime/misc/package-info.java +++ b/transitclock/src/main/java/org/transitclock/misc/package-info.java @@ -21,4 +21,4 @@ * @author SkiBu Smith * */ -package org.transitime.misc; \ No newline at end of file +package org.transitclock.misc; \ No newline at end of file diff --git a/transitime/src/main/java/org/transitime/modules/Module.java b/transitclock/src/main/java/org/transitclock/modules/Module.java similarity index 96% rename from transitime/src/main/java/org/transitime/modules/Module.java rename to transitclock/src/main/java/org/transitclock/modules/Module.java index eb2fd8227..1e307d7cd 100644 --- a/transitime/src/main/java/org/transitime/modules/Module.java +++ b/transitclock/src/main/java/org/transitclock/modules/Module.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.modules; +package org.transitclock.modules; import java.lang.reflect.Constructor; import java.util.concurrent.ExecutorService; @@ -22,8 +22,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.configData.AgencyConfig; -import org.transitime.utils.threading.NamedThreadFactory; +import org.transitclock.configData.AgencyConfig; +import org.transitclock.utils.threading.NamedThreadFactory; /** * Modules are run in a separate thread and continuously process data. They are diff --git a/transitime/src/main/java/org/transitime/modules/package-info.java b/transitclock/src/main/java/org/transitclock/modules/package-info.java similarity index 83% rename from transitime/src/main/java/org/transitime/modules/package-info.java rename to transitclock/src/main/java/org/transitclock/modules/package-info.java index 08b00fd8a..fc9fe2944 100644 --- a/transitime/src/main/java/org/transitime/modules/package-info.java +++ b/transitclock/src/main/java/org/transitclock/modules/package-info.java @@ -22,13 +22,13 @@ * For the core predictor application modules can be started simply * by configuring on the command line * which ones should be run by using the VM argument - * -Dtransitime.modules.optionalModulesList. Multiple modules can be specified + * -Dtransitclock.modules.optionalModulesList. Multiple modules can be specified * by separating them with a semicolon. For example, to use one module to * read AVL data and put it into JMS, and another module for reading the * AVL data from JMS and processing it could use something like: - * -Dtransitime.modules.optionalModulesList=org.transitime.avl.MbtaNextBusAvlModule;org.transitime.avl.AvlJmsClientModule + * -Dtransitclock.modules.optionalModulesList=org.transitclock.avl.MbtaNextBusAvlModule;org.transitclock.avl.AvlJmsClientModule * * @author SkiBu Smith * */ -package org.transitime.modules; \ No newline at end of file +package org.transitclock.modules; \ No newline at end of file diff --git a/transitclock/src/main/java/org/transitclock/monitoring/ActiveBlocksMonitor.java b/transitclock/src/main/java/org/transitclock/monitoring/ActiveBlocksMonitor.java new file mode 100644 index 000000000..9b61c5d49 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/monitoring/ActiveBlocksMonitor.java @@ -0,0 +1,78 @@ +/* + * 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.monitoring; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.applications.Core; +import org.transitclock.core.BlocksInfo; +import org.transitclock.db.structs.Block; +import org.transitclock.utils.EmailSender; + +import java.util.Date; +import java.util.List; + +/** + * For monitoring active blocks. Unlike the other monitors, + * this one never triggers an alarm, it simply posts metrics to cloudwatch + */ +public class ActiveBlocksMonitor extends MonitorBase { + + private long reportingIntervalInMillis = 60l * 1000l; + + private Date lastUpdate = new Date(); + + private MonitoringService monitoringService; + + private static final Logger logger = LoggerFactory + .getLogger(ActiveBlocksMonitor.class); + + public ActiveBlocksMonitor(MonitoringService monitoringService, EmailSender emailSender, String agencyId) { + super(emailSender, agencyId); + this.monitoringService = monitoringService; + } + + /* (non-Javadoc) + * @see org.transitclock.monitoring.MonitorBase#triggered() + */ + @Override + protected boolean triggered() { + Date now = new Date(); + if(now.getTime() - lastUpdate.getTime() > reportingIntervalInMillis){ + List blocks = BlocksInfo.getCurrentlyActiveBlocks(); + double activeBlockCount = (blocks != null ? blocks.size() : 0); + double totalBlockCount = Core.getInstance().getDbConfig().getBlockCount(); + // cloudwatch metrics for active/total moved to PredictabilityMonitor + double activeBlockCountPercentage = 0; + if(activeBlockCount > 0){ + activeBlockCountPercentage = activeBlockCount / totalBlockCount; + } + monitoringService.averageMetric("PercentageActiveBlockCount", activeBlockCountPercentage); + lastUpdate = new Date(); + } + return false; + } + + /* (non-Javadoc) + * @see org.transitclock.monitoring.MonitorBase#type() + */ + @Override + protected String type() { + return "Active Blocks"; + } +} diff --git a/transitime/src/main/java/org/transitime/monitoring/AgencyMonitor.java b/transitclock/src/main/java/org/transitclock/monitoring/AgencyMonitor.java similarity index 78% rename from transitime/src/main/java/org/transitime/monitoring/AgencyMonitor.java rename to transitclock/src/main/java/org/transitclock/monitoring/AgencyMonitor.java index d7cc3eafa..65ead98dd 100644 --- a/transitime/src/main/java/org/transitime/monitoring/AgencyMonitor.java +++ b/transitclock/src/main/java/org/transitclock/monitoring/AgencyMonitor.java @@ -15,17 +15,17 @@ * along with Transitime.org . If not, see . */ -package org.transitime.monitoring; +package org.transitclock.monitoring; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.utils.EmailSender; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.transitime.utils.EmailSender; - /** * For monitoring whether the core system is working properly. For calling * all of the specific monitoring functions. @@ -35,6 +35,8 @@ */ public class AgencyMonitor { + private final MonitoringService monitoringService; + // So can send out notification email if monitor triggered private final EmailSender emailSender; @@ -50,6 +52,8 @@ public class AgencyMonitor { private static final Logger logger = LoggerFactory .getLogger(AgencyMonitor.class); + private static final String enableSystemMonitoring = System.getProperty("transitclock.enableSystemMonitoring"); + /********************** Member Functions **************************/ /** @@ -61,18 +65,21 @@ public class AgencyMonitor { */ private AgencyMonitor(String agencyId) { emailSender = new EmailSender(); + monitoringService = MonitoringService.getInstance(); // Create all the monitors and add them to the monitors list monitors = new ArrayList(); - monitors.add(new AvlFeedMonitor(emailSender, agencyId)); - monitors.add(new PredictabilityMonitor(emailSender, agencyId)); - monitors.add(new SystemMemoryMonitor(emailSender, agencyId)); - monitors.add(new SystemCpuMonitor(emailSender, agencyId)); - monitors.add(new SystemDiskSpaceMonitor(emailSender, - agencyId)); - monitors.add(new DatabaseMonitor(emailSender, agencyId)); - monitors.add(new DatabaseQueueMonitor(emailSender, - agencyId)); + monitors.add(new AvlFeedMonitor(monitoringService, emailSender, agencyId)); + monitors.add(new PredictabilityMonitor(monitoringService, emailSender, agencyId)); + monitors.add(new DatabaseQueueMonitor(monitoringService, emailSender, agencyId)); + monitors.add(new ActiveBlocksMonitor(monitoringService, emailSender, agencyId)); + if(enableSystemMonitoring != null && enableSystemMonitoring.equalsIgnoreCase("true")){ + monitors.add(new SystemMemoryMonitor(emailSender, agencyId)); + monitors.add(new SystemCpuMonitor(emailSender, agencyId)); + monitors.add(new SystemDiskSpaceMonitor(emailSender, + agencyId)); + monitors.add(new DatabaseMonitor(emailSender, agencyId)); + } } /** diff --git a/transitime/src/main/java/org/transitime/monitoring/AvlFeedMonitor.java b/transitclock/src/main/java/org/transitclock/monitoring/AvlFeedMonitor.java similarity index 74% rename from transitime/src/main/java/org/transitime/monitoring/AvlFeedMonitor.java rename to transitclock/src/main/java/org/transitclock/monitoring/AvlFeedMonitor.java index 897120060..f0c189f34 100644 --- a/transitime/src/main/java/org/transitime/monitoring/AvlFeedMonitor.java +++ b/transitclock/src/main/java/org/transitclock/monitoring/AvlFeedMonitor.java @@ -15,19 +15,19 @@ * along with Transitime.org . If not, see . */ -package org.transitime.monitoring; - -import java.util.List; +package org.transitclock.monitoring; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.config.IntegerConfigValue; -import org.transitime.config.StringConfigValue; -import org.transitime.core.AvlProcessor; -import org.transitime.core.BlocksInfo; -import org.transitime.db.structs.Block; -import org.transitime.utils.EmailSender; -import org.transitime.utils.Time; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.config.StringConfigValue; +import org.transitclock.core.AvlProcessor; +import org.transitclock.core.BlocksInfo; +import org.transitclock.db.structs.Block; +import org.transitclock.utils.EmailSender; +import org.transitclock.utils.Time; + +import java.util.List; /** * For determining if the AVL feed is up. If not getting data when blocks are @@ -40,17 +40,19 @@ */ public class AvlFeedMonitor extends MonitorBase { + private MonitoringService monitoringService; + private static IntegerConfigValue allowableNoAvlSecs = new IntegerConfigValue( - "transitime.monitoring.allowableNoAvlSecs", + "transitclock.monitoring.allowableNoAvlSecs", 5 * Time.SEC_PER_MIN, "How long in seconds that can not receive valid AVL data " + "before monitoring triggers an alert."); private static StringConfigValue avlFeedEmailRecipients = new StringConfigValue( - "transitime.monitoring.avlFeedEmailRecipients", - "monitoring@transitime.org", + "transitclock.monitoring.avlFeedEmailRecipients", + "monitoring@transitclock.org", "Comma separated list of e-mail addresses indicating who " + "should be e-mail when monitor state changes for AVL " + "feed."); @@ -66,13 +68,14 @@ public class AvlFeedMonitor extends MonitorBase { * @param emailSender * @param agencyId */ - public AvlFeedMonitor(EmailSender emailSender, String agencyId) { + public AvlFeedMonitor(MonitoringService monitoringService, EmailSender emailSender, String agencyId) { super(emailSender, agencyId); + this.monitoringService = monitoringService; } /** * Checks GPS time of last AVL report from the AVL feed. If it is recent, as - * specified by transitime.monitoring.allowableAvlFeedTimeNoDataSecs, then + * specified by transitclock.monitoring.allowableAvlFeedTimeNoDataSecs, then * this method returns 0. If no GPS data or the data is too old then returns * age of last AVL report in seconds. * @@ -83,12 +86,14 @@ private int avlFeedOutageSecs() { // Determine age of AVL report long lastAvlReportTime = AvlProcessor.getInstance().lastAvlReportTime(); long ageOfAvlReport = System.currentTimeMillis() - lastAvlReportTime; - - logger.debug("When monitoring AVL feed last AVL report={}", + Double ageOfAvlReportInSecs = new Double(ageOfAvlReport / Time.MS_PER_SEC ); + monitoringService.averageMetric("PredictionLatestAvlReportAgeInSeconds", ageOfAvlReportInSecs); + + logger.debug("When monitoring AVL feed last AVL report={}", AvlProcessor.getInstance().getLastAvlReport()); setMessage("Last valid AVL report was " - + ageOfAvlReport / Time.MS_PER_SEC + + ageOfAvlReport / Time.MS_PER_SEC + " secs old while allowable age is " + allowableNoAvlSecs.getValue() + " secs as specified by " + "parameter " + allowableNoAvlSecs.getID() + " .", @@ -105,7 +110,7 @@ private int avlFeedOutageSecs() { } /* (non-Javadoc) - * @see org.transitime.monitoring.MonitorBase#triggered() + * @see org.transitclock.monitoring.MonitorBase#triggered() */ @Override protected boolean triggered() { @@ -132,7 +137,7 @@ protected boolean acceptableEvenIfTriggered() { } /* (non-Javadoc) - * @see org.transitime.monitoring.MonitorBase#type() + * @see org.transitclock.monitoring.MonitorBase#type() */ @Override protected String type() { @@ -142,7 +147,7 @@ protected String type() { /** * Returns comma separated list of who should be notified via e-mail when * trigger state changes for the monitor. Specified by the Java property - * transitime.monitoring.emailRecipients . Can be overwritten by an + * transitclock.monitoring.emailRecipients . Can be overwritten by an * implementation of a monitor if want different list for a monitor. * * @return E-mail addresses of who to notify diff --git a/transitime/src/main/java/org/transitime/monitoring/DatabaseMonitor.java b/transitclock/src/main/java/org/transitclock/monitoring/DatabaseMonitor.java similarity index 86% rename from transitime/src/main/java/org/transitime/monitoring/DatabaseMonitor.java rename to transitclock/src/main/java/org/transitclock/monitoring/DatabaseMonitor.java index 633c68274..fa675cf47 100644 --- a/transitime/src/main/java/org/transitime/monitoring/DatabaseMonitor.java +++ b/transitclock/src/main/java/org/transitclock/monitoring/DatabaseMonitor.java @@ -15,13 +15,13 @@ * along with Transitime.org . If not, see . */ -package org.transitime.monitoring; +package org.transitclock.monitoring; import java.util.List; import org.hibernate.HibernateException; -import org.transitime.db.structs.DbTest; -import org.transitime.utils.EmailSender; +import org.transitclock.db.structs.DbTest; +import org.transitclock.utils.EmailSender; /** * For monitoring access to database. Makes sure can read and write to database. @@ -44,7 +44,7 @@ public DatabaseMonitor(EmailSender emailSender, String agencyId) { } /* (non-Javadoc) - * @see org.transitime.monitoring.MonitorBase#triggered() + * @see org.transitclock.monitoring.MonitorBase#triggered() */ @Override protected boolean triggered() { @@ -75,7 +75,7 @@ protected boolean triggered() { } /* (non-Javadoc) - * @see org.transitime.monitoring.MonitorBase#type() + * @see org.transitclock.monitoring.MonitorBase#type() */ @Override protected String type() { diff --git a/transitime/src/main/java/org/transitime/monitoring/DatabaseQueueMonitor.java b/transitclock/src/main/java/org/transitclock/monitoring/DatabaseQueueMonitor.java similarity index 74% rename from transitime/src/main/java/org/transitime/monitoring/DatabaseQueueMonitor.java rename to transitclock/src/main/java/org/transitclock/monitoring/DatabaseQueueMonitor.java index 9b9aa125a..1f7194a6d 100644 --- a/transitime/src/main/java/org/transitime/monitoring/DatabaseQueueMonitor.java +++ b/transitclock/src/main/java/org/transitclock/monitoring/DatabaseQueueMonitor.java @@ -15,13 +15,13 @@ * along with Transitime.org . If not, see . */ -package org.transitime.monitoring; +package org.transitclock.monitoring; -import org.transitime.applications.Core; -import org.transitime.config.DoubleConfigValue; -import org.transitime.db.hibernate.DataDbLogger; -import org.transitime.utils.EmailSender; -import org.transitime.utils.StringUtils; +import org.transitclock.applications.Core; +import org.transitclock.config.DoubleConfigValue; +import org.transitclock.db.hibernate.DataDbLogger; +import org.transitclock.utils.EmailSender; +import org.transitclock.utils.StringUtils; /** * For monitoring access to database. Examines size of the db logging queue @@ -32,15 +32,17 @@ */ public class DatabaseQueueMonitor extends MonitorBase { + private MonitoringService monitoringService; + DoubleConfigValue maxQueueFraction = new DoubleConfigValue( - "transitime.monitoring.maxQueueFraction", + "transitclock.monitoring.maxQueueFraction", 0.4, "If database queue fills up by more than this 0.0 - 1.0 " + "fraction then database monitoring is triggered."); private static DoubleConfigValue maxQueueFractionGap = new DoubleConfigValue( - "transitime.monitoring.maxQueueFractionGap", + "transitclock.monitoring.maxQueueFractionGap", 0.1, "When transitioning from triggered to untriggered don't " + "want to send out an e-mail right away if actually " @@ -56,12 +58,13 @@ public class DatabaseQueueMonitor extends MonitorBase { * @param emailSender * @param agencyId */ - public DatabaseQueueMonitor(EmailSender emailSender, String agencyId) { + public DatabaseQueueMonitor(MonitoringService monitoringService, EmailSender emailSender, String agencyId) { super(emailSender, agencyId); + this.monitoringService = monitoringService; } /* (non-Javadoc) - * @see org.transitime.monitoring.MonitorBase#triggered() + * @see org.transitclock.monitoring.MonitorBase#triggered() */ @Override protected boolean triggered() { @@ -78,6 +81,8 @@ protected boolean triggered() { + ", and items in queue=" + dbLogger.queueSize() + ".", dbLogger.queueLevel()); + + monitoringService.averageMetric("PredictionDatabaseQueuePercentageLevel", dbLogger.queueLevel()); // Determine the threshold for triggering. If already triggered // then lower the threshold by maxQueueFractionGap in order @@ -91,7 +96,7 @@ protected boolean triggered() { } /* (non-Javadoc) - * @see org.transitime.monitoring.MonitorBase#type() + * @see org.transitclock.monitoring.MonitorBase#type() */ @Override protected String type() { diff --git a/transitime/src/main/java/org/transitime/monitoring/MonitorBase.java b/transitclock/src/main/java/org/transitclock/monitoring/MonitorBase.java similarity index 91% rename from transitime/src/main/java/org/transitime/monitoring/MonitorBase.java rename to transitclock/src/main/java/org/transitclock/monitoring/MonitorBase.java index e027c4fa0..9b7decd5e 100644 --- a/transitime/src/main/java/org/transitime/monitoring/MonitorBase.java +++ b/transitclock/src/main/java/org/transitclock/monitoring/MonitorBase.java @@ -15,17 +15,17 @@ * along with Transitime.org . If not, see . */ -package org.transitime.monitoring; +package org.transitclock.monitoring; import java.util.Date; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.config.IntegerConfigValue; -import org.transitime.config.StringConfigValue; -import org.transitime.db.structs.MonitoringEvent; -import org.transitime.utils.EmailSender; -import org.transitime.utils.Time; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.config.StringConfigValue; +import org.transitclock.db.structs.MonitoringEvent; +import org.transitclock.utils.EmailSender; +import org.transitclock.utils.Time; /** * Base class for doing monitoring. If event is first triggered then an ERROR @@ -57,13 +57,13 @@ public abstract class MonitorBase { private static StringConfigValue emailRecipients = new StringConfigValue( - "transitime.monitoring.emailRecipients", + "transitclock.monitoring.emailRecipients", "Comma separated list of e-mail addresses indicating who " + "should be e-mailed when monitor state changes."); private static IntegerConfigValue retryTimeoutSecs = new IntegerConfigValue( - "transitime.monitoring.retryTimeoutSecs", + "transitclock.monitoring.retryTimeoutSecs", 5, "How long in seconds system should wait before rexamining " + "monitor. This way a short lived outage can be ignored. " @@ -153,7 +153,7 @@ agencyId, type(), isTriggered, wasTriggered, message, emailSender.send(recipients(), subject, message); } else { logger.error("Could not send ERROR e-mail because " - + "transitime.monitoring.emailRecipients Java property " + + "transitclock.monitoring.emailRecipients Java property " + "not set"); } } else if (wasTriggered && !isTriggered) { @@ -168,7 +168,7 @@ agencyId, type(), isTriggered, wasTriggered, message, emailSender.send(recipients(), subject, message); } else { logger.error("Could not send ERROR e-mail because " - + "transitime.monitoring.emailRecipients Java property " + + "transitclock.monitoring.emailRecipients Java property " + "not set"); } } @@ -273,7 +273,7 @@ protected boolean acceptableEvenIfTriggered() { /** * Returns comma separated list of who should be notified via e-mail when * trigger state changes for the monitor. Specified by the Java property - * transitime.monitoring.emailRecipients . Can be overwritten by an + * transitclock.monitoring.emailRecipients . Can be overwritten by an * implementation of a monitor if want different list for a monitor. * * @return E-mail addresses of who to notify @@ -285,7 +285,7 @@ protected String recipients() { /** * Returns comma separated list of who should be notified via e-mail when * trigger state changes for the monitor. Specified by the Java property - * transitime.monitoring.emailRecipients . A static class so cannot be + * transitclock.monitoring.emailRecipients . A static class so cannot be * overwritten. * * @return E-mail addresses of who to notify diff --git a/transitime/src/main/java/org/transitime/monitoring/MonitorResult.java b/transitclock/src/main/java/org/transitclock/monitoring/MonitorResult.java similarity index 93% rename from transitime/src/main/java/org/transitime/monitoring/MonitorResult.java rename to transitclock/src/main/java/org/transitclock/monitoring/MonitorResult.java index 0153c1635..4b6f561ea 100644 --- a/transitime/src/main/java/org/transitime/monitoring/MonitorResult.java +++ b/transitclock/src/main/java/org/transitclock/monitoring/MonitorResult.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.monitoring; +package org.transitclock.monitoring; import java.io.Serializable; diff --git a/transitime/src/main/java/org/transitime/monitoring/MonitoringModule.java b/transitclock/src/main/java/org/transitclock/monitoring/MonitoringModule.java similarity index 86% rename from transitime/src/main/java/org/transitime/monitoring/MonitoringModule.java rename to transitclock/src/main/java/org/transitclock/monitoring/MonitoringModule.java index c76ed0aa3..cc0f72b52 100644 --- a/transitime/src/main/java/org/transitime/monitoring/MonitoringModule.java +++ b/transitclock/src/main/java/org/transitclock/monitoring/MonitoringModule.java @@ -15,16 +15,16 @@ * along with Transitime.org . If not, see . */ -package org.transitime.monitoring; +package org.transitclock.monitoring; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.config.IntegerConfigValue; -import org.transitime.configData.AgencyConfig; -import org.transitime.logging.Markers; -import org.transitime.modules.Module; -import org.transitime.utils.IntervalTimer; -import org.transitime.utils.Time; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.configData.AgencyConfig; +import org.transitclock.logging.Markers; +import org.transitclock.modules.Module; +import org.transitclock.utils.IntervalTimer; +import org.transitclock.utils.Time; /** * A module that runs in a separate thread and repeatedly uses AgencyMonitor to @@ -32,7 +32,7 @@ * AgencyMonitor is used notification e-mails are automatically sent. *

    * To use with a core project use: - * -Dtransitime.modules.optionalModulesList=org.transitime.monitor.MonitoringModule + * -Dtransitclock.modules.optionalModulesList=org.transitclock.monitor.MonitoringModule * * @author SkiBu Smith * @@ -41,7 +41,7 @@ public class MonitoringModule extends Module { private static IntegerConfigValue secondsBetweenMonitorinPolling = new IntegerConfigValue( - "transitime.monitoring.secondsBetweenMonitorinPolling", + "transitclock.monitoring.secondsBetweenMonitorinPolling", 120, "How frequently an monitoring should be run to look for " + "problems."); diff --git a/transitclock/src/main/java/org/transitclock/monitoring/MonitoringService.java b/transitclock/src/main/java/org/transitclock/monitoring/MonitoringService.java new file mode 100644 index 000000000..538a4b31f --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/monitoring/MonitoringService.java @@ -0,0 +1,266 @@ +package org.transitclock.monitoring; + +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.services.cloudwatch.AmazonCloudWatchClient; +import com.amazonaws.services.cloudwatch.model.MetricDatum; +import com.amazonaws.services.cloudwatch.model.PutMetricDataRequest; +import com.amazonaws.services.cloudwatch.model.StandardUnit; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; +import java.util.concurrent.*; + +/** + * Provide a monitoring service to an external datastore, such as a CSV file or + * CloudWatch if configured + * + * @author dbenoff + * @author sheldonabrown + */ +public class MonitoringService { + private String environmentName = System.getProperty("transitclock.environmentName"); + private String accessKey = System.getProperty("transitclock.cloudwatch.awsAccessKey"); + private String secretKey = System.getProperty("transitclock.cloudwatch.awsSecretKey"); + private String endpoint = System.getProperty("transitclock.cloudwatch.awsEndpoint"); + private AmazonCloudWatchClient cloudWatch; + private ConcurrentHashMap> averageMetrics = new ConcurrentHashMap<>(); + private ConcurrentHashMap> rateMetrics = new ConcurrentHashMap>(); + private ConcurrentHashMap sumMetrics = new ConcurrentHashMap(); + private ScheduledExecutorService executor; + private boolean isCloudWatchInitialized = false; + private static Object singeltonLock = new Object(); + + private static final Logger logger = LoggerFactory + .getLogger(MonitoringService.class); + + private void init() { + environmentName = System.getProperty("transitclock.environmentName"); + accessKey = System.getProperty("transitclock.cloudwatch.awsAccessKey"); + secretKey = System.getProperty("transitclock.cloudwatch.awsSecretKey"); + endpoint = System.getProperty("transitclock.cloudwatch.awsEndpoint"); + logger.info("re-loading system properties env={} awsAccessKey={} awsSecretKey={} awsEndpoint={}", + environmentName, accessKey, secretKey, endpoint); + } + + public void flush() { + publishSimpleMetrics(); + } + + private static MonitoringService singleton; + + private MonitoringService() { + logger.info("MonitoringService service starting up"); + + init(); + + if(StringUtils.isBlank(environmentName) || StringUtils.isBlank(accessKey) || StringUtils.isBlank(secretKey) || StringUtils.isBlank(endpoint)) { + logger.warn("MonitoringService monitoring not enabled, please specify environmentName, accessKey, secretKey and endpoint in configuration file"); + }else{ + logger.info("starting MonitoringService in env {} with accessKey {} and pass {}", environmentName, accessKey, secretKey); + AmazonCloudWatchClient cloudWatch = new AmazonCloudWatchClient(new BasicAWSCredentials(accessKey, secretKey)); + cloudWatch.setEndpoint(endpoint); + this.cloudWatch = cloudWatch; + isCloudWatchInitialized = true; + } + + // start up threads regardless of cloudwatch status so logging is available + executor = Executors.newSingleThreadScheduledExecutor(); + executor.scheduleAtFixedRate(new PublishMetricsTask(), 0, 1, TimeUnit.MINUTES); + + } + + /** + * Returns the singleton MonitoringService + * + * @return + */ + public static MonitoringService getInstance() { + if(singleton == null) { + synchronized (singeltonLock) { + // check to see if we won the race + if (singleton == null) + singleton = new MonitoringService(); + } + } + return singleton; + } + + + /** + * increment the metric sum + * @param metricName the metric to increment + */ + public void sumMetric(String metricName) { + synchronized (sumMetrics) { + Double sum = sumMetrics.get(metricName); + if (sum == null) { + sum = 0.0; + } + sum = sum + 1.0; + sumMetrics.put(metricName, sum); + } + } + + /** + * provide another value to average into a rolling average metric + * @param metricName the rolling average metric + * @param metricValue the value to merge in + */ + public void averageMetric(String metricName, double metricValue) { + synchronized (averageMetrics) { + List metrics = averageMetrics.get(metricName); + if (metrics == null) { + metrics = new ArrayList<>(); + } + metrics.add(metricValue); + averageMetrics.put(metricName, metrics); + } + } + + /** + * track a rate (such as cache miss/hit rate) and the overall usage count. + * @param metricName + * @param hit + */ + public void rateMetric(String metricName, boolean hit) { + double metricValue = (hit? 1.0: 0.0); + synchronized (rateMetrics) { + List metrics = rateMetrics.get(metricName); + if (metrics == null) { + metrics = new ArrayList<>(); + } + metrics.add(metricValue); + rateMetrics.put(metricName, metrics); + } + + } + + private void publishMetrics(List metrics) { + if (metrics == null || metrics.isEmpty()) + return; + + List remainingMetrics = publishOnly20Metrics(metrics); + while (!remainingMetrics.isEmpty()) { + remainingMetrics = publishOnly20Metrics(remainingMetrics); + } + } + + private List publishOnly20Metrics(List metrics) { + if (metrics == null || metrics.isEmpty()) return metrics; + + try { + int i = 0; + List only20 = new ArrayList<>(20); + Iterator iterator = metrics.iterator(); + // CloudWatch API limits us to 20 metrics in a batch at a time + while (iterator.hasNext() && i < 20) { + i++; + MetricDatum datum = (MetricDatum) iterator.next(); + if (datum != null && datum.getValue() != null) { + only20.add(datum); + logger.info("{},{},{}", datum.getUnit(), datum.getMetricName(), datum.getValue()); + } else { + logger.info("discarding empty metric {}", datum.getMetricName()); + } + iterator.remove(); + } + if (isCloudWatchInitialized) { + PutMetricDataRequest putMetricDataRequest = new PutMetricDataRequest() + .withNamespace(environmentName) + .withMetricData(only20); + cloudWatch.putMetricData(putMetricDataRequest); + } + + } catch (Exception any) { + logger.error("exception publishing for {}, {}", metrics, any, any); + } + return metrics; + } + + private MetricDatum toDatumCount(String metricName, double metricValue) { + return toDatum(metricName, metricValue, StandardUnit.Count); + } + private MetricDatum toDatumPercent(String metricName, double metricValue) { + return toDatum(metricName, metricValue, StandardUnit.Percent); + } + + private MetricDatum toDatum(String metricName, double metricValue, StandardUnit unit) { + Date timestamp = new Date(); + + MetricDatum datum = new MetricDatum(). + withMetricName(metricName). + withTimestamp(timestamp). + withValue(metricValue). + withUnit(unit); + return datum; + } + + private void publishSimpleMetrics() { + long start = System.currentTimeMillis(); + List simpleMetrics = new ArrayList<>(); + + synchronized (averageMetrics) { + for (String key : averageMetrics.keySet()) { + List metrics = averageMetrics.get(key); + if (metrics != null && !metrics.isEmpty()) { + int size = metrics.size(); + double total = 0.0; + for (double d : metrics) { + total += d; + } + simpleMetrics.add(toDatumCount(key, total/size)); + } + } + averageMetrics.clear(); + } + + synchronized (rateMetrics) { + for (String key : rateMetrics.keySet()) { + List metrics = rateMetrics.get(key); + if (metrics != null && !metrics.isEmpty()) { + int size = metrics.size(); + double total = 0.0; + for (double d : metrics) { + total += d; + } + simpleMetrics.add(toDatumPercent(key + "Rate", total/size)); + simpleMetrics.add(toDatumCount(key, total)); + } + } + rateMetrics.clear(); + } + synchronized (sumMetrics) { + for (String key : sumMetrics.keySet()) { + Double sum = sumMetrics.get(key); + simpleMetrics.add(toDatumCount(key, sum)); + } + sumMetrics.clear(); + } + + int size = simpleMetrics.size(); + publishMetrics(simpleMetrics); + long end = System.currentTimeMillis(); + logger.info("published {} metrics in {}ms", size, (end - start)); + + } + + private class PublishMetricsTask implements Runnable { + @Override + public void run() { + publishSimpleMetrics(); + } + } + + public static void main(String[] args){ + MonitoringService cloudwatchService = MonitoringService.getInstance(); + int i = 0; + while (i < 100){ + cloudwatchService.averageMetric("testing", Math.random()); + i++; + } + cloudwatchService.flush(); + } +} diff --git a/transitime/src/main/java/org/transitime/monitoring/PidFile.java b/transitclock/src/main/java/org/transitclock/monitoring/PidFile.java similarity index 95% rename from transitime/src/main/java/org/transitime/monitoring/PidFile.java rename to transitclock/src/main/java/org/transitclock/monitoring/PidFile.java index a6229f446..2633082ef 100644 --- a/transitime/src/main/java/org/transitime/monitoring/PidFile.java +++ b/transitclock/src/main/java/org/transitclock/monitoring/PidFile.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.monitoring; +package org.transitclock.monitoring; import java.io.IOException; import java.io.OutputStream; diff --git a/transitime/src/main/java/org/transitime/monitoring/PredictabilityMonitor.java b/transitclock/src/main/java/org/transitclock/monitoring/PredictabilityMonitor.java similarity index 72% rename from transitime/src/main/java/org/transitime/monitoring/PredictabilityMonitor.java rename to transitclock/src/main/java/org/transitclock/monitoring/PredictabilityMonitor.java index f69228686..e2c670e7c 100644 --- a/transitime/src/main/java/org/transitime/monitoring/PredictabilityMonitor.java +++ b/transitclock/src/main/java/org/transitclock/monitoring/PredictabilityMonitor.java @@ -15,19 +15,19 @@ * along with Transitime.org . If not, see . */ -package org.transitime.monitoring; +package org.transitclock.monitoring; import java.util.ArrayList; import java.util.Collection; import java.util.List; -import org.transitime.config.DoubleConfigValue; -import org.transitime.config.IntegerConfigValue; -import org.transitime.core.BlocksInfo; -import org.transitime.core.dataCache.VehicleDataCache; -import org.transitime.db.structs.Block; -import org.transitime.utils.EmailSender; -import org.transitime.utils.StringUtils; +import org.transitclock.config.DoubleConfigValue; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.core.BlocksInfo; +import org.transitclock.core.dataCache.VehicleDataCache; +import org.transitclock.db.structs.Block; +import org.transitclock.utils.EmailSender; +import org.transitclock.utils.StringUtils; /** * Monitors how many vehicles are predictable compared to how many active blocks @@ -38,16 +38,18 @@ */ public class PredictabilityMonitor extends MonitorBase { + private MonitoringService monitoringService; + private static DoubleConfigValue minPredictableBlocks = new DoubleConfigValue( - "transitime.monitoring.minPredictableBlocks", + "transitclock.monitoring.minPredictableBlocks", 0.50, "The minimum fraction of currently active blocks that " + "should have a predictable vehicle"); private static DoubleConfigValue minPredictableBlocksGap = new DoubleConfigValue( - "transitime.monitoring.minPredictableBlocksGap", + "transitclock.monitoring.minPredictableBlocksGap", 0.25, "When transitioning from triggered to untriggered don't " + "want to send out an e-mail right away if actually " @@ -57,7 +59,7 @@ public class PredictabilityMonitor extends MonitorBase { private static IntegerConfigValue minimumPredictableVehicles = new IntegerConfigValue( - "transitime.monitoring.minimumPredictableVehicles", + "transitclock.monitoring.minimumPredictableVehicles", 3, "When looking at small number of vehicles it is too easy " + "to get below minimumPredictableBlocks. So number of " @@ -72,8 +74,9 @@ public class PredictabilityMonitor extends MonitorBase { * @param emailSender * @param agencyId */ - public PredictabilityMonitor(EmailSender emailSender, String agencyId) { + public PredictabilityMonitor(MonitoringService monitoringService, EmailSender emailSender, String agencyId) { super(emailSender, agencyId); + this.monitoringService = monitoringService; } /** @@ -87,7 +90,6 @@ public PredictabilityMonitor(EmailSender emailSender, String agencyId) { private double fractionBlocksPredictable(double threshold) { // For creating message List activeBlocksWithoutVehicle = new ArrayList(); - // Determine number of currently active blocks. // If there are no currently active blocks then don't need to be // getting AVL data so return 0 @@ -95,6 +97,7 @@ private double fractionBlocksPredictable(double threshold) { if (activeBlocks.size() == 0) { setMessage("No currently active blocks so predictability " + "considered to be OK."); + monitoringService.averageMetric("PredictionPredictablePercentageOfBlocks", 1d); return 1.0; } @@ -113,8 +116,12 @@ private double fractionBlocksPredictable(double threshold) { // Determine fraction of active blocks that have a predictable vehicle double fraction = ((double) Math.max(predictableVehicleCount, - minimumPredictableVehicles.getValue())) / activeBlocks.size(); - + minimumPredictableVehicles.getValue())) / activeBlocks.size(); + monitoringService.averageMetric("PredictionActiveBlockCount", (double)Math.max(predictableVehicleCount, + minimumPredictableVehicles.getValue())); + monitoringService.averageMetric("PredictionTotalBlockCount", (double)activeBlocks.size()); + monitoringService.averageMetric("PredictablePercentageOfBlocks", fraction); + // Provide simple message explaining the situation String message = "Predictable blocks fraction=" + StringUtils.twoDigitFormat(fraction) @@ -127,18 +134,21 @@ private double fractionBlocksPredictable(double threshold) { minimumPredictableVehicles.getValue()) + ")."; - // Add all the active block IDs to the message so can more easily see - // what is going on - StringBuilder sb = new StringBuilder(); - sb.append(" Currently active blocks without vehicles: "); - for (Block block : activeBlocksWithoutVehicle) { - sb.append("block=") - .append(block.getId()) - .append(", serviceId=") - .append(block.getServiceId()) - .append("; "); + // If below the threshold then add all the active block IDs to the + // message so can more easily see + if (fraction < threshold) { + StringBuilder sb = new StringBuilder(); + sb.append(" Currently active blocks without vehicles: "); + for (Block block : activeBlocksWithoutVehicle) { + sb.append("block=") + .append(block.getId()) + .append(", serviceId=") + .append(block.getServiceId()) + .append("; "); + } + message += sb.toString(); } - message += sb.toString(); + setMessage(message, fraction); @@ -147,7 +157,7 @@ private double fractionBlocksPredictable(double threshold) { } /* (non-Javadoc) - * @see org.transitime.monitoring.MonitorBase#triggered() + * @see org.transitclock.monitoring.MonitorBase#triggered() */ @Override protected boolean triggered() { @@ -165,7 +175,7 @@ protected boolean triggered() { } /* (non-Javadoc) - * @see org.transitime.monitoring.MonitorBase#type() + * @see org.transitclock.monitoring.MonitorBase#type() */ @Override protected String type() { diff --git a/transitime/src/main/java/org/transitime/monitoring/SystemCpuMonitor.java b/transitclock/src/main/java/org/transitclock/monitoring/SystemCpuMonitor.java similarity index 91% rename from transitime/src/main/java/org/transitime/monitoring/SystemCpuMonitor.java rename to transitclock/src/main/java/org/transitclock/monitoring/SystemCpuMonitor.java index fc33eca98..66821c083 100644 --- a/transitime/src/main/java/org/transitime/monitoring/SystemCpuMonitor.java +++ b/transitclock/src/main/java/org/transitclock/monitoring/SystemCpuMonitor.java @@ -15,17 +15,17 @@ * along with Transitime.org . If not, see . */ -package org.transitime.monitoring; +package org.transitclock.monitoring; import java.util.Calendar; import java.util.GregorianCalendar; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.config.DoubleConfigValue; -import org.transitime.utils.EmailSender; -import org.transitime.utils.StringUtils; -import org.transitime.utils.Time; +import org.transitclock.config.DoubleConfigValue; +import org.transitclock.utils.EmailSender; +import org.transitclock.utils.StringUtils; +import org.transitclock.utils.Time; /** * Monitors to make sure that server CPU is not too high. @@ -36,14 +36,14 @@ public class SystemCpuMonitor extends MonitorBase { DoubleConfigValue cpuThreshold = new DoubleConfigValue( - "transitime.monitoring.cpuThreshold", + "transitclock.monitoring.cpuThreshold", 0.99, "If CPU load averaged over a minute exceeds this 0.0 - 1.0 " + "value then CPU monitoring is triggered."); private static DoubleConfigValue cpuThresholdGap = new DoubleConfigValue( - "transitime.monitoring.cpuThresholdGap", + "transitclock.monitoring.cpuThresholdGap", 0.1, "When transitioning from triggered to untriggered don't " + "want to send out an e-mail right away if actually " @@ -67,7 +67,7 @@ public SystemCpuMonitor(EmailSender emailSender, String agencyId) { } /* (non-Javadoc) - * @see org.transitime.monitoring.MonitorBase#triggered() + * @see org.transitclock.monitoring.MonitorBase#triggered() */ /** * Sees if recent CPU load is higher than value specified by cpuThreshold. @@ -138,7 +138,7 @@ protected boolean triggered() { } /* (non-Javadoc) - * @see org.transitime.monitoring.MonitorBase#type() + * @see org.transitclock.monitoring.MonitorBase#type() */ @Override protected String type() { diff --git a/transitime/src/main/java/org/transitime/monitoring/SystemDiskSpaceMonitor.java b/transitclock/src/main/java/org/transitclock/monitoring/SystemDiskSpaceMonitor.java similarity index 87% rename from transitime/src/main/java/org/transitime/monitoring/SystemDiskSpaceMonitor.java rename to transitclock/src/main/java/org/transitclock/monitoring/SystemDiskSpaceMonitor.java index 33b118c01..477f520f5 100644 --- a/transitime/src/main/java/org/transitime/monitoring/SystemDiskSpaceMonitor.java +++ b/transitclock/src/main/java/org/transitclock/monitoring/SystemDiskSpaceMonitor.java @@ -15,13 +15,13 @@ * along with Transitime.org . If not, see . */ -package org.transitime.monitoring; +package org.transitclock.monitoring; import java.io.File; -import org.transitime.config.LongConfigValue; -import org.transitime.utils.EmailSender; -import org.transitime.utils.StringUtils; +import org.transitclock.config.LongConfigValue; +import org.transitclock.utils.EmailSender; +import org.transitclock.utils.StringUtils; /** * Monitors to make sure there is sufficient disk space. @@ -32,14 +32,14 @@ public class SystemDiskSpaceMonitor extends MonitorBase { private LongConfigValue usableDiskSpaceThreshold = new LongConfigValue( - "transitime.monitoring.usableDiskSpaceThreshold", + "transitclock.monitoring.usableDiskSpaceThreshold", 1024 * 1024 * 1024L, // ~1 GB "If usable disk space is less than this " + "value then file space monitoring is triggered."); private static LongConfigValue usableDiskSpaceThresholdGap = new LongConfigValue( - "transitime.monitoring.usableDiskSpaceThresholdGap", + "transitclock.monitoring.usableDiskSpaceThresholdGap", 100 * 1024 * 1024L, // ~100 MB "When transitioning from triggered to untriggered don't " + "want to send out an e-mail right away if actually " @@ -60,7 +60,7 @@ public SystemDiskSpaceMonitor(EmailSender emailSender, String agencyId) { } /* (non-Javadoc) - * @see org.transitime.monitoring.MonitorBase#triggered() + * @see org.transitclock.monitoring.MonitorBase#triggered() */ /** * Checks whether file system getting too full, beyond @@ -93,7 +93,7 @@ protected boolean triggered() { } /* (non-Javadoc) - * @see org.transitime.monitoring.MonitorBase#type() + * @see org.transitclock.monitoring.MonitorBase#type() */ @Override protected String type() { diff --git a/transitime/src/main/java/org/transitime/monitoring/SystemMemoryMonitor.java b/transitclock/src/main/java/org/transitclock/monitoring/SystemMemoryMonitor.java similarity index 90% rename from transitime/src/main/java/org/transitime/monitoring/SystemMemoryMonitor.java rename to transitclock/src/main/java/org/transitclock/monitoring/SystemMemoryMonitor.java index d7600fca2..10f1b189a 100644 --- a/transitime/src/main/java/org/transitime/monitoring/SystemMemoryMonitor.java +++ b/transitclock/src/main/java/org/transitclock/monitoring/SystemMemoryMonitor.java @@ -15,16 +15,16 @@ * along with Transitime.org . If not, see . */ -package org.transitime.monitoring; +package org.transitclock.monitoring; import java.lang.management.ManagementFactory; import java.lang.management.OperatingSystemMXBean; import java.lang.reflect.Method; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.config.LongConfigValue; -import org.transitime.utils.EmailSender; -import org.transitime.utils.StringUtils; +import org.transitclock.config.LongConfigValue; +import org.transitclock.utils.EmailSender; +import org.transitclock.utils.StringUtils; /** * For monitoring CPU, available memory, and available disk space. @@ -41,7 +41,7 @@ public class SystemMemoryMonitor extends MonitorBase { LongConfigValue availableFreePhysicalMemoryThreshold = new LongConfigValue( - "transitime.monitoring.availableFreePhysicalMemoryThreshold", + "transitclock.monitoring.availableFreePhysicalMemoryThreshold", 10 * 1024 * 1024L, // ~10 MB "If available free physical memory is less than this " + "value then free memory monitoring is triggered. This should be " @@ -52,7 +52,7 @@ public class SystemMemoryMonitor extends MonitorBase { private static LongConfigValue availableFreePhysicalMemoryThresholdGap = new LongConfigValue( - "transitime.monitoring.availableFreePhysicalMemoryThresholdGap", + "transitclock.monitoring.availableFreePhysicalMemoryThresholdGap", 150 * 1024 * 1024L, // ~150 MB "When transitioning from triggered to untriggered don't " + "want to send out an e-mail right away if actually " @@ -108,7 +108,7 @@ public static Object getOperatingSystemValue(String methodName) { } /* (non-Javadoc) - * @see org.transitime.monitoring.MonitorBase#triggered() + * @see org.transitclock.monitoring.MonitorBase#triggered() */ /** * Sees if recent available memory is lower than value specified by @@ -152,7 +152,7 @@ protected boolean triggered() { } /* (non-Javadoc) - * @see org.transitime.monitoring.MonitorBase#type() + * @see org.transitclock.monitoring.MonitorBase#type() */ @Override protected String type() { diff --git a/transitime/src/main/java/org/transitime/monitoring/package-info.java b/transitclock/src/main/java/org/transitclock/monitoring/package-info.java similarity index 95% rename from transitime/src/main/java/org/transitime/monitoring/package-info.java rename to transitclock/src/main/java/org/transitclock/monitoring/package-info.java index a476161e9..7e555461b 100644 --- a/transitime/src/main/java/org/transitime/monitoring/package-info.java +++ b/transitclock/src/main/java/org/transitclock/monitoring/package-info.java @@ -21,4 +21,4 @@ * @author SkiBu Smith * */ -package org.transitime.monitoring; \ No newline at end of file +package org.transitclock.monitoring; \ No newline at end of file diff --git a/transitclock/src/main/java/org/transitclock/reporting/RouteStatistics.java b/transitclock/src/main/java/org/transitclock/reporting/RouteStatistics.java new file mode 100644 index 000000000..15beefaa9 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/reporting/RouteStatistics.java @@ -0,0 +1,33 @@ +package org.transitclock.reporting; + +public class RouteStatistics { + private int early; + private int onTime; + private int late; + + public RouteStatistics() { } + + public void addEarly(){ + ++this.early; + } + + public void addOnTime(){ + ++this.onTime; + } + + public void addLate(){ + ++this.late; + } + + public int getEarlyCount() { + return early; + } + + public int getOnTimeCount() { + return onTime; + } + + public int getLateCount() { + return late; + } +} diff --git a/transitclock/src/main/java/org/transitclock/reporting/StopPathStatistics.java b/transitclock/src/main/java/org/transitclock/reporting/StopPathStatistics.java new file mode 100644 index 000000000..2f7fde5c0 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/reporting/StopPathStatistics.java @@ -0,0 +1,52 @@ +package org.transitclock.reporting; + +import java.util.DoubleSummaryStatistics; + +public class StopPathStatistics { + private final String tripId; + private final String stopPathId; + private final int stopPathIndex; + private final boolean isLastStop; + private final boolean isFirstStop; + + DoubleSummaryStatistics dwellTimeStats = new DoubleSummaryStatistics(); + DoubleSummaryStatistics runTimeStats = new DoubleSummaryStatistics(); + + public StopPathStatistics(String tripId, String stopPathId, int stopPathIndex, boolean isLastStop) { + this.tripId = tripId; + this.stopPathId = stopPathId; + this.stopPathIndex = stopPathIndex; + this.isLastStop = isLastStop; + this.isFirstStop = stopPathIndex == 0; + } + + public DoubleSummaryStatistics getDwellTimeStats() { + return dwellTimeStats; + } + + public DoubleSummaryStatistics getRunTimeStats() { + return runTimeStats; + } + + public Double getAverageRunTime() { + return runTimeStats.getAverage(); + } + + public Double getMinRunTime() { + return runTimeStats.getMin(); + } + + public Double getAverageDwellTime() { + return dwellTimeStats.getAverage(); + } + + public long getCount() { return runTimeStats.getCount(); } + + public boolean isLastStop() { + return isLastStop; + } + + public boolean isFirstStop() { + return isFirstStop; + } +} diff --git a/transitclock/src/main/java/org/transitclock/reporting/TripStatistics.java b/transitclock/src/main/java/org/transitclock/reporting/TripStatistics.java new file mode 100644 index 000000000..70281d7f4 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/reporting/TripStatistics.java @@ -0,0 +1,241 @@ +package org.transitclock.reporting; + +import org.transitclock.db.structs.ArrivalDeparture; +import org.transitclock.db.structs.StopPath; +import org.transitclock.db.structs.Trip; +import org.transitclock.reporting.keys.StopPathRunTimeKey; + +import java.util.*; +import java.util.stream.Collectors; + +public class TripStatistics { + + private Trip trip; + private int tripIndex; + private String blockId; + private int expectedStopPathCount; + Map stopPathsGroupedById = new HashMap<>(); + + private Set uniqueStopPathRunTimes= new LinkedHashSet<>(); + private Set uniqueStopPathDwellTimes=new LinkedHashSet<>(); + private Map stopPathStatistics = new HashMap<>(); + + public TripStatistics(Trip trip, int tripIndex) { + this.trip = trip; + this.tripIndex = tripIndex; + this.expectedStopPathCount = trip.getNumberStopPaths() - 1; + stopPathsGroupedById = trip.getStopPaths().stream() + .collect(Collectors.toMap(StopPath::getId, stopPath -> stopPath)); + } + + /** + * Method to collect stopPath run-times + * @param ad + * @param runTime + */ + public void addStopPathRunTime(ArrivalDeparture ad, Double runTime){ + if(ad.getTripId().equals(trip.getId()) + && stopPathsGroupedById.get(ad.getStopPathId()) != null){ + StopPathRunTimeKey stopPathkey = new StopPathRunTimeKey(ad.getStopId(), ad.getStopPathId(), ad.getStopPathIndex()); + addStopPathRunTime(stopPathkey, ad, runTime); + } + } + + /** + * Method to collect stopPath dwell-times + * @param ad + * @param dwellTime + */ + public void addStopPathDwellTime(ArrivalDeparture ad, Double dwellTime){ + if(ad.getTripId().equals(trip.getId()) + && ad.getStopPathIndex() < expectedStopPathCount + && stopPathsGroupedById.get(ad.getStopPathId()) != null){ + StopPathRunTimeKey stopPathkey = new StopPathRunTimeKey(ad.getStopId(), ad.getStopPathId(), ad.getStopPathIndex()); + addStopPathDwellTime(stopPathkey, ad, dwellTime); + } + } + + + public Map getAllStopPathStatistics() { + return stopPathStatistics; + } + + public StopPathStatistics getStopPathStatistics(String stopId, String stopPathId, Integer stopPathIndex) { + return stopPathStatistics.get(new StopPathRunTimeKey(stopId, stopPathId, stopPathIndex)); + } + + public Trip getTrip() { + return trip; + } + + public int getTripIndex() { + return tripIndex; + } + + public Double getTripAverageRunTime(){ + double tripRunTime = 0; + if(hasAllStopPathsForRunTimes()){ + for(Map.Entry entry : stopPathStatistics.entrySet()){ + StopPathStatistics sps = entry.getValue(); + Double avgRunTime = sps.getAverageRunTime(); + if(avgRunTime == null){ + return null; + } + tripRunTime += avgRunTime; + } + return tripRunTime; + } + return null; + } + + public Double getTripFixedRunTime(){ + double tripFixedRunTime = 0; + if(hasAllStopPathsForRunTimes()){ + for(Map.Entry entry : stopPathStatistics.entrySet()){ + StopPathStatistics sps = entry.getValue(); + if(sps.isFirstStop()){ + continue; + } + Double minRunTime = sps.getMinRunTime(); + if(minRunTime == null){ + return null; + } + tripFixedRunTime += minRunTime; + } + return tripFixedRunTime; + } + return null; + } + + public Double getTripAvgDwellTime(){ + double tripDwellTime = 0; + if(hasAllStopPathsForDwellTimes()){ + for(Map.Entry entry : stopPathStatistics.entrySet()){ + StopPathStatistics sps = entry.getValue(); + Double avgDwellTime = sps.getAverageDwellTime(); + if(sps.isLastStop()){ + continue; + } + else if(avgDwellTime == null){ + return null; + } + tripDwellTime += avgDwellTime; + } + return tripDwellTime; + } + return null; + } + + public Map getStopPathAverageRunTime(){ + if(hasAllStopPathsForRunTimes()){ + Map stopPathAverageRunTime = new LinkedHashMap<>(); + for(Map.Entry entry : stopPathStatistics.entrySet()){ + StopPathStatistics sps = entry.getValue(); + Double avgRunTime = sps.getAverageRunTime(); + if(avgRunTime == null){ + return null; + } + stopPathAverageRunTime.put(entry.getKey(), avgRunTime); + } + return stopPathAverageRunTime; + } + return null; + } + + public Map getStopPathAvgDwellTime(){ + if(hasAllStopPathsForDwellTimes()){ + Map stopPathAvgDwellTime = new LinkedHashMap<>(); + for(Map.Entry entry : stopPathStatistics.entrySet()){ + StopPathStatistics sps = entry.getValue(); + Double avgDwellTime = sps.getAverageDwellTime(); + if(sps.isLastStop()){ + continue; + } + else if(avgDwellTime == null){ + return null; + } + stopPathAvgDwellTime.put(entry.getKey(), avgDwellTime); + } + return stopPathAvgDwellTime; + } + return null; + } + + public Map getStopPathFixedRunTime(){ + if(hasAllStopPathsForRunTimes()){ + Map stopPathFixedRunTime = new LinkedHashMap<>(); + for(Map.Entry entry : stopPathStatistics.entrySet()){ + StopPathStatistics sps = entry.getValue(); + if(sps.isFirstStop()){ + continue; + } + Double minRunTime = sps.getMinRunTime(); + if(minRunTime == null){ + return null; + } + stopPathFixedRunTime.put(entry.getKey(),minRunTime); + } + return stopPathFixedRunTime; + } + return null; + } + + public long getTotalTrips(){ + if(hasAllStopPathsForRunTimes()){ + long count = 0; + for(Map.Entry entry : stopPathStatistics.entrySet()){ + StopPathStatistics sps = entry.getValue(); + if(sps.getCount() > count){ + count = sps.getCount(); + } + } + return count; + } + return 0; + } + + + public String getTripId() { + return trip.getId(); + } + + public String getBlockId() { + return trip.getBlockId(); + } + + public int getExpectedStopPathCount() { + return expectedStopPathCount; + } + + public boolean hasAllStopPathsForRunTimes(){ + return uniqueStopPathRunTimes.size() == expectedStopPathCount; + } + + public boolean hasAllStopPathsForDwellTimes(){ + return uniqueStopPathDwellTimes.size() == expectedStopPathCount; + } + + private void addStopPathRunTime(StopPathRunTimeKey key, ArrivalDeparture ad, Double runTime){ + StopPathStatistics result = getStatsMapResult(key, ad); + result.getRunTimeStats().accept(runTime); + uniqueStopPathRunTimes.add(key); + } + + private void addStopPathDwellTime(StopPathRunTimeKey key, ArrivalDeparture ad, Double dwellTime){ + StopPathStatistics result = getStatsMapResult(key, ad); + result.getDwellTimeStats().accept(dwellTime); + uniqueStopPathDwellTimes.add(key); + } + + private StopPathStatistics getStatsMapResult(StopPathRunTimeKey key, ArrivalDeparture ad){ + StopPathStatistics result = stopPathStatistics.get(key); + if(result == null){ + boolean isLastStop = ad.getStopPathIndex() == expectedStopPathCount; + result = new StopPathStatistics(ad.getTripId(), ad.getStopPathId(), ad.getStopPathIndex(), isLastStop); + stopPathStatistics.put(key, result); + } + return result; + } + + +} diff --git a/transitclock/src/main/java/org/transitclock/reporting/dao/RunTimeRoutesDao.java b/transitclock/src/main/java/org/transitclock/reporting/dao/RunTimeRoutesDao.java new file mode 100644 index 000000000..f0613c12f --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/reporting/dao/RunTimeRoutesDao.java @@ -0,0 +1,130 @@ +package org.transitclock.reporting.dao; + +import org.hibernate.HibernateException; +import org.hibernate.Query; +import org.hibernate.Session; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.applications.Core; +import org.transitclock.core.ServiceType; +import org.transitclock.db.hibernate.HibernateUtils; +import org.transitclock.db.query.RunTimeForRouteQuery; +import org.transitclock.db.structs.RunTimesForRoutes; +import org.transitclock.utils.IntervalTimer; + +import java.sql.Timestamp; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class RunTimeRoutesDao { + + private static final Logger logger = + LoggerFactory.getLogger(RunTimeRoutesDao.class); + + /** + * + * @param rtQuery + * @return + * @throws Exception + */ + public List getRunTimesForRoutes(RunTimeForRouteQuery rtQuery) throws Exception { + IntervalTimer timer = new IntervalTimer(); + + // Get the database session. This is supposed to be pretty light weight + Session session = HibernateUtils.getSession(rtQuery.isReadOnly()); + + Map parameterNameAndValues = new HashMap<>(); + + String hql = "SELECT " + + "rt " + + "FROM " + + "RunTimesForRoutes rt " + + "WHERE " + + getTimeRange(rtQuery, parameterNameAndValues) + + getServiceTypeWhere(rtQuery, parameterNameAndValues) + + "ORDER BY rt.routeShortName, rt.startTime DESC"; + + try { + Query query = session.createQuery(hql); + for (Map.Entry e : parameterNameAndValues.entrySet()) { + query.setParameter(e.getKey(), e.getValue()); + } + List results = query.list(); + + logger.debug("Getting runtimes for routes from database took {} msec", + timer.elapsedMsec()); + + return results; + + } catch (HibernateException e) { + Core.getLogger().error("Unable to retrieve runtimes for routes", e); + return Collections.EMPTY_LIST; + } finally { + // Clean things up. Not sure if this absolutely needed nor if + // it might actually be detrimental and slow things down. + session.close(); + } + } + + public static String getTimeRange(RunTimeForRouteQuery rtQuery, Map parameterNameAndValues) { + String hql = ""; + + Integer beginTime = rtQuery.getBeginTime(); + Integer endTime = rtQuery.getEndTime(); + LocalDateTime beginDate; + LocalDateTime endDate = null; + + // Set Default Values For Dates + if(rtQuery.getBeginDate() == null) { + beginDate = LocalDate.now().atStartOfDay(); + } + else { + beginDate = rtQuery.getBeginDate().atStartOfDay(); + } + + if(rtQuery.getEndDate() != null) { + endDate = rtQuery.getEndDate().atTime(LocalTime.MAX).plusHours(3); + } + + // Set Query and Params for Dates + parameterNameAndValues.put("beginDate", Timestamp.valueOf(beginDate)); + + if (endDate != null && endDate.isAfter(beginDate)) { + hql += " rt.startTime BETWEEN :beginDate AND :endDate "; + parameterNameAndValues.put("endDate", Timestamp.valueOf(endDate)); + } else if (endDate != null && endDate.isEqual(beginDate)) { + endDate = endDate.plusDays(1); + hql += " rt.startTime BETWEEN :beginDate AND :endDate "; + parameterNameAndValues.put("endDate", Timestamp.valueOf(endDate)); + } else if (endDate == null || endDate.isBefore(beginDate)) { + hql += " rt.startTime >= :beginDate "; + } + + // Set Default Values for Time + beginTime = beginTime != null ? beginTime : 0; + endTime = endTime != null ? endTime : Integer.MAX_VALUE; + + parameterNameAndValues.put("beginTime", beginTime); + parameterNameAndValues.put("endTime", endTime); + + // Set Query and Params for Times + hql += "AND rt.scheduledStartTime BETWEEN :beginTime AND :endTime "; + + return hql; + } + + private static String getServiceTypeWhere(RunTimeForRouteQuery rtQuery, Map parameterNameAndValues){ + String hql = ""; + ServiceType serviceType = rtQuery.getServiceType(); + if(serviceType != null) { + hql += " AND rt.serviceType = :serviceType "; + parameterNameAndValues.put("serviceType", serviceType); + } + return hql; + } +} diff --git a/transitclock/src/main/java/org/transitclock/reporting/keys/ArrivalDepartureTripKey.java b/transitclock/src/main/java/org/transitclock/reporting/keys/ArrivalDepartureTripKey.java new file mode 100644 index 000000000..c0466ec67 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/reporting/keys/ArrivalDepartureTripKey.java @@ -0,0 +1,43 @@ +package org.transitclock.reporting.keys; + +import org.transitclock.db.structs.ArrivalDeparture; +import org.transitclock.utils.MapKey; + +import java.time.ZoneId; +import java.util.Date; + +import static org.transitclock.ipc.util.GtfsTimeUtil.dayOfYearForTrip; + +public class ArrivalDepartureTripKey extends MapKey { + public ArrivalDepartureTripKey(String serviceId, + Integer dayOfYear, + String tripId, + String vehicleId) { + super(serviceId, dayOfYear, tripId, vehicleId); + } + + public static ArrivalDepartureTripKey getKey(final String serviceId, final Date date, + final String tripId, final String vehicleId, ZoneId zone) { + return new ArrivalDepartureTripKey(serviceId, dayOfYearForTrip(date, zone), tripId, vehicleId); + } + + public static ArrivalDepartureTripKey getKey(final ArrivalDeparture ad, final ZoneId zoneId) { + return new ArrivalDepartureTripKey(ad.getServiceId(), dayOfYearForTrip(ad.getDate(), zoneId), + ad.getTripId(), ad.getVehicleId()); + } + + public String getTripId(){ + return o3.toString(); + } + + @Override + public String toString() { + return "DbDataMapKey [" + + "serviceId=" + o1 + + ", dayOfYear=" + o2 + + ", tripId=" + o3 + + ", vehicleId=" + o4 + + "]"; + } + +} diff --git a/transitclock/src/main/java/org/transitclock/reporting/keys/StopPathRunTimeKey.java b/transitclock/src/main/java/org/transitclock/reporting/keys/StopPathRunTimeKey.java new file mode 100644 index 000000000..142b8291a --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/reporting/keys/StopPathRunTimeKey.java @@ -0,0 +1,33 @@ +package org.transitclock.reporting.keys; + +import org.transitclock.utils.MapKey; + +public class StopPathRunTimeKey extends MapKey { + public StopPathRunTimeKey(String stopId, String stopPathId, Integer stopPathIndex){ + super(stopId, stopPathId, stopPathIndex); + } + + public String getStopId(){ + return (String) o1; + } + + public String getStopPathId(){ + return (String) o2; + } + + public Integer getStopPathIndex(){ + if(o3 != null) { + return (Integer) o3; + } + return null; + } + + @Override + public String toString() { + return "StopPathRunTimeKey [" + + "stopId=" + o1 + + ", stopPathId=" + o2 + + ", stopPathIndex=" + o3 + + "]"; + } +} diff --git a/transitclock/src/main/java/org/transitclock/reporting/keys/TripDateKey.java b/transitclock/src/main/java/org/transitclock/reporting/keys/TripDateKey.java new file mode 100644 index 000000000..67a50d22f --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/reporting/keys/TripDateKey.java @@ -0,0 +1,37 @@ +package org.transitclock.reporting.keys; + +import com.google.common.base.Objects; + +import java.time.LocalDate; + +public class TripDateKey{ + private final String tripId; + private final LocalDate date; + + public TripDateKey(String tripId, LocalDate date){ + this.tripId = tripId; + this.date = date; + } + + public String getTripId() { + return tripId; + } + + public LocalDate getDate() { + return date; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TripDateKey that = (TripDateKey) o; + return Objects.equal(tripId, that.tripId) && + Objects.equal(date, that.date); + } + + @Override + public int hashCode() { + return Objects.hashCode(tripId, date); + } +} diff --git a/transitclock/src/main/java/org/transitclock/reporting/service/OnTimePerformanceService.java b/transitclock/src/main/java/org/transitclock/reporting/service/OnTimePerformanceService.java new file mode 100644 index 000000000..1fc4df713 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/reporting/service/OnTimePerformanceService.java @@ -0,0 +1,62 @@ +package org.transitclock.reporting.service; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.core.ServiceType; +import org.transitclock.db.query.ArrivalDepartureQuery; +import org.transitclock.db.structs.ArrivalDeparture; +import org.transitclock.ipc.data.IpcArrivalDepartureScheduleAdherence; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.ArrayList; +import java.util.List; + +import static org.transitclock.ipc.util.GtfsDbDataUtil.*; + +public class OnTimePerformanceService { + + private static final Logger logger = + LoggerFactory.getLogger(OnTimePerformanceService.class); + + public List getArrivalsDeparturesForOtp( + LocalDate beginDate, LocalDate endDate, LocalTime beginTime, LocalTime endTime, + String routeIdOrShortName, ServiceType serviceType, + boolean timePointsOnly, String headsign) throws Exception{ + return getArrivalsDeparturesForOtp(beginDate, endDate, beginTime, endTime, routeIdOrShortName, serviceType, + timePointsOnly, headsign, false); + } + + public List getArrivalsDeparturesForOtp( + LocalDate beginDate, LocalDate endDate, LocalTime beginTime, LocalTime endTime, + String routeIdOrShortName, ServiceType serviceType, boolean timePointsOnly, + String headsign, boolean readOnly) throws Exception { + + String routeShortName = getRouteShortName(routeIdOrShortName); + + boolean scheduledStopsOnly = true; + + ArrivalDepartureQuery.Builder adBuilder = new ArrivalDepartureQuery.Builder(); + ArrivalDepartureQuery adQuery = adBuilder + .beginDate(beginDate) + .endDate(endDate) + .beginTime(beginTime) + .endTime(endTime) + .routeShortName(routeShortName) + .headsign(headsign) + .serviceType(serviceType) + .timePointsOnly(timePointsOnly) + .scheduledTimesOnly(scheduledStopsOnly) + .readOnly(readOnly) + .build(); + + List arrivalDepartures = ArrivalDeparture.getArrivalsDeparturesFromDb(adQuery); + List ipcArrivalDepartures = new ArrayList<>(); + + for(ArrivalDeparture arrivalDeparture : arrivalDepartures){ + IpcArrivalDepartureScheduleAdherence ipcArrivalDeparture = new IpcArrivalDepartureScheduleAdherence(arrivalDeparture); + ipcArrivalDepartures.add(ipcArrivalDeparture); + } + return ipcArrivalDepartures; + } +} diff --git a/transitclock/src/main/java/org/transitclock/reporting/service/RunTimeService.java b/transitclock/src/main/java/org/transitclock/reporting/service/RunTimeService.java new file mode 100644 index 000000000..e3c557bc6 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/reporting/service/RunTimeService.java @@ -0,0 +1,821 @@ +package org.transitclock.reporting.service; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.core.ServiceType; +import org.transitclock.core.TemporalDifference; +import org.transitclock.core.travelTimes.TravelTimesProcessor; +import org.transitclock.db.query.ArrivalDepartureQuery; +import org.transitclock.db.query.RunTimeForRouteQuery; +import org.transitclock.db.query.TripQuery; +import org.transitclock.db.structs.*; +import org.transitclock.ipc.data.*; +import org.transitclock.reporting.RouteStatistics; +import org.transitclock.reporting.dao.RunTimeRoutesDao; +import org.transitclock.reporting.keys.StopPathRunTimeKey; +import org.transitclock.reporting.StopPathStatistics; +import org.transitclock.reporting.TripStatistics; +import org.transitclock.ipc.data.IpcDoubleSummaryStatistics; +import org.transitclock.ipc.data.IpcRunTime; +import org.transitclock.ipc.data.IpcRunTimeForTrip; +import org.transitclock.reporting.keys.ArrivalDepartureTripKey; +import org.transitclock.reporting.keys.TripDateKey; +import org.transitclock.utils.IntervalTimer; + +import javax.inject.Inject; +import java.time.*; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.transitclock.configData.ReportingConfig.*; +import static org.transitclock.ipc.util.GtfsDbDataUtil.*; + +public class RunTimeService { + + private static final Logger logger = + LoggerFactory.getLogger(RunTimeService.class); + + @Inject + private RunTimeRoutesDao dao; + + /** + * Get avg run time for all trips in a route for a specified period of time + * + * @param beginDate + * @param endDate + * @param beginTime + * @param endTime + * @param routeIdOrShortName + * @param serviceType + * @param timePointsOnly + * @param headsign + * @param readOnly + * @return + * @throws Exception + */ + public IpcDoubleSummaryStatistics getAverageRunTime(LocalDate beginDate, + LocalDate endDate, + LocalTime beginTime, + LocalTime endTime, + String routeIdOrShortName, + ServiceType serviceType, + boolean timePointsOnly, + String headsign, + boolean readOnly) throws Exception { + + String routeShortName = getRouteShortName(routeIdOrShortName); + + ArrivalDepartureQuery.Builder adBuilder = new ArrivalDepartureQuery.Builder(); + ArrivalDepartureQuery adQuery = adBuilder + .beginDate(beginDate) + .endDate(endDate) + .beginTime(beginTime) + .endTime(endTime) + .routeShortName(routeShortName) + .headsign(headsign) + .serviceType(serviceType) + .timePointsOnly(timePointsOnly) + .scheduledTimesOnly(true) + .includeTrip(true) + .readOnly(readOnly) + .build(); + + List arrivalDepartures = ArrivalDeparture.getArrivalsDeparturesFromDb(adQuery); + + Map runTimeByTripId = getRunTimeByTripDateKey(arrivalDepartures); + + return getAverageRunTimeForAllTrips(runTimeByTripId); + } + + /* + * Calculate the trip run times from the list of arrival departures + * Currently only gets run times for trips with arrival/departure for first and last stops + * Trips that do not include first and last stop in the selected time range GET CUT OFF + * + * TODO - May want to refactor to be smarter about including the entire trip if + * the trip is part of the selected time range + */ + private Map getRunTimeByTripDateKey(List arrivalDepartures){ + + Map runTimeByTripDate = new LinkedHashMap<>(); + + Map> arrivalDeparturesByTripDateKey = getArrivalDeparturesByTripDateKey(arrivalDepartures); + + for(Map.Entry> arrivalDepartureByTripAndDate : arrivalDeparturesByTripDateKey.entrySet()){ + TripDateKey key = arrivalDepartureByTripAndDate.getKey(); + List arrivalDeparturesForTrip = arrivalDepartureByTripAndDate.getValue(); + + if(arrivalDeparturesForTrip != null && arrivalDeparturesForTrip.size() > 0){ + Long firstStopDepartureTime = null; + Long lastStopArrivalTime = null; + + Trip trip = arrivalDeparturesForTrip.get(0).getTripFromDb(); + int lastStopPathIndex = trip.getNumberStopPaths() - 1; + + ArrivalDeparture firstDeparture = arrivalDeparturesForTrip.get(0); + if(firstDeparture.getStopPathIndex() == 0 && firstDeparture.isDeparture()){ + firstStopDepartureTime = firstDeparture.getTime(); + } else { + continue; + } + + for(int i=1; i< arrivalDeparturesForTrip.size(); i++){ + ArrivalDeparture ad = arrivalDeparturesForTrip.get(i); + if(ad.getStopPathIndex() == lastStopPathIndex && ad.isArrival()){ + lastStopArrivalTime = ad.getTime(); + break; + } + } + + if(firstStopDepartureTime != null && lastStopArrivalTime != null){ + runTimeByTripDate.put(key,lastStopArrivalTime - firstStopDepartureTime); + } + } + } + + return runTimeByTripDate; + } + + /* + * Groups Arrival Departures by Trip/Date key + * Allows us to retrieve first and last arrival/departure per trip + */ + private Map> getArrivalDeparturesByTripDateKey(List arrivalDepartures){ + Map> arrivalDeparturesByTripKey = new LinkedHashMap<>(); + + for(ArrivalDeparture ad : arrivalDepartures){ + LocalDate localDate = Instant.ofEpochMilli(ad.getTime()).atZone(ZoneId.systemDefault()).toLocalDate(); + TripDateKey tripKey = new TripDateKey(ad.getTripId(), localDate); + if(arrivalDeparturesByTripKey.get(tripKey) == null){ + arrivalDeparturesByTripKey.put(tripKey, new ArrayList<>()); + } + arrivalDeparturesByTripKey.get(tripKey).add(ad); + } + return arrivalDeparturesByTripKey; + } + + /* + * Get summary statistics for trip/date run times + */ + private IpcDoubleSummaryStatistics getAverageRunTimeForAllTrips(Map runTimeByTripId) { + DoubleSummaryStatistics summaryStatistics = runTimeByTripId.entrySet().stream() + .mapToDouble(Map.Entry::getValue) + .summaryStatistics(); + + return new IpcDoubleSummaryStatistics(summaryStatistics); + } + + + public IpcRunTime getRunTimeSummary(LocalDate beginDate, + LocalDate endDate, + LocalTime beginTime, + LocalTime endTime, + String routeIdOrShortName, + String headsign, + String startStop, + String endStop, + ServiceType serviceType, + boolean timePointsOnly, + boolean currentTripsOnly, + String agencyId, + boolean readOnly) throws Exception { + + String routeShortName = getRouteShortName(routeIdOrShortName); + + ArrivalDepartureQuery.Builder adBuilder = new ArrivalDepartureQuery.Builder(); + ArrivalDepartureQuery adQuery = adBuilder + .beginDate(beginDate) + .endDate(endDate) + .beginTime(beginTime) + .endTime(endTime) + .routeShortName(routeShortName) + .headsign(headsign) + .startStop(startStop) + .endStop(endStop) + .serviceType(serviceType) + .timePointsOnly(timePointsOnly) + .includeTrip(true) + .readOnly(readOnly) + .build(); + + List arrivalDepartures = ArrivalDeparture.getArrivalsDeparturesFromDb(adQuery); + + Set configRevs = new HashSet<>(); + Map> arrivalDeparturesByTripMap = + groupArrivalDeparturesByUniqueTrip(arrivalDepartures, agencyId); + + Collection> arrivalDeparturesByTrip = arrivalDeparturesByTripMap.values(); + + Map tripStatsByRunTimeKey = new HashMap<>(); + for(List arrivalDepartureList : arrivalDeparturesByTrip){ + processTripStatsMap(tripStatsByRunTimeKey, arrivalDepartureList); + } + + IpcRunTime runTime = getRunTimeStats(tripStatsByRunTimeKey); + return runTime; + } + + private Map> groupArrivalDeparturesByUniqueTrip( + List arrivalDepartures, + String agencyId){ + + TimeZone tz = Agency.getTimeZoneFromDb(agencyId); + Map> arrivalDeparturesByTrip = new HashMap<>(); + + for(ArrivalDeparture ad: arrivalDepartures){ + ArrivalDepartureTripKey key = ArrivalDepartureTripKey.getKey(ad, tz.toZoneId()); + List list = arrivalDeparturesByTrip.get(key); + if (list == null) { + list = new ArrayList<>(); + arrivalDeparturesByTrip.put(key, list); + } + list.add(ad); + } + return arrivalDeparturesByTrip; + } + + /** + * Store TripStatistics in map with tripId as key + * This is done by going through list of ArrivalsDepartures grouped by TripId and populating stopPath info + * for TripStastics. Its keyed off Trip so it will collect run time information for all trips that share the same + * trip Id. This is inspired by TravelTimesProcessor. + * @param tripStatsByTripId + * @param arrDepList + */ + private void processTripStatsMap(Map tripStatsByTripId, + List arrDepList) { + + for (int i=0; i tripStatsByTripId, + ArrivalDeparture arrDep1, + ArrivalDeparture arrDep2) { + + Double travelTimeForStopPath = null; + Double dwellTime = null; + long departureTime = arrDep1.getTime(); + + if (TravelTimesProcessor.shouldResetEarlyTerminalDepartures() + && arrDep1.isDeparture() + && arrDep1.getStopPathIndex() == 0 + && arrDep1.getTime() < arrDep1.getScheduledTime()) { + logger.debug("Note: for {} using scheduled departure time instead " + + "of the calculated departure time since " + + "transitclock.travelTimes.resetEarlyTerminalDepartures is " + + "true and the departure time was (likely incorrectly) " + + "calculated to be before the scheduled departure time", + arrDep1); + departureTime = arrDep1.getScheduledTime(); + } + + // If schedule adherence is really far off then ignore the data + // point because it would skew the results. + TemporalDifference schedAdh = arrDep1.getScheduleAdherence(); + if (schedAdh == null) + schedAdh = arrDep2.getScheduleAdherence(); + if (schedAdh != null + && !schedAdh.isWithinBounds(getMaxSchedAdh(), + getMaxSchedAdh())) { + // Schedule adherence is off so don't use this data + return ; + } + + // If looking at arrival and departure for same stop then continue + if (arrDep1.getStopPathIndex() == arrDep2.getStopPathIndex() + && arrDep1.isArrival() + && arrDep2.isDeparture()) { + if(arrDep1.getStopPathIndex() == 0){ + long expectedDepartureTime = arrDep2.getScheduledTime() >= arrDep1.getTime() ? arrDep2.getScheduledTime() : arrDep1.getTime(); + double firstDwellTime = (double) (arrDep2.getTime() - expectedDepartureTime); + // check for case where trip left before expected departure time + if(firstDwellTime < 0){ + dwellTime = 1000.0; + } else { + dwellTime = firstDwellTime; + } + }else { + dwellTime = (double) (arrDep2.getTime() - arrDep1.getTime()); + } + if (dwellTime == null) + return; + addStopPathDwellTimeToMap(tripStatsByTripId, arrDep2, dwellTime); + } + + // If looking at departure from one stop to the arrival time at the + // very next stop then can determine the travel times between the stops. + else if (arrDep1.getStopPathIndex() - arrDep2.getStopPathIndex() != 1 + && arrDep1.isDeparture() + && arrDep2.isArrival()) { + // Determine the travel times and add them to the map + travelTimeForStopPath = (double) (arrDep2.getTime() - departureTime); + + // Ignore a stop path if any segment travel time is negative. Nulls will + // be ignored downstream anyway so can also ignore those. + if (travelTimeForStopPath == null) + return; + + addStopPathRunTimeToMap(tripStatsByTripId, arrDep2, travelTimeForStopPath); + } + } + + private void addStopPathRunTimeToMap(Map tripStatsByTripId, + ArrivalDeparture ad, + Double runTime){ + TripStatistics tps = getTripStats(tripStatsByTripId, ad); + tps.addStopPathRunTime(ad, runTime); + } + + private void addStopPathDwellTimeToMap(Map tripStatsByTripId, + ArrivalDeparture ad, + Double dwellTime){ + TripStatistics tps = getTripStats(tripStatsByTripId, ad); + tps.addStopPathDwellTime(ad, dwellTime); + } + + private TripStatistics getTripStats(Map tripStatsByTripId, + ArrivalDeparture ad){ + String key = ad.getTripId(); + TripStatistics tps = tripStatsByTripId.get(key); + if(tps == null){ + tps = new TripStatistics(ad.getTripFromDb(), ad.getTripIndex()); + tripStatsByTripId.put(key, tps); + } + return tps; + } + + private IpcRunTime getRunTimeStats(Map tripStatsByRunTimeKey){ + DoubleSummaryStatistics avgRunTimeStats = new DoubleSummaryStatistics(); + DoubleSummaryStatistics fixedTimeStats = new DoubleSummaryStatistics(); + DoubleSummaryStatistics avgDwellTimeStats = new DoubleSummaryStatistics(); + + for(Map.Entry entry : tripStatsByRunTimeKey.entrySet()){ + + Double avgTripRunTime = entry.getValue().getTripAverageRunTime(); + if(avgTripRunTime != null && avgTripRunTime > 0){ + avgRunTimeStats.accept(avgTripRunTime); + } + + Double tripFixedTime = entry.getValue().getTripFixedRunTime(); + if(tripFixedTime != null && tripFixedTime > 0){ + fixedTimeStats.accept(tripFixedTime); + } + + Double tripDwellTime = entry.getValue().getTripAvgDwellTime(); + if(tripDwellTime != null && tripDwellTime > 0){ + avgDwellTimeStats.accept(tripDwellTime); + } + + } + + Double avgRunTime = avgRunTimeStats.getCount() > 0 ? avgRunTimeStats.getAverage() : null; + Double fixedTime = fixedTimeStats.getCount() > 0 ? fixedTimeStats.getMin() : null; + Double dwellTime = avgDwellTimeStats.getCount() > 0 ? avgDwellTimeStats.getAverage() : null; + Double variableTime = null; + + + if(avgRunTime != null && fixedTime != null) { + variableTime = avgRunTime - fixedTime; + } + + return new IpcRunTime(avgRunTime, fixedTime, variableTime, dwellTime); + } + + /** + * Get RunTime for Group of Trips + * @param beginDate + * @param endDate + * @param beginTime + * @param endTime + * @param routeIdOrShortName + * @param headsign + * @param tripPatternId + * @param serviceType + * @param timePointsOnly + * @param currentTripsOnly + * @param agencyId + * @param readOnly + * @return + * @throws Exception + */ + public List getRunTimeForTrips(LocalDate beginDate, + LocalDate endDate, + LocalTime beginTime, + LocalTime endTime, + String routeIdOrShortName, + String headsign, + String tripPatternId, + ServiceType serviceType, + boolean timePointsOnly, + boolean currentTripsOnly, + String agencyId, + boolean readOnly) throws Exception { + + String routeShortName = getRouteShortName(routeIdOrShortName); + + // Step 1 - Get Relevant Trips for Specified Time Range Grouped By Trip Id + + Map scheduledTripsGroupedById = getScheduledTripsGroupedByIdForDateRange(routeShortName, + headsign,beginDate,endDate,beginTime,endTime,readOnly); + + if(scheduledTripsGroupedById == null || scheduledTripsGroupedById.size() == 0){ + return null; + } + + // Step 2 - Get Arrival/Departures filtering by tripIds specified in Step 1 + ArrivalDepartureQuery.Builder adBuilder = new ArrivalDepartureQuery.Builder(); + ArrivalDepartureQuery adQuery = adBuilder + .beginDate(beginDate) + .endDate(endDate) + .beginTime(beginTime) + .endTime(endTime) + .routeShortName(routeShortName) + .headsign(headsign) + .tripIds(scheduledTripsGroupedById.keySet()) + .tripPatternId(tripPatternId) + .serviceType(serviceType) + .timePointsOnly(timePointsOnly) + .includeTrip(true) + .readOnly(readOnly) + .build(); + + List arrivalDepartures = ArrivalDeparture.getArrivalsDeparturesFromDb(adQuery); + + + if(arrivalDepartures.size() > 0) { + + Map> arrivalDeparturesByTripMap = + groupArrivalDeparturesByUniqueTrip(arrivalDepartures, agencyId); + + + Collection> arrivalDeparturesByTrip = arrivalDeparturesByTripMap.values(); + + Map tripStatsByTripId = new HashMap<>(); + for (List arrivalDepartureList : arrivalDeparturesByTrip) { + processTripStatsMap(tripStatsByTripId, arrivalDepartureList); + } + + List runTime = getRunTimeStatsForTrips(tripStatsByTripId); + + return runTime; + } + return Collections.EMPTY_LIST; + } + + + private Map getScheduledTripsGroupedByIdForDateRange(String routeShortName, + String headSign, + LocalDate beginDate, + LocalDate endDate, + LocalTime beginTime, + LocalTime endTime, + boolean readOnly){ + + LocalDateTime configRevEndDate = LocalDateTime.of(endDate, endTime); + List configRevisions = ConfigRevision.getConfigRevisionsForMaxDate(configRevEndDate, readOnly); + + Set configRevisionIds = configRevisions.stream() + .map(c -> c.getConfigRev()) + .collect(Collectors.toSet()); + + + TripQuery.Builder tripBuilder = new TripQuery.Builder(routeShortName, configRevisionIds); + tripBuilder.headsign(headSign); + tripBuilder.firstStartTime(beginTime); + tripBuilder.lastStartTime(endTime); + + List scheduledTrips = Trip.getTripsFromDb(tripBuilder.build()); + + Map scheduledTripsGroupedById = scheduledTrips.stream() + .collect(Collectors.toMap(Trip::getId, Function.identity())); + + return scheduledTripsGroupedById; + } + + private List getRunTimeStatsForTrips(Map tripStatsByTripId){ + + List ipcRunTimeForTrips = new ArrayList<>(); + + + // Loop through each TripStats grouped by Trip Id + for(Map.Entry tripStatEntry : tripStatsByTripId.entrySet()){ + + TripStatistics tripStatistics = tripStatEntry.getValue(); + + //Validation -- Make sure trip is complete + if(!tripStatistics.hasAllStopPathsForRunTimes() || !tripStatistics.hasAllStopPathsForDwellTimes()){ + continue; + } + + //////////////////////////////////////////////// + + + // Looping through the stoppath stats + Map stopPathStatsMap = tripStatistics.getAllStopPathStatistics(); + + for(Map.Entry stopPathEntry : stopPathStatsMap.entrySet()){ + // Combining stop path values + StopPathStatistics sps = stopPathStatsMap.get(stopPathEntry.getKey()); + if(sps != null){ + sps.getRunTimeStats().combine(stopPathEntry.getValue().getRunTimeStats()); + sps.getDwellTimeStats().combine(stopPathEntry.getValue().getDwellTimeStats()); + } + } + + DoubleSummaryStatistics avgRunTimeStats = new DoubleSummaryStatistics(); + DoubleSummaryStatistics fixedTimeStats = new DoubleSummaryStatistics(); + DoubleSummaryStatistics avgDwellTimeStats = new DoubleSummaryStatistics(); + + Double avgTripRunTime = tripStatistics.getTripAverageRunTime(); + if(avgTripRunTime != null && avgTripRunTime > 0){ + avgRunTimeStats.accept(avgTripRunTime); + } + + Double tripFixedTime = tripStatistics.getTripFixedRunTime(); + if(tripFixedTime != null && tripFixedTime > 0){ + fixedTimeStats.accept(tripFixedTime); + } + + Double tripDwellTime = tripStatistics.getTripAvgDwellTime(); + if(tripDwellTime != null && tripDwellTime > 0){ + avgDwellTimeStats.accept(tripDwellTime); + } + + Double avgRunTime = avgRunTimeStats.getCount() > 0 ? avgRunTimeStats.getAverage() : null; + Double fixedTime = fixedTimeStats.getCount() > 0 ? fixedTimeStats.getMin() : null; + Double dwellTime = avgDwellTimeStats.getCount() > 0 ? avgDwellTimeStats.getAverage() : null; + Double variableTime = null; + + if(avgRunTime != null && fixedTime != null) { + variableTime = avgRunTime - fixedTime; + } + + // Get Current Trip and Next Trip Info + Trip currentTrip = tripStatistics.getTrip(); + Trip nextTrip = null; + + // Commenting out for now - takes too long to lookup + //Trip nextTrip = currentTrip.getBlockFromDb(readOnly).getTripFromDb(tripStatistics.getTripIndex() + 1, readOnly); + + Integer nextTripStartTime = null; + if(nextTrip != null){ + nextTripStartTime = nextTrip.getStartTime(); + } + + ipcRunTimeForTrips.add(new IpcRunTimeForTrip(currentTrip.getId(), + currentTrip.getStartTime(), + currentTrip.getEndTime(), + nextTripStartTime, + avgRunTime, + fixedTime, + variableTime, + dwellTime)); + } + + return ipcRunTimeForTrips; + } + + /** + * Get RunTime Information for Trip Stop Paths + * @param beginDate + * @param endDate + * @param beginTime + * @param endTime + * @param routeIdOrShortName + * @param tripId + * @param serviceType + * @param timePointsOnly + * @param agencyId + * @param readOnly + * @return + */ + public List getRunTimeForStopPaths(LocalDate beginDate, + LocalDate endDate, + LocalTime beginTime, + LocalTime endTime, + String routeIdOrShortName, + String tripId, + ServiceType serviceType, + boolean timePointsOnly, + String agencyId, + boolean readOnly) throws Exception { + + String routeShortName = getRouteShortName(routeIdOrShortName); + + // Step 1 - Get Relevant Trips for Specified Time Range Grouped By Trip Id + + Set tripIds = Stream.of(tripId).collect(Collectors.toCollection(HashSet::new)); + + // Step 2 - Get Arrival/Departures filtering by tripIds specified in Step 1 + ArrivalDepartureQuery.Builder adBuilder = new ArrivalDepartureQuery.Builder(); + ArrivalDepartureQuery adQuery = adBuilder + .beginDate(beginDate) + .endDate(endDate) + .beginTime(beginTime) + .endTime(endTime) + .routeShortName(routeShortName) + .tripIds(tripIds) + .serviceType(serviceType) + .timePointsOnly(timePointsOnly) + .includeTrip(true) + .readOnly(readOnly) + .build(); + + List arrivalDepartures = ArrivalDeparture.getArrivalsDeparturesFromDb(adQuery); + + if(arrivalDepartures.size() > 0) { + + Map> arrivalDeparturesByTripMap = + groupArrivalDeparturesByUniqueTrip(arrivalDepartures, agencyId); + + + Collection> arrivalDeparturesByTrip = arrivalDeparturesByTripMap.values(); + + Map tripStatsByTripId = new HashMap<>(); + for (List arrivalDepartureList : arrivalDeparturesByTrip) { + processTripStatsMap(tripStatsByTripId, arrivalDepartureList); + } + + List runTime = getRunTimeStatsForStopPaths(tripStatsByTripId); + + return runTime; + } + + return null; + } + + private List getRunTimeStatsForStopPaths(Map tripStatsByTripId){ + + List ipcRunTimeForStopPaths = new ArrayList<>(); + + + // Loop through each TripStats grouped by Trip Id + for(Map.Entry tripStatEntry : tripStatsByTripId.entrySet()){ + + TripStatistics tripStatistics = tripStatEntry.getValue(); + + //Validation -- Make sure trip is complete + if(!tripStatistics.hasAllStopPathsForRunTimes() || !tripStatistics.hasAllStopPathsForDwellTimes()){ + continue; + } + + // Looping through the stoppath stats + Map stopPathStatsMap = tripStatistics.getAllStopPathStatistics(); + + for(Map.Entry stopPathEntry : stopPathStatsMap.entrySet()){ + + StopPathRunTimeKey stopPathRunTimeKey = stopPathEntry.getKey(); + StopPathStatistics stopPathStatistics = stopPathEntry.getValue(); + + DoubleSummaryStatistics avgRunTimeStats = new DoubleSummaryStatistics(); + DoubleSummaryStatistics fixedTimeStats = new DoubleSummaryStatistics(); + DoubleSummaryStatistics avgDwellTimeStats = new DoubleSummaryStatistics(); + + Double avgStopPathRunTime = stopPathStatistics.getAverageRunTime(); + if(avgStopPathRunTime != null && avgStopPathRunTime > 0){ + avgRunTimeStats.accept(avgStopPathRunTime); + } + + Double stopPathFixedTime = stopPathStatistics.getMinRunTime(); + if(stopPathFixedTime != null && stopPathFixedTime > 0){ + fixedTimeStats.accept(stopPathFixedTime); + } + + Double stopPathDwellTime = stopPathStatistics.getAverageDwellTime(); + if(stopPathDwellTime != null && stopPathDwellTime > 0){ + avgDwellTimeStats.accept(stopPathDwellTime); + } + + Double avgRunTime = avgRunTimeStats.getCount() > 0 ? avgRunTimeStats.getAverage() : null; + Double fixedTime = fixedTimeStats.getCount() > 0 ? fixedTimeStats.getMin() : null; + Double dwellTime = avgDwellTimeStats.getCount() > 0 ? avgDwellTimeStats.getAverage() : null; + Double variableTime = null; + + if(avgRunTime != null && fixedTime != null) { + variableTime = avgRunTime - fixedTime; + } + + + ipcRunTimeForStopPaths.add( + new IpcRunTimeForStopPath( + stopPathRunTimeKey.getStopPathId(), + stopPathRunTimeKey.getStopPathIndex(), + null, + null, + avgRunTime, + fixedTime, + variableTime, + dwellTime + ) + ); + + } + + + } + + return ipcRunTimeForStopPaths; + } + + /** + * Get RunTime Information for Trip Stop Paths + * @param beginDate + * @param endDate + * @param beginTime + * @param endTime + * @param serviceType + * @param readOnly + * @return + */ + public List getRunTimeForRoutes(LocalDate beginDate, + LocalDate endDate, + LocalTime beginTime, + LocalTime endTime, + ServiceType serviceType, + Integer earlyThreshold, + Integer lateThreshold, + boolean readOnly) throws Exception { + + + Integer beginTimeSeconds = beginTime != null ? beginTime.toSecondOfDay() : null; + Integer endTimeSeconds = endTime != null ? endTime.toSecondOfDay(): null; + + RunTimeForRouteQuery.Builder rtBuilder = new RunTimeForRouteQuery.Builder(); + RunTimeForRouteQuery rtQuery = rtBuilder + .beginDate(beginDate) + .endDate(endDate) + .beginTime(beginTimeSeconds) + .endTime(endTimeSeconds) + .serviceType(serviceType) + .readOnly(readOnly) + .build(); + + List runTimesForRoutes = dao.getRunTimesForRoutes(rtQuery); + + Map routeStatisticsByRouteName = getRouteRunTimeStatistics(runTimesForRoutes, earlyThreshold, lateThreshold); + + List ipcRunTimeForRoutes = getRunTimeStatsForRoutes(routeStatisticsByRouteName); + + return ipcRunTimeForRoutes; + } + + private Map getRouteRunTimeStatistics(List runTimesForRoutes, + Integer earlyThresholdSec, + Integer lateThresholdSec) { + Map routeStatisticsByRouteName = new HashMap<>(); + for(RunTimesForRoutes rt : runTimesForRoutes){ + if(rt.getRouteShortName() == null || rt.getEndTime() == null || rt.getStartTime() == null || + rt.getScheduledStartTime() == null || rt.getScheduledEndTime() == null){ + continue; + } + RouteStatistics routeStatistics = routeStatisticsByRouteName.get(rt.getRouteShortName()); + if(routeStatistics == null){ + routeStatistics = new RouteStatistics(); + routeStatisticsByRouteName.put(rt.getRouteShortName(), routeStatistics); + } + long runTimeSec = TimeUnit.MILLISECONDS.toSeconds(rt.getEndTime().getTime() - rt.getStartTime().getTime()); + int scheduledRunTimeSec = rt.getScheduledEndTime() - rt.getScheduledStartTime(); + long runTimeDiff = runTimeSec - scheduledRunTimeSec; + if(runTimeDiff < 0 && Math.abs(runTimeDiff) > earlyThresholdSec){ + routeStatistics.addEarly(); + } + else if(runTimeDiff > 0 && runTimeDiff > lateThresholdSec){ + routeStatistics.addLate(); + } + else { + routeStatistics.addOnTime(); + } + } + + return routeStatisticsByRouteName; + } + + + private List getRunTimeStatsForRoutes(Map routeRunTimeStatistics) { + List ipcRunTimeForRoutes = new ArrayList<>(); + for(Map.Entry rtStats : routeRunTimeStatistics.entrySet()){ + String routeName = rtStats.getKey(); + RouteStatistics stats = rtStats.getValue(); + ipcRunTimeForRoutes.add(new IpcRunTimeForRoute(routeName, stats.getEarlyCount(), stats.getOnTimeCount(), + stats.getLateCount())); + + } + return ipcRunTimeForRoutes; + } +} diff --git a/transitclock/src/main/java/org/transitclock/reporting/service/SpeedMapService.java b/transitclock/src/main/java/org/transitclock/reporting/service/SpeedMapService.java new file mode 100644 index 000000000..da354d6bc --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/reporting/service/SpeedMapService.java @@ -0,0 +1,288 @@ +package org.transitclock.reporting.service; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.core.ServiceType; +import org.transitclock.core.TemporalDifference; +import org.transitclock.db.query.ArrivalDepartureQuery; +import org.transitclock.db.structs.Agency; +import org.transitclock.db.structs.ArrivalDeparture; +import org.transitclock.db.structs.Stop; +import org.transitclock.db.structs.StopPath; +import org.transitclock.ipc.data.IpcStopPathWithSpeed; +import org.transitclock.ipc.data.IpcStopWithDwellTime; +import org.transitclock.reporting.keys.ArrivalDepartureTripKey; +import org.transitclock.utils.Geo; +import org.transitclock.utils.IntervalTimer; +import org.transitclock.utils.Time; + +import javax.inject.Inject; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZoneId; +import java.util.*; + +import static org.transitclock.configData.ReportingConfig.*; +import static org.transitclock.ipc.util.GtfsDbDataUtil.*; +import static org.transitclock.ipc.util.GtfsTimeUtil.dayOfYearForTrip; + +public class SpeedMapService { + + private static final Logger logger = + LoggerFactory.getLogger(SpeedMapService.class); + + + /** + * + * Method to get a list of stops paths and their avg speed for a specified period of time + * + * @param beginDate + * @param endDate + * @param beginTime + * @param endTime + * @param routeIdOrShortName + * @param serviceType + * @param headsign + * @param agencyId + * @param readOnly + * @return + * @throws Exception + */ + public List getStopPathsWithSpeed(LocalDate beginDate, LocalDate endDate, + LocalTime beginTime, LocalTime endTime, + String routeIdOrShortName, ServiceType serviceType, + String headsign, String agencyId, boolean readOnly) throws Exception{ + + String routeShortName = getRouteShortName(routeIdOrShortName); + + ArrivalDepartureQuery.Builder adBuilder = new ArrivalDepartureQuery.Builder(); + ArrivalDepartureQuery adQuery = adBuilder + .beginDate(beginDate) + .endDate(endDate) + .beginTime(beginTime) + .endTime(endTime) + .routeShortName(routeShortName) + .headsign(headsign) + .serviceType(serviceType) + .includeStopPath(true) + .readOnly(readOnly) + .build(); + + IntervalTimer timer = new IntervalTimer(); + List arrivalDeparturesList = ArrivalDeparture.getArrivalsDeparturesFromDb(adQuery); + System.out.println(timer.elapsedMsecStr()); + + Map> resultsMap = new HashMap<>(); + Map stopPathsMap = new HashMap<>(); + + // Put Arrival Departure and StopPaths data into maps + processSpeedDataIntoMaps(arrivalDeparturesList, resultsMap, stopPathsMap, agencyId); + + // Get Average Speed Data for StopPaths + Map stopPathsSpeedsMap = getStopPathsSpeedMap(resultsMap.values()); + + List ipcStopPathsWithSpeed = new ArrayList<>(); + for(Map.Entry stopPath : stopPathsMap.entrySet()){ + Double speed = stopPathsSpeedsMap.containsKey(stopPath.getKey()) ? + stopPathsSpeedsMap.get(stopPath.getKey()).getAverage() : null; + ipcStopPathsWithSpeed.add(new IpcStopPathWithSpeed(stopPath.getValue(), speed)); + } + + return ipcStopPathsWithSpeed; + } + + private void processSpeedDataIntoMaps(List arrivalDeparturesList, + Map> resultsMap, + Map stopPathsMap, + String agencyId){ + + TimeZone tz = Agency.getTimeZoneFromDb(agencyId); + + for (ArrivalDeparture arrDep : arrivalDeparturesList) { + + addArrivalDepartureToMap(resultsMap, arrDep, tz.toZoneId()); + + StopPath stopPath = arrDep.getStopPathFromDb(); + if(stopPath != null){ + if(!stopPathsMap.containsKey(stopPath.getId()) || + stopPathsMap.get(stopPath.getId()).getConfigRev() < stopPath.getConfigRev()){ + stopPathsMap.put(stopPath.getId(),stopPath); + } + } + } + } + + private void addArrivalDepartureToMap( + Map> map, + ArrivalDeparture arrDep, + ZoneId zoneId) { + ArrivalDepartureTripKey key = new ArrivalDepartureTripKey(arrDep.getServiceId(), + dayOfYearForTrip(arrDep.getDate(), zoneId), arrDep.getTripId(), arrDep.getVehicleId()); + List list = map.get(key); + if (list == null) { + list = new ArrayList<>(); + map.put(key, list); + } + list.add(arrDep); + } + + + private Map getStopPathsSpeedMap(Collection> arrivalDepartures){ + Map stopPathsSpeedsMap = new HashMap<>(); + for (List arrDepList : arrivalDepartures) { + + for (int i=0; i getStopsWithAvgDwellTimes(LocalDate beginDate, LocalDate endDate, + LocalTime beginTime, LocalTime endTime, + String routeIdOrShortName, ServiceType serviceType, + boolean timePointsOnly, String headsign, + boolean readOnly) throws Exception { + + String routeShortName = getRouteShortName(routeIdOrShortName); + + ArrivalDepartureQuery.Builder adBuilder = new ArrivalDepartureQuery.Builder(); + ArrivalDepartureQuery adQuery = adBuilder + .beginDate(beginDate) + .endDate(endDate) + .beginTime(beginTime) + .endTime(endTime) + .routeShortName(routeShortName) + .headsign(headsign) + .serviceType(serviceType) + .timePointsOnly(timePointsOnly) + .dwellTimeOnly(true) + .includeStop(true) + .readOnly(readOnly) + .build(); + + List arrivalDepartures = ArrivalDeparture.getArrivalsDeparturesFromDb(adQuery); + + List stopsWithAvgDwellTime = new ArrayList<>(); + + for(ArrivalDeparture ad : arrivalDepartures){ + Stop stop = ad.getStopFromDb(); + IpcStopWithDwellTime ipcStopWithDwellTime = new IpcStopWithDwellTime(stop, ad.getDirectionId(), ad.getDwellTime()); + stopsWithAvgDwellTime.add(ipcStopWithDwellTime); + } + + return stopsWithAvgDwellTime; + } +} diff --git a/transitime/src/main/java/org/transitime/statistics/ScheduleDataProcessor.java b/transitclock/src/main/java/org/transitclock/statistics/ScheduleDataProcessor.java similarity index 97% rename from transitime/src/main/java/org/transitime/statistics/ScheduleDataProcessor.java rename to transitclock/src/main/java/org/transitclock/statistics/ScheduleDataProcessor.java index c77822ea6..3d50a8a5c 100644 --- a/transitime/src/main/java/org/transitime/statistics/ScheduleDataProcessor.java +++ b/transitclock/src/main/java/org/transitclock/statistics/ScheduleDataProcessor.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.statistics; +package org.transitclock.statistics; import java.util.ArrayList; import java.util.Collections; @@ -29,19 +29,19 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.configData.DbSetupConfig; -import org.transitime.db.structs.ArrivalDeparture; -import org.transitime.db.structs.ArrivalDeparture.ArrivalsOrDepartures; -import org.transitime.gtfs.gtfsStructs.GtfsExtendedStopTime; -import org.transitime.gtfs.gtfsStructs.GtfsFrequency; -import org.transitime.gtfs.gtfsStructs.GtfsStopTime; -import org.transitime.gtfs.readers.GtfsFrequenciesReader; -import org.transitime.gtfs.readers.GtfsStopTimesReader; -import org.transitime.gtfs.writers.GtfsExtendedStopTimesWriter; -import org.transitime.gtfs.writers.GtfsStopTimesWriter; -import org.transitime.statistics.ScheduleStatistics.Stats; -import org.transitime.utils.MapKey; -import org.transitime.utils.Time; +import org.transitclock.configData.DbSetupConfig; +import org.transitclock.db.structs.ArrivalDeparture; +import org.transitclock.db.structs.ArrivalDeparture.ArrivalsOrDepartures; +import org.transitclock.gtfs.gtfsStructs.GtfsExtendedStopTime; +import org.transitclock.gtfs.gtfsStructs.GtfsFrequency; +import org.transitclock.gtfs.gtfsStructs.GtfsStopTime; +import org.transitclock.gtfs.readers.GtfsFrequenciesReader; +import org.transitclock.gtfs.readers.GtfsStopTimesReader; +import org.transitclock.gtfs.writers.GtfsExtendedStopTimesWriter; +import org.transitclock.gtfs.writers.GtfsStopTimesWriter; +import org.transitclock.statistics.ScheduleStatistics.Stats; +import org.transitclock.utils.MapKey; +import org.transitclock.utils.Time; /** * For processing arrival/departure times based on AVL data in order to diff --git a/transitime/src/main/java/org/transitime/statistics/ScheduleStatistics.java b/transitclock/src/main/java/org/transitclock/statistics/ScheduleStatistics.java similarity index 98% rename from transitime/src/main/java/org/transitime/statistics/ScheduleStatistics.java rename to transitclock/src/main/java/org/transitclock/statistics/ScheduleStatistics.java index a0c2efc9f..147fcd270 100644 --- a/transitime/src/main/java/org/transitime/statistics/ScheduleStatistics.java +++ b/transitclock/src/main/java/org/transitclock/statistics/ScheduleStatistics.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.statistics; +package org.transitclock.statistics; import java.util.ArrayList; import java.util.Collection; @@ -27,11 +27,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.db.structs.ArrivalDeparture.ArrivalsOrDepartures; -import org.transitime.gtfs.gtfsStructs.GtfsStopTime; -import org.transitime.statistics.ScheduleDataProcessor.TripStopKey; -import org.transitime.utils.StringUtils; -import org.transitime.utils.Time; +import org.transitclock.db.structs.ArrivalDeparture.ArrivalsOrDepartures; +import org.transitclock.gtfs.gtfsStructs.GtfsStopTime; +import org.transitclock.statistics.ScheduleDataProcessor.TripStopKey; +import org.transitclock.utils.StringUtils; +import org.transitclock.utils.Time; /** * Contains methods for taking arrival/departure info that is collected diff --git a/transitime/src/main/java/org/transitime/statistics/Statistics.java b/transitclock/src/main/java/org/transitclock/statistics/Statistics.java similarity index 99% rename from transitime/src/main/java/org/transitime/statistics/Statistics.java rename to transitclock/src/main/java/org/transitclock/statistics/Statistics.java index 4edd76279..f3eb94e31 100644 --- a/transitime/src/main/java/org/transitime/statistics/Statistics.java +++ b/transitclock/src/main/java/org/transitclock/statistics/Statistics.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.statistics; +package org.transitclock.statistics; import java.util.ArrayList; import java.util.Arrays; diff --git a/transitime/src/main/java/org/transitime/statistics/package-info.java b/transitclock/src/main/java/org/transitclock/statistics/package-info.java similarity index 96% rename from transitime/src/main/java/org/transitime/statistics/package-info.java rename to transitclock/src/main/java/org/transitclock/statistics/package-info.java index 07b2e1771..da5cbfa4f 100644 --- a/transitime/src/main/java/org/transitime/statistics/package-info.java +++ b/transitclock/src/main/java/org/transitclock/statistics/package-info.java @@ -22,4 +22,4 @@ * @author SkiBu Smith * */ -package org.transitime.statistics; \ No newline at end of file +package org.transitclock.statistics; \ No newline at end of file diff --git a/transitclock/src/main/java/org/transitclock/traffic/FeatureData.java b/transitclock/src/main/java/org/transitclock/traffic/FeatureData.java new file mode 100644 index 000000000..768831812 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/traffic/FeatureData.java @@ -0,0 +1,79 @@ +/* + * 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.traffic; + + +import org.transitclock.db.structs.StopPath; + +import java.util.ArrayList; +import java.util.List; + +/** + * POJO to contain traffic sensor specifics. Will eventually become + * TrafficSenor. + */ +public class FeatureData { + private long time; + private String label; + private String externalId; + private FeatureGeometry fg; + private float length; + private List snappedStopPaths = new ArrayList(); + + public long getTime() { + return time; + } + public void setTime(long time) { + this.time = time; + } + + public String getLabel() { + return label; + } + public void setLabel(String label) { + this.label = label; + } + + public String getId() { + return externalId; + } + public void setId(String externalId) { + this.externalId = externalId; + } + public void setFeatureGeometry(FeatureGeometry fg) { + this.fg = fg; + } + + public void addStopPath(StopPath stopPath) { + snappedStopPaths.add(stopPath); + } + + public FeatureGeometry getFeatureGeometry() { + return fg; + } + + public List getStopPaths() { + return snappedStopPaths; + } + + public float getLength() { + return length; + } + public void setLength(float length) { + this.length = length; + } +} diff --git a/transitclock/src/main/java/org/transitclock/traffic/FeatureGeometry.java b/transitclock/src/main/java/org/transitclock/traffic/FeatureGeometry.java new file mode 100644 index 000000000..6365ca9ef --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/traffic/FeatureGeometry.java @@ -0,0 +1,43 @@ +/* + * 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.traffic; + +import org.locationtech.jts.geom.Coordinate; + +import java.util.ArrayList; +import java.util.List; + +/** + * POJO to contain geometry data of traffic sensor. Will eventually + * become TrafficPath. + */ +public class FeatureGeometry { + private List lats = new ArrayList<>(); + private List lons = new ArrayList<>(); + public void addLatLon(double lat, double lon) { + lats.add(lat); + lons.add(lon); + } + + public Coordinate[] getAsCoordinateArray() { + ArrayList list = new ArrayList<>(); + for (int i = 0; i. + */ +package org.transitclock.traffic; + +import org.hibernate.Session; +import org.transitclock.db.structs.TrafficPath; +import org.transitclock.db.structs.TrafficSensor; + +import java.util.List; + +/** + * Write out traffic config data to database. Follows conventions + * of DbWriter to remain consistent. + */ +public class TrafficWriter { + private Session session; + private Integer trafficRev; + public TrafficWriter(Session session, Integer trafficRev) { + this.session = session; + this.trafficRev = trafficRev; + } + + /** + * Write data to database. Assumes transaction management occurs + * external to this method. + * @param trafficPaths + */ + public void writeTrafficPaths(List trafficPaths) { + for (TrafficPath path : trafficPaths) { + session.save(path); + } + } + + /** + * + * Write data to database. Assumes transaction management occurs + * external to this method. + * @param sensors + */ + public void writeSensors(List sensors) { + for (TrafficSensor sensor : sensors) { + session.save(sensor); + } + } +} diff --git a/transitclock/src/main/java/org/transitclock/trip/PollUrlTripModule.java b/transitclock/src/main/java/org/transitclock/trip/PollUrlTripModule.java new file mode 100644 index 000000000..d74f6747d --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/trip/PollUrlTripModule.java @@ -0,0 +1,185 @@ +/* + * 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.trip; + +import org.json.JSONException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.config.BooleanConfigValue; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.config.StringConfigValue; +import org.transitclock.configData.AgencyConfig; +import org.transitclock.logging.Markers; +import org.transitclock.modules.Module; +import org.transitclock.utils.IntervalTimer; +import org.transitclock.utils.JsonUtils; +import org.transitclock.utils.Time; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.SocketTimeoutException; +import java.net.URLConnection; + +/** + * Subclass of Module to be used when reading Trip data from a feed. Calls the + * abstract method getAndProcessData() for the subclass to actually get data + * from the feed. + */ +public abstract class PollUrlTripModule extends Module { + + private static StringConfigValue url = + new StringConfigValue("transitclock.trip.feedUrl", + "The URL of the Trip feed to poll."); + + private static StringConfigValue authenticationUser = + new StringConfigValue("transitclock.trip.authenticationUser", + "If authentication used for the feed then this specifies " + + "the user."); + + private static StringConfigValue authenticationPassword = + new StringConfigValue("transitclock.trip.authenticationPassword", + "If authentication used for the feed then this specifies " + + "the password."); + + private static BooleanConfigValue shouldProcessTripFeed = + new BooleanConfigValue("transitclock.trip.shouldProcessTripFeed", + true, + "Usually want to process the Trip Feed if it is provided and if it contains " + + "supplemental data such as information about canceled trips."); + + private static IntegerConfigValue secondsBetweenTripFeedPolling = + new IntegerConfigValue("transitclock.trip.feedPollingRateSecs", 5, + "How frequently a Trip feed should be polled for new data."); + + private static IntegerConfigValue tripFeedTimeoutInMSecs = + new IntegerConfigValue("transitclock.trip.feedTimeoutInMSecs", 10000, + "For when polling Trip XML feed. The feed logs error if " + + "the timeout value is exceeded when performing the XML " + + "request."); + + // Usually want to use compression when reading data but for some Trip Feeds + // feeds might be binary where don't want additional compression. A + // superclass can override this value. + + private static final Logger logger = LoggerFactory + .getLogger(PollUrlTripModule.class); + + /********************** Member Functions **************************/ + + /** + * Constructor + * + * @param agencyId + */ + protected PollUrlTripModule(String agencyId) { + super(agencyId); + } + + /** + * Feed specific URL to use when accessing data. Will often be + * overridden by subclass. + * + * @return + */ + protected String getUrl() { + return url.getValue(); + } + + /** + * Override this method if Trip feed needs to specify header info + * @param con + */ + protected void setRequestHeaders(URLConnection con) {} + + /** + * Converts the input stream into a JSON string. Useful for when processing + * a JSON feed. + * + * @param in + * @return the JSON string + * @throws IOException + * @throws JSONException + */ + protected String getJsonString(InputStream in) throws IOException, + JSONException { + return JsonUtils.getJsonString(in); + } + + /** + * Abstract method for getting feed from a data source and then processing it. + * + * @throws Exception + * Throws a generic exception since the processing is done in + * the abstract method processData() and it could throw any type + * of exception since we don't really know how the Trip feed will + * be processed. + */ + protected abstract void getAndProcessData(String url) throws Exception; + + + /** + * Does all of the work for the class. Runs forever and reads in + * Trip data from feed and processes it. + * @see Runnable#run() + */ + @Override + public void run() { + // Log that module successfully started + logger.info("Started module {} for agencyId={}", + getClass().getName(), getAgencyId()); + + // Run forever + while (true) { + IntervalTimer timer = new IntervalTimer(); + + try { + // Process data + String[] urls = getUrl().split(","); + for(String url : urls){ + getAndProcessData(url); + } + } catch (SocketTimeoutException e) { + logger.error(Markers.email(), + "Error for agencyId={} accessing Trip feed using URL={} " + + "with a timeout of {} msec.", + AgencyConfig.getAgencyId(), getUrl(), + tripFeedTimeoutInMSecs.getValue(), e); + } catch (Exception e) { + logger.error("Error accessing Trip feed using URL={}.", + getUrl(), e); + } + + // Wait appropriate amount of time till poll again + long elapsedMsec = timer.elapsedMsec(); + long sleepTime = + secondsBetweenTripFeedPolling.getValue()*Time.MS_PER_SEC - + elapsedMsec; + if (sleepTime < 0) { + logger.warn("Supposed to have a polling rate of " + + secondsBetweenTripFeedPolling.getValue()*Time.MS_PER_SEC + + " msec but processing previous data took " + + elapsedMsec + " msec so polling again immediately."); + } else { + Time.sleep(sleepTime); + } + } + } + +} \ No newline at end of file diff --git a/transitime/src/main/java/org/transitime/utils/ChinaGpsOffset.java b/transitclock/src/main/java/org/transitclock/utils/ChinaGpsOffset.java similarity index 99% rename from transitime/src/main/java/org/transitime/utils/ChinaGpsOffset.java rename to transitclock/src/main/java/org/transitclock/utils/ChinaGpsOffset.java index 120b0d7c9..cd7c3e4cc 100644 --- a/transitime/src/main/java/org/transitime/utils/ChinaGpsOffset.java +++ b/transitclock/src/main/java/org/transitclock/utils/ChinaGpsOffset.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.utils; +package org.transitclock.utils; /** * For dealing with the map offset issue in China. All maps have to be offset in diff --git a/transitime/src/main/java/org/transitime/utils/ClassInstantiator.java b/transitclock/src/main/java/org/transitclock/utils/ClassInstantiator.java similarity index 96% rename from transitime/src/main/java/org/transitime/utils/ClassInstantiator.java rename to transitclock/src/main/java/org/transitclock/utils/ClassInstantiator.java index 8be57ec54..8450d472b 100644 --- a/transitime/src/main/java/org/transitime/utils/ClassInstantiator.java +++ b/transitclock/src/main/java/org/transitclock/utils/ClassInstantiator.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.utils; +package org.transitclock.utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -63,7 +63,7 @@ public static T instantiate(String className, Class clazz) { return null; } catch (ClassNotFoundException | SecurityException | InstantiationException | IllegalAccessException - | IllegalArgumentException e) { + | IllegalArgumentException | NoClassDefFoundError e) { logger.error("Could not instantiate class {}. ", className, e); return null; } diff --git a/transitime/src/main/java/org/transitime/utils/ConcurrentHashMapNullKeyOk.java b/transitclock/src/main/java/org/transitclock/utils/ConcurrentHashMapNullKeyOk.java similarity index 98% rename from transitime/src/main/java/org/transitime/utils/ConcurrentHashMapNullKeyOk.java rename to transitclock/src/main/java/org/transitclock/utils/ConcurrentHashMapNullKeyOk.java index cea5e7ce0..4e1956427 100644 --- a/transitime/src/main/java/org/transitime/utils/ConcurrentHashMapNullKeyOk.java +++ b/transitclock/src/main/java/org/transitclock/utils/ConcurrentHashMapNullKeyOk.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.utils; +package org.transitclock.utils; import java.util.concurrent.ConcurrentHashMap; diff --git a/transitime/src/main/java/org/transitime/utils/EmailSender.java b/transitclock/src/main/java/org/transitclock/utils/EmailSender.java similarity index 91% rename from transitime/src/main/java/org/transitime/utils/EmailSender.java rename to transitclock/src/main/java/org/transitclock/utils/EmailSender.java index cd6709673..ae35e88eb 100644 --- a/transitime/src/main/java/org/transitime/utils/EmailSender.java +++ b/transitclock/src/main/java/org/transitclock/utils/EmailSender.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.utils; +package org.transitclock.utils; import java.io.IOException; import java.io.InputStream; @@ -35,13 +35,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.config.StringConfigValue; -import org.transitime.configData.AgencyConfig; -import org.transitime.logging.Markers; +import org.transitclock.config.StringConfigValue; +import org.transitclock.configData.AgencyConfig; +import org.transitclock.logging.Markers; /** * For programmatically sending out an e-mail. Uses a file specified by - * Java property transitime.utils.emailConfigFile to configure parameters. + * Java property transitclock.utils.emailConfigFile to configure parameters. * * @author SkiBu Smith * @@ -55,8 +55,8 @@ public class EmailSender { /********************** Parameters ********************************/ private static StringConfigValue emailConfigFile = - new StringConfigValue("transitime.utils.emailConfigFile", - "/home/ec2-user/transitimeScripts/emailConfig.txt", + new StringConfigValue("transitclock.utils.emailConfigFile", + "/usr/local/transitclock/config/emailconfig.properties", "Specifies name of configuration file used for sending " + "out e-mails."); @@ -154,7 +154,7 @@ public void send(String recipients, String subject, String messageBody) { */ public static void main(String[] args) { EmailSender email = new EmailSender(); - email.send("monitoring@transitime.org", "test subject", "test message"); + email.send("monitoring@transitclock.org", "test subject", "test message"); } } diff --git a/transitime/src/main/java/org/transitime/utils/Encryption.java b/transitclock/src/main/java/org/transitclock/utils/Encryption.java similarity index 95% rename from transitime/src/main/java/org/transitime/utils/Encryption.java rename to transitclock/src/main/java/org/transitclock/utils/Encryption.java index ad34e0db3..03a82a138 100644 --- a/transitime/src/main/java/org/transitime/utils/Encryption.java +++ b/transitclock/src/main/java/org/transitclock/utils/Encryption.java @@ -15,14 +15,14 @@ * along with Transitime.org . If not, see . */ -package org.transitime.utils; +package org.transitclock.utils; import org.jasypt.exceptions.EncryptionOperationNotPossibleException; import org.jasypt.util.text.BasicTextEncryptor; import org.jasypt.util.text.TextEncryptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.config.StringConfigValue; +import org.transitclock.config.StringConfigValue; /** * For encrypting and decrypting strings. @@ -33,7 +33,7 @@ public class Encryption { private static final StringConfigValue encryptionPassword = - new StringConfigValue("transitime.db.encryptionPassword", + new StringConfigValue("transitclock.db.encryptionPassword", "SET THIS!", "Used for encrypting, deencrypting passwords for storage " + "in a database. This value should be customized for each " diff --git a/transitime/src/main/java/org/transitime/utils/Geo.java b/transitclock/src/main/java/org/transitclock/utils/Geo.java similarity index 97% rename from transitime/src/main/java/org/transitime/utils/Geo.java rename to transitclock/src/main/java/org/transitclock/utils/Geo.java index 1e947bbe9..1b430c8da 100644 --- a/transitime/src/main/java/org/transitime/utils/Geo.java +++ b/transitclock/src/main/java/org/transitclock/utils/Geo.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.utils; +package org.transitclock.utils; import java.text.DecimalFormat; -import org.transitime.db.structs.Location; -import org.transitime.db.structs.Vector; +import org.transitclock.db.structs.Location; +import org.transitclock.db.structs.Vector; /** * Some possibly handle geometric conversions. @@ -73,6 +73,27 @@ public static String distanceFormat(double arg) { return twoDigitFormat.format(arg) + "m"; } + /** + * For formatting distances in meters to consistent 2 decimal places. + * Appends "m" to indicate units. + * + * @param arg + * @return + */ + public static String distanceFormat(Double arg) { + // Handle NaN and other special cases + if (arg == null) + return "null"; + if (arg.isNaN()) + return "NaN"; + if (arg == Double.MAX_VALUE) + return "Double.MAX_VALUE"; + + // Not a special case so output the value with just two digits + // past decimal place and append "m" to indicate meters. + return twoDigitFormat.format(arg) + "m"; + } + /** * Outputs arg with just a single digit. No other formatting is done. * Useful when want to output speed or heading but don't want to diff --git a/transitime/src/main/java/org/transitime/utils/Gzip.java b/transitclock/src/main/java/org/transitclock/utils/Gzip.java similarity index 99% rename from transitime/src/main/java/org/transitime/utils/Gzip.java rename to transitclock/src/main/java/org/transitclock/utils/Gzip.java index d14c8d644..5bc4df8d2 100644 --- a/transitime/src/main/java/org/transitime/utils/Gzip.java +++ b/transitclock/src/main/java/org/transitclock/utils/Gzip.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.utils; +package org.transitclock.utils; import java.io.File; import java.io.FileInputStream; diff --git a/transitime/src/main/java/org/transitime/utils/HttpGetFile.java b/transitclock/src/main/java/org/transitclock/utils/HttpGetFile.java similarity index 96% rename from transitime/src/main/java/org/transitime/utils/HttpGetFile.java rename to transitclock/src/main/java/org/transitclock/utils/HttpGetFile.java index 9f4438318..8e12a3f00 100644 --- a/transitime/src/main/java/org/transitime/utils/HttpGetFile.java +++ b/transitclock/src/main/java/org/transitclock/utils/HttpGetFile.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.utils; +package org.transitclock.utils; import java.io.File; import java.io.FileOutputStream; @@ -218,6 +218,7 @@ public int getFile() throws IOException { } // Close things up + in.close(); fos.close(); // Set the last modified time so that it is the same as on the diff --git a/transitime/src/main/java/org/transitime/utils/IntervalTimer.java b/transitclock/src/main/java/org/transitclock/utils/IntervalTimer.java similarity index 90% rename from transitime/src/main/java/org/transitime/utils/IntervalTimer.java rename to transitclock/src/main/java/org/transitclock/utils/IntervalTimer.java index af0157451..00b6cdd29 100644 --- a/transitime/src/main/java/org/transitime/utils/IntervalTimer.java +++ b/transitclock/src/main/java/org/transitclock/utils/IntervalTimer.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.utils; +package org.transitclock.utils; import java.text.DecimalFormat; @@ -30,6 +30,8 @@ public class IntervalTimer { // The time the interval timer created or reset private long initialNanotime; + + private final String name; private static DecimalFormat decimalFormat = new DecimalFormat("#.###"); @@ -39,6 +41,12 @@ public class IntervalTimer { * Records the current time so that elapsed time can later be determined. */ public IntervalTimer() { + this.name = "timer"; + initialNanotime = System.nanoTime(); + } + + public IntervalTimer(String name){ + this.name = name; initialNanotime = System.nanoTime(); } @@ -77,6 +85,10 @@ public String elapsedMsecStr() { long microSecs = (System.nanoTime() - initialNanotime)/1000; return decimalFormat.format((double) microSecs / 1000); } + + public void printTime(){ + System.out.println(name + " - " + elapsedMsecStr()); + } /** * toString() is defined so that it works well when debug logging. This way diff --git a/transitclock/src/main/java/org/transitclock/utils/JsonUtils.java b/transitclock/src/main/java/org/transitclock/utils/JsonUtils.java new file mode 100644 index 000000000..ea7739260 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/utils/JsonUtils.java @@ -0,0 +1,54 @@ +/* + * 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.utils; + +import org.json.JSONException; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +/** + * Utilities for JSON parsing. + */ +public class JsonUtils { + + /** + * Converts the input stream into a JSON string. Useful for when processing + * a JSON feed. + * + * @param in + * @return the JSON string + * @throws IOException + * @throws JSONException + */ + public static 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(); + return responseStr; + } + +} diff --git a/transitime/src/main/java/org/transitime/utils/MapKey.java b/transitclock/src/main/java/org/transitclock/utils/MapKey.java similarity index 99% rename from transitime/src/main/java/org/transitime/utils/MapKey.java rename to transitclock/src/main/java/org/transitclock/utils/MapKey.java index e6315ed32..3f5576df9 100644 --- a/transitime/src/main/java/org/transitime/utils/MapKey.java +++ b/transitclock/src/main/java/org/transitclock/utils/MapKey.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.utils; +package org.transitclock.utils; import net.jcip.annotations.Immutable; diff --git a/transitime/src/main/java/org/transitime/utils/MathUtils.java b/transitclock/src/main/java/org/transitclock/utils/MathUtils.java similarity index 60% rename from transitime/src/main/java/org/transitime/utils/MathUtils.java rename to transitclock/src/main/java/org/transitclock/utils/MathUtils.java index b897f5be3..8ebb352ef 100644 --- a/transitime/src/main/java/org/transitime/utils/MathUtils.java +++ b/transitclock/src/main/java/org/transitclock/utils/MathUtils.java @@ -14,7 +14,9 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.utils; +package org.transitclock.utils; + +import java.util.Collection; /** * Simple math utilities. Note that didn't call class "Math" because @@ -51,4 +53,44 @@ public static double round(double v, int precision) { long unscaledLong = (long) (unscaled + (v < 0 ? -0.5 : 0.5)); return (double) unscaledLong / TENS[precision]; } + + public static double average(Collection doubles) { + Double avg = 0d; + int count = 0; + for(Double doubleVal : doubles){ + count++; + avg += doubleVal; + } + return avg / count; + } + + public static double sum(Collection doubles) { + Double sum = 0d; + for(Double doubleVal : doubles){ + sum += doubleVal; + } + return sum; + } + + public static double min(Collection doubles) { + if(doubles.size() < 1) + throw new IllegalArgumentException("No items in list"); + Double min = Double.MAX_VALUE; + for(Double doubleVal : doubles){ + if(doubleVal < min) + min = doubleVal; + } + return min; + } + + public static double max(Collection doubles) { + if(doubles.size() < 1) + throw new IllegalArgumentException("No items in list"); + Double max = 0d; + for(Double doubleVal : doubles){ + if(doubleVal > max) + max = doubleVal; + } + return max; + } } diff --git a/transitime/src/main/java/org/transitime/utils/OrderedCollection.java b/transitclock/src/main/java/org/transitclock/utils/OrderedCollection.java similarity index 96% rename from transitime/src/main/java/org/transitime/utils/OrderedCollection.java rename to transitclock/src/main/java/org/transitclock/utils/OrderedCollection.java index dd07f975c..e40d6f5f8 100644 --- a/transitime/src/main/java/org/transitime/utils/OrderedCollection.java +++ b/transitclock/src/main/java/org/transitclock/utils/OrderedCollection.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.utils; +package org.transitclock.utils; import java.util.ArrayList; import java.util.Arrays; diff --git a/transitclock/src/main/java/org/transitclock/utils/PlaybackIntervalTimer.java b/transitclock/src/main/java/org/transitclock/utils/PlaybackIntervalTimer.java new file mode 100644 index 000000000..58ea0aa03 --- /dev/null +++ b/transitclock/src/main/java/org/transitclock/utils/PlaybackIntervalTimer.java @@ -0,0 +1,83 @@ +/* + * 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.utils; + +import java.text.DecimalFormat; + +import org.transitclock.applications.Core; + +/** + * Very simple class for timing duration of events. Main use is at the beginning + * of the process to construct an IntervalTimer and then at the end to call + * elapsedMsec() to determine elapsed time. + * + * @author SkiBu Smith + * + */ +public class PlaybackIntervalTimer { + + // The time the interval timer created or reset + private long initialtime; + + private static DecimalFormat decimalFormat = new DecimalFormat("#.###"); + + /********************** Member Functions **************************/ + + /** + * Records the current time so that elapsed time can later be determined. + */ + public PlaybackIntervalTimer() { + initialtime = Core.getInstance().getSystemTime(); + } + + /** + * Resets the timer to the current time. + */ + public void resetTimer() { + initialtime = Core.getInstance().getSystemTime(); + } + + /** + * Time elapsed in msec since IntervalTimer created or resetTimer() called + * + * @return elapsed time in msec + */ + public long elapsedMsec() { + return Math.abs(Core.getInstance().getSystemTime() - initialtime); + } + + /** + * For outputting elapsed time in milliseconds + * + * @return String of elapsed time in milliseconds + */ + private String elapsedMsecStr() { + + return ""+(Core.getInstance().getSystemTime() - initialtime); + } + + /** + * toString() is defined so that it works well when debug logging. This way + * can pass in reference to the timer object. Only if debug logging is + * enabled will the toString() be called causing the string to actually be + * generated. + */ + @Override + public String toString() { + return elapsedMsecStr(); + } +} diff --git a/transitime/src/main/java/org/transitime/utils/SettableSystemTime.java b/transitclock/src/main/java/org/transitclock/utils/SettableSystemTime.java similarity index 94% rename from transitime/src/main/java/org/transitime/utils/SettableSystemTime.java rename to transitclock/src/main/java/org/transitclock/utils/SettableSystemTime.java index 5d361b18b..cd51c1b5f 100644 --- a/transitime/src/main/java/org/transitime/utils/SettableSystemTime.java +++ b/transitclock/src/main/java/org/transitclock/utils/SettableSystemTime.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.utils; +package org.transitclock.utils; /** * For when need a system time that can be set to a specific value. For playback @@ -37,7 +37,7 @@ public SettableSystemTime() { } /* (non-Javadoc) - * @see org.transitime.utils.SystemTime#get() + * @see org.transitclock.utils.SystemTime#get() */ @Override public long get() { diff --git a/transitime/src/main/java/org/transitime/utils/StringUtils.java b/transitclock/src/main/java/org/transitclock/utils/StringUtils.java similarity index 99% rename from transitime/src/main/java/org/transitime/utils/StringUtils.java rename to transitclock/src/main/java/org/transitclock/utils/StringUtils.java index 7de0995bd..bcd53d75c 100644 --- a/transitime/src/main/java/org/transitime/utils/StringUtils.java +++ b/transitclock/src/main/java/org/transitclock/utils/StringUtils.java @@ -15,7 +15,7 @@ * Transitime.org . If not, see . */ -package org.transitime.utils; +package org.transitclock.utils; import java.text.DecimalFormat; import java.util.Collections; diff --git a/transitime/src/main/java/org/transitime/utils/SystemCurrentTime.java b/transitclock/src/main/java/org/transitclock/utils/SystemCurrentTime.java similarity index 96% rename from transitime/src/main/java/org/transitime/utils/SystemCurrentTime.java rename to transitclock/src/main/java/org/transitclock/utils/SystemCurrentTime.java index 7f8acff4d..58cf33b27 100644 --- a/transitime/src/main/java/org/transitime/utils/SystemCurrentTime.java +++ b/transitclock/src/main/java/org/transitclock/utils/SystemCurrentTime.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.utils; +package org.transitclock.utils; /** * Implements SystemTime and returns the system current time when get() diff --git a/transitime/src/main/java/org/transitime/utils/SystemTime.java b/transitclock/src/main/java/org/transitclock/utils/SystemTime.java similarity index 97% rename from transitime/src/main/java/org/transitime/utils/SystemTime.java rename to transitclock/src/main/java/org/transitclock/utils/SystemTime.java index 48daa959d..df1a98225 100644 --- a/transitime/src/main/java/org/transitime/utils/SystemTime.java +++ b/transitclock/src/main/java/org/transitclock/utils/SystemTime.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.utils; +package org.transitclock.utils; /** * So that can have access to a system time whether in normal diff --git a/transitime/src/main/java/org/transitime/utils/Time.java b/transitclock/src/main/java/org/transitclock/utils/Time.java old mode 100644 new mode 100755 similarity index 78% rename from transitime/src/main/java/org/transitime/utils/Time.java rename to transitclock/src/main/java/org/transitclock/utils/Time.java index d01d7be4a..1de70f3f5 --- a/transitime/src/main/java/org/transitime/utils/Time.java +++ b/transitclock/src/main/java/org/transitclock/utils/Time.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,19 +14,20 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.utils; +package org.transitclock.utils; + +import org.transitclock.config.BooleanConfigValue; +import org.transitclock.db.structs.Agency; +import org.transitclock.gtfs.DbConfig; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.TimeZone; import java.text.DateFormat; import java.text.DecimalFormat; import java.text.ParseException; import java.text.SimpleDateFormat; - -import org.transitime.db.structs.Agency; -import org.transitime.gtfs.DbConfig; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.TimeZone; /** * Contains convenience methods for dealing with time issues. @@ -35,9 +36,9 @@ * TimeZone.setDefault(TimeZone.getTimeZone(timeZoneStr)); before * this class is initialized. Otherwise the SimpleDateFormat objects will * wrongly use the system default timezone. - * + * * @author SkiBu Smith - * + * */ public class Time { // Some handy constants for dealing with time. @@ -61,7 +62,7 @@ public class Time { public static final long WEEK_IN_MSECS = MS_PER_WEEK; public static final long MS_PER_YEAR = 365 * MS_PER_DAY; public static final long YEAR_IN_MSECS = MS_PER_YEAR; - + public static final int SEC_PER_MIN = 60; public static final int MIN_IN_SECS = SEC_PER_MIN; public static final int SEC_PER_HOUR = 60 * SEC_PER_MIN; @@ -74,38 +75,68 @@ public class Time { public static final long NSEC_PER_MSEC = 1000000; public static final long MSEC_IN_NSECS = NSEC_PER_MSEC; - - // These two are for reading in dates in various formats - private static final DateFormat defaultDateFormat = + + + private static final String dateTimePatternMDY = "MM-dd-yyy HH:mm:ss"; + private static final String dateTimePatternYMD = "yyyy-MM-dd HH:mm:ss"; + + // Month, Day, Year Format + private static final DateFormat defaultDateFormatMDY = SimpleDateFormat.getDateInstance(DateFormat.SHORT); - private static final DateFormat dateFormatDashesShortYear = + private static final DateFormat dateFormatDashesShortYearMDY = new SimpleDateFormat("MM-dd-yy"); - - private static final DateFormat readableDateFormat = + private static final DateFormat readableDateFormatMDY = new SimpleDateFormat("MM-dd-yyyy"); - - private static final DateFormat readableDateFormat24 = + + private static final DateFormat readableDateFormat24MDY = new SimpleDateFormat("MM-dd-yyyy HH:mm:ss z"); - - private static final DateFormat readableDateFormat24NoSecs = - new SimpleDateFormat("MM-dd-yyyy HH:mm"); - private static final DateFormat readableDateFormat24Msec = + private static final DateFormat readableDateFormat24NoSecsMDY = + new SimpleDateFormat("MM-dd-yyyy HH:mm"); + + private static final DateFormat readableDateFormat24MsecMDY = new SimpleDateFormat("MM-dd-yyyy HH:mm:ss.SSS z"); - - private static final DateFormat readableDateFormat24NoTimeZoneMsec = + + private static final DateFormat readableDateFormat24NoTimeZoneMsecMDY = new SimpleDateFormat("MM-dd-yyyy HH:mm:ss.SSS"); - - private static final DateFormat readableDateFormat24NoTimeZoneNoMsec = + + private static final DateFormat readableDateFormat24NoTimeZoneNoMsecMDY = new SimpleDateFormat("MM-dd-yyyy HH:mm:ss"); + // Year, Month, Day Format + // These two are for reading in dates in various formats + private static final DateFormat defaultDateFormatYMD = + new SimpleDateFormat("yyyy-MM-dd"); + private static final DateFormat dateFormatDashesShortYearYMD = + new SimpleDateFormat("yy-MM-dd"); + + + private static final DateFormat readableDateFormatYMD = + new SimpleDateFormat("yyyy-MM-dd"); + + private static final DateFormat readableDateFormat24YMD = + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z"); + + private static final DateFormat readableDateFormat24NoSecsYMD = + new SimpleDateFormat("yyyy-MM-dd HH:mm"); + + private static final DateFormat readableDateFormat24MsecYMD = + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS z"); + + private static final DateFormat readableDateFormat24NoTimeZoneMsecYMD = + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + + private static final DateFormat readableDateFormat24NoTimeZoneNoMsecYMD = + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + // Time private static final DateFormat timeFormat24 = new SimpleDateFormat("HH:mm:ss z"); private static final DateFormat timeFormat24NoTimezone = new SimpleDateFormat("HH:mm:ss"); - + private static final DateFormat timeFormat24Msec = new SimpleDateFormat("HH:mm:ss.SSS z"); @@ -115,36 +146,127 @@ public class Time { // Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 private static final DateFormat httpFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z"); - + // Note that this one is not static. It is for when need to include // timezone via a Time object. - private final DateFormat readableDateFormat24MsecForTimeZone = + private final DateFormat readableDateFormat24MsecForTimeZoneMDY = new SimpleDateFormat("MM-dd-yyyy HH:mm:ss.SSS z"); + private final DateFormat readableDateFormatForTimeZoneMDY = + new SimpleDateFormat("MM-dd-yyyy"); + + private final DateFormat readableDateFormat24MsecForTimeZoneYMD = + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS z"); + private final DateFormat readableDateFormatForTimeZoneYMD = + new SimpleDateFormat("yyyy-MM-dd"); + private final DateFormat readableTimeFormatForTimeZone = new SimpleDateFormat("HH:mm:ss"); - private final DateFormat readableDateFormatForTimeZone = - new SimpleDateFormat("MM-dd-yyyy"); - + // So can output headings and such with a consistent number of decimal places private static final DecimalFormat oneDigitFormat = new DecimalFormat("0.0"); // Have a shared calendar so don't have to keep creating one private Calendar calendar; - + + private static BooleanConfigValue useMonthDayYearFormat = + new BooleanConfigValue( + "transitclock.utils.useMonthDayYearFormat", + false, + "Use the month-day-year date format instead of year-month-date."); + /******************* Methods ******************/ - + public Time(DbConfig dbConfig) { + Agency agency = dbConfig.getFirstAgency(); this.calendar = agency != null ? new GregorianCalendar(agency.getTimeZone()) : new GregorianCalendar(); } - + + public static String getDateTimePattern(){ + if(useMonthDayYearFormat.getValue()) { + return dateTimePatternMDY; + } + return dateTimePatternYMD; + } + + private static DateFormat getDefaultDateFormat() { + if(useMonthDayYearFormat.getValue()) { + return defaultDateFormatMDY; + } + return defaultDateFormatYMD; + } + + private static DateFormat getDateFormatDashesShortYear() { + if(useMonthDayYearFormat.getValue()) { + return dateFormatDashesShortYearMDY; + } + return dateFormatDashesShortYearYMD; + } + + private static DateFormat getReadableDateFormat() { + if(useMonthDayYearFormat.getValue()) { + return readableDateFormatMDY; + } + return defaultDateFormatYMD; + } + + private static DateFormat getReadableDateFormat24() { + if(useMonthDayYearFormat.getValue()) { + return readableDateFormat24MDY; + } + return readableDateFormat24YMD; + } + + private static DateFormat getReadableDateFormat24NoSecs() { + if(useMonthDayYearFormat.getValue()) { + return readableDateFormat24NoSecsMDY; + } + return readableDateFormat24NoSecsYMD; + } + + private static DateFormat getReadableDateFormat24Msec() { + if(useMonthDayYearFormat.getValue()) { + return readableDateFormat24MsecMDY; + } + return readableDateFormat24MsecYMD; + } + + private static DateFormat getReadableDateFormat24NoTimeZoneMsec() { + if(useMonthDayYearFormat.getValue()) { + return readableDateFormat24NoTimeZoneMsecMDY; + } + return readableDateFormat24NoTimeZoneMsecYMD; + } + + private static DateFormat getReadableDateFormat24NoTimeZoneNoMsec() { + if(useMonthDayYearFormat.getValue()) { + return readableDateFormat24NoTimeZoneNoMsecMDY; + } + return readableDateFormat24NoTimeZoneNoMsecYMD; + } + + private DateFormat getReadableDateFormat24MsecForTimeZone() { + if(useMonthDayYearFormat.getValue()) { + return readableDateFormat24MsecForTimeZoneMDY; + } + return readableDateFormat24MsecForTimeZoneYMD; + } + + private DateFormat getReadableDateFormatForTimeZone() { + if(useMonthDayYearFormat.getValue()) { + return readableDateFormatForTimeZoneMDY; + } + return readableDateFormatForTimeZoneYMD; + } + + /** * Creates a Time object for the specified timezone. Useful for when have to * frequently call members such as getSecondsIntoDay() that need an * expensive calendar object. - * + * * @param timeZoneStr * Such as "America/Los_Angeles" . List of time zones can be found * at http://en.wikipedia.org/wiki/List_of_tz_database_time_zones . @@ -154,18 +276,18 @@ public Time(String timeZoneStr) { // If no time zone string specified then use local timezone if (timeZoneStr == null) return; - + TimeZone timeZone = TimeZone.getTimeZone(timeZoneStr); this.calendar = new GregorianCalendar(timeZone); - - readableDateFormat24MsecForTimeZone.setCalendar(this.calendar); + + getReadableDateFormat24MsecForTimeZone().setCalendar(this.calendar); readableTimeFormatForTimeZone.setCalendar(this.calendar); - readableDateFormatForTimeZone.setCalendar(this.calendar); + getReadableDateFormatForTimeZone().setCalendar(this.calendar); } - + /** * Converts the epoch time into number of seconds into the day. - * + * * @param epochTime * @return seconds into the day */ @@ -180,22 +302,22 @@ public int getSecondsIntoDay(long epochTime) { calendar.get(Calendar.SECOND); } } - + /** * Converts the epoch time into number of seconds into the day. - * - * @param epochTime + * + * @param epochDate * @return seconds into the day */ public int getSecondsIntoDay(Date epochDate) { return getSecondsIntoDay(epochDate.getTime()); } - + /** * Returns day of year. This method is not threadsafe in that it first sets * the time of the calendar and then gets the day of the year without * synchronizing the calendar. But this is a bit faster. - * + * * @param epochDate * @return */ @@ -203,10 +325,10 @@ public int getDayOfYear(Date epochDate) { calendar.setTimeInMillis(epochDate.getTime()); return calendar.get(Calendar.DAY_OF_YEAR); } - + /** * Converts the epoch time into number of msec into the day. - * + * * @param epochTime * @return msec into the day */ @@ -222,11 +344,11 @@ public int getMsecsIntoDay(Date epochTime) { calendar.get(Calendar.MILLISECOND); } } - + /** * Returns the epoch time of the start of the day for the date and timezone * specified. - * + * * @param date * the time that the start of the day is needed for * @param tz @@ -236,7 +358,7 @@ public int getMsecsIntoDay(Date epochTime) { public static long getStartOfDay(Date date, TimeZone tz) { Calendar calendar = new GregorianCalendar(tz); calendar.setTime(date); - + calendar.set(Calendar.MILLISECOND, 0); calendar.set(Calendar.SECOND, 0); calendar.set(Calendar.MINUTE, 0); @@ -246,12 +368,12 @@ public static long getStartOfDay(Date date, TimeZone tz) { long epochTime = calendar.getTimeInMillis(); return epochTime; } - + /** * Returns the epoch time of the start of the current day for the default * timezone. The default timezone should be set by the application at * startup using TimeZone.setDefault(TimeZone.getTimeZone(timezoneName)). - * + * * @param date * the time that the start of the day is needed for * @return start of the current day @@ -269,10 +391,10 @@ public static long getStartOfDay(Date date) { long epochTime = calendar.getTimeInMillis(); return epochTime; } - + /** * Converts secondsIntoDay into an epoch time. - * + * * @param secondsIntoDay * To be converted into epoch time * @param referenceDate @@ -289,11 +411,11 @@ public long getEpochTime(int secondsIntoDay, Date referenceDate) { int minutes = minutesIntoDay % 60; int hoursIntoDay = minutesIntoDay / 60; int hours = hoursIntoDay % 24; - + // Set the calendar to use the reference time so that get the // proper date. calendar.setTime(referenceDate); - + // Set the seconds, minutes, and hours so that the calendar has // the proper time. Need to also set milliseconds because otherwise // would use milliseconds from the referenceDate. @@ -301,10 +423,10 @@ public long getEpochTime(int secondsIntoDay, Date referenceDate) { calendar.set(Calendar.SECOND, seconds); calendar.set(Calendar.MINUTE, minutes); calendar.set(Calendar.HOUR_OF_DAY, hours); - + // Get the epoch time long epochTime = calendar.getTimeInMillis(); - + // Need to make sure that didn't have a problem around midnight. // For example, a vehicle is supposed to depart a layover at // 00:05:00 right after midnight but the AVL time might be for @@ -327,15 +449,15 @@ public long getEpochTime(int secondsIntoDay, Date referenceDate) { // add a day epochTime += MS_PER_DAY; } - + // Get the results return epochTime; } } - + /** * Converts secondsIntoDay into an epoch time. - * + * * @param secondsIntoDay * To be converted into epoch time * @param referenceTime @@ -346,7 +468,7 @@ public long getEpochTime(int secondsIntoDay, Date referenceDate) { public long getEpochTime(int secondsIntoDay, long referenceTime) { return getEpochTime(secondsIntoDay, new Date(referenceTime)); } - + /** * Returns time of day in msecs. But uses reference time to determine * if interested in a time before midnight (a negative value) or a @@ -354,7 +476,7 @@ public long getEpochTime(int secondsIntoDay, long referenceTime) { * at schedule adherence and such it is much easier to deal with * situations where have blocks that span midnight. Only need to * get the time and do schedule adherence comparison once. - * + * * @param epochTime * @param referenceTimeIntoDayMsecs * @return @@ -362,20 +484,20 @@ public long getEpochTime(int secondsIntoDay, long referenceTime) { public long getMsecsIntoDay(Date epochTime, long referenceTimeIntoDayMsecs) { int timeIntoDay = getMsecsIntoDay(epochTime); long delta = Math.abs(referenceTimeIntoDayMsecs - timeIntoDay); - - long deltaForBeforeMidnight = + + long deltaForBeforeMidnight = Math.abs(referenceTimeIntoDayMsecs - (timeIntoDay - Time.MS_PER_DAY)); if (deltaForBeforeMidnight < delta) return timeIntoDay - (int)Time.MS_PER_DAY; - - long deltaForAfterMidnight = + + long deltaForAfterMidnight = Math.abs(referenceTimeIntoDayMsecs - (timeIntoDay + Time.MS_PER_DAY)); if (deltaForAfterMidnight < delta) return timeIntoDay + (int)Time.MS_PER_DAY; - + return timeIntoDay; } - + /** * Parses the dateStr into a Date using the timezone for this Time object. * @param dateStr @@ -383,14 +505,14 @@ public long getMsecsIntoDay(Date epochTime, long referenceTimeIntoDayMsecs) { * @throws ParseException */ public Date parseUsingTimezone(String dateStr) throws ParseException { - return readableDateFormatForTimeZone.parse(dateStr); + return getReadableDateFormatForTimeZone().parse(dateStr); } - + /** * Parses the datetimeStr and returns a Date object. Format is * "MM-dd-yyyy HH:mm:ss z". Tries multiple formats including with * milliseconds and with and without time zones. - * + * * @param datetimeStr * @return * @throws ParseException @@ -398,72 +520,72 @@ public Date parseUsingTimezone(String dateStr) throws ParseException { public static Date parse(String datetimeStr) throws ParseException { // First try with timezone and msec, the most complete form try { - Date date = readableDateFormat24Msec.parse(datetimeStr); + Date date = getReadableDateFormat24Msec().parse(datetimeStr); return date; } catch (ParseException e) {} // Got exception so try without timezone but still try msec try { - Date date = readableDateFormat24NoTimeZoneMsec.parse(datetimeStr); + Date date = getReadableDateFormat24NoTimeZoneMsec().parse(datetimeStr); return date; } catch (ParseException e) {} - + // Still not working so try without seconds but with timezone try { - Date date = readableDateFormat24.parse(datetimeStr); + Date date = getReadableDateFormat24().parse(datetimeStr); return date; } catch (ParseException e) {} - + // Still not working so try without msecs and without timezone try { - Date date = readableDateFormat24NoTimeZoneNoMsec.parse(datetimeStr); + Date date = getReadableDateFormat24NoTimeZoneNoMsec().parse(datetimeStr); return date; } catch (ParseException e) {} - + // Still not working so try without seconds and without timezone try { - Date date = readableDateFormat24NoSecs.parse(datetimeStr); + Date date = getReadableDateFormat24NoSecs().parse(datetimeStr); return date; } catch (ParseException e) {} - + // Still not working so try date alone. This will ignore any time // specification so this attempt needs to be done after trying all // the other formats. try { - Date date = readableDateFormat.parse(datetimeStr); - return date; + Date date = getReadableDateFormat().parse(datetimeStr); + return date; } catch (ParseException e) {} - + // As last resort try the default syntax. Will throw a ParseException // if can't parse. return new SimpleDateFormat().parse(datetimeStr); } - + /** * Parses the dateStr and returns a Date object. Format of * date is "MM-dd-yyyy". - * + * * @param dateStr * @return * @throws ParseException */ public static Date parseDate(String dateStr) throws ParseException { try { - return defaultDateFormat.parse(dateStr); + return getDefaultDateFormat().parse(dateStr); } catch (ParseException e) {} // Try using "-" instead of "/" as separator. Having the date formatter // specify only two digits for the year means it also works when 4 // digits are used, making it pretty versatile. - return dateFormatDashesShortYear.parse(dateStr); + return getDateFormatDashesShortYear().parse(dateStr); } - + /** * Parses a time such as HH:MM:SS or HH:MM into seconds into the day. * Instead of using SimpleDateFormat or such this function does the * conversion directly and simply in order to be quicker. This is useful for * reading in large volumes of GTFS data and such. - * + * * @return Seconds into the day */ public static int parseTimeOfDay(String timeStr) { @@ -474,8 +596,8 @@ public static int parseTimeOfDay(String timeStr) { String positiveTimeStr = negative ? timeStr.substring(1) : timeStr; int firstColon = positiveTimeStr.indexOf(":"); - int hours = Integer.parseInt(positiveTimeStr.substring(0, firstColon)); - + int hours = Integer.parseInt(positiveTimeStr.substring(0, firstColon)); + // If there is a second colon then also process seconds int secondColon = positiveTimeStr.lastIndexOf(":"); int minutes, seconds; @@ -486,20 +608,20 @@ public static int parseTimeOfDay(String timeStr) { } else { // No second colon so just handle minutes minutes = Integer.parseInt(positiveTimeStr.substring(firstColon+1)); - seconds = 0; + seconds = 0; } - + int result = hours * 60 * 60 + minutes*60 + seconds; if (negative) return -result; else return result; } - + /** * Converts seconds in day to a string HH:MM:SS. * Note: secInDay can be negative. - * + * * @param secInDay * @return */ @@ -512,7 +634,7 @@ public static String timeOfDayStr(long secInDay) { long hours = secInDay / (60*60); long minutes = (secInDay % (60*60)) / 60; long seconds = secInDay % 60; - + // Use StringBuilder instead of just concatenating strings since it // indeed is faster. Actually measured it and when writing out // GTFS stop_times file it was about 10% faster when using @@ -527,12 +649,12 @@ public static String timeOfDayStr(long secInDay) { b.append(seconds); return b.toString(); } - + /** * Converts seconds in day to a string HH:MM:SS. * If secInDay null then returns null. * Note: secInDay can be negative. - * + * * @param secInDay * @return Can be null */ @@ -545,7 +667,7 @@ public static String timeOfDayStr(Integer secInDay) { /** * Converts seconds in day to a string HH:MM. * Note: secInDay can be negative. - * + * * @param secInDay * @return */ @@ -557,7 +679,7 @@ public static String timeOfDayShortStr(long secInDay) { } long hours = secInDay / (60*60); long minutes = (secInDay % (60*60)) / 60; - + // Use StringBuilder instead of just concatenating strings since it // indeed is faster. Actually measured it and when writing out // GTFS stop_times file it was about 10% faster when using @@ -570,12 +692,12 @@ public static String timeOfDayShortStr(long secInDay) { b.append(minutes); return b.toString(); } - + /** * Converts seconds in day to a string HH:MM. * If secInDay null then returns null. * Note: secInDay can be negative. - * + * * @param secInDay * @return Can be null */ @@ -588,7 +710,7 @@ public static String timeOfDayShortStr(Integer secInDay) { /** * Converts seconds in day to a string HH:MM AM/PM. * Note: secInDay can be negative. - * + * * @param secInDay * @return */ @@ -598,21 +720,21 @@ public static String timeOfDayAmPmStr(long secInDay) { timeStr="-"; secInDay = -secInDay; } - + // Handle if time is into next day if (secInDay > 24*60*60) secInDay -= 24*60*60; - + // Handle if PM instead of AM boolean pm = false; if (secInDay > 12*60*60) { pm = true; secInDay -= 12*60*60; } - + long hours = secInDay / (60*60); long minutes = (secInDay % (60*60)) / 60; - + // Use StringBuilder instead of just concatenating strings since it // indeed is faster. Actually measured it and when writing out // GTFS stop_times file it was about 10% faster when using @@ -623,17 +745,17 @@ public static String timeOfDayAmPmStr(long secInDay) { b.append(hours).append(":"); if (minutes < 10) b.append("0"); b.append(minutes); - if (pm) + if (pm) b.append("PM"); else b.append("AM"); return b.toString(); } - + /** * Converts seconds in day to a string HH:MM AM/PM. * Note: secInDay can be negative. - * + * * @param secInDay Can be null * @return */ @@ -642,7 +764,7 @@ public static String timeOfDayAmPmStr(Integer secInDay) { return null; return timeOfDayAmPmStr(secInDay.intValue()); } - + /** * Outputs time in minutes with a single digit past the decimal point * @param msec @@ -652,7 +774,7 @@ public static String minutesStr(long msec) { float minutes = (float) msec / Time.MS_PER_MIN; return oneDigitFormat.format(minutes); } - + /** * Outputs time in seconds with a single digit past the decimal point * @param msec @@ -669,7 +791,7 @@ public static String secondsStr(long msec) { * then it is displayed in minutes. For both, 1 digit after the * decimal point is displayed. The units, either " sec" or " msec" * are appended. - * + * * @param msec * @return */ @@ -680,83 +802,83 @@ public static String elapsedTimeStr(long msec) { return Time.minutesStr(msec) + " min"; } } - + /** * Returns date in format "MM-dd-yyyy" * @param epochTime * @return */ public static String dateStr(long epochTime) { - return readableDateFormat.format(epochTime); + return getReadableDateFormat().format(epochTime); } - + /** * Returns date in format "MM-dd-yyyy" * @param epochTime * @return */ public static String dateStr(Date epochTime) { - return readableDateFormat.format(epochTime); + return getReadableDateFormat().format(epochTime); } - + /** * Returns epochTime as a string in the format MM-dd-yyyy HH:mm:ss z * @param epochTime * @return */ public static String dateTimeStr(long epochTime) { - return readableDateFormat24.format(epochTime); + return getReadableDateFormat24().format(epochTime); } - + /** * Returns epochTime as a string in the format MM-dd-yyyy HH:mm:ss z - * + * * @param epochTime * @return */ public static String dateTimeStr(Date epochTime) { - return readableDateFormat24.format(epochTime.getTime()); - } - + return getReadableDateFormat24().format(epochTime.getTime()); + } + /** * Returns epochTime as a string in the format MM-dd-yyyy HH:mm:ss.SSS z - * + * * @param epochTime * @return */ public static String dateTimeStrMsec(long epochTime) { - return readableDateFormat24Msec.format(epochTime); + return getReadableDateFormat24Msec().format(epochTime); } - + /** * Returns epochTime as a string in the format MM-dd-yyyy HH:mm:ss.SSS z * but does so for the Timezone specified by this Time object. - * + * * @param epochTime * @return */ public String dateTimeStrMsecForTimezone(long epochTime) { - return readableDateFormat24MsecForTimeZone.format(epochTime); + return getReadableDateFormat24MsecForTimeZone().format(epochTime); } - + public String timeStrForTimezone(long epochTime) { return readableTimeFormatForTimeZone.format(epochTime); } - + /** * Returns epochTime as a string, including msec, in the * format MM-dd-yyyy HH:mm:ss.SSS z - * + * * @param epochTime * @return */ public static String dateTimeStrMsec(Date epochTime) { - return readableDateFormat24Msec.format(epochTime.getTime()); - } - + return getReadableDateFormat24Msec().format(epochTime.getTime()); + } + /** * Returns just the time string in format "HH:mm:ss z" - * + * * @param epochTime * @return */ @@ -766,27 +888,27 @@ public static String timeStr(long epochTime) { /** * Returns just the time string in format "HH:mm:ss z" - * + * * @param epochTime * @return */ public static String timeStr(Date epochTime) { return timeStr(epochTime.getTime()); } - + /** * Returns just the time string in format "HH:mm:ss" - * + * * @param epochTime * @return */ public static String timeStrNoTimeZone(long epochTime) { return timeFormat24NoTimezone.format(epochTime); } - + /** * Returns just the time string in format "HH:mm:ss" - * + * * @param epochTime * @return */ @@ -796,18 +918,18 @@ public static String timeStrNoTimeZone(Date epochTime) { /** * Returns just the time string. Includes msec. - * + * * @param epochTime * @return */ public static String timeStrMsec(Date epochTime) { return timeFormat24Msec.format(epochTime.getTime()); } - + /** * Returns just the time string. Includes msec. * e.g. "HH:mm:ss.SSS z" - * + * * @param epochTime * @return */ @@ -818,18 +940,18 @@ public static String timeStrMsec(long epochTime) { /** * Returns just the time string. Includes msec but no timezone. * e.g. "HH:mm:ss.SSS" - * + * * @param epochTime * @return */ public static String timeStrMsecNoTimeZone(long epochTime) { return timeFormat24MsecNoTimeZone.format(epochTime); } - + /** * Returns just the time string. Includes msec but no timezone. * e.g. "HH:mm:ss.SSS" - * + * * @param epochTime * @return */ @@ -840,7 +962,7 @@ public static String timeStrMsecNoTimeZone(Date epochTime) { /** * For when sending date as part of http request. * Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 - * + * * @param epochTime * @return */ @@ -848,27 +970,34 @@ public static String httpDate(long epochTime) { httpFormat.setTimeZone(TimeZone.getTimeZone("GMT")); return httpFormat.format(epochTime); } - + /** * Returns the absolute value of the difference between the two times. If * the difference is greater than 12 hours then 24hours-difference is * returned. This is useful for when the times wrap around midnight. For * example, if the times are 11:50pm and 12:05am then the difference will be * 15 minutes instead of 23 hours and 45 minutes. - * + * * @param time1SecsIntoDay * @param time2SecsIntoDay * @return The absolute value of the difference between the two times */ public static int getTimeDifference(int time1SecsIntoDay, - int time2SecsIntoDay) { + int time2SecsIntoDay) { int timeDiffSecs = Math.abs(time1SecsIntoDay - time2SecsIntoDay); if (timeDiffSecs > 12 * Time.SEC_PER_HOUR) return Time.SEC_PER_DAY - timeDiffSecs; else return timeDiffSecs; } - + + public static Long getTimeDifference(Date minuendDate, Date subtrahendDate) { + if(minuendDate != null && subtrahendDate !=null){ + return Math.abs(minuendDate.getTime() - subtrahendDate.getTime()); + } + return null; + } + /** * Simply calls Thread.sleep() but catches the InterruptedException * so that the calling function doesn't need to. @@ -881,7 +1010,7 @@ public static void sleep(long msec) { // ignore } } - + public static void main(String args[]) { try { // TODO make this a unit test @@ -891,6 +1020,6 @@ public static void main(String args[]) { long epochTime = time.getEpochTime(secondsIntoDay, referenceDate); System.out.println(new Date(epochTime)); } catch (ParseException e) {} - + } } diff --git a/transitime/src/main/java/org/transitime/utils/TimeZoneSetter.java b/transitclock/src/main/java/org/transitclock/utils/TimeZoneSetter.java similarity index 87% rename from transitime/src/main/java/org/transitime/utils/TimeZoneSetter.java rename to transitclock/src/main/java/org/transitclock/utils/TimeZoneSetter.java index 801a1146d..e63cbe1e0 100644 --- a/transitime/src/main/java/org/transitime/utils/TimeZoneSetter.java +++ b/transitclock/src/main/java/org/transitclock/utils/TimeZoneSetter.java @@ -13,11 +13,11 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.utils; +package org.transitclock.utils; import java.util.TimeZone; -import org.transitime.config.StringConfigValue; +import org.transitclock.config.StringConfigValue; /** * For setting timezone for application. Ideally would get timezone from the @@ -33,7 +33,7 @@ public static String getTimezone() { return timezone.getValue(); } private static StringConfigValue timezone = - new StringConfigValue("transitime.core.timezone", + new StringConfigValue("transitclock.core.timezone", "For setting timezone for application. Ideally would get " + "timezone from the agency db but once a Hibernate " + "session factory is created, such as for reading " @@ -43,7 +43,7 @@ public static String getTimezone() { /** * For setting timezone for application to name specified by the Java - * property transitime.core.timezone. Ideally would get timezone from the + * property transitclock.core.timezone. Ideally would get timezone from the * agency db but once a Hibernate session factory is created, such as for * reading timezone from db, then it is too late to set the timezone. * Therefore this provides ability to set it manually. diff --git a/transitime/src/main/java/org/transitime/utils/Timer.java b/transitclock/src/main/java/org/transitclock/utils/Timer.java similarity index 95% rename from transitime/src/main/java/org/transitime/utils/Timer.java rename to transitclock/src/main/java/org/transitclock/utils/Timer.java index 5386cac6b..69cc7d6a7 100644 --- a/transitime/src/main/java/org/transitime/utils/Timer.java +++ b/transitclock/src/main/java/org/transitclock/utils/Timer.java @@ -14,11 +14,11 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.utils; +package org.transitclock.utils; import java.util.concurrent.ScheduledThreadPoolExecutor; -import org.transitime.utils.threading.NamedThreadFactory; +import org.transitclock.utils.threading.NamedThreadFactory; /** * The standard java.util.Timer and java.util.TimerTask classes should be diff --git a/transitime/src/main/java/org/transitime/utils/TrimmableArrayList.java b/transitclock/src/main/java/org/transitclock/utils/TrimmableArrayList.java similarity index 95% rename from transitime/src/main/java/org/transitime/utils/TrimmableArrayList.java rename to transitclock/src/main/java/org/transitclock/utils/TrimmableArrayList.java index 44a796022..9c81e2e3f 100644 --- a/transitime/src/main/java/org/transitime/utils/TrimmableArrayList.java +++ b/transitclock/src/main/java/org/transitclock/utils/TrimmableArrayList.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.utils; +package org.transitclock.utils; import java.util.ArrayList; diff --git a/transitime/src/main/java/org/transitime/utils/Zip.java b/transitclock/src/main/java/org/transitclock/utils/Zip.java similarity index 95% rename from transitime/src/main/java/org/transitime/utils/Zip.java rename to transitclock/src/main/java/org/transitclock/utils/Zip.java index 710d6df0d..1504e1522 100644 --- a/transitime/src/main/java/org/transitime/utils/Zip.java +++ b/transitclock/src/main/java/org/transitclock/utils/Zip.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.utils; +package org.transitclock.utils; import java.io.BufferedInputStream; import java.io.File; @@ -67,7 +67,10 @@ public static String unzip(String zipFileName, String subDirectory) { String parentDirName = new File(zipFileName).getParent(); String directoryName = parentDirName; if (subDirectory != null) { - directoryName += "/" + subDirectory; + if(subDirectory.startsWith(File.separator)) + directoryName=subDirectory; + else + directoryName += "/" + subDirectory; } logger.info("Unzipping file {} into directory {}", diff --git a/transitime/src/main/java/org/transitime/utils/csv/CsvBase.java b/transitclock/src/main/java/org/transitclock/utils/csv/CsvBase.java similarity index 99% rename from transitime/src/main/java/org/transitime/utils/csv/CsvBase.java rename to transitclock/src/main/java/org/transitclock/utils/csv/CsvBase.java index 2f79350b7..e26ce1c89 100644 --- a/transitime/src/main/java/org/transitime/utils/csv/CsvBase.java +++ b/transitclock/src/main/java/org/transitclock/utils/csv/CsvBase.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.utils.csv; +package org.transitclock.utils.csv; import org.apache.commons.csv.CSVRecord; import org.slf4j.Logger; diff --git a/transitime/src/main/java/org/transitime/utils/csv/CsvBaseReader.java b/transitclock/src/main/java/org/transitclock/utils/csv/CsvBaseReader.java similarity index 94% rename from transitime/src/main/java/org/transitime/utils/csv/CsvBaseReader.java rename to transitclock/src/main/java/org/transitclock/utils/csv/CsvBaseReader.java index c023b4b4d..edf9c2b60 100644 --- a/transitime/src/main/java/org/transitime/utils/csv/CsvBaseReader.java +++ b/transitclock/src/main/java/org/transitclock/utils/csv/CsvBaseReader.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.utils.csv; +package org.transitclock.utils.csv; import java.io.BufferedReader; import java.io.FileInputStream; @@ -22,6 +22,7 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; +import java.net.URL; import java.text.ParseException; import java.util.ArrayList; import java.util.Iterator; @@ -31,8 +32,8 @@ import org.apache.commons.csv.CSVRecord; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.utils.IntervalTimer; -import org.transitime.utils.Time; +import org.transitclock.utils.IntervalTimer; +import org.transitclock.utils.Time; /** * For parsing a CSV file. Does all of the hard work. This class is @@ -125,9 +126,9 @@ private void parse() { // first character and then discard if it is a BOM character or // reset the reader to back to the beginning if it is not. This // way the CSV parser will process the file starting with the first - // true character. - Reader in = new BufferedReader(new InputStreamReader( - new FileInputStream(fileName), "UTF-8")); + // true character. + + Reader in = new BufferedReader(new InputStreamReader(new FileInputStream(fileName), "UTF-8")); // Deal with the possible BOM character at the beginning of the file in.mark(1); diff --git a/transitime/src/main/java/org/transitime/utils/csv/CsvWriterBase.java b/transitclock/src/main/java/org/transitclock/utils/csv/CsvWriterBase.java similarity index 98% rename from transitime/src/main/java/org/transitime/utils/csv/CsvWriterBase.java rename to transitclock/src/main/java/org/transitclock/utils/csv/CsvWriterBase.java index edcd864c5..4f5bd227a 100644 --- a/transitime/src/main/java/org/transitime/utils/csv/CsvWriterBase.java +++ b/transitclock/src/main/java/org/transitclock/utils/csv/CsvWriterBase.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.utils.csv; +package org.transitclock.utils.csv; import java.io.BufferedWriter; import java.io.File; @@ -22,7 +22,8 @@ import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; -import org.transitime.utils.StringUtils; + +import org.transitclock.utils.StringUtils; /** * A base class for writing out a CSV file. A subclass needs diff --git a/transitime/src/main/java/org/transitime/utils/csv/package-info.java b/transitclock/src/main/java/org/transitclock/utils/csv/package-info.java similarity index 96% rename from transitime/src/main/java/org/transitime/utils/csv/package-info.java rename to transitclock/src/main/java/org/transitclock/utils/csv/package-info.java index 603864cf2..90071a30c 100644 --- a/transitime/src/main/java/org/transitime/utils/csv/package-info.java +++ b/transitclock/src/main/java/org/transitclock/utils/csv/package-info.java @@ -23,4 +23,4 @@ * @author SkiBu Smith * */ -package org.transitime.utils.csv; \ No newline at end of file +package org.transitclock.utils.csv; \ No newline at end of file diff --git a/transitime/src/main/java/org/transitime/utils/package-info.java b/transitclock/src/main/java/org/transitclock/utils/package-info.java similarity index 96% rename from transitime/src/main/java/org/transitime/utils/package-info.java rename to transitclock/src/main/java/org/transitclock/utils/package-info.java index 34d6a697c..9caaed516 100644 --- a/transitime/src/main/java/org/transitime/utils/package-info.java +++ b/transitclock/src/main/java/org/transitclock/utils/package-info.java @@ -21,4 +21,4 @@ * @author SkiBu Smith * */ -package org.transitime.utils; \ No newline at end of file +package org.transitclock.utils; \ No newline at end of file diff --git a/transitime/src/main/java/org/transitime/utils/threading/BoundedExecutor.java b/transitclock/src/main/java/org/transitclock/utils/threading/BoundedExecutor.java similarity index 97% rename from transitime/src/main/java/org/transitime/utils/threading/BoundedExecutor.java rename to transitclock/src/main/java/org/transitclock/utils/threading/BoundedExecutor.java index 88b25fe88..54ab2f185 100644 --- a/transitime/src/main/java/org/transitime/utils/threading/BoundedExecutor.java +++ b/transitclock/src/main/java/org/transitclock/utils/threading/BoundedExecutor.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.utils.threading; +package org.transitclock.utils.threading; import java.util.concurrent.Executor; import java.util.concurrent.RejectedExecutionException; @@ -22,8 +22,8 @@ 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; /** * An Executor but limits how many tasks can be queued. If queue is full and diff --git a/transitime/src/main/java/org/transitime/utils/threading/LoggingThreadPoolExecutor.java b/transitclock/src/main/java/org/transitclock/utils/threading/LoggingThreadPoolExecutor.java similarity index 97% rename from transitime/src/main/java/org/transitime/utils/threading/LoggingThreadPoolExecutor.java rename to transitclock/src/main/java/org/transitclock/utils/threading/LoggingThreadPoolExecutor.java index cc809b9d1..447ae162b 100644 --- a/transitime/src/main/java/org/transitime/utils/threading/LoggingThreadPoolExecutor.java +++ b/transitclock/src/main/java/org/transitclock/utils/threading/LoggingThreadPoolExecutor.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.utils.threading; +package org.transitclock.utils.threading; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ThreadPoolExecutor; diff --git a/transitime/src/main/java/org/transitime/utils/threading/NamedThread.java b/transitclock/src/main/java/org/transitclock/utils/threading/NamedThread.java similarity index 97% rename from transitime/src/main/java/org/transitime/utils/threading/NamedThread.java rename to transitclock/src/main/java/org/transitclock/utils/threading/NamedThread.java index a499716e1..715f99bfe 100644 --- a/transitime/src/main/java/org/transitime/utils/threading/NamedThread.java +++ b/transitclock/src/main/java/org/transitclock/utils/threading/NamedThread.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.utils.threading; +package org.transitclock.utils.threading; import java.util.Date; import java.util.HashMap; @@ -22,8 +22,8 @@ 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; /** * Creates a Thread but sets the name of it and sets the diff --git a/transitime/src/main/java/org/transitime/utils/threading/NamedThreadFactory.java b/transitclock/src/main/java/org/transitclock/utils/threading/NamedThreadFactory.java similarity index 96% rename from transitime/src/main/java/org/transitime/utils/threading/NamedThreadFactory.java rename to transitclock/src/main/java/org/transitclock/utils/threading/NamedThreadFactory.java index 4b2562616..9f8d459e3 100644 --- a/transitime/src/main/java/org/transitime/utils/threading/NamedThreadFactory.java +++ b/transitclock/src/main/java/org/transitclock/utils/threading/NamedThreadFactory.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.utils.threading; +package org.transitclock.utils.threading; import java.util.concurrent.ThreadFactory; diff --git a/transitime/src/main/java/org/transitime/utils/threading/ThreadTest.java b/transitclock/src/main/java/org/transitclock/utils/threading/ThreadTest.java similarity index 97% rename from transitime/src/main/java/org/transitime/utils/threading/ThreadTest.java rename to transitclock/src/main/java/org/transitclock/utils/threading/ThreadTest.java index 9800c6642..421c8c899 100644 --- a/transitime/src/main/java/org/transitime/utils/threading/ThreadTest.java +++ b/transitclock/src/main/java/org/transitclock/utils/threading/ThreadTest.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.utils.threading; +package org.transitclock.utils.threading; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; diff --git a/transitime/src/main/java/org/transitime/utils/threading/package-info.java b/transitclock/src/main/java/org/transitclock/utils/threading/package-info.java similarity index 95% rename from transitime/src/main/java/org/transitime/utils/threading/package-info.java rename to transitclock/src/main/java/org/transitclock/utils/threading/package-info.java index 2d4033fa9..dd8d0bfaa 100644 --- a/transitime/src/main/java/org/transitime/utils/threading/package-info.java +++ b/transitclock/src/main/java/org/transitclock/utils/threading/package-info.java @@ -24,4 +24,4 @@ * @author SkiBu Smith * */ -package org.transitime.utils.threading; \ No newline at end of file +package org.transitclock.utils.threading; \ No newline at end of file diff --git a/transitime/src/main/java/org/transitime/web/ReadConfigListener.java b/transitclock/src/main/java/org/transitclock/web/ReadConfigListener.java similarity index 90% rename from transitime/src/main/java/org/transitime/web/ReadConfigListener.java rename to transitclock/src/main/java/org/transitclock/web/ReadConfigListener.java index 165b58261..4508c13e5 100644 --- a/transitime/src/main/java/org/transitime/web/ReadConfigListener.java +++ b/transitclock/src/main/java/org/transitclock/web/ReadConfigListener.java @@ -14,32 +14,32 @@ * You should have received a copy of the GNU General Public License along with * Transitime.org . If not, see . */ -package org.transitime.web; +package org.transitclock.web; /** * To be called when webapp starts up so that the configuration xml or * properties file is read in. The webapp web.xml file should be configured to * associate this class as a listener so that contextInitialized() is * automatically called at webapp startup. And the initialization parameter - * transitime_config_file_location needs to be set to specify the config file to + * transitclock_config_file_location needs to be set to specify the config file to * read in. *

    * When running Tomcat within Eclipse it is most likely easiest to just set the * necessary Java properties in the Debug Configurations VM properties. *

    - * This class in in the main transitime package so that it can be used by all + * This class in in the main transitclock package so that it can be used by all * webapps. * * @author Sean Crudden */ +import org.transitclock.config.ConfigFileReader; + import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; -import org.transitime.config.ConfigFileReader; - public class ReadConfigListener implements ServletContextListener { private static final String FILE_LOCATION_PARAM_NAME = - "transitime_config_file_location"; + "transitclock_config_file_location"; /** * Doesn't need to do anything since this class is only for reading on diff --git a/transitime/src/main/java/org/transitime/web/WebConfigParams.java b/transitclock/src/main/java/org/transitclock/web/WebConfigParams.java similarity index 84% rename from transitime/src/main/java/org/transitime/web/WebConfigParams.java rename to transitclock/src/main/java/org/transitclock/web/WebConfigParams.java index 737cc1706..6817e4bee 100644 --- a/transitime/src/main/java/org/transitime/web/WebConfigParams.java +++ b/transitclock/src/main/java/org/transitclock/web/WebConfigParams.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU General Public License along with * Transitime.org . If not, see . */ -package org.transitime.web; +package org.transitclock.web; -import org.transitime.config.StringConfigValue; +import org.transitclock.config.StringConfigValue; /** * Contains Java properties use by web server. These parameters are read in @@ -30,7 +30,7 @@ public static String getMapTileUrl() { return mapTileUrl.getValue(); } private static StringConfigValue mapTileUrl = - new StringConfigValue("transitime.web.mapTileUrl", + new StringConfigValue("transitclock.web.mapTileUrl", "http://otile4.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.png", "Specifies the URL used by Leaflet maps to fetch map " + "tiles."); @@ -39,7 +39,7 @@ public static String getMapTileCopyright() { return mapTileCopyright.getValue(); } private static StringConfigValue mapTileCopyright = - new StringConfigValue("transitime.web.mapTileCopyright", + new StringConfigValue("transitclock.web.mapTileCopyright", "MapQuest", "For displaying as map attributing for the where map tiles " + "from."); diff --git a/transitime/src/main/java/org/transitime/worldbank/gtfsRtExample/ConfigurableParameters.java b/transitclock/src/main/java/org/transitclock/worldbank/gtfsRtExample/ConfigurableParameters.java similarity index 98% rename from transitime/src/main/java/org/transitime/worldbank/gtfsRtExample/ConfigurableParameters.java rename to transitclock/src/main/java/org/transitclock/worldbank/gtfsRtExample/ConfigurableParameters.java index c2830c70f..29dff907b 100644 --- a/transitime/src/main/java/org/transitime/worldbank/gtfsRtExample/ConfigurableParameters.java +++ b/transitclock/src/main/java/org/transitclock/worldbank/gtfsRtExample/ConfigurableParameters.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.worldbank.gtfsRtExample; +package org.transitclock.worldbank.gtfsRtExample; /** diff --git a/transitime/src/main/java/org/transitime/worldbank/gtfsRtExample/DataFetcher.java b/transitclock/src/main/java/org/transitclock/worldbank/gtfsRtExample/DataFetcher.java similarity index 94% rename from transitime/src/main/java/org/transitime/worldbank/gtfsRtExample/DataFetcher.java rename to transitclock/src/main/java/org/transitclock/worldbank/gtfsRtExample/DataFetcher.java index e7b4ad1eb..b0958c79a 100644 --- a/transitime/src/main/java/org/transitime/worldbank/gtfsRtExample/DataFetcher.java +++ b/transitclock/src/main/java/org/transitclock/worldbank/gtfsRtExample/DataFetcher.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.worldbank.gtfsRtExample; +package org.transitclock.worldbank.gtfsRtExample; import java.sql.Connection; import java.sql.DriverManager; @@ -29,6 +29,8 @@ import java.util.Properties; import java.util.Set; +import org.transitclock.configData.DbSetupConfig; + /** * * For retrieving ApiVehicle from the database @@ -105,6 +107,12 @@ private static Connection getConnection() { Properties connectionProps = new Properties(); connectionProps.put("user", ConfigurableParameters.DB_USER); connectionProps.put("password", ConfigurableParameters.DB_PASSWORD); + // some performance optimizations for mysql + if ("mysql".equals(DbSetupConfig.getDbType())) { + connectionProps.put("rewriteBatchedStatements", true); + connectionProps.put("cachePrepStmts", true); + connectionProps.put("autoReconnect", true); + } try { databaseConnection = diff --git a/transitime/src/main/java/org/transitime/worldbank/gtfsRtExample/GtfsRealtimeExample.java b/transitclock/src/main/java/org/transitclock/worldbank/gtfsRtExample/GtfsRealtimeExample.java similarity index 99% rename from transitime/src/main/java/org/transitime/worldbank/gtfsRtExample/GtfsRealtimeExample.java rename to transitclock/src/main/java/org/transitclock/worldbank/gtfsRtExample/GtfsRealtimeExample.java index dbc6005af..c0264429f 100644 --- a/transitime/src/main/java/org/transitime/worldbank/gtfsRtExample/GtfsRealtimeExample.java +++ b/transitclock/src/main/java/org/transitclock/worldbank/gtfsRtExample/GtfsRealtimeExample.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.worldbank.gtfsRtExample; +package org.transitclock.worldbank.gtfsRtExample; import java.io.BufferedWriter; import java.io.FileOutputStream; diff --git a/transitime/src/main/java/org/transitime/worldbank/gtfsRtExample/VehicleData.java b/transitclock/src/main/java/org/transitclock/worldbank/gtfsRtExample/VehicleData.java similarity index 98% rename from transitime/src/main/java/org/transitime/worldbank/gtfsRtExample/VehicleData.java rename to transitclock/src/main/java/org/transitclock/worldbank/gtfsRtExample/VehicleData.java index 3ada2642d..c0aabc9f5 100644 --- a/transitime/src/main/java/org/transitime/worldbank/gtfsRtExample/VehicleData.java +++ b/transitclock/src/main/java/org/transitclock/worldbank/gtfsRtExample/VehicleData.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.worldbank.gtfsRtExample; +package org.transitclock.worldbank.gtfsRtExample; import java.util.Date; diff --git a/transitime/src/main/java/org/transitime/worldbank/gtfsRtExample/package-info.java b/transitclock/src/main/java/org/transitclock/worldbank/gtfsRtExample/package-info.java similarity index 94% rename from transitime/src/main/java/org/transitime/worldbank/gtfsRtExample/package-info.java rename to transitclock/src/main/java/org/transitclock/worldbank/gtfsRtExample/package-info.java index 0d0e23cba..d33967a0d 100644 --- a/transitime/src/main/java/org/transitime/worldbank/gtfsRtExample/package-info.java +++ b/transitclock/src/main/java/org/transitclock/worldbank/gtfsRtExample/package-info.java @@ -23,4 +23,4 @@ * @author SkiBu Smith * */ -package org.transitime.worldbank.gtfsRtExample; \ No newline at end of file +package org.transitclock.worldbank.gtfsRtExample; \ No newline at end of file diff --git a/transitime/src/main/java/org/transitime/worldbank/package-info.java b/transitclock/src/main/java/org/transitclock/worldbank/package-info.java similarity index 96% rename from transitime/src/main/java/org/transitime/worldbank/package-info.java rename to transitclock/src/main/java/org/transitclock/worldbank/package-info.java index f39e5baff..dcc3bc387 100644 --- a/transitime/src/main/java/org/transitime/worldbank/package-info.java +++ b/transitclock/src/main/java/org/transitclock/worldbank/package-info.java @@ -24,4 +24,4 @@ * @author SkiBu Smith * */ -package org.transitime.worldbank; \ No newline at end of file +package org.transitclock.worldbank; \ No newline at end of file diff --git a/transitime/src/main/resources/.gitignore b/transitclock/src/main/resources/.gitignore similarity index 100% rename from transitime/src/main/resources/.gitignore rename to transitclock/src/main/resources/.gitignore diff --git a/transitclock/src/main/resources/cache.ccf b/transitclock/src/main/resources/cache.ccf new file mode 100644 index 000000000..eb74a67e0 --- /dev/null +++ b/transitclock/src/main/resources/cache.ccf @@ -0,0 +1,28 @@ +############################################################## +##### Default Region Configuration +jcs.default=DC +jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes +jcs.default.cacheattributes.MaxObjects=0 +jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache +jcs.default.cacheattributes.DiskUsagePatternName=UPDATE + +############################################################## +##### CACHE REGIONS +jcs.region.myRegion1=DC +jcs.region.myRegion1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes +jcs.region.myRegion1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache +jcs.region.myRegion1.cacheattributes.DiskUsagePatternName=UPDATE +jcs.region.myRegion1.elementattributes.IsEternal=true +jcs.region.myRegion1.cacheattributes.MaxObjects=0 + +############################################################## +##### AUXILIARY CACHES +# Indexed Disk Cache +jcs.auxiliary.DC=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory +jcs.auxiliary.DC.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes +jcs.auxiliary.DC.attributes.diskPath=${user.home}/jcs_swap +jcs.auxiliary.DC.attributes.MaxPurgatorySize=100000 +jcs.auxiliary.DC.attributes.MaxKeySize=100000 +jcs.auxiliary.DC.attributes.OptimizeAtRemoveCount=300000 +jcs.auxiliary.DC.attributes.OptimizeOnShutdown=true +jcs.auxiliary.DC.attributes.DiskLimitType=COUNT \ No newline at end of file diff --git a/transitime/src/main/resources/ddl_mysql_org_transitime_db_structs.sql b/transitclock/src/main/resources/ddl_mysql_org_transitclock_db_structs.sql similarity index 64% rename from transitime/src/main/resources/ddl_mysql_org_transitime_db_structs.sql rename to transitclock/src/main/resources/ddl_mysql_org_transitclock_db_structs.sql index 2a4782398..6d7a00e34 100644 --- a/transitime/src/main/resources/ddl_mysql_org_transitime_db_structs.sql +++ b/transitclock/src/main/resources/ddl_mysql_org_transitclock_db_structs.sql @@ -2,6 +2,7 @@ create table ActiveRevisions ( id integer not null auto_increment, configRev integer, + trafficRev integer, travelTimesRev integer, primary key (id) ); @@ -34,14 +35,18 @@ blockId varchar(60), configRev integer, directionId varchar(60), + dwellTime bigint, + freqStartTime datetime(3), routeId varchar(60), routeShortName varchar(60), scheduledTime datetime(3), serviceId varchar(60), stopOrder integer, + stopPathId varchar(120), stopPathIndex integer, stopPathLength float, tripIndex integer, + tripPatternId varchar(120), primary key (vehicleId, tripId, time, stopId, isArrival, gtfsStopSeq) ); @@ -66,14 +71,14 @@ ); create table Block_to_Trip_joinTable ( - Blocks_serviceId varchar(60) not null, - Blocks_configRev integer not null, - Blocks_blockId varchar(60) not null, + blocks_serviceId varchar(60) not null, + blocks_configRev integer not null, + blocks_blockId varchar(60) not null, trips_tripId varchar(60) not null, trips_startTime integer not null, trips_configRev integer not null, listIndex integer not null, - primary key (Blocks_serviceId, Blocks_configRev, Blocks_blockId, listIndex) + primary key (blocks_serviceId, blocks_configRev, blocks_blockId, listIndex) ); create table Blocks ( @@ -138,9 +143,20 @@ originId varchar(60) not null, fareId varchar(60) not null, destinationId varchar(60) not null, + containsId varchar(60) not null, configRev integer not null, - containsId varchar(60), - primary key (routeId, originId, fareId, destinationId, configRev) + primary key (routeId, originId, fareId, destinationId, containsId, configRev) + ); + + create table FeedInfo ( + feedPublisherName varchar(255) not null, + configRev integer not null, + feedEndDate date, + feedLanguage varchar(15), + feedPublisherUrl longtext, + feedStartDate date, + feedVersion varchar(120), + primary key (feedPublisherName, configRev) ); create table Frequencies ( @@ -153,6 +169,42 @@ primary key (tripId, startTime, configRev) ); + create table Headway ( + id bigint not null auto_increment, + average double precision, + coefficientOfVariation double precision, + configRev integer, + creationTime datetime(3), + firstDeparture datetime(3), + headway double precision, + numVehicles integer, + otherVehicleId varchar(60), + routeId varchar(60), + secondDeparture datetime(3), + stopId varchar(60), + tripId varchar(60), + variance double precision, + vehicleId varchar(60), + primary key (id) + ); + + create table HoldingTimes ( + id bigint not null auto_increment, + arrivalPredictionUsed bit, + arrivalTime datetime(3), + arrivalUsed bit, + configRev integer, + creationTime datetime(3), + hasD1 bit, + holdingTime datetime(3), + numberPredictionsUsed integer, + routeId varchar(60), + stopId varchar(60), + tripId varchar(60), + vehicleId varchar(60), + primary key (id) + ); + create table Matches ( vehicleId varchar(60) not null, avlTime datetime(3) not null, @@ -204,12 +256,35 @@ primary key (id) ); + create table PredictionEvents ( + vehicleId varchar(60) not null, + time datetime(3) not null, + eventType varchar(60) not null, + arrivalTime datetime(3), + arrivalstopid varchar(60), + avlTime datetime(3), + blockId varchar(60), + departureTime datetime(3), + departurestopid varchar(60), + description longtext, + lat double precision, + lon double precision, + referenceVehicleId varchar(60), + routeId varchar(60), + routeShortName varchar(60), + serviceId varchar(60), + stopId varchar(60), + tripId varchar(60), + primary key (vehicleId, time, eventType) + ); + create table Predictions ( id bigint not null auto_increment, affectedByWaitStop bit, avlTime datetime(3), configRev integer, creationTime datetime(3), + gtfsStopSeq integer, isArrival bit, predictionTime datetime(3), routeId varchar(60), @@ -224,22 +299,45 @@ id varchar(60) not null, configRev integer not null, color varchar(10), - description varchar(255), + description longtext, maxLat double precision, maxLon double precision, minLat double precision, minLon double precision, hidden bit, - longName varchar(80), + longName varchar(255), maxDistance double precision, - name varchar(80), + name varchar(255), routeOrder integer, - shortName varchar(80), + shortName varchar(255), textColor varchar(10), type varchar(2), primary key (id, configRev) ); + create table StopPathPredictions ( + id bigint not null auto_increment, + algorithm varchar(255), + creationTime datetime(3), + predictionTime double precision, + startTime integer, + stopPathIndex integer, + travelTime bit, + tripId varchar(60), + vehicleId varchar(255), + primary key (id) + ); + + create table StopPath_locations ( + StopPath_tripPatternId varchar(120) not null, + StopPath_stopPathId varchar(120) not null, + StopPath_configRev integer not null, + lat double precision, + lon double precision, + locations_ORDER integer not null, + primary key (StopPath_tripPatternId, StopPath_stopPathId, StopPath_configRev, locations_ORDER) + ); + create table StopPaths ( tripPatternId varchar(120) not null, stopPathId varchar(120) not null, @@ -248,7 +346,8 @@ gtfsStopSeq integer, lastStopInTrip bit, layoverStop bit, - locations blob, + maxDistance double precision, + maxSpeed double precision, pathLength double precision, routeId varchar(60), scheduleAdherenceStop bit, @@ -271,6 +370,53 @@ primary key (id, configRev) ); + create table TrafficPath_locations ( + TrafficPath_trafficRev integer not null, + TrafficPath_trafficPathId varchar(120) not null, + lat double precision, + lon double precision, + locations_ORDER integer not null, + primary key (TrafficPath_trafficRev, TrafficPath_trafficPathId, locations_ORDER) + ); + + create table TrafficPath_to_StopPath_joinTable ( + TrafficPaths_trafficRev integer not null, + TrafficPaths_trafficPathId varchar(120) not null, + stopPaths_tripPatternId varchar(120) not null, + stopPaths_stopPathId varchar(120) not null, + stopPaths_configRev integer not null, + listIndex integer not null, + primary key (TrafficPaths_trafficRev, TrafficPaths_trafficPathId, listIndex) + ); + + create table TrafficPaths ( + trafficRev integer not null, + trafficPathId varchar(120) not null, + pathLength float, + primary key (trafficRev, trafficPathId) + ); + + create table TrafficSensor ( + trafficRev integer not null, + id varchar(60) not null, + description varchar(255), + externalId varchar(60), + trafficPathId varchar(120), + primary key (trafficRev, id) + ); + + create table TrafficSensorData ( + trafficSensorId varchar(255) not null, + trafficRev integer not null, + time datetime(3) not null, + confidence double precision, + delayMillis double precision, + length double precision, + speed double precision, + travelTimeMillis integer, + primary key (trafficSensorId, trafficRev, time) + ); + create table Transfers ( toStopId varchar(60) not null, fromStopId varchar(60) not null, @@ -288,7 +434,7 @@ stopPathId varchar(120), stopTimeMsec integer, travelTimeSegmentLength float, - travelTimesMsec blob, + travelTimesMsec mediumblob, travelTimesRev integer, primary key (id) ); @@ -334,6 +480,16 @@ primary key (id, configRev) ); + create table Trip_scheduledTimesList ( + Trip_tripId varchar(60) not null, + Trip_startTime integer not null, + Trip_configRev integer not null, + arrivalTime integer, + departureTime integer, + scheduledTimesList_ORDER integer not null, + primary key (Trip_tripId, Trip_startTime, Trip_configRev, scheduledTimesList_ORDER) + ); + create table Trips ( tripId varchar(60) not null, startTime integer not null, @@ -346,7 +502,6 @@ noSchedule bit, routeId varchar(60), routeShortName varchar(60), - scheduledTimesList blob, serviceId varchar(60), shapeId varchar(60), tripShortName varchar(60), @@ -410,8 +565,14 @@ create index ArrivalsDeparturesRouteTimeIndex on ArrivalsDepartures (routeShortName, time); + create index ArrivalsDeparturesTripPatternIdIndex on ArrivalsDepartures (tripPatternId); + create index AvlReportsTimeIndex on AvlReports (time); + create index HeadwayIndex on Headway (creationTime); + + create index HoldingTimeIndex on HoldingTimes (creationTime); + create index AvlTimeIndex on Matches (avlTime); create index MeasuredArrivalTimesIndex on MeasuredArrivalTimes (time); @@ -420,8 +581,15 @@ create index PredictionAccuracyTimeIndex on PredictionAccuracy (arrivalDepartureTime); + create index PredictionEventsTimeIndex on PredictionEvents (time); + create index PredictionTimeIndex on Predictions (creationTime); + create index StopPathPredictionTimeIndex on StopPathPredictions (tripId, stopPathIndex); + + alter table TrafficPath_to_StopPath_joinTable + add constraint UK_ohqplmhw0t46tipi7i9bxuur8 unique (stopPaths_tripPatternId, stopPaths_stopPathId, stopPaths_configRev); + create index TravelTimesRevIndex on TravelTimesForTrips (travelTimesRev); alter table TripPattern_to_Path_joinTable @@ -431,16 +599,46 @@ create index VehicleStateAvlTimeIndex on VehicleStates (avlTime); + alter table ArrivalsDepartures + add constraint FK_m1eyesv8rr42fo6qpcrkcgjp3 + foreign key (stopId, configRev) + references Stops (id, configRev); + + alter table ArrivalsDepartures + add constraint FK_axgfl7fxphggp7qcwy6h8vbs4 + foreign key (tripPatternId, stopPathId, configRev) + references StopPaths (tripPatternId, stopPathId, configRev); + alter table Block_to_Trip_joinTable add constraint FK_abaj8ke6oh4imbbgnaercsowo foreign key (trips_tripId, trips_startTime, trips_configRev) references Trips (tripId, startTime, configRev); alter table Block_to_Trip_joinTable - add constraint FK_1c1e1twdap19vq0xkav0amvm - foreign key (Blocks_serviceId, Blocks_configRev, Blocks_blockId) + add constraint FK_kobr9qxbawdjnf5fced46rfpo + foreign key (blocks_serviceId, blocks_configRev, blocks_blockId) references Blocks (serviceId, configRev, blockId); + alter table StopPath_locations + add constraint FK_sdjt3vtd3w0cl07p0doob6khi + foreign key (StopPath_tripPatternId, StopPath_stopPathId, StopPath_configRev) + references StopPaths (tripPatternId, stopPathId, configRev); + + alter table TrafficPath_locations + add constraint FK_j3otbyk8qsh9rg02q8kk8931q + foreign key (TrafficPath_trafficRev, TrafficPath_trafficPathId) + references TrafficPaths (trafficRev, trafficPathId); + + alter table TrafficPath_to_StopPath_joinTable + add constraint FK_ohqplmhw0t46tipi7i9bxuur8 + foreign key (stopPaths_tripPatternId, stopPaths_stopPathId, stopPaths_configRev) + references StopPaths (tripPatternId, stopPathId, configRev); + + alter table TrafficPath_to_StopPath_joinTable + add constraint FK_6aib4u1tr2wfpxoog3a5ycou9 + foreign key (TrafficPaths_trafficRev, TrafficPaths_trafficPathId) + references TrafficPaths (trafficRev, trafficPathId); + alter table TravelTimesForTrip_to_TravelTimesForPath_joinTable add constraint FK_hh5uepurijcqj0pyc6e3h5mqw foreign key (travelTimesForStopPaths_id) @@ -461,6 +659,11 @@ foreign key (TripPatterns_id, TripPatterns_configRev) references TripPatterns (id, configRev); + alter table Trip_scheduledTimesList + add constraint FK_n5et0p70cwe1dwo4m6lq0k4h0 + foreign key (Trip_tripId, Trip_startTime, Trip_configRev) + references Trips (tripId, startTime, configRev); + alter table Trips add constraint FK_p1er53449kkfsca6mbnxkdyst foreign key (travelTimes_id) diff --git a/transitime/src/main/resources/ddl_mysql_org_transitime_db_webstructs.sql b/transitclock/src/main/resources/ddl_mysql_org_transitclock_db_webstructs.sql similarity index 100% rename from transitime/src/main/resources/ddl_mysql_org_transitime_db_webstructs.sql rename to transitclock/src/main/resources/ddl_mysql_org_transitclock_db_webstructs.sql diff --git a/transitime/src/main/resources/ddl_oracle_org_transitime_db_structs.sql b/transitclock/src/main/resources/ddl_oracle_org_transitclock_db_structs.sql similarity index 65% rename from transitime/src/main/resources/ddl_oracle_org_transitime_db_structs.sql rename to transitclock/src/main/resources/ddl_oracle_org_transitclock_db_structs.sql index 60645b28b..ae0c1e1de 100644 --- a/transitime/src/main/resources/ddl_oracle_org_transitime_db_structs.sql +++ b/transitclock/src/main/resources/ddl_oracle_org_transitclock_db_structs.sql @@ -2,6 +2,7 @@ create table ActiveRevisions ( id number(10,0) not null, configRev number(10,0), + trafficRev number(10,0), travelTimesRev number(10,0), primary key (id) ); @@ -34,14 +35,18 @@ blockId varchar2(60 char), configRev number(10,0), directionId varchar2(60 char), + dwellTime number(19,0), + freqStartTime timestamp, routeId varchar2(60 char), routeShortName varchar2(60 char), scheduledTime timestamp, serviceId varchar2(60 char), stopOrder number(10,0), + stopPathId varchar2(120 char), stopPathIndex number(10,0), stopPathLength float, tripIndex number(10,0), + tripPatternId varchar2(120 char), primary key (vehicleId, tripId, time, stopId, isArrival, gtfsStopSeq) ); @@ -66,14 +71,14 @@ ); create table Block_to_Trip_joinTable ( - Blocks_serviceId varchar2(60 char) not null, - Blocks_configRev number(10,0) not null, - Blocks_blockId varchar2(60 char) not null, + blocks_serviceId varchar2(60 char) not null, + blocks_configRev number(10,0) not null, + blocks_blockId varchar2(60 char) not null, trips_tripId varchar2(60 char) not null, trips_startTime number(10,0) not null, trips_configRev number(10,0) not null, listIndex number(10,0) not null, - primary key (Blocks_serviceId, Blocks_configRev, Blocks_blockId, listIndex) + primary key (blocks_serviceId, blocks_configRev, blocks_blockId, listIndex) ); create table Blocks ( @@ -138,9 +143,20 @@ originId varchar2(60 char) not null, fareId varchar2(60 char) not null, destinationId varchar2(60 char) not null, + containsId varchar2(60 char) not null, + configRev number(10,0) not null, + primary key (routeId, originId, fareId, destinationId, containsId, configRev) + ); + + create table FeedInfo ( + feedPublisherName varchar2(255 char) not null, configRev number(10,0) not null, - containsId varchar2(60 char), - primary key (routeId, originId, fareId, destinationId, configRev) + feedEndDate date, + feedLanguage varchar2(15 char), + feedPublisherUrl varchar2(512 char), + feedStartDate date, + feedVersion varchar2(120 char), + primary key (feedPublisherName, configRev) ); create table Frequencies ( @@ -153,6 +169,42 @@ primary key (tripId, startTime, configRev) ); + create table Headway ( + id number(19,0) not null, + average double precision, + coefficientOfVariation double precision, + configRev number(10,0), + creationTime timestamp, + firstDeparture timestamp, + headway double precision, + numVehicles number(10,0), + otherVehicleId varchar2(60 char), + routeId varchar2(60 char), + secondDeparture timestamp, + stopId varchar2(60 char), + tripId varchar2(60 char), + variance double precision, + vehicleId varchar2(60 char), + primary key (id) + ); + + create table HoldingTimes ( + id number(19,0) not null, + arrivalPredictionUsed number(1,0), + arrivalTime timestamp, + arrivalUsed number(1,0), + configRev number(10,0), + creationTime timestamp, + hasD1 number(1,0), + holdingTime timestamp, + numberPredictionsUsed number(10,0), + routeId varchar2(60 char), + stopId varchar2(60 char), + tripId varchar2(60 char), + vehicleId varchar2(60 char), + primary key (id) + ); + create table Matches ( vehicleId varchar2(60 char) not null, avlTime timestamp not null, @@ -204,12 +256,35 @@ primary key (id) ); + create table PredictionEvents ( + vehicleId varchar2(60 char) not null, + time timestamp not null, + eventType varchar2(60 char) not null, + arrivalTime timestamp, + arrivalstopid varchar2(60 char), + avlTime timestamp, + blockId varchar2(60 char), + departureTime timestamp, + departurestopid varchar2(60 char), + description varchar2(500 char), + lat double precision, + lon double precision, + referenceVehicleId varchar2(60 char), + routeId varchar2(60 char), + routeShortName varchar2(60 char), + serviceId varchar2(60 char), + stopId varchar2(60 char), + tripId varchar2(60 char), + primary key (vehicleId, time, eventType) + ); + create table Predictions ( id number(19,0) not null, affectedByWaitStop number(1,0), avlTime timestamp, configRev number(10,0), creationTime timestamp, + gtfsStopSeq number(10,0), isArrival number(1,0), predictionTime timestamp, routeId varchar2(60 char), @@ -224,22 +299,45 @@ id varchar2(60 char) not null, configRev number(10,0) not null, color varchar2(10 char), - description varchar2(255 char), + description varchar2(1024 char), maxLat double precision, maxLon double precision, minLat double precision, minLon double precision, hidden number(1,0), - longName varchar2(80 char), + longName varchar2(255 char), maxDistance double precision, - name varchar2(80 char), + name varchar2(255 char), routeOrder number(10,0), - shortName varchar2(80 char), + shortName varchar2(255 char), textColor varchar2(10 char), type varchar2(2 char), primary key (id, configRev) ); + create table StopPathPredictions ( + id number(19,0) not null, + algorithm varchar2(255 char), + creationTime timestamp, + predictionTime double precision, + startTime number(10,0), + stopPathIndex number(10,0), + travelTime number(1,0), + tripId varchar2(60 char), + vehicleId varchar2(255 char), + primary key (id) + ); + + create table StopPath_locations ( + StopPath_tripPatternId varchar2(120 char) not null, + StopPath_stopPathId varchar2(120 char) not null, + StopPath_configRev number(10,0) not null, + lat double precision, + lon double precision, + locations_ORDER number(10,0) not null, + primary key (StopPath_tripPatternId, StopPath_stopPathId, StopPath_configRev, locations_ORDER) + ); + create table StopPaths ( tripPatternId varchar2(120 char) not null, stopPathId varchar2(120 char) not null, @@ -248,7 +346,8 @@ gtfsStopSeq number(10,0), lastStopInTrip number(1,0), layoverStop number(1,0), - locations raw(1000), + maxDistance double precision, + maxSpeed double precision, pathLength double precision, routeId varchar2(60 char), scheduleAdherenceStop number(1,0), @@ -271,6 +370,53 @@ primary key (id, configRev) ); + create table TrafficPath_locations ( + TrafficPath_trafficRev number(10,0) not null, + TrafficPath_trafficPathId varchar2(120 char) not null, + lat double precision, + lon double precision, + locations_ORDER number(10,0) not null, + primary key (TrafficPath_trafficRev, TrafficPath_trafficPathId, locations_ORDER) + ); + + create table TrafficPath_to_StopPath_joinTable ( + TrafficPaths_trafficRev number(10,0) not null, + TrafficPaths_trafficPathId varchar2(120 char) not null, + stopPaths_tripPatternId varchar2(120 char) not null, + stopPaths_stopPathId varchar2(120 char) not null, + stopPaths_configRev number(10,0) not null, + listIndex number(10,0) not null, + primary key (TrafficPaths_trafficRev, TrafficPaths_trafficPathId, listIndex) + ); + + create table TrafficPaths ( + trafficRev number(10,0) not null, + trafficPathId varchar2(120 char) not null, + pathLength float, + primary key (trafficRev, trafficPathId) + ); + + create table TrafficSensor ( + trafficRev number(10,0) not null, + id varchar2(60 char) not null, + description varchar2(255 char), + externalId varchar2(60 char), + trafficPathId varchar2(120 char), + primary key (trafficRev, id) + ); + + create table TrafficSensorData ( + trafficSensorId varchar2(255 char) not null, + trafficRev number(10,0) not null, + time timestamp not null, + confidence double precision, + delayMillis double precision, + length double precision, + speed double precision, + travelTimeMillis number(10,0), + primary key (trafficSensorId, trafficRev, time) + ); + create table Transfers ( toStopId varchar2(60 char) not null, fromStopId varchar2(60 char) not null, @@ -288,7 +434,7 @@ stopPathId varchar2(120 char), stopTimeMsec number(10,0), travelTimeSegmentLength float, - travelTimesMsec raw(2000), + travelTimesMsec long raw, travelTimesRev number(10,0), primary key (id) ); @@ -334,6 +480,16 @@ primary key (id, configRev) ); + create table Trip_scheduledTimesList ( + Trip_tripId varchar2(60 char) not null, + Trip_startTime number(10,0) not null, + Trip_configRev number(10,0) not null, + arrivalTime number(10,0), + departureTime number(10,0), + scheduledTimesList_ORDER number(10,0) not null, + primary key (Trip_tripId, Trip_startTime, Trip_configRev, scheduledTimesList_ORDER) + ); + create table Trips ( tripId varchar2(60 char) not null, startTime number(10,0) not null, @@ -346,7 +502,6 @@ noSchedule number(1,0), routeId varchar2(60 char), routeShortName varchar2(60 char), - scheduledTimesList long raw, serviceId varchar2(60 char), shapeId varchar2(60 char), tripShortName varchar2(60 char), @@ -410,8 +565,14 @@ create index ArrivalsDeparturesRouteTimeIndex on ArrivalsDepartures (routeShortName, time); + create index ArrivalsDeparturesTripPatternIdIndex on ArrivalsDepartures (tripPatternId); + create index AvlReportsTimeIndex on AvlReports (time); + create index HeadwayIndex on Headway (creationTime); + + create index HoldingTimeIndex on HoldingTimes (creationTime); + create index AvlTimeIndex on Matches (avlTime); create index MeasuredArrivalTimesIndex on MeasuredArrivalTimes (time); @@ -420,8 +581,15 @@ create index PredictionAccuracyTimeIndex on PredictionAccuracy (arrivalDepartureTime); + create index PredictionEventsTimeIndex on PredictionEvents (time); + create index PredictionTimeIndex on Predictions (creationTime); + create index StopPathPredictionTimeIndex on StopPathPredictions (tripId, stopPathIndex); + + alter table TrafficPath_to_StopPath_joinTable + add constraint UK_ohqplmhw0t46tipi7i9bxuur8 unique (stopPaths_tripPatternId, stopPaths_stopPathId, stopPaths_configRev); + create index TravelTimesRevIndex on TravelTimesForTrips (travelTimesRev); alter table TripPattern_to_Path_joinTable @@ -431,16 +599,46 @@ create index VehicleStateAvlTimeIndex on VehicleStates (avlTime); + alter table ArrivalsDepartures + add constraint FK_m1eyesv8rr42fo6qpcrkcgjp3 + foreign key (stopId, configRev) + references Stops; + + alter table ArrivalsDepartures + add constraint FK_axgfl7fxphggp7qcwy6h8vbs4 + foreign key (tripPatternId, stopPathId, configRev) + references StopPaths; + alter table Block_to_Trip_joinTable add constraint FK_abaj8ke6oh4imbbgnaercsowo foreign key (trips_tripId, trips_startTime, trips_configRev) references Trips; alter table Block_to_Trip_joinTable - add constraint FK_1c1e1twdap19vq0xkav0amvm - foreign key (Blocks_serviceId, Blocks_configRev, Blocks_blockId) + add constraint FK_kobr9qxbawdjnf5fced46rfpo + foreign key (blocks_serviceId, blocks_configRev, blocks_blockId) references Blocks; + alter table StopPath_locations + add constraint FK_sdjt3vtd3w0cl07p0doob6khi + foreign key (StopPath_tripPatternId, StopPath_stopPathId, StopPath_configRev) + references StopPaths; + + alter table TrafficPath_locations + add constraint FK_j3otbyk8qsh9rg02q8kk8931q + foreign key (TrafficPath_trafficRev, TrafficPath_trafficPathId) + references TrafficPaths; + + alter table TrafficPath_to_StopPath_joinTable + add constraint FK_ohqplmhw0t46tipi7i9bxuur8 + foreign key (stopPaths_tripPatternId, stopPaths_stopPathId, stopPaths_configRev) + references StopPaths; + + alter table TrafficPath_to_StopPath_joinTable + add constraint FK_6aib4u1tr2wfpxoog3a5ycou9 + foreign key (TrafficPaths_trafficRev, TrafficPaths_trafficPathId) + references TrafficPaths; + alter table TravelTimesForTrip_to_TravelTimesForPath_joinTable add constraint FK_hh5uepurijcqj0pyc6e3h5mqw foreign key (travelTimesForStopPaths_id) @@ -461,6 +659,11 @@ foreign key (TripPatterns_id, TripPatterns_configRev) references TripPatterns; + alter table Trip_scheduledTimesList + add constraint FK_n5et0p70cwe1dwo4m6lq0k4h0 + foreign key (Trip_tripId, Trip_startTime, Trip_configRev) + references Trips; + alter table Trips add constraint FK_p1er53449kkfsca6mbnxkdyst foreign key (travelTimes_id) diff --git a/transitime/src/main/resources/ddl_oracle_org_transitime_db_webstructs.sql b/transitclock/src/main/resources/ddl_oracle_org_transitclock_db_webstructs.sql similarity index 100% rename from transitime/src/main/resources/ddl_oracle_org_transitime_db_webstructs.sql rename to transitclock/src/main/resources/ddl_oracle_org_transitclock_db_webstructs.sql diff --git a/transitime/src/main/resources/ddl_postgres_org_transitime_db_structs.sql b/transitclock/src/main/resources/ddl_postgres_org_transitclock_db_structs.sql similarity index 65% rename from transitime/src/main/resources/ddl_postgres_org_transitime_db_structs.sql rename to transitclock/src/main/resources/ddl_postgres_org_transitclock_db_structs.sql index b064843a8..6df34bf25 100644 --- a/transitime/src/main/resources/ddl_postgres_org_transitime_db_structs.sql +++ b/transitclock/src/main/resources/ddl_postgres_org_transitclock_db_structs.sql @@ -2,6 +2,7 @@ create table ActiveRevisions ( id int4 not null, configRev int4, + trafficRev int4, travelTimesRev int4, primary key (id) ); @@ -34,14 +35,18 @@ blockId varchar(60), configRev int4, directionId varchar(60), + dwellTime int8, + freqStartTime timestamp, routeId varchar(60), routeShortName varchar(60), scheduledTime timestamp, serviceId varchar(60), stopOrder int4, + stopPathId varchar(120), stopPathIndex int4, stopPathLength float4, tripIndex int4, + tripPatternId varchar(120), primary key (vehicleId, tripId, time, stopId, isArrival, gtfsStopSeq) ); @@ -66,14 +71,14 @@ ); create table Block_to_Trip_joinTable ( - Blocks_serviceId varchar(60) not null, - Blocks_configRev int4 not null, - Blocks_blockId varchar(60) not null, + blocks_serviceId varchar(60) not null, + blocks_configRev int4 not null, + blocks_blockId varchar(60) not null, trips_tripId varchar(60) not null, trips_startTime int4 not null, trips_configRev int4 not null, listIndex int4 not null, - primary key (Blocks_serviceId, Blocks_configRev, Blocks_blockId, listIndex) + primary key (blocks_serviceId, blocks_configRev, blocks_blockId, listIndex) ); create table Blocks ( @@ -138,9 +143,20 @@ originId varchar(60) not null, fareId varchar(60) not null, destinationId varchar(60) not null, + containsId varchar(60) not null, + configRev int4 not null, + primary key (routeId, originId, fareId, destinationId, containsId, configRev) + ); + + create table FeedInfo ( + feedPublisherName varchar(255) not null, configRev int4 not null, - containsId varchar(60), - primary key (routeId, originId, fareId, destinationId, configRev) + feedEndDate date, + feedLanguage varchar(15), + feedPublisherUrl varchar(512), + feedStartDate date, + feedVersion varchar(120), + primary key (feedPublisherName, configRev) ); create table Frequencies ( @@ -153,6 +169,42 @@ primary key (tripId, startTime, configRev) ); + create table Headway ( + id int8 not null, + average float8, + coefficientOfVariation float8, + configRev int4, + creationTime timestamp, + firstDeparture timestamp, + headway float8, + numVehicles int4, + otherVehicleId varchar(60), + routeId varchar(60), + secondDeparture timestamp, + stopId varchar(60), + tripId varchar(60), + variance float8, + vehicleId varchar(60), + primary key (id) + ); + + create table HoldingTimes ( + id int8 not null, + arrivalPredictionUsed boolean, + arrivalTime timestamp, + arrivalUsed boolean, + configRev int4, + creationTime timestamp, + hasD1 boolean, + holdingTime timestamp, + numberPredictionsUsed int4, + routeId varchar(60), + stopId varchar(60), + tripId varchar(60), + vehicleId varchar(60), + primary key (id) + ); + create table Matches ( vehicleId varchar(60) not null, avlTime timestamp not null, @@ -204,12 +256,35 @@ primary key (id) ); + create table PredictionEvents ( + vehicleId varchar(60) not null, + time timestamp not null, + eventType varchar(60) not null, + arrivalTime timestamp, + arrivalstopid varchar(60), + avlTime timestamp, + blockId varchar(60), + departureTime timestamp, + departurestopid varchar(60), + description varchar(500), + lat float8, + lon float8, + referenceVehicleId varchar(60), + routeId varchar(60), + routeShortName varchar(60), + serviceId varchar(60), + stopId varchar(60), + tripId varchar(60), + primary key (vehicleId, time, eventType) + ); + create table Predictions ( id int8 not null, affectedByWaitStop boolean, avlTime timestamp, configRev int4, creationTime timestamp, + gtfsStopSeq int4, isArrival boolean, predictionTime timestamp, routeId varchar(60), @@ -224,22 +299,45 @@ id varchar(60) not null, configRev int4 not null, color varchar(10), - description varchar(255), + description varchar(1024), maxLat float8, maxLon float8, minLat float8, minLon float8, hidden boolean, - longName varchar(80), + longName varchar(255), maxDistance float8, - name varchar(80), + name varchar(255), routeOrder int4, - shortName varchar(80), + shortName varchar(255), textColor varchar(10), type varchar(2), primary key (id, configRev) ); + create table StopPathPredictions ( + id int8 not null, + algorithm varchar(255), + creationTime timestamp, + predictionTime float8, + startTime int4, + stopPathIndex int4, + travelTime boolean, + tripId varchar(60), + vehicleId varchar(255), + primary key (id) + ); + + create table StopPath_locations ( + StopPath_tripPatternId varchar(120) not null, + StopPath_stopPathId varchar(120) not null, + StopPath_configRev int4 not null, + lat float8, + lon float8, + locations_ORDER int4 not null, + primary key (StopPath_tripPatternId, StopPath_stopPathId, StopPath_configRev, locations_ORDER) + ); + create table StopPaths ( tripPatternId varchar(120) not null, stopPathId varchar(120) not null, @@ -248,7 +346,8 @@ gtfsStopSeq int4, lastStopInTrip boolean, layoverStop boolean, - locations bytea, + maxDistance float8, + maxSpeed float8, pathLength float8, routeId varchar(60), scheduleAdherenceStop boolean, @@ -271,6 +370,53 @@ primary key (id, configRev) ); + create table TrafficPath_locations ( + TrafficPath_trafficRev int4 not null, + TrafficPath_trafficPathId varchar(120) not null, + lat float8, + lon float8, + locations_ORDER int4 not null, + primary key (TrafficPath_trafficRev, TrafficPath_trafficPathId, locations_ORDER) + ); + + create table TrafficPath_to_StopPath_joinTable ( + TrafficPaths_trafficRev int4 not null, + TrafficPaths_trafficPathId varchar(120) not null, + stopPaths_tripPatternId varchar(120) not null, + stopPaths_stopPathId varchar(120) not null, + stopPaths_configRev int4 not null, + listIndex int4 not null, + primary key (TrafficPaths_trafficRev, TrafficPaths_trafficPathId, listIndex) + ); + + create table TrafficPaths ( + trafficRev int4 not null, + trafficPathId varchar(120) not null, + pathLength float4, + primary key (trafficRev, trafficPathId) + ); + + create table TrafficSensor ( + trafficRev int4 not null, + id varchar(60) not null, + description varchar(255), + externalId varchar(60), + trafficPathId varchar(120), + primary key (trafficRev, id) + ); + + create table TrafficSensorData ( + trafficSensorId varchar(255) not null, + trafficRev int4 not null, + time timestamp not null, + confidence float8, + delayMillis float8, + length float8, + speed float8, + travelTimeMillis int4, + primary key (trafficSensorId, trafficRev, time) + ); + create table Transfers ( toStopId varchar(60) not null, fromStopId varchar(60) not null, @@ -334,6 +480,16 @@ primary key (id, configRev) ); + create table Trip_scheduledTimesList ( + Trip_tripId varchar(60) not null, + Trip_startTime int4 not null, + Trip_configRev int4 not null, + arrivalTime int4, + departureTime int4, + scheduledTimesList_ORDER int4 not null, + primary key (Trip_tripId, Trip_startTime, Trip_configRev, scheduledTimesList_ORDER) + ); + create table Trips ( tripId varchar(60) not null, startTime int4 not null, @@ -346,7 +502,6 @@ noSchedule boolean, routeId varchar(60), routeShortName varchar(60), - scheduledTimesList bytea, serviceId varchar(60), shapeId varchar(60), tripShortName varchar(60), @@ -410,8 +565,14 @@ create index ArrivalsDeparturesRouteTimeIndex on ArrivalsDepartures (routeShortName, time); + create index ArrivalsDeparturesTripPatternIdIndex on ArrivalsDepartures (tripPatternId); + create index AvlReportsTimeIndex on AvlReports (time); + create index HeadwayIndex on Headway (creationTime); + + create index HoldingTimeIndex on HoldingTimes (creationTime); + create index AvlTimeIndex on Matches (avlTime); create index MeasuredArrivalTimesIndex on MeasuredArrivalTimes (time); @@ -420,8 +581,15 @@ create index PredictionAccuracyTimeIndex on PredictionAccuracy (arrivalDepartureTime); + create index PredictionEventsTimeIndex on PredictionEvents (time); + create index PredictionTimeIndex on Predictions (creationTime); + create index StopPathPredictionTimeIndex on StopPathPredictions (tripId, stopPathIndex); + + alter table TrafficPath_to_StopPath_joinTable + add constraint UK_ohqplmhw0t46tipi7i9bxuur8 unique (stopPaths_tripPatternId, stopPaths_stopPathId, stopPaths_configRev); + create index TravelTimesRevIndex on TravelTimesForTrips (travelTimesRev); alter table TripPattern_to_Path_joinTable @@ -431,16 +599,46 @@ create index VehicleStateAvlTimeIndex on VehicleStates (avlTime); + alter table ArrivalsDepartures + add constraint FK_m1eyesv8rr42fo6qpcrkcgjp3 + foreign key (stopId, configRev) + references Stops; + + alter table ArrivalsDepartures + add constraint FK_axgfl7fxphggp7qcwy6h8vbs4 + foreign key (tripPatternId, stopPathId, configRev) + references StopPaths; + alter table Block_to_Trip_joinTable add constraint FK_abaj8ke6oh4imbbgnaercsowo foreign key (trips_tripId, trips_startTime, trips_configRev) references Trips; alter table Block_to_Trip_joinTable - add constraint FK_1c1e1twdap19vq0xkav0amvm - foreign key (Blocks_serviceId, Blocks_configRev, Blocks_blockId) + add constraint FK_kobr9qxbawdjnf5fced46rfpo + foreign key (blocks_serviceId, blocks_configRev, blocks_blockId) references Blocks; + alter table StopPath_locations + add constraint FK_sdjt3vtd3w0cl07p0doob6khi + foreign key (StopPath_tripPatternId, StopPath_stopPathId, StopPath_configRev) + references StopPaths; + + alter table TrafficPath_locations + add constraint FK_j3otbyk8qsh9rg02q8kk8931q + foreign key (TrafficPath_trafficRev, TrafficPath_trafficPathId) + references TrafficPaths; + + alter table TrafficPath_to_StopPath_joinTable + add constraint FK_ohqplmhw0t46tipi7i9bxuur8 + foreign key (stopPaths_tripPatternId, stopPaths_stopPathId, stopPaths_configRev) + references StopPaths; + + alter table TrafficPath_to_StopPath_joinTable + add constraint FK_6aib4u1tr2wfpxoog3a5ycou9 + foreign key (TrafficPaths_trafficRev, TrafficPaths_trafficPathId) + references TrafficPaths; + alter table TravelTimesForTrip_to_TravelTimesForPath_joinTable add constraint FK_hh5uepurijcqj0pyc6e3h5mqw foreign key (travelTimesForStopPaths_id) @@ -461,6 +659,11 @@ foreign key (TripPatterns_id, TripPatterns_configRev) references TripPatterns; + alter table Trip_scheduledTimesList + add constraint FK_n5et0p70cwe1dwo4m6lq0k4h0 + foreign key (Trip_tripId, Trip_startTime, Trip_configRev) + references Trips; + alter table Trips add constraint FK_p1er53449kkfsca6mbnxkdyst foreign key (travelTimes_id) diff --git a/transitime/src/main/resources/ddl_postgres_org_transitime_db_webstructs.sql b/transitclock/src/main/resources/ddl_postgres_org_transitclock_db_webstructs.sql similarity index 100% rename from transitime/src/main/resources/ddl_postgres_org_transitime_db_webstructs.sql rename to transitclock/src/main/resources/ddl_postgres_org_transitclock_db_webstructs.sql diff --git a/transitclock/src/main/resources/ehcache-large.xml b/transitclock/src/main/resources/ehcache-large.xml new file mode 100644 index 000000000..7bbe47174 --- /dev/null +++ b/transitclock/src/main/resources/ehcache-large.xml @@ -0,0 +1,98 @@ + + + + + + + + org.transitclock.core.dataCache.StopPathCacheKey + org.transitclock.core.predictiongenerator.scheduled.dwell.DwellModel + + 1 + + + 1 + + + + + + org.transitclock.core.dataCache.TripKey + org.transitclock.core.dataCache.TripEvents + + 21 + + + 31500 + 30 + 40 + + + + + + org.transitclock.core.dataCache.StopArrivalDepartureCacheKey + org.transitclock.core.dataCache.StopEvents + + 1 + + + 150000 + 30 + 40 + + + + + + org.transitclock.core.dataCache.KalmanErrorCacheKey + java.lang.Double + + 7 + + + 1000000 + 5 + 10 + + + + org.transitclock.core.dataCache.StopPathCacheKey + org.transitclock.core.dataCache.HistoricalAverage + + 14 + + + 1 + + + + + + java.lang.Long + java.lang.String + + 1 + + + 1 + + + + + + org.transitclock.core.dataCache.HoldingTimeCacheKey + org.transitclock.db.structs.HoldingTime + + 1 + + + 1 + + + \ No newline at end of file diff --git a/transitclock/src/main/resources/ehcache-small.xml b/transitclock/src/main/resources/ehcache-small.xml new file mode 100644 index 000000000..56a1abbb7 --- /dev/null +++ b/transitclock/src/main/resources/ehcache-small.xml @@ -0,0 +1,103 @@ + + + + + + + + + org.transitclock.core.dataCache.StopPathCacheKey + org.transitclock.core.predictiongenerator.scheduled.dwell.DwellModel + + 7 + + + 2000 + 100 + 200 + + + + + + org.transitclock.core.dataCache.TripKey + org.transitclock.core.dataCache.TripEvents + + 7 + + + 1000 + 100 + 200 + + + + + + org.transitclock.core.dataCache.StopArrivalDepartureCacheKey + org.transitclock.core.dataCache.StopEvents + + 1 + + + 2000 + 100 + 200 + + + + + + org.transitclock.core.dataCache.KalmanErrorCacheKey + java.lang.Double + + 24 + + + 10000 + 5 + + + + + org.transitclock.core.dataCache.StopPathCacheKey + org.transitclock.core.dataCache.HistoricalAverage + + 14 + + + 10000 + 100 + + + + + + java.lang.Long + java.lang.String + + 1 + + + 100 + 1 + + + + + org.transitclock.core.dataCache.HoldingTimeCacheKey + org.transitclock.db.structs.HoldingTime + + 2 + + + 100 + 1 + + + \ No newline at end of file diff --git a/transitclock/src/main/resources/ehcache.xml b/transitclock/src/main/resources/ehcache.xml new file mode 100644 index 000000000..b9375c10c --- /dev/null +++ b/transitclock/src/main/resources/ehcache.xml @@ -0,0 +1,103 @@ + + + + + + + + + org.transitclock.core.dataCache.StopPathCacheKey + org.transitclock.core.predictiongenerator.scheduled.dwell.DwellModel + + 7 + + + 2000 + 100 + 200 + + + + + + org.transitclock.core.dataCache.TripKey + org.transitclock.core.dataCache.TripEvents + + 7 + + + 1000 + 100 + 200 + + + + + + org.transitclock.core.dataCache.StopArrivalDepartureCacheKey + org.transitclock.core.dataCache.StopEvents + + 1 + + + 2000 + 100 + 200 + + + + + + org.transitclock.core.dataCache.KalmanErrorCacheKey + org.transitclock.core.dataCache.KalmanError + + 24 + + + 10000 + 5 + + + + + org.transitclock.core.dataCache.StopPathCacheKey + org.transitclock.core.dataCache.HistoricalAverage + + 14 + + + 10000 + 100 + + + + + + java.lang.Long + java.lang.String + + 1 + + + 100 + 1 + + + + + org.transitclock.core.dataCache.HoldingTimeCacheKey + org.transitclock.db.structs.HoldingTime + + 2 + + + 100 + 1 + + + \ No newline at end of file diff --git a/transitclock/src/main/resources/hsql_hibernate.cfg.xml b/transitclock/src/main/resources/hsql_hibernate.cfg.xml new file mode 100644 index 000000000..ef9d78df2 --- /dev/null +++ b/transitclock/src/main/resources/hsql_hibernate.cfg.xml @@ -0,0 +1,85 @@ + + + + + + + + org.hibernate.dialect.HSQLDialect + + + org.hsqldb.jdbc.JDBCDriver + + + + true + true + true + + + + + + + 1 + 100 + true + true + true + update + + + + + org.hsqldb.jdbcDriver + jdbc:hsqldb:hsql://localhost/test + sa + + + + + + + + + diff --git a/transitclock/src/main/resources/logback.xml b/transitclock/src/main/resources/logback.xml new file mode 100755 index 000000000..bf3d67d10 --- /dev/null +++ b/transitclock/src/main/resources/logback.xml @@ -0,0 +1,588 @@ + + + + + + + + + + + + + + + + + + + + + + + + ${LOG_FILE_ROOT}/core.log.gz + + + + + DEBUG + DENY + ACCEPT + + + + + UTF-8 + ${DEFAULT_PATTERN} + + + + + + + + + + ${LOG_FILE_ROOT}/core.log.gz + + + + + DEBUG + ACCEPT + DENY + + + + + UTF-8 + ${DEBUG_PATTERN} + + + + + + + + + ${logback.errorEmail} + + + + EMAIL + + + + + INFO + + + + + 1 + + + + false + + + ${logback.smtpHost} + 465 + true + ${logback.smtpUsername} + ${logback.smtpPassword} + errorLogger@transitclock.org + %level %logger{0} - %m + + %d{MMM dd yyyy - HH:mm:ss.SSS} - %-5level - %logger{35} + :%n%message%n + + + + + + + ${LOG_FILE_ROOT}/configParams.log.gz + + + + UTF-8 + ${DATA_PATTERN} + + + + + + + ${LOG_FILE_ROOT}/avl.log.gz + + + UTF-8 + ${DEFAULT_PATTERN} + + + + + + ${LOG_FILE_ROOT}/matcher.log.gz + + + UTF-8 + ${DEFAULT_PATTERN} + + + + + + + ${LOG_FILE_ROOT}/frequency.log.gz + + + + UTF-8 + ${DATA_PATTERN} + + + + + + + ${LOG_FILE_ROOT}/arrivalDeparture.log.gz + + + + UTF-8 + ${DATA_PATTERN} + + + + + + + ${LOG_FILE_ROOT}/vehicleEvents.log.gz + + + + UTF-8 + ${DATA_PATTERN} + + + + + + + ${LOG_FILE_ROOT}/cache.log.gz + + + UTF-8 + ${DATA_PATTERN} + + + + + + ${LOG_FILE_ROOT}/holding.log.gz + + + UTF-8 + ${DATA_PATTERN} + + + + + + ${LOG_FILE_ROOT}/prediction.log.gz + + + UTF-8 + ${DATA_PATTERN} + + + + + + + ${LOG_FILE_ROOT}/schedBasedPreds.log.gz + + + + UTF-8 + ${DATA_PATTERN} + + + + + + + ${LOG_FILE_ROOT}/predAccuracy.log.gz + + + + UTF-8 + ${DEFAULT_PATTERN} + + + + + + + ${LOG_FILE_ROOT}/externalPredAccuracy.log.gz + + + + UTF-8 + ${DEFAULT_PATTERN} + + + + + + + ${LOG_FILE_ROOT}/match.log.gz + + + UTF-8 + ${DATA_PATTERN} + + + + + + + ${LOG_FILE_ROOT}/monitoring.log.gz + + + UTF-8 + ${DEFAULT_PATTERN} + + + + + + + ${LOG_FILE_ROOT}/autoAssigner.log.gz + + + + UTF-8 + ${DEFAULT_PATTERN} + + + + + + + ${LOG_FILE_ROOT}/sql.log.gz + + + UTF-8 + ${DEFAULT_PATTERN} + + + + + + + UTF-8 + ${DATA_PATTERN} + + + + + + + ${LOG_FILE_ROOT}/mapmatch.log.gz + + + UTF-8 + ${DATA_PATTERN} + + + + + + + ${LOG_FILE_ROOT}/update_travel_times.log.gz + + + UTF-8 + ${DEFAULT_PATTERN} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/transitime/src/main/resources/logbackDebugMatching.xml b/transitclock/src/main/resources/logbackDebugMatching.xml similarity index 93% rename from transitime/src/main/resources/logbackDebugMatching.xml rename to transitclock/src/main/resources/logbackDebugMatching.xml index ef37768eb..0a8c7f9d6 100644 --- a/transitime/src/main/resources/logbackDebugMatching.xml +++ b/transitclock/src/main/resources/logbackDebugMatching.xml @@ -11,14 +11,14 @@ + value="${transitclock.logging.dir:-/Logs}/${transitclock.core.agencyId:-DEFAULT}/core/%d{yyyy/MM/dd}" /> - - - - - - @@ -335,25 +335,25 @@ config params logged in separate file but also want some errors to send out an e-mail. Need to use ERROR_SMTP and additivity=true to make sure the e-mail is sent out. But unfortunately when I did - this the regular info logging for org.transitime.config stopped + this the regular info logging for org.transitclock.config stopped working. So not sending out e-mail error messages for now. --> - - - @@ -386,7 +386,7 @@ - - - - + value="${transitclock.logging.dir:-/Logs}/${transitclock.core.agencyId:-DEFAULT}/core/%d{yyyy/MM/dd}" /> - - - - - - @@ -323,25 +323,25 @@ config params logged in separate file but also want some errors to send out an e-mail. Need to use ERROR_SMTP and additivity=true to make sure the e-mail is sent out. But unfortunately when I did - this the regular info logging for org.transitime.config stopped + this the regular info logging for org.transitclock.config stopped working. So not sending out e-mail error messages for now. --> - - - @@ -374,7 +374,7 @@ @@ -42,7 +42,7 @@ - + @@ -53,27 +53,27 @@ level is specified for each class. --> - - - - - diff --git a/transitime/src/main/resources/logbackGtfsWithSql.xml b/transitclock/src/main/resources/logbackGtfsWithSql.xml similarity index 90% rename from transitime/src/main/resources/logbackGtfsWithSql.xml rename to transitclock/src/main/resources/logbackGtfsWithSql.xml index 08a4eb439..f224a6102 100644 --- a/transitime/src/main/resources/logbackGtfsWithSql.xml +++ b/transitclock/src/main/resources/logbackGtfsWithSql.xml @@ -13,7 +13,7 @@ + value="${transitclock.logging.dir:-/Logs}/${transitclock.core.agencyId:-DEFAULT}/gtfsConfig/" /> @@ -55,7 +55,7 @@ - + @@ -66,27 +66,27 @@ level is specified for each class. --> - - - - - diff --git a/transitime/src/main/resources/logbackPlayback.xml b/transitclock/src/main/resources/logbackPlayback.xml similarity index 91% rename from transitime/src/main/resources/logbackPlayback.xml rename to transitclock/src/main/resources/logbackPlayback.xml index 88c57db5e..4127a8e4e 100644 --- a/transitime/src/main/resources/logbackPlayback.xml +++ b/transitclock/src/main/resources/logbackPlayback.xml @@ -11,7 +11,7 @@ - - - - - - @@ -237,7 +237,7 @@ --> - + @@ -246,39 +246,39 @@ though CORE and CORE_DEBUG have filters to only show proper info. --> - - - - - - diff --git a/transitime/src/main/resources/logbackUpdateGtfsSchedule.xml b/transitclock/src/main/resources/logbackUpdateGtfsSchedule.xml similarity index 92% rename from transitime/src/main/resources/logbackUpdateGtfsSchedule.xml rename to transitclock/src/main/resources/logbackUpdateGtfsSchedule.xml index 7aafcd234..179f57c60 100644 --- a/transitime/src/main/resources/logbackUpdateGtfsSchedule.xml +++ b/transitclock/src/main/resources/logbackUpdateGtfsSchedule.xml @@ -7,7 +7,7 @@ - + - diff --git a/transitime/src/main/resources/logbackUpdateTravelTimes.xml b/transitclock/src/main/resources/logbackUpdateTravelTimes.xml similarity index 91% rename from transitime/src/main/resources/logbackUpdateTravelTimes.xml rename to transitclock/src/main/resources/logbackUpdateTravelTimes.xml index bfe148639..0b7d859b0 100644 --- a/transitime/src/main/resources/logbackUpdateTravelTimes.xml +++ b/transitclock/src/main/resources/logbackUpdateTravelTimes.xml @@ -7,7 +7,7 @@ - @@ -91,22 +91,22 @@ - - - - diff --git a/transitime/src/main/resources/logbackWithPredictions.xml b/transitclock/src/main/resources/logbackWithPredictions.xml similarity index 93% rename from transitime/src/main/resources/logbackWithPredictions.xml rename to transitclock/src/main/resources/logbackWithPredictions.xml index 0484299c3..053baede0 100644 --- a/transitime/src/main/resources/logbackWithPredictions.xml +++ b/transitclock/src/main/resources/logbackWithPredictions.xml @@ -11,14 +11,14 @@ + value="${transitclock.logging.dir:-/Logs}/${transitclock.core.agencyId:-DEFAULT}/core/%d{yyyy/MM/dd}" /> - - - - - - @@ -323,25 +323,25 @@ config params logged in separate file but also want some errors to send out an e-mail. Need to use ERROR_SMTP and additivity=true to make sure the e-mail is sent out. But unfortunately when I did - this the regular info logging for org.transitime.config stopped + this the regular info logging for org.transitclock.config stopped working. So not sending out e-mail error messages for now. --> - - - @@ -374,7 +374,7 @@ @@ -21,7 +25,7 @@ be nice, but again, should probably be disabled in the future. --> - true + false true 2 - 20 + 50 - 300 + 600 - 50 + 5000 - - 25 + + 1000 + 100 + true + true + + - + diff --git a/transitimeApi/src/main/resources/postgres_hibernate.cfg.xml b/transitclock/src/main/resources/postgres_hibernate.cfg.xml similarity index 97% rename from transitimeApi/src/main/resources/postgres_hibernate.cfg.xml rename to transitclock/src/main/resources/postgres_hibernate.cfg.xml index d50e7f382..60959a506 100644 --- a/transitimeApi/src/main/resources/postgres_hibernate.cfg.xml +++ b/transitclock/src/main/resources/postgres_hibernate.cfg.xml @@ -78,7 +78,7 @@ the classes programatically via AnnoatedClassesList so that when the classes change don't need to modify all of the hibernate config files. --> - + diff --git a/transitime/src/main/resources/scripts/dbArchive.sh b/transitclock/src/main/resources/scripts/dbArchive.sh similarity index 97% rename from transitime/src/main/resources/scripts/dbArchive.sh rename to transitclock/src/main/resources/scripts/dbArchive.sh index d21190c0b..1d6ab320b 100644 --- a/transitime/src/main/resources/scripts/dbArchive.sh +++ b/transitclock/src/main/resources/scripts/dbArchive.sh @@ -135,7 +135,7 @@ log "Copying data file to Amazon AWS Glacier" archiveDescription=$cutoffDate vaultName="$agencyId-dbData" region="us-west-2" -java -Dlogback.configurationFile=/home/ec2-user/config/logbackStdout.xml -cp "/home/ec2-user/jars/*" org.transitime.maintenance.AwsGlacierArchiver $dataTarFile $archiveDescription $region $vaultName $logDir +java -Dlogback.configurationFile=/home/ec2-user/config/logbackStdout.xml -cp "/home/ec2-user/jars/*" org.transitclock.maintenance.AwsGlacierArchiver $dataTarFile $archiveDescription $region $vaultName $logDir if [ $? != 0 ]; then log "Archiving tar file to AWS Glacier failed. Exiting!" exit -1 diff --git a/transitime/src/main/resources/scripts/tomcatLogfilesArchive.sh b/transitclock/src/main/resources/scripts/tomcatLogfilesArchive.sh similarity index 92% rename from transitime/src/main/resources/scripts/tomcatLogfilesArchive.sh rename to transitclock/src/main/resources/scripts/tomcatLogfilesArchive.sh index a084bc7cf..ff03ed41a 100644 --- a/transitime/src/main/resources/scripts/tomcatLogfilesArchive.sh +++ b/transitclock/src/main/resources/scripts/tomcatLogfilesArchive.sh @@ -33,7 +33,7 @@ function archive() { log "Copying file to Amazon AWS Glacier" archiveDescription="up to $cutoffDate" vaultName="tomcat-logs" - command="java -Dlogback.configurationFile=/home/ec2-user/config/logbackStdout.xml -cp \"/home/ec2-user/jars/*\" org.transitime.maintenance.AwsGlacierArchiver $zipFile \"$archiveDescription\" $region $vaultName $logDir" + command="java -Dlogback.configurationFile=/home/ec2-user/config/logbackStdout.xml -cp \"/home/ec2-user/jars/*\" org.transitclock.maintenance.AwsGlacierArchiver $zipFile \"$archiveDescription\" $region $vaultName $logDir" log "Executing: $command" eval "$command" if [ $? != 0 ]; then diff --git a/transitclock/src/main/resources/testConfig.xml b/transitclock/src/main/resources/testConfig.xml new file mode 100644 index 000000000..6f5c978d1 --- /dev/null +++ b/transitclock/src/main/resources/testConfig.xml @@ -0,0 +1,25 @@ + + + + org.transitclock.avl.GtfsRealtimeModule + + true + + 02 + + + + http://realtime.prod.obahart.org:8088/vehicle-positions + http://realtime.prod.obahart.org:8088/vehicle-positions + + + + hsql_hibernate.cfg.xml + + \ No newline at end of file diff --git a/transitime/src/main/resources/tomcat/config/README.txt b/transitclock/src/main/resources/tomcat/config/README.txt similarity index 100% rename from transitime/src/main/resources/tomcat/config/README.txt rename to transitclock/src/main/resources/tomcat/config/README.txt diff --git a/transitime/src/main/resources/tomcat/config/catalina.properties b/transitclock/src/main/resources/tomcat/config/catalina.properties similarity index 100% rename from transitime/src/main/resources/tomcat/config/catalina.properties rename to transitclock/src/main/resources/tomcat/config/catalina.properties diff --git a/transitime/src/main/resources/tomcat/config/server.xml b/transitclock/src/main/resources/tomcat/config/server.xml similarity index 100% rename from transitime/src/main/resources/tomcat/config/server.xml rename to transitclock/src/main/resources/tomcat/config/server.xml diff --git a/transitclock/src/main/resources/transiTimeconfig.xml b/transitclock/src/main/resources/transiTimeconfig.xml new file mode 100644 index 000000000..e20474d16 --- /dev/null +++ b/transitclock/src/main/resources/transiTimeconfig.xml @@ -0,0 +1,24 @@ + + + + org.transitclock.avl.GtfsRealtimeModule + + true + + 02 + + + http://realtime.prod.obahart.org:8088/vehicle-positions + http://realtime.prod.obahart.org:8088/vehicle-positions + + + + + + src/main/resources/hsql_hibernate.cfg.xml + + \ No newline at end of file diff --git a/transitclock/src/test/java/org/transitclock/applications/LoadTrafficSensorsTest.java b/transitclock/src/test/java/org/transitclock/applications/LoadTrafficSensorsTest.java new file mode 100644 index 000000000..3bcf40925 --- /dev/null +++ b/transitclock/src/test/java/org/transitclock/applications/LoadTrafficSensorsTest.java @@ -0,0 +1,816 @@ +/* + * 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.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.locationtech.jts.geom.Coordinate; +import org.transitclock.db.structs.Location; +import org.transitclock.db.structs.StopPath; +import org.transitclock.traffic.FeatureData; +import org.transitclock.traffic.FeatureGeometry; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.*; + +/** + * Test the loading/scoring of LoadTrafficSensors application. + */ +public class LoadTrafficSensorsTest { + + private LoadTrafficSensors app; + + @Before + public void setup() { + app = new LoadTrafficSensors() { + void debugShape(String shapeName, Coordinate[] shape) { + System.out.println(shapeName+":"); + StringBuffer sb = new StringBuffer(); + for (Coordinate c : shape) { + sb.append(c.x).append(",").append(c.y).append('\n'); + } + System.out.println(sb.toString()); + } + + }; + } + + @Test + public void gapSizeLargeGap() { + Coordinate[] featureLineString = parseShape(SENSOR_SHAPE_1); + Coordinate[] stopPathLineString = parseShape(STOP_PATH_SHAPE_1); + Coordinate[] matches = parseShape(MATCHES_1); + int gapSize = app.countGapsInShape(featureLineString, stopPathLineString, matches); + // 0, 0, 0, 1,...,2,7,8....,9,10 = gap of 5 (2-7) + assertEquals(5, gapSize); + } + + @Test + public void gabSizeSmallBackTracks() { + Coordinate[] featureLineString = parseShape(SENSOR_SHAPE_2); + Coordinate[] stopPathLineString = parseShape(STOP_PATH_SHAPE_2); + Coordinate[] matches = parseShape(MATCHES_2); + int gapSize = app.countGapsInShape(featureLineString, stopPathLineString, matches); + assertEquals(2, gapSize); + } + + @Test + // center line feature shape, more complex version of testBigFeatureSizeSmallStopSegment + // short stop segment (4 points, 2 on center line, 2 off center line at stop + // gapSize counts flapping / backmatches and penalizes this, make sure overall + // score is still above DEFAULT_MIN_SNAP_SCORE + public void TestFlappingScoreSizeMediumStopSegment() { + Coordinate[] featureLineString = parseShape(SENSOR_SHAPE_3); + Coordinate[] stopPathLineString = parseShape(STOP_PATH_SHAPE_3); + Coordinate[] matches = parseShape(MATCHES_3); + FeatureData fd = createFeatureData(" shape3", SENSOR_SHAPE_3); + List allStopPaths = new ArrayList<>(); + allStopPaths.add(createStopPath("shape3", STOP_PATH_SHAPE_3)); + double score = app.mapFeatureToStopPaths(fd, allStopPaths); + assertTrue(score > LoadTrafficSensors.DEFAULT_MIN_SNAP_SCORE); + + int gapSize = app.countGapsInShape(featureLineString, stopPathLineString, matches); + assertEquals(3, gapSize); + + } + + @Test + // a borderline bad match -- but by our algorithm its acceptable + public void testFeatureLargeStopSegment() { + Coordinate[] featureLineString = parseShape(SENSOR_SHAPE_4); + Coordinate[] stopPathLineString = parseShape(STOP_PATH_SHAPE_4); + Coordinate[] matches = parseShape(MATCHES_4); + FeatureData fd = createFeatureData(" shape4", SENSOR_SHAPE_4); + List allStopPaths = new ArrayList<>(); + allStopPaths.add(createStopPath("shape3", STOP_PATH_SHAPE_4)); + double score = app.mapFeatureToStopPaths(fd, allStopPaths); + assertTrue(score < LoadTrafficSensors.DEFAULT_MIN_SNAP_SCORE); + int gapSize = app.countGapsInShape(featureLineString, stopPathLineString, matches); + assertEquals(2, gapSize); + + assertTrue(app.scoreOverlay(featureLineString, stopPathLineString, matches) > LoadTrafficSensors.DEFAULT_MIN_SNAP_SCORE); + } + + @Test + // a really bad match -- confirm by our algorithm its not acceptable + // snapping to the end of the segment, but many points of a complex + // shape not matched + // sensor Walker St. to Pickett St via Duke St. Eastbound to + // stop path 115_to_340 + // LARGE COMPLEX STOP SHAPES score well based on point count and shouldn't + public void testFeatureLargerStopSegment() { + Coordinate[] featureLineString = parseShape(SENSOR_SHAPE_5); + Coordinate[] stopPathLineString = parseShape(STOP_PATH_SHAPE_5); + Coordinate[] matches = parseShape(MATCHES_5); + FeatureData fd = createFeatureData(" shape5", SENSOR_SHAPE_5); + List allStopPaths = new ArrayList<>(); + allStopPaths.add(createStopPath("shape5", STOP_PATH_SHAPE_5)); + double score = app.mapFeatureToStopPaths(fd, allStopPaths); + assertTrue(score < LoadTrafficSensors.DEFAULT_MIN_SNAP_SCORE); + + int gapSize = app.countGapsInShape(featureLineString, stopPathLineString, matches); + assertEquals(2, gapSize); + assertTrue(app.scoreOverlay(featureLineString, stopPathLineString, matches) > LoadTrafficSensors.DEFAULT_MIN_SNAP_SCORE); + } + + /** + * another example of above: + * simple sensor, large complex stop segment + * ensure socre is below acceptable min + * 10:47:56.399 INFO thread=main [o.t.a.LoadTrafficSensors:179] match score 0.3333333333333333 for sensor King St & Callahan Dr to King St & S West St to stop path 17_to_25 + * + * 10:47:56.399 INFO thread=main [o.t.a.LoadTrafficSensors:233] + * http://developer.onebusaway.org/maps/debug.html?polyline=38.8072%2C-77.0625%2038.8071%2C-77.0624%2038.807%2C-77.0619%2038.8069%2C-77.0613%2038.8064%2C-77.0573%2038.806%2C-77.0546%20&points=38.805386%2C-77.068962%2038.80543%2C-77.06895%2038.80502%2C-77.06552%2038.805227%2C-77.065193%2038.80559%2C-77.06453%2038.80634%2C-77.06303%2038.80696%2C-77.06262%2038.8071%2C-77.06236%2038.80694%2C-77.0616%2038.806896%2C-77.061607%20 + */ + @Test + public void testFeatureLargerStopSegment2() { + Coordinate[] featureLineString = parseShape(SENSOR_SHAPE_6); + Coordinate[] stopPathLineString = parseShape(STOP_PATH_SHAPE_6); + Coordinate[] matches = parseShape(MATCHES_6); + FeatureData fd = createFeatureData(" shape6", SENSOR_SHAPE_6); + List allStopPaths = new ArrayList<>(); + allStopPaths.add(createStopPath("shape6", STOP_PATH_SHAPE_6)); + double score = app.mapFeatureToStopPaths(fd, allStopPaths); + assertTrue(score < LoadTrafficSensors.DEFAULT_MIN_SNAP_SCORE); + + int gapSize = app.countGapsInShape(featureLineString, stopPathLineString, matches); + assertEquals(1, gapSize); + } + + @Test + public void score() { + Coordinate[] featureLineString = parseShape(SENSOR_SHAPE_1); + Coordinate[] stopPathLineString = parseShape(STOP_PATH_SHAPE_1); + Coordinate[] matches = parseShape(MATCHES_1); + + FeatureData fd = createFeatureData(" shape1", SENSOR_SHAPE_1); + List allStopPaths = new ArrayList<>(); + allStopPaths.add(createStopPath("shape1", STOP_PATH_SHAPE_1)); + + double score = app.scoreOverlay(featureLineString, stopPathLineString, matches); + assertEquals(-5.0, score, 0.01); + + score = app.mapFeatureToStopPaths(fd, allStopPaths); + assertTrue(score < LoadTrafficSensors.DEFAULT_MIN_SNAP_SCORE); + } + + private Coordinate[] parseShape(String csvShape) { + return toLineString(csvShape); + } + + @Test + public void testNoMatchMapFeatureToStopPaths() { + FeatureData fd = createFeatureData(" S. Van Dorn St & Edsall Rd. to N. Van Dorn St & W. Braddock Rd", SENSOR_SHAPE_1); + List allStopPaths = new ArrayList<>(); + allStopPaths.add(createStopPath("shape1", STOP_PATH_SHAPE_1)); + app.mapFeatureToStopPaths(fd, allStopPaths); + List results = fd.getStopPaths(); + // this is one of the more interesting shapes. To see it + // uncomment this statement + //debugMatch(STOP_PATH_SHAPE_1, SENSOR_SHAPE_1, MATCHES_1); + assertNotNull(results); + // it didn't match! No results + assertEquals(0, results.size()); + } + + @Test + @Ignore + public void testLengthOfShape() throws Exception { + List featureDataList = app.loadFeatureDataFromURL(app.getTrafficUrl()); + FeatureData fd = findFeature(featureDataList, "938"); + assertEquals(fd.getLength(), app.calculateLengthInMeters(fd.getFeatureGeometry().getAsCoordinateArray()), 0.001); + } + + private FeatureData findFeature(List featureDataList, String s) { + for (FeatureData fd : featureDataList) { + if (fd.getId().equals(s)) + return fd; + } + return null; + } + + private void debugMatch(String stopPathShape1, String sensorShape1, String matches1) { + app.debugMatch(toLineString(sensorShape1), toLineString(stopPathShape1), toLineString(matches1)); + + } + + /** + * Adjust snapping tolerance - settle for a high match that + * is meant to be a perfect match + * http://developer.onebusaway.org/maps/debug.html?polyline=38.8009%2C-77.0673%2038.802%2C-77.0675%2038.8023%2C-77.0675%2038.8024%2C-77.0675%2038.8024%2C-77.0674%2038.8024%2C-77.0672%2038.8026%2C-77.0669%2038.8028%2C-77.0665%2038.8032%2C-77.0658%2038.8034%2C-77.0655%2038.8035%2C-77.0653%2038.8036%2C-77.065%2038.8037%2C-77.0646%2038.8037%2C-77.0642%2038.8036%2C-77.0636%2038.8036%2C-77.0634%2038.8042%2C-77.0634%2038.8048%2C-77.0633%20&points=38.80357%2C-77.063774%2038.80363%2C-77.06376%2038.803585%2C-77.063448%2038.804244%2C-77.063389%2038.804371%2C-77.063303%20 + */ + @Test + public void testMatchingRadius() { + FeatureData fd = createFeatureData("f\n" + + "or sensor Eisenhower Ave @ Mill Rd (East of Telegraph) to Duke St & Dulany", SENSOR_SHAPE_7); + List allStopPaths = new ArrayList<>(); + allStopPaths.add(createStopPath("shape1", STOP_PATH_SHAPE_7)); + assertEquals(0.8, app.mapFeatureToStopPaths(fd, allStopPaths), 0.001); + } + + + /** + * Another radius snapping test, with some centerline data that isn't perfect + * http://developer.onebusaway.org/maps/debug.html?polyline=38.8068%2C-77.0759%2038.8068%2C-77.0767%2038.8069%2C-77.0773%2038.8071%2C-77.0786%2038.8073%2C-77.0801%2038.8075%2C-77.0812%2038.8074%2C-77.0815%2038.8075%2C-77.0817%2038.8075%2C-77.082%2038.8076%2C-77.0827%2038.8078%2C-77.0838%2038.808%2C-77.0852%2038.8081%2C-77.0862%2038.8082%2C-77.0869%2038.8083%2C-77.0872%2038.8083%2C-77.0873%2038.8084%2C-77.0876%2038.8084%2C-77.088%2038.8085%2C-77.0887%20&points=38.806889%2C-77.076523%2038.80682%2C-77.07653%2038.80745%2C-77.08118%2038.80745%2C-77.08168%2038.807552%2C-77.081673%20 + */ + @Test + public void testMatchingRadius2() { + FeatureData fd = createFeatureData("f\n" + + " Taylor Run Pkwy. to Quaker Ln. via Duke St. Westbound", SENSOR_SHAPE_8); + List allStopPaths = new ArrayList<>(); + allStopPaths.add(createStopPath("shape1", STOP_PATH_SHAPE_8)); //2 4_to_32 + assertEquals(0.6, app.mapFeatureToStopPaths(fd, allStopPaths), 0.001); + } + + /** + * Another radius snapping test, this one that if radius is too large it will match to the wrong lane + * sensor Taylor Run Pkwy to Patrick St via Duke St. Eastbound to stop path 6_to_7 + * + * http://developer.onebusaway.org/maps/debug.html?polyline=38.8067%2C-77.0761%2038.8065%2C-77.0747%2038.8064%2C-77.0739%2038.8062%2C-77.073%2038.8062%2C-77.0728%2038.8059%2C-77.0715%2038.8057%2C-77.0703%2038.8055%2C-77.0694%2038.8053%2C-77.0675%2038.805%2C-77.0655%2038.8048%2C-77.0639%2038.8046%2C-77.0624%2038.8045%2C-77.0613%2038.8043%2C-77.06%2038.8041%2C-77.0584%2038.804%2C-77.0573%2038.8039%2C-77.0568%2038.8039%2C-77.0568%2038.8039%2C-77.0567%2038.8039%2C-77.0564%2038.8038%2C-77.0556%2038.8038%2C-77.0553%2038.8037%2C-77.0551%2038.8035%2C-77.054%2038.8034%2C-77.0529%2038.8034%2C-77.0526%2038.8033%2C-77.0522%2038.8033%2C-77.0519%2038.8033%2C-77.0517%2038.8032%2C-77.0508%20&points=38.803116%2C-77.04953%2038.80304%2C-77.04954%2038.80332%2C-77.05174%2038.80339%2C-77.05198%2038.80343%2C-77.05244%2038.803493%2C-77.052436%20 + */ + @Test + public void testMatchingRadius3() { + FeatureData fd = createFeatureData("f\n" + + "Taylor Run Pkwy to Patrick St via Duke St. Eastbound", SENSOR_SHAPE_9); + List allStopPaths = new ArrayList<>(); + allStopPaths.add(createStopPath("shape1", STOP_PATH_SHAPE_9)); // 6_to_7 + assertTrue(app.mapFeatureToStopPaths(fd, allStopPaths) < LoadTrafficSensors.DEFAULT_MIN_SNAP_SCORE); + } + + @Test + public void testMatchMapFeatureToStopPaths() { + FeatureData fd = createFeatureData("sensor2", SENSOR_SHAPE_2); + List allStopPaths = new ArrayList<>(); + allStopPaths.add(createStopPath("shape2", STOP_PATH_SHAPE_2)); + app.mapFeatureToStopPaths(fd, allStopPaths); + List results = fd.getStopPaths(); + assertNotNull(results); + // we matched, expecting a result + assertEquals(1, results.size()); + Coordinate[] matches = app.toLineString(results.get(0)); + app.debugShape("test match", matches); + } + + + private StopPath createStopPath(String shapeName, String stopPathShape) { + + StopPath sp = new StopPath(-1, + shapeName, + "stopId", + -1, + false, + "routeId", + false, + false, + false, + -1, + 1.0, + 2.0); + sp.setLocations(new ArrayList<>()); + for (Coordinate c : toLineString(stopPathShape)) { + sp.getLocations().add(new Location(c.x, c.y)); + } + return sp; + } + + private FeatureData createFeatureData(String s, String sensorShape) { + FeatureData fd = new FeatureData(); + FeatureGeometry fg = new FeatureGeometry(); + fd.setFeatureGeometry(fg); + for (Coordinate c : toLineString(sensorShape)) { + fg.addLatLon(c.x, c.y); + } + + return fd; + } + + private Coordinate[] toLineString(String csvCoordinates) { + List lineString = new ArrayList<>(); + String[] lines = csvCoordinates.split("\n"); + for (String line : lines) { + String[] latLonStr = line.split(","); + lineString.add(new Coordinate(Double.parseDouble(latLonStr[0]), Double.parseDouble(latLonStr[1]))); + } + return lineString.toArray(new Coordinate[lineString.size()]); + } + + private static final String SENSOR_SHAPE_1 = + "38.8076,-77.1331\n" + + "38.8107,-77.132\n" + + "38.8111,-77.1319\n" + + "38.8115,-77.1317\n" + + "38.8118,-77.1315\n" + + "38.8122,-77.1312\n" + + "38.8126,-77.1309\n" + + "38.8131,-77.1305\n" + + "38.8139,-77.1297\n" + + "38.8153,-77.1284\n" + + "38.8155,-77.1283\n" + + "38.8157,-77.1283\n" + + "38.816,-77.1282\n" + + "38.8162,-77.1281\n" + + "38.8164,-77.1281\n" + + "38.8167,-77.1282\n" + + "38.817,-77.1283\n" + + "38.8174,-77.1285\n" + + "38.8176,-77.1285\n" + + "38.8178,-77.1286\n" + + "38.818,-77.1286\n" + + "38.8183,-77.1286\n" + + "38.8185,-77.1285\n" + + "38.8187,-77.1285\n" + + "38.819,-77.1283\n" + + "38.8191,-77.1283\n" + + "38.8192,-77.1283\n" + + "38.8192,-77.1283\n" + + "38.8195,-77.1281\n" + + "38.82,-77.1279\n" + + "38.8205,-77.1276\n" + + "38.8209,-77.1274\n" + + "38.821,-77.1272\n" + + "38.8217,-77.1264\n" + + "38.8222,-77.1257\n" + + "38.8226,-77.1252\n" + + "38.8228,-77.125\n" + + "38.8237,-77.1237\n" + + "38.8244,-77.1226\n" + + "38.8252,-77.1214\n" + + "38.826,-77.1201\n" + + "38.8266,-77.119\n" + + "38.827,-77.1182\n" + + "38.8279,-77.1164\n" + + "38.8287,-77.1145\n" + + "38.8295,-77.1128\n" + + "38.8297,-77.1123\n" + + "38.8299,-77.1116\n" + + "38.8299,-77.1114\n" + + "38.8301,-77.111\n" + + "38.8301,-77.1109\n" + + "38.8301,-77.1109\n" + + "38.8302,-77.1104\n" + + "38.8304,-77.1099\n" + + "38.8305,-77.1097\n" + + "38.8305,-77.1094\n" + + "38.8306,-77.1091\n" + + "38.8308,-77.1087\n" + + "38.8309,-77.1084\n" + + "38.831,-77.1084\n" + + "38.8313,-77.1077\n" + + "38.8314,-77.1074\n"; + private static final String SENSOR_SHAPE_2 = + "38.8067,-77.0761\n" + + "38.8065,-77.0747\n" + + "38.8064,-77.0739\n" + + "38.8062,-77.073\n" + + "38.8062,-77.0728\n" + + "38.8059,-77.0715\n" + + "38.8057,-77.0703\n" + + "38.8055,-77.0694\n" + + "38.8053,-77.0675\n" + + "38.805,-77.0655\n" + + "38.8048,-77.0639\n" + + "38.8046,-77.0624\n" + + "38.8045,-77.0613\n" + + "38.8043,-77.06\n" + + "38.8041,-77.0584\n" + + "38.804,-77.0573\n" + + "38.8039,-77.0568\n" + + "38.8039,-77.0568\n" + + "38.8039,-77.0567\n" + + "38.8039,-77.0564\n" + + "38.8038,-77.0556\n" + + "38.8038,-77.0553\n" + + "38.8037,-77.0551\n" + + "38.8035,-77.054\n" + + "38.8034,-77.0529\n" + + "38.8034,-77.0526\n" + + "38.8033,-77.0522\n" + + "38.8033,-77.0519\n" + + "38.8033,-77.0517\n" + + "38.8032,-77.0508"; + private static final String SENSOR_SHAPE_3 = + "38.814,-77.0716\n" + + "38.8139,-77.0718\n" + + "38.8138,-77.0731\n" + + "38.8138,-77.0736\n" + + "38.8137,-77.0741\n" + + "38.8138,-77.0744\n" + + "38.8138,-77.0748\n" + + "38.814,-77.0756\n" + + "38.8141,-77.0766\n" + + "38.8141,-77.0768\n" + + "38.8142,-77.077\n" + + "38.8142,-77.0783\n" + + "38.8142,-77.0792\n" + + "38.8142,-77.0802\n" + + "38.8142,-77.0803\n" + + "38.8142,-77.0806\n" + + "38.8142,-77.0812\n" + + "38.8143,-77.0815\n" + + "38.8145,-77.0823\n" + + "38.8147,-77.0826\n" + + "38.8149,-77.0831\n" + + "38.8151,-77.0835\n" + + "38.8153,-77.0838\n" + + "38.8155,-77.0842\n" + + "38.816,-77.0856\n" + + "38.8167,-77.0872\n" + + "38.8173,-77.0889\n" + + "38.8173,-77.0891\n" + + "38.8174,-77.0895"; + private static final String SENSOR_SHAPE_4 = + "38.8141,-77.1339\n" + + "38.8141,-77.1333\n" + + "38.8141,-77.1325\n" + + "38.8142,-77.1312\n" + + "38.8142,-77.1309\n" + + "38.8142,-77.1304\n" + + "38.8142,-77.1298\n" + + "38.8142,-77.129\n" + + "38.8141,-77.1283\n" + + "38.8139,-77.1276\n" + + "38.8137,-77.1274\n" + + "38.8132,-77.1261\n" + + "38.813,-77.1257\n" + + "38.8128,-77.1252\n" + + "38.8127,-77.1246\n" + + "38.8126,-77.1239\n" + + "38.8123,-77.122\n" + + "38.8123,-77.1217"; + private static final String SENSOR_SHAPE_5 = + "38.8141,-77.1339\n" + + "38.8141,-77.1333\n" + + "38.8141,-77.1325\n" + + "38.8142,-77.1312\n" + + "38.8142,-77.1309\n" + + "38.8142,-77.1304\n" + + "38.8142,-77.1298\n" + + "38.8142,-77.129\n" + + "38.8141,-77.1283\n" + + "38.8139,-77.1276\n" + + "38.8137,-77.1274\n" + + "38.8132,-77.1261\n" + + "38.813,-77.1257\n" + + "38.8128,-77.1252\n" + + "38.8127,-77.1246\n" + + "38.8126,-77.1239\n" + + "38.8123,-77.122\n" + + "38.8123,-77.1217"; + private static final String SENSOR_SHAPE_6 = + "38.8072,-77.0625\n" + + "38.8071,-77.0624\n" + + "38.807,-77.0619\n" + + "38.8069,-77.0613\n" + + "38.8064,-77.0573\n" + + "38.806,-77.0546"; + private static final String SENSOR_SHAPE_7 = + "38.8009,-77.0673\n" + + "38.802,-77.0675\n" + + "38.8023,-77.0675\n" + + "38.8024,-77.0675\n" + + "38.8024,-77.0674\n" + + "38.8024,-77.0672\n" + + "38.8026,-77.0669\n" + + "38.8028,-77.0665\n" + + "38.8032,-77.0658\n" + + "38.8034,-77.0655\n" + + "38.8035,-77.0653\n" + + "38.8036,-77.065\n" + + "38.8037,-77.0646\n" + + "38.8037,-77.0642\n" + + "38.8036,-77.0636\n" + + "38.8036,-77.0634\n" + + "38.8042,-77.0634\n" + + "38.8048,-77.0633"; + private static final String SENSOR_SHAPE_8 = + "38.8068,-77.0759\n" + + "38.8068,-77.0767\n" + + "38.8069,-77.0773\n" + + "38.8071,-77.0786\n" + + "38.8073,-77.0801\n" + + "38.8075,-77.0812\n" + + "38.8074,-77.0815\n" + + "38.8075,-77.0817\n" + + "38.8075,-77.082\n" + + "38.8076,-77.0827\n" + + "38.8078,-77.0838\n" + + "38.808,-77.0852\n" + + "38.8081,-77.0862\n" + + "38.8082,-77.0869\n" + + "38.8083,-77.0872\n" + + "38.8083,-77.0873\n" + + "38.8084,-77.0876\n" + + "38.8084,-77.088\n" + + "38.8085,-77.0887\n"; + private static final String SENSOR_SHAPE_9 = + "38.8067,-77.0761\n" + + "38.8065,-77.0747\n" + + "38.8064,-77.0739\n" + + "38.8062,-77.073\n" + + "38.8062,-77.0728\n" + + "38.8059,-77.0715\n" + + "38.8057,-77.0703\n" + + "38.8055,-77.0694\n" + + "38.8053,-77.0675\n" + + "38.805,-77.0655\n" + + "38.8048,-77.0639\n" + + "38.8046,-77.0624\n" + + "38.8045,-77.0613\n" + + "38.8043,-77.06\n" + + "38.8041,-77.0584\n" + + "38.804,-77.0573\n" + + "38.8039,-77.0568\n" + + "38.8039,-77.0568\n" + + "38.8039,-77.0567\n" + + "38.8039,-77.0564\n" + + "38.8038,-77.0556\n" + + "38.8038,-77.0553\n" + + "38.8037,-77.0551\n" + + "38.8035,-77.054\n" + + "38.8034,-77.0529\n" + + "38.8034,-77.0526\n" + + "38.8033,-77.0522\n" + + "38.8033,-77.0519\n" + + "38.8033,-77.0517\n" + + "38.8032,-77.0508"; + private static final String STOP_PATH_SHAPE_1 = + "38.812252,-77.131073\n" + + "38.81228,-77.13114\n" + + "38.81316,-77.13039\n" + + "38.81362,-77.13105\n" + + "38.81386,-77.13108\n" + + "38.81406,-77.1309\n" + + "38.81421,-77.13061\n" + + "38.81422,-77.12945\n" + + "38.81413,-77.1287\n" + + "38.81385,-77.12765\n" + + "38.81324,-77.12627\n" + + "38.813187,-77.126305\n"; + private static final String STOP_PATH_SHAPE_2 = + "38.80352,-77.054298\n" + + "38.80356,-77.05429\n" + + "38.80332,-77.05216\n" + + "38.803212,-77.050955\n" + + "38.80309,-77.04989\n" + + "38.803051,-77.049896"; + private static final String STOP_PATH_SHAPE_3 = + "38.813942,-77.075043\n" + + "38.81387,-77.07506\n" + + "38.81415,-77.07693\n" + + "38.814194,-77.076904"; + private static final String STOP_PATH_SHAPE_4 = + "38.814014,-77.133179\n" + + "38.8141,-77.13318\n" + + "38.81408,-77.1325\n" + + "38.81404,-77.13241\n" + + "38.81403,-77.13154\n" + + "38.81384,-77.1313\n" + + "38.81344,-77.13103\n" + + "38.81328,-77.13076\n" + + "38.81298,-77.13075\n" + + "38.8123,-77.13136\n" + + "38.812359,-77.131416"; + private static final String STOP_PATH_SHAPE_5 = + "38.814014,-77.133179\n" + + "38.8141,-77.13318\n" + + "38.81408,-77.1325\n" + + "38.81404,-77.13241\n" + + "38.81403,-77.13154\n" + + "38.81384,-77.1313\n" + + "38.81344,-77.13103\n" + + "38.81309,-77.13045\n" + + "38.815261,-77.12853\n" + + "38.815424,-77.128426\n" + + "38.815636,-77.128721\n" + + "38.815846,-77.128813\n" + + "38.816217,-77.128653\n" + + "38.817843,-77.12934\n" + + "38.818376,-77.129372\n" + + "38.818804,-77.12956\n" + + "38.818867,-77.12964\n" + + "38.81888,-77.129806\n" + + "38.81872,-77.130213\n" + + "38.817751,-77.132086\n" + + "38.817283,-77.131855\n" + + "38.817145,-77.131459\n" + + "38.817223,-77.131126"; + private static final String STOP_PATH_SHAPE_6 = + "38.805386,-77.068962\n" + + "38.80543,-77.06895\n" + + "38.80502,-77.06552\n" + + "38.805227,-77.065193\n" + + "38.80559,-77.06453\n" + + "38.80634,-77.06303\n" + + "38.80696,-77.06262\n" + + "38.8071,-77.06236\n" + + "38.80694,-77.0616\n" + + "38.806896,-77.061607"; + private static final String STOP_PATH_SHAPE_7 = + "38.80357,-77.063774\n" + + "38.80363,-77.06376\n" + + "38.803585,-77.063448\n" + + "38.804244,-77.063389\n" + + "38.804371,-77.063303"; + private static final String STOP_PATH_SHAPE_8 = + "38.806889,-77.076523\n" + + "38.80682,-77.07653\n" + + "38.80745,-77.08118\n" + + "38.80745,-77.08168\n" + + "38.807552,-77.081673\n"; + private static final String STOP_PATH_SHAPE_9 = + "38.803116,-77.04953\n" + + "38.80304,-77.04954\n" + + "38.80332,-77.05174\n" + + "38.80339,-77.05198\n" + + "38.80343,-77.05244\n" + + "38.803493,-77.052436"; + private static final String MATCHES_1 = + "38.8076,-77.1331\n" + + "38.8107,-77.132\n" + + "38.8111,-77.1319\n" + + "38.8115,-77.1317\n" + + "38.8118,-77.1315\n" + + "38.8122,-77.1312\n" + + "38.81228,-77.13114\n" + + "38.8126,-77.1309\n" + + "38.8131,-77.1305\n" + + "38.8139,-77.1297\n" + + "38.8153,-77.1284\n" + + "38.8155,-77.1283\n" + + "38.8157,-77.1283\n" + + "38.816,-77.1282\n" + + "38.8162,-77.1281\n" + + "38.8164,-77.1281\n" + + "38.8167,-77.1282\n" + + "38.817,-77.1283\n" + + "38.8174,-77.1285\n" + + "38.8176,-77.1285\n" + + "38.8178,-77.1286\n" + + "38.818,-77.1286\n" + + "38.8183,-77.1286\n" + + "38.8185,-77.1285\n" + + "38.8187,-77.1285\n" + + "38.819,-77.1283\n" + + "38.8191,-77.1283\n" + + "38.8192,-77.1283\n" + + "38.8192,-77.1283\n" + + "38.8195,-77.1281\n" + + "38.82,-77.1279\n" + + "38.8205,-77.1276\n" + + "38.8209,-77.1274\n" + + "38.821,-77.1272\n" + + "38.8217,-77.1264\n" + + "38.8222,-77.1257\n" + + "38.8226,-77.1252\n" + + "38.8228,-77.125\n" + + "38.8237,-77.1237\n" + + "38.8244,-77.1226\n" + + "38.8252,-77.1214\n" + + "38.826,-77.1201\n" + + "38.8266,-77.119\n" + + "38.827,-77.1182\n" + + "38.8279,-77.1164\n" + + "38.8287,-77.1145\n" + + "38.8295,-77.1128\n" + + "38.8297,-77.1123\n" + + "38.8299,-77.1116\n" + + "38.8299,-77.1114\n" + + "38.8301,-77.111\n" + + "38.8301,-77.1109\n" + + "38.8301,-77.1109\n" + + "38.8302,-77.1104\n" + + "38.8304,-77.1099\n" + + "38.8305,-77.1097\n" + + "38.8305,-77.1094\n" + + "38.8306,-77.1091\n" + + "38.8308,-77.1087\n" + + "38.8309,-77.1084\n" + + "38.831,-77.1084\n" + + "38.8313,-77.1077\n" + + "38.8314,-77.1074"; + private static final String MATCHES_2 = + "38.8067,-77.0761\n" + + "38.8065,-77.0747\n" + + "38.8064,-77.0739\n" + + "38.8062,-77.073\n" + + "38.8062,-77.0728\n" + + "38.8059,-77.0715\n" + + "38.8057,-77.0703\n" + + "38.8055,-77.0694\n" + + "38.8053,-77.0675\n" + + "38.805,-77.0655\n" + + "38.8048,-77.0639\n" + + "38.8046,-77.0624\n" + + "38.8045,-77.0613\n" + + "38.8043,-77.06\n" + + "38.8041,-77.0584\n" + + "38.804,-77.0573\n" + + "38.8039,-77.0568\n" + + "38.8039,-77.0568\n" + + "38.8039,-77.0567\n" + + "38.8039,-77.0564\n" + + "38.8038,-77.0556\n" + + "38.8038,-77.0553\n" + + "38.8037,-77.0551\n" + + "38.80356,-77.05429\n" + + "38.8035,-77.054\n" + + "38.8034,-77.0529\n" + + "38.8034,-77.0526\n" + + "38.8033,-77.0522\n" + + "38.8033,-77.0519\n" + + "38.8033,-77.0517\n" + + "38.803212,-77.050955\n" + + "38.8032,-77.0508"; + private static final String MATCHES_3 = + "38.814,-77.0716\n" + + "38.8139,-77.0718\n" + + "38.8138,-77.0731\n" + + "38.8138,-77.0736\n" + + "38.8137,-77.0741\n" + + "38.8138,-77.0744\n" + + "38.8138,-77.0748\n" + + "38.81387,-77.07506\n" + + "38.814,-77.0756\n" + + "38.8141,-77.0766\n" + + "38.8141,-77.0768\n" + + "38.8142,-77.077\n" + + "38.8142,-77.0783\n" + + "38.8142,-77.0792\n" + + "38.8142,-77.0802\n" + + "38.8142,-77.0803\n" + + "38.8142,-77.0806\n" + + "38.8142,-77.0812\n" + + "38.8143,-77.0815\n" + + "38.8145,-77.0823\n" + + "38.8147,-77.0826\n" + + "38.8149,-77.0831\n" + + "38.8151,-77.0835\n" + + "38.8153,-77.0838\n" + + "38.8155,-77.0842\n" + + "38.816,-77.0856\n" + + "38.8167,-77.0872\n" + + "38.8173,-77.0889\n" + + "38.8173,-77.0891\n" + + "38.8174,-77.0895"; + + private static final String MATCHES_4 = + "38.8141,-77.1339\n" + + "38.8141,-77.1333\n" + + "38.8141,-77.13318\n" + + "38.8141,-77.1325\n" + + "38.8142,-77.1312\n" + + "38.8142,-77.1309\n" + + "38.8142,-77.1304\n" + + "38.8142,-77.1298\n" + + "38.8142,-77.129\n" + + "38.8141,-77.1283\n" + + "38.8139,-77.1276\n" + + "38.8137,-77.1274\n" + + "38.8132,-77.1261\n" + + "38.813,-77.1257\n" + + "38.8128,-77.1252\n" + + "38.8127,-77.1246\n" + + "38.8126,-77.1239\n" + + "38.8123,-77.122\n" + + "38.8123,-77.1217"; + private static final String MATCHES_5 = + "38.8141,-77.1339\n" + + "38.8141,-77.1333\n" + + "38.8141,-77.13318\n" + + "38.8141,-77.1325\n" + + "38.8142,-77.1312\n" + + "38.8142,-77.1309\n" + + "38.8142,-77.1304\n" + + "38.8142,-77.1298\n" + + "38.8142,-77.129\n" + + "38.8141,-77.1283\n" + + "38.8139,-77.1276\n" + + "38.8137,-77.1274\n" + + "38.8132,-77.1261\n" + + "38.813,-77.1257\n" + + "38.8128,-77.1252\n" + + "38.8127,-77.1246\n" + + "38.8126,-77.1239\n" + + "38.8123,-77.122\n" + + "38.8123,-77.1217"; + private static final String MATCHES_6 = + "38.8071,-77.06236\n" + + "38.80694,-77.0616"; +} \ No newline at end of file diff --git a/transitclock/src/test/java/org/transitclock/core/ExternalBlockAssignerTest.java b/transitclock/src/test/java/org/transitclock/core/ExternalBlockAssignerTest.java new file mode 100644 index 000000000..6bee23aa5 --- /dev/null +++ b/transitclock/src/test/java/org/transitclock/core/ExternalBlockAssignerTest.java @@ -0,0 +1,161 @@ +package org.transitclock.core; + +import junit.framework.TestCase; +import org.transitclock.db.structs.AvlReport; +import org.transitclock.db.structs.Block; +import org.transitclock.db.structs.Trip; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Date; +import java.util.Map; + +public class ExternalBlockAssignerTest extends TestCase { + + private static String FEED_0 = "block,vehicle\n"; + private static String FEED_1 = "block,vehicle\nblock-1,vehicle-1\n"; + private static String FEED_2 = "block,vehicle\nblock-1,vehicle-1\nblock-2,vehicle-2\n"; + + public void testSuite() throws Exception { + // we need to control order of tests + runGetFeed(); + runGetActiveAssignmentForVehicle(); + } + + + public void runGetFeed() throws Exception { + ExternalBlockAssigner.reset(); + ExternalBlockAssigner eba = ExternalBlockAssigner.getInstance(); + assertNull(eba.getBlockAssignmentsByVehicleIdFeed()); + + System.setProperty("transitclock.externalAssignerEnabled", "true"); + eba.externalAssignerEnabled.readValue(); + assertNull(eba.getBlockAssignmentsByVehicleIdFeed()); + System.setProperty("transitclock.externalAssignerUrl", "file:///tmp/no_such_file.txt"); + eba.externalAssignerUrl.readValue(); + try { + InputStream feed = eba.getBlockAssignmentsByVehicleIdFeed(); + fail(); + } catch (FileNotFoundException fnfe) { + // good! + } + + // flush the feed to a file on disk and load + writeToTempFile(eba, FEED_2); + + InputStream feed = eba.getBlockAssignmentsByVehicleIdFeed(); + assertNotNull(feed); + + Map> feedMap = eba.getBlockAssignmentsByVehicleIdMap(); + assertNotNull(feedMap); + assertEquals(2, feedMap.size()); // force throwing away header + assertTrue(feedMap.containsKey("vehicle-1")); + assertEquals("block-1", feedMap.get("vehicle-1").get(0)); + assertTrue(feedMap.containsKey("vehicle-2")); + assertEquals("block-2", feedMap.get("vehicle-2").get(0)); + + eba.forceUpdate(); // ensure cache is consistent for testing + feedMap = eba.getBlockAssignmentsByVehicleIdMapFromCache(); + + assertNotNull(feedMap); + assertEquals(2, feedMap.size()); // force throwing away header + assertTrue(feedMap.containsKey("vehicle-1")); + assertEquals("block-1", feedMap.get("vehicle-1").get(0)); + assertTrue(feedMap.containsKey("vehicle-2")); + assertEquals("block-2", feedMap.get("vehicle-2").get(0)); + + // now retrieve an empty feed and verify assignments were removed + // flush the feed to a file on disk and load + writeToTempFile(eba, FEED_1); + eba.forceUpdate(); // ensure cache is consistent for testing + feedMap = eba.getBlockAssignmentsByVehicleIdMapFromCache(); + + assertNotNull(feedMap); + assertEquals(1, feedMap.size()); + + + // now retrieve an empty feed and verify assignments were removed + // flush the feed to a file on disk and load + writeToTempFile(eba, FEED_0); + eba.forceUpdate(); // ensure cache is consistent for testing + feedMap = eba.getBlockAssignmentsByVehicleIdMapFromCache(); + + assertNotNull(feedMap); + assertEquals(0, feedMap.size()); + + } + + public void runGetActiveAssignmentForVehicle() throws Exception { + // do some cleanup from last test + ExternalBlockAssigner.reset(); + System.setProperty("transitclock.externalAssignerEnabled", "false"); + System.setProperty("transitclock.externalAssignerUrl", "file:///tmp/no_such_file.txt"); + + + // mock out database retrieval of block + ExternalBlockAssigner eba = new ExternalBlockAssigner() { + @Override + Block getActiveBlock(String assignmentId, Date serviceDate) { + if (assignmentId != null && assignmentId.startsWith("block-") && serviceDate.getTime() > 0) { + Block b = new Block(-1, + assignmentId, + "serviceId", + (int)(System.currentTimeMillis()-1000)/1000, + (int)(System.currentTimeMillis()+1000)/1000, + new ArrayList()); + return b; + } + return null; + } + }; + + eba.externalAssignerUrl.readValue(); + eba.externalAssignerEnabled.readValue(); + eba.getInstance(); + // verify we are empty, and no state left over from previous test + assertNotNull(eba.getBlockAssignmentsByVehicleIdMap()); + assertTrue(eba.getBlockAssignmentsByVehicleIdMap().isEmpty()); + assertNotNull(eba.getBlockAssignmentsByVehicleIdMapFromCache()); + assertTrue(eba.getBlockAssignmentsByVehicleIdMapFromCache().isEmpty()); + + // now re-enable + System.setProperty("transitclock.externalAssignerEnabled", "true"); + eba.externalAssignerEnabled.readValue(); + eba.getInstance(); + + // flush the feed to a file on disk and load + writeToTempFile(eba, FEED_2); + + AvlReport avl = new AvlReport("vehicle-1", System.currentTimeMillis(), 0.0, 0.0, + "OpenGTS"); + String blockId = eba.getActiveAssignmentForVehicle(avl); + + assertNotNull(blockId); + assertEquals("block-1", blockId); + + avl = new AvlReport("vehicle-1", -1, 0.0, 0.0, + "OpenGTS"); + blockId = eba.getActiveAssignmentForVehicle(avl); + assertNull(blockId); + + } + + private void writeToFile(File file, String contents) throws Exception { + PrintWriter out = new PrintWriter(file); + out.write(contents); + out.close(); + } + + private void writeToTempFile(ExternalBlockAssigner eba, String contents) throws Exception { + File tmpFile = File.createTempFile("externalBlockAssiger", ".csv"); + tmpFile.deleteOnExit(); + writeToFile(tmpFile, contents); + System.setProperty("transitclock.externalAssignerUrl", "file://" + tmpFile.getAbsolutePath()); + eba.externalAssignerUrl.readValue(); + eba.getInstance().forceUpdate(); + + } +} diff --git a/transitclock/src/test/java/org/transitclock/core/predAccuracy/PredictionAccuracyModuleTest.java b/transitclock/src/test/java/org/transitclock/core/predAccuracy/PredictionAccuracyModuleTest.java new file mode 100644 index 000000000..67e0b6fc5 --- /dev/null +++ b/transitclock/src/test/java/org/transitclock/core/predAccuracy/PredictionAccuracyModuleTest.java @@ -0,0 +1,207 @@ +package org.transitclock.core.predAccuracy; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.transitclock.db.structs.*; +import org.transitclock.gtfs.GtfsData; +import org.transitclock.gtfs.TitleFormatter; +import org.transitclock.gtfs.gtfsStructs.GtfsRoute; +import org.transitclock.gtfs.gtfsStructs.GtfsStop; +import org.transitclock.gtfs.gtfsStructs.GtfsTrip; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +public class PredictionAccuracyModuleTest { + @Mock + private GtfsData gtfsData; + + + @Before + public void setup(){ + gtfsData = mock(GtfsData.class); + when(gtfsData.isTripPatternIdAlreadyUsed(any())).thenReturn(false); + when(gtfsData.getStop(any())).thenReturn(getStop()); + } + + @Test + public void testGetRoutesAndStops(){ + PredictionAccuracyModule predictionAccuracyModule = new PredictionAccuracyModule("1"); + List routes = new ArrayList<>(); + String routeId = "A"; + TitleFormatter formatter = getTitleFormatter(); + List tripPatterns = getLongestTripPatternsForRoute(routeId, formatter); + + Route route = getRoute(routeId, tripPatterns, formatter); + + Route spyRoute = spy(route); + doReturn(Arrays.asList("0","1")).when(spyRoute).getDirectionIds(); + doReturn(tripPatterns.get(0)).when(spyRoute).getLongestTripPatternForDirection("0"); + doReturn(tripPatterns.get(1)).when(spyRoute).getLongestTripPatternForDirection("1"); + + routes.add(spyRoute); + + List routeAndStops = predictionAccuracyModule.getRoutesAndStops(routes); + + assertEquals("A", routeAndStops.get(0).routeId); + + // test to confirm number of stops on longest trip pattern for dir 0 + assertEquals(5, routeAndStops.get(0).stopIds.get("0").size()); + + // test to confirm number of stops on longest trip pattern for dir 1 + assertEquals(4, routeAndStops.get(0).stopIds.get("1").size()); + + } + + @Test + public void testGetAllRoutesAndStops(){ + PredictionAccuracyModule predictionAccuracyModule = new PredictionAccuracyModule("1"); + + List routes = new ArrayList<>(); + String routeId = "A"; + TitleFormatter formatter = getTitleFormatter(); + List tripPatterns = getAllTripPatterns(routeId, formatter); + + List tripPatternsDir0 = getAllTripPatternsForRouteDir0(routeId, formatter); + List tripPatternsDir1 = getAllTripPatternsForRouteDir1(routeId, formatter); + + Route route = getRoute(routeId, tripPatterns, formatter); + + Route spyRoute = spy(route); + doReturn(Arrays.asList("0","1")).when(spyRoute).getDirectionIds(); + doReturn(tripPatternsDir0).when(spyRoute).getTripPatterns("0"); + doReturn(tripPatternsDir1).when(spyRoute).getTripPatterns("1"); + + routes.add(spyRoute); + + List routeAndStops = predictionAccuracyModule.getAllRoutesAndStops(routes); + + assertEquals("A", routeAndStops.get(0).routeId); + + // test to confirm number of unique stops for all trip patterns for dir 0 + // first trip pattern has two stops {0, 1} + // second trip pattern has three stops {1, 2, 3} + // stopIds should contain union of all trip patterns in direction 0 + assertEquals(4, routeAndStops.get(0).stopIds.get("0").size()); + + // test to confirm number of unique stops for all trip patterns for dir 1 + // first trip pattern has two stops {0, 1} + // second trip pattern has three stops {2, 3, 4} + // stopIds should contain union of all trip patterns in direction 1 + assertEquals(5, routeAndStops.get(0).stopIds.get("1").size()); + } + + private TitleFormatter getTitleFormatter(){ + return new TitleFormatter("", false); + } + + private List getLongestTripPatternsForRoute(String routeId, TitleFormatter formatter){ + List tripPatternsForRoute = new ArrayList<>(); + + TripPattern tripPattern0 = getTripPattern("0", routeId, 0, 5, formatter); + TripPattern tripPattern1 = getTripPattern("1", routeId, 10, 4, formatter); + + tripPatternsForRoute.add(tripPattern0); + tripPatternsForRoute.add(tripPattern1); + + return tripPatternsForRoute; + } + + private List getAllTripPatterns(String routeId, TitleFormatter formatter){ + List tripPatternsForRoute = new ArrayList<>(); + tripPatternsForRoute.addAll(getAllTripPatternsForRouteDir0(routeId, formatter)); + tripPatternsForRoute.addAll(getAllTripPatternsForRouteDir1(routeId, formatter)); + return tripPatternsForRoute; + } + + // Get Trip Patterns that have Overlapping Stops in direction 0 + private List getAllTripPatternsForRouteDir0(String routeId, TitleFormatter formatter){ + List tripPatternsForRoute = new ArrayList<>(); + + TripPattern tripPattern0a = getTripPattern("0", routeId, 0, 2, formatter); + TripPattern tripPattern0b = getTripPattern("0", routeId, 1, 3, formatter); + + tripPatternsForRoute.add(tripPattern0a); + tripPatternsForRoute.add(tripPattern0b); + + return tripPatternsForRoute; + } + + // Get Trip Patterns that have no overlapping Stops in direction 1 + private List getAllTripPatternsForRouteDir1(String routeId, TitleFormatter formatter){ + List tripPatternsForRoute = new ArrayList<>(); + + TripPattern tripPattern1a = getTripPattern("1", routeId, 0, 2, formatter); + TripPattern tripPattern1b = getTripPattern("1", routeId, 2, 3, formatter); + + tripPatternsForRoute.add(tripPattern1a); + tripPatternsForRoute.add(tripPattern1b); + + return tripPatternsForRoute; + } + + + private Route getRoute(String routeId, List tripPatterns, TitleFormatter formatter){ + GtfsRoute gtfsRoute = getGtfsRoute(routeId); + return new Route(-1,gtfsRoute, tripPatterns, formatter); + } + + private GtfsRoute getGtfsRoute(String routeId){ + String agencyId = "1"; + String routeShortName = "A"; + String routeLongName = "A"; + String routeType = "routeType"; + String routeColor = "color"; + String routeTextColor = "routeTextColor"; + return new GtfsRoute(routeId, agencyId, routeShortName, routeLongName, routeType, routeColor, + routeTextColor); + } + + private TripPattern getTripPattern(String directionId, String routeId, int stopIndexStart, int numberOfStops, TitleFormatter formatter){ + Trip trip = getTrip(directionId, formatter, routeId); + List stopPathsDir = getStopPaths(routeId, stopIndexStart,numberOfStops); + return new TripPattern(-1, "shapeId", stopPathsDir, trip, gtfsData); + } + + private Trip getTrip(String directionId, TitleFormatter formatter, String routeId){ + String routeShortName = routeId; + String headsign = "headsign"; + GtfsTrip gtfsTrip = getGtfsTrip(routeId, directionId, headsign); + + return new Trip(-1, gtfsTrip, routeId, routeShortName, headsign, formatter); + } + + private GtfsTrip getGtfsTrip(String routeId, String directionId, String headsign){ + String serviceId = "serviceId"; + String tripId = "tripId"; + String tripShortName = "tripA"; + String blockId = "blockId"; + String shapeId = "shapeId"; + + return new GtfsTrip(routeId, serviceId, tripId, headsign, tripShortName, directionId, blockId, shapeId); + } + + private List getStopPaths(String routeId, int indexStart, int numberOfStops){ + List stopPaths = new ArrayList<>(); + for(int i=indexStart; i < indexStart + numberOfStops; i++){ + stopPaths.add(new StopPath(-1, null, Integer.toString(i), i, false, routeId, + false, false, false, null, null, + null)); + } + return stopPaths; + } + + private Stop getStop(){ + return new Stop(-1, getGtfsStop(), 12345, getTitleFormatter()); + } + + private GtfsStop getGtfsStop(){ + return new GtfsStop("1", 12345, "stop1", 38.272689,-98.964961); + } +} diff --git a/transitclock/src/test/java/org/transitclock/core/predictiongenerator/kalman/TestKalman.java b/transitclock/src/test/java/org/transitclock/core/predictiongenerator/kalman/TestKalman.java new file mode 100644 index 000000000..eee986140 --- /dev/null +++ b/transitclock/src/test/java/org/transitclock/core/predictiongenerator/kalman/TestKalman.java @@ -0,0 +1,245 @@ +package org.transitclock.core.predictiongenerator.kalman; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class TestKalman { + static KalmanPredictionResult result; + @Test + public void test1() { + KalmanPrediction kalmanPrediction=new KalmanPrediction(); + + Vehicle vehicle=new Vehicle("RIY 30"); + + VehicleStopDetail originDetail=new VehicleStopDetail(null, 0, vehicle); + VehicleStopDetail destinationDetail_1_k=new VehicleStopDetail(null, 380, vehicle); + VehicleStopDetail destinationDetail_2_k=new VehicleStopDetail(null, 420, vehicle); + VehicleStopDetail destinationDetail_3_k=new VehicleStopDetail(null, 400, vehicle); + + VehicleStopDetail destinationDetail_0_k_1=new VehicleStopDetail(null, 400, vehicle); + + + TripSegment ts_day_1_k=new TripSegment(originDetail, destinationDetail_1_k); + TripSegment ts_day_2_k=new TripSegment(originDetail, destinationDetail_2_k); + TripSegment ts_day_3_k=new TripSegment(originDetail, destinationDetail_3_k); + + TripSegment ts_day_0_k_1=new TripSegment(originDetail, destinationDetail_0_k_1); + + TripSegment historical_segments_k[]={ts_day_1_k, ts_day_2_k,ts_day_3_k}; + + TripSegment last_vehicle_segment=ts_day_0_k_1; + + try { + result = kalmanPrediction.predict(last_vehicle_segment, historical_segments_k, 0); + + System.out.println(result); + + } catch (Exception e) { + System.out.println("Whoops"); + e.printStackTrace(); + } + + } + + @Test + public void test2() { + KalmanPrediction kalmanPrediction=new KalmanPrediction(); + + Vehicle vehicle=new Vehicle("RIY 30"); + + VehicleStopDetail originDetail=new VehicleStopDetail(null, 0, vehicle); + VehicleStopDetail destinationDetail_1_k=new VehicleStopDetail(null, 420, vehicle); + VehicleStopDetail destinationDetail_2_k=new VehicleStopDetail(null, 400, vehicle); + VehicleStopDetail destinationDetail_3_k=new VehicleStopDetail(null, 380, vehicle); + + VehicleStopDetail destinationDetail_0_k_1=new VehicleStopDetail(null, 400, vehicle); + + + TripSegment ts_day_1_k=new TripSegment(originDetail, destinationDetail_1_k); + TripSegment ts_day_2_k=new TripSegment(originDetail, destinationDetail_2_k); + TripSegment ts_day_3_k=new TripSegment(originDetail, destinationDetail_3_k); + + TripSegment ts_day_0_k_1=new TripSegment(originDetail, destinationDetail_0_k_1); + + TripSegment historical_segments_k[]={ts_day_1_k, ts_day_2_k,ts_day_3_k}; + + TripSegment last_vehicle_segment=ts_day_0_k_1; + + try { + result = kalmanPrediction.predict(last_vehicle_segment, historical_segments_k, result.getFilterError()); + + if(result!=null) + { + System.out.println(result); + + }else + { + System.out.println("No result."); + } + } catch (Exception e) { + System.out.println("Whoops"); + e.printStackTrace(); + } + } + @Test + public void test3() { + KalmanPrediction kalmanPrediction=new KalmanPrediction(); + + Vehicle vehicle=new Vehicle("RIY 30"); + + VehicleStopDetail originDetail=new VehicleStopDetail(null, 0, vehicle); + VehicleStopDetail destinationDetail_1_k=new VehicleStopDetail(null, 400, vehicle); + VehicleStopDetail destinationDetail_2_k=new VehicleStopDetail(null, 380, vehicle); + VehicleStopDetail destinationDetail_3_k=new VehicleStopDetail(null, 420, vehicle); + + VehicleStopDetail destinationDetail_0_k_1=new VehicleStopDetail(null, 400, vehicle); + + + TripSegment ts_day_1_k=new TripSegment(originDetail, destinationDetail_1_k); + TripSegment ts_day_2_k=new TripSegment(originDetail, destinationDetail_2_k); + TripSegment ts_day_3_k=new TripSegment(originDetail, destinationDetail_3_k); + + TripSegment ts_day_0_k_1=new TripSegment(originDetail, destinationDetail_0_k_1); + + TripSegment historical_segments_k[]={ts_day_1_k, ts_day_2_k,ts_day_3_k}; + + TripSegment last_vehicle_segment=ts_day_0_k_1; + + try { + result = kalmanPrediction.predict(last_vehicle_segment, historical_segments_k, result.getFilterError()); + + if(result!=null) + { + System.out.println(result); + + }else + { + System.out.println("No result."); + } + } catch (Exception e) { + System.out.println("Whoops"); + e.printStackTrace(); + } + } + @Test + public void test4() { + KalmanPrediction kalmanPrediction=new KalmanPrediction(); + + Vehicle vehicle=new Vehicle("RIY 30"); + + VehicleStopDetail originDetail=new VehicleStopDetail(null, 0, vehicle); + VehicleStopDetail destinationDetail_1_k=new VehicleStopDetail(null, 380, vehicle); + VehicleStopDetail destinationDetail_2_k=new VehicleStopDetail(null, 420, vehicle); + VehicleStopDetail destinationDetail_3_k=new VehicleStopDetail(null, 400, vehicle); + + VehicleStopDetail destinationDetail_0_k_1=new VehicleStopDetail(null, 400, vehicle); + + + TripSegment ts_day_1_k=new TripSegment(originDetail, destinationDetail_1_k); + TripSegment ts_day_2_k=new TripSegment(originDetail, destinationDetail_2_k); + TripSegment ts_day_3_k=new TripSegment(originDetail, destinationDetail_3_k); + + TripSegment ts_day_0_k_1=new TripSegment(originDetail, destinationDetail_0_k_1); + + TripSegment historical_segments_k[]={ts_day_1_k, ts_day_2_k,ts_day_3_k}; + + TripSegment last_vehicle_segment=ts_day_0_k_1; + + try { + result = kalmanPrediction.predict(last_vehicle_segment, historical_segments_k, result.getFilterError()); + + if(result!=null) + { + System.out.println(result); + + }else + { + System.out.println("No result."); + } + } catch (Exception e) { + System.out.println("Whoops"); + e.printStackTrace(); + } + } + @Test + public void test5() { + KalmanPrediction kalmanPrediction=new KalmanPrediction(); + + Vehicle vehicle=new Vehicle("RIY 30"); + + VehicleStopDetail originDetail=new VehicleStopDetail(null, 0, vehicle); + VehicleStopDetail destinationDetail_1_k=new VehicleStopDetail(null, 420, vehicle); + VehicleStopDetail destinationDetail_2_k=new VehicleStopDetail(null, 400, vehicle); + VehicleStopDetail destinationDetail_3_k=new VehicleStopDetail(null, 380, vehicle); + + VehicleStopDetail destinationDetail_0_k_1=new VehicleStopDetail(null, 300, vehicle); + + + TripSegment ts_day_1_k=new TripSegment(originDetail, destinationDetail_1_k); + TripSegment ts_day_2_k=new TripSegment(originDetail, destinationDetail_2_k); + TripSegment ts_day_3_k=new TripSegment(originDetail, destinationDetail_3_k); + + TripSegment ts_day_0_k_1=new TripSegment(originDetail, destinationDetail_0_k_1); + + TripSegment historical_segments_k[]={ts_day_1_k, ts_day_2_k,ts_day_3_k}; + + TripSegment last_vehicle_segment=ts_day_0_k_1; + + try { + result = kalmanPrediction.predict(last_vehicle_segment, historical_segments_k, result.getFilterError()); + + if(result!=null) + { + System.out.println(result); + + }else + { + System.out.println("No result."); + } + } catch (Exception e) { + System.out.println("Whoops"); + e.printStackTrace(); + } + } + @Test + public void test6() { + KalmanPrediction kalmanPrediction=new KalmanPrediction(); + + Vehicle vehicle=new Vehicle("RIY 30"); + + VehicleStopDetail originDetail=new VehicleStopDetail(null, 0, vehicle); + VehicleStopDetail destinationDetail_1_k=new VehicleStopDetail(null, 400, vehicle); + VehicleStopDetail destinationDetail_2_k=new VehicleStopDetail(null, 380, vehicle); + VehicleStopDetail destinationDetail_3_k=new VehicleStopDetail(null, 420, vehicle); + + VehicleStopDetail destinationDetail_0_k_1=new VehicleStopDetail(null, 500, vehicle); + + + TripSegment ts_day_1_k=new TripSegment(originDetail, destinationDetail_1_k); + TripSegment ts_day_2_k=new TripSegment(originDetail, destinationDetail_2_k); + TripSegment ts_day_3_k=new TripSegment(originDetail, destinationDetail_3_k); + + TripSegment ts_day_0_k_1=new TripSegment(originDetail, destinationDetail_0_k_1); + + TripSegment historical_segments_k[]={ts_day_1_k, ts_day_2_k,ts_day_3_k}; + + TripSegment last_vehicle_segment=ts_day_0_k_1; + + try { + result = kalmanPrediction.predict(last_vehicle_segment, historical_segments_k, result.getFilterError()); + + if(result!=null) + { + System.out.println(result); + + }else + { + System.out.println("No result."); + } + } catch (Exception e) { + System.out.println("Whoops"); + e.printStackTrace(); + } + } +} diff --git a/transitclock/src/test/java/org/transitclock/core/predictiongenerator/kalman/dwell/RLSCache.java b/transitclock/src/test/java/org/transitclock/core/predictiongenerator/kalman/dwell/RLSCache.java new file mode 100644 index 000000000..9abd6536d --- /dev/null +++ b/transitclock/src/test/java/org/transitclock/core/predictiongenerator/kalman/dwell/RLSCache.java @@ -0,0 +1,69 @@ +package org.transitclock.core.predictiongenerator.kalman.dwell; + +import static org.junit.Assert.*; + +import org.apache.commons.jcs.JCS; +import org.apache.commons.jcs.access.CacheAccess; +import org.junit.Test; + +import junit.framework.TestCase; +import smile.regression.RLS; + +public class RLSCache extends TestCase{ + @Override + protected void setUp() throws Exception { + super.setUp(); + cache = JCS.getInstance(cacheName); + } + final private static String cacheName = "TestDwellTimeModelCache"; + + private CacheAccess cache = null; + @Test + public void test() { + + try { + RLS rls = null; + String key="1"; + if(cache.get(key)!=null) + { + rls=cache.get(key); + }else + { + double samplex[][]=new double[4][1]; + double sampley[]=new double[4]; + + samplex[0][0]=0; + samplex[1][0]=2; + samplex[2][0]=3; + samplex[3][0]=4; + + sampley[0]=0; + sampley[1]=2; + sampley[2]=3; + sampley[3]=4; + + rls=new RLS(samplex, sampley); + } + double predict1[]={5}; + double result1 = rls.predict(predict1); + cache.put(key,rls); + + rls=cache.get(key); + + double predict2[]={5}; + double result2 = rls.predict(predict2); + + // check if RLS works same when read back from cache. + if(result1!=result2) + { + fail(); + } + + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + +} diff --git a/transitclock/src/test/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/GtfsTestData.java b/transitclock/src/test/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/GtfsTestData.java new file mode 100644 index 000000000..c660bcb18 --- /dev/null +++ b/transitclock/src/test/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/GtfsTestData.java @@ -0,0 +1,50 @@ +/* + * 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.predictiongenerator.scheduled.traveltime.kalman; + +import org.transitclock.db.structs.Stop; +import org.transitclock.gtfs.GtfsData; +import org.transitclock.gtfs.TitleFormatter; +import org.transitclock.gtfs.gtfsStructs.GtfsRoute; + +import java.util.Date; + +/** + * Testing version of GtfsData that allows some hooks for test data. + */ +public class GtfsTestData extends GtfsData { + public KalmanDataGenerator dataGenerator; + private long referenceTime; + + public GtfsTestData(long referenceTime, int configRev, String notes, Date zipFileLastModifiedTime, boolean shouldStoreNewRevs, boolean shouldDeleteRevs, String projectId, String gtfsDirectoryName, String supplementDir, double pathOffsetDistance, double maxStopToPathDistance, double maxDistanceForEliminatingVertices, int defaultWaitTimeAtStopMsec, double maxSpeedKph, double maxTravelTimeSegmentLength, boolean trimPathBeforeFirstStopOfTrip, TitleFormatter titleFormatter) { + super(configRev, notes, zipFileLastModifiedTime, shouldStoreNewRevs, shouldDeleteRevs, projectId, gtfsDirectoryName, supplementDir, pathOffsetDistance, maxStopToPathDistance, maxDistanceForEliminatingVertices, defaultWaitTimeAtStopMsec, maxSpeedKph, maxTravelTimeSegmentLength, trimPathBeforeFirstStopOfTrip, titleFormatter); + this.referenceTime = referenceTime; + this.dataGenerator = new KalmanDataGenerator(referenceTime); + } + + + + public GtfsRoute getGtfsRoute(String routeId) { + return dataGenerator.getGtfsRoute(); + } + + public Stop getStop(String stopId) { + Stop stop = dataGenerator.getStop(); + return stop; + } + +} diff --git a/transitclock/src/test/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/KalmanDataGenerator.java b/transitclock/src/test/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/KalmanDataGenerator.java new file mode 100644 index 000000000..33fc84ed5 --- /dev/null +++ b/transitclock/src/test/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/KalmanDataGenerator.java @@ -0,0 +1,364 @@ +/* + * 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.predictiongenerator.scheduled.traveltime.kalman; + +import org.transitclock.core.Indices; +import org.transitclock.core.SpatialMatch; +import org.transitclock.core.TemporalDifference; +import org.transitclock.core.TemporalMatch; +import org.transitclock.core.TravelTimeDetails; +import org.transitclock.core.VehicleState; +import org.transitclock.core.dataCache.KalmanError; +import org.transitclock.db.structs.Arrival; +import org.transitclock.db.structs.AvlReport; +import org.transitclock.db.structs.Block; +import org.transitclock.db.structs.Departure; +import org.transitclock.db.structs.Location; +import org.transitclock.db.structs.Route; +import org.transitclock.db.structs.ScheduleTime; +import org.transitclock.db.structs.Stop; +import org.transitclock.db.structs.StopPath; +import org.transitclock.db.structs.TravelTimesForStopPath; +import org.transitclock.db.structs.TravelTimesForTrip; +import org.transitclock.db.structs.Trip; +import org.transitclock.db.structs.TripPattern; +import org.transitclock.gtfs.GtfsData; +import org.transitclock.gtfs.TitleFormatter; +import org.transitclock.gtfs.gtfsStructs.GtfsRoute; +import org.transitclock.gtfs.gtfsStructs.GtfsStop; +import org.transitclock.gtfs.gtfsStructs.GtfsTrip; +import org.transitclock.ipc.data.IpcArrivalDeparture; +import org.transitclock.utils.Time; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * Generate test data for Kalman prediction generation. + */ +public class KalmanDataGenerator { + + public static final String VEHICLE = "1234"; + public static final int CONFIG_REV = -1; + public static final int TRAVEL_TIMES_REV = -2; + public static final String AGENCY_ID = "a1"; + public static final String BLOCK_ID = "block1"; + public static final String ROUTE_ID = "route1"; + public static final String SERVICE_ID = "winter"; + public static final String TRIP_ID = "t1"; + public static final String TRIP_HEADSIGN = "h1"; + public static final String DIRECTION_ID = "0"; + public static final String SHAPE_ID = "shape1"; + public static final String PATH_ID = "path1"; + public static final String STOP_ID = "stop1"; + public static final Integer STOP_CODE = 1; + public static final String STOP_NAME = "StOp1"; + public static final String PROJECT_ID = "project1"; + public static final String STOP_PATH_ID = "sp1"; + private static final String TRIP_PATTERN_ID = "tp1"; + private static final int DEFAULT_TRAVEL_TIMES = 300; + private Long avlTime = null; + + public KalmanDataGenerator(long referenceTime) { + avlTime = referenceTime; + } + + + public long getAvlTime() { + if (avlTime == null || avlTime == 0) + avlTime = new Date().getTime(); + return avlTime; + } + + public VehicleState getVehicleState() { + VehicleState vs = new VehicleState(VEHICLE); + SpatialMatch spatialMatch = new SpatialMatch(getAvlTime(), + getBlock(), + 0, 0, 0, + 0.0, 0.0); + TemporalMatch match = new TemporalMatch(spatialMatch, new TemporalDifference(300)); + vs.setMatch(match); + + return vs; + } + + public AvlReport getAvlReport() { + AvlReport avlReport = new AvlReport(VEHICLE, + getAvlTime(), 0.0, 0.0, + 0.0f, 0.0f, "source"); + return avlReport; + } + + public Indices getIndicies() { + Block block = getBlock(); + int tripIndex = 0; + int stopPathIndex = 0; + int segmentIndex = 0; + Indices index = new Indices(block, tripIndex, stopPathIndex, + segmentIndex); + return index; + } + + public Block getBlock() { + int configRev = CONFIG_REV; + String blockId = BLOCK_ID; + String serviceId = SERVICE_ID; + int startTime = 0; + int endTime = 1; + + return new Block(configRev, blockId, serviceId, + startTime, endTime, getTrips()); + } + + public List getTrips() { + List trips = new ArrayList<>(); + + Trip t = getTrip(); + trips.add(t); + return trips; + } + + private Trip getTrip() { + Trip t = new Trip(CONFIG_REV, getGtfsTrip(), ROUTE_ID, + ROUTE_ID, TRIP_HEADSIGN, + new TitleFormatter("", false)); + TripPattern pattern = getTripPattern(t); + Route route = getRoute(t); + t.setRoute(route); + t.setTripPattern(pattern); + t.setTravelTimes(getTravelTimes(t)); + t.getStopPaths().add(getStopPaths().get(0)); + ScheduleTime scheduleTime = new ScheduleTime(getScheduleTime(getArrivalTime().getTime()), + getScheduleTime(getDepartureTime().getTime())); + ArrayList times = new ArrayList<>(); + times.add(scheduleTime); + t.addScheduleTimes(times); + + return t; + } + + private int getScheduleTime(long time) { + return new Time(getTimeZone()).getSecondsIntoDay(time); + } + + public String getTimeZone() { + return "America/New_York"; + } + + public GtfsRoute getGtfsRoute() { + return new GtfsRoute( + ROUTE_ID, + AGENCY_ID, + ROUTE_ID, + ROUTE_ID, + "3", + "black", + "white" + ); + } + + public GtfsStop getGtfsStop() { + return new GtfsStop(STOP_ID, STOP_CODE, STOP_NAME, + 0.0, 0.0); + } + + public Stop getStop() { + return new Stop(CONFIG_REV, getGtfsStop(), null, + new TitleFormatter("", false)); + } + + public Route getRoute(Trip trip) { + GtfsRoute gtfsRoute = getGtfsRoute(); + List patterns = new ArrayList<>(); + patterns.add(getTripPattern(trip)); + Route route = new Route( + CONFIG_REV, + gtfsRoute, + patterns, + new TitleFormatter("", false) ); + ArrayList directionIds = new ArrayList<>(); + directionIds.add("0"); + route.setDirectionIds(directionIds); + route.setTripPatterns(patterns); + return route; + } + + private TripPattern getTripPattern(Trip t) { + return new TripPattern(CONFIG_REV, SHAPE_ID, getStopPaths(), + t, getGtfsData()); + + } + + public TravelTimesForTrip getTravelTimes(Trip trip) { + return getTravelTimes(trip, DEFAULT_TRAVEL_TIMES); + } + + public TravelTimesForTrip getTravelTimes(Trip trip, int travelTimes) { + TravelTimesForTrip ttt = new TravelTimesForTrip( + CONFIG_REV, + TRAVEL_TIMES_REV, + trip + ); + List travelTimesMsec = new ArrayList<>(); + travelTimesMsec.add(travelTimes); + double travelTimeSegmentDistance = 200; + TravelTimesForStopPath ttsp = new TravelTimesForStopPath( + CONFIG_REV, + TRAVEL_TIMES_REV, + STOP_PATH_ID, + travelTimeSegmentDistance, + travelTimesMsec, + 0, + 0, + TravelTimesForStopPath.HowSet.TRIP, + trip + ); + ttt.add(ttsp); + return ttt; + } + + public GtfsData getGtfsData() { + GtfsData gd = new GtfsTestData( + getAvlTime(), + CONFIG_REV, + null, + new Date(), + false, + false, + PROJECT_ID, + "gtfs", + null, + 50., + 200., + 200., + 10, + 100, + 200, + false, + new TitleFormatter("", false)); + return gd; + } + + public List getStopPaths() { + List paths = new ArrayList<>(); + StopPath path = new StopPath( + CONFIG_REV, + PATH_ID, + STOP_ID, + 1, + false, + ROUTE_ID, + false, + false, + false, + null, + null, + null); + path.setTripPatternId(TRIP_PATTERN_ID); + path.setLocations(getLocations()); + path.onLoad(null, null); + paths.add(path); + return paths; + } + + public ArrayList getLocations() { + ArrayList locations = new ArrayList<>(); + Location l1 = new Location(38.831741, -77.116982); + locations.add(l1); + Location l2 = new Location(38.833675, -77.115494); + locations.add(l2); + return locations; + } + + public GtfsTrip getGtfsTrip() { + return new GtfsTrip(ROUTE_ID, SERVICE_ID, TRIP_ID, + TRIP_HEADSIGN, TRIP_ID, DIRECTION_ID, + BLOCK_ID, SHAPE_ID); + } + + public Date getDepartureTime() { + return new Date(getAvlTime() + 1001); + } + + // sample data of 300 + public Date getArrivalTime() { + return new Date(getAvlTime() + 1001 + 300); + } + + // sample data of 380,420,400 + private int[] travelTimes = {380, 420, 400}; + public List getLastDaysTimes() { + List lastDays = new ArrayList<>(); + long dayTime = getAvlTime(); + // we can only generate 3 as max kalman days defaults to 3 + for (int i = 0; i < 3; i++) { + dayTime = dayTime - Time.MS_PER_DAY; + TravelTimeDetails ttd = null; + try { + ttd = new TravelTimeDetails( + new IpcArrivalDeparture(getDeparture(new Date(dayTime), + new Date(dayTime), + getBlock())), + new IpcArrivalDeparture(getArrival(new Date(dayTime + travelTimes[i]), + new Date(dayTime), + getBlock()))); + } catch (Exception e) { + e.printStackTrace(); + } + lastDays.add(ttd); + } + + return lastDays; + } + + public Departure getDeparture(Date departureTime, + Date avlTime, + Block block) { + Departure d = new Departure( + KalmanDataGenerator.CONFIG_REV, + KalmanDataGenerator.VEHICLE, + departureTime, + avlTime, + block, + 0, + 0, + null, + 0l, + KalmanDataGenerator.STOP_PATH_ID); + return d; + } + + public Arrival getArrival(Date arrivalTime, + Date avlTime, + Block block) { + Arrival a = new Arrival(KalmanDataGenerator.CONFIG_REV, + KalmanDataGenerator.VEHICLE, + arrivalTime, + avlTime, + block, + 0, + 0, + null, + KalmanDataGenerator.STOP_PATH_ID); + return a; + } + + public KalmanError getErrorValue(Indices indices) { + return new KalmanError(72.40); + } +} diff --git a/transitclock/src/test/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/KalmanErrorTestCache.java b/transitclock/src/test/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/KalmanErrorTestCache.java new file mode 100644 index 000000000..8535cbdf7 --- /dev/null +++ b/transitclock/src/test/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/KalmanErrorTestCache.java @@ -0,0 +1,68 @@ +/* + * 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.predictiongenerator.scheduled.traveltime.kalman; + +import org.transitclock.core.Indices; +import org.transitclock.core.dataCache.ErrorCache; +import org.transitclock.core.dataCache.KalmanError; +import org.transitclock.core.dataCache.KalmanErrorCacheKey; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Testing implementation of KalmanErrorCache that integrates + * with the testing data generator. + */ +public class KalmanErrorTestCache implements ErrorCache { + + private KalmanDataGenerator test = null; + public KalmanErrorTestCache(KalmanDataGenerator test) { + this.test = test; + } + + private Map map = new HashMap<>(); + + @Override + public KalmanError getErrorValue(Indices indices) { + KalmanError error = map.get(indices); + if (error == null) + return test.getErrorValue(indices); + return error; + } + + @Override + public KalmanError getErrorValue(KalmanErrorCacheKey key) { + throw new UnsupportedOperationException("getErrorValue(KalmanErrorCacheKey key)"); + } + + @Override + public void putErrorValue(Indices indices, Double value) { + map.put(indices, new KalmanError(value)); + } + + @Override + public void putErrorValue(KalmanErrorCacheKey key, Double value) { + throw new UnsupportedOperationException("putErrorValue(KalmanErrorCacheKey key, Double value)"); + } + + @Override + public List getKeys() { + throw new UnsupportedOperationException("getKeys()"); + } +} diff --git a/transitclock/src/test/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/KalmanPredictionGeneratorImplTest.java b/transitclock/src/test/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/KalmanPredictionGeneratorImplTest.java new file mode 100644 index 000000000..3c80a06db --- /dev/null +++ b/transitclock/src/test/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/KalmanPredictionGeneratorImplTest.java @@ -0,0 +1,66 @@ +/* + * 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.predictiongenerator.scheduled.traveltime.kalman; + +import org.junit.Before; +import org.junit.Test; +import org.transitclock.applications.Core; +import org.transitclock.utils.Time; + +import java.util.Date; + +import static org.junit.Assert.*; + +/** + * Test the scheduled implementation of the kalman prediction generator. + */ +public class KalmanPredictionGeneratorImplTest { + + + private KalmanPredictionGeneratorImpl generator; + private KalmanDataGenerator dataGenerator; + + @Before + public void setup() { + long referenceTime = new Date().getTime(); + dataGenerator = new KalmanDataGenerator(referenceTime); + generator = new KalmanPredictionGeneratorTestImpl(); + + /** + * time needs to be populated for ArrivalDeparture creation + */ + if (!Core.isCoreApplication()) { + Core.createTestCore(dataGenerator.AGENCY_ID); + if (Core.getInstance().getTime() == null) { + Core.getInstance().setTime(new Time(dataGenerator.getTimeZone())); + } + } + } + + @Test + public void getTravelTimeForPath() { + + long prediction = generator.getTravelTimeForPath( + dataGenerator.getIndicies(), + dataGenerator.getAvlReport(), + dataGenerator.getVehicleState()); + + assertEquals(355, prediction); + + } + +} \ No newline at end of file diff --git a/transitclock/src/test/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/KalmanPredictionGeneratorTestImpl.java b/transitclock/src/test/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/KalmanPredictionGeneratorTestImpl.java new file mode 100644 index 000000000..d9654f222 --- /dev/null +++ b/transitclock/src/test/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/KalmanPredictionGeneratorTestImpl.java @@ -0,0 +1,112 @@ +/* + * 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.predictiongenerator.scheduled.traveltime.kalman; + +import org.transitclock.applications.Core; +import org.transitclock.core.Indices; +import org.transitclock.core.TravelTimeDetails; +import org.transitclock.core.VehicleState; +import org.transitclock.core.dataCache.ErrorCache; +import org.transitclock.core.dataCache.TripDataHistoryCacheInterface; +import org.transitclock.core.dataCache.VehicleStateManager; +import org.transitclock.db.structs.Arrival; +import org.transitclock.db.structs.ArrivalDeparture; +import org.transitclock.db.structs.AvlReport; +import org.transitclock.ipc.data.IpcArrivalDeparture; + +import java.util.Date; +import java.util.List; + +/** + * This is a supporting class to KalmanPredictionGeneratorImplTest + * It is an extension of KalmanPredictionGeneratorImpl that overrides some of the + * otherwise static references / object lookups. + */ +public class KalmanPredictionGeneratorTestImpl extends KalmanPredictionGeneratorImpl { + + private KalmanDataGenerator test; + private TripDataHistoryTestCache tripDataHistoryCache = new TripDataHistoryTestCache(); + private KalmanErrorTestCache kalmanErrorCache; + private long referenceTime; + + public void setReferenceTime(long time) { + referenceTime = time; + } + + @Override + protected VehicleStateManager getVehicleStateManager() { + return super.getVehicleStateManager(); + } + + @Override + protected TripDataHistoryCacheInterface getTripCache() { + return tripDataHistoryCache; + } + + @Override + protected ErrorCache getKalmanErrorCache() { + if (kalmanErrorCache == null) { + kalmanErrorCache = new KalmanErrorTestCache(test); + } + return kalmanErrorCache; + } + + @Override + protected TravelTimeDetails getLastVehicleTravelTime(VehicleState currentVehicleState, Indices indices) throws Exception { + KalmanDataGenerator test = getKalmanDataGenerator(); + ArrivalDeparture d = test.getDeparture(test.getDepartureTime(), + new Date(test.getAvlTime()), + test.getBlock()); + IpcArrivalDeparture departure = new IpcArrivalDeparture(d); + ArrivalDeparture a = new Arrival(KalmanDataGenerator.CONFIG_REV, + KalmanDataGenerator.VEHICLE, + test.getArrivalTime(), + new Date(test.getAvlTime()), + test.getBlock(), + 0, + 0, + null, + KalmanDataGenerator.STOP_PATH_ID); + IpcArrivalDeparture arrival = new IpcArrivalDeparture(a); + TravelTimeDetails ttd = new TravelTimeDetails(departure, arrival); + Core.getInstance().getDbConfig().addBlockToServiceMap(KalmanDataGenerator.SERVICE_ID, KalmanDataGenerator.BLOCK_ID, test.getBlock()); + return ttd; + } + + + private KalmanDataGenerator getKalmanDataGenerator() { + if (test == null) { + test = new KalmanDataGenerator(referenceTime); + } + return test; + } + + private ErrorCache getTestKalmanErrorCache() { + if (kalmanErrorCache == null) { + kalmanErrorCache = new KalmanErrorTestCache(test); + } + return kalmanErrorCache; + } + + @Override + protected List getHistoricalTravelTimes(AvlReport avlReport, + Indices indices, + VehicleState currentVehicleState) { + KalmanDataGenerator test = new KalmanDataGenerator(referenceTime); + return test.getLastDaysTimes(); + } +} diff --git a/transitclock/src/test/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/TrafficDataCacheTest.java b/transitclock/src/test/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/TrafficDataCacheTest.java new file mode 100644 index 000000000..e49198ad4 --- /dev/null +++ b/transitclock/src/test/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/TrafficDataCacheTest.java @@ -0,0 +1,67 @@ +/* + * 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.predictiongenerator.scheduled.traveltime.kalman; + +import org.junit.Before; +import org.junit.Test; +import org.transitclock.db.structs.TrafficSensorData; +import org.transitclock.utils.Time; + +import java.util.Date; + +import static org.junit.Assert.*; + +public class TrafficDataCacheTest { + + TrafficDataCache cache; + + @Before + public void setUp() throws Exception { + cache = new TrafficDataCache(null, null, -1); + } + + @Test + public void isLatent() { + long now = System.currentTimeMillis(); + long old = System.currentTimeMillis() - (10 * Time.MS_PER_MIN); + TrafficSensorData oldElement + = new TrafficSensorData( + "1", + -1, + new Date(old), + null, + null, + null, + null, + null); + assertTrue(cache.isLatent(oldElement)); + + TrafficSensorData newElement + = new TrafficSensorData( + "1", + -1, + new Date(now), + null, + null, + null, + null, + null); + + assertFalse(cache.isLatent(newElement)); + + } +} \ No newline at end of file diff --git a/transitclock/src/test/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/TrafficDataKeyTest.java b/transitclock/src/test/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/TrafficDataKeyTest.java new file mode 100644 index 000000000..f383d06ce --- /dev/null +++ b/transitclock/src/test/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/TrafficDataKeyTest.java @@ -0,0 +1,41 @@ +/* + * 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.predictiongenerator.scheduled.traveltime.kalman; + +import org.junit.Test; +import org.transitclock.utils.Time; + +import java.text.ParseException; +import java.util.Date; + +import static org.junit.Assert.*; + +public class TrafficDataKeyTest { + + @Test + public void shave() throws ParseException { + long time = parseStr("2021-01-02 12:34:56.789"); + TrafficDataKey key = new TrafficDataKey(null, time); + assertEquals(parseStr("2021-01-02 12:34:00.000"), key.getTime()); + + } + + private Long parseStr(String s) throws ParseException { + Date parse = Time.parse(s); + return parse.getTime(); + } +} \ No newline at end of file diff --git a/transitclock/src/test/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/TrafficManagerTest.java b/transitclock/src/test/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/TrafficManagerTest.java new file mode 100644 index 000000000..fc2363182 --- /dev/null +++ b/transitclock/src/test/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/TrafficManagerTest.java @@ -0,0 +1,139 @@ +/* + * 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.predictiongenerator.scheduled.traveltime.kalman; + +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.transitclock.db.structs.Location; +import org.transitclock.db.structs.StopPath; +import org.transitclock.db.structs.TrafficPath; +import org.transitclock.db.structs.TrafficSensorData; +import org.transitclock.utils.Time; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.*; + +public class TrafficManagerTest { + + private TrafficManager tm; + private TrafficDataCache cache; + private TrafficDataMapper mapper; + + @Before + public void setUp() throws Exception { + int trafficRev = 0; + tm = new TrafficManager(); + tm.setTrafficRev(trafficRev); + mapper = new TrafficDataMapper(); + tm.setMapper(mapper); + TrafficDataHistoricalCache historicalCache = new TrafficDataHistoricalCache(mapper, trafficRev); + cache = new TrafficDataCache(historicalCache, mapper, trafficRev); + tm.setCache(cache); + tm.setHistoricalCache(historicalCache); + tm.setEnabled(true); + cache.setArchiving(false); + } + + @Test + @Ignore // run this only if integrating with traffic data + public void loadData() throws Exception { + List sensorData = cache.loadData(); + assertNotNull(sensorData); + assertTrue(sensorData.size() > 40); + TrafficSensorData data1 = findSensorId(sensorData, "938"); + assertEquals("938", data1.getTrafficSensorId()); + assertTrue(data1.getTime().getTime() > System.currentTimeMillis() - Time.MS_PER_MIN); + } + + @Test + @Ignore + public void getTravelTime() throws Exception { + Long travelTime = tm.getTravelTime(null); + assertNull(travelTime); + + StopPath sp = createStopPath(); + TrafficPath tp = createTrafficPath(); + travelTime = tm.getTravelTime(sp); + assertNull(travelTime); + + mapper.mapStopPath(sp, tp); + mapper.addTrafficPath(tp); + travelTime = tm.getTravelTime(sp); + assertNull(travelTime); + + List sensorData = cache.loadData(); + cache.updateCache(sensorData); + cache.archive(sensorData); + assertNotNull(cache.get(tp)); + long now = System.currentTimeMillis(); + travelTime = tm.getTravelTime(sp); + assertNotNull(travelTime); + + Long historicalTravelTime = tm.getHistoricalTravelTime(sp, now); + assertNotNull(historicalTravelTime); + } + + private TrafficPath createTrafficPath() { + String trafficPathId = "1522"; + int trafficRev = 0; + float pathLength = 1000; + return new TrafficPath(trafficPathId, + trafficRev, + pathLength); + } + + private StopPath createStopPath() { + String pathId = "p1"; + String stopId = "s1"; + String routeId = "r1"; + StopPath sp = new StopPath(-1, + pathId, + stopId, + 0, + false, + routeId, + false, + false, + false, + null, + null, + null); + sp.setLocations(createLocations()); + + assertTrue(sp.getLength() > 1.0); + return sp; + } + + private ArrayList createLocations() { + ArrayList list = new ArrayList<>(); + list.add(new Location(38.8141,-77.1339)); + list.add(new Location(38.8123,-77.1217)); + return list; + } + + private TrafficSensorData findSensorId(List sensorData, String id) { + for (TrafficSensorData element : sensorData) { + if (element.getTrafficSensorId().equals(id)) + return element; + } + return null; + } + +} \ No newline at end of file diff --git a/transitclock/src/test/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/TripDataHistoryTestCache.java b/transitclock/src/test/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/TripDataHistoryTestCache.java new file mode 100644 index 000000000..0364640e9 --- /dev/null +++ b/transitclock/src/test/java/org/transitclock/core/predictiongenerator/scheduled/traveltime/kalman/TripDataHistoryTestCache.java @@ -0,0 +1,61 @@ +/* + * 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.predictiongenerator.scheduled.traveltime.kalman; + +import org.hibernate.Session; +import org.transitclock.core.dataCache.TripDataHistoryCacheInterface; +import org.transitclock.core.dataCache.TripKey; +import org.transitclock.db.structs.ArrivalDeparture; +import org.transitclock.ipc.data.IpcArrivalDeparture; + +import java.util.Date; +import java.util.List; + +/** + * Test implementation of TripDataHistoryCache that does nothing. + */ +public class TripDataHistoryTestCache implements TripDataHistoryCacheInterface { + @Override + public List getTripHistory(TripKey tripKey) { + return null; + } + + @Override + public TripKey putArrivalDeparture(ArrivalDeparture arrivalDeparture) { + return null; + } + + @Override + public void populateCacheFromDb(List results) { + + } + + @Override + public IpcArrivalDeparture findPreviousArrivalEvent(List arrivalDepartures, IpcArrivalDeparture current) { + return null; + } + + @Override + public IpcArrivalDeparture findPreviousDepartureEvent(List arrivalDepartures, IpcArrivalDeparture current) { + return null; + } + + @Override + public List getKeys() { + return null; + } +} diff --git a/transitclock/src/test/java/org/transitclock/predAccuracy/gtfsrt/GTFSRealtimePredictionsAccuracyModuleTest.java b/transitclock/src/test/java/org/transitclock/predAccuracy/gtfsrt/GTFSRealtimePredictionsAccuracyModuleTest.java new file mode 100644 index 000000000..99c1674c8 --- /dev/null +++ b/transitclock/src/test/java/org/transitclock/predAccuracy/gtfsrt/GTFSRealtimePredictionsAccuracyModuleTest.java @@ -0,0 +1,621 @@ +package org.transitclock.predAccuracy.gtfsrt; + +import com.google.transit.realtime.GtfsRealtime; +import org.junit.Before; +import org.junit.Test; +import org.mockito.*; +import org.transitclock.core.predAccuracy.PredAccuracyPrediction; +import org.transitclock.core.predAccuracy.gtfsrt.GTFSRealtimePredictionAccuracyModule; +import org.transitclock.db.structs.*; +import org.transitclock.gtfs.DbConfig; + +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; +import java.util.TimeZone; + +import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.*; + +public class GTFSRealtimePredictionsAccuracyModuleTest { + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private DbConfig dbConfig; + + @Captor + ArgumentCaptor arrivalPredictionCaptor; + + @Captor + ArgumentCaptor departurePredictionCaptor; + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private Trip gtfsTrip; + + @Spy + private GTFSRealtimePredictionAccuracyModule module = new GTFSRealtimePredictionAccuracyModule("1"); + + @Before + public void setup(){ + TimeZone.setDefault(TimeZone.getTimeZone("America/Chicago")); + MockitoAnnotations.initMocks(this); + when(dbConfig.getFirstAgency().getTimeZone()).thenReturn(getTimeZone()); + } + + private TimeZone getTimeZone(){ + return TimeZone.getTimeZone("America/Chicago"); + } + + @Test + public void feedMessageTest(){ + List stopTimes = getStopTimesOne(); + TripUpdateTestClass tripUpdate = getTripUpdateOne(120, stopTimes); + List entities = getEntities(tripUpdate); + + assertEquals(1, entities.size()); + + for (int i = 0; i < entities.size(); i++) { + GtfsRealtime.FeedEntity fe = entities.get(i); + assertFalse(fe.hasAlert()); + assertTrue(fe.hasTripUpdate()); + assertFalse(fe.hasVehicle()); + assertEquals(Integer.toString(i), fe.getId()); + } + + // (GMT) Saturday, April 10, 2021 5:54:12 AM + long feedMessageRequestTime = 1618034052000l; + long feedMessageRequestTimeSec = feedMessageRequestTime / 1000; + + GtfsRealtime.FeedMessage feedMessage = getFeedMessage(feedMessageRequestTime, entities); + assertEquals(entities.size(), feedMessage.getEntityCount()); + assertTrue(feedMessage.hasHeader()); + assertEquals(feedMessageRequestTimeSec, feedMessage.getHeader().getTimestamp()); + + } + + /** + * Test stop time predictions and trip update delay + */ + @Test + public void testTripUpdatesWithDelay(){ + // (GMT) Saturday, April 10, 2021 5:54:12 AM + long feedMessageRequestTime = 1618034052000l; + + // Mock StopPaths + List stopPathTestClasses = new ArrayList<>(); + StopPathTestClass stopPathOne = new StopPathTestClass("0", 0); + stopPathTestClasses.add(stopPathOne); + StopPathTestClass stopPathTwo = new StopPathTestClass("1", 1); + stopPathTestClasses.add(stopPathTwo); + StopPathTestClass stopPathThree = new StopPathTestClass("2", 2); + stopPathTestClasses.add(stopPathThree); + List stopPaths = getStopPaths(stopPathTestClasses); + + // Mock ScheduleTime + ScheduleTime scheduleTimeOne = new ScheduleTime(3115, 3155); + when(gtfsTrip.getScheduleTime(0)).thenReturn(scheduleTimeOne); + + ScheduleTime scheduleTimeTwo = new ScheduleTime(3255, 3270); + when(gtfsTrip.getScheduleTime(1)).thenReturn(scheduleTimeTwo); + + ScheduleTime scheduleTimeThree = new ScheduleTime(3375, 3390); + when(gtfsTrip.getScheduleTime(2)).thenReturn(scheduleTimeThree); + + // Mock Trip + when(gtfsTrip.getDirectionId()).thenReturn("0"); + when(gtfsTrip.getStopPaths()).thenReturn(stopPaths); + when(gtfsTrip.getStopPath(anyInt())).then(i -> stopPaths.get(i.getArgument(0))); + when(dbConfig.getTrip(anyString())).thenReturn(gtfsTrip); + doNothing().when(module).storeArrivalPrediction(arrivalPredictionCaptor.capture()); + doNothing().when(module).storeDeparturePrediction(departurePredictionCaptor.capture()); + + // Setup Feed Message + List stopTimes = getStopTimesOne(); + TripUpdateTestClass tripUpdate = getTripUpdateOne(120, stopTimes); + List entities = getEntities(tripUpdate); + GtfsRealtime.FeedMessage feedMessage = getFeedMessage(feedMessageRequestTime, entities); + + module.processExternalPredictions(feedMessage, dbConfig); + List arrivalPredictions = arrivalPredictionCaptor.getAllValues(); + List departurePredictions = departurePredictionCaptor.getAllValues(); + + assertEquals(2, arrivalPredictions.size()); + assertEquals(2, departurePredictions.size()); + + // Check Stop Time Update Arrival and Departure Times + long expectedArrivalTime = 1618034175000l; + long expectedDepartureTime = 1618034190000l; + + assertEquals(expectedArrivalTime, arrivalPredictions.get(0).getPredictedTime().getTime()); + assertEquals(expectedDepartureTime, departurePredictions.get(0).getPredictedTime().getTime()); + + // Check Scheduled Arrival and Departure Times With Trip Delay + expectedArrivalTime = 1618034295000l; + expectedDepartureTime = 1618034310000l; + assertEquals(expectedArrivalTime, arrivalPredictions.get(1).getPredictedTime().getTime()); + assertEquals(expectedDepartureTime, departurePredictions.get(1).getPredictedTime().getTime()); + + } + + /** + * Test StopTimes with No Delay + */ + @Test + public void testStopTimeWithNoDelays(){ + // (GMT) Saturday, April 10, 2021 5:54:12 AM + long feedMessageRequestTime = 1618034052000l; + + // Mock StopPaths + List stopPathTestClasses = new ArrayList<>(); + StopPathTestClass stopPathOne = new StopPathTestClass("0", 0); + stopPathTestClasses.add(stopPathOne); + StopPathTestClass stopPathTwo = new StopPathTestClass("1", 1); + stopPathTestClasses.add(stopPathTwo); + StopPathTestClass stopPathThree = new StopPathTestClass("2", 2); + stopPathTestClasses.add(stopPathThree); + List stopPaths = getStopPaths(stopPathTestClasses); + + // Mock ScheduleTime + ScheduleTime scheduleTimeOne = new ScheduleTime(3115, 3155); + when(gtfsTrip.getScheduleTime(0)).thenReturn(scheduleTimeOne); + + ScheduleTime scheduleTimeTwo = new ScheduleTime(3255, 3270); + when(gtfsTrip.getScheduleTime(1)).thenReturn(scheduleTimeTwo); + + ScheduleTime scheduleTimeThree = new ScheduleTime(3375, 3390); + when(gtfsTrip.getScheduleTime(2)).thenReturn(scheduleTimeThree); + + // Mock Trip + when(gtfsTrip.getDirectionId()).thenReturn("0"); + when(gtfsTrip.getStopPaths()).thenReturn(stopPaths); + when(gtfsTrip.getStopPath(anyInt())).then(i -> stopPaths.get(i.getArgument(0))); + when(dbConfig.getTrip(anyString())).thenReturn(gtfsTrip); + doNothing().when(module).storeArrivalPrediction(arrivalPredictionCaptor.capture()); + doNothing().when(module).storeDeparturePrediction(departurePredictionCaptor.capture()); + + // Setup Feed Message + List stopTimes = getStopTimesOne(); + TripUpdateTestClass tripUpdate = getTripUpdateOne(null, stopTimes); + List entities = getEntities(tripUpdate); + GtfsRealtime.FeedMessage feedMessage = getFeedMessage(feedMessageRequestTime, entities); + + module.processExternalPredictions(feedMessage, dbConfig); + List arrivalPredictions = arrivalPredictionCaptor.getAllValues(); + List departurePredictions = departurePredictionCaptor.getAllValues(); + + assertEquals(2, arrivalPredictions.size()); + assertEquals(2, departurePredictions.size()); + + // Check Stop Time Update Arrival and Departure Times + long expectedArrivalTime = 1618034175000l; + long expectedDepartureTime = 1618034190000l; + + assertEquals(expectedArrivalTime, arrivalPredictions.get(0).getPredictedTime().getTime()); + assertEquals(expectedDepartureTime, departurePredictions.get(0).getPredictedTime().getTime()); + + // Check Scheduled Arrival and Departure Times With No Trip Delay or Stop Time Delay + expectedArrivalTime = 1618034295000l; + expectedDepartureTime = 1618034310000l; + assertEquals(expectedArrivalTime, arrivalPredictions.get(1).getPredictedTime().getTime()); + assertEquals(expectedDepartureTime, departurePredictions.get(1).getPredictedTime().getTime()); + } + + @Test + public void testStopTimeWithMultipleStopTimes(){ + // (GMT) Saturday, April 10, 2021 5:54:12 AM + long feedMessageRequestTime = 1618034052000l; + + // Mock StopPaths + List stopPathTestClasses = new ArrayList<>(); + StopPathTestClass stopPathOne = new StopPathTestClass("0", 0); + stopPathTestClasses.add(stopPathOne); + StopPathTestClass stopPathTwo = new StopPathTestClass("1", 1); + stopPathTestClasses.add(stopPathTwo); + StopPathTestClass stopPathThree = new StopPathTestClass("2", 2); + stopPathTestClasses.add(stopPathThree); + StopPathTestClass stopPathFour = new StopPathTestClass("3", 3); + stopPathTestClasses.add(stopPathFour); + StopPathTestClass stopPathFive = new StopPathTestClass("4", 4); + stopPathTestClasses.add(stopPathFive); + List stopPaths = getStopPaths(stopPathTestClasses); + + // Mock ScheduleTime + ScheduleTime scheduleTimeOne = new ScheduleTime(3115, 3155); + when(gtfsTrip.getScheduleTime(0)).thenReturn(scheduleTimeOne); + + ScheduleTime scheduleTimeTwo = new ScheduleTime(3255, 3270); + when(gtfsTrip.getScheduleTime(1)).thenReturn(scheduleTimeTwo); + + ScheduleTime scheduleTimeThree = new ScheduleTime(3375, 3390); + when(gtfsTrip.getScheduleTime(2)).thenReturn(scheduleTimeThree); + + ScheduleTime scheduleTimeFour = new ScheduleTime(3490, 3520); + when(gtfsTrip.getScheduleTime(3)).thenReturn(scheduleTimeFour); + + ScheduleTime scheduleTimeFive = new ScheduleTime(3600, 3615); + when(gtfsTrip.getScheduleTime(4)).thenReturn(scheduleTimeFive); + + // Mock Trip + when(gtfsTrip.getDirectionId()).thenReturn("0"); + when(gtfsTrip.getStopPaths()).thenReturn(stopPaths); + when(gtfsTrip.getStopPath(anyInt())).then(i -> stopPaths.get(i.getArgument(0))); + when(dbConfig.getTrip(anyString())).thenReturn(gtfsTrip); + doNothing().when(module).storeArrivalPrediction(arrivalPredictionCaptor.capture()); + doNothing().when(module).storeDeparturePrediction(departurePredictionCaptor.capture()); + + // Setup Feed Message + List stopTimes = getStopTimesTwo(); + TripUpdateTestClass tripUpdate = getTripUpdateOne(null, stopTimes); + List entities = getEntities(tripUpdate); + GtfsRealtime.FeedMessage feedMessage = getFeedMessage(feedMessageRequestTime, entities); + + module.processExternalPredictions(feedMessage, dbConfig); + List arrivalPredictions = arrivalPredictionCaptor.getAllValues(); + List departurePredictions = departurePredictionCaptor.getAllValues(); + + assertEquals(4, arrivalPredictions.size()); + assertEquals(4, departurePredictions.size()); + + // Check Stop Time Update Arrival and Departure Times With No Trip Delay and Stop Time arrival departure time + long expectedArrivalTime = 1618034175000l; + long expectedDepartureTime = 1618034190000l; + + assertEquals(expectedArrivalTime, arrivalPredictions.get(0).getPredictedTime().getTime()); + assertEquals(expectedDepartureTime, departurePredictions.get(0).getPredictedTime().getTime()); + + // Check Scheduled Arrival and Departure Times With No Trip Delay and inferred stop time delay + expectedArrivalTime = 1618034295000l; + expectedDepartureTime = 1618034310000l; + assertEquals(expectedArrivalTime, arrivalPredictions.get(1).getPredictedTime().getTime()); + assertEquals(expectedDepartureTime, departurePredictions.get(1).getPredictedTime().getTime()); + + // Check Scheduled Arrival and Departure Times With No Trip Delay and Stop Time arrival departure time + expectedArrivalTime = 1618034350000l; + expectedDepartureTime = 1618034385000l; + assertEquals(expectedArrivalTime, arrivalPredictions.get(2).getPredictedTime().getTime()); + assertEquals(expectedDepartureTime, departurePredictions.get(2).getPredictedTime().getTime()); + + // Check Scheduled Arrival and Departure Times With 60 second Stop Time Delay + expectedArrivalTime = 1618034460000l; + expectedDepartureTime = 1618034475000l; + assertEquals(expectedArrivalTime, arrivalPredictions.get(3).getPredictedTime().getTime()); + assertEquals(expectedDepartureTime, departurePredictions.get(3).getPredictedTime().getTime()); + } + + private List getStopPaths(List stopPathTestClasses){ + List stopPaths = new ArrayList<>(); + + for(StopPathTestClass sptc : stopPathTestClasses){ + StopPath stopPath = new StopPath(sptc.getConfigRev(), + sptc.getStopPathId(), + sptc.getStopId(), + sptc.getGtfsStopSeq(), + sptc.isLastStopInTrip(), + sptc.getRouteId(), + sptc.isLayoverStop(), + sptc.isWaitStop(), + sptc.isScheduleAdherenceStop(), + sptc.getBreakTime(), + sptc.getMaxDistance(), + sptc.getMaxSpeed()); + stopPaths.add(stopPath); + } + return stopPaths; + } + + private TripUpdateTestClass getTripUpdateOne(Integer delay, List stopTimes){ + // (GMT) Saturday, April 10, 2021 5:53:30 AM + long tripStartTime = 1618034010000l; + + LocalDateTime tripStartDateTime = Instant.ofEpochMilli(tripStartTime).atZone(getTimeZone().toZoneId()).toLocalDateTime(); + TripUpdateTestClass tripUpdate = new TripUpdateTestClass("0", "001", "0", + tripStartDateTime, "111", delay, stopTimes); + + return tripUpdate; + } + + private List getStopTimesOne(){ + // (GMT) Saturday, April 10, 2021 5:56:15 AM + long arrivalTime = 1618034175000l; + + // (GMT) Saturday, April 10, 2021 5:56:30 AM + long departureTime = 1618034190000l; + + List stopTimes = new ArrayList<>(); + StopTimeTestClass stopTime = new StopTimeTestClass("1", 1, arrivalTime, departureTime, null); + stopTimes.add(stopTime); + + return stopTimes; + } + + private List getStopTimesTwo(){ + List stopTimes = new ArrayList<>(); + + // (GMT) Saturday, April 10, 2021 5:56:15 AM + long arrivalTimeOne = 1618034175000l; + + // (GMT) Saturday, April 10, 2021 5:56:30 AM + long departureTimeOne = 1618034190000l; + + StopTimeTestClass stopTimeOne = new StopTimeTestClass("1", 1, arrivalTimeOne, departureTimeOne, null); + stopTimes.add(stopTimeOne); + + // (GMT) Saturday, April 10, 2021 5:59:10 AM + long arrivalTimeTwo = 1618034350000l; + + // (GMT) Saturday, April 10, 2021 5:59:45 AM + long departureTimeTwo = 1618034385000l; + + StopTimeTestClass stopTimeTwo = new StopTimeTestClass("3", 3, arrivalTimeTwo, departureTimeTwo, 60); + stopTimes.add(stopTimeTwo); + + return stopTimes; + } + + private GtfsRealtime.FeedMessage getFeedMessage(Long requestTime, List feedEntities){ + GtfsRealtime.FeedMessage.Builder builder = GtfsRealtime.FeedMessage.newBuilder(); + GtfsRealtime.FeedHeader.Builder header = GtfsRealtime.FeedHeader.newBuilder(); + header.setGtfsRealtimeVersion("1.0"); + long headerTime = requestTime != null ? requestTime : System.currentTimeMillis(); + header.setTimestamp(headerTime / 1000); + header.setIncrementality(GtfsRealtime.FeedHeader.Incrementality.FULL_DATASET); + builder.setHeader(header); + builder.addAllEntity(feedEntities); + return builder.build(); + } + + private List getEntities(TripUpdateTestClass tripUpdateTestClass){ + + List entities = new ArrayList<>(); + List stopTimeUpdates = new ArrayList<>(); + + for(StopTimeTestClass sttc : tripUpdateTestClass.getStopTimeTestClasses()){ + stopTimeUpdates.add(makeStopTimeUpdate(sttc.getStopId(), + sttc.getStopSeq(), + sttc.getArrivalTimeMillis(), + sttc.getDepartureTimeMillis(), + sttc.getDelay())); + + } + + GtfsRealtime.TripUpdate tripUpdate = makeTripUpdate(tripUpdateTestClass.getTripId(), + tripUpdateTestClass.getRouteId(), + tripUpdateTestClass.getDirectionId(), + tripUpdateTestClass.getTripDateTime(), + tripUpdateTestClass.getVehicleId(), + tripUpdateTestClass.getDelay(), + stopTimeUpdates); + + GtfsRealtime.FeedEntity.Builder entity = GtfsRealtime.FeedEntity.newBuilder(); + entity.setTripUpdate(tripUpdate); + entity.setId(tripUpdate.getTrip().getTripId()); + entities.add(entity.build()); + + return entities; + } + + private GtfsRealtime.TripUpdate makeTripUpdate(String tripId, + String routeId, + String directionId, + LocalDateTime tripDateTime, + String vehicleId, + Integer delay, + List stopTimeUpdates){ + GtfsRealtime.TripUpdate.Builder tripUpdate = GtfsRealtime.TripUpdate.newBuilder(); + tripUpdate.setTrip(getTripDescriptor(tripId, routeId, directionId, tripDateTime.toLocalDate())); + tripUpdate.setVehicle(getVehicleDescriptor(vehicleId)); + if(delay != null){ + tripUpdate.setDelay(delay); + } + tripUpdate.setTimestamp(tripDateTime.atZone(getTimeZone().toZoneId()).toInstant().toEpochMilli()); + tripUpdate.addAllStopTimeUpdate(stopTimeUpdates); + return tripUpdate.build(); + } + + private GtfsRealtime.TripUpdate.StopTimeUpdate makeStopTimeUpdate(String stopId, + int stopSeq, + long arrivalTimeMillis, + long departureTimeMillis, + Integer delay) { + GtfsRealtime.TripUpdate.StopTimeUpdate.Builder builder = GtfsRealtime.TripUpdate.StopTimeUpdate.newBuilder(); + builder.setStopId(stopId); + builder.setArrival(makeStopTimeEvent(arrivalTimeMillis/1000, delay)); + builder.setDeparture(makeStopTimeEvent(departureTimeMillis/1000, delay)); + if (stopSeq >= 0) + builder.setStopSequence(stopSeq); + return builder.build(); + } + + private GtfsRealtime.TripUpdate.StopTimeEvent.Builder makeStopTimeEvent(long time, Integer delay) { + GtfsRealtime.TripUpdate.StopTimeEvent.Builder builder = GtfsRealtime.TripUpdate.StopTimeEvent.newBuilder(); + builder.setTime(time); + if(delay != null){ + builder.setDelay(delay); + } + return builder; + } + + + + private GtfsRealtime.TripDescriptor.Builder getTripDescriptor(String tripId, String routeId, String directionId, + LocalDate startDate){ + GtfsRealtime.TripDescriptor.Builder trip = GtfsRealtime.TripDescriptor.newBuilder(); + trip.setTripId(tripId); + trip.setRouteId(routeId); + int direction = Integer.parseInt(directionId); + trip.setDirectionId(direction); + + trip.setStartDate(startDate.format(DateTimeFormatter.ofPattern("yyyyMMdd"))); + + return trip; + } + + private GtfsRealtime.VehicleDescriptor.Builder getVehicleDescriptor(String vehicleId){ + GtfsRealtime.VehicleDescriptor.Builder vehicle = GtfsRealtime.VehicleDescriptor.newBuilder(); + vehicle.setId(vehicleId); + return vehicle; + } + + private class TripUpdateTestClass{ + private String tripId; + private String routeId; + private String directionId; + private LocalDateTime tripDateTime; + private String vehicleId; + private Integer delay; + private List stopTimeTestClasses; + + public TripUpdateTestClass(String tripId, String routeId, String directionId, LocalDateTime tripDateTime, + String vehicleId, Integer delay, List stopTimeTestClasses) { + this.tripId = tripId; + this.routeId = routeId; + this.directionId = directionId; + this.tripDateTime = tripDateTime; + this.vehicleId = vehicleId; + this.delay = delay; + this.stopTimeTestClasses = stopTimeTestClasses; + } + + public String getTripId() { + return tripId; + } + + public String getRouteId() { + return routeId; + } + + public String getDirectionId() { + return directionId; + } + + public LocalDateTime getTripDateTime() { + return tripDateTime; + } + + public String getVehicleId() { + return vehicleId; + } + + public Integer getDelay() { + return delay; + } + + public List getStopTimeTestClasses() { + return stopTimeTestClasses; + } + + public void setStopTimeTestClasses(List stopTimeTestClasses) { + this.stopTimeTestClasses = stopTimeTestClasses; + } + } + + private class StopTimeTestClass { + private String stopId; + private int stopSeq; + private long arrivalTimeMillis; + private long departureTimeMillis; + private Integer delay; + + public StopTimeTestClass(String stopId, int stopSeq, long arrivalTimeMillis, long departureTimeMillis, Integer delay) { + this.stopId = stopId; + this.stopSeq = stopSeq; + this.arrivalTimeMillis = arrivalTimeMillis; + this.departureTimeMillis = departureTimeMillis; + this.delay = delay; + } + + public String getStopId() { + return stopId; + } + + public int getStopSeq() { + return stopSeq; + } + + public long getArrivalTimeMillis() { + return arrivalTimeMillis; + } + + public long getDepartureTimeMillis() { + return departureTimeMillis; + } + + public Integer getDelay(){ + return delay; + } + + } + + private class StopPathTestClass { + private String stopId; + private int gtfsStopSeq; + private int configRev = -1; + private String stopPathId = null; + private boolean lastStopInTrip = false; + private String routeId = null; + private boolean layoverStop = false; + private boolean waitStop = false; + private boolean scheduleAdherenceStop = false; + private Integer breakTime = null; + private Double maxDistance = null; + private Double maxSpeed = null; + + public StopPathTestClass(String stopId, int gtfsStopSeq) { + this.stopId = stopId; + this.gtfsStopSeq = gtfsStopSeq; + } + + public String getStopId() { + return stopId; + } + + public int getGtfsStopSeq() { + return gtfsStopSeq; + } + + public int getConfigRev() { + return configRev; + } + + public String getStopPathId() { + return stopPathId; + } + + public boolean isLastStopInTrip() { + return lastStopInTrip; + } + + public String getRouteId() { + return routeId; + } + + public boolean isLayoverStop() { + return layoverStop; + } + + public boolean isWaitStop() { + return waitStop; + } + + public boolean isScheduleAdherenceStop() { + return scheduleAdherenceStop; + } + + public Integer getBreakTime() { + return breakTime; + } + + public Double getMaxDistance() { + return maxDistance; + } + + public Double getMaxSpeed() { + return maxSpeed; + } + } + +} diff --git a/transitclock/src/test/resources/avltest.csv b/transitclock/src/test/resources/avltest.csv new file mode 100644 index 000000000..65a04c0a0 --- /dev/null +++ b/transitclock/src/test/resources/avltest.csv @@ -0,0 +1,2078 @@ +vehicleId,time,latitude,longitude,assignmentId,assignmentType +7167,2015-08-16 09:51:25.000,38.93965,-77.07503,71,ROUTE_ID +7167,2015-08-16 09:52:11.000,38.94225,-77.07642,71,ROUTE_ID +7167,2015-08-16 09:53:17.000,38.94412,-77.07751,71,ROUTE_ID +7167,2015-08-16 09:53:49.000,38.94635,-77.0787,71,ROUTE_ID +7167,2015-08-16 09:54:37.000,38.94769,-77.07942,71,ROUTE_ID +7167,2015-08-16 09:55:55.000,38.94988,-77.08034,71,ROUTE_ID +7167,2015-08-16 09:56:28.000,38.95261,-77.0815,71,ROUTE_ID +7167,2015-08-16 09:57:00.000,38.95491,-77.0827,71,ROUTE_ID +7167,2015-08-16 09:58:06.000,38.957,-77.08371,71,ROUTE_ID +7167,2015-08-16 09:58:39.000,38.95959,-77.08511,71,ROUTE_ID +7167,2015-08-16 09:59:12.000,38.96074,-77.08567,71,ROUTE_ID +7167,2015-08-16 10:00:07.000,38.96082,-77.08564,71,ROUTE_ID +7167,2015-08-16 10:01:13.000,38.96153,-77.08479,71,ROUTE_ID +7167,2015-08-16 10:01:45.000,38.96196,-77.08511,71,ROUTE_ID +7167,2015-08-16 10:02:17.000,38.96149,-77.08575,71,ROUTE_ID +7167,2015-08-16 10:02:49.000,38.96148,-77.08574,71,ROUTE_ID +7167,2015-08-16 10:03:53.000,38.96148,-77.08574,71,ROUTE_ID +7167,2015-08-16 10:04:57.000,38.96148,-77.08574,71,ROUTE_ID +7167,2015-08-16 10:05:29.000,38.96148,-77.08574,71,ROUTE_ID +7167,2015-08-16 10:07:06.000,38.96148,-77.08574,71,ROUTE_ID +7167,2015-08-16 10:08:01.000,38.96159,-77.08566,71,ROUTE_ID +7167,2015-08-16 10:08:31.000,38.96139,-77.08569,71,ROUTE_ID +7167,2015-08-16 10:09:04.000,38.96139,-77.08569,71,ROUTE_ID +7167,2015-08-16 10:10:07.000,38.96148,-77.08549,71,ROUTE_ID +7167,2015-08-16 10:10:37.000,38.96148,-77.08549,71,ROUTE_ID +7167,2015-08-16 10:11:39.000,38.9617,-77.0849,71,ROUTE_ID +7167,2015-08-16 10:12:45.000,38.96198,-77.086,71,ROUTE_ID +7167,2015-08-16 10:13:37.000,38.96061,-77.08568,71,ROUTE_ID +7167,2015-08-16 10:14:22.000,38.95793,-77.08426,71,ROUTE_ID +7167,2015-08-16 10:14:59.000,38.95652,-77.08361,71,ROUTE_ID +7167,2015-08-16 10:15:59.000,38.95208,-77.08171,71,ROUTE_ID +7167,2015-08-16 10:16:30.000,38.95033,-77.08058,71,ROUTE_ID +7167,2015-08-16 10:17:21.000,38.94858,-77.08011,71,ROUTE_ID +7167,2015-08-16 10:17:57.000,38.94807,-77.07983,71,ROUTE_ID +7167,2015-08-16 10:18:47.000,38.94679,-77.07909,71,ROUTE_ID +7167,2015-08-16 10:19:21.000,38.94616,-77.07876,71,ROUTE_ID +7167,2015-08-16 10:20:31.000,38.94353,-77.07726,71,ROUTE_ID +7167,2015-08-16 10:21:05.000,38.94262,-77.0768,71,ROUTE_ID +7167,2015-08-16 10:21:28.000,38.94135,-77.07607,71,ROUTE_ID +7167,2015-08-16 10:22:28.000,38.93736,-77.07391,71,ROUTE_ID +7167,2015-08-16 10:22:58.000,38.93714,-77.0738,71,ROUTE_ID +7167,2015-08-16 10:23:31.000,38.93591,-77.07314,71,ROUTE_ID +7167,2015-08-16 10:23:52.000,38.93463,-77.07258,71,ROUTE_ID +7167,2015-08-16 10:24:35.000,38.93434,-77.07249,71,ROUTE_ID +7167,2015-08-16 10:24:46.000,38.93409,-77.07248,71,ROUTE_ID +7167,2015-08-16 10:25:21.000,38.93209,-77.07272,71,ROUTE_ID +7167,2015-08-16 10:25:53.000,38.932,-77.07271,71,ROUTE_ID +7167,2015-08-16 10:26:06.000,38.93169,-77.07271,71,ROUTE_ID +7167,2015-08-16 10:27:03.000,38.92874,-77.07323,71,ROUTE_ID +7167,2015-08-16 10:27:56.000,38.92395,-77.07324,71,ROUTE_ID +7167,2015-08-16 10:29:02.000,38.9222,-77.07279,71,ROUTE_ID +7167,2015-08-16 10:29:12.000,38.92201,-77.07257,71,ROUTE_ID +7167,2015-08-16 10:29:44.000,38.9202,-77.07156,71,ROUTE_ID +7167,2015-08-16 10:30:16.000,38.9202,-77.07156,71,ROUTE_ID +7167,2015-08-16 10:31:35.000,38.91938,-77.07085,71,ROUTE_ID +7167,2015-08-16 10:32:38.000,38.91595,-77.06796,71,ROUTE_ID +7167,2015-08-16 10:33:25.000,38.91362,-77.06664,71,ROUTE_ID +7167,2015-08-16 10:34:00.000,38.9114,-77.06544,71,ROUTE_ID +7167,2015-08-16 10:34:31.000,38.91113,-77.0653,71,ROUTE_ID +7167,2015-08-16 10:35:03.000,38.91113,-77.0653,71,ROUTE_ID +7167,2015-08-16 10:35:33.000,38.91113,-77.0653,71,ROUTE_ID +7167,2015-08-16 10:36:06.000,38.91113,-77.0653,71,ROUTE_ID +7167,2015-08-16 10:36:36.000,38.9109,-77.06519,71,ROUTE_ID +7167,2015-08-16 10:36:53.000,38.91064,-77.06507,71,ROUTE_ID +7167,2015-08-16 10:37:58.000,38.90775,-77.06356,71,ROUTE_ID +7167,2015-08-16 10:38:31.000,38.90721,-77.06329,71,ROUTE_ID +7167,2015-08-16 10:39:34.000,38.90517,-77.06279,71,ROUTE_ID +7167,2015-08-16 10:40:06.000,38.90511,-77.06219,71,ROUTE_ID +7167,2015-08-16 10:40:36.000,38.90511,-77.06219,71,ROUTE_ID +7167,2015-08-16 10:40:55.000,38.90512,-77.06187,71,ROUTE_ID +7167,2015-08-16 10:41:49.000,38.9052,-77.0598,71,ROUTE_ID +7167,2015-08-16 10:42:53.000,38.90493,-77.05739,71,ROUTE_ID +7167,2015-08-16 10:43:31.000,38.90379,-77.05402,71,ROUTE_ID +7167,2015-08-16 10:44:03.000,38.90295,-77.05169,71,ROUTE_ID +7167,2015-08-16 10:44:33.000,38.9029,-77.05157,71,ROUTE_ID +7167,2015-08-16 10:45:06.000,38.9029,-77.05157,71,ROUTE_ID +7167,2015-08-16 10:45:25.000,38.90282,-77.05132,71,ROUTE_ID +7167,2015-08-16 10:46:06.000,38.90203,-77.0491,71,ROUTE_ID +7167,2015-08-16 10:47:08.000,38.90203,-77.0491,71,ROUTE_ID +7167,2015-08-16 10:47:53.000,38.90127,-77.04691,71,ROUTE_ID +7167,2015-08-16 10:48:25.000,38.90127,-77.0469,71,ROUTE_ID +7167,2015-08-16 10:49:43.000,38.90033,-77.04395,71,ROUTE_ID +7167,2015-08-16 10:50:15.000,38.90033,-77.04393,71,ROUTE_ID +7167,2015-08-16 10:50:47.000,38.90019,-77.04263,71,ROUTE_ID +7167,2015-08-16 10:51:19.000,38.90012,-77.04021,71,ROUTE_ID +7167,2015-08-16 10:51:33.000,38.90027,-77.03978,71,ROUTE_ID +7167,2015-08-16 10:52:35.000,38.90028,-77.03963,71,ROUTE_ID +7167,2015-08-16 10:53:08.000,38.90026,-77.03713,71,ROUTE_ID +7167,2015-08-16 10:53:44.000,38.90014,-77.03535,71,ROUTE_ID +7167,2015-08-16 10:54:18.000,38.90014,-77.03535,71,ROUTE_ID +7167,2015-08-16 10:55:15.000,38.90014,-77.03407,71,ROUTE_ID +7167,2015-08-16 10:56:09.000,38.89883,-77.03375,71,ROUTE_ID +7167,2015-08-16 10:56:42.000,38.89795,-77.03373,71,ROUTE_ID +7167,2015-08-16 10:57:14.000,38.89738,-77.03372,71,ROUTE_ID +7167,2015-08-16 10:58:20.000,38.89563,-77.03365,71,ROUTE_ID +7167,2015-08-16 10:58:52.000,38.89563,-77.03365,71,ROUTE_ID +7167,2015-08-16 10:59:26.000,38.89542,-77.03233,71,ROUTE_ID +7167,2015-08-16 10:59:58.000,38.89543,-77.03222,71,ROUTE_ID +7167,2015-08-16 11:00:31.000,38.89543,-77.0322,71,ROUTE_ID +7167,2015-08-16 11:00:59.000,38.89543,-77.03193,71,ROUTE_ID +7167,2015-08-16 11:02:03.000,38.895,-77.02858,71,ROUTE_ID +7167,2015-08-16 11:02:33.000,38.89498,-77.02853,71,ROUTE_ID +7167,2015-08-16 11:03:42.000,38.89396,-77.02473,71,ROUTE_ID +7167,2015-08-16 11:04:15.000,38.89383,-77.02427,71,ROUTE_ID +7167,2015-08-16 11:04:49.000,38.89329,-77.02232,71,ROUTE_ID +7167,2015-08-16 11:05:22.000,38.89325,-77.02213,71,ROUTE_ID +7167,2015-08-16 11:05:55.000,38.89325,-77.02213,71,ROUTE_ID +7167,2015-08-16 11:07:16.000,38.89117,-77.02195,71,ROUTE_ID +7167,2015-08-16 11:07:46.000,38.89013,-77.02195,71,ROUTE_ID +7167,2015-08-16 11:08:53.000,38.88748,-77.01995,71,ROUTE_ID +7167,2015-08-16 11:09:26.000,38.88747,-77.01562,71,ROUTE_ID +7167,2015-08-16 11:10:31.000,38.88763,-77.00819,71,ROUTE_ID +7167,2015-08-16 11:11:05.000,38.88762,-77.00629,71,ROUTE_ID +7167,2015-08-16 11:11:55.000,38.88742,-77.00341,71,ROUTE_ID +7167,2015-08-16 11:12:28.000,38.88624,-77.00043,71,ROUTE_ID +7167,2015-08-16 11:13:26.000,38.88601,-76.99978,71,ROUTE_ID +7167,2015-08-16 11:14:02.000,38.88601,-76.99976,71,ROUTE_ID +7167,2015-08-16 11:14:32.000,38.88515,-76.9976,71,ROUTE_ID +7167,2015-08-16 11:15:04.000,38.88421,-76.99543,71,ROUTE_ID +7167,2015-08-16 11:16:11.000,38.88358,-76.99387,71,ROUTE_ID +7167,2015-08-16 11:16:41.000,38.88311,-76.99274,71,ROUTE_ID +7167,2015-08-16 11:17:38.000,38.88133,-76.9882,71,ROUTE_ID +7167,2015-08-16 11:18:53.000,38.88003,-76.98505,71,ROUTE_ID +7167,2015-08-16 11:19:29.000,38.87927,-76.98335,71,ROUTE_ID +7167,2015-08-16 11:20:01.000,38.87762,-76.97968,71,ROUTE_ID +7167,2015-08-16 11:21:03.000,38.87415,-76.97245,71,ROUTE_ID +7167,2015-08-16 11:21:34.000,38.87395,-76.97203,71,ROUTE_ID +7167,2015-08-16 11:22:37.000,38.87373,-76.97163,71,ROUTE_ID +7167,2015-08-16 11:23:09.000,38.8733,-76.97058,71,ROUTE_ID +7167,2015-08-16 11:23:49.000,38.87242,-76.96807,71,ROUTE_ID +7167,2015-08-16 11:24:52.000,38.87079,-76.96412,71,ROUTE_ID +7167,2015-08-16 11:25:26.000,38.86938,-76.96093,71,ROUTE_ID +7167,2015-08-16 11:25:47.000,38.86893,-76.96041,71,ROUTE_ID +7167,2015-08-16 11:27:01.000,38.86207,-76.95905,71,ROUTE_ID +7167,2015-08-16 11:27:32.000,38.86214,-76.95905,71,ROUTE_ID +7167,2015-08-16 11:28:12.000,38.86185,-76.95905,71,ROUTE_ID +7167,2015-08-16 11:28:42.000,38.85946,-76.95906,71,ROUTE_ID +7167,2015-08-16 11:29:17.000,38.8586,-76.96023,71,ROUTE_ID +7167,2015-08-16 11:30:34.000,38.85734,-76.96175,71,ROUTE_ID +7167,2015-08-16 11:30:59.000,38.85733,-76.96365,71,ROUTE_ID +7167,2015-08-16 11:31:19.000,38.85706,-76.96465,71,ROUTE_ID +7167,2015-08-16 11:32:19.000,38.85485,-76.96561,71,ROUTE_ID +7167,2015-08-16 11:32:44.000,38.8532,-76.96416,71,ROUTE_ID +7167,2015-08-16 11:33:54.000,38.85163,-76.9593,71,ROUTE_ID +7167,2015-08-16 11:34:27.000,38.8515,-76.95903,71,ROUTE_ID +7167,2015-08-16 11:34:59.000,38.8503,-76.95807,71,ROUTE_ID +7167,2015-08-16 11:35:59.000,38.8507,-76.95591,71,ROUTE_ID +7167,2015-08-16 11:36:32.000,38.85075,-76.95658,71,ROUTE_ID +7167,2015-08-16 11:37:03.000,38.8503,-76.95651,71,ROUTE_ID +7167,2015-08-16 11:38:06.000,38.84999,-76.95643,71,ROUTE_ID +7167,2015-08-16 11:39:33.000,38.84986,-76.95653,71,ROUTE_ID +7167,2015-08-16 11:40:06.000,38.84986,-76.95653,71,ROUTE_ID +7167,2015-08-16 11:40:36.000,38.84986,-76.95653,71,ROUTE_ID +7167,2015-08-16 11:41:33.000,38.84986,-76.95653,71,ROUTE_ID +7167,2015-08-16 11:42:06.000,38.84986,-76.95653,71,ROUTE_ID +7167,2015-08-16 11:42:36.000,38.84986,-76.95653,71,ROUTE_ID +7167,2015-08-16 11:43:10.000,38.84986,-76.95653,71,ROUTE_ID +7167,2015-08-16 11:44:41.000,38.84986,-76.95653,71,ROUTE_ID +7167,2015-08-16 11:45:14.000,38.84971,-76.95647,71,ROUTE_ID +7167,2015-08-16 11:45:28.000,38.84934,-76.95673,71,ROUTE_ID +7167,2015-08-16 11:46:04.000,38.85078,-76.9583,71,ROUTE_ID +7167,2015-08-16 11:46:34.000,38.85159,-76.95929,71,ROUTE_ID +7167,2015-08-16 11:47:08.000,38.85182,-76.96221,71,ROUTE_ID +7167,2015-08-16 11:47:57.000,38.85082,-76.96353,71,ROUTE_ID +7167,2015-08-16 11:48:38.000,38.85258,-76.96564,71,ROUTE_ID +7167,2015-08-16 11:49:10.000,38.85456,-76.96554,71,ROUTE_ID +7167,2015-08-16 11:50:14.000,38.85717,-76.96429,71,ROUTE_ID +7167,2015-08-16 11:51:08.000,38.85729,-76.9614,71,ROUTE_ID +7167,2015-08-16 11:51:59.000,38.85846,-76.96017,71,ROUTE_ID +7167,2015-08-16 11:52:32.000,38.85946,-76.95927,71,ROUTE_ID +7167,2015-08-16 11:53:35.000,38.86171,-76.95885,71,ROUTE_ID +7167,2015-08-16 11:54:08.000,38.86171,-76.95885,71,ROUTE_ID +7167,2015-08-16 11:54:39.000,38.86171,-76.95885,71,ROUTE_ID +7167,2015-08-16 11:55:26.000,38.86537,-76.95959,71,ROUTE_ID +7167,2015-08-16 11:55:58.000,38.86811,-76.96012,71,ROUTE_ID +7167,2015-08-16 11:56:42.000,38.86839,-76.96015,71,ROUTE_ID +7167,2015-08-16 11:57:47.000,38.86903,-76.9603,71,ROUTE_ID +7167,2015-08-16 11:59:14.000,38.87265,-76.9684,71,ROUTE_ID +7167,2015-08-16 11:59:36.000,38.87288,-76.96894,71,ROUTE_ID +7167,2015-08-16 12:00:09.000,38.87362,-76.97056,71,ROUTE_ID +7167,2015-08-16 12:00:39.000,38.87375,-76.97084,71,ROUTE_ID +7167,2015-08-16 12:01:12.000,38.87375,-76.97084,71,ROUTE_ID +7167,2015-08-16 12:02:07.000,38.87481,-76.9732,71,ROUTE_ID +7167,2015-08-16 12:03:12.000,38.87897,-76.9818,71,ROUTE_ID +7167,2015-08-16 12:03:45.000,38.88029,-76.98497,71,ROUTE_ID +7167,2015-08-16 12:04:18.000,38.88067,-76.98604,71,ROUTE_ID +7167,2015-08-16 12:05:07.000,38.88171,-76.98853,71,ROUTE_ID +7167,2015-08-16 12:05:37.000,38.88233,-76.99003,71,ROUTE_ID +7167,2015-08-16 12:06:03.000,38.88282,-76.99123,71,ROUTE_ID +7167,2015-08-16 12:07:05.000,38.88427,-76.99477,71,ROUTE_ID +7167,2015-08-16 12:07:36.000,38.88427,-76.99477,71,ROUTE_ID +7167,2015-08-16 12:08:13.000,38.88477,-76.99606,71,ROUTE_ID +7167,2015-08-16 12:08:45.000,38.88565,-76.99821,71,ROUTE_ID +7167,2015-08-16 12:09:31.000,38.88662,-77.00053,71,ROUTE_ID +7167,2015-08-16 12:10:19.000,38.8872,-77.00185,71,ROUTE_ID +7167,2015-08-16 12:10:54.000,38.88771,-77.00537,71,ROUTE_ID +7167,2015-08-16 12:12:00.000,38.88763,-77.00998,71,ROUTE_ID +7167,2015-08-16 12:12:36.000,38.88763,-77.0136,71,ROUTE_ID +7167,2015-08-16 12:13:08.000,38.88767,-77.01743,71,ROUTE_ID +7167,2015-08-16 12:13:52.000,38.88766,-77.0177,71,ROUTE_ID +7167,2015-08-16 12:14:27.000,38.88764,-77.01964,71,ROUTE_ID +7167,2015-08-16 12:15:39.000,38.88762,-77.02144,71,ROUTE_ID +7167,2015-08-16 12:29:33.000,38.90219,-77.03369,71,ROUTE_ID +7167,2015-08-16 12:34:25.000,38.9014,-77.0415,71,ROUTE_ID +7167,2015-08-16 12:35:18.000,38.90102,-77.04417,71,ROUTE_ID +7167,2015-08-16 12:35:53.000,38.90184,-77.04781,71,ROUTE_ID +7167,2015-08-16 12:37:26.000,38.90309,-77.05123,71,ROUTE_ID +7167,2015-08-16 12:37:58.000,38.90309,-77.05123,71,ROUTE_ID +7167,2015-08-16 12:39:19.000,38.90421,-77.0545,71,ROUTE_ID +7167,2015-08-16 12:39:51.000,38.9043,-77.05483,71,ROUTE_ID +7167,2015-08-16 12:41:29.000,38.90531,-77.06136,71,ROUTE_ID +7167,2015-08-16 12:42:36.000,38.90539,-77.06233,71,ROUTE_ID +7167,2015-08-16 12:45:02.000,38.90826,-77.06386,71,ROUTE_ID +7167,2015-08-16 12:46:29.000,38.90999,-77.06493,71,ROUTE_ID +7167,2015-08-16 12:48:05.000,38.91281,-77.06631,71,ROUTE_ID +7167,2015-08-16 12:50:01.000,38.91569,-77.06775,71,ROUTE_ID +7167,2015-08-16 12:51:06.000,38.91601,-77.06796,71,ROUTE_ID +7167,2015-08-16 12:52:08.000,38.92005,-77.07136,71,ROUTE_ID +7167,2015-08-16 12:53:08.000,38.92247,-77.07279,71,ROUTE_ID +7167,2015-08-16 12:54:41.000,38.92463,-77.07313,71,ROUTE_ID +7167,2015-08-16 12:55:03.000,38.92501,-77.07313,71,ROUTE_ID +7167,2015-08-16 12:56:38.000,38.92762,-77.07308,71,ROUTE_ID +7167,2015-08-16 12:58:08.000,38.93196,-77.07252,71,ROUTE_ID +7167,2015-08-16 12:59:37.000,38.93344,-77.07253,71,ROUTE_ID +7167,2015-08-16 13:00:39.000,38.93602,-77.07313,71,ROUTE_ID +7167,2015-08-16 13:01:40.000,38.93741,-77.07388,71,ROUTE_ID +7167,2015-08-16 13:02:22.000,38.93979,-77.0751,71,ROUTE_ID +7167,2015-08-16 13:03:29.000,38.94211,-77.07635,71,ROUTE_ID +7167,2015-08-16 13:04:43.000,38.94407,-77.07748,71,ROUTE_ID +7167,2015-08-16 13:05:50.000,38.94641,-77.07875,71,ROUTE_ID +7167,2015-08-16 13:07:15.000,38.9479,-77.07957,71,ROUTE_ID +7167,2015-08-16 13:07:23.000,38.94847,-77.07991,71,ROUTE_ID +7167,2015-08-16 13:08:28.000,38.94946,-77.08021,71,ROUTE_ID +7167,2015-08-16 13:09:21.000,38.95272,-77.08156,71,ROUTE_ID +7167,2015-08-16 13:11:07.000,38.95877,-77.08463,71,ROUTE_ID +7167,2015-08-16 13:12:57.000,38.96072,-77.08567,71,ROUTE_ID +7167,2015-08-16 13:15:14.000,38.96177,-77.08476,71,ROUTE_ID +7167,2015-08-16 13:16:00.000,38.9616,-77.08585,71,ROUTE_ID +7167,2015-08-16 13:18:32.000,38.96158,-77.08585,71,ROUTE_ID +7167,2015-08-16 13:20:27.000,38.96158,-77.08585,71,ROUTE_ID +7167,2015-08-16 13:22:39.000,38.96158,-77.08585,71,ROUTE_ID +7167,2015-08-16 13:23:43.000,38.96158,-77.08585,71,ROUTE_ID +7167,2015-08-16 13:26:24.000,38.96168,-77.08606,71,ROUTE_ID +7167,2015-08-16 13:26:56.000,38.96168,-77.08606,71,ROUTE_ID +7167,2015-08-16 13:28:56.000,38.96168,-77.08606,71,ROUTE_ID +7167,2015-08-16 13:30:02.000,38.96163,-77.0862,72,ROUTE_ID +7167,2015-08-16 13:32:17.000,38.96163,-77.0862,72,ROUTE_ID +7167,2015-08-16 13:34:58.000,38.96152,-77.08556,72,ROUTE_ID +7167,2015-08-16 13:36:02.000,38.96171,-77.08488,72,ROUTE_ID +7167,2015-08-16 13:38:25.000,38.95652,-77.08361,72,ROUTE_ID +7167,2015-08-16 13:39:57.000,38.95466,-77.08286,72,ROUTE_ID +7167,2015-08-16 13:41:30.000,38.95096,-77.08075,72,ROUTE_ID +7167,2015-08-16 13:43:04.000,38.94814,-77.07986,72,ROUTE_ID +7167,2015-08-16 13:45:00.000,38.94406,-77.07755,72,ROUTE_ID +7167,2015-08-16 14:02:51.000,38.90732,-77.06336,72,ROUTE_ID +7167,2015-08-16 14:03:14.000,38.90714,-77.06322,72,ROUTE_ID +7167,2015-08-16 14:03:48.000,38.90608,-77.06283,72,ROUTE_ID +7167,2015-08-16 14:04:53.000,38.90553,-77.06285,72,ROUTE_ID +7167,2015-08-16 14:05:25.000,38.90511,-77.06258,72,ROUTE_ID +7167,2015-08-16 14:06:11.000,38.90512,-77.06186,72,ROUTE_ID +7167,2015-08-16 14:06:52.000,38.9052,-77.05984,72,ROUTE_ID +7167,2015-08-16 14:07:25.000,38.90489,-77.0573,72,ROUTE_ID +7167,2015-08-16 14:08:17.000,38.90485,-77.05684,72,ROUTE_ID +7167,2015-08-16 14:08:51.000,38.90369,-77.0537,72,ROUTE_ID +7167,2015-08-16 14:09:23.000,38.90294,-77.05167,72,ROUTE_ID +7167,2015-08-16 14:10:28.000,38.90281,-77.05128,72,ROUTE_ID +7167,2015-08-16 14:11:01.000,38.90209,-77.04957,72,ROUTE_ID +7167,2015-08-16 14:11:32.000,38.90206,-77.04918,72,ROUTE_ID +7167,2015-08-16 14:12:42.000,38.90113,-77.04641,72,ROUTE_ID +7167,2015-08-16 14:13:16.000,38.90023,-77.04358,72,ROUTE_ID +7167,2015-08-16 14:13:49.000,38.90023,-77.04358,72,ROUTE_ID +7167,2015-08-16 14:14:49.000,38.90027,-77.03984,72,ROUTE_ID +7167,2015-08-16 14:15:02.000,38.90028,-77.0397,72,ROUTE_ID +7167,2015-08-16 14:16:05.000,38.90025,-77.03893,72,ROUTE_ID +7167,2015-08-16 14:16:35.000,38.90026,-77.03658,72,ROUTE_ID +7167,2015-08-16 14:17:10.000,38.90014,-77.03529,72,ROUTE_ID +7167,2015-08-16 14:18:18.000,38.90014,-77.03406,72,ROUTE_ID +7167,2015-08-16 14:18:51.000,38.89902,-77.03378,72,ROUTE_ID +7167,2015-08-16 14:19:23.000,38.89674,-77.03369,72,ROUTE_ID +7167,2015-08-16 14:19:55.000,38.89565,-77.03366,72,ROUTE_ID +7167,2015-08-16 14:20:27.000,38.89565,-77.03366,72,ROUTE_ID +7167,2015-08-16 14:21:25.000,38.89543,-77.03189,72,ROUTE_ID +7167,2015-08-16 14:22:02.000,38.89508,-77.02888,72,ROUTE_ID +7167,2015-08-16 14:22:49.000,38.89502,-77.02866,72,ROUTE_ID +7167,2015-08-16 14:23:23.000,38.8941,-77.02612,72,ROUTE_ID +7167,2015-08-16 14:24:49.000,38.89342,-77.0228,72,ROUTE_ID +7167,2015-08-16 14:25:21.000,38.89328,-77.02228,72,ROUTE_ID +7167,2015-08-16 14:26:25.000,38.89328,-77.02228,72,ROUTE_ID +7167,2015-08-16 14:26:55.000,38.8924,-77.02201,72,ROUTE_ID +7167,2015-08-16 14:28:00.000,38.88899,-77.0219,72,ROUTE_ID +7167,2015-08-16 14:28:58.000,38.88748,-77.02016,72,ROUTE_ID +7167,2015-08-16 14:29:31.000,38.88748,-77.01616,72,ROUTE_ID +7167,2015-08-16 14:30:34.000,38.88746,-77.01412,72,ROUTE_ID +7167,2015-08-16 14:31:17.000,38.88755,-77.01198,72,ROUTE_ID +7167,2015-08-16 14:31:50.000,38.88757,-77.00932,72,ROUTE_ID +7167,2015-08-16 14:32:44.000,38.88762,-77.00587,72,ROUTE_ID +7167,2015-08-16 14:33:18.000,38.88761,-77.0056,72,ROUTE_ID +7167,2015-08-16 14:33:51.000,38.88745,-77.00359,72,ROUTE_ID +7167,2015-08-16 14:33:59.000,38.88741,-77.0034,72,ROUTE_ID +7167,2015-08-16 14:34:56.000,38.88587,-76.99944,72,ROUTE_ID +7167,2015-08-16 14:35:07.000,38.88577,-76.99918,72,ROUTE_ID +7167,2015-08-16 14:36:11.000,38.8849,-76.99696,72,ROUTE_ID +7167,2015-08-16 14:36:41.000,38.88487,-76.99692,72,ROUTE_ID +7167,2015-08-16 14:37:49.000,38.88415,-76.99528,72,ROUTE_ID +7167,2015-08-16 14:38:23.000,38.88345,-76.99354,72,ROUTE_ID +7167,2015-08-16 14:39:12.000,38.88271,-76.99167,72,ROUTE_ID +7167,2015-08-16 14:39:55.000,38.88196,-76.98982,72,ROUTE_ID +7167,2015-08-16 14:40:06.000,38.88196,-76.98982,72,ROUTE_ID +7167,2015-08-16 14:41:10.000,38.881,-76.98736,72,ROUTE_ID +7167,2015-08-16 14:41:40.000,38.88079,-76.98685,72,ROUTE_ID +7167,2015-08-16 14:42:33.000,38.88009,-76.98521,72,ROUTE_ID +7167,2015-08-16 14:43:16.000,38.87931,-76.98348,72,ROUTE_ID +7167,2015-08-16 14:43:48.000,38.87791,-76.98037,72,ROUTE_ID +7167,2015-08-16 14:44:54.000,38.87415,-76.97244,72,ROUTE_ID +7167,2015-08-16 14:45:28.000,38.87376,-76.97161,72,ROUTE_ID +7167,2015-08-16 14:46:27.000,38.87326,-76.9709,72,ROUTE_ID +7167,2015-08-16 14:47:10.000,38.87111,-76.97092,72,ROUTE_ID +7167,2015-08-16 14:48:39.000,38.8684,-76.97079,72,ROUTE_ID +7167,2015-08-16 14:49:12.000,38.8684,-76.97079,72,ROUTE_ID +7167,2015-08-16 14:49:53.000,38.86813,-76.9707,72,ROUTE_ID +7167,2015-08-16 14:50:40.000,38.86549,-76.97056,72,ROUTE_ID +7167,2015-08-16 14:51:13.000,38.86291,-76.97007,72,ROUTE_ID +7167,2015-08-16 14:52:05.000,38.86068,-76.96882,72,ROUTE_ID +7167,2015-08-16 14:52:35.000,38.85901,-76.96825,72,ROUTE_ID +7167,2015-08-16 14:53:14.000,38.85885,-76.96832,72,ROUTE_ID +7167,2015-08-16 14:53:25.000,38.85807,-76.96896,72,ROUTE_ID +7167,2015-08-16 14:54:16.000,38.85717,-76.9698,72,ROUTE_ID +7167,2015-08-16 14:54:51.000,38.85605,-76.97004,72,ROUTE_ID +7167,2015-08-16 14:55:57.000,38.85572,-76.96999,72,ROUTE_ID +7167,2015-08-16 14:56:35.000,38.85378,-76.96999,72,ROUTE_ID +7167,2015-08-16 14:57:52.000,38.85102,-76.97362,72,ROUTE_ID +7167,2015-08-16 14:58:23.000,38.85059,-76.97295,72,ROUTE_ID +7167,2015-08-16 14:59:33.000,38.84823,-76.96988,72,ROUTE_ID +7167,2015-08-16 15:00:06.000,38.84641,-76.97005,72,ROUTE_ID +7167,2015-08-16 15:01:11.000,38.8458,-76.96999,72,ROUTE_ID +7167,2015-08-16 15:01:41.000,38.84528,-76.97077,72,ROUTE_ID +7167,2015-08-16 15:02:17.000,38.84433,-76.97172,72,ROUTE_ID +7167,2015-08-16 15:03:17.000,38.84132,-76.97558,72,ROUTE_ID +7167,2015-08-16 15:03:36.000,38.8402,-76.9769,72,ROUTE_ID +7167,2015-08-16 15:04:09.000,38.83982,-76.97702,72,ROUTE_ID +7167,2015-08-16 15:05:13.000,38.84032,-76.97633,72,ROUTE_ID +7167,2015-08-16 15:05:43.000,38.84032,-76.97633,72,ROUTE_ID +7167,2015-08-16 15:06:15.000,38.84032,-76.97633,72,ROUTE_ID +7167,2015-08-16 15:07:01.000,38.84032,-76.97633,72,ROUTE_ID +7167,2015-08-16 15:07:33.000,38.84041,-76.97633,72,ROUTE_ID +7167,2015-08-16 15:08:35.000,38.84041,-76.97633,72,ROUTE_ID +7167,2015-08-16 15:09:09.000,38.84041,-76.97633,72,ROUTE_ID +7167,2015-08-16 15:09:39.000,38.84041,-76.97633,72,ROUTE_ID +7167,2015-08-16 15:10:41.000,38.84088,-76.97561,72,ROUTE_ID +7167,2015-08-16 15:10:57.000,38.84159,-76.97449,72,ROUTE_ID +7167,2015-08-16 15:11:32.000,38.84014,-76.97581,72,ROUTE_ID +7167,2015-08-16 15:12:36.000,38.8433,-76.97289,72,ROUTE_ID +7167,2015-08-16 15:12:55.000,38.84373,-76.97237,72,ROUTE_ID +7167,2015-08-16 15:13:44.000,38.84494,-76.97265,72,ROUTE_ID +7167,2015-08-16 15:14:48.000,38.84745,-76.97261,72,ROUTE_ID +7167,2015-08-16 15:15:21.000,38.84795,-76.9711,72,ROUTE_ID +7167,2015-08-16 15:16:13.000,38.8481,-76.97093,72,ROUTE_ID +7167,2015-08-16 15:16:47.000,38.84974,-76.97195,72,ROUTE_ID +7167,2015-08-16 15:17:30.000,38.85099,-76.97351,72,ROUTE_ID +7167,2015-08-16 15:18:32.000,38.85119,-76.97289,72,ROUTE_ID +7167,2015-08-16 15:19:04.000,38.85127,-76.97199,72,ROUTE_ID +7167,2015-08-16 15:20:23.000,38.85461,-76.96984,72,ROUTE_ID +7167,2015-08-16 15:20:55.000,38.85759,-76.96915,72,ROUTE_ID +7167,2015-08-16 15:21:27.000,38.85881,-76.96817,72,ROUTE_ID +7167,2015-08-16 15:22:30.000,38.85991,-76.96815,72,ROUTE_ID +7167,2015-08-16 15:23:03.000,38.85991,-76.96815,72,ROUTE_ID +7167,2015-08-16 15:23:48.000,38.86005,-76.96814,72,ROUTE_ID +7167,2015-08-16 15:24:14.000,38.86034,-76.96828,72,ROUTE_ID +7167,2015-08-16 15:24:48.000,38.86123,-76.96902,72,ROUTE_ID +7167,2015-08-16 15:25:39.000,38.86369,-76.9705,72,ROUTE_ID +7167,2015-08-16 15:26:08.000,38.86614,-76.97021,72,ROUTE_ID +7167,2015-08-16 15:26:45.000,38.86834,-76.97068,72,ROUTE_ID +7167,2015-08-16 15:27:36.000,38.87094,-76.97314,72,ROUTE_ID +7167,2015-08-16 15:28:31.000,38.87269,-76.97211,72,ROUTE_ID +7167,2015-08-16 15:29:04.000,38.87306,-76.97144,72,ROUTE_ID +7167,2015-08-16 15:30:08.000,38.87416,-76.97176,72,ROUTE_ID +7167,2015-08-16 15:30:38.000,38.87416,-76.97176,72,ROUTE_ID +7167,2015-08-16 15:31:25.000,38.87456,-76.97263,72,ROUTE_ID +7167,2015-08-16 15:31:58.000,38.87637,-76.97679,72,ROUTE_ID +7167,2015-08-16 15:33:06.000,38.8802,-76.98477,72,ROUTE_ID +7167,2015-08-16 15:33:36.000,38.88049,-76.98549,72,ROUTE_ID +7167,2015-08-16 15:35:01.000,38.88155,-76.9881,72,ROUTE_ID +7167,2015-08-16 15:35:16.000,38.88168,-76.98843,72,ROUTE_ID +7167,2015-08-16 15:35:50.000,38.8834,-76.99267,72,ROUTE_ID +7167,2015-08-16 15:36:24.000,38.88426,-76.99474,72,ROUTE_ID +7167,2015-08-16 15:36:56.000,38.88428,-76.99478,72,ROUTE_ID +7167,2015-08-16 15:38:03.000,38.8857,-76.99835,72,ROUTE_ID +7167,2015-08-16 15:38:22.000,38.88587,-76.99874,72,ROUTE_ID +7167,2015-08-16 15:39:23.000,38.88771,-77.00541,72,ROUTE_ID +7167,2015-08-16 15:39:56.000,38.88769,-77.00598,72,ROUTE_ID +7167,2015-08-16 15:40:28.000,38.88763,-77.00903,72,ROUTE_ID +7167,2015-08-16 15:41:00.000,38.8876,-77.01093,72,ROUTE_ID +7167,2015-08-16 15:41:36.000,38.88763,-77.01353,72,ROUTE_ID +7167,2015-08-16 15:42:38.000,38.88764,-77.01501,72,ROUTE_ID +7167,2015-08-16 15:43:12.000,38.88765,-77.01897,72,ROUTE_ID +7167,2015-08-16 15:44:24.000,38.88833,-77.02178,72,ROUTE_ID +7167,2015-08-16 15:45:32.000,38.89182,-77.0218,72,ROUTE_ID +7167,2015-08-16 15:45:58.000,38.89204,-77.02168,72,ROUTE_ID +7167,2015-08-16 15:46:32.000,38.8921,-77.02004,72,ROUTE_ID +7167,2015-08-16 15:47:05.000,38.89254,-77.01987,72,ROUTE_ID +7167,2015-08-16 15:48:09.000,38.8936,-77.02263,72,ROUTE_ID +7167,2015-08-16 15:49:12.000,38.89386,-77.0237,72,ROUTE_ID +7167,2015-08-16 15:49:42.000,38.89422,-77.02496,72,ROUTE_ID +7167,2015-08-16 15:50:48.000,38.89476,-77.02679,72,ROUTE_ID +7167,2015-08-16 15:51:20.000,38.89492,-77.02741,72,ROUTE_ID +7167,2015-08-16 15:51:52.000,38.89531,-77.02873,72,ROUTE_ID +7167,2015-08-16 15:52:51.000,38.89552,-77.02948,72,ROUTE_ID +7167,2015-08-16 15:54:57.000,38.89558,-77.03335,72,ROUTE_ID +7167,2015-08-16 15:55:31.000,38.89623,-77.0336,72,ROUTE_ID +7167,2015-08-16 15:56:52.000,38.89796,-77.03364,72,ROUTE_ID +7167,2015-08-16 15:57:27.000,38.89903,-77.03365,72,ROUTE_ID +7167,2015-08-16 15:58:31.000,38.90139,-77.03394,72,ROUTE_ID +7167,2015-08-16 15:59:12.000,38.90139,-77.03462,72,ROUTE_ID +7167,2015-08-16 15:59:42.000,38.90141,-77.03829,72,ROUTE_ID +7167,2015-08-16 16:00:58.000,38.90143,-77.0394,72,ROUTE_ID +7167,2015-08-16 16:01:09.000,38.90144,-77.04032,72,ROUTE_ID +7167,2015-08-16 16:02:32.000,38.90214,-77.04842,72,ROUTE_ID +7167,2015-08-16 16:02:41.000,38.90222,-77.04889,72,ROUTE_ID +7167,2015-08-16 16:03:14.000,38.90242,-77.04925,72,ROUTE_ID +7167,2015-08-16 16:04:20.000,38.90305,-77.05105,72,ROUTE_ID +7167,2015-08-16 16:04:55.000,38.90319,-77.05159,72,ROUTE_ID +7167,2015-08-16 16:05:27.000,38.90363,-77.0528,72,ROUTE_ID +7167,2015-08-16 16:06:00.000,38.90365,-77.05287,72,ROUTE_ID +7167,2015-08-16 16:06:47.000,38.9037,-77.05315,72,ROUTE_ID +7167,2015-08-16 16:07:21.000,38.90481,-77.05634,72,ROUTE_ID +7167,2015-08-16 16:07:54.000,38.90496,-77.05678,72,ROUTE_ID +7167,2015-08-16 16:08:10.000,38.90497,-77.05682,72,ROUTE_ID +7167,2015-08-16 16:08:46.000,38.90532,-77.0591,72,ROUTE_ID +7167,2015-08-16 16:09:33.000,38.90531,-77.05985,72,ROUTE_ID +7167,2015-08-16 16:10:36.000,38.90532,-77.06152,72,ROUTE_ID +7167,2015-08-16 16:11:32.000,38.9054,-77.06258,72,ROUTE_ID +7167,2015-08-16 16:12:05.000,38.90612,-77.0627,72,ROUTE_ID +7167,2015-08-16 16:12:35.000,38.90675,-77.06289,72,ROUTE_ID +7167,2015-08-16 16:13:09.000,38.90689,-77.06313,72,ROUTE_ID +7167,2015-08-16 16:13:39.000,38.9074,-77.06343,72,ROUTE_ID +7167,2015-08-16 16:14:11.000,38.90874,-77.06413,72,ROUTE_ID +7167,2015-08-16 16:15:31.000,38.9108,-77.06532,72,ROUTE_ID +7167,2015-08-16 16:15:43.000,38.91092,-77.0654,72,ROUTE_ID +7167,2015-08-16 16:16:16.000,38.91223,-77.06603,72,ROUTE_ID +7167,2015-08-16 16:17:19.000,38.91413,-77.0669,72,ROUTE_ID +7167,2015-08-16 16:18:35.000,38.91604,-77.06799,72,ROUTE_ID +7167,2015-08-16 16:18:45.000,38.91638,-77.06825,72,ROUTE_ID +7167,2015-08-16 16:19:17.000,38.91639,-77.06826,72,ROUTE_ID +7167,2015-08-16 16:19:49.000,38.9185,-77.07002,72,ROUTE_ID +7167,2015-08-16 16:20:44.000,38.92023,-77.07145,72,ROUTE_ID +7167,2015-08-16 16:21:19.000,38.92249,-77.07281,72,ROUTE_ID +7167,2015-08-16 16:22:28.000,38.92257,-77.07287,72,ROUTE_ID +7167,2015-08-16 16:23:01.000,38.92453,-77.07311,72,ROUTE_ID +7167,2015-08-16 16:23:57.000,38.92821,-77.0731,72,ROUTE_ID +7167,2015-08-16 16:24:27.000,38.92988,-77.07288,72,ROUTE_ID +7167,2015-08-16 16:25:00.000,38.93176,-77.07252,72,ROUTE_ID +7167,2015-08-16 16:26:05.000,38.932,-77.07251,72,ROUTE_ID +7167,2015-08-16 16:26:35.000,38.93416,-77.07259,72,ROUTE_ID +7167,2015-08-16 16:27:31.000,38.93481,-77.07254,72,ROUTE_ID +7167,2015-08-16 16:28:06.000,38.937,-77.07363,72,ROUTE_ID +7167,2015-08-16 16:28:50.000,38.93964,-77.07502,72,ROUTE_ID +7167,2015-08-16 16:29:40.000,38.9423,-77.07645,72,ROUTE_ID +7167,2015-08-16 16:30:12.000,38.94297,-77.07682,72,ROUTE_ID +7167,2015-08-16 16:30:57.000,38.94403,-77.07745,72,ROUTE_ID +7167,2015-08-16 16:31:32.000,38.94537,-77.07819,72,ROUTE_ID +7167,2015-08-16 16:32:05.000,38.94677,-77.07903,72,ROUTE_ID +7167,2015-08-16 16:32:35.000,38.94769,-77.07943,72,ROUTE_ID +7167,2015-08-16 16:33:07.000,38.94776,-77.07947,72,ROUTE_ID +7167,2015-08-16 16:33:43.000,38.94849,-77.07992,72,ROUTE_ID +7167,2015-08-16 16:34:15.000,38.94944,-77.0802,72,ROUTE_ID +7167,2015-08-16 16:34:22.000,38.94975,-77.08031,72,ROUTE_ID +7167,2015-08-16 16:34:55.000,38.95194,-77.08116,72,ROUTE_ID +7167,2015-08-16 16:35:28.000,38.95194,-77.08116,72,ROUTE_ID +7167,2015-08-16 16:36:01.000,38.95413,-77.08228,72,ROUTE_ID +7167,2015-08-16 16:37:08.000,38.958,-77.08424,72,ROUTE_ID +7167,2015-08-16 16:38:11.000,38.95881,-77.08466,72,ROUTE_ID +7167,2015-08-16 16:38:34.000,38.9595,-77.08506,72,ROUTE_ID +7167,2015-08-16 16:39:08.000,38.9607,-77.08566,72,ROUTE_ID +7167,2015-08-16 16:40:11.000,38.96079,-77.08566,72,ROUTE_ID +7167,2015-08-16 16:40:41.000,38.96105,-77.08538,72,ROUTE_ID +7167,2015-08-16 16:41:15.000,38.96154,-77.08478,72,ROUTE_ID +7167,2015-08-16 16:42:25.000,38.96172,-77.08593,72,ROUTE_ID +7167,2015-08-16 16:43:30.000,38.96172,-77.08593,72,ROUTE_ID +7167,2015-08-16 16:44:02.000,38.96172,-77.08593,72,ROUTE_ID +7167,2015-08-16 16:44:32.000,38.96172,-77.08593,72,ROUTE_ID +7167,2015-08-16 16:45:35.000,38.96172,-77.08593,72,ROUTE_ID +7167,2015-08-16 16:46:08.000,38.96172,-77.08593,72,ROUTE_ID +7167,2015-08-16 16:46:39.000,38.96172,-77.08593,72,ROUTE_ID +7167,2015-08-16 16:47:12.000,38.96172,-77.08593,72,ROUTE_ID +7167,2015-08-16 16:48:14.000,38.96172,-77.08593,72,ROUTE_ID +7167,2015-08-16 16:48:46.000,38.96172,-77.08593,72,ROUTE_ID +7167,2015-08-16 16:49:19.000,38.96172,-77.08593,72,ROUTE_ID +7167,2015-08-16 16:49:52.000,38.96172,-77.08593,72,ROUTE_ID +7167,2015-08-16 16:50:24.000,38.96172,-77.08593,72,ROUTE_ID +7167,2015-08-16 16:51:01.000,38.96172,-77.08593,72,ROUTE_ID +7167,2015-08-16 16:51:33.000,38.96174,-77.08595,72,ROUTE_ID +7167,2015-08-16 16:52:37.000,38.96174,-77.08595,72,ROUTE_ID +7167,2015-08-16 16:53:09.000,38.96174,-77.08595,72,ROUTE_ID +7167,2015-08-16 16:53:39.000,38.96174,-77.08595,72,ROUTE_ID +7167,2015-08-16 16:54:32.000,38.96174,-77.08595,72,ROUTE_ID +7167,2015-08-16 16:55:06.000,38.96139,-77.08569,72,ROUTE_ID +7167,2015-08-16 16:55:36.000,38.96139,-77.08569,72,ROUTE_ID +7167,2015-08-16 16:56:32.000,38.9615,-77.08576,72,ROUTE_ID +7167,2015-08-16 16:57:04.000,38.9615,-77.08576,72,ROUTE_ID +7167,2015-08-16 16:58:06.000,38.9615,-77.08576,72,ROUTE_ID +7167,2015-08-16 16:58:37.000,38.9615,-77.08576,72,ROUTE_ID +7167,2015-08-16 16:59:09.000,38.9615,-77.08576,72,ROUTE_ID +7167,2015-08-16 17:00:32.000,38.9615,-77.08576,71,ROUTE_ID +7167,2015-08-16 17:01:06.000,38.9615,-77.08576,71,ROUTE_ID +7167,2015-08-16 17:02:09.000,38.96149,-77.08576,71,ROUTE_ID +7167,2015-08-16 17:02:39.000,38.96149,-77.08576,71,ROUTE_ID +7167,2015-08-16 17:03:12.000,38.96149,-77.08576,71,ROUTE_ID +7167,2015-08-16 17:04:14.000,38.96139,-77.08569,71,ROUTE_ID +7167,2015-08-16 17:04:46.000,38.96139,-77.08569,71,ROUTE_ID +7167,2015-08-16 17:05:50.000,38.96167,-77.08488,71,ROUTE_ID +7167,2015-08-16 17:06:23.000,38.96167,-77.08488,71,ROUTE_ID +7167,2015-08-16 17:06:56.000,38.962,-77.08524,71,ROUTE_ID +7167,2015-08-16 17:08:00.000,38.96193,-77.08616,71,ROUTE_ID +7167,2015-08-16 17:08:32.000,38.96132,-77.08606,71,ROUTE_ID +7167,2015-08-16 17:09:22.000,38.95926,-77.08504,71,ROUTE_ID +7167,2015-08-16 17:09:54.000,38.95926,-77.08504,71,ROUTE_ID +7167,2015-08-16 17:10:26.000,38.95853,-77.08472,71,ROUTE_ID +7167,2015-08-16 17:11:09.000,38.95779,-77.08421,71,ROUTE_ID +7167,2015-08-16 17:11:51.000,38.95465,-77.08285,71,ROUTE_ID +7167,2015-08-16 17:12:25.000,38.95271,-77.08194,71,ROUTE_ID +7167,2015-08-16 17:13:10.000,38.95091,-77.08073,71,ROUTE_ID +7167,2015-08-16 17:13:41.000,38.95011,-77.08054,71,ROUTE_ID +7167,2015-08-16 17:15:04.000,38.94854,-77.08009,71,ROUTE_ID +7167,2015-08-16 17:15:34.000,38.94813,-77.07986,71,ROUTE_ID +7167,2015-08-16 17:15:59.000,38.94785,-77.07967,71,ROUTE_ID +7167,2015-08-16 17:16:42.000,38.9468,-77.07909,71,ROUTE_ID +7167,2015-08-16 17:17:15.000,38.94456,-77.07782,71,ROUTE_ID +7167,2015-08-16 17:18:06.000,38.94341,-77.07719,71,ROUTE_ID +7167,2015-08-16 17:18:15.000,38.94305,-77.07703,71,ROUTE_ID +7167,2015-08-16 17:19:03.000,38.93939,-77.075,71,ROUTE_ID +7167,2015-08-16 17:20:13.000,38.93723,-77.07385,71,ROUTE_ID +7167,2015-08-16 17:21:37.000,38.93417,-77.07248,71,ROUTE_ID +7167,2015-08-16 17:22:40.000,38.93215,-77.07272,71,ROUTE_ID +7167,2015-08-16 17:23:14.000,38.93207,-77.07272,71,ROUTE_ID +7167,2015-08-16 17:23:28.000,38.93169,-77.07271,71,ROUTE_ID +7167,2015-08-16 17:24:32.000,38.92918,-77.07319,71,ROUTE_ID +7167,2015-08-16 17:25:12.000,38.92811,-77.07327,71,ROUTE_ID +7167,2015-08-16 17:26:16.000,38.92395,-77.07324,71,ROUTE_ID +7167,2015-08-16 17:27:10.000,38.92191,-77.0725,71,ROUTE_ID +7167,2015-08-16 17:28:12.000,38.92023,-77.07158,71,ROUTE_ID +7167,2015-08-16 17:28:39.000,38.92001,-77.07143,71,ROUTE_ID +7167,2015-08-16 17:29:42.000,38.91737,-77.0692,71,ROUTE_ID +7167,2015-08-16 17:30:03.000,38.91708,-77.06891,71,ROUTE_ID +7167,2015-08-16 17:30:34.000,38.91594,-77.06796,71,ROUTE_ID +7167,2015-08-16 17:31:38.000,38.91366,-77.06667,71,ROUTE_ID +7167,2015-08-16 17:32:11.000,38.91105,-77.06525,71,ROUTE_ID +7167,2015-08-16 17:32:54.000,38.91073,-77.06512,71,ROUTE_ID +7167,2015-08-16 17:33:28.000,38.90935,-77.06441,71,ROUTE_ID +7167,2015-08-16 17:33:52.000,38.90921,-77.06433,71,ROUTE_ID +7167,2015-08-16 17:34:48.000,38.90709,-77.06318,71,ROUTE_ID +7167,2015-08-16 17:35:22.000,38.90646,-77.06286,71,ROUTE_ID +7167,2015-08-16 17:35:54.000,38.90548,-77.06285,71,ROUTE_ID +7167,2015-08-16 17:37:00.000,38.90548,-77.06285,71,ROUTE_ID +7167,2015-08-16 17:37:33.000,38.90509,-77.06237,71,ROUTE_ID +7167,2015-08-16 17:38:34.000,38.90516,-77.06116,71,ROUTE_ID +7167,2015-08-16 17:39:07.000,38.90514,-77.06055,71,ROUTE_ID +7167,2015-08-16 17:39:56.000,38.9052,-77.05985,71,ROUTE_ID +7167,2015-08-16 17:40:32.000,38.90522,-77.05912,71,ROUTE_ID +7167,2015-08-16 17:41:03.000,38.90488,-77.05697,71,ROUTE_ID +7167,2015-08-16 17:42:08.000,38.90378,-77.05397,71,ROUTE_ID +7167,2015-08-16 17:42:38.000,38.90292,-77.05164,71,ROUTE_ID +7167,2015-08-16 17:43:12.000,38.90292,-77.05164,71,ROUTE_ID +7167,2015-08-16 17:44:15.000,38.90199,-77.05006,71,ROUTE_ID +7167,2015-08-16 17:44:45.000,38.90163,-77.04784,71,ROUTE_ID +7167,2015-08-16 17:45:55.000,38.90118,-77.04659,71,ROUTE_ID +7167,2015-08-16 17:46:28.000,38.90028,-77.04378,71,ROUTE_ID +7167,2015-08-16 17:47:32.000,38.90014,-77.04204,71,ROUTE_ID +7167,2015-08-16 17:48:31.000,38.90026,-77.03658,71,ROUTE_ID +7167,2015-08-16 17:49:03.000,38.90014,-77.03533,71,ROUTE_ID +7167,2015-08-16 17:49:48.000,38.90014,-77.0351,71,ROUTE_ID +7167,2015-08-16 17:50:35.000,38.89913,-77.03378,71,ROUTE_ID +7167,2015-08-16 17:51:09.000,38.89768,-77.03373,71,ROUTE_ID +7167,2015-08-16 17:51:39.000,38.89664,-77.03369,71,ROUTE_ID +7167,2015-08-16 17:52:11.000,38.89664,-77.03369,71,ROUTE_ID +7167,2015-08-16 17:52:41.000,38.89542,-77.03316,71,ROUTE_ID +7167,2015-08-16 17:53:13.000,38.89543,-77.03229,71,ROUTE_ID +7167,2015-08-16 17:54:20.000,38.89543,-77.03189,71,ROUTE_ID +7167,2015-08-16 17:54:53.000,38.89509,-77.02893,71,ROUTE_ID +7167,2015-08-16 17:55:12.000,38.89502,-77.02863,71,ROUTE_ID +7167,2015-08-16 17:56:18.000,38.89471,-77.02742,71,ROUTE_ID +7167,2015-08-16 17:56:58.000,38.89399,-77.02489,71,ROUTE_ID +7167,2015-08-16 17:57:30.000,38.89386,-77.02437,71,ROUTE_ID +7167,2015-08-16 17:58:10.000,38.89342,-77.02277,71,ROUTE_ID +7167,2015-08-16 17:59:14.000,38.89326,-77.02218,71,ROUTE_ID +7167,2015-08-16 18:00:29.000,38.89202,-77.022,71,ROUTE_ID +7167,2015-08-16 18:01:03.000,38.88961,-77.02194,71,ROUTE_ID +7167,2015-08-16 18:01:33.000,38.88775,-77.0219,71,ROUTE_ID +7167,2015-08-16 18:02:06.000,38.88775,-77.0219,71,ROUTE_ID +7167,2015-08-16 18:03:34.000,38.88751,-77.01788,71,ROUTE_ID +7167,2015-08-16 18:03:50.000,38.88746,-77.01749,71,ROUTE_ID +7167,2015-08-16 18:04:25.000,38.88746,-77.01515,71,ROUTE_ID +7167,2015-08-16 18:04:59.000,38.88758,-77.0107,71,ROUTE_ID +7167,2015-08-16 18:05:53.000,38.88762,-77.00646,71,ROUTE_ID +7167,2015-08-16 18:06:26.000,38.88761,-77.00557,71,ROUTE_ID +7167,2015-08-16 18:07:18.000,38.8869,-77.00204,71,ROUTE_ID +7167,2015-08-16 18:07:42.000,38.88597,-76.99965,71,ROUTE_ID +7167,2015-08-16 18:08:26.000,38.88414,-76.99525,71,ROUTE_ID +7167,2015-08-16 18:09:29.000,38.88366,-76.99405,71,ROUTE_ID +7167,2015-08-16 18:09:57.000,38.88319,-76.99291,71,ROUTE_ID +7167,2015-08-16 18:10:39.000,38.88266,-76.99155,71,ROUTE_ID +7167,2015-08-16 18:11:12.000,38.88233,-76.99072,71,ROUTE_ID +7167,2015-08-16 18:11:42.000,38.88136,-76.98846,71,ROUTE_ID +7167,2015-08-16 18:12:39.000,38.88129,-76.98808,71,ROUTE_ID +7167,2015-08-16 18:13:12.000,38.88047,-76.98608,71,ROUTE_ID +7167,2015-08-16 18:13:46.000,38.88008,-76.98518,71,ROUTE_ID +7167,2015-08-16 18:14:56.000,38.87869,-76.98186,71,ROUTE_ID +7167,2015-08-16 18:15:29.000,38.87804,-76.98079,71,ROUTE_ID +7167,2015-08-16 18:16:01.000,38.87679,-76.978,71,ROUTE_ID +7167,2015-08-16 18:17:07.000,38.87372,-76.97161,71,ROUTE_ID +7167,2015-08-16 18:18:03.000,38.87339,-76.97084,71,ROUTE_ID +7167,2015-08-16 18:18:44.000,38.87242,-76.96808,71,ROUTE_ID +7167,2015-08-16 18:19:19.000,38.87103,-76.96468,71,ROUTE_ID +7167,2015-08-16 18:19:50.000,38.8707,-76.96389,71,ROUTE_ID +7167,2015-08-16 18:20:56.000,38.86932,-76.9608,71,ROUTE_ID +7167,2015-08-16 18:21:39.000,38.86703,-76.96004,71,ROUTE_ID +7167,2015-08-16 18:22:12.000,38.86326,-76.95929,71,ROUTE_ID +7167,2015-08-16 18:22:50.000,38.86235,-76.95911,71,ROUTE_ID +7167,2015-08-16 18:23:26.000,38.86105,-76.95898,71,ROUTE_ID +7167,2015-08-16 18:23:47.000,38.85942,-76.95921,71,ROUTE_ID +7167,2015-08-16 18:24:53.000,38.85734,-76.96025,71,ROUTE_ID +7167,2015-08-16 18:26:08.000,38.85734,-76.96352,71,ROUTE_ID +7167,2015-08-16 18:27:08.000,38.85484,-76.96561,71,ROUTE_ID +7167,2015-08-16 18:27:38.000,38.85346,-76.96439,71,ROUTE_ID +7167,2015-08-16 18:28:22.000,38.852,-76.96272,71,ROUTE_ID +7167,2015-08-16 18:28:55.000,38.85173,-76.95949,71,ROUTE_ID +7167,2015-08-16 18:29:27.000,38.84944,-76.95729,71,ROUTE_ID +7167,2015-08-16 18:30:33.000,38.85075,-76.95658,71,ROUTE_ID +7167,2015-08-16 18:31:07.000,38.85075,-76.95658,71,ROUTE_ID +7167,2015-08-16 18:32:09.000,38.84999,-76.95643,71,ROUTE_ID +7167,2015-08-16 18:32:38.000,38.84999,-76.95643,71,ROUTE_ID +7167,2015-08-16 18:33:40.000,38.84999,-76.95643,71,ROUTE_ID +7167,2015-08-16 18:34:01.000,38.84999,-76.95643,71,ROUTE_ID +7167,2015-08-16 18:34:33.000,38.84999,-76.95643,71,ROUTE_ID +7167,2015-08-16 18:35:35.000,38.84999,-76.95643,71,ROUTE_ID +7167,2015-08-16 18:36:08.000,38.84999,-76.95643,71,ROUTE_ID +7167,2015-08-16 18:36:38.000,38.84999,-76.95643,71,ROUTE_ID +7167,2015-08-16 18:37:11.000,38.84999,-76.95643,71,ROUTE_ID +7167,2015-08-16 18:38:14.000,38.84999,-76.95643,71,ROUTE_ID +7167,2015-08-16 18:39:01.000,38.84999,-76.95643,71,ROUTE_ID +7167,2015-08-16 18:39:34.000,38.84999,-76.9566,71,ROUTE_ID +7167,2015-08-16 18:40:06.000,38.84999,-76.9566,71,ROUTE_ID +7167,2015-08-16 18:41:09.000,38.84999,-76.9566,71,ROUTE_ID +7167,2015-08-16 18:41:39.000,38.84999,-76.9566,71,ROUTE_ID +7167,2015-08-16 18:42:43.000,38.85077,-76.9583,71,ROUTE_ID +7167,2015-08-16 18:43:17.000,38.85145,-76.95901,71,ROUTE_ID +7167,2015-08-16 18:43:50.000,38.85145,-76.95901,71,ROUTE_ID +7167,2015-08-16 18:44:23.000,38.85201,-76.96033,71,ROUTE_ID +7167,2015-08-16 18:45:16.000,38.85159,-76.96459,71,ROUTE_ID +7167,2015-08-16 18:45:53.000,38.85255,-76.96564,71,ROUTE_ID +7167,2015-08-16 18:46:26.000,38.85447,-76.9656,71,ROUTE_ID +7167,2015-08-16 18:47:46.000,38.85716,-76.96432,71,ROUTE_ID +7167,2015-08-16 18:48:21.000,38.85728,-76.96169,71,ROUTE_ID +7167,2015-08-16 18:49:20.000,38.85848,-76.96017,71,ROUTE_ID +7167,2015-08-16 18:49:55.000,38.85945,-76.95934,71,ROUTE_ID +7167,2015-08-16 18:50:28.000,38.86137,-76.95888,71,ROUTE_ID +7167,2015-08-16 18:51:01.000,38.86174,-76.95892,71,ROUTE_ID +7167,2015-08-16 18:51:33.000,38.86209,-76.95899,71,ROUTE_ID +7167,2015-08-16 18:52:34.000,38.8684,-76.96015,71,ROUTE_ID +7167,2015-08-16 18:53:23.000,38.8696,-76.96095,71,ROUTE_ID +7167,2015-08-16 18:54:17.000,38.87265,-76.96838,71,ROUTE_ID +7167,2015-08-16 18:54:39.000,38.87295,-76.96912,71,ROUTE_ID +7167,2015-08-16 18:55:18.000,38.87442,-76.97224,71,ROUTE_ID +7167,2015-08-16 18:55:33.000,38.8748,-76.97318,71,ROUTE_ID +7167,2015-08-16 18:56:07.000,38.87772,-76.97956,71,ROUTE_ID +7167,2015-08-16 18:56:37.000,38.87941,-76.98289,71,ROUTE_ID +7167,2015-08-16 18:57:40.000,38.88067,-76.98604,71,ROUTE_ID +7167,2015-08-16 18:58:12.000,38.88067,-76.98604,71,ROUTE_ID +7167,2015-08-16 18:58:43.000,38.88145,-76.98792,71,ROUTE_ID +7167,2015-08-16 18:59:58.000,38.88406,-76.99433,71,ROUTE_ID +7167,2015-08-16 19:00:05.000,38.88443,-76.99512,71,ROUTE_ID +7167,2015-08-16 19:00:35.000,38.88454,-76.99543,71,ROUTE_ID +7167,2015-08-16 19:01:38.000,38.88454,-76.99543,71,ROUTE_ID +7167,2015-08-16 19:02:41.000,38.88454,-76.99543,71,ROUTE_ID +7167,2015-08-16 19:03:13.000,38.88454,-76.99543,71,ROUTE_ID +7167,2015-08-16 19:03:39.000,38.88454,-76.99543,71,ROUTE_ID +7167,2015-08-16 19:04:11.000,38.88484,-76.99621,71,ROUTE_ID +7167,2015-08-16 19:05:08.000,38.88723,-77.00191,71,ROUTE_ID +7167,2015-08-16 19:05:38.000,38.88768,-77.00557,71,ROUTE_ID +7167,2015-08-16 19:06:40.000,38.88763,-77.01003,71,ROUTE_ID +7167,2015-08-16 19:07:09.000,38.88763,-77.01357,71,ROUTE_ID +7167,2015-08-16 19:07:51.000,38.88766,-77.01768,71,ROUTE_ID +7167,2015-08-16 19:08:54.000,38.88846,-77.02179,71,ROUTE_ID +7167,2015-08-16 19:09:30.000,38.89121,-77.02184,71,ROUTE_ID +7167,2015-08-16 19:10:02.000,38.89183,-77.0218,71,ROUTE_ID +7167,2015-08-16 19:10:30.000,38.89204,-77.02168,71,ROUTE_ID +7167,2015-08-16 19:11:05.000,38.89209,-77.02016,71,ROUTE_ID +7167,2015-08-16 19:12:08.000,38.89248,-77.01987,71,ROUTE_ID +7167,2015-08-16 19:12:38.000,38.89323,-77.02118,71,ROUTE_ID +7167,2015-08-16 19:13:12.000,38.89356,-77.02229,71,ROUTE_ID +7167,2015-08-16 19:14:10.000,38.89392,-77.02393,71,ROUTE_ID +7167,2015-08-16 19:14:40.000,38.89436,-77.02541,71,ROUTE_ID +7167,2015-08-16 19:15:31.000,38.89538,-77.02904,71,ROUTE_ID +7167,2015-08-16 19:16:05.000,38.89558,-77.02972,71,ROUTE_ID +7167,2015-08-16 19:17:00.000,38.89556,-77.03223,71,ROUTE_ID +7167,2015-08-16 19:17:09.000,38.89556,-77.0331,71,ROUTE_ID +7167,2015-08-16 19:18:30.000,38.89621,-77.0336,71,ROUTE_ID +7167,2015-08-16 19:19:14.000,38.89799,-77.03365,71,ROUTE_ID +7167,2015-08-16 19:19:48.000,38.90023,-77.03363,71,ROUTE_ID +7167,2015-08-16 19:20:20.000,38.90025,-77.03364,71,ROUTE_ID +7167,2015-08-16 19:21:25.000,38.90137,-77.03387,71,ROUTE_ID +7167,2015-08-16 19:22:02.000,38.90139,-77.03427,71,ROUTE_ID +7167,2015-08-16 19:22:33.000,38.9014,-77.03439,71,ROUTE_ID +7167,2015-08-16 19:23:37.000,38.90143,-77.03941,71,ROUTE_ID +7167,2015-08-16 19:24:19.000,38.9011,-77.04487,71,ROUTE_ID +7167,2015-08-16 19:24:54.000,38.90121,-77.04575,71,ROUTE_ID +7167,2015-08-16 19:25:27.000,38.90209,-77.04856,71,ROUTE_ID +7167,2015-08-16 19:26:31.000,38.90255,-77.04933,71,ROUTE_ID +7167,2015-08-16 19:27:08.000,38.90368,-77.05311,71,ROUTE_ID +7167,2015-08-16 19:28:03.000,38.9043,-77.05484,71,ROUTE_ID +7167,2015-08-16 19:28:41.000,38.90497,-77.05682,71,ROUTE_ID +7167,2015-08-16 19:29:35.000,38.90531,-77.05982,71,ROUTE_ID +7167,2015-08-16 19:30:08.000,38.90532,-77.06155,71,ROUTE_ID +7167,2015-08-16 19:30:35.000,38.90535,-77.06191,71,ROUTE_ID +7167,2015-08-16 19:31:41.000,38.9054,-77.06256,71,ROUTE_ID +7167,2015-08-16 19:32:49.000,38.90689,-77.06313,71,ROUTE_ID +7167,2015-08-16 19:33:22.000,38.90742,-77.06344,71,ROUTE_ID +7167,2015-08-16 19:34:26.000,38.90923,-77.06445,71,ROUTE_ID +7167,2015-08-16 19:35:16.000,38.91091,-77.06539,71,ROUTE_ID +7167,2015-08-16 19:35:52.000,38.91245,-77.06614,71,ROUTE_ID +7167,2015-08-16 19:37:43.000,38.916,-77.06795,71,ROUTE_ID +7167,2015-08-16 19:39:06.000,38.92262,-77.07291,71,ROUTE_ID +7167,2015-08-16 19:39:29.000,38.9226,-77.07289,71,ROUTE_ID +7167,2015-08-16 19:39:54.000,38.92492,-77.07313,71,ROUTE_ID +7167,2015-08-16 19:41:40.000,38.92844,-77.07308,71,ROUTE_ID +7167,2015-08-16 19:42:29.000,38.93144,-77.0726,71,ROUTE_ID +7167,2015-08-16 19:43:24.000,38.93194,-77.07252,71,ROUTE_ID +7167,2015-08-16 19:44:10.000,38.93489,-77.07258,71,ROUTE_ID +7167,2015-08-16 19:45:13.000,38.93713,-77.07362,71,ROUTE_ID +7167,2015-08-16 19:45:38.000,38.93742,-77.07389,71,ROUTE_ID +7167,2015-08-16 19:46:30.000,38.94225,-77.07642,71,ROUTE_ID +7167,2015-08-16 19:47:15.000,38.94414,-77.07753,71,ROUTE_ID +7167,2015-08-16 19:47:49.000,38.9459,-77.07848,71,ROUTE_ID +7167,2015-08-16 19:48:22.000,38.9464,-77.07874,71,ROUTE_ID +7167,2015-08-16 19:49:00.000,38.94851,-77.07993,71,ROUTE_ID +7167,2015-08-16 19:50:16.000,38.95405,-77.08224,71,ROUTE_ID +7167,2015-08-16 19:50:46.000,38.95663,-77.08354,71,ROUTE_ID +7167,2015-08-16 19:51:06.000,38.95719,-77.08381,71,ROUTE_ID +7167,2015-08-16 19:52:11.000,38.96087,-77.08562,71,ROUTE_ID +7167,2015-08-16 19:52:46.000,38.96186,-77.08491,71,ROUTE_ID +7167,2015-08-16 19:53:48.000,38.96159,-77.0858,71,ROUTE_ID +7167,2015-08-16 19:54:20.000,38.96159,-77.0858,71,ROUTE_ID +7167,2015-08-16 19:55:21.000,38.96159,-77.0858,71,ROUTE_ID +7167,2015-08-16 19:55:54.000,38.96159,-77.0858,71,ROUTE_ID +7167,2015-08-16 19:56:26.000,38.96159,-77.0858,71,ROUTE_ID +7167,2015-08-16 19:57:32.000,38.96159,-77.0858,71,ROUTE_ID +7167,2015-08-16 19:58:05.000,38.96159,-77.0858,71,ROUTE_ID +7167,2015-08-16 19:58:36.000,38.96159,-77.0858,71,ROUTE_ID +7167,2015-08-16 19:59:39.000,38.96159,-77.0858,71,ROUTE_ID +7167,2015-08-16 20:00:12.000,38.96156,-77.08588,71,ROUTE_ID +7167,2015-08-16 20:00:43.000,38.96156,-77.08588,71,ROUTE_ID +7167,2015-08-16 20:01:00.000,38.96156,-77.08588,71,ROUTE_ID +7167,2015-08-16 20:02:05.000,38.96156,-77.08588,71,ROUTE_ID +7167,2015-08-16 20:02:35.000,38.96156,-77.08588,71,ROUTE_ID +7167,2015-08-16 20:03:38.000,38.96156,-77.08588,71,ROUTE_ID +7167,2015-08-16 20:04:41.000,38.96156,-77.08588,71,ROUTE_ID +7167,2015-08-16 20:05:13.000,38.96156,-77.08588,71,ROUTE_ID +7167,2015-08-16 20:05:48.000,38.96156,-77.08588,71,ROUTE_ID +7167,2015-08-16 20:06:20.000,38.96156,-77.08588,71,ROUTE_ID +7167,2015-08-16 20:07:01.000,38.96155,-77.08587,71,ROUTE_ID +7167,2015-08-16 20:08:05.000,38.96198,-77.086,71,ROUTE_ID +7167,2015-08-16 20:08:35.000,38.96198,-77.086,71,ROUTE_ID +7167,2015-08-16 20:09:08.000,38.96198,-77.086,71,ROUTE_ID +7167,2015-08-16 20:10:11.000,38.96139,-77.08569,71,ROUTE_ID +7167,2015-08-16 20:10:41.000,38.96149,-77.08551,71,ROUTE_ID +7167,2015-08-16 20:11:14.000,38.96173,-77.08495,71,ROUTE_ID +7167,2015-08-16 20:11:46.000,38.96173,-77.08495,71,ROUTE_ID +7167,2015-08-16 20:12:18.000,38.96196,-77.08508,71,ROUTE_ID +7167,2015-08-16 20:12:51.000,38.96198,-77.086,71,ROUTE_ID +7167,2015-08-16 20:13:31.000,38.96061,-77.08568,71,ROUTE_ID +7167,2015-08-16 20:14:33.000,38.95852,-77.08463,71,ROUTE_ID +7167,2015-08-16 20:15:59.000,38.95531,-77.08292,71,ROUTE_ID +7167,2015-08-16 20:16:29.000,38.95458,-77.08282,71,ROUTE_ID +7167,2015-08-16 20:17:03.000,38.95218,-77.08173,71,ROUTE_ID +7167,2015-08-16 20:18:32.000,38.95026,-77.08057,71,ROUTE_ID +7167,2015-08-16 20:18:46.000,38.94949,-77.08038,71,ROUTE_ID +7167,2015-08-16 20:19:20.000,38.94874,-77.0802,71,ROUTE_ID +7167,2015-08-16 20:19:52.000,38.94806,-77.07982,71,ROUTE_ID +7167,2015-08-16 20:20:48.000,38.94616,-77.07876,71,ROUTE_ID +7167,2015-08-16 20:21:22.000,38.94612,-77.07877,71,ROUTE_ID +7167,2015-08-16 20:22:10.000,38.94361,-77.0773,71,ROUTE_ID +7167,2015-08-16 20:22:49.000,38.94102,-77.07585,71,ROUTE_ID +7167,2015-08-16 20:23:34.000,38.93936,-77.07498,71,ROUTE_ID +7167,2015-08-16 20:24:08.000,38.93753,-77.07402,71,ROUTE_ID +7167,2015-08-16 20:24:38.000,38.93648,-77.07343,71,ROUTE_ID +7167,2015-08-16 20:25:36.000,38.93432,-77.07249,71,ROUTE_ID +7167,2015-08-16 20:26:30.000,38.93235,-77.07266,71,ROUTE_ID +7167,2015-08-16 20:27:02.000,38.93195,-77.07271,71,ROUTE_ID +7167,2015-08-16 20:27:58.000,38.92927,-77.07316,71,ROUTE_ID +7167,2015-08-16 20:28:37.000,38.92816,-77.07327,71,ROUTE_ID +7167,2015-08-16 20:29:54.000,38.92383,-77.07324,71,ROUTE_ID +7167,2015-08-16 20:30:26.000,38.92268,-77.0731,71,ROUTE_ID +7167,2015-08-16 20:30:58.000,38.92266,-77.07309,71,ROUTE_ID +7167,2015-08-16 20:32:02.000,38.92023,-77.07158,71,ROUTE_ID +7167,2015-08-16 20:32:33.000,38.9201,-77.07151,71,ROUTE_ID +7167,2015-08-16 20:33:14.000,38.91951,-77.07096,71,ROUTE_ID +7167,2015-08-16 20:33:46.000,38.91825,-77.06989,71,ROUTE_ID +7167,2015-08-16 20:34:19.000,38.91785,-77.06959,71,ROUTE_ID +7167,2015-08-16 20:35:36.000,38.9139,-77.06684,71,ROUTE_ID +7167,2015-08-16 20:36:06.000,38.91383,-77.06679,71,ROUTE_ID +7167,2015-08-16 20:37:17.000,38.91071,-77.0651,71,ROUTE_ID +7167,2015-08-16 20:37:50.000,38.90942,-77.06445,71,ROUTE_ID +7167,2015-08-16 20:38:57.000,38.90773,-77.06355,71,ROUTE_ID +7167,2015-08-16 20:39:36.000,38.90713,-77.06322,71,ROUTE_ID +7167,2015-08-16 20:40:39.000,38.90544,-77.06284,71,ROUTE_ID +7167,2015-08-16 20:41:11.000,38.9051,-77.06244,71,ROUTE_ID +7167,2015-08-16 20:41:54.000,38.90513,-77.06184,71,ROUTE_ID +7167,2015-08-16 20:42:26.000,38.90514,-77.06042,71,ROUTE_ID +7167,2015-08-16 20:42:58.000,38.9052,-77.05991,71,ROUTE_ID +7167,2015-08-16 20:44:04.000,38.90497,-77.05751,71,ROUTE_ID +7167,2015-08-16 20:44:34.000,38.90497,-77.05751,71,ROUTE_ID +7167,2015-08-16 20:44:49.000,38.90486,-77.05688,71,ROUTE_ID +7167,2015-08-16 20:46:13.000,38.90296,-77.05171,71,ROUTE_ID +7167,2015-08-16 20:46:43.000,38.90296,-77.05171,71,ROUTE_ID +7167,2015-08-16 20:47:05.000,38.90281,-77.05131,71,ROUTE_ID +7167,2015-08-16 20:48:12.000,38.90204,-77.04914,71,ROUTE_ID +7167,2015-08-16 20:48:49.000,38.90192,-77.04868,71,ROUTE_ID +7167,2015-08-16 20:49:54.000,38.90025,-77.0437,71,ROUTE_ID +7167,2015-08-16 20:50:26.000,38.90025,-77.0437,71,ROUTE_ID +7167,2015-08-16 20:50:58.000,38.90025,-77.0437,71,ROUTE_ID +7167,2015-08-16 20:52:13.000,38.90027,-77.0395,71,ROUTE_ID +7167,2015-08-16 20:52:43.000,38.90026,-77.0393,71,ROUTE_ID +7167,2015-08-16 20:53:18.000,38.90022,-77.0359,71,ROUTE_ID +7167,2015-08-16 20:53:51.000,38.90014,-77.0353,71,ROUTE_ID +7167,2015-08-16 20:55:03.000,38.90015,-77.03401,71,ROUTE_ID +7167,2015-08-16 20:55:34.000,38.90012,-77.03391,71,ROUTE_ID +7167,2015-08-16 20:56:06.000,38.89971,-77.03378,71,ROUTE_ID +7167,2015-08-16 20:57:09.000,38.89743,-77.03372,71,ROUTE_ID +7167,2015-08-16 20:57:39.000,38.89742,-77.03372,71,ROUTE_ID +7167,2015-08-16 20:58:12.000,38.89567,-77.03366,71,ROUTE_ID +7167,2015-08-16 20:59:14.000,38.89543,-77.0322,71,ROUTE_ID +7167,2015-08-16 20:59:43.000,38.89543,-77.03191,71,ROUTE_ID +7167,2015-08-16 21:00:16.000,38.8954,-77.03,71,ROUTE_ID +7167,2015-08-16 21:01:13.000,38.89497,-77.02851,71,ROUTE_ID +7167,2015-08-16 21:01:43.000,38.89465,-77.02723,71,ROUTE_ID +7167,2015-08-16 21:02:37.000,38.89386,-77.02435,71,ROUTE_ID +7167,2015-08-16 21:03:10.000,38.89331,-77.02239,71,ROUTE_ID +7167,2015-08-16 21:03:40.000,38.89331,-77.02238,71,ROUTE_ID +7167,2015-08-16 21:04:45.000,38.89311,-77.022,71,ROUTE_ID +7167,2015-08-16 21:05:00.000,38.89239,-77.02202,71,ROUTE_ID +7167,2015-08-16 21:05:35.000,38.89142,-77.02195,71,ROUTE_ID +7167,2015-08-16 21:06:37.000,38.88944,-77.02194,71,ROUTE_ID +7167,2015-08-16 21:07:09.000,38.88776,-77.0219,71,ROUTE_ID +7167,2015-08-16 21:07:39.000,38.88776,-77.0219,71,ROUTE_ID +7167,2015-08-16 21:08:26.000,38.88748,-77.02019,71,ROUTE_ID +7167,2015-08-16 21:09:01.000,38.88748,-77.02004,71,ROUTE_ID +7167,2015-08-16 21:09:33.000,38.88748,-77.0163,71,ROUTE_ID +7167,2015-08-16 21:10:45.000,38.88762,-77.00831,71,ROUTE_ID +7167,2015-08-16 21:11:18.000,38.88762,-77.00629,71,ROUTE_ID +7167,2015-08-16 21:11:58.000,38.88742,-77.00341,71,ROUTE_ID +7167,2015-08-16 21:12:34.000,38.88684,-77.00192,71,ROUTE_ID +7167,2015-08-16 21:13:05.000,38.88597,-76.99967,71,ROUTE_ID +7167,2015-08-16 21:14:11.000,38.88417,-76.99532,71,ROUTE_ID +7167,2015-08-16 21:14:40.000,38.88414,-76.99525,71,ROUTE_ID +7167,2015-08-16 21:15:32.000,38.88293,-76.99231,71,ROUTE_ID +7167,2015-08-16 21:16:53.000,38.88138,-76.98837,71,ROUTE_ID +7167,2015-08-16 21:17:27.000,38.88026,-76.98563,71,ROUTE_ID +7167,2015-08-16 21:18:00.000,38.88017,-76.98543,71,ROUTE_ID +7167,2015-08-16 21:18:34.000,38.88011,-76.98526,71,ROUTE_ID +7167,2015-08-16 21:19:21.000,38.87932,-76.98351,71,ROUTE_ID +7167,2015-08-16 21:20:26.000,38.87597,-76.97634,71,ROUTE_ID +7167,2015-08-16 21:21:29.000,38.87386,-76.97184,71,ROUTE_ID +7167,2015-08-16 21:22:01.000,38.8737,-76.97159,71,ROUTE_ID +7167,2015-08-16 21:22:33.000,38.8737,-76.97159,71,ROUTE_ID +7167,2015-08-16 21:24:00.000,38.87239,-76.96799,71,ROUTE_ID +7167,2015-08-16 21:24:36.000,38.87108,-76.96481,71,ROUTE_ID +7167,2015-08-16 21:25:40.000,38.87041,-76.96317,71,ROUTE_ID +7167,2015-08-16 21:26:41.000,38.86929,-76.96074,71,ROUTE_ID +7167,2015-08-16 21:27:30.000,38.86895,-76.96041,71,ROUTE_ID +7167,2015-08-16 21:28:49.000,38.86208,-76.95905,71,ROUTE_ID +7167,2015-08-16 21:29:21.000,38.86206,-76.95905,71,ROUTE_ID +7167,2015-08-16 21:30:28.000,38.8597,-76.95899,71,ROUTE_ID +7167,2015-08-16 21:30:41.000,38.85942,-76.95921,71,ROUTE_ID +7167,2015-08-16 21:31:50.000,38.85735,-76.96024,71,ROUTE_ID +7167,2015-08-16 21:32:24.000,38.85734,-76.96172,71,ROUTE_ID +7167,2015-08-16 21:32:55.000,38.85732,-76.96371,71,ROUTE_ID +7167,2015-08-16 21:33:12.000,38.85706,-76.96465,71,ROUTE_ID +7167,2015-08-16 21:33:44.000,38.85516,-76.96544,71,ROUTE_ID +7167,2015-08-16 21:34:18.000,38.85498,-76.96557,71,ROUTE_ID +7167,2015-08-16 21:34:59.000,38.85488,-76.96561,71,ROUTE_ID +7167,2015-08-16 21:35:32.000,38.85347,-76.96439,71,ROUTE_ID +7167,2015-08-16 21:36:31.000,38.8519,-76.96231,71,ROUTE_ID +7167,2015-08-16 21:37:04.000,38.8519,-76.96231,71,ROUTE_ID +7167,2015-08-16 21:38:38.000,38.85159,-76.95921,71,ROUTE_ID +7167,2015-08-16 21:39:10.000,38.84991,-76.95804,71,ROUTE_ID +7167,2015-08-16 21:40:38.000,38.85098,-76.95639,71,ROUTE_ID +7167,2015-08-16 21:41:11.000,38.85098,-76.95639,71,ROUTE_ID +7167,2015-08-16 21:42:12.000,38.85098,-76.95639,71,ROUTE_ID +7167,2015-08-16 21:42:42.000,38.85098,-76.95639,71,ROUTE_ID +7167,2015-08-16 21:43:49.000,38.85098,-76.95639,71,ROUTE_ID +7167,2015-08-16 21:44:22.000,38.85098,-76.95639,71,ROUTE_ID +7167,2015-08-16 21:44:55.000,38.85098,-76.95639,71,ROUTE_ID +7167,2015-08-16 21:46:01.000,38.85098,-76.95639,71,ROUTE_ID +7167,2015-08-16 21:46:34.000,38.85098,-76.95639,71,ROUTE_ID +7167,2015-08-16 21:47:08.000,38.85098,-76.95639,71,ROUTE_ID +7167,2015-08-16 21:48:10.000,38.85084,-76.95662,71,ROUTE_ID +7167,2015-08-16 21:48:40.000,38.84999,-76.9566,71,ROUTE_ID +7167,2015-08-16 21:49:13.000,38.84951,-76.9565,71,ROUTE_ID +7167,2015-08-16 21:49:43.000,38.84984,-76.95788,71,ROUTE_ID +7167,2015-08-16 21:50:16.000,38.85153,-76.95918,71,ROUTE_ID +7167,2015-08-16 21:51:23.000,38.85149,-76.96262,71,ROUTE_ID +7167,2015-08-16 21:51:58.000,38.85084,-76.96347,71,ROUTE_ID +7167,2015-08-16 21:52:33.000,38.85218,-76.96534,71,ROUTE_ID +7167,2015-08-16 21:53:20.000,38.85425,-76.96558,71,ROUTE_ID +7167,2015-08-16 21:53:47.000,38.8547,-76.96552,71,ROUTE_ID +7167,2015-08-16 21:54:57.000,38.85729,-76.96252,71,ROUTE_ID +7167,2015-08-16 21:55:39.000,38.85738,-76.96022,71,ROUTE_ID +7167,2015-08-16 21:56:11.000,38.8586,-76.96017,71,ROUTE_ID +7167,2015-08-16 21:57:14.000,38.85943,-76.95959,71,ROUTE_ID +7167,2015-08-16 21:58:22.000,38.85944,-76.95908,71,ROUTE_ID +7167,2015-08-16 21:59:07.000,38.86041,-76.95888,71,ROUTE_ID +7167,2015-08-16 21:59:25.000,38.8606,-76.95888,71,ROUTE_ID +7167,2015-08-16 22:00:40.000,38.86547,-76.9596,71,ROUTE_ID +7167,2015-08-16 22:01:16.000,38.86839,-76.96015,71,ROUTE_ID +7167,2015-08-16 22:01:48.000,38.86904,-76.9603,71,ROUTE_ID +7167,2015-08-16 22:02:20.000,38.86904,-76.9603,71,ROUTE_ID +7167,2015-08-16 22:03:03.000,38.86963,-76.96101,71,ROUTE_ID +7167,2015-08-16 22:04:01.000,38.87108,-76.96465,71,ROUTE_ID +7167,2015-08-16 22:04:24.000,38.87121,-76.96496,71,ROUTE_ID +7167,2015-08-16 22:05:03.000,38.87193,-76.9667,71,ROUTE_ID +7167,2015-08-16 22:05:44.000,38.87288,-76.96895,71,ROUTE_ID +7167,2015-08-16 22:06:16.000,38.87295,-76.96912,71,ROUTE_ID +7167,2015-08-16 22:06:50.000,38.87374,-76.97081,71,ROUTE_ID +7167,2015-08-16 22:07:23.000,38.87376,-76.97086,71,ROUTE_ID +7167,2015-08-16 22:07:56.000,38.87377,-76.97088,71,ROUTE_ID +7167,2015-08-16 22:08:42.000,38.8744,-76.97221,71,ROUTE_ID +7167,2015-08-16 22:09:34.000,38.87677,-76.97763,71,ROUTE_ID +7167,2015-08-16 22:10:08.000,38.87877,-76.98134,71,ROUTE_ID +7167,2015-08-16 22:10:38.000,38.87991,-76.9841,71,ROUTE_ID +7167,2015-08-16 22:11:42.000,38.88049,-76.98548,71,ROUTE_ID +7167,2015-08-16 22:12:19.000,38.88078,-76.98631,71,ROUTE_ID +7167,2015-08-16 22:13:16.000,38.8818,-76.98873,71,ROUTE_ID +7167,2015-08-16 22:13:49.000,38.88192,-76.98904,71,ROUTE_ID +7167,2015-08-16 22:14:51.000,38.88355,-76.99306,71,ROUTE_ID +7167,2015-08-16 22:15:23.000,38.88426,-76.99474,71,ROUTE_ID +7167,2015-08-16 22:16:47.000,38.88572,-76.99837,71,ROUTE_ID +7167,2015-08-16 22:17:03.000,38.88584,-76.99867,71,ROUTE_ID +7167,2015-08-16 22:17:34.000,38.88697,-77.00142,71,ROUTE_ID +7167,2015-08-16 22:18:48.000,38.88769,-77.00413,71,ROUTE_ID +7167,2015-08-16 22:19:21.000,38.8877,-77.00577,71,ROUTE_ID +7167,2015-08-16 22:19:48.000,38.88765,-77.00826,71,ROUTE_ID +7167,2015-08-16 22:20:31.000,38.88763,-77.01358,71,ROUTE_ID +7167,2015-08-16 22:21:06.000,38.88766,-77.01768,71,ROUTE_ID +7167,2015-08-16 22:21:52.000,38.88762,-77.01981,71,ROUTE_ID +7167,2015-08-16 22:22:24.000,38.88759,-77.0201,71,ROUTE_ID +7167,2015-08-16 22:23:29.000,38.88801,-77.02178,71,ROUTE_ID +7167,2015-08-16 22:24:01.000,38.89049,-77.02184,71,ROUTE_ID +7167,2015-08-16 22:25:07.000,38.89192,-77.02179,71,ROUTE_ID +7167,2015-08-16 22:25:26.000,38.89202,-77.02173,71,ROUTE_ID +7167,2015-08-16 22:26:01.000,38.89209,-77.02034,71,ROUTE_ID +7167,2015-08-16 22:27:06.000,38.89225,-77.01988,71,ROUTE_ID +7167,2015-08-16 22:27:36.000,38.89306,-77.02058,71,ROUTE_ID +7167,2015-08-16 22:28:41.000,38.89374,-77.02319,71,ROUTE_ID +7167,2015-08-16 22:29:13.000,38.89394,-77.02397,71,ROUTE_ID +7167,2015-08-16 22:30:22.000,38.89449,-77.02586,71,ROUTE_ID +7167,2015-08-16 22:30:55.000,38.8945,-77.02589,71,ROUTE_ID +7167,2015-08-16 22:31:28.000,38.89508,-77.02802,71,ROUTE_ID +7167,2015-08-16 22:32:33.000,38.89559,-77.02985,71,ROUTE_ID +7167,2015-08-16 22:33:05.000,38.89557,-77.03201,71,ROUTE_ID +7167,2015-08-16 22:34:07.000,38.89573,-77.03358,71,ROUTE_ID +7167,2015-08-16 22:34:37.000,38.89646,-77.0336,71,ROUTE_ID +7167,2015-08-16 22:35:50.000,38.90007,-77.03362,71,ROUTE_ID +7167,2015-08-16 22:36:22.000,38.90022,-77.03363,71,ROUTE_ID +7167,2015-08-16 22:37:28.000,38.90137,-77.03387,71,ROUTE_ID +7167,2015-08-16 22:37:44.000,38.90139,-77.03462,71,ROUTE_ID +7167,2015-08-16 22:38:51.000,38.90148,-77.03697,71,ROUTE_ID +7167,2015-08-16 22:39:24.000,38.90143,-77.03928,71,ROUTE_ID +7167,2015-08-16 22:40:22.000,38.9014,-77.04149,71,ROUTE_ID +7167,2015-08-16 22:40:56.000,38.90141,-77.04247,71,ROUTE_ID +7167,2015-08-16 22:41:30.000,38.90121,-77.04568,71,ROUTE_ID +7167,2015-08-16 22:42:35.000,38.90183,-77.04778,71,ROUTE_ID +7167,2015-08-16 22:43:06.000,38.90213,-77.04868,71,ROUTE_ID +7167,2015-08-16 22:43:36.000,38.90213,-77.04868,71,ROUTE_ID +7167,2015-08-16 22:44:13.000,38.90222,-77.0489,71,ROUTE_ID +7167,2015-08-16 22:44:43.000,38.90234,-77.04916,71,ROUTE_ID +7167,2015-08-16 22:45:18.000,38.903,-77.0509,71,ROUTE_ID +7167,2015-08-16 22:46:30.000,38.90315,-77.05148,71,ROUTE_ID +7167,2015-08-16 22:47:02.000,38.90365,-77.05289,71,ROUTE_ID +7167,2015-08-16 22:48:07.000,38.90364,-77.05295,71,ROUTE_ID +7167,2015-08-16 22:48:12.000,38.90368,-77.05309,71,ROUTE_ID +7167,2015-08-16 22:49:09.000,38.90496,-77.0568,71,ROUTE_ID +7167,2015-08-16 22:49:41.000,38.90499,-77.05687,71,ROUTE_ID +7167,2015-08-16 22:50:43.000,38.9053,-77.05947,71,ROUTE_ID +7167,2015-08-16 22:51:11.000,38.90531,-77.05981,71,ROUTE_ID +7167,2015-08-16 22:51:48.000,38.90532,-77.06146,71,ROUTE_ID +7167,2015-08-16 22:52:19.000,38.90536,-77.06207,71,ROUTE_ID +7167,2015-08-16 22:53:25.000,38.90675,-77.06289,71,ROUTE_ID +7167,2015-08-16 22:54:12.000,38.9069,-77.06314,71,ROUTE_ID +7167,2015-08-16 22:54:43.000,38.90751,-77.06349,71,ROUTE_ID +7167,2015-08-16 22:55:15.000,38.90876,-77.06414,71,ROUTE_ID +7167,2015-08-16 22:55:49.000,38.90914,-77.0644,71,ROUTE_ID +7167,2015-08-16 22:56:21.000,38.91076,-77.06526,71,ROUTE_ID +7167,2015-08-16 22:57:38.000,38.91545,-77.06763,71,ROUTE_ID +7167,2015-08-16 22:58:12.000,38.91607,-77.068,71,ROUTE_ID +7167,2015-08-16 22:58:58.000,38.9184,-77.06995,71,ROUTE_ID +7167,2015-08-16 22:59:32.000,38.92021,-77.07143,71,ROUTE_ID +7167,2015-08-16 23:00:10.000,38.92026,-77.07147,71,ROUTE_ID +7167,2015-08-16 23:01:13.000,38.92248,-77.0728,71,ROUTE_ID +7167,2015-08-16 23:01:51.000,38.92457,-77.07311,71,ROUTE_ID +7167,2015-08-16 23:02:42.000,38.92488,-77.07313,71,ROUTE_ID +7167,2015-08-16 23:03:14.000,38.92728,-77.07312,71,ROUTE_ID +7167,2015-08-16 23:03:51.000,38.9278,-77.07308,71,ROUTE_ID +7167,2015-08-16 23:04:40.000,38.92974,-77.07289,71,ROUTE_ID +7167,2015-08-16 23:05:43.000,38.9317,-77.07251,71,ROUTE_ID +7167,2015-08-16 23:06:05.000,38.93193,-77.07252,71,ROUTE_ID +7167,2015-08-16 23:06:37.000,38.93328,-77.0723,71,ROUTE_ID +7167,2015-08-16 23:07:24.000,38.93447,-77.07243,71,ROUTE_ID +7167,2015-08-16 23:08:19.000,38.93741,-77.07388,71,ROUTE_ID +7167,2015-08-16 23:08:43.000,38.93966,-77.07503,71,ROUTE_ID +7167,2015-08-16 23:09:33.000,38.94228,-77.07644,71,ROUTE_ID +7167,2015-08-16 23:10:07.000,38.943,-77.07684,71,ROUTE_ID +7167,2015-08-16 23:11:02.000,38.94408,-77.07749,71,ROUTE_ID +7167,2015-08-16 23:11:35.000,38.94573,-77.07838,71,ROUTE_ID +7167,2015-08-16 23:12:09.000,38.94662,-77.07893,71,ROUTE_ID +7167,2015-08-16 23:13:12.000,38.94773,-77.07945,71,ROUTE_ID +7167,2015-08-16 23:13:42.000,38.94846,-77.0799,71,ROUTE_ID +7167,2015-08-16 23:14:17.000,38.95125,-77.0808,71,ROUTE_ID +7167,2015-08-16 23:15:21.000,38.95488,-77.08268,71,ROUTE_ID +7167,2015-08-16 23:16:26.000,38.95862,-77.08456,71,ROUTE_ID +7167,2015-08-16 23:17:06.000,38.95966,-77.08515,71,ROUTE_ID +7167,2015-08-16 23:17:36.000,38.96078,-77.08566,71,ROUTE_ID +7167,2015-08-16 23:18:27.000,38.96079,-77.08566,71,ROUTE_ID +7167,2015-08-16 23:19:02.000,38.96105,-77.08538,71,ROUTE_ID +7167,2015-08-17 05:35:23.000,38.96138,-77.08569,228,ROUTE_ID +7167,2015-08-17 05:35:56.000,38.96163,-77.08501,228,ROUTE_ID +7167,2015-08-17 05:36:58.000,38.962,-77.08606,228,ROUTE_ID +7167,2015-08-17 05:37:31.000,38.962,-77.08606,228,ROUTE_ID +7167,2015-08-17 05:38:34.000,38.96062,-77.08591,228,ROUTE_ID +7167,2015-08-17 05:39:04.000,38.96002,-77.08679,228,ROUTE_ID +7167,2015-08-17 05:40:48.000,38.95565,-77.09233,228,ROUTE_ID +7167,2015-08-17 05:41:39.000,38.95075,-77.09869,228,ROUTE_ID +7167,2015-08-17 05:42:48.000,38.94847,-77.10049,228,ROUTE_ID +7167,2015-08-17 05:43:09.000,38.94701,-77.09845,228,ROUTE_ID +7167,2015-08-17 05:43:42.000,38.94431,-77.09476,228,ROUTE_ID +7167,2015-08-17 05:44:25.000,38.94057,-77.08962,228,ROUTE_ID +7167,2015-08-17 05:45:14.000,38.93815,-77.08646,228,ROUTE_ID +7167,2015-08-17 05:46:00.000,38.93759,-77.08624,228,ROUTE_ID +7167,2015-08-17 05:46:32.000,38.93579,-77.08307,228,ROUTE_ID +7167,2015-08-17 05:47:01.000,38.93507,-77.08205,228,ROUTE_ID +7167,2015-08-17 05:48:01.000,38.93135,-77.07703,228,ROUTE_ID +7167,2015-08-17 05:49:42.000,38.92794,-77.07244,228,ROUTE_ID +7167,2015-08-17 05:50:14.000,38.92767,-77.07205,228,ROUTE_ID +7167,2015-08-17 05:50:44.000,38.9262,-77.06996,228,ROUTE_ID +7167,2015-08-17 05:51:45.000,38.92424,-77.06623,228,ROUTE_ID +7167,2015-08-17 05:52:16.000,38.92143,-77.06323,228,ROUTE_ID +7167,2015-08-17 05:53:21.000,38.91628,-77.05639,228,ROUTE_ID +7167,2015-08-17 05:53:48.000,38.91375,-77.05292,228,ROUTE_ID +7167,2015-08-17 05:54:02.000,38.91257,-77.05132,228,ROUTE_ID +7167,2015-08-17 05:54:58.000,38.91119,-77.04798,228,ROUTE_ID +7167,2015-08-17 05:55:30.000,38.90981,-77.04431,228,ROUTE_ID +7167,2015-08-17 05:56:16.000,38.9084,-77.04284,228,ROUTE_ID +7167,2015-08-17 05:57:05.000,38.90352,-77.04002,228,ROUTE_ID +7167,2015-08-17 05:57:39.000,38.90231,-77.03895,228,ROUTE_ID +7167,2015-08-17 05:58:45.000,38.90162,-77.03866,228,ROUTE_ID +7167,2015-08-17 05:59:17.000,38.90162,-77.03866,228,ROUTE_ID +7167,2015-08-17 05:59:49.000,38.90162,-77.03866,228,ROUTE_ID +7167,2015-08-17 06:00:54.000,38.90162,-77.03866,228,ROUTE_ID +7167,2015-08-17 06:01:59.000,38.90162,-77.03866,228,ROUTE_ID +7167,2015-08-17 06:02:31.000,38.90162,-77.03866,228,ROUTE_ID +7167,2015-08-17 06:03:01.000,38.90162,-77.03866,228,ROUTE_ID +7167,2015-08-17 06:03:35.000,38.90162,-77.03866,228,ROUTE_ID +7167,2015-08-17 06:04:39.000,38.90162,-77.03866,228,ROUTE_ID +7167,2015-08-17 06:05:09.000,38.90162,-77.03866,228,ROUTE_ID +7167,2015-08-17 06:05:42.000,38.90162,-77.03866,228,ROUTE_ID +7167,2015-08-17 06:07:01.000,38.90162,-77.03866,228,ROUTE_ID +7167,2015-08-17 06:07:35.000,38.90162,-77.0386,228,ROUTE_ID +7167,2015-08-17 06:08:05.000,38.90162,-77.0386,228,ROUTE_ID +7167,2015-08-17 06:09:12.000,38.90162,-77.0386,228,ROUTE_ID +7167,2015-08-17 06:09:44.000,38.90162,-77.0386,228,ROUTE_ID +7167,2015-08-17 06:10:49.000,38.90162,-77.0386,228,ROUTE_ID +7167,2015-08-17 06:11:22.000,38.90162,-77.0386,228,ROUTE_ID +7167,2015-08-17 06:11:55.000,38.90155,-77.03857,228,ROUTE_ID +7167,2015-08-17 06:12:51.000,38.90137,-77.0397,228,ROUTE_ID +7167,2015-08-17 06:13:26.000,38.9021,-77.0416,228,ROUTE_ID +7167,2015-08-17 06:13:52.000,38.90265,-77.04161,228,ROUTE_ID +7167,2015-08-17 06:14:58.000,38.90336,-77.04166,228,ROUTE_ID +7167,2015-08-17 06:15:31.000,38.90508,-77.04166,228,ROUTE_ID +7167,2015-08-17 06:16:34.000,38.90552,-77.04163,228,ROUTE_ID +7167,2015-08-17 06:17:04.000,38.9062,-77.04164,228,ROUTE_ID +7167,2015-08-17 06:17:38.000,38.90631,-77.04164,228,ROUTE_ID +7167,2015-08-17 06:18:35.000,38.90908,-77.04176,228,ROUTE_ID +7167,2015-08-17 06:21:51.000,38.91013,-77.04475,228,ROUTE_ID +7167,2015-08-17 06:23:06.000,38.91064,-77.04638,228,ROUTE_ID +7167,2015-08-17 06:24:09.000,38.9117,-77.04929,228,ROUTE_ID +7167,2015-08-17 06:24:31.000,38.91255,-77.05114,228,ROUTE_ID +7167,2015-08-17 06:25:10.000,38.91555,-77.05531,228,ROUTE_ID +7167,2015-08-17 06:25:51.000,38.91859,-77.05942,228,ROUTE_ID +7167,2015-08-17 06:26:16.000,38.91919,-77.0603,228,ROUTE_ID +7167,2015-08-17 06:27:04.000,38.92203,-77.06329,228,ROUTE_ID +7167,2015-08-17 06:27:56.000,38.92621,-77.06986,228,ROUTE_ID +7167,2015-08-17 06:28:30.000,38.92726,-77.07128,228,ROUTE_ID +7167,2015-08-17 06:29:28.000,38.92846,-77.07289,228,ROUTE_ID +7167,2015-08-17 06:30:05.000,38.92932,-77.07402,228,ROUTE_ID +7167,2015-08-17 06:31:10.000,38.93194,-77.07762,228,ROUTE_ID +7167,2015-08-17 06:31:40.000,38.9347,-77.08144,228,ROUTE_ID +7167,2015-08-17 06:31:57.000,38.9363,-77.0836,228,ROUTE_ID +7167,2015-08-17 06:33:03.000,38.93914,-77.08746,228,ROUTE_ID +7167,2015-08-17 06:33:39.000,38.94194,-77.09131,228,ROUTE_ID +7167,2015-08-17 06:34:30.000,38.94457,-77.09483,228,ROUTE_ID +7167,2015-08-17 06:35:15.000,38.94794,-77.09951,228,ROUTE_ID +7167,2015-08-17 06:36:04.000,38.94987,-77.09957,228,ROUTE_ID +7167,2015-08-17 06:36:34.000,38.95205,-77.09689,228,ROUTE_ID +7167,2015-08-17 06:37:12.000,38.95476,-77.09344,228,ROUTE_ID +7167,2015-08-17 06:38:12.000,38.95655,-77.09106,228,ROUTE_ID +7167,2015-08-17 06:39:13.000,38.95922,-77.08768,228,ROUTE_ID +7167,2015-08-17 06:39:52.000,38.96054,-77.08575,228,ROUTE_ID +7167,2015-08-17 06:40:25.000,38.96194,-77.085,228,ROUTE_ID +7167,2015-08-17 06:40:57.000,38.96144,-77.08556,228,ROUTE_ID +7167,2015-08-17 06:42:01.000,38.96166,-77.08481,228,ROUTE_ID +7167,2015-08-17 06:42:31.000,38.96166,-77.08481,228,ROUTE_ID +7167,2015-08-17 06:44:07.000,38.96166,-77.08481,228,ROUTE_ID +7167,2015-08-17 06:44:37.000,38.96166,-77.08481,228,ROUTE_ID +7167,2015-08-17 06:45:09.000,38.96166,-77.08481,228,ROUTE_ID +7167,2015-08-17 06:47:20.000,38.96166,-77.08481,228,ROUTE_ID +7167,2015-08-17 06:47:52.000,38.96166,-77.08481,228,ROUTE_ID +7167,2015-08-17 06:48:24.000,38.96166,-77.08481,228,ROUTE_ID +7167,2015-08-17 06:48:56.000,38.96166,-77.08481,228,ROUTE_ID +7167,2015-08-17 06:49:28.000,38.96166,-77.08481,228,ROUTE_ID +7167,2015-08-17 06:51:02.000,38.96163,-77.08501,228,ROUTE_ID +7167,2015-08-17 06:51:33.000,38.96163,-77.08501,228,ROUTE_ID +7167,2015-08-17 06:52:35.000,38.96163,-77.08501,228,ROUTE_ID +7167,2015-08-17 06:53:08.000,38.96163,-77.08501,228,ROUTE_ID +7167,2015-08-17 06:53:38.000,38.96163,-77.08501,228,ROUTE_ID +7167,2015-08-17 06:55:15.000,38.96163,-77.08501,228,ROUTE_ID +7167,2015-08-17 06:56:08.000,38.96182,-77.0863,228,ROUTE_ID +7167,2015-08-17 06:56:50.000,38.96062,-77.08591,228,ROUTE_ID +7167,2015-08-17 06:57:24.000,38.96004,-77.08672,228,ROUTE_ID +7167,2015-08-17 06:58:54.000,38.95605,-77.0918,228,ROUTE_ID +7167,2015-08-17 06:59:08.000,38.95469,-77.09354,228,ROUTE_ID +7167,2015-08-17 07:00:19.000,38.95041,-77.09912,228,ROUTE_ID +7167,2015-08-17 07:01:04.000,38.9491,-77.10129,228,ROUTE_ID +7167,2015-08-17 07:01:35.000,38.948,-77.09991,228,ROUTE_ID +7167,2015-08-17 07:02:17.000,38.94717,-77.09869,228,ROUTE_ID +7167,2015-08-17 07:02:55.000,38.94563,-77.09658,228,ROUTE_ID +7167,2015-08-17 07:04:02.000,38.94165,-77.09116,228,ROUTE_ID +7167,2015-08-17 07:05:06.000,38.94148,-77.0909,228,ROUTE_ID +7167,2015-08-17 07:06:09.000,38.93843,-77.08675,228,ROUTE_ID +7167,2015-08-17 07:07:07.000,38.93579,-77.08307,228,ROUTE_ID +7167,2015-08-17 07:07:40.000,38.93517,-77.08219,228,ROUTE_ID +7167,2015-08-17 07:08:13.000,38.93213,-77.07807,228,ROUTE_ID +7167,2015-08-17 07:09:03.000,38.93132,-77.07697,228,ROUTE_ID +7167,2015-08-17 07:10:32.000,38.92791,-77.0724,228,ROUTE_ID +7167,2015-08-17 07:11:35.000,38.92537,-77.06882,228,ROUTE_ID +7167,2015-08-17 07:12:29.000,38.92169,-77.06337,228,ROUTE_ID +7167,2015-08-17 07:13:31.000,38.91807,-77.05881,228,ROUTE_ID +7167,2015-08-17 07:14:09.000,38.91453,-77.054,228,ROUTE_ID +7167,2015-08-17 07:15:38.000,38.91122,-77.04808,228,ROUTE_ID +7167,2015-08-17 07:16:11.000,38.91019,-77.04524,228,ROUTE_ID +7167,2015-08-17 07:17:28.000,38.90981,-77.04432,228,ROUTE_ID +7167,2015-08-17 07:18:18.000,38.90832,-77.04279,228,ROUTE_ID +7167,2015-08-17 07:18:54.000,38.90646,-77.04163,228,ROUTE_ID +7167,2015-08-17 07:20:09.000,38.90339,-77.03992,228,ROUTE_ID +7167,2015-08-17 07:21:13.000,38.90264,-77.03947,228,ROUTE_ID +7167,2015-08-17 07:21:46.000,38.90244,-77.0394,228,ROUTE_ID +7167,2015-08-17 07:22:19.000,38.9023,-77.0386,228,ROUTE_ID +7167,2015-08-17 07:22:52.000,38.90136,-77.03854,228,ROUTE_ID +7167,2015-08-17 07:24:30.000,38.90145,-77.03852,226,ROUTE_ID +7167,2015-08-17 07:25:33.000,38.90145,-77.03852,226,ROUTE_ID +7167,2015-08-17 07:26:36.000,38.90145,-77.03852,226,ROUTE_ID +7167,2015-08-17 07:27:39.000,38.90145,-77.03852,226,ROUTE_ID +7167,2015-08-17 07:29:25.000,38.90136,-77.03905,226,ROUTE_ID +7167,2015-08-17 07:29:49.000,38.90137,-77.03967,226,ROUTE_ID +7167,2015-08-17 07:30:55.000,38.90123,-77.04147,226,ROUTE_ID +7167,2015-08-17 07:32:12.000,38.90219,-77.0416,226,ROUTE_ID +7167,2015-08-17 07:32:46.000,38.90374,-77.04166,226,ROUTE_ID +7167,2015-08-17 07:34:23.000,38.90598,-77.04163,226,ROUTE_ID +7167,2015-08-17 07:35:27.000,38.90811,-77.04182,226,ROUTE_ID +7167,2015-08-17 07:35:59.000,38.90975,-77.04272,226,ROUTE_ID +7167,2015-08-17 07:37:03.000,38.90975,-77.04272,226,ROUTE_ID +7167,2015-08-17 07:38:07.000,38.91013,-77.04475,226,ROUTE_ID +7167,2015-08-17 07:39:00.000,38.91061,-77.04629,226,ROUTE_ID +7167,2015-08-17 07:40:04.000,38.9114,-77.04839,226,ROUTE_ID +7167,2015-08-17 07:40:53.000,38.91146,-77.04858,226,ROUTE_ID +7167,2015-08-17 07:41:53.000,38.91408,-77.05329,226,ROUTE_ID +7167,2015-08-17 07:42:46.000,38.91628,-77.05633,226,ROUTE_ID +7167,2015-08-17 07:43:25.000,38.9173,-77.05762,226,ROUTE_ID +7167,2015-08-17 07:44:09.000,38.91921,-77.06033,226,ROUTE_ID +7167,2015-08-17 07:45:02.000,38.92201,-77.06328,226,ROUTE_ID +7167,2015-08-17 07:46:34.000,38.92633,-77.07002,226,ROUTE_ID +7167,2015-08-17 07:47:38.000,38.92843,-77.07285,226,ROUTE_ID +7167,2015-08-17 07:48:10.000,38.92883,-77.07335,226,ROUTE_ID +7167,2015-08-17 07:49:30.000,38.93126,-77.0768,226,ROUTE_ID +7167,2015-08-17 07:50:02.000,38.93126,-77.0768,226,ROUTE_ID +7167,2015-08-17 07:50:51.000,38.93087,-77.07742,226,ROUTE_ID +7167,2015-08-17 07:52:20.000,38.93037,-77.08274,226,ROUTE_ID +7167,2015-08-17 07:53:18.000,38.93039,-77.08398,226,ROUTE_ID +7167,2015-08-17 07:54:05.000,38.93232,-77.08511,226,ROUTE_ID +7167,2015-08-17 07:55:27.000,38.93453,-77.08634,226,ROUTE_ID +7167,2015-08-17 07:56:01.000,38.93584,-77.08752,226,ROUTE_ID +7167,2015-08-17 07:57:10.000,38.93795,-77.08566,226,ROUTE_ID +7167,2015-08-17 07:58:41.000,38.94543,-77.07946,226,ROUTE_ID +7167,2015-08-17 08:00:03.000,38.94644,-77.07879,226,ROUTE_ID +7167,2015-08-17 08:00:45.000,38.94848,-77.07992,226,ROUTE_ID +7167,2015-08-17 08:01:49.000,38.9494,-77.08019,226,ROUTE_ID +7167,2015-08-17 08:02:45.000,38.95189,-77.08114,226,ROUTE_ID +7167,2015-08-17 08:04:02.000,38.95914,-77.08486,226,ROUTE_ID +7167,2015-08-17 08:05:00.000,38.96062,-77.08565,226,ROUTE_ID +7167,2015-08-17 08:05:56.000,38.96127,-77.08505,226,ROUTE_ID +7167,2015-08-17 08:07:00.000,38.96175,-77.08604,226,ROUTE_ID +7167,2015-08-17 08:08:02.000,38.96151,-77.08505,226,ROUTE_ID +7167,2015-08-17 08:09:05.000,38.96151,-77.08505,226,ROUTE_ID +7167,2015-08-17 08:10:38.000,38.96151,-77.08505,226,ROUTE_ID +7167,2015-08-17 08:11:10.000,38.96151,-77.08505,226,ROUTE_ID +7167,2015-08-17 08:12:15.000,38.96151,-77.08505,226,ROUTE_ID +7167,2015-08-17 08:12:48.000,38.96151,-77.08505,226,ROUTE_ID +7167,2015-08-17 08:14:25.000,38.96151,-77.08505,226,ROUTE_ID +7167,2015-08-17 08:14:57.000,38.96151,-77.08505,226,ROUTE_ID +7167,2015-08-17 08:15:28.000,38.96151,-77.08505,226,ROUTE_ID +7167,2015-08-17 08:16:59.000,38.96186,-77.08487,228,ROUTE_ID +7167,2015-08-17 08:18:32.000,38.96106,-77.08587,228,ROUTE_ID +7167,2015-08-17 08:18:57.000,38.96062,-77.08591,228,ROUTE_ID +7167,2015-08-17 08:20:12.000,38.95835,-77.08889,228,ROUTE_ID +7167,2015-08-17 08:21:10.000,38.95692,-77.0907,228,ROUTE_ID +7167,2015-08-17 08:22:47.000,38.95646,-77.09129,228,ROUTE_ID +7167,2015-08-17 08:23:11.000,38.95467,-77.09354,228,ROUTE_ID +7167,2015-08-17 08:24:07.000,38.95134,-77.09791,228,ROUTE_ID +7167,2015-08-17 08:25:08.000,38.94911,-77.10128,228,ROUTE_ID +7167,2015-08-17 08:26:20.000,38.94597,-77.09704,228,ROUTE_ID +7167,2015-08-17 08:26:34.000,38.94573,-77.09671,228,ROUTE_ID +7167,2015-08-17 08:28:00.000,38.94166,-77.09117,228,ROUTE_ID +7167,2015-08-17 08:29:52.000,38.93814,-77.08646,228,ROUTE_ID +7167,2015-08-17 08:30:01.000,38.93756,-77.08617,228,ROUTE_ID +7167,2015-08-17 08:30:37.000,38.9366,-77.08418,228,ROUTE_ID +7167,2015-08-17 08:32:05.000,38.93268,-77.07883,228,ROUTE_ID +7167,2015-08-17 08:32:35.000,38.9316,-77.07728,228,ROUTE_ID +7167,2015-08-17 08:34:09.000,38.93057,-77.07593,228,ROUTE_ID +7167,2015-08-17 08:34:39.000,38.93027,-77.07551,228,ROUTE_ID +7167,2015-08-17 08:35:12.000,38.93015,-77.07534,228,ROUTE_ID +7167,2015-08-17 08:36:16.000,38.92962,-77.0746,228,ROUTE_ID +7167,2015-08-17 08:36:46.000,38.92882,-77.07356,228,ROUTE_ID +7167,2015-08-17 08:38:30.000,38.92765,-77.07204,228,ROUTE_ID +7167,2015-08-17 08:39:07.000,38.9271,-77.07126,228,ROUTE_ID +7167,2015-08-17 08:40:32.000,38.92424,-77.06619,228,ROUTE_ID +7167,2015-08-17 08:41:35.000,38.91921,-77.06034,228,ROUTE_ID +7167,2015-08-17 08:41:42.000,38.91859,-77.05948,228,ROUTE_ID +7167,2015-08-17 08:42:32.000,38.91624,-77.05633,228,ROUTE_ID +7167,2015-08-17 08:43:37.000,38.91444,-77.05386,228,ROUTE_ID +7167,2015-08-17 08:44:10.000,38.9123,-77.05113,228,ROUTE_ID +7167,2015-08-17 08:45:16.000,38.91182,-77.04977,228,ROUTE_ID +7167,2015-08-17 08:46:21.000,38.91127,-77.04826,228,ROUTE_ID +7167,2015-08-17 08:47:29.000,38.91024,-77.04536,228,ROUTE_ID +7167,2015-08-17 08:48:31.000,38.91018,-77.0451,228,ROUTE_ID +7167,2015-08-17 08:49:51.000,38.90872,-77.04305,228,ROUTE_ID +7167,2015-08-17 08:50:51.000,38.90742,-77.04221,228,ROUTE_ID +7167,2015-08-17 08:51:47.000,38.90583,-77.04132,228,ROUTE_ID +7167,2015-08-17 08:52:21.000,38.90583,-77.04132,228,ROUTE_ID +7167,2015-08-17 08:52:57.000,38.90464,-77.0406,228,ROUTE_ID +7167,2015-08-17 08:54:09.000,38.90244,-77.0394,228,ROUTE_ID +7167,2015-08-17 08:55:12.000,38.90232,-77.03918,228,ROUTE_ID +7167,2015-08-17 08:56:18.000,38.90176,-77.03853,228,ROUTE_ID +7167,2015-08-17 08:56:51.000,38.90176,-77.03853,228,ROUTE_ID +7167,2015-08-17 08:57:25.000,38.90176,-77.03853,228,ROUTE_ID +7167,2015-08-17 08:58:29.000,38.90176,-77.03853,228,ROUTE_ID +7167,2015-08-17 08:59:32.000,38.90176,-77.03853,228,ROUTE_ID +7167,2015-08-17 09:00:06.000,38.90176,-77.03853,228,ROUTE_ID +7167,2015-08-17 09:01:45.000,38.90176,-77.03853,228,ROUTE_ID +7167,2015-08-17 09:02:18.000,38.90176,-77.03853,228,ROUTE_ID +7167,2015-08-17 09:02:52.000,38.90176,-77.03853,228,ROUTE_ID +7167,2015-08-17 09:04:29.000,38.90176,-77.03853,228,ROUTE_ID +7167,2015-08-17 09:05:02.000,38.90176,-77.03853,228,ROUTE_ID +7167,2015-08-17 09:06:05.000,38.90176,-77.03853,228,ROUTE_ID +7167,2015-08-17 09:06:35.000,38.90176,-77.03853,228,ROUTE_ID +7167,2015-08-17 09:07:00.000,38.90176,-77.03853,226,ROUTE_ID +7167,2015-08-17 09:07:33.000,38.90162,-77.0386,226,ROUTE_ID +7167,2015-08-17 09:08:36.000,38.90162,-77.0386,226,ROUTE_ID +7167,2015-08-17 09:09:10.000,38.90162,-77.0386,226,ROUTE_ID +7167,2015-08-17 09:10:19.000,38.90162,-77.0386,226,ROUTE_ID +7167,2015-08-17 09:10:49.000,38.90174,-77.03853,226,ROUTE_ID +7167,2015-08-17 09:11:22.000,38.9016,-77.03859,226,ROUTE_ID +7167,2015-08-17 09:11:55.000,38.90145,-77.03854,226,ROUTE_ID +7167,2015-08-17 09:13:12.000,38.90141,-77.04109,226,ROUTE_ID +7167,2015-08-17 09:13:45.000,38.90141,-77.04109,226,ROUTE_ID +7167,2015-08-17 09:14:51.000,38.90133,-77.04153,226,ROUTE_ID +7167,2015-08-17 09:16:07.000,38.90154,-77.0416,226,ROUTE_ID +7167,2015-08-17 09:17:11.000,38.90207,-77.0416,226,ROUTE_ID +7167,2015-08-17 09:18:06.000,38.90295,-77.04163,226,ROUTE_ID +7167,2015-08-17 09:18:36.000,38.90295,-77.04163,226,ROUTE_ID +7167,2015-08-17 09:19:10.000,38.90544,-77.04163,226,ROUTE_ID +7167,2015-08-17 09:19:43.000,38.90548,-77.04163,226,ROUTE_ID +7167,2015-08-17 09:20:47.000,38.90594,-77.04163,226,ROUTE_ID +7167,2015-08-17 09:21:19.000,38.90697,-77.0418,226,ROUTE_ID +7167,2015-08-17 09:21:51.000,38.90838,-77.04182,226,ROUTE_ID +7167,2015-08-17 09:22:55.000,38.90917,-77.04302,226,ROUTE_ID +7167,2015-08-17 09:23:20.000,38.91,-77.0429,226,ROUTE_ID +7167,2015-08-17 09:23:53.000,38.91019,-77.04367,226,ROUTE_ID +7167,2015-08-17 09:24:25.000,38.91002,-77.04403,226,ROUTE_ID +7167,2015-08-17 09:24:57.000,38.91002,-77.04403,226,ROUTE_ID +7167,2015-08-17 09:25:28.000,38.90997,-77.04423,226,ROUTE_ID +7167,2015-08-17 09:26:01.000,38.91013,-77.04475,226,ROUTE_ID +7167,2015-08-17 09:27:16.000,38.91065,-77.04639,226,ROUTE_ID +7167,2015-08-17 09:28:25.000,38.91249,-77.05067,226,ROUTE_ID +7167,2015-08-17 09:28:58.000,38.91312,-77.05194,226,ROUTE_ID +7167,2015-08-17 09:30:21.000,38.91558,-77.05536,226,ROUTE_ID +7167,2015-08-17 09:30:57.000,38.91659,-77.05669,226,ROUTE_ID +7167,2015-08-17 09:31:29.000,38.91692,-77.05711,226,ROUTE_ID +7167,2015-08-17 09:32:10.000,38.91813,-77.05876,226,ROUTE_ID +7167,2015-08-17 09:32:42.000,38.91927,-77.0604,226,ROUTE_ID +7167,2015-08-17 09:33:07.000,38.92189,-77.06325,226,ROUTE_ID +7167,2015-08-17 09:33:56.000,38.92471,-77.06782,226,ROUTE_ID +7167,2015-08-17 09:34:29.000,38.92672,-77.07056,226,ROUTE_ID +7167,2015-08-17 09:35:00.000,38.92725,-77.07127,226,ROUTE_ID +7167,2015-08-17 09:35:37.000,38.9279,-77.07209,226,ROUTE_ID +7167,2015-08-17 09:37:17.000,38.93099,-77.07638,226,ROUTE_ID +7167,2015-08-17 09:37:50.000,38.93132,-77.07693,226,ROUTE_ID +7167,2015-08-17 09:38:24.000,38.93084,-77.07745,226,ROUTE_ID +7167,2015-08-17 09:39:12.000,38.92954,-77.0787,226,ROUTE_ID +7167,2015-08-17 09:39:45.000,38.93017,-77.08006,226,ROUTE_ID +7167,2015-08-17 09:40:20.000,38.93039,-77.08356,226,ROUTE_ID +7167,2015-08-17 09:41:33.000,38.93227,-77.08508,226,ROUTE_ID +7167,2015-08-17 09:42:06.000,38.93322,-77.08562,226,ROUTE_ID +7167,2015-08-17 09:42:35.000,38.9342,-77.08615,226,ROUTE_ID +7167,2015-08-17 09:43:30.000,38.93585,-77.08752,226,ROUTE_ID +7167,2015-08-17 09:44:03.000,38.93784,-77.08571,226,ROUTE_ID +7167,2015-08-17 09:45:34.000,38.94175,-77.08261,226,ROUTE_ID +7167,2015-08-17 09:46:13.000,38.94305,-77.0815,226,ROUTE_ID +7167,2015-08-17 09:46:45.000,38.94529,-77.07958,226,ROUTE_ID +7167,2015-08-17 09:48:18.000,38.94649,-77.07886,226,ROUTE_ID +7167,2015-08-17 09:51:01.000,38.95226,-77.08132,226,ROUTE_ID +7167,2015-08-17 09:52:15.000,38.957,-77.08371,226,ROUTE_ID +7167,2015-08-17 09:54:34.000,38.96059,-77.08561,226,ROUTE_ID +7167,2015-08-17 09:55:48.000,38.96098,-77.0854,226,ROUTE_ID +7167,2015-08-17 09:56:54.000,38.96177,-77.08604,226,ROUTE_ID +7167,2015-08-17 09:58:28.000,38.96166,-77.08497,226,ROUTE_ID +7167,2015-08-17 10:00:04.000,38.96163,-77.08501,228,ROUTE_ID +7167,2015-08-17 10:02:07.000,38.96039,-77.08617,228,ROUTE_ID +7167,2015-08-17 10:02:49.000,38.95984,-77.08703,228,ROUTE_ID +7167,2015-08-17 10:04:28.000,38.95663,-77.09107,228,ROUTE_ID +7167,2015-08-17 10:06:29.000,38.95177,-77.09734,228,ROUTE_ID +7167,2015-08-17 10:07:28.000,38.94935,-77.10094,228,ROUTE_ID +7167,2015-08-17 10:10:26.000,38.9434,-77.09351,228,ROUTE_ID +7167,2015-08-17 10:12:02.000,38.93828,-77.08656,228,ROUTE_ID +7167,2015-08-17 10:15:34.000,38.93128,-77.07692,228,ROUTE_ID +7167,2015-08-17 10:17:10.000,38.9287,-77.0735,228,ROUTE_ID +7167,2015-08-17 10:18:42.000,38.92469,-77.06789,228,ROUTE_ID +7167,2015-08-17 10:19:11.000,38.92421,-77.06606,228,ROUTE_ID +7167,2015-08-17 10:22:33.000,38.91376,-77.05292,228,ROUTE_ID +7167,2015-08-17 10:23:36.000,38.91164,-77.04929,228,ROUTE_ID +7167,2015-08-17 10:25:11.000,38.91015,-77.04504,228,ROUTE_ID +7167,2015-08-17 10:26:08.000,38.9099,-77.04442,228,ROUTE_ID +7167,2015-08-17 10:27:11.000,38.90839,-77.04283,228,ROUTE_ID +7167,2015-08-17 10:28:41.000,38.90724,-77.04209,228,ROUTE_ID +7167,2015-08-17 10:30:11.000,38.90342,-77.03995,228,ROUTE_ID +7167,2015-08-17 10:30:49.000,38.9023,-77.0386,228,ROUTE_ID +7167,2015-08-17 10:33:01.000,38.90157,-77.03856,228,ROUTE_ID +7167,2015-08-17 10:34:04.000,38.90157,-77.03856,228,ROUTE_ID +7167,2015-08-17 10:35:37.000,38.90157,-77.03856,228,ROUTE_ID +7167,2015-08-17 10:37:01.000,38.90157,-77.03856,226,ROUTE_ID +7167,2015-08-17 10:38:05.000,38.90162,-77.0386,226,ROUTE_ID +7167,2015-08-17 10:39:08.000,38.90162,-77.0386,226,ROUTE_ID +7167,2015-08-17 10:40:40.000,38.90137,-77.03884,226,ROUTE_ID +7167,2015-08-17 10:41:47.000,38.90144,-77.0416,226,ROUTE_ID +7167,2015-08-17 10:45:07.000,38.90695,-77.0418,226,ROUTE_ID +7167,2015-08-17 10:45:37.000,38.90798,-77.04182,226,ROUTE_ID +7167,2015-08-17 10:46:49.000,38.91013,-77.04475,226,ROUTE_ID +7167,2015-08-17 10:47:33.000,38.91063,-77.04633,226,ROUTE_ID +7167,2015-08-17 10:49:16.000,38.91145,-77.04856,226,ROUTE_ID +7167,2015-08-17 10:49:49.000,38.91252,-77.05105,226,ROUTE_ID +7167,2015-08-17 10:51:37.000,38.91692,-77.05711,226,ROUTE_ID +7167,2015-08-17 10:53:19.000,38.92188,-77.06325,226,ROUTE_ID +7167,2015-08-17 10:54:17.000,38.92461,-77.06767,226,ROUTE_ID +7167,2015-08-17 10:55:25.000,38.92797,-77.07219,226,ROUTE_ID +7167,2015-08-17 10:57:25.000,38.93131,-77.07708,226,ROUTE_ID +7167,2015-08-17 10:57:53.000,38.93087,-77.07742,226,ROUTE_ID +7167,2015-08-17 10:59:14.000,38.93037,-77.08319,226,ROUTE_ID +7167,2015-08-17 11:00:44.000,38.93101,-77.08439,226,ROUTE_ID +7167,2015-08-17 11:01:34.000,38.93312,-77.08557,226,ROUTE_ID +7167,2015-08-17 11:03:13.000,38.93587,-77.08751,226,ROUTE_ID +7167,2015-08-17 11:04:26.000,38.93921,-77.08476,226,ROUTE_ID +7167,2015-08-17 11:05:59.000,38.94517,-77.07969,226,ROUTE_ID +7167,2015-08-17 11:06:33.000,38.94588,-77.07906,226,ROUTE_ID +7167,2015-08-17 11:07:54.000,38.948,-77.07964,226,ROUTE_ID +7167,2015-08-17 11:09:33.000,38.95123,-77.0808,226,ROUTE_ID +7167,2015-08-17 11:10:50.000,38.95478,-77.08263,226,ROUTE_ID +7167,2015-08-17 11:12:16.000,38.95926,-77.08494,226,ROUTE_ID +7167,2015-08-17 11:13:21.000,38.95981,-77.08524,226,ROUTE_ID +7167,2015-08-17 11:13:53.000,38.96016,-77.08538,226,ROUTE_ID +7167,2015-08-17 11:15:18.000,38.96083,-77.08562,226,ROUTE_ID +7167,2015-08-17 11:17:29.000,38.96153,-77.08499,226,ROUTE_ID +7167,2015-08-17 11:19:18.000,38.96168,-77.085,226,ROUTE_ID +7167,2015-08-17 11:21:19.000,38.96168,-77.085,226,ROUTE_ID +7167,2015-08-17 11:21:51.000,38.96168,-77.085,226,ROUTE_ID +7167,2015-08-17 11:22:55.000,38.96168,-77.085,226,ROUTE_ID +7167,2015-08-17 11:23:28.000,38.96168,-77.085,226,ROUTE_ID +7167,2015-08-17 11:26:06.000,38.9617,-77.08495,226,ROUTE_ID +7167,2015-08-17 11:28:34.000,38.96163,-77.08501,228,ROUTE_ID +7167,2015-08-17 11:29:27.000,38.96163,-77.08501,228,ROUTE_ID +7167,2015-08-17 11:30:31.000,38.96163,-77.08501,228,ROUTE_ID +7167,2015-08-17 11:32:06.000,38.962,-77.08606,228,ROUTE_ID +7167,2015-08-17 11:33:48.000,38.96062,-77.08591,228,ROUTE_ID +7167,2015-08-17 11:34:20.000,38.96002,-77.08679,228,ROUTE_ID +7167,2015-08-17 11:35:34.000,38.95744,-77.09003,228,ROUTE_ID +7167,2015-08-17 11:37:18.000,38.95448,-77.09377,228,ROUTE_ID +7167,2015-08-17 11:37:52.000,38.95145,-77.09778,228,ROUTE_ID +7167,2015-08-17 11:39:23.000,38.94781,-77.09964,228,ROUTE_ID +7167,2015-08-17 11:40:34.000,38.94314,-77.09315,228,ROUTE_ID +7167,2015-08-17 11:46:43.000,38.93136,-77.07703,228,ROUTE_ID +7167,2015-08-17 11:48:46.000,38.9262,-77.06995,228,ROUTE_ID +7167,2015-08-17 11:51:00.000,38.91684,-77.05714,228,ROUTE_ID +7167,2015-08-17 11:52:38.000,38.91256,-77.05132,228,ROUTE_ID +7167,2015-08-17 11:54:31.000,38.91121,-77.04806,228,ROUTE_ID +7167,2015-08-17 11:57:15.000,38.90841,-77.04284,228,ROUTE_ID +7167,2015-08-17 11:59:35.000,38.90399,-77.04021,228,ROUTE_ID +7167,2015-08-17 12:02:29.000,38.90165,-77.03857,228,ROUTE_ID +7167,2015-08-17 12:07:00.000,38.90165,-77.03857,226,ROUTE_ID +7167,2015-08-17 12:09:44.000,38.90162,-77.0386,226,ROUTE_ID +7167,2015-08-17 12:11:52.000,38.90141,-77.04142,226,ROUTE_ID +7167,2015-08-17 12:15:26.000,38.90549,-77.04163,226,ROUTE_ID +7167,2015-08-17 12:19:03.000,38.91038,-77.04546,226,ROUTE_ID +7167,2015-08-17 12:21:01.000,38.91146,-77.0486,226,ROUTE_ID +7167,2015-08-17 12:22:44.000,38.91636,-77.05643,226,ROUTE_ID +7167,2015-08-17 12:24:25.000,38.91891,-77.05984,226,ROUTE_ID +7167,2015-08-17 12:24:39.000,38.91918,-77.0603,226,ROUTE_ID +7167,2015-08-17 12:26:18.000,38.92461,-77.06767,226,ROUTE_ID +7167,2015-08-17 12:28:30.000,38.92928,-77.07402,226,ROUTE_ID +7167,2015-08-17 12:30:17.000,38.93087,-77.07742,226,ROUTE_ID +7167,2015-08-17 12:32:46.000,38.93103,-77.0844,226,ROUTE_ID +7167,2015-08-17 12:35:11.000,38.93586,-77.08752,226,ROUTE_ID +7167,2015-08-17 12:36:55.000,38.94359,-77.08103,226,ROUTE_ID +7167,2015-08-17 12:39:30.000,38.94674,-77.079,226,ROUTE_ID +7167,2015-08-17 12:42:28.000,38.9545,-77.08243,226,ROUTE_ID +7167,2015-08-17 12:45:00.000,38.95822,-77.08437,226,ROUTE_ID +7167,2015-08-17 12:47:20.000,38.95924,-77.08492,226,ROUTE_ID +7167,2015-08-17 12:48:59.000,38.95974,-77.0852,226,ROUTE_ID +7167,2015-08-17 12:51:17.000,38.96142,-77.08488,226,ROUTE_ID +7167,2015-08-17 12:53:08.000,38.96148,-77.08515,226,ROUTE_ID +7167,2015-08-17 12:55:16.000,38.96148,-77.08515,226,ROUTE_ID +7167,2015-08-17 12:57:26.000,38.96148,-77.08515,226,ROUTE_ID +7167,2015-08-17 13:00:41.000,38.96186,-77.08487,228,ROUTE_ID +7167,2015-08-17 13:01:48.000,38.96134,-77.08606,228,ROUTE_ID +7167,2015-08-17 13:02:47.000,38.95957,-77.08739,228,ROUTE_ID +7167,2015-08-17 13:05:23.000,38.95426,-77.09405,228,ROUTE_ID +7167,2015-08-17 13:08:29.000,38.94429,-77.09474,228,ROUTE_ID +7167,2015-08-17 13:10:09.000,38.94149,-77.0909,228,ROUTE_ID +7167,2015-08-17 13:11:14.000,38.93843,-77.08675,228,ROUTE_ID +7167,2015-08-17 13:13:45.000,38.9313,-77.07694,228,ROUTE_ID +7167,2015-08-17 13:15:21.000,38.92871,-77.07352,228,ROUTE_ID +7167,2015-08-17 13:17:18.000,38.92424,-77.06623,228,ROUTE_ID +7167,2015-08-17 13:19:29.000,38.91618,-77.05624,228,ROUTE_ID +7167,2015-08-17 13:21:11.000,38.91196,-77.05014,228,ROUTE_ID +7167,2015-08-17 13:23:30.000,38.90902,-77.04346,228,ROUTE_ID +7167,2015-08-17 13:25:21.000,38.90738,-77.04218,228,ROUTE_ID +7167,2015-08-17 13:29:21.000,38.9023,-77.03857,228,ROUTE_ID +7167,2015-08-17 13:31:00.000,38.90162,-77.03854,228,ROUTE_ID +7167,2015-08-17 13:32:03.000,38.90162,-77.03854,228,ROUTE_ID +7167,2015-08-17 13:33:33.000,38.90162,-77.03854,228,ROUTE_ID +7167,2015-08-17 13:34:36.000,38.90162,-77.03854,228,ROUTE_ID +7167,2015-08-17 13:35:36.000,38.90162,-77.03854,228,ROUTE_ID +7167,2015-08-17 13:37:06.000,38.90162,-77.0386,226,ROUTE_ID +7167,2015-08-17 13:38:43.000,38.90162,-77.0386,226,ROUTE_ID +7167,2015-08-17 13:40:28.000,38.90145,-77.03852,226,ROUTE_ID +7167,2015-08-17 13:42:36.000,38.90294,-77.04163,226,ROUTE_ID +7167,2015-08-17 13:44:05.000,38.90578,-77.04163,226,ROUTE_ID +7167,2015-08-17 13:46:06.000,38.90987,-77.04282,226,ROUTE_ID +7167,2015-08-17 13:48:21.000,38.91013,-77.04475,226,ROUTE_ID +7167,2015-08-17 13:51:24.000,38.91256,-77.05115,226,ROUTE_ID +7167,2015-08-17 13:54:27.000,38.91932,-77.06048,226,ROUTE_ID +7167,2015-08-17 13:56:07.000,38.9262,-77.06984,226,ROUTE_ID +7167,2015-08-17 13:57:04.000,38.92793,-77.07212,226,ROUTE_ID +7167,2015-08-17 13:58:07.000,38.92884,-77.07336,226,ROUTE_ID +7167,2015-08-17 13:59:25.000,38.92925,-77.07398,226,ROUTE_ID +7167,2015-08-17 14:00:38.000,38.93085,-77.07744,226,ROUTE_ID +7167,2015-08-17 14:01:57.000,38.93037,-77.0825,226,ROUTE_ID +7167,2015-08-17 14:05:08.000,38.93453,-77.08634,226,ROUTE_ID +7167,2015-08-17 14:07:02.000,38.93702,-77.08666,226,ROUTE_ID +7167,2015-08-17 14:08:28.000,38.93893,-77.08499,226,ROUTE_ID +7167,2015-08-17 14:10:49.000,38.94439,-77.08035,226,ROUTE_ID +7167,2015-08-17 14:13:04.000,38.94924,-77.08016,226,ROUTE_ID +7167,2015-08-17 14:15:11.000,38.95626,-77.08338,226,ROUTE_ID +7167,2015-08-17 14:17:20.000,38.95705,-77.08374,226,ROUTE_ID +7167,2015-08-17 14:17:54.000,38.95753,-77.08399,226,ROUTE_ID +7167,2015-08-17 14:23:35.000,38.96045,-77.08552,226,ROUTE_ID +7167,2015-08-17 14:25:31.000,38.96141,-77.0858,226,ROUTE_ID +7167,2015-08-17 14:27:36.000,38.96163,-77.08501,228,ROUTE_ID +7167,2015-08-17 14:28:42.000,38.96163,-77.08501,228,ROUTE_ID +7167,2015-08-17 14:30:54.000,38.96206,-77.08543,228,ROUTE_ID +7167,2015-08-17 14:32:39.000,38.95981,-77.08707,228,ROUTE_ID +7167,2015-08-17 14:34:55.000,38.95466,-77.09356,228,ROUTE_ID +7167,2015-08-17 14:37:09.000,38.9478,-77.09962,228,ROUTE_ID +7167,2015-08-17 14:38:57.000,38.94459,-77.09515,228,ROUTE_ID +7167,2015-08-17 14:41:11.000,38.93899,-77.08752,228,ROUTE_ID +7167,2015-08-17 14:43:17.000,38.93584,-77.08313,228,ROUTE_ID +7167,2015-08-17 14:45:21.000,38.93134,-77.077,228,ROUTE_ID +7167,2015-08-17 14:46:59.000,38.92879,-77.07353,228,ROUTE_ID +7167,2015-08-17 14:49:41.000,38.92137,-77.06318,228,ROUTE_ID +7167,2015-08-17 14:52:23.000,38.91376,-77.05293,228,ROUTE_ID +7167,2015-08-17 14:54:04.000,38.91171,-77.04945,228,ROUTE_ID +7167,2015-08-17 14:55:58.000,38.90992,-77.04446,228,ROUTE_ID +7167,2015-08-17 15:08:06.000,38.90145,-77.03852,226,ROUTE_ID +7167,2015-08-17 15:18:44.000,38.91,-77.0429,226,ROUTE_ID +7167,2015-08-17 15:25:16.000,38.91718,-77.05745,226,ROUTE_ID +7167,2015-08-17 15:46:38.000,38.95702,-77.08372,226,ROUTE_ID +7167,2015-08-17 15:58:59.000,38.96186,-77.08487,228,ROUTE_ID +7167,2015-08-17 16:10:19.000,38.9472,-77.09877,228,ROUTE_ID +7167,2015-08-17 16:32:58.000,38.90891,-77.04319,228,ROUTE_ID +7167,2015-08-17 16:35:21.000,38.90584,-77.04133,228,ROUTE_ID +7167,2015-08-17 16:37:07.000,38.904,-77.04024,228,ROUTE_ID +7167,2015-08-17 16:37:40.000,38.90346,-77.03997,228,ROUTE_ID +7167,2015-08-17 16:46:34.000,38.90123,-77.04147,228,ROUTE_ID +7167,2015-08-17 16:48:46.000,38.90326,-77.04165,228,ROUTE_ID +7167,2015-08-17 16:49:19.000,38.90541,-77.04163,228,ROUTE_ID +7167,2015-08-17 16:50:56.000,38.9059,-77.04163,228,ROUTE_ID +7167,2015-08-17 16:54:52.000,38.91013,-77.04475,228,ROUTE_ID +7167,2015-08-17 16:56:27.000,38.91139,-77.04838,228,ROUTE_ID +7167,2015-08-17 16:56:59.000,38.91139,-77.04838,228,ROUTE_ID +7167,2015-08-17 16:58:13.000,38.91254,-77.05112,228,ROUTE_ID +7167,2015-08-17 16:58:53.000,38.91408,-77.05328,228,ROUTE_ID +7167,2015-08-17 17:00:09.000,38.91718,-77.05746,228,ROUTE_ID +7167,2015-08-17 17:00:43.000,38.91983,-77.06119,228,ROUTE_ID +7167,2015-08-17 17:02:29.000,38.92461,-77.06767,228,ROUTE_ID +7167,2015-08-17 17:04:40.000,38.92886,-77.07339,228,ROUTE_ID +7167,2015-08-17 17:06:34.000,38.93389,-77.08034,228,ROUTE_ID +7167,2015-08-17 17:10:39.000,38.94119,-77.0903,228,ROUTE_ID +7167,2015-08-17 17:11:45.000,38.94428,-77.09447,228,ROUTE_ID +7167,2015-08-17 17:13:50.000,38.9483,-77.10001,228,ROUTE_ID +7167,2015-08-17 17:14:22.000,38.94901,-77.1004,228,ROUTE_ID +7167,2015-08-17 17:15:37.000,38.9514,-77.0977,228,ROUTE_ID +7167,2015-08-17 17:16:10.000,38.95347,-77.09505,228,ROUTE_ID +7167,2015-08-17 17:16:29.000,38.95375,-77.09473,228,ROUTE_ID +7167,2015-08-17 17:18:00.000,38.95887,-77.08813,228,ROUTE_ID +7167,2015-08-17 17:18:30.000,38.95997,-77.08669,228,ROUTE_ID +7167,2015-08-17 17:20:12.000,38.96097,-77.08524,228,ROUTE_ID +7167,2015-08-17 17:21:17.000,38.96196,-77.08504,228,ROUTE_ID +7167,2015-08-17 17:21:49.000,38.96176,-77.08578,228,ROUTE_ID +7167,2015-08-17 17:22:54.000,38.96155,-77.08533,228,ROUTE_ID +7167,2015-08-17 17:23:25.000,38.96161,-77.08527,228,ROUTE_ID +7167,2015-08-17 17:23:57.000,38.96163,-77.08518,228,ROUTE_ID +7167,2015-08-17 17:24:59.000,38.96163,-77.08518,228,ROUTE_ID +7167,2015-08-17 17:25:29.000,38.96164,-77.08517,228,ROUTE_ID +7167,2015-08-17 17:26:02.000,38.96169,-77.08508,228,ROUTE_ID +7167,2015-08-17 17:27:06.000,38.96169,-77.08508,228,ROUTE_ID +7167,2015-08-17 17:29:15.000,38.96169,-77.08508,228,ROUTE_ID +7167,2015-08-17 17:29:49.000,38.96169,-77.08508,228,ROUTE_ID +7167,2015-08-17 17:31:25.000,38.96169,-77.08508,228,ROUTE_ID +7167,2015-08-17 17:32:01.000,38.96169,-77.08508,228,ROUTE_ID +7167,2015-08-17 17:33:03.000,38.96163,-77.08501,228,ROUTE_ID +7167,2015-08-17 17:33:33.000,38.96163,-77.08501,228,ROUTE_ID +7167,2015-08-17 17:35:00.000,38.96163,-77.08501,228,ROUTE_ID +7167,2015-08-17 17:36:05.000,38.962,-77.08606,228,ROUTE_ID +7167,2015-08-17 17:36:35.000,38.96195,-77.08623,228,ROUTE_ID +7167,2015-08-17 17:37:22.000,38.96195,-77.08623,228,ROUTE_ID +7167,2015-08-17 17:37:56.000,38.96106,-77.08587,228,ROUTE_ID +7167,2015-08-17 17:38:26.000,38.96077,-77.08576,228,ROUTE_ID +7167,2015-08-17 17:39:30.000,38.95888,-77.08831,228,ROUTE_ID +7167,2015-08-17 17:40:32.000,38.95695,-77.09065,228,ROUTE_ID +7167,2015-08-17 17:41:33.000,38.95469,-77.09354,228,ROUTE_ID +7167,2015-08-17 17:41:54.000,38.95314,-77.09557,228,ROUTE_ID +7167,2015-08-17 17:42:43.000,38.9511,-77.09823,228,ROUTE_ID +7167,2015-08-17 17:43:51.000,38.94938,-77.10039,228,ROUTE_ID +7167,2015-08-17 17:44:13.000,38.94919,-77.10123,228,ROUTE_ID +7167,2015-08-17 17:44:31.000,38.94847,-77.10048,228,ROUTE_ID +7167,2015-08-17 17:45:51.000,38.94571,-77.09668,228,ROUTE_ID +7167,2015-08-17 17:46:24.000,38.94441,-77.09491,228,ROUTE_ID +7167,2015-08-17 17:48:17.000,38.93759,-77.08624,228,ROUTE_ID +7167,2015-08-17 17:48:55.000,38.93649,-77.08402,228,ROUTE_ID +7167,2015-08-17 17:50:15.000,38.93276,-77.07894,228,ROUTE_ID +7167,2015-08-17 17:50:48.000,38.93156,-77.07724,228,ROUTE_ID +7167,2015-08-17 17:51:42.000,38.93134,-77.077,228,ROUTE_ID +7167,2015-08-17 17:52:17.000,38.92925,-77.0741,228,ROUTE_ID +7167,2015-08-17 17:52:54.000,38.92795,-77.07245,228,ROUTE_ID +7167,2015-08-17 17:54:30.000,38.92241,-77.06362,228,ROUTE_ID +7167,2015-08-17 17:54:56.000,38.92075,-77.06244,228,ROUTE_ID +7167,2015-08-17 17:55:28.000,38.91859,-77.05949,228,ROUTE_ID +7167,2015-08-17 17:57:19.000,38.91375,-77.05291,228,ROUTE_ID +7167,2015-08-17 17:57:35.000,38.91256,-77.05131,228,ROUTE_ID +7167,2015-08-17 17:59:25.000,38.91002,-77.04472,228,ROUTE_ID +7167,2015-08-17 17:59:57.000,38.90995,-77.04453,228,ROUTE_ID +7167,2015-08-17 18:01:25.000,38.90892,-77.0432,228,ROUTE_ID +7167,2015-08-17 18:02:58.000,38.9059,-77.04136,228,ROUTE_ID +7167,2015-08-17 18:03:28.000,38.9059,-77.04136,228,ROUTE_ID +7167,2015-08-17 18:04:05.000,38.90561,-77.04118,228,ROUTE_ID +7167,2015-08-17 18:05:39.000,38.90388,-77.04018,228,ROUTE_ID +7167,2015-08-17 18:05:45.000,38.9035,-77.04001,228,ROUTE_ID +7167,2015-08-17 18:07:24.000,38.90224,-77.03851,228,ROUTE_ID +7167,2015-08-17 18:08:26.000,38.90171,-77.03829,228,ROUTE_ID +7167,2015-08-17 18:08:46.000,38.90168,-77.03829,228,ROUTE_ID +7167,2015-08-17 18:09:21.000,38.90158,-77.03828,228,ROUTE_ID +7167,2015-08-17 18:09:54.000,38.90158,-77.03828,228,ROUTE_ID +7167,2015-08-17 18:10:58.000,38.90158,-77.03828,228,ROUTE_ID +7167,2015-08-17 18:12:31.000,38.90158,-77.03828,228,ROUTE_ID +7167,2015-08-17 18:13:04.000,38.90158,-77.03828,228,ROUTE_ID +7167,2015-08-17 18:13:34.000,38.90158,-77.03828,228,ROUTE_ID +7167,2015-08-17 18:14:33.000,38.90145,-77.03852,228,ROUTE_ID +7167,2015-08-17 18:15:06.000,38.90145,-77.03852,228,ROUTE_ID +7167,2015-08-17 18:16:13.000,38.90145,-77.03852,228,ROUTE_ID +7167,2015-08-17 18:16:46.000,38.90145,-77.03852,228,ROUTE_ID +7167,2015-08-17 18:17:20.000,38.90145,-77.03852,228,ROUTE_ID +7167,2015-08-17 18:18:25.000,38.90145,-77.03852,228,ROUTE_ID +7167,2015-08-17 18:18:59.000,38.90139,-77.03867,228,ROUTE_ID +7167,2015-08-17 18:20:00.000,38.90123,-77.04147,228,ROUTE_ID +7167,2015-08-17 18:20:30.000,38.90123,-77.04147,228,ROUTE_ID +7167,2015-08-17 18:21:09.000,38.90158,-77.0416,228,ROUTE_ID +7167,2015-08-17 18:22:15.000,38.9022,-77.0416,228,ROUTE_ID +7167,2015-08-17 18:22:50.000,38.90421,-77.04166,228,ROUTE_ID +7167,2015-08-17 18:23:22.000,38.90547,-77.04163,228,ROUTE_ID +7167,2015-08-17 18:24:17.000,38.90578,-77.04163,228,ROUTE_ID +7167,2015-08-17 18:24:52.000,38.90601,-77.04163,228,ROUTE_ID +7167,2015-08-17 18:25:25.000,38.90697,-77.0418,228,ROUTE_ID +7167,2015-08-17 18:25:58.000,38.9075,-77.04181,228,ROUTE_ID +7167,2015-08-17 18:26:28.000,38.90906,-77.04176,228,ROUTE_ID +7167,2015-08-17 18:27:25.000,38.90922,-77.04176,228,ROUTE_ID +7167,2015-08-17 18:28:26.000,38.90999,-77.04437,228,ROUTE_ID +7167,2015-08-17 18:28:59.000,38.91008,-77.04463,228,ROUTE_ID +7167,2015-08-17 18:29:19.000,38.9104,-77.04568,228,ROUTE_ID +7167,2015-08-17 18:30:07.000,38.91104,-77.04742,228,ROUTE_ID +7167,2015-08-17 18:31:04.000,38.91127,-77.04803,228,ROUTE_ID +7167,2015-08-17 18:31:42.000,38.91254,-77.05112,228,ROUTE_ID +7167,2015-08-17 18:32:18.000,38.91354,-77.0525,228,ROUTE_ID +7167,2015-08-17 18:33:04.000,38.91556,-77.05532,228,ROUTE_ID +7167,2015-08-17 18:33:39.000,38.91646,-77.05654,228,ROUTE_ID +7167,2015-08-17 18:34:09.000,38.91658,-77.05667,228,ROUTE_ID +7167,2015-08-17 18:35:12.000,38.91803,-77.05862,228,ROUTE_ID +7167,2015-08-17 18:35:30.000,38.91915,-77.06025,228,ROUTE_ID +7167,2015-08-17 18:36:02.000,38.92192,-77.06326,228,ROUTE_ID +7167,2015-08-17 18:37:12.000,38.92437,-77.06679,228,ROUTE_ID +7167,2015-08-17 18:37:44.000,38.92624,-77.06989,228,ROUTE_ID +7167,2015-08-17 18:38:39.000,38.9278,-77.07196,228,ROUTE_ID +7167,2015-08-17 18:39:12.000,38.92834,-77.07271,228,ROUTE_ID +7167,2015-08-17 18:39:44.000,38.92883,-77.07335,228,ROUTE_ID +7167,2015-08-17 18:40:33.000,38.93153,-77.07702,228,ROUTE_ID +7167,2015-08-17 18:41:06.000,38.93193,-77.07761,228,ROUTE_ID +7167,2015-08-17 18:41:42.000,38.93462,-77.08134,228,ROUTE_ID +7167,2015-08-17 18:42:28.000,38.93633,-77.08363,228,ROUTE_ID +7167,2015-08-17 18:43:18.000,38.93771,-77.08537,228,ROUTE_ID +7167,2015-08-17 18:44:24.000,38.9383,-77.08607,228,ROUTE_ID +7167,2015-08-17 18:45:19.000,38.94082,-77.0898,228,ROUTE_ID +7167,2015-08-17 18:46:27.000,38.9437,-77.09373,228,ROUTE_ID +7167,2015-08-17 18:47:15.000,38.94457,-77.09483,228,ROUTE_ID +7167,2015-08-17 18:48:14.000,38.94793,-77.09949,228,ROUTE_ID +7167,2015-08-17 18:48:59.000,38.94992,-77.0995,228,ROUTE_ID +7167,2015-08-17 18:50:22.000,38.95476,-77.09344,228,ROUTE_ID +7167,2015-08-17 18:50:52.000,38.95655,-77.09106,228,ROUTE_ID +7167,2015-08-17 18:51:24.000,38.95887,-77.08813,228,ROUTE_ID +7167,2015-08-17 18:51:57.000,38.95984,-77.08688,228,ROUTE_ID +7167,2015-08-17 18:52:27.000,38.96008,-77.08653,228,ROUTE_ID +7167,2015-08-17 18:52:55.000,38.96054,-77.08575,228,ROUTE_ID +7167,2015-08-17 18:54:00.000,38.96164,-77.08459,228,ROUTE_ID +7167,2015-08-17 18:54:30.000,38.96164,-77.08459,228,ROUTE_ID +7167,2015-08-17 18:55:02.000,38.96189,-77.08488,228,ROUTE_ID +7167,2015-08-17 18:55:32.000,38.96173,-77.08578,228,ROUTE_ID +7167,2015-08-17 18:56:41.000,38.96142,-77.08549,228,ROUTE_ID +7167,2015-08-17 18:57:12.000,38.96166,-77.08506,228,ROUTE_ID +7167,2015-08-17 18:57:57.000,38.96167,-77.08486,228,ROUTE_ID +7167,2015-08-17 18:58:28.000,38.96167,-77.08486,228,ROUTE_ID +7167,2015-08-17 18:59:00.000,38.96167,-77.08486,228,ROUTE_ID +7167,2015-08-17 19:00:04.000,38.96167,-77.08486,228,ROUTE_ID +7167,2015-08-17 19:00:34.000,38.96167,-77.08486,228,ROUTE_ID +7167,2015-08-17 19:01:41.000,38.96167,-77.08486,228,ROUTE_ID +7167,2015-08-17 19:02:14.000,38.96167,-77.08486,228,ROUTE_ID +7167,2015-08-17 19:02:48.000,38.96167,-77.08486,228,ROUTE_ID +7167,2015-08-17 19:03:20.000,38.96167,-77.08486,228,ROUTE_ID +7167,2015-08-17 19:04:24.000,38.96167,-77.08486,228,ROUTE_ID +7167,2015-08-17 19:04:57.000,38.96167,-77.08486,228,ROUTE_ID +7167,2015-08-17 19:05:27.000,38.96167,-77.08486,228,ROUTE_ID +7167,2015-08-17 19:06:29.000,38.96167,-77.08486,228,ROUTE_ID +7167,2015-08-17 19:07:01.000,38.96167,-77.08486,228,ROUTE_ID +7167,2015-08-17 19:07:31.000,38.96167,-77.08486,228,ROUTE_ID +7167,2015-08-17 19:08:34.000,38.96167,-77.08486,228,ROUTE_ID +7167,2015-08-17 19:09:07.000,38.96167,-77.08486,228,ROUTE_ID +7167,2015-08-17 19:09:39.000,38.96167,-77.08486,228,ROUTE_ID +7167,2015-08-17 19:10:11.000,38.96167,-77.08486,228,ROUTE_ID +7167,2015-08-17 19:10:43.000,38.96167,-77.08486,228,ROUTE_ID +7167,2015-08-17 19:11:15.000,38.96167,-77.08486,228,ROUTE_ID +7167,2015-08-17 19:12:20.000,38.96167,-77.08486,228,ROUTE_ID +7167,2015-08-17 19:13:01.000,38.96167,-77.08486,226,ROUTE_ID +7167,2015-08-17 19:13:31.000,38.96163,-77.08501,226,ROUTE_ID +7167,2015-08-17 19:14:37.000,38.96163,-77.08501,226,ROUTE_ID +7167,2015-08-17 19:15:09.000,38.96163,-77.08501,226,ROUTE_ID +7167,2015-08-17 19:15:41.000,38.96163,-77.08501,226,ROUTE_ID +7167,2015-08-17 19:16:24.000,38.96163,-77.08501,226,ROUTE_ID +7167,2015-08-17 19:16:57.000,38.96172,-77.08495,226,ROUTE_ID +7167,2015-08-17 19:18:00.000,38.96185,-77.0862,226,ROUTE_ID +7167,2015-08-17 19:18:37.000,38.96041,-77.0856,226,ROUTE_ID +7167,2015-08-17 19:19:12.000,38.95844,-77.08456,226,ROUTE_ID +7167,2015-08-17 19:20:00.000,38.95599,-77.08331,226,ROUTE_ID +7167,2015-08-17 19:20:35.000,38.95467,-77.08286,226,ROUTE_ID +7167,2015-08-17 19:21:43.000,38.95065,-77.08064,226,ROUTE_ID +7167,2015-08-17 19:22:15.000,38.94995,-77.0805,226,ROUTE_ID +7167,2015-08-17 19:22:33.000,38.94876,-77.0802,226,ROUTE_ID +7167,2015-08-17 19:23:35.000,38.94792,-77.07971,226,ROUTE_ID +7167,2015-08-17 19:24:14.000,38.945,-77.07993,226,ROUTE_ID +7167,2015-08-17 19:25:05.000,38.9408,-77.08356,226,ROUTE_ID +7167,2015-08-17 19:25:32.000,38.93843,-77.08556,226,ROUTE_ID +7167,2015-08-17 19:26:04.000,38.93783,-77.08624,226,ROUTE_ID +7167,2015-08-17 19:27:14.000,38.93554,-77.08711,226,ROUTE_ID +7167,2015-08-17 19:27:27.000,38.93528,-77.08691,226,ROUTE_ID +7167,2015-08-17 19:28:00.000,38.93452,-77.08643,226,ROUTE_ID +7167,2015-08-17 19:28:53.000,38.93239,-77.08524,226,ROUTE_ID +7167,2015-08-17 19:29:26.000,38.93065,-77.08427,226,ROUTE_ID +7167,2015-08-17 19:30:15.000,38.93028,-77.08289,226,ROUTE_ID +7167,2015-08-17 19:31:20.000,38.9291,-77.07785,226,ROUTE_ID +7167,2015-08-17 19:31:53.000,38.92899,-77.07413,226,ROUTE_ID +7167,2015-08-17 19:32:48.000,38.92794,-77.07243,226,ROUTE_ID +7167,2015-08-17 19:33:09.000,38.92712,-77.07128,226,ROUTE_ID +7167,2015-08-17 19:33:48.000,38.9247,-77.06791,226,ROUTE_ID +7167,2015-08-17 19:34:07.000,38.92424,-77.06622,226,ROUTE_ID +7167,2015-08-17 19:34:53.000,38.92078,-77.06247,226,ROUTE_ID +7167,2015-08-17 19:35:58.000,38.91656,-77.05678,226,ROUTE_ID +7167,2015-08-17 19:36:16.000,38.91623,-77.0563,226,ROUTE_ID +7167,2015-08-17 19:36:50.000,38.91568,-77.05556,226,ROUTE_ID +7167,2015-08-17 19:37:35.000,38.91256,-77.05131,226,ROUTE_ID +7167,2015-08-17 19:38:11.000,38.91164,-77.04928,226,ROUTE_ID +7167,2015-08-17 19:39:17.000,38.91121,-77.04802,226,ROUTE_ID +7167,2015-08-17 19:39:52.000,38.90991,-77.04444,226,ROUTE_ID +7167,2015-08-17 19:40:24.000,38.90841,-77.04285,226,ROUTE_ID +7167,2015-08-17 19:41:18.000,38.90721,-77.04207,226,ROUTE_ID +7167,2015-08-17 19:43:08.000,38.90546,-77.04108,226,ROUTE_ID +7167,2015-08-17 19:43:41.000,38.904,-77.04025,226,ROUTE_ID +7167,2015-08-17 19:44:13.000,38.904,-77.04025,226,ROUTE_ID +7167,2015-08-17 19:44:41.000,38.90353,-77.04002,226,ROUTE_ID +7167,2015-08-17 19:45:15.000,38.90239,-77.03934,226,ROUTE_ID +7167,2015-08-17 19:46:21.000,38.90163,-77.03859,226,ROUTE_ID +7167,2015-08-17 19:46:54.000,38.90163,-77.03859,226,ROUTE_ID +7167,2015-08-17 19:47:24.000,38.90163,-77.03859,226,ROUTE_ID +7167,2015-08-17 19:48:27.000,38.90163,-77.03859,226,ROUTE_ID +7167,2015-08-17 19:49:01.000,38.90163,-77.03859,226,ROUTE_ID +7167,2015-08-17 19:49:31.000,38.90163,-77.03859,226,ROUTE_ID +7167,2015-08-17 19:50:05.000,38.90163,-77.03859,226,ROUTE_ID +7167,2015-08-17 19:50:41.000,38.90163,-77.03859,226,ROUTE_ID +7167,2015-08-17 19:51:47.000,38.90162,-77.03859,226,ROUTE_ID +7167,2015-08-17 19:52:13.000,38.90162,-77.03859,226,ROUTE_ID +7167,2015-08-17 19:53:01.000,38.90162,-77.03859,226,ROUTE_ID +7167,2015-08-17 19:53:33.000,38.90162,-77.0386,226,ROUTE_ID +7167,2015-08-17 19:54:40.000,38.90162,-77.0386,226,ROUTE_ID +7167,2015-08-17 19:55:47.000,38.90162,-77.0386,226,ROUTE_ID +7167,2015-08-17 19:56:34.000,38.90137,-77.03967,226,ROUTE_ID +7167,2015-08-17 19:57:36.000,38.90328,-77.04165,226,ROUTE_ID +7167,2015-08-17 19:58:09.000,38.90549,-77.04163,226,ROUTE_ID +7167,2015-08-17 19:59:36.000,38.90578,-77.04163,226,ROUTE_ID +7167,2015-08-17 20:00:11.000,38.90613,-77.04163,226,ROUTE_ID +7167,2015-08-17 20:01:17.000,38.90766,-77.04181,226,ROUTE_ID +7167,2015-08-17 20:01:50.000,38.90975,-77.04272,226,ROUTE_ID +7167,2015-08-17 20:03:07.000,38.91002,-77.04403,226,ROUTE_ID +7167,2015-08-17 20:03:34.000,38.91038,-77.04546,226,ROUTE_ID +7167,2015-08-17 20:03:55.000,38.91061,-77.04628,226,ROUTE_ID +7167,2015-08-17 20:05:01.000,38.91185,-77.04971,226,ROUTE_ID +7167,2015-08-17 20:05:43.000,38.91406,-77.05326,226,ROUTE_ID +7167,2015-08-17 20:06:03.000,38.91556,-77.05533,226,ROUTE_ID +7167,2015-08-17 20:07:11.000,38.91715,-77.05742,226,ROUTE_ID +7167,2015-08-17 20:07:38.000,38.91913,-77.06023,226,ROUTE_ID +7167,2015-08-17 20:08:43.000,38.92437,-77.06677,226,ROUTE_ID +7167,2015-08-17 20:09:14.000,38.92619,-77.06983,226,ROUTE_ID +7167,2015-08-17 20:10:21.000,38.92791,-77.0721,226,ROUTE_ID +7167,2015-08-17 20:10:53.000,38.92821,-77.07252,226,ROUTE_ID +7167,2015-08-17 20:11:45.000,38.92925,-77.07398,226,ROUTE_ID +7167,2015-08-17 20:12:18.000,38.93106,-77.07647,226,ROUTE_ID +7167,2015-08-17 20:13:04.000,38.93086,-77.07742,226,ROUTE_ID +7167,2015-08-17 20:13:57.000,38.92954,-77.0787,226,ROUTE_ID +7167,2015-08-17 20:14:30.000,38.93037,-77.08223,226,ROUTE_ID +7167,2015-08-17 20:15:03.000,38.93037,-77.08372,226,ROUTE_ID +7167,2015-08-17 20:15:41.000,38.93108,-77.08443,226,ROUTE_ID +7167,2015-08-17 20:16:53.000,38.93454,-77.08635,226,ROUTE_ID +7167,2015-08-17 20:17:56.000,38.93575,-77.08756,226,ROUTE_ID +7167,2015-08-17 20:18:56.000,38.93892,-77.08501,226,ROUTE_ID +7167,2015-08-17 20:19:12.000,38.93923,-77.08474,226,ROUTE_ID +7167,2015-08-17 20:19:44.000,38.94188,-77.0825,226,ROUTE_ID +7167,2015-08-17 20:20:16.000,38.94296,-77.08158,226,ROUTE_ID +7167,2015-08-17 20:20:28.000,38.94357,-77.08105,226,ROUTE_ID +7167,2015-08-17 20:21:00.000,38.94598,-77.0789,226,ROUTE_ID +7167,2015-08-17 20:21:43.000,38.94657,-77.0789,226,ROUTE_ID +7167,2015-08-17 20:22:40.000,38.95124,-77.0808,226,ROUTE_ID +7167,2015-08-17 20:23:13.000,38.95299,-77.08168,226,ROUTE_ID +7167,2015-08-17 20:23:36.000,38.95475,-77.08261,226,ROUTE_ID +7167,2015-08-17 20:24:33.000,38.95868,-77.08459,226,ROUTE_ID +7167,2015-08-17 20:25:04.000,38.95919,-77.08489,226,ROUTE_ID +7167,2015-08-17 20:26:15.000,38.9616,-77.08479,226,ROUTE_ID +7167,2015-08-17 20:26:48.000,38.96168,-77.08603,226,ROUTE_ID +7167,2015-08-17 20:27:20.000,38.96149,-77.08588,226,ROUTE_ID +7167,2015-08-17 20:28:17.000,38.96149,-77.08588,226,ROUTE_ID +7167,2015-08-17 20:28:49.000,38.96149,-77.08588,226,ROUTE_ID +7167,2015-08-17 20:29:21.000,38.96149,-77.08588,226,ROUTE_ID +7167,2015-08-17 20:29:53.000,38.96149,-77.08588,226,ROUTE_ID +7167,2015-08-17 20:30:23.000,38.96149,-77.08588,226,ROUTE_ID +7167,2015-08-17 20:30:56.000,38.96149,-77.08588,226,ROUTE_ID +7167,2015-08-17 20:31:27.000,38.96149,-77.08588,226,ROUTE_ID +7167,2015-08-17 20:32:30.000,38.96149,-77.08588,226,ROUTE_ID +7167,2015-08-17 20:33:32.000,38.96149,-77.08588,226,ROUTE_ID +7167,2015-08-17 20:34:40.000,38.96149,-77.08588,226,ROUTE_ID +7167,2015-08-17 20:35:12.000,38.96145,-77.08527,226,ROUTE_ID +7167,2015-08-17 20:35:45.000,38.96146,-77.08525,226,ROUTE_ID +7167,2015-08-17 21:18:02.000,38.96633,-77.07734,221,ROUTE_ID +7167,2015-08-17 21:19:05.000,38.96633,-77.07734,221,ROUTE_ID +7167,2015-08-17 21:19:37.000,38.96633,-77.07734,221,ROUTE_ID +7167,2015-08-17 21:20:09.000,38.96633,-77.07734,221,ROUTE_ID +7167,2015-08-17 21:21:13.000,38.96633,-77.07734,221,ROUTE_ID +7167,2015-08-17 21:21:45.000,38.96633,-77.07734,221,ROUTE_ID +7167,2015-08-17 21:22:17.000,38.96633,-77.07734,221,ROUTE_ID +7167,2015-08-17 21:23:22.000,38.96633,-77.07734,221,ROUTE_ID +7167,2015-08-17 21:23:53.000,38.96633,-77.07734,221,ROUTE_ID +7167,2015-08-17 21:24:55.000,38.96633,-77.07734,221,ROUTE_ID +7167,2015-08-17 21:25:25.000,38.96633,-77.07734,221,ROUTE_ID +7167,2015-08-17 21:25:57.000,38.96633,-77.07734,221,ROUTE_ID +7167,2015-08-17 21:26:55.000,38.96633,-77.07734,221,ROUTE_ID +7167,2015-08-17 21:27:26.000,38.96655,-77.07715,221,ROUTE_ID +7167,2015-08-17 21:27:51.000,38.96609,-77.07632,221,ROUTE_ID +7167,2015-08-17 21:29:06.000,38.96008,-77.07278,221,ROUTE_ID +7167,2015-08-17 21:29:18.000,38.95895,-77.07215,221,ROUTE_ID +7167,2015-08-17 21:29:41.000,38.95697,-77.07097,221,ROUTE_ID +7167,2015-08-17 21:31:05.000,38.95528,-77.06995,221,ROUTE_ID +7167,2015-08-17 21:31:35.000,38.95275,-77.06853,221,ROUTE_ID +7167,2015-08-17 21:32:01.000,38.95045,-77.06717,221,ROUTE_ID +7167,2015-08-17 21:32:59.000,38.9481,-77.06583,221,ROUTE_ID +7167,2015-08-17 21:33:18.000,38.9475,-77.06544,221,ROUTE_ID +7167,2015-08-17 21:34:22.000,38.94382,-77.06332,221,ROUTE_ID +7167,2015-08-17 21:34:55.000,38.94333,-77.06304,221,ROUTE_ID +7167,2015-08-17 21:35:17.000,38.9432,-77.06306,221,ROUTE_ID +7167,2015-08-17 21:39:45.000,38.94243,-77.06243,221,ROUTE_ID +7167,2015-08-17 21:41:09.000,38.94441,-77.06352,221,ROUTE_ID +7167,2015-08-17 21:41:41.000,38.94471,-77.06371,221,ROUTE_ID +7167,2015-08-17 21:42:46.000,38.94471,-77.06371,221,ROUTE_ID +7167,2015-08-17 21:43:18.000,38.94471,-77.06371,221,ROUTE_ID +7167,2015-08-17 21:44:23.000,38.94471,-77.06371,221,ROUTE_ID +7167,2015-08-17 21:44:54.000,38.94471,-77.06371,221,ROUTE_ID +7167,2015-08-17 21:45:25.000,38.94471,-77.06371,221,ROUTE_ID +7167,2015-08-17 21:45:57.000,38.94471,-77.06371,221,ROUTE_ID +7167,2015-08-17 21:47:00.000,38.94471,-77.06371,221,ROUTE_ID +7167,2015-08-17 21:47:30.000,38.94471,-77.06371,221,ROUTE_ID +7167,2015-08-17 21:49:06.000,38.94471,-77.06371,221,ROUTE_ID +7167,2015-08-17 21:50:11.000,38.94471,-77.06371,221,ROUTE_ID +7167,2015-08-17 21:50:43.000,38.94471,-77.06371,221,ROUTE_ID +7167,2015-08-17 21:51:15.000,38.94471,-77.06371,221,ROUTE_ID +7167,2015-08-17 21:51:47.000,38.94471,-77.06371,221,ROUTE_ID +7167,2015-08-17 21:52:51.000,38.94471,-77.06371,221,ROUTE_ID +7167,2015-08-17 21:53:23.000,38.94471,-77.06371,221,ROUTE_ID +7167,2015-08-17 21:54:25.000,38.94471,-77.06371,221,ROUTE_ID +7167,2015-08-17 21:55:27.000,38.94471,-77.06371,221,ROUTE_ID +7167,2015-08-17 21:56:00.000,38.94471,-77.06371,221,ROUTE_ID +7167,2015-08-17 21:56:48.000,38.94471,-77.06371,221,ROUTE_ID +7167,2015-08-17 21:57:27.000,38.94491,-77.06387,221,ROUTE_ID +7167,2015-08-17 21:58:01.000,38.94567,-77.06432,221,ROUTE_ID +7167,2015-08-17 21:59:09.000,38.95081,-77.0673,221,ROUTE_ID +7167,2015-08-17 21:59:42.000,38.95211,-77.06804,221,ROUTE_ID +7167,2015-08-17 22:00:14.000,38.9521,-77.06803,221,ROUTE_ID +7167,2015-08-17 22:01:28.000,38.95476,-77.06961,221,ROUTE_ID +7167,2015-08-17 22:02:01.000,38.95591,-77.07027,221,ROUTE_ID +7167,2015-08-17 22:03:46.000,38.95788,-77.07141,221,ROUTE_ID +7167,2015-08-17 22:04:54.000,38.96088,-77.07314,221,ROUTE_ID +7167,2015-08-17 22:05:24.000,38.96091,-77.07315,221,ROUTE_ID +7167,2015-08-17 22:05:56.000,38.96091,-77.07315,221,ROUTE_ID +7167,2015-08-17 22:06:39.000,38.96337,-77.07456,221,ROUTE_ID +7167,2015-08-17 22:07:11.000,38.96468,-77.07528,221,ROUTE_ID +7167,2015-08-17 22:08:28.000,38.96653,-77.07676,221,ROUTE_ID +7167,2015-08-17 22:09:00.000,38.9664,-77.07703,221,ROUTE_ID +7167,2015-08-17 22:09:30.000,38.9664,-77.07703,221,ROUTE_ID +7167,2015-08-17 22:10:03.000,38.9664,-77.07703,221,ROUTE_ID +7167,2015-08-17 22:10:33.000,38.9664,-77.07703,221,ROUTE_ID +7167,2015-08-17 22:11:05.000,38.9664,-77.07703,221,ROUTE_ID +7167,2015-08-17 22:12:31.000,38.9664,-77.07703,221,ROUTE_ID +7167,2015-08-17 22:13:05.000,38.9664,-77.07703,221,ROUTE_ID +7167,2015-08-17 22:13:38.000,38.9664,-77.07703,221,ROUTE_ID +7167,2015-08-17 22:14:10.000,38.9664,-77.07703,221,ROUTE_ID +7167,2015-08-17 22:14:41.000,38.9664,-77.07703,221,ROUTE_ID +7167,2015-08-17 22:15:33.000,38.9664,-77.07703,221,ROUTE_ID +7167,2015-08-17 22:16:06.000,38.9664,-77.07703,221,ROUTE_ID +7167,2015-08-17 22:16:39.000,38.96634,-77.07717,221,ROUTE_ID +7167,2015-08-17 22:17:43.000,38.9667,-77.07693,221,ROUTE_ID +7167,2015-08-17 22:18:17.000,38.96609,-77.07632,221,ROUTE_ID +7167,2015-08-17 22:19:26.000,38.96407,-77.07512,221,ROUTE_ID +7167,2015-08-17 22:20:00.000,38.96292,-77.07443,221,ROUTE_ID +7167,2015-08-17 22:21:08.000,38.95987,-77.07269,221,ROUTE_ID +7167,2015-08-17 22:21:45.000,38.95727,-77.07116,221,ROUTE_ID +7167,2015-08-17 22:22:49.000,38.95521,-77.06991,221,ROUTE_ID +7167,2015-08-17 22:24:00.000,38.95077,-77.06737,221,ROUTE_ID +7167,2015-08-17 22:24:18.000,38.94961,-77.0667,221,ROUTE_ID +7167,2015-08-17 22:25:35.000,38.94633,-77.06471,221,ROUTE_ID +7167,2015-08-17 22:26:43.000,38.9444,-77.06365,221,ROUTE_ID +7167,2015-08-17 22:27:42.000,38.94153,-77.062,221,ROUTE_ID +7167,2015-08-17 22:28:12.000,38.93932,-77.06078,221,ROUTE_ID +7167,2015-08-17 22:28:31.000,38.93816,-77.06007,221,ROUTE_ID +7167,2015-08-17 22:29:03.000,38.93706,-77.05943,221,ROUTE_ID +7167,2015-08-17 22:29:33.000,38.93706,-77.05943,221,ROUTE_ID +7167,2015-08-17 22:30:10.000,38.93613,-77.05888,221,ROUTE_ID +7167,2015-08-17 22:30:48.000,38.9355,-77.05854,221,ROUTE_ID +7167,2015-08-17 22:31:21.000,38.9338,-77.05754,221,ROUTE_ID +7167,2015-08-17 22:32:36.000,38.93002,-77.05531,221,ROUTE_ID +7167,2015-08-17 22:33:13.000,38.92749,-77.05383,221,ROUTE_ID +7167,2015-08-17 22:33:46.000,38.92582,-77.05293,221,ROUTE_ID +7167,2015-08-17 22:34:20.000,38.92577,-77.05291,221,ROUTE_ID +7167,2015-08-17 22:34:52.000,38.92577,-77.05291,221,ROUTE_ID +7167,2015-08-17 22:35:49.000,38.92545,-77.0527,221,ROUTE_ID +7167,2015-08-17 22:36:23.000,38.92411,-77.05266,221,ROUTE_ID +7167,2015-08-17 22:36:56.000,38.92382,-77.05265,221,ROUTE_ID +7167,2015-08-17 22:37:26.000,38.92344,-77.05236,221,ROUTE_ID +7167,2015-08-17 22:38:14.000,38.92344,-77.05039,221,ROUTE_ID +7167,2015-08-17 22:38:44.000,38.92324,-77.04724,221,ROUTE_ID +7167,2015-08-17 22:40:15.000,38.92323,-77.04397,221,ROUTE_ID +7167,2015-08-17 22:40:48.000,38.92265,-77.04282,221,ROUTE_ID +7167,2015-08-17 22:41:54.000,38.92081,-77.04384,221,ROUTE_ID +7167,2015-08-17 22:42:24.000,38.91964,-77.04445,221,ROUTE_ID +7167,2015-08-17 22:42:48.000,38.9191,-77.04479,221,ROUTE_ID +7167,2015-08-17 22:43:43.000,38.91698,-77.04623,221,ROUTE_ID +7167,2015-08-17 22:44:33.000,38.91456,-77.04625,221,ROUTE_ID +7167,2015-08-17 22:45:06.000,38.91285,-77.04543,221,ROUTE_ID +7167,2015-08-17 22:45:39.000,38.91285,-77.04543,221,ROUTE_ID +7167,2015-08-17 22:46:40.000,38.90957,-77.04347,221,ROUTE_ID +7167,2015-08-17 22:47:14.000,38.90718,-77.04205,221,ROUTE_ID +7167,2015-08-17 22:47:46.000,38.90594,-77.04137,221,ROUTE_ID +7167,2015-08-17 22:48:51.000,38.90594,-77.04137,221,ROUTE_ID +7167,2015-08-17 22:49:03.000,38.90559,-77.04118,221,ROUTE_ID +7167,2015-08-17 22:49:59.000,38.90165,-77.03949,221,ROUTE_ID +7167,2015-08-17 23:03:24.000,38.90115,-77.03935,221,ROUTE_ID +7167,2015-08-17 23:04:23.000,38.90237,-77.03934,221,ROUTE_ID +7167,2015-08-17 23:04:55.000,38.90285,-77.03938,221,ROUTE_ID +7167,2015-08-17 23:05:57.000,38.90336,-77.03973,221,ROUTE_ID +7167,2015-08-17 23:07:00.000,38.90528,-77.04076,221,ROUTE_ID +7167,2015-08-17 23:08:07.000,38.90639,-77.0414,221,ROUTE_ID +7167,2015-08-17 23:08:39.000,38.90639,-77.0414,221,ROUTE_ID +7167,2015-08-17 23:09:12.000,38.9084,-77.04276,221,ROUTE_ID +7167,2015-08-17 23:09:42.000,38.9112,-77.04436,221,ROUTE_ID +7167,2015-08-17 23:10:15.000,38.91354,-77.04565,221,ROUTE_ID +7167,2015-08-17 23:11:15.000,38.91586,-77.04623,221,ROUTE_ID +7167,2015-08-17 23:11:36.000,38.9173,-77.04585,221,ROUTE_ID +7167,2015-08-17 23:12:24.000,38.91903,-77.04475,221,ROUTE_ID +7167,2015-08-17 23:13:40.000,38.92257,-77.0428,221,ROUTE_ID +7167,2015-08-17 23:14:13.000,38.92257,-77.0428,221,ROUTE_ID +7167,2015-08-17 23:14:47.000,38.92273,-77.04268,221,ROUTE_ID +7167,2015-08-17 23:15:24.000,38.92321,-77.04321,221,ROUTE_ID +7167,2015-08-17 23:15:57.000,38.92324,-77.04352,221,ROUTE_ID +7167,2015-08-17 23:16:27.000,38.92323,-77.04385,221,ROUTE_ID +7167,2015-08-17 23:17:01.000,38.92327,-77.04696,221,ROUTE_ID +7167,2015-08-17 23:17:24.000,38.92329,-77.04734,221,ROUTE_ID +7167,2015-08-17 23:17:57.000,38.92352,-77.05075,221,ROUTE_ID +7167,2015-08-17 23:18:59.000,38.92358,-77.05124,221,ROUTE_ID +7167,2015-08-17 23:19:29.000,38.92463,-77.05207,221,ROUTE_ID +7167,2015-08-17 23:20:33.000,38.92801,-77.05405,221,ROUTE_ID +7167,2015-08-17 23:21:06.000,38.9297,-77.05497,221,ROUTE_ID +7167,2015-08-17 23:22:00.000,38.93152,-77.05606,221,ROUTE_ID +7167,2015-08-17 23:22:30.000,38.93159,-77.05611,221,ROUTE_ID +7167,2015-08-17 23:22:57.000,38.93354,-77.05727,221,ROUTE_ID +7167,2015-08-17 23:23:28.000,38.93536,-77.05825,221,ROUTE_ID +7167,2015-08-17 23:24:00.000,38.93544,-77.0583,221,ROUTE_ID +7167,2015-08-17 23:24:29.000,38.93602,-77.05868,221,ROUTE_ID +7167,2015-08-17 23:25:22.000,38.93925,-77.06052,221,ROUTE_ID +7167,2015-08-17 23:25:56.000,38.94071,-77.06132,221,ROUTE_ID +7167,2015-08-17 23:27:16.000,38.94323,-77.06281,221,ROUTE_ID +7167,2015-08-17 23:27:49.000,38.94458,-77.06363,221,ROUTE_ID +7167,2015-08-17 23:28:07.000,38.94498,-77.06391,221,ROUTE_ID +7167,2015-08-17 23:29:15.000,38.94771,-77.06548,221,ROUTE_ID +7167,2015-08-17 23:29:47.000,38.9495,-77.06651,221,ROUTE_ID +7167,2015-08-17 23:30:22.000,38.94969,-77.06664,221,ROUTE_ID +7167,2015-08-17 23:31:14.000,38.95223,-77.06812,221,ROUTE_ID +7167,2015-08-17 23:31:49.000,38.95451,-77.06946,221,ROUTE_ID +7167,2015-08-17 23:32:53.000,38.95589,-77.07025,221,ROUTE_ID +7167,2015-08-17 23:33:41.000,38.95784,-77.07139,221,ROUTE_ID +7167,2015-08-17 23:34:14.000,38.95886,-77.07199,221,ROUTE_ID +7167,2015-08-17 23:34:37.000,38.95974,-77.0725,221,ROUTE_ID +7167,2015-08-17 23:35:56.000,38.96288,-77.07426,221,ROUTE_ID +7167,2015-08-17 23:36:11.000,38.96314,-77.07442,221,ROUTE_ID +7167,2015-08-17 23:37:28.000,38.96615,-77.07626,221,ROUTE_ID +7167,2015-08-17 23:38:01.000,38.96646,-77.07693,221,ROUTE_ID +7167,2015-08-17 23:38:31.000,38.96646,-77.07693,221,ROUTE_ID +7167,2015-08-17 23:39:03.000,38.96646,-77.07693,221,ROUTE_ID +7167,2015-08-17 23:40:05.000,38.96646,-77.07693,221,ROUTE_ID +7167,2015-08-17 23:41:09.000,38.96646,-77.07693,221,ROUTE_ID +7167,2015-08-17 23:42:13.000,38.96646,-77.07693,221,ROUTE_ID +7167,2015-08-17 23:42:45.000,38.96646,-77.07693,221,ROUTE_ID +7167,2015-08-17 23:43:17.000,38.96646,-77.07693,221,ROUTE_ID +7167,2015-08-17 23:43:49.000,38.96646,-77.07693,221,ROUTE_ID +7167,2015-08-17 23:44:21.000,38.96646,-77.07693,221,ROUTE_ID +7167,2015-08-17 23:45:00.000,38.96646,-77.07693,221,ROUTE_ID +7167,2015-08-17 23:46:02.000,38.96637,-77.07705,221,ROUTE_ID +7167,2015-08-17 23:46:32.000,38.96637,-77.07705,221,ROUTE_ID +7167,2015-08-17 23:47:05.000,38.96637,-77.07705,221,ROUTE_ID +7167,2015-08-17 23:47:41.000,38.9667,-77.07693,221,ROUTE_ID +7167,2015-08-17 23:48:13.000,38.9667,-77.07693,221,ROUTE_ID +7167,2015-08-17 23:48:56.000,38.96609,-77.07632,221,ROUTE_ID +7167,2015-08-17 23:49:27.000,38.96413,-77.07516,221,ROUTE_ID +7167,2015-08-17 23:50:02.000,38.9616,-77.07368,221,ROUTE_ID +7167,2015-08-17 23:50:40.000,38.9591,-77.07225,221,ROUTE_ID +7167,2015-08-17 23:51:08.000,38.95697,-77.07097,221,ROUTE_ID +7167,2015-08-17 23:52:13.000,38.95528,-77.06995,221,ROUTE_ID +7167,2015-08-17 23:53:03.000,38.95134,-77.0677,221,ROUTE_ID +7167,2015-08-17 23:53:31.000,38.94916,-77.06641,221,ROUTE_ID +7167,2015-08-17 23:54:03.000,38.9481,-77.06583,221,ROUTE_ID +7167,2015-08-17 23:54:35.000,38.9481,-77.06583,221,ROUTE_ID +7167,2015-08-17 23:55:07.000,38.9481,-77.06583,221,ROUTE_ID +7167,2015-08-17 23:55:58.000,38.9475,-77.06544,221,ROUTE_ID +7167,2015-08-17 23:56:59.000,38.94453,-77.06373,221,ROUTE_ID +7167,2015-08-17 23:57:50.000,38.94382,-77.06332,221,ROUTE_ID +7167,2015-08-17 23:58:36.000,38.94041,-77.06137,221,ROUTE_ID +7167,2015-08-17 23:59:37.000,38.93617,-77.05891,221,ROUTE_ID +7167,2015-08-18 00:00:08.000,38.93574,-77.05868,221,ROUTE_ID +7167,2015-08-18 00:01:19.000,38.93287,-77.05699,221,ROUTE_ID +7167,2015-08-18 00:02:39.000,38.92701,-77.05355,221,ROUTE_ID +7167,2015-08-18 00:03:54.000,38.92506,-77.05248,221,ROUTE_ID +7167,2015-08-18 00:04:30.000,38.92368,-77.05264,221,ROUTE_ID +7167,2015-08-18 00:05:03.000,38.92368,-77.05264,221,ROUTE_ID +7167,2015-08-18 00:06:08.000,38.92346,-77.05103,221,ROUTE_ID +7167,2015-08-18 00:06:41.000,38.92345,-77.0508,221,ROUTE_ID +7167,2015-08-18 00:07:45.000,38.92345,-77.0508,221,ROUTE_ID +7167,2015-08-18 00:08:39.000,38.92341,-77.04997,221,ROUTE_ID +7167,2015-08-18 00:09:12.000,38.92323,-77.04719,221,ROUTE_ID +7167,2015-08-18 00:09:19.000,38.92323,-77.04642,221,ROUTE_ID +7167,2015-08-18 00:10:30.000,38.92183,-77.0434,221,ROUTE_ID +7167,2015-08-18 00:11:32.000,38.91905,-77.04482,221,ROUTE_ID +7167,2015-08-18 00:11:57.000,38.91853,-77.04514,221,ROUTE_ID +7167,2015-08-18 00:13:16.000,38.91534,-77.04636,221,ROUTE_ID +7167,2015-08-18 00:13:30.000,38.91453,-77.04625,221,ROUTE_ID +7167,2015-08-18 00:14:03.000,38.91403,-77.04604,221,ROUTE_ID +7167,2015-08-18 00:15:15.000,38.90886,-77.04307,221,ROUTE_ID +7167,2015-08-18 00:16:19.000,38.90587,-77.04135,221,ROUTE_ID +7167,2015-08-18 00:16:51.000,38.90587,-77.04135,221,ROUTE_ID +7167,2015-08-18 00:18:10.000,38.90427,-77.04038,221,ROUTE_ID +7167,2015-08-18 00:19:49.000,38.904,-77.04022,221,ROUTE_ID +7167,2015-08-18 00:20:02.000,38.90318,-77.0397,221,ROUTE_ID +7167,2015-08-18 00:20:59.000,38.90183,-77.03949,221,ROUTE_ID +7167,2015-08-18 00:21:29.000,38.90183,-77.03949,221,ROUTE_ID +7167,2015-08-18 00:22:31.000,38.90183,-77.03949,221,ROUTE_ID +7167,2015-08-18 00:23:38.000,38.90183,-77.03949,221,ROUTE_ID +7167,2015-08-18 00:24:44.000,38.90183,-77.03949,221,ROUTE_ID +7167,2015-08-18 00:25:49.000,38.90183,-77.03949,221,ROUTE_ID +7167,2015-08-18 00:26:21.000,38.90183,-77.03949,221,ROUTE_ID +7167,2015-08-18 00:27:09.000,38.90034,-77.03939,221,ROUTE_ID +7167,2015-08-18 00:27:43.000,38.89934,-77.03934,221,ROUTE_ID +7167,2015-08-18 00:28:48.000,38.90014,-77.0415,221,ROUTE_ID +7167,2015-08-18 00:29:21.000,38.90031,-77.04013,221,ROUTE_ID +7167,2015-08-18 00:30:24.000,38.90115,-77.03935,221,ROUTE_ID +7167,2015-08-18 00:30:58.000,38.90115,-77.03935,221,ROUTE_ID +7167,2015-08-18 00:31:28.000,38.90115,-77.03935,221,ROUTE_ID +7167,2015-08-18 00:32:07.000,38.90216,-77.03934,221,ROUTE_ID +7167,2015-08-18 00:33:22.000,38.90446,-77.04028,221,ROUTE_ID +7167,2015-08-18 00:33:58.000,38.90567,-77.04094,221,ROUTE_ID +7167,2015-08-18 00:35:02.000,38.90639,-77.0414,221,ROUTE_ID +7167,2015-08-18 00:35:27.000,38.90672,-77.0417,221,ROUTE_ID +7167,2015-08-18 00:36:33.000,38.9119,-77.04473,221,ROUTE_ID +7167,2015-08-18 00:37:05.000,38.91254,-77.0451,221,ROUTE_ID +7167,2015-08-18 00:38:45.000,38.91586,-77.04623,221,ROUTE_ID +7167,2015-08-18 00:39:07.000,38.91729,-77.04585,221,ROUTE_ID +7167,2015-08-18 00:40:17.000,38.92007,-77.04404,221,ROUTE_ID +7167,2015-08-18 00:40:49.000,38.92083,-77.04366,221,ROUTE_ID +7167,2015-08-18 00:41:05.000,38.92115,-77.04368,221,ROUTE_ID +7167,2015-08-18 00:42:15.000,38.92321,-77.04322,221,ROUTE_ID +7167,2015-08-18 00:42:48.000,38.92324,-77.04344,221,ROUTE_ID +7167,2015-08-18 00:43:21.000,38.92323,-77.04539,221,ROUTE_ID +7167,2015-08-18 00:43:54.000,38.92332,-77.04784,221,ROUTE_ID +7167,2015-08-18 00:45:24.000,38.92566,-77.05268,221,ROUTE_ID +7167,2015-08-18 00:46:49.000,38.93171,-77.05618,221,ROUTE_ID +7167,2015-08-18 00:47:22.000,38.93334,-77.05713,221,ROUTE_ID +7167,2015-08-18 00:47:50.000,38.93353,-77.05726,221,ROUTE_ID +7167,2015-08-18 00:48:55.000,38.93539,-77.05827,221,ROUTE_ID +7167,2015-08-18 00:49:08.000,38.936,-77.05867,221,ROUTE_ID +7167,2015-08-18 00:50:14.000,38.93702,-77.05921,221,ROUTE_ID +7167,2015-08-18 00:50:57.000,38.93776,-77.05968,221,ROUTE_ID +7167,2015-08-18 00:52:03.000,38.9409,-77.06143,221,ROUTE_ID +7167,2015-08-18 00:52:32.000,38.94316,-77.06276,221,ROUTE_ID +7167,2015-08-18 00:53:38.000,38.94795,-77.06564,221,ROUTE_ID +7167,2015-08-18 00:54:23.000,38.94971,-77.06665,221,ROUTE_ID +7167,2015-08-18 00:55:11.000,38.95221,-77.0681,221,ROUTE_ID +7167,2015-08-18 00:56:28.000,38.9558,-77.07018,221,ROUTE_ID +7167,2015-08-18 00:57:00.000,38.95587,-77.07023,221,ROUTE_ID +7167,2015-08-18 00:57:37.000,38.95782,-77.07138,221,ROUTE_ID +7167,2015-08-18 00:58:11.000,38.95885,-77.07198,221,ROUTE_ID +7167,2015-08-18 00:59:23.000,38.96111,-77.07329,221,ROUTE_ID +7167,2015-08-18 00:59:55.000,38.96332,-77.07453,221,ROUTE_ID +7167,2015-08-18 07:22:32.000,38.95549,-76.9939,178,ROUTE_ID +7167,2015-08-18 07:22:58.000,38.95574,-76.99109,178,ROUTE_ID +7167,2015-08-18 07:23:21.000,38.95668,-76.99078,178,ROUTE_ID +7167,2015-08-18 07:24:55.000,38.95691,-76.99115,178,ROUTE_ID +7167,2015-08-18 07:25:29.000,38.95691,-76.99115,178,ROUTE_ID +7167,2015-08-18 07:26:35.000,38.95694,-76.99117,178,ROUTE_ID +7167,2015-08-18 07:28:34.000,38.95808,-76.99525,178,ROUTE_ID +7167,2015-08-18 07:29:04.000,38.95922,-76.99748,178,ROUTE_ID +7167,2015-08-18 07:30:28.000,38.96078,-76.99909,178,ROUTE_ID +7167,2015-08-18 07:33:01.000,38.95731,-77.00208,178,ROUTE_ID +7167,2015-08-18 07:34:59.000,38.95574,-77.00452,178,ROUTE_ID +7167,2015-08-18 07:35:32.000,38.95567,-77.0046,178,ROUTE_ID +7167,2015-08-18 07:36:02.000,38.954,-77.0046,178,ROUTE_ID +7167,2015-08-18 07:37:09.000,38.95232,-77.00182,178,ROUTE_ID +7167,2015-08-18 07:37:43.000,38.95274,-77.00035,178,ROUTE_ID +7167,2015-08-18 07:38:17.000,38.95254,-77.00249,178,ROUTE_ID +7167,2015-08-18 07:38:49.000,38.95257,-77.00289,178,ROUTE_ID +7167,2015-08-18 07:39:21.000,38.95274,-77.00342,178,ROUTE_ID +7167,2015-08-18 07:40:14.000,38.95274,-77.00342,178,ROUTE_ID +7167,2015-08-18 07:41:43.000,38.95542,-77.00464,178,ROUTE_ID +7167,2015-08-18 07:42:49.000,38.95555,-77.00462,178,ROUTE_ID +7167,2015-08-18 07:43:22.000,38.95559,-77.00553,178,ROUTE_ID +7167,2015-08-18 07:45:49.000,38.95494,-77.0086,178,ROUTE_ID +7167,2015-08-18 07:46:53.000,38.95488,-77.00914,178,ROUTE_ID +7167,2015-08-18 07:47:25.000,38.95522,-77.01031,178,ROUTE_ID \ No newline at end of file diff --git a/transitclock/src/test/resources/avltest_values.csv b/transitclock/src/test/resources/avltest_values.csv new file mode 100644 index 000000000..9ecbac5a3 --- /dev/null +++ b/transitclock/src/test/resources/avltest_values.csv @@ -0,0 +1,3311 @@ +71,39642,5974,7167,2,2015-08-16 11:04:49.000000,2015-08-16 11:04:49.517000 +71,27196,5974,7167,3,2015-08-16 11:04:49.000000,2015-08-16 11:05:13.517000 +71,5574,5974,7167,4,2015-08-16 11:04:49.000000,2015-08-16 11:07:07.517000 +71,5575,5974,7167,5,2015-08-16 11:04:49.000000,2015-08-16 11:07:49.517000 +71,27061,5974,7167,6,2015-08-16 11:04:49.000000,2015-08-16 11:09:13.517000 +71,5585,5974,7167,7,2015-08-16 11:04:49.000000,2015-08-16 11:10:07.517000 +71,5589,5974,7167,8,2015-08-16 11:04:49.000000,2015-08-16 11:10:55.517000 +71,5587,5974,7167,9,2015-08-16 11:04:49.000000,2015-08-16 11:11:25.517000 +71,6515,5974,7167,10,2015-08-16 11:04:49.000000,2015-08-16 11:12:25.517000 +71,27014,5974,7167,11,2015-08-16 11:04:49.000000,2015-08-16 11:13:43.517000 +71,6512,5974,7167,12,2015-08-16 11:04:49.000000,2015-08-16 11:14:49.517000 +71,6482,5974,7167,13,2015-08-16 11:04:49.000000,2015-08-16 11:15:55.517000 +71,28903,5974,7167,14,2015-08-16 11:04:49.000000,2015-08-16 11:16:55.517000 +71,6508,5974,7167,15,2015-08-16 11:04:49.000000,2015-08-16 11:17:19.517000 +71,26871,5974,7167,16,2015-08-16 11:04:49.000000,2015-08-16 11:19:55.517000 +71,8064,5974,7167,17,2015-08-16 11:04:49.000000,2015-08-16 11:21:07.517000 +71,40841,5974,7167,18,2015-08-16 11:04:49.000000,2015-08-16 11:21:49.517000 +71,6183,5974,7167,19,2015-08-16 11:04:49.000000,2015-08-16 11:22:19.517000 +71,26771,5974,7167,20,2015-08-16 11:04:49.000000,2015-08-16 11:23:31.517000 +71,26721,5974,7167,21,2015-08-16 11:04:49.000000,2015-08-16 11:24:43.517000 +71,5455,5974,7167,22,2015-08-16 11:04:49.000000,2015-08-16 11:25:43.517000 +71,6190,5974,7167,23,2015-08-16 11:04:49.000000,2015-08-16 11:26:31.517000 +71,6195,5974,7167,24,2015-08-16 11:04:49.000000,2015-08-16 11:27:19.517000 +71,6194,5974,7167,25,2015-08-16 11:04:49.000000,2015-08-16 11:28:13.517000 +71,26557,5974,7167,26,2015-08-16 11:04:49.000000,2015-08-16 11:29:01.517000 +71,6192,5974,7167,27,2015-08-16 11:04:49.000000,2015-08-16 11:29:37.517000 +71,28801,5974,7167,28,2015-08-16 11:04:49.000000,2015-08-16 11:32:05.517000 +71,27196,5974,7167,3,2015-08-16 11:05:22.000000,2015-08-16 11:05:34.593000 +71,5574,5974,7167,4,2015-08-16 11:05:22.000000,2015-08-16 11:07:28.593000 +71,5575,5974,7167,5,2015-08-16 11:05:22.000000,2015-08-16 11:08:10.593000 +71,27061,5974,7167,6,2015-08-16 11:05:22.000000,2015-08-16 11:09:34.593000 +71,5585,5974,7167,7,2015-08-16 11:05:22.000000,2015-08-16 11:10:28.593000 +71,5589,5974,7167,8,2015-08-16 11:05:22.000000,2015-08-16 11:11:16.593000 +71,5587,5974,7167,9,2015-08-16 11:05:22.000000,2015-08-16 11:11:46.593000 +71,6515,5974,7167,10,2015-08-16 11:05:22.000000,2015-08-16 11:12:46.593000 +71,27014,5974,7167,11,2015-08-16 11:05:22.000000,2015-08-16 11:14:04.593000 +71,6512,5974,7167,12,2015-08-16 11:05:22.000000,2015-08-16 11:15:10.593000 +71,6482,5974,7167,13,2015-08-16 11:05:22.000000,2015-08-16 11:16:16.593000 +71,28903,5974,7167,14,2015-08-16 11:05:22.000000,2015-08-16 11:17:16.593000 +71,6508,5974,7167,15,2015-08-16 11:05:22.000000,2015-08-16 11:17:40.593000 +71,26871,5974,7167,16,2015-08-16 11:05:22.000000,2015-08-16 11:20:16.593000 +71,8064,5974,7167,17,2015-08-16 11:05:22.000000,2015-08-16 11:21:28.593000 +71,40841,5974,7167,18,2015-08-16 11:05:22.000000,2015-08-16 11:22:10.593000 +71,6183,5974,7167,19,2015-08-16 11:05:22.000000,2015-08-16 11:22:40.593000 +71,26771,5974,7167,20,2015-08-16 11:05:22.000000,2015-08-16 11:23:52.593000 +71,26721,5974,7167,21,2015-08-16 11:05:22.000000,2015-08-16 11:25:04.593000 +71,5455,5974,7167,22,2015-08-16 11:05:22.000000,2015-08-16 11:26:04.593000 +71,6190,5974,7167,23,2015-08-16 11:05:22.000000,2015-08-16 11:26:52.593000 +71,6195,5974,7167,24,2015-08-16 11:05:22.000000,2015-08-16 11:27:40.593000 +71,6194,5974,7167,25,2015-08-16 11:05:22.000000,2015-08-16 11:28:34.593000 +71,26557,5974,7167,26,2015-08-16 11:05:22.000000,2015-08-16 11:29:22.593000 +71,6192,5974,7167,27,2015-08-16 11:05:22.000000,2015-08-16 11:29:58.593000 +71,28801,5974,7167,28,2015-08-16 11:05:22.000000,2015-08-16 11:32:26.593000 +71,27196,5974,7167,3,2015-08-16 11:05:55.000000,2015-08-16 11:06:07.593000 +71,5574,5974,7167,4,2015-08-16 11:05:55.000000,2015-08-16 11:08:01.593000 +71,5575,5974,7167,5,2015-08-16 11:05:55.000000,2015-08-16 11:08:43.593000 +71,27061,5974,7167,6,2015-08-16 11:05:55.000000,2015-08-16 11:10:07.593000 +71,5585,5974,7167,7,2015-08-16 11:05:55.000000,2015-08-16 11:11:01.593000 +71,5589,5974,7167,8,2015-08-16 11:05:55.000000,2015-08-16 11:11:49.593000 +71,5587,5974,7167,9,2015-08-16 11:05:55.000000,2015-08-16 11:12:19.593000 +71,6515,5974,7167,10,2015-08-16 11:05:55.000000,2015-08-16 11:13:19.593000 +71,27014,5974,7167,11,2015-08-16 11:05:55.000000,2015-08-16 11:14:37.593000 +71,6512,5974,7167,12,2015-08-16 11:05:55.000000,2015-08-16 11:15:43.593000 +71,6482,5974,7167,13,2015-08-16 11:05:55.000000,2015-08-16 11:16:49.593000 +71,28903,5974,7167,14,2015-08-16 11:05:55.000000,2015-08-16 11:17:49.593000 +71,6508,5974,7167,15,2015-08-16 11:05:55.000000,2015-08-16 11:18:13.593000 +71,26871,5974,7167,16,2015-08-16 11:05:55.000000,2015-08-16 11:20:49.593000 +71,8064,5974,7167,17,2015-08-16 11:05:55.000000,2015-08-16 11:22:01.593000 +71,40841,5974,7167,18,2015-08-16 11:05:55.000000,2015-08-16 11:22:43.593000 +71,6183,5974,7167,19,2015-08-16 11:05:55.000000,2015-08-16 11:23:13.593000 +71,26771,5974,7167,20,2015-08-16 11:05:55.000000,2015-08-16 11:24:25.593000 +71,26721,5974,7167,21,2015-08-16 11:05:55.000000,2015-08-16 11:25:37.593000 +71,5455,5974,7167,22,2015-08-16 11:05:55.000000,2015-08-16 11:26:37.593000 +71,6190,5974,7167,23,2015-08-16 11:05:55.000000,2015-08-16 11:27:25.593000 +71,6195,5974,7167,24,2015-08-16 11:05:55.000000,2015-08-16 11:28:13.593000 +71,6194,5974,7167,25,2015-08-16 11:05:55.000000,2015-08-16 11:29:07.593000 +71,26557,5974,7167,26,2015-08-16 11:05:55.000000,2015-08-16 11:29:55.593000 +71,6192,5974,7167,27,2015-08-16 11:05:55.000000,2015-08-16 11:30:31.593000 +71,28801,5974,7167,28,2015-08-16 11:05:55.000000,2015-08-16 11:32:59.593000 +71,5574,5974,7167,4,2015-08-16 11:07:16.000000,2015-08-16 11:08:39.571000 +71,5575,5974,7167,5,2015-08-16 11:07:16.000000,2015-08-16 11:09:21.571000 +71,27061,5974,7167,6,2015-08-16 11:07:16.000000,2015-08-16 11:10:45.571000 +71,5585,5974,7167,7,2015-08-16 11:07:16.000000,2015-08-16 11:11:39.571000 +71,5589,5974,7167,8,2015-08-16 11:07:16.000000,2015-08-16 11:12:27.571000 +71,5587,5974,7167,9,2015-08-16 11:07:16.000000,2015-08-16 11:12:57.571000 +71,6515,5974,7167,10,2015-08-16 11:07:16.000000,2015-08-16 11:13:57.571000 +71,27014,5974,7167,11,2015-08-16 11:07:16.000000,2015-08-16 11:15:15.571000 +71,6512,5974,7167,12,2015-08-16 11:07:16.000000,2015-08-16 11:16:21.571000 +71,6482,5974,7167,13,2015-08-16 11:07:16.000000,2015-08-16 11:17:27.571000 +71,28903,5974,7167,14,2015-08-16 11:07:16.000000,2015-08-16 11:18:27.571000 +71,6508,5974,7167,15,2015-08-16 11:07:16.000000,2015-08-16 11:18:51.571000 +71,26871,5974,7167,16,2015-08-16 11:07:16.000000,2015-08-16 11:21:27.571000 +71,8064,5974,7167,17,2015-08-16 11:07:16.000000,2015-08-16 11:22:39.571000 +71,40841,5974,7167,18,2015-08-16 11:07:16.000000,2015-08-16 11:23:21.571000 +71,6183,5974,7167,19,2015-08-16 11:07:16.000000,2015-08-16 11:23:51.571000 +71,26771,5974,7167,20,2015-08-16 11:07:16.000000,2015-08-16 11:25:03.571000 +71,26721,5974,7167,21,2015-08-16 11:07:16.000000,2015-08-16 11:26:15.571000 +71,5455,5974,7167,22,2015-08-16 11:07:16.000000,2015-08-16 11:27:15.571000 +71,6190,5974,7167,23,2015-08-16 11:07:16.000000,2015-08-16 11:28:03.571000 +71,6195,5974,7167,24,2015-08-16 11:07:16.000000,2015-08-16 11:28:51.571000 +71,6194,5974,7167,25,2015-08-16 11:07:16.000000,2015-08-16 11:29:45.571000 +71,26557,5974,7167,26,2015-08-16 11:07:16.000000,2015-08-16 11:30:33.571000 +71,6192,5974,7167,27,2015-08-16 11:07:16.000000,2015-08-16 11:31:09.571000 +71,28801,5974,7167,28,2015-08-16 11:07:16.000000,2015-08-16 11:33:37.571000 +71,5574,5974,7167,4,2015-08-16 11:07:46.000000,2015-08-16 11:08:51.112000 +71,5575,5974,7167,5,2015-08-16 11:07:46.000000,2015-08-16 11:09:33.112000 +71,27061,5974,7167,6,2015-08-16 11:07:46.000000,2015-08-16 11:10:57.112000 +71,5585,5974,7167,7,2015-08-16 11:07:46.000000,2015-08-16 11:11:51.112000 +71,5589,5974,7167,8,2015-08-16 11:07:46.000000,2015-08-16 11:12:39.112000 +71,5587,5974,7167,9,2015-08-16 11:07:46.000000,2015-08-16 11:13:09.112000 +71,6515,5974,7167,10,2015-08-16 11:07:46.000000,2015-08-16 11:14:09.112000 +71,27014,5974,7167,11,2015-08-16 11:07:46.000000,2015-08-16 11:15:27.112000 +71,6512,5974,7167,12,2015-08-16 11:07:46.000000,2015-08-16 11:16:33.112000 +71,6482,5974,7167,13,2015-08-16 11:07:46.000000,2015-08-16 11:17:39.112000 +71,28903,5974,7167,14,2015-08-16 11:07:46.000000,2015-08-16 11:18:39.112000 +71,6508,5974,7167,15,2015-08-16 11:07:46.000000,2015-08-16 11:19:03.112000 +71,26871,5974,7167,16,2015-08-16 11:07:46.000000,2015-08-16 11:21:39.112000 +71,8064,5974,7167,17,2015-08-16 11:07:46.000000,2015-08-16 11:22:51.112000 +71,40841,5974,7167,18,2015-08-16 11:07:46.000000,2015-08-16 11:23:33.112000 +71,6183,5974,7167,19,2015-08-16 11:07:46.000000,2015-08-16 11:24:03.112000 +71,26771,5974,7167,20,2015-08-16 11:07:46.000000,2015-08-16 11:25:15.112000 +71,26721,5974,7167,21,2015-08-16 11:07:46.000000,2015-08-16 11:26:27.112000 +71,5455,5974,7167,22,2015-08-16 11:07:46.000000,2015-08-16 11:27:27.112000 +71,6190,5974,7167,23,2015-08-16 11:07:46.000000,2015-08-16 11:28:15.112000 +71,6195,5974,7167,24,2015-08-16 11:07:46.000000,2015-08-16 11:29:03.112000 +71,6194,5974,7167,25,2015-08-16 11:07:46.000000,2015-08-16 11:29:57.112000 +71,26557,5974,7167,26,2015-08-16 11:07:46.000000,2015-08-16 11:30:45.112000 +71,6192,5974,7167,27,2015-08-16 11:07:46.000000,2015-08-16 11:31:21.112000 +71,28801,5974,7167,28,2015-08-16 11:07:46.000000,2015-08-16 11:33:49.112000 +71,5575,5974,7167,5,2015-08-16 11:08:53.000000,2015-08-16 11:09:18.458000 +71,27061,5974,7167,6,2015-08-16 11:08:53.000000,2015-08-16 11:10:42.458000 +71,5585,5974,7167,7,2015-08-16 11:08:53.000000,2015-08-16 11:11:36.458000 +71,5589,5974,7167,8,2015-08-16 11:08:53.000000,2015-08-16 11:12:24.458000 +71,5587,5974,7167,9,2015-08-16 11:08:53.000000,2015-08-16 11:12:54.458000 +71,6515,5974,7167,10,2015-08-16 11:08:53.000000,2015-08-16 11:13:54.458000 +71,27014,5974,7167,11,2015-08-16 11:08:53.000000,2015-08-16 11:15:12.458000 +71,6512,5974,7167,12,2015-08-16 11:08:53.000000,2015-08-16 11:16:18.458000 +71,6482,5974,7167,13,2015-08-16 11:08:53.000000,2015-08-16 11:17:24.458000 +71,28903,5974,7167,14,2015-08-16 11:08:53.000000,2015-08-16 11:18:24.458000 +71,6508,5974,7167,15,2015-08-16 11:08:53.000000,2015-08-16 11:18:48.458000 +71,26871,5974,7167,16,2015-08-16 11:08:53.000000,2015-08-16 11:21:24.458000 +71,8064,5974,7167,17,2015-08-16 11:08:53.000000,2015-08-16 11:22:36.458000 +71,40841,5974,7167,18,2015-08-16 11:08:53.000000,2015-08-16 11:23:18.458000 +71,6183,5974,7167,19,2015-08-16 11:08:53.000000,2015-08-16 11:23:48.458000 +71,26771,5974,7167,20,2015-08-16 11:08:53.000000,2015-08-16 11:25:00.458000 +71,26721,5974,7167,21,2015-08-16 11:08:53.000000,2015-08-16 11:26:12.458000 +71,5455,5974,7167,22,2015-08-16 11:08:53.000000,2015-08-16 11:27:12.458000 +71,6190,5974,7167,23,2015-08-16 11:08:53.000000,2015-08-16 11:28:00.458000 +71,6195,5974,7167,24,2015-08-16 11:08:53.000000,2015-08-16 11:28:48.458000 +71,6194,5974,7167,25,2015-08-16 11:08:53.000000,2015-08-16 11:29:42.458000 +71,26557,5974,7167,26,2015-08-16 11:08:53.000000,2015-08-16 11:30:30.458000 +71,6192,5974,7167,27,2015-08-16 11:08:53.000000,2015-08-16 11:31:06.458000 +71,28801,5974,7167,28,2015-08-16 11:08:53.000000,2015-08-16 11:33:34.458000 +71,27061,5974,7167,6,2015-08-16 11:09:26.000000,2015-08-16 11:10:09.809000 +71,5585,5974,7167,7,2015-08-16 11:09:26.000000,2015-08-16 11:11:03.809000 +71,5589,5974,7167,8,2015-08-16 11:09:26.000000,2015-08-16 11:11:51.809000 +71,5587,5974,7167,9,2015-08-16 11:09:26.000000,2015-08-16 11:12:21.809000 +71,6515,5974,7167,10,2015-08-16 11:09:26.000000,2015-08-16 11:13:21.809000 +71,27014,5974,7167,11,2015-08-16 11:09:26.000000,2015-08-16 11:14:39.809000 +71,6512,5974,7167,12,2015-08-16 11:09:26.000000,2015-08-16 11:15:45.809000 +71,6482,5974,7167,13,2015-08-16 11:09:26.000000,2015-08-16 11:16:51.809000 +71,28903,5974,7167,14,2015-08-16 11:09:26.000000,2015-08-16 11:17:51.809000 +71,6508,5974,7167,15,2015-08-16 11:09:26.000000,2015-08-16 11:18:15.809000 +71,26871,5974,7167,16,2015-08-16 11:09:26.000000,2015-08-16 11:20:51.809000 +71,8064,5974,7167,17,2015-08-16 11:09:26.000000,2015-08-16 11:22:03.809000 +71,40841,5974,7167,18,2015-08-16 11:09:26.000000,2015-08-16 11:22:45.809000 +71,6183,5974,7167,19,2015-08-16 11:09:26.000000,2015-08-16 11:23:15.809000 +71,26771,5974,7167,20,2015-08-16 11:09:26.000000,2015-08-16 11:24:27.809000 +71,26721,5974,7167,21,2015-08-16 11:09:26.000000,2015-08-16 11:25:39.809000 +71,5455,5974,7167,22,2015-08-16 11:09:26.000000,2015-08-16 11:26:39.809000 +71,6190,5974,7167,23,2015-08-16 11:09:26.000000,2015-08-16 11:27:27.809000 +71,6195,5974,7167,24,2015-08-16 11:09:26.000000,2015-08-16 11:28:15.809000 +71,6194,5974,7167,25,2015-08-16 11:09:26.000000,2015-08-16 11:29:09.809000 +71,26557,5974,7167,26,2015-08-16 11:09:26.000000,2015-08-16 11:29:57.809000 +71,6192,5974,7167,27,2015-08-16 11:09:26.000000,2015-08-16 11:30:33.809000 +71,28801,5974,7167,28,2015-08-16 11:09:26.000000,2015-08-16 11:33:01.809000 +71,5589,5974,7167,8,2015-08-16 11:10:31.000000,2015-08-16 11:11:02.967000 +71,5587,5974,7167,9,2015-08-16 11:10:31.000000,2015-08-16 11:11:32.967000 +71,6515,5974,7167,10,2015-08-16 11:10:31.000000,2015-08-16 11:12:32.967000 +71,27014,5974,7167,11,2015-08-16 11:10:31.000000,2015-08-16 11:13:50.967000 +71,6512,5974,7167,12,2015-08-16 11:10:31.000000,2015-08-16 11:14:56.967000 +71,6482,5974,7167,13,2015-08-16 11:10:31.000000,2015-08-16 11:16:02.967000 +71,28903,5974,7167,14,2015-08-16 11:10:31.000000,2015-08-16 11:17:02.967000 +71,6508,5974,7167,15,2015-08-16 11:10:31.000000,2015-08-16 11:17:26.967000 +71,26871,5974,7167,16,2015-08-16 11:10:31.000000,2015-08-16 11:20:02.967000 +71,8064,5974,7167,17,2015-08-16 11:10:31.000000,2015-08-16 11:21:14.967000 +71,40841,5974,7167,18,2015-08-16 11:10:31.000000,2015-08-16 11:21:56.967000 +71,6183,5974,7167,19,2015-08-16 11:10:31.000000,2015-08-16 11:22:26.967000 +71,26771,5974,7167,20,2015-08-16 11:10:31.000000,2015-08-16 11:23:38.967000 +71,26721,5974,7167,21,2015-08-16 11:10:31.000000,2015-08-16 11:24:50.967000 +71,5455,5974,7167,22,2015-08-16 11:10:31.000000,2015-08-16 11:25:50.967000 +71,6190,5974,7167,23,2015-08-16 11:10:31.000000,2015-08-16 11:26:38.967000 +71,6195,5974,7167,24,2015-08-16 11:10:31.000000,2015-08-16 11:27:26.967000 +71,6194,5974,7167,25,2015-08-16 11:10:31.000000,2015-08-16 11:28:20.967000 +71,26557,5974,7167,26,2015-08-16 11:10:31.000000,2015-08-16 11:29:08.967000 +71,6192,5974,7167,27,2015-08-16 11:10:31.000000,2015-08-16 11:29:44.967000 +71,28801,5974,7167,28,2015-08-16 11:10:31.000000,2015-08-16 11:32:12.967000 +71,5589,5974,7167,8,2015-08-16 11:11:05.000000,2015-08-16 11:11:13.618000 +71,5587,5974,7167,9,2015-08-16 11:11:05.000000,2015-08-16 11:11:43.618000 +71,6515,5974,7167,10,2015-08-16 11:11:05.000000,2015-08-16 11:12:43.618000 +71,27014,5974,7167,11,2015-08-16 11:11:05.000000,2015-08-16 11:14:01.618000 +71,6512,5974,7167,12,2015-08-16 11:11:05.000000,2015-08-16 11:15:07.618000 +71,6482,5974,7167,13,2015-08-16 11:11:05.000000,2015-08-16 11:16:13.618000 +71,28903,5974,7167,14,2015-08-16 11:11:05.000000,2015-08-16 11:17:13.618000 +71,6508,5974,7167,15,2015-08-16 11:11:05.000000,2015-08-16 11:17:37.618000 +71,26871,5974,7167,16,2015-08-16 11:11:05.000000,2015-08-16 11:20:13.618000 +71,8064,5974,7167,17,2015-08-16 11:11:05.000000,2015-08-16 11:21:25.618000 +71,40841,5974,7167,18,2015-08-16 11:11:05.000000,2015-08-16 11:22:07.618000 +71,6183,5974,7167,19,2015-08-16 11:11:05.000000,2015-08-16 11:22:37.618000 +71,26771,5974,7167,20,2015-08-16 11:11:05.000000,2015-08-16 11:23:49.618000 +71,26721,5974,7167,21,2015-08-16 11:11:05.000000,2015-08-16 11:25:01.618000 +71,5455,5974,7167,22,2015-08-16 11:11:05.000000,2015-08-16 11:26:01.618000 +71,6190,5974,7167,23,2015-08-16 11:11:05.000000,2015-08-16 11:26:49.618000 +71,6195,5974,7167,24,2015-08-16 11:11:05.000000,2015-08-16 11:27:37.618000 +71,6194,5974,7167,25,2015-08-16 11:11:05.000000,2015-08-16 11:28:31.618000 +71,26557,5974,7167,26,2015-08-16 11:11:05.000000,2015-08-16 11:29:19.618000 +71,6192,5974,7167,27,2015-08-16 11:11:05.000000,2015-08-16 11:29:55.618000 +71,28801,5974,7167,28,2015-08-16 11:11:05.000000,2015-08-16 11:32:23.618000 +71,6515,5974,7167,10,2015-08-16 11:11:55.000000,2015-08-16 11:12:40.919000 +71,27014,5974,7167,11,2015-08-16 11:11:55.000000,2015-08-16 11:13:58.919000 +71,6512,5974,7167,12,2015-08-16 11:11:55.000000,2015-08-16 11:15:04.919000 +71,6482,5974,7167,13,2015-08-16 11:11:55.000000,2015-08-16 11:16:10.919000 +71,28903,5974,7167,14,2015-08-16 11:11:55.000000,2015-08-16 11:17:10.919000 +71,6508,5974,7167,15,2015-08-16 11:11:55.000000,2015-08-16 11:17:34.919000 +71,26871,5974,7167,16,2015-08-16 11:11:55.000000,2015-08-16 11:20:10.919000 +71,8064,5974,7167,17,2015-08-16 11:11:55.000000,2015-08-16 11:21:22.919000 +71,40841,5974,7167,18,2015-08-16 11:11:55.000000,2015-08-16 11:22:04.919000 +71,6183,5974,7167,19,2015-08-16 11:11:55.000000,2015-08-16 11:22:34.919000 +71,26771,5974,7167,20,2015-08-16 11:11:55.000000,2015-08-16 11:23:46.919000 +71,26721,5974,7167,21,2015-08-16 11:11:55.000000,2015-08-16 11:24:58.919000 +71,5455,5974,7167,22,2015-08-16 11:11:55.000000,2015-08-16 11:25:58.919000 +71,6190,5974,7167,23,2015-08-16 11:11:55.000000,2015-08-16 11:26:46.919000 +71,6195,5974,7167,24,2015-08-16 11:11:55.000000,2015-08-16 11:27:34.919000 +71,6194,5974,7167,25,2015-08-16 11:11:55.000000,2015-08-16 11:28:28.919000 +71,26557,5974,7167,26,2015-08-16 11:11:55.000000,2015-08-16 11:29:16.919000 +71,6192,5974,7167,27,2015-08-16 11:11:55.000000,2015-08-16 11:29:52.919000 +71,28801,5974,7167,28,2015-08-16 11:11:55.000000,2015-08-16 11:32:20.919000 +71,6515,5974,7167,10,2015-08-16 11:12:28.000000,2015-08-16 11:12:34.181000 +71,27014,5974,7167,11,2015-08-16 11:12:28.000000,2015-08-16 11:13:52.181000 +71,6512,5974,7167,12,2015-08-16 11:12:28.000000,2015-08-16 11:14:58.181000 +71,6482,5974,7167,13,2015-08-16 11:12:28.000000,2015-08-16 11:16:04.181000 +71,28903,5974,7167,14,2015-08-16 11:12:28.000000,2015-08-16 11:17:04.181000 +71,6508,5974,7167,15,2015-08-16 11:12:28.000000,2015-08-16 11:17:28.181000 +71,26871,5974,7167,16,2015-08-16 11:12:28.000000,2015-08-16 11:20:04.181000 +71,8064,5974,7167,17,2015-08-16 11:12:28.000000,2015-08-16 11:21:16.181000 +71,40841,5974,7167,18,2015-08-16 11:12:28.000000,2015-08-16 11:21:58.181000 +71,6183,5974,7167,19,2015-08-16 11:12:28.000000,2015-08-16 11:22:28.181000 +71,26771,5974,7167,20,2015-08-16 11:12:28.000000,2015-08-16 11:23:40.181000 +71,26721,5974,7167,21,2015-08-16 11:12:28.000000,2015-08-16 11:24:52.181000 +71,5455,5974,7167,22,2015-08-16 11:12:28.000000,2015-08-16 11:25:52.181000 +71,6190,5974,7167,23,2015-08-16 11:12:28.000000,2015-08-16 11:26:40.181000 +71,6195,5974,7167,24,2015-08-16 11:12:28.000000,2015-08-16 11:27:28.181000 +71,6194,5974,7167,25,2015-08-16 11:12:28.000000,2015-08-16 11:28:22.181000 +71,26557,5974,7167,26,2015-08-16 11:12:28.000000,2015-08-16 11:29:10.181000 +71,6192,5974,7167,27,2015-08-16 11:12:28.000000,2015-08-16 11:29:46.181000 +71,28801,5974,7167,28,2015-08-16 11:12:28.000000,2015-08-16 11:32:14.181000 +71,27014,5974,7167,11,2015-08-16 11:13:26.000000,2015-08-16 11:14:31.336000 +71,6512,5974,7167,12,2015-08-16 11:13:26.000000,2015-08-16 11:15:37.336000 +71,6482,5974,7167,13,2015-08-16 11:13:26.000000,2015-08-16 11:16:43.336000 +71,28903,5974,7167,14,2015-08-16 11:13:26.000000,2015-08-16 11:17:43.336000 +71,6508,5974,7167,15,2015-08-16 11:13:26.000000,2015-08-16 11:18:07.336000 +71,26871,5974,7167,16,2015-08-16 11:13:26.000000,2015-08-16 11:20:43.336000 +71,8064,5974,7167,17,2015-08-16 11:13:26.000000,2015-08-16 11:21:55.336000 +71,40841,5974,7167,18,2015-08-16 11:13:26.000000,2015-08-16 11:22:37.336000 +71,6183,5974,7167,19,2015-08-16 11:13:26.000000,2015-08-16 11:23:07.336000 +71,26771,5974,7167,20,2015-08-16 11:13:26.000000,2015-08-16 11:24:19.336000 +71,26721,5974,7167,21,2015-08-16 11:13:26.000000,2015-08-16 11:25:31.336000 +71,5455,5974,7167,22,2015-08-16 11:13:26.000000,2015-08-16 11:26:31.336000 +71,6190,5974,7167,23,2015-08-16 11:13:26.000000,2015-08-16 11:27:19.336000 +71,6195,5974,7167,24,2015-08-16 11:13:26.000000,2015-08-16 11:28:07.336000 +71,6194,5974,7167,25,2015-08-16 11:13:26.000000,2015-08-16 11:29:01.336000 +71,26557,5974,7167,26,2015-08-16 11:13:26.000000,2015-08-16 11:29:49.336000 +71,6192,5974,7167,27,2015-08-16 11:13:26.000000,2015-08-16 11:30:25.336000 +71,28801,5974,7167,28,2015-08-16 11:13:26.000000,2015-08-16 11:32:53.336000 +71,27014,5974,7167,11,2015-08-16 11:14:02.000000,2015-08-16 11:15:07.099000 +71,6512,5974,7167,12,2015-08-16 11:14:02.000000,2015-08-16 11:16:13.099000 +71,6482,5974,7167,13,2015-08-16 11:14:02.000000,2015-08-16 11:17:19.099000 +71,28903,5974,7167,14,2015-08-16 11:14:02.000000,2015-08-16 11:18:19.099000 +71,6508,5974,7167,15,2015-08-16 11:14:02.000000,2015-08-16 11:18:43.099000 +71,26871,5974,7167,16,2015-08-16 11:14:02.000000,2015-08-16 11:21:19.099000 +71,8064,5974,7167,17,2015-08-16 11:14:02.000000,2015-08-16 11:22:31.099000 +71,40841,5974,7167,18,2015-08-16 11:14:02.000000,2015-08-16 11:23:13.099000 +71,6183,5974,7167,19,2015-08-16 11:14:02.000000,2015-08-16 11:23:43.099000 +71,26771,5974,7167,20,2015-08-16 11:14:02.000000,2015-08-16 11:24:55.099000 +71,26721,5974,7167,21,2015-08-16 11:14:02.000000,2015-08-16 11:26:07.099000 +71,5455,5974,7167,22,2015-08-16 11:14:02.000000,2015-08-16 11:27:07.099000 +71,6190,5974,7167,23,2015-08-16 11:14:02.000000,2015-08-16 11:27:55.099000 +71,6195,5974,7167,24,2015-08-16 11:14:02.000000,2015-08-16 11:28:43.099000 +71,6194,5974,7167,25,2015-08-16 11:14:02.000000,2015-08-16 11:29:37.099000 +71,26557,5974,7167,26,2015-08-16 11:14:02.000000,2015-08-16 11:30:25.099000 +71,6192,5974,7167,27,2015-08-16 11:14:02.000000,2015-08-16 11:31:01.099000 +71,28801,5974,7167,28,2015-08-16 11:14:02.000000,2015-08-16 11:33:29.099000 +71,27014,5974,7167,11,2015-08-16 11:14:32.000000,2015-08-16 11:15:03.978000 +71,6512,5974,7167,12,2015-08-16 11:14:32.000000,2015-08-16 11:16:09.978000 +71,6482,5974,7167,13,2015-08-16 11:14:32.000000,2015-08-16 11:17:15.978000 +71,28903,5974,7167,14,2015-08-16 11:14:32.000000,2015-08-16 11:18:15.978000 +71,6508,5974,7167,15,2015-08-16 11:14:32.000000,2015-08-16 11:18:39.978000 +71,26871,5974,7167,16,2015-08-16 11:14:32.000000,2015-08-16 11:21:15.978000 +71,8064,5974,7167,17,2015-08-16 11:14:32.000000,2015-08-16 11:22:27.978000 +71,40841,5974,7167,18,2015-08-16 11:14:32.000000,2015-08-16 11:23:09.978000 +71,6183,5974,7167,19,2015-08-16 11:14:32.000000,2015-08-16 11:23:39.978000 +71,26771,5974,7167,20,2015-08-16 11:14:32.000000,2015-08-16 11:24:51.978000 +71,26721,5974,7167,21,2015-08-16 11:14:32.000000,2015-08-16 11:26:03.978000 +71,5455,5974,7167,22,2015-08-16 11:14:32.000000,2015-08-16 11:27:03.978000 +71,6190,5974,7167,23,2015-08-16 11:14:32.000000,2015-08-16 11:27:51.978000 +71,6195,5974,7167,24,2015-08-16 11:14:32.000000,2015-08-16 11:28:39.978000 +71,6194,5974,7167,25,2015-08-16 11:14:32.000000,2015-08-16 11:29:33.978000 +71,26557,5974,7167,26,2015-08-16 11:14:32.000000,2015-08-16 11:30:21.978000 +71,6192,5974,7167,27,2015-08-16 11:14:32.000000,2015-08-16 11:30:57.978000 +71,28801,5974,7167,28,2015-08-16 11:14:32.000000,2015-08-16 11:33:25.978000 +71,6512,5974,7167,12,2015-08-16 11:15:04.000000,2015-08-16 11:15:58.245000 +71,6482,5974,7167,13,2015-08-16 11:15:04.000000,2015-08-16 11:17:04.245000 +71,28903,5974,7167,14,2015-08-16 11:15:04.000000,2015-08-16 11:18:04.245000 +71,6508,5974,7167,15,2015-08-16 11:15:04.000000,2015-08-16 11:18:28.245000 +71,26871,5974,7167,16,2015-08-16 11:15:04.000000,2015-08-16 11:21:04.245000 +71,8064,5974,7167,17,2015-08-16 11:15:04.000000,2015-08-16 11:22:16.245000 +71,40841,5974,7167,18,2015-08-16 11:15:04.000000,2015-08-16 11:22:58.245000 +71,6183,5974,7167,19,2015-08-16 11:15:04.000000,2015-08-16 11:23:28.245000 +71,26771,5974,7167,20,2015-08-16 11:15:04.000000,2015-08-16 11:24:40.245000 +71,26721,5974,7167,21,2015-08-16 11:15:04.000000,2015-08-16 11:25:52.245000 +71,5455,5974,7167,22,2015-08-16 11:15:04.000000,2015-08-16 11:26:52.245000 +71,6190,5974,7167,23,2015-08-16 11:15:04.000000,2015-08-16 11:27:40.245000 +71,6195,5974,7167,24,2015-08-16 11:15:04.000000,2015-08-16 11:28:28.245000 +71,6194,5974,7167,25,2015-08-16 11:15:04.000000,2015-08-16 11:29:22.245000 +71,26557,5974,7167,26,2015-08-16 11:15:04.000000,2015-08-16 11:30:10.245000 +71,6192,5974,7167,27,2015-08-16 11:15:04.000000,2015-08-16 11:30:46.245000 +71,28801,5974,7167,28,2015-08-16 11:15:04.000000,2015-08-16 11:33:14.245000 +71,6512,5974,7167,12,2015-08-16 11:16:11.000000,2015-08-16 11:16:39.300000 +71,6482,5974,7167,13,2015-08-16 11:16:11.000000,2015-08-16 11:17:45.300000 +71,28903,5974,7167,14,2015-08-16 11:16:11.000000,2015-08-16 11:18:45.300000 +71,6508,5974,7167,15,2015-08-16 11:16:11.000000,2015-08-16 11:19:09.300000 +71,26871,5974,7167,16,2015-08-16 11:16:11.000000,2015-08-16 11:21:45.300000 +71,8064,5974,7167,17,2015-08-16 11:16:11.000000,2015-08-16 11:22:57.300000 +71,40841,5974,7167,18,2015-08-16 11:16:11.000000,2015-08-16 11:23:39.300000 +71,6183,5974,7167,19,2015-08-16 11:16:11.000000,2015-08-16 11:24:09.300000 +71,26771,5974,7167,20,2015-08-16 11:16:11.000000,2015-08-16 11:25:21.300000 +71,26721,5974,7167,21,2015-08-16 11:16:11.000000,2015-08-16 11:26:33.300000 +71,5455,5974,7167,22,2015-08-16 11:16:11.000000,2015-08-16 11:27:33.300000 +71,6190,5974,7167,23,2015-08-16 11:16:11.000000,2015-08-16 11:28:21.300000 +71,6195,5974,7167,24,2015-08-16 11:16:11.000000,2015-08-16 11:29:09.300000 +71,6194,5974,7167,25,2015-08-16 11:16:11.000000,2015-08-16 11:30:03.300000 +71,26557,5974,7167,26,2015-08-16 11:16:11.000000,2015-08-16 11:30:51.300000 +71,6192,5974,7167,27,2015-08-16 11:16:11.000000,2015-08-16 11:31:27.300000 +71,28801,5974,7167,28,2015-08-16 11:16:11.000000,2015-08-16 11:33:55.300000 +71,6512,5974,7167,12,2015-08-16 11:16:41.000000,2015-08-16 11:16:51.242000 +71,6482,5974,7167,13,2015-08-16 11:16:41.000000,2015-08-16 11:17:57.242000 +71,28903,5974,7167,14,2015-08-16 11:16:41.000000,2015-08-16 11:18:57.242000 +71,6508,5974,7167,15,2015-08-16 11:16:41.000000,2015-08-16 11:19:21.242000 +71,26871,5974,7167,16,2015-08-16 11:16:41.000000,2015-08-16 11:21:57.242000 +71,8064,5974,7167,17,2015-08-16 11:16:41.000000,2015-08-16 11:23:09.242000 +71,40841,5974,7167,18,2015-08-16 11:16:41.000000,2015-08-16 11:23:51.242000 +71,6183,5974,7167,19,2015-08-16 11:16:41.000000,2015-08-16 11:24:21.242000 +71,26771,5974,7167,20,2015-08-16 11:16:41.000000,2015-08-16 11:25:33.242000 +71,26721,5974,7167,21,2015-08-16 11:16:41.000000,2015-08-16 11:26:45.242000 +71,5455,5974,7167,22,2015-08-16 11:16:41.000000,2015-08-16 11:27:45.242000 +71,6190,5974,7167,23,2015-08-16 11:16:41.000000,2015-08-16 11:28:33.242000 +71,6195,5974,7167,24,2015-08-16 11:16:41.000000,2015-08-16 11:29:21.242000 +71,6194,5974,7167,25,2015-08-16 11:16:41.000000,2015-08-16 11:30:15.242000 +71,26557,5974,7167,26,2015-08-16 11:16:41.000000,2015-08-16 11:31:03.242000 +71,6192,5974,7167,27,2015-08-16 11:16:41.000000,2015-08-16 11:31:39.242000 +71,28801,5974,7167,28,2015-08-16 11:16:41.000000,2015-08-16 11:34:07.242000 +71,28903,5974,7167,14,2015-08-16 11:17:38.000000,2015-08-16 11:18:23.898000 +71,6508,5974,7167,15,2015-08-16 11:17:38.000000,2015-08-16 11:18:47.898000 +71,26871,5974,7167,16,2015-08-16 11:17:38.000000,2015-08-16 11:21:23.898000 +71,8064,5974,7167,17,2015-08-16 11:17:38.000000,2015-08-16 11:22:35.898000 +71,40841,5974,7167,18,2015-08-16 11:17:38.000000,2015-08-16 11:23:17.898000 +71,6183,5974,7167,19,2015-08-16 11:17:38.000000,2015-08-16 11:23:47.898000 +71,26771,5974,7167,20,2015-08-16 11:17:38.000000,2015-08-16 11:24:59.898000 +71,26721,5974,7167,21,2015-08-16 11:17:38.000000,2015-08-16 11:26:11.898000 +71,5455,5974,7167,22,2015-08-16 11:17:38.000000,2015-08-16 11:27:11.898000 +71,6190,5974,7167,23,2015-08-16 11:17:38.000000,2015-08-16 11:27:59.898000 +71,6195,5974,7167,24,2015-08-16 11:17:38.000000,2015-08-16 11:28:47.898000 +71,6194,5974,7167,25,2015-08-16 11:17:38.000000,2015-08-16 11:29:41.898000 +71,26557,5974,7167,26,2015-08-16 11:17:38.000000,2015-08-16 11:30:29.898000 +71,6192,5974,7167,27,2015-08-16 11:17:38.000000,2015-08-16 11:31:05.898000 +71,28801,5974,7167,28,2015-08-16 11:17:38.000000,2015-08-16 11:33:33.898000 +71,6508,5974,7167,15,2015-08-16 11:18:53.000000,2015-08-16 11:19:03.199000 +71,26871,5974,7167,16,2015-08-16 11:18:53.000000,2015-08-16 11:21:39.199000 +71,8064,5974,7167,17,2015-08-16 11:18:53.000000,2015-08-16 11:22:51.199000 +71,40841,5974,7167,18,2015-08-16 11:18:53.000000,2015-08-16 11:23:33.199000 +71,6183,5974,7167,19,2015-08-16 11:18:53.000000,2015-08-16 11:24:03.199000 +71,26771,5974,7167,20,2015-08-16 11:18:53.000000,2015-08-16 11:25:15.199000 +71,26721,5974,7167,21,2015-08-16 11:18:53.000000,2015-08-16 11:26:27.199000 +71,5455,5974,7167,22,2015-08-16 11:18:53.000000,2015-08-16 11:27:27.199000 +71,6190,5974,7167,23,2015-08-16 11:18:53.000000,2015-08-16 11:28:15.199000 +71,6195,5974,7167,24,2015-08-16 11:18:53.000000,2015-08-16 11:29:03.199000 +71,6194,5974,7167,25,2015-08-16 11:18:53.000000,2015-08-16 11:29:57.199000 +71,26557,5974,7167,26,2015-08-16 11:18:53.000000,2015-08-16 11:30:45.199000 +71,6192,5974,7167,27,2015-08-16 11:18:53.000000,2015-08-16 11:31:21.199000 +71,28801,5974,7167,28,2015-08-16 11:18:53.000000,2015-08-16 11:33:49.199000 +71,26871,5974,7167,16,2015-08-16 11:19:29.000000,2015-08-16 11:21:49.786000 +71,8064,5974,7167,17,2015-08-16 11:19:29.000000,2015-08-16 11:23:01.786000 +71,40841,5974,7167,18,2015-08-16 11:19:29.000000,2015-08-16 11:23:43.786000 +71,6183,5974,7167,19,2015-08-16 11:19:29.000000,2015-08-16 11:24:13.786000 +71,26771,5974,7167,20,2015-08-16 11:19:29.000000,2015-08-16 11:25:25.786000 +71,26721,5974,7167,21,2015-08-16 11:19:29.000000,2015-08-16 11:26:37.786000 +71,5455,5974,7167,22,2015-08-16 11:19:29.000000,2015-08-16 11:27:37.786000 +71,6190,5974,7167,23,2015-08-16 11:19:29.000000,2015-08-16 11:28:25.786000 +71,6195,5974,7167,24,2015-08-16 11:19:29.000000,2015-08-16 11:29:13.786000 +71,6194,5974,7167,25,2015-08-16 11:19:29.000000,2015-08-16 11:30:07.786000 +71,26557,5974,7167,26,2015-08-16 11:19:29.000000,2015-08-16 11:30:55.786000 +71,6192,5974,7167,27,2015-08-16 11:19:29.000000,2015-08-16 11:31:31.786000 +71,28801,5974,7167,28,2015-08-16 11:19:29.000000,2015-08-16 11:33:59.786000 +71,26871,5974,7167,16,2015-08-16 11:20:01.000000,2015-08-16 11:21:37.874000 +71,8064,5974,7167,17,2015-08-16 11:20:01.000000,2015-08-16 11:22:49.874000 +71,40841,5974,7167,18,2015-08-16 11:20:01.000000,2015-08-16 11:23:31.874000 +71,6183,5974,7167,19,2015-08-16 11:20:01.000000,2015-08-16 11:24:01.874000 +71,26771,5974,7167,20,2015-08-16 11:20:01.000000,2015-08-16 11:25:13.874000 +71,26721,5974,7167,21,2015-08-16 11:20:01.000000,2015-08-16 11:26:25.874000 +71,5455,5974,7167,22,2015-08-16 11:20:01.000000,2015-08-16 11:27:25.874000 +71,6190,5974,7167,23,2015-08-16 11:20:01.000000,2015-08-16 11:28:13.874000 +71,6195,5974,7167,24,2015-08-16 11:20:01.000000,2015-08-16 11:29:01.874000 +71,6194,5974,7167,25,2015-08-16 11:20:01.000000,2015-08-16 11:29:55.874000 +71,26557,5974,7167,26,2015-08-16 11:20:01.000000,2015-08-16 11:30:43.874000 +71,6192,5974,7167,27,2015-08-16 11:20:01.000000,2015-08-16 11:31:19.874000 +71,28801,5974,7167,28,2015-08-16 11:20:01.000000,2015-08-16 11:33:47.874000 +71,26871,5974,7167,16,2015-08-16 11:21:03.000000,2015-08-16 11:21:12.494000 +71,8064,5974,7167,17,2015-08-16 11:21:03.000000,2015-08-16 11:22:24.494000 +71,40841,5974,7167,18,2015-08-16 11:21:03.000000,2015-08-16 11:23:06.494000 +71,6183,5974,7167,19,2015-08-16 11:21:03.000000,2015-08-16 11:23:36.494000 +71,26771,5974,7167,20,2015-08-16 11:21:03.000000,2015-08-16 11:24:48.494000 +71,26721,5974,7167,21,2015-08-16 11:21:03.000000,2015-08-16 11:26:00.494000 +71,5455,5974,7167,22,2015-08-16 11:21:03.000000,2015-08-16 11:27:00.494000 +71,6190,5974,7167,23,2015-08-16 11:21:03.000000,2015-08-16 11:27:48.494000 +71,6195,5974,7167,24,2015-08-16 11:21:03.000000,2015-08-16 11:28:36.494000 +71,6194,5974,7167,25,2015-08-16 11:21:03.000000,2015-08-16 11:29:30.494000 +71,26557,5974,7167,26,2015-08-16 11:21:03.000000,2015-08-16 11:30:18.494000 +71,6192,5974,7167,27,2015-08-16 11:21:03.000000,2015-08-16 11:30:54.494000 +71,28801,5974,7167,28,2015-08-16 11:21:03.000000,2015-08-16 11:33:22.494000 +71,26871,5974,7167,16,2015-08-16 11:21:34.000000,2015-08-16 11:21:38.445000 +71,8064,5974,7167,17,2015-08-16 11:21:34.000000,2015-08-16 11:22:50.445000 +71,40841,5974,7167,18,2015-08-16 11:21:34.000000,2015-08-16 11:23:32.445000 +71,6183,5974,7167,19,2015-08-16 11:21:34.000000,2015-08-16 11:24:02.445000 +71,26771,5974,7167,20,2015-08-16 11:21:34.000000,2015-08-16 11:25:14.445000 +71,26721,5974,7167,21,2015-08-16 11:21:34.000000,2015-08-16 11:26:26.445000 +71,5455,5974,7167,22,2015-08-16 11:21:34.000000,2015-08-16 11:27:26.445000 +71,6190,5974,7167,23,2015-08-16 11:21:34.000000,2015-08-16 11:28:14.445000 +71,6195,5974,7167,24,2015-08-16 11:21:34.000000,2015-08-16 11:29:02.445000 +71,6194,5974,7167,25,2015-08-16 11:21:34.000000,2015-08-16 11:29:56.445000 +71,26557,5974,7167,26,2015-08-16 11:21:34.000000,2015-08-16 11:30:44.445000 +71,6192,5974,7167,27,2015-08-16 11:21:34.000000,2015-08-16 11:31:20.445000 +71,28801,5974,7167,28,2015-08-16 11:21:34.000000,2015-08-16 11:33:48.445000 +71,8064,5974,7167,17,2015-08-16 11:22:37.000000,2015-08-16 11:23:37.938000 +71,40841,5974,7167,18,2015-08-16 11:22:37.000000,2015-08-16 11:24:19.938000 +71,6183,5974,7167,19,2015-08-16 11:22:37.000000,2015-08-16 11:24:49.938000 +71,26771,5974,7167,20,2015-08-16 11:22:37.000000,2015-08-16 11:26:01.938000 +71,26721,5974,7167,21,2015-08-16 11:22:37.000000,2015-08-16 11:27:13.938000 +71,5455,5974,7167,22,2015-08-16 11:22:37.000000,2015-08-16 11:28:13.938000 +71,6190,5974,7167,23,2015-08-16 11:22:37.000000,2015-08-16 11:29:01.938000 +71,6195,5974,7167,24,2015-08-16 11:22:37.000000,2015-08-16 11:29:49.938000 +71,6194,5974,7167,25,2015-08-16 11:22:37.000000,2015-08-16 11:30:43.938000 +71,26557,5974,7167,26,2015-08-16 11:22:37.000000,2015-08-16 11:31:31.938000 +71,6192,5974,7167,27,2015-08-16 11:22:37.000000,2015-08-16 11:32:07.938000 +71,28801,5974,7167,28,2015-08-16 11:22:37.000000,2015-08-16 11:34:35.938000 +71,8064,5974,7167,17,2015-08-16 11:23:09.000000,2015-08-16 11:23:53.343000 +71,40841,5974,7167,18,2015-08-16 11:23:09.000000,2015-08-16 11:24:35.343000 +71,6183,5974,7167,19,2015-08-16 11:23:09.000000,2015-08-16 11:25:05.343000 +71,26771,5974,7167,20,2015-08-16 11:23:09.000000,2015-08-16 11:26:17.343000 +71,26721,5974,7167,21,2015-08-16 11:23:09.000000,2015-08-16 11:27:29.343000 +71,5455,5974,7167,22,2015-08-16 11:23:09.000000,2015-08-16 11:28:29.343000 +71,6190,5974,7167,23,2015-08-16 11:23:09.000000,2015-08-16 11:29:17.343000 +71,6195,5974,7167,24,2015-08-16 11:23:09.000000,2015-08-16 11:30:05.343000 +71,6194,5974,7167,25,2015-08-16 11:23:09.000000,2015-08-16 11:30:59.343000 +71,26557,5974,7167,26,2015-08-16 11:23:09.000000,2015-08-16 11:31:47.343000 +71,6192,5974,7167,27,2015-08-16 11:23:09.000000,2015-08-16 11:32:23.343000 +71,28801,5974,7167,28,2015-08-16 11:23:09.000000,2015-08-16 11:34:51.343000 +71,6192,6011,7167,27,2015-08-16 11:32:44.000000,2015-08-16 11:33:05.519000 +71,28801,6011,7167,28,2015-08-16 11:32:44.000000,2015-08-16 11:35:57.519000 +71,28801,6011,7167,28,2015-08-16 11:33:54.000000,2015-08-16 11:35:08.208000 +71,28801,6011,7167,28,2015-08-16 11:34:27.000000,2015-08-16 11:35:36.713000 +71,6868,5991,7167,2,2015-08-16 11:47:08.000000,2015-08-16 11:47:47.102000 +71,6869,5991,7167,3,2015-08-16 11:47:08.000000,2015-08-16 11:48:23.102000 +71,8085,5991,7167,4,2015-08-16 11:47:08.000000,2015-08-16 11:48:53.102000 +71,8087,5991,7167,5,2015-08-16 11:47:08.000000,2015-08-16 11:49:41.102000 +71,6191,5991,7167,6,2015-08-16 11:47:08.000000,2015-08-16 11:50:41.102000 +71,6189,5991,7167,7,2015-08-16 11:47:08.000000,2015-08-16 11:51:47.102000 +71,10376,5991,7167,8,2015-08-16 11:47:08.000000,2015-08-16 11:52:29.102000 +71,26719,5991,7167,9,2015-08-16 11:47:08.000000,2015-08-16 11:53:11.102000 +71,26770,5991,7167,10,2015-08-16 11:47:08.000000,2015-08-16 11:54:17.102000 +71,6199,5991,7167,11,2015-08-16 11:47:08.000000,2015-08-16 11:54:59.102000 +71,40840,5991,7167,12,2015-08-16 11:47:08.000000,2015-08-16 11:55:53.102000 +71,8059,5991,7167,13,2015-08-16 11:47:08.000000,2015-08-16 11:56:23.102000 +71,5998,5991,7167,14,2015-08-16 11:47:08.000000,2015-08-16 11:57:17.102000 +71,26877,5991,7167,15,2015-08-16 11:47:08.000000,2015-08-16 11:58:17.102000 +71,6483,5991,7167,16,2015-08-16 11:47:08.000000,2015-08-16 12:01:11.102000 +71,6510,5991,7167,17,2015-08-16 11:47:08.000000,2015-08-16 12:02:29.102000 +71,6481,5991,7167,18,2015-08-16 11:47:08.000000,2015-08-16 12:03:35.102000 +71,8930,5991,7167,19,2015-08-16 11:47:08.000000,2015-08-16 12:05:11.102000 +71,6476,5991,7167,20,2015-08-16 11:47:08.000000,2015-08-16 12:06:59.102000 +71,6475,5991,7167,21,2015-08-16 11:47:08.000000,2015-08-16 12:07:29.102000 +71,5586,5991,7167,22,2015-08-16 11:47:08.000000,2015-08-16 12:08:41.102000 +71,5590,5991,7167,23,2015-08-16 11:47:08.000000,2015-08-16 12:09:23.102000 +71,40835,5991,7167,24,2015-08-16 11:47:08.000000,2015-08-16 12:10:53.102000 +71,7367,5991,7167,25,2015-08-16 11:47:08.000000,2015-08-16 12:12:05.102000 +71,5580,5991,7167,26,2015-08-16 11:47:08.000000,2015-08-16 12:12:47.102000 +71,7495,5991,7167,27,2015-08-16 11:47:08.000000,2015-08-16 12:13:41.102000 +71,7554,5991,7167,28,2015-08-16 11:47:08.000000,2015-08-16 12:15:53.102000 +71,28988,5991,7167,29,2015-08-16 11:47:08.000000,2015-08-16 12:17:09.102000 +71,6868,5991,7167,2,2015-08-16 11:47:57.000000,2015-08-16 11:48:01.598000 +71,6869,5991,7167,3,2015-08-16 11:47:57.000000,2015-08-16 11:48:37.598000 +71,8085,5991,7167,4,2015-08-16 11:47:57.000000,2015-08-16 11:49:07.598000 +71,8087,5991,7167,5,2015-08-16 11:47:57.000000,2015-08-16 11:49:55.598000 +71,6191,5991,7167,6,2015-08-16 11:47:57.000000,2015-08-16 11:50:55.598000 +71,6189,5991,7167,7,2015-08-16 11:47:57.000000,2015-08-16 11:52:01.598000 +71,10376,5991,7167,8,2015-08-16 11:47:57.000000,2015-08-16 11:52:43.598000 +71,26719,5991,7167,9,2015-08-16 11:47:57.000000,2015-08-16 11:53:25.598000 +71,26770,5991,7167,10,2015-08-16 11:47:57.000000,2015-08-16 11:54:31.598000 +71,6199,5991,7167,11,2015-08-16 11:47:57.000000,2015-08-16 11:55:13.598000 +71,40840,5991,7167,12,2015-08-16 11:47:57.000000,2015-08-16 11:56:07.598000 +71,8059,5991,7167,13,2015-08-16 11:47:57.000000,2015-08-16 11:56:37.598000 +71,5998,5991,7167,14,2015-08-16 11:47:57.000000,2015-08-16 11:57:31.598000 +71,26877,5991,7167,15,2015-08-16 11:47:57.000000,2015-08-16 11:58:31.598000 +71,6483,5991,7167,16,2015-08-16 11:47:57.000000,2015-08-16 12:01:25.598000 +71,6510,5991,7167,17,2015-08-16 11:47:57.000000,2015-08-16 12:02:43.598000 +71,6481,5991,7167,18,2015-08-16 11:47:57.000000,2015-08-16 12:03:49.598000 +71,8930,5991,7167,19,2015-08-16 11:47:57.000000,2015-08-16 12:05:25.598000 +71,6476,5991,7167,20,2015-08-16 11:47:57.000000,2015-08-16 12:07:13.598000 +71,6475,5991,7167,21,2015-08-16 11:47:57.000000,2015-08-16 12:07:43.598000 +71,5586,5991,7167,22,2015-08-16 11:47:57.000000,2015-08-16 12:08:55.598000 +71,5590,5991,7167,23,2015-08-16 11:47:57.000000,2015-08-16 12:09:37.598000 +71,40835,5991,7167,24,2015-08-16 11:47:57.000000,2015-08-16 12:11:07.598000 +71,7367,5991,7167,25,2015-08-16 11:47:57.000000,2015-08-16 12:12:19.598000 +71,5580,5991,7167,26,2015-08-16 11:47:57.000000,2015-08-16 12:13:01.598000 +71,7495,5991,7167,27,2015-08-16 11:47:57.000000,2015-08-16 12:13:55.598000 +71,7554,5991,7167,28,2015-08-16 11:47:57.000000,2015-08-16 12:16:07.598000 +71,28988,5991,7167,29,2015-08-16 11:47:57.000000,2015-08-16 12:17:23.598000 +71,6868,5991,7167,2,2015-08-16 11:48:38.000000,2015-08-16 11:48:42.598000 +71,6869,5991,7167,3,2015-08-16 11:48:38.000000,2015-08-16 11:49:18.598000 +71,8085,5991,7167,4,2015-08-16 11:48:38.000000,2015-08-16 11:49:48.598000 +71,8087,5991,7167,5,2015-08-16 11:48:38.000000,2015-08-16 11:50:36.598000 +71,6191,5991,7167,6,2015-08-16 11:48:38.000000,2015-08-16 11:51:36.598000 +71,6189,5991,7167,7,2015-08-16 11:48:38.000000,2015-08-16 11:52:42.598000 +71,10376,5991,7167,8,2015-08-16 11:48:38.000000,2015-08-16 11:53:24.598000 +71,26719,5991,7167,9,2015-08-16 11:48:38.000000,2015-08-16 11:54:06.598000 +71,26770,5991,7167,10,2015-08-16 11:48:38.000000,2015-08-16 11:55:12.598000 +71,6199,5991,7167,11,2015-08-16 11:48:38.000000,2015-08-16 11:55:54.598000 +71,40840,5991,7167,12,2015-08-16 11:48:38.000000,2015-08-16 11:56:48.598000 +71,8059,5991,7167,13,2015-08-16 11:48:38.000000,2015-08-16 11:57:18.598000 +71,5998,5991,7167,14,2015-08-16 11:48:38.000000,2015-08-16 11:58:12.598000 +71,26877,5991,7167,15,2015-08-16 11:48:38.000000,2015-08-16 11:59:12.598000 +71,6483,5991,7167,16,2015-08-16 11:48:38.000000,2015-08-16 12:02:06.598000 +71,6510,5991,7167,17,2015-08-16 11:48:38.000000,2015-08-16 12:03:24.598000 +71,6481,5991,7167,18,2015-08-16 11:48:38.000000,2015-08-16 12:04:30.598000 +71,8930,5991,7167,19,2015-08-16 11:48:38.000000,2015-08-16 12:06:06.598000 +71,6476,5991,7167,20,2015-08-16 11:48:38.000000,2015-08-16 12:07:54.598000 +71,6475,5991,7167,21,2015-08-16 11:48:38.000000,2015-08-16 12:08:24.598000 +71,5586,5991,7167,22,2015-08-16 11:48:38.000000,2015-08-16 12:09:36.598000 +71,5590,5991,7167,23,2015-08-16 11:48:38.000000,2015-08-16 12:10:18.598000 +71,40835,5991,7167,24,2015-08-16 11:48:38.000000,2015-08-16 12:11:48.598000 +71,7367,5991,7167,25,2015-08-16 11:48:38.000000,2015-08-16 12:13:00.598000 +71,5580,5991,7167,26,2015-08-16 11:48:38.000000,2015-08-16 12:13:42.598000 +71,7495,5991,7167,27,2015-08-16 11:48:38.000000,2015-08-16 12:14:36.598000 +71,7554,5991,7167,28,2015-08-16 11:48:38.000000,2015-08-16 12:16:48.598000 +71,28988,5991,7167,29,2015-08-16 11:48:38.000000,2015-08-16 12:18:04.598000 +71,6191,5991,7167,6,2015-08-16 11:49:10.000000,2015-08-16 11:49:58.135000 +71,6189,5991,7167,7,2015-08-16 11:49:10.000000,2015-08-16 11:51:04.135000 +71,10376,5991,7167,8,2015-08-16 11:49:10.000000,2015-08-16 11:51:46.135000 +71,26719,5991,7167,9,2015-08-16 11:49:10.000000,2015-08-16 11:52:28.135000 +71,26770,5991,7167,10,2015-08-16 11:49:10.000000,2015-08-16 11:53:34.135000 +71,6199,5991,7167,11,2015-08-16 11:49:10.000000,2015-08-16 11:54:16.135000 +71,40840,5991,7167,12,2015-08-16 11:49:10.000000,2015-08-16 11:55:10.135000 +71,8059,5991,7167,13,2015-08-16 11:49:10.000000,2015-08-16 11:55:40.135000 +71,5998,5991,7167,14,2015-08-16 11:49:10.000000,2015-08-16 11:56:34.135000 +71,26877,5991,7167,15,2015-08-16 11:49:10.000000,2015-08-16 11:57:34.135000 +71,6483,5991,7167,16,2015-08-16 11:49:10.000000,2015-08-16 12:00:28.135000 +71,6510,5991,7167,17,2015-08-16 11:49:10.000000,2015-08-16 12:01:46.135000 +71,6481,5991,7167,18,2015-08-16 11:49:10.000000,2015-08-16 12:02:52.135000 +71,8930,5991,7167,19,2015-08-16 11:49:10.000000,2015-08-16 12:04:28.135000 +71,6476,5991,7167,20,2015-08-16 11:49:10.000000,2015-08-16 12:06:16.135000 +71,6475,5991,7167,21,2015-08-16 11:49:10.000000,2015-08-16 12:06:46.135000 +71,5586,5991,7167,22,2015-08-16 11:49:10.000000,2015-08-16 12:07:58.135000 +71,5590,5991,7167,23,2015-08-16 11:49:10.000000,2015-08-16 12:08:40.135000 +71,40835,5991,7167,24,2015-08-16 11:49:10.000000,2015-08-16 12:10:10.135000 +71,7367,5991,7167,25,2015-08-16 11:49:10.000000,2015-08-16 12:11:22.135000 +71,5580,5991,7167,26,2015-08-16 11:49:10.000000,2015-08-16 12:12:04.135000 +71,7495,5991,7167,27,2015-08-16 11:49:10.000000,2015-08-16 12:12:58.135000 +71,7554,5991,7167,28,2015-08-16 11:49:10.000000,2015-08-16 12:15:10.135000 +71,28988,5991,7167,29,2015-08-16 11:49:10.000000,2015-08-16 12:16:26.135000 +71,6483,5991,7167,16,2015-08-16 12:02:07.000000,2015-08-16 12:04:34.473000 +71,6510,5991,7167,17,2015-08-16 12:02:07.000000,2015-08-16 12:05:52.473000 +71,6481,5991,7167,18,2015-08-16 12:02:07.000000,2015-08-16 12:06:58.473000 +71,8930,5991,7167,19,2015-08-16 12:02:07.000000,2015-08-16 12:08:34.473000 +71,6476,5991,7167,20,2015-08-16 12:02:07.000000,2015-08-16 12:10:22.473000 +71,6475,5991,7167,21,2015-08-16 12:02:07.000000,2015-08-16 12:10:52.473000 +71,5586,5991,7167,22,2015-08-16 12:02:07.000000,2015-08-16 12:12:04.473000 +71,5590,5991,7167,23,2015-08-16 12:02:07.000000,2015-08-16 12:12:46.473000 +71,40835,5991,7167,24,2015-08-16 12:02:07.000000,2015-08-16 12:14:16.473000 +71,7367,5991,7167,25,2015-08-16 12:02:07.000000,2015-08-16 12:15:28.473000 +71,5580,5991,7167,26,2015-08-16 12:02:07.000000,2015-08-16 12:16:10.473000 +71,7495,5991,7167,27,2015-08-16 12:02:07.000000,2015-08-16 12:17:04.473000 +71,7554,5991,7167,28,2015-08-16 12:02:07.000000,2015-08-16 12:19:16.473000 +71,28988,5991,7167,29,2015-08-16 12:02:07.000000,2015-08-16 12:20:32.473000 +71,6483,5991,7167,16,2015-08-16 12:03:12.000000,2015-08-16 12:04:00.600000 +71,6510,5991,7167,17,2015-08-16 12:03:12.000000,2015-08-16 12:05:18.600000 +71,6481,5991,7167,18,2015-08-16 12:03:12.000000,2015-08-16 12:06:24.600000 +71,8930,5991,7167,19,2015-08-16 12:03:12.000000,2015-08-16 12:08:00.600000 +71,6476,5991,7167,20,2015-08-16 12:03:12.000000,2015-08-16 12:09:48.600000 +71,6475,5991,7167,21,2015-08-16 12:03:12.000000,2015-08-16 12:10:18.600000 +71,5586,5991,7167,22,2015-08-16 12:03:12.000000,2015-08-16 12:11:30.600000 +71,5590,5991,7167,23,2015-08-16 12:03:12.000000,2015-08-16 12:12:12.600000 +71,40835,5991,7167,24,2015-08-16 12:03:12.000000,2015-08-16 12:13:42.600000 +71,7367,5991,7167,25,2015-08-16 12:03:12.000000,2015-08-16 12:14:54.600000 +71,5580,5991,7167,26,2015-08-16 12:03:12.000000,2015-08-16 12:15:36.600000 +71,7495,5991,7167,27,2015-08-16 12:03:12.000000,2015-08-16 12:16:30.600000 +71,7554,5991,7167,28,2015-08-16 12:03:12.000000,2015-08-16 12:18:42.600000 +71,28988,5991,7167,29,2015-08-16 12:03:12.000000,2015-08-16 12:19:58.600000 +71,6483,5991,7167,16,2015-08-16 12:03:45.000000,2015-08-16 12:03:58.775000 +71,6510,5991,7167,17,2015-08-16 12:03:45.000000,2015-08-16 12:05:16.775000 +71,6481,5991,7167,18,2015-08-16 12:03:45.000000,2015-08-16 12:06:22.775000 +71,8930,5991,7167,19,2015-08-16 12:03:45.000000,2015-08-16 12:07:58.775000 +71,6476,5991,7167,20,2015-08-16 12:03:45.000000,2015-08-16 12:09:46.775000 +71,6475,5991,7167,21,2015-08-16 12:03:45.000000,2015-08-16 12:10:16.775000 +71,5586,5991,7167,22,2015-08-16 12:03:45.000000,2015-08-16 12:11:28.775000 +71,5590,5991,7167,23,2015-08-16 12:03:45.000000,2015-08-16 12:12:10.775000 +71,40835,5991,7167,24,2015-08-16 12:03:45.000000,2015-08-16 12:13:40.775000 +71,7367,5991,7167,25,2015-08-16 12:03:45.000000,2015-08-16 12:14:52.775000 +71,5580,5991,7167,26,2015-08-16 12:03:45.000000,2015-08-16 12:15:34.775000 +71,7495,5991,7167,27,2015-08-16 12:03:45.000000,2015-08-16 12:16:28.775000 +71,7554,5991,7167,28,2015-08-16 12:03:45.000000,2015-08-16 12:18:40.775000 +71,28988,5991,7167,29,2015-08-16 12:03:45.000000,2015-08-16 12:19:56.775000 +71,6510,5991,7167,17,2015-08-16 12:04:18.000000,2015-08-16 12:05:12.974000 +71,6481,5991,7167,18,2015-08-16 12:04:18.000000,2015-08-16 12:06:18.974000 +71,8930,5991,7167,19,2015-08-16 12:04:18.000000,2015-08-16 12:07:54.974000 +71,6476,5991,7167,20,2015-08-16 12:04:18.000000,2015-08-16 12:09:42.974000 +71,6475,5991,7167,21,2015-08-16 12:04:18.000000,2015-08-16 12:10:12.974000 +71,5586,5991,7167,22,2015-08-16 12:04:18.000000,2015-08-16 12:11:24.974000 +71,5590,5991,7167,23,2015-08-16 12:04:18.000000,2015-08-16 12:12:06.974000 +71,40835,5991,7167,24,2015-08-16 12:04:18.000000,2015-08-16 12:13:36.974000 +71,7367,5991,7167,25,2015-08-16 12:04:18.000000,2015-08-16 12:14:48.974000 +71,5580,5991,7167,26,2015-08-16 12:04:18.000000,2015-08-16 12:15:30.974000 +71,7495,5991,7167,27,2015-08-16 12:04:18.000000,2015-08-16 12:16:24.974000 +71,7554,5991,7167,28,2015-08-16 12:04:18.000000,2015-08-16 12:18:36.974000 +71,28988,5991,7167,29,2015-08-16 12:04:18.000000,2015-08-16 12:19:52.974000 +71,6481,5991,7167,18,2015-08-16 12:05:07.000000,2015-08-16 12:05:56.436000 +71,8930,5991,7167,19,2015-08-16 12:05:07.000000,2015-08-16 12:07:32.436000 +71,6476,5991,7167,20,2015-08-16 12:05:07.000000,2015-08-16 12:09:20.436000 +71,6475,5991,7167,21,2015-08-16 12:05:07.000000,2015-08-16 12:09:50.436000 +71,5586,5991,7167,22,2015-08-16 12:05:07.000000,2015-08-16 12:11:02.436000 +71,5590,5991,7167,23,2015-08-16 12:05:07.000000,2015-08-16 12:11:44.436000 +71,40835,5991,7167,24,2015-08-16 12:05:07.000000,2015-08-16 12:13:14.436000 +71,7367,5991,7167,25,2015-08-16 12:05:07.000000,2015-08-16 12:14:26.436000 +71,5580,5991,7167,26,2015-08-16 12:05:07.000000,2015-08-16 12:15:08.436000 +71,7495,5991,7167,27,2015-08-16 12:05:07.000000,2015-08-16 12:16:02.436000 +71,7554,5991,7167,28,2015-08-16 12:05:07.000000,2015-08-16 12:18:14.436000 +71,28988,5991,7167,29,2015-08-16 12:05:07.000000,2015-08-16 12:19:30.436000 +71,6481,5991,7167,18,2015-08-16 12:05:37.000000,2015-08-16 12:05:55.877000 +71,8930,5991,7167,19,2015-08-16 12:05:37.000000,2015-08-16 12:07:31.877000 +71,6476,5991,7167,20,2015-08-16 12:05:37.000000,2015-08-16 12:09:19.877000 +71,6475,5991,7167,21,2015-08-16 12:05:37.000000,2015-08-16 12:09:49.877000 +71,5586,5991,7167,22,2015-08-16 12:05:37.000000,2015-08-16 12:11:01.877000 +71,5590,5991,7167,23,2015-08-16 12:05:37.000000,2015-08-16 12:11:43.877000 +71,40835,5991,7167,24,2015-08-16 12:05:37.000000,2015-08-16 12:13:13.877000 +71,7367,5991,7167,25,2015-08-16 12:05:37.000000,2015-08-16 12:14:25.877000 +71,5580,5991,7167,26,2015-08-16 12:05:37.000000,2015-08-16 12:15:07.877000 +71,7495,5991,7167,27,2015-08-16 12:05:37.000000,2015-08-16 12:16:01.877000 +71,7554,5991,7167,28,2015-08-16 12:05:37.000000,2015-08-16 12:18:13.877000 +71,28988,5991,7167,29,2015-08-16 12:05:37.000000,2015-08-16 12:19:29.877000 +71,8930,5991,7167,19,2015-08-16 12:06:03.000000,2015-08-16 12:07:22.915000 +71,6476,5991,7167,20,2015-08-16 12:06:03.000000,2015-08-16 12:09:10.915000 +71,6475,5991,7167,21,2015-08-16 12:06:03.000000,2015-08-16 12:09:40.915000 +71,5586,5991,7167,22,2015-08-16 12:06:03.000000,2015-08-16 12:10:52.915000 +71,5590,5991,7167,23,2015-08-16 12:06:03.000000,2015-08-16 12:11:34.915000 +71,40835,5991,7167,24,2015-08-16 12:06:03.000000,2015-08-16 12:13:04.915000 +71,7367,5991,7167,25,2015-08-16 12:06:03.000000,2015-08-16 12:14:16.915000 +71,5580,5991,7167,26,2015-08-16 12:06:03.000000,2015-08-16 12:14:58.915000 +71,7495,5991,7167,27,2015-08-16 12:06:03.000000,2015-08-16 12:15:52.915000 +71,7554,5991,7167,28,2015-08-16 12:06:03.000000,2015-08-16 12:18:04.915000 +71,28988,5991,7167,29,2015-08-16 12:06:03.000000,2015-08-16 12:19:20.915000 +71,8930,5991,7167,19,2015-08-16 12:07:05.000000,2015-08-16 12:07:05.417000 +71,6476,5991,7167,20,2015-08-16 12:07:05.000000,2015-08-16 12:08:53.417000 +71,6475,5991,7167,21,2015-08-16 12:07:05.000000,2015-08-16 12:09:23.417000 +71,5586,5991,7167,22,2015-08-16 12:07:05.000000,2015-08-16 12:10:35.417000 +71,5590,5991,7167,23,2015-08-16 12:07:05.000000,2015-08-16 12:11:17.417000 +71,40835,5991,7167,24,2015-08-16 12:07:05.000000,2015-08-16 12:12:47.417000 +71,7367,5991,7167,25,2015-08-16 12:07:05.000000,2015-08-16 12:13:59.417000 +71,5580,5991,7167,26,2015-08-16 12:07:05.000000,2015-08-16 12:14:41.417000 +71,7495,5991,7167,27,2015-08-16 12:07:05.000000,2015-08-16 12:15:35.417000 +71,7554,5991,7167,28,2015-08-16 12:07:05.000000,2015-08-16 12:17:47.417000 +71,28988,5991,7167,29,2015-08-16 12:07:05.000000,2015-08-16 12:19:03.417000 +71,8930,5991,7167,19,2015-08-16 12:07:36.000000,2015-08-16 12:07:36.417000 +71,6476,5991,7167,20,2015-08-16 12:07:36.000000,2015-08-16 12:09:24.417000 +71,6475,5991,7167,21,2015-08-16 12:07:36.000000,2015-08-16 12:09:54.417000 +71,5586,5991,7167,22,2015-08-16 12:07:36.000000,2015-08-16 12:11:06.417000 +71,5590,5991,7167,23,2015-08-16 12:07:36.000000,2015-08-16 12:11:48.417000 +71,40835,5991,7167,24,2015-08-16 12:07:36.000000,2015-08-16 12:13:18.417000 +71,7367,5991,7167,25,2015-08-16 12:07:36.000000,2015-08-16 12:14:30.417000 +71,5580,5991,7167,26,2015-08-16 12:07:36.000000,2015-08-16 12:15:12.417000 +71,7495,5991,7167,27,2015-08-16 12:07:36.000000,2015-08-16 12:16:06.417000 +71,7554,5991,7167,28,2015-08-16 12:07:36.000000,2015-08-16 12:18:18.417000 +71,28988,5991,7167,29,2015-08-16 12:07:36.000000,2015-08-16 12:19:34.417000 +71,6476,5991,7167,20,2015-08-16 12:08:13.000000,2015-08-16 12:09:25.471000 +71,6475,5991,7167,21,2015-08-16 12:08:13.000000,2015-08-16 12:09:55.471000 +71,5586,5991,7167,22,2015-08-16 12:08:13.000000,2015-08-16 12:11:07.471000 +71,5590,5991,7167,23,2015-08-16 12:08:13.000000,2015-08-16 12:11:49.471000 +71,40835,5991,7167,24,2015-08-16 12:08:13.000000,2015-08-16 12:13:19.471000 +71,7367,5991,7167,25,2015-08-16 12:08:13.000000,2015-08-16 12:14:31.471000 +71,5580,5991,7167,26,2015-08-16 12:08:13.000000,2015-08-16 12:15:13.471000 +71,7495,5991,7167,27,2015-08-16 12:08:13.000000,2015-08-16 12:16:07.471000 +71,7554,5991,7167,28,2015-08-16 12:08:13.000000,2015-08-16 12:18:19.471000 +71,28988,5991,7167,29,2015-08-16 12:08:13.000000,2015-08-16 12:19:35.471000 +71,6476,5991,7167,20,2015-08-16 12:08:45.000000,2015-08-16 12:09:18.351000 +71,6475,5991,7167,21,2015-08-16 12:08:45.000000,2015-08-16 12:09:48.351000 +71,5586,5991,7167,22,2015-08-16 12:08:45.000000,2015-08-16 12:11:00.351000 +71,5590,5991,7167,23,2015-08-16 12:08:45.000000,2015-08-16 12:11:42.351000 +71,40835,5991,7167,24,2015-08-16 12:08:45.000000,2015-08-16 12:13:12.351000 +71,7367,5991,7167,25,2015-08-16 12:08:45.000000,2015-08-16 12:14:24.351000 +71,5580,5991,7167,26,2015-08-16 12:08:45.000000,2015-08-16 12:15:06.351000 +71,7495,5991,7167,27,2015-08-16 12:08:45.000000,2015-08-16 12:16:00.351000 +71,7554,5991,7167,28,2015-08-16 12:08:45.000000,2015-08-16 12:18:12.351000 +71,28988,5991,7167,29,2015-08-16 12:08:45.000000,2015-08-16 12:19:28.351000 +71,6475,5991,7167,21,2015-08-16 12:09:31.000000,2015-08-16 12:09:45.065000 +71,5586,5991,7167,22,2015-08-16 12:09:31.000000,2015-08-16 12:10:57.065000 +71,5590,5991,7167,23,2015-08-16 12:09:31.000000,2015-08-16 12:11:39.065000 +71,40835,5991,7167,24,2015-08-16 12:09:31.000000,2015-08-16 12:13:09.065000 +71,7367,5991,7167,25,2015-08-16 12:09:31.000000,2015-08-16 12:14:21.065000 +71,5580,5991,7167,26,2015-08-16 12:09:31.000000,2015-08-16 12:15:03.065000 +71,7495,5991,7167,27,2015-08-16 12:09:31.000000,2015-08-16 12:15:57.065000 +71,7554,5991,7167,28,2015-08-16 12:09:31.000000,2015-08-16 12:18:09.065000 +71,28988,5991,7167,29,2015-08-16 12:09:31.000000,2015-08-16 12:19:25.065000 +71,5586,5991,7167,22,2015-08-16 12:10:19.000000,2015-08-16 12:11:18.152000 +71,5590,5991,7167,23,2015-08-16 12:10:19.000000,2015-08-16 12:12:00.152000 +71,40835,5991,7167,24,2015-08-16 12:10:19.000000,2015-08-16 12:13:30.152000 +71,7367,5991,7167,25,2015-08-16 12:10:19.000000,2015-08-16 12:14:42.152000 +71,5580,5991,7167,26,2015-08-16 12:10:19.000000,2015-08-16 12:15:24.152000 +71,7495,5991,7167,27,2015-08-16 12:10:19.000000,2015-08-16 12:16:18.152000 +71,7554,5991,7167,28,2015-08-16 12:10:19.000000,2015-08-16 12:18:30.152000 +71,28988,5991,7167,29,2015-08-16 12:10:19.000000,2015-08-16 12:19:46.152000 +71,5586,5991,7167,22,2015-08-16 12:10:54.000000,2015-08-16 12:10:57.671000 +71,5590,5991,7167,23,2015-08-16 12:10:54.000000,2015-08-16 12:11:39.671000 +71,40835,5991,7167,24,2015-08-16 12:10:54.000000,2015-08-16 12:13:09.671000 +71,7367,5991,7167,25,2015-08-16 12:10:54.000000,2015-08-16 12:14:21.671000 +71,5580,5991,7167,26,2015-08-16 12:10:54.000000,2015-08-16 12:15:03.671000 +71,7495,5991,7167,27,2015-08-16 12:10:54.000000,2015-08-16 12:15:57.671000 +71,7554,5991,7167,28,2015-08-16 12:10:54.000000,2015-08-16 12:18:09.671000 +71,28988,5991,7167,29,2015-08-16 12:10:54.000000,2015-08-16 12:19:25.671000 +71,40835,5991,7167,24,2015-08-16 12:12:00.000000,2015-08-16 12:12:48.329000 +71,7367,5991,7167,25,2015-08-16 12:12:00.000000,2015-08-16 12:14:00.329000 +71,5580,5991,7167,26,2015-08-16 12:12:00.000000,2015-08-16 12:14:42.329000 +71,7495,5991,7167,27,2015-08-16 12:12:00.000000,2015-08-16 12:15:36.329000 +71,7554,5991,7167,28,2015-08-16 12:12:00.000000,2015-08-16 12:17:48.329000 +71,28988,5991,7167,29,2015-08-16 12:12:00.000000,2015-08-16 12:19:04.329000 +71,7367,5991,7167,25,2015-08-16 12:12:36.000000,2015-08-16 12:13:32.106000 +71,5580,5991,7167,26,2015-08-16 12:12:36.000000,2015-08-16 12:14:14.106000 +71,7495,5991,7167,27,2015-08-16 12:12:36.000000,2015-08-16 12:15:08.106000 +71,7554,5991,7167,28,2015-08-16 12:12:36.000000,2015-08-16 12:17:20.106000 +71,28988,5991,7167,29,2015-08-16 12:12:36.000000,2015-08-16 12:18:36.106000 +71,5580,5991,7167,26,2015-08-16 12:13:08.000000,2015-08-16 12:13:38.625000 +71,7495,5991,7167,27,2015-08-16 12:13:08.000000,2015-08-16 12:14:32.625000 +71,7554,5991,7167,28,2015-08-16 12:13:08.000000,2015-08-16 12:16:44.625000 +71,28988,5991,7167,29,2015-08-16 12:13:08.000000,2015-08-16 12:18:00.625000 +71,5580,5991,7167,26,2015-08-16 12:13:52.000000,2015-08-16 12:14:18.997000 +71,7495,5991,7167,27,2015-08-16 12:13:52.000000,2015-08-16 12:15:12.997000 +71,7554,5991,7167,28,2015-08-16 12:13:52.000000,2015-08-16 12:17:24.997000 +71,28988,5991,7167,29,2015-08-16 12:13:52.000000,2015-08-16 12:18:40.997000 +71,5580,5991,7167,26,2015-08-16 12:14:27.000000,2015-08-16 12:14:27.539000 +71,7495,5991,7167,27,2015-08-16 12:14:27.000000,2015-08-16 12:15:21.539000 +71,7554,5991,7167,28,2015-08-16 12:14:27.000000,2015-08-16 12:17:33.539000 +71,28988,5991,7167,29,2015-08-16 12:14:27.000000,2015-08-16 12:18:49.539000 +71,7495,5991,7167,27,2015-08-16 12:15:39.000000,2015-08-16 12:15:56.276000 +71,7554,5991,7167,28,2015-08-16 12:15:39.000000,2015-08-16 12:18:08.276000 +71,28988,5991,7167,29,2015-08-16 12:15:39.000000,2015-08-16 12:19:24.276000 +72,14557,6257,7167,7,2015-08-16 14:12:42.000000,2015-08-16 14:14:39.066000 +72,14616,6257,7167,8,2015-08-16 14:12:42.000000,2015-08-16 14:15:27.066000 +72,5501,6257,7167,9,2015-08-16 14:12:42.000000,2015-08-16 14:17:33.066000 +72,31870,6257,7167,10,2015-08-16 14:12:42.000000,2015-08-16 14:19:09.066000 +72,7815,6257,7167,11,2015-08-16 14:12:42.000000,2015-08-16 14:22:39.066000 +72,27249,6257,7167,12,2015-08-16 14:12:42.000000,2015-08-16 14:24:09.066000 +72,6455,6257,7167,13,2015-08-16 14:12:42.000000,2015-08-16 14:26:45.066000 +72,39642,6257,7167,14,2015-08-16 14:12:42.000000,2015-08-16 14:28:27.066000 +72,27196,6257,7167,15,2015-08-16 14:12:42.000000,2015-08-16 14:29:03.066000 +72,5574,6257,7167,16,2015-08-16 14:12:42.000000,2015-08-16 14:31:51.066000 +72,5575,6257,7167,17,2015-08-16 14:12:42.000000,2015-08-16 14:32:45.066000 +72,27061,6257,7167,18,2015-08-16 14:12:42.000000,2015-08-16 14:34:51.066000 +72,5585,6257,7167,19,2015-08-16 14:12:42.000000,2015-08-16 14:36:09.066000 +72,5589,6257,7167,20,2015-08-16 14:12:42.000000,2015-08-16 14:37:21.066000 +72,5587,6257,7167,21,2015-08-16 14:12:42.000000,2015-08-16 14:38:03.066000 +72,6515,6257,7167,22,2015-08-16 14:12:42.000000,2015-08-16 14:39:33.066000 +72,27014,6257,7167,23,2015-08-16 14:12:42.000000,2015-08-16 14:41:21.066000 +72,6512,6257,7167,24,2015-08-16 14:12:42.000000,2015-08-16 14:42:51.066000 +72,6482,6257,7167,25,2015-08-16 14:12:42.000000,2015-08-16 14:44:15.066000 +72,28903,6257,7167,26,2015-08-16 14:12:42.000000,2015-08-16 14:45:39.066000 +72,6508,6257,7167,27,2015-08-16 14:12:42.000000,2015-08-16 14:46:03.066000 +72,26871,6257,7167,28,2015-08-16 14:12:42.000000,2015-08-16 14:48:39.066000 +72,37212,6257,7167,29,2015-08-16 14:12:42.000000,2015-08-16 14:49:39.066000 +72,40419,6257,7167,30,2015-08-16 14:12:42.000000,2015-08-16 14:51:09.066000 +72,6490,6257,7167,31,2015-08-16 14:12:42.000000,2015-08-16 14:51:45.066000 +72,6501,6257,7167,32,2015-08-16 14:12:42.000000,2015-08-16 14:52:33.066000 +72,4693,6257,7167,33,2015-08-16 14:12:42.000000,2015-08-16 14:53:21.066000 +72,4692,6257,7167,34,2015-08-16 14:12:42.000000,2015-08-16 14:54:09.066000 +72,4690,6257,7167,35,2015-08-16 14:12:42.000000,2015-08-16 14:55:33.066000 +72,10531,6257,7167,36,2015-08-16 14:12:42.000000,2015-08-16 14:56:33.066000 +72,14557,6257,7167,7,2015-08-16 14:13:16.000000,2015-08-16 14:13:58.043000 +72,14616,6257,7167,8,2015-08-16 14:13:16.000000,2015-08-16 14:14:46.043000 +72,5501,6257,7167,9,2015-08-16 14:13:16.000000,2015-08-16 14:16:52.043000 +72,31870,6257,7167,10,2015-08-16 14:13:16.000000,2015-08-16 14:18:28.043000 +72,7815,6257,7167,11,2015-08-16 14:13:16.000000,2015-08-16 14:21:58.043000 +72,27249,6257,7167,12,2015-08-16 14:13:16.000000,2015-08-16 14:23:28.043000 +72,6455,6257,7167,13,2015-08-16 14:13:16.000000,2015-08-16 14:26:04.043000 +72,39642,6257,7167,14,2015-08-16 14:13:16.000000,2015-08-16 14:27:46.043000 +72,27196,6257,7167,15,2015-08-16 14:13:16.000000,2015-08-16 14:28:22.043000 +72,5574,6257,7167,16,2015-08-16 14:13:16.000000,2015-08-16 14:31:10.043000 +72,5575,6257,7167,17,2015-08-16 14:13:16.000000,2015-08-16 14:32:04.043000 +72,27061,6257,7167,18,2015-08-16 14:13:16.000000,2015-08-16 14:34:10.043000 +72,5585,6257,7167,19,2015-08-16 14:13:16.000000,2015-08-16 14:35:28.043000 +72,5589,6257,7167,20,2015-08-16 14:13:16.000000,2015-08-16 14:36:40.043000 +72,5587,6257,7167,21,2015-08-16 14:13:16.000000,2015-08-16 14:37:22.043000 +72,6515,6257,7167,22,2015-08-16 14:13:16.000000,2015-08-16 14:38:52.043000 +72,27014,6257,7167,23,2015-08-16 14:13:16.000000,2015-08-16 14:40:40.043000 +72,6512,6257,7167,24,2015-08-16 14:13:16.000000,2015-08-16 14:42:10.043000 +72,6482,6257,7167,25,2015-08-16 14:13:16.000000,2015-08-16 14:43:34.043000 +72,28903,6257,7167,26,2015-08-16 14:13:16.000000,2015-08-16 14:44:58.043000 +72,6508,6257,7167,27,2015-08-16 14:13:16.000000,2015-08-16 14:45:22.043000 +72,26871,6257,7167,28,2015-08-16 14:13:16.000000,2015-08-16 14:47:58.043000 +72,37212,6257,7167,29,2015-08-16 14:13:16.000000,2015-08-16 14:48:58.043000 +72,40419,6257,7167,30,2015-08-16 14:13:16.000000,2015-08-16 14:50:28.043000 +72,6490,6257,7167,31,2015-08-16 14:13:16.000000,2015-08-16 14:51:04.043000 +72,6501,6257,7167,32,2015-08-16 14:13:16.000000,2015-08-16 14:51:52.043000 +72,4693,6257,7167,33,2015-08-16 14:13:16.000000,2015-08-16 14:52:40.043000 +72,4692,6257,7167,34,2015-08-16 14:13:16.000000,2015-08-16 14:53:28.043000 +72,4690,6257,7167,35,2015-08-16 14:13:16.000000,2015-08-16 14:54:52.043000 +72,10531,6257,7167,36,2015-08-16 14:13:16.000000,2015-08-16 14:55:52.043000 +72,26632,6257,7167,37,2015-08-16 14:13:16.000000,2015-08-16 14:57:04.043000 +72,26624,6257,7167,38,2015-08-16 14:13:16.000000,2015-08-16 14:57:52.043000 +72,14557,6257,7167,7,2015-08-16 14:13:49.000000,2015-08-16 14:14:31.043000 +72,14616,6257,7167,8,2015-08-16 14:13:49.000000,2015-08-16 14:15:19.043000 +72,5501,6257,7167,9,2015-08-16 14:13:49.000000,2015-08-16 14:17:25.043000 +72,31870,6257,7167,10,2015-08-16 14:13:49.000000,2015-08-16 14:19:01.043000 +72,7815,6257,7167,11,2015-08-16 14:13:49.000000,2015-08-16 14:22:31.043000 +72,27249,6257,7167,12,2015-08-16 14:13:49.000000,2015-08-16 14:24:01.043000 +72,6455,6257,7167,13,2015-08-16 14:13:49.000000,2015-08-16 14:26:37.043000 +72,39642,6257,7167,14,2015-08-16 14:13:49.000000,2015-08-16 14:28:19.043000 +72,27196,6257,7167,15,2015-08-16 14:13:49.000000,2015-08-16 14:28:55.043000 +72,5574,6257,7167,16,2015-08-16 14:13:49.000000,2015-08-16 14:31:43.043000 +72,5575,6257,7167,17,2015-08-16 14:13:49.000000,2015-08-16 14:32:37.043000 +72,27061,6257,7167,18,2015-08-16 14:13:49.000000,2015-08-16 14:34:43.043000 +72,5585,6257,7167,19,2015-08-16 14:13:49.000000,2015-08-16 14:36:01.043000 +72,5589,6257,7167,20,2015-08-16 14:13:49.000000,2015-08-16 14:37:13.043000 +72,5587,6257,7167,21,2015-08-16 14:13:49.000000,2015-08-16 14:37:55.043000 +72,6515,6257,7167,22,2015-08-16 14:13:49.000000,2015-08-16 14:39:25.043000 +72,27014,6257,7167,23,2015-08-16 14:13:49.000000,2015-08-16 14:41:13.043000 +72,6512,6257,7167,24,2015-08-16 14:13:49.000000,2015-08-16 14:42:43.043000 +72,6482,6257,7167,25,2015-08-16 14:13:49.000000,2015-08-16 14:44:07.043000 +72,28903,6257,7167,26,2015-08-16 14:13:49.000000,2015-08-16 14:45:31.043000 +72,6508,6257,7167,27,2015-08-16 14:13:49.000000,2015-08-16 14:45:55.043000 +72,26871,6257,7167,28,2015-08-16 14:13:49.000000,2015-08-16 14:48:31.043000 +72,37212,6257,7167,29,2015-08-16 14:13:49.000000,2015-08-16 14:49:31.043000 +72,40419,6257,7167,30,2015-08-16 14:13:49.000000,2015-08-16 14:51:01.043000 +72,6490,6257,7167,31,2015-08-16 14:13:49.000000,2015-08-16 14:51:37.043000 +72,6501,6257,7167,32,2015-08-16 14:13:49.000000,2015-08-16 14:52:25.043000 +72,4693,6257,7167,33,2015-08-16 14:13:49.000000,2015-08-16 14:53:13.043000 +72,4692,6257,7167,34,2015-08-16 14:13:49.000000,2015-08-16 14:54:01.043000 +72,4690,6257,7167,35,2015-08-16 14:13:49.000000,2015-08-16 14:55:25.043000 +72,10531,6257,7167,36,2015-08-16 14:13:49.000000,2015-08-16 14:56:25.043000 +72,26632,6257,7167,37,2015-08-16 14:13:49.000000,2015-08-16 14:57:37.043000 +72,26624,6257,7167,38,2015-08-16 14:13:49.000000,2015-08-16 14:58:25.043000 +72,5501,6257,7167,9,2015-08-16 14:14:49.000000,2015-08-16 14:16:37.472000 +72,31870,6257,7167,10,2015-08-16 14:14:49.000000,2015-08-16 14:18:13.472000 +72,7815,6257,7167,11,2015-08-16 14:14:49.000000,2015-08-16 14:21:43.472000 +72,27249,6257,7167,12,2015-08-16 14:14:49.000000,2015-08-16 14:23:13.472000 +72,6455,6257,7167,13,2015-08-16 14:14:49.000000,2015-08-16 14:25:49.472000 +72,39642,6257,7167,14,2015-08-16 14:14:49.000000,2015-08-16 14:27:31.472000 +72,27196,6257,7167,15,2015-08-16 14:14:49.000000,2015-08-16 14:28:07.472000 +72,5574,6257,7167,16,2015-08-16 14:14:49.000000,2015-08-16 14:30:55.472000 +72,5575,6257,7167,17,2015-08-16 14:14:49.000000,2015-08-16 14:31:49.472000 +72,27061,6257,7167,18,2015-08-16 14:14:49.000000,2015-08-16 14:33:55.472000 +72,5585,6257,7167,19,2015-08-16 14:14:49.000000,2015-08-16 14:35:13.472000 +72,5589,6257,7167,20,2015-08-16 14:14:49.000000,2015-08-16 14:36:25.472000 +72,5587,6257,7167,21,2015-08-16 14:14:49.000000,2015-08-16 14:37:07.472000 +72,6515,6257,7167,22,2015-08-16 14:14:49.000000,2015-08-16 14:38:37.472000 +72,27014,6257,7167,23,2015-08-16 14:14:49.000000,2015-08-16 14:40:25.472000 +72,6512,6257,7167,24,2015-08-16 14:14:49.000000,2015-08-16 14:41:55.472000 +72,6482,6257,7167,25,2015-08-16 14:14:49.000000,2015-08-16 14:43:19.472000 +72,28903,6257,7167,26,2015-08-16 14:14:49.000000,2015-08-16 14:44:43.472000 +72,6508,6257,7167,27,2015-08-16 14:14:49.000000,2015-08-16 14:45:07.472000 +72,26871,6257,7167,28,2015-08-16 14:14:49.000000,2015-08-16 14:47:43.472000 +72,37212,6257,7167,29,2015-08-16 14:14:49.000000,2015-08-16 14:48:43.472000 +72,40419,6257,7167,30,2015-08-16 14:14:49.000000,2015-08-16 14:50:13.472000 +72,6490,6257,7167,31,2015-08-16 14:14:49.000000,2015-08-16 14:50:49.472000 +72,6501,6257,7167,32,2015-08-16 14:14:49.000000,2015-08-16 14:51:37.472000 +72,4693,6257,7167,33,2015-08-16 14:14:49.000000,2015-08-16 14:52:25.472000 +72,4692,6257,7167,34,2015-08-16 14:14:49.000000,2015-08-16 14:53:13.472000 +72,4690,6257,7167,35,2015-08-16 14:14:49.000000,2015-08-16 14:54:37.472000 +72,10531,6257,7167,36,2015-08-16 14:14:49.000000,2015-08-16 14:55:37.472000 +72,26632,6257,7167,37,2015-08-16 14:14:49.000000,2015-08-16 14:56:49.472000 +72,26624,6257,7167,38,2015-08-16 14:14:49.000000,2015-08-16 14:57:37.472000 +72,7319,6257,7167,39,2015-08-16 14:14:49.000000,2015-08-16 14:58:19.472000 +72,28802,6257,7167,40,2015-08-16 14:14:49.000000,2015-08-16 14:59:25.472000 +72,5501,6257,7167,9,2015-08-16 14:15:02.000000,2015-08-16 14:16:47.189000 +72,31870,6257,7167,10,2015-08-16 14:15:02.000000,2015-08-16 14:18:23.189000 +72,7815,6257,7167,11,2015-08-16 14:15:02.000000,2015-08-16 14:21:53.189000 +72,27249,6257,7167,12,2015-08-16 14:15:02.000000,2015-08-16 14:23:23.189000 +72,6455,6257,7167,13,2015-08-16 14:15:02.000000,2015-08-16 14:25:59.189000 +72,39642,6257,7167,14,2015-08-16 14:15:02.000000,2015-08-16 14:27:41.189000 +72,27196,6257,7167,15,2015-08-16 14:15:02.000000,2015-08-16 14:28:17.189000 +72,5574,6257,7167,16,2015-08-16 14:15:02.000000,2015-08-16 14:31:05.189000 +72,5575,6257,7167,17,2015-08-16 14:15:02.000000,2015-08-16 14:31:59.189000 +72,27061,6257,7167,18,2015-08-16 14:15:02.000000,2015-08-16 14:34:05.189000 +72,5585,6257,7167,19,2015-08-16 14:15:02.000000,2015-08-16 14:35:23.189000 +72,5589,6257,7167,20,2015-08-16 14:15:02.000000,2015-08-16 14:36:35.189000 +72,5587,6257,7167,21,2015-08-16 14:15:02.000000,2015-08-16 14:37:17.189000 +72,6515,6257,7167,22,2015-08-16 14:15:02.000000,2015-08-16 14:38:47.189000 +72,27014,6257,7167,23,2015-08-16 14:15:02.000000,2015-08-16 14:40:35.189000 +72,6512,6257,7167,24,2015-08-16 14:15:02.000000,2015-08-16 14:42:05.189000 +72,6482,6257,7167,25,2015-08-16 14:15:02.000000,2015-08-16 14:43:29.189000 +72,28903,6257,7167,26,2015-08-16 14:15:02.000000,2015-08-16 14:44:53.189000 +72,6508,6257,7167,27,2015-08-16 14:15:02.000000,2015-08-16 14:45:17.189000 +72,26871,6257,7167,28,2015-08-16 14:15:02.000000,2015-08-16 14:47:53.189000 +72,37212,6257,7167,29,2015-08-16 14:15:02.000000,2015-08-16 14:48:53.189000 +72,40419,6257,7167,30,2015-08-16 14:15:02.000000,2015-08-16 14:50:23.189000 +72,6490,6257,7167,31,2015-08-16 14:15:02.000000,2015-08-16 14:50:59.189000 +72,6501,6257,7167,32,2015-08-16 14:15:02.000000,2015-08-16 14:51:47.189000 +72,4693,6257,7167,33,2015-08-16 14:15:02.000000,2015-08-16 14:52:35.189000 +72,4692,6257,7167,34,2015-08-16 14:15:02.000000,2015-08-16 14:53:23.189000 +72,4690,6257,7167,35,2015-08-16 14:15:02.000000,2015-08-16 14:54:47.189000 +72,10531,6257,7167,36,2015-08-16 14:15:02.000000,2015-08-16 14:55:47.189000 +72,26632,6257,7167,37,2015-08-16 14:15:02.000000,2015-08-16 14:56:59.189000 +72,26624,6257,7167,38,2015-08-16 14:15:02.000000,2015-08-16 14:57:47.189000 +72,7319,6257,7167,39,2015-08-16 14:15:02.000000,2015-08-16 14:58:29.189000 +72,28802,6257,7167,40,2015-08-16 14:15:02.000000,2015-08-16 14:59:35.189000 +72,5501,6257,7167,9,2015-08-16 14:16:05.000000,2015-08-16 14:17:32.143000 +72,31870,6257,7167,10,2015-08-16 14:16:05.000000,2015-08-16 14:19:08.143000 +72,7815,6257,7167,11,2015-08-16 14:16:05.000000,2015-08-16 14:22:38.143000 +72,27249,6257,7167,12,2015-08-16 14:16:05.000000,2015-08-16 14:24:08.143000 +72,6455,6257,7167,13,2015-08-16 14:16:05.000000,2015-08-16 14:26:44.143000 +72,39642,6257,7167,14,2015-08-16 14:16:05.000000,2015-08-16 14:28:26.143000 +72,27196,6257,7167,15,2015-08-16 14:16:05.000000,2015-08-16 14:29:02.143000 +72,5574,6257,7167,16,2015-08-16 14:16:05.000000,2015-08-16 14:31:50.143000 +72,5575,6257,7167,17,2015-08-16 14:16:05.000000,2015-08-16 14:32:44.143000 +72,27061,6257,7167,18,2015-08-16 14:16:05.000000,2015-08-16 14:34:50.143000 +72,5585,6257,7167,19,2015-08-16 14:16:05.000000,2015-08-16 14:36:08.143000 +72,5589,6257,7167,20,2015-08-16 14:16:05.000000,2015-08-16 14:37:20.143000 +72,5587,6257,7167,21,2015-08-16 14:16:05.000000,2015-08-16 14:38:02.143000 +72,6515,6257,7167,22,2015-08-16 14:16:05.000000,2015-08-16 14:39:32.143000 +72,27014,6257,7167,23,2015-08-16 14:16:05.000000,2015-08-16 14:41:20.143000 +72,6512,6257,7167,24,2015-08-16 14:16:05.000000,2015-08-16 14:42:50.143000 +72,6482,6257,7167,25,2015-08-16 14:16:05.000000,2015-08-16 14:44:14.143000 +72,28903,6257,7167,26,2015-08-16 14:16:05.000000,2015-08-16 14:45:38.143000 +72,6508,6257,7167,27,2015-08-16 14:16:05.000000,2015-08-16 14:46:02.143000 +72,26871,6257,7167,28,2015-08-16 14:16:05.000000,2015-08-16 14:48:38.143000 +72,37212,6257,7167,29,2015-08-16 14:16:05.000000,2015-08-16 14:49:38.143000 +72,40419,6257,7167,30,2015-08-16 14:16:05.000000,2015-08-16 14:51:08.143000 +72,6490,6257,7167,31,2015-08-16 14:16:05.000000,2015-08-16 14:51:44.143000 +72,6501,6257,7167,32,2015-08-16 14:16:05.000000,2015-08-16 14:52:32.143000 +72,4693,6257,7167,33,2015-08-16 14:16:05.000000,2015-08-16 14:53:20.143000 +72,4692,6257,7167,34,2015-08-16 14:16:05.000000,2015-08-16 14:54:08.143000 +72,4690,6257,7167,35,2015-08-16 14:16:05.000000,2015-08-16 14:55:32.143000 +72,10531,6257,7167,36,2015-08-16 14:16:05.000000,2015-08-16 14:56:32.143000 +72,26632,6257,7167,37,2015-08-16 14:16:05.000000,2015-08-16 14:57:44.143000 +72,26624,6257,7167,38,2015-08-16 14:16:05.000000,2015-08-16 14:58:32.143000 +72,7319,6257,7167,39,2015-08-16 14:16:05.000000,2015-08-16 14:59:14.143000 +72,28802,6257,7167,40,2015-08-16 14:16:05.000000,2015-08-16 15:00:20.143000 +72,5501,6257,7167,9,2015-08-16 14:16:35.000000,2015-08-16 14:17:07.025000 +72,31870,6257,7167,10,2015-08-16 14:16:35.000000,2015-08-16 14:18:43.025000 +72,7815,6257,7167,11,2015-08-16 14:16:35.000000,2015-08-16 14:22:13.025000 +72,27249,6257,7167,12,2015-08-16 14:16:35.000000,2015-08-16 14:23:43.025000 +72,6455,6257,7167,13,2015-08-16 14:16:35.000000,2015-08-16 14:26:19.025000 +72,39642,6257,7167,14,2015-08-16 14:16:35.000000,2015-08-16 14:28:01.025000 +72,27196,6257,7167,15,2015-08-16 14:16:35.000000,2015-08-16 14:28:37.025000 +72,5574,6257,7167,16,2015-08-16 14:16:35.000000,2015-08-16 14:31:25.025000 +72,5575,6257,7167,17,2015-08-16 14:16:35.000000,2015-08-16 14:32:19.025000 +72,27061,6257,7167,18,2015-08-16 14:16:35.000000,2015-08-16 14:34:25.025000 +72,5585,6257,7167,19,2015-08-16 14:16:35.000000,2015-08-16 14:35:43.025000 +72,5589,6257,7167,20,2015-08-16 14:16:35.000000,2015-08-16 14:36:55.025000 +72,5587,6257,7167,21,2015-08-16 14:16:35.000000,2015-08-16 14:37:37.025000 +72,6515,6257,7167,22,2015-08-16 14:16:35.000000,2015-08-16 14:39:07.025000 +72,27014,6257,7167,23,2015-08-16 14:16:35.000000,2015-08-16 14:40:55.025000 +72,6512,6257,7167,24,2015-08-16 14:16:35.000000,2015-08-16 14:42:25.025000 +72,6482,6257,7167,25,2015-08-16 14:16:35.000000,2015-08-16 14:43:49.025000 +72,28903,6257,7167,26,2015-08-16 14:16:35.000000,2015-08-16 14:45:13.025000 +72,6508,6257,7167,27,2015-08-16 14:16:35.000000,2015-08-16 14:45:37.025000 +72,26871,6257,7167,28,2015-08-16 14:16:35.000000,2015-08-16 14:48:13.025000 +72,37212,6257,7167,29,2015-08-16 14:16:35.000000,2015-08-16 14:49:13.025000 +72,40419,6257,7167,30,2015-08-16 14:16:35.000000,2015-08-16 14:50:43.025000 +72,6490,6257,7167,31,2015-08-16 14:16:35.000000,2015-08-16 14:51:19.025000 +72,6501,6257,7167,32,2015-08-16 14:16:35.000000,2015-08-16 14:52:07.025000 +72,4693,6257,7167,33,2015-08-16 14:16:35.000000,2015-08-16 14:52:55.025000 +72,4692,6257,7167,34,2015-08-16 14:16:35.000000,2015-08-16 14:53:43.025000 +72,4690,6257,7167,35,2015-08-16 14:16:35.000000,2015-08-16 14:55:07.025000 +72,10531,6257,7167,36,2015-08-16 14:16:35.000000,2015-08-16 14:56:07.025000 +72,26632,6257,7167,37,2015-08-16 14:16:35.000000,2015-08-16 14:57:19.025000 +72,26624,6257,7167,38,2015-08-16 14:16:35.000000,2015-08-16 14:58:07.025000 +72,7319,6257,7167,39,2015-08-16 14:16:35.000000,2015-08-16 14:58:49.025000 +72,28802,6257,7167,40,2015-08-16 14:16:35.000000,2015-08-16 14:59:55.025000 +72,26557,6257,7167,41,2015-08-16 14:16:35.000000,2015-08-16 15:00:49.025000 +72,6192,6257,7167,42,2015-08-16 14:16:35.000000,2015-08-16 15:01:31.025000 +72,31870,6257,7167,10,2015-08-16 14:17:10.000000,2015-08-16 14:18:34.315000 +72,7815,6257,7167,11,2015-08-16 14:17:10.000000,2015-08-16 14:22:04.315000 +72,27249,6257,7167,12,2015-08-16 14:17:10.000000,2015-08-16 14:23:34.315000 +72,6455,6257,7167,13,2015-08-16 14:17:10.000000,2015-08-16 14:26:10.315000 +72,39642,6257,7167,14,2015-08-16 14:17:10.000000,2015-08-16 14:27:52.315000 +72,27196,6257,7167,15,2015-08-16 14:17:10.000000,2015-08-16 14:28:28.315000 +72,5574,6257,7167,16,2015-08-16 14:17:10.000000,2015-08-16 14:31:16.315000 +72,5575,6257,7167,17,2015-08-16 14:17:10.000000,2015-08-16 14:32:10.315000 +72,27061,6257,7167,18,2015-08-16 14:17:10.000000,2015-08-16 14:34:16.315000 +72,5585,6257,7167,19,2015-08-16 14:17:10.000000,2015-08-16 14:35:34.315000 +72,5589,6257,7167,20,2015-08-16 14:17:10.000000,2015-08-16 14:36:46.315000 +72,5587,6257,7167,21,2015-08-16 14:17:10.000000,2015-08-16 14:37:28.315000 +72,6515,6257,7167,22,2015-08-16 14:17:10.000000,2015-08-16 14:38:58.315000 +72,27014,6257,7167,23,2015-08-16 14:17:10.000000,2015-08-16 14:40:46.315000 +72,6512,6257,7167,24,2015-08-16 14:17:10.000000,2015-08-16 14:42:16.315000 +72,6482,6257,7167,25,2015-08-16 14:17:10.000000,2015-08-16 14:43:40.315000 +72,28903,6257,7167,26,2015-08-16 14:17:10.000000,2015-08-16 14:45:04.315000 +72,6508,6257,7167,27,2015-08-16 14:17:10.000000,2015-08-16 14:45:28.315000 +72,26871,6257,7167,28,2015-08-16 14:17:10.000000,2015-08-16 14:48:04.315000 +72,37212,6257,7167,29,2015-08-16 14:17:10.000000,2015-08-16 14:49:04.315000 +72,40419,6257,7167,30,2015-08-16 14:17:10.000000,2015-08-16 14:50:34.315000 +72,6490,6257,7167,31,2015-08-16 14:17:10.000000,2015-08-16 14:51:10.315000 +72,6501,6257,7167,32,2015-08-16 14:17:10.000000,2015-08-16 14:51:58.315000 +72,4693,6257,7167,33,2015-08-16 14:17:10.000000,2015-08-16 14:52:46.315000 +72,4692,6257,7167,34,2015-08-16 14:17:10.000000,2015-08-16 14:53:34.315000 +72,4690,6257,7167,35,2015-08-16 14:17:10.000000,2015-08-16 14:54:58.315000 +72,10531,6257,7167,36,2015-08-16 14:17:10.000000,2015-08-16 14:55:58.315000 +72,26632,6257,7167,37,2015-08-16 14:17:10.000000,2015-08-16 14:57:10.315000 +72,26624,6257,7167,38,2015-08-16 14:17:10.000000,2015-08-16 14:57:58.315000 +72,7319,6257,7167,39,2015-08-16 14:17:10.000000,2015-08-16 14:58:40.315000 +72,28802,6257,7167,40,2015-08-16 14:17:10.000000,2015-08-16 14:59:46.315000 +72,26557,6257,7167,41,2015-08-16 14:17:10.000000,2015-08-16 15:00:40.315000 +72,6192,6257,7167,42,2015-08-16 14:17:10.000000,2015-08-16 15:01:22.315000 +72,31870,6257,7167,10,2015-08-16 14:18:18.000000,2015-08-16 14:19:03.504000 +72,7815,6257,7167,11,2015-08-16 14:18:18.000000,2015-08-16 14:22:33.504000 +72,27249,6257,7167,12,2015-08-16 14:18:18.000000,2015-08-16 14:24:03.504000 +72,6455,6257,7167,13,2015-08-16 14:18:18.000000,2015-08-16 14:26:39.504000 +72,39642,6257,7167,14,2015-08-16 14:18:18.000000,2015-08-16 14:28:21.504000 +72,27196,6257,7167,15,2015-08-16 14:18:18.000000,2015-08-16 14:28:57.504000 +72,5574,6257,7167,16,2015-08-16 14:18:18.000000,2015-08-16 14:31:45.504000 +72,5575,6257,7167,17,2015-08-16 14:18:18.000000,2015-08-16 14:32:39.504000 +72,27061,6257,7167,18,2015-08-16 14:18:18.000000,2015-08-16 14:34:45.504000 +72,5585,6257,7167,19,2015-08-16 14:18:18.000000,2015-08-16 14:36:03.504000 +72,5589,6257,7167,20,2015-08-16 14:18:18.000000,2015-08-16 14:37:15.504000 +72,5587,6257,7167,21,2015-08-16 14:18:18.000000,2015-08-16 14:37:57.504000 +72,6515,6257,7167,22,2015-08-16 14:18:18.000000,2015-08-16 14:39:27.504000 +72,27014,6257,7167,23,2015-08-16 14:18:18.000000,2015-08-16 14:41:15.504000 +72,6512,6257,7167,24,2015-08-16 14:18:18.000000,2015-08-16 14:42:45.504000 +72,6482,6257,7167,25,2015-08-16 14:18:18.000000,2015-08-16 14:44:09.504000 +72,28903,6257,7167,26,2015-08-16 14:18:18.000000,2015-08-16 14:45:33.504000 +72,6508,6257,7167,27,2015-08-16 14:18:18.000000,2015-08-16 14:45:57.504000 +72,26871,6257,7167,28,2015-08-16 14:18:18.000000,2015-08-16 14:48:33.504000 +72,37212,6257,7167,29,2015-08-16 14:18:18.000000,2015-08-16 14:49:33.504000 +72,40419,6257,7167,30,2015-08-16 14:18:18.000000,2015-08-16 14:51:03.504000 +72,6490,6257,7167,31,2015-08-16 14:18:18.000000,2015-08-16 14:51:39.504000 +72,6501,6257,7167,32,2015-08-16 14:18:18.000000,2015-08-16 14:52:27.504000 +72,4693,6257,7167,33,2015-08-16 14:18:18.000000,2015-08-16 14:53:15.504000 +72,4692,6257,7167,34,2015-08-16 14:18:18.000000,2015-08-16 14:54:03.504000 +72,4690,6257,7167,35,2015-08-16 14:18:18.000000,2015-08-16 14:55:27.504000 +72,10531,6257,7167,36,2015-08-16 14:18:18.000000,2015-08-16 14:56:27.504000 +72,26632,6257,7167,37,2015-08-16 14:18:18.000000,2015-08-16 14:57:39.504000 +72,26624,6257,7167,38,2015-08-16 14:18:18.000000,2015-08-16 14:58:27.504000 +72,7319,6257,7167,39,2015-08-16 14:18:18.000000,2015-08-16 14:59:09.504000 +72,28802,6257,7167,40,2015-08-16 14:18:18.000000,2015-08-16 15:00:15.504000 +72,26557,6257,7167,41,2015-08-16 14:18:18.000000,2015-08-16 15:01:09.504000 +72,6192,6257,7167,42,2015-08-16 14:18:18.000000,2015-08-16 15:01:51.504000 +72,7815,6257,7167,11,2015-08-16 14:18:51.000000,2015-08-16 14:21:56.840000 +72,27249,6257,7167,12,2015-08-16 14:18:51.000000,2015-08-16 14:23:26.840000 +72,6455,6257,7167,13,2015-08-16 14:18:51.000000,2015-08-16 14:26:02.840000 +72,39642,6257,7167,14,2015-08-16 14:18:51.000000,2015-08-16 14:27:44.840000 +72,27196,6257,7167,15,2015-08-16 14:18:51.000000,2015-08-16 14:28:20.840000 +72,5574,6257,7167,16,2015-08-16 14:18:51.000000,2015-08-16 14:31:08.840000 +72,5575,6257,7167,17,2015-08-16 14:18:51.000000,2015-08-16 14:32:02.840000 +72,27061,6257,7167,18,2015-08-16 14:18:51.000000,2015-08-16 14:34:08.840000 +72,5585,6257,7167,19,2015-08-16 14:18:51.000000,2015-08-16 14:35:26.840000 +72,5589,6257,7167,20,2015-08-16 14:18:51.000000,2015-08-16 14:36:38.840000 +72,5587,6257,7167,21,2015-08-16 14:18:51.000000,2015-08-16 14:37:20.840000 +72,6515,6257,7167,22,2015-08-16 14:18:51.000000,2015-08-16 14:38:50.840000 +72,27014,6257,7167,23,2015-08-16 14:18:51.000000,2015-08-16 14:40:38.840000 +72,6512,6257,7167,24,2015-08-16 14:18:51.000000,2015-08-16 14:42:08.840000 +72,6482,6257,7167,25,2015-08-16 14:18:51.000000,2015-08-16 14:43:32.840000 +72,28903,6257,7167,26,2015-08-16 14:18:51.000000,2015-08-16 14:44:56.840000 +72,6508,6257,7167,27,2015-08-16 14:18:51.000000,2015-08-16 14:45:20.840000 +72,26871,6257,7167,28,2015-08-16 14:18:51.000000,2015-08-16 14:47:56.840000 +72,37212,6257,7167,29,2015-08-16 14:18:51.000000,2015-08-16 14:48:56.840000 +72,40419,6257,7167,30,2015-08-16 14:18:51.000000,2015-08-16 14:50:26.840000 +72,6490,6257,7167,31,2015-08-16 14:18:51.000000,2015-08-16 14:51:02.840000 +72,6501,6257,7167,32,2015-08-16 14:18:51.000000,2015-08-16 14:51:50.840000 +72,4693,6257,7167,33,2015-08-16 14:18:51.000000,2015-08-16 14:52:38.840000 +72,4692,6257,7167,34,2015-08-16 14:18:51.000000,2015-08-16 14:53:26.840000 +72,4690,6257,7167,35,2015-08-16 14:18:51.000000,2015-08-16 14:54:50.840000 +72,10531,6257,7167,36,2015-08-16 14:18:51.000000,2015-08-16 14:55:50.840000 +72,26632,6257,7167,37,2015-08-16 14:18:51.000000,2015-08-16 14:57:02.840000 +72,26624,6257,7167,38,2015-08-16 14:18:51.000000,2015-08-16 14:57:50.840000 +72,7319,6257,7167,39,2015-08-16 14:18:51.000000,2015-08-16 14:58:32.840000 +72,28802,6257,7167,40,2015-08-16 14:18:51.000000,2015-08-16 14:59:38.840000 +72,26557,6257,7167,41,2015-08-16 14:18:51.000000,2015-08-16 15:00:32.840000 +72,6192,6257,7167,42,2015-08-16 14:18:51.000000,2015-08-16 15:01:14.840000 +72,7815,6257,7167,11,2015-08-16 14:19:23.000000,2015-08-16 14:20:58.108000 +72,27249,6257,7167,12,2015-08-16 14:19:23.000000,2015-08-16 14:22:28.108000 +72,6455,6257,7167,13,2015-08-16 14:19:23.000000,2015-08-16 14:25:04.108000 +72,39642,6257,7167,14,2015-08-16 14:19:23.000000,2015-08-16 14:26:46.108000 +72,27196,6257,7167,15,2015-08-16 14:19:23.000000,2015-08-16 14:27:22.108000 +72,5574,6257,7167,16,2015-08-16 14:19:23.000000,2015-08-16 14:30:10.108000 +72,5575,6257,7167,17,2015-08-16 14:19:23.000000,2015-08-16 14:31:04.108000 +72,27061,6257,7167,18,2015-08-16 14:19:23.000000,2015-08-16 14:33:10.108000 +72,5585,6257,7167,19,2015-08-16 14:19:23.000000,2015-08-16 14:34:28.108000 +72,5589,6257,7167,20,2015-08-16 14:19:23.000000,2015-08-16 14:35:40.108000 +72,5587,6257,7167,21,2015-08-16 14:19:23.000000,2015-08-16 14:36:22.108000 +72,6515,6257,7167,22,2015-08-16 14:19:23.000000,2015-08-16 14:37:52.108000 +72,27014,6257,7167,23,2015-08-16 14:19:23.000000,2015-08-16 14:39:40.108000 +72,6512,6257,7167,24,2015-08-16 14:19:23.000000,2015-08-16 14:41:10.108000 +72,6482,6257,7167,25,2015-08-16 14:19:23.000000,2015-08-16 14:42:34.108000 +72,28903,6257,7167,26,2015-08-16 14:19:23.000000,2015-08-16 14:43:58.108000 +72,6508,6257,7167,27,2015-08-16 14:19:23.000000,2015-08-16 14:44:22.108000 +72,26871,6257,7167,28,2015-08-16 14:19:23.000000,2015-08-16 14:46:58.108000 +72,37212,6257,7167,29,2015-08-16 14:19:23.000000,2015-08-16 14:47:58.108000 +72,40419,6257,7167,30,2015-08-16 14:19:23.000000,2015-08-16 14:49:28.108000 +72,6490,6257,7167,31,2015-08-16 14:19:23.000000,2015-08-16 14:50:04.108000 +72,6501,6257,7167,32,2015-08-16 14:19:23.000000,2015-08-16 14:50:52.108000 +72,4693,6257,7167,33,2015-08-16 14:19:23.000000,2015-08-16 14:51:40.108000 +72,4692,6257,7167,34,2015-08-16 14:19:23.000000,2015-08-16 14:52:28.108000 +72,4690,6257,7167,35,2015-08-16 14:19:23.000000,2015-08-16 14:53:52.108000 +72,10531,6257,7167,36,2015-08-16 14:19:23.000000,2015-08-16 14:54:52.108000 +72,26632,6257,7167,37,2015-08-16 14:19:23.000000,2015-08-16 14:56:04.108000 +72,26624,6257,7167,38,2015-08-16 14:19:23.000000,2015-08-16 14:56:52.108000 +72,7319,6257,7167,39,2015-08-16 14:19:23.000000,2015-08-16 14:57:34.108000 +72,28802,6257,7167,40,2015-08-16 14:19:23.000000,2015-08-16 14:58:40.108000 +72,26557,6257,7167,41,2015-08-16 14:19:23.000000,2015-08-16 14:59:34.108000 +72,6192,6257,7167,42,2015-08-16 14:19:23.000000,2015-08-16 15:00:16.108000 +72,28803,6257,7167,43,2015-08-16 14:19:23.000000,2015-08-16 15:03:08.108000 +72,7815,6257,7167,11,2015-08-16 14:19:55.000000,2015-08-16 14:20:46.706000 +72,27249,6257,7167,12,2015-08-16 14:19:55.000000,2015-08-16 14:22:16.706000 +72,6455,6257,7167,13,2015-08-16 14:19:55.000000,2015-08-16 14:24:52.706000 +72,39642,6257,7167,14,2015-08-16 14:19:55.000000,2015-08-16 14:26:34.706000 +72,27196,6257,7167,15,2015-08-16 14:19:55.000000,2015-08-16 14:27:10.706000 +72,5574,6257,7167,16,2015-08-16 14:19:55.000000,2015-08-16 14:29:58.706000 +72,5575,6257,7167,17,2015-08-16 14:19:55.000000,2015-08-16 14:30:52.706000 +72,27061,6257,7167,18,2015-08-16 14:19:55.000000,2015-08-16 14:32:58.706000 +72,5585,6257,7167,19,2015-08-16 14:19:55.000000,2015-08-16 14:34:16.706000 +72,5589,6257,7167,20,2015-08-16 14:19:55.000000,2015-08-16 14:35:28.706000 +72,5587,6257,7167,21,2015-08-16 14:19:55.000000,2015-08-16 14:36:10.706000 +72,6515,6257,7167,22,2015-08-16 14:19:55.000000,2015-08-16 14:37:40.706000 +72,27014,6257,7167,23,2015-08-16 14:19:55.000000,2015-08-16 14:39:28.706000 +72,6512,6257,7167,24,2015-08-16 14:19:55.000000,2015-08-16 14:40:58.706000 +72,6482,6257,7167,25,2015-08-16 14:19:55.000000,2015-08-16 14:42:22.706000 +72,28903,6257,7167,26,2015-08-16 14:19:55.000000,2015-08-16 14:43:46.706000 +72,6508,6257,7167,27,2015-08-16 14:19:55.000000,2015-08-16 14:44:10.706000 +72,26871,6257,7167,28,2015-08-16 14:19:55.000000,2015-08-16 14:46:46.706000 +72,37212,6257,7167,29,2015-08-16 14:19:55.000000,2015-08-16 14:47:46.706000 +72,40419,6257,7167,30,2015-08-16 14:19:55.000000,2015-08-16 14:49:16.706000 +72,6490,6257,7167,31,2015-08-16 14:19:55.000000,2015-08-16 14:49:52.706000 +72,6501,6257,7167,32,2015-08-16 14:19:55.000000,2015-08-16 14:50:40.706000 +72,4693,6257,7167,33,2015-08-16 14:19:55.000000,2015-08-16 14:51:28.706000 +72,4692,6257,7167,34,2015-08-16 14:19:55.000000,2015-08-16 14:52:16.706000 +72,4690,6257,7167,35,2015-08-16 14:19:55.000000,2015-08-16 14:53:40.706000 +72,10531,6257,7167,36,2015-08-16 14:19:55.000000,2015-08-16 14:54:40.706000 +72,26632,6257,7167,37,2015-08-16 14:19:55.000000,2015-08-16 14:55:52.706000 +72,26624,6257,7167,38,2015-08-16 14:19:55.000000,2015-08-16 14:56:40.706000 +72,7319,6257,7167,39,2015-08-16 14:19:55.000000,2015-08-16 14:57:22.706000 +72,28802,6257,7167,40,2015-08-16 14:19:55.000000,2015-08-16 14:58:28.706000 +72,26557,6257,7167,41,2015-08-16 14:19:55.000000,2015-08-16 14:59:22.706000 +72,6192,6257,7167,42,2015-08-16 14:19:55.000000,2015-08-16 15:00:04.706000 +72,28803,6257,7167,43,2015-08-16 14:19:55.000000,2015-08-16 15:02:56.706000 +72,7815,6257,7167,11,2015-08-16 14:20:27.000000,2015-08-16 14:21:18.706000 +72,27249,6257,7167,12,2015-08-16 14:20:27.000000,2015-08-16 14:22:48.706000 +72,6455,6257,7167,13,2015-08-16 14:20:27.000000,2015-08-16 14:25:24.706000 +72,39642,6257,7167,14,2015-08-16 14:20:27.000000,2015-08-16 14:27:06.706000 +72,27196,6257,7167,15,2015-08-16 14:20:27.000000,2015-08-16 14:27:42.706000 +72,5574,6257,7167,16,2015-08-16 14:20:27.000000,2015-08-16 14:30:30.706000 +72,5575,6257,7167,17,2015-08-16 14:20:27.000000,2015-08-16 14:31:24.706000 +72,27061,6257,7167,18,2015-08-16 14:20:27.000000,2015-08-16 14:33:30.706000 +72,5585,6257,7167,19,2015-08-16 14:20:27.000000,2015-08-16 14:34:48.706000 +72,5589,6257,7167,20,2015-08-16 14:20:27.000000,2015-08-16 14:36:00.706000 +72,5587,6257,7167,21,2015-08-16 14:20:27.000000,2015-08-16 14:36:42.706000 +72,6515,6257,7167,22,2015-08-16 14:20:27.000000,2015-08-16 14:38:12.706000 +72,27014,6257,7167,23,2015-08-16 14:20:27.000000,2015-08-16 14:40:00.706000 +72,6512,6257,7167,24,2015-08-16 14:20:27.000000,2015-08-16 14:41:30.706000 +72,6482,6257,7167,25,2015-08-16 14:20:27.000000,2015-08-16 14:42:54.706000 +72,28903,6257,7167,26,2015-08-16 14:20:27.000000,2015-08-16 14:44:18.706000 +72,6508,6257,7167,27,2015-08-16 14:20:27.000000,2015-08-16 14:44:42.706000 +72,26871,6257,7167,28,2015-08-16 14:20:27.000000,2015-08-16 14:47:18.706000 +72,37212,6257,7167,29,2015-08-16 14:20:27.000000,2015-08-16 14:48:18.706000 +72,40419,6257,7167,30,2015-08-16 14:20:27.000000,2015-08-16 14:49:48.706000 +72,6490,6257,7167,31,2015-08-16 14:20:27.000000,2015-08-16 14:50:24.706000 +72,6501,6257,7167,32,2015-08-16 14:20:27.000000,2015-08-16 14:51:12.706000 +72,4693,6257,7167,33,2015-08-16 14:20:27.000000,2015-08-16 14:52:00.706000 +72,4692,6257,7167,34,2015-08-16 14:20:27.000000,2015-08-16 14:52:48.706000 +72,4690,6257,7167,35,2015-08-16 14:20:27.000000,2015-08-16 14:54:12.706000 +72,10531,6257,7167,36,2015-08-16 14:20:27.000000,2015-08-16 14:55:12.706000 +72,26632,6257,7167,37,2015-08-16 14:20:27.000000,2015-08-16 14:56:24.706000 +72,26624,6257,7167,38,2015-08-16 14:20:27.000000,2015-08-16 14:57:12.706000 +72,7319,6257,7167,39,2015-08-16 14:20:27.000000,2015-08-16 14:57:54.706000 +72,28802,6257,7167,40,2015-08-16 14:20:27.000000,2015-08-16 14:59:00.706000 +72,26557,6257,7167,41,2015-08-16 14:20:27.000000,2015-08-16 14:59:54.706000 +72,6192,6257,7167,42,2015-08-16 14:20:27.000000,2015-08-16 15:00:36.706000 +72,28803,6257,7167,43,2015-08-16 14:20:27.000000,2015-08-16 15:03:28.706000 +72,27249,6257,7167,12,2015-08-16 14:21:25.000000,2015-08-16 14:22:37.237000 +72,6455,6257,7167,13,2015-08-16 14:21:25.000000,2015-08-16 14:25:13.237000 +72,39642,6257,7167,14,2015-08-16 14:21:25.000000,2015-08-16 14:26:55.237000 +72,27196,6257,7167,15,2015-08-16 14:21:25.000000,2015-08-16 14:27:31.237000 +72,5574,6257,7167,16,2015-08-16 14:21:25.000000,2015-08-16 14:30:19.237000 +72,5575,6257,7167,17,2015-08-16 14:21:25.000000,2015-08-16 14:31:13.237000 +72,27061,6257,7167,18,2015-08-16 14:21:25.000000,2015-08-16 14:33:19.237000 +72,5585,6257,7167,19,2015-08-16 14:21:25.000000,2015-08-16 14:34:37.237000 +72,5589,6257,7167,20,2015-08-16 14:21:25.000000,2015-08-16 14:35:49.237000 +72,5587,6257,7167,21,2015-08-16 14:21:25.000000,2015-08-16 14:36:31.237000 +72,6515,6257,7167,22,2015-08-16 14:21:25.000000,2015-08-16 14:38:01.237000 +72,27014,6257,7167,23,2015-08-16 14:21:25.000000,2015-08-16 14:39:49.237000 +72,6512,6257,7167,24,2015-08-16 14:21:25.000000,2015-08-16 14:41:19.237000 +72,6482,6257,7167,25,2015-08-16 14:21:25.000000,2015-08-16 14:42:43.237000 +72,28903,6257,7167,26,2015-08-16 14:21:25.000000,2015-08-16 14:44:07.237000 +72,6508,6257,7167,27,2015-08-16 14:21:25.000000,2015-08-16 14:44:31.237000 +72,26871,6257,7167,28,2015-08-16 14:21:25.000000,2015-08-16 14:47:07.237000 +72,37212,6257,7167,29,2015-08-16 14:21:25.000000,2015-08-16 14:48:07.237000 +72,40419,6257,7167,30,2015-08-16 14:21:25.000000,2015-08-16 14:49:37.237000 +72,6490,6257,7167,31,2015-08-16 14:21:25.000000,2015-08-16 14:50:13.237000 +72,6501,6257,7167,32,2015-08-16 14:21:25.000000,2015-08-16 14:51:01.237000 +72,4693,6257,7167,33,2015-08-16 14:21:25.000000,2015-08-16 14:51:49.237000 +72,4692,6257,7167,34,2015-08-16 14:21:25.000000,2015-08-16 14:52:37.237000 +72,4690,6257,7167,35,2015-08-16 14:21:25.000000,2015-08-16 14:54:01.237000 +72,10531,6257,7167,36,2015-08-16 14:21:25.000000,2015-08-16 14:55:01.237000 +72,26632,6257,7167,37,2015-08-16 14:21:25.000000,2015-08-16 14:56:13.237000 +72,26624,6257,7167,38,2015-08-16 14:21:25.000000,2015-08-16 14:57:01.237000 +72,7319,6257,7167,39,2015-08-16 14:21:25.000000,2015-08-16 14:57:43.237000 +72,28802,6257,7167,40,2015-08-16 14:21:25.000000,2015-08-16 14:58:49.237000 +72,26557,6257,7167,41,2015-08-16 14:21:25.000000,2015-08-16 14:59:43.237000 +72,6192,6257,7167,42,2015-08-16 14:21:25.000000,2015-08-16 15:00:25.237000 +72,28803,6257,7167,43,2015-08-16 14:21:25.000000,2015-08-16 15:03:17.237000 +72,6455,6257,7167,13,2015-08-16 14:22:02.000000,2015-08-16 14:24:20.989000 +72,39642,6257,7167,14,2015-08-16 14:22:02.000000,2015-08-16 14:26:02.989000 +72,27196,6257,7167,15,2015-08-16 14:22:02.000000,2015-08-16 14:26:38.989000 +72,5574,6257,7167,16,2015-08-16 14:22:02.000000,2015-08-16 14:29:26.989000 +72,5575,6257,7167,17,2015-08-16 14:22:02.000000,2015-08-16 14:30:20.989000 +72,27061,6257,7167,18,2015-08-16 14:22:02.000000,2015-08-16 14:32:26.989000 +72,5585,6257,7167,19,2015-08-16 14:22:02.000000,2015-08-16 14:33:44.989000 +72,5589,6257,7167,20,2015-08-16 14:22:02.000000,2015-08-16 14:34:56.989000 +72,5587,6257,7167,21,2015-08-16 14:22:02.000000,2015-08-16 14:35:38.989000 +72,6515,6257,7167,22,2015-08-16 14:22:02.000000,2015-08-16 14:37:08.989000 +72,27014,6257,7167,23,2015-08-16 14:22:02.000000,2015-08-16 14:38:56.989000 +72,6512,6257,7167,24,2015-08-16 14:22:02.000000,2015-08-16 14:40:26.989000 +72,6482,6257,7167,25,2015-08-16 14:22:02.000000,2015-08-16 14:41:50.989000 +72,28903,6257,7167,26,2015-08-16 14:22:02.000000,2015-08-16 14:43:14.989000 +72,6508,6257,7167,27,2015-08-16 14:22:02.000000,2015-08-16 14:43:38.989000 +72,26871,6257,7167,28,2015-08-16 14:22:02.000000,2015-08-16 14:46:14.989000 +72,37212,6257,7167,29,2015-08-16 14:22:02.000000,2015-08-16 14:47:14.989000 +72,40419,6257,7167,30,2015-08-16 14:22:02.000000,2015-08-16 14:48:44.989000 +72,6490,6257,7167,31,2015-08-16 14:22:02.000000,2015-08-16 14:49:20.989000 +72,6501,6257,7167,32,2015-08-16 14:22:02.000000,2015-08-16 14:50:08.989000 +72,4693,6257,7167,33,2015-08-16 14:22:02.000000,2015-08-16 14:50:56.989000 +72,4692,6257,7167,34,2015-08-16 14:22:02.000000,2015-08-16 14:51:44.989000 +72,4690,6257,7167,35,2015-08-16 14:22:02.000000,2015-08-16 14:53:08.989000 +72,10531,6257,7167,36,2015-08-16 14:22:02.000000,2015-08-16 14:54:08.989000 +72,26632,6257,7167,37,2015-08-16 14:22:02.000000,2015-08-16 14:55:20.989000 +72,26624,6257,7167,38,2015-08-16 14:22:02.000000,2015-08-16 14:56:08.989000 +72,7319,6257,7167,39,2015-08-16 14:22:02.000000,2015-08-16 14:56:50.989000 +72,28802,6257,7167,40,2015-08-16 14:22:02.000000,2015-08-16 14:57:56.989000 +72,26557,6257,7167,41,2015-08-16 14:22:02.000000,2015-08-16 14:58:50.989000 +72,6192,6257,7167,42,2015-08-16 14:22:02.000000,2015-08-16 14:59:32.989000 +72,28803,6257,7167,43,2015-08-16 14:22:02.000000,2015-08-16 15:02:24.989000 +72,6455,6257,7167,13,2015-08-16 14:22:49.000000,2015-08-16 14:24:59.632000 +72,39642,6257,7167,14,2015-08-16 14:22:49.000000,2015-08-16 14:26:41.632000 +72,27196,6257,7167,15,2015-08-16 14:22:49.000000,2015-08-16 14:27:17.632000 +72,5574,6257,7167,16,2015-08-16 14:22:49.000000,2015-08-16 14:30:05.632000 +72,5575,6257,7167,17,2015-08-16 14:22:49.000000,2015-08-16 14:30:59.632000 +72,27061,6257,7167,18,2015-08-16 14:22:49.000000,2015-08-16 14:33:05.632000 +72,5585,6257,7167,19,2015-08-16 14:22:49.000000,2015-08-16 14:34:23.632000 +72,5589,6257,7167,20,2015-08-16 14:22:49.000000,2015-08-16 14:35:35.632000 +72,5587,6257,7167,21,2015-08-16 14:22:49.000000,2015-08-16 14:36:17.632000 +72,6515,6257,7167,22,2015-08-16 14:22:49.000000,2015-08-16 14:37:47.632000 +72,27014,6257,7167,23,2015-08-16 14:22:49.000000,2015-08-16 14:39:35.632000 +72,6512,6257,7167,24,2015-08-16 14:22:49.000000,2015-08-16 14:41:05.632000 +72,6482,6257,7167,25,2015-08-16 14:22:49.000000,2015-08-16 14:42:29.632000 +72,28903,6257,7167,26,2015-08-16 14:22:49.000000,2015-08-16 14:43:53.632000 +72,6508,6257,7167,27,2015-08-16 14:22:49.000000,2015-08-16 14:44:17.632000 +72,26871,6257,7167,28,2015-08-16 14:22:49.000000,2015-08-16 14:46:53.632000 +72,37212,6257,7167,29,2015-08-16 14:22:49.000000,2015-08-16 14:47:53.632000 +72,40419,6257,7167,30,2015-08-16 14:22:49.000000,2015-08-16 14:49:23.632000 +72,6490,6257,7167,31,2015-08-16 14:22:49.000000,2015-08-16 14:49:59.632000 +72,6501,6257,7167,32,2015-08-16 14:22:49.000000,2015-08-16 14:50:47.632000 +72,4693,6257,7167,33,2015-08-16 14:22:49.000000,2015-08-16 14:51:35.632000 +72,4692,6257,7167,34,2015-08-16 14:22:49.000000,2015-08-16 14:52:23.632000 +72,4690,6257,7167,35,2015-08-16 14:22:49.000000,2015-08-16 14:53:47.632000 +72,10531,6257,7167,36,2015-08-16 14:22:49.000000,2015-08-16 14:54:47.632000 +72,26632,6257,7167,37,2015-08-16 14:22:49.000000,2015-08-16 14:55:59.632000 +72,26624,6257,7167,38,2015-08-16 14:22:49.000000,2015-08-16 14:56:47.632000 +72,7319,6257,7167,39,2015-08-16 14:22:49.000000,2015-08-16 14:57:29.632000 +72,28802,6257,7167,40,2015-08-16 14:22:49.000000,2015-08-16 14:58:35.632000 +72,26557,6257,7167,41,2015-08-16 14:22:49.000000,2015-08-16 14:59:29.632000 +72,6192,6257,7167,42,2015-08-16 14:22:49.000000,2015-08-16 15:00:11.632000 +72,28803,6257,7167,43,2015-08-16 14:22:49.000000,2015-08-16 15:03:03.632000 +72,6455,6257,7167,13,2015-08-16 14:23:23.000000,2015-08-16 14:23:53.631000 +72,39642,6257,7167,14,2015-08-16 14:23:23.000000,2015-08-16 14:25:35.631000 +72,27196,6257,7167,15,2015-08-16 14:23:23.000000,2015-08-16 14:26:11.631000 +72,5574,6257,7167,16,2015-08-16 14:23:23.000000,2015-08-16 14:28:59.631000 +72,5575,6257,7167,17,2015-08-16 14:23:23.000000,2015-08-16 14:29:53.631000 +72,27061,6257,7167,18,2015-08-16 14:23:23.000000,2015-08-16 14:31:59.631000 +72,5585,6257,7167,19,2015-08-16 14:23:23.000000,2015-08-16 14:33:17.631000 +72,5589,6257,7167,20,2015-08-16 14:23:23.000000,2015-08-16 14:34:29.631000 +72,5587,6257,7167,21,2015-08-16 14:23:23.000000,2015-08-16 14:35:11.631000 +72,6515,6257,7167,22,2015-08-16 14:23:23.000000,2015-08-16 14:36:41.631000 +72,27014,6257,7167,23,2015-08-16 14:23:23.000000,2015-08-16 14:38:29.631000 +72,6512,6257,7167,24,2015-08-16 14:23:23.000000,2015-08-16 14:39:59.631000 +72,6482,6257,7167,25,2015-08-16 14:23:23.000000,2015-08-16 14:41:23.631000 +72,28903,6257,7167,26,2015-08-16 14:23:23.000000,2015-08-16 14:42:47.631000 +72,6508,6257,7167,27,2015-08-16 14:23:23.000000,2015-08-16 14:43:11.631000 +72,26871,6257,7167,28,2015-08-16 14:23:23.000000,2015-08-16 14:45:47.631000 +72,37212,6257,7167,29,2015-08-16 14:23:23.000000,2015-08-16 14:46:47.631000 +72,40419,6257,7167,30,2015-08-16 14:23:23.000000,2015-08-16 14:48:17.631000 +72,6490,6257,7167,31,2015-08-16 14:23:23.000000,2015-08-16 14:48:53.631000 +72,6501,6257,7167,32,2015-08-16 14:23:23.000000,2015-08-16 14:49:41.631000 +72,4693,6257,7167,33,2015-08-16 14:23:23.000000,2015-08-16 14:50:29.631000 +72,4692,6257,7167,34,2015-08-16 14:23:23.000000,2015-08-16 14:51:17.631000 +72,4690,6257,7167,35,2015-08-16 14:23:23.000000,2015-08-16 14:52:41.631000 +72,10531,6257,7167,36,2015-08-16 14:23:23.000000,2015-08-16 14:53:41.631000 +72,26632,6257,7167,37,2015-08-16 14:23:23.000000,2015-08-16 14:54:53.631000 +72,26624,6257,7167,38,2015-08-16 14:23:23.000000,2015-08-16 14:55:41.631000 +72,7319,6257,7167,39,2015-08-16 14:23:23.000000,2015-08-16 14:56:23.631000 +72,28802,6257,7167,40,2015-08-16 14:23:23.000000,2015-08-16 14:57:29.631000 +72,26557,6257,7167,41,2015-08-16 14:23:23.000000,2015-08-16 14:58:23.631000 +72,6192,6257,7167,42,2015-08-16 14:23:23.000000,2015-08-16 14:59:05.631000 +72,28803,6257,7167,43,2015-08-16 14:23:23.000000,2015-08-16 15:01:57.631000 +72,39642,6257,7167,14,2015-08-16 14:24:49.000000,2015-08-16 14:25:05.210000 +72,27196,6257,7167,15,2015-08-16 14:24:49.000000,2015-08-16 14:25:41.210000 +72,5574,6257,7167,16,2015-08-16 14:24:49.000000,2015-08-16 14:28:29.210000 +72,5575,6257,7167,17,2015-08-16 14:24:49.000000,2015-08-16 14:29:23.210000 +72,27061,6257,7167,18,2015-08-16 14:24:49.000000,2015-08-16 14:31:29.210000 +72,5585,6257,7167,19,2015-08-16 14:24:49.000000,2015-08-16 14:32:47.210000 +72,5589,6257,7167,20,2015-08-16 14:24:49.000000,2015-08-16 14:33:59.210000 +72,5587,6257,7167,21,2015-08-16 14:24:49.000000,2015-08-16 14:34:41.210000 +72,6515,6257,7167,22,2015-08-16 14:24:49.000000,2015-08-16 14:36:11.210000 +72,27014,6257,7167,23,2015-08-16 14:24:49.000000,2015-08-16 14:37:59.210000 +72,6512,6257,7167,24,2015-08-16 14:24:49.000000,2015-08-16 14:39:29.210000 +72,6482,6257,7167,25,2015-08-16 14:24:49.000000,2015-08-16 14:40:53.210000 +72,28903,6257,7167,26,2015-08-16 14:24:49.000000,2015-08-16 14:42:17.210000 +72,6508,6257,7167,27,2015-08-16 14:24:49.000000,2015-08-16 14:42:41.210000 +72,26871,6257,7167,28,2015-08-16 14:24:49.000000,2015-08-16 14:45:17.210000 +72,37212,6257,7167,29,2015-08-16 14:24:49.000000,2015-08-16 14:46:17.210000 +72,40419,6257,7167,30,2015-08-16 14:24:49.000000,2015-08-16 14:47:47.210000 +72,6490,6257,7167,31,2015-08-16 14:24:49.000000,2015-08-16 14:48:23.210000 +72,6501,6257,7167,32,2015-08-16 14:24:49.000000,2015-08-16 14:49:11.210000 +72,4693,6257,7167,33,2015-08-16 14:24:49.000000,2015-08-16 14:49:59.210000 +72,4692,6257,7167,34,2015-08-16 14:24:49.000000,2015-08-16 14:50:47.210000 +72,4690,6257,7167,35,2015-08-16 14:24:49.000000,2015-08-16 14:52:11.210000 +72,10531,6257,7167,36,2015-08-16 14:24:49.000000,2015-08-16 14:53:11.210000 +72,26632,6257,7167,37,2015-08-16 14:24:49.000000,2015-08-16 14:54:23.210000 +72,26624,6257,7167,38,2015-08-16 14:24:49.000000,2015-08-16 14:55:11.210000 +72,7319,6257,7167,39,2015-08-16 14:24:49.000000,2015-08-16 14:55:53.210000 +72,28802,6257,7167,40,2015-08-16 14:24:49.000000,2015-08-16 14:56:59.210000 +72,26557,6257,7167,41,2015-08-16 14:24:49.000000,2015-08-16 14:57:53.210000 +72,6192,6257,7167,42,2015-08-16 14:24:49.000000,2015-08-16 14:58:35.210000 +72,28803,6257,7167,43,2015-08-16 14:24:49.000000,2015-08-16 15:01:27.210000 +72,27196,6257,7167,15,2015-08-16 14:25:21.000000,2015-08-16 14:25:46.954000 +72,5574,6257,7167,16,2015-08-16 14:25:21.000000,2015-08-16 14:28:34.954000 +72,5575,6257,7167,17,2015-08-16 14:25:21.000000,2015-08-16 14:29:28.954000 +72,27061,6257,7167,18,2015-08-16 14:25:21.000000,2015-08-16 14:31:34.954000 +72,5585,6257,7167,19,2015-08-16 14:25:21.000000,2015-08-16 14:32:52.954000 +72,5589,6257,7167,20,2015-08-16 14:25:21.000000,2015-08-16 14:34:04.954000 +72,5587,6257,7167,21,2015-08-16 14:25:21.000000,2015-08-16 14:34:46.954000 +72,6515,6257,7167,22,2015-08-16 14:25:21.000000,2015-08-16 14:36:16.954000 +72,27014,6257,7167,23,2015-08-16 14:25:21.000000,2015-08-16 14:38:04.954000 +72,6512,6257,7167,24,2015-08-16 14:25:21.000000,2015-08-16 14:39:34.954000 +72,6482,6257,7167,25,2015-08-16 14:25:21.000000,2015-08-16 14:40:58.954000 +72,28903,6257,7167,26,2015-08-16 14:25:21.000000,2015-08-16 14:42:22.954000 +72,6508,6257,7167,27,2015-08-16 14:25:21.000000,2015-08-16 14:42:46.954000 +72,26871,6257,7167,28,2015-08-16 14:25:21.000000,2015-08-16 14:45:22.954000 +72,37212,6257,7167,29,2015-08-16 14:25:21.000000,2015-08-16 14:46:22.954000 +72,40419,6257,7167,30,2015-08-16 14:25:21.000000,2015-08-16 14:47:52.954000 +72,6490,6257,7167,31,2015-08-16 14:25:21.000000,2015-08-16 14:48:28.954000 +72,6501,6257,7167,32,2015-08-16 14:25:21.000000,2015-08-16 14:49:16.954000 +72,4693,6257,7167,33,2015-08-16 14:25:21.000000,2015-08-16 14:50:04.954000 +72,4692,6257,7167,34,2015-08-16 14:25:21.000000,2015-08-16 14:50:52.954000 +72,4690,6257,7167,35,2015-08-16 14:25:21.000000,2015-08-16 14:52:16.954000 +72,10531,6257,7167,36,2015-08-16 14:25:21.000000,2015-08-16 14:53:16.954000 +72,26632,6257,7167,37,2015-08-16 14:25:21.000000,2015-08-16 14:54:28.954000 +72,26624,6257,7167,38,2015-08-16 14:25:21.000000,2015-08-16 14:55:16.954000 +72,7319,6257,7167,39,2015-08-16 14:25:21.000000,2015-08-16 14:55:58.954000 +72,28802,6257,7167,40,2015-08-16 14:25:21.000000,2015-08-16 14:57:04.954000 +72,26557,6257,7167,41,2015-08-16 14:25:21.000000,2015-08-16 14:57:58.954000 +72,6192,6257,7167,42,2015-08-16 14:25:21.000000,2015-08-16 14:58:40.954000 +72,28803,6257,7167,43,2015-08-16 14:25:21.000000,2015-08-16 15:01:32.954000 +72,27196,6257,7167,15,2015-08-16 14:26:25.000000,2015-08-16 14:26:50.954000 +72,5574,6257,7167,16,2015-08-16 14:26:25.000000,2015-08-16 14:29:38.954000 +72,5575,6257,7167,17,2015-08-16 14:26:25.000000,2015-08-16 14:30:32.954000 +72,27061,6257,7167,18,2015-08-16 14:26:25.000000,2015-08-16 14:32:38.954000 +72,5585,6257,7167,19,2015-08-16 14:26:25.000000,2015-08-16 14:33:56.954000 +72,5589,6257,7167,20,2015-08-16 14:26:25.000000,2015-08-16 14:35:08.954000 +72,5587,6257,7167,21,2015-08-16 14:26:25.000000,2015-08-16 14:35:50.954000 +72,6515,6257,7167,22,2015-08-16 14:26:25.000000,2015-08-16 14:37:20.954000 +72,27014,6257,7167,23,2015-08-16 14:26:25.000000,2015-08-16 14:39:08.954000 +72,6512,6257,7167,24,2015-08-16 14:26:25.000000,2015-08-16 14:40:38.954000 +72,6482,6257,7167,25,2015-08-16 14:26:25.000000,2015-08-16 14:42:02.954000 +72,28903,6257,7167,26,2015-08-16 14:26:25.000000,2015-08-16 14:43:26.954000 +72,6508,6257,7167,27,2015-08-16 14:26:25.000000,2015-08-16 14:43:50.954000 +72,26871,6257,7167,28,2015-08-16 14:26:25.000000,2015-08-16 14:46:26.954000 +72,37212,6257,7167,29,2015-08-16 14:26:25.000000,2015-08-16 14:47:26.954000 +72,40419,6257,7167,30,2015-08-16 14:26:25.000000,2015-08-16 14:48:56.954000 +72,6490,6257,7167,31,2015-08-16 14:26:25.000000,2015-08-16 14:49:32.954000 +72,6501,6257,7167,32,2015-08-16 14:26:25.000000,2015-08-16 14:50:20.954000 +72,4693,6257,7167,33,2015-08-16 14:26:25.000000,2015-08-16 14:51:08.954000 +72,4692,6257,7167,34,2015-08-16 14:26:25.000000,2015-08-16 14:51:56.954000 +72,4690,6257,7167,35,2015-08-16 14:26:25.000000,2015-08-16 14:53:20.954000 +72,10531,6257,7167,36,2015-08-16 14:26:25.000000,2015-08-16 14:54:20.954000 +72,26632,6257,7167,37,2015-08-16 14:26:25.000000,2015-08-16 14:55:32.954000 +72,26624,6257,7167,38,2015-08-16 14:26:25.000000,2015-08-16 14:56:20.954000 +72,7319,6257,7167,39,2015-08-16 14:26:25.000000,2015-08-16 14:57:02.954000 +72,28802,6257,7167,40,2015-08-16 14:26:25.000000,2015-08-16 14:58:08.954000 +72,26557,6257,7167,41,2015-08-16 14:26:25.000000,2015-08-16 14:59:02.954000 +72,6192,6257,7167,42,2015-08-16 14:26:25.000000,2015-08-16 14:59:44.954000 +72,28803,6257,7167,43,2015-08-16 14:26:25.000000,2015-08-16 15:02:36.954000 +72,27196,6257,7167,15,2015-08-16 14:26:55.000000,2015-08-16 14:26:56.714000 +72,5574,6257,7167,16,2015-08-16 14:26:55.000000,2015-08-16 14:29:44.714000 +72,5575,6257,7167,17,2015-08-16 14:26:55.000000,2015-08-16 14:30:38.714000 +72,27061,6257,7167,18,2015-08-16 14:26:55.000000,2015-08-16 14:32:44.714000 +72,5585,6257,7167,19,2015-08-16 14:26:55.000000,2015-08-16 14:34:02.714000 +72,5589,6257,7167,20,2015-08-16 14:26:55.000000,2015-08-16 14:35:14.714000 +72,5587,6257,7167,21,2015-08-16 14:26:55.000000,2015-08-16 14:35:56.714000 +72,6515,6257,7167,22,2015-08-16 14:26:55.000000,2015-08-16 14:37:26.714000 +72,27014,6257,7167,23,2015-08-16 14:26:55.000000,2015-08-16 14:39:14.714000 +72,6512,6257,7167,24,2015-08-16 14:26:55.000000,2015-08-16 14:40:44.714000 +72,6482,6257,7167,25,2015-08-16 14:26:55.000000,2015-08-16 14:42:08.714000 +72,28903,6257,7167,26,2015-08-16 14:26:55.000000,2015-08-16 14:43:32.714000 +72,6508,6257,7167,27,2015-08-16 14:26:55.000000,2015-08-16 14:43:56.714000 +72,26871,6257,7167,28,2015-08-16 14:26:55.000000,2015-08-16 14:46:32.714000 +72,37212,6257,7167,29,2015-08-16 14:26:55.000000,2015-08-16 14:47:32.714000 +72,40419,6257,7167,30,2015-08-16 14:26:55.000000,2015-08-16 14:49:02.714000 +72,6490,6257,7167,31,2015-08-16 14:26:55.000000,2015-08-16 14:49:38.714000 +72,6501,6257,7167,32,2015-08-16 14:26:55.000000,2015-08-16 14:50:26.714000 +72,4693,6257,7167,33,2015-08-16 14:26:55.000000,2015-08-16 14:51:14.714000 +72,4692,6257,7167,34,2015-08-16 14:26:55.000000,2015-08-16 14:52:02.714000 +72,4690,6257,7167,35,2015-08-16 14:26:55.000000,2015-08-16 14:53:26.714000 +72,10531,6257,7167,36,2015-08-16 14:26:55.000000,2015-08-16 14:54:26.714000 +72,26632,6257,7167,37,2015-08-16 14:26:55.000000,2015-08-16 14:55:38.714000 +72,26624,6257,7167,38,2015-08-16 14:26:55.000000,2015-08-16 14:56:26.714000 +72,7319,6257,7167,39,2015-08-16 14:26:55.000000,2015-08-16 14:57:08.714000 +72,28802,6257,7167,40,2015-08-16 14:26:55.000000,2015-08-16 14:58:14.714000 +72,26557,6257,7167,41,2015-08-16 14:26:55.000000,2015-08-16 14:59:08.714000 +72,6192,6257,7167,42,2015-08-16 14:26:55.000000,2015-08-16 14:59:50.714000 +72,28803,6257,7167,43,2015-08-16 14:26:55.000000,2015-08-16 15:02:42.714000 +72,5574,6257,7167,16,2015-08-16 14:28:00.000000,2015-08-16 14:29:08.191000 +72,5575,6257,7167,17,2015-08-16 14:28:00.000000,2015-08-16 14:30:02.191000 +72,27061,6257,7167,18,2015-08-16 14:28:00.000000,2015-08-16 14:32:08.191000 +72,5585,6257,7167,19,2015-08-16 14:28:00.000000,2015-08-16 14:33:26.191000 +72,5589,6257,7167,20,2015-08-16 14:28:00.000000,2015-08-16 14:34:38.191000 +72,5587,6257,7167,21,2015-08-16 14:28:00.000000,2015-08-16 14:35:20.191000 +72,6515,6257,7167,22,2015-08-16 14:28:00.000000,2015-08-16 14:36:50.191000 +72,27014,6257,7167,23,2015-08-16 14:28:00.000000,2015-08-16 14:38:38.191000 +72,6512,6257,7167,24,2015-08-16 14:28:00.000000,2015-08-16 14:40:08.191000 +72,6482,6257,7167,25,2015-08-16 14:28:00.000000,2015-08-16 14:41:32.191000 +72,28903,6257,7167,26,2015-08-16 14:28:00.000000,2015-08-16 14:42:56.191000 +72,6508,6257,7167,27,2015-08-16 14:28:00.000000,2015-08-16 14:43:20.191000 +72,26871,6257,7167,28,2015-08-16 14:28:00.000000,2015-08-16 14:45:56.191000 +72,37212,6257,7167,29,2015-08-16 14:28:00.000000,2015-08-16 14:46:56.191000 +72,40419,6257,7167,30,2015-08-16 14:28:00.000000,2015-08-16 14:48:26.191000 +72,6490,6257,7167,31,2015-08-16 14:28:00.000000,2015-08-16 14:49:02.191000 +72,6501,6257,7167,32,2015-08-16 14:28:00.000000,2015-08-16 14:49:50.191000 +72,4693,6257,7167,33,2015-08-16 14:28:00.000000,2015-08-16 14:50:38.191000 +72,4692,6257,7167,34,2015-08-16 14:28:00.000000,2015-08-16 14:51:26.191000 +72,4690,6257,7167,35,2015-08-16 14:28:00.000000,2015-08-16 14:52:50.191000 +72,10531,6257,7167,36,2015-08-16 14:28:00.000000,2015-08-16 14:53:50.191000 +72,26632,6257,7167,37,2015-08-16 14:28:00.000000,2015-08-16 14:55:02.191000 +72,26624,6257,7167,38,2015-08-16 14:28:00.000000,2015-08-16 14:55:50.191000 +72,7319,6257,7167,39,2015-08-16 14:28:00.000000,2015-08-16 14:56:32.191000 +72,28802,6257,7167,40,2015-08-16 14:28:00.000000,2015-08-16 14:57:38.191000 +72,26557,6257,7167,41,2015-08-16 14:28:00.000000,2015-08-16 14:58:32.191000 +72,6192,6257,7167,42,2015-08-16 14:28:00.000000,2015-08-16 14:59:14.191000 +72,28803,6257,7167,43,2015-08-16 14:28:00.000000,2015-08-16 15:02:06.191000 +72,5575,6257,7167,17,2015-08-16 14:28:58.000000,2015-08-16 14:29:36.549000 +72,27061,6257,7167,18,2015-08-16 14:28:58.000000,2015-08-16 14:31:42.549000 +72,5585,6257,7167,19,2015-08-16 14:28:58.000000,2015-08-16 14:33:00.549000 +72,5589,6257,7167,20,2015-08-16 14:28:58.000000,2015-08-16 14:34:12.549000 +72,5587,6257,7167,21,2015-08-16 14:28:58.000000,2015-08-16 14:34:54.549000 +72,6515,6257,7167,22,2015-08-16 14:28:58.000000,2015-08-16 14:36:24.549000 +72,27014,6257,7167,23,2015-08-16 14:28:58.000000,2015-08-16 14:38:12.549000 +72,6512,6257,7167,24,2015-08-16 14:28:58.000000,2015-08-16 14:39:42.549000 +72,6482,6257,7167,25,2015-08-16 14:28:58.000000,2015-08-16 14:41:06.549000 +72,28903,6257,7167,26,2015-08-16 14:28:58.000000,2015-08-16 14:42:30.549000 +72,6508,6257,7167,27,2015-08-16 14:28:58.000000,2015-08-16 14:42:54.549000 +72,26871,6257,7167,28,2015-08-16 14:28:58.000000,2015-08-16 14:45:30.549000 +72,37212,6257,7167,29,2015-08-16 14:28:58.000000,2015-08-16 14:46:30.549000 +72,40419,6257,7167,30,2015-08-16 14:28:58.000000,2015-08-16 14:48:00.549000 +72,6490,6257,7167,31,2015-08-16 14:28:58.000000,2015-08-16 14:48:36.549000 +72,6501,6257,7167,32,2015-08-16 14:28:58.000000,2015-08-16 14:49:24.549000 +72,4693,6257,7167,33,2015-08-16 14:28:58.000000,2015-08-16 14:50:12.549000 +72,4692,6257,7167,34,2015-08-16 14:28:58.000000,2015-08-16 14:51:00.549000 +72,4690,6257,7167,35,2015-08-16 14:28:58.000000,2015-08-16 14:52:24.549000 +72,10531,6257,7167,36,2015-08-16 14:28:58.000000,2015-08-16 14:53:24.549000 +72,26632,6257,7167,37,2015-08-16 14:28:58.000000,2015-08-16 14:54:36.549000 +72,26624,6257,7167,38,2015-08-16 14:28:58.000000,2015-08-16 14:55:24.549000 +72,7319,6257,7167,39,2015-08-16 14:28:58.000000,2015-08-16 14:56:06.549000 +72,28802,6257,7167,40,2015-08-16 14:28:58.000000,2015-08-16 14:57:12.549000 +72,26557,6257,7167,41,2015-08-16 14:28:58.000000,2015-08-16 14:58:06.549000 +72,6192,6257,7167,42,2015-08-16 14:28:58.000000,2015-08-16 14:58:48.549000 +72,28803,6257,7167,43,2015-08-16 14:28:58.000000,2015-08-16 15:01:40.549000 +72,27061,6257,7167,18,2015-08-16 14:29:31.000000,2015-08-16 14:30:51.006000 +72,5585,6257,7167,19,2015-08-16 14:29:31.000000,2015-08-16 14:32:09.006000 +72,5589,6257,7167,20,2015-08-16 14:29:31.000000,2015-08-16 14:33:21.006000 +72,5587,6257,7167,21,2015-08-16 14:29:31.000000,2015-08-16 14:34:03.006000 +72,6515,6257,7167,22,2015-08-16 14:29:31.000000,2015-08-16 14:35:33.006000 +72,27014,6257,7167,23,2015-08-16 14:29:31.000000,2015-08-16 14:37:21.006000 +72,6512,6257,7167,24,2015-08-16 14:29:31.000000,2015-08-16 14:38:51.006000 +72,6482,6257,7167,25,2015-08-16 14:29:31.000000,2015-08-16 14:40:15.006000 +72,28903,6257,7167,26,2015-08-16 14:29:31.000000,2015-08-16 14:41:39.006000 +72,6508,6257,7167,27,2015-08-16 14:29:31.000000,2015-08-16 14:42:03.006000 +72,26871,6257,7167,28,2015-08-16 14:29:31.000000,2015-08-16 14:44:39.006000 +72,37212,6257,7167,29,2015-08-16 14:29:31.000000,2015-08-16 14:45:39.006000 +72,40419,6257,7167,30,2015-08-16 14:29:31.000000,2015-08-16 14:47:09.006000 +72,6490,6257,7167,31,2015-08-16 14:29:31.000000,2015-08-16 14:47:45.006000 +72,6501,6257,7167,32,2015-08-16 14:29:31.000000,2015-08-16 14:48:33.006000 +72,4693,6257,7167,33,2015-08-16 14:29:31.000000,2015-08-16 14:49:21.006000 +72,4692,6257,7167,34,2015-08-16 14:29:31.000000,2015-08-16 14:50:09.006000 +72,4690,6257,7167,35,2015-08-16 14:29:31.000000,2015-08-16 14:51:33.006000 +72,10531,6257,7167,36,2015-08-16 14:29:31.000000,2015-08-16 14:52:33.006000 +72,26632,6257,7167,37,2015-08-16 14:29:31.000000,2015-08-16 14:53:45.006000 +72,26624,6257,7167,38,2015-08-16 14:29:31.000000,2015-08-16 14:54:33.006000 +72,7319,6257,7167,39,2015-08-16 14:29:31.000000,2015-08-16 14:55:15.006000 +72,28802,6257,7167,40,2015-08-16 14:29:31.000000,2015-08-16 14:56:21.006000 +72,26557,6257,7167,41,2015-08-16 14:29:31.000000,2015-08-16 14:57:15.006000 +72,6192,6257,7167,42,2015-08-16 14:29:31.000000,2015-08-16 14:57:57.006000 +72,28803,6257,7167,43,2015-08-16 14:29:31.000000,2015-08-16 15:00:49.006000 +72,27061,6257,7167,18,2015-08-16 14:30:34.000000,2015-08-16 14:31:11.025000 +72,5585,6257,7167,19,2015-08-16 14:30:34.000000,2015-08-16 14:32:29.025000 +72,5589,6257,7167,20,2015-08-16 14:30:34.000000,2015-08-16 14:33:41.025000 +72,5587,6257,7167,21,2015-08-16 14:30:34.000000,2015-08-16 14:34:23.025000 +72,6515,6257,7167,22,2015-08-16 14:30:34.000000,2015-08-16 14:35:53.025000 +72,27014,6257,7167,23,2015-08-16 14:30:34.000000,2015-08-16 14:37:41.025000 +72,6512,6257,7167,24,2015-08-16 14:30:34.000000,2015-08-16 14:39:11.025000 +72,6482,6257,7167,25,2015-08-16 14:30:34.000000,2015-08-16 14:40:35.025000 +72,28903,6257,7167,26,2015-08-16 14:30:34.000000,2015-08-16 14:41:59.025000 +72,6508,6257,7167,27,2015-08-16 14:30:34.000000,2015-08-16 14:42:23.025000 +72,26871,6257,7167,28,2015-08-16 14:30:34.000000,2015-08-16 14:44:59.025000 +72,37212,6257,7167,29,2015-08-16 14:30:34.000000,2015-08-16 14:45:59.025000 +72,40419,6257,7167,30,2015-08-16 14:30:34.000000,2015-08-16 14:47:29.025000 +72,6490,6257,7167,31,2015-08-16 14:30:34.000000,2015-08-16 14:48:05.025000 +72,6501,6257,7167,32,2015-08-16 14:30:34.000000,2015-08-16 14:48:53.025000 +72,4693,6257,7167,33,2015-08-16 14:30:34.000000,2015-08-16 14:49:41.025000 +72,4692,6257,7167,34,2015-08-16 14:30:34.000000,2015-08-16 14:50:29.025000 +72,4690,6257,7167,35,2015-08-16 14:30:34.000000,2015-08-16 14:51:53.025000 +72,10531,6257,7167,36,2015-08-16 14:30:34.000000,2015-08-16 14:52:53.025000 +72,26632,6257,7167,37,2015-08-16 14:30:34.000000,2015-08-16 14:54:05.025000 +72,26624,6257,7167,38,2015-08-16 14:30:34.000000,2015-08-16 14:54:53.025000 +72,7319,6257,7167,39,2015-08-16 14:30:34.000000,2015-08-16 14:55:35.025000 +72,28802,6257,7167,40,2015-08-16 14:30:34.000000,2015-08-16 14:56:41.025000 +72,26557,6257,7167,41,2015-08-16 14:30:34.000000,2015-08-16 14:57:35.025000 +72,6192,6257,7167,42,2015-08-16 14:30:34.000000,2015-08-16 14:58:17.025000 +72,28803,6257,7167,43,2015-08-16 14:30:34.000000,2015-08-16 15:01:09.025000 +72,5585,6257,7167,19,2015-08-16 14:31:17.000000,2015-08-16 14:32:18.182000 +72,5589,6257,7167,20,2015-08-16 14:31:17.000000,2015-08-16 14:33:30.182000 +72,5587,6257,7167,21,2015-08-16 14:31:17.000000,2015-08-16 14:34:12.182000 +72,6515,6257,7167,22,2015-08-16 14:31:17.000000,2015-08-16 14:35:42.182000 +72,27014,6257,7167,23,2015-08-16 14:31:17.000000,2015-08-16 14:37:30.182000 +72,6512,6257,7167,24,2015-08-16 14:31:17.000000,2015-08-16 14:39:00.182000 +72,6482,6257,7167,25,2015-08-16 14:31:17.000000,2015-08-16 14:40:24.182000 +72,28903,6257,7167,26,2015-08-16 14:31:17.000000,2015-08-16 14:41:48.182000 +72,6508,6257,7167,27,2015-08-16 14:31:17.000000,2015-08-16 14:42:12.182000 +72,26871,6257,7167,28,2015-08-16 14:31:17.000000,2015-08-16 14:44:48.182000 +72,37212,6257,7167,29,2015-08-16 14:31:17.000000,2015-08-16 14:45:48.182000 +72,40419,6257,7167,30,2015-08-16 14:31:17.000000,2015-08-16 14:47:18.182000 +72,6490,6257,7167,31,2015-08-16 14:31:17.000000,2015-08-16 14:47:54.182000 +72,6501,6257,7167,32,2015-08-16 14:31:17.000000,2015-08-16 14:48:42.182000 +72,4693,6257,7167,33,2015-08-16 14:31:17.000000,2015-08-16 14:49:30.182000 +72,4692,6257,7167,34,2015-08-16 14:31:17.000000,2015-08-16 14:50:18.182000 +72,4690,6257,7167,35,2015-08-16 14:31:17.000000,2015-08-16 14:51:42.182000 +72,10531,6257,7167,36,2015-08-16 14:31:17.000000,2015-08-16 14:52:42.182000 +72,26632,6257,7167,37,2015-08-16 14:31:17.000000,2015-08-16 14:53:54.182000 +72,26624,6257,7167,38,2015-08-16 14:31:17.000000,2015-08-16 14:54:42.182000 +72,7319,6257,7167,39,2015-08-16 14:31:17.000000,2015-08-16 14:55:24.182000 +72,28802,6257,7167,40,2015-08-16 14:31:17.000000,2015-08-16 14:56:30.182000 +72,26557,6257,7167,41,2015-08-16 14:31:17.000000,2015-08-16 14:57:24.182000 +72,6192,6257,7167,42,2015-08-16 14:31:17.000000,2015-08-16 14:58:06.182000 +72,28803,6257,7167,43,2015-08-16 14:31:17.000000,2015-08-16 15:00:58.182000 +72,5585,6257,7167,19,2015-08-16 14:31:50.000000,2015-08-16 14:32:01.862000 +72,5589,6257,7167,20,2015-08-16 14:31:50.000000,2015-08-16 14:33:13.862000 +72,5587,6257,7167,21,2015-08-16 14:31:50.000000,2015-08-16 14:33:55.862000 +72,6515,6257,7167,22,2015-08-16 14:31:50.000000,2015-08-16 14:35:25.862000 +72,27014,6257,7167,23,2015-08-16 14:31:50.000000,2015-08-16 14:37:13.862000 +72,6512,6257,7167,24,2015-08-16 14:31:50.000000,2015-08-16 14:38:43.862000 +72,6482,6257,7167,25,2015-08-16 14:31:50.000000,2015-08-16 14:40:07.862000 +72,28903,6257,7167,26,2015-08-16 14:31:50.000000,2015-08-16 14:41:31.862000 +72,6508,6257,7167,27,2015-08-16 14:31:50.000000,2015-08-16 14:41:55.862000 +72,26871,6257,7167,28,2015-08-16 14:31:50.000000,2015-08-16 14:44:31.862000 +72,37212,6257,7167,29,2015-08-16 14:31:50.000000,2015-08-16 14:45:31.862000 +72,40419,6257,7167,30,2015-08-16 14:31:50.000000,2015-08-16 14:47:01.862000 +72,6490,6257,7167,31,2015-08-16 14:31:50.000000,2015-08-16 14:47:37.862000 +72,6501,6257,7167,32,2015-08-16 14:31:50.000000,2015-08-16 14:48:25.862000 +72,4693,6257,7167,33,2015-08-16 14:31:50.000000,2015-08-16 14:49:13.862000 +72,4692,6257,7167,34,2015-08-16 14:31:50.000000,2015-08-16 14:50:01.862000 +72,4690,6257,7167,35,2015-08-16 14:31:50.000000,2015-08-16 14:51:25.862000 +72,10531,6257,7167,36,2015-08-16 14:31:50.000000,2015-08-16 14:52:25.862000 +72,26632,6257,7167,37,2015-08-16 14:31:50.000000,2015-08-16 14:53:37.862000 +72,26624,6257,7167,38,2015-08-16 14:31:50.000000,2015-08-16 14:54:25.862000 +72,7319,6257,7167,39,2015-08-16 14:31:50.000000,2015-08-16 14:55:07.862000 +72,28802,6257,7167,40,2015-08-16 14:31:50.000000,2015-08-16 14:56:13.862000 +72,26557,6257,7167,41,2015-08-16 14:31:50.000000,2015-08-16 14:57:07.862000 +72,6192,6257,7167,42,2015-08-16 14:31:50.000000,2015-08-16 14:57:49.862000 +72,28803,6257,7167,43,2015-08-16 14:31:50.000000,2015-08-16 15:00:41.862000 +72,5589,6257,7167,20,2015-08-16 14:32:44.000000,2015-08-16 14:32:49.638000 +72,5587,6257,7167,21,2015-08-16 14:32:44.000000,2015-08-16 14:33:31.638000 +72,6515,6257,7167,22,2015-08-16 14:32:44.000000,2015-08-16 14:35:01.638000 +72,27014,6257,7167,23,2015-08-16 14:32:44.000000,2015-08-16 14:36:49.638000 +72,6512,6257,7167,24,2015-08-16 14:32:44.000000,2015-08-16 14:38:19.638000 +72,6482,6257,7167,25,2015-08-16 14:32:44.000000,2015-08-16 14:39:43.638000 +72,28903,6257,7167,26,2015-08-16 14:32:44.000000,2015-08-16 14:41:07.638000 +72,6508,6257,7167,27,2015-08-16 14:32:44.000000,2015-08-16 14:41:31.638000 +72,26871,6257,7167,28,2015-08-16 14:32:44.000000,2015-08-16 14:44:07.638000 +72,37212,6257,7167,29,2015-08-16 14:32:44.000000,2015-08-16 14:45:07.638000 +72,40419,6257,7167,30,2015-08-16 14:32:44.000000,2015-08-16 14:46:37.638000 +72,6490,6257,7167,31,2015-08-16 14:32:44.000000,2015-08-16 14:47:13.638000 +72,6501,6257,7167,32,2015-08-16 14:32:44.000000,2015-08-16 14:48:01.638000 +72,4693,6257,7167,33,2015-08-16 14:32:44.000000,2015-08-16 14:48:49.638000 +72,4692,6257,7167,34,2015-08-16 14:32:44.000000,2015-08-16 14:49:37.638000 +72,4690,6257,7167,35,2015-08-16 14:32:44.000000,2015-08-16 14:51:01.638000 +72,10531,6257,7167,36,2015-08-16 14:32:44.000000,2015-08-16 14:52:01.638000 +72,26632,6257,7167,37,2015-08-16 14:32:44.000000,2015-08-16 14:53:13.638000 +72,26624,6257,7167,38,2015-08-16 14:32:44.000000,2015-08-16 14:54:01.638000 +72,7319,6257,7167,39,2015-08-16 14:32:44.000000,2015-08-16 14:54:43.638000 +72,28802,6257,7167,40,2015-08-16 14:32:44.000000,2015-08-16 14:55:49.638000 +72,26557,6257,7167,41,2015-08-16 14:32:44.000000,2015-08-16 14:56:43.638000 +72,6192,6257,7167,42,2015-08-16 14:32:44.000000,2015-08-16 14:57:25.638000 +72,28803,6257,7167,43,2015-08-16 14:32:44.000000,2015-08-16 15:00:17.638000 +72,5589,6257,7167,20,2015-08-16 14:33:18.000000,2015-08-16 14:33:18.229000 +72,5587,6257,7167,21,2015-08-16 14:33:18.000000,2015-08-16 14:34:00.229000 +72,6515,6257,7167,22,2015-08-16 14:33:18.000000,2015-08-16 14:35:30.229000 +72,27014,6257,7167,23,2015-08-16 14:33:18.000000,2015-08-16 14:37:18.229000 +72,6512,6257,7167,24,2015-08-16 14:33:18.000000,2015-08-16 14:38:48.229000 +72,6482,6257,7167,25,2015-08-16 14:33:18.000000,2015-08-16 14:40:12.229000 +72,28903,6257,7167,26,2015-08-16 14:33:18.000000,2015-08-16 14:41:36.229000 +72,6508,6257,7167,27,2015-08-16 14:33:18.000000,2015-08-16 14:42:00.229000 +72,26871,6257,7167,28,2015-08-16 14:33:18.000000,2015-08-16 14:44:36.229000 +72,37212,6257,7167,29,2015-08-16 14:33:18.000000,2015-08-16 14:45:36.229000 +72,40419,6257,7167,30,2015-08-16 14:33:18.000000,2015-08-16 14:47:06.229000 +72,6490,6257,7167,31,2015-08-16 14:33:18.000000,2015-08-16 14:47:42.229000 +72,6501,6257,7167,32,2015-08-16 14:33:18.000000,2015-08-16 14:48:30.229000 +72,4693,6257,7167,33,2015-08-16 14:33:18.000000,2015-08-16 14:49:18.229000 +72,4692,6257,7167,34,2015-08-16 14:33:18.000000,2015-08-16 14:50:06.229000 +72,4690,6257,7167,35,2015-08-16 14:33:18.000000,2015-08-16 14:51:30.229000 +72,10531,6257,7167,36,2015-08-16 14:33:18.000000,2015-08-16 14:52:30.229000 +72,26632,6257,7167,37,2015-08-16 14:33:18.000000,2015-08-16 14:53:42.229000 +72,26624,6257,7167,38,2015-08-16 14:33:18.000000,2015-08-16 14:54:30.229000 +72,7319,6257,7167,39,2015-08-16 14:33:18.000000,2015-08-16 14:55:12.229000 +72,28802,6257,7167,40,2015-08-16 14:33:18.000000,2015-08-16 14:56:18.229000 +72,26557,6257,7167,41,2015-08-16 14:33:18.000000,2015-08-16 14:57:12.229000 +72,6192,6257,7167,42,2015-08-16 14:33:18.000000,2015-08-16 14:57:54.229000 +72,28803,6257,7167,43,2015-08-16 14:33:18.000000,2015-08-16 15:00:46.229000 +72,6515,6257,7167,22,2015-08-16 14:33:51.000000,2015-08-16 14:35:07.703000 +72,27014,6257,7167,23,2015-08-16 14:33:51.000000,2015-08-16 14:36:55.703000 +72,6512,6257,7167,24,2015-08-16 14:33:51.000000,2015-08-16 14:38:25.703000 +72,6482,6257,7167,25,2015-08-16 14:33:51.000000,2015-08-16 14:39:49.703000 +72,28903,6257,7167,26,2015-08-16 14:33:51.000000,2015-08-16 14:41:13.703000 +72,6508,6257,7167,27,2015-08-16 14:33:51.000000,2015-08-16 14:41:37.703000 +72,26871,6257,7167,28,2015-08-16 14:33:51.000000,2015-08-16 14:44:13.703000 +72,37212,6257,7167,29,2015-08-16 14:33:51.000000,2015-08-16 14:45:13.703000 +72,40419,6257,7167,30,2015-08-16 14:33:51.000000,2015-08-16 14:46:43.703000 +72,6490,6257,7167,31,2015-08-16 14:33:51.000000,2015-08-16 14:47:19.703000 +72,6501,6257,7167,32,2015-08-16 14:33:51.000000,2015-08-16 14:48:07.703000 +72,4693,6257,7167,33,2015-08-16 14:33:51.000000,2015-08-16 14:48:55.703000 +72,4692,6257,7167,34,2015-08-16 14:33:51.000000,2015-08-16 14:49:43.703000 +72,4690,6257,7167,35,2015-08-16 14:33:51.000000,2015-08-16 14:51:07.703000 +72,10531,6257,7167,36,2015-08-16 14:33:51.000000,2015-08-16 14:52:07.703000 +72,26632,6257,7167,37,2015-08-16 14:33:51.000000,2015-08-16 14:53:19.703000 +72,26624,6257,7167,38,2015-08-16 14:33:51.000000,2015-08-16 14:54:07.703000 +72,7319,6257,7167,39,2015-08-16 14:33:51.000000,2015-08-16 14:54:49.703000 +72,28802,6257,7167,40,2015-08-16 14:33:51.000000,2015-08-16 14:55:55.703000 +72,26557,6257,7167,41,2015-08-16 14:33:51.000000,2015-08-16 14:56:49.703000 +72,6192,6257,7167,42,2015-08-16 14:33:51.000000,2015-08-16 14:57:31.703000 +72,28803,6257,7167,43,2015-08-16 14:33:51.000000,2015-08-16 15:00:23.703000 +72,6515,6257,7167,22,2015-08-16 14:33:59.000000,2015-08-16 14:35:11.970000 +72,27014,6257,7167,23,2015-08-16 14:33:59.000000,2015-08-16 14:36:59.970000 +72,6512,6257,7167,24,2015-08-16 14:33:59.000000,2015-08-16 14:38:29.970000 +72,6482,6257,7167,25,2015-08-16 14:33:59.000000,2015-08-16 14:39:53.970000 +72,28903,6257,7167,26,2015-08-16 14:33:59.000000,2015-08-16 14:41:17.970000 +72,6508,6257,7167,27,2015-08-16 14:33:59.000000,2015-08-16 14:41:41.970000 +72,26871,6257,7167,28,2015-08-16 14:33:59.000000,2015-08-16 14:44:17.970000 +72,37212,6257,7167,29,2015-08-16 14:33:59.000000,2015-08-16 14:45:17.970000 +72,40419,6257,7167,30,2015-08-16 14:33:59.000000,2015-08-16 14:46:47.970000 +72,6490,6257,7167,31,2015-08-16 14:33:59.000000,2015-08-16 14:47:23.970000 +72,6501,6257,7167,32,2015-08-16 14:33:59.000000,2015-08-16 14:48:11.970000 +72,4693,6257,7167,33,2015-08-16 14:33:59.000000,2015-08-16 14:48:59.970000 +72,4692,6257,7167,34,2015-08-16 14:33:59.000000,2015-08-16 14:49:47.970000 +72,4690,6257,7167,35,2015-08-16 14:33:59.000000,2015-08-16 14:51:11.970000 +72,10531,6257,7167,36,2015-08-16 14:33:59.000000,2015-08-16 14:52:11.970000 +72,26632,6257,7167,37,2015-08-16 14:33:59.000000,2015-08-16 14:53:23.970000 +72,26624,6257,7167,38,2015-08-16 14:33:59.000000,2015-08-16 14:54:11.970000 +72,7319,6257,7167,39,2015-08-16 14:33:59.000000,2015-08-16 14:54:53.970000 +72,28802,6257,7167,40,2015-08-16 14:33:59.000000,2015-08-16 14:55:59.970000 +72,26557,6257,7167,41,2015-08-16 14:33:59.000000,2015-08-16 14:56:53.970000 +72,6192,6257,7167,42,2015-08-16 14:33:59.000000,2015-08-16 14:57:35.970000 +72,28803,6257,7167,43,2015-08-16 14:33:59.000000,2015-08-16 15:00:27.970000 +72,27014,6257,7167,23,2015-08-16 14:34:56.000000,2015-08-16 14:36:22.623000 +72,6512,6257,7167,24,2015-08-16 14:34:56.000000,2015-08-16 14:37:52.623000 +72,6482,6257,7167,25,2015-08-16 14:34:56.000000,2015-08-16 14:39:16.623000 +72,28903,6257,7167,26,2015-08-16 14:34:56.000000,2015-08-16 14:40:40.623000 +72,6508,6257,7167,27,2015-08-16 14:34:56.000000,2015-08-16 14:41:04.623000 +72,26871,6257,7167,28,2015-08-16 14:34:56.000000,2015-08-16 14:43:40.623000 +72,37212,6257,7167,29,2015-08-16 14:34:56.000000,2015-08-16 14:44:40.623000 +72,40419,6257,7167,30,2015-08-16 14:34:56.000000,2015-08-16 14:46:10.623000 +72,6490,6257,7167,31,2015-08-16 14:34:56.000000,2015-08-16 14:46:46.623000 +72,6501,6257,7167,32,2015-08-16 14:34:56.000000,2015-08-16 14:47:34.623000 +72,4693,6257,7167,33,2015-08-16 14:34:56.000000,2015-08-16 14:48:22.623000 +72,4692,6257,7167,34,2015-08-16 14:34:56.000000,2015-08-16 14:49:10.623000 +72,4690,6257,7167,35,2015-08-16 14:34:56.000000,2015-08-16 14:50:34.623000 +72,10531,6257,7167,36,2015-08-16 14:34:56.000000,2015-08-16 14:51:34.623000 +72,26632,6257,7167,37,2015-08-16 14:34:56.000000,2015-08-16 14:52:46.623000 +72,26624,6257,7167,38,2015-08-16 14:34:56.000000,2015-08-16 14:53:34.623000 +72,7319,6257,7167,39,2015-08-16 14:34:56.000000,2015-08-16 14:54:16.623000 +72,28802,6257,7167,40,2015-08-16 14:34:56.000000,2015-08-16 14:55:22.623000 +72,26557,6257,7167,41,2015-08-16 14:34:56.000000,2015-08-16 14:56:16.623000 +72,6192,6257,7167,42,2015-08-16 14:34:56.000000,2015-08-16 14:56:58.623000 +72,28803,6257,7167,43,2015-08-16 14:34:56.000000,2015-08-16 14:59:50.623000 +72,27014,6257,7167,23,2015-08-16 14:35:07.000000,2015-08-16 14:36:27.921000 +72,6512,6257,7167,24,2015-08-16 14:35:07.000000,2015-08-16 14:37:57.921000 +72,6482,6257,7167,25,2015-08-16 14:35:07.000000,2015-08-16 14:39:21.921000 +72,28903,6257,7167,26,2015-08-16 14:35:07.000000,2015-08-16 14:40:45.921000 +72,6508,6257,7167,27,2015-08-16 14:35:07.000000,2015-08-16 14:41:09.921000 +72,26871,6257,7167,28,2015-08-16 14:35:07.000000,2015-08-16 14:43:45.921000 +72,37212,6257,7167,29,2015-08-16 14:35:07.000000,2015-08-16 14:44:45.921000 +72,40419,6257,7167,30,2015-08-16 14:35:07.000000,2015-08-16 14:46:15.921000 +72,6490,6257,7167,31,2015-08-16 14:35:07.000000,2015-08-16 14:46:51.921000 +72,6501,6257,7167,32,2015-08-16 14:35:07.000000,2015-08-16 14:47:39.921000 +72,4693,6257,7167,33,2015-08-16 14:35:07.000000,2015-08-16 14:48:27.921000 +72,4692,6257,7167,34,2015-08-16 14:35:07.000000,2015-08-16 14:49:15.921000 +72,4690,6257,7167,35,2015-08-16 14:35:07.000000,2015-08-16 14:50:39.921000 +72,10531,6257,7167,36,2015-08-16 14:35:07.000000,2015-08-16 14:51:39.921000 +72,26632,6257,7167,37,2015-08-16 14:35:07.000000,2015-08-16 14:52:51.921000 +72,26624,6257,7167,38,2015-08-16 14:35:07.000000,2015-08-16 14:53:39.921000 +72,7319,6257,7167,39,2015-08-16 14:35:07.000000,2015-08-16 14:54:21.921000 +72,28802,6257,7167,40,2015-08-16 14:35:07.000000,2015-08-16 14:55:27.921000 +72,26557,6257,7167,41,2015-08-16 14:35:07.000000,2015-08-16 14:56:21.921000 +72,6192,6257,7167,42,2015-08-16 14:35:07.000000,2015-08-16 14:57:03.921000 +72,28803,6257,7167,43,2015-08-16 14:35:07.000000,2015-08-16 14:59:55.921000 +72,27014,6257,7167,23,2015-08-16 14:36:11.000000,2015-08-16 14:36:43.005000 +72,6512,6257,7167,24,2015-08-16 14:36:11.000000,2015-08-16 14:38:13.005000 +72,6482,6257,7167,25,2015-08-16 14:36:11.000000,2015-08-16 14:39:37.005000 +72,28903,6257,7167,26,2015-08-16 14:36:11.000000,2015-08-16 14:41:01.005000 +72,6508,6257,7167,27,2015-08-16 14:36:11.000000,2015-08-16 14:41:25.005000 +72,26871,6257,7167,28,2015-08-16 14:36:11.000000,2015-08-16 14:44:01.005000 +72,37212,6257,7167,29,2015-08-16 14:36:11.000000,2015-08-16 14:45:01.005000 +72,40419,6257,7167,30,2015-08-16 14:36:11.000000,2015-08-16 14:46:31.005000 +72,6490,6257,7167,31,2015-08-16 14:36:11.000000,2015-08-16 14:47:07.005000 +72,6501,6257,7167,32,2015-08-16 14:36:11.000000,2015-08-16 14:47:55.005000 +72,4693,6257,7167,33,2015-08-16 14:36:11.000000,2015-08-16 14:48:43.005000 +72,4692,6257,7167,34,2015-08-16 14:36:11.000000,2015-08-16 14:49:31.005000 +72,4690,6257,7167,35,2015-08-16 14:36:11.000000,2015-08-16 14:50:55.005000 +72,10531,6257,7167,36,2015-08-16 14:36:11.000000,2015-08-16 14:51:55.005000 +72,26632,6257,7167,37,2015-08-16 14:36:11.000000,2015-08-16 14:53:07.005000 +72,26624,6257,7167,38,2015-08-16 14:36:11.000000,2015-08-16 14:53:55.005000 +72,7319,6257,7167,39,2015-08-16 14:36:11.000000,2015-08-16 14:54:37.005000 +72,28802,6257,7167,40,2015-08-16 14:36:11.000000,2015-08-16 14:55:43.005000 +72,26557,6257,7167,41,2015-08-16 14:36:11.000000,2015-08-16 14:56:37.005000 +72,6192,6257,7167,42,2015-08-16 14:36:11.000000,2015-08-16 14:57:19.005000 +72,28803,6257,7167,43,2015-08-16 14:36:11.000000,2015-08-16 15:00:11.005000 +72,27014,6257,7167,23,2015-08-16 14:36:41.000000,2015-08-16 14:37:11.957000 +72,6512,6257,7167,24,2015-08-16 14:36:41.000000,2015-08-16 14:38:41.957000 +72,6482,6257,7167,25,2015-08-16 14:36:41.000000,2015-08-16 14:40:05.957000 +72,28903,6257,7167,26,2015-08-16 14:36:41.000000,2015-08-16 14:41:29.957000 +72,6508,6257,7167,27,2015-08-16 14:36:41.000000,2015-08-16 14:41:53.957000 +72,26871,6257,7167,28,2015-08-16 14:36:41.000000,2015-08-16 14:44:29.957000 +72,37212,6257,7167,29,2015-08-16 14:36:41.000000,2015-08-16 14:45:29.957000 +72,40419,6257,7167,30,2015-08-16 14:36:41.000000,2015-08-16 14:46:59.957000 +72,6490,6257,7167,31,2015-08-16 14:36:41.000000,2015-08-16 14:47:35.957000 +72,6501,6257,7167,32,2015-08-16 14:36:41.000000,2015-08-16 14:48:23.957000 +72,4693,6257,7167,33,2015-08-16 14:36:41.000000,2015-08-16 14:49:11.957000 +72,4692,6257,7167,34,2015-08-16 14:36:41.000000,2015-08-16 14:49:59.957000 +72,4690,6257,7167,35,2015-08-16 14:36:41.000000,2015-08-16 14:51:23.957000 +72,10531,6257,7167,36,2015-08-16 14:36:41.000000,2015-08-16 14:52:23.957000 +72,26632,6257,7167,37,2015-08-16 14:36:41.000000,2015-08-16 14:53:35.957000 +72,26624,6257,7167,38,2015-08-16 14:36:41.000000,2015-08-16 14:54:23.957000 +72,7319,6257,7167,39,2015-08-16 14:36:41.000000,2015-08-16 14:55:05.957000 +72,28802,6257,7167,40,2015-08-16 14:36:41.000000,2015-08-16 14:56:11.957000 +72,26557,6257,7167,41,2015-08-16 14:36:41.000000,2015-08-16 14:57:05.957000 +72,6192,6257,7167,42,2015-08-16 14:36:41.000000,2015-08-16 14:57:47.957000 +72,28803,6257,7167,43,2015-08-16 14:36:41.000000,2015-08-16 15:00:39.957000 +72,6512,6257,7167,24,2015-08-16 14:37:49.000000,2015-08-16 14:39:02.657000 +72,6482,6257,7167,25,2015-08-16 14:37:49.000000,2015-08-16 14:40:26.657000 +72,28903,6257,7167,26,2015-08-16 14:37:49.000000,2015-08-16 14:41:50.657000 +72,6508,6257,7167,27,2015-08-16 14:37:49.000000,2015-08-16 14:42:14.657000 +72,26871,6257,7167,28,2015-08-16 14:37:49.000000,2015-08-16 14:44:50.657000 +72,37212,6257,7167,29,2015-08-16 14:37:49.000000,2015-08-16 14:45:50.657000 +72,40419,6257,7167,30,2015-08-16 14:37:49.000000,2015-08-16 14:47:20.657000 +72,6490,6257,7167,31,2015-08-16 14:37:49.000000,2015-08-16 14:47:56.657000 +72,6501,6257,7167,32,2015-08-16 14:37:49.000000,2015-08-16 14:48:44.657000 +72,4693,6257,7167,33,2015-08-16 14:37:49.000000,2015-08-16 14:49:32.657000 +72,4692,6257,7167,34,2015-08-16 14:37:49.000000,2015-08-16 14:50:20.657000 +72,4690,6257,7167,35,2015-08-16 14:37:49.000000,2015-08-16 14:51:44.657000 +72,10531,6257,7167,36,2015-08-16 14:37:49.000000,2015-08-16 14:52:44.657000 +72,26632,6257,7167,37,2015-08-16 14:37:49.000000,2015-08-16 14:53:56.657000 +72,26624,6257,7167,38,2015-08-16 14:37:49.000000,2015-08-16 14:54:44.657000 +72,7319,6257,7167,39,2015-08-16 14:37:49.000000,2015-08-16 14:55:26.657000 +72,28802,6257,7167,40,2015-08-16 14:37:49.000000,2015-08-16 14:56:32.657000 +72,26557,6257,7167,41,2015-08-16 14:37:49.000000,2015-08-16 14:57:26.657000 +72,6192,6257,7167,42,2015-08-16 14:37:49.000000,2015-08-16 14:58:08.657000 +72,28803,6257,7167,43,2015-08-16 14:37:49.000000,2015-08-16 15:01:00.657000 +72,6512,6257,7167,24,2015-08-16 14:38:23.000000,2015-08-16 14:38:56.548000 +72,6482,6257,7167,25,2015-08-16 14:38:23.000000,2015-08-16 14:40:20.548000 +72,28903,6257,7167,26,2015-08-16 14:38:23.000000,2015-08-16 14:41:44.548000 +72,6508,6257,7167,27,2015-08-16 14:38:23.000000,2015-08-16 14:42:08.548000 +72,26871,6257,7167,28,2015-08-16 14:38:23.000000,2015-08-16 14:44:44.548000 +72,37212,6257,7167,29,2015-08-16 14:38:23.000000,2015-08-16 14:45:44.548000 +72,40419,6257,7167,30,2015-08-16 14:38:23.000000,2015-08-16 14:47:14.548000 +72,6490,6257,7167,31,2015-08-16 14:38:23.000000,2015-08-16 14:47:50.548000 +72,6501,6257,7167,32,2015-08-16 14:38:23.000000,2015-08-16 14:48:38.548000 +72,4693,6257,7167,33,2015-08-16 14:38:23.000000,2015-08-16 14:49:26.548000 +72,4692,6257,7167,34,2015-08-16 14:38:23.000000,2015-08-16 14:50:14.548000 +72,4690,6257,7167,35,2015-08-16 14:38:23.000000,2015-08-16 14:51:38.548000 +72,10531,6257,7167,36,2015-08-16 14:38:23.000000,2015-08-16 14:52:38.548000 +72,26632,6257,7167,37,2015-08-16 14:38:23.000000,2015-08-16 14:53:50.548000 +72,26624,6257,7167,38,2015-08-16 14:38:23.000000,2015-08-16 14:54:38.548000 +72,7319,6257,7167,39,2015-08-16 14:38:23.000000,2015-08-16 14:55:20.548000 +72,28802,6257,7167,40,2015-08-16 14:38:23.000000,2015-08-16 14:56:26.548000 +72,26557,6257,7167,41,2015-08-16 14:38:23.000000,2015-08-16 14:57:20.548000 +72,6192,6257,7167,42,2015-08-16 14:38:23.000000,2015-08-16 14:58:02.548000 +72,28803,6257,7167,43,2015-08-16 14:38:23.000000,2015-08-16 15:00:54.548000 +72,6482,6257,7167,25,2015-08-16 14:39:12.000000,2015-08-16 14:40:17.594000 +72,28903,6257,7167,26,2015-08-16 14:39:12.000000,2015-08-16 14:41:41.594000 +72,6508,6257,7167,27,2015-08-16 14:39:12.000000,2015-08-16 14:42:05.594000 +72,26871,6257,7167,28,2015-08-16 14:39:12.000000,2015-08-16 14:44:41.594000 +72,37212,6257,7167,29,2015-08-16 14:39:12.000000,2015-08-16 14:45:41.594000 +72,40419,6257,7167,30,2015-08-16 14:39:12.000000,2015-08-16 14:47:11.594000 +72,6490,6257,7167,31,2015-08-16 14:39:12.000000,2015-08-16 14:47:47.594000 +72,6501,6257,7167,32,2015-08-16 14:39:12.000000,2015-08-16 14:48:35.594000 +72,4693,6257,7167,33,2015-08-16 14:39:12.000000,2015-08-16 14:49:23.594000 +72,4692,6257,7167,34,2015-08-16 14:39:12.000000,2015-08-16 14:50:11.594000 +72,4690,6257,7167,35,2015-08-16 14:39:12.000000,2015-08-16 14:51:35.594000 +72,10531,6257,7167,36,2015-08-16 14:39:12.000000,2015-08-16 14:52:35.594000 +72,26632,6257,7167,37,2015-08-16 14:39:12.000000,2015-08-16 14:53:47.594000 +72,26624,6257,7167,38,2015-08-16 14:39:12.000000,2015-08-16 14:54:35.594000 +72,7319,6257,7167,39,2015-08-16 14:39:12.000000,2015-08-16 14:55:17.594000 +72,28802,6257,7167,40,2015-08-16 14:39:12.000000,2015-08-16 14:56:23.594000 +72,26557,6257,7167,41,2015-08-16 14:39:12.000000,2015-08-16 14:57:17.594000 +72,6192,6257,7167,42,2015-08-16 14:39:12.000000,2015-08-16 14:57:59.594000 +72,28803,6257,7167,43,2015-08-16 14:39:12.000000,2015-08-16 15:00:51.594000 +72,6482,6257,7167,25,2015-08-16 14:39:55.000000,2015-08-16 14:40:22.473000 +72,28903,6257,7167,26,2015-08-16 14:39:55.000000,2015-08-16 14:41:46.473000 +72,6508,6257,7167,27,2015-08-16 14:39:55.000000,2015-08-16 14:42:10.473000 +72,26871,6257,7167,28,2015-08-16 14:39:55.000000,2015-08-16 14:44:46.473000 +72,37212,6257,7167,29,2015-08-16 14:39:55.000000,2015-08-16 14:45:46.473000 +72,40419,6257,7167,30,2015-08-16 14:39:55.000000,2015-08-16 14:47:16.473000 +72,6490,6257,7167,31,2015-08-16 14:39:55.000000,2015-08-16 14:47:52.473000 +72,6501,6257,7167,32,2015-08-16 14:39:55.000000,2015-08-16 14:48:40.473000 +72,4693,6257,7167,33,2015-08-16 14:39:55.000000,2015-08-16 14:49:28.473000 +72,4692,6257,7167,34,2015-08-16 14:39:55.000000,2015-08-16 14:50:16.473000 +72,4690,6257,7167,35,2015-08-16 14:39:55.000000,2015-08-16 14:51:40.473000 +72,10531,6257,7167,36,2015-08-16 14:39:55.000000,2015-08-16 14:52:40.473000 +72,26632,6257,7167,37,2015-08-16 14:39:55.000000,2015-08-16 14:53:52.473000 +72,26624,6257,7167,38,2015-08-16 14:39:55.000000,2015-08-16 14:54:40.473000 +72,7319,6257,7167,39,2015-08-16 14:39:55.000000,2015-08-16 14:55:22.473000 +72,28802,6257,7167,40,2015-08-16 14:39:55.000000,2015-08-16 14:56:28.473000 +72,26557,6257,7167,41,2015-08-16 14:39:55.000000,2015-08-16 14:57:22.473000 +72,6192,6257,7167,42,2015-08-16 14:39:55.000000,2015-08-16 14:58:04.473000 +72,28803,6257,7167,43,2015-08-16 14:39:55.000000,2015-08-16 15:00:56.473000 +72,6482,6257,7167,25,2015-08-16 14:40:06.000000,2015-08-16 14:40:33.473000 +72,28903,6257,7167,26,2015-08-16 14:40:06.000000,2015-08-16 14:41:57.473000 +72,6508,6257,7167,27,2015-08-16 14:40:06.000000,2015-08-16 14:42:21.473000 +72,26871,6257,7167,28,2015-08-16 14:40:06.000000,2015-08-16 14:44:57.473000 +72,37212,6257,7167,29,2015-08-16 14:40:06.000000,2015-08-16 14:45:57.473000 +72,40419,6257,7167,30,2015-08-16 14:40:06.000000,2015-08-16 14:47:27.473000 +72,6490,6257,7167,31,2015-08-16 14:40:06.000000,2015-08-16 14:48:03.473000 +72,6501,6257,7167,32,2015-08-16 14:40:06.000000,2015-08-16 14:48:51.473000 +72,4693,6257,7167,33,2015-08-16 14:40:06.000000,2015-08-16 14:49:39.473000 +72,4692,6257,7167,34,2015-08-16 14:40:06.000000,2015-08-16 14:50:27.473000 +72,4690,6257,7167,35,2015-08-16 14:40:06.000000,2015-08-16 14:51:51.473000 +72,10531,6257,7167,36,2015-08-16 14:40:06.000000,2015-08-16 14:52:51.473000 +72,26632,6257,7167,37,2015-08-16 14:40:06.000000,2015-08-16 14:54:03.473000 +72,26624,6257,7167,38,2015-08-16 14:40:06.000000,2015-08-16 14:54:51.473000 +72,7319,6257,7167,39,2015-08-16 14:40:06.000000,2015-08-16 14:55:33.473000 +72,28802,6257,7167,40,2015-08-16 14:40:06.000000,2015-08-16 14:56:39.473000 +72,26557,6257,7167,41,2015-08-16 14:40:06.000000,2015-08-16 14:57:33.473000 +72,6192,6257,7167,42,2015-08-16 14:40:06.000000,2015-08-16 14:58:15.473000 +72,28803,6257,7167,43,2015-08-16 14:40:06.000000,2015-08-16 15:01:07.473000 +72,28903,6257,7167,26,2015-08-16 14:41:10.000000,2015-08-16 14:41:59.359000 +72,6508,6257,7167,27,2015-08-16 14:41:10.000000,2015-08-16 14:42:23.359000 +72,26871,6257,7167,28,2015-08-16 14:41:10.000000,2015-08-16 14:44:59.359000 +72,37212,6257,7167,29,2015-08-16 14:41:10.000000,2015-08-16 14:45:59.359000 +72,40419,6257,7167,30,2015-08-16 14:41:10.000000,2015-08-16 14:47:29.359000 +72,6490,6257,7167,31,2015-08-16 14:41:10.000000,2015-08-16 14:48:05.359000 +72,6501,6257,7167,32,2015-08-16 14:41:10.000000,2015-08-16 14:48:53.359000 +72,4693,6257,7167,33,2015-08-16 14:41:10.000000,2015-08-16 14:49:41.359000 +72,4692,6257,7167,34,2015-08-16 14:41:10.000000,2015-08-16 14:50:29.359000 +72,4690,6257,7167,35,2015-08-16 14:41:10.000000,2015-08-16 14:51:53.359000 +72,10531,6257,7167,36,2015-08-16 14:41:10.000000,2015-08-16 14:52:53.359000 +72,26632,6257,7167,37,2015-08-16 14:41:10.000000,2015-08-16 14:54:05.359000 +72,26624,6257,7167,38,2015-08-16 14:41:10.000000,2015-08-16 14:54:53.359000 +72,7319,6257,7167,39,2015-08-16 14:41:10.000000,2015-08-16 14:55:35.359000 +72,28802,6257,7167,40,2015-08-16 14:41:10.000000,2015-08-16 14:56:41.359000 +72,26557,6257,7167,41,2015-08-16 14:41:10.000000,2015-08-16 14:57:35.359000 +72,6192,6257,7167,42,2015-08-16 14:41:10.000000,2015-08-16 14:58:17.359000 +72,28803,6257,7167,43,2015-08-16 14:41:10.000000,2015-08-16 15:01:09.359000 +72,28903,6257,7167,26,2015-08-16 14:41:40.000000,2015-08-16 14:42:17.978000 +72,6508,6257,7167,27,2015-08-16 14:41:40.000000,2015-08-16 14:42:41.978000 +72,26871,6257,7167,28,2015-08-16 14:41:40.000000,2015-08-16 14:45:17.978000 +72,37212,6257,7167,29,2015-08-16 14:41:40.000000,2015-08-16 14:46:17.978000 +72,40419,6257,7167,30,2015-08-16 14:41:40.000000,2015-08-16 14:47:47.978000 +72,6490,6257,7167,31,2015-08-16 14:41:40.000000,2015-08-16 14:48:23.978000 +72,6501,6257,7167,32,2015-08-16 14:41:40.000000,2015-08-16 14:49:11.978000 +72,4693,6257,7167,33,2015-08-16 14:41:40.000000,2015-08-16 14:49:59.978000 +72,4692,6257,7167,34,2015-08-16 14:41:40.000000,2015-08-16 14:50:47.978000 +72,4690,6257,7167,35,2015-08-16 14:41:40.000000,2015-08-16 14:52:11.978000 +72,10531,6257,7167,36,2015-08-16 14:41:40.000000,2015-08-16 14:53:11.978000 +72,26632,6257,7167,37,2015-08-16 14:41:40.000000,2015-08-16 14:54:23.978000 +72,26624,6257,7167,38,2015-08-16 14:41:40.000000,2015-08-16 14:55:11.978000 +72,7319,6257,7167,39,2015-08-16 14:41:40.000000,2015-08-16 14:55:53.978000 +72,28802,6257,7167,40,2015-08-16 14:41:40.000000,2015-08-16 14:56:59.978000 +72,26557,6257,7167,41,2015-08-16 14:41:40.000000,2015-08-16 14:57:53.978000 +72,6192,6257,7167,42,2015-08-16 14:41:40.000000,2015-08-16 14:58:35.978000 +72,28803,6257,7167,43,2015-08-16 14:41:40.000000,2015-08-16 15:01:27.978000 +72,6508,6257,7167,27,2015-08-16 14:42:33.000000,2015-08-16 14:42:44.441000 +72,26871,6257,7167,28,2015-08-16 14:42:33.000000,2015-08-16 14:45:20.441000 +72,37212,6257,7167,29,2015-08-16 14:42:33.000000,2015-08-16 14:46:20.441000 +72,40419,6257,7167,30,2015-08-16 14:42:33.000000,2015-08-16 14:47:50.441000 +72,6490,6257,7167,31,2015-08-16 14:42:33.000000,2015-08-16 14:48:26.441000 +72,6501,6257,7167,32,2015-08-16 14:42:33.000000,2015-08-16 14:49:14.441000 +72,4693,6257,7167,33,2015-08-16 14:42:33.000000,2015-08-16 14:50:02.441000 +72,4692,6257,7167,34,2015-08-16 14:42:33.000000,2015-08-16 14:50:50.441000 +72,4690,6257,7167,35,2015-08-16 14:42:33.000000,2015-08-16 14:52:14.441000 +72,10531,6257,7167,36,2015-08-16 14:42:33.000000,2015-08-16 14:53:14.441000 +72,26632,6257,7167,37,2015-08-16 14:42:33.000000,2015-08-16 14:54:26.441000 +72,26624,6257,7167,38,2015-08-16 14:42:33.000000,2015-08-16 14:55:14.441000 +72,7319,6257,7167,39,2015-08-16 14:42:33.000000,2015-08-16 14:55:56.441000 +72,28802,6257,7167,40,2015-08-16 14:42:33.000000,2015-08-16 14:57:02.441000 +72,26557,6257,7167,41,2015-08-16 14:42:33.000000,2015-08-16 14:57:56.441000 +72,6192,6257,7167,42,2015-08-16 14:42:33.000000,2015-08-16 14:58:38.441000 +72,28803,6257,7167,43,2015-08-16 14:42:33.000000,2015-08-16 15:01:30.441000 +72,26871,6257,7167,28,2015-08-16 14:43:16.000000,2015-08-16 14:45:38.215000 +72,37212,6257,7167,29,2015-08-16 14:43:16.000000,2015-08-16 14:46:38.215000 +72,40419,6257,7167,30,2015-08-16 14:43:16.000000,2015-08-16 14:48:08.215000 +72,6490,6257,7167,31,2015-08-16 14:43:16.000000,2015-08-16 14:48:44.215000 +72,6501,6257,7167,32,2015-08-16 14:43:16.000000,2015-08-16 14:49:32.215000 +72,4693,6257,7167,33,2015-08-16 14:43:16.000000,2015-08-16 14:50:20.215000 +72,4692,6257,7167,34,2015-08-16 14:43:16.000000,2015-08-16 14:51:08.215000 +72,4690,6257,7167,35,2015-08-16 14:43:16.000000,2015-08-16 14:52:32.215000 +72,10531,6257,7167,36,2015-08-16 14:43:16.000000,2015-08-16 14:53:32.215000 +72,26632,6257,7167,37,2015-08-16 14:43:16.000000,2015-08-16 14:54:44.215000 +72,26624,6257,7167,38,2015-08-16 14:43:16.000000,2015-08-16 14:55:32.215000 +72,7319,6257,7167,39,2015-08-16 14:43:16.000000,2015-08-16 14:56:14.215000 +72,28802,6257,7167,40,2015-08-16 14:43:16.000000,2015-08-16 14:57:20.215000 +72,26557,6257,7167,41,2015-08-16 14:43:16.000000,2015-08-16 14:58:14.215000 +72,6192,6257,7167,42,2015-08-16 14:43:16.000000,2015-08-16 14:58:56.215000 +72,28803,6257,7167,43,2015-08-16 14:43:16.000000,2015-08-16 15:01:48.215000 +72,26871,6257,7167,28,2015-08-16 14:43:48.000000,2015-08-16 14:45:32.921000 +72,37212,6257,7167,29,2015-08-16 14:43:48.000000,2015-08-16 14:46:32.921000 +72,40419,6257,7167,30,2015-08-16 14:43:48.000000,2015-08-16 14:48:02.921000 +72,6490,6257,7167,31,2015-08-16 14:43:48.000000,2015-08-16 14:48:38.921000 +72,6501,6257,7167,32,2015-08-16 14:43:48.000000,2015-08-16 14:49:26.921000 +72,4693,6257,7167,33,2015-08-16 14:43:48.000000,2015-08-16 14:50:14.921000 +72,4692,6257,7167,34,2015-08-16 14:43:48.000000,2015-08-16 14:51:02.921000 +72,4690,6257,7167,35,2015-08-16 14:43:48.000000,2015-08-16 14:52:26.921000 +72,10531,6257,7167,36,2015-08-16 14:43:48.000000,2015-08-16 14:53:26.921000 +72,26632,6257,7167,37,2015-08-16 14:43:48.000000,2015-08-16 14:54:38.921000 +72,26624,6257,7167,38,2015-08-16 14:43:48.000000,2015-08-16 14:55:26.921000 +72,7319,6257,7167,39,2015-08-16 14:43:48.000000,2015-08-16 14:56:08.921000 +72,28802,6257,7167,40,2015-08-16 14:43:48.000000,2015-08-16 14:57:14.921000 +72,26557,6257,7167,41,2015-08-16 14:43:48.000000,2015-08-16 14:58:08.921000 +72,6192,6257,7167,42,2015-08-16 14:43:48.000000,2015-08-16 14:58:50.921000 +72,28803,6257,7167,43,2015-08-16 14:43:48.000000,2015-08-16 15:01:42.921000 +72,26871,6257,7167,28,2015-08-16 14:44:54.000000,2015-08-16 14:45:03.404000 +72,37212,6257,7167,29,2015-08-16 14:44:54.000000,2015-08-16 14:46:03.404000 +72,40419,6257,7167,30,2015-08-16 14:44:54.000000,2015-08-16 14:47:33.404000 +72,6490,6257,7167,31,2015-08-16 14:44:54.000000,2015-08-16 14:48:09.404000 +72,6501,6257,7167,32,2015-08-16 14:44:54.000000,2015-08-16 14:48:57.404000 +72,4693,6257,7167,33,2015-08-16 14:44:54.000000,2015-08-16 14:49:45.404000 +72,4692,6257,7167,34,2015-08-16 14:44:54.000000,2015-08-16 14:50:33.404000 +72,4690,6257,7167,35,2015-08-16 14:44:54.000000,2015-08-16 14:51:57.404000 +72,10531,6257,7167,36,2015-08-16 14:44:54.000000,2015-08-16 14:52:57.404000 +72,26632,6257,7167,37,2015-08-16 14:44:54.000000,2015-08-16 14:54:09.404000 +72,26624,6257,7167,38,2015-08-16 14:44:54.000000,2015-08-16 14:54:57.404000 +72,7319,6257,7167,39,2015-08-16 14:44:54.000000,2015-08-16 14:55:39.404000 +72,28802,6257,7167,40,2015-08-16 14:44:54.000000,2015-08-16 14:56:45.404000 +72,26557,6257,7167,41,2015-08-16 14:44:54.000000,2015-08-16 14:57:39.404000 +72,6192,6257,7167,42,2015-08-16 14:44:54.000000,2015-08-16 14:58:21.404000 +72,28803,6257,7167,43,2015-08-16 14:44:54.000000,2015-08-16 15:01:13.404000 +72,37212,6257,7167,29,2015-08-16 14:45:28.000000,2015-08-16 14:46:17.124000 +72,40419,6257,7167,30,2015-08-16 14:45:28.000000,2015-08-16 14:47:47.124000 +72,6490,6257,7167,31,2015-08-16 14:45:28.000000,2015-08-16 14:48:23.124000 +72,6501,6257,7167,32,2015-08-16 14:45:28.000000,2015-08-16 14:49:11.124000 +72,4693,6257,7167,33,2015-08-16 14:45:28.000000,2015-08-16 14:49:59.124000 +72,4692,6257,7167,34,2015-08-16 14:45:28.000000,2015-08-16 14:50:47.124000 +72,4690,6257,7167,35,2015-08-16 14:45:28.000000,2015-08-16 14:52:11.124000 +72,10531,6257,7167,36,2015-08-16 14:45:28.000000,2015-08-16 14:53:11.124000 +72,26632,6257,7167,37,2015-08-16 14:45:28.000000,2015-08-16 14:54:23.124000 +72,26624,6257,7167,38,2015-08-16 14:45:28.000000,2015-08-16 14:55:11.124000 +72,7319,6257,7167,39,2015-08-16 14:45:28.000000,2015-08-16 14:55:53.124000 +72,28802,6257,7167,40,2015-08-16 14:45:28.000000,2015-08-16 14:56:59.124000 +72,26557,6257,7167,41,2015-08-16 14:45:28.000000,2015-08-16 14:57:53.124000 +72,6192,6257,7167,42,2015-08-16 14:45:28.000000,2015-08-16 14:58:35.124000 +72,28803,6257,7167,43,2015-08-16 14:45:28.000000,2015-08-16 15:01:27.124000 +72,37212,6257,7167,29,2015-08-16 14:46:27.000000,2015-08-16 14:47:01.477000 +72,40419,6257,7167,30,2015-08-16 14:46:27.000000,2015-08-16 14:48:31.477000 +72,6490,6257,7167,31,2015-08-16 14:46:27.000000,2015-08-16 14:49:07.477000 +72,6501,6257,7167,32,2015-08-16 14:46:27.000000,2015-08-16 14:49:55.477000 +72,4693,6257,7167,33,2015-08-16 14:46:27.000000,2015-08-16 14:50:43.477000 +72,4692,6257,7167,34,2015-08-16 14:46:27.000000,2015-08-16 14:51:31.477000 +72,4690,6257,7167,35,2015-08-16 14:46:27.000000,2015-08-16 14:52:55.477000 +72,10531,6257,7167,36,2015-08-16 14:46:27.000000,2015-08-16 14:53:55.477000 +72,26632,6257,7167,37,2015-08-16 14:46:27.000000,2015-08-16 14:55:07.477000 +72,26624,6257,7167,38,2015-08-16 14:46:27.000000,2015-08-16 14:55:55.477000 +72,7319,6257,7167,39,2015-08-16 14:46:27.000000,2015-08-16 14:56:37.477000 +72,28802,6257,7167,40,2015-08-16 14:46:27.000000,2015-08-16 14:57:43.477000 +72,26557,6257,7167,41,2015-08-16 14:46:27.000000,2015-08-16 14:58:37.477000 +72,6192,6257,7167,42,2015-08-16 14:46:27.000000,2015-08-16 14:59:19.477000 +72,28803,6257,7167,43,2015-08-16 14:46:27.000000,2015-08-16 15:02:11.477000 +72,6483,6297,7167,19,2015-08-16 15:31:58.000000,2015-08-16 15:34:24.818000 +72,6510,6297,7167,20,2015-08-16 15:31:58.000000,2015-08-16 15:35:42.818000 +72,6481,6297,7167,21,2015-08-16 15:31:58.000000,2015-08-16 15:36:48.818000 +72,8930,6297,7167,22,2015-08-16 15:31:58.000000,2015-08-16 15:38:24.818000 +72,6476,6297,7167,23,2015-08-16 15:31:58.000000,2015-08-16 15:40:36.818000 +72,6475,6297,7167,24,2015-08-16 15:31:58.000000,2015-08-16 15:41:18.818000 +72,5586,6297,7167,25,2015-08-16 15:31:58.000000,2015-08-16 15:42:42.818000 +72,5590,6297,7167,26,2015-08-16 15:31:58.000000,2015-08-16 15:43:30.818000 +72,40835,6297,7167,27,2015-08-16 15:31:58.000000,2015-08-16 15:45:24.818000 +72,7367,6297,7167,28,2015-08-16 15:31:58.000000,2015-08-16 15:46:54.818000 +72,5580,6297,7167,29,2015-08-16 15:31:58.000000,2015-08-16 15:47:48.818000 +72,7495,6297,7167,30,2015-08-16 15:31:58.000000,2015-08-16 15:48:48.818000 +72,7478,6297,7167,31,2015-08-16 15:31:58.000000,2015-08-16 15:50:30.818000 +72,30054,6297,7167,32,2015-08-16 15:31:58.000000,2015-08-16 15:52:54.818000 +72,6463,6297,7167,33,2015-08-16 15:31:58.000000,2015-08-16 15:54:30.818000 +72,6464,6297,7167,34,2015-08-16 15:31:58.000000,2015-08-16 15:56:06.818000 +72,30060,6297,7167,35,2015-08-16 15:31:58.000000,2015-08-16 15:57:42.818000 +72,7830,6297,7167,36,2015-08-16 15:31:58.000000,2015-08-16 16:00:06.818000 +72,781,6297,7167,37,2015-08-16 15:31:58.000000,2015-08-16 16:02:54.818000 +72,6452,6297,7167,38,2015-08-16 15:31:58.000000,2015-08-16 16:05:18.818000 +72,12953,6297,7167,39,2015-08-16 15:31:58.000000,2015-08-16 16:07:18.818000 +72,14556,6297,7167,40,2015-08-16 15:31:58.000000,2015-08-16 16:07:54.818000 +72,6471,6297,7167,41,2015-08-16 15:31:58.000000,2015-08-16 16:09:18.818000 +72,28898,6297,7167,42,2015-08-16 15:31:58.000000,2015-08-16 16:11:54.818000 +72,8050,6297,7167,43,2015-08-16 15:31:58.000000,2015-08-16 16:12:54.818000 +72,7061,6297,7167,44,2015-08-16 15:31:58.000000,2015-08-16 16:13:54.818000 +72,7062,6297,7167,45,2015-08-16 15:31:58.000000,2015-08-16 16:14:48.818000 +72,36831,6297,7167,46,2015-08-16 15:31:58.000000,2015-08-16 16:16:34.818000 +72,6483,6297,7167,19,2015-08-16 15:33:06.000000,2015-08-16 15:33:27.871000 +72,6510,6297,7167,20,2015-08-16 15:33:06.000000,2015-08-16 15:34:45.871000 +72,6481,6297,7167,21,2015-08-16 15:33:06.000000,2015-08-16 15:35:51.871000 +72,8930,6297,7167,22,2015-08-16 15:33:06.000000,2015-08-16 15:37:27.871000 +72,6476,6297,7167,23,2015-08-16 15:33:06.000000,2015-08-16 15:39:39.871000 +72,6475,6297,7167,24,2015-08-16 15:33:06.000000,2015-08-16 15:40:21.871000 +72,5586,6297,7167,25,2015-08-16 15:33:06.000000,2015-08-16 15:41:45.871000 +72,5590,6297,7167,26,2015-08-16 15:33:06.000000,2015-08-16 15:42:33.871000 +72,40835,6297,7167,27,2015-08-16 15:33:06.000000,2015-08-16 15:44:27.871000 +72,7367,6297,7167,28,2015-08-16 15:33:06.000000,2015-08-16 15:45:57.871000 +72,5580,6297,7167,29,2015-08-16 15:33:06.000000,2015-08-16 15:46:51.871000 +72,7495,6297,7167,30,2015-08-16 15:33:06.000000,2015-08-16 15:47:51.871000 +72,7478,6297,7167,31,2015-08-16 15:33:06.000000,2015-08-16 15:49:33.871000 +72,30054,6297,7167,32,2015-08-16 15:33:06.000000,2015-08-16 15:51:57.871000 +72,6463,6297,7167,33,2015-08-16 15:33:06.000000,2015-08-16 15:53:33.871000 +72,6464,6297,7167,34,2015-08-16 15:33:06.000000,2015-08-16 15:55:09.871000 +72,30060,6297,7167,35,2015-08-16 15:33:06.000000,2015-08-16 15:56:45.871000 +72,7830,6297,7167,36,2015-08-16 15:33:06.000000,2015-08-16 15:59:09.871000 +72,781,6297,7167,37,2015-08-16 15:33:06.000000,2015-08-16 16:01:57.871000 +72,6452,6297,7167,38,2015-08-16 15:33:06.000000,2015-08-16 16:04:21.871000 +72,12953,6297,7167,39,2015-08-16 15:33:06.000000,2015-08-16 16:06:21.871000 +72,14556,6297,7167,40,2015-08-16 15:33:06.000000,2015-08-16 16:06:57.871000 +72,6471,6297,7167,41,2015-08-16 15:33:06.000000,2015-08-16 16:08:21.871000 +72,28898,6297,7167,42,2015-08-16 15:33:06.000000,2015-08-16 16:10:57.871000 +72,8050,6297,7167,43,2015-08-16 15:33:06.000000,2015-08-16 16:11:57.871000 +72,7061,6297,7167,44,2015-08-16 15:33:06.000000,2015-08-16 16:12:57.871000 +72,7062,6297,7167,45,2015-08-16 15:33:06.000000,2015-08-16 16:13:51.871000 +72,36831,6297,7167,46,2015-08-16 15:33:06.000000,2015-08-16 16:15:37.871000 +72,6483,6297,7167,19,2015-08-16 15:33:36.000000,2015-08-16 15:33:36.150000 +72,6510,6297,7167,20,2015-08-16 15:33:36.000000,2015-08-16 15:34:54.150000 +72,6481,6297,7167,21,2015-08-16 15:33:36.000000,2015-08-16 15:36:00.150000 +72,8930,6297,7167,22,2015-08-16 15:33:36.000000,2015-08-16 15:37:36.150000 +72,6476,6297,7167,23,2015-08-16 15:33:36.000000,2015-08-16 15:39:48.150000 +72,6475,6297,7167,24,2015-08-16 15:33:36.000000,2015-08-16 15:40:30.150000 +72,5586,6297,7167,25,2015-08-16 15:33:36.000000,2015-08-16 15:41:54.150000 +72,5590,6297,7167,26,2015-08-16 15:33:36.000000,2015-08-16 15:42:42.150000 +72,40835,6297,7167,27,2015-08-16 15:33:36.000000,2015-08-16 15:44:36.150000 +72,7367,6297,7167,28,2015-08-16 15:33:36.000000,2015-08-16 15:46:06.150000 +72,5580,6297,7167,29,2015-08-16 15:33:36.000000,2015-08-16 15:47:00.150000 +72,7495,6297,7167,30,2015-08-16 15:33:36.000000,2015-08-16 15:48:00.150000 +72,7478,6297,7167,31,2015-08-16 15:33:36.000000,2015-08-16 15:49:42.150000 +72,30054,6297,7167,32,2015-08-16 15:33:36.000000,2015-08-16 15:52:06.150000 +72,6463,6297,7167,33,2015-08-16 15:33:36.000000,2015-08-16 15:53:42.150000 +72,6464,6297,7167,34,2015-08-16 15:33:36.000000,2015-08-16 15:55:18.150000 +72,30060,6297,7167,35,2015-08-16 15:33:36.000000,2015-08-16 15:56:54.150000 +72,7830,6297,7167,36,2015-08-16 15:33:36.000000,2015-08-16 15:59:18.150000 +72,781,6297,7167,37,2015-08-16 15:33:36.000000,2015-08-16 16:02:06.150000 +72,6452,6297,7167,38,2015-08-16 15:33:36.000000,2015-08-16 16:04:30.150000 +72,12953,6297,7167,39,2015-08-16 15:33:36.000000,2015-08-16 16:06:30.150000 +72,14556,6297,7167,40,2015-08-16 15:33:36.000000,2015-08-16 16:07:06.150000 +72,6471,6297,7167,41,2015-08-16 15:33:36.000000,2015-08-16 16:08:30.150000 +72,28898,6297,7167,42,2015-08-16 15:33:36.000000,2015-08-16 16:11:06.150000 +72,8050,6297,7167,43,2015-08-16 15:33:36.000000,2015-08-16 16:12:06.150000 +72,7061,6297,7167,44,2015-08-16 15:33:36.000000,2015-08-16 16:13:06.150000 +72,7062,6297,7167,45,2015-08-16 15:33:36.000000,2015-08-16 16:14:00.150000 +72,36831,6297,7167,46,2015-08-16 15:33:36.000000,2015-08-16 16:15:46.150000 +72,6510,6297,7167,20,2015-08-16 15:35:01.000000,2015-08-16 15:35:03.499000 +72,6481,6297,7167,21,2015-08-16 15:35:01.000000,2015-08-16 15:36:09.499000 +72,8930,6297,7167,22,2015-08-16 15:35:01.000000,2015-08-16 15:37:45.499000 +72,6476,6297,7167,23,2015-08-16 15:35:01.000000,2015-08-16 15:39:57.499000 +72,6475,6297,7167,24,2015-08-16 15:35:01.000000,2015-08-16 15:40:39.499000 +72,5586,6297,7167,25,2015-08-16 15:35:01.000000,2015-08-16 15:42:03.499000 +72,5590,6297,7167,26,2015-08-16 15:35:01.000000,2015-08-16 15:42:51.499000 +72,40835,6297,7167,27,2015-08-16 15:35:01.000000,2015-08-16 15:44:45.499000 +72,7367,6297,7167,28,2015-08-16 15:35:01.000000,2015-08-16 15:46:15.499000 +72,5580,6297,7167,29,2015-08-16 15:35:01.000000,2015-08-16 15:47:09.499000 +72,7495,6297,7167,30,2015-08-16 15:35:01.000000,2015-08-16 15:48:09.499000 +72,7478,6297,7167,31,2015-08-16 15:35:01.000000,2015-08-16 15:49:51.499000 +72,30054,6297,7167,32,2015-08-16 15:35:01.000000,2015-08-16 15:52:15.499000 +72,6463,6297,7167,33,2015-08-16 15:35:01.000000,2015-08-16 15:53:51.499000 +72,6464,6297,7167,34,2015-08-16 15:35:01.000000,2015-08-16 15:55:27.499000 +72,30060,6297,7167,35,2015-08-16 15:35:01.000000,2015-08-16 15:57:03.499000 +72,7830,6297,7167,36,2015-08-16 15:35:01.000000,2015-08-16 15:59:27.499000 +72,781,6297,7167,37,2015-08-16 15:35:01.000000,2015-08-16 16:02:15.499000 +72,6452,6297,7167,38,2015-08-16 15:35:01.000000,2015-08-16 16:04:39.499000 +72,12953,6297,7167,39,2015-08-16 15:35:01.000000,2015-08-16 16:06:39.499000 +72,14556,6297,7167,40,2015-08-16 15:35:01.000000,2015-08-16 16:07:15.499000 +72,6471,6297,7167,41,2015-08-16 15:35:01.000000,2015-08-16 16:08:39.499000 +72,28898,6297,7167,42,2015-08-16 15:35:01.000000,2015-08-16 16:11:15.499000 +72,8050,6297,7167,43,2015-08-16 15:35:01.000000,2015-08-16 16:12:15.499000 +72,7061,6297,7167,44,2015-08-16 15:35:01.000000,2015-08-16 16:13:15.499000 +72,7062,6297,7167,45,2015-08-16 15:35:01.000000,2015-08-16 16:14:09.499000 +72,36831,6297,7167,46,2015-08-16 15:35:01.000000,2015-08-16 16:15:55.499000 +72,6481,6297,7167,21,2015-08-16 15:35:16.000000,2015-08-16 15:36:07.352000 +72,8930,6297,7167,22,2015-08-16 15:35:16.000000,2015-08-16 15:37:43.352000 +72,6476,6297,7167,23,2015-08-16 15:35:16.000000,2015-08-16 15:39:55.352000 +72,6475,6297,7167,24,2015-08-16 15:35:16.000000,2015-08-16 15:40:37.352000 +72,5586,6297,7167,25,2015-08-16 15:35:16.000000,2015-08-16 15:42:01.352000 +72,5590,6297,7167,26,2015-08-16 15:35:16.000000,2015-08-16 15:42:49.352000 +72,40835,6297,7167,27,2015-08-16 15:35:16.000000,2015-08-16 15:44:43.352000 +72,7367,6297,7167,28,2015-08-16 15:35:16.000000,2015-08-16 15:46:13.352000 +72,5580,6297,7167,29,2015-08-16 15:35:16.000000,2015-08-16 15:47:07.352000 +72,7495,6297,7167,30,2015-08-16 15:35:16.000000,2015-08-16 15:48:07.352000 +72,7478,6297,7167,31,2015-08-16 15:35:16.000000,2015-08-16 15:49:49.352000 +72,30054,6297,7167,32,2015-08-16 15:35:16.000000,2015-08-16 15:52:13.352000 +72,6463,6297,7167,33,2015-08-16 15:35:16.000000,2015-08-16 15:53:49.352000 +72,6464,6297,7167,34,2015-08-16 15:35:16.000000,2015-08-16 15:55:25.352000 +72,30060,6297,7167,35,2015-08-16 15:35:16.000000,2015-08-16 15:57:01.352000 +72,7830,6297,7167,36,2015-08-16 15:35:16.000000,2015-08-16 15:59:25.352000 +72,781,6297,7167,37,2015-08-16 15:35:16.000000,2015-08-16 16:02:13.352000 +72,6452,6297,7167,38,2015-08-16 15:35:16.000000,2015-08-16 16:04:37.352000 +72,12953,6297,7167,39,2015-08-16 15:35:16.000000,2015-08-16 16:06:37.352000 +72,14556,6297,7167,40,2015-08-16 15:35:16.000000,2015-08-16 16:07:13.352000 +72,6471,6297,7167,41,2015-08-16 15:35:16.000000,2015-08-16 16:08:37.352000 +72,28898,6297,7167,42,2015-08-16 15:35:16.000000,2015-08-16 16:11:13.352000 +72,8050,6297,7167,43,2015-08-16 15:35:16.000000,2015-08-16 16:12:13.352000 +72,7061,6297,7167,44,2015-08-16 15:35:16.000000,2015-08-16 16:13:13.352000 +72,7062,6297,7167,45,2015-08-16 15:35:16.000000,2015-08-16 16:14:07.352000 +72,36831,6297,7167,46,2015-08-16 15:35:16.000000,2015-08-16 16:15:53.352000 +72,8930,6297,7167,22,2015-08-16 15:35:50.000000,2015-08-16 15:36:37.699000 +72,6476,6297,7167,23,2015-08-16 15:35:50.000000,2015-08-16 15:38:49.699000 +72,6475,6297,7167,24,2015-08-16 15:35:50.000000,2015-08-16 15:39:31.699000 +72,5586,6297,7167,25,2015-08-16 15:35:50.000000,2015-08-16 15:40:55.699000 +72,5590,6297,7167,26,2015-08-16 15:35:50.000000,2015-08-16 15:41:43.699000 +72,40835,6297,7167,27,2015-08-16 15:35:50.000000,2015-08-16 15:43:37.699000 +72,7367,6297,7167,28,2015-08-16 15:35:50.000000,2015-08-16 15:45:07.699000 +72,5580,6297,7167,29,2015-08-16 15:35:50.000000,2015-08-16 15:46:01.699000 +72,7495,6297,7167,30,2015-08-16 15:35:50.000000,2015-08-16 15:47:01.699000 +72,7478,6297,7167,31,2015-08-16 15:35:50.000000,2015-08-16 15:48:43.699000 +72,30054,6297,7167,32,2015-08-16 15:35:50.000000,2015-08-16 15:51:07.699000 +72,6463,6297,7167,33,2015-08-16 15:35:50.000000,2015-08-16 15:52:43.699000 +72,6464,6297,7167,34,2015-08-16 15:35:50.000000,2015-08-16 15:54:19.699000 +72,30060,6297,7167,35,2015-08-16 15:35:50.000000,2015-08-16 15:55:55.699000 +72,7830,6297,7167,36,2015-08-16 15:35:50.000000,2015-08-16 15:58:19.699000 +72,781,6297,7167,37,2015-08-16 15:35:50.000000,2015-08-16 16:01:07.699000 +72,6452,6297,7167,38,2015-08-16 15:35:50.000000,2015-08-16 16:03:31.699000 +72,12953,6297,7167,39,2015-08-16 15:35:50.000000,2015-08-16 16:05:31.699000 +72,14556,6297,7167,40,2015-08-16 15:35:50.000000,2015-08-16 16:06:07.699000 +72,6471,6297,7167,41,2015-08-16 15:35:50.000000,2015-08-16 16:07:31.699000 +72,28898,6297,7167,42,2015-08-16 15:35:50.000000,2015-08-16 16:10:07.699000 +72,8050,6297,7167,43,2015-08-16 15:35:50.000000,2015-08-16 16:11:07.699000 +72,7061,6297,7167,44,2015-08-16 15:35:50.000000,2015-08-16 16:12:07.699000 +72,7062,6297,7167,45,2015-08-16 15:35:50.000000,2015-08-16 16:13:01.699000 +72,36831,6297,7167,46,2015-08-16 15:35:50.000000,2015-08-16 16:14:47.699000 +72,8930,6297,7167,22,2015-08-16 15:36:24.000000,2015-08-16 15:36:25.063000 +72,6476,6297,7167,23,2015-08-16 15:36:24.000000,2015-08-16 15:38:37.063000 +72,6475,6297,7167,24,2015-08-16 15:36:24.000000,2015-08-16 15:39:19.063000 +72,5586,6297,7167,25,2015-08-16 15:36:24.000000,2015-08-16 15:40:43.063000 +72,5590,6297,7167,26,2015-08-16 15:36:24.000000,2015-08-16 15:41:31.063000 +72,40835,6297,7167,27,2015-08-16 15:36:24.000000,2015-08-16 15:43:25.063000 +72,7367,6297,7167,28,2015-08-16 15:36:24.000000,2015-08-16 15:44:55.063000 +72,5580,6297,7167,29,2015-08-16 15:36:24.000000,2015-08-16 15:45:49.063000 +72,7495,6297,7167,30,2015-08-16 15:36:24.000000,2015-08-16 15:46:49.063000 +72,7478,6297,7167,31,2015-08-16 15:36:24.000000,2015-08-16 15:48:31.063000 +72,30054,6297,7167,32,2015-08-16 15:36:24.000000,2015-08-16 15:50:55.063000 +72,6463,6297,7167,33,2015-08-16 15:36:24.000000,2015-08-16 15:52:31.063000 +72,6464,6297,7167,34,2015-08-16 15:36:24.000000,2015-08-16 15:54:07.063000 +72,30060,6297,7167,35,2015-08-16 15:36:24.000000,2015-08-16 15:55:43.063000 +72,7830,6297,7167,36,2015-08-16 15:36:24.000000,2015-08-16 15:58:07.063000 +72,781,6297,7167,37,2015-08-16 15:36:24.000000,2015-08-16 16:00:55.063000 +72,6452,6297,7167,38,2015-08-16 15:36:24.000000,2015-08-16 16:03:19.063000 +72,12953,6297,7167,39,2015-08-16 15:36:24.000000,2015-08-16 16:05:19.063000 +72,14556,6297,7167,40,2015-08-16 15:36:24.000000,2015-08-16 16:05:55.063000 +72,6471,6297,7167,41,2015-08-16 15:36:24.000000,2015-08-16 16:07:19.063000 +72,28898,6297,7167,42,2015-08-16 15:36:24.000000,2015-08-16 16:09:55.063000 +72,8050,6297,7167,43,2015-08-16 15:36:24.000000,2015-08-16 16:10:55.063000 +72,7061,6297,7167,44,2015-08-16 15:36:24.000000,2015-08-16 16:11:55.063000 +72,7062,6297,7167,45,2015-08-16 15:36:24.000000,2015-08-16 16:12:49.063000 +72,36831,6297,7167,46,2015-08-16 15:36:24.000000,2015-08-16 16:14:35.063000 +72,8930,6297,7167,22,2015-08-16 15:36:56.000000,2015-08-16 15:36:56.122000 +72,6476,6297,7167,23,2015-08-16 15:36:56.000000,2015-08-16 15:39:08.122000 +72,6475,6297,7167,24,2015-08-16 15:36:56.000000,2015-08-16 15:39:50.122000 +72,5586,6297,7167,25,2015-08-16 15:36:56.000000,2015-08-16 15:41:14.122000 +72,5590,6297,7167,26,2015-08-16 15:36:56.000000,2015-08-16 15:42:02.122000 +72,40835,6297,7167,27,2015-08-16 15:36:56.000000,2015-08-16 15:43:56.122000 +72,7367,6297,7167,28,2015-08-16 15:36:56.000000,2015-08-16 15:45:26.122000 +72,5580,6297,7167,29,2015-08-16 15:36:56.000000,2015-08-16 15:46:20.122000 +72,7495,6297,7167,30,2015-08-16 15:36:56.000000,2015-08-16 15:47:20.122000 +72,7478,6297,7167,31,2015-08-16 15:36:56.000000,2015-08-16 15:49:02.122000 +72,30054,6297,7167,32,2015-08-16 15:36:56.000000,2015-08-16 15:51:26.122000 +72,6463,6297,7167,33,2015-08-16 15:36:56.000000,2015-08-16 15:53:02.122000 +72,6464,6297,7167,34,2015-08-16 15:36:56.000000,2015-08-16 15:54:38.122000 +72,30060,6297,7167,35,2015-08-16 15:36:56.000000,2015-08-16 15:56:14.122000 +72,7830,6297,7167,36,2015-08-16 15:36:56.000000,2015-08-16 15:58:38.122000 +72,781,6297,7167,37,2015-08-16 15:36:56.000000,2015-08-16 16:01:26.122000 +72,6452,6297,7167,38,2015-08-16 15:36:56.000000,2015-08-16 16:03:50.122000 +72,12953,6297,7167,39,2015-08-16 15:36:56.000000,2015-08-16 16:05:50.122000 +72,14556,6297,7167,40,2015-08-16 15:36:56.000000,2015-08-16 16:06:26.122000 +72,6471,6297,7167,41,2015-08-16 15:36:56.000000,2015-08-16 16:07:50.122000 +72,28898,6297,7167,42,2015-08-16 15:36:56.000000,2015-08-16 16:10:26.122000 +72,8050,6297,7167,43,2015-08-16 15:36:56.000000,2015-08-16 16:11:26.122000 +72,7061,6297,7167,44,2015-08-16 15:36:56.000000,2015-08-16 16:12:26.122000 +72,7062,6297,7167,45,2015-08-16 15:36:56.000000,2015-08-16 16:13:20.122000 +72,36831,6297,7167,46,2015-08-16 15:36:56.000000,2015-08-16 16:15:06.122000 +72,6476,6297,7167,23,2015-08-16 15:38:03.000000,2015-08-16 15:38:41.435000 +72,6475,6297,7167,24,2015-08-16 15:38:03.000000,2015-08-16 15:39:23.435000 +72,5586,6297,7167,25,2015-08-16 15:38:03.000000,2015-08-16 15:40:47.435000 +72,5590,6297,7167,26,2015-08-16 15:38:03.000000,2015-08-16 15:41:35.435000 +72,40835,6297,7167,27,2015-08-16 15:38:03.000000,2015-08-16 15:43:29.435000 +72,7367,6297,7167,28,2015-08-16 15:38:03.000000,2015-08-16 15:44:59.435000 +72,5580,6297,7167,29,2015-08-16 15:38:03.000000,2015-08-16 15:45:53.435000 +72,7495,6297,7167,30,2015-08-16 15:38:03.000000,2015-08-16 15:46:53.435000 +72,7478,6297,7167,31,2015-08-16 15:38:03.000000,2015-08-16 15:48:35.435000 +72,30054,6297,7167,32,2015-08-16 15:38:03.000000,2015-08-16 15:50:59.435000 +72,6463,6297,7167,33,2015-08-16 15:38:03.000000,2015-08-16 15:52:35.435000 +72,6464,6297,7167,34,2015-08-16 15:38:03.000000,2015-08-16 15:54:11.435000 +72,30060,6297,7167,35,2015-08-16 15:38:03.000000,2015-08-16 15:55:47.435000 +72,7830,6297,7167,36,2015-08-16 15:38:03.000000,2015-08-16 15:58:11.435000 +72,781,6297,7167,37,2015-08-16 15:38:03.000000,2015-08-16 16:00:59.435000 +72,6452,6297,7167,38,2015-08-16 15:38:03.000000,2015-08-16 16:03:23.435000 +72,12953,6297,7167,39,2015-08-16 15:38:03.000000,2015-08-16 16:05:23.435000 +72,14556,6297,7167,40,2015-08-16 15:38:03.000000,2015-08-16 16:05:59.435000 +72,6471,6297,7167,41,2015-08-16 15:38:03.000000,2015-08-16 16:07:23.435000 +72,28898,6297,7167,42,2015-08-16 15:38:03.000000,2015-08-16 16:09:59.435000 +72,8050,6297,7167,43,2015-08-16 15:38:03.000000,2015-08-16 16:10:59.435000 +72,7061,6297,7167,44,2015-08-16 15:38:03.000000,2015-08-16 16:11:59.435000 +72,7062,6297,7167,45,2015-08-16 15:38:03.000000,2015-08-16 16:12:53.435000 +72,36831,6297,7167,46,2015-08-16 15:38:03.000000,2015-08-16 16:14:39.435000 +72,6476,6297,7167,23,2015-08-16 15:38:22.000000,2015-08-16 15:38:51.473000 +72,6475,6297,7167,24,2015-08-16 15:38:22.000000,2015-08-16 15:39:33.473000 +72,5586,6297,7167,25,2015-08-16 15:38:22.000000,2015-08-16 15:40:57.473000 +72,5590,6297,7167,26,2015-08-16 15:38:22.000000,2015-08-16 15:41:45.473000 +72,40835,6297,7167,27,2015-08-16 15:38:22.000000,2015-08-16 15:43:39.473000 +72,7367,6297,7167,28,2015-08-16 15:38:22.000000,2015-08-16 15:45:09.473000 +72,5580,6297,7167,29,2015-08-16 15:38:22.000000,2015-08-16 15:46:03.473000 +72,7495,6297,7167,30,2015-08-16 15:38:22.000000,2015-08-16 15:47:03.473000 +72,7478,6297,7167,31,2015-08-16 15:38:22.000000,2015-08-16 15:48:45.473000 +72,30054,6297,7167,32,2015-08-16 15:38:22.000000,2015-08-16 15:51:09.473000 +72,6463,6297,7167,33,2015-08-16 15:38:22.000000,2015-08-16 15:52:45.473000 +72,6464,6297,7167,34,2015-08-16 15:38:22.000000,2015-08-16 15:54:21.473000 +72,30060,6297,7167,35,2015-08-16 15:38:22.000000,2015-08-16 15:55:57.473000 +72,7830,6297,7167,36,2015-08-16 15:38:22.000000,2015-08-16 15:58:21.473000 +72,781,6297,7167,37,2015-08-16 15:38:22.000000,2015-08-16 16:01:09.473000 +72,6452,6297,7167,38,2015-08-16 15:38:22.000000,2015-08-16 16:03:33.473000 +72,12953,6297,7167,39,2015-08-16 15:38:22.000000,2015-08-16 16:05:33.473000 +72,14556,6297,7167,40,2015-08-16 15:38:22.000000,2015-08-16 16:06:09.473000 +72,6471,6297,7167,41,2015-08-16 15:38:22.000000,2015-08-16 16:07:33.473000 +72,28898,6297,7167,42,2015-08-16 15:38:22.000000,2015-08-16 16:10:09.473000 +72,8050,6297,7167,43,2015-08-16 15:38:22.000000,2015-08-16 16:11:09.473000 +72,7061,6297,7167,44,2015-08-16 15:38:22.000000,2015-08-16 16:12:09.473000 +72,7062,6297,7167,45,2015-08-16 15:38:22.000000,2015-08-16 16:13:03.473000 +72,36831,6297,7167,46,2015-08-16 15:38:22.000000,2015-08-16 16:14:49.473000 +72,5586,6297,7167,25,2015-08-16 15:39:23.000000,2015-08-16 15:39:26.650000 +72,5590,6297,7167,26,2015-08-16 15:39:23.000000,2015-08-16 15:40:14.650000 +72,40835,6297,7167,27,2015-08-16 15:39:23.000000,2015-08-16 15:42:08.650000 +72,7367,6297,7167,28,2015-08-16 15:39:23.000000,2015-08-16 15:43:38.650000 +72,5580,6297,7167,29,2015-08-16 15:39:23.000000,2015-08-16 15:44:32.650000 +72,7495,6297,7167,30,2015-08-16 15:39:23.000000,2015-08-16 15:45:32.650000 +72,7478,6297,7167,31,2015-08-16 15:39:23.000000,2015-08-16 15:47:14.650000 +72,30054,6297,7167,32,2015-08-16 15:39:23.000000,2015-08-16 15:49:38.650000 +72,6463,6297,7167,33,2015-08-16 15:39:23.000000,2015-08-16 15:51:14.650000 +72,6464,6297,7167,34,2015-08-16 15:39:23.000000,2015-08-16 15:52:50.650000 +72,30060,6297,7167,35,2015-08-16 15:39:23.000000,2015-08-16 15:54:26.650000 +72,7830,6297,7167,36,2015-08-16 15:39:23.000000,2015-08-16 15:56:50.650000 +72,781,6297,7167,37,2015-08-16 15:39:23.000000,2015-08-16 15:59:38.650000 +72,6452,6297,7167,38,2015-08-16 15:39:23.000000,2015-08-16 16:02:02.650000 +72,12953,6297,7167,39,2015-08-16 15:39:23.000000,2015-08-16 16:04:02.650000 +72,14556,6297,7167,40,2015-08-16 15:39:23.000000,2015-08-16 16:04:38.650000 +72,6471,6297,7167,41,2015-08-16 15:39:23.000000,2015-08-16 16:06:02.650000 +72,28898,6297,7167,42,2015-08-16 15:39:23.000000,2015-08-16 16:08:38.650000 +72,8050,6297,7167,43,2015-08-16 15:39:23.000000,2015-08-16 16:09:38.650000 +72,7061,6297,7167,44,2015-08-16 15:39:23.000000,2015-08-16 16:10:38.650000 +72,7062,6297,7167,45,2015-08-16 15:39:23.000000,2015-08-16 16:11:32.650000 +72,36831,6297,7167,46,2015-08-16 15:39:23.000000,2015-08-16 16:13:18.650000 +72,5590,6297,7167,26,2015-08-16 15:39:56.000000,2015-08-16 15:40:27.750000 +72,40835,6297,7167,27,2015-08-16 15:39:56.000000,2015-08-16 15:42:21.750000 +72,7367,6297,7167,28,2015-08-16 15:39:56.000000,2015-08-16 15:43:51.750000 +72,5580,6297,7167,29,2015-08-16 15:39:56.000000,2015-08-16 15:44:45.750000 +72,7495,6297,7167,30,2015-08-16 15:39:56.000000,2015-08-16 15:45:45.750000 +72,7478,6297,7167,31,2015-08-16 15:39:56.000000,2015-08-16 15:47:27.750000 +72,30054,6297,7167,32,2015-08-16 15:39:56.000000,2015-08-16 15:49:51.750000 +72,6463,6297,7167,33,2015-08-16 15:39:56.000000,2015-08-16 15:51:27.750000 +72,6464,6297,7167,34,2015-08-16 15:39:56.000000,2015-08-16 15:53:03.750000 +72,30060,6297,7167,35,2015-08-16 15:39:56.000000,2015-08-16 15:54:39.750000 +72,7830,6297,7167,36,2015-08-16 15:39:56.000000,2015-08-16 15:57:03.750000 +72,781,6297,7167,37,2015-08-16 15:39:56.000000,2015-08-16 15:59:51.750000 +72,6452,6297,7167,38,2015-08-16 15:39:56.000000,2015-08-16 16:02:15.750000 +72,12953,6297,7167,39,2015-08-16 15:39:56.000000,2015-08-16 16:04:15.750000 +72,14556,6297,7167,40,2015-08-16 15:39:56.000000,2015-08-16 16:04:51.750000 +72,6471,6297,7167,41,2015-08-16 15:39:56.000000,2015-08-16 16:06:15.750000 +72,28898,6297,7167,42,2015-08-16 15:39:56.000000,2015-08-16 16:08:51.750000 +72,8050,6297,7167,43,2015-08-16 15:39:56.000000,2015-08-16 16:09:51.750000 +72,7061,6297,7167,44,2015-08-16 15:39:56.000000,2015-08-16 16:10:51.750000 +72,7062,6297,7167,45,2015-08-16 15:39:56.000000,2015-08-16 16:11:45.750000 +72,36831,6297,7167,46,2015-08-16 15:39:56.000000,2015-08-16 16:13:31.750000 +72,40835,6297,7167,27,2015-08-16 15:40:28.000000,2015-08-16 15:41:49.316000 +72,7367,6297,7167,28,2015-08-16 15:40:28.000000,2015-08-16 15:43:19.316000 +72,5580,6297,7167,29,2015-08-16 15:40:28.000000,2015-08-16 15:44:13.316000 +72,7495,6297,7167,30,2015-08-16 15:40:28.000000,2015-08-16 15:45:13.316000 +72,7478,6297,7167,31,2015-08-16 15:40:28.000000,2015-08-16 15:46:55.316000 +72,30054,6297,7167,32,2015-08-16 15:40:28.000000,2015-08-16 15:49:19.316000 +72,6463,6297,7167,33,2015-08-16 15:40:28.000000,2015-08-16 15:50:55.316000 +72,6464,6297,7167,34,2015-08-16 15:40:28.000000,2015-08-16 15:52:31.316000 +72,30060,6297,7167,35,2015-08-16 15:40:28.000000,2015-08-16 15:54:07.316000 +72,7830,6297,7167,36,2015-08-16 15:40:28.000000,2015-08-16 15:56:31.316000 +72,781,6297,7167,37,2015-08-16 15:40:28.000000,2015-08-16 15:59:19.316000 +72,6452,6297,7167,38,2015-08-16 15:40:28.000000,2015-08-16 16:01:43.316000 +72,12953,6297,7167,39,2015-08-16 15:40:28.000000,2015-08-16 16:03:43.316000 +72,14556,6297,7167,40,2015-08-16 15:40:28.000000,2015-08-16 16:04:19.316000 +72,6471,6297,7167,41,2015-08-16 15:40:28.000000,2015-08-16 16:05:43.316000 +72,28898,6297,7167,42,2015-08-16 15:40:28.000000,2015-08-16 16:08:19.316000 +72,8050,6297,7167,43,2015-08-16 15:40:28.000000,2015-08-16 16:09:19.316000 +72,7061,6297,7167,44,2015-08-16 15:40:28.000000,2015-08-16 16:10:19.316000 +72,7062,6297,7167,45,2015-08-16 15:40:28.000000,2015-08-16 16:11:13.316000 +72,36831,6297,7167,46,2015-08-16 15:40:28.000000,2015-08-16 16:12:59.316000 +72,40835,6297,7167,27,2015-08-16 15:41:00.000000,2015-08-16 15:41:44.351000 +72,7367,6297,7167,28,2015-08-16 15:41:00.000000,2015-08-16 15:43:14.351000 +72,5580,6297,7167,29,2015-08-16 15:41:00.000000,2015-08-16 15:44:08.351000 +72,7495,6297,7167,30,2015-08-16 15:41:00.000000,2015-08-16 15:45:08.351000 +72,7478,6297,7167,31,2015-08-16 15:41:00.000000,2015-08-16 15:46:50.351000 +72,30054,6297,7167,32,2015-08-16 15:41:00.000000,2015-08-16 15:49:14.351000 +72,6463,6297,7167,33,2015-08-16 15:41:00.000000,2015-08-16 15:50:50.351000 +72,6464,6297,7167,34,2015-08-16 15:41:00.000000,2015-08-16 15:52:26.351000 +72,30060,6297,7167,35,2015-08-16 15:41:00.000000,2015-08-16 15:54:02.351000 +72,7830,6297,7167,36,2015-08-16 15:41:00.000000,2015-08-16 15:56:26.351000 +72,781,6297,7167,37,2015-08-16 15:41:00.000000,2015-08-16 15:59:14.351000 +72,6452,6297,7167,38,2015-08-16 15:41:00.000000,2015-08-16 16:01:38.351000 +72,12953,6297,7167,39,2015-08-16 15:41:00.000000,2015-08-16 16:03:38.351000 +72,14556,6297,7167,40,2015-08-16 15:41:00.000000,2015-08-16 16:04:14.351000 +72,6471,6297,7167,41,2015-08-16 15:41:00.000000,2015-08-16 16:05:38.351000 +72,28898,6297,7167,42,2015-08-16 15:41:00.000000,2015-08-16 16:08:14.351000 +72,8050,6297,7167,43,2015-08-16 15:41:00.000000,2015-08-16 16:09:14.351000 +72,7061,6297,7167,44,2015-08-16 15:41:00.000000,2015-08-16 16:10:14.351000 +72,7062,6297,7167,45,2015-08-16 15:41:00.000000,2015-08-16 16:11:08.351000 +72,36831,6297,7167,46,2015-08-16 15:41:00.000000,2015-08-16 16:12:54.351000 +72,7367,6297,7167,28,2015-08-16 15:41:36.000000,2015-08-16 15:42:49.753000 +72,5580,6297,7167,29,2015-08-16 15:41:36.000000,2015-08-16 15:43:43.753000 +72,7495,6297,7167,30,2015-08-16 15:41:36.000000,2015-08-16 15:44:43.753000 +72,7478,6297,7167,31,2015-08-16 15:41:36.000000,2015-08-16 15:46:25.753000 +72,30054,6297,7167,32,2015-08-16 15:41:36.000000,2015-08-16 15:48:49.753000 +72,6463,6297,7167,33,2015-08-16 15:41:36.000000,2015-08-16 15:50:25.753000 +72,6464,6297,7167,34,2015-08-16 15:41:36.000000,2015-08-16 15:52:01.753000 +72,30060,6297,7167,35,2015-08-16 15:41:36.000000,2015-08-16 15:53:37.753000 +72,7830,6297,7167,36,2015-08-16 15:41:36.000000,2015-08-16 15:56:01.753000 +72,781,6297,7167,37,2015-08-16 15:41:36.000000,2015-08-16 15:58:49.753000 +72,6452,6297,7167,38,2015-08-16 15:41:36.000000,2015-08-16 16:01:13.753000 +72,12953,6297,7167,39,2015-08-16 15:41:36.000000,2015-08-16 16:03:13.753000 +72,14556,6297,7167,40,2015-08-16 15:41:36.000000,2015-08-16 16:03:49.753000 +72,6471,6297,7167,41,2015-08-16 15:41:36.000000,2015-08-16 16:05:13.753000 +72,28898,6297,7167,42,2015-08-16 15:41:36.000000,2015-08-16 16:07:49.753000 +72,8050,6297,7167,43,2015-08-16 15:41:36.000000,2015-08-16 16:08:49.753000 +72,7061,6297,7167,44,2015-08-16 15:41:36.000000,2015-08-16 16:09:49.753000 +72,7062,6297,7167,45,2015-08-16 15:41:36.000000,2015-08-16 16:10:43.753000 +72,36831,6297,7167,46,2015-08-16 15:41:36.000000,2015-08-16 16:12:29.753000 +72,7367,6297,7167,28,2015-08-16 15:42:38.000000,2015-08-16 15:43:23.035000 +72,5580,6297,7167,29,2015-08-16 15:42:38.000000,2015-08-16 15:44:17.035000 +72,7495,6297,7167,30,2015-08-16 15:42:38.000000,2015-08-16 15:45:17.035000 +72,7478,6297,7167,31,2015-08-16 15:42:38.000000,2015-08-16 15:46:59.035000 +72,30054,6297,7167,32,2015-08-16 15:42:38.000000,2015-08-16 15:49:23.035000 +72,6463,6297,7167,33,2015-08-16 15:42:38.000000,2015-08-16 15:50:59.035000 +72,6464,6297,7167,34,2015-08-16 15:42:38.000000,2015-08-16 15:52:35.035000 +72,30060,6297,7167,35,2015-08-16 15:42:38.000000,2015-08-16 15:54:11.035000 +72,7830,6297,7167,36,2015-08-16 15:42:38.000000,2015-08-16 15:56:35.035000 +72,781,6297,7167,37,2015-08-16 15:42:38.000000,2015-08-16 15:59:23.035000 +72,6452,6297,7167,38,2015-08-16 15:42:38.000000,2015-08-16 16:01:47.035000 +72,12953,6297,7167,39,2015-08-16 15:42:38.000000,2015-08-16 16:03:47.035000 +72,14556,6297,7167,40,2015-08-16 15:42:38.000000,2015-08-16 16:04:23.035000 +72,6471,6297,7167,41,2015-08-16 15:42:38.000000,2015-08-16 16:05:47.035000 +72,28898,6297,7167,42,2015-08-16 15:42:38.000000,2015-08-16 16:08:23.035000 +72,8050,6297,7167,43,2015-08-16 15:42:38.000000,2015-08-16 16:09:23.035000 +72,7061,6297,7167,44,2015-08-16 15:42:38.000000,2015-08-16 16:10:23.035000 +72,7062,6297,7167,45,2015-08-16 15:42:38.000000,2015-08-16 16:11:17.035000 +72,36831,6297,7167,46,2015-08-16 15:42:38.000000,2015-08-16 16:13:03.035000 +72,5580,6297,7167,29,2015-08-16 15:43:12.000000,2015-08-16 15:43:25.304000 +72,7495,6297,7167,30,2015-08-16 15:43:12.000000,2015-08-16 15:44:25.304000 +72,7478,6297,7167,31,2015-08-16 15:43:12.000000,2015-08-16 15:46:07.304000 +72,30054,6297,7167,32,2015-08-16 15:43:12.000000,2015-08-16 15:48:31.304000 +72,6463,6297,7167,33,2015-08-16 15:43:12.000000,2015-08-16 15:50:07.304000 +72,6464,6297,7167,34,2015-08-16 15:43:12.000000,2015-08-16 15:51:43.304000 +72,30060,6297,7167,35,2015-08-16 15:43:12.000000,2015-08-16 15:53:19.304000 +72,7830,6297,7167,36,2015-08-16 15:43:12.000000,2015-08-16 15:55:43.304000 +72,781,6297,7167,37,2015-08-16 15:43:12.000000,2015-08-16 15:58:31.304000 +72,6452,6297,7167,38,2015-08-16 15:43:12.000000,2015-08-16 16:00:55.304000 +72,12953,6297,7167,39,2015-08-16 15:43:12.000000,2015-08-16 16:02:55.304000 +72,14556,6297,7167,40,2015-08-16 15:43:12.000000,2015-08-16 16:03:31.304000 +72,6471,6297,7167,41,2015-08-16 15:43:12.000000,2015-08-16 16:04:55.304000 +72,28898,6297,7167,42,2015-08-16 15:43:12.000000,2015-08-16 16:07:31.304000 +72,8050,6297,7167,43,2015-08-16 15:43:12.000000,2015-08-16 16:08:31.304000 +72,7061,6297,7167,44,2015-08-16 15:43:12.000000,2015-08-16 16:09:31.304000 +72,7062,6297,7167,45,2015-08-16 15:43:12.000000,2015-08-16 16:10:25.304000 +72,36831,6297,7167,46,2015-08-16 15:43:12.000000,2015-08-16 16:12:11.304000 +72,7478,6297,7167,31,2015-08-16 15:44:24.000000,2015-08-16 15:45:50.243000 +72,30054,6297,7167,32,2015-08-16 15:44:24.000000,2015-08-16 15:48:14.243000 +72,6463,6297,7167,33,2015-08-16 15:44:24.000000,2015-08-16 15:49:50.243000 +72,6464,6297,7167,34,2015-08-16 15:44:24.000000,2015-08-16 15:51:26.243000 +72,30060,6297,7167,35,2015-08-16 15:44:24.000000,2015-08-16 15:53:02.243000 +72,7830,6297,7167,36,2015-08-16 15:44:24.000000,2015-08-16 15:55:26.243000 +72,781,6297,7167,37,2015-08-16 15:44:24.000000,2015-08-16 15:58:14.243000 +72,6452,6297,7167,38,2015-08-16 15:44:24.000000,2015-08-16 16:00:38.243000 +72,12953,6297,7167,39,2015-08-16 15:44:24.000000,2015-08-16 16:02:38.243000 +72,14556,6297,7167,40,2015-08-16 15:44:24.000000,2015-08-16 16:03:14.243000 +72,6471,6297,7167,41,2015-08-16 15:44:24.000000,2015-08-16 16:04:38.243000 +72,28898,6297,7167,42,2015-08-16 15:44:24.000000,2015-08-16 16:07:14.243000 +72,8050,6297,7167,43,2015-08-16 15:44:24.000000,2015-08-16 16:08:14.243000 +72,7061,6297,7167,44,2015-08-16 15:44:24.000000,2015-08-16 16:09:14.243000 +72,7062,6297,7167,45,2015-08-16 15:44:24.000000,2015-08-16 16:10:08.243000 +72,36831,6297,7167,46,2015-08-16 15:44:24.000000,2015-08-16 16:11:54.243000 +72,7478,6297,7167,31,2015-08-16 15:45:32.000000,2015-08-16 15:45:33.164000 +72,30054,6297,7167,32,2015-08-16 15:45:32.000000,2015-08-16 15:47:57.164000 +72,6463,6297,7167,33,2015-08-16 15:45:32.000000,2015-08-16 15:49:33.164000 +72,6464,6297,7167,34,2015-08-16 15:45:32.000000,2015-08-16 15:51:09.164000 +72,30060,6297,7167,35,2015-08-16 15:45:32.000000,2015-08-16 15:52:45.164000 +72,7830,6297,7167,36,2015-08-16 15:45:32.000000,2015-08-16 15:55:09.164000 +72,781,6297,7167,37,2015-08-16 15:45:32.000000,2015-08-16 15:57:57.164000 +72,6452,6297,7167,38,2015-08-16 15:45:32.000000,2015-08-16 16:00:21.164000 +72,12953,6297,7167,39,2015-08-16 15:45:32.000000,2015-08-16 16:02:21.164000 +72,14556,6297,7167,40,2015-08-16 15:45:32.000000,2015-08-16 16:02:57.164000 +72,6471,6297,7167,41,2015-08-16 15:45:32.000000,2015-08-16 16:04:21.164000 +72,28898,6297,7167,42,2015-08-16 15:45:32.000000,2015-08-16 16:06:57.164000 +72,8050,6297,7167,43,2015-08-16 15:45:32.000000,2015-08-16 16:07:57.164000 +72,7061,6297,7167,44,2015-08-16 15:45:32.000000,2015-08-16 16:08:57.164000 +72,7062,6297,7167,45,2015-08-16 15:45:32.000000,2015-08-16 16:09:51.164000 +72,36831,6297,7167,46,2015-08-16 15:45:32.000000,2015-08-16 16:11:37.164000 +72,30054,6297,7167,32,2015-08-16 15:45:58.000000,2015-08-16 15:48:00.515000 +72,6463,6297,7167,33,2015-08-16 15:45:58.000000,2015-08-16 15:49:36.515000 +72,6464,6297,7167,34,2015-08-16 15:45:58.000000,2015-08-16 15:51:12.515000 +72,30060,6297,7167,35,2015-08-16 15:45:58.000000,2015-08-16 15:52:48.515000 +72,7830,6297,7167,36,2015-08-16 15:45:58.000000,2015-08-16 15:55:12.515000 +72,781,6297,7167,37,2015-08-16 15:45:58.000000,2015-08-16 15:58:00.515000 +72,6452,6297,7167,38,2015-08-16 15:45:58.000000,2015-08-16 16:00:24.515000 +72,12953,6297,7167,39,2015-08-16 15:45:58.000000,2015-08-16 16:02:24.515000 +72,14556,6297,7167,40,2015-08-16 15:45:58.000000,2015-08-16 16:03:00.515000 +72,6471,6297,7167,41,2015-08-16 15:45:58.000000,2015-08-16 16:04:24.515000 +72,28898,6297,7167,42,2015-08-16 15:45:58.000000,2015-08-16 16:07:00.515000 +72,8050,6297,7167,43,2015-08-16 15:45:58.000000,2015-08-16 16:08:00.515000 +72,7061,6297,7167,44,2015-08-16 15:45:58.000000,2015-08-16 16:09:00.515000 +72,7062,6297,7167,45,2015-08-16 15:45:58.000000,2015-08-16 16:09:54.515000 +72,36831,6297,7167,46,2015-08-16 15:45:58.000000,2015-08-16 16:11:40.515000 +72,30054,6297,7167,32,2015-08-16 15:46:32.000000,2015-08-16 15:47:58.543000 +72,6463,6297,7167,33,2015-08-16 15:46:32.000000,2015-08-16 15:49:34.543000 +72,6464,6297,7167,34,2015-08-16 15:46:32.000000,2015-08-16 15:51:10.543000 +72,30060,6297,7167,35,2015-08-16 15:46:32.000000,2015-08-16 15:52:46.543000 +72,7830,6297,7167,36,2015-08-16 15:46:32.000000,2015-08-16 15:55:10.543000 +72,781,6297,7167,37,2015-08-16 15:46:32.000000,2015-08-16 15:57:58.543000 +72,6452,6297,7167,38,2015-08-16 15:46:32.000000,2015-08-16 16:00:22.543000 +72,12953,6297,7167,39,2015-08-16 15:46:32.000000,2015-08-16 16:02:22.543000 +72,14556,6297,7167,40,2015-08-16 15:46:32.000000,2015-08-16 16:02:58.543000 +72,6471,6297,7167,41,2015-08-16 15:46:32.000000,2015-08-16 16:04:22.543000 +72,28898,6297,7167,42,2015-08-16 15:46:32.000000,2015-08-16 16:06:58.543000 +72,8050,6297,7167,43,2015-08-16 15:46:32.000000,2015-08-16 16:07:58.543000 +72,7061,6297,7167,44,2015-08-16 15:46:32.000000,2015-08-16 16:08:58.543000 +72,7062,6297,7167,45,2015-08-16 15:46:32.000000,2015-08-16 16:09:52.543000 +72,36831,6297,7167,46,2015-08-16 15:46:32.000000,2015-08-16 16:11:38.543000 +72,30054,6297,7167,32,2015-08-16 15:47:05.000000,2015-08-16 15:48:16.525000 +72,6463,6297,7167,33,2015-08-16 15:47:05.000000,2015-08-16 15:49:52.525000 +72,6464,6297,7167,34,2015-08-16 15:47:05.000000,2015-08-16 15:51:28.525000 +72,30060,6297,7167,35,2015-08-16 15:47:05.000000,2015-08-16 15:53:04.525000 +72,7830,6297,7167,36,2015-08-16 15:47:05.000000,2015-08-16 15:55:28.525000 +72,781,6297,7167,37,2015-08-16 15:47:05.000000,2015-08-16 15:58:16.525000 +72,6452,6297,7167,38,2015-08-16 15:47:05.000000,2015-08-16 16:00:40.525000 +72,12953,6297,7167,39,2015-08-16 15:47:05.000000,2015-08-16 16:02:40.525000 +72,14556,6297,7167,40,2015-08-16 15:47:05.000000,2015-08-16 16:03:16.525000 +72,6471,6297,7167,41,2015-08-16 15:47:05.000000,2015-08-16 16:04:40.525000 +72,28898,6297,7167,42,2015-08-16 15:47:05.000000,2015-08-16 16:07:16.525000 +72,8050,6297,7167,43,2015-08-16 15:47:05.000000,2015-08-16 16:08:16.525000 +72,7061,6297,7167,44,2015-08-16 15:47:05.000000,2015-08-16 16:09:16.525000 +72,7062,6297,7167,45,2015-08-16 15:47:05.000000,2015-08-16 16:10:10.525000 +72,36831,6297,7167,46,2015-08-16 15:47:05.000000,2015-08-16 16:11:56.525000 +72,30054,6297,7167,32,2015-08-16 15:48:09.000000,2015-08-16 15:48:09.191000 +72,6463,6297,7167,33,2015-08-16 15:48:09.000000,2015-08-16 15:49:45.191000 +72,6464,6297,7167,34,2015-08-16 15:48:09.000000,2015-08-16 15:51:21.191000 +72,30060,6297,7167,35,2015-08-16 15:48:09.000000,2015-08-16 15:52:57.191000 +72,7830,6297,7167,36,2015-08-16 15:48:09.000000,2015-08-16 15:55:21.191000 +72,781,6297,7167,37,2015-08-16 15:48:09.000000,2015-08-16 15:58:09.191000 +72,6452,6297,7167,38,2015-08-16 15:48:09.000000,2015-08-16 16:00:33.191000 +72,12953,6297,7167,39,2015-08-16 15:48:09.000000,2015-08-16 16:02:33.191000 +72,14556,6297,7167,40,2015-08-16 15:48:09.000000,2015-08-16 16:03:09.191000 +72,6471,6297,7167,41,2015-08-16 15:48:09.000000,2015-08-16 16:04:33.191000 +72,28898,6297,7167,42,2015-08-16 15:48:09.000000,2015-08-16 16:07:09.191000 +72,8050,6297,7167,43,2015-08-16 15:48:09.000000,2015-08-16 16:08:09.191000 +72,7061,6297,7167,44,2015-08-16 15:48:09.000000,2015-08-16 16:09:09.191000 +72,7062,6297,7167,45,2015-08-16 15:48:09.000000,2015-08-16 16:10:03.191000 +72,36831,6297,7167,46,2015-08-16 15:48:09.000000,2015-08-16 16:11:49.191000 +72,6463,6297,7167,33,2015-08-16 15:49:12.000000,2015-08-16 15:50:07.724000 +72,6464,6297,7167,34,2015-08-16 15:49:12.000000,2015-08-16 15:51:43.724000 +72,30060,6297,7167,35,2015-08-16 15:49:12.000000,2015-08-16 15:53:19.724000 +72,7830,6297,7167,36,2015-08-16 15:49:12.000000,2015-08-16 15:55:43.724000 +72,781,6297,7167,37,2015-08-16 15:49:12.000000,2015-08-16 15:58:31.724000 +72,6452,6297,7167,38,2015-08-16 15:49:12.000000,2015-08-16 16:00:55.724000 +72,12953,6297,7167,39,2015-08-16 15:49:12.000000,2015-08-16 16:02:55.724000 +72,14556,6297,7167,40,2015-08-16 15:49:12.000000,2015-08-16 16:03:31.724000 +72,6471,6297,7167,41,2015-08-16 15:49:12.000000,2015-08-16 16:04:55.724000 +72,28898,6297,7167,42,2015-08-16 15:49:12.000000,2015-08-16 16:07:31.724000 +72,8050,6297,7167,43,2015-08-16 15:49:12.000000,2015-08-16 16:08:31.724000 +72,7061,6297,7167,44,2015-08-16 15:49:12.000000,2015-08-16 16:09:31.724000 +72,7062,6297,7167,45,2015-08-16 15:49:12.000000,2015-08-16 16:10:25.724000 +72,36831,6297,7167,46,2015-08-16 15:49:12.000000,2015-08-16 16:12:11.724000 +72,6463,6297,7167,33,2015-08-16 15:49:42.000000,2015-08-16 15:50:01.177000 +72,6464,6297,7167,34,2015-08-16 15:49:42.000000,2015-08-16 15:51:37.177000 +72,30060,6297,7167,35,2015-08-16 15:49:42.000000,2015-08-16 15:53:13.177000 +72,7830,6297,7167,36,2015-08-16 15:49:42.000000,2015-08-16 15:55:37.177000 +72,781,6297,7167,37,2015-08-16 15:49:42.000000,2015-08-16 15:58:25.177000 +72,6452,6297,7167,38,2015-08-16 15:49:42.000000,2015-08-16 16:00:49.177000 +72,12953,6297,7167,39,2015-08-16 15:49:42.000000,2015-08-16 16:02:49.177000 +72,14556,6297,7167,40,2015-08-16 15:49:42.000000,2015-08-16 16:03:25.177000 +72,6471,6297,7167,41,2015-08-16 15:49:42.000000,2015-08-16 16:04:49.177000 +72,28898,6297,7167,42,2015-08-16 15:49:42.000000,2015-08-16 16:07:25.177000 +72,8050,6297,7167,43,2015-08-16 15:49:42.000000,2015-08-16 16:08:25.177000 +72,7061,6297,7167,44,2015-08-16 15:49:42.000000,2015-08-16 16:09:25.177000 +72,7062,6297,7167,45,2015-08-16 15:49:42.000000,2015-08-16 16:10:19.177000 +72,36831,6297,7167,46,2015-08-16 15:49:42.000000,2015-08-16 16:12:05.177000 +72,6464,6297,7167,34,2015-08-16 15:50:48.000000,2015-08-16 15:51:41.504000 +72,30060,6297,7167,35,2015-08-16 15:50:48.000000,2015-08-16 15:53:17.504000 +72,7830,6297,7167,36,2015-08-16 15:50:48.000000,2015-08-16 15:55:41.504000 +72,781,6297,7167,37,2015-08-16 15:50:48.000000,2015-08-16 15:58:29.504000 +72,6452,6297,7167,38,2015-08-16 15:50:48.000000,2015-08-16 16:00:53.504000 +72,12953,6297,7167,39,2015-08-16 15:50:48.000000,2015-08-16 16:02:53.504000 +72,14556,6297,7167,40,2015-08-16 15:50:48.000000,2015-08-16 16:03:29.504000 +72,6471,6297,7167,41,2015-08-16 15:50:48.000000,2015-08-16 16:04:53.504000 +72,28898,6297,7167,42,2015-08-16 15:50:48.000000,2015-08-16 16:07:29.504000 +72,8050,6297,7167,43,2015-08-16 15:50:48.000000,2015-08-16 16:08:29.504000 +72,7061,6297,7167,44,2015-08-16 15:50:48.000000,2015-08-16 16:09:29.504000 +72,7062,6297,7167,45,2015-08-16 15:50:48.000000,2015-08-16 16:10:23.504000 +72,36831,6297,7167,46,2015-08-16 15:50:48.000000,2015-08-16 16:12:09.504000 +72,6464,6297,7167,34,2015-08-16 15:51:20.000000,2015-08-16 15:51:56.565000 +72,30060,6297,7167,35,2015-08-16 15:51:20.000000,2015-08-16 15:53:32.565000 +72,7830,6297,7167,36,2015-08-16 15:51:20.000000,2015-08-16 15:55:56.565000 +72,781,6297,7167,37,2015-08-16 15:51:20.000000,2015-08-16 15:58:44.565000 +72,6452,6297,7167,38,2015-08-16 15:51:20.000000,2015-08-16 16:01:08.565000 +72,12953,6297,7167,39,2015-08-16 15:51:20.000000,2015-08-16 16:03:08.565000 +72,14556,6297,7167,40,2015-08-16 15:51:20.000000,2015-08-16 16:03:44.565000 +72,6471,6297,7167,41,2015-08-16 15:51:20.000000,2015-08-16 16:05:08.565000 +72,28898,6297,7167,42,2015-08-16 15:51:20.000000,2015-08-16 16:07:44.565000 +72,8050,6297,7167,43,2015-08-16 15:51:20.000000,2015-08-16 16:08:44.565000 +72,7061,6297,7167,44,2015-08-16 15:51:20.000000,2015-08-16 16:09:44.565000 +72,7062,6297,7167,45,2015-08-16 15:51:20.000000,2015-08-16 16:10:38.565000 +72,36831,6297,7167,46,2015-08-16 15:51:20.000000,2015-08-16 16:12:24.565000 +72,30060,6297,7167,35,2015-08-16 15:51:52.000000,2015-08-16 15:53:17.937000 +72,7830,6297,7167,36,2015-08-16 15:51:52.000000,2015-08-16 15:55:41.937000 +72,781,6297,7167,37,2015-08-16 15:51:52.000000,2015-08-16 15:58:29.937000 +72,6452,6297,7167,38,2015-08-16 15:51:52.000000,2015-08-16 16:00:53.937000 +72,12953,6297,7167,39,2015-08-16 15:51:52.000000,2015-08-16 16:02:53.937000 +72,14556,6297,7167,40,2015-08-16 15:51:52.000000,2015-08-16 16:03:29.937000 +72,6471,6297,7167,41,2015-08-16 15:51:52.000000,2015-08-16 16:04:53.937000 +72,28898,6297,7167,42,2015-08-16 15:51:52.000000,2015-08-16 16:07:29.937000 +72,8050,6297,7167,43,2015-08-16 15:51:52.000000,2015-08-16 16:08:29.937000 +72,7061,6297,7167,44,2015-08-16 15:51:52.000000,2015-08-16 16:09:29.937000 +72,7062,6297,7167,45,2015-08-16 15:51:52.000000,2015-08-16 16:10:23.937000 +72,36831,6297,7167,46,2015-08-16 15:51:52.000000,2015-08-16 16:12:09.937000 +72,30060,6297,7167,35,2015-08-16 15:52:51.000000,2015-08-16 15:53:53.841000 +72,7830,6297,7167,36,2015-08-16 15:52:51.000000,2015-08-16 15:56:17.841000 +72,781,6297,7167,37,2015-08-16 15:52:51.000000,2015-08-16 15:59:05.841000 +72,6452,6297,7167,38,2015-08-16 15:52:51.000000,2015-08-16 16:01:29.841000 +72,12953,6297,7167,39,2015-08-16 15:52:51.000000,2015-08-16 16:03:29.841000 +72,14556,6297,7167,40,2015-08-16 15:52:51.000000,2015-08-16 16:04:05.841000 +72,6471,6297,7167,41,2015-08-16 15:52:51.000000,2015-08-16 16:05:29.841000 +72,28898,6297,7167,42,2015-08-16 15:52:51.000000,2015-08-16 16:08:05.841000 +72,8050,6297,7167,43,2015-08-16 15:52:51.000000,2015-08-16 16:09:05.841000 +72,7061,6297,7167,44,2015-08-16 15:52:51.000000,2015-08-16 16:10:05.841000 +72,7062,6297,7167,45,2015-08-16 15:52:51.000000,2015-08-16 16:10:59.841000 +72,36831,6297,7167,46,2015-08-16 15:52:51.000000,2015-08-16 16:12:45.841000 +72,7830,6297,7167,36,2015-08-16 15:54:57.000000,2015-08-16 15:56:23.785000 +72,781,6297,7167,37,2015-08-16 15:54:57.000000,2015-08-16 15:59:11.785000 +72,6452,6297,7167,38,2015-08-16 15:54:57.000000,2015-08-16 16:01:35.785000 +72,12953,6297,7167,39,2015-08-16 15:54:57.000000,2015-08-16 16:03:35.785000 +72,14556,6297,7167,40,2015-08-16 15:54:57.000000,2015-08-16 16:04:11.785000 +72,6471,6297,7167,41,2015-08-16 15:54:57.000000,2015-08-16 16:05:35.785000 +72,28898,6297,7167,42,2015-08-16 15:54:57.000000,2015-08-16 16:08:11.785000 +72,8050,6297,7167,43,2015-08-16 15:54:57.000000,2015-08-16 16:09:11.785000 +72,7061,6297,7167,44,2015-08-16 15:54:57.000000,2015-08-16 16:10:11.785000 +72,7062,6297,7167,45,2015-08-16 15:54:57.000000,2015-08-16 16:11:05.785000 +72,36831,6297,7167,46,2015-08-16 15:54:57.000000,2015-08-16 16:12:51.785000 +72,7830,6297,7167,36,2015-08-16 15:55:31.000000,2015-08-16 15:56:22.561000 +72,781,6297,7167,37,2015-08-16 15:55:31.000000,2015-08-16 15:59:10.561000 +72,6452,6297,7167,38,2015-08-16 15:55:31.000000,2015-08-16 16:01:34.561000 +72,12953,6297,7167,39,2015-08-16 15:55:31.000000,2015-08-16 16:03:34.561000 +72,14556,6297,7167,40,2015-08-16 15:55:31.000000,2015-08-16 16:04:10.561000 +72,6471,6297,7167,41,2015-08-16 15:55:31.000000,2015-08-16 16:05:34.561000 +72,28898,6297,7167,42,2015-08-16 15:55:31.000000,2015-08-16 16:08:10.561000 +72,8050,6297,7167,43,2015-08-16 15:55:31.000000,2015-08-16 16:09:10.561000 +72,7061,6297,7167,44,2015-08-16 15:55:31.000000,2015-08-16 16:10:10.561000 +72,7062,6297,7167,45,2015-08-16 15:55:31.000000,2015-08-16 16:11:04.561000 +72,36831,6297,7167,46,2015-08-16 15:55:31.000000,2015-08-16 16:12:50.561000 +72,781,6297,7167,37,2015-08-16 15:56:52.000000,2015-08-16 15:59:19.456000 +72,6452,6297,7167,38,2015-08-16 15:56:52.000000,2015-08-16 16:01:43.456000 +72,12953,6297,7167,39,2015-08-16 15:56:52.000000,2015-08-16 16:03:43.456000 +72,14556,6297,7167,40,2015-08-16 15:56:52.000000,2015-08-16 16:04:19.456000 +72,6471,6297,7167,41,2015-08-16 15:56:52.000000,2015-08-16 16:05:43.456000 +72,28898,6297,7167,42,2015-08-16 15:56:52.000000,2015-08-16 16:08:19.456000 +72,8050,6297,7167,43,2015-08-16 15:56:52.000000,2015-08-16 16:09:19.456000 +72,7061,6297,7167,44,2015-08-16 15:56:52.000000,2015-08-16 16:10:19.456000 +72,7062,6297,7167,45,2015-08-16 15:56:52.000000,2015-08-16 16:11:13.456000 +72,36831,6297,7167,46,2015-08-16 15:56:52.000000,2015-08-16 16:12:59.456000 +72,781,6297,7167,37,2015-08-16 15:57:27.000000,2015-08-16 15:59:14.333000 +72,6452,6297,7167,38,2015-08-16 15:57:27.000000,2015-08-16 16:01:38.333000 +72,12953,6297,7167,39,2015-08-16 15:57:27.000000,2015-08-16 16:03:38.333000 +72,14556,6297,7167,40,2015-08-16 15:57:27.000000,2015-08-16 16:04:14.333000 +72,6471,6297,7167,41,2015-08-16 15:57:27.000000,2015-08-16 16:05:38.333000 +72,28898,6297,7167,42,2015-08-16 15:57:27.000000,2015-08-16 16:08:14.333000 +72,8050,6297,7167,43,2015-08-16 15:57:27.000000,2015-08-16 16:09:14.333000 +72,7061,6297,7167,44,2015-08-16 15:57:27.000000,2015-08-16 16:10:14.333000 +72,7062,6297,7167,45,2015-08-16 15:57:27.000000,2015-08-16 16:11:08.333000 +72,36831,6297,7167,46,2015-08-16 15:57:27.000000,2015-08-16 16:12:54.333000 +72,781,6297,7167,37,2015-08-16 15:58:31.000000,2015-08-16 15:58:43.314000 +72,6452,6297,7167,38,2015-08-16 15:58:31.000000,2015-08-16 16:01:07.314000 +72,12953,6297,7167,39,2015-08-16 15:58:31.000000,2015-08-16 16:03:07.314000 +72,14556,6297,7167,40,2015-08-16 15:58:31.000000,2015-08-16 16:03:43.314000 +72,6471,6297,7167,41,2015-08-16 15:58:31.000000,2015-08-16 16:05:07.314000 +72,28898,6297,7167,42,2015-08-16 15:58:31.000000,2015-08-16 16:07:43.314000 +72,8050,6297,7167,43,2015-08-16 15:58:31.000000,2015-08-16 16:08:43.314000 +72,7061,6297,7167,44,2015-08-16 15:58:31.000000,2015-08-16 16:09:43.314000 +72,7062,6297,7167,45,2015-08-16 15:58:31.000000,2015-08-16 16:10:37.314000 +72,36831,6297,7167,46,2015-08-16 15:58:31.000000,2015-08-16 16:12:23.314000 +72,6452,6297,7167,38,2015-08-16 15:59:12.000000,2015-08-16 16:01:18.628000 +72,12953,6297,7167,39,2015-08-16 15:59:12.000000,2015-08-16 16:03:18.628000 +72,14556,6297,7167,40,2015-08-16 15:59:12.000000,2015-08-16 16:03:54.628000 +72,6471,6297,7167,41,2015-08-16 15:59:12.000000,2015-08-16 16:05:18.628000 +72,28898,6297,7167,42,2015-08-16 15:59:12.000000,2015-08-16 16:07:54.628000 +72,8050,6297,7167,43,2015-08-16 15:59:12.000000,2015-08-16 16:08:54.628000 +72,7061,6297,7167,44,2015-08-16 15:59:12.000000,2015-08-16 16:09:54.628000 +72,7062,6297,7167,45,2015-08-16 15:59:12.000000,2015-08-16 16:10:48.628000 +72,36831,6297,7167,46,2015-08-16 15:59:12.000000,2015-08-16 16:12:34.628000 +72,6452,6297,7167,38,2015-08-16 15:59:42.000000,2015-08-16 16:00:04.161000 +72,12953,6297,7167,39,2015-08-16 15:59:42.000000,2015-08-16 16:02:04.161000 +72,14556,6297,7167,40,2015-08-16 15:59:42.000000,2015-08-16 16:02:40.161000 +72,6471,6297,7167,41,2015-08-16 15:59:42.000000,2015-08-16 16:04:04.161000 +72,28898,6297,7167,42,2015-08-16 15:59:42.000000,2015-08-16 16:06:40.161000 +72,8050,6297,7167,43,2015-08-16 15:59:42.000000,2015-08-16 16:07:40.161000 +72,7061,6297,7167,44,2015-08-16 15:59:42.000000,2015-08-16 16:08:40.161000 +72,7062,6297,7167,45,2015-08-16 15:59:42.000000,2015-08-16 16:09:34.161000 +72,36831,6297,7167,46,2015-08-16 15:59:42.000000,2015-08-16 16:11:20.161000 +72,12953,6297,7167,39,2015-08-16 16:00:58.000000,2015-08-16 16:02:40.544000 +72,14556,6297,7167,40,2015-08-16 16:00:58.000000,2015-08-16 16:03:16.544000 +72,6471,6297,7167,41,2015-08-16 16:00:58.000000,2015-08-16 16:04:40.544000 +72,28898,6297,7167,42,2015-08-16 16:00:58.000000,2015-08-16 16:07:16.544000 +72,8050,6297,7167,43,2015-08-16 16:00:58.000000,2015-08-16 16:08:16.544000 +72,7061,6297,7167,44,2015-08-16 16:00:58.000000,2015-08-16 16:09:16.544000 +72,7062,6297,7167,45,2015-08-16 16:00:58.000000,2015-08-16 16:10:10.544000 +72,36831,6297,7167,46,2015-08-16 16:00:58.000000,2015-08-16 16:11:56.544000 +72,12953,6297,7167,39,2015-08-16 16:01:09.000000,2015-08-16 16:02:30.264000 +72,14556,6297,7167,40,2015-08-16 16:01:09.000000,2015-08-16 16:03:06.264000 +72,6471,6297,7167,41,2015-08-16 16:01:09.000000,2015-08-16 16:04:30.264000 +72,28898,6297,7167,42,2015-08-16 16:01:09.000000,2015-08-16 16:07:06.264000 +72,8050,6297,7167,43,2015-08-16 16:01:09.000000,2015-08-16 16:08:06.264000 +72,7061,6297,7167,44,2015-08-16 16:01:09.000000,2015-08-16 16:09:06.264000 +72,7062,6297,7167,45,2015-08-16 16:01:09.000000,2015-08-16 16:10:00.264000 +72,36831,6297,7167,46,2015-08-16 16:01:09.000000,2015-08-16 16:11:46.264000 +72,6471,6297,7167,41,2015-08-16 16:02:32.000000,2015-08-16 16:02:35.422000 +72,28898,6297,7167,42,2015-08-16 16:02:32.000000,2015-08-16 16:05:11.422000 +72,8050,6297,7167,43,2015-08-16 16:02:32.000000,2015-08-16 16:06:11.422000 +72,7061,6297,7167,44,2015-08-16 16:02:32.000000,2015-08-16 16:07:11.422000 +72,7062,6297,7167,45,2015-08-16 16:02:32.000000,2015-08-16 16:08:05.422000 +72,36831,6297,7167,46,2015-08-16 16:02:32.000000,2015-08-16 16:09:51.422000 +72,28898,6297,7167,42,2015-08-16 16:02:41.000000,2015-08-16 16:04:58.658000 +72,8050,6297,7167,43,2015-08-16 16:02:41.000000,2015-08-16 16:05:58.658000 +72,7061,6297,7167,44,2015-08-16 16:02:41.000000,2015-08-16 16:06:58.658000 +72,7062,6297,7167,45,2015-08-16 16:02:41.000000,2015-08-16 16:07:52.658000 +72,36831,6297,7167,46,2015-08-16 16:02:41.000000,2015-08-16 16:09:38.658000 +72,28898,6297,7167,42,2015-08-16 16:03:14.000000,2015-08-16 16:05:21.577000 +72,8050,6297,7167,43,2015-08-16 16:03:14.000000,2015-08-16 16:06:21.577000 +72,7061,6297,7167,44,2015-08-16 16:03:14.000000,2015-08-16 16:07:21.577000 +72,7062,6297,7167,45,2015-08-16 16:03:14.000000,2015-08-16 16:08:15.577000 +72,36831,6297,7167,46,2015-08-16 16:03:14.000000,2015-08-16 16:10:01.577000 +72,28898,6297,7167,42,2015-08-16 16:04:20.000000,2015-08-16 16:05:43.434000 +72,8050,6297,7167,43,2015-08-16 16:04:20.000000,2015-08-16 16:06:43.434000 +72,7061,6297,7167,44,2015-08-16 16:04:20.000000,2015-08-16 16:07:43.434000 +72,7062,6297,7167,45,2015-08-16 16:04:20.000000,2015-08-16 16:08:37.434000 +72,36831,6297,7167,46,2015-08-16 16:04:20.000000,2015-08-16 16:10:23.434000 +71,26871,5925,7167,16,2015-08-16 18:14:56.000000,2015-08-16 18:16:59.674000 +71,8064,5925,7167,17,2015-08-16 18:14:56.000000,2015-08-16 18:18:11.674000 +71,40841,5925,7167,18,2015-08-16 18:14:56.000000,2015-08-16 18:18:53.674000 +71,6183,5925,7167,19,2015-08-16 18:14:56.000000,2015-08-16 18:19:23.674000 +71,26771,5925,7167,20,2015-08-16 18:14:56.000000,2015-08-16 18:20:35.674000 +71,26721,5925,7167,21,2015-08-16 18:14:56.000000,2015-08-16 18:21:47.674000 +71,5455,5925,7167,22,2015-08-16 18:14:56.000000,2015-08-16 18:22:47.674000 +71,6190,5925,7167,23,2015-08-16 18:14:56.000000,2015-08-16 18:23:47.674000 +71,6195,5925,7167,24,2015-08-16 18:14:56.000000,2015-08-16 18:24:41.674000 +71,6194,5925,7167,25,2015-08-16 18:14:56.000000,2015-08-16 18:25:41.674000 +71,26557,5925,7167,26,2015-08-16 18:14:56.000000,2015-08-16 18:26:41.674000 +71,6192,5925,7167,27,2015-08-16 18:14:56.000000,2015-08-16 18:27:17.674000 +71,28801,5925,7167,28,2015-08-16 18:14:56.000000,2015-08-16 18:30:09.674000 +71,26871,5925,7167,16,2015-08-16 18:15:29.000000,2015-08-16 18:17:18.547000 +71,8064,5925,7167,17,2015-08-16 18:15:29.000000,2015-08-16 18:18:30.547000 +71,40841,5925,7167,18,2015-08-16 18:15:29.000000,2015-08-16 18:19:12.547000 +71,6183,5925,7167,19,2015-08-16 18:15:29.000000,2015-08-16 18:19:42.547000 +71,26771,5925,7167,20,2015-08-16 18:15:29.000000,2015-08-16 18:20:54.547000 +71,26721,5925,7167,21,2015-08-16 18:15:29.000000,2015-08-16 18:22:06.547000 +71,5455,5925,7167,22,2015-08-16 18:15:29.000000,2015-08-16 18:23:06.547000 +71,6190,5925,7167,23,2015-08-16 18:15:29.000000,2015-08-16 18:24:06.547000 +71,6195,5925,7167,24,2015-08-16 18:15:29.000000,2015-08-16 18:25:00.547000 +71,6194,5925,7167,25,2015-08-16 18:15:29.000000,2015-08-16 18:26:00.547000 +71,26557,5925,7167,26,2015-08-16 18:15:29.000000,2015-08-16 18:27:00.547000 +71,6192,5925,7167,27,2015-08-16 18:15:29.000000,2015-08-16 18:27:36.547000 +71,28801,5925,7167,28,2015-08-16 18:15:29.000000,2015-08-16 18:30:28.547000 +71,26871,5925,7167,16,2015-08-16 18:16:01.000000,2015-08-16 18:17:17.277000 +71,8064,5925,7167,17,2015-08-16 18:16:01.000000,2015-08-16 18:18:29.277000 +71,40841,5925,7167,18,2015-08-16 18:16:01.000000,2015-08-16 18:19:11.277000 +71,6183,5925,7167,19,2015-08-16 18:16:01.000000,2015-08-16 18:19:41.277000 +71,26771,5925,7167,20,2015-08-16 18:16:01.000000,2015-08-16 18:20:53.277000 +71,26721,5925,7167,21,2015-08-16 18:16:01.000000,2015-08-16 18:22:05.277000 +71,5455,5925,7167,22,2015-08-16 18:16:01.000000,2015-08-16 18:23:05.277000 +71,6190,5925,7167,23,2015-08-16 18:16:01.000000,2015-08-16 18:24:05.277000 +71,6195,5925,7167,24,2015-08-16 18:16:01.000000,2015-08-16 18:24:59.277000 +71,6194,5925,7167,25,2015-08-16 18:16:01.000000,2015-08-16 18:25:59.277000 +71,26557,5925,7167,26,2015-08-16 18:16:01.000000,2015-08-16 18:26:59.277000 +71,6192,5925,7167,27,2015-08-16 18:16:01.000000,2015-08-16 18:27:35.277000 +71,28801,5925,7167,28,2015-08-16 18:16:01.000000,2015-08-16 18:30:27.277000 +71,8064,5925,7167,17,2015-08-16 18:17:07.000000,2015-08-16 18:18:07.520000 +71,40841,5925,7167,18,2015-08-16 18:17:07.000000,2015-08-16 18:18:49.520000 +71,6183,5925,7167,19,2015-08-16 18:17:07.000000,2015-08-16 18:19:19.520000 +71,26771,5925,7167,20,2015-08-16 18:17:07.000000,2015-08-16 18:20:31.520000 +71,26721,5925,7167,21,2015-08-16 18:17:07.000000,2015-08-16 18:21:43.520000 +71,5455,5925,7167,22,2015-08-16 18:17:07.000000,2015-08-16 18:22:43.520000 +71,6190,5925,7167,23,2015-08-16 18:17:07.000000,2015-08-16 18:23:43.520000 +71,6195,5925,7167,24,2015-08-16 18:17:07.000000,2015-08-16 18:24:37.520000 +71,6194,5925,7167,25,2015-08-16 18:17:07.000000,2015-08-16 18:25:37.520000 +71,26557,5925,7167,26,2015-08-16 18:17:07.000000,2015-08-16 18:26:37.520000 +71,6192,5925,7167,27,2015-08-16 18:17:07.000000,2015-08-16 18:27:13.520000 +71,28801,5925,7167,28,2015-08-16 18:17:07.000000,2015-08-16 18:30:05.520000 +71,8064,5925,7167,17,2015-08-16 18:18:03.000000,2015-08-16 18:18:48.492000 +71,40841,5925,7167,18,2015-08-16 18:18:03.000000,2015-08-16 18:19:30.492000 +71,6183,5925,7167,19,2015-08-16 18:18:03.000000,2015-08-16 18:20:00.492000 +71,26771,5925,7167,20,2015-08-16 18:18:03.000000,2015-08-16 18:21:12.492000 +71,26721,5925,7167,21,2015-08-16 18:18:03.000000,2015-08-16 18:22:24.492000 +71,5455,5925,7167,22,2015-08-16 18:18:03.000000,2015-08-16 18:23:24.492000 +71,6190,5925,7167,23,2015-08-16 18:18:03.000000,2015-08-16 18:24:24.492000 +71,6195,5925,7167,24,2015-08-16 18:18:03.000000,2015-08-16 18:25:18.492000 +71,6194,5925,7167,25,2015-08-16 18:18:03.000000,2015-08-16 18:26:18.492000 +71,26557,5925,7167,26,2015-08-16 18:18:03.000000,2015-08-16 18:27:18.492000 +71,6192,5925,7167,27,2015-08-16 18:18:03.000000,2015-08-16 18:27:54.492000 +71,28801,5925,7167,28,2015-08-16 18:18:03.000000,2015-08-16 18:30:46.492000 +71,26557,5925,7167,26,2015-08-16 18:27:38.000000,2015-08-16 18:27:39.507000 +71,6192,5925,7167,27,2015-08-16 18:27:38.000000,2015-08-16 18:28:15.507000 +71,28801,5925,7167,28,2015-08-16 18:27:38.000000,2015-08-16 18:31:07.507000 +71,28801,5925,7167,28,2015-08-16 18:28:22.000000,2015-08-16 18:30:52.285000 +71,28801,5925,7167,28,2015-08-16 18:28:55.000000,2015-08-16 18:30:14.106000 +71,6868,6060,7167,2,2015-08-16 18:44:23.000000,2015-08-16 18:45:40.322000 +71,6869,6060,7167,3,2015-08-16 18:44:23.000000,2015-08-16 18:46:22.322000 +71,8085,6060,7167,4,2015-08-16 18:44:23.000000,2015-08-16 18:46:58.322000 +71,8087,6060,7167,5,2015-08-16 18:44:23.000000,2015-08-16 18:47:52.322000 +71,6191,6060,7167,6,2015-08-16 18:44:23.000000,2015-08-16 18:49:04.322000 +71,6189,6060,7167,7,2015-08-16 18:44:23.000000,2015-08-16 18:50:16.322000 +71,10376,6060,7167,8,2015-08-16 18:44:23.000000,2015-08-16 18:51:04.322000 +71,26719,6060,7167,9,2015-08-16 18:44:23.000000,2015-08-16 18:51:52.322000 +71,26770,6060,7167,10,2015-08-16 18:44:23.000000,2015-08-16 18:53:16.322000 +71,6199,6060,7167,11,2015-08-16 18:44:23.000000,2015-08-16 18:53:58.322000 +71,40840,6060,7167,12,2015-08-16 18:44:23.000000,2015-08-16 18:55:04.322000 +71,8059,6060,7167,13,2015-08-16 18:44:23.000000,2015-08-16 18:55:40.322000 +71,5998,6060,7167,14,2015-08-16 18:44:23.000000,2015-08-16 18:56:40.322000 +71,26877,6060,7167,15,2015-08-16 18:44:23.000000,2015-08-16 18:57:52.322000 +71,6483,6060,7167,16,2015-08-16 18:44:23.000000,2015-08-16 19:00:46.322000 +71,6510,6060,7167,17,2015-08-16 18:44:23.000000,2015-08-16 19:01:46.322000 +71,6481,6060,7167,18,2015-08-16 18:44:23.000000,2015-08-16 19:02:34.322000 +71,8930,6060,7167,19,2015-08-16 18:44:23.000000,2015-08-16 19:03:46.322000 +71,6476,6060,7167,20,2015-08-16 18:44:23.000000,2015-08-16 19:05:34.322000 +71,6475,6060,7167,21,2015-08-16 18:44:23.000000,2015-08-16 19:06:04.322000 +71,5586,6060,7167,22,2015-08-16 18:44:23.000000,2015-08-16 19:07:16.322000 +71,5590,6060,7167,23,2015-08-16 18:44:23.000000,2015-08-16 19:07:58.322000 +71,40835,6060,7167,24,2015-08-16 18:44:23.000000,2015-08-16 19:09:28.322000 +71,7367,6060,7167,25,2015-08-16 18:44:23.000000,2015-08-16 19:10:40.322000 +71,5580,6060,7167,26,2015-08-16 18:44:23.000000,2015-08-16 19:11:22.322000 +71,7495,6060,7167,27,2015-08-16 18:44:23.000000,2015-08-16 19:12:16.322000 +71,7554,6060,7167,28,2015-08-16 18:44:23.000000,2015-08-16 19:14:28.322000 +71,28988,6060,7167,29,2015-08-16 18:44:23.000000,2015-08-16 19:15:44.322000 +71,8085,6060,7167,4,2015-08-16 18:45:16.000000,2015-08-16 18:45:36.751000 +71,8087,6060,7167,5,2015-08-16 18:45:16.000000,2015-08-16 18:46:30.751000 +71,6191,6060,7167,6,2015-08-16 18:45:16.000000,2015-08-16 18:47:42.751000 +71,6189,6060,7167,7,2015-08-16 18:45:16.000000,2015-08-16 18:48:54.751000 +71,10376,6060,7167,8,2015-08-16 18:45:16.000000,2015-08-16 18:49:42.751000 +71,26719,6060,7167,9,2015-08-16 18:45:16.000000,2015-08-16 18:50:30.751000 +71,26770,6060,7167,10,2015-08-16 18:45:16.000000,2015-08-16 18:51:54.751000 +71,6199,6060,7167,11,2015-08-16 18:45:16.000000,2015-08-16 18:52:36.751000 +71,40840,6060,7167,12,2015-08-16 18:45:16.000000,2015-08-16 18:53:42.751000 +71,8059,6060,7167,13,2015-08-16 18:45:16.000000,2015-08-16 18:54:18.751000 +71,5998,6060,7167,14,2015-08-16 18:45:16.000000,2015-08-16 18:55:18.751000 +71,26877,6060,7167,15,2015-08-16 18:45:16.000000,2015-08-16 18:56:30.751000 +71,6483,6060,7167,16,2015-08-16 18:45:16.000000,2015-08-16 18:59:24.751000 +71,6510,6060,7167,17,2015-08-16 18:45:16.000000,2015-08-16 19:00:24.751000 +71,6481,6060,7167,18,2015-08-16 18:45:16.000000,2015-08-16 19:01:12.751000 +71,8930,6060,7167,19,2015-08-16 18:45:16.000000,2015-08-16 19:02:24.751000 +71,6476,6060,7167,20,2015-08-16 18:45:16.000000,2015-08-16 19:04:12.751000 +71,6475,6060,7167,21,2015-08-16 18:45:16.000000,2015-08-16 19:04:42.751000 +71,5586,6060,7167,22,2015-08-16 18:45:16.000000,2015-08-16 19:05:54.751000 +71,5590,6060,7167,23,2015-08-16 18:45:16.000000,2015-08-16 19:06:36.751000 +71,40835,6060,7167,24,2015-08-16 18:45:16.000000,2015-08-16 19:08:06.751000 +71,7367,6060,7167,25,2015-08-16 18:45:16.000000,2015-08-16 19:09:18.751000 +71,5580,6060,7167,26,2015-08-16 18:45:16.000000,2015-08-16 19:10:00.751000 +71,7495,6060,7167,27,2015-08-16 18:45:16.000000,2015-08-16 19:10:54.751000 +71,7554,6060,7167,28,2015-08-16 18:45:16.000000,2015-08-16 19:13:06.751000 +71,28988,6060,7167,29,2015-08-16 18:45:16.000000,2015-08-16 19:14:22.751000 +71,8087,6060,7167,5,2015-08-16 18:45:53.000000,2015-08-16 18:46:31.710000 +71,6191,6060,7167,6,2015-08-16 18:45:53.000000,2015-08-16 18:47:43.710000 +71,6189,6060,7167,7,2015-08-16 18:45:53.000000,2015-08-16 18:48:55.710000 +71,10376,6060,7167,8,2015-08-16 18:45:53.000000,2015-08-16 18:49:43.710000 +71,26719,6060,7167,9,2015-08-16 18:45:53.000000,2015-08-16 18:50:31.710000 +71,26770,6060,7167,10,2015-08-16 18:45:53.000000,2015-08-16 18:51:55.710000 +71,6199,6060,7167,11,2015-08-16 18:45:53.000000,2015-08-16 18:52:37.710000 +71,40840,6060,7167,12,2015-08-16 18:45:53.000000,2015-08-16 18:53:43.710000 +71,8059,6060,7167,13,2015-08-16 18:45:53.000000,2015-08-16 18:54:19.710000 +71,5998,6060,7167,14,2015-08-16 18:45:53.000000,2015-08-16 18:55:19.710000 +71,26877,6060,7167,15,2015-08-16 18:45:53.000000,2015-08-16 18:56:31.710000 +71,6483,6060,7167,16,2015-08-16 18:45:53.000000,2015-08-16 18:59:25.710000 +71,6510,6060,7167,17,2015-08-16 18:45:53.000000,2015-08-16 19:00:25.710000 +71,6481,6060,7167,18,2015-08-16 18:45:53.000000,2015-08-16 19:01:13.710000 +71,8930,6060,7167,19,2015-08-16 18:45:53.000000,2015-08-16 19:02:25.710000 +71,6476,6060,7167,20,2015-08-16 18:45:53.000000,2015-08-16 19:04:13.710000 +71,6475,6060,7167,21,2015-08-16 18:45:53.000000,2015-08-16 19:04:43.710000 +71,5586,6060,7167,22,2015-08-16 18:45:53.000000,2015-08-16 19:05:55.710000 +71,5590,6060,7167,23,2015-08-16 18:45:53.000000,2015-08-16 19:06:37.710000 +71,40835,6060,7167,24,2015-08-16 18:45:53.000000,2015-08-16 19:08:07.710000 +71,7367,6060,7167,25,2015-08-16 18:45:53.000000,2015-08-16 19:09:19.710000 +71,5580,6060,7167,26,2015-08-16 18:45:53.000000,2015-08-16 19:10:01.710000 +71,7495,6060,7167,27,2015-08-16 18:45:53.000000,2015-08-16 19:10:55.710000 +71,7554,6060,7167,28,2015-08-16 18:45:53.000000,2015-08-16 19:13:07.710000 +71,28988,6060,7167,29,2015-08-16 18:45:53.000000,2015-08-16 19:14:23.710000 +71,6191,6060,7167,6,2015-08-16 18:46:26.000000,2015-08-16 18:47:27.721000 +71,6189,6060,7167,7,2015-08-16 18:46:26.000000,2015-08-16 18:48:39.721000 +71,10376,6060,7167,8,2015-08-16 18:46:26.000000,2015-08-16 18:49:27.721000 +71,26719,6060,7167,9,2015-08-16 18:46:26.000000,2015-08-16 18:50:15.721000 +71,26770,6060,7167,10,2015-08-16 18:46:26.000000,2015-08-16 18:51:39.721000 +71,6199,6060,7167,11,2015-08-16 18:46:26.000000,2015-08-16 18:52:21.721000 +71,40840,6060,7167,12,2015-08-16 18:46:26.000000,2015-08-16 18:53:27.721000 +71,8059,6060,7167,13,2015-08-16 18:46:26.000000,2015-08-16 18:54:03.721000 +71,5998,6060,7167,14,2015-08-16 18:46:26.000000,2015-08-16 18:55:03.721000 +71,26877,6060,7167,15,2015-08-16 18:46:26.000000,2015-08-16 18:56:15.721000 +71,6483,6060,7167,16,2015-08-16 18:46:26.000000,2015-08-16 18:59:09.721000 +71,6510,6060,7167,17,2015-08-16 18:46:26.000000,2015-08-16 19:00:09.721000 +71,6481,6060,7167,18,2015-08-16 18:46:26.000000,2015-08-16 19:00:57.721000 +71,8930,6060,7167,19,2015-08-16 18:46:26.000000,2015-08-16 19:02:09.721000 +71,6476,6060,7167,20,2015-08-16 18:46:26.000000,2015-08-16 19:03:57.721000 +71,6475,6060,7167,21,2015-08-16 18:46:26.000000,2015-08-16 19:04:27.721000 +71,5586,6060,7167,22,2015-08-16 18:46:26.000000,2015-08-16 19:05:39.721000 +71,5590,6060,7167,23,2015-08-16 18:46:26.000000,2015-08-16 19:06:21.721000 +71,40835,6060,7167,24,2015-08-16 18:46:26.000000,2015-08-16 19:07:51.721000 +71,7367,6060,7167,25,2015-08-16 18:46:26.000000,2015-08-16 19:09:03.721000 +71,5580,6060,7167,26,2015-08-16 18:46:26.000000,2015-08-16 19:09:45.721000 +71,7495,6060,7167,27,2015-08-16 18:46:26.000000,2015-08-16 19:10:39.721000 +71,7554,6060,7167,28,2015-08-16 18:46:26.000000,2015-08-16 19:12:51.721000 +71,28988,6060,7167,29,2015-08-16 18:46:26.000000,2015-08-16 19:14:07.721000 +71,6483,6060,7167,16,2015-08-16 18:56:07.000000,2015-08-16 18:57:22.854000 +71,6510,6060,7167,17,2015-08-16 18:56:07.000000,2015-08-16 18:58:22.854000 +71,6481,6060,7167,18,2015-08-16 18:56:07.000000,2015-08-16 18:59:10.854000 +71,8930,6060,7167,19,2015-08-16 18:56:07.000000,2015-08-16 19:00:22.854000 +71,6476,6060,7167,20,2015-08-16 18:56:07.000000,2015-08-16 19:02:10.854000 +71,6475,6060,7167,21,2015-08-16 18:56:07.000000,2015-08-16 19:02:40.854000 +71,5586,6060,7167,22,2015-08-16 18:56:07.000000,2015-08-16 19:03:52.854000 +71,5590,6060,7167,23,2015-08-16 18:56:07.000000,2015-08-16 19:04:34.854000 +71,40835,6060,7167,24,2015-08-16 18:56:07.000000,2015-08-16 19:06:04.854000 +71,7367,6060,7167,25,2015-08-16 18:56:07.000000,2015-08-16 19:07:16.854000 +71,5580,6060,7167,26,2015-08-16 18:56:07.000000,2015-08-16 19:07:58.854000 +71,7495,6060,7167,27,2015-08-16 18:56:07.000000,2015-08-16 19:08:52.854000 +71,7554,6060,7167,28,2015-08-16 18:56:07.000000,2015-08-16 19:11:04.854000 +71,28988,6060,7167,29,2015-08-16 18:56:07.000000,2015-08-16 19:12:20.854000 +71,6483,6060,7167,16,2015-08-16 18:56:37.000000,2015-08-16 18:57:13.709000 +71,6510,6060,7167,17,2015-08-16 18:56:37.000000,2015-08-16 18:58:13.709000 +71,6481,6060,7167,18,2015-08-16 18:56:37.000000,2015-08-16 18:59:01.709000 +71,8930,6060,7167,19,2015-08-16 18:56:37.000000,2015-08-16 19:00:13.709000 +71,6476,6060,7167,20,2015-08-16 18:56:37.000000,2015-08-16 19:02:01.709000 +71,6475,6060,7167,21,2015-08-16 18:56:37.000000,2015-08-16 19:02:31.709000 +71,5586,6060,7167,22,2015-08-16 18:56:37.000000,2015-08-16 19:03:43.709000 +71,5590,6060,7167,23,2015-08-16 18:56:37.000000,2015-08-16 19:04:25.709000 +71,40835,6060,7167,24,2015-08-16 18:56:37.000000,2015-08-16 19:05:55.709000 +71,7367,6060,7167,25,2015-08-16 18:56:37.000000,2015-08-16 19:07:07.709000 +71,5580,6060,7167,26,2015-08-16 18:56:37.000000,2015-08-16 19:07:49.709000 +71,7495,6060,7167,27,2015-08-16 18:56:37.000000,2015-08-16 19:08:43.709000 +71,7554,6060,7167,28,2015-08-16 18:56:37.000000,2015-08-16 19:10:55.709000 +71,28988,6060,7167,29,2015-08-16 18:56:37.000000,2015-08-16 19:12:11.709000 +71,6510,6060,7167,17,2015-08-16 18:57:40.000000,2015-08-16 18:58:20.422000 +71,6481,6060,7167,18,2015-08-16 18:57:40.000000,2015-08-16 18:59:08.422000 +71,8930,6060,7167,19,2015-08-16 18:57:40.000000,2015-08-16 19:00:20.422000 +71,6476,6060,7167,20,2015-08-16 18:57:40.000000,2015-08-16 19:02:08.422000 +71,6475,6060,7167,21,2015-08-16 18:57:40.000000,2015-08-16 19:02:38.422000 +71,5586,6060,7167,22,2015-08-16 18:57:40.000000,2015-08-16 19:03:50.422000 +71,5590,6060,7167,23,2015-08-16 18:57:40.000000,2015-08-16 19:04:32.422000 +71,40835,6060,7167,24,2015-08-16 18:57:40.000000,2015-08-16 19:06:02.422000 +71,7367,6060,7167,25,2015-08-16 18:57:40.000000,2015-08-16 19:07:14.422000 +71,5580,6060,7167,26,2015-08-16 18:57:40.000000,2015-08-16 19:07:56.422000 +71,7495,6060,7167,27,2015-08-16 18:57:40.000000,2015-08-16 19:08:50.422000 +71,7554,6060,7167,28,2015-08-16 18:57:40.000000,2015-08-16 19:11:02.422000 +71,28988,6060,7167,29,2015-08-16 18:57:40.000000,2015-08-16 19:12:18.422000 +71,6510,6060,7167,17,2015-08-16 18:58:12.000000,2015-08-16 18:58:52.422000 +71,6481,6060,7167,18,2015-08-16 18:58:12.000000,2015-08-16 18:59:40.422000 +71,8930,6060,7167,19,2015-08-16 18:58:12.000000,2015-08-16 19:00:52.422000 +71,6476,6060,7167,20,2015-08-16 18:58:12.000000,2015-08-16 19:02:40.422000 +71,6475,6060,7167,21,2015-08-16 18:58:12.000000,2015-08-16 19:03:10.422000 +71,5586,6060,7167,22,2015-08-16 18:58:12.000000,2015-08-16 19:04:22.422000 +71,5590,6060,7167,23,2015-08-16 18:58:12.000000,2015-08-16 19:05:04.422000 +71,40835,6060,7167,24,2015-08-16 18:58:12.000000,2015-08-16 19:06:34.422000 +71,7367,6060,7167,25,2015-08-16 18:58:12.000000,2015-08-16 19:07:46.422000 +71,5580,6060,7167,26,2015-08-16 18:58:12.000000,2015-08-16 19:08:28.422000 +71,7495,6060,7167,27,2015-08-16 18:58:12.000000,2015-08-16 19:09:22.422000 +71,7554,6060,7167,28,2015-08-16 18:58:12.000000,2015-08-16 19:11:34.422000 +71,28988,6060,7167,29,2015-08-16 18:58:12.000000,2015-08-16 19:12:50.422000 +71,6510,6060,7167,17,2015-08-16 18:58:43.000000,2015-08-16 18:58:48.431000 +71,6481,6060,7167,18,2015-08-16 18:58:43.000000,2015-08-16 18:59:36.431000 +71,8930,6060,7167,19,2015-08-16 18:58:43.000000,2015-08-16 19:00:48.431000 +71,6476,6060,7167,20,2015-08-16 18:58:43.000000,2015-08-16 19:02:36.431000 +71,6475,6060,7167,21,2015-08-16 18:58:43.000000,2015-08-16 19:03:06.431000 +71,5586,6060,7167,22,2015-08-16 18:58:43.000000,2015-08-16 19:04:18.431000 +71,5590,6060,7167,23,2015-08-16 18:58:43.000000,2015-08-16 19:05:00.431000 +71,40835,6060,7167,24,2015-08-16 18:58:43.000000,2015-08-16 19:06:30.431000 +71,7367,6060,7167,25,2015-08-16 18:58:43.000000,2015-08-16 19:07:42.431000 +71,5580,6060,7167,26,2015-08-16 18:58:43.000000,2015-08-16 19:08:24.431000 +71,7495,6060,7167,27,2015-08-16 18:58:43.000000,2015-08-16 19:09:18.431000 +71,7554,6060,7167,28,2015-08-16 18:58:43.000000,2015-08-16 19:11:30.431000 +71,28988,6060,7167,29,2015-08-16 18:58:43.000000,2015-08-16 19:12:46.431000 +71,8930,6060,7167,19,2015-08-16 18:59:58.000000,2015-08-16 19:00:05.680000 +71,6476,6060,7167,20,2015-08-16 18:59:58.000000,2015-08-16 19:01:53.680000 +71,6475,6060,7167,21,2015-08-16 18:59:58.000000,2015-08-16 19:02:23.680000 +71,5586,6060,7167,22,2015-08-16 18:59:58.000000,2015-08-16 19:03:35.680000 +71,5590,6060,7167,23,2015-08-16 18:59:58.000000,2015-08-16 19:04:17.680000 +71,40835,6060,7167,24,2015-08-16 18:59:58.000000,2015-08-16 19:05:47.680000 +71,7367,6060,7167,25,2015-08-16 18:59:58.000000,2015-08-16 19:06:59.680000 +71,5580,6060,7167,26,2015-08-16 18:59:58.000000,2015-08-16 19:07:41.680000 +71,7495,6060,7167,27,2015-08-16 18:59:58.000000,2015-08-16 19:08:35.680000 +71,7554,6060,7167,28,2015-08-16 18:59:58.000000,2015-08-16 19:10:47.680000 +71,28988,6060,7167,29,2015-08-16 18:59:58.000000,2015-08-16 19:12:03.680000 +71,6476,6060,7167,20,2015-08-16 19:00:05.000000,2015-08-16 19:01:34.134000 +71,6475,6060,7167,21,2015-08-16 19:00:05.000000,2015-08-16 19:02:04.134000 +71,5586,6060,7167,22,2015-08-16 19:00:05.000000,2015-08-16 19:03:16.134000 +71,5590,6060,7167,23,2015-08-16 19:00:05.000000,2015-08-16 19:03:58.134000 +71,40835,6060,7167,24,2015-08-16 19:00:05.000000,2015-08-16 19:05:28.134000 +71,7367,6060,7167,25,2015-08-16 19:00:05.000000,2015-08-16 19:06:40.134000 +71,5580,6060,7167,26,2015-08-16 19:00:05.000000,2015-08-16 19:07:22.134000 +71,7495,6060,7167,27,2015-08-16 19:00:05.000000,2015-08-16 19:08:16.134000 +71,7554,6060,7167,28,2015-08-16 19:00:05.000000,2015-08-16 19:10:28.134000 +71,28988,6060,7167,29,2015-08-16 19:00:05.000000,2015-08-16 19:11:44.134000 +71,6476,6060,7167,20,2015-08-16 19:00:35.000000,2015-08-16 19:01:58.658000 +71,6475,6060,7167,21,2015-08-16 19:00:35.000000,2015-08-16 19:02:28.658000 +71,5586,6060,7167,22,2015-08-16 19:00:35.000000,2015-08-16 19:03:40.658000 +71,5590,6060,7167,23,2015-08-16 19:00:35.000000,2015-08-16 19:04:22.658000 +71,40835,6060,7167,24,2015-08-16 19:00:35.000000,2015-08-16 19:05:52.658000 +71,7367,6060,7167,25,2015-08-16 19:00:35.000000,2015-08-16 19:07:04.658000 +71,5580,6060,7167,26,2015-08-16 19:00:35.000000,2015-08-16 19:07:46.658000 +71,7495,6060,7167,27,2015-08-16 19:00:35.000000,2015-08-16 19:08:40.658000 +71,7554,6060,7167,28,2015-08-16 19:00:35.000000,2015-08-16 19:10:52.658000 +71,28988,6060,7167,29,2015-08-16 19:00:35.000000,2015-08-16 19:12:08.658000 +71,6476,6060,7167,20,2015-08-16 19:01:38.000000,2015-08-16 19:03:01.658000 +71,6475,6060,7167,21,2015-08-16 19:01:38.000000,2015-08-16 19:03:31.658000 +71,5586,6060,7167,22,2015-08-16 19:01:38.000000,2015-08-16 19:04:43.658000 +71,5590,6060,7167,23,2015-08-16 19:01:38.000000,2015-08-16 19:05:25.658000 +71,40835,6060,7167,24,2015-08-16 19:01:38.000000,2015-08-16 19:06:55.658000 +71,7367,6060,7167,25,2015-08-16 19:01:38.000000,2015-08-16 19:08:07.658000 +71,5580,6060,7167,26,2015-08-16 19:01:38.000000,2015-08-16 19:08:49.658000 +71,7495,6060,7167,27,2015-08-16 19:01:38.000000,2015-08-16 19:09:43.658000 +71,7554,6060,7167,28,2015-08-16 19:01:38.000000,2015-08-16 19:11:55.658000 +71,28988,6060,7167,29,2015-08-16 19:01:38.000000,2015-08-16 19:13:11.658000 +71,6476,6060,7167,20,2015-08-16 19:02:41.000000,2015-08-16 19:04:04.658000 +71,6475,6060,7167,21,2015-08-16 19:02:41.000000,2015-08-16 19:04:34.658000 +71,5586,6060,7167,22,2015-08-16 19:02:41.000000,2015-08-16 19:05:46.658000 +71,5590,6060,7167,23,2015-08-16 19:02:41.000000,2015-08-16 19:06:28.658000 +71,40835,6060,7167,24,2015-08-16 19:02:41.000000,2015-08-16 19:07:58.658000 +71,7367,6060,7167,25,2015-08-16 19:02:41.000000,2015-08-16 19:09:10.658000 +71,5580,6060,7167,26,2015-08-16 19:02:41.000000,2015-08-16 19:09:52.658000 +71,7495,6060,7167,27,2015-08-16 19:02:41.000000,2015-08-16 19:10:46.658000 +71,7554,6060,7167,28,2015-08-16 19:02:41.000000,2015-08-16 19:12:58.658000 +71,28988,6060,7167,29,2015-08-16 19:02:41.000000,2015-08-16 19:14:14.658000 +71,6476,6060,7167,20,2015-08-16 19:03:13.000000,2015-08-16 19:04:36.658000 +71,6475,6060,7167,21,2015-08-16 19:03:13.000000,2015-08-16 19:05:06.658000 +71,5586,6060,7167,22,2015-08-16 19:03:13.000000,2015-08-16 19:06:18.658000 +71,5590,6060,7167,23,2015-08-16 19:03:13.000000,2015-08-16 19:07:00.658000 +71,40835,6060,7167,24,2015-08-16 19:03:13.000000,2015-08-16 19:08:30.658000 +71,7367,6060,7167,25,2015-08-16 19:03:13.000000,2015-08-16 19:09:42.658000 +71,5580,6060,7167,26,2015-08-16 19:03:13.000000,2015-08-16 19:10:24.658000 +71,7495,6060,7167,27,2015-08-16 19:03:13.000000,2015-08-16 19:11:18.658000 +71,7554,6060,7167,28,2015-08-16 19:03:13.000000,2015-08-16 19:13:30.658000 +71,28988,6060,7167,29,2015-08-16 19:03:13.000000,2015-08-16 19:14:46.658000 +71,6476,6060,7167,20,2015-08-16 19:03:39.000000,2015-08-16 19:05:02.658000 +71,6475,6060,7167,21,2015-08-16 19:03:39.000000,2015-08-16 19:05:32.658000 +71,5586,6060,7167,22,2015-08-16 19:03:39.000000,2015-08-16 19:06:44.658000 +71,5590,6060,7167,23,2015-08-16 19:03:39.000000,2015-08-16 19:07:26.658000 +71,40835,6060,7167,24,2015-08-16 19:03:39.000000,2015-08-16 19:08:56.658000 +71,7367,6060,7167,25,2015-08-16 19:03:39.000000,2015-08-16 19:10:08.658000 +71,5580,6060,7167,26,2015-08-16 19:03:39.000000,2015-08-16 19:10:50.658000 +71,7495,6060,7167,27,2015-08-16 19:03:39.000000,2015-08-16 19:11:44.658000 +71,7554,6060,7167,28,2015-08-16 19:03:39.000000,2015-08-16 19:13:56.658000 +71,28988,6060,7167,29,2015-08-16 19:03:39.000000,2015-08-16 19:15:12.658000 +71,6476,6060,7167,20,2015-08-16 19:04:11.000000,2015-08-16 19:05:20.656000 +71,6475,6060,7167,21,2015-08-16 19:04:11.000000,2015-08-16 19:05:50.656000 +71,5586,6060,7167,22,2015-08-16 19:04:11.000000,2015-08-16 19:07:02.656000 +71,5590,6060,7167,23,2015-08-16 19:04:11.000000,2015-08-16 19:07:44.656000 +71,40835,6060,7167,24,2015-08-16 19:04:11.000000,2015-08-16 19:09:14.656000 +71,7367,6060,7167,25,2015-08-16 19:04:11.000000,2015-08-16 19:10:26.656000 +71,5580,6060,7167,26,2015-08-16 19:04:11.000000,2015-08-16 19:11:08.656000 +71,7495,6060,7167,27,2015-08-16 19:04:11.000000,2015-08-16 19:12:02.656000 +71,7554,6060,7167,28,2015-08-16 19:04:11.000000,2015-08-16 19:14:14.656000 +71,28988,6060,7167,29,2015-08-16 19:04:11.000000,2015-08-16 19:15:30.656000 +71,5586,6060,7167,22,2015-08-16 19:05:08.000000,2015-08-16 19:06:06.062000 +71,5590,6060,7167,23,2015-08-16 19:05:08.000000,2015-08-16 19:06:48.062000 +71,40835,6060,7167,24,2015-08-16 19:05:08.000000,2015-08-16 19:08:18.062000 +71,7367,6060,7167,25,2015-08-16 19:05:08.000000,2015-08-16 19:09:30.062000 +71,5580,6060,7167,26,2015-08-16 19:05:08.000000,2015-08-16 19:10:12.062000 +71,7495,6060,7167,27,2015-08-16 19:05:08.000000,2015-08-16 19:11:06.062000 +71,7554,6060,7167,28,2015-08-16 19:05:08.000000,2015-08-16 19:13:18.062000 +71,28988,6060,7167,29,2015-08-16 19:05:08.000000,2015-08-16 19:14:34.062000 +71,5586,6060,7167,22,2015-08-16 19:05:38.000000,2015-08-16 19:05:38.596000 +71,5590,6060,7167,23,2015-08-16 19:05:38.000000,2015-08-16 19:06:20.596000 +71,40835,6060,7167,24,2015-08-16 19:05:38.000000,2015-08-16 19:07:50.596000 +71,7367,6060,7167,25,2015-08-16 19:05:38.000000,2015-08-16 19:09:02.596000 +71,5580,6060,7167,26,2015-08-16 19:05:38.000000,2015-08-16 19:09:44.596000 +71,7495,6060,7167,27,2015-08-16 19:05:38.000000,2015-08-16 19:10:38.596000 +71,7554,6060,7167,28,2015-08-16 19:05:38.000000,2015-08-16 19:12:50.596000 +71,28988,6060,7167,29,2015-08-16 19:05:38.000000,2015-08-16 19:14:06.596000 +71,40835,6060,7167,24,2015-08-16 19:06:40.000000,2015-08-16 19:07:27.581000 +71,7367,6060,7167,25,2015-08-16 19:06:40.000000,2015-08-16 19:08:39.581000 +71,5580,6060,7167,26,2015-08-16 19:06:40.000000,2015-08-16 19:09:21.581000 +71,7495,6060,7167,27,2015-08-16 19:06:40.000000,2015-08-16 19:10:15.581000 +71,7554,6060,7167,28,2015-08-16 19:06:40.000000,2015-08-16 19:12:27.581000 +71,28988,6060,7167,29,2015-08-16 19:06:40.000000,2015-08-16 19:13:43.581000 +71,7367,6060,7167,25,2015-08-16 19:07:09.000000,2015-08-16 19:08:05.557000 +71,5580,6060,7167,26,2015-08-16 19:07:09.000000,2015-08-16 19:08:47.557000 +71,7495,6060,7167,27,2015-08-16 19:07:09.000000,2015-08-16 19:09:41.557000 +71,7554,6060,7167,28,2015-08-16 19:07:09.000000,2015-08-16 19:11:53.557000 +71,28988,6060,7167,29,2015-08-16 19:07:09.000000,2015-08-16 19:13:09.557000 +71,5580,6060,7167,26,2015-08-16 19:07:51.000000,2015-08-16 19:08:18.270000 +71,7495,6060,7167,27,2015-08-16 19:07:51.000000,2015-08-16 19:09:12.270000 +71,7554,6060,7167,28,2015-08-16 19:07:51.000000,2015-08-16 19:11:24.270000 +71,28988,6060,7167,29,2015-08-16 19:07:51.000000,2015-08-16 19:12:40.270000 +71,7554,6060,7167,28,2015-08-16 19:08:54.000000,2015-08-16 19:10:48.507000 +71,28988,6060,7167,29,2015-08-16 19:08:54.000000,2015-08-16 19:12:04.507000 +71,7554,6060,7167,28,2015-08-16 19:09:30.000000,2015-08-16 19:10:28.244000 +71,28988,6060,7167,29,2015-08-16 19:09:30.000000,2015-08-16 19:11:44.244000 +71,7554,6060,7167,28,2015-08-16 19:10:02.000000,2015-08-16 19:10:47.541000 +71,28988,6060,7167,29,2015-08-16 19:10:02.000000,2015-08-16 19:12:03.541000 +71,7554,6060,7167,28,2015-08-16 19:10:30.000000,2015-08-16 19:11:11.241000 +71,28988,6060,7167,29,2015-08-16 19:10:30.000000,2015-08-16 19:12:27.241000 +71,28801,6031,7167,28,2015-08-16 21:37:04.000000,2015-08-16 21:39:04.212000 +71,28801,6031,7167,28,2015-08-16 21:38:38.000000,2015-08-16 21:39:39.328000 +178,10792,26872,7167,2,2015-08-18 07:37:43.000000,2015-08-18 07:37:53.599000 +178,4758,26872,7167,3,2015-08-18 07:37:43.000000,2015-08-18 07:38:29.599000 +178,5366,26872,7167,4,2015-08-18 07:37:43.000000,2015-08-18 07:38:59.599000 +178,5367,26872,7167,5,2015-08-18 07:37:43.000000,2015-08-18 07:39:35.599000 +178,5368,26872,7167,6,2015-08-18 07:37:43.000000,2015-08-18 07:40:05.599000 +178,28946,26872,7167,7,2015-08-18 07:37:43.000000,2015-08-18 07:40:35.599000 +178,28020,26872,7167,8,2015-08-18 07:37:43.000000,2015-08-18 07:41:17.599000 +178,1004,26872,7167,9,2015-08-18 07:37:43.000000,2015-08-18 07:41:59.599000 +178,28947,26872,7167,10,2015-08-18 07:37:43.000000,2015-08-18 07:42:47.599000 +178,8136,26872,7167,11,2015-08-18 07:37:43.000000,2015-08-18 07:43:17.599000 +178,3577,26872,7167,12,2015-08-18 07:37:43.000000,2015-08-18 07:44:05.599000 +178,3578,26872,7167,13,2015-08-18 07:37:43.000000,2015-08-18 07:44:35.599000 +178,3259,26872,7167,14,2015-08-18 07:37:43.000000,2015-08-18 07:45:05.599000 +178,1324,26872,7167,15,2015-08-18 07:37:43.000000,2015-08-18 07:45:41.599000 +178,29702,26872,7167,16,2015-08-18 07:37:43.000000,2015-08-18 07:46:35.599000 +178,13220,26872,7167,17,2015-08-18 07:37:43.000000,2015-08-18 07:47:47.599000 +178,270,26872,7167,18,2015-08-18 07:37:43.000000,2015-08-18 07:48:41.599000 +178,28109,26872,7167,19,2015-08-18 07:37:43.000000,2015-08-18 07:49:23.599000 +178,266,26872,7167,20,2015-08-18 07:37:43.000000,2015-08-18 07:50:11.599000 +178,265,26872,7167,21,2015-08-18 07:37:43.000000,2015-08-18 07:51:17.599000 +178,13878,26872,7167,22,2015-08-18 07:37:43.000000,2015-08-18 07:51:59.599000 +178,261,26872,7167,23,2015-08-18 07:37:43.000000,2015-08-18 07:52:29.599000 +178,29550,26872,7167,24,2015-08-18 07:37:43.000000,2015-08-18 07:55:23.599000 +178,1821,26872,7167,25,2015-08-18 07:37:43.000000,2015-08-18 07:56:29.599000 +178,3063,26872,7167,26,2015-08-18 07:37:43.000000,2015-08-18 07:57:29.599000 +178,3064,26872,7167,27,2015-08-18 07:37:43.000000,2015-08-18 07:58:29.599000 +178,3065,26872,7167,28,2015-08-18 07:37:43.000000,2015-08-18 07:59:59.599000 +178,3066,26872,7167,29,2015-08-18 07:37:43.000000,2015-08-18 08:00:47.599000 +178,3067,26872,7167,30,2015-08-18 07:37:43.000000,2015-08-18 08:01:47.599000 +178,610,26872,7167,31,2015-08-18 07:37:43.000000,2015-08-18 08:03:35.599000 +178,29968,26872,7167,32,2015-08-18 07:37:43.000000,2015-08-18 08:03:53.599000 +178,28118,26872,7167,33,2015-08-18 07:37:43.000000,2015-08-18 08:04:41.599000 +178,28117,26872,7167,34,2015-08-18 07:37:43.000000,2015-08-18 08:05:11.599000 +178,28875,26872,7167,35,2015-08-18 07:37:43.000000,2015-08-18 08:06:05.599000 +178,28182,26872,7167,36,2015-08-18 07:37:43.000000,2015-08-18 08:06:47.599000 +178,29591,26872,7167,37,2015-08-18 07:37:43.000000,2015-08-18 08:07:59.599000 +178,29592,26872,7167,38,2015-08-18 07:37:43.000000,2015-08-18 08:08:17.599000 +178,28146,26872,7167,39,2015-08-18 07:37:43.000000,2015-08-18 08:08:29.599000 +178,14695,26872,7167,40,2015-08-18 07:37:43.000000,2015-08-18 08:08:39.890000 +178,255,26872,7167,41,2015-08-18 07:37:43.000000,2015-08-18 08:08:59.893000 +178,251,26872,7167,42,2015-08-18 07:37:43.000000,2015-08-18 08:09:23.893000 +178,247,26872,7167,43,2015-08-18 07:37:43.000000,2015-08-18 08:10:05.893000 +178,29593,26872,7167,44,2015-08-18 07:37:43.000000,2015-08-18 08:10:35.893000 +178,240,26872,7167,45,2015-08-18 07:37:43.000000,2015-08-18 08:10:53.893000 +178,235,26872,7167,46,2015-08-18 07:37:43.000000,2015-08-18 08:11:11.893000 +178,40380,26872,7167,47,2015-08-18 07:37:43.000000,2015-08-18 08:14:47.893000 +178,40409,26872,7167,48,2015-08-18 07:37:43.000000,2015-08-18 08:14:59.893000 +178,40382,26872,7167,49,2015-08-18 07:37:43.000000,2015-08-18 08:16:04.537000 +178,40410,26872,7167,50,2015-08-18 07:37:43.000000,2015-08-18 08:16:46.537000 +178,849,26872,7167,51,2015-08-18 07:37:43.000000,2015-08-18 08:18:04.537000 +178,28874,26872,7167,52,2015-08-18 07:37:43.000000,2015-08-18 08:19:16.537000 +178,20253,26872,7167,53,2015-08-18 07:37:43.000000,2015-08-18 08:19:26.537000 +178,10792,26872,7167,2,2015-08-18 07:38:17.000000,2015-08-18 07:38:27.599000 +178,4758,26872,7167,3,2015-08-18 07:38:17.000000,2015-08-18 07:39:03.599000 +178,5366,26872,7167,4,2015-08-18 07:38:17.000000,2015-08-18 07:39:33.599000 +178,5367,26872,7167,5,2015-08-18 07:38:17.000000,2015-08-18 07:40:09.599000 +178,5368,26872,7167,6,2015-08-18 07:38:17.000000,2015-08-18 07:40:39.599000 +178,28946,26872,7167,7,2015-08-18 07:38:17.000000,2015-08-18 07:41:09.599000 +178,28020,26872,7167,8,2015-08-18 07:38:17.000000,2015-08-18 07:41:51.599000 +178,1004,26872,7167,9,2015-08-18 07:38:17.000000,2015-08-18 07:42:33.599000 +178,28947,26872,7167,10,2015-08-18 07:38:17.000000,2015-08-18 07:43:21.599000 +178,8136,26872,7167,11,2015-08-18 07:38:17.000000,2015-08-18 07:43:51.599000 +178,3577,26872,7167,12,2015-08-18 07:38:17.000000,2015-08-18 07:44:39.599000 +178,3578,26872,7167,13,2015-08-18 07:38:17.000000,2015-08-18 07:45:09.599000 +178,3259,26872,7167,14,2015-08-18 07:38:17.000000,2015-08-18 07:45:39.599000 +178,1324,26872,7167,15,2015-08-18 07:38:17.000000,2015-08-18 07:46:15.599000 +178,29702,26872,7167,16,2015-08-18 07:38:17.000000,2015-08-18 07:47:09.599000 +178,13220,26872,7167,17,2015-08-18 07:38:17.000000,2015-08-18 07:48:21.599000 +178,270,26872,7167,18,2015-08-18 07:38:17.000000,2015-08-18 07:49:15.599000 +178,28109,26872,7167,19,2015-08-18 07:38:17.000000,2015-08-18 07:49:57.599000 +178,266,26872,7167,20,2015-08-18 07:38:17.000000,2015-08-18 07:50:45.599000 +178,265,26872,7167,21,2015-08-18 07:38:17.000000,2015-08-18 07:51:51.599000 +178,13878,26872,7167,22,2015-08-18 07:38:17.000000,2015-08-18 07:52:33.599000 +178,261,26872,7167,23,2015-08-18 07:38:17.000000,2015-08-18 07:53:03.599000 +178,29550,26872,7167,24,2015-08-18 07:38:17.000000,2015-08-18 07:55:57.599000 +178,1821,26872,7167,25,2015-08-18 07:38:17.000000,2015-08-18 07:57:03.599000 +178,3063,26872,7167,26,2015-08-18 07:38:17.000000,2015-08-18 07:58:03.599000 +178,3064,26872,7167,27,2015-08-18 07:38:17.000000,2015-08-18 07:59:03.599000 +178,3065,26872,7167,28,2015-08-18 07:38:17.000000,2015-08-18 08:00:33.599000 +178,3066,26872,7167,29,2015-08-18 07:38:17.000000,2015-08-18 08:01:21.599000 +178,3067,26872,7167,30,2015-08-18 07:38:17.000000,2015-08-18 08:02:21.599000 +178,610,26872,7167,31,2015-08-18 07:38:17.000000,2015-08-18 08:04:09.599000 +178,29968,26872,7167,32,2015-08-18 07:38:17.000000,2015-08-18 08:04:27.599000 +178,28118,26872,7167,33,2015-08-18 07:38:17.000000,2015-08-18 08:05:15.599000 +178,28117,26872,7167,34,2015-08-18 07:38:17.000000,2015-08-18 08:05:45.599000 +178,28875,26872,7167,35,2015-08-18 07:38:17.000000,2015-08-18 08:06:39.599000 +178,28182,26872,7167,36,2015-08-18 07:38:17.000000,2015-08-18 08:07:21.599000 +178,29591,26872,7167,37,2015-08-18 07:38:17.000000,2015-08-18 08:08:33.599000 +178,29592,26872,7167,38,2015-08-18 07:38:17.000000,2015-08-18 08:08:51.599000 +178,28146,26872,7167,39,2015-08-18 07:38:17.000000,2015-08-18 08:09:03.599000 +178,14695,26872,7167,40,2015-08-18 07:38:17.000000,2015-08-18 08:09:13.890000 +178,255,26872,7167,41,2015-08-18 07:38:17.000000,2015-08-18 08:09:33.893000 +178,251,26872,7167,42,2015-08-18 07:38:17.000000,2015-08-18 08:09:57.893000 +178,247,26872,7167,43,2015-08-18 07:38:17.000000,2015-08-18 08:10:39.893000 +178,29593,26872,7167,44,2015-08-18 07:38:17.000000,2015-08-18 08:11:09.893000 +178,240,26872,7167,45,2015-08-18 07:38:17.000000,2015-08-18 08:11:27.893000 +178,235,26872,7167,46,2015-08-18 07:38:17.000000,2015-08-18 08:11:45.893000 +178,40380,26872,7167,47,2015-08-18 07:38:17.000000,2015-08-18 08:15:21.893000 +178,40409,26872,7167,48,2015-08-18 07:38:17.000000,2015-08-18 08:15:33.893000 +178,40382,26872,7167,49,2015-08-18 07:38:17.000000,2015-08-18 08:16:38.537000 +178,40410,26872,7167,50,2015-08-18 07:38:17.000000,2015-08-18 08:17:20.537000 +178,849,26872,7167,51,2015-08-18 07:38:17.000000,2015-08-18 08:18:38.537000 +178,28874,26872,7167,52,2015-08-18 07:38:17.000000,2015-08-18 08:19:50.537000 +178,20253,26872,7167,53,2015-08-18 07:38:17.000000,2015-08-18 08:20:00.537000 +178,10792,26872,7167,2,2015-08-18 07:38:49.000000,2015-08-18 07:38:59.599000 +178,4758,26872,7167,3,2015-08-18 07:38:49.000000,2015-08-18 07:39:35.599000 +178,5366,26872,7167,4,2015-08-18 07:38:49.000000,2015-08-18 07:40:05.599000 +178,5367,26872,7167,5,2015-08-18 07:38:49.000000,2015-08-18 07:40:41.599000 +178,5368,26872,7167,6,2015-08-18 07:38:49.000000,2015-08-18 07:41:11.599000 +178,28946,26872,7167,7,2015-08-18 07:38:49.000000,2015-08-18 07:41:41.599000 +178,28020,26872,7167,8,2015-08-18 07:38:49.000000,2015-08-18 07:42:23.599000 +178,1004,26872,7167,9,2015-08-18 07:38:49.000000,2015-08-18 07:43:05.599000 +178,28947,26872,7167,10,2015-08-18 07:38:49.000000,2015-08-18 07:43:53.599000 +178,8136,26872,7167,11,2015-08-18 07:38:49.000000,2015-08-18 07:44:23.599000 +178,3577,26872,7167,12,2015-08-18 07:38:49.000000,2015-08-18 07:45:11.599000 +178,3578,26872,7167,13,2015-08-18 07:38:49.000000,2015-08-18 07:45:41.599000 +178,3259,26872,7167,14,2015-08-18 07:38:49.000000,2015-08-18 07:46:11.599000 +178,1324,26872,7167,15,2015-08-18 07:38:49.000000,2015-08-18 07:46:47.599000 +178,29702,26872,7167,16,2015-08-18 07:38:49.000000,2015-08-18 07:47:41.599000 +178,13220,26872,7167,17,2015-08-18 07:38:49.000000,2015-08-18 07:48:53.599000 +178,270,26872,7167,18,2015-08-18 07:38:49.000000,2015-08-18 07:49:47.599000 +178,28109,26872,7167,19,2015-08-18 07:38:49.000000,2015-08-18 07:50:29.599000 +178,266,26872,7167,20,2015-08-18 07:38:49.000000,2015-08-18 07:51:17.599000 +178,265,26872,7167,21,2015-08-18 07:38:49.000000,2015-08-18 07:52:23.599000 +178,13878,26872,7167,22,2015-08-18 07:38:49.000000,2015-08-18 07:53:05.599000 +178,261,26872,7167,23,2015-08-18 07:38:49.000000,2015-08-18 07:53:35.599000 +178,29550,26872,7167,24,2015-08-18 07:38:49.000000,2015-08-18 07:56:29.599000 +178,1821,26872,7167,25,2015-08-18 07:38:49.000000,2015-08-18 07:57:35.599000 +178,3063,26872,7167,26,2015-08-18 07:38:49.000000,2015-08-18 07:58:35.599000 +178,3064,26872,7167,27,2015-08-18 07:38:49.000000,2015-08-18 07:59:35.599000 +178,3065,26872,7167,28,2015-08-18 07:38:49.000000,2015-08-18 08:01:05.599000 +178,3066,26872,7167,29,2015-08-18 07:38:49.000000,2015-08-18 08:01:53.599000 +178,3067,26872,7167,30,2015-08-18 07:38:49.000000,2015-08-18 08:02:53.599000 +178,610,26872,7167,31,2015-08-18 07:38:49.000000,2015-08-18 08:04:41.599000 +178,29968,26872,7167,32,2015-08-18 07:38:49.000000,2015-08-18 08:04:59.599000 +178,28118,26872,7167,33,2015-08-18 07:38:49.000000,2015-08-18 08:05:47.599000 +178,28117,26872,7167,34,2015-08-18 07:38:49.000000,2015-08-18 08:06:17.599000 +178,28875,26872,7167,35,2015-08-18 07:38:49.000000,2015-08-18 08:07:11.599000 +178,28182,26872,7167,36,2015-08-18 07:38:49.000000,2015-08-18 08:07:53.599000 +178,29591,26872,7167,37,2015-08-18 07:38:49.000000,2015-08-18 08:09:05.599000 +178,29592,26872,7167,38,2015-08-18 07:38:49.000000,2015-08-18 08:09:23.599000 +178,28146,26872,7167,39,2015-08-18 07:38:49.000000,2015-08-18 08:09:35.599000 +178,14695,26872,7167,40,2015-08-18 07:38:49.000000,2015-08-18 08:09:45.890000 +178,255,26872,7167,41,2015-08-18 07:38:49.000000,2015-08-18 08:10:05.893000 +178,251,26872,7167,42,2015-08-18 07:38:49.000000,2015-08-18 08:10:29.893000 +178,247,26872,7167,43,2015-08-18 07:38:49.000000,2015-08-18 08:11:11.893000 +178,29593,26872,7167,44,2015-08-18 07:38:49.000000,2015-08-18 08:11:41.893000 +178,240,26872,7167,45,2015-08-18 07:38:49.000000,2015-08-18 08:11:59.893000 +178,235,26872,7167,46,2015-08-18 07:38:49.000000,2015-08-18 08:12:17.893000 +178,40380,26872,7167,47,2015-08-18 07:38:49.000000,2015-08-18 08:15:53.893000 +178,40409,26872,7167,48,2015-08-18 07:38:49.000000,2015-08-18 08:16:05.893000 +178,40382,26872,7167,49,2015-08-18 07:38:49.000000,2015-08-18 08:17:10.537000 +178,40410,26872,7167,50,2015-08-18 07:38:49.000000,2015-08-18 08:17:52.537000 +178,849,26872,7167,51,2015-08-18 07:38:49.000000,2015-08-18 08:19:10.537000 +178,28874,26872,7167,52,2015-08-18 07:38:49.000000,2015-08-18 08:20:22.537000 +178,20253,26872,7167,53,2015-08-18 07:38:49.000000,2015-08-18 08:20:32.537000 +178,10792,26872,7167,2,2015-08-18 07:39:21.000000,2015-08-18 07:39:31.599000 +178,4758,26872,7167,3,2015-08-18 07:39:21.000000,2015-08-18 07:40:07.599000 +178,5366,26872,7167,4,2015-08-18 07:39:21.000000,2015-08-18 07:40:37.599000 +178,5367,26872,7167,5,2015-08-18 07:39:21.000000,2015-08-18 07:41:13.599000 +178,5368,26872,7167,6,2015-08-18 07:39:21.000000,2015-08-18 07:41:43.599000 +178,28946,26872,7167,7,2015-08-18 07:39:21.000000,2015-08-18 07:42:13.599000 +178,28020,26872,7167,8,2015-08-18 07:39:21.000000,2015-08-18 07:42:55.599000 +178,1004,26872,7167,9,2015-08-18 07:39:21.000000,2015-08-18 07:43:37.599000 +178,28947,26872,7167,10,2015-08-18 07:39:21.000000,2015-08-18 07:44:25.599000 +178,8136,26872,7167,11,2015-08-18 07:39:21.000000,2015-08-18 07:44:55.599000 +178,3577,26872,7167,12,2015-08-18 07:39:21.000000,2015-08-18 07:45:43.599000 +178,3578,26872,7167,13,2015-08-18 07:39:21.000000,2015-08-18 07:46:13.599000 +178,3259,26872,7167,14,2015-08-18 07:39:21.000000,2015-08-18 07:46:43.599000 +178,1324,26872,7167,15,2015-08-18 07:39:21.000000,2015-08-18 07:47:19.599000 +178,29702,26872,7167,16,2015-08-18 07:39:21.000000,2015-08-18 07:48:13.599000 +178,13220,26872,7167,17,2015-08-18 07:39:21.000000,2015-08-18 07:49:25.599000 +178,270,26872,7167,18,2015-08-18 07:39:21.000000,2015-08-18 07:50:19.599000 +178,28109,26872,7167,19,2015-08-18 07:39:21.000000,2015-08-18 07:51:01.599000 +178,266,26872,7167,20,2015-08-18 07:39:21.000000,2015-08-18 07:51:49.599000 +178,265,26872,7167,21,2015-08-18 07:39:21.000000,2015-08-18 07:52:55.599000 +178,13878,26872,7167,22,2015-08-18 07:39:21.000000,2015-08-18 07:53:37.599000 +178,261,26872,7167,23,2015-08-18 07:39:21.000000,2015-08-18 07:54:07.599000 +178,29550,26872,7167,24,2015-08-18 07:39:21.000000,2015-08-18 07:57:01.599000 +178,1821,26872,7167,25,2015-08-18 07:39:21.000000,2015-08-18 07:58:07.599000 +178,3063,26872,7167,26,2015-08-18 07:39:21.000000,2015-08-18 07:59:07.599000 +178,3064,26872,7167,27,2015-08-18 07:39:21.000000,2015-08-18 08:00:07.599000 +178,3065,26872,7167,28,2015-08-18 07:39:21.000000,2015-08-18 08:01:37.599000 +178,3066,26872,7167,29,2015-08-18 07:39:21.000000,2015-08-18 08:02:25.599000 +178,3067,26872,7167,30,2015-08-18 07:39:21.000000,2015-08-18 08:03:25.599000 +178,610,26872,7167,31,2015-08-18 07:39:21.000000,2015-08-18 08:05:13.599000 +178,29968,26872,7167,32,2015-08-18 07:39:21.000000,2015-08-18 08:05:31.599000 +178,28118,26872,7167,33,2015-08-18 07:39:21.000000,2015-08-18 08:06:19.599000 +178,28117,26872,7167,34,2015-08-18 07:39:21.000000,2015-08-18 08:06:49.599000 +178,28875,26872,7167,35,2015-08-18 07:39:21.000000,2015-08-18 08:07:43.599000 +178,28182,26872,7167,36,2015-08-18 07:39:21.000000,2015-08-18 08:08:25.599000 +178,29591,26872,7167,37,2015-08-18 07:39:21.000000,2015-08-18 08:09:37.599000 +178,29592,26872,7167,38,2015-08-18 07:39:21.000000,2015-08-18 08:09:55.599000 +178,28146,26872,7167,39,2015-08-18 07:39:21.000000,2015-08-18 08:10:07.599000 +178,14695,26872,7167,40,2015-08-18 07:39:21.000000,2015-08-18 08:10:17.890000 +178,255,26872,7167,41,2015-08-18 07:39:21.000000,2015-08-18 08:10:37.893000 +178,251,26872,7167,42,2015-08-18 07:39:21.000000,2015-08-18 08:11:01.893000 +178,247,26872,7167,43,2015-08-18 07:39:21.000000,2015-08-18 08:11:43.893000 +178,29593,26872,7167,44,2015-08-18 07:39:21.000000,2015-08-18 08:12:13.893000 +178,240,26872,7167,45,2015-08-18 07:39:21.000000,2015-08-18 08:12:31.893000 +178,235,26872,7167,46,2015-08-18 07:39:21.000000,2015-08-18 08:12:49.893000 +178,40380,26872,7167,47,2015-08-18 07:39:21.000000,2015-08-18 08:16:25.893000 +178,40409,26872,7167,48,2015-08-18 07:39:21.000000,2015-08-18 08:16:37.893000 +178,40382,26872,7167,49,2015-08-18 07:39:21.000000,2015-08-18 08:17:42.537000 +178,40410,26872,7167,50,2015-08-18 07:39:21.000000,2015-08-18 08:18:24.537000 +178,849,26872,7167,51,2015-08-18 07:39:21.000000,2015-08-18 08:19:42.537000 +178,28874,26872,7167,52,2015-08-18 07:39:21.000000,2015-08-18 08:20:54.537000 +178,20253,26872,7167,53,2015-08-18 07:39:21.000000,2015-08-18 08:21:04.537000 +178,10792,26872,7167,2,2015-08-18 07:40:14.000000,2015-08-18 07:40:24.599000 +178,4758,26872,7167,3,2015-08-18 07:40:14.000000,2015-08-18 07:41:00.599000 +178,5366,26872,7167,4,2015-08-18 07:40:14.000000,2015-08-18 07:41:30.599000 +178,5367,26872,7167,5,2015-08-18 07:40:14.000000,2015-08-18 07:42:06.599000 +178,5368,26872,7167,6,2015-08-18 07:40:14.000000,2015-08-18 07:42:36.599000 +178,28946,26872,7167,7,2015-08-18 07:40:14.000000,2015-08-18 07:43:06.599000 +178,28020,26872,7167,8,2015-08-18 07:40:14.000000,2015-08-18 07:43:48.599000 +178,1004,26872,7167,9,2015-08-18 07:40:14.000000,2015-08-18 07:44:30.599000 +178,28947,26872,7167,10,2015-08-18 07:40:14.000000,2015-08-18 07:45:18.599000 +178,8136,26872,7167,11,2015-08-18 07:40:14.000000,2015-08-18 07:45:48.599000 +178,3577,26872,7167,12,2015-08-18 07:40:14.000000,2015-08-18 07:46:36.599000 +178,3578,26872,7167,13,2015-08-18 07:40:14.000000,2015-08-18 07:47:06.599000 +178,3259,26872,7167,14,2015-08-18 07:40:14.000000,2015-08-18 07:47:36.599000 +178,1324,26872,7167,15,2015-08-18 07:40:14.000000,2015-08-18 07:48:12.599000 +178,29702,26872,7167,16,2015-08-18 07:40:14.000000,2015-08-18 07:49:06.599000 +178,13220,26872,7167,17,2015-08-18 07:40:14.000000,2015-08-18 07:50:18.599000 +178,270,26872,7167,18,2015-08-18 07:40:14.000000,2015-08-18 07:51:12.599000 +178,28109,26872,7167,19,2015-08-18 07:40:14.000000,2015-08-18 07:51:54.599000 +178,266,26872,7167,20,2015-08-18 07:40:14.000000,2015-08-18 07:52:42.599000 +178,265,26872,7167,21,2015-08-18 07:40:14.000000,2015-08-18 07:53:48.599000 +178,13878,26872,7167,22,2015-08-18 07:40:14.000000,2015-08-18 07:54:30.599000 +178,261,26872,7167,23,2015-08-18 07:40:14.000000,2015-08-18 07:55:00.599000 +178,29550,26872,7167,24,2015-08-18 07:40:14.000000,2015-08-18 07:57:54.599000 +178,1821,26872,7167,25,2015-08-18 07:40:14.000000,2015-08-18 07:59:00.599000 +178,3063,26872,7167,26,2015-08-18 07:40:14.000000,2015-08-18 08:00:00.599000 +178,3064,26872,7167,27,2015-08-18 07:40:14.000000,2015-08-18 08:01:00.599000 +178,3065,26872,7167,28,2015-08-18 07:40:14.000000,2015-08-18 08:02:30.599000 +178,3066,26872,7167,29,2015-08-18 07:40:14.000000,2015-08-18 08:03:18.599000 +178,3067,26872,7167,30,2015-08-18 07:40:14.000000,2015-08-18 08:04:18.599000 +178,610,26872,7167,31,2015-08-18 07:40:14.000000,2015-08-18 08:06:06.599000 +178,29968,26872,7167,32,2015-08-18 07:40:14.000000,2015-08-18 08:06:24.599000 +178,28118,26872,7167,33,2015-08-18 07:40:14.000000,2015-08-18 08:07:12.599000 +178,28117,26872,7167,34,2015-08-18 07:40:14.000000,2015-08-18 08:07:42.599000 +178,28875,26872,7167,35,2015-08-18 07:40:14.000000,2015-08-18 08:08:36.599000 +178,28182,26872,7167,36,2015-08-18 07:40:14.000000,2015-08-18 08:09:18.599000 +178,29591,26872,7167,37,2015-08-18 07:40:14.000000,2015-08-18 08:10:30.599000 +178,29592,26872,7167,38,2015-08-18 07:40:14.000000,2015-08-18 08:10:48.599000 +178,28146,26872,7167,39,2015-08-18 07:40:14.000000,2015-08-18 08:11:00.599000 +178,14695,26872,7167,40,2015-08-18 07:40:14.000000,2015-08-18 08:11:10.890000 +178,255,26872,7167,41,2015-08-18 07:40:14.000000,2015-08-18 08:11:30.893000 +178,251,26872,7167,42,2015-08-18 07:40:14.000000,2015-08-18 08:11:54.893000 +178,247,26872,7167,43,2015-08-18 07:40:14.000000,2015-08-18 08:12:36.893000 +178,29593,26872,7167,44,2015-08-18 07:40:14.000000,2015-08-18 08:13:06.893000 +178,240,26872,7167,45,2015-08-18 07:40:14.000000,2015-08-18 08:13:24.893000 +178,235,26872,7167,46,2015-08-18 07:40:14.000000,2015-08-18 08:13:42.893000 +178,40380,26872,7167,47,2015-08-18 07:40:14.000000,2015-08-18 08:17:18.893000 +178,40409,26872,7167,48,2015-08-18 07:40:14.000000,2015-08-18 08:17:30.893000 +178,40382,26872,7167,49,2015-08-18 07:40:14.000000,2015-08-18 08:18:35.537000 +178,40410,26872,7167,50,2015-08-18 07:40:14.000000,2015-08-18 08:19:17.537000 +178,849,26872,7167,51,2015-08-18 07:40:14.000000,2015-08-18 08:20:35.537000 +178,28874,26872,7167,52,2015-08-18 07:40:14.000000,2015-08-18 08:21:47.537000 +178,20253,26872,7167,53,2015-08-18 07:40:14.000000,2015-08-18 08:21:57.537000 +178,10792,26872,7167,2,2015-08-18 07:41:43.000000,2015-08-18 07:41:53.599000 +178,4758,26872,7167,3,2015-08-18 07:41:43.000000,2015-08-18 07:42:29.599000 +178,5366,26872,7167,4,2015-08-18 07:41:43.000000,2015-08-18 07:42:59.599000 +178,5367,26872,7167,5,2015-08-18 07:41:43.000000,2015-08-18 07:43:35.599000 +178,5368,26872,7167,6,2015-08-18 07:41:43.000000,2015-08-18 07:44:05.599000 +178,28946,26872,7167,7,2015-08-18 07:41:43.000000,2015-08-18 07:44:35.599000 +178,28020,26872,7167,8,2015-08-18 07:41:43.000000,2015-08-18 07:45:17.599000 +178,1004,26872,7167,9,2015-08-18 07:41:43.000000,2015-08-18 07:45:59.599000 +178,28947,26872,7167,10,2015-08-18 07:41:43.000000,2015-08-18 07:46:47.599000 +178,8136,26872,7167,11,2015-08-18 07:41:43.000000,2015-08-18 07:47:17.599000 +178,3577,26872,7167,12,2015-08-18 07:41:43.000000,2015-08-18 07:48:05.599000 +178,3578,26872,7167,13,2015-08-18 07:41:43.000000,2015-08-18 07:48:35.599000 +178,3259,26872,7167,14,2015-08-18 07:41:43.000000,2015-08-18 07:49:05.599000 +178,1324,26872,7167,15,2015-08-18 07:41:43.000000,2015-08-18 07:49:41.599000 +178,29702,26872,7167,16,2015-08-18 07:41:43.000000,2015-08-18 07:50:35.599000 +178,13220,26872,7167,17,2015-08-18 07:41:43.000000,2015-08-18 07:51:47.599000 +178,270,26872,7167,18,2015-08-18 07:41:43.000000,2015-08-18 07:52:41.599000 +178,28109,26872,7167,19,2015-08-18 07:41:43.000000,2015-08-18 07:53:23.599000 +178,266,26872,7167,20,2015-08-18 07:41:43.000000,2015-08-18 07:54:11.599000 +178,265,26872,7167,21,2015-08-18 07:41:43.000000,2015-08-18 07:55:17.599000 +178,13878,26872,7167,22,2015-08-18 07:41:43.000000,2015-08-18 07:55:59.599000 +178,261,26872,7167,23,2015-08-18 07:41:43.000000,2015-08-18 07:56:29.599000 +178,29550,26872,7167,24,2015-08-18 07:41:43.000000,2015-08-18 07:59:23.599000 +178,1821,26872,7167,25,2015-08-18 07:41:43.000000,2015-08-18 08:00:29.599000 +178,3063,26872,7167,26,2015-08-18 07:41:43.000000,2015-08-18 08:01:29.599000 +178,3064,26872,7167,27,2015-08-18 07:41:43.000000,2015-08-18 08:02:29.599000 +178,3065,26872,7167,28,2015-08-18 07:41:43.000000,2015-08-18 08:03:59.599000 +178,3066,26872,7167,29,2015-08-18 07:41:43.000000,2015-08-18 08:04:47.599000 +178,3067,26872,7167,30,2015-08-18 07:41:43.000000,2015-08-18 08:05:47.599000 +178,610,26872,7167,31,2015-08-18 07:41:43.000000,2015-08-18 08:07:35.599000 diff --git a/transitclock/src/test/resources/hsql_hibernate.cfg.xml b/transitclock/src/test/resources/hsql_hibernate.cfg.xml new file mode 100644 index 000000000..1c7ed3986 --- /dev/null +++ b/transitclock/src/test/resources/hsql_hibernate.cfg.xml @@ -0,0 +1,95 @@ + + + + + + + + org.hibernate.dialect.HSQLDialect + + + org.hsqldb.jdbc.JDBCDriver + + + + true + true + true + + + + + + + + 100 + 100 + true + true + true + update + + + + + + + jdbc:hsqldb:mem://localhost/xdb + + + jdbc:hsqldb:mem://localhost/xdb + + + SA + + + + + + + + + + + + diff --git a/transitime/src/main/resources/postgres_hibernate.cfg.xml b/transitclock/src/test/resources/hsql_integration_test_hibernate.cfg.xml similarity index 76% rename from transitime/src/main/resources/postgres_hibernate.cfg.xml rename to transitclock/src/test/resources/hsql_integration_test_hibernate.cfg.xml index d50e7f382..0b9f1e8bc 100644 --- a/transitime/src/main/resources/postgres_hibernate.cfg.xml +++ b/transitclock/src/test/resources/hsql_integration_test_hibernate.cfg.xml @@ -6,10 +6,10 @@ - org.hibernate.dialect.PostgreSQLDialect + org.hibernate.dialect.HSQLDialect - org.postgresql.Driver + org.hsqldb.jdbc.JDBCDriver - 2 - + + connections. –> 300 - - 50 + easily speed things up. –> + 50 --> - 25 + 100 + 100 + true + true + create + true + + - + - + diff --git a/transitclock/src/test/resources/logback-test.xml b/transitclock/src/test/resources/logback-test.xml new file mode 100644 index 000000000..0d9839cb9 --- /dev/null +++ b/transitclock/src/test/resources/logback-test.xml @@ -0,0 +1,18 @@ + + + + + + + + + %d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n + + + + + + + + + \ No newline at end of file diff --git a/transitclock/src/test/resources/named_queries.hbm.xml b/transitclock/src/test/resources/named_queries.hbm.xml new file mode 100644 index 000000000..8c8b38b82 --- /dev/null +++ b/transitclock/src/test/resources/named_queries.hbm.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/transitclock/src/test/resources/testConfig.xml b/transitclock/src/test/resources/testConfig.xml new file mode 100755 index 000000000..8169c29ec --- /dev/null +++ b/transitclock/src/test/resources/testConfig.xml @@ -0,0 +1,66 @@ + + + + + + + + org.transitclock.avl.GtfsRealtimeModule;org.transitclock.core.predAccuracy.gtfsrt.GTFSRealtimePredictionAccuracyModule + + + 30000 + + http://realtime.prod.obahart.org:8088/trip-updates + 60 + 45 + 10 + + + + true + false + 600 + 600 + + + org.transitclock.core.predictiongenerator.average.HistoricalAveragePredictionGeneratorImpl + 15000 + + + + http://realtime.prod.obahart.org:8088/vehicle-positions + http://realtime.prod.obahart.org:8088/vehicle-positions + + + + + + + 30 + + + + + GOHART + 192.168.99.100:5432 + postgresql + postgres + transitime + + + + C:\\cygwin64\\home\\SeanOg\\workspace\\transitime-all\\transitime\\src\\test\\resources\\postgres_hibernate.cfg.xml + + + + diff --git a/transitclock/src/test/resources/transiTimeConfigIntegrationTest.xml b/transitclock/src/test/resources/transiTimeConfigIntegrationTest.xml new file mode 100755 index 000000000..a1240c7e6 --- /dev/null +++ b/transitclock/src/test/resources/transiTimeConfigIntegrationTest.xml @@ -0,0 +1,39 @@ + + + + + + 30000 + 5 + + + false + false + 180 + 180 + + + 1 + + + + http://developer.onebusaway.org/wmata-gtfsr/vehiclePositions + + + false + -360.0 + 360.0 + -360.0 + 360.0 + + + + + 30000 + 180000 + + + + transitclock/src/test/resources/mysql_integration_test_hibernate.cfg.xml + + diff --git a/transitclock/src/test/resources/transiTimeconfig.xml b/transitclock/src/test/resources/transiTimeconfig.xml new file mode 100644 index 000000000..dad9100e5 --- /dev/null +++ b/transitclock/src/test/resources/transiTimeconfig.xml @@ -0,0 +1,25 @@ + + + + org.transitclock.avl.GtfsRealtimeModule + + true + + 02 + + + http://realtime.prod.obahart.org:8088/vehicle-positions + http://realtime.prod.obahart.org:8088/vehicle-positions + + + + + hsql_hibernate.cfg.xml + + + 02 + + \ No newline at end of file diff --git a/transitclockApi/.gitignore b/transitclockApi/.gitignore new file mode 100755 index 000000000..a2604ae61 --- /dev/null +++ b/transitclockApi/.gitignore @@ -0,0 +1,12 @@ +*.class +*.project +*.classpath +*.iml +.idea/ +# Package Files # +*.jar +*.war +*.ear +/target/ +/bin/ +/.settings/ \ No newline at end of file diff --git a/transitclockApi/README.md b/transitclockApi/README.md new file mode 100644 index 000000000..eee13558e --- /dev/null +++ b/transitclockApi/README.md @@ -0,0 +1,29 @@ +This is the a REST service which provides the information required to run a web application or mobile application based on TheTransitClock. + +This can be built on its own by +``` +cd transitclockApi +mvn install +``` + +This will produce a api.war file which can be deployed on Tomcat. + +You will need to configure the location of the transitclockConfig.xml file as a command line argument: + +`-Dtransitclock.configFiles=/path/to/your/transitclockConfig.xml` + +The exact place to do this depends on how you're running TheTransitClock. In Eclipse, add this as a VM argument in the run configuration for Tomcat. In a bash script, add it to `CATALINA_OPTS` before Tomcat starts up. + +This server talks to core using RMI calls to get the information to support the REST service calls. + +To access the service a key is required to be provided in the URL. This key is compared against a key in the database. You can use the CreateAPIKey application in TheTransitClock to create a test/demo key. + +The tables that store this information are create by running the ddl_xxxx_org_transitime_db_webstructs.sql in the database. (Where xxxx is the type of database you are using) +``` +Example URLs + +http://[server]:[port]/v1/transitime/key/[Key from CreateAPIKey]/agency/[agency id]/command/gtfs-rt/tripUpdates?format=human + +http://127.0.0.1:8093/v1/transitime/key/8a3273b0/agency/02/command/gtfs-rt/tripUpdates?format=human +``` +The comments in the supporting classes are the best source of information for RESTFul calls. diff --git a/transitimeApi/.gitignore b/transitclockApi/bin/.gitignore similarity index 95% rename from transitimeApi/.gitignore rename to transitclockApi/bin/.gitignore index 731eb433c..1d79693a8 100644 --- a/transitimeApi/.gitignore +++ b/transitclockApi/bin/.gitignore @@ -1,2 +1,3 @@ /target/ /.settings/ + diff --git a/transitclockApi/pom.xml b/transitclockApi/pom.xml new file mode 100755 index 000000000..ba298ed39 --- /dev/null +++ b/transitclockApi/pom.xml @@ -0,0 +1,118 @@ + + 4.0.0 + + TheTransitClock + transitclock + 2.0.49-cs-SNAPSHOT + + war + transitclockApi + transitclockApi + + UTF-8 + 1.8 + 1.8 + + + + javax.servlet + javax.servlet-api + 3.0.1 + provided + + + + javax.ws.rs + javax.ws.rs-api + 2.0 + + + + org.glassfish.jersey.media + jersey-media-moxy + 2.24.1 + + + + ch.qos.logback + logback-classic + 1.2.3 + + + org.glassfish.jersey.containers + jersey-container-servlet + 2.24.1 + + + TheTransitClock + transitclockCore + 2.0.49-cs-SNAPSHOT + + + com.amazonaws + aws-java-sdk + + + TheTransitClock + transitclockBarefootClient + + + TheTransitClock + transitclockTraccarClient + + + + + com.google.guava + guava + 20.0 + + + org.glassfish.jersey.bundles.repackaged + jersey-guava + 2.6 + + + + + + + + io.swagger.core.v3 + swagger-jaxrs2 + 2.0.2 + + + + org.webjars + swagger-ui + 3.17.1 + + + TheTransitClock + transitclockCore + 2.0.49-cs-SNAPSHOT + compile + + + + + api + + + org.apache.maven.plugins + maven-compiler-plugin + 2.5.1 + true + + 1.8 + 1.8 + + + + + \ No newline at end of file diff --git a/transitimeApi/src/main/java/org/transitime/api/ApiApplication.java b/transitclockApi/src/main/java/org/transitclock/api/ApiApplication.java similarity index 88% rename from transitimeApi/src/main/java/org/transitime/api/ApiApplication.java rename to transitclockApi/src/main/java/org/transitclock/api/ApiApplication.java index f3fe22a71..c4cd37373 100644 --- a/transitimeApi/src/main/java/org/transitime/api/ApiApplication.java +++ b/transitclockApi/src/main/java/org/transitclock/api/ApiApplication.java @@ -15,14 +15,14 @@ * Transitime.org . If not, see . */ -package org.transitime.api; +package org.transitclock.api; import javax.ws.rs.ApplicationPath; import org.glassfish.jersey.server.ResourceConfig; /** - * Declares that all classes in package org.transitime.api.rootResources will be + * Declares that all classes in package org.transitclock.api.rootResources will be * searched for being a root-resource class with methods annotated with @Path * indicating that it handles requests. * @@ -37,6 +37,6 @@ public class ApiApplication extends ResourceConfig { public ApiApplication() { // Register all root-resource classes in package that handle @Path // requests - packages("org.transitime.api.rootResources"); + packages("org.transitclock.api.rootResources"); } } \ No newline at end of file diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiActiveBlock.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiActiveBlock.java similarity index 87% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiActiveBlock.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiActiveBlock.java index 20def6ade..6bb7d75b1 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiActiveBlock.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiActiveBlock.java @@ -15,8 +15,9 @@ * along with Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; +import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -24,11 +25,11 @@ import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; -import org.transitime.db.webstructs.WebAgency; -import org.transitime.ipc.data.IpcActiveBlock; -import org.transitime.ipc.data.IpcTrip; -import org.transitime.ipc.data.IpcVehicle; -import org.transitime.utils.Time; +import org.transitclock.db.webstructs.WebAgency; +import org.transitclock.ipc.data.IpcActiveBlock; +import org.transitclock.ipc.data.IpcTrip; +import org.transitclock.ipc.data.IpcVehicle; +import org.transitclock.utils.Time; /** * @@ -65,7 +66,7 @@ public class ApiActiveBlock { */ protected ApiActiveBlock() {} - public ApiActiveBlock(IpcActiveBlock ipcActiveBlock, String agencyId) { + public ApiActiveBlock(IpcActiveBlock ipcActiveBlock, String agencyId) throws IllegalAccessException, InvocationTargetException { id = ipcActiveBlock.getBlock().getId(); serviceId = ipcActiveBlock.getBlock().getServiceId(); startTime = Time.timeOfDayStr(ipcActiveBlock.getBlock().getStartTime()); diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiActiveBlocks.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiActiveBlocks.java similarity index 91% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiActiveBlocks.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiActiveBlocks.java index 8a7e62a38..788d03728 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiActiveBlocks.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiActiveBlocks.java @@ -15,8 +15,9 @@ * along with Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; +import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -26,7 +27,7 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; -import org.transitime.ipc.data.IpcActiveBlock; +import org.transitclock.ipc.data.IpcActiveBlock; /** * Collection of ActiveBlocks @@ -49,7 +50,7 @@ public class ApiActiveBlocks { protected ApiActiveBlocks() {} public ApiActiveBlocks(Collection ipcActiveBlocks, - String agencyId) { + String agencyId) throws IllegalAccessException, InvocationTargetException { activeBlocks = new ArrayList(); for (IpcActiveBlock ipcActiveBlock : ipcActiveBlocks) { activeBlocks.add(new ApiActiveBlock(ipcActiveBlock, agencyId)); diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiActiveBlocksRoute.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiActiveBlocksRoute.java similarity index 90% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiActiveBlocksRoute.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiActiveBlocksRoute.java index 66f52e20c..a01092fa5 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiActiveBlocksRoute.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiActiveBlocksRoute.java @@ -15,15 +15,16 @@ * along with Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; +import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.List; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; -import org.transitime.ipc.data.IpcActiveBlock; +import org.transitclock.ipc.data.IpcActiveBlock; /** * A route for when outputting active blocks @@ -66,7 +67,7 @@ public ApiActiveBlocksRoute(String id, String shortName, String name) { activeBlocks = new ArrayList(); } - public void add(IpcActiveBlock ipcActiveBlock, String agencyId) { + public void add(IpcActiveBlock ipcActiveBlock, String agencyId) throws IllegalAccessException, InvocationTargetException { activeBlocks.add(new ApiActiveBlock(ipcActiveBlock, agencyId)); } diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiActiveBlocksRoutes.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiActiveBlocksRoutes.java similarity index 87% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiActiveBlocksRoutes.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiActiveBlocksRoutes.java index f47d0e30b..4240e1f14 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiActiveBlocksRoutes.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiActiveBlocksRoutes.java @@ -15,8 +15,9 @@ * along with Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; +import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -24,8 +25,8 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; -import org.transitime.ipc.data.IpcActiveBlock; -import org.transitime.ipc.data.IpcTrip; +import org.transitclock.ipc.data.IpcActiveBlock; +import org.transitclock.ipc.data.IpcTrip; /** * A list of routes for when outputting active blocks @@ -54,9 +55,11 @@ protected ApiActiveBlocksRoutes() { * * @param activeBlocks Already ordered list of active blocks * @param agencyId + * @throws InvocationTargetException + * @throws IllegalAccessException */ public ApiActiveBlocksRoutes(Collection activeBlocks, - String agencyId) { + String agencyId) throws IllegalAccessException, InvocationTargetException { routeData = new ArrayList(); ApiActiveBlocksRoute apiRoute = null; diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/ApiAdherenceSummary.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiAdherenceSummary.java new file mode 100644 index 000000000..7157a2aa9 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiAdherenceSummary.java @@ -0,0 +1,39 @@ +package org.transitclock.api.data; + +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + +@XmlRootElement(name = "adherenceSummary") +public class ApiAdherenceSummary { + + @XmlAttribute + private Integer late; + + @XmlAttribute + private Integer ontime; + + @XmlAttribute + private Integer early; + + @XmlAttribute + private Integer nodata; + + @XmlAttribute + private Integer blocks; + + /** + * Need a no-arg constructor for Jersey. Otherwise get really obtuse + * "MessageBodyWriter not found for media type=application/json" exception. + */ + protected ApiAdherenceSummary() { + } + + public ApiAdherenceSummary(int late, int ontime, int early, int nodata, int blocks) { + this.late = new Integer(late); + this.ontime = new Integer(ontime); + this.early = new Integer(early); + this.nodata = new Integer(nodata); + this.blocks = new Integer(blocks); + } +} diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiAgencies.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiAgencies.java similarity index 97% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiAgencies.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiAgencies.java index 934afaff6..aa68896d1 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiAgencies.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiAgencies.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import java.util.List; diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiAgency.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiAgency.java similarity index 97% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiAgency.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiAgency.java index 83ec49b3f..dcc868bd3 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiAgency.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiAgency.java @@ -15,14 +15,14 @@ * along with Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import java.util.TimeZone; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; -import org.transitime.db.structs.Agency; +import org.transitclock.db.structs.Agency; /** * Contains API info for an agency. diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/ApiArrivalDeparture.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiArrivalDeparture.java new file mode 100755 index 000000000..a0acb36d2 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiArrivalDeparture.java @@ -0,0 +1,49 @@ +package org.transitclock.api.data; + +import java.lang.reflect.InvocationTargetException; +import java.util.Date; + +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; + +import org.transitclock.ipc.data.IpcArrivalDeparture; + +@XmlRootElement(name = "arrivaldeparture") +public class ApiArrivalDeparture { + @XmlAttribute + private String stopId; + @XmlAttribute + private String vehicleId; + @XmlAttribute + private Date time; + @XmlAttribute + private Date scheduledTime; + @XmlAttribute + private boolean isArrival; + @XmlAttribute + private String tripId; + @XmlAttribute + private String routeId; + @XmlAttribute + private Integer stopPathIndex; + /** + * Need a no-arg constructor for Jersey. Otherwise get really obtuse + * "MessageBodyWriter not found for media type=application/json" exception. + */ + protected ApiArrivalDeparture() { + } + + public ApiArrivalDeparture(IpcArrivalDeparture ipcArrivalDeparture) throws IllegalAccessException, InvocationTargetException { + this.vehicleId=ipcArrivalDeparture.getVehicleId(); + this.time=ipcArrivalDeparture.getTime(); + this.routeId=ipcArrivalDeparture.getRouteId(); + this.tripId=ipcArrivalDeparture.getTripId(); + this.isArrival=ipcArrivalDeparture.isArrival(); + this.stopId=ipcArrivalDeparture.getStopId(); + this.stopPathIndex=ipcArrivalDeparture.getStopPathIndex(); + + // TODO + //this.scheduledTime=ipcArrivalDeparture.getScheduledTime(); + } + +} diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/ApiArrivalDepartures.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiArrivalDepartures.java new file mode 100755 index 000000000..5cb7dddc4 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiArrivalDepartures.java @@ -0,0 +1,67 @@ +/* + * 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.api.data; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +import org.transitclock.ipc.data.IpcArrivalDeparture; +import org.transitclock.ipc.data.IpcRouteSummary; + +/** + * An ordered list of routes. + * + * @author SkiBu Smith + * + */ +@XmlRootElement(name = "arrivalDepartures") +public class ApiArrivalDepartures { + + @XmlElement(name = "arrivalDeparture") + private List arrivalDeparturesData; + + /********************** Member Functions **************************/ + + /** + * Need a no-arg constructor for Jersey. Otherwise get really obtuse + * "MessageBodyWriter not found for media type=application/json" exception. + */ + protected ApiArrivalDepartures() { + } + + /** + * Constructs an ApiRouteSummaries using a collection of IpcRouteSummary + * objects. + * + * @param arrivalDepartures + * @throws InvocationTargetException + * @throws IllegalAccessException + */ + public ApiArrivalDepartures(Collection arrivalDepartures) throws IllegalAccessException, InvocationTargetException { + arrivalDeparturesData = new ArrayList(); + for (IpcArrivalDeparture arrivalDeparture : arrivalDepartures) { + ApiArrivalDeparture apiArrivalDeparture = new ApiArrivalDeparture(arrivalDeparture); + arrivalDeparturesData.add(apiArrivalDeparture); + } + } +} diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/ApiAverageRunTime.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiAverageRunTime.java new file mode 100644 index 000000000..c97f9003d --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiAverageRunTime.java @@ -0,0 +1,63 @@ +/* + * 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.api.data; + +import org.transitclock.ipc.data.IpcDoubleSummaryStatistics; +import org.transitclock.utils.MathUtils; + +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * For outputting average run time value + + * @author Lenny Caraballo + * + */ +@XmlRootElement +public class ApiAverageRunTime { + + @XmlAttribute + private Double averageRunTime; + + @XmlAttribute + private int numberOfTrips; + + /********************** Member Functions **************************/ + + /** + * Need a no-arg constructor for Jersey. Otherwise get really obtuse + * "MessageBodyWriter not found for media type=application/json" exception. + */ + protected ApiAverageRunTime() { + } + + /** + * Creates API to display average run time value. + * @param summaryStatistics + */ + public ApiAverageRunTime(IpcDoubleSummaryStatistics summaryStatistics) { + this.numberOfTrips = (int) summaryStatistics.getCount(); + if(numberOfTrips == 0){ + this.averageRunTime = null; + } else{ + this.averageRunTime = MathUtils.round(summaryStatistics.getAverage(), 0); + } + + } + +} diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiBlock.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiBlock.java similarity index 91% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiBlock.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiBlock.java index 59000520d..4c8d12e99 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiBlock.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiBlock.java @@ -15,7 +15,7 @@ * Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import java.util.ArrayList; import java.util.List; @@ -24,10 +24,10 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; -import org.transitime.ipc.data.IpcBlock; -import org.transitime.ipc.data.IpcRouteSummary; -import org.transitime.ipc.data.IpcTrip; -import org.transitime.utils.Time; +import org.transitclock.ipc.data.IpcBlock; +import org.transitclock.ipc.data.IpcRouteSummary; +import org.transitclock.ipc.data.IpcTrip; +import org.transitclock.utils.Time; /** * Describes a block diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiBlockTerse.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiBlockTerse.java similarity index 88% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiBlockTerse.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiBlockTerse.java index da136ff5d..fc7e69c83 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiBlockTerse.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiBlockTerse.java @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License along with * Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import java.util.ArrayList; import java.util.List; @@ -22,10 +22,10 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; -import org.transitime.ipc.data.IpcBlock; -import org.transitime.ipc.data.IpcRouteSummary; -import org.transitime.ipc.data.IpcTrip; -import org.transitime.utils.Time; +import org.transitclock.ipc.data.IpcBlock; +import org.transitclock.ipc.data.IpcRouteSummary; +import org.transitclock.ipc.data.IpcTrip; +import org.transitclock.utils.Time; /** * Describes a block in terse form, without schedule and trip pattern info diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiBlocks.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiBlocks.java similarity index 91% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiBlocks.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiBlocks.java index 513ad2a33..0609364a0 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiBlocks.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiBlocks.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import java.util.ArrayList; import java.util.Collection; @@ -24,7 +24,7 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; -import org.transitime.ipc.data.IpcBlock; +import org.transitclock.ipc.data.IpcBlock; /** * A list of blocks diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiBlocksTerse.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiBlocksTerse.java similarity index 92% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiBlocksTerse.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiBlocksTerse.java index 34186162b..3ba3c4c9f 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiBlocksTerse.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiBlocksTerse.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import java.util.ArrayList; import java.util.Collection; @@ -24,7 +24,7 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; -import org.transitime.ipc.data.IpcBlock; +import org.transitclock.ipc.data.IpcBlock; /** * A list of terse blocks, without trip pattern or schedule info diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/ApiCacheDetails.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiCacheDetails.java new file mode 100755 index 000000000..818cb1335 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiCacheDetails.java @@ -0,0 +1,64 @@ +/* + * 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.api.data; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +import org.transitclock.api.rootResources.TransitimeApi.UiMode; +import org.transitclock.db.webstructs.WebAgency; +import org.transitclock.ipc.data.IpcVehicle; +import org.transitclock.utils.Time; + +/** + * For when have list of VehicleDetails. By using this class can control the + * element name when data is output. + * + * @author SkiBu Smith + * + */ +@XmlRootElement +public class ApiCacheDetails { + + public ApiCacheDetails(String name, Integer size) { + super(); + this.size = size; + this.name = name; + } + + @XmlElement(name = "name") + private String name; + + @XmlElement(name = "size") + private Integer size; + + /********************** Member Functions **************************/ + + /** + * Need a no-arg constructor for Jersey. Otherwise get really obtuse + * "MessageBodyWriter not found for media type=application/json" exception. + */ + protected ApiCacheDetails() { + } + +} diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiCalendar.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiCalendar.java similarity index 92% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiCalendar.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiCalendar.java index 57bae2398..1f1e3907d 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiCalendar.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiCalendar.java @@ -15,11 +15,11 @@ * along with Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import javax.xml.bind.annotation.XmlAttribute; -import org.transitime.ipc.data.IpcCalendar; +import org.transitclock.ipc.data.IpcCalendar; /** * A GTFS calendar diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiCalendars.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiCalendars.java similarity index 91% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiCalendars.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiCalendars.java index b24dbf977..d8898f798 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiCalendars.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiCalendars.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import java.util.ArrayList; import java.util.List; @@ -23,7 +23,7 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; -import org.transitime.ipc.data.IpcCalendar; +import org.transitclock.ipc.data.IpcCalendar; /** * List of GTFS calendars diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiCommandAck.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiCommandAck.java similarity index 94% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiCommandAck.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiCommandAck.java index 0df91d62b..c32b071bd 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiCommandAck.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiCommandAck.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.api.data; +package org.transitclock.api.data; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/ApiCurrentServerDate.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiCurrentServerDate.java new file mode 100644 index 000000000..c1b7e5d0d --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiCurrentServerDate.java @@ -0,0 +1,23 @@ +package org.transitclock.api.data; + +import java.util.Date; + +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement(name = "servertime") +public class ApiCurrentServerDate { + @XmlAttribute + private Date currentTime; + + /** + * Need a no-arg constructor for Jersey. Otherwise get really obtuse + * "MessageBodyWriter not found for media type=application/json" exception. + */ + protected ApiCurrentServerDate() { + } + + public ApiCurrentServerDate(Date currentTime) { + this.currentTime=currentTime; + } +} diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiDirection.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiDirection.java similarity index 93% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiDirection.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiDirection.java index 4b89e2f12..1faace4a6 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiDirection.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiDirection.java @@ -15,7 +15,7 @@ * Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import java.util.ArrayList; import java.util.List; @@ -23,8 +23,8 @@ import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; -import org.transitime.ipc.data.IpcDirection; -import org.transitime.ipc.data.IpcStop; +import org.transitclock.ipc.data.IpcDirection; +import org.transitclock.ipc.data.IpcStop; /** * A single direction, containing stops diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiDirections.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiDirections.java similarity index 88% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiDirections.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiDirections.java index 782d1dbb5..00d1f24e8 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiDirections.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiDirections.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import java.util.ArrayList; import java.util.Collection; @@ -24,8 +24,8 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; -import org.transitime.ipc.data.IpcDirection; -import org.transitime.ipc.data.IpcDirectionsForRoute; +import org.transitclock.ipc.data.IpcDirection; +import org.transitclock.ipc.data.IpcDirectionsForRoute; /** * A list of directions. @@ -50,7 +50,7 @@ protected ApiDirections() { public ApiDirections(IpcDirectionsForRoute stopsForRoute) { Collection directions = stopsForRoute.getDirections(); - directionsData = new ArrayList(directions.size()); + directionsData = new ArrayList<>(directions.size()); for (IpcDirection direction : directions) { directionsData.add(new ApiDirection(direction)); } diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/ApiDispatcher.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiDispatcher.java new file mode 100644 index 000000000..0f69fc49c --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiDispatcher.java @@ -0,0 +1,56 @@ +package org.transitclock.api.data; + + +import org.transitclock.api.utils.MathUtils; +import org.transitclock.api.utils.NumberFormatter; +import org.transitclock.ipc.data.IpcVehicle; +import org.transitclock.utils.Time; + +import javax.xml.bind.annotation.XmlElement; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class ApiDispatcher { + + @XmlElement(name="data") + private List dispatcherData = new ArrayList<>(); + + public ApiDispatcher() {} + + public ApiDispatcher(Collection vehicles, SpeedFormat speedFormat){ + for(IpcVehicle vehicle : vehicles){ + String vehicleId = vehicle.getId(); + String lastReportTime = Time.timeStr(vehicle.getGpsTime()); + String heading = NumberFormatter.getRoundedValueAsString(vehicle.getHeading(), 1); + String speed = getFormattedSpeed(vehicle.getSpeed(), speedFormat); + String route = vehicle.getRouteShortName(); + String scheduleAdherence = getFormattedScheduleAdherence(vehicle); + String operatorId = null; + dispatcherData.add(new ApiDispatcherData(vehicleId, lastReportTime, heading, speed, route, + scheduleAdherence, operatorId)); + } + } + + private String getFormattedSpeed(float value, SpeedFormat speedFormat){ + if(Float.isNaN(value)){ + return null; + } + return NumberFormatter.getRoundedValueAsString(MathUtils.convertSpeed(value, speedFormat), 2); + } + + private String getFormattedScheduleAdherence(IpcVehicle vehicle){ + try { + if (vehicle.getRealTimeSchedAdh() != null) { + return vehicle.getRealTimeSchedAdh().toString(); + } + }catch(Exception e){ + e.printStackTrace(); + } + return null; + } + + public List getDispatcherData() { + return dispatcherData; + } +} diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/ApiDispatcherData.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiDispatcherData.java new file mode 100644 index 000000000..93f50ceac --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiDispatcherData.java @@ -0,0 +1,67 @@ +package org.transitclock.api.data; + +import javax.xml.bind.annotation.XmlAttribute; + +public class ApiDispatcherData { + @XmlAttribute(name = "vehicle") + String vehicleId; + + @XmlAttribute(name = "last_report") + String lastReportTime; + + @XmlAttribute(name = "heading") + String heading; + + @XmlAttribute(name = "speed") + String speed; + + @XmlAttribute(name = "route_assignment") + String route; + + @XmlAttribute(name = "schedule_adherence") + String scheduleAdherence; + + @XmlAttribute(name = "operator_id") + String operatorId; + + public ApiDispatcherData() {} + + public ApiDispatcherData(String vehicleId, String lastReportTime, String heading, String speed, + String route, String scheduleAdherence, String operatorId){ + this.vehicleId = vehicleId; + this.lastReportTime = lastReportTime; + this.heading = heading; + this.speed = speed; + this.route = route; + this. scheduleAdherence = scheduleAdherence; + this.operatorId = operatorId; + } + + public String getVehicleId() { + return vehicleId; + } + + public String getLastReportTime() { + return lastReportTime; + } + + public String getHeading() { + return heading; + } + + public String getSpeed() { + return speed; + } + + public String getRoute() { + return route; + } + + public String getScheduleAdherence() { + return scheduleAdherence; + } + + public String getOperatorId() { + return operatorId; + } +} diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiExtent.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiExtent.java similarity index 92% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiExtent.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiExtent.java index 35e879502..5eb34f8bf 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiExtent.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiExtent.java @@ -15,12 +15,12 @@ * Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import javax.xml.bind.annotation.XmlAttribute; -import org.transitime.db.structs.Extent; -import org.transitime.utils.MathUtils; +import org.transitclock.db.structs.Extent; +import org.transitclock.utils.MathUtils; /** * Describes the extent of a route or agency via a min & max lat & lon. diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiGpsLocation.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiGpsLocation.java similarity index 82% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiGpsLocation.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiGpsLocation.java index 2e540c4b9..a57f969c4 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiGpsLocation.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiGpsLocation.java @@ -15,14 +15,15 @@ * Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlType; -import org.transitime.ipc.data.IpcVehicle; -import org.transitime.utils.MathUtils; -import org.transitime.utils.Time; +import org.transitclock.api.utils.NumberFormatter; +import org.transitclock.ipc.data.IpcVehicle; +import org.transitclock.utils.MathUtils; +import org.transitclock.utils.Time; /** * Extends a location by including GPS information including time, speed, @@ -57,19 +58,17 @@ protected ApiGpsLocation() { } /** - * @param lat - * @param lon + * + * @param vehicle */ public ApiGpsLocation(IpcVehicle vehicle) { super(vehicle.getLatitude(), vehicle.getLongitude()); this.time = vehicle.getGpsTime() / Time.MS_PER_SEC; // Output only 1 digit past decimal point - this.speed = Float.isNaN(vehicle.getSpeed()) ? - null : MathUtils.round(vehicle.getSpeed(), 1); + this.speed = NumberFormatter.getRoundedValueAsDouble(vehicle.getSpeed(), 1); // Output only 1 digit past decimal point - this.heading = Float.isNaN(vehicle.getHeading()) ? - null : MathUtils.round(vehicle.getHeading(), 1); + this.heading = NumberFormatter.getRoundedValueAsDouble(vehicle.getHeading(), 1); } diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/ApiHeadsign.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiHeadsign.java new file mode 100644 index 000000000..bd14769b7 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiHeadsign.java @@ -0,0 +1,79 @@ +/* + * 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.api.data; +import org.transitclock.ipc.data.IpcTripPattern; +import javax.xml.bind.annotation.XmlAttribute; +import java.util.Objects; + +/** + * A short description of a headsign. For when outputting list of headsigns for + * route and agency. + * + * @author Lenny Caraballo + * + */ +public class ApiHeadsign implements Comparable{ + + @XmlAttribute + private String headsign; + + @XmlAttribute + private String direction; + + @XmlAttribute + private String label; + + /********************** Member Functions **************************/ + + /** + * Need a no-arg constructor for Jersey. Otherwise get really obtuse + * "MessageBodyWriter not found for media type=application/json" exception. + */ + protected ApiHeadsign() { + } + + public ApiHeadsign(IpcTripPattern tripPattern) { + this.headsign = tripPattern.getHeadsign(); + this.direction = tripPattern.getDirectionId(); + this.label = this.headsign + " (" + this.direction + ")"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ApiHeadsign that = (ApiHeadsign) o; + return Objects.equals(headsign, that.headsign) && + Objects.equals(direction, that.direction); + } + + @Override + public int hashCode() { + return Objects.hash(headsign, direction); + } + + @Override + public int compareTo(ApiHeadsign apiHeadsign) { + return this.toString().compareTo(apiHeadsign.toString()); + } + + @Override + public String toString() { + return this.headsign + "_" + this.direction; + } +} diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/ApiHeadsigns.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiHeadsigns.java new file mode 100644 index 000000000..ebacf765b --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiHeadsigns.java @@ -0,0 +1,71 @@ +/* + * 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.api.data; + +import java.util.*; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +import org.transitclock.db.structs.Agency; +import org.transitclock.ipc.data.IpcTripPattern; + +/** + * An ordered list of headsigns. + * + * @author Lenny Caraballo + * + */ +@XmlRootElement +public class ApiHeadsigns{ + // So can easily get agency name when getting headsigns. Useful for db reports + // and such. + @XmlElement(name = "agency") + private String agencyName; + + // List of headsign info + @XmlElement(name = "headsigns") + private Set headsignsData; + + /********************** Member Functions **************************/ + + /** + * Need a no-arg constructor for Jersey. Otherwise get really obtuse + * "MessageBodyWriter not found for media type=application/json" exception. + */ + protected ApiHeadsigns() { + } + + /** + * Constructs an ApiHeadsigns using a collection of IpcTripPattern + * objects. + * + * @param tripPatterns + * @param agency so can get agency name + */ + public ApiHeadsigns(Collection tripPatterns, Agency agency) { + headsignsData = new TreeSet<>(); + for (IpcTripPattern tripPattern : tripPatterns) { + ApiHeadsign headsign = new ApiHeadsign(tripPattern); + headsignsData.add(headsign); + } + + // Also set agency name + agencyName = agency.getName(); + } +} \ No newline at end of file diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/ApiHistoricalAverage.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiHistoricalAverage.java new file mode 100755 index 000000000..4afd40213 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiHistoricalAverage.java @@ -0,0 +1,58 @@ +/* + * 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.api.data; + +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; + +import org.transitclock.ipc.data.IpcHistoricalAverage; + +/** + * Describes an historical average + * + * @author Sean Og Crudden + * + */ +@XmlRootElement(name = "HistoricalAverage") +public class ApiHistoricalAverage { + + + @XmlAttribute + private Integer count; + + @XmlAttribute + private Double average; + + + + + /********************** Member Functions **************************/ + + /** + * Need a no-arg constructor for Jersey. Otherwise get really obtuse + * "MessageBodyWriter not found for media type=application/json" exception. + */ + protected ApiHistoricalAverage() { + } + + public ApiHistoricalAverage(IpcHistoricalAverage ipcHistoricalAverage) { + this.count=ipcHistoricalAverage.getCount(); + this.average=ipcHistoricalAverage.getAverage(); + } + +} diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/ApiHistoricalAverageCacheKey.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiHistoricalAverageCacheKey.java new file mode 100755 index 000000000..348e7bdbc --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiHistoricalAverageCacheKey.java @@ -0,0 +1,29 @@ +package org.transitclock.api.data; + +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; + +import org.transitclock.ipc.data.IpcHistoricalAverageCacheKey; +/** + * Describes an historical average key which is used to refer to data elements in the cache + * + * @author Sean Og Crudden + * + */ +@XmlRootElement(name = "HistoricalAverageCacheKey") +public class ApiHistoricalAverageCacheKey { + + @XmlAttribute + private String tripId; + @XmlAttribute + private Integer stopPathIndex; + + public ApiHistoricalAverageCacheKey() { + + } + public ApiHistoricalAverageCacheKey(IpcHistoricalAverageCacheKey key) { + + this.tripId=key.getTripId(); + this.stopPathIndex=key.getStopPathIndex(); + } +} diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/ApiHistoricalAverageCacheKeys.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiHistoricalAverageCacheKeys.java new file mode 100755 index 000000000..c9c6c720d --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiHistoricalAverageCacheKeys.java @@ -0,0 +1,36 @@ +package org.transitclock.api.data; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +import org.transitclock.ipc.data.IpcHistoricalAverageCacheKey; +/** + * + * @author Sean Og Crudden + * + */ +@XmlRootElement(name = "HistoricalAverageCacheKeys") +public class ApiHistoricalAverageCacheKeys { + + @XmlElement(name = "HistoricalAverageCacheKey") + private List apiHistoricalAverageCacheKeys; + + /** + * Need a no-arg constructor for Jersey. Otherwise get really obtuse + * "MessageBodyWriter not found for media type=application/json" exception. + */ + protected ApiHistoricalAverageCacheKeys() { + + } + public ApiHistoricalAverageCacheKeys(Collection cacheKeys) { + apiHistoricalAverageCacheKeys=new ArrayList(); + for( IpcHistoricalAverageCacheKey key:cacheKeys) + { + apiHistoricalAverageCacheKeys.add(new ApiHistoricalAverageCacheKey(key) ); + } + } +} diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/ApiHoldingTime.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiHoldingTime.java new file mode 100755 index 000000000..42832af4c --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiHoldingTime.java @@ -0,0 +1,74 @@ +package org.transitclock.api.data; + +import java.lang.reflect.InvocationTargetException; +import java.util.Date; + +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; + +import org.transitclock.ipc.data.IpcHoldingTime; +@XmlRootElement(name = "holdingtime") +public class ApiHoldingTime { + + @XmlAttribute + private Date holdingTime; + + @XmlAttribute + private Date creationTime; + + @XmlAttribute + private Date currentTime; + + @XmlAttribute + private String vehicleId; + + @XmlAttribute + private String stopId; + + @XmlAttribute + private String tripId; + + @XmlAttribute + private String routeId; + + @XmlAttribute + private boolean arrivalPredictionUsed; + + @XmlAttribute + private boolean arrivalUsed; + + @XmlAttribute + private Date arrivalTime; + + @XmlAttribute + private boolean hasN1; + + @XmlAttribute + private boolean hasN2; + + @XmlAttribute + private boolean hasD1; + + @XmlAttribute + private int numberPredictionsUsed; + + protected ApiHoldingTime() { + + } + + public ApiHoldingTime(IpcHoldingTime ipcHoldingTime) throws IllegalAccessException, InvocationTargetException { + this.holdingTime=ipcHoldingTime.getHoldingTime(); + this.creationTime=ipcHoldingTime.getCreationTime(); + this.vehicleId=ipcHoldingTime.getVehicleId(); + this.tripId=ipcHoldingTime.getTripId(); + this.routeId=ipcHoldingTime.getRouteId(); + this.stopId=ipcHoldingTime.getStopId(); + this.arrivalPredictionUsed = ipcHoldingTime.isArrivalPredictionUsed(); + this.arrivalUsed = ipcHoldingTime.isArrivalUsed(); + this.currentTime = ipcHoldingTime.getCurrentTime(); + this.arrivalTime = ipcHoldingTime.getArrivalTime(); + this.hasD1=ipcHoldingTime.isHasD1(); + this.numberPredictionsUsed=ipcHoldingTime.getNumberPredictionsUsed(); + + } +} diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/ApiHoldingTimeCacheKey.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiHoldingTimeCacheKey.java new file mode 100755 index 000000000..d2b659e7b --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiHoldingTimeCacheKey.java @@ -0,0 +1,31 @@ +package org.transitclock.api.data; + +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; + +import org.transitclock.ipc.data.IpcHoldingTimeCacheKey; + +@XmlRootElement(name = "HoldingTimeCacheKey") +public class ApiHoldingTimeCacheKey { + + + @XmlAttribute + private String stopid; + + @XmlAttribute + private String vehicleId; + + @XmlAttribute + private String tripId; + + public ApiHoldingTimeCacheKey(IpcHoldingTimeCacheKey key) { + + this.stopid=key.getStopid(); + this.vehicleId=key.getVehicleId(); + this.tripId=key.getTripId(); + } + + protected ApiHoldingTimeCacheKey() { + } + +} diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/ApiHoldingTimeCacheKeys.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiHoldingTimeCacheKeys.java new file mode 100755 index 000000000..44d9fddac --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiHoldingTimeCacheKeys.java @@ -0,0 +1,37 @@ +package org.transitclock.api.data; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +import org.transitclock.ipc.data.IpcHistoricalAverageCacheKey; +import org.transitclock.ipc.data.IpcHoldingTimeCacheKey; +/** + * + * @author Sean Óg Crudden + * + */ +@XmlRootElement(name = "HoldingTimeCacheKeys") +public class ApiHoldingTimeCacheKeys { + + @XmlElement(name = "HoldingTimeCacheKey") + private List apiHoldingTimeCacheKeys; + + /** + * Need a no-arg constructor for Jersey. Otherwise get really obtuse + * "MessageBodyWriter not found for media type=application/json" exception. + */ + protected ApiHoldingTimeCacheKeys() { + + } + public ApiHoldingTimeCacheKeys(Collection cacheKeys) { + apiHoldingTimeCacheKeys=new ArrayList(); + for( IpcHoldingTimeCacheKey key:cacheKeys) + { + apiHoldingTimeCacheKeys.add(new ApiHoldingTimeCacheKey(key) ); + } + } +} diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiIds.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiIds.java similarity index 91% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiIds.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiIds.java index ffef380ad..e2b70cbd5 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiIds.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiIds.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.api.data; +package org.transitclock.api.data; import java.util.List; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; -import org.transitime.utils.StringUtils; +import org.transitclock.utils.StringUtils; /** * For outputting simple list of sorted alpha-number IDs diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/ApiKalmanErrorCacheKey.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiKalmanErrorCacheKey.java new file mode 100755 index 000000000..2f2007e06 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiKalmanErrorCacheKey.java @@ -0,0 +1,30 @@ +package org.transitclock.api.data; + +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; + +import org.transitclock.ipc.data.IpcHistoricalAverageCacheKey; +import org.transitclock.ipc.data.IpcKalmanErrorCacheKey; +/** + * Describes an kalman error key which is used to refer to data elements in the kalman error cache + * + * @author Sean Og Crudden + * + */ +@XmlRootElement(name = "KalmanErrorCacheKey") +public class ApiKalmanErrorCacheKey { + + @XmlAttribute + private String tripId; + @XmlAttribute + private Integer stopPathIndex; + + public ApiKalmanErrorCacheKey() { + + } + public ApiKalmanErrorCacheKey(IpcKalmanErrorCacheKey key) { + + this.tripId=key.getTripId(); + this.stopPathIndex=key.getStopPathIndex(); + } +} diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/ApiKalmanErrorCacheKeys.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiKalmanErrorCacheKeys.java new file mode 100755 index 000000000..518dc097e --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiKalmanErrorCacheKeys.java @@ -0,0 +1,36 @@ +package org.transitclock.api.data; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +import org.transitclock.ipc.data.IpcKalmanErrorCacheKey;; +/** +* +* @author Sean Og Crudden +* +*/ +@XmlRootElement(name = "KalmanErrorCacheKeys") +public class ApiKalmanErrorCacheKeys { + + @XmlElement(name = "KalmanErrorCacheKey") + private List apiKalmanErrorCacheKeys; + + /** + * Need a no-arg constructor for Jersey. Otherwise get really obtuse + * "MessageBodyWriter not found for media type=application/json" exception. + */ + protected ApiKalmanErrorCacheKeys() { + + } + public ApiKalmanErrorCacheKeys(Collection cacheKeys) { + apiKalmanErrorCacheKeys=new ArrayList(); + for( IpcKalmanErrorCacheKey key:cacheKeys) + { + apiKalmanErrorCacheKeys.add(new ApiKalmanErrorCacheKey(key) ); + } + } +} diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiLocation.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiLocation.java similarity index 94% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiLocation.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiLocation.java index 9f825c263..4e5fc83cf 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiLocation.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiLocation.java @@ -15,9 +15,9 @@ * Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; -import org.transitime.db.structs.Location; +import org.transitclock.db.structs.Location; /** * A simple latitude/longitude. diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiNearbyPredictionsForAgencies.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiNearbyPredictionsForAgencies.java similarity index 94% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiNearbyPredictionsForAgencies.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiNearbyPredictionsForAgencies.java index ac209f6f2..1ed66eded 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiNearbyPredictionsForAgencies.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiNearbyPredictionsForAgencies.java @@ -15,7 +15,7 @@ * Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import java.util.ArrayList; import java.util.List; diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiPrediction.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiPrediction.java similarity index 84% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiPrediction.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiPrediction.java index 7e2e44ce1..57c3becfe 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiPrediction.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiPrediction.java @@ -15,13 +15,13 @@ * Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; -import org.transitime.ipc.data.IpcPrediction; -import org.transitime.utils.Time; +import org.transitclock.ipc.data.IpcPrediction; +import org.transitclock.utils.Time; /** * Contains data for a single prediction. @@ -53,7 +53,10 @@ public class ApiPrediction { @XmlAttribute(name = "trip") private String tripId; - + + @XmlAttribute(name = "blockId") + private String blockId; + @XmlAttribute(name = "tripPattern") private String tripPatternId; @@ -78,7 +81,13 @@ public class ApiPrediction { // Only output if passenger count is valid @XmlAttribute(name = "passengerCount") - private Integer passengerCount; + private String passengerCount; + + @XmlAttribute(name = "isDeparture") + private String isDepartureDuplicate; //same field different name + + @XmlAttribute(name = "affectedByLayover") + private String affectedByLayover; /********************** Member Functions **************************/ @@ -121,7 +130,7 @@ public ApiPrediction(IpcPrediction prediction) { // Only set passengerCount if it is valid so that it is not output if it // is not valid since will then be null if (prediction.isPassengerCountValid()) - passengerCount = (int) prediction.getPassengerCount(); + passengerCount = String.valueOf(prediction.getPassengerCount()); // Only set if true so only output for rare case if (prediction.isDelayed()) @@ -129,6 +138,13 @@ public ApiPrediction(IpcPrediction prediction) { // Only set if true so only output for rare case if (prediction.isLateAndSubsequentTripSoMarkAsUncertain()) + isLateAndSubsequentTripSoMarkAsUncertain = Boolean.TRUE; + + affectedByLayover = Boolean.toString(prediction.isAffectedByWaitStop()); + + isDepartureDuplicate = Boolean.toString(!prediction.isArrival()); + + blockId = prediction.getBlockId(); isLateAndSubsequentTripSoMarkAsUncertain = true; } diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiPredictionDestination.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiPredictionDestination.java similarity index 92% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiPredictionDestination.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiPredictionDestination.java index f71cb1c85..66358b14b 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiPredictionDestination.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiPredictionDestination.java @@ -15,7 +15,7 @@ * Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import java.util.ArrayList; import java.util.List; @@ -24,8 +24,8 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; -import org.transitime.ipc.data.IpcPrediction; -import org.transitime.ipc.data.IpcPredictionsForRouteStopDest; +import org.transitclock.ipc.data.IpcPrediction; +import org.transitclock.ipc.data.IpcPredictionsForRouteStopDest; /** * Contains list of predictions for a particular headsign. diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/ApiPredictionForStopPath.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiPredictionForStopPath.java new file mode 100755 index 000000000..d648baa50 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiPredictionForStopPath.java @@ -0,0 +1,45 @@ +package org.transitclock.api.data; + +import java.util.Date; + +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; + +import org.transitclock.ipc.data.IpcPredictionForStopPath; +@XmlRootElement(name = "traveltimepredictionforstoppath") +public class ApiPredictionForStopPath { + + @XmlAttribute + private String tripId; + @XmlAttribute + private Integer stopPathIndex; + + @XmlAttribute + private Date creationTime; + + @XmlAttribute + private Double predictionTime; + + @XmlAttribute + private String algorithm; + + /** + * Need a no-arg constructor for Jersey. Otherwise get really obtuse + * "MessageBodyWriter not found for media type=application/json" exception. + */ + protected ApiPredictionForStopPath() { + + + } + + public ApiPredictionForStopPath(IpcPredictionForStopPath prediction + ) { + super(); + this.tripId = prediction.getTripId(); + this.stopPathIndex = prediction.getStopPathIndex(); + this.creationTime = prediction.getCreationTime(); + this.predictionTime = prediction.getPredictionTime(); + this.algorithm = prediction.getAlgorithm(); + } + +} diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiPredictionRouteStop.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiPredictionRouteStop.java similarity index 95% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiPredictionRouteStop.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiPredictionRouteStop.java index 67cfae48f..80b41b6a7 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiPredictionRouteStop.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiPredictionRouteStop.java @@ -15,7 +15,7 @@ * Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import java.util.ArrayList; import java.util.List; @@ -24,8 +24,8 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; -import org.transitime.ipc.data.IpcPredictionsForRouteStopDest; -import org.transitime.utils.MathUtils; +import org.transitclock.ipc.data.IpcPredictionsForRouteStopDest; +import org.transitclock.utils.MathUtils; /** * List of ApiPredictionDestination objects along with supporting information. diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiPredictions.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiPredictions.java similarity index 97% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiPredictions.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiPredictions.java index 5e1028fba..bad5e39b3 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiPredictions.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiPredictions.java @@ -15,7 +15,7 @@ * Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import java.util.ArrayList; import java.util.List; @@ -24,7 +24,7 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; -import org.transitime.ipc.data.IpcPredictionsForRouteStopDest; +import org.transitclock.ipc.data.IpcPredictionsForRouteStopDest; /** * Contains predictions for multiple routes/stops. Can also contain info for the diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/ApiPredictionsForStopPath.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiPredictionsForStopPath.java new file mode 100755 index 000000000..4c40a65c3 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiPredictionsForStopPath.java @@ -0,0 +1,68 @@ +/* + * 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.api.data; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +import org.transitclock.ipc.data.IpcArrivalDeparture; +import org.transitclock.ipc.data.IpcPredictionForStopPath; +import org.transitclock.ipc.data.IpcRouteSummary; + +/** + * An ordered list of routes. + * + * @author SkiBu Smith + * + */ +@XmlRootElement(name = "predictions") +public class ApiPredictionsForStopPath { + + @XmlElement(name = "prediction") + private List predictionsForStopPath; + + /********************** Member Functions **************************/ + + /** + * Need a no-arg constructor for Jersey. Otherwise get really obtuse + * "MessageBodyWriter not found for media type=application/json" exception. + */ + protected ApiPredictionsForStopPath() { + } + + /** + * Constructs an ApiRouteSummaries using a collection of IpcRouteSummary + * objects. + * + * @param routes + * @throws InvocationTargetException + * @throws IllegalAccessException + */ + public ApiPredictionsForStopPath(Collection predictions) throws IllegalAccessException, InvocationTargetException { + predictionsForStopPath = new ArrayList(); + for (IpcPredictionForStopPath prediction : predictions) { + ApiPredictionForStopPath apiPredictionForStopPath = new ApiPredictionForStopPath(prediction); + predictionsForStopPath.add(apiPredictionForStopPath); + } + } +} diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/ApiRevisionInformation.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiRevisionInformation.java new file mode 100644 index 000000000..47daa63d0 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiRevisionInformation.java @@ -0,0 +1,28 @@ +package org.transitclock.api.data; + +import org.transitclock.ipc.data.IpcRevisionInformation; + +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * Exposes metadata about the current dataset loaded. + */ +@XmlRootElement(name = "revisionInformation") +public class ApiRevisionInformation { + @XmlAttribute + private String agencyId; + + @XmlElement(name = "lastRevision") + private IpcRevisionInformation lastRevision; + + protected ApiRevisionInformation() { + + } + + public ApiRevisionInformation(String agencyId, IpcRevisionInformation ipcRevisionInformation) { + this.agencyId = agencyId; + this.lastRevision = ipcRevisionInformation; + } +} diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiRmiServerStatus.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiRmiServerStatus.java similarity index 96% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiRmiServerStatus.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiRmiServerStatus.java index 9877a19e5..cfc18c4bc 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiRmiServerStatus.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiRmiServerStatus.java @@ -15,7 +15,7 @@ * Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import java.util.ArrayList; import java.util.List; @@ -25,7 +25,7 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; -import org.transitime.ipc.rmi.RmiCallInvocationHandler; +import org.transitclock.ipc.rmi.RmiCallInvocationHandler; /** * diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiRoute.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiRoute.java similarity index 94% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiRoute.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiRoute.java index 821812632..cb3eafe3a 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiRoute.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiRoute.java @@ -15,11 +15,11 @@ * along with Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import javax.xml.bind.annotation.XmlAttribute; -import org.transitime.ipc.data.IpcRouteSummary; +import org.transitclock.ipc.data.IpcRouteSummary; /** * A short description of a route. For when outputting list of routes for diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiRouteDetails.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiRouteDetails.java similarity index 91% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiRouteDetails.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiRouteDetails.java index c470df9ee..2135ca96d 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiRouteDetails.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiRouteDetails.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import java.util.ArrayList; import java.util.List; @@ -23,11 +23,11 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; -import org.transitime.db.structs.Location; -import org.transitime.ipc.data.IpcDirection; -import org.transitime.ipc.data.IpcRoute; -import org.transitime.ipc.data.IpcShape; -import org.transitime.ipc.data.IpcDirectionsForRoute; +import org.transitclock.db.structs.Location; +import org.transitclock.ipc.data.IpcDirection; +import org.transitclock.ipc.data.IpcDirectionsForRoute; +import org.transitclock.ipc.data.IpcRoute; +import org.transitclock.ipc.data.IpcShape; /** * Provides detailed information for a route include stops and shape info. diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/ApiRouteDirections.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiRouteDirections.java new file mode 100644 index 000000000..7f54e5d73 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiRouteDirections.java @@ -0,0 +1,60 @@ +/* + * 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.api.data; + +import org.transitclock.ipc.data.IpcDirection; +import org.transitclock.ipc.data.IpcDirectionsForRoute; + +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * A list of directions. + * + * @author SkiBu Smith + * + */ +public class ApiRouteDirections { + + @XmlAttribute + private String routeId; + + @XmlAttribute + private String routeShortName; + + @XmlElement + private ApiDirections directions; + + /********************** Member Functions **************************/ + + /** + * Need a no-arg constructor for Jersey. Otherwise get really obtuse + * "MessageBodyWriter not found for media type=application/json" exception. + */ + protected ApiRouteDirections() { } + + public ApiRouteDirections(IpcDirectionsForRoute stopsForRoute) { + routeId = stopsForRoute.getRouteId(); + routeShortName = stopsForRoute.getRouteShortName(); + directions = new ApiDirections(stopsForRoute); + } +} diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiRoutes.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiRoutes.java similarity index 93% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiRoutes.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiRoutes.java index fe1a76cc4..9fbed740a 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiRoutes.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiRoutes.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import java.util.ArrayList; import java.util.Collection; @@ -24,9 +24,9 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; -import org.transitime.db.structs.Agency; -import org.transitime.ipc.data.IpcRoute; -import org.transitime.ipc.data.IpcRouteSummary; +import org.transitclock.db.structs.Agency; +import org.transitclock.ipc.data.IpcRoute; +import org.transitclock.ipc.data.IpcRouteSummary; /** * An ordered list of routes. diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiRoutesDetails.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiRoutesDetails.java similarity index 90% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiRoutesDetails.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiRoutesDetails.java index 5bb3a30bd..a325b8bca 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiRoutesDetails.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiRoutesDetails.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.api.data; +package org.transitclock.api.data; import java.util.ArrayList; import java.util.Collection; @@ -23,8 +23,8 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; -import org.transitime.db.structs.Agency; -import org.transitime.ipc.data.IpcRoute; +import org.transitclock.db.structs.Agency; +import org.transitclock.ipc.data.IpcRoute; /** * When have a list of routes. diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/ApiRoutesDirections.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiRoutesDirections.java new file mode 100644 index 000000000..1161067f8 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiRoutesDirections.java @@ -0,0 +1,53 @@ +/* + * 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.api.data; + +import org.transitclock.ipc.data.IpcDirectionsForRoute; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import java.util.ArrayList; +import java.util.List; + +/** + * A list of directions. + * + * @author SkiBu Smith + * + */ +@XmlRootElement(name = "routesAndDirections") +public class ApiRoutesDirections { + + @XmlElement + private List routeDirections; + + /********************** Member Functions **************************/ + + /** + * Need a no-arg constructor for Jersey. Otherwise get really obtuse + * "MessageBodyWriter not found for media type=application/json" exception. + */ + protected ApiRoutesDirections() { } + + public ApiRoutesDirections(List stopsForRoutes) { + routeDirections = new ArrayList<>(stopsForRoutes.size()); + for(IpcDirectionsForRoute directionsForRoute : stopsForRoutes){ + routeDirections.add(new ApiRouteDirections(directionsForRoute)); + } + } +} diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/ApiRunTimeSummary.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiRunTimeSummary.java new file mode 100644 index 000000000..561060722 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiRunTimeSummary.java @@ -0,0 +1,28 @@ +package org.transitclock.api.data; + +import org.transitclock.ipc.data.IpcRunTime; +import org.transitclock.utils.MathUtils; +import javax.xml.bind.annotation.XmlAttribute; + +public class ApiRunTimeSummary{ + @XmlAttribute(name = "avgRunTime") + private Double avgRunTime; + + @XmlAttribute(name = "fixed") + private Double fixed; + + @XmlAttribute(name = "variable") + private Double variable; + + @XmlAttribute(name = "dwell") + private Double dwell; + + public ApiRunTimeSummary(){} + + public ApiRunTimeSummary(IpcRunTime ipcRunTime){ + this.avgRunTime = ipcRunTime.getAvgRunTime() != null ? MathUtils.round(ipcRunTime.getAvgRunTime(), 1) : null; + this.fixed = ipcRunTime.getFixed() != null ? MathUtils.round(ipcRunTime.getFixed(), 1) : null; + this.variable = ipcRunTime.getVariable() != null ? MathUtils.round(ipcRunTime.getVariable(), 1) : null; + this.dwell = ipcRunTime.getDwell() != null ? MathUtils.round(ipcRunTime.getDwell(), 1) : null; + } +} diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiScheduleArrDepTime.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiScheduleArrDepTime.java similarity index 93% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiScheduleArrDepTime.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiScheduleArrDepTime.java index 8e199b92b..8036fabcd 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiScheduleArrDepTime.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiScheduleArrDepTime.java @@ -15,12 +15,12 @@ * along with Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import javax.xml.bind.annotation.XmlAttribute; -import org.transitime.ipc.data.IpcSchedTimes; -import org.transitime.utils.Time; +import org.transitclock.ipc.data.IpcSchedTimes; +import org.transitclock.utils.Time; /** * Represents a schedule time for a stop. Contains both arrival and departure diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiScheduleHorizStops.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiScheduleHorizStops.java similarity index 90% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiScheduleHorizStops.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiScheduleHorizStops.java index c33bf2421..8e561d468 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiScheduleHorizStops.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiScheduleHorizStops.java @@ -15,16 +15,16 @@ * along with Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import java.util.ArrayList; import java.util.List; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; -import org.transitime.ipc.data.IpcSchedule; -import org.transitime.ipc.data.IpcSchedTime; -import org.transitime.ipc.data.IpcSchedTrip; +import org.transitclock.ipc.data.IpcSchedTime; +import org.transitclock.ipc.data.IpcSchedTrip; +import org.transitclock.ipc.data.IpcSchedule; /** * Represents a schedule for a route for a specific direction and service class. diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiScheduleStop.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiScheduleStop.java similarity index 93% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiScheduleStop.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiScheduleStop.java index 6ba24cdb8..77d1ef281 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiScheduleStop.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiScheduleStop.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.api.data; +package org.transitclock.api.data; import javax.xml.bind.annotation.XmlAttribute; diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiScheduleTime.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiScheduleTime.java similarity index 91% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiScheduleTime.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiScheduleTime.java index 33f2f0385..d2079c341 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiScheduleTime.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiScheduleTime.java @@ -15,11 +15,11 @@ * along with Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import javax.xml.bind.annotation.XmlAttribute; -import org.transitime.utils.Time; +import org.transitclock.utils.Time; /** * Represents a schedule time for a stop. Intended to be used for displaying a diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiScheduleTimesForStop.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiScheduleTimesForStop.java similarity index 98% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiScheduleTimesForStop.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiScheduleTimesForStop.java index ce1142459..7c4347ac5 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiScheduleTimesForStop.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiScheduleTimesForStop.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import java.util.ArrayList; import java.util.List; diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiScheduleTimesForTrip.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiScheduleTimesForTrip.java similarity index 90% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiScheduleTimesForTrip.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiScheduleTimesForTrip.java index 3eaa4a4df..07f027ecb 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiScheduleTimesForTrip.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiScheduleTimesForTrip.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.api.data; +package org.transitclock.api.data; import java.util.ArrayList; import java.util.List; @@ -22,8 +22,8 @@ import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; -import org.transitime.ipc.data.IpcSchedTime; -import org.transitime.ipc.data.IpcSchedTrip; +import org.transitclock.ipc.data.IpcSchedTime; +import org.transitclock.ipc.data.IpcSchedTrip; /** * Contains the schedule times for a trip. For when outputting stops diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiScheduleTrip.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiScheduleTrip.java similarity index 94% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiScheduleTrip.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiScheduleTrip.java index 895df90ef..112a9e835 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiScheduleTrip.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiScheduleTrip.java @@ -15,10 +15,11 @@ * along with Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import javax.xml.bind.annotation.XmlAttribute; -import org.transitime.ipc.data.IpcSchedTrip; + +import org.transitclock.ipc.data.IpcSchedTrip; /** * Represents a single trip for an ApiSchedule diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiScheduleVertStops.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiScheduleVertStops.java similarity index 92% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiScheduleVertStops.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiScheduleVertStops.java index 71b1c02da..65a42caad 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiScheduleVertStops.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiScheduleVertStops.java @@ -15,16 +15,16 @@ * along with Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import java.util.ArrayList; import java.util.List; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; -import org.transitime.ipc.data.IpcSchedule; -import org.transitime.ipc.data.IpcSchedTime; -import org.transitime.ipc.data.IpcSchedTrip; +import org.transitclock.ipc.data.IpcSchedTime; +import org.transitclock.ipc.data.IpcSchedTrip; +import org.transitclock.ipc.data.IpcSchedule; /** * Represents a schedule for a route for a specific direction and service class. diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiSchedulesHorizStops.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiSchedulesHorizStops.java similarity index 93% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiSchedulesHorizStops.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiSchedulesHorizStops.java index 459520592..76ec11de6 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiSchedulesHorizStops.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiSchedulesHorizStops.java @@ -24,7 +24,7 @@ * @author SkiBu Smith * */ -package org.transitime.api.data; +package org.transitclock.api.data; import java.util.ArrayList; import java.util.List; @@ -33,7 +33,7 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; -import org.transitime.ipc.data.IpcSchedule; +import org.transitclock.ipc.data.IpcSchedule; @XmlRootElement(name = "schedules") public class ApiSchedulesHorizStops { diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiSchedulesVertStops.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiSchedulesVertStops.java similarity index 96% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiSchedulesVertStops.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiSchedulesVertStops.java index dc839d6fe..36c073977 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiSchedulesVertStops.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiSchedulesVertStops.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import java.util.ArrayList; import java.util.List; @@ -24,7 +24,7 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; -import org.transitime.ipc.data.IpcSchedule; +import org.transitclock.ipc.data.IpcSchedule; /** * Represents a collection of ApiScheduleVertStops objects for a route. There is diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiServerMonitor.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiServerMonitor.java similarity index 94% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiServerMonitor.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiServerMonitor.java index c90dcc3ff..cf4498cf9 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiServerMonitor.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiServerMonitor.java @@ -15,12 +15,12 @@ * along with Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlValue; -import org.transitime.monitoring.MonitorResult; +import org.transitclock.monitoring.MonitorResult; /** * diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiServerStatus.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiServerStatus.java similarity index 93% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiServerStatus.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiServerStatus.java index cf9a4baa2..64ad37127 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiServerStatus.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiServerStatus.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import java.util.ArrayList; import java.util.List; @@ -24,8 +24,8 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; -import org.transitime.ipc.data.IpcServerStatus; -import org.transitime.monitoring.MonitorResult; +import org.transitclock.ipc.data.IpcServerStatus; +import org.transitclock.monitoring.MonitorResult; /** * Server status for an agency server diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiShape.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiShape.java similarity index 70% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiShape.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiShape.java index af9f47655..b2476988b 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiShape.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiShape.java @@ -15,7 +15,7 @@ * Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import java.util.ArrayList; import java.util.List; @@ -23,8 +23,9 @@ import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; -import org.transitime.db.structs.Location; -import org.transitime.ipc.data.IpcShape; +import org.transitclock.db.structs.Location; +import org.transitclock.ipc.data.IpcShape; +import org.transitclock.utils.Geo; /** * A portion of a shape that defines a trip pattern. A List of ApiLocation @@ -48,7 +49,18 @@ public class ApiShape { @XmlElement(name = "loc") private List locations; + @XmlAttribute + private double length; + @XmlAttribute + private String directionId; + + //To define what kind of pattern is: circular (loop, one ending), linear (normal line with two different endings) + @XmlAttribute + private String patternType="linear"; + private static final int LOOP_ENDING_MAX_DISTANCE=200; + private static final String LOOP_PATTERN="circular"; + private static final String LINAR_PATTER="linear"; /********************** Member Functions **************************/ /** @@ -61,15 +73,20 @@ protected ApiShape() { public ApiShape(IpcShape shape) { this.tripPatternId = shape.getTripPatternId(); this.headsign = shape.getHeadsign(); - + this.length=shape.getLength(); + this.directionId=shape.getDirectionId(); // If true then set to null so that this attribute won't then be // output as XML/JSON, therefore making output a bit more compact. this.minor = shape.isUiShape() ? null : true; - this.locations = new ArrayList(); for (Location loc : shape.getLocations()) { this.locations.add(new ApiLocation(loc.getLat(), loc.getLon())); } + int size=shape.getLocations().size(); + if(size>0 && Geo.distance(shape.getLocations().get(0), shape.getLocations().get(size-1)). */ -package org.transitime.api.data; +package org.transitclock.api.data; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlType; -import org.transitime.ipc.data.IpcStop; +import org.transitclock.ipc.data.IpcStop; /** * Full description of a stop. @@ -32,7 +32,7 @@ * @author SkiBu Smith * */ -@XmlType(propOrder = { "id", "lat", "lon", "name", "code", "minor" }) +@XmlType(propOrder = { "id", "lat", "lon", "name", "code", "minor", "pathLength"}) public class ApiStop extends ApiTransientLocation { @XmlAttribute @@ -48,6 +48,8 @@ public class ApiStop extends ApiTransientLocation { // is not on a main trip pattern. @XmlAttribute(name = "minor") private Boolean minor; + @XmlAttribute + private Double pathLength; /********************** Member Functions **************************/ @@ -63,6 +65,7 @@ public ApiStop(IpcStop stop) { this.id = stop.getId(); this.name = stop.getName(); this.code = stop.getCode(); + this.pathLength=stop.getStopPathLength()==null?0.0:stop.getStopPathLength(); // If true then set to null so that this attribute won't then be // output as XML/JSON, therefore making output a bit more compact. this.minor = stop.isUiStop() ? null : true; diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/ApiStopLevel.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiStopLevel.java new file mode 100644 index 000000000..727c7d253 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiStopLevel.java @@ -0,0 +1,92 @@ +package org.transitclock.api.data; + +import org.transitclock.ipc.data.IpcPrediction; +import org.transitclock.ipc.data.IpcPredictionsForRouteStopDest; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import java.util.ArrayList; +import java.util.List; + +/** + * Model representing StopLevel Prediction data per the + * /coammand/StopLevels API + */ +@XmlRootElement +public class ApiStopLevel { + + + @XmlElement(name = "stopId") + private String stopId; + @XmlElement(name = "route") + private String route; //routeShortName + @XmlElement(name = "number_of_services") + private String numberOfServices = "3"; + @XmlElement(name = "predictions") + private ArrayList predictions = new ArrayList(); + + private int services = 3; + + // needed for serialization + protected ApiStopLevel() { + + } + + /** + * Error constructor -- only use this if no predictions will be present. + * @param rsn containing the errorDescription + */ + public ApiStopLevel(RouteStopNumberParameter rsn) { + stopId = rsn.getStopId(); // could be null + route = rsn.getRoute(); // could be null + numberOfServices = "0"; + predictions.add(new ApiStopLevelPrediction(rsn.getErrorDescription())); + } + + /** + * Typical contructor used to supply the input query and the resulting + * predictions + * @param rsn + * @param routeStopPredictions + */ + public ApiStopLevel(RouteStopNumberParameter rsn, List routeStopPredictions) { + stopId = rsn.getStopId(); + route = rsn.getRoute(); + + try { + numberOfServices = rsn.getNumberOfServices(); + services = Integer.parseInt(numberOfServices); + } catch (NumberFormatException nfe) { + services = 3; + // we just ignore an invalid service count + } + + int servicesCount = 0; + for (IpcPredictionsForRouteStopDest routeStopPrediction : routeStopPredictions) { + if (routeStopPrediction.getPredictionsForRouteStop() != null) { + for (IpcPrediction ipc : routeStopPrediction.getPredictionsForRouteStop()) { + servicesCount++; + predictions.add(new ApiStopLevelPrediction(ipc)); + if (servicesCount >= rsn.getNumberOfServicesAsInt()) { + return; + } + } + } + } + + // now set the services to be what was actually provided + this.numberOfServices = String.valueOf(servicesCount); + if (servicesCount == 0) { + if (rsn.getErrorDescription() == null) { + predictions.add(new ApiStopLevelPrediction("Route and Stop combination yielded no predictions")); + } else { + predictions.add(new ApiStopLevelPrediction(rsn.getErrorDescription())); + } + } else if (rsn.getErrorDescription() != null) { + // report an error even though we have predictions + predictions.add(new ApiStopLevelPrediction(rsn.getErrorDescription())); + } + + } + +} diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/ApiStopLevelPrediction.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiStopLevelPrediction.java new file mode 100644 index 000000000..aecdbe145 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiStopLevelPrediction.java @@ -0,0 +1,51 @@ +package org.transitclock.api.data; + +import org.transitclock.ipc.data.IpcPrediction; + +import javax.xml.bind.annotation.XmlElement; + +/** + * Model representing an individual prediction of a stop level API. + */ +public class ApiStopLevelPrediction { + + private static final int VALID_FALSE = 0; + private static final int VALID_TRUE = 1; + + @XmlElement(name = "is_valid") + private Integer isValid = VALID_TRUE; + @XmlElement(name = "error_desc") + private String errorDescription = null; + @XmlElement(name = "time") + private Long time = null; //seconds from epoch + @XmlElement(name = "trip") + private String trip; + @XmlElement(name = "vehicle") + private String vehicle; + + // required for serialization + protected ApiStopLevelPrediction() { + } + + public ApiStopLevelPrediction(IpcPrediction prediction) { + if (prediction == null) { + isValid = VALID_FALSE; + return; + } + trip = prediction.getTripId(); + vehicle = prediction.getVehicleId(); + if (prediction.getPredictionTime() > 0) { + time = prediction.getPredictionTime() / 1000; // seconds + } else { + isValid = VALID_FALSE; + errorDescription = "Invalid prediction value of " + time; + } + + } + + public ApiStopLevelPrediction(String errorDescription) { + this.errorDescription = errorDescription; + isValid = VALID_FALSE; + } + +} diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/ApiStopLevels.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiStopLevels.java new file mode 100644 index 000000000..5dd00522d --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiStopLevels.java @@ -0,0 +1,291 @@ +package org.transitclock.api.data; + +import org.transitclock.api.utils.StandardParameters; +import org.transitclock.ipc.data.IpcPredictionsForRouteStopDest; +import org.transitclock.ipc.interfaces.PredictionsInterface; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import java.util.ArrayList; +import java.util.List; + +/** + * Model representing aggregation of StopLevel Prediction data per the + * /coammand/StopLevels API + */ + +@XmlRootElement +public class ApiStopLevels { + + @XmlElement(name = "stop_level") + private ArrayList stopLevels = new ArrayList<>(); + + // needed for serialzation + protected ApiStopLevels() { + + } + + /** + * Public constructor that passed through prediction interfaces and query parameters + * via the StandardParmeters object. Typical errors are caught and reported via + * the errorDescription field in child objects. + * @param stdParameters + */ + public ApiStopLevels(StandardParameters stdParameters) { + List queries = parseQuery(stdParameters.getRequest().getParameter("rs")); + + for (RouteStopNumberParameter rsn : queries) { + if (rsn.isInvalid()) { + // invalid query -- no need to try and retrieve prediction + ApiStopLevel asl = new ApiStopLevel(rsn); + } else { + try { + PredictionsInterface.RouteStop routeStop = new PredictionsInterface.RouteStop(rsn.getRoute(), rsn.getStopId()); + List routeStopList = new ArrayList<>(); + routeStopList.add(routeStop); + List ipcPredictionsForRouteStopDests = stdParameters.getPredictionsInterface().get(routeStopList, rsn.getNumberOfServicesAsInt()); + stopLevels.add(new ApiStopLevel(rsn, ipcPredictionsForRouteStopDests)); + } catch (Exception any) { + rsn.setInvalid(); + rsn.setErrorDescription(any.toString()); + stopLevels.add(new ApiStopLevel(rsn)); } + } + } + } + + /** + * Break the user request in a list of RouteStopNumberParameters. + * @param query + * @return + */ + private List parseQuery(String query) { + // query format: s=stopId|r=route|n=number_of_services{:s=stopId|r=route|n=number_of_services} + ArrayList queries = new ArrayList(); + + + int start = 0; + int delim = query.indexOf(":", start); + while (delim > 0) { + String subQuery = query.substring(start, delim); + RouteStopNumberParameter rsn = parseFragment(subQuery); + queries.add(rsn); + start = delim; + start++; // advance start past delim + delim = query.indexOf(":", start); + } + + String lastQuery = query.substring(start); + RouteStopNumberParameter rsn = parseFragment(lastQuery); + queries.add(rsn); + return queries; + } + + private String[] toArray(String s) { + String a[] = {s}; + return a; + } + private String[] toArray(String s1, String s2) { + String a[] = {s1, s2}; + return a; + } + private String[] toArray(String s1, String s2, String s3) { + String a[] = {s1, s2, s3}; + return a; + } + + private RouteStopNumberParameter parseArgs(String[] args) { + RouteStopNumberParameter rsn = new RouteStopNumberParameter(); + if (args == null) return rsn; + for (String s:args) { + if (s.startsWith("s=")) + rsn.setStopId(s.substring(2)); + if (s.startsWith("r=")) + rsn.setRoute(s.substring(2)); + if (s.startsWith("n=")) + rsn.setNumberOfServices(s.substring(2)); + } + return rsn; + } + /** + * Break a fragment of the query into a single RouteStopNumberParemeter + * or pupulate errorDescription if the syntax is invald. + * @param query + * @return + */ + private RouteStopNumberParameter parseFragment(String query) { + RouteStopNumberParameter rsn = new RouteStopNumberParameter(); + + if (query == null || query.length() == 0) { + rsn.setInvalid(); + rsn.setErrorDescription("Missing parameter"); + return rsn; + } + + int firstDelim = query.indexOf("|"); + if (firstDelim < 0) { + return parseArgs(toArray(query)); + } + int secondDelim = query.indexOf("|", firstDelim+1); + if (secondDelim < 0) { + return parseArgs(toArray(query.substring(0, firstDelim), + query.substring(firstDelim+1))); + } + return parseArgs(toArray(query.substring(0, firstDelim), + query.substring(firstDelim+1, secondDelim), + query.substring(secondDelim+1))); + } + + public static void main(String[] argsv) { + // unit test of parameter parsing + RouteStopNumberParameter rsn; + ApiStopLevels asl = new ApiStopLevels(); + List rsns; + + String p = null; + rsn = asl.parseFragment(p); + testAssert(rsn != null); + testAssert(rsn.isInvalid()); + testAssert(rsn.getNumberOfServices() == null); + + String p1 = ""; + rsn = asl.parseFragment(p1); + testAssert(rsn != null); + testAssert(rsn.isInvalid() == true); + testAssert(rsn.getRoute() == null); + testAssert(rsn.getNumberOfServices() == null); + + String p2 = "s=stop1"; + rsn = asl.parseFragment(p2); + testAssert(rsn.isInvalid() == false); + testAssert(rsn.getStopId().equals("stop1")); + testAssert(rsn.getRoute() == null); + testAssert(rsn.getNumberOfServices().equals("3")); + + String p2a = "s=stop1|n=6"; + rsn = asl.parseFragment(p2a); + testAssert(rsn.isInvalid() == false); + testAssert(rsn.getStopId().equals("stop1")); + testAssert(rsn.getRoute() == null); + testAssert(rsn.getNumberOfServices().equals("6")); + + + String p3 = "s=stop1|r=routeShortName2"; + rsn = asl.parseFragment(p3); + testAssert(rsn.isInvalid() == false); + testAssert(rsn.getStopId().equals("stop1")); + testAssert(rsn.getRoute().equals("routeShortName2")); + testAssert(rsn.getNumberOfServices().equals("3")); + + String p4 = "s=stop1|r=routeShortName2|n=4"; + rsn = asl.parseFragment(p4); + testAssert(rsn.isInvalid() == false); + testAssert(rsn.getStopId().equals("stop1")); + testAssert(rsn.getRoute().equals("routeShortName2")); + testAssert(rsn.getNumberOfServices().equals("4")); + + String p5 = "s=stop1|r=routeShortName2|n=4"; + rsns = asl.parseQuery(p5); + testAssert(rsns.size() == 1); + rsn = asl.parseQuery(p5).get(0); + testAssert(rsn.isInvalid() == false); + testAssert(rsn.getStopId().equals("stop1")); + testAssert(rsn.getRoute().equals("routeShortName2")); + testAssert(rsn.getNumberOfServices().equals("4")); + + String p6 = "s=stop1|r=routeShortName2|n=4:"; + rsns = asl.parseQuery(p6); + testAssert(rsns.size() == 2); + rsn = rsns.get(0); + testAssert(rsn.isInvalid() == false); + testAssert(rsn.getStopId().equals("stop1")); + testAssert(rsn.getRoute().equals("routeShortName2")); + testAssert(rsn.getNumberOfServices().equals("4")); + rsn = rsns.get(1); + testAssert(rsn != null); + testAssert(rsn.isInvalid() == true); + testAssert(rsn.getRoute() == null); + testAssert(rsn.getNumberOfServices() == null); + + String p6a = "r=routeShortName2|s=stop1|n=4:"; + rsns = asl.parseQuery(p6a); + testAssert(rsns.size() == 2); + rsn = rsns.get(0); + testAssert(rsn.isInvalid() == false); + testAssert(rsn.getStopId().equals("stop1")); + testAssert(rsn.getRoute().equals("routeShortName2")); + testAssert(rsn.getNumberOfServices().equals("4")); + rsn = rsns.get(1); + testAssert(rsn != null); + testAssert(rsn.isInvalid() == true); + testAssert(rsn.getRoute() == null); + testAssert(rsn.getNumberOfServices() == null); + + String p6b = "n=4|r=routeShortName2|s=stop1:"; + rsns = asl.parseQuery(p6b); + testAssert(rsns.size() == 2); + rsn = rsns.get(0); + testAssert(rsn.isInvalid() == false); + testAssert(rsn.getStopId().equals("stop1")); + testAssert(rsn.getRoute().equals("routeShortName2")); + testAssert(rsn.getNumberOfServices().equals("4")); + rsn = rsns.get(1); + testAssert(rsn != null); + testAssert(rsn.isInvalid() == true); + testAssert(rsn.getRoute() == null); + testAssert(rsn.getNumberOfServices() == null); + + String p7 = "s=stop1|r=routeShortName2|n=4:s=stop2|r=routeShortName3|n=5:"; + rsns = asl.parseQuery(p7); + testAssert(rsns.size() == 3); + rsn = rsns.get(0); + testAssert(rsn.isInvalid() == false); + testAssert(rsn.getStopId().equals("stop1")); + testAssert(rsn.getRoute().equals("routeShortName2")); + testAssert(rsn.getNumberOfServices().equals("4")); + rsn = rsns.get(1); + testAssert(rsn.isInvalid() == false); + testAssert(rsn.getStopId().equals("stop2")); + testAssert(rsn.getRoute().equals("routeShortName3")); + testAssert(rsn.getNumberOfServices().equals("5")); + rsn = rsns.get(2); + testAssert(rsn != null); + testAssert(rsn.isInvalid() == true); + testAssert(rsn.getRoute() == null); + testAssert(rsn.getNumberOfServices() == null); + + String p8 = "s=stop1|r=routeShortName2|n=4:s=stop2|r=routeShortName3|n=5:s=stop3:"; + rsns = asl.parseQuery(p8); + testAssert(rsns.size() == 4); + rsn = rsns.get(0); + testAssert(rsn.isInvalid() == false); + testAssert(rsn.getStopId().equals("stop1")); + testAssert(rsn.getRoute().equals("routeShortName2")); + testAssert(rsn.getNumberOfServices().equals("4")); + rsn = rsns.get(1); + testAssert(rsn.isInvalid() == false); + testAssert(rsn.getStopId().equals("stop2")); + testAssert(rsn.getRoute().equals("routeShortName3")); + testAssert(rsn.getNumberOfServices().equals("5")); + + rsn = rsns.get(2); + testAssert(rsn.isInvalid() == false); + testAssert(rsn.getStopId().equals("stop3")); + testAssert(rsn.getRoute() == null); + testAssert(rsn.getNumberOfServices().equals("3")); + + rsn = rsns.get(3); + testAssert(rsn != null); + testAssert(rsn.isInvalid() == true); + testAssert(rsn.getRoute() == null); + testAssert(rsn.getNumberOfServices() == null); + + } + + private static void testAssert(boolean flag) { + if (!flag) { + throw new AssertionError("fail"); + } + } + + +} diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiStopPath.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiStopPath.java similarity index 93% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiStopPath.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiStopPath.java index 0f6e3b6b0..8751f05f2 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiStopPath.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiStopPath.java @@ -15,7 +15,7 @@ * Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import java.util.ArrayList; import java.util.List; @@ -23,9 +23,9 @@ import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; -import org.transitime.db.structs.Location; -import org.transitime.ipc.data.IpcStopPath; -import org.transitime.utils.MathUtils; +import org.transitclock.db.structs.Location; +import org.transitclock.ipc.data.IpcStopPath; +import org.transitclock.utils.MathUtils; /** * Represents a path from one stop to another. diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/ApiStopPathWithSpeed.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiStopPathWithSpeed.java new file mode 100644 index 000000000..3023605e9 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiStopPathWithSpeed.java @@ -0,0 +1,106 @@ +/* + * 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.api.data; + +import org.transitclock.ipc.data.IpcStopPathWithSpeed; +import org.transitclock.utils.MathUtils; + +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import java.util.List; + +/** + * Represents a path from one stop to another. + * + * @author SkiBu Smith + * + */ +public class ApiStopPathWithSpeed { + + @XmlAttribute + private int configRev; + + @XmlAttribute + private String stopPathId; + + @XmlAttribute + private String stopId; + + @XmlAttribute + private String stopName; + + @XmlAttribute + private int gtfsStopSeq; + + @XmlAttribute + private Boolean layoverStop; + + @XmlAttribute + private Boolean waitStop; + + @XmlAttribute + private Boolean scheduleAdherenceStop; + + @XmlAttribute + private Integer breakTime; + + @XmlElement(name = "latlngs") + private List locations; + + @XmlAttribute + private Double pathLength; + + @XmlAttribute + private Double speed; + + /********************** Member Functions **************************/ + + /** + * Need a no-arg constructor for Jersey. Otherwise get really obtuse + * "MessageBodyWriter not found for media type=application/json" exception. + */ + protected ApiStopPathWithSpeed() { + } + + public ApiStopPathWithSpeed(IpcStopPathWithSpeed ipcStopPathWithSpeed) { + stopPathId = ipcStopPathWithSpeed.getStopPathId(); + stopId = ipcStopPathWithSpeed.getStopId(); + stopName = ipcStopPathWithSpeed.getStopName(); + gtfsStopSeq = ipcStopPathWithSpeed.getGtfsStopSeq(); + layoverStop = ipcStopPathWithSpeed.isLayoverStop() ? true : null; + waitStop = ipcStopPathWithSpeed.isWaitStop() ? true : null; + scheduleAdherenceStop = + ipcStopPathWithSpeed.isScheduleAdherenceStop() ? true : null; + breakTime = + ipcStopPathWithSpeed.getBreakTime() != 0 ? ipcStopPathWithSpeed.getBreakTime() + : null; + + locations = ipcStopPathWithSpeed.getLocations(); + + //locations = new ArrayList<>(); + + /*for(int i = 0; i < locationsList.size(); i++){ + Double lat = MathUtils.round(locationsList.get(i).getLat(),1); + Double lon = MathUtils.round(locationsList.get(i).getLon(),1); + locations.add(Arrays.asList(lat, lon)); + }*/ + + pathLength = MathUtils.round(ipcStopPathWithSpeed.getPathLength(), 1); + speed = ipcStopPathWithSpeed.getSpeed() != null ? MathUtils.round(ipcStopPathWithSpeed.getSpeed(), 1) : null; + } +} diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/ApiStopPathsWithSpeed.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiStopPathsWithSpeed.java new file mode 100644 index 000000000..1e38ea191 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiStopPathsWithSpeed.java @@ -0,0 +1,64 @@ +/* + * 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.api.data; + +import org.transitclock.ipc.data.IpcStopPathWithSpeed; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import java.util.ArrayList; +import java.util.List; + +/** + * An ordered list of stopPaths. + * + * @author SkiBu Smith + * + */ +@XmlRootElement +public class ApiStopPathsWithSpeed { + // So can easily get agency name when getting routes. Useful for db reports + // and such. + + // List of stop paths info + @XmlElement(name = "stopPaths") + private List stopPathData; + + /********************** Member Functions **************************/ + + /** + * Need a no-arg constructor for Jersey. Otherwise get really obtuse + * "MessageBodyWriter not found for media type=application/json" exception. + */ + protected ApiStopPathsWithSpeed() { + } + + /** + * Constructs an ApiStopPaths using a collection of IpcStopPath objects. + * + * @param stopPaths + * so can get agency name + */ + public ApiStopPathsWithSpeed(List stopPaths) { + stopPathData = new ArrayList<>(); + for (IpcStopPathWithSpeed stopPath : stopPaths) { + ApiStopPathWithSpeed apiStopPath = new ApiStopPathWithSpeed(stopPath); + stopPathData.add(apiStopPath); + } + } +} diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/ApiStopSimple.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiStopSimple.java new file mode 100644 index 000000000..6a9c2af67 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiStopSimple.java @@ -0,0 +1,32 @@ +package org.transitclock.api.data; + +import org.transitclock.ipc.data.IpcStop; + +import javax.xml.bind.annotation.XmlAttribute; + +public class ApiStopSimple { + @XmlAttribute + private String id; + + @XmlAttribute + private String name; + + @XmlAttribute + private Integer code; + + /** + * Need a no-arg constructor for Jersey. Otherwise get really obtuse + * "MessageBodyWriter not found for media type=application/json" exception. + */ + protected ApiStopSimple() { + } + + public ApiStopSimple(IpcStop stop) { + this.id = stop.getId(); + this.name = stop.getName(); + this.code = stop.getCode(); + } + + +} + diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/ApiStopWithDwellTime.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiStopWithDwellTime.java new file mode 100644 index 000000000..5aa0e98de --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiStopWithDwellTime.java @@ -0,0 +1,33 @@ +package org.transitclock.api.data; + +import org.transitclock.ipc.data.IpcStopWithDwellTime; + +import javax.xml.bind.annotation.XmlAttribute; + +public class ApiStopWithDwellTime extends ApiTransientLocation{ + @XmlAttribute(name = "stopId") + private String stopId; + + @XmlAttribute(name = "stopName") + private String stopName; + + @XmlAttribute(name = "stopCode") + private Integer stopCode; + + @XmlAttribute(name = "avgDwellTime") + private Double avgDwellTime; + + public ApiStopWithDwellTime(){} + + public ApiStopWithDwellTime(IpcStopWithDwellTime stopWithDwellTime, Double avgDwellTime){ + super(stopWithDwellTime.getLoc().getLat(), stopWithDwellTime.getLoc().getLon()); + this.stopId = stopWithDwellTime.getId(); + this.stopName = stopWithDwellTime.getName(); + this.stopCode = stopWithDwellTime.getCode(); + if(avgDwellTime != null){ + this.avgDwellTime = Math.rint(avgDwellTime/1000); + } else { + this.avgDwellTime = null; + } + } +} diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/ApiStopsForRoute.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiStopsForRoute.java new file mode 100644 index 000000000..8970aa265 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiStopsForRoute.java @@ -0,0 +1,36 @@ +package org.transitclock.api.data; + +import org.transitclock.ipc.data.IpcDirection; +import org.transitclock.ipc.data.IpcDirectionsForRoute; +import org.transitclock.ipc.data.IpcStop; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +@XmlRootElement +public class ApiStopsForRoute { + @XmlElement + List stops; + + /** + * Need a no-arg constructor for Jersey. Otherwise get really obtuse + * "MessageBodyWriter not found for media type=application/json" exception. + */ + protected ApiStopsForRoute(){} + + public ApiStopsForRoute(List stopsForRoutes) { + Set stopsSet = new LinkedHashSet<>(); + for(IpcDirectionsForRoute directionsForRoute : stopsForRoutes){ + for(IpcDirection direction : directionsForRoute.getDirections()){ + for(IpcStop stop: direction.getStops()){ + stopsSet.add(new ApiStopSimple(stop)); + } + } + } + stops = new ArrayList<>(stopsSet); + } +} diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/ApiStopsWithDwellTime.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiStopsWithDwellTime.java new file mode 100644 index 000000000..a4cb0f309 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiStopsWithDwellTime.java @@ -0,0 +1,56 @@ +package org.transitclock.api.data; + +import org.transitclock.ipc.data.IpcStopWithDwellTime; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import java.util.*; + +@XmlRootElement +public class ApiStopsWithDwellTime { + + @XmlElement + private List stops; + + public ApiStopsWithDwellTime(){} + + public ApiStopsWithDwellTime(List stopsWithDwellTime){ + Map stopsMap = new LinkedHashMap<>(); + Map stopIdToDwellTimesMap = new HashMap<>(); + + for(IpcStopWithDwellTime stop : stopsWithDwellTime) { + + IpcStopWithDwellTime ipcStopFromMap = stopsMap.get(stop.getId()); + + if(ipcStopFromMap == null){ + stopsMap.put(stop.getId(), stop); + stopIdToDwellTimesMap.put(stop.getId(), new LongSummaryStatistics()); + } + else if(isStopNewer(ipcStopFromMap, stop)) { + stopsMap.put(stop.getId(), stop); + } + + if (stop.getDwellTime() != null) { + stopIdToDwellTimesMap.get(stop.getId()).accept(stop.getDwellTime()); + } + } + + stops = new ArrayList<>(stopsMap.entrySet().size()); + + for (Map.Entry entry : stopsMap.entrySet()){ + IpcStopWithDwellTime ipcStopWithDwellTime = entry.getValue(); + LongSummaryStatistics dwellTimeStats = stopIdToDwellTimesMap.get(ipcStopWithDwellTime.getId()); + Double avgDwellTime = dwellTimeStats != null && dwellTimeStats.getCount() > 0 ? dwellTimeStats.getAverage() : null; + stops.add(new ApiStopWithDwellTime(ipcStopWithDwellTime, avgDwellTime)); + } + } + + private boolean isStopNewer(IpcStopWithDwellTime currentStop, IpcStopWithDwellTime newStop){ + return currentStop.getConfigRev() < newStop.getConfigRev(); + } + + public List getStops() { + return stops; + } + +} diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiTransientLocation.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiTransientLocation.java similarity index 94% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiTransientLocation.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiTransientLocation.java index ea5af87c9..94a066364 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiTransientLocation.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiTransientLocation.java @@ -15,13 +15,13 @@ * Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlTransient; -import org.transitime.utils.ChinaGpsOffset; -import org.transitime.utils.MathUtils; +import org.transitclock.utils.ChinaGpsOffset; +import org.transitclock.utils.MathUtils; /** * A simple latitude/longitude: diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiTravelTimeForSegment.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiTravelTimeForSegment.java similarity index 93% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiTravelTimeForSegment.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiTravelTimeForSegment.java index 9561e8761..aab2fcdc0 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiTravelTimeForSegment.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiTravelTimeForSegment.java @@ -15,13 +15,13 @@ * Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import javax.xml.bind.annotation.XmlAttribute; -import org.transitime.utils.Geo; -import org.transitime.utils.MathUtils; -import org.transitime.utils.Time; +import org.transitclock.utils.Geo; +import org.transitclock.utils.MathUtils; +import org.transitclock.utils.Time; /** * For representing travel time for a single segment. diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiTravelTimes.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiTravelTimes.java similarity index 93% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiTravelTimes.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiTravelTimes.java index ae1428dec..c0e633c05 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiTravelTimes.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiTravelTimes.java @@ -15,7 +15,7 @@ * Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import java.util.ArrayList; import java.util.List; @@ -23,8 +23,8 @@ import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; -import org.transitime.db.structs.TravelTimesForStopPath; -import org.transitime.db.structs.TravelTimesForTrip; +import org.transitclock.db.structs.TravelTimesForStopPath; +import org.transitclock.db.structs.TravelTimesForTrip; /** * diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiTravelTimesForStopPath.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiTravelTimesForStopPath.java similarity index 93% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiTravelTimesForStopPath.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiTravelTimesForStopPath.java index b6b9e8800..a79ce3eb3 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiTravelTimesForStopPath.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiTravelTimesForStopPath.java @@ -15,7 +15,7 @@ * Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import java.util.ArrayList; import java.util.List; @@ -23,9 +23,9 @@ import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; -import org.transitime.db.structs.TravelTimesForStopPath; -import org.transitime.db.structs.TravelTimesForStopPath.HowSet; -import org.transitime.utils.MathUtils; +import org.transitclock.db.structs.TravelTimesForStopPath; +import org.transitclock.db.structs.TravelTimesForStopPath.HowSet; +import org.transitclock.utils.MathUtils; /** * Represents travel times for a stop path diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiTrip.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiTrip.java similarity index 95% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiTrip.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiTrip.java index 317e562a6..f5e4bec1d 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiTrip.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiTrip.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import java.util.ArrayList; import java.util.List; @@ -24,9 +24,9 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; -import org.transitime.ipc.data.IpcSchedTimes; -import org.transitime.ipc.data.IpcTrip; -import org.transitime.utils.Time; +import org.transitclock.ipc.data.IpcSchedTimes; +import org.transitclock.ipc.data.IpcTrip; +import org.transitclock.utils.Time; /** * Specifies how trip data is formatted for the API. diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiTripPattern.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiTripPattern.java similarity index 88% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiTripPattern.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiTripPattern.java index 5c68ffa56..055bd5041 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiTripPattern.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiTripPattern.java @@ -15,7 +15,7 @@ * Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import java.util.ArrayList; import java.util.List; @@ -23,8 +23,8 @@ import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; -import org.transitime.ipc.data.IpcStopPath; -import org.transitime.ipc.data.IpcTripPattern; +import org.transitclock.ipc.data.IpcStopPath; +import org.transitclock.ipc.data.IpcTripPattern; /** * A single trip pattern @@ -58,6 +58,12 @@ public class ApiTripPattern { @XmlAttribute private String shapeId; + @XmlAttribute + private String firstStopName; + + @XmlAttribute + private String lastStopName; + @XmlElement private List stopPaths; @@ -87,6 +93,8 @@ public ApiTripPattern(IpcTripPattern ipcTripPattern, routeShortName = ipcTripPattern.getRouteShortName(); extent = new ApiExtent(ipcTripPattern.getExtent()); shapeId = ipcTripPattern.getShapeId(); + firstStopName = ipcTripPattern.getFirstStopName(); + lastStopName = ipcTripPattern.getLastStopName(); // Only include stop paths if actually want them. This // can greatly reduce volume of the output. diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiTripPatterns.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiTripPatterns.java similarity index 89% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiTripPatterns.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiTripPatterns.java index c61555d8a..2210e1cf5 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiTripPatterns.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiTripPatterns.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import java.util.ArrayList; import java.util.Collection; @@ -24,7 +24,7 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; -import org.transitime.ipc.data.IpcTripPattern; +import org.transitclock.ipc.data.IpcTripPattern; /** * A list of ApiTripPattern objects @@ -47,13 +47,13 @@ public class ApiTripPatterns { */ protected ApiTripPatterns() {} - public ApiTripPatterns(Collection ipcTripPatterns) { + public ApiTripPatterns(Collection ipcTripPatterns, Boolean includeStopPaths) { tripPatterns = new ArrayList(); for (IpcTripPattern ipcTripPattern : ipcTripPatterns) { // Including stop paths in output since that is likely // what user wants since they are trying to understand // the trip patterns for a route. - tripPatterns.add(new ApiTripPattern(ipcTripPattern, true)); + tripPatterns.add(new ApiTripPattern(ipcTripPattern, includeStopPaths)); } } } diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiTripTerse.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiTripTerse.java similarity index 94% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiTripTerse.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiTripTerse.java index 19b2dc3df..7f77366ba 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiTripTerse.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiTripTerse.java @@ -15,13 +15,13 @@ * along with Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; -import org.transitime.ipc.data.IpcTrip; -import org.transitime.utils.Time; +import org.transitclock.ipc.data.IpcTrip; +import org.transitclock.utils.Time; /** * A shorter version of ApiTrip for when all the detailed info is not diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiTripWithTravelTimes.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiTripWithTravelTimes.java similarity index 95% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiTripWithTravelTimes.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiTripWithTravelTimes.java index a6e89a231..8ce9a5367 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiTripWithTravelTimes.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiTripWithTravelTimes.java @@ -15,13 +15,13 @@ * Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; + +import org.transitclock.ipc.data.IpcTrip; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; -import org.transitime.ipc.data.IpcTrip; - /** * Specifies how trip data along with travel times is formatted for the API. * diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiVehicle.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiVehicle.java similarity index 93% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiVehicle.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiVehicle.java index 9b864e5bf..6d16c7b3b 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiVehicle.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiVehicle.java @@ -15,13 +15,13 @@ * along with Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; -import org.transitime.api.rootResources.TransitimeApi.UiMode; -import org.transitime.ipc.data.IpcVehicle; +import org.transitclock.api.rootResources.TransitimeApi.UiMode; +import org.transitclock.ipc.data.IpcVehicle; /** * Contains the data for a single vehicle. diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiVehicleAbstract.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiVehicleAbstract.java similarity index 96% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiVehicleAbstract.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiVehicleAbstract.java index eb346ec11..7c9ad3c0e 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiVehicleAbstract.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiVehicleAbstract.java @@ -15,14 +15,14 @@ * Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlTransient; -import org.transitime.api.rootResources.TransitimeApi.UiMode; -import org.transitime.ipc.data.IpcVehicle; +import org.transitclock.api.rootResources.TransitimeApi.UiMode; +import org.transitclock.ipc.data.IpcVehicle; /** * This class exists so that can have multiple subclasses that inherent from diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiVehicleConfig.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiVehicleConfig.java similarity index 95% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiVehicleConfig.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiVehicleConfig.java index e36f5e418..6e75b8ee0 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiVehicleConfig.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiVehicleConfig.java @@ -15,13 +15,13 @@ * along with Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; -import org.transitime.ipc.data.IpcVehicleConfig; +import org.transitclock.ipc.data.IpcVehicleConfig; /** * Contains config data for single vehicle. diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiVehicleConfigs.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiVehicleConfigs.java similarity index 96% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiVehicleConfigs.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiVehicleConfigs.java index 26ca9728f..98805d129 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiVehicleConfigs.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiVehicleConfigs.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import java.util.ArrayList; import java.util.Collection; @@ -26,7 +26,7 @@ import javax.xml.bind.annotation.XmlElementRef; import javax.xml.bind.annotation.XmlRootElement; -import org.transitime.ipc.data.IpcVehicleConfig; +import org.transitclock.ipc.data.IpcVehicleConfig; /** * For when have collection of ApiVehicleConfig diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiVehicleDetails.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiVehicleDetails.java similarity index 70% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiVehicleDetails.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiVehicleDetails.java index f50ba5bce..7ca1ed6be 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiVehicleDetails.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiVehicleDetails.java @@ -15,17 +15,21 @@ * along with Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; + +import java.lang.reflect.InvocationTargetException; +import java.util.Date; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; -import org.transitime.api.rootResources.TransitimeApi.UiMode; -import org.transitime.core.BlockAssignmentMethod; -import org.transitime.ipc.data.IpcVehicle; -import org.transitime.utils.Time; +import org.transitclock.api.rootResources.TransitimeApi.UiMode; +import org.transitclock.core.BlockAssignmentMethod; +import org.transitclock.ipc.data.IpcVehicle; +import org.transitclock.ipc.data.IpcVehicleComplete; +import org.transitclock.utils.Time; /** * Contains data for a single vehicle with additional info that is meant more @@ -40,7 +44,7 @@ "scheduleAdherence", "scheduleAdherenceStr", "blockId", "blockAssignmentMethod", "tripId", "tripPatternId", "isDelayed", "isLayover", "layoverDepTime", "layoverDepTimeStr", "nextStopId", - "nextStopName", "driverId" }) + "nextStopName", "driverId", "holdingTime" }) public class ApiVehicleDetails extends ApiVehicleAbstract { @XmlAttribute @@ -86,6 +90,30 @@ public class ApiVehicleDetails extends ApiVehicleAbstract { @XmlElement(name = "driver") private String driverId; + + @XmlAttribute + private Boolean isScheduledService; + + @XmlAttribute + private Long freqStartTime; + + @XmlAttribute + private Boolean isAtStop; + + @XmlElement + private ApiHoldingTime holdingTime; + + @XmlAttribute + private double distanceAlongTrip; + + @XmlAttribute + private String licensePlate; + + @XmlAttribute + private boolean isCanceled; + + @XmlAttribute + private double headway; /** * Need a no-arg constructor for Jersey. Otherwise get really obtuse @@ -103,8 +131,10 @@ protected ApiVehicleDetails() { * @param uiType * Optional parameter. If should be labeled as "minor" in output * for UI. Default is UiMode.NORMAL. + * @throws InvocationTargetException + * @throws IllegalAccessException */ - public ApiVehicleDetails(IpcVehicle vehicle, Time timeForAgency, UiMode... uiType) { + public ApiVehicleDetails(IpcVehicle vehicle, Time timeForAgency, UiMode... uiType) throws IllegalAccessException, InvocationTargetException { super(vehicle, uiType.length > 0 ? uiType[0] : UiMode.NORMAL); routeName = vehicle.getRouteName(); @@ -130,7 +160,32 @@ public ApiVehicleDetails(IpcVehicle vehicle, Time timeForAgency, UiMode... uiTyp nextStopName = vehicle.getNextStopName() != null ? vehicle.getNextStopName() : null; - driverId = vehicle.getAvl().getDriverId(); + driverId = vehicle.getAvl().getDriverId(); + licensePlate=vehicle.getLicensePlate(); + isCanceled=false; + headway=-1; + if(vehicle instanceof IpcVehicleComplete && tripId!=null ) + { + distanceAlongTrip=((IpcVehicleComplete)vehicle).getDistanceAlongTrip(); + isCanceled=((IpcVehicleComplete)vehicle).isCanceled(); + headway=((IpcVehicleComplete)vehicle).getHeadway(); + } + isScheduledService = vehicle.getFreqStartTime() > 0 ? false : true; + if(!isScheduledService) + freqStartTime = vehicle.getFreqStartTime(); + else + freqStartTime = null; + + this.isAtStop = vehicle.isAtStop(); + + if(vehicle.getHoldingTime()!=null) + { + this.holdingTime = new ApiHoldingTime(vehicle.getHoldingTime()); + } + else + { + this.holdingTime = null; + } } } diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiVehicles.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiVehicles.java similarity index 94% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiVehicles.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiVehicles.java index 54a158c7e..c7b20b183 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiVehicles.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiVehicles.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; import java.util.ArrayList; import java.util.Collection; @@ -25,8 +25,8 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; -import org.transitime.api.rootResources.TransitimeApi.UiMode; -import org.transitime.ipc.data.IpcVehicle; +import org.transitclock.api.rootResources.TransitimeApi.UiMode; +import org.transitclock.ipc.data.IpcVehicle; /** * For when have list of Vehicles. By using this class can control the element diff --git a/transitimeApi/src/main/java/org/transitime/api/data/ApiVehiclesDetails.java b/transitclockApi/src/main/java/org/transitclock/api/data/ApiVehiclesDetails.java similarity index 68% rename from transitimeApi/src/main/java/org/transitime/api/data/ApiVehiclesDetails.java rename to transitclockApi/src/main/java/org/transitclock/api/data/ApiVehiclesDetails.java index 71e488984..481e6c96d 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/ApiVehiclesDetails.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/ApiVehiclesDetails.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,21 +15,23 @@ * along with Transitime.org . If not, see . */ -package org.transitime.api.data; +package org.transitclock.api.data; +import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.concurrent.TimeUnit; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; -import org.transitime.api.rootResources.TransitimeApi.UiMode; -import org.transitime.db.structs.Agency; -import org.transitime.db.webstructs.WebAgency; -import org.transitime.ipc.data.IpcVehicle; -import org.transitime.utils.Time; +import org.transitclock.api.rootResources.TransitimeApi.UiMode; +import org.transitclock.db.structs.Agency; +import org.transitclock.db.webstructs.WebAgency; +import org.transitclock.ipc.data.IpcVehicle; +import org.transitclock.utils.Time; /** * For when have list of VehicleDetails. By using this class can control the @@ -41,6 +43,9 @@ @XmlRootElement public class ApiVehiclesDetails { + @XmlElement(name = "responseTime") + private long responseTime; + @XmlElement(name = "vehicles") private List vehiclesData; @@ -56,29 +61,34 @@ protected ApiVehiclesDetails() { /** * For constructing a ApiVehiclesDetails object from a Collection of Vehicle * objects. - * + * * @param vehicles * @param agencyId * @param uiTypesForVehicles * Specifies how vehicles should be drawn in UI. Can be NORMAL, * SECONDARY, or MINOR + * @param assigned + * @throws InvocationTargetException + * @throws IllegalAccessException */ public ApiVehiclesDetails(Collection vehicles, - String agencyId, Map uiTypesForVehicles) { + String agencyId, Map uiTypesForVehicles, boolean assigned) throws IllegalAccessException, InvocationTargetException { // Get Time object based on timezone for agency WebAgency webAgency = WebAgency.getCachedWebAgency(agencyId); Agency agency = webAgency.getAgency(); - Time timeForAgency = agency != null ? - agency.getTime() : new Time((String) null); - + Time timeForAgency = agency != null ? + agency.getTime() : new Time((String) null); + + responseTime = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()); + // Process each vehicle vehiclesData = new ArrayList(); for (IpcVehicle vehicle : vehicles) { // Determine UI type for vehicle UiMode uiType = uiTypesForVehicles.get(vehicle.getId()); - - vehiclesData.add(new ApiVehicleDetails(vehicle, timeForAgency, - uiType)); + if((assigned && vehicle.getTripId()!=null ) || !assigned) + vehiclesData.add(new ApiVehicleDetails(vehicle, timeForAgency, + uiType)); } } diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/IpcPredictionComparator.java b/transitclockApi/src/main/java/org/transitclock/api/data/IpcPredictionComparator.java new file mode 100644 index 000000000..aea3b91d6 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/IpcPredictionComparator.java @@ -0,0 +1,23 @@ +package org.transitclock.api.data; + +import java.util.Comparator; + +import org.transitclock.ipc.data.IpcPrediction; +/** + * A simple comparator for ordering IpcPrediction. + * In this way, the main class IcpPrediction is not modified. + * @author vperez + * + */ +public class IpcPredictionComparator implements Comparator { + + /** + * In this moment, only sequence is needed by GtfsRtTripFeed + */ + @Override + public int compare(IpcPrediction o1, IpcPrediction o2) { + // TODO Auto-generated method stub + return o1.getGtfsStopSeq()-o2.getGtfsStopSeq(); + } + +} diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/RouteStopNumberParameter.java b/transitclockApi/src/main/java/org/transitclock/api/data/RouteStopNumberParameter.java new file mode 100644 index 000000000..e19e9ebd0 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/RouteStopNumberParameter.java @@ -0,0 +1,65 @@ +package org.transitclock.api.data; + +/** + * Represents a fragment of a parameter for Stop level API + * in /command/stopLevel + */ +public class RouteStopNumberParameter { + private String stopId; + private String route; + private String numberOfServices = "3"; + private Integer isValid = null; + private String errorDescription = null; + + public String getStopId() { + return stopId; + } + + public void setStopId(String id) { + stopId = id; + } + + public String getRoute() { + return route; + } + public void setRoute(String r) { + route = r; + } + + public String getNumberOfServices() { + return numberOfServices; + } + + public int getNumberOfServicesAsInt() { + try { + return Integer.parseInt(numberOfServices); + } catch (NumberFormatException nfe) { + return 3; + } + } + public void setNumberOfServices(String number) { + numberOfServices = number; + } + + public Integer getValidCode() { + return isValid; + } + + public void setInvalid() { + isValid = 0; //FALSE + numberOfServices = null; + } + + public String getErrorDescription() { + return errorDescription; + } + + public void setErrorDescription(String msg) { + errorDescription = msg; + } + + public boolean isInvalid() { + return isValid != null && isValid == 0; + } + +} diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/SpeedFormat.java b/transitclockApi/src/main/java/org/transitclock/api/data/SpeedFormat.java new file mode 100644 index 000000000..8bdd6472e --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/SpeedFormat.java @@ -0,0 +1,7 @@ +package org.transitclock.api.data; + +public enum SpeedFormat { + MPH, + KM, + MS +} diff --git a/transitimeApi/src/main/java/org/transitime/api/data/package-info.java b/transitclockApi/src/main/java/org/transitclock/api/data/package-info.java similarity index 98% rename from transitimeApi/src/main/java/org/transitime/api/data/package-info.java rename to transitclockApi/src/main/java/org/transitclock/api/data/package-info.java index 75a516f68..bf275ed0b 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/package-info.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/package-info.java @@ -48,4 +48,4 @@ * @author SkiBu Smith * */ -package org.transitime.api.data; +package org.transitclock.api.data; diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/reporting/OnTimePerformanceOutput.java b/transitclockApi/src/main/java/org/transitclock/api/data/reporting/OnTimePerformanceOutput.java new file mode 100644 index 000000000..03a4b9753 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/reporting/OnTimePerformanceOutput.java @@ -0,0 +1,68 @@ +package org.transitclock.api.data.reporting; + +import org.transitclock.api.data.reporting.chartjs.pie.PieChart; +import org.transitclock.api.data.reporting.chartjs.pie.PieChartData; +import org.transitclock.api.data.reporting.chartjs.pie.PieChartDataset; +import org.transitclock.ipc.data.IpcArrivalDeparture; +import org.transitclock.ipc.data.IpcArrivalDepartureScheduleAdherence; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * Retrieves on-time performance data for reporting + * + * @author carabalb + * + */ +public class OnTimePerformanceOutput { + + private static final String EARLY = "Early"; + private static final String LATE = "Late"; + private static final String ON_TIME = "On-Time"; + + public static PieChart getOnTimePerformanceForRoutesPieChart(List arrivalDepartures, + int minEarlyMSec, int minLateMSec){ + + Map labelsAndCountsMap = getOTPLabelsAndCounts(arrivalDepartures, minEarlyMSec/1000, minLateMSec/1000); + + PieChartDataset dataset = new PieChartDataset(); + dataset.addData(labelsAndCountsMap.get(EARLY), labelsAndCountsMap.get(LATE), labelsAndCountsMap.get(ON_TIME)); + + PieChartData data = new PieChartData(); + data.addDataset(dataset); + data.addLabels(EARLY, LATE, ON_TIME); + + org.transitclock.api.data.reporting.chartjs.pie.PieChart chart = new org.transitclock.api.data.reporting.chartjs.pie.PieChart(); + chart.setData(data); + + return chart; + } + + private static Map getOTPLabelsAndCounts(List arrivalDepartures, + int minEarlySec, int minLateSec){ + + Map otpLabelsAndCountsMap = new LinkedHashMap<>(); + otpLabelsAndCountsMap.put(EARLY, 0); + otpLabelsAndCountsMap.put(LATE, 0); + otpLabelsAndCountsMap.put(ON_TIME, 0); + + int count; + + for(IpcArrivalDeparture ad : arrivalDepartures) { + if (ad.getScheduledAdherence().isEarlierThan(minEarlySec)) { + count = otpLabelsAndCountsMap.get(EARLY); + otpLabelsAndCountsMap.put(EARLY, count + 1); + } else if (ad.getScheduledAdherence().isLaterThan(minLateSec)) { + count = otpLabelsAndCountsMap.get(LATE); + otpLabelsAndCountsMap.put(LATE, count + 1); + } else { + count = otpLabelsAndCountsMap.get(ON_TIME); + otpLabelsAndCountsMap.put(ON_TIME, count + 1); + } + } + + return otpLabelsAndCountsMap; + } +} diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/reporting/RouteRunTimeOutput.java b/transitclockApi/src/main/java/org/transitclock/api/data/reporting/RouteRunTimeOutput.java new file mode 100644 index 000000000..8b789f68f --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/reporting/RouteRunTimeOutput.java @@ -0,0 +1,37 @@ +package org.transitclock.api.data.reporting; + +import org.apache.commons.lang3.text.WordUtils; +import org.transitclock.api.data.reporting.chartjs.custom.RouteRunTimeData; +import org.transitclock.api.data.reporting.chartjs.custom.RouteRunTimeMixedChart; +import org.transitclock.ipc.data.IpcRunTimeForRoute; + +import java.io.Serializable; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; + + +public class RouteRunTimeOutput implements Serializable { + + public static RouteRunTimeMixedChart getRunTimes(List runTimeForRoutes){ + RouteRunTimeData data = new RouteRunTimeData(); + for(IpcRunTimeForRoute runTimeForRoute : sortRunTimes(runTimeForRoutes)){ + data.getRoutesList().add(getFormattedRouteId(runTimeForRoute)); + data.getEarlyList().add(Long.valueOf(runTimeForRoute.getEarlyCount())); + data.getOnTimeList().add(Long.valueOf(runTimeForRoute.getOnTimeCount())); + data.getLateList().add(Long.valueOf(runTimeForRoute.getLateCount())); + } + return new RouteRunTimeMixedChart(data); + } + + private static String getFormattedRouteId(IpcRunTimeForRoute runTimeForRoute){ + return WordUtils.capitalizeFully(runTimeForRoute.getRouteShortName()); + } + + private static List sortRunTimes(List runTimeForRoutes){ + return runTimeForRoutes.stream() + .sorted(Comparator.comparing(IpcRunTimeForRoute::getRouteShortName)) + .collect(Collectors.toList()); + } + +} diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/reporting/StopPathRunTimeOutput.java b/transitclockApi/src/main/java/org/transitclock/api/data/reporting/StopPathRunTimeOutput.java new file mode 100644 index 000000000..46a12d413 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/reporting/StopPathRunTimeOutput.java @@ -0,0 +1,40 @@ +package org.transitclock.api.data.reporting; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.api.data.reporting.chartjs.custom.StopPathRunTimeData; +import org.transitclock.api.data.reporting.chartjs.custom.StopPathRunTimeMixedChart; +import org.transitclock.ipc.data.IpcRunTimeForStopPath; + +import java.io.Serializable; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; +import static org.transitclock.api.utils.NumberFormatter.*; + + +public class StopPathRunTimeOutput implements Serializable { + + public static StopPathRunTimeMixedChart getRunTimes(List runTimeForStopPaths){ + StopPathRunTimeData data = new StopPathRunTimeData(); + for(IpcRunTimeForStopPath runTimeForStopPath : sortRunTimes(runTimeForStopPaths)){ + data.getStopPathsList().add(getFormattedTripId(runTimeForStopPath)); + data.getFixedList().add(getValueAsLong(runTimeForStopPath.getFixed())); + data.getVariableList().add(getValueAsLong(runTimeForStopPath.getVariable())); + data.getDwellList().add(getValueAsLong(runTimeForStopPath.getDwell())); + data.getScheduledList().add(getValueAsLong(runTimeForStopPath.getScheduledCompletionTime())); + } + return new StopPathRunTimeMixedChart(data); + } + + private static String getFormattedTripId(IpcRunTimeForStopPath runTimeForStopPath){ + return runTimeForStopPath.getStopPathId(); + } + + private static List sortRunTimes(List runTimeForStopPaths){ + return runTimeForStopPaths.stream() + .sorted(Comparator.comparingInt(IpcRunTimeForStopPath::getStopPathIndex)) + .collect(Collectors.toList()); + } + +} diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/reporting/TripRunTimeOutput.java b/transitclockApi/src/main/java/org/transitclock/api/data/reporting/TripRunTimeOutput.java new file mode 100644 index 000000000..c12079d3d --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/reporting/TripRunTimeOutput.java @@ -0,0 +1,46 @@ +package org.transitclock.api.data.reporting; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.api.data.reporting.chartjs.custom.TripRunTimeData; +import org.transitclock.api.data.reporting.chartjs.custom.TripRunTimeMixedChart; +import org.transitclock.ipc.data.IpcRunTimeForTrip; +import org.transitclock.utils.Time; + +import javax.xml.bind.annotation.XmlRootElement; +import java.io.Serializable; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; +import static org.transitclock.api.utils.NumberFormatter.*; + +@XmlRootElement +public class TripRunTimeOutput implements Serializable { + + private static final Logger logger = LoggerFactory.getLogger(TripRunTimeOutput.class); + + public TripRunTimeOutput() {} + + public static TripRunTimeMixedChart getRunTimes(List runTimeForTrips){ + TripRunTimeData data = new TripRunTimeData(); + for(IpcRunTimeForTrip runTimeForTrip : sortRunTimes(runTimeForTrips)){ + data.getTripsList().add(getFormattedTripId(runTimeForTrip)); + data.getFixedList().add(getValueAsLong(runTimeForTrip.getFixed())); + data.getVariableList().add(getValueAsLong(runTimeForTrip.getVariable())); + data.getDwellList().add(getValueAsLong(runTimeForTrip.getDwell())); + data.getScheduledList().add(getValueAsLong(runTimeForTrip.getScheduledTripCompletionTime())); + data.getNextTripStartList().add(getValueAsLong(runTimeForTrip.getNextScheduledTripStartTime())); + } + return new TripRunTimeMixedChart(data); + } + + private static String getFormattedTripId(IpcRunTimeForTrip runTimeForTrip){ + return Time.timeOfDayShortStr(runTimeForTrip.getScheduledTripStartTime()) + " - " + runTimeForTrip.getTripId(); + } + + private static List sortRunTimes(List runTimeForTrips){ + return runTimeForTrips.stream() + .sorted(Comparator.comparingInt(IpcRunTimeForTrip::getScheduledTripStartTime)) + .collect(Collectors.toList()); + } +} diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/reporting/chartjs/ChartType.java b/transitclockApi/src/main/java/org/transitclock/api/data/reporting/chartjs/ChartType.java new file mode 100644 index 000000000..7040fbda0 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/reporting/chartjs/ChartType.java @@ -0,0 +1,5 @@ +package org.transitclock.api.data.reporting.chartjs; + +public enum ChartType { + PIE, BAR, LINE, RADAR, POLAR, BUBBLE, SCATTER, AREA, MIXED; +} diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/reporting/chartjs/custom/RouteRunTimeData.java b/transitclockApi/src/main/java/org/transitclock/api/data/reporting/chartjs/custom/RouteRunTimeData.java new file mode 100644 index 000000000..fd2538292 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/reporting/chartjs/custom/RouteRunTimeData.java @@ -0,0 +1,35 @@ +package org.transitclock.api.data.reporting.chartjs.custom; + +import javax.xml.bind.annotation.XmlElement; +import java.util.ArrayList; +import java.util.List; + +public class RouteRunTimeData { + @XmlElement(name = "routes") + private List routesList = new ArrayList<>(); + + @XmlElement(name = "early") + private List earlyList = new ArrayList<>(); + + @XmlElement(name = "onTime") + private List onTimeList = new ArrayList<>(); + + @XmlElement(name = "late") + private List lateList = new ArrayList<>(); + + public List getRoutesList() { + return routesList; + } + + public List getEarlyList() { + return earlyList; + } + + public List getOnTimeList() { + return onTimeList; + } + + public List getLateList() { + return lateList; + } +} diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/reporting/chartjs/custom/RouteRunTimeMixedChart.java b/transitclockApi/src/main/java/org/transitclock/api/data/reporting/chartjs/custom/RouteRunTimeMixedChart.java new file mode 100644 index 000000000..03a6f8227 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/reporting/chartjs/custom/RouteRunTimeMixedChart.java @@ -0,0 +1,22 @@ +package org.transitclock.api.data.reporting.chartjs.custom; + +import javax.xml.bind.annotation.XmlElement; + +public class RouteRunTimeMixedChart { + @XmlElement + private RouteRunTimeData data; + + public RouteRunTimeMixedChart() {} + + public RouteRunTimeMixedChart(RouteRunTimeData data) { + this.data = data; + } + + public RouteRunTimeData getData() { + return data; + } + + public void setData(RouteRunTimeData data) { + this.data = data; + } +} diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/reporting/chartjs/custom/StopPathRunTimeData.java b/transitclockApi/src/main/java/org/transitclock/api/data/reporting/chartjs/custom/StopPathRunTimeData.java new file mode 100644 index 000000000..41f0b9229 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/reporting/chartjs/custom/StopPathRunTimeData.java @@ -0,0 +1,43 @@ +package org.transitclock.api.data.reporting.chartjs.custom; + +import javax.xml.bind.annotation.XmlElement; +import java.util.ArrayList; +import java.util.List; + +public class StopPathRunTimeData { + @XmlElement(name = "stopPaths") + private List stopPathsList = new ArrayList<>(); + + @XmlElement(name = "fixed") + private List fixedList = new ArrayList<>(); + + @XmlElement(name = "variable") + private List variableList = new ArrayList<>(); + + @XmlElement(name = "dwell") + private List dwellList = new ArrayList<>(); + + @XmlElement(name = "scheduled") + private List scheduledList = new ArrayList<>(); + + public List getStopPathsList() { + return stopPathsList; + } + + public List getFixedList() { + return fixedList; + } + + public List getVariableList() { + return variableList; + } + + public List getDwellList() { + return dwellList; + } + + public List getScheduledList() { + return scheduledList; + } + +} diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/reporting/chartjs/custom/StopPathRunTimeMixedChart.java b/transitclockApi/src/main/java/org/transitclock/api/data/reporting/chartjs/custom/StopPathRunTimeMixedChart.java new file mode 100644 index 000000000..2fcbf5358 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/reporting/chartjs/custom/StopPathRunTimeMixedChart.java @@ -0,0 +1,22 @@ +package org.transitclock.api.data.reporting.chartjs.custom; + +import javax.xml.bind.annotation.XmlElement; + +public class StopPathRunTimeMixedChart { + @XmlElement + private StopPathRunTimeData data; + + public StopPathRunTimeMixedChart() {} + + public StopPathRunTimeMixedChart(StopPathRunTimeData data) { + this.data = data; + } + + public StopPathRunTimeData getData() { + return data; + } + + public void setData(StopPathRunTimeData data) { + this.data = data; + } +} diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/reporting/chartjs/custom/TripRunTimeData.java b/transitclockApi/src/main/java/org/transitclock/api/data/reporting/chartjs/custom/TripRunTimeData.java new file mode 100644 index 000000000..f26ed145a --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/reporting/chartjs/custom/TripRunTimeData.java @@ -0,0 +1,50 @@ +package org.transitclock.api.data.reporting.chartjs.custom; + +import javax.xml.bind.annotation.XmlElement; +import java.util.ArrayList; +import java.util.List; + +public class TripRunTimeData { + @XmlElement(name = "trips") + private List tripsList = new ArrayList<>(); + + @XmlElement(name = "fixed") + private List fixedList = new ArrayList<>(); + + @XmlElement(name = "variable") + private List variableList = new ArrayList<>(); + + @XmlElement(name = "dwell") + private List dwellList = new ArrayList<>(); + + @XmlElement(name = "scheduled") + private List scheduledList = new ArrayList<>(); + + @XmlElement(name = "nextTripStart") + private List nextTripStartList = new ArrayList<>(); + + public List getTripsList() { + return tripsList; + } + + public List getFixedList() { + return fixedList; + } + + public List getVariableList() { + return variableList; + } + + public List getDwellList() { + return dwellList; + } + + public List getScheduledList() { + return scheduledList; + } + + public List getNextTripStartList() { + return nextTripStartList; + } + +} diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/reporting/chartjs/custom/TripRunTimeMixedChart.java b/transitclockApi/src/main/java/org/transitclock/api/data/reporting/chartjs/custom/TripRunTimeMixedChart.java new file mode 100644 index 000000000..b233595ab --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/reporting/chartjs/custom/TripRunTimeMixedChart.java @@ -0,0 +1,32 @@ +package org.transitclock.api.data.reporting.chartjs.custom; + +import javax.xml.bind.annotation.XmlElement; + +/** + * Class to help serialize PieChart data into the correct format for Chart.js + * Conforms to data documentation specified for Chart.js 2.x + * + * TODO: Should either be its own module or should use other external dependency + * + * @author carabalb + * + */ +public class TripRunTimeMixedChart { + + @XmlElement + private TripRunTimeData data; + + public TripRunTimeMixedChart() {} + + public TripRunTimeMixedChart(TripRunTimeData data) { + this.data = data; + } + + public TripRunTimeData getData() { + return data; + } + + public void setData(TripRunTimeData data) { + this.data = data; + } +} diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/reporting/chartjs/pie/PieChart.java b/transitclockApi/src/main/java/org/transitclock/api/data/reporting/chartjs/pie/PieChart.java new file mode 100644 index 000000000..82d0d78b9 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/reporting/chartjs/pie/PieChart.java @@ -0,0 +1,28 @@ +package org.transitclock.api.data.reporting.chartjs.pie; + +import javax.xml.bind.annotation.XmlElement; + +/** + * Class to help serialize PieChart data into the correct format for Chart.js + * Conforms to data documentation specified for Chart.js 2.x + * + * TODO: Should either be its own module or should use other external dependency + * + * @author carabalb + * + */ +public class PieChart { + + public PieChart() {} + + @XmlElement + private PieChartData data; + + public PieChartData getData() { + return data; + } + + public void setData(PieChartData data) { + this.data = data; + } +} diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/reporting/chartjs/pie/PieChartData.java b/transitclockApi/src/main/java/org/transitclock/api/data/reporting/chartjs/pie/PieChartData.java new file mode 100644 index 000000000..9e280af22 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/reporting/chartjs/pie/PieChartData.java @@ -0,0 +1,55 @@ +package org.transitclock.api.data.reporting.chartjs.pie; + +import javax.xml.bind.annotation.XmlElement; +import java.util.ArrayList; +import java.util.List; + +/** + * Class to help serialize PieChart data into the correct format for Chart.js + * Conforms to data documentation specified for Chart.js 2.x + * + * TODO: Should either be its own module or should use other external dependency + * + * @author carabalb + * + */ +public class PieChartData { + + public PieChartData() {} + + @XmlElement(name = "datasets") + private List dataset = new ArrayList<>(); + + @XmlElement(name = "labels") + private List labels = new ArrayList<>(); + + public List getDataset() { + return dataset; + } + + public void setDataset(PieChartDataset ... datasets) { + this.dataset.clear(); + addDataset(datasets); + } + + public void addDataset(PieChartDataset ... datasets){ + for(PieChartDataset dataset : datasets){ + this.dataset.add(dataset); + } + } + + public List getLabels() { + return labels; + } + + public void setLabels(String ... labels) { + this.labels.clear(); + addLabels(labels); + } + + public void addLabels(String ... labels){ + for(String label : labels){ + this.labels.add(label); + } + } +} diff --git a/transitclockApi/src/main/java/org/transitclock/api/data/reporting/chartjs/pie/PieChartDataset.java b/transitclockApi/src/main/java/org/transitclock/api/data/reporting/chartjs/pie/PieChartDataset.java new file mode 100644 index 000000000..bfae2128a --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/data/reporting/chartjs/pie/PieChartDataset.java @@ -0,0 +1,59 @@ +package org.transitclock.api.data.reporting.chartjs.pie; +import javax.xml.bind.annotation.XmlElement; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +/** + * Class to help serialize PieChart data into the correct format for Chart.js + * Conforms to data documentation specified for Chart.js 2.x + * + * TODO: Should either be its own module or should use other external dependency + * + * @author carabalb + * + */ +public class PieChartDataset { + + @XmlElement(name = "data") + private List data = new ArrayList<>(); + + public PieChartDataset(){} + + public List getData() { + return data; + } + + public void setData(Integer ... integers) { + this.data.clear(); + addData(integers); + } + + public void addData(Integer ... integers){ + for(Integer integer : integers){ + this.data.add(new BigDecimal(integer)); + } + } + + public void setData(BigDecimal ... bigDecimals) { + this.data.clear(); + addData(bigDecimals); + } + + public void addData(BigDecimal ... bigDecimals){ + for(BigDecimal bigDecimal : bigDecimals){ + this.data.add(bigDecimal); + } + } + + public void setData(Long ... longs) { + this.data.clear(); + addData(longs); + } + + public void addData(Long ... longs){ + for(Long l : longs){ + new BigDecimal(l); + } + } +} diff --git a/transitimeApi/src/main/java/org/transitime/api/data/siri/SiriFramedVehicleJourneyRef.java b/transitclockApi/src/main/java/org/transitclock/api/data/siri/SiriFramedVehicleJourneyRef.java similarity index 95% rename from transitimeApi/src/main/java/org/transitime/api/data/siri/SiriFramedVehicleJourneyRef.java rename to transitclockApi/src/main/java/org/transitclock/api/data/siri/SiriFramedVehicleJourneyRef.java index e48bc43db..dff85a820 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/siri/SiriFramedVehicleJourneyRef.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/siri/SiriFramedVehicleJourneyRef.java @@ -15,14 +15,14 @@ * Transitime.org . If not, see . */ -package org.transitime.api.data.siri; +package org.transitclock.api.data.siri; import java.text.DateFormat; import java.util.Date; import javax.xml.bind.annotation.XmlElement; -import org.transitime.ipc.data.IpcVehicle; +import org.transitclock.ipc.data.IpcVehicle; /** * Describes the trip diff --git a/transitimeApi/src/main/java/org/transitime/api/data/siri/SiriLocation.java b/transitclockApi/src/main/java/org/transitclock/api/data/siri/SiriLocation.java similarity index 94% rename from transitimeApi/src/main/java/org/transitime/api/data/siri/SiriLocation.java rename to transitclockApi/src/main/java/org/transitclock/api/data/siri/SiriLocation.java index 25925ea4b..557e9f989 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/siri/SiriLocation.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/siri/SiriLocation.java @@ -15,11 +15,11 @@ * Transitime.org . If not, see . */ -package org.transitime.api.data.siri; +package org.transitclock.api.data.siri; import javax.xml.bind.annotation.XmlElement; -import org.transitime.utils.Geo; +import org.transitclock.utils.Geo; /** * Location object for SIRI diff --git a/transitimeApi/src/main/java/org/transitime/api/data/siri/SiriMonitoredCall.java b/transitclockApi/src/main/java/org/transitclock/api/data/siri/SiriMonitoredCall.java similarity index 96% rename from transitimeApi/src/main/java/org/transitime/api/data/siri/SiriMonitoredCall.java rename to transitclockApi/src/main/java/org/transitclock/api/data/siri/SiriMonitoredCall.java index bc720a3ab..0cada0df1 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/siri/SiriMonitoredCall.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/siri/SiriMonitoredCall.java @@ -15,16 +15,16 @@ * Transitime.org . If not, see . */ -package org.transitime.api.data.siri; +package org.transitclock.api.data.siri; import java.text.DateFormat; import java.util.Date; import javax.xml.bind.annotation.XmlElement; -import org.transitime.ipc.data.IpcVehicleComplete; -import org.transitime.ipc.data.IpcPrediction; -import org.transitime.utils.StringUtils; +import org.transitclock.ipc.data.IpcPrediction; +import org.transitclock.ipc.data.IpcVehicleComplete; +import org.transitclock.utils.StringUtils; /** * For SIRI MonitorCall element. diff --git a/transitimeApi/src/main/java/org/transitime/api/data/siri/SiriMonitoredVehicleJourney.java b/transitclockApi/src/main/java/org/transitclock/api/data/siri/SiriMonitoredVehicleJourney.java similarity index 97% rename from transitimeApi/src/main/java/org/transitime/api/data/siri/SiriMonitoredVehicleJourney.java rename to transitclockApi/src/main/java/org/transitclock/api/data/siri/SiriMonitoredVehicleJourney.java index ccf1ccd1d..3d435290a 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/siri/SiriMonitoredVehicleJourney.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/siri/SiriMonitoredVehicleJourney.java @@ -15,15 +15,15 @@ * Transitime.org . If not, see . */ -package org.transitime.api.data.siri; +package org.transitclock.api.data.siri; import java.text.DateFormat; import java.util.Date; import javax.xml.bind.annotation.XmlElement; -import org.transitime.ipc.data.IpcVehicleComplete; -import org.transitime.ipc.data.IpcPrediction; +import org.transitclock.ipc.data.IpcPrediction; +import org.transitclock.ipc.data.IpcVehicleComplete; /** * For SIRI MonitoredVehicleJourney element diff --git a/transitimeApi/src/main/java/org/transitime/api/data/siri/SiriStopMonitoring.java b/transitclockApi/src/main/java/org/transitclock/api/data/siri/SiriStopMonitoring.java similarity index 95% rename from transitimeApi/src/main/java/org/transitime/api/data/siri/SiriStopMonitoring.java rename to transitclockApi/src/main/java/org/transitclock/api/data/siri/SiriStopMonitoring.java index 09e5f68b7..a4b527b43 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/siri/SiriStopMonitoring.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/siri/SiriStopMonitoring.java @@ -15,7 +15,7 @@ * Transitime.org . If not, see . */ -package org.transitime.api.data.siri; +package org.transitclock.api.data.siri; import java.text.DateFormat; import java.text.SimpleDateFormat; @@ -30,11 +30,11 @@ import javax.xml.bind.annotation.XmlTransient; import javax.xml.bind.annotation.XmlType; -import org.transitime.api.utils.AgencyTimezoneCache; -import org.transitime.ipc.data.IpcVehicleComplete; -import org.transitime.ipc.data.IpcPrediction; -import org.transitime.ipc.data.IpcPredictionsForRouteStopDest; -import org.transitime.utils.Time; +import org.transitclock.api.utils.AgencyTimezoneCache; +import org.transitclock.ipc.data.IpcPrediction; +import org.transitclock.ipc.data.IpcPredictionsForRouteStopDest; +import org.transitclock.ipc.data.IpcVehicleComplete; +import org.transitclock.utils.Time; /** * Top level XML element for SIRI StopMonitoring command. diff --git a/transitimeApi/src/main/java/org/transitime/api/data/siri/SiriVehiclesMonitoring.java b/transitclockApi/src/main/java/org/transitclock/api/data/siri/SiriVehiclesMonitoring.java similarity index 96% rename from transitimeApi/src/main/java/org/transitime/api/data/siri/SiriVehiclesMonitoring.java rename to transitclockApi/src/main/java/org/transitclock/api/data/siri/SiriVehiclesMonitoring.java index 23107b244..b8a7673b2 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/siri/SiriVehiclesMonitoring.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/siri/SiriVehiclesMonitoring.java @@ -15,7 +15,7 @@ * Transitime.org . If not, see . */ -package org.transitime.api.data.siri; +package org.transitclock.api.data.siri; import java.text.DateFormat; import java.text.SimpleDateFormat; @@ -30,9 +30,9 @@ import javax.xml.bind.annotation.XmlTransient; import javax.xml.bind.annotation.XmlType; -import org.transitime.api.utils.AgencyTimezoneCache; -import org.transitime.ipc.data.IpcVehicleComplete; -import org.transitime.utils.Time; +import org.transitclock.api.utils.AgencyTimezoneCache; +import org.transitclock.ipc.data.IpcVehicleComplete; +import org.transitclock.utils.Time; /** * Top level XML element for SIRI VehicleMonitoring command. diff --git a/transitimeApi/src/main/java/org/transitime/api/data/siri/package-info.java b/transitclockApi/src/main/java/org/transitclock/api/data/siri/package-info.java similarity index 97% rename from transitimeApi/src/main/java/org/transitime/api/data/siri/package-info.java rename to transitclockApi/src/main/java/org/transitclock/api/data/siri/package-info.java index 1d9cb53a6..ec7f5f03a 100644 --- a/transitimeApi/src/main/java/org/transitime/api/data/siri/package-info.java +++ b/transitclockApi/src/main/java/org/transitclock/api/data/siri/package-info.java @@ -35,4 +35,4 @@ * @author SkiBu Smith * */ -package org.transitime.api.data.siri; \ No newline at end of file +package org.transitclock.api.data.siri; \ No newline at end of file diff --git a/transitimeApi/src/main/java/org/transitime/api/gtfsRealtime/DataCache.java b/transitclockApi/src/main/java/org/transitclock/api/gtfsRealtime/DataCache.java similarity index 96% rename from transitimeApi/src/main/java/org/transitime/api/gtfsRealtime/DataCache.java rename to transitclockApi/src/main/java/org/transitclock/api/gtfsRealtime/DataCache.java index 4073bd1b9..efdb9c75b 100644 --- a/transitimeApi/src/main/java/org/transitime/api/gtfsRealtime/DataCache.java +++ b/transitclockApi/src/main/java/org/transitclock/api/gtfsRealtime/DataCache.java @@ -15,12 +15,12 @@ * along with Transitime.org . If not, see . */ -package org.transitime.api.gtfsRealtime; +package org.transitclock.api.gtfsRealtime; import java.util.HashMap; import java.util.Map; -import org.transitime.utils.Time; +import org.transitclock.utils.Time; import com.google.transit.realtime.GtfsRealtime.FeedMessage; diff --git a/transitclockApi/src/main/java/org/transitclock/api/gtfsRealtime/GtfsRtTripFeed.java b/transitclockApi/src/main/java/org/transitclock/api/gtfsRealtime/GtfsRtTripFeed.java new file mode 100644 index 000000000..36b6bdea4 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/gtfsRealtime/GtfsRtTripFeed.java @@ -0,0 +1,599 @@ +/* + * 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.api.gtfsRealtime; + +import com.google.transit.realtime.GtfsRealtime.*; +import com.google.transit.realtime.GtfsRealtime.FeedHeader.Incrementality; +import com.google.transit.realtime.GtfsRealtime.TripUpdate.StopTimeEvent; +import com.google.transit.realtime.GtfsRealtime.TripUpdate.StopTimeUpdate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.api.data.IpcPredictionComparator; +import org.transitclock.api.utils.AgencyTimezoneCache; +import org.transitclock.config.BooleanConfigValue; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.core.dataCache.CanceledTripKey; +import org.transitclock.core.holdingmethod.PredictionTimeComparator; +import org.transitclock.ipc.clients.ConfigInterfaceFactory; +import org.transitclock.ipc.clients.PredictionsInterfaceFactory; +import org.transitclock.ipc.data.IpcCanceledTrip; +import org.transitclock.ipc.data.IpcPrediction; +import org.transitclock.ipc.data.IpcPredictionsForRouteStopDest; +import org.transitclock.ipc.data.IpcSkippedStop; +import org.transitclock.ipc.interfaces.ConfigInterface; +import org.transitclock.utils.IntervalTimer; +import org.transitclock.utils.Time; + +import java.rmi.RemoteException; +import java.text.SimpleDateFormat; +import java.util.*; + +/** + * For creating GTFS-realtime trip feed. The data is obtained from the server + * via RMI. + *

    + * Note: for the trip feed predictions that are schedule based instead of GPS + * based the StopTimeEvent uncertainty is set to + * SCHED_BASED_PRED_UNCERTAINTY_VALUE so that the client can treat the + * prediction differently. If a vehicle is delayed and not moving then + * uncertainty is set to DELAYED_UNCERTAINTY_VALUE. And if a vehicle is late and + * the prediction is for a subsequent trip then uncertainty is set to + * LATE_AND_SUBSEQUENT_TRIP_UNCERTAINTY_VALUE. + * + * @author SkiBu Smith + * + */ +public class GtfsRtTripFeed { + + private final String agencyId; + + // For outputting date in GTFS-realtime format + private SimpleDateFormat gtfsRealtimeDateFormatter = + new SimpleDateFormat("yyyyMMdd"); + + private SimpleDateFormat gtfsRealtimeTimeFormatter = + new SimpleDateFormat("HH:mm:ss"); + + + private static IntegerConfigValue predictionMaxFutureSecs = new IntegerConfigValue( + "transitclock.api.predictionMaxFutureSecs", 60 * 60, + "Number of seconds in the future to accept predictions before"); + private static final int PREDICTION_MAX_FUTURE_SECS = predictionMaxFutureSecs.getValue(); + + + private static BooleanConfigValue includeTripUpdateDelay = new BooleanConfigValue( + "transitclock.api.includeTripUpdateDelay", false, + "Whether or not to include delay in the TripUpdate message"); + private static final boolean INCLUDE_TRIP_UPDATE_DELAY = includeTripUpdateDelay.getValue(); + + private static BooleanConfigValue includeSkippedStops = new BooleanConfigValue( + "transitclock.api.includeSkippedStops", false, + "Whether or not to include delay in the TripUpdate message"); + private static final boolean INCLUDE_SKIPPED_STOPS = includeSkippedStops.getValue(); + + // For when creating StopTimeEvent for schedule based prediction + // 5 minutes (300 seconds) + private static final int SCHED_BASED_PRED_UNCERTAINTY_VALUE = 5 * 60; + + // For when creating StopTimeEvent and the vehicle is delayed + private static final int DELAYED_UNCERTAINTY_VALUE = + SCHED_BASED_PRED_UNCERTAINTY_VALUE + 1; + + // If vehicle is late and prediction is for a subsequent trip then + // the predictions are not as certain because it is reasonably likely + // that another vehicle will take over the subsequent trip. Takes + // precedence over SCHED_BASED_PRED_UNCERTAINTY_VALUE. + private static final int LATE_AND_SUBSEQUENT_TRIP_UNCERTAINTY_VALUE = + DELAYED_UNCERTAINTY_VALUE + 1; + + private static final Logger logger = + LoggerFactory.getLogger(GtfsRtTripFeed.class); + + /********************** Member Functions **************************/ + + public GtfsRtTripFeed(String agencyId) { + this.agencyId = agencyId; + + this.gtfsRealtimeDateFormatter.setTimeZone(AgencyTimezoneCache + .get(agencyId)); + } + + /** + * Create TripUpdate for the trip. + * + * @param predsForTrip + * @return + */ + private TripUpdate createTripUpdate(List predsForTrip, + HashMap> allSkippedStops, + boolean serviceSuffixId) { + // Create the parent TripUpdate object that is returned. + TripUpdate.Builder tripUpdate = TripUpdate.newBuilder(); + + // Add the trip descriptor information + IpcPrediction firstPred = predsForTrip.get(0); + TripDescriptor.Builder tripDescriptor = TripDescriptor.newBuilder(); + + String routeId = firstPred.getRouteId(); + String tripId = firstPred.getTripId(); + String vehicleId = firstPred.getVehicleId(); + + if (routeId != null) + tripDescriptor.setRouteId(routeId); + + if (tripId != null) { + tripDescriptor.setTripId(tripId); + + try { + if(firstPred.getFreqStartTime()>0) + { + String tripStartTimeStr=gtfsRealtimeTimeFormatter.format(new Date(firstPred.getFreqStartTime())); + tripDescriptor.setStartTime(tripStartTimeStr); + } + } catch (Exception e) { + + } + + long tripStartEpochTime = firstPred.getTripStartEpochTime(); + String tripStartDateStr = + gtfsRealtimeDateFormatter.format(new Date( + tripStartEpochTime)); + tripDescriptor.setStartDate(tripStartDateStr); + + // Set the relation between this trip and the static schedule. ADDED and CANCELED not supported. + if (firstPred.isTripUnscheduled()) { + // A trip that is running with no schedule associated to it - + // this value is used to identify trips defined in GTFS frequencies.txt with exact_times = 0 + tripDescriptor.setScheduleRelationship(TripDescriptor.ScheduleRelationship.UNSCHEDULED); + } else { + // Trip that is running in accordance with its GTFS schedule, + // or is close enough to the scheduled trip to be associated with it. + tripDescriptor.setScheduleRelationship(TripDescriptor.ScheduleRelationship.SCHEDULED); + } + } + + //Set trip as canceled if it is mark as that from schedBasePreds + if(firstPred.isCanceled()) + tripDescriptor.setScheduleRelationship(TripDescriptor.ScheduleRelationship.CANCELED); + + tripUpdate.setTrip(tripDescriptor); + if (firstPred.getDelay() != null && INCLUDE_TRIP_UPDATE_DELAY) + tripUpdate.setDelay(firstPred.getDelay()); // set schedule deviation + + // Add the VehicleDescriptor information + VehicleDescriptor.Builder vehicleDescriptor = + VehicleDescriptor.newBuilder().setId(vehicleId); + tripUpdate.setVehicle(vehicleDescriptor); + + // according to the GTFS-RT spec, predictions need to be sorted by gtfs stop seq + Collections.sort(predsForTrip, new GtfsStopSequenceComparator()); + + Set skippedStopsForTrip = null; + if(allSkippedStops != null && !allSkippedStops.isEmpty()) { + skippedStopsForTrip = allSkippedStops.get(tripId); + logger.info("Checking skipped stops for Trip {}", tripId); + if(skippedStopsForTrip != null) { + logger.info("Skipped Stop Entries {} for Trip {}", skippedStopsForTrip.size(), tripId); + logger.info("Skipped Stops For Trip {} {}", tripId, skippedStopsForTrip); + } + } else { + logger.warn("All Skipped Stops is empty"); + } + + // Add the StopTimeUpdate information for each prediction + if(!firstPred.isCanceled()) + { + for (IpcPrediction pred : predsForTrip ) { + StopTimeUpdate.Builder stopTimeUpdate = StopTimeUpdate.newBuilder() + .setStopSequence(pred.getGtfsStopSeq()) + .setStopId(pred.getStopId()); + + StopTimeEvent.Builder stopTimeEvent = StopTimeEvent.newBuilder(); + stopTimeEvent.setTime(pred.getPredictionTime() / Time.MS_PER_SEC); + + // If schedule based prediction then set the uncertainty to special + // value so that client can tell + if (pred.isSchedBasedPred()) + stopTimeEvent.setUncertainty(SCHED_BASED_PRED_UNCERTAINTY_VALUE); + + // If vehicle is late and prediction is for a subsequent trip then + // the predictions are not as certain because it is reasonably likely + // that another vehicle will take over the subsequent trip. Takes + // precedence over SCHED_BASED_PRED_UNCERTAINTY_VALUE. + if (pred.isLateAndSubsequentTripSoMarkAsUncertain()) + stopTimeEvent.setUncertainty(LATE_AND_SUBSEQUENT_TRIP_UNCERTAINTY_VALUE); + + // If vehicle not making forward progress then set uncertainty to + // special value so that client can tell. Takes precedence over + // LATE_AND_SUBSEQUENT_TRIP_UNCERTAINTY_VALUE. + if (pred.isDelayed()) + stopTimeEvent.setUncertainty(DELAYED_UNCERTAINTY_VALUE); + + if (pred.isArrival()) + stopTimeUpdate.setArrival(stopTimeEvent); + else + stopTimeUpdate.setDeparture(stopTimeEvent); + + //The relationship should always be SCHEDULED if departure or arrival time is given. + stopTimeUpdate.setScheduleRelationship(getStopScheduleRelationship(vehicleId, stopTimeUpdate, skippedStopsForTrip)); + + tripUpdate.addStopTimeUpdate(stopTimeUpdate); + } + } + // Add timestamp + tripUpdate.setTimestamp(firstPred.getAvlTime() / Time.MS_PER_SEC); + + // Return the results + return tripUpdate.build(); + } + + private StopTimeUpdate.ScheduleRelationship getStopScheduleRelationship(String vehicleId, StopTimeUpdate.Builder stopTimeUpdate, + Set skippedStopsForTrip){ + if(skippedStopsForTrip != null && !skippedStopsForTrip.isEmpty()) { + IpcSkippedStop stop = new IpcSkippedStop(vehicleId, stopTimeUpdate.getStopId(), stopTimeUpdate.getStopSequence()); + if (skippedStopsForTrip.contains(stop)) { + return StopTimeUpdate.ScheduleRelationship.SKIPPED; + } + } + return StopTimeUpdate.ScheduleRelationship.SCHEDULED; + } + + private TripUpdate createCanceledTripUpdate(String vehicleId, IpcCanceledTrip canceledTrip){ + // Create the parent TripUpdate object that is returned. + TripUpdate.Builder tripUpdate = TripUpdate.newBuilder(); + + TripDescriptor.Builder tripDescriptor = TripDescriptor.newBuilder(); + + if (canceledTrip.getRouteId() != null) + tripDescriptor.setRouteId(canceledTrip.getRouteId()); + + if (canceledTrip.getTripId() != null) + tripDescriptor.setTripId(canceledTrip.getTripId()); + + if (canceledTrip.getTripStartDate() != null) + tripDescriptor.setStartDate(canceledTrip.getTripStartDate()); + + tripDescriptor.setScheduleRelationship(TripDescriptor.ScheduleRelationship.CANCELED); + + // Add timestamp + tripUpdate.setTimestamp(canceledTrip.getTimeStamp() / Time.MS_PER_SEC); + + tripUpdate.setTrip(tripDescriptor); + + // Add the VehicleDescriptor information + if(vehicleId != null) { + VehicleDescriptor.Builder vehicleDescriptor = + VehicleDescriptor.newBuilder().setId(vehicleId); + tripUpdate.setVehicle(vehicleDescriptor); + } + + // Return the results + return tripUpdate.build(); + + } + + private boolean isTripCanceledAndCached(List predictions, + Map canceledTrips, + boolean serviceSuffixId) { + IpcPrediction firstPred = predictions.get(0); + String tripId = firstPred.getTripId(); + String vehicleId = firstPred.getVehicleId(); + + IpcCanceledTrip canceledTrip = canceledTrips.get(new CanceledTripKey(vehicleId, tripId)); + if(canceledTrip != null) { + String canceledTripTripId = canceledTrip.getTripId(); + logger.info("Found canceled tripId {} found for firstPred tripId {} and vehicleId {}",canceledTripTripId, tripId, vehicleId); + } else { + logger.info("No canceled trip found for firstPred tripId {} and vehicleId {}", tripId, vehicleId); + } + + if(canceledTrip != null && canceledTrip.getTripId() != null && canceledTrip.getTripId().equalsIgnoreCase(tripId)){ + return true; + } + + return false; + } + + + /** + * Creates a GTFS-realtime message for the predictions by trip passed in. + * + * @param predsByTripMap + * the data to be put into the GTFS-realtime message + * @return the GTFS-realtime FeedMessage + */ + private FeedMessage createMessage(Map> predsByTripMap) { + FeedMessage.Builder message = FeedMessage.newBuilder(); + + FeedHeader.Builder feedheader = FeedHeader.newBuilder() + .setGtfsRealtimeVersion("1.0") + .setIncrementality(Incrementality.FULL_DATASET) + .setTimestamp(System.currentTimeMillis() / Time.MS_PER_SEC); + message.setHeader(feedheader); + //Create a comparator to sort each trip data + Comparator comparator=new IpcPredictionComparator(); + + HashMap allCanceledTrips = getAllCanceledTrips(); + HashMap> allSkippedStops = getSkippedStops(); + boolean serviceIdSuffix = getServiceIdSuffix(); + + // For each trip... + for (List predsForTrip : predsByTripMap.values()) { + + // trip is already in cancelled list, skip for now + if(isTripCanceledAndCached(predsForTrip, allCanceledTrips, serviceIdSuffix)){ + continue; + } + + //Sort trip data according to sequence + Collections.sort(predsForTrip, comparator); + + // Need to check if predictions for frequency based trip and group by start time if they are. + if(isFrequencyBasedTrip(predsForTrip)) + { + createTripUpdateForFrequencyTrips(message, predsForTrip, allSkippedStops, serviceIdSuffix); + }else + { + createTripUpdateForTrips(message, predsForTrip, allSkippedStops, serviceIdSuffix); + } + } + + for(Map.Entry entry : allCanceledTrips.entrySet()){ + CanceledTripKey key = entry.getKey(); + IpcCanceledTrip canceledTrip = entry.getValue(); + + if(canceledTrip != null){ + FeedEntity.Builder feedEntity = FeedEntity.newBuilder() + .setId(canceledTrip.getTripId()); + try { + TripUpdate tripUpdate = createCanceledTripUpdate(key.getVehicleId(), canceledTrip); + if(tripUpdate == null) continue; + feedEntity.setTripUpdate(tripUpdate); + message.addEntity(feedEntity); + } catch (Exception e) { + logger.error("Error parsing canceled trip update data {} for vehicle id {}.", + canceledTrip, key.getVehicleId(), e); + } + } + } + + return message.build(); + } + + private HashMap getAllCanceledTrips(){ + HashMap allCanceledTrips = new HashMap<>(); + try { + allCanceledTrips = PredictionsInterfaceFactory.get(agencyId).getAllCanceledTrips(); + if(allCanceledTrips != null && !allCanceledTrips.isEmpty()) { + for(Map.Entry entry : allCanceledTrips.entrySet()){ + logger.info("Canceled Trip vehicle id and trip id is {}", entry.getKey()); + logger.info(entry.getValue().toString()); + } + } + } catch (RemoteException e) { + logger.error("Exception when getting all canceled trips from RMI", e); + } + return allCanceledTrips; + } + + private HashMap> getSkippedStops(){ + HashMap> allSkippedStops = new HashMap<>(); + if(INCLUDE_SKIPPED_STOPS) { + try { + allSkippedStops = PredictionsInterfaceFactory.get(agencyId).getAllSkippedStops(); + if(allSkippedStops != null && !allSkippedStops.isEmpty()) { + for(Map.Entry> entry : allSkippedStops.entrySet()){ + logger.info("Trip is {}", entry.getKey()); + for(IpcSkippedStop stop: entry.getValue()){ + logger.info(stop.toString()); + } + } + } + } catch (RemoteException e) { + logger.error("Exception when getting all skipped stops from RMI", e); + } + } + return allSkippedStops; + } + + private boolean getServiceIdSuffix(){ + ConfigInterface configInterface = ConfigInterfaceFactory.get(agencyId); + if (configInterface == null) { + logger.error("Agency ID {} is not valid", agencyId); + return false; + } + try { + return configInterface.getServiceIdSuffix(); + } catch (Exception e){ + return false; + } + } + + + private boolean isFrequencyBasedTrip(List predsForTrip) + { + for(IpcPrediction prediction:predsForTrip) + { + if(prediction.getFreqStartTime() > 0) + return true; + } + return false; + } + + private void createTripUpdateForFrequencyTrips(FeedMessage.Builder message, + List predsForTrip, + HashMap> allSkippedStops, + boolean serviceIdSuffix){ + try { + Map> map = createFreqStartTimePredictionMap(predsForTrip); + + for(Long key:map.keySet()) + { + if(!map.get(key).isEmpty()) + { + FeedEntity.Builder feedEntity = FeedEntity.newBuilder() + .setId(map.get(key).get(0).getVehicleId()); + TripUpdate tripUpdate = createTripUpdate(map.get(key), allSkippedStops, serviceIdSuffix); + if(tripUpdate == null) continue; + feedEntity.setTripUpdate(tripUpdate); + message.addEntity(feedEntity); + } + } + } catch (Exception e) { + logger.error("Unable to process trip update for frequency trip", e); + } + } + + private Map> createFreqStartTimePredictionMap(List predsForTrip) + { + Map> map=new HashMap<>(); + for(IpcPrediction prediction:predsForTrip) + { + if(map.get(prediction.getFreqStartTime())==null) + { + List list=new ArrayList(); + map.put(prediction.getFreqStartTime(), list); + + } + map.get(prediction.getFreqStartTime()).add(prediction); + + Collections.sort(map.get(prediction.getFreqStartTime()), new PredictionTimeComparator()); + } + return map; + } + + private void createTripUpdateForTrips(FeedMessage.Builder message, + List predsForTrip, + HashMap> allSkippedStops, + boolean serviceIdSuffix){ + // Create feed entity for each schedule trip + FeedEntity.Builder feedEntity = FeedEntity.newBuilder() + .setId(predsForTrip.get(0).getTripId()); + try { + TripUpdate tripUpdate = createTripUpdate(predsForTrip, allSkippedStops, serviceIdSuffix); + if(tripUpdate == null){ + logger.warn("No trip update created for trip {}", predsForTrip.get(0).getTripId()); + return; + } + feedEntity.setTripUpdate(tripUpdate); + message.addEntity(feedEntity); + } catch (Exception e) { + logger.error("Error parsing trip update data. {}", predsForTrip, e); + } + } + + /** + * Returns map of all predictions for the project. Returns null if there was + * a problem getting the data via RMI. There is a separate list of + * predictions for each trip. The map is keyed by tripId. + * + * @return Map keyed on tripId of List of Predictions for the trip, or null + * if could not get data from server. + */ + private Map> getPredictionsPerTrip() { + // Get all the predictions, grouped by vehicle, from the server + List allPredictionsByStop; + try { + allPredictionsByStop = PredictionsInterfaceFactory.get(agencyId) + .getAllPredictions(PREDICTION_MAX_FUTURE_SECS); + } catch (RemoteException e) { + logger.error("Exception when getting vehicles from RMI", e); + return null; + } + + // Group the predictions by trip instead of by vehicle + Map> predictionsByTrip = + new HashMap>(); + for (IpcPredictionsForRouteStopDest predictionsForStop : + allPredictionsByStop) { + for (IpcPrediction prediction : + predictionsForStop.getPredictionsForRouteStop()) { + String tripId = prediction.getTripId(); + List predsForTrip = predictionsByTrip.get(tripId); + if (predsForTrip == null) { + // A new trip so need to use a new trip list + predsForTrip = new ArrayList(); + predictionsByTrip.put(tripId, predsForTrip); + } + + predsForTrip.add(prediction); + } + } + + // Return results + return predictionsByTrip; + } + + /** + * Gets the Vehicle data from RMI and creates corresponding + * GTFS-RT vehicle feed. + * + * @return GTFS-RT FeedMessage for vehicle positions + */ + public FeedMessage createMessage() { + // Get prediction data from server + IntervalTimer timer = new IntervalTimer(); + Map> predsByTrip = getPredictionsPerTrip(); + logger.debug("Getting predictions via RMI for " + + "GtfsRtTripFeed.createMessage() took {} msec", + timer.elapsedMsec()); + + // Use prediction data to create GTFS-RT message and return it. + return createMessage(predsByTrip); + } + + // For getPossiblyCachedMessage() + private static final DataCache tripFeedDataCache = new DataCache(); + + /** + * For caching Vehicle Positions feed messages. + * + * @param agencyId + * @param cacheTime + * @return + */ + public static FeedMessage getPossiblyCachedMessage(String agencyId, int cacheTime) { + FeedMessage feedMessage = tripFeedDataCache.get(agencyId, cacheTime); + if (feedMessage != null) + return feedMessage; + + synchronized(tripFeedDataCache) { + + // Cache may have been filled while waiting. + feedMessage = tripFeedDataCache.get(agencyId, cacheTime); + if (feedMessage != null) + return feedMessage; + + GtfsRtTripFeed feed = new GtfsRtTripFeed(agencyId); + feedMessage = feed.createMessage(); + tripFeedDataCache.put(agencyId, feedMessage); + } + + return feedMessage; + } + + public static class GtfsStopSequenceComparator implements Comparator { + + @Override + public int compare(Object o1, Object o2) { + IpcPrediction ip1 = (IpcPrediction) o1; + IpcPrediction ip2 = (IpcPrediction) o2; + return ip1.getGtfsStopSeq() - ip2.getGtfsStopSeq(); + } + } +} diff --git a/transitimeApi/src/main/java/org/transitime/api/gtfsRealtime/GtfsRtVehicleFeed.java b/transitclockApi/src/main/java/org/transitclock/api/gtfsRealtime/GtfsRtVehicleFeed.java similarity index 55% rename from transitimeApi/src/main/java/org/transitime/api/gtfsRealtime/GtfsRtVehicleFeed.java rename to transitclockApi/src/main/java/org/transitclock/api/gtfsRealtime/GtfsRtVehicleFeed.java index 966649c83..3d559b8f9 100644 --- a/transitimeApi/src/main/java/org/transitime/api/gtfsRealtime/GtfsRtVehicleFeed.java +++ b/transitclockApi/src/main/java/org/transitclock/api/gtfsRealtime/GtfsRtVehicleFeed.java @@ -15,31 +15,31 @@ * Transitime.org . If not, see . */ -package org.transitime.api.gtfsRealtime; +package org.transitclock.api.gtfsRealtime; + +import com.google.transit.realtime.GtfsRealtime.*; +import com.google.transit.realtime.GtfsRealtime.FeedHeader.Incrementality; +import com.google.transit.realtime.GtfsRealtime.VehiclePosition.VehicleStopStatus; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.api.utils.AgencyTimezoneCache; +import org.transitclock.core.dataCache.CanceledTripKey; +import org.transitclock.ipc.clients.ConfigInterfaceFactory; +import org.transitclock.ipc.clients.PredictionsInterfaceFactory; +import org.transitclock.ipc.clients.VehiclesInterfaceFactory; +import org.transitclock.ipc.data.IpcCanceledTrip; +import org.transitclock.ipc.data.IpcVehicleGtfsRealtime; +import org.transitclock.ipc.interfaces.ConfigInterface; +import org.transitclock.ipc.interfaces.VehiclesInterface; +import org.transitclock.utils.Time; import java.rmi.RemoteException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Collection; import java.util.Date; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.transitime.api.utils.AgencyTimezoneCache; -import org.transitime.ipc.clients.VehiclesInterfaceFactory; -import org.transitime.ipc.data.IpcVehicleGtfsRealtime; -import org.transitime.ipc.interfaces.VehiclesInterface; -import org.transitime.utils.Time; - -import com.google.transit.realtime.GtfsRealtime.FeedEntity; -import com.google.transit.realtime.GtfsRealtime.FeedHeader; -import com.google.transit.realtime.GtfsRealtime.FeedMessage; -import com.google.transit.realtime.GtfsRealtime.Position; -import com.google.transit.realtime.GtfsRealtime.TripDescriptor; -import com.google.transit.realtime.GtfsRealtime.VehicleDescriptor; -import com.google.transit.realtime.GtfsRealtime.VehiclePosition; -import com.google.transit.realtime.GtfsRealtime.FeedHeader.Incrementality; -import com.google.transit.realtime.GtfsRealtime.VehiclePosition.VehicleStopStatus; +import java.util.HashMap; +import java.util.Map; /** * For creating GTFS-realtime Vehicle feed. The data is obtained via RMI. @@ -55,6 +55,9 @@ public class GtfsRtVehicleFeed { private SimpleDateFormat gtfsRealtimeDateFormatter = new SimpleDateFormat("yyyyMMdd"); + private SimpleDateFormat gtfsRealtimeTimeFormatter = + new SimpleDateFormat("HH:mm:ss"); + private static final Logger logger = LoggerFactory .getLogger(GtfsRtVehicleFeed.class); @@ -90,8 +93,30 @@ private VehiclePosition createVehiclePosition( TripDescriptor.newBuilder() .setRouteId(vehicleData.getRouteId()) .setTripId(vehicleData.getTripId()) + .setStartDate(tripStartDateStr); + + if(vehicleData.getFreqStartTime()>0) + { + String tripStartTimeStr=gtfsRealtimeTimeFormatter.format(new Date(vehicleData.getFreqStartTime())); + tripDescriptor.setStartTime(tripStartTimeStr); + } + + TripDescriptor.ScheduleRelationship scheduleRelationship = getScheduleRelationship(vehicleData); + + // Set the relation between this trip and the static schedule. ADDED and CANCELED not supported. + if (vehicleData.isTripUnscheduled()) { + // A trip that is running with no schedule associated to it - + // this value is used to identify trips defined in GTFS frequencies.txt with exact_times = 0 + tripDescriptor.setScheduleRelationship(TripDescriptor.ScheduleRelationship.UNSCHEDULED); + } else { + // Trip that is running in accordance with its GTFS schedule, + // or is close enough to the scheduled trip to be associated with it. + tripDescriptor.setScheduleRelationship(scheduleRelationship); + } + vehiclePosition.setTrip(tripDescriptor); + } // Add the VehicleDescriptor information @@ -143,6 +168,55 @@ private VehiclePosition createVehiclePosition( return vehiclePosition.build(); } + private VehiclePosition createCanceledVehiclePosition(String vehicleId, IpcCanceledTrip canceledTrip) throws ParseException { + // Create the parent VehiclePosition object that is returned. + VehiclePosition.Builder vehiclePosition = VehiclePosition.newBuilder(); + TripDescriptor.Builder tripDescriptor = TripDescriptor.newBuilder(); + + if(canceledTrip.getRouteId() != null){ + tripDescriptor.setRouteId(canceledTrip.getRouteId()); + } + + if(canceledTrip.getTripId() != null){ + tripDescriptor.setTripId(canceledTrip.getTripId()); + } + + if(canceledTrip.getTripStartDate() != null){ + tripDescriptor.setStartDate(canceledTrip.getTripStartDate()); + } + + tripDescriptor.setScheduleRelationship(TripDescriptor.ScheduleRelationship.CANCELED); + + vehiclePosition.setTrip(tripDescriptor); + + + // Add the VehicleDescriptor information + VehicleDescriptor.Builder vehicleDescriptor = + VehicleDescriptor.newBuilder().setId(vehicleId); + + vehiclePosition.setVehicle(vehicleDescriptor); + + // Add the Position information + Position.Builder position = + Position.newBuilder().setLatitude(canceledTrip.getLatitude()) + .setLongitude(canceledTrip.getLongitude()); + + vehiclePosition.setPosition(position); + + // Return the results + return vehiclePosition.build(); + } + + private TripDescriptor.ScheduleRelationship getScheduleRelationship(IpcVehicleGtfsRealtime prediction){ + + if(prediction.isCanceled()){ + return TripDescriptor.ScheduleRelationship.CANCELED; + } + + return TripDescriptor.ScheduleRelationship.SCHEDULED; + + } + /** * Creates a GTFS-realtime message for the list of ApiVehicle passed in. * @@ -163,10 +237,23 @@ private FeedMessage createMessage( System.currentTimeMillis() / Time.MS_PER_SEC); message.setHeader(feedheader); + HashMap allCanceledTrips = new HashMap<>(); + + try { + allCanceledTrips = PredictionsInterfaceFactory.get(agencyId).getAllCanceledTrips(); + } catch (RemoteException e) { + logger.error("Exception when getting all canceled trips from RMI", e); + } + + boolean serviceIdSuffix = getServiceIdSuffix(); + for (IpcVehicleGtfsRealtime vehicle : vehicles) { + if(isVehicleAndTripCanceledAndCached(vehicle, allCanceledTrips, serviceIdSuffix)){ + continue; + } + FeedEntity.Builder vehiclePositionEntity = FeedEntity.newBuilder().setId(vehicle.getId()); - try { VehiclePosition vehiclePosition = createVehiclePosition(vehicle); @@ -178,9 +265,42 @@ private FeedMessage createMessage( } } + for(Map.Entry entry : allCanceledTrips.entrySet()){ + CanceledTripKey key = entry.getKey(); + String vehicleId = key.getVehicleId(); + IpcCanceledTrip canceledTrip = entry.getValue(); + + if(vehicleId != null && canceledTrip != null){ + FeedEntity.Builder vehiclePositionEntity = + FeedEntity.newBuilder().setId(vehicleId); + try { + VehiclePosition vehiclePosition = + createCanceledVehiclePosition(vehicleId, canceledTrip); + vehiclePositionEntity.setVehicle(vehiclePosition); + message.addEntity(vehiclePositionEntity); + } catch (Exception e) { + logger.error("Error parsing vehicle data for canceled trip vehicle={}", + vehicleId, e); + } + } + } + return message.build(); } + private boolean getServiceIdSuffix(){ + ConfigInterface configInterface = ConfigInterfaceFactory.get(agencyId); + if (configInterface == null) { + logger.error("Agency ID {} is not valid", agencyId); + return false; + } + try { + return configInterface.getServiceIdSuffix(); + } catch (Exception e){ + return false; + } + } + /** * Returns collection of all vehicles for the project obtained via RMI. * Returns null if there was a problem getting the data via RMI @@ -199,6 +319,21 @@ private Collection getVehicles() { return vehicles; } + private boolean isVehicleAndTripCanceledAndCached(IpcVehicleGtfsRealtime vehicle, + Map canceledTrips, + boolean serviceIdSuffix) { + String tripId = vehicle.getTripId(); + String vehicleId = vehicle.getId(); + + IpcCanceledTrip canceledTrip = canceledTrips.get(new CanceledTripKey(vehicleId, tripId)); + + if(canceledTrip != null && canceledTrip.getTripId() != null && canceledTrip.getTripId().equalsIgnoreCase(tripId)){ + return true; + } + + return false; + } + /** * Gets the Vehicle data from RMI and creates corresponding GTFS-RT vehicle * feed. @@ -225,10 +360,19 @@ public static FeedMessage getPossiblyCachedMessage(String agencyId, FeedMessage feedMessage = vehicleFeedDataCache.get(agencyId, cacheTime); if (feedMessage != null) return feedMessage; - - GtfsRtVehicleFeed feed = new GtfsRtVehicleFeed(agencyId); - feedMessage = feed.createMessage(); - vehicleFeedDataCache.put(agencyId, feedMessage); + + synchronized(vehicleFeedDataCache) { + + // Cache may have been filled while waiting. + feedMessage = vehicleFeedDataCache.get(agencyId, cacheTime); + if (feedMessage != null) + return feedMessage; + + GtfsRtVehicleFeed feed = new GtfsRtVehicleFeed(agencyId); + feedMessage = feed.createMessage(); + vehicleFeedDataCache.put(agencyId, feedMessage); + } + return feedMessage; } } diff --git a/transitimeApi/src/main/java/org/transitime/api/package-info.java b/transitclockApi/src/main/java/org/transitclock/api/package-info.java similarity index 99% rename from transitimeApi/src/main/java/org/transitime/api/package-info.java rename to transitclockApi/src/main/java/org/transitclock/api/package-info.java index 73d27d218..ddae5c4d4 100644 --- a/transitimeApi/src/main/java/org/transitime/api/package-info.java +++ b/transitclockApi/src/main/java/org/transitclock/api/package-info.java @@ -189,4 +189,4 @@ * */ -package org.transitime.api; \ No newline at end of file +package org.transitclock.api; \ No newline at end of file diff --git a/transitimeApi/src/main/java/org/transitime/api/predsByLoc/PredsByLoc.java b/transitclockApi/src/main/java/org/transitclock/api/predsByLoc/PredsByLoc.java similarity index 89% rename from transitimeApi/src/main/java/org/transitime/api/predsByLoc/PredsByLoc.java rename to transitclockApi/src/main/java/org/transitclock/api/predsByLoc/PredsByLoc.java index 9465699b8..4e24c9f4e 100644 --- a/transitimeApi/src/main/java/org/transitime/api/predsByLoc/PredsByLoc.java +++ b/transitclockApi/src/main/java/org/transitclock/api/predsByLoc/PredsByLoc.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.api.predsByLoc; +package org.transitclock.api.predsByLoc; import java.util.ArrayList; import java.util.Collection; @@ -22,11 +22,11 @@ import java.util.List; import java.util.Map; -import org.transitime.db.structs.Agency; -import org.transitime.db.structs.Extent; -import org.transitime.db.structs.Location; -import org.transitime.db.webstructs.WebAgency; -import org.transitime.utils.Time; +import org.transitclock.db.structs.Agency; +import org.transitclock.db.structs.Extent; +import org.transitclock.db.structs.Location; +import org.transitclock.db.webstructs.WebAgency; +import org.transitclock.utils.Time; /** * For determining predictions by location for when agency is not specified so diff --git a/transitclockApi/src/main/java/org/transitclock/api/rootResources/CacheApi.java b/transitclockApi/src/main/java/org/transitclock/api/rootResources/CacheApi.java new file mode 100755 index 000000000..4ae7a4798 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/rootResources/CacheApi.java @@ -0,0 +1,396 @@ +/* + * 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.api.rootResources; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.util.Date; +import java.util.List; +import javax.ws.rs.BeanParam; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.transitclock.api.data.ApiArrivalDepartures; +import org.transitclock.api.data.ApiCacheDetails; +import org.transitclock.api.data.ApiHistoricalAverage; +import org.transitclock.api.data.ApiHistoricalAverageCacheKeys; +import org.transitclock.api.data.ApiHoldingTime; +import org.transitclock.api.data.ApiHoldingTimeCacheKeys; +import org.transitclock.api.data.ApiKalmanErrorCacheKeys; +import org.transitclock.api.data.ApiPredictionsForStopPath; +import org.transitclock.api.utils.StandardParameters; +import org.transitclock.api.utils.WebUtils; +import org.transitclock.ipc.data.IpcArrivalDeparture; +import org.transitclock.ipc.data.IpcHistoricalAverage; +import org.transitclock.ipc.data.IpcHistoricalAverageCacheKey; +import org.transitclock.ipc.data.IpcHoldingTime; +import org.transitclock.ipc.data.IpcHoldingTimeCacheKey; +import org.transitclock.ipc.data.IpcKalmanErrorCacheKey; +import org.transitclock.ipc.data.IpcPredictionForStopPath; +import org.transitclock.ipc.interfaces.CacheQueryInterface; +import org.transitclock.ipc.interfaces.HoldingTimeInterface; +import org.transitclock.ipc.interfaces.PredictionAnalysisInterface; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.servers.Server; +import io.swagger.v3.oas.annotations.servers.Servers; + +/** + * Contains the API commands for the Transitime API for getting info on data that is cached. + *

    + * The data output can be in either JSON or XML. The output format is specified + * by the accept header or by using the query string parameter "format=json" or + * "format=xml". + * + * @author SkiBu Smith + * + */ +@Path("/key/{key}/agency/{agency}") +public class CacheApi { + + + @Path("/command/kalmanerrorcachekeys") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Gets the list of Kalman Cache error.", + description="Gets the list of Kalman Cache error.", + tags= {"kalman","cache"}) + public Response getKalmanErrorCacheKeys(@BeanParam StandardParameters stdParameters) + throws WebApplicationException { + try { + CacheQueryInterface cachequeryInterface = stdParameters.getCacheQueryInterface(); + + List result = cachequeryInterface.getKalmanErrorCacheKeys(); + + ApiKalmanErrorCacheKeys keys = new ApiKalmanErrorCacheKeys(result); + + Response response = stdParameters.createResponse(keys); + + return response; + + } catch (Exception e) { + // If problem getting result then return a Bad Request + throw WebUtils.badRequestException(e.getMessage()); + } + } + @Path("/command/scheduledbasedhistoricalaveragecachekeys") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Gets a list of the keys that have values in the historical average cache for schedules based services.", + description="Gets a list of the keys that have values in the historical average cache for schedules based services.", + tags= {"cache"}) + public Response getSchedulesBasedHistoricalAverageCacheKeys(@BeanParam StandardParameters stdParameters) + throws WebApplicationException { + try { + CacheQueryInterface cachequeryInterface = stdParameters.getCacheQueryInterface(); + + List result = cachequeryInterface.getScheduledBasedHistoricalAverageCacheKeys(); + + ApiHistoricalAverageCacheKeys keys = new ApiHistoricalAverageCacheKeys(result); + + Response response = stdParameters.createResponse(keys); + + return response; + + } catch (Exception e) { + // If problem getting result then return a Bad Request + throw WebUtils.badRequestException(e.getMessage()); + } + } + // TODO This is not completed and should not be used. + @Path("/command/frequencybasedhistoricalaveragecachekeys") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Gets a list of the keys that have values in the historical average cache for frequency based services.", + description="Gets a list of the keys that have values in the historical average cache for frequency based services." + + "This is not completed and should not be used.", + tags= {"cache"}) + public Response getFrequencyBasedHistoricalAverageCacheKeys(@BeanParam StandardParameters stdParameters) + throws WebApplicationException { + try { + CacheQueryInterface cachequeryInterface = stdParameters.getCacheQueryInterface(); + + List result = cachequeryInterface.getFrequencyBasedHistoricalAverageCacheKeys(); + + ApiHistoricalAverageCacheKeys keys = new ApiHistoricalAverageCacheKeys(result); + + Response response = stdParameters.createResponse(keys); + + return response; + + } catch (Exception e) { + // If problem getting result then return a Bad Request + throw WebUtils.badRequestException(e.getMessage()); + } + } + + @Path("/command/holdingtimecachekeys") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Gets a list of the keys for the holding times in the cache.", + description="Gets a list of the keys for the holding times in the cache.", + tags= {"cache"}) + public Response getHoldingTimeCacheKeys(@BeanParam StandardParameters stdParameters) + throws WebApplicationException { + try { + CacheQueryInterface cachequeryInterface = stdParameters.getCacheQueryInterface(); + + List result = cachequeryInterface.getHoldingTimeCacheKeys(); + + ApiHoldingTimeCacheKeys keys = new ApiHoldingTimeCacheKeys(result); + + Response response = stdParameters.createResponse(keys); + + return response; + + } catch (Exception e) { + // If problem getting result then return a Bad Request + throw WebUtils.badRequestException(e.getMessage()); + } + } + + /** + * Returns info about a cache. + * + * @param stdParameters + * @param cachename + * this is the name of the cache to get the size of. + * @return + * @throws WebApplicationException + */ + @Path("/command/cacheinfo") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Returns the number of entries in the cacheName cache.", + description="Returns the number of entries in the cacheName cache. The name is passed throug the cachename parameter.", + tags= {"cache"}) + public Response getCacheInfo(@BeanParam StandardParameters stdParameters, + @Parameter(description="Name of the cache",required=true) + @QueryParam(value = "cachename") String cachename) throws WebApplicationException { + try { + + CacheQueryInterface cachequeryInterface = stdParameters.getCacheQueryInterface(); + + Integer size = cachequeryInterface.entriesInCache(cachename); + + if (size != null) + return stdParameters.createResponse(new ApiCacheDetails(cachename, size)); + else + throw new Exception("No cache named:" + cachename); + + } catch (Exception e) { + // If problem getting result then return a Bad Request + throw WebUtils.badRequestException(e.getMessage()); + } + + } + + @Path("/command/stoparrivaldeparturecachedata") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Returns a list of current arrival or departure events for a specified stop that are in the cache.", + description="Returns a list of current arrival or departure events for a specified stop that are in the cache.", + tags= {"cache"}) + public Response getStopArrivalDepartureCacheData(@BeanParam StandardParameters stdParameters, + @Parameter(description="Stop Id.",required=true)@QueryParam(value = "stopid") String stopid, @QueryParam(value = "date") Date date) + throws WebApplicationException { + try { + + CacheQueryInterface cachequeryInterface = stdParameters.getCacheQueryInterface(); + + List result = cachequeryInterface.getStopArrivalDepartures(stopid); + + ApiArrivalDepartures apiResult = new ApiArrivalDepartures(result); + Response response = stdParameters.createResponse(apiResult); + return response; + + } catch (Exception e) { + // If problem getting result then return a Bad Request + throw WebUtils.badRequestException(e.getMessage()); + } + } + + @Path("/command/triparrivaldeparturecachedata") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Returns the arrivals and departures for a trip on a specific day and start time.", + description="Returns a list of the arrivals and departures for a trip on a specific day and start time." + + "Either tripId or date must be specified.", + tags= {"cache"}) + public Response getTripArrivalDepartureCacheData(@BeanParam StandardParameters stdParameters, + @Parameter(description="if specified, returns the list for that tripId.",required=false) + @QueryParam(value = "tripId") String tripid, + @Parameter(description="if specified, returns the list for that date.",required=false) + @QueryParam(value = "date") DateParam date, + @Parameter(description="if specified, returns the list for that starttime.",required=false) + @QueryParam(value = "starttime") Integer starttime) throws WebApplicationException { + try { + + CacheQueryInterface cachequeryInterface = stdParameters.getCacheQueryInterface(); + LocalDate queryDate = null; + if (date != null) + queryDate = date.getDate(); + List result = cachequeryInterface.getTripArrivalDepartures(tripid, queryDate, + starttime); + + ApiArrivalDepartures apiResult = new ApiArrivalDepartures(result); + Response response = stdParameters.createResponse(apiResult); + return response; + + } catch (Exception e) { + // If problem getting result then return a Bad Request + throw WebUtils.badRequestException(e.getMessage()); + } + } + + /* + * This will give the historical cache value for an individual stop path + * index of a trip private String tripId; private Integer stopPathIndex; + */ + @Path("/command/historicalaveragecachedata") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Returns the historical cache value for an individual stop path index of a trip.", + description="Returns the historical cache value for an individual stop path index of a trip.", + tags= {"cache"}) + public Response getHistoricalAverageCacheData(@BeanParam StandardParameters stdParameters, + @Parameter(description="Trip Id",required=true) + @QueryParam(value = "tripId") String tripId, + @Parameter(description="Stop path index",required=true) + @QueryParam(value = "stopPathIndex") Integer stopPathIndex) { + try { + + CacheQueryInterface cachequeryInterface = stdParameters.getCacheQueryInterface(); + + IpcHistoricalAverage result = cachequeryInterface.getHistoricalAverage(tripId, stopPathIndex); + + Response response = stdParameters.createResponse(new ApiHistoricalAverage(result)); + + return response; + + } catch (Exception e) { + // If problem getting result then return a Bad Request + throw WebUtils.badRequestException(e.getMessage()); + } + } + + @Path("/command/getkalmanerrorvalue") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Returns the latest Kalman error value for a the stop path of a trip.", + description="Returns the latest Kalman error value for a the stop path of a trip.", + tags= {"kalman","cache"}) + public Response getKalmanErrorValue(@BeanParam StandardParameters stdParameters, + @Parameter(description="Trip Id",required=true) + @QueryParam(value = "tripId") String tripId, + @Parameter(description="Stop path index",required=true) + @QueryParam(value = "stopPathIndex") Integer stopPathIndex) { + try { + + CacheQueryInterface cachequeryInterface = stdParameters.getCacheQueryInterface(); + + Double result = cachequeryInterface.getKalmanErrorValue(tripId, stopPathIndex); + + Response response = stdParameters.createResponse(result); + + return response; + + } catch (Exception e) { + // If problem getting result then return a Bad Request + throw WebUtils.badRequestException(e.getMessage()); + } + } + @Path("/command/getstoppathpredictions") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Returns a list of predictions for a the stop path of a trip.", + description="Returns a list of predictions for a the stop path of a trip.", + tags= {"cache"}) + //TODO: (vsperez) I believe date is not used at all + public Response getStopPathPredictions(@BeanParam StandardParameters stdParameters, + @Parameter(description="Algorith used for calculating the perdiction",required=false) + @QueryParam(value = "algorithm") String algorithm, + @Parameter(description="Trip Id",required=true) + @QueryParam(value = "tripId") String tripId, + @Parameter(description="Stop path index",required=true) + @QueryParam(value = "stopPathIndex" ) Integer stopPathIndex, + @Parameter(description="Specified the date.",required=true) + @QueryParam(value = "date") DateParam date) + { + try { + LocalTime midnight = LocalTime.MIDNIGHT; + Date end_date=null; + Date start_date=null; + if(date!=null) + { + LocalDate now = date.getDate(); + + LocalDateTime todayMidnight = LocalDateTime.of(now, midnight); + LocalDateTime yesterdatMidnight = todayMidnight.plusDays(-1); + + end_date = Date.from(todayMidnight.atZone(ZoneId.systemDefault()).toInstant()); + start_date = Date.from(yesterdatMidnight.atZone(ZoneId.systemDefault()).toInstant()); + } + + PredictionAnalysisInterface predictionAnalysisInterface = stdParameters.getPredictionAnalysisInterface(); + + List result = predictionAnalysisInterface.getCachedTravelTimePredictions(tripId, stopPathIndex, start_date, end_date, algorithm); + + Response response = stdParameters.createResponse(new ApiPredictionsForStopPath(result)); + + return response; + + } catch (Exception e) { + // If problem getting result then return a Bad Request + throw WebUtils.badRequestException(e.getMessage()); + } + } + @Path("/command/getholdingtime") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Returns the IpcHoldingTime for a specific stop Id and vehicle Id.", + description="Returns the IpcHoldingTime for a specific stop Id and vehicle Id.", + tags= {"cache"}) + public Response getHoldingTime(@BeanParam StandardParameters stdParameters, + @Parameter(description="Stop id",required=true) + @QueryParam(value = "stopId") String stopId, + @Parameter(description="Vehicle id",required=true) + @QueryParam(value = "vehicleId" ) String vehicleId) + { + try { + HoldingTimeInterface holdingtimeInterface = stdParameters.getHoldingTimeInterface(); + IpcHoldingTime result = holdingtimeInterface.getHoldTime(stopId, vehicleId); + + Response response = stdParameters.createResponse(new ApiHoldingTime(result)); + + return response; + } catch (Exception e) { + // If problem getting result then return a Bad Request + throw WebUtils.badRequestException(e.getMessage()); + } + } + +} diff --git a/transitimeApi/src/main/java/org/transitime/api/rootResources/CommandsApi.java b/transitclockApi/src/main/java/org/transitclock/api/rootResources/CommandsApi.java similarity index 50% rename from transitimeApi/src/main/java/org/transitime/api/rootResources/CommandsApi.java rename to transitclockApi/src/main/java/org/transitclock/api/rootResources/CommandsApi.java index eb6d1b69c..138ef9561 100644 --- a/transitimeApi/src/main/java/org/transitime/api/rootResources/CommandsApi.java +++ b/transitclockApi/src/main/java/org/transitclock/api/rootResources/CommandsApi.java @@ -14,21 +14,24 @@ * You should have received a copy of the GNU General Public License * along with Transitime.org . If not, see . */ -package org.transitime.api.rootResources; +package org.transitclock.api.rootResources; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.rmi.RemoteException; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Collection; +import java.util.List; import java.util.Date; - import javax.ws.rs.BeanParam; import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; +import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.WebApplicationException; @@ -38,15 +41,24 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; -import org.transitime.api.data.ApiCommandAck; -import org.transitime.api.utils.StandardParameters; -import org.transitime.api.utils.WebUtils; -import org.transitime.db.GenericQuery; -import org.transitime.db.structs.AvlReport; -import org.transitime.db.structs.MeasuredArrivalTime; -import org.transitime.db.structs.AvlReport.AssignmentType; -import org.transitime.ipc.data.IpcAvl; -import org.transitime.ipc.interfaces.CommandsInterface; +import org.transitclock.api.data.ApiCommandAck; +import org.transitclock.api.utils.StandardParameters; +import org.transitclock.api.utils.WebUtils; +import org.transitclock.db.GenericQuery; +import org.transitclock.db.structs.AvlReport; +import org.transitclock.db.structs.MeasuredArrivalTime; +import org.transitclock.db.structs.AvlReport.AssignmentType; +import org.transitclock.ipc.data.IpcAvl; +import org.transitclock.ipc.data.IpcTrip; +import org.transitclock.ipc.interfaces.CommandsInterface; +import org.transitclock.ipc.interfaces.ConfigInterface; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.servers.Server; +import io.swagger.v3.oas.annotations.servers.Servers; @Path("/key/{key}/agency/{agency}") public class CommandsApi { @@ -79,16 +91,18 @@ public class CommandsApi { @Path("/command/pushAvl") @GET @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Reads in a single AVL report specified by the query string parameters", + description="Reads in a single AVL report specified by the query string parameters.",tags= {"operation","vehicle","avl"}) public Response pushAvlData( @BeanParam StandardParameters stdParameters, - @QueryParam(value = "v") String vehicleId, - @QueryParam(value = "t") long time, - @QueryParam(value = "lat") double lat, - @QueryParam(value = "lon") double lon, - @QueryParam(value = "s") @DefaultValue("NaN") float speed, - @QueryParam(value = "h") @DefaultValue("NaN") float heading, - @QueryParam(value = "assignmentId") String assignmentId, - @QueryParam(value = "assignmentType") String assignmentTypeStr) + @Parameter(description="VehicleId. Unique identifier of the vehicle.",required=true)@QueryParam(value = "v") String vehicleId, + @Parameter(description="GPS epoch time in msec.",required=true) @QueryParam(value = "t") long time, + @Parameter(description="Latitude of AVL reporte. Decimal degrees.",required=true) @QueryParam(value = "lat") double lat, + @Parameter(description="Longitude of AVL reporte. Decimal degrees.",required=true) @QueryParam(value = "lon") double lon, + @Parameter(description="Speed of AVL reporte. m/s.",required=false) @QueryParam(value = "s") @DefaultValue("NaN") float speed, + @Parameter(description="Heading of AVL report. Degrees. 0 degrees=North. Should be set to Float.NaN if speed not available",required=false)@QueryParam(value = "h") @DefaultValue("NaN") float heading, + @Parameter(description="Indicates the assignmet id of the AVL report according to the assingment tyoe. For example, if assingment type is ROUTE_ID, the assingment ID should be one route_id loaded in the system.", required=false)@QueryParam(value = "assignmentId") String assignmentId, + @Parameter(description="Indicates the assignmet type of the AV report. This parameter can take the next values:

    • ROUTE_ID
    • TRIP_ID
    • TRIP_SHORT_NAME
    ")@QueryParam(value = "assignmentType") String assignmentTypeStr) throws WebApplicationException { // Make sure request is valid stdParameters.validate(); @@ -129,7 +143,7 @@ else if (assignmentTypeStr.equals("TRIP_SHORT_NAME")) return stdParameters.createResponse(ack); } catch (Exception e) { // If problem getting data then return a Bad Request - throw WebUtils.badRequestException(e.getMessage()); + throw WebUtils.badRequestException(e); } } @@ -183,8 +197,16 @@ private static JSONObject getJsonObject(InputStream requestBody) @Path("/command/pushAvl") @POST @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Reads in a single AVL report in the message body.", + description="Reads in a single AVL report specified by the query string parameters.

    {avl: [{v: \"vehicleId1\", t: epochTimeMsec, lat: latitude, lon: longitude, s:speed(optional), h:heading(optional)},\r\n" + + " {v: \"vehicleId2\", t: epochTimeMsec, lat: latitude, lon: longitude, " + + " s: speed(optional), h: heading(optional)}, "+ + " {etc...}]

    . Can also specify assignment info using " + + " \"assignmentId: 4321, assignmentType: TRIP_ID\" " + + " where assignmentType can be BLOCK_ID, ROUTE_ID, TRIP_ID, or " + + " TRIP_SHORT_NAME.",tags= {"operation","vehicle","avl"}) public Response pushAvlData(@BeanParam StandardParameters stdParameters, - InputStream requestBody) throws WebApplicationException { + @Parameter(description="Json of avl report.",required=true)InputStream requestBody) throws WebApplicationException { // Make sure request is valid stdParameters.validate(); @@ -235,13 +257,39 @@ else if (assignmentTypeStr.equals("TRIP_SHORT_NAME")) inter.pushAvl(avlData); } catch (JSONException | IOException e) { // If problem getting data then return a Bad Request - throw WebUtils.badRequestException(e.getMessage()); + throw WebUtils.badRequestException(e); } // Create the acknowledgment and return it as JSON or XML ApiCommandAck ack = new ApiCommandAck(true, "AVL processed"); return stdParameters.createResponse(ack); } + @Path("/command/resetVehicle") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Reset a vehicle", + description="This is to give the means of manually setting a vehicle unpredictable and unassigned so it will be reassigned quickly.", + tags= {"command","vehicle"}) + public Response getVehicles(@BeanParam StandardParameters stdParameters, + @Parameter(description="List of vechilesId.")@QueryParam(value = "v") List vehicleIds) throws WebApplicationException { + // Make sure request is valid + stdParameters.validate(); + + try { + CommandsInterface inter = stdParameters.getCommandsInterface(); + + for(String vehicleId: vehicleIds) + { + inter.setVehicleUnpredictable(vehicleId); + } + } catch (RemoteException e) { + throw WebUtils.badRequestException(e.getMessage()); + } + + ApiCommandAck ack = new ApiCommandAck(true, "Vehicle reset"); + + return stdParameters.createResponse(ack); + } /** * Reads in information from request and stores arrival information into db. @@ -255,13 +303,15 @@ else if (assignmentTypeStr.equals("TRIP_SHORT_NAME")) @Path("/command/pushMeasuredArrivalTime") @GET @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Reads in information from request and stores arrival information into db", + description="For storing a measured arrival time so that can see if measured arrival time via GPS is accurate.",tags= {"command"}) public Response pushAvlData( @BeanParam StandardParameters stdParameters, - @QueryParam(value = "r") String routeId, - @QueryParam(value = "rShortName") String routeShortName, - @QueryParam(value = "s") String stopId, - @QueryParam(value = "d") String directionId, - @QueryParam(value = "headsign") String headsign) + @Parameter(description="Route id",required=true) @QueryParam(value = "r") String routeId, + @Parameter(description="Route short name.",required=true) @QueryParam(value = "rShortName") String routeShortName, + @Parameter(description="Route stop id.",required=true) @QueryParam(value = "s") String stopId, + @Parameter(description="Direcction id.",required=true) @QueryParam(value = "d") String directionId, + @Parameter(description="headsign.",required=true) @QueryParam(value = "headsign") String headsign) throws WebApplicationException { // Make sure request is valid stdParameters.validate(); @@ -283,8 +333,75 @@ public Response pushAvlData( return stdParameters.createResponse(ack); } catch (Exception e) { // If problem getting data then return a Bad Request - throw WebUtils.badRequestException(e.getMessage()); + throw WebUtils.badRequestException(e); + } + } +// WORK IN PROGRESS + @Path("/command/cancelTrip/{tripId}") + @GET //SHOULD BE POST,IT IS AN UPDATE + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Cancel a trip in order to be shown in GTFS realtime.", + description="Experimental. It will work olny with the correct version. It cancel a trip that has no vechilce assigned." + ,tags= {"command","trip"}) + + public Response cancelTrip(@BeanParam StandardParameters stdParameters, + @Parameter(description="tripId to be marked as canceled.",required=true)@PathParam("tripId") String tripId, + @Parameter(description="start trip time",required=false) @QueryParam( value="at") DateTimeParam at + ) + { + stdParameters.validate(); + String result=null; + try + { + CommandsInterface inter = stdParameters.getCommandsInterface(); + //We need to get the block id in order to get the vehicle + ConfigInterface cofingInterface = stdParameters.getConfigInterface(); + IpcTrip ipcTrip = cofingInterface.getTrip(tripId); + if(ipcTrip==null) + throw WebUtils.badRequestException("TripId=" + tripId + " does not exist."); + result=inter.cancelTrip(tripId,at==null?null:at.getDate()); } + catch (RemoteException e) { + e.printStackTrace(); + throw WebUtils.badRequestException("Could not send request to Core server. "+e.getMessage()); + } + + if(result==null) + return stdParameters.createResponse(new ApiCommandAck(true,"Processed")); + else + return stdParameters.createResponse(new ApiCommandAck(true,result)); } + @Path("/command/reenableTrip/{tripId}") + @GET //SHOULD BE POST,IT IS AN UPDATE + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Cancel a trip in order to be shown in GTFS realtime.", + description="Experimental. It will work olny with the correct version. It cancel a trip that has no vechilce assigned." + ,tags= {"command","trip"}) + + public Response reenableTrip(@BeanParam StandardParameters stdParameters, + @Parameter(description="tripId to remove calceled satate.",required=true)@PathParam("tripId") String tripId, + @Parameter(description="start trip time",required=false) @QueryParam( value="at") DateTimeParam at) + { + stdParameters.validate(); + String result=null; + try + { + CommandsInterface inter = stdParameters.getCommandsInterface(); + //We need to get the block id in order to get the vehicle + ConfigInterface cofingInterface = stdParameters.getConfigInterface(); + IpcTrip ipcTrip = cofingInterface.getTrip(tripId); + if(ipcTrip==null) + throw WebUtils.badRequestException("TripId=" + tripId + " does not exist."); + result=inter.reenableTrip(tripId,at==null?null:at.getDate()); + } + catch (RemoteException e) { + e.printStackTrace(); + throw WebUtils.badRequestException("Could not send request to Core server. "+e.getMessage()); + } + if(result==null) + return stdParameters.createResponse(new ApiCommandAck(true,"Processed")); + else + return stdParameters.createResponse(new ApiCommandAck(true,result)); + } } diff --git a/transitclockApi/src/main/java/org/transitclock/api/rootResources/DateParam.java b/transitclockApi/src/main/java/org/transitclock/api/rootResources/DateParam.java new file mode 100755 index 000000000..305ba2e6e --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/rootResources/DateParam.java @@ -0,0 +1,50 @@ +package org.transitclock.api.rootResources; + +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.Date; + +import javax.ws.rs.WebApplicationException; + + +public class DateParam { + + private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + + private LocalDate date; + + public DateParam(String in) throws WebApplicationException { + try { + date=LocalDate.parse(in, DateTimeFormatter.ISO_DATE); + + } + catch (Exception exception) { + throw new WebApplicationException(400); + } + } + public LocalDate getDate() { + return date; + } + public String format() { + return date.toString(); + } + public static void main(String[] args) { + + LocalTime midnight = LocalTime.MIDNIGHT; + + DateParam date=new DateParam("2016-09-03"); + LocalDate now = date.getDate(); // 2015-11-19T19:42:19.224 + + LocalDateTime todayMidnight = LocalDateTime.of(now, midnight); + LocalDateTime yesterdatMidnight = todayMidnight.plusDays(-1); + + Date end_date = Date.from(todayMidnight.atZone(ZoneId.systemDefault()).toInstant()); + Date start_date = Date.from(yesterdatMidnight.atZone(ZoneId.systemDefault()).toInstant()); + + System.out.println(start_date+ " " +end_date); + } +} \ No newline at end of file diff --git a/transitclockApi/src/main/java/org/transitclock/api/rootResources/DateTimeParam.java b/transitclockApi/src/main/java/org/transitclock/api/rootResources/DateTimeParam.java new file mode 100644 index 000000000..b8b26a66f --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/rootResources/DateTimeParam.java @@ -0,0 +1,46 @@ +package org.transitclock.api.rootResources; + +import java.text.SimpleDateFormat; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; + +import javax.ws.rs.WebApplicationException; + + + +public class DateTimeParam { + + private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); + + private LocalDateTime date; + + public DateTimeParam(String in) throws WebApplicationException { + try { + date=LocalDateTime.parse(in,DateTimeFormatter.ISO_LOCAL_DATE_TIME); + + } + catch (Exception exception) { + throw new WebApplicationException(400); + } + } + public LocalDateTime getDate() { + return date; + } + public long getTimeStamp() { + return date.toEpochSecond(ZoneOffset.UTC)*1000L; + } + public String format() { + return date.toString(); + } + public static void main(String[] args) { + + + DateTimeParam date=new DateTimeParam("2018-02-02T18:02:00"); + LocalDateTime now = date.getDate(); // 2015-11-19T19:42:19.224 + + + + System.out.println(now+ " "+date.getTimeStamp()+ " "+format.format(now.toEpochSecond(ZoneOffset.UTC)*1000L)); + } + } \ No newline at end of file diff --git a/transitimeApi/src/main/java/org/transitime/api/rootResources/GtfsRealtimeApi.java b/transitclockApi/src/main/java/org/transitclock/api/rootResources/GtfsRealtimeApi.java similarity index 78% rename from transitimeApi/src/main/java/org/transitime/api/rootResources/GtfsRealtimeApi.java rename to transitclockApi/src/main/java/org/transitclock/api/rootResources/GtfsRealtimeApi.java index d29efc789..f6f42f374 100644 --- a/transitimeApi/src/main/java/org/transitime/api/rootResources/GtfsRealtimeApi.java +++ b/transitclockApi/src/main/java/org/transitclock/api/rootResources/GtfsRealtimeApi.java @@ -15,7 +15,7 @@ * Transitime.org . If not, see . */ -package org.transitime.api.rootResources; +package org.transitclock.api.rootResources; import java.io.IOException; import java.io.OutputStream; @@ -30,13 +30,19 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.StreamingOutput; -import org.transitime.api.utils.StandardParameters; -import org.transitime.api.gtfsRealtime.GtfsRtTripFeed; -import org.transitime.api.gtfsRealtime.GtfsRtVehicleFeed; -import org.transitime.feed.gtfsRt.OctalDecoder; +import org.transitclock.api.gtfsRealtime.GtfsRtTripFeed; +import org.transitclock.api.gtfsRealtime.GtfsRtVehicleFeed; +import org.transitclock.api.utils.StandardParameters; +import org.transitclock.config.IntegerConfigValue; +import org.transitclock.feed.gtfsRt.OctalDecoder; import com.google.transit.realtime.GtfsRealtime.FeedMessage; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.servers.Server; +import io.swagger.v3.oas.annotations.servers.Servers; + /** * Contains API commands for the GTFS-realtime API. * @@ -46,8 +52,15 @@ @Path("/key/{key}/agency/{agency}") public class GtfsRealtimeApi { - private final int MAX_GTFS_RT_CACHE_SECS = 15; + private final static int DEFAULT_MAX_GTFS_RT_CACHE_SECS = 15; + + private static IntegerConfigValue gtfsRtCacheSeconds = + new IntegerConfigValue( + "transitclock.api.gtfsRtCacheSeconds", + DEFAULT_MAX_GTFS_RT_CACHE_SECS, + "How long to cache GTFS Realtime"); + /********************** Member Functions **************************/ /** @@ -63,8 +76,10 @@ public class GtfsRealtimeApi { @Path("/command/gtfs-rt/vehiclePositions") @GET @Produces({ MediaType.TEXT_PLAIN, MediaType.APPLICATION_OCTET_STREAM }) + @Operation(summary="GTFS-realtime Vehicle Positions data for all vehicles.",description="Gets real time vehicle position feed. It might be in human readeable format or binary.",tags= {"GTFS","feed"}) public Response getGtfsRealtimeVehiclePositionsFeed( final @BeanParam StandardParameters stdParameters, + @Parameter(description="If specified as human, it will get the output in human readable format. Otherwise will output data in binary format", required=false) @QueryParam(value = "format") String format) throws WebApplicationException { @@ -91,7 +106,7 @@ public void write(OutputStream outputStream) throws IOException, FeedMessage message = GtfsRtVehicleFeed.getPossiblyCachedMessage( stdParameters.getAgencyId(), - MAX_GTFS_RT_CACHE_SECS); + gtfsRtCacheSeconds.getValue()); // Output in human readable format or in standard binary // format @@ -117,7 +132,7 @@ public void write(OutputStream outputStream) throws IOException, } /** - * For getting GTFS-realtime Vehicle Positions data for all vehicles. + * For getting GTFS-realtime for all trips. * * @param stdParameters * @param format @@ -129,8 +144,11 @@ public void write(OutputStream outputStream) throws IOException, @Path("/command/gtfs-rt/tripUpdates") @GET @Produces({ MediaType.TEXT_PLAIN, MediaType.APPLICATION_OCTET_STREAM }) + @Operation(summary="GTFS-realtime trip data.",description="Gets real time trip feed. It might be in human readeable format or binary.",tags= {"GTFS","feed"}) + public Response getGtfsRealtimeTripFeed( final @BeanParam StandardParameters stdParameters, + @Parameter(description="If specified as human, it will get the output in human readable format. Otherwise will output data in binary format", required=false) @QueryParam(value = "format") String format) throws WebApplicationException { @@ -157,7 +175,7 @@ public void write(OutputStream outputStream) throws IOException, FeedMessage message = GtfsRtTripFeed.getPossiblyCachedMessage( stdParameters.getAgencyId(), - MAX_GTFS_RT_CACHE_SECS); + gtfsRtCacheSeconds.getValue()); // Output in human readable format or in standard binary // format diff --git a/transitclockApi/src/main/java/org/transitclock/api/rootResources/ReportingApi.java b/transitclockApi/src/main/java/org/transitclock/api/rootResources/ReportingApi.java new file mode 100644 index 000000000..f6fa1fe00 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/rootResources/ReportingApi.java @@ -0,0 +1,555 @@ +package org.transitclock.api.rootResources; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import org.apache.commons.lang3.StringUtils; +import org.transitclock.api.data.*; +import org.transitclock.api.data.reporting.OnTimePerformanceOutput; +import org.transitclock.api.data.reporting.RouteRunTimeOutput; +import org.transitclock.api.data.reporting.StopPathRunTimeOutput; +import org.transitclock.api.data.reporting.TripRunTimeOutput; +import org.transitclock.api.data.reporting.chartjs.ChartType; +import org.transitclock.api.data.ApiDispatcher; +import org.transitclock.api.utils.StandardParameters; +import org.transitclock.api.utils.WebUtils; +import org.transitclock.core.ServiceType; +import org.transitclock.ipc.data.*; +import org.transitclock.ipc.interfaces.ReportingInterface; +import org.transitclock.ipc.interfaces.VehiclesInterface; + +import javax.ws.rs.*; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.util.Collection; +import java.util.List; + +/** + * Contains the API commands for the Transitime API for getting data for reports. + *

    + * The data output can be in either JSON or XML. The output format is specified + * by the accept header or by using the query string parameter "format=json" or + * "format=xml". + * + * @author carabalb + * + */ +@Path("/key/{key}/agency/{agency}") +public class ReportingApi { + + @Path("/report/chartjs/onTimePerformanceByRoute") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Gets arrival / departures for date range and route", + description="Retrives a list of arrival departures for a specified date range " + + "Optionally can be filered accorditn to routesIdOrShortNames params." + + "Every trip is associated with a block.",tags= {"prediction","trip","block","route","vehicle"}) + public Response getArrivalDeparturesByRoute( + @BeanParam StandardParameters stdParameters, + @Parameter(description="Begin date to use for retrieving arrival departures",required=true) + @QueryParam(value = "beginDate") DateParam beginDate, + @Parameter(description="End date to use for retrieving arrival departures",required=true) + @QueryParam(value = "endDate") DateParam endDate, + @Parameter(description="Begin time of time-band to use for retrieving arrival departures",required=true) + @QueryParam(value = "beginTime") TimeParam beginTime, + @Parameter(description="End time of time-band to use for retrieving arrival departures",required=true) + @QueryParam(value = "endTime") TimeParam endTime, + @Parameter(description="if set, retrives only arrivalDepartures belonging to the route name specified.",required=false) + @QueryParam(value = "r") String route, + @Parameter(description="Retrives only arrivalDepartures belonging to the headsign specified.",required=false) + @QueryParam(value = "headSign") String headsign, + @Parameter(description="Begin date to use for retrieving arrival departures",required=false) + @QueryParam(value = "minEarlyMSec") @DefaultValue("90000") int minEarlyMSec, + @Parameter(description="Begin date to use for retrieving arrival departures",required=false) + @QueryParam(value = "minLateMSec") @DefaultValue("150000") int minLateMSec, + @Parameter(description="if set, retrives only arrivalDepartures belonging to the serviceType (Weekday, Saturday,Sunday)",required=false) + @QueryParam(value = "serviceType") String serviceType, + @Parameter(description="if set, retrives only arrivalDepartures with stops that are timePoints",required=false) + @QueryParam(value = "timePointsOnly") @DefaultValue("false") boolean timePointsOnly, + @Parameter(description="Specify chart data type where applicable.",required=false) + @QueryParam(value = "chartType") @DefaultValue("pie") String chartType) + throws WebApplicationException { + + // Make sure request is valid + stdParameters.validate(); + + try { + // Get active block data from server + ReportingInterface reportingInterface = + stdParameters.getReportingInterface(); + + ServiceType serviceTypeEnum = null; + + if(StringUtils.isNotBlank(serviceType)){ + serviceTypeEnum = ServiceType.valueOf(serviceType.toUpperCase()); + } + + List arrivalDepartures = reportingInterface.getArrivalsDeparturesForOtp( + beginDate.getDate(), endDate.getDate(), beginTime.getTime(), endTime.getTime(), route, + serviceTypeEnum, timePointsOnly, headsign,false); + + Object response = null; + + if(arrivalDepartures != null){ + if(ChartType.valueOf(chartType.toUpperCase()).equals(ChartType.PIE)){ + response = OnTimePerformanceOutput.getOnTimePerformanceForRoutesPieChart(arrivalDepartures, minEarlyMSec, minLateMSec); + } + } + + return stdParameters.createResponse(response); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + + } + + @Path("/report/speedmap/stops") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Gets arrival / departures for date range and route", + description="Retrives a list of arrival departures for a specified date range " + + "Optionally can be filered accorditn to routesIdOrShortNames params." + + "Every trip is associated with a block.",tags= {"prediction","trip","block","route","vehicle"}) + public Response getSpeedMapStops( + @BeanParam StandardParameters stdParameters, + @Parameter(description="Begin date to use for retrieving arrival departures",required=true) + @QueryParam(value = "beginDate") DateParam beginDate, + @Parameter(description="End date to use for retrieving arrival departures",required=true) + @QueryParam(value = "endDate") DateParam endDate, + @Parameter(description="Begin time of time-band to use for retrieving arrival departures",required=true) + @QueryParam(value = "beginTime") TimeParam beginTime, + @Parameter(description="End time of time-band to use for retrieving arrival departures",required=true) + @QueryParam(value = "endTime") TimeParam endTime, + @Parameter(description="Retrives only arrivalDepartures belonging to the route name specified.",required=true) + @QueryParam(value = "r") String route, + @Parameter(description="Retrives only arrivalDepartures belonging to the headsign specified.",required=true) + @QueryParam(value = "headsign") String headsign, + @Parameter(description="if set, retrives only arrivalDepartures belonging to the serviceType (Weekday, Saturday,Sunday)",required=false) + @QueryParam(value = "serviceType") String serviceType, + @Parameter(description="if set, retrives only arrivalDepartures with stops that are timePoints",required=false) + @QueryParam(value = "timePointsOnly") @DefaultValue("false") boolean timePointsOnly) + throws WebApplicationException { + + // Make sure request is valid + stdParameters.validate(); + + try { + // Get active block data from server + ReportingInterface reportingInterface = + stdParameters.getReportingInterface(); + + ServiceType serviceTypeEnum = null; + + if(StringUtils.isNotBlank(serviceType)){ + serviceTypeEnum = ServiceType.valueOf(serviceType.toUpperCase()); + } + + List stopsWithDwellTime = reportingInterface.getStopsWithAvgDwellTimes( + beginDate.getDate(), endDate.getDate(), beginTime.getTime(), endTime.getTime(), route, + serviceTypeEnum, timePointsOnly, headsign, false); + + Object response = null; + + if(stopsWithDwellTime != null){ + ApiStopsWithDwellTime apiStopsWithDwellTime = new ApiStopsWithDwellTime(stopsWithDwellTime); + response = apiStopsWithDwellTime; + } + return stdParameters.createResponse(response); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + + } + + @Path("/report/speedmap/stopPathsSpeed") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Gets arrival / departures for date range and route", + description="Retrives a list of arrival departures for a specified date range " + + "Optionally can be filered accorditn to routesIdOrShortNames params." + + "Every trip is associated with a block.",tags= {"prediction","trip","block","route","vehicle"}) + public Response getStopPathsSpeed( + @BeanParam StandardParameters stdParameters, + @Parameter(description="Begin date to use for retrieving arrival departures",required=true) + @QueryParam(value = "beginDate") DateParam beginDate, + @Parameter(description="End date to use for retrieving arrival departures",required=true) + @QueryParam(value = "endDate") DateParam endDate, + @Parameter(description="Begin time of time-band to use for retrieving arrival departures",required=true) + @QueryParam(value = "beginTime") TimeParam beginTime, + @Parameter(description="End time of time-band to use for retrieving arrival departures",required=true) + @QueryParam(value = "endTime") TimeParam endTime, + @Parameter(description="Retrives only arrivalDepartures belonging to the route name specified.",required=true) + @QueryParam(value = "r") String route, + @Parameter(description="Retrives only arrivalDepartures belonging to the headsign specified.",required=true) + @QueryParam(value = "headsign") String headsign, + @Parameter(description="if set, retrives only arrivalDepartures belonging to the serviceType (Weekday, Saturday,Sunday)",required=false) + @QueryParam(value = "serviceType") String serviceType) + throws WebApplicationException { + + // Make sure request is valid + stdParameters.validate(); + + try { + // Get active block data from server + ReportingInterface reportingInterface = + stdParameters.getReportingInterface(); + + ServiceType serviceTypeEnum = null; + if(StringUtils.isNotBlank(serviceType)){ + serviceTypeEnum = ServiceType.valueOf(serviceType.toUpperCase()); + } + + List stopPaths = reportingInterface.getStopPathsWithSpeed( + beginDate.getDate(), endDate.getDate(), beginTime.getTime(), endTime.getTime(), + route, serviceTypeEnum, headsign, false); + + Object response = null; + + ApiStopPathsWithSpeed apiStopPathsWithSpeed = new ApiStopPathsWithSpeed(stopPaths); + response = apiStopPathsWithSpeed; + + return stdParameters.createResponse(response); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + + } + + @Path("/report/speedmap/runTime") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Gets arrival / departures for date range and route", + description="Retrives a list of arrival departures for a specified date range " + + "Optionally can be filered accorditn to routesIdOrShortNames params." + + "Every trip is associated with a block.",tags= {"prediction","trip","block","route","vehicle"}) + public Response getSpeedMapRunTime( + @BeanParam StandardParameters stdParameters, + @Parameter(description="Begin date to use for retrieving arrival departures",required=true) + @QueryParam(value = "beginDate") DateParam beginDate, + @Parameter(description="End date to use for retrieving arrival departures",required=true) + @QueryParam(value = "endDate") DateParam endDate, + @Parameter(description="Begin time of time-band to use for retrieving arrival departures",required=true) + @QueryParam(value = "beginTime") TimeParam beginTime, + @Parameter(description="End time of time-band to use for retrieving arrival departures",required=true) + @QueryParam(value = "endTime") TimeParam endTime, + @Parameter(description="Retrives only arrivalDepartures belonging to the route name specified.",required=true) + @QueryParam(value = "r") String route, + @Parameter(description="Retrives only arrivalDepartures belonging to the headsign specified.",required=true) + @QueryParam(value = "headsign") String headsign, + @Parameter(description="if set, retrives only arrivalDepartures belonging to the serviceType (Weekday, Saturday,Sunday)",required=false) + @QueryParam(value = "serviceType") String serviceType) + throws WebApplicationException { + + // Make sure request is valid + stdParameters.validate(); + + try { + // Get active block data from server + ReportingInterface reportingInterface = + stdParameters.getReportingInterface(); + + ServiceType serviceTypeEnum = null; + + if(StringUtils.isNotBlank(serviceType)){ + serviceTypeEnum = ServiceType.valueOf(serviceType.toUpperCase()); + } + + IpcDoubleSummaryStatistics summaryStatistics = reportingInterface.getAverageRunTime( + beginDate.getDate(), endDate.getDate(), beginTime.getTime(), endTime.getTime(), + route, serviceTypeEnum, false, headsign, false); + + Object response = null; + + ApiAverageRunTime apiAverageRunTime = new ApiAverageRunTime(summaryStatistics); + response = apiAverageRunTime; + + return stdParameters.createResponse(response); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + + } + + @Path("/report/runTime/avgRunTime") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Gets arrival / departures for date range and route", + description="Retrives a list of arrival departures for a specified date range " + + "Optionally can be filered accorditn to routesIdOrShortNames params." + + "Every trip is associated with a block.",tags= {"prediction","trip","block","route","vehicle"}) + public Response getRunTimeAvgRunTime( + @BeanParam StandardParameters stdParameters, + @Parameter(description="Begin date to use for retrieving arrival departures",required=true) + @QueryParam(value = "beginDate") DateParam beginDate, + @Parameter(description="End date to use for retrieving arrival departures",required=true) + @QueryParam(value = "endDate") DateParam endDate, + @Parameter(description="Begin time of time-band to use for retrieving arrival departures",required=true) + @QueryParam(value = "beginTime") TimeParam beginTime, + @Parameter(description="End time of time-band to use for retrieving arrival departures",required=true) + @QueryParam(value = "endTime") TimeParam endTime, + @Parameter(description="Retrives only arrivalDepartures belonging to the route name specified.",required=true) + @QueryParam(value = "r") String route, + @Parameter(description="Retrives only arrivalDepartures belonging to the headsign specified.",required=true) + @QueryParam(value = "headsign") String headsign, + @Parameter(description="Specifies to the starting stop Id for the trips to filter by.",required=true) + @QueryParam(value = "startStop") String startStop, + @Parameter(description="Specifies to the ending stop Id for the trips to filter by.",required=true) + @QueryParam(value = "endStop") String endStop, + @Parameter(description="if set, retrives only arrivalDepartures belonging to the serviceType (Weekday, Saturday,Sunday)",required=false) + @QueryParam(value = "serviceType") String serviceType, + @Parameter(description="if set, retrives only arrivalDepartures with stops that are timePoints",required=false) + @QueryParam(value = "timePointsOnly") @DefaultValue("false") boolean timePointsOnly) + throws WebApplicationException { + + // Make sure request is valid + stdParameters.validate(); + + try { + // Get active block data from server + ReportingInterface reportingInterface = + stdParameters.getReportingInterface(); + + ServiceType serviceTypeEnum = null; + + if (StringUtils.isNotBlank(serviceType)) { + serviceTypeEnum = ServiceType.valueOf(serviceType.toUpperCase()); + } + + IpcRunTime ipcRunTime = reportingInterface.getRunTimeSummary(beginDate.getDate(), endDate.getDate(), + beginTime.getTime(), endTime.getTime(), route, headsign, startStop, endStop, + serviceTypeEnum, timePointsOnly, false, false); + + Object response = null; + + ApiRunTimeSummary apiRunTimeSummary = new ApiRunTimeSummary(ipcRunTime); + response = apiRunTimeSummary; + + return stdParameters.createResponse(response); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + } + + @Path("/report/runTime/avgTripRunTimes") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Gets arrival / departures for date range and route", + description="Retrives a list of arrival departures for a specified date range " + + "Optionally can be filered accorditn to routesIdOrShortNames params." + + "Every trip is associated with a block.",tags= {"prediction","trip","block","route","vehicle"}) + public Response getRunTimeTripAvgRunTime( + @BeanParam StandardParameters stdParameters, + @Parameter(description="Begin date to use for retrieving arrival departures",required=true) + @QueryParam(value = "beginDate") DateParam beginDate, + @Parameter(description="End date to use for retrieving arrival departures",required=true) + @QueryParam(value = "endDate") DateParam endDate, + @Parameter(description="Begin time of time-band to use for retrieving arrival departures") + @QueryParam(value = "beginTime") TimeParam beginTime, + @Parameter(description="End time of time-band to use for retrieving arrival departures") + @QueryParam(value = "endTime") TimeParam endTime, + @Parameter(description="Retrives only arrivalDepartures belonging to the route name specified.",required=true) + @QueryParam(value = "r") String route, + @Parameter(description="Retrives only arrivalDepartures belonging to the headsign specified.",required=true) + @QueryParam(value = "headsign") String headsign, + @Parameter(description="Specifies the tripPatternId to filter by.") + @QueryParam(value = "tripPattern") String tripPatternId, + @Parameter(description="if set, retrives only arrivalDepartures belonging to the serviceType (Weekday, Saturday,Sunday)") + @QueryParam(value = "serviceType") String serviceType, + @Parameter(description="if set, retrives only arrivalDepartures with stops that are timePoints") + @QueryParam(value = "timePointsOnly") @DefaultValue("false") boolean timePointsOnly) + throws WebApplicationException { + + // Make sure request is valid + stdParameters.validate(); + + try { + // Get active block data from server + ReportingInterface reportingInterface = + stdParameters.getReportingInterface(); + + ServiceType serviceTypeEnum = null; + if(StringUtils.isNotBlank(serviceType)){ + serviceTypeEnum = ServiceType.valueOf(serviceType.toUpperCase()); + } + + List ipcRunTimeTrips = reportingInterface.getRunTimeForTrips(beginDate.getDate(), + endDate.getDate(), + beginTime.getTime(), + endTime.getTime(), + route, + headsign, + tripPatternId, + serviceTypeEnum, + timePointsOnly, + false, + true); + + Object response = TripRunTimeOutput.getRunTimes(ipcRunTimeTrips); + + return stdParameters.createResponse(response); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + + } + + @Path("/report/runTime/avgStopPathRunTimes") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Gets arrival / departures for date range and route", + description="Retrives a list of arrival departures for a specified date range " + + "Optionally can be filered according to routesIdOrShortNames params." + + "Every trip is associated with a block.",tags= {"prediction","trip","block","route","vehicle"}) + public Response getAvgStopPathRunTimes( + @BeanParam StandardParameters stdParameters, + @Parameter(description="Begin date to use for retrieving arrival departures",required=true) + @QueryParam(value = "beginDate") DateParam beginDate, + @Parameter(description="End date to use for retrieving arrival departures",required=true) + @QueryParam(value = "endDate") DateParam endDate, + @Parameter(description="Begin time of time-band to use for retrieving arrival departures") + @QueryParam(value = "beginTime") TimeParam beginTime, + @Parameter(description="End time of time-band to use for retrieving arrival departures") + @QueryParam(value = "endTime") TimeParam endTime, + @Parameter(description="Retrives only arrivalDepartures belonging to the route name specified.",required=true) + @QueryParam(value = "r") String route, + @Parameter(description="Specifies the tripId to filter by.",required=true) + @QueryParam(value = "tripId") String tripId, + @Parameter(description="if set, retrives only arrivalDepartures belonging to the serviceType (Weekday, Saturday,Sunday)") + @QueryParam(value = "serviceType") String serviceType, + @Parameter(description="if set, retrives only arrivalDepartures with stops that are timePoints") + @QueryParam(value = "timePointsOnly") @DefaultValue("false") boolean timePointsOnly) + throws WebApplicationException { + + // Make sure request is valid + stdParameters.validate(); + + try { + // Get active block data from server + ReportingInterface reportingInterface = + stdParameters.getReportingInterface(); + + ServiceType serviceTypeEnum = null; + if(StringUtils.isNotBlank(serviceType)){ + serviceTypeEnum = ServiceType.valueOf(serviceType.toUpperCase()); + } + + List ipcRunTimeForStopPaths = reportingInterface.getRunTimeForStopPaths( + beginDate.getDate(), + endDate.getDate(), + beginTime.getTime(), + endTime.getTime(), + route, + tripId, + serviceTypeEnum, + timePointsOnly, + true); + + Object response = StopPathRunTimeOutput.getRunTimes(ipcRunTimeForStopPaths); + + return stdParameters.createResponse(response); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + + } + + @Path("/report/runTime/routeRunTimes") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Gets route run-times for date range", + description="Retrives a list of route run-times for a specified date range " + + "Optionally can be filered according to routesIdOrShortNames params." + + "Every trip is associated with a block.",tags= {"prediction","trip","block","route","vehicle"}) + public Response getRouteRunTimes( + @BeanParam StandardParameters stdParameters, + @Parameter(description="Begin date to use for retrieving run-times",required=true) + @QueryParam(value = "beginDate") DateParam beginDate, + @Parameter(description="End date to use for retrieving run-times",required=true) + @QueryParam(value = "endDate") DateParam endDate, + @Parameter(description="Begin time of time-band to use for retrieving run-times") + @QueryParam(value = "beginTime") TimeParam beginTime, + @Parameter(description="End time of time-band to use for retrieving run-times") + @QueryParam(value = "endTime") TimeParam endTime, + @Parameter(description="if set, retrives only run-times belonging to the serviceType (Weekday, Saturday,Sunday)") + @QueryParam(value = "serviceType") String serviceType, + @Parameter(description="if set, minimum number of seconds that a route has to complete a run ahead of schedule to be considered early") + @QueryParam(value = "minEarlySec") @DefaultValue("120") int minEarlySec, + @Parameter(description="if set, minimum number of seconds that a route has to complete a run after the scheduled time to be considered late") + @QueryParam(value = "minLateSec") @DefaultValue("120") int minLateSec) + throws WebApplicationException { + + // Make sure request is valid + stdParameters.validate(); + + try { + // Get active block data from server + ReportingInterface reportingInterface = + stdParameters.getReportingInterface(); + + ServiceType serviceTypeEnum = null; + if(StringUtils.isNotBlank(serviceType)){ + serviceTypeEnum = ServiceType.valueOf(serviceType.toUpperCase()); + } + + List ipcRunTimeForRoutes = reportingInterface.getRunTimeForRoutes( + beginDate.getDate(), + endDate.getDate(), + beginTime.getTime(), + endTime.getTime(), + serviceTypeEnum, + minEarlySec, + minLateSec, + true); + + Object response = RouteRunTimeOutput.getRunTimes(ipcRunTimeForRoutes); + + return stdParameters.createResponse(response); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + + } + + @Path("/report/live/dispatch") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Gets arrival / departures for date range and route", + description="Retrives a list of arrival departures for a specified date range " + + "Optionally can be filered accorditn to routesIdOrShortNames params." + + "Every trip is associated with a block.",tags= {"prediction","trip","block","route","vehicle"}) + public Response getLiveDispatchView( + @BeanParam StandardParameters stdParameters, + @Parameter(description="if set, formats speed to the specified format (MS,KM,MPH)") + @QueryParam(value = "speedFormat") @DefaultValue("MS") String speedFormat) + throws WebApplicationException { + + // Make sure request is valid + stdParameters.validate(); + + try { + // Convert speedFormat param to Enum + SpeedFormat speedFormatEnum = SpeedFormat.valueOf(speedFormat.toUpperCase()); + + // Get vehicles interface + VehiclesInterface vehiclesInterface = stdParameters.getVehiclesInterface(); + Collection vehicles = vehiclesInterface.get(); + + Object response = null; + ApiDispatcher dispatcher = new ApiDispatcher(vehicles, speedFormatEnum); + response = dispatcher; + + return stdParameters.createResponse(response); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + + } +} diff --git a/transitimeApi/src/main/java/org/transitime/api/rootResources/SiriApi.java b/transitclockApi/src/main/java/org/transitclock/api/rootResources/SiriApi.java similarity index 68% rename from transitimeApi/src/main/java/org/transitime/api/rootResources/SiriApi.java rename to transitclockApi/src/main/java/org/transitclock/api/rootResources/SiriApi.java index 6b0a4f4c1..b70b1fe18 100644 --- a/transitimeApi/src/main/java/org/transitime/api/rootResources/SiriApi.java +++ b/transitclockApi/src/main/java/org/transitclock/api/rootResources/SiriApi.java @@ -15,7 +15,7 @@ * Transitime.org . If not, see . */ -package org.transitime.api.rootResources; +package org.transitclock.api.rootResources; import java.util.ArrayList; import java.util.Collection; @@ -31,15 +31,20 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import org.transitime.api.data.siri.SiriStopMonitoring; -import org.transitime.api.data.siri.SiriVehiclesMonitoring; -import org.transitime.api.utils.StandardParameters; -import org.transitime.api.utils.WebUtils; -import org.transitime.ipc.data.IpcVehicleComplete; -import org.transitime.ipc.data.IpcPrediction; -import org.transitime.ipc.data.IpcPredictionsForRouteStopDest; -import org.transitime.ipc.interfaces.PredictionsInterface; -import org.transitime.ipc.interfaces.VehiclesInterface; +import org.transitclock.api.data.siri.SiriStopMonitoring; +import org.transitclock.api.data.siri.SiriVehiclesMonitoring; +import org.transitclock.api.utils.StandardParameters; +import org.transitclock.api.utils.WebUtils; +import org.transitclock.ipc.data.IpcPrediction; +import org.transitclock.ipc.data.IpcPredictionsForRouteStopDest; +import org.transitclock.ipc.data.IpcVehicleComplete; +import org.transitclock.ipc.interfaces.PredictionsInterface; +import org.transitclock.ipc.interfaces.VehiclesInterface; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.servers.Server; +import io.swagger.v3.oas.annotations.servers.Servers; /** * The Siri API @@ -68,9 +73,13 @@ public class SiriApi { @Path("/command/siri/vehicleMonitoring") @GET @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Returns vehicleMonitoring vehicle information in SIRI format.", + description="It is possible to specify vehicleIds, routeIds, or routeShortNames " + + "to get subset of data. If not specified then vehicle information for entire agency is returned.", + tags= {"SIRI","feed"}) public Response getVehicles(@BeanParam StandardParameters stdParameters, - @QueryParam(value = "v") List vehicleIds, - @QueryParam(value = "r") List routesIdOrShortNames) + @Parameter(description="List of vehicles id", required=false)@QueryParam(value = "v") List vehicleIds, + @Parameter(description="List of routesId or routeShortName", required=false)@QueryParam(value = "r") List routesIdOrShortNames) throws WebApplicationException { // Make sure request is valid stdParameters.validate(); @@ -95,7 +104,7 @@ public Response getVehicles(@BeanParam StandardParameters stdParameters, return stdParameters.createResponse(siriVehicles); } catch (Exception e) { // If problem getting data then return a Bad Request - throw WebUtils.badRequestException(e.getMessage()); + throw WebUtils.badRequestException(e); } } @@ -114,13 +123,18 @@ public Response getVehicles(@BeanParam StandardParameters stdParameters, @Path("/command/siri/stopMonitoring") @GET @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Returns stopMonitoring vehicle information in SIRI format.", + description="It is possible to specify vehicleIds, routeIds, or routeShortNames " + + "to get subset of data. It is possible to specify the number of perdictions per stop." + + " If not specified then vehicle information for entire agency is returned.", + tags= {"SIRI","feed"}) public Response getVehicles( @BeanParam StandardParameters stdParameters, - @QueryParam(value = "r") String routeIdOrShortName, - @QueryParam(value = "s") String stopId, - @QueryParam(value = "numPreds") @DefaultValue("3") int numberPredictions) + @Parameter(description="RoutesId or routeShortName", required=true) @QueryParam(value = "r") String routeIdOrShortName, + @Parameter(description="StopIds", required=true) @QueryParam(value = "s") String stopId, + @Parameter(description="Number of predictions", required=false) @QueryParam(value = "numPreds") @DefaultValue("3") int numberPredictions) throws WebApplicationException { // Make sure request is valid stdParameters.validate(); @@ -156,7 +170,7 @@ public Response getVehicles(@BeanParam StandardParameters stdParameters, return stdParameters.createResponse(siriStopMonitoring); } catch (Exception e) { // If problem getting data then return a Bad Request - throw WebUtils.badRequestException(e.getMessage()); + throw WebUtils.badRequestException(e); } } diff --git a/transitclockApi/src/main/java/org/transitclock/api/rootResources/TimeParam.java b/transitclockApi/src/main/java/org/transitclock/api/rootResources/TimeParam.java new file mode 100755 index 000000000..c546175ec --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/rootResources/TimeParam.java @@ -0,0 +1,27 @@ +package org.transitclock.api.rootResources; + +import javax.ws.rs.WebApplicationException; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; + + +public class TimeParam { + + private LocalTime time; + + public TimeParam(String in) throws WebApplicationException { + try { + time=LocalTime.parse(in, DateTimeFormatter.ISO_TIME); + } + catch (Exception exception) { + throw new WebApplicationException(400); + } + } + public LocalTime getTime() { + return time; + } + public String format() { + return time.toString(); + } + +} \ No newline at end of file diff --git a/transitclockApi/src/main/java/org/transitclock/api/rootResources/TransitimeApi.java b/transitclockApi/src/main/java/org/transitclock/api/rootResources/TransitimeApi.java new file mode 100644 index 000000000..8c4b0a739 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/rootResources/TransitimeApi.java @@ -0,0 +1,1989 @@ +/* + * 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.api.rootResources; + +import java.rmi.RemoteException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.ws.rs.BeanParam; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.apache.commons.lang3.StringUtils; +import org.transitclock.api.data.*; +import org.transitclock.api.predsByLoc.PredsByLoc; +import org.transitclock.api.utils.StandardParameters; +import org.transitclock.api.utils.WebUtils; +import org.transitclock.core.TemporalDifference; +import org.transitclock.db.structs.Agency; +import org.transitclock.db.structs.Location; +import org.transitclock.ipc.data.IpcActiveBlock; +import org.transitclock.ipc.data.IpcBlock; +import org.transitclock.ipc.data.IpcCalendar; +import org.transitclock.ipc.data.IpcDirectionsForRoute; +import org.transitclock.ipc.data.IpcPrediction; +import org.transitclock.ipc.data.IpcPredictionsForRouteStopDest; +import org.transitclock.ipc.data.IpcRevisionInformation; +import org.transitclock.ipc.data.IpcRoute; +import org.transitclock.ipc.data.IpcRouteSummary; +import org.transitclock.ipc.data.IpcSchedule; +import org.transitclock.ipc.data.IpcServerStatus; +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.data.IpcVehicleConfig; +import org.transitclock.ipc.interfaces.ConfigInterface; +import org.transitclock.ipc.interfaces.PredictionsInterface; +import org.transitclock.ipc.interfaces.RevisionInformationInterface; +import org.transitclock.ipc.interfaces.ServerStatusInterface; +import org.transitclock.ipc.interfaces.VehiclesInterface; + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.info.Info; +import io.swagger.v3.oas.annotations.servers.Server; +import io.swagger.v3.oas.annotations.servers.ServerVariable; +import io.swagger.v3.oas.annotations.servers.Servers; + +//import io.swagger.annotations.Api; +//import io.swagger.annotations.ApiOperation; + +import org.transitclock.ipc.interfaces.PredictionsInterface.RouteStop; + + +/** + * Contains the API commands for the Transitime API for getting real-time + * vehicle and prediction information plus the static configuration information. + * The intent of this feed is to provide what is needed for creating a user + * interface application, such as a smartphone application. + *

    + * The data output can be in either JSON or XML. The output format is specified + * by the accept header or by using the query string parameter "format=json" or + * "format=xml". + * + * @author SkiBu Smith + * + */ +@OpenAPIDefinition(info = @Info(title = "TrasnsitClockAPI", version = "1.0", +description = "TheTransitClock is an open source transit information system." + + " It’s core function is to provide and analyze arrival predictions for transit " + + "systems.
    Here you will find the detailed description of The Transit Clock API.
    " + + "For more information visit thetransitclock.github.io.
    " + + "The original documentation can be found in Api doc." +),servers= {@Server(url="/api/v1")}) +@Path("/key/{key}/agency/{agency}") +public class TransitimeApi { + + /** + * Handles the "vehicles" command. Returns data for all vehicles or for the + * vehicles specified via the query string. + *

    + * A Response object is returned instead of a regular object so that can + * have one method for the both XML and JSON yet always return the proper + * media type even if it is configured via the query string "format" + * parameter as opposed to the accept header. + * + * @param stdParameters + * StdParametersBean that gets the standard parameters from the + * URI, query string, and headers. + * @param vehicleIds + * Optional way of specifying which vehicles to get data for + * @param routesIdOrShortNames + * Optional way of specifying which routes to get data for + * @param stopId + * Optional way of specifying a stop so can get predictions for + * routes and determine which vehicles are the ones generating + * the predictions. The other vehicles are labeled as minor so + * they can be drawn specially in the UI. + * @param numberPredictions + * For when determining which vehicles are generating the + * predictions so can label minor vehicles + * @return The Response object already configured for the specified media + * type. + */ + @Operation(summary="Returns data for all vehicles or for the vehicles specified via the query string.", + description="Returns data for all vehicles or for the vehicles specified via the query string.",tags= {"vehicle","prediction"}) + @Path("/command/vehicles") + + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + public Response getVehicles( + @BeanParam StandardParameters stdParameters, + @Parameter(description="Vehicles is list.") @QueryParam(value = "v") List vehicleIds, + @Parameter(description="Specifies which vehicles to get data for.",required=false) @QueryParam(value = "r") List routesIdOrShortNames, + @Parameter(description="Specifies a stop so can get predictions for" + + " routes and determine which vehicles are the ones generating the predictions. " + + "The other vehicles are labeled as minor so they can be drawn specially in the UI.",required=false) + @QueryParam(value = "s") String stopId, + @Parameter(description="Number of predictions to show.", required=false) @QueryParam(value = "numPreds") @DefaultValue("2") int numberPredictions) throws WebApplicationException { + // Make sure request is valid + stdParameters.validate(); + + try { + // Get Vehicle data from server + VehiclesInterface inter = stdParameters.getVehiclesInterface(); + + Collection vehicles; + if (!routesIdOrShortNames.isEmpty() && !routesIdOrShortNames.get(0).trim().isEmpty()) { + vehicles = inter.getForRoute(routesIdOrShortNames); + } else if (!vehicleIds.isEmpty() && !vehicleIds.get(0).trim().isEmpty()) { + vehicles = inter.get(vehicleIds); + } else { + vehicles = inter.get(); + } + + // If the vehicles doesn't exist then throw exception such that + // Bad Request with an appropriate message is returned. + if (vehicles == null) + throw WebUtils.badRequestException("Invalid specifier for " + "vehicles"); + + // To determine how vehicles should be drawn in UI. If stop + // specified + // when getting vehicle info then only the vehicles being predicted + // for, should be highlighted. The others should be dimmed. + Map uiTypesForVehicles = determineUiModesForVehicles(vehicles, stdParameters, + routesIdOrShortNames, stopId, numberPredictions); + + ApiVehicles apiVehicles = new ApiVehicles(vehicles, uiTypesForVehicles); + + // return ApiVehicles response + return stdParameters.createResponse(apiVehicles); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + } + + /** + * Handles the vehicleIds command. Returns list of vehicle IDs. + * + * @param stdParameters + * @return + * @throws WebApplicationException + */ + @Path("/command/vehicleIds") + @Operation(summary="Gets the list of vehicles Id", tags= {"vehicle"}) + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + public Response getVehicleIds(@BeanParam StandardParameters stdParameters) throws WebApplicationException { + // Make sure request is valid + stdParameters.validate(); + + try { + // Get Vehicle data from server + ConfigInterface inter = stdParameters.getConfigInterface(); + List ids = inter.getVehicleIds(); + + ApiIds apiIds = new ApiIds(ids); + return stdParameters.createResponse(apiIds); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + } + + @Path("/command/vehicleLocation") + @GET + @Operation(summary="It gets the location for the specified vehicle.",description="It gets the location for the specified vehicle.", + tags= {"vehicle"}) + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + public Response getVehicleLocation(@BeanParam StandardParameters stdParameters, + @Parameter(description="Specifies the vehicle from which to get the location from.",required=true) + @QueryParam(value = "v") String vehicleId) throws WebApplicationException { + try { + + // Get Vehicle data from server + VehiclesInterface inter = stdParameters.getVehiclesInterface(); + IpcVehicle vehicle = inter.get(vehicleId); + if (vehicle == null) { + throw WebUtils.badRequestException("Invalid specifier for " + + "vehicle"); + } + + Location matchedLocation = new Location(vehicle.getPredictedLatitude(), vehicle.getPredictedLongitude()); + return stdParameters.createResponse(matchedLocation.toString()); + } catch (Exception e) { + throw WebUtils.badRequestException(e); + } + } + + /** + * Handles the "vehiclesDetails" command. Returns detailed data for all + * vehicles or for the vehicles specified via the query string. This data + * includes things not necessarily intended for the public, such as schedule + * adherence and driver IDs. + *

    + * A Response object is returned instead of a regular object so that can + * have one method for the both XML and JSON yet always return the proper + * media type even if it is configured via the query string "format" + * parameter as opposed to the accept header. + * + * @param stdParameters + * StdParametersBean that gets the standard parameters from the + * URI, query string, and headers. + * @param vehicleIds + * Optional way of specifying which vehicles to get data for + * @param routesIdOrShortNames + * Optional way of specifying which routes to get data for + * @param stopId + * Optional way of specifying a stop so can get predictions for + * routes and determine which vehicles are the ones generating + * the predictions. The other vehicles are labeled as minor so + * they can be drawn specially in the UI. + * @param numberPredictions + * For when determining which vehicles are generating the + * predictions so can label minor vehicles + * @return The Response object already configured for the specified media + * type. + */ + @Path("/command/vehiclesDetails") + @GET + @Operation(summary="Returns detailed data for all " + + "vehicles or for the vehicles specified via the query string", + description="Returns detailed data for all" + + " vehicles or for the vehicles specified via the query string. This data " + + " includes things not necessarily intended for the public, such as schedule" + + " adherence and driver IDs.",tags= {"vehicle"}) + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + public Response getVehiclesDetails(@BeanParam StandardParameters stdParameters, + @Parameter(description="Specifies which vehicles to get data for",required=false) + @QueryParam(value = "v") List vehicleIds, + @Parameter(description="Specifies which routes to get data for",required=false) + @QueryParam(value = "r") List routesIdOrShortNames, + @Parameter(description="Specifies a stop so can get predictions for" + + " routes and determine which vehicles are the ones generating" + + " the predictions. The other vehicles are labeled as minor so" + + " they can be drawn specially in the UI. ",required=false) + @QueryParam(value = "s") String stopId, + @Parameter(description=" For when determining which vehicles are generating the" + + "predictions so can label minor vehicles",required=false)@QueryParam(value = "numPreds") + @DefaultValue("3") int numberPredictions + , + @Parameter(description=" Return only assigned vehicles",required=false)@QueryParam(value = "onlyAssigned") + @DefaultValue("false") boolean onlyAssigned + + ) throws WebApplicationException { + // Make sure request is valid + stdParameters.validate(); + + try { + // Get Vehicle data from server + VehiclesInterface inter = stdParameters.getVehiclesInterface(); + + Collection vehicles; + if (!routesIdOrShortNames.isEmpty() && !routesIdOrShortNames.get(0).trim().isEmpty()) { + vehicles = inter.getForRoute(routesIdOrShortNames); + } else if (!vehicleIds.isEmpty() && !vehicleIds.get(0).trim().isEmpty()) { + vehicles = inter.get(vehicleIds); + } else { + vehicles = inter.get(); + } + + // If the vehicles doesn't exist then throw exception such that + // Bad Request with an appropriate message is returned. + if (vehicles == null) + throw WebUtils.badRequestException("Invalid specifier for vehicles"); + + // To determine how vehicles should be drawn in UI. If stop + // specified + // when getting vehicle info then only the vehicles being predicted + // for, should be highlighted. The others should be dimmed. + Map uiTypesForVehicles = determineUiModesForVehicles(vehicles, stdParameters, + routesIdOrShortNames, stopId, numberPredictions); + + // Convert IpcVehiclesDetails to ApiVehiclesDetails + ApiVehiclesDetails apiVehiclesDetails = new ApiVehiclesDetails(vehicles, stdParameters.getAgencyId(), + uiTypesForVehicles,onlyAssigned); + + // return ApiVehiclesDetails response + Response result = null; + try { + result = stdParameters.createResponse(apiVehiclesDetails); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + return result; + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + + } + } + + // For specifying how vehicles should be drawn in the UI. + public enum UiMode { + NORMAL, SECONDARY, MINOR + }; + + /** + * Gets information including vehicle IDs for all vehicles that have been + * configured. Useful for creating a vehicle selector. + * + * @param stdParameters + * @return + * @throws WebApplicationException + */ + @Path("/command/vehicleConfigs") + @GET + @Operation(summary="Returns a list of vehilces with its configurarion.", + description="Returns a list of vehicles coniguration which inclides description, capacity, type and crushCapacity.", + tags= {"vehicle"}) + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + public Response getVehicleConfigs(@BeanParam StandardParameters stdParameters) throws WebApplicationException { + // Make sure request is valid + stdParameters.validate(); + + try { + // Get Vehicle data from server + VehiclesInterface inter = stdParameters.getVehiclesInterface(); + Collection ipcVehicleConfigs = inter.getVehicleConfigs(); + ApiVehicleConfigs apiVehicleConfigs = new ApiVehicleConfigs(ipcVehicleConfigs); + + // return ApiVehiclesDetails response + return stdParameters.createResponse(apiVehicleConfigs); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + } + + @Path("/command/stopLevel") + @GET + @Produces(MediaType.APPLICATION_JSON) + public Response getStopLevelDetail(@BeanParam StandardParameters stdParameters) throws WebApplicationException { + // Make sure request is valid + stdParameters.validate(); + try { + ApiStopLevels apiStopLevels = new ApiStopLevels(stdParameters); + return stdParameters.createResponse(apiStopLevels); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + + } + /** + * Determines Map of UiTypes for vehicles so that the vehicles can be drawn + * correctly in the UI. If when getting vehicles no specific route and stop + * were specified then want to highlight all vehicles. Therefore for this + * situation all vehicle IDs will be mapped to UiType.NORMAL. + *

    + * But if route and stop were specified then the first vehicle predicted for + * at the specified stop should be UiType.NORMAL, the subsequent ones are + * set to UiType.SECONDARY, and the remaining vehicles are set to + * UiType.MINOR. + * + * @param vehicles + * @param stdParameters + * @param routesIdOrShortNames + * @param stopId + * @param numberPredictions + * @return + * @throws RemoteException + */ + private static Map determineUiModesForVehicles(Collection vehicles, + StandardParameters stdParameters, List routesIdOrShortNames, String stopId, int numberPredictions) + throws RemoteException { + // Create map and initialize all vehicles to NORMAL UI mode + Map modeMap = new HashMap(); + + if (routesIdOrShortNames.isEmpty() || stopId == null) { + // Stop not specified so simply return NORMAL type for all vehicles + for (IpcVehicle ipcVehicle : vehicles) { + modeMap.put(ipcVehicle.getId(), UiMode.NORMAL); + } + } else { + // Stop specified so get predictions and set UI type accordingly + List vehiclesGeneratingPreds = determineVehiclesGeneratingPreds(stdParameters, routesIdOrShortNames, + stopId, numberPredictions); + for (IpcVehicle ipcVehicle : vehicles) { + UiMode uiType = UiMode.MINOR; + if (!vehiclesGeneratingPreds.isEmpty() && ipcVehicle.getId().equals(vehiclesGeneratingPreds.get(0))) + uiType = UiMode.NORMAL; + else if (vehiclesGeneratingPreds.contains(ipcVehicle.getId())) + uiType = UiMode.SECONDARY; + + modeMap.put(ipcVehicle.getId(), uiType); + } + + } + + // Return results + return modeMap; + } + + /** + * Provides just a list of vehicle IDs of the vehicles generating + * predictions for the specified stop. The list of vehicle IDs will be in + * time order such that the first one will be the next predicted vehicle + * etc. If routeShortNames or stopId not specified then will return empty + * array. + * + * @param stdParameters + * @param routesIdOrShortNames + * @param stopId + * @param numberPredictions + * @return List of vehicle IDs + * @throws RemoteException + */ + private static List determineVehiclesGeneratingPreds(StandardParameters stdParameters, + List routesIdOrShortNames, String stopId, int numberPredictions) throws RemoteException { + // The array of vehicle IDs to be returned + List vehiclesGeneratingPreds = new ArrayList(); + + // If stop specified then also get predictions for the stop to + // determine which vehicles are generating the predictions. + // If vehicle is not one of the ones generating a prediction + // then it is labeled as a minor vehicle for the UI. + if (!routesIdOrShortNames.isEmpty() && stopId != null) { + PredictionsInterface predsInter = stdParameters.getPredictionsInterface(); + List predictions = predsInter.get(routesIdOrShortNames.get(0), stopId, + numberPredictions); + + // Determine set of which vehicles predictions generated for + for (IpcPredictionsForRouteStopDest predsForRouteStop : predictions) { + for (IpcPrediction ipcPrediction : predsForRouteStop.getPredictionsForRouteStop()) { + vehiclesGeneratingPreds.add(ipcPrediction.getVehicleId()); + } + } + } + + return vehiclesGeneratingPreds; + } + + /** + * Handles "predictions" command. Gets predictions from server and returns + * the corresponding response. + *

    + * A Response object is returned instead of a regular object so that can + * have one method for the both XML and JSON yet always return the proper + * media type even if it is configured via the query string "format" + * parameter as opposed to the accept header. + * + * @param stdParameters + * StdParametersBean that gets the standard parameters from the + * URI, query string, and headers. + * @param routeStopStrs + * List of route/stops to return predictions for. If route not + * specified then data will be returned for all routes for the + * specified stop. The route specifier is the route id or the + * route short name. It is often best to use route short name for + * consistency across configuration changes (route ID is not + * consistent for many agencies). The stop specified can either + * be the stop ID or the stop code. Each route/stop is separated + * by the "|" character so for example the query string could + * have "rs=43|2029&rs=43|3029" + * @param stopStrs + * List of stops to return predictions for. Provides predictions + * for all routes that serve the stop. Can use either stop ID or + * stop code. Can specify multiple stops. + * @param numberPredictions + * Maximum number of predictions to return. Default value is 3. + * @return + * @throws WebApplicationException + */ + @Path("/command/predictions") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Gets predictions from server",tags= {"prediction"}) + public + Response + getPredictions( + @BeanParam StandardParameters stdParameters, + @Parameter(description="List of route/stops to return predictions for. " + + "If route not specified then data will be returned for all routes " + + "for the specified stop. The route specifier is the route id or the route short name. " + + "It is often best to use route short name for " + + "consistency across configuration changes (route ID is not consistent for many agencies). " + + "The stop specified can either be the stop ID or the stop code. " + + "Each route/stop is separated by the \"|\" character so" + + " for example the query string could have \"rs=43|2029&rs=43|3029\"") + @QueryParam(value = "rs") List routeStopStrs, + @Parameter(description="List of stops to return predictions for. Can use either stop ID or stop code.") + @QueryParam(value = "s") List stopStrs, + @Parameter(description="Maximum number of predictions to return.") + @QueryParam(value = "numPreds") @DefaultValue("3") int numberPredictions) + throws WebApplicationException { + // Make sure request is valid + stdParameters.validate(); + + try { + // Get Prediction data from server + PredictionsInterface inter = stdParameters.getPredictionsInterface(); + + // Create list of route/stops that should get predictions for + List routeStopsList = new ArrayList(); + for (String routeStopStr : routeStopStrs) { + // Each route/stop is specified as a single string using "\" + // as a divider (e.g. "routeId|stopId") + String routeStopParams[] = routeStopStr.split("\\|"); + + String routeIdOrShortName; + String stopIdOrCode; + if (routeStopParams.length == 1) { + // Just stop specified + routeIdOrShortName = null; + stopIdOrCode = routeStopParams[0]; + } else { + // Both route and stop specified + routeIdOrShortName = routeStopParams[0]; + stopIdOrCode = routeStopParams[1]; + } + RouteStop routeStop = + new RouteStop(routeIdOrShortName, stopIdOrCode); + routeStopsList.add(routeStop); + } + + // Add to list the stops that should get predictions for + for (String stopStr : stopStrs) { + // Use null for route identifier so get predictions for all + // routes for the stop + RouteStop routeStop = new RouteStop(null, stopStr); + routeStopsList.add(routeStop); + } + + // Actually get the predictions via IPC + List predictions = + inter.get(routeStopsList, numberPredictions); + + // return ApiPredictions response + ApiPredictions predictionsData = new ApiPredictions(predictions); + return stdParameters.createResponse(predictionsData); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + } + + /** + * Handles "predictionsByLoc" command. Gets predictions from server and + * returns the corresponding response. + *

    + * A Response object is returned instead of a regular object so that can + * have one method for the both XML and JSON yet always return the proper + * media type even if it is configured via the query string "format" + * parameter as opposed to the accept header. + * + * @param stdParameters + * StdParametersBean that gets the standard parameters from the + * URI, query string, and headers. + * @param lat + * latitude in decimal degrees + * @param lon + * longitude in decimal degrees + * @param maxDistance + * How far away a stop can be from the lat/lon. Default is 1,500 + * m. + * @param numberPredictions + * Maximum number of predictions to return. Default value is 3. + * @return + * @throws WebApplicationException + */ + @Path("/command/predictionsByLoc") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Gets predictions from server by location",tags= {"prediction"}) + public Response getPredictions(@BeanParam StandardParameters stdParameters, + @Parameter(description="Latitude of the location in decimal degrees.",required=true) + @QueryParam(value = "lat") Double lat, + @Parameter(description="Longitude of the location in decimal degrees.",required=true) + @QueryParam(value = "lon") Double lon, + @Parameter(description="How far away a stop can be from the location (lat/lon).",required=false) + @QueryParam(value = "maxDistance") @DefaultValue("1500.0") double maxDistance, + @Parameter(description="Maximum number of predictions to return.") + @QueryParam(value = "numPreds") @DefaultValue("3") int numberPredictions) throws WebApplicationException { + // Make sure request is valid + stdParameters.validate(); + + if (maxDistance > PredsByLoc.MAX_MAX_DISTANCE) + throw WebUtils.badRequestException("Maximum maxDistance parameter is " + PredsByLoc.MAX_MAX_DISTANCE + + "m but " + maxDistance + "m was specified in the request."); + + try { + // Get Prediction data from server + PredictionsInterface inter = stdParameters.getPredictionsInterface(); + + // Get predictions by location + List predictions = inter.get(new Location(lat, lon), maxDistance, + numberPredictions); + + // return ApiPredictions response + ApiPredictions predictionsData = new ApiPredictions(predictions); + return stdParameters.createResponse(predictionsData); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + } + + /** + * Handles the "routes" command. Returns summary data describing all of the + * routes. Useful for creating a route selector as part of a UI. + * + * @param stdParameters + * @return + * @throws WebApplicationException + */ + @Path("/command/routes") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Gets the list of routes.", description="Gets a list of the existing routes in the server." + + " It might be filtered according to routeId or routeShortName. " + + "If more than one route have the same shortName, it is possible to specify keepDuplicates parameter to show all of them. ", + tags={"base data","route"} ) + public Response getRoutes(@BeanParam StandardParameters stdParameters, + @Parameter(description="List of routeId or routeShortName. Example: r=1&r=2" ,required=false) + @QueryParam(value = "r") List routeIdsOrShortNames, + @Parameter(description="Return all routes when more than one have the same shortName.",required=false) + @QueryParam(value = "keepDuplicates") Boolean keepDuplicates) + throws WebApplicationException { + + // Make sure request is valid + stdParameters.validate(); + + try { + ConfigInterface inter = stdParameters.getConfigInterface(); + + // Get agency info so can also return agency name + List agencies = inter.getAgencies(); + + // Get route data from server + ApiRoutes routesData; + if (routeIdsOrShortNames == null || routeIdsOrShortNames.isEmpty()) { + // Get all routes + List routes = + new ArrayList(inter.getRoutes()); + + // Handle duplicates. If should keep duplicates (where couple + // of routes have the same route_short_name) then modify + // the route name to indicate the different IDs. If should + // ignore duplicates then don't include them in final list + Collection processedRoutes = + new ArrayList(); + for (int i = 0; i < routes.size()-1; ++i) { + IpcRouteSummary route = routes.get(i); + IpcRouteSummary nextRoute = routes.get(i+1); + + // If find a duplicate route_short_name... + if (route.getShortName().equals(nextRoute.getShortName())) { + // Only keep route if supposed to + if (keepDuplicates != null && keepDuplicates) { + // Keep duplicates but change route name + IpcRouteSummary routeWithModifiedName = + new IpcRouteSummary(route, route.getName() + + " (ID=" + route.getId() + ")"); + processedRoutes.add(routeWithModifiedName); + + IpcRouteSummary nextRouteWithModifiedName = + new IpcRouteSummary(nextRoute, + nextRoute.getName() + " (ID=" + + nextRoute.getId() + ")"); + processedRoutes.add(nextRouteWithModifiedName); + + // Since processed both this route and the next + // route can skip to next one + ++i; + } + } else { + // Not a duplicate so simply add it + processedRoutes.add(route); + } + } + // Add the last route + processedRoutes.add(routes.get(routes.size()-1)); + + routesData = new ApiRoutes(processedRoutes, agencies.get(0)); + } else { + // Get specified routes + List ipcRoutes = inter.getRoutes(routeIdsOrShortNames); + routesData = new ApiRoutes(ipcRoutes, agencies.get(0)); + } + + // Create and return response + return stdParameters.createResponse(routesData); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + } + + /** + * Handles the "headsigns" command. Returns summary data describing all of the + * headsigns. Useful for creating a headsign selector as part of a UI. + * + * @param stdParameters + * @return + * @throws WebApplicationException + */ + @Path("/command/headsigns") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Gets the list of headsigns.", description="Gets a list of the existing headsigns in the server." + + " It is filtered according to routeId or routeShortName.", + tags={"base data","headsign"} ) + public Response getHeadsigns(@BeanParam StandardParameters stdParameters, + @Parameter(description="Specifies the routeId or routeShortName." ,required=true) + @QueryParam(value = "r") String routeIdOrShortName) + throws WebApplicationException { + + // Make sure request is valid + stdParameters.validate(); + + try { + ConfigInterface inter = stdParameters.getConfigInterface(); + + // Get agency info so can also return agency name + List agencies = inter.getAgencies(); + + // Get headsigns data from server + ApiHeadsigns headsignsData; + + // Get specified headsigns + List ipcTripPatterns = inter.getTripPatterns(routeIdOrShortName); + headsignsData = new ApiHeadsigns(ipcTripPatterns, agencies.get(0)); + + + // Create and return response + return stdParameters.createResponse(headsignsData); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + } + + /** + * Handles the "routesDetails" command. Provides detailed information for a + * route includes all stops and paths such that it can be drawn in a map. + * + * @param stdParameters + * @param routeIdsOrShortNames + * list of route IDs or route short names. If a single route is + * specified then data for just the single route is returned. If + * no route is specified then data for all routes returned in an + * array. If multiple routes specified then data for those routes + * returned in an array. + * @param stopId + * optional. If set then only this stop and the remaining ones on + * the trip pattern are marked as being for the UI and can be + * highlighted. Useful for when want to emphasize in the UI only + * the stops that are of interest to the user. + * @param directionId + * optional. If set then only the shape for specified direction + * is marked as being for the UI. Needed for situations where a + * single stop is used for both directions of a route and want to + * highlight in the UI only the stops and the shapes that the + * user is actually interested in. + * @param tripPatternId + * optional. If set then only the specified trip pattern is + * marked as being for the UI. + * @return + * @throws WebApplicationException + */ + @Path("/command/routesDetails") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Provides detailed information for a route.", + description="Provides detailed information for a route includes all stops " + + "and paths such that it can be drawn in a map.",tags= {"base data","route"}) + public Response getRouteDetails(@BeanParam StandardParameters stdParameters, + @Parameter(description="List of routeId or routeShortName. Example: r=1&r=2" ,required=false) + @QueryParam(value = "r") List routeIdsOrShortNames, + @Parameter(description="If set then only the shape for specified direction is marked as being for the UI." ,required=false) + @QueryParam(value = "d") String directionId, + @Parameter(description="If set then only this stop and the remaining ones on " + + "the trip pattern are marked as being for the UI and can be " + + "highlighted. Useful for when want to emphasize in the UI only " + + " the stops that are of interest to the user.",required=false) + @QueryParam(value = "s") String stopId, + @Parameter(description="If set then only the specified trip pattern is marked as being for the UI",required=false) + @QueryParam(value = "tripPattern") String tripPatternId) + throws WebApplicationException { + // Make sure request is valid + stdParameters.validate(); + + try { + // Get Vehicle data from server + ConfigInterface inter = stdParameters.getConfigInterface(); + + // Get agency info so can also return agency name + List agencies = inter.getAgencies(); + + List ipcRoutes; + + // If single route specified + if (routeIdsOrShortNames != null && routeIdsOrShortNames.size() == 1) { + String routeIdOrShortName = routeIdsOrShortNames.get(0); + IpcRoute route = inter.getRoute(routeIdOrShortName, directionId, stopId, tripPatternId); + + // If the route doesn't exist then throw exception such that + // Bad Request with an appropriate message is returned. + if (route == null) + throw WebUtils.badRequestException("Route for route=" + routeIdOrShortName + " does not exist."); + + ipcRoutes = new ArrayList<>(); + ipcRoutes.add(route); + } else if(routeIdsOrShortNames == null || routeIdsOrShortNames.isEmpty() && StringUtils.isNotBlank(stopId)){ + ipcRoutes = inter.getRoutesForStop(stopId); + } else { + // Multiple routes specified + ipcRoutes = inter.getRoutes(routeIdsOrShortNames); + } + + // Take the IpcRoute data array and create and return + // ApiRoutesDetails object + ApiRoutesDetails routeData = + new ApiRoutesDetails(ipcRoutes, agencies.get(0)); + return stdParameters.createResponse(routeData); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + } + + /** + * Handles the "stops" command. Returns all stops associated with a route, + * grouped by direction. Useful for creating a UI where user needs to select + * a stop from a list. + * + * @param stdParameters + * @param routesIdOrShortNames + * @return + * @throws WebApplicationException + */ + @Path("/command/stops") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Retrives bus stops from the server.", + description="Returns all stops associated with a route,"+ + " grouped by direction. Useful for creating a UI where user needs to select" + + " a stop from a list.",tags= {"base data","stop"}) + public Response getStops(@BeanParam StandardParameters stdParameters, + @Parameter(description="if set, retrives only busstops belongind to the route. " + + "It might be routeId or route shrot name.",required=false) + @QueryParam(value = "r") String routesIdOrShortNames) throws WebApplicationException { + + // Make sure request is valid + stdParameters.validate(); + + try { + // Get stops data from server + ConfigInterface inter = stdParameters.getConfigInterface(); + IpcDirectionsForRoute stopsForRoute = inter.getStops(routesIdOrShortNames); + + // If the route doesn't exist then throw exception such that + // Bad Request with an appropriate message is returned. + if (stopsForRoute == null) + throw WebUtils.badRequestException("route=" + routesIdOrShortNames + " does not exist."); + + // Create and return ApiDirections response + ApiDirections directionsData = new ApiDirections(stopsForRoute); + return stdParameters.createResponse(directionsData); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + } + + /** + * Handles the "stops" command. Returns all stops associated with a route, + * grouped by direction. Useful for creating a UI where user needs to select + * a stop from a list. + * + * @param stdParameters + * @param routeIdsOrShortNames + * @return + * @throws WebApplicationException + */ + @Path("/command/stopsForRoutes") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Retrives bus stops from the server.", + description="Returns all stops associated with a route,"+ + " grouped by direction. Useful for creating a UI where user needs to select" + + " a stop from a list.",tags= {"base data","stop"}) + public Response getStops(@BeanParam StandardParameters stdParameters, + @Parameter(description="if set, retrives only busstops belongind to the route. " + + "It might be routeId or route shrot name.",required=false) + @QueryParam(value = "r") List routeIdsOrShortNames, + @Parameter(description="if set, includes route and direction information for retrieved stops",required=false) + @QueryParam(value = "directions") @DefaultValue("false") boolean includeDirections ) throws WebApplicationException { + + // Make sure request is valid + stdParameters.validate(); + + try { + + List stopsForRoutes; + + // Get Vehicle data from server + ConfigInterface inter = stdParameters.getConfigInterface(); + + if (routeIdsOrShortNames != null && routeIdsOrShortNames.size() == 1) { + String routeIdOrShortName = routeIdsOrShortNames.get(0); + IpcDirectionsForRoute stops = inter.getStops(routeIdOrShortName); + + // If the stops doesn't exist then throw exception such that + // Bad Request with an appropriate message is returned. + if (stops == null) + throw WebUtils.badRequestException("Route for route=" + routeIdOrShortName + " does not exist."); + + stopsForRoutes = new ArrayList<>(); + stopsForRoutes.add(stops); + } else { + // Multiple routes specified + stopsForRoutes = inter.getStops(routeIdsOrShortNames); + } + + // If the route doesn't exist then throw exception such that + // Bad Request with an appropriate message is returned. + if (stopsForRoutes == null) + throw WebUtils.badRequestException("Stops for Routes do not exist."); + + // Create and return stops response + if(includeDirections){ + ApiRoutesDirections stops = new ApiRoutesDirections(stopsForRoutes); + return stdParameters.createResponse(stops); + } else { + ApiStopsForRoute stops = new ApiStopsForRoute(stopsForRoutes); + return stdParameters.createResponse(stops); + } + + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + } + + /** + * Handles the "block" command which outputs configuration data for the + * specified block ID and service ID. Includes all sub-data such as trips + * and trip patterns. + * + * @param stdParameters + * @param blockId + * @param serviceId + * @return + * @throws WebApplicationException + */ + @Path("/command/block") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Retrives configuration data for the specified block ID and service ID", + description="Retrives configuration data for the specified block ID and service ID. " + + "Includes all sub-data such as trips ans trip patterns." + + "Every trip is associated with a block.",tags= {"base data","trip","block"}) + public Response getBlock(@BeanParam StandardParameters stdParameters, + @Parameter(description="Block id to be asked.",required=true) + @QueryParam(value = "blockId") String blockId, + @Parameter(description="Service id to be asked.",required=true) + @QueryParam(value = "serviceId") String serviceId) throws WebApplicationException { + + // Make sure request is valid + stdParameters.validate(); + if (serviceId == null) + throw WebUtils.badRequestException("Must specify serviceId"); + + try { + // Get block data from server + ConfigInterface inter = stdParameters.getConfigInterface(); + IpcBlock ipcBlock = inter.getBlock(blockId, serviceId); + + // If the block doesn't exist then throw exception such that + // Bad Request with an appropriate message is returned. + if (ipcBlock == null) + throw WebUtils.badRequestException( + "The blockId=" + blockId + " for serviceId=" + serviceId + " does not exist."); + + // Create and return ApiBlock response + ApiBlock apiBlock = new ApiBlock(ipcBlock); + return stdParameters.createResponse(apiBlock); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + } + + /** + * Handles the "blocksTerse" command which outputs configuration data for + * the specified block ID. Does not include trip pattern and schedule data + * for trips. + * + * @param stdParameters + * @param blockId + * @return + * @throws WebApplicationException + */ + @Path("/command/blocksTerse") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Retrives configuration data for the specified block ID.", + description="Retrives configuration data for the specified block ID. It does not include trip patterns." + + "Every trip is associated with a block.",tags= {"base data","trip","block"}) + public Response getBlocksTerse(@BeanParam StandardParameters stdParameters, + @Parameter(description="Block id to be asked.",required=true) + @QueryParam(value = "blockId") String blockId) throws WebApplicationException { + + // Make sure request is valid + stdParameters.validate(); + + try { + // Get block data from server + ConfigInterface inter = stdParameters.getConfigInterface(); + Collection ipcBlocks = inter.getBlocks(blockId); + + // If the block doesn't exist then throw exception such that + // Bad Request with an appropriate message is returned. + if (ipcBlocks.isEmpty()) + throw WebUtils.badRequestException("The blockId=" + blockId + " does not exist."); + + // Create and return ApiBlock response + ApiBlocksTerse apiBlocks = new ApiBlocksTerse(ipcBlocks); + return stdParameters.createResponse(apiBlocks); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + } + + /** + * Handles the "blocks" command which outputs configuration data for the + * specified block ID. Includes all sub-data such as trips and trip + * patterns. + * + * @param stdParameters + * @param blockId + * @return + * @throws WebApplicationException + */ + @Path("/command/blocks") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Retrives configuration data for the specified block ID", + description="Retrives configuration data for the specified block ID. Includes all sub-data such as trips and trip." + + "Every trip is associated with a block.",tags= {"base data","trip","block"}) + public Response getBlocks(@BeanParam StandardParameters stdParameters, + @Parameter(description="Block id to be asked.",required=true) @QueryParam(value = "blockId") String blockId) throws WebApplicationException { + + // Make sure request is valid + stdParameters.validate(); + + try { + // Get block data from server + ConfigInterface inter = stdParameters.getConfigInterface(); + Collection ipcBlocks = inter.getBlocks(blockId); + + // If the block doesn't exist then throw exception such that + // Bad Request with an appropriate message is returned. + if (ipcBlocks.isEmpty()) + throw WebUtils.badRequestException("The blockId=" + blockId + " does not exist."); + + // Create and return ApiBlock response + ApiBlocks apiBlocks = new ApiBlocks(ipcBlocks); + return stdParameters.createResponse(apiBlocks); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + } + + /** + * Handles the "blockIds" command. Returns list of block IDs. + * + * @param stdParameters + * @return + * @throws WebApplicationException + */ + @Path("/command/blockIds") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + + @Operation(summary="Retrives a list of all blockId for the specified service ID", + description="Retrives a list of all blockId for the specified service ID." + + "Every trip is associated with a block.",tags= {"base data","trip","block"}) + public Response getBlockIds(@BeanParam StandardParameters stdParameters, + @Parameter(description="if set, returns only the data for that serviceId.",required=false) + @QueryParam(value = "serviceId") String serviceId) throws WebApplicationException { + // Make sure request is valid + stdParameters.validate(); + + try { + // Get Vehicle data from server + ConfigInterface inter = stdParameters.getConfigInterface(); + List ids = inter.getBlockIds(serviceId); + + ApiIds apiIds = new ApiIds(ids); + return stdParameters.createResponse(apiIds); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + } + + /** + * Gets which blocks are active. Can optionally specify list of routes and + * how much before a block is supposed to start is it considered active. + * + * @param stdParameters + * StdParametersBean that gets the standard parameters from the + * URI, query string, and headers. + * @param routesIdOrShortNames + * Optional parameter for specifying which routes want data for. + * @param allowableBeforeTimeSecs + * Optional parameter. A block will be active if the time is + * between the block start time minus allowableBeforeTimeSecs and + * the block end time. Default value for allowableBeforeTimeSecs + * is 0. + * @return + * @throws WebApplicationException + */ + + @Path("/command/activeBlocks") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Gets which blocks are active", + description="Retrives a list of active blocks. Optionally can be filered accorditn to routesIdOrShortNames params." + + "Every trip is associated with a block.",tags= {"prediction","trip","block"}) + + public Response getActiveBlocks(@BeanParam StandardParameters stdParameters, + @Parameter(description="if set, retrives only active blocks belongind to the route. " + + "It might be routeId or route shrot name.",required=false) + @QueryParam(value = "r") List routesIdOrShortNames, + @Parameter(description="A block will be active if the time is between the" + + " block start time minus allowableBeforeTimeSecs and the block end time") + @QueryParam(value = "t") @DefaultValue("0") int allowableBeforeTimeSecs) throws WebApplicationException { + + // Make sure request is valid + stdParameters.validate(); + + try { + // Get active block data from server + + VehiclesInterface vehiclesInterface = + stdParameters.getVehiclesInterface(); + Collection activeBlocks = vehiclesInterface + .getActiveBlocks(routesIdOrShortNames, + allowableBeforeTimeSecs); + + + // Create and return ApiBlock response + ApiActiveBlocks apiActiveBlocks = new ApiActiveBlocks(activeBlocks, stdParameters.getAgencyId()); + return stdParameters.createResponse(apiActiveBlocks); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + } + + @Path("/command/activeBlocksByRoute") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Gets which blocks are active by route", + description="Retrives a list routes with its active blocks. Optionally can be filered according to routesIdOrShortNames params." + + "Every trip is associated with a block.",tags= {"prediction","trip","block","route"}) + + public Response getActiveBlocksByRoute(@BeanParam StandardParameters stdParameters, + @Parameter(description="if set, retrives only active blocks belongind to the route. " + + "It might be routeId or route shrot name.",required=false) + @QueryParam(value = "r") List routesIdOrShortNames, + @Parameter(description="A block will be active if the time is between the block start time minus" + + " allowableBeforeTimeSecs and the block end time") + @QueryParam(value = "t") @DefaultValue("0") int allowableBeforeTimeSecs) throws WebApplicationException { + + // Make sure request is valid + stdParameters.validate(); + + try { + // Get active block data from server + + VehiclesInterface vehiclesInterface = stdParameters.getVehiclesInterface(); + Collection activeBlocks = vehiclesInterface.getActiveBlocks(routesIdOrShortNames, + allowableBeforeTimeSecs); + + + // Create and return ApiBlock response + ApiActiveBlocksRoutes apiActiveBlocksRoutes = new ApiActiveBlocksRoutes(activeBlocks, + stdParameters.getAgencyId()); + return stdParameters.createResponse(apiActiveBlocksRoutes); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + } + + + @Path("/command/activeBlocksByRouteWithoutVehicles") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Gets which blocks are active by route.", + description="Retrives a list routes with its active blocks, without the vechicles. Optionally " + + "can be filered accorditn to routesIdOrShortNames params." + + "Every trip is associated with a block.",tags= {"prediction","trip","block","route"}) + public Response getActiveBlocksByRouteWithoutVehicles( + @BeanParam StandardParameters stdParameters, + @Parameter(description="if set, retrives only active blocks belongind to the route. " + + "It might be routeId or route shrot name.",required=false) + @QueryParam(value = "r") List routesIdOrShortNames, + @Parameter(description="A block will be active if the time is between the block start " + + "time minus allowableBeforeTimeSecs and the block end time") + @QueryParam(value = "t") @DefaultValue("0") int allowableBeforeTimeSecs) + throws WebApplicationException { + + // Make sure request is valid + stdParameters.validate(); + + try { + // Get active block data from server + VehiclesInterface vehiclesInterface = + stdParameters.getVehiclesInterface(); + Collection activeBlocks = vehiclesInterface + .getActiveBlocksWithoutVehicles(routesIdOrShortNames, + allowableBeforeTimeSecs); + + // Create and return ApiBlock response + ApiActiveBlocksRoutes apiActiveBlocksRoutes = new ApiActiveBlocksRoutes( + activeBlocks, stdParameters.getAgencyId()); + return stdParameters.createResponse(apiActiveBlocksRoutes); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + } + + + + @Path("/command/activeBlockByRouteWithVehicles") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Gets which blocks are active by route.", + description="Retrives a list routes with its active blocks, including the vechicles. " + + "Optionally can be filered accorditn to routesIdOrShortNames params." + + "Every trip is associated with a block.",tags= {"prediction","trip","block","route","vehicle"}) + public Response getActiveBlockByRouteWithVehicles( + @BeanParam StandardParameters stdParameters, + @Parameter(description="if set, retrives only active blocks belongind to the route. It might be routeId or route shrot name.",required=false) + @QueryParam(value = "r") String routesIdOrShortName, + @Parameter(description="A block will be active if the time is between the block start time minus allowableBeforeTimeSecs and the block end time") + @QueryParam(value = "t") @DefaultValue("0") int allowableBeforeTimeSecs) + throws WebApplicationException { + + // Make sure request is valid + stdParameters.validate(); + + try { + // Get active block data from server + VehiclesInterface vehiclesInterface = + stdParameters.getVehiclesInterface(); + Collection activeBlocks = vehiclesInterface + .getActiveBlocksAndVehiclesByRouteId(routesIdOrShortName, + allowableBeforeTimeSecs); + + // Create and return ApiBlock response + ApiActiveBlocksRoutes apiActiveBlocksRoutes = new ApiActiveBlocksRoutes( + activeBlocks, stdParameters.getAgencyId()); + return stdParameters.createResponse(apiActiveBlocksRoutes); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + } + + @Path("/command/activeBlockByRouteNameWithVehicles") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Gets which blocks are active by routeName.", + description="Retrives a list routes with its active blocks, including the vechicles. " + + "Optionally can be filered accorditn to routesIdOrShortNames params." + + "Every trip is associated with a block.",tags= {"prediction","trip","block","route","vehicle"}) + public Response getActiveBlockByRouteNameWithVehicles( + @BeanParam StandardParameters stdParameters, + @Parameter(description="if set, retrives only active blocks belongind to the route name specified.",required=false) + @QueryParam(value = "r") String routeName, + @Parameter(description="A block will be active if the time is between the block start time minus allowableBeforeTimeSecs and the block end time") + @QueryParam(value = "t") @DefaultValue("0") int allowableBeforeTimeSecs) + throws WebApplicationException { + // Make sure request is valid + stdParameters.validate(); + + try { + // Get active block data from server + VehiclesInterface vehiclesInterface = + stdParameters.getVehiclesInterface(); + Collection activeBlocks = vehiclesInterface + .getActiveBlocksAndVehiclesByRouteName(routeName, + allowableBeforeTimeSecs); + + // Create and return ApiBlock response + ApiActiveBlocksRoutes apiActiveBlocksRoutes = new ApiActiveBlocksRoutes( + activeBlocks, stdParameters.getAgencyId()); + return stdParameters.createResponse(apiActiveBlocksRoutes); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + + } + @Path("/command/vehicleAdherenceSummary") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Returns the counters for the number of current vehicles running early, late or on time. ", + description="Returns the amount of vehicles running early, late or on time. " + + "Besides specify the amount of vehicles no predictables and the amount of active blocks.", + tags= {"prediction"}) + public Response getVehicleAdherenceSummary(@BeanParam StandardParameters stdParameters, + @Parameter(description="The number of seconds early a vehicle has to be before it is considered in the early counter.", required=false) @QueryParam(value = "allowableEarlySec") @DefaultValue("0") int allowableEarlySec, + @Parameter(description="The number of seconds early a vehicle has to be before it is considered in the late counter.", required=false) @QueryParam(value = "allowableLateSec") @DefaultValue("0") int allowableLateSec, + @Parameter(description="A block will be active if the time is between the block start time minus allowableBeforeTimeSecs (t) and the block end time") + @QueryParam(value = "t") @DefaultValue("0") int allowableBeforeTimeSecs) throws WebApplicationException { + + // Make sure request is valid + stdParameters.validate(); + + try { + + int late = 0, ontime = 0, early = 0, nodata = 0, blocks = 0; + + VehiclesInterface vehiclesInterface = stdParameters.getVehiclesInterface(); + + Collection ipcVehicles = vehiclesInterface.getVehiclesForBlocks(); + + for (IpcVehicle v : ipcVehicles) { + TemporalDifference adh = v.getRealTimeSchedAdh(); + + if (adh == null) + nodata++; + else if (adh.isEarlierThan(allowableEarlySec)) + early++; + else if (adh.isLaterThan(allowableLateSec)) + late++; + else + ontime++; + } + + blocks = vehiclesInterface.getNumActiveBlocks(null, allowableBeforeTimeSecs); + + ApiAdherenceSummary resp = new ApiAdherenceSummary(late, ontime, early, nodata, blocks); + + return stdParameters.createResponse(resp); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + } + + /** + * Handles the "trip" command which outputs configuration data for the + * specified trip. Includes all sub-data such as trip patterns. + * + * @param stdParameters + * @param tripId + * Can be the GTFS trip_id or the trip_short_name + * @return + * @throws WebApplicationException + */ + @Path("/command/trip") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Gets the configuration data of a trip.", + description="Gets the configuration data of a trip",tags= {"base data","trip"}) + public Response getTrip(@BeanParam StandardParameters stdParameters,@Parameter(description="Trip id",required=true) @QueryParam(value = "tripId") String tripId) + throws WebApplicationException { + + // Make sure request is valid + stdParameters.validate(); + + try { + // Get block data from server + ConfigInterface inter = stdParameters.getConfigInterface(); + IpcTrip ipcTrip = inter.getTrip(tripId); + + // If the trip doesn't exist then throw exception such that + // Bad Request with an appropriate message is returned. + if (ipcTrip == null) + throw WebUtils.badRequestException("TripId=" + tripId + " does not exist."); + + // Create and return ApiBlock response. + // Include stop path info since just outputting single trip. + ApiTrip apiTrip = new ApiTrip(ipcTrip, true); + return stdParameters.createResponse(apiTrip); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + } + + /** + * Handles the "tripWithTravelTimes" command which outputs configuration + * data for the specified trip. Includes all sub-data such as trip patterns. + * + * @param stdParameters + * @param tripId + * @return + * @throws WebApplicationException + */ + @Path("/command/tripWithTravelTimes") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Gets the configuration data of a trip.", + description="Gets the configuration data of a trip. Includes all sub-data such as trip patterns",tags= {"base data","trip"}) + public Response getTripWithTravelTimes(@BeanParam StandardParameters stdParameters, + @Parameter(description="Trip id",required=true) @QueryParam(value = "tripId") String tripId) throws WebApplicationException { + + // Make sure request is valid + stdParameters.validate(); + + try { + // Get block data from server + ConfigInterface inter = stdParameters.getConfigInterface(); + IpcTrip ipcTrip = inter.getTrip(tripId); + + // If the trip doesn't exist then throw exception such that + // Bad Request with an appropriate message is returned. + if (ipcTrip == null) + throw WebUtils.badRequestException("TripId=" + tripId + " does not exist."); + + // Create and return ApiBlock response. + // Include stop path info since just outputting single trip. + ApiTripWithTravelTimes apiTrip = new ApiTripWithTravelTimes(ipcTrip, true); + return stdParameters.createResponse(apiTrip); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + } + + /** + * Handles the tripIds command. Returns list of trip IDs. + * + * @param stdParameters + * @return + * @throws WebApplicationException + */ + @Path("/command/tripIds") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Retrives a list of all tripIds", + description="Retrives a list of all tripIds." + ,tags= {"base data","trip"}) + public Response getTripIds(@BeanParam StandardParameters stdParameters) throws WebApplicationException { + // Make sure request is valid + stdParameters.validate(); + + try { + // Get Vehicle data from server + ConfigInterface inter = stdParameters.getConfigInterface(); + List ids = inter.getTripIds(); + + ApiIds apiIds = new ApiIds(ids); + return stdParameters.createResponse(apiIds); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + } + + /** + * Handles the "tripPattern" command which outputs trip pattern + * configuration data for the specified route. + * + * @param stdParameters + * @param routesIdOrShortNames + * @return + * @throws WebApplicationException + */ + @Path("/command/tripPatterns") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Retrives a list of all trip patters.", + description="Retrives a list of all trip patters for the specific routeId or routeShortName." + ,tags= {"base data","trip"}) + public Response getTripPatterns(@BeanParam StandardParameters stdParameters, + @Parameter(description="Specifies the routeId or routeShortName.",required=true) @QueryParam(value = "r") String routesIdOrShortNames, + @Parameter(description="Specifies the headsign",required=false) @QueryParam(value = "headsign") String headsign, + @Parameter(description="Specifies whether to show StopPaths",required=false) @DefaultValue("true") @QueryParam(value = "includeStopPaths") boolean includeStopPaths) throws WebApplicationException { + + // Make sure request is valid + stdParameters.validate(); + + try { + // Get block data from server + ConfigInterface inter = stdParameters.getConfigInterface(); + List ipcTripPatterns; + if(StringUtils.isBlank(headsign)){ + ipcTripPatterns = inter.getTripPatterns(routesIdOrShortNames); + } else{ + ipcTripPatterns = inter.getTripPatterns(routesIdOrShortNames, headsign); + } + + // If the trip doesn't exist then throw exception such that + // Bad Request with an appropriate message is returned. + if (ipcTripPatterns == null) + throw WebUtils.badRequestException("route=" + routesIdOrShortNames + " does not exist."); + + // Create and return ApiTripPatterns response + ApiTripPatterns apiTripPatterns = new ApiTripPatterns(ipcTripPatterns, includeStopPaths); + return stdParameters.createResponse(apiTripPatterns); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + } + + /** + * Handles the "scheduleVertStops" command which outputs schedule for the + * specified route. The data is output such that the stops are listed + * vertically (and the trips are horizontal). For when there are a good + * number of stops but not as many trips, such as for commuter rail. + * + * @param stdParameters + * @param routesIdOrShortNames + * @return + * @throws WebApplicationException + */ + @Path("/command/scheduleVertStops") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Retrives schedule for the specified route.", + description="Retrives schedule for the specified route. The data is output such that the stops are listed " + + "vertically (and the trips are horizontal). For when there are a good number of stops but not as" + + " many trips, such as for commuter rail." + ,tags= {"base data","schedule"}) + public Response getScheduleVertStops(@BeanParam StandardParameters stdParameters, + @Parameter(description="Specifies the routeId or routeShortName.",required=true) @QueryParam(value = "r") String routesIdOrShortNames) throws WebApplicationException { + + // Make sure request is valid + stdParameters.validate(); + + try { + // Get block data from server + ConfigInterface inter = stdParameters.getConfigInterface(); + List ipcSchedules = inter.getSchedules(routesIdOrShortNames); + + // If the trip doesn't exist then throw exception such that + // Bad Request with an appropriate message is returned. + if (ipcSchedules == null) + throw WebUtils.badRequestException("route=" + routesIdOrShortNames + " does not exist."); + + // Create and return ApiSchedules response + ApiSchedulesVertStops apiSchedules = new ApiSchedulesVertStops(ipcSchedules); + return stdParameters.createResponse(apiSchedules); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + } + + /** + * Handles the "scheduleVertStops" command which outputs schedule for the + * specified route. The data is output such that the stops are listed + * vertically (and the trips are horizontal). For when there are a good + * number of stops but not as many trips, such as for commuter rail. + * + * @param stdParameters + * @param tripId + * @return + * @throws WebApplicationException + */ + @Path("/command/scheduleTripVertStops") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Retrives schedule for the specified trip.", + description="Retrives schedule for the specified trip. The data is output such that the stops are listed " + + "vertically (and the trips are horizontal)." + ,tags= {"base data","schedule"}) + public Response getScheduleTripVertStops(@BeanParam StandardParameters stdParameters, + @Parameter(description="Specifies the tripId",required=true) + @QueryParam(value = "t") String tripId) throws WebApplicationException { + + // Make sure request is valid + stdParameters.validate(); + + try { + // Get block data from server + ConfigInterface inter = stdParameters.getConfigInterface(); + List ipcSchedules = inter.getSchedulesForTrip(tripId); + + // If the trip doesn't exist then throw exception such that + // Bad Request with an appropriate message is returned. + if (ipcSchedules == null) + throw WebUtils.badRequestException("trip=" + tripId + " does not exist."); + + // Create and return ApiSchedules response + ApiSchedulesVertStops apiSchedules = new ApiSchedulesVertStops(ipcSchedules); + return stdParameters.createResponse(apiSchedules); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + } + + /** + * Handles the "scheduleHorizStops" command which outputs schedule for the + * specified route. The data is output such that the stops are listed + * horizontally (and the trips are vertical). For when there are many more + * trips than stops, which is typical for bus routes. + * + * @param stdParameters + * @param routesIdOrShortNames + * @return + * @throws WebApplicationException + */ + @Path("/command/scheduleHorizStops") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Retrives schedule for the specified route.", + description="Retrives schedule for the specified route. The data is output such that the stops are listed " + + "horizontally (and the trips are vertical). For when there are a good number of stops but not as" + + " many trips, such as for commuter rail." + ,tags= {"base data","schedule"}) + public Response getScheduleHorizStops(@BeanParam StandardParameters stdParameters, + @Parameter(description="Specifies the routeId or routeShortName.",required=true) @QueryParam(value = "r") String routesIdOrShortNames) throws WebApplicationException { + + // Make sure request is valid + stdParameters.validate(); + + try { + // Get block data from server + ConfigInterface inter = stdParameters.getConfigInterface(); + List ipcSchedules = inter.getSchedules(routesIdOrShortNames); + + // If the trip doesn't exist then throw exception such that + // Bad Request with an appropriate message is returned. + if (ipcSchedules == null) + throw WebUtils.badRequestException("route=" + routesIdOrShortNames + " does not exist."); + + // Create and return ApiSchedules response + ApiSchedulesHorizStops apiSchedules = new ApiSchedulesHorizStops(ipcSchedules); + return stdParameters.createResponse(apiSchedules); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + } + + @Path("/command/scheduleTripHorizStops") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Retrives schedule for the specified trip.", + description="Retrives schedule for the specified trip. The data is output such that the stops are listed " + + "horizontally (and the trip are vertical). For when there are a good number of stops but not as" + + " many trips, such as for commuter rail." + ,tags= {"base data","schedule"}) + public Response getScheduleTripHorizStops(@BeanParam StandardParameters stdParameters, + @Parameter(description="Specifies the tripId",required=true) + @QueryParam(value = "t") String tripId) throws WebApplicationException { + + // Make sure request is valid + stdParameters.validate(); + + try { + // Get block data from server + ConfigInterface inter = stdParameters.getConfigInterface(); + List ipcSchedules = inter.getSchedulesForTrip(tripId); + + // If the trip doesn't exist then throw exception such that + // Bad Request with an appropriate message is returned. + if (ipcSchedules == null) + throw WebUtils.badRequestException("tripId=" + tripId + " does not exist."); + + // Create and return ApiSchedules response + ApiSchedulesHorizStops apiSchedules = new ApiSchedulesHorizStops(ipcSchedules); + return stdParameters.createResponse(apiSchedules); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + } + + /** + * For getting Agency data for a specific agencyId. + * + * @param stdParameters + * @return + * @throws WebApplicationException + */ + @Path("/command/agencyGroup") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Retrives agency infomation.",description="Retrives agency infomation, including extent.",tags= {"base data","agency"}) + public Response getAgencyGroup(@BeanParam StandardParameters stdParameters) throws WebApplicationException { + + // Make sure request is valid + stdParameters.validate(); + + try { + // Get block data from server + ConfigInterface inter = stdParameters.getConfigInterface(); + List agencies = inter.getAgencies(); + + // Create and return ApiAgencies response + List apiAgencyList = new ArrayList(); + for (Agency agency : agencies) { + apiAgencyList.add(new ApiAgency(stdParameters.getAgencyId(), agency)); + } + ApiAgencies apiAgencies = new ApiAgencies(apiAgencyList); + return stdParameters.createResponse(apiAgencies); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + } + + /** + * For getting calendars that are currently active. + * + * @param stdParameters + * @return + * @throws WebApplicationException + */ + @Path("/command/currentCalendars") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Retrives current calendar infomation.",description="Retrives current calendar infomation. Only teh calendar that applies to current day",tags= {"base data","calendar","serviceId"}) + public Response getCurrentCalendars(@BeanParam StandardParameters stdParameters) throws WebApplicationException { + + // Make sure request is valid + stdParameters.validate(); + + try { + // Get block data from server + ConfigInterface inter = stdParameters.getConfigInterface(); + List ipcCalendars = inter.getCurrentCalendars(); + + // Create and return ApiAgencies response + ApiCalendars apiCalendars = new ApiCalendars(ipcCalendars); + return stdParameters.createResponse(apiCalendars); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + } + + /** + * For getting all calendars. + * + * @param stdParameters + * @return + * @throws WebApplicationException + */ + @Path("/command/allCalendars") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Retrives all calendar infomation.",description="Retrives all calendar infomation.",tags= {"base data","calendar","serviceId"}) + public Response getAllCalendars(@BeanParam StandardParameters stdParameters) throws WebApplicationException { + + // Make sure request is valid + stdParameters.validate(); + + try { + // Get block data from server + ConfigInterface inter = stdParameters.getConfigInterface(); + List ipcCalendars = inter.getAllCalendars(); + + // Create and return ApiAgencies response + ApiCalendars apiCalendars = new ApiCalendars(ipcCalendars); + return stdParameters.createResponse(apiCalendars); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + } + + /** + * Handles the "serviceIds" command. Returns list of all service IDs. + * + * @param stdParameters + * @return + * @throws WebApplicationException + */ + @Path("/command/serviceIds") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Retrives all service id.",description="Retrives all service id.",tags= {"base data","serviceId"}) + public Response getServiceIds(@BeanParam StandardParameters stdParameters) throws WebApplicationException { + // Make sure request is valid + stdParameters.validate(); + + try { + // Get Vehicle data from server + ConfigInterface inter = stdParameters.getConfigInterface(); + List ids = inter.getServiceIds(); + + ApiIds apiIds = new ApiIds(ids); + return stdParameters.createResponse(apiIds); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + } + + /** + * Handles the currentServiceIds command. Returns list of service IDs that + * are currently active. + * + * @param stdParameters + * @return + * @throws WebApplicationException + */ + @Path("/command/currentServiceIds") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Retrives current service id.",description="Retrives current service id.",tags= {"base data","serviceId"}) + public Response getCurrentServiceIds(@BeanParam StandardParameters stdParameters) throws WebApplicationException { + // Make sure request is valid + stdParameters.validate(); + + try { + // Get Vehicle data from server + ConfigInterface inter = stdParameters.getConfigInterface(); + List ids = inter.getCurrentServiceIds(); + + ApiIds apiIds = new ApiIds(ids); + return stdParameters.createResponse(apiIds); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + } + + /** + * Returns status about the specified agency server. Currently provides info + * on the DbLogger queue. + * + * @param stdParameters + * @return + * @throws WebApplicationException + */ + @Path("/command/serverStatus") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Retrives server status information.",description="Retrives server status information.",tags= {"server status"}) + public Response getServerStatus(@BeanParam StandardParameters stdParameters) throws WebApplicationException { + + // Make sure request is valid + stdParameters.validate(); + + try { + // Get status information from server + ServerStatusInterface inter = stdParameters.getServerStatusInterface(); + IpcServerStatus ipcServerStatus = inter.get(); + + // Create and return ApiServerStatus response + ApiServerStatus apiServerStatus = new ApiServerStatus(stdParameters.getAgencyId(), ipcServerStatus); + return stdParameters.createResponse(apiServerStatus); + } catch (Exception e) { + // If problem getting data then return a Bad Request + throw WebUtils.badRequestException(e); + } + } + + @Path("/command/revisionInformation") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Retrives internal db metadata.",description="Retrives internal db include revision and data load date.",tags= {"configRev"}) + public Response getConfigRev(@BeanParam StandardParameters stdParameters) throws WebApplicationException { + stdParameters.validate(); + try { + RevisionInformationInterface inter = stdParameters.getRevisionInformationInterface(); + IpcRevisionInformation ipcRevisionInformation = inter.get(); + + ApiRevisionInformation apiRevisionInformation = new ApiRevisionInformation(stdParameters.getAgencyId(), ipcRevisionInformation); + return stdParameters.createResponse(apiRevisionInformation); + } catch (Exception e) { + throw WebUtils.badRequestException(e); + } + } + /** + * Returns info for this particular web server for each agency on how many + * outstanding RMI calls there are. + * + * @param stdParameters + * @return + * @throws WebApplicationException + */ + @Path("/command/rmiStatus") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Retrives RMI status information.",description="Retrives RMI server status information.",tags= {"server status"}) + public Response getRmiStatus(@BeanParam StandardParameters stdParameters) throws WebApplicationException { + + // Make sure request is valid + stdParameters.validate(); + + ApiRmiServerStatus apiRmiServerStatus = new ApiRmiServerStatus(); + return stdParameters.createResponse(apiRmiServerStatus); + } + + @Path("/command/currentServerTime") + @GET + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Retrives server time.",description="Retrives server time",tags= {"server status"}) + public Response getCurrentServerTime(@BeanParam StandardParameters stdParameters) throws WebApplicationException, RemoteException { + // Make sure request is valid + stdParameters.validate(); + ServerStatusInterface inter = stdParameters.getServerStatusInterface(); + Date currentTime=inter.getCurrentServerTime(); + + return stdParameters.createResponse(new ApiCurrentServerDate(currentTime)); + } + // /** + // * For creating response of list of vehicles. Would like to make this a + // * generic type but due to type erasure cannot do so since GenericEntity + // * somehow works differently with generic types. + // *

    + // * Deprecated because found that much better off using a special + // * container class for lists of items since that way can control the + // * name of the list element. + // * + // * @param collection + // * Collection of Vehicle objects to be returned in XML or JSON. + // * Must be ArrayList so can use GenericEntity to create Response. + // * @param stdParameters + // * For specifying media type. + // * @return The created response in the proper media type. + // */ + // private static Response createListResponse(Collection + // collection, + // StdParametersBean stdParameters) { + // // Must be ArrayList so can use GenericEntity to create Response. + // ArrayList arrayList = (ArrayList) collection; + // + // // Create a GenericEntity that can handle list of the appropriate + // // type. + // GenericEntity> entity = + // new GenericEntity>(arrayList) {}; + // + // // Return the response using the generic entity + // return createResponse(entity, stdParameters); + // } + +} diff --git a/transitimeApi/src/main/java/org/transitime/api/rootResources/TransitimeNonAgencyApi.java b/transitclockApi/src/main/java/org/transitclock/api/rootResources/TransitimeNonAgencyApi.java similarity index 80% rename from transitimeApi/src/main/java/org/transitime/api/rootResources/TransitimeNonAgencyApi.java rename to transitclockApi/src/main/java/org/transitclock/api/rootResources/TransitimeNonAgencyApi.java index 307c19819..0d8bfe04b 100644 --- a/transitimeApi/src/main/java/org/transitime/api/rootResources/TransitimeNonAgencyApi.java +++ b/transitclockApi/src/main/java/org/transitclock/api/rootResources/TransitimeNonAgencyApi.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.api.rootResources; +package org.transitclock.api.rootResources; import java.rmi.RemoteException; import java.util.ArrayList; @@ -32,21 +32,25 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import org.transitime.api.data.ApiAgencies; -import org.transitime.api.data.ApiAgency; -import org.transitime.api.data.ApiNearbyPredictionsForAgencies; -import org.transitime.api.data.ApiPredictions; -import org.transitime.api.predsByLoc.PredsByLoc; -import org.transitime.api.utils.StandardParameters; -import org.transitime.api.utils.WebUtils; -import org.transitime.db.structs.Agency; -import org.transitime.db.structs.Location; -import org.transitime.db.webstructs.WebAgency; -import org.transitime.ipc.clients.ConfigInterfaceFactory; -import org.transitime.ipc.clients.PredictionsInterfaceFactory; -import org.transitime.ipc.data.IpcPredictionsForRouteStopDest; -import org.transitime.ipc.interfaces.ConfigInterface; -import org.transitime.ipc.interfaces.PredictionsInterface; +import org.transitclock.api.data.ApiAgencies; +import org.transitclock.api.data.ApiAgency; +import org.transitclock.api.data.ApiNearbyPredictionsForAgencies; +import org.transitclock.api.data.ApiPredictions; +import org.transitclock.api.predsByLoc.PredsByLoc; +import org.transitclock.api.utils.StandardParameters; +import org.transitclock.api.utils.WebUtils; +import org.transitclock.db.structs.Agency; +import org.transitclock.db.structs.Location; +import org.transitclock.db.webstructs.WebAgency; +import org.transitclock.ipc.clients.ConfigInterfaceFactory; +import org.transitclock.ipc.clients.PredictionsInterfaceFactory; +import org.transitclock.ipc.data.IpcPredictionsForRouteStopDest; +import org.transitclock.ipc.interfaces.ConfigInterface; +import org.transitclock.ipc.interfaces.PredictionsInterface; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.servers.Server; +import io.swagger.v3.oas.annotations.servers.Servers; /** * Contains the API commands for the Transitime API for system wide commands, @@ -76,6 +80,8 @@ public class TransitimeNonAgencyApi { @Path("/command/agencies") @GET @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Rerives all tha agencies managed by the server.", + description="Rerives all tha agencies managed by the server.",tags= {"base data","agency"}) public Response getAgencies(@BeanParam StandardParameters stdParameters) throws WebApplicationException { // Make sure request is valid @@ -111,7 +117,7 @@ public Response getAgencies(@BeanParam StandardParameters stdParameters) return stdParameters.createResponse(apiAgencies); } catch (RemoteException e) { // If problem getting data then return a Bad Request - throw WebUtils.badRequestException(e.getMessage()); + throw WebUtils.badRequestException(e); } } @@ -135,6 +141,9 @@ public Response getAgencies(@BeanParam StandardParameters stdParameters) @Path("/command/predictionsByLoc") @GET @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Operation(summary="Gets predictions from server by location", + description="Gets a list of prediction by location for all angencies managed by the api.", + tags= {"prediction"}) public Response getPredictions( @BeanParam StandardParameters stdParameters, @QueryParam(value = "lat") Double lat, @@ -181,7 +190,7 @@ public Response getPredictions( return stdParameters.createResponse(predsForAgencies); } catch (Exception e) { // If problem getting data then return a Bad Request - throw WebUtils.badRequestException(e.getMessage()); + throw WebUtils.badRequestException(e); } } diff --git a/transitimeApi/src/main/java/org/transitime/api/rootResources/package-info.java b/transitclockApi/src/main/java/org/transitclock/api/rootResources/package-info.java similarity index 94% rename from transitimeApi/src/main/java/org/transitime/api/rootResources/package-info.java rename to transitclockApi/src/main/java/org/transitclock/api/rootResources/package-info.java index 9901af77a..9c66e6ab4 100644 --- a/transitimeApi/src/main/java/org/transitime/api/rootResources/package-info.java +++ b/transitclockApi/src/main/java/org/transitclock/api/rootResources/package-info.java @@ -19,4 +19,4 @@ * Contains descriptions of all of all of the API calls * and specifies their syntax. */ -package org.transitime.api.rootResources; \ No newline at end of file +package org.transitclock.api.rootResources; diff --git a/transitimeApi/src/main/java/org/transitime/api/utils/AgencyTimezoneCache.java b/transitclockApi/src/main/java/org/transitclock/api/utils/AgencyTimezoneCache.java similarity index 89% rename from transitimeApi/src/main/java/org/transitime/api/utils/AgencyTimezoneCache.java rename to transitclockApi/src/main/java/org/transitclock/api/utils/AgencyTimezoneCache.java index 599a16d5a..f9a523c08 100644 --- a/transitimeApi/src/main/java/org/transitime/api/utils/AgencyTimezoneCache.java +++ b/transitclockApi/src/main/java/org/transitclock/api/utils/AgencyTimezoneCache.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.api.utils; +package org.transitclock.api.utils; import java.rmi.RemoteException; import java.util.HashMap; @@ -24,9 +24,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.transitime.db.structs.Agency; -import org.transitime.ipc.clients.ConfigInterfaceFactory; -import org.transitime.ipc.interfaces.ConfigInterface; +import org.transitclock.db.structs.Agency; +import org.transitclock.ipc.clients.ConfigInterfaceFactory; +import org.transitclock.ipc.interfaces.ConfigInterface; /** * So that can get quick access to TimeZone for agency so that can properly diff --git a/transitclockApi/src/main/java/org/transitclock/api/utils/ApiLoggingFilter.java b/transitclockApi/src/main/java/org/transitclock/api/utils/ApiLoggingFilter.java new file mode 100644 index 000000000..c1645a548 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/utils/ApiLoggingFilter.java @@ -0,0 +1,52 @@ +/* + * 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.api.utils; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ApiLoggingFilter implements Filter { + + private static final Logger logger = LoggerFactory + .getLogger(ApiLoggingFilter.class); + + @Override + public void init(FilterConfig filterConfg) { + logger.info("ApiLoggingFilter init"); + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) { + try { + filterChain.doFilter(request, response); + } catch (Throwable ex) { + logger.error("Filter caught exception: ", ex); + throw new RuntimeException(ex); + } + } + + @Override + public void destroy() { + logger.info("ApiLoggingFilter destroy"); + } +} \ No newline at end of file diff --git a/transitclockApi/src/main/java/org/transitclock/api/utils/MathUtils.java b/transitclockApi/src/main/java/org/transitclock/api/utils/MathUtils.java new file mode 100644 index 000000000..710e36170 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/utils/MathUtils.java @@ -0,0 +1,27 @@ +package org.transitclock.api.utils; + +import org.transitclock.api.data.SpeedFormat; + +public class MathUtils { + + public static float convertSpeed(float speedValue, SpeedFormat speedFormat){ + if(!Float.isNaN(speedValue)) { + if (speedFormat.equals(SpeedFormat.MS)) { + return speedValue; + } else if (speedFormat.equals(SpeedFormat.KM)) { + return convertMetersPerSecondToKm(speedValue); + } else if (speedFormat.equals(SpeedFormat.MPH)) { + return convertMetersPerSecondToMph(speedValue); + } + } + return speedValue; + } + + public static float convertMetersPerSecondToMph(float value){ + return value * 2.2369f; + } + + public static float convertMetersPerSecondToKm(float value){ + return value * 3.6f; + } +} diff --git a/transitclockApi/src/main/java/org/transitclock/api/utils/NumberFormatter.java b/transitclockApi/src/main/java/org/transitclock/api/utils/NumberFormatter.java new file mode 100644 index 000000000..2817d4e71 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/utils/NumberFormatter.java @@ -0,0 +1,34 @@ +package org.transitclock.api.utils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.transitclock.api.data.SpeedFormat; +import org.transitclock.utils.MathUtils; +import static org.transitclock.api.utils.MathUtils.*; + +public class NumberFormatter { + private static final Logger logger = LoggerFactory.getLogger(NumberFormatter.class); + + public static Long getValueAsLong(Double value){ + if(value != null){ + try{ + return value.longValue(); + } catch (NumberFormatException nfe){ + logger.warn("Unable to convert {} to a Long do to a number format exception",value, nfe); + } catch(Exception e){ + logger.warn("Hit unexpected issue converting value {} to Big Decimal", value, e); + } + } + return 0L; + } + + public static Double getRoundedValueAsDouble(float value, int precision){ + return Float.isNaN(value) ? + null : MathUtils.round(value, precision); + } + + public static String getRoundedValueAsString(float value, int precision){ + return Float.isNaN(value) ? + null : String.valueOf(MathUtils.round(value, precision)); + } +} diff --git a/transitimeApi/src/main/java/org/transitime/api/utils/StandardParameters.java b/transitclockApi/src/main/java/org/transitclock/api/utils/StandardParameters.java similarity index 65% rename from transitimeApi/src/main/java/org/transitime/api/utils/StandardParameters.java rename to transitclockApi/src/main/java/org/transitclock/api/utils/StandardParameters.java index bd38983cb..eb3a7ea24 100644 --- a/transitimeApi/src/main/java/org/transitime/api/utils/StandardParameters.java +++ b/transitclockApi/src/main/java/org/transitclock/api/utils/StandardParameters.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.api.utils; +package org.transitclock.api.utils; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.DefaultValue; @@ -29,17 +29,29 @@ import javax.ws.rs.core.Response.ResponseBuilder; import javax.ws.rs.core.Response.Status; -import org.transitime.db.webstructs.ApiKeyManager; -import org.transitime.ipc.clients.CommandsInterfaceFactory; -import org.transitime.ipc.clients.ConfigInterfaceFactory; -import org.transitime.ipc.clients.PredictionsInterfaceFactory; -import org.transitime.ipc.clients.ServerStatusInterfaceFactory; -import org.transitime.ipc.clients.VehiclesInterfaceFactory; -import org.transitime.ipc.interfaces.CommandsInterface; -import org.transitime.ipc.interfaces.ConfigInterface; -import org.transitime.ipc.interfaces.PredictionsInterface; -import org.transitime.ipc.interfaces.ServerStatusInterface; -import org.transitime.ipc.interfaces.VehiclesInterface; +import org.transitclock.db.webstructs.ApiKeyManager; +import org.transitclock.ipc.clients.CacheQueryInterfaceFactory; +import org.transitclock.ipc.clients.CommandsInterfaceFactory; +import org.transitclock.ipc.clients.ConfigInterfaceFactory; +import org.transitclock.ipc.clients.HoldingTimeInterfaceFactory; +import org.transitclock.ipc.clients.PredictionAnalysisInterfaceFactory; +import org.transitclock.ipc.clients.PredictionsInterfaceFactory; +import org.transitclock.ipc.clients.RevisionInterfaceFactory; +import org.transitclock.ipc.clients.ScheduleAdherenceInterfaceFactory; +import org.transitclock.ipc.clients.ServerStatusInterfaceFactory; +import org.transitclock.ipc.clients.VehiclesInterfaceFactory; +import org.transitclock.ipc.interfaces.CacheQueryInterface; +import org.transitclock.ipc.interfaces.CommandsInterface; +import org.transitclock.ipc.interfaces.ConfigInterface; +import org.transitclock.ipc.interfaces.HoldingTimeInterface; +import org.transitclock.ipc.interfaces.PredictionAnalysisInterface; +import org.transitclock.ipc.interfaces.PredictionsInterface; +import org.transitclock.ipc.interfaces.ReportingInterface; +import org.transitclock.ipc.interfaces.RevisionInformationInterface; +import org.transitclock.ipc.interfaces.ServerStatusInterface; +import org.transitclock.ipc.interfaces.VehiclesInterface; + +import io.swagger.v3.oas.annotations.Parameter; /** * For getting the standard parameters from the URI used to access the feed. @@ -51,9 +63,11 @@ */ public class StandardParameters { @PathParam("key") + @Parameter(description="Application key to access this api.") private String key; @PathParam("agency") + @Parameter(description="Specify the agency the request is intended to.") private String agencyId; @QueryParam("format") @@ -254,6 +268,85 @@ public ServerStatusInterface getServerStatusInterface() return serverStatusInterface; } + /** + * Query for metadata about dataset. + * @return + * @throws WebApplicationException + */ + public RevisionInformationInterface getRevisionInformationInterface() throws WebApplicationException { + RevisionInformationInterface revisionInformationInterface = + RevisionInterfaceFactory.get(agencyId); + if (revisionInformationInterface == null) + throw WebUtils.badRequestException("Agency ID " + agencyId + " is not valid"); + return revisionInformationInterface; + } + + + /** + * Gets the CacheQueryInterface for the specified agencyId. If not valid + * then throws WebApplicationException. + * + * @return The CacheQueryInterface + */ + public CacheQueryInterface getCacheQueryInterface() + throws WebApplicationException { + CacheQueryInterface cachequeryInterface = + CacheQueryInterfaceFactory.get(agencyId); + if (cachequeryInterface == null) + throw WebUtils.badRequestException("Agency ID " + agencyId + + " is not valid"); + + return cachequeryInterface; + } + /** + * Gets the PredictionAnalysisInterface for the specified agencyId. If not valid + * then throws WebApplicationException. + * + * @return The PredictionAnalysisInterface + */ + public PredictionAnalysisInterface getPredictionAnalysisInterface() + throws WebApplicationException { + PredictionAnalysisInterface predictionAnalysisInterface = + PredictionAnalysisInterfaceFactory.get(agencyId); + if (predictionAnalysisInterface == null) + throw WebUtils.badRequestException("Agency ID " + agencyId + + " is not valid"); + + return predictionAnalysisInterface ; + } + + /** + * Gets the HoldingTimeInterface for the specified agencyId. If not valid + * then throws WebApplicationException. + * + * @return The HoldingTimeInterface + */ + public HoldingTimeInterface getHoldingTimeInterface() + { + HoldingTimeInterface holdingTimeInterface = HoldingTimeInterfaceFactory.get(agencyId); + if (holdingTimeInterface == null) + throw WebUtils.badRequestException("Agency ID " + agencyId + + " is not valid"); + + return holdingTimeInterface ; + } + + /** + * Gets the ScheduleAdherenceInterface for the specified agencyId. If not valid + * then throws WebApplicationException. + * + * @return The ScheduleAdherenceInterface + */ + public ReportingInterface getReportingInterface() + { + ReportingInterface reportingInterface = ScheduleAdherenceInterfaceFactory.get(agencyId); + if (reportingInterface == null) + throw WebUtils.badRequestException("Agency ID " + agencyId + + " is not valid"); + + return reportingInterface; + } + /** * Simple getter for the key * diff --git a/transitclockApi/src/main/java/org/transitclock/api/utils/TripFormatter.java b/transitclockApi/src/main/java/org/transitclock/api/utils/TripFormatter.java new file mode 100644 index 000000000..0af44ec95 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/api/utils/TripFormatter.java @@ -0,0 +1,11 @@ +package org.transitclock.api.utils; + +public class TripFormatter { + + public static String getFormattedTripId(boolean serviceIdSuffix, String tripId){ + if(tripId != null && serviceIdSuffix){ + return tripId.split("-")[0]; + } + return tripId; + } +} diff --git a/transitimeApi/src/main/java/org/transitime/api/utils/UsageValidator.java b/transitclockApi/src/main/java/org/transitclock/api/utils/UsageValidator.java similarity index 55% rename from transitimeApi/src/main/java/org/transitime/api/utils/UsageValidator.java rename to transitclockApi/src/main/java/org/transitclock/api/utils/UsageValidator.java index e8ea4aca2..b5510b247 100644 --- a/transitimeApi/src/main/java/org/transitime/api/utils/UsageValidator.java +++ b/transitclockApi/src/main/java/org/transitclock/api/utils/UsageValidator.java @@ -15,7 +15,7 @@ * along with Transitime.org . If not, see . */ -package org.transitime.api.utils; +package org.transitclock.api.utils; import java.util.LinkedList; import java.util.Map; @@ -23,6 +23,8 @@ import javax.ws.rs.WebApplicationException; +import org.transitclock.config.IntegerConfigValue; + /** * For making sure that use of API doesn't exceed limits. Intended to deal with * bad applications that are requesting too much data or a denial of service @@ -42,8 +44,16 @@ public class UsageValidator { // The limits of requests per IP address - private static int MAX_REQUESTS = 100; - private static int MAX_REQUESTS_TIME_MSEC = 10000; + + + private static IntegerConfigValue maxRequests = new IntegerConfigValue( + "transitclock.usage.maxRequests", 2000, + "Maximum number of requests to allow within the specified time frame"); + + private static IntegerConfigValue maxRequestsTimeMsec = new IntegerConfigValue( + "transitclock.usage.maxRequestsTimeMsec", 1000, + "Amount of time in msec before max requests count limit is reset"); + // This is a singleton class private static UsageValidator singleton = new UsageValidator(); @@ -71,55 +81,15 @@ public static UsageValidator getInstance() { } /** - * Makes sure that usage doesn't exceed limits. Intended to deal with bad - * applications that are requesting too much data or a denial of service - * attack. Currently simply checks to make sure that for a given request IP - * address that there aren't more than a certain number of requests per time - * frame. - *

    - * Probably should be refined in the future. - * + * TODO: previous impl was not thread safe -- so now its just a placeholder + * for future checks * @param stdParameters * @throws WebApplicationException */ public void validateUsage(StandardParameters stdParameters) throws WebApplicationException { - if (stdParameters.getRequest() != null) { - String requestIpAddress = stdParameters.getRequest() - .getRemoteAddr(); - - long currentTime = System.currentTimeMillis(); - - // Get the list of access times for the IP address. Can be empty. - LinkedList accessTimes = requestTimesPerIp - .get(requestIpAddress); - if (accessTimes == null) { - accessTimes = new LinkedList(); - requestTimesPerIp.put(requestIpAddress, accessTimes); - } - - // If number of requests already reached see if got too many - // within the time limit. - if (accessTimes.size() == MAX_REQUESTS) { - Long oldestAccessTime = accessTimes.getLast(); - if (oldestAccessTime > currentTime - MAX_REQUESTS_TIME_MSEC) { - // Note that using special HTTP response 429, which is for - // Too Many Requests. See - // http://en.wikipedia.org/wiki/List_of_HTTP_status_codes - throw WebUtils.badRequestException(429, "Exceeded " - + MAX_REQUESTS + " requests within " - + MAX_REQUESTS_TIME_MSEC + " msec for IP address " - + requestIpAddress); - } - } - - // Add the current request time to the queue - accessTimes.addFirst(currentTime); + return; - // Clean up queue of old requests that are beyond the time limit - while (accessTimes.getLast() < currentTime - MAX_REQUESTS_TIME_MSEC) - accessTimes.removeLast(); - } } } diff --git a/transitimeApi/src/main/java/org/transitime/api/utils/WebUtils.java b/transitclockApi/src/main/java/org/transitclock/api/utils/WebUtils.java similarity index 76% rename from transitimeApi/src/main/java/org/transitime/api/utils/WebUtils.java rename to transitclockApi/src/main/java/org/transitclock/api/utils/WebUtils.java index 02550d9a2..ba641a46c 100644 --- a/transitimeApi/src/main/java/org/transitime/api/utils/WebUtils.java +++ b/transitclockApi/src/main/java/org/transitclock/api/utils/WebUtils.java @@ -15,13 +15,16 @@ * along with Transitime.org . If not, see . */ -package org.transitime.api.utils; +package org.transitclock.api.utils; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * Utilities for web based API. * @@ -30,6 +33,9 @@ */ public class WebUtils { + private static final Logger logger = LoggerFactory + .getLogger(WebUtils.class); + /** * Provides the API key to be used to access the Transitime API by * Transitime web pages. @@ -40,6 +46,19 @@ public static String apiKey() { return "5ec0de94"; } + /** + * Convenience method for when need to throw a BAD_REQUEST exception + * response. + * + * @param ex the exception which will be logged + * Message to be provided as part of the response. + * @return Exception to be thrown + */ + public static WebApplicationException badRequestException(Throwable ex) { + logger.error("Bad Request", ex); + return badRequestException(Status.BAD_REQUEST.getStatusCode(), ex.getMessage()); + } + /** * Convenience method for when need to throw a BAD_REQUEST exception * response. diff --git a/transitimeApi/src/main/java/org/transitime/api/utils/package-info.java b/transitclockApi/src/main/java/org/transitclock/api/utils/package-info.java similarity index 96% rename from transitimeApi/src/main/java/org/transitime/api/utils/package-info.java rename to transitclockApi/src/main/java/org/transitclock/api/utils/package-info.java index f6a091f7f..64445194a 100644 --- a/transitimeApi/src/main/java/org/transitime/api/utils/package-info.java +++ b/transitclockApi/src/main/java/org/transitclock/api/utils/package-info.java @@ -20,4 +20,4 @@ * and validating usage to make sure key is proper and that not exceeding * access limits. */ -package org.transitime.api.utils; \ No newline at end of file +package org.transitclock.api.utils; \ No newline at end of file diff --git a/transitclockApi/src/main/java/org/transitclock/package-info.java b/transitclockApi/src/main/java/org/transitclock/package-info.java new file mode 100644 index 000000000..fd692b487 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/package-info.java @@ -0,0 +1 @@ +package org.transitclock; diff --git a/transitclockApi/src/main/java/org/transitclock/servlet/swagger/ApiOriginFilter.java b/transitclockApi/src/main/java/org/transitclock/servlet/swagger/ApiOriginFilter.java new file mode 100644 index 000000000..6940892e6 --- /dev/null +++ b/transitclockApi/src/main/java/org/transitclock/servlet/swagger/ApiOriginFilter.java @@ -0,0 +1,42 @@ +/** + * Copyright 2016 SmartBear Software + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.transitclock.servlet.swagger; + +import java.io.IOException; + +import javax.servlet.*; +import javax.servlet.http.HttpServletResponse; + +public class ApiOriginFilter implements javax.servlet.Filter { + @Override + public void doFilter(ServletRequest request, ServletResponse response, + FilterChain chain) throws IOException, ServletException { + HttpServletResponse res = (HttpServletResponse) response; + res.addHeader("Access-Control-Allow-Origin", "*"); + res.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT"); + res.addHeader("Access-Control-Allow-Headers", "Content-Type"); + chain.doFilter(request, response); + } + + @Override + public void destroy() { + } + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + } +} \ No newline at end of file diff --git a/transitimeWebapp/src/main/resources/logbackTomcat.xml b/transitclockApi/src/main/resources/logbackTomcat.xml similarity index 95% rename from transitimeWebapp/src/main/resources/logbackTomcat.xml rename to transitclockApi/src/main/resources/logbackTomcat.xml index 6d0694c40..18c0f83b3 100644 --- a/transitimeWebapp/src/main/resources/logbackTomcat.xml +++ b/transitclockApi/src/main/resources/logbackTomcat.xml @@ -7,14 +7,14 @@ + value="${transitclock.logging.dir:-/Logs}/tomcat/%d{yyyy/MM/dd}" /> - @@ -95,7 +95,7 @@ level to debug for the root because then get a huge number of unuseful Hibernate messages. --> - diff --git a/transitclockApi/src/main/resources/transiTimeConfig.xml b/transitclockApi/src/main/resources/transiTimeConfig.xml new file mode 100755 index 000000000..4a1839124 --- /dev/null +++ b/transitclockApi/src/main/resources/transiTimeConfig.xml @@ -0,0 +1,53 @@ + + + + + + + 30000 + 5 + + + false + false + 180 + 180 + + + 1 + + + + http://developer.onebusaway.org/wmata-gtfsr/vehiclePositions + + + false + -360.0 + 360.0 + -360.0 + 360.0 + + + + + 30000 + 180000 + + + transitime-mnrt + localhost + mysql + root + changeme + + + /Users/sbrown/src/mnrt/transitime/transitimeWebapp/src/main/resources/mysql_hibernate.cfg.xml + + + false + + diff --git a/transitclockApi/src/main/webapp/WEB-INF/web.xml b/transitclockApi/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..8265beac4 --- /dev/null +++ b/transitclockApi/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,88 @@ + + + The Transit Clock api + + + transitclock_config_file_location + + ${transitclock.configFiles} + + + + + + + org.transitclock.web.ReadConfigListener + + + + + 416 + /error/messageOnlyError.jsp + + + + + ApiLoggingFilter + org.transitclock.api.utils.ApiLoggingFilter + + + ApiLoggingFilter + /api/* + + + + + + + jersey + org.glassfish.jersey.servlet.ServletContainer + + jersey.config.server.wadl.disableWadl + true + + + jersey.config.server.provider.packages + + + io.swagger.v3.jaxrs2.integration.resources, org.transitclock.api.rootResources,org.transitclock.api.utils + + + + openApi.configuration.prettyPrint + true + + + 1 + + + + jersey + /doc/* + + + + ApiOriginFilter + org.transitclock.servlet.swagger.ApiOriginFilter + + + + ApiOriginFilter + /* + + + + + + diff --git a/transitclockApi/src/main/webapp/doc.html b/transitclockApi/src/main/webapp/doc.html new file mode 100644 index 000000000..2960e1010 --- /dev/null +++ b/transitclockApi/src/main/webapp/doc.html @@ -0,0 +1,64 @@ + + + + + + Swagger UI + + + + + + + + +

    + + +
    + + + + + + diff --git a/transitimeApi/src/main/webapp/index.jsp b/transitclockApi/src/main/webapp/index.jsp similarity index 62% rename from transitimeApi/src/main/webapp/index.jsp rename to transitclockApi/src/main/webapp/index.jsp index 50a8a5a2b..0b1e2800b 100644 --- a/transitimeApi/src/main/webapp/index.jsp +++ b/transitclockApi/src/main/webapp/index.jsp @@ -1,7 +1,7 @@

    Transitime API

    -

    Documentation on Transitime API +

    Documentation on Transitime API

    Implementations of agencies, including real-time maps diff --git a/transitclockApi/src/main/webapp/oauth2-redirect.html b/transitclockApi/src/main/webapp/oauth2-redirect.html new file mode 100644 index 000000000..fb68399d2 --- /dev/null +++ b/transitclockApi/src/main/webapp/oauth2-redirect.html @@ -0,0 +1,67 @@ + + + + + + diff --git a/transitclockApi/src/main/webapp/swagger-ui-bundle.js b/transitclockApi/src/main/webapp/swagger-ui-bundle.js new file mode 100644 index 000000000..2215aa3a1 --- /dev/null +++ b/transitclockApi/src/main/webapp/swagger-ui-bundle.js @@ -0,0 +1,104 @@ +!function webpackUniversalModuleDefinition(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.SwaggerUIBundle=t():e.SwaggerUIBundle=t()}(this,function(){return function(e){var t={};function __webpack_require__(r){if(t[r])return t[r].exports;var n=t[r]={i:r,l:!1,exports:{}};return e[r].call(n.exports,n,n.exports,__webpack_require__),n.l=!0,n.exports}return __webpack_require__.m=e,__webpack_require__.c=t,__webpack_require__.d=function(e,t,r){__webpack_require__.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:r})},__webpack_require__.n=function(e){var t=e&&e.__esModule?function getDefault(){return e.default}:function getModuleExports(){return e};return __webpack_require__.d(t,"a",t),t},__webpack_require__.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},__webpack_require__.p="/dist",__webpack_require__(__webpack_require__.s=491)}([function(e,t,r){"use strict";e.exports=r(77)},function(e,t,r){e.exports=r(898)()},function(e,t,r){"use strict";t.__esModule=!0,t.default=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}},function(e,t,r){"use strict";t.__esModule=!0;var n=function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}(r(285));t.default=function(){function defineProperties(e,t){for(var r=0;r>>0;if(""+r!==t||4294967295===r)return NaN;t=r}return t<0?ensureSize(e)+t:t}function returnTrue(){return!0}function wholeSlice(e,t,r){return(0===e||void 0!==r&&e<=-r)&&(void 0===t||void 0!==r&&t>=r)}function resolveBegin(e,t){return resolveIndex(e,t,0)}function resolveEnd(e,t){return resolveIndex(e,t,t)}function resolveIndex(e,t,r){return void 0===e?r:e<0?Math.max(0,t+e):void 0===t?e:Math.min(t,e)}var p=0,f=1,d=2,h="function"==typeof Symbol&&Symbol.iterator,m="@@iterator",v=h||m;function Iterator(e){this.next=e}function iteratorValue(e,t,r,n){var i=0===e?t:1===e?r:[t,r];return n?n.value=i:n={value:i,done:!1},n}function iteratorDone(){return{value:void 0,done:!0}}function hasIterator(e){return!!getIteratorFn(e)}function isIterator(e){return e&&"function"==typeof e.next}function getIterator(e){var t=getIteratorFn(e);return t&&t.call(e)}function getIteratorFn(e){var t=e&&(h&&e[h]||e[m]);if("function"==typeof t)return t}function isArrayLike(e){return e&&"number"==typeof e.length}function Seq(e){return null===e||void 0===e?emptySequence():isIterable(e)?e.toSeq():function seqFromValue(e){var t=maybeIndexedSeqFromValue(e)||"object"==typeof e&&new ObjectSeq(e);if(!t)throw new TypeError("Expected Array or iterable object of values, or keyed object: "+e);return t}(e)}function KeyedSeq(e){return null===e||void 0===e?emptySequence().toKeyedSeq():isIterable(e)?isKeyed(e)?e.toSeq():e.fromEntrySeq():keyedSeqFromValue(e)}function IndexedSeq(e){return null===e||void 0===e?emptySequence():isIterable(e)?isKeyed(e)?e.entrySeq():e.toIndexedSeq():indexedSeqFromValue(e)}function SetSeq(e){return(null===e||void 0===e?emptySequence():isIterable(e)?isKeyed(e)?e.entrySeq():e:indexedSeqFromValue(e)).toSetSeq()}Iterator.prototype.toString=function(){return"[Iterator]"},Iterator.KEYS=p,Iterator.VALUES=f,Iterator.ENTRIES=d,Iterator.prototype.inspect=Iterator.prototype.toSource=function(){return this.toString()},Iterator.prototype[v]=function(){return this},createClass(Seq,Iterable),Seq.of=function(){return Seq(arguments)},Seq.prototype.toSeq=function(){return this},Seq.prototype.toString=function(){return this.__toString("Seq {","}")},Seq.prototype.cacheResult=function(){return!this._cache&&this.__iterateUncached&&(this._cache=this.entrySeq().toArray(),this.size=this._cache.length),this},Seq.prototype.__iterate=function(e,t){return seqIterate(this,e,t,!0)},Seq.prototype.__iterator=function(e,t){return seqIterator(this,e,t,!0)},createClass(KeyedSeq,Seq),KeyedSeq.prototype.toKeyedSeq=function(){return this},createClass(IndexedSeq,Seq),IndexedSeq.of=function(){return IndexedSeq(arguments)},IndexedSeq.prototype.toIndexedSeq=function(){return this},IndexedSeq.prototype.toString=function(){return this.__toString("Seq [","]")},IndexedSeq.prototype.__iterate=function(e,t){return seqIterate(this,e,t,!1)},IndexedSeq.prototype.__iterator=function(e,t){return seqIterator(this,e,t,!1)},createClass(SetSeq,Seq),SetSeq.of=function(){return SetSeq(arguments)},SetSeq.prototype.toSetSeq=function(){return this},Seq.isSeq=isSeq,Seq.Keyed=KeyedSeq,Seq.Set=SetSeq,Seq.Indexed=IndexedSeq;var g,y,_,b="@@__IMMUTABLE_SEQ__@@";function ArraySeq(e){this._array=e,this.size=e.length}function ObjectSeq(e){var t=Object.keys(e);this._object=e,this._keys=t,this.size=t.length}function IterableSeq(e){this._iterable=e,this.size=e.length||e.size}function IteratorSeq(e){this._iterator=e,this._iteratorCache=[]}function isSeq(e){return!(!e||!e[b])}function emptySequence(){return g||(g=new ArraySeq([]))}function keyedSeqFromValue(e){var t=Array.isArray(e)?new ArraySeq(e).fromEntrySeq():isIterator(e)?new IteratorSeq(e).fromEntrySeq():hasIterator(e)?new IterableSeq(e).fromEntrySeq():"object"==typeof e?new ObjectSeq(e):void 0;if(!t)throw new TypeError("Expected Array or iterable object of [k, v] entries, or keyed object: "+e);return t}function indexedSeqFromValue(e){var t=maybeIndexedSeqFromValue(e);if(!t)throw new TypeError("Expected Array or iterable object of values: "+e);return t}function maybeIndexedSeqFromValue(e){return isArrayLike(e)?new ArraySeq(e):isIterator(e)?new IteratorSeq(e):hasIterator(e)?new IterableSeq(e):void 0}function seqIterate(e,t,r,n){var i=e._cache;if(i){for(var o=i.length-1,a=0;a<=o;a++){var s=i[r?o-a:a];if(!1===t(s[1],n?s[0]:a,e))return a+1}return a}return e.__iterateUncached(t,r)}function seqIterator(e,t,r,n){var i=e._cache;if(i){var o=i.length-1,a=0;return new Iterator(function(){var e=i[r?o-a:a];return a++>o?{value:void 0,done:!0}:iteratorValue(t,n?e[0]:a-1,e[1])})}return e.__iteratorUncached(t,r)}function fromJS(e,t){return t?function fromJSWith(e,t,r,n){if(Array.isArray(t))return e.call(n,r,IndexedSeq(t).map(function(r,n){return fromJSWith(e,r,n,t)}));if(isPlainObj(t))return e.call(n,r,KeyedSeq(t).map(function(r,n){return fromJSWith(e,r,n,t)}));return t}(t,e,"",{"":e}):fromJSDefault(e)}function fromJSDefault(e){return Array.isArray(e)?IndexedSeq(e).map(fromJSDefault).toList():isPlainObj(e)?KeyedSeq(e).map(fromJSDefault).toMap():e}function isPlainObj(e){return e&&(e.constructor===Object||void 0===e.constructor)}function is(e,t){if(e===t||e!=e&&t!=t)return!0;if(!e||!t)return!1;if("function"==typeof e.valueOf&&"function"==typeof t.valueOf){if((e=e.valueOf())===(t=t.valueOf())||e!=e&&t!=t)return!0;if(!e||!t)return!1}return!("function"!=typeof e.equals||"function"!=typeof t.equals||!e.equals(t))}function deepEqual(e,t){if(e===t)return!0;if(!isIterable(t)||void 0!==e.size&&void 0!==t.size&&e.size!==t.size||void 0!==e.__hash&&void 0!==t.__hash&&e.__hash!==t.__hash||isKeyed(e)!==isKeyed(t)||isIndexed(e)!==isIndexed(t)||isOrdered(e)!==isOrdered(t))return!1;if(0===e.size&&0===t.size)return!0;var r=!isAssociative(e);if(isOrdered(e)){var n=e.entries();return t.every(function(e,t){var i=n.next().value;return i&&is(i[1],e)&&(r||is(i[0],t))})&&n.next().done}var i=!1;if(void 0===e.size)if(void 0===t.size)"function"==typeof e.cacheResult&&e.cacheResult();else{i=!0;var o=e;e=t,t=o}var a=!0,s=t.__iterate(function(t,n){if(r?!e.has(t):i?!is(t,e.get(n,u)):!is(e.get(n,u),t))return a=!1,!1});return a&&e.size===s}function Repeat(e,t){if(!(this instanceof Repeat))return new Repeat(e,t);if(this._value=e,this.size=void 0===t?1/0:Math.max(0,t),0===this.size){if(y)return y;y=this}}function invariant(e,t){if(!e)throw new Error(t)}function Range(e,t,r){if(!(this instanceof Range))return new Range(e,t,r);if(invariant(0!==r,"Cannot step a Range by 0"),e=e||0,void 0===t&&(t=1/0),r=void 0===r?1:Math.abs(r),tn?{value:void 0,done:!0}:iteratorValue(e,i,r[t?n-i++:i++])})},createClass(ObjectSeq,KeyedSeq),ObjectSeq.prototype.get=function(e,t){return void 0===t||this.has(e)?this._object[e]:t},ObjectSeq.prototype.has=function(e){return this._object.hasOwnProperty(e)},ObjectSeq.prototype.__iterate=function(e,t){for(var r=this._object,n=this._keys,i=n.length-1,o=0;o<=i;o++){var a=n[t?i-o:o];if(!1===e(r[a],a,this))return o+1}return o},ObjectSeq.prototype.__iterator=function(e,t){var r=this._object,n=this._keys,i=n.length-1,o=0;return new Iterator(function(){var a=n[t?i-o:o];return o++>i?{value:void 0,done:!0}:iteratorValue(e,a,r[a])})},ObjectSeq.prototype[i]=!0,createClass(IterableSeq,IndexedSeq),IterableSeq.prototype.__iterateUncached=function(e,t){if(t)return this.cacheResult().__iterate(e,t);var r=getIterator(this._iterable),n=0;if(isIterator(r))for(var i;!(i=r.next()).done&&!1!==e(i.value,n++,this););return n},IterableSeq.prototype.__iteratorUncached=function(e,t){if(t)return this.cacheResult().__iterator(e,t);var r=getIterator(this._iterable);if(!isIterator(r))return new Iterator(iteratorDone);var n=0;return new Iterator(function(){var t=r.next();return t.done?t:iteratorValue(e,n++,t.value)})},createClass(IteratorSeq,IndexedSeq),IteratorSeq.prototype.__iterateUncached=function(e,t){if(t)return this.cacheResult().__iterate(e,t);for(var r,n=this._iterator,i=this._iteratorCache,o=0;o=n.length){var t=r.next();if(t.done)return t;n[i]=t.value}return iteratorValue(e,i,n[i++])})},createClass(Repeat,IndexedSeq),Repeat.prototype.toString=function(){return 0===this.size?"Repeat []":"Repeat [ "+this._value+" "+this.size+" times ]"},Repeat.prototype.get=function(e,t){return this.has(e)?this._value:t},Repeat.prototype.includes=function(e){return is(this._value,e)},Repeat.prototype.slice=function(e,t){var r=this.size;return wholeSlice(e,t,r)?this:new Repeat(this._value,resolveEnd(t,r)-resolveBegin(e,r))},Repeat.prototype.reverse=function(){return this},Repeat.prototype.indexOf=function(e){return is(this._value,e)?0:-1},Repeat.prototype.lastIndexOf=function(e){return is(this._value,e)?this.size:-1},Repeat.prototype.__iterate=function(e,t){for(var r=0;r=0&&t=0&&rr?{value:void 0,done:!0}:iteratorValue(e,o++,a)})},Range.prototype.equals=function(e){return e instanceof Range?this._start===e._start&&this._end===e._end&&this._step===e._step:deepEqual(this,e)},createClass(Collection,Iterable),createClass(KeyedCollection,Collection),createClass(IndexedCollection,Collection),createClass(SetCollection,Collection),Collection.Keyed=KeyedCollection,Collection.Indexed=IndexedCollection,Collection.Set=SetCollection;var S="function"==typeof Math.imul&&-2===Math.imul(4294967295,2)?Math.imul:function imul(e,t){var r=65535&(e|=0),n=65535&(t|=0);return r*n+((e>>>16)*n+r*(t>>>16)<<16>>>0)|0};function smi(e){return e>>>1&1073741824|3221225471&e}function hash(e){if(!1===e||null===e||void 0===e)return 0;if("function"==typeof e.valueOf&&(!1===(e=e.valueOf())||null===e||void 0===e))return 0;if(!0===e)return 1;var t=typeof e;if("number"===t){if(e!=e||e===1/0)return 0;var r=0|e;for(r!==e&&(r^=4294967295*e);e>4294967295;)r^=e/=4294967295;return smi(r)}if("string"===t)return e.length>A?function cachedHashString(e){var t=T[e];void 0===t&&(t=hashString(e),M===R&&(M=0,T={}),M++,T[e]=t);return t}(e):hashString(e);if("function"==typeof e.hashCode)return e.hashCode();if("object"===t)return function hashJSObj(e){var t;if(C&&void 0!==(t=E.get(e)))return t;if(void 0!==(t=e[D]))return t;if(!x){if(void 0!==(t=e.propertyIsEnumerable&&e.propertyIsEnumerable[D]))return t;if(void 0!==(t=function getIENodeHash(e){if(e&&e.nodeType>0)switch(e.nodeType){case 1:return e.uniqueID;case 9:return e.documentElement&&e.documentElement.uniqueID}}(e)))return t}t=++w,1073741824&w&&(w=0);if(C)E.set(e,t);else{if(void 0!==k&&!1===k(e))throw new Error("Non-extensible objects are not allowed as keys.");if(x)Object.defineProperty(e,D,{enumerable:!1,configurable:!1,writable:!1,value:t});else if(void 0!==e.propertyIsEnumerable&&e.propertyIsEnumerable===e.constructor.prototype.propertyIsEnumerable)e.propertyIsEnumerable=function(){return this.constructor.prototype.propertyIsEnumerable.apply(this,arguments)},e.propertyIsEnumerable[D]=t;else{if(void 0===e.nodeType)throw new Error("Unable to set a non-enumerable property on object.");e[D]=t}}return t}(e);if("function"==typeof e.toString)return hashString(e.toString());throw new Error("Value type "+t+" cannot be hashed.")}function hashString(e){for(var t=0,r=0;r=t.length)throw new Error("Missing value for key: "+t[r]);e.set(t[r],t[r+1])}})},Map.prototype.toString=function(){return this.__toString("Map {","}")},Map.prototype.get=function(e,t){return this._root?this._root.get(0,void 0,e,t):t},Map.prototype.set=function(e,t){return updateMap(this,e,t)},Map.prototype.setIn=function(e,t){return this.updateIn(e,u,function(){return t})},Map.prototype.remove=function(e){return updateMap(this,e,u)},Map.prototype.deleteIn=function(e){return this.updateIn(e,function(){return u})},Map.prototype.update=function(e,t,r){return 1===arguments.length?e(this):this.updateIn([e],t,r)},Map.prototype.updateIn=function(e,t,r){r||(r=t,t=void 0);var n=function updateInDeepMap(e,t,r,n){var i=e===u;var o=t.next();if(o.done){var a=i?r:e,s=n(a);return s===a?e:s}invariant(i||e&&e.set,"invalid keyPath");var l=o.value;var c=i?u:e.get(l,u);var p=updateInDeepMap(c,t,r,n);return p===c?e:p===u?e.remove(l):(i?emptyMap():e).set(l,p)}(this,forceIterator(e),t,r);return n===u?void 0:n},Map.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._root=null,this.__hash=void 0,this.__altered=!0,this):emptyMap()},Map.prototype.merge=function(){return mergeIntoMapWith(this,void 0,arguments)},Map.prototype.mergeWith=function(t){return mergeIntoMapWith(this,t,e.call(arguments,1))},Map.prototype.mergeIn=function(t){var r=e.call(arguments,1);return this.updateIn(t,emptyMap(),function(e){return"function"==typeof e.merge?e.merge.apply(e,r):r[r.length-1]})},Map.prototype.mergeDeep=function(){return mergeIntoMapWith(this,deepMerger,arguments)},Map.prototype.mergeDeepWith=function(t){var r=e.call(arguments,1);return mergeIntoMapWith(this,deepMergerWith(t),r)},Map.prototype.mergeDeepIn=function(t){var r=e.call(arguments,1);return this.updateIn(t,emptyMap(),function(e){return"function"==typeof e.mergeDeep?e.mergeDeep.apply(e,r):r[r.length-1]})},Map.prototype.sort=function(e){return OrderedMap(sortFactory(this,e))},Map.prototype.sortBy=function(e,t){return OrderedMap(sortFactory(this,t,e))},Map.prototype.withMutations=function(e){var t=this.asMutable();return e(t),t.wasAltered()?t.__ensureOwner(this.__ownerID):this},Map.prototype.asMutable=function(){return this.__ownerID?this:this.__ensureOwner(new OwnerID)},Map.prototype.asImmutable=function(){return this.__ensureOwner()},Map.prototype.wasAltered=function(){return this.__altered},Map.prototype.__iterator=function(e,t){return new MapIterator(this,e,t)},Map.prototype.__iterate=function(e,t){var r=this,n=0;return this._root&&this._root.iterate(function(t){return n++,e(t[1],t[0],r)},t),n},Map.prototype.__ensureOwner=function(e){return e===this.__ownerID?this:e?makeMap(this.size,this._root,e,this.__hash):(this.__ownerID=e,this.__altered=!1,this)},Map.isMap=isMap;var O,P="@@__IMMUTABLE_MAP__@@",I=Map.prototype;function ArrayMapNode(e,t){this.ownerID=e,this.entries=t}function BitmapIndexedNode(e,t,r){this.ownerID=e,this.bitmap=t,this.nodes=r}function HashArrayMapNode(e,t,r){this.ownerID=e,this.count=t,this.nodes=r}function HashCollisionNode(e,t,r){this.ownerID=e,this.keyHash=t,this.entries=r}function ValueNode(e,t,r){this.ownerID=e,this.keyHash=t,this.entry=r}function MapIterator(e,t,r){this._type=t,this._reverse=r,this._stack=e._root&&mapIteratorFrame(e._root)}function mapIteratorValue(e,t){return iteratorValue(e,t[0],t[1])}function mapIteratorFrame(e,t){return{node:e,index:0,__prev:t}}function makeMap(e,t,r,n){var i=Object.create(I);return i.size=e,i._root=t,i.__ownerID=r,i.__hash=n,i.__altered=!1,i}function emptyMap(){return O||(O=makeMap(0))}function updateMap(e,t,r){var n,i;if(e._root){var o=MakeRef(l),a=MakeRef(c);if(n=updateNode(e._root,e.__ownerID,0,void 0,t,r,o,a),!a.value)return e;i=e.size+(o.value?r===u?-1:1:0)}else{if(r===u)return e;i=1,n=new ArrayMapNode(e.__ownerID,[[t,r]])}return e.__ownerID?(e.size=i,e._root=n,e.__hash=void 0,e.__altered=!0,e):n?makeMap(i,n):emptyMap()}function updateNode(e,t,r,n,i,o,a,s){return e?e.update(t,r,n,i,o,a,s):o===u?e:(SetRef(s),SetRef(a),new ValueNode(t,n,[i,o]))}function isLeafNode(e){return e.constructor===ValueNode||e.constructor===HashCollisionNode}function mergeIntoNode(e,t,r,n,i){if(e.keyHash===n)return new HashCollisionNode(t,n,[e.entry,i]);var a,u=(0===r?e.keyHash:e.keyHash>>>r)&s,l=(0===r?n:n>>>r)&s;return new BitmapIndexedNode(t,1<>1&1431655765))+(e>>2&858993459))+(e>>4)&252645135,e+=e>>8,127&(e+=e>>16)}function setIn(e,t,r,n){var i=n?e:arrCopy(e);return i[t]=r,i}I[P]=!0,I.delete=I.remove,I.removeIn=I.deleteIn,ArrayMapNode.prototype.get=function(e,t,r,n){for(var i=this.entries,o=0,a=i.length;o=q)return function createNodes(e,t,r,n){e||(e=new OwnerID);for(var i=new ValueNode(e,hash(r),[r,n]),o=0;o>>e)&s),a=this.bitmap;return 0==(a&i)?n:this.nodes[popCount(a&i-1)].get(e+o,t,r,n)},BitmapIndexedNode.prototype.update=function(e,t,r,n,i,l,c){void 0===r&&(r=hash(n));var p=(0===t?r:r>>>t)&s,f=1<=F)return function expandNodes(e,t,r,n,i){for(var o=0,s=new Array(a),u=0;0!==r;u++,r>>>=1)s[u]=1&r?t[o++]:void 0;return s[n]=i,new HashArrayMapNode(e,o+1,s)}(e,v,d,p,y);if(h&&!y&&2===v.length&&isLeafNode(v[1^m]))return v[1^m];if(h&&y&&1===v.length&&isLeafNode(y))return y;var _=e&&e===this.ownerID,b=h?y?d:d^f:d|f,S=h?y?setIn(v,m,y,_):function spliceOut(e,t,r){var n=e.length-1;if(r&&t===n)return e.pop(),e;for(var i=new Array(n),o=0,a=0;a>>e)&s,a=this.nodes[i];return a?a.get(e+o,t,r,n):n},HashArrayMapNode.prototype.update=function(e,t,r,n,i,a,l){void 0===r&&(r=hash(n));var c=(0===t?r:r>>>t)&s,p=i===u,f=this.nodes,d=f[c];if(p&&!d)return this;var h=updateNode(d,e,t+o,r,n,i,a,l);if(h===d)return this;var m=this.count;if(d){if(!h&&--m0&&n=0&&e=e.size||t<0)return e.withMutations(function(e){t<0?setListBounds(e,t).set(0,r):setListBounds(e,0,t+1).set(t,r)});t+=e._origin;var n=e._tail,i=e._root,o=MakeRef(c);t>=getTailOffset(e._capacity)?n=updateVNode(n,e.__ownerID,0,t,r,o):i=updateVNode(i,e.__ownerID,e._level,t,r,o);if(!o.value)return e;if(e.__ownerID)return e._root=i,e._tail=n,e.__hash=void 0,e.__altered=!0,e;return makeList(e._origin,e._capacity,e._level,i,n)}(this,e,t)},List.prototype.remove=function(e){return this.has(e)?0===e?this.shift():e===this.size-1?this.pop():this.splice(e,1):this},List.prototype.insert=function(e,t){return this.splice(e,0,t)},List.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=this._origin=this._capacity=0,this._level=o,this._root=this._tail=null,this.__hash=void 0,this.__altered=!0,this):emptyList()},List.prototype.push=function(){var e=arguments,t=this.size;return this.withMutations(function(r){setListBounds(r,0,t+e.length);for(var n=0;n>>t&s;if(n>=this.array.length)return new VNode([],e);var i,a=0===n;if(t>0){var u=this.array[n];if((i=u&&u.removeBefore(e,t-o,r))===u&&a)return this}if(a&&!i)return this;var l=editableVNode(this,e);if(!a)for(var c=0;c>>t&s;if(i>=this.array.length)return this;if(t>0){var a=this.array[i];if((n=a&&a.removeAfter(e,t-o,r))===a&&i===this.array.length-1)return this}var u=editableVNode(this,e);return u.array.splice(i+1),n&&(u.array[i]=n),u};var L,z,U={};function iterateList(e,t){var r=e._origin,n=e._capacity,i=getTailOffset(n),s=e._tail;return iterateNodeOrLeaf(e._root,e._level,0);function iterateNodeOrLeaf(e,u,l){return 0===u?function iterateLeaf(e,o){var u=o===i?s&&s.array:e&&e.array,l=o>r?0:r-o,c=n-o;c>a&&(c=a);return function(){if(l===c)return U;var e=t?--c:l++;return u&&u[e]}}(e,l):function iterateNode(e,i,s){var u,l=e&&e.array,c=s>r?0:r-s>>i,p=1+(n-s>>i);p>a&&(p=a);return function(){for(;;){if(u){var e=u();if(e!==U)return e;u=null}if(c===p)return U;var r=t?--p:c++;u=iterateNodeOrLeaf(l&&l[r],i-o,s+(r<>>r&s,c=e&&l0){var p=e&&e.array[l],f=updateVNode(p,t,r-o,n,i,a);return f===p?e:((u=editableVNode(e,t)).array[l]=f,u)}return c&&e.array[l]===i?e:(SetRef(a),u=editableVNode(e,t),void 0===i&&l===u.array.length-1?u.array.pop():u.array[l]=i,u)}function editableVNode(e,t){return t&&e&&t===e.ownerID?e:new VNode(e?e.array.slice():[],t)}function listNodeFor(e,t){if(t>=getTailOffset(e._capacity))return e._tail;if(t<1<0;)r=r.array[t>>>n&s],n-=o;return r}}function setListBounds(e,t,r){void 0!==t&&(t|=0),void 0!==r&&(r|=0);var n=e.__ownerID||new OwnerID,i=e._origin,a=e._capacity,u=i+t,l=void 0===r?a:r<0?a+r:i+r;if(u===i&&l===a)return e;if(u>=l)return e.clear();for(var c=e._level,p=e._root,f=0;u+f<0;)p=new VNode(p&&p.array.length?[void 0,p]:[],n),f+=1<<(c+=o);f&&(u+=f,i+=f,l+=f,a+=f);for(var d=getTailOffset(a),h=getTailOffset(l);h>=1<d?new VNode([],n):m;if(m&&h>d&&uo;y-=o){var _=d>>>y&s;g=g.array[_]=editableVNode(g.array[_],n)}g.array[d>>>o&s]=m}if(l=h)u-=h,l-=h,c=o,p=null,v=v&&v.removeBefore(n,0,u);else if(u>i||h>>c&s;if(b!==h>>>c&s)break;b&&(f+=(1<i&&(p=p.removeBefore(n,c,u-f)),p&&hi&&(i=s.size),isIterable(a)||(s=s.map(function(e){return fromJS(e)})),n.push(s)}return i>e.size&&(e=e.setSize(i)),mergeIntoCollectionWith(e,t,n)}function getTailOffset(e){return e>>o<=a&&s.size>=2*o.size?(n=(i=s.filter(function(e,t){return void 0!==e&&l!==t})).toKeyedSeq().map(function(e){return e[0]}).flip().toMap(),e.__ownerID&&(n.__ownerID=i.__ownerID=e.__ownerID)):(n=o.remove(t),i=l===s.size-1?s.pop():s.set(l,void 0))}else if(c){if(r===s.get(l)[1])return e;n=o,i=s.set(l,[t,r])}else n=o.set(t,s.size),i=s.set(s.size,[t,r]);return e.__ownerID?(e.size=n.size,e._map=n,e._list=i,e.__hash=void 0,e):makeOrderedMap(n,i)}function ToKeyedSequence(e,t){this._iter=e,this._useKeys=t,this.size=e.size}function ToIndexedSequence(e){this._iter=e,this.size=e.size}function ToSetSequence(e){this._iter=e,this.size=e.size}function FromEntriesSequence(e){this._iter=e,this.size=e.size}function flipFactory(e){var t=makeSequence(e);return t._iter=e,t.size=e.size,t.flip=function(){return e},t.reverse=function(){var t=e.reverse.apply(this);return t.flip=function(){return e.reverse()},t},t.has=function(t){return e.includes(t)},t.includes=function(t){return e.has(t)},t.cacheResult=cacheResultThrough,t.__iterateUncached=function(t,r){var n=this;return e.__iterate(function(e,r){return!1!==t(r,e,n)},r)},t.__iteratorUncached=function(t,r){if(t===d){var n=e.__iterator(t,r);return new Iterator(function(){var e=n.next();if(!e.done){var t=e.value[0];e.value[0]=e.value[1],e.value[1]=t}return e})}return e.__iterator(t===f?p:f,r)},t}function mapFactory(e,t,r){var n=makeSequence(e);return n.size=e.size,n.has=function(t){return e.has(t)},n.get=function(n,i){var o=e.get(n,u);return o===u?i:t.call(r,o,n,e)},n.__iterateUncached=function(n,i){var o=this;return e.__iterate(function(e,i,a){return!1!==n(t.call(r,e,i,a),i,o)},i)},n.__iteratorUncached=function(n,i){var o=e.__iterator(d,i);return new Iterator(function(){var i=o.next();if(i.done)return i;var a=i.value,s=a[0];return iteratorValue(n,s,t.call(r,a[1],s,e),i)})},n}function reverseFactory(e,t){var r=makeSequence(e);return r._iter=e,r.size=e.size,r.reverse=function(){return e},e.flip&&(r.flip=function(){var t=flipFactory(e);return t.reverse=function(){return e.flip()},t}),r.get=function(r,n){return e.get(t?r:-1-r,n)},r.has=function(r){return e.has(t?r:-1-r)},r.includes=function(t){return e.includes(t)},r.cacheResult=cacheResultThrough,r.__iterate=function(t,r){var n=this;return e.__iterate(function(e,r){return t(e,r,n)},!r)},r.__iterator=function(t,r){return e.__iterator(t,!r)},r}function filterFactory(e,t,r,n){var i=makeSequence(e);return n&&(i.has=function(n){var i=e.get(n,u);return i!==u&&!!t.call(r,i,n,e)},i.get=function(n,i){var o=e.get(n,u);return o!==u&&t.call(r,o,n,e)?o:i}),i.__iterateUncached=function(i,o){var a=this,s=0;return e.__iterate(function(e,o,u){if(t.call(r,e,o,u))return s++,i(e,n?o:s-1,a)},o),s},i.__iteratorUncached=function(i,o){var a=e.__iterator(d,o),s=0;return new Iterator(function(){for(;;){var o=a.next();if(o.done)return o;var u=o.value,l=u[0],c=u[1];if(t.call(r,c,l,e))return iteratorValue(i,n?l:s++,c,o)}})},i}function sliceFactory(e,t,r,n){var i=e.size;if(void 0!==t&&(t|=0),void 0!==r&&(r===1/0?r=i:r|=0),wholeSlice(t,r,i))return e;var o=resolveBegin(t,i),a=resolveEnd(r,i);if(o!=o||a!=a)return sliceFactory(e.toSeq().cacheResult(),t,r,n);var s,u=a-o;u==u&&(s=u<0?0:u);var l=makeSequence(e);return l.size=0===s?s:e.size&&s||void 0,!n&&isSeq(e)&&s>=0&&(l.get=function(t,r){return(t=wrapIndex(this,t))>=0&&ts)return{value:void 0,done:!0};var e=i.next();return n||t===f?e:iteratorValue(t,u-1,t===p?void 0:e.value[1],e)})},l}function skipWhileFactory(e,t,r,n){var i=makeSequence(e);return i.__iterateUncached=function(i,o){var a=this;if(o)return this.cacheResult().__iterate(i,o);var s=!0,u=0;return e.__iterate(function(e,o,l){if(!s||!(s=t.call(r,e,o,l)))return u++,i(e,n?o:u-1,a)}),u},i.__iteratorUncached=function(i,o){var a=this;if(o)return this.cacheResult().__iterator(i,o);var s=e.__iterator(d,o),u=!0,l=0;return new Iterator(function(){var e,o,c;do{if((e=s.next()).done)return n||i===f?e:iteratorValue(i,l++,i===p?void 0:e.value[1],e);var h=e.value;o=h[0],c=h[1],u&&(u=t.call(r,c,o,a))}while(u);return i===d?e:iteratorValue(i,o,c,e)})},i}function flattenFactory(e,t,r){var n=makeSequence(e);return n.__iterateUncached=function(n,i){var o=0,a=!1;return function flatDeep(e,s){var u=this;e.__iterate(function(e,i){return(!t||s0}function zipWithFactory(e,t,r){var n=makeSequence(e);return n.size=new ArraySeq(r).map(function(e){return e.size}).min(),n.__iterate=function(e,t){for(var r,n=this.__iterator(f,t),i=0;!(r=n.next()).done&&!1!==e(r.value,i++,this););return i},n.__iteratorUncached=function(e,n){var i=r.map(function(e){return e=Iterable(e),getIterator(n?e.reverse():e)}),o=0,a=!1;return new Iterator(function(){var r;return a||(r=i.map(function(e){return e.next()}),a=r.some(function(e){return e.done})),a?{value:void 0,done:!0}:iteratorValue(e,o++,t.apply(null,r.map(function(e){return e.value})))})},n}function reify(e,t){return isSeq(e)?t:e.constructor(t)}function validateEntry(e){if(e!==Object(e))throw new TypeError("Expected [K, V] tuple: "+e)}function resolveSize(e){return assertNotInfinite(e.size),ensureSize(e)}function iterableClass(e){return isKeyed(e)?KeyedIterable:isIndexed(e)?IndexedIterable:SetIterable}function makeSequence(e){return Object.create((isKeyed(e)?KeyedSeq:isIndexed(e)?IndexedSeq:SetSeq).prototype)}function cacheResultThrough(){return this._iter.cacheResult?(this._iter.cacheResult(),this.size=this._iter.size,this):Seq.prototype.cacheResult.call(this)}function defaultComparator(e,t){return e>t?1:e=0;r--)t={value:arguments[r],next:t};return this.__ownerID?(this.size=e,this._head=t,this.__hash=void 0,this.__altered=!0,this):makeStack(e,t)},Stack.prototype.pushAll=function(e){if(0===(e=IndexedIterable(e)).size)return this;assertNotInfinite(e.size);var t=this.size,r=this._head;return e.reverse().forEach(function(e){t++,r={value:e,next:r}}),this.__ownerID?(this.size=t,this._head=r,this.__hash=void 0,this.__altered=!0,this):makeStack(t,r)},Stack.prototype.pop=function(){return this.slice(1)},Stack.prototype.unshift=function(){return this.push.apply(this,arguments)},Stack.prototype.unshiftAll=function(e){return this.pushAll(e)},Stack.prototype.shift=function(){return this.pop.apply(this,arguments)},Stack.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._head=void 0,this.__hash=void 0,this.__altered=!0,this):emptyStack()},Stack.prototype.slice=function(e,t){if(wholeSlice(e,t,this.size))return this;var r=resolveBegin(e,this.size);if(resolveEnd(t,this.size)!==this.size)return IndexedCollection.prototype.slice.call(this,e,t);for(var n=this.size-r,i=this._head;r--;)i=i.next;return this.__ownerID?(this.size=n,this._head=i,this.__hash=void 0,this.__altered=!0,this):makeStack(n,i)},Stack.prototype.__ensureOwner=function(e){return e===this.__ownerID?this:e?makeStack(this.size,this._head,e,this.__hash):(this.__ownerID=e,this.__altered=!1,this)},Stack.prototype.__iterate=function(e,t){if(t)return this.reverse().__iterate(e);for(var r=0,n=this._head;n&&!1!==e(n.value,r++,this);)n=n.next;return r},Stack.prototype.__iterator=function(e,t){if(t)return this.reverse().__iterator(e);var r=0,n=this._head;return new Iterator(function(){if(n){var t=n.value;return n=n.next,iteratorValue(e,r++,t)}return{value:void 0,done:!0}})},Stack.isStack=isStack;var X,Y="@@__IMMUTABLE_STACK__@@",$=Stack.prototype;function makeStack(e,t,r,n){var i=Object.create($);return i.size=e,i._head=t,i.__ownerID=r,i.__hash=n,i.__altered=!1,i}function emptyStack(){return X||(X=makeStack(0))}function mixin(e,t){var r=function(r){e.prototype[r]=t[r]};return Object.keys(t).forEach(r),Object.getOwnPropertySymbols&&Object.getOwnPropertySymbols(t).forEach(r),e}$[Y]=!0,$.withMutations=I.withMutations,$.asMutable=I.asMutable,$.asImmutable=I.asImmutable,$.wasAltered=I.wasAltered,Iterable.Iterator=Iterator,mixin(Iterable,{toArray:function(){assertNotInfinite(this.size);var e=new Array(this.size||0);return this.valueSeq().__iterate(function(t,r){e[r]=t}),e},toIndexedSeq:function(){return new ToIndexedSequence(this)},toJS:function(){return this.toSeq().map(function(e){return e&&"function"==typeof e.toJS?e.toJS():e}).__toJS()},toJSON:function(){return this.toSeq().map(function(e){return e&&"function"==typeof e.toJSON?e.toJSON():e}).__toJS()},toKeyedSeq:function(){return new ToKeyedSequence(this,!0)},toMap:function(){return Map(this.toKeyedSeq())},toObject:function(){assertNotInfinite(this.size);var e={};return this.__iterate(function(t,r){e[r]=t}),e},toOrderedMap:function(){return OrderedMap(this.toKeyedSeq())},toOrderedSet:function(){return OrderedSet(isKeyed(this)?this.valueSeq():this)},toSet:function(){return Set(isKeyed(this)?this.valueSeq():this)},toSetSeq:function(){return new ToSetSequence(this)},toSeq:function(){return isIndexed(this)?this.toIndexedSeq():isKeyed(this)?this.toKeyedSeq():this.toSetSeq()},toStack:function(){return Stack(isKeyed(this)?this.valueSeq():this)},toList:function(){return List(isKeyed(this)?this.valueSeq():this)},toString:function(){return"[Iterable]"},__toString:function(e,t){return 0===this.size?e+t:e+" "+this.toSeq().map(this.__toStringMapper).join(", ")+" "+t},concat:function(){return reify(this,function concatFactory(e,t){var r=isKeyed(e),n=[e].concat(t).map(function(e){return isIterable(e)?r&&(e=KeyedIterable(e)):e=r?keyedSeqFromValue(e):indexedSeqFromValue(Array.isArray(e)?e:[e]),e}).filter(function(e){return 0!==e.size});if(0===n.length)return e;if(1===n.length){var i=n[0];if(i===e||r&&isKeyed(i)||isIndexed(e)&&isIndexed(i))return i}var o=new ArraySeq(n);return r?o=o.toKeyedSeq():isIndexed(e)||(o=o.toSetSeq()),(o=o.flatten(!0)).size=n.reduce(function(e,t){if(void 0!==e){var r=t.size;if(void 0!==r)return e+r}},0),o}(this,e.call(arguments,0)))},includes:function(e){return this.some(function(t){return is(t,e)})},entries:function(){return this.__iterator(d)},every:function(e,t){assertNotInfinite(this.size);var r=!0;return this.__iterate(function(n,i,o){if(!e.call(t,n,i,o))return r=!1,!1}),r},filter:function(e,t){return reify(this,filterFactory(this,e,t,!0))},find:function(e,t,r){var n=this.findEntry(e,t);return n?n[1]:r},forEach:function(e,t){return assertNotInfinite(this.size),this.__iterate(t?e.bind(t):e)},join:function(e){assertNotInfinite(this.size),e=void 0!==e?""+e:",";var t="",r=!0;return this.__iterate(function(n){r?r=!1:t+=e,t+=null!==n&&void 0!==n?n.toString():""}),t},keys:function(){return this.__iterator(p)},map:function(e,t){return reify(this,mapFactory(this,e,t))},reduce:function(e,t,r){var n,i;return assertNotInfinite(this.size),arguments.length<2?i=!0:n=t,this.__iterate(function(t,o,a){i?(i=!1,n=t):n=e.call(r,n,t,o,a)}),n},reduceRight:function(e,t,r){var n=this.toKeyedSeq().reverse();return n.reduce.apply(n,arguments)},reverse:function(){return reify(this,reverseFactory(this,!0))},slice:function(e,t){return reify(this,sliceFactory(this,e,t,!0))},some:function(e,t){return!this.every(not(e),t)},sort:function(e){return reify(this,sortFactory(this,e))},values:function(){return this.__iterator(f)},butLast:function(){return this.slice(0,-1)},isEmpty:function(){return void 0!==this.size?0===this.size:!this.some(function(){return!0})},count:function(e,t){return ensureSize(e?this.toSeq().filter(e,t):this)},countBy:function(e,t){return function countByFactory(e,t,r){var n=Map().asMutable();return e.__iterate(function(i,o){n.update(t.call(r,i,o,e),0,function(e){return e+1})}),n.asImmutable()}(this,e,t)},equals:function(e){return deepEqual(this,e)},entrySeq:function(){var e=this;if(e._cache)return new ArraySeq(e._cache);var t=e.toSeq().map(entryMapper).toIndexedSeq();return t.fromEntrySeq=function(){return e.toSeq()},t},filterNot:function(e,t){return this.filter(not(e),t)},findEntry:function(e,t,r){var n=r;return this.__iterate(function(r,i,o){if(e.call(t,r,i,o))return n=[i,r],!1}),n},findKey:function(e,t){var r=this.findEntry(e,t);return r&&r[0]},findLast:function(e,t,r){return this.toKeyedSeq().reverse().find(e,t,r)},findLastEntry:function(e,t,r){return this.toKeyedSeq().reverse().findEntry(e,t,r)},findLastKey:function(e,t){return this.toKeyedSeq().reverse().findKey(e,t)},first:function(){return this.find(returnTrue)},flatMap:function(e,t){return reify(this,function flatMapFactory(e,t,r){var n=iterableClass(e);return e.toSeq().map(function(i,o){return n(t.call(r,i,o,e))}).flatten(!0)}(this,e,t))},flatten:function(e){return reify(this,flattenFactory(this,e,!0))},fromEntrySeq:function(){return new FromEntriesSequence(this)},get:function(e,t){return this.find(function(t,r){return is(r,e)},void 0,t)},getIn:function(e,t){for(var r,n=this,i=forceIterator(e);!(r=i.next()).done;){var o=r.value;if((n=n&&n.get?n.get(o,u):u)===u)return t}return n},groupBy:function(e,t){return function groupByFactory(e,t,r){var n=isKeyed(e),i=(isOrdered(e)?OrderedMap():Map()).asMutable();e.__iterate(function(o,a){i.update(t.call(r,o,a,e),function(e){return(e=e||[]).push(n?[a,o]:o),e})});var o=iterableClass(e);return i.map(function(t){return reify(e,o(t))})}(this,e,t)},has:function(e){return this.get(e,u)!==u},hasIn:function(e){return this.getIn(e,u)!==u},isSubset:function(e){return e="function"==typeof e.includes?e:Iterable(e),this.every(function(t){return e.includes(t)})},isSuperset:function(e){return(e="function"==typeof e.isSubset?e:Iterable(e)).isSubset(this)},keyOf:function(e){return this.findKey(function(t){return is(t,e)})},keySeq:function(){return this.toSeq().map(keyMapper).toIndexedSeq()},last:function(){return this.toSeq().reverse().first()},lastKeyOf:function(e){return this.toKeyedSeq().reverse().keyOf(e)},max:function(e){return maxFactory(this,e)},maxBy:function(e,t){return maxFactory(this,t,e)},min:function(e){return maxFactory(this,e?neg(e):defaultNegComparator)},minBy:function(e,t){return maxFactory(this,t?neg(t):defaultNegComparator,e)},rest:function(){return this.slice(1)},skip:function(e){return this.slice(Math.max(0,e))},skipLast:function(e){return reify(this,this.toSeq().reverse().skip(e).reverse())},skipWhile:function(e,t){return reify(this,skipWhileFactory(this,e,t,!0))},skipUntil:function(e,t){return this.skipWhile(not(e),t)},sortBy:function(e,t){return reify(this,sortFactory(this,t,e))},take:function(e){return this.slice(0,Math.max(0,e))},takeLast:function(e){return reify(this,this.toSeq().reverse().take(e).reverse())},takeWhile:function(e,t){return reify(this,function takeWhileFactory(e,t,r){var n=makeSequence(e);return n.__iterateUncached=function(n,i){var o=this;if(i)return this.cacheResult().__iterate(n,i);var a=0;return e.__iterate(function(e,i,s){return t.call(r,e,i,s)&&++a&&n(e,i,o)}),a},n.__iteratorUncached=function(n,i){var o=this;if(i)return this.cacheResult().__iterator(n,i);var a=e.__iterator(d,i),s=!0;return new Iterator(function(){if(!s)return{value:void 0,done:!0};var e=a.next();if(e.done)return e;var i=e.value,u=i[0],l=i[1];return t.call(r,l,u,o)?n===d?e:iteratorValue(n,u,l,e):(s=!1,{value:void 0,done:!0})})},n}(this,e,t))},takeUntil:function(e,t){return this.takeWhile(not(e),t)},valueSeq:function(){return this.toIndexedSeq()},hashCode:function(){return this.__hash||(this.__hash=function hashIterable(e){if(e.size===1/0)return 0;var t=isOrdered(e),r=isKeyed(e),n=t?1:0;return function murmurHashOfSize(e,t){return t=S(t,3432918353),t=S(t<<15|t>>>-15,461845907),t=S(t<<13|t>>>-13,5),t=S((t=(t+3864292196|0)^e)^t>>>16,2246822507),t=smi((t=S(t^t>>>13,3266489909))^t>>>16)}(e.__iterate(r?t?function(e,t){n=31*n+hashMerge(hash(e),hash(t))|0}:function(e,t){n=n+hashMerge(hash(e),hash(t))|0}:t?function(e){n=31*n+hash(e)|0}:function(e){n=n+hash(e)|0}),n)}(this))}});var Z=Iterable.prototype;Z[t]=!0,Z[v]=Z.values,Z.__toJS=Z.toArray,Z.__toStringMapper=quoteString,Z.inspect=Z.toSource=function(){return this.toString()},Z.chain=Z.flatMap,Z.contains=Z.includes,mixin(KeyedIterable,{flip:function(){return reify(this,flipFactory(this))},mapEntries:function(e,t){var r=this,n=0;return reify(this,this.toSeq().map(function(i,o){return e.call(t,[o,i],n++,r)}).fromEntrySeq())},mapKeys:function(e,t){var r=this;return reify(this,this.toSeq().flip().map(function(n,i){return e.call(t,n,i,r)}).flip())}});var Q=KeyedIterable.prototype;function keyMapper(e,t){return t}function entryMapper(e,t){return[t,e]}function not(e){return function(){return!e.apply(this,arguments)}}function neg(e){return function(){return-e.apply(this,arguments)}}function quoteString(e){return"string"==typeof e?JSON.stringify(e):String(e)}function defaultZipper(){return arrCopy(arguments)}function defaultNegComparator(e,t){return et?-1:0}function hashMerge(e,t){return e^t+2654435769+(e<<6)+(e>>2)|0}return Q[r]=!0,Q[v]=Z.entries,Q.__toJS=Z.toObject,Q.__toStringMapper=function(e,t){return JSON.stringify(t)+": "+quoteString(e)},mixin(IndexedIterable,{toKeyedSeq:function(){return new ToKeyedSequence(this,!1)},filter:function(e,t){return reify(this,filterFactory(this,e,t,!1))},findIndex:function(e,t){var r=this.findEntry(e,t);return r?r[0]:-1},indexOf:function(e){var t=this.keyOf(e);return void 0===t?-1:t},lastIndexOf:function(e){var t=this.lastKeyOf(e);return void 0===t?-1:t},reverse:function(){return reify(this,reverseFactory(this,!1))},slice:function(e,t){return reify(this,sliceFactory(this,e,t,!1))},splice:function(e,t){var r=arguments.length;if(t=Math.max(0|t,0),0===r||2===r&&!t)return this;e=resolveBegin(e,e<0?this.count():this.size);var n=this.slice(0,e);return reify(this,1===r?n:n.concat(arrCopy(arguments,2),this.slice(e+t)))},findLastIndex:function(e,t){var r=this.findLastEntry(e,t);return r?r[0]:-1},first:function(){return this.get(0)},flatten:function(e){return reify(this,flattenFactory(this,e,!1))},get:function(e,t){return(e=wrapIndex(this,e))<0||this.size===1/0||void 0!==this.size&&e>this.size?t:this.find(function(t,r){return r===e},void 0,t)},has:function(e){return(e=wrapIndex(this,e))>=0&&(void 0!==this.size?this.size===1/0||e5e3)return e.textContent;return function reset(e){for(var r,n,i,o,a,s=e.textContent,u=0,l=s[0],c=1,p=e.innerHTML="",f=0;n=r,r=f<7&&"\\"==r?1:c;){if(c=l,l=s[++u],o=p.length>1,!c||f>8&&"\n"==c||[/\S/.test(c),1,1,!/[$\w]/.test(c),("/"==r||"\n"==r)&&o,'"'==r&&o,"'"==r&&o,s[u-4]+n+r=="--\x3e",n+r=="*/"][f])for(p&&(e.appendChild(a=t.createElement("span")).setAttribute("style",["color: #555; font-weight: bold;","","","color: #555;",""][f?f<3?2:f>6?4:f>3?3:+/^(a(bstract|lias|nd|rguments|rray|s(m|sert)?|uto)|b(ase|egin|ool(ean)?|reak|yte)|c(ase|atch|har|hecked|lass|lone|ompl|onst|ontinue)|de(bugger|cimal|clare|f(ault|er)?|init|l(egate|ete)?)|do|double|e(cho|ls?if|lse(if)?|nd|nsure|num|vent|x(cept|ec|p(licit|ort)|te(nds|nsion|rn)))|f(allthrough|alse|inal(ly)?|ixed|loat|or(each)?|riend|rom|unc(tion)?)|global|goto|guard|i(f|mp(lements|licit|ort)|n(it|clude(_once)?|line|out|stanceof|t(erface|ernal)?)?|s)|l(ambda|et|ock|ong)|m(icrolight|odule|utable)|NaN|n(amespace|ative|ext|ew|il|ot|ull)|o(bject|perator|r|ut|verride)|p(ackage|arams|rivate|rotected|rotocol|ublic)|r(aise|e(adonly|do|f|gister|peat|quire(_once)?|scue|strict|try|turn))|s(byte|ealed|elf|hort|igned|izeof|tatic|tring|truct|ubscript|uper|ynchronized|witch)|t(emplate|hen|his|hrows?|ransient|rue|ry|ype(alias|def|id|name|of))|u(n(checked|def(ined)?|ion|less|signed|til)|se|sing)|v(ar|irtual|oid|olatile)|w(char_t|hen|here|hile|ith)|xor|yield)$/.test(p):0]),a.appendChild(t.createTextNode(p))),i=f&&f<7?f:i,p="",f=11;![1,/[\/{}[(\-+*=<>:;|\\.,?!&@~]/.test(c),/[\])]/.test(c),/[$\w]/.test(c),"/"==c&&i<2&&"<"!=r,'"'==c,"'"==c,c+l+s[u+1]+s[u+2]=="\x3c!--",c+l=="/*",c+l=="//","#"==c][--f];);p+=c}}(e)},t.mapToList=function mapToList(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"key";var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:l.default.Map();if(!l.default.Map.isMap(e)||!e.size)return l.default.List();Array.isArray(t)||(t=[t]);if(t.length<1)return e.merge(r);var n=l.default.List();var a=t[0];var s=!0;var u=!1;var c=void 0;try{for(var p,f=(0,o.default)(e.entries());!(s=(p=f.next()).done);s=!0){var d=p.value,h=(0,i.default)(d,2),m=h[0],v=h[1],g=mapToList(v,t.slice(1),r.set(a,m));n=l.default.List.isList(g)?n.concat(g):n.push(g)}}catch(e){u=!0,c=e}finally{try{!s&&f.return&&f.return()}finally{if(u)throw c}}return n},t.extractFileNameFromContentDispositionHeader=function extractFileNameFromContentDispositionHeader(e){var t=/filename="([^;]*);?"/i.exec(e);null===t&&(t=/filename=([^;]*);?/i.exec(e));if(null!==t&&t.length>1)return t[1];return null},t.pascalCase=pascalCase,t.pascalCaseFilename=function pascalCaseFilename(e){return pascalCase(e.replace(/\.[^./]*$/,""))},t.sanitizeUrl=function sanitizeUrl(e){if("string"!=typeof e||""===e)return"";return(0,c.sanitizeUrl)(e)},t.getAcceptControllingResponse=function getAcceptControllingResponse(e){if(!l.default.OrderedMap.isOrderedMap(e))return null;if(!e.size)return null;var t=e.find(function(e,t){return t.startsWith("2")&&(0,s.default)(e.get("content")||{}).length>0}),r=e.get("default")||l.default.OrderedMap(),n=(r.get("content")||l.default.OrderedMap()).keySeq().toJS().length?r:null;return t||n},t.deeplyStripKey=function deeplyStripKey(e,t){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:function(){return!0};if("object"!==(void 0===e?"undefined":(0,u.default)(e))||Array.isArray(e)||null===e||!t)return e;var n=(0,a.default)({},e);(0,s.default)(n).forEach(function(e){e===t&&r(n[e],e)?delete n[e]:n[e]=deeplyStripKey(n[e],t,r)});return n};var l=_interopRequireDefault(r(7)),c=r(617),p=_interopRequireDefault(r(618)),f=_interopRequireDefault(r(303)),d=_interopRequireDefault(r(308)),h=_interopRequireDefault(r(193)),m=_interopRequireDefault(r(695)),v=_interopRequireDefault(r(111)),g=r(204),y=_interopRequireDefault(r(32)),_=_interopRequireDefault(r(768));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}var b="default",S=t.isImmutable=function isImmutable(e){return l.default.Iterable.isIterable(e)};function normalizeArray(e){return Array.isArray(e)?e:[e]}function isObject(e){return!!e&&"object"===(void 0===e?"undefined":(0,u.default)(e))}t.memoize=d.default;function pascalCase(e){return(0,f.default)((0,p.default)(e))}t.propChecker=function propChecker(e,t){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[],n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:[];return(0,s.default)(e).length!==(0,s.default)(t).length||((0,m.default)(e,function(e,r){if(n.includes(r))return!1;var i=t[r];return l.default.Iterable.isIterable(e)?!l.default.is(e,i):("object"!==(void 0===e?"undefined":(0,u.default)(e))||"object"!==(void 0===i?"undefined":(0,u.default)(i)))&&e!==i})||r.some(function(r){return!(0,v.default)(e[r],t[r])}))};var k=t.validateMaximum=function validateMaximum(e,t){if(e>t)return"Value must be less than Maximum"},x=t.validateMinimum=function validateMinimum(e,t){if(et)return"Value must be less than MaxLength"},O=t.validateMinLength=function validateMinLength(e,t){if(e.length2&&void 0!==arguments[2]&&arguments[2],n=[],i=t&&"body"===e.get("in")?e.get("value_xml"):e.get("value"),o=e.get("required"),a=r?e.get("schema"):e;if(!a)return n;var s=a.get("maximum"),c=a.get("minimum"),p=a.get("type"),f=a.get("format"),d=a.get("maxLength"),h=a.get("minLength"),m=a.get("pattern");if(p&&(o||i)){var v="string"===p&&i,g="array"===p&&Array.isArray(i)&&i.length,_="array"===p&&l.default.List.isList(i)&&i.count(),b="file"===p&&i instanceof y.default.File,S="boolean"===p&&(i||!1===i),I="number"===p&&(i||0===i),q="integer"===p&&(i||0===i),F=!1;if(r&&"object"===p)if("object"===(void 0===i?"undefined":(0,u.default)(i)))F=!0;else if("string"==typeof i)try{JSON.parse(i),F=!0}catch(e){return n.push("Parameter string value must be valid JSON"),n}var B=[v,g,_,b,S,I,q,F].some(function(e){return!!e});if(o&&!B)return n.push("Required field is not provided"),n;if(m){var N=P(i,m);N&&n.push(N)}if(d||0===d){var j=T(i,d);j&&n.push(j)}if(h){var L=O(i,h);L&&n.push(L)}if(s||0===s){var z=k(i,s);z&&n.push(z)}if(c||0===c){var U=x(i,c);U&&n.push(U)}if("string"===p){var W=void 0;if(!(W="date-time"===f?R(i):"uuid"===f?M(i):A(i)))return n;n.push(W)}else if("boolean"===p){var V=D(i);if(!V)return n;n.push(V)}else if("number"===p){var H=E(i);if(!H)return n;n.push(H)}else if("integer"===p){var J=C(i);if(!J)return n;n.push(J)}else if("array"===p){var K;if(!_||!i.count())return n;K=a.getIn(["items","type"]),i.forEach(function(e,t){var r=void 0;"number"===K?r=E(e):"integer"===K?r=C(e):"string"===K&&(r=A(e)),r&&n.push({index:t,error:r})})}else if("file"===p){var G=w(i);if(!G)return n;n.push(G)}}return n},t.getSampleSchema=function getSampleSchema(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};if(/xml/.test(t)){if(!e.xml||!e.xml.name){if(e.xml=e.xml||{},!e.$$ref)return e.type||e.items||e.properties||e.additionalProperties?'\n\x3c!-- XML example cannot be generated --\x3e':null;var i=e.$$ref.match(/\S*\/(\S+)$/);e.xml.name=i[1]}return(0,g.memoizedCreateXMLExample)(e,r)}return(0,n.default)((0,g.memoizedSampleFromSchema)(e,r),null,2)},t.parseSearch=function parseSearch(){var e={},t=y.default.location.search;if(!t)return{};if(""!=t){var r=t.substr(1).split("&");for(var n in r)r.hasOwnProperty(n)&&(n=r[n].split("="),e[decodeURIComponent(n[0])]=n[1]&&decodeURIComponent(n[1])||"")}return e},t.serializeSearch=function serializeSearch(e){return(0,s.default)(e).map(function(t){return encodeURIComponent(t)+"="+encodeURIComponent(e[t])}).join("&")},t.btoa=function btoa(t){return(t instanceof e?t:new e(t.toString(),"utf-8")).toString("base64")},t.sorters={operationsSorter:{alpha:function alpha(e,t){return e.get("path").localeCompare(t.get("path"))},method:function method(e,t){return e.get("method").localeCompare(t.get("method"))}},tagsSorter:{alpha:function alpha(e,t){return e.localeCompare(t)}}},t.buildFormData=function buildFormData(e){var t=[];for(var r in e){var n=e[r];void 0!==n&&""!==n&&t.push([r,"=",encodeURIComponent(n).replace(/%20/g,"+")].join(""))}return t.join("&")},t.shallowEqualKeys=function shallowEqualKeys(e,t,r){return!!(0,h.default)(r,function(r){return(0,v.default)(e[r],t[r])})};var I=t.createDeepLinkPath=function createDeepLinkPath(e){return"string"==typeof e||e instanceof String?e.trim().replace(/\s/g,"_"):""};t.escapeDeepLinkPath=function escapeDeepLinkPath(e){return(0,_.default)(I(e))},t.getExtensions=function getExtensions(e){return e.filter(function(e,t){return/^x-/.test(t)})},t.getCommonExtensions=function getCommonExtensions(e){return e.filter(function(e,t){return/^pattern|maxLength|minLength|maximum|minimum/.test(t)})}}).call(t,r(48).Buffer)},function(e,t,r){"use strict";e.exports=function reactProdInvariant(e){for(var t=arguments.length-1,r="Minified React error #"+e+"; visit http://facebook.github.io/react/docs/error-decoder.html?invariant="+e,n=0;n>",o={listOf:function createListOfTypeChecker(e){return createIterableTypeChecker(e,"List",n.List.isList)},mapOf:function createMapOfTypeChecker(e,t){return createMapOfTypeCheckerFactory(e,t,"Map",n.Map.isMap)},orderedMapOf:function createOrderedMapOfTypeChecker(e,t){return createMapOfTypeCheckerFactory(e,t,"OrderedMap",n.OrderedMap.isOrderedMap)},setOf:function createSetOfTypeChecker(e){return createIterableTypeChecker(e,"Set",n.Set.isSet)},orderedSetOf:function createOrderedSetOfTypeChecker(e){return createIterableTypeChecker(e,"OrderedSet",n.OrderedSet.isOrderedSet)},stackOf:function createStackOfTypeChecker(e){return createIterableTypeChecker(e,"Stack",n.Stack.isStack)},iterableOf:function createIterableOfTypeChecker(e){return createIterableTypeChecker(e,"Iterable",n.Iterable.isIterable)},recordOf:function createRecordOfTypeChecker(e){return createChainableTypeChecker(function validate(t,r,i,o,a){for(var s=arguments.length,u=Array(s>5?s-5:0),l=5;l6?u-6:0),c=6;c5?u-5:0),c=5;c5?a-5:0),u=5;u key("+c[p]+")"].concat(s));if(d instanceof Error)return d}})}(t).apply(void 0,o)})}function createShapeTypeChecker(e){var t=void 0===arguments[1]?"Iterable":arguments[1],r=void 0===arguments[2]?n.Iterable.isIterable:arguments[2];return createChainableTypeChecker(function validate(n,i,o,a,s){for(var u=arguments.length,l=Array(u>5?u-5:0),c=5;c?@[\]^_`{|}~-])/g;function isValidEntityCode(e){return!(e>=55296&&e<=57343)&&(!(e>=64976&&e<=65007)&&(65535!=(65535&e)&&65534!=(65535&e)&&(!(e>=0&&e<=8)&&(11!==e&&(!(e>=14&&e<=31)&&(!(e>=127&&e<=159)&&!(e>1114111)))))))}function fromCodePoint(e){if(e>65535){var t=55296+((e-=65536)>>10),r=56320+(1023&e);return String.fromCharCode(t,r)}return String.fromCharCode(e)}var o=/&([a-z#][a-z0-9]{1,31});/gi,a=/^#((?:x[a-f0-9]{1,8}|[0-9]{1,8}))/i,s=r(456);function replaceEntityPattern(e,t){var r=0;return has(s,t)?s[t]:35===t.charCodeAt(0)&&a.test(t)&&isValidEntityCode(r="x"===t[1].toLowerCase()?parseInt(t.slice(2),16):parseInt(t.slice(1),10))?fromCodePoint(r):e}var u=/[&<>"]/,l=/[&<>"]/g,c={"&":"&","<":"<",">":">",'"':"""};function replaceUnsafeChar(e){return c[e]}t.assign=function assign(e){return[].slice.call(arguments,1).forEach(function(t){if(t){if("object"!=typeof t)throw new TypeError(t+"must be object");Object.keys(t).forEach(function(r){e[r]=t[r]})}}),e},t.isString=function isString(e){return"[object String]"===function typeOf(e){return Object.prototype.toString.call(e)}(e)},t.has=has,t.unescapeMd=function unescapeMd(e){return e.indexOf("\\")<0?e:e.replace(i,"$1")},t.isValidEntityCode=isValidEntityCode,t.fromCodePoint=fromCodePoint,t.replaceEntities=function replaceEntities(e){return e.indexOf("&")<0?e:e.replace(o,replaceEntityPattern)},t.escapeHtml=function escapeHtml(e){return u.test(e)?e.replace(l,replaceUnsafeChar):e}},function(e,t){e.exports=function(e){return"object"==typeof e?null!==e:"function"==typeof e}},function(e,t,r){var n=r(33),i=r(63),o=r(61),a=r(75),s=r(127),u=function(e,t,r){var l,c,p,f,d=e&u.F,h=e&u.G,m=e&u.S,v=e&u.P,g=e&u.B,y=h?n:m?n[t]||(n[t]={}):(n[t]||{}).prototype,_=h?i:i[t]||(i[t]={}),b=_.prototype||(_.prototype={});for(l in h&&(r=t),r)p=((c=!d&&y&&void 0!==y[l])?y:r)[l],f=g&&c?s(p,n):v&&"function"==typeof p?s(Function.call,p):p,y&&a(y,l,p,e&u.U),_[l]!=p&&o(_,l,f),v&&b[l]!=p&&(b[l]=p)};n.core=i,u.F=1,u.G=2,u.S=4,u.P=8,u.B=16,u.W=32,u.U=64,u.R=128,e.exports=u},function(e,t,r){var n=r(30),i=r(107),o=r(57),a=/"/g,s=function(e,t,r,n){var i=String(o(e)),s="<"+t;return""!==r&&(s+=" "+r+'="'+String(n).replace(a,""")+'"'),s+">"+i+""};e.exports=function(e,t){var r={};r[e]=t(s),n(n.P+n.F*i(function(){var t=""[e]('"');return t!==t.toLowerCase()||t.split('"').length>3}),"String",r)}},function(e,t,r){"use strict";var n=function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}(r(97));e.exports=function makeWindow(){var e={location:{},history:{},open:function open(){},close:function close(){},File:function File(){}};if("undefined"==typeof window)return e;try{e=window;var t=!0,r=!1,i=void 0;try{for(var o,a=(0,n.default)(["File","Blob","FormData"]);!(t=(o=a.next()).done);t=!0){var s=o.value;s in window&&(e[s]=window[s])}}catch(e){r=!0,i=e}finally{try{!t&&a.return&&a.return()}finally{if(r)throw i}}}catch(e){console.error(e)}return e}()},function(e,t){var r=e.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=r)},function(e,t,r){"use strict";function makeEmptyFunction(e){return function(){return e}}var n=function emptyFunction(){};n.thatReturns=makeEmptyFunction,n.thatReturnsFalse=makeEmptyFunction(!1),n.thatReturnsTrue=makeEmptyFunction(!0),n.thatReturnsNull=makeEmptyFunction(null),n.thatReturnsThis=function(){return this},n.thatReturnsArgument=function(e){return e},e.exports=n},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=_interopRequireDefault(r(26));t.isOAS3=isOAS3,t.isSwagger2=function isSwagger2(e){var t=e.get("swagger");if(!t)return!1;return t.startsWith("2.0")},t.OAS3ComponentWrapFactory=function OAS3ComponentWrapFactory(e){return function(t,r){return function(o){if(r&&r.specSelectors&&r.specSelectors.specJson){var a=r.specSelectors.specJson();return isOAS3(a)?i.default.createElement(e,(0,n.default)({},o,r,{Ori:t})):i.default.createElement(t,o)}return console.warn("OAS3 wrapper: couldn't get spec"),null}}};var i=_interopRequireDefault(r(0));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function isOAS3(e){var t=e.get("openapi");return!!t&&t.startsWith("3.0.")}},function(e,t,r){var n=r(29);e.exports=function(e){if(!n(e))throw TypeError(e+" is not an object!");return e}},function(e,t,r){e.exports={default:r(562),__esModule:!0}},function(e,t,r){var n=r(301),i="object"==typeof self&&self&&self.Object===Object&&self,o=n||i||Function("return this")();e.exports=o},function(e,t){e.exports=function isObject(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)}},function(e,t){var r,n,i=e.exports={};function defaultSetTimout(){throw new Error("setTimeout has not been defined")}function defaultClearTimeout(){throw new Error("clearTimeout has not been defined")}function runTimeout(e){if(r===setTimeout)return setTimeout(e,0);if((r===defaultSetTimout||!r)&&setTimeout)return r=setTimeout,setTimeout(e,0);try{return r(e,0)}catch(t){try{return r.call(null,e,0)}catch(t){return r.call(this,e,0)}}}!function(){try{r="function"==typeof setTimeout?setTimeout:defaultSetTimout}catch(e){r=defaultSetTimout}try{n="function"==typeof clearTimeout?clearTimeout:defaultClearTimeout}catch(e){n=defaultClearTimeout}}();var o,a=[],s=!1,u=-1;function cleanUpNextTick(){s&&o&&(s=!1,o.length?a=o.concat(a):u=-1,a.length&&drainQueue())}function drainQueue(){if(!s){var e=runTimeout(cleanUpNextTick);s=!0;for(var t=a.length;t;){for(o=a,a=[];++u1)for(var r=1;r0&&(o=this.buffer[s-1],e.call("\0\r\nÂ…\u2028\u2029",o)<0);)if(s--,this.pointer-s>r/2-1){i=" ... ",s+=5;break}for(u="",n=this.pointer;nr/2-1){u=" ... ",n-=5;break}return""+new Array(t).join(" ")+i+this.buffer.slice(s,n)+u+"\n"+new Array(t+this.pointer-s+i.length).join(" ")+"^"},Mark.prototype.toString=function(){var e,t;return e=this.get_snippet(),t=" on line "+(this.line+1)+", column "+(this.column+1),e?t:t+":\n"+e},Mark}(),this.YAMLError=function(e){function YAMLError(e){this.message=e,YAMLError.__super__.constructor.call(this),this.stack=this.toString()+"\n"+(new Error).stack.split("\n").slice(1).join("\n")}return t(YAMLError,e),YAMLError.prototype.toString=function(){return this.message},YAMLError}(Error),this.MarkedYAMLError=function(e){function MarkedYAMLError(e,t,r,n,i){this.context=e,this.context_mark=t,this.problem=r,this.problem_mark=n,this.note=i,MarkedYAMLError.__super__.constructor.call(this)}return t(MarkedYAMLError,e),MarkedYAMLError.prototype.toString=function(){var e;return e=[],null!=this.context&&e.push(this.context),null==this.context_mark||null!=this.problem&&null!=this.problem_mark&&this.context_mark.line===this.problem_mark.line&&this.context_mark.column===this.problem_mark.column||e.push(this.context_mark.toString()),null!=this.problem&&e.push(this.problem),null!=this.problem_mark&&e.push(this.problem_mark.toString()),null!=this.note&&e.push(this.note),e.join("\n")},MarkedYAMLError}(this.YAMLError)}).call(this)},function(e,t,r){e.exports=!r(55)(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},function(e,t,r){"use strict";(function(e){ +/*! + * The buffer module from node.js, for the browser. + * + * @author Feross Aboukhadijeh + * @license MIT + */ +var n=r(574),i=r(575),o=r(284);function kMaxLength(){return Buffer.TYPED_ARRAY_SUPPORT?2147483647:1073741823}function createBuffer(e,t){if(kMaxLength()=kMaxLength())throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+kMaxLength().toString(16)+" bytes");return 0|e}function byteLength(e,t){if(Buffer.isBuffer(e))return e.length;if("undefined"!=typeof ArrayBuffer&&"function"==typeof ArrayBuffer.isView&&(ArrayBuffer.isView(e)||e instanceof ArrayBuffer))return e.byteLength;"string"!=typeof e&&(e=""+e);var r=e.length;if(0===r)return 0;for(var n=!1;;)switch(t){case"ascii":case"latin1":case"binary":return r;case"utf8":case"utf-8":case void 0:return utf8ToBytes(e).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*r;case"hex":return r>>>1;case"base64":return base64ToBytes(e).length;default:if(n)return utf8ToBytes(e).length;t=(""+t).toLowerCase(),n=!0}}function swap(e,t,r){var n=e[t];e[t]=e[r],e[r]=n}function bidirectionalIndexOf(e,t,r,n,i){if(0===e.length)return-1;if("string"==typeof r?(n=r,r=0):r>2147483647?r=2147483647:r<-2147483648&&(r=-2147483648),r=+r,isNaN(r)&&(r=i?0:e.length-1),r<0&&(r=e.length+r),r>=e.length){if(i)return-1;r=e.length-1}else if(r<0){if(!i)return-1;r=0}if("string"==typeof t&&(t=Buffer.from(t,n)),Buffer.isBuffer(t))return 0===t.length?-1:arrayIndexOf(e,t,r,n,i);if("number"==typeof t)return t&=255,Buffer.TYPED_ARRAY_SUPPORT&&"function"==typeof Uint8Array.prototype.indexOf?i?Uint8Array.prototype.indexOf.call(e,t,r):Uint8Array.prototype.lastIndexOf.call(e,t,r):arrayIndexOf(e,[t],r,n,i);throw new TypeError("val must be string, number or Buffer")}function arrayIndexOf(e,t,r,n,i){var o,a=1,s=e.length,u=t.length;if(void 0!==n&&("ucs2"===(n=String(n).toLowerCase())||"ucs-2"===n||"utf16le"===n||"utf-16le"===n)){if(e.length<2||t.length<2)return-1;a=2,s/=2,u/=2,r/=2}function read(e,t){return 1===a?e[t]:e.readUInt16BE(t*a)}if(i){var l=-1;for(o=r;os&&(r=s-u),o=r;o>=0;o--){for(var c=!0,p=0;pi&&(n=i):n=i;var o=t.length;if(o%2!=0)throw new TypeError("Invalid hex string");n>o/2&&(n=o/2);for(var a=0;a>8,i=r%256,o.push(i),o.push(n);return o}(t,e.length-r),e,r,n)}function base64Slice(e,t,r){return 0===t&&r===e.length?n.fromByteArray(e):n.fromByteArray(e.slice(t,r))}function utf8Slice(e,t,r){r=Math.min(e.length,r);for(var n=[],i=t;i239?4:c>223?3:c>191?2:1;if(i+f<=r)switch(f){case 1:c<128&&(p=c);break;case 2:128==(192&(o=e[i+1]))&&(l=(31&c)<<6|63&o)>127&&(p=l);break;case 3:o=e[i+1],s=e[i+2],128==(192&o)&&128==(192&s)&&(l=(15&c)<<12|(63&o)<<6|63&s)>2047&&(l<55296||l>57343)&&(p=l);break;case 4:o=e[i+1],s=e[i+2],u=e[i+3],128==(192&o)&&128==(192&s)&&128==(192&u)&&(l=(15&c)<<18|(63&o)<<12|(63&s)<<6|63&u)>65535&&l<1114112&&(p=l)}null===p?(p=65533,f=1):p>65535&&(p-=65536,n.push(p>>>10&1023|55296),p=56320|1023&p),n.push(p),i+=f}return function decodeCodePointsArray(e){var t=e.length;if(t<=a)return String.fromCharCode.apply(String,e);var r="",n=0;for(;nthis.length)return"";if((void 0===r||r>this.length)&&(r=this.length),r<=0)return"";if((r>>>=0)<=(t>>>=0))return"";for(e||(e="utf8");;)switch(e){case"hex":return hexSlice(this,t,r);case"utf8":case"utf-8":return utf8Slice(this,t,r);case"ascii":return asciiSlice(this,t,r);case"latin1":case"binary":return latin1Slice(this,t,r);case"base64":return base64Slice(this,t,r);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return utf16leSlice(this,t,r);default:if(n)throw new TypeError("Unknown encoding: "+e);e=(e+"").toLowerCase(),n=!0}}.apply(this,arguments)},Buffer.prototype.equals=function equals(e){if(!Buffer.isBuffer(e))throw new TypeError("Argument must be a Buffer");return this===e||0===Buffer.compare(this,e)},Buffer.prototype.inspect=function inspect(){var e="",r=t.INSPECT_MAX_BYTES;return this.length>0&&(e=this.toString("hex",0,r).match(/.{2}/g).join(" "),this.length>r&&(e+=" ... ")),""},Buffer.prototype.compare=function compare(e,t,r,n,i){if(!Buffer.isBuffer(e))throw new TypeError("Argument must be a Buffer");if(void 0===t&&(t=0),void 0===r&&(r=e?e.length:0),void 0===n&&(n=0),void 0===i&&(i=this.length),t<0||r>e.length||n<0||i>this.length)throw new RangeError("out of range index");if(n>=i&&t>=r)return 0;if(n>=i)return-1;if(t>=r)return 1;if(t>>>=0,r>>>=0,n>>>=0,i>>>=0,this===e)return 0;for(var o=i-n,a=r-t,s=Math.min(o,a),u=this.slice(n,i),l=e.slice(t,r),c=0;ci)&&(r=i),e.length>0&&(r<0||t<0)||t>this.length)throw new RangeError("Attempt to write outside buffer bounds");n||(n="utf8");for(var o=!1;;)switch(n){case"hex":return hexWrite(this,e,t,r);case"utf8":case"utf-8":return utf8Write(this,e,t,r);case"ascii":return asciiWrite(this,e,t,r);case"latin1":case"binary":return latin1Write(this,e,t,r);case"base64":return base64Write(this,e,t,r);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return ucs2Write(this,e,t,r);default:if(o)throw new TypeError("Unknown encoding: "+n);n=(""+n).toLowerCase(),o=!0}},Buffer.prototype.toJSON=function toJSON(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};var a=4096;function asciiSlice(e,t,r){var n="";r=Math.min(e.length,r);for(var i=t;in)&&(r=n);for(var i="",o=t;or)throw new RangeError("Trying to access beyond buffer length")}function checkInt(e,t,r,n,i,o){if(!Buffer.isBuffer(e))throw new TypeError('"buffer" argument must be a Buffer instance');if(t>i||te.length)throw new RangeError("Index out of range")}function objectWriteUInt16(e,t,r,n){t<0&&(t=65535+t+1);for(var i=0,o=Math.min(e.length-r,2);i>>8*(n?i:1-i)}function objectWriteUInt32(e,t,r,n){t<0&&(t=4294967295+t+1);for(var i=0,o=Math.min(e.length-r,4);i>>8*(n?i:3-i)&255}function checkIEEE754(e,t,r,n,i,o){if(r+n>e.length)throw new RangeError("Index out of range");if(r<0)throw new RangeError("Index out of range")}function writeFloat(e,t,r,n,o){return o||checkIEEE754(e,0,r,4),i.write(e,t,r,n,23,4),r+4}function writeDouble(e,t,r,n,o){return o||checkIEEE754(e,0,r,8),i.write(e,t,r,n,52,8),r+8}Buffer.prototype.slice=function slice(e,t){var r,n=this.length;if(e=~~e,t=void 0===t?n:~~t,e<0?(e+=n)<0&&(e=0):e>n&&(e=n),t<0?(t+=n)<0&&(t=0):t>n&&(t=n),t0&&(i*=256);)n+=this[e+--t]*i;return n},Buffer.prototype.readUInt8=function readUInt8(e,t){return t||checkOffset(e,1,this.length),this[e]},Buffer.prototype.readUInt16LE=function readUInt16LE(e,t){return t||checkOffset(e,2,this.length),this[e]|this[e+1]<<8},Buffer.prototype.readUInt16BE=function readUInt16BE(e,t){return t||checkOffset(e,2,this.length),this[e]<<8|this[e+1]},Buffer.prototype.readUInt32LE=function readUInt32LE(e,t){return t||checkOffset(e,4,this.length),(this[e]|this[e+1]<<8|this[e+2]<<16)+16777216*this[e+3]},Buffer.prototype.readUInt32BE=function readUInt32BE(e,t){return t||checkOffset(e,4,this.length),16777216*this[e]+(this[e+1]<<16|this[e+2]<<8|this[e+3])},Buffer.prototype.readIntLE=function readIntLE(e,t,r){e|=0,t|=0,r||checkOffset(e,t,this.length);for(var n=this[e],i=1,o=0;++o=(i*=128)&&(n-=Math.pow(2,8*t)),n},Buffer.prototype.readIntBE=function readIntBE(e,t,r){e|=0,t|=0,r||checkOffset(e,t,this.length);for(var n=t,i=1,o=this[e+--n];n>0&&(i*=256);)o+=this[e+--n]*i;return o>=(i*=128)&&(o-=Math.pow(2,8*t)),o},Buffer.prototype.readInt8=function readInt8(e,t){return t||checkOffset(e,1,this.length),128&this[e]?-1*(255-this[e]+1):this[e]},Buffer.prototype.readInt16LE=function readInt16LE(e,t){t||checkOffset(e,2,this.length);var r=this[e]|this[e+1]<<8;return 32768&r?4294901760|r:r},Buffer.prototype.readInt16BE=function readInt16BE(e,t){t||checkOffset(e,2,this.length);var r=this[e+1]|this[e]<<8;return 32768&r?4294901760|r:r},Buffer.prototype.readInt32LE=function readInt32LE(e,t){return t||checkOffset(e,4,this.length),this[e]|this[e+1]<<8|this[e+2]<<16|this[e+3]<<24},Buffer.prototype.readInt32BE=function readInt32BE(e,t){return t||checkOffset(e,4,this.length),this[e]<<24|this[e+1]<<16|this[e+2]<<8|this[e+3]},Buffer.prototype.readFloatLE=function readFloatLE(e,t){return t||checkOffset(e,4,this.length),i.read(this,e,!0,23,4)},Buffer.prototype.readFloatBE=function readFloatBE(e,t){return t||checkOffset(e,4,this.length),i.read(this,e,!1,23,4)},Buffer.prototype.readDoubleLE=function readDoubleLE(e,t){return t||checkOffset(e,8,this.length),i.read(this,e,!0,52,8)},Buffer.prototype.readDoubleBE=function readDoubleBE(e,t){return t||checkOffset(e,8,this.length),i.read(this,e,!1,52,8)},Buffer.prototype.writeUIntLE=function writeUIntLE(e,t,r,n){(e=+e,t|=0,r|=0,n)||checkInt(this,e,t,r,Math.pow(2,8*r)-1,0);var i=1,o=0;for(this[t]=255&e;++o=0&&(o*=256);)this[t+i]=e/o&255;return t+r},Buffer.prototype.writeUInt8=function writeUInt8(e,t,r){return e=+e,t|=0,r||checkInt(this,e,t,1,255,0),Buffer.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),this[t]=255&e,t+1},Buffer.prototype.writeUInt16LE=function writeUInt16LE(e,t,r){return e=+e,t|=0,r||checkInt(this,e,t,2,65535,0),Buffer.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8):objectWriteUInt16(this,e,t,!0),t+2},Buffer.prototype.writeUInt16BE=function writeUInt16BE(e,t,r){return e=+e,t|=0,r||checkInt(this,e,t,2,65535,0),Buffer.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=255&e):objectWriteUInt16(this,e,t,!1),t+2},Buffer.prototype.writeUInt32LE=function writeUInt32LE(e,t,r){return e=+e,t|=0,r||checkInt(this,e,t,4,4294967295,0),Buffer.TYPED_ARRAY_SUPPORT?(this[t+3]=e>>>24,this[t+2]=e>>>16,this[t+1]=e>>>8,this[t]=255&e):objectWriteUInt32(this,e,t,!0),t+4},Buffer.prototype.writeUInt32BE=function writeUInt32BE(e,t,r){return e=+e,t|=0,r||checkInt(this,e,t,4,4294967295,0),Buffer.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e):objectWriteUInt32(this,e,t,!1),t+4},Buffer.prototype.writeIntLE=function writeIntLE(e,t,r,n){if(e=+e,t|=0,!n){var i=Math.pow(2,8*r-1);checkInt(this,e,t,r,i-1,-i)}var o=0,a=1,s=0;for(this[t]=255&e;++o>0)-s&255;return t+r},Buffer.prototype.writeIntBE=function writeIntBE(e,t,r,n){if(e=+e,t|=0,!n){var i=Math.pow(2,8*r-1);checkInt(this,e,t,r,i-1,-i)}var o=r-1,a=1,s=0;for(this[t+o]=255&e;--o>=0&&(a*=256);)e<0&&0===s&&0!==this[t+o+1]&&(s=1),this[t+o]=(e/a>>0)-s&255;return t+r},Buffer.prototype.writeInt8=function writeInt8(e,t,r){return e=+e,t|=0,r||checkInt(this,e,t,1,127,-128),Buffer.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),e<0&&(e=255+e+1),this[t]=255&e,t+1},Buffer.prototype.writeInt16LE=function writeInt16LE(e,t,r){return e=+e,t|=0,r||checkInt(this,e,t,2,32767,-32768),Buffer.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8):objectWriteUInt16(this,e,t,!0),t+2},Buffer.prototype.writeInt16BE=function writeInt16BE(e,t,r){return e=+e,t|=0,r||checkInt(this,e,t,2,32767,-32768),Buffer.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=255&e):objectWriteUInt16(this,e,t,!1),t+2},Buffer.prototype.writeInt32LE=function writeInt32LE(e,t,r){return e=+e,t|=0,r||checkInt(this,e,t,4,2147483647,-2147483648),Buffer.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8,this[t+2]=e>>>16,this[t+3]=e>>>24):objectWriteUInt32(this,e,t,!0),t+4},Buffer.prototype.writeInt32BE=function writeInt32BE(e,t,r){return e=+e,t|=0,r||checkInt(this,e,t,4,2147483647,-2147483648),e<0&&(e=4294967295+e+1),Buffer.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e):objectWriteUInt32(this,e,t,!1),t+4},Buffer.prototype.writeFloatLE=function writeFloatLE(e,t,r){return writeFloat(this,e,t,!0,r)},Buffer.prototype.writeFloatBE=function writeFloatBE(e,t,r){return writeFloat(this,e,t,!1,r)},Buffer.prototype.writeDoubleLE=function writeDoubleLE(e,t,r){return writeDouble(this,e,t,!0,r)},Buffer.prototype.writeDoubleBE=function writeDoubleBE(e,t,r){return writeDouble(this,e,t,!1,r)},Buffer.prototype.copy=function copy(e,t,r,n){if(r||(r=0),n||0===n||(n=this.length),t>=e.length&&(t=e.length),t||(t=0),n>0&&n=this.length)throw new RangeError("sourceStart out of bounds");if(n<0)throw new RangeError("sourceEnd out of bounds");n>this.length&&(n=this.length),e.length-t=0;--i)e[i+t]=this[i+r];else if(o<1e3||!Buffer.TYPED_ARRAY_SUPPORT)for(i=0;i>>=0,r=void 0===r?this.length:r>>>0,e||(e=0),"number"==typeof e)for(o=t;o55295&&r<57344){if(!i){if(r>56319){(t-=3)>-1&&o.push(239,191,189);continue}if(a+1===n){(t-=3)>-1&&o.push(239,191,189);continue}i=r;continue}if(r<56320){(t-=3)>-1&&o.push(239,191,189),i=r;continue}r=65536+(i-55296<<10|r-56320)}else i&&(t-=3)>-1&&o.push(239,191,189);if(i=null,r<128){if((t-=1)<0)break;o.push(r)}else if(r<2048){if((t-=2)<0)break;o.push(r>>6|192,63&r|128)}else if(r<65536){if((t-=3)<0)break;o.push(r>>12|224,r>>6&63|128,63&r|128)}else{if(!(r<1114112))throw new Error("Invalid code point");if((t-=4)<0)break;o.push(r>>18|240,r>>12&63|128,r>>6&63|128,63&r|128)}}return o}function base64ToBytes(e){return n.toByteArray(function base64clean(e){if((e=function stringtrim(e){return e.trim?e.trim():e.replace(/^\s+|\s+$/g,"")}(e).replace(s,"")).length<2)return"";for(;e.length%4!=0;)e+="=";return e}(e))}function blitBuffer(e,t,r,n){for(var i=0;i=t.length||i>=e.length);++i)t[i+r]=e[i];return i}}).call(t,r(18))},function(e,t,r){"use strict";e.exports={current:null}},function(e,t){e.exports=function isObjectLike(e){return null!=e&&"object"==typeof e}},function(e,t,r){"use strict";var n=r(13),i=r(71),o=r(34),a=(r(9),["dispatchConfig","_targetInst","nativeEvent","isDefaultPrevented","isPropagationStopped","_dispatchListeners","_dispatchInstances"]),s={type:null,target:null,currentTarget:o.thatReturnsNull,eventPhase:null,bubbles:null,cancelable:null,timeStamp:function(e){return e.timeStamp||Date.now()},defaultPrevented:null,isTrusted:null};function SyntheticEvent(e,t,r,n){this.dispatchConfig=e,this._targetInst=t,this.nativeEvent=r;var i=this.constructor.Interface;for(var a in i)if(i.hasOwnProperty(a)){0;var s=i[a];s?this[a]=s(r):"target"===a?this.target=n:this[a]=r[a]}var u=null!=r.defaultPrevented?r.defaultPrevented:!1===r.returnValue;return this.isDefaultPrevented=u?o.thatReturnsTrue:o.thatReturnsFalse,this.isPropagationStopped=o.thatReturnsFalse,this}n(SyntheticEvent.prototype,{preventDefault:function(){this.defaultPrevented=!0;var e=this.nativeEvent;e&&(e.preventDefault?e.preventDefault():"unknown"!=typeof e.returnValue&&(e.returnValue=!1),this.isDefaultPrevented=o.thatReturnsTrue)},stopPropagation:function(){var e=this.nativeEvent;e&&(e.stopPropagation?e.stopPropagation():"unknown"!=typeof e.cancelBubble&&(e.cancelBubble=!0),this.isPropagationStopped=o.thatReturnsTrue)},persist:function(){this.isPersistent=o.thatReturnsTrue},isPersistent:o.thatReturnsFalse,destructor:function(){var e=this.constructor.Interface;for(var t in e)this[t]=null;for(var r=0;r1&&void 0!==arguments[1]?arguments[1]:defaultEqualityCheck,r=null,n=null;return function(){return function areArgumentsShallowlyEqual(e,t,r){if(null===t||null===r||t.length!==r.length)return!1;for(var n=t.length,i=0;i1?t-1:0),n=1;n1&&void 0!==arguments[1]?arguments[1]:n;if("object"!=typeof e)throw new Error("createStructuredSelector expects first argument to be an object where each property is a selector, instead received a "+typeof e);var r=Object.keys(e);return t(r.map(function(t){return e[t]}),function(){for(var e=arguments.length,t=Array(e),n=0;n=r?e:e.length+1===r?""+t+e:""+new Array(r-e.length+1).join(t)+e},this.to_hex=function(e){return"string"==typeof e&&(e=e.charCodeAt(0)),e.toString(16)}}).call(this)}).call(t,r(18))},function(e,t,r){var n=r(124),i=r(266);e.exports=r(106)?function(e,t,r){return n.f(e,t,i(1,r))}:function(e,t,r){return e[t]=r,e}},function(e,t,r){var n=r(76);e.exports=function(e){if(!n(e))throw TypeError(e+" is not an object!");return e}},function(e,t){var r=e.exports={version:"2.5.5"};"number"==typeof __e&&(__e=r)},function(e,t,r){var n=r(80),i=r(620),o=r(621),a="[object Null]",s="[object Undefined]",u=n?n.toStringTag:void 0;e.exports=function baseGetTag(e){return null==e?void 0===e?s:a:u&&u in Object(e)?i(e):o(e)}},function(e,t,r){var n=r(637),i=r(640);e.exports=function getNative(e,t){var r=i(e,t);return n(r)?r:void 0}},function(e,t,r){var n=r(318),i=r(677),o=r(81);e.exports=function keys(e){return o(e)?n(e):i(e)}},function(e,t,r){"use strict";var n=r(147),i=Object.keys||function(e){var t=[];for(var r in e)t.push(r);return t};e.exports=Duplex;var o=r(112);o.inherits=r(84);var a=r(328),s=r(207);o.inherits(Duplex,a);for(var u=i(s.prototype),l=0;l1){for(var f=Array(p),d=0;d1){for(var m=Array(h),v=0;v=0||Object.prototype.hasOwnProperty.call(e,n)&&(r[n]=e[n]);return r}},function(e,t,r){"use strict";function isNothing(e){return void 0===e||null===e}e.exports.isNothing=isNothing,e.exports.isObject=function isObject(e){return"object"==typeof e&&null!==e},e.exports.toArray=function toArray(e){return Array.isArray(e)?e:isNothing(e)?[]:[e]},e.exports.repeat=function repeat(e,t){var r,n="";for(r=0;r`\\x00-\\x20]+|'[^']*'|\"[^\"]*\"))?)*\\s*/?>",u="]",l=new RegExp("^(?:<[A-Za-z][A-Za-z0-9-]*(?:\\s+[a-zA-Z_:][a-zA-Z0-9:._-]*(?:\\s*=\\s*(?:[^\"'=<>`\\x00-\\x20]+|'[^']*'|\"[^\"]*\"))?)*\\s*/?>|]|\x3c!----\x3e|\x3c!--(?:-?[^>-])(?:-?[^-])*--\x3e|[<][?].*?[?][>]|]*>|)","i"),c=/[\\&]/,p="[!\"#$%&'()*+,./:;<=>?@[\\\\\\]^_`{|}~-]",f=new RegExp("\\\\"+p+"|"+a,"gi"),d=new RegExp('[&<>"]',"g"),h=new RegExp(a+'|[&<>"]',"gi"),m=function(e){return 92===e.charCodeAt(0)?e.charAt(1):o(e)},v=function(e){switch(e){case"&":return"&";case"<":return"<";case">":return">";case'"':return""";default:return e}};e.exports={unescapeString:function(e){return c.test(e)?e.replace(f,m):e},normalizeURI:function(e){try{return n(i(e))}catch(t){return e}},escapeXml:function(e,t){return d.test(e)?t?e.replace(h,v):e.replace(d,v):e},reHtmlTag:l,OPENTAG:s,CLOSETAG:u,ENTITY:a,ESCAPABLE:p}},function(e,t,r){"use strict";var n=r(476),i=r(477),o=r(164).decodeHTML,a="&(?:#x[a-f0-9]{1,8}|#[0-9]{1,8}|[a-z][a-z0-9]{1,31});",s="<[A-Za-z][A-Za-z0-9-]*(?:\\s+[a-zA-Z_:][a-zA-Z0-9:._-]*(?:\\s*=\\s*(?:[^\"'=<>`\\x00-\\x20]+|'[^']*'|\"[^\"]*\"))?)*\\s*/?>",u="]",l=new RegExp("^(?:<[A-Za-z][A-Za-z0-9-]*(?:\\s+[a-zA-Z_:][a-zA-Z0-9:._-]*(?:\\s*=\\s*(?:[^\"'=<>`\\x00-\\x20]+|'[^']*'|\"[^\"]*\"))?)*\\s*/?>|]|\x3c!----\x3e|\x3c!--(?:-?[^>-])(?:-?[^-])*--\x3e|[<][?].*?[?][>]|]*>|)","i"),c=/[\\&]/,p="[!\"#$%&'()*+,./:;<=>?@[\\\\\\]^_`{|}~-]",f=new RegExp("\\\\"+p+"|"+a,"gi"),d=new RegExp('[&<>"]',"g"),h=new RegExp(a+'|[&<>"]',"gi"),m=function(e){return 92===e.charCodeAt(0)?e.charAt(1):o(e)},v=function(e){switch(e){case"&":return"&";case"<":return"<";case">":return">";case'"':return""";default:return e}};e.exports={unescapeString:function(e){return c.test(e)?e.replace(f,m):e},normalizeURI:function(e){try{return n(i(e))}catch(t){return e}},escapeXml:function(e,t){return d.test(e)?t?e.replace(h,v):e.replace(d,v):e},reHtmlTag:l,OPENTAG:s,CLOSETAG:u,ENTITY:a,ESCAPABLE:p}},function(e,t,r){e.exports={default:r(493),__esModule:!0}},function(e,t,r){r(494);for(var n=r(23),i=r(54),o=r(72),a=r(20)("toStringTag"),s="CSSRuleList,CSSStyleDeclaration,CSSValueList,ClientRectList,DOMRectList,DOMStringList,DOMTokenList,DataTransferItemList,FileList,HTMLAllCollection,HTMLCollection,HTMLFormElement,HTMLSelectElement,MediaList,MimeTypeArray,NamedNodeMap,NodeList,PaintRequestList,Plugin,PluginArray,SVGLengthList,SVGNumberList,SVGPathSegList,SVGPointList,SVGStringList,SVGTransformList,SourceBufferList,StyleSheetList,TextTrackCueList,TextTrackList,TouchList".split(","),u=0;u=t.length?{value:void 0,done:!0}:(e=n(t,r),this._i+=e.length,{value:e,done:!1})})},function(e,t){var r={}.toString;e.exports=function(e){return r.call(e).slice(8,-1)}},function(e,t,r){e.exports=!r(107)(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},function(e,t){e.exports=function(e){try{return!!e()}catch(e){return!0}}},function(e,t){e.exports={}},function(e,t,r){var n=r(126),i=Math.min;e.exports=function(e){return e>0?i(n(e),9007199254740991):0}},function(e,t,r){"use strict";e.exports=function reactProdInvariant(e){for(var t=arguments.length-1,r="Minified React error #"+e+"; visit http://facebook.github.io/react/docs/error-decoder.html?invariant="+e,n=0;n0?i(n(e),9007199254740991):0}},function(e,t){var r=0,n=Math.random();e.exports=function(e){return"Symbol(".concat(void 0===e?"":e,")_",(++r+n).toString(36))}},function(e,t,r){var n=r(62),i=r(505),o=r(506),a=Object.defineProperty;t.f=r(106)?Object.defineProperty:function defineProperty(e,t,r){if(n(e),t=o(t,!0),n(r),i)try{return a(e,t,r)}catch(e){}if("get"in r||"set"in r)throw TypeError("Accessors not supported!");return"value"in r&&(e[t]=r.value),e}},function(e,t){var r={}.hasOwnProperty;e.exports=function(e,t){return r.call(e,t)}},function(e,t){var r=Math.ceil,n=Math.floor;e.exports=function(e){return isNaN(e=+e)?0:(e>0?n:r)(e)}},function(e,t,r){var n=r(128);e.exports=function(e,t,r){if(n(e),void 0===t)return e;switch(r){case 1:return function(r){return e.call(t,r)};case 2:return function(r,n){return e.call(t,r,n)};case 3:return function(r,n,i){return e.call(t,r,n,i)}}return function(){return e.apply(t,arguments)}}},function(e,t){e.exports=function(e){if("function"!=typeof e)throw TypeError(e+" is not a function!");return e}},function(e,t,r){var n=r(511),i=r(57);e.exports=function(e){return n(i(e))}},function(e,t,r){"use strict";var n=r(61),i=r(75),o=r(107),a=r(57),s=r(17);e.exports=function(e,t,r){var u=s(e),l=r(a,u,""[e]),c=l[0],p=l[1];o(function(){var t={};return t[u]=function(){return 7},7!=""[e](t)})&&(i(String.prototype,e,c),n(RegExp.prototype,u,2==t?function(e,t){return p.call(e,this,t)}:function(e){return p.call(e,this)}))}},function(e,t,r){var n=r(123)("meta"),i=r(29),o=r(56),a=r(42).f,s=0,u=Object.isExtensible||function(){return!0},l=!r(55)(function(){return u(Object.preventExtensions({}))}),c=function(e){a(e,n,{value:{i:"O"+ ++s,w:{}}})},p=e.exports={KEY:n,NEED:!1,fastKey:function(e,t){if(!i(e))return"symbol"==typeof e?e:("string"==typeof e?"S":"P")+e;if(!o(e,n)){if(!u(e))return"F";if(!t)return"E";c(e)}return e[n].i},getWeak:function(e,t){if(!o(e,n)){if(!u(e))return!0;if(!t)return!1;c(e)}return e[n].w},onFreeze:function(e){return l&&p.NEED&&u(e)&&!o(e,n)&&c(e),e}}},function(e,t){t.f={}.propertyIsEnumerable},function(e,t,r){"use strict";var n={};e.exports=n},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.CLEAR_BY=t.CLEAR=t.NEW_AUTH_ERR=t.NEW_SPEC_ERR_BATCH=t.NEW_SPEC_ERR=t.NEW_THROWN_ERR_BATCH=t.NEW_THROWN_ERR=void 0,t.newThrownErr=function newThrownErr(e){return{type:i,payload:(0,n.default)(e)}},t.newThrownErrBatch=function newThrownErrBatch(e){return{type:o,payload:e}},t.newSpecErr=function newSpecErr(e){return{type:a,payload:e}},t.newSpecErrBatch=function newSpecErrBatch(e){return{type:s,payload:e}},t.newAuthErr=function newAuthErr(e){return{type:u,payload:e}},t.clear=function clear(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return{type:l,payload:e}},t.clearBy=function clearBy(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:function(){return!0};return{type:c,payload:e}};var n=function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}(r(190));var i=t.NEW_THROWN_ERR="err_new_thrown_err",o=t.NEW_THROWN_ERR_BATCH="err_new_thrown_err_batch",a=t.NEW_SPEC_ERR="err_new_spec_err",s=t.NEW_SPEC_ERR_BATCH="err_new_spec_err_batch",u=t.NEW_AUTH_ERR="err_new_auth_err",l=t.CLEAR="err_clear",c=t.CLEAR_BY="err_clear_by"},function(e,t,r){var n=r(64),i=r(50),o="[object Symbol]";e.exports=function isSymbol(e){return"symbol"==typeof e||i(e)&&n(e)==o}},function(e,t,r){var n=r(65)(Object,"create");e.exports=n},function(e,t,r){var n=r(645),i=r(646),o=r(647),a=r(648),s=r(649);function ListCache(e){var t=-1,r=null==e?0:e.length;for(this.clear();++t-1&&e%1==0&&e_;_++)if((v=t?y(a(h=e[_])[0],h[1]):y(e[_]))===l||v===c)return v}else for(m=g.call(e);!(h=m.next()).done;)if((v=i(m,y,h.value,t))===l||v===c)return v}).BREAK=l,t.RETURN=c},function(e,t,r){"use strict";var n=r(89);e.exports=n.DEFAULT=new n({include:[r(114)],explicit:[r(802),r(803),r(804)]})},function(e,t,r){var n=r(367),i=r(111),o=Object.prototype.hasOwnProperty;e.exports=function assignValue(e,t,r){var a=e[t];o.call(e,t)&&i(a,r)&&(void 0!==r||t in e)||n(e,t,r)}},function(e,t,r){"use strict";var n=r(11),i=(r(8),{}),o={reinitializeTransaction:function(){this.transactionWrappers=this.getTransactionWrappers(),this.wrapperInitData?this.wrapperInitData.length=0:this.wrapperInitData=[],this._isInTransaction=!1},_isInTransaction:!1,getTransactionWrappers:null,isInTransaction:function(){return!!this._isInTransaction},perform:function(e,t,r,i,o,a,s,u){var l,c;this.isInTransaction()&&n("27");try{this._isInTransaction=!0,l=!0,this.initializeAll(0),c=e.call(t,r,i,o,a,s,u),l=!1}finally{try{if(l)try{this.closeAll(0)}catch(e){}else this.closeAll(0)}finally{this._isInTransaction=!1}}return c},initializeAll:function(e){for(var t=this.transactionWrappers,r=e;r]/,u=r(229)(function(e,t){if(e.namespaceURI!==o.svg||"innerHTML"in e)e.innerHTML=t;else{(n=n||document.createElement("div")).innerHTML=""+t+"";for(var r=n.firstChild;r.firstChild;)e.appendChild(r.firstChild)}});if(i.canUseDOM){var l=document.createElement("div");l.innerHTML=" ",""===l.innerHTML&&(u=function(e,t){if(e.parentNode&&e.parentNode.replaceChild(e,e),a.test(t)||"<"===t[0]&&s.test(t)){e.innerHTML=String.fromCharCode(65279)+t;var r=e.firstChild;1===r.data.length?e.removeChild(r):r.deleteData(0,1)}else e.innerHTML=t}),l=null}e.exports=u},function(e,t,r){"use strict";var n=/["'&<>]/;e.exports=function escapeTextContentForBrowser(e){return"boolean"==typeof e||"number"==typeof e?""+e:function escapeHtml(e){var t,r=""+e,i=n.exec(r);if(!i)return r;var o="",a=0,s=0;for(a=i.index;adocument.F=Object<\/script>"),e.close(),u=e.F;n--;)delete u.prototype[o[n]];return u()};e.exports=Object.create||function create(e,t){var r;return null!==e?(s.prototype=n(e),r=new s,s.prototype=null,r[a]=e):r=u(),void 0===t?r:i(r,t)}},function(e,t){var r=Math.ceil,n=Math.floor;e.exports=function(e){return isNaN(e=+e)?0:(e>0?n:r)(e)}},function(e,t,r){var n=r(173)("keys"),i=r(123);e.exports=function(e){return n[e]||(n[e]=i(e))}},function(e,t,r){var n=r(23),i=n["__core-js_shared__"]||(n["__core-js_shared__"]={});e.exports=function(e){return i[e]||(i[e]={})}},function(e,t){e.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},function(e,t,r){var n=r(176),i=r(20)("iterator"),o=r(72);e.exports=r(15).getIteratorMethod=function(e){if(void 0!=e)return e[i]||e["@@iterator"]||o[n(e)]}},function(e,t,r){var n=r(99),i=r(20)("toStringTag"),o="Arguments"==n(function(){return arguments}());e.exports=function(e){var t,r,a;return void 0===e?"Undefined":null===e?"Null":"string"==typeof(r=function(e,t){try{return e[t]}catch(e){}}(t=Object(e),i))?r:o?n(t):"Object"==(a=n(t))&&"function"==typeof t.callee?"Arguments":a}},function(e,t,r){var n=r(105),i=r(17)("toStringTag"),o="Arguments"==n(function(){return arguments}());e.exports=function(e){var t,r,a;return void 0===e?"Undefined":null===e?"Null":"string"==typeof(r=function(e,t){try{return e[t]}catch(e){}}(t=Object(e),i))?r:o?n(t):"Object"==(a=n(t))&&"function"==typeof t.callee?"Arguments":a}},function(e,t){var r=0,n=Math.random();e.exports=function(e){return"Symbol(".concat(void 0===e?"":e,")_",(++r+n).toString(36))}},function(e,t,r){var n=r(76),i=r(33).document,o=n(i)&&n(i.createElement);e.exports=function(e){return o?i.createElement(e):{}}},function(e,t,r){var n=r(265)("keys"),i=r(178);e.exports=function(e){return n[e]||(n[e]=i(e))}},function(e,t,r){var n=r(124).f,i=r(125),o=r(17)("toStringTag");e.exports=function(e,t,r){e&&!i(e=r?e:e.prototype,o)&&n(e,o,{configurable:!0,value:t})}},function(e,t,r){"use strict";var n=r(128);e.exports.f=function(e){return new function PromiseCapability(e){var t,r;this.promise=new e(function(e,n){if(void 0!==t||void 0!==r)throw TypeError("Bad Promise constructor");t=e,r=n}),this.resolve=n(t),this.reject=n(r)}(e)}},function(e,t,r){var n=r(279),i=r(57);e.exports=function(e,t,r){if(n(t))throw TypeError("String#"+r+" doesn't accept regex!");return String(i(e))}},function(e,t,r){var n=r(17)("match");e.exports=function(e){var t=/./;try{"/./"[e](t)}catch(r){try{return t[n]=!1,!"/./"[e](t)}catch(e){}}return!0}},function(e,t,r){t.f=r(20)},function(e,t,r){var n=r(23),i=r(15),o=r(121),a=r(185),s=r(42).f;e.exports=function(e){var t=i.Symbol||(i.Symbol=o?{}:n.Symbol||{});"_"==e.charAt(0)||e in t||s(t,e,{value:a.f(e)})}},function(e,t){t.f=Object.getOwnPropertySymbols},function(e,t){},function(e,t,r){"use strict";(function(t){ +/*! + * @description Recursive object extending + * @author Viacheslav Lotsmanov + * @license MIT + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2018 Viacheslav Lotsmanov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function isSpecificValue(e){return e instanceof t||e instanceof Date||e instanceof RegExp}function cloneSpecificValue(e){if(e instanceof t){var r=t.alloc?t.alloc(e.length):new t(e.length);return e.copy(r),r}if(e instanceof Date)return new Date(e.getTime());if(e instanceof RegExp)return new RegExp(e);throw new Error("Unexpected situation")}function safeGetProperty(e,t){return"__proto__"===t?void 0:e[t]}var r=e.exports=function(){if(arguments.length<1||"object"!=typeof arguments[0])return!1;if(arguments.length<2)return arguments[0];var e,t,n=arguments[0];return Array.prototype.slice.call(arguments,1).forEach(function(i){"object"!=typeof i||null===i||Array.isArray(i)||Object.keys(i).forEach(function(o){return t=safeGetProperty(n,o),(e=safeGetProperty(i,o))===n?void 0:"object"!=typeof e||null===e?void(n[o]=e):Array.isArray(e)?void(n[o]=function deepCloneArray(e){var t=[];return e.forEach(function(e,n){"object"==typeof e&&null!==e?Array.isArray(e)?t[n]=deepCloneArray(e):isSpecificValue(e)?t[n]=cloneSpecificValue(e):t[n]=r({},e):t[n]=e}),t}(e)):isSpecificValue(e)?void(n[o]=cloneSpecificValue(e)):"object"!=typeof t||null===t||Array.isArray(t)?void(n[o]=r({},e)):void(n[o]=r(t,e))})}),n}}).call(t,r(48).Buffer)},function(e,t,r){"use strict";e.exports=function(e){return"object"==typeof e?function destroyCircular(e,t){var r;r=Array.isArray(e)?[]:{};t.push(e);Object.keys(e).forEach(function(n){var i=e[n];"function"!=typeof i&&(i&&"object"==typeof i?-1!==t.indexOf(e[n])?r[n]="[Circular]":r[n]=destroyCircular(e[n],t.slice(0)):r[n]=i)});"string"==typeof e.name&&(r.name=e.name);"string"==typeof e.message&&(r.message=e.message);"string"==typeof e.stack&&(r.stack=e.stack);return r}(e,[]):"function"==typeof e?"[Function: "+(e.name||"anonymous")+"]":e}},function(e,t,r){var n=r(634),i=r(650),o=r(652),a=r(653),s=r(654);function MapCache(e){var t=-1,r=null==e?0:e.length;for(this.clear();++t-1&&e%1==0&&e<=r}},function(e,t){e.exports=function baseUnary(e){return function(t){return e(t)}}},function(e,t,r){(function(e){var n=r(301),i="object"==typeof t&&t&&!t.nodeType&&t,o=i&&"object"==typeof e&&e&&!e.nodeType&&e,a=o&&o.exports===i&&n.process,s=function(){try{var e=o&&o.require&&o.require("util").types;return e||a&&a.binding&&a.binding("util")}catch(e){}}();e.exports=s}).call(t,r(141)(e))},function(e,t,r){var n=r(21),i=r(135),o=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,a=/^\w*$/;e.exports=function isKey(e,t){if(n(e))return!1;var r=typeof e;return!("number"!=r&&"symbol"!=r&&"boolean"!=r&&null!=e&&!i(e))||a.test(e)||!o.test(e)||null!=t&&e in Object(t)}},function(e,t){e.exports=function identity(e){return e}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.memoizedSampleFromSchema=t.memoizedCreateXMLExample=t.sampleXmlFromSchema=t.inferSchema=t.sampleFromSchema=void 0,t.createXMLExample=createXMLExample;var n=r(10),i=_interopRequireDefault(r(701)),o=_interopRequireDefault(r(714));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}var a={string:function string(){return"string"},string_email:function string_email(){return"user@example.com"},"string_date-time":function string_dateTime(){return(new Date).toISOString()},number:function number(){return 0},number_float:function number_float(){return 0},integer:function integer(){return 0},boolean:function boolean(e){return"boolean"!=typeof e.default||e.default}},s=function primitive(e){var t=e=(0,n.objectify)(e),r=t.type,i=t.format,o=a[r+"_"+i]||a[r];return(0,n.isFunc)(o)?o(e):"Unknown Type: "+e.type},u=t.sampleFromSchema=function sampleFromSchema(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=(0,n.objectify)(e),i=r.type,o=r.example,a=r.properties,u=r.additionalProperties,l=r.items,c=t.includeReadOnly,p=t.includeWriteOnly;if(void 0!==o)return(0,n.deeplyStripKey)(o,"$$ref",function(e){return"string"==typeof e&&e.indexOf("#")>-1});if(!i)if(a)i="object";else{if(!l)return;i="array"}if("object"===i){var f=(0,n.objectify)(a),d={};for(var h in f)f[h].readOnly&&!c||f[h].writeOnly&&!p||(d[h]=sampleFromSchema(f[h],t));if(!0===u)d.additionalProp1={};else if(u)for(var m=(0,n.objectify)(u),v=sampleFromSchema(m,t),g=1;g<4;g++)d["additionalProp"+g]=v;return d}return"array"===i?Array.isArray(l.anyOf)?l.anyOf.map(function(e){return sampleFromSchema(e,t)}):Array.isArray(l.oneOf)?l.oneOf.map(function(e){return sampleFromSchema(e,t)}):[sampleFromSchema(l,t)]:e.enum?e.default?e.default:(0,n.normalizeArray)(e.enum)[0]:"file"!==i?s(e):void 0},l=(t.inferSchema=function inferSchema(e){return e.schema&&(e=e.schema),e.properties&&(e.type="object"),e},t.sampleXmlFromSchema=function sampleXmlFromSchema(e){var t,r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},i=(0,n.objectify)(e),o=i.type,a=i.properties,u=i.additionalProperties,l=i.items,c=i.example,p=r.includeReadOnly,f=r.includeWriteOnly,d=i.default,h={},m={},v=e.xml,g=v.name,y=v.prefix,_=v.namespace,b=i.enum,S=void 0;if(!o)if(a||u)o="object";else{if(!l)return;o="array"}(g=g||"notagname",t=(y?y+":":"")+g,_)&&(m[y?"xmlns:"+y:"xmlns"]=_);if("array"===o&&l){if(l.xml=l.xml||v||{},l.xml.name=l.xml.name||v.name,v.wrapped)return h[t]=[],Array.isArray(c)?c.forEach(function(e){l.example=e,h[t].push(sampleXmlFromSchema(l,r))}):Array.isArray(d)?d.forEach(function(e){l.default=e,h[t].push(sampleXmlFromSchema(l,r))}):h[t]=[sampleXmlFromSchema(l,r)],m&&h[t].push({_attr:m}),h;var k=[];return Array.isArray(c)?(c.forEach(function(e){l.example=e,k.push(sampleXmlFromSchema(l,r))}),k):Array.isArray(d)?(d.forEach(function(e){l.default=e,k.push(sampleXmlFromSchema(l,r))}),k):sampleXmlFromSchema(l,r)}if("object"===o){var x=(0,n.objectify)(a);for(var E in h[t]=[],c=c||{},x)if(x.hasOwnProperty(E)&&(!x[E].readOnly||p)&&(!x[E].writeOnly||f))if(x[E].xml=x[E].xml||{},x[E].xml.attribute){var C=Array.isArray(x[E].enum)&&x[E].enum[0],w=x[E].example,D=x[E].default;m[x[E].xml.name||E]=void 0!==w&&w||void 0!==c[E]&&c[E]||void 0!==D&&D||C||s(x[E])}else{x[E].xml.name=x[E].xml.name||E,void 0===x[E].example&&void 0!==c[E]&&(x[E].example=c[E]);var A=sampleXmlFromSchema(x[E]);Array.isArray(A)?h[t]=h[t].concat(A):h[t].push(A)}return!0===u?h[t].push({additionalProp:"Anything can be here"}):u&&h[t].push({additionalProp:s(u)}),m&&h[t].push({_attr:m}),h}return S=void 0!==c?c:void 0!==d?d:Array.isArray(b)?b[0]:s(e),h[t]=m?[{_attr:m},S]:S,h});function createXMLExample(e,t){var r=l(e,t);if(r)return(0,i.default)(r,{declaration:!0,indent:"\t"})}t.memoizedCreateXMLExample=(0,o.default)(createXMLExample),t.memoizedSampleFromSchema=(0,o.default)(u)},function(e,t){function EventEmitter(){this._events=this._events||{},this._maxListeners=this._maxListeners||void 0}function isFunction(e){return"function"==typeof e}function isObject(e){return"object"==typeof e&&null!==e}function isUndefined(e){return void 0===e}e.exports=EventEmitter,EventEmitter.EventEmitter=EventEmitter,EventEmitter.prototype._events=void 0,EventEmitter.prototype._maxListeners=void 0,EventEmitter.defaultMaxListeners=10,EventEmitter.prototype.setMaxListeners=function(e){if(!function isNumber(e){return"number"==typeof e}(e)||e<0||isNaN(e))throw TypeError("n must be a positive number");return this._maxListeners=e,this},EventEmitter.prototype.emit=function(e){var t,r,n,i,o,a;if(this._events||(this._events={}),"error"===e&&(!this._events.error||isObject(this._events.error)&&!this._events.error.length)){if((t=arguments[1])instanceof Error)throw t;var s=new Error('Uncaught, unspecified "error" event. ('+t+")");throw s.context=t,s}if(isUndefined(r=this._events[e]))return!1;if(isFunction(r))switch(arguments.length){case 1:r.call(this);break;case 2:r.call(this,arguments[1]);break;case 3:r.call(this,arguments[1],arguments[2]);break;default:i=Array.prototype.slice.call(arguments,1),r.apply(this,i)}else if(isObject(r))for(i=Array.prototype.slice.call(arguments,1),n=(a=r.slice()).length,o=0;o0&&this._events[e].length>r&&(this._events[e].warned=!0,console.error("(node) warning: possible EventEmitter memory leak detected. %d listeners added. Use emitter.setMaxListeners() to increase limit.",this._events[e].length),"function"==typeof console.trace&&console.trace()),this},EventEmitter.prototype.on=EventEmitter.prototype.addListener,EventEmitter.prototype.once=function(e,t){if(!isFunction(t))throw TypeError("listener must be a function");var r=!1;function g(){this.removeListener(e,g),r||(r=!0,t.apply(this,arguments))}return g.listener=t,this.on(e,g),this},EventEmitter.prototype.removeListener=function(e,t){var r,n,i,o;if(!isFunction(t))throw TypeError("listener must be a function");if(!this._events||!this._events[e])return this;if(i=(r=this._events[e]).length,n=-1,r===t||isFunction(r.listener)&&r.listener===t)delete this._events[e],this._events.removeListener&&this.emit("removeListener",e,t);else if(isObject(r)){for(o=i;o-- >0;)if(r[o]===t||r[o].listener&&r[o].listener===t){n=o;break}if(n<0)return this;1===r.length?(r.length=0,delete this._events[e]):r.splice(n,1),this._events.removeListener&&this.emit("removeListener",e,t)}return this},EventEmitter.prototype.removeAllListeners=function(e){var t,r;if(!this._events)return this;if(!this._events.removeListener)return 0===arguments.length?this._events={}:this._events[e]&&delete this._events[e],this;if(0===arguments.length){for(t in this._events)"removeListener"!==t&&this.removeAllListeners(t);return this.removeAllListeners("removeListener"),this._events={},this}if(isFunction(r=this._events[e]))this.removeListener(e,r);else if(r)for(;r.length;)this.removeListener(e,r[r.length-1]);return delete this._events[e],this},EventEmitter.prototype.listeners=function(e){return this._events&&this._events[e]?isFunction(this._events[e])?[this._events[e]]:this._events[e].slice():[]},EventEmitter.prototype.listenerCount=function(e){if(this._events){var t=this._events[e];if(isFunction(t))return 1;if(t)return t.length}return 0},EventEmitter.listenerCount=function(e,t){return e.listenerCount(t)}},function(e,t,r){(t=e.exports=r(328)).Stream=t,t.Readable=t,t.Writable=r(207),t.Duplex=r(67),t.Transform=r(333),t.PassThrough=r(709)},function(e,t,r){"use strict";(function(t,n,i){var o=r(147);function CorkedRequest(e){var t=this;this.next=null,this.entry=null,this.finish=function(){!function onCorkedFinish(e,t,r){var n=e.entry;e.entry=null;for(;n;){var i=n.callback;t.pendingcb--,i(r),n=n.next}t.corkedRequestsFree?t.corkedRequestsFree.next=e:t.corkedRequestsFree=e}(t,e)}}e.exports=Writable;var a,s=!t.browser&&["v0.10","v0.9."].indexOf(t.version.slice(0,5))>-1?n:o.nextTick;Writable.WritableState=WritableState;var u=r(112);u.inherits=r(84);var l={deprecate:r(708)},c=r(329),p=r(148).Buffer,f=i.Uint8Array||function(){};var d,h=r(330);function nop(){}function WritableState(e,t){a=a||r(67),e=e||{};var n=t instanceof a;this.objectMode=!!e.objectMode,n&&(this.objectMode=this.objectMode||!!e.writableObjectMode);var i=e.highWaterMark,u=e.writableHighWaterMark,l=this.objectMode?16:16384;this.highWaterMark=i||0===i?i:n&&(u||0===u)?u:l,this.highWaterMark=Math.floor(this.highWaterMark),this.finalCalled=!1,this.needDrain=!1,this.ending=!1,this.ended=!1,this.finished=!1,this.destroyed=!1;var c=!1===e.decodeStrings;this.decodeStrings=!c,this.defaultEncoding=e.defaultEncoding||"utf8",this.length=0,this.writing=!1,this.corked=0,this.sync=!0,this.bufferProcessing=!1,this.onwrite=function(e){!function onwrite(e,t){var r=e._writableState,n=r.sync,i=r.writecb;if(function onwriteStateUpdate(e){e.writing=!1,e.writecb=null,e.length-=e.writelen,e.writelen=0}(r),t)!function onwriteError(e,t,r,n,i){--t.pendingcb,r?(o.nextTick(i,n),o.nextTick(finishMaybe,e,t),e._writableState.errorEmitted=!0,e.emit("error",n)):(i(n),e._writableState.errorEmitted=!0,e.emit("error",n),finishMaybe(e,t))}(e,r,n,t,i);else{var a=needFinish(r);a||r.corked||r.bufferProcessing||!r.bufferedRequest||clearBuffer(e,r),n?s(afterWrite,e,r,a,i):afterWrite(e,r,a,i)}}(t,e)},this.writecb=null,this.writelen=0,this.bufferedRequest=null,this.lastBufferedRequest=null,this.pendingcb=0,this.prefinished=!1,this.errorEmitted=!1,this.bufferedRequestCount=0,this.corkedRequestsFree=new CorkedRequest(this)}function Writable(e){if(a=a||r(67),!(d.call(Writable,this)||this instanceof a))return new Writable(e);this._writableState=new WritableState(e,this),this.writable=!0,e&&("function"==typeof e.write&&(this._write=e.write),"function"==typeof e.writev&&(this._writev=e.writev),"function"==typeof e.destroy&&(this._destroy=e.destroy),"function"==typeof e.final&&(this._final=e.final)),c.call(this)}function doWrite(e,t,r,n,i,o,a){t.writelen=n,t.writecb=a,t.writing=!0,t.sync=!0,r?e._writev(i,t.onwrite):e._write(i,o,t.onwrite),t.sync=!1}function afterWrite(e,t,r,n){r||function onwriteDrain(e,t){0===t.length&&t.needDrain&&(t.needDrain=!1,e.emit("drain"))}(e,t),t.pendingcb--,n(),finishMaybe(e,t)}function clearBuffer(e,t){t.bufferProcessing=!0;var r=t.bufferedRequest;if(e._writev&&r&&r.next){var n=t.bufferedRequestCount,i=new Array(n),o=t.corkedRequestsFree;o.entry=r;for(var a=0,s=!0;r;)i[a]=r,r.isBuf||(s=!1),r=r.next,a+=1;i.allBuffers=s,doWrite(e,t,!0,t.length,i,"",o.finish),t.pendingcb++,t.lastBufferedRequest=null,o.next?(t.corkedRequestsFree=o.next,o.next=null):t.corkedRequestsFree=new CorkedRequest(t),t.bufferedRequestCount=0}else{for(;r;){var u=r.chunk,l=r.encoding,c=r.callback;if(doWrite(e,t,!1,t.objectMode?1:u.length,u,l,c),r=r.next,t.bufferedRequestCount--,t.writing)break}null===r&&(t.lastBufferedRequest=null)}t.bufferedRequest=r,t.bufferProcessing=!1}function needFinish(e){return e.ending&&0===e.length&&null===e.bufferedRequest&&!e.finished&&!e.writing}function callFinal(e,t){e._final(function(r){t.pendingcb--,r&&e.emit("error",r),t.prefinished=!0,e.emit("prefinish"),finishMaybe(e,t)})}function finishMaybe(e,t){var r=needFinish(t);return r&&(!function prefinish(e,t){t.prefinished||t.finalCalled||("function"==typeof e._final?(t.pendingcb++,t.finalCalled=!0,o.nextTick(callFinal,e,t)):(t.prefinished=!0,e.emit("prefinish")))}(e,t),0===t.pendingcb&&(t.finished=!0,e.emit("finish"))),r}u.inherits(Writable,c),WritableState.prototype.getBuffer=function getBuffer(){for(var e=this.bufferedRequest,t=[];e;)t.push(e),e=e.next;return t},function(){try{Object.defineProperty(WritableState.prototype,"buffer",{get:l.deprecate(function(){return this.getBuffer()},"_writableState.buffer is deprecated. Use _writableState.getBuffer instead.","DEP0003")})}catch(e){}}(),"function"==typeof Symbol&&Symbol.hasInstance&&"function"==typeof Function.prototype[Symbol.hasInstance]?(d=Function.prototype[Symbol.hasInstance],Object.defineProperty(Writable,Symbol.hasInstance,{value:function(e){return!!d.call(this,e)||this===Writable&&(e&&e._writableState instanceof WritableState)}})):d=function(e){return e instanceof this},Writable.prototype.pipe=function(){this.emit("error",new Error("Cannot pipe, not readable"))},Writable.prototype.write=function(e,t,r){var n=this._writableState,i=!1,a=!n.objectMode&&function _isUint8Array(e){return p.isBuffer(e)||e instanceof f}(e);return a&&!p.isBuffer(e)&&(e=function _uint8ArrayToBuffer(e){return p.from(e)}(e)),"function"==typeof t&&(r=t,t=null),a?t="buffer":t||(t=n.defaultEncoding),"function"!=typeof r&&(r=nop),n.ended?function writeAfterEnd(e,t){var r=new Error("write after end");e.emit("error",r),o.nextTick(t,r)}(this,r):(a||function validChunk(e,t,r,n){var i=!0,a=!1;return null===r?a=new TypeError("May not write null values to stream"):"string"==typeof r||void 0===r||t.objectMode||(a=new TypeError("Invalid non-string/buffer chunk")),a&&(e.emit("error",a),o.nextTick(n,a),i=!1),i}(this,n,e,r))&&(n.pendingcb++,i=function writeOrBuffer(e,t,r,n,i,o){if(!r){var a=function decodeChunk(e,t,r){e.objectMode||!1===e.decodeStrings||"string"!=typeof t||(t=p.from(t,r));return t}(t,n,i);n!==a&&(r=!0,i="buffer",n=a)}var s=t.objectMode?1:n.length;t.length+=s;var u=t.length-1))throw new TypeError("Unknown encoding: "+e);return this._writableState.defaultEncoding=e,this},Object.defineProperty(Writable.prototype,"writableHighWaterMark",{enumerable:!1,get:function(){return this._writableState.highWaterMark}}),Writable.prototype._write=function(e,t,r){r(new Error("_write() is not implemented"))},Writable.prototype._writev=null,Writable.prototype.end=function(e,t,r){var n=this._writableState;"function"==typeof e?(r=e,e=null,t=null):"function"==typeof t&&(r=t,t=null),null!==e&&void 0!==e&&this.write(e,t),n.corked&&(n.corked=1,this.uncork()),n.ending||n.finished||function endWritable(e,t,r){t.ending=!0,finishMaybe(e,t),r&&(t.finished?o.nextTick(r):e.once("finish",r));t.ended=!0,e.writable=!1}(this,n,r)},Object.defineProperty(Writable.prototype,"destroyed",{get:function(){return void 0!==this._writableState&&this._writableState.destroyed},set:function(e){this._writableState&&(this._writableState.destroyed=e)}}),Writable.prototype.destroy=h.destroy,Writable.prototype._undestroy=h.undestroy,Writable.prototype._destroy=function(e,t){this.end(),t(e)}}).call(t,r(40),r(331).setImmediate,r(18))},function(e,t,r){"use strict";e.exports=function(e){return"function"==typeof e}},function(e,t,r){"use strict";e.exports=r(735)()?Array.from:r(736)},function(e,t,r){"use strict";var n=r(749),i=r(69),o=r(85),a=Array.prototype.indexOf,s=Object.prototype.hasOwnProperty,u=Math.abs,l=Math.floor;e.exports=function(e){var t,r,c,p;if(!n(e))return a.apply(this,arguments);for(r=i(o(this).length),c=arguments[1],t=c=isNaN(c)?0:c>=0?l(c):i(this.length)-l(u(c));t1&&void 0!==arguments[1])||arguments[1];return e=(0,n.normalizeArray)(e),{type:s,payload:{thing:e,shown:t}}},t.changeMode=function changeMode(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";return e=(0,n.normalizeArray)(e),{type:a,payload:{thing:e,mode:t}}};var n=r(10),i=t.UPDATE_LAYOUT="layout_update_layout",o=t.UPDATE_FILTER="layout_update_filter",a=t.UPDATE_MODE="layout_update_mode",s=t.SHOW="layout_show"},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.validateBeforeExecute=t.canExecuteScheme=t.operationScheme=t.hasHost=t.operationWithMeta=t.parameterWithMeta=t.parameterWithMetaByIdentity=t.allowTryItOutFor=t.mutatedRequestFor=t.requestFor=t.responseFor=t.mutatedRequests=t.requests=t.responses=t.taggedOperations=t.operationsWithTags=t.tagDetails=t.tags=t.operationsWithRootInherited=t.schemes=t.host=t.basePath=t.definitions=t.findDefinition=t.securityDefinitions=t.security=t.produces=t.consumes=t.operations=t.paths=t.semver=t.version=t.externalDocs=t.info=t.isOAS3=t.spec=t.specJsonWithResolvedSubtrees=t.specResolvedSubtree=t.specResolved=t.specJson=t.specSource=t.specStr=t.url=t.lastError=void 0;var n=function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}(r(86));t.getParameter=function getParameter(e,t,r,i){return t=t||[],e.getIn(["meta","paths"].concat((0,n.default)(t),["parameters"]),(0,a.fromJS)([])).find(function(e){return a.Map.isMap(e)&&e.get("name")===r&&e.get("in")===i})||(0,a.Map)()},t.parameterValues=function parameterValues(e,t,r){return t=t||[],D.apply(void 0,[e].concat((0,n.default)(t))).get("parameters",(0,a.List)()).reduce(function(e,t){var n=r&&"body"===t.get("in")?t.get("value_xml"):t.get("value");return e.set(t.get("in")+"."+t.get("name"),n)},(0,a.fromJS)({}))},t.parametersIncludeIn=function parametersIncludeIn(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";if(a.List.isList(e))return e.some(function(e){return a.Map.isMap(e)&&e.get("in")===t})},t.parametersIncludeType=parametersIncludeType,t.contentTypeValues=function contentTypeValues(e,t){t=t||[];var r=p(e).getIn(["paths"].concat((0,n.default)(t)),(0,a.fromJS)({})),i=e.getIn(["meta","paths"].concat((0,n.default)(t)),(0,a.fromJS)({})),o=currentProducesFor(e,t),s=r.get("parameters")||new a.List,u=i.get("consumes_value")?i.get("consumes_value"):parametersIncludeType(s,"file")?"multipart/form-data":parametersIncludeType(s,"formData")?"application/x-www-form-urlencoded":void 0;return(0,a.fromJS)({requestContentType:u,responseContentType:o})},t.operationConsumes=function operationConsumes(e,t){return t=t||[],p(e).getIn(["paths"].concat((0,n.default)(t),["consumes"]),(0,a.fromJS)({}))},t.currentProducesFor=currentProducesFor;var i=r(59),o=r(10),a=r(7);var s=["get","put","post","delete","options","head","patch","trace"],u=function state(e){return e||(0,a.Map)()},l=(t.lastError=(0,i.createSelector)(u,function(e){return e.get("lastError")}),t.url=(0,i.createSelector)(u,function(e){return e.get("url")}),t.specStr=(0,i.createSelector)(u,function(e){return e.get("spec")||""}),t.specSource=(0,i.createSelector)(u,function(e){return e.get("specSource")||"not-editor"}),t.specJson=(0,i.createSelector)(u,function(e){return e.get("json",(0,a.Map)())})),c=(t.specResolved=(0,i.createSelector)(u,function(e){return e.get("resolved",(0,a.Map)())}),t.specResolvedSubtree=function specResolvedSubtree(e,t){return e.getIn(["resolvedSubtrees"].concat((0,n.default)(t)),void 0)},function mergerFn(e,t){return a.Map.isMap(e)&&a.Map.isMap(t)?t.get("$$ref")?t:(0,a.OrderedMap)().mergeWith(mergerFn,e,t):t}),p=t.specJsonWithResolvedSubtrees=(0,i.createSelector)(u,function(e){return(0,a.OrderedMap)().mergeWith(c,e.get("json"),e.get("resolvedSubtrees"))}),f=t.spec=function spec(e){return l(e)},d=(t.isOAS3=(0,i.createSelector)(f,function(){return!1}),t.info=(0,i.createSelector)(f,function(e){return returnSelfOrNewMap(e&&e.get("info"))})),h=(t.externalDocs=(0,i.createSelector)(f,function(e){return returnSelfOrNewMap(e&&e.get("externalDocs"))}),t.version=(0,i.createSelector)(d,function(e){return e&&e.get("version")})),m=(t.semver=(0,i.createSelector)(h,function(e){return/v?([0-9]*)\.([0-9]*)\.([0-9]*)/i.exec(e).slice(1)}),t.paths=(0,i.createSelector)(p,function(e){return e.get("paths")})),v=t.operations=(0,i.createSelector)(m,function(e){if(!e||e.size<1)return(0,a.List)();var t=(0,a.List)();return e&&e.forEach?(e.forEach(function(e,r){if(!e||!e.forEach)return{};e.forEach(function(e,n){s.indexOf(n)<0||(t=t.push((0,a.fromJS)({path:r,method:n,operation:e,id:n+"-"+r})))})}),t):(0,a.List)()}),g=t.consumes=(0,i.createSelector)(f,function(e){return(0,a.Set)(e.get("consumes"))}),y=t.produces=(0,i.createSelector)(f,function(e){return(0,a.Set)(e.get("produces"))}),_=(t.security=(0,i.createSelector)(f,function(e){return e.get("security",(0,a.List)())}),t.securityDefinitions=(0,i.createSelector)(f,function(e){return e.get("securityDefinitions")}),t.findDefinition=function findDefinition(e,t){var r=e.getIn(["resolvedSubtrees","definitions",t],null),n=e.getIn(["json","definitions",t],null);return r||n||null},t.definitions=(0,i.createSelector)(f,function(e){return e.get("definitions")||(0,a.Map)()}),t.basePath=(0,i.createSelector)(f,function(e){return e.get("basePath")}),t.host=(0,i.createSelector)(f,function(e){return e.get("host")}),t.schemes=(0,i.createSelector)(f,function(e){return e.get("schemes",(0,a.Map)())}),t.operationsWithRootInherited=(0,i.createSelector)(v,g,y,function(e,t,r){return e.map(function(e){return e.update("operation",function(e){if(e){if(!a.Map.isMap(e))return;return e.withMutations(function(e){return e.get("consumes")||e.update("consumes",function(e){return(0,a.Set)(e).merge(t)}),e.get("produces")||e.update("produces",function(e){return(0,a.Set)(e).merge(r)}),e})}return(0,a.Map)()})})})),b=t.tags=(0,i.createSelector)(f,function(e){return e.get("tags",(0,a.List)())}),S=t.tagDetails=function tagDetails(e,t){return(b(e)||(0,a.List)()).filter(a.Map.isMap).find(function(e){return e.get("name")===t},(0,a.Map)())},k=t.operationsWithTags=(0,i.createSelector)(_,b,function(e,t){return e.reduce(function(e,t){var r=(0,a.Set)(t.getIn(["operation","tags"]));return r.count()<1?e.update("default",(0,a.List)(),function(e){return e.push(t)}):r.reduce(function(e,r){return e.update(r,(0,a.List)(),function(e){return e.push(t)})},e)},t.reduce(function(e,t){return e.set(t.get("name"),(0,a.List)())},(0,a.OrderedMap)()))}),x=(t.taggedOperations=function taggedOperations(e){return function(t){var r=(0,t.getConfigs)(),n=r.tagsSorter,i=r.operationsSorter;return k(e).sortBy(function(e,t){return t},function(e,t){var r="function"==typeof n?n:o.sorters.tagsSorter[n];return r?r(e,t):null}).map(function(t,r){var n="function"==typeof i?i:o.sorters.operationsSorter[i],s=n?t.sort(n):t;return(0,a.Map)({tagDetails:S(e,r),operations:s})})}},t.responses=(0,i.createSelector)(u,function(e){return e.get("responses",(0,a.Map)())})),E=t.requests=(0,i.createSelector)(u,function(e){return e.get("requests",(0,a.Map)())}),C=t.mutatedRequests=(0,i.createSelector)(u,function(e){return e.get("mutatedRequests",(0,a.Map)())}),w=(t.responseFor=function responseFor(e,t,r){return x(e).getIn([t,r],null)},t.requestFor=function requestFor(e,t,r){return E(e).getIn([t,r],null)},t.mutatedRequestFor=function mutatedRequestFor(e,t,r){return C(e).getIn([t,r],null)},t.allowTryItOutFor=function allowTryItOutFor(){return!0},t.parameterWithMetaByIdentity=function parameterWithMetaByIdentity(e,t,r){var i=p(e).getIn(["paths"].concat((0,n.default)(t),["parameters"]),(0,a.OrderedMap)()),o=e.getIn(["meta","paths"].concat((0,n.default)(t),["parameters"]),(0,a.OrderedMap)());return i.map(function(e){var t=o.get(r.get("name")+"."+r.get("in")),n=o.get(r.get("name")+"."+r.get("in")+".hash-"+r.hashCode());return(0,a.OrderedMap)().merge(e,t,n)}).find(function(e){return e.get("in")===r.get("in")&&e.get("name")===r.get("name")},(0,a.OrderedMap)())}),D=(t.parameterWithMeta=function parameterWithMeta(e,t,r,i){var o=p(e).getIn(["paths"].concat((0,n.default)(t),["parameters"]),(0,a.OrderedMap)()).find(function(e){return e.get("in")===i&&e.get("name")===r},(0,a.OrderedMap)());return w(e,t,o)},t.operationWithMeta=function operationWithMeta(e,t,r){var n=p(e).getIn(["paths",t,r],(0,a.OrderedMap)()),i=e.getIn(["meta","paths",t,r],(0,a.OrderedMap)()),o=n.get("parameters",(0,a.List)()).map(function(n){return w(e,[t,r],n)});return(0,a.OrderedMap)().merge(n,i).set("parameters",o)});t.hasHost=(0,i.createSelector)(f,function(e){var t=e.get("host");return"string"==typeof t&&t.length>0&&"/"!==t[0]});function parametersIncludeType(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";if(a.List.isList(e))return e.some(function(e){return a.Map.isMap(e)&&e.get("type")===t})}function currentProducesFor(e,t){t=t||[];var r=p(e).getIn(["paths"].concat((0,n.default)(t)),null);if(null!==r){var i=e.getIn(["meta","paths"].concat((0,n.default)(t),["produces_value"]),null),o=r.getIn(["produces",0],null);return i||o||"application/json"}}var A=t.operationScheme=function operationScheme(e,t,r){var n=e.get("url").match(/^([a-z][a-z0-9+\-.]*):/),i=Array.isArray(n)?n[1]:null;return e.getIn(["scheme",t,r])||e.getIn(["scheme","_defaultScheme"])||i||""};t.canExecuteScheme=function canExecuteScheme(e,t,r){return["http","https"].indexOf(A(e,t,r))>-1},t.validateBeforeExecute=function validateBeforeExecute(e,t){t=t||[];var r=!0;return e.getIn(["meta","paths"].concat((0,n.default)(t),["parameters"]),(0,a.fromJS)([])).forEach(function(e){var t=e.get("errors");t&&t.count()&&(r=!1)}),r};function returnSelfOrNewMap(e){return a.Map.isMap(e)?e:new a.Map}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.execute=t.executeRequest=t.logRequest=t.setMutatedRequest=t.setRequest=t.setResponse=t.validateParams=t.invalidateResolvedSubtreeCache=t.updateResolvedSubtree=t.requestResolvedSubtree=t.resolveSpec=t.parseToJson=t.SET_SCHEME=t.UPDATE_RESOLVED_SUBTREE=t.UPDATE_RESOLVED=t.UPDATE_OPERATION_META_VALUE=t.CLEAR_VALIDATE_PARAMS=t.CLEAR_REQUEST=t.CLEAR_RESPONSE=t.LOG_REQUEST=t.SET_MUTATED_REQUEST=t.SET_REQUEST=t.SET_RESPONSE=t.VALIDATE_PARAMS=t.UPDATE_PARAM=t.UPDATE_JSON=t.UPDATE_URL=t.UPDATE_SPEC=void 0;var n=_interopRequireDefault(r(26)),i=_interopRequireDefault(r(87)),o=_interopRequireDefault(r(25)),a=_interopRequireDefault(r(43)),s=_interopRequireDefault(r(151)),u=_interopRequireDefault(r(361)),l=_interopRequireDefault(r(362)),c=_interopRequireDefault(r(44));t.updateSpec=function updateSpec(e){var t=q(e).replace(/\t/g," ");if("string"==typeof e)return{type:_,payload:t}},t.updateResolved=function updateResolved(e){return{type:O,payload:e}},t.updateUrl=function updateUrl(e){return{type:b,payload:e}},t.updateJsonSpec=function updateJsonSpec(e){return{type:S,payload:e}},t.changeParam=function changeParam(e,t,r,n,i){return{type:k,payload:{path:e,value:n,paramName:t,paramIn:r,isXml:i}}},t.changeParamByIdentity=function changeParamByIdentity(e,t,r,n){return{type:k,payload:{path:e,param:t,value:r,isXml:n}}},t.clearValidateParams=function clearValidateParams(e){return{type:M,payload:{pathMethod:e}}},t.changeConsumesValue=function changeConsumesValue(e,t){return{type:T,payload:{path:e,value:t,key:"consumes_value"}}},t.changeProducesValue=function changeProducesValue(e,t){return{type:T,payload:{path:e,value:t,key:"produces_value"}}},t.clearResponse=function clearResponse(e,t){return{type:A,payload:{path:e,method:t}}},t.clearRequest=function clearRequest(e,t){return{type:R,payload:{path:e,method:t}}},t.setScheme=function setScheme(e,t,r){return{type:I,payload:{scheme:e,path:t,method:r}}};var p=_interopRequireDefault(r(218)),f=r(7),d=_interopRequireDefault(r(220)),h=_interopRequireDefault(r(190)),m=_interopRequireDefault(r(365)),v=_interopRequireDefault(r(809)),g=_interopRequireDefault(r(811)),y=r(10);function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}var _=t.UPDATE_SPEC="spec_update_spec",b=t.UPDATE_URL="spec_update_url",S=t.UPDATE_JSON="spec_update_json",k=t.UPDATE_PARAM="spec_update_param",x=t.VALIDATE_PARAMS="spec_validate_param",E=t.SET_RESPONSE="spec_set_response",C=t.SET_REQUEST="spec_set_request",w=t.SET_MUTATED_REQUEST="spec_set_mutated_request",D=t.LOG_REQUEST="spec_log_request",A=t.CLEAR_RESPONSE="spec_clear_response",R=t.CLEAR_REQUEST="spec_clear_request",M=t.CLEAR_VALIDATE_PARAMS="spec_clear_validate_param",T=t.UPDATE_OPERATION_META_VALUE="spec_update_operation_meta_value",O=t.UPDATE_RESOLVED="spec_update_resolved",P=t.UPDATE_RESOLVED_SUBTREE="spec_update_resolved_subtree",I=t.SET_SCHEME="set_scheme",q=function toStr(e){return(0,m.default)(e)?e:""};t.parseToJson=function parseToJson(e){return function(t){var r=t.specActions,n=t.specSelectors,i=t.errActions,o=n.specStr,a=null;try{e=e||o(),i.clear({source:"parser"}),a=p.default.safeLoad(e)}catch(e){return console.error(e),i.newSpecErr({source:"parser",level:"error",message:e.reason,line:e.mark&&e.mark.line?e.mark.line+1:void 0})}return a&&"object"===(void 0===a?"undefined":(0,c.default)(a))?r.updateJsonSpec(a):{}}};var F=!1,B=(t.resolveSpec=function resolveSpec(e,t){return function(r){var n=r.specActions,i=r.specSelectors,o=r.errActions,a=r.fn,s=a.fetch,u=a.resolve,l=a.AST,c=r.getConfigs;F||(console.warn("specActions.resolveSpec is deprecated since v3.10.0 and will be removed in v4.0.0; use requestResolvedSubtree instead!"),F=!0);var p=c(),f=p.modelPropertyMacro,d=p.parameterMacro,h=p.requestInterceptor,m=p.responseInterceptor;void 0===e&&(e=i.specJson()),void 0===t&&(t=i.url());var v=l.getLineNumberForPath,g=i.specStr();return u({fetch:s,spec:e,baseDoc:t,modelPropertyMacro:f,parameterMacro:d,requestInterceptor:h,responseInterceptor:m}).then(function(e){var t=e.spec,r=e.errors;if(o.clear({type:"thrown"}),Array.isArray(r)&&r.length>0){var i=r.map(function(e){return console.error(e),e.line=e.fullPath?v(g,e.fullPath):null,e.path=e.fullPath?e.fullPath.join("."):null,e.level="error",e.type="thrown",e.source="resolver",Object.defineProperty(e,"message",{enumerable:!0,value:e.message}),e});o.newThrownErrBatch(i)}return n.updateResolved(t)})}},[]),N=(0,v.default)((0,l.default)(u.default.mark(function _callee2(){var e,t,r,n,i,o,a,c,p,d,h,m,v,y,_;return u.default.wrap(function _callee2$(b){for(;;)switch(b.prev=b.next){case 0:if(e=B.system){b.next=4;break}return console.error("debResolveSubtrees: don't have a system to operate on, aborting."),b.abrupt("return");case 4:if(t=e.errActions,r=e.errSelectors,n=e.fn,i=n.resolveSubtree,o=n.AST.getLineNumberForPath,a=e.specSelectors,c=e.specActions,i){b.next=8;break}return console.error("Error: Swagger-Client did not provide a `resolveSubtree` method, doing nothing."),b.abrupt("return");case 8:return p=a.specStr(),d=e.getConfigs(),h=d.modelPropertyMacro,m=d.parameterMacro,v=d.requestInterceptor,y=d.responseInterceptor,b.prev=10,b.next=13,B.reduce(function(){var e=(0,l.default)(u.default.mark(function _callee(e,n){var s,l,c,f,d,_,b;return u.default.wrap(function _callee$(u){for(;;)switch(u.prev=u.next){case 0:return u.next=2,e;case 2:return s=u.sent,l=s.resultMap,c=s.specWithCurrentSubtrees,u.next=7,i(c,n,{baseDoc:a.url(),modelPropertyMacro:h,parameterMacro:m,requestInterceptor:v,responseInterceptor:y});case 7:return f=u.sent,d=f.errors,_=f.spec,r.allErrors().size&&t.clear({type:"thrown"}),Array.isArray(d)&&d.length>0&&(b=d.map(function(e){return e.line=e.fullPath?o(p,e.fullPath):null,e.path=e.fullPath?e.fullPath.join("."):null,e.level="error",e.type="thrown",e.source="resolver",Object.defineProperty(e,"message",{enumerable:!0,value:e.message}),e}),t.newThrownErrBatch(b)),(0,g.default)(l,n,_),(0,g.default)(c,n,_),u.abrupt("return",{resultMap:l,specWithCurrentSubtrees:c});case 15:case"end":return u.stop()}},_callee,void 0)}));return function(t,r){return e.apply(this,arguments)}}(),s.default.resolve({resultMap:(a.specResolvedSubtree([])||(0,f.Map)()).toJS(),specWithCurrentSubtrees:a.specJson().toJS()}));case 13:_=b.sent,delete B.system,B=[],b.next=21;break;case 18:b.prev=18,b.t0=b.catch(10),console.error(b.t0);case 21:c.updateResolvedSubtree([],_.resultMap);case 22:case"end":return b.stop()}},_callee2,void 0,[[10,18]])})),35);t.requestResolvedSubtree=function requestResolvedSubtree(e){return function(t){B.push(e),B.system=t,N()}};t.updateResolvedSubtree=function updateResolvedSubtree(e,t){return{type:P,payload:{path:e,value:t}}},t.invalidateResolvedSubtreeCache=function invalidateResolvedSubtreeCache(){return{type:P,payload:{path:[],value:(0,f.Map)()}}},t.validateParams=function validateParams(e,t){return{type:x,payload:{pathMethod:e,isOAS3:t}}};t.setResponse=function setResponse(e,t,r){return{payload:{path:e,method:t,res:r},type:E}},t.setRequest=function setRequest(e,t,r){return{payload:{path:e,method:t,req:r},type:C}},t.setMutatedRequest=function setMutatedRequest(e,t,r){return{payload:{path:e,method:t,req:r},type:w}},t.logRequest=function logRequest(e){return{payload:e,type:D}},t.executeRequest=function executeRequest(e){return function(t){var r=t.fn,n=t.specActions,i=t.specSelectors,s=t.getConfigs,u=t.oas3Selectors,l=e.pathName,c=e.method,p=e.operation,f=s(),m=f.requestInterceptor,v=f.responseInterceptor,g=p.toJS();if(e.contextUrl=(0,d.default)(i.url()).toString(),g&&g.operationId?e.operationId=g.operationId:g&&l&&c&&(e.operationId=r.opId(g,l,c)),i.isOAS3()){var _=l+":"+c;e.server=u.selectedServer(_)||u.selectedServer();var b=u.serverVariables({server:e.server,namespace:_}).toJS(),S=u.serverVariables({server:e.server}).toJS();e.serverVariables=(0,a.default)(b).length?b:S,e.requestContentType=u.requestContentType(l,c),e.responseContentType=u.responseContentType(l,c)||"*/*";var k=u.requestBodyValue(l,c);(0,y.isJSONObject)(k)?e.requestBody=JSON.parse(k):k&&k.toJS?e.requestBody=k.toJS():e.requestBody=k}var x=(0,o.default)({},e);x=r.buildRequest(x),n.setRequest(e.pathName,e.method,x);e.requestInterceptor=function requestInterceptorWrapper(t){var r=m.apply(this,[t]),i=(0,o.default)({},r);return n.setMutatedRequest(e.pathName,e.method,i),r},e.responseInterceptor=v;var E=Date.now();return r.execute(e).then(function(t){t.duration=Date.now()-E,n.setResponse(e.pathName,e.method,t)}).catch(function(t){return n.setResponse(e.pathName,e.method,{error:!0,err:(0,h.default)(t)})})}};t.execute=function execute(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.path,r=e.method,o=(0,i.default)(e,["path","method"]);return function(e){var i=e.fn.fetch,a=e.specSelectors,s=e.specActions,u=a.specJsonWithResolvedSubtrees().toJS(),l=a.operationScheme(t,r),c=a.contentTypeValues([t,r]).toJS(),p=c.requestContentType,f=c.responseContentType,d=/xml/i.test(p),h=a.parameterValues([t,r],d).toJS();return s.executeRequest((0,n.default)({},o,{fetch:i,spec:u,pathName:t,method:r,parameters:h,requestContentType:p,scheme:l,responseContentType:f}))}}},function(e,t){e.exports=function(e,t,r,n){if(!(e instanceof t)||void 0!==n&&n in e)throw TypeError(r+": incorrect invocation!");return e}},function(e,t,r){"use strict";var n=r(100);e.exports.f=function(e){return new function PromiseCapability(e){var t,r;this.promise=new e(function(e,n){if(void 0!==t||void 0!==r)throw TypeError("Bad Promise constructor");t=e,r=n}),this.resolve=n(t),this.reject=n(r)}(e)}},function(e,t,r){var n=r(54);e.exports=function(e,t,r){for(var i in t)r&&e[i]?e[i]=t[i]:n(e,i,t[i]);return e}},function(e,t,r){"use strict";var n=r(786);e.exports=n},function(e,t,r){"use strict";var n=r(89);e.exports=new n({explicit:[r(789),r(790),r(791)]})},function(e,t,r){"use strict";(function(t){var n=r(807),i=r(808),o=/^([a-z][a-z0-9.+-]*:)?(\/\/)?([\S\s]*)/i,a=/^[A-Za-z][A-Za-z0-9+-.]*:\/\//,s=[["#","hash"],["?","query"],["/","pathname"],["@","auth",1],[NaN,"host",void 0,1,1],[/:(\d+)$/,"port",void 0,1],[NaN,"hostname",void 0,1,1]],u={hash:1,query:1};function lolcation(e){var r,n={},i=typeof(e=e||t.location||{});if("blob:"===e.protocol)n=new URL(unescape(e.pathname),{});else if("string"===i)for(r in n=new URL(e,{}),u)delete n[r];else if("object"===i){for(r in e)r in u||(n[r]=e[r]);void 0===n.slashes&&(n.slashes=a.test(e.href))}return n}function extractProtocol(e){var t=o.exec(e);return{protocol:t[1]?t[1].toLowerCase():"",slashes:!!t[2],rest:t[3]}}function URL(e,t,r){if(!(this instanceof URL))return new URL(e,t,r);var o,a,u,l,c,p,f=s.slice(),d=typeof t,h=this,m=0;for("object"!==d&&"string"!==d&&(r=t,t=null),r&&"function"!=typeof r&&(r=i.parse),t=lolcation(t),o=!(a=extractProtocol(e||"")).protocol&&!a.slashes,h.slashes=a.slashes||o&&t.slashes,h.protocol=a.protocol||t.protocol||"",e=a.rest,a.slashes||(f[2]=[/(.*)/,"pathname"]);m-1||n("96",e),!a.plugins[r]){t.extractEvents||n("97",e),a.plugins[r]=t;var s=t.eventTypes;for(var u in s)publishEventForPlugin(s[u],t,u)||n("98",u,e)}}}function publishEventForPlugin(e,t,r){a.eventNameDispatchConfigs.hasOwnProperty(r)&&n("99",r),a.eventNameDispatchConfigs[r]=e;var i=e.phasedRegistrationNames;if(i){for(var o in i){if(i.hasOwnProperty(o))publishRegistrationName(i[o],t,r)}return!0}return!!e.registrationName&&(publishRegistrationName(e.registrationName,t,r),!0)}function publishRegistrationName(e,t,r){a.registrationNameModules[e]&&n("100",e),a.registrationNameModules[e]=t,a.registrationNameDependencies[e]=t.eventTypes[r].dependencies}var a={plugins:[],eventNameDispatchConfigs:{},registrationNameModules:{},registrationNameDependencies:{},possibleRegistrationNames:null,injectEventPluginOrder:function(e){i&&n("101"),i=Array.prototype.slice.call(e),recomputePluginOrdering()},injectEventPluginsByName:function(e){var t=!1;for(var r in e)if(e.hasOwnProperty(r)){var i=e[r];o.hasOwnProperty(r)&&o[r]===i||(o[r]&&n("102",r),o[r]=i,t=!0)}t&&recomputePluginOrdering()},getPluginModuleForEvent:function(e){var t=e.dispatchConfig;if(t.registrationName)return a.registrationNameModules[t.registrationName]||null;if(void 0!==t.phasedRegistrationNames){var r=t.phasedRegistrationNames;for(var n in r)if(r.hasOwnProperty(n)){var i=a.registrationNameModules[r[n]];if(i)return i}}return null},_resetEventPlugins:function(){for(var e in i=null,o)o.hasOwnProperty(e)&&delete o[e];a.plugins.length=0;var t=a.eventNameDispatchConfigs;for(var r in t)t.hasOwnProperty(r)&&delete t[r];var n=a.registrationNameModules;for(var s in n)n.hasOwnProperty(s)&&delete n[s]}};e.exports=a},function(e,t,r){"use strict";var n,i,o=r(11),a=r(223);r(8),r(9);function executeDispatch(e,t,r,n){var i=e.type||"unknown-event";e.currentTarget=s.getNodeFromInstance(n),t?a.invokeGuardedCallbackWithCatch(i,r,e):a.invokeGuardedCallback(i,r,e),e.currentTarget=null}var s={isEndish:function isEndish(e){return"topMouseUp"===e||"topTouchEnd"===e||"topTouchCancel"===e},isMoveish:function isMoveish(e){return"topMouseMove"===e||"topTouchMove"===e},isStartish:function isStartish(e){return"topMouseDown"===e||"topTouchStart"===e},executeDirectDispatch:function executeDirectDispatch(e){var t=e._dispatchListeners,r=e._dispatchInstances;Array.isArray(t)&&o("103"),e.currentTarget=t?s.getNodeFromInstance(r):null;var n=t?t(e):null;return e.currentTarget=null,e._dispatchListeners=null,e._dispatchInstances=null,n},executeDispatchesInOrder:function executeDispatchesInOrder(e,t){var r=e._dispatchListeners,n=e._dispatchInstances;if(Array.isArray(r))for(var i=0;i0&&n.length<20?r+" (keys: "+n.join(", ")+")":r}function getInternalInstanceReadyForUpdate(e,t){var r=i.get(e);return r||null}var a={isMounted:function(e){var t=i.get(e);return!!t&&!!t._renderedComponent},enqueueCallback:function(e,t,r){a.validateCallback(t,r);var n=getInternalInstanceReadyForUpdate(e);if(!n)return null;n._pendingCallbacks?n._pendingCallbacks.push(t):n._pendingCallbacks=[t],enqueueUpdate(n)},enqueueCallbackInternal:function(e,t){e._pendingCallbacks?e._pendingCallbacks.push(t):e._pendingCallbacks=[t],enqueueUpdate(e)},enqueueForceUpdate:function(e){var t=getInternalInstanceReadyForUpdate(e);t&&(t._pendingForceUpdate=!0,enqueueUpdate(t))},enqueueReplaceState:function(e,t,r){var n=getInternalInstanceReadyForUpdate(e);n&&(n._pendingStateQueue=[t],n._pendingReplaceState=!0,void 0!==r&&null!==r&&(a.validateCallback(r,"replaceState"),n._pendingCallbacks?n._pendingCallbacks.push(r):n._pendingCallbacks=[r]),enqueueUpdate(n))},enqueueSetState:function(e,t){var r=getInternalInstanceReadyForUpdate(e);r&&((r._pendingStateQueue||(r._pendingStateQueue=[])).push(t),enqueueUpdate(r))},enqueueElementInternal:function(e,t,r){e._pendingElement=t,e._context=r,enqueueUpdate(e)},validateCallback:function(e,t){e&&"function"!=typeof e&&n("122",t,formatUnexpectedArgument(e))}};e.exports=a},function(e,t,r){"use strict";r(13);var n=r(34),i=(r(9),n);e.exports=i},function(e,t,r){"use strict";e.exports=function getEventCharCode(e){var t,r=e.keyCode;return"charCode"in e?0===(t=e.charCode)&&13===r&&(t=13):t=r,t>=32||13===t?t:0}},function(e,t,r){var n=r(64),i=r(239),o=r(50),a="[object Object]",s=Function.prototype,u=Object.prototype,l=s.toString,c=u.hasOwnProperty,p=l.call(Object);e.exports=function isPlainObject(e){if(!o(e)||n(e)!=a)return!1;var t=i(e);if(null===t)return!0;var r=c.call(t,"constructor")&&t.constructor;return"function"==typeof r&&r instanceof r&&l.call(r)==p}},function(e,t,r){var n=r(320)(Object.getPrototypeOf,Object);e.exports=n},function(e,t,r){var n=r(314);e.exports=function cloneArrayBuffer(e){var t=new e.constructor(e.byteLength);return new n(t).set(new n(e)),t}},function(e,t,r){(function(){var e,t,n,i=function(e,t){for(var r in t)o.call(t,r)&&(e[r]=t[r]);function ctor(){this.constructor=e}return ctor.prototype=t.prototype,e.prototype=new ctor,e.__super__=t.prototype,e},o={}.hasOwnProperty,a=[].indexOf||function(e){for(var t=0,r=this.length;tr?p.push([c,s]):i[s]=this.yaml_path_resolvers[c][s]);else for(a=0,l=(h=this.yaml_path_resolvers).length;a=0)return o[e];if(a.call(o,null)>=0)return o.null}return e===t.ScalarNode?"tag:yaml.org,2002:str":e===t.SequenceNode?"tag:yaml.org,2002:seq":e===t.MappingNode?"tag:yaml.org,2002:map":void 0},BaseResolver}(),this.Resolver=function(e){function Resolver(){return Resolver.__super__.constructor.apply(this,arguments)}return i(Resolver,e),Resolver}(this.BaseResolver),this.Resolver.add_implicit_resolver("tag:yaml.org,2002:bool",/^(?:yes|Yes|YES|true|True|TRUE|on|On|ON|no|No|NO|false|False|FALSE|off|Off|OFF)$/,"yYnNtTfFoO"),this.Resolver.add_implicit_resolver("tag:yaml.org,2002:float",/^(?:[-+]?(?:[0-9][0-9_]*)\.[0-9_]*(?:[eE][-+][0-9]+)?|\.[0-9_]+(?:[eE][-+][0-9]+)?|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\.[0-9_]*|[-+]?\.(?:inf|Inf|INF)|\.(?:nan|NaN|NAN))$/,"-+0123456789."),this.Resolver.add_implicit_resolver("tag:yaml.org,2002:int",/^(?:[-+]?0b[01_]+|[-+]?0[0-7_]+|[-+]?(?:0|[1-9][0-9_]*)|[-+]?0x[0-9a-fA-F_]+|[-+]?0o[0-7_]+|[-+]?[1-9][0-9_]*(?::[0-5]?[0-9])+)$/,"-+0123456789"),this.Resolver.add_implicit_resolver("tag:yaml.org,2002:merge",/^(?:<<)$/,"<"),this.Resolver.add_implicit_resolver("tag:yaml.org,2002:null",/^(?:~|null|Null|NULL|)$/,["~","n","N",""]),this.Resolver.add_implicit_resolver("tag:yaml.org,2002:timestamp",/^(?:[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]|[0-9][0-9][0-9][0-9]-[0-9][0-9]?-[0-9][0-9]?(?:[Tt]|[\x20\t]+)[0-9][0-9]?:[0-9][0-9]:[0-9][0-9](?:\.[0-9]*)?(?:[\x20\t]*(?:Z|[-+][0-9][0-9]?(?::[0-9][0-9])?))?)$/,"0123456789"),this.Resolver.add_implicit_resolver("tag:yaml.org,2002:value",/^(?:=)$/,"="),this.Resolver.add_implicit_resolver("tag:yaml.org,2002:yaml",/^(?:!|&|\*)$/,"!&*")}).call(this)},function(e,t){(function(){var e=function(e,r){for(var n in r)t.call(r,n)&&(e[n]=r[n]);function ctor(){this.constructor=e}return ctor.prototype=r.prototype,e.prototype=new ctor,e.__super__=r.prototype,e},t={}.hasOwnProperty;this.Token=function(){return function Token(e,t){this.start_mark=e,this.end_mark=t}}(),this.DirectiveToken=function(t){function DirectiveToken(e,t,r,n){this.name=e,this.value=t,this.start_mark=r,this.end_mark=n}return e(DirectiveToken,t),DirectiveToken.prototype.id="",DirectiveToken}(this.Token),this.DocumentStartToken=function(t){function DocumentStartToken(){return DocumentStartToken.__super__.constructor.apply(this,arguments)}return e(DocumentStartToken,t),DocumentStartToken.prototype.id="",DocumentStartToken}(this.Token),this.DocumentEndToken=function(t){function DocumentEndToken(){return DocumentEndToken.__super__.constructor.apply(this,arguments)}return e(DocumentEndToken,t),DocumentEndToken.prototype.id="",DocumentEndToken}(this.Token),this.StreamStartToken=function(t){function StreamStartToken(e,t,r){this.start_mark=e,this.end_mark=t,this.encoding=r}return e(StreamStartToken,t),StreamStartToken.prototype.id="",StreamStartToken}(this.Token),this.StreamEndToken=function(t){function StreamEndToken(){return StreamEndToken.__super__.constructor.apply(this,arguments)}return e(StreamEndToken,t),StreamEndToken.prototype.id="",StreamEndToken}(this.Token),this.BlockSequenceStartToken=function(t){function BlockSequenceStartToken(){return BlockSequenceStartToken.__super__.constructor.apply(this,arguments)}return e(BlockSequenceStartToken,t),BlockSequenceStartToken.prototype.id="",BlockSequenceStartToken}(this.Token),this.BlockMappingStartToken=function(t){function BlockMappingStartToken(){return BlockMappingStartToken.__super__.constructor.apply(this,arguments)}return e(BlockMappingStartToken,t),BlockMappingStartToken.prototype.id="",BlockMappingStartToken}(this.Token),this.BlockEndToken=function(t){function BlockEndToken(){return BlockEndToken.__super__.constructor.apply(this,arguments)}return e(BlockEndToken,t),BlockEndToken.prototype.id="",BlockEndToken}(this.Token),this.FlowSequenceStartToken=function(t){function FlowSequenceStartToken(){return FlowSequenceStartToken.__super__.constructor.apply(this,arguments)}return e(FlowSequenceStartToken,t),FlowSequenceStartToken.prototype.id="[",FlowSequenceStartToken}(this.Token),this.FlowMappingStartToken=function(t){function FlowMappingStartToken(){return FlowMappingStartToken.__super__.constructor.apply(this,arguments)}return e(FlowMappingStartToken,t),FlowMappingStartToken.prototype.id="{",FlowMappingStartToken}(this.Token),this.FlowSequenceEndToken=function(t){function FlowSequenceEndToken(){return FlowSequenceEndToken.__super__.constructor.apply(this,arguments)}return e(FlowSequenceEndToken,t),FlowSequenceEndToken.prototype.id="]",FlowSequenceEndToken}(this.Token),this.FlowMappingEndToken=function(t){function FlowMappingEndToken(){return FlowMappingEndToken.__super__.constructor.apply(this,arguments)}return e(FlowMappingEndToken,t),FlowMappingEndToken.prototype.id="}",FlowMappingEndToken}(this.Token),this.KeyToken=function(t){function KeyToken(){return KeyToken.__super__.constructor.apply(this,arguments)}return e(KeyToken,t),KeyToken.prototype.id="?",KeyToken}(this.Token),this.ValueToken=function(t){function ValueToken(){return ValueToken.__super__.constructor.apply(this,arguments)}return e(ValueToken,t),ValueToken.prototype.id=":",ValueToken}(this.Token),this.BlockEntryToken=function(t){function BlockEntryToken(){return BlockEntryToken.__super__.constructor.apply(this,arguments)}return e(BlockEntryToken,t),BlockEntryToken.prototype.id="-",BlockEntryToken}(this.Token),this.FlowEntryToken=function(t){function FlowEntryToken(){return FlowEntryToken.__super__.constructor.apply(this,arguments)}return e(FlowEntryToken,t),FlowEntryToken.prototype.id=",",FlowEntryToken}(this.Token),this.AliasToken=function(t){function AliasToken(e,t,r){this.value=e,this.start_mark=t,this.end_mark=r}return e(AliasToken,t),AliasToken.prototype.id="",AliasToken}(this.Token),this.AnchorToken=function(t){function AnchorToken(e,t,r){this.value=e,this.start_mark=t,this.end_mark=r}return e(AnchorToken,t),AnchorToken.prototype.id="",AnchorToken}(this.Token),this.TagToken=function(t){function TagToken(e,t,r){this.value=e,this.start_mark=t,this.end_mark=r}return e(TagToken,t),TagToken.prototype.id="",TagToken}(this.Token),this.ScalarToken=function(t){function ScalarToken(e,t,r,n,i){this.value=e,this.plain=t,this.start_mark=r,this.end_mark=n,this.style=i}return e(ScalarToken,t),ScalarToken.prototype.id="",ScalarToken}(this.Token)}).call(this)},function(e,t){var r=this&&this.__extends||function(e,t){for(var r in t)t.hasOwnProperty(r)&&(e[r]=t[r]);function __(){this.constructor=e}e.prototype=null===t?Object.create(t):(__.prototype=t.prototype,new __)},n=Object.prototype.hasOwnProperty; +/*! + * https://github.com/Starcounter-Jack/JSON-Patch + * (c) 2017 Joachim Wester + * MIT license + */function hasOwnProperty(e,t){return n.call(e,t)}function _objectKeys(e){if(Array.isArray(e)){for(var t=new Array(e.length),r=0;r=48&&t<=57))return!1;r++}return!0},t.escapePathComponent=escapePathComponent,t.unescapePathComponent=function unescapePathComponent(e){return e.replace(/~1/g,"/").replace(/~0/g,"~")},t._getPathRecursive=_getPathRecursive,t.getPath=function getPath(e,t){if(e===t)return"/";var r=_getPathRecursive(e,t);if(""===r)throw new Error("Object not found in root");return"/"+r},t.hasUndefined=function hasUndefined(e){if(void 0===e)return!0;if(e)if(Array.isArray(e)){for(var t=0,r=e.length;tS;S++)if((f||S in y)&&(v=_(m=y[S],S,g),e))if(r)k[S]=v;else if(v)switch(e){case 3:return!0;case 5:return m;case 6:return S;case 2:k.push(m)}else if(c)return!1;return p?-1:l||c?c:k}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.authorizeRequest=t.authorizeAccessCodeWithBasicAuthentication=t.authorizeAccessCodeWithFormParams=t.authorizeApplication=t.authorizePassword=t.preAuthorizeImplicit=t.CONFIGURE_AUTH=t.VALIDATE=t.AUTHORIZE_OAUTH2=t.PRE_AUTHORIZE_OAUTH2=t.LOGOUT=t.AUTHORIZE=t.SHOW_AUTH_POPUP=void 0;var n=_interopRequireDefault(r(44)),i=_interopRequireDefault(r(25)),o=_interopRequireDefault(r(37));t.showDefinitions=function showDefinitions(e){return{type:l,payload:e}},t.authorize=function authorize(e){return{type:c,payload:e}},t.logout=function logout(e){return{type:p,payload:e}},t.authorizeOauth2=function authorizeOauth2(e){return{type:f,payload:e}},t.configureAuth=function configureAuth(e){return{type:d,payload:e}};var a=_interopRequireDefault(r(220)),s=_interopRequireDefault(r(32)),u=r(10);function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}var l=t.SHOW_AUTH_POPUP="show_popup",c=t.AUTHORIZE="authorize",p=t.LOGOUT="logout",f=(t.PRE_AUTHORIZE_OAUTH2="pre_authorize_oauth2",t.AUTHORIZE_OAUTH2="authorize_oauth2"),d=(t.VALIDATE="validate",t.CONFIGURE_AUTH="configure_auth");t.preAuthorizeImplicit=function preAuthorizeImplicit(e){return function(t){var r=t.authActions,n=t.errActions,i=e.auth,a=e.token,u=e.isValid,l=i.schema,c=i.name,p=l.get("flow");delete s.default.swaggerUIRedirectOauth2,"accessCode"===p||u||n.newAuthErr({authId:c,source:"auth",level:"warning",message:"Authorization may be unsafe, passed state was changed in server Passed state wasn't returned from auth server"}),a.error?n.newAuthErr({authId:c,source:"auth",level:"error",message:(0,o.default)(a)}):r.authorizeOauth2({auth:i,token:a})}};t.authorizePassword=function authorizePassword(e){return function(t){var r=t.authActions,n=e.schema,o=e.name,a=e.username,s=e.password,l=e.passwordType,c=e.clientId,p=e.clientSecret,f={grant_type:"password",scope:e.scopes.join(" ")},d={},h={};return"basic"===l?h.Authorization="Basic "+(0,u.btoa)(a+":"+s):((0,i.default)(f,{username:a},{password:s}),"query"===l?(c&&(d.client_id=c),p&&(d.client_secret=p)):h.Authorization="Basic "+(0,u.btoa)(c+":"+p)),r.authorizeRequest({body:(0,u.buildFormData)(f),url:n.get("tokenUrl"),name:o,headers:h,query:d,auth:e})}},t.authorizeApplication=function authorizeApplication(e){return function(t){var r=t.authActions,n=e.schema,i=e.scopes,o=e.name,a=e.clientId,s=e.clientSecret,l={Authorization:"Basic "+(0,u.btoa)(a+":"+s)},c={grant_type:"client_credentials",scope:i.join(" ")};return r.authorizeRequest({body:(0,u.buildFormData)(c),name:o,url:n.get("tokenUrl"),auth:e,headers:l})}},t.authorizeAccessCodeWithFormParams=function authorizeAccessCodeWithFormParams(e){var t=e.auth,r=e.redirectUrl;return function(e){var n=e.authActions,i=t.schema,o=t.name,a=t.clientId,s=t.clientSecret,l={grant_type:"authorization_code",code:t.code,client_id:a,client_secret:s,redirect_uri:r};return n.authorizeRequest({body:(0,u.buildFormData)(l),name:o,url:i.get("tokenUrl"),auth:t})}},t.authorizeAccessCodeWithBasicAuthentication=function authorizeAccessCodeWithBasicAuthentication(e){var t=e.auth,r=e.redirectUrl;return function(e){var n=e.authActions,i=t.schema,o=t.name,a=t.clientId,s=t.clientSecret,l={Authorization:"Basic "+(0,u.btoa)(a+":"+s)},c={grant_type:"authorization_code",code:t.code,client_id:a,redirect_uri:r};return n.authorizeRequest({body:(0,u.buildFormData)(c),name:o,url:i.get("tokenUrl"),auth:t,headers:l})}},t.authorizeRequest=function authorizeRequest(e){return function(t){var r=t.fn,s=t.getConfigs,u=t.authActions,l=t.errActions,c=t.oas3Selectors,p=t.specSelectors,f=t.authSelectors,d=e.body,h=e.query,m=void 0===h?{}:h,v=e.headers,g=void 0===v?{}:v,y=e.name,_=e.url,b=e.auth,S=(f.getConfigs()||{}).additionalQueryStringParams,k=void 0;k=p.isOAS3()?(0,a.default)(_,c.selectedServer(),!0):(0,a.default)(_,p.url(),!0),"object"===(void 0===S?"undefined":(0,n.default)(S))&&(k.query=(0,i.default)({},k.query,S));var x=k.toString(),E=(0,i.default)({Accept:"application/json, text/plain, */*","Content-Type":"application/x-www-form-urlencoded"},g);r.fetch({url:x,method:"post",headers:E,query:m,body:d,requestInterceptor:s().requestInterceptor,responseInterceptor:s().responseInterceptor}).then(function(e){var t=JSON.parse(e.data),r=t&&(t.error||""),n=t&&(t.parseError||"");e.ok?r||n?l.newAuthErr({authId:y,level:"error",source:"auth",message:(0,o.default)(t)}):u.authorizeOauth2({auth:b,token:t}):l.newAuthErr({authId:y,level:"error",source:"auth",message:e.statusText})}).catch(function(e){var t=new Error(e);l.newAuthErr({authId:y,level:"error",source:"auth",message:t.message})})}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=_interopRequireDefault(r(991)),i=_interopRequireDefault(r(996)),o=_interopRequireDefault(r(997)),a=_interopRequireDefault(r(998)),s=_interopRequireDefault(r(999)),u=_interopRequireDefault(r(1e3)),l=_interopRequireDefault(r(1001)),c=_interopRequireDefault(r(1002)),p=_interopRequireDefault(r(1003)),f=_interopRequireDefault(r(1004)),d=_interopRequireDefault(r(1005)),h=_interopRequireDefault(r(1007)),m=_interopRequireDefault(r(1021));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}var v=[o.default,i.default,a.default,u.default,l.default,c.default,p.default,f.default,d.default,s.default],g=(0,n.default)({prefixMap:m.default.prefixMap,plugins:v},h.default);t.default=g,e.exports=t.default},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function capitalizeString(e){return e.charAt(0).toUpperCase()+e.slice(1)},e.exports=t.default},function(e,t,r){var n=r(1022),i=r(1);e.exports=function(e,t,r){var i=e[t];if(i){var o=[];if(Object.keys(i).forEach(function(e){-1===n.indexOf(e)&&o.push(e)}),o.length)throw new Error("Prop "+t+" passed to "+r+". Has invalid keys "+o.join(", "))}},e.exports.isRequired=function(t,r,n){if(!t[r])throw new Error("Prop "+r+" passed to "+n+" is required");return e.exports(t,r,n)},e.exports.supportingArrays=i.oneOfType([i.arrayOf(e.exports),e.exports])},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.parseYamlConfig=void 0;var n=function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}(r(218));t.parseYamlConfig=function parseYamlConfig(e,t){try{return n.default.safeLoad(e)}catch(e){return t&&t.errActions.newThrownErr(new Error(e)),{}}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.loaded=t.TOGGLE_CONFIGS=t.UPDATE_CONFIGS=void 0;var n=function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}(r(24));t.update=function update(e,t){return{type:i,payload:(0,n.default)({},e,t)}},t.toggle=function toggle(e){return{type:o,payload:e}};var i=t.UPDATE_CONFIGS="configs_update",o=t.TOGGLE_CONFIGS="configs_toggle";t.loaded=function loaded(){return function(){}}},function(e,t,r){"use strict";t.__esModule=!0,t.default=function mapToZero(e){var t={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[r]=0);return t},e.exports=t.default},function(e,t,r){"use strict";t.__esModule=!0,t.default=function stepper(e,t,r,i,o,a,s){var u=r+(-o*(t-i)+-a*r)*e,l=t+u*e;if(Math.abs(u)u;)n(s,r=t[u++])&&(~o(l,r)||l.push(r));return l}},function(e,t,r){var n=r(23).document;e.exports=n&&n.documentElement},function(e,t,r){var n=r(56),i=r(74),o=r(172)("IE_PROTO"),a=Object.prototype;e.exports=Object.getPrototypeOf||function(e){return e=i(e),n(e,o)?e[o]:"function"==typeof e.constructor&&e instanceof e.constructor?e.constructor.prototype:e instanceof Object?a:null}},function(e,t,r){var n=r(33),i=n["__core-js_shared__"]||(n["__core-js_shared__"]={});e.exports=function(e){return i[e]||(i[e]={})}},function(e,t){e.exports=function(e,t){return{enumerable:!(1&e),configurable:!(2&e),writable:!(4&e),value:t}}},function(e,t,r){"use strict";var n=r(268)(!0);r(269)(String,"String",function(e){this._t=String(e),this._i=0},function(){var e,t=this._t,r=this._i;return r>=t.length?{value:void 0,done:!0}:(e=n(t,r),this._i+=e.length,{value:e,done:!1})})},function(e,t,r){var n=r(126),i=r(57);e.exports=function(e){return function(t,r){var o,a,s=String(i(t)),u=n(r),l=s.length;return u<0||u>=l?e?"":void 0:(o=s.charCodeAt(u))<55296||o>56319||u+1===l||(a=s.charCodeAt(u+1))<56320||a>57343?e?s.charAt(u):o:e?s.slice(u,u+2):a-56320+(o-55296<<10)+65536}}},function(e,t,r){"use strict";var n=r(270),i=r(30),o=r(75),a=r(61),s=r(108),u=r(507),l=r(181),c=r(513),p=r(17)("iterator"),f=!([].keys&&"next"in[].keys()),d=function(){return this};e.exports=function(e,t,r,h,m,v,g){u(r,t,h);var y,_,b,S=function(e){if(!f&&e in C)return C[e];switch(e){case"keys":return function keys(){return new r(this,e)};case"values":return function values(){return new r(this,e)}}return function entries(){return new r(this,e)}},k=t+" Iterator",x="values"==m,E=!1,C=e.prototype,w=C[p]||C["@@iterator"]||m&&C[m],D=w||S(m),A=m?x?S("entries"):D:void 0,R="Array"==t&&C.entries||w;if(R&&(b=c(R.call(new e)))!==Object.prototype&&b.next&&(l(b,k,!0),n||"function"==typeof b[p]||a(b,p,d)),x&&w&&"values"!==w.name&&(E=!0,D=function values(){return w.call(this)}),n&&!g||!f&&!E&&C[p]||a(C,p,D),s[t]=D,s[k]=d,m)if(y={values:x?D:S("values"),keys:v?D:S("keys"),entries:A},g)for(_ in y)_ in C||o(C,_,y[_]);else i(i.P+i.F*(f||E),t,y);return y}},function(e,t){e.exports=!1},function(e,t,r){var n=r(510),i=r(273);e.exports=Object.keys||function keys(e){return n(e,i)}},function(e,t,r){var n=r(126),i=Math.max,o=Math.min;e.exports=function(e,t){return(e=n(e))<0?i(e+t,0):o(e,t)}},function(e,t){e.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},function(e,t,r){var n=r(33).document;e.exports=n&&n.documentElement},function(e,t,r){var n=r(62),i=r(128),o=r(17)("species");e.exports=function(e,t){var r,a=n(e).constructor;return void 0===a||void 0==(r=n(a)[o])?t:i(r)}},function(e,t,r){var n,i,o,a=r(127),s=r(525),u=r(274),l=r(179),c=r(33),p=c.process,f=c.setImmediate,d=c.clearImmediate,h=c.MessageChannel,m=c.Dispatch,v=0,g={},y=function(){var e=+this;if(g.hasOwnProperty(e)){var t=g[e];delete g[e],t()}},_=function(e){y.call(e.data)};f&&d||(f=function setImmediate(e){for(var t=[],r=1;arguments.length>r;)t.push(arguments[r++]);return g[++v]=function(){s("function"==typeof e?e:Function(e),t)},n(v),v},d=function clearImmediate(e){delete g[e]},"process"==r(105)(p)?n=function(e){p.nextTick(a(y,e,1))}:m&&m.now?n=function(e){m.now(a(y,e,1))}:h?(o=(i=new h).port2,i.port1.onmessage=_,n=a(o.postMessage,o,1)):c.addEventListener&&"function"==typeof postMessage&&!c.importScripts?(n=function(e){c.postMessage(e+"","*")},c.addEventListener("message",_,!1)):n="onreadystatechange"in l("script")?function(e){u.appendChild(l("script")).onreadystatechange=function(){u.removeChild(this),y.call(e)}}:function(e){setTimeout(a(y,e,1),0)}),e.exports={set:f,clear:d}},function(e,t){e.exports=function(e){try{return{e:!1,v:e()}}catch(e){return{e:!0,v:e}}}},function(e,t,r){var n=r(62),i=r(76),o=r(182);e.exports=function(e,t){if(n(e),i(t)&&t.constructor===e)return t;var r=o.f(e);return(0,r.resolve)(t),r.promise}},function(e,t,r){var n=r(76),i=r(105),o=r(17)("match");e.exports=function(e){var t;return n(e)&&(void 0!==(t=e[o])?!!t:"RegExp"==i(e))}},function(e,t,r){var n=r(22),i=r(15),o=r(55);e.exports=function(e,t){var r=(i.Object||{})[e]||Object[e],a={};a[e]=t(r),n(n.S+n.F*o(function(){r(1)}),"Object",a)}},function(e,t,r){var n=r(99);e.exports=Array.isArray||function isArray(e){return"Array"==n(e)}},function(e,t,r){var n=r(262),i=r(174).concat("length","prototype");t.f=Object.getOwnPropertyNames||function getOwnPropertyNames(e){return n(e,i)}},function(e,t,r){var n=r(132),i=r(101),o=r(73),a=r(168),s=r(56),u=r(261),l=Object.getOwnPropertyDescriptor;t.f=r(47)?l:function getOwnPropertyDescriptor(e,t){if(e=o(e),t=a(t,!0),u)try{return l(e,t)}catch(e){}if(s(e,t))return i(!n.f.call(e,t),e[t])}},function(e,t){var r={}.toString;e.exports=Array.isArray||function(e){return"[object Array]"==r.call(e)}},function(e,t,r){e.exports={default:r(577),__esModule:!0}},function(e,t,r){"use strict";var n=r(102),i=r(187),o=r(132),a=r(74),s=r(165),u=Object.assign;e.exports=!u||r(55)(function(){var e={},t={},r=Symbol(),n="abcdefghijklmnopqrst";return e[r]=7,n.split("").forEach(function(e){t[e]=e}),7!=u({},e)[r]||Object.keys(u({},t)).join("")!=n})?function assign(e,t){for(var r=a(e),u=arguments.length,l=1,c=i.f,p=o.f;u>l;)for(var f,d=s(arguments[l++]),h=c?n(d).concat(c(d)):n(d),m=h.length,v=0;m>v;)p.call(d,f=h[v++])&&(r[f]=d[f]);return r}:u},function(e,t,r){"use strict";var n=r(110),i=r(13),o=r(288),a=(r(289),r(133));r(8),r(581);function ReactComponent(e,t,r){this.props=e,this.context=t,this.refs=a,this.updater=r||o}function ReactPureComponent(e,t,r){this.props=e,this.context=t,this.refs=a,this.updater=r||o}function ComponentDummy(){}ReactComponent.prototype.isReactComponent={},ReactComponent.prototype.setState=function(e,t){"object"!=typeof e&&"function"!=typeof e&&null!=e&&n("85"),this.updater.enqueueSetState(this,e),t&&this.updater.enqueueCallback(this,t,"setState")},ReactComponent.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this),e&&this.updater.enqueueCallback(this,e,"forceUpdate")},ComponentDummy.prototype=ReactComponent.prototype,ReactPureComponent.prototype=new ComponentDummy,ReactPureComponent.prototype.constructor=ReactPureComponent,i(ReactPureComponent.prototype,ReactComponent.prototype),ReactPureComponent.prototype.isPureReactComponent=!0,e.exports={Component:ReactComponent,PureComponent:ReactPureComponent}},function(e,t,r){"use strict";r(9);var n={isMounted:function(e){return!1},enqueueCallback:function(e,t){},enqueueForceUpdate:function(e){},enqueueReplaceState:function(e,t){},enqueueSetState:function(e,t){}};e.exports=n},function(e,t,r){"use strict";var n=!1;e.exports=n},function(e,t,r){"use strict";var n="function"==typeof Symbol&&Symbol.for&&Symbol.for("react.element")||60103;e.exports=n},function(e,t,r){"use strict";var n=r(589);e.exports=function(e){return n(e,!1)}},function(e,t,r){"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=r(294),i=r(606),o=r(607),a=r(608),s=r(298);r(297);r.d(t,"createStore",function(){return n.b}),r.d(t,"combineReducers",function(){return i.a}),r.d(t,"bindActionCreators",function(){return o.a}),r.d(t,"applyMiddleware",function(){return a.a}),r.d(t,"compose",function(){return s.a})},function(e,t,r){"use strict";r.d(t,"a",function(){return o}),t.b=function createStore(e,t,r){var a;"function"==typeof t&&void 0===r&&(r=t,t=void 0);if(void 0!==r){if("function"!=typeof r)throw new Error("Expected the enhancer to be a function.");return r(createStore)(e,t)}if("function"!=typeof e)throw new Error("Expected the reducer to be a function.");var s=e;var u=t;var l=[];var c=l;var p=!1;function ensureCanMutateNextListeners(){c===l&&(c=l.slice())}function getState(){return u}function subscribe(e){if("function"!=typeof e)throw new Error("Expected listener to be a function.");var t=!0;return ensureCanMutateNextListeners(),c.push(e),function unsubscribe(){if(t){t=!1,ensureCanMutateNextListeners();var r=c.indexOf(e);c.splice(r,1)}}}function dispatch(e){if(!n.a(e))throw new Error("Actions must be plain objects. Use custom middleware for async actions.");if(void 0===e.type)throw new Error('Actions may not have an undefined "type" property. Have you misspelled a constant?');if(p)throw new Error("Reducers may not dispatch actions.");try{p=!0,u=s(u,e)}finally{p=!1}for(var t=l=c,r=0;ri?0:i+t),(r=r>i?i:r)<0&&(r+=i),i=t>r?0:r-t>>>0,t>>>=0;for(var o=Array(i);++nf))return!1;var h=c.get(e);if(h&&c.get(t))return h==t;var m=-1,v=!0,g=r&s?new n:void 0;for(c.set(e,t),c.set(t,e);++m0?("string"==typeof t||a.objectMode||Object.getPrototypeOf(t)===l.prototype||(t=function _uint8ArrayToBuffer(e){return l.from(e)}(t)),n?a.endEmitted?e.emit("error",new Error("stream.unshift() after end event")):addChunk(e,a,t,!0):a.ended?e.emit("error",new Error("stream.push() after EOF")):(a.reading=!1,a.decoder&&!r?(t=a.decoder.write(t),a.objectMode||0!==t.length?addChunk(e,a,t,!1):maybeReadMore(e,a)):addChunk(e,a,t,!1))):n||(a.reading=!1));return function needMoreData(e){return!e.ended&&(e.needReadable||e.lengtht.highWaterMark&&(t.highWaterMark=function computeNewHighWaterMark(e){return e>=y?e=y:(e--,e|=e>>>1,e|=e>>>2,e|=e>>>4,e|=e>>>8,e|=e>>>16,e++),e}(e)),e<=t.length?e:t.ended?t.length:(t.needReadable=!0,0))}function emitReadable(e){var t=e._readableState;t.needReadable=!1,t.emittedReadable||(d("emitReadable",t.flowing),t.emittedReadable=!0,t.sync?i.nextTick(emitReadable_,e):emitReadable_(e))}function emitReadable_(e){d("emit readable"),e.emit("readable"),flow(e)}function maybeReadMore(e,t){t.readingMore||(t.readingMore=!0,i.nextTick(maybeReadMore_,e,t))}function maybeReadMore_(e,t){for(var r=t.length;!t.reading&&!t.flowing&&!t.ended&&t.length=t.length?(r=t.decoder?t.buffer.join(""):1===t.buffer.length?t.buffer.head.data:t.buffer.concat(t.length),t.buffer.clear()):r=function fromListPartial(e,t,r){var n;eo.length?o.length:e;if(a===o.length?i+=o:i+=o.slice(0,e),0===(e-=a)){a===o.length?(++n,r.next?t.head=r.next:t.head=t.tail=null):(t.head=r,r.data=o.slice(a));break}++n}return t.length-=n,i}(e,t):function copyFromBuffer(e,t){var r=l.allocUnsafe(e),n=t.head,i=1;n.data.copy(r),e-=n.data.length;for(;n=n.next;){var o=n.data,a=e>o.length?o.length:e;if(o.copy(r,r.length-e,0,a),0===(e-=a)){a===o.length?(++i,n.next?t.head=n.next:t.head=t.tail=null):(t.head=n,n.data=o.slice(a));break}++i}return t.length-=i,r}(e,t);return n}(e,t.buffer,t.decoder),r);var r}function endReadable(e){var t=e._readableState;if(t.length>0)throw new Error('"endReadable()" called on non-empty stream');t.endEmitted||(t.ended=!0,i.nextTick(endReadableNT,t,e))}function endReadableNT(e,t){e.endEmitted||0!==e.length||(e.endEmitted=!0,t.readable=!1,t.emit("end"))}function indexOf(e,t){for(var r=0,n=e.length;r=t.highWaterMark||t.ended))return d("read: emitReadable",t.length,t.ended),0===t.length&&t.ended?endReadable(this):emitReadable(this),null;if(0===(e=howMuchToRead(e,t))&&t.ended)return 0===t.length&&endReadable(this),null;var n,i=t.needReadable;return d("need readable",i),(0===t.length||t.length-e0?fromList(e,t):null)?(t.needReadable=!0,e=0):t.length-=e,0===t.length&&(t.ended||(t.needReadable=!0),r!==e&&t.ended&&endReadable(this)),null!==n&&this.emit("data",n),n},Readable.prototype._read=function(e){this.emit("error",new Error("_read() is not implemented"))},Readable.prototype.pipe=function(e,t){var r=this,o=this._readableState;switch(o.pipesCount){case 0:o.pipes=e;break;case 1:o.pipes=[o.pipes,e];break;default:o.pipes.push(e)}o.pipesCount+=1,d("pipe count=%d opts=%j",o.pipesCount,t);var u=(!t||!1!==t.end)&&e!==n.stdout&&e!==n.stderr?onend:unpipe;function onunpipe(t,n){d("onunpipe"),t===r&&n&&!1===n.hasUnpiped&&(n.hasUnpiped=!0,function cleanup(){d("cleanup"),e.removeListener("close",onclose),e.removeListener("finish",onfinish),e.removeListener("drain",l),e.removeListener("error",onerror),e.removeListener("unpipe",onunpipe),r.removeListener("end",onend),r.removeListener("end",unpipe),r.removeListener("data",ondata),c=!0,!o.awaitDrain||e._writableState&&!e._writableState.needDrain||l()}())}function onend(){d("onend"),e.end()}o.endEmitted?i.nextTick(u):r.once("end",u),e.on("unpipe",onunpipe);var l=function pipeOnDrain(e){return function(){var t=e._readableState;d("pipeOnDrain",t.awaitDrain),t.awaitDrain&&t.awaitDrain--,0===t.awaitDrain&&s(e,"data")&&(t.flowing=!0,flow(e))}}(r);e.on("drain",l);var c=!1;var p=!1;function ondata(t){d("ondata"),p=!1,!1!==e.write(t)||p||((1===o.pipesCount&&o.pipes===e||o.pipesCount>1&&-1!==indexOf(o.pipes,e))&&!c&&(d("false write response, pause",r._readableState.awaitDrain),r._readableState.awaitDrain++,p=!0),r.pause())}function onerror(t){d("onerror",t),unpipe(),e.removeListener("error",onerror),0===s(e,"error")&&e.emit("error",t)}function onclose(){e.removeListener("finish",onfinish),unpipe()}function onfinish(){d("onfinish"),e.removeListener("close",onclose),unpipe()}function unpipe(){d("unpipe"),r.unpipe(e)}return r.on("data",ondata),function prependListener(e,t,r){if("function"==typeof e.prependListener)return e.prependListener(t,r);e._events&&e._events[t]?a(e._events[t])?e._events[t].unshift(r):e._events[t]=[r,e._events[t]]:e.on(t,r)}(e,"error",onerror),e.once("close",onclose),e.once("finish",onfinish),e.emit("pipe",r),o.flowing||(d("pipe resume"),r.resume()),e},Readable.prototype.unpipe=function(e){var t=this._readableState,r={hasUnpiped:!1};if(0===t.pipesCount)return this;if(1===t.pipesCount)return e&&e!==t.pipes?this:(e||(e=t.pipes),t.pipes=null,t.pipesCount=0,t.flowing=!1,e&&e.emit("unpipe",this,r),this);if(!e){var n=t.pipes,i=t.pipesCount;t.pipes=null,t.pipesCount=0,t.flowing=!1;for(var o=0;o=0&&(e._idleTimeoutId=setTimeout(function onTimeout(){e._onTimeout&&e._onTimeout()},t))},r(707),t.setImmediate="undefined"!=typeof self&&self.setImmediate||void 0!==e&&e.setImmediate||this&&this.setImmediate,t.clearImmediate="undefined"!=typeof self&&self.clearImmediate||void 0!==e&&e.clearImmediate||this&&this.clearImmediate}).call(t,r(18))},function(e,t,r){"use strict";var n=r(148).Buffer,i=n.isEncoding||function(e){switch((e=""+e)&&e.toLowerCase()){case"hex":case"utf8":case"utf-8":case"ascii":case"binary":case"base64":case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":case"raw":return!0;default:return!1}};function StringDecoder(e){var t;switch(this.encoding=function normalizeEncoding(e){var t=function _normalizeEncoding(e){if(!e)return"utf8";for(var t;;)switch(e){case"utf8":case"utf-8":return"utf8";case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return"utf16le";case"latin1":case"binary":return"latin1";case"base64":case"ascii":case"hex":return e;default:if(t)return;e=(""+e).toLowerCase(),t=!0}}(e);if("string"!=typeof t&&(n.isEncoding===i||!i(e)))throw new Error("Unknown encoding: "+e);return t||e}(e),this.encoding){case"utf16le":this.text=utf16Text,this.end=utf16End,t=4;break;case"utf8":this.fillLast=utf8FillLast,t=4;break;case"base64":this.text=base64Text,this.end=base64End,t=3;break;default:return this.write=simpleWrite,void(this.end=simpleEnd)}this.lastNeed=0,this.lastTotal=0,this.lastChar=n.allocUnsafe(t)}function utf8CheckByte(e){return e<=127?0:e>>5==6?2:e>>4==14?3:e>>3==30?4:e>>6==2?-1:-2}function utf8FillLast(e){var t=this.lastTotal-this.lastNeed,r=function utf8CheckExtraBytes(e,t,r){if(128!=(192&t[0]))return e.lastNeed=0,"�";if(e.lastNeed>1&&t.length>1){if(128!=(192&t[1]))return e.lastNeed=1,"�";if(e.lastNeed>2&&t.length>2&&128!=(192&t[2]))return e.lastNeed=2,"�"}}(this,e);return void 0!==r?r:this.lastNeed<=e.length?(e.copy(this.lastChar,t,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal)):(e.copy(this.lastChar,t,0,e.length),void(this.lastNeed-=e.length))}function utf16Text(e,t){if((e.length-t)%2==0){var r=e.toString("utf16le",t);if(r){var n=r.charCodeAt(r.length-1);if(n>=55296&&n<=56319)return this.lastNeed=2,this.lastTotal=4,this.lastChar[0]=e[e.length-2],this.lastChar[1]=e[e.length-1],r.slice(0,-1)}return r}return this.lastNeed=1,this.lastTotal=2,this.lastChar[0]=e[e.length-1],e.toString("utf16le",t,e.length-1)}function utf16End(e){var t=e&&e.length?this.write(e):"";if(this.lastNeed){var r=this.lastTotal-this.lastNeed;return t+this.lastChar.toString("utf16le",0,r)}return t}function base64Text(e,t){var r=(e.length-t)%3;return 0===r?e.toString("base64",t):(this.lastNeed=3-r,this.lastTotal=3,1===r?this.lastChar[0]=e[e.length-1]:(this.lastChar[0]=e[e.length-2],this.lastChar[1]=e[e.length-1]),e.toString("base64",t,e.length-r))}function base64End(e){var t=e&&e.length?this.write(e):"";return this.lastNeed?t+this.lastChar.toString("base64",0,3-this.lastNeed):t}function simpleWrite(e){return e.toString(this.encoding)}function simpleEnd(e){return e&&e.length?this.write(e):""}t.StringDecoder=StringDecoder,StringDecoder.prototype.write=function(e){if(0===e.length)return"";var t,r;if(this.lastNeed){if(void 0===(t=this.fillLast(e)))return"";r=this.lastNeed,this.lastNeed=0}else r=0;return r=0)return i>0&&(e.lastNeed=i-1),i;if(--n=0)return i>0&&(e.lastNeed=i-2),i;if(--n=0)return i>0&&(2===i?i=0:e.lastNeed=i-3),i;return 0}(this,e,t);if(!this.lastNeed)return e.toString("utf8",t);this.lastTotal=r;var n=e.length-(r-this.lastNeed);return e.copy(this.lastChar,0,n),e.toString("utf8",t,n)},StringDecoder.prototype.fillLast=function(e){if(this.lastNeed<=e.length)return e.copy(this.lastChar,this.lastTotal-this.lastNeed,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal);e.copy(this.lastChar,this.lastTotal-this.lastNeed,0,e.length),this.lastNeed-=e.length}},function(e,t,r){"use strict";e.exports=Transform;var n=r(67),i=r(112);function Transform(e){if(!(this instanceof Transform))return new Transform(e);n.call(this,e),this._transformState={afterTransform:function afterTransform(e,t){var r=this._transformState;r.transforming=!1;var n=r.writecb;if(!n)return this.emit("error",new Error("write callback called multiple times"));r.writechunk=null,r.writecb=null,null!=t&&this.push(t),n(e);var i=this._readableState;i.reading=!1,(i.needReadable||i.length=0?r&&i?i-1:i:1:!1!==e&&n(e)}},function(e,t,r){"use strict";e.exports=r(723)()?Object.assign:r(724)},function(e,t,r){"use strict";var n,i,o,a,s,u=r(69),l=function(e,t){return t};try{Object.defineProperty(l,"length",{configurable:!0,writable:!1,enumerable:!1,value:1})}catch(e){}1===l.length?(n={configurable:!0,writable:!1,enumerable:!1},i=Object.defineProperty,e.exports=function(e,t){return t=u(t),e.length===t?e:(n.value=t,i(e,"length",n))}):(a=r(339),s=[],o=function(e){var t,r=0;if(s[e])return s[e];for(t=[];e--;)t.push("a"+(++r).toString(36));return new Function("fn","return function ("+t.join(", ")+") { return fn.apply(this, arguments); };")},e.exports=function(e,t){var r;if(t=u(t),e.length===t)return e;r=o(t)(e);try{a(r,e)}catch(e){}return r})},function(e,t,r){"use strict";var n=r(85),i=Object.defineProperty,o=Object.getOwnPropertyDescriptor,a=Object.getOwnPropertyNames,s=Object.getOwnPropertySymbols;e.exports=function(e,t){var r,u=Object(n(t));if(e=Object(n(e)),a(u).forEach(function(n){try{i(e,n,o(t,n))}catch(e){r=e}}),"function"==typeof s&&s(u).forEach(function(n){try{i(e,n,o(t,n))}catch(e){r=e}}),void 0!==r)throw r;return e}},function(e,t,r){"use strict";var n=r(58),i=r(149),o=Function.prototype.call;e.exports=function(e,t){var r={},a=arguments[2];return n(t),i(e,function(e,n,i,s){r[n]=o.call(t,a,e,n,i,s)}),r}},function(e,t){e.exports=function isPromise(e){return!!e&&("object"==typeof e||"function"==typeof e)&&"function"==typeof e.then}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){return{statePlugins:{err:{reducers:(0,n.default)(e),actions:i,selectors:o}}}};var n=function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}(r(343)),i=_interopRequireWildcard(r(134)),o=_interopRequireWildcard(r(348));function _interopRequireWildcard(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[r]=e[r]);return t.default=e,t}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=_interopRequireDefault(r(24)),i=_interopRequireDefault(r(25));t.default=function(e){var t;return t={},(0,n.default)(t,o.NEW_THROWN_ERR,function(t,r){var n=r.payload,o=(0,i.default)(u,n,{type:"thrown"});return t.update("errors",function(e){return(e||(0,a.List)()).push((0,a.fromJS)(o))}).update("errors",function(t){return(0,s.default)(t,e.getSystem())})}),(0,n.default)(t,o.NEW_THROWN_ERR_BATCH,function(t,r){var n=r.payload;return n=n.map(function(e){return(0,a.fromJS)((0,i.default)(u,e,{type:"thrown"}))}),t.update("errors",function(e){return(e||(0,a.List)()).concat((0,a.fromJS)(n))}).update("errors",function(t){return(0,s.default)(t,e.getSystem())})}),(0,n.default)(t,o.NEW_SPEC_ERR,function(t,r){var n=r.payload,i=(0,a.fromJS)(n);return i=i.set("type","spec"),t.update("errors",function(e){return(e||(0,a.List)()).push((0,a.fromJS)(i)).sortBy(function(e){return e.get("line")})}).update("errors",function(t){return(0,s.default)(t,e.getSystem())})}),(0,n.default)(t,o.NEW_SPEC_ERR_BATCH,function(t,r){var n=r.payload;return n=n.map(function(e){return(0,a.fromJS)((0,i.default)(u,e,{type:"spec"}))}),t.update("errors",function(e){return(e||(0,a.List)()).concat((0,a.fromJS)(n))}).update("errors",function(t){return(0,s.default)(t,e.getSystem())})}),(0,n.default)(t,o.NEW_AUTH_ERR,function(t,r){var n=r.payload,o=(0,a.fromJS)((0,i.default)({},n));return o=o.set("type","auth"),t.update("errors",function(e){return(e||(0,a.List)()).push((0,a.fromJS)(o))}).update("errors",function(t){return(0,s.default)(t,e.getSystem())})}),(0,n.default)(t,o.CLEAR,function(e,t){var r=t.payload;if(!r||!e.get("errors"))return e;var n=e.get("errors").filter(function(e){return e.keySeq().every(function(t){var n=e.get(t),i=r[t];return!i||n!==i})});return e.merge({errors:n})}),(0,n.default)(t,o.CLEAR_BY,function(e,t){var r=t.payload;if(!r||"function"!=typeof r)return e;var n=e.get("errors").filter(function(e){return r(e)});return e.merge({errors:n})}),t};var o=r(134),a=r(7),s=_interopRequireDefault(r(344));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}var u={line:0,level:"error",message:"Unknown error"}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function transformErrors(e,t){var r={jsSpec:t.specSelectors.specJson().toJS()};return(0,n.default)(i,function(e,t){try{var n=t.transform(e,r);return n.filter(function(e){return!!e})}catch(t){return console.error("Transformer error:",t),e}},e).filter(function(e){return!!e}).map(function(e){return!e.get("line")&&e.get("path"),e})};var n=function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}(r(771));function _interopRequireWildcard(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[r]=e[r]);return t.default=e,t}var i=[_interopRequireWildcard(r(345)),_interopRequireWildcard(r(346)),_interopRequireWildcard(r(347))]},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.transform=function transform(e){return e.map(function(e){var t=e.get("message").indexOf("is not of a type(s)");if(t>-1){var r=e.get("message").slice(t+"is not of a type(s)".length).split(",");return e.set("message",e.get("message").slice(0,t)+function makeNewMessage(e){return e.reduce(function(e,t,r,n){return r===n.length-1&&n.length>1?e+"or "+t:n[r+1]&&n.length>2?e+t+", ":n[r+1]?e+t+" ":e+t},"should be a")}(r))}return e})}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.transform=function transform(e,t){t.jsSpec;return e};(function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}})(r(145)),r(7)},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.transform=function transform(e){return e.map(function(e){return e.set("message",function removeSubstring(e,t){return e.replace(new RegExp(t,"g"),"")}(e.get("message"),"instance."))})}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.lastError=t.allErrors=void 0;var n=r(7),i=r(59),o=t.allErrors=(0,i.createSelector)(function state(e){return e},function(e){return e.get("errors",(0,n.List)())});t.lastError=(0,i.createSelector)(o,function(e){return e.last()})},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(){return{statePlugins:{layout:{reducers:n.default,actions:i,selectors:o}}}};var n=function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}(r(350)),i=_interopRequireWildcard(r(212)),o=_interopRequireWildcard(r(351));function _interopRequireWildcard(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[r]=e[r]);return t.default=e,t}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n,i=function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}(r(24)),o=r(7),a=r(212);t.default=(n={},(0,i.default)(n,a.UPDATE_LAYOUT,function(e,t){return e.set("layout",t.payload)}),(0,i.default)(n,a.UPDATE_FILTER,function(e,t){return e.set("filter",t.payload)}),(0,i.default)(n,a.SHOW,function(e,t){var r=t.payload.shown,n=(0,o.fromJS)(t.payload.thing);return e.update("shown",(0,o.fromJS)({}),function(e){return e.set(n,r)})}),(0,i.default)(n,a.UPDATE_MODE,function(e,t){var r=t.payload.thing,n=t.payload.mode;return e.setIn(["modes"].concat(r),(n||"")+"")}),n)},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.showSummary=t.whatMode=t.isShown=t.currentFilter=t.current=void 0;var n=function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}(r(86)),i=r(59),o=r(10),a=r(7);t.current=function current(e){return e.get("layout")},t.currentFilter=function currentFilter(e){return e.get("filter")};var s=t.isShown=function isShown(e,t,r){return t=(0,o.normalizeArray)(t),e.get("shown",(0,a.fromJS)({})).get((0,a.fromJS)(t),r)};t.whatMode=function whatMode(e,t){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"";return t=(0,o.normalizeArray)(t),e.getIn(["modes"].concat((0,n.default)(t)),r)},t.showSummary=(0,i.createSelector)(function state(e){return e},function(e){return!s(e,"editor")})},function(e,t,r){var n=r(36);e.exports=function(e,t,r,i){try{return i?t(n(r)[0],r[1]):t(r)}catch(t){var o=e.return;throw void 0!==o&&n(o.call(e)),t}}},function(e,t,r){var n=r(72),i=r(20)("iterator"),o=Array.prototype;e.exports=function(e){return void 0!==e&&(n.Array===e||o[i]===e)}},function(e,t,r){var n=r(20)("iterator"),i=!1;try{var o=[7][n]();o.return=function(){i=!0},Array.from(o,function(){throw 2})}catch(e){}e.exports=function(e,t){if(!t&&!i)return!1;var r=!1;try{var o=[7],a=o[n]();a.next=function(){return{done:r=!0}},o[n]=function(){return a},e(o)}catch(e){}return r}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(){return{statePlugins:{spec:{wrapActions:a,reducers:n.default,actions:i,selectors:o}}}};var n=function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}(r(356)),i=_interopRequireWildcard(r(214)),o=_interopRequireWildcard(r(213)),a=_interopRequireWildcard(r(369));function _interopRequireWildcard(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[r]=e[r]);return t.default=e,t}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n,i=_interopRequireDefault(r(24)),o=_interopRequireDefault(r(25)),a=_interopRequireDefault(r(86)),s=r(7),u=r(10),l=_interopRequireDefault(r(32)),c=r(213),p=r(214);function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}t.default=(n={},(0,i.default)(n,p.UPDATE_SPEC,function(e,t){return"string"==typeof t.payload?e.set("spec",t.payload):e}),(0,i.default)(n,p.UPDATE_URL,function(e,t){return e.set("url",t.payload+"")}),(0,i.default)(n,p.UPDATE_JSON,function(e,t){return e.set("json",(0,u.fromJSOrdered)(t.payload))}),(0,i.default)(n,p.UPDATE_RESOLVED,function(e,t){return e.setIn(["resolved"],(0,u.fromJSOrdered)(t.payload))}),(0,i.default)(n,p.UPDATE_RESOLVED_SUBTREE,function(e,t){var r=t.payload,n=r.value,i=r.path;return e.setIn(["resolvedSubtrees"].concat((0,a.default)(i)),(0,u.fromJSOrdered)(n))}),(0,i.default)(n,p.UPDATE_PARAM,function(e,t){var r=t.payload,n=r.path,i=r.paramName,o=r.paramIn,s=r.param,u=r.value,l=r.isXml,c=void 0;c=s&&s.hashCode&&!o&&!i?s.get("name")+"."+s.get("in")+".hash-"+s.hashCode():i+"."+o;var p=l?"value_xml":"value";return e.setIn(["meta","paths"].concat((0,a.default)(n),["parameters",c,p]),u)}),(0,i.default)(n,p.VALIDATE_PARAMS,function(e,t){var r=t.payload,n=r.pathMethod,i=r.isOAS3,o=e.getIn(["meta","paths"].concat((0,a.default)(n)),(0,s.fromJS)({})),l=/xml/i.test(o.get("consumes_value")),p=c.operationWithMeta.apply(void 0,[e].concat((0,a.default)(n)));return e.updateIn(["meta","paths"].concat((0,a.default)(n),["parameters"]),(0,s.fromJS)({}),function(e){return p.get("parameters",(0,s.List)()).reduce(function(e,t){var r=(0,u.validateParam)(t,l,i);return e.setIn([t.get("name")+"."+t.get("in"),"errors"],(0,s.fromJS)(r))},e)})}),(0,i.default)(n,p.CLEAR_VALIDATE_PARAMS,function(e,t){var r=t.payload.pathMethod;return e.updateIn(["meta","paths"].concat((0,a.default)(r),["parameters"]),(0,s.fromJS)([]),function(e){return e.map(function(e){return e.set("errors",(0,s.fromJS)([]))})})}),(0,i.default)(n,p.SET_RESPONSE,function(e,t){var r=t.payload,n=r.res,i=r.path,a=r.method,s=void 0;(s=n.error?(0,o.default)({error:!0,name:n.err.name,message:n.err.message,statusCode:n.err.statusCode},n.err.response):n).headers=s.headers||{};var c=e.setIn(["responses",i,a],(0,u.fromJSOrdered)(s));return l.default.Blob&&n.data instanceof l.default.Blob&&(c=c.setIn(["responses",i,a,"text"],n.data)),c}),(0,i.default)(n,p.SET_REQUEST,function(e,t){var r=t.payload,n=r.req,i=r.path,o=r.method;return e.setIn(["requests",i,o],(0,u.fromJSOrdered)(n))}),(0,i.default)(n,p.SET_MUTATED_REQUEST,function(e,t){var r=t.payload,n=r.req,i=r.path,o=r.method;return e.setIn(["mutatedRequests",i,o],(0,u.fromJSOrdered)(n))}),(0,i.default)(n,p.UPDATE_OPERATION_META_VALUE,function(e,t){var r=t.payload,n=r.path,i=r.value,o=r.key,u=["paths"].concat((0,a.default)(n)),l=["meta","paths"].concat((0,a.default)(n));return e.getIn(["json"].concat((0,a.default)(u)))||e.getIn(["resolved"].concat((0,a.default)(u)))||e.getIn(["resolvedSubtrees"].concat((0,a.default)(u)))?e.setIn([].concat((0,a.default)(l),[o]),(0,s.fromJS)(i)):e}),(0,i.default)(n,p.CLEAR_RESPONSE,function(e,t){var r=t.payload,n=r.path,i=r.method;return e.deleteIn(["responses",n,i])}),(0,i.default)(n,p.CLEAR_REQUEST,function(e,t){var r=t.payload,n=r.path,i=r.method;return e.deleteIn(["requests",n,i])}),(0,i.default)(n,p.SET_SCHEME,function(e,t){var r=t.payload,n=r.scheme,i=r.path,o=r.method;return i&&o?e.setIn(["scheme",i,o],n):i||o?void 0:e.setIn(["scheme","_defaultScheme"],n)}),n)},function(e,t,r){var n=r(36),i=r(100),o=r(20)("species");e.exports=function(e,t){var r,a=n(e).constructor;return void 0===a||void 0==(r=n(a)[o])?t:i(r)}},function(e,t,r){var n,i,o,a=r(53),s=r(779),u=r(263),l=r(167),c=r(23),p=c.process,f=c.setImmediate,d=c.clearImmediate,h=c.MessageChannel,m=c.Dispatch,v=0,g={},y=function(){var e=+this;if(g.hasOwnProperty(e)){var t=g[e];delete g[e],t()}},_=function(e){y.call(e.data)};f&&d||(f=function setImmediate(e){for(var t=[],r=1;arguments.length>r;)t.push(arguments[r++]);return g[++v]=function(){s("function"==typeof e?e:Function(e),t)},n(v),v},d=function clearImmediate(e){delete g[e]},"process"==r(99)(p)?n=function(e){p.nextTick(a(y,e,1))}:m&&m.now?n=function(e){m.now(a(y,e,1))}:h?(o=(i=new h).port2,i.port1.onmessage=_,n=a(o.postMessage,o,1)):c.addEventListener&&"function"==typeof postMessage&&!c.importScripts?(n=function(e){c.postMessage(e+"","*")},c.addEventListener("message",_,!1)):n="onreadystatechange"in l("script")?function(e){u.appendChild(l("script")).onreadystatechange=function(){u.removeChild(this),y.call(e)}}:function(e){setTimeout(a(y,e,1),0)}),e.exports={set:f,clear:d}},function(e,t){e.exports=function(e){try{return{e:!1,v:e()}}catch(e){return{e:!0,v:e}}}},function(e,t,r){var n=r(36),i=r(29),o=r(216);e.exports=function(e,t){if(n(e),i(t)&&t.constructor===e)return t;var r=o.f(e);return(0,r.resolve)(t),r.promise}},function(e,t,r){e.exports=r(784)},function(e,t,r){"use strict";t.__esModule=!0;var n=function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}(r(151));t.default=function(e){return function(){var t=e.apply(this,arguments);return new n.default(function(e,r){return function step(i,o){try{var a=t[i](o),s=a.value}catch(e){return void r(e)}if(!a.done)return n.default.resolve(s).then(function(e){step("next",e)},function(e){step("throw",e)});e(s)}("next")})}}},function(e,t,r){"use strict";var n=r(89);e.exports=new n({include:[r(364)]})},function(e,t,r){"use strict";var n=r(89);e.exports=new n({include:[r(219)],implicit:[r(792),r(793),r(794),r(795)]})},function(e,t,r){var n=r(64),i=r(21),o=r(50),a="[object String]";e.exports=function isString(e){return"string"==typeof e||!i(e)&&o(e)&&n(e)==a}},function(e,t,r){var n=r(154),i=r(82),o=r(142),a=r(39),s=r(83);e.exports=function baseSet(e,t,r,u){if(!a(e))return e;for(var l=-1,c=(t=i(t,e)).length,p=c-1,f=e;null!=f&&++l.":"function"==typeof t?" Instead of passing a class like Foo, pass React.createElement(Foo) or .":null!=t&&void 0!==t.props?" This may be caused by unintentionally loading two independent copies of React.":"");var o,s=a.createElement(A,{child:t});if(e){var u=f.get(e);o=u._processChildContext(u._context)}else o=g;var l=getTopLevelWrapperInContainer(r);if(l){var c=l._currentElement.props.child;if(b(c,t)){var p=l._renderedComponent.getPublicInstance(),d=i&&function(){i.call(p)};return R._updateRootComponent(l,s,o,r,d),p}R.unmountComponentAtNode(r)}var h=getReactRootElementInContainer(r),v=h&&!!internalGetID(h),y=hasNonRootReactChild(r),_=v&&!l&&!y,S=R._renderNewRootComponent(s,r,_,o)._renderedComponent.getPublicInstance();return i&&i.call(S),S},render:function(e,t,r){return R._renderSubtreeIntoContainer(null,e,t,r)},unmountComponentAtNode:function(e){isValidContainer(e)||n("40");var t=getTopLevelWrapperInContainer(e);if(!t){hasNonRootReactChild(e),1===e.nodeType&&e.hasAttribute(k);return!1}return delete w[t._instance.rootID],v.batchedUpdates(unmountComponentFromNode,t,e,!1),!0},_mountImageIntoNode:function(e,t,r,o,a){if(isValidContainer(t)||n("41"),o){var s=getReactRootElementInContainer(t);if(d.canReuseMarkup(e,s))return void u.precacheNode(r,s);var l=s.getAttribute(d.CHECKSUM_ATTR_NAME);s.removeAttribute(d.CHECKSUM_ATTR_NAME);var c=s.outerHTML;s.setAttribute(d.CHECKSUM_ATTR_NAME,l);var p=e,f=function firstDifferenceIndex(e,t){for(var r=Math.min(e.length,t.length),n=0;n1?n-1:0),a=1;a=i&&(t=console)[e].apply(t,o)}return log.warn=log.bind(null,"warn"),log.error=log.bind(null,"error"),log.info=log.bind(null,"info"),log.debug=log.bind(null,"debug"),{rootInjects:{log:log}}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(){return{fn:{AST:n},components:{JumpToPath:i.default}}};var n=function _interopRequireWildcard(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[r]=e[r]);return t.default=e,t}(r(411)),i=function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}(r(417))},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getLineNumberForPathAsync=t.positionRangeForPathAsync=t.pathForPositionAsync=void 0;var n=_interopRequireDefault(r(151)),i=_interopRequireDefault(r(44));t.getLineNumberForPath=getLineNumberForPath,t.positionRangeForPath=positionRangeForPath,t.pathForPosition=pathForPosition;var o=_interopRequireDefault(r(937)),a=_interopRequireDefault(r(21)),s=_interopRequireDefault(r(193));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}var u=(0,r(10).memoize)(o.default.compose),l="tag:yaml.org,2002:map",c="tag:yaml.org,2002:seq";function getLineNumberForPath(e,t){if("string"!=typeof e)throw new TypeError("yaml should be a string");if(!(0,a.default)(t))throw new TypeError("path should be an array of strings");var r=0;return function find(e,t,n){if(!e)return n&&n.start_mark?n.start_mark.line:0;if(t.length&&e.tag===l)for(r=0;r=t.column:t.line===e.start_mark.line?t.column>=e.start_mark.column:t.line===e.end_mark.line?t.column<=e.end_mark.column:e.start_mark.linet.line}}(r)}t.pathForPositionAsync=promisifySyncFn(pathForPosition),t.positionRangeForPathAsync=promisifySyncFn(positionRangeForPath),t.getLineNumberForPathAsync=promisifySyncFn(getLineNumberForPath);function promisifySyncFn(e){return function(){for(var t=arguments.length,r=Array(t),i=0;i=0)throw new t.ConstructorError(null,null,"found unconstructable recursive node",e.start_mark);if(this.constructing_nodes.push(e.unique_id),r=null,s=null,e.tag in this.yaml_constructors)r=this.yaml_constructors[e.tag];else{for(a in this.yaml_multi_constructors)if(e.tag.indexOf(0===a)){s=e.tag.slice(a.length),r=this.yaml_multi_constructors[a];break}null==r&&(null in this.yaml_multi_constructors?(s=e.tag,r=this.yaml_multi_constructors.null):null in this.yaml_constructors?r=this.yaml_constructors.null:e instanceof i.ScalarNode?r=this.construct_scalar:e instanceof i.SequenceNode?r=this.construct_sequence:e instanceof i.MappingNode&&(r=this.construct_mapping))}return n=r.call(this,null!=s?s:e,e),this.constructed_objects[e.unique_id]=n,this.constructing_nodes.pop(),n},BaseConstructor.prototype.construct_scalar=function(e){if(!(e instanceof i.ScalarNode))throw new t.ConstructorError(null,null,"expected a scalar node but found "+e.id,e.start_mark);return e.value},BaseConstructor.prototype.construct_sequence=function(e){var r,n,o,a,s;if(!(e instanceof i.SequenceNode))throw new t.ConstructorError(null,null,"expected a sequence node but found "+e.id,e.start_mark);for(s=[],n=0,o=(a=e.value).length;n=0&&(c=c.slice(1)),"0"===c)return 0;if(0===c.indexOf("0b"))return l*parseInt(c.slice(2),2);if(0===c.indexOf("0x"))return l*parseInt(c.slice(2),16);if(0===c.indexOf("0o"))return l*parseInt(c.slice(2),8);if("0"===c[0])return l*parseInt(c,8);if(u.call(c,":")>=0){for((n=function(){var e,t,r,n;for(n=[],e=0,t=(r=c.split(/:/g)).length;e=0&&(c=c.slice(1)),".inf"===c)return Infinity*l;if(".nan"===c)return NaN;if(u.call(c,":")>=0){for((n=function(){var e,t,r,n;for(n=[],e=0,t=(r=c.split(/:/g)).length;e=0||"\r"===t&&"\n"!==this.string[this.index]?(this.line++,this.column=0):this.column++,r.push(e--);return r},Reader.prototype.get_mark=function(){return new e(this.line,this.column,this.string,this.index)},Reader.prototype.check_printable=function(){var e,n,i;if(n=r.exec(this.string))throw e=n[0],i=this.string.length-this.index+n.index,new t.ReaderError(i,e,"special characters are not allowed")},Reader}()}).call(this)},function(e,t,r){(function(){var e,n,i,o,a={}.hasOwnProperty,s=[].slice,u=[].indexOf||function(e){for(var t=0,r=this.length;t"===e&&0===this.flow_level)return this.fetch_folded();if("'"===e)return this.fetch_single();if('"'===e)return this.fetch_double();if(this.check_plain())return this.fetch_plain();throw new t.ScannerError("while scanning for the next token",null,"found character "+e+" that cannot start any token",this.get_mark())},Scanner.prototype.next_possible_simple_key=function(){var e,t,r,n;for(t in r=null,n=this.possible_simple_keys)a.call(n,t)&&(e=n[t],(null===r||e.token_numbere;)t=this.get_mark(),this.indent=this.indents.pop(),r.push(this.tokens.push(new i.BlockEndToken(t,t)));return r}},Scanner.prototype.add_indent=function(e){return e>this.indent&&(this.indents.push(this.indent),this.indent=e,!0)},Scanner.prototype.fetch_stream_start=function(){var e;return e=this.get_mark(),this.tokens.push(new i.StreamStartToken(e,e,this.encoding))},Scanner.prototype.fetch_stream_end=function(){var e;return this.unwind_indent(-1),this.remove_possible_simple_key(),this.allow_possible_simple_key=!1,this.possible_simple_keys={},e=this.get_mark(),this.tokens.push(new i.StreamEndToken(e,e)),this.done=!0},Scanner.prototype.fetch_directive=function(){return this.unwind_indent(-1),this.remove_possible_simple_key(),this.allow_simple_key=!1,this.tokens.push(this.scan_directive())},Scanner.prototype.fetch_document_start=function(){return this.fetch_document_indicator(i.DocumentStartToken)},Scanner.prototype.fetch_document_end=function(){return this.fetch_document_indicator(i.DocumentEndToken)},Scanner.prototype.fetch_document_indicator=function(e){var t;return this.unwind_indent(-1),this.remove_possible_simple_key(),this.allow_simple_key=!1,t=this.get_mark(),this.forward(3),this.tokens.push(new e(t,this.get_mark()))},Scanner.prototype.fetch_flow_sequence_start=function(){return this.fetch_flow_collection_start(i.FlowSequenceStartToken)},Scanner.prototype.fetch_flow_mapping_start=function(){return this.fetch_flow_collection_start(i.FlowMappingStartToken)},Scanner.prototype.fetch_flow_collection_start=function(e){var t;return this.save_possible_simple_key(),this.flow_level++,this.allow_simple_key=!0,t=this.get_mark(),this.forward(),this.tokens.push(new e(t,this.get_mark()))},Scanner.prototype.fetch_flow_sequence_end=function(){return this.fetch_flow_collection_end(i.FlowSequenceEndToken)},Scanner.prototype.fetch_flow_mapping_end=function(){return this.fetch_flow_collection_end(i.FlowMappingEndToken)},Scanner.prototype.fetch_flow_collection_end=function(e){var t;return this.remove_possible_simple_key(),this.flow_level--,this.allow_simple_key=!1,t=this.get_mark(),this.forward(),this.tokens.push(new e(t,this.get_mark()))},Scanner.prototype.fetch_flow_entry=function(){var e;return this.allow_simple_key=!0,this.remove_possible_simple_key(),e=this.get_mark(),this.forward(),this.tokens.push(new i.FlowEntryToken(e,this.get_mark()))},Scanner.prototype.fetch_block_entry=function(){var e,r;if(0===this.flow_level){if(!this.allow_simple_key)throw new t.ScannerError(null,null,"sequence entries are not allowed here",this.get_mark());this.add_indent(this.column)&&(e=this.get_mark(),this.tokens.push(new i.BlockSequenceStartToken(e,e)))}return this.allow_simple_key=!0,this.remove_possible_simple_key(),r=this.get_mark(),this.forward(),this.tokens.push(new i.BlockEntryToken(r,this.get_mark()))},Scanner.prototype.fetch_key=function(){var e,r;if(0===this.flow_level){if(!this.allow_simple_key)throw new t.ScannerError(null,null,"mapping keys are not allowed here",this.get_mark());this.add_indent(this.column)&&(e=this.get_mark(),this.tokens.push(new i.BlockMappingStartToken(e,e)))}return this.allow_simple_key=!this.flow_level,this.remove_possible_simple_key(),r=this.get_mark(),this.forward(),this.tokens.push(new i.KeyToken(r,this.get_mark()))},Scanner.prototype.fetch_value=function(){var e,r,n;if(e=this.possible_simple_keys[this.flow_level])delete this.possible_simple_keys[this.flow_level],this.tokens.splice(e.token_number-this.tokens_taken,0,new i.KeyToken(e.mark,e.mark)),0===this.flow_level&&this.add_indent(e.column)&&this.tokens.splice(e.token_number-this.tokens_taken,0,new i.BlockMappingStartToken(e.mark,e.mark)),this.allow_simple_key=!1;else{if(0===this.flow_level){if(!this.allow_simple_key)throw new t.ScannerError(null,null,"mapping values are not allowed here",this.get_mark());this.add_indent(this.column)&&(r=this.get_mark(),this.tokens.push(new i.BlockMappingStartToken(r,r)))}this.allow_simple_key=!this.flow_level,this.remove_possible_simple_key()}return n=this.get_mark(),this.forward(),this.tokens.push(new i.ValueToken(n,this.get_mark()))},Scanner.prototype.fetch_alias=function(){return this.save_possible_simple_key(),this.allow_simple_key=!1,this.tokens.push(this.scan_anchor(i.AliasToken))},Scanner.prototype.fetch_anchor=function(){return this.save_possible_simple_key(),this.allow_simple_key=!1,this.tokens.push(this.scan_anchor(i.AnchorToken))},Scanner.prototype.fetch_tag=function(){return this.save_possible_simple_key(),this.allow_simple_key=!1,this.tokens.push(this.scan_tag())},Scanner.prototype.fetch_literal=function(){return this.fetch_block_scalar("|")},Scanner.prototype.fetch_folded=function(){return this.fetch_block_scalar(">")},Scanner.prototype.fetch_block_scalar=function(e){return this.allow_simple_key=!0,this.remove_possible_simple_key(),this.tokens.push(this.scan_block_scalar(e))},Scanner.prototype.fetch_single=function(){return this.fetch_flow_scalar("'")},Scanner.prototype.fetch_double=function(){return this.fetch_flow_scalar('"')},Scanner.prototype.fetch_flow_scalar=function(e){return this.save_possible_simple_key(),this.allow_simple_key=!1,this.tokens.push(this.scan_flow_scalar(e))},Scanner.prototype.fetch_plain=function(){return this.save_possible_simple_key(),this.allow_simple_key=!1,this.tokens.push(this.scan_plain())},Scanner.prototype.check_directive=function(){return 0===this.column},Scanner.prototype.check_document_start=function(){var t;return 0===this.column&&"---"===this.prefix(3)&&(t=this.peek(3),u.call(e+r+"\0",t)>=0)},Scanner.prototype.check_document_end=function(){var t;return 0===this.column&&"..."===this.prefix(3)&&(t=this.peek(3),u.call(e+r+"\0",t)>=0)},Scanner.prototype.check_block_entry=function(){var t;return t=this.peek(1),u.call(e+r+"\0",t)>=0},Scanner.prototype.check_key=function(){var t;return 0!==this.flow_level||(t=this.peek(1),u.call(e+r+"\0",t)>=0)},Scanner.prototype.check_value=function(){var t;return 0!==this.flow_level||(t=this.peek(1),u.call(e+r+"\0",t)>=0)},Scanner.prototype.check_plain=function(){var t,n;return t=this.peek(),u.call(e+r+"\0-?:,[]{}#&*!|>'\"%@`",t)<0||(n=this.peek(1),u.call(e+r+"\0",n)<0&&("-"===t||0===this.flow_level&&u.call("?:",t)>=0))},Scanner.prototype.scan_to_next_token=function(){var t,r,n;for(0===this.index&&"\ufeff"===this.peek()&&this.forward(),t=!1,n=[];!t;){for(;" "===this.peek();)this.forward();if("#"===this.peek())for(;r=this.peek(),u.call(e+"\0",r)<0;)this.forward();this.scan_line_break()?0===this.flow_level?n.push(this.allow_simple_key=!0):n.push(void 0):n.push(t=!0)}return n},Scanner.prototype.scan_directive=function(){var t,r,n,o,a;if(o=this.get_mark(),this.forward(),a=null,"YAML"===(r=this.scan_directive_name(o)))a=this.scan_yaml_directive_value(o),t=this.get_mark();else if("TAG"===r)a=this.scan_tag_directive_value(o),t=this.get_mark();else for(t=this.get_mark();n=this.peek(),u.call(e+"\0",n)<0;)this.forward();return this.scan_directive_ignored_line(o),new i.DirectiveToken(r,a,o,t)},Scanner.prototype.scan_directive_name=function(r){var n,i,o;for(i=0,n=this.peek(i);"0"<=n&&n<="9"||"A"<=n&&n<="Z"||"a"<=n&&n<="z"||u.call("-_",n)>=0;)i++,n=this.peek(i);if(0===i)throw new t.ScannerError("while scanning a directive",r,"expected alphanumeric or numeric character but found "+n,this.get_mark());if(o=this.prefix(i),this.forward(i),n=this.peek(),u.call(e+"\0 ",n)<0)throw new t.ScannerError("while scanning a directive",r,"expected alphanumeric or numeric character but found "+n,this.get_mark());return o},Scanner.prototype.scan_yaml_directive_value=function(r){for(var n,i,o;" "===this.peek();)this.forward();if(n=this.scan_yaml_directive_number(r),"."!==this.peek())throw new t.ScannerError("while scanning a directive",r,"expected a digit or '.' but found "+this.peek(),this.get_mark());if(this.forward(),i=this.scan_yaml_directive_number(r),o=this.peek(),u.call(e+"\0 ",o)<0)throw new t.ScannerError("while scanning a directive",r,"expected a digit or ' ' but found "+this.peek(),this.get_mark());return[n,i]},Scanner.prototype.scan_yaml_directive_number=function(e){var r,n,i,o;if(!("0"<=(r=this.peek())&&r<="9"))throw new t.ScannerError("while scanning a directive",e,"expected a digit but found "+r,this.get_mark());for(n=0;"0"<=(i=this.peek(n))&&i<="9";)n++;return o=parseInt(this.prefix(n)),this.forward(n),o},Scanner.prototype.scan_tag_directive_value=function(e){for(var t;" "===this.peek();)this.forward();for(t=this.scan_tag_directive_handle(e);" "===this.peek();)this.forward();return[t,this.scan_tag_directive_prefix(e)]},Scanner.prototype.scan_tag_directive_handle=function(e){var r,n;if(n=this.scan_tag_handle("directive",e)," "!==(r=this.peek()))throw new t.ScannerError("while scanning a directive",e,"expected ' ' but found "+r,this.get_mark());return n},Scanner.prototype.scan_tag_directive_prefix=function(r){var n,i;if(i=this.scan_tag_uri("directive",r),n=this.peek(),u.call(e+"\0 ",n)<0)throw new t.ScannerError("while scanning a directive",r,"expected ' ' but found "+n,this.get_mark());return i},Scanner.prototype.scan_directive_ignored_line=function(r){for(var n,i;" "===this.peek();)this.forward();if("#"===this.peek())for(;i=this.peek(),u.call(e+"\0",i)<0;)this.forward();if(n=this.peek(),u.call(e+"\0",n)<0)throw new t.ScannerError("while scanning a directive",r,"expected a comment or a line break but found "+n,this.get_mark());return this.scan_line_break()},Scanner.prototype.scan_anchor=function(n){var i,o,a,s,l;for(s=this.get_mark(),a="*"===this.peek()?"alias":"anchor",this.forward(),o=0,i=this.peek(o);"0"<=i&&i<="9"||"A"<=i&&i<="Z"||"a"<=i&&i<="z"||u.call("-_",i)>=0;)o++,i=this.peek(o);if(0===o)throw new t.ScannerError("while scanning an "+a,s,"expected alphabetic or numeric character but found '"+i+"'",this.get_mark());if(l=this.prefix(o),this.forward(o),i=this.peek(),u.call(e+r+"\0?:,]}%@`",i)<0)throw new t.ScannerError("while scanning an "+a,s,"expected alphabetic or numeric character but found '"+i+"'",this.get_mark());return new n(l,s,this.get_mark())},Scanner.prototype.scan_tag=function(){var n,o,a,s,l,c;if(s=this.get_mark(),"<"===(n=this.peek(1))){if(o=null,this.forward(2),l=this.scan_tag_uri("tag",s),">"!==this.peek())throw new t.ScannerError("while parsing a tag",s,"expected '>' but found "+this.peek(),this.get_mark());this.forward()}else if(u.call(e+r+"\0",n)>=0)o=null,l="!",this.forward();else{for(a=1,c=!1;u.call(e+"\0 ",n)<0;){if("!"===n){c=!0;break}a++,n=this.peek(a)}c?o=this.scan_tag_handle("tag",s):(o="!",this.forward()),l=this.scan_tag_uri("tag",s)}if(n=this.peek(),u.call(e+"\0 ",n)<0)throw new t.ScannerError("while scanning a tag",s,"expected ' ' but found "+n,this.get_mark());return new i.TagToken([o,l],s,this.get_mark())},Scanner.prototype.scan_block_scalar=function(t){var r,n,a,s,l,c,p,f,d,h,m,v,g,y,_,b,S,k,x,E;for(l=">"===t,a=[],E=this.get_mark(),this.forward(),n=(g=this.scan_block_scalar_indicators(E))[0],c=g[1],this.scan_block_scalar_ignored_line(E),(v=this.indent+1)<1&&(v=1),null==c?(r=(y=this.scan_block_scalar_indentation())[0],m=y[1],s=y[2],p=Math.max(v,m)):(p=v+c-1,r=(_=this.scan_block_scalar_breaks(p))[0],s=_[1]),h="";this.column===p&&"\0"!==this.peek();){for(a=a.concat(r),b=this.peek(),f=u.call(" \t",b)<0,d=0;S=this.peek(d),u.call(e+"\0",S)<0;)d++;if(a.push(this.prefix(d)),this.forward(d),h=this.scan_line_break(),r=(k=this.scan_block_scalar_breaks(p))[0],s=k[1],this.column!==p||"\0"===this.peek())break;l&&"\n"===h&&f&&(x=this.peek(),u.call(" \t",x)<0)?o.is_empty(r)&&a.push(" "):a.push(h)}return!1!==n&&a.push(h),!0===n&&(a=a.concat(r)),new i.ScalarToken(a.join(""),!1,E,s,t)},Scanner.prototype.scan_block_scalar_indicators=function(r){var n,i,o;if(i=null,o=null,n=this.peek(),u.call("+-",n)>=0){if(i="+"===n,this.forward(),n=this.peek(),u.call("0123456789",n)>=0){if(0===(o=parseInt(n)))throw new t.ScannerError("while scanning a block scalar",r,"expected indentation indicator in the range 1-9 but found 0",this.get_mark());this.forward()}}else if(u.call("0123456789",n)>=0){if(0===(o=parseInt(n)))throw new t.ScannerError("while scanning a block scalar",r,"expected indentation indicator in the range 1-9 but found 0",this.get_mark());this.forward(),n=this.peek(),u.call("+-",n)>=0&&(i="+"===n,this.forward())}if(n=this.peek(),u.call(e+"\0 ",n)<0)throw new t.ScannerError("while scanning a block scalar",r,"expected chomping or indentation indicators, but found "+n,this.get_mark());return[i,o]},Scanner.prototype.scan_block_scalar_ignored_line=function(r){for(var n,i;" "===this.peek();)this.forward();if("#"===this.peek())for(;i=this.peek(),u.call(e+"\0",i)<0;)this.forward();if(n=this.peek(),u.call(e+"\0",n)<0)throw new t.ScannerError("while scanning a block scalar",r,"expected a comment or a line break but found "+n,this.get_mark());return this.scan_line_break()},Scanner.prototype.scan_block_scalar_indentation=function(){var t,r,n,i;for(t=[],n=0,r=this.get_mark();i=this.peek(),u.call(e+" ",i)>=0;)" "!==this.peek()?(t.push(this.scan_line_break()),r=this.get_mark()):(this.forward(),this.column>n&&(n=this.column));return[t,n,r]},Scanner.prototype.scan_block_scalar_breaks=function(t){var r,n,i;for(r=[],n=this.get_mark();this.column=0;)for(r.push(this.scan_line_break()),n=this.get_mark();this.column=0)a.push(o),this.forward();else{if(!n||"\\"!==o)return a;if(this.forward(),(o=this.peek())in c)a.push(c[o]),this.forward();else if(o in l){for(d=l[o],this.forward(),f=p=0,m=d;0<=m?pm;f=0<=m?++p:--p)if(v=this.peek(f),u.call("0123456789ABCDEFabcdef",v)<0)throw new t.ScannerError("while scanning a double-quoted scalar",i,"expected escape sequence of "+d+" hexadecimal numbers, but found "+this.peek(f),this.get_mark());s=parseInt(this.prefix(d),16),a.push(String.fromCharCode(s)),this.forward(d)}else{if(!(u.call(e,o)>=0))throw new t.ScannerError("while scanning a double-quoted scalar",i,"found unknown escape character "+o,this.get_mark());this.scan_line_break(),a=a.concat(this.scan_flow_scalar_breaks(n,i))}}else a.push("'"),this.forward(2)}},Scanner.prototype.scan_flow_scalar_spaces=function(n,i){var o,a,s,l,c,p,f;for(s=[],l=0;p=this.peek(l),u.call(r,p)>=0;)l++;if(f=this.prefix(l),this.forward(l),"\0"===(a=this.peek()))throw new t.ScannerError("while scanning a quoted scalar",i,"found unexpected end of stream",this.get_mark());return u.call(e,a)>=0?(c=this.scan_line_break(),o=this.scan_flow_scalar_breaks(n,i),"\n"!==c?s.push(c):0===o.length&&s.push(" "),s=s.concat(o)):s.push(f),s},Scanner.prototype.scan_flow_scalar_breaks=function(n,i){var o,a,s,l,c;for(o=[];;){if("---"===(a=this.prefix(3))||"..."===a&&(s=this.peek(3),u.call(e+r+"\0",s)>=0))throw new t.ScannerError("while scanning a quoted scalar",i,"found unexpected document separator",this.get_mark());for(;l=this.peek(),u.call(r,l)>=0;)this.forward();if(c=this.peek(),!(u.call(e,c)>=0))return o;o.push(this.scan_line_break())}},Scanner.prototype.scan_plain=function(){var n,o,a,s,l,c,p,f,d;for(o=[],d=a=this.get_mark(),s=this.indent+1,f=[];l=0,"#"!==this.peek();){for(;n=this.peek(l),!(u.call(e+r+"\0",n)>=0||0===this.flow_level&&":"===n&&(c=this.peek(l+1),u.call(e+r+"\0",c)>=0)||0!==this.flow_level&&u.call(",:?[]{}",n)>=0);)l++;if(0!==this.flow_level&&":"===n&&(p=this.peek(l+1),u.call(e+r+"\0,[]{}",p)<0))throw this.forward(l),new t.ScannerError("while scanning a plain scalar",d,"found unexpected ':'",this.get_mark(),"Please check http://pyyaml.org/wiki/YAMLColonInFlowContext");if(0===l)break;if(this.allow_simple_key=!1,(o=o.concat(f)).push(this.prefix(l)),this.forward(l),a=this.get_mark(),null==(f=this.scan_plain_spaces(s,d))||0===f.length||"#"===this.peek()||0===this.flow_level&&this.column=0;)s++;if(m=this.prefix(s),this.forward(s),o=this.peek(),u.call(e,o)>=0){if(l=this.scan_line_break(),this.allow_simple_key=!0,"---"===(c=this.prefix(3))||"..."===c&&(f=this.peek(3),u.call(e+r+"\0",f)>=0))return;for(i=[];h=this.peek(),u.call(e+" ",h)>=0;)if(" "===this.peek())this.forward();else if(i.push(this.scan_line_break()),"---"===(c=this.prefix(3))||"..."===c&&(d=this.peek(3),u.call(e+r+"\0",d)>=0))return;"\n"!==l?a.push(l):0===i.length&&a.push(" "),a=a.concat(i)}else m&&a.push(m);return a},Scanner.prototype.scan_tag_handle=function(e,r){var n,i,o;if("!"!==(n=this.peek()))throw new t.ScannerError("while scanning a "+e,r,"expected '!' but found "+n,this.get_mark());if(i=1," "!==(n=this.peek(i))){for(;"0"<=n&&n<="9"||"A"<=n&&n<="Z"||"a"<=n&&n<="z"||u.call("-_",n)>=0;)i++,n=this.peek(i);if("!"!==n)throw this.forward(i),new t.ScannerError("while scanning a "+e,r,"expected '!' but found "+n,this.get_mark());i++}return o=this.prefix(i),this.forward(i),o},Scanner.prototype.scan_tag_uri=function(e,r){var n,i,o;for(i=[],o=0,n=this.peek(o);"0"<=n&&n<="9"||"A"<=n&&n<="Z"||"a"<=n&&n<="z"||u.call("-;/?:@&=+$,_.!~*'()[]%",n)>=0;)"%"===n?(i.push(this.prefix(o)),this.forward(o),o=0,i.push(this.scan_uri_escapes(e,r))):o++,n=this.peek(o);if(0!==o&&(i.push(this.prefix(o)),this.forward(o),o=0),0===i.length)throw new t.ScannerError("while parsing a "+e,r,"expected URI but found "+n,this.get_mark());return i.join("")},Scanner.prototype.scan_uri_escapes=function(e,r){var n,i,o;for(n=[],this.get_mark();"%"===this.peek();){for(this.forward(),o=i=0;i<=2;o=++i)throw new t.ScannerError("while scanning a "+e,r,"expected URI escape sequence of 2 hexadecimal numbers but found "+this.peek(o),this.get_mark());n.push(String.fromCharCode(parseInt(this.prefix(2),16))),this.forward(2)}return n.join("")},Scanner.prototype.scan_line_break=function(){var e;return e=this.peek(),u.call("\r\nÂ…",e)>=0?("\r\n"===this.prefix(2)?this.forward(2):this.forward(),"\n"):u.call("\u2028\u2029",e)>=0?(this.forward(),e):""},Scanner}()}).call(this)},function(e,t,r){(function(){var e,n,i,o={}.hasOwnProperty,a=[].slice;n=r(119),e=r(46).MarkedYAMLError,i=r(242),this.ParserError=function(t){function ParserError(){return ParserError.__super__.constructor.apply(this,arguments)}return function(e,t){for(var r in t)o.call(t,r)&&(e[r]=t[r]);function ctor(){this.constructor=e}ctor.prototype=t.prototype,e.prototype=new ctor,e.__super__=t.prototype}(ParserError,e),ParserError}(),this.Parser=function(){var e;function Parser(){this.current_event=null,this.yaml_version=null,this.tag_handles={},this.states=[],this.marks=[],this.state="parse_stream_start"}return e={"!":"!","!!":"tag:yaml.org,2002:"},Parser.prototype.dispose=function(){return this.states=[],this.state=null},Parser.prototype.check_event=function(){var e,t,r,n;if(t=1<=arguments.length?a.call(arguments,0):[],null===this.current_event&&null!=this.state&&(this.current_event=this[this.state]()),null!==this.current_event){if(0===t.length)return!0;for(r=0,n=t.length;r', but found "+this.peek_token().id,this.peek_token().start_mark);e=(u=this.get_token()).end_mark,r=new n.DocumentStartEvent(a,e,!0,l,s),this.states.push("parse_document_end"),this.state="parse_document_content"}return r},Parser.prototype.parse_document_end=function(){var e,t,r,o;return o=e=this.peek_token().start_mark,r=!1,this.check_token(i.DocumentEndToken)&&(e=this.get_token().end_mark,r=!0),t=new n.DocumentEndEvent(o,e,r),this.state="parse_document_start",t},Parser.prototype.parse_document_content=function(){var e;return this.check_token(i.DirectiveToken,i.DocumentStartToken,i.DocumentEndToken,i.StreamEndToken)?(e=this.process_empty_scalar(this.peek_token().start_mark),this.state=this.states.pop(),e):this.parse_block_node()},Parser.prototype.process_directives=function(){var r,n,a,s,u,l,c,p,f;for(this.yaml_version=null,this.tag_handles={};this.check_token(i.DirectiveToken);)if("YAML"===(p=this.get_token()).name){if(null!==this.yaml_version)throw new t.ParserError(null,null,"found duplicate YAML directive",p.start_mark);if(n=(s=p.value)[0],s[1],1!==n)throw new t.ParserError(null,null,"found incompatible YAML document (version 1.* is required)",p.start_mark);this.yaml_version=p.value}else if("TAG"===p.name){if(r=(u=p.value)[0],a=u[1],r in this.tag_handles)throw new t.ParserError(null,null,"duplicate tag handle "+r,p.start_mark);this.tag_handles[r]=a}for(r in c=null,l=this.tag_handles)o.call(l,r)&&(a=l[r],null==c&&(c={}),c[r]=a);for(r in f=[this.yaml_version,c],e)o.call(e,r)&&((a=e[r])in this.tag_handles||(this.tag_handles[r]=a));return f},Parser.prototype.parse_block_node=function(){return this.parse_node(!0)},Parser.prototype.parse_flow_node=function(){return this.parse_node()},Parser.prototype.parse_block_node_or_indentless_sequence=function(){return this.parse_node(!0,!0)},Parser.prototype.parse_node=function(e,r){var o,a,s,u,l,c,p,f,d,h,m;if(null==e&&(e=!1),null==r&&(r=!1),this.check_token(i.AliasToken))m=this.get_token(),s=new n.AliasEvent(m.value,m.start_mark,m.end_mark),this.state=this.states.pop();else{if(o=null,d=null,p=a=h=null,this.check_token(i.AnchorToken)?(p=(m=this.get_token()).start_mark,a=m.end_mark,o=m.value,this.check_token(i.TagToken)&&(h=(m=this.get_token()).start_mark,a=m.end_mark,d=m.value)):this.check_token(i.TagToken)&&(p=h=(m=this.get_token()).start_mark,a=m.end_mark,d=m.value,this.check_token(i.AnchorToken)&&(a=(m=this.get_token()).end_mark,o=m.value)),null!==d)if(u=d[0],f=d[1],null!==u){if(!(u in this.tag_handles))throw new t.ParserError("while parsing a node",p,"found undefined tag handle "+u,h);d=this.tag_handles[u]+f}else d=f;if(null===p&&(p=a=this.peek_token().start_mark),s=null,l=null===d||"!"===d,r&&this.check_token(i.BlockEntryToken))a=this.peek_token().end_mark,s=new n.SequenceStartEvent(o,d,l,p,a),this.state="parse_indentless_sequence_entry";else if(this.check_token(i.ScalarToken))a=(m=this.get_token()).end_mark,l=m.plain&&null===d||"!"===d?[!0,!1]:null===d?[!1,!0]:[!1,!1],s=new n.ScalarEvent(o,d,l,m.value,p,a,m.style),this.state=this.states.pop();else if(this.check_token(i.FlowSequenceStartToken))a=this.peek_token().end_mark,s=new n.SequenceStartEvent(o,d,l,p,a,!0),this.state="parse_flow_sequence_first_entry";else if(this.check_token(i.FlowMappingStartToken))a=this.peek_token().end_mark,s=new n.MappingStartEvent(o,d,l,p,a,!0),this.state="parse_flow_mapping_first_key";else if(e&&this.check_token(i.BlockSequenceStartToken))a=this.peek_token().end_mark,s=new n.SequenceStartEvent(o,d,l,p,a,!1),this.state="parse_block_sequence_first_entry";else if(e&&this.check_token(i.BlockMappingStartToken))a=this.peek_token().end_mark,s=new n.MappingStartEvent(o,d,l,p,a,!1),this.state="parse_block_mapping_first_key";else{if(null===o&&null===d)throw c=e?"block":"flow",m=this.peek_token(),new t.ParserError("while parsing a "+c+" node",p,"expected the node content, but found "+m.id,m.start_mark);s=new n.ScalarEvent(o,d,[l,!1],"",p,a),this.state=this.states.pop()}}return s},Parser.prototype.parse_block_sequence_first_entry=function(){var e;return e=this.get_token(),this.marks.push(e.start_mark),this.parse_block_sequence_entry()},Parser.prototype.parse_block_sequence_entry=function(){var e,r;if(this.check_token(i.BlockEntryToken))return r=this.get_token(),this.check_token(i.BlockEntryToken,i.BlockEndToken)?(this.state="parse_block_sequence_entry",this.process_empty_scalar(r.end_mark)):(this.states.push("parse_block_sequence_entry"),this.parse_block_node());if(!this.check_token(i.BlockEndToken))throw r=this.peek_token(),new t.ParserError("while parsing a block collection",this.marks.slice(-1)[0],"expected , but found "+r.id,r.start_mark);return r=this.get_token(),e=new n.SequenceEndEvent(r.start_mark,r.end_mark),this.state=this.states.pop(),this.marks.pop(),e},Parser.prototype.parse_indentless_sequence_entry=function(){var e,t;return this.check_token(i.BlockEntryToken)?(t=this.get_token(),this.check_token(i.BlockEntryToken,i.KeyToken,i.ValueToken,i.BlockEndToken)?(this.state="parse_indentless_sequence_entry",this.process_empty_scalar(t.end_mark)):(this.states.push("parse_indentless_sequence_entry"),this.parse_block_node())):(t=this.peek_token(),e=new n.SequenceEndEvent(t.start_mark,t.start_mark),this.state=this.states.pop(),e)},Parser.prototype.parse_block_mapping_first_key=function(){var e;return e=this.get_token(),this.marks.push(e.start_mark),this.parse_block_mapping_key()},Parser.prototype.parse_block_mapping_key=function(){var e,r;if(this.check_token(i.KeyToken))return r=this.get_token(),this.check_token(i.KeyToken,i.ValueToken,i.BlockEndToken)?(this.state="parse_block_mapping_value",this.process_empty_scalar(r.end_mark)):(this.states.push("parse_block_mapping_value"),this.parse_block_node_or_indentless_sequence());if(!this.check_token(i.BlockEndToken))throw r=this.peek_token(),new t.ParserError("while parsing a block mapping",this.marks.slice(-1)[0],"expected , but found "+r.id,r.start_mark);return r=this.get_token(),e=new n.MappingEndEvent(r.start_mark,r.end_mark),this.state=this.states.pop(),this.marks.pop(),e},Parser.prototype.parse_block_mapping_value=function(){var e;return this.check_token(i.ValueToken)?(e=this.get_token(),this.check_token(i.KeyToken,i.ValueToken,i.BlockEndToken)?(this.state="parse_block_mapping_key",this.process_empty_scalar(e.end_mark)):(this.states.push("parse_block_mapping_key"),this.parse_block_node_or_indentless_sequence())):(this.state="parse_block_mapping_key",e=this.peek_token(),this.process_empty_scalar(e.start_mark))},Parser.prototype.parse_flow_sequence_first_entry=function(){var e;return e=this.get_token(),this.marks.push(e.start_mark),this.parse_flow_sequence_entry(!0)},Parser.prototype.parse_flow_sequence_entry=function(e){var r,o;if(null==e&&(e=!1),!this.check_token(i.FlowSequenceEndToken)){if(!e){if(!this.check_token(i.FlowEntryToken))throw o=this.peek_token(),new t.ParserError("while parsing a flow sequence",this.marks.slice(-1)[0],"expected ',' or ']', but got "+o.id,o.start_mark);this.get_token()}if(this.check_token(i.KeyToken))return o=this.peek_token(),r=new n.MappingStartEvent(null,null,!0,o.start_mark,o.end_mark,!0),this.state="parse_flow_sequence_entry_mapping_key",r;if(!this.check_token(i.FlowSequenceEndToken))return this.states.push("parse_flow_sequence_entry"),this.parse_flow_node()}return o=this.get_token(),r=new n.SequenceEndEvent(o.start_mark,o.end_mark),this.state=this.states.pop(),this.marks.pop(),r},Parser.prototype.parse_flow_sequence_entry_mapping_key=function(){var e;return e=this.get_token(),this.check_token(i.ValueToken,i.FlowEntryToken,i.FlowSequenceEndToken)?(this.state="parse_flow_sequence_entry_mapping_value",this.process_empty_scalar(e.end_mark)):(this.states.push("parse_flow_sequence_entry_mapping_value"),this.parse_flow_node())},Parser.prototype.parse_flow_sequence_entry_mapping_value=function(){var e;return this.check_token(i.ValueToken)?(e=this.get_token(),this.check_token(i.FlowEntryToken,i.FlowSequenceEndToken)?(this.state="parse_flow_sequence_entry_mapping_end",this.process_empty_scalar(e.end_mark)):(this.states.push("parse_flow_sequence_entry_mapping_end"),this.parse_flow_node())):(this.state="parse_flow_sequence_entry_mapping_end",e=this.peek_token(),this.process_empty_scalar(e.start_mark))},Parser.prototype.parse_flow_sequence_entry_mapping_end=function(){var e;return this.state="parse_flow_sequence_entry",e=this.peek_token(),new n.MappingEndEvent(e.start_mark,e.start_mark)},Parser.prototype.parse_flow_mapping_first_key=function(){var e;return e=this.get_token(),this.marks.push(e.start_mark),this.parse_flow_mapping_key(!0)},Parser.prototype.parse_flow_mapping_key=function(e){var r,o;if(null==e&&(e=!1),!this.check_token(i.FlowMappingEndToken)){if(!e){if(!this.check_token(i.FlowEntryToken))throw o=this.peek_token(),new t.ParserError("while parsing a flow mapping",this.marks.slice(-1)[0],"expected ',' or '}', but got "+o.id,o.start_mark);this.get_token()}if(this.check_token(i.KeyToken))return o=this.get_token(),this.check_token(i.ValueToken,i.FlowEntryToken,i.FlowMappingEndToken)?(this.state="parse_flow_mapping_value",this.process_empty_scalar(o.end_mark)):(this.states.push("parse_flow_mapping_value"),this.parse_flow_node());if(!this.check_token(i.FlowMappingEndToken))return this.states.push("parse_flow_mapping_empty_value"),this.parse_flow_node()}return o=this.get_token(),r=new n.MappingEndEvent(o.start_mark,o.end_mark),this.state=this.states.pop(),this.marks.pop(),r},Parser.prototype.parse_flow_mapping_value=function(){var e;return this.check_token(i.ValueToken)?(e=this.get_token(),this.check_token(i.FlowEntryToken,i.FlowMappingEndToken)?(this.state="parse_flow_mapping_key",this.process_empty_scalar(e.end_mark)):(this.states.push("parse_flow_mapping_key"),this.parse_flow_node())):(this.state="parse_flow_mapping_key",e=this.peek_token(),this.process_empty_scalar(e.start_mark))},Parser.prototype.parse_flow_mapping_empty_value=function(){return this.state="parse_flow_mapping_key",this.process_empty_scalar(this.peek_token().start_mark)},Parser.prototype.process_empty_scalar=function(e){return new n.ScalarEvent(null,null,[!0,!1],"",e,e)},Parser}()}).call(this)},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=_interopRequireDefault(r(4)),i=_interopRequireDefault(r(2)),o=_interopRequireDefault(r(3)),a=_interopRequireDefault(r(5)),s=_interopRequireDefault(r(6));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}var u=function(e){function JumpToPath(){return(0,i.default)(this,JumpToPath),(0,a.default)(this,(JumpToPath.__proto__||(0,n.default)(JumpToPath)).apply(this,arguments))}return(0,s.default)(JumpToPath,e),(0,o.default)(JumpToPath,[{key:"render",value:function render(){return null}}]),JumpToPath}(_interopRequireDefault(r(0)).default.Component);t.default=u},function(e,t,r){"use strict";var n=function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}(r(419));e.exports=function(e){var t=e.configs;return{fn:{fetch:n.default.makeHttp(t.preFetch,t.postFetch),buildRequest:n.default.buildRequest,execute:n.default.execute,resolve:n.default.resolve,resolveSubtree:n.default.resolveSubtree,serializeRes:n.default.serializeRes,opId:n.default.helpers.opId}}}},function(e,t,r){e.exports=function(e){function t(n){if(r[n])return r[n].exports;var i=r[n]={i:n,l:!1,exports:{}};return e[n].call(i.exports,i,i.exports,t),i.l=!0,i.exports}var r={};return t.m=e,t.c=r,t.d=function(e,r,n){t.o(e,r)||Object.defineProperty(e,r,{configurable:!1,enumerable:!0,get:n})},t.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(r,"a",r),r},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=23)}([function(e,t){e.exports=r(43)},function(e,t){e.exports=r(44)},function(e,t){e.exports=r(25)},function(e,t){e.exports=r(26)},function(e,t,r){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function o(e,t){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"",n=(arguments.length>3&&void 0!==arguments[3]?arguments[3]:{}).v2OperationIdCompatibilityMode;return e&&"object"===(void 0===e?"undefined":(0,h.default)(e))?(e.operationId||"").replace(/\s/g,"").length?y(e.operationId):i(t,r,{v2OperationIdCompatibilityMode:n}):null}function i(e,t){if((arguments.length>2&&void 0!==arguments[2]?arguments[2]:{}).v2OperationIdCompatibilityMode){var r=(t.toLowerCase()+"_"+e).replace(/[\s!@#$%^&*()_+=[{\]};:<>|.\/?,\\'""-]/g,"_");return(r=r||e.substring(1)+"_"+t).replace(/((_){2,})/g,"_").replace(/^(_)*/g,"").replace(/([_])*$/g,"")}return""+g(t)+y(e)}function s(e,t){return g(t)+"-"+e}function c(e,t){return f(e,t,!0)||null}function f(e,t,r){if(!e||"object"!==(void 0===e?"undefined":(0,h.default)(e))||!e.paths||"object"!==(0,h.default)(e.paths))return null;var n=e.paths;for(var i in n)for(var o in n[i])if("PARAMETERS"!==o.toUpperCase()){var a=n[i][o];if(a&&"object"===(void 0===a?"undefined":(0,h.default)(a))){var s={spec:e,pathName:i,method:o.toUpperCase(),operation:a},u=t(s);if(r&&u)return s}}}Object.defineProperty(t,"__esModule",{value:!0});var d=n(r(18)),h=n(r(1));t.isOAS3=function a(e){var t=e.openapi;return!!t&&(0,v.default)(t,"3")},t.isSwagger2=function u(e){var t=e.swagger;return!!t&&(0,v.default)(t,"2")},t.opId=o,t.idFromPathMethod=i,t.legacyIdFromPathMethod=s,t.getOperationRaw=function l(e,t){return e&&e.paths?c(e,function(e){var r=e.pathName,n=e.method,i=e.operation;if(!i||"object"!==(void 0===i?"undefined":(0,h.default)(i)))return!1;var a=i.operationId;return[o(i,r,n),s(r,n),a].some(function(e){return e&&e===t})}):null},t.findOperation=c,t.eachOperation=f,t.normalizeSwagger=function p(e){var t=e.spec,r=t.paths,n={};if(!r||t.$$normalized)return e;for(var i in r){var a=r[i];if((0,m.default)(a)){var s=a.parameters;for(var u in a)!function(e){var r=a[e];if(!(0,m.default)(r))return"continue";var u=o(r,i,e);if(u){n[u]?n[u].push(r):n[u]=[r];var l=n[u];if(l.length>1)l.forEach(function(e,t){e.__originalOperationId=e.__originalOperationId||e.operationId,e.operationId=""+u+(t+1)});else if(void 0!==r.operationId){var c=l[0];c.__originalOperationId=c.__originalOperationId||r.operationId,c.operationId=u}}if("parameters"!==e){var p=[],f={};for(var h in t)"produces"!==h&&"consumes"!==h&&"security"!==h||(f[h]=t[h],p.push(f));if(s&&(f.parameters=s,p.push(f)),p.length){var v=!0,g=!1,y=void 0;try{for(var _,b=(0,d.default)(p);!(v=(_=b.next()).done);v=!0){var S=_.value;for(var k in S)if(r[k]){if("parameters"===k){var x=!0,E=!1,C=void 0;try{for(var w,D=(0,d.default)(S[k]);!(x=(w=D.next()).done);x=!0)!function(){var e=w.value;r[k].some(function(t){return t.name&&t.name===e.name||t.$ref&&t.$ref===e.$ref||t.$$ref&&t.$$ref===e.$$ref||t===e})||r[k].push(e)}()}catch(e){E=!0,C=e}finally{try{!x&&D.return&&D.return()}finally{if(E)throw C}}}}else r[k]=S[k]}}catch(e){g=!0,y=e}finally{try{!v&&b.return&&b.return()}finally{if(g)throw y}}}}}(u)}}return t.$$normalized=!0,e};var m=n(r(48)),v=n(r(14)),g=function(e){return String.prototype.toLowerCase.call(e)},y=function(e){return e.replace(/[^\w]/gi,"_")}},function(e,t){e.exports=r(946)},function(t,r,p){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function u(e,t){var r=(arguments.length>2&&void 0!==arguments[2]?arguments[2]:{}).loadSpec,n=void 0!==r&&r,i={ok:e.ok,url:e.url||t,status:e.status,statusText:e.statusText,headers:o(e.headers)},s=i.headers["content-type"],u=n||x(s);return(u?e.text:e.blob||e.buffer).call(e).then(function(e){if(i.text=e,i.data=e,u)try{var t=function a(e,t){return"application/json"===t?JSON.parse(e):b.default.safeLoad(e)}(e,s);i.body=t,i.obj=t}catch(e){i.parseError=e}return i})}function o(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t={};return"function"==typeof e.forEach?(e.forEach(function(e,r){void 0!==t[r]?(t[r]=Array.isArray(t[r])?t[r]:[t[r]],t[r].push(e)):t[r]=e}),t):t}function i(e){return"undefined"!=typeof File?e instanceof File:null!==e&&"object"===(void 0===e?"undefined":(0,g.default)(e))&&"function"==typeof e.pipe}function s(e,t){var r=e.collectionFormat,n=e.allowEmptyValue,o="object"===(void 0===e?"undefined":(0,g.default)(e))?e.value:e;if(void 0===o&&n)return"";if(i(o)||"boolean"==typeof o)return o;var a=encodeURIComponent;return t&&(a=(0,S.default)(o)?function(e){return e}:function(e){return(0,m.default)(e)}),"object"!==(void 0===o?"undefined":(0,g.default)(o))||Array.isArray(o)?Array.isArray(o)?Array.isArray(o)&&!r?o.map(a).join(","):"multi"===r?o.map(a):o.map(a).join({csv:",",ssv:"%20",tsv:"%09",pipes:"|"}[r]):a(o):""}function l(e){var t=(0,h.default)(e).reduce(function(t,r){var n=e[r],i=!!n.skipEncoding,o=i?r:encodeURIComponent(r),a=function(e){return e&&"object"===(void 0===e?"undefined":(0,g.default)(e))}(n)&&!Array.isArray(n);return t[o]=s(a?n:{value:n},i),t},{});return _.default.stringify(t,{encode:!1,indices:!1})||""}function c(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.url,r=void 0===t?"":t,n=e.query,o=e.form;if(o){var a=(0,h.default)(o).some(function(e){return i(o[e].value)}),u=e.headers["content-type"]||e.headers["Content-Type"];if(a||/multipart\/form-data/i.test(u)){var c=p(30);e.body=new c,(0,h.default)(o).forEach(function(t){e.body.append(t,s(o[t],!0))})}else e.body=l(o);delete e.form}if(n){var f=r.split("?"),m=(0,d.default)(f,2),v=m[0],g=m[1],y="";if(g){var b=_.default.parse(g);(0,h.default)(n).forEach(function(e){return delete b[e]}),y=_.default.stringify(b,{encode:!0})}var S=function(){for(var e=arguments.length,t=Array(e),r=0;r1&&void 0!==arguments[1]?arguments[1]:{};return v.default.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if("object"===(void 0===t?"undefined":(0,g.default)(t))&&(t=(a=t).url),a.headers=a.headers||{},k.mergeInQueryOrForm(a),!a.requestInterceptor){e.next=10;break}return e.next=6,a.requestInterceptor(a);case 6:if(e.t0=e.sent,e.t0){e.next=9;break}e.t0=a;case 9:a=e.t0;case 10:return r=a.headers["content-type"]||a.headers["Content-Type"],/multipart\/form-data/i.test(r)&&(delete a.headers["content-type"],delete a.headers["Content-Type"]),n=void 0,e.prev=13,e.next=16,(a.userFetch||fetch)(a.url,a);case 16:return n=e.sent,e.next=19,k.serializeRes(n,t,a);case 19:if(n=e.sent,!a.responseInterceptor){e.next=27;break}return e.next=23,a.responseInterceptor(n);case 23:if(e.t1=e.sent,e.t1){e.next=26;break}e.t1=n;case 26:n=e.t1;case 27:e.next=37;break;case 29:if(e.prev=29,e.t2=e.catch(13),n){e.next=33;break}throw e.t2;case 33:throw(i=new Error(n.statusText)).statusCode=i.status=n.status,i.responseError=e.t2,i;case 37:if(n.ok){e.next=42;break}throw(o=new Error(n.statusText)).statusCode=o.status=n.status,o.response=n,o;case 42:return e.abrupt("return",n);case 43:case"end":return e.stop()}},e,this,[[13,29]])}));return function e(r){return t.apply(this,arguments)}}();var x=r.shouldDownloadAsText=function(){return/(json|xml|yaml|text)\b/.test(arguments.length>0&&void 0!==arguments[0]?arguments[0]:"")}},function(e,t){e.exports=r(37)},function(e,t){e.exports=r(361)},function(e,t,r){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function u(e){return Array.isArray(e)?e.length<1?"":"/"+e.map(function(e){return(e+"").replace(/~/g,"~0").replace(/\//g,"~1")}).join("/"):e}function i(e,t,r){return{op:"replace",path:e,value:t,meta:r}}function h(e,t,r){return k(P(e.filter(j).map(function(e){return t(e.value,r,e.path)})||[]))}function v(e,t,r){return r=r||[],Array.isArray(e)?e.map(function(e,n){return v(e,t,r.concat(n))}):w(e)?(0,R.default)(e).map(function(n){return v(e[n],t,r.concat(n))}):t(e,r[r.length-1],r)}function m(e,t,r){var n=[];if((r=r||[]).length>0){var i=t(e,r[r.length-1],r);i&&(n=n.concat(i))}if(Array.isArray(e)){var o=e.map(function(e,n){return m(e,t,r.concat(n))});o&&(n=n.concat(o))}else if(w(e)){var a=(0,R.default)(e).map(function(n){return m(e[n],t,r.concat(n))});a&&(n=n.concat(a))}return P(n)}function x(e){return Array.isArray(e)?e:[e]}function P(e){var t;return(t=[]).concat.apply(t,(0,D.default)(e.map(function(e){return Array.isArray(e)?P(e):e})))}function k(e){return e.filter(function(e){return void 0!==e})}function w(e){return e&&"object"===(void 0===e?"undefined":(0,S.default)(e))}function q(e){return e&&"function"==typeof e}function M(e){if(I(e)){var t=e.op;return"add"===t||"remove"===t||"replace"===t}return!1}function A(e){return M(e)||I(e)&&"mutation"===e.type}function j(e){return A(e)&&("add"===e.op||"replace"===e.op||"merge"===e.op||"mergeDeep"===e.op)}function I(e){return e&&"object"===(void 0===e?"undefined":(0,S.default)(e))}function E(e,t){try{return B.default.getValueByPointer(e,t)}catch(e){return console.error(e),{}}}Object.defineProperty(t,"__esModule",{value:!0});var S=n(r(1)),D=n(r(34)),R=n(r(0)),T=n(r(35)),F=n(r(2)),B=n(r(36)),N=n(r(37)),L=r(38),z=n(r(39));t.default={add:function o(e,t){return{op:"add",path:e,value:t}},replace:i,remove:function s(e,t){return{op:"remove",path:e}},merge:function l(e,t){return{type:"mutation",op:"merge",path:e,value:t}},mergeDeep:function c(e,t){return{type:"mutation",op:"mergeDeep",path:e,value:t}},context:function f(e,t){return{type:"context",path:e,value:t}},getIn:function g(e,t){return t.reduce(function(e,t){return void 0!==t&&e?e[t]:e},e)},applyPatch:function a(e,t,r){if(r=r||{},"merge"===(t=(0,F.default)({},t,{path:t.path&&u(t.path)})).op){var n=E(e,t.path);(0,F.default)(n,t.value),B.default.applyPatch(e,[i(t.path,n)])}else if("mergeDeep"===t.op){var o=E(e,t.path);for(var a in t.value){var s=t.value[a],l=Array.isArray(s);if(l){var c=o[a]||[];o[a]=c.concat(s)}else if(w(s)&&!l){var p=(0,F.default)({},o[a]);for(var f in s){if(Object.prototype.hasOwnProperty.call(p,f)){p=(0,N.default)((0,z.default)({},p),s);break}(0,F.default)(p,(0,T.default)({},f,s[f]))}o[a]=p}else o[a]=s}}else if("add"===t.op&&""===t.path&&w(t.value)){var d=(0,R.default)(t.value).reduce(function(e,r){return e.push({op:"add",path:"/"+u(r),value:t.value[r]}),e},[]);B.default.applyPatch(e,d)}else if("replace"===t.op&&""===t.path){var h=t.value;r.allowMetaPatches&&t.meta&&j(t)&&(Array.isArray(t.value)||w(t.value))&&(h=(0,F.default)({},h,t.meta)),e=h}else if(B.default.applyPatch(e,[t]),r.allowMetaPatches&&t.meta&&j(t)&&(Array.isArray(t.value)||w(t.value))){var m=E(e,t.path),v=(0,F.default)({},m,t.meta);B.default.applyPatch(e,[i(t.path,v)])}return e},parentPathMatch:function y(e,t){if(!Array.isArray(t))return!1;for(var r=0,n=t.length;r1&&void 0!==arguments[1]?arguments[1]:{},r=t.requestInterceptor,n=t.responseInterceptor,i=e.withCredentials?"include":"same-origin";return function(t){return e({url:t,loadSpec:!0,requestInterceptor:r,responseInterceptor:n,headers:{Accept:"application/json"},credentials:i}).then(function(e){return e.body})}}Object.defineProperty(i,"__esModule",{value:!0});var l=n(s(8)),c=n(s(11));i.makeFetchJSON=a,i.clearCache=function u(){f.plugins.refs.clearCache()},i.default=function o(e){function t(t){var r=this;k&&(f.plugins.refs.docCache[k]=t),f.plugins.refs.fetchJSON=a(S,{requestInterceptor:y,responseInterceptor:_});var n=[f.plugins.refs];return"function"==typeof g&&n.push(f.plugins.parameters),"function"==typeof v&&n.push(f.plugins.properties),"strict"!==o&&n.push(f.plugins.allOf),(0,d.default)({spec:t,context:{baseDoc:k},plugins:n,allowMetaPatches:u,pathDiscriminator:m,parameterMacro:g,modelPropertyMacro:v}).then(b?function(){var e=(0,c.default)(l.default.mark(function e(t){return l.default.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",t);case 1:case"end":return e.stop()}},e,r)}));return function(t){return e.apply(this,arguments)}}():h.normalizeSwagger)}var r=e.fetch,n=e.spec,i=e.url,o=e.mode,s=e.allowMetaPatches,u=void 0===s||s,m=e.pathDiscriminator,v=e.modelPropertyMacro,g=e.parameterMacro,y=e.requestInterceptor,_=e.responseInterceptor,b=e.skipNormalization,S=e.http,k=e.baseDoc;return k=k||i,S=r||S||p.default,n?t(n):a(S,{requestInterceptor:y,responseInterceptor:_})(k).then(t)};var p=n(s(6)),f=s(31),d=n(f),h=s(4)},function(e,t){e.exports=r(151)},function(e,t){e.exports=r(97)},function(e,t){e.exports=r(2)},function(e,t){e.exports=r(3)},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function n(e,t){function r(){Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=(new Error).stack;for(var e=arguments.length,r=Array(e),n=0;n-1||o.indexOf(r)>-1};var i=["properties"],o=["definitions","parameters","responses","securityDefinitions","components/schemas","components/responses","components/parameters","components/securitySchemes"]},function(e,t,r){e.exports=r(24)},function(e,t,r){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=this,r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if("string"==typeof e?r.url=e:r=e,!(this instanceof a))return new a(r);(0,o.default)(this,r);var n=this.resolve().then(function(){return t.disableInterfaces||(0,o.default)(t,a.makeApisTagOperation(t)),t});return n.client=this,n}Object.defineProperty(t,"__esModule",{value:!0});var i=n(r(3)),o=n((n(r(25)),r(5))),s=n(r(14)),u=n(r(10)),l=r(6),c=n(l),p=r(16),f=n(p),d=n(r(49)),h=r(50),m=r(52),v=r(4);a.http=c.default,a.makeHttp=l.makeHttp.bind(null,a.http),a.resolve=f.default,a.resolveSubtree=d.default,a.execute=m.execute,a.serializeRes=l.serializeRes,a.serializeHeaders=l.serializeHeaders,a.clearCache=p.clearCache,a.parameterBuilders=m.PARAMETER_BUILDERS,a.makeApisTagOperation=h.makeApisTagOperation,a.buildRequest=m.buildRequest,a.helpers={opId:v.opId},a.prototype={http:c.default,execute:function(e){return this.applyDefaults(),a.execute((0,i.default)({spec:this.spec,http:this.http,securities:{authorized:this.authorizations},contextUrl:"string"==typeof this.url?this.url:void 0},e))},resolve:function(){var e=this;return a.resolve({spec:this.spec,url:this.url,allowMetaPatches:this.allowMetaPatches,requestInterceptor:this.requestInterceptor||null,responseInterceptor:this.responseInterceptor||null}).then(function(t){return e.originalSpec=e.spec,e.spec=t.spec,e.errors=t.errors,e})}},a.prototype.applyDefaults=function(){var e=this.spec,t=this.url;if(t&&(0,s.default)(t,"http")){var r=u.default.parse(t);e.host||(e.host=r.host),e.schemes||(e.schemes=[r.protocol.replace(":","")]),e.basePath||(e.basePath="/")}},t.default=a,e.exports=t.default},function(e,t){e.exports=r(958)},function(e,t){e.exports=r(19)},function(e,t){e.exports=r(959)},function(e,t){e.exports=r(960)},function(e,t){e.exports=r(365)},function(e,t){e.exports=r(963)},function(t,r,i){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(r,"__esModule",{value:!0}),r.plugins=r.SpecMap=void 0;var o=n(i(7)),s=n(i(1)),u=n(i(17)),l=n(i(8)),c=n(i(0)),p=n(i(18)),f=n(i(32)),d=n(i(2)),h=n(i(19)),m=n(i(20));r.default=function a(e){return new x(e).dispatch()};var v=n(i(33)),g=n(i(9)),y=n(i(40)),_=n(i(44)),b=n(i(45)),S=n(i(46)),k=n(i(47)),x=function(){function e(t){(0,h.default)(this,e),(0,d.default)(this,{spec:"",debugLevel:"info",plugins:[],pluginHistory:{},errors:[],mutations:[],promisedPatches:[],state:{},patches:[],context:{},contextTree:new k.default,showDebug:!1,allPatches:[],pluginProp:"specMap",libMethods:(0,d.default)((0,f.default)(this),g.default),allowMetaPatches:!1},t),this.get=this._get.bind(this),this.getContext=this._getContext.bind(this),this.hasRun=this._hasRun.bind(this),this.wrappedPlugins=this.plugins.map(this.wrapPlugin.bind(this)).filter(g.default.isFunction),this.patches.push(g.default.add([],this.spec)),this.patches.push(g.default.context([],this.context)),this.updatePatches(this.patches)}return(0,m.default)(e,[{key:"debug",value:function(e){if(this.debugLevel===e){for(var t,r=arguments.length,n=Array(r>1?r-1:0),i=1;i1?r-1:0),i=1;i0})}},{key:"nextPromisedPatch",value:function(){if(this.promisedPatches.length>0)return u.default.race(this.promisedPatches.map(function(e){return e.value}))}},{key:"getPluginHistory",value:function(e){var t=this.getPluginName(e);return this.pluginHistory[t]||[]}},{key:"getPluginRunCount",value:function(e){return this.getPluginHistory(e).length}},{key:"getPluginHistoryTip",value:function(e){var t=this.getPluginHistory(e);return t&&t[t.length-1]||{}}},{key:"getPluginMutationIndex",value:function(e){var t=this.getPluginHistoryTip(e).mutationIndex;return"number"!=typeof t?-1:t}},{key:"getPluginName",value:function(e){return e.pluginName}},{key:"updatePluginHistory",value:function(e,t){var r=this.getPluginName(e);(this.pluginHistory[r]=this.pluginHistory[r]||[]).push(t)}},{key:"updatePatches",value:function(e,t){var r=this;g.default.normalizeArray(e).forEach(function(e){if(e instanceof Error)r.errors.push(e);else try{if(!g.default.isObject(e))return void r.debug("updatePatches","Got a non-object patch",e);if(r.showDebug&&r.allPatches.push(e),g.default.isPromise(e.value))return r.promisedPatches.push(e),void r.promisedPatchThen(e);if(g.default.isContextPatch(e))return void r.setContext(e.path,e.value);if(g.default.isMutation(e))return void r.updateMutations(e)}catch(e){console.error(e),r.errors.push(e)}})}},{key:"updateMutations",value:function(e){"object"===(0,s.default)(e.value)&&!Array.isArray(e.value)&&this.allowMetaPatches&&(e.value=(0,d.default)({},e.value));var t=g.default.applyPatch(this.state,e,{allowMetaPatches:this.allowMetaPatches});t&&(this.mutations.push(e),this.state=t)}},{key:"removePromisedPatch",value:function(e){var t=this.promisedPatches.indexOf(e);t<0?this.debug("Tried to remove a promisedPatch that isn't there!"):this.promisedPatches.splice(t,1)}},{key:"promisedPatchThen",value:function(e){var t=this;return e.value=e.value.then(function(r){var n=(0,d.default)({},e,{value:r});t.removePromisedPatch(e),t.updatePatches(n)}).catch(function(r){t.removePromisedPatch(e),t.updatePatches(r)})}},{key:"getMutations",value:function(e,t){return e=e||0,"number"!=typeof t&&(t=this.mutations.length),this.mutations.slice(e,t)}},{key:"getCurrentMutations",value:function(){return this.getMutationsForPlugin(this.getCurrentPlugin())}},{key:"getMutationsForPlugin",value:function(e){var t=this.getPluginMutationIndex(e);return this.getMutations(t+1)}},{key:"getCurrentPlugin",value:function(){return this.currentPlugin}},{key:"getPatchesOfType",value:function(e,t){return e.filter(t)}},{key:"getLib",value:function(){return this.libMethods}},{key:"_get",value:function(e){return g.default.getIn(this.state,e)}},{key:"_getContext",value:function(e){return this.contextTree.get(e)}},{key:"setContext",value:function(e,t){return this.contextTree.set(e,t)}},{key:"_hasRun",value:function(e){return this.getPluginRunCount(this.getCurrentPlugin())>(e||0)}},{key:"_clone",value:function(e){return JSON.parse((0,o.default)(e))}},{key:"dispatch",value:function(){function e(e){e&&(e=g.default.fullyNormalizeArray(e),r.updatePatches(e,n))}var t=this,r=this,n=this.nextPlugin();if(!n){var i=this.nextPromisedPatch();if(i)return i.then(function(){return t.dispatch()}).catch(function(){return t.dispatch()});var o={spec:this.state,errors:this.errors};return this.showDebug&&(o.patches=this.allPatches),u.default.resolve(o)}if(r.pluginCount=r.pluginCount||{},r.pluginCount[n]=(r.pluginCount[n]||0)+1,r.pluginCount[n]>100)return u.default.resolve({spec:r.state,errors:r.errors.concat(new Error("We've reached a hard limit of 100 plugin runs"))});if(n!==this.currentPlugin&&this.promisedPatches.length){var a=this.promisedPatches.map(function(e){return e.value});return u.default.all(a.map(function(e){return e.then(Function,Function)})).then(function(){return t.dispatch()})}return function(){r.currentPlugin=n;var t=r.getCurrentMutations(),i=r.mutations.length-1;try{if(n.isGeneratorFunction){var o=!0,a=!1,s=void 0;try{for(var u,l=(0,p.default)(n(t,r.getLib()));!(o=(u=l.next()).done);o=!0)e(u.value)}catch(e){a=!0,s=e}finally{try{!o&&l.return&&l.return()}finally{if(a)throw s}}}else e(n(t,r.getLib()))}catch(t){console.error(t),e([(0,d.default)((0,f.default)(t),{plugin:n})])}finally{r.updatePluginHistory(n,{mutationIndex:i})}return r.dispatch()}()}}]),e}(),E={refs:y.default,allOf:_.default,parameters:b.default,properties:S.default};r.SpecMap=x,r.plugins=E},function(e,t){e.exports=r(372)},function(e,t){e.exports=r(193)},function(e,t){e.exports=r(86)},function(e,t){e.exports=r(24)},function(e,t){e.exports=r(964)},function(e,t){e.exports=r(189)},function(e,t){e.exports=r(967)},function(e,t){e.exports=r(968)},function(e,t,_){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function a(e,t){if(!O.test(e)){if(!t)throw new P("Tried to resolve a relative URL, without having a basePath. path: '"+e+"' basePath: '"+t+"'");return A.default.resolve(t,e)}return e}function u(e,t){return new P("Could not resolve reference because of: "+e.message,t,e)}function o(e){return(e+"").split("#")}function i(e,t){var r=I[e];if(r&&!R.default.isPromise(r))try{var n=f(t,r);return(0,E.default)(k.default.resolve(n),{__value:n})}catch(e){return k.default.reject(e)}return l(e).then(function(e){return f(t,e)})}function l(e){var t=I[e];return t?R.default.isPromise(t)?t:k.default.resolve(t):(I[e]=B.fetchJSON(e).then(function(t){return I[e]=t,t}),I[e])}function f(e,t){var r=p(e);if(r.length<1)return t;var n=R.default.getIn(t,r);if(void 0===n)throw new P("Could not resolve pointer: "+e+" does not exist in document",{pointer:e});return n}function p(e){if("string"!=typeof e)throw new TypeError("Expected a string, got a "+(void 0===e?"undefined":(0,b.default)(e)));return"/"===e[0]&&(e=e.substr(1)),""===e?[]:e.split("/").map(d)}function d(e){return"string"!=typeof e?e:D.default.unescape(e.replace(/~1/g,"/").replace(/~0/g,"~"))}function h(e){return D.default.escape(e.replace(/~/g,"~0").replace(/\//g,"~1"))}function m(e,t){if(N(t))return!0;var r=e.charAt(t.length),n=t.slice(-1);return 0===e.indexOf(t)&&(!r||"/"===r||"#"===r)&&"#"!==n}function y(e,t,r,n){var i=q.get(n);i||(i={},q.set(n,i));var o=function v(e){return 0===e.length?"":"/"+e.map(h).join("/")}(r),a=(t||"")+"#"+e;if(t==n.contextTree.get([]).baseDoc&&m(o,e))return!0;var s="";if(r.some(function(e){return s=s+"/"+h(e),i[s]&&i[s].some(function(e){return m(e,a)||m(a,e)})}))return!0;i[o]=(i[o]||[]).concat(a)}Object.defineProperty(t,"__esModule",{value:!0});var b=n(_(1)),S=n(_(0)),k=n(_(17)),x=n(_(41)),E=n(_(2)),C=_(42),w=n(_(15)),D=n(_(43)),A=n(_(10)),R=n(_(9)),M=n(_(21)),T=_(22),O=new RegExp("^([a-z]+://|//)","i"),P=(0,M.default)("JSONRefError",function(e,t,r){this.originalError=r,(0,E.default)(this,t||{})}),I={},q=new x.default,F={key:"$ref",plugin:function(e,t,r,n){var s=r.slice(0,-1);if(!(0,T.isFreelyNamed)(s)){var l=n.getContext(r).baseDoc;if("string"!=typeof e)return new P("$ref: must be a string (JSON-Ref)",{$ref:e,baseDoc:l,fullPath:r});var c=o(e),f=c[0],d=c[1]||"",h=void 0;try{h=l||f?a(f,l):null}catch(t){return u(t,{pointer:d,$ref:e,basePath:h,fullPath:r})}var m=void 0,v=void 0;if(!y(d,h,s,n)){if(null==h?(v=p(d),void 0===(m=n.get(v))&&(m=new P("Could not resolve reference: "+e,{pointer:d,$ref:e,baseDoc:l,fullPath:r}))):m=null!=(m=i(h,d)).__value?m.__value:m.catch(function(t){throw u(t,{pointer:d,$ref:e,baseDoc:l,fullPath:r})}),m instanceof Error)return[R.default.remove(r),m];var _=R.default.replace(s,m,{$$ref:e});return h&&h!==l?[_,R.default.context(s,{baseDoc:h})]:function g(e,t){var n=[e];return t.path.reduce(function(e,t){return n.push(e[t]),e[t]},e),function r(e){return R.default.isObject(e)&&(n.indexOf(e)>=0||(0,S.default)(e).some(function(t){return r(e[t])}))}(t.value)}(n.state,_)?void 0:_}}}},B=(0,E.default)(F,{docCache:I,absoluteify:a,clearCache:function s(e){void 0!==e?delete I[e]:(0,S.default)(I).forEach(function(e){delete I[e]})},JSONRefError:P,wrapError:u,getDoc:l,split:o,extractFromDoc:i,fetchJSON:function c(e){return(0,C.fetch)(e,{headers:{Accept:"application/json, application/yaml"},loadSpec:!0}).then(function(e){return e.text()}).then(function(e){return w.default.safeLoad(e)})},extract:f,jsonPointerToArray:p,unescapeJsonPointerToken:d});t.default=B;var N=function(e){return!e||"/"===e||"#"===e};e.exports=t.default},function(e,t){e.exports=r(969)},function(e,t){e.exports=r(980)},function(e,t){e.exports=r(981)},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=function(e){return e&&e.__esModule?e:{default:e}}(r(2)),i=r(22);t.default={key:"allOf",plugin:function(e,t,r,o,a){if(!a.meta||!a.meta.$$ref){var s=r.slice(0,-1);if(!(0,i.isFreelyNamed)(s)){if(!Array.isArray(e)){var u=new TypeError("allOf must be an array");return u.fullPath=r,u}var l=!1,c=a.value;s.forEach(function(e){c&&(c=c[e])}),delete(c=(0,n.default)({},c)).allOf;var p=[o.replace(s,{})].concat(e.map(function(e,t){if(!o.isObject(e)){if(l)return null;l=!0;var n=new TypeError("Elements in allOf must be objects");return n.fullPath=r,n}return o.mergeDeep(s,e)}));return p.push(o.mergeDeep(s,c)),c.$$ref||p.push(o.remove([].concat(s,"$$ref"))),p}}}},e.exports=t.default},function(e,t,r){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var i=n(r(2)),o=n(r(9));t.default={key:"parameters",plugin:function(e,t,r,n,a){if(Array.isArray(e)&&e.length){var s=(0,i.default)([],e),u=r.slice(0,-1),l=(0,i.default)({},o.default.getIn(n.spec,u));return e.forEach(function(e,t){try{s[t].default=n.parameterMacro(l,e)}catch(e){var i=new Error(e);return i.fullPath=r,i}}),o.default.replace(r,s)}return o.default.replace(r,e)}},e.exports=t.default},function(e,t,r){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var i=n(r(2)),o=n(r(9));t.default={key:"properties",plugin:function(e,t,r,n){var a=(0,i.default)({},e);for(var s in e)try{a[s].default=n.modelPropertyMacro(a[s])}catch(e){var u=new Error(e);return u.fullPath=r,u}return o.default.replace(r,a)}},e.exports=t.default},function(t,r,i){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function a(e,t){return u({children:{}},e,t)}function u(e,t,r){return e.value=t||{},e.protoValue=r?(0,s.default)({},r.protoValue,e.value):e.value,(0,o.default)(e.children).forEach(function(t){var r=e.children[t];e.children[t]=u(r,r.value,e)}),e}Object.defineProperty(r,"__esModule",{value:!0});var o=n(i(0)),s=n(i(3)),l=n(i(19)),c=n(i(20)),p=function(){function e(t){(0,l.default)(this,e),this.root=a(t||{})}return(0,c.default)(e,[{key:"set",value:function(e,t){var r=this.getParent(e,!0);if(r){var n=e[e.length-1],i=r.children;i[n]?u(i[n],t,r):i[n]=a(t,r)}else u(this.root,t,null)}},{key:"get",value:function(e){if((e=e||[]).length<1)return this.root.value;for(var t=this.root,r=void 0,n=void 0,i=0;i2&&void 0!==arguments[2]?arguments[2]:{};return o.default.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return n=y.returnEntireTree,i=y.baseDoc,s=y.requestInterceptor,p=y.responseInterceptor,f=y.parameterMacro,d=y.modelPropertyMacro,h={pathDiscriminator:r,baseDoc:i,requestInterceptor:s,responseInterceptor:p,parameterMacro:f,modelPropertyMacro:d},m=(0,c.normalizeSwagger)({spec:t}),v=m.spec,e.next=5,(0,l.default)((0,a.default)({},h,{spec:v,allowMetaPatches:!0,skipNormalization:!0}));case 5:return g=e.sent,!n&&Array.isArray(r)&&r.length&&(g.spec=(0,u.default)(g.spec,r)||null),e.abrupt("return",g);case 8:case"end":return e.stop()}},e,this)}));return function e(r,n){return t.apply(this,arguments)}}(),t.exports=r.default},function(e,t,r){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function a(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return function(t){var r=t.pathName,n=t.method,i=t.operationId;return function(t){var o=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return e.execute((0,s.default)({spec:e.spec},(0,l.default)(e,"requestInterceptor","responseInterceptor","userFetch"),{pathName:r,method:n,parameters:t,operationId:i},o))}}}function i(e){var t=e.spec,r=e.cb,n=void 0===r?p:r,i=e.defaultTag,o=void 0===i?"default":i,a=e.v2OperationIdCompatibilityMode,s={},u={};return(0,c.eachOperation)(t,function(e){var r=e.pathName,i=e.method,l=e.operation;(l.tags?f(l.tags):[o]).forEach(function(e){if("string"==typeof e){var o=u[e]=u[e]||{},p=(0,c.opId)(l,r,i,{v2OperationIdCompatibilityMode:a}),f=n({spec:t,pathName:r,method:i,operation:l,operationId:p});if(s[p])s[p]++,o[""+p+s[p]]=f;else if(void 0!==o[p]){var d=s[p]||1;s[p]=d+1,o[""+p+s[p]]=f;var h=o[p];delete o[p],o[""+p+d]=h}else o[p]=f}})}),u}Object.defineProperty(t,"__esModule",{value:!0}),t.self=void 0;var s=n(r(3));t.makeExecute=a,t.makeApisTagOperationsOperationExecute=function u(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=d.makeExecute(e),r=d.mapTagOperations({v2OperationIdCompatibilityMode:e.v2OperationIdCompatibilityMode,spec:e.spec,cb:t}),n={};for(var i in r)for(var o in n[i]={operations:{}},r[i])n[i].operations[o]={execute:r[i][o]};return{apis:n}},t.makeApisTagOperation=function o(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=d.makeExecute(e);return{apis:d.mapTagOperations({v2OperationIdCompatibilityMode:e.v2OperationIdCompatibilityMode,spec:e.spec,cb:t})}},t.mapTagOperations=i;var l=n(r(51)),c=r(4),p=function(){return null},f=function(e){return Array.isArray(e)?e:[e]},d=t.self={mapTagOperations:i,makeExecute:a}},function(e,t){e.exports=r(982)},function(e,t,r){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function u(e){var t=e.spec,r=e.operationId,n=(e.securities,e.requestContentType,e.responseContentType),i=e.scheme,a=e.requestInterceptor,s=e.responseInterceptor,u=e.contextUrl,l=e.userFetch,c=(e.requestBody,e.server),p=e.serverVariables,d=e.http,m=e.parameters,v=e.parameterBuilders,g=(0,A.isOAS3)(t);v||(v=g?C.default:E.default);var y={url:"",credentials:d&&d.withCredentials?"include":"same-origin",headers:{},cookies:{}};a&&(y.requestInterceptor=a),s&&(y.responseInterceptor=s),l&&(y.userFetch=l);var _=(0,A.getOperationRaw)(t,r);if(!_)throw new M("Operation "+r+" not found");var k=_.operation,x=void 0===k?{}:k,P=_.method,I=_.pathName;if(y.url+=o({spec:t,scheme:i,contextUrl:u,server:c,serverVariables:p,pathName:I,method:P}),!r)return delete y.cookies,y;y.url+=I,y.method=(""+P).toUpperCase(),m=m||{};var q=t.paths[I]||{};n&&(y.headers.accept=n);var F=O([].concat(R(x.parameters)).concat(R(q.parameters)));F.forEach(function(e){var r=v[e.in],n=void 0;if("body"===e.in&&e.schema&&e.schema.properties&&(n=m),void 0===(n=e&&e.name&&m[e.name])?n=e&&e.name&&m[e.in+"."+e.name]:T(e.name,F).length>1&&console.warn("Parameter '"+e.name+"' is ambiguous because the defined spec has more than one parameter with the name: '"+e.name+"' and the passed-in parameter values did not define an 'in' value."),void 0!==e.default&&void 0===n&&(n=e.default),void 0===n&&e.required&&!e.allowEmptyValue)throw new Error("Required parameter "+e.name+" is not provided");if(g&&e.schema&&"object"===e.schema.type&&"string"==typeof n)try{n=JSON.parse(n)}catch(e){throw new Error("Could not parse object parameter value string as JSON")}r&&r({req:y,parameter:e,value:n,operation:x,spec:t})});var B=(0,f.default)({},e,{operation:x});if((y=g?(0,w.default)(B,y):(0,D.default)(B,y)).cookies&&(0,h.default)(y.cookies).length){var N=(0,h.default)(y.cookies).reduce(function(e,t){var r=y.cookies[t];return e+(e?"&":"")+b.default.serialize(t,r)},"");y.headers.Cookie=N}return y.cookies&&delete y.cookies,(0,S.mergeInQueryOrForm)(y),y}function o(e){return(0,A.isOAS3)(e.spec)?function i(e){var t=e.spec,r=e.pathName,n=e.method,i=e.server,o=e.contextUrl,a=e.serverVariables,u=void 0===a?{}:a,c=(0,v.default)(t,["paths",r,(n||"").toLowerCase(),"servers"])||(0,v.default)(t,["paths",r,"servers"])||(0,v.default)(t,["servers"]),p="",f=null;if(i&&c&&c.length){var d=c.map(function(e){return e.url});d.indexOf(i)>-1&&(p=i,f=c[d.indexOf(i)])}!p&&c&&c.length&&(p=c[0].url,f=c[0]),p.indexOf("{")>-1&&function l(e){for(var t=[],r=/{([^}]+)}/g,n=void 0;n=r.exec(e);)t.push(n[1]);return t}(p).forEach(function(e){if(f.variables&&f.variables[e]){var t=f.variables[e],r=u[e]||t.default,n=new RegExp("{"+e+"}","g");p=p.replace(n,r)}});return function s(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",r=_.default.parse(e),n=_.default.parse(t),i=I(r.protocol)||I(n.protocol)||"",o=r.host||n.host,a=r.pathname||"",s=void 0;return"/"===(s=i&&o?i+"://"+(o+a):a)[s.length-1]?s.slice(0,-1):s}(p,o)}(e):function c(e){var t=e.spec,r=e.scheme,n=e.contextUrl,i=void 0===n?"":n,o=_.default.parse(i),a=Array.isArray(t.schemes)?t.schemes[0]:null,s=r||a||I(o.protocol)||"http",u=t.host||o.host||"",l=t.basePath||"",c=void 0;return"/"===(c=s&&u?s+"://"+(u+l):l)[c.length-1]?c.slice(0,-1):c}(e)}Object.defineProperty(t,"__esModule",{value:!0}),t.self=void 0;var p=n(r(7)),f=n(r(3)),d=n(r(53)),h=n(r(0)),m=n(r(2));t.execute=function a(e){var t=e.http,r=e.fetch,n=e.spec,i=e.operationId,o=e.pathName,a=e.method,s=e.parameters,u=e.securities,l=(0,d.default)(e,["http","fetch","spec","operationId","pathName","method","parameters","securities"]),c=t||r||k.default;o&&a&&!i&&(i=(0,A.legacyIdFromPathMethod)(o,a));var h=P.buildRequest((0,f.default)({spec:n,operationId:i,parameters:s,securities:u,http:c},l));return h.body&&((0,g.default)(h.body)||(0,y.default)(h.body))&&(h.body=(0,p.default)(h.body)),c(h)},t.buildRequest=u,t.baseUrl=o;var v=n((n(r(5)),r(12))),g=n(r(54)),y=n(r(55)),_=n((n(r(13)),r(10))),b=n(r(56)),S=r(6),k=n(S),x=n(r(21)),E=n(r(57)),C=n(r(58)),w=n(r(63)),D=n(r(65)),A=r(4),R=function(e){return Array.isArray(e)?e:[]},M=(0,x.default)("OperationNotFoundError",function(e,t,r){this.originalError=r,(0,m.default)(this,t||{})}),T=function(e,t){return t.filter(function(t){return t.name===e})},O=function(e){var t={};e.forEach(function(e){t[e.in]||(t[e.in]={}),t[e.in][e.name]=e});var r=[];return(0,h.default)(t).forEach(function(e){(0,h.default)(t[e]).forEach(function(n){r.push(t[e][n])})}),r},P=t.self={buildRequest:u},I=function(e){return e?e.replace(/\W/g,""):null}},function(e,t){e.exports=r(87)},function(e,t){e.exports=r(238)},function(e,t){e.exports=r(21)},function(e,t){e.exports=r(985)},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={body:function n(e){var t=e.req,r=e.value;t.body=r},header:function u(e){var t=e.req,r=e.parameter,n=e.value;t.headers=t.headers||{},void 0!==n&&(t.headers[r.name]=n)},query:function i(e){var t=e.req,r=e.value,n=e.parameter;if(t.query=t.query||{},!1===r&&"boolean"===n.type&&(r="false"),0===r&&["number","integer"].indexOf(n.type)>-1&&(r="0"),r)t.query[n.name]={collectionFormat:n.collectionFormat,value:r};else if(n.allowEmptyValue&&void 0!==r){var i=n.name;t.query[i]=t.query[i]||{},t.query[i].allowEmptyValue=!0}},path:function o(e){var t=e.req,r=e.value,n=e.parameter;t.url=t.url.replace("{"+n.name+"}",encodeURIComponent(r))},formData:function a(e){var t=e.req,r=e.value,n=e.parameter;(r||n.allowEmptyValue)&&(t.form=t.form||{},t.form[n.name]={value:r,allowEmptyValue:n.allowEmptyValue,collectionFormat:n.collectionFormat})}},e.exports=t.default},function(e,t,r){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var s=n(r(0)),l=n(r(1)),c=n(r(59));t.default={path:function a(e){var t=e.req,r=e.value,n=e.parameter,i=n.name,o=n.style,a=n.explode,s=(0,c.default)({key:n.name,value:r,style:o||"simple",explode:a||!1,escape:!1});t.url=t.url.replace("{"+i+"}",s)},query:function u(e){var t=e.req,r=e.value,n=e.parameter;if(t.query=t.query||{},!1===r&&(r="false"),0===r&&(r="0"),r){var i=void 0===r?"undefined":(0,l.default)(r);"deepObject"===n.style?(0,s.default)(r).forEach(function(e){var i=r[e];t.query[n.name+"["+e+"]"]={value:(0,c.default)({key:e,value:i,style:"deepObject",escape:n.allowReserved?"unsafe":"reserved"}),skipEncoding:!0}}):"object"!==i||Array.isArray(r)||"form"!==n.style&&n.style||!n.explode&&void 0!==n.explode?t.query[n.name]={value:(0,c.default)({key:n.name,value:r,style:n.style||"form",explode:void 0===n.explode||n.explode,escape:n.allowReserved?"unsafe":"reserved"}),skipEncoding:!0}:(0,s.default)(r).forEach(function(e){var i=r[e];t.query[e]={value:(0,c.default)({key:e,value:i,style:n.style||"form",escape:n.allowReserved?"unsafe":"reserved"}),skipEncoding:!0}})}else if(n.allowEmptyValue&&void 0!==r){var o=n.name;t.query[o]=t.query[o]||{},t.query[o].allowEmptyValue=!0}},header:function o(e){var t=e.req,r=e.parameter,n=e.value;t.headers=t.headers||{},p.indexOf(r.name.toLowerCase())>-1||void 0!==n&&(t.headers[r.name]=(0,c.default)({key:r.name,value:n,style:r.style||"simple",explode:void 0!==r.explode&&r.explode,escape:!1}))},cookie:function i(e){var t=e.req,r=e.parameter,n=e.value;t.headers=t.headers||{};var i=void 0===n?"undefined":(0,l.default)(n);if("undefined"!==i){var o="object"===i&&!Array.isArray(n)&&r.explode?"":r.name+"=";t.headers.Cookie=o+(0,c.default)({key:r.name,value:n,escape:!1,style:r.style||"form",explode:void 0!==r.explode&&r.explode})}}};var p=["accept","authorization","content-type"];e.exports=t.default},function(e,t,r){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=(arguments.length>1&&void 0!==arguments[1]?arguments[1]:{}).escape,r=arguments[2];return"number"==typeof e&&(e=e.toString()),"string"==typeof e&&e.length&&t?r?JSON.parse(e):(0,p.stringToCharArray)(e).map(function(e){return d(e)?e:f(e)&&"unsafe"===t?e:((0,c.default)(e)||[]).map(function(e){return e.toString(16).toUpperCase()}).map(function(e){return"%"+e}).join("")}).join(""):e}Object.defineProperty(t,"__esModule",{value:!0});var s=n(r(0)),l=n(r(1));t.encodeDisallowedCharacters=a,t.default=function(e){var t=e.value;return Array.isArray(t)?function u(e){var t=e.key,r=e.value,n=e.style,i=e.explode,o=e.escape,s=function(e){return a(e,{escape:o})};if("simple"===n)return r.map(function(e){return s(e)}).join(",");if("label"===n)return"."+r.map(function(e){return s(e)}).join(".");if("matrix"===n)return r.map(function(e){return s(e)}).reduce(function(e,r){return!e||i?(e||"")+";"+t+"="+r:e+","+r},"");if("form"===n){var u=i?"&"+t+"=":",";return r.map(function(e){return s(e)}).join(u)}if("spaceDelimited"===n){var l=i?t+"=":"";return r.map(function(e){return s(e)}).join(" "+l)}if("pipeDelimited"===n){var c=i?t+"=":"";return r.map(function(e){return s(e)}).join("|"+c)}}(e):"object"===(void 0===t?"undefined":(0,l.default)(t))?function o(e){var t=e.key,r=e.value,n=e.style,i=e.explode,o=e.escape,u=function(e){return a(e,{escape:o})},l=(0,s.default)(r);return"simple"===n?l.reduce(function(e,t){var n=u(r[t]);return(e?e+",":"")+t+(i?"=":",")+n},""):"label"===n?l.reduce(function(e,t){var n=u(r[t]);return(e?e+".":".")+t+(i?"=":".")+n},""):"matrix"===n&&i?l.reduce(function(e,t){var n=u(r[t]);return(e?e+";":";")+t+"="+n},""):"matrix"===n?l.reduce(function(e,n){var i=u(r[n]);return(e?e+",":";"+t+"=")+n+","+i},""):"form"===n?l.reduce(function(e,t){var n=u(r[t]);return(e?e+(i?"&":","):"")+t+(i?"=":",")+n},""):void 0}(e):function i(e){var t=e.key,r=e.value,n=e.style,i=e.escape,o=function(e){return a(e,{escape:i})};return"simple"===n?o(r):"label"===n?"."+o(r):"matrix"===n?";"+t+"="+o(r):"form"===n?o(r):"deepObject"===n?o(r):void 0}(e)};var c=n((n(r(60)),r(61))),p=r(62),f=function(e){return":/?#[]@!$&'()*+,;=".indexOf(e)>-1},d=function(e){return/^[a-z0-9\-._~]+$/i.test(e)}},function(e,t){e.exports=r(986)},function(e,t){e.exports=r(987)},function(e,t){e.exports=r(988)},function(e,t,r){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=e.request,r=e.securities,n=void 0===r?{}:r,i=e.operation,o=void 0===i?{}:i,a=e.spec,p=(0,u.default)({},t),f=n.authorized,d=void 0===f?{}:f,h=o.security||a.security||[],m=d&&!!(0,s.default)(d).length,v=(0,l.default)(a,["components","securitySchemes"])||{};return p.headers=p.headers||{},p.query=p.query||{},(0,s.default)(n).length&&m&&h&&(!Array.isArray(o.security)||o.security.length)?(h.forEach(function(e,t){for(var r in e){var n=d[r],i=v[r];if(n){var o=n.value||n,a=i.type;if(n)if("apiKey"===a)"query"===i.in&&(p.query[i.name]=o),"header"===i.in&&(p.headers[i.name]=o),"cookie"===i.in&&(p.cookies[i.name]=o);else if("http"===a){if("basic"===i.scheme){var s=o.username,u=o.password,l=(0,c.default)(s+":"+u);p.headers.Authorization="Basic "+l}"bearer"===i.scheme&&(p.headers.Authorization="Bearer "+o)}else if("oauth2"===a){var f=n.token||{},h=f.access_token,m=f.token_type;m&&"bearer"!==m.toLowerCase()||(m="Bearer"),p.headers.Authorization=m+" "+h}}}}),p):t}Object.defineProperty(t,"__esModule",{value:!0});var i=n(r(7)),o=n(r(1)),s=n(r(0));t.default=function(e,t){var r=e.operation,n=e.requestBody,u=e.securities,l=e.spec,c=e.attachContentTypeForEmptyPayload,f=e.requestContentType;t=a({request:t,securities:u,operation:r,spec:l});var d=r.requestBody||{},h=(0,s.default)(d.content||{}),m=f&&h.indexOf(f)>-1;if(n||c){if(f&&m)t.headers["Content-Type"]=f;else if(!f){var v=h[0];v&&(t.headers["Content-Type"]=v,f=v)}}else f&&m&&(t.headers["Content-Type"]=f);return n&&(f?h.indexOf(f)>-1&&("application/x-www-form-urlencoded"===f||0===f.indexOf("multipart/")?"object"===(void 0===n?"undefined":(0,o.default)(n))?(t.form={},(0,s.default)(n).forEach(function(e){var r,a=n[e],s=void 0;"undefined"!=typeof File&&(s=a instanceof File),"undefined"!=typeof Blob&&(s=s||a instanceof Blob),void 0!==p.Buffer&&(s=s||p.Buffer.isBuffer(a)),r="object"!==(void 0===a?"undefined":(0,o.default)(a))||s?a:Array.isArray(a)?a.toString():(0,i.default)(a),t.form[e]={value:r}})):t.form=n:t.body=n):t.body=n),t},t.applySecurities=a;var u=n(r(5)),l=n(r(12)),c=n(r(13)),p=r(64)},function(e,t){e.exports=r(48)},function(e,t,r){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=e.request,r=e.securities,n=void 0===r?{}:r,a=e.operation,u=void 0===a?{}:a,l=e.spec,c=(0,s.default)({},t),p=n.authorized,f=void 0===p?{}:p,d=n.specSecurity,h=void 0===d?[]:d,m=u.security||h,v=f&&!!(0,i.default)(f).length,g=l.securityDefinitions;return c.headers=c.headers||{},c.query=c.query||{},(0,i.default)(n).length&&v&&m&&(!Array.isArray(u.security)||u.security.length)?(m.forEach(function(e,t){for(var r in e){var n=f[r];if(n){var i=n.token,a=n.value||n,s=g[r],u=s.type,l=i&&i.access_token,p=i&&i.token_type;if(n)if("apiKey"===u){var d="query"===s.in?"query":"headers";c[d]=c[d]||{},c[d][s.name]=a}else"basic"===u?a.header?c.headers.authorization=a.header:(a.base64=(0,o.default)(a.username+":"+a.password),c.headers.authorization="Basic "+a.base64):"oauth2"===u&&l&&(p=p&&"bearer"!==p.toLowerCase()?p:"Bearer",c.headers.authorization=p+" "+l)}}}),c):t}Object.defineProperty(t,"__esModule",{value:!0});var i=n(r(0));t.default=function(e,t){var r=e.spec,n=e.operation,i=e.securities,o=e.requestContentType,s=e.attachContentTypeForEmptyPayload;if((t=a({request:t,securities:i,operation:n,spec:r})).body||t.form||s)o?t.headers["Content-Type"]=o:Array.isArray(n.consumes)?t.headers["Content-Type"]=n.consumes[0]:Array.isArray(r.consumes)?t.headers["Content-Type"]=r.consumes[0]:n.parameters&&n.parameters.filter(function(e){return"file"===e.type}).length?t.headers["Content-Type"]="multipart/form-data":n.parameters&&n.parameters.filter(function(e){return"formData"===e.in}).length&&(t.headers["Content-Type"]="application/x-www-form-urlencoded");else if(o){var u=n.parameters&&n.parameters.filter(function(e){return"body"===e.in}).length>0,l=n.parameters&&n.parameters.filter(function(e){return"formData"===e.in}).length>0;(u||l)&&(t.headers["Content-Type"]=o)}return t},t.applySecurities=a;var o=n(r(13)),s=n(r(5));n(r(6))}])},function(e,t,r){"use strict";var n=Object.prototype.hasOwnProperty,i=function(){for(var e=[],t=0;t<256;++t)e.push("%"+((t<16?"0":"")+t.toString(16)).toUpperCase());return e}();t.arrayToObject=function arrayToObject(e,t){for(var r=t&&t.plainObjects?Object.create(null):{},n=0;n=48&&o<=57||o>=65&&o<=90||o>=97&&o<=122?r+=t.charAt(n):o<128?r+=i[o]:o<2048?r+=i[192|o>>6]+i[128|63&o]:o<55296||o>=57344?r+=i[224|o>>12]+i[128|o>>6&63]+i[128|63&o]:(n+=1,o=65536+((1023&o)<<10|1023&t.charCodeAt(n)),r+=i[240|o>>18]+i[128|o>>12&63]+i[128|o>>6&63]+i[128|63&o])}return r},t.compact=function compact(e){for(var t=[{obj:{o:e},prop:"o"}],r=[],n=0;n=0;s--)if(l[s]!=c[s])return!1;for(s=l.length-1;s>=0;s--)if(u=l[s],!a(e[u],t[u],r))return!1;return typeof e==typeof t}(e,t,r))};function isUndefinedOrNull(e){return null===e||void 0===e}function isBuffer(e){return!(!e||"object"!=typeof e||"number"!=typeof e.length)&&("function"==typeof e.copy&&"function"==typeof e.slice&&!(e.length>0&&"number"!=typeof e[0]))}},function(e,t,r){var n={strict:!0},i=r(422),o=function(e,t){return i(e,t,n)},a=r(243);t.JsonPatchError=a.PatchError,t.deepClone=a._deepClone;var s={add:function(e,t,r){return e[t]=this.value,{newDocument:r}},remove:function(e,t,r){var n=e[t];return delete e[t],{newDocument:r,removed:n}},replace:function(e,t,r){var n=e[t];return e[t]=this.value,{newDocument:r,removed:n}},move:function(e,t,r){var n=getValueByPointer(r,this.path);n&&(n=a._deepClone(n));var i=applyOperation(r,{op:"remove",path:this.from}).removed;return applyOperation(r,{op:"add",path:this.path,value:i}),{newDocument:r,removed:n}},copy:function(e,t,r){var n=getValueByPointer(r,this.from);return applyOperation(r,{op:"add",path:this.path,value:a._deepClone(n)}),{newDocument:r}},test:function(e,t,r){return{newDocument:r,test:o(e[t],this.value)}},_get:function(e,t,r){return this.value=e[t],{newDocument:r}}},u={add:function(e,t,r){return a.isInteger(t)?e.splice(t,0,this.value):e[t]=this.value,{newDocument:r,index:t}},remove:function(e,t,r){return{newDocument:r,removed:e.splice(t,1)[0]}},replace:function(e,t,r){var n=e[t];return e[t]=this.value,{newDocument:r,removed:n}},move:s.move,copy:s.copy,test:s.test,_get:s._get};function getValueByPointer(e,t){if(""==t)return e;var r={op:"_get",path:t};return applyOperation(e,r),r.value}function applyOperation(e,r,n,i){if(void 0===n&&(n=!1),void 0===i&&(i=!0),n&&("function"==typeof n?n(r,0,e,r.path):validator(r,0)),""===r.path){var l={newDocument:e};if("add"===r.op)return l.newDocument=r.value,l;if("replace"===r.op)return l.newDocument=r.value,l.removed=e,l;if("move"===r.op||"copy"===r.op)return l.newDocument=getValueByPointer(e,r.from),"move"===r.op&&(l.removed=e),l;if("test"===r.op){if(l.test=o(e,r.value),!1===l.test)throw new t.JsonPatchError("Test operation failed","TEST_OPERATION_FAILED",0,r,e);return l.newDocument=e,l}if("remove"===r.op)return l.removed=e,l.newDocument=null,l;if("_get"===r.op)return r.value=e,l;if(n)throw new t.JsonPatchError("Operation `op` property is not one of operations defined in RFC-6902","OPERATION_OP_INVALID",0,r,e);return l}i||(e=a._deepClone(e));var c=(r.path||"").split("/"),p=e,f=1,d=c.length,h=void 0,m=void 0,v=void 0;for(v="function"==typeof n?n:validator;;){if(m=c[f],n&&void 0===h&&(void 0===p[m]?h=c.slice(0,f).join("/"):f==d-1&&(h=r.path),void 0!==h&&v(r,0,e,h)),f++,Array.isArray(p)){if("-"===m)m=p.length;else{if(n&&!a.isInteger(m))throw new t.JsonPatchError("Expected an unsigned base-10 integer value, making the new referenced value the array element with the zero-based index","OPERATION_PATH_ILLEGAL_ARRAY_INDEX",0,r.path,r);a.isInteger(m)&&(m=~~m)}if(f>=d){if(n&&"add"===r.op&&m>p.length)throw new t.JsonPatchError("The specified index MUST NOT be greater than the number of elements in the array","OPERATION_VALUE_OUT_OF_BOUNDS",0,r.path,r);if(!1===(l=u[r.op].call(r,p,m,e)).test)throw new t.JsonPatchError("Test operation failed","TEST_OPERATION_FAILED",0,r,e);return l}}else if(m&&-1!=m.indexOf("~")&&(m=a.unescapePathComponent(m)),f>=d){if(!1===(l=s[r.op].call(r,p,m,e)).test)throw new t.JsonPatchError("Test operation failed","TEST_OPERATION_FAILED",0,r,e);return l}p=p[m]}}function applyPatch(e,r,n,i){if(void 0===i&&(i=!0),n&&!Array.isArray(r))throw new t.JsonPatchError("Patch sequence must be an array","SEQUENCE_NOT_AN_ARRAY");i||(e=a._deepClone(e));for(var o=new Array(r.length),s=0,u=r.length;s0)throw new t.JsonPatchError('Operation `path` property must start with "/"',"OPERATION_PATH_INVALID",r,e,n);if(("move"===e.op||"copy"===e.op)&&"string"!=typeof e.from)throw new t.JsonPatchError("Operation `from` property is not present (applicable in `move` and `copy` operations)","OPERATION_FROM_REQUIRED",r,e,n);if(("add"===e.op||"replace"===e.op||"test"===e.op)&&void 0===e.value)throw new t.JsonPatchError("Operation `value` property is not present (applicable in `add`, `replace` and `test` operations)","OPERATION_VALUE_REQUIRED",r,e,n);if(("add"===e.op||"replace"===e.op||"test"===e.op)&&a.hasUndefined(e.value))throw new t.JsonPatchError("Operation `value` property is not present (applicable in `add`, `replace` and `test` operations)","OPERATION_VALUE_CANNOT_CONTAIN_UNDEFINED",r,e,n);if(n)if("add"==e.op){var o=e.path.split("/").length,u=i.split("/").length;if(o!==u+1&&o!==u)throw new t.JsonPatchError("Cannot perform an `add` operation at the desired path","OPERATION_PATH_CANNOT_ADD",r,e,n)}else if("replace"===e.op||"remove"===e.op||"_get"===e.op){if(e.path!==i)throw new t.JsonPatchError("Cannot perform the operation at a path that does not exist","OPERATION_PATH_UNRESOLVABLE",r,e,n)}else if("move"===e.op||"copy"===e.op){var l=validate([{op:"_get",path:e.from,value:void 0}],n);if(l&&"OPERATION_PATH_UNRESOLVABLE"===l.name)throw new t.JsonPatchError("Cannot perform the operation from a path that does not exist","OPERATION_FROM_UNRESOLVABLE",r,e,n)}}function validate(e,r,n){try{if(!Array.isArray(e))throw new t.JsonPatchError("Patch sequence must be an array","SEQUENCE_NOT_AN_ARRAY");if(r)applyPatch(a._deepClone(r),a._deepClone(e),n||!0);else{n=n||validator;for(var i=0;i1&&void 0!==arguments[1]?arguments[1]:(0,a.List)();return function(e){return(e.authSelectors.definitionsToAuthorize()||(0,a.List)()).filter(function(e){return t.some(function(t){return t.get(e.keySeq().first())})})}},t.authorized=(0,o.createSelector)(s,function(e){return e.get("authorized")||(0,a.Map)()}),t.isAuthorized=function isAuthorized(e,t){return function(e){var r=e.authSelectors.authorized();return a.List.isList(t)?!!t.toJS().filter(function(e){return-1===(0,n.default)(e).map(function(e){return!!r.get(e)}).indexOf(!1)}).length:null}},t.getConfigs=(0,o.createSelector)(s,function(e){return e.get("configs")})},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.execute=void 0;var n=function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}(r(26));t.execute=function execute(e,t){var r=t.authSelectors,i=t.specSelectors;return function(t){var o=t.path,a=t.method,s=t.operation,u=t.extras,l={authorized:r.authorized()&&r.authorized().toJS(),definitions:i.securityDefinitions()&&i.securityDefinitions().toJS(),specSecurity:i.security()&&i.security().toJS()};return e((0,n.default)({path:o,method:a,operation:s,securities:l},u))}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(){return{fn:{shallowEqualKeys:n.shallowEqualKeys}}};var n=r(10)},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function SplitPaneModePlugin(){return{components:{SplitPaneMode:n.default}}};var n=function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}(r(431))},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=_interopRequireDefault(r(4)),i=_interopRequireDefault(r(2)),o=_interopRequireDefault(r(3)),a=_interopRequireDefault(r(5)),s=_interopRequireDefault(r(6)),u=_interopRequireDefault(r(0)),l=_interopRequireDefault(r(1)),c=_interopRequireDefault(r(989));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}var p=["split-pane-mode"],f="left",d="right",h="both",m=function(e){function SplitPaneMode(){var e,t,r,o;(0,i.default)(this,SplitPaneMode);for(var s=arguments.length,u=Array(s),l=0;l=400)return a.updateLoadingStatus("failed"),n.newThrownErr((0,i.default)(new Error((t.message||t.statusText)+" "+e),{source:"fetch"})),void(!t.status&&t instanceof Error&&function checkPossibleFailReasons(){try{var t=void 0;if("URL"in s.default?t=new URL(e):(t=document.createElement("a")).href=e,"https:"!==t.protocol&&"https:"===s.default.location.protocol){var r=(0,i.default)(new Error("Possible mixed-content issue? The page was loaded over https:// but a "+t.protocol+"// URL was specified. Check that you are not attempting to load mixed content."),{source:"fetch"});return void n.newThrownErr(r)}if(t.origin!==s.default.location.origin){var o=(0,i.default)(new Error("Possible cross-origin (CORS) issue? The URL origin ("+t.origin+") does not match the page ("+s.default.location.origin+"). Check the server returns the correct 'Access-Control-Allow-*' headers."),{source:"fetch"});n.newThrownErr(o)}}catch(e){return}}());a.updateLoadingStatus("success"),a.updateSpec(t.text),o.url()!==e&&a.updateUrl(e)}e=e||o.url(),a.updateLoadingStatus("loading"),n.clear({source:"fetch"}),l({url:e,loadSpec:!0,requestInterceptor:c.requestInterceptor||function(e){return e},responseInterceptor:c.responseInterceptor||function(e){return e},credentials:"same-origin",headers:{Accept:"application/json,*/*"}}).then(next,next)}},updateLoadingStatus:function updateLoadingStatus(e){var t=[null,"loading","failed","success","failedConfig"];return-1===t.indexOf(e)&&console.error("Error: "+e+" is not one of "+(0,n.default)(t)),{type:"spec_update_loading_status",payload:e}}},u={loadingStatus:(0,o.createSelector)(function(e){return e||(0,a.Map)()},function(e){return e.get("loadingStatus")||null})};return{statePlugins:{spec:{actions:r,reducers:{spec_update_loading_status:function spec_update_loading_status(e,t){return"string"==typeof t.payload?e.set("loadingStatus",t.payload):e}},selectors:u}}}};var o=r(59),a=r(7),s=_interopRequireDefault(r(32));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function configsPlugin(){return{statePlugins:{spec:{actions:a,selectors:l},configs:{reducers:u.default,actions:o,selectors:s}}}};var n=_interopRequireDefault(r(1025)),i=r(249),o=_interopRequireWildcard(r(250)),a=_interopRequireWildcard(r(438)),s=_interopRequireWildcard(r(439)),u=_interopRequireDefault(r(440));function _interopRequireWildcard(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[r]=e[r]);return t.default=e,t}function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}var l={getLocalConfig:function getLocalConfig(){return(0,i.parseYamlConfig)(n.default)}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getConfigByUrl=t.downloadConfig=void 0;var n=r(249);t.downloadConfig=function downloadConfig(e){return function(t){return(0,t.fn.fetch)(e)}},t.getConfigByUrl=function getConfigByUrl(e,t){return function(r){var i=r.specActions;if(e)return i.downloadConfig(e).then(next,next);function next(r){r instanceof Error||r.status>=400?(i.updateLoadingStatus("failedConfig"),i.updateLoadingStatus("failedConfig"),i.updateUrl(""),console.error(r.statusText+" "+e.url),t(null)):t((0,n.parseYamlConfig)(r.text))}}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});t.get=function get(e,t){return e.getIn(Array.isArray(t)?t:[t])}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n,i=function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}(r(24)),o=r(7),a=r(250);t.default=(n={},(0,i.default)(n,a.UPDATE_CONFIGS,function(e,t){return e.merge((0,o.fromJS)(t.payload))}),(0,i.default)(n,a.TOGGLE_CONFIGS,function(e,t){var r=t.payload,n=e.get(r);return e.set(r,!n)}),n)},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(){return[n.default,{statePlugins:{configs:{wrapActions:{loaded:function loaded(e,t){return function(){e.apply(void 0,arguments);var r=window.location.hash;t.layoutActions.parseDeepLinkHash(r)}}}}},wrapComponents:{operation:i.default,OperationTag:o.default}}]};var n=_interopRequireDefault(r(442)),i=_interopRequireDefault(r(444)),o=_interopRequireDefault(r(445));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.clearScrollTo=t.scrollToElement=t.readyToScroll=t.parseDeepLinkHash=t.scrollTo=t.show=void 0;var n,i=_interopRequireDefault(r(24)),o=_interopRequireDefault(r(19)),a=r(443),s=_interopRequireDefault(r(1026)),u=r(7),l=_interopRequireDefault(u);function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}var c=t.show=function show(e,t){var r=t.getConfigs,n=t.layoutSelectors;return function(){for(var t=arguments.length,i=Array(t),s=0;s-1?t:e.content.clientHeight},this.getWrapperStyle=function(t){if(e.state.currentState===u&&e.state.to){var r=e.props.fixedHeight;return r>-1?{overflow:"hidden",height:r}:{height:"auto"}}return"WAITING"!==e.state.currentState||e.state.to?{overflow:"hidden",height:Math.max(0,t)}:{overflow:"hidden",height:0}},this.getMotionProps=function(){var t=e.props.springConfig;return e.state.currentState===u?{defaultStyle:{height:e.state.to},style:{height:e.state.to}}:{defaultStyle:{height:e.state.from},style:{height:(0,s.spring)(e.state.to,n({precision:1},t))}}},this.renderContent=function(t){var r=t.height,i=e.props,a=(i.isOpened,i.springConfig,i.forceInitialAnimation,i.hasNestedCollapse,i.fixedHeight,i.theme),s=i.style,u=i.onRender,l=(i.onRest,i.onMeasure,i.children),c=function _objectWithoutProperties(e,t){var r={};for(var n in e)t.indexOf(n)>=0||Object.prototype.hasOwnProperty.call(e,n)&&(r[n]=e[n]);return r}(i,["isOpened","springConfig","forceInitialAnimation","hasNestedCollapse","fixedHeight","theme","style","onRender","onRest","onMeasure","children"]),p=e.state;return u({current:r,from:p.from,to:p.to}),o.default.createElement("div",n({ref:e.onWrapperRef,className:a.collapse,style:n({},e.getWrapperStyle(Math.max(0,r)),s)},c),o.default.createElement("div",{ref:e.onContentRef,className:a.content},l))}}},function(e,t,r){"use strict";t.__esModule=!0,t.default={noWobble:{stiffness:170,damping:26},gentle:{stiffness:120,damping:14},wobbly:{stiffness:180,damping:12},stiff:{stiffness:210,damping:20}},e.exports=t.default},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Collapse=t.Link=t.Select=t.Input=t.TextArea=t.Button=t.Row=t.Col=t.Container=void 0;var n=_interopRequireDefault(r(26)),i=_interopRequireDefault(r(87)),o=_interopRequireDefault(r(4)),a=_interopRequireDefault(r(2)),s=_interopRequireDefault(r(3)),u=_interopRequireDefault(r(5)),l=_interopRequireDefault(r(6)),c=_interopRequireDefault(r(0)),p=_interopRequireDefault(r(1)),f=r(450);function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function xclass(){for(var e=arguments.length,t=Array(e),r=0;r",Gt:"≫",gt:">",gtcc:"⪧",gtcir:"⩺",gtdot:"â‹—",gtlPar:"⦕",gtquest:"⩼",gtrapprox:"⪆",gtrarr:"⥸",gtrdot:"â‹—",gtreqless:"â‹›",gtreqqless:"⪌",gtrless:"≷",gtrsim:"≳",gvertneqq:"≩︀",gvnE:"≩︀",Hacek:"ˇ",hairsp:" ",half:"½",hamilt:"â„‹",HARDcy:"Ъ",hardcy:"ÑŠ",hArr:"⇔",harr:"↔",harrcir:"⥈",harrw:"↭",Hat:"^",hbar:"â„",Hcirc:"Ĥ",hcirc:"Ä¥",hearts:"♥",heartsuit:"♥",hellip:"…",hercon:"⊹",Hfr:"ℌ",hfr:"ð”¥",HilbertSpace:"â„‹",hksearow:"⤥",hkswarow:"⤦",hoarr:"⇿",homtht:"∻",hookleftarrow:"↩",hookrightarrow:"↪",Hopf:"â„",hopf:"ð•™",horbar:"―",HorizontalLine:"─",Hscr:"â„‹",hscr:"ð’½",hslash:"â„",Hstrok:"Ħ",hstrok:"ħ",HumpDownHump:"≎",HumpEqual:"â‰",hybull:"âƒ",hyphen:"â€",Iacute:"Ã",iacute:"í",ic:"â£",Icirc:"ÃŽ",icirc:"î",Icy:"И",icy:"и",Idot:"İ",IEcy:"Е",iecy:"е",iexcl:"¡",iff:"⇔",Ifr:"â„‘",ifr:"ð”¦",Igrave:"ÃŒ",igrave:"ì",ii:"â…ˆ",iiiint:"⨌",iiint:"∭",iinfin:"â§œ",iiota:"â„©",IJlig:"IJ",ijlig:"ij",Im:"â„‘",Imacr:"Ī",imacr:"Ä«",image:"â„‘",ImaginaryI:"â…ˆ",imagline:"â„",imagpart:"â„‘",imath:"ı",imof:"⊷",imped:"Ƶ",Implies:"⇒",in:"∈",incare:"â„…",infin:"∞",infintie:"â§",inodot:"ı",Int:"∬",int:"∫",intcal:"⊺",integers:"ℤ",Integral:"∫",intercal:"⊺",Intersection:"â‹‚",intlarhk:"⨗",intprod:"⨼",InvisibleComma:"â£",InvisibleTimes:"â¢",IOcy:"Ð",iocy:"Ñ‘",Iogon:"Ä®",iogon:"į",Iopf:"ð•€",iopf:"ð•š",Iota:"Ι",iota:"ι",iprod:"⨼",iquest:"¿",Iscr:"â„",iscr:"ð’¾",isin:"∈",isindot:"⋵",isinE:"⋹",isins:"â‹´",isinsv:"⋳",isinv:"∈",it:"â¢",Itilde:"Ĩ",itilde:"Ä©",Iukcy:"І",iukcy:"Ñ–",Iuml:"Ã",iuml:"ï",Jcirc:"Ä´",jcirc:"ĵ",Jcy:"Й",jcy:"й",Jfr:"ð”",jfr:"ð”§",jmath:"È·",Jopf:"ð•",jopf:"ð•›",Jscr:"ð’¥",jscr:"ð’¿",Jsercy:"Ј",jsercy:"ј",Jukcy:"Є",jukcy:"Ñ”",Kappa:"Κ",kappa:"κ",kappav:"ϰ",Kcedil:"Ķ",kcedil:"Ä·",Kcy:"К",kcy:"к",Kfr:"ð”Ž",kfr:"ð”¨",kgreen:"ĸ",KHcy:"Ð¥",khcy:"Ñ…",KJcy:"ÐŒ",kjcy:"Ñœ",Kopf:"ð•‚",kopf:"ð•œ",Kscr:"ð’¦",kscr:"ð“€",lAarr:"⇚",Lacute:"Ĺ",lacute:"ĺ",laemptyv:"⦴",lagran:"â„’",Lambda:"Λ",lambda:"λ",Lang:"⟪",lang:"⟨",langd:"⦑",langle:"⟨",lap:"⪅",Laplacetrf:"â„’",laquo:"«",Larr:"↞",lArr:"â‡",larr:"â†",larrb:"⇤",larrbfs:"⤟",larrfs:"â¤",larrhk:"↩",larrlp:"↫",larrpl:"⤹",larrsim:"⥳",larrtl:"↢",lat:"⪫",lAtail:"⤛",latail:"⤙",late:"⪭",lates:"⪭︀",lBarr:"⤎",lbarr:"⤌",lbbrk:"â²",lbrace:"{",lbrack:"[",lbrke:"⦋",lbrksld:"â¦",lbrkslu:"â¦",Lcaron:"Ľ",lcaron:"ľ",Lcedil:"Ä»",lcedil:"ļ",lceil:"⌈",lcub:"{",Lcy:"Л",lcy:"л",ldca:"⤶",ldquo:"“",ldquor:"„",ldrdhar:"⥧",ldrushar:"⥋",ldsh:"↲",lE:"≦",le:"≤",LeftAngleBracket:"⟨",LeftArrow:"â†",Leftarrow:"â‡",leftarrow:"â†",LeftArrowBar:"⇤",LeftArrowRightArrow:"⇆",leftarrowtail:"↢",LeftCeiling:"⌈",LeftDoubleBracket:"⟦",LeftDownTeeVector:"⥡",LeftDownVector:"⇃",LeftDownVectorBar:"⥙",LeftFloor:"⌊",leftharpoondown:"↽",leftharpoonup:"↼",leftleftarrows:"⇇",LeftRightArrow:"↔",Leftrightarrow:"⇔",leftrightarrow:"↔",leftrightarrows:"⇆",leftrightharpoons:"⇋",leftrightsquigarrow:"↭",LeftRightVector:"⥎",LeftTee:"⊣",LeftTeeArrow:"↤",LeftTeeVector:"⥚",leftthreetimes:"â‹‹",LeftTriangle:"⊲",LeftTriangleBar:"â§",LeftTriangleEqual:"⊴",LeftUpDownVector:"⥑",LeftUpTeeVector:"⥠",LeftUpVector:"↿",LeftUpVectorBar:"⥘",LeftVector:"↼",LeftVectorBar:"⥒",lEg:"⪋",leg:"⋚",leq:"≤",leqq:"≦",leqslant:"⩽",les:"⩽",lescc:"⪨",lesdot:"â©¿",lesdoto:"âª",lesdotor:"⪃",lesg:"⋚︀",lesges:"⪓",lessapprox:"⪅",lessdot:"â‹–",lesseqgtr:"⋚",lesseqqgtr:"⪋",LessEqualGreater:"⋚",LessFullEqual:"≦",LessGreater:"≶",lessgtr:"≶",LessLess:"⪡",lesssim:"≲",LessSlantEqual:"⩽",LessTilde:"≲",lfisht:"⥼",lfloor:"⌊",Lfr:"ð”",lfr:"ð”©",lg:"≶",lgE:"⪑",lHar:"⥢",lhard:"↽",lharu:"↼",lharul:"⥪",lhblk:"â–„",LJcy:"Љ",ljcy:"Ñ™",Ll:"⋘",ll:"≪",llarr:"⇇",llcorner:"⌞",Lleftarrow:"⇚",llhard:"⥫",lltri:"â—º",Lmidot:"Ä¿",lmidot:"Å€",lmoust:"⎰",lmoustache:"⎰",lnap:"⪉",lnapprox:"⪉",lnE:"≨",lne:"⪇",lneq:"⪇",lneqq:"≨",lnsim:"⋦",loang:"⟬",loarr:"⇽",lobrk:"⟦",LongLeftArrow:"⟵",Longleftarrow:"⟸",longleftarrow:"⟵",LongLeftRightArrow:"⟷",Longleftrightarrow:"⟺",longleftrightarrow:"⟷",longmapsto:"⟼",LongRightArrow:"⟶",Longrightarrow:"⟹",longrightarrow:"⟶",looparrowleft:"↫",looparrowright:"↬",lopar:"⦅",Lopf:"ð•ƒ",lopf:"ð•",loplus:"⨭",lotimes:"⨴",lowast:"∗",lowbar:"_",LowerLeftArrow:"↙",LowerRightArrow:"↘",loz:"â—Š",lozenge:"â—Š",lozf:"â§«",lpar:"(",lparlt:"⦓",lrarr:"⇆",lrcorner:"⌟",lrhar:"⇋",lrhard:"⥭",lrm:"‎",lrtri:"⊿",lsaquo:"‹",Lscr:"â„’",lscr:"ð“",Lsh:"↰",lsh:"↰",lsim:"≲",lsime:"âª",lsimg:"âª",lsqb:"[",lsquo:"‘",lsquor:"‚",Lstrok:"Å",lstrok:"Å‚",LT:"<",Lt:"≪",lt:"<",ltcc:"⪦",ltcir:"⩹",ltdot:"â‹–",lthree:"â‹‹",ltimes:"⋉",ltlarr:"⥶",ltquest:"â©»",ltri:"â—ƒ",ltrie:"⊴",ltrif:"â—‚",ltrPar:"⦖",lurdshar:"⥊",luruhar:"⥦",lvertneqq:"≨︀",lvnE:"≨︀",macr:"¯",male:"♂",malt:"✠",maltese:"✠",Map:"⤅",map:"↦",mapsto:"↦",mapstodown:"↧",mapstoleft:"↤",mapstoup:"↥",marker:"â–®",mcomma:"⨩",Mcy:"М",mcy:"м",mdash:"—",mDDot:"∺",measuredangle:"∡",MediumSpace:"âŸ",Mellintrf:"ℳ",Mfr:"ð”",mfr:"ð”ª",mho:"â„§",micro:"µ",mid:"∣",midast:"*",midcir:"â«°",middot:"·",minus:"−",minusb:"⊟",minusd:"∸",minusdu:"⨪",MinusPlus:"∓",mlcp:"â«›",mldr:"…",mnplus:"∓",models:"⊧",Mopf:"ð•„",mopf:"ð•ž",mp:"∓",Mscr:"ℳ",mscr:"ð“‚",mstpos:"∾",Mu:"Μ",mu:"μ",multimap:"⊸",mumap:"⊸",nabla:"∇",Nacute:"Ń",nacute:"Å„",nang:"∠⃒",nap:"≉",napE:"⩰̸",napid:"≋̸",napos:"ʼn",napprox:"≉",natur:"â™®",natural:"â™®",naturals:"â„•",nbsp:" ",nbump:"≎̸",nbumpe:"â‰Ì¸",ncap:"⩃",Ncaron:"Ň",ncaron:"ň",Ncedil:"Å…",ncedil:"ņ",ncong:"≇",ncongdot:"⩭̸",ncup:"â©‚",Ncy:"Ð",ncy:"н",ndash:"–",ne:"≠",nearhk:"⤤",neArr:"⇗",nearr:"↗",nearrow:"↗",nedot:"â‰Ì¸",NegativeMediumSpace:"​",NegativeThickSpace:"​",NegativeThinSpace:"​",NegativeVeryThinSpace:"​",nequiv:"≢",nesear:"⤨",nesim:"≂̸",NestedGreaterGreater:"≫",NestedLessLess:"≪",NewLine:"\n",nexist:"∄",nexists:"∄",Nfr:"ð”‘",nfr:"ð”«",ngE:"≧̸",nge:"≱",ngeq:"≱",ngeqq:"≧̸",ngeqslant:"⩾̸",nges:"⩾̸",nGg:"⋙̸",ngsim:"≵",nGt:"≫⃒",ngt:"≯",ngtr:"≯",nGtv:"≫̸",nhArr:"⇎",nharr:"↮",nhpar:"⫲",ni:"∋",nis:"⋼",nisd:"⋺",niv:"∋",NJcy:"Њ",njcy:"Ñš",nlArr:"â‡",nlarr:"↚",nldr:"‥",nlE:"≦̸",nle:"≰",nLeftarrow:"â‡",nleftarrow:"↚",nLeftrightarrow:"⇎",nleftrightarrow:"↮",nleq:"≰",nleqq:"≦̸",nleqslant:"⩽̸",nles:"⩽̸",nless:"≮",nLl:"⋘̸",nlsim:"≴",nLt:"≪⃒",nlt:"≮",nltri:"⋪",nltrie:"⋬",nLtv:"≪̸",nmid:"∤",NoBreak:"â ",NonBreakingSpace:" ",Nopf:"â„•",nopf:"ð•Ÿ",Not:"⫬",not:"¬",NotCongruent:"≢",NotCupCap:"≭",NotDoubleVerticalBar:"∦",NotElement:"∉",NotEqual:"≠",NotEqualTilde:"≂̸",NotExists:"∄",NotGreater:"≯",NotGreaterEqual:"≱",NotGreaterFullEqual:"≧̸",NotGreaterGreater:"≫̸",NotGreaterLess:"≹",NotGreaterSlantEqual:"⩾̸",NotGreaterTilde:"≵",NotHumpDownHump:"≎̸",NotHumpEqual:"â‰Ì¸",notin:"∉",notindot:"⋵̸",notinE:"⋹̸",notinva:"∉",notinvb:"â‹·",notinvc:"â‹¶",NotLeftTriangle:"⋪",NotLeftTriangleBar:"â§Ì¸",NotLeftTriangleEqual:"⋬",NotLess:"≮",NotLessEqual:"≰",NotLessGreater:"≸",NotLessLess:"≪̸",NotLessSlantEqual:"⩽̸",NotLessTilde:"≴",NotNestedGreaterGreater:"⪢̸",NotNestedLessLess:"⪡̸",notni:"∌",notniva:"∌",notnivb:"⋾",notnivc:"⋽",NotPrecedes:"⊀",NotPrecedesEqual:"⪯̸",NotPrecedesSlantEqual:"â‹ ",NotReverseElement:"∌",NotRightTriangle:"â‹«",NotRightTriangleBar:"â§Ì¸",NotRightTriangleEqual:"â‹­",NotSquareSubset:"âŠÌ¸",NotSquareSubsetEqual:"â‹¢",NotSquareSuperset:"âŠÌ¸",NotSquareSupersetEqual:"â‹£",NotSubset:"⊂⃒",NotSubsetEqual:"⊈",NotSucceeds:"âŠ",NotSucceedsEqual:"⪰̸",NotSucceedsSlantEqual:"â‹¡",NotSucceedsTilde:"≿̸",NotSuperset:"⊃⃒",NotSupersetEqual:"⊉",NotTilde:"â‰",NotTildeEqual:"≄",NotTildeFullEqual:"≇",NotTildeTilde:"≉",NotVerticalBar:"∤",npar:"∦",nparallel:"∦",nparsl:"⫽⃥",npart:"∂̸",npolint:"⨔",npr:"⊀",nprcue:"â‹ ",npre:"⪯̸",nprec:"⊀",npreceq:"⪯̸",nrArr:"â‡",nrarr:"↛",nrarrc:"⤳̸",nrarrw:"â†Ì¸",nRightarrow:"â‡",nrightarrow:"↛",nrtri:"â‹«",nrtrie:"â‹­",nsc:"âŠ",nsccue:"â‹¡",nsce:"⪰̸",Nscr:"ð’©",nscr:"ð“ƒ",nshortmid:"∤",nshortparallel:"∦",nsim:"â‰",nsime:"≄",nsimeq:"≄",nsmid:"∤",nspar:"∦",nsqsube:"â‹¢",nsqsupe:"â‹£",nsub:"⊄",nsubE:"⫅̸",nsube:"⊈",nsubset:"⊂⃒",nsubseteq:"⊈",nsubseteqq:"⫅̸",nsucc:"âŠ",nsucceq:"⪰̸",nsup:"⊅",nsupE:"⫆̸",nsupe:"⊉",nsupset:"⊃⃒",nsupseteq:"⊉",nsupseteqq:"⫆̸",ntgl:"≹",Ntilde:"Ñ",ntilde:"ñ",ntlg:"≸",ntriangleleft:"⋪",ntrianglelefteq:"⋬",ntriangleright:"â‹«",ntrianglerighteq:"â‹­",Nu:"Î",nu:"ν",num:"#",numero:"â„–",numsp:" ",nvap:"â‰âƒ’",nVDash:"⊯",nVdash:"⊮",nvDash:"⊭",nvdash:"⊬",nvge:"≥⃒",nvgt:">⃒",nvHarr:"⤄",nvinfin:"â§ž",nvlArr:"⤂",nvle:"≤⃒",nvlt:"<⃒",nvltrie:"⊴⃒",nvrArr:"⤃",nvrtrie:"⊵⃒",nvsim:"∼⃒",nwarhk:"⤣",nwArr:"⇖",nwarr:"↖",nwarrow:"↖",nwnear:"⤧",Oacute:"Ó",oacute:"ó",oast:"⊛",ocir:"⊚",Ocirc:"Ô",ocirc:"ô",Ocy:"О",ocy:"о",odash:"âŠ",Odblac:"Å",odblac:"Å‘",odiv:"⨸",odot:"⊙",odsold:"⦼",OElig:"Å’",oelig:"Å“",ofcir:"⦿",Ofr:"ð”’",ofr:"ð”¬",ogon:"Ë›",Ograve:"Ã’",ograve:"ò",ogt:"â§",ohbar:"⦵",ohm:"Ω",oint:"∮",olarr:"↺",olcir:"⦾",olcross:"⦻",oline:"‾",olt:"â§€",Omacr:"ÅŒ",omacr:"Å",Omega:"Ω",omega:"ω",Omicron:"Ο",omicron:"ο",omid:"⦶",ominus:"⊖",Oopf:"ð•†",oopf:"ð• ",opar:"⦷",OpenCurlyDoubleQuote:"“",OpenCurlyQuote:"‘",operp:"⦹",oplus:"⊕",Or:"â©”",or:"∨",orarr:"↻",ord:"â©",order:"â„´",orderof:"â„´",ordf:"ª",ordm:"º",origof:"⊶",oror:"â©–",orslope:"â©—",orv:"â©›",oS:"Ⓢ",Oscr:"ð’ª",oscr:"â„´",Oslash:"Ø",oslash:"ø",osol:"⊘",Otilde:"Õ",otilde:"õ",Otimes:"⨷",otimes:"⊗",otimesas:"⨶",Ouml:"Ö",ouml:"ö",ovbar:"⌽",OverBar:"‾",OverBrace:"âž",OverBracket:"⎴",OverParenthesis:"âœ",par:"∥",para:"¶",parallel:"∥",parsim:"⫳",parsl:"⫽",part:"∂",PartialD:"∂",Pcy:"П",pcy:"п",percnt:"%",period:".",permil:"‰",perp:"⊥",pertenk:"‱",Pfr:"ð”“",pfr:"ð”­",Phi:"Φ",phi:"φ",phiv:"Ï•",phmmat:"ℳ",phone:"☎",Pi:"Π",pi:"Ï€",pitchfork:"â‹”",piv:"Ï–",planck:"â„",planckh:"ℎ",plankv:"â„",plus:"+",plusacir:"⨣",plusb:"⊞",pluscir:"⨢",plusdo:"∔",plusdu:"⨥",pluse:"⩲",PlusMinus:"±",plusmn:"±",plussim:"⨦",plustwo:"⨧",pm:"±",Poincareplane:"ℌ",pointint:"⨕",Popf:"â„™",popf:"ð•¡",pound:"£",Pr:"⪻",pr:"≺",prap:"⪷",prcue:"≼",prE:"⪳",pre:"⪯",prec:"≺",precapprox:"⪷",preccurlyeq:"≼",Precedes:"≺",PrecedesEqual:"⪯",PrecedesSlantEqual:"≼",PrecedesTilde:"≾",preceq:"⪯",precnapprox:"⪹",precneqq:"⪵",precnsim:"⋨",precsim:"≾",Prime:"″",prime:"′",primes:"â„™",prnap:"⪹",prnE:"⪵",prnsim:"⋨",prod:"âˆ",Product:"âˆ",profalar:"⌮",profline:"⌒",profsurf:"⌓",prop:"âˆ",Proportion:"∷",Proportional:"âˆ",propto:"âˆ",prsim:"≾",prurel:"⊰",Pscr:"ð’«",pscr:"ð“…",Psi:"Ψ",psi:"ψ",puncsp:" ",Qfr:"ð””",qfr:"ð”®",qint:"⨌",Qopf:"ℚ",qopf:"ð•¢",qprime:"â—",Qscr:"ð’¬",qscr:"ð“†",quaternions:"â„",quatint:"⨖",quest:"?",questeq:"≟",QUOT:'"',quot:'"',rAarr:"⇛",race:"∽̱",Racute:"Å”",racute:"Å•",radic:"√",raemptyv:"⦳",Rang:"⟫",rang:"⟩",rangd:"⦒",range:"⦥",rangle:"⟩",raquo:"»",Rarr:"↠",rArr:"⇒",rarr:"→",rarrap:"⥵",rarrb:"⇥",rarrbfs:"⤠",rarrc:"⤳",rarrfs:"⤞",rarrhk:"↪",rarrlp:"↬",rarrpl:"⥅",rarrsim:"⥴",Rarrtl:"⤖",rarrtl:"↣",rarrw:"â†",rAtail:"⤜",ratail:"⤚",ratio:"∶",rationals:"ℚ",RBarr:"â¤",rBarr:"â¤",rbarr:"â¤",rbbrk:"â³",rbrace:"}",rbrack:"]",rbrke:"⦌",rbrksld:"⦎",rbrkslu:"â¦",Rcaron:"Ř",rcaron:"Å™",Rcedil:"Å–",rcedil:"Å—",rceil:"⌉",rcub:"}",Rcy:"Р",rcy:"Ñ€",rdca:"⤷",rdldhar:"⥩",rdquo:"â€",rdquor:"â€",rdsh:"↳",Re:"ℜ",real:"ℜ",realine:"â„›",realpart:"ℜ",reals:"â„",rect:"â–­",REG:"®",reg:"®",ReverseElement:"∋",ReverseEquilibrium:"⇋",ReverseUpEquilibrium:"⥯",rfisht:"⥽",rfloor:"⌋",Rfr:"ℜ",rfr:"ð”¯",rHar:"⥤",rhard:"â‡",rharu:"⇀",rharul:"⥬",Rho:"Ρ",rho:"Ï",rhov:"ϱ",RightAngleBracket:"⟩",RightArrow:"→",Rightarrow:"⇒",rightarrow:"→",RightArrowBar:"⇥",RightArrowLeftArrow:"⇄",rightarrowtail:"↣",RightCeiling:"⌉",RightDoubleBracket:"⟧",RightDownTeeVector:"â¥",RightDownVector:"⇂",RightDownVectorBar:"⥕",RightFloor:"⌋",rightharpoondown:"â‡",rightharpoonup:"⇀",rightleftarrows:"⇄",rightleftharpoons:"⇌",rightrightarrows:"⇉",rightsquigarrow:"â†",RightTee:"⊢",RightTeeArrow:"↦",RightTeeVector:"⥛",rightthreetimes:"⋌",RightTriangle:"⊳",RightTriangleBar:"â§",RightTriangleEqual:"⊵",RightUpDownVector:"â¥",RightUpTeeVector:"⥜",RightUpVector:"↾",RightUpVectorBar:"⥔",RightVector:"⇀",RightVectorBar:"⥓",ring:"Ëš",risingdotseq:"≓",rlarr:"⇄",rlhar:"⇌",rlm:"â€",rmoust:"⎱",rmoustache:"⎱",rnmid:"â«®",roang:"⟭",roarr:"⇾",robrk:"⟧",ropar:"⦆",Ropf:"â„",ropf:"ð•£",roplus:"⨮",rotimes:"⨵",RoundImplies:"⥰",rpar:")",rpargt:"⦔",rppolint:"⨒",rrarr:"⇉",Rrightarrow:"⇛",rsaquo:"›",Rscr:"â„›",rscr:"ð“‡",Rsh:"↱",rsh:"↱",rsqb:"]",rsquo:"’",rsquor:"’",rthree:"⋌",rtimes:"⋊",rtri:"â–¹",rtrie:"⊵",rtrif:"â–¸",rtriltri:"â§Ž",RuleDelayed:"â§´",ruluhar:"⥨",rx:"℞",Sacute:"Åš",sacute:"Å›",sbquo:"‚",Sc:"⪼",sc:"≻",scap:"⪸",Scaron:"Å ",scaron:"Å¡",sccue:"≽",scE:"⪴",sce:"⪰",Scedil:"Åž",scedil:"ÅŸ",Scirc:"Åœ",scirc:"Å",scnap:"⪺",scnE:"⪶",scnsim:"â‹©",scpolint:"⨓",scsim:"≿",Scy:"С",scy:"Ñ",sdot:"â‹…",sdotb:"⊡",sdote:"⩦",searhk:"⤥",seArr:"⇘",searr:"↘",searrow:"↘",sect:"§",semi:";",seswar:"⤩",setminus:"∖",setmn:"∖",sext:"✶",Sfr:"ð”–",sfr:"ð”°",sfrown:"⌢",sharp:"♯",SHCHcy:"Щ",shchcy:"щ",SHcy:"Ш",shcy:"ш",ShortDownArrow:"↓",ShortLeftArrow:"â†",shortmid:"∣",shortparallel:"∥",ShortRightArrow:"→",ShortUpArrow:"↑",shy:"­",Sigma:"Σ",sigma:"σ",sigmaf:"Ï‚",sigmav:"Ï‚",sim:"∼",simdot:"⩪",sime:"≃",simeq:"≃",simg:"⪞",simgE:"⪠",siml:"âª",simlE:"⪟",simne:"≆",simplus:"⨤",simrarr:"⥲",slarr:"â†",SmallCircle:"∘",smallsetminus:"∖",smashp:"⨳",smeparsl:"⧤",smid:"∣",smile:"⌣",smt:"⪪",smte:"⪬",smtes:"⪬︀",SOFTcy:"Ь",softcy:"ÑŒ",sol:"/",solb:"â§„",solbar:"⌿",Sopf:"ð•Š",sopf:"ð•¤",spades:"â™ ",spadesuit:"â™ ",spar:"∥",sqcap:"⊓",sqcaps:"⊓︀",sqcup:"⊔",sqcups:"⊔︀",Sqrt:"√",sqsub:"âŠ",sqsube:"⊑",sqsubset:"âŠ",sqsubseteq:"⊑",sqsup:"âŠ",sqsupe:"⊒",sqsupset:"âŠ",sqsupseteq:"⊒",squ:"â–¡",Square:"â–¡",square:"â–¡",SquareIntersection:"⊓",SquareSubset:"âŠ",SquareSubsetEqual:"⊑",SquareSuperset:"âŠ",SquareSupersetEqual:"⊒",SquareUnion:"⊔",squarf:"â–ª",squf:"â–ª",srarr:"→",Sscr:"ð’®",sscr:"ð“ˆ",ssetmn:"∖",ssmile:"⌣",sstarf:"⋆",Star:"⋆",star:"☆",starf:"★",straightepsilon:"ϵ",straightphi:"Ï•",strns:"¯",Sub:"â‹",sub:"⊂",subdot:"⪽",subE:"â«…",sube:"⊆",subedot:"⫃",submult:"â«",subnE:"â«‹",subne:"⊊",subplus:"⪿",subrarr:"⥹",Subset:"â‹",subset:"⊂",subseteq:"⊆",subseteqq:"â«…",SubsetEqual:"⊆",subsetneq:"⊊",subsetneqq:"â«‹",subsim:"⫇",subsub:"â«•",subsup:"â«“",succ:"≻",succapprox:"⪸",succcurlyeq:"≽",Succeeds:"≻",SucceedsEqual:"⪰",SucceedsSlantEqual:"≽",SucceedsTilde:"≿",succeq:"⪰",succnapprox:"⪺",succneqq:"⪶",succnsim:"â‹©",succsim:"≿",SuchThat:"∋",Sum:"∑",sum:"∑",sung:"♪",Sup:"â‹‘",sup:"⊃",sup1:"¹",sup2:"²",sup3:"³",supdot:"⪾",supdsub:"⫘",supE:"⫆",supe:"⊇",supedot:"â«„",Superset:"⊃",SupersetEqual:"⊇",suphsol:"⟉",suphsub:"â«—",suplarr:"⥻",supmult:"â«‚",supnE:"⫌",supne:"⊋",supplus:"â«€",Supset:"â‹‘",supset:"⊃",supseteq:"⊇",supseteqq:"⫆",supsetneq:"⊋",supsetneqq:"⫌",supsim:"⫈",supsub:"â«”",supsup:"â«–",swarhk:"⤦",swArr:"⇙",swarr:"↙",swarrow:"↙",swnwar:"⤪",szlig:"ß",Tab:"\t",target:"⌖",Tau:"Τ",tau:"Ï„",tbrk:"⎴",Tcaron:"Ť",tcaron:"Å¥",Tcedil:"Å¢",tcedil:"Å£",Tcy:"Т",tcy:"Ñ‚",tdot:"⃛",telrec:"⌕",Tfr:"ð”—",tfr:"ð”±",there4:"∴",Therefore:"∴",therefore:"∴",Theta:"Θ",theta:"θ",thetasym:"Ï‘",thetav:"Ï‘",thickapprox:"≈",thicksim:"∼",ThickSpace:"âŸâ€Š",thinsp:" ",ThinSpace:" ",thkap:"≈",thksim:"∼",THORN:"Þ",thorn:"þ",Tilde:"∼",tilde:"Ëœ",TildeEqual:"≃",TildeFullEqual:"≅",TildeTilde:"≈",times:"×",timesb:"⊠",timesbar:"⨱",timesd:"⨰",tint:"∭",toea:"⤨",top:"⊤",topbot:"⌶",topcir:"⫱",Topf:"ð•‹",topf:"ð•¥",topfork:"⫚",tosa:"⤩",tprime:"‴",TRADE:"â„¢",trade:"â„¢",triangle:"â–µ",triangledown:"â–¿",triangleleft:"â—ƒ",trianglelefteq:"⊴",triangleq:"≜",triangleright:"â–¹",trianglerighteq:"⊵",tridot:"â—¬",trie:"≜",triminus:"⨺",TripleDot:"⃛",triplus:"⨹",trisb:"â§",tritime:"⨻",trpezium:"â¢",Tscr:"ð’¯",tscr:"ð“‰",TScy:"Ц",tscy:"ц",TSHcy:"Ћ",tshcy:"Ñ›",Tstrok:"Ŧ",tstrok:"ŧ",twixt:"≬",twoheadleftarrow:"↞",twoheadrightarrow:"↠",Uacute:"Ú",uacute:"ú",Uarr:"↟",uArr:"⇑",uarr:"↑",Uarrocir:"⥉",Ubrcy:"ÐŽ",ubrcy:"Ñž",Ubreve:"Ŭ",ubreve:"Å­",Ucirc:"Û",ucirc:"û",Ucy:"У",ucy:"у",udarr:"⇅",Udblac:"Ű",udblac:"ű",udhar:"⥮",ufisht:"⥾",Ufr:"ð”˜",ufr:"ð”²",Ugrave:"Ù",ugrave:"ù",uHar:"⥣",uharl:"↿",uharr:"↾",uhblk:"â–€",ulcorn:"⌜",ulcorner:"⌜",ulcrop:"âŒ",ultri:"â—¸",Umacr:"Ū",umacr:"Å«",uml:"¨",UnderBar:"_",UnderBrace:"âŸ",UnderBracket:"⎵",UnderParenthesis:"â",Union:"⋃",UnionPlus:"⊎",Uogon:"Ų",uogon:"ų",Uopf:"ð•Œ",uopf:"ð•¦",UpArrow:"↑",Uparrow:"⇑",uparrow:"↑",UpArrowBar:"⤒",UpArrowDownArrow:"⇅",UpDownArrow:"↕",Updownarrow:"⇕",updownarrow:"↕",UpEquilibrium:"⥮",upharpoonleft:"↿",upharpoonright:"↾",uplus:"⊎",UpperLeftArrow:"↖",UpperRightArrow:"↗",Upsi:"Ï’",upsi:"Ï…",upsih:"Ï’",Upsilon:"Î¥",upsilon:"Ï…",UpTee:"⊥",UpTeeArrow:"↥",upuparrows:"⇈",urcorn:"âŒ",urcorner:"âŒ",urcrop:"⌎",Uring:"Å®",uring:"ů",urtri:"â—¹",Uscr:"ð’°",uscr:"ð“Š",utdot:"â‹°",Utilde:"Ũ",utilde:"Å©",utri:"â–µ",utrif:"â–´",uuarr:"⇈",Uuml:"Ü",uuml:"ü",uwangle:"⦧",vangrt:"⦜",varepsilon:"ϵ",varkappa:"ϰ",varnothing:"∅",varphi:"Ï•",varpi:"Ï–",varpropto:"âˆ",vArr:"⇕",varr:"↕",varrho:"ϱ",varsigma:"Ï‚",varsubsetneq:"⊊︀",varsubsetneqq:"⫋︀",varsupsetneq:"⊋︀",varsupsetneqq:"⫌︀",vartheta:"Ï‘",vartriangleleft:"⊲",vartriangleright:"⊳",Vbar:"â««",vBar:"⫨",vBarv:"â«©",Vcy:"Ð’",vcy:"в",VDash:"⊫",Vdash:"⊩",vDash:"⊨",vdash:"⊢",Vdashl:"⫦",Vee:"â‹",vee:"∨",veebar:"⊻",veeeq:"≚",vellip:"â‹®",Verbar:"‖",verbar:"|",Vert:"‖",vert:"|",VerticalBar:"∣",VerticalLine:"|",VerticalSeparator:"â˜",VerticalTilde:"≀",VeryThinSpace:" ",Vfr:"ð”™",vfr:"ð”³",vltri:"⊲",vnsub:"⊂⃒",vnsup:"⊃⃒",Vopf:"ð•",vopf:"ð•§",vprop:"âˆ",vrtri:"⊳",Vscr:"ð’±",vscr:"ð“‹",vsubnE:"⫋︀",vsubne:"⊊︀",vsupnE:"⫌︀",vsupne:"⊋︀",Vvdash:"⊪",vzigzag:"⦚",Wcirc:"Å´",wcirc:"ŵ",wedbar:"⩟",Wedge:"â‹€",wedge:"∧",wedgeq:"≙",weierp:"℘",Wfr:"ð”š",wfr:"ð”´",Wopf:"ð•Ž",wopf:"ð•¨",wp:"℘",wr:"≀",wreath:"≀",Wscr:"ð’²",wscr:"ð“Œ",xcap:"â‹‚",xcirc:"â—¯",xcup:"⋃",xdtri:"â–½",Xfr:"ð”›",xfr:"ð”µ",xhArr:"⟺",xharr:"⟷",Xi:"Ξ",xi:"ξ",xlArr:"⟸",xlarr:"⟵",xmap:"⟼",xnis:"â‹»",xodot:"⨀",Xopf:"ð•",xopf:"ð•©",xoplus:"â¨",xotime:"⨂",xrArr:"⟹",xrarr:"⟶",Xscr:"ð’³",xscr:"ð“",xsqcup:"⨆",xuplus:"⨄",xutri:"â–³",xvee:"â‹",xwedge:"â‹€",Yacute:"Ã",yacute:"ý",YAcy:"Я",yacy:"Ñ",Ycirc:"Ŷ",ycirc:"Å·",Ycy:"Ы",ycy:"Ñ‹",yen:"Â¥",Yfr:"ð”œ",yfr:"ð”¶",YIcy:"Ї",yicy:"Ñ—",Yopf:"ð•",yopf:"ð•ª",Yscr:"ð’´",yscr:"ð“Ž",YUcy:"Ю",yucy:"ÑŽ",Yuml:"Ÿ",yuml:"ÿ",Zacute:"Ź",zacute:"ź",Zcaron:"Ž",zcaron:"ž",Zcy:"З",zcy:"з",Zdot:"Å»",zdot:"ż",zeetrf:"ℨ",ZeroWidthSpace:"​",Zeta:"Ζ",zeta:"ζ",Zfr:"ℨ",zfr:"ð”·",ZHcy:"Ж",zhcy:"ж",zigrarr:"â‡",Zopf:"ℤ",zopf:"ð•«",Zscr:"ð’µ",zscr:"ð“",zwj:"â€",zwnj:"‌"}},function(e,t,r){"use strict";var n=r(458),i=r(28).unescapeMd;e.exports=function parseLinkDestination(e,t){var r,o,a,s=t,u=e.posMax;if(60===e.src.charCodeAt(t)){for(t++;t8&&r<14);)if(92===r&&t+11)break;if(41===r&&--o<0)break;t++}return s!==t&&(a=i(e.src.slice(s,t)),!!e.parser.validateLink(a)&&(e.linkContent=a,e.pos=t,!0))}},function(e,t,r){"use strict";var n=r(28).replaceEntities;e.exports=function normalizeLink(e){var t=n(e);try{t=decodeURI(t)}catch(e){}return encodeURI(t)}},function(e,t,r){"use strict";var n=r(28).unescapeMd;e.exports=function parseLinkTitle(e,t){var r,i=t,o=e.posMax,a=e.src.charCodeAt(t);if(34!==a&&39!==a&&40!==a)return!1;for(t++,40===a&&(a=41);t1?i-1:0),a=1;a1?r-1:0),i=1;i0?Array(e+1).join(" ")+t:t}).join("\n")}(0,(0,n.default)(a,null,2))||"{}",c.default.createElement("br",null)))}}]),OperationLink}(l.Component);d.propTypes={getComponent:p.default.func.isRequired,link:f.default.orderedMap.isRequired,name:p.default.String},t.default=d},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=_interopRequireDefault(r(4)),i=_interopRequireDefault(r(2)),o=_interopRequireDefault(r(3)),a=_interopRequireDefault(r(5)),s=_interopRequireDefault(r(6)),u=_interopRequireDefault(r(0)),l=r(7),c=_interopRequireDefault(r(1)),p=_interopRequireDefault(r(12));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}var f=function(e){function Servers(){var e,t,r,o;(0,i.default)(this,Servers);for(var s=arguments.length,u=Array(s),l=0;l=55296&&a<=57343){if(a>=55296&&a<=56319&&i+1=56320&&s<=57343){l+=encodeURIComponent(e[i]+e[i+1]),i++;continue}l+="%EF%BF%BD"}else l+=encodeURIComponent(e[i]);return l}encode.defaultChars=";/?:@&=+$,-_.!~*'()#",encode.componentChars="-_.!~*'()",e.exports=encode},function(e,t,r){"use strict";var n={};function decode(e,t){var r;return"string"!=typeof t&&(t=decode.defaultChars),r=function getDecodeCache(e){var t,r,i=n[e];if(i)return i;for(i=n[e]=[],t=0;t<128;t++)r=String.fromCharCode(t),i.push(r);for(t=0;t=55296&&u<=57343?"���":String.fromCharCode(u),t+=6):240==(248&i)&&t+91114111?l+="����":(u-=65536,l+=String.fromCharCode(55296+(u>>10),56320+(1023&u))),t+=9):l+="�";return l})}decode.defaultChars=";/?:@&=+$,#",decode.componentChars="",e.exports=decode},function(e,t){e.exports={amp:"&",apos:"'",gt:">",lt:"<",quot:'"'}},function(e,t){e.exports={Aacute:"Ã",aacute:"á",Abreve:"Ä‚",abreve:"ă",ac:"∾",acd:"∿",acE:"∾̳",Acirc:"Â",acirc:"â",acute:"´",Acy:"Ð",acy:"а",AElig:"Æ",aelig:"æ",af:"â¡",Afr:"ð”„",afr:"ð”ž",Agrave:"À",agrave:"à",alefsym:"ℵ",aleph:"ℵ",Alpha:"Α",alpha:"α",Amacr:"Ä€",amacr:"Ä",amalg:"⨿",amp:"&",AMP:"&",andand:"â©•",And:"â©“",and:"∧",andd:"⩜",andslope:"⩘",andv:"⩚",ang:"∠",ange:"⦤",angle:"∠",angmsdaa:"⦨",angmsdab:"⦩",angmsdac:"⦪",angmsdad:"⦫",angmsdae:"⦬",angmsdaf:"⦭",angmsdag:"⦮",angmsdah:"⦯",angmsd:"∡",angrt:"∟",angrtvb:"⊾",angrtvbd:"â¦",angsph:"∢",angst:"Ã…",angzarr:"â¼",Aogon:"Ä„",aogon:"Ä…",Aopf:"ð”¸",aopf:"ð•’",apacir:"⩯",ap:"≈",apE:"â©°",ape:"≊",apid:"≋",apos:"'",ApplyFunction:"â¡",approx:"≈",approxeq:"≊",Aring:"Ã…",aring:"Ã¥",Ascr:"ð’œ",ascr:"ð’¶",Assign:"≔",ast:"*",asymp:"≈",asympeq:"â‰",Atilde:"Ã",atilde:"ã",Auml:"Ä",auml:"ä",awconint:"∳",awint:"⨑",backcong:"≌",backepsilon:"϶",backprime:"‵",backsim:"∽",backsimeq:"â‹",Backslash:"∖",Barv:"â«§",barvee:"⊽",barwed:"⌅",Barwed:"⌆",barwedge:"⌅",bbrk:"⎵",bbrktbrk:"⎶",bcong:"≌",Bcy:"Б",bcy:"б",bdquo:"„",becaus:"∵",because:"∵",Because:"∵",bemptyv:"⦰",bepsi:"϶",bernou:"ℬ",Bernoullis:"ℬ",Beta:"Î’",beta:"β",beth:"â„¶",between:"≬",Bfr:"ð”…",bfr:"ð”Ÿ",bigcap:"â‹‚",bigcirc:"â—¯",bigcup:"⋃",bigodot:"⨀",bigoplus:"â¨",bigotimes:"⨂",bigsqcup:"⨆",bigstar:"★",bigtriangledown:"â–½",bigtriangleup:"â–³",biguplus:"⨄",bigvee:"â‹",bigwedge:"â‹€",bkarow:"â¤",blacklozenge:"â§«",blacksquare:"â–ª",blacktriangle:"â–´",blacktriangledown:"â–¾",blacktriangleleft:"â—‚",blacktriangleright:"â–¸",blank:"â£",blk12:"â–’",blk14:"â–‘",blk34:"â–“",block:"â–ˆ",bne:"=⃥",bnequiv:"≡⃥",bNot:"â«­",bnot:"âŒ",Bopf:"ð”¹",bopf:"ð•“",bot:"⊥",bottom:"⊥",bowtie:"⋈",boxbox:"⧉",boxdl:"â”",boxdL:"â••",boxDl:"â•–",boxDL:"â•—",boxdr:"┌",boxdR:"â•’",boxDr:"â•“",boxDR:"â•”",boxh:"─",boxH:"â•",boxhd:"┬",boxHd:"╤",boxhD:"â•¥",boxHD:"╦",boxhu:"â”´",boxHu:"â•§",boxhU:"╨",boxHU:"â•©",boxminus:"⊟",boxplus:"⊞",boxtimes:"⊠",boxul:"┘",boxuL:"â•›",boxUl:"╜",boxUL:"â•",boxur:"â””",boxuR:"╘",boxUr:"â•™",boxUR:"╚",boxv:"│",boxV:"â•‘",boxvh:"┼",boxvH:"╪",boxVh:"â•«",boxVH:"╬",boxvl:"┤",boxvL:"â•¡",boxVl:"â•¢",boxVL:"â•£",boxvr:"├",boxvR:"╞",boxVr:"╟",boxVR:"â• ",bprime:"‵",breve:"˘",Breve:"˘",brvbar:"¦",bscr:"ð’·",Bscr:"ℬ",bsemi:"â",bsim:"∽",bsime:"â‹",bsolb:"â§…",bsol:"\\",bsolhsub:"⟈",bull:"•",bullet:"•",bump:"≎",bumpE:"⪮",bumpe:"â‰",Bumpeq:"≎",bumpeq:"â‰",Cacute:"Ć",cacute:"ć",capand:"â©„",capbrcup:"⩉",capcap:"â©‹",cap:"∩",Cap:"â‹’",capcup:"⩇",capdot:"â©€",CapitalDifferentialD:"â……",caps:"∩︀",caret:"â",caron:"ˇ",Cayleys:"â„­",ccaps:"â©",Ccaron:"ÄŒ",ccaron:"Ä",Ccedil:"Ç",ccedil:"ç",Ccirc:"Ĉ",ccirc:"ĉ",Cconint:"∰",ccups:"⩌",ccupssm:"â©",Cdot:"ÄŠ",cdot:"Ä‹",cedil:"¸",Cedilla:"¸",cemptyv:"⦲",cent:"¢",centerdot:"·",CenterDot:"·",cfr:"ð” ",Cfr:"â„­",CHcy:"Ч",chcy:"ч",check:"✓",checkmark:"✓",Chi:"Χ",chi:"χ",circ:"ˆ",circeq:"≗",circlearrowleft:"↺",circlearrowright:"↻",circledast:"⊛",circledcirc:"⊚",circleddash:"âŠ",CircleDot:"⊙",circledR:"®",circledS:"Ⓢ",CircleMinus:"⊖",CirclePlus:"⊕",CircleTimes:"⊗",cir:"â—‹",cirE:"⧃",cire:"≗",cirfnint:"â¨",cirmid:"⫯",cirscir:"â§‚",ClockwiseContourIntegral:"∲",CloseCurlyDoubleQuote:"â€",CloseCurlyQuote:"’",clubs:"♣",clubsuit:"♣",colon:":",Colon:"∷",Colone:"â©´",colone:"≔",coloneq:"≔",comma:",",commat:"@",comp:"âˆ",compfn:"∘",complement:"âˆ",complexes:"â„‚",cong:"≅",congdot:"â©­",Congruent:"≡",conint:"∮",Conint:"∯",ContourIntegral:"∮",copf:"ð•”",Copf:"â„‚",coprod:"âˆ",Coproduct:"âˆ",copy:"©",COPY:"©",copysr:"â„—",CounterClockwiseContourIntegral:"∳",crarr:"↵",cross:"✗",Cross:"⨯",Cscr:"ð’ž",cscr:"ð’¸",csub:"â«",csube:"â«‘",csup:"â«",csupe:"â«’",ctdot:"⋯",cudarrl:"⤸",cudarrr:"⤵",cuepr:"⋞",cuesc:"⋟",cularr:"↶",cularrp:"⤽",cupbrcap:"⩈",cupcap:"⩆",CupCap:"â‰",cup:"∪",Cup:"â‹“",cupcup:"⩊",cupdot:"âŠ",cupor:"â©…",cups:"∪︀",curarr:"↷",curarrm:"⤼",curlyeqprec:"⋞",curlyeqsucc:"⋟",curlyvee:"⋎",curlywedge:"â‹",curren:"¤",curvearrowleft:"↶",curvearrowright:"↷",cuvee:"⋎",cuwed:"â‹",cwconint:"∲",cwint:"∱",cylcty:"⌭",dagger:"†",Dagger:"‡",daleth:"ℸ",darr:"↓",Darr:"↡",dArr:"⇓",dash:"â€",Dashv:"⫤",dashv:"⊣",dbkarow:"â¤",dblac:"Ë",Dcaron:"ÄŽ",dcaron:"Ä",Dcy:"Д",dcy:"д",ddagger:"‡",ddarr:"⇊",DD:"â……",dd:"â…†",DDotrahd:"⤑",ddotseq:"â©·",deg:"°",Del:"∇",Delta:"Δ",delta:"δ",demptyv:"⦱",dfisht:"⥿",Dfr:"ð”‡",dfr:"ð”¡",dHar:"⥥",dharl:"⇃",dharr:"⇂",DiacriticalAcute:"´",DiacriticalDot:"Ë™",DiacriticalDoubleAcute:"Ë",DiacriticalGrave:"`",DiacriticalTilde:"Ëœ",diam:"â‹„",diamond:"â‹„",Diamond:"â‹„",diamondsuit:"♦",diams:"♦",die:"¨",DifferentialD:"â…†",digamma:"Ï",disin:"⋲",div:"÷",divide:"÷",divideontimes:"⋇",divonx:"⋇",DJcy:"Ђ",djcy:"Ñ’",dlcorn:"⌞",dlcrop:"âŒ",dollar:"$",Dopf:"ð”»",dopf:"ð••",Dot:"¨",dot:"Ë™",DotDot:"⃜",doteq:"â‰",doteqdot:"≑",DotEqual:"â‰",dotminus:"∸",dotplus:"∔",dotsquare:"⊡",doublebarwedge:"⌆",DoubleContourIntegral:"∯",DoubleDot:"¨",DoubleDownArrow:"⇓",DoubleLeftArrow:"â‡",DoubleLeftRightArrow:"⇔",DoubleLeftTee:"⫤",DoubleLongLeftArrow:"⟸",DoubleLongLeftRightArrow:"⟺",DoubleLongRightArrow:"⟹",DoubleRightArrow:"⇒",DoubleRightTee:"⊨",DoubleUpArrow:"⇑",DoubleUpDownArrow:"⇕",DoubleVerticalBar:"∥",DownArrowBar:"⤓",downarrow:"↓",DownArrow:"↓",Downarrow:"⇓",DownArrowUpArrow:"⇵",DownBreve:"Ì‘",downdownarrows:"⇊",downharpoonleft:"⇃",downharpoonright:"⇂",DownLeftRightVector:"â¥",DownLeftTeeVector:"⥞",DownLeftVectorBar:"⥖",DownLeftVector:"↽",DownRightTeeVector:"⥟",DownRightVectorBar:"⥗",DownRightVector:"â‡",DownTeeArrow:"↧",DownTee:"⊤",drbkarow:"â¤",drcorn:"⌟",drcrop:"⌌",Dscr:"ð’Ÿ",dscr:"ð’¹",DScy:"Ð…",dscy:"Ñ•",dsol:"â§¶",Dstrok:"Ä",dstrok:"Ä‘",dtdot:"⋱",dtri:"â–¿",dtrif:"â–¾",duarr:"⇵",duhar:"⥯",dwangle:"⦦",DZcy:"Ð",dzcy:"ÑŸ",dzigrarr:"⟿",Eacute:"É",eacute:"é",easter:"â©®",Ecaron:"Äš",ecaron:"Ä›",Ecirc:"Ê",ecirc:"ê",ecir:"≖",ecolon:"≕",Ecy:"Э",ecy:"Ñ",eDDot:"â©·",Edot:"Ä–",edot:"Ä—",eDot:"≑",ee:"â…‡",efDot:"≒",Efr:"ð”ˆ",efr:"ð”¢",eg:"⪚",Egrave:"È",egrave:"è",egs:"⪖",egsdot:"⪘",el:"⪙",Element:"∈",elinters:"â§",ell:"â„“",els:"⪕",elsdot:"⪗",Emacr:"Ä’",emacr:"Ä“",empty:"∅",emptyset:"∅",EmptySmallSquare:"â—»",emptyv:"∅",EmptyVerySmallSquare:"â–«",emsp13:" ",emsp14:" ",emsp:" ",ENG:"ÅŠ",eng:"Å‹",ensp:" ",Eogon:"Ę",eogon:"Ä™",Eopf:"ð”¼",eopf:"ð•–",epar:"â‹•",eparsl:"â§£",eplus:"⩱",epsi:"ε",Epsilon:"Ε",epsilon:"ε",epsiv:"ϵ",eqcirc:"≖",eqcolon:"≕",eqsim:"≂",eqslantgtr:"⪖",eqslantless:"⪕",Equal:"⩵",equals:"=",EqualTilde:"≂",equest:"≟",Equilibrium:"⇌",equiv:"≡",equivDD:"⩸",eqvparsl:"â§¥",erarr:"⥱",erDot:"≓",escr:"ℯ",Escr:"â„°",esdot:"â‰",Esim:"⩳",esim:"≂",Eta:"Η",eta:"η",ETH:"Ã",eth:"ð",Euml:"Ë",euml:"ë",euro:"€",excl:"!",exist:"∃",Exists:"∃",expectation:"â„°",exponentiale:"â…‡",ExponentialE:"â…‡",fallingdotseq:"≒",Fcy:"Ф",fcy:"Ñ„",female:"♀",ffilig:"ffi",fflig:"ff",ffllig:"ffl",Ffr:"ð”‰",ffr:"ð”£",filig:"ï¬",FilledSmallSquare:"â—¼",FilledVerySmallSquare:"â–ª",fjlig:"fj",flat:"â™­",fllig:"fl",fltns:"â–±",fnof:"Æ’",Fopf:"ð”½",fopf:"ð•—",forall:"∀",ForAll:"∀",fork:"â‹”",forkv:"â«™",Fouriertrf:"ℱ",fpartint:"â¨",frac12:"½",frac13:"â…“",frac14:"¼",frac15:"â…•",frac16:"â…™",frac18:"â…›",frac23:"â…”",frac25:"â…–",frac34:"¾",frac35:"â…—",frac38:"â…œ",frac45:"â…˜",frac56:"â…š",frac58:"â…",frac78:"â…ž",frasl:"â„",frown:"⌢",fscr:"ð’»",Fscr:"ℱ",gacute:"ǵ",Gamma:"Γ",gamma:"γ",Gammad:"Ïœ",gammad:"Ï",gap:"⪆",Gbreve:"Äž",gbreve:"ÄŸ",Gcedil:"Ä¢",Gcirc:"Äœ",gcirc:"Ä",Gcy:"Г",gcy:"г",Gdot:"Ä ",gdot:"Ä¡",ge:"≥",gE:"≧",gEl:"⪌",gel:"â‹›",geq:"≥",geqq:"≧",geqslant:"⩾",gescc:"⪩",ges:"⩾",gesdot:"⪀",gesdoto:"⪂",gesdotol:"⪄",gesl:"⋛︀",gesles:"⪔",Gfr:"ð”Š",gfr:"ð”¤",gg:"≫",Gg:"â‹™",ggg:"â‹™",gimel:"â„·",GJcy:"Ѓ",gjcy:"Ñ“",gla:"⪥",gl:"≷",glE:"⪒",glj:"⪤",gnap:"⪊",gnapprox:"⪊",gne:"⪈",gnE:"≩",gneq:"⪈",gneqq:"≩",gnsim:"â‹§",Gopf:"ð”¾",gopf:"ð•˜",grave:"`",GreaterEqual:"≥",GreaterEqualLess:"â‹›",GreaterFullEqual:"≧",GreaterGreater:"⪢",GreaterLess:"≷",GreaterSlantEqual:"⩾",GreaterTilde:"≳",Gscr:"ð’¢",gscr:"ℊ",gsim:"≳",gsime:"⪎",gsiml:"âª",gtcc:"⪧",gtcir:"⩺",gt:">",GT:">",Gt:"≫",gtdot:"â‹—",gtlPar:"⦕",gtquest:"⩼",gtrapprox:"⪆",gtrarr:"⥸",gtrdot:"â‹—",gtreqless:"â‹›",gtreqqless:"⪌",gtrless:"≷",gtrsim:"≳",gvertneqq:"≩︀",gvnE:"≩︀",Hacek:"ˇ",hairsp:" ",half:"½",hamilt:"â„‹",HARDcy:"Ъ",hardcy:"ÑŠ",harrcir:"⥈",harr:"↔",hArr:"⇔",harrw:"↭",Hat:"^",hbar:"â„",Hcirc:"Ĥ",hcirc:"Ä¥",hearts:"♥",heartsuit:"♥",hellip:"…",hercon:"⊹",hfr:"ð”¥",Hfr:"ℌ",HilbertSpace:"â„‹",hksearow:"⤥",hkswarow:"⤦",hoarr:"⇿",homtht:"∻",hookleftarrow:"↩",hookrightarrow:"↪",hopf:"ð•™",Hopf:"â„",horbar:"―",HorizontalLine:"─",hscr:"ð’½",Hscr:"â„‹",hslash:"â„",Hstrok:"Ħ",hstrok:"ħ",HumpDownHump:"≎",HumpEqual:"â‰",hybull:"âƒ",hyphen:"â€",Iacute:"Ã",iacute:"í",ic:"â£",Icirc:"ÃŽ",icirc:"î",Icy:"И",icy:"и",Idot:"İ",IEcy:"Е",iecy:"е",iexcl:"¡",iff:"⇔",ifr:"ð”¦",Ifr:"â„‘",Igrave:"ÃŒ",igrave:"ì",ii:"â…ˆ",iiiint:"⨌",iiint:"∭",iinfin:"â§œ",iiota:"â„©",IJlig:"IJ",ijlig:"ij",Imacr:"Ī",imacr:"Ä«",image:"â„‘",ImaginaryI:"â…ˆ",imagline:"â„",imagpart:"â„‘",imath:"ı",Im:"â„‘",imof:"⊷",imped:"Ƶ",Implies:"⇒",incare:"â„…",in:"∈",infin:"∞",infintie:"â§",inodot:"ı",intcal:"⊺",int:"∫",Int:"∬",integers:"ℤ",Integral:"∫",intercal:"⊺",Intersection:"â‹‚",intlarhk:"⨗",intprod:"⨼",InvisibleComma:"â£",InvisibleTimes:"â¢",IOcy:"Ð",iocy:"Ñ‘",Iogon:"Ä®",iogon:"į",Iopf:"ð•€",iopf:"ð•š",Iota:"Ι",iota:"ι",iprod:"⨼",iquest:"¿",iscr:"ð’¾",Iscr:"â„",isin:"∈",isindot:"⋵",isinE:"⋹",isins:"â‹´",isinsv:"⋳",isinv:"∈",it:"â¢",Itilde:"Ĩ",itilde:"Ä©",Iukcy:"І",iukcy:"Ñ–",Iuml:"Ã",iuml:"ï",Jcirc:"Ä´",jcirc:"ĵ",Jcy:"Й",jcy:"й",Jfr:"ð”",jfr:"ð”§",jmath:"È·",Jopf:"ð•",jopf:"ð•›",Jscr:"ð’¥",jscr:"ð’¿",Jsercy:"Ј",jsercy:"ј",Jukcy:"Є",jukcy:"Ñ”",Kappa:"Κ",kappa:"κ",kappav:"ϰ",Kcedil:"Ķ",kcedil:"Ä·",Kcy:"К",kcy:"к",Kfr:"ð”Ž",kfr:"ð”¨",kgreen:"ĸ",KHcy:"Ð¥",khcy:"Ñ…",KJcy:"ÐŒ",kjcy:"Ñœ",Kopf:"ð•‚",kopf:"ð•œ",Kscr:"ð’¦",kscr:"ð“€",lAarr:"⇚",Lacute:"Ĺ",lacute:"ĺ",laemptyv:"⦴",lagran:"â„’",Lambda:"Λ",lambda:"λ",lang:"⟨",Lang:"⟪",langd:"⦑",langle:"⟨",lap:"⪅",Laplacetrf:"â„’",laquo:"«",larrb:"⇤",larrbfs:"⤟",larr:"â†",Larr:"↞",lArr:"â‡",larrfs:"â¤",larrhk:"↩",larrlp:"↫",larrpl:"⤹",larrsim:"⥳",larrtl:"↢",latail:"⤙",lAtail:"⤛",lat:"⪫",late:"⪭",lates:"⪭︀",lbarr:"⤌",lBarr:"⤎",lbbrk:"â²",lbrace:"{",lbrack:"[",lbrke:"⦋",lbrksld:"â¦",lbrkslu:"â¦",Lcaron:"Ľ",lcaron:"ľ",Lcedil:"Ä»",lcedil:"ļ",lceil:"⌈",lcub:"{",Lcy:"Л",lcy:"л",ldca:"⤶",ldquo:"“",ldquor:"„",ldrdhar:"⥧",ldrushar:"⥋",ldsh:"↲",le:"≤",lE:"≦",LeftAngleBracket:"⟨",LeftArrowBar:"⇤",leftarrow:"â†",LeftArrow:"â†",Leftarrow:"â‡",LeftArrowRightArrow:"⇆",leftarrowtail:"↢",LeftCeiling:"⌈",LeftDoubleBracket:"⟦",LeftDownTeeVector:"⥡",LeftDownVectorBar:"⥙",LeftDownVector:"⇃",LeftFloor:"⌊",leftharpoondown:"↽",leftharpoonup:"↼",leftleftarrows:"⇇",leftrightarrow:"↔",LeftRightArrow:"↔",Leftrightarrow:"⇔",leftrightarrows:"⇆",leftrightharpoons:"⇋",leftrightsquigarrow:"↭",LeftRightVector:"⥎",LeftTeeArrow:"↤",LeftTee:"⊣",LeftTeeVector:"⥚",leftthreetimes:"â‹‹",LeftTriangleBar:"â§",LeftTriangle:"⊲",LeftTriangleEqual:"⊴",LeftUpDownVector:"⥑",LeftUpTeeVector:"⥠",LeftUpVectorBar:"⥘",LeftUpVector:"↿",LeftVectorBar:"⥒",LeftVector:"↼",lEg:"⪋",leg:"⋚",leq:"≤",leqq:"≦",leqslant:"⩽",lescc:"⪨",les:"⩽",lesdot:"â©¿",lesdoto:"âª",lesdotor:"⪃",lesg:"⋚︀",lesges:"⪓",lessapprox:"⪅",lessdot:"â‹–",lesseqgtr:"⋚",lesseqqgtr:"⪋",LessEqualGreater:"⋚",LessFullEqual:"≦",LessGreater:"≶",lessgtr:"≶",LessLess:"⪡",lesssim:"≲",LessSlantEqual:"⩽",LessTilde:"≲",lfisht:"⥼",lfloor:"⌊",Lfr:"ð”",lfr:"ð”©",lg:"≶",lgE:"⪑",lHar:"⥢",lhard:"↽",lharu:"↼",lharul:"⥪",lhblk:"â–„",LJcy:"Љ",ljcy:"Ñ™",llarr:"⇇",ll:"≪",Ll:"⋘",llcorner:"⌞",Lleftarrow:"⇚",llhard:"⥫",lltri:"â—º",Lmidot:"Ä¿",lmidot:"Å€",lmoustache:"⎰",lmoust:"⎰",lnap:"⪉",lnapprox:"⪉",lne:"⪇",lnE:"≨",lneq:"⪇",lneqq:"≨",lnsim:"⋦",loang:"⟬",loarr:"⇽",lobrk:"⟦",longleftarrow:"⟵",LongLeftArrow:"⟵",Longleftarrow:"⟸",longleftrightarrow:"⟷",LongLeftRightArrow:"⟷",Longleftrightarrow:"⟺",longmapsto:"⟼",longrightarrow:"⟶",LongRightArrow:"⟶",Longrightarrow:"⟹",looparrowleft:"↫",looparrowright:"↬",lopar:"⦅",Lopf:"ð•ƒ",lopf:"ð•",loplus:"⨭",lotimes:"⨴",lowast:"∗",lowbar:"_",LowerLeftArrow:"↙",LowerRightArrow:"↘",loz:"â—Š",lozenge:"â—Š",lozf:"â§«",lpar:"(",lparlt:"⦓",lrarr:"⇆",lrcorner:"⌟",lrhar:"⇋",lrhard:"⥭",lrm:"‎",lrtri:"⊿",lsaquo:"‹",lscr:"ð“",Lscr:"â„’",lsh:"↰",Lsh:"↰",lsim:"≲",lsime:"âª",lsimg:"âª",lsqb:"[",lsquo:"‘",lsquor:"‚",Lstrok:"Å",lstrok:"Å‚",ltcc:"⪦",ltcir:"⩹",lt:"<",LT:"<",Lt:"≪",ltdot:"â‹–",lthree:"â‹‹",ltimes:"⋉",ltlarr:"⥶",ltquest:"â©»",ltri:"â—ƒ",ltrie:"⊴",ltrif:"â—‚",ltrPar:"⦖",lurdshar:"⥊",luruhar:"⥦",lvertneqq:"≨︀",lvnE:"≨︀",macr:"¯",male:"♂",malt:"✠",maltese:"✠",Map:"⤅",map:"↦",mapsto:"↦",mapstodown:"↧",mapstoleft:"↤",mapstoup:"↥",marker:"â–®",mcomma:"⨩",Mcy:"М",mcy:"м",mdash:"—",mDDot:"∺",measuredangle:"∡",MediumSpace:"âŸ",Mellintrf:"ℳ",Mfr:"ð”",mfr:"ð”ª",mho:"â„§",micro:"µ",midast:"*",midcir:"â«°",mid:"∣",middot:"·",minusb:"⊟",minus:"−",minusd:"∸",minusdu:"⨪",MinusPlus:"∓",mlcp:"â«›",mldr:"…",mnplus:"∓",models:"⊧",Mopf:"ð•„",mopf:"ð•ž",mp:"∓",mscr:"ð“‚",Mscr:"ℳ",mstpos:"∾",Mu:"Μ",mu:"μ",multimap:"⊸",mumap:"⊸",nabla:"∇",Nacute:"Ń",nacute:"Å„",nang:"∠⃒",nap:"≉",napE:"⩰̸",napid:"≋̸",napos:"ʼn",napprox:"≉",natural:"â™®",naturals:"â„•",natur:"â™®",nbsp:" ",nbump:"≎̸",nbumpe:"â‰Ì¸",ncap:"⩃",Ncaron:"Ň",ncaron:"ň",Ncedil:"Å…",ncedil:"ņ",ncong:"≇",ncongdot:"⩭̸",ncup:"â©‚",Ncy:"Ð",ncy:"н",ndash:"–",nearhk:"⤤",nearr:"↗",neArr:"⇗",nearrow:"↗",ne:"≠",nedot:"â‰Ì¸",NegativeMediumSpace:"​",NegativeThickSpace:"​",NegativeThinSpace:"​",NegativeVeryThinSpace:"​",nequiv:"≢",nesear:"⤨",nesim:"≂̸",NestedGreaterGreater:"≫",NestedLessLess:"≪",NewLine:"\n",nexist:"∄",nexists:"∄",Nfr:"ð”‘",nfr:"ð”«",ngE:"≧̸",nge:"≱",ngeq:"≱",ngeqq:"≧̸",ngeqslant:"⩾̸",nges:"⩾̸",nGg:"⋙̸",ngsim:"≵",nGt:"≫⃒",ngt:"≯",ngtr:"≯",nGtv:"≫̸",nharr:"↮",nhArr:"⇎",nhpar:"⫲",ni:"∋",nis:"⋼",nisd:"⋺",niv:"∋",NJcy:"Њ",njcy:"Ñš",nlarr:"↚",nlArr:"â‡",nldr:"‥",nlE:"≦̸",nle:"≰",nleftarrow:"↚",nLeftarrow:"â‡",nleftrightarrow:"↮",nLeftrightarrow:"⇎",nleq:"≰",nleqq:"≦̸",nleqslant:"⩽̸",nles:"⩽̸",nless:"≮",nLl:"⋘̸",nlsim:"≴",nLt:"≪⃒",nlt:"≮",nltri:"⋪",nltrie:"⋬",nLtv:"≪̸",nmid:"∤",NoBreak:"â ",NonBreakingSpace:" ",nopf:"ð•Ÿ",Nopf:"â„•",Not:"⫬",not:"¬",NotCongruent:"≢",NotCupCap:"≭",NotDoubleVerticalBar:"∦",NotElement:"∉",NotEqual:"≠",NotEqualTilde:"≂̸",NotExists:"∄",NotGreater:"≯",NotGreaterEqual:"≱",NotGreaterFullEqual:"≧̸",NotGreaterGreater:"≫̸",NotGreaterLess:"≹",NotGreaterSlantEqual:"⩾̸",NotGreaterTilde:"≵",NotHumpDownHump:"≎̸",NotHumpEqual:"â‰Ì¸",notin:"∉",notindot:"⋵̸",notinE:"⋹̸",notinva:"∉",notinvb:"â‹·",notinvc:"â‹¶",NotLeftTriangleBar:"â§Ì¸",NotLeftTriangle:"⋪",NotLeftTriangleEqual:"⋬",NotLess:"≮",NotLessEqual:"≰",NotLessGreater:"≸",NotLessLess:"≪̸",NotLessSlantEqual:"⩽̸",NotLessTilde:"≴",NotNestedGreaterGreater:"⪢̸",NotNestedLessLess:"⪡̸",notni:"∌",notniva:"∌",notnivb:"⋾",notnivc:"⋽",NotPrecedes:"⊀",NotPrecedesEqual:"⪯̸",NotPrecedesSlantEqual:"â‹ ",NotReverseElement:"∌",NotRightTriangleBar:"â§Ì¸",NotRightTriangle:"â‹«",NotRightTriangleEqual:"â‹­",NotSquareSubset:"âŠÌ¸",NotSquareSubsetEqual:"â‹¢",NotSquareSuperset:"âŠÌ¸",NotSquareSupersetEqual:"â‹£",NotSubset:"⊂⃒",NotSubsetEqual:"⊈",NotSucceeds:"âŠ",NotSucceedsEqual:"⪰̸",NotSucceedsSlantEqual:"â‹¡",NotSucceedsTilde:"≿̸",NotSuperset:"⊃⃒",NotSupersetEqual:"⊉",NotTilde:"â‰",NotTildeEqual:"≄",NotTildeFullEqual:"≇",NotTildeTilde:"≉",NotVerticalBar:"∤",nparallel:"∦",npar:"∦",nparsl:"⫽⃥",npart:"∂̸",npolint:"⨔",npr:"⊀",nprcue:"â‹ ",nprec:"⊀",npreceq:"⪯̸",npre:"⪯̸",nrarrc:"⤳̸",nrarr:"↛",nrArr:"â‡",nrarrw:"â†Ì¸",nrightarrow:"↛",nRightarrow:"â‡",nrtri:"â‹«",nrtrie:"â‹­",nsc:"âŠ",nsccue:"â‹¡",nsce:"⪰̸",Nscr:"ð’©",nscr:"ð“ƒ",nshortmid:"∤",nshortparallel:"∦",nsim:"â‰",nsime:"≄",nsimeq:"≄",nsmid:"∤",nspar:"∦",nsqsube:"â‹¢",nsqsupe:"â‹£",nsub:"⊄",nsubE:"⫅̸",nsube:"⊈",nsubset:"⊂⃒",nsubseteq:"⊈",nsubseteqq:"⫅̸",nsucc:"âŠ",nsucceq:"⪰̸",nsup:"⊅",nsupE:"⫆̸",nsupe:"⊉",nsupset:"⊃⃒",nsupseteq:"⊉",nsupseteqq:"⫆̸",ntgl:"≹",Ntilde:"Ñ",ntilde:"ñ",ntlg:"≸",ntriangleleft:"⋪",ntrianglelefteq:"⋬",ntriangleright:"â‹«",ntrianglerighteq:"â‹­",Nu:"Î",nu:"ν",num:"#",numero:"â„–",numsp:" ",nvap:"â‰âƒ’",nvdash:"⊬",nvDash:"⊭",nVdash:"⊮",nVDash:"⊯",nvge:"≥⃒",nvgt:">⃒",nvHarr:"⤄",nvinfin:"â§ž",nvlArr:"⤂",nvle:"≤⃒",nvlt:"<⃒",nvltrie:"⊴⃒",nvrArr:"⤃",nvrtrie:"⊵⃒",nvsim:"∼⃒",nwarhk:"⤣",nwarr:"↖",nwArr:"⇖",nwarrow:"↖",nwnear:"⤧",Oacute:"Ó",oacute:"ó",oast:"⊛",Ocirc:"Ô",ocirc:"ô",ocir:"⊚",Ocy:"О",ocy:"о",odash:"âŠ",Odblac:"Å",odblac:"Å‘",odiv:"⨸",odot:"⊙",odsold:"⦼",OElig:"Å’",oelig:"Å“",ofcir:"⦿",Ofr:"ð”’",ofr:"ð”¬",ogon:"Ë›",Ograve:"Ã’",ograve:"ò",ogt:"â§",ohbar:"⦵",ohm:"Ω",oint:"∮",olarr:"↺",olcir:"⦾",olcross:"⦻",oline:"‾",olt:"â§€",Omacr:"ÅŒ",omacr:"Å",Omega:"Ω",omega:"ω",Omicron:"Ο",omicron:"ο",omid:"⦶",ominus:"⊖",Oopf:"ð•†",oopf:"ð• ",opar:"⦷",OpenCurlyDoubleQuote:"“",OpenCurlyQuote:"‘",operp:"⦹",oplus:"⊕",orarr:"↻",Or:"â©”",or:"∨",ord:"â©",order:"â„´",orderof:"â„´",ordf:"ª",ordm:"º",origof:"⊶",oror:"â©–",orslope:"â©—",orv:"â©›",oS:"Ⓢ",Oscr:"ð’ª",oscr:"â„´",Oslash:"Ø",oslash:"ø",osol:"⊘",Otilde:"Õ",otilde:"õ",otimesas:"⨶",Otimes:"⨷",otimes:"⊗",Ouml:"Ö",ouml:"ö",ovbar:"⌽",OverBar:"‾",OverBrace:"âž",OverBracket:"⎴",OverParenthesis:"âœ",para:"¶",parallel:"∥",par:"∥",parsim:"⫳",parsl:"⫽",part:"∂",PartialD:"∂",Pcy:"П",pcy:"п",percnt:"%",period:".",permil:"‰",perp:"⊥",pertenk:"‱",Pfr:"ð”“",pfr:"ð”­",Phi:"Φ",phi:"φ",phiv:"Ï•",phmmat:"ℳ",phone:"☎",Pi:"Π",pi:"Ï€",pitchfork:"â‹”",piv:"Ï–",planck:"â„",planckh:"ℎ",plankv:"â„",plusacir:"⨣",plusb:"⊞",pluscir:"⨢",plus:"+",plusdo:"∔",plusdu:"⨥",pluse:"⩲",PlusMinus:"±",plusmn:"±",plussim:"⨦",plustwo:"⨧",pm:"±",Poincareplane:"ℌ",pointint:"⨕",popf:"ð•¡",Popf:"â„™",pound:"£",prap:"⪷",Pr:"⪻",pr:"≺",prcue:"≼",precapprox:"⪷",prec:"≺",preccurlyeq:"≼",Precedes:"≺",PrecedesEqual:"⪯",PrecedesSlantEqual:"≼",PrecedesTilde:"≾",preceq:"⪯",precnapprox:"⪹",precneqq:"⪵",precnsim:"⋨",pre:"⪯",prE:"⪳",precsim:"≾",prime:"′",Prime:"″",primes:"â„™",prnap:"⪹",prnE:"⪵",prnsim:"⋨",prod:"âˆ",Product:"âˆ",profalar:"⌮",profline:"⌒",profsurf:"⌓",prop:"âˆ",Proportional:"âˆ",Proportion:"∷",propto:"âˆ",prsim:"≾",prurel:"⊰",Pscr:"ð’«",pscr:"ð“…",Psi:"Ψ",psi:"ψ",puncsp:" ",Qfr:"ð””",qfr:"ð”®",qint:"⨌",qopf:"ð•¢",Qopf:"ℚ",qprime:"â—",Qscr:"ð’¬",qscr:"ð“†",quaternions:"â„",quatint:"⨖",quest:"?",questeq:"≟",quot:'"',QUOT:'"',rAarr:"⇛",race:"∽̱",Racute:"Å”",racute:"Å•",radic:"√",raemptyv:"⦳",rang:"⟩",Rang:"⟫",rangd:"⦒",range:"⦥",rangle:"⟩",raquo:"»",rarrap:"⥵",rarrb:"⇥",rarrbfs:"⤠",rarrc:"⤳",rarr:"→",Rarr:"↠",rArr:"⇒",rarrfs:"⤞",rarrhk:"↪",rarrlp:"↬",rarrpl:"⥅",rarrsim:"⥴",Rarrtl:"⤖",rarrtl:"↣",rarrw:"â†",ratail:"⤚",rAtail:"⤜",ratio:"∶",rationals:"ℚ",rbarr:"â¤",rBarr:"â¤",RBarr:"â¤",rbbrk:"â³",rbrace:"}",rbrack:"]",rbrke:"⦌",rbrksld:"⦎",rbrkslu:"â¦",Rcaron:"Ř",rcaron:"Å™",Rcedil:"Å–",rcedil:"Å—",rceil:"⌉",rcub:"}",Rcy:"Р",rcy:"Ñ€",rdca:"⤷",rdldhar:"⥩",rdquo:"â€",rdquor:"â€",rdsh:"↳",real:"ℜ",realine:"â„›",realpart:"ℜ",reals:"â„",Re:"ℜ",rect:"â–­",reg:"®",REG:"®",ReverseElement:"∋",ReverseEquilibrium:"⇋",ReverseUpEquilibrium:"⥯",rfisht:"⥽",rfloor:"⌋",rfr:"ð”¯",Rfr:"ℜ",rHar:"⥤",rhard:"â‡",rharu:"⇀",rharul:"⥬",Rho:"Ρ",rho:"Ï",rhov:"ϱ",RightAngleBracket:"⟩",RightArrowBar:"⇥",rightarrow:"→",RightArrow:"→",Rightarrow:"⇒",RightArrowLeftArrow:"⇄",rightarrowtail:"↣",RightCeiling:"⌉",RightDoubleBracket:"⟧",RightDownTeeVector:"â¥",RightDownVectorBar:"⥕",RightDownVector:"⇂",RightFloor:"⌋",rightharpoondown:"â‡",rightharpoonup:"⇀",rightleftarrows:"⇄",rightleftharpoons:"⇌",rightrightarrows:"⇉",rightsquigarrow:"â†",RightTeeArrow:"↦",RightTee:"⊢",RightTeeVector:"⥛",rightthreetimes:"⋌",RightTriangleBar:"â§",RightTriangle:"⊳",RightTriangleEqual:"⊵",RightUpDownVector:"â¥",RightUpTeeVector:"⥜",RightUpVectorBar:"⥔",RightUpVector:"↾",RightVectorBar:"⥓",RightVector:"⇀",ring:"Ëš",risingdotseq:"≓",rlarr:"⇄",rlhar:"⇌",rlm:"â€",rmoustache:"⎱",rmoust:"⎱",rnmid:"â«®",roang:"⟭",roarr:"⇾",robrk:"⟧",ropar:"⦆",ropf:"ð•£",Ropf:"â„",roplus:"⨮",rotimes:"⨵",RoundImplies:"⥰",rpar:")",rpargt:"⦔",rppolint:"⨒",rrarr:"⇉",Rrightarrow:"⇛",rsaquo:"›",rscr:"ð“‡",Rscr:"â„›",rsh:"↱",Rsh:"↱",rsqb:"]",rsquo:"’",rsquor:"’",rthree:"⋌",rtimes:"⋊",rtri:"â–¹",rtrie:"⊵",rtrif:"â–¸",rtriltri:"â§Ž",RuleDelayed:"â§´",ruluhar:"⥨",rx:"℞",Sacute:"Åš",sacute:"Å›",sbquo:"‚",scap:"⪸",Scaron:"Å ",scaron:"Å¡",Sc:"⪼",sc:"≻",sccue:"≽",sce:"⪰",scE:"⪴",Scedil:"Åž",scedil:"ÅŸ",Scirc:"Åœ",scirc:"Å",scnap:"⪺",scnE:"⪶",scnsim:"â‹©",scpolint:"⨓",scsim:"≿",Scy:"С",scy:"Ñ",sdotb:"⊡",sdot:"â‹…",sdote:"⩦",searhk:"⤥",searr:"↘",seArr:"⇘",searrow:"↘",sect:"§",semi:";",seswar:"⤩",setminus:"∖",setmn:"∖",sext:"✶",Sfr:"ð”–",sfr:"ð”°",sfrown:"⌢",sharp:"♯",SHCHcy:"Щ",shchcy:"щ",SHcy:"Ш",shcy:"ш",ShortDownArrow:"↓",ShortLeftArrow:"â†",shortmid:"∣",shortparallel:"∥",ShortRightArrow:"→",ShortUpArrow:"↑",shy:"­",Sigma:"Σ",sigma:"σ",sigmaf:"Ï‚",sigmav:"Ï‚",sim:"∼",simdot:"⩪",sime:"≃",simeq:"≃",simg:"⪞",simgE:"⪠",siml:"âª",simlE:"⪟",simne:"≆",simplus:"⨤",simrarr:"⥲",slarr:"â†",SmallCircle:"∘",smallsetminus:"∖",smashp:"⨳",smeparsl:"⧤",smid:"∣",smile:"⌣",smt:"⪪",smte:"⪬",smtes:"⪬︀",SOFTcy:"Ь",softcy:"ÑŒ",solbar:"⌿",solb:"â§„",sol:"/",Sopf:"ð•Š",sopf:"ð•¤",spades:"â™ ",spadesuit:"â™ ",spar:"∥",sqcap:"⊓",sqcaps:"⊓︀",sqcup:"⊔",sqcups:"⊔︀",Sqrt:"√",sqsub:"âŠ",sqsube:"⊑",sqsubset:"âŠ",sqsubseteq:"⊑",sqsup:"âŠ",sqsupe:"⊒",sqsupset:"âŠ",sqsupseteq:"⊒",square:"â–¡",Square:"â–¡",SquareIntersection:"⊓",SquareSubset:"âŠ",SquareSubsetEqual:"⊑",SquareSuperset:"âŠ",SquareSupersetEqual:"⊒",SquareUnion:"⊔",squarf:"â–ª",squ:"â–¡",squf:"â–ª",srarr:"→",Sscr:"ð’®",sscr:"ð“ˆ",ssetmn:"∖",ssmile:"⌣",sstarf:"⋆",Star:"⋆",star:"☆",starf:"★",straightepsilon:"ϵ",straightphi:"Ï•",strns:"¯",sub:"⊂",Sub:"â‹",subdot:"⪽",subE:"â«…",sube:"⊆",subedot:"⫃",submult:"â«",subnE:"â«‹",subne:"⊊",subplus:"⪿",subrarr:"⥹",subset:"⊂",Subset:"â‹",subseteq:"⊆",subseteqq:"â«…",SubsetEqual:"⊆",subsetneq:"⊊",subsetneqq:"â«‹",subsim:"⫇",subsub:"â«•",subsup:"â«“",succapprox:"⪸",succ:"≻",succcurlyeq:"≽",Succeeds:"≻",SucceedsEqual:"⪰",SucceedsSlantEqual:"≽",SucceedsTilde:"≿",succeq:"⪰",succnapprox:"⪺",succneqq:"⪶",succnsim:"â‹©",succsim:"≿",SuchThat:"∋",sum:"∑",Sum:"∑",sung:"♪",sup1:"¹",sup2:"²",sup3:"³",sup:"⊃",Sup:"â‹‘",supdot:"⪾",supdsub:"⫘",supE:"⫆",supe:"⊇",supedot:"â«„",Superset:"⊃",SupersetEqual:"⊇",suphsol:"⟉",suphsub:"â«—",suplarr:"⥻",supmult:"â«‚",supnE:"⫌",supne:"⊋",supplus:"â«€",supset:"⊃",Supset:"â‹‘",supseteq:"⊇",supseteqq:"⫆",supsetneq:"⊋",supsetneqq:"⫌",supsim:"⫈",supsub:"â«”",supsup:"â«–",swarhk:"⤦",swarr:"↙",swArr:"⇙",swarrow:"↙",swnwar:"⤪",szlig:"ß",Tab:"\t",target:"⌖",Tau:"Τ",tau:"Ï„",tbrk:"⎴",Tcaron:"Ť",tcaron:"Å¥",Tcedil:"Å¢",tcedil:"Å£",Tcy:"Т",tcy:"Ñ‚",tdot:"⃛",telrec:"⌕",Tfr:"ð”—",tfr:"ð”±",there4:"∴",therefore:"∴",Therefore:"∴",Theta:"Θ",theta:"θ",thetasym:"Ï‘",thetav:"Ï‘",thickapprox:"≈",thicksim:"∼",ThickSpace:"âŸâ€Š",ThinSpace:" ",thinsp:" ",thkap:"≈",thksim:"∼",THORN:"Þ",thorn:"þ",tilde:"Ëœ",Tilde:"∼",TildeEqual:"≃",TildeFullEqual:"≅",TildeTilde:"≈",timesbar:"⨱",timesb:"⊠",times:"×",timesd:"⨰",tint:"∭",toea:"⤨",topbot:"⌶",topcir:"⫱",top:"⊤",Topf:"ð•‹",topf:"ð•¥",topfork:"⫚",tosa:"⤩",tprime:"‴",trade:"â„¢",TRADE:"â„¢",triangle:"â–µ",triangledown:"â–¿",triangleleft:"â—ƒ",trianglelefteq:"⊴",triangleq:"≜",triangleright:"â–¹",trianglerighteq:"⊵",tridot:"â—¬",trie:"≜",triminus:"⨺",TripleDot:"⃛",triplus:"⨹",trisb:"â§",tritime:"⨻",trpezium:"â¢",Tscr:"ð’¯",tscr:"ð“‰",TScy:"Ц",tscy:"ц",TSHcy:"Ћ",tshcy:"Ñ›",Tstrok:"Ŧ",tstrok:"ŧ",twixt:"≬",twoheadleftarrow:"↞",twoheadrightarrow:"↠",Uacute:"Ú",uacute:"ú",uarr:"↑",Uarr:"↟",uArr:"⇑",Uarrocir:"⥉",Ubrcy:"ÐŽ",ubrcy:"Ñž",Ubreve:"Ŭ",ubreve:"Å­",Ucirc:"Û",ucirc:"û",Ucy:"У",ucy:"у",udarr:"⇅",Udblac:"Ű",udblac:"ű",udhar:"⥮",ufisht:"⥾",Ufr:"ð”˜",ufr:"ð”²",Ugrave:"Ù",ugrave:"ù",uHar:"⥣",uharl:"↿",uharr:"↾",uhblk:"â–€",ulcorn:"⌜",ulcorner:"⌜",ulcrop:"âŒ",ultri:"â—¸",Umacr:"Ū",umacr:"Å«",uml:"¨",UnderBar:"_",UnderBrace:"âŸ",UnderBracket:"⎵",UnderParenthesis:"â",Union:"⋃",UnionPlus:"⊎",Uogon:"Ų",uogon:"ų",Uopf:"ð•Œ",uopf:"ð•¦",UpArrowBar:"⤒",uparrow:"↑",UpArrow:"↑",Uparrow:"⇑",UpArrowDownArrow:"⇅",updownarrow:"↕",UpDownArrow:"↕",Updownarrow:"⇕",UpEquilibrium:"⥮",upharpoonleft:"↿",upharpoonright:"↾",uplus:"⊎",UpperLeftArrow:"↖",UpperRightArrow:"↗",upsi:"Ï…",Upsi:"Ï’",upsih:"Ï’",Upsilon:"Î¥",upsilon:"Ï…",UpTeeArrow:"↥",UpTee:"⊥",upuparrows:"⇈",urcorn:"âŒ",urcorner:"âŒ",urcrop:"⌎",Uring:"Å®",uring:"ů",urtri:"â—¹",Uscr:"ð’°",uscr:"ð“Š",utdot:"â‹°",Utilde:"Ũ",utilde:"Å©",utri:"â–µ",utrif:"â–´",uuarr:"⇈",Uuml:"Ü",uuml:"ü",uwangle:"⦧",vangrt:"⦜",varepsilon:"ϵ",varkappa:"ϰ",varnothing:"∅",varphi:"Ï•",varpi:"Ï–",varpropto:"âˆ",varr:"↕",vArr:"⇕",varrho:"ϱ",varsigma:"Ï‚",varsubsetneq:"⊊︀",varsubsetneqq:"⫋︀",varsupsetneq:"⊋︀",varsupsetneqq:"⫌︀",vartheta:"Ï‘",vartriangleleft:"⊲",vartriangleright:"⊳",vBar:"⫨",Vbar:"â««",vBarv:"â«©",Vcy:"Ð’",vcy:"в",vdash:"⊢",vDash:"⊨",Vdash:"⊩",VDash:"⊫",Vdashl:"⫦",veebar:"⊻",vee:"∨",Vee:"â‹",veeeq:"≚",vellip:"â‹®",verbar:"|",Verbar:"‖",vert:"|",Vert:"‖",VerticalBar:"∣",VerticalLine:"|",VerticalSeparator:"â˜",VerticalTilde:"≀",VeryThinSpace:" ",Vfr:"ð”™",vfr:"ð”³",vltri:"⊲",vnsub:"⊂⃒",vnsup:"⊃⃒",Vopf:"ð•",vopf:"ð•§",vprop:"âˆ",vrtri:"⊳",Vscr:"ð’±",vscr:"ð“‹",vsubnE:"⫋︀",vsubne:"⊊︀",vsupnE:"⫌︀",vsupne:"⊋︀",Vvdash:"⊪",vzigzag:"⦚",Wcirc:"Å´",wcirc:"ŵ",wedbar:"⩟",wedge:"∧",Wedge:"â‹€",wedgeq:"≙",weierp:"℘",Wfr:"ð”š",wfr:"ð”´",Wopf:"ð•Ž",wopf:"ð•¨",wp:"℘",wr:"≀",wreath:"≀",Wscr:"ð’²",wscr:"ð“Œ",xcap:"â‹‚",xcirc:"â—¯",xcup:"⋃",xdtri:"â–½",Xfr:"ð”›",xfr:"ð”µ",xharr:"⟷",xhArr:"⟺",Xi:"Ξ",xi:"ξ",xlarr:"⟵",xlArr:"⟸",xmap:"⟼",xnis:"â‹»",xodot:"⨀",Xopf:"ð•",xopf:"ð•©",xoplus:"â¨",xotime:"⨂",xrarr:"⟶",xrArr:"⟹",Xscr:"ð’³",xscr:"ð“",xsqcup:"⨆",xuplus:"⨄",xutri:"â–³",xvee:"â‹",xwedge:"â‹€",Yacute:"Ã",yacute:"ý",YAcy:"Я",yacy:"Ñ",Ycirc:"Ŷ",ycirc:"Å·",Ycy:"Ы",ycy:"Ñ‹",yen:"Â¥",Yfr:"ð”œ",yfr:"ð”¶",YIcy:"Ї",yicy:"Ñ—",Yopf:"ð•",yopf:"ð•ª",Yscr:"ð’´",yscr:"ð“Ž",YUcy:"Ю",yucy:"ÑŽ",yuml:"ÿ",Yuml:"Ÿ",Zacute:"Ź",zacute:"ź",Zcaron:"Ž",zcaron:"ž",Zcy:"З",zcy:"з",Zdot:"Å»",zdot:"ż",zeetrf:"ℨ",ZeroWidthSpace:"​",Zeta:"Ζ",zeta:"ζ",zfr:"ð”·",Zfr:"ℨ",ZHcy:"Ж",zhcy:"ж",zigrarr:"â‡",zopf:"ð•«",Zopf:"ℤ",Zscr:"ð’µ",zscr:"ð“",zwj:"â€",zwnj:"‌"}},function(e,t){ +/*! http://mths.be/repeat v0.2.0 by @mathias */ +String.prototype.repeat||function(){"use strict";var e=function(){try{var e={},t=Object.defineProperty,r=t(e,e,e)&&t}catch(e){}return r}(),t=function(e){if(null==this)throw TypeError();var t=String(this),r=e?Number(e):0;if(r!=r&&(r=0),r<0||r==1/0)throw RangeError();for(var n="";r;)r%2==1&&(n+=t),r>1&&(t+=t),r>>=1;return n};e?e(String.prototype,"repeat",{value:t,configurable:!0,writable:!0}):String.prototype.repeat=t}()},function(e,t,r){"use strict";function Renderer(){}Renderer.prototype.render=function render(e){var t,r,n=e.walker();for(this.buffer="",this.lastOut="\n";t=n.next();)this[r=t.node.type]&&this[r](t.node,t.entering);return this.buffer},Renderer.prototype.out=function out(e){this.lit(e)},Renderer.prototype.lit=function lit(e){this.buffer+=e,this.lastOut=e},Renderer.prototype.cr=function cr(){"\n"!==this.lastOut&&this.lit("\n")},Renderer.prototype.esc=function esc(e){return e},e.exports=Renderer},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=_interopRequireDefault(r(87)),i=_interopRequireDefault(r(0)),o=r(35);function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}t.default=(0,o.OAS3ComponentWrapFactory)(function(e){var t=e.Ori,r=(0,n.default)(e,["Ori"]),o=r.schema,a=r.getComponent,s=r.errSelectors,u=r.authorized,l=r.onAuthChange,c=r.name,p=a("HttpAuth");return"http"===o.get("type")?i.default.createElement(p,{key:c,schema:o,name:c,errSelectors:s,authorized:u,getComponent:a,onChange:l}):i.default.createElement(t,r)})},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=_interopRequireDefault(r(86)),i=_interopRequireDefault(r(4)),o=_interopRequireDefault(r(2)),a=_interopRequireDefault(r(3)),s=_interopRequireDefault(r(5)),u=_interopRequireDefault(r(6)),l=r(0),c=_interopRequireDefault(l),p=_interopRequireDefault(r(1)),f=r(7),d=_interopRequireDefault(f),h=_interopRequireDefault(r(12)),m=r(35);function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}var v=function(e){function Parameters(e){(0,o.default)(this,Parameters);var t=(0,s.default)(this,(Parameters.__proto__||(0,i.default)(Parameters)).call(this,e));return t.onChange=function(e,r,n){var i=t.props;(0,i.specActions.changeParamByIdentity)(i.onChangeKey,e,r,n)},t.onChangeConsumesWrapper=function(e){var r=t.props;(0,r.specActions.changeConsumesValue)(r.onChangeKey,e)},t.toggleTab=function(e){return"parameters"===e?t.setState({parametersVisible:!0,callbackVisible:!1}):"callbacks"===e?t.setState({callbackVisible:!0,parametersVisible:!1}):void 0},t.state={callbackVisible:!1,parametersVisible:!0},t}return(0,u.default)(Parameters,e),(0,a.default)(Parameters,[{key:"render",value:function render(){var e=this,t=this.props,r=t.onTryoutClick,i=t.onCancelClick,o=t.parameters,a=t.allowTryItOut,s=t.tryItOutEnabled,u=t.fn,l=t.getComponent,p=t.getConfigs,h=t.specSelectors,m=t.oas3Actions,v=t.oas3Selectors,g=t.pathMethod,y=t.specPath,_=t.operation,b=l("parameterRow"),S=l("TryItOutButton"),k=l("contentType"),x=l("Callbacks",!0),E=l("RequestBody",!0),C=s&&a,w=h.isOAS3,D=_.get("requestBody"),A=y.slice(0,-1).push("requestBody");return c.default.createElement("div",{className:"opblock-section"},c.default.createElement("div",{className:"opblock-section-header"},c.default.createElement("div",{className:"tab-header"},c.default.createElement("div",{onClick:function onClick(){return e.toggleTab("parameters")},className:"tab-item "+(this.state.parametersVisible&&"active")},c.default.createElement("h4",{className:"opblock-title"},c.default.createElement("span",null,"Parameters"))),_.get("callbacks")?c.default.createElement("div",{onClick:function onClick(){return e.toggleTab("callbacks")},className:"tab-item "+(this.state.callbackVisible&&"active")},c.default.createElement("h4",{className:"opblock-title"},c.default.createElement("span",null,"Callbacks"))):null),a?c.default.createElement(S,{enabled:s,onCancelClick:i,onTryoutClick:r}):null),this.state.parametersVisible?c.default.createElement("div",{className:"parameters-container"},o.count()?c.default.createElement("div",{className:"table-container"},c.default.createElement("table",{className:"parameters"},c.default.createElement("thead",null,c.default.createElement("tr",null,c.default.createElement("th",{className:"col col_header parameters-col_name"},"Name"),c.default.createElement("th",{className:"col col_header parameters-col_description"},"Description"))),c.default.createElement("tbody",null,function eachMap(e,t){return e.valueSeq().filter(d.default.Map.isMap).map(t)}(o,function(t,r){return c.default.createElement(b,{fn:u,getComponent:l,specPath:y.push(r),getConfigs:p,rawParam:t,param:h.parameterWithMetaByIdentity(g,t),key:t.get("name"),onChange:e.onChange,onChangeConsumes:e.onChangeConsumesWrapper,specSelectors:h,pathMethod:g,isExecute:C})}).toArray()))):c.default.createElement("div",{className:"opblock-description-wrapper"},c.default.createElement("p",null,"No parameters"))):"",this.state.callbackVisible?c.default.createElement("div",{className:"callbacks-container opblock-description-wrapper"},c.default.createElement(x,{callbacks:(0,f.Map)(_.get("callbacks")),specPath:y.slice(0,-1).push("callbacks")})):"",w()&&D&&this.state.parametersVisible&&c.default.createElement("div",{className:"opblock-section"},c.default.createElement("div",{className:"opblock-section-header"},c.default.createElement("h4",{className:"opblock-title parameter__name "+(D.get("required")&&"required")},"Request body"),c.default.createElement("label",null,c.default.createElement(k,{value:v.requestContentType.apply(v,(0,n.default)(g)),contentTypes:D.get("content").keySeq(),onChange:function onChange(e){m.setRequestContentType({value:e,pathMethod:g})},className:"body-param-content-type"}))),c.default.createElement("div",{className:"opblock-description-wrapper"},c.default.createElement(E,{specPath:A,requestBody:D,requestBodyValue:v.requestBodyValue.apply(v,(0,n.default)(g))||(0,f.Map)(),isExecute:C,onChange:function onChange(e,t){if(t){var r=v.requestBodyValue.apply(v,(0,n.default)(g)),i=f.Map.isMap(r)?r:(0,f.Map)();return m.setRequestBodyValue({pathMethod:g,value:i.setIn(t,e)})}m.setRequestBodyValue({value:e,pathMethod:g})},contentType:v.requestContentType.apply(v,(0,n.default)(g))}))))}}]),Parameters}(l.Component);v.propTypes={parameters:h.default.list.isRequired,specActions:p.default.object.isRequired,operation:p.default.object.isRequired,getComponent:p.default.func.isRequired,getConfigs:p.default.func.isRequired,specSelectors:p.default.object.isRequired,oas3Actions:p.default.object.isRequired,oas3Selectors:p.default.object.isRequired,fn:p.default.object.isRequired,tryItOutEnabled:p.default.bool,allowTryItOut:p.default.bool,specPath:h.default.list.isRequired,onTryoutClick:p.default.func,onCancelClick:p.default.func,onChangeKey:p.default.array,pathMethod:p.default.array.isRequired},v.defaultProps={onTryoutClick:Function.prototype,onCancelClick:Function.prototype,tryItOutEnabled:!1,allowTryItOut:!0,onChangeKey:[]},t.default=(0,m.OAS3ComponentWrapFactory)(v)},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}(r(0)),i=r(35);t.default=(0,i.OAS3ComponentWrapFactory)(function(e){var t=e.Ori;return n.default.createElement("span",null,n.default.createElement(t,e),n.default.createElement("small",{style:{backgroundColor:"#89bf04"}},n.default.createElement("pre",{className:"version"},"OAS3")))})},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=r(35);t.default=(0,n.OAS3ComponentWrapFactory)(function(){return null})},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=_interopRequireDefault(r(26)),i=_interopRequireDefault(r(4)),o=_interopRequireDefault(r(2)),a=_interopRequireDefault(r(3)),s=_interopRequireDefault(r(5)),u=_interopRequireDefault(r(6)),l=r(0),c=_interopRequireDefault(l),p=_interopRequireDefault(r(1)),f=r(35),d=r(454);function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}var h=function(e){function ModelComponent(){return(0,o.default)(this,ModelComponent),(0,s.default)(this,(ModelComponent.__proto__||(0,i.default)(ModelComponent)).apply(this,arguments))}return(0,u.default)(ModelComponent,e),(0,a.default)(ModelComponent,[{key:"render",value:function render(){var e=this.props,t=e.getConfigs,r=["model-box"],i=null;return!0===e.schema.get("deprecated")&&(r.push("deprecated"),i=c.default.createElement("span",{className:"model-deprecated-warning"},"Deprecated:")),c.default.createElement("div",{className:r.join(" ")},i,c.default.createElement(d.Model,(0,n.default)({},this.props,{getConfigs:t,depth:1,expandDepth:this.props.expandDepth||0})))}}]),ModelComponent}(l.Component);h.propTypes={schema:p.default.object.isRequired,name:p.default.string,getComponent:p.default.func.isRequired,getConfigs:p.default.func.isRequired,specSelectors:p.default.object.isRequired,expandDepth:p.default.number},t.default=(0,f.OAS3ComponentWrapFactory)(h)},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=_interopRequireDefault(r(87)),i=_interopRequireDefault(r(0)),o=r(35);function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}t.default=(0,o.OAS3ComponentWrapFactory)(function(e){var t=e.Ori,r=(0,n.default)(e,["Ori"]),o=r.schema,a=r.getComponent,s=r.errors,u=r.onChange,l=o.type,c=o.format,p=a("Input");return"string"!==l||"binary"!==c&&"base64"!==c?i.default.createElement(t,r):i.default.createElement(p,{type:"file",className:s.length?"invalid":"",title:s.length?s:"",onChange:function onChange(e){u(e.target.files[0])},disabled:t.isDisabled})})},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.serverEffectiveValue=t.serverVariables=t.serverVariableValue=t.responseContentType=t.requestContentType=t.requestBodyValue=t.selectedServer=void 0;var n=r(7),i=r(35);function onlyOAS3(e){return function(){for(var t=arguments.length,r=Array(t),n=0;n=e.length?(this._t=void 0,i(1)):i(0,"keys"==t?r:"values"==t?e[r]:[r,e[r]])},"values"),o.Arguments=o.Array,n("keys"),n("values"),n("entries")},function(e,t){e.exports=function(){}},function(e,t){e.exports=function(e,t){return{value:t,done:!!e}}},function(e,t,r){"use strict";var n=r(170),i=r(101),o=r(103),a={};r(54)(a,r(20)("iterator"),function(){return this}),e.exports=function(e,t,r){e.prototype=n(a,{next:i(1,r)}),o(e,t+" Iterator")}},function(e,t,r){var n=r(42),i=r(36),o=r(102);e.exports=r(47)?Object.defineProperties:function defineProperties(e,t){i(e);for(var r,a=o(t),s=a.length,u=0;s>u;)n.f(e,r=a[u++],t[r]);return e}},function(e,t,r){var n=r(73),i=r(122),o=r(500);e.exports=function(e){return function(t,r,a){var s,u=n(t),l=i(u.length),c=o(a,l);if(e&&r!=r){for(;l>c;)if((s=u[c++])!=s)return!0}else for(;l>c;c++)if((e||c in u)&&u[c]===r)return e||c||0;return!e&&-1}}},function(e,t,r){var n=r(171),i=Math.max,o=Math.min;e.exports=function(e,t){return(e=n(e))<0?i(e+t,0):o(e,t)}},function(e,t,r){var n=r(171),i=r(166);e.exports=function(e){return function(t,r){var o,a,s=String(i(t)),u=n(r),l=s.length;return u<0||u>=l?e?"":void 0:(o=s.charCodeAt(u))<55296||o>56319||u+1===l||(a=s.charCodeAt(u+1))<56320||a>57343?e?s.charAt(u):o:e?s.slice(u,u+2):a-56320+(o-55296<<10)+65536}}},function(e,t,r){var n=r(36),i=r(175);e.exports=r(15).getIterator=function(e){var t=i(e);if("function"!=typeof t)throw TypeError(e+" is not iterable!");return n(t.call(e))}},function(e,t,r){r(504),r(267),r(515),r(519),r(530),r(531),e.exports=r(63).Promise},function(e,t,r){"use strict";var n=r(177),i={};i[r(17)("toStringTag")]="z",i+""!="[object z]"&&r(75)(Object.prototype,"toString",function toString(){return"[object "+n(this)+"]"},!0)},function(e,t,r){e.exports=!r(106)&&!r(107)(function(){return 7!=Object.defineProperty(r(179)("div"),"a",{get:function(){return 7}}).a})},function(e,t,r){var n=r(76);e.exports=function(e,t){if(!n(e))return e;var r,i;if(t&&"function"==typeof(r=e.toString)&&!n(i=r.call(e)))return i;if("function"==typeof(r=e.valueOf)&&!n(i=r.call(e)))return i;if(!t&&"function"==typeof(r=e.toString)&&!n(i=r.call(e)))return i;throw TypeError("Can't convert object to primitive value")}},function(e,t,r){"use strict";var n=r(508),i=r(266),o=r(181),a={};r(61)(a,r(17)("iterator"),function(){return this}),e.exports=function(e,t,r){e.prototype=n(a,{next:i(1,r)}),o(e,t+" Iterator")}},function(e,t,r){var n=r(62),i=r(509),o=r(273),a=r(180)("IE_PROTO"),s=function(){},u=function(){var e,t=r(179)("iframe"),n=o.length;for(t.style.display="none",r(274).appendChild(t),t.src="javascript:",(e=t.contentWindow.document).open(),e.write(" + + + + + Vehicle Holding Times + + + +

    +
    Holding Times
    +
    +
    + + + +
    +
    + + + +
    +
    + + + +
    + + + + + + + + + + + + + + + + + + +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    +
    +
    + +
    +
    +
    + +
    + + + \ No newline at end of file diff --git a/transitclockWebapp/src/main/webapp/holding/js/ion.sound.js b/transitclockWebapp/src/main/webapp/holding/js/ion.sound.js new file mode 100644 index 000000000..028bae7a6 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/holding/js/ion.sound.js @@ -0,0 +1,1082 @@ +/** + * Ion.Sound + * version 3.0.7 Build 89 + * © Denis Ineshin, 2016 + * + * Project page: http://ionden.com/a/plugins/ion.sound/en.html + * GitHub page: https://github.com/IonDen/ion.sound + * + * Released under MIT licence: + * http://ionden.com/a/plugins/licence-en.html + */ + +;(function (window, navigator, $, undefined) { + "use strict"; + + window.ion = window.ion || {}; + + if (ion.sound) { + return; + } + + var warn = function (text) { + if (!text) text = "undefined"; + + if (window.console) { + if (console.warn && typeof console.warn === "function") { + console.warn(text); + } else if (console.log && typeof console.log === "function") { + console.log(text); + } + + var d = $ && $("#debug"); + if (d && d.length) { + var a = d.html(); + d.html(a + text + '
    '); + } + } + }; + + var extend = function (parent, child) { + var prop; + child = child || {}; + + for (prop in parent) { + if (parent.hasOwnProperty(prop)) { + child[prop] = parent[prop]; + } + } + + return child; + }; + + + + /** + * DISABLE for unsupported browsers + */ + + if (typeof Audio !== "function" && typeof Audio !== "object") { + var func = function () { + warn("HTML5 Audio is not supported in this browser"); + }; + ion.sound = func; + ion.sound.play = func; + ion.sound.stop = func; + ion.sound.pause = func; + ion.sound.preload = func; + ion.sound.destroy = func; + func(); + return; + } + + + + /** + * CORE + * - creating sounds collection + * - public methods + */ + + var is_iOS = /iPad|iPhone|iPod/.test(navigator.appVersion), + sounds_num = 0, + settings = {}, + sounds = {}, + i; + + + + if (!settings.supported && is_iOS) { + settings.supported = ["mp3", "mp4", "aac"]; + } else if (!settings.supported) { + settings.supported = ["mp3", "ogg", "mp4", "aac", "wav"]; + } + + var createSound = function (obj) { + var name = obj.alias || obj.name; + + if (!sounds[name]) { + sounds[name] = new Sound(obj); + sounds[name].init(); + } + }; + + ion.sound = function (options) { + extend(options, settings); + + settings.path = settings.path || ""; + settings.volume = settings.volume || 1; + settings.preload = settings.preload || false; + settings.multiplay = settings.multiplay || false; + settings.loop = settings.loop || false; + settings.sprite = settings.sprite || null; + settings.scope = settings.scope || null; + settings.ready_callback = settings.ready_callback || null; + settings.ended_callback = settings.ended_callback || null; + + sounds_num = settings.sounds.length; + + if (!sounds_num) { + warn("No sound-files provided!"); + return; + } + + for (i = 0; i < sounds_num; i++) { + createSound(settings.sounds[i]); + } + }; + + ion.sound.VERSION = "3.0.7"; + + ion.sound._method = function (method, name, options) { + if (name) { + sounds[name] && sounds[name][method](options); + } else { + for (i in sounds) { + if (!sounds.hasOwnProperty(i) || !sounds[i]) { + continue; + } + + sounds[i][method](options); + } + } + }; + + ion.sound.preload = function (name, options) { + options = options || {}; + extend({preload: true}, options); + + ion.sound._method("init", name, options); + }; + + ion.sound.destroy = function (name) { + ion.sound._method("destroy", name); + + if (name) { + sounds[name] = null; + } else { + for (i in sounds) { + if (!sounds.hasOwnProperty(i)) { + continue; + } + if (sounds[i]) { + sounds[i] = null; + } + } + } + }; + + ion.sound.play = function (name, options) { + ion.sound._method("play", name, options); + }; + + ion.sound.stop = function (name, options) { + ion.sound._method("stop", name, options); + }; + + ion.sound.pause = function (name, options) { + ion.sound._method("pause", name, options); + }; + + ion.sound.volume = function (name, options) { + ion.sound._method("volume", name, options); + }; + + if ($) { + $.ionSound = ion.sound; + } + + + + /** + * Web Audio API core + * - for most advanced browsers + */ + + var AudioContext = window.AudioContext || window.webkitAudioContext, + audio; + + if (AudioContext) { + audio = new AudioContext(); + } + + + var Sound = function (options) { + this.options = extend(settings); + delete this.options.sounds; + extend(options, this.options); + + this.request = null; + this.streams = {}; + this.result = {}; + this.ext = 0; + this.url = ""; + + this.loaded = false; + this.decoded = false; + this.no_file = false; + this.autoplay = false; + }; + + Sound.prototype = { + init: function (options) { + if (options) { + extend(options, this.options); + } + + if (this.options.preload) { + this.load(); + } + }, + + destroy: function () { + var stream; + + for (i in this.streams) { + stream = this.streams[i]; + + if (stream) { + stream.destroy(); + stream = null; + } + } + this.streams = {}; + this.result = null; + this.options.buffer = null; + this.options = null; + + if (this.request) { + this.request.removeEventListener("load", this.ready.bind(this), false); + this.request.removeEventListener("error", this.error.bind(this), false); + this.request.abort(); + this.request = null; + } + }, + + createUrl: function () { + var no_cache = new Date().valueOf(); + this.url = this.options.path + encodeURIComponent(this.options.name) + "." + this.options.supported[this.ext] + "?" + no_cache; + }, + + load: function () { + if (this.no_file) { + warn("No sources for \"" + this.options.name + "\" sound :("); + return; + } + + if (this.request) { + return; + } + + this.createUrl(); + + this.request = new XMLHttpRequest(); + this.request.open("GET", this.url, true); + this.request.responseType = "arraybuffer"; + this.request.addEventListener("load", this.ready.bind(this), false); + this.request.addEventListener("error", this.error.bind(this), false); + + this.request.send(); + }, + + reload: function () { + this.ext++; + + if (this.options.supported[this.ext]) { + this.load(); + } else { + this.no_file = true; + warn("No sources for \"" + this.options.name + "\" sound :("); + } + }, + + ready: function (data) { + this.result = data.target; + + if (this.result.readyState !== 4) { + this.reload(); + return; + } + + if (this.result.status !== 200 && this.result.status !== 0) { + warn(this.url + " was not found on server!"); + this.reload(); + return; + } + + this.request.removeEventListener("load", this.ready.bind(this), false); + this.request.removeEventListener("error", this.error.bind(this), false); + this.request = null; + this.loaded = true; + //warn("Loaded: " + this.options.name + "." + settings.supported[this.ext]); + + this.decode(); + }, + + decode: function () { + if (!audio) { + return; + } + + audio.decodeAudioData(this.result.response, this.setBuffer.bind(this), this.error.bind(this)); + }, + + setBuffer: function (buffer) { + this.options.buffer = buffer; + this.decoded = true; + //warn("Decoded: " + this.options.name + "." + settings.supported[this.ext]); + + var config = { + name: this.options.name, + alias: this.options.alias, + ext: this.options.supported[this.ext], + duration: this.options.buffer.duration + }; + + if (this.options.ready_callback && typeof this.options.ready_callback === "function") { + this.options.ready_callback.call(this.options.scope, config); + } + + if (this.options.sprite) { + + for (i in this.options.sprite) { + this.options.start = this.options.sprite[i][0]; + this.options.end = this.options.sprite[i][1]; + this.streams[i] = new Stream(this.options, i); + } + + } else { + + this.streams[0] = new Stream(this.options); + + } + + if (this.autoplay) { + this.autoplay = false; + this.play(); + } + }, + + error: function () { + this.reload(); + }, + + play: function (options) { + delete this.options.part; + + if (options) { + extend(options, this.options); + } + + if (!this.loaded) { + this.autoplay = true; + this.load(); + + return; + } + + if (this.no_file || !this.decoded) { + return; + } + + if (this.options.sprite) { + if (this.options.part) { + this.streams[this.options.part].play(this.options); + } else { + for (i in this.options.sprite) { + this.streams[i].play(this.options); + } + } + } else { + this.streams[0].play(this.options); + } + }, + + stop: function (options) { + if (this.options.sprite) { + + if (options) { + this.streams[options.part].stop(); + } else { + for (i in this.options.sprite) { + this.streams[i].stop(); + } + } + + } else { + this.streams[0].stop(); + } + }, + + pause: function (options) { + if (this.options.sprite) { + + if (options) { + this.streams[options.part].pause(); + } else { + for (i in this.options.sprite) { + this.streams[i].pause(); + } + } + + } else { + this.streams[0].pause(); + } + }, + + volume: function (options) { + var stream; + + if (options) { + extend(options, this.options); + } else { + return; + } + + if (this.options.sprite) { + if (this.options.part) { + stream = this.streams[this.options.part]; + stream && stream.setVolume(this.options); + } else { + for (i in this.options.sprite) { + stream = this.streams[i]; + stream && stream.setVolume(this.options); + } + } + } else { + stream = this.streams[0]; + stream && stream.setVolume(this.options); + } + } + }; + + + + var Stream = function (options, sprite_part) { + this.alias = options.alias; + this.name = options.name; + this.sprite_part = sprite_part; + + this.buffer = options.buffer; + this.start = options.start || 0; + this.end = options.end || this.buffer.duration; + this.multiplay = options.multiplay || false; + this.volume = options.volume || 1; + this.scope = options.scope; + this.ended_callback = options.ended_callback; + + this.setLoop(options); + + this.source = null; + this.gain = null; + this.playing = false; + this.paused = false; + + this.time_started = 0; + this.time_ended = 0; + this.time_played = 0; + this.time_offset = 0; + }; + + Stream.prototype = { + destroy: function () { + this.stop(); + + this.buffer = null; + this.source = null; + + this.gain && this.gain.disconnect(); + this.source && this.source.disconnect(); + this.gain = null; + this.source = null; + }, + + setLoop: function (options) { + if (options.loop === true) { + this.loop = 9999999; + } else if (typeof options.loop === "number") { + this.loop = +options.loop - 1; + } else { + this.loop = false; + } + }, + + update: function (options) { + this.setLoop(options); + if ("volume" in options) { + this.volume = options.volume; + } + }, + + play: function (options) { + if (options) { + this.update(options); + } + + if (!this.multiplay && this.playing) { + return; + } + + this.gain = audio.createGain(); + this.source = audio.createBufferSource(); + this.source.buffer = this.buffer; + this.source.connect(this.gain); + this.gain.connect(audio.destination); + this.gain.gain.value = this.volume; + + this.source.onended = this.ended.bind(this); + + this._play(); + }, + + _play: function () { + var start, + end; + + if (this.paused) { + start = this.start + this.time_offset; + end = this.end - this.time_offset; + } else { + start = this.start; + end = this.end; + } + + if (end <= 0) { + this.clear(); + return; + } + + if (typeof this.source.start === "function") { + this.source.start(0, start, end); + } else { + this.source.noteOn(0, start, end); + } + + this.playing = true; + this.paused = false; + this.time_started = new Date().valueOf(); + }, + + stop: function () { + if (this.playing && this.source) { + if (typeof this.source.stop === "function") { + this.source.stop(0); + } else { + this.source.noteOff(0); + } + } + + this.clear(); + }, + + pause: function () { + if (this.paused) { + this.play(); + return; + } + + if (!this.playing) { + return; + } + + this.source && this.source.stop(0); + this.paused = true; + }, + + ended: function () { + this.playing = false; + this.time_ended = new Date().valueOf(); + this.time_played = (this.time_ended - this.time_started) / 1000; + this.time_offset += this.time_played; + + if (this.time_offset >= this.end || this.end - this.time_offset < 0.015) { + this._ended(); + this.clear(); + + if (this.loop) { + this.loop--; + this.play(); + } + } + }, + + _ended: function () { + var config = { + name: this.name, + alias: this.alias, + part: this.sprite_part, + start: this.start, + duration: this.end + }; + + if (this.ended_callback && typeof this.ended_callback === "function") { + this.ended_callback.call(this.scope, config); + } + }, + + clear: function () { + this.time_played = 0; + this.time_offset = 0; + this.paused = false; + this.playing = false; + }, + + setVolume: function (options) { + this.volume = options.volume; + + if (this.gain) { + this.gain.gain.value = this.volume; + } + } + }; + + if (audio) { + return; + } + + + + /** + * Fallback for HTML5 audio + * - for not so modern browsers + */ + + var checkSupport = function () { + var sound = new Audio(), + can_play_mp3 = sound.canPlayType('audio/mpeg'), + can_play_ogg = sound.canPlayType('audio/ogg'), + can_play_aac = sound.canPlayType('audio/mp4; codecs="mp4a.40.2"'), + item, i; + + for (i = 0; i < settings.supported.length; i++) { + item = settings.supported[i]; + + if (!can_play_mp3 && item === "mp3") { + settings.supported.splice(i, 1); + } + + if (!can_play_ogg && item === "ogg") { + settings.supported.splice(i, 1); + } + + if (!can_play_aac && item === "aac") { + settings.supported.splice(i, 1); + } + + if (!can_play_aac && item === "mp4") { + settings.supported.splice(i, 1); + } + } + + sound = null; + }; + checkSupport(); + + + + Sound.prototype = { + init: function (options) { + if (options) { + extend(options, this.options); + } + + this.inited = true; + + if (this.options.preload) { + this.load(); + } + }, + + destroy: function () { + var stream; + + for (i in this.streams) { + stream = this.streams[i]; + + if (stream) { + stream.destroy(); + stream = null; + } + } + this.streams = {}; + this.loaded = false; + this.inited = false; + }, + + load: function () { + var part; + + this.options.preload = true; + this.options._ready = this.ready; + this.options._scope = this; + + if (this.options.sprite) { + + for (i in this.options.sprite) { + part = this.options.sprite[i]; + + this.options.start = part[0]; + this.options.end = part[1]; + + this.streams[i] = new Stream(this.options, i); + } + + } else { + + this.streams[0] = new Stream(this.options); + + } + }, + + ready: function (duration) { + if (this.loaded) { + return; + } + + this.loaded = true; + + var config = { + name: this.options.name, + alias: this.options.alias, + ext: this.options.supported[this.ext], + duration: duration + }; + + if (this.options.ready_callback && typeof this.options.ready_callback === "function") { + this.options.ready_callback.call(this.options.scope, config); + } + + if (this.autoplay) { + this.autoplay = false; + this.play(); + } + }, + + play: function (options) { + if (!this.inited) { + return; + } + + delete this.options.part; + + if (options) { + extend(options, this.options); + } + + console.log(1); + if (!this.loaded) { + if (!this.options.preload) { + this.autoplay = true; + this.load(); + } else { + this.autoplay = true; + } + + return; + } + + if (this.options.sprite) { + if (this.options.part) { + this.streams[this.options.part].play(this.options); + } else { + for (i in this.options.sprite) { + this.streams[i].play(this.options); + } + } + } else { + this.streams[0].play(this.options); + } + }, + + stop: function (options) { + if (!this.inited) { + return; + } + + if (this.options.sprite) { + + if (options) { + this.streams[options.part].stop(); + } else { + for (i in this.options.sprite) { + this.streams[i].stop(); + } + } + + } else { + this.streams[0].stop(); + } + }, + + pause: function (options) { + if (!this.inited) { + return; + } + + if (this.options.sprite) { + + if (options) { + this.streams[options.part].pause(); + } else { + for (i in this.options.sprite) { + this.streams[i].pause(); + } + } + + } else { + this.streams[0].pause(); + } + }, + + volume: function (options) { + var stream; + + if (options) { + extend(options, this.options); + } else { + return; + } + + if (this.options.sprite) { + if (this.options.part) { + stream = this.streams[this.options.part]; + stream && stream.setVolume(this.options); + } else { + for (i in this.options.sprite) { + stream = this.streams[i]; + stream && stream.setVolume(this.options); + } + } + } else { + stream = this.streams[0]; + stream && stream.setVolume(this.options); + } + } + }; + + + + Stream = function (options, sprite_part) { + this.name = options.name; + this.alias = options.alias; + this.sprite_part = sprite_part; + + this.multiplay = options.multiplay; + this.volume = options.volume; + this.preload = options.preload; + this.path = settings.path; + this.start = options.start || 0; + this.end = options.end || 0; + this.scope = options.scope; + this.ended_callback = options.ended_callback; + + this._scope = options._scope; + this._ready = options._ready; + + this.setLoop(options); + + this.sound = null; + this.url = null; + this.loaded = false; + + this.start_time = 0; + this.paused_time = 0; + this.played_time = 0; + + this.init(); + }; + + Stream.prototype = { + init: function () { + this.sound = new Audio(); + this.sound.volume = this.volume; + + this.createUrl(); + + this.sound.addEventListener("ended", this.ended.bind(this), false); + this.sound.addEventListener("canplaythrough", this.can_play_through.bind(this), false); + this.sound.addEventListener("timeupdate", this._update.bind(this), false); + + this.load(); + }, + + destroy: function () { + this.stop(); + + this.sound.removeEventListener("ended", this.ended.bind(this), false); + this.sound.removeEventListener("canplaythrough", this.can_play_through.bind(this), false); + this.sound.removeEventListener("timeupdate", this._update.bind(this), false); + + this.sound = null; + this.loaded = false; + }, + + createUrl: function () { + var rand = new Date().valueOf(); + this.url = this.path + encodeURIComponent(this.name) + "." + settings.supported[0] + "?" + rand; + }, + + can_play_through: function () { + if (this.preload) { + this.ready(); + } + }, + + load: function () { + this.sound.src = this.url; + this.sound.preload = this.preload ? "auto" : "none"; + if (this.preload) { + this.sound.load(); + } + }, + + setLoop: function (options) { + if (options.loop === true) { + this.loop = 9999999; + } else if (typeof options.loop === "number") { + this.loop = +options.loop - 1; + } else { + this.loop = false; + } + }, + + update: function (options) { + this.setLoop(options); + + if ("volume" in options) { + this.volume = options.volume; + } + }, + + ready: function () { + if (this.loaded || !this.sound) { + return; + } + + this.loaded = true; + this._ready.call(this._scope, this.sound.duration); + + if (!this.end) { + this.end = this.sound.duration; + } + }, + + play: function (options) { + if (options) { + this.update(options); + } + + if (!this.multiplay && this.playing) { + return; + } + + this._play(); + }, + + _play: function () { + if (this.paused) { + this.paused = false; + } else { + try { + this.sound.currentTime = this.start; + } catch (e) {} + } + + this.playing = true; + this.start_time = new Date().valueOf(); + this.sound.volume = this.volume; + this.sound.play(); + }, + + stop: function () { + if (!this.playing) { + return; + } + + this.playing = false; + this.paused = false; + this.sound.pause(); + this.clear(); + + try { + this.sound.currentTime = this.start; + } catch (e) {} + }, + + pause: function () { + if (this.paused) { + this._play(); + } else { + this.playing = false; + this.paused = true; + this.sound.pause(); + this.paused_time = new Date().valueOf(); + this.played_time += this.paused_time - this.start_time; + } + }, + + _update: function () { + if (!this.start_time) { + return; + } + + var current_time = new Date().valueOf(), + played_time = current_time - this.start_time, + played = (this.played_time + played_time) / 1000; + + if (played >= this.end) { + if (this.playing) { + this.stop(); + this._ended(); + } + } + }, + + ended: function () { + if (this.playing) { + this.stop(); + this._ended(); + } + }, + + _ended: function () { + this.playing = false; + + var config = { + name: this.name, + alias: this.alias, + part: this.sprite_part, + start: this.start, + duration: this.end + }; + + if (this.ended_callback && typeof this.ended_callback === "function") { + this.ended_callback.call(this.scope, config); + } + + if (this.loop) { + setTimeout(this.looper.bind(this), 15); + } + }, + + looper: function () { + this.loop--; + this.play(); + }, + + clear: function () { + this.start_time = 0; + this.played_time = 0; + this.paused_time = 0; + }, + + setVolume: function (options) { + this.volume = options.volume; + + if (this.sound) { + this.sound.volume = this.volume; + } + } + }; + +} (window, navigator, window.jQuery || window.$)); diff --git a/transitclockWebapp/src/main/webapp/holding/js/transiTimeEvents.js b/transitclockWebapp/src/main/webapp/holding/js/transiTimeEvents.js new file mode 100755 index 000000000..8185c5089 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/holding/js/transiTimeEvents.js @@ -0,0 +1,29 @@ +function getStopEvents(host, stopId, processData, data) +{ + var baseurl = "http://"+host+"/api/v1/key/f78a2e9a/agency/ASC/command/stoparrivaldeparturecachedata?"; + + var xmlhttp = new XMLHttpRequest(); + + xmlhttp.onreadystatechange = function() { + + if (this.readyState == 4 && this.status == 200) + { + var response = JSON.parse(this.responseText); + + processData(data, response); + } + }; + + var queryObject = { + stopid:null + }; + + queryObject.stopid=stopId; + + + + var encodedQueryObject = $.param( queryObject ); + + xmlhttp.open("GET", baseurl+encodedQueryObject, true); + xmlhttp.send(); +}; diff --git a/transitclockWebapp/src/main/webapp/holding/js/transiTimeHolding.js b/transitclockWebapp/src/main/webapp/holding/js/transiTimeHolding.js new file mode 100755 index 000000000..80a246c30 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/holding/js/transiTimeHolding.js @@ -0,0 +1,57 @@ +function alertHolding(paramTest) { + alert(paramTest.tosay); +}; +function getHoldingTime(host, agencyId, stopId, vehicleId, processHoldingTime, data) +{ + console.log("Calling getHoldingTime."); + + var baseurl = "http://"+host+"/api/v1/key/f78a2e9a/agency/"+agencyId+"/command/getholdingtime?"; + + var xmlhttp = new XMLHttpRequest(); + + xmlhttp.onreadystatechange = function() { + + if (this.readyState == 4 && this.status == 200) { + + var holdingTime = JSON.parse(this.responseText); + + $("#result").text(this.responseText); + + processHoldingTime(data, vehicleId, holdingTime); + } + }; + + var queryObject = { + stopId:null, + vehicleId:null + }; + queryObject.stopId=stopId; + queryObject.vehicleId=vehicleId; + + var encodedQueryObject = $.param( queryObject ); + + xmlhttp.open("GET", baseurl+encodedQueryObject, true); + xmlhttp.send(); + +}; +function getHoldingTimeKeys(host, agencyId, processHoldingTimeKeys, data) +{ + console.log("Calling getHoldingTimeKeys."); + var baseurl = "http://"+host+"/api/v1/key/f78a2e9a/agency/"+agencyId+"/command/holdingtimecachekeys"; + + var xmlhttp = new XMLHttpRequest(); + + xmlhttp.onreadystatechange = function() { + + if (this.readyState == 4 && this.status == 200) + { + var response = JSON.parse(this.responseText); + + $("#holdingtimes").text(this.responseText); + + processHoldingTimeKeys(data, response); + } + }; + xmlhttp.open("GET", baseurl, true); + xmlhttp.send(); +} \ No newline at end of file diff --git a/transitclockWebapp/src/main/webapp/holding/js/transiTimePredictions.js b/transitclockWebapp/src/main/webapp/holding/js/transiTimePredictions.js new file mode 100755 index 000000000..b7d669511 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/holding/js/transiTimePredictions.js @@ -0,0 +1,130 @@ + + + +function getStopPathPredictions(host, tripId, stopPathIndex, algorithm, processData, stopPathData) +{ + + var baseurl= "http://"+host+"/api/v1/key/f78a2e9a/agency/ASC/command/getstoppathpredictions?"; + + var xmlhttp = new XMLHttpRequest(); + + xmlhttp.onreadystatechange = function() { + + if (this.readyState == 4 && this.status == 200) { + var predictions = JSON.parse(this.responseText); + processData(predictions, stopPathData); + } + }; + var queryObject = { + tripId:null, + stopPathIndex:null, + algorithm:null, + + }; + + queryObject.tripId=tripId; + queryObject.stopPathIndex=stopPathIndex; + queryObject.algorithm=algorithm; + + + var encodedQueryObject = $.param( queryObject ); + + xmlhttp.open("GET", baseurl+encodedQueryObject, true); + xmlhttp.send(); + +}; + +function getStopPathTravelTimes(host, tripId, stopPathIndex, processData , stopPathData) +{ + var baseurl = "http://"+host+"/api/v1/key/f78a2e9a/agency/ASC/command/triparrivaldeparturecachedata?"; + + var xmlhttp = new XMLHttpRequest(); + + xmlhttp.onreadystatechange = function() { + + if (this.readyState == 4 && this.status == 200) { + + var response = JSON.parse(this.responseText); + stopPath = findStopPathDuration(response, $("#stopPathIndex").val()); + processData(stopPath, stopPathData); + } + }; + + var queryObject = { + tripId:null, + stopPathIndex:null, + + + + }; + + queryObject.tripId=tripId; + queryObject.stopPathIndex=stopPathIndex; + + + var encodedQueryObject = $.param( queryObject ); + + xmlhttp.open("GET", baseurl+encodedQueryObject, true); + xmlhttp.send(); +}; + +function findStopPathDuration(results, stopPathIndex) { + var result, departure, arrival, duration; + + for (var i=0; i 0) + baseurl = "http://"+host+"/api/v1/key/f78a2e9a/agency/ASC/command/trip?tripId="+tripId; + + var xmlhttp = new XMLHttpRequest(); + + xmlhttp.onreadystatechange = function() { + + if (this.readyState == 4 && this.status == 200) + { + var tripDetails = JSON.parse(this.responseText); + processData(tripDetails, stopPathData); + } + }; + xmlhttp.open("GET", baseurl, true); + xmlhttp.send(); +}; \ No newline at end of file diff --git a/transitclockWebapp/src/main/webapp/holding/js/transiTimeUtils.js b/transitclockWebapp/src/main/webapp/holding/js/transiTimeUtils.js new file mode 100755 index 000000000..438c0dafe --- /dev/null +++ b/transitclockWebapp/src/main/webapp/holding/js/transiTimeUtils.js @@ -0,0 +1,29 @@ +function unique(arr) { + var u = {}, a = []; + for(var i = 0, l = arr.length; i < l; ++i){ + if(!u.hasOwnProperty(arr[i])) { + a.push(arr[i]); + u[arr[i]] = 1; + } + } + return a; +}; +function getCurrentServerTime(host, agencyId, processTime, data) +{ + console.log("Calling getCurrentServerTime."); + + var baseurl = "http://"+host+"/api/v1/key/f78a2e9a/agency/"+agencyId+"/command/currentServerTime"; + + var xmlhttp = new XMLHttpRequest(); + + xmlhttp.onreadystatechange = function() { + + if (this.readyState == 4 && this.status == 200) + { + var currentservertime = JSON.parse(this.responseText); + processTime(data, currentservertime); + } + }; + xmlhttp.open("GET", baseurl, true); + xmlhttp.send(); +}; \ No newline at end of file diff --git a/transitclockWebapp/src/main/webapp/holding/js/transiTimeVehicles.js b/transitclockWebapp/src/main/webapp/holding/js/transiTimeVehicles.js new file mode 100755 index 000000000..46124fd0b --- /dev/null +++ b/transitclockWebapp/src/main/webapp/holding/js/transiTimeVehicles.js @@ -0,0 +1,69 @@ +function getVehicleDetailsByRoute(host, routeId, processData) +{ + console.log("Calling getVehicleDetailsByRoute."); + var baseurl = "http://"+host+"/api/v1/key/f78a2e9a/agency/ASC/command/vehiclesDetails"; + if(routeId.length > 0) + baseurl = "http://"+host+"/api/v1/key/f78a2e9a/agency/ASC/command/vehiclesDetails?r="+routeId; + + var xmlhttp = new XMLHttpRequest(); + + xmlhttp.onreadystatechange = function() { + + if (this.readyState == 4 && this.status == 200) + { + var vehicleDetails = JSON.parse(this.responseText); + processData(vehicleDetails); + } + }; + xmlhttp.open("GET", baseurl, true); + xmlhttp.send(); +}; +function getVehicleDetails(host, agencyId, vehicleId, processData, data) +{ + console.log("Calling getVehicleDetails."); + var baseurl = "http://"+host+"/api/v1/key/f78a2e9a/agency/"+agencyId+"/command/vehiclesDetails"; + if(vehicleId.length > 0) + baseurl = "http://"+host+"/api/v1/key/f78a2e9a/agency/"+agencyId+"/command/vehiclesDetails?v="+vehicleId; + + var xmlhttp = new XMLHttpRequest(); + + xmlhttp.onreadystatechange = function() { + + if (this.readyState == 4 && this.status == 200) + { + var vehicleDetails = JSON.parse(this.responseText); + processData(data, vehicleDetails); + } + }; + xmlhttp.open("GET", baseurl, true); + xmlhttp.send(); +}; +function getNextVehicleArrivalPredictions(host, agencyId, routeId, stopId, processData, data) +{ + console.log("Calling getNextVehicleArrivalPredictions."); + + //http://ec2-54-187-31-47.us-west-2.compute.amazonaws.com:8080/api/v1/key/f78a2e9a/agency/ASC/command/predictions?format=json&rs=ASC_4560585%7C4560595 + var baseurl = "http://"+host+"/api/v1/key/f78a2e9a/agency/"+agencyId+"/command/predictions?format=json&rs="+routeId+"%7C"+stopId; + + var xmlhttp = new XMLHttpRequest(); + + xmlhttp.onreadystatechange = function() { + + if (this.readyState == 4 && this.status == 200) + { + var vehicleDetails = JSON.parse(this.responseText); + processData(vehicleDetails, data); + } + }; + xmlhttp.open("GET", baseurl, true); + xmlhttp.send(); +}; +function getCurrentStopPathIndex(stopPathData) +{ + for(var i=0; i< stopPathData.trip.schedule.length; i++) + { + if(stopPathData.trip.schedule[i].stopId===stopPathData.vehicle.nextStopId) + return i; + } + return -1; +}; \ No newline at end of file diff --git a/transitclockWebapp/src/main/webapp/holding/predictions.html b/transitclockWebapp/src/main/webapp/holding/predictions.html new file mode 100755 index 000000000..04dbc0eac --- /dev/null +++ b/transitclockWebapp/src/main/webapp/holding/predictions.html @@ -0,0 +1,184 @@ + + + + + + + + + + + + transiTime Prediction Quality Analysis + + + + +
    Prediction Quality Analysis
    +
    +
    + + +
    +
    + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    NameValue
    tripId:
    stopPathIndex:
    algorithm:
    Calculate MAPE values:
    + +
    + + + + + + + + + + + + +
    +
    +
    +
    + + + \ No newline at end of file diff --git a/transitclockWebapp/src/main/webapp/holding/reports/params/dateFromToTime.jsp b/transitclockWebapp/src/main/webapp/holding/reports/params/dateFromToTime.jsp new file mode 100644 index 000000000..0aa964af5 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/holding/reports/params/dateFromToTime.jsp @@ -0,0 +1,77 @@ +<%-- For specifying a begin date & time and an end date & time --%> + + + + + + +<% +String currentDateStr = org.transitclock.utils.Time.dateStr(new java.util.Date()); +%> + +
    + + +
    + +
    + + (hh:mm) +
    + +
    + + (hh:mm) +
    \ No newline at end of file diff --git a/transitclockWebapp/src/main/webapp/holding/reports/realTimeSchAdhByVehicle.jsp b/transitclockWebapp/src/main/webapp/holding/reports/realTimeSchAdhByVehicle.jsp new file mode 100644 index 000000000..a63f7085e --- /dev/null +++ b/transitclockWebapp/src/main/webapp/holding/reports/realTimeSchAdhByVehicle.jsp @@ -0,0 +1,64 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1"%> +<%@ page import="org.transitclock.utils.web.WebUtils" %> + +<% +String agencyId = request.getParameter("a"); +if (agencyId == null || agencyId.isEmpty()) { + response.getWriter().write("You must specify agency in query string (e.g. ?a=mbta)"); + return; +} +%> + + + <%@include file="/template/includes.jsp" %> + +Real Time Schedule Adherence By Vehicle Report + + + + + + +<%@include file="/template/header.jsp" %> +
    Real Time Schedule Adherence By Vehicle
    + + +
    VehicleBlockRouteLast Updated OnSchedule Adherence
    + + \ No newline at end of file diff --git a/transitclockWebapp/src/main/webapp/holding/reports/realTimeSchAdhByVehicleData.jsp b/transitclockWebapp/src/main/webapp/holding/reports/realTimeSchAdhByVehicleData.jsp new file mode 100644 index 000000000..6eaca7fd5 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/holding/reports/realTimeSchAdhByVehicleData.jsp @@ -0,0 +1,14 @@ +<%@ page language="java" contentType="application/json; charset=ISO-8859-1" + pageEncoding="ISO-8859-1"%> +<%@ page import="org.transitclock.reports.SchAdhJsonQuery" %> +<% + +// Get the params +String agencyId = request.getParameter("a"); +String vehicleId = request.getParameter("v"); + +// Query db and get JSON string +String jsonString = SchAdhJsonQuery.getJson(agencyId, vehicleId); +response.setHeader("Access-Control-Allow-Origin", "*"); +response.getWriter().write(jsonString); +%> \ No newline at end of file diff --git a/transitimeWebapp/src/main/webapp/reports/scheduleVertStopsParams.jsp b/transitclockWebapp/src/main/webapp/holding/reports/realTimeSchAdhByVehicleParams.jsp similarity index 85% rename from transitimeWebapp/src/main/webapp/reports/scheduleVertStopsParams.jsp rename to transitclockWebapp/src/main/webapp/holding/reports/realTimeSchAdhByVehicleParams.jsp index 2f12d29b0..47836cdc4 100644 --- a/transitimeWebapp/src/main/webapp/reports/scheduleVertStopsParams.jsp +++ b/transitclockWebapp/src/main/webapp/holding/reports/realTimeSchAdhByVehicleParams.jsp @@ -6,27 +6,26 @@ <%@include file="/template/includes.jsp" %> Specify Parameters - + - + - <%@include file="/template/header.jsp" %> -
    - Select Parameters for Schedule Report + Select Parameters for Schedule Adherence by Vehicle
    - +
    -
    + <%-- For passing agency param to the report --%> "> - - + + + diff --git a/transitclockWebapp/src/main/webapp/holding/reports/tripBlockRoute.jsp b/transitclockWebapp/src/main/webapp/holding/reports/tripBlockRoute.jsp new file mode 100644 index 000000000..b3a00b96e --- /dev/null +++ b/transitclockWebapp/src/main/webapp/holding/reports/tripBlockRoute.jsp @@ -0,0 +1,80 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1"%> +<%@ page import="org.transitclock.utils.web.WebUtils" %> + +<% +String agencyId = request.getParameter("a"); +if (agencyId == null || agencyId.isEmpty()) { + response.getWriter().write("You must specify agency in query string (e.g. ?a=mbta)"); + return; +} +%> + + + <%@include file="/template/includes.jsp" %> + +Trip Block By Route Report + + + + + + +<%@include file="/template/header.jsp" %> +
    Trip Block By Route
    + + +
    RouteBlockTripStart TimeEnd Time
    + + \ No newline at end of file diff --git a/transitclockWebapp/src/main/webapp/holding/reports/tripBlockRouteData.jsp b/transitclockWebapp/src/main/webapp/holding/reports/tripBlockRouteData.jsp new file mode 100644 index 000000000..5f5a6490a --- /dev/null +++ b/transitclockWebapp/src/main/webapp/holding/reports/tripBlockRouteData.jsp @@ -0,0 +1,17 @@ +<%@ page language="java" contentType="application/json; charset=ISO-8859-1" + pageEncoding="ISO-8859-1"%> +<%@ page import="org.transitclock.reports.TripBlockJsonQuery" %> +<% + +// Get the params +String agencyId = request.getParameter("a"); +String routeId = request.getParameter("r"); +String date = request.getParameter("date"); +String beginTime = request.getParameter("beginTime"); +String endTime = request.getParameter("endTime"); + +// Query db and get JSON string +String jsonString = TripBlockJsonQuery.getJson(agencyId, date, beginTime, endTime, routeId); +response.setHeader("Access-Control-Allow-Origin", "*"); +response.getWriter().write(jsonString); +%> \ No newline at end of file diff --git a/transitimeWebapp/src/main/webapp/reports/scheduleHorizStopsParams.jsp b/transitclockWebapp/src/main/webapp/holding/reports/tripBlockRouteParams.jsp similarity index 72% rename from transitimeWebapp/src/main/webapp/reports/scheduleHorizStopsParams.jsp rename to transitclockWebapp/src/main/webapp/holding/reports/tripBlockRouteParams.jsp index 9007b30ed..74eea6a27 100644 --- a/transitimeWebapp/src/main/webapp/reports/scheduleHorizStopsParams.jsp +++ b/transitclockWebapp/src/main/webapp/holding/reports/tripBlockRouteParams.jsp @@ -6,29 +6,31 @@ <%@include file="/template/includes.jsp" %> Specify Parameters - - + + - - + + <%@include file="/template/header.jsp" %>
    - Select Parameters for Schedule Report + Select Parameters for Trip Block By Route Report
    -
    + <%-- For passing agency param to the report --%> "> + + - +
    diff --git a/transitclockWebapp/src/main/webapp/holding/singlestopholding.html b/transitclockWebapp/src/main/webapp/holding/singlestopholding.html new file mode 100755 index 000000000..1562d4473 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/holding/singlestopholding.html @@ -0,0 +1,517 @@ + + + + + + + + + + + + +Vehicle Holding Times + + + + + +
    + + + + + + + + + +
    + + + Remaining Holding Time +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Vehicle ID +
    +
    Time of Arrival +
    +
    Time of Departure +
    +
    Next departure +
    +
    Next arrival +
    +
    Predicted arrival +
    +
    +
    +
    +
    Developed by Sean Óg Crudden (og.crudden@gmail.com) and Simon Berrebi (simon@berrebi.net)
    +
    + +
    + + + Real-Time Vehicle Location + + +
    + +
    + + + +
    + +
    +
    + + +
    + + +
    + + + + + \ No newline at end of file diff --git a/transitclockWebapp/src/main/webapp/holding/sounds/ship_bell.mp3 b/transitclockWebapp/src/main/webapp/holding/sounds/ship_bell.mp3 new file mode 100644 index 000000000..eddcd2c30 Binary files /dev/null and b/transitclockWebapp/src/main/webapp/holding/sounds/ship_bell.mp3 differ diff --git a/transitclockWebapp/src/main/webapp/holding/sounds/store_door.mp3 b/transitclockWebapp/src/main/webapp/holding/sounds/store_door.mp3 new file mode 100644 index 000000000..fedb4d99d Binary files /dev/null and b/transitclockWebapp/src/main/webapp/holding/sounds/store_door.mp3 differ diff --git a/transitimeWebapp/src/main/webapp/javascript/air-datepicker/README.txt b/transitclockWebapp/src/main/webapp/javascript/air-datepicker/README.txt similarity index 100% rename from transitimeWebapp/src/main/webapp/javascript/air-datepicker/README.txt rename to transitclockWebapp/src/main/webapp/javascript/air-datepicker/README.txt diff --git a/transitimeWebapp/src/main/webapp/javascript/air-datepicker/css/datepicker.css b/transitclockWebapp/src/main/webapp/javascript/air-datepicker/css/datepicker.css similarity index 100% rename from transitimeWebapp/src/main/webapp/javascript/air-datepicker/css/datepicker.css rename to transitclockWebapp/src/main/webapp/javascript/air-datepicker/css/datepicker.css diff --git a/transitimeWebapp/src/main/webapp/javascript/air-datepicker/css/datepicker.min.css b/transitclockWebapp/src/main/webapp/javascript/air-datepicker/css/datepicker.min.css similarity index 100% rename from transitimeWebapp/src/main/webapp/javascript/air-datepicker/css/datepicker.min.css rename to transitclockWebapp/src/main/webapp/javascript/air-datepicker/css/datepicker.min.css diff --git a/transitimeWebapp/src/main/webapp/javascript/air-datepicker/js/datepicker.js b/transitclockWebapp/src/main/webapp/javascript/air-datepicker/js/datepicker.js similarity index 100% rename from transitimeWebapp/src/main/webapp/javascript/air-datepicker/js/datepicker.js rename to transitclockWebapp/src/main/webapp/javascript/air-datepicker/js/datepicker.js diff --git a/transitimeWebapp/src/main/webapp/javascript/air-datepicker/js/datepicker.min.js b/transitclockWebapp/src/main/webapp/javascript/air-datepicker/js/datepicker.min.js similarity index 100% rename from transitimeWebapp/src/main/webapp/javascript/air-datepicker/js/datepicker.min.js rename to transitclockWebapp/src/main/webapp/javascript/air-datepicker/js/datepicker.min.js diff --git a/transitimeWebapp/src/main/webapp/javascript/air-datepicker/js/i18n/datepicker.en.js b/transitclockWebapp/src/main/webapp/javascript/air-datepicker/js/i18n/datepicker.en.js similarity index 100% rename from transitimeWebapp/src/main/webapp/javascript/air-datepicker/js/i18n/datepicker.en.js rename to transitclockWebapp/src/main/webapp/javascript/air-datepicker/js/i18n/datepicker.en.js diff --git a/transitimeWebapp/src/main/webapp/javascript/jquery-dateFormat.min.js b/transitclockWebapp/src/main/webapp/javascript/jquery-dateFormat.min.js similarity index 100% rename from transitimeWebapp/src/main/webapp/javascript/jquery-dateFormat.min.js rename to transitclockWebapp/src/main/webapp/javascript/jquery-dateFormat.min.js diff --git a/transitclockWebapp/src/main/webapp/javascript/jquery-timepicker/README.md b/transitclockWebapp/src/main/webapp/javascript/jquery-timepicker/README.md new file mode 100644 index 000000000..2e14d0977 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/javascript/jquery-timepicker/README.md @@ -0,0 +1,283 @@ +Timepicker Plugin for jQuery +======================== + +[timepicker screenshot](http://jonthornton.github.com/jquery-timepicker) + +[See a demo and examples here](http://jonthornton.github.com/jquery-timepicker) + +jquery.timepicker is a lightweight timepicker plugin for jQuery inspired by Google Calendar. It supports both mouse and keyboard navigation, and weighs in at 2.7kb minified and gzipped. + +Requirements +------------ +* [jQuery](http://jquery.com/) (>= 1.7) + +Usage +----- + +```javascript +$('.some-time-inputs').timepicker(options); +``` + +Include `jquery.timepicker.css` and `jquery.timepicker.min.js` in your page. + +```options``` is an optional javascript object with parameters explained below. + +You can also set options as [data attributes](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Using_data_attributes) on the intput elements, like ``````. Timepicker still needs to be initialized by calling ```$('#someElement').timepicker();```. + +The defaults for all options are exposed through the ```$.fn.timepicker.defaults``` object. Properties changed in this object (same properties configurable through the constructor) will take effect for every instance created after the change. + +Options +------- + +- **appendTo** +Override where the dropdown is appended. +Takes either a `string` to use as a selector, a `function` that gets passed the clicked input element as argument or a jquery `object` to use directly. +*default: "body"* + +- **className** +A class name to apply to the HTML element that contains the timepicker dropdown. +*default: null* + +- **closeOnWindowScroll** +Close the timepicker when the window is scrolled. (Replicates ```` control. This is ideal for small screen devices, or if you want to prevent the user from entering arbitrary values. This option is not compatible with the following options: ```appendTo```, ```closeOnWindowScroll```, ```disableTouchKeyboard```, ```forceRoundTime```, ```scrollDefault```, ```selectOnBlur```, ```typeAheadHighlight```. +*default: false* + +Methods +------- + +- **getSecondsFromMidnight** +Get the time as an integer, expressed as seconds from 12am. + + ```javascript + $('#getTimeExample').timepicker('getSecondsFromMidnight'); + ``` + +- **getTime** +Get the time using a Javascript Date object, relative to a Date object (default: Jan 1, 1970). + + ```javascript + $('#getTimeExample').timepicker('getTime'); + $('#getTimeExample').timepicker('getTime', new Date()); + ``` + + You can get the time as a string using jQuery's built-in ```val()``` function: + + ```javascript + $('#getTimeExample').val(); + ``` + +- **hide** +Close the timepicker dropdown. + + ```javascript + $('#hideExample').timepicker('hide'); + ``` + +- **option** +Change the settings of an existing timepicker. Calling ```option``` on a visible timepicker will cause the picker to be hidden. + + ```javascript + $('#optionExample').timepicker({ 'timeFormat': 'g:ia' }); // initialize the timepicker sometime earlier in your code + ... + $('#optionExample').timepicker('option', 'minTime', '2:00am'); + $('#optionExample').timepicker('option', { 'minTime': '4:00am', 'timeFormat': 'H:i' }); + ``` + +- **remove** +Unbind an existing timepicker element. + + ```javascript + $('#removeExample').timepicker('remove'); + ``` + +- **setTime** +Set the time using a Javascript Date object. + + ```javascript + $('#setTimeExample').timepicker('setTime', new Date()); + ``` + +- **show** +Display the timepicker dropdown. + + ```javascript + $('#showExample').timepicker('show'); + ``` + +Events +------ + +- **change** +The native ```onChange``` event will fire any time the input value is updated, whether by selection from the timepicker list or manual entry into the text input. Your code should bind to ```change``` after initializing timepicker, or use [event delegation](http://api.jquery.com/on/). + +- **changeTime** +Called after a valid time value is entered or selected. See ```timeFormatError``` and ```timeRangeError``` for error events. Fires before ```change``` event. + +- **hideTimepicker** +Called after the timepicker is closed. + +- **selectTime** +Called after a time value is selected from the timepicker list. Fires before ```change``` event. + +- **showTimepicker** +Called after the timepicker is shown. + +- **timeFormatError** +Called if an unparseable time string is manually entered into the timepicker input. Fires before ```change``` event. + +- **timeRangeError** +Called if a maxTime, minTime, or disableTimeRanges is set and an invalid time is manually entered into the timepicker input. Fires before ```change``` event. + +The `selectTime` and `hideTimepicker` events fire slightly differently when using the `useSelect` option. See https://github.com/jonthornton/jquery-timepicker/issues/427 for more information. + +Theming +------- + +Sample markup with class names: + +```html + +... +
    +
      +
    • 12:00am
    • +
    • 12:30am
    • + ... +
    • 4:30pm
    • +
    • 5:00pm
    • +
    • 5:30pm
    • +
    • 6:00pm (1 hour)
    • +
    • 6:30pm
    • + ... +
    • 11:30pm
    • +
    +
    +``` + +The `ui-timepicker-positioned-top` class will be applied only when the dropdown is positioned above the input. + +## Packaging + +Install from [Bower](http://bower.io/) as ```jquery-timepicker-jt```. + +An AngularJS directive is available at https://github.com/Recras/angular-jquery-timepicker + +Available via CDN at [https://cdnjs.com/libraries/jquery-timepicker](https://cdnjs.com/libraries/jquery-timepicker). + +Help +---- + +Submit a [GitHub Issues request](https://github.com/jonthornton/jquery-timepicker/issues/new). Please try provide code that demonstrates the problem; you can use [this jsFiddle](http://jsfiddle.net/jonthornton/28uvg/) as a starting point. + +Development guidelines +---------------------- + +1. Install dependencies (jquery + grunt) `npm install` +2. For sanity checks and minification run `grunt`, or just `grunt lint` to have the code linted + +jquery-timepicker follows [semantic versioning](http://semver.org/). + +- - - + +This software is made available under the open source MIT License. © 2014 [Jon Thornton](http://www.jonthornton.com) and [contributors](https://github.com/jonthornton/jquery-timepicker/graphs/contributors) diff --git a/transitclockWebapp/src/main/webapp/javascript/jquery-timepicker/jquery.timepicker.css b/transitclockWebapp/src/main/webapp/javascript/jquery-timepicker/jquery.timepicker.css new file mode 100644 index 000000000..cd75f13f8 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/javascript/jquery-timepicker/jquery.timepicker.css @@ -0,0 +1,72 @@ +.ui-timepicker-wrapper { + overflow-y: auto; + height: 150px; + width: 6.5em; + background: #fff; + border: 1px solid #ddd; + -webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2); + -moz-box-shadow:0 5px 10px rgba(0,0,0,0.2); + box-shadow:0 5px 10px rgba(0,0,0,0.2); + outline: none; + z-index: 10001; + margin: 0; +} + +.ui-timepicker-wrapper.ui-timepicker-with-duration { + width: 13em; +} + +.ui-timepicker-wrapper.ui-timepicker-with-duration.ui-timepicker-step-30, +.ui-timepicker-wrapper.ui-timepicker-with-duration.ui-timepicker-step-60 { + width: 11em; +} + +.ui-timepicker-list { + margin: 0; + padding: 0; + list-style: none; +} + +.ui-timepicker-duration { + margin-left: 5px; color: #888; +} + +.ui-timepicker-list:hover .ui-timepicker-duration { + color: #888; +} + +.ui-timepicker-list li { + padding: 3px 0 3px 5px; + cursor: pointer; + white-space: nowrap; + color: #000; + list-style: none; + margin: 0; +} + +.ui-timepicker-list:hover .ui-timepicker-selected { + background: #fff; color: #000; +} + +li.ui-timepicker-selected, +.ui-timepicker-list li:hover, +.ui-timepicker-list .ui-timepicker-selected:hover { + background: #1980EC; color: #fff; +} + +li.ui-timepicker-selected .ui-timepicker-duration, +.ui-timepicker-list li:hover .ui-timepicker-duration { + color: #ccc; +} + +.ui-timepicker-list li.ui-timepicker-disabled, +.ui-timepicker-list li.ui-timepicker-disabled:hover, +.ui-timepicker-list li.ui-timepicker-selected.ui-timepicker-disabled { + color: #888; + cursor: default; +} + +.ui-timepicker-list li.ui-timepicker-disabled:hover, +.ui-timepicker-list li.ui-timepicker-selected.ui-timepicker-disabled { + background: #f2f2f2; +} diff --git a/transitclockWebapp/src/main/webapp/javascript/jquery-timepicker/jquery.timepicker.min.js b/transitclockWebapp/src/main/webapp/javascript/jquery-timepicker/jquery.timepicker.min.js new file mode 100644 index 000000000..008fe4572 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/javascript/jquery-timepicker/jquery.timepicker.min.js @@ -0,0 +1,7 @@ +/*! + * jquery-timepicker v1.8.9 - A jQuery timepicker plugin inspired by Google Calendar. It supports both mouse and keyboard navigation. + * Copyright (c) 2016 Jon Thornton - http://jonthornton.github.com/jquery-timepicker/ + * License: MIT + */ + +!function(a){"object"==typeof exports&&exports&&"object"==typeof module&&module&&module.exports===exports?a(require("jquery")):"function"==typeof define&&define.amd?define(["jquery"],a):a(jQuery)}(function(a){function b(a){var b=a[0];return b.offsetWidth>0&&b.offsetHeight>0}function c(b){if(b.minTime&&(b.minTime=t(b.minTime)),b.maxTime&&(b.maxTime=t(b.maxTime)),b.durationTime&&"function"!=typeof b.durationTime&&(b.durationTime=t(b.durationTime)),"now"==b.scrollDefault)b.scrollDefault=function(){return b.roundingFunction(t(new Date),b)};else if(b.scrollDefault&&"function"!=typeof b.scrollDefault){var c=b.scrollDefault;b.scrollDefault=function(){return b.roundingFunction(t(c),b)}}else b.minTime&&(b.scrollDefault=function(){return b.roundingFunction(b.minTime,b)});if("string"===a.type(b.timeFormat)&&b.timeFormat.match(/[gh]/)&&(b._twelveHourTime=!0),b.showOnFocus===!1&&-1!=b.showOn.indexOf("focus")&&b.showOn.splice(b.showOn.indexOf("focus"),1),b.disableTimeRanges.length>0){for(var d in b.disableTimeRanges)b.disableTimeRanges[d]=[t(b.disableTimeRanges[d][0]),t(b.disableTimeRanges[d][1])];b.disableTimeRanges=b.disableTimeRanges.sort(function(a,b){return a[0]-b[0]});for(var d=b.disableTimeRanges.length-1;d>0;d--)b.disableTimeRanges[d][0]<=b.disableTimeRanges[d-1][1]&&(b.disableTimeRanges[d-1]=[Math.min(b.disableTimeRanges[d][0],b.disableTimeRanges[d-1][0]),Math.max(b.disableTimeRanges[d][1],b.disableTimeRanges[d-1][1])],b.disableTimeRanges.splice(d,1))}return b}function d(b){var c=b.data("timepicker-settings"),d=b.data("timepicker-list");if(d&&d.length&&(d.remove(),b.data("timepicker-list",!1)),c.useSelect){d=a("

    +

    Or inline

    +
    +
    +
    Github
    https://github.com/kbwood/datepick
    +
    Bower
    kbw-datepick
    +
    + + diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-af.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-af.js new file mode 100755 index 000000000..6cecd960b --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-af.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Afrikaans localisation for jQuery Datepicker. + Written by Renier Pretorius and Ruediger Thiede. */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.af = { + monthNames: ['Januarie','Februarie','Maart','April','Mei','Junie', + 'Julie','Augustus','September','Oktober','November','Desember'], + monthNamesShort: ['Jan','Feb','Mrt','Apr','Mei','Jun', + 'Jul','Aug','Sep','Okt','Nov','Des'], + dayNames: ['Sondag','Maandag','Dinsdag','Woensdag','Donderdag','Vrydag','Saterdag'], + dayNamesShort: ['Son','Maan','Dins','Woens','Don','Vry','Sat'], + dayNamesMin: ['So','Ma','Di','Wo','Do','Vr','Sa'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: 'Vorige', + prevStatus: 'Vertoon vorige maand', + prevJumpText: '<<', + prevJumpStatus: 'Vertoon vorige jaar', + nextText: 'Volgende', + nextStatus: 'Vertoon volgende maand', + nextJumpText: '>>', + nextJumpStatus: 'Vertoon volgende jaar', + currentText: 'Vandag', + currentStatus: 'Vertoon huidige maand', + todayText: 'Vandag', + todayStatus: 'Vertoon huidige maand', + clearText: 'Vee uit', + clearStatus: 'Verwyder die huidige datum', + closeText: 'Klaar', + closeStatus: 'Sluit sonder verandering', + yearStatus: 'Vertoon \'n ander jaar', + monthStatus: 'Vertoon \'n ander maand', + weekText: 'Wk', + weekStatus: 'Week van die jaar', + dayStatus: 'Kies DD, M d', + defaultStatus: 'Kies \'n datum', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.af); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-am.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-am.js new file mode 100755 index 000000000..9024df82e --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-am.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Amharic (አማርኛ) localisation for jQuery datepicker. + Leyu Sisay. */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.am = { + monthNames: ['ጃንዋሪ','áˆá‰¥áˆ­á‹‹áˆª','ማርች','አá•ሪáˆ','ሜይ','áŒáŠ•', + 'áŒáˆ‹á‹­','ኦገስት','ሴá•ቴáˆá‰ áˆ­','ኦክቶበር','ኖቬáˆá‰ áˆ­','ዲሴáˆá‰ áˆ­'], + monthNamesShort: ['ጃንዋ','áˆá‰¥áˆ­','ማርች','አá•ሪ','ሜይ','áŒáŠ•', + 'áŒáˆ‹á‹­','ኦገስ','ሴá•ቴ','ኦክቶ','ኖቬáˆ','ዲሴáˆ'], + dayNames: ['ሰንዴይ','መንዴይ','ትዩስዴይ','ዌንስዴይ','ተርሰዴይ','áራይዴይ','ሳተርዴይ'], + dayNamesShort: ['ሰንዴ','መንዴ','ትዩስ','ዌንስ','ተርሰ','áራይ','ሳተር'], + dayNamesMin: ['ሰን','መን','ትዩ','ዌን','ተር','áራ','ሳተ'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: 'ያለáˆ', + prevStatus: 'ያለáˆá‹áŠ• ወር አሳይ', + prevJumpText: '<<', + prevJumpStatus: 'ያለáˆá‹áŠ• ዓመት አሳይ', + nextText: 'ቀጣይ', + nextStatus: 'ቀጣዩን ወር አሳይ', + nextJumpText: '>>', + nextJumpStatus: 'ቀጣዩን ዓመት አሳይ', + currentText: 'አáˆáŠ•', + currentStatus: 'የአáˆáŠ‘áŠ• ወር አሳይ', + todayText: 'ዛሬ', + todayStatus: 'የዛሬን ወር አሳይ', + clearText: 'አጥá‹', + clearStatus: 'የተመረጠá‹áŠ• ቀን አጥá‹', + closeText: 'á‹áŒ‹', + closeStatus: 'የቀን መáˆáˆ¨áŒ«á‹áŠ• á‹áŒ‹', + yearStatus: 'ዓመቱን ቀይር', + monthStatus: 'ወሩን ቀይር', + weekText: 'ሳáˆ', + weekStatus: 'የዓመቱ ሳáˆáŠ•á‰µ ', + dayStatus: 'DD, M d, yyyy áˆáˆ¨áŒ¥', + defaultStatus: 'ቀን áˆáˆ¨áŒ¥', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.am); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-ar-DZ.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-ar-DZ.js new file mode 100755 index 000000000..dc5f3b60e --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-ar-DZ.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Algerian (and Tunisian) Arabic localisation for jQuery Datepicker. + Mohamed Cherif BOUCHELAGHEM -- cherifbouchelaghem@yahoo.fr */ +(function($) { + 'use strict'; + $.datepick.regionalOptions['ar-DZ'] = { + monthNames: ['جانÙÙŠ','ÙÙŠÙØ±ÙŠ','مارس','Ø£ÙØ±ÙŠÙ„','ماي','جوان', + 'جويلية','أوت','سبتمبر','أكتوبر','نوÙمبر','ديسمبر'], + monthNamesShort: ['1','2','3','4','5','6', + '7','8','9','10','11','12'], + dayNames: ['الأحد','الاثنين','الثلاثاء','الأربعاء','الخميس','الجمعة','السبت'], + dayNamesShort: ['الأحد','الاثنين','الثلاثاء','الأربعاء','الخميس','الجمعة','السبت'], + dayNamesMin: ['الأحد','الاثنين','الثلاثاء','الأربعاء','الخميس','الجمعة','السبت'], + dateFormat: 'dd/mm/yyyy', + firstDay: 6, + renderer: $.datepick.defaultRenderer, + prevText: '<السابق', + prevStatus: 'عرض الشهر السابق', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'التالي>', + nextStatus: 'عرض الشهر القادم', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'اليوم', + currentStatus: 'عرض الشهر الحالي', + todayText: 'اليوم', + todayStatus: 'عرض الشهر الحالي', + clearText: 'مسح', + clearStatus: 'امسح التاريخ الحالي', + closeText: 'إغلاق', + closeStatus: 'إغلاق بدون Ø­ÙØ¸', + yearStatus: 'عرض سنة آخرى', + monthStatus: 'عرض شهر آخر', + weekText: 'أسبوع', + weekStatus: 'أسبوع السنة', + dayStatus: 'اختر D, M d', + defaultStatus: 'اختر يوم', + isRTL: true + }; + $.datepick.setDefaults($.datepick.regionalOptions['ar-DZ']); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-ar-EG.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-ar-EG.js new file mode 100755 index 000000000..568de4a23 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-ar-EG.js @@ -0,0 +1,43 @@ +/* http://keith-wood.name/datepick.html + Arabic localisation for jQuery Datepicker. + Mahmoud Khaled -- mahmoud.khaled@badrit.com + NOTE: monthNames are the new months names */ +(function($) { + 'use strict'; + $.datepick.regionalOptions['ar-EG'] = { + monthNames: ['يناير','ÙØ¨Ø±Ø§ÙŠØ±','مارس','إبريل','مايو','يونية', + 'يوليو','أغسطس','سبتمبر','أكتوبر','نوÙمبر','ديسمبر'], + monthNamesShort: ['1','2','3','4','5','6', + '7','8','9','10','11','12'], + dayNames: ['الأحد','الاثنين','الثلاثاء','الأربعاء','الخميس','الجمعة','السبت'], + dayNamesShort: ['أحد','اثنين','ثلاثاء','أربعاء','خميس','جمعة','سبت'], + dayNamesMin: ['أحد','اثنين','ثلاثاء','أربعاء','خميس','جمعة','سبت'], + dateFormat: 'dd/mm/yyyy', + firstDay: 6, + renderer: $.datepick.defaultRenderer, + prevText: '<السابق', + prevStatus: 'عرض الشهر السابق', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'التالي>', + nextStatus: 'عرض الشهر القادم', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'اليوم', + currentStatus: 'عرض الشهر الحالي', + todayText: 'اليوم', + todayStatus: 'عرض الشهر الحالي', + clearText: 'مسح', + clearStatus: 'امسح التاريخ الحالي', + closeText: 'إغلاق', + closeStatus: 'إغلاق بدون Ø­ÙØ¸', + yearStatus: 'عرض سنة آخرى', + monthStatus: 'عرض شهر آخر', + weekText: 'أسبوع', + weekStatus: 'أسبوع السنة', + dayStatus: 'اختر D, M d', + defaultStatus: 'اختر يوم', + isRTL: true + }; + $.datepick.setDefaults($.datepick.regionalOptions['ar-EG']); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-ar.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-ar.js new file mode 100755 index 000000000..873e57fc2 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-ar.js @@ -0,0 +1,44 @@ +/* http://keith-wood.name/datepick.html + Arabic localisation for jQuery Datepicker. + Khaled Al Horani -- koko.dw@gmail.com + خالد الحوراني -- koko.dw@gmail.com + NOTE: monthNames are the original months names and they are the Arabic names, not the new months name ÙØ¨Ø±Ø§ÙŠØ± - يناير and there isn't any Arabic roots for these months */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.ar = { + monthNames: ['كانون الثاني','شباط','آذار','نيسان','آذار','حزيران', + 'تموز','آب','أيلول','تشرين الأول','تشرين الثاني','كانون الأول'], + monthNamesShort: ['1','2','3','4','5','6', + '7','8','9','10','11','12'], + dayNames: ['الأحد','الاثنين','الثلاثاء','الأربعاء','الخميس','الجمعة','السبت'], + dayNamesShort: ['الأحد','الاثنين','الثلاثاء','الأربعاء','الخميس','الجمعة','السبت'], + dayNamesMin: ['الأحد','الاثنين','الثلاثاء','الأربعاء','الخميس','الجمعة','السبت'], + dateFormat: 'dd/mm/yyyy', + firstDay: 6, + renderer: $.datepick.defaultRenderer, + prevText: '<السابق', + prevStatus: 'عرض الشهر السابق', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'التالي>', + nextStatus: 'عرض الشهر القادم', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'اليوم', + currentStatus: 'عرض الشهر الحالي', + todayText: 'اليوم', + todayStatus: 'عرض الشهر الحالي', + clearText: 'مسح', + clearStatus: 'امسح التاريخ الحالي', + closeText: 'إغلاق', + closeStatus: 'إغلاق بدون Ø­ÙØ¸', + yearStatus: 'عرض سنة آخرى', + monthStatus: 'عرض شهر آخر', + weekText: 'أسبوع', + weekStatus: 'أسبوع السنة', + dayStatus: 'اختر D, M d', + defaultStatus: 'اختر يوم', + isRTL: true + }; + $.datepick.setDefaults($.datepick.regionalOptions.ar); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-az.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-az.js new file mode 100755 index 000000000..d4fff545f --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-az.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Azerbaijani localisation for jQuery Datepicker. + Written by Jamil Najafov (necefov33@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.az = { + monthNames: ['Yanvar','Fevral','Mart','Aprel','May','İyun', + 'İyul','Avqust','Sentyabr','Oktyabr','Noyabr','Dekabr'], + monthNamesShort: ['Yan','Fev','Mar','Apr','May','İyun', + 'İyul','Avq','Sen','Okt','Noy','Dek'], + dayNames: ['Bazar','Bazar ertÉ™si','ÇərÅŸÉ™nbÉ™ axÅŸamı','ÇərÅŸÉ™nbÉ™','CümÉ™ axÅŸamı','CümÉ™','ŞənbÉ™'], + dayNamesShort: ['B','Be','Ça','Ç','Ca','C','Åž'], + dayNamesMin: ['B','B','Ç','С','Ç','C','Åž'], + dateFormat: 'dd.mm.yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<Geri', + prevStatus: 'ÆvvÉ™lki ay', + prevJumpText: '<<', + prevJumpStatus: 'ÆvvÉ™lki il', + nextText: 'İrÉ™li>', + nextStatus: 'Sonrakı ay', + nextJumpText: '>>', + nextJumpStatus: 'Sonrakı il', + currentText: 'Bugün', + currentStatus: 'İndiki ay', + todayText: 'Bugün', + todayStatus: 'İndiki ay', + clearText: 'TÉ™mizlÉ™', + clearStatus: 'Tarixi sil', + closeText: 'BaÄŸla', + closeStatus: 'TÉ™qvimi baÄŸla', + yearStatus: 'BaÅŸqa il', + monthStatus: 'BaÅŸqa ay', + weekText: 'Hf', + weekStatus: 'HÉ™ftÉ™lÉ™r', + dayStatus: 'D, M d seçin', + defaultStatus: 'Bir tarix seçin', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.az); +})(jQuery); \ No newline at end of file diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-bg.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-bg.js new file mode 100755 index 000000000..35873b6cd --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-bg.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Bulgarian localisation for jQuery Datepicker. + Written by Stoyan Kyosev (http://svest.org). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.bg = { + monthNames: ['Януари','Февруари','Март','Ðприл','Май','Юни', + 'Юли','ÐвгуÑÑ‚','Септември','Октомври','Ðоември','Декември'], + monthNamesShort: ['Яну','Фев','Мар','Ðпр','Май','Юни', + 'Юли','Ðвг','Сеп','Окт','Ðов','Дек'], + dayNames: ['ÐеделÑ','Понеделник','Вторник','СрÑда','Четвъртък','Петък','Събота'], + dayNamesShort: ['Ðед','Пон','Вто','СрÑ','Чет','Пет','Съб'], + dayNamesMin: ['Ðе','По','Ð’Ñ‚','Ср','Че','Пе','Съ'], + dateFormat: 'dd.mm.yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<назад', + prevStatus: 'покажи поÑÐ»ÐµÐ´Ð½Ð¸Ñ Ð¼ÐµÑец', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'напред>', + nextStatus: 'покажи ÑÐ»ÐµÐ´Ð²Ð°Ñ‰Ð¸Ñ Ð¼ÐµÑец', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'днеÑ', + currentStatus: '', + todayText: 'днеÑ', + todayStatus: '', + clearText: 'изчиÑти', + clearStatus: 'изчиÑти актуалната дата', + closeText: 'затвори', + closeStatus: 'затвори без промени', + yearStatus: 'покажи друга година', + monthStatus: 'покажи друг меÑец', + weekText: 'Wk', + weekStatus: 'Ñедмица от меÑеца', + dayStatus: 'Избери D, M d', + defaultStatus: 'Избери дата', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.bg); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-bs.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-bs.js new file mode 100755 index 000000000..b465f866a --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-bs.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Bosnian localisation for jQuery Datepicker. + Written by Kenan Konjo. */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.bs = { + monthNames: ['Januar','Februar','Mart','April','Maj','Juni', + 'Juli','August','Septembar','Oktobar','Novembar','Decembar'], + monthNamesShort: ['Jan','Feb','Mar','Apr','Maj','Jun', + 'Jul','Aug','Sep','Okt','Nov','Dec'], + dayNames: ['Nedelja','Ponedeljak','Utorak','Srijeda','ÄŒetvrtak','Petak','Subota'], + dayNamesShort: ['Ned','Pon','Uto','Sri','ÄŒet','Pet','Sub'], + dayNamesMin: ['Ne','Po','Ut','Sr','ÄŒe','Pe','Su'], + dateFormat: 'dd.mm.yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: '>', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Danas', + currentStatus: '', + todayText: 'Danas', + todayStatus: '', + clearText: 'X', + clearStatus: '', + closeText: 'Zatvori', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Wk', + weekStatus: '', + dayStatus: 'DD d MM', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.bs); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-ca.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-ca.js new file mode 100755 index 000000000..4b489aabd --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-ca.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Catalan localisation for jQuery Datepicker. + Writers: (joan.leon@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.ca = { + monthNames: ['Gener','Febrer','Març','Abril','Maig','Juny', + 'Juliol','Agost','Setembre','Octubre','Novembre','Desembre'], + monthNamesShort: ['Gen','Feb','Mar','Abr','Mai','Jun', + 'Jul','Ago','Set','Oct','Nov','Des'], + dayNames: ['Diumenge','Dilluns','Dimarts','Dimecres','Dijous','Divendres','Dissabte'], + dayNamesShort: ['Dug','Dln','Dmt','Dmc','Djs','Dvn','Dsb'], + dayNamesMin: ['Dg','Dl','Dt','Dc','Dj','Dv','Ds'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<Ant', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Seg>', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Avui', + currentStatus: '', + todayText: 'Avui', + todayStatus: '', + clearText: 'Netejar', + clearStatus: '', + closeText: 'Tancar', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Sm', + weekStatus: '', + dayStatus: 'D, M d', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.ca); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-cs.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-cs.js new file mode 100755 index 000000000..68d82ce5c --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-cs.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Czech localisation for jQuery Datepicker. + Written by Tomas Muller (tomas@tomas-muller.net). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.cs = { + monthNames: ['leden','únor','bÅ™ezen','duben','kvÄ›ten','Äerven', + 'Äervenec','srpen','září','říjen','listopad','prosinec'], + monthNamesShort: ['led','úno','bÅ™e','dub','kvÄ›','Äer', + 'Ävc','srp','zář','říj','lis','pro'], + dayNames: ['nedÄ›le','pondÄ›lí','úterý','stÅ™eda','Ätvrtek','pátek','sobota'], + dayNamesShort: ['ne','po','út','st','Ät','pá','so'], + dayNamesMin: ['ne','po','út','st','Ät','pá','so'], + dateFormat: 'dd.mm.yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<Dříve', + prevStatus: 'PÅ™ejít na pÅ™edchozí mÄ›sí', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'PozdÄ›ji>', + nextStatus: 'PÅ™ejít na další mÄ›síc', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Nyní', + currentStatus: 'PÅ™ejde na aktuální mÄ›síc', + todayText: 'Nyní', + todayStatus: 'PÅ™ejde na aktuální mÄ›síc', + clearText: 'Vymazat', + clearStatus: 'Vymaže zadané datum', + closeText: 'Zavřít', + closeStatus: 'ZavÅ™e kalendář beze zmÄ›ny', + yearStatus: 'PÅ™ejít na jiný rok', + monthStatus: 'PÅ™ejít na jiný mÄ›síc', + weekText: 'Týd', + weekStatus: 'Týden v roce', + dayStatus: '\'Vyber\' DD, M d', + defaultStatus: 'Vyberte datum', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.cs); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-da.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-da.js new file mode 100755 index 000000000..507af9b25 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-da.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Danish localisation for jQuery Datepicker. + Written by Jan Christensen ( deletestuff@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.da = { + monthNames: ['Januar','Februar','Marts','April','Maj','Juni', + 'Juli','August','September','Oktober','November','December'], + monthNamesShort: ['Jan','Feb','Mar','Apr','Maj','Jun', + 'Jul','Aug','Sep','Okt','Nov','Dec'], + dayNames: ['Søndag','Mandag','Tirsdag','Onsdag','Torsdag','Fredag','Lørdag'], + dayNamesShort: ['Søn','Man','Tir','Ons','Tor','Fre','Lør'], + dayNamesMin: ['Sø','Ma','Ti','On','To','Fr','Lø'], + dateFormat: 'dd-mm-yyyy', + firstDay: 0, + renderer: $.datepick.defaultRenderer, + prevText: '<Forrige', + prevStatus: 'Vis forrige mÃ¥ned', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Næste>', + nextStatus: 'Vis næste mÃ¥ned', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Idag', + currentStatus: 'Vis aktuel mÃ¥ned', + todayText: 'Idag', + todayStatus: 'Vis aktuel mÃ¥ned', + clearText: 'Nulstil', + clearStatus: 'Nulstil den aktuelle dato', + closeText: 'Luk', + closeStatus: 'Luk uden ændringer', + yearStatus: 'Vis et andet Ã¥r', + monthStatus: 'Vis en anden mÃ¥ned', + weekText: 'Uge', + weekStatus: 'Ã…rets uge', + dayStatus: 'Vælg D, M d', + defaultStatus: 'Vælg en dato', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.da); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-de-CH.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-de-CH.js new file mode 100755 index 000000000..82655f893 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-de-CH.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Swiss-German localisation for jQuery Datepicker. + Written by Douglas Jose & Juerg Meier. */ +(function($) { + 'use strict'; + $.datepick.regionalOptions['de-CH'] = { + monthNames: ['Januar','Februar','März','April','Mai','Juni', + 'Juli','August','September','Oktober','November','Dezember'], + monthNamesShort: ['Jan','Feb','Mär','Apr','Mai','Jun', + 'Jul','Aug','Sep','Okt','Nov','Dez'], + dayNames: ['Sonntag','Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag'], + dayNamesShort: ['So','Mo','Di','Mi','Do','Fr','Sa'], + dayNamesMin: ['So','Mo','Di','Mi','Do','Fr','Sa'], + dateFormat: 'dd.mm.yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<zurück', + prevStatus: 'letzten Monat zeigen', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'nächster>', + nextStatus: 'nächsten Monat zeigen', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'heute', + currentStatus: '', + todayText: 'heute', + todayStatus: '', + clearText: 'löschen', + clearStatus: 'aktuelles Datum löschen', + closeText: 'schliessen', + closeStatus: 'ohne Änderungen schliessen', + yearStatus: 'anderes Jahr anzeigen', + monthStatus: 'anderen Monat anzeigen', + weekText: 'Wo', + weekStatus: 'Woche des Monats', + dayStatus: 'Wähle D, M d', + defaultStatus: 'Wähle ein Datum', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions['de-CH']); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-de.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-de.js new file mode 100755 index 000000000..9c0be2edb --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-de.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + German localisation for jQuery Datepicker. + Written by Milian Wolff (mail@milianw.de). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.de = { + monthNames: ['Januar','Februar','März','April','Mai','Juni', + 'Juli','August','September','Oktober','November','Dezember'], + monthNamesShort: ['Jan','Feb','Mär','Apr','Mai','Jun', + 'Jul','Aug','Sep','Okt','Nov','Dez'], + dayNames: ['Sonntag','Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag'], + dayNamesShort: ['So','Mo','Di','Mi','Do','Fr','Sa'], + dayNamesMin: ['So','Mo','Di','Mi','Do','Fr','Sa'], + dateFormat: 'dd.mm.yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<zurück', + prevStatus: 'letzten Monat zeigen', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Vor>', + nextStatus: 'nächsten Monat zeigen', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'heute', + currentStatus: '', + todayText: 'heute', + todayStatus: '', + clearText: 'löschen', + clearStatus: 'aktuelles Datum löschen', + closeText: 'schließen', + closeStatus: 'ohne Änderungen schließen', + yearStatus: 'anderes Jahr anzeigen', + monthStatus: 'anderen Monat anzeigen', + weekText: 'Wo', + weekStatus: 'Woche des Monats', + dayStatus: 'Wähle D, M d', + defaultStatus: 'Wähle ein Datum', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.de); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-el.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-el.js new file mode 100755 index 000000000..b28d71ad2 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-el.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Greek localisation for jQuery Datepicker. + Written by Alex Cicovic (http://www.alexcicovic.com) */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.el = { + monthNames: ['ΙανουάÏιος','ΦεβÏουάÏιος','ΜάÏτιος','ΑπÏίλιος','Μάιος','ΙοÏνιος', + 'ΙοÏλιος','ΑÏγουστος','ΣεπτέμβÏιος','ΟκτώβÏιος','ÎοέμβÏιος','ΔεκέμβÏιος'], + monthNamesShort: ['Ιαν','Φεβ','ΜαÏ','ΑπÏ','Μαι','Ιουν', + 'Ιουλ','Αυγ','Σεπ','Οκτ','Îοε','Δεκ'], + dayNames: ['ΚυÏιακή','ΔευτέÏα','ΤÏίτη','ΤετάÏτη','Πέμπτη','ΠαÏασκευή','Σάββατο'], + dayNamesShort: ['ΚυÏ','Δευ','ΤÏι','Τετ','Πεμ','ΠαÏ','Σαβ'], + dayNamesMin: ['Κυ','Δε','ΤÏ','Τε','Πε','Πα','Σα'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: 'ΠÏοηγοÏμενος', + prevStatus: 'Επισκόπηση Ï€ÏοηγοÏμενου μήνα', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Επόμενος', + nextStatus: 'Επισκόπηση επόμενου μήνα', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'ΤÏέχων Μήνας', + currentStatus: 'Επισκόπηση Ï„Ïέχοντος μήνα', + todayText: 'ΤÏέχων Μήνας', + todayStatus: 'Επισκόπηση Ï„Ïέχοντος μήνα', + clearText: 'Σβήσιμο', + clearStatus: 'Σβήσιμο της επιλεγμένης ημεÏομηνίας', + closeText: 'Κλείσιμο', + closeStatus: 'Κλείσιμο χωÏίς αλλαγή', + yearStatus: 'Επισκόπηση άλλου έτους', + monthStatus: 'Επισκόπηση άλλου μήνα', + weekText: 'Εβδ', + weekStatus: '', + dayStatus: 'Επιλογή DD d MM', + defaultStatus: 'Επιλέξτε μια ημεÏομηνία', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.el); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-en-AU.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-en-AU.js new file mode 100755 index 000000000..2958a1dd2 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-en-AU.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + English/Australia localisation for jQuery Datepicker. + Based on en-GB. */ +(function($) { + 'use strict'; + $.datepick.regionalOptions['en-AU'] = { + monthNames: ['January','February','March','April','May','June', + 'July','August','September','October','November','December'], + monthNamesShort: ['Jan','Feb','Mar','Apr','May','Jun', + 'Jul','Aug','Sep','Oct','Nov','Dec'], + dayNames: ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'], + dayNamesShort: ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'], + dayNamesMin: ['Su','Mo','Tu','We','Th','Fr','Sa'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: 'Prev', + prevStatus: 'Show the previous month', + prevJumpText: '<<', + prevJumpStatus: 'Show the previous year', + nextText: 'Next', + nextStatus: 'Show the next month', + nextJumpText: '>>', + nextJumpStatus: 'Show the next year', + currentText: 'Current', + currentStatus: 'Show the current month', + todayText: 'Today', + todayStatus: 'Show today\'s month', + clearText: 'Clear', + clearStatus: 'Erase the current date', + closeText: 'Done', + closeStatus: 'Close without change', + yearStatus: 'Show a different year', + monthStatus: 'Show a different month', + weekText: 'Wk', + weekStatus: 'Week of the year', + dayStatus: 'Select DD, M d', + defaultStatus: 'Select a date', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions['en-AU']); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-en-GB.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-en-GB.js new file mode 100755 index 000000000..a5c8bfeae --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-en-GB.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + English UK localisation for jQuery Datepicker. + Written by Stuart. */ +(function($) { + 'use strict'; + $.datepick.regionalOptions['en-GB'] = { + monthNames: ['January','February','March','April','May','June', + 'July','August','September','October','November','December'], + monthNamesShort: ['Jan','Feb','Mar','Apr','May','Jun', + 'Jul','Aug','Sep','Oct','Nov','Dec'], + dayNames: ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'], + dayNamesShort: ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'], + dayNamesMin: ['Su','Mo','Tu','We','Th','Fr','Sa'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: 'Prev', + prevStatus: 'Show the previous month', + prevJumpText: '<<', + prevJumpStatus: 'Show the previous year', + nextText: 'Next', + nextStatus: 'Show the next month', + nextJumpText: '>>', + nextJumpStatus: 'Show the next year', + currentText: 'Current', + currentStatus: 'Show the current month', + todayText: 'Today', + todayStatus: 'Show today\'s month', + clearText: 'Clear', + clearStatus: 'Erase the current date', + closeText: 'Done', + closeStatus: 'Close without change', + yearStatus: 'Show a different year', + monthStatus: 'Show a different month', + weekText: 'Wk', + weekStatus: 'Week of the year', + dayStatus: 'Select DD, M d', + defaultStatus: 'Select a date', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions['en-GB']); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-en-NZ.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-en-NZ.js new file mode 100755 index 000000000..f6f841b90 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-en-NZ.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + English/New Zealand localisation for jQuery Datepicker. + Based on en-GB. */ +(function($) { + 'use strict'; + $.datepick.regionalOptions['en-NZ'] = { + monthNames: ['January','February','March','April','May','June', + 'July','August','September','October','November','December'], + monthNamesShort: ['Jan','Feb','Mar','Apr','May','Jun', + 'Jul','Aug','Sep','Oct','Nov','Dec'], + dayNames: ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'], + dayNamesShort: ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'], + dayNamesMin: ['Su','Mo','Tu','We','Th','Fr','Sa'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: 'Prev', + prevStatus: 'Show the previous month', + prevJumpText: '<<', + prevJumpStatus: 'Show the previous year', + nextText: 'Next', + nextStatus: 'Show the next month', + nextJumpText: '>>', + nextJumpStatus: 'Show the next year', + currentText: 'Current', + currentStatus: 'Show the current month', + todayText: 'Today', + todayStatus: 'Show today\'s month', + clearText: 'Clear', + clearStatus: 'Erase the current date', + closeText: 'Done', + closeStatus: 'Close without change', + yearStatus: 'Show a different year', + monthStatus: 'Show a different month', + weekText: 'Wk', + weekStatus: 'Week of the year', + dayStatus: 'Select DD, M d', + defaultStatus: 'Select a date', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions['en-NZ']); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-eo.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-eo.js new file mode 100755 index 000000000..70ea32d71 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-eo.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Esperanto localisation for jQuery Datepicker. + Written by Olivier M. (olivierweb@ifrance.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.eo = { + monthNames: ['Januaro','Februaro','Marto','Aprilo','Majo','Junio', + 'Julio','AÅ­gusto','Septembro','Oktobro','Novembro','Decembro'], + monthNamesShort: ['Jan','Feb','Mar','Apr','Maj','Jun', + 'Jul','AÅ­g','Sep','Okt','Nov','Dec'], + dayNames: ['Dimanĉo','Lundo','Mardo','Merkredo','Ä´aÅ­do','Vendredo','Sabato'], + dayNamesShort: ['Dim','Lun','Mar','Mer','Ä´aÅ­','Ven','Sab'], + dayNamesMin: ['Di','Lu','Ma','Me','Ä´a','Ve','Sa'], + dateFormat: 'dd/mm/yyyy', + firstDay: 0, + renderer: $.datepick.defaultRenderer, + prevText: '<Anta', + prevStatus: 'Vidi la antaÅ­an monaton', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Sekv>', + nextStatus: 'Vidi la sekvan monaton', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Nuna', + currentStatus: 'Vidi la nunan monaton', + todayText: 'Nuna', + todayStatus: 'Vidi la nunan monaton', + clearText: 'Vakigi', + clearStatus: '', + closeText: 'Fermi', + closeStatus: 'Fermi sen modifi', + yearStatus: 'Vidi alian jaron', + monthStatus: 'Vidi alian monaton', + weekText: 'Sb', + weekStatus: '', + dayStatus: 'Elekti DD, MM d', + defaultStatus: 'Elekti la daton', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.eo); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-es-AR.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-es-AR.js new file mode 100755 index 000000000..e4ec6b35c --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-es-AR.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Spanish/Argentina localisation for jQuery Datepicker. + Written by Esteban Acosta Villafane (esteban.acosta@globant.com) of Globant (http://www.globant.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions['es-AR'] = { + monthNames: ['Enero','Febrero','Marzo','Abril','Mayo','Junio', + 'Julio','Agosto','Septiembre','Octubre','Noviembre','Diciembre'], + monthNamesShort: ['Ene','Feb','Mar','Abr','May','Jun', + 'Jul','Ago','Sep','Oct','Nov','Dic'], + dayNames: ['Domingo','Lunes','Martes','Miércoles','Jueves','Viernes','Sábado'], + dayNamesShort: ['Dom','Lun','Mar','Mié','Juv','Vie','Sáb'], + dayNamesMin: ['Do','Lu','Ma','Mi','Ju','Vi','Sá'], + dateFormat: 'dd/mm/yyyy', + firstDay: 0, + renderer: $.datepick.defaultRenderer, + prevText: '<Ant', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Sig>', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Hoy', + currentStatus: '', + todayText: 'Hoy', + todayStatus: '', + clearText: 'Limpiar', + clearStatus: '', + closeText: 'Cerrar', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Sm', + weekStatus: '', + dayStatus: 'D, M d', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions['es-AR']); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-es-PE.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-es-PE.js new file mode 100755 index 000000000..3cd0c3579 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-es-PE.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Spanish/Perú localisation for jQuery Datepicker. + Written by Fischer Tirado (fishdev@globant.com) of ASIX (http://www.asixonline.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions['es-PE'] = { + monthNames: ['Enero','Febrero','Marzo','Abril','Mayo','Junio', + 'Julio','Agosto','Septiembre','Octubre','Noviembre','Diciembre'], + monthNamesShort: ['Ene','Feb','Mar','Abr','May','Jun', + 'Jul','Ago','Sep','Oct','Nov','Dic'], + dayNames: ['Domingo','Lunes','Martes','Miércoles','Jueves','Viernes','Sábado'], + dayNamesShort: ['Dom','Lun','Mar','Mié','Jue','Vie','Sab'], + dayNamesMin: ['Do','Lu','Ma','Mi','Ju','Vi','Sa'], + dateFormat: 'dd/mm/yyyy', + firstDay: 0, + renderer: $.datepick.defaultRenderer, + prevText: '<Ant', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Sig>', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Hoy', + currentStatus: '', + todayText: 'Hoy', + todayStatus: '', + clearText: 'Limpiar', + clearStatus: '', + closeText: 'Cerrar', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Sm', + weekStatus: '', + dayStatus: 'DD d, MM yyyy', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions['es-PE']); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-es.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-es.js new file mode 100755 index 000000000..d1ea3d746 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-es.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Spanish localisation for jQuery Datepicker. + Traducido por Vester (xvester@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.es = { + monthNames: ['Enero','Febrero','Marzo','Abril','Mayo','Junio', + 'Julio','Agosto','Septiembre','Octubre','Noviembre','Diciembre'], + monthNamesShort: ['Ene','Feb','Mar','Abr','May','Jun', + 'Jul','Ago','Sep','Oct','Nov','Dic'], + dayNames: ['Domingo','Lunes','Martes','Miércoles','Jueves','Viernes','Sábado'], + dayNamesShort: ['Dom','Lun','Mar','Mié','Juv','Vie','Sáb'], + dayNamesMin: ['Do','Lu','Ma','Mi','Ju','Vi','Sá'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<Ant', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Sig>', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Hoy', + currentStatus: '', + todayText: 'Hoy', + todayStatus: '', + clearText: 'Limpiar', + clearStatus: '', + closeText: 'Cerrar', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Sm', + weekStatus: '', + dayStatus: 'D, M d', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.es); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-et.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-et.js new file mode 100755 index 000000000..6509bbf26 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-et.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Estonian localisation for jQuery Datepicker. + Written by Mart Sõmermaa (mrts.pydev at gmail com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.et = { + monthNames: ['Jaanuar','Veebruar','Märts','Aprill','Mai','Juuni', + 'Juuli','August','September','Oktoober','November','Detsember'], + monthNamesShort: ['Jaan','Veebr','Märts','Apr','Mai','Juuni', + 'Juuli','Aug','Sept','Okt','Nov','Dets'], + dayNames: ['Pühapäev','Esmaspäev','Teisipäev','Kolmapäev','Neljapäev','Reede','Laupäev'], + dayNamesShort: ['Pühap','Esmasp','Teisip','Kolmap','Neljap','Reede','Laup'], + dayNamesMin: ['P','E','T','K','N','R','L'], + dateFormat: 'dd.mm.yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: 'Eelnev', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Järgnev', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Täna', + currentStatus: '', + todayText: 'Täna', + todayStatus: '', + clearText: '', + clearStatus: '', + closeText: 'Sulge', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Sm', + weekStatus: '', + dayStatus: '', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.et); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-eu.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-eu.js new file mode 100755 index 000000000..b2a539a3d --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-eu.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Basque localisation for jQuery Datepicker. + Karrikas-ek itzulia (karrikas@karrikas.com) */ +(function($){ + 'use strict'; + $.datepick.regionalOptions.eu = { + monthNames: ['Urtarrila','Otsaila','Martxoa','Apirila','Maiatza','Ekaina', + 'Uztaila','Abuztua','Iraila','Urria','Azaroa','Abendua'], + monthNamesShort: ['Urt','Ots','Mar','Api','Mai','Eka', + 'Uzt','Abu','Ira','Urr','Aza','Abe'], + dayNames: ['Igandea','Astelehena','Asteartea','Asteazkena','Osteguna','Ostirala','Larunbata'], + dayNamesShort: ['Iga','Ast','Ast','Ast','Ost','Ost','Lar'], + dayNamesMin: ['Ig','As','As','As','Os','Os','La'], + dateFormat: 'yyyy/mm/dd', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<Aur', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Hur>', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Gaur', + currentStatus: '', + todayText: 'Gaur', + todayStatus: '', + clearText: 'X', + clearStatus: '', + closeText: 'Egina', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Wk', + weekStatus: '', + dayStatus: 'DD d MM', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.eu); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-fa.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-fa.js new file mode 100755 index 000000000..d98798104 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-fa.js @@ -0,0 +1,43 @@ +/* http://keith-wood.name/datepick.html + Persian (Farsi) localisation for jQuery Datepicker. + Javad Mowlanezhad -- jmowla@gmail.com */ +(function($) { + 'use strict'; + /* jshint -W100 */ + $.datepick.regionalOptions.fa = { + monthNames: ['ÙØ±ÙˆØ±Ø¯ÙŠÙ†','ارديبهشت','خرداد','تير','مرداد','شهريور', + 'مهر','آبان','آذر','دي','بهمن','اسÙند'], + monthNamesShort: ['1','2','3','4','5','6', + '7','8','9','10','11','12'], + dayNames: ['يکشنبه','دوشنبه','سه‌شنبه','چهارشنبه','پنجشنبه','جمعه','شنبه'], + dayNamesShort: ['ÙŠ','د','س','Ú†','Ù¾','ج','Ø´'], + dayNamesMin: ['ÙŠ','د','س','Ú†','Ù¾','ج','Ø´'], + dateFormat: 'yyyy/mm/dd', + firstDay: 6, + renderer: $.datepick.defaultRenderer, + prevText: '<قبلي', + prevStatus: 'نمايش ماه قبل', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'بعدي>', + nextStatus: 'نمايش ماه بعد', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'امروز', + currentStatus: 'نمايش ماه جاري', + todayText: 'امروز', + todayStatus: 'نمايش ماه جاري', + clearText: 'حذ٠تاريخ', + clearStatus: 'پاک کردن تاريخ جاري', + closeText: 'بستن', + closeStatus: 'بستن بدون اعمال تغييرات', + yearStatus: 'نمايش سال Ù…ØªÙØ§ÙˆØª', + monthStatus: 'نمايش ماه Ù…ØªÙØ§ÙˆØª', + weekText: 'Ù‡Ù', + weekStatus: 'Ù‡ÙØªÙ‡Ù سال', + dayStatus: 'انتخاب D, M d', + defaultStatus: 'انتخاب تاريخ', + isRTL: true + }; + $.datepick.setDefaults($.datepick.regionalOptions.fa); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-fi.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-fi.js new file mode 100755 index 000000000..702d7a44b --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-fi.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Finnish localisation for jQuery Datepicker. + Written by Harri Kilpiö (harrikilpio@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.fi = { + monthNames: ['Tammikuu','Helmikuu','Maaliskuu','Huhtikuu','Toukokuu','Kesäkuu', + 'Heinäkuu','Elokuu','Syyskuu','Lokakuu','Marraskuu','Joulukuu'], + monthNamesShort: ['Tammi','Helmi','Maalis','Huhti','Touko','Kesä', + 'Heinä','Elo','Syys','Loka','Marras','Joulu'], + dayNamesShort: ['Su','Ma','Ti','Ke','To','Pe','Su'], + dayNames: ['Sunnuntai','Maanantai','Tiistai','Keskiviikko','Torstai','Perjantai','Lauantai'], + dayNamesMin: ['Su','Ma','Ti','Ke','To','Pe','La'], + dateFormat: 'dd.mm.yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '«Edellinen', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Seuraava»', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Tänään', + currentStatus: '', + todayText: 'Tänään', + todayStatus: '', + clearText: 'Tyhjennä', + clearStatus: '', + closeText: 'Sulje', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Vk', + weekStatus: '', + dayStatus: 'D, M d', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.fi); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-fo.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-fo.js new file mode 100755 index 000000000..937a21d86 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-fo.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Faroese localisation for jQuery Datepicker. + Written by Sverri Mohr Olsen, sverrimo@gmail.com */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.fo = { + monthNames: ['Januar','Februar','Mars','Apríl','Mei','Juni', + 'Juli','August','September','Oktober','November','Desember'], + monthNamesShort: ['Jan','Feb','Mar','Apr','Mei','Jun', + 'Jul','Aug','Sep','Okt','Nov','Des'], + dayNames: ['Sunnudagur','Mánadagur','Týsdagur','Mikudagur','Hósdagur','Fríggjadagur','Leyardagur'], + dayNamesShort: ['Sun','Mán','Týs','Mik','Hós','Frí','Ley'], + dayNamesMin: ['Su','Má','Tý','Mi','Hó','Fr','Le'], + dateFormat: 'dd-mm-yyyy', + firstDay: 0, + renderer: $.datepick.defaultRenderer, + prevText: '<Sísta', + prevStatus: 'Vís sísta mánaðan', + prevJumpText: '<<', + prevJumpStatus: 'Vís sísta árið', + nextText: 'Næsta>', + nextStatus: 'Vís næsta mánaðan', + nextJumpText: '>>', + nextJumpStatus: 'Vís næsta árið', + currentText: 'à dag', + currentStatus: 'Vís mánaðan fyri í dag', + todayText: 'à dag', + todayStatus: 'Vís mánaðan fyri í dag', + clearText: 'Strika', + clearStatus: 'Strika allir mánaðarnar', + closeText: 'Goym', + closeStatus: 'Goym hetta vindeyðga', + yearStatus: 'Broyt árið', + monthStatus: 'Broyt mánaðan', + weekText: 'Vk', + weekStatus: 'Vika av árinum', + dayStatus: 'Vel DD, M d, yyyy', + defaultStatus: 'Vel ein dato', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.fo); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-fr-CH.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-fr-CH.js new file mode 100755 index 000000000..b8a7a0881 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-fr-CH.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Swiss French localisation for jQuery Datepicker. + Written by Martin Voelkle (martin.voelkle@e-tc.ch). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions['fr-CH'] = { + monthNames: ['Janvier','Février','Mars','Avril','Mai','Juin', + 'Juillet','Août','Septembre','Octobre','Novembre','Décembre'], + monthNamesShort: ['Jan','Fév','Mar','Avr','Mai','Jun', + 'Jul','Aoû','Sep','Oct','Nov','Déc'], + dayNames: ['Dimanche','Lundi','Mardi','Mercredi','Jeudi','Vendredi','Samedi'], + dayNamesShort: ['Dim','Lun','Mar','Mer','Jeu','Ven','Sam'], + dayNamesMin: ['Di','Lu','Ma','Me','Je','Ve','Sa'], + dateFormat: 'dd.mm.yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<Préc', + prevStatus: 'Voir le mois précédent', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Suiv>', + nextStatus: 'Voir le mois suivant', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Courant', + currentStatus: 'Voir le mois courant', + todayText: 'Aujourd\'hui', + todayStatus: 'Voir aujourd\'hui', + clearText: 'Effacer', + clearStatus: 'Effacer la date sélectionnée', + closeText: 'Fermer', + closeStatus: 'Fermer sans modifier', + yearStatus: 'Voir une autre année', + monthStatus: 'Voir un autre mois', + weekText: 'Sm', + weekStatus: '', + dayStatus: '\'Choisir\' le DD d MM', + defaultStatus: 'Choisir la date', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions['fr-CH']); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-fr.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-fr.js new file mode 100755 index 000000000..ff2be7b2c --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-fr.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + French localisation for jQuery Datepicker. + Stéphane Nahmani (sholby@sholby.net). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.fr = { + monthNames: ['Janvier','Février','Mars','Avril','Mai','Juin', + 'Juillet','Août','Septembre','Octobre','Novembre','Décembre'], + monthNamesShort: ['Jan','Fév','Mar','Avr','Mai','Jun', + 'Jul','Aoû','Sep','Oct','Nov','Déc'], + dayNames: ['Dimanche','Lundi','Mardi','Mercredi','Jeudi','Vendredi','Samedi'], + dayNamesShort: ['Dim','Lun','Mar','Mer','Jeu','Ven','Sam'], + dayNamesMin: ['Di','Lu','Ma','Me','Je','Ve','Sa'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<Préc', + prevStatus: 'Voir le mois précédent', + prevJumpText: '<<', + prevJumpStatus: 'Voir l\'année précédent', + nextText: 'Suiv>', + nextStatus: 'Voir le mois suivant', + nextJumpText: '>>', + nextJumpStatus: 'Voir l\'année suivant', + currentText: 'Courant', + currentStatus: 'Voir le mois courant', + todayText: 'Aujourd\'hui', + todayStatus: 'Voir aujourd\'hui', + clearText: 'Effacer', + clearStatus: 'Effacer la date sélectionnée', + closeText: 'Fermer', + closeStatus: 'Fermer sans modifier', + yearStatus: 'Voir une autre année', + monthStatus: 'Voir un autre mois', + weekText: 'Sm', + weekStatus: 'Semaine de l\'année', + dayStatus: '\'Choisir\' le DD d MM', + defaultStatus: 'Choisir la date', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.fr); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-gl.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-gl.js new file mode 100755 index 000000000..fc2160050 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-gl.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Galician localisation for jQuery Datepicker. + Traducido por Manuel (McNuel@gmx.net). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.gl = { + monthNames: ['Xaneiro','Febreiro','Marzo','Abril','Maio','Xuño', + 'Xullo','Agosto','Setembro','Outubro','Novembro','Decembro'], + monthNamesShort: ['Xan','Feb','Mar','Abr','Mai','Xuñ', + 'Xul','Ago','Set','Out','Nov','Dec'], + dayNames: ['Domingo','Luns','Martes','Mércores','Xoves','Venres','Sábado'], + dayNamesShort: ['Dom','Lun','Mar','Mér','Xov','Ven','Sáb'], + dayNamesMin: ['Do','Lu','Ma','Me','Xo','Ve','Sá'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<Ant', + prevStatus: 'Amosar mes anterior', + prevJumpText: '<<', + prevJumpStatus: 'Amosar ano anterior', + nextText: 'Seg>', + nextStatus: 'Amosar mes seguinte', + nextJumpText: '>>', + nextJumpStatus: 'Amosar ano seguinte', + currentText: 'Hoxe', + currentStatus: 'Amosar mes actual', + todayText: 'Hoxe', + todayStatus: 'Amosar mes actual', + clearText: 'Limpar', + clearStatus: 'Borrar data actual', + closeText: 'Pechar', + closeStatus: 'Pechar sen gardar', + yearStatus: 'Amosar outro ano', + monthStatus: 'Amosar outro mes', + weekText: 'Sm', + weekStatus: 'Semana do ano', + dayStatus: 'D, M d', + defaultStatus: 'Selecciona Data', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.gl); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-gu.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-gu.js new file mode 100755 index 000000000..c2e64bffc --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-gu.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Gujarati (ગà«àªœàª°àª¾àª¤à«€) localisation for jQuery Datepicker. + Naymesh Mistry (naymesh@yahoo.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.gu = { + monthNames: ['જાનà«àª¯à«àª†àª°à«€','ફેબà«àª°à«àª†àª°à«€','મારà«àªš','àªàªªà«àª°àª¿àª²','મે','જૂન', + 'જà«àª²àª¾àªˆ','ઑગસà«àªŸ','સપà«àªŸà«‡àª®à«àª¬àª°','ઑકà«àªŸà«‹àª¬àª°','નવેમà«àª¬àª°','ડિસેમà«àª¬àª°'], + monthNamesShort: ['જાનà«àª¯à«','ફેબà«àª°à«','મારà«àªš','àªàªªà«àª°àª¿àª²','મે','જૂન', + 'જà«àª²àª¾àªˆ','ઑગસà«àªŸ','સપà«àªŸà«‡','ઑકà«àªŸà«‹','નવે','ડિસે'], + dayNames: ['રવિવાર','સોમવાર','મંગળવાર','બà«àª§àªµàª¾àª°','ગà«àª°à«àªµàª¾àª°','શà«àª•à«àª°àªµàª¾àª°','શનિવાર'], + dayNamesShort: ['રવિ','સોમ','મંગળ','બà«àª§','ગà«àª°à«','શà«àª•à«àª°','શનિ'], + dayNamesMin: ['ર','સો','મં','બà«','ગà«','શà«','શ'], + dateFormat: 'dd-M-yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<પાછળ', + prevStatus: 'પાછલો મહિનો બતાવો', + prevJumpText: '<<', + prevJumpStatus: 'પાછળ', + nextText: 'આગળ>', + nextStatus: 'આગલો મહિનો બતાવો', + nextJumpText: '>>', + nextJumpStatus: 'આગળ', + currentText: 'આજે', + currentStatus: 'આજનો દિવસ બતાવો', + todayText: 'આજે', + todayStatus: 'આજનો દિવસ', + clearText: 'ભૂંસો', + clearStatus: 'હાલ પસંદ કરેલી તારીખ ભૂંસો', + closeText: 'બંધ કરો', + closeStatus: 'તારીખ પસંદ કરà«àª¯àª¾ વગર બંધ કરો', + yearStatus: 'જà«àª¦à« વરà«àª· બતાવો', + monthStatus: 'જà«àª¦à«‹ મહિનો બતાવો', + weekText: 'અઠવાડિયà«àª‚', + weekStatus: 'અઠવાડિયà«àª‚', + dayStatus: 'અઠવાડિયાનો પહેલો દિવસ પસંદ કરો', + defaultStatus: 'તારીખ પસંદ કરો', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.gu); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-he.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-he.js new file mode 100755 index 000000000..3b1191bc0 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-he.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Hebrew localisation for jQuery Datepicker. + Written by Amir Hardon (ahardon at gmail dot com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.he = { + monthNames: ['ינו×ר','פברו×ר','מרץ','×פריל','מ××™','יוני', + 'יולי','×וגוסט','ספטמבר','×וקטובר','נובמבר','דצמבר'], + monthNamesShort: ['1','2','3','4','5','6', + '7','8','9','10','11','12'], + dayNames: ['ר×שון','שני','שלישי','רביעי','חמישי','שישי','שבת'], + dayNamesShort: ['×\'','ב\'','×’\'','ד\'','×”\'','ו\'','שבת'], + dayNamesMin: ['×\'','ב\'','×’\'','ד\'','×”\'','ו\'','שבת'], + dateFormat: 'dd/mm/yyyy', + firstDay: 0, + renderer: $.datepick.defaultRenderer, + prevText: '<הקוד×', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'הב×>', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'היו×', + currentStatus: '', + todayText: 'היו×', + todayStatus: '', + clearText: '× ×§×”', + clearStatus: '', + closeText: 'סגור', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Wk', + weekStatus: '', + dayStatus: 'DD, M d', + defaultStatus: '', + isRTL: true + }; + $.datepick.setDefaults($.datepick.regionalOptions.he); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-hi-IN.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-hi-IN.js new file mode 100755 index 000000000..01bea0dd8 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-hi-IN.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Hindi INDIA localisation for jQuery Datepicker. + Written by Pawan Kumar Singh. */ +(function($) { + 'use strict'; + $.datepick.regionalOptions['hi-IN'] = { + monthNames: ['जनवरी',' फरवरी','मारà¥à¤š','अपà¥à¤°à¥ˆà¤²','मई','जून', + 'जà¥à¤²à¤¾à¤ˆ','अगसà¥à¤¤','सितमà¥à¤¬à¤°','अकà¥à¤Ÿà¥‚बर','नवमà¥à¤¬à¤°','दिसमà¥à¤¬à¤°'], + monthNamesShort: ['जन','फर','मारà¥à¤š','अपà¥à¤°à¥ˆ','मई','जून', + 'जà¥à¤²à¤¾à¤ˆ','अग','सित','अकà¥à¤Ÿà¥‚','नव','दिस'], + dayNames: ['रविवार','सोमवार','मंगलवार','बà¥à¤§à¤µà¤¾à¤°','गà¥à¤°à¥à¤µà¤¾à¤°','शà¥à¤•à¥à¤°à¤µà¤¾à¤°','शनिवार'], + dayNamesShort: ['रवि','सोम','मंगल','बà¥à¤§','गà¥à¤°à¥','शà¥à¤•à¥à¤°','शनि'], + dayNamesMin: ['र','सो','मं','बà¥','गà¥','शà¥','श'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: 'पिछला', + prevStatus: 'पिछला महीना देखें', + prevJumpText: '<<', + prevJumpStatus: 'पिछला वरà¥à¤· देखें', + nextText: 'अगला', + nextStatus: 'अगला महीना देखें', + nextJumpText: '>>', + nextJumpStatus: 'अगला वरà¥à¤· देखें', + currentText: 'वरà¥à¤¤à¤®à¤¾à¤¨', + currentStatus: 'वरà¥à¤¤à¤®à¤¾à¤¨ महीना देखें', + todayText: 'आज', + todayStatus: 'वरà¥à¤¤à¤®à¤¾à¤¨ दिन देखें', + clearText: 'साफ', + clearStatus: 'वरà¥à¤¤à¤®à¤¾à¤¨ दिनांक मिटाà¤', + closeText: 'समापà¥à¤¤', + closeStatus: 'बदलाव के बिना बंद', + yearStatus: 'à¤à¤• अलग वरà¥à¤· का चयन करें', + monthStatus: 'à¤à¤• अलग महीने का चयन करें', + weekText: 'Wk', + weekStatus: 'वरà¥à¤· का सपà¥à¤¤à¤¾à¤¹', + dayStatus: 'चà¥à¤¨à¥‡ DD, M d', + defaultStatus: 'à¤à¤• तिथि का चयन करें', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions['hi-IN']); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-hi.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-hi.js new file mode 100755 index 000000000..b6de8347c --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-hi.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + English Hindi localisation for jQuery Datepicker. + Written by Tirumal Rao of designphilic.com. */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.hi = { + monthNames: ['जनवरी','फरवरी','मारà¥à¤š','अपà¥à¤°à¥ˆà¤²','मई','जून', + 'जà¥à¤²à¤¾à¤ˆ','अगसà¥à¤¤','सितंबर','अकà¥à¤Ÿà¥‚बर','नवंबर','दिसंबर'], + monthNamesShort: ['जन','फ़र.','मारà¥à¤š','अपà¥à¤°à¥ˆà¤²','मई','जून', + 'जà¥à¤²à¤¾à¤ˆ','अगसà¥à¤¤','सितंबर','अकà¥à¤Ÿà¥‚बर','नवंबर','दिसंबर'], + dayNames: ['रविवार','सोमवार','मंगलवार','बà¥à¤§à¤µà¤¾à¤°','बृहसà¥à¤ªà¤¤à¤¿à¤µà¤¾à¤°','शà¥à¤•à¥à¤°à¤µà¤¾à¤°','शनिवार'], + dayNamesShort: ['रवि','सोम','मंगल','बà¥à¤§','बृहसà¥à¤ªà¤¤','शà¥à¤•à¥à¤°','शनि'], + dayNamesMin: ['रवि','सोम','मंगल','बà¥à¤§','बृहसà¥à¤ªà¤¤','शà¥à¤•à¥à¤°','शनि'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: 'पिछला', + prevStatus: 'पिछले महीने', + prevJumpText: '<<', + prevJumpStatus: 'पिछले वरà¥à¤·', + nextText: 'अगला', + nextStatus: 'अगले महीने', + nextJumpText: '>>', + nextJumpStatus: 'अगले साल', + currentText: 'वरà¥à¤¤à¤®à¤¾à¤¨', + currentStatus: 'चालू माह', + todayText: 'आज', + todayStatus: 'आजका महीना', + clearText: 'साफ़', + clearStatus: 'वरà¥à¤¤à¤®à¤¾à¤¨ दिनांक मिटा', + closeText: 'ठीक है', + closeStatus: 'बदलाव के बिना बंद', + yearStatus: 'à¤à¤• अलग वरà¥à¤· दिखाà¤à¤', + monthStatus: 'दिखाà¤à¤ किसी अनà¥à¤¯ महीने के', + weekText: 'Wk', + weekStatus: 'Week of the year', + dayStatus: 'चयन DD, M d', + defaultStatus: 'à¤à¤• तिथि का चयन करें', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.hi); +})(jQuery); \ No newline at end of file diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-hr.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-hr.js new file mode 100755 index 000000000..741393573 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-hr.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Croatian localisation for jQuery Datepicker. + Written by Vjekoslav Nesek. */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.hr = { + monthNames: ['SijeÄanj','VeljaÄa','Ožujak','Travanj','Svibanj','Lipanj', + 'Srpanj','Kolovoz','Rujan','Listopad','Studeni','Prosinac'], + monthNamesShort: ['Sij','Velj','Ožu','Tra','Svi','Lip', + 'Srp','Kol','Ruj','Lis','Stu','Pro'], + dayNames: ['Nedjelja','Ponedjeljak','Utorak','Srijeda','ÄŒetvrtak','Petak','Subota'], + dayNamesShort: ['Ned','Pon','Uto','Sri','ÄŒet','Pet','Sub'], + dayNamesMin: ['Ne','Po','Ut','Sr','ÄŒe','Pe','Su'], + dateFormat: 'dd.mm.yyyy.', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<', + prevStatus: 'Prikaži prethodni mjesec', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: '>', + nextStatus: 'Prikaži slijedeći mjesec', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Danas', + currentStatus: 'DanaÅ¡nji datum', + todayText: 'Danas', + todayStatus: 'DanaÅ¡nji datum', + clearText: 'izbriÅ¡i', + clearStatus: 'IzbriÅ¡i trenutni datum', + closeText: 'Zatvori', + closeStatus: 'Zatvori kalendar', + yearStatus: 'Prikaži godine', + monthStatus: 'Prikaži mjesece', + weekText: 'Tje', + weekStatus: 'Tjedan', + dayStatus: '\'Datum\' D, M d', + defaultStatus: 'Odaberi datum', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.hr); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-hu.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-hu.js new file mode 100755 index 000000000..e7957165c --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-hu.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Hungarian localisation for jQuery Datepicker. + Written by Istvan Karaszi (jquery@spam.raszi.hu). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.hu = { + monthNames: ['Január','Február','Március','Ãprilis','Május','Június', + 'Július','Augusztus','Szeptember','Október','November','December'], + monthNamesShort: ['Jan','Feb','Már','Ãpr','Máj','Jún', + 'Júl','Aug','Szep','Okt','Nov','Dec'], + dayNames: ['Vasárnap','Hétfö','Kedd','Szerda','Csütörtök','Péntek','Szombat'], + dayNamesShort: ['Vas','Hét','Ked','Sze','Csü','Pén','Szo'], + dayNamesMin: ['V','H','K','Sze','Cs','P','Szo'], + dateFormat: 'yyyy-mm-dd', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '« vissza', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'elÅ‘re »', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'ma', + currentStatus: '', + todayText: 'ma', + todayStatus: '', + clearText: 'törlés', + clearStatus: '', + closeText: 'bezárás', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Hé', + weekStatus: '', + dayStatus: 'D, M d', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.hu); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-hy.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-hy.js new file mode 100755 index 000000000..571032a94 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-hy.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Armenian localisation for jQuery Datepicker. + Written by Levon Zakaryan (levon.zakaryan@gmail.com)*/ +(function($) { + 'use strict'; + $.datepick.regionalOptions.hy = { + monthNames: ['Õ€Õ¸Ö‚Õ¶Õ¾Õ¡Ö€','Õ“Õ¥Õ¿Ö€Õ¾Õ¡Ö€','Õ„Õ¡Ö€Õ¿','Ô±ÕºÖ€Õ«Õ¬','Õ„Õ¡ÕµÕ«Õ½','Õ€Õ¸Ö‚Õ¶Õ«Õ½', + 'Õ€Õ¸Ö‚Õ¬Õ«Õ½','Õ•Õ£Õ¸Õ½Õ¿Õ¸Õ½','ÕÕ¥ÕºÕ¿Õ¥Õ´Õ¢Õ¥Ö€','Õ€Õ¸Õ¯Õ¿Õ¥Õ´Õ¢Õ¥Ö€','Õ†Õ¸ÕµÕ¥Õ´Õ¢Õ¥Ö€','Ô´Õ¥Õ¯Õ¿Õ¥Õ´Õ¢Õ¥Ö€'], + monthNamesShort: ['Õ€Õ¸Ö‚Õ¶Õ¾','Õ“Õ¥Õ¿Ö€','Õ„Õ¡Ö€Õ¿','Ô±ÕºÖ€','Õ„Õ¡ÕµÕ«Õ½','Õ€Õ¸Ö‚Õ¶Õ«Õ½', + 'Õ€Õ¸Ö‚Õ¬','Õ•Õ£Õ½','ÕÕ¥Õº','Õ€Õ¸Õ¯','Õ†Õ¸Õµ','Ô´Õ¥Õ¯'], + dayNames: ['Õ¯Õ«Ö€Õ¡Õ¯Õ«','Õ¥Õ¯Õ¸Ö‚Õ·Õ¡Õ¢Õ©Õ«','Õ¥Ö€Õ¥Ö„Õ·Õ¡Õ¢Õ©Õ«','Õ¹Õ¸Ö€Õ¥Ö„Õ·Õ¡Õ¢Õ©Õ«','Õ°Õ«Õ¶Õ£Õ·Õ¡Õ¢Õ©Õ«','Õ¸Ö‚Ö€Õ¢Õ¡Õ©','Õ·Õ¡Õ¢Õ¡Õ©'], + dayNamesShort: ['Õ¯Õ«Ö€','Õ¥Ö€Õ¯','Õ¥Ö€Ö„','Õ¹Ö€Ö„','Õ°Õ¶Õ£','Õ¸Ö‚Ö€Õ¢','Õ·Õ¢Õ©'], + dayNamesMin: ['Õ¯Õ«Ö€','Õ¥Ö€Õ¯','Õ¥Ö€Ö„','Õ¹Ö€Ö„','Õ°Õ¶Õ£','Õ¸Ö‚Ö€Õ¢','Õ·Õ¢Õ©'], + dateFormat: 'dd.mm.yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<Õ†Õ¡Õ­.', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Õ€Õ¡Õ».>', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Ô±ÕµÕ½Ö…Ö€', + currentStatus: '', + todayText: 'Ô±ÕµÕ½Ö…Ö€', + todayStatus: '', + clearText: 'Õ„Õ¡Ö„Ö€Õ¥Õ¬', + clearStatus: '', + closeText: 'Õ“Õ¡Õ¯Õ¥Õ¬', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Õ‡Ô²Õ', + weekStatus: '', + dayStatus: 'D, M d', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.hy); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-id.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-id.js new file mode 100755 index 000000000..f4b4ac253 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-id.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Indonesian localisation for jQuery Datepicker. + Written by Deden Fathurahman (dedenf@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.id = { + monthNames: ['Januari','Februari','Maret','April','Mei','Juni', + 'Juli','Agustus','September','Oktober','Nopember','Desember'], + monthNamesShort: ['Jan','Feb','Mar','Apr','Mei','Jun', + 'Jul','Agus','Sep','Okt','Nop','Des'], + dayNames: ['Minggu','Senin','Selasa','Rabu','Kamis','Jumat','Sabtu'], + dayNamesShort: ['Min','Sen','Sel','Rab','kam','Jum','Sab'], + dayNamesMin: ['Mg','Sn','Sl','Rb','Km','jm','Sb'], + dateFormat: 'dd/mm/yyyy', + firstDay: 0, + renderer: $.datepick.defaultRenderer, + prevText: '<mundur', + prevStatus: 'Tampilkan bulan sebelumnya', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'maju>', + nextStatus: 'Tampilkan bulan berikutnya', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'hari ini', + currentStatus: 'Tampilkan bulan sekarang', + todayText: 'hari ini', + todayStatus: 'Tampilkan bulan sekarang', + clearText: 'kosongkan', + clearStatus: 'bersihkan tanggal yang sekarang', + closeText: 'Tutup', + closeStatus: 'Tutup tanpa mengubah', + yearStatus: 'Tampilkan tahun yang berbeda', + monthStatus: 'Tampilkan bulan yang berbeda', + weekText: 'Mg', + weekStatus: 'Minggu dalam tahun', + dayStatus: 'pilih le DD, MM d', + defaultStatus: 'Pilih Tanggal', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.id); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-is.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-is.js new file mode 100755 index 000000000..5f09ccf79 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-is.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Icelandic localisation for jQuery Datepicker. + Written by Haukur H. Thorsson (haukur@eskill.is). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.is = { + monthNames: ['Janúar','Febrúar','Mars','Apríl','Maí','Júní', + 'Júlí','Ágúst','September','Október','Nóvember','Desember'], + monthNamesShort: ['Jan','Feb','Mar','Apr','Maí','Jún', + 'Júl','Ágú','Sep','Okt','Nóv','Des'], + dayNames: ['Sunnudagur','Mánudagur','Þriðjudagur','Miðvikudagur','Fimmtudagur','Föstudagur','Laugardagur'], + dayNamesShort: ['Sun','Mán','Þri','Mið','Fim','Fös','Lau'], + dayNamesMin: ['Su','Má','Þr','Mi','Fi','Fö','La'], + dateFormat: 'dd/mm/yyyy', + firstDay: 0, + renderer: $.datepick.defaultRenderer, + prevText: '< Fyrri', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Næsti >', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Í dag', + currentStatus: '', + todayText: 'Í dag', + todayStatus: '', + clearText: 'Hreinsa', + clearStatus: '', + closeText: 'Loka', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Vika', + weekStatus: '', + dayStatus: 'D, M d', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.is); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-it.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-it.js new file mode 100755 index 000000000..483dfad12 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-it.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Italian localisation for jQuery Datepicker. + Written by Apaella (apaella@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.it = { + monthNames: ['Gennaio','Febbraio','Marzo','Aprile','Maggio','Giugno', + 'Luglio','Agosto','Settembre','Ottobre','Novembre','Dicembre'], + monthNamesShort: ['Gen','Feb','Mar','Apr','Mag','Giu', + 'Lug','Ago','Set','Ott','Nov','Dic'], + dayNames: ['Domenica','Lunedì','Martedì','Mercoledì','Giovedì','Venerdì','Sabato'], + dayNamesShort: ['Dom','Lun','Mar','Mer','Gio','Ven','Sab'], + dayNamesMin: ['Do','Lu','Ma','Me','Gi','Ve','Sa'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<Prec', + prevStatus: 'Mese precedente', + prevJumpText: '<<', + prevJumpStatus: 'Mostra l\'anno precedente', + nextText: 'Succ>', + nextStatus: 'Mese successivo', + nextJumpText: '>>', + nextJumpStatus: 'Mostra l\'anno successivo', + currentText: 'Oggi', + currentStatus: 'Mese corrente', + todayText: 'Oggi', + todayStatus: 'Mese corrente', + clearText: 'Svuota', + clearStatus: 'Annulla', + closeText: 'Chiudi', + closeStatus: 'Chiudere senza modificare', + yearStatus: 'Seleziona un altro anno', + monthStatus: 'Seleziona un altro mese', + weekText: 'Sm', + weekStatus: 'Settimana dell\'anno', + dayStatus: '\'Seleziona\' D, M d', + defaultStatus: 'Scegliere una data', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.it); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-ja.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-ja.js new file mode 100755 index 000000000..13616c48e --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-ja.js @@ -0,0 +1,44 @@ +/* http://keith-wood.name/datepick.html + Japanese localisation for jQuery Datepicker. + Written by Kentaro SATO (kentaro@ranvis.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.ja = { + monthNames: ['1月','2月','3月','4月','5月','6月', + '7月','8月','9月','10月','11月','12月'], + monthNamesShort: ['1月','2月','3月','4月','5月','6月', + '7月','8月','9月','10月','11月','12月'], + dayNames: ['日曜日','月曜日','ç«æ›œæ—¥','水曜日','木曜日','金曜日','土曜日'], + dayNamesShort: ['æ—¥','月','ç«','æ°´','木','金','土'], + dayNamesMin: ['æ—¥','月','ç«','æ°´','木','金','土'], + dateFormat: 'yyyy/mm/dd', + firstDay: 0, + renderer: $.extend({}, $.datepick.defaultRenderer, { + month: $.datepick.defaultRenderer.month.replace(/monthHeader/, 'monthHeader:yyyyå¹´ MM') + }), + prevText: '<å‰', + prevStatus: '剿œˆã‚’表示ã—ã¾ã™', + prevJumpText: '<<', + prevJumpStatus: 'å‰å¹´ã‚’表示ã—ã¾ã™', + nextText: '次>', + nextStatus: '翌月を表示ã—ã¾ã™', + nextJumpText: '>>', + nextJumpStatus: '翌年を表示ã—ã¾ã™', + currentText: '今日', + currentStatus: '今月を表示ã—ã¾ã™', + todayText: '今日', + todayStatus: '今月を表示ã—ã¾ã™', + clearText: 'クリア', + clearStatus: '日付をクリアã—ã¾ã™', + closeText: 'é–‰ã˜ã‚‹', + closeStatus: '変更ã›ãšã«é–‰ã˜ã¾ã™', + yearStatus: '表示ã™ã‚‹å¹´ã‚’変更ã—ã¾ã™', + monthStatus: '表示ã™ã‚‹æœˆã‚’変更ã—ã¾ã™', + weekText: '週', + weekStatus: '暦週ã§ç¬¬ä½•週目ã‹ã‚’表ã—ã¾ã™', + dayStatus: 'Mdæ—¥(D)', + defaultStatus: 'æ—¥ä»˜ã‚’é¸æŠžã—ã¾ã™', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.ja); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-ka.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-ka.js new file mode 100755 index 000000000..a8abbc0ec --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-ka.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Georgian localisation for jQuery Datepicker. + Andrei Gorbushkin. */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.ka = { + monthNames: ['იáƒáƒœáƒ•áƒáƒ áƒ˜','თებერვáƒáƒšáƒ˜','მáƒáƒ áƒ¢áƒ˜','áƒáƒžáƒ áƒ˜áƒšáƒ˜','მáƒáƒ˜áƒ¡áƒ˜','ივნისი', + 'ივლისი','áƒáƒ’ვისტáƒ','სექტემბერი','áƒáƒ¥áƒ¢áƒáƒ›áƒ‘ერი','ნáƒáƒ”მბერი','დეკემბერი'], + monthNamesShort: ['იáƒáƒœ','თებ','მáƒáƒ ','áƒáƒžáƒ ','მáƒáƒ˜áƒ¡áƒ˜','ივნ', + 'ივლ','áƒáƒ’ვ','სექ','áƒáƒ¥áƒ¢','ნáƒáƒ”','დეკ'], + dayNames: ['კვირáƒ','áƒáƒ áƒ¨áƒáƒ‘áƒáƒ—ი','სáƒáƒ›áƒ¨áƒáƒ‘áƒáƒ—ი','áƒáƒ—ხშáƒáƒ‘áƒáƒ—ი','ხუთშáƒáƒ‘áƒáƒ—ი','პáƒáƒ áƒáƒ¡áƒ™áƒ”ვი','შáƒáƒ‘áƒáƒ—ი'], + dayNamesShort: ['კვ','áƒáƒ áƒ¨','სáƒáƒ›','áƒáƒ—ხ','ხუთ','პáƒáƒ ','შáƒáƒ‘'], + dayNamesMin: ['კვ','áƒáƒ ','სმ','áƒáƒ—','ხშ','პრ','შბ'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<უკáƒáƒœ', + prevStatus: 'წინრთვე', + prevJumpText: '<<', + prevJumpStatus: 'წინრწელი', + nextText: 'წინ>', + nextStatus: 'შემდეგი თვე', + nextJumpText: '>>', + nextJumpStatus: 'შემდეგი წელი', + currentText: 'მიმდინáƒáƒ áƒ”', + currentStatus: 'მიმდინáƒáƒ áƒ” თვე', + todayText: 'დღეს', + todayStatus: 'მიმდინáƒáƒ áƒ” დღე', + clearText: 'გáƒáƒ¡áƒ£áƒ¤áƒ—áƒáƒ•ებáƒ', + clearStatus: 'მიმდინáƒáƒ áƒ” თáƒáƒ áƒ˜áƒ¦áƒ˜áƒ¡ წáƒáƒ¨áƒšáƒ', + closeText: 'áƒáƒ áƒ˜áƒ¡', + closeStatus: 'დáƒáƒ®áƒ£áƒ áƒ•რუცვლილებáƒáƒ“', + yearStatus: 'სხვრწელი', + monthStatus: 'სხვრთვე', + weekText: 'კვ', + weekStatus: 'წლის კვირáƒ', + dayStatus: 'áƒáƒ˜áƒ áƒ©áƒ˜áƒ”თ DD, M d', + defaultStatus: 'áƒáƒ˜áƒ¦áƒ©áƒ˜áƒ”თ თáƒáƒ áƒ˜áƒ¦áƒ˜', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.ka); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-km.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-km.js new file mode 100755 index 000000000..2bb5c6815 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-km.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Khmer initialisation for jQuery Datepicker. + Written by Sovichet Tep (sovichet.tep@gmail.com). */ +(function($){ + 'use strict'; + $.datepick.regionalOptions.km = { + monthNames: ['ážáŸ‚​មករា','ážáŸ‚​កុម្ភៈ','ážáŸ‚​មិនា','ážáŸ‚​មáŸážŸáž¶','ážáŸ‚​ឧសភា','ážáŸ‚​មិážáž»áž“áž¶', + 'ážáŸ‚​កក្កដា','ážáŸ‚​សីហា','ážáŸ‚​កញ្ញា','ážáŸ‚​ážáž»áž›áž¶','ážáŸ‚​វិច្ឆិកា','ážáŸ‚​ធ្នូ'], + monthNamesShort: ['មក','កុ','មិនា','មáŸ','ឧស','មិážáž»', + 'កក្ក','សី','កញ្ញា','ážáž»áž›áž¶','វិច្ឆិ','ធ្នូ'], + dayNames: ['ážáŸ’ងៃ​អាទិážáŸ’áž™','ážáŸ’ងៃ​ចន្ទ','ážáŸ’ងៃ​អង្គារ','ážáŸ’ងៃ​ពុធ','ážáŸ’ងៃ​ព្រហស្បážáŸ’ážáž·áŸ','ážáŸ’ងៃ​សុក្រ','ážáŸ’ងៃ​សៅរáŸ'], + dayNamesShort: ['អា','ចន្ទ','អង្គ','ពុធ','ព្រហ','សុ','សៅរáŸ'], + dayNamesMin: ['អា','áž…','អ','áž–áž»','ព្រ','សុ','ស'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: 'ážáž™â€‹áž€áŸ’រោយ', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'ទៅ​មុáž', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'ážáŸ’ងៃ​នáŸáŸ‡', + currentStatus: '', + todayText: 'ážáŸ’ងៃ​នáŸáŸ‡', + todayStatus: '', + clearText: 'X', + clearStatus: '', + closeText: 'រួច​រាល់', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Wk', + weekStatus: '', + dayStatus: 'DD d MM', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.km); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-ko.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-ko.js new file mode 100755 index 000000000..82a5fd302 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-ko.js @@ -0,0 +1,44 @@ +/* http://keith-wood.name/datepick.html + Korean localisation for jQuery Datepicker. + Written by DaeKwon Kang (ncrash.dk@gmail.com), Edited by Genie. */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.ko = { + monthNames: ['1ì›”','2ì›”','3ì›”','4ì›”','5ì›”','6ì›”', + '7ì›”','8ì›”','9ì›”','10ì›”','11ì›”','12ì›”'], + monthNamesShort: ['1ì›”','2ì›”','3ì›”','4ì›”','5ì›”','6ì›”', + '7ì›”','8ì›”','9ì›”','10ì›”','11ì›”','12ì›”'], + dayNames: ['ì¼ìš”ì¼','월요ì¼','화요ì¼','수요ì¼','목요ì¼','금요ì¼','토요ì¼'], + dayNamesShort: ['ì¼','ì›”','í™”','수','목','금','토'], + dayNamesMin: ['ì¼','ì›”','í™”','수','목','금','토'], + dateFormat: 'yyyy-mm-dd', + firstDay: 0, + renderer: $.extend({}, $.datepick.defaultRenderer, { + month: $.datepick.defaultRenderer.month.replace(/monthHeader/, 'monthHeader:yyyyë…„ MM') + }), + prevText: 'ì´ì „달', + prevStatus: 'ì´ì „ë‹¬ì„ í‘œì‹œí•©ë‹ˆë‹¤', + prevJumpText: '<<', + prevJumpStatus: 'ì´ì „ ì—°ë„를 표시합니다', + nextText: '다ìŒë‹¬', + nextStatus: '다ìŒë‹¬ì„ 표시합니다', + nextJumpText: '>>', + nextJumpStatus: 'ë‹¤ìŒ ì—°ë„를 표시합니다', + currentText: '현재', + currentStatus: '입력한 ë‹¬ì„ í‘œì‹œí•©ë‹ˆë‹¤', + todayText: '오늘', + todayStatus: 'ì´ë²ˆë‹¬ì„ 표시합니다', + clearText: '지우기', + clearStatus: '입력한 날짜를 ì§€ì›ë‹ˆë‹¤', + closeText: '닫기', + closeStatus: '', + yearStatus: '표시할 ì—°ë„를 변경합니다', + monthStatus: '표시할 ì›”ì„ ë³€ê²½í•©ë‹ˆë‹¤', + weekText: 'Wk', + weekStatus: '해당 ì—°ë„ì˜ ì£¼ì°¨', + dayStatus: 'M dì¼ (D)', + defaultStatus: '날짜를 ì„ íƒí•˜ì„¸ìš”', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.ko); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-lt.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-lt.js new file mode 100755 index 000000000..68ac25f6d --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-lt.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Lithuanian localisation for jQuery Datepicker. + Written by Arturas Paleicikas */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.lt = { + monthNames: ['Sausis','Vasaris','Kovas','Balandis','Gegužė','Birželis', + 'Liepa','RugpjÅ«tis','RugsÄ—jis','Spalis','Lapkritis','Gruodis'], + monthNamesShort: ['Sau','Vas','Kov','Bal','Geg','Bir', + 'Lie','Rugp','Rugs','Spa','Lap','Gru'], + dayNames: ['sekmadienis','pirmadienis','antradienis','treÄiadienis','ketvirtadienis','penktadienis','Å¡eÅ¡tadienis'], + dayNamesShort: ['sek','pir','ant','tre','ket','pen','Å¡eÅ¡'], + dayNamesMin: ['Se','Pr','An','Tr','Ke','Pe','Å e'], + dateFormat: 'yyyy-mm-dd', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<Atgal', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Pirmyn>', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Å iandien', + currentStatus: '', + todayText: 'Å iandien', + todayStatus: '', + clearText: 'IÅ¡valyti', + clearStatus: '', + closeText: 'Uždaryti', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Wk', + weekStatus: '', + dayStatus: 'D, M d', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.lt); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-lv.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-lv.js new file mode 100755 index 000000000..95d438d62 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-lv.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Latvian localisation for jQuery Datepicker. + Written by Arturas Paleicikas */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.lv = { + monthNames: ['JanvÄris','FebruÄris','Marts','AprÄ«lis','Maijs','JÅ«nijs', + 'JÅ«lijs','Augusts','Septembris','Oktobris','Novembris','Decembris'], + monthNamesShort: ['Jan','Feb','Mar','Apr','Mai','JÅ«n', + 'JÅ«l','Aug','Sep','Okt','Nov','Dec'], + dayNames: ['svÄ“tdiena','pirmdiena','otrdiena','treÅ¡diena','ceturtdiena','piektdiena','sestdiena'], + dayNamesShort: ['svt','prm','otr','tre','ctr','pkt','sst'], + dayNamesMin: ['Sv','Pr','Ot','Tr','Ct','Pk','Ss'], + dateFormat: 'dd-mm-yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: 'Iepr', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'NÄka', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Å odien', + currentStatus: '', + todayText: 'Å odien', + todayStatus: '', + clearText: 'NotÄ«rÄ«t', + clearStatus: '', + closeText: 'AizvÄ“rt', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Nav', + weekStatus: '', + dayStatus: 'D, M d', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.lv); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-me-ME.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-me-ME.js new file mode 100755 index 000000000..9cdb80f2d --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-me-ME.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Montenegrin localisation for jQuery Datepicker. + By MiloÅ¡ MiloÅ¡ević - fleka d.o.o. */ +(function($) { + 'use strict'; + $.datepick.regionalOptions['me-ME'] = { + monthNames: ['Januar','Februar','Mart','April','Maj','Jun', + 'Jul','Avgust','Septembar','Oktobar','Novembar','Decembar'], + monthNamesShort: ['Jan','Feb','Mar','Apr','Maj','Jun', + 'Jul','Avg','Sep','Okt','Nov','Dec'], + dayNames: ['NeÄ‘elja','PoneÄ‘eljak','Utorak','Srijeda','ÄŒetvrtak','Petak','Subota'], + dayNamesShort: ['NeÄ‘','Pon','Uto','Sri','ÄŒet','Pet','Sub'], + dayNamesMin: ['Ne','Po','Ut','Sr','ÄŒe','Pe','Su'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<', + prevStatus: 'Prikaži prethodni mjesec', + prevJumpText: '<<', + prevJumpStatus: 'Prikaži prethodnu godinu', + nextText: '>', + nextStatus: 'Prikaži sljedeći mjesec', + nextJumpText: '>>', + nextJumpStatus: 'Prikaži sljedeću godinu', + currentText: 'Danas', + currentStatus: 'Tekući mjesec', + todayText: 'Danas', + todayStatus: 'Tekući mjesec', + clearText: 'ObriÅ¡i', + clearStatus: 'ObriÅ¡i trenutni datum', + closeText: 'Zatvori', + closeStatus: 'Zatvori kalendar', + yearStatus: 'Prikaži godine', + monthStatus: 'Prikaži mjesece', + weekText: 'Sed', + weekStatus: 'Sedmica', + dayStatus: '\'Datum\' DD, M d', + defaultStatus: 'Odaberi datum', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions['me-ME']); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-me.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-me.js new file mode 100755 index 000000000..b3c2619f7 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-me.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Montenegrin localisation for jQuery Datepicker. + By MiloÅ¡ MiloÅ¡ević - fleka d.o.o. */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.me = { + monthNames: ['Јануар','Фебруар','Март','Ðприл','Мај','Јун', + 'Јул','ÐвгуÑÑ‚','Септембар','Октобар','Ðовембар','Децембар'], + monthNamesShort: ['Јан','Феб','Мар','Ðпр','Мај','Јун', + 'Јул','Ðвг','Сеп','Окт','Ðов','Дец'], + dayNames: ['Ðеђеља','Понеђељак','Уторак','Сриједа','Четвртак','Петак','Субота'], + dayNamesShort: ['Ðеђ','Пон','Уто','Сри','Чет','Пет','Суб'], + dayNamesMin: ['Ðе','По','Ут','Ср','Че','Пе','Су'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<', + prevStatus: 'Прикажи претходни мјеÑец', + prevJumpText: '<<', + prevJumpStatus: 'Прикажи претходну годину', + nextText: '>', + nextStatus: 'Прикажи Ñљедећи мјеÑец', + nextJumpText: '>>', + nextJumpStatus: 'Прикажи Ñљедећу годину', + currentText: 'ДанаÑ', + currentStatus: 'Текући мјеÑец', + todayText: 'ДанаÑ', + todayStatus: 'Текући мјеÑец', + clearText: 'Обриши', + clearStatus: 'Обриши тренутни датум', + closeText: 'Затвори', + closeStatus: 'Затвори календар', + yearStatus: 'Прикажи године', + monthStatus: 'Прикажи мјеÑеце', + weekText: 'Сед', + weekStatus: 'Седмица', + dayStatus: '\'Датум\' DD d MM', + defaultStatus: 'Одабери датум', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.me); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-mk.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-mk.js new file mode 100755 index 000000000..955ab0598 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-mk.js @@ -0,0 +1,44 @@ +/* http://keith-wood.name/datepick.html + МакедонÑки MK localisation for jQuery Datepicker. + Written by Hajan Selmani + email: hajan [at] live [dot] com + url: http://weblogs.asp.net/hajan | http://codeasp.net/blogs/hajan | http://mkdot.net/blogs/hajan */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.mk = { + monthNames: ['Јануари','Февруари','Март','Ðприл','Мај','Јуни', + 'Јули','ÐвгуÑÑ‚','Септември','Октомври','Ðоември','Декември'], + monthNamesShort: ['Јан','Фев','Мар','Ðпр','Мај','Јун', + 'Јул','Ðвг','Сеп','Окт','Ðов','Дек'], + dayNames: ['Ðедела','Понеделник','Вторник','Среда','Четврток','Петок','Сабота'], + dayNamesShort: ['Ðед','Пон','Вто','Сре','Чет','Пет','Саб'], + dayNamesMin: ['Ðе','По','Ð’Ñ‚','Ср','Че','Пе','Са'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: 'Претх.', + prevStatus: 'Прикажи го претходниот меÑец', + prevJumpText: '<<', + prevJumpStatus: 'Прикажи ја претходната година', + nextText: 'Следен', + nextStatus: 'Прикажи го Ñледниот меÑец', + nextJumpText: '>>', + nextJumpStatus: 'Прикажи ја Ñледната година', + currentText: 'Тековен', + currentStatus: 'Прикажи го тековниот меÑец', + todayText: 'ДенеÑ', + todayStatus: 'Прикажи го денешниот меÑец', + clearText: 'Бриши', + clearStatus: 'Избриши го тековниот датум', + closeText: 'Затвори', + closeStatus: 'Затвори без промени', + yearStatus: 'Избери друга година', + monthStatus: 'Избери друг меÑец', + weekText: 'Ðед', + weekStatus: 'Ðедела во годината', + dayStatus: 'Избери DD, M d', + defaultStatus: 'Избери датум', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.mk); +})(jQuery); \ No newline at end of file diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-ml.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-ml.js new file mode 100755 index 000000000..ae942ce7b --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-ml.js @@ -0,0 +1,43 @@ +/* http://keith-wood.name/datepick.html + Malayalam localisation for jQuery Datepicker. + Saji Nediyanchath (saji89@gmail.com). */ +(function($) { + 'use strict'; + /* jshint -W100 */ + $.datepick.regionalOptions.ml = { + monthNames: ['ജനàµà´µà´°à´¿','ഫെബàµà´°àµà´µà´°à´¿','മാരàµâ€à´šàµà´šàµ','à´à´ªàµà´°à´¿à´²àµâ€','മേയàµ','ജൂണàµâ€', + 'ജൂലൈ','ആഗസàµà´±àµà´±àµ','സെപàµà´±àµà´±à´‚ബരàµâ€','à´’à´•àµà´Ÿàµ‹à´¬à´°àµâ€','നവംബരàµâ€','ഡിസംബരàµâ€'], + monthNamesShort: ['ജനàµ','ഫെബàµ','മാരàµâ€','à´à´ªàµà´°à´¿','മേയàµ','ജൂണàµâ€', + 'ജൂലാ','ആഗ','സെപàµ','à´’à´•àµà´Ÿàµ‹','നവം','à´¡à´¿à´¸'], + dayNames: ['ഞായരàµâ€','തിങàµà´•à´³àµâ€','ചൊവàµà´µ','à´¬àµà´§à´¨àµâ€','à´µàµà´¯à´¾à´´à´‚','വെളàµà´³à´¿','ശനി'], + dayNamesShort: ['ഞായ','തിങàµà´•','ചൊവàµà´µ','à´¬àµà´§','à´µàµà´¯à´¾à´´à´‚','വെളàµà´³à´¿','ശനി'], + dayNamesMin: ['à´žà´¾','തി','ചൊ','à´¬àµ','à´µàµà´¯à´¾','വെ','à´¶'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: 'à´®àµà´¨àµà´¨à´¤àµà´¤àµ†', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'à´…à´Ÿàµà´¤àµà´¤à´¤àµ ', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'ഇനàµà´¨àµ', + currentStatus: '', + todayText: 'ഇനàµà´¨àµ', + todayStatus: '', + clearText: 'X', + clearStatus: '', + closeText: 'à´¶à´°à´¿', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'à´†', + weekStatus: '', + dayStatus: 'DD d MM', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.ml); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-ms.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-ms.js new file mode 100755 index 000000000..970928a60 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-ms.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Malaysian localisation for jQuery Datepicker. + Written by Mohd Nawawi Mohamad Jamili (nawawi@ronggeng.net). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.ms = { + monthNames: ['Januari','Februari','Mac','April','Mei','Jun', + 'Julai','Ogos','September','Oktober','November','Disember'], + monthNamesShort: ['Jan','Feb','Mac','Apr','Mei','Jun', + 'Jul','Ogo','Sep','Okt','Nov','Dis'], + dayNames: ['Ahad','Isnin','Selasa','Rabu','Khamis','Jumaat','Sabtu'], + dayNamesShort: ['Aha','Isn','Sel','Rab','Kha','Jum','Sab'], + dayNamesMin: ['Ah','Is','Se','Ra','Kh','Ju','Sa'], + dateFormat: 'dd/mm/yyyy', + firstDay: 0, + renderer: $.datepick.defaultRenderer, + prevText: '<Sebelum', + prevStatus: 'Tunjukkan bulan lepas', + prevJumpText: '<<', + prevJumpStatus: 'Tunjukkan tahun lepas', + nextText: 'Selepas>', + nextStatus: 'Tunjukkan bulan depan', + nextJumpText: '>>', + nextJumpStatus: 'Tunjukkan tahun depan', + currentText: 'hari ini', + currentStatus: 'Tunjukkan bulan terkini', + todayText: 'hari ini', + todayStatus: 'Tunjukkan bulan terkini', + clearText: 'Padam', + clearStatus: 'Padamkan tarikh terkini', + closeText: 'Tutup', + closeStatus: 'Tutup tanpa perubahan', + yearStatus: 'Tunjukkan tahun yang lain', + monthStatus: 'Tunjukkan bulan yang lain', + weekText: 'Mg', + weekStatus: 'Minggu bagi tahun ini', + dayStatus: 'DD, d MM', + defaultStatus: 'Sila pilih tarikh', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.ms); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-mt.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-mt.js new file mode 100755 index 000000000..8938a864a --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-mt.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Maltese localisation for jQuery Datepicker. + Written by Chritian Sciberras (uuf6429@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.mt = { + monthNames: ['Jannar','Frar','Marzu','April','Mejju','Ä unju', + 'Lulju','Awissu','Settembru','Ottubru','Novembru','DiÄ‹embru'], + monthNamesShort: ['Jan','Fra','Mar','Apr','Mej','Ä un', + 'Lul','Awi','Set','Ott','Nov','DiÄ‹'], + dayNames: ['Il-Ħadd','It-Tnejn','It-Tlieta','L-Erbgħa','Il-Ħamis','Il-Ä imgħa','Is-Sibt'], + dayNamesShort: ['Ħad','Tne','Tli','Erb','Ħam','Ä im','Sib'], + dayNamesMin: ['Ħ','T','T','E','Ħ','Ä ','S'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: 'Ta Qabel', + prevStatus: 'Ix-xahar ta qabel', + prevJumpText: '<<', + prevJumpStatus: 'Is-sena ta qabel', + nextText: 'Li Jmiss', + nextStatus: 'Ix-xahar li jmiss', + nextJumpText: '>>', + nextJumpStatus: 'Is-sena li jmiss', + currentText: 'Illum', + currentStatus: 'Ix-xahar ta llum', + todayText: 'Illum', + todayStatus: 'Uri ix-xahar ta llum', + clearText: 'Ħassar', + clearStatus: 'Ħassar id-data', + closeText: 'Lest', + closeStatus: 'Għalaq mingħajr tibdiliet', + yearStatus: 'Uri sena differenti', + monthStatus: 'Uri xahar differenti', + weekText: 'Ä m', + weekStatus: 'Il-Ä imgħa fis-sena', + dayStatus: 'Għazel DD, M d', + defaultStatus: 'Għazel data', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.mt); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-nl-BE.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-nl-BE.js new file mode 100755 index 000000000..f8204ecb9 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-nl-BE.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Dutch/Belgium localisation for jQuery Datepicker. + Written by Mathias Bynens */ +(function($) { + 'use strict'; + $.datepick.regionalOptions['nl-BE'] = { + monthNames: ['januari','februari','maart','april','mei','juni', + 'juli','augustus','september','oktober','november','december'], + monthNamesShort: ['jan','feb','maa','apr','mei','jun', + 'jul','aug','sep','okt','nov','dec'], + dayNames: ['zondag','maandag','dinsdag','woensdag','donderdag','vrijdag','zaterdag'], + dayNamesShort: ['zon','maa','din','woe','don','vri','zat'], + dayNamesMin: ['zo','ma','di','wo','do','vr','za'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: 'â†', + prevStatus: 'Bekijk de vorige maand', + prevJumpText: '«', + prevJumpStatus: 'Bekijk het vorige jaar', + nextText: '→', + nextStatus: 'Bekijk de volgende maand', + nextJumpText: '»', + nextJumpStatus: 'Bekijk het volgende jaar', + currentText: 'Vandaag', + currentStatus: 'Bekijk de huidige maand', + todayText: 'Vandaag', + todayStatus: 'Bekijk de huidige maand', + clearText: 'Wissen', + clearStatus: 'Wis de huidige datum', + closeText: 'Sluiten', + closeStatus: 'Sluit zonder verandering', + yearStatus: 'Bekijk een ander jaar', + monthStatus: 'Bekijk een andere maand', + weekText: 'Wk', + weekStatus: 'Week van het jaar', + dayStatus: 'dd/mm/yyyy', + defaultStatus: 'Kies een datum', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions['nl-BE']); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-nl.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-nl.js new file mode 100755 index 000000000..ab225fe16 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-nl.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Dutch localisation for jQuery Datepicker. + Written by Mathias Bynens */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.nl = { + monthNames: ['januari','februari','maart','april','mei','juni', + 'juli','augustus','september','oktober','november','december'], + monthNamesShort: ['jan','feb','maa','apr','mei','jun', + 'jul','aug','sep','okt','nov','dec'], + dayNames: ['zondag','maandag','dinsdag','woensdag','donderdag','vrijdag','zaterdag'], + dayNamesShort: ['zon','maa','din','woe','don','vri','zat'], + dayNamesMin: ['zo','ma','di','wo','do','vr','za'], + dateFormat: 'dd-mm-yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: 'â†', + prevStatus: 'Bekijk de vorige maand', + prevJumpText: '«', + prevJumpStatus: 'Bekijk het vorige jaar', + nextText: '→', + nextStatus: 'Bekijk de volgende maand', + nextJumpText: '»', + nextJumpStatus: 'Bekijk het volgende jaar', + currentText: 'Vandaag', + currentStatus: 'Bekijk de huidige maand', + todayText: 'Vandaag', + todayStatus: 'Bekijk de huidige maand', + clearText: 'Wissen', + clearStatus: 'Wis de huidige datum', + closeText: 'Sluiten', + closeStatus: 'Sluit zonder verandering', + yearStatus: 'Bekijk een ander jaar', + monthStatus: 'Bekijk een andere maand', + weekText: 'Wk', + weekStatus: 'Week van het jaar', + dayStatus: 'dd-mm-yyyy', + defaultStatus: 'Kies een datum', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.nl); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-no.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-no.js new file mode 100755 index 000000000..09fc187bd --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-no.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Norwegian localisation for jQuery Datepicker. + Written by Naimdjon Takhirov (naimdjon@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.no = { + monthNames: ['Januar','Februar','Mars','April','Mai','Juni', + 'Juli','August','September','Oktober','November','Desember'], + monthNamesShort: ['Jan','Feb','Mar','Apr','Mai','Jun', + 'Jul','Aug','Sep','Okt','Nov','Des'], + dayNamesShort: ['Søn','Man','Tir','Ons','Tor','Fre','Lør'], + dayNames: ['Søndag','Mandag','Tirsdag','Onsdag','Torsdag','Fredag','Lørdag'], + dayNamesMin: ['Sø','Ma','Ti','On','To','Fr','Lø'], + dateFormat: 'dd.mm.yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '«Forrige', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Neste»', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'I dag', + currentStatus: '', + todayText: 'I dag', + todayStatus: '', + clearText: 'Tøm', + clearStatus: '', + closeText: 'Lukk', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Uke', + weekStatus: '', + dayStatus: 'D, M d', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.no); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-pl.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-pl.js new file mode 100755 index 000000000..79b99873a --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-pl.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Polish localisation for jQuery Datepicker. + Written by Jacek Wysocki (jacek.wysocki@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.pl = { + monthNames: ['StyczeÅ„','Luty','Marzec','KwiecieÅ„','Maj','Czerwiec', + 'Lipiec','SierpieÅ„','WrzesieÅ„','Październik','Listopad','GrudzieÅ„'], + monthNamesShort: ['Sty','Lu','Mar','Kw','Maj','Cze', + 'Lip','Sie','Wrz','Pa','Lis','Gru'], + dayNames: ['Niedziela','Poniedzialek','Wtorek','Åšroda','Czwartek','PiÄ…tek','Sobota'], + dayNamesShort: ['Nie','Pn','Wt','Åšr','Czw','Pt','So'], + dayNamesMin: ['N','Pn','Wt','Åšr','Cz','Pt','So'], + dateFormat: 'yyyy-mm-dd', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<Poprzedni', + prevStatus: 'Pokaż poprzedni miesiÄ…c', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'NastÄ™pny>', + nextStatus: 'Pokaż nastÄ™pny miesiÄ…c', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'DziÅ›', + currentStatus: 'Pokaż aktualny miesiÄ…c', + todayText: 'DziÅ›', + todayStatus: 'Pokaż aktualny miesiÄ…c', + clearText: 'Wyczyść', + clearStatus: 'Wyczyść obecnÄ… datÄ™', + closeText: 'Zamknij', + closeStatus: 'Zamknij bez zapisywania', + yearStatus: 'Pokaż inny rok', + monthStatus: 'Pokaż inny miesiÄ…c', + weekText: 'Tydz', + weekStatus: 'TydzieÅ„ roku', + dayStatus: '\'Wybierz\' D, M d', + defaultStatus: 'Wybierz datÄ™', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.pl); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-pt-BR.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-pt-BR.js new file mode 100755 index 000000000..e79e86df1 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-pt-BR.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Brazilian Portuguese localisation for jQuery Datepicker. + Written by Leonildo Costa Silva (leocsilva@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions['pt-BR'] = { + monthNames: ['Janeiro','Fevereiro','Março','Abril','Maio','Junho', + 'Julho','Agosto','Setembro','Outubro','Novembro','Dezembro'], + monthNamesShort: ['Jan','Fev','Mar','Abr','Mai','Jun', + 'Jul','Ago','Set','Out','Nov','Dez'], + dayNames: ['Domingo','Segunda-feira','Terça-feira','Quarta-feira','Quinta-feira','Sexta-feira','Sábado'], + dayNamesShort: ['Dom','Seg','Ter','Qua','Qui','Sex','Sáb'], + dayNamesMin: ['D','S','T','Q','Q','S','S'], + dateFormat: 'dd/mm/yyyy', + firstDay: 0, + renderer: $.datepick.defaultRenderer, + prevText: '<Anterior', + prevStatus: 'Mostra o mês anterior', + prevJumpText: '<<', + prevJumpStatus: 'Mostra o ano anterior', + nextText: 'Próximo>', + nextStatus: 'Mostra o próximo mês', + nextJumpText: '>>', + nextJumpStatus: 'Mostra o próximo ano', + currentText: 'Atual', + currentStatus: 'Mostra o mês atual', + todayText: 'Hoje', + todayStatus: 'Vai para hoje', + clearText: 'Limpar', + clearStatus: 'Limpar data', + closeText: 'Fechar', + closeStatus: 'Fechar o calendário', + yearStatus: 'Selecionar ano', + monthStatus: 'Selecionar mês', + weekText: 's', + weekStatus: 'Semana do ano', + dayStatus: 'DD, d \'de\' M \'de\' yyyy', + defaultStatus: 'Selecione um dia', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions['pt-BR']); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-pt.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-pt.js new file mode 100755 index 000000000..3be60c6ab --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-pt.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Portuguese Portuguese localisation for jQuery Datepicker. + Written by Telmo Martinho (telmomartinho@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.pt = { + monthNames: ['Janeiro','Fevereiro','Março','Abril','Maio','Junho', + 'Julho','Agosto','Setembro','Outubro','Novembro','Dezembro'], + monthNamesShort: ['Jan','Fev','Mar','Abr','Mai','Jun', + 'Jul','Ago','Set','Out','Nov','Dez'], + dayNames: ['Domingo','Segunda-feira','Terça-feira','Quarta-feira','Quinta-feira','Sexta-feira','Sábado'], + dayNamesShort: ['Dom','Seg','Ter','Qua','Qui','Sex','Sáb'], + dayNamesMin: ['D','S','T','Q','Q','S','S'], + dateFormat: 'dd/mm/yyyy', + firstDay: 0, + renderer: $.datepick.defaultRenderer, + prevText: '<Anterior', + prevStatus: 'Mês anterior', + prevJumpText: '<<', + prevJumpStatus: 'Ano anterior', + nextText: 'Próximo>', + nextStatus: 'Próximo mês', + nextJumpText: '>>', + nextJumpStatus: 'Próximo ano', + currentText: 'Atual', + currentStatus: 'Mês atual', + todayText: 'Hoje', + todayStatus: 'Hoje', + clearText: 'Limpar', + clearStatus: 'Limpar data', + closeText: 'Fechar', + closeStatus: 'Fechar o calendário', + yearStatus: 'Selecionar ano', + monthStatus: 'Selecionar mês', + weekText: 's', + weekStatus: 'Semana do ano', + dayStatus: 'DD, d \'de\' M \'de\' yyyy', + defaultStatus: 'Selecione um dia', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.pt); +})(jQuery); \ No newline at end of file diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-rm.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-rm.js new file mode 100755 index 000000000..e17a2c15e --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-rm.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Romansh localisation for jQuery Datepicker. + Yvonne Gienal (yvonne.gienal@educa.ch). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.rm = { + monthNames: ['Schaner','Favrer','Mars','Avrigl','Matg','Zercladur', + 'Fanadur','Avust','Settember','October','November','December'], + monthNamesShort: ['Scha','Fev','Mar','Avr','Matg','Zer', + 'Fan','Avu','Sett','Oct','Nov','Dec'], + dayNames: ['Dumengia','Glindesdi','Mardi','Mesemna','Gievgia','Venderdi','Sonda'], + dayNamesShort: ['Dum','Gli','Mar','Mes','Gie','Ven','Som'], + dayNamesMin: ['Du','Gl','Ma','Me','Gi','Ve','So'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<Suandant', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Precedent>', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Actual', + currentStatus: '', + todayText: 'Actual', + todayStatus: '', + clearText: 'X', + clearStatus: '', + closeText: 'Serrar', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'emna', + weekStatus: '', + dayStatus: 'DD d MM', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.rm); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-ro.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-ro.js new file mode 100755 index 000000000..a30b77f29 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-ro.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Romanian localisation for jQuery Datepicker. + Written by Edmond L. (ll_edmond@walla.com) and Ionut G. Stan (ionut.g.stan@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.ro = { + monthNames: ['Ianuarie','Februarie','Martie','Aprilie','Mai','Iunie', + 'Iulie','August','Septembrie','Octombrie','Noiembrie','Decembrie'], + monthNamesShort: ['Ian','Feb','Mar','Apr','Mai','Iun', + 'Iul','Aug','Sep','Oct','Noi','Dec'], + dayNames: ['Duminică','Luni','Marti','Miercuri','Joi','Vineri','Sâmbătă'], + dayNamesShort: ['Dum','Lun','Mar','Mie','Joi','Vin','Sâm'], + dayNamesMin: ['Du','Lu','Ma','Mi','Jo','Vi','Sâ'], + dateFormat: 'dd.mm.yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '«Precedentă', + prevStatus: 'Arată luna precedenta', + prevJumpText: '««', + prevJumpStatus: '', + nextText: 'Urmatoare»', + nextStatus: 'Arată luna urmatoare', + nextJumpText: '»»', + nextJumpStatus: '', + currentText: 'Azi', + currentStatus: 'Arată luna curenta', + todayText: 'Azi', + todayStatus: 'Arată luna curenta', + clearText: 'Curat', + clearStatus: 'Sterge data curenta', + closeText: 'ÃŽnchide', + closeStatus: 'ÃŽnchide fara schimbare', + yearStatus: 'Arată un an diferit', + monthStatus: 'Arată o luna diferita', + weekText: 'Săpt', + weekStatus: 'Săptamana anului', + dayStatus: 'Selectează D, M d', + defaultStatus: 'Selectează o data', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.ro); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-ru.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-ru.js new file mode 100755 index 000000000..74e5a9d6e --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-ru.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Russian localisation for jQuery Datepicker. + Written by Andrew Stromnov (stromnov@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.ru = { + monthNames: ['Январь','Февраль','Март','Ðпрель','Май','Июнь', + 'Июль','ÐвгуÑÑ‚','СентÑбрь','ОктÑбрь','ÐоÑбрь','Декабрь'], + monthNamesShort: ['Янв','Фев','Мар','Ðпр','Май','Июн', + 'Июл','Ðвг','Сен','Окт','ÐоÑ','Дек'], + dayNames: ['воÑкреÑенье','понедельник','вторник','Ñреда','четверг','пÑтница','Ñуббота'], + dayNamesShort: ['вÑк','пнд','втр','Ñрд','чтв','птн','Ñбт'], + dayNamesMin: ['Ð’Ñ','Пн','Ð’Ñ‚','Ср','Чт','Пт','Сб'], + dateFormat: 'dd.mm.yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<Пред', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'След>', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'СегоднÑ', + currentStatus: '', + todayText: 'СегоднÑ', + todayStatus: '', + clearText: 'ОчиÑтить', + clearStatus: '', + closeText: 'Закрыть', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Ðе', + weekStatus: '', + dayStatus: 'D, M d', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.ru); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-sk.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-sk.js new file mode 100755 index 000000000..43af9b7ab --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-sk.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Slovak localisation for jQuery Datepicker. + Written by Vojtech Rinik (vojto@hmm.sk). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.sk = { + monthNames: ['Január','Február','Marec','Apríl','Máj','Jún', + 'Júl','August','September','Október','November','December'], + monthNamesShort: ['Jan','Feb','Mar','Apr','Máj','Jún', + 'Júl','Aug','Sep','Okt','Nov','Dec'], + dayNames: ['Nedel\'a','Pondelok','Utorok','Streda','Å tvrtok','Piatok','Sobota'], + dayNamesShort: ['Ned','Pon','Uto','Str','Å tv','Pia','Sob'], + dayNamesMin: ['Ne','Po','Ut','St','Å t','Pia','So'], + dateFormat: 'dd.mm.yyyy', + firstDay: 0, + renderer: $.datepick.defaultRenderer, + prevText: '<Predchádzajúci', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Nasledujúci>', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Dnes', + currentStatus: '', + todayText: 'Dnes', + todayStatus: '', + clearText: 'ZmazaÅ¥', + clearStatus: '', + closeText: 'ZavrieÅ¥', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Ty', + weekStatus: '', + dayStatus: 'D, M d', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.sk); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-sl.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-sl.js new file mode 100755 index 000000000..4043c6dd4 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-sl.js @@ -0,0 +1,43 @@ +/* http://keith-wood.name/datepick.html + Slovenian localisation for jQuery Datepicker. + Written by Jaka Jancar (jaka@kubje.org). */ +/* c = č, s = š z = ž C = Č S = Š Z = Ž */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.sl = { + monthNames: ['Januar','Februar','Marec','April','Maj','Junij', + 'Julij','Avgust','September','Oktober','November','December'], + monthNamesShort: ['Jan','Feb','Mar','Apr','Maj','Jun', + 'Jul','Avg','Sep','Okt','Nov','Dec'], + dayNames: ['Nedelja','Ponedeljek','Torek','Sreda','Četrtek','Petek','Sobota'], + dayNamesShort: ['Ned','Pon','Tor','Sre','Čet','Pet','Sob'], + dayNamesMin: ['Ne','Po','To','Sr','Če','Pe','So'], + dateFormat: 'dd.mm.yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<Prejšnji', + prevStatus: 'Prikaži prejšnji mesec', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Naslednji>', + nextStatus: 'Prikaži naslednji mesec', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Trenutni', + currentStatus: 'Prikaži trenutni mesec', + todayText: 'Trenutni', + todayStatus: 'Prikaži trenutni mesec', + clearText: 'Izbriši', + clearStatus: 'Izbriši trenutni datum', + closeText: 'Zapri', + closeStatus: 'Zapri brez spreminjanja', + yearStatus: 'Prikaži drugo leto', + monthStatus: 'Prikaži drug mesec', + weekText: 'Teden', + weekStatus: 'Teden v letu', + dayStatus: 'Izberi DD, d MM yy', + defaultStatus: 'Izbira datuma', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.sl); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-sq.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-sq.js new file mode 100755 index 000000000..59f7f18a8 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-sq.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Albanian localisation for jQuery Datepicker. + Written by Flakron Bytyqi (flakron@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.sq = { + monthNames: ['Janar','Shkurt','Mars','Prill','Maj','Qershor', + 'Korrik','Gusht','Shtator','Tetor','Nëntor','Dhjetor'], + monthNamesShort: ['Jan','Shk','Mar','Pri','Maj','Qer', + 'Kor','Gus','Sht','Tet','Nën','Dhj'], + dayNames: ['E Diel','E Hënë','E Martë','E Mërkurë','E Enjte','E Premte','E Shtune'], + dayNamesShort: ['Di','Hë','Ma','Më','En','Pr','Sh'], + dayNamesMin: ['Di','Hë','Ma','Më','En','Pr','Sh'], + dateFormat: 'dd.mm.yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<mbrapa', + prevStatus: 'trego muajin e fundit', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Përpara>', + nextStatus: 'trego muajin tjetër', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'sot', + currentStatus: '', + todayText: 'sot', + todayStatus: '', + clearText: 'fshije', + clearStatus: 'fshije datën aktuale', + closeText: 'mbylle', + closeStatus: 'mbylle pa ndryshime', + yearStatus: 'trego tjetër vit', + monthStatus: 'trego muajin tjetër', + weekText: 'Ja', + weekStatus: 'Java e muajit', + dayStatus: '\'Zgjedh\' D, M d', + defaultStatus: 'Zgjedhe një datë', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.sq); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-sr-SR.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-sr-SR.js new file mode 100755 index 000000000..7b943c037 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-sr-SR.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Serbian localisation for jQuery Datepicker. + Written by Dejan Dimić. */ +(function($){ + 'use strict'; + $.datepick.regionalOptions['sr-SR'] = { + monthNames: ['Januar','Februar','Mart','April','Maj','Jun', + 'Jul','Avgust','Septembar','Oktobar','Novembar','Decembar'], + monthNamesShort: ['Jan','Feb','Mar','Apr','Maj','Jun', + 'Jul','Avg','Sep','Okt','Nov','Dec'], + dayNames: ['Nedelja','Ponedeljak','Utorak','Sreda','ÄŒetvrtak','Petak','Subota'], + dayNamesShort: ['Ned','Pon','Uto','Sre','ÄŒet','Pet','Sub'], + dayNamesMin: ['Ne','Po','Ut','Sr','ÄŒe','Pe','Su'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<', + prevStatus: 'Prikaži prethodni mesec', + prevJumpText: '<<', + prevJumpStatus: 'Prikaži prethodnu godinu', + nextText: '>', + nextStatus: 'Prikaži sledeći mesec', + nextJumpText: '>>', + nextJumpStatus: 'Prikaži sledeću godinu', + currentText: 'Danas', + currentStatus: 'Tekući mesec', + todayText: 'Danas', + todayStatus: 'Tekući mesec', + clearText: 'ObriÅ¡i', + clearStatus: 'ObriÅ¡i trenutni datum', + closeText: 'Zatvori', + closeStatus: 'Zatvori kalendar', + yearStatus: 'Prikaži godine', + monthStatus: 'Prikaži mesece', + weekText: 'Sed', + weekStatus: 'Sedmica', + dayStatus: '\'Datum\' D, M d', + defaultStatus: 'Odaberi datum', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions['sr-SR']); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-sr.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-sr.js new file mode 100755 index 000000000..d645a3fef --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-sr.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Serbian localisation for jQuery Datepicker. + Written by Dejan Dimić. */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.sr = { + monthNames: ['Јануар','Фебруар','Март','Ðприл','Мај','Јун', + 'Јул','ÐвгуÑÑ‚','Септембар','Октобар','Ðовембар','Децембар'], + monthNamesShort: ['Јан','Феб','Мар','Ðпр','Мај','Јун', + 'Јул','Ðвг','Сеп','Окт','Ðов','Дец'], + dayNames: ['Ðедеља','Понедељак','Уторак','Среда','Четвртак','Петак','Субота'], + dayNamesShort: ['Ðед','Пон','Уто','Сре','Чет','Пет','Суб'], + dayNamesMin: ['Ðе','По','Ут','Ср','Че','Пе','Су'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<', + prevStatus: 'Прикажи претходни меÑец', + prevJumpText: '<<', + prevJumpStatus: 'Прикажи претходну годину', + nextText: '>', + nextStatus: 'Прикажи Ñледећи меÑец', + nextJumpText: '>>', + nextJumpStatus: 'Прикажи Ñледећу годину', + currentText: 'ДанаÑ', + currentStatus: 'Текући меÑец', + todayText: 'ДанаÑ', + todayStatus: 'Текући меÑец', + clearText: 'Обриши', + clearStatus: 'Обриши тренутни датум', + closeText: 'Затвори', + closeStatus: 'Затвори календар', + yearStatus: 'Прикажи године', + monthStatus: 'Прикажи меÑеце', + weekText: 'Сед', + weekStatus: 'Седмица', + dayStatus: '\'Датум\' DD d MM', + defaultStatus: 'Одабери датум', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.sr); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-sv.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-sv.js new file mode 100755 index 000000000..0a42291a1 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-sv.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Swedish localisation for jQuery Datepicker. + Written by Anders Ekdahl ( anders@nomadiz.se). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.sv = { + monthNames: ['Januari','Februari','Mars','April','Maj','Juni', + 'Juli','Augusti','September','Oktober','November','December'], + monthNamesShort: ['Jan','Feb','Mar','Apr','Maj','Jun', + 'Jul','Aug','Sep','Okt','Nov','Dec'], + dayNames: ['Söndag','MÃ¥ndag','Tisdag','Onsdag','Torsdag','Fredag','Lördag'], + dayNamesShort: ['Sön','MÃ¥n','Tis','Ons','Tor','Fre','Lör'], + dayNamesMin: ['Sö','MÃ¥','Ti','On','To','Fr','Lö'], + dateFormat: 'yyyy-mm-dd', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '«Förra', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Nästa»', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Idag', + currentStatus: '', + todayText: 'Idag', + todayStatus: '', + clearText: 'Rensa', + clearStatus: '', + closeText: 'Stäng', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Ve', + weekStatus: '', + dayStatus: 'D, M d', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.sv); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-ta.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-ta.js new file mode 100755 index 000000000..3d70b2ec4 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-ta.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Tamil localisation for jQuery Datepicker. + Written by S A Sureshkumar (saskumar@live.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.ta = { + monthNames: ['தை','மாசி','பஙà¯à®•à¯à®©à®¿','சிதà¯à®¤à®¿à®°à¯ˆ','வைகாசி','ஆனி', + 'ஆடி','ஆவணி','பà¯à®°à®Ÿà¯à®Ÿà®¾à®šà®¿','à®à®ªà¯à®ªà®šà®¿','காரà¯à®¤à¯à®¤à®¿à®•ை','மாரà¯à®•ழி'], + monthNamesShort: ['தை','மாசி','பஙà¯','சிதà¯','வைகா','ஆனி', + 'ஆடி','ஆவ','பà¯à®°','à®à®ªà¯','காரà¯','மாரà¯'], + dayNames: ['ஞாயிறà¯à®±à¯à®•à¯à®•ிழமை','திஙà¯à®•டà¯à®•ிழமை','செவà¯à®µà®¾à®¯à¯à®•à¯à®•ிழமை','பà¯à®¤à®©à¯à®•ிழமை','வியாழகà¯à®•ிழமை','வெளà¯à®³à®¿à®•à¯à®•ிழமை','சனிகà¯à®•ிழமை'], + dayNamesShort: ['ஞாயிறà¯','திஙà¯à®•ளà¯','செவà¯à®µà®¾à®¯à¯','பà¯à®¤à®©à¯','வியாழனà¯','வெளà¯à®³à®¿','சனி'], + dayNamesMin: ['ஞா','தி','செ','பà¯','வி','வெ','ச'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: 'à®®à¯à®©à¯à®©à¯ˆà®¯à®¤à¯', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'அடà¯à®¤à¯à®¤à®¤à¯', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'இனà¯à®±à¯', + currentStatus: '', + todayText: 'இனà¯à®±à¯', + todayStatus: '', + clearText: 'அழி', + clearStatus: '', + closeText: 'மூடà¯', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Wk', + weekStatus: '', + dayStatus: 'D, M d', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.ta); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-th.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-th.js new file mode 100755 index 000000000..be48fdbf2 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-th.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Thai localisation for jQuery Datepicker. + Written by pipo (pipo@sixhead.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.th = { + monthNames: ['มà¸à¸£à¸²à¸„ม','à¸à¸¸à¸¡à¸ à¸²à¸žà¸±à¸™à¸˜à¹Œ','มีนาคม','เมษายน','พฤษภาคม','มิถุนายน', + 'à¸à¸£à¸à¸Žà¸²à¸„ม','สิงหาคม','à¸à¸±à¸™à¸¢à¸²à¸¢à¸™','ตุลาคม','พฤศจิà¸à¸²à¸¢à¸™','ธันวาคม'], + monthNamesShort: ['ม.ค.','à¸.พ.','มี.ค.','เม.ย.','พ.ค.','มิ.ย.', + 'à¸.ค.','ส.ค.','à¸.ย.','ต.ค.','พ.ย.','ธ.ค.'], + dayNames: ['อาทิตย์','จันทร์','อังคาร','พุธ','พฤหัสบดี','ศุà¸à¸£à¹Œ','เสาร์'], + dayNamesShort: ['อา.','จ.','อ.','พ.','พฤ.','ศ.','ส.'], + dayNamesMin: ['อา.','จ.','อ.','พ.','พฤ.','ศ.','ส.'], + dateFormat: 'dd/mm/yyyy', + firstDay: 0, + renderer: $.datepick.defaultRenderer, + prevText: '« à¸¢à¹‰à¸­à¸™', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'ถัดไป »', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'วันนี้', + currentStatus: '', + todayText: 'วันนี้', + todayStatus: '', + clearText: 'ลบ', + clearStatus: '', + closeText: 'ปิด', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Wk', + weekStatus: '', + dayStatus: 'D, M d', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.th); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-tr.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-tr.js new file mode 100755 index 000000000..20476b8f1 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-tr.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Turkish localisation for jQuery Datepicker. + Written by Izzet Emre Erkan (kara@karalamalar.net). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.tr = { + monthNames: ['Ocak','Åžubat','Mart','Nisan','Mayıs','Haziran', + 'Temmuz','AÄŸustos','Eylül','Ekim','Kasım','Aralık'], + monthNamesShort: ['Oca','Åžub','Mar','Nis','May','Haz', + 'Tem','AÄŸu','Eyl','Eki','Kas','Ara'], + dayNames: ['Pazar','Pazartesi','Salı','ÇarÅŸamba','PerÅŸembe','Cuma','Cumartesi'], + dayNamesShort: ['Pz','Pt','Sa','Ça','Pe','Cu','Ct'], + dayNamesMin: ['Pz','Pt','Sa','Ça','Pe','Cu','Ct'], + dateFormat: 'dd.mm.yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<geri', + prevStatus: 'önceki ayı göster', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'ileri>', + nextStatus: 'sonraki ayı göster', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'bugün', + currentStatus: '', + todayText: 'bugün', + todayStatus: '', + clearText: 'temizle', + clearStatus: 'geçerli tarihi temizler', + closeText: 'kapat', + closeStatus: 'sadece göstergeyi kapat', + yearStatus: 'baÅŸka yıl', + monthStatus: 'baÅŸka ay', + weekText: 'Hf', + weekStatus: 'Ayın haftaları', + dayStatus: 'D, M d seçiniz', + defaultStatus: 'Bir tarih seçiniz', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.tr); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-tt.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-tt.js new file mode 100755 index 000000000..d4ffd76df --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-tt.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Tatar localisation for jQuery Datepicker. + Written by Irek Khaziev (khazirek@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.tt = { + monthNames: ['Гынвар','Февраль','Март','Ðпрель','Май','Июнь', + 'Июль','ÐвгуÑÑ‚','СентÑбрь','ОктÑбрь','ÐоÑбрь','Декабрь'], + monthNamesShort: ['Гыйн','Фев','Мар','Ðпр','Май','Июн', + 'Июл','Ðвг','Сен','Окт','ÐоÑ','Дек'], + dayNames: ['Ñкшәмбе','дүшәмбе','Ñишәмбе','чәршәмбе','пәнҗешәмбе','җомга','шимбә'], + dayNamesShort: ['Ñкш','дүш','Ñиш','чәр','пән','җом','шим'], + dayNamesMin: ['Як','Дү','Си','Чә','Пә','Җо','Ши'], + dateFormat: 'dd.mm.yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: 'Ðлдагы', + prevStatus: 'Ðлдагы айны күрÑәтү', + prevJumpText: '<<', + prevJumpStatus: 'Ðлдагы елны күрÑәтү', + nextText: 'КиләÑе', + nextStatus: 'КиләÑе айны күрÑәтү', + nextJumpText: '>>', + nextJumpStatus: 'КиләÑе елны күрÑәтү', + currentText: 'Хәзер', + currentStatus: 'Хәзерге айны күрÑәтү', + todayText: 'Бүген', + todayStatus: 'Бүгенге айны күрÑәтү', + clearText: 'ЧиÑтарту', + clearStatus: 'Барлык көннәрне чиÑтарту', + closeText: 'Ябарга', + closeStatus: 'Көн Ñайлауны Ñбарга', + yearStatus: 'Елны кертегез', + monthStatus: 'Ðйны кертегез', + weekText: 'Ðтна', + weekStatus: 'Елда атна Ñаны', + dayStatus: 'DD, M d', + defaultStatus: 'Көнне Ñайлагыз', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.tt); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-uk.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-uk.js new file mode 100755 index 000000000..209da007e --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-uk.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Ukrainian localisation for jQuery Datepicker. + Written by Maxim Drogobitskiy (maxdao@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.uk = { + monthNames: ['Січень','Лютий','Березень','Квітень','Травень','Червень', + 'Липень','Серпень','ВереÑень','Жовтень','ЛиÑтопад','Грудень'], + monthNamesShort: ['Січ','Лют','Бер','Кві','Тра','Чер', + 'Лип','Сер','Вер','Жов','ЛиÑ','Гру'], + dayNames: ['неділÑ','понеділок','вівторок','Ñереда','четвер','п\'ÑтницÑ','Ñубота'], + dayNamesShort: ['нед','пнд','вів','Ñрд','чтв','птн','Ñбт'], + dayNamesMin: ['Ðд','Пн','Ð’Ñ‚','Ср','Чт','Пт','Сб'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: '>', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Сьогодні', + currentStatus: '', + todayText: 'Сьогодні', + todayStatus: '', + clearText: 'ОчиÑтити', + clearStatus: '', + closeText: 'Закрити', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Ðе', + weekStatus: '', + dayStatus: 'D, M d', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.uk); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-ur.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-ur.js new file mode 100755 index 000000000..45fe44a69 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-ur.js @@ -0,0 +1,43 @@ +/* http://keith-wood.name/datepick.html + Urdu localisation for jQuery Datepicker. + Mansoor Munib -- mansoormunib@gmail.com + Thanks to Habib Ahmed, ObaidUllah Anwar. */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.ur = { + monthNames: ['جنوری','ÙØ±ÙˆØ±ÛŒ','مارچ','اپریل','مئی','جون', + 'جولائی','اگست','ستمبر','اکتوبر','نومبر','دسمبر'], + monthNamesShort: ['1','2','3','4','5','6', + '7','8','9','10','11','12'], + dayNames: ['اتوار','پير','منگل','بدھ','جمعرات','جمعÛ','ÛÙØªÛ'], + dayNamesShort: ['اتوار','پير','منگل','بدھ','جمعرات','جمعÛ','ÛÙØªÛ'], + dayNamesMin: ['اتوار','پير','منگل','بدھ','جمعرات','جمعÛ','ÛÙØªÛ'], + dateFormat: 'dd/mm/yyyy', + firstDay: 0, + renderer: $.datepick.defaultRenderer, + prevText: '<گذشتÛ', + prevStatus: 'ماه گذشتÛ', + prevJumpText: '<<', + prevJumpStatus: 'برس گذشتÛ', + nextText: 'آئندÛ>', + nextStatus: 'ماه آئندÛ', + nextJumpText: '>>', + nextJumpStatus: 'برس آئندÛ', + currentText: 'رواں', + currentStatus: 'ماه رواں', + todayText: 'آج', + todayStatus: 'آج', + clearText: 'حذ٠تاريخ', + clearStatus: 'کریں حذ٠تاریخ', + closeText: 'کریں بند', + closeStatus: 'کیلئے کرنے بند', + yearStatus: 'برس تبدیلی', + monthStatus: 'ماه تبدیلی', + weekText: 'ÛÙØªÛ', + weekStatus: 'ÛÙØªÛ', + dayStatus: 'انتخاب D, M d', + defaultStatus: 'کریں منتخب تاريخ', + isRTL: true + }; + $.datepick.setDefaults($.datepick.regionalOptions.ur); +})(jQuery); \ No newline at end of file diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-vi.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-vi.js new file mode 100755 index 000000000..7bc752bc2 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-vi.js @@ -0,0 +1,42 @@ +/* http://keith-wood.name/datepick.html + Vietnamese localisation for jQuery Datepicker. + Translated by Le Thanh Huy (lthanhhuy@cit.ctu.edu.vn). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.vi = { + monthNames: ['Tháng Má»™t', 'Tháng Hai', 'Tháng Ba', 'Tháng Tư', 'Tháng Năm', 'Tháng Sáu', + 'Tháng Bảy', 'Tháng Tám', 'Tháng Chín', 'Tháng Mưá»i', 'Tháng Mưá»i Má»™t', 'Tháng Mưá»i Hai'], + monthNamesShort: ['Tháng 1', 'Tháng 2', 'Tháng 3', 'Tháng 4', 'Tháng 5', 'Tháng 6', + 'Tháng 7', 'Tháng 8', 'Tháng 9', 'Tháng 10', 'Tháng 11', 'Tháng 12'], + dayNames: ['Chá»§ Nhật', 'Thứ Hai', 'Thứ Ba', 'Thứ Tư', 'Thứ Năm', 'Thứ Sáu', 'Thứ Bảy'], + dayNamesShort: ['CN', 'T2', 'T3', 'T4', 'T5', 'T6', 'T7'], + dayNamesMin: ['CN', 'T2', 'T3', 'T4', 'T5', 'T6', 'T7'], + dateFormat: 'dd/mm/yyyy', + firstDay: 0, + renderer: $.datepick.defaultRenderer, + prevText: '<Trước', + prevStatus: 'Tháng trước', + prevJumpText: '<<', + prevJumpStatus: 'Năm trước', + nextText: 'Tiếp>', + nextStatus: 'Tháng sau', + nextJumpText: '>>', + nextJumpStatus: 'Năm sau', + currentText: 'Hôm nay', + currentStatus: 'Tháng hiện tại', + todayText: 'Hôm nay', + todayStatus: 'Tháng hiện tại', + clearText: 'Xóa', + clearStatus: 'Xóa ngày hiện tại', + closeText: 'Äóng', + closeStatus: 'Äóng và không lưu lại thay đổi', + yearStatus: 'Năm khác', + monthStatus: 'Tháng khác', + weekText: 'Tu', + weekStatus: 'Tuần trong năm', + dayStatus: 'Äang chá»n DD, \'ngày\' d M', + defaultStatus: 'Chá»n ngày', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.vi); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-zh-CN.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-zh-CN.js new file mode 100755 index 000000000..15149ce58 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-zh-CN.js @@ -0,0 +1,44 @@ +/* http://keith-wood.name/datepick.html + Simplified Chinese localisation for jQuery Datepicker. + Written by Cloudream (cloudream@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions['zh-CN'] = { + monthNames: ['一月','二月','三月','四月','五月','六月', + '七月','八月','乿œˆ','åæœˆ','å一月','å二月'], + monthNamesShort: ['一','二','三','å››','五','å…­', + '七','å…«','ä¹','å','å一','å二'], + dayNames: ['星期日','星期一','星期二','星期三','星期四','星期五','星期六'], + dayNamesShort: ['周日','周一','周二','周三','周四','周五','周六'], + dayNamesMin: ['æ—¥','一','二','三','å››','五','å…­'], + dateFormat: 'yyyy-mm-dd', + firstDay: 1, + renderer: $.extend({}, $.datepick.defaultRenderer, + {month: $.datepick.defaultRenderer.month. + replace(/monthHeader/, 'monthHeader:MM yyyyå¹´')}), + prevText: '<上月', + prevStatus: '显示上月', + prevJumpText: '<<', + prevJumpStatus: '显示上一年', + nextText: '下月>', + nextStatus: '显示下月', + nextJumpText: '>>', + nextJumpStatus: '显示下一年', + currentText: '今天', + currentStatus: '显示本月', + todayText: '今天', + todayStatus: '显示本月', + clearText: '清除', + clearStatus: '清除已选日期', + closeText: '关闭', + closeStatus: '䏿”¹å˜å½“å‰é€‰æ‹©', + yearStatus: '选择年份', + monthStatus: '选择月份', + weekText: '周', + weekStatus: '年内周次', + dayStatus: '选择 m月 dæ—¥, DD', + defaultStatus: '请选择日期', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions['zh-CN']); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-zh-HK.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-zh-HK.js new file mode 100755 index 000000000..8f4635efb --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-zh-HK.js @@ -0,0 +1,44 @@ +/* http://keith-wood.name/datepick.html + Hong Kong Chinese localisation for jQuery Datepicker. + Written by SCCY (samuelcychan@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions['zh-HK'] = { + monthNames: ['一月','二月','三月','四月','五月','六月', + '七月','八月','乿œˆ','åæœˆ','å一月','å二月'], + monthNamesShort: ['一','二','三','å››','五','å…­', + '七','å…«','ä¹','å','å一','å二'], + dayNames: ['星期日','星期一','星期二','星期三','星期四','星期五','星期六'], + dayNamesShort: ['周日','周一','周二','周三','周四','周五','周六'], + dayNamesMin: ['æ—¥','一','二','三','å››','五','å…­'], + dateFormat: 'dd-mm-yyyy', + firstDay: 0, + renderer: $.extend({}, $.datepick.defaultRenderer, + {month: $.datepick.defaultRenderer.month. + replace(/monthHeader/, 'monthHeader:yyyyå¹´ MM')}), + prevText: '<上月', + prevStatus: '顯示上月', + prevJumpText: '<<', + prevJumpStatus: '顯示上一年', + nextText: '下月>', + nextStatus: '顯示下月', + nextJumpText: '>>', + nextJumpStatus: '顯示下一年', + currentText: '今天', + currentStatus: '顯示本月', + todayText: '今天', + todayStatus: '顯示本月', + clearText: '清除', + clearStatus: 'æ¸…é™¤å·²é¸æ—¥æœŸ', + closeText: '關閉', + closeStatus: '䏿”¹è®Šç›®å‰çš„鏿“‡', + yearStatus: '鏿“‡å¹´ä»½', + monthStatus: '鏿“‡æœˆä»½', + weekText: '周', + weekStatus: '年內周次', + dayStatus: '鏿“‡ m月 dæ—¥, DD', + defaultStatus: 'è«‹é¸æ“‡æ—¥æœŸ', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions['zh-HK']); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-zh-TW.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-zh-TW.js new file mode 100755 index 000000000..a05c3f1ad --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick-zh-TW.js @@ -0,0 +1,44 @@ +/* http://keith-wood.name/datepick.html + Traditional Chinese localisation for jQuery Datepicker. + Written by Ressol (ressol@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions['zh-TW'] = { + monthNames: ['一月','二月','三月','四月','五月','六月', + '七月','八月','乿œˆ','åæœˆ','å一月','å二月'], + monthNamesShort: ['一','二','三','å››','五','å…­', + '七','å…«','ä¹','å','å一','å二'], + dayNames: ['星期日','星期一','星期二','星期三','星期四','星期五','星期六'], + dayNamesShort: ['周日','周一','周二','周三','周四','周五','周六'], + dayNamesMin: ['æ—¥','一','二','三','å››','五','å…­'], + dateFormat: 'yyyy/mm/dd', + firstDay: 1, + renderer: $.extend({}, $.datepick.defaultRenderer, + {month: $.datepick.defaultRenderer.month. + replace(/monthHeader/, 'monthHeader:MM yyyyå¹´')}), + prevText: '<上月', + prevStatus: '顯示上月', + prevJumpText: '<<', + prevJumpStatus: '顯示上一年', + nextText: '下月>', + nextStatus: '顯示下月', + nextJumpText: '>>', + nextJumpStatus: '顯示下一年', + currentText: '今天', + currentStatus: '顯示本月', + todayText: '今天', + todayStatus: '顯示本月', + clearText: '清除', + clearStatus: 'æ¸…é™¤å·²é¸æ—¥æœŸ', + closeText: '關閉', + closeStatus: '䏿”¹è®Šç›®å‰çš„鏿“‡', + yearStatus: '鏿“‡å¹´ä»½', + monthStatus: '鏿“‡æœˆä»½', + weekText: '周', + weekStatus: '年內周次', + dayStatus: '鏿“‡ m月 dæ—¥, DD', + defaultStatus: 'è«‹é¸æ“‡æ—¥æœŸ', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions['zh-TW']); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick.ext.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick.ext.js new file mode 100755 index 000000000..ffcbee968 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick.ext.js @@ -0,0 +1,301 @@ +/*! http://keith-wood.name/datepick.html + Datepicker extensions for jQuery v5.1.1. + Written by Keith Wood (wood.keith{at}optusnet.com.au) August 2009. + Licensed under the MIT (http://keith-wood.name/licence.html) licence. + Please attribute the author if you use it. */ + +(function($) { // Hide scope, no $ conflict + 'use strict'; + + var themeRollerRenderer = { + picker: '' + + '
    ' + + '{link:prev}{link:today}{link:next}
    {months}' + + '{popup:start}
    {button:clear}{button:close}
    {popup:end}' + + '
    ', + monthRow: '
    {months}
    ', + month: '
    ' + + '
    {monthHeader:MM yyyy}
    ' + + '{weekHeader}{weeks}
    ', + weekHeader: '{days}', + dayHeader: '{day}', + week: '{days}', + day: '{day}', + monthSelector: '.ui-datepicker-group', + daySelector: 'td', + rtlClass: 'ui-datepicker-rtl', + multiClass: 'ui-datepicker-multi', + defaultClass: 'ui-state-default', + selectedClass: 'ui-state-active', + highlightedClass: 'ui-state-hover', + todayClass: 'ui-state-highlight', + otherMonthClass: 'ui-datepicker-other-month', + weekendClass: 'ui-datepicker-week-end', + commandClass: 'ui-datepicker-cmd', + commandButtonClass: 'ui-state-default ui-corner-all', + commandLinkClass: '', + disabledClass: 'ui-datepicker-disabled' + }; + + /** Extensions to the {@linkcode module:Datepick|Datepick} plugin. + @module Datepick-ext */ + $.extend($.datepick, { + + /** Template for generating a datepicker showing week of year. + Use it with the {@linkcode module:Datepick~regionalOptions|renderer} option. + @example renderer: $.datepick.weekOfYearRenderer */ + weekOfYearRenderer: $.extend({}, $.datepick.defaultRenderer, { + weekHeader: '' + + '{l10n:weekText}{days}', + week: '{weekOfYear}{days}' + }), + + /** ThemeRoller template for generating a datepicker. + Use it with the {@linkcode module:Datepick~regionalOptions|renderer} option. + @example renderer: $.datepick.themeRollerRenderer */ + themeRollerRenderer: themeRollerRenderer, + + /** ThemeRoller template for generating a datepicker showing week of year. + Use it with the {@linkcode module:Datepick~regionalOptions|renderer} option. + @example renderer: $.datepick.themeRollerWeekOfYearRenderer */ + themeRollerWeekOfYearRenderer: $.extend({}, themeRollerRenderer, { + weekHeader: '{l10n:weekText}{days}', + week: '{weekOfYear}{days}' + }), + + /** Don't allow weekends to be selected. + Use it with the {@linkcode module:Datepick~defaultOptions|onDate} option. + @param {Date} date The current date. + @return {object} Information about this date. + @example onDate: $.datepick.noWeekends */ + noWeekends: function(date) { + return {selectable: (date.getDay() || 7) < 6}; + }, + + /** Change the first day of the week by clicking on the day header. + Use it with the {@linkcode module:Datepick~defaultOptions|onShow} option. + @param {jQuery} picker The completed datepicker division. + @param {object} inst The current instance settings. + @example onShow: $.datepick.changeFirstDay */ + changeFirstDay: function(picker, inst) { // jshint unused:false + var target = $(this); + picker.find('th span').each(function() { + var parent = $(this).parent(); + if (parent.is('.datepick-week') || parent.is('.ui-state-hover')) { + return; + } + $('' + $(this).text() + ''). + click(function() { + var dow = parseInt(this.className.replace(/^.*datepick-dow-(\d+).*$/, '$1'), 10); + target.datepick('option', {firstDay: dow}); + return false; + }). + replaceAll(this); + }); + }, + + /** Callback when hovering over a date within the datepicker. + Use it with the {@linkcode module:Datepick-ext~hoverCallback|hoverCallback} function. + @callback DatepickOnHover + @global + @param {Date} date The currently hovered date. + @param {boolean} selectable true if this date can be selected. + @example $.datepick.hoverCallback(function(date, selectable) { + $('#hoverOutput').text( + (selectable ? $.datepick.formatDate(date) : null) || 'nothing'); +}) */ + + /** Add a callback when hovering over dates. + Use it with the {@linkcode module:Datepick~defaultOptions|onShow} option. + @param {DatepickOnHover} onHover The callback when hovering, it receives the current date and + a flag indicating selectability as parameters on entry, + and no parameters on exit, this refers to the target input or division. + @example onShow: $.datepick.hoverCallback(handleHover) */ + hoverCallback: function(onHover) { + return function(picker, inst) { + var target = this; + var renderer = inst.get('renderer'); + picker.find(renderer.daySelector + ' a, ' + renderer.daySelector + ' span'). + hover(function() { + onHover.apply(target, [$.datepick.retrieveDate(target, this), + this.nodeName.toLowerCase() === 'a']); + }, + function() { onHover.apply(target, []); }); + }; + }, + + /** Highlight the entire week when hovering over it. + Use it with the {@linkcode module:Datepick~defaultOptions|onShow} option. + @param {jQuery} picker The completed datepicker division. + @param {object} inst The current instance settings. + @example onShow: $.datepick.highlightWeek */ + highlightWeek: function(picker, inst) { // jshint unused:false + var target = this; + var renderer = inst.get('renderer'); + picker.find(renderer.daySelector + ' a, ' + renderer.daySelector + ' span'). + hover(function() { + $(this).parents('tr').find(renderer.daySelector + ' *'). + addClass(renderer.highlightedClass); + }, + function() { + $(this).parents('tr').find(renderer.daySelector + ' *'). + removeClass(renderer.highlightedClass); + }); + }, + + /** Show a status bar with messages. + Use it with the {@linkcode module:Datepick~defaultOptions|onShow} option. + @param {jQuery} picker The completed datepicker division. + @param {object} inst The current instance settings. + @example onShow: $.datepick.showStatus */ + showStatus: function(picker, inst) { + var renderer = inst.get('renderer'); + var isTR = (renderer.selectedClass === themeRollerRenderer.selectedClass); + var defaultStatus = inst.get('defaultStatus') || ' '; + var status = $('
    ' + + defaultStatus + '
    '). + insertAfter(picker.find('.datepick-month-row:last,.ui-datepicker-row-break:last')); + picker.find('*[title]').each(function() { + var title = $(this).attr('title'); + $(this).removeAttr('title').hover( + function() { status.text(title || defaultStatus); }, + function() { status.text(defaultStatus); }); + }); + }, + + /** Allow easier navigation by month/year. + Use it with the {@linkcode module:Datepick~defaultOptions|onShow} option. + @param {jQuery} picker The completed datepicker division. + @param {object} inst The current instance settings. + @example onShow: $.datepick.monthNavigation */ + monthNavigation: function(picker, inst) { + var target = $(this); + var renderer = inst.get('renderer'); + var isTR = (renderer.selectedClass === themeRollerRenderer.selectedClass); + var minDate = inst.curMinDate(); + var maxDate = inst.get('maxDate'); + var monthNames = inst.get('monthNames'); + var monthNamesShort = inst.get('monthNamesShort'); + var month = inst.drawDate.getMonth(); + var year = inst.drawDate.getFullYear(); + var inRange = false; + var html = ''; + html = $(html).insertAfter(picker.find('div.datepick-nav,div.ui-datepicker-header:first')); + html.find('a').click(function() { + var date = $.datepick.retrieveDate(target[0], this); + html.slideToggle(function() { + target.datepick('showMonth', date.getFullYear(), date.getMonth() + 1); + }); + return false; + }); + picker.find('div.datepick-month-header,div.ui-datepicker-month-header').click(function() { + html.slideToggle(); + }).css('cursor', 'pointer'); + }, + + /** Select an entire week when clicking on a week number. + Use it with the {@linkcode module:Datepick~defaultOptions|onShow} option. + Use in conjunction with {@linkcode module:Datepick-ext~weekOfYearRenderer|weekOfYearRenderer} or + {@linkcode module:Datepick-ext~themeRollerWeekOfYearRenderer|themeRollerWeekOfYearRenderer}. + @param {jQuery} picker The completed datepicker division. + @param {object} inst The current instance settings. + @example onShow: $.datepick.selectWeek */ + selectWeek: function(picker, inst) { + var target = $(this); + picker.find('td.datepick-week span,td.ui-state-hover span').each(function() { + $('' + + $(this).text() + ''). + click(function() { + var date = target.datepick('retrieveDate', this); + var dates = [date]; + for (var i = 1; i < 7; i++) { + dates.push(date = $.datepick.add($.datepick.newDate(date), 1, 'd')); + } + if (inst.get('rangeSelect')) { + dates.splice(1, dates.length - 2); + } + target.datepick('setDate', dates).datepick('hide'); + return false; + }). + replaceAll(this); + }); + }, + + /** Select an entire month when clicking on the week header. + Use it with the {@linkcode module:Datepick~defaultOptions|onShow} option. + Use in conjunction with {@linkcode module:Datepick-ext~weekOfYearRenderer|weekOfYearRenderer} or + {@linkcode module:Datepick-ext~themeRollerWeekOfYearRenderer|themeRollerWeekOfYearRenderer}. + @param {jQuery} picker The completed datepicker division. + @param {object} inst The current instance settings. + @example onShow: $.datepick.selectMonth */ + selectMonth: function(picker, inst) { + var target = $(this); + picker.find('th.datepick-week span,th.ui-state-hover span').each(function() { + $('' + + $(this).text() + ''). + click(function() { + var date = target.datepick('retrieveDate', $(this).parents('table'). + find('td:not(.datepick-week):not(.ui-state-hover) ' + + '*:not(.datepick-other-month):not(.ui-datepicker-other-month)')[0]); + var dates = [date]; + var dim = $.datepick.daysInMonth(date); + for (var i = 1; i < dim; i++) { + dates.push(date = $.datepick.add($.datepick.newDate(date), 1, 'd')); + } + if (inst.get('rangeSelect')) { + dates.splice(1, dates.length - 2); + } + target.datepick('setDate', dates).datepick('hide'); + return false; + }). + replaceAll(this); + }); + }, + + /** Select a month only instead of a single day. + Use it with the {@linkcode module:Datepick~defaultOptions|onShow} option. + @param {jQuery} picker The completed datepicker division. + @param {object} inst The current instance settings. + @example onShow: $.datepick.monthOnly */ + monthOnly: function(picker, inst) { // jshint unused:false + var target = $(this); + $('
    '). + insertAfter(picker.find('.datepick-month-row:last,.ui-datepicker-row-break:last')). + children().click(function() { + var monthYear = picker.find('.datepick-month-year:first').val().split('/'); + target.datepick('setDate', $.datepick.newDate( + parseInt(monthYear[1], 10), parseInt(monthYear[0], 10), 1)). + datepick('hide'); + }); + picker.find('.datepick-month-row table,.ui-datepicker-row-break table').remove(); + } + }); + +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick.ext.min.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick.ext.min.js new file mode 100755 index 000000000..fc199158a --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick.ext.min.js @@ -0,0 +1,7 @@ +/*! http://keith-wood.name/datepick.html + Datepicker extensions for jQuery v5.1.1. + Written by Keith Wood (wood.keith{at}optusnet.com.au) August 2009. + Licensed under the MIT (http://keith-wood.name/licence.html) licence. + Please attribute the author if you use it. */ +!function(a){"use strict";var b={picker:'
    {link:prev}{link:today}{link:next}
    {months}{popup:start}
    {button:clear}{button:close}
    {popup:end}
    ',monthRow:'
    {months}
    ',month:'
    {monthHeader:MM yyyy}
    {weekHeader}{weeks}
    ',weekHeader:"{days}",dayHeader:"{day}",week:"{days}",day:"{day}",monthSelector:".ui-datepicker-group",daySelector:"td",rtlClass:"ui-datepicker-rtl",multiClass:"ui-datepicker-multi",defaultClass:"ui-state-default",selectedClass:"ui-state-active",highlightedClass:"ui-state-hover",todayClass:"ui-state-highlight",otherMonthClass:"ui-datepicker-other-month",weekendClass:"ui-datepicker-week-end",commandClass:"ui-datepicker-cmd",commandButtonClass:"ui-state-default ui-corner-all",commandLinkClass:"",disabledClass:"ui-datepicker-disabled"};a.extend(a.datepick,{weekOfYearRenderer:a.extend({},a.datepick.defaultRenderer,{weekHeader:'{l10n:weekText}{days}',week:'{weekOfYear}{days}'}),themeRollerRenderer:b,themeRollerWeekOfYearRenderer:a.extend({},b,{weekHeader:'{l10n:weekText}{days}',week:'{weekOfYear}{days}'}),noWeekends:function(a){return{selectable:(a.getDay()||7)<6}},changeFirstDay:function(b,c){var d=a(this);b.find("th span").each(function(){var b=a(this).parent();b.is(".datepick-week")||b.is(".ui-state-hover")||a(''+a(this).text()+"").click(function(){var a=parseInt(this.className.replace(/^.*datepick-dow-(\d+).*$/,"$1"),10);return d.datepick("option",{firstDay:a}),!1}).replaceAll(this)})},hoverCallback:function(b){return function(c,d){var e=this,f=d.get("renderer");c.find(f.daySelector+" a, "+f.daySelector+" span").hover(function(){b.apply(e,[a.datepick.retrieveDate(e,this),"a"===this.nodeName.toLowerCase()])},function(){b.apply(e,[])})}},highlightWeek:function(b,c){var d=c.get("renderer");b.find(d.daySelector+" a, "+d.daySelector+" span").hover(function(){a(this).parents("tr").find(d.daySelector+" *").addClass(d.highlightedClass)},function(){a(this).parents("tr").find(d.daySelector+" *").removeClass(d.highlightedClass)})},showStatus:function(c,d){var e=d.get("renderer"),f=e.selectedClass===b.selectedClass,g=d.get("defaultStatus")||" ",h=a('
    '+g+"
    ").insertAfter(c.find(".datepick-month-row:last,.ui-datepicker-row-break:last"));c.find("*[title]").each(function(){var b=a(this).attr("title");a(this).removeAttr("title").hover(function(){h.text(b||g)},function(){h.text(g)})})},monthNavigation:function(c,d){for(var e=a(this),f=d.get("renderer"),g=f.selectedClass===b.selectedClass,h=d.curMinDate(),i=d.get("maxDate"),j=d.get("monthNames"),k=d.get("monthNamesShort"),l=d.drawDate.getMonth(),m=d.drawDate.getFullYear(),n=!1,o='",o=a(o).insertAfter(c.find("div.datepick-nav,div.ui-datepicker-header:first")),o.find("a").click(function(){var b=a.datepick.retrieveDate(e[0],this);return o.slideToggle(function(){e.datepick("showMonth",b.getFullYear(),b.getMonth()+1)}),!1}),c.find("div.datepick-month-header,div.ui-datepicker-month-header").click(function(){o.slideToggle()}).css("cursor","pointer")},selectWeek:function(b,c){var d=a(this);b.find("td.datepick-week span,td.ui-state-hover span").each(function(){a(''+a(this).text()+"").click(function(){for(var b=d.datepick("retrieveDate",this),e=[b],f=1;f<7;f++)e.push(b=a.datepick.add(a.datepick.newDate(b),1,"d"));return c.get("rangeSelect")&&e.splice(1,e.length-2),d.datepick("setDate",e).datepick("hide"),!1}).replaceAll(this)})},selectMonth:function(b,c){var d=a(this);b.find("th.datepick-week span,th.ui-state-hover span").each(function(){a(''+a(this).text()+"").click(function(){for(var b=d.datepick("retrieveDate",a(this).parents("table").find("td:not(.datepick-week):not(.ui-state-hover) *:not(.datepick-other-month):not(.ui-datepicker-other-month)")[0]),e=[b],f=a.datepick.daysInMonth(b),g=1;g').insertAfter(b.find(".datepick-month-row:last,.ui-datepicker-row-break:last")).children().click(function(){var c=b.find(".datepick-month-year:first").val().split("/");d.datepick("setDate",a.datepick.newDate(parseInt(c[1],10),parseInt(c[0],10),1)).datepick("hide")}),b.find(".datepick-month-row table,.ui-datepicker-row-break table").remove()}})}(jQuery); +//# sourceMappingURL=jquery.datepick.ext.min.map \ No newline at end of file diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick.ext.min.map b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick.ext.min.map new file mode 100755 index 000000000..51eced3e2 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick.ext.min.map @@ -0,0 +1 @@ +{"version":3,"sources":["..\\..\\src\\js\\jquery.datepick.ext.js"],"names":["$","themeRollerRenderer","picker","monthRow","month","weekHeader","dayHeader","week","day","monthSelector","daySelector","rtlClass","multiClass","defaultClass","selectedClass","highlightedClass","todayClass","otherMonthClass","weekendClass","commandClass","commandButtonClass","commandLinkClass","disabledClass","extend","datepick","weekOfYearRenderer","defaultRenderer","themeRollerWeekOfYearRenderer","noWeekends","date","selectable","getDay","changeFirstDay","inst","target","this","find","each","parent","is","className","text","click","dow","parseInt","replace","firstDay","replaceAll","hoverCallback","onHover","renderer","get","hover","apply","retrieveDate","nodeName","toLowerCase","highlightWeek","parents","addClass","removeClass","showStatus","isTR","defaultStatus","status","insertAfter","title","attr","removeAttr","monthNavigation","minDate","curMinDate","maxDate","monthNames","monthNamesShort","drawDate","getMonth","year","getFullYear","inRange","html","i","length","Date","getTime","slideToggle","css","selectWeek","dates","push","add","newDate","splice","selectMonth","dim","daysInMonth","monthOnly","children","monthYear","val","split","remove","jQuery"],"mappings":";;;;;CAMA,SAAUA,GACT,YAEA,IAAIC,IACHC,OAAQ,ufAORC,SAAU,sDACVC,MAAO,wPAGPC,WAAY,kBACZC,UAAW,iBACXC,KAAM,kBACNC,IAAK,iBACLC,cAAe,uBACfC,YAAa,KACbC,SAAU,oBACVC,WAAY,sBACZC,aAAc,mBACdC,cAAe,kBACfC,iBAAkB,iBAClBC,WAAY,qBACZC,gBAAiB,4BACjBC,aAAc,yBACdC,aAAc,oBACdC,mBAAoB,iCACpBC,iBAAkB,GAClBC,cAAe,yBAKhBtB,GAAEuB,OAAOvB,EAAEwB,UAKVC,mBAAoBzB,EAAEuB,UAAWvB,EAAEwB,SAASE,iBAC3CrB,WAAY,uGAEZE,KAAM,+DAMPN,oBAAqBA,EAKrB0B,8BAA+B3B,EAAEuB,UAAWtB,GAC3CI,WAAY,8EACZE,KAAM,gEAQPqB,WAAY,SAASC,GACpB,OAAQC,YAAaD,EAAKE,UAAY,GAAK,IAQ5CC,eAAgB,SAAS9B,EAAQ+B,GAChC,GAAIC,GAASlC,EAAEmC,KACfjC,GAAOkC,KAAK,WAAWC,KAAK,WAC3B,GAAIC,GAAStC,EAAEmC,MAAMG,QACjBA,GAAOC,GAAG,mBAAqBD,EAAOC,GAAG,oBAG7CvC,EAAE,sBAAwBmC,KAAKK,UAC7B,0CAA4CxC,EAAEmC,MAAMM,OAAS,QAC9DC,MAAM,WACL,GAAIC,GAAMC,SAAST,KAAKK,UAAUK,QAAQ,2BAA4B,MAAO,GAE7E,OADAX,GAAOV,SAAS,UAAWsB,SAAUH,KAC9B,IAERI,WAAWZ,SAqBda,cAAe,SAASC,GACvB,MAAO,UAAS/C,EAAQ+B,GACvB,GAAIC,GAASC,KACTe,EAAWjB,EAAKkB,IAAI,WACxBjD,GAAOkC,KAAKc,EAASxC,YAAc,OAASwC,EAASxC,YAAc,SAClE0C,MAAM,WACLH,EAAQI,MAAMnB,GAASlC,EAAEwB,SAAS8B,aAAapB,EAAQC,MACtB,MAAhCA,KAAKoB,SAASC,iBAEhB,WAAaP,EAAQI,MAAMnB,UAS9BuB,cAAe,SAASvD,EAAQ+B,GAC/B,GACIiB,GAAWjB,EAAKkB,IAAI,WACxBjD,GAAOkC,KAAKc,EAASxC,YAAc,OAASwC,EAASxC,YAAc,SAClE0C,MAAM,WACLpD,EAAEmC,MAAMuB,QAAQ,MAAMtB,KAAKc,EAASxC,YAAc,MACjDiD,SAAST,EAASnC,mBAEpB,WACCf,EAAEmC,MAAMuB,QAAQ,MAAMtB,KAAKc,EAASxC,YAAc,MACjDkD,YAAYV,EAASnC,qBASzB8C,WAAY,SAAS3D,EAAQ+B,GAC5B,GAAIiB,GAAWjB,EAAKkB,IAAI,YACpBW,EAAQZ,EAASpC,gBAAkBb,EAAoBa,cACvDiD,EAAgB9B,EAAKkB,IAAI,kBAAoB,SAC7Ca,EAAShE,EAAE,gBAAmB8D,EACjC,yEADwC,mBACoC,KAC5EC,EAAgB,UAChBE,YAAY/D,EAAOkC,KAAK,0DACzBlC,GAAOkC,KAAK,YAAYC,KAAK,WAC3B,GAAI6B,GAAQlE,EAAEmC,MAAMgC,KAAK,QACzBnE,GAAEmC,MAAMiC,WAAW,SAAShB,MAC3B,WAAaY,EAAOvB,KAAKyB,GAASH,IAClC,WAAaC,EAAOvB,KAAKsB,QAS7BM,gBAAiB,SAASnE,EAAQ+B,GAajC,IAAK,GAZDC,GAASlC,EAAEmC,MACXe,EAAWjB,EAAKkB,IAAI,YACpBW,EAAQZ,EAASpC,gBAAkBb,EAAoBa,cACvDwD,EAAUrC,EAAKsC,aACfC,EAAUvC,EAAKkB,IAAI,WACnBsB,EAAaxC,EAAKkB,IAAI,cACtBuB,EAAkBzC,EAAKkB,IAAI,mBAC3B/C,EAAQ6B,EAAK0C,SAASC,WACtBC,EAAO5C,EAAK0C,SAASG,cACrBC,GAAU,EACVC,EAAO,gBAAmBlB,EAA8B,0BAAvB,sBAAoD,4BAEhFmB,EAAI,EAAGA,EAAIR,EAAWS,OAAQD,IACtCF,IAAaT,GAAW,GAAIa,MAAKN,EAAMI,EAAI,EAAG,GAAGG,WAAad,EAAQc,cACnEZ,GAAW,GAAIW,MAAKN,EAAMI,EAAG,GAAGG,WAAaZ,EAAQY,WACxDJ,GAAQ,SACND,EAAU,wBAA0B,GAAII,MAAKN,EAAMI,EAAG,GAAGG,UAAY,IAAM,SAC5E,WAAaX,EAAWQ,GAAK,KAAOP,EAAgBO,IACnDF,EAAU,OAAS,WAAa,QAEnC,KAAKE,GAAI,EAAIA,GAAK,EAAGA,IACV,IAANA,IAGJF,IACIT,GAAW,GAAIa,MAAKN,EAAOI,EAAG,GAAQ,IAAIG,WAAad,EAAQc,cAChEZ,GAAW,GAAIW,MAAKN,EAAOI,EAAG,EAAO,GAAGG,WAAaZ,EAAQY,WAChEJ,GAAQ,SAAWD,EAAU,wBAC5B,GAAII,MAAKN,EAAOI,EAAG7E,EAAO,GAAGgF,UAAY,IAAM,SAC/C,YAAcP,EAAOI,GAAK,MAAQJ,EAAOI,IACxCF,EAAU,OAAS,WAAa,SAEnCC,IAAQ,SACRA,EAAOhF,EAAEgF,GAAMf,YAAY/D,EAAOkC,KAAK,oDACvC4C,EAAK5C,KAAK,KAAKM,MAAM,WACnB,GAAIb,GAAO7B,EAAEwB,SAAS8B,aAAapB,EAAO,GAAIC,KAI9C,OAHA6C,GAAKK,YAAY,WAChBnD,EAAOV,SAAS,YAAaK,EAAKiD,cAAejD,EAAK+C,WAAa,MAE7D,IAET1E,EAAOkC,KAAK,4DAA4DM,MAAM,WAC7EsC,EAAKK,gBACHC,IAAI,SAAU,YAUlBC,WAAY,SAASrF,EAAQ+B,GAC5B,GAAIC,GAASlC,EAAEmC,KACfjC,GAAOkC,KAAK,gDAAgDC,KAAK,WAChErC,EAAE,sBACAmC,KAAKK,UAAY,oCACjBxC,EAAEmC,MAAMM,OAAS,QAClBC,MAAM,WAGL,IAAK,GAFDb,GAAOK,EAAOV,SAAS,eAAgBW,MACvCqD,GAAS3D,GACJoD,EAAI,EAAGA,EAAI,EAAGA,IACtBO,EAAMC,KAAK5D,EAAO7B,EAAEwB,SAASkE,IAAI1F,EAAEwB,SAASmE,QAAQ9D,GAAO,EAAG,KAM/D,OAJII,GAAKkB,IAAI,gBACZqC,EAAMI,OAAO,EAAGJ,EAAMN,OAAS,GAEhChD,EAAOV,SAAS,UAAWgE,GAAOhE,SAAS,SACpC,IAERuB,WAAWZ,SAWd0D,YAAa,SAAS3F,EAAQ+B,GAC7B,GAAIC,GAASlC,EAAEmC,KACfjC,GAAOkC,KAAK,gDAAgDC,KAAK,WAChErC,EAAE,+CACAA,EAAEmC,MAAMM,OAAS,QAClBC,MAAM,WAML,IAAK,GALDb,GAAOK,EAAOV,SAAS,eAAgBxB,EAAEmC,MAAMuB,QAAQ,SAC1DtB,KAAK,4GAC4D,IAC9DoD,GAAS3D,GACTiE,EAAM9F,EAAEwB,SAASuE,YAAYlE,GACxBoD,EAAI,EAAGA,EAAIa,EAAKb,IACxBO,EAAMC,KAAK5D,EAAO7B,EAAEwB,SAASkE,IAAI1F,EAAEwB,SAASmE,QAAQ9D,GAAO,EAAG,KAM/D,OAJII,GAAKkB,IAAI,gBACZqC,EAAMI,OAAO,EAAGJ,EAAMN,OAAS,GAEhChD,EAAOV,SAAS,UAAWgE,GAAOhE,SAAS,SACpC,IAERuB,WAAWZ,SASd6D,UAAW,SAAS9F,EAAQ+B,GAC3B,GAAIC,GAASlC,EAAEmC,KACfnC,GAAE,gFACDiE,YAAY/D,EAAOkC,KAAK,2DACxB6D,WAAWvD,MAAM,WAChB,GAAIwD,GAAYhG,EAAOkC,KAAK,8BAA8B+D,MAAMC,MAAM,IACtElE,GAAOV,SAAS,UAAWxB,EAAEwB,SAASmE,QACrC/C,SAASsD,EAAU,GAAI,IAAKtD,SAASsD,EAAU,GAAI,IAAK,IACxD1E,SAAS,UAEZtB,EAAOkC,KAAK,4DAA4DiE,aAIxEC","file":"jquery.datepick.ext.min.js"} \ No newline at end of file diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick.js new file mode 100755 index 000000000..933d92b6c --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick.js @@ -0,0 +1,2406 @@ +/*! http://keith-wood.name/datepick.html + Date picker for jQuery v5.1.1. + Written by Keith Wood (wood.keith{at}optusnet.com.au) February 2010. + Licensed under the MIT (http://keith-wood.name/licence.html) licence. + Please attribute the author if you use it. */ + +(function($) { // Hide scope, no $ conflict + 'use strict'; + + var pluginName = 'datepick'; + + + /** Create the datepicker plugin. +

    Sets an input field to popup a calendar for date entry, + or a div or span to show an inline calendar.

    +

    Expects HTML like:

    +
    <input type="text">
    +

    or

    +
    <div></div>
    +

    Provide inline configuration like:

    +
    <input type="text" data-datepick="name: 'value',..."/>
    + @module Datepick + @augments JQPlugin + @example $(selector).datepick() +$(selector).datepick({minDate: 0, maxDate: '+1m +1w'}) */ + $.JQPlugin.createPlugin({ + + /** The name of the plugin. + @default 'datepick' */ + name: pluginName, + + /** Default template for generating a datepicker. + Insert anywhere: '{l10n:name}' to insert localised value for name, + '{link:name}' to insert a link trigger for command name, + '{button:name}' to insert a button trigger for command name, + '{popup:start}...{popup:end}' to mark a section for inclusion in a popup datepicker only, + '{inline:start}...{inline:end}' to mark a section for inclusion in an inline datepicker only. + @property {string} picker Overall structure: '{months}' to insert calendar months. + @property {string} monthRow One row of months: '{months}' to insert calendar months. + @property {string} month A single month: '{monthHeader:dateFormat}' to insert the month header - + dateFormat is optional and defaults to 'MM yyyy', + '{weekHeader}' to insert a week header, '{weeks}' to insert the month's weeks. + @property {string} weekHeader A week header: '{days}' to insert individual day names. + @property {string} dayHeader Individual day header: '{day}' to insert day name. + @property {string} week One week of the month: '{days}' to insert the week's days, + '{weekOfYear}' to insert week of year. + @property {string} day An individual day: '{day}' to insert day value. + @property {string} monthSelector jQuery selector, relative to picker, for a single month. + @property {string} daySelector jQuery selector, relative to picker, for individual days. + @property {string} rtlClass Class for right-to-left (RTL) languages. + @property {string} multiClass Class for multi-month datepickers. + @property {string} defaultClass Class for selectable dates. + @property {string} selectedClass Class for currently selected dates. + @property {string} highlightedClass Class for highlighted dates. + @property {string} todayClass Class for today. + @property {string} otherMonthClass Class for days from other months. + @property {string} weekendClass Class for days on weekends. + @property {string} commandClass Class prefix for commands. + @property {string} commandButtonClass Extra class(es) for commands that are buttons. + @property {string} commandLinkClass Extra class(es) for commands that are links. + @property {string} disabledClass Class for disabled commands. */ + defaultRenderer: { + picker: '
    ' + + '
    {link:prev}{link:today}{link:next}
    {months}' + + '{popup:start}
    {link:clear}{link:close}
    {popup:end}' + + '
    ', + monthRow: '
    {months}
    ', + month: '
    {monthHeader}
    ' + + '{weekHeader}{weeks}
    ', + weekHeader: '{days}', + dayHeader: '{day}', + week: '{days}', + day: '{day}', + monthSelector: '.datepick-month', + daySelector: 'td', + rtlClass: 'datepick-rtl', + multiClass: 'datepick-multi', + defaultClass: '', + selectedClass: 'datepick-selected', + highlightedClass: 'datepick-highlight', + todayClass: 'datepick-today', + otherMonthClass: 'datepick-other-month', + weekendClass: 'datepick-weekend', + commandClass: 'datepick-cmd', + commandButtonClass: '', + commandLinkClass: '', + disabledClass: 'datepick-disabled' + }, + + /** Command actions that may be added to a layout by name. +
      +
    • prev - Show the previous month (based on monthsToStep option) - PageUp
    • +
    • prevJump - Show the previous year (based on monthsToJump option) - Ctrl+PageUp
    • +
    • next - Show the next month (based on monthsToStep option) - PageDown
    • +
    • nextJump - Show the next year (based on monthsToJump option) - Ctrl+PageDown
    • +
    • current - Show the currently selected month or today's if none selected - Ctrl+Home
    • +
    • today - Show today's month - Ctrl+Home
    • +
    • clear - Erase the date and close the datepicker popup - Ctrl+End
    • +
    • close - Close the datepicker popup - Esc
    • +
    • prevWeek - Move the cursor to the previous week - Ctrl+Up
    • +
    • prevDay - Move the cursor to the previous day - Ctrl+Left
    • +
    • nextDay - Move the cursor to the next day - Ctrl+Right
    • +
    • nextWeek - Move the cursor to the next week - Ctrl+Down
    • +
    + The command name is the key name and is used to add the command to a layout + with '{button:name}' or '{link:name}'. Each has the following attributes: + @property {string} text The field in the regional settings for the displayed text. + @property {string} status The field in the regional settings for the status text. + @property {object} keystroke The keystroke to trigger the action. + @property {number} keystroke.keyCode The code for the keystroke. + @property {boolean} keystroke.ctrlKey true if Ctrl is required, + @property {boolean} keystroke.altKey true if Alt is required, + @property {boolean} keystroke.shiftKey true if Shift is required. + @property {DatepickCommandEnabled} enabled The function that indicates the command is enabled. + @property {DatepickCommandDate} date The function to get the date associated with this action. + @property {DatepickCommandAction} action The function that implements the action. */ + commands: { + prev: {text: 'prevText', status: 'prevStatus', // Previous month + keystroke: {keyCode: 33}, // Page up + enabled: function(inst) { + var minDate = inst.curMinDate(); + return (!minDate || plugin.add(plugin.day( + plugin._applyMonthsOffset(plugin.add(plugin.newDate(inst.drawDate), + 1 - inst.options.monthsToStep, 'm'), inst), 1), -1, 'd'). + getTime() >= minDate.getTime()); + }, + date: function(inst) { + return plugin.day(plugin._applyMonthsOffset(plugin.add( + plugin.newDate(inst.drawDate), -inst.options.monthsToStep, 'm'), inst), 1); + }, + action: function(inst) { + plugin.changeMonth(this, -inst.options.monthsToStep); + } + }, + prevJump: {text: 'prevJumpText', status: 'prevJumpStatus', // Previous year + keystroke: {keyCode: 33, ctrlKey: true}, // Ctrl + Page up + enabled: function(inst) { + var minDate = inst.curMinDate(); + return (!minDate || plugin.add(plugin.day( + plugin._applyMonthsOffset(plugin.add(plugin.newDate(inst.drawDate), + 1 - inst.options.monthsToJump, 'm'), inst), 1), -1, 'd'). + getTime() >= minDate.getTime()); + }, + date: function(inst) { + return plugin.day(plugin._applyMonthsOffset(plugin.add( + plugin.newDate(inst.drawDate), -inst.options.monthsToJump, 'm'), inst), 1); + }, + action: function(inst) { + plugin.changeMonth(this, -inst.options.monthsToJump); + } + }, + next: {text: 'nextText', status: 'nextStatus', // Next month + keystroke: {keyCode: 34}, // Page down + enabled: function(inst) { + var maxDate = inst.get('maxDate'); + return (!maxDate || plugin.day(plugin._applyMonthsOffset(plugin.add( + plugin.newDate(inst.drawDate), inst.options.monthsToStep, 'm'), inst), 1). + getTime() <= maxDate.getTime()); + }, + date: function(inst) { + return plugin.day(plugin._applyMonthsOffset(plugin.add( + plugin.newDate(inst.drawDate), inst.options.monthsToStep, 'm'), inst), 1); + }, + action: function(inst) { + plugin.changeMonth(this, inst.options.monthsToStep); + } + }, + nextJump: {text: 'nextJumpText', status: 'nextJumpStatus', // Next year + keystroke: {keyCode: 34, ctrlKey: true}, // Ctrl + Page down + enabled: function(inst) { + var maxDate = inst.get('maxDate'); + return (!maxDate || plugin.day(plugin._applyMonthsOffset(plugin.add( + plugin.newDate(inst.drawDate), inst.options.monthsToJump, 'm'), inst), 1). + getTime() <= maxDate.getTime()); + }, + date: function(inst) { + return plugin.day(plugin._applyMonthsOffset(plugin.add( + plugin.newDate(inst.drawDate), inst.options.monthsToJump, 'm'), inst), 1); + }, + action: function(inst) { + plugin.changeMonth(this, inst.options.monthsToJump); + } + }, + current: {text: 'currentText', status: 'currentStatus', // Current month + keystroke: {keyCode: 36, ctrlKey: true}, // Ctrl + Home + enabled: function(inst) { + var minDate = inst.curMinDate(); + var maxDate = inst.get('maxDate'); + var curDate = inst.selectedDates[0] || plugin.today(); + return (!minDate || curDate.getTime() >= minDate.getTime()) && + (!maxDate || curDate.getTime() <= maxDate.getTime()); + }, + date: function(inst) { + return inst.selectedDates[0] || plugin.today(); + }, + action: function(inst) { + var curDate = inst.selectedDates[0] || plugin.today(); + plugin.showMonth(this, curDate.getFullYear(), curDate.getMonth() + 1); + } + }, + today: {text: 'todayText', status: 'todayStatus', // Today's month + keystroke: {keyCode: 36, ctrlKey: true}, // Ctrl + Home + enabled: function(inst) { + var minDate = inst.curMinDate(); + var maxDate = inst.get('maxDate'); + return (!minDate || plugin.today().getTime() >= minDate.getTime()) && + (!maxDate || plugin.today().getTime() <= maxDate.getTime()); + }, + date: function() { return plugin.today(); }, + action: function() { plugin.showMonth(this); } + }, + clear: {text: 'clearText', status: 'clearStatus', // Clear the datepicker + keystroke: {keyCode: 35, ctrlKey: true}, // Ctrl + End + enabled: function() { return true; }, + date: function() { return null; }, + action: function() { plugin.clear(this); } + }, + close: {text: 'closeText', status: 'closeStatus', // Close the datepicker + keystroke: {keyCode: 27}, // Escape + enabled: function() { return true; }, + date: function() { return null; }, + action: function() { plugin.hide(this); } + }, + prevWeek: {text: 'prevWeekText', status: 'prevWeekStatus', // Previous week + keystroke: {keyCode: 38, ctrlKey: true}, // Ctrl + Up + enabled: function(inst) { + var minDate = inst.curMinDate(); + return (!minDate || plugin.add(plugin.newDate(inst.drawDate), -7, 'd'). + getTime() >= minDate.getTime()); + }, + date: function(inst) { return plugin.add(plugin.newDate(inst.drawDate), -7, 'd'); }, + action: function() { plugin.changeDay(this, -7); } + }, + prevDay: {text: 'prevDayText', status: 'prevDayStatus', // Previous day + keystroke: {keyCode: 37, ctrlKey: true}, // Ctrl + Left + enabled: function(inst) { + var minDate = inst.curMinDate(); + return (!minDate || plugin.add(plugin.newDate(inst.drawDate), -1, 'd'). + getTime() >= minDate.getTime()); + }, + date: function(inst) { return plugin.add(plugin.newDate(inst.drawDate), -1, 'd'); }, + action: function() { plugin.changeDay(this, -1); } + }, + nextDay: {text: 'nextDayText', status: 'nextDayStatus', // Next day + keystroke: {keyCode: 39, ctrlKey: true}, // Ctrl + Right + enabled: function(inst) { + var maxDate = inst.get('maxDate'); + return (!maxDate || plugin.add(plugin.newDate(inst.drawDate), 1, 'd'). + getTime() <= maxDate.getTime()); + }, + date: function(inst) { return plugin.add(plugin.newDate(inst.drawDate), 1, 'd'); }, + action: function() { plugin.changeDay(this, 1); } + }, + nextWeek: {text: 'nextWeekText', status: 'nextWeekStatus', // Next week + keystroke: {keyCode: 40, ctrlKey: true}, // Ctrl + Down + enabled: function(inst) { + var maxDate = inst.get('maxDate'); + return (!maxDate || plugin.add(plugin.newDate(inst.drawDate), 7, 'd'). + getTime() <= maxDate.getTime()); + }, + date: function(inst) { return plugin.add(plugin.newDate(inst.drawDate), 7, 'd'); }, + action: function() { plugin.changeDay(this, 7); } + } + }, + + /** Determine whether a {@linkcode module:Datepick~commands|command} is enabled. + @callback DatepickCommandEnabled + @global + @param {object} inst The current instance settings. + @return {boolean} true if this command is enabled, false if not. + @example enabled: function(inst) { + return !!inst.curMinDate(); +} */ + + /** Calculate the representative date for a {@linkcode module:Datepick~commands|command}. + @callback DatepickCommandDate + @global + @param {object} inst The current instance settings. + @return {Date} A date appropriate for this command. + @example date: function(inst) { + return inst.curMinDate(); +} */ + + /** Perform the action for a {@linkcode module:Datepick~commands|command}. + @callback DatepickCommandAction + @global + @param {object} inst The current instance settings. + @example action: function(inst) { + $.datepick.setDate(inst.elem, inst.curMinDate()); +} */ + + /** Calculate the week of the year for a date. + Use it with the {@linkcode module:Datepick~defaultOptions|calculateWeek} option. + @callback DatepickCalculateWeek + @global + @param {Date} date The date to evaluate. + @return {number} The week of the year. + @example calculateWeek: function(date) { + return Math.floor(($.datepick.dayOfYear(date) - 1) / 7) + 1; +} */ + + /** Determine where the first month shows in a multi-month calendar. + Use it with the {@linkcode module:Datepick~defaultOptions|monthsOffset} option. + @callback DatepickMonthsOffset + @global + @param {Date} date The first date to be shown. + @return {number} The offset within the calendar for the first month - first position is 0. + @example monthsToShow: 3, +monthsToStep: 3, +monthsOffset: function(date) { // Always start on the quarter + return date.getMonth() % 3; +} */ + + /** Provide information about an individual date shown in the calendar. + Use it with the {@linkcode module:Datepick~defaultOptions|onDate} option. + @callback DatepickOnDate + @global + @param {Date} date The date to evaluate. + @return {object} Information about that date, with the properties above. + @property selectable {boolean} true if this date can be selected. + @property dateClass {string} Class(es) to be applied to the date. + @property content {string} The date cell content. + @property tooltip {string} A popup tooltip for the date. + @example onDate: function(date) { + return {selectable: date.getDay() > 0 && date.getDay() < 5, + dateClass: date.getDay() == 4 ? 'last-day' : ''}; +} */ + + /** Update the datepicker display. + Use it with the {@linkcode module:Datepick~defaultOptions|onShow} option. + @callback DatepickOnShow + @global + @param {jQuery} picker The datepicker div to be shown. + @param {object} inst The current instance settings. + @example onShow: function(picker, inst) { + picker.append('<button type="button">Hi</button>'). + find('button:last').click(function() { + alert('Hi!'); + }); +} */ + + /** React to navigating through the months/years. + Use it with the {@linkcode module:Datepick~defaultOptions|onChangeMonthYear} option. + @callback DatepickOnChangeMonthYear + @global + @param {number} year The new year. + @param {number} month The new month (1 to 12). + @example onChangeMonthYear: function(year, month) { + alert('Now in ' + month + '/' + year); +} */ + + /** Datepicker on select callback. + Triggered when a date is selected. + Use it with the {@linkcode module:Datepick~defaultOptions|onSelect} option. + @callback DatepickOnSelect + @global + @param {Date[]} dates The selected date(s). + @example onSelect: function(dates) { + alert('Selected ' + dates); +} */ + + /** Datepicker on close callback. + Triggered when a popup calendar is closed. + Use it with the {@linkcode module:Datepick~defaultOptions|onClose} option. + @callback DatepickOnClose + @global + @param {Date[]} dates The selected date(s). + @example onClose: function(dates) { + alert('Selected ' + dates); +} */ + + /** Default settings for the plugin. + @property {string} [pickerClass=''] CSS class to add to this instance of the datepicker. + @property {boolean} [showOnFocus=true] true for popup on focus, false for not. + @property {string|Element|jQuery} [showTrigger=null] Element to be cloned for a trigger, + null for none. + @property {string} [showAnim='show'] Name of jQuery animation for popup, '' for no animation. + @property {object} [showOptions=null] Options for enhanced animations. + @property {string|number} [showSpeed='normal'] Duration of display/closure, named value or milliseconds. + @property {string|Element|jQuery} [popupContainer=null] The element to which a popup calendar is added, + null for body. + @property {string} [alignment='bottom'] Alignment of popup - with nominated corner of input: + 'top' or 'bottom' aligns depending on language direction, + 'topLeft', 'topRight', 'bottomLeft', 'bottomRight'. + @property {boolean} [fixedWeeks=false] true to always show 6 weeks, + false to only show as many as are needed. + @property {number} [firstDay=0] First day of the week, 0 = Sunday, 1 = Monday, etc. + @property {DatepickCalculateWeek} [calculateWeek=this.iso8601Week] Calculate week of the year from a date, + null for ISO8601. + @property {number|number[]} [monthsToShow=1] How many months to show, cols or [rows, cols]. + @property {number|DatepickMonthsOffset} [monthsOffset=0] How many months to offset the primary month by; + may be a function that takes the date and returns the offset. + @property {number} [monthsToStep=1] How many months to move when prev/next clicked. + @property {number} [monthsToJump=12] How many months to move when large prev/next clicked. + @property {boolean} [useMouseWheel=true] true to use mousewheel if available, + false to never use it. + @property {boolean} [changeMonth=true] true to change month/year via drop-down, + false for navigation only. + @property {string} [yearRange='c-10:c+10'] Range of years to show in drop-down: 'any' for direct text entry + or 'start:end', where start/end are '±nn' for relative to today + or 'c±nn' for relative to the currently selected date + or 'nnnn' for an absolute year. + @property {string|number} [shortYearCutoff='+10'] Cutoff for two-digit year in the current century. + If expressed as a string it is offset from the current year. + If expressed as a number it is used directly. + Use -1 to always use 1900 as the base year, or use 100 to disable the functionality. + Any short year ('yy') entered is transformed into a full year in the current century + if less than or equal to this cutoff value, and the previous century otherwise. + @property {boolean} [showOtherMonths=false] true to show dates from other months, + false to not show them. + @property {boolean} [selectOtherMonths=false] true to allow selection of dates + from other months too. + @property {string|number|Date} [defaultDate=null] Date to show if no other selected. + If expressed as a string it is parsed using the current + {@linkcode module:Datepick~regionalOptions|dateFormat}. + If expressed as a number it is offset that number of days from today. + If expressed as a Date it is used directly. + @property {boolean} [selectDefaultDate=false] true to pre-select the default date + if no other is chosen. + @property {string|number|Date} [minDate=null] The minimum selectable date. + See the allowed values in defaultDate above. + @property {string|number|Date} [maxDate=null] The maximum selectable date. + See the allowed values in defaultDate above. + @property {string} [dateFormat='mm/dd/yyyy'] Format for dates. + See {@linkcode module:Datepick~formatDate|formatDate} for allowed formats. + @property {boolean} [autoSize=false] true to size the input field according to + the {@linkcode module:Datepick~regionalOptions|dateFormat}. + @property {boolean} [rangeSelect=false] Allows for selecting a date range on one date picker. + @property {string} [rangeSeparator=' - '] Text between two dates in a range when displayed. + @property {number} [multiSelect=0] Maximum number of selectable dates for multiple independent dates, + zero for single select. If specified, + multiSelect takes precedence over rangeSelect. + @property {string} [multiSeparator=','] Text between multiple dates. + @property {DatepickOnDate} [onDate=null] Callback as each date is added to the display calendar. + This allows you to customise the behaviour and presentation of each date. + @property {DatepickOnShow} [onShow=null] Callback just before a datepicker is shown. + This allows you to customise the datepicker before display. + @property {DatepickOnChangeMonthYear} [onChangeMonthYear=null] Callback when a new month/year is selected. + This allows you to perform other actions when the calendar changes. + @property {DatepickOnSelect} [onSelect=null] Callback when a date is selected. + @property {DatepickOnClose} [onClose=null] Callback when a datepicker is closed. + @property {string|Element|jQuery} [altField=null] Alternate field to update in synch with the datepicker. + @property {string} [altFormat=null] Date format for alternate field, defaults to + {@linkcode module:Datepick~regionalOptions|dateFormat}. + This allows you to display one (human-friendly) format, + while automatically maintaining another (computer-friendly) format. + @property {boolean} [constrainInput=true] true to constrain typed input to + {@linkcode module:Datepick~regionalOptions|dateFormat} allowed characters. + @property {boolean} [commandsAsDateFormat=false] true to apply + {@linkcode module:Datepick~formatDate|formatDate} to the command texts. + @property {object} [commands=this.commands] Command actions that may be added to a layout by name. */ + defaultOptions: { + pickerClass: '', + showOnFocus: true, + showTrigger: null, + showAnim: 'show', + showOptions: {}, + showSpeed: 'normal', + popupContainer: null, + alignment: 'bottom', + fixedWeeks: false, + firstDay: 0, + calculateWeek: null, // this.iso8601Week, + monthsToShow: 1, + monthsOffset: 0, + monthsToStep: 1, + monthsToJump: 12, + useMouseWheel: true, + changeMonth: true, + yearRange: 'c-10:c+10', + shortYearCutoff: '+10', + showOtherMonths: false, + selectOtherMonths: false, + defaultDate: null, + selectDefaultDate: false, + minDate: null, + maxDate: null, + dateFormat: 'mm/dd/yyyy', + autoSize: false, + rangeSelect: false, + rangeSeparator: ' - ', + multiSelect: 0, + multiSeparator: ',', + onDate: null, + onShow: null, + onChangeMonthYear: null, + onSelect: null, + onClose: null, + altField: null, + altFormat: null, + constrainInput: true, + commandsAsDateFormat: false, + commands: {} // this.commands + }, + + /** Localisations for the plugin. + Entries are objects indexed by the language code ('' being the default US/English). + Each object has the following attributes. + @property {string[]} [monthNames=['January','February',...,'November','December']] + The long names of the months. + @property {string[]} [monthNamesShort=['Jan','Feb',...,'Nov','Dec']] + The short names of the months. + @property {string[]} [dayNames=['Sunday','Monday',...,'Friday','Saturday']] + The long names of the days of the week. + @property {string[]} [dayNamesShort=['Sun','Mon','Tue','Wed','Thu','Fri','Sat']] + The short names of the days of the week. + @property {string[]} [dayNamesMin=['Su','Mo','Tu','We','Th','Fr','Sa']] + The minimal names of the days of the week. + @property {string} [dateFormat='mm/dd/yyyy'] See options on {@linkcode module:Datepick~formatDate|formatDate}. + @property {number} [firstDay=0] The first day of the week, Sun = 0, Mon = 1, etc. + @property {string} [renderer=this.defaultRenderer] The rendering templates. + @property {string} [prevText='<Prev'] Text for the previous month command. + @property {string} [prevStatus='Show the previous month'] Status text for the previous month command. + @property {string} [prevJumpText='<<'] Text for the previous year command. + @property {string} [prevJumpStatus='Show the previous year'] Status text for the previous year command. + @property {string} [nextText='Next>'] Text for the next month command. + @property {string} [nextStatus='Show the next month'] Status text for the next month command. + @property {string} [nextJumpText='>>'] Text for the next year command. + @property {string} [nextJumpStatus='Show the next year'] Status text for the next year command. + @property {string} [currentText='Current'] Text for the current month command. + @property {string} [currentStatus='Show the current month'] Status text for the current month command. + @property {string} [todayText='Today'] Text for the today's month command. + @property {string} [todayStatus='Show today\'s month'] Status text for the today's month command. + @property {string} [clearText='Clear'] Text for the clear command. + @property {string} [clearStatus='Clear all the dates'] Status text for the clear command. + @property {string} [closeText='Close'] Text for the close command. + @property {string} [closeStatus='Close the datepicker'] Status text for the close command. + @property {string} [yearStatus='Change the year'] Status text for year selection. + @property {string} [earlierText='  â–²'] Text for earlier years. + @property {string} [laterText='  â–¼'] Text for later years. + @property {string} [monthStatus='Change the month'] Status text for month selection. + @property {string} [weekText='Wk'] Text for week of the year column header. + @property {string} [weekStatus='Week of the year'] Status text for week of the year column header. + @property {string} [dayStatus='Select DD, M d, yyyy'] Status text for selectable days. + @property {string} [defaultStatus='Select a date'] Status text shown by default. + @property {boolean} [isRTL=false] true if language is written right-to-left. */ + regionalOptions: { // Available regional settings, indexed by language/country code + '': { // Default regional settings - English/US + monthNames: ['January', 'February', 'March', 'April', 'May', 'June', + 'July', 'August', 'September', 'October', 'November', 'December'], + monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], + dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], + dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], + dayNamesMin: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'], + dateFormat: 'mm/dd/yyyy', + firstDay: 0, + renderer: {}, // this.defaultRenderer + prevText: '<Prev', + prevStatus: 'Show the previous month', + prevJumpText: '<<', + prevJumpStatus: 'Show the previous year', + nextText: 'Next>', + nextStatus: 'Show the next month', + nextJumpText: '>>', + nextJumpStatus: 'Show the next year', + currentText: 'Current', + currentStatus: 'Show the current month', + todayText: 'Today', + todayStatus: 'Show today\'s month', + clearText: 'Clear', + clearStatus: 'Clear all the dates', + closeText: 'Close', + closeStatus: 'Close the datepicker', + yearStatus: 'Change the year', + earlierText: '  â–²', + laterText: '  â–¼', + monthStatus: 'Change the month', + weekText: 'Wk', + weekStatus: 'Week of the year', + dayStatus: 'Select DD, M d, yyyy', + defaultStatus: 'Select a date', + isRTL: false + } + }, + + _disabled: [], + + _popupClass: pluginName + '-popup', // Marker for popup division + _triggerClass: pluginName + '-trigger', // Marker for trigger element + _disableClass: pluginName + '-disable', // Marker for disabled element + _monthYearClass: pluginName + '-month-year', // Marker for month/year inputs + _curMonthClass: pluginName + '-month-', // Marker for current month/year + _anyYearClass: pluginName + '-any-year', // Marker for year direct input + _curDoWClass: pluginName + '-dow-', // Marker for day of week + + _ticksTo1970: (((1970 - 1) * 365 + Math.floor(1970 / 4) - Math.floor(1970 / 100) + + Math.floor(1970 / 400)) * 24 * 60 * 60 * 10000000), + _msPerDay: 24 * 60 * 60 * 1000, + + /** The {@linkcode module:Datepick~formatDate|date format} for use with Atom (RFC 3339/ISO 8601): yyyy-mm-dd. */ + ATOM: 'yyyy-mm-dd', + /** The {@linkcode module:Datepick~formatDate|date format} for use with cookies: D, dd M yyyy. */ + COOKIE: 'D, dd M yyyy', + /** The {@linkcode module:Datepick~formatDate|date format} for full display: DD, MM d, yyyy. */ + FULL: 'DD, MM d, yyyy', + /** The {@linkcode module:Datepick~formatDate|date format} for use with ISO 8601: yyyy-mm-dd. */ + ISO_8601: 'yyyy-mm-dd', + /** The {@linkcode module:Datepick~formatDate|date format} for Julian dates: J. */ + JULIAN: 'J', + /** The {@linkcode module:Datepick~formatDate|date format} for use with RFC 822: D, d M yy. */ + RFC_822: 'D, d M yy', + /** The {@linkcode module:Datepick~formatDate|date format} for use with RFC 850: DD, dd-M-yy. */ + RFC_850: 'DD, dd-M-yy', + /** The {@linkcode module:Datepick~formatDate|date format} for use with RFC 1036: D, d M yy. */ + RFC_1036: 'D, d M yy', + /** The {@linkcode module:Datepick~formatDate|date format} for use with RFC 1123: D, d M yyyy. */ + RFC_1123: 'D, d M yyyy', + /** The {@linkcode module:Datepick~formatDate|date format} for use with RFC 2822: D, d M yyyy. */ + RFC_2822: 'D, d M yyyy', + /** The {@linkcode module:Datepick~formatDate|date format} for use with RSS (RFC 822): D, d M yy. */ + RSS: 'D, d M yy', + /** The {@linkcode module:Datepick~formatDate|date format} for Windows ticks: !. */ + TICKS: '!', + /** The {@linkcode module:Datepick~formatDate|date format} for Unix timestamp: @. */ + TIMESTAMP: '@', + /** The {@linkcode module:Datepick~formatDate|date format} for use with W3C (ISO 8601): yyyy-mm-dd. */ + W3C: 'yyyy-mm-dd', + + /** Format a date object into a string value. + The format can be combinations of the following: +
      +
    • d - day of month (no leading zero)
    • +
    • dd - day of month (two digit)
    • +
    • o - day of year (no leading zeros)
    • +
    • oo - day of year (three digit)
    • +
    • D - day name short
    • +
    • DD - day name long
    • +
    • w - week of year (no leading zero)
    • +
    • ww - week of year (two digit)
    • +
    • m - month of year (no leading zero)
    • +
    • mm - month of year (two digit)
    • +
    • M - month name short
    • +
    • MM - month name long
    • +
    • yy - year (two digit)
    • +
    • yyyy - year (four digit)
    • +
    • @ - Unix timestamp (s since 01/01/1970)
    • +
    • ! - Windows ticks (100ns since 01/01/0001)
    • +
    • '...' - literal text
    • +
    • '' - single quote
    • +
    + @param {string} [format=defaultOptions.dateFormat] The desired format of the date. + @param {Date} date The date value to format. + @param {object} [settings] With these properties: + @param {string[]} [settings.dayNames] Names of the days from Sunday. + @param {string[]} [settings.dayNamesShort] Abbreviated names of the days from Sunday. + @param {string[]} [settings.monthNames] Names of the months. + @param {string[]} [settings.monthNamesShort] Abbreviated names of the months. + @param {DatepickCalculateWeek} [settings.calculateWeek] Function that determines week of the year. + @return {string} The date in the above format. + @example var display = $.datepick.formatDate('yyyy-mm-dd', new Date(2014, 12-1, 25)) */ + formatDate: function(format, date, settings) { + if (typeof format !== 'string') { + settings = date; + date = format; + format = ''; + } + if (!date) { + return ''; + } + format = format || this.defaultOptions.dateFormat; + settings = settings || {}; + var dayNamesShort = settings.dayNamesShort || this.defaultOptions.dayNamesShort; + var dayNames = settings.dayNames || this.defaultOptions.dayNames; + var monthNamesShort = settings.monthNamesShort || this.defaultOptions.monthNamesShort; + var monthNames = settings.monthNames || this.defaultOptions.monthNames; + var calculateWeek = settings.calculateWeek || this.defaultOptions.calculateWeek; + // Check whether a format character is doubled + var doubled = function(match, step) { + var matches = 1; + while (iFormat + matches < format.length && format.charAt(iFormat + matches) === match) { + matches++; + } + iFormat += matches - 1; + return Math.floor(matches / (step || 1)) > 1; + }; + // Format a number, with leading zeroes if necessary + var formatNumber = function(match, value, len, step) { + var num = '' + value; + if (doubled(match, step)) { + while (num.length < len) { + num = '0' + num; + } + } + return num; + }; + // Format a name, short or long as requested + var formatName = function(match, value, shortNames, longNames) { + return (doubled(match) ? longNames[value] : shortNames[value]); + }; + var output = ''; + var literal = false; + for (var iFormat = 0; iFormat < format.length; iFormat++) { + if (literal) { + if (format.charAt(iFormat) === '\'' && !doubled('\'')) { + literal = false; + } + else { + output += format.charAt(iFormat); + } + } + else { + switch (format.charAt(iFormat)) { + case 'd': + output += formatNumber('d', date.getDate(), 2); + break; + case 'D': + output += formatName('D', date.getDay(), dayNamesShort, dayNames); + break; + case 'o': + output += formatNumber('o', this.dayOfYear(date), 3); + break; + case 'w': + output += formatNumber('w', calculateWeek(date), 2); + break; + case 'm': + output += formatNumber('m', date.getMonth() + 1, 2); + break; + case 'M': + output += formatName('M', date.getMonth(), monthNamesShort, monthNames); + break; + case 'y': + output += (doubled('y', 2) ? date.getFullYear() : + (date.getFullYear() % 100 < 10 ? '0' : '') + date.getFullYear() % 100); + break; + case '@': + output += Math.floor(date.getTime() / 1000); + break; + case '!': + output += date.getTime() * 10000 + this._ticksTo1970; + break; + case '\'': + if (doubled('\'')) { + output += '\''; + } + else { + literal = true; + } + break; + default: + output += format.charAt(iFormat); + } + } + } + return output; + }, + + /** Parse a string value into a date object. + See {@linkcode module:Datepick~formatDate|formatDate} for the possible formats, plus: +
      +
    • * - ignore rest of string
    • +
    + @param {string} format The expected format of the date ('' for default datepicker format). + @param {string} value The date in the above format. + @param {object} [settings] With these properties: + @param {number} [settings.shortYearCutoff] The cutoff year for determining the century. + @param {string[]} [settings.dayNames] The names of the days from Sunday. + @param {string[]} [settings.dayNamesShort] The abbreviated names of the days from Sunday. + @param {string[]} [settings.monthNames] The Names of the months. + @param {string[]} [settings.monthNamesShort] The abbreviated names of the months. + @return {Date} The extracted date value or null if value is blank. + @throws Errors if the format and/or value are missing, if the value doesn't match the format, + or if the date is invalid. + @example var date = $.datepick.parseDate('dd/mm/yyyy', '25/12/2014') */ + parseDate: function(format, value, settings) { + if (typeof value === 'undefined' || value === null) { + throw 'Invalid arguments'; + } + value = (typeof value === 'object' ? value.toString() : value + ''); + if (value === '') { + return null; + } + format = format || this.defaultOptions.dateFormat; + settings = settings || {}; + var shortYearCutoff = settings.shortYearCutoff || this.defaultOptions.shortYearCutoff; + shortYearCutoff = (typeof shortYearCutoff !== 'string' ? shortYearCutoff : + this.today().getFullYear() % 100 + parseInt(shortYearCutoff, 10)); + var dayNamesShort = settings.dayNamesShort || this.defaultOptions.dayNamesShort; + var dayNames = settings.dayNames || this.defaultOptions.dayNames; + var monthNamesShort = settings.monthNamesShort || this.defaultOptions.monthNamesShort; + var monthNames = settings.monthNames || this.defaultOptions.monthNames; + var year = -1; + var month = -1; + var day = -1; + var doy = -1; + var shortYear = false; + var literal = false; + var date = null; + // Check whether a format character is doubled + var doubled = function(match, step) { + var matches = 1; + while (iFormat + matches < format.length && format.charAt(iFormat + matches) === match) { + matches++; + } + iFormat += matches - 1; + return Math.floor(matches / (step || 1)) > 1; + }; + // Extract a number from the string value + var getNumber = function(match, step) { + var isDoubled = doubled(match, step); + var size = [2, 3, isDoubled ? 4 : 2, 11, 20]['oy@!'.indexOf(match) + 1]; + var digits = new RegExp('^-?\\d{1,' + size + '}'); + var num = value.substring(iValue).match(digits); + if (!num) { + throw 'Missing number at position {0}'.replace(/\{0\}/, iValue); + } + iValue += num[0].length; + return parseInt(num[0], 10); + }; + // Extract a name from the string value and convert to an index + var getName = function(match, shortNames, longNames, step) { + var names = (doubled(match, step) ? longNames : shortNames); + for (var i = 0; i < names.length; i++) { + if (value.substr(iValue, names[i].length).toLowerCase() === names[i].toLowerCase()) { + iValue += names[i].length; + return i + 1; + } + } + throw 'Unknown name at position {0}'.replace(/\{0\}/, iValue); + }; + // Confirm that a literal character matches the string value + var checkLiteral = function() { + if (value.charAt(iValue) !== format.charAt(iFormat)) { + throw 'Unexpected literal at position {0}'.replace(/\{0\}/, iValue); + } + iValue++; + }; + var iValue = 0; + for (var iFormat = 0; iFormat < format.length; iFormat++) { + if (literal) { + if (format.charAt(iFormat) === '\'' && !doubled('\'')) { + literal = false; + } + else { + checkLiteral(); + } + } + else { + switch (format.charAt(iFormat)) { + case 'd': + day = getNumber('d'); + break; + case 'D': + getName('D', dayNamesShort, dayNames); + break; + case 'o': + doy = getNumber('o'); + break; + case 'w': + getNumber('w'); + break; + case 'm': + month = getNumber('m'); + break; + case 'M': + month = getName('M', monthNamesShort, monthNames); + break; + case 'y': + var iSave = iFormat; + shortYear = !doubled('y', 2); + iFormat = iSave; + year = getNumber('y', 2); + break; + case '@': + date = this._normaliseDate(new Date(getNumber('@') * 1000)); + year = date.getFullYear(); + month = date.getMonth() + 1; + day = date.getDate(); + break; + case '!': + date = this._normaliseDate( + new Date((getNumber('!') - this._ticksTo1970) / 10000)); + year = date.getFullYear(); + month = date.getMonth() + 1; + day = date.getDate(); + break; + case '*': + iValue = value.length; + break; + case '\'': + if (doubled('\'')) { + checkLiteral(); + } + else { + literal = true; + } + break; + default: + checkLiteral(); + } + } + } + if (iValue < value.length) { + throw 'Additional text found at end'; + } + if (year === -1) { + year = this.today().getFullYear(); + } + else if (year < 100 && shortYear) { + year += (shortYearCutoff === -1 ? 1900 : this.today().getFullYear() - + this.today().getFullYear() % 100 - (year <= shortYearCutoff ? 0 : 100)); + } + if (doy > -1) { + month = 1; + day = doy; + for (var dim = this.daysInMonth(year, month); day > dim; + dim = this.daysInMonth(year, month)) { + month++; + day -= dim; + } + } + date = this.newDate(year, month, day); + if (date.getFullYear() !== year || date.getMonth() + 1 !== month || date.getDate() !== day) { + throw 'Invalid date'; + } + return date; + }, + + /** A date may be specified as an exact value or a relative one. + @param {Date|number|string} dateSpec The date as a Date, + or as a string in the current {@linkcode module:Datepick~regionalOptions|dateFormat}, + or as a numeric offset - in days from today, + or as a string of amounts and periods, e.g. '+1m +2w', + using 'd' for days, 'w' for weeks, 'm' for months, and 'y' for years. + @param {Date} [defaultDate] The date to use if no other supplied, may be null. + @param {Date} [currentDate] The current date as a possible basis for relative dates, + if null today is used. + @param {string} [dateFormat] The expected date format - see {@linkcode module:Datepick~formatDate|formatDate}. + @param {object} [settings] With these properties: + @param {number} [settings.shortYearCutoff] The cutoff year for determining the century. + @param {string[]} [settings.dayNamesShort] Abbreviated names of the days from Sunday. + @param {string[]} [settings.dayNames] Names of the days from Sunday. + @param {string[]} [settings.monthNamesShort] Abbreviated names of the months. + @param {string[]} [settings.monthNames] Names of the months. + @return {Date} The decoded date. + @example var date = $.datepick.determineDate('+1m +2w', new Date()) */ + determineDate: function(dateSpec, defaultDate, currentDate, dateFormat, settings) { + if (currentDate && typeof currentDate !== 'object') { + settings = dateFormat; + dateFormat = currentDate; + currentDate = null; + } + if (typeof dateFormat !== 'string') { + settings = dateFormat; + dateFormat = ''; + } + var offsetString = function(offset) { + try { + return plugin.parseDate(dateFormat, offset, settings); + } + catch (e) { + // Ignore + } + offset = offset.toLowerCase(); + var date = (offset.match(/^c/) && currentDate ? plugin.newDate(currentDate) : null) || + plugin.today(); + var pattern = /([+-]?[0-9]+)\s*(d|w|m|y)?/g; + var matches = null; + while ((matches = pattern.exec(offset))) { + date = plugin.add(date, parseInt(matches[1], 10), matches[2] || 'd'); + } + return date; + }; + defaultDate = (defaultDate ? plugin.newDate(defaultDate) : null); + dateSpec = (typeof dateSpec === 'undefined' ? defaultDate : + (typeof dateSpec === 'string' ? offsetString(dateSpec) : (typeof dateSpec === 'number' ? + (isNaN(dateSpec) || dateSpec === Infinity || dateSpec === -Infinity ? defaultDate : + plugin.add(plugin.today(), dateSpec, 'd')) : plugin.newDate(dateSpec)))); + return dateSpec; + }, + + /** Find the number of days in a given month. + @param {Date|number} year The date to get days for or the full year. + @param {number} [month] The month (1 to 12), if the year is a number. + @return {number} The number of days in this month. + @example var days = $.datepick.daysInMonth(2014, 12) +var days = $.datepick.daysInMonth(new Date(2014, 12-1, 25)) */ + daysInMonth: function(year, month) { + month = (year.getFullYear ? year.getMonth() + 1 : month); + year = (year.getFullYear ? year.getFullYear() : year); + return this.newDate(year, month + 1, 0).getDate(); + }, + + /** Calculate the day of the year for a date. + @param {Date|number} year The date to get the day-of-year for or the full year. + @param {number} [month] The month (1-12), if the year is a number. + @param {number} [day] The day, if the year is a number. + @return {number} The day of the year. + @example var doy = $.datepick.dayOfYear(2014, 12, 25) +var doy = $.datepick.dayOfYear(new Date(2014, 12-1, 25)) */ + dayOfYear: function(year, month, day) { + var date = (year.getFullYear ? year : plugin.newDate(year, month, day)); + var newYear = plugin.newDate(date.getFullYear(), 1, 1); + return Math.floor((date.getTime() - newYear.getTime()) / plugin._msPerDay) + 1; + }, + + /** Set as calculateWeek to determine the week of the year based on the ISO 8601 definition. + @param {Date|number} year The date to get the week for or the full year. + @param {number} [month] The month (1-12), if the year is a number. + @param {number} [day] The day, if the year is a number. + @return {number} The number of the week within the year that contains this date. + @example var week = $.datepick.iso8601Week(2014, 12, 25) +var week = $.datepick.iso8601Week(new Date(2014, 12-1, 25)) */ + iso8601Week: function(year, month, day) { + var checkDate = (year.getFullYear ? + new Date(year.getTime()) : plugin.newDate(year, month, day)); + // Find Thursday of this week starting on Monday + checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7)); + var time = checkDate.getTime(); + checkDate.setMonth(0, 1); // Compare with Jan 1 + return Math.floor(Math.round((time - checkDate) / plugin._msPerDay) / 7) + 1; + }, + + /** Return today's date. + @return {Date} Today. + @example var today = $.datepick.today() */ + today: function() { + return this._normaliseDate(new Date()); + }, + + /** Return a new date. + @param {Date|number} year The date to clone or the year. + @param {number} [month] The month (1-12), if the year is a number. + @param {number} [day] The day, if the year is a number. + @return {Date} The date. + @example $.datepick.newDate(oldDate) +$.datepick.newDate(2014, 12, 25) */ + newDate: function(year, month, day) { + return (!year ? null : (year.getFullYear ? this._normaliseDate(new Date(year.getTime())) : + new Date(year, month - 1, day, 12))); + }, + + /** Standardise a date into a common format - time portion is 12 noon. + @private + @param {Date} date The date to standardise. + @return {Date} The normalised date. */ + _normaliseDate: function(date) { + if (date) { + date.setHours(12, 0, 0, 0); + } + return date; + }, + + /** Set the year for a date. + @param {Date} date The original date. + @param {number} year The new year. + @return {Date} The updated date. + @example $.datepick.year(date, 2014) */ + year: function(date, year) { + date.setFullYear(year); + return this._normaliseDate(date); + }, + + /** Set the month for a date. + @param {Date} date The original date. + @param {number} month The new month (1-12). + @return {Date} The updated date. + @example $.datepick.month(date, 12) */ + month: function(date, month) { + date.setMonth(month - 1); + return this._normaliseDate(date); + }, + + /** Set the day for a date. + @param {Date} date The original date. + @param {number} day The new day of the month. + @return {Date} The updated date. + @example $.datepick.day(date, 25) */ + day: function(date, day) { + date.setDate(day); + return this._normaliseDate(date); + }, + + /** Add a number of periods to a date. + @param {Date} date The original date. + @param {number} amount The number of periods. + @param {string} period The type of period 'd' for days, 'w' for weeks, 'm' for months, 'y' for years. + @return {Date} The updated date. + @example $.datepick.add(date, 10, 'd') */ + add: function(date, amount, period) { + if (period === 'd' || period === 'w') { + this._normaliseDate(date); + date.setDate(date.getDate() + amount * (period === 'w' ? 7 : 1)); + } + else { + var year = date.getFullYear() + (period === 'y' ? amount : 0); + var month = date.getMonth() + (period === 'm' ? amount : 0); + date.setTime(plugin.newDate(year, month + 1, + Math.min(date.getDate(), this.daysInMonth(year, month + 1))).getTime()); + } + return date; + }, + + /** Apply the months offset value to a date. + @private + @param {Date} date The original date. + @param {object} inst The current instance settings. + @return {Date} The updated date. */ + _applyMonthsOffset: function(date, inst) { + var monthsOffset = inst.options.monthsOffset; + if ($.isFunction(monthsOffset)) { + monthsOffset = monthsOffset.apply(inst.elem[0], [date]); + } + return plugin.add(date, -monthsOffset, 'm'); + }, + + _init: function() { + this.defaultOptions.commands = this.commands; + this.defaultOptions.calculateWeek = this.iso8601Week; + this.regionalOptions[''].renderer = this.defaultRenderer; + this._super(); + }, + + _instSettings: function(elem) { + return {selectedDates: [], drawDate: null, pickingRange: false, + inline: ($.inArray(elem[0].nodeName.toLowerCase(), ['div', 'span']) > -1), + get: function(name) { // Get a setting value, computing if necessary + if ($.inArray(name, ['defaultDate', 'minDate', 'maxDate']) > -1) { // Decode date settings + return plugin.determineDate(this.options[name], null, + this.selectedDates[0], this.options.dateFormat, this.getConfig()); + } + return this.options[name]; + }, + curMinDate: function() { + return (this.pickingRange ? this.selectedDates[0] : this.get('minDate')); + }, + getConfig: function() { + return {dayNamesShort: this.options.dayNamesShort, dayNames: this.options.dayNames, + monthNamesShort: this.options.monthNamesShort, monthNames: this.options.monthNames, + calculateWeek: this.options.calculateWeek, + shortYearCutoff: this.options.shortYearCutoff}; + } + }; + }, + + _postAttach: function(elem, inst) { + if (inst.inline) { + inst.drawDate = plugin._checkMinMax(plugin.newDate(inst.selectedDates[0] || + inst.get('defaultDate') || plugin.today()), inst); + inst.prevDate = plugin.newDate(inst.drawDate); + this._update(elem[0]); + if ($.fn.mousewheel) { + elem.mousewheel(this._doMouseWheel); + } + } + else { + this._attachments(elem, inst); + elem.on('keydown.' + inst.name, this._keyDown).on('keypress.' + inst.name, this._keyPress). + on('keyup.' + inst.name, this._keyUp); + if (elem.attr('disabled')) { + this.disable(elem[0]); + } + } + }, + + _optionsChanged: function(elem, inst, options) { + if (options.calendar && options.calendar !== inst.options.calendar) { + var discardDate = function(name) { + return (typeof inst.options[name] === 'object' ? null : inst.options[name]); + }; + options = $.extend({defaultDate: discardDate('defaultDate'), + minDate: discardDate('minDate'), maxDate: discardDate('maxDate')}, options); + inst.selectedDates = []; + inst.drawDate = null; + } + var dates = inst.selectedDates; + $.extend(inst.options, options); + this.setDate(elem[0], dates, null, false, true); + inst.pickingRange = false; + inst.drawDate = plugin.newDate(this._checkMinMax( + (inst.options.defaultDate ? inst.get('defaultDate') : inst.drawDate) || + inst.get('defaultDate') || plugin.today(), inst)); + if (!inst.inline) { + this._attachments(elem, inst); + } + if (inst.inline || inst.div) { + this._update(elem[0]); + } + }, + + /** Attach events and trigger, if necessary. + @private + @param {jQuery} elem The control to affect. + @param {object} inst The current instance settings. */ + _attachments: function(elem, inst) { + elem.off('focus.' + inst.name); + if (inst.options.showOnFocus) { + elem.on('focus.' + inst.name, this.show); + } + if (inst.trigger) { + inst.trigger.remove(); + } + var trigger = inst.options.showTrigger; + inst.trigger = (!trigger ? $([]) : + $(trigger).clone().removeAttr('id').addClass(this._triggerClass) + [inst.options.isRTL ? 'insertBefore' : 'insertAfter'](elem). + click(function() { + if (!plugin.isDisabled(elem[0])) { + plugin[plugin.curInst === inst ? 'hide' : 'show'](elem[0]); + } + })); + this._autoSize(elem, inst); + var dates = this._extractDates(inst, elem.val()); + if (dates) { + this.setDate(elem[0], dates, null, true); + } + var defaultDate = inst.get('defaultDate'); + if (inst.options.selectDefaultDate && defaultDate && inst.selectedDates.length === 0) { + this.setDate(elem[0], plugin.newDate(defaultDate || plugin.today())); + } + }, + + /** Apply the maximum length for the date format. + @private + @param {jQuery} elem The control to affect. + @param {object} inst The current instance settings. */ + _autoSize: function(elem, inst) { + if (inst.options.autoSize && !inst.inline) { + var date = plugin.newDate(2009, 10, 20); // Ensure double digits + var dateFormat = inst.options.dateFormat; + if (dateFormat.match(/[DM]/)) { + var findMax = function(names) { + var max = 0; + var maxI = 0; + for (var i = 0; i < names.length; i++) { + if (names[i].length > max) { + max = names[i].length; + maxI = i; + } + } + return maxI; + }; + date.setMonth(findMax(inst.options[dateFormat.match(/MM/) ? // Longest month + 'monthNames' : 'monthNamesShort'])); + date.setDate(findMax(inst.options[dateFormat.match(/DD/) ? // Longest day + 'dayNames' : 'dayNamesShort']) + 20 - date.getDay()); + } + inst.elem.attr('size', plugin.formatDate(dateFormat, date, inst.getConfig()).length); + } + }, + + _preDestroy: function(elem, inst) { + if (inst.trigger) { + inst.trigger.remove(); + } + elem.empty().off('.' + inst.name); + if (inst.inline && $.fn.mousewheel) { + elem.unmousewheel(); + } + if (!inst.inline && inst.options.autoSize) { + elem.removeAttr('size'); + } + }, + + /** Apply multiple event functions. + @param {function} fns The functions to apply. + @example onShow: $.datepick.multipleEvents(fn1, fn2, ...) */ + multipleEvents: function() { + var funcs = arguments; + return function() { + for (var i = 0; i < funcs.length; i++) { + funcs[i].apply(this, arguments); + } + }; + }, + + /** Enable the control. + @param {Element} elem The control to affect. + @example $(selector).datepick('enable') */ + enable: function(elem) { + elem = $(elem); + if (!elem.hasClass(this._getMarker())) { + return; + } + var inst = this._getInst(elem); + if (inst.inline) { + elem.children('.' + this._disableClass).remove().end(). + find('button,select').prop('disabled', false).end(). + find('a').attr('href', '#'); + } + else { + elem.prop('disabled', false); + inst.trigger.filter('button.' + this._triggerClass).prop('disabled', false).end(). + filter('img.' + this._triggerClass).css({opacity: '1.0', cursor: ''}); + } + this._disabled = $.map(this._disabled, + function(value) { return (value === elem[0] ? null : value); }); // Delete entry + }, + + /** Disable the control. + @param {Element} elem The control to affect. + @example $(selector).datepick('disable') */ + disable: function(elem) { + elem = $(elem); + if (!elem.hasClass(this._getMarker())) { + return; + } + var inst = this._getInst(elem); + if (inst.inline) { + var inline = elem.children(':last'); + var offset = inline.offset(); + var relOffset = {left: 0, top: 0}; + inline.parents().each(function() { + if ($(this).css('position') === 'relative') { + relOffset = $(this).offset(); + return false; + } + }); + var zIndex = elem.css('zIndex'); + zIndex = (zIndex === 'auto' ? 0 : parseInt(zIndex, 10)) + 1; + elem.prepend('
    '). + find('button,select').prop('disabled', true).end(). + find('a').removeAttr('href'); + } + else { + elem.prop('disabled', true); + inst.trigger.filter('button.' + this._triggerClass).prop('disabled', true).end(). + filter('img.' + this._triggerClass).css({opacity: '0.5', cursor: 'default'}); + } + this._disabled = $.map(this._disabled, + function(value) { return (value === elem[0] ? null : value); }); // Delete entry + this._disabled.push(elem[0]); + }, + + /** Is the first field in a jQuery collection disabled as a datepicker? + @param {Element} elem The control to examine. + @return {boolean} true if disabled, false if enabled. + @example if ($(selector).datepick('isDisabled')) {...} */ + isDisabled: function(elem) { + return (elem && $.inArray(elem, this._disabled) > -1); + }, + + /** Show a popup datepicker. + @param {Element|Event} elem The control to use or a focus event (internal). + @example $(selector).datepick('show') */ + show: function(elem) { + elem = $(elem.target || elem); + var inst = plugin._getInst(elem); + if (plugin.curInst === inst) { + return; + } + if (plugin.curInst) { + plugin.hide(plugin.curInst, true); + } + if (!$.isEmptyObject(inst)) { + // Retrieve existing date(s) + inst.lastVal = null; + inst.selectedDates = plugin._extractDates(inst, elem.val()); + inst.pickingRange = false; + inst.drawDate = plugin._checkMinMax(plugin.newDate(inst.selectedDates[0] || + inst.get('defaultDate') || plugin.today()), inst); + inst.prevDate = plugin.newDate(inst.drawDate); + plugin.curInst = inst; + // Generate content + plugin._update(elem[0], true); + // Adjust position before showing + var offset = plugin._checkOffset(inst); + inst.div.css({left: offset.left, top: offset.top}); + // And display + var showAnim = inst.options.showAnim; + var showSpeed = inst.options.showSpeed; + showSpeed = (showSpeed === 'normal' && $.ui && + parseInt($.ui.version.substring(2)) >= 8 ? '_default' : showSpeed); + if ($.effects && ($.effects[showAnim] || ($.effects.effect && $.effects.effect[showAnim]))) { + var data = inst.div.data(); // Update old effects data + for (var key in data) { + if (key.match(/^ec\.storage\./)) { + data[key] = inst._mainDiv.css(key.replace(/ec\.storage\./, '')); + } + } + inst.div.data(data).show(showAnim, inst.options.showOptions, showSpeed); + } + else { + inst.div[showAnim || 'show'](showAnim ? showSpeed : 0); + } + } + }, + + /** Extract possible dates from a string. + @private + @param {object} inst The current instance settings. + @param {string} text The text to extract from. + @return {Date[]} The extracted dates. */ + _extractDates: function(inst, datesText) { + if (datesText === inst.lastVal) { + return; + } + inst.lastVal = datesText; + datesText = datesText.split(inst.options.multiSelect ? inst.options.multiSeparator : + (inst.options.rangeSelect ? inst.options.rangeSeparator : '\x00')); + var dates = []; + for (var i = 0; i < datesText.length; i++) { + try { + var date = plugin.parseDate(inst.options.dateFormat, datesText[i], inst.getConfig()); + if (date) { + var found = false; + for (var j = 0; j < dates.length; j++) { + if (dates[j].getTime() === date.getTime()) { + found = true; + break; + } + } + if (!found) { + dates.push(date); + } + } + } + catch (e) { + // Ignore + } + } + dates.splice(inst.options.multiSelect || (inst.options.rangeSelect ? 2 : 1), dates.length); + if (inst.options.rangeSelect && dates.length === 1) { + dates[1] = dates[0]; + } + return dates; + }, + + /** Update the datepicker display. + @private + @param {Event|Element} elem A focus event or the control to use. + @param {boolean} hidden true to initially hide the datepicker. */ + _update: function(elem, hidden) { + elem = $(elem.target || elem); + var inst = plugin._getInst(elem); + if (!$.isEmptyObject(inst)) { + if (inst.inline || plugin.curInst === inst) { + if ($.isFunction(inst.options.onChangeMonthYear) && (!inst.prevDate || + inst.prevDate.getFullYear() !== inst.drawDate.getFullYear() || + inst.prevDate.getMonth() !== inst.drawDate.getMonth())) { + inst.options.onChangeMonthYear.apply(elem[0], + [inst.drawDate.getFullYear(), inst.drawDate.getMonth() + 1]); + } + } + if (inst.inline) { + var index = $('a, :input', elem).index($(':focus', elem)); + elem.html(this._generateContent(elem[0], inst)); + var focus = elem.find('a, :input'); + focus.eq(Math.max(Math.min(index, focus.length - 1), 0)).focus(); + } + else if (plugin.curInst === inst) { + if (!inst.div) { + inst.div = $('
    ').addClass(this._popupClass). + css({display: (hidden ? 'none' : 'static'), position: 'absolute', + left: elem.offset().left, top: elem.offset().top + elem.outerHeight()}). + appendTo($(inst.options.popupContainer || 'body')); + if ($.fn.mousewheel) { + inst.div.mousewheel(this._doMouseWheel); + } + } + inst.div.html(this._generateContent(elem[0], inst)); + elem.focus(); + } + } + }, + + /** Update the input field and any alternate field with the current dates. + @private + @param {Element} elem The control to use. + @param {boolean} keyUp true if coming from keyUp processing (internal). */ + _updateInput: function(elem, keyUp) { + var inst = this._getInst(elem); + if (!$.isEmptyObject(inst)) { + var value = ''; + var altValue = ''; + var sep = (inst.options.multiSelect ? inst.options.multiSeparator : + inst.options.rangeSeparator); + var altFormat = inst.options.altFormat || inst.options.dateFormat; + for (var i = 0; i < inst.selectedDates.length; i++) { + value += (keyUp ? '' : (i > 0 ? sep : '') + plugin.formatDate( + inst.options.dateFormat, inst.selectedDates[i], inst.getConfig())); + altValue += (i > 0 ? sep : '') + plugin.formatDate( + altFormat, inst.selectedDates[i], inst.getConfig()); + } + if (!inst.inline && !keyUp) { + $(elem).val(value); + } + $(inst.options.altField).val(altValue); + if ($.isFunction(inst.options.onSelect) && !keyUp && !inst.inSelect) { + inst.inSelect = true; // Prevent endless loops + inst.options.onSelect.apply(elem, [inst.selectedDates]); + inst.inSelect = false; + } + } + }, + + /** Retrieve the size of left and top borders for an element. + @private + @param {jQuery} elem The element of interest. + @return {number[]} The left and top borders. */ + _getBorders: function(elem) { + var convert = function(value) { + return {thin: 1, medium: 3, thick: 5}[value] || value; + }; + return [parseFloat(convert(elem.css('border-left-width'))), + parseFloat(convert(elem.css('border-top-width')))]; + }, + + /** Check positioning to remain on the screen. + @private + @param {object} inst The current instance settings. + @return {object} The updated offset for the datepicker. */ + _checkOffset: function(inst) { + var base = (inst.elem.is(':hidden') && inst.trigger ? inst.trigger : inst.elem); + var offset = base.offset(); + var browserWidth = $(window).width(); + var browserHeight = $(window).height(); + if (browserWidth === 0) { + return offset; + } + var isFixed = false; + $(inst.elem).parents().each(function() { + isFixed = isFixed || ($(this).css('position') === 'fixed'); + return !isFixed; + }); + var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft; + var scrollY = document.documentElement.scrollTop || document.body.scrollTop; + var above = offset.top - (isFixed ? scrollY : 0) - inst.div.outerHeight(); + var below = offset.top - (isFixed ? scrollY : 0) + base.outerHeight(); + var alignL = offset.left - (isFixed ? scrollX : 0); + var alignR = offset.left - (isFixed ? scrollX : 0) + base.outerWidth() - inst.div.outerWidth(); + var tooWide = (offset.left - scrollX + inst.div.outerWidth()) > browserWidth; + var tooHigh = (offset.top - scrollY + inst.elem.outerHeight() + + inst.div.outerHeight()) > browserHeight; + inst.div.css('position', isFixed ? 'fixed' : 'absolute'); + var alignment = inst.options.alignment; + if (alignment === 'topLeft') { + offset = {left: alignL, top: above}; + } + else if (alignment === 'topRight') { + offset = {left: alignR, top: above}; + } + else if (alignment === 'bottomLeft') { + offset = {left: alignL, top: below}; + } + else if (alignment === 'bottomRight') { + offset = {left: alignR, top: below}; + } + else if (alignment === 'top') { + offset = {left: (inst.options.isRTL || tooWide ? alignR : alignL), top: above}; + } + else { // bottom + offset = {left: (inst.options.isRTL || tooWide ? alignR : alignL), + top: (tooHigh ? above : below)}; + } + offset.left = Math.max((isFixed ? 0 : scrollX), offset.left); + offset.top = Math.max((isFixed ? 0 : scrollY), offset.top); + return offset; + }, + + /** Close date picker if clicked elsewhere. + @private + @param {MouseEvent} event The mouse click to check. */ + _checkExternalClick: function(event) { + if (!plugin.curInst) { + return; + } + var elem = $(event.target); + if (elem.closest('.' + plugin._popupClass + ',.' + plugin._triggerClass).length === 0 && + !elem.hasClass(plugin._getMarker())) { + plugin.hide(plugin.curInst); + } + }, + + /** Hide a popup datepicker. + @param {Element|object} elem The control to use or the current instance settings. + @param {boolean} [immediate=false] true to close immediately without animation (internal). + @example $(selector).datepick('hide') */ + hide: function(elem, immediate) { + if (!elem) { + return; + } + var inst = this._getInst(elem); + if ($.isEmptyObject(inst)) { + inst = elem; + } + if (inst && inst === plugin.curInst) { + var showAnim = (immediate ? '' : inst.options.showAnim); + var showSpeed = inst.options.showSpeed; + showSpeed = (showSpeed === 'normal' && $.ui && + parseInt($.ui.version.substring(2)) >= 8 ? '_default' : showSpeed); + var postProcess = function() { + if (!inst.div) { + return; + } + inst.div.remove(); + inst.div = null; + plugin.curInst = null; + if ($.isFunction(inst.options.onClose)) { + inst.options.onClose.apply(elem, [inst.selectedDates]); + } + }; + inst.div.stop(); + if ($.effects && ($.effects[showAnim] || ($.effects.effect && $.effects.effect[showAnim]))) { + inst.div.hide(showAnim, inst.options.showOptions, showSpeed, postProcess); + } + else { + var hideAnim = (showAnim === 'slideDown' ? 'slideUp' : + (showAnim === 'fadeIn' ? 'fadeOut' : 'hide')); + inst.div[hideAnim]((showAnim ? showSpeed : ''), postProcess); + } + if (!showAnim) { + postProcess(); + } + } + }, + + /** Handle keystrokes in the datepicker. + @private + @param {KeyEvent} event The keystroke. + @return {boolean} true if not handled, false if handled. */ + _keyDown: function(event) { + var elem = (event.data && event.data.elem) || event.target; + var inst = plugin._getInst(elem); + var handled = false; + var command = null; + if (inst.inline || inst.div) { + if (event.keyCode === 9) { // Tab - close + plugin.hide(elem); + } + else if (event.keyCode === 13) { // Enter - select + plugin.selectDate(elem, + $('a.' + inst.options.renderer.highlightedClass, inst.div)[0]); + handled = true; + } + else { // Command keystrokes + for (var key in inst.options.commands) { + if (inst.options.commands.hasOwnProperty(key)) { + command = inst.options.commands[key]; + /* jshint -W018 */ // Dislikes !! + if (command.keystroke.keyCode === event.keyCode && + !!command.keystroke.ctrlKey === !!(event.ctrlKey || event.metaKey) && + !!command.keystroke.altKey === event.altKey && + !!command.keystroke.shiftKey === event.shiftKey) { + /* jshint +W018 */ + plugin.performAction(elem, key); + handled = true; + break; + } + } + } + } + } + else { // Show on 'current' keystroke + command = inst.options.commands.current; + /* jshint -W018 */ // Dislikes !! + if (command.keystroke.keyCode === event.keyCode && + !!command.keystroke.ctrlKey === !!(event.ctrlKey || event.metaKey) && + !!command.keystroke.altKey === event.altKey && + !!command.keystroke.shiftKey === event.shiftKey) { + /* jshint +W018 */ + plugin.show(elem); + handled = true; + } + } + inst.ctrlKey = ((event.keyCode < 48 && event.keyCode !== 32) || event.ctrlKey || event.metaKey); + if (handled) { + event.preventDefault(); + event.stopPropagation(); + } + return !handled; + }, + + /** Filter keystrokes in the datepicker. + @private + @param {KeyEvent} event The keystroke. + @return {boolean} true if allowed, false if not allowed. */ + _keyPress: function(event) { + var inst = plugin._getInst((event.data && event.data.elem) || event.target); + if (!$.isEmptyObject(inst) && inst.options.constrainInput) { + var ch = String.fromCharCode(event.keyCode || event.charCode); + var allowedChars = plugin._allowedChars(inst); + return (event.metaKey || inst.ctrlKey || ch < ' ' || + !allowedChars || allowedChars.indexOf(ch) > -1); + } + return true; + }, + + /** Determine the set of characters allowed by the date format. + @private + @param {object} inst The current instance settings. + @return {string} The set of allowed characters, or null if anything allowed. */ + _allowedChars: function(inst) { + var allowedChars = (inst.options.multiSelect ? inst.options.multiSeparator : + (inst.options.rangeSelect ? inst.options.rangeSeparator : '')); + var literal = false; + var hasNum = false; + var dateFormat = inst.options.dateFormat; + for (var i = 0; i < dateFormat.length; i++) { + var ch = dateFormat.charAt(i); + if (literal) { + if (ch === '\'' && dateFormat.charAt(i + 1) !== '\'') { + literal = false; + } + else { + allowedChars += ch; + } + } + else { + switch (ch) { + case 'd': + case 'm': + case 'o': + case 'w': + allowedChars += (hasNum ? '' : '0123456789'); + hasNum = true; + break; + case 'y': + case '@': + case '!': + allowedChars += (hasNum ? '' : '0123456789') + '-'; + hasNum = true; + break; + case 'J': + allowedChars += (hasNum ? '' : '0123456789') + '-.'; + hasNum = true; + break; + case 'D': + case 'M': + case 'Y': + return null; // Accept anything + case '\'': + if (dateFormat.charAt(i + 1) === '\'') { + allowedChars += '\''; + } + else { + literal = true; + } + break; + default: + allowedChars += ch; + } + } + } + return allowedChars; + }, + + /** Synchronise datepicker with the field. + @private + @param {KeyEvent} event The keystroke. + @return {boolean} true if allowed, false if not allowed. */ + _keyUp: function(event) { + var elem = (event.data && event.data.elem) || event.target; + var inst = plugin._getInst(elem); + if (!$.isEmptyObject(inst) && !inst.ctrlKey && inst.lastVal !== inst.elem.val()) { + try { + var dates = plugin._extractDates(inst, inst.elem.val()); + if (dates.length > 0) { + plugin.setDate(elem, dates, null, true); + } + } + catch (e) { + // Ignore + } + } + return true; + }, + + /** Increment/decrement month/year on mouse wheel activity. + @private + @param {event} event The mouse wheel event. + @param {number} delta The amount of change. */ + _doMouseWheel: function(event, delta) { + var elem = (plugin.curInst && plugin.curInst.elem[0]) || + $(event.target).closest('.' + plugin._getMarker())[0]; + if (plugin.isDisabled(elem)) { + return; + } + var inst = plugin._getInst(elem); + if (inst.options.useMouseWheel) { + delta = (delta < 0 ? -1 : +1); + plugin.changeMonth(elem, -inst.options[event.ctrlKey ? 'monthsToJump' : 'monthsToStep'] * delta); + } + event.preventDefault(); + }, + + /** Clear an input and close a popup datepicker. + @param {Element} elem The control to use. + @example $(selector).datepick('clear') */ + clear: function(elem) { + var inst = this._getInst(elem); + if (!$.isEmptyObject(inst)) { + inst.selectedDates = []; + this.hide(elem); + var defaultDate = inst.get('defaultDate'); + if (inst.options.selectDefaultDate && defaultDate) { + this.setDate(elem, plugin.newDate(defaultDate || plugin.today())); + } + else { + this._updateInput(elem); + } + } + }, + + /** Retrieve the selected date(s) for a datepicker. + @param {Element} elem The control to examine. + @return {Date[]} The selected date(s). + @example var dates = $(selector).datepick('getDate') */ + getDate: function(elem) { + var inst = this._getInst(elem); + return (!$.isEmptyObject(inst) ? inst.selectedDates : []); + }, + + /** Set the selected date(s) for a datepicker. + @param {Element} elem The control to examine. + @param {Date|number|string|array} dates The selected date(s), as a Date, + or as a string in the current {@linkcode module:Datepick~regionalOptions|dateFormat} + or as a numeric offset - in days from today, + or as a string of amounts and periods, e.g. '+1m +2w', + using 'd' for days, 'w' for weeks, 'm' for months, and 'y' for years, + or as an array of these. + @param {Date|number|string} [endDate] The ending date for a range. + @param {boolean} [keyUp=false] true if coming from keyUp processing (internal). + @param {boolean} [setOpt=false] true if coming from option processing (internal). + @example $(selector).datepick('setDate', new Date(2014, 12-1, 25)) +$(selector).datepick('setDate', '12/25/2014', '01/01/2015') +$(selector).datepick('setDate', [date1, date2, date3]) */ + setDate: function(elem, dates, endDate, keyUp, setOpt) { + var inst = this._getInst(elem); + if (!$.isEmptyObject(inst)) { + if (!$.isArray(dates)) { + dates = [dates]; + if (endDate) { + dates.push(endDate); + } + } + var minDate = inst.get('minDate'); + var maxDate = inst.get('maxDate'); + var curDate = inst.selectedDates[0]; + inst.selectedDates = []; + for (var i = 0; i < dates.length; i++) { + var date = plugin.determineDate( + dates[i], null, curDate, inst.options.dateFormat, inst.getConfig()); + if (date) { + if ((!minDate || date.getTime() >= minDate.getTime()) && + (!maxDate || date.getTime() <= maxDate.getTime())) { + var found = false; + for (var j = 0; j < inst.selectedDates.length; j++) { + if (inst.selectedDates[j].getTime() === date.getTime()) { + found = true; + break; + } + } + if (!found) { + inst.selectedDates.push(date); + } + } + } + } + inst.selectedDates.splice(inst.options.multiSelect || + (inst.options.rangeSelect ? 2 : 1), inst.selectedDates.length); + if (inst.options.rangeSelect) { + switch (inst.selectedDates.length) { + case 1: + inst.selectedDates[1] = inst.selectedDates[0]; + break; + case 2: + inst.selectedDates[1] = + (inst.selectedDates[0].getTime() > inst.selectedDates[1].getTime() ? + inst.selectedDates[0] : inst.selectedDates[1]); + break; + } + inst.pickingRange = false; + } + inst.prevDate = (inst.drawDate ? plugin.newDate(inst.drawDate) : null); + inst.drawDate = this._checkMinMax(plugin.newDate(inst.selectedDates[0] || + inst.get('defaultDate') || plugin.today()), inst); + if (!setOpt) { + this._update(elem); + this._updateInput(elem, keyUp); + } + } + }, + + /** Determine whether a date is selectable for this datepicker. + @private + @param {Element} elem The control to check. + @param {Date|string|number} date The date to check. + @return {boolean} true if selectable, false if not. + @example var selectable = $(selector).datepick('isSelectable', date) */ + isSelectable: function(elem, date) { + var inst = this._getInst(elem); + if ($.isEmptyObject(inst)) { + return false; + } + date = plugin.determineDate(date, inst.selectedDates[0] || this.today(), null, + inst.options.dateFormat, inst.getConfig()); + return this._isSelectable(elem, date, inst.options.onDate, + inst.get('minDate'), inst.get('maxDate')); + }, + + /** Internally determine whether a date is selectable for this datepicker. + @private + @param {Element} elem the control to check. + @param {Date} date The date to check. + @param {DatepickOnDate|boolean} onDate Any {@linkcode module:Datepick~defaultOptions|onDate} callback + or callback.selectable. + @param {Date} minDate The minimum allowed date. + @param {Date} maxDate The maximum allowed date. + @return {boolean} true if selectable, false if not. */ + _isSelectable: function(elem, date, onDate, minDate, maxDate) { + var dateInfo = (typeof onDate === 'boolean' ? {selectable: onDate} : + (!$.isFunction(onDate) ? {} : onDate.apply(elem, [date, true]))); + return (dateInfo.selectable !== false) && + (!minDate || date.getTime() >= minDate.getTime()) && + (!maxDate || date.getTime() <= maxDate.getTime()); + }, + + /** Perform a {@linkcode module:Datepick~commands|named action} for a datepicker. + @param {element} elem The control to affect. + @param {string} action The name of the action. + @example $(selector).datepick('performAction', 'prev') */ + performAction: function(elem, action) { + var inst = this._getInst(elem); + if (!$.isEmptyObject(inst) && !this.isDisabled(elem)) { + var commands = inst.options.commands; + if (commands[action] && commands[action].enabled.apply(elem, [inst])) { + commands[action].action.apply(elem, [inst]); + } + } + }, + + /** Set the currently shown month and day, defaulting to today. + @param {Element} elem The control to affect. + @param {number} [year] The year to show. + @param {number} [month] The month to show (1-12). + @param {number} [day] The day to show. + @example $(selector).datepick('showMonth', 2014, 12, 25) */ + showMonth: function(elem, year, month, day) { + var inst = this._getInst(elem); + if (!$.isEmptyObject(inst) && (typeof day !== 'undefined' || + (inst.drawDate.getFullYear() !== year || inst.drawDate.getMonth() + 1 !== month))) { + inst.prevDate = plugin.newDate(inst.drawDate); + var show = this._checkMinMax((typeof year !== 'undefined' ? + plugin.newDate(year, month, 1) : plugin.today()), inst); + inst.drawDate = plugin.newDate(show.getFullYear(), show.getMonth() + 1, + (typeof day !== 'undefined' ? day : Math.min(inst.drawDate.getDate(), + plugin.daysInMonth(show.getFullYear(), show.getMonth() + 1)))); + this._update(elem); + } + }, + + /** Adjust the currently shown month. + @param {Element} elem The control to affect. + @param {number} offset The number of months to change by. + @example $(selector).datepick('changeMonth', 2)*/ + changeMonth: function(elem, offset) { + var inst = this._getInst(elem); + if (!$.isEmptyObject(inst)) { + var date = plugin.add(plugin.newDate(inst.drawDate), offset, 'm'); + this.showMonth(elem, date.getFullYear(), date.getMonth() + 1); + } + }, + + /** Adjust the currently shown day. + @param {Element} elem The control to affect. + @param {number} offset The number of days to change by. + @example $(selector).datepick('changeDay', 7)*/ + changeDay: function(elem, offset) { + var inst = this._getInst(elem); + if (!$.isEmptyObject(inst)) { + var date = plugin.add(plugin.newDate(inst.drawDate), offset, 'd'); + this.showMonth(elem, date.getFullYear(), date.getMonth() + 1, date.getDate()); + } + }, + + /** Restrict a date to the minimum/maximum specified. + @private + @param {Date} date The date to check. + @param {object} inst The current instance settings. */ + _checkMinMax: function(date, inst) { + var minDate = inst.get('minDate'); + var maxDate = inst.get('maxDate'); + date = (minDate && date.getTime() < minDate.getTime() ? plugin.newDate(minDate) : date); + date = (maxDate && date.getTime() > maxDate.getTime() ? plugin.newDate(maxDate) : date); + return date; + }, + + /** Retrieve the date associated with an entry in the datepicker. + @param {Element} elem The control to examine. + @param {Element} target The selected datepicker element. + @return {Date} The corresponding date, or null. + @example var date = $(selector).datepick('retrieveDate', $('div.datepick-popup a:contains(10)')[0]) */ + retrieveDate: function(elem, target) { + var inst = this._getInst(elem); + return ($.isEmptyObject(inst) ? null : this._normaliseDate( + new Date(parseInt(target.className.replace(/^.*dp(-?\d+).*$/, '$1'), 10)))); + }, + + /** Select a date for this datepicker. + @param {Element} elem The control to examine. + @param {Element} target The selected datepicker element. + @example $(selector).datepick('selectDate', $('div.datepick-popup a:contains(10)')[0]) */ + selectDate: function(elem, target) { + var inst = this._getInst(elem); + if (!$.isEmptyObject(inst) && !this.isDisabled(elem)) { + var date = this.retrieveDate(elem, target); + if (inst.options.multiSelect) { + var found = false; + for (var i = 0; i < inst.selectedDates.length; i++) { + if (date.getTime() === inst.selectedDates[i].getTime()) { + inst.selectedDates.splice(i, 1); + found = true; + break; + } + } + if (!found && inst.selectedDates.length < inst.options.multiSelect) { + inst.selectedDates.push(date); + } + } + else if (inst.options.rangeSelect) { + if (inst.pickingRange) { + inst.selectedDates[1] = date; + } + else { + inst.selectedDates = [date, date]; + } + inst.pickingRange = !inst.pickingRange; + } + else { + inst.selectedDates = [date]; + } + inst.prevDate = inst.drawDate = plugin.newDate(date); + this._updateInput(elem); + if (inst.inline || inst.pickingRange || inst.selectedDates.length < + (inst.options.multiSelect || (inst.options.rangeSelect ? 2 : 1))) { + this._update(elem); + } + else { + this.hide(elem); + } + } + }, + + /** Generate the datepicker content for this control. + @private + @param {Element} elem The control to affect. + @param {object} inst The current instance settings. + @return {jQuery} The datepicker content */ + _generateContent: function(elem, inst) { + var monthsToShow = inst.options.monthsToShow; + monthsToShow = ($.isArray(monthsToShow) ? monthsToShow : [1, monthsToShow]); + inst.drawDate = this._checkMinMax( + inst.drawDate || inst.get('defaultDate') || plugin.today(), inst); + var drawDate = plugin._applyMonthsOffset(plugin.newDate(inst.drawDate), inst); + // Generate months + var monthRows = ''; + for (var row = 0; row < monthsToShow[0]; row++) { + var months = ''; + for (var col = 0; col < monthsToShow[1]; col++) { + months += this._generateMonth(elem, inst, drawDate.getFullYear(), + drawDate.getMonth() + 1, inst.options.renderer, (row === 0 && col === 0)); + plugin.add(drawDate, 1, 'm'); + } + monthRows += this._prepare(inst.options.renderer.monthRow, inst).replace(/\{months\}/, months); + } + var picker = this._prepare(inst.options.renderer.picker, inst).replace(/\{months\}/, monthRows). + replace(/\{weekHeader\}/g, this._generateDayHeaders(inst, inst.options.renderer)); + // Add commands + var addCommand = function(type, open, close, name, classes) { + if (picker.indexOf('{' + type + ':' + name + '}') === -1) { + return; + } + var command = inst.options.commands[name]; + var date = (inst.options.commandsAsDateFormat ? command.date.apply(elem, [inst]) : null); + picker = picker.replace(new RegExp('\\{' + type + ':' + name + '\\}', 'g'), + '<' + open + (command.status ? ' title="' + inst.options[command.status] + '"' : '') + + ' class="' + inst.options.renderer.commandClass + ' ' + + inst.options.renderer.commandClass + '-' + name + ' ' + classes + + (command.enabled(inst) ? '' : ' ' + inst.options.renderer.disabledClass) + '">' + + (date ? plugin.formatDate(inst.options[command.text], date, inst.getConfig()) : + inst.options[command.text]) + ''); + }; + for (var key in inst.options.commands) { + if (inst.options.commands.hasOwnProperty(key)) { + addCommand('button', 'button type="button"', 'button', key, + inst.options.renderer.commandButtonClass); + addCommand('link', 'a href="javascript:void(0)"', 'a', key, + inst.options.renderer.commandLinkClass); + } + } + picker = $(picker); + if (monthsToShow[1] > 1) { + var count = 0; + $(inst.options.renderer.monthSelector, picker).each(function() { + var nth = ++count % monthsToShow[1]; + $(this).addClass(nth === 1 ? 'first' : (nth === 0 ? 'last' : '')); + }); + } + // Add datepicker behaviour + var self = this; + function removeHighlight() { + /* jshint -W040 */ + (inst.inline ? $(this).closest('.' + self._getMarker()) : inst.div). + find(inst.options.renderer.daySelector + ' a'). + removeClass(inst.options.renderer.highlightedClass); + /* jshint +W040 */ + } + picker.find(inst.options.renderer.daySelector + ' a').hover( + function() { + removeHighlight.apply(this); + $(this).addClass(inst.options.renderer.highlightedClass); + }, + removeHighlight). + click(function() { + self.selectDate(elem, this); + }).end(). + find('select.' + this._monthYearClass + ':not(.' + this._anyYearClass + ')'). + change(function() { + var monthYear = $(this).val().split('/'); + self.showMonth(elem, parseInt(monthYear[1], 10), parseInt(monthYear[0], 10)); + }).end(). + find('select.' + this._anyYearClass).click(function() { + $(this).css('visibility', 'hidden'). + next('input').css({left: this.offsetLeft, top: this.offsetTop, + width: this.offsetWidth, height: this.offsetHeight}).show().focus(); + }).end(). + find('input.' + self._monthYearClass).change(function() { + try { + var year = parseInt($(this).val(), 10); + year = (isNaN(year) ? inst.drawDate.getFullYear() : year); + self.showMonth(elem, year, inst.drawDate.getMonth() + 1, inst.drawDate.getDate()); + } + catch (e) { + window.alert(e); + } + }).keydown(function(event) { + if (event.keyCode === 13) { // Enter + $(event.elem).change(); + } + else if (event.keyCode === 27) { // Escape + $(event.elem).hide().prev('select').css('visibility', 'visible'); + inst.elem.focus(); + } + }); + // Add keyboard handling + var data = {elem: inst.elem[0]}; + picker.keydown(data, this._keyDown).keypress(data, this._keyPress).keyup(data, this._keyUp); + // Add command behaviour + picker.find('.' + inst.options.renderer.commandClass).click(function() { + if (!$(this).hasClass(inst.options.renderer.disabledClass)) { + var action = this.className.replace( + new RegExp('^.*' + inst.options.renderer.commandClass + '-([^ ]+).*$'), '$1'); + plugin.performAction(elem, action); + } + }); + // Add classes + if (inst.options.isRTL) { + picker.addClass(inst.options.renderer.rtlClass); + } + if (monthsToShow[0] * monthsToShow[1] > 1) { + picker.addClass(inst.options.renderer.multiClass); + } + if (inst.options.pickerClass) { + picker.addClass(inst.options.pickerClass); + } + // Resize + $('body').append(picker); + var width = 0; + picker.find(inst.options.renderer.monthSelector).each(function() { + width += $(this).outerWidth(); + }); + picker.width(width / monthsToShow[0]); + // Pre-show customisation + if ($.isFunction(inst.options.onShow)) { + inst.options.onShow.apply(elem, [picker, inst]); + } + return picker; + }, + + /** Generate the content for a single month. + @private + @param {Element} elem The control to affect. + @param {object} inst The current instance settings. + @param {number} year The year to generate. + @param {number} month The month to generate. + @param {object} renderer The rendering templates. + @param {boolean} first true if first of multiple months. + @return {string} The month content. */ + _generateMonth: function(elem, inst, year, month, renderer, first) { + var daysInMonth = plugin.daysInMonth(year, month); + var monthsToShow = inst.options.monthsToShow; + monthsToShow = ($.isArray(monthsToShow) ? monthsToShow : [1, monthsToShow]); + var fixedWeeks = inst.options.fixedWeeks || (monthsToShow[0] * monthsToShow[1] > 1); + var firstDay = inst.options.firstDay; + var leadDays = (plugin.newDate(year, month, 1).getDay() - firstDay + 7) % 7; + var numWeeks = (fixedWeeks ? 6 : Math.ceil((leadDays + daysInMonth) / 7)); + var selectOtherMonths = inst.options.selectOtherMonths && inst.options.showOtherMonths; + var minDate = (inst.pickingRange ? inst.selectedDates[0] : inst.get('minDate')); + var maxDate = inst.get('maxDate'); + var showWeeks = renderer.week.indexOf('{weekOfYear}') > -1; + var today = plugin.today(); + var drawDate = plugin.newDate(year, month, 1); + plugin.add(drawDate, -leadDays - (fixedWeeks && (drawDate.getDay() === firstDay) ? 7 : 0), 'd'); + var ts = drawDate.getTime(); + // Generate weeks + var weeks = ''; + for (var week = 0; week < numWeeks; week++) { + var weekOfYear = (!showWeeks ? '' : '' + + ($.isFunction(inst.options.calculateWeek) ? inst.options.calculateWeek(drawDate) : 0) + ''); + var days = ''; + for (var day = 0; day < 7; day++) { + var selected = false; + if (inst.options.rangeSelect && inst.selectedDates.length > 0) { + selected = (drawDate.getTime() >= inst.selectedDates[0] && + drawDate.getTime() <= inst.selectedDates[1]); + } + else { + for (var i = 0; i < inst.selectedDates.length; i++) { + if (inst.selectedDates[i].getTime() === drawDate.getTime()) { + selected = true; + break; + } + } + } + var dateInfo = (!$.isFunction(inst.options.onDate) ? {} : + inst.options.onDate.apply(elem, [drawDate, drawDate.getMonth() + 1 === month])); + var selectable = (selectOtherMonths || drawDate.getMonth() + 1 === month) && + this._isSelectable(elem, drawDate, dateInfo.selectable, minDate, maxDate); + days += this._prepare(renderer.day, inst).replace(/\{day\}/g, + (selectable ? '' + + (inst.options.showOtherMonths || (drawDate.getMonth() + 1) === month ? + dateInfo.content || drawDate.getDate() : ' ') + + (selectable ? '' : '')); + plugin.add(drawDate, 1, 'd'); + ts = drawDate.getTime(); + } + weeks += this._prepare(renderer.week, inst).replace(/\{days\}/g, days). + replace(/\{weekOfYear\}/g, weekOfYear); + } + var monthHeader = this._prepare(renderer.month, inst).match(/\{monthHeader(:[^\}]+)?\}/); + monthHeader = (monthHeader[0].length <= 13 ? 'MM yyyy' : + monthHeader[0].substring(13, monthHeader[0].length - 1)); + monthHeader = (first ? this._generateMonthSelection( + inst, year, month, minDate, maxDate, monthHeader, renderer) : + plugin.formatDate(monthHeader, plugin.newDate(year, month, 1), inst.getConfig())); + var weekHeader = this._prepare(renderer.weekHeader, inst). + replace(/\{days\}/g, this._generateDayHeaders(inst, renderer)); + return this._prepare(renderer.month, inst).replace(/\{monthHeader(:[^\}]+)?\}/g, monthHeader). + replace(/\{weekHeader\}/g, weekHeader).replace(/\{weeks\}/g, weeks); + }, + + /** Generate the HTML for the day headers. + @private + @param {object} inst The current instance settings. + @param {object} renderer The rendering templates. + @return {string} A week's worth of day headers. */ + _generateDayHeaders: function(inst, renderer) { + var header = ''; + for (var day = 0; day < 7; day++) { + var dow = (day + inst.options.firstDay) % 7; + header += this._prepare(renderer.dayHeader, inst).replace(/\{day\}/g, + '' + inst.options.dayNamesMin[dow] + ''); + } + return header; + }, + + /** Generate selection controls for month. + @private + @param {object} inst The current instance settings. + @param {number} year The year to generate. + @param {number} month The month to generate. + @param {Date} minDate The minimum date allowed. + @param {Date} maxDate The maximum date allowed. + @param {string} monthHeader The month/year format. + @return {string} The month selection content. */ + _generateMonthSelection: function(inst, year, month, minDate, maxDate, monthHeader) { + if (!inst.options.changeMonth) { + return plugin.formatDate( + monthHeader, plugin.newDate(year, month, 1), inst.getConfig()); + } + // Months + var monthNames = inst.options['monthNames' + (monthHeader.match(/mm/i) ? '' : 'Short')]; + var html = monthHeader.replace(/m+/i, '\\x2E').replace(/y+/i, '\\x2F'); + var selector = ''; + html = html.replace(/\\x2E/, selector); + // Years + var yearRange = inst.options.yearRange; + if (yearRange === 'any') { + selector = '' + + ''; + } + else { + yearRange = yearRange.split(':'); + var todayYear = plugin.today().getFullYear(); + var start = (yearRange[0].match('c[+-].*') ? year + parseInt(yearRange[0].substring(1), 10) : + ((yearRange[0].match('[+-].*') ? todayYear : 0) + parseInt(yearRange[0], 10))); + var end = (yearRange[1].match('c[+-].*') ? year + parseInt(yearRange[1].substring(1), 10) : + ((yearRange[1].match('[+-].*') ? todayYear : 0) + parseInt(yearRange[1], 10))); + selector = ''; + } + html = html.replace(/\\x2F/, selector); + return html; + }, + + /** Prepare a render template for use. + Exclude popup/inline sections that are not applicable. + Localise text of the form: {l10n:name}. + @private + @param {string} text The text to localise. + @param {object} inst The current instance settings. + @return {string} The localised text. */ + _prepare: function(text, inst) { + var replaceSection = function(type, retain) { + while (true) { + var start = text.indexOf('{' + type + ':start}'); + if (start === -1) { + return; + } + var end = text.substring(start).indexOf('{' + type + ':end}'); + if (end > -1) { + text = text.substring(0, start) + + (retain ? text.substr(start + type.length + 8, end - type.length - 8) : '') + + text.substring(start + end + type.length + 6); + } + } + }; + replaceSection('inline', inst.inline); + replaceSection('popup', !inst.inline); + var pattern = /\{l10n:([^\}]+)\}/; + var matches = null; + while ((matches = pattern.exec(text))) { + text = text.replace(matches[0], inst.options[matches[1]]); + } + return text; + } + }); + + var plugin = $.datepick; // Singleton instance + + $(function() { + $(document).on('mousedown.' + pluginName, plugin._checkExternalClick). + on('resize.' + pluginName, function() { plugin.hide(plugin.curInst); }); + }); + +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick.lang.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick.lang.js new file mode 100755 index 000000000..8a64f6ed1 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick.lang.js @@ -0,0 +1,3283 @@ +/*! http://keith-wood.name/datepick.html + Datepicker localisations. */ +/* http://keith-wood.name/datepick.html + Afrikaans localisation for jQuery Datepicker. + Written by Renier Pretorius and Ruediger Thiede. */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.af = { + monthNames: ['Januarie','Februarie','Maart','April','Mei','Junie', + 'Julie','Augustus','September','Oktober','November','Desember'], + monthNamesShort: ['Jan','Feb','Mrt','Apr','Mei','Jun', + 'Jul','Aug','Sep','Okt','Nov','Des'], + dayNames: ['Sondag','Maandag','Dinsdag','Woensdag','Donderdag','Vrydag','Saterdag'], + dayNamesShort: ['Son','Maan','Dins','Woens','Don','Vry','Sat'], + dayNamesMin: ['So','Ma','Di','Wo','Do','Vr','Sa'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: 'Vorige', + prevStatus: 'Vertoon vorige maand', + prevJumpText: '<<', + prevJumpStatus: 'Vertoon vorige jaar', + nextText: 'Volgende', + nextStatus: 'Vertoon volgende maand', + nextJumpText: '>>', + nextJumpStatus: 'Vertoon volgende jaar', + currentText: 'Vandag', + currentStatus: 'Vertoon huidige maand', + todayText: 'Vandag', + todayStatus: 'Vertoon huidige maand', + clearText: 'Vee uit', + clearStatus: 'Verwyder die huidige datum', + closeText: 'Klaar', + closeStatus: 'Sluit sonder verandering', + yearStatus: 'Vertoon \'n ander jaar', + monthStatus: 'Vertoon \'n ander maand', + weekText: 'Wk', + weekStatus: 'Week van die jaar', + dayStatus: 'Kies DD, M d', + defaultStatus: 'Kies \'n datum', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.af); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Amharic (አማርኛ) localisation for jQuery datepicker. + Leyu Sisay. */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.am = { + monthNames: ['ጃንዋሪ','áˆá‰¥áˆ­á‹‹áˆª','ማርች','አá•ሪáˆ','ሜይ','áŒáŠ•', + 'áŒáˆ‹á‹­','ኦገስት','ሴá•ቴáˆá‰ áˆ­','ኦክቶበር','ኖቬáˆá‰ áˆ­','ዲሴáˆá‰ áˆ­'], + monthNamesShort: ['ጃንዋ','áˆá‰¥áˆ­','ማርች','አá•ሪ','ሜይ','áŒáŠ•', + 'áŒáˆ‹á‹­','ኦገስ','ሴá•ቴ','ኦክቶ','ኖቬáˆ','ዲሴáˆ'], + dayNames: ['ሰንዴይ','መንዴይ','ትዩስዴይ','ዌንስዴይ','ተርሰዴይ','áራይዴይ','ሳተርዴይ'], + dayNamesShort: ['ሰንዴ','መንዴ','ትዩስ','ዌንስ','ተርሰ','áራይ','ሳተር'], + dayNamesMin: ['ሰን','መን','ትዩ','ዌን','ተር','áራ','ሳተ'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: 'ያለáˆ', + prevStatus: 'ያለáˆá‹áŠ• ወር አሳይ', + prevJumpText: '<<', + prevJumpStatus: 'ያለáˆá‹áŠ• ዓመት አሳይ', + nextText: 'ቀጣይ', + nextStatus: 'ቀጣዩን ወር አሳይ', + nextJumpText: '>>', + nextJumpStatus: 'ቀጣዩን ዓመት አሳይ', + currentText: 'አáˆáŠ•', + currentStatus: 'የአáˆáŠ‘áŠ• ወር አሳይ', + todayText: 'ዛሬ', + todayStatus: 'የዛሬን ወር አሳይ', + clearText: 'አጥá‹', + clearStatus: 'የተመረጠá‹áŠ• ቀን አጥá‹', + closeText: 'á‹áŒ‹', + closeStatus: 'የቀን መáˆáˆ¨áŒ«á‹áŠ• á‹áŒ‹', + yearStatus: 'ዓመቱን ቀይር', + monthStatus: 'ወሩን ቀይር', + weekText: 'ሳáˆ', + weekStatus: 'የዓመቱ ሳáˆáŠ•á‰µ ', + dayStatus: 'DD, M d, yyyy áˆáˆ¨áŒ¥', + defaultStatus: 'ቀን áˆáˆ¨áŒ¥', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.am); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Algerian (and Tunisian) Arabic localisation for jQuery Datepicker. + Mohamed Cherif BOUCHELAGHEM -- cherifbouchelaghem@yahoo.fr */ +(function($) { + 'use strict'; + $.datepick.regionalOptions['ar-DZ'] = { + monthNames: ['جانÙÙŠ','ÙÙŠÙØ±ÙŠ','مارس','Ø£ÙØ±ÙŠÙ„','ماي','جوان', + 'جويلية','أوت','سبتمبر','أكتوبر','نوÙمبر','ديسمبر'], + monthNamesShort: ['1','2','3','4','5','6', + '7','8','9','10','11','12'], + dayNames: ['الأحد','الاثنين','الثلاثاء','الأربعاء','الخميس','الجمعة','السبت'], + dayNamesShort: ['الأحد','الاثنين','الثلاثاء','الأربعاء','الخميس','الجمعة','السبت'], + dayNamesMin: ['الأحد','الاثنين','الثلاثاء','الأربعاء','الخميس','الجمعة','السبت'], + dateFormat: 'dd/mm/yyyy', + firstDay: 6, + renderer: $.datepick.defaultRenderer, + prevText: '<السابق', + prevStatus: 'عرض الشهر السابق', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'التالي>', + nextStatus: 'عرض الشهر القادم', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'اليوم', + currentStatus: 'عرض الشهر الحالي', + todayText: 'اليوم', + todayStatus: 'عرض الشهر الحالي', + clearText: 'مسح', + clearStatus: 'امسح التاريخ الحالي', + closeText: 'إغلاق', + closeStatus: 'إغلاق بدون Ø­ÙØ¸', + yearStatus: 'عرض سنة آخرى', + monthStatus: 'عرض شهر آخر', + weekText: 'أسبوع', + weekStatus: 'أسبوع السنة', + dayStatus: 'اختر D, M d', + defaultStatus: 'اختر يوم', + isRTL: true + }; + $.datepick.setDefaults($.datepick.regionalOptions['ar-DZ']); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Arabic localisation for jQuery Datepicker. + Mahmoud Khaled -- mahmoud.khaled@badrit.com + NOTE: monthNames are the new months names */ +(function($) { + 'use strict'; + $.datepick.regionalOptions['ar-EG'] = { + monthNames: ['يناير','ÙØ¨Ø±Ø§ÙŠØ±','مارس','إبريل','مايو','يونية', + 'يوليو','أغسطس','سبتمبر','أكتوبر','نوÙمبر','ديسمبر'], + monthNamesShort: ['1','2','3','4','5','6', + '7','8','9','10','11','12'], + dayNames: ['الأحد','الاثنين','الثلاثاء','الأربعاء','الخميس','الجمعة','السبت'], + dayNamesShort: ['أحد','اثنين','ثلاثاء','أربعاء','خميس','جمعة','سبت'], + dayNamesMin: ['أحد','اثنين','ثلاثاء','أربعاء','خميس','جمعة','سبت'], + dateFormat: 'dd/mm/yyyy', + firstDay: 6, + renderer: $.datepick.defaultRenderer, + prevText: '<السابق', + prevStatus: 'عرض الشهر السابق', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'التالي>', + nextStatus: 'عرض الشهر القادم', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'اليوم', + currentStatus: 'عرض الشهر الحالي', + todayText: 'اليوم', + todayStatus: 'عرض الشهر الحالي', + clearText: 'مسح', + clearStatus: 'امسح التاريخ الحالي', + closeText: 'إغلاق', + closeStatus: 'إغلاق بدون Ø­ÙØ¸', + yearStatus: 'عرض سنة آخرى', + monthStatus: 'عرض شهر آخر', + weekText: 'أسبوع', + weekStatus: 'أسبوع السنة', + dayStatus: 'اختر D, M d', + defaultStatus: 'اختر يوم', + isRTL: true + }; + $.datepick.setDefaults($.datepick.regionalOptions['ar-EG']); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Arabic localisation for jQuery Datepicker. + Khaled Al Horani -- koko.dw@gmail.com + خالد الحوراني -- koko.dw@gmail.com + NOTE: monthNames are the original months names and they are the Arabic names, not the new months name ÙØ¨Ø±Ø§ÙŠØ± - يناير and there isn't any Arabic roots for these months */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.ar = { + monthNames: ['كانون الثاني','شباط','آذار','نيسان','آذار','حزيران', + 'تموز','آب','أيلول','تشرين الأول','تشرين الثاني','كانون الأول'], + monthNamesShort: ['1','2','3','4','5','6', + '7','8','9','10','11','12'], + dayNames: ['الأحد','الاثنين','الثلاثاء','الأربعاء','الخميس','الجمعة','السبت'], + dayNamesShort: ['الأحد','الاثنين','الثلاثاء','الأربعاء','الخميس','الجمعة','السبت'], + dayNamesMin: ['الأحد','الاثنين','الثلاثاء','الأربعاء','الخميس','الجمعة','السبت'], + dateFormat: 'dd/mm/yyyy', + firstDay: 6, + renderer: $.datepick.defaultRenderer, + prevText: '<السابق', + prevStatus: 'عرض الشهر السابق', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'التالي>', + nextStatus: 'عرض الشهر القادم', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'اليوم', + currentStatus: 'عرض الشهر الحالي', + todayText: 'اليوم', + todayStatus: 'عرض الشهر الحالي', + clearText: 'مسح', + clearStatus: 'امسح التاريخ الحالي', + closeText: 'إغلاق', + closeStatus: 'إغلاق بدون Ø­ÙØ¸', + yearStatus: 'عرض سنة آخرى', + monthStatus: 'عرض شهر آخر', + weekText: 'أسبوع', + weekStatus: 'أسبوع السنة', + dayStatus: 'اختر D, M d', + defaultStatus: 'اختر يوم', + isRTL: true + }; + $.datepick.setDefaults($.datepick.regionalOptions.ar); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Azerbaijani localisation for jQuery Datepicker. + Written by Jamil Najafov (necefov33@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.az = { + monthNames: ['Yanvar','Fevral','Mart','Aprel','May','İyun', + 'İyul','Avqust','Sentyabr','Oktyabr','Noyabr','Dekabr'], + monthNamesShort: ['Yan','Fev','Mar','Apr','May','İyun', + 'İyul','Avq','Sen','Okt','Noy','Dek'], + dayNames: ['Bazar','Bazar ertÉ™si','ÇərÅŸÉ™nbÉ™ axÅŸamı','ÇərÅŸÉ™nbÉ™','CümÉ™ axÅŸamı','CümÉ™','ŞənbÉ™'], + dayNamesShort: ['B','Be','Ça','Ç','Ca','C','Åž'], + dayNamesMin: ['B','B','Ç','С','Ç','C','Åž'], + dateFormat: 'dd.mm.yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<Geri', + prevStatus: 'ÆvvÉ™lki ay', + prevJumpText: '<<', + prevJumpStatus: 'ÆvvÉ™lki il', + nextText: 'İrÉ™li>', + nextStatus: 'Sonrakı ay', + nextJumpText: '>>', + nextJumpStatus: 'Sonrakı il', + currentText: 'Bugün', + currentStatus: 'İndiki ay', + todayText: 'Bugün', + todayStatus: 'İndiki ay', + clearText: 'TÉ™mizlÉ™', + clearStatus: 'Tarixi sil', + closeText: 'BaÄŸla', + closeStatus: 'TÉ™qvimi baÄŸla', + yearStatus: 'BaÅŸqa il', + monthStatus: 'BaÅŸqa ay', + weekText: 'Hf', + weekStatus: 'HÉ™ftÉ™lÉ™r', + dayStatus: 'D, M d seçin', + defaultStatus: 'Bir tarix seçin', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.az); +})(jQuery); +/* http://keith-wood.name/datepick.html + Bulgarian localisation for jQuery Datepicker. + Written by Stoyan Kyosev (http://svest.org). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.bg = { + monthNames: ['Януари','Февруари','Март','Ðприл','Май','Юни', + 'Юли','ÐвгуÑÑ‚','Септември','Октомври','Ðоември','Декември'], + monthNamesShort: ['Яну','Фев','Мар','Ðпр','Май','Юни', + 'Юли','Ðвг','Сеп','Окт','Ðов','Дек'], + dayNames: ['ÐеделÑ','Понеделник','Вторник','СрÑда','Четвъртък','Петък','Събота'], + dayNamesShort: ['Ðед','Пон','Вто','СрÑ','Чет','Пет','Съб'], + dayNamesMin: ['Ðе','По','Ð’Ñ‚','Ср','Че','Пе','Съ'], + dateFormat: 'dd.mm.yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<назад', + prevStatus: 'покажи поÑÐ»ÐµÐ´Ð½Ð¸Ñ Ð¼ÐµÑец', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'напред>', + nextStatus: 'покажи ÑÐ»ÐµÐ´Ð²Ð°Ñ‰Ð¸Ñ Ð¼ÐµÑец', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'днеÑ', + currentStatus: '', + todayText: 'днеÑ', + todayStatus: '', + clearText: 'изчиÑти', + clearStatus: 'изчиÑти актуалната дата', + closeText: 'затвори', + closeStatus: 'затвори без промени', + yearStatus: 'покажи друга година', + monthStatus: 'покажи друг меÑец', + weekText: 'Wk', + weekStatus: 'Ñедмица от меÑеца', + dayStatus: 'Избери D, M d', + defaultStatus: 'Избери дата', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.bg); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Bosnian localisation for jQuery Datepicker. + Written by Kenan Konjo. */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.bs = { + monthNames: ['Januar','Februar','Mart','April','Maj','Juni', + 'Juli','August','Septembar','Oktobar','Novembar','Decembar'], + monthNamesShort: ['Jan','Feb','Mar','Apr','Maj','Jun', + 'Jul','Aug','Sep','Okt','Nov','Dec'], + dayNames: ['Nedelja','Ponedeljak','Utorak','Srijeda','ÄŒetvrtak','Petak','Subota'], + dayNamesShort: ['Ned','Pon','Uto','Sri','ÄŒet','Pet','Sub'], + dayNamesMin: ['Ne','Po','Ut','Sr','ÄŒe','Pe','Su'], + dateFormat: 'dd.mm.yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: '>', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Danas', + currentStatus: '', + todayText: 'Danas', + todayStatus: '', + clearText: 'X', + clearStatus: '', + closeText: 'Zatvori', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Wk', + weekStatus: '', + dayStatus: 'DD d MM', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.bs); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Catalan localisation for jQuery Datepicker. + Writers: (joan.leon@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.ca = { + monthNames: ['Gener','Febrer','Març','Abril','Maig','Juny', + 'Juliol','Agost','Setembre','Octubre','Novembre','Desembre'], + monthNamesShort: ['Gen','Feb','Mar','Abr','Mai','Jun', + 'Jul','Ago','Set','Oct','Nov','Des'], + dayNames: ['Diumenge','Dilluns','Dimarts','Dimecres','Dijous','Divendres','Dissabte'], + dayNamesShort: ['Dug','Dln','Dmt','Dmc','Djs','Dvn','Dsb'], + dayNamesMin: ['Dg','Dl','Dt','Dc','Dj','Dv','Ds'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<Ant', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Seg>', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Avui', + currentStatus: '', + todayText: 'Avui', + todayStatus: '', + clearText: 'Netejar', + clearStatus: '', + closeText: 'Tancar', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Sm', + weekStatus: '', + dayStatus: 'D, M d', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.ca); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Czech localisation for jQuery Datepicker. + Written by Tomas Muller (tomas@tomas-muller.net). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.cs = { + monthNames: ['leden','únor','bÅ™ezen','duben','kvÄ›ten','Äerven', + 'Äervenec','srpen','září','říjen','listopad','prosinec'], + monthNamesShort: ['led','úno','bÅ™e','dub','kvÄ›','Äer', + 'Ävc','srp','zář','říj','lis','pro'], + dayNames: ['nedÄ›le','pondÄ›lí','úterý','stÅ™eda','Ätvrtek','pátek','sobota'], + dayNamesShort: ['ne','po','út','st','Ät','pá','so'], + dayNamesMin: ['ne','po','út','st','Ät','pá','so'], + dateFormat: 'dd.mm.yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<Dříve', + prevStatus: 'PÅ™ejít na pÅ™edchozí mÄ›sí', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'PozdÄ›ji>', + nextStatus: 'PÅ™ejít na další mÄ›síc', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Nyní', + currentStatus: 'PÅ™ejde na aktuální mÄ›síc', + todayText: 'Nyní', + todayStatus: 'PÅ™ejde na aktuální mÄ›síc', + clearText: 'Vymazat', + clearStatus: 'Vymaže zadané datum', + closeText: 'Zavřít', + closeStatus: 'ZavÅ™e kalendář beze zmÄ›ny', + yearStatus: 'PÅ™ejít na jiný rok', + monthStatus: 'PÅ™ejít na jiný mÄ›síc', + weekText: 'Týd', + weekStatus: 'Týden v roce', + dayStatus: '\'Vyber\' DD, M d', + defaultStatus: 'Vyberte datum', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.cs); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Danish localisation for jQuery Datepicker. + Written by Jan Christensen ( deletestuff@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.da = { + monthNames: ['Januar','Februar','Marts','April','Maj','Juni', + 'Juli','August','September','Oktober','November','December'], + monthNamesShort: ['Jan','Feb','Mar','Apr','Maj','Jun', + 'Jul','Aug','Sep','Okt','Nov','Dec'], + dayNames: ['Søndag','Mandag','Tirsdag','Onsdag','Torsdag','Fredag','Lørdag'], + dayNamesShort: ['Søn','Man','Tir','Ons','Tor','Fre','Lør'], + dayNamesMin: ['Sø','Ma','Ti','On','To','Fr','Lø'], + dateFormat: 'dd-mm-yyyy', + firstDay: 0, + renderer: $.datepick.defaultRenderer, + prevText: '<Forrige', + prevStatus: 'Vis forrige mÃ¥ned', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Næste>', + nextStatus: 'Vis næste mÃ¥ned', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Idag', + currentStatus: 'Vis aktuel mÃ¥ned', + todayText: 'Idag', + todayStatus: 'Vis aktuel mÃ¥ned', + clearText: 'Nulstil', + clearStatus: 'Nulstil den aktuelle dato', + closeText: 'Luk', + closeStatus: 'Luk uden ændringer', + yearStatus: 'Vis et andet Ã¥r', + monthStatus: 'Vis en anden mÃ¥ned', + weekText: 'Uge', + weekStatus: 'Ã…rets uge', + dayStatus: 'Vælg D, M d', + defaultStatus: 'Vælg en dato', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.da); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Swiss-German localisation for jQuery Datepicker. + Written by Douglas Jose & Juerg Meier. */ +(function($) { + 'use strict'; + $.datepick.regionalOptions['de-CH'] = { + monthNames: ['Januar','Februar','März','April','Mai','Juni', + 'Juli','August','September','Oktober','November','Dezember'], + monthNamesShort: ['Jan','Feb','Mär','Apr','Mai','Jun', + 'Jul','Aug','Sep','Okt','Nov','Dez'], + dayNames: ['Sonntag','Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag'], + dayNamesShort: ['So','Mo','Di','Mi','Do','Fr','Sa'], + dayNamesMin: ['So','Mo','Di','Mi','Do','Fr','Sa'], + dateFormat: 'dd.mm.yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<zurück', + prevStatus: 'letzten Monat zeigen', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'nächster>', + nextStatus: 'nächsten Monat zeigen', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'heute', + currentStatus: '', + todayText: 'heute', + todayStatus: '', + clearText: 'löschen', + clearStatus: 'aktuelles Datum löschen', + closeText: 'schliessen', + closeStatus: 'ohne Änderungen schliessen', + yearStatus: 'anderes Jahr anzeigen', + monthStatus: 'anderen Monat anzeigen', + weekText: 'Wo', + weekStatus: 'Woche des Monats', + dayStatus: 'Wähle D, M d', + defaultStatus: 'Wähle ein Datum', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions['de-CH']); +})(jQuery); + +/* http://keith-wood.name/datepick.html + German localisation for jQuery Datepicker. + Written by Milian Wolff (mail@milianw.de). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.de = { + monthNames: ['Januar','Februar','März','April','Mai','Juni', + 'Juli','August','September','Oktober','November','Dezember'], + monthNamesShort: ['Jan','Feb','Mär','Apr','Mai','Jun', + 'Jul','Aug','Sep','Okt','Nov','Dez'], + dayNames: ['Sonntag','Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag'], + dayNamesShort: ['So','Mo','Di','Mi','Do','Fr','Sa'], + dayNamesMin: ['So','Mo','Di','Mi','Do','Fr','Sa'], + dateFormat: 'dd.mm.yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<zurück', + prevStatus: 'letzten Monat zeigen', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Vor>', + nextStatus: 'nächsten Monat zeigen', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'heute', + currentStatus: '', + todayText: 'heute', + todayStatus: '', + clearText: 'löschen', + clearStatus: 'aktuelles Datum löschen', + closeText: 'schließen', + closeStatus: 'ohne Änderungen schließen', + yearStatus: 'anderes Jahr anzeigen', + monthStatus: 'anderen Monat anzeigen', + weekText: 'Wo', + weekStatus: 'Woche des Monats', + dayStatus: 'Wähle D, M d', + defaultStatus: 'Wähle ein Datum', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.de); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Greek localisation for jQuery Datepicker. + Written by Alex Cicovic (http://www.alexcicovic.com) */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.el = { + monthNames: ['ΙανουάÏιος','ΦεβÏουάÏιος','ΜάÏτιος','ΑπÏίλιος','Μάιος','ΙοÏνιος', + 'ΙοÏλιος','ΑÏγουστος','ΣεπτέμβÏιος','ΟκτώβÏιος','ÎοέμβÏιος','ΔεκέμβÏιος'], + monthNamesShort: ['Ιαν','Φεβ','ΜαÏ','ΑπÏ','Μαι','Ιουν', + 'Ιουλ','Αυγ','Σεπ','Οκτ','Îοε','Δεκ'], + dayNames: ['ΚυÏιακή','ΔευτέÏα','ΤÏίτη','ΤετάÏτη','Πέμπτη','ΠαÏασκευή','Σάββατο'], + dayNamesShort: ['ΚυÏ','Δευ','ΤÏι','Τετ','Πεμ','ΠαÏ','Σαβ'], + dayNamesMin: ['Κυ','Δε','ΤÏ','Τε','Πε','Πα','Σα'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: 'ΠÏοηγοÏμενος', + prevStatus: 'Επισκόπηση Ï€ÏοηγοÏμενου μήνα', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Επόμενος', + nextStatus: 'Επισκόπηση επόμενου μήνα', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'ΤÏέχων Μήνας', + currentStatus: 'Επισκόπηση Ï„Ïέχοντος μήνα', + todayText: 'ΤÏέχων Μήνας', + todayStatus: 'Επισκόπηση Ï„Ïέχοντος μήνα', + clearText: 'Σβήσιμο', + clearStatus: 'Σβήσιμο της επιλεγμένης ημεÏομηνίας', + closeText: 'Κλείσιμο', + closeStatus: 'Κλείσιμο χωÏίς αλλαγή', + yearStatus: 'Επισκόπηση άλλου έτους', + monthStatus: 'Επισκόπηση άλλου μήνα', + weekText: 'Εβδ', + weekStatus: '', + dayStatus: 'Επιλογή DD d MM', + defaultStatus: 'Επιλέξτε μια ημεÏομηνία', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.el); +})(jQuery); + +/* http://keith-wood.name/datepick.html + English/Australia localisation for jQuery Datepicker. + Based on en-GB. */ +(function($) { + 'use strict'; + $.datepick.regionalOptions['en-AU'] = { + monthNames: ['January','February','March','April','May','June', + 'July','August','September','October','November','December'], + monthNamesShort: ['Jan','Feb','Mar','Apr','May','Jun', + 'Jul','Aug','Sep','Oct','Nov','Dec'], + dayNames: ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'], + dayNamesShort: ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'], + dayNamesMin: ['Su','Mo','Tu','We','Th','Fr','Sa'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: 'Prev', + prevStatus: 'Show the previous month', + prevJumpText: '<<', + prevJumpStatus: 'Show the previous year', + nextText: 'Next', + nextStatus: 'Show the next month', + nextJumpText: '>>', + nextJumpStatus: 'Show the next year', + currentText: 'Current', + currentStatus: 'Show the current month', + todayText: 'Today', + todayStatus: 'Show today\'s month', + clearText: 'Clear', + clearStatus: 'Erase the current date', + closeText: 'Done', + closeStatus: 'Close without change', + yearStatus: 'Show a different year', + monthStatus: 'Show a different month', + weekText: 'Wk', + weekStatus: 'Week of the year', + dayStatus: 'Select DD, M d', + defaultStatus: 'Select a date', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions['en-AU']); +})(jQuery); + +/* http://keith-wood.name/datepick.html + English UK localisation for jQuery Datepicker. + Written by Stuart. */ +(function($) { + 'use strict'; + $.datepick.regionalOptions['en-GB'] = { + monthNames: ['January','February','March','April','May','June', + 'July','August','September','October','November','December'], + monthNamesShort: ['Jan','Feb','Mar','Apr','May','Jun', + 'Jul','Aug','Sep','Oct','Nov','Dec'], + dayNames: ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'], + dayNamesShort: ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'], + dayNamesMin: ['Su','Mo','Tu','We','Th','Fr','Sa'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: 'Prev', + prevStatus: 'Show the previous month', + prevJumpText: '<<', + prevJumpStatus: 'Show the previous year', + nextText: 'Next', + nextStatus: 'Show the next month', + nextJumpText: '>>', + nextJumpStatus: 'Show the next year', + currentText: 'Current', + currentStatus: 'Show the current month', + todayText: 'Today', + todayStatus: 'Show today\'s month', + clearText: 'Clear', + clearStatus: 'Erase the current date', + closeText: 'Done', + closeStatus: 'Close without change', + yearStatus: 'Show a different year', + monthStatus: 'Show a different month', + weekText: 'Wk', + weekStatus: 'Week of the year', + dayStatus: 'Select DD, M d', + defaultStatus: 'Select a date', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions['en-GB']); +})(jQuery); + +/* http://keith-wood.name/datepick.html + English/New Zealand localisation for jQuery Datepicker. + Based on en-GB. */ +(function($) { + 'use strict'; + $.datepick.regionalOptions['en-NZ'] = { + monthNames: ['January','February','March','April','May','June', + 'July','August','September','October','November','December'], + monthNamesShort: ['Jan','Feb','Mar','Apr','May','Jun', + 'Jul','Aug','Sep','Oct','Nov','Dec'], + dayNames: ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'], + dayNamesShort: ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'], + dayNamesMin: ['Su','Mo','Tu','We','Th','Fr','Sa'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: 'Prev', + prevStatus: 'Show the previous month', + prevJumpText: '<<', + prevJumpStatus: 'Show the previous year', + nextText: 'Next', + nextStatus: 'Show the next month', + nextJumpText: '>>', + nextJumpStatus: 'Show the next year', + currentText: 'Current', + currentStatus: 'Show the current month', + todayText: 'Today', + todayStatus: 'Show today\'s month', + clearText: 'Clear', + clearStatus: 'Erase the current date', + closeText: 'Done', + closeStatus: 'Close without change', + yearStatus: 'Show a different year', + monthStatus: 'Show a different month', + weekText: 'Wk', + weekStatus: 'Week of the year', + dayStatus: 'Select DD, M d', + defaultStatus: 'Select a date', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions['en-NZ']); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Esperanto localisation for jQuery Datepicker. + Written by Olivier M. (olivierweb@ifrance.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.eo = { + monthNames: ['Januaro','Februaro','Marto','Aprilo','Majo','Junio', + 'Julio','AÅ­gusto','Septembro','Oktobro','Novembro','Decembro'], + monthNamesShort: ['Jan','Feb','Mar','Apr','Maj','Jun', + 'Jul','AÅ­g','Sep','Okt','Nov','Dec'], + dayNames: ['Dimanĉo','Lundo','Mardo','Merkredo','Ä´aÅ­do','Vendredo','Sabato'], + dayNamesShort: ['Dim','Lun','Mar','Mer','Ä´aÅ­','Ven','Sab'], + dayNamesMin: ['Di','Lu','Ma','Me','Ä´a','Ve','Sa'], + dateFormat: 'dd/mm/yyyy', + firstDay: 0, + renderer: $.datepick.defaultRenderer, + prevText: '<Anta', + prevStatus: 'Vidi la antaÅ­an monaton', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Sekv>', + nextStatus: 'Vidi la sekvan monaton', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Nuna', + currentStatus: 'Vidi la nunan monaton', + todayText: 'Nuna', + todayStatus: 'Vidi la nunan monaton', + clearText: 'Vakigi', + clearStatus: '', + closeText: 'Fermi', + closeStatus: 'Fermi sen modifi', + yearStatus: 'Vidi alian jaron', + monthStatus: 'Vidi alian monaton', + weekText: 'Sb', + weekStatus: '', + dayStatus: 'Elekti DD, MM d', + defaultStatus: 'Elekti la daton', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.eo); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Spanish/Argentina localisation for jQuery Datepicker. + Written by Esteban Acosta Villafane (esteban.acosta@globant.com) of Globant (http://www.globant.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions['es-AR'] = { + monthNames: ['Enero','Febrero','Marzo','Abril','Mayo','Junio', + 'Julio','Agosto','Septiembre','Octubre','Noviembre','Diciembre'], + monthNamesShort: ['Ene','Feb','Mar','Abr','May','Jun', + 'Jul','Ago','Sep','Oct','Nov','Dic'], + dayNames: ['Domingo','Lunes','Martes','Miércoles','Jueves','Viernes','Sábado'], + dayNamesShort: ['Dom','Lun','Mar','Mié','Juv','Vie','Sáb'], + dayNamesMin: ['Do','Lu','Ma','Mi','Ju','Vi','Sá'], + dateFormat: 'dd/mm/yyyy', + firstDay: 0, + renderer: $.datepick.defaultRenderer, + prevText: '<Ant', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Sig>', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Hoy', + currentStatus: '', + todayText: 'Hoy', + todayStatus: '', + clearText: 'Limpiar', + clearStatus: '', + closeText: 'Cerrar', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Sm', + weekStatus: '', + dayStatus: 'D, M d', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions['es-AR']); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Spanish/Perú localisation for jQuery Datepicker. + Written by Fischer Tirado (fishdev@globant.com) of ASIX (http://www.asixonline.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions['es-PE'] = { + monthNames: ['Enero','Febrero','Marzo','Abril','Mayo','Junio', + 'Julio','Agosto','Septiembre','Octubre','Noviembre','Diciembre'], + monthNamesShort: ['Ene','Feb','Mar','Abr','May','Jun', + 'Jul','Ago','Sep','Oct','Nov','Dic'], + dayNames: ['Domingo','Lunes','Martes','Miércoles','Jueves','Viernes','Sábado'], + dayNamesShort: ['Dom','Lun','Mar','Mié','Jue','Vie','Sab'], + dayNamesMin: ['Do','Lu','Ma','Mi','Ju','Vi','Sa'], + dateFormat: 'dd/mm/yyyy', + firstDay: 0, + renderer: $.datepick.defaultRenderer, + prevText: '<Ant', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Sig>', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Hoy', + currentStatus: '', + todayText: 'Hoy', + todayStatus: '', + clearText: 'Limpiar', + clearStatus: '', + closeText: 'Cerrar', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Sm', + weekStatus: '', + dayStatus: 'DD d, MM yyyy', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions['es-PE']); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Spanish localisation for jQuery Datepicker. + Traducido por Vester (xvester@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.es = { + monthNames: ['Enero','Febrero','Marzo','Abril','Mayo','Junio', + 'Julio','Agosto','Septiembre','Octubre','Noviembre','Diciembre'], + monthNamesShort: ['Ene','Feb','Mar','Abr','May','Jun', + 'Jul','Ago','Sep','Oct','Nov','Dic'], + dayNames: ['Domingo','Lunes','Martes','Miércoles','Jueves','Viernes','Sábado'], + dayNamesShort: ['Dom','Lun','Mar','Mié','Juv','Vie','Sáb'], + dayNamesMin: ['Do','Lu','Ma','Mi','Ju','Vi','Sá'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<Ant', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Sig>', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Hoy', + currentStatus: '', + todayText: 'Hoy', + todayStatus: '', + clearText: 'Limpiar', + clearStatus: '', + closeText: 'Cerrar', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Sm', + weekStatus: '', + dayStatus: 'D, M d', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.es); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Estonian localisation for jQuery Datepicker. + Written by Mart Sõmermaa (mrts.pydev at gmail com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.et = { + monthNames: ['Jaanuar','Veebruar','Märts','Aprill','Mai','Juuni', + 'Juuli','August','September','Oktoober','November','Detsember'], + monthNamesShort: ['Jaan','Veebr','Märts','Apr','Mai','Juuni', + 'Juuli','Aug','Sept','Okt','Nov','Dets'], + dayNames: ['Pühapäev','Esmaspäev','Teisipäev','Kolmapäev','Neljapäev','Reede','Laupäev'], + dayNamesShort: ['Pühap','Esmasp','Teisip','Kolmap','Neljap','Reede','Laup'], + dayNamesMin: ['P','E','T','K','N','R','L'], + dateFormat: 'dd.mm.yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: 'Eelnev', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Järgnev', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Täna', + currentStatus: '', + todayText: 'Täna', + todayStatus: '', + clearText: '', + clearStatus: '', + closeText: 'Sulge', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Sm', + weekStatus: '', + dayStatus: '', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.et); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Basque localisation for jQuery Datepicker. + Karrikas-ek itzulia (karrikas@karrikas.com) */ +(function($){ + 'use strict'; + $.datepick.regionalOptions.eu = { + monthNames: ['Urtarrila','Otsaila','Martxoa','Apirila','Maiatza','Ekaina', + 'Uztaila','Abuztua','Iraila','Urria','Azaroa','Abendua'], + monthNamesShort: ['Urt','Ots','Mar','Api','Mai','Eka', + 'Uzt','Abu','Ira','Urr','Aza','Abe'], + dayNames: ['Igandea','Astelehena','Asteartea','Asteazkena','Osteguna','Ostirala','Larunbata'], + dayNamesShort: ['Iga','Ast','Ast','Ast','Ost','Ost','Lar'], + dayNamesMin: ['Ig','As','As','As','Os','Os','La'], + dateFormat: 'yyyy/mm/dd', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<Aur', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Hur>', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Gaur', + currentStatus: '', + todayText: 'Gaur', + todayStatus: '', + clearText: 'X', + clearStatus: '', + closeText: 'Egina', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Wk', + weekStatus: '', + dayStatus: 'DD d MM', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.eu); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Persian (Farsi) localisation for jQuery Datepicker. + Javad Mowlanezhad -- jmowla@gmail.com */ +(function($) { + 'use strict'; + /* jshint -W100 */ + $.datepick.regionalOptions.fa = { + monthNames: ['ÙØ±ÙˆØ±Ø¯ÙŠÙ†','ارديبهشت','خرداد','تير','مرداد','شهريور', + 'مهر','آبان','آذر','دي','بهمن','اسÙند'], + monthNamesShort: ['1','2','3','4','5','6', + '7','8','9','10','11','12'], + dayNames: ['يکشنبه','دوشنبه','سه‌شنبه','چهارشنبه','پنجشنبه','جمعه','شنبه'], + dayNamesShort: ['ÙŠ','د','س','Ú†','Ù¾','ج','Ø´'], + dayNamesMin: ['ÙŠ','د','س','Ú†','Ù¾','ج','Ø´'], + dateFormat: 'yyyy/mm/dd', + firstDay: 6, + renderer: $.datepick.defaultRenderer, + prevText: '<قبلي', + prevStatus: 'نمايش ماه قبل', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'بعدي>', + nextStatus: 'نمايش ماه بعد', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'امروز', + currentStatus: 'نمايش ماه جاري', + todayText: 'امروز', + todayStatus: 'نمايش ماه جاري', + clearText: 'حذ٠تاريخ', + clearStatus: 'پاک کردن تاريخ جاري', + closeText: 'بستن', + closeStatus: 'بستن بدون اعمال تغييرات', + yearStatus: 'نمايش سال Ù…ØªÙØ§ÙˆØª', + monthStatus: 'نمايش ماه Ù…ØªÙØ§ÙˆØª', + weekText: 'Ù‡Ù', + weekStatus: 'Ù‡ÙØªÙ‡Ù سال', + dayStatus: 'انتخاب D, M d', + defaultStatus: 'انتخاب تاريخ', + isRTL: true + }; + $.datepick.setDefaults($.datepick.regionalOptions.fa); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Finnish localisation for jQuery Datepicker. + Written by Harri Kilpiö (harrikilpio@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.fi = { + monthNames: ['Tammikuu','Helmikuu','Maaliskuu','Huhtikuu','Toukokuu','Kesäkuu', + 'Heinäkuu','Elokuu','Syyskuu','Lokakuu','Marraskuu','Joulukuu'], + monthNamesShort: ['Tammi','Helmi','Maalis','Huhti','Touko','Kesä', + 'Heinä','Elo','Syys','Loka','Marras','Joulu'], + dayNamesShort: ['Su','Ma','Ti','Ke','To','Pe','Su'], + dayNames: ['Sunnuntai','Maanantai','Tiistai','Keskiviikko','Torstai','Perjantai','Lauantai'], + dayNamesMin: ['Su','Ma','Ti','Ke','To','Pe','La'], + dateFormat: 'dd.mm.yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '«Edellinen', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Seuraava»', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Tänään', + currentStatus: '', + todayText: 'Tänään', + todayStatus: '', + clearText: 'Tyhjennä', + clearStatus: '', + closeText: 'Sulje', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Vk', + weekStatus: '', + dayStatus: 'D, M d', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.fi); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Faroese localisation for jQuery Datepicker. + Written by Sverri Mohr Olsen, sverrimo@gmail.com */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.fo = { + monthNames: ['Januar','Februar','Mars','Apríl','Mei','Juni', + 'Juli','August','September','Oktober','November','Desember'], + monthNamesShort: ['Jan','Feb','Mar','Apr','Mei','Jun', + 'Jul','Aug','Sep','Okt','Nov','Des'], + dayNames: ['Sunnudagur','Mánadagur','Týsdagur','Mikudagur','Hósdagur','Fríggjadagur','Leyardagur'], + dayNamesShort: ['Sun','Mán','Týs','Mik','Hós','Frí','Ley'], + dayNamesMin: ['Su','Má','Tý','Mi','Hó','Fr','Le'], + dateFormat: 'dd-mm-yyyy', + firstDay: 0, + renderer: $.datepick.defaultRenderer, + prevText: '<Sísta', + prevStatus: 'Vís sísta mánaðan', + prevJumpText: '<<', + prevJumpStatus: 'Vís sísta árið', + nextText: 'Næsta>', + nextStatus: 'Vís næsta mánaðan', + nextJumpText: '>>', + nextJumpStatus: 'Vís næsta árið', + currentText: 'à dag', + currentStatus: 'Vís mánaðan fyri í dag', + todayText: 'à dag', + todayStatus: 'Vís mánaðan fyri í dag', + clearText: 'Strika', + clearStatus: 'Strika allir mánaðarnar', + closeText: 'Goym', + closeStatus: 'Goym hetta vindeyðga', + yearStatus: 'Broyt árið', + monthStatus: 'Broyt mánaðan', + weekText: 'Vk', + weekStatus: 'Vika av árinum', + dayStatus: 'Vel DD, M d, yyyy', + defaultStatus: 'Vel ein dato', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.fo); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Swiss French localisation for jQuery Datepicker. + Written by Martin Voelkle (martin.voelkle@e-tc.ch). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions['fr-CH'] = { + monthNames: ['Janvier','Février','Mars','Avril','Mai','Juin', + 'Juillet','Août','Septembre','Octobre','Novembre','Décembre'], + monthNamesShort: ['Jan','Fév','Mar','Avr','Mai','Jun', + 'Jul','Aoû','Sep','Oct','Nov','Déc'], + dayNames: ['Dimanche','Lundi','Mardi','Mercredi','Jeudi','Vendredi','Samedi'], + dayNamesShort: ['Dim','Lun','Mar','Mer','Jeu','Ven','Sam'], + dayNamesMin: ['Di','Lu','Ma','Me','Je','Ve','Sa'], + dateFormat: 'dd.mm.yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<Préc', + prevStatus: 'Voir le mois précédent', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Suiv>', + nextStatus: 'Voir le mois suivant', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Courant', + currentStatus: 'Voir le mois courant', + todayText: 'Aujourd\'hui', + todayStatus: 'Voir aujourd\'hui', + clearText: 'Effacer', + clearStatus: 'Effacer la date sélectionnée', + closeText: 'Fermer', + closeStatus: 'Fermer sans modifier', + yearStatus: 'Voir une autre année', + monthStatus: 'Voir un autre mois', + weekText: 'Sm', + weekStatus: '', + dayStatus: '\'Choisir\' le DD d MM', + defaultStatus: 'Choisir la date', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions['fr-CH']); +})(jQuery); + +/* http://keith-wood.name/datepick.html + French localisation for jQuery Datepicker. + Stéphane Nahmani (sholby@sholby.net). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.fr = { + monthNames: ['Janvier','Février','Mars','Avril','Mai','Juin', + 'Juillet','Août','Septembre','Octobre','Novembre','Décembre'], + monthNamesShort: ['Jan','Fév','Mar','Avr','Mai','Jun', + 'Jul','Aoû','Sep','Oct','Nov','Déc'], + dayNames: ['Dimanche','Lundi','Mardi','Mercredi','Jeudi','Vendredi','Samedi'], + dayNamesShort: ['Dim','Lun','Mar','Mer','Jeu','Ven','Sam'], + dayNamesMin: ['Di','Lu','Ma','Me','Je','Ve','Sa'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<Préc', + prevStatus: 'Voir le mois précédent', + prevJumpText: '<<', + prevJumpStatus: 'Voir l\'année précédent', + nextText: 'Suiv>', + nextStatus: 'Voir le mois suivant', + nextJumpText: '>>', + nextJumpStatus: 'Voir l\'année suivant', + currentText: 'Courant', + currentStatus: 'Voir le mois courant', + todayText: 'Aujourd\'hui', + todayStatus: 'Voir aujourd\'hui', + clearText: 'Effacer', + clearStatus: 'Effacer la date sélectionnée', + closeText: 'Fermer', + closeStatus: 'Fermer sans modifier', + yearStatus: 'Voir une autre année', + monthStatus: 'Voir un autre mois', + weekText: 'Sm', + weekStatus: 'Semaine de l\'année', + dayStatus: '\'Choisir\' le DD d MM', + defaultStatus: 'Choisir la date', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.fr); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Galician localisation for jQuery Datepicker. + Traducido por Manuel (McNuel@gmx.net). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.gl = { + monthNames: ['Xaneiro','Febreiro','Marzo','Abril','Maio','Xuño', + 'Xullo','Agosto','Setembro','Outubro','Novembro','Decembro'], + monthNamesShort: ['Xan','Feb','Mar','Abr','Mai','Xuñ', + 'Xul','Ago','Set','Out','Nov','Dec'], + dayNames: ['Domingo','Luns','Martes','Mércores','Xoves','Venres','Sábado'], + dayNamesShort: ['Dom','Lun','Mar','Mér','Xov','Ven','Sáb'], + dayNamesMin: ['Do','Lu','Ma','Me','Xo','Ve','Sá'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<Ant', + prevStatus: 'Amosar mes anterior', + prevJumpText: '<<', + prevJumpStatus: 'Amosar ano anterior', + nextText: 'Seg>', + nextStatus: 'Amosar mes seguinte', + nextJumpText: '>>', + nextJumpStatus: 'Amosar ano seguinte', + currentText: 'Hoxe', + currentStatus: 'Amosar mes actual', + todayText: 'Hoxe', + todayStatus: 'Amosar mes actual', + clearText: 'Limpar', + clearStatus: 'Borrar data actual', + closeText: 'Pechar', + closeStatus: 'Pechar sen gardar', + yearStatus: 'Amosar outro ano', + monthStatus: 'Amosar outro mes', + weekText: 'Sm', + weekStatus: 'Semana do ano', + dayStatus: 'D, M d', + defaultStatus: 'Selecciona Data', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.gl); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Gujarati (ગà«àªœàª°àª¾àª¤à«€) localisation for jQuery Datepicker. + Naymesh Mistry (naymesh@yahoo.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.gu = { + monthNames: ['જાનà«àª¯à«àª†àª°à«€','ફેબà«àª°à«àª†àª°à«€','મારà«àªš','àªàªªà«àª°àª¿àª²','મે','જૂન', + 'જà«àª²àª¾àªˆ','ઑગસà«àªŸ','સપà«àªŸà«‡àª®à«àª¬àª°','ઑકà«àªŸà«‹àª¬àª°','નવેમà«àª¬àª°','ડિસેમà«àª¬àª°'], + monthNamesShort: ['જાનà«àª¯à«','ફેબà«àª°à«','મારà«àªš','àªàªªà«àª°àª¿àª²','મે','જૂન', + 'જà«àª²àª¾àªˆ','ઑગસà«àªŸ','સપà«àªŸà«‡','ઑકà«àªŸà«‹','નવે','ડિસે'], + dayNames: ['રવિવાર','સોમવાર','મંગળવાર','બà«àª§àªµàª¾àª°','ગà«àª°à«àªµàª¾àª°','શà«àª•à«àª°àªµàª¾àª°','શનિવાર'], + dayNamesShort: ['રવિ','સોમ','મંગળ','બà«àª§','ગà«àª°à«','શà«àª•à«àª°','શનિ'], + dayNamesMin: ['ર','સો','મં','બà«','ગà«','શà«','શ'], + dateFormat: 'dd-M-yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<પાછળ', + prevStatus: 'પાછલો મહિનો બતાવો', + prevJumpText: '<<', + prevJumpStatus: 'પાછળ', + nextText: 'આગળ>', + nextStatus: 'આગલો મહિનો બતાવો', + nextJumpText: '>>', + nextJumpStatus: 'આગળ', + currentText: 'આજે', + currentStatus: 'આજનો દિવસ બતાવો', + todayText: 'આજે', + todayStatus: 'આજનો દિવસ', + clearText: 'ભૂંસો', + clearStatus: 'હાલ પસંદ કરેલી તારીખ ભૂંસો', + closeText: 'બંધ કરો', + closeStatus: 'તારીખ પસંદ કરà«àª¯àª¾ વગર બંધ કરો', + yearStatus: 'જà«àª¦à« વરà«àª· બતાવો', + monthStatus: 'જà«àª¦à«‹ મહિનો બતાવો', + weekText: 'અઠવાડિયà«àª‚', + weekStatus: 'અઠવાડિયà«àª‚', + dayStatus: 'અઠવાડિયાનો પહેલો દિવસ પસંદ કરો', + defaultStatus: 'તારીખ પસંદ કરો', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.gu); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Hebrew localisation for jQuery Datepicker. + Written by Amir Hardon (ahardon at gmail dot com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.he = { + monthNames: ['ינו×ר','פברו×ר','מרץ','×פריל','מ××™','יוני', + 'יולי','×וגוסט','ספטמבר','×וקטובר','נובמבר','דצמבר'], + monthNamesShort: ['1','2','3','4','5','6', + '7','8','9','10','11','12'], + dayNames: ['ר×שון','שני','שלישי','רביעי','חמישי','שישי','שבת'], + dayNamesShort: ['×\'','ב\'','×’\'','ד\'','×”\'','ו\'','שבת'], + dayNamesMin: ['×\'','ב\'','×’\'','ד\'','×”\'','ו\'','שבת'], + dateFormat: 'dd/mm/yyyy', + firstDay: 0, + renderer: $.datepick.defaultRenderer, + prevText: '<הקוד×', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'הב×>', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'היו×', + currentStatus: '', + todayText: 'היו×', + todayStatus: '', + clearText: '× ×§×”', + clearStatus: '', + closeText: 'סגור', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Wk', + weekStatus: '', + dayStatus: 'DD, M d', + defaultStatus: '', + isRTL: true + }; + $.datepick.setDefaults($.datepick.regionalOptions.he); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Hindi INDIA localisation for jQuery Datepicker. + Written by Pawan Kumar Singh. */ +(function($) { + 'use strict'; + $.datepick.regionalOptions['hi-IN'] = { + monthNames: ['जनवरी',' फरवरी','मारà¥à¤š','अपà¥à¤°à¥ˆà¤²','मई','जून', + 'जà¥à¤²à¤¾à¤ˆ','अगसà¥à¤¤','सितमà¥à¤¬à¤°','अकà¥à¤Ÿà¥‚बर','नवमà¥à¤¬à¤°','दिसमà¥à¤¬à¤°'], + monthNamesShort: ['जन','फर','मारà¥à¤š','अपà¥à¤°à¥ˆ','मई','जून', + 'जà¥à¤²à¤¾à¤ˆ','अग','सित','अकà¥à¤Ÿà¥‚','नव','दिस'], + dayNames: ['रविवार','सोमवार','मंगलवार','बà¥à¤§à¤µà¤¾à¤°','गà¥à¤°à¥à¤µà¤¾à¤°','शà¥à¤•à¥à¤°à¤µà¤¾à¤°','शनिवार'], + dayNamesShort: ['रवि','सोम','मंगल','बà¥à¤§','गà¥à¤°à¥','शà¥à¤•à¥à¤°','शनि'], + dayNamesMin: ['र','सो','मं','बà¥','गà¥','शà¥','श'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: 'पिछला', + prevStatus: 'पिछला महीना देखें', + prevJumpText: '<<', + prevJumpStatus: 'पिछला वरà¥à¤· देखें', + nextText: 'अगला', + nextStatus: 'अगला महीना देखें', + nextJumpText: '>>', + nextJumpStatus: 'अगला वरà¥à¤· देखें', + currentText: 'वरà¥à¤¤à¤®à¤¾à¤¨', + currentStatus: 'वरà¥à¤¤à¤®à¤¾à¤¨ महीना देखें', + todayText: 'आज', + todayStatus: 'वरà¥à¤¤à¤®à¤¾à¤¨ दिन देखें', + clearText: 'साफ', + clearStatus: 'वरà¥à¤¤à¤®à¤¾à¤¨ दिनांक मिटाà¤', + closeText: 'समापà¥à¤¤', + closeStatus: 'बदलाव के बिना बंद', + yearStatus: 'à¤à¤• अलग वरà¥à¤· का चयन करें', + monthStatus: 'à¤à¤• अलग महीने का चयन करें', + weekText: 'Wk', + weekStatus: 'वरà¥à¤· का सपà¥à¤¤à¤¾à¤¹', + dayStatus: 'चà¥à¤¨à¥‡ DD, M d', + defaultStatus: 'à¤à¤• तिथि का चयन करें', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions['hi-IN']); +})(jQuery); + +/* http://keith-wood.name/datepick.html + English Hindi localisation for jQuery Datepicker. + Written by Tirumal Rao of designphilic.com. */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.hi = { + monthNames: ['जनवरी','फरवरी','मारà¥à¤š','अपà¥à¤°à¥ˆà¤²','मई','जून', + 'जà¥à¤²à¤¾à¤ˆ','अगसà¥à¤¤','सितंबर','अकà¥à¤Ÿà¥‚बर','नवंबर','दिसंबर'], + monthNamesShort: ['जन','फ़र.','मारà¥à¤š','अपà¥à¤°à¥ˆà¤²','मई','जून', + 'जà¥à¤²à¤¾à¤ˆ','अगसà¥à¤¤','सितंबर','अकà¥à¤Ÿà¥‚बर','नवंबर','दिसंबर'], + dayNames: ['रविवार','सोमवार','मंगलवार','बà¥à¤§à¤µà¤¾à¤°','बृहसà¥à¤ªà¤¤à¤¿à¤µà¤¾à¤°','शà¥à¤•à¥à¤°à¤µà¤¾à¤°','शनिवार'], + dayNamesShort: ['रवि','सोम','मंगल','बà¥à¤§','बृहसà¥à¤ªà¤¤','शà¥à¤•à¥à¤°','शनि'], + dayNamesMin: ['रवि','सोम','मंगल','बà¥à¤§','बृहसà¥à¤ªà¤¤','शà¥à¤•à¥à¤°','शनि'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: 'पिछला', + prevStatus: 'पिछले महीने', + prevJumpText: '<<', + prevJumpStatus: 'पिछले वरà¥à¤·', + nextText: 'अगला', + nextStatus: 'अगले महीने', + nextJumpText: '>>', + nextJumpStatus: 'अगले साल', + currentText: 'वरà¥à¤¤à¤®à¤¾à¤¨', + currentStatus: 'चालू माह', + todayText: 'आज', + todayStatus: 'आजका महीना', + clearText: 'साफ़', + clearStatus: 'वरà¥à¤¤à¤®à¤¾à¤¨ दिनांक मिटा', + closeText: 'ठीक है', + closeStatus: 'बदलाव के बिना बंद', + yearStatus: 'à¤à¤• अलग वरà¥à¤· दिखाà¤à¤', + monthStatus: 'दिखाà¤à¤ किसी अनà¥à¤¯ महीने के', + weekText: 'Wk', + weekStatus: 'Week of the year', + dayStatus: 'चयन DD, M d', + defaultStatus: 'à¤à¤• तिथि का चयन करें', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.hi); +})(jQuery); +/* http://keith-wood.name/datepick.html + Croatian localisation for jQuery Datepicker. + Written by Vjekoslav Nesek. */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.hr = { + monthNames: ['SijeÄanj','VeljaÄa','Ožujak','Travanj','Svibanj','Lipanj', + 'Srpanj','Kolovoz','Rujan','Listopad','Studeni','Prosinac'], + monthNamesShort: ['Sij','Velj','Ožu','Tra','Svi','Lip', + 'Srp','Kol','Ruj','Lis','Stu','Pro'], + dayNames: ['Nedjelja','Ponedjeljak','Utorak','Srijeda','ÄŒetvrtak','Petak','Subota'], + dayNamesShort: ['Ned','Pon','Uto','Sri','ÄŒet','Pet','Sub'], + dayNamesMin: ['Ne','Po','Ut','Sr','ÄŒe','Pe','Su'], + dateFormat: 'dd.mm.yyyy.', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<', + prevStatus: 'Prikaži prethodni mjesec', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: '>', + nextStatus: 'Prikaži slijedeći mjesec', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Danas', + currentStatus: 'DanaÅ¡nji datum', + todayText: 'Danas', + todayStatus: 'DanaÅ¡nji datum', + clearText: 'izbriÅ¡i', + clearStatus: 'IzbriÅ¡i trenutni datum', + closeText: 'Zatvori', + closeStatus: 'Zatvori kalendar', + yearStatus: 'Prikaži godine', + monthStatus: 'Prikaži mjesece', + weekText: 'Tje', + weekStatus: 'Tjedan', + dayStatus: '\'Datum\' D, M d', + defaultStatus: 'Odaberi datum', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.hr); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Hungarian localisation for jQuery Datepicker. + Written by Istvan Karaszi (jquery@spam.raszi.hu). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.hu = { + monthNames: ['Január','Február','Március','Ãprilis','Május','Június', + 'Július','Augusztus','Szeptember','Október','November','December'], + monthNamesShort: ['Jan','Feb','Már','Ãpr','Máj','Jún', + 'Júl','Aug','Szep','Okt','Nov','Dec'], + dayNames: ['Vasárnap','Hétfö','Kedd','Szerda','Csütörtök','Péntek','Szombat'], + dayNamesShort: ['Vas','Hét','Ked','Sze','Csü','Pén','Szo'], + dayNamesMin: ['V','H','K','Sze','Cs','P','Szo'], + dateFormat: 'yyyy-mm-dd', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '« vissza', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'elÅ‘re »', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'ma', + currentStatus: '', + todayText: 'ma', + todayStatus: '', + clearText: 'törlés', + clearStatus: '', + closeText: 'bezárás', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Hé', + weekStatus: '', + dayStatus: 'D, M d', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.hu); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Armenian localisation for jQuery Datepicker. + Written by Levon Zakaryan (levon.zakaryan@gmail.com)*/ +(function($) { + 'use strict'; + $.datepick.regionalOptions.hy = { + monthNames: ['Õ€Õ¸Ö‚Õ¶Õ¾Õ¡Ö€','Õ“Õ¥Õ¿Ö€Õ¾Õ¡Ö€','Õ„Õ¡Ö€Õ¿','Ô±ÕºÖ€Õ«Õ¬','Õ„Õ¡ÕµÕ«Õ½','Õ€Õ¸Ö‚Õ¶Õ«Õ½', + 'Õ€Õ¸Ö‚Õ¬Õ«Õ½','Õ•Õ£Õ¸Õ½Õ¿Õ¸Õ½','ÕÕ¥ÕºÕ¿Õ¥Õ´Õ¢Õ¥Ö€','Õ€Õ¸Õ¯Õ¿Õ¥Õ´Õ¢Õ¥Ö€','Õ†Õ¸ÕµÕ¥Õ´Õ¢Õ¥Ö€','Ô´Õ¥Õ¯Õ¿Õ¥Õ´Õ¢Õ¥Ö€'], + monthNamesShort: ['Õ€Õ¸Ö‚Õ¶Õ¾','Õ“Õ¥Õ¿Ö€','Õ„Õ¡Ö€Õ¿','Ô±ÕºÖ€','Õ„Õ¡ÕµÕ«Õ½','Õ€Õ¸Ö‚Õ¶Õ«Õ½', + 'Õ€Õ¸Ö‚Õ¬','Õ•Õ£Õ½','ÕÕ¥Õº','Õ€Õ¸Õ¯','Õ†Õ¸Õµ','Ô´Õ¥Õ¯'], + dayNames: ['Õ¯Õ«Ö€Õ¡Õ¯Õ«','Õ¥Õ¯Õ¸Ö‚Õ·Õ¡Õ¢Õ©Õ«','Õ¥Ö€Õ¥Ö„Õ·Õ¡Õ¢Õ©Õ«','Õ¹Õ¸Ö€Õ¥Ö„Õ·Õ¡Õ¢Õ©Õ«','Õ°Õ«Õ¶Õ£Õ·Õ¡Õ¢Õ©Õ«','Õ¸Ö‚Ö€Õ¢Õ¡Õ©','Õ·Õ¡Õ¢Õ¡Õ©'], + dayNamesShort: ['Õ¯Õ«Ö€','Õ¥Ö€Õ¯','Õ¥Ö€Ö„','Õ¹Ö€Ö„','Õ°Õ¶Õ£','Õ¸Ö‚Ö€Õ¢','Õ·Õ¢Õ©'], + dayNamesMin: ['Õ¯Õ«Ö€','Õ¥Ö€Õ¯','Õ¥Ö€Ö„','Õ¹Ö€Ö„','Õ°Õ¶Õ£','Õ¸Ö‚Ö€Õ¢','Õ·Õ¢Õ©'], + dateFormat: 'dd.mm.yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<Õ†Õ¡Õ­.', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Õ€Õ¡Õ».>', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Ô±ÕµÕ½Ö…Ö€', + currentStatus: '', + todayText: 'Ô±ÕµÕ½Ö…Ö€', + todayStatus: '', + clearText: 'Õ„Õ¡Ö„Ö€Õ¥Õ¬', + clearStatus: '', + closeText: 'Õ“Õ¡Õ¯Õ¥Õ¬', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Õ‡Ô²Õ', + weekStatus: '', + dayStatus: 'D, M d', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.hy); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Indonesian localisation for jQuery Datepicker. + Written by Deden Fathurahman (dedenf@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.id = { + monthNames: ['Januari','Februari','Maret','April','Mei','Juni', + 'Juli','Agustus','September','Oktober','Nopember','Desember'], + monthNamesShort: ['Jan','Feb','Mar','Apr','Mei','Jun', + 'Jul','Agus','Sep','Okt','Nop','Des'], + dayNames: ['Minggu','Senin','Selasa','Rabu','Kamis','Jumat','Sabtu'], + dayNamesShort: ['Min','Sen','Sel','Rab','kam','Jum','Sab'], + dayNamesMin: ['Mg','Sn','Sl','Rb','Km','jm','Sb'], + dateFormat: 'dd/mm/yyyy', + firstDay: 0, + renderer: $.datepick.defaultRenderer, + prevText: '<mundur', + prevStatus: 'Tampilkan bulan sebelumnya', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'maju>', + nextStatus: 'Tampilkan bulan berikutnya', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'hari ini', + currentStatus: 'Tampilkan bulan sekarang', + todayText: 'hari ini', + todayStatus: 'Tampilkan bulan sekarang', + clearText: 'kosongkan', + clearStatus: 'bersihkan tanggal yang sekarang', + closeText: 'Tutup', + closeStatus: 'Tutup tanpa mengubah', + yearStatus: 'Tampilkan tahun yang berbeda', + monthStatus: 'Tampilkan bulan yang berbeda', + weekText: 'Mg', + weekStatus: 'Minggu dalam tahun', + dayStatus: 'pilih le DD, MM d', + defaultStatus: 'Pilih Tanggal', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.id); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Icelandic localisation for jQuery Datepicker. + Written by Haukur H. Thorsson (haukur@eskill.is). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.is = { + monthNames: ['Janúar','Febrúar','Mars','Apríl','Maí','Júní', + 'Júlí','Ágúst','September','Október','Nóvember','Desember'], + monthNamesShort: ['Jan','Feb','Mar','Apr','Maí','Jún', + 'Júl','Ágú','Sep','Okt','Nóv','Des'], + dayNames: ['Sunnudagur','Mánudagur','Þriðjudagur','Miðvikudagur','Fimmtudagur','Föstudagur','Laugardagur'], + dayNamesShort: ['Sun','Mán','Þri','Mið','Fim','Fös','Lau'], + dayNamesMin: ['Su','Má','Þr','Mi','Fi','Fö','La'], + dateFormat: 'dd/mm/yyyy', + firstDay: 0, + renderer: $.datepick.defaultRenderer, + prevText: '< Fyrri', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Næsti >', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Í dag', + currentStatus: '', + todayText: 'Í dag', + todayStatus: '', + clearText: 'Hreinsa', + clearStatus: '', + closeText: 'Loka', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Vika', + weekStatus: '', + dayStatus: 'D, M d', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.is); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Italian localisation for jQuery Datepicker. + Written by Apaella (apaella@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.it = { + monthNames: ['Gennaio','Febbraio','Marzo','Aprile','Maggio','Giugno', + 'Luglio','Agosto','Settembre','Ottobre','Novembre','Dicembre'], + monthNamesShort: ['Gen','Feb','Mar','Apr','Mag','Giu', + 'Lug','Ago','Set','Ott','Nov','Dic'], + dayNames: ['Domenica','Lunedì','Martedì','Mercoledì','Giovedì','Venerdì','Sabato'], + dayNamesShort: ['Dom','Lun','Mar','Mer','Gio','Ven','Sab'], + dayNamesMin: ['Do','Lu','Ma','Me','Gi','Ve','Sa'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<Prec', + prevStatus: 'Mese precedente', + prevJumpText: '<<', + prevJumpStatus: 'Mostra l\'anno precedente', + nextText: 'Succ>', + nextStatus: 'Mese successivo', + nextJumpText: '>>', + nextJumpStatus: 'Mostra l\'anno successivo', + currentText: 'Oggi', + currentStatus: 'Mese corrente', + todayText: 'Oggi', + todayStatus: 'Mese corrente', + clearText: 'Svuota', + clearStatus: 'Annulla', + closeText: 'Chiudi', + closeStatus: 'Chiudere senza modificare', + yearStatus: 'Seleziona un altro anno', + monthStatus: 'Seleziona un altro mese', + weekText: 'Sm', + weekStatus: 'Settimana dell\'anno', + dayStatus: '\'Seleziona\' D, M d', + defaultStatus: 'Scegliere una data', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.it); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Japanese localisation for jQuery Datepicker. + Written by Kentaro SATO (kentaro@ranvis.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.ja = { + monthNames: ['1月','2月','3月','4月','5月','6月', + '7月','8月','9月','10月','11月','12月'], + monthNamesShort: ['1月','2月','3月','4月','5月','6月', + '7月','8月','9月','10月','11月','12月'], + dayNames: ['日曜日','月曜日','ç«æ›œæ—¥','水曜日','木曜日','金曜日','土曜日'], + dayNamesShort: ['æ—¥','月','ç«','æ°´','木','金','土'], + dayNamesMin: ['æ—¥','月','ç«','æ°´','木','金','土'], + dateFormat: 'yyyy/mm/dd', + firstDay: 0, + renderer: $.extend({}, $.datepick.defaultRenderer, { + month: $.datepick.defaultRenderer.month.replace(/monthHeader/, 'monthHeader:yyyyå¹´ MM') + }), + prevText: '<å‰', + prevStatus: '剿œˆã‚’表示ã—ã¾ã™', + prevJumpText: '<<', + prevJumpStatus: 'å‰å¹´ã‚’表示ã—ã¾ã™', + nextText: '次>', + nextStatus: '翌月を表示ã—ã¾ã™', + nextJumpText: '>>', + nextJumpStatus: '翌年を表示ã—ã¾ã™', + currentText: '今日', + currentStatus: '今月を表示ã—ã¾ã™', + todayText: '今日', + todayStatus: '今月を表示ã—ã¾ã™', + clearText: 'クリア', + clearStatus: '日付をクリアã—ã¾ã™', + closeText: 'é–‰ã˜ã‚‹', + closeStatus: '変更ã›ãšã«é–‰ã˜ã¾ã™', + yearStatus: '表示ã™ã‚‹å¹´ã‚’変更ã—ã¾ã™', + monthStatus: '表示ã™ã‚‹æœˆã‚’変更ã—ã¾ã™', + weekText: '週', + weekStatus: '暦週ã§ç¬¬ä½•週目ã‹ã‚’表ã—ã¾ã™', + dayStatus: 'Mdæ—¥(D)', + defaultStatus: 'æ—¥ä»˜ã‚’é¸æŠžã—ã¾ã™', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.ja); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Georgian localisation for jQuery Datepicker. + Andrei Gorbushkin. */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.ka = { + monthNames: ['იáƒáƒœáƒ•áƒáƒ áƒ˜','თებერვáƒáƒšáƒ˜','მáƒáƒ áƒ¢áƒ˜','áƒáƒžáƒ áƒ˜áƒšáƒ˜','მáƒáƒ˜áƒ¡áƒ˜','ივნისი', + 'ივლისი','áƒáƒ’ვისტáƒ','სექტემბერი','áƒáƒ¥áƒ¢áƒáƒ›áƒ‘ერი','ნáƒáƒ”მბერი','დეკემბერი'], + monthNamesShort: ['იáƒáƒœ','თებ','მáƒáƒ ','áƒáƒžáƒ ','მáƒáƒ˜áƒ¡áƒ˜','ივნ', + 'ივლ','áƒáƒ’ვ','სექ','áƒáƒ¥áƒ¢','ნáƒáƒ”','დეკ'], + dayNames: ['კვირáƒ','áƒáƒ áƒ¨áƒáƒ‘áƒáƒ—ი','სáƒáƒ›áƒ¨áƒáƒ‘áƒáƒ—ი','áƒáƒ—ხშáƒáƒ‘áƒáƒ—ი','ხუთშáƒáƒ‘áƒáƒ—ი','პáƒáƒ áƒáƒ¡áƒ™áƒ”ვი','შáƒáƒ‘áƒáƒ—ი'], + dayNamesShort: ['კვ','áƒáƒ áƒ¨','სáƒáƒ›','áƒáƒ—ხ','ხუთ','პáƒáƒ ','შáƒáƒ‘'], + dayNamesMin: ['კვ','áƒáƒ ','სმ','áƒáƒ—','ხშ','პრ','შბ'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<უკáƒáƒœ', + prevStatus: 'წინრთვე', + prevJumpText: '<<', + prevJumpStatus: 'წინრწელი', + nextText: 'წინ>', + nextStatus: 'შემდეგი თვე', + nextJumpText: '>>', + nextJumpStatus: 'შემდეგი წელი', + currentText: 'მიმდინáƒáƒ áƒ”', + currentStatus: 'მიმდინáƒáƒ áƒ” თვე', + todayText: 'დღეს', + todayStatus: 'მიმდინáƒáƒ áƒ” დღე', + clearText: 'გáƒáƒ¡áƒ£áƒ¤áƒ—áƒáƒ•ებáƒ', + clearStatus: 'მიმდინáƒáƒ áƒ” თáƒáƒ áƒ˜áƒ¦áƒ˜áƒ¡ წáƒáƒ¨áƒšáƒ', + closeText: 'áƒáƒ áƒ˜áƒ¡', + closeStatus: 'დáƒáƒ®áƒ£áƒ áƒ•რუცვლილებáƒáƒ“', + yearStatus: 'სხვრწელი', + monthStatus: 'სხვრთვე', + weekText: 'კვ', + weekStatus: 'წლის კვირáƒ', + dayStatus: 'áƒáƒ˜áƒ áƒ©áƒ˜áƒ”თ DD, M d', + defaultStatus: 'áƒáƒ˜áƒ¦áƒ©áƒ˜áƒ”თ თáƒáƒ áƒ˜áƒ¦áƒ˜', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.ka); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Khmer initialisation for jQuery Datepicker. + Written by Sovichet Tep (sovichet.tep@gmail.com). */ +(function($){ + 'use strict'; + $.datepick.regionalOptions.km = { + monthNames: ['ážáŸ‚​មករា','ážáŸ‚​កុម្ភៈ','ážáŸ‚​មិនា','ážáŸ‚​មáŸážŸáž¶','ážáŸ‚​ឧសភា','ážáŸ‚​មិážáž»áž“áž¶', + 'ážáŸ‚​កក្កដា','ážáŸ‚​សីហា','ážáŸ‚​កញ្ញា','ážáŸ‚​ážáž»áž›áž¶','ážáŸ‚​វិច្ឆិកា','ážáŸ‚​ធ្នូ'], + monthNamesShort: ['មក','កុ','មិនា','មáŸ','ឧស','មិážáž»', + 'កក្ក','សី','កញ្ញា','ážáž»áž›áž¶','វិច្ឆិ','ធ្នូ'], + dayNames: ['ážáŸ’ងៃ​អាទិážáŸ’áž™','ážáŸ’ងៃ​ចន្ទ','ážáŸ’ងៃ​អង្គារ','ážáŸ’ងៃ​ពុធ','ážáŸ’ងៃ​ព្រហស្បážáŸ’ážáž·áŸ','ážáŸ’ងៃ​សុក្រ','ážáŸ’ងៃ​សៅរáŸ'], + dayNamesShort: ['អា','ចន្ទ','អង្គ','ពុធ','ព្រហ','សុ','សៅរáŸ'], + dayNamesMin: ['អា','áž…','អ','áž–áž»','ព្រ','សុ','ស'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: 'ážáž™â€‹áž€áŸ’រោយ', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'ទៅ​មុáž', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'ážáŸ’ងៃ​នáŸáŸ‡', + currentStatus: '', + todayText: 'ážáŸ’ងៃ​នáŸáŸ‡', + todayStatus: '', + clearText: 'X', + clearStatus: '', + closeText: 'រួច​រាល់', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Wk', + weekStatus: '', + dayStatus: 'DD d MM', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.km); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Korean localisation for jQuery Datepicker. + Written by DaeKwon Kang (ncrash.dk@gmail.com), Edited by Genie. */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.ko = { + monthNames: ['1ì›”','2ì›”','3ì›”','4ì›”','5ì›”','6ì›”', + '7ì›”','8ì›”','9ì›”','10ì›”','11ì›”','12ì›”'], + monthNamesShort: ['1ì›”','2ì›”','3ì›”','4ì›”','5ì›”','6ì›”', + '7ì›”','8ì›”','9ì›”','10ì›”','11ì›”','12ì›”'], + dayNames: ['ì¼ìš”ì¼','월요ì¼','화요ì¼','수요ì¼','목요ì¼','금요ì¼','토요ì¼'], + dayNamesShort: ['ì¼','ì›”','í™”','수','목','금','토'], + dayNamesMin: ['ì¼','ì›”','í™”','수','목','금','토'], + dateFormat: 'yyyy-mm-dd', + firstDay: 0, + renderer: $.extend({}, $.datepick.defaultRenderer, { + month: $.datepick.defaultRenderer.month.replace(/monthHeader/, 'monthHeader:yyyyë…„ MM') + }), + prevText: 'ì´ì „달', + prevStatus: 'ì´ì „ë‹¬ì„ í‘œì‹œí•©ë‹ˆë‹¤', + prevJumpText: '<<', + prevJumpStatus: 'ì´ì „ ì—°ë„를 표시합니다', + nextText: '다ìŒë‹¬', + nextStatus: '다ìŒë‹¬ì„ 표시합니다', + nextJumpText: '>>', + nextJumpStatus: 'ë‹¤ìŒ ì—°ë„를 표시합니다', + currentText: '현재', + currentStatus: '입력한 ë‹¬ì„ í‘œì‹œí•©ë‹ˆë‹¤', + todayText: '오늘', + todayStatus: 'ì´ë²ˆë‹¬ì„ 표시합니다', + clearText: '지우기', + clearStatus: '입력한 날짜를 ì§€ì›ë‹ˆë‹¤', + closeText: '닫기', + closeStatus: '', + yearStatus: '표시할 ì—°ë„를 변경합니다', + monthStatus: '표시할 ì›”ì„ ë³€ê²½í•©ë‹ˆë‹¤', + weekText: 'Wk', + weekStatus: '해당 ì—°ë„ì˜ ì£¼ì°¨', + dayStatus: 'M dì¼ (D)', + defaultStatus: '날짜를 ì„ íƒí•˜ì„¸ìš”', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.ko); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Lithuanian localisation for jQuery Datepicker. + Written by Arturas Paleicikas */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.lt = { + monthNames: ['Sausis','Vasaris','Kovas','Balandis','Gegužė','Birželis', + 'Liepa','RugpjÅ«tis','RugsÄ—jis','Spalis','Lapkritis','Gruodis'], + monthNamesShort: ['Sau','Vas','Kov','Bal','Geg','Bir', + 'Lie','Rugp','Rugs','Spa','Lap','Gru'], + dayNames: ['sekmadienis','pirmadienis','antradienis','treÄiadienis','ketvirtadienis','penktadienis','Å¡eÅ¡tadienis'], + dayNamesShort: ['sek','pir','ant','tre','ket','pen','Å¡eÅ¡'], + dayNamesMin: ['Se','Pr','An','Tr','Ke','Pe','Å e'], + dateFormat: 'yyyy-mm-dd', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<Atgal', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Pirmyn>', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Å iandien', + currentStatus: '', + todayText: 'Å iandien', + todayStatus: '', + clearText: 'IÅ¡valyti', + clearStatus: '', + closeText: 'Uždaryti', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Wk', + weekStatus: '', + dayStatus: 'D, M d', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.lt); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Latvian localisation for jQuery Datepicker. + Written by Arturas Paleicikas */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.lv = { + monthNames: ['JanvÄris','FebruÄris','Marts','AprÄ«lis','Maijs','JÅ«nijs', + 'JÅ«lijs','Augusts','Septembris','Oktobris','Novembris','Decembris'], + monthNamesShort: ['Jan','Feb','Mar','Apr','Mai','JÅ«n', + 'JÅ«l','Aug','Sep','Okt','Nov','Dec'], + dayNames: ['svÄ“tdiena','pirmdiena','otrdiena','treÅ¡diena','ceturtdiena','piektdiena','sestdiena'], + dayNamesShort: ['svt','prm','otr','tre','ctr','pkt','sst'], + dayNamesMin: ['Sv','Pr','Ot','Tr','Ct','Pk','Ss'], + dateFormat: 'dd-mm-yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: 'Iepr', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'NÄka', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Å odien', + currentStatus: '', + todayText: 'Å odien', + todayStatus: '', + clearText: 'NotÄ«rÄ«t', + clearStatus: '', + closeText: 'AizvÄ“rt', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Nav', + weekStatus: '', + dayStatus: 'D, M d', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.lv); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Montenegrin localisation for jQuery Datepicker. + By MiloÅ¡ MiloÅ¡ević - fleka d.o.o. */ +(function($) { + 'use strict'; + $.datepick.regionalOptions['me-ME'] = { + monthNames: ['Januar','Februar','Mart','April','Maj','Jun', + 'Jul','Avgust','Septembar','Oktobar','Novembar','Decembar'], + monthNamesShort: ['Jan','Feb','Mar','Apr','Maj','Jun', + 'Jul','Avg','Sep','Okt','Nov','Dec'], + dayNames: ['NeÄ‘elja','PoneÄ‘eljak','Utorak','Srijeda','ÄŒetvrtak','Petak','Subota'], + dayNamesShort: ['NeÄ‘','Pon','Uto','Sri','ÄŒet','Pet','Sub'], + dayNamesMin: ['Ne','Po','Ut','Sr','ÄŒe','Pe','Su'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<', + prevStatus: 'Prikaži prethodni mjesec', + prevJumpText: '<<', + prevJumpStatus: 'Prikaži prethodnu godinu', + nextText: '>', + nextStatus: 'Prikaži sljedeći mjesec', + nextJumpText: '>>', + nextJumpStatus: 'Prikaži sljedeću godinu', + currentText: 'Danas', + currentStatus: 'Tekući mjesec', + todayText: 'Danas', + todayStatus: 'Tekući mjesec', + clearText: 'ObriÅ¡i', + clearStatus: 'ObriÅ¡i trenutni datum', + closeText: 'Zatvori', + closeStatus: 'Zatvori kalendar', + yearStatus: 'Prikaži godine', + monthStatus: 'Prikaži mjesece', + weekText: 'Sed', + weekStatus: 'Sedmica', + dayStatus: '\'Datum\' DD, M d', + defaultStatus: 'Odaberi datum', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions['me-ME']); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Montenegrin localisation for jQuery Datepicker. + By MiloÅ¡ MiloÅ¡ević - fleka d.o.o. */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.me = { + monthNames: ['Јануар','Фебруар','Март','Ðприл','Мај','Јун', + 'Јул','ÐвгуÑÑ‚','Септембар','Октобар','Ðовембар','Децембар'], + monthNamesShort: ['Јан','Феб','Мар','Ðпр','Мај','Јун', + 'Јул','Ðвг','Сеп','Окт','Ðов','Дец'], + dayNames: ['Ðеђеља','Понеђељак','Уторак','Сриједа','Четвртак','Петак','Субота'], + dayNamesShort: ['Ðеђ','Пон','Уто','Сри','Чет','Пет','Суб'], + dayNamesMin: ['Ðе','По','Ут','Ср','Че','Пе','Су'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<', + prevStatus: 'Прикажи претходни мјеÑец', + prevJumpText: '<<', + prevJumpStatus: 'Прикажи претходну годину', + nextText: '>', + nextStatus: 'Прикажи Ñљедећи мјеÑец', + nextJumpText: '>>', + nextJumpStatus: 'Прикажи Ñљедећу годину', + currentText: 'ДанаÑ', + currentStatus: 'Текући мјеÑец', + todayText: 'ДанаÑ', + todayStatus: 'Текући мјеÑец', + clearText: 'Обриши', + clearStatus: 'Обриши тренутни датум', + closeText: 'Затвори', + closeStatus: 'Затвори календар', + yearStatus: 'Прикажи године', + monthStatus: 'Прикажи мјеÑеце', + weekText: 'Сед', + weekStatus: 'Седмица', + dayStatus: '\'Датум\' DD d MM', + defaultStatus: 'Одабери датум', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.me); +})(jQuery); + +/* http://keith-wood.name/datepick.html + МакедонÑки MK localisation for jQuery Datepicker. + Written by Hajan Selmani + email: hajan [at] live [dot] com + url: http://weblogs.asp.net/hajan | http://codeasp.net/blogs/hajan | http://mkdot.net/blogs/hajan */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.mk = { + monthNames: ['Јануари','Февруари','Март','Ðприл','Мај','Јуни', + 'Јули','ÐвгуÑÑ‚','Септември','Октомври','Ðоември','Декември'], + monthNamesShort: ['Јан','Фев','Мар','Ðпр','Мај','Јун', + 'Јул','Ðвг','Сеп','Окт','Ðов','Дек'], + dayNames: ['Ðедела','Понеделник','Вторник','Среда','Четврток','Петок','Сабота'], + dayNamesShort: ['Ðед','Пон','Вто','Сре','Чет','Пет','Саб'], + dayNamesMin: ['Ðе','По','Ð’Ñ‚','Ср','Че','Пе','Са'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: 'Претх.', + prevStatus: 'Прикажи го претходниот меÑец', + prevJumpText: '<<', + prevJumpStatus: 'Прикажи ја претходната година', + nextText: 'Следен', + nextStatus: 'Прикажи го Ñледниот меÑец', + nextJumpText: '>>', + nextJumpStatus: 'Прикажи ја Ñледната година', + currentText: 'Тековен', + currentStatus: 'Прикажи го тековниот меÑец', + todayText: 'ДенеÑ', + todayStatus: 'Прикажи го денешниот меÑец', + clearText: 'Бриши', + clearStatus: 'Избриши го тековниот датум', + closeText: 'Затвори', + closeStatus: 'Затвори без промени', + yearStatus: 'Избери друга година', + monthStatus: 'Избери друг меÑец', + weekText: 'Ðед', + weekStatus: 'Ðедела во годината', + dayStatus: 'Избери DD, M d', + defaultStatus: 'Избери датум', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.mk); +})(jQuery); +/* http://keith-wood.name/datepick.html + Malayalam localisation for jQuery Datepicker. + Saji Nediyanchath (saji89@gmail.com). */ +(function($) { + 'use strict'; + /* jshint -W100 */ + $.datepick.regionalOptions.ml = { + monthNames: ['ജനàµà´µà´°à´¿','ഫെബàµà´°àµà´µà´°à´¿','മാരàµâ€à´šàµà´šàµ','à´à´ªàµà´°à´¿à´²àµâ€','മേയàµ','ജൂണàµâ€', + 'ജൂലൈ','ആഗസàµà´±àµà´±àµ','സെപàµà´±àµà´±à´‚ബരàµâ€','à´’à´•àµà´Ÿàµ‹à´¬à´°àµâ€','നവംബരàµâ€','ഡിസംബരàµâ€'], + monthNamesShort: ['ജനàµ','ഫെബàµ','മാരàµâ€','à´à´ªàµà´°à´¿','മേയàµ','ജൂണàµâ€', + 'ജൂലാ','ആഗ','സെപàµ','à´’à´•àµà´Ÿàµ‹','നവം','à´¡à´¿à´¸'], + dayNames: ['ഞായരàµâ€','തിങàµà´•à´³àµâ€','ചൊവàµà´µ','à´¬àµà´§à´¨àµâ€','à´µàµà´¯à´¾à´´à´‚','വെളàµà´³à´¿','ശനി'], + dayNamesShort: ['ഞായ','തിങàµà´•','ചൊവàµà´µ','à´¬àµà´§','à´µàµà´¯à´¾à´´à´‚','വെളàµà´³à´¿','ശനി'], + dayNamesMin: ['à´žà´¾','തി','ചൊ','à´¬àµ','à´µàµà´¯à´¾','വെ','à´¶'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: 'à´®àµà´¨àµà´¨à´¤àµà´¤àµ†', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'à´…à´Ÿàµà´¤àµà´¤à´¤àµ ', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'ഇനàµà´¨àµ', + currentStatus: '', + todayText: 'ഇനàµà´¨àµ', + todayStatus: '', + clearText: 'X', + clearStatus: '', + closeText: 'à´¶à´°à´¿', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'à´†', + weekStatus: '', + dayStatus: 'DD d MM', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.ml); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Malaysian localisation for jQuery Datepicker. + Written by Mohd Nawawi Mohamad Jamili (nawawi@ronggeng.net). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.ms = { + monthNames: ['Januari','Februari','Mac','April','Mei','Jun', + 'Julai','Ogos','September','Oktober','November','Disember'], + monthNamesShort: ['Jan','Feb','Mac','Apr','Mei','Jun', + 'Jul','Ogo','Sep','Okt','Nov','Dis'], + dayNames: ['Ahad','Isnin','Selasa','Rabu','Khamis','Jumaat','Sabtu'], + dayNamesShort: ['Aha','Isn','Sel','Rab','Kha','Jum','Sab'], + dayNamesMin: ['Ah','Is','Se','Ra','Kh','Ju','Sa'], + dateFormat: 'dd/mm/yyyy', + firstDay: 0, + renderer: $.datepick.defaultRenderer, + prevText: '<Sebelum', + prevStatus: 'Tunjukkan bulan lepas', + prevJumpText: '<<', + prevJumpStatus: 'Tunjukkan tahun lepas', + nextText: 'Selepas>', + nextStatus: 'Tunjukkan bulan depan', + nextJumpText: '>>', + nextJumpStatus: 'Tunjukkan tahun depan', + currentText: 'hari ini', + currentStatus: 'Tunjukkan bulan terkini', + todayText: 'hari ini', + todayStatus: 'Tunjukkan bulan terkini', + clearText: 'Padam', + clearStatus: 'Padamkan tarikh terkini', + closeText: 'Tutup', + closeStatus: 'Tutup tanpa perubahan', + yearStatus: 'Tunjukkan tahun yang lain', + monthStatus: 'Tunjukkan bulan yang lain', + weekText: 'Mg', + weekStatus: 'Minggu bagi tahun ini', + dayStatus: 'DD, d MM', + defaultStatus: 'Sila pilih tarikh', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.ms); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Maltese localisation for jQuery Datepicker. + Written by Chritian Sciberras (uuf6429@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.mt = { + monthNames: ['Jannar','Frar','Marzu','April','Mejju','Ä unju', + 'Lulju','Awissu','Settembru','Ottubru','Novembru','DiÄ‹embru'], + monthNamesShort: ['Jan','Fra','Mar','Apr','Mej','Ä un', + 'Lul','Awi','Set','Ott','Nov','DiÄ‹'], + dayNames: ['Il-Ħadd','It-Tnejn','It-Tlieta','L-Erbgħa','Il-Ħamis','Il-Ä imgħa','Is-Sibt'], + dayNamesShort: ['Ħad','Tne','Tli','Erb','Ħam','Ä im','Sib'], + dayNamesMin: ['Ħ','T','T','E','Ħ','Ä ','S'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: 'Ta Qabel', + prevStatus: 'Ix-xahar ta qabel', + prevJumpText: '<<', + prevJumpStatus: 'Is-sena ta qabel', + nextText: 'Li Jmiss', + nextStatus: 'Ix-xahar li jmiss', + nextJumpText: '>>', + nextJumpStatus: 'Is-sena li jmiss', + currentText: 'Illum', + currentStatus: 'Ix-xahar ta llum', + todayText: 'Illum', + todayStatus: 'Uri ix-xahar ta llum', + clearText: 'Ħassar', + clearStatus: 'Ħassar id-data', + closeText: 'Lest', + closeStatus: 'Għalaq mingħajr tibdiliet', + yearStatus: 'Uri sena differenti', + monthStatus: 'Uri xahar differenti', + weekText: 'Ä m', + weekStatus: 'Il-Ä imgħa fis-sena', + dayStatus: 'Għazel DD, M d', + defaultStatus: 'Għazel data', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.mt); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Dutch/Belgium localisation for jQuery Datepicker. + Written by Mathias Bynens */ +(function($) { + 'use strict'; + $.datepick.regionalOptions['nl-BE'] = { + monthNames: ['januari','februari','maart','april','mei','juni', + 'juli','augustus','september','oktober','november','december'], + monthNamesShort: ['jan','feb','maa','apr','mei','jun', + 'jul','aug','sep','okt','nov','dec'], + dayNames: ['zondag','maandag','dinsdag','woensdag','donderdag','vrijdag','zaterdag'], + dayNamesShort: ['zon','maa','din','woe','don','vri','zat'], + dayNamesMin: ['zo','ma','di','wo','do','vr','za'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: 'â†', + prevStatus: 'Bekijk de vorige maand', + prevJumpText: '«', + prevJumpStatus: 'Bekijk het vorige jaar', + nextText: '→', + nextStatus: 'Bekijk de volgende maand', + nextJumpText: '»', + nextJumpStatus: 'Bekijk het volgende jaar', + currentText: 'Vandaag', + currentStatus: 'Bekijk de huidige maand', + todayText: 'Vandaag', + todayStatus: 'Bekijk de huidige maand', + clearText: 'Wissen', + clearStatus: 'Wis de huidige datum', + closeText: 'Sluiten', + closeStatus: 'Sluit zonder verandering', + yearStatus: 'Bekijk een ander jaar', + monthStatus: 'Bekijk een andere maand', + weekText: 'Wk', + weekStatus: 'Week van het jaar', + dayStatus: 'dd/mm/yyyy', + defaultStatus: 'Kies een datum', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions['nl-BE']); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Dutch localisation for jQuery Datepicker. + Written by Mathias Bynens */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.nl = { + monthNames: ['januari','februari','maart','april','mei','juni', + 'juli','augustus','september','oktober','november','december'], + monthNamesShort: ['jan','feb','maa','apr','mei','jun', + 'jul','aug','sep','okt','nov','dec'], + dayNames: ['zondag','maandag','dinsdag','woensdag','donderdag','vrijdag','zaterdag'], + dayNamesShort: ['zon','maa','din','woe','don','vri','zat'], + dayNamesMin: ['zo','ma','di','wo','do','vr','za'], + dateFormat: 'dd-mm-yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: 'â†', + prevStatus: 'Bekijk de vorige maand', + prevJumpText: '«', + prevJumpStatus: 'Bekijk het vorige jaar', + nextText: '→', + nextStatus: 'Bekijk de volgende maand', + nextJumpText: '»', + nextJumpStatus: 'Bekijk het volgende jaar', + currentText: 'Vandaag', + currentStatus: 'Bekijk de huidige maand', + todayText: 'Vandaag', + todayStatus: 'Bekijk de huidige maand', + clearText: 'Wissen', + clearStatus: 'Wis de huidige datum', + closeText: 'Sluiten', + closeStatus: 'Sluit zonder verandering', + yearStatus: 'Bekijk een ander jaar', + monthStatus: 'Bekijk een andere maand', + weekText: 'Wk', + weekStatus: 'Week van het jaar', + dayStatus: 'dd-mm-yyyy', + defaultStatus: 'Kies een datum', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.nl); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Norwegian localisation for jQuery Datepicker. + Written by Naimdjon Takhirov (naimdjon@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.no = { + monthNames: ['Januar','Februar','Mars','April','Mai','Juni', + 'Juli','August','September','Oktober','November','Desember'], + monthNamesShort: ['Jan','Feb','Mar','Apr','Mai','Jun', + 'Jul','Aug','Sep','Okt','Nov','Des'], + dayNamesShort: ['Søn','Man','Tir','Ons','Tor','Fre','Lør'], + dayNames: ['Søndag','Mandag','Tirsdag','Onsdag','Torsdag','Fredag','Lørdag'], + dayNamesMin: ['Sø','Ma','Ti','On','To','Fr','Lø'], + dateFormat: 'dd.mm.yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '«Forrige', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Neste»', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'I dag', + currentStatus: '', + todayText: 'I dag', + todayStatus: '', + clearText: 'Tøm', + clearStatus: '', + closeText: 'Lukk', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Uke', + weekStatus: '', + dayStatus: 'D, M d', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.no); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Polish localisation for jQuery Datepicker. + Written by Jacek Wysocki (jacek.wysocki@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.pl = { + monthNames: ['StyczeÅ„','Luty','Marzec','KwiecieÅ„','Maj','Czerwiec', + 'Lipiec','SierpieÅ„','WrzesieÅ„','Październik','Listopad','GrudzieÅ„'], + monthNamesShort: ['Sty','Lu','Mar','Kw','Maj','Cze', + 'Lip','Sie','Wrz','Pa','Lis','Gru'], + dayNames: ['Niedziela','Poniedzialek','Wtorek','Åšroda','Czwartek','PiÄ…tek','Sobota'], + dayNamesShort: ['Nie','Pn','Wt','Åšr','Czw','Pt','So'], + dayNamesMin: ['N','Pn','Wt','Åšr','Cz','Pt','So'], + dateFormat: 'yyyy-mm-dd', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<Poprzedni', + prevStatus: 'Pokaż poprzedni miesiÄ…c', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'NastÄ™pny>', + nextStatus: 'Pokaż nastÄ™pny miesiÄ…c', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'DziÅ›', + currentStatus: 'Pokaż aktualny miesiÄ…c', + todayText: 'DziÅ›', + todayStatus: 'Pokaż aktualny miesiÄ…c', + clearText: 'Wyczyść', + clearStatus: 'Wyczyść obecnÄ… datÄ™', + closeText: 'Zamknij', + closeStatus: 'Zamknij bez zapisywania', + yearStatus: 'Pokaż inny rok', + monthStatus: 'Pokaż inny miesiÄ…c', + weekText: 'Tydz', + weekStatus: 'TydzieÅ„ roku', + dayStatus: '\'Wybierz\' D, M d', + defaultStatus: 'Wybierz datÄ™', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.pl); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Brazilian Portuguese localisation for jQuery Datepicker. + Written by Leonildo Costa Silva (leocsilva@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions['pt-BR'] = { + monthNames: ['Janeiro','Fevereiro','Março','Abril','Maio','Junho', + 'Julho','Agosto','Setembro','Outubro','Novembro','Dezembro'], + monthNamesShort: ['Jan','Fev','Mar','Abr','Mai','Jun', + 'Jul','Ago','Set','Out','Nov','Dez'], + dayNames: ['Domingo','Segunda-feira','Terça-feira','Quarta-feira','Quinta-feira','Sexta-feira','Sábado'], + dayNamesShort: ['Dom','Seg','Ter','Qua','Qui','Sex','Sáb'], + dayNamesMin: ['D','S','T','Q','Q','S','S'], + dateFormat: 'dd/mm/yyyy', + firstDay: 0, + renderer: $.datepick.defaultRenderer, + prevText: '<Anterior', + prevStatus: 'Mostra o mês anterior', + prevJumpText: '<<', + prevJumpStatus: 'Mostra o ano anterior', + nextText: 'Próximo>', + nextStatus: 'Mostra o próximo mês', + nextJumpText: '>>', + nextJumpStatus: 'Mostra o próximo ano', + currentText: 'Atual', + currentStatus: 'Mostra o mês atual', + todayText: 'Hoje', + todayStatus: 'Vai para hoje', + clearText: 'Limpar', + clearStatus: 'Limpar data', + closeText: 'Fechar', + closeStatus: 'Fechar o calendário', + yearStatus: 'Selecionar ano', + monthStatus: 'Selecionar mês', + weekText: 's', + weekStatus: 'Semana do ano', + dayStatus: 'DD, d \'de\' M \'de\' yyyy', + defaultStatus: 'Selecione um dia', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions['pt-BR']); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Portuguese Portuguese localisation for jQuery Datepicker. + Written by Telmo Martinho (telmomartinho@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.pt = { + monthNames: ['Janeiro','Fevereiro','Março','Abril','Maio','Junho', + 'Julho','Agosto','Setembro','Outubro','Novembro','Dezembro'], + monthNamesShort: ['Jan','Fev','Mar','Abr','Mai','Jun', + 'Jul','Ago','Set','Out','Nov','Dez'], + dayNames: ['Domingo','Segunda-feira','Terça-feira','Quarta-feira','Quinta-feira','Sexta-feira','Sábado'], + dayNamesShort: ['Dom','Seg','Ter','Qua','Qui','Sex','Sáb'], + dayNamesMin: ['D','S','T','Q','Q','S','S'], + dateFormat: 'dd/mm/yyyy', + firstDay: 0, + renderer: $.datepick.defaultRenderer, + prevText: '<Anterior', + prevStatus: 'Mês anterior', + prevJumpText: '<<', + prevJumpStatus: 'Ano anterior', + nextText: 'Próximo>', + nextStatus: 'Próximo mês', + nextJumpText: '>>', + nextJumpStatus: 'Próximo ano', + currentText: 'Atual', + currentStatus: 'Mês atual', + todayText: 'Hoje', + todayStatus: 'Hoje', + clearText: 'Limpar', + clearStatus: 'Limpar data', + closeText: 'Fechar', + closeStatus: 'Fechar o calendário', + yearStatus: 'Selecionar ano', + monthStatus: 'Selecionar mês', + weekText: 's', + weekStatus: 'Semana do ano', + dayStatus: 'DD, d \'de\' M \'de\' yyyy', + defaultStatus: 'Selecione um dia', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.pt); +})(jQuery); +/* http://keith-wood.name/datepick.html + Romansh localisation for jQuery Datepicker. + Yvonne Gienal (yvonne.gienal@educa.ch). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.rm = { + monthNames: ['Schaner','Favrer','Mars','Avrigl','Matg','Zercladur', + 'Fanadur','Avust','Settember','October','November','December'], + monthNamesShort: ['Scha','Fev','Mar','Avr','Matg','Zer', + 'Fan','Avu','Sett','Oct','Nov','Dec'], + dayNames: ['Dumengia','Glindesdi','Mardi','Mesemna','Gievgia','Venderdi','Sonda'], + dayNamesShort: ['Dum','Gli','Mar','Mes','Gie','Ven','Som'], + dayNamesMin: ['Du','Gl','Ma','Me','Gi','Ve','So'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<Suandant', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Precedent>', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Actual', + currentStatus: '', + todayText: 'Actual', + todayStatus: '', + clearText: 'X', + clearStatus: '', + closeText: 'Serrar', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'emna', + weekStatus: '', + dayStatus: 'DD d MM', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.rm); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Romanian localisation for jQuery Datepicker. + Written by Edmond L. (ll_edmond@walla.com) and Ionut G. Stan (ionut.g.stan@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.ro = { + monthNames: ['Ianuarie','Februarie','Martie','Aprilie','Mai','Iunie', + 'Iulie','August','Septembrie','Octombrie','Noiembrie','Decembrie'], + monthNamesShort: ['Ian','Feb','Mar','Apr','Mai','Iun', + 'Iul','Aug','Sep','Oct','Noi','Dec'], + dayNames: ['Duminică','Luni','Marti','Miercuri','Joi','Vineri','Sâmbătă'], + dayNamesShort: ['Dum','Lun','Mar','Mie','Joi','Vin','Sâm'], + dayNamesMin: ['Du','Lu','Ma','Mi','Jo','Vi','Sâ'], + dateFormat: 'dd.mm.yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '«Precedentă', + prevStatus: 'Arată luna precedenta', + prevJumpText: '««', + prevJumpStatus: '', + nextText: 'Urmatoare»', + nextStatus: 'Arată luna urmatoare', + nextJumpText: '»»', + nextJumpStatus: '', + currentText: 'Azi', + currentStatus: 'Arată luna curenta', + todayText: 'Azi', + todayStatus: 'Arată luna curenta', + clearText: 'Curat', + clearStatus: 'Sterge data curenta', + closeText: 'ÃŽnchide', + closeStatus: 'ÃŽnchide fara schimbare', + yearStatus: 'Arată un an diferit', + monthStatus: 'Arată o luna diferita', + weekText: 'Săpt', + weekStatus: 'Săptamana anului', + dayStatus: 'Selectează D, M d', + defaultStatus: 'Selectează o data', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.ro); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Russian localisation for jQuery Datepicker. + Written by Andrew Stromnov (stromnov@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.ru = { + monthNames: ['Январь','Февраль','Март','Ðпрель','Май','Июнь', + 'Июль','ÐвгуÑÑ‚','СентÑбрь','ОктÑбрь','ÐоÑбрь','Декабрь'], + monthNamesShort: ['Янв','Фев','Мар','Ðпр','Май','Июн', + 'Июл','Ðвг','Сен','Окт','ÐоÑ','Дек'], + dayNames: ['воÑкреÑенье','понедельник','вторник','Ñреда','четверг','пÑтница','Ñуббота'], + dayNamesShort: ['вÑк','пнд','втр','Ñрд','чтв','птн','Ñбт'], + dayNamesMin: ['Ð’Ñ','Пн','Ð’Ñ‚','Ср','Чт','Пт','Сб'], + dateFormat: 'dd.mm.yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<Пред', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'След>', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'СегоднÑ', + currentStatus: '', + todayText: 'СегоднÑ', + todayStatus: '', + clearText: 'ОчиÑтить', + clearStatus: '', + closeText: 'Закрыть', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Ðе', + weekStatus: '', + dayStatus: 'D, M d', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.ru); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Slovak localisation for jQuery Datepicker. + Written by Vojtech Rinik (vojto@hmm.sk). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.sk = { + monthNames: ['Január','Február','Marec','Apríl','Máj','Jún', + 'Júl','August','September','Október','November','December'], + monthNamesShort: ['Jan','Feb','Mar','Apr','Máj','Jún', + 'Júl','Aug','Sep','Okt','Nov','Dec'], + dayNames: ['Nedel\'a','Pondelok','Utorok','Streda','Å tvrtok','Piatok','Sobota'], + dayNamesShort: ['Ned','Pon','Uto','Str','Å tv','Pia','Sob'], + dayNamesMin: ['Ne','Po','Ut','St','Å t','Pia','So'], + dateFormat: 'dd.mm.yyyy', + firstDay: 0, + renderer: $.datepick.defaultRenderer, + prevText: '<Predchádzajúci', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Nasledujúci>', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Dnes', + currentStatus: '', + todayText: 'Dnes', + todayStatus: '', + clearText: 'ZmazaÅ¥', + clearStatus: '', + closeText: 'ZavrieÅ¥', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Ty', + weekStatus: '', + dayStatus: 'D, M d', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.sk); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Slovenian localisation for jQuery Datepicker. + Written by Jaka Jancar (jaka@kubje.org). */ +/* c = č, s = š z = ž C = Č S = Š Z = Ž */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.sl = { + monthNames: ['Januar','Februar','Marec','April','Maj','Junij', + 'Julij','Avgust','September','Oktober','November','December'], + monthNamesShort: ['Jan','Feb','Mar','Apr','Maj','Jun', + 'Jul','Avg','Sep','Okt','Nov','Dec'], + dayNames: ['Nedelja','Ponedeljek','Torek','Sreda','Četrtek','Petek','Sobota'], + dayNamesShort: ['Ned','Pon','Tor','Sre','Čet','Pet','Sob'], + dayNamesMin: ['Ne','Po','To','Sr','Če','Pe','So'], + dateFormat: 'dd.mm.yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<Prejšnji', + prevStatus: 'Prikaži prejšnji mesec', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Naslednji>', + nextStatus: 'Prikaži naslednji mesec', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Trenutni', + currentStatus: 'Prikaži trenutni mesec', + todayText: 'Trenutni', + todayStatus: 'Prikaži trenutni mesec', + clearText: 'Izbriši', + clearStatus: 'Izbriši trenutni datum', + closeText: 'Zapri', + closeStatus: 'Zapri brez spreminjanja', + yearStatus: 'Prikaži drugo leto', + monthStatus: 'Prikaži drug mesec', + weekText: 'Teden', + weekStatus: 'Teden v letu', + dayStatus: 'Izberi DD, d MM yy', + defaultStatus: 'Izbira datuma', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.sl); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Albanian localisation for jQuery Datepicker. + Written by Flakron Bytyqi (flakron@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.sq = { + monthNames: ['Janar','Shkurt','Mars','Prill','Maj','Qershor', + 'Korrik','Gusht','Shtator','Tetor','Nëntor','Dhjetor'], + monthNamesShort: ['Jan','Shk','Mar','Pri','Maj','Qer', + 'Kor','Gus','Sht','Tet','Nën','Dhj'], + dayNames: ['E Diel','E Hënë','E Martë','E Mërkurë','E Enjte','E Premte','E Shtune'], + dayNamesShort: ['Di','Hë','Ma','Më','En','Pr','Sh'], + dayNamesMin: ['Di','Hë','Ma','Më','En','Pr','Sh'], + dateFormat: 'dd.mm.yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<mbrapa', + prevStatus: 'trego muajin e fundit', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Përpara>', + nextStatus: 'trego muajin tjetër', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'sot', + currentStatus: '', + todayText: 'sot', + todayStatus: '', + clearText: 'fshije', + clearStatus: 'fshije datën aktuale', + closeText: 'mbylle', + closeStatus: 'mbylle pa ndryshime', + yearStatus: 'trego tjetër vit', + monthStatus: 'trego muajin tjetër', + weekText: 'Ja', + weekStatus: 'Java e muajit', + dayStatus: '\'Zgjedh\' D, M d', + defaultStatus: 'Zgjedhe një datë', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.sq); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Serbian localisation for jQuery Datepicker. + Written by Dejan Dimić. */ +(function($){ + 'use strict'; + $.datepick.regionalOptions['sr-SR'] = { + monthNames: ['Januar','Februar','Mart','April','Maj','Jun', + 'Jul','Avgust','Septembar','Oktobar','Novembar','Decembar'], + monthNamesShort: ['Jan','Feb','Mar','Apr','Maj','Jun', + 'Jul','Avg','Sep','Okt','Nov','Dec'], + dayNames: ['Nedelja','Ponedeljak','Utorak','Sreda','ÄŒetvrtak','Petak','Subota'], + dayNamesShort: ['Ned','Pon','Uto','Sre','ÄŒet','Pet','Sub'], + dayNamesMin: ['Ne','Po','Ut','Sr','ÄŒe','Pe','Su'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<', + prevStatus: 'Prikaži prethodni mesec', + prevJumpText: '<<', + prevJumpStatus: 'Prikaži prethodnu godinu', + nextText: '>', + nextStatus: 'Prikaži sledeći mesec', + nextJumpText: '>>', + nextJumpStatus: 'Prikaži sledeću godinu', + currentText: 'Danas', + currentStatus: 'Tekući mesec', + todayText: 'Danas', + todayStatus: 'Tekući mesec', + clearText: 'ObriÅ¡i', + clearStatus: 'ObriÅ¡i trenutni datum', + closeText: 'Zatvori', + closeStatus: 'Zatvori kalendar', + yearStatus: 'Prikaži godine', + monthStatus: 'Prikaži mesece', + weekText: 'Sed', + weekStatus: 'Sedmica', + dayStatus: '\'Datum\' D, M d', + defaultStatus: 'Odaberi datum', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions['sr-SR']); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Serbian localisation for jQuery Datepicker. + Written by Dejan Dimić. */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.sr = { + monthNames: ['Јануар','Фебруар','Март','Ðприл','Мај','Јун', + 'Јул','ÐвгуÑÑ‚','Септембар','Октобар','Ðовембар','Децембар'], + monthNamesShort: ['Јан','Феб','Мар','Ðпр','Мај','Јун', + 'Јул','Ðвг','Сеп','Окт','Ðов','Дец'], + dayNames: ['Ðедеља','Понедељак','Уторак','Среда','Четвртак','Петак','Субота'], + dayNamesShort: ['Ðед','Пон','Уто','Сре','Чет','Пет','Суб'], + dayNamesMin: ['Ðе','По','Ут','Ср','Че','Пе','Су'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<', + prevStatus: 'Прикажи претходни меÑец', + prevJumpText: '<<', + prevJumpStatus: 'Прикажи претходну годину', + nextText: '>', + nextStatus: 'Прикажи Ñледећи меÑец', + nextJumpText: '>>', + nextJumpStatus: 'Прикажи Ñледећу годину', + currentText: 'ДанаÑ', + currentStatus: 'Текући меÑец', + todayText: 'ДанаÑ', + todayStatus: 'Текући меÑец', + clearText: 'Обриши', + clearStatus: 'Обриши тренутни датум', + closeText: 'Затвори', + closeStatus: 'Затвори календар', + yearStatus: 'Прикажи године', + monthStatus: 'Прикажи меÑеце', + weekText: 'Сед', + weekStatus: 'Седмица', + dayStatus: '\'Датум\' DD d MM', + defaultStatus: 'Одабери датум', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.sr); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Swedish localisation for jQuery Datepicker. + Written by Anders Ekdahl ( anders@nomadiz.se). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.sv = { + monthNames: ['Januari','Februari','Mars','April','Maj','Juni', + 'Juli','Augusti','September','Oktober','November','December'], + monthNamesShort: ['Jan','Feb','Mar','Apr','Maj','Jun', + 'Jul','Aug','Sep','Okt','Nov','Dec'], + dayNames: ['Söndag','MÃ¥ndag','Tisdag','Onsdag','Torsdag','Fredag','Lördag'], + dayNamesShort: ['Sön','MÃ¥n','Tis','Ons','Tor','Fre','Lör'], + dayNamesMin: ['Sö','MÃ¥','Ti','On','To','Fr','Lö'], + dateFormat: 'yyyy-mm-dd', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '«Förra', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'Nästa»', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Idag', + currentStatus: '', + todayText: 'Idag', + todayStatus: '', + clearText: 'Rensa', + clearStatus: '', + closeText: 'Stäng', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Ve', + weekStatus: '', + dayStatus: 'D, M d', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.sv); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Tamil localisation for jQuery Datepicker. + Written by S A Sureshkumar (saskumar@live.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.ta = { + monthNames: ['தை','மாசி','பஙà¯à®•à¯à®©à®¿','சிதà¯à®¤à®¿à®°à¯ˆ','வைகாசி','ஆனி', + 'ஆடி','ஆவணி','பà¯à®°à®Ÿà¯à®Ÿà®¾à®šà®¿','à®à®ªà¯à®ªà®šà®¿','காரà¯à®¤à¯à®¤à®¿à®•ை','மாரà¯à®•ழி'], + monthNamesShort: ['தை','மாசி','பஙà¯','சிதà¯','வைகா','ஆனி', + 'ஆடி','ஆவ','பà¯à®°','à®à®ªà¯','காரà¯','மாரà¯'], + dayNames: ['ஞாயிறà¯à®±à¯à®•à¯à®•ிழமை','திஙà¯à®•டà¯à®•ிழமை','செவà¯à®µà®¾à®¯à¯à®•à¯à®•ிழமை','பà¯à®¤à®©à¯à®•ிழமை','வியாழகà¯à®•ிழமை','வெளà¯à®³à®¿à®•à¯à®•ிழமை','சனிகà¯à®•ிழமை'], + dayNamesShort: ['ஞாயிறà¯','திஙà¯à®•ளà¯','செவà¯à®µà®¾à®¯à¯','பà¯à®¤à®©à¯','வியாழனà¯','வெளà¯à®³à®¿','சனி'], + dayNamesMin: ['ஞா','தி','செ','பà¯','வி','வெ','ச'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: 'à®®à¯à®©à¯à®©à¯ˆà®¯à®¤à¯', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'அடà¯à®¤à¯à®¤à®¤à¯', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'இனà¯à®±à¯', + currentStatus: '', + todayText: 'இனà¯à®±à¯', + todayStatus: '', + clearText: 'அழி', + clearStatus: '', + closeText: 'மூடà¯', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Wk', + weekStatus: '', + dayStatus: 'D, M d', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.ta); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Thai localisation for jQuery Datepicker. + Written by pipo (pipo@sixhead.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.th = { + monthNames: ['มà¸à¸£à¸²à¸„ม','à¸à¸¸à¸¡à¸ à¸²à¸žà¸±à¸™à¸˜à¹Œ','มีนาคม','เมษายน','พฤษภาคม','มิถุนายน', + 'à¸à¸£à¸à¸Žà¸²à¸„ม','สิงหาคม','à¸à¸±à¸™à¸¢à¸²à¸¢à¸™','ตุลาคม','พฤศจิà¸à¸²à¸¢à¸™','ธันวาคม'], + monthNamesShort: ['ม.ค.','à¸.พ.','มี.ค.','เม.ย.','พ.ค.','มิ.ย.', + 'à¸.ค.','ส.ค.','à¸.ย.','ต.ค.','พ.ย.','ธ.ค.'], + dayNames: ['อาทิตย์','จันทร์','อังคาร','พุธ','พฤหัสบดี','ศุà¸à¸£à¹Œ','เสาร์'], + dayNamesShort: ['อา.','จ.','อ.','พ.','พฤ.','ศ.','ส.'], + dayNamesMin: ['อา.','จ.','อ.','พ.','พฤ.','ศ.','ส.'], + dateFormat: 'dd/mm/yyyy', + firstDay: 0, + renderer: $.datepick.defaultRenderer, + prevText: '« à¸¢à¹‰à¸­à¸™', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'ถัดไป »', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'วันนี้', + currentStatus: '', + todayText: 'วันนี้', + todayStatus: '', + clearText: 'ลบ', + clearStatus: '', + closeText: 'ปิด', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Wk', + weekStatus: '', + dayStatus: 'D, M d', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.th); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Turkish localisation for jQuery Datepicker. + Written by Izzet Emre Erkan (kara@karalamalar.net). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.tr = { + monthNames: ['Ocak','Åžubat','Mart','Nisan','Mayıs','Haziran', + 'Temmuz','AÄŸustos','Eylül','Ekim','Kasım','Aralık'], + monthNamesShort: ['Oca','Åžub','Mar','Nis','May','Haz', + 'Tem','AÄŸu','Eyl','Eki','Kas','Ara'], + dayNames: ['Pazar','Pazartesi','Salı','ÇarÅŸamba','PerÅŸembe','Cuma','Cumartesi'], + dayNamesShort: ['Pz','Pt','Sa','Ça','Pe','Cu','Ct'], + dayNamesMin: ['Pz','Pt','Sa','Ça','Pe','Cu','Ct'], + dateFormat: 'dd.mm.yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<geri', + prevStatus: 'önceki ayı göster', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: 'ileri>', + nextStatus: 'sonraki ayı göster', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'bugün', + currentStatus: '', + todayText: 'bugün', + todayStatus: '', + clearText: 'temizle', + clearStatus: 'geçerli tarihi temizler', + closeText: 'kapat', + closeStatus: 'sadece göstergeyi kapat', + yearStatus: 'baÅŸka yıl', + monthStatus: 'baÅŸka ay', + weekText: 'Hf', + weekStatus: 'Ayın haftaları', + dayStatus: 'D, M d seçiniz', + defaultStatus: 'Bir tarih seçiniz', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.tr); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Tatar localisation for jQuery Datepicker. + Written by Irek Khaziev (khazirek@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.tt = { + monthNames: ['Гынвар','Февраль','Март','Ðпрель','Май','Июнь', + 'Июль','ÐвгуÑÑ‚','СентÑбрь','ОктÑбрь','ÐоÑбрь','Декабрь'], + monthNamesShort: ['Гыйн','Фев','Мар','Ðпр','Май','Июн', + 'Июл','Ðвг','Сен','Окт','ÐоÑ','Дек'], + dayNames: ['Ñкшәмбе','дүшәмбе','Ñишәмбе','чәршәмбе','пәнҗешәмбе','җомга','шимбә'], + dayNamesShort: ['Ñкш','дүш','Ñиш','чәр','пән','җом','шим'], + dayNamesMin: ['Як','Дү','Си','Чә','Пә','Җо','Ши'], + dateFormat: 'dd.mm.yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: 'Ðлдагы', + prevStatus: 'Ðлдагы айны күрÑәтү', + prevJumpText: '<<', + prevJumpStatus: 'Ðлдагы елны күрÑәтү', + nextText: 'КиләÑе', + nextStatus: 'КиләÑе айны күрÑәтү', + nextJumpText: '>>', + nextJumpStatus: 'КиләÑе елны күрÑәтү', + currentText: 'Хәзер', + currentStatus: 'Хәзерге айны күрÑәтү', + todayText: 'Бүген', + todayStatus: 'Бүгенге айны күрÑәтү', + clearText: 'ЧиÑтарту', + clearStatus: 'Барлык көннәрне чиÑтарту', + closeText: 'Ябарга', + closeStatus: 'Көн Ñайлауны Ñбарга', + yearStatus: 'Елны кертегез', + monthStatus: 'Ðйны кертегез', + weekText: 'Ðтна', + weekStatus: 'Елда атна Ñаны', + dayStatus: 'DD, M d', + defaultStatus: 'Көнне Ñайлагыз', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.tt); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Ukrainian localisation for jQuery Datepicker. + Written by Maxim Drogobitskiy (maxdao@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.uk = { + monthNames: ['Січень','Лютий','Березень','Квітень','Травень','Червень', + 'Липень','Серпень','ВереÑень','Жовтень','ЛиÑтопад','Грудень'], + monthNamesShort: ['Січ','Лют','Бер','Кві','Тра','Чер', + 'Лип','Сер','Вер','Жов','ЛиÑ','Гру'], + dayNames: ['неділÑ','понеділок','вівторок','Ñереда','четвер','п\'ÑтницÑ','Ñубота'], + dayNamesShort: ['нед','пнд','вів','Ñрд','чтв','птн','Ñбт'], + dayNamesMin: ['Ðд','Пн','Ð’Ñ‚','Ср','Чт','Пт','Сб'], + dateFormat: 'dd/mm/yyyy', + firstDay: 1, + renderer: $.datepick.defaultRenderer, + prevText: '<', + prevStatus: '', + prevJumpText: '<<', + prevJumpStatus: '', + nextText: '>', + nextStatus: '', + nextJumpText: '>>', + nextJumpStatus: '', + currentText: 'Сьогодні', + currentStatus: '', + todayText: 'Сьогодні', + todayStatus: '', + clearText: 'ОчиÑтити', + clearStatus: '', + closeText: 'Закрити', + closeStatus: '', + yearStatus: '', + monthStatus: '', + weekText: 'Ðе', + weekStatus: '', + dayStatus: 'D, M d', + defaultStatus: '', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.uk); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Urdu localisation for jQuery Datepicker. + Mansoor Munib -- mansoormunib@gmail.com + Thanks to Habib Ahmed, ObaidUllah Anwar. */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.ur = { + monthNames: ['جنوری','ÙØ±ÙˆØ±ÛŒ','مارچ','اپریل','مئی','جون', + 'جولائی','اگست','ستمبر','اکتوبر','نومبر','دسمبر'], + monthNamesShort: ['1','2','3','4','5','6', + '7','8','9','10','11','12'], + dayNames: ['اتوار','پير','منگل','بدھ','جمعرات','جمعÛ','ÛÙØªÛ'], + dayNamesShort: ['اتوار','پير','منگل','بدھ','جمعرات','جمعÛ','ÛÙØªÛ'], + dayNamesMin: ['اتوار','پير','منگل','بدھ','جمعرات','جمعÛ','ÛÙØªÛ'], + dateFormat: 'dd/mm/yyyy', + firstDay: 0, + renderer: $.datepick.defaultRenderer, + prevText: '<گذشتÛ', + prevStatus: 'ماه گذشتÛ', + prevJumpText: '<<', + prevJumpStatus: 'برس گذشتÛ', + nextText: 'آئندÛ>', + nextStatus: 'ماه آئندÛ', + nextJumpText: '>>', + nextJumpStatus: 'برس آئندÛ', + currentText: 'رواں', + currentStatus: 'ماه رواں', + todayText: 'آج', + todayStatus: 'آج', + clearText: 'حذ٠تاريخ', + clearStatus: 'کریں حذ٠تاریخ', + closeText: 'کریں بند', + closeStatus: 'کیلئے کرنے بند', + yearStatus: 'برس تبدیلی', + monthStatus: 'ماه تبدیلی', + weekText: 'ÛÙØªÛ', + weekStatus: 'ÛÙØªÛ', + dayStatus: 'انتخاب D, M d', + defaultStatus: 'کریں منتخب تاريخ', + isRTL: true + }; + $.datepick.setDefaults($.datepick.regionalOptions.ur); +})(jQuery); +/* http://keith-wood.name/datepick.html + Vietnamese localisation for jQuery Datepicker. + Translated by Le Thanh Huy (lthanhhuy@cit.ctu.edu.vn). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions.vi = { + monthNames: ['Tháng Má»™t', 'Tháng Hai', 'Tháng Ba', 'Tháng Tư', 'Tháng Năm', 'Tháng Sáu', + 'Tháng Bảy', 'Tháng Tám', 'Tháng Chín', 'Tháng Mưá»i', 'Tháng Mưá»i Má»™t', 'Tháng Mưá»i Hai'], + monthNamesShort: ['Tháng 1', 'Tháng 2', 'Tháng 3', 'Tháng 4', 'Tháng 5', 'Tháng 6', + 'Tháng 7', 'Tháng 8', 'Tháng 9', 'Tháng 10', 'Tháng 11', 'Tháng 12'], + dayNames: ['Chá»§ Nhật', 'Thứ Hai', 'Thứ Ba', 'Thứ Tư', 'Thứ Năm', 'Thứ Sáu', 'Thứ Bảy'], + dayNamesShort: ['CN', 'T2', 'T3', 'T4', 'T5', 'T6', 'T7'], + dayNamesMin: ['CN', 'T2', 'T3', 'T4', 'T5', 'T6', 'T7'], + dateFormat: 'dd/mm/yyyy', + firstDay: 0, + renderer: $.datepick.defaultRenderer, + prevText: '<Trước', + prevStatus: 'Tháng trước', + prevJumpText: '<<', + prevJumpStatus: 'Năm trước', + nextText: 'Tiếp>', + nextStatus: 'Tháng sau', + nextJumpText: '>>', + nextJumpStatus: 'Năm sau', + currentText: 'Hôm nay', + currentStatus: 'Tháng hiện tại', + todayText: 'Hôm nay', + todayStatus: 'Tháng hiện tại', + clearText: 'Xóa', + clearStatus: 'Xóa ngày hiện tại', + closeText: 'Äóng', + closeStatus: 'Äóng và không lưu lại thay đổi', + yearStatus: 'Năm khác', + monthStatus: 'Tháng khác', + weekText: 'Tu', + weekStatus: 'Tuần trong năm', + dayStatus: 'Äang chá»n DD, \'ngày\' d M', + defaultStatus: 'Chá»n ngày', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions.vi); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Simplified Chinese localisation for jQuery Datepicker. + Written by Cloudream (cloudream@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions['zh-CN'] = { + monthNames: ['一月','二月','三月','四月','五月','六月', + '七月','八月','乿œˆ','åæœˆ','å一月','å二月'], + monthNamesShort: ['一','二','三','å››','五','å…­', + '七','å…«','ä¹','å','å一','å二'], + dayNames: ['星期日','星期一','星期二','星期三','星期四','星期五','星期六'], + dayNamesShort: ['周日','周一','周二','周三','周四','周五','周六'], + dayNamesMin: ['æ—¥','一','二','三','å››','五','å…­'], + dateFormat: 'yyyy-mm-dd', + firstDay: 1, + renderer: $.extend({}, $.datepick.defaultRenderer, + {month: $.datepick.defaultRenderer.month. + replace(/monthHeader/, 'monthHeader:MM yyyyå¹´')}), + prevText: '<上月', + prevStatus: '显示上月', + prevJumpText: '<<', + prevJumpStatus: '显示上一年', + nextText: '下月>', + nextStatus: '显示下月', + nextJumpText: '>>', + nextJumpStatus: '显示下一年', + currentText: '今天', + currentStatus: '显示本月', + todayText: '今天', + todayStatus: '显示本月', + clearText: '清除', + clearStatus: '清除已选日期', + closeText: '关闭', + closeStatus: '䏿”¹å˜å½“å‰é€‰æ‹©', + yearStatus: '选择年份', + monthStatus: '选择月份', + weekText: '周', + weekStatus: '年内周次', + dayStatus: '选择 m月 dæ—¥, DD', + defaultStatus: '请选择日期', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions['zh-CN']); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Hong Kong Chinese localisation for jQuery Datepicker. + Written by SCCY (samuelcychan@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions['zh-HK'] = { + monthNames: ['一月','二月','三月','四月','五月','六月', + '七月','八月','乿œˆ','åæœˆ','å一月','å二月'], + monthNamesShort: ['一','二','三','å››','五','å…­', + '七','å…«','ä¹','å','å一','å二'], + dayNames: ['星期日','星期一','星期二','星期三','星期四','星期五','星期六'], + dayNamesShort: ['周日','周一','周二','周三','周四','周五','周六'], + dayNamesMin: ['æ—¥','一','二','三','å››','五','å…­'], + dateFormat: 'dd-mm-yyyy', + firstDay: 0, + renderer: $.extend({}, $.datepick.defaultRenderer, + {month: $.datepick.defaultRenderer.month. + replace(/monthHeader/, 'monthHeader:yyyyå¹´ MM')}), + prevText: '<上月', + prevStatus: '顯示上月', + prevJumpText: '<<', + prevJumpStatus: '顯示上一年', + nextText: '下月>', + nextStatus: '顯示下月', + nextJumpText: '>>', + nextJumpStatus: '顯示下一年', + currentText: '今天', + currentStatus: '顯示本月', + todayText: '今天', + todayStatus: '顯示本月', + clearText: '清除', + clearStatus: 'æ¸…é™¤å·²é¸æ—¥æœŸ', + closeText: '關閉', + closeStatus: '䏿”¹è®Šç›®å‰çš„鏿“‡', + yearStatus: '鏿“‡å¹´ä»½', + monthStatus: '鏿“‡æœˆä»½', + weekText: '周', + weekStatus: '年內周次', + dayStatus: '鏿“‡ m月 dæ—¥, DD', + defaultStatus: 'è«‹é¸æ“‡æ—¥æœŸ', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions['zh-HK']); +})(jQuery); + +/* http://keith-wood.name/datepick.html + Traditional Chinese localisation for jQuery Datepicker. + Written by Ressol (ressol@gmail.com). */ +(function($) { + 'use strict'; + $.datepick.regionalOptions['zh-TW'] = { + monthNames: ['一月','二月','三月','四月','五月','六月', + '七月','八月','乿œˆ','åæœˆ','å一月','å二月'], + monthNamesShort: ['一','二','三','å››','五','å…­', + '七','å…«','ä¹','å','å一','å二'], + dayNames: ['星期日','星期一','星期二','星期三','星期四','星期五','星期六'], + dayNamesShort: ['周日','周一','周二','周三','周四','周五','周六'], + dayNamesMin: ['æ—¥','一','二','三','å››','五','å…­'], + dateFormat: 'yyyy/mm/dd', + firstDay: 1, + renderer: $.extend({}, $.datepick.defaultRenderer, + {month: $.datepick.defaultRenderer.month. + replace(/monthHeader/, 'monthHeader:MM yyyyå¹´')}), + prevText: '<上月', + prevStatus: '顯示上月', + prevJumpText: '<<', + prevJumpStatus: '顯示上一年', + nextText: '下月>', + nextStatus: '顯示下月', + nextJumpText: '>>', + nextJumpStatus: '顯示下一年', + currentText: '今天', + currentStatus: '顯示本月', + todayText: '今天', + todayStatus: '顯示本月', + clearText: '清除', + clearStatus: 'æ¸…é™¤å·²é¸æ—¥æœŸ', + closeText: '關閉', + closeStatus: '䏿”¹è®Šç›®å‰çš„鏿“‡', + yearStatus: '鏿“‡å¹´ä»½', + monthStatus: '鏿“‡æœˆä»½', + weekText: '周', + weekStatus: '年內周次', + dayStatus: '鏿“‡ m月 dæ—¥, DD', + defaultStatus: 'è«‹é¸æ“‡æ—¥æœŸ', + isRTL: false + }; + $.datepick.setDefaults($.datepick.regionalOptions['zh-TW']); +})(jQuery); diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick.lang.min.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick.lang.min.js new file mode 100755 index 000000000..ff2b2f8dc --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick.lang.min.js @@ -0,0 +1,6 @@ +/*! http://keith-wood.name/datepick.html + Datepicker localisations. */ +!function(a){"use strict";a.datepick.regionalOptions.af={monthNames:["Januarie","Februarie","Maart","April","Mei","Junie","Julie","Augustus","September","Oktober","November","Desember"],monthNamesShort:["Jan","Feb","Mrt","Apr","Mei","Jun","Jul","Aug","Sep","Okt","Nov","Des"],dayNames:["Sondag","Maandag","Dinsdag","Woensdag","Donderdag","Vrydag","Saterdag"],dayNamesShort:["Son","Maan","Dins","Woens","Don","Vry","Sat"],dayNamesMin:["So","Ma","Di","Wo","Do","Vr","Sa"],dateFormat:"dd/mm/yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"Vorige",prevStatus:"Vertoon vorige maand",prevJumpText:"<<",prevJumpStatus:"Vertoon vorige jaar",nextText:"Volgende",nextStatus:"Vertoon volgende maand",nextJumpText:">>",nextJumpStatus:"Vertoon volgende jaar",currentText:"Vandag",currentStatus:"Vertoon huidige maand",todayText:"Vandag",todayStatus:"Vertoon huidige maand",clearText:"Vee uit",clearStatus:"Verwyder die huidige datum",closeText:"Klaar",closeStatus:"Sluit sonder verandering",yearStatus:"Vertoon 'n ander jaar",monthStatus:"Vertoon 'n ander maand",weekText:"Wk",weekStatus:"Week van die jaar",dayStatus:"Kies DD, M d",defaultStatus:"Kies 'n datum",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.af)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.am={monthNames:["ጃንዋሪ","áˆá‰¥áˆ­á‹‹áˆª","ማርች","አá•ሪáˆ","ሜይ","áŒáŠ•","áŒáˆ‹á‹­","ኦገስት","ሴá•ቴáˆá‰ áˆ­","ኦክቶበር","ኖቬáˆá‰ áˆ­","ዲሴáˆá‰ áˆ­"],monthNamesShort:["ጃንዋ","áˆá‰¥áˆ­","ማርች","አá•ሪ","ሜይ","áŒáŠ•","áŒáˆ‹á‹­","ኦገስ","ሴá•ቴ","ኦክቶ","ኖቬáˆ","ዲሴáˆ"],dayNames:["ሰንዴይ","መንዴይ","ትዩስዴይ","ዌንስዴይ","ተርሰዴይ","áራይዴይ","ሳተርዴይ"],dayNamesShort:["ሰንዴ","መንዴ","ትዩስ","ዌንስ","ተርሰ","áራይ","ሳተር"],dayNamesMin:["ሰን","መን","ትዩ","ዌን","ተር","áራ","ሳተ"],dateFormat:"dd/mm/yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"ያለáˆ",prevStatus:"ያለáˆá‹áŠ• ወር አሳይ",prevJumpText:"<<",prevJumpStatus:"ያለáˆá‹áŠ• ዓመት አሳይ",nextText:"ቀጣይ",nextStatus:"ቀጣዩን ወር አሳይ",nextJumpText:">>",nextJumpStatus:"ቀጣዩን ዓመት አሳይ",currentText:"አáˆáŠ•",currentStatus:"የአáˆáŠ‘áŠ• ወር አሳይ",todayText:"ዛሬ",todayStatus:"የዛሬን ወር አሳይ",clearText:"አጥá‹",clearStatus:"የተመረጠá‹áŠ• ቀን አጥá‹",closeText:"á‹áŒ‹",closeStatus:"የቀን መáˆáˆ¨áŒ«á‹áŠ• á‹áŒ‹",yearStatus:"ዓመቱን ቀይር",monthStatus:"ወሩን ቀይር",weekText:"ሳáˆ",weekStatus:"የዓመቱ ሳáˆáŠ•á‰µ ",dayStatus:"DD, M d, yyyy áˆáˆ¨áŒ¥",defaultStatus:"ቀን áˆáˆ¨áŒ¥",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.am)}(jQuery),function(a){"use strict";a.datepick.regionalOptions["ar-DZ"]={monthNames:["جانÙÙŠ","ÙÙŠÙØ±ÙŠ","مارس","Ø£ÙØ±ÙŠÙ„","ماي","جوان","جويلية","أوت","سبتمبر","أكتوبر","نوÙمبر","ديسمبر"],monthNamesShort:["1","2","3","4","5","6","7","8","9","10","11","12"],dayNames:["الأحد","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت"],dayNamesShort:["الأحد","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت"],dayNamesMin:["الأحد","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت"],dateFormat:"dd/mm/yyyy",firstDay:6,renderer:a.datepick.defaultRenderer,prevText:"<السابق",prevStatus:"عرض الشهر السابق",prevJumpText:"<<",prevJumpStatus:"",nextText:"التالي>",nextStatus:"عرض الشهر القادم",nextJumpText:">>",nextJumpStatus:"",currentText:"اليوم",currentStatus:"عرض الشهر الحالي",todayText:"اليوم",todayStatus:"عرض الشهر الحالي",clearText:"مسح",clearStatus:"امسح التاريخ الحالي",closeText:"إغلاق",closeStatus:"إغلاق بدون Ø­ÙØ¸",yearStatus:"عرض سنة آخرى",monthStatus:"عرض شهر آخر",weekText:"أسبوع",weekStatus:"أسبوع السنة",dayStatus:"اختر D, M d",defaultStatus:"اختر يوم",isRTL:!0},a.datepick.setDefaults(a.datepick.regionalOptions["ar-DZ"])}(jQuery),function(a){"use strict";a.datepick.regionalOptions["ar-EG"]={monthNames:["يناير","ÙØ¨Ø±Ø§ÙŠØ±","مارس","إبريل","مايو","يونية","يوليو","أغسطس","سبتمبر","أكتوبر","نوÙمبر","ديسمبر"],monthNamesShort:["1","2","3","4","5","6","7","8","9","10","11","12"],dayNames:["الأحد","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت"],dayNamesShort:["أحد","اثنين","ثلاثاء","أربعاء","خميس","جمعة","سبت"],dayNamesMin:["أحد","اثنين","ثلاثاء","أربعاء","خميس","جمعة","سبت"],dateFormat:"dd/mm/yyyy",firstDay:6,renderer:a.datepick.defaultRenderer,prevText:"<السابق",prevStatus:"عرض الشهر السابق",prevJumpText:"<<",prevJumpStatus:"",nextText:"التالي>",nextStatus:"عرض الشهر القادم",nextJumpText:">>",nextJumpStatus:"",currentText:"اليوم",currentStatus:"عرض الشهر الحالي",todayText:"اليوم",todayStatus:"عرض الشهر الحالي",clearText:"مسح",clearStatus:"امسح التاريخ الحالي",closeText:"إغلاق",closeStatus:"إغلاق بدون Ø­ÙØ¸",yearStatus:"عرض سنة آخرى",monthStatus:"عرض شهر آخر",weekText:"أسبوع",weekStatus:"أسبوع السنة",dayStatus:"اختر D, M d",defaultStatus:"اختر يوم",isRTL:!0},a.datepick.setDefaults(a.datepick.regionalOptions["ar-EG"])}(jQuery),function(a){"use strict";a.datepick.regionalOptions.ar={monthNames:["كانون الثاني","شباط","آذار","نيسان","آذار","حزيران","تموز","آب","أيلول","تشرين الأول","تشرين الثاني","كانون الأول"],monthNamesShort:["1","2","3","4","5","6","7","8","9","10","11","12"],dayNames:["الأحد","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت"],dayNamesShort:["الأحد","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت"],dayNamesMin:["الأحد","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت"],dateFormat:"dd/mm/yyyy",firstDay:6,renderer:a.datepick.defaultRenderer,prevText:"<السابق",prevStatus:"عرض الشهر السابق",prevJumpText:"<<",prevJumpStatus:"",nextText:"التالي>",nextStatus:"عرض الشهر القادم",nextJumpText:">>",nextJumpStatus:"",currentText:"اليوم",currentStatus:"عرض الشهر الحالي",todayText:"اليوم",todayStatus:"عرض الشهر الحالي",clearText:"مسح",clearStatus:"امسح التاريخ الحالي",closeText:"إغلاق",closeStatus:"إغلاق بدون Ø­ÙØ¸",yearStatus:"عرض سنة آخرى",monthStatus:"عرض شهر آخر",weekText:"أسبوع",weekStatus:"أسبوع السنة",dayStatus:"اختر D, M d",defaultStatus:"اختر يوم",isRTL:!0},a.datepick.setDefaults(a.datepick.regionalOptions.ar)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.az={monthNames:["Yanvar","Fevral","Mart","Aprel","May","İyun","İyul","Avqust","Sentyabr","Oktyabr","Noyabr","Dekabr"],monthNamesShort:["Yan","Fev","Mar","Apr","May","İyun","İyul","Avq","Sen","Okt","Noy","Dek"],dayNames:["Bazar","Bazar ertÉ™si","ÇərÅŸÉ™nbÉ™ axÅŸamı","ÇərÅŸÉ™nbÉ™","CümÉ™ axÅŸamı","CümÉ™","ŞənbÉ™"],dayNamesShort:["B","Be","Ça","Ç","Ca","C","Åž"],dayNamesMin:["B","B","Ç","С","Ç","C","Åž"],dateFormat:"dd.mm.yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"<Geri",prevStatus:"ÆvvÉ™lki ay",prevJumpText:"<<",prevJumpStatus:"ÆvvÉ™lki il",nextText:"İrÉ™li>",nextStatus:"Sonrakı ay",nextJumpText:">>",nextJumpStatus:"Sonrakı il",currentText:"Bugün",currentStatus:"İndiki ay",todayText:"Bugün",todayStatus:"İndiki ay",clearText:"TÉ™mizlÉ™",clearStatus:"Tarixi sil",closeText:"BaÄŸla",closeStatus:"TÉ™qvimi baÄŸla",yearStatus:"BaÅŸqa il",monthStatus:"BaÅŸqa ay",weekText:"Hf",weekStatus:"HÉ™ftÉ™lÉ™r",dayStatus:"D, M d seçin",defaultStatus:"Bir tarix seçin",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.az)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.bg={monthNames:["Януари","Февруари","Март","Ðприл","Май","Юни","Юли","ÐвгуÑÑ‚","Септември","Октомври","Ðоември","Декември"],monthNamesShort:["Яну","Фев","Мар","Ðпр","Май","Юни","Юли","Ðвг","Сеп","Окт","Ðов","Дек"],dayNames:["ÐеделÑ","Понеделник","Вторник","СрÑда","Четвъртък","Петък","Събота"],dayNamesShort:["Ðед","Пон","Вто","СрÑ","Чет","Пет","Съб"],dayNamesMin:["Ðе","По","Ð’Ñ‚","Ср","Че","Пе","Съ"],dateFormat:"dd.mm.yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"<назад",prevStatus:"покажи поÑÐ»ÐµÐ´Ð½Ð¸Ñ Ð¼ÐµÑец",prevJumpText:"<<",prevJumpStatus:"",nextText:"напред>",nextStatus:"покажи ÑÐ»ÐµÐ´Ð²Ð°Ñ‰Ð¸Ñ Ð¼ÐµÑец",nextJumpText:">>",nextJumpStatus:"",currentText:"днеÑ",currentStatus:"",todayText:"днеÑ",todayStatus:"",clearText:"изчиÑти",clearStatus:"изчиÑти актуалната дата",closeText:"затвори",closeStatus:"затвори без промени",yearStatus:"покажи друга година",monthStatus:"покажи друг меÑец",weekText:"Wk",weekStatus:"Ñедмица от меÑеца",dayStatus:"Избери D, M d",defaultStatus:"Избери дата",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.bg)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.bs={monthNames:["Januar","Februar","Mart","April","Maj","Juni","Juli","August","Septembar","Oktobar","Novembar","Decembar"],monthNamesShort:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Aug","Sep","Okt","Nov","Dec"],dayNames:["Nedelja","Ponedeljak","Utorak","Srijeda","ÄŒetvrtak","Petak","Subota"],dayNamesShort:["Ned","Pon","Uto","Sri","ÄŒet","Pet","Sub"],dayNamesMin:["Ne","Po","Ut","Sr","ÄŒe","Pe","Su"],dateFormat:"dd.mm.yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"<",prevStatus:"",prevJumpText:"<<",prevJumpStatus:"",nextText:">",nextStatus:"",nextJumpText:">>",nextJumpStatus:"",currentText:"Danas",currentStatus:"",todayText:"Danas",todayStatus:"",clearText:"X",clearStatus:"",closeText:"Zatvori",closeStatus:"",yearStatus:"",monthStatus:"",weekText:"Wk",weekStatus:"",dayStatus:"DD d MM",defaultStatus:"",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.bs)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.ca={monthNames:["Gener","Febrer","Març","Abril","Maig","Juny","Juliol","Agost","Setembre","Octubre","Novembre","Desembre"],monthNamesShort:["Gen","Feb","Mar","Abr","Mai","Jun","Jul","Ago","Set","Oct","Nov","Des"],dayNames:["Diumenge","Dilluns","Dimarts","Dimecres","Dijous","Divendres","Dissabte"],dayNamesShort:["Dug","Dln","Dmt","Dmc","Djs","Dvn","Dsb"],dayNamesMin:["Dg","Dl","Dt","Dc","Dj","Dv","Ds"],dateFormat:"dd/mm/yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"<Ant",prevStatus:"",prevJumpText:"<<",prevJumpStatus:"",nextText:"Seg>",nextStatus:"",nextJumpText:">>",nextJumpStatus:"",currentText:"Avui",currentStatus:"",todayText:"Avui",todayStatus:"",clearText:"Netejar",clearStatus:"",closeText:"Tancar",closeStatus:"",yearStatus:"",monthStatus:"",weekText:"Sm",weekStatus:"",dayStatus:"D, M d",defaultStatus:"",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.ca)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.cs={monthNames:["leden","únor","bÅ™ezen","duben","kvÄ›ten","Äerven","Äervenec","srpen","září","říjen","listopad","prosinec"],monthNamesShort:["led","úno","bÅ™e","dub","kvÄ›","Äer","Ävc","srp","zář","říj","lis","pro"],dayNames:["nedÄ›le","pondÄ›lí","úterý","stÅ™eda","Ätvrtek","pátek","sobota"],dayNamesShort:["ne","po","út","st","Ät","pá","so"],dayNamesMin:["ne","po","út","st","Ät","pá","so"],dateFormat:"dd.mm.yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"<Dříve",prevStatus:"PÅ™ejít na pÅ™edchozí mÄ›sí",prevJumpText:"<<",prevJumpStatus:"",nextText:"PozdÄ›ji>",nextStatus:"PÅ™ejít na další mÄ›síc",nextJumpText:">>",nextJumpStatus:"",currentText:"Nyní",currentStatus:"PÅ™ejde na aktuální mÄ›síc",todayText:"Nyní",todayStatus:"PÅ™ejde na aktuální mÄ›síc",clearText:"Vymazat",clearStatus:"Vymaže zadané datum",closeText:"Zavřít",closeStatus:"ZavÅ™e kalendář beze zmÄ›ny",yearStatus:"PÅ™ejít na jiný rok",monthStatus:"PÅ™ejít na jiný mÄ›síc",weekText:"Týd",weekStatus:"Týden v roce",dayStatus:"'Vyber' DD, M d",defaultStatus:"Vyberte datum",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.cs)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.da={monthNames:["Januar","Februar","Marts","April","Maj","Juni","Juli","August","September","Oktober","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Aug","Sep","Okt","Nov","Dec"],dayNames:["Søndag","Mandag","Tirsdag","Onsdag","Torsdag","Fredag","Lørdag"],dayNamesShort:["Søn","Man","Tir","Ons","Tor","Fre","Lør"],dayNamesMin:["Sø","Ma","Ti","On","To","Fr","Lø"],dateFormat:"dd-mm-yyyy",firstDay:0,renderer:a.datepick.defaultRenderer,prevText:"<Forrige",prevStatus:"Vis forrige mÃ¥ned",prevJumpText:"<<",prevJumpStatus:"",nextText:"Næste>",nextStatus:"Vis næste mÃ¥ned",nextJumpText:">>",nextJumpStatus:"",currentText:"Idag",currentStatus:"Vis aktuel mÃ¥ned",todayText:"Idag",todayStatus:"Vis aktuel mÃ¥ned",clearText:"Nulstil",clearStatus:"Nulstil den aktuelle dato",closeText:"Luk",closeStatus:"Luk uden ændringer",yearStatus:"Vis et andet Ã¥r",monthStatus:"Vis en anden mÃ¥ned",weekText:"Uge",weekStatus:"Ã…rets uge",dayStatus:"Vælg D, M d",defaultStatus:"Vælg en dato",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.da)}(jQuery),function(a){"use strict";a.datepick.regionalOptions["de-CH"]={monthNames:["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"],monthNamesShort:["Jan","Feb","Mär","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez"],dayNames:["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"],dayNamesShort:["So","Mo","Di","Mi","Do","Fr","Sa"],dayNamesMin:["So","Mo","Di","Mi","Do","Fr","Sa"],dateFormat:"dd.mm.yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"<zurück",prevStatus:"letzten Monat zeigen",prevJumpText:"<<",prevJumpStatus:"",nextText:"nächster>",nextStatus:"nächsten Monat zeigen",nextJumpText:">>",nextJumpStatus:"",currentText:"heute",currentStatus:"",todayText:"heute",todayStatus:"",clearText:"löschen",clearStatus:"aktuelles Datum löschen",closeText:"schliessen",closeStatus:"ohne Änderungen schliessen",yearStatus:"anderes Jahr anzeigen",monthStatus:"anderen Monat anzeigen",weekText:"Wo",weekStatus:"Woche des Monats",dayStatus:"Wähle D, M d",defaultStatus:"Wähle ein Datum",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions["de-CH"])}(jQuery),function(a){"use strict";a.datepick.regionalOptions.de={monthNames:["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"],monthNamesShort:["Jan","Feb","Mär","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez"],dayNames:["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"],dayNamesShort:["So","Mo","Di","Mi","Do","Fr","Sa"],dayNamesMin:["So","Mo","Di","Mi","Do","Fr","Sa"],dateFormat:"dd.mm.yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"<zurück",prevStatus:"letzten Monat zeigen",prevJumpText:"<<",prevJumpStatus:"",nextText:"Vor>",nextStatus:"nächsten Monat zeigen",nextJumpText:">>",nextJumpStatus:"",currentText:"heute",currentStatus:"",todayText:"heute",todayStatus:"",clearText:"löschen",clearStatus:"aktuelles Datum löschen",closeText:"schließen",closeStatus:"ohne Änderungen schließen",yearStatus:"anderes Jahr anzeigen",monthStatus:"anderen Monat anzeigen",weekText:"Wo",weekStatus:"Woche des Monats",dayStatus:"Wähle D, M d",defaultStatus:"Wähle ein Datum",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.de)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.el={monthNames:["ΙανουάÏιος","ΦεβÏουάÏιος","ΜάÏτιος","ΑπÏίλιος","Μάιος","ΙοÏνιος","ΙοÏλιος","ΑÏγουστος","ΣεπτέμβÏιος","ΟκτώβÏιος","ÎοέμβÏιος","ΔεκέμβÏιος"],monthNamesShort:["Ιαν","Φεβ","ΜαÏ","ΑπÏ","Μαι","Ιουν","Ιουλ","Αυγ","Σεπ","Οκτ","Îοε","Δεκ"],dayNames:["ΚυÏιακή","ΔευτέÏα","ΤÏίτη","ΤετάÏτη","Πέμπτη","ΠαÏασκευή","Σάββατο"],dayNamesShort:["ΚυÏ","Δευ","ΤÏι","Τετ","Πεμ","ΠαÏ","Σαβ"],dayNamesMin:["Κυ","Δε","ΤÏ","Τε","Πε","Πα","Σα"],dateFormat:"dd/mm/yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"ΠÏοηγοÏμενος",prevStatus:"Επισκόπηση Ï€ÏοηγοÏμενου μήνα",prevJumpText:"<<",prevJumpStatus:"",nextText:"Επόμενος",nextStatus:"Επισκόπηση επόμενου μήνα",nextJumpText:">>",nextJumpStatus:"",currentText:"ΤÏέχων Μήνας",currentStatus:"Επισκόπηση Ï„Ïέχοντος μήνα",todayText:"ΤÏέχων Μήνας",todayStatus:"Επισκόπηση Ï„Ïέχοντος μήνα",clearText:"Σβήσιμο",clearStatus:"Σβήσιμο της επιλεγμένης ημεÏομηνίας",closeText:"Κλείσιμο",closeStatus:"Κλείσιμο χωÏίς αλλαγή",yearStatus:"Επισκόπηση άλλου έτους",monthStatus:"Επισκόπηση άλλου μήνα",weekText:"Εβδ",weekStatus:"",dayStatus:"Επιλογή DD d MM",defaultStatus:"Επιλέξτε μια ημεÏομηνία",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.el)}(jQuery),function(a){"use strict";a.datepick.regionalOptions["en-AU"]={monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],dateFormat:"dd/mm/yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"Prev",prevStatus:"Show the previous month",prevJumpText:"<<",prevJumpStatus:"Show the previous year",nextText:"Next",nextStatus:"Show the next month",nextJumpText:">>",nextJumpStatus:"Show the next year",currentText:"Current",currentStatus:"Show the current month",todayText:"Today",todayStatus:"Show today's month",clearText:"Clear",clearStatus:"Erase the current date",closeText:"Done",closeStatus:"Close without change",yearStatus:"Show a different year",monthStatus:"Show a different month",weekText:"Wk",weekStatus:"Week of the year",dayStatus:"Select DD, M d",defaultStatus:"Select a date",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions["en-AU"])}(jQuery),function(a){"use strict";a.datepick.regionalOptions["en-GB"]={monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],dateFormat:"dd/mm/yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"Prev",prevStatus:"Show the previous month",prevJumpText:"<<",prevJumpStatus:"Show the previous year",nextText:"Next",nextStatus:"Show the next month",nextJumpText:">>",nextJumpStatus:"Show the next year",currentText:"Current",currentStatus:"Show the current month",todayText:"Today",todayStatus:"Show today's month",clearText:"Clear",clearStatus:"Erase the current date",closeText:"Done",closeStatus:"Close without change",yearStatus:"Show a different year",monthStatus:"Show a different month",weekText:"Wk",weekStatus:"Week of the year",dayStatus:"Select DD, M d",defaultStatus:"Select a date",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions["en-GB"])}(jQuery),function(a){"use strict";a.datepick.regionalOptions["en-NZ"]={monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],dateFormat:"dd/mm/yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"Prev",prevStatus:"Show the previous month",prevJumpText:"<<",prevJumpStatus:"Show the previous year",nextText:"Next",nextStatus:"Show the next month",nextJumpText:">>",nextJumpStatus:"Show the next year",currentText:"Current",currentStatus:"Show the current month",todayText:"Today",todayStatus:"Show today's month",clearText:"Clear",clearStatus:"Erase the current date",closeText:"Done",closeStatus:"Close without change",yearStatus:"Show a different year",monthStatus:"Show a different month",weekText:"Wk",weekStatus:"Week of the year",dayStatus:"Select DD, M d",defaultStatus:"Select a date",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions["en-NZ"])}(jQuery),function(a){"use strict";a.datepick.regionalOptions.eo={monthNames:["Januaro","Februaro","Marto","Aprilo","Majo","Junio","Julio","AÅ­gusto","Septembro","Oktobro","Novembro","Decembro"],monthNamesShort:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","AÅ­g","Sep","Okt","Nov","Dec"],dayNames:["Dimanĉo","Lundo","Mardo","Merkredo","Ä´aÅ­do","Vendredo","Sabato"],dayNamesShort:["Dim","Lun","Mar","Mer","Ä´aÅ­","Ven","Sab"],dayNamesMin:["Di","Lu","Ma","Me","Ä´a","Ve","Sa"],dateFormat:"dd/mm/yyyy",firstDay:0,renderer:a.datepick.defaultRenderer,prevText:"<Anta",prevStatus:"Vidi la antaÅ­an monaton",prevJumpText:"<<",prevJumpStatus:"",nextText:"Sekv>",nextStatus:"Vidi la sekvan monaton",nextJumpText:">>",nextJumpStatus:"",currentText:"Nuna",currentStatus:"Vidi la nunan monaton",todayText:"Nuna",todayStatus:"Vidi la nunan monaton",clearText:"Vakigi",clearStatus:"",closeText:"Fermi",closeStatus:"Fermi sen modifi",yearStatus:"Vidi alian jaron",monthStatus:"Vidi alian monaton",weekText:"Sb",weekStatus:"",dayStatus:"Elekti DD, MM d",defaultStatus:"Elekti la daton",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.eo)}(jQuery),function(a){"use strict";a.datepick.regionalOptions["es-AR"]={monthNames:["Enero","Febrero","Marzo","Abril","Mayo","Junio","Julio","Agosto","Septiembre","Octubre","Noviembre","Diciembre"],monthNamesShort:["Ene","Feb","Mar","Abr","May","Jun","Jul","Ago","Sep","Oct","Nov","Dic"],dayNames:["Domingo","Lunes","Martes","Miércoles","Jueves","Viernes","Sábado"],dayNamesShort:["Dom","Lun","Mar","Mié","Juv","Vie","Sáb"],dayNamesMin:["Do","Lu","Ma","Mi","Ju","Vi","Sá"],dateFormat:"dd/mm/yyyy",firstDay:0,renderer:a.datepick.defaultRenderer,prevText:"<Ant",prevStatus:"",prevJumpText:"<<",prevJumpStatus:"",nextText:"Sig>",nextStatus:"",nextJumpText:">>",nextJumpStatus:"",currentText:"Hoy",currentStatus:"",todayText:"Hoy",todayStatus:"",clearText:"Limpiar",clearStatus:"",closeText:"Cerrar",closeStatus:"",yearStatus:"",monthStatus:"",weekText:"Sm",weekStatus:"",dayStatus:"D, M d",defaultStatus:"",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions["es-AR"])}(jQuery),function(a){"use strict";a.datepick.regionalOptions["es-PE"]={monthNames:["Enero","Febrero","Marzo","Abril","Mayo","Junio","Julio","Agosto","Septiembre","Octubre","Noviembre","Diciembre"],monthNamesShort:["Ene","Feb","Mar","Abr","May","Jun","Jul","Ago","Sep","Oct","Nov","Dic"],dayNames:["Domingo","Lunes","Martes","Miércoles","Jueves","Viernes","Sábado"],dayNamesShort:["Dom","Lun","Mar","Mié","Jue","Vie","Sab"],dayNamesMin:["Do","Lu","Ma","Mi","Ju","Vi","Sa"],dateFormat:"dd/mm/yyyy",firstDay:0,renderer:a.datepick.defaultRenderer,prevText:"<Ant",prevStatus:"",prevJumpText:"<<",prevJumpStatus:"",nextText:"Sig>",nextStatus:"",nextJumpText:">>",nextJumpStatus:"",currentText:"Hoy",currentStatus:"",todayText:"Hoy",todayStatus:"",clearText:"Limpiar",clearStatus:"",closeText:"Cerrar",closeStatus:"",yearStatus:"",monthStatus:"",weekText:"Sm",weekStatus:"",dayStatus:"DD d, MM yyyy",defaultStatus:"",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions["es-PE"])}(jQuery),function(a){"use strict";a.datepick.regionalOptions.es={monthNames:["Enero","Febrero","Marzo","Abril","Mayo","Junio","Julio","Agosto","Septiembre","Octubre","Noviembre","Diciembre"],monthNamesShort:["Ene","Feb","Mar","Abr","May","Jun","Jul","Ago","Sep","Oct","Nov","Dic"],dayNames:["Domingo","Lunes","Martes","Miércoles","Jueves","Viernes","Sábado"],dayNamesShort:["Dom","Lun","Mar","Mié","Juv","Vie","Sáb"],dayNamesMin:["Do","Lu","Ma","Mi","Ju","Vi","Sá"],dateFormat:"dd/mm/yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"<Ant",prevStatus:"",prevJumpText:"<<",prevJumpStatus:"",nextText:"Sig>",nextStatus:"",nextJumpText:">>",nextJumpStatus:"",currentText:"Hoy",currentStatus:"",todayText:"Hoy",todayStatus:"",clearText:"Limpiar",clearStatus:"",closeText:"Cerrar",closeStatus:"",yearStatus:"",monthStatus:"",weekText:"Sm",weekStatus:"",dayStatus:"D, M d",defaultStatus:"",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.es)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.et={monthNames:["Jaanuar","Veebruar","Märts","Aprill","Mai","Juuni","Juuli","August","September","Oktoober","November","Detsember"],monthNamesShort:["Jaan","Veebr","Märts","Apr","Mai","Juuni","Juuli","Aug","Sept","Okt","Nov","Dets"],dayNames:["Pühapäev","Esmaspäev","Teisipäev","Kolmapäev","Neljapäev","Reede","Laupäev"],dayNamesShort:["Pühap","Esmasp","Teisip","Kolmap","Neljap","Reede","Laup"],dayNamesMin:["P","E","T","K","N","R","L"],dateFormat:"dd.mm.yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"Eelnev",prevStatus:"",prevJumpText:"<<",prevJumpStatus:"",nextText:"Järgnev",nextStatus:"",nextJumpText:">>",nextJumpStatus:"",currentText:"Täna",currentStatus:"",todayText:"Täna",todayStatus:"",clearText:"",clearStatus:"",closeText:"Sulge",closeStatus:"",yearStatus:"",monthStatus:"",weekText:"Sm",weekStatus:"",dayStatus:"",defaultStatus:"",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.et)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.eu={monthNames:["Urtarrila","Otsaila","Martxoa","Apirila","Maiatza","Ekaina","Uztaila","Abuztua","Iraila","Urria","Azaroa","Abendua"],monthNamesShort:["Urt","Ots","Mar","Api","Mai","Eka","Uzt","Abu","Ira","Urr","Aza","Abe"],dayNames:["Igandea","Astelehena","Asteartea","Asteazkena","Osteguna","Ostirala","Larunbata"],dayNamesShort:["Iga","Ast","Ast","Ast","Ost","Ost","Lar"],dayNamesMin:["Ig","As","As","As","Os","Os","La"],dateFormat:"yyyy/mm/dd",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"<Aur",prevStatus:"",prevJumpText:"<<",prevJumpStatus:"",nextText:"Hur>",nextStatus:"",nextJumpText:">>",nextJumpStatus:"",currentText:"Gaur",currentStatus:"",todayText:"Gaur",todayStatus:"",clearText:"X",clearStatus:"",closeText:"Egina",closeStatus:"",yearStatus:"",monthStatus:"",weekText:"Wk",weekStatus:"",dayStatus:"DD d MM",defaultStatus:"",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.eu)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.fa={monthNames:["ÙØ±ÙˆØ±Ø¯ÙŠÙ†","ارديبهشت","خرداد","تير","مرداد","شهريور","مهر","آبان","آذر","دي","بهمن","اسÙند"],monthNamesShort:["1","2","3","4","5","6","7","8","9","10","11","12"],dayNames:["يکشنبه","دوشنبه","سه‌شنبه","چهارشنبه","پنجشنبه","جمعه","شنبه"],dayNamesShort:["ÙŠ","د","س","Ú†","Ù¾","ج","Ø´"],dayNamesMin:["ÙŠ","د","س","Ú†","Ù¾","ج","Ø´"],dateFormat:"yyyy/mm/dd",firstDay:6,renderer:a.datepick.defaultRenderer,prevText:"<قبلي",prevStatus:"نمايش ماه قبل",prevJumpText:"<<",prevJumpStatus:"",nextText:"بعدي>",nextStatus:"نمايش ماه بعد",nextJumpText:">>",nextJumpStatus:"",currentText:"امروز",currentStatus:"نمايش ماه جاري",todayText:"امروز",todayStatus:"نمايش ماه جاري",clearText:"حذ٠تاريخ",clearStatus:"پاک کردن تاريخ جاري",closeText:"بستن",closeStatus:"بستن بدون اعمال تغييرات",yearStatus:"نمايش سال Ù…ØªÙØ§ÙˆØª",monthStatus:"نمايش ماه Ù…ØªÙØ§ÙˆØª",weekText:"Ù‡Ù",weekStatus:"Ù‡ÙØªÙ‡Ù سال",dayStatus:"انتخاب D, M d",defaultStatus:"انتخاب تاريخ",isRTL:!0},a.datepick.setDefaults(a.datepick.regionalOptions.fa)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.fi={monthNames:["Tammikuu","Helmikuu","Maaliskuu","Huhtikuu","Toukokuu","Kesäkuu","Heinäkuu","Elokuu","Syyskuu","Lokakuu","Marraskuu","Joulukuu"],monthNamesShort:["Tammi","Helmi","Maalis","Huhti","Touko","Kesä","Heinä","Elo","Syys","Loka","Marras","Joulu"],dayNamesShort:["Su","Ma","Ti","Ke","To","Pe","Su"],dayNames:["Sunnuntai","Maanantai","Tiistai","Keskiviikko","Torstai","Perjantai","Lauantai"],dayNamesMin:["Su","Ma","Ti","Ke","To","Pe","La"],dateFormat:"dd.mm.yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"«Edellinen",prevStatus:"",prevJumpText:"<<",prevJumpStatus:"",nextText:"Seuraava»",nextStatus:"",nextJumpText:">>",nextJumpStatus:"",currentText:"Tänään",currentStatus:"",todayText:"Tänään",todayStatus:"",clearText:"Tyhjennä",clearStatus:"",closeText:"Sulje",closeStatus:"",yearStatus:"",monthStatus:"",weekText:"Vk",weekStatus:"",dayStatus:"D, M d",defaultStatus:"",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.fi)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.fo={monthNames:["Januar","Februar","Mars","Apríl","Mei","Juni","Juli","August","September","Oktober","November","Desember"],monthNamesShort:["Jan","Feb","Mar","Apr","Mei","Jun","Jul","Aug","Sep","Okt","Nov","Des"],dayNames:["Sunnudagur","Mánadagur","Týsdagur","Mikudagur","Hósdagur","Fríggjadagur","Leyardagur"],dayNamesShort:["Sun","Mán","Týs","Mik","Hós","Frí","Ley"],dayNamesMin:["Su","Má","Tý","Mi","Hó","Fr","Le"],dateFormat:"dd-mm-yyyy",firstDay:0,renderer:a.datepick.defaultRenderer,prevText:"<Sísta",prevStatus:"Vís sísta mánaðan",prevJumpText:"<<",prevJumpStatus:"Vís sísta árið",nextText:"Næsta>",nextStatus:"Vís næsta mánaðan",nextJumpText:">>",nextJumpStatus:"Vís næsta árið",currentText:"à dag",currentStatus:"Vís mánaðan fyri í dag",todayText:"à dag",todayStatus:"Vís mánaðan fyri í dag",clearText:"Strika",clearStatus:"Strika allir mánaðarnar",closeText:"Goym",closeStatus:"Goym hetta vindeyðga",yearStatus:"Broyt árið",monthStatus:"Broyt mánaðan",weekText:"Vk",weekStatus:"Vika av árinum",dayStatus:"Vel DD, M d, yyyy",defaultStatus:"Vel ein dato",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.fo)}(jQuery),function(a){"use strict";a.datepick.regionalOptions["fr-CH"]={monthNames:["Janvier","Février","Mars","Avril","Mai","Juin","Juillet","Août","Septembre","Octobre","Novembre","Décembre"],monthNamesShort:["Jan","Fév","Mar","Avr","Mai","Jun","Jul","Aoû","Sep","Oct","Nov","Déc"],dayNames:["Dimanche","Lundi","Mardi","Mercredi","Jeudi","Vendredi","Samedi"],dayNamesShort:["Dim","Lun","Mar","Mer","Jeu","Ven","Sam"],dayNamesMin:["Di","Lu","Ma","Me","Je","Ve","Sa"],dateFormat:"dd.mm.yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"<Préc",prevStatus:"Voir le mois précédent",prevJumpText:"<<",prevJumpStatus:"",nextText:"Suiv>",nextStatus:"Voir le mois suivant",nextJumpText:">>",nextJumpStatus:"",currentText:"Courant",currentStatus:"Voir le mois courant",todayText:"Aujourd'hui",todayStatus:"Voir aujourd'hui",clearText:"Effacer",clearStatus:"Effacer la date sélectionnée",closeText:"Fermer",closeStatus:"Fermer sans modifier",yearStatus:"Voir une autre année",monthStatus:"Voir un autre mois",weekText:"Sm",weekStatus:"",dayStatus:"'Choisir' le DD d MM",defaultStatus:"Choisir la date",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions["fr-CH"])}(jQuery),function(a){"use strict";a.datepick.regionalOptions.fr={monthNames:["Janvier","Février","Mars","Avril","Mai","Juin","Juillet","Août","Septembre","Octobre","Novembre","Décembre"],monthNamesShort:["Jan","Fév","Mar","Avr","Mai","Jun","Jul","Aoû","Sep","Oct","Nov","Déc"],dayNames:["Dimanche","Lundi","Mardi","Mercredi","Jeudi","Vendredi","Samedi"],dayNamesShort:["Dim","Lun","Mar","Mer","Jeu","Ven","Sam"],dayNamesMin:["Di","Lu","Ma","Me","Je","Ve","Sa"],dateFormat:"dd/mm/yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"<Préc",prevStatus:"Voir le mois précédent",prevJumpText:"<<",prevJumpStatus:"Voir l'année précédent",nextText:"Suiv>",nextStatus:"Voir le mois suivant",nextJumpText:">>",nextJumpStatus:"Voir l'année suivant",currentText:"Courant",currentStatus:"Voir le mois courant",todayText:"Aujourd'hui",todayStatus:"Voir aujourd'hui",clearText:"Effacer",clearStatus:"Effacer la date sélectionnée",closeText:"Fermer",closeStatus:"Fermer sans modifier",yearStatus:"Voir une autre année",monthStatus:"Voir un autre mois",weekText:"Sm",weekStatus:"Semaine de l'année",dayStatus:"'Choisir' le DD d MM",defaultStatus:"Choisir la date",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.fr)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.gl={monthNames:["Xaneiro","Febreiro","Marzo","Abril","Maio","Xuño","Xullo","Agosto","Setembro","Outubro","Novembro","Decembro"],monthNamesShort:["Xan","Feb","Mar","Abr","Mai","Xuñ","Xul","Ago","Set","Out","Nov","Dec"], +dayNames:["Domingo","Luns","Martes","Mércores","Xoves","Venres","Sábado"],dayNamesShort:["Dom","Lun","Mar","Mér","Xov","Ven","Sáb"],dayNamesMin:["Do","Lu","Ma","Me","Xo","Ve","Sá"],dateFormat:"dd/mm/yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"<Ant",prevStatus:"Amosar mes anterior",prevJumpText:"<<",prevJumpStatus:"Amosar ano anterior",nextText:"Seg>",nextStatus:"Amosar mes seguinte",nextJumpText:">>",nextJumpStatus:"Amosar ano seguinte",currentText:"Hoxe",currentStatus:"Amosar mes actual",todayText:"Hoxe",todayStatus:"Amosar mes actual",clearText:"Limpar",clearStatus:"Borrar data actual",closeText:"Pechar",closeStatus:"Pechar sen gardar",yearStatus:"Amosar outro ano",monthStatus:"Amosar outro mes",weekText:"Sm",weekStatus:"Semana do ano",dayStatus:"D, M d",defaultStatus:"Selecciona Data",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.gl)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.gu={monthNames:["જાનà«àª¯à«àª†àª°à«€","ફેબà«àª°à«àª†àª°à«€","મારà«àªš","àªàªªà«àª°àª¿àª²","મે","જૂન","જà«àª²àª¾àªˆ","ઑગસà«àªŸ","સપà«àªŸà«‡àª®à«àª¬àª°","ઑકà«àªŸà«‹àª¬àª°","નવેમà«àª¬àª°","ડિસેમà«àª¬àª°"],monthNamesShort:["જાનà«àª¯à«","ફેબà«àª°à«","મારà«àªš","àªàªªà«àª°àª¿àª²","મે","જૂન","જà«àª²àª¾àªˆ","ઑગસà«àªŸ","સપà«àªŸà«‡","ઑકà«àªŸà«‹","નવે","ડિસે"],dayNames:["રવિવાર","સોમવાર","મંગળવાર","બà«àª§àªµàª¾àª°","ગà«àª°à«àªµàª¾àª°","શà«àª•à«àª°àªµàª¾àª°","શનિવાર"],dayNamesShort:["રવિ","સોમ","મંગળ","બà«àª§","ગà«àª°à«","શà«àª•à«àª°","શનિ"],dayNamesMin:["ર","સો","મં","બà«","ગà«","શà«","શ"],dateFormat:"dd-M-yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"<પાછળ",prevStatus:"પાછલો મહિનો બતાવો",prevJumpText:"<<",prevJumpStatus:"પાછળ",nextText:"આગળ>",nextStatus:"આગલો મહિનો બતાવો",nextJumpText:">>",nextJumpStatus:"આગળ",currentText:"આજે",currentStatus:"આજનો દિવસ બતાવો",todayText:"આજે",todayStatus:"આજનો દિવસ",clearText:"ભૂંસો",clearStatus:"હાલ પસંદ કરેલી તારીખ ભૂંસો",closeText:"બંધ કરો",closeStatus:"તારીખ પસંદ કરà«àª¯àª¾ વગર બંધ કરો",yearStatus:"જà«àª¦à« વરà«àª· બતાવો",monthStatus:"જà«àª¦à«‹ મહિનો બતાવો",weekText:"અઠવાડિયà«àª‚",weekStatus:"અઠવાડિયà«àª‚",dayStatus:"અઠવાડિયાનો પહેલો દિવસ પસંદ કરો",defaultStatus:"તારીખ પસંદ કરો",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.gu)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.he={monthNames:["ינו×ר","פברו×ר","מרץ","×פריל","מ××™","יוני","יולי","×וגוסט","ספטמבר","×וקטובר","נובמבר","דצמבר"],monthNamesShort:["1","2","3","4","5","6","7","8","9","10","11","12"],dayNames:["ר×שון","שני","שלישי","רביעי","חמישי","שישי","שבת"],dayNamesShort:["×'","ב'","×’'","ד'","×”'","ו'","שבת"],dayNamesMin:["×'","ב'","×’'","ד'","×”'","ו'","שבת"],dateFormat:"dd/mm/yyyy",firstDay:0,renderer:a.datepick.defaultRenderer,prevText:"<הקוד×",prevStatus:"",prevJumpText:"<<",prevJumpStatus:"",nextText:"הב×>",nextStatus:"",nextJumpText:">>",nextJumpStatus:"",currentText:"היו×",currentStatus:"",todayText:"היו×",todayStatus:"",clearText:"× ×§×”",clearStatus:"",closeText:"סגור",closeStatus:"",yearStatus:"",monthStatus:"",weekText:"Wk",weekStatus:"",dayStatus:"DD, M d",defaultStatus:"",isRTL:!0},a.datepick.setDefaults(a.datepick.regionalOptions.he)}(jQuery),function(a){"use strict";a.datepick.regionalOptions["hi-IN"]={monthNames:["जनवरी"," फरवरी","मारà¥à¤š","अपà¥à¤°à¥ˆà¤²","मई","जून","जà¥à¤²à¤¾à¤ˆ","अगसà¥à¤¤","सितमà¥à¤¬à¤°","अकà¥à¤Ÿà¥‚बर","नवमà¥à¤¬à¤°","दिसमà¥à¤¬à¤°"],monthNamesShort:["जन","फर","मारà¥à¤š","अपà¥à¤°à¥ˆ","मई","जून","जà¥à¤²à¤¾à¤ˆ","अग","सित","अकà¥à¤Ÿà¥‚","नव","दिस"],dayNames:["रविवार","सोमवार","मंगलवार","बà¥à¤§à¤µà¤¾à¤°","गà¥à¤°à¥à¤µà¤¾à¤°","शà¥à¤•à¥à¤°à¤µà¤¾à¤°","शनिवार"],dayNamesShort:["रवि","सोम","मंगल","बà¥à¤§","गà¥à¤°à¥","शà¥à¤•à¥à¤°","शनि"],dayNamesMin:["र","सो","मं","बà¥","गà¥","शà¥","श"],dateFormat:"dd/mm/yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"पिछला",prevStatus:"पिछला महीना देखें",prevJumpText:"<<",prevJumpStatus:"पिछला वरà¥à¤· देखें",nextText:"अगला",nextStatus:"अगला महीना देखें",nextJumpText:">>",nextJumpStatus:"अगला वरà¥à¤· देखें",currentText:"वरà¥à¤¤à¤®à¤¾à¤¨",currentStatus:"वरà¥à¤¤à¤®à¤¾à¤¨ महीना देखें",todayText:"आज",todayStatus:"वरà¥à¤¤à¤®à¤¾à¤¨ दिन देखें",clearText:"साफ",clearStatus:"वरà¥à¤¤à¤®à¤¾à¤¨ दिनांक मिटाà¤",closeText:"समापà¥à¤¤",closeStatus:"बदलाव के बिना बंद",yearStatus:"à¤à¤• अलग वरà¥à¤· का चयन करें",monthStatus:"à¤à¤• अलग महीने का चयन करें",weekText:"Wk",weekStatus:"वरà¥à¤· का सपà¥à¤¤à¤¾à¤¹",dayStatus:"चà¥à¤¨à¥‡ DD, M d",defaultStatus:"à¤à¤• तिथि का चयन करें",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions["hi-IN"])}(jQuery),function(a){"use strict";a.datepick.regionalOptions.hi={monthNames:["जनवरी","फरवरी","मारà¥à¤š","अपà¥à¤°à¥ˆà¤²","मई","जून","जà¥à¤²à¤¾à¤ˆ","अगसà¥à¤¤","सितंबर","अकà¥à¤Ÿà¥‚बर","नवंबर","दिसंबर"],monthNamesShort:["जन","फ़र.","मारà¥à¤š","अपà¥à¤°à¥ˆà¤²","मई","जून","जà¥à¤²à¤¾à¤ˆ","अगसà¥à¤¤","सितंबर","अकà¥à¤Ÿà¥‚बर","नवंबर","दिसंबर"],dayNames:["रविवार","सोमवार","मंगलवार","बà¥à¤§à¤µà¤¾à¤°","बृहसà¥à¤ªà¤¤à¤¿à¤µà¤¾à¤°","शà¥à¤•à¥à¤°à¤µà¤¾à¤°","शनिवार"],dayNamesShort:["रवि","सोम","मंगल","बà¥à¤§","बृहसà¥à¤ªà¤¤","शà¥à¤•à¥à¤°","शनि"],dayNamesMin:["रवि","सोम","मंगल","बà¥à¤§","बृहसà¥à¤ªà¤¤","शà¥à¤•à¥à¤°","शनि"],dateFormat:"dd/mm/yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"पिछला",prevStatus:"पिछले महीने",prevJumpText:"<<",prevJumpStatus:"पिछले वरà¥à¤·",nextText:"अगला",nextStatus:"अगले महीने",nextJumpText:">>",nextJumpStatus:"अगले साल",currentText:"वरà¥à¤¤à¤®à¤¾à¤¨",currentStatus:"चालू माह",todayText:"आज",todayStatus:"आजका महीना",clearText:"साफ़",clearStatus:"वरà¥à¤¤à¤®à¤¾à¤¨ दिनांक मिटा",closeText:"ठीक है",closeStatus:"बदलाव के बिना बंद",yearStatus:"à¤à¤• अलग वरà¥à¤· दिखाà¤à¤",monthStatus:"दिखाà¤à¤ किसी अनà¥à¤¯ महीने के",weekText:"Wk",weekStatus:"Week of the year",dayStatus:"चयन DD, M d",defaultStatus:"à¤à¤• तिथि का चयन करें",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.hi)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.hr={monthNames:["SijeÄanj","VeljaÄa","Ožujak","Travanj","Svibanj","Lipanj","Srpanj","Kolovoz","Rujan","Listopad","Studeni","Prosinac"],monthNamesShort:["Sij","Velj","Ožu","Tra","Svi","Lip","Srp","Kol","Ruj","Lis","Stu","Pro"],dayNames:["Nedjelja","Ponedjeljak","Utorak","Srijeda","ÄŒetvrtak","Petak","Subota"],dayNamesShort:["Ned","Pon","Uto","Sri","ÄŒet","Pet","Sub"],dayNamesMin:["Ne","Po","Ut","Sr","ÄŒe","Pe","Su"],dateFormat:"dd.mm.yyyy.",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"<",prevStatus:"Prikaži prethodni mjesec",prevJumpText:"<<",prevJumpStatus:"",nextText:">",nextStatus:"Prikaži slijedeći mjesec",nextJumpText:">>",nextJumpStatus:"",currentText:"Danas",currentStatus:"DanaÅ¡nji datum",todayText:"Danas",todayStatus:"DanaÅ¡nji datum",clearText:"izbriÅ¡i",clearStatus:"IzbriÅ¡i trenutni datum",closeText:"Zatvori",closeStatus:"Zatvori kalendar",yearStatus:"Prikaži godine",monthStatus:"Prikaži mjesece",weekText:"Tje",weekStatus:"Tjedan",dayStatus:"'Datum' D, M d",defaultStatus:"Odaberi datum",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.hr)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.hu={monthNames:["Január","Február","Március","Ãprilis","Május","Június","Július","Augusztus","Szeptember","Október","November","December"],monthNamesShort:["Jan","Feb","Már","Ãpr","Máj","Jún","Júl","Aug","Szep","Okt","Nov","Dec"],dayNames:["Vasárnap","Hétfö","Kedd","Szerda","Csütörtök","Péntek","Szombat"],dayNamesShort:["Vas","Hét","Ked","Sze","Csü","Pén","Szo"],dayNamesMin:["V","H","K","Sze","Cs","P","Szo"],dateFormat:"yyyy-mm-dd",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"« vissza",prevStatus:"",prevJumpText:"<<",prevJumpStatus:"",nextText:"elÅ‘re »",nextStatus:"",nextJumpText:">>",nextJumpStatus:"",currentText:"ma",currentStatus:"",todayText:"ma",todayStatus:"",clearText:"törlés",clearStatus:"",closeText:"bezárás",closeStatus:"",yearStatus:"",monthStatus:"",weekText:"Hé",weekStatus:"",dayStatus:"D, M d",defaultStatus:"",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.hu)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.hy={monthNames:["Õ€Õ¸Ö‚Õ¶Õ¾Õ¡Ö€","Õ“Õ¥Õ¿Ö€Õ¾Õ¡Ö€","Õ„Õ¡Ö€Õ¿","Ô±ÕºÖ€Õ«Õ¬","Õ„Õ¡ÕµÕ«Õ½","Õ€Õ¸Ö‚Õ¶Õ«Õ½","Õ€Õ¸Ö‚Õ¬Õ«Õ½","Õ•Õ£Õ¸Õ½Õ¿Õ¸Õ½","ÕÕ¥ÕºÕ¿Õ¥Õ´Õ¢Õ¥Ö€","Õ€Õ¸Õ¯Õ¿Õ¥Õ´Õ¢Õ¥Ö€","Õ†Õ¸ÕµÕ¥Õ´Õ¢Õ¥Ö€","Ô´Õ¥Õ¯Õ¿Õ¥Õ´Õ¢Õ¥Ö€"],monthNamesShort:["Õ€Õ¸Ö‚Õ¶Õ¾","Õ“Õ¥Õ¿Ö€","Õ„Õ¡Ö€Õ¿","Ô±ÕºÖ€","Õ„Õ¡ÕµÕ«Õ½","Õ€Õ¸Ö‚Õ¶Õ«Õ½","Õ€Õ¸Ö‚Õ¬","Õ•Õ£Õ½","ÕÕ¥Õº","Õ€Õ¸Õ¯","Õ†Õ¸Õµ","Ô´Õ¥Õ¯"],dayNames:["Õ¯Õ«Ö€Õ¡Õ¯Õ«","Õ¥Õ¯Õ¸Ö‚Õ·Õ¡Õ¢Õ©Õ«","Õ¥Ö€Õ¥Ö„Õ·Õ¡Õ¢Õ©Õ«","Õ¹Õ¸Ö€Õ¥Ö„Õ·Õ¡Õ¢Õ©Õ«","Õ°Õ«Õ¶Õ£Õ·Õ¡Õ¢Õ©Õ«","Õ¸Ö‚Ö€Õ¢Õ¡Õ©","Õ·Õ¡Õ¢Õ¡Õ©"],dayNamesShort:["Õ¯Õ«Ö€","Õ¥Ö€Õ¯","Õ¥Ö€Ö„","Õ¹Ö€Ö„","Õ°Õ¶Õ£","Õ¸Ö‚Ö€Õ¢","Õ·Õ¢Õ©"],dayNamesMin:["Õ¯Õ«Ö€","Õ¥Ö€Õ¯","Õ¥Ö€Ö„","Õ¹Ö€Ö„","Õ°Õ¶Õ£","Õ¸Ö‚Ö€Õ¢","Õ·Õ¢Õ©"],dateFormat:"dd.mm.yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"<Õ†Õ¡Õ­.",prevStatus:"",prevJumpText:"<<",prevJumpStatus:"",nextText:"Õ€Õ¡Õ».>",nextStatus:"",nextJumpText:">>",nextJumpStatus:"",currentText:"Ô±ÕµÕ½Ö…Ö€",currentStatus:"",todayText:"Ô±ÕµÕ½Ö…Ö€",todayStatus:"",clearText:"Õ„Õ¡Ö„Ö€Õ¥Õ¬",clearStatus:"",closeText:"Õ“Õ¡Õ¯Õ¥Õ¬",closeStatus:"",yearStatus:"",monthStatus:"",weekText:"Õ‡Ô²Õ",weekStatus:"",dayStatus:"D, M d",defaultStatus:"",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.hy)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.id={monthNames:["Januari","Februari","Maret","April","Mei","Juni","Juli","Agustus","September","Oktober","Nopember","Desember"],monthNamesShort:["Jan","Feb","Mar","Apr","Mei","Jun","Jul","Agus","Sep","Okt","Nop","Des"],dayNames:["Minggu","Senin","Selasa","Rabu","Kamis","Jumat","Sabtu"],dayNamesShort:["Min","Sen","Sel","Rab","kam","Jum","Sab"],dayNamesMin:["Mg","Sn","Sl","Rb","Km","jm","Sb"],dateFormat:"dd/mm/yyyy",firstDay:0,renderer:a.datepick.defaultRenderer,prevText:"<mundur",prevStatus:"Tampilkan bulan sebelumnya",prevJumpText:"<<",prevJumpStatus:"",nextText:"maju>",nextStatus:"Tampilkan bulan berikutnya",nextJumpText:">>",nextJumpStatus:"",currentText:"hari ini",currentStatus:"Tampilkan bulan sekarang",todayText:"hari ini",todayStatus:"Tampilkan bulan sekarang",clearText:"kosongkan",clearStatus:"bersihkan tanggal yang sekarang",closeText:"Tutup",closeStatus:"Tutup tanpa mengubah",yearStatus:"Tampilkan tahun yang berbeda",monthStatus:"Tampilkan bulan yang berbeda",weekText:"Mg",weekStatus:"Minggu dalam tahun",dayStatus:"pilih le DD, MM d",defaultStatus:"Pilih Tanggal",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.id)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.is={monthNames:["Janúar","Febrúar","Mars","Apríl","Maí","Júní","Júlí","Ágúst","September","Október","Nóvember","Desember"],monthNamesShort:["Jan","Feb","Mar","Apr","Maí","Jún","Júl","Ágú","Sep","Okt","Nóv","Des"],dayNames:["Sunnudagur","Mánudagur","Þriðjudagur","Miðvikudagur","Fimmtudagur","Föstudagur","Laugardagur"],dayNamesShort:["Sun","Mán","Þri","Mið","Fim","Fös","Lau"],dayNamesMin:["Su","Má","Þr","Mi","Fi","Fö","La"],dateFormat:"dd/mm/yyyy",firstDay:0,renderer:a.datepick.defaultRenderer,prevText:"< Fyrri",prevStatus:"",prevJumpText:"<<",prevJumpStatus:"",nextText:"Næsti >",nextStatus:"",nextJumpText:">>",nextJumpStatus:"",currentText:"Í dag",currentStatus:"",todayText:"Í dag",todayStatus:"",clearText:"Hreinsa",clearStatus:"",closeText:"Loka",closeStatus:"",yearStatus:"",monthStatus:"",weekText:"Vika",weekStatus:"",dayStatus:"D, M d",defaultStatus:"",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.is)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.it={monthNames:["Gennaio","Febbraio","Marzo","Aprile","Maggio","Giugno","Luglio","Agosto","Settembre","Ottobre","Novembre","Dicembre"],monthNamesShort:["Gen","Feb","Mar","Apr","Mag","Giu","Lug","Ago","Set","Ott","Nov","Dic"],dayNames:["Domenica","Lunedì","Martedì","Mercoledì","Giovedì","Venerdì","Sabato"],dayNamesShort:["Dom","Lun","Mar","Mer","Gio","Ven","Sab"],dayNamesMin:["Do","Lu","Ma","Me","Gi","Ve","Sa"],dateFormat:"dd/mm/yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"<Prec",prevStatus:"Mese precedente",prevJumpText:"<<",prevJumpStatus:"Mostra l'anno precedente",nextText:"Succ>",nextStatus:"Mese successivo",nextJumpText:">>",nextJumpStatus:"Mostra l'anno successivo",currentText:"Oggi",currentStatus:"Mese corrente",todayText:"Oggi",todayStatus:"Mese corrente",clearText:"Svuota",clearStatus:"Annulla",closeText:"Chiudi",closeStatus:"Chiudere senza modificare",yearStatus:"Seleziona un altro anno",monthStatus:"Seleziona un altro mese",weekText:"Sm",weekStatus:"Settimana dell'anno",dayStatus:"'Seleziona' D, M d",defaultStatus:"Scegliere una data",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.it)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.ja={monthNames:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],monthNamesShort:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],dayNames:["日曜日","月曜日","ç«æ›œæ—¥","水曜日","木曜日","金曜日","土曜日"],dayNamesShort:["æ—¥","月","ç«","æ°´","木","金","土"],dayNamesMin:["æ—¥","月","ç«","æ°´","木","金","土"],dateFormat:"yyyy/mm/dd",firstDay:0,renderer:a.extend({},a.datepick.defaultRenderer,{month:a.datepick.defaultRenderer.month.replace(/monthHeader/,"monthHeader:yyyyå¹´ MM")}),prevText:"<å‰",prevStatus:"剿œˆã‚’表示ã—ã¾ã™",prevJumpText:"<<",prevJumpStatus:"å‰å¹´ã‚’表示ã—ã¾ã™",nextText:"次>",nextStatus:"翌月を表示ã—ã¾ã™",nextJumpText:">>",nextJumpStatus:"翌年を表示ã—ã¾ã™",currentText:"今日",currentStatus:"今月を表示ã—ã¾ã™",todayText:"今日",todayStatus:"今月を表示ã—ã¾ã™",clearText:"クリア",clearStatus:"日付をクリアã—ã¾ã™",closeText:"é–‰ã˜ã‚‹",closeStatus:"変更ã›ãšã«é–‰ã˜ã¾ã™",yearStatus:"表示ã™ã‚‹å¹´ã‚’変更ã—ã¾ã™",monthStatus:"表示ã™ã‚‹æœˆã‚’変更ã—ã¾ã™",weekText:"週",weekStatus:"暦週ã§ç¬¬ä½•週目ã‹ã‚’表ã—ã¾ã™",dayStatus:"Mdæ—¥(D)",defaultStatus:"æ—¥ä»˜ã‚’é¸æŠžã—ã¾ã™",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.ja)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.ka={monthNames:["იáƒáƒœáƒ•áƒáƒ áƒ˜","თებერვáƒáƒšáƒ˜","მáƒáƒ áƒ¢áƒ˜","áƒáƒžáƒ áƒ˜áƒšáƒ˜","მáƒáƒ˜áƒ¡áƒ˜","ივნისი","ივლისი","áƒáƒ’ვისტáƒ","სექტემბერი","áƒáƒ¥áƒ¢áƒáƒ›áƒ‘ერი","ნáƒáƒ”მბერი","დეკემბერი"],monthNamesShort:["იáƒáƒœ","თებ","მáƒáƒ ","áƒáƒžáƒ ","მáƒáƒ˜áƒ¡áƒ˜","ივნ","ივლ","áƒáƒ’ვ","სექ","áƒáƒ¥áƒ¢","ნáƒáƒ”","დეკ"],dayNames:["კვირáƒ","áƒáƒ áƒ¨áƒáƒ‘áƒáƒ—ი","სáƒáƒ›áƒ¨áƒáƒ‘áƒáƒ—ი","áƒáƒ—ხშáƒáƒ‘áƒáƒ—ი","ხუთშáƒáƒ‘áƒáƒ—ი","პáƒáƒ áƒáƒ¡áƒ™áƒ”ვი","შáƒáƒ‘áƒáƒ—ი"],dayNamesShort:["კვ","áƒáƒ áƒ¨","სáƒáƒ›","áƒáƒ—ხ","ხუთ","პáƒáƒ ","შáƒáƒ‘"],dayNamesMin:["კვ","áƒáƒ ","სმ","áƒáƒ—","ხშ","პრ","შბ"],dateFormat:"dd/mm/yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"<უკáƒáƒœ",prevStatus:"წინრთვე",prevJumpText:"<<",prevJumpStatus:"წინრწელი",nextText:"წინ>",nextStatus:"შემდეგი თვე",nextJumpText:">>",nextJumpStatus:"შემდეგი წელი",currentText:"მიმდინáƒáƒ áƒ”",currentStatus:"მიმდინáƒáƒ áƒ” თვე",todayText:"დღეს",todayStatus:"მიმდინáƒáƒ áƒ” დღე",clearText:"გáƒáƒ¡áƒ£áƒ¤áƒ—áƒáƒ•ებáƒ",clearStatus:"მიმდინáƒáƒ áƒ” თáƒáƒ áƒ˜áƒ¦áƒ˜áƒ¡ წáƒáƒ¨áƒšáƒ",closeText:"áƒáƒ áƒ˜áƒ¡",closeStatus:"დáƒáƒ®áƒ£áƒ áƒ•რუცვლილებáƒáƒ“",yearStatus:"სხვრწელი",monthStatus:"სხვრთვე",weekText:"კვ",weekStatus:"წლის კვირáƒ",dayStatus:"áƒáƒ˜áƒ áƒ©áƒ˜áƒ”თ DD, M d",defaultStatus:"áƒáƒ˜áƒ¦áƒ©áƒ˜áƒ”თ თáƒáƒ áƒ˜áƒ¦áƒ˜",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.ka)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.km={monthNames:["ážáŸ‚​មករា","ážáŸ‚​កុម្ភៈ","ážáŸ‚​មិនា","ážáŸ‚​មáŸážŸáž¶","ážáŸ‚​ឧសភា","ážáŸ‚​មិážáž»áž“áž¶","ážáŸ‚​កក្កដា","ážáŸ‚​សីហា","ážáŸ‚​កញ្ញា","ážáŸ‚​ážáž»áž›áž¶","ážáŸ‚​វិច្ឆិកា","ážáŸ‚​ធ្នូ"],monthNamesShort:["មក","កុ","មិនា","មáŸ","ឧស","មិážáž»","កក្ក","សី","កញ្ញា","ážáž»áž›áž¶","វិច្ឆិ","ធ្នូ"],dayNames:["ážáŸ’ងៃ​អាទិážáŸ’áž™","ážáŸ’ងៃ​ចន្ទ","ážáŸ’ងៃ​អង្គារ","ážáŸ’ងៃ​ពុធ","ážáŸ’ងៃ​ព្រហស្បážáŸ’ážáž·áŸ","ážáŸ’ងៃ​សុក្រ","ážáŸ’ងៃ​សៅរáŸ"],dayNamesShort:["អា","ចន្ទ","អង្គ","ពុធ","ព្រហ","សុ","សៅរáŸ"],dayNamesMin:["អា","áž…","អ","áž–áž»","ព្រ","សុ","ស"],dateFormat:"dd/mm/yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"ážáž™â€‹áž€áŸ’រោយ",prevStatus:"",prevJumpText:"<<",prevJumpStatus:"",nextText:"ទៅ​មុáž",nextStatus:"",nextJumpText:">>",nextJumpStatus:"",currentText:"ážáŸ’ងៃ​នáŸáŸ‡",currentStatus:"",todayText:"ážáŸ’ងៃ​នáŸáŸ‡",todayStatus:"",clearText:"X",clearStatus:"",closeText:"រួច​រាល់",closeStatus:"",yearStatus:"",monthStatus:"",weekText:"Wk",weekStatus:"",dayStatus:"DD d MM",defaultStatus:"",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.km)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.ko={monthNames:["1ì›”","2ì›”","3ì›”","4ì›”","5ì›”","6ì›”","7ì›”","8ì›”","9ì›”","10ì›”","11ì›”","12ì›”"],monthNamesShort:["1ì›”","2ì›”","3ì›”","4ì›”","5ì›”","6ì›”","7ì›”","8ì›”","9ì›”","10ì›”","11ì›”","12ì›”"],dayNames:["ì¼ìš”ì¼","월요ì¼","화요ì¼","수요ì¼","목요ì¼","금요ì¼","토요ì¼"],dayNamesShort:["ì¼","ì›”","í™”","수","목","금","토"],dayNamesMin:["ì¼","ì›”","í™”","수","목","금","토"],dateFormat:"yyyy-mm-dd",firstDay:0,renderer:a.extend({},a.datepick.defaultRenderer,{month:a.datepick.defaultRenderer.month.replace(/monthHeader/,"monthHeader:yyyyë…„ MM")}),prevText:"ì´ì „달",prevStatus:"ì´ì „ë‹¬ì„ í‘œì‹œí•©ë‹ˆë‹¤",prevJumpText:"<<",prevJumpStatus:"ì´ì „ ì—°ë„를 표시합니다",nextText:"다ìŒë‹¬",nextStatus:"다ìŒë‹¬ì„ 표시합니다",nextJumpText:">>",nextJumpStatus:"ë‹¤ìŒ ì—°ë„를 표시합니다",currentText:"현재",currentStatus:"입력한 ë‹¬ì„ í‘œì‹œí•©ë‹ˆë‹¤",todayText:"오늘",todayStatus:"ì´ë²ˆë‹¬ì„ 표시합니다",clearText:"지우기",clearStatus:"입력한 날짜를 ì§€ì›ë‹ˆë‹¤",closeText:"닫기",closeStatus:"",yearStatus:"표시할 ì—°ë„를 변경합니다",monthStatus:"표시할 ì›”ì„ ë³€ê²½í•©ë‹ˆë‹¤",weekText:"Wk",weekStatus:"해당 ì—°ë„ì˜ ì£¼ì°¨",dayStatus:"M dì¼ (D)",defaultStatus:"날짜를 ì„ íƒí•˜ì„¸ìš”",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.ko)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.lt={monthNames:["Sausis","Vasaris","Kovas","Balandis","Gegužė","Birželis","Liepa","RugpjÅ«tis","RugsÄ—jis","Spalis","Lapkritis","Gruodis"],monthNamesShort:["Sau","Vas","Kov","Bal","Geg","Bir","Lie","Rugp","Rugs","Spa","Lap","Gru"],dayNames:["sekmadienis","pirmadienis","antradienis","treÄiadienis","ketvirtadienis","penktadienis","Å¡eÅ¡tadienis"],dayNamesShort:["sek","pir","ant","tre","ket","pen","Å¡eÅ¡"],dayNamesMin:["Se","Pr","An","Tr","Ke","Pe","Å e"],dateFormat:"yyyy-mm-dd",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"<Atgal",prevStatus:"",prevJumpText:"<<",prevJumpStatus:"",nextText:"Pirmyn>",nextStatus:"",nextJumpText:">>",nextJumpStatus:"",currentText:"Å iandien",currentStatus:"",todayText:"Å iandien",todayStatus:"",clearText:"IÅ¡valyti",clearStatus:"",closeText:"Uždaryti",closeStatus:"",yearStatus:"",monthStatus:"",weekText:"Wk",weekStatus:"",dayStatus:"D, M d",defaultStatus:"",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.lt)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.lv={monthNames:["JanvÄris","FebruÄris","Marts","AprÄ«lis","Maijs","JÅ«nijs","JÅ«lijs","Augusts","Septembris","Oktobris","Novembris","Decembris"],monthNamesShort:["Jan","Feb","Mar","Apr","Mai","JÅ«n","JÅ«l","Aug","Sep","Okt","Nov","Dec"],dayNames:["svÄ“tdiena","pirmdiena","otrdiena","treÅ¡diena","ceturtdiena","piektdiena","sestdiena"],dayNamesShort:["svt","prm","otr","tre","ctr","pkt","sst"],dayNamesMin:["Sv","Pr","Ot","Tr","Ct","Pk","Ss"],dateFormat:"dd-mm-yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"Iepr",prevStatus:"",prevJumpText:"<<",prevJumpStatus:"",nextText:"NÄka",nextStatus:"",nextJumpText:">>",nextJumpStatus:"",currentText:"Å odien",currentStatus:"",todayText:"Å odien",todayStatus:"",clearText:"NotÄ«rÄ«t",clearStatus:"",closeText:"AizvÄ“rt",closeStatus:"",yearStatus:"",monthStatus:"",weekText:"Nav",weekStatus:"",dayStatus:"D, M d",defaultStatus:"",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.lv)}(jQuery),function(a){"use strict";a.datepick.regionalOptions["me-ME"]={monthNames:["Januar","Februar","Mart","April","Maj","Jun","Jul","Avgust","Septembar","Oktobar","Novembar","Decembar"],monthNamesShort:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Avg","Sep","Okt","Nov","Dec"],dayNames:["NeÄ‘elja","PoneÄ‘eljak","Utorak","Srijeda","ÄŒetvrtak","Petak","Subota"],dayNamesShort:["NeÄ‘","Pon","Uto","Sri","ÄŒet","Pet","Sub"],dayNamesMin:["Ne","Po","Ut","Sr","ÄŒe","Pe","Su"],dateFormat:"dd/mm/yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"<",prevStatus:"Prikaži prethodni mjesec",prevJumpText:"<<",prevJumpStatus:"Prikaži prethodnu godinu",nextText:">",nextStatus:"Prikaži sljedeći mjesec",nextJumpText:">>",nextJumpStatus:"Prikaži sljedeću godinu",currentText:"Danas",currentStatus:"Tekući mjesec",todayText:"Danas",todayStatus:"Tekući mjesec",clearText:"ObriÅ¡i",clearStatus:"ObriÅ¡i trenutni datum",closeText:"Zatvori",closeStatus:"Zatvori kalendar",yearStatus:"Prikaži godine",monthStatus:"Prikaži mjesece",weekText:"Sed",weekStatus:"Sedmica",dayStatus:"'Datum' DD, M d",defaultStatus:"Odaberi datum",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions["me-ME"])}(jQuery),function(a){"use strict";a.datepick.regionalOptions.me={monthNames:["Јануар","Фебруар","Март","Ðприл","Мај","Јун","Јул","ÐвгуÑÑ‚","Септембар","Октобар","Ðовембар","Децембар"],monthNamesShort:["Јан","Феб","Мар","Ðпр","Мај","Јун","Јул","Ðвг","Сеп","Окт","Ðов","Дец"],dayNames:["Ðеђеља","Понеђељак","Уторак","Сриједа","Четвртак","Петак","Субота"],dayNamesShort:["Ðеђ","Пон","Уто","Сри","Чет","Пет","Суб"],dayNamesMin:["Ðе","По","Ут","Ср","Че","Пе","Су"],dateFormat:"dd/mm/yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"<",prevStatus:"Прикажи претходни мјеÑец",prevJumpText:"<<",prevJumpStatus:"Прикажи претходну годину",nextText:">",nextStatus:"Прикажи Ñљедећи мјеÑец",nextJumpText:">>",nextJumpStatus:"Прикажи Ñљедећу годину",currentText:"ДанаÑ",currentStatus:"Текући мјеÑец",todayText:"ДанаÑ",todayStatus:"Текући мјеÑец",clearText:"Обриши",clearStatus:"Обриши тренутни датум",closeText:"Затвори",closeStatus:"Затвори календар",yearStatus:"Прикажи године",monthStatus:"Прикажи мјеÑеце",weekText:"Сед",weekStatus:"Седмица",dayStatus:"'Датум' DD d MM",defaultStatus:"Одабери датум",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.me)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.mk={monthNames:["Јануари","Февруари","Март","Ðприл","Мај","Јуни","Јули","ÐвгуÑÑ‚","Септември","Октомври","Ðоември","Декември"],monthNamesShort:["Јан","Фев","Мар","Ðпр","Мај","Јун","Јул","Ðвг","Сеп","Окт","Ðов","Дек"],dayNames:["Ðедела","Понеделник","Вторник","Среда","Четврток","Петок","Сабота"],dayNamesShort:["Ðед","Пон","Вто","Сре","Чет","Пет","Саб"],dayNamesMin:["Ðе","По","Ð’Ñ‚","Ср","Че","Пе","Са"],dateFormat:"dd/mm/yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"Претх.",prevStatus:"Прикажи го претходниот меÑец",prevJumpText:"<<",prevJumpStatus:"Прикажи ја претходната година",nextText:"Следен",nextStatus:"Прикажи го Ñледниот меÑец",nextJumpText:">>",nextJumpStatus:"Прикажи ја Ñледната година",currentText:"Тековен",currentStatus:"Прикажи го тековниот меÑец",todayText:"ДенеÑ",todayStatus:"Прикажи го денешниот меÑец",clearText:"Бриши",clearStatus:"Избриши го тековниот датум",closeText:"Затвори",closeStatus:"Затвори без промени",yearStatus:"Избери друга година",monthStatus:"Избери друг меÑец",weekText:"Ðед",weekStatus:"Ðедела во годината",dayStatus:"Избери DD, M d",defaultStatus:"Избери датум",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.mk)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.ml={monthNames:["ജനàµà´µà´°à´¿","ഫെബàµà´°àµà´µà´°à´¿","മാരàµâ€à´šàµà´šàµ","à´à´ªàµà´°à´¿à´²àµâ€","മേയàµ","ജൂണàµâ€","ജൂലൈ","ആഗസàµà´±àµà´±àµ","സെപàµà´±àµà´±à´‚ബരàµâ€","à´’à´•àµà´Ÿàµ‹à´¬à´°àµâ€","നവംബരàµâ€","ഡിസംബരàµâ€"],monthNamesShort:["ജനàµ","ഫെബàµ","മാരàµâ€","à´à´ªàµà´°à´¿","മേയàµ","ജൂണàµâ€","ജൂലാ","ആഗ","സെപàµ","à´’à´•àµà´Ÿàµ‹","നവം","à´¡à´¿à´¸"],dayNames:["ഞായരàµâ€","തിങàµà´•à´³àµâ€","ചൊവàµà´µ","à´¬àµà´§à´¨àµâ€","à´µàµà´¯à´¾à´´à´‚","വെളàµà´³à´¿","ശനി"],dayNamesShort:["ഞായ","തിങàµà´•","ചൊവàµà´µ","à´¬àµà´§","à´µàµà´¯à´¾à´´à´‚","വെളàµà´³à´¿","ശനി"],dayNamesMin:["à´žà´¾","തി","ചൊ","à´¬àµ","à´µàµà´¯à´¾","വെ","à´¶"],dateFormat:"dd/mm/yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"à´®àµà´¨àµà´¨à´¤àµà´¤àµ†",prevStatus:"",prevJumpText:"<<",prevJumpStatus:"",nextText:"à´…à´Ÿàµà´¤àµà´¤à´¤àµ ",nextStatus:"",nextJumpText:">>",nextJumpStatus:"",currentText:"ഇനàµà´¨àµ",currentStatus:"",todayText:"ഇനàµà´¨àµ",todayStatus:"",clearText:"X",clearStatus:"",closeText:"à´¶à´°à´¿",closeStatus:"",yearStatus:"",monthStatus:"",weekText:"à´†",weekStatus:"",dayStatus:"DD d MM",defaultStatus:"",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.ml)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.ms={monthNames:["Januari","Februari","Mac","April","Mei","Jun","Julai","Ogos","September","Oktober","November","Disember"],monthNamesShort:["Jan","Feb","Mac","Apr","Mei","Jun","Jul","Ogo","Sep","Okt","Nov","Dis"],dayNames:["Ahad","Isnin","Selasa","Rabu","Khamis","Jumaat","Sabtu"],dayNamesShort:["Aha","Isn","Sel","Rab","Kha","Jum","Sab"],dayNamesMin:["Ah","Is","Se","Ra","Kh","Ju","Sa"],dateFormat:"dd/mm/yyyy",firstDay:0,renderer:a.datepick.defaultRenderer,prevText:"<Sebelum",prevStatus:"Tunjukkan bulan lepas",prevJumpText:"<<",prevJumpStatus:"Tunjukkan tahun lepas",nextText:"Selepas>",nextStatus:"Tunjukkan bulan depan",nextJumpText:">>",nextJumpStatus:"Tunjukkan tahun depan",currentText:"hari ini",currentStatus:"Tunjukkan bulan terkini",todayText:"hari ini",todayStatus:"Tunjukkan bulan terkini",clearText:"Padam",clearStatus:"Padamkan tarikh terkini",closeText:"Tutup",closeStatus:"Tutup tanpa perubahan",yearStatus:"Tunjukkan tahun yang lain",monthStatus:"Tunjukkan bulan yang lain",weekText:"Mg",weekStatus:"Minggu bagi tahun ini",dayStatus:"DD, d MM",defaultStatus:"Sila pilih tarikh",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.ms)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.mt={monthNames:["Jannar","Frar","Marzu","April","Mejju","Ä unju","Lulju","Awissu","Settembru","Ottubru","Novembru","DiÄ‹embru"],monthNamesShort:["Jan","Fra","Mar","Apr","Mej","Ä un","Lul","Awi","Set","Ott","Nov","DiÄ‹"],dayNames:["Il-Ħadd","It-Tnejn","It-Tlieta","L-Erbgħa","Il-Ħamis","Il-Ä imgħa","Is-Sibt"],dayNamesShort:["Ħad","Tne","Tli","Erb","Ħam","Ä im","Sib"],dayNamesMin:["Ħ","T","T","E","Ħ","Ä ","S"],dateFormat:"dd/mm/yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"Ta Qabel",prevStatus:"Ix-xahar ta qabel",prevJumpText:"<<",prevJumpStatus:"Is-sena ta qabel",nextText:"Li Jmiss",nextStatus:"Ix-xahar li jmiss",nextJumpText:">>",nextJumpStatus:"Is-sena li jmiss",currentText:"Illum",currentStatus:"Ix-xahar ta llum",todayText:"Illum",todayStatus:"Uri ix-xahar ta llum",clearText:"Ħassar",clearStatus:"Ħassar id-data",closeText:"Lest",closeStatus:"Għalaq mingħajr tibdiliet",yearStatus:"Uri sena differenti",monthStatus:"Uri xahar differenti",weekText:"Ä m",weekStatus:"Il-Ä imgħa fis-sena",dayStatus:"Għazel DD, M d",defaultStatus:"Għazel data",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.mt)}(jQuery),function(a){"use strict";a.datepick.regionalOptions["nl-BE"]={monthNames:["januari","februari","maart","april","mei","juni","juli","augustus","september","oktober","november","december"],monthNamesShort:["jan","feb","maa","apr","mei","jun","jul","aug","sep","okt","nov","dec"],dayNames:["zondag","maandag","dinsdag","woensdag","donderdag","vrijdag","zaterdag"],dayNamesShort:["zon","maa","din","woe","don","vri","zat"],dayNamesMin:["zo","ma","di","wo","do","vr","za"],dateFormat:"dd/mm/yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"â†",prevStatus:"Bekijk de vorige maand",prevJumpText:"«",prevJumpStatus:"Bekijk het vorige jaar",nextText:"→",nextStatus:"Bekijk de volgende maand",nextJumpText:"»",nextJumpStatus:"Bekijk het volgende jaar",currentText:"Vandaag",currentStatus:"Bekijk de huidige maand",todayText:"Vandaag",todayStatus:"Bekijk de huidige maand",clearText:"Wissen",clearStatus:"Wis de huidige datum",closeText:"Sluiten",closeStatus:"Sluit zonder verandering",yearStatus:"Bekijk een ander jaar",monthStatus:"Bekijk een andere maand",weekText:"Wk",weekStatus:"Week van het jaar",dayStatus:"dd/mm/yyyy",defaultStatus:"Kies een datum",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions["nl-BE"])}(jQuery),function(a){"use strict";a.datepick.regionalOptions.nl={monthNames:["januari","februari","maart","april","mei","juni","juli","augustus","september","oktober","november","december"],monthNamesShort:["jan","feb","maa","apr","mei","jun","jul","aug","sep","okt","nov","dec"],dayNames:["zondag","maandag","dinsdag","woensdag","donderdag","vrijdag","zaterdag"],dayNamesShort:["zon","maa","din","woe","don","vri","zat"],dayNamesMin:["zo","ma","di","wo","do","vr","za"],dateFormat:"dd-mm-yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"â†",prevStatus:"Bekijk de vorige maand",prevJumpText:"«",prevJumpStatus:"Bekijk het vorige jaar",nextText:"→",nextStatus:"Bekijk de volgende maand",nextJumpText:"»",nextJumpStatus:"Bekijk het volgende jaar",currentText:"Vandaag",currentStatus:"Bekijk de huidige maand",todayText:"Vandaag",todayStatus:"Bekijk de huidige maand",clearText:"Wissen",clearStatus:"Wis de huidige datum",closeText:"Sluiten",closeStatus:"Sluit zonder verandering",yearStatus:"Bekijk een ander jaar",monthStatus:"Bekijk een andere maand",weekText:"Wk",weekStatus:"Week van het jaar",dayStatus:"dd-mm-yyyy",defaultStatus:"Kies een datum",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.nl)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.no={monthNames:["Januar","Februar","Mars","April","Mai","Juni","Juli","August","September","Oktober","November","Desember"],monthNamesShort:["Jan","Feb","Mar","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Des"],dayNamesShort:["Søn","Man","Tir","Ons","Tor","Fre","Lør"],dayNames:["Søndag","Mandag","Tirsdag","Onsdag","Torsdag","Fredag","Lørdag"],dayNamesMin:["Sø","Ma","Ti","On","To","Fr","Lø"],dateFormat:"dd.mm.yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"«Forrige",prevStatus:"",prevJumpText:"<<",prevJumpStatus:"",nextText:"Neste»",nextStatus:"",nextJumpText:">>",nextJumpStatus:"",currentText:"I dag",currentStatus:"",todayText:"I dag",todayStatus:"",clearText:"Tøm",clearStatus:"",closeText:"Lukk",closeStatus:"",yearStatus:"",monthStatus:"",weekText:"Uke",weekStatus:"",dayStatus:"D, M d",defaultStatus:"",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.no)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.pl={monthNames:["StyczeÅ„","Luty","Marzec","KwiecieÅ„","Maj","Czerwiec","Lipiec","SierpieÅ„","WrzesieÅ„","Październik","Listopad","GrudzieÅ„"],monthNamesShort:["Sty","Lu","Mar","Kw","Maj","Cze","Lip","Sie","Wrz","Pa","Lis","Gru"],dayNames:["Niedziela","Poniedzialek","Wtorek","Åšroda","Czwartek","PiÄ…tek","Sobota"],dayNamesShort:["Nie","Pn","Wt","Åšr","Czw","Pt","So"],dayNamesMin:["N","Pn","Wt","Åšr","Cz","Pt","So"],dateFormat:"yyyy-mm-dd",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"<Poprzedni",prevStatus:"Pokaż poprzedni miesiÄ…c",prevJumpText:"<<",prevJumpStatus:"",nextText:"NastÄ™pny>",nextStatus:"Pokaż nastÄ™pny miesiÄ…c",nextJumpText:">>",nextJumpStatus:"",currentText:"DziÅ›",currentStatus:"Pokaż aktualny miesiÄ…c",todayText:"DziÅ›",todayStatus:"Pokaż aktualny miesiÄ…c",clearText:"Wyczyść",clearStatus:"Wyczyść obecnÄ… datÄ™",closeText:"Zamknij",closeStatus:"Zamknij bez zapisywania",yearStatus:"Pokaż inny rok",monthStatus:"Pokaż inny miesiÄ…c",weekText:"Tydz",weekStatus:"TydzieÅ„ roku",dayStatus:"'Wybierz' D, M d",defaultStatus:"Wybierz datÄ™",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.pl)}(jQuery),function(a){"use strict";a.datepick.regionalOptions["pt-BR"]={monthNames:["Janeiro","Fevereiro","Março","Abril","Maio","Junho","Julho","Agosto","Setembro","Outubro","Novembro","Dezembro"],monthNamesShort:["Jan","Fev","Mar","Abr","Mai","Jun","Jul","Ago","Set","Out","Nov","Dez"],dayNames:["Domingo","Segunda-feira","Terça-feira","Quarta-feira","Quinta-feira","Sexta-feira","Sábado"],dayNamesShort:["Dom","Seg","Ter","Qua","Qui","Sex","Sáb"],dayNamesMin:["D","S","T","Q","Q","S","S"],dateFormat:"dd/mm/yyyy",firstDay:0,renderer:a.datepick.defaultRenderer,prevText:"<Anterior",prevStatus:"Mostra o mês anterior",prevJumpText:"<<",prevJumpStatus:"Mostra o ano anterior",nextText:"Próximo>",nextStatus:"Mostra o próximo mês",nextJumpText:">>",nextJumpStatus:"Mostra o próximo ano",currentText:"Atual",currentStatus:"Mostra o mês atual",todayText:"Hoje",todayStatus:"Vai para hoje",clearText:"Limpar",clearStatus:"Limpar data",closeText:"Fechar",closeStatus:"Fechar o calendário",yearStatus:"Selecionar ano",monthStatus:"Selecionar mês",weekText:"s",weekStatus:"Semana do ano",dayStatus:"DD, d 'de' M 'de' yyyy",defaultStatus:"Selecione um dia",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions["pt-BR"])}(jQuery),function(a){"use strict";a.datepick.regionalOptions.pt={monthNames:["Janeiro","Fevereiro","Março","Abril","Maio","Junho","Julho","Agosto","Setembro","Outubro","Novembro","Dezembro"], +monthNamesShort:["Jan","Fev","Mar","Abr","Mai","Jun","Jul","Ago","Set","Out","Nov","Dez"],dayNames:["Domingo","Segunda-feira","Terça-feira","Quarta-feira","Quinta-feira","Sexta-feira","Sábado"],dayNamesShort:["Dom","Seg","Ter","Qua","Qui","Sex","Sáb"],dayNamesMin:["D","S","T","Q","Q","S","S"],dateFormat:"dd/mm/yyyy",firstDay:0,renderer:a.datepick.defaultRenderer,prevText:"<Anterior",prevStatus:"Mês anterior",prevJumpText:"<<",prevJumpStatus:"Ano anterior",nextText:"Próximo>",nextStatus:"Próximo mês",nextJumpText:">>",nextJumpStatus:"Próximo ano",currentText:"Atual",currentStatus:"Mês atual",todayText:"Hoje",todayStatus:"Hoje",clearText:"Limpar",clearStatus:"Limpar data",closeText:"Fechar",closeStatus:"Fechar o calendário",yearStatus:"Selecionar ano",monthStatus:"Selecionar mês",weekText:"s",weekStatus:"Semana do ano",dayStatus:"DD, d 'de' M 'de' yyyy",defaultStatus:"Selecione um dia",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.pt)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.rm={monthNames:["Schaner","Favrer","Mars","Avrigl","Matg","Zercladur","Fanadur","Avust","Settember","October","November","December"],monthNamesShort:["Scha","Fev","Mar","Avr","Matg","Zer","Fan","Avu","Sett","Oct","Nov","Dec"],dayNames:["Dumengia","Glindesdi","Mardi","Mesemna","Gievgia","Venderdi","Sonda"],dayNamesShort:["Dum","Gli","Mar","Mes","Gie","Ven","Som"],dayNamesMin:["Du","Gl","Ma","Me","Gi","Ve","So"],dateFormat:"dd/mm/yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"<Suandant",prevStatus:"",prevJumpText:"<<",prevJumpStatus:"",nextText:"Precedent>",nextStatus:"",nextJumpText:">>",nextJumpStatus:"",currentText:"Actual",currentStatus:"",todayText:"Actual",todayStatus:"",clearText:"X",clearStatus:"",closeText:"Serrar",closeStatus:"",yearStatus:"",monthStatus:"",weekText:"emna",weekStatus:"",dayStatus:"DD d MM",defaultStatus:"",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.rm)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.ro={monthNames:["Ianuarie","Februarie","Martie","Aprilie","Mai","Iunie","Iulie","August","Septembrie","Octombrie","Noiembrie","Decembrie"],monthNamesShort:["Ian","Feb","Mar","Apr","Mai","Iun","Iul","Aug","Sep","Oct","Noi","Dec"],dayNames:["Duminică","Luni","Marti","Miercuri","Joi","Vineri","Sâmbătă"],dayNamesShort:["Dum","Lun","Mar","Mie","Joi","Vin","Sâm"],dayNamesMin:["Du","Lu","Ma","Mi","Jo","Vi","Sâ"],dateFormat:"dd.mm.yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"«Precedentă",prevStatus:"Arată luna precedenta",prevJumpText:"««",prevJumpStatus:"",nextText:"Urmatoare»",nextStatus:"Arată luna urmatoare",nextJumpText:"»»",nextJumpStatus:"",currentText:"Azi",currentStatus:"Arată luna curenta",todayText:"Azi",todayStatus:"Arată luna curenta",clearText:"Curat",clearStatus:"Sterge data curenta",closeText:"ÃŽnchide",closeStatus:"ÃŽnchide fara schimbare",yearStatus:"Arată un an diferit",monthStatus:"Arată o luna diferita",weekText:"Săpt",weekStatus:"Săptamana anului",dayStatus:"Selectează D, M d",defaultStatus:"Selectează o data",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.ro)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.ru={monthNames:["Январь","Февраль","Март","Ðпрель","Май","Июнь","Июль","ÐвгуÑÑ‚","СентÑбрь","ОктÑбрь","ÐоÑбрь","Декабрь"],monthNamesShort:["Янв","Фев","Мар","Ðпр","Май","Июн","Июл","Ðвг","Сен","Окт","ÐоÑ","Дек"],dayNames:["воÑкреÑенье","понедельник","вторник","Ñреда","четверг","пÑтница","Ñуббота"],dayNamesShort:["вÑк","пнд","втр","Ñрд","чтв","птн","Ñбт"],dayNamesMin:["Ð’Ñ","Пн","Ð’Ñ‚","Ср","Чт","Пт","Сб"],dateFormat:"dd.mm.yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"<Пред",prevStatus:"",prevJumpText:"<<",prevJumpStatus:"",nextText:"След>",nextStatus:"",nextJumpText:">>",nextJumpStatus:"",currentText:"СегоднÑ",currentStatus:"",todayText:"СегоднÑ",todayStatus:"",clearText:"ОчиÑтить",clearStatus:"",closeText:"Закрыть",closeStatus:"",yearStatus:"",monthStatus:"",weekText:"Ðе",weekStatus:"",dayStatus:"D, M d",defaultStatus:"",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.ru)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.sk={monthNames:["Január","Február","Marec","Apríl","Máj","Jún","Júl","August","September","Október","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","Máj","Jún","Júl","Aug","Sep","Okt","Nov","Dec"],dayNames:["Nedel'a","Pondelok","Utorok","Streda","Å tvrtok","Piatok","Sobota"],dayNamesShort:["Ned","Pon","Uto","Str","Å tv","Pia","Sob"],dayNamesMin:["Ne","Po","Ut","St","Å t","Pia","So"],dateFormat:"dd.mm.yyyy",firstDay:0,renderer:a.datepick.defaultRenderer,prevText:"<Predchádzajúci",prevStatus:"",prevJumpText:"<<",prevJumpStatus:"",nextText:"Nasledujúci>",nextStatus:"",nextJumpText:">>",nextJumpStatus:"",currentText:"Dnes",currentStatus:"",todayText:"Dnes",todayStatus:"",clearText:"ZmazaÅ¥",clearStatus:"",closeText:"ZavrieÅ¥",closeStatus:"",yearStatus:"",monthStatus:"",weekText:"Ty",weekStatus:"",dayStatus:"D, M d",defaultStatus:"",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.sk)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.sl={monthNames:["Januar","Februar","Marec","April","Maj","Junij","Julij","Avgust","September","Oktober","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Avg","Sep","Okt","Nov","Dec"],dayNames:["Nedelja","Ponedeljek","Torek","Sreda","Četrtek","Petek","Sobota"],dayNamesShort:["Ned","Pon","Tor","Sre","Čet","Pet","Sob"],dayNamesMin:["Ne","Po","To","Sr","Če","Pe","So"],dateFormat:"dd.mm.yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"<Prejšnji",prevStatus:"Prikaži prejšnji mesec",prevJumpText:"<<",prevJumpStatus:"",nextText:"Naslednji>",nextStatus:"Prikaži naslednji mesec",nextJumpText:">>",nextJumpStatus:"",currentText:"Trenutni",currentStatus:"Prikaži trenutni mesec",todayText:"Trenutni",todayStatus:"Prikaži trenutni mesec",clearText:"Izbriši",clearStatus:"Izbriši trenutni datum",closeText:"Zapri",closeStatus:"Zapri brez spreminjanja",yearStatus:"Prikaži drugo leto",monthStatus:"Prikaži drug mesec",weekText:"Teden",weekStatus:"Teden v letu",dayStatus:"Izberi DD, d MM yy",defaultStatus:"Izbira datuma",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.sl)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.sq={monthNames:["Janar","Shkurt","Mars","Prill","Maj","Qershor","Korrik","Gusht","Shtator","Tetor","Nëntor","Dhjetor"],monthNamesShort:["Jan","Shk","Mar","Pri","Maj","Qer","Kor","Gus","Sht","Tet","Nën","Dhj"],dayNames:["E Diel","E Hënë","E Martë","E Mërkurë","E Enjte","E Premte","E Shtune"],dayNamesShort:["Di","Hë","Ma","Më","En","Pr","Sh"],dayNamesMin:["Di","Hë","Ma","Më","En","Pr","Sh"],dateFormat:"dd.mm.yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"<mbrapa",prevStatus:"trego muajin e fundit",prevJumpText:"<<",prevJumpStatus:"",nextText:"Përpara>",nextStatus:"trego muajin tjetër",nextJumpText:">>",nextJumpStatus:"",currentText:"sot",currentStatus:"",todayText:"sot",todayStatus:"",clearText:"fshije",clearStatus:"fshije datën aktuale",closeText:"mbylle",closeStatus:"mbylle pa ndryshime",yearStatus:"trego tjetër vit",monthStatus:"trego muajin tjetër",weekText:"Ja",weekStatus:"Java e muajit",dayStatus:"'Zgjedh' D, M d",defaultStatus:"Zgjedhe një datë",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.sq)}(jQuery),function(a){"use strict";a.datepick.regionalOptions["sr-SR"]={monthNames:["Januar","Februar","Mart","April","Maj","Jun","Jul","Avgust","Septembar","Oktobar","Novembar","Decembar"],monthNamesShort:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Avg","Sep","Okt","Nov","Dec"],dayNames:["Nedelja","Ponedeljak","Utorak","Sreda","ÄŒetvrtak","Petak","Subota"],dayNamesShort:["Ned","Pon","Uto","Sre","ÄŒet","Pet","Sub"],dayNamesMin:["Ne","Po","Ut","Sr","ÄŒe","Pe","Su"],dateFormat:"dd/mm/yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"<",prevStatus:"Prikaži prethodni mesec",prevJumpText:"<<",prevJumpStatus:"Prikaži prethodnu godinu",nextText:">",nextStatus:"Prikaži sledeći mesec",nextJumpText:">>",nextJumpStatus:"Prikaži sledeću godinu",currentText:"Danas",currentStatus:"Tekući mesec",todayText:"Danas",todayStatus:"Tekući mesec",clearText:"ObriÅ¡i",clearStatus:"ObriÅ¡i trenutni datum",closeText:"Zatvori",closeStatus:"Zatvori kalendar",yearStatus:"Prikaži godine",monthStatus:"Prikaži mesece",weekText:"Sed",weekStatus:"Sedmica",dayStatus:"'Datum' D, M d",defaultStatus:"Odaberi datum",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions["sr-SR"])}(jQuery),function(a){"use strict";a.datepick.regionalOptions.sr={monthNames:["Јануар","Фебруар","Март","Ðприл","Мај","Јун","Јул","ÐвгуÑÑ‚","Септембар","Октобар","Ðовембар","Децембар"],monthNamesShort:["Јан","Феб","Мар","Ðпр","Мај","Јун","Јул","Ðвг","Сеп","Окт","Ðов","Дец"],dayNames:["Ðедеља","Понедељак","Уторак","Среда","Четвртак","Петак","Субота"],dayNamesShort:["Ðед","Пон","Уто","Сре","Чет","Пет","Суб"],dayNamesMin:["Ðе","По","Ут","Ср","Че","Пе","Су"],dateFormat:"dd/mm/yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"<",prevStatus:"Прикажи претходни меÑец",prevJumpText:"<<",prevJumpStatus:"Прикажи претходну годину",nextText:">",nextStatus:"Прикажи Ñледећи меÑец",nextJumpText:">>",nextJumpStatus:"Прикажи Ñледећу годину",currentText:"ДанаÑ",currentStatus:"Текући меÑец",todayText:"ДанаÑ",todayStatus:"Текући меÑец",clearText:"Обриши",clearStatus:"Обриши тренутни датум",closeText:"Затвори",closeStatus:"Затвори календар",yearStatus:"Прикажи године",monthStatus:"Прикажи меÑеце",weekText:"Сед",weekStatus:"Седмица",dayStatus:"'Датум' DD d MM",defaultStatus:"Одабери датум",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.sr)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.sv={monthNames:["Januari","Februari","Mars","April","Maj","Juni","Juli","Augusti","September","Oktober","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Aug","Sep","Okt","Nov","Dec"],dayNames:["Söndag","MÃ¥ndag","Tisdag","Onsdag","Torsdag","Fredag","Lördag"],dayNamesShort:["Sön","MÃ¥n","Tis","Ons","Tor","Fre","Lör"],dayNamesMin:["Sö","MÃ¥","Ti","On","To","Fr","Lö"],dateFormat:"yyyy-mm-dd",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"«Förra",prevStatus:"",prevJumpText:"<<",prevJumpStatus:"",nextText:"Nästa»",nextStatus:"",nextJumpText:">>",nextJumpStatus:"",currentText:"Idag",currentStatus:"",todayText:"Idag",todayStatus:"",clearText:"Rensa",clearStatus:"",closeText:"Stäng",closeStatus:"",yearStatus:"",monthStatus:"",weekText:"Ve",weekStatus:"",dayStatus:"D, M d",defaultStatus:"",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.sv)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.ta={monthNames:["தை","மாசி","பஙà¯à®•à¯à®©à®¿","சிதà¯à®¤à®¿à®°à¯ˆ","வைகாசி","ஆனி","ஆடி","ஆவணி","பà¯à®°à®Ÿà¯à®Ÿà®¾à®šà®¿","à®à®ªà¯à®ªà®šà®¿","காரà¯à®¤à¯à®¤à®¿à®•ை","மாரà¯à®•ழி"],monthNamesShort:["தை","மாசி","பஙà¯","சிதà¯","வைகா","ஆனி","ஆடி","ஆவ","பà¯à®°","à®à®ªà¯","காரà¯","மாரà¯"],dayNames:["ஞாயிறà¯à®±à¯à®•à¯à®•ிழமை","திஙà¯à®•டà¯à®•ிழமை","செவà¯à®µà®¾à®¯à¯à®•à¯à®•ிழமை","பà¯à®¤à®©à¯à®•ிழமை","வியாழகà¯à®•ிழமை","வெளà¯à®³à®¿à®•à¯à®•ிழமை","சனிகà¯à®•ிழமை"],dayNamesShort:["ஞாயிறà¯","திஙà¯à®•ளà¯","செவà¯à®µà®¾à®¯à¯","பà¯à®¤à®©à¯","வியாழனà¯","வெளà¯à®³à®¿","சனி"],dayNamesMin:["ஞா","தி","செ","பà¯","வி","வெ","ச"],dateFormat:"dd/mm/yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"à®®à¯à®©à¯à®©à¯ˆà®¯à®¤à¯",prevStatus:"",prevJumpText:"<<",prevJumpStatus:"",nextText:"அடà¯à®¤à¯à®¤à®¤à¯",nextStatus:"",nextJumpText:">>",nextJumpStatus:"",currentText:"இனà¯à®±à¯",currentStatus:"",todayText:"இனà¯à®±à¯",todayStatus:"",clearText:"அழி",clearStatus:"",closeText:"மூடà¯",closeStatus:"",yearStatus:"",monthStatus:"",weekText:"Wk",weekStatus:"",dayStatus:"D, M d",defaultStatus:"",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.ta)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.th={monthNames:["มà¸à¸£à¸²à¸„ม","à¸à¸¸à¸¡à¸ à¸²à¸žà¸±à¸™à¸˜à¹Œ","มีนาคม","เมษายน","พฤษภาคม","มิถุนายน","à¸à¸£à¸à¸Žà¸²à¸„ม","สิงหาคม","à¸à¸±à¸™à¸¢à¸²à¸¢à¸™","ตุลาคม","พฤศจิà¸à¸²à¸¢à¸™","ธันวาคม"],monthNamesShort:["ม.ค.","à¸.พ.","มี.ค.","เม.ย.","พ.ค.","มิ.ย.","à¸.ค.","ส.ค.","à¸.ย.","ต.ค.","พ.ย.","ธ.ค."],dayNames:["อาทิตย์","จันทร์","อังคาร","พุธ","พฤหัสบดี","ศุà¸à¸£à¹Œ","เสาร์"],dayNamesShort:["อา.","จ.","อ.","พ.","พฤ.","ศ.","ส."],dayNamesMin:["อา.","จ.","อ.","พ.","พฤ.","ศ.","ส."],dateFormat:"dd/mm/yyyy",firstDay:0,renderer:a.datepick.defaultRenderer,prevText:"« à¸¢à¹‰à¸­à¸™",prevStatus:"",prevJumpText:"<<",prevJumpStatus:"",nextText:"ถัดไป »",nextStatus:"",nextJumpText:">>",nextJumpStatus:"",currentText:"วันนี้",currentStatus:"",todayText:"วันนี้",todayStatus:"",clearText:"ลบ",clearStatus:"",closeText:"ปิด",closeStatus:"",yearStatus:"",monthStatus:"",weekText:"Wk",weekStatus:"",dayStatus:"D, M d",defaultStatus:"",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.th)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.tr={monthNames:["Ocak","Åžubat","Mart","Nisan","Mayıs","Haziran","Temmuz","AÄŸustos","Eylül","Ekim","Kasım","Aralık"],monthNamesShort:["Oca","Åžub","Mar","Nis","May","Haz","Tem","AÄŸu","Eyl","Eki","Kas","Ara"],dayNames:["Pazar","Pazartesi","Salı","ÇarÅŸamba","PerÅŸembe","Cuma","Cumartesi"],dayNamesShort:["Pz","Pt","Sa","Ça","Pe","Cu","Ct"],dayNamesMin:["Pz","Pt","Sa","Ça","Pe","Cu","Ct"],dateFormat:"dd.mm.yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"<geri",prevStatus:"önceki ayı göster",prevJumpText:"<<",prevJumpStatus:"",nextText:"ileri>",nextStatus:"sonraki ayı göster",nextJumpText:">>",nextJumpStatus:"",currentText:"bugün",currentStatus:"",todayText:"bugün",todayStatus:"",clearText:"temizle",clearStatus:"geçerli tarihi temizler",closeText:"kapat",closeStatus:"sadece göstergeyi kapat",yearStatus:"baÅŸka yıl",monthStatus:"baÅŸka ay",weekText:"Hf",weekStatus:"Ayın haftaları",dayStatus:"D, M d seçiniz",defaultStatus:"Bir tarih seçiniz",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.tr)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.tt={monthNames:["Гынвар","Февраль","Март","Ðпрель","Май","Июнь","Июль","ÐвгуÑÑ‚","СентÑбрь","ОктÑбрь","ÐоÑбрь","Декабрь"],monthNamesShort:["Гыйн","Фев","Мар","Ðпр","Май","Июн","Июл","Ðвг","Сен","Окт","ÐоÑ","Дек"],dayNames:["Ñкшәмбе","дүшәмбе","Ñишәмбе","чәршәмбе","пәнҗешәмбе","җомга","шимбә"],dayNamesShort:["Ñкш","дүш","Ñиш","чәр","пән","җом","шим"],dayNamesMin:["Як","Дү","Си","Чә","Пә","Җо","Ши"],dateFormat:"dd.mm.yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"Ðлдагы",prevStatus:"Ðлдагы айны күрÑәтү",prevJumpText:"<<",prevJumpStatus:"Ðлдагы елны күрÑәтү",nextText:"КиләÑе",nextStatus:"КиләÑе айны күрÑәтү",nextJumpText:">>",nextJumpStatus:"КиләÑе елны күрÑәтү",currentText:"Хәзер",currentStatus:"Хәзерге айны күрÑәтү",todayText:"Бүген",todayStatus:"Бүгенге айны күрÑәтү",clearText:"ЧиÑтарту",clearStatus:"Барлык көннәрне чиÑтарту",closeText:"Ябарга",closeStatus:"Көн Ñайлауны Ñбарга",yearStatus:"Елны кертегез",monthStatus:"Ðйны кертегез",weekText:"Ðтна",weekStatus:"Елда атна Ñаны",dayStatus:"DD, M d",defaultStatus:"Көнне Ñайлагыз",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.tt)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.uk={monthNames:["Січень","Лютий","Березень","Квітень","Травень","Червень","Липень","Серпень","ВереÑень","Жовтень","ЛиÑтопад","Грудень"],monthNamesShort:["Січ","Лют","Бер","Кві","Тра","Чер","Лип","Сер","Вер","Жов","ЛиÑ","Гру"],dayNames:["неділÑ","понеділок","вівторок","Ñереда","четвер","п'ÑтницÑ","Ñубота"],dayNamesShort:["нед","пнд","вів","Ñрд","чтв","птн","Ñбт"],dayNamesMin:["Ðд","Пн","Ð’Ñ‚","Ср","Чт","Пт","Сб"],dateFormat:"dd/mm/yyyy",firstDay:1,renderer:a.datepick.defaultRenderer,prevText:"<",prevStatus:"",prevJumpText:"<<",prevJumpStatus:"",nextText:">",nextStatus:"",nextJumpText:">>",nextJumpStatus:"",currentText:"Сьогодні",currentStatus:"",todayText:"Сьогодні",todayStatus:"",clearText:"ОчиÑтити",clearStatus:"",closeText:"Закрити",closeStatus:"",yearStatus:"",monthStatus:"",weekText:"Ðе",weekStatus:"",dayStatus:"D, M d",defaultStatus:"",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.uk)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.ur={monthNames:["جنوری","ÙØ±ÙˆØ±ÛŒ","مارچ","اپریل","مئی","جون","جولائی","اگست","ستمبر","اکتوبر","نومبر","دسمبر"],monthNamesShort:["1","2","3","4","5","6","7","8","9","10","11","12"],dayNames:["اتوار","پير","منگل","بدھ","جمعرات","جمعÛ","ÛÙØªÛ"],dayNamesShort:["اتوار","پير","منگل","بدھ","جمعرات","جمعÛ","ÛÙØªÛ"],dayNamesMin:["اتوار","پير","منگل","بدھ","جمعرات","جمعÛ","ÛÙØªÛ"],dateFormat:"dd/mm/yyyy",firstDay:0,renderer:a.datepick.defaultRenderer,prevText:"<گذشتÛ",prevStatus:"ماه گذشتÛ",prevJumpText:"<<",prevJumpStatus:"برس گذشتÛ",nextText:"آئندÛ>",nextStatus:"ماه آئندÛ",nextJumpText:">>",nextJumpStatus:"برس آئندÛ",currentText:"رواں",currentStatus:"ماه رواں",todayText:"آج",todayStatus:"آج",clearText:"حذ٠تاريخ",clearStatus:"کریں حذ٠تاریخ",closeText:"کریں بند",closeStatus:"کیلئے کرنے بند",yearStatus:"برس تبدیلی",monthStatus:"ماه تبدیلی",weekText:"ÛÙØªÛ",weekStatus:"ÛÙØªÛ",dayStatus:"انتخاب D, M d",defaultStatus:"کریں منتخب تاريخ",isRTL:!0},a.datepick.setDefaults(a.datepick.regionalOptions.ur)}(jQuery),function(a){"use strict";a.datepick.regionalOptions.vi={monthNames:["Tháng Má»™t","Tháng Hai","Tháng Ba","Tháng Tư","Tháng Năm","Tháng Sáu","Tháng Bảy","Tháng Tám","Tháng Chín","Tháng Mưá»i","Tháng Mưá»i Má»™t","Tháng Mưá»i Hai"],monthNamesShort:["Tháng 1","Tháng 2","Tháng 3","Tháng 4","Tháng 5","Tháng 6","Tháng 7","Tháng 8","Tháng 9","Tháng 10","Tháng 11","Tháng 12"],dayNames:["Chá»§ Nhật","Thứ Hai","Thứ Ba","Thứ Tư","Thứ Năm","Thứ Sáu","Thứ Bảy"],dayNamesShort:["CN","T2","T3","T4","T5","T6","T7"],dayNamesMin:["CN","T2","T3","T4","T5","T6","T7"],dateFormat:"dd/mm/yyyy",firstDay:0,renderer:a.datepick.defaultRenderer,prevText:"<Trước",prevStatus:"Tháng trước",prevJumpText:"<<",prevJumpStatus:"Năm trước",nextText:"Tiếp>",nextStatus:"Tháng sau",nextJumpText:">>",nextJumpStatus:"Năm sau",currentText:"Hôm nay",currentStatus:"Tháng hiện tại",todayText:"Hôm nay",todayStatus:"Tháng hiện tại",clearText:"Xóa",clearStatus:"Xóa ngày hiện tại",closeText:"Äóng",closeStatus:"Äóng và không lưu lại thay đổi",yearStatus:"Năm khác",monthStatus:"Tháng khác",weekText:"Tu",weekStatus:"Tuần trong năm",dayStatus:"Äang chá»n DD, 'ngày' d M",defaultStatus:"Chá»n ngày",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions.vi)}(jQuery),function(a){"use strict";a.datepick.regionalOptions["zh-CN"]={monthNames:["一月","二月","三月","四月","五月","六月","七月","八月","乿œˆ","åæœˆ","å一月","å二月"],monthNamesShort:["一","二","三","å››","五","å…­","七","å…«","ä¹","å","å一","å二"],dayNames:["星期日","星期一","星期二","星期三","星期四","星期五","星期六"],dayNamesShort:["周日","周一","周二","周三","周四","周五","周六"],dayNamesMin:["æ—¥","一","二","三","å››","五","å…­"],dateFormat:"yyyy-mm-dd",firstDay:1,renderer:a.extend({},a.datepick.defaultRenderer,{month:a.datepick.defaultRenderer.month.replace(/monthHeader/,"monthHeader:MM yyyyå¹´")}),prevText:"<上月",prevStatus:"显示上月",prevJumpText:"<<",prevJumpStatus:"显示上一年",nextText:"下月>",nextStatus:"显示下月",nextJumpText:">>",nextJumpStatus:"显示下一年",currentText:"今天",currentStatus:"显示本月",todayText:"今天",todayStatus:"显示本月",clearText:"清除",clearStatus:"清除已选日期",closeText:"关闭",closeStatus:"䏿”¹å˜å½“å‰é€‰æ‹©",yearStatus:"选择年份",monthStatus:"选择月份",weekText:"周",weekStatus:"年内周次",dayStatus:"选择 m月 dæ—¥, DD",defaultStatus:"请选择日期",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions["zh-CN"])}(jQuery),function(a){"use strict";a.datepick.regionalOptions["zh-HK"]={monthNames:["一月","二月","三月","四月","五月","六月","七月","八月","乿œˆ","åæœˆ","å一月","å二月"],monthNamesShort:["一","二","三","å››","五","å…­","七","å…«","ä¹","å","å一","å二"],dayNames:["星期日","星期一","星期二","星期三","星期四","星期五","星期六"],dayNamesShort:["周日","周一","周二","周三","周四","周五","周六"],dayNamesMin:["æ—¥","一","二","三","å››","五","å…­"],dateFormat:"dd-mm-yyyy",firstDay:0,renderer:a.extend({},a.datepick.defaultRenderer,{month:a.datepick.defaultRenderer.month.replace(/monthHeader/,"monthHeader:yyyyå¹´ MM")}),prevText:"<上月",prevStatus:"顯示上月",prevJumpText:"<<",prevJumpStatus:"顯示上一年",nextText:"下月>",nextStatus:"顯示下月",nextJumpText:">>",nextJumpStatus:"顯示下一年",currentText:"今天",currentStatus:"顯示本月",todayText:"今天",todayStatus:"顯示本月",clearText:"清除",clearStatus:"æ¸…é™¤å·²é¸æ—¥æœŸ",closeText:"關閉",closeStatus:"䏿”¹è®Šç›®å‰çš„鏿“‡",yearStatus:"鏿“‡å¹´ä»½",monthStatus:"鏿“‡æœˆä»½",weekText:"周",weekStatus:"年內周次",dayStatus:"鏿“‡ m月 dæ—¥, DD",defaultStatus:"è«‹é¸æ“‡æ—¥æœŸ",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions["zh-HK"])}(jQuery),function(a){"use strict";a.datepick.regionalOptions["zh-TW"]={monthNames:["一月","二月","三月","四月","五月","六月","七月","八月","乿œˆ","åæœˆ","å一月","å二月"],monthNamesShort:["一","二","三","å››","五","å…­","七","å…«","ä¹","å","å一","å二"],dayNames:["星期日","星期一","星期二","星期三","星期四","星期五","星期六"],dayNamesShort:["周日","周一","周二","周三","周四","周五","周六"],dayNamesMin:["æ—¥","一","二","三","å››","五","å…­"],dateFormat:"yyyy/mm/dd",firstDay:1,renderer:a.extend({},a.datepick.defaultRenderer,{month:a.datepick.defaultRenderer.month.replace(/monthHeader/,"monthHeader:MM yyyyå¹´")}),prevText:"<上月",prevStatus:"顯示上月",prevJumpText:"<<",prevJumpStatus:"顯示上一年",nextText:"下月>",nextStatus:"顯示下月",nextJumpText:">>",nextJumpStatus:"顯示下一年",currentText:"今天",currentStatus:"顯示本月",todayText:"今天",todayStatus:"顯示本月",clearText:"清除",clearStatus:"æ¸…é™¤å·²é¸æ—¥æœŸ",closeText:"關閉",closeStatus:"䏿”¹è®Šç›®å‰çš„鏿“‡",yearStatus:"鏿“‡å¹´ä»½",monthStatus:"鏿“‡æœˆä»½",weekText:"周",weekStatus:"年內周次",dayStatus:"鏿“‡ m月 dæ—¥, DD",defaultStatus:"è«‹é¸æ“‡æ—¥æœŸ",isRTL:!1},a.datepick.setDefaults(a.datepick.regionalOptions["zh-TW"])}(jQuery); +//# sourceMappingURL=jquery.datepick.lang.min.map \ No newline at end of file diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick.lang.min.map b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick.lang.min.map new file mode 100755 index 000000000..46c43ec00 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick.lang.min.map @@ -0,0 +1 @@ +{"version":3,"sources":["jquery.datepick.lang.js"],"names":["$","datepick","regionalOptions","af","monthNames","monthNamesShort","dayNames","dayNamesShort","dayNamesMin","dateFormat","firstDay","renderer","defaultRenderer","prevText","prevStatus","prevJumpText","prevJumpStatus","nextText","nextStatus","nextJumpText","nextJumpStatus","currentText","currentStatus","todayText","todayStatus","clearText","clearStatus","closeText","closeStatus","yearStatus","monthStatus","weekText","weekStatus","dayStatus","defaultStatus","isRTL","setDefaults","jQuery","am","ar","az","bg","bs","ca","cs","da","de","el","eo","es","et","eu","fa","fi","fo","fr","gl","gu","he","hi","hr","hu","hy","id","is","it","ja","extend","month","replace","ka","km","ko","lt","lv","me","mk","ml","ms","mt","nl","no","pl","pt","rm","ro","ru","sk","sl","sq","sr","sv","ta","th","tr","tt","uk","ur","vi"],"mappings":";;CAKA,SAAUA,GACT,YACAA,GAAEC,SAASC,gBAAgBC,IAC1BC,YAAa,WAAW,YAAY,QAAQ,QAAQ,MAAM,QAC1D,QAAQ,WAAW,YAAY,UAAU,WAAW,YACpDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,SAAS,UAAU,UAAU,WAAW,YAAY,SAAS,YACxEC,eAAgB,MAAM,OAAO,OAAO,QAAQ,MAAM,MAAM,OACxDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,SACVC,WAAY,uBACZC,aAAc,eACdC,eAAgB,sBAChBC,SAAU,WACVC,WAAY,yBACZC,aAAc,eACdC,eAAgB,wBAChBC,YAAa,SACbC,cAAe,wBACfC,UAAW,SACXC,YAAa,wBACbC,UAAW,UACXC,YAAa,6BACbC,UAAW,QACXC,YAAa,2BACbC,WAAY,wBACZC,YAAa,yBACbC,SAAU,KACVC,WAAY,oBACZC,UAAW,eACXC,cAAe,gBACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgBC,KAChDkC,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgBoC,IAC1BlC,YAAa,OAAO,QAAQ,MAAM,OAAO,KAAK,KAC9C,MAAM,OAAO,SAAS,QAAQ,QAAQ,SACtCC,iBAAkB,MAAM,MAAM,MAAM,MAAM,KAAK,KAC/C,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,OAAO,OAAO,QAAQ,QAAQ,QAAQ,QAAQ,SACzDC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,MACVC,WAAY,eACZC,aAAc,eACdC,eAAgB,gBAChBC,SAAU,MACVC,WAAY,cACZC,aAAc,eACdC,eAAgB,eAChBC,YAAa,MACbC,cAAe,eACfC,UAAW,KACXC,YAAa,cACbC,UAAW,MACXC,YAAa,iBACbC,UAAW,KACXC,YAAa,gBACbC,WAAY,WACZC,YAAa,UACbC,SAAU,KACVC,WAAY,aACZC,UAAW,oBACXC,cAAe,SACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgBoC,KAChDD,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgB,UAC1BE,YAAa,QAAQ,QAAQ,OAAO,QAAQ,MAAM,OAClD,SAAS,MAAM,SAAS,SAAS,SAAS,UAC1CC,iBAAkB,IAAI,IAAI,IAAI,IAAI,IAAI,IACtC,IAAI,IAAI,IAAI,KAAK,KAAK,MACtBC,UAAW,QAAQ,UAAU,WAAW,WAAW,SAAS,SAAS,SACrEC,eAAgB,QAAQ,UAAU,WAAW,WAAW,SAAS,SAAS,SAC1EC,aAAc,QAAQ,UAAU,WAAW,WAAW,SAAS,SAAS,SACxEC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,eACVC,WAAY,mBACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,eACVC,WAAY,mBACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,QACbC,cAAe,mBACfC,UAAW,QACXC,YAAa,mBACbC,UAAW,MACXC,YAAa,sBACbC,UAAW,QACXC,YAAa,iBACbC,WAAY,eACZC,YAAa,cACbC,SAAU,QACVC,WAAY,cACZC,UAAW,cACXC,cAAe,WACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB,WAChDmC,QAMH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgB,UAC1BE,YAAa,QAAQ,SAAS,OAAO,QAAQ,OAAO,QACpD,QAAQ,QAAQ,SAAS,SAAS,SAAS,UAC3CC,iBAAkB,IAAI,IAAI,IAAI,IAAI,IAAI,IACtC,IAAI,IAAI,IAAI,KAAK,KAAK,MACtBC,UAAY,QAAQ,UAAU,WAAW,WAAW,SAAS,SAAS,SACtEC,eAAgB,MAAM,QAAQ,SAAS,SAAS,OAAO,OAAO,OAC9DC,aAAc,MAAM,QAAQ,SAAS,SAAS,OAAO,OAAO,OAC5DC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,eACVC,WAAY,mBACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,eACVC,WAAY,mBACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,QACbC,cAAe,mBACfC,UAAW,QACXC,YAAa,mBACbC,UAAW,MACXC,YAAa,sBACbC,UAAW,QACXC,YAAa,iBACbC,WAAY,eACZC,YAAa,cACbC,SAAU,QACVC,WAAY,cACZC,UAAW,cACXC,cAAe,WACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB,WAChDmC,QAOH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgBqC,IAC1BnC,YAAa,eAAe,OAAO,OAAO,QAAQ,OAAO,SACzD,OAAO,KAAK,QAAQ,cAAc,eAAe,eACjDC,iBAAkB,IAAI,IAAI,IAAI,IAAI,IAAI,IACtC,IAAI,IAAI,IAAI,KAAK,KAAK,MACtBC,UAAW,QAAQ,UAAU,WAAW,WAAW,SAAS,SAAS,SACrEC,eAAgB,QAAQ,UAAU,WAAW,WAAW,SAAS,SAAS,SAC1EC,aAAc,QAAQ,UAAU,WAAW,WAAW,SAAS,SAAS,SACxEC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,eACVC,WAAY,mBACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,eACVC,WAAY,mBACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,QACbC,cAAe,mBACfC,UAAW,QACXC,YAAa,mBACbC,UAAW,MACXC,YAAa,sBACbC,UAAW,QACXC,YAAa,iBACbC,WAAY,eACZC,YAAa,cACbC,SAAU,QACVC,WAAY,cACZC,UAAW,cACXC,cAAe,WACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgBqC,KAChDF,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgBsC,IAC1BpC,YAAa,SAAS,SAAS,OAAO,QAAQ,MAAM,OACpD,OAAO,SAAS,WAAW,UAAU,SAAS,UAC9CC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,OAChD,OAAO,MAAM,MAAM,MAAM,MAAM,OAC/BC,UAAW,QAAQ,eAAe,kBAAkB,WAAW,cAAc,OAAO,SACpFC,eAAgB,IAAI,KAAK,KAAK,IAAI,KAAK,IAAI,KAC3CC,aAAc,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,KACtCC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,aACVC,WAAY,aACZC,aAAc,eACdC,eAAgB,aAChBC,SAAU,cACVC,WAAY,aACZC,aAAc,eACdC,eAAgB,aAChBC,YAAa,QACbC,cAAe,YACfC,UAAW,QACXC,YAAa,YACbC,UAAW,UACXC,YAAa,aACbC,UAAW,QACXC,YAAa,gBACbC,WAAY,WACZC,YAAa,WACbC,SAAU,KACVC,WAAY,WACZC,UAAW,eACXC,cAAe,kBACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgBsC,KAChDH,QAIH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgBuC,IAC1BrC,YAAa,SAAS,WAAW,OAAO,QAAQ,MAAM,MACtD,MAAM,SAAS,YAAY,WAAW,UAAU,YAChDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,SAAS,aAAa,UAAU,QAAQ,YAAY,QAAQ,UACvEC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,cACVC,WAAY,yBACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,eACVC,WAAY,yBACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,OACbC,cAAe,GACfC,UAAW,OACXC,YAAa,GACbC,UAAW,UACXC,YAAa,0BACbC,UAAW,UACXC,YAAa,sBACbC,WAAY,sBACZC,YAAa,oBACbC,SAAU,KACVC,WAAY,oBACZC,UAAW,gBACXC,cAAe,cACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgBuC,KAChDJ,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgBwC,IAC1BtC,YAAa,SAAS,UAAU,OAAO,QAAQ,MAAM,OACrD,OAAO,SAAS,YAAY,UAAU,WAAW,YACjDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,UAAU,aAAa,SAAS,UAAU,WAAW,QAAQ,UACxEC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,SACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,SACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,QACbC,cAAe,GACfC,UAAW,QACXC,YAAa,GACbC,UAAW,IACXC,YAAa,GACbC,UAAW,UACXC,YAAa,GACbC,WAAY,GACZC,YAAa,GACbC,SAAU,KACVC,WAAY,GACZC,UAAW,UACXC,cAAe,GACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgBwC,KAChDL,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgByC,IAC1BvC,YAAa,QAAQ,SAAS,cAAc,QAAQ,OAAO,OAC3D,SAAS,QAAQ,WAAW,UAAU,WAAW,YACjDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,WAAW,UAAU,UAAU,WAAW,SAAS,YAAY,YAC1EC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,YACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,YACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,OACbC,cAAe,GACfC,UAAW,OACXC,YAAa,GACbC,UAAW,UACXC,YAAa,GACbC,UAAW,SACXC,YAAa,GACbC,WAAY,GACZC,YAAa,GACbC,SAAU,KACVC,WAAY,GACZC,UAAW,SACXC,cAAe,GACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgByC,KAChDN,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgB0C,IAC1BxC,YAAa,QAAQ,OAAO,SAAS,QAAQ,SAAS,SACtD,WAAW,QAAQ,OAAO,QAAQ,WAAW,YAC7CC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,SAAS,UAAU,QAAQ,SAAS,UAAU,QAAQ,UACjEC,eAAgB,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC9CC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,cACVC,WAAY,2BACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,gBACVC,WAAY,wBACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,OACbC,cAAe,2BACfC,UAAW,OACXC,YAAa,2BACbC,UAAW,UACXC,YAAa,sBACbC,UAAW,SACXC,YAAa,4BACbC,WAAY,qBACZC,YAAa,uBACbC,SAAU,MACVC,WAAY,eACZC,UAAW,kBACXC,cAAe,gBACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB0C,KAChDP,QAKH,SAAUrC,GACT,YACGA,GAAEC,SAASC,gBAAgB2C,IACvBzC,YAAa,SAAS,UAAU,QAAQ,QAAQ,MAAM,OACtD,OAAO,SAAS,YAAY,UAAU,WAAW,YACjDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OACpCC,UAAW,SAAS,SAAS,UAAU,SAAS,UAAU,SAAS,UACnEC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MACtCC,WAAY,aAClBC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACfC,SAAU,gBAChBC,WAAY,oBACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,cACVC,WAAY,kBACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,OACbC,cAAe,mBACfC,UAAW,OACXC,YAAa,mBACbC,UAAW,UACXC,YAAa,4BACbC,UAAW,MACXC,YAAa,qBACbC,WAAY,kBACZC,YAAa,qBACbC,SAAU,MACVC,WAAY,YACZC,UAAW,cACXC,cAAe,eACfC,OAAO,GAELnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB2C,KACnDR,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgB,UAC1BE,YAAa,SAAS,UAAU,OAAO,QAAQ,MAAM,OACrD,OAAO,SAAS,YAAY,UAAU,WAAW,YACjDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,UAAU,SAAS,WAAW,WAAW,aAAa,UAAU,WAC3EC,eAAgB,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC9CC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,eACVC,WAAY,uBACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,iBACVC,WAAY,wBACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,QACbC,cAAe,GACfC,UAAW,QACXC,YAAa,GACbC,UAAW,UACXC,YAAa,0BACbC,UAAW,aACXC,YAAa,6BACbC,WAAY,wBACZC,YAAa,yBACbC,SAAU,KACVC,WAAY,mBACZC,UAAW,eACXC,cAAe,kBACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB,WAChDmC,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgB4C,IAC1B1C,YAAa,SAAS,UAAU,OAAO,QAAQ,MAAM,OACrD,OAAO,SAAS,YAAY,UAAU,WAAW,YACjDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,UAAU,SAAS,WAAW,WAAW,aAAa,UAAU,WAC3EC,eAAgB,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC9CC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,eACVC,WAAY,uBACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,YACVC,WAAY,wBACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,QACbC,cAAe,GACfC,UAAW,QACXC,YAAa,GACbC,UAAW,UACXC,YAAa,0BACbC,UAAW,YACXC,YAAa,4BACbC,WAAY,wBACZC,YAAa,yBACbC,SAAU,KACVC,WAAY,mBACZC,UAAW,eACXC,cAAe,kBACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB4C,KAChDT,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgB6C,IAC1B3C,YAAa,aAAa,cAAc,UAAU,WAAW,QAAQ,UACrE,UAAU,YAAY,cAAc,YAAY,YAAY,cAC5DC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,OAChD,OAAO,MAAM,MAAM,MAAM,MAAM,OAC/BC,UAAW,UAAU,UAAU,QAAQ,UAAU,SAAS,YAAY,WACtEC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,eACVC,WAAY,+BACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,WACVC,WAAY,2BACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,eACbC,cAAe,4BACfC,UAAW,eACXC,YAAa,4BACbC,UAAW,UACXC,YAAa,sCACbC,UAAW,WACXC,YAAa,wBACbC,WAAY,yBACZC,YAAa,wBACbC,SAAU,MACVC,WAAY,GACZC,UAAW,kBACXC,cAAe,0BACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB6C,KAChDV,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgB,UAC1BE,YAAa,UAAU,WAAW,QAAQ,QAAQ,MAAM,OACxD,OAAO,SAAS,YAAY,UAAU,WAAW,YACjDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,SAAS,SAAS,UAAU,YAAY,WAAW,SAAS,YACvEC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,OACVC,WAAY,0BACZC,aAAc,eACdC,eAAgB,yBAChBC,SAAU,OACVC,WAAY,sBACZC,aAAc,eACdC,eAAgB,qBAChBC,YAAa,UACbC,cAAe,yBACfC,UAAW,QACXC,YAAa,qBACbC,UAAW,QACXC,YAAa,yBACbC,UAAW,OACXC,YAAa,uBACbC,WAAY,wBACZC,YAAa,yBACbC,SAAU,KACVC,WAAY,mBACZC,UAAW,iBACXC,cAAe,gBACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB,WAChDmC,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgB,UAC1BE,YAAa,UAAU,WAAW,QAAQ,QAAQ,MAAM,OACxD,OAAO,SAAS,YAAY,UAAU,WAAW,YACjDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,SAAS,SAAS,UAAU,YAAY,WAAW,SAAS,YACvEC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,OACVC,WAAY,0BACZC,aAAc,eACdC,eAAgB,yBAChBC,SAAU,OACVC,WAAY,sBACZC,aAAc,eACdC,eAAgB,qBAChBC,YAAa,UACbC,cAAe,yBACfC,UAAW,QACXC,YAAa,qBACbC,UAAW,QACXC,YAAa,yBACbC,UAAW,OACXC,YAAa,uBACbC,WAAY,wBACZC,YAAa,yBACbC,SAAU,KACVC,WAAY,mBACZC,UAAW,iBACXC,cAAe,gBACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB,WAChDmC,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgB,UAC1BE,YAAa,UAAU,WAAW,QAAQ,QAAQ,MAAM,OACxD,OAAO,SAAS,YAAY,UAAU,WAAW,YACjDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,SAAS,SAAS,UAAU,YAAY,WAAW,SAAS,YACvEC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,OACVC,WAAY,0BACZC,aAAc,eACdC,eAAgB,yBAChBC,SAAU,OACVC,WAAY,sBACZC,aAAc,eACdC,eAAgB,qBAChBC,YAAa,UACbC,cAAe,yBACfC,UAAW,QACXC,YAAa,qBACbC,UAAW,QACXC,YAAa,yBACbC,UAAW,OACXC,YAAa,uBACbC,WAAY,wBACZC,YAAa,yBACbC,SAAU,KACVC,WAAY,mBACZC,UAAW,iBACXC,cAAe,gBACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB,WAChDmC,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgB8C,IAC1B5C,YAAa,UAAU,WAAW,QAAQ,SAAS,OAAO,QAC1D,QAAQ,UAAU,YAAY,UAAU,WAAW,YACnDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,UAAU,QAAQ,QAAQ,WAAW,QAAQ,WAAW,UACnEC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,WACVC,WAAY,0BACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,WACVC,WAAY,yBACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,OACbC,cAAe,wBACfC,UAAW,OACXC,YAAa,wBACbC,UAAW,SACXC,YAAa,GACbC,UAAW,QACXC,YAAa,mBACbC,WAAY,mBACZC,YAAa,qBACbC,SAAU,KACVC,WAAY,GACZC,UAAW,kBACXC,cAAe,kBACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB8C,KAChDX,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgB,UAC1BE,YAAa,QAAQ,UAAU,QAAQ,QAAQ,OAAO,QACtD,QAAQ,SAAS,aAAa,UAAU,YAAY,aACpDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,UAAU,QAAQ,SAAS,YAAY,SAAS,UAAU,UACrEC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,YACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,YACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,MACbC,cAAe,GACfC,UAAW,MACXC,YAAa,GACbC,UAAW,UACXC,YAAa,GACbC,UAAW,SACXC,YAAa,GACbC,WAAY,GACZC,YAAa,GACbC,SAAU,KACVC,WAAY,GACZC,UAAW,SACXC,cAAe,GACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB,WAChDmC,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgB,UAC1BE,YAAa,QAAQ,UAAU,QAAQ,QAAQ,OAAO,QACtD,QAAQ,SAAS,aAAa,UAAU,YAAY,aACpDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,UAAU,QAAQ,SAAS,YAAY,SAAS,UAAU,UACrEC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,YACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,YACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,MACbC,cAAe,GACfC,UAAW,MACXC,YAAa,GACbC,UAAW,UACXC,YAAa,GACbC,UAAW,SACXC,YAAa,GACbC,WAAY,GACZC,YAAa,GACbC,SAAU,KACVC,WAAY,GACZC,UAAW,gBACXC,cAAe,GACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB,WAChDmC,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgB+C,IAC1B7C,YAAa,QAAQ,UAAU,QAAQ,QAAQ,OAAO,QACtD,QAAQ,SAAS,aAAa,UAAU,YAAY,aACpDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,UAAU,QAAQ,SAAS,YAAY,SAAS,UAAU,UACrEC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,YACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,YACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,MACbC,cAAe,GACfC,UAAW,MACXC,YAAa,GACbC,UAAW,UACXC,YAAa,GACbC,UAAW,SACXC,YAAa,GACbC,WAAY,GACZC,YAAa,GACbC,SAAU,KACVC,WAAY,GACZC,UAAW,SACXC,cAAe,GACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB+C,KAChDZ,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgBgD,IAC1B9C,YAAa,UAAU,WAAW,QAAQ,SAAS,MAAM,QACzD,QAAQ,SAAS,YAAY,WAAW,WAAW,aACnDC,iBAAkB,OAAO,QAAQ,QAAQ,MAAM,MAAM,QACrD,QAAQ,MAAM,OAAO,MAAM,MAAM,QACjCC,UAAW,WAAW,YAAY,YAAY,YAAY,YAAY,QAAQ,WAC9EC,eAAgB,QAAQ,SAAS,SAAS,SAAS,SAAS,QAAQ,QACpEC,aAAc,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,KACtCC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,SACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,UACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,OACbC,cAAe,GACfC,UAAW,OACXC,YAAa,GACbC,UAAW,GACXC,YAAa,GACbC,UAAW,QACXC,YAAa,GACbC,WAAY,GACZC,YAAa,GACbC,SAAU,KACVC,WAAY,GACZC,UAAW,GACXC,cAAe,GACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgBgD,KAChDb,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgBiD,IAC1B/C,YAAa,YAAY,UAAU,UAAU,UAAU,UAAU,SACjE,UAAU,UAAU,SAAS,QAAQ,SAAS,WAC9CC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,UAAU,aAAa,YAAY,aAAa,WAAW,WAAW,aACjFC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,YACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,YACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,OACbC,cAAe,GACfC,UAAW,OACXC,YAAa,GACbC,UAAW,IACXC,YAAa,GACbC,UAAW,QACXC,YAAa,GACbC,WAAY,GACZC,YAAa,GACbC,SAAU,KACVC,WAAY,GACZC,UAAW,UACXC,cAAe,GACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgBiD,KAChDd,QAKH,SAAUrC,GACT,YAEAA,GAAEC,SAASC,gBAAgBkD,IAC1BhD,YAAa,UAAU,WAAW,QAAQ,MAAM,QAAQ,SACxD,MAAM,OAAO,MAAM,KAAK,OAAO,SAC/BC,iBAAkB,IAAI,IAAI,IAAI,IAAI,IAAI,IACtC,IAAI,IAAI,IAAI,KAAK,KAAK,MACtBC,UAAW,SAAS,SAAS,UAAU,WAAW,UAAU,OAAO,QACnEC,eAAgB,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,KACxCC,aAAc,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,KACtCC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,aACVC,WAAY,gBACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,aACVC,WAAY,gBACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,QACbC,cAAe,iBACfC,UAAW,QACXC,YAAa,iBACbC,UAAW,YACXC,YAAa,sBACbC,UAAW,OACXC,YAAa,0BACbC,WAAY,mBACZC,YAAa,mBACbC,SAAU,KACVC,WAAY,YACZC,UAAW,gBACXC,cAAe,eACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgBkD,KAChDf,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgBmD,IAC1BjD,YAAa,WAAW,WAAW,YAAY,WAAW,WAAW,eACrE,gBAAgB,SAAS,UAAU,UAAU,YAAY,YACzDC,iBAAkB,QAAQ,QAAQ,SAAS,QAAQ,QAAQ,YAC3D,aAAa,MAAM,OAAO,OAAO,SAAS,SAC1CE,eAAgB,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC9CD,UAAW,YAAY,YAAY,UAAU,cAAc,UAAU,YAAY,YACjFE,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,mBACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,kBACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,wBACbC,cAAe,GACfC,UAAW,wBACXC,YAAa,GACbC,UAAW,gBACXC,YAAa,GACbC,UAAW,QACXC,YAAa,GACbC,WAAY,GACZC,YAAa,GACbC,SAAU,KACVC,WAAY,GACZC,UAAW,SACXC,cAAe,GACfC,OAAO,GAELnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgBmD,KACnDhB,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgBoD,IAC1BlD,YAAa,SAAS,UAAU,OAAO,QAAQ,MAAM,OACrD,OAAO,SAAS,YAAY,UAAU,WAAW,YACjDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,aAAa,YAAY,WAAW,YAAY,WAAW,eAAe,cACrFC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,cACVC,WAAY,oBACZC,aAAc,eACdC,eAAgB,iBAChBC,SAAU,cACVC,WAAY,oBACZC,aAAc,eACdC,eAAgB,iBAChBC,YAAa,QACbC,cAAe,yBACfC,UAAW,QACXC,YAAa,yBACbC,UAAW,SACXC,YAAa,0BACbC,UAAW,OACXC,YAAa,uBACbC,WAAY,aACZC,YAAa,gBACbC,SAAU,KACVC,WAAY,iBACZC,UAAW,oBACXC,cAAe,eACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgBoD,KAChDjB,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgB,UAC1BE,YAAa,UAAU,UAAU,OAAO,QAAQ,MAAM,OACtD,UAAU,OAAO,YAAY,UAAU,WAAW,YAClDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,WAAW,QAAQ,QAAQ,WAAW,QAAQ,WAAW,UACpEC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,aACVC,WAAY,yBACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,aACVC,WAAY,uBACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,UACbC,cAAe,uBACfC,UAAW,cACXC,YAAa,mBACbC,UAAW,UACXC,YAAa,+BACbC,UAAW,SACXC,YAAa,uBACbC,WAAY,uBACZC,YAAa,qBACbC,SAAU,KACVC,WAAY,GACZC,UAAW,uBACXC,cAAe,kBACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB,WAChDmC,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgBqD,IAC1BnD,YAAa,UAAU,UAAU,OAAO,QAAQ,MAAM,OACtD,UAAU,OAAO,YAAY,UAAU,WAAW,YAClDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,WAAW,QAAQ,QAAQ,WAAW,QAAQ,WAAW,UACpEC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,aACVC,WAAY,yBACZC,aAAc,eACdC,eAAgB,yBAChBC,SAAU,aACVC,WAAY,uBACZC,aAAc,eACdC,eAAgB,uBAChBC,YAAa,UACbC,cAAe,uBACfC,UAAW,cACXC,YAAa,mBACbC,UAAW,UACXC,YAAa,+BACbC,UAAW,SACXC,YAAa,uBACbC,WAAY,uBACZC,YAAa,qBACbC,SAAU,KACVC,WAAY,qBACZC,UAAW,uBACXC,cAAe,kBACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgBqD,KAChDlB,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgBsD,IAC1BpD,YAAa,UAAU,WAAW,QAAQ,QAAQ,OAAO,OACzD,QAAQ,SAAS,WAAW,UAAU,WAAW,YACjDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM;AAC9BC,UAAW,UAAU,OAAO,SAAS,WAAW,QAAQ,SAAS,UACjEC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,YACVC,WAAY,sBACZC,aAAc,eACdC,eAAgB,sBAChBC,SAAU,YACVC,WAAY,sBACZC,aAAc,eACdC,eAAgB,sBAChBC,YAAa,OACbC,cAAe,oBACfC,UAAW,OACXC,YAAa,oBACbC,UAAW,SACXC,YAAa,qBACbC,UAAW,SACXC,YAAa,oBACbC,WAAY,mBACZC,YAAa,mBACbC,SAAU,KACVC,WAAY,gBACZC,UAAW,SACXC,cAAe,kBACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgBsD,KAChDnB,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgBuD,IAC1BrD,YAAa,YAAY,YAAY,QAAQ,SAAS,KAAK,MAC3D,QAAQ,QAAQ,YAAY,UAAU,UAAU,YAChDC,iBAAkB,SAAS,SAAS,QAAQ,SAAS,KAAK,MAC1D,QAAQ,QAAQ,QAAQ,QAAQ,MAAM,QACtCC,UAAW,SAAS,SAAS,UAAU,SAAS,UAAU,WAAW,UACrEC,eAAgB,MAAM,MAAM,OAAO,MAAM,OAAO,QAAQ,OACxDC,aAAc,IAAI,KAAK,KAAK,KAAK,KAAK,KAAK,KAC3CC,WAAY,YACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,aACVC,WAAY,oBACZC,aAAc,eACdC,eAAgB,OAChBC,SAAU,YACVC,WAAY,mBACZC,aAAc,eACdC,eAAgB,MAChBC,YAAa,MACbC,cAAe,kBACfC,UAAW,MACXC,YAAa,YACbC,UAAW,QACXC,YAAa,6BACbC,UAAW,UACXC,YAAa,+BACbC,WAAY,kBACZC,YAAa,mBACbC,SAAU,YACVC,WAAY,YACZC,UAAW,iCACXC,cAAe,iBACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgBuD,KAChDpB,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgBwD,IAC1BtD,YAAa,QAAQ,SAAS,MAAM,QAAQ,MAAM,OAClD,OAAO,SAAS,SAAS,UAAU,SAAS,SAC5CC,iBAAkB,IAAI,IAAI,IAAI,IAAI,IAAI,IACtC,IAAI,IAAI,IAAI,KAAK,KAAK,MACtBC,UAAW,QAAQ,MAAM,QAAQ,QAAQ,QAAQ,OAAO,OACxDC,eAAgB,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,OACpDC,aAAc,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,OAClDC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,cACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,YACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,OACbC,cAAe,GACfC,UAAW,OACXC,YAAa,GACbC,UAAW,MACXC,YAAa,GACbC,UAAW,OACXC,YAAa,GACbC,WAAY,GACZC,YAAa,GACbC,SAAU,KACVC,WAAY,GACZC,UAAW,UACXC,cAAe,GACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgBwD,KAChDrB,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgB,UAC1BE,YAAa,QAAQ,SAAS,QAAQ,SAAS,KAAK,MACpD,QAAQ,QAAQ,UAAU,UAAU,SAAS,WAC7CC,iBAAkB,KAAK,KAAK,QAAQ,QAAQ,KAAK,MACjD,QAAQ,KAAK,MAAM,QAAQ,KAAK,OAChCC,UAAW,SAAS,SAAS,UAAU,SAAS,UAAU,WAAW,UACrEC,eAAgB,MAAM,MAAM,OAAO,MAAM,OAAO,QAAQ,OACxDC,aAAc,IAAI,KAAK,KAAK,KAAK,KAAK,KAAK,KAC3CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,QACVC,WAAY,oBACZC,aAAc,eACdC,eAAgB,mBAChBC,SAAU,OACVC,WAAY,mBACZC,aAAc,eACdC,eAAgB,kBAChBC,YAAa,UACbC,cAAe,sBACfC,UAAW,KACXC,YAAa,oBACbC,UAAW,MACXC,YAAa,uBACbC,UAAW,SACXC,YAAa,oBACbC,WAAY,0BACZC,YAAa,2BACbC,SAAU,KACVC,WAAY,iBACZC,UAAW,eACXC,cAAe,sBACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB,WAChDmC,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgByD,IAC1BvD,YAAa,QAAQ,QAAQ,QAAQ,SAAS,KAAK,MACnD,QAAQ,QAAQ,SAAS,UAAU,QAAQ,UAC3CC,iBAAkB,KAAK,OAAO,QAAQ,SAAS,KAAK,MACpD,QAAQ,QAAQ,SAAS,UAAU,QAAQ,UAC3CC,UAAW,SAAS,SAAS,UAAU,SAAS,cAAc,WAAW,UACzEC,eAAgB,MAAM,MAAM,OAAO,MAAM,UAAU,QAAQ,OAC3DC,aAAc,MAAM,MAAM,OAAO,MAAM,UAAU,QAAQ,OACzDC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,QACVC,WAAY,cACZC,aAAc,eACdC,eAAgB,aAChBC,SAAU,OACVC,WAAY,aACZC,aAAc,eACdC,eAAgB,WAChBC,YAAa,UACbC,cAAe,WACfC,UAAW,KACXC,YAAa,aACbC,UAAW,OACXC,YAAa,sBACbC,UAAW,SACXC,YAAa,oBACbC,WAAY,qBACZC,YAAa,4BACbC,SAAU,KACVC,WAAY,mBACZC,UAAW,cACXC,cAAe,sBACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgByD,KAChDtB,QAIH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgB0D,IAC1BxD,YAAa,WAAW,UAAU,SAAS,UAAU,UAAU,SAC/D,SAAS,UAAU,QAAQ,WAAW,UAAU,YAChDC,iBAAkB,MAAM,OAAO,MAAM,MAAM,MAAM,MACjD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,WAAW,cAAc,SAAS,UAAU,WAAW,QAAQ,UAC1EC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,cACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,SACVC,WAAY,2BACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,SACVC,WAAY,2BACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,QACbC,cAAe,iBACfC,UAAW,QACXC,YAAa,iBACbC,UAAW,UACXC,YAAa,yBACbC,UAAW,UACXC,YAAa,mBACbC,WAAY,iBACZC,YAAa,kBACbC,SAAU,MACVC,WAAY,SACZC,UAAW,iBACXC,cAAe,gBACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB0D,KAChDvB,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgB2D,IAC1BzD,YAAa,SAAS,UAAU,UAAU,UAAU,QAAQ,SAC5D,SAAS,YAAY,aAAa,UAAU,WAAW,YACvDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,OAAO,MAAM,MAAM,OAC/BC,UAAW,WAAW,QAAQ,OAAO,SAAS,YAAY,SAAS,WACnEC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,IAAI,IAAI,IAAI,MAAM,KAAK,IAAI,OACzCC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,sBACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,qBACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,KACbC,cAAe,GACfC,UAAW,KACXC,YAAa,GACbC,UAAW,SACXC,YAAa,GACbC,UAAW,UACXC,YAAa,GACbC,WAAY,GACZC,YAAa,GACbC,SAAU,KACVC,WAAY,GACZC,UAAW,SACXC,cAAe,GACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB2D,KAChDxB,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgB4D,IAC1B1D,YAAa,UAAU,UAAU,OAAO,QAAQ,QAAQ,SACxD,SAAS,UAAU,YAAY,YAAY,WAAW,aACtDC,iBAAkB,QAAQ,OAAO,OAAO,MAAM,QAAQ,SACtD,OAAO,MAAM,MAAM,MAAM,MAAM,OAC/BC,UAAW,SAAS,YAAY,YAAY,aAAa,YAAY,SAAS,SAC9EC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,OAAO,OACrDC,aAAc,MAAM,MAAM,MAAM,MAAM,MAAM,OAAO,OACnDC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,aACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,aACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,QACbC,cAAe,GACfC,UAAW,QACXC,YAAa,GACbC,UAAW,SACXC,YAAa,GACbC,UAAW,QACXC,YAAa,GACbC,WAAY,GACZC,YAAa,GACbC,SAAU,MACVC,WAAY,GACZC,UAAW,SACXC,cAAe,GACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB4D,KAChDzB,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgB6D,IAC1B3D,YAAa,UAAU,WAAW,QAAQ,QAAQ,MAAM,OACxD,OAAO,UAAU,YAAY,UAAU,WAAW,YAClDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,OAAO,MAAM,MAAM,MAAM,OAC/BC,UAAW,SAAS,QAAQ,SAAS,OAAO,QAAQ,QAAQ,SAC5DC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,eACVC,WAAY,6BACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,aACVC,WAAY,6BACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,WACbC,cAAe,2BACfC,UAAW,WACXC,YAAa,2BACbC,UAAW,YACXC,YAAa,kCACbC,UAAW,QACXC,YAAa,uBACbC,WAAY,+BACZC,YAAa,+BACbC,SAAU,KACVC,WAAY,qBACZC,UAAW,oBACXC,cAAe,gBACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB6D,KAChD1B,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgB8D,IAC1B5D,YAAa,gBAAgB,iBAAiB,OAAO,eAAe,YAAY,qBAChF,qBAAqB,sBAAsB,YAAY,iBAAiB,kBAAkB,YAC1FC,iBAAkB,MAAM,MAAM,MAAM,MAAM,aAAa,aACvD,aAAa,oBAAoB,MAAM,MAAM,aAAa,OAC1DC,UAAW,aAAa,mBAAmB,wBAAwB,mBAAmB,cAAc,kBAAkB,eACtHC,eAAgB,MAAM,aAAa,YAAY,UAAU,MAAM,WAAW,OAC1EC,aAAc,KAAK,YAAY,WAAW,KAAK,KAAK,UAAU,MAC9DC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,eACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,qBACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,eACbC,cAAe,GACfC,UAAW,eACXC,YAAa,GACbC,UAAW,UACXC,YAAa,GACbC,UAAW,OACXC,YAAa,GACbC,WAAY,GACZC,YAAa,GACbC,SAAU,OACVC,WAAY,GACZC,UAAW,SACXC,cAAe,GACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB8D,KAChD3B,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgB+D,IAC1B7D,YAAa,UAAU,WAAW,QAAQ,SAAS,SAAS,SAC5D,SAAS,SAAS,YAAY,UAAU,WAAW,YACnDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,WAAW,SAAS,UAAU,YAAY,UAAU,UAAU,UACzEC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,aACVC,WAAY,kBACZC,aAAc,eACdC,eAAgB,2BAChBC,SAAU,aACVC,WAAY,kBACZC,aAAc,eACdC,eAAgB,2BAChBC,YAAa,OACbC,cAAe,gBACfC,UAAW,OACXC,YAAa,gBACbC,UAAW,SACXC,YAAa,UACbC,UAAW,SACXC,YAAa,4BACbC,WAAY,0BACZC,YAAa,0BACbC,SAAU,KACVC,WAAY,sBACZC,UAAW,qBACXC,cAAe,qBACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB+D,KAChD5B,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgBgE,IAC1B9D,YAAa,KAAK,KAAK,KAAK,KAAK,KAAK,KACtC,KAAK,KAAK,KAAK,MAAM,MAAM,OAC3BC,iBAAkB,KAAK,KAAK,KAAK,KAAK,KAAK,KAC3C,KAAK,KAAK,KAAK,MAAM,MAAM,OAC3BC,UAAW,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OAC/CC,eAAgB,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,KACxCC,aAAc,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,KACtCC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEmE,UAAWnE,EAAEC,SAASW,iBACjCwD,MAAOpE,EAAEC,SAASW,gBAAgBwD,MAAMC,QAAQ,cAAe,0BAEhExD,SAAU,UACVC,WAAY,WACZC,aAAc,eACdC,eAAgB,WAChBC,SAAU,UACVC,WAAY,WACZC,aAAc,eACdC,eAAgB,WAChBC,YAAa,KACbC,cAAe,WACfC,UAAW,KACXC,YAAa,WACbC,UAAW,MACXC,YAAa,YACbC,UAAW,MACXC,YAAa,YACbC,WAAY,cACZC,YAAa,cACbC,SAAU,IACVC,WAAY,gBACZC,UAAW,SACXC,cAAe,WACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgBgE,KAChD7B,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgBoE,IAC1BlE,YAAa,UAAU,YAAY,QAAQ,SAAS,QAAQ,SAC5D,SAAS,UAAU,aAAa,YAAY,WAAW,aACvDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,QAAQ,MAClD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,QAAQ,WAAW,YAAY,YAAY,YAAY,YAAY,UAC9EC,eAAgB,KAAK,MAAM,MAAM,MAAM,MAAM,MAAM,OACnDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,QACVC,WAAY,WACZC,aAAc,eACdC,eAAgB,YAChBC,SAAU,OACVC,WAAY,cACZC,aAAc,eACdC,eAAgB,eAChBC,YAAa,YACbC,cAAe,gBACfC,UAAW,OACXC,YAAa,gBACbC,UAAW,cACXC,YAAa,0BACbC,UAAW,OACXC,YAAa,qBACbC,WAAY,YACZC,YAAa,WACbC,SAAU,KACVC,WAAY,aACZC,UAAW,kBACXC,cAAe,iBACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgBoE,KAChDjC,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgBqE,IAC1BnE,YAAa,UAAU,YAAY,UAAU,UAAU,UAAU,YACjE,YAAY,UAAU,WAAW,UAAU,cAAc,WACzDC,iBAAkB,KAAK,KAAK,OAAO,KAAK,KAAK,OAC7C,OAAO,KAAK,QAAQ,OAAO,SAAS,QACpCC,UAAW,eAAe,YAAY,cAAc,WAAW,oBAAoB,aAAa,aAChGC,eAAgB,KAAK,OAAO,OAAO,MAAM,OAAO,KAAK,QACrDC,aAAc,KAAK,IAAI,IAAI,KAAK,MAAM,KAAK,KAC3CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,WACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,SACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,WACbC,cAAe,GACfC,UAAW,WACXC,YAAa,GACbC,UAAW,IACXC,YAAa,GACbC,UAAW,WACXC,YAAa,GACbC,WAAY,GACZC,YAAa,GACbC,SAAU,KACVC,WAAY,GACZC,UAAW,UACXC,cAAe,GACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgBqE,KAChDlC,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgBsE,IAC1BpE,YAAa,KAAK,KAAK,KAAK,KAAK,KAAK,KACtC,KAAK,KAAK,KAAK,MAAM,MAAM,OAC3BC,iBAAkB,KAAK,KAAK,KAAK,KAAK,KAAK,KAC3C,KAAK,KAAK,KAAK,MAAM,MAAM,OAC3BC,UAAW,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OAC/CC,eAAgB,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,KACxCC,aAAc,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,KACtCC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEmE,UAAWnE,EAAEC,SAASW,iBACjCwD,MAAOpE,EAAEC,SAASW,gBAAgBwD,MAAMC,QAAQ,cAAe,0BAEhExD,SAAU,MACVC,WAAY,aACZC,aAAc,eACdC,eAAgB,eAChBC,SAAU,MACVC,WAAY,aACZC,aAAc,eACdC,eAAgB,eAChBC,YAAa,KACbC,cAAe,eACfC,UAAW,KACXC,YAAa,aACbC,UAAW,MACXC,YAAa,eACbC,UAAW,KACXC,YAAa,GACbC,WAAY,gBACZC,YAAa,eACbC,SAAU,KACVC,WAAY,YACZC,UAAW,WACXC,cAAe,YACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgBsE,KAChDnC,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgBuE,IAC1BrE,YAAa,SAAS,UAAU,QAAQ,WAAW,SAAS,WAC5D,QAAQ,YAAY,WAAW,SAAS,YAAY,WACpDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,OAAO,OAAO,MAAM,MAAM,OAChCC,UAAW,cAAc,cAAc,cAAc,eAAe,iBAAiB,eAAe,eACpGC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,cACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,eACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,WACbC,cAAe,GACfC,UAAW,WACXC,YAAa,GACbC,UAAW,WACXC,YAAa,GACbC,UAAW,WACXC,YAAa,GACbC,WAAY,GACZC,YAAa,GACbC,SAAU,KACVC,WAAY,GACZC,UAAW,SACXC,cAAe,GACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgBuE,KAChDpC,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgBwE,IAC1BtE,YAAa,WAAW,YAAY,QAAQ,UAAU,QAAQ,SAC9D,SAAS,UAAU,aAAa,WAAW,YAAY,aACvDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,YAAY,YAAY,WAAW,YAAY,cAAc,aAAa,aACrFC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,OACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,OACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,SACbC,cAAe,GACfC,UAAW,SACXC,YAAa,GACbC,UAAW,UACXC,YAAa,GACbC,UAAW,UACXC,YAAa,GACbC,WAAY,GACZC,YAAa,GACbC,SAAU,MACVC,WAAY,GACZC,UAAW,SACXC,cAAe,GACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgBwE,KAChDrC,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgB,UAC1BE,YAAa,SAAS,UAAU,OAAO,QAAQ,MAAM,MACrD,MAAM,SAAS,YAAY,UAAU,WAAW,YAChDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,UAAU,aAAa,SAAS,UAAU,WAAW,QAAQ,UACxEC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,SACVC,WAAY,2BACZC,aAAc,eACdC,eAAgB,2BAChBC,SAAU,SACVC,WAAY,0BACZC,aAAc,eACdC,eAAgB,0BAChBC,YAAa,QACbC,cAAe,gBACfC,UAAW,QACXC,YAAa,gBACbC,UAAW,SACXC,YAAa,wBACbC,UAAW,UACXC,YAAa,mBACbC,WAAY,iBACZC,YAAa,kBACbC,SAAU,MACVC,WAAY,UACZC,UAAW,kBACXC,cAAe,gBACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB,WAChDmC,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgByE,IAC1BvE,YAAa,SAAS,UAAU,OAAO,QAAQ,MAAM,MACrD,MAAM,SAAS,YAAY,UAAU,WAAW,YAChDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,SAAS,YAAY,SAAS,UAAU,WAAW,QAAQ,UACtEC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,SACVC,WAAY,2BACZC,aAAc,eACdC,eAAgB,2BAChBC,SAAU,SACVC,WAAY,yBACZC,aAAc,eACdC,eAAgB,yBAChBC,YAAa,QACbC,cAAe,gBACfC,UAAW,QACXC,YAAa,gBACbC,UAAW,SACXC,YAAa,wBACbC,UAAW,UACXC,YAAa,mBACbC,WAAY,iBACZC,YAAa,kBACbC,SAAU,MACVC,WAAY,UACZC,UAAW,kBACXC,cAAe,gBACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgByE,KAChDtC,QAOH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgB0E,IAC1BxE,YAAa,UAAU,WAAW,OAAO,QAAQ,MAAM,OACvD,OAAO,SAAS,YAAY,WAAW,UAAU,YACjDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,SAAS,aAAa,UAAU,QAAQ,WAAW,QAAQ,UACtEC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,SACVC,WAAY,+BACZC,aAAc,eACdC,eAAgB,gCAChBC,SAAU,SACVC,WAAY,4BACZC,aAAc,eACdC,eAAgB,6BAChBC,YAAa,UACbC,cAAe,6BACfC,UAAW,QACXC,YAAa,6BACbC,UAAW,QACXC,YAAa,6BACbC,UAAW,UACXC,YAAa,sBACbC,WAAY,sBACZC,YAAa,oBACbC,SAAU,MACVC,WAAY,qBACZC,UAAW,iBACXC,cAAe,eACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB0E,KAChDvC,QAIH,SAAUrC,GACT,YAEAA,GAAEC,SAASC,gBAAgB2E,IAC1BzE,YAAa,SAAS,YAAY,YAAY,WAAW,OAAO,QAChE,OAAO,WAAW,eAAe,YAAY,UAAU,YACvDC,iBAAkB,MAAM,OAAO,QAAQ,QAAQ,OAAO,QACtD,OAAO,KAAK,OAAO,QAAQ,MAAM,OACjCC,UAAW,SAAS,WAAW,QAAQ,SAAS,SAAS,SAAS,OAClEC,eAAgB,MAAM,QAAQ,QAAQ,MAAM,SAAS,SAAS,OAC9DC,aAAc,KAAK,KAAK,KAAK,KAAK,OAAO,KAAK,KAC9CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,YACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,YACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,QACbC,cAAe,GACfC,UAAW,QACXC,YAAa,GACbC,UAAW,IACXC,YAAa,GACbC,UAAW,MACXC,YAAa,GACbC,WAAY,GACZC,YAAa,GACbC,SAAU,IACVC,WAAY,GACZC,UAAW,UACXC,cAAe,GACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB2E,KAChDxC,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgB4E,IAC1B1E,YAAa,UAAU,WAAW,MAAM,QAAQ,MAAM,MACtD,QAAQ,OAAO,YAAY,UAAU,WAAW,YAChDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,OAAO,QAAQ,SAAS,OAAO,SAAS,SAAS,SAC5DC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,gBACVC,WAAY,wBACZC,aAAc,eACdC,eAAgB,wBAChBC,SAAU,gBACVC,WAAY,wBACZC,aAAc,eACdC,eAAgB,wBAChBC,YAAa,WACbC,cAAe,0BACfC,UAAW,WACXC,YAAa,0BACbC,UAAW,QACXC,YAAa,0BACbC,UAAW,QACXC,YAAa,wBACbC,WAAY,4BACZC,YAAa,4BACbC,SAAU,KACVC,WAAY,wBACZC,UAAW,WACXC,cAAe,oBACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB4E,KAChDzC,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgB6E,IAC1B3E,YAAa,SAAS,OAAO,QAAQ,QAAQ,QAAQ,QACrD,QAAQ,SAAS,YAAY,UAAU,WAAW,YAClDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,UAAU,WAAW,YAAY,WAAW,WAAW,YAAY,WAC9EC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,KACtCC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,WACVC,WAAY,oBACZC,aAAc,eACdC,eAAgB,mBAChBC,SAAU,WACVC,WAAY,oBACZC,aAAc,eACdC,eAAgB,mBAChBC,YAAa,QACbC,cAAe,mBACfC,UAAW,QACXC,YAAa,uBACbC,UAAW,SACXC,YAAa,iBACbC,UAAW,OACXC,YAAa,4BACbC,WAAY,sBACZC,YAAa,uBACbC,SAAU,KACVC,WAAY,qBACZC,UAAW,iBACXC,cAAe,cACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB6E,KAChD1C,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgB,UAC1BE,YAAa,UAAU,WAAW,QAAQ,QAAQ,MAAM,OACxD,OAAO,WAAW,YAAY,UAAU,WAAW,YACnDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,SAAS,UAAU,UAAU,WAAW,YAAY,UAAU,YACzEC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,IACVC,WAAY,yBACZC,aAAc,IACdC,eAAgB,yBAChBC,SAAU,IACVC,WAAY,2BACZC,aAAc,IACdC,eAAgB,2BAChBC,YAAa,UACbC,cAAe,0BACfC,UAAW,UACXC,YAAa,0BACbC,UAAW,SACXC,YAAa,uBACbC,UAAW,UACXC,YAAa,2BACbC,WAAY,wBACZC,YAAa,0BACbC,SAAU,KACVC,WAAY,oBACZC,UAAW,aACXC,cAAe,iBACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB,WAChDmC,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgB8E,IAC1B5E,YAAa,UAAU,WAAW,QAAQ,QAAQ,MAAM,OACxD,OAAO,WAAW,YAAY,UAAU,WAAW,YACnDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,SAAS,UAAU,UAAU,WAAW,YAAY,UAAU,YACzEC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,IACVC,WAAY,yBACZC,aAAc,IACdC,eAAgB,yBAChBC,SAAU,IACVC,WAAY,2BACZC,aAAc,IACdC,eAAgB,2BAChBC,YAAa,UACbC,cAAe,0BACfC,UAAW,UACXC,YAAa,0BACbC,UAAW,SACXC,YAAa,uBACbC,UAAW,UACXC,YAAa,2BACbC,WAAY,wBACZC,YAAa,0BACbC,SAAU,KACVC,WAAY,oBACZC,UAAW,aACXC,cAAe,iBACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB8E,KAChD3C,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgB+E,IAC1B7E,YAAa,SAAS,UAAU,OAAO,QAAQ,MAAM,OACrD,OAAO,SAAS,YAAY,UAAU,WAAW,YACjDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BE,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDD,UAAW,SAAS,SAAS,UAAU,SAAS,UAAU,SAAS,UACnEE,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,iBACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,eACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,QACbC,cAAe,GACfC,UAAW,QACXC,YAAa,GACbC,UAAW,MACXC,YAAa,GACbC,UAAW,OACXC,YAAa,GACbC,WAAY,GACZC,YAAa,GACbC,SAAU,MACVC,WAAY,GACZC,UAAW,SACXC,cAAe,GACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB+E,KAChD5C,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgBgF,IAC1B9E,YAAa,UAAU,OAAO,SAAS,WAAW,MAAM,WACxD,SAAS,WAAW,WAAW,cAAc,WAAW,YACxDC,iBAAkB,MAAM,KAAK,MAAM,KAAK,MAAM,MAC9C,MAAM,MAAM,MAAM,KAAK,MAAM,OAC7BC,UAAW,YAAY,eAAe,SAAS,QAAQ,WAAW,SAAS,UAC3EC,eAAgB,MAAM,KAAK,KAAK,KAAK,MAAM,KAAK,MAChDC,aAAc,IAAI,KAAK,KAAK,KAAK,KAAK,KAAK,MAC3CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,kBACVC,WAAY,0BACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,iBACVC,WAAY,yBACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,OACbC,cAAe,yBACfC,UAAW,OACXC,YAAa,yBACbC,UAAW,UACXC,YAAa,sBACbC,UAAW,UACXC,YAAa,0BACbC,WAAY,iBACZC,YAAa,qBACbC,SAAU,OACVC,WAAY,eACZC,UAAW,mBACXC,cAAe,eACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgBgF,KAChD7C,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgB,UAC1BE,YAAa,UAAU,YAAY,QAAQ,QAAQ,OAAO,QAC1D,QAAQ,SAAS,WAAW,UAAU,WAAW,YACjDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,UAAU,gBAAgB,cAAc,eAAe,eAAe,cAAc,UAC/FC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,KACtCC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,eACVC,WAAY,wBACZC,aAAc,WACdC,eAAgB,wBAChBC,SAAU,cACVC,WAAY,uBACZC,aAAc,WACdC,eAAgB,uBAChBC,YAAa,QACbC,cAAe,qBACfC,UAAW,OACXC,YAAa,gBACbC,UAAW,SACXC,YAAa,cACbC,UAAW,SACXC,YAAa,sBACbC,WAAY,iBACZC,YAAa,iBACbC,SAAU,IACVC,WAAY,gBACZC,UAAW,yBACXC,cAAe,mBACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB,WAChDmC,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgBiF,IAC1B/E,YAAa,UAAU,YAAY,QAAQ,QAAQ,OAAO,QAC1D,QAAQ,SAAS,WAAW,UAAU,WAAW;AACjDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,UAAU,gBAAgB,cAAc,eAAe,eAAe,cAAc,UAC/FC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,KACtCC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,eACVC,WAAY,eACZC,aAAc,WACdC,eAAgB,eAChBC,SAAU,cACVC,WAAY,cACZC,aAAc,WACdC,eAAgB,cAChBC,YAAa,QACbC,cAAe,YACfC,UAAW,OACXC,YAAa,OACbC,UAAW,SACXC,YAAa,cACbC,UAAW,SACXC,YAAa,sBACbC,WAAY,iBACZC,YAAa,iBACbC,SAAU,IACVC,WAAY,gBACZC,UAAW,yBACXC,cAAe,mBACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgBiF,KAChD9C,QAIH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgBkF,IAC1BhF,YAAa,UAAU,SAAS,OAAO,SAAS,OAAO,YACvD,UAAU,QAAQ,YAAY,UAAU,WAAW,YACnDC,iBAAkB,OAAO,MAAM,MAAM,MAAM,OAAO,MAClD,MAAM,MAAM,OAAO,MAAM,MAAM,OAC/BC,UAAW,WAAW,YAAY,QAAQ,UAAU,UAAU,WAAW,SACzEC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,iBACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,kBACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,SACbC,cAAe,GACfC,UAAW,SACXC,YAAa,GACbC,UAAW,IACXC,YAAa,GACbC,UAAW,SACXC,YAAa,GACbC,WAAY,GACZC,YAAa,GACbC,SAAU,OACVC,WAAY,GACZC,UAAW,UACXC,cAAe,GACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgBkF,KAChD/C,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgBmF,IAC1BjF,YAAa,WAAW,YAAY,SAAS,UAAU,MAAM,QAC7D,QAAQ,SAAS,aAAa,YAAY,YAAY,aACtDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,WAAW,OAAO,QAAQ,WAAW,MAAM,SAAS,WAC/DC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,oBACVC,WAAY,wBACZC,aAAc,iBACdC,eAAgB,GAChBC,SAAU,mBACVC,WAAY,uBACZC,aAAc,iBACdC,eAAgB,GAChBC,YAAa,MACbC,cAAe,qBACfC,UAAW,MACXC,YAAa,qBACbC,UAAW,QACXC,YAAa,sBACbC,UAAW,UACXC,YAAa,yBACbC,WAAY,sBACZC,YAAa,wBACbC,SAAU,OACVC,WAAY,mBACZC,UAAW,oBACXC,cAAe,oBACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgBmF,KAChDhD,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgBoF,IAC1BlF,YAAa,SAAS,UAAU,OAAO,SAAS,MAAM,OACtD,OAAO,SAAS,WAAW,UAAU,SAAS,WAC9CC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,cAAc,cAAc,UAAU,QAAQ,UAAU,UAAU,WAC7EC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,aACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,aACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,UACbC,cAAe,GACfC,UAAW,UACXC,YAAa,GACbC,UAAW,WACXC,YAAa,GACbC,UAAW,UACXC,YAAa,GACbC,WAAY,GACZC,YAAa,GACbC,SAAU,KACVC,WAAY,GACZC,UAAW,SACXC,cAAe,GACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgBoF,KAChDjD,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgBqF,IAC1BnF,YAAa,SAAS,UAAU,QAAQ,QAAQ,MAAM,MACtD,MAAM,SAAS,YAAY,UAAU,WAAW,YAChDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,UAAW,WAAW,SAAS,SAAS,UAAU,SAAS,UACtEC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,MAAM,MAC7CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,uBACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,oBACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,OACbC,cAAe,GACfC,UAAW,OACXC,YAAa,GACbC,UAAW,SACXC,YAAa,GACbC,UAAW,UACXC,YAAa,GACbC,WAAY,GACZC,YAAa,GACbC,SAAU,KACVC,WAAY,GACZC,UAAW,SACXC,cAAe,GACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgBqF,KAChDlD,QAMH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgBsF,IAC1BpF,YAAa,SAAS,UAAU,QAAQ,QAAQ,MAAM,QACtD,QAAQ,SAAS,YAAY,UAAU,WAAW,YAClDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,UAAU,aAAa,QAAQ,QAAQ,gBAAgB,QAAQ,UAC1EC,eAAgB,MAAM,MAAM,MAAM,MAAM,YAAY,MAAM,OAC1DC,aAAc,KAAK,KAAK,KAAK,KAAK,WAAW,KAAK,MAClDC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,qBACVC,WAAY,qCACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,gBACVC,WAAY,gCACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,WACbC,cAAe,+BACfC,UAAW,WACXC,YAAa,+BACbC,UAAW,gBACXC,YAAa,+BACbC,UAAW,QACXC,YAAa,0BACbC,WAAY,2BACZC,YAAa,2BACbC,SAAU,QACVC,WAAY,eACZC,UAAW,qBACXC,cAAe,gBACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgBsF,KAChDnD,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgBuF,IAC1BrF,YAAa,QAAQ,SAAS,OAAO,QAAQ,MAAM,UACnD,SAAS,QAAQ,UAAU,QAAQ,SAAS,WAC5CC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,SAAS,SAAS,UAAU,YAAY,UAAU,WAAW,YACxEC,eAAgB,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC9CC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,eACVC,WAAY,wBACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,gBACVC,WAAY,sBACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,MACbC,cAAe,GACfC,UAAW,MACXC,YAAa,GACbC,UAAW,SACXC,YAAa,uBACbC,UAAW,SACXC,YAAa,sBACbC,WAAY,mBACZC,YAAa,sBACbC,SAAU,KACVC,WAAY,gBACZC,UAAW,kBACXC,cAAe,mBACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgBuF,KAChDpD,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgB,UAC1BE,YAAa,SAAS,UAAU,OAAO,QAAQ,MAAM,MACrD,MAAM,SAAS,YAAY,UAAU,WAAW,YAChDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,UAAU,aAAa,SAAS,QAAQ,WAAW,QAAQ,UACtEC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,SACVC,WAAY,0BACZC,aAAc,eACdC,eAAgB,2BAChBC,SAAU,SACVC,WAAY,wBACZC,aAAc,eACdC,eAAgB,yBAChBC,YAAa,QACbC,cAAe,eACfC,UAAW,QACXC,YAAa,eACbC,UAAW,SACXC,YAAa,wBACbC,UAAW,UACXC,YAAa,mBACbC,WAAY,iBACZC,YAAa,iBACbC,SAAU,MACVC,WAAY,UACZC,UAAW,iBACXC,cAAe,gBACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB,WAChDmC,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgBwF,IAC1BtF,YAAa,SAAS,UAAU,OAAO,QAAQ,MAAM,MACrD,MAAM,SAAS,YAAY,UAAU,WAAW,YAChDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,SAAS,YAAY,SAAS,QAAQ,WAAW,QAAQ,UACpEC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,SACVC,WAAY,0BACZC,aAAc,eACdC,eAAgB,2BAChBC,SAAU,SACVC,WAAY,wBACZC,aAAc,eACdC,eAAgB,yBAChBC,YAAa,QACbC,cAAe,eACfC,UAAW,QACXC,YAAa,eACbC,UAAW,SACXC,YAAa,wBACbC,UAAW,UACXC,YAAa,mBACbC,WAAY,iBACZC,YAAa,iBACbC,SAAU,MACVC,WAAY,UACZC,UAAW,kBACXC,cAAe,gBACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgBwF,KAChDrD,QAKH,SAAUrC,GACT,YACGA,GAAEC,SAASC,gBAAgByF,IACvBvF,YAAa,UAAU,WAAW,OAAO,QAAQ,MAAM,OACvD,OAAO,UAAU,YAAY,UAAU,WAAW,YAClDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OACpCC,UAAW,SAAS,SAAS,SAAS,SAAS,UAAU,SAAS,UAClEC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MACtCC,WAAY,aAClBC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACfC,SAAU,eAChBC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,eACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,OACbC,cAAe,GACfC,UAAW,OACXC,YAAa,GACbC,UAAW,QACXC,YAAa,GACbC,UAAW,QACXC,YAAa,GACbC,WAAY,GACZC,YAAa,GACbC,SAAU,KACVC,WAAY,GACZC,UAAW,SACXC,cAAe,GACfC,OAAO,GAELnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgByF,KACnDtD,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgB0F,IAC1BxF,YAAa,KAAK,OAAO,UAAU,WAAW,SAAS,MACvD,MAAM,OAAO,YAAY,SAAS,aAAa,WAC/CC,iBAAkB,KAAK,OAAO,MAAM,OAAO,OAAO,MAClD,MAAM,KAAK,MAAM,MAAM,OAAO,QAC9BC,UAAW,kBAAkB,eAAe,kBAAkB,aAAa,eAAe,gBAAgB,cAC1GC,eAAgB,SAAS,UAAU,WAAW,QAAQ,UAAU,SAAS,OACzEC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,KAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,YACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,WACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,QACbC,cAAe,GACfC,UAAW,QACXC,YAAa,GACbC,UAAW,MACXC,YAAa,GACbC,UAAW,OACXC,YAAa,GACbC,WAAY,GACZC,YAAa,GACbC,SAAU,KACVC,WAAY,GACZC,UAAW,SACXC,cAAe,GACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB0F,KAChDvD,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgB2F,IAC1BzF,YAAa,SAAS,aAAa,SAAS,SAAS,UAAU,WAC/D,UAAU,UAAU,UAAU,SAAS,YAAY,WACnDC,iBAAkB,OAAO,OAAO,QAAQ,QAAQ,OAAO,QACvD,OAAO,OAAO,OAAO,OAAO,OAAO,QACnCC,UAAW,UAAU,SAAS,SAAS,MAAM,WAAW,QAAQ,SAChEC,eAAgB,MAAM,KAAK,KAAK,KAAK,MAAM,KAAK,MAChDC,aAAc,MAAM,KAAK,KAAK,KAAK,MAAM,KAAK,MAC9CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,oBACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,qBACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,SACbC,cAAe,GACfC,UAAW,SACXC,YAAa,GACbC,UAAW,KACXC,YAAa,GACbC,UAAW,MACXC,YAAa,GACbC,WAAY,GACZC,YAAa,GACbC,SAAU,KACVC,WAAY,GACZC,UAAW,SACXC,cAAe,GACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB2F,KAChDxD,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgB4F,IAC1B1F,YAAa,OAAO,QAAQ,OAAO,QAAQ,QAAQ,UACnD,SAAS,UAAU,QAAQ,OAAO,QAAQ,UAC1CC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,QAAQ,YAAY,OAAO,WAAW,WAAW,OAAO,aACnEC,eAAgB,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC9CC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,aACVC,WAAY,oBACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,aACVC,WAAY,qBACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,QACbC,cAAe,GACfC,UAAW,QACXC,YAAa,GACbC,UAAW,UACXC,YAAa,0BACbC,UAAW,QACXC,YAAa,0BACbC,WAAY,YACZC,YAAa,WACbC,SAAU,KACVC,WAAY,iBACZC,UAAW,iBACXC,cAAe,oBACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB4F,KAChDzD,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgB6F,IAC1B3F,YAAa,SAAS,UAAU,OAAO,SAAS,MAAM,OACtD,OAAO,SAAS,WAAW,UAAU,SAAS,WAC9CC,iBAAkB,OAAO,MAAM,MAAM,MAAM,MAAM,MACjD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,UAAU,UAAU,UAAU,WAAW,aAAa,QAAQ,SACzEC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,SACVC,WAAY,sBACZC,aAAc,WACdC,eAAgB,sBAChBC,SAAU,SACVC,WAAY,sBACZC,aAAc,WACdC,eAAgB,sBAChBC,YAAa,QACbC,cAAe,uBACfC,UAAW,QACXC,YAAa,uBACbC,UAAW,WACXC,YAAa,2BACbC,UAAW,SACXC,YAAa,sBACbC,WAAY,gBACZC,YAAa,gBACbC,SAAU,OACVC,WAAY,iBACZC,UAAW,UACXC,cAAe,iBACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB6F,KAChD1D,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgB8F,IAC1B5F,YAAa,SAAS,QAAQ,WAAW,UAAU,UAAU,UAC7D,SAAS,UAAU,WAAW,UAAU,WAAW,WACnDC,iBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,MAChD,MAAM,MAAM,MAAM,MAAM,MAAM,OAC9BC,UAAW,SAAS,YAAY,WAAW,SAAS,SAAS,WAAY,UACzEC,eAAgB,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OACpDC,aAAc,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC5CC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,SACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,SAAU,SACVC,WAAY,GACZC,aAAc,eACdC,eAAgB,GAChBC,YAAa,WACbC,cAAe,GACfC,UAAW,WACXC,YAAa,GACbC,UAAW,WACXC,YAAa,GACbC,UAAW,UACXC,YAAa,GACbC,WAAY,GACZC,YAAa,GACbC,SAAU,KACVC,WAAY,GACZC,UAAW,SACXC,cAAe,GACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB8F,KAChD3D,QAMH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgB+F,IAC1B7F,YAAa,QAAQ,QAAQ,OAAO,QAAQ,MAAM,MAClD,SAAS,OAAO,QAAQ,SAAS,QAAQ,SACzCC,iBAAkB,IAAI,IAAI,IAAI,IAAI,IAAI,IACtC,IAAI,IAAI,IAAI,KAAK,KAAK,MACtBC,UAAW,QAAQ,MAAM,OAAO,MAAM,SAAS,OAAO,QACtDC,eAAgB,QAAQ,MAAM,OAAO,MAAM,SAAS,OAAO,QAC3DC,aAAc,QAAQ,MAAM,OAAO,MAAM,SAAS,OAAO,QACzDC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,cACVC,WAAY,YACZC,aAAc,eACdC,eAAgB,YAChBC,SAAU,cACVC,WAAY,YACZC,aAAc,eACdC,eAAgB,YAChBC,YAAa,OACbC,cAAe,WACfC,UAAW,KACXC,YAAa,KACbC,UAAW,YACXC,YAAa,iBACbC,UAAW,WACXC,YAAa,iBACbC,WAAY,aACZC,YAAa,aACbC,SAAU,OACVC,WAAY,OACZC,UAAW,gBACXC,cAAe,mBACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB+F,KAChD5D,QAIH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgBgG,IAC1B9F,YAAa,YAAa,YAAa,WAAY,WAAY,YAAa,YAC5E,YAAa,YAAa,aAAc,aAAc,iBAAkB,kBACxEC,iBAAkB,UAAW,UAAW,UAAW,UAAW,UAAW,UACzE,UAAW,UAAW,UAAW,WAAY,WAAY,YACzDC,UAAW,WAAY,UAAW,SAAU,SAAU,UAAW,UAAW,WAC5EC,eAAgB,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,MACpDC,aAAc,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,MAClDC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEC,SAASW,gBACrBC,SAAU,cACVC,WAAY,cACZC,aAAc,eACdC,eAAgB,YAChBC,SAAU,aACVC,WAAY,YACZC,aAAc,eACdC,eAAgB,UAChBC,YAAa,UACbC,cAAe,iBACfC,UAAW,UACXC,YAAa,iBACbC,UAAW,MACXC,YAAa,oBACbC,UAAW,OACXC,YAAa,iCACbC,WAAY,WACZC,YAAa,aACbC,SAAU,KACVC,WAAY,iBACZC,UAAW,2BACXC,cAAe,YACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgBgG,KAChD7D,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgB,UAC1BE,YAAa,KAAK,KAAK,KAAK,KAAK,KAAK,KACtC,KAAK,KAAK,KAAK,KAAK,MAAM,OAC1BC,iBAAkB,IAAI,IAAI,IAAI,IAAI,IAAI,IACtC,IAAI,IAAI,IAAI,IAAI,KAAK,MACrBC,UAAW,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OAC/CC,eAAgB,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC9CC,aAAc,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,KACtCC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEmE,UAAWnE,EAAEC,SAASW,iBAChCwD,MAAOpE,EAAEC,SAASW,gBAAgBwD,MAClCC,QAAQ,cAAe,0BACzBxD,SAAU,WACVC,WAAY,OACZC,aAAc,eACdC,eAAgB,QAChBC,SAAU,WACVC,WAAY,OACZC,aAAc,eACdC,eAAgB,QAChBC,YAAa,KACbC,cAAe,OACfC,UAAW,KACXC,YAAa,OACbC,UAAW,KACXC,YAAa,SACbC,UAAW,KACXC,YAAa,UACbC,WAAY,OACZC,YAAa,OACbC,SAAU,IACVC,WAAY,OACZC,UAAW,eACXC,cAAe,QACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB,WAChDmC,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgB,UAC1BE,YAAa,KAAK,KAAK,KAAK,KAAK,KAAK,KACtC,KAAK,KAAK,KAAK,KAAK,MAAM,OAC1BC,iBAAkB,IAAI,IAAI,IAAI,IAAI,IAAI,IACtC,IAAI,IAAI,IAAI,IAAI,KAAK,MACrBC,UAAW,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OAC/CC,eAAgB,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC9CC,aAAc,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,KACtCC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEmE,UAAWnE,EAAEC,SAASW,iBAChCwD,MAAOpE,EAAEC,SAASW,gBAAgBwD,MAClCC,QAAQ,cAAe,0BACzBxD,SAAU,WACVC,WAAY,OACZC,aAAc,eACdC,eAAgB,QAChBC,SAAU,WACVC,WAAY,OACZC,aAAc,eACdC,eAAgB,QAChBC,YAAa,KACbC,cAAe,OACfC,UAAW,KACXC,YAAa,OACbC,UAAW,KACXC,YAAa,SACbC,UAAW,KACXC,YAAa,WACbC,WAAY,OACZC,YAAa,OACbC,SAAU,IACVC,WAAY,OACZC,UAAW,eACXC,cAAe,QACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB,WAChDmC,QAKH,SAAUrC,GACT,YACAA,GAAEC,SAASC,gBAAgB,UAC1BE,YAAa,KAAK,KAAK,KAAK,KAAK,KAAK,KACtC,KAAK,KAAK,KAAK,KAAK,MAAM,OAC1BC,iBAAkB,IAAI,IAAI,IAAI,IAAI,IAAI,IACtC,IAAI,IAAI,IAAI,IAAI,KAAK,MACrBC,UAAW,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,OAC/CC,eAAgB,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAC9CC,aAAc,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,KACtCC,WAAY,aACZC,SAAU,EACVC,SAAUX,EAAEmE,UAAWnE,EAAEC,SAASW,iBAChCwD,MAAOpE,EAAEC,SAASW,gBAAgBwD,MAClCC,QAAQ,cAAe,0BACzBxD,SAAU,WACVC,WAAY,OACZC,aAAc,eACdC,eAAgB,QAChBC,SAAU,WACVC,WAAY,OACZC,aAAc,eACdC,eAAgB,QAChBC,YAAa,KACbC,cAAe,OACfC,UAAW,KACXC,YAAa,OACbC,UAAW,KACXC,YAAa,SACbC,UAAW,KACXC,YAAa,WACbC,WAAY,OACZC,YAAa,OACbC,SAAU,IACVC,WAAY,OACZC,UAAW,eACXC,cAAe,QACfC,OAAO,GAERnC,EAAEC,SAASmC,YAAYpC,EAAEC,SAASC,gBAAgB,WAChDmC","file":"jquery.datepick.lang.min.js"} \ No newline at end of file diff --git a/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick.min.js b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick.min.js new file mode 100755 index 000000000..ca7d8fde1 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/jquery.datepick.package-5.1.0/js/jquery.datepick.min.js @@ -0,0 +1,8 @@ +/*! http://keith-wood.name/datepick.html + Date picker for jQuery v5.1.1. + Written by Keith Wood (wood.keith{at}optusnet.com.au) February 2010. + Licensed under the MIT (http://keith-wood.name/licence.html) licence. + Please attribute the author if you use it. */ +!function(a){"use strict";var b="datepick";a.JQPlugin.createPlugin({name:b,defaultRenderer:{picker:'
    {link:prev}{link:today}{link:next}
    {months}{popup:start}
    {link:clear}{link:close}
    {popup:end}
    ',monthRow:'
    {months}
    ',month:'
    {monthHeader}
    {weekHeader}{weeks}
    ',weekHeader:"{days}",dayHeader:"{day}",week:"{days}",day:"{day}",monthSelector:".datepick-month",daySelector:"td",rtlClass:"datepick-rtl",multiClass:"datepick-multi",defaultClass:"",selectedClass:"datepick-selected",highlightedClass:"datepick-highlight",todayClass:"datepick-today",otherMonthClass:"datepick-other-month",weekendClass:"datepick-weekend",commandClass:"datepick-cmd",commandButtonClass:"",commandLinkClass:"",disabledClass:"datepick-disabled"},commands:{prev:{text:"prevText",status:"prevStatus",keystroke:{keyCode:33},enabled:function(a){var b=a.curMinDate();return!b||c.add(c.day(c._applyMonthsOffset(c.add(c.newDate(a.drawDate),1-a.options.monthsToStep,"m"),a),1),-1,"d").getTime()>=b.getTime()},date:function(a){return c.day(c._applyMonthsOffset(c.add(c.newDate(a.drawDate),-a.options.monthsToStep,"m"),a),1)},action:function(a){c.changeMonth(this,-a.options.monthsToStep)}},prevJump:{text:"prevJumpText",status:"prevJumpStatus",keystroke:{keyCode:33,ctrlKey:!0},enabled:function(a){var b=a.curMinDate();return!b||c.add(c.day(c._applyMonthsOffset(c.add(c.newDate(a.drawDate),1-a.options.monthsToJump,"m"),a),1),-1,"d").getTime()>=b.getTime()},date:function(a){return c.day(c._applyMonthsOffset(c.add(c.newDate(a.drawDate),-a.options.monthsToJump,"m"),a),1)},action:function(a){c.changeMonth(this,-a.options.monthsToJump)}},next:{text:"nextText",status:"nextStatus",keystroke:{keyCode:34},enabled:function(a){var b=a.get("maxDate");return!b||c.day(c._applyMonthsOffset(c.add(c.newDate(a.drawDate),a.options.monthsToStep,"m"),a),1).getTime()<=b.getTime()},date:function(a){return c.day(c._applyMonthsOffset(c.add(c.newDate(a.drawDate),a.options.monthsToStep,"m"),a),1)},action:function(a){c.changeMonth(this,a.options.monthsToStep)}},nextJump:{text:"nextJumpText",status:"nextJumpStatus",keystroke:{keyCode:34,ctrlKey:!0},enabled:function(a){var b=a.get("maxDate");return!b||c.day(c._applyMonthsOffset(c.add(c.newDate(a.drawDate),a.options.monthsToJump,"m"),a),1).getTime()<=b.getTime()},date:function(a){return c.day(c._applyMonthsOffset(c.add(c.newDate(a.drawDate),a.options.monthsToJump,"m"),a),1)},action:function(a){c.changeMonth(this,a.options.monthsToJump)}},current:{text:"currentText",status:"currentStatus",keystroke:{keyCode:36,ctrlKey:!0},enabled:function(a){var b=a.curMinDate(),d=a.get("maxDate"),e=a.selectedDates[0]||c.today();return(!b||e.getTime()>=b.getTime())&&(!d||e.getTime()<=d.getTime())},date:function(a){return a.selectedDates[0]||c.today()},action:function(a){var b=a.selectedDates[0]||c.today();c.showMonth(this,b.getFullYear(),b.getMonth()+1)}},today:{text:"todayText",status:"todayStatus",keystroke:{keyCode:36,ctrlKey:!0},enabled:function(a){var b=a.curMinDate(),d=a.get("maxDate");return(!b||c.today().getTime()>=b.getTime())&&(!d||c.today().getTime()<=d.getTime())},date:function(){return c.today()},action:function(){c.showMonth(this)}},clear:{text:"clearText",status:"clearStatus",keystroke:{keyCode:35,ctrlKey:!0},enabled:function(){return!0},date:function(){return null},action:function(){c.clear(this)}},close:{text:"closeText",status:"closeStatus",keystroke:{keyCode:27},enabled:function(){return!0},date:function(){return null},action:function(){c.hide(this)}},prevWeek:{text:"prevWeekText",status:"prevWeekStatus",keystroke:{keyCode:38,ctrlKey:!0},enabled:function(a){var b=a.curMinDate();return!b||c.add(c.newDate(a.drawDate),-7,"d").getTime()>=b.getTime()},date:function(a){return c.add(c.newDate(a.drawDate),-7,"d")},action:function(){c.changeDay(this,-7)}},prevDay:{text:"prevDayText",status:"prevDayStatus",keystroke:{keyCode:37,ctrlKey:!0},enabled:function(a){var b=a.curMinDate();return!b||c.add(c.newDate(a.drawDate),-1,"d").getTime()>=b.getTime()},date:function(a){return c.add(c.newDate(a.drawDate),-1,"d")},action:function(){c.changeDay(this,-1)}},nextDay:{text:"nextDayText",status:"nextDayStatus",keystroke:{keyCode:39,ctrlKey:!0},enabled:function(a){var b=a.get("maxDate");return!b||c.add(c.newDate(a.drawDate),1,"d").getTime()<=b.getTime()},date:function(a){return c.add(c.newDate(a.drawDate),1,"d")},action:function(){c.changeDay(this,1)}},nextWeek:{text:"nextWeekText",status:"nextWeekStatus",keystroke:{keyCode:40,ctrlKey:!0},enabled:function(a){var b=a.get("maxDate");return!b||c.add(c.newDate(a.drawDate),7,"d").getTime()<=b.getTime()},date:function(a){return c.add(c.newDate(a.drawDate),7,"d")},action:function(){c.changeDay(this,7)}}},defaultOptions:{pickerClass:"",showOnFocus:!0,showTrigger:null,showAnim:"show",showOptions:{},showSpeed:"normal",popupContainer:null,alignment:"bottom",fixedWeeks:!1,firstDay:0,calculateWeek:null,monthsToShow:1,monthsOffset:0,monthsToStep:1,monthsToJump:12,useMouseWheel:!0,changeMonth:!0,yearRange:"c-10:c+10",shortYearCutoff:"+10",showOtherMonths:!1,selectOtherMonths:!1,defaultDate:null,selectDefaultDate:!1,minDate:null,maxDate:null,dateFormat:"mm/dd/yyyy",autoSize:!1,rangeSelect:!1,rangeSeparator:" - ",multiSelect:0,multiSeparator:",",onDate:null,onShow:null,onChangeMonthYear:null,onSelect:null,onClose:null,altField:null,altFormat:null,constrainInput:!0,commandsAsDateFormat:!1,commands:{}},regionalOptions:{"":{monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],dateFormat:"mm/dd/yyyy",firstDay:0,renderer:{},prevText:"<Prev",prevStatus:"Show the previous month",prevJumpText:"<<",prevJumpStatus:"Show the previous year",nextText:"Next>",nextStatus:"Show the next month",nextJumpText:">>",nextJumpStatus:"Show the next year",currentText:"Current",currentStatus:"Show the current month",todayText:"Today",todayStatus:"Show today's month",clearText:"Clear",clearStatus:"Clear all the dates",closeText:"Close",closeStatus:"Close the datepicker",yearStatus:"Change the year",earlierText:"  â–²",laterText:"  â–¼",monthStatus:"Change the month",weekText:"Wk",weekStatus:"Week of the year",dayStatus:"Select DD, M d, yyyy",defaultStatus:"Select a date",isRTL:!1}},_disabled:[],_popupClass:b+"-popup",_triggerClass:b+"-trigger",_disableClass:b+"-disable",_monthYearClass:b+"-month-year",_curMonthClass:b+"-month-",_anyYearClass:b+"-any-year",_curDoWClass:b+"-dow-",_ticksTo1970:24*(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925))*60*60*1e7,_msPerDay:864e5,ATOM:"yyyy-mm-dd",COOKIE:"D, dd M yyyy",FULL:"DD, MM d, yyyy",ISO_8601:"yyyy-mm-dd",JULIAN:"J",RFC_822:"D, d M yy",RFC_850:"DD, dd-M-yy",RFC_1036:"D, d M yy",RFC_1123:"D, d M yyyy",RFC_2822:"D, d M yyyy",RSS:"D, d M yy",TICKS:"!",TIMESTAMP:"@",W3C:"yyyy-mm-dd",formatDate:function(a,b,c){if("string"!=typeof a&&(c=b,b=a,a=""),!b)return"";a=a||this.defaultOptions.dateFormat,c=c||{};for(var d=c.dayNamesShort||this.defaultOptions.dayNamesShort,e=c.dayNames||this.defaultOptions.dayNames,f=c.monthNamesShort||this.defaultOptions.monthNamesShort,g=c.monthNames||this.defaultOptions.monthNames,h=c.calculateWeek||this.defaultOptions.calculateWeek,i=function(b,c){for(var d=1;n+d1},j=function(a,b,c,d){var e=""+b;if(i(a,d))for(;e.length1},q=function(a,c){var d=p(a,c),e=[2,3,d?4:2,11,20]["oy@!".indexOf(a)+1],f=new RegExp("^-?\\d{1,"+e+"}"),g=b.substring(t).match(f);if(!g)throw"Missing number at position {0}".replace(/\{0\}/,t);return t+=g[0].length,parseInt(g[0],10)},r=function(a,c,d,e){for(var f=p(a,e)?d:c,g=0;g-1){j=1,k=l;for(var w=this.daysInMonth(i,j);k>w;w=this.daysInMonth(i,j))j++,k-=w}if(o=this.newDate(i,j,k),o.getFullYear()!==i||o.getMonth()+1!==j||o.getDate()!==k)throw"Invalid date";return o},determineDate:function(a,b,d,e,f){d&&"object"!=typeof d&&(f=e,e=d,d=null),"string"!=typeof e&&(f=e,e="");var g=function(a){try{return c.parseDate(e,a,f)}catch(a){}a=a.toLowerCase();for(var b=(a.match(/^c/)&&d?c.newDate(d):null)||c.today(),g=/([+-]?[0-9]+)\s*(d|w|m|y)?/g,h=null;h=g.exec(a);)b=c.add(b,parseInt(h[1],10),h[2]||"d");return b};return b=b?c.newDate(b):null,a="undefined"==typeof a?b:"string"==typeof a?g(a):"number"==typeof a?isNaN(a)||a===1/0||a===-(1/0)?b:c.add(c.today(),a,"d"):c.newDate(a)},daysInMonth:function(a,b){return b=a.getFullYear?a.getMonth()+1:b,a=a.getFullYear?a.getFullYear():a,this.newDate(a,b+1,0).getDate()},dayOfYear:function(a,b,d){var e=a.getFullYear?a:c.newDate(a,b,d),f=c.newDate(e.getFullYear(),1,1);return Math.floor((e.getTime()-f.getTime())/c._msPerDay)+1},iso8601Week:function(a,b,d){var e=a.getFullYear?new Date(a.getTime()):c.newDate(a,b,d);e.setDate(e.getDate()+4-(e.getDay()||7));var f=e.getTime();return e.setMonth(0,1),Math.floor(Math.round((f-e)/c._msPerDay)/7)+1},today:function(){return this._normaliseDate(new Date)},newDate:function(a,b,c){return a?a.getFullYear?this._normaliseDate(new Date(a.getTime())):new Date(a,b-1,c,12):null},_normaliseDate:function(a){return a&&a.setHours(12,0,0,0),a},year:function(a,b){return a.setFullYear(b),this._normaliseDate(a)},month:function(a,b){return a.setMonth(b-1),this._normaliseDate(a)},day:function(a,b){return a.setDate(b),this._normaliseDate(a)},add:function(a,b,d){if("d"===d||"w"===d)this._normaliseDate(a),a.setDate(a.getDate()+b*("w"===d?7:1));else{var e=a.getFullYear()+("y"===d?b:0),f=a.getMonth()+("m"===d?b:0);a.setTime(c.newDate(e,f+1,Math.min(a.getDate(),this.daysInMonth(e,f+1))).getTime())}return a},_applyMonthsOffset:function(b,d){var e=d.options.monthsOffset;return a.isFunction(e)&&(e=e.apply(d.elem[0],[b])),c.add(b,-e,"m")},_init:function(){this.defaultOptions.commands=this.commands,this.defaultOptions.calculateWeek=this.iso8601Week,this.regionalOptions[""].renderer=this.defaultRenderer,this._super()},_instSettings:function(b){return{selectedDates:[],drawDate:null,pickingRange:!1,inline:a.inArray(b[0].nodeName.toLowerCase(),["div","span"])>-1,get:function(b){return a.inArray(b,["defaultDate","minDate","maxDate"])>-1?c.determineDate(this.options[b],null,this.selectedDates[0],this.options.dateFormat,this.getConfig()):this.options[b]},curMinDate:function(){return this.pickingRange?this.selectedDates[0]:this.get("minDate")},getConfig:function(){return{dayNamesShort:this.options.dayNamesShort,dayNames:this.options.dayNames,monthNamesShort:this.options.monthNamesShort,monthNames:this.options.monthNames,calculateWeek:this.options.calculateWeek,shortYearCutoff:this.options.shortYearCutoff}}}},_postAttach:function(b,d){d.inline?(d.drawDate=c._checkMinMax(c.newDate(d.selectedDates[0]||d.get("defaultDate")||c.today()),d),d.prevDate=c.newDate(d.drawDate),this._update(b[0]),a.fn.mousewheel&&b.mousewheel(this._doMouseWheel)):(this._attachments(b,d),b.on("keydown."+d.name,this._keyDown).on("keypress."+d.name,this._keyPress).on("keyup."+d.name,this._keyUp),b.attr("disabled")&&this.disable(b[0]))},_optionsChanged:function(b,d,e){if(e.calendar&&e.calendar!==d.options.calendar){var f=function(a){return"object"==typeof d.options[a]?null:d.options[a]};e=a.extend({defaultDate:f("defaultDate"),minDate:f("minDate"),maxDate:f("maxDate")},e),d.selectedDates=[],d.drawDate=null}var g=d.selectedDates;a.extend(d.options,e),this.setDate(b[0],g,null,!1,!0),d.pickingRange=!1,d.drawDate=c.newDate(this._checkMinMax((d.options.defaultDate?d.get("defaultDate"):d.drawDate)||d.get("defaultDate")||c.today(),d)),d.inline||this._attachments(b,d),(d.inline||d.div)&&this._update(b[0])},_attachments:function(b,d){b.off("focus."+d.name),d.options.showOnFocus&&b.on("focus."+d.name,this.show),d.trigger&&d.trigger.remove();var e=d.options.showTrigger;d.trigger=e?a(e).clone().removeAttr("id").addClass(this._triggerClass)[d.options.isRTL?"insertBefore":"insertAfter"](b).click(function(){c.isDisabled(b[0])||c[c.curInst===d?"hide":"show"](b[0])}):a([]),this._autoSize(b,d);var f=this._extractDates(d,b.val());f&&this.setDate(b[0],f,null,!0);var g=d.get("defaultDate");d.options.selectDefaultDate&&g&&0===d.selectedDates.length&&this.setDate(b[0],c.newDate(g||c.today()))},_autoSize:function(a,b){if(b.options.autoSize&&!b.inline){var d=c.newDate(2009,10,20),e=b.options.dateFormat;if(e.match(/[DM]/)){var f=function(a){for(var b=0,c=0,d=0;db&&(b=a[d].length,c=d);return c};d.setMonth(f(b.options[e.match(/MM/)?"monthNames":"monthNamesShort"])),d.setDate(f(b.options[e.match(/DD/)?"dayNames":"dayNamesShort"])+20-d.getDay())}b.elem.attr("size",c.formatDate(e,d,b.getConfig()).length)}},_preDestroy:function(b,c){c.trigger&&c.trigger.remove(),b.empty().off("."+c.name),c.inline&&a.fn.mousewheel&&b.unmousewheel(),!c.inline&&c.options.autoSize&&b.removeAttr("size")},multipleEvents:function(){var a=arguments;return function(){for(var b=0;b').find("button,select").prop("disabled",!0).end().find("a").removeAttr("href")}else b.prop("disabled",!0),c.trigger.filter("button."+this._triggerClass).prop("disabled",!0).end().filter("img."+this._triggerClass).css({opacity:"0.5",cursor:"default"});this._disabled=a.map(this._disabled,function(a){return a===b[0]?null:a}),this._disabled.push(b[0])}},isDisabled:function(b){return b&&a.inArray(b,this._disabled)>-1},show:function(b){b=a(b.target||b);var d=c._getInst(b);if(c.curInst!==d&&(c.curInst&&c.hide(c.curInst,!0),!a.isEmptyObject(d))){d.lastVal=null,d.selectedDates=c._extractDates(d,b.val()),d.pickingRange=!1,d.drawDate=c._checkMinMax(c.newDate(d.selectedDates[0]||d.get("defaultDate")||c.today()),d),d.prevDate=c.newDate(d.drawDate),c.curInst=d,c._update(b[0],!0);var e=c._checkOffset(d);d.div.css({left:e.left,top:e.top});var f=d.options.showAnim,g=d.options.showSpeed;if(g="normal"===g&&a.ui&&parseInt(a.ui.version.substring(2))>=8?"_default":g,a.effects&&(a.effects[f]||a.effects.effect&&a.effects.effect[f])){var h=d.div.data();for(var i in h)i.match(/^ec\.storage\./)&&(h[i]=d._mainDiv.css(i.replace(/ec\.storage\./,"")));d.div.data(h).show(f,d.options.showOptions,g)}else d.div[f||"show"](f?g:0)}},_extractDates:function(a,b){if(b!==a.lastVal){a.lastVal=b,b=b.split(a.options.multiSelect?a.options.multiSeparator:a.options.rangeSelect?a.options.rangeSeparator:"\0");for(var d=[],e=0;e").addClass(this._popupClass).css({display:d?"none":"static",position:"absolute",left:b.offset().left,top:b.offset().top+b.outerHeight()}).appendTo(a(e.options.popupContainer||"body")),a.fn.mousewheel&&e.div.mousewheel(this._doMouseWheel)),e.div.html(this._generateContent(b[0],e)),b.focus())},_updateInput:function(b,d){var e=this._getInst(b);if(!a.isEmptyObject(e)){for(var f="",g="",h=e.options.multiSelect?e.options.multiSeparator:e.options.rangeSeparator,i=e.options.altFormat||e.options.dateFormat,j=0;j0?h:"")+c.formatDate(e.options.dateFormat,e.selectedDates[j],e.getConfig()),g+=(j>0?h:"")+c.formatDate(i,e.selectedDates[j],e.getConfig());e.inline||d||a(b).val(f),a(e.options.altField).val(g),!a.isFunction(e.options.onSelect)||d||e.inSelect||(e.inSelect=!0,e.options.onSelect.apply(b,[e.selectedDates]),e.inSelect=!1)}},_getBorders:function(a){var b=function(a){return{thin:1,medium:3,thick:5}[a]||a};return[parseFloat(b(a.css("border-left-width"))),parseFloat(b(a.css("border-top-width")))]},_checkOffset:function(b){var c=b.elem.is(":hidden")&&b.trigger?b.trigger:b.elem,d=c.offset(),e=a(window).width(),f=a(window).height();if(0===e)return d;var g=!1;a(b.elem).parents().each(function(){return g=g||"fixed"===a(this).css("position"),!g});var h=document.documentElement.scrollLeft||document.body.scrollLeft,i=document.documentElement.scrollTop||document.body.scrollTop,j=d.top-(g?i:0)-b.div.outerHeight(),k=d.top-(g?i:0)+c.outerHeight(),l=d.left-(g?h:0),m=d.left-(g?h:0)+c.outerWidth()-b.div.outerWidth(),n=d.left-h+b.div.outerWidth()>e,o=d.top-i+b.elem.outerHeight()+b.div.outerHeight()>f;b.div.css("position",g?"fixed":"absolute");var p=b.options.alignment;return d="topLeft"===p?{left:l,top:j}:"topRight"===p?{left:m,top:j}:"bottomLeft"===p?{left:l,top:k}:"bottomRight"===p?{left:m,top:k}:"top"===p?{left:b.options.isRTL||n?m:l,top:j}:{left:b.options.isRTL||n?m:l,top:o?j:k},d.left=Math.max(g?0:h,d.left),d.top=Math.max(g?0:i,d.top),d},_checkExternalClick:function(b){if(c.curInst){var d=a(b.target);0!==d.closest("."+c._popupClass+",."+c._triggerClass).length||d.hasClass(c._getMarker())||c.hide(c.curInst)}},hide:function(b,d){if(b){var e=this._getInst(b);if(a.isEmptyObject(e)&&(e=b),e&&e===c.curInst){var f=d?"":e.options.showAnim,g=e.options.showSpeed;g="normal"===g&&a.ui&&parseInt(a.ui.version.substring(2))>=8?"_default":g;var h=function(){e.div&&(e.div.remove(),e.div=null,c.curInst=null,a.isFunction(e.options.onClose)&&e.options.onClose.apply(b,[e.selectedDates]))};if(e.div.stop(),a.effects&&(a.effects[f]||a.effects.effect&&a.effects.effect[f]))e.div.hide(f,e.options.showOptions,g,h);else{var i="slideDown"===f?"slideUp":"fadeIn"===f?"fadeOut":"hide";e.div[i](f?g:"",h)}f||h()}}},_keyDown:function(b){var d=b.data&&b.data.elem||b.target,e=c._getInst(d),f=!1,g=null;if(e.inline||e.div){if(9===b.keyCode)c.hide(d);else if(13===b.keyCode)c.selectDate(d,a("a."+e.options.renderer.highlightedClass,e.div)[0]),f=!0;else for(var h in e.options.commands)if(e.options.commands.hasOwnProperty(h)&&(g=e.options.commands[h],g.keystroke.keyCode===b.keyCode&&!!g.keystroke.ctrlKey==!(!b.ctrlKey&&!b.metaKey)&&!!g.keystroke.altKey===b.altKey&&!!g.keystroke.shiftKey===b.shiftKey)){c.performAction(d,h),f=!0;break}}else g=e.options.commands.current,g.keystroke.keyCode===b.keyCode&&!!g.keystroke.ctrlKey==!(!b.ctrlKey&&!b.metaKey)&&!!g.keystroke.altKey===b.altKey&&!!g.keystroke.shiftKey===b.shiftKey&&(c.show(d),f=!0);return e.ctrlKey=b.keyCode<48&&32!==b.keyCode||b.ctrlKey||b.metaKey,f&&(b.preventDefault(),b.stopPropagation()),!f},_keyPress:function(b){var d=c._getInst(b.data&&b.data.elem||b.target);if(!a.isEmptyObject(d)&&d.options.constrainInput){var e=String.fromCharCode(b.keyCode||b.charCode),f=c._allowedChars(d);return b.metaKey||d.ctrlKey||e<" "||!f||f.indexOf(e)>-1}return!0},_allowedChars:function(a){for(var b=a.options.multiSelect?a.options.multiSeparator:a.options.rangeSelect?a.options.rangeSeparator:"",c=!1,d=!1,e=a.options.dateFormat,f=0;f0&&c.setDate(d,f,null,!0)}catch(a){}return!0},_doMouseWheel:function(b,d){var e=c.curInst&&c.curInst.elem[0]||a(b.target).closest("."+c._getMarker())[0];if(!c.isDisabled(e)){var f=c._getInst(e);f.options.useMouseWheel&&(d=d<0?-1:1,c.changeMonth(e,-f.options[b.ctrlKey?"monthsToJump":"monthsToStep"]*d)),b.preventDefault()}},clear:function(b){var d=this._getInst(b);if(!a.isEmptyObject(d)){d.selectedDates=[],this.hide(b);var e=d.get("defaultDate");d.options.selectDefaultDate&&e?this.setDate(b,c.newDate(e||c.today())):this._updateInput(b)}},getDate:function(b){var c=this._getInst(b);return a.isEmptyObject(c)?[]:c.selectedDates},setDate:function(b,d,e,f,g){var h=this._getInst(b);if(!a.isEmptyObject(h)){a.isArray(d)||(d=[d],e&&d.push(e));var i=h.get("minDate"),j=h.get("maxDate"),k=h.selectedDates[0];h.selectedDates=[];for(var l=0;l=i.getTime())&&(!j||m.getTime()<=j.getTime())){for(var n=!1,o=0;oh.selectedDates[1].getTime()?h.selectedDates[0]:h.selectedDates[1]}h.pickingRange=!1}h.prevDate=h.drawDate?c.newDate(h.drawDate):null,h.drawDate=this._checkMinMax(c.newDate(h.selectedDates[0]||h.get("defaultDate")||c.today()),h),g||(this._update(b),this._updateInput(b,f))}},isSelectable:function(b,d){var e=this._getInst(b);return!a.isEmptyObject(e)&&(d=c.determineDate(d,e.selectedDates[0]||this.today(),null,e.options.dateFormat,e.getConfig()),this._isSelectable(b,d,e.options.onDate,e.get("minDate"),e.get("maxDate")))},_isSelectable:function(b,c,d,e,f){var g="boolean"==typeof d?{selectable:d}:a.isFunction(d)?d.apply(b,[c,!0]):{};return g.selectable!==!1&&(!e||c.getTime()>=e.getTime())&&(!f||c.getTime()<=f.getTime())},performAction:function(b,c){var d=this._getInst(b);if(!a.isEmptyObject(d)&&!this.isDisabled(b)){var e=d.options.commands;e[c]&&e[c].enabled.apply(b,[d])&&e[c].action.apply(b,[d])}},showMonth:function(b,d,e,f){var g=this._getInst(b);if(!a.isEmptyObject(g)&&("undefined"!=typeof f||g.drawDate.getFullYear()!==d||g.drawDate.getMonth()+1!==e)){g.prevDate=c.newDate(g.drawDate);var h=this._checkMinMax("undefined"!=typeof d?c.newDate(d,e,1):c.today(),g);g.drawDate=c.newDate(h.getFullYear(),h.getMonth()+1,"undefined"!=typeof f?f:Math.min(g.drawDate.getDate(),c.daysInMonth(h.getFullYear(),h.getMonth()+1))),this._update(b)}},changeMonth:function(b,d){var e=this._getInst(b);if(!a.isEmptyObject(e)){var f=c.add(c.newDate(e.drawDate),d,"m");this.showMonth(b,f.getFullYear(),f.getMonth()+1)}},changeDay:function(b,d){var e=this._getInst(b);if(!a.isEmptyObject(e)){var f=c.add(c.newDate(e.drawDate),d,"d");this.showMonth(b,f.getFullYear(),f.getMonth()+1,f.getDate())}},_checkMinMax:function(a,b){var d=b.get("minDate"),e=b.get("maxDate");return a=d&&a.getTime()e.getTime()?c.newDate(e):a},retrieveDate:function(b,c){var d=this._getInst(b);return a.isEmptyObject(d)?null:this._normaliseDate(new Date(parseInt(c.className.replace(/^.*dp(-?\d+).*$/,"$1"),10)))},selectDate:function(b,d){var e=this._getInst(b);if(!a.isEmptyObject(e)&&!this.isDisabled(b)){var f=this.retrieveDate(b,d);if(e.options.multiSelect){for(var g=!1,h=0;h'+(j?c.formatDate(d.options[i.text],j,d.getConfig()):d.options[i.text])+"")}};for(var n in d.options.commands)d.options.commands.hasOwnProperty(n)&&(m("button",'button type="button"',"button",n,d.options.renderer.commandButtonClass),m("link",'a href="javascript:void(0)"',"a",n,d.options.renderer.commandLinkClass));if(l=a(l),f[1]>1){var o=0;a(d.options.renderer.monthSelector,l).each(function(){var b=++o%f[1];a(this).addClass(1===b?"first":0===b?"last":"")})}var p=this;l.find(d.options.renderer.daySelector+" a").hover(function(){e.apply(this),a(this).addClass(d.options.renderer.highlightedClass)},e).click(function(){p.selectDate(b,this)}).end().find("select."+this._monthYearClass+":not(."+this._anyYearClass+")").change(function(){var c=a(this).val().split("/");p.showMonth(b,parseInt(c[1],10),parseInt(c[0],10))}).end().find("select."+this._anyYearClass).click(function(){a(this).css("visibility","hidden").next("input").css({left:this.offsetLeft,top:this.offsetTop,width:this.offsetWidth,height:this.offsetHeight}).show().focus()}).end().find("input."+p._monthYearClass).change(function(){try{var c=parseInt(a(this).val(),10);c=isNaN(c)?d.drawDate.getFullYear():c,p.showMonth(b,c,d.drawDate.getMonth()+1,d.drawDate.getDate())}catch(a){window.alert(a)}}).keydown(function(b){13===b.keyCode?a(b.elem).change():27===b.keyCode&&(a(b.elem).hide().prev("select").css("visibility","visible"),d.elem.focus())});var q={elem:d.elem[0]};l.keydown(q,this._keyDown).keypress(q,this._keyPress).keyup(q,this._keyUp),l.find("."+d.options.renderer.commandClass).click(function(){if(!a(this).hasClass(d.options.renderer.disabledClass)){var e=this.className.replace(new RegExp("^.*"+d.options.renderer.commandClass+"-([^ ]+).*$"),"$1");c.performAction(b,e)}}),d.options.isRTL&&l.addClass(d.options.renderer.rtlClass),f[0]*f[1]>1&&l.addClass(d.options.renderer.multiClass),d.options.pickerClass&&l.addClass(d.options.pickerClass),a("body").append(l);var r=0;return l.find(d.options.renderer.monthSelector).each(function(){r+=a(this).outerWidth()}),l.width(r/f[0]),a.isFunction(d.options.onShow)&&d.options.onShow.apply(b,[l,d]),l},_generateMonth:function(b,d,e,f,g,h){var i=c.daysInMonth(e,f),j=d.options.monthsToShow;j=a.isArray(j)?j:[1,j];var k=d.options.fixedWeeks||j[0]*j[1]>1,l=d.options.firstDay,m=(c.newDate(e,f,1).getDay()-l+7)%7,n=k?6:Math.ceil((m+i)/7),o=d.options.selectOtherMonths&&d.options.showOtherMonths,p=d.pickingRange?d.selectedDates[0]:d.get("minDate"),q=d.get("maxDate"),r=g.week.indexOf("{weekOfYear}")>-1,s=c.today(),t=c.newDate(e,f,1);c.add(t,-m-(k&&t.getDay()===l?7:0),"d");for(var u=t.getTime(),v="",w=0;w'+(a.isFunction(d.options.calculateWeek)?d.options.calculateWeek(t):0)+"":"",y="",z=0;z<7;z++){var A=!1;if(d.options.rangeSelect&&d.selectedDates.length>0)A=t.getTime()>=d.selectedDates[0]&&t.getTime()<=d.selectedDates[1];else for(var B=0;B"+(d.options.showOtherMonths||t.getMonth()+1===f?C.content||t.getDate():" ")+(D?"":"")), +c.add(t,1,"d"),u=t.getTime()}v+=this._prepare(g.week,d).replace(/\{days\}/g,y).replace(/\{weekOfYear\}/g,x)}var E=this._prepare(g.month,d).match(/\{monthHeader(:[^\}]+)?\}/);E=E[0].length<=13?"MM yyyy":E[0].substring(13,E[0].length-1),E=h?this._generateMonthSelection(d,e,f,p,q,E,g):c.formatDate(E,c.newDate(e,f,1),d.getConfig());var F=this._prepare(g.weekHeader,d).replace(/\{days\}/g,this._generateDayHeaders(d,g));return this._prepare(g.month,d).replace(/\{monthHeader(:[^\}]+)?\}/g,E).replace(/\{weekHeader\}/g,F).replace(/\{weeks\}/g,v)},_generateDayHeaders:function(a,b){for(var c="",d=0;d<7;d++){var e=(d+a.options.firstDay)%7;c+=this._prepare(b.dayHeader,a).replace(/\{day\}/g,''+a.options.dayNamesMin[e]+"")}return c},_generateMonthSelection:function(a,b,d,e,f,g){if(!a.options.changeMonth)return c.formatDate(g,c.newDate(b,d,1),a.getConfig());for(var h=a.options["monthNames"+(g.match(/mm/i)?"":"Short")],i=g.replace(/m+/i,"\\x2E").replace(/y+/i,"\\x2F"),j='",i=i.replace(/\\x2E/,j);var l=a.options.yearRange;if("any"===l)j='';else{l=l.split(":");var m=c.today().getFullYear(),n=l[0].match("c[+-].*")?b+parseInt(l[0].substring(1),10):(l[0].match("[+-].*")?m:0)+parseInt(l[0],10),o=l[1].match("c[+-].*")?b+parseInt(l[1].substring(1),10):(l[1].match("[+-].*")?m:0)+parseInt(l[1],10);j=' + - + +
    +
    +
    + + + + + + + + + + + + + + + + +
    + + + + + \ No newline at end of file diff --git a/transitimeWebapp/src/main/webapp/maps/css/mapUi.css b/transitclockWebapp/src/main/webapp/maps/css/mapUi.css similarity index 54% rename from transitimeWebapp/src/main/webapp/maps/css/mapUi.css rename to transitclockWebapp/src/main/webapp/maps/css/mapUi.css index 8446f0fef..1c1bbefd2 100644 --- a/transitimeWebapp/src/main/webapp/maps/css/mapUi.css +++ b/transitclockWebapp/src/main/webapp/maps/css/mapUi.css @@ -25,7 +25,7 @@ html, body, #map { /* Set the title text here in css */ #mapTitle:before { - content: "Transitime"; + content: "TransitClock"; } /* Center the route selector. @@ -54,3 +54,75 @@ sup { vertical-align: super; font-size: xx-small; } + + + +/*Update styles*/ +.page-container { + display: flex; + width: 100%; + height: 100%; + flex-direction: row; +} + +.side-container-view { + width: 20%; + height: 100%; + border-right: 1px solid black; +} + +.map-container-view { + width: 80%; + position: relative; + height: 100%; +} +.param { + display: flex; + flex-flow: row; + justify-content: space-between; + margin-top: 6%; + align-items: center; +} + +.param > * { + font-size: 14px; + font-family: 'Montserrat', sans-serif; +} + +.select2-container--default { + background-color: white; + border: 1px solid #c1c1c1c1; + border-radius: 0px; + cursor: text; + box-shadow: 0px 1px 4px rgb(0 0 0 / 33% ); +} +.select2-container { + width: 236px!important; +} +.param-container{ + flex-flow: column; + width: 94%; + max-width: 30vw; + justify-content: space-between; + padding: 0 3%; + display: flex; +} + +.param-container label{ + margin-right: 2px; + width: auto; +} +.margintop{ + margin-top: 20px; +} + +.title { + margin-top: 40px; + margin-bottom: 10px; + font-weight: normal; + text-align: center; + background: #019932; + color: white; + padding: 8px; + font-size: 24px; +} \ No newline at end of file diff --git a/transitimeWebapp/src/main/webapp/maps/css/mbtaMapUi.css b/transitclockWebapp/src/main/webapp/maps/css/mbtaMapUi.css similarity index 100% rename from transitimeWebapp/src/main/webapp/maps/css/mbtaMapUi.css rename to transitclockWebapp/src/main/webapp/maps/css/mbtaMapUi.css diff --git a/transitimeWebapp/src/main/webapp/maps/heatmap.jsp b/transitclockWebapp/src/main/webapp/maps/heatmap.jsp similarity index 96% rename from transitimeWebapp/src/main/webapp/maps/heatmap.jsp rename to transitclockWebapp/src/main/webapp/maps/heatmap.jsp index b37086cf7..3441e8833 100644 --- a/transitimeWebapp/src/main/webapp/maps/heatmap.jsp +++ b/transitclockWebapp/src/main/webapp/maps/heatmap.jsp @@ -4,7 +4,7 @@ It is kept here for now only in case want to look at creating another heatmap in the future. --%> -<%@page import="org.transitime.web.WebConfigParams"%> +<%@page import="org.transitclock.web.WebConfigParams"%> diff --git a/transitimeWebapp/src/main/webapp/maps/heatmap/heatmap.js b/transitclockWebapp/src/main/webapp/maps/heatmap/heatmap.js similarity index 99% rename from transitimeWebapp/src/main/webapp/maps/heatmap/heatmap.js rename to transitclockWebapp/src/main/webapp/maps/heatmap/heatmap.js index 2542a52f9..88b20e397 100644 --- a/transitimeWebapp/src/main/webapp/maps/heatmap/heatmap.js +++ b/transitclockWebapp/src/main/webapp/maps/heatmap/heatmap.js @@ -11,7 +11,7 @@ var HeatmapConfig = { defaultRadius: 40, defaultRenderer: 'canvas2d', - defaultGradient: { 0.25: "rgb(0,0,255)", 0.55: "rgb(0,255,0)", 0.85: "yellow", 1.0: "rgb(255,0,0)"}, + defaultGradient: { '0.25': "rgb(0,0,255)", '0.55': "rgb(0,255,0)", '0.85': "yellow", '1.0': "rgb(255,0,0)"}, defaultMaxOpacity: 1, defaultMinOpacity: 0, defaultBlur: .85, diff --git a/transitclockWebapp/src/main/webapp/maps/heatmap/heatmap.min.js b/transitclockWebapp/src/main/webapp/maps/heatmap/heatmap.min.js new file mode 100644 index 000000000..ceb28db67 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/maps/heatmap/heatmap.min.js @@ -0,0 +1,9 @@ +/* + * heatmap.js v2.0.0 | JavaScript Heatmap Library + * + * Copyright 2008-2014 Patrick Wied - All rights reserved. + * Dual licensed under MIT and Beerware license + * + * :: 2014-08-05 01:42 + */ +(function(a){var b={defaultRadius:40,defaultRenderer:"canvas2d",defaultGradient:{'.25':"rgb(0,0,255)",'.55':"rgb(0,255,0)",'.85':"yellow",1:"rgb(255,0,0)"},defaultMaxOpacity:1,defaultMinOpacity:0,defaultBlur:.85,defaultXField:"x",defaultYField:"y",defaultValueField:"value",plugins:{}};var c=function i(){var a=function d(a){this._coordinator={};this._data=[];this._radi=[];this._min=0;this._max=1;this._xField=a["xField"]||a.defaultXField;this._yField=a["yField"]||a.defaultYField;this._valueField=a["valueField"]||a.defaultValueField;if(a["radius"]){this._cfgRadius=a["radius"]}};var c=b.defaultRadius;a.prototype={_organiseData:function(a,b){var d=a[this._xField];var e=a[this._yField];var f=this._radi;var g=this._data;var h=this._max;var i=this._min;var j=a[this._valueField]||1;var k=a.radius||this._cfgRadius||c;if(!g[d]){g[d]=[];f[d]=[]}if(!g[d][e]){g[d][e]=j;f[d][e]=k}else{g[d][e]+=j}if(g[d][e]>h){if(!b){this._max=g[d][e]}else{this.setDataMax(g[d][e])}return false}else{return{x:d,y:e,value:j,radius:k,min:i,max:h}}},_unOrganizeData:function(){var a=[];var b=this._data;var c=this._radi;for(var d in b){for(var e in b[d]){a.push({x:d,y:e,radius:c[d][e],value:b[d][e]})}}return{min:this._min,max:this._max,data:a}},_onExtremaChange:function(){this._coordinator.emit("extremachange",{min:this._min,max:this._max})},addData:function(){if(arguments[0].length>0){var a=arguments[0];var b=a.length;while(b--){this.addData.call(this,a[b])}}else{var c=this._organiseData(arguments[0],true);if(c){this._coordinator.emit("renderpartial",{min:this._min,max:this._max,data:[c]})}}return this},setData:function(a){var b=a.data;var c=b.length;this._max=a.max;this._min=a.min||0;this._data=[];this._radi=[];for(var d=0;dthis._renderBoundaries[2]){this._renderBoundaries[2]=l+2*j}if(m+2*j>this._renderBoundaries[3]){this._renderBoundaries[3]=m+2*j}}},_colorize:function(){var a=this._renderBoundaries[0];var b=this._renderBoundaries[1];var c=this._renderBoundaries[2]-a;var d=this._renderBoundaries[3]-b;var e=this._width;var f=this._height;var g=this._opacity;var h=this._maxOpacity;var i=this._minOpacity;if(a<0){a=0}if(b<0){b=0}if(a+c>e){c=e-a}if(b+d>f){d=f-b}var j=this.shadowCtx.getImageData(a,b,c,d);var k=j.data;var l=k.length;var m=this._palette;for(var n=3;n0){q=g}else{if(o>0;return b},getDataURL:function(){return this.canvas.toDataURL()}};return d}();var e=function k(){var a=false;if(b["defaultRenderer"]==="canvas2d"){a=d}return a}();var f={merge:function(){var a={};var b=arguments.length;for(var c=0;c +<%@page import="org.transitclock.db.webstructs.WebAgency"%> <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> @@ -21,10 +21,10 @@ if (agencyId == null || agencyId.isEmpty()) {
    Real-time Maps for <%= WebAgency.getCachedWebAgency(agencyId).getAgencyName() %>
    Not Agency Specific
    @@ -103,7 +106,8 @@ if (agencyId == null || agencyId.isEmpty()) {
  • Predictions by Location
  • + - \ No newline at end of file + diff --git a/transitimeWebapp/src/main/webapp/reports/apiCalls/predsByLocApiParams.jsp b/transitclockWebapp/src/main/webapp/reports/apiCalls/predsByLocApiParams.jsp similarity index 100% rename from transitimeWebapp/src/main/webapp/reports/apiCalls/predsByLocApiParams.jsp rename to transitclockWebapp/src/main/webapp/reports/apiCalls/predsByLocApiParams.jsp diff --git a/transitimeWebapp/src/main/webapp/reports/apiCalls/predsByLocForAllAgenciesApiParams.jsp b/transitclockWebapp/src/main/webapp/reports/apiCalls/predsByLocForAllAgenciesApiParams.jsp similarity index 100% rename from transitimeWebapp/src/main/webapp/reports/apiCalls/predsByLocForAllAgenciesApiParams.jsp rename to transitclockWebapp/src/main/webapp/reports/apiCalls/predsByLocForAllAgenciesApiParams.jsp diff --git a/transitimeWebapp/src/main/webapp/reports/apiCalls/predsByRouteStopApiParams.jsp b/transitclockWebapp/src/main/webapp/reports/apiCalls/predsByRouteStopApiParams.jsp similarity index 100% rename from transitimeWebapp/src/main/webapp/reports/apiCalls/predsByRouteStopApiParams.jsp rename to transitclockWebapp/src/main/webapp/reports/apiCalls/predsByRouteStopApiParams.jsp diff --git a/transitclockWebapp/src/main/webapp/reports/apiCalls/resetVehicleApiParams.jsp b/transitclockWebapp/src/main/webapp/reports/apiCalls/resetVehicleApiParams.jsp new file mode 100644 index 000000000..5615bbbf7 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/apiCalls/resetVehicleApiParams.jsp @@ -0,0 +1,48 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1"%> + + + +<%@include file="/template/includes.jsp" %> + +Specify Parameters + + + + + + + + + + + + +<%@include file="/template/header.jsp" %> + +
    + Select Parameters for Reset Vehicle API +
    + +
    + <%-- Create route selector --%> + + + <%-- Create json/xml format radio buttons --%> + + + <%-- Create submit button --%> + +
    + + + diff --git a/transitimeWebapp/src/main/webapp/reports/apiCalls/routeApiParams.jsp b/transitclockWebapp/src/main/webapp/reports/apiCalls/routeApiParams.jsp similarity index 100% rename from transitimeWebapp/src/main/webapp/reports/apiCalls/routeApiParams.jsp rename to transitclockWebapp/src/main/webapp/reports/apiCalls/routeApiParams.jsp diff --git a/transitimeWebapp/src/main/webapp/reports/apiCalls/routeDetailsApiParams.jsp b/transitclockWebapp/src/main/webapp/reports/apiCalls/routeDetailsApiParams.jsp similarity index 100% rename from transitimeWebapp/src/main/webapp/reports/apiCalls/routeDetailsApiParams.jsp rename to transitclockWebapp/src/main/webapp/reports/apiCalls/routeDetailsApiParams.jsp diff --git a/transitimeWebapp/src/main/webapp/reports/apiCalls/serviceIdsApiParams.jsp b/transitclockWebapp/src/main/webapp/reports/apiCalls/serviceIdsApiParams.jsp similarity index 100% rename from transitimeWebapp/src/main/webapp/reports/apiCalls/serviceIdsApiParams.jsp rename to transitclockWebapp/src/main/webapp/reports/apiCalls/serviceIdsApiParams.jsp diff --git a/transitimeWebapp/src/main/webapp/reports/apiCalls/serviceIdsCurrentApiParams.jsp b/transitclockWebapp/src/main/webapp/reports/apiCalls/serviceIdsCurrentApiParams.jsp similarity index 100% rename from transitimeWebapp/src/main/webapp/reports/apiCalls/serviceIdsCurrentApiParams.jsp rename to transitclockWebapp/src/main/webapp/reports/apiCalls/serviceIdsCurrentApiParams.jsp diff --git a/transitimeWebapp/src/main/webapp/reports/apiCalls/siriStopMonitoringApiParams.jsp b/transitclockWebapp/src/main/webapp/reports/apiCalls/siriStopMonitoringApiParams.jsp similarity index 100% rename from transitimeWebapp/src/main/webapp/reports/apiCalls/siriStopMonitoringApiParams.jsp rename to transitclockWebapp/src/main/webapp/reports/apiCalls/siriStopMonitoringApiParams.jsp diff --git a/transitimeWebapp/src/main/webapp/reports/apiCalls/siriVehicleMonitoringApiParams.jsp b/transitclockWebapp/src/main/webapp/reports/apiCalls/siriVehicleMonitoringApiParams.jsp similarity index 100% rename from transitimeWebapp/src/main/webapp/reports/apiCalls/siriVehicleMonitoringApiParams.jsp rename to transitclockWebapp/src/main/webapp/reports/apiCalls/siriVehicleMonitoringApiParams.jsp diff --git a/transitimeWebapp/src/main/webapp/reports/apiCalls/tripApiParams.jsp b/transitclockWebapp/src/main/webapp/reports/apiCalls/tripApiParams.jsp similarity index 100% rename from transitimeWebapp/src/main/webapp/reports/apiCalls/tripApiParams.jsp rename to transitclockWebapp/src/main/webapp/reports/apiCalls/tripApiParams.jsp diff --git a/transitimeWebapp/src/main/webapp/reports/apiCalls/tripWithTravelTimesApiParams.jsp b/transitclockWebapp/src/main/webapp/reports/apiCalls/tripWithTravelTimesApiParams.jsp similarity index 100% rename from transitimeWebapp/src/main/webapp/reports/apiCalls/tripWithTravelTimesApiParams.jsp rename to transitclockWebapp/src/main/webapp/reports/apiCalls/tripWithTravelTimesApiParams.jsp diff --git a/transitimeWebapp/src/main/webapp/reports/apiCalls/vehicleConfigsApiParams.jsp b/transitclockWebapp/src/main/webapp/reports/apiCalls/vehicleConfigsApiParams.jsp similarity index 100% rename from transitimeWebapp/src/main/webapp/reports/apiCalls/vehicleConfigsApiParams.jsp rename to transitclockWebapp/src/main/webapp/reports/apiCalls/vehicleConfigsApiParams.jsp diff --git a/transitimeWebapp/src/main/webapp/reports/apiCalls/vehiclesApiParams.jsp b/transitclockWebapp/src/main/webapp/reports/apiCalls/vehiclesApiParams.jsp similarity index 100% rename from transitimeWebapp/src/main/webapp/reports/apiCalls/vehiclesApiParams.jsp rename to transitclockWebapp/src/main/webapp/reports/apiCalls/vehiclesApiParams.jsp diff --git a/transitimeWebapp/src/main/webapp/reports/apiCalls/vehiclesDetailsApiParams.jsp b/transitclockWebapp/src/main/webapp/reports/apiCalls/vehiclesDetailsApiParams.jsp similarity index 100% rename from transitimeWebapp/src/main/webapp/reports/apiCalls/vehiclesDetailsApiParams.jsp rename to transitclockWebapp/src/main/webapp/reports/apiCalls/vehiclesDetailsApiParams.jsp diff --git a/transitimeWebapp/src/main/webapp/reports/apiCalls/vertStopsScheduleApiParams.jsp b/transitclockWebapp/src/main/webapp/reports/apiCalls/vertStopsScheduleApiParams.jsp similarity index 100% rename from transitimeWebapp/src/main/webapp/reports/apiCalls/vertStopsScheduleApiParams.jsp rename to transitclockWebapp/src/main/webapp/reports/apiCalls/vertStopsScheduleApiParams.jsp diff --git a/transitimeWebapp/src/main/webapp/reports/avlJsonData.jsp b/transitclockWebapp/src/main/webapp/reports/avlJsonData.jsp similarity index 62% rename from transitimeWebapp/src/main/webapp/reports/avlJsonData.jsp rename to transitclockWebapp/src/main/webapp/reports/avlJsonData.jsp index 83dd9b6b8..788382ba1 100644 --- a/transitimeWebapp/src/main/webapp/reports/avlJsonData.jsp +++ b/transitclockWebapp/src/main/webapp/reports/avlJsonData.jsp @@ -1,21 +1,25 @@ -<%@ page language="java" contentType="application/json; charset=UTF-8" - pageEncoding="UTF-8"%> -<%@ page import="org.transitime.reports.AvlJsonQuery" %> -<% -// Get the params -String agencyId = request.getParameter("a"); -String vehicleId = request.getParameter("v"); -String routeId = request.getParameter("r"); -String beginDate = request.getParameter("beginDate"); -String numDays = request.getParameter("numDays"); -String beginTime = request.getParameter("beginTime"); -String endTime = request.getParameter("endTime"); - -// Query db and get JSON string -String jsonString = AvlJsonQuery.getAvlWithMatchesJson(agencyId, vehicleId, routeId, beginDate, numDays, beginTime, endTime); - -// Respond with the JSON string -response.setContentType("application/json"); -response.setHeader("Access-Control-Allow-Origin", "*"); -response.getWriter().write(jsonString); -%> \ No newline at end of file +<%@ page language="java" contentType="application/json; charset=UTF-8" + pageEncoding="UTF-8"%> +<%@ page import="org.transitclock.reports.AvlJsonQuery" %> +<% +// Get the params +String agencyId = request.getParameter("a"); +String vehicleId = request.getParameter("v"); +String routeId = request.getParameter("r"); +String beginDate = request.getParameter("beginDate"); +String beginTime = request.getParameter("beginTime"); +String endTime = request.getParameter("endTime"); +String includeHeadway = request.getParameter("includeHeadway"); +String earlyMsec = request.getParameter("early"); +String lateMsec = request.getParameter("late"); + + + +// Query db and get JSON string +String jsonString = AvlJsonQuery.getAvlJson(agencyId, vehicleId, beginDate, beginTime, endTime, routeId, includeHeadway,earlyMsec,lateMsec); + +// Respond with the JSON string +response.setContentType("application/json"); +response.setHeader("Access-Control-Allow-Origin", "*"); +response.getWriter().write(jsonString); +%> diff --git a/transitclockWebapp/src/main/webapp/reports/avlMap.jsp b/transitclockWebapp/src/main/webapp/reports/avlMap.jsp new file mode 100644 index 000000000..95263e2e5 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/avlMap.jsp @@ -0,0 +1,92 @@ +<%@ page import="org.transitclock.utils.web.WebUtils" %> +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1"%> + + + + + + + + + + + <%@include file="/template/includes.jsp" %> + + + + + + + + + + + + + + + AVL Data Map + + + + + +
    +
    +
    + + +
    + + +
    +
    + + +
    +
    + + Export +
    +
    +
    +
    + + + + + +
    +
    1X
    +
    00:00:00
    +
    +
    +
    +

    AVL Marker Legend

    +
    +
    + = On Time +
    +
    +
    + = Late +
    +
    +
    + = Early +
    +
    +
    + = No Data +
    +
    + + + + + + diff --git a/transitimeWebapp/src/main/webapp/reports/avlMapByRouteParams.jsp b/transitclockWebapp/src/main/webapp/reports/avlMapByRouteParams.jsp similarity index 95% rename from transitimeWebapp/src/main/webapp/reports/avlMapByRouteParams.jsp rename to transitclockWebapp/src/main/webapp/reports/avlMapByRouteParams.jsp index 6d31fd78f..89ba15028 100644 --- a/transitimeWebapp/src/main/webapp/reports/avlMapByRouteParams.jsp +++ b/transitclockWebapp/src/main/webapp/reports/avlMapByRouteParams.jsp @@ -25,6 +25,7 @@
    <%-- For passing agency param to the report --%> "> + diff --git a/transitimeWebapp/src/main/webapp/reports/avlMapByVehicleParams.jsp b/transitclockWebapp/src/main/webapp/reports/avlMapByVehicleParams.jsp similarity index 96% rename from transitimeWebapp/src/main/webapp/reports/avlMapByVehicleParams.jsp rename to transitclockWebapp/src/main/webapp/reports/avlMapByVehicleParams.jsp index 361cbc5d2..c12e91a6a 100644 --- a/transitimeWebapp/src/main/webapp/reports/avlMapByVehicleParams.jsp +++ b/transitclockWebapp/src/main/webapp/reports/avlMapByVehicleParams.jsp @@ -25,6 +25,7 @@ <%-- For passing agency param to the report --%> "> + diff --git a/transitclockWebapp/src/main/webapp/reports/avlMapParams.jsp b/transitclockWebapp/src/main/webapp/reports/avlMapParams.jsp new file mode 100644 index 000000000..d06da5e7e --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/avlMapParams.jsp @@ -0,0 +1,42 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1"%> + + + + <%@include file="/template/includes.jsp" %> + + Specify Parameters + + + + + + + + + + +<%@include file="/template/header.jsp" %> + +
    + Select Parameters for Displaying AVL Data in Map +
    + +
    + + <%-- For passing agency param to the report --%> + "> + + + + + + + + + + +
    + + + \ No newline at end of file diff --git a/transitclockWebapp/src/main/webapp/reports/data/routeScheduleAdherence.jsp b/transitclockWebapp/src/main/webapp/reports/data/routeScheduleAdherence.jsp new file mode 100644 index 000000000..1d4fc003a --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/data/routeScheduleAdherence.jsp @@ -0,0 +1,41 @@ +<%@ page import="org.transitclock.reports.ScheduleAdherenceController" %> +<%@ page import="java.util.List" %> +<%@ page import="java.util.Date" %> +<%@ page import="java.util.Arrays" %> +<%@ page import="java.text.SimpleDateFormat" %> +<%@ page import="org.json.JSONArray" %> +<%@ page contentType="application/json" %> +<% + +String startDateStr = request.getParameter("beginDate"); +String numDaysStr = request.getParameter("numDays"); +String startTime = request.getParameter("beginTime"); +String endTime = request.getParameter("endTime"); +boolean byRoute = new Boolean(request.getParameter("byGroup")); +String datatype = request.getParameter("datatype"); + +if (startTime == null || startTime == "") + startTime = "00:00:00"; +else + startTime += ":00"; + +if (endTime == null || endTime == "") + endTime = "23:59:59"; +else + endTime += ":00"; + +String routeIdList = request.getParameter("routeIds"); +List routeIds = routeIdList == null ? null : Arrays.asList(routeIdList.split(",")); + + +SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); +Date startDate = dateFormat.parse(startDateStr); +int numDays = Integer.parseInt(numDaysStr); + +List results = ScheduleAdherenceController.routeScheduleAdherence(startDate, + numDays, startTime, endTime, routeIds, byRoute, datatype); + +JSONArray json = new JSONArray(results); +json.write(out); + +%> \ No newline at end of file diff --git a/transitclockWebapp/src/main/webapp/reports/data/stopScheduleAdherence.jsp b/transitclockWebapp/src/main/webapp/reports/data/stopScheduleAdherence.jsp new file mode 100644 index 000000000..37e27a354 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/data/stopScheduleAdherence.jsp @@ -0,0 +1,41 @@ +<%@ page import="org.transitclock.reports.ScheduleAdherenceController" %> +<%@ page import="java.util.List" %> +<%@ page import="java.util.Date" %> +<%@ page import="java.util.Arrays" %> +<%@ page import="java.text.SimpleDateFormat" %> +<%@ page import="org.json.JSONArray" %> +<%@ page contentType="application/json" %> +<% + +String startDateStr = request.getParameter("beginDate"); +String numDaysStr = request.getParameter("numDays"); +String startTime = request.getParameter("beginTime"); +String endTime = request.getParameter("endTime"); +boolean byStop = new Boolean(request.getParameter("byGroup")); +String datatype = request.getParameter("datatype"); + +if (startTime == null || startTime == "") + startTime = "00:00:00"; +else + startTime += ":00"; + +if (endTime == null || endTime == "") + endTime = "23:59:59"; +else + endTime += ":00"; + +String stopIdList = request.getParameter("stopIds"); +List stopIds = stopIdList == null ? null : Arrays.asList(stopIdList.split(",")); + + +SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); +Date startDate = dateFormat.parse(startDateStr); +int numDays = Integer.parseInt(numDaysStr); + +List results = ScheduleAdherenceController.stopScheduleAdherence(startDate, + numDays, startTime, endTime, stopIds, byStop, datatype); + +JSONArray json = new JSONArray(results); +json.write(out); + +%> \ No newline at end of file diff --git a/transitclockWebapp/src/main/webapp/reports/data/summaryScheduleAdherence.jsp b/transitclockWebapp/src/main/webapp/reports/data/summaryScheduleAdherence.jsp new file mode 100644 index 000000000..ad59a95e7 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/data/summaryScheduleAdherence.jsp @@ -0,0 +1,54 @@ +<%@ page import="org.transitclock.reports.ScheduleAdherenceController" %> +<%@ page import="org.transitclock.utils.Time" %> +<%@ page import="java.sql.Timestamp" %> +<%@ page import="java.util.List" %> +<%@ page import="java.util.Date" %> +<%@ page import="java.util.Arrays" %> +<%@ page import="java.text.SimpleDateFormat" %> +<%@ page import="org.json.JSONArray" %> +<%@ page import="org.apache.commons.lang3.StringUtils" %> + +<%@ page contentType="application/json" %> +<% +// todo this code should be in a struts action +String startDateStr = request.getParameter("beginDate"); +String numDaysStr = StringUtils.isBlank(request.getParameter("numDays")) ? "1" : request.getParameter("numDays"); +String startTime = request.getParameter("beginTime"); +String endTime = request.getParameter("endTime"); +String earlyLimitStr = request.getParameter("allowableEarly"); +String lateLimitStr = request.getParameter("allowableLate"); +Double earlyLimit = -60.0; +Double lateLimit = 60.0; + +if (StringUtils.isEmpty(startTime)) + startTime = "00:00:00"; +else + startTime += ":00"; + +if (StringUtils.isEmpty(endTime)) + endTime = "23:59:59"; +else + endTime += ":00"; + +if (!StringUtils.isEmpty(earlyLimitStr)) { + earlyLimit = Double.parseDouble(earlyLimitStr) * 60; +} +if (!StringUtils.isEmpty(lateLimitStr)) { + lateLimit = Double.parseDouble(lateLimitStr) * 60; +} + + +String routeIdList = request.getParameter("r"); +List routeIds = routeIdList == null ? null : Arrays.asList(routeIdList.split(",")); + + +SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); +Date startDate = dateFormat.parse(startDateStr); + +List results = ScheduleAdherenceController.routeScheduleAdherenceSummary(startDate, + Integer.parseInt(numDaysStr), startTime, endTime, earlyLimit, lateLimit, routeIds); + +JSONArray json = new JSONArray(results); +json.write(out); + +%> \ No newline at end of file diff --git a/transitclockWebapp/src/main/webapp/reports/images/arrow.png b/transitclockWebapp/src/main/webapp/reports/images/arrow.png new file mode 100644 index 000000000..2612ffda7 Binary files /dev/null and b/transitclockWebapp/src/main/webapp/reports/images/arrow.png differ diff --git a/transitclockWebapp/src/main/webapp/reports/images/bus-24.png b/transitclockWebapp/src/main/webapp/reports/images/bus-24.png new file mode 100644 index 000000000..5114fcbb3 Binary files /dev/null and b/transitclockWebapp/src/main/webapp/reports/images/bus-24.png differ diff --git a/transitclockWebapp/src/main/webapp/reports/images/bus-24@2x.png b/transitclockWebapp/src/main/webapp/reports/images/bus-24@2x.png new file mode 100644 index 000000000..99bcaed59 Binary files /dev/null and b/transitclockWebapp/src/main/webapp/reports/images/bus-24@2x.png differ diff --git a/transitclockWebapp/src/main/webapp/reports/images/bus.png b/transitclockWebapp/src/main/webapp/reports/images/bus.png new file mode 100644 index 000000000..b835af155 Binary files /dev/null and b/transitclockWebapp/src/main/webapp/reports/images/bus.png differ diff --git a/transitclockWebapp/src/main/webapp/reports/images/cafe-24.png b/transitclockWebapp/src/main/webapp/reports/images/cafe-24.png new file mode 100644 index 000000000..f278fc930 Binary files /dev/null and b/transitclockWebapp/src/main/webapp/reports/images/cafe-24.png differ diff --git a/transitclockWebapp/src/main/webapp/reports/images/cafe-24@2x.png b/transitclockWebapp/src/main/webapp/reports/images/cafe-24@2x.png new file mode 100644 index 000000000..5d3281fce Binary files /dev/null and b/transitclockWebapp/src/main/webapp/reports/images/cafe-24@2x.png differ diff --git a/transitimeWebapp/src/main/webapp/reports/images/calendar.gif b/transitclockWebapp/src/main/webapp/reports/images/calendar.gif similarity index 100% rename from transitimeWebapp/src/main/webapp/reports/images/calendar.gif rename to transitclockWebapp/src/main/webapp/reports/images/calendar.gif diff --git a/transitimeWebapp/src/main/webapp/reports/images/calendar.png b/transitclockWebapp/src/main/webapp/reports/images/calendar.png similarity index 100% rename from transitimeWebapp/src/main/webapp/reports/images/calendar.png rename to transitclockWebapp/src/main/webapp/reports/images/calendar.png diff --git a/transitimeWebapp/src/main/webapp/reports/images/evolution_calendar.png b/transitclockWebapp/src/main/webapp/reports/images/evolution_calendar.png similarity index 100% rename from transitimeWebapp/src/main/webapp/reports/images/evolution_calendar.png rename to transitclockWebapp/src/main/webapp/reports/images/evolution_calendar.png diff --git a/transitclockWebapp/src/main/webapp/reports/images/ferry-24.png b/transitclockWebapp/src/main/webapp/reports/images/ferry-24.png new file mode 100644 index 000000000..411ac1eaf Binary files /dev/null and b/transitclockWebapp/src/main/webapp/reports/images/ferry-24.png differ diff --git a/transitclockWebapp/src/main/webapp/reports/images/ferry-24@2x.png b/transitclockWebapp/src/main/webapp/reports/images/ferry-24@2x.png new file mode 100644 index 000000000..70d702f80 Binary files /dev/null and b/transitclockWebapp/src/main/webapp/reports/images/ferry-24@2x.png differ diff --git a/transitclockWebapp/src/main/webapp/reports/images/harbor-24.png b/transitclockWebapp/src/main/webapp/reports/images/harbor-24.png new file mode 100644 index 000000000..9d1974e93 Binary files /dev/null and b/transitclockWebapp/src/main/webapp/reports/images/harbor-24.png differ diff --git a/transitclockWebapp/src/main/webapp/reports/images/harbor-24@2x.png b/transitclockWebapp/src/main/webapp/reports/images/harbor-24@2x.png new file mode 100644 index 000000000..1094bc415 Binary files /dev/null and b/transitclockWebapp/src/main/webapp/reports/images/harbor-24@2x.png differ diff --git a/transitimeWebapp/src/main/webapp/reports/images/page-loader.gif b/transitclockWebapp/src/main/webapp/reports/images/page-loader.gif similarity index 100% rename from transitimeWebapp/src/main/webapp/reports/images/page-loader.gif rename to transitclockWebapp/src/main/webapp/reports/images/page-loader.gif diff --git a/transitclockWebapp/src/main/webapp/reports/images/pitch-12.png b/transitclockWebapp/src/main/webapp/reports/images/pitch-12.png new file mode 100644 index 000000000..91ad5f329 Binary files /dev/null and b/transitclockWebapp/src/main/webapp/reports/images/pitch-12.png differ diff --git a/transitclockWebapp/src/main/webapp/reports/images/pitch-12@2x.png b/transitclockWebapp/src/main/webapp/reports/images/pitch-12@2x.png new file mode 100644 index 000000000..4fa7a6ef8 Binary files /dev/null and b/transitclockWebapp/src/main/webapp/reports/images/pitch-12@2x.png differ diff --git a/transitclockWebapp/src/main/webapp/reports/images/pitch-18.png b/transitclockWebapp/src/main/webapp/reports/images/pitch-18.png new file mode 100644 index 000000000..149a6599e Binary files /dev/null and b/transitclockWebapp/src/main/webapp/reports/images/pitch-18.png differ diff --git a/transitclockWebapp/src/main/webapp/reports/images/pitch-18@2x.png b/transitclockWebapp/src/main/webapp/reports/images/pitch-18@2x.png new file mode 100644 index 000000000..3c80905d9 Binary files /dev/null and b/transitclockWebapp/src/main/webapp/reports/images/pitch-18@2x.png differ diff --git a/transitclockWebapp/src/main/webapp/reports/images/playback/media-playback-pause.svg b/transitclockWebapp/src/main/webapp/reports/images/playback/media-playback-pause.svg new file mode 100644 index 000000000..8a434cabb --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/images/playback/media-playback-pause.svg @@ -0,0 +1,641 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Media Playback Pause + + + Lapo Calamandrei + + + + + + media + pause + playback + video + music + + + + + Jakub Steiner + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/transitclockWebapp/src/main/webapp/reports/images/playback/media-playback-start.svg b/transitclockWebapp/src/main/webapp/reports/images/playback/media-playback-start.svg new file mode 100644 index 000000000..75616de46 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/images/playback/media-playback-start.svg @@ -0,0 +1,319 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Media Playback Start + + + Lapo Calamandrei + + + + + + play + media + music + video + player + + + + + Jakub Steiner + + + + + + + + + + + + + + + + + + + diff --git a/transitclockWebapp/src/main/webapp/reports/images/playback/media-seek-backward.svg b/transitclockWebapp/src/main/webapp/reports/images/playback/media-seek-backward.svg new file mode 100644 index 000000000..75f49f1a8 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/images/playback/media-seek-backward.svg @@ -0,0 +1,374 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Media Seek Backward + + + Lapo Calamandrei + + + + + + + + + + + + + + + + + + + + + + diff --git a/transitclockWebapp/src/main/webapp/reports/images/playback/media-seek-forward.svg b/transitclockWebapp/src/main/webapp/reports/images/playback/media-seek-forward.svg new file mode 100644 index 000000000..b1b9fe950 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/images/playback/media-seek-forward.svg @@ -0,0 +1,383 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Media Seek Forward + + + Lapo Calamandrei + + + + + + Jakub Steiner + + + + + + + + + + + + + + + + + + + + + diff --git a/transitclockWebapp/src/main/webapp/reports/images/playback/media-skip-backward.svg b/transitclockWebapp/src/main/webapp/reports/images/playback/media-skip-backward.svg new file mode 100755 index 000000000..fccd7762c --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/images/playback/media-skip-backward.svg @@ -0,0 +1,1025 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Media Skip Backward + + + Lapo Calamandrei + + + + + + Jakub Steiner + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/transitclockWebapp/src/main/webapp/reports/images/playback/media-skip-forward.svg b/transitclockWebapp/src/main/webapp/reports/images/playback/media-skip-forward.svg new file mode 100755 index 000000000..7c4d40054 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/images/playback/media-skip-forward.svg @@ -0,0 +1,1013 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Media Skip Forward + + + Lapo Calamandrei + + + + + + Jakub Steiner + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/transitclockWebapp/src/main/webapp/reports/images/rail-24.png b/transitclockWebapp/src/main/webapp/reports/images/rail-24.png new file mode 100644 index 000000000..48bbe8b01 Binary files /dev/null and b/transitclockWebapp/src/main/webapp/reports/images/rail-24.png differ diff --git a/transitclockWebapp/src/main/webapp/reports/images/rail-24@2x.png b/transitclockWebapp/src/main/webapp/reports/images/rail-24@2x.png new file mode 100644 index 000000000..9818b8e80 Binary files /dev/null and b/transitclockWebapp/src/main/webapp/reports/images/rail-24@2x.png differ diff --git a/transitclockWebapp/src/main/webapp/reports/images/rail-light-24.png b/transitclockWebapp/src/main/webapp/reports/images/rail-light-24.png new file mode 100644 index 000000000..93fb13bdb Binary files /dev/null and b/transitclockWebapp/src/main/webapp/reports/images/rail-light-24.png differ diff --git a/transitclockWebapp/src/main/webapp/reports/images/rail-light-24@2x.png b/transitclockWebapp/src/main/webapp/reports/images/rail-light-24@2x.png new file mode 100644 index 000000000..e7461fbfb Binary files /dev/null and b/transitclockWebapp/src/main/webapp/reports/images/rail-light-24@2x.png differ diff --git a/transitclockWebapp/src/main/webapp/reports/images/rail-metro-24.png b/transitclockWebapp/src/main/webapp/reports/images/rail-metro-24.png new file mode 100644 index 000000000..40aa1815f Binary files /dev/null and b/transitclockWebapp/src/main/webapp/reports/images/rail-metro-24.png differ diff --git a/transitclockWebapp/src/main/webapp/reports/images/rail-metro-24@2x.png b/transitclockWebapp/src/main/webapp/reports/images/rail-metro-24@2x.png new file mode 100644 index 000000000..f048b1ac3 Binary files /dev/null and b/transitclockWebapp/src/main/webapp/reports/images/rail-metro-24@2x.png differ diff --git a/transitclockWebapp/src/main/webapp/reports/index.jsp b/transitclockWebapp/src/main/webapp/reports/index.jsp new file mode 100644 index 000000000..25abc59f5 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/index.jsp @@ -0,0 +1,162 @@ +<%@page import="org.transitclock.db.webstructs.WebAgency" %> + +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1" %> + +<% + String agencyId = request.getParameter("a"); + if (agencyId == null || agencyId.isEmpty()) { + response.getWriter().write("You must specify agency in query string (e.g. ?a=mbta)"); + return; + } +%> + + + <%@include file="/template/includes.jsp" %> + + + Historical Reports + + +<%@include file="/template/header.jsp" %> +
    +
    Historical Reports for <%= WebAgency.getCachedWebAgency(agencyId).getAgencyName() %> +
    + +
    Prediction Accuracy
    (only for agencies where prediction accuracy stored to database) +
    + + +
    AVL Reports
    + + + <%--
    Event Reports
    + +
    --%> + +
    Schedule Adherence Reports
    + + + +
    Miscellaneous Reports
    + + +
    Speed Reports
    + + +
    Run Time Reports
    + + +
    Real Time Reports
    + + +
    Real Time Vehicle Monitoring
    + + +
    Status Reports
    + + + + + + diff --git a/transitclockWebapp/src/main/webapp/reports/javascript/routePerformanceTable.js b/transitclockWebapp/src/main/webapp/reports/javascript/routePerformanceTable.js new file mode 100644 index 000000000..fd21e7305 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/javascript/routePerformanceTable.js @@ -0,0 +1,40 @@ +function getDataAndDrawChart() { + $("#loading").show(); + + $.ajax({ + "url": "routePerformanceData.jsp?" + $("#menu form").serialize(), + "success": drawChart + }); +} + +function drawChart(resp) { + $("#loading").hide(); + + var url = "predAccuracyRangeChart.jsp?" + $("#menu form").serialize(); + var rows = resp.map(function(d) { + + var id = d.routeId; + var link = $("").attr("href", url+"&r="+id).text(id); + + var route = {v: id, f:link[0].outerHTML} + + var pv = parseFloat(d.performance); + var pf = pv*100 + "%"; + var perf = {v: pv, f: pf} + + return [route, perf]; + }) + + var data = new google.visualization.DataTable(); + data.addColumn('string', 'Route'); + data.addColumn('number', 'Performance'); + data.addRows(rows); + + var table = new google.visualization.Table(document.getElementById('tableDiv')); + table.draw(data, {showRowNumber: true, width: '100%', height: '100%', page: "enable", allowHtml: true}); +} + +google.load("visualization", "1", {packages:["table"]}); + +$("#submit").click(getDataAndDrawChart); +$("#loading").hide() \ No newline at end of file diff --git a/transitclockWebapp/src/main/webapp/reports/javascript/scheduleAdherence.js b/transitclockWebapp/src/main/webapp/reports/javascript/scheduleAdherence.js new file mode 100644 index 000000000..a77358f16 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/javascript/scheduleAdherence.js @@ -0,0 +1,220 @@ +// Code for creating the box plot is based on +// https://developers.google.com/chart/interactive/docs/gallery/intervals#box-plot + +// This code should work for routes or stops depending on the value of the "group" and "groupId" variables + +google.load('visualization', 1, {'packages':['corechart']}); + +$("#loading, #extra, #boxPlotInfo").hide() + +var today = new Date().toISOString().slice(0,10) +$("#fromDate, #toDate").val(today); +$("#fromTime, #toTime").val("09:00") + +$("#groups").select2() + +var group = $("#_group").text(); +var groupId = $("#_groupId").text(); +var dataUrl = "data/" + group + "ScheduleAdherence.jsp?"; + +$("#getGroups").click(function() { + + $("#message").text("Loading...") + + var params = $("#params").serialize(); + + $.get(dataUrl + params + "&byGroup=true", function(groups) { + + $("#extra").show(); + initGroups(groups); + + $("#go").off("click"); + $("#go").click(function() { + $("#loading").show() + var r = $("#groups").val() + $.get(dataUrl + params + "&" + groupId + "s=" + r.join(","), main); + }) + + $("#limitGroup").off("change"); + $("#limitGroup").on("change", function(evt) { + var limit = evt.target.value; + var filterGroups = groups.filter(function(d) { return d.count > limit }) + initGroups(filterGroups); + }) + }) +}) + + + +function initGroups(groups) { + $("#groups *").remove(); + + $("#message").text("" + groups.length + " " + group + "s"); + + groups.forEach(function(x) { + var option = $("
    +
    diff --git a/transitimeWebapp/src/main/webapp/reports/params/submitReport.jsp b/transitclockWebapp/src/main/webapp/reports/params/submitReport.jsp similarity index 74% rename from transitimeWebapp/src/main/webapp/reports/params/submitReport.jsp rename to transitclockWebapp/src/main/webapp/reports/params/submitReport.jsp index dd775cce3..db476f927 100644 --- a/transitimeWebapp/src/main/webapp/reports/params/submitReport.jsp +++ b/transitclockWebapp/src/main/webapp/reports/params/submitReport.jsp @@ -1,2 +1,2 @@ <%-- For creating a centered submit button --%> -
    +
    diff --git a/transitimeWebapp/src/main/webapp/reports/params/text.jsp b/transitclockWebapp/src/main/webapp/reports/params/text.jsp similarity index 100% rename from transitimeWebapp/src/main/webapp/reports/params/text.jsp rename to transitclockWebapp/src/main/webapp/reports/params/text.jsp diff --git a/transitclockWebapp/src/main/webapp/reports/params/vehicle.jsp b/transitclockWebapp/src/main/webapp/reports/params/vehicle.jsp new file mode 100644 index 000000000..57ae4c929 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/params/vehicle.jsp @@ -0,0 +1,55 @@ +<%-- For creating a vehicle selector parameter via a jsp include. + User can select all vehicles (v set to "") OR a single VEHICLE. + Reads in routes via API for the agency specified by the "a" param. --%> + + + + + +
    + + +
    + diff --git a/transitimeWebapp/src/main/webapp/reports/params/vehicleSingle.jsp b/transitclockWebapp/src/main/webapp/reports/params/vehicleSingle.jsp similarity index 56% rename from transitimeWebapp/src/main/webapp/reports/params/vehicleSingle.jsp rename to transitclockWebapp/src/main/webapp/reports/params/vehicleSingle.jsp index 02c4fd35c..8cc3c0728 100644 --- a/transitimeWebapp/src/main/webapp/reports/params/vehicleSingle.jsp +++ b/transitclockWebapp/src/main/webapp/reports/params/vehicleSingle.jsp @@ -25,21 +25,21 @@ $.getJSON(apiUrlPrefix + "/command/vehicleIds", // Configure the selector to be a select2 one that has // search capability $("#vehicle").select2({ - placeholder: "Select VehicleXXX MGS", - data : selectorData}) - // Need to reset tooltip after selector is used. Sheesh! - .on("select2:select", function(e) { - var configuredTitle = $( "#vehicle" ).attr("title"); - $( "#select2-route-container" ).tooltip({ content: configuredTitle, - position: { my: "left+10 center", at: "right center" } }); - }); - - // Tooltips for a select2 widget are rather broken. So get - // the tooltip title attribute from the original route element - // and set the tooltip for the newly created element. - var configuredTitle = $( "#vehicle" ).attr("title"); - $( "#select2-vehicle-container" ).tooltip({ content: configuredTitle, - position: { my: "left+10 center", at: "right center" } }); + placeholder: "Select Vehicle", + data : selectorData}); + + // Tooltips for a select2 widget don't automatically go away when + // item selected so remove the tooltip manually. This is a really + // complicated interaction between select2 and jquery UI tooltips. + // First need to set the tooltip title content but getting the + // originally configured title for the element. + var modifiedRouteElement = $( "#s2id_vehicle" ); + var configuredTitle = modifiedRouteElement.attr("title"); + $( "#s2id_vehicle" ).tooltip({ content: configuredTitle }); + + // Now that the title has set need to manually remove the tooltip + // when a select2 item is selected. Sheesh! + $("#vehicle").on("change", function(e) { $("#s2id_vehicle").tooltip("close") }); }); diff --git a/transitclockWebapp/src/main/webapp/reports/predAccuracyBucketChart.jsp b/transitclockWebapp/src/main/webapp/reports/predAccuracyBucketChart.jsp new file mode 100644 index 000000000..fb1969882 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/predAccuracyBucketChart.jsp @@ -0,0 +1,226 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1"%> +<%@ page import="org.transitclock.utils.web.WebUtils" %> +<%@page import="org.transitclock.db.webstructs.WebAgency"%> +<% +// Determine all the parameters from the query string + +// Determine agency using "a" param +String agencyId = request.getParameter("a"); + +// Determine list of routes for title using "r" param. +// Note that can specify multiple routes. +String stopIds[] = request.getParameterValues("s"); +String routeIds[] = request.getParameterValues("r"); +String titleRoutes = request.getParameter("t"); +if (titleRoutes == null) titleRoutes = ""; +if (routeIds != null && !routeIds[0].isEmpty()) { + titleRoutes += ", route "; + if (routeIds.length > 1) + titleRoutes += "s"; + titleRoutes += routeIds[0]; + for (int i=1; i 1 ? "s" : ""); + +if ((beginTime != null && !beginTime.isEmpty()) || (endTime != null && !endTime.isEmpty())) { + chartTitle += ", " + beginTime + " to " + endTime; +} + +%> + + + + + <%@include file="/template/includes.jsp" %> + + Prediction Accuracy + + + + + + <%@include file="/template/header.jsp" %> + +
    +
    +
    +
    Schedule Adherence loading....
    + + + + + + diff --git a/transitclockWebapp/src/main/webapp/reports/predAccuracyBucketData.jsp b/transitclockWebapp/src/main/webapp/reports/predAccuracyBucketData.jsp new file mode 100644 index 000000000..9b16d8346 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/predAccuracyBucketData.jsp @@ -0,0 +1,137 @@ +<%@ page import="org.transitclock.reports.PredAccuracyFiveBucketQuery" %> +<%@ page import="org.transitclock.utils.Time" %> +<%@ page import="java.util.Arrays" %> +<%@ page import="java.text.ParseException" %> +<%@ page import="java.util.Map" %> +<%@ page import="java.util.HashMap" %> +<%@ page import="org.apache.commons.lang3.StringUtils" %> + +<% + // Get params from the query string + String agencyId = request.getParameter("a"); + + String beginDate = request.getParameter("beginDate"); + String numDays = StringUtils.isBlank(request.getParameter("numDays")) ? "1" : request.getParameter("numDays"); + String beginTime = request.getParameter("beginTime"); + String endTime = request.getParameter("endTime"); + + String allowableEarly1 = request.getParameter("allowableEarly1"); + String allowableEarly2 = request.getParameter("allowableEarly2"); + String allowableEarly3 = request.getParameter("allowableEarly3"); + String allowableEarly4 = request.getParameter("allowableEarly4"); + String allowableEarly5 = request.getParameter("allowableEarly5"); + + String allowableLate1 = request.getParameter("allowableLate1"); + String allowableLate2 = request.getParameter("allowableLate2"); + String allowableLate3 = request.getParameter("allowableLate3"); + String allowableLate4 = request.getParameter("allowableLate4"); + String allowableLate5 = request.getParameter("allowableLate5"); + + String[] allowableEarlyStr = new String[] { allowableEarly1, allowableEarly2, allowableEarly3, allowableEarly4, allowableEarly5 }; + String[] allowableLateStr = new String[] { allowableLate1, allowableLate2, allowableLate3, allowableLate4, allowableLate5 }; + + Integer[] allowableEarlySec = new Integer[5]; + Integer[] allowableLateSec = new Integer[5]; + + Map defaultEarlySec = new HashMap<>(); + defaultEarlySec.put(0,0); + defaultEarlySec.put(1, 1*60); + defaultEarlySec.put(2, 2*60); + defaultEarlySec.put(3, 2*60); + defaultEarlySec.put(4, 2*60); + + Map defaultLateSec = new HashMap<>(); + defaultLateSec.put(0, 2*60); + defaultLateSec.put(1, 2*60); + defaultLateSec.put(2, 3*60); + defaultLateSec.put(3, 3*60); + defaultLateSec.put(4, 4*60); + + String predictionType = request.getParameter("predictionType"); + + String routeIds[] = request.getParameterValues("r"); + String stopIds[]; + String stopIdParam = request.getParameter("s"); + // source can be "" (for all), "Transitime", or "Other"; + String source = request.getParameter("source"); + + if(stopIdParam != null){ + stopIds = stopIdParam.trim().split("\\s*,\\s*"); + if(stopIds.length > 0){ + stopIds[0] = stopIds[0].trim(); + } + } else { + stopIds = new String[0]; + } + + for (int i=0; i < allowableEarlyStr.length; i++) { + if(allowableEarlyStr[i] != null && !allowableEarlyStr[i].isEmpty() ) { + try { + allowableEarlySec[i] = (int) Double.parseDouble(allowableEarlyStr[i]) * Time.SEC_PER_MIN; + } catch (NumberFormatException e) { + response.sendError(416 /* Requested Range Not Satisfiable */, + "Could not parse Allowable Early value of " + allowableEarlyStr + "for bucket " + (i > 0 ? 5 * i : 1)); + return; + } + } else { + allowableEarlySec[i] = defaultEarlySec.get(i); + } + + if(allowableLateStr[i] != null && !allowableLateStr[i].isEmpty() ) { + try { + allowableLateSec[i] = (int) Double.parseDouble(allowableLateStr[i]) * Time.SEC_PER_MIN; + } catch (NumberFormatException e) { + response.sendError(416 /* Requested Range Not Satisfiable */, + "Could not parse Allowable Late value of " + allowableLateStr + "for bucket " + (i > 0 ? 5 * i : 1)); + return; + } + } else { + allowableLateSec[i] = defaultLateSec.get(i); + } + } + + if (agencyId == null || beginDate == null || numDays == null ) { + response.getWriter().write("For predAccuracyBucketData.jsp must " + + "specify parameters 'a' (agencyId=" + agencyId + "), 'beginDate' (" + beginDate + ") , " + + "and 'numDays' (" + numDays + ") + ."); + return; + } + + // Make sure not trying to get data for too long of a time span since + // that could bog down the database. + if (Integer.parseInt(numDays) > 31) { + throw new ParseException( + "Number of days of " + numDays + " spans more than a month", 0); + } + + try { + // Perform the query. + PredAccuracyFiveBucketQuery query = new PredAccuracyFiveBucketQuery(agencyId); + + // Convert results of query to a JSON string + String jsonString = query + .getJson(beginDate, numDays, beginTime, endTime, + routeIds, stopIds, source, predictionType, + allowableEarlySec, allowableLateSec); + + // If no data then return error status with an error message + if (jsonString == null || jsonString.isEmpty()) { + String message = "No data for beginDate=" + beginDate + + " numDays=" + numDays + + " beginTime=" + beginTime + + " endTime=" + endTime + + " source=" + source + + " allowableEarlyMsec=" + allowableEarlySec + + " allowableLateMsec=" + allowableLateSec; + response.sendError( + 416 /* Requested Range Not Satisfiable */, message); + return; + } + + // Respond with the JSON string + response.setHeader("Access-Control-Allow-Origin", "*"); + response.getWriter().write(jsonString); + } catch (java.sql.SQLException e) { + response.setStatus(400); + response.getWriter().write(e.getMessage()); } +%> \ No newline at end of file diff --git a/transitclockWebapp/src/main/webapp/reports/predAccuracyBucketParams.jsp b/transitclockWebapp/src/main/webapp/reports/predAccuracyBucketParams.jsp new file mode 100644 index 000000000..9e3e1c8ce --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/predAccuracyBucketParams.jsp @@ -0,0 +1,188 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1" %> + + + + <%@include file="/template/includes.jsp" %> + + Specify Parameters + + + + + + + + + +<%@include file="/template/header.jsp" %> +
    + Select Parameters for Prediction Accuracy Bucket Chart +
    + +
    +
    + <%-- For passing agency param to the report --%> + "/> + + + + + + + + +
    + + +
    + + + +
    +
    + 1 Minute Bucket +
    + + +
    +
    + + +
    +
    +
    + 5 Minute Bucket +
    + + +
    +
    + + + +
    +
    +
    + 10 Minute Bucket +
    + + +
    +
    + + + +
    +
    +
    + 15 Minute Bucket +
    + + +
    +
    + + + +
    +
    +
    + 20 Minute Bucket +
    + + +
    +
    + + +
    +
    +
    + + + +
    + + + \ No newline at end of file diff --git a/transitclockWebapp/src/main/webapp/reports/predAccuracyCsv.jsp b/transitclockWebapp/src/main/webapp/reports/predAccuracyCsv.jsp new file mode 100644 index 000000000..48519e3d8 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/predAccuracyCsv.jsp @@ -0,0 +1,109 @@ +<%@ page import="org.transitclock.db.GenericCsvQuery" %> +<%@ page import="org.transitclock.db.webstructs.WebAgency" %> +<%@ page import="org.transitclock.utils.Time" %> +<%@ page import="java.text.ParseException" %> +<%-- This file is for outputting prediction accuracy data in CSV format. + --%> +<% +// In order to make at least Windows based systems treat the file as +// a CSV file need to change the file name suffix to csv and also +// set the content type. This way the file can be loaded into Excel +// directory from the web browser. +response.setHeader("Content-Disposition", "filename=predAccuracy.csv"); +response.setContentType("application/csv"); + +// Parameters from request +String agencyId = request.getParameter("a"); +String beginDate = request.getParameter("beginDate"); +String numDays = request.getParameter("numDays"); +String beginTime = request.getParameter("beginTime"); +String endTime = request.getParameter("endTime"); +String routeId = request.getParameter("r"); + +if (agencyId == null || beginDate == null || numDays == null) { + response.getWriter().write("For predAccuracyCsv.jsp must " + + "specify parameters 'a' (agencyId), " + + "'beginDate', and 'numDays'."); + return; +} + +//Determine the time portion of the SQL +String timeSql = ""; +if ((beginTime != null && !beginTime.isEmpty()) + || (endTime != null && !endTime.isEmpty())) { + // If only begin or only end time set then use default value + if (beginTime == null || beginTime.isEmpty()) + beginTime = "00:00:00"; + if (endTime == null || endTime.isEmpty()) + endTime = "23:59:59"; + timeSql = " AND arrivalDepartureTime::time BETWEEN '" + + beginTime + "' AND '" + endTime + "' "; +} + +//Determine route portion of SQL. Default is to provide info for +//all routes. +String routeSql = ""; +if (routeId!=null && !routeId.trim().isEmpty()) { + routeSql = " AND routeShortName='" + routeId + "' "; +} + +// NOTE: this query only works on postgreSQL. For mySQL would need to change +// from using to_char(), not using casting like "::interger", don't put +// single quotes around "'1 day'", etc. + + + WebAgency agency = WebAgency.getCachedWebAgency(agencyId); + String dbtype = agency.getDbType(); + String sql = null; + if(dbtype.equals("mysql")){ + sql = "SELECT " + + " predictedTime-predictionReadTime as pred_length_secs, " + + " predictionAccuracyMsecs/1000 as accuracy_secs, " + + " predictedTime AS predicted_time," + + " arrivalDepartureTime AS actual_time," + + " predictionreadtime AS prediction_read_time," + + " predictionSource AS source, routeId AS route, " + + " directionId AS direction, tripId AS trip, " + + " stopId AS stop, vehicleId AS vehicle, " + + " affectedByWaitStop AS affected_by_wait_stop" + + " FROM PredictionAccuracy " + + "WHERE arrivalDepartureTime BETWEEN STR_TO_DATE('" + beginDate + "', '%Y-%m-%d') and DATE_ADD(STR_TO_DATE('" + beginDate + "', '%Y-%m-%d'),INTERVAL " + numDays + " DAY) " + + " AND predictedTime-predictionReadTime < (15 * 60) " + + routeSql + // Filter out MBTA_seconds source since it is isn't significantly different from MBTA_epoch. + // TODO should clean this up by not having MBTA_seconds source at all + // in the prediction accuracy module for MBTA. + + " AND predictionSource <> 'MBTA_seconds' "; + }else{ + sql = "SELECT " + + " to_char(predictedTime-predictionReadTime, 'SSSS')::integer as pred_length_secs, " + + " predictionAccuracyMsecs/1000 as accuracy_secs, " + + " predictedTime AS predicted_time," + + " arrivalDepartureTime AS actual_time," + + " predictionreadtime AS prediction_read_time," + + " predictionSource AS source, routeId AS route, " + + " directionId AS direction, tripId AS trip, " + + " stopId AS stop, vehicleId AS vehicle, " + + " affectedByWaitStop AS affected_by_wait_stop" + + " FROM predictionAccuracy " + + "WHERE arrivalDepartureTime BETWEEN cast(? as timestamp) " + + "AND cast(? as timestamp) + INTERVAL '" + numDays + " day' " + + timeSql + + " AND predictedTime-predictionReadTime < '00:15:00' " + + routeSql + // Filter out MBTA_seconds source since it is isn't significantly different from MBTA_epoch. + // TODO should clean this up by not having MBTA_seconds source at all + // in the prediction accuracy module for MBTA. + + " AND predictionSource <> 'MBTA_seconds' "; + } + +// Do the actual query +String csvStr; +if (dbtype.equals("mysql")) { + csvStr = GenericCsvQuery.getCsvString(agencyId, sql); +} else { + csvStr = GenericCsvQuery.getCsvString(agencyId, sql, Time.parseDate(beginDate), Time.parseDate(beginDate)); +} + +response.getWriter().write(csvStr); +%> diff --git a/transitclockWebapp/src/main/webapp/reports/predAccuracyCsvParams.jsp b/transitclockWebapp/src/main/webapp/reports/predAccuracyCsvParams.jsp new file mode 100644 index 000000000..70c03ecf6 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/predAccuracyCsvParams.jsp @@ -0,0 +1,40 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1" %> + + + + <%@include file="/template/includes.jsp" %> + + Specify Parameters + + + + + + + + + + +<%@include file="/template/header.jsp" %> + +
    + Select Parameters for Prediction Accuracy CSV Download +
    + +
    +
    + <%-- For passing agency param to the report --%> + "> + + + + + + + + +
    + + + \ No newline at end of file diff --git a/transitimeWebapp/src/main/webapp/reports/predAccuracyIntervalsChart.jsp b/transitclockWebapp/src/main/webapp/reports/predAccuracyIntervalsChart.jsp similarity index 95% rename from transitimeWebapp/src/main/webapp/reports/predAccuracyIntervalsChart.jsp rename to transitclockWebapp/src/main/webapp/reports/predAccuracyIntervalsChart.jsp index 38a6960fc..9a2320d1f 100644 --- a/transitimeWebapp/src/main/webapp/reports/predAccuracyIntervalsChart.jsp +++ b/transitclockWebapp/src/main/webapp/reports/predAccuracyIntervalsChart.jsp @@ -1,8 +1,8 @@ <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> -<%@ page import="org.transitime.utils.web.WebUtils" %> -<%@page import="org.transitime.db.webstructs.WebAgency"%> +<%@ page import="org.transitclock.utils.web.WebUtils" %> +<%@page import="org.transitclock.db.webstructs.WebAgency"%> <% // Determine all the parameters from the query string @@ -123,9 +123,9 @@ if ((beginTime != null && !beginTime.isEmpty()) || (endTime != null && !endTime. }, // When there is an AJAX problem alert the user error: function(request, status, error) { - //alert(error + '. ' + request.responseText); - $("#errorMessage").html(request.responseText + - "

    Hit back button to try other parameters."); + console.log(request.responseText) + var msg = $("

    ").html("
    No data for requested parameters. Hit back button to try other parameters.") + $("#errorMessage").append(msg); $("#errorMessage").fadeIn("slow"); }, }).responseJSON; diff --git a/transitimeWebapp/src/main/webapp/reports/predAccuracyIntervalsData.jsp b/transitclockWebapp/src/main/webapp/reports/predAccuracyIntervalsData.jsp similarity index 95% rename from transitimeWebapp/src/main/webapp/reports/predAccuracyIntervalsData.jsp rename to transitclockWebapp/src/main/webapp/reports/predAccuracyIntervalsData.jsp index ad64175c8..e45138e74 100644 --- a/transitimeWebapp/src/main/webapp/reports/predAccuracyIntervalsData.jsp +++ b/transitclockWebapp/src/main/webapp/reports/predAccuracyIntervalsData.jsp @@ -1,5 +1,5 @@ -<%@ page import="org.transitime.reports.PredictionAccuracyQuery.IntervalsType" %> -<%@ page import="org.transitime.reports.PredAccuracyIntervalQuery" %> +<%@ page import="org.transitclock.reports.PredictionAccuracyQuery.IntervalsType" %> +<%@ page import="org.transitclock.reports.PredAccuracyIntervalQuery" %> <%@ page import="java.util.Arrays" %> <%@ page import="java.text.ParseException" %> diff --git a/transitclockWebapp/src/main/webapp/reports/predAccuracyIntervalsParams.jsp b/transitclockWebapp/src/main/webapp/reports/predAccuracyIntervalsParams.jsp new file mode 100644 index 000000000..c7a2e43c6 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/predAccuracyIntervalsParams.jsp @@ -0,0 +1,100 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1"%> + + + + <%@include file="/template/includes.jsp" %> + + Specify Parameters + + + + + + + + + + +<%@include file="/template/header.jsp" %> + +

    + Select Parameters for Prediction Accuracy Intervals Chart +
    + +
    +
    + <%-- For passing agency param to the report --%> + "> + + + + + + + + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + + + +
    + + + \ No newline at end of file diff --git a/transitimeWebapp/src/main/webapp/reports/predAccuracyRangeChart.jsp b/transitclockWebapp/src/main/webapp/reports/predAccuracyRangeChart.jsp similarity index 79% rename from transitimeWebapp/src/main/webapp/reports/predAccuracyRangeChart.jsp rename to transitclockWebapp/src/main/webapp/reports/predAccuracyRangeChart.jsp index c048b35b5..14e3dc009 100644 --- a/transitimeWebapp/src/main/webapp/reports/predAccuracyRangeChart.jsp +++ b/transitclockWebapp/src/main/webapp/reports/predAccuracyRangeChart.jsp @@ -1,7 +1,8 @@ <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> -<%@ page import="org.transitime.utils.web.WebUtils" %> -<%@page import="org.transitime.db.webstructs.WebAgency"%> +<%@ page import="org.transitclock.utils.web.WebUtils" %> +<%@page import="org.transitclock.db.webstructs.WebAgency"%> +<%@ page import="org.apache.commons.lang3.StringUtils" %> <% // Determine all the parameters from the query string @@ -27,7 +28,8 @@ String sourceParam = request.getParameter("source"); String source = (sourceParam != null && !sourceParam.isEmpty()) ? ", " + sourceParam + " predictions" : ""; String beginDate = request.getParameter("beginDate"); -String numDays = request.getParameter("numDays"); +String numDays = StringUtils.isBlank(request.getParameter("numDays")) ? "1" : request.getParameter("numDays"); +if (numDays == null) numDays = "1"; String beginTime = request.getParameter("beginTime"); String endTime = request.getParameter("endTime"); @@ -64,6 +66,13 @@ if ((beginTime != null && !beginTime.isEmpty()) || (endTime != null && !endTime. z-index: 9999; background: url('images/page-loader.gif') 50% 50% no-repeat rgb(249,249,249); } + + #summary { + font-family: arial, sans-serif; + width: 100%; + text-align: center; + margin-top: 1%; + } #errorMessage { display: none; @@ -86,6 +95,7 @@ if ((beginTime != null && !beginTime.isEmpty()) || (endTime != null && !endTime.
    +
    Schedule Adherence loading....
    @@ -119,9 +129,9 @@ function getDataTable() { }, // When there is an AJAX problem alert the user error: function(request, status, error) { - //alert(error + '. ' + request.responseText); - $("#errorMessage").html(request.responseText + - "

    Hit back button to try other parameters."); + console.log(request.responseText) + var msg = $("

    ").html("
    No data for requested parameters. Hit back button to try other parameters.") + $("#errorMessage").append(msg); $("#errorMessage").fadeIn("slow"); }, }).responseJSON; @@ -145,9 +155,9 @@ function drawChart() { hAxis: { title: 'Prediction Length (minutes)', // So that last column is labeled - maxValue: 15, + maxValue: 20, // Want a gridline for every minute, not just the default of 5 gridlines - gridlines: {count: 16}, + gridlines: {count: 21}, // Nice to show a faint line for every 30 seconds as well minorGridlines: {count: 1} }, @@ -172,10 +182,36 @@ function drawChart() { chart.draw(globalDataTable, chartOptions); } +function parseSummary(data) { + var results = []; + data.forEach(function(d) { + results.push(d); + }); + document.getElementById('summary').innerHTML = "Schedule Adherence over " + results[0] + " arrival and departures
    " + + "Early: " + results[1] + + "% OnTime: " + results[2] + + "% Late: " + results[3] + "%"; + +} + +function formatQueryParams() { + return "<%= WebUtils.getQueryParamsString(request) %>"; +} + +function showSummary() { + $("#summary").show(); + $.get("data/summaryScheduleAdherence.jsp?"+formatQueryParams(), parseSummary) + .fail(function() { + document.getElementById('summary').innerHTML ="Error!"; + }); +} + function getDataAndDrawChart() { getDataTable(); - if (globalDataTable != null) + if (globalDataTable != null) { drawChart(); + showSummary(); + } // Now that chart has been drawn faceout the loading image $("#loading").fadeOut("slow"); @@ -186,4 +222,4 @@ function getDataAndDrawChart() { google.load("visualization", "1", {packages:["corechart"]}); google.setOnLoadCallback(getDataAndDrawChart); - \ No newline at end of file + diff --git a/transitimeWebapp/src/main/webapp/reports/predAccuracyRangeData.jsp b/transitclockWebapp/src/main/webapp/reports/predAccuracyRangeData.jsp similarity index 92% rename from transitimeWebapp/src/main/webapp/reports/predAccuracyRangeData.jsp rename to transitclockWebapp/src/main/webapp/reports/predAccuracyRangeData.jsp index 84aea5520..1d2430472 100644 --- a/transitimeWebapp/src/main/webapp/reports/predAccuracyRangeData.jsp +++ b/transitclockWebapp/src/main/webapp/reports/predAccuracyRangeData.jsp @@ -1,5 +1,5 @@ -<%@ page import="org.transitime.reports.PredAccuracyRangeQuery" %> -<%@ page import="org.transitime.utils.Time" %> +<%@ page import="org.transitclock.reports.PredAccuracyRangeQuery" %> +<%@ page import="org.transitclock.utils.Time" %> <%@ page import="java.util.Arrays" %> <%@ page import="java.text.ParseException" %> @@ -44,8 +44,8 @@ if (agencyId == null || beginDate == null || numDays == null ) { response.getWriter().write("For predAccuracyRangeData.jsp must " - + "specify parameters 'a' (agencyId), 'beginDate', " - + "and 'numDays'."); + + "specify parameters 'a' (agencyId=" + agencyId + "), 'beginDate' (" + beginDate + ") , " + + "and 'numDays' (" + numDays + ") + ."); return; } diff --git a/transitclockWebapp/src/main/webapp/reports/predAccuracyRangeParams.jsp b/transitclockWebapp/src/main/webapp/reports/predAccuracyRangeParams.jsp new file mode 100644 index 000000000..94d697e37 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/predAccuracyRangeParams.jsp @@ -0,0 +1,79 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1"%> + + + + <%@include file="/template/includes.jsp" %> + + Specify Parameters + + + + + + + + + +<%@include file="/template/header.jsp" %> +

    + Select Parameters for Prediction Accuracy Range Chart +
    + +
    +
    + <%-- For passing agency param to the report --%> + "> + + + + + + + + +
    + + +
    + +
    + + +
    + +
    + + +
    + + + +
    + + + \ No newline at end of file diff --git a/transitimeWebapp/src/main/webapp/reports/predAccuracyScatterChart.jsp b/transitclockWebapp/src/main/webapp/reports/predAccuracyScatterChart.jsp similarity index 87% rename from transitimeWebapp/src/main/webapp/reports/predAccuracyScatterChart.jsp rename to transitclockWebapp/src/main/webapp/reports/predAccuracyScatterChart.jsp index b5d71afaa..de74d39c7 100644 --- a/transitimeWebapp/src/main/webapp/reports/predAccuracyScatterChart.jsp +++ b/transitclockWebapp/src/main/webapp/reports/predAccuracyScatterChart.jsp @@ -1,5 +1,5 @@ -<%@ page import="org.transitime.utils.web.WebUtils" %> -<%@page import="org.transitime.db.webstructs.WebAgency"%> +<%@ page import="org.transitclock.utils.web.WebUtils" %> +<%@page import="org.transitclock.db.webstructs.WebAgency"%> <% // Create title for chart String agencyId = request.getParameter("a"); @@ -109,8 +109,9 @@ if ((beginTime != null && !beginTime.isEmpty()) || (endTime != null && !endTime. globalDataTable = new google.visualization.DataTable(jsonData); }, error: function(request, status, error) { - $("#errorMessage").html(request.responseText + - "

    Hit back button to try other parameters.") + console.log(request.responseText) + var msg = $("

    ").html("
    No data for requested parameters. Hit back button to try other parameters.") + $("#errorMessage").append(msg); $("#errorMessage").fadeIn("slow"); }, }).responseJSON; @@ -125,9 +126,9 @@ if ((beginTime != null && !beginTime.isEmpty()) || (endTime != null && !endTime. // Could use html tooltips so can format them but for now using regular ones // FIXME tooltip: {isHtml: false}, hAxis: { - title: 'Prediction Time (secs)', + title: 'Prediction Time (minutes)', minValue: 0, - maxValue: 900, + maxValue: 1200, ticks: [ {v:60, f:'1'}, {v:120, f:'2'}, @@ -143,7 +144,12 @@ if ((beginTime != null && !beginTime.isEmpty()) || (endTime != null && !endTime. {v:720, f:'12'}, {v:780, f:'13'}, {v:840, f:'14'}, - {v:900, f:'15'}] + {v:900, f:'15'}, + {v:960, f:'16'}, + {v:1020, f:'17'}, + {v:1080, f:'18'}, + {v:1140, f:'19'}, + {v:1200, f:'20'}] }, vAxis: {title: 'Prediction Accuracy (secs) (postive means vehicle later than predicted)', // Try to show accuracy on a consistent vertical axis and @@ -165,15 +171,18 @@ if ((beginTime != null && !beginTime.isEmpty()) || (endTime != null && !endTime. series: [{'color': '<%= (source==null || !source.equals("Other")) ? "blue" : "red" %>'},{'color': 'red'}], legend: 'none', // Use small points since have lots of them - pointSize: 2, - // Draw a trendline for data series 0 - //trendlines: { - // 0: { - // color: 'purple', - // lineWidth: 5, - // opacity: 0.5, - // } - //}, + pointSize: 1, + dataOpacity: 0.4, + + // Draw a trendline for data series 0 + trendlines: { + 0: { + color: 'red', + lineWidth: 3, + visibleInLegend: true, + opacity: 1, + } + }, // Need to not use 100% or else labels won't appear chartArea: {width:'90%', height:'80%', backgroundColor: '#f2f2f2'}, // Allow zooming diff --git a/transitclockWebapp/src/main/webapp/reports/predAccuracyScatterData.jsp b/transitclockWebapp/src/main/webapp/reports/predAccuracyScatterData.jsp new file mode 100644 index 000000000..56d67f6f3 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/predAccuracyScatterData.jsp @@ -0,0 +1,173 @@ +<%@ page import="org.transitclock.db.webstructs.WebAgency" %> +<%@ page import="org.transitclock.reports.ChartGenericJsonQuery" %> +<%@ page import="org.transitclock.utils.Time" %> +<%@ page import="java.text.ParseException" %> +<%@ page import="org.transitclock.reports.SqlUtils" %> +<% + // Parameters from request +String agencyId = request.getParameter("a"); +String beginDate = request.getParameter("beginDate"); +String numDays = request.getParameter("numDays"); +String beginTime = request.getParameter("beginTime"); +String endTime = request.getParameter("endTime"); +String routeId = request.getParameter("r"); +String source = request.getParameter("source"); +String predictionType = request.getParameter("predictionType"); + +WebAgency agency = WebAgency.getCachedWebAgency(agencyId); +String dbtype = agency.getDbType(); +boolean isMysql = "mysql".equals(dbtype); + +boolean showTooltips = false; +String showTooltipsStr = request.getParameter("tooltips"); +if (showTooltipsStr != null && showTooltipsStr.toLowerCase().equals("true")) + showTooltips = true; + +if (agencyId == null || beginDate == null || numDays == null) { + response.getWriter().write("For predAccuracyScatterData.jsp must " + + "specify parameters 'a' (agencyId), " + + "'beginDate', and 'numDays'."); + return; +} + +// Make sure not trying to get data for too long of a time span since +// that could bog down the database. +if (Integer.parseInt(numDays) > 31) { + throw new ParseException( + "Number of days of " + numDays + " spans more than a month", 0); +} + +// Determine the time portion of the SQL + String timeSql = ""; + if ((beginTime != null && !beginTime.isEmpty()) + || (endTime != null && !endTime.isEmpty())) { + // If only begin or only end time set then use default value + if (beginTime == null || beginTime.isEmpty()) + beginTime = "00:00:00"; + if (endTime == null || endTime.isEmpty()) + endTime = "23:59:59"; + + timeSql = SqlUtils.timeRangeClause(request, "arrivalDepatureTime", Integer.parseInt(numDays)); + } + +// Determine route portion of SQL. Default is to provide info for +// all routes. + String routeSql = ""; + if (routeId!=null && !routeId.trim().isEmpty()) { + routeSql = String.format(" AND (routeId='%1$s' OR routeShortName='%1$s')", routeId); + } + +// Determine the source portion of the SQL. Default is to provide +// predictions for all sources + String sourceSql = ""; + if (source != null && !source.isEmpty()) { + if (source.equals("TransitClock")) { + // Only "Transitime" predictions + sourceSql = " AND predictionSource='TransitClock'"; + } else { + // Anything but "Transitime" + sourceSql = " AND predictionSource<>'TransitClock'"; + } + } + +// Determine SQL for prediction type () + String predTypeSql = ""; + if (predictionType != null && !predictionType.isEmpty()) { + if (source.equals("AffectedByWaitStop")) { + // Only "AffectedByLayover" predictions + predTypeSql = " AND affectedByWaitStop = true "; + } else { + // Only "NotAffectedByLayover" predictions + predTypeSql = " AND affectedByWaitStop = false "; + } + } + + String predLengthSql = " to_char(predictedTime-predictionReadTime, 'SSSS')::integer"; + String predAccuracySql = " predictionAccuracyMsecs/1000 "; + if (isMysql) { + predLengthSql = "TIMESTAMPDIFF(SECOND,predictionReadTime,predictedTime)"; + predAccuracySql = "CAST(predictionAccuracyMsecs/1000 AS DECIMAL)"; + } + + String tooltipsSql = ""; + if (showTooltips) { + if (isMysql) { + tooltipsSql = ", CONCAT(" + + "\'time=\'," + + "predictionReadTime" + + ",\'\\n\'" + + ",\'stopId=\'," + + "stopId" + + ",\'\\n\'" + + ",\'routeId=\'," + + "routeId" + + ",\'\\n\'" + + ",\'vehicleId=\'," + + "vehicleId" + + ") as tooltip"; + + } else { + tooltipsSql = + ", format(E'predAccuracy=%s\\n" + + "prediction=%s\\n" + + "stopId=%s\\n" + + "routeId=%s\\n" + + "tripId=%s\\n" + + "arrDepTime=%s\\n" + + "predTime=%s\\n" + + "predReadTime=%s\\n" + + "vehicleId=%s\\n" + + "source=%s\\n" + + "affectedByLayover=%s', " + + " CAST(predictionAccuracyMsecs || ' msec' AS INTERVAL), predictedTime-predictionReadTime," + + " stopId, routeId, tripId, " + + " to_char(arrivalDepartureTime, 'HH24:MI:SS.MS MM/DD/YYYY')," + + " to_char(predictedTime, 'HH24:MI:SS.MS')," + + " to_char(predictionReadTime, 'HH24:MI:SS.MS')," + + " vehicleId," + + " predictionSource," + + " CASE WHEN affectedbywaitstop THEN 'True' ELSE 'False' END) AS tooltip "; + } + } + + String sql = "SELECT " + + predLengthSql + " as predLength, " + + predAccuracySql + " as predAccuracy " + + tooltipsSql + + " FROM PredictionAccuracy " + + "WHERE " + + "1=1 " + + SqlUtils.timeRangeClause(request, "arrivalDepartureTime", 30) + + " AND "+predLengthSql+" <= 1200 " + + routeSql + + sourceSql + + predTypeSql + // Filter out MBTA_seconds source since it is isn't significantly different from MBTA_epoch. + // TODO should clean this up by not having MBTA_seconds source at all + // in the prediction accuracy module for MBTA. + + " AND predictionSource <> 'MBTA_seconds' "; + + + + // Determine the json data by running the query + String jsonString = ChartGenericJsonQuery.getJsonString(agencyId, sql); + + +// If no data then return error status with an error message +if (jsonString == null || jsonString.isEmpty()) { + String message = "No data for beginDate=" + beginDate + + " numDays=" + numDays + + " beginTime=" + beginTime + + " endTime=" + endTime + + " routeId=" + routeId + + " source=" + source + + " predictionType=" + predictionType; + response.setStatus(400); + response.getWriter().write(message); + return; +} + +// Return the JSON data +response.setHeader("Access-Control-Allow-Origin", "*"); +response.getWriter().write(jsonString); +%> diff --git a/transitclockWebapp/src/main/webapp/reports/predAccuracyScatterParams.jsp b/transitclockWebapp/src/main/webapp/reports/predAccuracyScatterParams.jsp new file mode 100644 index 000000000..9095fd909 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/predAccuracyScatterParams.jsp @@ -0,0 +1,65 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1"%> + + + + <%@include file="/template/includes.jsp" %> + + Specify Parameters + + + + + + + + + +<%@include file="/template/header.jsp" %> + +

    + Select Parameters for Prediction Accuracy Scatter Chart +
    + +
    +
    + <%-- For passing agency param to the report --%> + "> + + + + + + + + + + + + + + + +
    + + +
    + + + +
    + + + diff --git a/transitclockWebapp/src/main/webapp/reports/realTimeDispatcher.jsp b/transitclockWebapp/src/main/webapp/reports/realTimeDispatcher.jsp new file mode 100644 index 000000000..ce3ca8fd9 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/realTimeDispatcher.jsp @@ -0,0 +1,153 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1"%> + +<%@page import="org.transitclock.web.WebConfigParams"%> + + + <%@include file="/template/includes.jsp" %> + + Real-time Operations + + + + + + + + + + + <%@include file="/template/header.jsp" %> +
    + +
    + +
    + + + + + + + + + + + + + + + +
    VehicleLast ReportHeadingSpeedRoute AssignmentSched AdherenceOperator IDView on Map
    +
    +
    + + + + \ No newline at end of file diff --git a/transitclockWebapp/src/main/webapp/reports/realTimeLiveMap.jsp b/transitclockWebapp/src/main/webapp/reports/realTimeLiveMap.jsp new file mode 100644 index 000000000..9009a5017 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/realTimeLiveMap.jsp @@ -0,0 +1,1154 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1"%> + +<%@page import="org.transitclock.web.WebConfigParams"%> + + + <%@include file="/template/includes.jsp" %> + + Real-time Operations + + + + + + + + + + + + + + + + + + <%-- --%> + + + TransitClock Map + + + <%@include file="/template/header.jsp" %> +
    +
    + Live Map +
    + +
    + <%-- For passing agency param to the report --%> + "> + + + +
    + +
    + +
    +
    +
    + + + \ No newline at end of file diff --git a/transitclockWebapp/src/main/webapp/reports/realTimeScheduleAdherence.jsp b/transitclockWebapp/src/main/webapp/reports/realTimeScheduleAdherence.jsp new file mode 100644 index 000000000..fdfe01495 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/realTimeScheduleAdherence.jsp @@ -0,0 +1,374 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1" %> + +<%@page import="org.transitclock.web.WebConfigParams" %> + + + <%@include file="/template/includes.jsp" %> + + Real-time Operations + + + + + + + + + + + + + + <%-- --%> + + + + + + +<%@include file="/template/header.jsp" %> +
    +
    + Schedule Adherence +
    + +
    + <%-- For passing agency param to the report --%> + "> + + + + +
    + +
    + +
    +
    +
    + + \ No newline at end of file diff --git a/transitclockWebapp/src/main/webapp/reports/routePerformanceData.jsp b/transitclockWebapp/src/main/webapp/reports/routePerformanceData.jsp new file mode 100644 index 000000000..65541333c --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/routePerformanceData.jsp @@ -0,0 +1,39 @@ +<%@ page import="org.transitclock.reports.RoutePerformanceQuery" %> +<%@ page import="java.util.List" %> +<%@ page import="java.util.Date" %> +<%@ page import="java.text.SimpleDateFormat" %> +<%@ page import="org.json.JSONArray" %> +<%@ page import="org.apache.commons.lang3.StringUtils" %> + +<%@ page contentType="application/json" %> +<% + +String agency = request.getParameter("a"); +String beginDateStr = request.getParameter("beginDate"); +String numDaysStr = request.getParameter("numDays"); +String beginTimeStr = request.getParameter("beginTime"); +String endTimeStr = request.getParameter("endTime"); +double allowableEarly = Double.parseDouble(request.getParameter("allowableEarly")); +double allowableLate = Double.parseDouble(request.getParameter("allowableLate")); +String predictionType = request.getParameter("predictionType"); +String predictionSource = request.getParameter("source"); + +if (StringUtils.isEmpty(beginTimeStr)) + beginTimeStr = "00:00:00"; +else + beginTimeStr += ":00"; +if (StringUtils.isEmpty(endTimeStr)) + endTimeStr = "23:59:59"; +else + endTimeStr += ":00"; + +SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); +Date beginDate = dateFormat.parse(beginDateStr + " " + beginTimeStr); +int numDays = Integer.parseInt(numDaysStr); + +List results = new RoutePerformanceQuery().query(agency, beginDate, numDays, allowableEarly, allowableLate, predictionType, predictionSource); + +JSONArray json = new JSONArray(results); +json.write(out); + +%> \ No newline at end of file diff --git a/transitclockWebapp/src/main/webapp/reports/routePerformanceTable.jsp b/transitclockWebapp/src/main/webapp/reports/routePerformanceTable.jsp new file mode 100644 index 000000000..b2c3f7a39 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/routePerformanceTable.jsp @@ -0,0 +1,89 @@ +<%@ page language="java" contentType="text/html; charset=UTF-8" + pageEncoding="UTF-8"%> +<%@ page import="org.transitclock.utils.web.WebUtils" %> + + + +<%@include file="/template/includes.jsp" %> + + +Route Performance Report + + + + +<%@include file="/template/header.jsp" %> + +
    + +
    Route Performance Table
    + + + +
    + +
    + +
    + +
    + + + + + + + + + \ No newline at end of file diff --git a/transitclockWebapp/src/main/webapp/reports/routeScheduleAdherence.jsp b/transitclockWebapp/src/main/webapp/reports/routeScheduleAdherence.jsp new file mode 100644 index 000000000..59c218a9b --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/routeScheduleAdherence.jsp @@ -0,0 +1,95 @@ +<%@ page language="java" contentType="text/html; charset=UTF-8" + pageEncoding="UTF-8"%> +<%@ page import="org.transitclock.utils.web.WebUtils" %> + + + +<%@include file="/template/includes.jsp" %> + + + + +Route Schedule Adherence + + + + +<%@include file="/template/header.jsp" %> + + + + +
    + +
    Route Schedule Adherence
    + + + +
    + +
    + +
    + +
    +
    +This box plot shows the distribution of schedule adherence data across routes. +For each route, the maximum and minimum schedule adherences are horizontal grey +lines. The median, first quartile (middle value between the minimum and median), +and third quartile (middle value between the median and maximum) are horizontal +colored lines. 50% of a route's data points lie within the colored box. +
    + + + + + + + + + + \ No newline at end of file diff --git a/transitclockWebapp/src/main/webapp/reports/runTime.jsp b/transitclockWebapp/src/main/webapp/reports/runTime.jsp new file mode 100644 index 000000000..f2010a28f --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/runTime.jsp @@ -0,0 +1,1576 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1" %> + + + + <%@include file="/template/includes.jsp" %> + + Run Time Analysis + + + + + + + + + + + + + + + + +<%@include file="/template/header.jsp" %> +
    +
    +
    +
    + Run Time Analysis +
    + +
    + <%-- For passing agency param to the report --%> + "> + + + +
    + + +
    + +
    + + +
    + + + + + + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    +
    +
    + +
    +
    +
    + + +
    +
    +

    +
    +
    + +
    + + +
    +
    + + + + +
    +

    Summary

    + + + + + + + + + +
    AverageFixedVariableDwell
    +
    + + +
    + + + + + + + +
    +
    + +
    +
    + <%--

    Route Breakdown

    --%> + +
    +
    +
    +
    + +
    +
    +

    Summary

    +
    +

    Trip Breakdown

    + + +
    +
    + +
    + Distribution Tab +
    + +
    + +
    +
    + + + + \ No newline at end of file diff --git a/transitimeWebapp/src/main/webapp/reports/schAdhByRouteChart.jsp b/transitclockWebapp/src/main/webapp/reports/schAdhByRouteChart.jsp similarity index 92% rename from transitimeWebapp/src/main/webapp/reports/schAdhByRouteChart.jsp rename to transitclockWebapp/src/main/webapp/reports/schAdhByRouteChart.jsp index cf5e8b0c1..c435d324b 100644 --- a/transitimeWebapp/src/main/webapp/reports/schAdhByRouteChart.jsp +++ b/transitclockWebapp/src/main/webapp/reports/schAdhByRouteChart.jsp @@ -1,6 +1,6 @@ <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> -<%@ page import="org.transitime.utils.web.WebUtils" %> +<%@ page import="org.transitclock.utils.web.WebUtils" %> @@ -50,12 +50,12 @@ <%@include file="/template/header.jsp" %> <% + String dateRange = request.getParameter("beginDate") + " (+" + request.getParameter("numDays") + " days)"; String allowableEarly = request.getParameter("allowableEarly");; String allowableLate = request.getParameter("allowableLate");; String chartSubtitle = allowableEarly + " min early to " - + allowableLate + " min late
    " - + request.getParameter("dateRange"); - + + allowableLate + " min late
    " + dateRange; + String beginTime = request.getParameter("beginTime"); String endTime = request.getParameter("endTime"); if (!beginTime.isEmpty() || !endTime.isEmpty()) { @@ -220,9 +220,9 @@ var globalNumberOfRoutes; success: createDataTableAndDrawChart, // When there is an AJAX problem alert the user error: function(request, status, error) { - //alert(error + '. ' + request.responseText); - $("#errorMessage").html(request.responseText + - "

    Hit back button to try other parameters."); + console.log(request.responseText) + var msg = $("

    ").html("
    No data for requested parameters. Hit back button to try other parameters.") + $("#errorMessage").append(msg);; $("#errorMessage").fadeIn("fast"); $("#loading").fadeOut("slow"); }, diff --git a/transitimeWebapp/src/main/webapp/reports/schAdhByRouteData.jsp b/transitclockWebapp/src/main/webapp/reports/schAdhByRouteData.jsp similarity index 69% rename from transitimeWebapp/src/main/webapp/reports/schAdhByRouteData.jsp rename to transitclockWebapp/src/main/webapp/reports/schAdhByRouteData.jsp index 286b37563..fed969309 100644 --- a/transitimeWebapp/src/main/webapp/reports/schAdhByRouteData.jsp +++ b/transitclockWebapp/src/main/webapp/reports/schAdhByRouteData.jsp @@ -15,10 +15,22 @@ --%> <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> -<%@ page import="org.transitime.reports.GenericJsonQuery" %> -<%@ page import="org.transitime.reports.SqlUtils" %> +<%@ page import="org.transitclock.reports.GenericJsonQuery" %> +<%@ page import="org.transitclock.reports.SqlUtils" %> +<%@ page import="org.transitclock.db.webstructs.WebAgency" %> <% -try { +try { + +String agencyId = request.getParameter("a"); +WebAgency agency = WebAgency.getCachedWebAgency(agencyId); +String dbtype = agency.getDbType(); +boolean isMysql = "mysql".equals(dbtype); + +String epochCommandPre = ""; +if (isMysql) { + epochCommandPre = "UNIX_TIMESTAMP"; +} + String allowableEarlyStr = request.getParameter("allowableEarly"); if (allowableEarlyStr == null || allowableEarlyStr.isEmpty()) allowableEarlyStr = "1.0"; @@ -28,33 +40,32 @@ String allowableLateStr = request.getParameter("allowableLate"); if (allowableLateStr == null || allowableLateStr.isEmpty()) allowableLateStr = "4.0"; String allowableLateMinutesStr = "'" + SqlUtils.convertMinutesToSecs(allowableLateStr) + " seconds'"; - + String sql = "SELECT " - + " COUNT(CASE WHEN scheduledtime-time > " + allowableEarlyMinutesStr + " THEN 1 ELSE null END) as early, \n" - + " COUNT(CASE WHEN scheduledtime-time <= " + allowableEarlyMinutesStr + " AND time-scheduledtime <= " + + " COUNT(CASE WHEN " + epochCommandPre + "(scheduledTime-time) > " + allowableEarlyMinutesStr + " THEN 1 ELSE null END) as early, \n" + + " COUNT(CASE WHEN " + epochCommandPre + "(scheduledTime-time) <= " + allowableEarlyMinutesStr + " AND " + epochCommandPre + "(time-scheduledTime) <= " + allowableLateMinutesStr + " THEN 1 ELSE null END) AS ontime, \n" - + " COUNT(CASE WHEN time-scheduledtime > " + allowableLateMinutesStr + " THEN 1 ELSE null END) AS late, \n" + + " COUNT(CASE WHEN " + epochCommandPre + "(time-scheduledTime) > " + allowableLateMinutesStr + " THEN 1 ELSE null END) AS late, \n" + " COUNT(*) AS total, \n" + " r.name \n" - + "FROM arrivalsdepartures ad, routes r \n" + + "FROM ArrivalsDepartures ad, Routes r \n" + "WHERE " // For joining in route table to get route name + "ad.configrev = r.configrev \n" - + " AND ad.routeshortname = r.shortname \n" + + " AND ad.routeShortName = r.shortName \n" // Only need arrivals/departures that have a schedule time - + " AND ad.scheduledtime IS NOT NULL \n" + + " AND ad.scheduledTime IS NOT NULL \n" // Specifies which routes to provide data for + SqlUtils.routeClause(request, "ad") + "\n" + SqlUtils.timeRangeClause(request, "ad.time", 7) + "\n" // Grouping needed since want to output route name - + " GROUP BY r.name, r.routeorder ORDER BY r.routeorder, r.name;"; + + " GROUP BY r.name, r.routeOrder ORDER BY r.routeOrder, r.name;"; // Just for debugging System.out.println("\nFor schedule adherence by route query sql=\n" + sql); // Do the query and return result in JSON format -String agencyId = request.getParameter("a"); String jsonString = GenericJsonQuery.getJsonString(agencyId, sql); response.setContentType("application/json"); response.setHeader("Access-Control-Allow-Origin", "*"); diff --git a/transitclockWebapp/src/main/webapp/reports/schAdhByRouteParams.jsp b/transitclockWebapp/src/main/webapp/reports/schAdhByRouteParams.jsp new file mode 100644 index 000000000..0b5b044a2 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/schAdhByRouteParams.jsp @@ -0,0 +1,61 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1" %> + + + + <%@include file="/template/includes.jsp" %> + + Specify Parameters + + + + + + + + + +<%@include file="/template/header.jsp" %> +

    + Select Parameters for Schedule Adherence by Route Chart +
    + +
    +
    + <%-- For passing agency param to the report --%> + "> + + + + + +
    + + +
    + +
    + + +
    + + + +
    + + + \ No newline at end of file diff --git a/transitimeWebapp/src/main/webapp/reports/schAdhByStopChart.jsp b/transitclockWebapp/src/main/webapp/reports/schAdhByStopChart.jsp similarity index 93% rename from transitimeWebapp/src/main/webapp/reports/schAdhByStopChart.jsp rename to transitclockWebapp/src/main/webapp/reports/schAdhByStopChart.jsp index 207d09bdf..ec8693bf8 100644 --- a/transitimeWebapp/src/main/webapp/reports/schAdhByStopChart.jsp +++ b/transitclockWebapp/src/main/webapp/reports/schAdhByStopChart.jsp @@ -1,6 +1,6 @@ <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> -<%@ page import="org.transitime.utils.web.WebUtils" %> +<%@ page import="org.transitclock.utils.web.WebUtils" %> @@ -226,11 +226,12 @@ var routeName = routeData.routes[0].name; <% + String dateRange = request.getParameter("beginDate") + " (+" + request.getParameter("numDays") + " days)"; String allowableEarly = request.getParameter("allowableEarly");; String allowableLate = request.getParameter("allowableLate");; String chartParams = allowableEarly + " min early to " + allowableLate + " min late
    " - + request.getParameter("dateRange"); + + dateRange; String beginTime = request.getParameter("beginTime"); String endTime = request.getParameter("endTime"); @@ -264,9 +265,9 @@ success: createDataTablesAndDrawCharts, // When there is an AJAX problem alert the user error: function(request, status, error) { - //alert(error + '. ' + request.responseText); - $("#errorMessage").html(request.responseText + - "

    Hit back button to try other parameters."); + console.log(request.responseText) + var msg = $("

    ").html("
    No data for requested parameters. Hit back button to try other parameters.") + $("#errorMessage").append(msg); $("#errorMessage").fadeIn("fast"); $("#loading").fadeOut("slow"); }, diff --git a/transitimeWebapp/src/main/webapp/reports/schAdhByStopData.jsp b/transitclockWebapp/src/main/webapp/reports/schAdhByStopData.jsp similarity index 82% rename from transitimeWebapp/src/main/webapp/reports/schAdhByStopData.jsp rename to transitclockWebapp/src/main/webapp/reports/schAdhByStopData.jsp index 7ea5e6569..056167dc8 100644 --- a/transitimeWebapp/src/main/webapp/reports/schAdhByStopData.jsp +++ b/transitclockWebapp/src/main/webapp/reports/schAdhByStopData.jsp @@ -15,8 +15,8 @@ --%> <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> -<%@ page import="org.transitime.reports.GenericJsonQuery" %> -<%@ page import="org.transitime.reports.SqlUtils" %> +<%@ page import="org.transitclock.reports.GenericJsonQuery" %> +<%@ page import="org.transitclock.reports.SqlUtils" %> <% try { String allowableEarlyStr = request.getParameter("allowableEarly"); @@ -31,20 +31,20 @@ String allowableLateMinutesStr = "'" + SqlUtils.convertMinutesToSecs(allowableLa String sql = "SELECT " - + " COUNT(CASE WHEN scheduledtime-time > " + allowableEarlyMinutesStr + " THEN 1 ELSE null END) as early, \n" - + " COUNT(CASE WHEN scheduledtime-time <= " + allowableEarlyMinutesStr + " AND time-scheduledtime <= " + + " COUNT(CASE WHEN scheduledTime-time > " + allowableEarlyMinutesStr + " THEN 1 ELSE null END) as early, \n" + + " COUNT(CASE WHEN scheduledTime-time <= " + allowableEarlyMinutesStr + " AND time-scheduledTime <= " + allowableLateMinutesStr + " THEN 1 ELSE null END) AS ontime, \n" - + " COUNT(CASE WHEN time-scheduledtime > " + allowableLateMinutesStr + " THEN 1 ELSE null END) AS late, \n" + + " COUNT(CASE WHEN time-scheduledTime > " + allowableLateMinutesStr + " THEN 1 ELSE null END) AS late, \n" + " COUNT(*) AS total, \n" + " s.name AS stop_name, \n" + " ad.directionid AS direction_id \n" - + "FROM arrivalsdepartures ad, stops s \n" + + "FROM ArrivalsDepartures ad, Stops s \n" + "WHERE " // To get stop name - + " ad.configrev = s.configrev \n" - + " AND ad.stopid = s.id \n" + + " ad.configRev = s.configRev \n" + + " AND ad.stopId = s.id \n" // Only need arrivals/departures that have a schedule time - + " AND ad.scheduledtime IS NOT NULL \n" + + " AND ad.scheduledTime IS NOT NULL \n" // Specifies which routes to provide data for + SqlUtils.routeClause(request, "ad") + "\n" + SqlUtils.timeRangeClause(request, "ad.time", 7) + "\n" @@ -57,8 +57,8 @@ String sql = // need to order by direction id and stop order, but also the stop name // as a backup for if stoporder not defined for data and is therefore // always the same and doesn't provide any ordering info. - + " GROUP BY directionid, s.name, ad.stoporder \n" - + " ORDER BY directionid, ad.stoporder, s.name"; + + " GROUP BY directionid, s.name, ad.stopOrder \n" + + " ORDER BY directionid, ad.stopOrder, s.name"; // Just for debugging System.out.println("\nFor schedule adherence by stop query sql=\n" + sql); diff --git a/transitclockWebapp/src/main/webapp/reports/schAdhByStopParams.jsp b/transitclockWebapp/src/main/webapp/reports/schAdhByStopParams.jsp new file mode 100644 index 000000000..e01c4d168 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/schAdhByStopParams.jsp @@ -0,0 +1,61 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1"%> + + + + <%@include file="/template/includes.jsp" %> + + Specify Parameters + + + + + + + + + +<%@include file="/template/header.jsp" %> +

    + Select Parameters for Schedule Adherence by Stop Chart +
    + +
    +
    + <%-- For passing agency param to the report --%> + "> + + + + + +
    + + +
    + +
    + + +
    + + + +
    + + + \ No newline at end of file diff --git a/transitimeWebapp/src/main/webapp/reports/schAdhByTimeChart.jsp b/transitclockWebapp/src/main/webapp/reports/schAdhByTimeChart.jsp similarity index 92% rename from transitimeWebapp/src/main/webapp/reports/schAdhByTimeChart.jsp rename to transitclockWebapp/src/main/webapp/reports/schAdhByTimeChart.jsp index 89b95ac53..c8e702674 100644 --- a/transitimeWebapp/src/main/webapp/reports/schAdhByTimeChart.jsp +++ b/transitclockWebapp/src/main/webapp/reports/schAdhByTimeChart.jsp @@ -1,6 +1,6 @@ <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> -<%@ page import="org.transitime.utils.web.WebUtils" %> +<%@ page import="org.transitclock.utils.web.WebUtils" %> @@ -50,11 +50,12 @@ <%@include file="/template/header.jsp" %> <% + String dateRange = request.getParameter("beginDate") + " (+" + request.getParameter("numDays") + " days)"; String allowableEarly = request.getParameter("allowableEarly");; String allowableLate = request.getParameter("allowableLate");; String chartSubtitle = allowableEarly + " min early to " + allowableLate + " min late
    " - + request.getParameter("dateRange"); + + dateRange; String beginTime = request.getParameter("beginTime"); String endTime = request.getParameter("endTime"); @@ -225,9 +226,9 @@ function getDataAndDrawChart() { success: createDataTableAndDrawChart, // When there is an AJAX problem alert the user error: function(request, status, error) { - //alert(error + '. ' + request.responseText); - $("#errorMessage").html(request.responseText + - "

    Hit back button to try other parameters."); + console.log(request.responseText) + var msg = $("

    ").html("
    No data for requested parameters. Hit back button to try other parameters.") + $("#errorMessage").append(msg); $("#errorMessage").fadeIn("fast"); $("#loading").fadeOut("slow"); }, diff --git a/transitimeWebapp/src/main/webapp/reports/schAdhByTimeData.jsp b/transitclockWebapp/src/main/webapp/reports/schAdhByTimeData.jsp similarity index 75% rename from transitimeWebapp/src/main/webapp/reports/schAdhByTimeData.jsp rename to transitclockWebapp/src/main/webapp/reports/schAdhByTimeData.jsp index 48d4044ac..2dce19903 100644 --- a/transitimeWebapp/src/main/webapp/reports/schAdhByTimeData.jsp +++ b/transitclockWebapp/src/main/webapp/reports/schAdhByTimeData.jsp @@ -19,10 +19,16 @@ --%> <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> -<%@ page import="org.transitime.reports.GenericJsonQuery" %> -<%@ page import="org.transitime.reports.SqlUtils" %> +<%@ page import="org.transitclock.db.webstructs.WebAgency" %> +<%@ page import="org.transitclock.reports.GenericJsonQuery" %> +<%@ page import="org.transitclock.reports.SqlUtils" %> <% try { + String agencyId = request.getParameter("a"); + WebAgency agency = WebAgency.getCachedWebAgency(agencyId); + String dbtype = agency.getDbType(); + boolean isMysql = "mysql".equals(dbtype); + String allowableEarlyStr = request.getParameter("allowableEarly"); if (allowableEarlyStr == null || allowableEarlyStr.isEmpty()) allowableEarlyStr = "1.0"; @@ -35,18 +41,23 @@ String allowableLateMinutesStr = "'" + SqlUtils.convertMinutesToSecs(allowableLa // Group into timebuckets of 30 seconds int BUCKET_TIME = 30; - +String epochCommandPre = "EXTRACT (EPOCH FROM "; +String epochCommandPost = ")"; +if (isMysql) { + epochCommandPre = "UNIX_TIMESTAMP"; + epochCommandPost = ""; +} String sql = "SELECT " + " COUNT(*) AS counts_per_time_period, \n" // Put into time buckets of every BUCKET_TIME seconds. - + " FLOOR(EXTRACT (EPOCH FROM (scheduledtime-time)) / " + BUCKET_TIME + ")*" + BUCKET_TIME + " AS time_period \n" - + "FROM arrivalsdepartures ad\n" + + " FLOOR(" + epochCommandPre + " (scheduledTime-time)" + epochCommandPost + " / " + BUCKET_TIME + ")*" + BUCKET_TIME + " AS time_period \n" + + "FROM ArrivalsDepartures ad\n" + "WHERE " // Only need arrivals/departures that have a schedule time - + " ad.scheduledtime IS NOT NULL \n" + + " ad.scheduledTime IS NOT NULL \n" // Ignore stops where schedule adherence really far off - + " AND ABS(EXTRACT (EPOCH FROM (scheduledtime-time))) < 3600\n" + + " AND ABS(" + epochCommandPre + " (scheduledTime-time)" + epochCommandPost + ") < 3600\n" // Specifies which routes to provide data for + SqlUtils.routeClause(request, "ad") + "\n" + SqlUtils.timeRangeClause(request, "ad.time", 7) + "\n" @@ -59,7 +70,6 @@ String sql = System.out.println("\nFor schedule adherence by time buckets query sql=\n" + sql); // Do the query and return result in JSON format -String agencyId = request.getParameter("a"); String jsonString = GenericJsonQuery.getJsonString(agencyId, sql); response.setContentType("application/json"); response.setHeader("Access-Control-Allow-Origin", "*"); diff --git a/transitclockWebapp/src/main/webapp/reports/schAdhByTimeParams.jsp b/transitclockWebapp/src/main/webapp/reports/schAdhByTimeParams.jsp new file mode 100644 index 000000000..18571f519 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/schAdhByTimeParams.jsp @@ -0,0 +1,61 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1" %> + + + + <%@include file="/template/includes.jsp" %> + + Specify Parameters + + + + + + + + + +<%@include file="/template/header.jsp" %> +

    + Select Parameters for Schedule Adherence by Route Chart +
    + +
    +
    + <%-- For passing agency param to the report --%> + "> + + + + + +
    + + +
    + +
    + + +
    + + + +
    + + + \ No newline at end of file diff --git a/transitclockWebapp/src/main/webapp/reports/scheduleHorizStopsParams.jsp b/transitclockWebapp/src/main/webapp/reports/scheduleHorizStopsParams.jsp new file mode 100644 index 000000000..5dfbae970 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/scheduleHorizStopsParams.jsp @@ -0,0 +1,37 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1" %> + + + + <%@include file="/template/includes.jsp" %> + + Specify Parameters + + + + + + + + + + +<%@include file="/template/header.jsp" %> + +
    + Select Parameters for Schedule Report +
    + +
    +
    + <%-- For passing agency param to the report --%> + "> + + + + + +
    + + + \ No newline at end of file diff --git a/transitimeWebapp/src/main/webapp/reports/scheduleHorizStopsReport.jsp b/transitclockWebapp/src/main/webapp/reports/scheduleHorizStopsReport.jsp similarity index 95% rename from transitimeWebapp/src/main/webapp/reports/scheduleHorizStopsReport.jsp rename to transitclockWebapp/src/main/webapp/reports/scheduleHorizStopsReport.jsp index 56e015626..12803e1ac 100644 --- a/transitimeWebapp/src/main/webapp/reports/scheduleHorizStopsReport.jsp +++ b/transitclockWebapp/src/main/webapp/reports/scheduleHorizStopsReport.jsp @@ -1,7 +1,7 @@ <%-- Displays the schedule for a route--%> <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> -<%@ page import="org.transitime.utils.web.WebUtils" %> +<%@ page import="org.transitclock.utils.web.WebUtils" %> <% String agencyId = request.getParameter("a"); diff --git a/transitclockWebapp/src/main/webapp/reports/scheduleVertStopsParams.jsp b/transitclockWebapp/src/main/webapp/reports/scheduleVertStopsParams.jsp new file mode 100644 index 000000000..4565ddeeb --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/scheduleVertStopsParams.jsp @@ -0,0 +1,37 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1" %> + + + + <%@include file="/template/includes.jsp" %> + + Specify Parameters + + + + + + + + + + +<%@include file="/template/header.jsp" %> + +
    + Select Parameters for Schedule Report +
    + +
    +
    + <%-- For passing agency param to the report --%> + "> + + + + + +
    + + + \ No newline at end of file diff --git a/transitimeWebapp/src/main/webapp/reports/scheduleVertStopsReport.jsp b/transitclockWebapp/src/main/webapp/reports/scheduleVertStopsReport.jsp similarity index 95% rename from transitimeWebapp/src/main/webapp/reports/scheduleVertStopsReport.jsp rename to transitclockWebapp/src/main/webapp/reports/scheduleVertStopsReport.jsp index dbca374fc..9a15f796f 100644 --- a/transitimeWebapp/src/main/webapp/reports/scheduleVertStopsReport.jsp +++ b/transitclockWebapp/src/main/webapp/reports/scheduleVertStopsReport.jsp @@ -1,7 +1,7 @@ <%-- Displays the schedule for a route--%> <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> -<%@ page import="org.transitime.utils.web.WebUtils" %> +<%@ page import="org.transitclock.utils.web.WebUtils" %> <% String agencyId = request.getParameter("a"); diff --git a/transitclockWebapp/src/main/webapp/reports/speedMap.jsp b/transitclockWebapp/src/main/webapp/reports/speedMap.jsp new file mode 100644 index 000000000..ec3f6e05c --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/speedMap.jsp @@ -0,0 +1,1007 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1"%> + + + + <%@include file="/template/includes.jsp" %> + + Speed Map + + + + + + + + + + + + + <%-- --%> + + + + + + + + + + + + + + + + <%@include file="/template/header.jsp" %> +
    + +
    + +
    +
    + Speed Map +
    + +
    + <%-- For passing agency param to the report --%> + "> + + + +
    + + +
    + + + + + + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + +
    +
    +
    +
    + Low Speed (mph max) +
    +
    + + +
    +
    +
    +
    +
    + Mid Speed (mph max) +
    +
    + + + +
    +
    +
    +
    +
    + High Speed (mph max) +
    +
    +
    +
    + + + +
    + +
    + +
    +
    +

    +
    +
    + +
    + +
    +
    + +
    + + \ No newline at end of file diff --git a/transitclockWebapp/src/main/webapp/reports/stopScheduleAdherence.jsp b/transitclockWebapp/src/main/webapp/reports/stopScheduleAdherence.jsp new file mode 100644 index 000000000..98de18b1b --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/stopScheduleAdherence.jsp @@ -0,0 +1,95 @@ +<%@ page language="java" contentType="text/html; charset=UTF-8" + pageEncoding="UTF-8"%> +<%@ page import="org.transitclock.utils.web.WebUtils" %> + + + +<%@include file="/template/includes.jsp" %> + + + + +Stop Schedule Adherence + + + + +<%@include file="/template/header.jsp" %> + + + + +
    + +
    Stop Schedule Adherence
    + + + +
    + +
    + +
    + +
    +
    +This box plot shows the distribution of schedule adherence data across stops. +For each stop, the maximum and minimum schedule adherences are horizontal grey +lines. The median, first quartile (middle value between the minimum and median), +and third quartile (middle value between the median and maximum) are horizontal +colored lines. 50% of a stop's data points lie within the colored box. +
    + + + + + + + + + + \ No newline at end of file diff --git a/transitclockWebapp/src/main/webapp/reports/stopsJsonData.jsp b/transitclockWebapp/src/main/webapp/reports/stopsJsonData.jsp new file mode 100644 index 000000000..33267783c --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/stopsJsonData.jsp @@ -0,0 +1,20 @@ +<%@ page language="java" contentType="application/json; charset=UTF-8" + pageEncoding="UTF-8"%> +<%@ page import="org.transitclock.reports.StopsForRouteDirectionJsonQuery"%> + +<% +// Get the params +String agencyId = request.getParameter("a"); +String routeId = request.getParameter("r").replaceAll("&", "&"); +String headsign = request.getParameter("headsign").replaceAll("&", "&"); + + + +// Query db and get JSON string +String jsonString = StopsForRouteDirectionJsonQuery.getStopsJson(agencyId, routeId, headsign); + +// Respond with the JSON string +response.setContentType("application/json"); +response.setHeader("Access-Control-Allow-Origin", "*"); +response.getWriter().write(jsonString); +%> diff --git a/transitclockWebapp/src/main/webapp/reports/vehicleEventData.jsp b/transitclockWebapp/src/main/webapp/reports/vehicleEventData.jsp new file mode 100644 index 000000000..c6bee6352 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/vehicleEventData.jsp @@ -0,0 +1,28 @@ +<%@ page language="java" contentType="application/json; charset=ISO-8859-1" + pageEncoding="ISO-8859-1"%> +<%@ page import="org.transitclock.reports.GenericJsonQuery" %> +<%@ page import="org.transitclock.reports.SqlUtils" %> +<% +try +{ +String agencyId = request.getParameter("a"); +String vehicleId = request.getParameter("v"); + +String sql = "select * from vehicleevents ve where ve.vehicleid=ve.vehicleid"; +// If only want data for single vehicle then specify so in SQL +if (vehicleId != null && !vehicleId.isEmpty()&&!vehicleId.startsWith(" ")) +{ + sql += " AND ve.vehicleId='" + vehicleId + "' "; +} +sql += SqlUtils.timeRangeClause(request, "ve.time", 5) + "\n"; +sql += " order by ve.time desc"; + +String jsonString = GenericJsonQuery.getJsonString(agencyId, sql); +response.setHeader("Access-Control-Allow-Origin", "*"); +response.getWriter().write(jsonString); +} catch (Exception e) { + response.setStatus(400); + response.getWriter().write(e.getMessage()); + return; +} +%> \ No newline at end of file diff --git a/transitimeWebapp/src/main/webapp/reports/predAccuracyCsvParams.jsp b/transitclockWebapp/src/main/webapp/reports/vehicleEventParams.jsp similarity index 82% rename from transitimeWebapp/src/main/webapp/reports/predAccuracyCsvParams.jsp rename to transitclockWebapp/src/main/webapp/reports/vehicleEventParams.jsp index e6819e8f6..056826ac4 100644 --- a/transitimeWebapp/src/main/webapp/reports/predAccuracyCsvParams.jsp +++ b/transitclockWebapp/src/main/webapp/reports/vehicleEventParams.jsp @@ -18,18 +18,18 @@ <%@include file="/template/header.jsp" %>
    - Select Parameters for Prediction Accuracy CSV Download + Select Parameters for Displaying Event Log by Vehicle
    -
    + <%-- For passing agency param to the report --%> "> - - - + + +
    diff --git a/transitclockWebapp/src/main/webapp/reports/vehicleEventReport.jsp b/transitclockWebapp/src/main/webapp/reports/vehicleEventReport.jsp new file mode 100644 index 000000000..d1128c5dc --- /dev/null +++ b/transitclockWebapp/src/main/webapp/reports/vehicleEventReport.jsp @@ -0,0 +1,65 @@ +<%@ page language="java" contentType="text/html; charset=ISO-8859-1" + pageEncoding="ISO-8859-1"%> +<%@ page import="org.transitclock.utils.web.WebUtils" %> + +<% +String agencyId = request.getParameter("a"); +String vehicleId = request.getParameter("v"); +if (agencyId == null || agencyId.isEmpty()) { + response.getWriter().write("You must specify agency in query string (e.g. ?a=mbta)"); + return; +} +if (vehicleId == null || vehicleId.isEmpty()) { + response.getWriter().write("You must specify vehicle in query string (e.g. ?v=1234)"); + return; +} +%> + + + <%@include file="/template/includes.jsp" %> + +Events for Vehicle + + + + + + +<%@include file="/template/header.jsp" %> +
    Vehicle Events for vehicle
    + + +
    TimeEvent Description
    + + \ No newline at end of file diff --git a/transitimeWebapp/src/main/webapp/smartphone/index.html b/transitclockWebapp/src/main/webapp/smartphone/index.html similarity index 100% rename from transitimeWebapp/src/main/webapp/smartphone/index.html rename to transitclockWebapp/src/main/webapp/smartphone/index.html diff --git a/transitimeWebapp/src/main/webapp/status/activeBlocks.jsp b/transitclockWebapp/src/main/webapp/status/activeBlocks.jsp similarity index 69% rename from transitimeWebapp/src/main/webapp/status/activeBlocks.jsp rename to transitclockWebapp/src/main/webapp/status/activeBlocks.jsp index 2d16f877f..74e986311 100644 --- a/transitimeWebapp/src/main/webapp/status/activeBlocks.jsp +++ b/transitclockWebapp/src/main/webapp/status/activeBlocks.jsp @@ -1,5 +1,6 @@ <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> +<%@ page import="org.transitclock.reports.ScheduleAdherenceController" %> <% String agencyId = request.getParameter("a"); @@ -78,7 +79,7 @@ if (agencyId == null || agencyId.isEmpty()) { text-align: center; } -#percentWithVehiclesLabel, #percentOnTimeLabel, #percentEarlyLabel { +#percentWithVehiclesLabel, #percentOnTimeLabel, #percentEarlyLabel, #asOfLabel { margin-left: 20px; } @@ -86,16 +87,31 @@ if (agencyId == null || agencyId.isEmpty()) { margin-left: 40px; } -#totalBlocksLabel, #percentWithVehiclesLabel, #percentOnTimeLabel, #percentEarlyLabel, #percentLateLabel { +#totalBlocksLabel, #percentWithVehiclesLabel, #percentOnTimeLabel, #percentEarlyLabel, #percentLateLabel, #asOfLabel { margin-right: 4px; color: graytext; } +#menu { + text-align: center; +} + + + + + + + + + + + + + + + + + + + + + TheTransitClock Synoptic + + + + +
    +
    + + +
    +
    + +
    + + diff --git a/transitclockWebapp/src/main/webapp/synoptic/javascript/animation.js b/transitclockWebapp/src/main/webapp/synoptic/javascript/animation.js new file mode 100644 index 000000000..c7692d36e --- /dev/null +++ b/transitclockWebapp/src/main/webapp/synoptic/javascript/animation.js @@ -0,0 +1,179 @@ +// D3-style object to create and control an animation of the AVL +// data for a particular vehicle. +// map : map or group where animation will be added. +// clock: DOM object where current time should be updated. +// icon: Leaflet icon which will be animated. +function avlAnimation(map, icon, clock) { + + var startTime, endTime, rate, currentIndex, elapsedTime, + lastTime, lineDone, paused, durations, sprite, positions, end; + + var ready = false; + + // create icon for animation and initialize values + // positions is an array of position values: { lat, lon, timestamp } + function animation(data) { + + // remove old sprite. + if (sprite) + map.removeLayer(sprite); + + positions = data + + ready = true; + startTime = positions[0].timestamp; + endTime = positions[positions.length-1].timestamp; + rate = 1; + + currentIndex = 0; // this means we're going to 1 + + elapsedTime = positions[0].timestamp, + lastTime = 0, + lineDone = 0; + + paused = true; + + durations = [] + for (var i = 0; i < positions.length - 1; i++) + durations.push(positions[i+1].timestamp - positions[i].timestamp); + + sprite = L.marker(positions[0], {icon: icon}).addTo(map); + clock.textContent = parseTime(elapsedTime); + } + + function tick() { + var now = Date.now(), + delta = now - lastTime; + + lastTime = now; + + elapsedTime += delta * rate; + + lineDone += delta * rate; + + if (lineDone > durations[currentIndex]) { + // advance index and icon + currentIndex += 1 + lineDone = 0; + + if (currentIndex == positions.length - 1) { + if (end) + end() + currentIndex = 0; + paused = true; + return; + } + + sprite.setLatLng(positions[currentIndex]) + sprite.update() + elapsedTime = positions[currentIndex].timestamp + } + else { + var pos = interpolatePosition(positions[currentIndex], positions[currentIndex+1], durations[currentIndex], lineDone) + sprite.setLatLng(pos) + sprite.update() + + } + clock.textContent = parseTime(elapsedTime); + + if (!paused) + requestAnimationFrame(tick) + } + + animation.ready = function() { + return ready; + } + + animation.start = function() { + lastTime = Date.now(); + paused = false; + tick(); + } + + animation.pause = function() { + paused = true; + } + + animation.paused = function() { + return paused; + } + + animation.onEnd = function (_) { + end = _; + } + + animation.rate = function(_) { + if(_) + rate = _; + else + return rate; + } + + // skip to next AVL + animation.next = function() { + updateToIndex(currentIndex+1); + } + + // previous AVL + animation.prev = function() { + // In most cases, we don't actually want to go *back* an index, just + // restart this one. Exception: if we are less than 500ms (in realtime) + // into this avl. + + var delta = elapsedTime - positions[currentIndex].timestamp; + if (delta/rate < 500) + updateToIndex(currentIndex-1); + else + updateToIndex(currentIndex); + } + + // find next AVL that has a different lat/lng + animation.advance = function() { + var pos = positions[currentIndex] + var nextIndex = currentIndex + 1; + while(nextIndex < positions.length) { + var next = positions[currentIndex]; + if (pos.lat != next.lat || pos.lon != next.lon) + break; + nextIndex++; + } + updateToIndex(nextIndex); + console.log(nextIndex) + } + + function updateToIndex(i) { + if (i > positions.length - 1) + i = positions.length - 1; + if (i < 0) + i = 0; + + currentIndex = i; //+= 1; + lineDone = 0; + var avl = positions[currentIndex]; + elapsedTime = avl.timestamp; + + // update GUI if tick won't. + if (paused) { + sprite.setLatLng(avl); + sprite.update(); + clock.textContent = parseTime(elapsedTime); + } + } + + function parseTime(x) { + return new Date(x).toTimeString().slice(0, 8); + } + + // taken from leafletMovingMarker.js + var interpolatePosition = function(p1, p2, duration, t) { + var k = t/duration; + k = (k>0) ? k : 0; + k = (k>1) ? 1 : k; + return L.latLng(p1.lat + k*(p2.lat-p1.lat), p1.lon + k*(p2.lon-p1.lon)); + }; + + + return animation; +} + + \ No newline at end of file diff --git a/transitclockWebapp/src/main/webapp/synoptic/javascript/avlMap.js b/transitclockWebapp/src/main/webapp/synoptic/javascript/avlMap.js new file mode 100644 index 000000000..b6a36fe96 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/synoptic/javascript/avlMap.js @@ -0,0 +1,359 @@ +//Edit route input width. +$("#route").attr("style", "width: 200px"); + +/* For drawing the route and stops */ +var routeOptions = { + color: '#00ee00', + weight: 4, + opacity: 0.4, + lineJoin: 'round', + clickable: false +}; + + var stopOptions = { + color: '#006600', + opacity: 0.4, + radius: 4, + weight: 2, + fillColor: '#006600', + fillOpacity: 0.3, +}; + +var routePolylineOptions = {clickable: false, color: "#00f", opacity: 0.5, weight: 4}; + +var stopPopupOptions = {closeButton: false}; + +function drawAvlMarker(avl) { + var latLng = L.latLng(avl.lat, avl.lon); + + // Create the marker. Use a divIcon so that can have tooltips + var tooltip = avl.time.substring(avl.time.indexOf(' ') + 1); + var avlMarker = L.rotatedMarker(avl, { + icon: L.divIcon({ + className: 'avlMarker_', + html: "
    ", + iconSize: [7,7] + }), + angle: avl.heading, + title: tooltip + }).addTo(vehicleGroup); + + // Create popup with detailed info + + var labels = ["Vehicle", "GPS Time", "Time Proc", "Lat/Lon", "Speed", "Heading", "Assignment ID"], + keys = ["vehicleId", "time", "timeProcessed", "latlon", "niceSpeed", "heading", "assignmentId"]; + + // populate missing keys + avl.latlon = avl.lat + ", " + avl.lon + avl.niceSpeed = Math.round(parseFloat(avl.speed) * 10)/10 + " kph"; + + var content = $("").attr("class", "popupTable"); + + for (var i = 0; i < labels.length; i++) { + var label = $("").append(label, value) ) + } + + avlMarker.bindPopup(content[0]); + + return avlMarker; +} + +/* Called when receiving the AVL data via AJAX call */ +function processAvlCallback(jsonData) { + + /* Save avl data */ + + // List of all the latLngs + var latLngs = []; + + // So can draw separate polyline per vehicle + var previousVehicleId = ""; + var latLngsForVehicle = []; + + // reset list of vehicles + var vehicles = []; + + // For each AVL report... + var vehicle; + for (var i=0; i= 2) + L.polyline(latLngsForVehicle, routePolylineOptions).addTo(map); //.bringToBack(); + latLngsForVehicle = []; + vehicle = {id: avl.vehicleId, data: []} + vehicles.push(vehicle); + } + + // parse date string -> number + avl.timestamp = Date.parse(avl.time.replace(/-/g, '/').slice(0,-2)) + + vehicle.data.push(avl) + previousVehicleId = avl.vehicleId; + + var latLng = drawAvlMarker(avl).getLatLng(); + + latLngsForVehicle.push(latLng); + latLngs.push(latLng); + } + + // Draw polyline for the last vehicle in the AVL data + if (latLngsForVehicle.length >= 2) + L.polyline(latLngsForVehicle, routePolylineOptions).addTo(vehicleGroup); //.bringToBack(); + + // If actually read AVL data... + if (latLngs.length > 0) { + // To make all AVL reports fit in bounds of map. + map.fitBounds(latLngs); + } else { + alert("No AVL data for the criteria specified.") + } + return vehicles; +} + + +/** + * Reads in route data obtained via AJAX and draws route and stops on map. + */ +function routeConfigCallback(data, status) { + // Draw the paths for the route + + var route = data.routes[0]; + + for (var i=0; i").attr("class", "popupTable"); + var labels = ["Stop ID", "Name"], keys = ["id", "name"]; + for (var i = 0; i < labels.length; i++) { + var label = $("").append(label, value) ); + } + + stopMarker.bindPopup(content[0]); + } + } +} + +// Data in vehicles will be available as CSV when you click the `export' link. +// CSV should be the AVL CSV format used elsewhere in Transitime. +// org.transitclock.avl.AvlCsvWriter writes the following header: +// vehicleId,time,justTime,latitude,longitude,speed,heading,assignmentId,assignmentType +// org.transitclock.avl.AvlCsvRecord has required keys vehicleId, time, latitude, longitude +// all others optional +function createExport(vehicles) { + + var data = vehicles[0].data + + // set keys + var keys = ["vehicleId", "time", "latitude", "longitude", "speed", "heading", "assignmentId"] + // CSV key => JS object key + function mapKey(k) { + var o = {"latitude": "lat", "longitude": "lon"} + return o[k] || k; + } + + // write header + var text = keys[0]; + for (var i = 1; i < keys.length; i++) + text += "," + keys[i] + text += '\n' + + // write rows + for (var i = 0; i < data.length; i++) { + text += data[i][keys[0]] + for (var j = 1; j < keys.length; j++) { + var k = mapKey(keys[j]) + text += "," + data[i][k] + } + text += "\n"; + } + + var blob = new Blob([text], { type: 'text/plain' }); // change to text/csv for download prompt + $("#exportData")[0].href = window.URL.createObjectURL(blob); + } + +//Add a new layer for only route/bus markers, so that it can be refreshed +//when selections change without having to redraw tiles. +var mapTileUrl ='http://tile.openstreetmap.org/{z}/{x}/{y}.png' +var map = L.map('map'); +L.control.scale({metric: false}).addTo(map); +L.tileLayer(mapTileUrl, { + attribution: '© OpenStreetMap & CC-BY-SA, Imagery © Mapbox', + maxZoom: 19 +}).addTo(map); + +//fit map to agency boundaries. +$.getJSON(apiUrlPrefix + "/command/agencyGroup", function(agencies) { + var e = agencies.agency[0].extent; + map.fitBounds([[e.minLat, e.minLon], [e.maxLat, e.maxLon]]); +}) +.fail(function(request, status, error) { + alert(error + '. ' + request.responseText); +}); + + +var vehicleGroup = L.layerGroup().addTo(map); +var routeGroup = L.layerGroup().addTo(map); +var animationGroup = L.layerGroup().addTo(map); + +//Set the CLIP_PADDING to a higher value so that when user pans on map +//the route path doesn't need to be redrawn. Note: leaflet documentation +//says that this could decrease drawing performance. But hey, it looks +//better. +L.Path.CLIP_PADDING = 0.8;0 + +if (request.v || request.r) { + // Request exists; set all the controls to match the values in the request. + $("#vehicle").val(request.v).trigger("change"); + $("#beginDate").val(request.beginDate).trigger("change"); + $("#numDays").val(parseInt(request.numDays)).trigger("change"); + $("#beginTime").val(request.beginTime).trigger("change"); + $("#endTime").val(request.endTime).trigger("change"); + $("#route").val(request.r).trigger("change"); + + //draw vehicles if there is already request information + if (request.v) { + drawAvlData(); + } + if (request.r && request.r != "") + drawRoute(request.r); + +} +else { + // no request + // set beginDate and endDate to defaults + request.beginDate = $("#beginDate").val() + request.numDays = $("#numDays").val() +} + +// draw route data when dropdown is selected +$("#route").change(function(evt) { drawRoute(evt.target.value) }); + +// draw AVL data when submit button is clicked +$("#submit").on("click", function() { + /* Set request object to match new values */ + request.v = $("#vehicle").val(); + request.beginDate = $("#beginDate").val(); + request.numDays = $("#numDays").val(); + request.beginTime = $("#beginTime").val(); + request.endTime = $("#endTime").val(); + request.r = $("#route").val(); + + // Clear existing layer and draw new objects on map. + drawAvlData(); +}); + + +//Get the AVL data via AJAX and call processAvlCallback to draw it +function drawAvlData() { + $.ajax({ + // The page being requested + url: contextPath + "/reports/avlJsonData.jsp", + // Pass in query string parameters to page being requested + data: request, + // Needed so that parameters passed properly to page being requested + traditional: true, + dataType:"json", + async: true, + // When successful process JSON data + success: function(resp) { + vehicleGroup.clearLayers(); + var vehicles = processAvlCallback(resp); + // connect export to link to csv creation. + createExport(vehicles); + if (vehicles.length) + prepareAnimation(vehicles[0].data); // only animate first vehicle returned. + }, + // When there is an AJAX problem alert the user + error: function(request, status, error) { + alert(error + '. ' + request.responseText); + }, + }); +} + +function drawRoute(route) { + routeGroup.clearLayers(); + if (route != "") { + var url = apiUrlPrefix + "/command/routesDetails?r=" + route; + $.getJSON(url, routeConfigCallback); + } +} + +/* Animation controls */ + +var busIcon = L.icon({ + iconUrl: contextPath + "/reports/images/bus.png", + iconSize: [25,25] +}); +var animate = avlAnimation(animationGroup, busIcon, $("#playbackTime")[0]); + +var playButton = contextPath + "/reports/images/playback/media-playback-start.svg", + pauseButton = contextPath + "/reports/images/playback/media-playback-pause.svg"; + +animate.onEnd(function() { + $("#playbackPlay").attr("src", playButton); +}) + +// Given a list of AVL positions, initialize the animation object. +function prepareAnimation(avlData) { + + // Make sure animation controls are in their initial state. + $("#playbackPlay").attr("src", playButton); + $("#playbackRate").text("1X"); + + animate(avlData); + +} + +$("#playbackNext").on("click", animate.next); + +$("#playbackPrev").on("click", animate.prev); + +$("#playbackPlay").on("click", function() { + + if (!animate.paused()) { + animate.pause(); + $("#playbackPlay").attr("src", playButton); + } + else { // need to start it + animate.start(); + $("#playbackPlay").attr("src", pauseButton); + } + +}); + +$("#playbackFF").on("click", function() { + var rate = animate.rate()*2; + animate.rate(rate); + $("#playbackRate").text(rate + "X"); +}); + +$("#playbackRew").on("click", function() { + var rate = animate.rate()/2; + animate.rate(rate); + $("#playbackRate").text(rate + "X"); +}); + + diff --git a/transitclockWebapp/src/main/webapp/synoptic/javascript/leafletRotatedMarker.js b/transitclockWebapp/src/main/webapp/synoptic/javascript/leafletRotatedMarker.js new file mode 100644 index 000000000..2ce968de2 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/synoptic/javascript/leafletRotatedMarker.js @@ -0,0 +1,26 @@ +// Class for creating marker with an orientation. Found at +// https://www.mapbox.com/mapbox.js/example/v1.0.0/rotating-controlling-marker/ +// Orients the icon to marker.options.angle when setLatLng() is called. +// MIT-licensed code by Benjamin Becquet +// https://github.com/bbecquet/Leaflet.PolylineDecorator +L.RotatedMarker = L.Marker.extend({ +options: { angle: 0 }, +_setPos: function(pos) { + L.Marker.prototype._setPos.call(this, pos); + if (L.DomUtil.TRANSFORM) { + // use the CSS transform rule if available + this._icon.style[L.DomUtil.TRANSFORM] += ' rotate(' + this.options.angle + 'deg)'; + } else if (L.Browser.ie) { + // fallback for IE6, IE7, IE8 + var rad = this.options.angle * L.LatLng.DEG_TO_RAD, + costheta = Math.cos(rad), + sintheta = Math.sin(rad); + this._icon.style.filter += ' progid:DXImageTransform.Microsoft.Matrix(sizingMethod=\'auto expand\', M11=' + + costheta + ', M12=' + (-sintheta) + ', M21=' + sintheta + ', M22=' + costheta + ')'; + } +} +}); + +L.rotatedMarker = function(pos, options) { + return new L.RotatedMarker(pos, options); +}; diff --git a/transitclockWebapp/src/main/webapp/synoptic/javascript/mapUiOptions.js b/transitclockWebapp/src/main/webapp/synoptic/javascript/mapUiOptions.js new file mode 100644 index 000000000..41fa0faf3 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/synoptic/javascript/mapUiOptions.js @@ -0,0 +1,145 @@ +/** + * Options that effect how routes, stops, and vehicles are drawn + */ + +var shapeOptions = { + color: '#00ee00', + weight: 8, + opacity: 0.8, + lineJoin: 'round' +}; + +var minorShapeOptions = { + color: '#00ee00', + weight: 2, + opacity: 0.4, +}; + +var stopOptions = { + color: '#006600', + opacity: 1.0, + radius: 4, + weight: 2, + fillColor: '#006600', + fillOpacity: 0.6, +}; + +var firstStopOptions = { + color: '#006600', + opacity: 1.0, + radius: 7, + weight: 2, + fillColor: '#ccffcc', + fillOpacity: 0.9, +} + +var minorStopOptions = { + color: '#006600', + opacity: 0.2, + radius: 3, + weight: 2, + fillColor: '#006600', + fillOpacity: 0.2, +}; + +var busIcon = L.icon({ + iconUrl: 'images/bus-24.png', + iconRetinaUrl: 'images/bus-24@2x.png', + iconSize: [24, 24], + iconAnchor: [13, 12], + popupAnchor: [0, -12], +}); + +var streetcarIcon = L.icon({ + iconUrl: 'images/rail-light-24.png', + iconRetinaUrl: 'images/rail-light-24@2x.png', + iconSize: [24, 24], + iconAnchor: [12, 12], + popupAnchor: [0, -12], +}); + +var railIcon = L.icon({ + iconUrl: 'images/rail-24.png', + iconRetinaUrl: 'images/rail-24@2x.png', + iconSize: [24, 24], + iconAnchor: [13, 12], + popupAnchor: [0, -12], +}); + +var ferryIcon = L.icon({ + iconUrl: 'images/ferry-24.png', + iconRetinaUrl: 'images/ferry-24@2x.png', + iconSize: [24, 24], + iconAnchor: [12, 12], + popupAnchor: [0, -12], +}); + +var layoverIcon = L.icon({ + iconUrl: 'images/cafe-24.png', + iconRetinaUrl: 'images/cafe-24@2x.png', + iconSize: [24, 24], + iconAnchor: [12, 12], + popupAnchor: [0, -12], + +}); + +var arrowIcon = L.icon({ + iconUrl: 'images/arrow.png', + iconSize: [30, 30], + iconAnchor: [15,15], +}); + +var vehicleMarkerOptions = { + opacity: 1.0, +}; + +var secondaryVehicleMarkerOptions = { + opacity: 0.8, +}; + +var minorVehicleMarkerOptions = { + opacity: 0.3, +}; + +var vehicleMarkerBackgroundOptions = { + radius: 12, + weight: 0, + fillColor: '#ffffff', + fillOpacity: 1.0, +}; + +var secondaryVehicleMarkerBackgroundOptions = { + radius: 12, + weight: 0, + fillColor: '#ffffff', + fillOpacity: 0.80, +}; + +var minorVehicleMarkerBackgroundOptions = { + radius: 12, + weight: 0, + fillColor: '#ffffff', + fillOpacity: 0.3, +}; + +var unassignedVehicleMarkerBackgroundOptions = { + radius: 10, + weight: 0, + fillColor: '#F0FA39', + fillOpacity: 0.6, +}; + +var vehiclePopupOptions = { + offset: L.point(0,-2), + closeButton: false +}; + +var stopPopupOptions = { + offset: L.point(0, -0), + closeButton: false +} + +var tripPatternPopupOptions = { + closeButton: false +} + diff --git a/transitclockWebapp/src/main/webapp/synoptic/javascript/synoptic.js b/transitclockWebapp/src/main/webapp/synoptic/javascript/synoptic.js new file mode 100644 index 000000000..49b9543b7 --- /dev/null +++ b/transitclockWebapp/src/main/webapp/synoptic/javascript/synoptic.js @@ -0,0 +1,1089 @@ +var doStuff = function (indice,top, left ) { + this.__showContexMenuFromMuli({ top, left },indice) + } + + + +/** + * It defines a synoptic + * The contructor receivs as paramters: + * container:div|window + * onclick:callback + * border:int + * routeName:string + * showReturn:true|false + * drawReturnUpside:true|false + */ +class Sinoptico +{ + //{container:div|window,onclick:calback,border:int,vehicleIcon:image} + // + constructor(params) + //constructor(canvas,onclick,menu) + { + this.name=params.routeName; + this.container=params.container; + this.infoStop=(data) =>`
    ").attr("class", "popupTableLabel").text(labels[i] + ": "); + var value = $("").text(avl[keys[i]]); + content.append( $("
    ").attr("class", "popupTableLabel").text(labels[i] + ": "); + var value = $("").text(stop[keys[i]]); + content.append( $("
    ${data.identifier}
    ${data.projection}
    `; + this.infoVehicle=(data)=>`
    Móvil ${data.identifier}
    ${data.projection}
    `; + if(params.infoStop!=undefined) + { + this.infoStop=params.infoStop; + this.infoStop=this.infoStop.bind(this); + } + if(params.infoVehicle!=undefined) + { + this.infoVehicle=params.infoVehicle; + } + this.__showReturn=true; + if(params.showReturn!=undefined) + this.__showReturn=params.showReturn; + //TEST: + //this.__showReturn=true; + + this.toolTipDiv = document.createElement('div'); + this.menuDiv = document.createElement('div'); + this.menuDiv.className='menu-content'; + this.canvas = document.createElement('canvas'); + this.canvas.style.position = "absolute"; + this.canvas.border=0; + if(params.border!=undefined) + { + this.canvas.border= params.border; + } + this.canvas.style.border =this.canvas.border+"px solid"; + this.canvas.style.left="0"; + this.canvas.style.top="0"; + this.drawReturnUpside=true; + if(params.vehicleAlternColorCallBack!=undefined) + this.vehicleAlternColorCallBack=params.vehicleAlternColorCallBack; + + this.zoomFactor=1.0; +// if(params.drawReturnUpside!=undefined) +// this.drawReturnUpside=params.drawReturnUpside; + this.stopImg = new Image(); + this.stopImg.width=10; + this.stopImg.height=10; + //this.patternType="circular"; + //this.patternType="linear"; + if(params.patternType!=undefined) + this.patternType=params.patternType; + //TEST: + //this.patternType="linear"; + + //this.canvas=canvas; + if(params.routeName!=undefined) + { + + this.title = document.createElement('div'); + this.title.className='synopticTitle'; + this.title.innerHTML=''+params.routeName+''; + this.container.appendChild(this.title); + + } + + this.container.appendChild(this.canvas); + this.container.appendChild(this.toolTipDiv); + this.container.appendChild(this.menuDiv); + + this.ctx= this.canvas.getContext('2d'); + // this.ctx.fillRect(0, 0,80, 98); + //this.busPath=Path2d(); + + this.vehicleIcon//= document.getElementById("vehicleIcon"); + = new Image(); + this.vehicleIcon.addEventListener('load', function() { + console.log("Image loaded"); + + }, false); +// this.vehicleIcon.addEventListener('load', function() { +// // execute drawImage statements here +// }, false); + this.vehicleIcon.src="mybus.png"; + this.vehicleIcon.width=30; + this.vehicleIcon.height=30; + + this.linewidth=0.9;//Porcentaje que ocupa del canvas + // var lineWidth=(canvas.width*linewidth); + //this.margen=(canvas.width-lineWidth)/2.0;//MOVE TO POSITION OF THE LINE + this.forwarLinePosition=80; + + + if(this.patternType=="linear") + { + this.margin=40; + this.returnLinePosition=150; + } + else + { + this.margin=60; + this.returnLinePosition=300; + if(this.container.height0) + { + console.log(e); + this.__zoomOut(); + } + else + { + this.__zoomIn(); + } + } + ////////////////////////////////// + getElements(x,y) + { + var elements=[]; + for(var m=0;m=x && this.buses[m].posY+this.vehicleIcon.height>=y ) + elements.push(this.buses[m]); + } + for(var m=0;m=x && this.stops[m].posY+5>=y ) + elements.push(this.stops[m]); + } + return elements; + } + __hideContexMenu( ){ + + const menu = this.menuDiv; + menu.style.visibility = "hidden"; + + } + __hideTooltip( ){ + const tooltip=this.toolTipDiv; + // menu = window.getComputedStyle ? getComputedStyle(this.tootTipDiv, null) : this.tootTipDiv.currentStyle; + +// var menu = document.querySelector(".tooltip-right"); +// menu.style.visibility = "hidden"; +// menu.style.opacity = 0; +// menu = document.querySelector(".tooltip-left"); + tooltip.style.visibility = "hidden"; + tooltip.style.opacity = 0; + } + __showTooltipBus(elements,{ top, left }) + { + var leftScroll=0; + if(this.container.scrollLeft!=undefined) + leftScroll=this.container.scrollLeft; + if(this.toolTipDiv.clientWidth!=undefined) + { + if(left+this.toolTipDiv.clientWidth + Móvil ${data.identifier} + ${data.projection} + `; + tooltip.innerHTML+=this.infoVehicle(data); + } +// const toggleMenu = command => { +// menu.style.display = command === "show" ? "block" : "none"; +// menuVisible = !menuVisible; +// }; + + + } + setPredictionsContent(content) + { + const tooltip=this.toolTipDiv; + tooltip.innerHTML=content; + } + + __showTooltipStop(elements,{ top, left }) + { + + this.toolTipDiv.className="tooltip-bottom"; + top+=15; + var leftScroll=0; + if(this.container.scrollLeft!=undefined) + leftScroll=this.container.scrollLeft; + if(this.toolTipDiv.clientWidth!=undefined) + { + //alert(left+ " "+(window.innerWidth+leftScroll)+ " "+window.innerWidth); + + left-=this.toolTipDiv.clientWidth/2; + console.log((window.innerWidth+leftScroll)+ " LEFT "+(left+this.toolTipDiv.clientWidth)); + if(left<0) + { + this.toolTipDiv.className="tooltip-bottom-overflow-left"; + left+=this.toolTipDiv.clientWidth*0.40; + + } + else if(left+this.toolTipDiv.clientWidth>window.innerWidth+leftScroll) + { + //alert(this.container.scrollLeft); + this.toolTipDiv.className="tooltip-bottom-overflow-right"; + //left+=this.toolTipDiv.clientWidth*0.45; + left-=this.toolTipDiv.clientWidth*0.40; + } + } + +// if(this.toolTipDiv.clientWidth!=undefined) +// { +// console.log("a"); +// if(left+this.toolTipDiv.clientWidth { +// menu.style.display = command === "show" ? "block" : "none"; +// menuVisible = !menuVisible; +// }; + + + } + + __showTooltip(elements,{ top, left }) + { + + if(elements[0].type=='bus') + this.__showTooltipBus(elements,{ top, left }) + else if (elements[0].type=='stop') + this.__showTooltipStop(elements,{ top, left }) + } + __showContexMenuFromMuli(event) + { + console.log("__showContexMenuFromMuli"); + console.log(event); +// { top, left },indice +// console.log(this.__menuElements); +// console.log(indice); + var top=event.target.top; + var left= event.target.left; + this.__showContexMenu([event.target.element],{ top, left}); + + } + __showContexMenu(elements,{ top, left }) + { + + const menu = this.menuDiv; + if(elements == null) + { + const menu = this.menuDiv; + menu.innerHTML=""; + var a=document.createElement("a"); + a.innerHTML="Zoom in"; + a.addEventListener('click',this.__zoomIn) + // a.onclick=function (){this.__zoomIn}; + console.log("===>"+this.zoomFactor) + menu.appendChild(a); + a=document.createElement("a"); + a.innerHTML="Zoom out"; + a.addEventListener('click',this.__zoomOut) + // a.onclick=function (){this.__zoomIn}; + console.log("===>"+this.zoomFactor) + menu.appendChild(a); + menu.style.left = `${left}px`; + menu.style.top = `${top}px`; + menu.style.visibility = "visible"; + menu.style.border='1px solid'; + return; + } + if(elements[0].type=='bus') + { + top+=this.vehicleIcon.height+3; + } + else + top+=3; + + if(left+menu.clientWidth>window.innerWidth) + { + left=window.innerWidth-menu.clientWidth; + + } + menu.style.left = `${left}px`; + menu.style.top = `${top}px`; + menu.style.visibility = "visible"; + menu.style.border='1px solid'; + if(elements[0].type=='bus') + { + + if(elements.length>1) + { + menu.innerHTML=""; + for(var pos=0;pos${data.identifier}`; + ; + } + } + else + { + menu.innerHTML="menu1menu2"; +// var a=document.createElement("a"); +// a.innerHTML="Zoom in"; +// a.addEventListener('click',this.__zoomIn) +// // a.onclick=function (){this.__zoomIn}; +// console.log("===>"+this.zoomFactor) +// menu.appendChild(a); +// a=document.createElement("a"); +// a.innerHTML="Zoom out"; +// a.addEventListener('click',this.__zoomOut) +// // a.onclick=function (){this.__zoomIn}; +// console.log("===>"+this.zoomFactor) +// menu.appendChild(a); + } + + } + + if(elements[0].type=='stop') + menu.innerHTML="stop"; + + + + + } + __zoomIn() + { + if(this.zoomFactor<4) + this.zoomFactor+=0.1; + this.resize(); + this.__hideContexMenu(); + } + __zoomOut() + { + if(this.zoomFactor>1) + this.zoomFactor-=0.1; + this.resize(); + this.__hideContexMenu(); + } + __handleMouseEvent(event) + { + var rect = this.canvas.getBoundingClientRect(); + var x = event.pageX-rect.left, + y = event.pageY-rect.top; + if(event.type=="click") + { + this.__hideTooltip(); + this.__hideContexMenu(); + var elements=this.getElements(x,y); + if(elements!=undefined && elements.length>0) + this.onVehiClick(elements) + } + if(event.type=="contextmenu") + { + this.__hideTooltip(); + event.preventDefault(); + var elements=this.getElements(x,y); + + if(elements!=undefined && elements.length>0) + { + + const origin = { + left: elements[0].posX, + top: elements[0].posY + }; + this.__showContexMenu(elements,origin); + } + else + { + const origin = { + left: x, + top: y + }; + this.__showContexMenu(null,origin); + //this.__hideContexMenu(); + } + //alert(event.type); + } + if(event.type=="mousemove") + { + var elements=this.getElements(x,y); + + if(elements!=undefined && elements.length>0) + { + const origin = { + left: elements[0].posX, + top: elements[0].posY + }; + //console.log(origin); + this.__showTooltip(elements,origin); + } + else + this.__hideTooltip(); + + } + //console.log("CLICK "+x+ " "+y+ " canvas.width " +canvas.width+ " line "+lineWidth); + + + + + } + getLineWidth() + { + //return (this.canvas.width*this.linewidth); + return (this.canvas.width-this.margin*2); + } + + getLineMargin() + { + return (this.canvas.width-this.getLineWidth())/2.0; + } + + drawLinearLine(direction) + { + this.ctx.save(); + this.ctx.beginPath(); + this.ctx.lineCap="round"; + this.ctx.moveTo(this.getLineMargin(), direction==0?this.forwarLinePosition:this.returnLinePosition); + this.ctx.lineWidth = 5; + this.ctx.strokeStyle = this.linecolour; + this.ctx.lineTo(this.getLineWidth()+this.getLineMargin(), direction==0?this.forwarLinePosition:this.returnLinePosition); + this.ctx.stroke(); + this.ctx.closePath(); + this.ctx.restore(); + } + + drawCircularLine() + { + this.drawLinearLine(0); + this.ctx.save(); + this.ctx.beginPath(); + this.ctx.moveTo(this.getLineWidth()+this.getLineMargin(), this.forwarLinePosition); + this.ctx.lineWidth = 5; + this.ctx.strokeStyle = this.linecolour; + this.ctx.lineTo(this.getLineWidth()+this.getLineMargin(), this.returnLinePosition); + this.ctx.stroke(); + this.ctx.closePath(); + this.ctx.restore(); + this.drawLinearLine(1); + this.ctx.save(); + this.ctx.beginPath(); + this.ctx.moveTo(this.getLineMargin(),this.returnLinePosition); + this.ctx.lineWidth = 5; + this.ctx.strokeStyle = this.linecolour; + this.ctx.lineTo(this.getLineMargin(),this.forwarLinePosition); + this.ctx.stroke(); + this.ctx.closePath(); + this.ctx.restore(); + } + + drawLine(direction) + { + if(this.patternType=="linear") + this.drawLinearLine(direction); + if(this.patternType=="circular" && direction==0) + this.drawCircularLine(); + + + } + resize() { + console.log("RESIZE "+this.zoomFactor); + var width=this.container.innerWidth; + var height=this.container.innerHeight + if(width == undefined) + { + width=this.container.clientWidth; + height=this.container.clientHeight + } + width=this.zoomFactor*width; + this.container.style.overflow="auto"; + //console.log(this.canvas.style); + if(this.canvas.border!= undefined) + { + + width-=2*this.canvas.border; + height-=2*this.canvas.border; + } + //console.log("resizing"); +// console.log("resizing"+ this.container.innerWidth); +// console.log("resizing"+ this.container.clientWidth); +// console.log("resizing"+ this.container.clientHeight);//offsetHeight==>incluye padding, scrollbar y bordes + this.canvas.width = width; + this.canvas.height = height; + this.paint(); + this.resized=true; + } + getLastPostition(id) + { + + var lastPosition={}; + if(this.buses == undefined) + return lastPosition; + + for (var i=0; i < this.buses.length; i++) { + if (this.buses[i].id == id) { + //console.log("FOUND ID "+id); + lastPosition.posX=this.buses[i].posX; + lastPosition.posY=this.buses[i].posY; + if(this.patternType=="linear") + lastPosition.projected=this.buses[i].projection*this.getLineWidth(); + else if(this.patternType=="circular") + { + var h=this.returnLinePosition-this.forwarLinePosition; + var lineLength=2*(h+this.getLineWidth()); + lastPosition.projected=this.buses[i].projection*lineLength; + } + return lastPosition; + } + + } + return lastPosition; + } + setBuses(buses) + { + // console.log("SETTING BUSES"); + if(this.vehicleIcon.width==undefined || this.vehicleIcon.height==undefined) + { + this.vehicleIcon.height=this.vehicleIcon.naturalHeight; + this.vehicleIcon.width=this.vehicleIcon.naturalWidth; + } + this.maxBusLabelWidth=this.vehicleIcon.width; + for(var pos=0;posthis.maxBusLabelWidth) + this.maxBusLabelWidth=this.ctx.measureText(bus.identifier).width; + +// this.vehicleIcon.width=this.vehicleIcon.naturalWidth; +// this.vehicleIcon.height=this.vehicleIcon.naturalHeight; + //console.log(this.vehicleIcon.height+ " x "+ this.vehicleIcon.width); + + + bus.lastPosition=this.getLastPostition(bus.id); + bus.type='bus'; + + } + this.buses=buses; + //this.paint(); + } + animateBus(steps) + { + this.counter=0; + this.steps=steps; + window.requestAnimationFrame(this.__animateBus); + } + __getXYPositionByPixelDistance(distance) + { + var _object={}; + var _objWidth=this.vehicleIcon.width; + var _objHeight=this.vehicleIcon.height; + var distanceToLine=this.busDistanceToLine; + var h=this.returnLinePosition-this.forwarLinePosition; + var lineLength=2*(h+this.getLineWidth()); + //console.log("distance "+distance); + if(distance= this.getLineWidth() && distance= this.getLineWidth()+h && distance<2*this.getLineWidth()+h) + { + //TODO: THIS NEEDS TO REDRAW THE LINE +// if(this.drawReturnUpside==true) +// _object.posY =this.returnLinePosition-this.vehicleIcon.height-this.busDistanceToLine; +// else + //console.log("CASE 3"); + _object.posY = this.returnLinePosition+(distanceToLine); + _object.posX =this.getLineWidth()-(distance-(this.getLineWidth()+h))+this.getLineMargin()-_objWidth/2; + _object.labelYPosition=_object.posY+this.textSeparation+_objHeight; + + } + else + { + // console.log("CASE 4"); + _object.posX = this.getLineMargin()-(distanceToLine+(this.maxBusLabelWidth)); + _object.posY = this.returnLinePosition-(distance-(2*this.getLineWidth()+h)) -(_objHeight/2); + _object.labelYPosition=_object.posY-this.textSeparation; + } + //console.log(_object.posX + " ,"+_object.posY ); + return _object; + } + //TODO: Borrar bien los buses + __animateBus() + { + //Clear forward + this.ctx.save(); + //console.log(counter); + this.ctx.font="10px Georgia"; + var metrics=this.ctx.measureText("anyText"); + metrics.height=parseInt(this.ctx.font.match(/\d+/), 10); + + this.ctx.clearRect(0,0,this.getLineMargin()-this.busDistanceToLine+1,this.canvas.height); + this.ctx.clearRect(this.canvas.width-this.getLineMargin()+this.busDistanceToLine,0,this.getLineMargin(),this.canvas.height); + this.ctx.clearRect(0, this.forwarLinePosition-(this.vehicleIcon.height+metrics.height+this.textSeparation+this.busDistanceToLine+1), this.canvas.width, (this.vehicleIcon.height+metrics.height+this.textSeparation)+1);//Tenemos que tener el cuadrado de los buses. + if(this.drawReturnUpside==true && this.patternType=="linear")//TODO: If it is circular, we should repaint line + this.ctx.clearRect( 0,this.returnLinePosition-(this.vehicleIcon.height+metrics.height+this.textSeparation+this.busDistanceToLine+1), this.canvas.width,(this.vehicleIcon.height+metrics.height+this.textSeparation)+1);//Tenemos que tener el cuadrado de los buses. + else + this.ctx.clearRect( 0,this.returnLinePosition+this.busDistanceToLine, this.canvas.width, (this.vehicleIcon.height+metrics.height+this.textSeparation));//Tenemos que tener el cuadrado de los buses. + + for(var pos=0;posbus.posX) + xToDraw=bus.lastPosition.posX-(bus.lastPosition.posX-bus.posX)/this.steps*this.counter; + else + xToDraw=bus.lastPosition.posX+((bus.posX-bus.lastPosition.posX)/this.steps)*this.counter; + } + else if(this.patternType=="circular" && bus.lastPosition.projected!=undefined) + { + var distance=bus.lastPosition.projected+((bus.projected-bus.lastPosition.projected)/this.steps)*this.counter; + var positionStep=this.__getXYPositionByPixelDistance(distance); + xToDraw=positionStep.posX; + yToDraw=positionStep.posY; + bus.labelYPosition=positionStep.labelYPosition; + } + } + this.ctx.fillStyle = bus.fill; + // this.ctx.fillRect(xToDraw, yToDraw, this.vehicleIcon.width, this.vehicleIcon.height); + // this.ctx.drawImage(this.vehicleIcon,xToDraw, yToDraw,this.vehicleIcon.width, this.vehicleIcon.height); + if(this.vehicleAlternColorCallBack==undefined) + this.ctx.drawImage(this.vehicleIcon,xToDraw, yToDraw,this.vehicleIcon.width, this.vehicleIcon.height); + else + this.ctx.drawImage(this.vehicleAlternColorCallBack(bus),xToDraw, yToDraw,this.vehicleIcon.width, this.vehicleIcon.height); + + var fontPostY=0; + //console.log(metrics); + if(this.patternType=="linear") + fontPostY=(bus.direction==0)?bus.posY-this.textSeparation:(this.drawReturnUpside==true)?bus.posY-this.textSeparation:bus.posY+metrics.height+this.vehicleIcon.height+this.textSeparation; + else + fontPostY=bus.labelYPosition; + //console.log("fontPostY="+fontPostY); + this.ctx.fillText(bus.identifier,xToDraw,fontPostY ); + +// ctx.stroke(); + } + this.ctx.restore(); + this.counter++; + if(this.counter<=this.steps) + { + //console.log("TIME "+ this.counter); + window.requestAnimationFrame(this.__animateBus); + } + } + setStops(stops) + { + console.log('setting stops'+stops.length); + for(var pos=0;posthis.getLineWidth()) + { + console.warn("object projection>1"); + _object.posX=this.getLineWidth(); + } + } + else if(this.patternType=="circular") + { + // console.log("CIRCULAR"); + var h=this.returnLinePosition-this.forwarLinePosition; + var lineLength=2*(h+this.getLineWidth()); + var busLinePosition=lineLength*_object.projection; + _object.projected=busLinePosition; + if(busLinePosition>lineLength) + { + console.warn("object projection>1"); + busLinePosition=lineLength; + } + + if(busLinePosition= this.getLineWidth() && busLinePosition= this.getLineWidth()+h && busLinePosition<2*this.getLineWidth()+h) + { + //TODO: THIS NEEDS TO REDRAW THE LINE +// if(this.drawReturnUpside==true) +// _object.posY =this.returnLinePosition-this.vehicleIcon.height-this.busDistanceToLine; +// else + // console.log("CASE 3"); + _object.posY = this.returnLinePosition+((_object.type=="stop")?0:distanceToLine); + _object.posX =this.getLineWidth()-(busLinePosition-(this.getLineWidth()+h))+this.getLineMargin()-_objWidth/2; + if(_object.type=='bus') + _object.labelYPosition=_object.posY+this.textSeparation+_objHeight; + + } + else + { + // console.log("CASE 4"); + _object.posX = this.getLineMargin()-(distanceToLine+((_object.type=="stop")?0:this.maxBusLabelWidth)); + _object.posY = this.returnLinePosition-(busLinePosition-(2*this.getLineWidth()+h)) -((_object.type=="stop")?0:_objHeight/2); + if(_object.type=='bus') + _object.labelYPosition=_object.posY-this.textSeparation; + } + + } + + } + + paintBus() + { + //this.repaint(); + this.ctx.save(); + //console.log(counter); + this.ctx.font="10px Georgia"; + var metrics=this.ctx.measureText("anyText"); + metrics.height=parseInt(this.ctx.font.match(/\d+/), 10); + this.ctx.clearRect(this.getLineMargin()+2.5, this.forwarLinePosition-(this.vehicleIcon.height+metrics.height+this.textSeparation+this.busDistanceToLine+1), this.canvas.width-2*this.getLineMargin()-5, (this.vehicleIcon.height+metrics.height+this.textSeparation));//Tenemos que tener el cuadrado de los buses. + + //this.ctx.clearRect(0, this.forwarLinePosition-this.vehicleIcon.height-metrics.height-this.textSeparation, this.canvas.width, this.vehicleIcon.height+metrics.height+this.textSeparation-3);//Tenemos que tener el cuadrado de los buses. + if(this.drawReturnUpside==true && this.patternType=="linear") + this.ctx.clearRect( this.getLineMargin()+2.5,this.returnLinePosition-(this.vehicleIcon.height+metrics.height+this.textSeparation+this.busDistanceToLine+1), this.canvas.width-2*this.getLineMargin()-5, (this.vehicleIcon.height+metrics.height+this.textSeparation));//Tenemos que tener el cuadrado de los buses. + else + this.ctx.clearRect( this.getLineMargin()+2.5,this.returnLinePosition+this.busDistanceToLine, this.canvas.width-2*this.getLineMargin()-5, (this.vehicleIcon.height+metrics.height+this.textSeparation));//Tenemos que tener el cuadrado de los buses. + + for(var pos=0;posbus.posX) + xToDraw=bus.lastPosition.posX-(bus.lastPosition.posX-bus.posX)/this.steps*this.counter; + else + xToDraw=bus.lastPosition.posX+((bus.posX-bus.lastPosition.posX)/this.steps)*this.counter; + + }*/ + this.ctx.fillStyle = bus.fill; + // this.ctx.fillRect(xToDraw, yToDraw, this.vehicleIcon.width, this.vehicleIcon.height); + if(this.vehicleAlternColorCallBack==undefined) + this.ctx.drawImage(this.vehicleIcon,xToDraw, yToDraw,this.vehicleIcon.width, this.vehicleIcon.height); + else + this.ctx.drawImage(this.vehicleAlternColorCallBack(bus),xToDraw, yToDraw,this.vehicleIcon.width, this.vehicleIcon.height); + + + //console.log(metrics); +// var fontPostY=(bus.direction==0)?bus.posY-this.textSeparation:(this.drawReturnUpside==true)?bus.posY-this.textSeparation:bus.posY+metrics.height+this.vehicleIcon.height+this.textSeparation; +// this.ctx.fillText(bus.identifier,xToDraw,fontPostY ); + var fontPostY=0; + //console.log(metrics); + if(this.patternType=="linear") + fontPostY=(bus.direction==0)?bus.posY-this.textSeparation:(this.drawReturnUpside==true)?bus.posY-this.textSeparation:bus.posY+metrics.height+this.vehicleIcon.height+this.textSeparation; + else + fontPostY=bus.labelYPosition; + console.log("fontPostY="+fontPostY); + this.ctx.fillText(bus.identifier,xToDraw,fontPostY ); +// ctx.stroke(); + } + this.ctx.restore(); + + } + repaint() + { + this.drawLine(0); + if(this.__showReturn) + this.drawLine(1); + this.paintStop(); + } + paint() + { + if(this.resize) + { + this.repaint(); + } + if(this.buses== undefined || this.buses==null) + { + console.warn("no bus to paint"); + return; + } + this.paintBus(); + this.resized=false; + // + //this.ctx.translate(25,20); + } + draw() + { + this.ctx.save(); + this.ctx.fillStyle = 'rgb(255,255,0)'; + + this.ctx.translate(40,40); + this.ctx.fillRect(0,0,25,25); + this.ctx.restore(); + this.ctx.save(); + this.ctx.fillStyle = 'rgb(255,0,0)'; + + //this.ctx.restore(); + // + this.ctx.translate(40,40); + this.ctx.drawImage(this.vehicleIcon,0,0); + this.ctx.fillRect(5,5,5,5); + this.ctx.restore(); + + + } + +} + + + +function myFunction() +{ + + + + var buses=[{ + id:33, + width: 20, + height: 10, + direction: 0, + projection: Math.random(), + identifier:"ppu-4111", + fill: "#444444", + isDragging: false + },{ + id:23, + width: 20, + height: 10, + direction: 0, + projection:Math.random(), + identifier:"ppu-4211", + fill: "#444444", + isDragging: false, + isScheduledService: false, + freqStartTime:1537843461864, + },{ + id:1, + width: 20, + height: 10, + direction: 1, + projection:Math.random(), + identifier:"ppu-4311", + fill: "#444444", + isDragging: false + }]; + synoptic.setBuses(buses); + synoptic.steps=100; + synoptic.counter=0; + console.log("llamando a paint bus"); + //window.requestAnimationFrame(test.animateBus); + synoptic.animateBus(20); +// test.paintBus(); +} diff --git a/transitclockWebapp/src/main/webapp/synoptic/mybus.png b/transitclockWebapp/src/main/webapp/synoptic/mybus.png new file mode 100644 index 000000000..60ce127e3 Binary files /dev/null and b/transitclockWebapp/src/main/webapp/synoptic/mybus.png differ diff --git a/transitclockWebapp/src/main/webapp/synoptic/mybusRed.png b/transitclockWebapp/src/main/webapp/synoptic/mybusRed.png new file mode 100644 index 000000000..a4a465a99 Binary files /dev/null and b/transitclockWebapp/src/main/webapp/synoptic/mybusRed.png differ diff --git a/transitimeWebapp/src/main/webapp/template/header.jsp b/transitclockWebapp/src/main/webapp/template/header.jsp similarity index 50% rename from transitimeWebapp/src/main/webapp/template/header.jsp rename to transitclockWebapp/src/main/webapp/template/header.jsp index c57074596..c931ca504 100644 --- a/transitimeWebapp/src/main/webapp/template/header.jsp +++ b/transitclockWebapp/src/main/webapp/template/header.jsp @@ -1,3 +1,3 @@ <%-- Contains a header that should be included into most web pages --%> - \ No newline at end of file + \ No newline at end of file diff --git a/transitimeWebapp/src/main/webapp/template/includes.jsp b/transitclockWebapp/src/main/webapp/template/includes.jsp similarity index 83% rename from transitimeWebapp/src/main/webapp/template/includes.jsp rename to transitclockWebapp/src/main/webapp/template/includes.jsp index 0516814c1..3bed229aa 100644 --- a/transitimeWebapp/src/main/webapp/template/includes.jsp +++ b/transitclockWebapp/src/main/webapp/template/includes.jsp @@ -1,7 +1,7 @@ <%-- This file contains includes that can be included with every file --%> <%-- Load in JQuery --%> - + <%-- Load in JQuery UI javascript and css to set general look and feel, such as for tooltips --%> @@ -13,9 +13,11 @@ \ No newline at end of file + diff --git a/transitimeWebapp/src/main/webapp/testing/index.jsp b/transitclockWebapp/src/main/webapp/testing/index.jsp similarity index 100% rename from transitimeWebapp/src/main/webapp/testing/index.jsp rename to transitclockWebapp/src/main/webapp/testing/index.jsp diff --git a/transitimeWebapp/src/main/webapp/tracker/index.jsp b/transitclockWebapp/src/main/webapp/tracker/index.jsp similarity index 96% rename from transitimeWebapp/src/main/webapp/tracker/index.jsp rename to transitclockWebapp/src/main/webapp/tracker/index.jsp index 69533001f..c63030646 100644 --- a/transitimeWebapp/src/main/webapp/tracker/index.jsp +++ b/transitclockWebapp/src/main/webapp/tracker/index.jsp @@ -4,7 +4,7 @@ - + Tracking diff --git a/transitimeWebapp/src/main/webapp/tracker/store.jsp b/transitclockWebapp/src/main/webapp/tracker/store.jsp similarity index 100% rename from transitimeWebapp/src/main/webapp/tracker/store.jsp rename to transitclockWebapp/src/main/webapp/tracker/store.jsp diff --git a/transitimeWebapp/src/main/webapp/welcome/index.jsp b/transitclockWebapp/src/main/webapp/welcome/index.jsp similarity index 84% rename from transitimeWebapp/src/main/webapp/welcome/index.jsp rename to transitclockWebapp/src/main/webapp/welcome/index.jsp index 6da6860e0..e336fd658 100644 --- a/transitimeWebapp/src/main/webapp/welcome/index.jsp +++ b/transitclockWebapp/src/main/webapp/welcome/index.jsp @@ -1,4 +1,4 @@ -<%@page import="org.transitime.db.webstructs.WebAgency"%> +<%@page import="org.transitclock.db.webstructs.WebAgency"%> <%@page import="java.util.Collection"%> <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> @@ -63,6 +63,8 @@ for (WebAgency webAgency : webAgencies) { Reports API Status + Synoptic +<%-- Extensions--%> <% } diff --git a/transitime/.classpath b/transitime/.classpath deleted file mode 100644 index 2506097df..000000000 --- a/transitime/.classpath +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/transitime/.project b/transitime/.project deleted file mode 100644 index 64ea8653f..000000000 --- a/transitime/.project +++ /dev/null @@ -1,54 +0,0 @@ - - - mainProject - - - - - - org.eclipse.wst.common.project.facet.core.builder - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.hibernate.eclipse.console.hibernateBuilder - - - - - org.jboss.tools.jst.web.kb.kbbuilder - - - - - org.jboss.tools.cdi.core.cdibuilder - - - - - org.eclipse.wst.validation.validationbuilder - - - - - org.eclipse.m2e.core.maven2Builder - - - - - - org.eclipse.jem.workbench.JavaEMFNature - org.eclipse.wst.common.modulecore.ModuleCoreNature - org.eclipse.m2e.core.maven2Nature - org.eclipse.jdt.core.javanature - org.hibernate.eclipse.console.hibernateNature - org.jboss.tools.jst.web.kb.kbnature - org.jboss.tools.cdi.core.cdinature - org.eclipse.wst.common.project.facet.core.nature - - diff --git a/transitime/pom.xml b/transitime/pom.xml deleted file mode 100644 index 3857691d2..000000000 --- a/transitime/pom.xml +++ /dev/null @@ -1,367 +0,0 @@ - - 4.0.0 - transitime - transitimeCore - 0.0.1-SNAPSHOT - - - UTF-8 - - - - - - - false - - bintray-kevinlee-maven - bintray - http://dl.bintray.com/kevinlee/maven - - - - - - onejar-maven-plugin.googlecode.com - http://onejar-maven-plugin.googlecode.com/svn/mavenrepo - - - - - - com.simontuffs - one-jar-boot - 0.97.3 - - - - - mysql - mysql-connector-java - 5.1.35 - - - org.postgresql - postgresql - 9.3-1103-jdbc41 - - - org.hibernate - hibernate-core - 4.3.9.Final - - - org.hibernate.common - hibernate-commons-annotations - 4.0.5.Final - - - org.hibernate - hibernate-c3p0 - 4.3.9.Final - - - - - xml-apis - xml-apis - 1.4.01 - - - - - ch.qos.logback - logback-core - 1.1.2 - - - ch.qos.logback - logback-classic - 1.1.2 - - - org.slf4j - slf4j-api - 1.7.2 - - - - - com.amazonaws - aws-java-sdk - 1.9.13 - - - - - org.apache.commons - commons-lang3 - 3.3.2 - - - - - org.jdom - jdom - 2.0.2 - - - - - com.google.transit - gtfs-realtime-bindings - 0.0.4 - - - - - commons-cli - commons-cli - 1.2 - - - - - - - org.hornetq - hornetq-core-client - 2.3.25.Final - - - org.hornetq - hornetq-jms-client - 2.3.25.Final - - - - - org.java-websocket - Java-WebSocket - 1.3.0 - - - - - org.json - json - 20140107 - - - - - org.apache.commons - commons-csv - 1.1 - - - - - net.jcip - jcip-annotations - 1.0 - - - - - org.jasypt - jasypt - 1.9.2 - - - - - javax.mail - mail - 1.4.7 - - - - - junit - junit - 4.11 - test - - - - - com.google.guava - guava - 18.0 - - - - javax.servlet - javax.servlet-api - 3.0.1 - provided - - - - - - - maven-compiler-plugin - 2.5.1 - - 1.7 - 1.7 - - - - org.apache.maven.plugins - maven-shade-plugin - - - shade-UpdateTravelTimes - package - - shade - - - - - org.transitime.applications.UpdateTravelTimes - - - target/UpdateTravelTimes.jar - - - - shade-Core - package - - shade - - - - - org.transitime.applications.Core - - - target/Core.jar - - - - shade-SchemaGenerator - package - - shade - - - - - org.transitime.applications.SchemaGenerator - - - target/SchemaGenerator.jar - - - - shade-ScheduleGenerator - package - - shade - - - - - org.transitime.applications.ScheduleGenerator - - - target/ScheduleGenerator.jar - - - - shade-GtfsFileProcessor - package - - shade - - - - - org.transitime.applications.GtfsFileProcessor - - - target/GtfsFileProcessor.jar - - - - shade-RmiQuery - package - - shade - - - - - org.transitime.applications.RmiQuery - - - target/RmiQuery.jar - - - - shade-CreateAPIKey - package - - shade - - - - - org.transitime.applications.CreateAPIKey - - - target/CreateAPIKey.jar - - - - shade-CreateWebAgency - package - - shade - - - - - org.transitime.applications.CreateWebAgency - - - target/CreateWebAgency.jar - - - - - - - - - - - - - diff --git a/transitime/src/main/java/org/transitime/avl/GtfsRealtimeModule.java b/transitime/src/main/java/org/transitime/avl/GtfsRealtimeModule.java deleted file mode 100644 index 1977f028d..000000000 --- a/transitime/src/main/java/org/transitime/avl/GtfsRealtimeModule.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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.transitime.avl; - -import java.io.InputStream; -import java.util.Collection; -import org.transitime.db.structs.AvlReport; -import org.transitime.feed.gtfsRt.GtfsRtVehiclePositionsReader; -import org.transitime.modules.Module; - -/** - * 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 { - - /********************** 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; - } - - /* (non-Javadoc) - * @see org.transitime.avl.AvlModule#processData(java.io.InputStream) - */ - @Override - protected Collection processData(InputStream inputStream) - throws Exception { - Collection avlReports = - GtfsRtVehiclePositionsReader.process(inputStream); - - return avlReports; - } - - /** - * Just for debugging - */ - public static void main(String[] args) { - // Create a GtfsRealtimeModule for testing - Module.start("org.transitime.avl.GtfsRealtimeModule"); - } - -} diff --git a/transitime/src/main/java/org/transitime/avl/PlaybackModule.java b/transitime/src/main/java/org/transitime/avl/PlaybackModule.java deleted file mode 100644 index 6e3c1e36b..000000000 --- a/transitime/src/main/java/org/transitime/avl/PlaybackModule.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * 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.transitime.avl; - -import java.util.Date; -import java.util.List; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.transitime.applications.Core; -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; - -/** - * 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 - private long dbReadBeginTime; - - /*********** Configurable Parameters for this module ***********/ - private static String getPlaybackVehicleId() { - return playbackVehicleId.getValue(); - } - private static StringConfigValue playbackVehicleId = - new StringConfigValue("transitime.avl.playbackVehicleId", - "", - "ID of vehicle to playback."); - - private static String getPlaybackStartTimeStr() { - return playbackStartTimeStr.getValue(); - } - private static StringConfigValue playbackStartTimeStr = - new StringConfigValue("transitime.avl.playbackStartTime", - "", - "Date and time of when to start the playback."); - - - /********************* 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()) { - System.err.println("Parameters not set. See log file for details. Exiting."); - System.exit(-1); - } - - // 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 " + - "transitime.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 " + - "transitime.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; - } - } - - /** - * Gets a batch of AVl data from the database - * @return - */ - private 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; - } - - /* Reads AVL data from db and processes it - * (non-Javadoc) - * @see java.lang.Runnable#run() - */ - @Override - public void run() { - // Keep running as long as not trying to access in the future. - while (dbReadBeginTime < System.currentTimeMillis()) { - List avlReports = getBatchOfAvlReportsFromDb(); - - // Process the AVL Reports read in. - for (AvlReport avlReport : avlReports) { - 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); - } - } - - 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/core/BlockAssigner.java b/transitime/src/main/java/org/transitime/core/BlockAssigner.java deleted file mode 100644 index 63fc665c8..000000000 --- a/transitime/src/main/java/org/transitime/core/BlockAssigner.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * 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.transitime.core; - -import java.util.Collection; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.transitime.applications.Core; -import org.transitime.db.structs.AvlReport; -import org.transitime.db.structs.Block; -import org.transitime.db.structs.Trip; -import org.transitime.gtfs.DbConfig; -import org.transitime.utils.Time; - -/** - * 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) { - 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 { - 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; - } - - /** - * 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/transitime/src/main/java/org/transitime/core/predAccuracy/gtfsrt/GTFSRealtimePredictionAccuracyModule.java b/transitime/src/main/java/org/transitime/core/predAccuracy/gtfsrt/GTFSRealtimePredictionAccuracyModule.java deleted file mode 100644 index 9341a5b95..000000000 --- a/transitime/src/main/java/org/transitime/core/predAccuracy/gtfsrt/GTFSRealtimePredictionAccuracyModule.java +++ /dev/null @@ -1,225 +0,0 @@ -/* - * 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.transitime.core.predAccuracy.gtfsrt; - -import java.net.URL; -import java.util.Date; -import java.util.List; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.transitime.applications.Core; -import org.transitime.config.StringConfigValue; -import org.transitime.core.predAccuracy.PredAccuracyPrediction; -import org.transitime.core.predAccuracy.PredictionAccuracyModule; -import org.transitime.db.structs.Trip; -import org.transitime.gtfs.DbConfig; -import com.google.transit.realtime.GtfsRealtime.FeedEntity; -import com.google.transit.realtime.GtfsRealtime.FeedMessage; -import com.google.transit.realtime.GtfsRealtime.TripUpdate; -import com.google.transit.realtime.GtfsRealtime.TripUpdate.StopTimeUpdate; - -/** - * Reads in external prediction data from a GTFS realtime trip updates feed and stores the data in - * memory. Then when arrivals/departures occur the prediction accuracy can be - * determined and stored. - * - * @author Sean Og Crudden - * - */ -public class GTFSRealtimePredictionAccuracyModule extends PredictionAccuracyModule { - - private static final Logger logger = LoggerFactory - .getLogger(GTFSRealtimePredictionAccuracyModule.class); - - /********************** Config Params **************************/ - - private static final StringConfigValue gtfsTripUpdateUrl = - new StringConfigValue("transitime.predAccuracy.gtfsTripUpdateUrl", - "http://127.0.0.1:8091/trip-updates", - "URL to access gtfs-rt trip updates."); - - - /** - * @return the gtfstripupdateurl - */ - public static StringConfigValue getGtfstripupdateurl() { - return gtfsTripUpdateUrl; - } - - - - /********************** Member Functions **************************/ - - /** - * @param agencyId - */ - public GTFSRealtimePredictionAccuracyModule(String agencyId) { - super(agencyId); - } - - - - /** - * Gets GTFS realtime feed all routes from URL and return FeedMessage - * - * @return the FeedMessage to be processed - */ - private FeedMessage getExternalPredictions() { - - // Will just read all data from gtfs-rt url - URL url=null; - logger.info("Getting predictions from API using URL={}", getGtfstripupdateurl().getValue()); - - try { - // Create the connection - url = new URL(getGtfstripupdateurl().getValue()); - - FeedMessage feed = FeedMessage.parseFrom(url.openStream()); - logger.info("Prediction read successfully from URL={}",getGtfstripupdateurl().getValue()); - return feed; - } catch (Exception e) { - logger.error("Problem when getting data from GTFS realtime trip updates URL={}", - url, e); - return null; - } - } - - /** - * Takes data from XML Document object and processes it and - * calls storePrediction() on the predictions. - * - * @param feed - * @param predictionsReadTime - */ - private void processExternalPredictions( - FeedMessage feed, - Date predictionsReadTime) { - - // If couldn't read data from feed then can't process it - if (feed == null) - return; - - // So can look up direction in database - DbConfig dbConfig = Core.getInstance().getDbConfig(); - - logger.info("Processing GTFS-rt feed....."); - for (FeedEntity entity : feed.getEntityList()) - { - if (entity.hasTripUpdate()) - { - TripUpdate update = entity.getTripUpdate(); - List stopTimes = update.getStopTimeUpdateList(); - for(StopTimeUpdate stopTime : stopTimes) - { - if(stopTime.hasArrival()||stopTime.hasDeparture()) - { - - String direction=null; - - if(update.getTrip().hasDirectionId()) - direction=""+update.getTrip().getDirectionId(); - - if (update.getTrip() != null) { - Trip trip = dbConfig.getTrip(update.getTrip().getTripId()); - if (trip != null) { - direction = trip.getDirectionId(); - } else { - logger.error("Got tripTag={} but no such trip in " - + "the configuration.", update.getTrip().getTripId()); - } - } - - logger.info("Storing external prediction routeId={}, " - + "directionId={}, tripId={}, vehicleId={}, " - + "stopId={}, prediction={}, isArrival={}", - update.getTrip().getRouteId(), direction, update.getTrip().getTripId(), update.getVehicle().getId(), stopTime.getStopId(), - new Date(stopTime.getArrival().getTime()*1000), true); - - logger.info("Prediction in milliseonds is {} and converted is {}",stopTime.getArrival().getTime()*1000, new Date(stopTime.getArrival().getTime()*1000)); - - if(stopTime.hasArrival()) - { - PredAccuracyPrediction pred = new PredAccuracyPrediction( - update.getTrip().getRouteId(), - direction, - stopTime.getStopId(), - update.getTrip().getTripId(), - update.getVehicle().getId(), - new Date(stopTime.getArrival().getTime()*1000) , - new Date(feed.getHeader().getTimestamp()*1000), - true, - new Boolean(false), - "GTFS-rt"); - - storePrediction(pred); - } - if(stopTime.hasDeparture()) - { - PredAccuracyPrediction pred = new PredAccuracyPrediction( - update.getTrip().getRouteId(), - direction, - stopTime.getStopId(), - update.getTrip().getTripId(), - update.getVehicle().getId(), - new Date(stopTime.getDeparture().getTime()*1000) , - new Date(feed.getHeader().getTimestamp()*1000), - false, - new Boolean(false), - "GTFS-rt"); - - storePrediction(pred); - } - } - else - { - logger.debug("No predictions for vehicleId={} for stop={}",update.getVehicle().getId(),stopTime.getStopId()); - } - } - } - } - } - - /** - * Processes both the internal and external predictions - * - * @param routesAndStops - * @param predictionsReadTime - * For keeping track of when the predictions read in. Used for - * determining length of predictions. Should be the same for all - * predictions read in during a polling cycle even if the - * predictions are read at slightly different times. By using the - * same time can easily see from data in db which internal and - * external predictions are associated with each other. - */ - @Override - protected void getAndProcessData(List routesAndStops, - Date predictionsReadTime) { - // Process internal predictions - super.getAndProcessData(routesAndStops, predictionsReadTime); - - logger.info("Calling GTFSRealtimePredictionAccuracyModule." - + "getAndProcessData()"); - - // Get data for all items in the GTFS-RT trip updates feed - FeedMessage feed = getExternalPredictions(); - - processExternalPredictions(feed, predictionsReadTime); - - } -} diff --git a/transitime/src/main/java/org/transitime/custom/lametro/LametroNextBusAvlModule.java b/transitime/src/main/java/org/transitime/custom/lametro/LametroNextBusAvlModule.java deleted file mode 100644 index 0bd46034f..000000000 --- a/transitime/src/main/java/org/transitime/custom/lametro/LametroNextBusAvlModule.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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.transitime.custom.lametro; - -import org.transitime.avl.NextBusAvlModule; - -/** - * For lametro agency the block assignments from the feed don't - * match to the GTFS data. Therefore this module must be used - * for the sfmta AVL feed. - * - * @author SkiBu Smith - * - */ -public class LametroNextBusAvlModule extends NextBusAvlModule { - - /** - * @param agencyId - */ - public LametroNextBusAvlModule(String agencyId) { - super(agencyId); - } - - /** - * At least for sfmta agency they don't use a leading 0 in the block ID in - * the GTFS data. Therefore to match strip out leading zeros from the block - * ID here. - * - * @param originalBlockIdFromFeed - * the block ID to be modified - * @return the modified block ID that corresponds to the GTFS data - */ - @Override - protected String processBlockId(String originalBlockIdFromFeed) { - String block = originalBlockIdFromFeed; - while (block != null && block.startsWith("0")) - block = block.substring(1); - return block; - } - -} diff --git a/transitime/src/main/java/org/transitime/db/hibernate/DataDbLogger.java b/transitime/src/main/java/org/transitime/db/hibernate/DataDbLogger.java deleted file mode 100644 index d5e58759d..000000000 --- a/transitime/src/main/java/org/transitime/db/hibernate/DataDbLogger.java +++ /dev/null @@ -1,647 +0,0 @@ -/* - * 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.transitime.db.hibernate; - -import java.net.SocketTimeoutException; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.LinkedBlockingQueue; - -import org.hibernate.HibernateException; -import org.hibernate.Session; -import org.hibernate.SessionFactory; -import org.hibernate.Transaction; -import org.hibernate.exception.GenericJDBCException; -import org.hibernate.exception.JDBCConnectionException; -import org.hibernate.exception.SQLGrammarException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.transitime.db.structs.AvlReport; -import org.transitime.logging.Markers; -import org.transitime.utils.IntervalTimer; -import org.transitime.utils.Time; -import org.transitime.utils.threading.NamedThreadFactory; - -/** - * DataDbLogger is for storing to the db a stream of data objects. It is intended - * for example for storing AVL reports, vehicle matches, arrivals, departures, etc. - * There can be quite a large volume of this type of data. - * - * The database might not always be available. It could be vacuumed, restarted, - * moved, etc. When this happens don't want the data to be lost and don't want - * to tie up the core predictor. Therefore a queue is used to log objects. - * This makes the application far more robust with respect to database issues. - * The application simply calls add(Object o) to add the object to be stored - * to the queue. - * - * A separate thread is used to read from the queue and write the data to the - * database. If the queue starts filling up then error messages are e-mailed - * to users alerting them that there is a problem. E-mail messages are also - * sent out when the queue level is going down again. - * - * A goal with this class was to make the writing to the database is - * efficient as possible. Therefore the objects are written in batches. - * This reduces network traffic as well as database load. But this did - * make handling exceptions more complicated. If there is an exception - * with a batch then each item is individually written so that don't - * lose any data. - * - * When in playback mode then don't want to store the data because it would - * interfere with data stored when the application was run in real time. - * Therefore when running in playback mode set shouldStoreToDb to true - * when calling getDataDbLogger(). - * - * @author SkiBu Smith - * - */ -public class DataDbLogger { - - // For when cannot connect to data the length of time in msec between retries - private static final long TIME_BETWEEN_RETRIES = 2 * Time.MS_PER_SEC; - - private static final int QUEUE_CAPACITY = 100000; - - // The queue capacity levels when an error message should be e-mailed out. - // The max value should be 1.0. - private final double levels[] = { 0.5, 0.8, 1.00 }; - - // The queue that objects to be stored are placed in - private BlockingQueue queue = new LinkedBlockingQueue(QUEUE_CAPACITY); - - // When running in playback mode where getting AVLReports from database - // instead of from an AVL feed, then debugging and don't want to store - // derived data into the database because that would interfere with the - // derived data that was already stored in real time. For that situation - // shouldStoreToDb should be set to false. - private final boolean shouldStoreToDb; - - // Used by add(). If queue filling up to 25% and shouldPauseToReduceQueue is - // true then will pause the calling thread for a few seconds so that more - // objects can be written out and not have the queue fill up. - private final boolean shouldPauseToReduceQueue; - - // For keeping track of index into levels, which level of capacity of - // queue being used. When level changes then an e-mail is sent out warning - // the operators. - private double indexOfLevelWhenMessageLogged = 0; - - // For keeping track of maximum capacity of queue that was used. - // Used for logging when queue use is going down. - private double maxQueueLevel = 0.0; - - // This is a singleton class that only returns a single object per agencyId. - private static Map dataDbLoggerMap = - new HashMap(1); - - // So can access agencyId for logging messages - private String agencyId; - - // The Session for writing data to db - private SessionFactory sessionFactory; - - private static final Logger logger = - LoggerFactory.getLogger(DataDbLogger.class); - - /********************** Member Functions **************************/ - - /** - * Factory method. Returns the singleton db logger for the specified - * agencyId. - * - * @param agencyId - * Id of database to be written to - * @param shouldStoreToDb - * Specifies whether data should actually be written to db. If in - * playback mode and shouldn't write data to db then set to - * false. - * @param shouldPauseToReduceQueue - * Specifies if should pause the thread calling add() if the - * queue is filling up. Useful for when in batch mode and dumping - * a whole bunch of data to the db really quickly. - * @return The DataDbLogger for the specified agencyId - */ - public static DataDbLogger getDataDbLogger(String agencyId, - boolean shouldStoreToDb, boolean shouldPauseToReduceQueue) { - synchronized (dataDbLoggerMap) { - DataDbLogger logger = dataDbLoggerMap.get(agencyId); - if (logger == null) { - logger = new DataDbLogger(agencyId, shouldStoreToDb, - shouldPauseToReduceQueue); - dataDbLoggerMap.put(agencyId, logger); - } - return logger; - } - } - - /** - * Constructor. Private so that factory method getDataDbLogger() has to be - * used. Starts up separate thread that actually reads from queue and stores - * the data. - * - * @param agencyId - * Id of database to be written to - * @param shouldStoreToDb - * Specifies whether data should actually be written to db. If in - * playback mode and shouldn't write data to db then set to - * false. - * @param shouldPauseToReduceQueue - * Specifies if should pause the thread calling add() if the - * queue is filling up. Useful for when in batch mode and dumping - * a whole bunch of data to the db really quickly. - */ - private DataDbLogger(String agencyId, boolean shouldStoreToDb, - boolean shouldPauseToReduceQueue) { - this.agencyId = agencyId; - this.shouldStoreToDb = shouldStoreToDb; - this.shouldPauseToReduceQueue = shouldPauseToReduceQueue; - - // Create the reusable heavy weight session factory - sessionFactory = HibernateUtils.getSessionFactory(agencyId); - - // Start up separate thread that reads from the queue and - // actually stores the data - NamedThreadFactory threadFactory = new NamedThreadFactory(getClass().getSimpleName()); - ExecutorService executor = Executors.newSingleThreadExecutor(threadFactory); - executor.execute(new Runnable() { - public void run() { - processData(); - } - }); - - } - - /** - * Returns how much capacity of the queue is being used up. - * - * @return a value between 0.0 and 1.0 indicating how much of queue being used - */ - public double queueLevel() { - int remainingCapacity = queue.remainingCapacity(); - int totalCapacity = queue.size() + remainingCapacity; - double level = 1.0 - (double) remainingCapacity / totalCapacity; - return level; - } - - /** - * Returns how many items are in queue to be processed - * @return items in queue - */ - public int queueSize() { - return queue.size(); - } - - /** - * Returns the index into levels that the queue capacity is at. - * For determining if should send e-mail warning message. - * - * @param queueLevel - * @return - */ - private int indexOfLevel(double queueLevel) { - for (int i=0; i getClassNamesInQueue() { - Map classNamesMap = new HashMap(); - for (Object o : queue) { - String className = o.getClass().getName(); - Integer count = classNamesMap.get(className); - if (count == null) { - count = new Integer(0); - classNamesMap.put(className, count); - } - ++count; - } - return classNamesMap; - } - - /** - * Adds an object to be saved in the database to the queue. If queue is - * getting filled up then an e-mail will be sent out indicating there is a - * problem. The queue levels at which an e-mail is sent out is specified by - * levels. If queue has reached capacity then an error message is logged. - * - * @param o - * The object that should be logged to the database - * @return True if OK (object added to queue or logging disabled). False if - * queue was full. - */ - public boolean add(Object o) { - // If in playback mode then don't want to store the - // derived data because it would interfere with the - // derived data already stored when was running in real time. - if (!shouldStoreToDb) - return true; - - // Add the object to the queue - boolean success = queue.offer(o); - - double level = queueLevel(); - int levelIndex = indexOfLevel(level); - // If reached a new level then output message e-mail to warn users - if (levelIndex > indexOfLevelWhenMessageLogged) { - indexOfLevelWhenMessageLogged = levelIndex; - String message = success ? - "DataDbLogger queue filling up " + - " for agencyId=" + agencyId +". It is now at " + - String.format("%.1f", level*100) + "% capacity with " + - queue.size() + " elements already in the queue." - : - "DataDbLogger queue is now completely full for agencyId=" + - agencyId + ". LOSING DATA!!!"; - - // Add to message the class names of the objects in the queue so - // can see what objects are causing the problem - Map classNamesCount = getClassNamesInQueue(); - for (String className : classNamesCount.keySet()) { - int count = classNamesCount.get(className); - message += " Class " + className + " count: " + count + ";"; - } - - // Log and send out email since this is an important issue - logger.error(Markers.email(), message); - } - - // If losing data then log such - if (!success) { - logger.error("DataDbLogger queue is now completely full for " + - "agencyId=" + agencyId + ". LOSING DATA!!! Failed to " + - "store object=[" + o + "]"); - } - - // Keep track of max queue level so can log it when queue level - // is decreasing again. - if (level > maxQueueLevel) - maxQueueLevel = level; - - // If shouldPauseToReduceQueue (because in batch mode or such) and - // if queue is starting to get more full then pause the calling - // thread for 10 seconds so that separate thread can clear out - // queue a bit. - if (shouldPauseToReduceQueue && level > 0.2) { - logger.info("Pausing thread adding data to DataDbLogger queue " + - "so that queue can be cleared out. Level={}%", - level*100.0); - Time.sleep(10 * Time.MS_PER_SEC); - } - - // Return whether was successful in adding object to queue - return success; - } - - /** - * Gets the next object from the head of the queue, waiting if - * necessary until an object becomes available. If the capacity - * level drops significantly from when last logged then that - * info is logged to indicate that the situation is getting better. - * When the queue level drops down below 10% of a specified level - * then an e-mail mail message is sent out indicating such. That way - * a supervisor can see that the queue is being cleared out. - * @return The object to be stored in the database - */ - private Object get() { - // Get the next object from the head of the queue - Object o = null; - do { - try { - o = queue.take(); - } catch (InterruptedException e) { - // If interrupted simply try again - } - } while (o == null); - - // Log if went below a capacity level - // See if queue dropped to 10% less than the previously logged level. - // Use a margin of 10% so that don't get flood of messages if queue - // oscillating around a level. - double level = queueLevel(); - int levelIndexIncludingMargin = indexOfLevel(level + 0.10); - if (levelIndexIncludingMargin < indexOfLevelWhenMessageLogged) { - logger.error(Markers.email(), "DataDbLogger queue emptying out somewhat " + - " for agencyId=" + agencyId +". It is now at " + - String.format("%.1f", level*100) + "% capacity with " + queue.size() + - " elements already in the queue. The maximum capacity was " + - String.format("%.1f", maxQueueLevel*100) + "%."); - indexOfLevelWhenMessageLogged = levelIndexIncludingMargin; - - // Reset the maxQueueLevel so can determine what next peak is - maxQueueLevel = level; - } - - // Return the result - return o; - } - - /** - * Returns whether queue has any elements in it that should be stored. - * @return true if queue has data that should be stored to db - */ - private boolean queueHasData() { - return !queue.isEmpty(); - } - - /** - * Store just a single object into data. This is slower than batching a few - * at a time. Should be used when the batching encounters an exception. This - * way can still store all of the good data from a batch. - * - * @param o - */ - private void processSingleObject(Object objectToBeStored) { - Session session = null; - Transaction tx = null; - try { - session = sessionFactory.openSession(); - tx = session.beginTransaction(); - logger.debug("Individually saving object {}", objectToBeStored); - session.save(objectToBeStored); - tx.commit(); - } catch (HibernateException e) { - if (tx != null) { - try { - tx.rollback(); - } catch (HibernateException e2) { - logger.error("Error rolling back transaction in " - + "processSingleObject(). ", e2); - } - } - } finally { - if (session != null) - session.close(); - } - } - - /** - * Returns true if the exception indicates that there is a problem connecting - * to the database as opposed to with the SQL. - * - * @param e - * @return - */ - private boolean shouldKeepTryingBecauseConnectionException(HibernateException e) { - // Need to know if it is a problem with the database not - // being accessible or if there is a problem with the SQL/data. - // If there is a problem accessibility of the database then - // want to keep trying writing the old data. But if it is - // a problem with the SQL/data then only want to try to write - // the good data from the batch a single time to make sure - // all good data is written. - // From javadocs for for org.hivernate.exception at - // http://docs.jboss.org/hibernate/orm/3.5/javadocs/org/hibernate/exception/package-frame.html - // can see that there are a couple of different exception types. - // From looking at documentation and testing found out that - // bad SQL is indicated by - // ConstraintViolationException - // DataException - // SQLGrammarException - // Appears that for bad connection could get: - // JDBCConnectionException (was not able to verify experimentally) - // GenericJDBCException (obtained when committing transaction with db turned off) - // So if exception is JDBCConnectionException or JDBCGenericException - // then should keep retrying until successful. - boolean keepTryingTillSuccessfull = e instanceof JDBCConnectionException || - e instanceof GenericJDBCException; - return keepTryingTillSuccessfull; - } - - /** - * Process a batch of data, as specified by BATCH_SIZE member. The goal - * is to batch a few db writes together to reduce load on network and on - * db machines. There this method will try to store multiple objects - * from the queue at once, up to the BATCH_SIZE. - * - * If there is an exception with an object being written then the - * batch of objects will be written individually so that all of the - * good data will still be stored. - * - * When looked at Hibernate documentation on batch writing there is - * mention of using: - * if (++batchingCounter % BATCH_SIZE == 0) { - * session.flush(); - * session.clear(); - * } - * But the above doesn't commit the data to the db until the transaction - * commit is done. Therefore the need here isn't true Hibernate batch - * processing. Instead, need to use a transaction for each batch. - */ - private void processBatchOfData() { - // Create an array for holding what is being written to db. If there - // is an exception with one of the objects, such as a constraint violation, - // then can try to write the objects one at a time to make sure that the - // the good ones are written. This way don't lose any good data even if - // an exception occurs while batching data. - List objectsForThisBatch = new ArrayList(HibernateUtils.BATCH_SIZE); - - Transaction tx = null; - Session session = null; - - try { - int batchingCounter = 0; - do { - // Get the object to be stored from the queue - Object objectToBeStored = get(); - - objectsForThisBatch.add(objectToBeStored); - } while (queueHasData() && ++batchingCounter < HibernateUtils.BATCH_SIZE); - - session = sessionFactory.openSession(); - tx = session.beginTransaction(); - for (Object objectToBeStored : objectsForThisBatch) { - // Write the data to the session. This doesn't yet - // actually write the data to the db though. That is only - // done when the session is flushed or committed. - logger.debug("DataDbLogger batch saving object={}", - objectToBeStored); - session.save(objectToBeStored); - } - - // Sometimes useful for debugging via the console - //System.err.println(new Date() + " Committing " - // + objectsForThisBatch.size() + " objects. " + queueSize() - // + " objects still in queue."); - logger.debug("Committing {} objects. {} objects still in queue.", - objectsForThisBatch.size(), queueSize()); - IntervalTimer timer = new IntervalTimer(); - - // Actually do the commit - tx.commit(); - - // Sometimes useful for debugging via the console - //System.err.println(new Date() + " Done committing. Took " - // + timer.elapsedMsec() + " msec"); - logger.debug("Done committing. Took {} msec", timer.elapsedMsec()); - - session.close(); - } catch (HibernateException e) { - e.printStackTrace(); - - // If there was a connection problem then create a whole session - // factory so that get new connections. - Throwable rootCause = HibernateUtils.getRootCause(e); - - if (rootCause instanceof SocketTimeoutException - || (rootCause instanceof SQLException - && rootCause.getMessage().contains("statement closed"))) { - logger.error(Markers.email(), - "Had a connection problem to the database for agencyId={}. " - + "Likely means that the db was rebooted or that the " - + "connection to it was lost. Therefore creating a new " - + "SessionFactory so get new connections.", agencyId); - HibernateUtils.clearSessionFactory(); - sessionFactory = HibernateUtils.getSessionFactory(agencyId); - } else { - // Rollback the transaction since it likely was not committed. - // Otherwise can get an error when using Postgres "ERROR: - // current transaction is aborted, commands ignored until end of - // transaction block". - try { - if (tx != null) - tx.rollback(); - } catch (HibernateException e2) { - logger.error( - "Error rolling back transaction after processing " - + "batch of data via DataDbLogger.", e2); - } - - // Close session here so that can process the objects - // individually - // using a new session. - try { - if (session != null) - session.close(); - } catch (HibernateException e2) { - logger.error("Error closing session after processing " - + "batch of data via DataDbLogger.", e2); - } - - // If it is a SQLGrammarException then also log the SQL to - // help in debugging. - String additionaInfo = e instanceof SQLGrammarException ? - " SQL=\"" + ((SQLGrammarException) e).getSQL() + "\"" - : ""; - Throwable cause = HibernateUtils.getRootCause(e); - logger.error("{} for database for project={} when batch writing " - + "objects: {}. Will try to write each object " - + "from batch individually. {}", - e.getClass().getSimpleName(), agencyId, - cause.getMessage(), additionaInfo); - } - - // Write each object individually so that the valid ones will be - // successfully written. - for (Object o : objectsForThisBatch) { - boolean shouldKeepTrying = false; - do { - try { - processSingleObject(o); - shouldKeepTrying = false; - } catch (HibernateException e2) { - // Need to know if it is a problem with the database not - // being accessible or if there is a problem with the SQL/data. - // If there is a problem accessibility of the database then - // want to keep trying writing the old data. But if it is - // a problem with the SQL/data then only want to try to write - // the good data from the batch a single time to make sure - // all good data is written. - if (shouldKeepTryingBecauseConnectionException(e2)) { - shouldKeepTrying = true; - logger.error("Encountered database connection " + - "exception so will sleep for {} msec and " + - "will then try again.", TIME_BETWEEN_RETRIES); - Time.sleep(TIME_BETWEEN_RETRIES); - } - - // Output message on what is going on - Throwable cause2 = HibernateUtils.getRootCause(e2); - logger.error(e2.getClass().getSimpleName() + " when individually writing object " + - o + ". " + - (shouldKeepTrying?"Will keep trying. " : "") + - "msg=" + cause2.getMessage()); - } - } while (shouldKeepTrying); - } - } - } - - /** - * This is the main method for processing data. It simply keeps on calling - * processBatchOfData() so that data is batched as efficiently as possible. - * Exceptions are caught such that this method will continue to run - * indefinitely. - */ - private void processData() { - while (true) { - try { - logger.debug("DataDbLogger.processData() processing batch of " + - "data to be stored in database."); - processBatchOfData(); - } catch (Exception e) { - logger.error("Error writing data to database via DataDbLogger. " + - "Look for ERROR in log file to see if the database classes " + - "were configured correctly.", e); - - // Don't try again right away because that would be wasteful - Time.sleep(TIME_BETWEEN_RETRIES); - } - } - } - - /** - * Just for doing some testing - * - * @param args - */ - public static void main(String[] args) { - DataDbLogger logger = getDataDbLogger("test", false, false); - - long initialTime = (System.currentTimeMillis() / 1000) * 1000; - - for (int i=0; i<25; ++i) - logger.add(new AvlReport("test", initialTime + i, 1.23, 4.56, null)); - - // This one should cause constraint problem with the second batch. - // Need to not retry for such an exception - logger.add(new AvlReport("test", initialTime, 1.23, 4.56, null)); - - for (int i=HibernateUtils.BATCH_SIZE; i<2*HibernateUtils.BATCH_SIZE;++i) - logger.add(new AvlReport("test", initialTime+i, 1.23, 4.56, null)); - - - // Wait for all data to be processed - while(logger.queueHasData()) - Time.sleep(1000); - } - -} diff --git a/transitime/src/main/java/org/transitime/ipc/servers/CommandsServer.java b/transitime/src/main/java/org/transitime/ipc/servers/CommandsServer.java deleted file mode 100644 index 7835a89b6..000000000 --- a/transitime/src/main/java/org/transitime/ipc/servers/CommandsServer.java +++ /dev/null @@ -1,99 +0,0 @@ -package org.transitime.ipc.servers; - -import java.rmi.RemoteException; -import java.util.Collection; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.transitime.avl.AvlExecutor; -import org.transitime.db.structs.AvlReport; -import org.transitime.ipc.data.IpcAvl; -import org.transitime.ipc.interfaces.CommandsInterface; -import org.transitime.ipc.rmi.AbstractServer; - -public class CommandsServer extends AbstractServer - implements CommandsInterface { - - // Should only be accessed as singleton class - private static CommandsServer singleton; - - private static final Logger logger = - LoggerFactory.getLogger(CommandsServer.class); - - /********************** Member Functions **************************/ - - /** - * Starts up the CommandsServer so that RMI calls can be used to control the - * server. This will automatically cause the object to continue to run and - * serve requests. - * - * @param agencyId - * @return the singleton CommandsServer object. Usually does not need to - * used since the server will be fully running. - */ - public static CommandsServer start(String agencyId) { - if (singleton == null) { - singleton = new CommandsServer(agencyId); - } - - if (!singleton.getAgencyId().equals(agencyId)) { - logger.error("Tried calling CommandsServer.start() for " + - "agencyId={} but the singleton was created for agencyId={}", - agencyId, singleton.getAgencyId()); - return null; - } - - return singleton; - } - - /** - * Constructor. Made private so that can only be instantiated by - * get(). Doesn't actually do anything since all the work is done in - * the superclass constructor. - * - * @param agencyId - * for registering this object with the rmiregistry - */ - private CommandsServer(String agencyId) { - super(agencyId, CommandsInterface.class.getSimpleName()); - } - - /** - * Called on server side via RMI when AVL data is to be processed - * - * @param avlData - * AVL data sent to server - * @return Null if OK, otherwise an error message - */ - @Override - public String pushAvl(IpcAvl avlData) throws RemoteException { - // Use AvlExecutor to actually process the data using a thread executor - AvlReport avlReport = new AvlReport(avlData); - logger.debug("Processing AVL report {}", avlReport); - AvlExecutor.getInstance().processAvlReport(avlReport); - - // Return that was successful - return null; - } - - /** - * Called on server side via RMI when AVL data is to be processed - * - * @param avlDataCollection - * AVL data sent to server - * @return Null if OK, otherwise an error message - */ - @Override - public String pushAvl(Collection avlDataCollection) throws RemoteException { - for (IpcAvl avlData : avlDataCollection) { - // Use AvlExecutor to actually process the data using a thread executor - AvlReport avlReport = new AvlReport(avlData); - logger.debug("Processing AVL report {}", avlReport); - AvlExecutor.getInstance().processAvlReport(avlReport); - } - - // Return that was successful - return null; - } - -} diff --git a/transitime/src/test/java/org/transitime/config/TestAPIKeyManager.java b/transitime/src/test/java/org/transitime/config/TestAPIKeyManager.java deleted file mode 100644 index 8a44d726d..000000000 --- a/transitime/src/test/java/org/transitime/config/TestAPIKeyManager.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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.transitime.config; - - -import junit.framework.TestCase; - -import org.transitime.config.ConfigFileReader.ConfigException; -import org.transitime.config.ConfigValue.ConfigParamException; -import org.transitime.db.webstructs.ApiKey; -import org.transitime.db.webstructs.ApiKeyManager; - -/** - * - * @author Sean Crudden - * - */ -public class TestAPIKeyManager extends TestCase { - - static String fileName = "testConfig.xml"; - - protected void setUp() throws Exception { - super.setUp(); - } - - protected void tearDown() throws Exception { - super.tearDown(); - } - - public void testAPIKeyManager() { - try { - - ConfigFileReader.processConfig(this.getClass().getClassLoader() - .getResource(fileName).getPath()); - - ApiKeyManager manager = ApiKeyManager.getInstance(); - - ApiKey apiKey = manager.generateApiKey("test", - "http://127.0.0.1:8080/transitime", "test@test.com", - "12345678", "test"); - - assert(manager.isKeyValid(apiKey.getKey())); - - } catch (ConfigException | ConfigParamException e) { - fail(e.toString()); - } - - } -} \ No newline at end of file diff --git a/transitime/src/test/java/org/transitime/config/TestConfig.java b/transitime/src/test/java/org/transitime/config/TestConfig.java deleted file mode 100644 index c933983d0..000000000 --- a/transitime/src/test/java/org/transitime/config/TestConfig.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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.transitime.config; - -import java.util.List; - -import org.transitime.config.ConfigFileReader.ConfigException; -import org.transitime.config.ConfigValue.ConfigParamException; -import org.transitime.configData.CoreConfig; - -import junit.framework.Assert; -import junit.framework.TestCase; - -/** - * - * @author Sean Crudden - * - */ -@SuppressWarnings("deprecation") -public class TestConfig extends TestCase { - - static String fileName = "testConfig.xml"; - - protected void setUp() throws Exception { - super.setUp(); - } - - protected void tearDown() throws Exception { - super.tearDown(); - } - - public void testReadConfigFile() { - try { - // Load in config file - ConfigFileReader.processConfig(this.getClass().getClassLoader() - .getResource(fileName).getPath()); - - // Get the module list param - List moduleList = CoreConfig.getOptionalModules(); - - Assert.assertTrue(moduleList.get(0).equals( - "org.transitime.avl.GtfsRealtimeModule")); - } catch (ConfigException | ConfigParamException e) { - fail(e.toString()); - } - - } - -} diff --git a/transitime/src/test/java/org/transitime/db/TestDatabase.java b/transitime/src/test/java/org/transitime/db/TestDatabase.java deleted file mode 100644 index eba81cc33..000000000 --- a/transitime/src/test/java/org/transitime/db/TestDatabase.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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.transitime.db; - -import java.util.Date; -import java.util.List; - -import junit.framework.TestCase; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.transitime.config.ConfigFileReader; -import org.transitime.configData.DbSetupConfig; -import org.transitime.db.structs.AvlReport; - -/** - * - * @author Sean Crudden - * - */ -public class TestDatabase extends TestCase { - - static String fileName = "testConfig.xml"; - - private static final Logger logger = LoggerFactory - .getLogger(TestDatabase.class); - - public void testDatabase() { - try { - - ConfigFileReader.processConfig(this.getClass().getClassLoader() - .getResource(fileName).getPath()); - - List avlReports = AvlReport.getAvlReportsFromDb( - new Date(), // beginTime - new Date(), // endTime - null, // vehicleId - null); // SQL clause - - assertTrue(avlReports!=null); - } catch (Exception e) { - logger.error("Error occurred when trying to access database for " - + "dbName={}. {}", DbSetupConfig.getDbName(), - e.getMessage(), e); - fail(e.toString()); - } - } - -} diff --git a/transitime/src/test/resources/postgres_hibernate.cfg.xml b/transitime/src/test/resources/postgres_hibernate.cfg.xml deleted file mode 100644 index 0713d35fb..000000000 --- a/transitime/src/test/resources/postgres_hibernate.cfg.xml +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - org.postgresql.Driver - - org.hibernate.dialect.PostgreSQLDialect - true - - - true - true - - - - 2 - - 20 - - 300 - - 50 - - - 25 - - - - - - - - - - diff --git a/transitime/src/test/resources/testConfig.xml b/transitime/src/test/resources/testConfig.xml deleted file mode 100644 index 76ce7fc4f..000000000 --- a/transitime/src/test/resources/testConfig.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - transitime.core.schedBasedPreds.SchedBasedPredsModule;org.transitime.avl.GtfsRealtimeModule - - true - - 02 - - - - http://127.0.0.1:8092/vehiclePositions - -10.725 - -5.35 - 51.35 - 55.45 - - - transiTime - localhost:5432 - postgresql - transiTime - welcome - - - postgres_hibernate.cfg.xml - - \ No newline at end of file diff --git a/transitimeApi/.classpath b/transitimeApi/.classpath deleted file mode 100644 index b9ced66c9..000000000 --- a/transitimeApi/.classpath +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/transitimeApi/.project b/transitimeApi/.project deleted file mode 100644 index 34bb507cb..000000000 --- a/transitimeApi/.project +++ /dev/null @@ -1,61 +0,0 @@ - - - transiTimeApi - - - - - - org.eclipse.wst.jsdt.core.javascriptValidator - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.wst.common.project.facet.core.builder - - - - - org.jboss.tools.jst.web.kb.kbbuilder - - - - - org.jboss.tools.cdi.core.cdibuilder - - - - - org.eclipse.wst.validation.validationbuilder - - - - - org.hibernate.eclipse.console.hibernateBuilder - - - - - org.eclipse.m2e.core.maven2Builder - - - - - - org.eclipse.jem.workbench.JavaEMFNature - org.eclipse.wst.common.modulecore.ModuleCoreNature - org.eclipse.jdt.core.javanature - org.eclipse.m2e.core.maven2Nature - org.eclipse.wst.common.project.facet.core.nature - org.jboss.tools.jst.web.kb.kbnature - org.jboss.tools.cdi.core.cdinature - org.hibernate.eclipse.console.hibernateNature - org.jboss.tools.jsf.jsfnature - org.eclipse.wst.jsdt.core.jsNature - - diff --git a/transitimeApi/README.md b/transitimeApi/README.md deleted file mode 100644 index 8f0a63cd1..000000000 --- a/transitimeApi/README.md +++ /dev/null @@ -1,22 +0,0 @@ -This is the a REST service which provides the information required to run a web application or mobile application based on transitTime. - -This can be built on its own by - -cd transitTimeApi
    -mvn install - -This will produce a war file which can be deployed on Tomcat. - -This server talks to core using RMI calls to get the information to support the REST service calls. - -To access the service a key is required to be provided in the URL. This key is compared against a key in the database. You can use the CreateAPIKey application in transiTime to create a test/demo key. - -The tables that store this information are create by running the ddl_xxxx_org_transitime_db_webstructs.sql in the database.(Where xxxx is the type of database you are using) - -Example URLs - -http://[server]:[port]/v1/transitime/key/[Key from CreateAPIKey]/agency/[agency id]/command/gtfs-rt/tripUpdates?format=human - -http://127.0.0.1:8093/v1/transitime/key/8a3273b0/agency/02/command/gtfs-rt/tripUpdates?format=human - -The comments in the supporting classes are the best source of information for RESTFul calls. diff --git a/transitimeApi/pom.xml b/transitimeApi/pom.xml deleted file mode 100644 index da75ec7ff..000000000 --- a/transitimeApi/pom.xml +++ /dev/null @@ -1,66 +0,0 @@ - - 4.0.0 - transitime - transitimeApi - war - 0.0.1-SNAPSHOT - transitimeApi - - - UTF-8 - - - - - javax.servlet - javax.servlet-api - 3.0.1 - provided - - - - javax.ws.rs - javax.ws.rs-api - 2.0 - - - - org.glassfish.jersey.media - jersey-media-moxy - 2.11 - - - - org.glassfish.jersey.containers - jersey-container-servlet - 2.11 - - - - transitime - transitimeCore - 0.0.1-SNAPSHOT - - - - - - api - - - - org.apache.maven.plugins - maven-compiler-plugin - 2.5.1 - true - - 1.7 - 1.7 - - - - - - - diff --git a/transitimeApi/src/main/java/org/transitime/api/gtfsRealtime/GtfsRtTripFeed.java b/transitimeApi/src/main/java/org/transitime/api/gtfsRealtime/GtfsRtTripFeed.java deleted file mode 100644 index 3e10015d3..000000000 --- a/transitimeApi/src/main/java/org/transitime/api/gtfsRealtime/GtfsRtTripFeed.java +++ /dev/null @@ -1,290 +0,0 @@ -/* - * 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.transitime.api.gtfsRealtime; - -import java.rmi.RemoteException; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.transitime.api.utils.AgencyTimezoneCache; -import org.transitime.ipc.clients.PredictionsInterfaceFactory; -import org.transitime.ipc.data.IpcPrediction; -import org.transitime.ipc.data.IpcPredictionsForRouteStopDest; -import org.transitime.utils.IntervalTimer; -import org.transitime.utils.Time; - -import com.google.transit.realtime.GtfsRealtime.FeedEntity; -import com.google.transit.realtime.GtfsRealtime.FeedHeader; -import com.google.transit.realtime.GtfsRealtime.FeedMessage; -import com.google.transit.realtime.GtfsRealtime.TripDescriptor; -import com.google.transit.realtime.GtfsRealtime.TripUpdate; -import com.google.transit.realtime.GtfsRealtime.TripUpdate.StopTimeEvent; -import com.google.transit.realtime.GtfsRealtime.TripUpdate.StopTimeUpdate.ScheduleRelationship; -import com.google.transit.realtime.GtfsRealtime.VehicleDescriptor; -import com.google.transit.realtime.GtfsRealtime.FeedHeader.Incrementality; -import com.google.transit.realtime.GtfsRealtime.TripUpdate.StopTimeUpdate; - -/** - * For creating GTFS-realtime trip feed. The data is obtained from the server - * via RMI. - *

    - * Note: for the trip feed predictions that are schedule based instead of GPS - * based the StopTimeEvent uncertainty is set to - * SCHED_BASED_PRED_UNCERTAINTY_VALUE so that the client can treat the - * prediction differently. If a vehicle is delayed and not moving then - * uncertainty is set to DELAYED_UNCERTAINTY_VALUE. And if a vehicle is late and - * the prediction is for a subsequent trip then uncertainty is set to - * LATE_AND_SUBSEQUENT_TRIP_UNCERTAINTY_VALUE. - * - * @author SkiBu Smith - * - */ -public class GtfsRtTripFeed { - - private final String agencyId; - - // For outputting date in GTFS-realtime format - private SimpleDateFormat gtfsRealtimeDateFormatter = - new SimpleDateFormat("yyyyMMdd"); - - // 25 minutes - private static final int PREDICTION_MAX_FUTURE_SECS = 25 * 60; - - // For when creating StopTimeEvent for schedule based prediction - // 5 minutes (300 seconds) - private static final int SCHED_BASED_PRED_UNCERTAINTY_VALUE = 5 * 60; - - // For when creating StopTimeEvent and the vehicle is delayed - private static final int DELAYED_UNCERTAINTY_VALUE = - SCHED_BASED_PRED_UNCERTAINTY_VALUE + 1; - - // If vehicle is late and prediction is for a subsequent trip then - // the predictions are not as certain because it is reasonably likely - // that another vehicle will take over the subsequent trip. Takes - // precedence over SCHED_BASED_PRED_UNCERTAINTY_VALUE. - private static final int LATE_AND_SUBSEQUENT_TRIP_UNCERTAINTY_VALUE = - DELAYED_UNCERTAINTY_VALUE + 1; - - private static final Logger logger = - LoggerFactory.getLogger(GtfsRtTripFeed.class); - - /********************** Member Functions **************************/ - - public GtfsRtTripFeed(String agencyId) { - this.agencyId = agencyId; - - this.gtfsRealtimeDateFormatter.setTimeZone(AgencyTimezoneCache - .get(agencyId)); - } - - /** - * Create TripUpdate for the trip. - * - * @param predsForTrip - * @return - */ - private TripUpdate createTripUpdate(List predsForTrip) { - // Create the parent TripUpdate object that is returned. - TripUpdate.Builder tripUpdate = TripUpdate.newBuilder(); - - // Add the trip descriptor information - IpcPrediction firstPred = predsForTrip.get(0); - TripDescriptor.Builder tripDescriptor = TripDescriptor.newBuilder(); - if (firstPred.getRouteId() != null) - tripDescriptor.setRouteId(firstPred.getRouteId()); - if (firstPred.getTripId() != null) { - tripDescriptor.setTripId(firstPred.getTripId()); - - long tripStartEpochTime = firstPred.getTripStartEpochTime(); - String tripStartDateStr = - gtfsRealtimeDateFormatter.format(new Date( - tripStartEpochTime)); - tripDescriptor.setStartDate(tripStartDateStr); - } - tripUpdate.setTrip(tripDescriptor); - - // Add the VehicleDescriptor information - VehicleDescriptor.Builder vehicleDescriptor = - VehicleDescriptor.newBuilder().setId(firstPred.getVehicleId()); - tripUpdate.setVehicle(vehicleDescriptor); - - // Add the StopTimeUpdate information for each prediction - for (IpcPrediction pred : predsForTrip) { - StopTimeUpdate.Builder stopTimeUpdate = StopTimeUpdate.newBuilder() - .setStopSequence(pred.getGtfsStopSeq()) - .setStopId(pred.getStopId()); - - StopTimeEvent.Builder stopTimeEvent = StopTimeEvent.newBuilder(); - stopTimeEvent.setTime(pred.getPredictionTime() / Time.MS_PER_SEC); - - // If schedule based prediction then set the uncertainty to special - // value so that client can tell - if (pred.isSchedBasedPred()) - stopTimeEvent.setUncertainty(SCHED_BASED_PRED_UNCERTAINTY_VALUE); - - // If vehicle is late and prediction is for a subsequent trip then - // the predictions are not as certain because it is reasonably likely - // that another vehicle will take over the subsequent trip. Takes - // precedence over SCHED_BASED_PRED_UNCERTAINTY_VALUE. - if (pred.isLateAndSubsequentTripSoMarkAsUncertain()) - stopTimeEvent.setUncertainty(LATE_AND_SUBSEQUENT_TRIP_UNCERTAINTY_VALUE); - - // If vehicle not making forward progress then set uncertainty to - // special value so that client can tell. Takes precedence over - // LATE_AND_SUBSEQUENT_TRIP_UNCERTAINTY_VALUE. - if (pred.isDelayed()) - stopTimeEvent.setUncertainty(DELAYED_UNCERTAINTY_VALUE); - - if (pred.isArrival()) - stopTimeUpdate.setArrival(stopTimeEvent); - else - stopTimeUpdate.setDeparture(stopTimeEvent); - - stopTimeUpdate.setScheduleRelationship(ScheduleRelationship.SCHEDULED); - tripUpdate.addStopTimeUpdate(stopTimeUpdate); - } - - // Add timestamp - tripUpdate.setTimestamp(firstPred.getAvlTime() / Time.MS_PER_SEC); - - // Return the results - return tripUpdate.build(); - } - - /** - * Creates a GTFS-realtime message for the predictions by trip passed in. - * - * @param predsByTripMap - * the data to be put into the GTFS-realtime message - * @return the GTFS-realtime FeedMessage - */ - private FeedMessage createMessage(Map> predsByTripMap) { - FeedMessage.Builder message = FeedMessage.newBuilder(); - - FeedHeader.Builder feedheader = FeedHeader.newBuilder() - .setGtfsRealtimeVersion("1.0") - .setIncrementality(Incrementality.FULL_DATASET) - .setTimestamp(System.currentTimeMillis() / Time.MS_PER_SEC); - message.setHeader(feedheader); - - // For each trip... - for (List predsForTrip : predsByTripMap.values()) { - // Create feed entity for each trip - FeedEntity.Builder feedEntity = FeedEntity.newBuilder() - .setId(predsForTrip.get(0).getTripId()); - try { - TripUpdate tripUpdate = createTripUpdate(predsForTrip); - feedEntity.setTripUpdate(tripUpdate); - message.addEntity(feedEntity); - } catch (Exception e) { - logger.error("Error parsing trip update data. {}", - predsForTrip, e); - } - } - - return message.build(); - } - - /** - * Returns map of all predictions for the project. Returns null if there was - * a problem getting the data via RMI. There is a separate list of - * predictions for each trip. The map is keyed by tripId. - * - * @return Map keyed on tripId of List of Predictions for the trip, or null - * if could not get data from server. - */ - private Map> getPredictionsPerTrip() { - // Get all the predictions, grouped by vehicle, from the server - List allPredictionsByStop; - try { - allPredictionsByStop = PredictionsInterfaceFactory.get(agencyId) - .getAllPredictions(PREDICTION_MAX_FUTURE_SECS); - } catch (RemoteException e) { - logger.error("Exception when getting vehicles from RMI", e); - return null; - } - - // Group the predictions by trip instead of by vehicle - Map> predictionsByTrip = - new HashMap>(); - for (IpcPredictionsForRouteStopDest predictionsForStop : - allPredictionsByStop) { - for (IpcPrediction prediction : - predictionsForStop.getPredictionsForRouteStop()) { - String tripId = prediction.getTripId(); - List predsForTrip = predictionsByTrip.get(tripId); - if (predsForTrip == null) { - // A new trip so need to use a new trip list - predsForTrip = new ArrayList(); - predictionsByTrip.put(tripId, predsForTrip); - } - - predsForTrip.add(prediction); - } - } - - // Return results - return predictionsByTrip; - } - - /** - * Gets the Vehicle data from RMI and creates corresponding - * GTFS-RT vehicle feed. - * - * @return GTFS-RT FeedMessage for vehicle positions - */ - public FeedMessage createMessage() { - // Get prediction data from server - IntervalTimer timer = new IntervalTimer(); - Map> predsByTrip = getPredictionsPerTrip(); - logger.debug("Getting predictions via RMI for " + - "GtfsRtTripFeed.createMessage() took {} msec", - timer.elapsedMsec()); - - // Use prediction data to create GTFS-RT message and return it. - return createMessage(predsByTrip); - } - - // For getPossiblyCachedMessage() - private static final DataCache tripFeedDataCache = new DataCache(); - - /** - * For caching Vehicle Positions feed messages. - * - * @param agencyId - * @param cacheTime - * @return - */ - public static FeedMessage getPossiblyCachedMessage(String agencyId, int cacheTime) { - FeedMessage feedMessage = tripFeedDataCache.get(agencyId, cacheTime); - if (feedMessage != null) - return feedMessage; - - GtfsRtTripFeed feed = new GtfsRtTripFeed(agencyId); - feedMessage = feed.createMessage(); - tripFeedDataCache.put(agencyId, feedMessage); - return feedMessage; - } - -} diff --git a/transitimeApi/src/main/java/org/transitime/api/rootResources/TransitimeApi.java b/transitimeApi/src/main/java/org/transitime/api/rootResources/TransitimeApi.java deleted file mode 100644 index 877474f92..000000000 --- a/transitimeApi/src/main/java/org/transitime/api/rootResources/TransitimeApi.java +++ /dev/null @@ -1,1481 +0,0 @@ -/* - * 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.transitime.api.rootResources; - -import java.rmi.RemoteException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.ws.rs.BeanParam; -import javax.ws.rs.DefaultValue; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import org.transitime.api.data.ApiActiveBlocksRoutes; -import org.transitime.api.data.ApiAgencies; -import org.transitime.api.data.ApiAgency; -import org.transitime.api.data.ApiBlock; -import org.transitime.api.data.ApiBlocks; -import org.transitime.api.data.ApiBlocksTerse; -import org.transitime.api.data.ApiCalendars; -import org.transitime.api.data.ApiDirections; -import org.transitime.api.data.ApiIds; -import org.transitime.api.data.ApiPredictions; -import org.transitime.api.data.ApiRmiServerStatus; -import org.transitime.api.data.ApiRoutes; -import org.transitime.api.data.ApiRoutesDetails; -import org.transitime.api.data.ApiSchedulesHorizStops; -import org.transitime.api.data.ApiSchedulesVertStops; -import org.transitime.api.data.ApiServerStatus; -import org.transitime.api.data.ApiTrip; -import org.transitime.api.data.ApiTripPatterns; -import org.transitime.api.data.ApiTripWithTravelTimes; -import org.transitime.api.data.ApiVehicleConfigs; -import org.transitime.api.data.ApiVehicles; -import org.transitime.api.data.ApiVehiclesDetails; -import org.transitime.api.predsByLoc.PredsByLoc; -import org.transitime.api.utils.StandardParameters; -import org.transitime.api.utils.WebUtils; -import org.transitime.db.structs.Agency; -import org.transitime.db.structs.Location; -import org.transitime.ipc.data.IpcActiveBlock; -import org.transitime.ipc.data.IpcBlock; -import org.transitime.ipc.data.IpcCalendar; -import org.transitime.ipc.data.IpcPrediction; -import org.transitime.ipc.data.IpcPredictionsForRouteStopDest; -import org.transitime.ipc.data.IpcRoute; -import org.transitime.ipc.data.IpcRouteSummary; -import org.transitime.ipc.data.IpcSchedule; -import org.transitime.ipc.data.IpcServerStatus; -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.data.IpcVehicleConfig; -import org.transitime.ipc.interfaces.ConfigInterface; -import org.transitime.ipc.interfaces.PredictionsInterface; -import org.transitime.ipc.interfaces.ServerStatusInterface; -import org.transitime.ipc.interfaces.VehiclesInterface; -import org.transitime.ipc.interfaces.PredictionsInterface.RouteStop; - -/** - * Contains the API commands for the Transitime API for getting real-time - * vehicle and prediction information plus the static configuration information. - * The intent of this feed is to provide what is needed for creating a user - * interface application, such as a smartphone application. - *

    - * The data output can be in either JSON or XML. The output format is specified - * by the accept header or by using the query string parameter "format=json" or - * "format=xml". - * - * @author SkiBu Smith - * - */ -@Path("/key/{key}/agency/{agency}") -public class TransitimeApi { - - /** - * Handles the "vehicles" command. Returns data for all vehicles or for the - * vehicles specified via the query string. - *

    - * A Response object is returned instead of a regular object so that can - * have one method for the both XML and JSON yet always return the proper - * media type even if it is configured via the query string "format" - * parameter as opposed to the accept header. - * - * @param stdParameters - * StdParametersBean that gets the standard parameters from the - * URI, query string, and headers. - * @param vehicleIds - * Optional way of specifying which vehicles to get data for - * @param routesIdOrShortNames - * Optional way of specifying which routes to get data for - * @param stopId - * Optional way of specifying a stop so can get predictions for - * routes and determine which vehicles are the ones generating - * the predictions. The other vehicles are labeled as minor so - * they can be drawn specially in the UI. - * @param numberPredictions - * For when determining which vehicles are generating the - * predictions so can label minor vehicles - * @return The Response object already configured for the specified media - * type. - */ - @Path("/command/vehicles") - @GET - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public - Response - getVehicles( - @BeanParam StandardParameters stdParameters, - @QueryParam(value = "v") List vehicleIds, - @QueryParam(value = "r") List routesIdOrShortNames, - @QueryParam(value = "s") String stopId, - @QueryParam(value = "numPreds") @DefaultValue("2") int numberPredictions) - throws WebApplicationException { - // Make sure request is valid - stdParameters.validate(); - - try { - // Get Vehicle data from server - VehiclesInterface inter = stdParameters.getVehiclesInterface(); - - Collection vehicles; - if (!routesIdOrShortNames.isEmpty() - && !routesIdOrShortNames.get(0).trim().isEmpty()) { - vehicles = inter.getForRoute(routesIdOrShortNames); - } else if (!vehicleIds.isEmpty() - && !vehicleIds.get(0).trim().isEmpty()) { - vehicles = inter.get(vehicleIds); - } else { - vehicles = inter.get(); - } - - // If the vehicles doesn't exist then throw exception such that - // Bad Request with an appropriate message is returned. - if (vehicles == null) - throw WebUtils.badRequestException("Invalid specifier for " - + "vehicles"); - - // To determine how vehicles should be drawn in UI. If stop - // specified - // when getting vehicle info then only the vehicles being predicted - // for, should be highlighted. The others should be dimmed. - Map uiTypesForVehicles = - determineUiModesForVehicles(vehicles, stdParameters, - routesIdOrShortNames, stopId, numberPredictions); - - ApiVehicles apiVehicles = - new ApiVehicles(vehicles, uiTypesForVehicles); - - // return ApiVehicles response - return stdParameters.createResponse(apiVehicles); - } catch (Exception e) { - // If problem getting data then return a Bad Request - throw WebUtils.badRequestException(e.getMessage()); - } - } - - /** - * Handles the vehicleIds command. Returns list of vehicle IDs. - * - * @param stdParameters - * @return - * @throws WebApplicationException - */ - @Path("/command/vehicleIds") - @GET - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response getVehicleIds(@BeanParam StandardParameters stdParameters) - throws WebApplicationException { - // Make sure request is valid - stdParameters.validate(); - - try { - // Get Vehicle data from server - ConfigInterface inter = stdParameters.getConfigInterface(); - List ids = inter.getVehicleIds(); - - ApiIds apiIds = new ApiIds(ids); - return stdParameters.createResponse(apiIds); - } catch (Exception e) { - // If problem getting data then return a Bad Request - throw WebUtils.badRequestException(e.getMessage()); - } - } - - /** - * Handles the "vehiclesDetails" command. Returns detailed data for all - * vehicles or for the vehicles specified via the query string. This data - * includes things not necessarily intended for the public, such as schedule - * adherence and driver IDs. - *

    - * A Response object is returned instead of a regular object so that can - * have one method for the both XML and JSON yet always return the proper - * media type even if it is configured via the query string "format" - * parameter as opposed to the accept header. - * - * @param stdParameters - * StdParametersBean that gets the standard parameters from the - * URI, query string, and headers. - * @param vehicleIds - * Optional way of specifying which vehicles to get data for - * @param routesIdOrShortNames - * Optional way of specifying which routes to get data for - * @param stopId - * Optional way of specifying a stop so can get predictions for - * routes and determine which vehicles are the ones generating - * the predictions. The other vehicles are labeled as minor so - * they can be drawn specially in the UI. - * @param numberPredictions - * For when determining which vehicles are generating the - * predictions so can label minor vehicles - * @return The Response object already configured for the specified media - * type. - */ - @Path("/command/vehiclesDetails") - @GET - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public - Response - getVehiclesDetails( - @BeanParam StandardParameters stdParameters, - @QueryParam(value = "v") List vehicleIds, - @QueryParam(value = "r") List routesIdOrShortNames, - @QueryParam(value = "s") String stopId, - @QueryParam(value = "numPreds") @DefaultValue("3") int numberPredictions) - throws WebApplicationException { - // Make sure request is valid - stdParameters.validate(); - - try { - // Get Vehicle data from server - VehiclesInterface inter = stdParameters.getVehiclesInterface(); - - Collection vehicles; - if (!routesIdOrShortNames.isEmpty() - && !routesIdOrShortNames.get(0).trim().isEmpty()) { - vehicles = inter.getForRoute(routesIdOrShortNames); - } else if (!vehicleIds.isEmpty() - && !vehicleIds.get(0).trim().isEmpty()) { - vehicles = inter.get(vehicleIds); - } else { - vehicles = inter.get(); - } - - // If the vehicles doesn't exist then throw exception such that - // Bad Request with an appropriate message is returned. - if (vehicles == null) - throw WebUtils.badRequestException("Invalid specifier for " - + "vehicles"); - - // To determine how vehicles should be drawn in UI. If stop - // specified - // when getting vehicle info then only the vehicles being predicted - // for, should be highlighted. The others should be dimmed. - Map uiTypesForVehicles = - determineUiModesForVehicles(vehicles, stdParameters, - routesIdOrShortNames, stopId, numberPredictions); - - // Convert IpcVehiclesDetails to ApiVehiclesDetails - ApiVehiclesDetails apiVehiclesDetails = - new ApiVehiclesDetails(vehicles, - stdParameters.getAgencyId(), uiTypesForVehicles); - - // return ApiVehiclesDetails response - return stdParameters.createResponse(apiVehiclesDetails); - } catch (Exception e) { - // If problem getting data then return a Bad Request - throw WebUtils.badRequestException(e.getMessage()); - } - } - - // For specifying how vehicles should be drawn in the UI. - public enum UiMode { - NORMAL, SECONDARY, MINOR - }; - - /** - * Gets information including vehicle IDs for all vehicles that have been - * configured. Useful for creating a vehicle selector. - * - * @param stdParameters - * @return - * @throws WebApplicationException - */ - @Path("/command/vehicleConfigs") - @GET - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response getVehicleConfigs( - @BeanParam StandardParameters stdParameters) - throws WebApplicationException { - // Make sure request is valid - stdParameters.validate(); - - try { - // Get Vehicle data from server - VehiclesInterface inter = stdParameters.getVehiclesInterface(); - Collection ipcVehicleConfigs = - inter.getVehicleConfigs(); - ApiVehicleConfigs apiVehicleConfigs = - new ApiVehicleConfigs(ipcVehicleConfigs); - - // return ApiVehiclesDetails response - return stdParameters.createResponse(apiVehicleConfigs); - } catch (Exception e) { - // If problem getting data then return a Bad Request - throw WebUtils.badRequestException(e.getMessage()); - } - } - - /** - * Determines Map of UiTypes for vehicles so that the vehicles can be drawn - * correctly in the UI. If when getting vehicles no specific route and stop - * were specified then want to highlight all vehicles. Therefore for this - * situation all vehicle IDs will be mapped to UiType.NORMAL. - *

    - * But if route and stop were specified then the first vehicle predicted for - * at the specified stop should be UiType.NORMAL, the subsequent ones are - * set to UiType.SECONDARY, and the remaining vehicles are set to - * UiType.MINOR. - * - * @param vehicles - * @param stdParameters - * @param routesIdOrShortNames - * @param stopId - * @param numberPredictions - * @return - * @throws RemoteException - */ - private static Map determineUiModesForVehicles( - Collection vehicles, StandardParameters stdParameters, - List routesIdOrShortNames, String stopId, - int numberPredictions) throws RemoteException { - // Create map and initialize all vehicles to NORMAL UI mode - Map modeMap = new HashMap(); - - if (routesIdOrShortNames.isEmpty() || stopId == null) { - // Stop not specified so simply return NORMAL type for all vehicles - for (IpcVehicle ipcVehicle : vehicles) { - modeMap.put(ipcVehicle.getId(), UiMode.NORMAL); - } - } else { - // Stop specified so get predictions and set UI type accordingly - List vehiclesGeneratingPreds = - determineVehiclesGeneratingPreds(stdParameters, - routesIdOrShortNames, stopId, numberPredictions); - for (IpcVehicle ipcVehicle : vehicles) { - UiMode uiType = UiMode.MINOR; - if (!vehiclesGeneratingPreds.isEmpty() - && ipcVehicle.getId().equals( - vehiclesGeneratingPreds.get(0))) - uiType = UiMode.NORMAL; - else if (vehiclesGeneratingPreds.contains(ipcVehicle.getId())) - uiType = UiMode.SECONDARY; - - modeMap.put(ipcVehicle.getId(), uiType); - } - - } - - // Return results - return modeMap; - } - - /** - * Provides just a list of vehicle IDs of the vehicles generating - * predictions for the specified stop. The list of vehicle IDs will be in - * time order such that the first one will be the next predicted vehicle - * etc. If routeShortNames or stopId not specified then will return empty - * array. - * - * @param stdParameters - * @param routesIdOrShortNames - * @param stopId - * @param numberPredictions - * @return List of vehicle IDs - * @throws RemoteException - */ - private static List determineVehiclesGeneratingPreds( - StandardParameters stdParameters, - List routesIdOrShortNames, String stopId, - int numberPredictions) throws RemoteException { - // The array of vehicle IDs to be returned - List vehiclesGeneratingPreds = new ArrayList(); - - // If stop specified then also get predictions for the stop to - // determine which vehicles are generating the predictions. - // If vehicle is not one of the ones generating a prediction - // then it is labeled as a minor vehicle for the UI. - if (!routesIdOrShortNames.isEmpty() && stopId != null) { - PredictionsInterface predsInter = - stdParameters.getPredictionsInterface(); - List predictions = - predsInter.get(routesIdOrShortNames.get(0), stopId, - numberPredictions); - - // Determine set of which vehicles predictions generated for - for (IpcPredictionsForRouteStopDest predsForRouteStop : predictions) { - for (IpcPrediction ipcPrediction : predsForRouteStop - .getPredictionsForRouteStop()) { - vehiclesGeneratingPreds.add(ipcPrediction.getVehicleId()); - } - } - } - - return vehiclesGeneratingPreds; - } - - /** - * Handles "predictions" command. Gets predictions from server and returns - * the corresponding response. - *

    - * A Response object is returned instead of a regular object so that can - * have one method for the both XML and JSON yet always return the proper - * media type even if it is configured via the query string "format" - * parameter as opposed to the accept header. - * - * @param stdParameters - * StdParametersBean that gets the standard parameters from the - * URI, query string, and headers. - * @param routeStopStrs - * List of route/stops to return predictions for. If route not - * specified then data will be returned for all routes for the - * specified stop. The route specifier is the route id or the - * route short name. It is often best to use route short name for - * consistency across configuration changes (route ID is not - * consistent for many agencies). The stop specified can either - * be the stop ID or the stop code. Each route/stop is separated - * by the "|" character so for example the query string could - * have "rs=43|2029&rs=43|3029" - * @param stopStrs - * List of stops to return predictions for. Provides predictions - * for all routes that serve the stop. Can use either stop ID or - * stop code. Can specify multiple stops. - * @param numberPredictions - * Maximum number of predictions to return. Default value is 3. - * @return - * @throws WebApplicationException - */ - @Path("/command/predictions") - @GET - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public - Response - getPredictions( - @BeanParam StandardParameters stdParameters, - @QueryParam(value = "rs") List routeStopStrs, - @QueryParam(value = "s") List stopStrs, - @QueryParam(value = "numPreds") @DefaultValue("3") int numberPredictions) - throws WebApplicationException { - // Make sure request is valid - stdParameters.validate(); - - try { - // Get Prediction data from server - PredictionsInterface inter = - stdParameters.getPredictionsInterface(); - - // Create list of route/stops that should get predictions for - List routeStopsList = new ArrayList(); - for (String routeStopStr : routeStopStrs) { - // Each route/stop is specified as a single string using "\" - // as a divider (e.g. "routeId|stopId") - String routeStopParams[] = routeStopStr.split("\\|"); - String routeIdOrShortName; - String stopIdOrCode; - if (routeStopParams.length == 1) { - // Just stop specified - routeIdOrShortName = null; - stopIdOrCode = routeStopParams[0]; - } else { - // Both route and stop specified - routeIdOrShortName = routeStopParams[0]; - stopIdOrCode = routeStopParams[1]; - } - RouteStop routeStop = - new RouteStop(routeIdOrShortName, stopIdOrCode); - routeStopsList.add(routeStop); - } - - // Add to list the stops that should get predictions for - for (String stopStr : stopStrs) { - // Use null for route identifier so get predictions for all - // routes for the stop - RouteStop routeStop = new RouteStop(null, stopStr); - routeStopsList.add(routeStop); - } - - // Actually get the predictions via IPC - List predictions = - inter.get(routeStopsList, numberPredictions); - - // return ApiPredictions response - ApiPredictions predictionsData = new ApiPredictions(predictions); - return stdParameters.createResponse(predictionsData); - } catch (Exception e) { - // If problem getting data then return a Bad Request - throw WebUtils.badRequestException(e.getMessage()); - } - } - - /** - * Handles "predictionsByLoc" command. Gets predictions from server and - * returns the corresponding response. - *

    - * A Response object is returned instead of a regular object so that can - * have one method for the both XML and JSON yet always return the proper - * media type even if it is configured via the query string "format" - * parameter as opposed to the accept header. - * - * @param stdParameters - * StdParametersBean that gets the standard parameters from the - * URI, query string, and headers. - * @param lat - * latitude in decimal degrees - * @param lon - * longitude in decimal degrees - * @param maxDistance - * How far away a stop can be from the lat/lon. Default is 1,500 - * m. - * @param numberPredictions - * Maximum number of predictions to return. Default value is 3. - * @return - * @throws WebApplicationException - */ - @Path("/command/predictionsByLoc") - @GET - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public - Response - getPredictions( - @BeanParam StandardParameters stdParameters, - @QueryParam(value = "lat") Double lat, - @QueryParam(value = "lon") Double lon, - @QueryParam(value = "maxDistance") @DefaultValue("1500.0") double maxDistance, - @QueryParam(value = "numPreds") @DefaultValue("3") int numberPredictions) - throws WebApplicationException { - // Make sure request is valid - stdParameters.validate(); - - if (maxDistance > PredsByLoc.MAX_MAX_DISTANCE) - throw WebUtils.badRequestException("Maximum maxDistance parameter " - + "is " + PredsByLoc.MAX_MAX_DISTANCE + "m but " - + maxDistance + "m was specified in the request."); - - try { - // Get Prediction data from server - PredictionsInterface inter = - stdParameters.getPredictionsInterface(); - - // Get predictions by location - List predictions = - inter.get(new Location(lat, lon), maxDistance, - numberPredictions); - - // return ApiPredictions response - ApiPredictions predictionsData = new ApiPredictions(predictions); - return stdParameters.createResponse(predictionsData); - } catch (Exception e) { - // If problem getting data then return a Bad Request - throw WebUtils.badRequestException(e.getMessage()); - } - } - - /** - * Handles the "routes" command. Returns summary data describing all of the - * routes. Useful for creating a route selector as part of a UI. - * - * @param stdParameters - * @return - * @throws WebApplicationException - */ - @Path("/command/routes") - @GET - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response getRoutes(@BeanParam StandardParameters stdParameters, - @QueryParam(value = "r") List routeIdsOrShortNames, - @QueryParam(value = "keepDuplicates") Boolean keepDuplicates) - throws WebApplicationException { - // Make sure request is valid - stdParameters.validate(); - - try { - ConfigInterface inter = stdParameters.getConfigInterface(); - - // Get agency info so can also return agency name - List agencies = inter.getAgencies(); - - // Get route data from server - ApiRoutes routesData; - if (routeIdsOrShortNames == null || routeIdsOrShortNames.isEmpty()) { - // Get all routes - List routes = - new ArrayList(inter.getRoutes()); - - // Handle duplicates. If should keep duplicates (where couple - // of routes have the same route_short_name) then modify - // the route name to indicate the different IDs. If should - // ignore duplicates then don't include them in final list - Collection processedRoutes = - new ArrayList(); - for (int i = 0; i < routes.size()-1; ++i) { - IpcRouteSummary route = routes.get(i); - IpcRouteSummary nextRoute = routes.get(i+1); - - // If find a duplicate route_short_name... - if (route.getShortName().equals(nextRoute.getShortName())) { - // Only keep route if supposed to - if (keepDuplicates != null && keepDuplicates) { - // Keep duplicates but change route name - IpcRouteSummary routeWithModifiedName = - new IpcRouteSummary(route, route.getName() - + " (ID=" + route.getId() + ")"); - processedRoutes.add(routeWithModifiedName); - - IpcRouteSummary nextRouteWithModifiedName = - new IpcRouteSummary(nextRoute, - nextRoute.getName() + " (ID=" - + nextRoute.getId() + ")"); - processedRoutes.add(nextRouteWithModifiedName); - - // Since processed both this route and the next - // route can skip to next one - ++i; - } - } else { - // Not a duplicate so simply add it - processedRoutes.add(route); - } - } - // Add the last route - processedRoutes.add(routes.get(routes.size()-1)); - - routesData = new ApiRoutes(processedRoutes, agencies.get(0)); - } else { - // Get specified routes - List ipcRoutes = inter.getRoutes(routeIdsOrShortNames); - routesData = new ApiRoutes(ipcRoutes, agencies.get(0)); - } - - // Create and return response - return stdParameters.createResponse(routesData); - } catch (Exception e) { - // If problem getting data then return a Bad Request - throw WebUtils.badRequestException(e.getMessage()); - } - } - - /** - * Handles the "routesDetails" command. Provides detailed information for a - * route includes all stops and paths such that it can be drawn in a map. - * - * @param stdParameters - * @param routeIdsOrShortNames - * list of route IDs or route short names. If a single route is - * specified then data for just the single route is returned. If - * no route is specified then data for all routes returned in an - * array. If multiple routes specified then data for those routes - * returned in an array. - * @param stopId - * optional. If set then only this stop and the remaining ones on - * the trip pattern are marked as being for the UI and can be - * highlighted. Useful for when want to emphasize in the UI only - * the stops that are of interest to the user. - * @param direction - * optional. If set then only the shape for specified direction - * is marked as being for the UI. Needed for situations where a - * single stop is used for both directions of a route and want to - * highlight in the UI only the stops and the shapes that the - * user is actually interested in. - * @param tripPatternId - * optional. If set then only the specified trip pattern is - * marked as being for the UI. - * @return - * @throws WebApplicationException - */ - @Path("/command/routesDetails") - @GET - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response getRouteDetails( - @BeanParam StandardParameters stdParameters, - @QueryParam(value = "r") List routeIdsOrShortNames, - @QueryParam(value = "d") String directionId, - @QueryParam(value = "s") String stopId, - @QueryParam(value = "tripPattern") String tripPatternId) - throws WebApplicationException { - // Make sure request is valid - stdParameters.validate(); - - try { - // Get Vehicle data from server - ConfigInterface inter = stdParameters.getConfigInterface(); - - // Get agency info so can also return agency name - List agencies = inter.getAgencies(); - - List ipcRoutes; - - // If single route specified - if (routeIdsOrShortNames != null - && routeIdsOrShortNames.size() == 1) { - String routeIdOrShortName = routeIdsOrShortNames.get(0); - IpcRoute route = - inter.getRoute(routeIdOrShortName, directionId, stopId, - tripPatternId); - - // If the route doesn't exist then throw exception such that - // Bad Request with an appropriate message is returned. - if (route == null) - throw WebUtils.badRequestException("Route for route=" - + routeIdOrShortName + " does not exist."); - - ipcRoutes = new ArrayList(); - ipcRoutes.add(route); - } else { - // Multiple routes specified - ipcRoutes = inter.getRoutes(routeIdsOrShortNames); - } - - // Take the IpcRoute data array and create and return - // ApiRoutesDetails object - ApiRoutesDetails routeData = - new ApiRoutesDetails(ipcRoutes, agencies.get(0)); - return stdParameters.createResponse(routeData); - } catch (Exception e) { - // If problem getting data then return a Bad Request - throw WebUtils.badRequestException(e.getMessage()); - } - } - - /** - * Handles the "stops" command. Returns all stops associated with a route, - * grouped by direction. Useful for creating a UI where user needs to select - * a stop from a list. - * - * @param stdParameters - * @param routeShortName - * @return - * @throws WebApplicationException - */ - @Path("/command/stops") - @GET - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response getStops(@BeanParam StandardParameters stdParameters, - @QueryParam(value = "r") String routesIdOrShortNames) - throws WebApplicationException { - - // Make sure request is valid - stdParameters.validate(); - - try { - // Get stops data from server - ConfigInterface inter = stdParameters.getConfigInterface(); - IpcDirectionsForRoute stopsForRoute = - inter.getStops(routesIdOrShortNames); - - // If the route doesn't exist then throw exception such that - // Bad Request with an appropriate message is returned. - if (stopsForRoute == null) - throw WebUtils.badRequestException("route=" - + routesIdOrShortNames + " does not exist."); - - // Create and return ApiDirections response - ApiDirections directionsData = new ApiDirections(stopsForRoute); - return stdParameters.createResponse(directionsData); - } catch (Exception e) { - // If problem getting data then return a Bad Request - throw WebUtils.badRequestException(e.getMessage()); - } - } - - /** - * Handles the "block" command which outputs configuration data for the - * specified block ID and service ID. Includes all sub-data such as trips - * and trip patterns. - * - * @param stdParameters - * @param blockId - * @param serviceId - * @return - * @throws WebApplicationException - */ - @Path("/command/block") - @GET - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response getBlock(@BeanParam StandardParameters stdParameters, - @QueryParam(value = "blockId") String blockId, - @QueryParam(value = "serviceId") String serviceId) - throws WebApplicationException { - - // Make sure request is valid - stdParameters.validate(); - if (serviceId == null) - throw WebUtils.badRequestException("Must specify serviceId"); - - try { - // Get block data from server - ConfigInterface inter = stdParameters.getConfigInterface(); - IpcBlock ipcBlock = inter.getBlock(blockId, serviceId); - - // If the block doesn't exist then throw exception such that - // Bad Request with an appropriate message is returned. - if (ipcBlock == null) - throw WebUtils.badRequestException("The blockId=" + blockId - + " for serviceId=" + serviceId + " does not exist."); - - // Create and return ApiBlock response - ApiBlock apiBlock = new ApiBlock(ipcBlock); - return stdParameters.createResponse(apiBlock); - } catch (Exception e) { - // If problem getting data then return a Bad Request - throw WebUtils.badRequestException(e.getMessage()); - } - } - - /** - * Handles the "blocksTerse" command which outputs configuration data for - * the specified block ID. Does not include trip pattern and schedule data - * for trips. - * - * @param stdParameters - * @param blockId - * @return - * @throws WebApplicationException - */ - @Path("/command/blocksTerse") - @GET - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response getBlocksTerse(@BeanParam StandardParameters stdParameters, - @QueryParam(value = "blockId") String blockId) - throws WebApplicationException { - - // Make sure request is valid - stdParameters.validate(); - - try { - // Get block data from server - ConfigInterface inter = stdParameters.getConfigInterface(); - Collection ipcBlocks = inter.getBlocks(blockId); - - // If the block doesn't exist then throw exception such that - // Bad Request with an appropriate message is returned. - if (ipcBlocks.isEmpty()) - throw WebUtils.badRequestException("The blockId=" + blockId - + " does not exist."); - - // Create and return ApiBlock response - ApiBlocksTerse apiBlocks = new ApiBlocksTerse(ipcBlocks); - return stdParameters.createResponse(apiBlocks); - } catch (Exception e) { - // If problem getting data then return a Bad Request - throw WebUtils.badRequestException(e.getMessage()); - } - } - - /** - * Handles the "blocks" command which outputs configuration data for the - * specified block ID. Includes all sub-data such as trips and trip - * patterns. - * - * @param stdParameters - * @param blockId - * @return - * @throws WebApplicationException - */ - @Path("/command/blocks") - @GET - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response getBlocks(@BeanParam StandardParameters stdParameters, - @QueryParam(value = "blockId") String blockId) - throws WebApplicationException { - - // Make sure request is valid - stdParameters.validate(); - - try { - // Get block data from server - ConfigInterface inter = stdParameters.getConfigInterface(); - Collection ipcBlocks = inter.getBlocks(blockId); - - // If the block doesn't exist then throw exception such that - // Bad Request with an appropriate message is returned. - if (ipcBlocks.isEmpty()) - throw WebUtils.badRequestException("The blockId=" + blockId - + " does not exist."); - - // Create and return ApiBlock response - ApiBlocks apiBlocks = new ApiBlocks(ipcBlocks); - return stdParameters.createResponse(apiBlocks); - } catch (Exception e) { - // If problem getting data then return a Bad Request - throw WebUtils.badRequestException(e.getMessage()); - } - } - - /** - * Handles the "blockIds" command. Returns list of block IDs. - * - * @param stdParameters - * @return - * @throws WebApplicationException - */ - @Path("/command/blockIds") - @GET - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response getBlockIds(@BeanParam StandardParameters stdParameters, - @QueryParam(value = "serviceId") String serviceId) - throws WebApplicationException { - // Make sure request is valid - stdParameters.validate(); - - try { - // Get Vehicle data from server - ConfigInterface inter = stdParameters.getConfigInterface(); - List ids = inter.getBlockIds(serviceId); - - ApiIds apiIds = new ApiIds(ids); - return stdParameters.createResponse(apiIds); - } catch (Exception e) { - // If problem getting data then return a Bad Request - throw WebUtils.badRequestException(e.getMessage()); - } - } - - /** - * Gets which blocks are active. Can optionally specify list of routes and - * how much before a block is supposed to start is it considered active. - * - * @param stdParameters - * StdParametersBean that gets the standard parameters from the - * URI, query string, and headers. - * @param routesIdOrShortNames - * Optional parameter for specifying which routes want data for. - * @param allowableBeforeTimeSecs - * Optional parameter. A block will be active if the time is - * between the block start time minus allowableBeforeTimeSecs and - * the block end time. Default value for allowableBeforeTimeSecs - * is 0. - * @return - * @throws WebApplicationException - */ - @Path("/command/activeBlocksByRoute") - @GET - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public - Response - getActiveBlocksByRoute( - @BeanParam StandardParameters stdParameters, - @QueryParam(value = "r") List routesIdOrShortNames, - @QueryParam(value = "t") @DefaultValue("0") int allowableBeforeTimeSecs) - throws WebApplicationException { - - // Make sure request is valid - stdParameters.validate(); - - try { - // Get active block data from server - VehiclesInterface vehiclesInterface = - stdParameters.getVehiclesInterface(); - Collection activeBlocks = - vehiclesInterface.getActiveBlocks(routesIdOrShortNames, - allowableBeforeTimeSecs); - - // Create and return ApiBlock response - ApiActiveBlocksRoutes apiActiveBlocksRoutes = - new ApiActiveBlocksRoutes(activeBlocks, - stdParameters.getAgencyId()); - return stdParameters.createResponse(apiActiveBlocksRoutes); - } catch (Exception e) { - // If problem getting data then return a Bad Request - throw WebUtils.badRequestException(e.getMessage()); - } - } - - /** - * Handles the "trip" command which outputs configuration data for the - * specified trip. Includes all sub-data such as trip patterns. - * - * @param stdParameters - * @param tripId - * Can be the GTFS trip_id or the trip_short_name - * @return - * @throws WebApplicationException - */ - @Path("/command/trip") - @GET - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response getTrip(@BeanParam StandardParameters stdParameters, - @QueryParam(value = "tripId") String tripId) - throws WebApplicationException { - - // Make sure request is valid - stdParameters.validate(); - - try { - // Get block data from server - ConfigInterface inter = stdParameters.getConfigInterface(); - IpcTrip ipcTrip = inter.getTrip(tripId); - - // If the trip doesn't exist then throw exception such that - // Bad Request with an appropriate message is returned. - if (ipcTrip == null) - throw WebUtils.badRequestException("TripId=" + tripId - + " does not exist."); - - // Create and return ApiBlock response. - // Include stop path info since just outputting single trip. - ApiTrip apiTrip = new ApiTrip(ipcTrip, true); - return stdParameters.createResponse(apiTrip); - } catch (Exception e) { - // If problem getting data then return a Bad Request - throw WebUtils.badRequestException(e.getMessage()); - } - } - - /** - * Handles the "tripWithTravelTimes" command which outputs configuration - * data for the specified trip. Includes all sub-data such as trip patterns. - * - * @param stdParameters - * @param tripId - * @return - * @throws WebApplicationException - */ - @Path("/command/tripWithTravelTimes") - @GET - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response getTripWithTravelTimes( - @BeanParam StandardParameters stdParameters, - @QueryParam(value = "tripId") String tripId) - throws WebApplicationException { - - // Make sure request is valid - stdParameters.validate(); - - try { - // Get block data from server - ConfigInterface inter = stdParameters.getConfigInterface(); - IpcTrip ipcTrip = inter.getTrip(tripId); - - // If the trip doesn't exist then throw exception such that - // Bad Request with an appropriate message is returned. - if (ipcTrip == null) - throw WebUtils.badRequestException("TripId=" + tripId - + " does not exist."); - - // Create and return ApiBlock response. - // Include stop path info since just outputting single trip. - ApiTripWithTravelTimes apiTrip = - new ApiTripWithTravelTimes(ipcTrip, true); - return stdParameters.createResponse(apiTrip); - } catch (Exception e) { - // If problem getting data then return a Bad Request - throw WebUtils.badRequestException(e.getMessage()); - } - } - - /** - * Handles the tripIds command. Returns list of trip IDs. - * - * @param stdParameters - * @return - * @throws WebApplicationException - */ - @Path("/command/tripIds") - @GET - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response getTripIds(@BeanParam StandardParameters stdParameters) - throws WebApplicationException { - // Make sure request is valid - stdParameters.validate(); - - try { - // Get Vehicle data from server - ConfigInterface inter = stdParameters.getConfigInterface(); - List ids = inter.getTripIds(); - - ApiIds apiIds = new ApiIds(ids); - return stdParameters.createResponse(apiIds); - } catch (Exception e) { - // If problem getting data then return a Bad Request - throw WebUtils.badRequestException(e.getMessage()); - } - } - - /** - * Handles the "tripPattern" command which outputs trip pattern - * configuration data for the specified route. - * - * @param stdParameters - * @param routesIdOrShortNames - * @return - * @throws WebApplicationException - */ - @Path("/command/tripPatterns") - @GET - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response getTripPatterns( - @BeanParam StandardParameters stdParameters, - @QueryParam(value = "r") String routesIdOrShortNames) - throws WebApplicationException { - - // Make sure request is valid - stdParameters.validate(); - - try { - // Get block data from server - ConfigInterface inter = stdParameters.getConfigInterface(); - List ipcTripPatterns = - inter.getTripPatterns(routesIdOrShortNames); - - // If the trip doesn't exist then throw exception such that - // Bad Request with an appropriate message is returned. - if (ipcTripPatterns == null) - throw WebUtils.badRequestException("route=" - + routesIdOrShortNames + " does not exist."); - - // Create and return ApiTripPatterns response - ApiTripPatterns apiTripPatterns = - new ApiTripPatterns(ipcTripPatterns); - return stdParameters.createResponse(apiTripPatterns); - } catch (Exception e) { - // If problem getting data then return a Bad Request - throw WebUtils.badRequestException(e.getMessage()); - } - } - - /** - * Handles the "scheduleVertStops" command which outputs schedule for the - * specified route. The data is output such that the stops are listed - * vertically (and the trips are horizontal). For when there are a good - * number of stops but not as many trips, such as for commuter rail. - * - * @param stdParameters - * @param routesIdOrShortNames - * @return - * @throws WebApplicationException - */ - @Path("/command/scheduleVertStops") - @GET - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response getScheduleVertStops( - @BeanParam StandardParameters stdParameters, - @QueryParam(value = "r") String routesIdOrShortNames) - throws WebApplicationException { - - // Make sure request is valid - stdParameters.validate(); - - try { - // Get block data from server - ConfigInterface inter = stdParameters.getConfigInterface(); - List ipcSchedules = - inter.getSchedules(routesIdOrShortNames); - - // If the trip doesn't exist then throw exception such that - // Bad Request with an appropriate message is returned. - if (ipcSchedules == null) - throw WebUtils.badRequestException("route=" - + routesIdOrShortNames + " does not exist."); - - // Create and return ApiSchedules response - ApiSchedulesVertStops apiSchedules = - new ApiSchedulesVertStops(ipcSchedules); - return stdParameters.createResponse(apiSchedules); - } catch (Exception e) { - // If problem getting data then return a Bad Request - throw WebUtils.badRequestException(e.getMessage()); - } - } - - /** - * Handles the "scheduleHorizStops" command which outputs schedule for the - * specified route. The data is output such that the stops are listed - * horizontally (and the trips are vertical). For when there are many more - * trips than stops, which is typical for bus routes. - * - * @param stdParameters - * @param routesIdOrShortNames - * @return - * @throws WebApplicationException - */ - @Path("/command/scheduleHorizStops") - @GET - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response getScheduleHorizStops( - @BeanParam StandardParameters stdParameters, - @QueryParam(value = "r") String routesIdOrShortNames) - throws WebApplicationException { - - // Make sure request is valid - stdParameters.validate(); - - try { - // Get block data from server - ConfigInterface inter = stdParameters.getConfigInterface(); - List ipcSchedules = - inter.getSchedules(routesIdOrShortNames); - - // If the trip doesn't exist then throw exception such that - // Bad Request with an appropriate message is returned. - if (ipcSchedules == null) - throw WebUtils.badRequestException("route=" - + routesIdOrShortNames + " does not exist."); - - // Create and return ApiSchedules response - ApiSchedulesHorizStops apiSchedules = - new ApiSchedulesHorizStops(ipcSchedules); - return stdParameters.createResponse(apiSchedules); - } catch (Exception e) { - // If problem getting data then return a Bad Request - throw WebUtils.badRequestException(e.getMessage()); - } - } - - /** - * For getting Agency data for a specific agencyId. - * - * @param stdParameters - * @return - * @throws WebApplicationException - */ - @Path("/command/agencyGroup") - @GET - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response getAgencyGroup(@BeanParam StandardParameters stdParameters) - throws WebApplicationException { - - // Make sure request is valid - stdParameters.validate(); - - try { - // Get block data from server - ConfigInterface inter = stdParameters.getConfigInterface(); - List agencies = inter.getAgencies(); - - // Create and return ApiAgencies response - List apiAgencyList = new ArrayList(); - for (Agency agency : agencies) { - apiAgencyList.add(new ApiAgency(stdParameters.getAgencyId(), - agency)); - } - ApiAgencies apiAgencies = new ApiAgencies(apiAgencyList); - return stdParameters.createResponse(apiAgencies); - } catch (Exception e) { - // If problem getting data then return a Bad Request - throw WebUtils.badRequestException(e.getMessage()); - } - } - - /** - * For getting calendars that are currently active. - * - * @param stdParameters - * @return - * @throws WebApplicationException - */ - @Path("/command/currentCalendars") - @GET - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response getCurrentCalendars( - @BeanParam StandardParameters stdParameters) - throws WebApplicationException { - - // Make sure request is valid - stdParameters.validate(); - - try { - // Get block data from server - ConfigInterface inter = stdParameters.getConfigInterface(); - List ipcCalendars = inter.getCurrentCalendars(); - - // Create and return ApiAgencies response - ApiCalendars apiCalendars = new ApiCalendars(ipcCalendars); - return stdParameters.createResponse(apiCalendars); - } catch (Exception e) { - // If problem getting data then return a Bad Request - throw WebUtils.badRequestException(e.getMessage()); - } - } - - /** - * For getting all calendars. - * - * @param stdParameters - * @return - * @throws WebApplicationException - */ - @Path("/command/allCalendars") - @GET - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response - getAllCalendars(@BeanParam StandardParameters stdParameters) - throws WebApplicationException { - - // Make sure request is valid - stdParameters.validate(); - - try { - // Get block data from server - ConfigInterface inter = stdParameters.getConfigInterface(); - List ipcCalendars = inter.getAllCalendars(); - - // Create and return ApiAgencies response - ApiCalendars apiCalendars = new ApiCalendars(ipcCalendars); - return stdParameters.createResponse(apiCalendars); - } catch (Exception e) { - // If problem getting data then return a Bad Request - throw WebUtils.badRequestException(e.getMessage()); - } - } - - /** - * Handles the "serviceIds" command. Returns list of all service IDs. - * - * @param stdParameters - * @return - * @throws WebApplicationException - */ - @Path("/command/serviceIds") - @GET - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response getServiceIds(@BeanParam StandardParameters stdParameters) - throws WebApplicationException { - // Make sure request is valid - stdParameters.validate(); - - try { - // Get Vehicle data from server - ConfigInterface inter = stdParameters.getConfigInterface(); - List ids = inter.getServiceIds(); - - ApiIds apiIds = new ApiIds(ids); - return stdParameters.createResponse(apiIds); - } catch (Exception e) { - // If problem getting data then return a Bad Request - throw WebUtils.badRequestException(e.getMessage()); - } - } - - /** - * Handles the currentServiceIds command. Returns list of service IDs that - * are currently active. - * - * @param stdParameters - * @return - * @throws WebApplicationException - */ - @Path("/command/currentServiceIds") - @GET - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response getCurrentServiceIds( - @BeanParam StandardParameters stdParameters) - throws WebApplicationException { - // Make sure request is valid - stdParameters.validate(); - - try { - // Get Vehicle data from server - ConfigInterface inter = stdParameters.getConfigInterface(); - List ids = inter.getCurrentServiceIds(); - - ApiIds apiIds = new ApiIds(ids); - return stdParameters.createResponse(apiIds); - } catch (Exception e) { - // If problem getting data then return a Bad Request - throw WebUtils.badRequestException(e.getMessage()); - } - } - - /** - * Returns status about the specified agency server. Currently provides info - * on the DbLogger queue. - * - * @param stdParameters - * @return - * @throws WebApplicationException - */ - @Path("/command/serverStatus") - @GET - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response - getServerStatus(@BeanParam StandardParameters stdParameters) - throws WebApplicationException { - - // Make sure request is valid - stdParameters.validate(); - - try { - // Get status information from server - ServerStatusInterface inter = - stdParameters.getServerStatusInterface(); - IpcServerStatus ipcServerStatus = inter.get(); - - // Create and return ApiServerStatus response - ApiServerStatus apiServerStatus = - new ApiServerStatus(stdParameters.getAgencyId(), - ipcServerStatus); - return stdParameters.createResponse(apiServerStatus); - } catch (Exception e) { - // If problem getting data then return a Bad Request - throw WebUtils.badRequestException(e.getMessage()); - } - } - - /** - * Returns info for this particular web server for each agency on how many - * outstanding RMI calls there are. - * - * @param stdParameters - * @return - * @throws WebApplicationException - */ - @Path("/command/rmiStatus") - @GET - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response getRmiStatus(@BeanParam StandardParameters stdParameters) - throws WebApplicationException { - - // Make sure request is valid - stdParameters.validate(); - - ApiRmiServerStatus apiRmiServerStatus = new ApiRmiServerStatus(); - return stdParameters.createResponse(apiRmiServerStatus); - } - - // /** - // * For creating response of list of vehicles. Would like to make this a - // * generic type but due to type erasure cannot do so since GenericEntity - // * somehow works differently with generic types. - // *

    - // * Deprecated because found that much better off using a special - // * container class for lists of items since that way can control the - // * name of the list element. - // * - // * @param collection - // * Collection of Vehicle objects to be returned in XML or JSON. - // * Must be ArrayList so can use GenericEntity to create Response. - // * @param stdParameters - // * For specifying media type. - // * @return The created response in the proper media type. - // */ - // private static Response createListResponse(Collection - // collection, - // StdParametersBean stdParameters) { - // // Must be ArrayList so can use GenericEntity to create Response. - // ArrayList arrayList = (ArrayList) collection; - // - // // Create a GenericEntity that can handle list of the appropriate - // // type. - // GenericEntity> entity = - // new GenericEntity>(arrayList) {}; - // - // // Return the response using the generic entity - // return createResponse(entity, stdParameters); - // } - -} diff --git a/transitimeApi/src/main/webapp/WEB-INF/web.xml b/transitimeApi/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index 91630ec3b..000000000 --- a/transitimeApi/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - Transitime api - - - transitime_config_file_location - - /usr/local/transitimeTomcatConfig/transitime.properties - - - - - org.transitime.web.ReadConfigListener - - - - - 416 - /error/messageOnlyError.jsp - - - - - org.transitime.web.ReadConfigListener - - - diff --git a/transitimeWebapp/.classpath b/transitimeWebapp/.classpath deleted file mode 100644 index 28e4a52cd..000000000 --- a/transitimeWebapp/.classpath +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/transitimeWebapp/.gitignore b/transitimeWebapp/.gitignore deleted file mode 100644 index 731eb433c..000000000 --- a/transitimeWebapp/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target/ -/.settings/ diff --git a/transitimeWebapp/.project b/transitimeWebapp/.project deleted file mode 100644 index 10c74672f..000000000 --- a/transitimeWebapp/.project +++ /dev/null @@ -1,43 +0,0 @@ - - - transitime.transitimeWebapp-v1 - - - transitimeCore - - - - org.eclipse.wst.jsdt.core.javascriptValidator - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.wst.common.project.facet.core.builder - - - - - org.eclipse.wst.validation.validationbuilder - - - - - org.eclipse.m2e.core.maven2Builder - - - - - - org.eclipse.jem.workbench.JavaEMFNature - org.eclipse.wst.common.modulecore.ModuleCoreNature - org.eclipse.jdt.core.javanature - org.eclipse.m2e.core.maven2Nature - org.eclipse.wst.common.project.facet.core.nature - org.eclipse.wst.jsdt.core.jsNature - - diff --git a/transitimeWebapp/pom.xml b/transitimeWebapp/pom.xml deleted file mode 100644 index 0a4ef68bc..000000000 --- a/transitimeWebapp/pom.xml +++ /dev/null @@ -1,51 +0,0 @@ - - 4.0.0 - transitime - transitimeWebapp - war - 0.0.1-SNAPSHOT - transitimeWebapp - http://maven.apache.org - - - UTF-8 - - - - - junit - junit - 3.8.1 - test - - - javax.servlet - javax.servlet-api - 3.0.1 - provided - - - transitime - transitimeCore - 0.0.1-SNAPSHOT - - - - - web - - - - org.apache.maven.plugins - maven-compiler-plugin - 2.5.1 - true - - 1.7 - 1.7 - - - - - diff --git a/transitimeWebapp/src/main/java/org/transitime/reports/PredictionAccuracyQuery.java b/transitimeWebapp/src/main/java/org/transitime/reports/PredictionAccuracyQuery.java deleted file mode 100644 index c4a9453c3..000000000 --- a/transitimeWebapp/src/main/java/org/transitime/reports/PredictionAccuracyQuery.java +++ /dev/null @@ -1,349 +0,0 @@ -/* - * 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.transitime.reports; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Timestamp; -import java.text.ParseException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.transitime.db.GenericQuery; -import org.transitime.db.webstructs.WebAgency; -import org.transitime.utils.Time; - -/** - * For doing SQL query and generating JSON data for a prediction accuracy chart. - * This abstract class does the SQL query and puts data into a map. Then a - * subclass must be used to convert the data to JSON rows and columns for Google - * chart. - * - * @author SkiBu Smith - * - */ -abstract public class PredictionAccuracyQuery { - - private final Connection connection; - - protected static final int MAX_PRED_LENGTH = 900; - protected static final int PREDICTION_LENGTH_BUCKET_SIZE = 30; - - // Keyed on source (so can show data for multiple sources at - // once in order to compare prediction accuracy. Contains a array, - // with an element for each prediction bucket, containing an array - // of the prediction accuracy values in seconds for that bucket. Each bucket - // is for - // a certain prediction range, specified by predictionLengthBucketSize. - protected final Map>> map = new HashMap>>(); - - // Defines the output type for the intervals, whether should show - // standard deviation, percentage, or both. - // Can iterate over the enumerated type using: - // for (IntervalsType type : IntervalsType.values()) {} - public enum IntervalsType { - PERCENTAGE("PERCENTAGE"), STD_DEV("STD_DEV"), BOTH("BOTH"); - - private final String text; - - private IntervalsType(final String text) { - this.text = text; - } - - /** - * For converting from a string to an IntervalsType - * - * @param text - * String to be converted - * @return The corresponding IntervalsType, or IntervalsType.PERCENTAGE - * as the default if text doesn't match a type. - */ - public static IntervalsType createIntervalsType(String text) { - for (IntervalsType type : IntervalsType.values()) { - if (type.toString().equals(text)) { - return type; - } - } - - // If a bad non-null value was specified then log the error - if (text != null) - logger.error("\"{}\" is not a valid IntervalsType", text); - - // Couldn't match so use default value - return IntervalsType.PERCENTAGE; - } - - @Override - public String toString() { - return text; - } - - } - - private static final Logger logger = LoggerFactory - .getLogger(PredictionAccuracyQuery.class); - - /********************** Member Functions **************************/ - - /** - * Creates connection to database. - * - * @param dbType - * @param dbHost - * @param dbName - * @param dbUserName - * @param dbPassword - * @throws SQLException - */ - public PredictionAccuracyQuery(String dbType, String dbHost, String dbName, - String dbUserName, String dbPassword) throws SQLException { - connection = GenericQuery.getConnection(dbType, dbHost, dbName, - dbUserName, dbPassword); - - } - - /** - * Creates connection to the database for the specified agency. - * - * @param agencyId - * @throws SQLException - */ - public PredictionAccuracyQuery(String agencyId) throws SQLException { - WebAgency agency = WebAgency.getCachedWebAgency(agencyId); - connection = GenericQuery.getConnection(agency.getDbType(), - agency.getDbHost(), agency.getDbName(), agency.getDbUserName(), - agency.getDbPassword()); - } - - /** - * Determines which prediction bucket in the map to use. Want to have each - * bucket to be for an easily understood value, such as 1 minute. Best way - * to do this is then have the predictions for that bucket be 45 seconds to - * 75 seconds so that the indicator for the bucket (1 minute) is in the - * middle of the range. - * - * @param predLength - * @return - */ - private static int index(int predLength) { - return (predLength + PREDICTION_LENGTH_BUCKET_SIZE / 2) - / PREDICTION_LENGTH_BUCKET_SIZE; - } - - /** - * Puts the data from the query into the map so it can be further processed - * later. - * - * @param predLength - * @param predAccuracy - * @param source - */ - private void addDataToMap(int predLength, int predAccuracy, String source) { - // Get the prediction buckets for the specified source - List> predictionBuckets = map.get(source); - if (predictionBuckets == null) { - predictionBuckets = new ArrayList>(); - map.put(source, predictionBuckets); - } - - // Determine the index of the appropriate prediction bucket - int predictionBucketIndex = index(predLength); - while (predictionBuckets.size() < predictionBucketIndex + 1) - predictionBuckets.add(new ArrayList()); - List predictionAccuracies = predictionBuckets - .get(predictionBucketIndex); - - // Add the prediction accuracy to the bucket. - predictionAccuracies.add(predAccuracy); - } - - /** - * Performs the SQL query and puts the resulting data into the map. - * - * @param beginDateStr - * Begin date for date range of data to use. - * @param numDaysStr - * How many days to do the query for - * @param beginTimeStr - * For specifying time of day between the begin and end date to - * use data for. Can thereby specify a date range of a week but - * then just look at data for particular time of day, such as 7am - * to 9am, for those days. Set to null or empty string to use - * data for entire day. - * @param endTimeStr - * For specifying time of day between the begin and end date to - * use data for. Can thereby specify a date range of a week but - * then just look at data for particular time of day, such as 7am - * to 9am, for those days. Set to null or empty string to use - * data for entire day. - * @param routeIds - * Array of IDs of routes to get data for - * @param predSource - * The source of the predictions. Can be null or "" (for all), - * "Transitime", or "Other" - * @param predType - * Whether predictions are affected by wait stop. Can be "" (for - * all), "AffectedByWaitStop", or "NotAffectedByWaitStop". - * @throws SQLException - * @throws ParseException - */ - protected void doQuery(String beginDateStr, String numDaysStr, - String beginTimeStr, String endTimeStr, String routeIds[], - String predSource, String predType) throws SQLException, - ParseException { - // Make sure not trying to get data for too long of a time span since - // that could bog down the database. - int numDays = Integer.parseInt(numDaysStr); - if (numDays > 31) { - throw new ParseException( - "Number of days of " + numDays + " spans more than a month", 0); - } - - // Determine the time of day portion of the SQL - String timeSql = ""; - if ((beginTimeStr != null && !beginTimeStr.isEmpty()) - || (endTimeStr != null && !endTimeStr.isEmpty())) { - // If only begin or only end time set then use default value - if (beginTimeStr == null || beginTimeStr.isEmpty()) - beginTimeStr = "00:00:00"; - else { - // beginTimeStr set so make sure it is valid, and prevent - // possible SQL injection - if (!beginTimeStr.matches("\\d+:\\d+")) - throw new ParseException("begin time \"" + beginTimeStr - + "\" is not valid.", 0); - } - if (endTimeStr == null || endTimeStr.isEmpty()) - endTimeStr = "23:59:59"; - else { - // endTimeStr set so make sure it is valid, and prevent - // possible SQL injection - if (!endTimeStr.matches("\\d+:\\d+")) - throw new ParseException("end time \"" + endTimeStr - + "\" is not valid.", 0); - } - timeSql = " AND arrivalDepartureTime::time BETWEEN '" + beginTimeStr - + "' AND '" + endTimeStr + "' "; - } - - // Determine route portion of SQL - // Need to examine each route ID twice since doing a - // routeId='stableId' OR routeShortName='stableId' in - // order to handle agencies where GTFS route_id is not - // stable but the GTFS route_short_name is. - String routeSql = ""; - if (routeIds != null && routeIds.length > 0 && !routeIds[0].trim().isEmpty()) { - routeSql = " AND (routeId=? OR routeShortName=?"; - for (int i = 1; i < routeIds.length; ++i) - routeSql += " OR routeId=? OR routeShortName=?"; - routeSql += ")"; - } - - // Determine the source portion of the SQL. Default is to provide - // predictions for all sources - String sourceSql = ""; - if (predSource != null && !predSource.isEmpty()) { - if (predSource.equals("Transitime")) { - // Only "Transitime" predictions - sourceSql = " AND predictionSource='Transitime'"; - } else { - // Anything but "Transitime" - sourceSql = " AND predictionSource<>'Transitime'"; - } - } - - // Determine SQL for prediction type. Can be "" (for - // all), "AffectedByWaitStop", or "NotAffectedByWaitStop". - String predTypeSql = ""; - if (predType != null && !predType.isEmpty()) { - if (predSource.equals("AffectedByWaitStop")) { - // Only "AffectedByLayover" predictions - predTypeSql = " AND affectedByWaitStop = true "; - } else { - // Only "NotAffectedByLayover" predictions - predTypeSql = " AND affectedByWaitStop = false "; - } - } - - // Put the entire SQL query together - String sql = "SELECT " - + " to_char(predictedTime-predictionReadTime, 'SSSS')::integer as predLength, " - + " predictionAccuracyMsecs/1000 as predAccuracy, " - + " predictionSource as source " - + " FROM predictionAccuracy " - + "WHERE arrivalDepartureTime BETWEEN ? " - + " AND TIMESTAMP '" + beginDateStr + "' + INTERVAL '" + numDays + " day' " - + timeSql - + " AND predictedTime-predictionReadTime < '00:15:00' " - + routeSql - + sourceSql - + predTypeSql; - - PreparedStatement statement = null; - try { - statement = connection.prepareStatement(sql); - - // Determine the date parameters for the query - Timestamp beginDate = null; - java.util.Date date = Time.parseDate(beginDateStr); - beginDate = new Timestamp(date.getTime()); - - // Set the parameters for the query - int i = 1; - statement.setTimestamp(i++, beginDate); - - if (routeIds != null) { - for (String routeId : routeIds) - if (!routeId.trim().isEmpty()) { - // Need to add the route ID twice since doing a - // routeId='stableId' OR routeShortName='stableId' in - // order to handle agencies where GTFS route_id is not - // stable but the GTFS route_short_name is. - statement.setString(i++, routeId); - statement.setString(i++, routeId); - } - } - - // Actually execute the query - ResultSet rs = statement.executeQuery(); - - // Process results of query - while (rs.next()) { - int predLength = rs.getInt("predLength"); - int predAccuracy = rs.getInt("predAccuracy"); - String sourceResult = rs.getString("source"); - - addDataToMap(predLength, predAccuracy, sourceResult); - logger.debug("predLength={} predAccuracy={} source={}", - predLength, predAccuracy, sourceResult); - } - } catch (SQLException e) { - throw e; - } finally { - if (statement != null) - statement.close(); - } - } - -} diff --git a/transitimeWebapp/src/main/java/org/transitime/utils/web/WebUtils.java b/transitimeWebapp/src/main/java/org/transitime/utils/web/WebUtils.java deleted file mode 100644 index c1d2cb000..000000000 --- a/transitimeWebapp/src/main/java/org/transitime/utils/web/WebUtils.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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.transitime.utils.web; - -import javax.servlet.http.HttpServletRequest; - -/** - * - * - * @author SkiBu Smith - * - */ -public class WebUtils { - - /********************** Member Functions **************************/ - - /** - * Goes through all the request parameters, such as from the query string, - * and puts them into a String version of a JSON set of key values. This - * string can be used as the data parameter for a JQuery AJAX call to - * forward all parameters to the page being requested via AJAX. - * - * @param request - * @return The parameters to be used as data for an AJAX call - */ - public static String getAjaxDataString(HttpServletRequest request) { - String queryStringParams = ""; - java.util.Map paramsMap = request.getParameterMap(); - boolean firstParam = true; - for (String paramName : paramsMap.keySet()) { - if (!firstParam) - queryStringParams += ", "; - firstParam = false; - - queryStringParams += paramName + ":["; - String paramValues[] = paramsMap.get(paramName); - boolean firstValue = true; - for (String paramValue : paramValues) { - if (!firstValue) - queryStringParams += ", "; - firstValue = false; - - queryStringParams += "\"" + paramValue + "\""; - } - queryStringParams += "]"; - } - - return queryStringParams; - } -} diff --git a/transitimeWebapp/src/main/resources/postgres_hibernate.cfg.xml b/transitimeWebapp/src/main/resources/postgres_hibernate.cfg.xml deleted file mode 100644 index d50e7f382..000000000 --- a/transitimeWebapp/src/main/resources/postgres_hibernate.cfg.xml +++ /dev/null @@ -1,84 +0,0 @@ - - - - - - - - org.hibernate.dialect.PostgreSQLDialect - - - org.postgresql.Driver - - - - - true - true - - - - 2 - - 20 - - 300 - - 50 - - - 25 - - - - - - - - - diff --git a/transitimeWebapp/src/main/webapp/WEB-INF/web.xml b/transitimeWebapp/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index f7f0c609b..000000000 --- a/transitimeWebapp/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - Transitime webapp - - - transitime_config_file_location - - /usr/local/transitimeTomcatConfig/transitime.properties - - - - welcome/index.jsp - index.html - index.htm - index.jsp - default.html - default.htm - default.jsp - - - - - org.transitime.web.ReadConfigListener - - - \ No newline at end of file diff --git a/transitimeWebapp/src/main/webapp/maps/heatmap/heatmap.min.js b/transitimeWebapp/src/main/webapp/maps/heatmap/heatmap.min.js deleted file mode 100644 index 8a8bbe198..000000000 --- a/transitimeWebapp/src/main/webapp/maps/heatmap/heatmap.min.js +++ /dev/null @@ -1,9 +0,0 @@ -/* - * heatmap.js v2.0.0 | JavaScript Heatmap Library - * - * Copyright 2008-2014 Patrick Wied - All rights reserved. - * Dual licensed under MIT and Beerware license - * - * :: 2014-08-05 01:42 - */ -(function(a){var b={defaultRadius:40,defaultRenderer:"canvas2d",defaultGradient:{.25:"rgb(0,0,255)",.55:"rgb(0,255,0)",.85:"yellow",1:"rgb(255,0,0)"},defaultMaxOpacity:1,defaultMinOpacity:0,defaultBlur:.85,defaultXField:"x",defaultYField:"y",defaultValueField:"value",plugins:{}};var c=function i(){var a=function d(a){this._coordinator={};this._data=[];this._radi=[];this._min=0;this._max=1;this._xField=a["xField"]||a.defaultXField;this._yField=a["yField"]||a.defaultYField;this._valueField=a["valueField"]||a.defaultValueField;if(a["radius"]){this._cfgRadius=a["radius"]}};var c=b.defaultRadius;a.prototype={_organiseData:function(a,b){var d=a[this._xField];var e=a[this._yField];var f=this._radi;var g=this._data;var h=this._max;var i=this._min;var j=a[this._valueField]||1;var k=a.radius||this._cfgRadius||c;if(!g[d]){g[d]=[];f[d]=[]}if(!g[d][e]){g[d][e]=j;f[d][e]=k}else{g[d][e]+=j}if(g[d][e]>h){if(!b){this._max=g[d][e]}else{this.setDataMax(g[d][e])}return false}else{return{x:d,y:e,value:j,radius:k,min:i,max:h}}},_unOrganizeData:function(){var a=[];var b=this._data;var c=this._radi;for(var d in b){for(var e in b[d]){a.push({x:d,y:e,radius:c[d][e],value:b[d][e]})}}return{min:this._min,max:this._max,data:a}},_onExtremaChange:function(){this._coordinator.emit("extremachange",{min:this._min,max:this._max})},addData:function(){if(arguments[0].length>0){var a=arguments[0];var b=a.length;while(b--){this.addData.call(this,a[b])}}else{var c=this._organiseData(arguments[0],true);if(c){this._coordinator.emit("renderpartial",{min:this._min,max:this._max,data:[c]})}}return this},setData:function(a){var b=a.data;var c=b.length;this._max=a.max;this._min=a.min||0;this._data=[];this._radi=[];for(var d=0;dthis._renderBoundaries[2]){this._renderBoundaries[2]=l+2*j}if(m+2*j>this._renderBoundaries[3]){this._renderBoundaries[3]=m+2*j}}},_colorize:function(){var a=this._renderBoundaries[0];var b=this._renderBoundaries[1];var c=this._renderBoundaries[2]-a;var d=this._renderBoundaries[3]-b;var e=this._width;var f=this._height;var g=this._opacity;var h=this._maxOpacity;var i=this._minOpacity;if(a<0){a=0}if(b<0){b=0}if(a+c>e){c=e-a}if(b+d>f){d=f-b}var j=this.shadowCtx.getImageData(a,b,c,d);var k=j.data;var l=k.length;var m=this._palette;for(var n=3;n0){q=g}else{if(o>0;return b},getDataURL:function(){return this.canvas.toDataURL()}};return d}();var e=function k(){var a=false;if(b["defaultRenderer"]==="canvas2d"){a=d}return a}();var f={merge:function(){var a={};var b=arguments.length;for(var c=0;c -<%@ page language="java" contentType="text/html; charset=ISO-8859-1" - pageEncoding="ISO-8859-1"%> - - - - - - - - - - <%@include file="/template/includes.jsp" %> - - - - - - - - - - - - - - AVL Data Map - - - - - -

    - - - - \ No newline at end of file diff --git a/transitimeWebapp/src/main/webapp/reports/index.jsp b/transitimeWebapp/src/main/webapp/reports/index.jsp deleted file mode 100644 index b07a45dc8..000000000 --- a/transitimeWebapp/src/main/webapp/reports/index.jsp +++ /dev/null @@ -1,88 +0,0 @@ -<%@page import="org.transitime.db.webstructs.WebAgency"%> - -<%@ page language="java" contentType="text/html; charset=ISO-8859-1" - pageEncoding="ISO-8859-1"%> - -<% -String agencyId = request.getParameter("a"); -if (agencyId == null || agencyId.isEmpty()) { - response.getWriter().write("You must specify agency in query string (e.g. ?a=mbta)"); - return; -} -%> - - - <%@include file="/template/includes.jsp" %> - - -Historical Reports - - -<%@include file="/template/header.jsp" %> -
    -
    Historical Reports for <%= WebAgency.getCachedWebAgency(agencyId).getAgencyName() %>
    - -
    Prediction Accuracy
    (only for agencies where prediction accuracy stored to database)
    - - -
    Schedule Adherence Reports
    - - -
    AVL Reports
    - - -
    Miscellaneous Reports
    - -
    - - \ No newline at end of file diff --git a/transitimeWebapp/src/main/webapp/reports/lastAvlJsonData.jsp b/transitimeWebapp/src/main/webapp/reports/lastAvlJsonData.jsp deleted file mode 100644 index 8ce5fd4e2..000000000 --- a/transitimeWebapp/src/main/webapp/reports/lastAvlJsonData.jsp +++ /dev/null @@ -1,13 +0,0 @@ -<%@ page language="java" contentType="application/json; charset=ISO-8859-1" - pageEncoding="ISO-8859-1"%> -<%@ page import="org.transitime.reports.GenericJsonQuery" %> -<% -String sql = - "SELECT vehicleId, max(time) AS maxTime " - + "FROM avlreports WHERE time > now() + '-24 hours' " - + "GROUP BY vehicleId;"; -String agencyId = request.getParameter("a"); -String jsonString = GenericJsonQuery.getJsonString(agencyId, sql); -response.setHeader("Access-Control-Allow-Origin", "*"); -response.getWriter().write(jsonString); -%> \ No newline at end of file diff --git a/transitimeWebapp/src/main/webapp/reports/params/fromDateNumDaysTime.jsp b/transitimeWebapp/src/main/webapp/reports/params/fromDateNumDaysTime.jsp deleted file mode 100644 index e82ac82ca..000000000 --- a/transitimeWebapp/src/main/webapp/reports/params/fromDateNumDaysTime.jsp +++ /dev/null @@ -1,103 +0,0 @@ -<%-- For specifying a begin date, number of days, begin time, and end time --%> - - - -<% -String currentDateStr = org.transitime.utils.Time.dateStr(new java.util.Date()); -%> - -
    - - -
    - -
    - - -
    - -
    - - (hh:mm) -
    - -
    - - (hh:mm) -
    \ No newline at end of file diff --git a/transitimeWebapp/src/main/webapp/reports/params/fromToDateTime.jsp b/transitimeWebapp/src/main/webapp/reports/params/fromToDateTime.jsp deleted file mode 100644 index 95c3f4613..000000000 --- a/transitimeWebapp/src/main/webapp/reports/params/fromToDateTime.jsp +++ /dev/null @@ -1,89 +0,0 @@ -<%-- For specifying a begin date & time and an end date & time --%> - - - - - - - - - - - - - -<% -String currentDateStr = org.transitime.utils.Time.dateStr(new java.util.Date()); -%> - -
    - - -
    - -
    - - (hh:mm) -
    - -
    - - (hh:mm) -
    \ No newline at end of file diff --git a/transitimeWebapp/src/main/webapp/reports/params/reportParams.css b/transitimeWebapp/src/main/webapp/reports/params/reportParams.css deleted file mode 100644 index d524c8f8d..000000000 --- a/transitimeWebapp/src/main/webapp/reports/params/reportParams.css +++ /dev/null @@ -1,9 +0,0 @@ -/* - * CSS styles for report parameter pages - */ -label {width: 200px; float: left; text-align: right; margin-top: 4px; margin-right: 10px;} -.param {margin-top: 10px;} -.submitDiv{ margin-top: 40px; text-align: center; } -#radioButtonsDiv {margin-top: 13px; text-align: center;} -.note {font-size: small;} - \ No newline at end of file diff --git a/transitimeWebapp/src/main/webapp/reports/params/routeMultiple.jsp b/transitimeWebapp/src/main/webapp/reports/params/routeMultiple.jsp deleted file mode 100644 index 705d326bb..000000000 --- a/transitimeWebapp/src/main/webapp/reports/params/routeMultiple.jsp +++ /dev/null @@ -1,60 +0,0 @@ -<%-- For creating a route selector parameter via a jsp include. - User can select all routes (r param then set to " ") or any - number of routes. - Reads in routes via API for the agency specified by the "a" param. --%> - - - - - -
    - - -
    - diff --git a/transitimeWebapp/src/main/webapp/reports/params/vehicle.jsp b/transitimeWebapp/src/main/webapp/reports/params/vehicle.jsp deleted file mode 100644 index 83fd6e7bd..000000000 --- a/transitimeWebapp/src/main/webapp/reports/params/vehicle.jsp +++ /dev/null @@ -1,52 +0,0 @@ -<%-- For creating a vehicle selector parameter via a jsp include. - User can select all vehicles (v set to " ") OR a single vehicle. - Reads in routes via API for the agency specified by the "a" param. --%> - - - - - -
    - - -
    - diff --git a/transitimeWebapp/src/main/webapp/reports/predAccuracyCsv.jsp b/transitimeWebapp/src/main/webapp/reports/predAccuracyCsv.jsp deleted file mode 100644 index 0abcab9f5..000000000 --- a/transitimeWebapp/src/main/webapp/reports/predAccuracyCsv.jsp +++ /dev/null @@ -1,77 +0,0 @@ -<%@ page import="org.transitime.utils.Time" %> -<%@ page import="org.transitime.db.GenericCsvQuery" %> -<%@ page import="java.text.ParseException" %> -<%-- This file is for outputting prediction accuracy data in CSV format. - --%> -<% -// In order to make at least Windows based systems treat the file as -// a CSV file need to change the file name suffix to csv and also -// set the content type. This way the file can be loaded into Excel -// directory from the web browser. -response.setHeader("Content-Disposition", "filename=predAccuracy.csv"); -response.setContentType("application/csv"); - -// Parameters from request -String agencyId = request.getParameter("a"); -String beginDate = request.getParameter("beginDate"); -String numDays = request.getParameter("numDays"); -String beginTime = request.getParameter("beginTime"); -String endTime = request.getParameter("endTime"); -String routeId = request.getParameter("r"); - -if (agencyId == null || beginDate == null || numDays == null) { - response.getWriter().write("For predAccuracyCsv.jsp must " - + "specify parameters 'a' (agencyId), " - + "'beginDate', and 'numDays'."); - return; -} - -//Determine the time portion of the SQL -String timeSql = ""; -if ((beginTime != null && !beginTime.isEmpty()) - || (endTime != null && !endTime.isEmpty())) { - // If only begin or only end time set then use default value - if (beginTime == null || beginTime.isEmpty()) - beginTime = "00:00:00"; - if (endTime == null || endTime.isEmpty()) - endTime = "23:59:59"; - timeSql = " AND arrivalDepartureTime::time BETWEEN '" - + beginTime + "' AND '" + endTime + "' "; -} - -//Determine route portion of SQL. Default is to provide info for -//all routes. -String routeSql = ""; -if (routeId!=null && !routeId.trim().isEmpty()) { - routeSql = " AND routeShortName='" + routeId + "' "; -} - -// NOTE: this query only works on postgreSQL. For mySQL would need to change -// from using to_char(), not using casting like "::interger", don't put -// single quotes around "'1 day'", etc. -String sql = "SELECT " - + " to_char(predictedTime-predictionReadTime, 'SSSS')::integer as pred_length_secs, \n" - + " predictionAccuracyMsecs/1000 as accuracy_secs, \n" - + " predictedTime AS predicted_time, \n" - + " arrivalDepartureTime AS actual_time, \n" - + " predictionreadtime AS prediction_read_time, \n" - + " predictionSource AS source, routeId AS route, \n" - + " directionId AS direction, tripId AS trip, \n" - + " stopId AS stop, vehicleId AS vehicle, \n" - + " affectedByWaitStop AS affected_by_wait_stop \n" - + " FROM predictionAccuracy \n" - + "WHERE arrivalDepartureTime BETWEEN '" + beginDate - + "' AND TIMESTAMP '" + beginDate + "' + INTERVAL '" + numDays + " day' \n" - + timeSql - + " AND predictedTime-predictionReadTime < '00:15:00' \n" - + routeSql + "\n" - // Filter out MBTA_seconds source since it is isn't significantly different from MBTA_epoch. - // TODO should clean this up by not having MBTA_seconds source at all - // in the prediction accuracy module for MBTA. - + " AND predictionSource <> 'MBTA_seconds' "; - - System.out.println("\nFor prediction accuracy by route query sql=\n" + sql); -// Do the actual query -String csvStr = GenericCsvQuery.getCsvString(agencyId, sql); -response.getWriter().write(csvStr); -%> \ No newline at end of file diff --git a/transitimeWebapp/src/main/webapp/reports/predAccuracyIntervalsParams.jsp b/transitimeWebapp/src/main/webapp/reports/predAccuracyIntervalsParams.jsp deleted file mode 100644 index 2c577202b..000000000 --- a/transitimeWebapp/src/main/webapp/reports/predAccuracyIntervalsParams.jsp +++ /dev/null @@ -1,103 +0,0 @@ -<%@ page language="java" contentType="text/html; charset=ISO-8859-1" - pageEncoding="ISO-8859-1"%> - - - -<%@include file="/template/includes.jsp" %> - -Specify Parameters - - - - - - - - - -<%@include file="/template/header.jsp" %> - -
    - Select Parameters for Prediction Accuracy Intervals Chart -
    - -
    -
    - <%-- For passing agency param to the report --%> - "> - - - - - -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - % -
    - -
    - - % -
    - - - -
    - - - \ No newline at end of file diff --git a/transitimeWebapp/src/main/webapp/reports/predAccuracyRangeParams.jsp b/transitimeWebapp/src/main/webapp/reports/predAccuracyRangeParams.jsp deleted file mode 100644 index a395940be..000000000 --- a/transitimeWebapp/src/main/webapp/reports/predAccuracyRangeParams.jsp +++ /dev/null @@ -1,82 +0,0 @@ -<%@ page language="java" contentType="text/html; charset=ISO-8859-1" - pageEncoding="ISO-8859-1"%> - - - -<%@include file="/template/includes.jsp" %> - -Specify Parameters - - - - - - - - -<%@include file="/template/header.jsp" %> -
    - Select Parameters for Prediction Accuracy Range Chart -
    - -
    -
    - <%-- For passing agency param to the report --%> - "> - - - - - -
    - - -
    - -
    - - -
    - -
    - - minutes -
    - -
    - - minutes -
    - - - -
    - - - \ No newline at end of file diff --git a/transitimeWebapp/src/main/webapp/reports/predAccuracyScatterData.jsp b/transitimeWebapp/src/main/webapp/reports/predAccuracyScatterData.jsp deleted file mode 100644 index bd64e223f..000000000 --- a/transitimeWebapp/src/main/webapp/reports/predAccuracyScatterData.jsp +++ /dev/null @@ -1,154 +0,0 @@ -<%@ page import="org.transitime.reports.ChartGenericJsonQuery" %> -<%@ page import="org.transitime.utils.Time" %> -<%@ page import="java.text.ParseException" %> -<% - // Parameters from request -String agencyId = request.getParameter("a"); -String beginDate = request.getParameter("beginDate"); -String numDays = request.getParameter("numDays"); -String beginTime = request.getParameter("beginTime"); -String endTime = request.getParameter("endTime"); -String routeId = request.getParameter("r"); -String source = request.getParameter("source"); -String predictionType = request.getParameter("predictionType"); - -boolean showTooltips = true; -String showTooltipsStr = request.getParameter("tooltips"); -if (showTooltipsStr != null && showTooltipsStr.toLowerCase().equals("false")) - showTooltips = false; - -if (agencyId == null || beginDate == null || numDays == null) { - response.getWriter().write("For predAccuracyScatterData.jsp must " - + "specify parameters 'a' (agencyId), " - + "'beginDate', and 'numDays'."); - return; -} - -// Make sure not trying to get data for too long of a time span since -// that could bog down the database. -if (Integer.parseInt(numDays) > 31) { - throw new ParseException( - "Number of days of " + numDays + " spans more than a month", 0); -} - -// Determine the time portion of the SQL -String timeSql = ""; -if ((beginTime != null && !beginTime.isEmpty()) - || (endTime != null && !endTime.isEmpty())) { - // If only begin or only end time set then use default value - if (beginTime == null || beginTime.isEmpty()) - beginTime = "00:00:00"; - else { - // beginTimeStr set so make sure it is valid, and prevent - // possible SQL injection - if (!beginTime.matches("\\d+:\\d+")) - throw new ParseException("begin time \"" + beginTime - + "\" is not valid.", 0); - } - if (endTime == null || endTime.isEmpty()) - endTime = "23:59:59"; - else { - // endTimeStr set so make sure it is valid, and prevent - // possible SQL injection - if (!endTime.matches("\\d+:\\d+")) - throw new ParseException("end time \"" + endTime - + "\" is not valid.", 0); - } - - timeSql = " AND arrivalDepartureTime::time BETWEEN '" - + beginTime + "' AND '" + endTime + "' "; -} - -// Determine route portion of SQL. Default is to provide info for -// all routes. -String routeSql = ""; -if (routeId != null && !routeId.trim().isEmpty()) { - routeSql = " AND (routeId='" + routeId + "' OR routeShortName='" + routeId + "') "; -} - -// Determine the source portion of the SQL. Default is to provide -// predictions for all sources -String sourceSql = ""; -if (source != null && !source.isEmpty()) { - if (source.equals("Transitime")) { - // Only "Transitime" predictions - sourceSql = " AND predictionSource='Transitime'"; - } else { - // Anything but "Transitime" - sourceSql = " AND predictionSource<>'Transitime'"; - } -} - -// Determine SQL for prediction type () -String predTypeSql = ""; -if (predictionType != null && !predictionType.isEmpty()) { - if (source.equals("AffectedByWaitStop")) { - // Only "AffectedByLayover" predictions - predTypeSql = " AND affectedByWaitStop = true "; - } else { - // Only "NotAffectedByLayover" predictions - predTypeSql = " AND affectedByWaitStop = false "; - } -} - -String tooltipsSql = ""; -if (showTooltips) - tooltipsSql = - ", format(E'predAccuracy= %s\\n" - + "prediction=%s\\n" - + "stopId=%s\\n" - + "routeId=%s\\n" - + "tripId=%s\\n" - + "arrDepTime=%s\\n" - + "predTime=%s\\n" - + "predReadTime=%s\\n" - + "vehicleId=%s\\n" - + "source=%s\\n" - + "affectedByLayover=%s', " - + " CAST(predictionAccuracyMsecs || ' msec' AS INTERVAL), predictedTime-predictionReadTime," - + " stopId, routeId, tripId, " - + " to_char(arrivalDepartureTime, 'HH24:MI:SS.MS MM/DD/YYYY')," - + " to_char(predictedTime, 'HH24:MI:SS.MS')," - + " to_char(predictionReadTime, 'HH24:MI:SS.MS')," - + " vehicleId," - + " predictionSource," - + " CASE WHEN affectedbywaitstop THEN 'True' ELSE 'False' END) AS tooltip "; - -String sql = "SELECT " - + " to_char(predictedTime-predictionReadTime, 'SSSS')::integer as predLength, " - + " predictionAccuracyMsecs/1000 as predAccuracy " - + tooltipsSql - + " FROM predictionAccuracy " - + "WHERE arrivalDepartureTime BETWEEN '" + beginDate - + "' AND TIMESTAMP '" + beginDate + "' + INTERVAL '" + numDays + " day' " - + timeSql - + " AND predictedTime-predictionReadTime < '00:15:00' " - + routeSql - + sourceSql - + predTypeSql - // Filter out MBTA_seconds source since it is isn't significantly different from MBTA_epoch. - // TODO should clean this up by not having MBTA_seconds source at all - // in the prediction accuracy module for MBTA. - + " AND predictionSource <> 'MBTA_seconds' "; - -// Determine the json data by running the query -String jsonString = ChartGenericJsonQuery.getJsonString(agencyId, sql); - -// If no data then return error status with an error message -if (jsonString == null || jsonString.isEmpty()) { - String message = "No data for beginDate=" + beginDate - + " numDays=" + numDays - + " beginTime=" + beginTime - + " endTime=" + endTime - + " routeId=" + routeId - + " source=" + source - + " predictionType=" + predictionType; - response.setStatus(400); - response.getWriter().write(message); - return; -} - -// Return the JSON data -response.setHeader("Access-Control-Allow-Origin", "*"); -response.getWriter().write(jsonString); -%> \ No newline at end of file diff --git a/transitimeWebapp/src/main/webapp/reports/predAccuracyScatterParams.jsp b/transitimeWebapp/src/main/webapp/reports/predAccuracyScatterParams.jsp deleted file mode 100644 index 88711fde3..000000000 --- a/transitimeWebapp/src/main/webapp/reports/predAccuracyScatterParams.jsp +++ /dev/null @@ -1,75 +0,0 @@ -<%@ page language="java" contentType="text/html; charset=ISO-8859-1" - pageEncoding="ISO-8859-1"%> - - - -<%@include file="/template/includes.jsp" %> - -Specify Parameters - - - - - - - - - -<%@include file="/template/header.jsp" %> - -
    - Select Parameters for Prediction Accuracy Scatter Chart -
    - -
    -
    - <%-- For passing agency param to the report --%> - "> - - - - - - - - - - - - -
    - - -
    - -
    - - -
    - - - -
    - - - \ No newline at end of file diff --git a/transitimeWebapp/src/main/webapp/reports/schAdhByRouteParams.jsp b/transitimeWebapp/src/main/webapp/reports/schAdhByRouteParams.jsp deleted file mode 100644 index 36f248117..000000000 --- a/transitimeWebapp/src/main/webapp/reports/schAdhByRouteParams.jsp +++ /dev/null @@ -1,56 +0,0 @@ -<%@ page language="java" contentType="text/html; charset=ISO-8859-1" - pageEncoding="ISO-8859-1"%> - - - -<%@include file="/template/includes.jsp" %> - -Specify Parameters - - - - - - - - -<%@include file="/template/header.jsp" %> -
    - Select Parameters for Schedule Adherence by Route Chart -
    - -
    -
    - <%-- For passing agency param to the report --%> - "> - - - - - -
    - - minutes -
    - -
    - - minutes -
    - - - -
    - - - \ No newline at end of file diff --git a/transitimeWebapp/src/main/webapp/reports/schAdhByStopParams.jsp b/transitimeWebapp/src/main/webapp/reports/schAdhByStopParams.jsp deleted file mode 100644 index 9d89eaab5..000000000 --- a/transitimeWebapp/src/main/webapp/reports/schAdhByStopParams.jsp +++ /dev/null @@ -1,56 +0,0 @@ -<%@ page language="java" contentType="text/html; charset=ISO-8859-1" - pageEncoding="ISO-8859-1"%> - - - -<%@include file="/template/includes.jsp" %> - -Specify Parameters - - - - - - - - -<%@include file="/template/header.jsp" %> -
    - Select Parameters for Schedule Adherence by Stop Chart -
    - -
    -
    - <%-- For passing agency param to the report --%> - "> - - - - - -
    - - minutes -
    - -
    - - minutes -
    - - - -
    - - - \ No newline at end of file diff --git a/transitimeWebapp/src/main/webapp/reports/schAdhByTimeParams.jsp b/transitimeWebapp/src/main/webapp/reports/schAdhByTimeParams.jsp deleted file mode 100644 index eb5a0e454..000000000 --- a/transitimeWebapp/src/main/webapp/reports/schAdhByTimeParams.jsp +++ /dev/null @@ -1,56 +0,0 @@ -<%@ page language="java" contentType="text/html; charset=ISO-8859-1" - pageEncoding="ISO-8859-1"%> - - - -<%@include file="/template/includes.jsp" %> - -Specify Parameters - - - - - - - - -<%@include file="/template/header.jsp" %> -
    - Select Parameters for Schedule Adherence by Route Chart -
    - -
    -
    - <%-- For passing agency param to the report --%> - "> - - - - - -
    - - minutes -
    - -
    - - minutes -
    - - - -
    - - - \ No newline at end of file