{"action":"created","comment":{"url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/pulls/comments/218620848","pull_request_review_id":156601383,"id":218620848,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIxODYyMDg0OA==","diff_hunk":"@@ -16,688 +16,446 @@\n package edu.usf.cutr.gtfs_realtime.bullrunner;\r\n \r\n \r\n-import java.util.List;\r\n-import java.io.BufferedReader;\r\n-import java.io.IOException;\r\n-import java.io.InputStreamReader;\r\n-import java.io.Reader;\r\n-import java.net.URL;\r\n-import java.net.URLConnection;\r\n-import java.text.DateFormat;\r\n-import java.text.ParseException;\r\n-import java.text.SimpleDateFormat;\r\n-import java.util.ArrayList;\r\n-import java.util.Calendar;\r\n-import java.util.Collections;\r\n-import java.util.Date;\r\n-import java.util.concurrent.Executors;\r\n-import java.util.concurrent.ScheduledExecutorService;\r\n-import java.util.concurrent.TimeUnit;\r\n-import java.text.ParsePosition;\r\n-\r\n-import javax.annotation.PostConstruct;\r\n-import javax.annotation.PreDestroy;\r\n-import javax.inject.Singleton;\r\n-\r\n-import org.joda.time.DateTime;\r\n-import org.joda.time.format.DateTimeFormat;\r\n-import org.joda.time.format.DateTimeFormatter;\r\n+import com.google.inject.Inject;\r\n+import com.google.transit.realtime.GtfsRealtime.*;\r\n+import com.google.transit.realtime.GtfsRealtime.VehiclePosition.OccupancyStatus;\r\n import org.json.JSONArray;\r\n import org.json.JSONException;\r\n import org.json.JSONObject;\r\n-import org.json.JSONTokener;\r\n-import org.omg.CORBA.portable.InputStream;\r\n import org.onebusaway.gtfs_realtime.exporter.GtfsRealtimeExporterModule;\r\n-import edu.usf.cutr.gtfs_realtime.bullrunner.GtfsRealtimeExporterCutr;\r\n-import org.onebusaway.gtfs_realtime.exporter.GtfsRealtimeLibrary;\r\n-import org.onebusaway.gtfs_realtime.exporter.GtfsRealtimeSink;\r\n import org.onebusaway.gtfs_realtime.exporter.GtfsRealtimeFullUpdate;\r\n-import org.onebusaway.gtfs_realtime.exporter.GtfsRealtimeGuiceBindingTypes;\r\n-import org.onebusaway.gtfs_realtime.exporter.GtfsRealtimeGuiceBindingTypes.VehiclePositions;\r\n import org.onebusaway.gtfs_realtime.exporter.GtfsRealtimeGuiceBindingTypes.TripUpdates;\r\n-import org.onebusaway.guice.jsr250.JSR250Module;\r\n-import org.onebusaway.guice.jsr250.LifecycleService;\r\n+import org.onebusaway.gtfs_realtime.exporter.GtfsRealtimeGuiceBindingTypes.VehiclePositions;\r\n+import org.onebusaway.gtfs_realtime.exporter.GtfsRealtimeSink;\r\n import org.slf4j.Logger;\r\n import org.slf4j.LoggerFactory;\r\n \r\n-import com.google.transit.realtime.GtfsRealtime.FeedEntity;\r\n-import com.google.transit.realtime.GtfsRealtime.FeedMessage;\r\n-import com.google.transit.realtime.GtfsRealtime.Position;\r\n-import com.google.transit.realtime.GtfsRealtime.TripDescriptor;\r\n-import com.google.transit.realtime.GtfsRealtime.TripDescriptor.ScheduleRelationship;\r\n-import com.google.transit.realtime.GtfsRealtime.TripUpdate;\r\n-import com.google.transit.realtime.GtfsRealtime.TripUpdate.StopTimeEvent;\r\n-import com.google.transit.realtime.GtfsRealtime.TripUpdate.StopTimeUpdate;\r\n-import com.google.transit.realtime.GtfsRealtime.VehicleDescriptor;\r\n-import com.google.transit.realtime.GtfsRealtime.VehiclePosition;\r\n-import com.google.transit.realtime.GtfsRealtime.VehiclePosition.OccupancyStatus;\r\n-\r\n+import javax.annotation.PostConstruct;\r\n+import javax.annotation.PreDestroy;\r\n+import javax.inject.Singleton;\r\n+import java.io.*;\r\n+import java.net.SocketTimeoutException;\r\n import java.net.URL;\r\n-\r\n-import com.google.inject.Guice;\r\n-import com.google.inject.Inject;\r\n-import com.google.inject.Injector;\r\n-import com.google.inject.Module;\r\n+import java.net.URLConnection;\r\n+import java.nio.file.Files;\r\n+import java.nio.file.Paths;\r\n+import java.time.Instant;\r\n+import java.util.Calendar;\r\n+import java.util.Iterator;\r\n+import java.util.Map;\r\n+import java.util.concurrent.Executors;\r\n+import java.util.concurrent.ScheduledExecutorService;\r\n+import java.util.concurrent.TimeUnit;\r\n \r\n /**\r\n  * This class produces GTFS-realtime trip updates and vehicle positions by\r\n  * periodically polling the custom SEPTA vehicle data API and converting the\r\n  * resulting vehicle data into the GTFS-realtime format.\r\n- * \r\n+ *\r\n  * Since this class implements {@link GtfsRealtimeProvider}, it will\r\n  * automatically be queried by the {@link GtfsRealtimeExporterModule} to export\r\n  * the GTFS-realtime feeds to file or to host them using a simple web-server, as\r\n  * configured by the client.\r\n- * \r\n+ *\r\n  * @author bdferris\r\n- * \r\n+ *\r\n  */\r\n @Singleton\r\n public class GtfsRealtimeProviderImpl {\r\n- \r\n-\tprivate static final Logger _log = LoggerFactory\r\n-\t\t\t.getLogger(GtfsRealtimeProviderImpl.class);\r\n-\tprivate String responseTimeStamp;\r\n-\tprivate ScheduledExecutorService _executor;\r\n-\r\n-\tprivate GtfsRealtimeExporterCutr _gtfsRealtimeProvider;\r\n-\tprivate URL _url;\r\n-\tprivate BiHashMap<String, String, Float> routeVehiDirMap;\r\n-\tprivate BiHashMap<String, String, vehicleInfo> tripVehicleInfoMap;\r\n-\tprivate BiHashMap<String, String, StartTimes> routeVehicleStartTimeMap;\r\n-\t\r\n-\t/**\r\n-\t * How often vehicle data will be downloaded, in seconds.\r\n-\t */\r\n-\tprivate int _refreshInterval = 30;\r\n-\tprivate BullRunnerConfigExtract _providerConfig;\r\n-\r\n-\t@Inject\r\n-\tpublic void setGtfsRealtimeProvider(\r\n-\t\t\tGtfsRealtimeExporterCutr gtfsRealtimeProvider) {\r\n-\t\t_gtfsRealtimeProvider = gtfsRealtimeProvider;\r\n-\t}\r\n-\r\n-\t/**\r\n-\t * @param url\r\n-\t *            the URL for the SEPTA vehicle data API.\r\n-\t */\r\n-\tpublic void setUrl(URL url) {\r\n-\t\t_url = url;\r\n-\t\t// System.out.println(_url.toString());\r\n-\t}\r\n-\r\n-\t/**\r\n-\t * @param refreshInterval\r\n-\t *            how often vehicle data will be downloaded, in seconds.\r\n-\t */\r\n-\tpublic void setRefreshInterval(int refreshInterval) {\r\n-\t\t_refreshInterval = refreshInterval;\r\n-\t}\r\n-\r\n-\t/**\r\n-\t * The start method automatically starts up a recurring task that\r\n-\t * periodically downloads the latest vehicle data from the SEPTA vehicle\r\n-\t * stream and processes them.\r\n-\t */\r\n-\t@Inject\r\n-\tpublic void setProvider(BullRunnerConfigExtract providerConfig) {\r\n-\t\t_providerConfig = providerConfig;\r\n-\t}\r\n-\r\n-\r\n-        private GtfsRealtimeSink _vehiclePositionsSink;\r\n-\tprivate GtfsRealtimeSink _tripUpdatesSink;\r\n-\r\n-        @Inject\r\n-        public void setVehiclePositionsSink(@VehiclePositions GtfsRealtimeSink vehiclePositionsSink) {\r\n-  \t     _vehiclePositionsSink = vehiclePositionsSink;\r\n-\t}\r\n-\r\n-        @Inject\r\n-        public void setTripUpdatesSink(@TripUpdates GtfsRealtimeSink tripUpdatesSink) {\r\n-             _tripUpdatesSink = tripUpdatesSink;\r\n-        }\r\n \r\n-  \r\n-\t@PostConstruct\r\n-\tpublic void start() {\r\n-\t\t\r\n-\t\trouteVehicleStartTimeMap = new BiHashMap<String, String, StartTimes>();\r\n-\t\t\r\n-\t\ttry {\r\n-\t\t\t//_providerConfig .setUrl(new URL( \"http://usfbullrunner.com/region/0/routes\"));\r\n-\t\t\t_providerConfig.generatesRouteMap(new URL( \"https://usfbullrunner.com/region/0/routes\"));\r\n-\t\t\t_providerConfig.generateTripMap();\r\n-\t\t\t_providerConfig.generateServiceMap();\r\n-\t\t\t_providerConfig.extractSeqId();\r\n-\t\t\t_providerConfig.extractStartTime();\r\n-\t\t\t\r\n-\t\t\t\r\n-\t\t} catch (Exception ex) {\r\n-\t\t\t_log.warn(\"Error in retriving confirmation data!\", ex);\r\n-\t\t}\r\n-\t\t_log.info(\"starting GTFS-realtime service\");\r\n-\t\t_executor = Executors.newSingleThreadScheduledExecutor();\r\n-\t\t_executor.scheduleAtFixedRate(new VehiclesRefreshTask(), 0,\r\n-\t\t\t\t_refreshInterval, TimeUnit.SECONDS);\r\n-\t}\r\n-\r\n-\t/**\r\n-\t * The stop method cancels the recurring vehicle data downloader task.\r\n-\t */\r\n-\t@PreDestroy\r\n-\tpublic void stop() {\r\n-\t\t_log.info(\"stopping GTFS-realtime service\");\r\n-\t\t_executor.shutdownNow();\r\n-\t}\r\n-\r\n-\t/****\r\n-\t * Private Methods - Here is where the real work happens\r\n-\t ****/\r\n-\r\n-\t/**\r\n-\t * This method downloads the latest vehicle data, processes each vehicle in\r\n-\t * turn, and create a GTFS-realtime feed of trip updates and vehicle\r\n-\t * positions as a result.\r\n-\t */\r\n-\tprivate void refreshTripVehicle() throws IOException, JSONException {\r\n-\t\t \r\n-\t\tPair pair = downloadVehicleDetails();\r\n-\t\tJSONArray stopIDsArray = pair.getArray1();\r\n-\t\tJSONArray vehicleArray = pair.getArray2();\r\n-\t\t \r\n-\t\tif (stopIDsArray.length() == 0) {\r\n-\t\t\trouteVehicleStartTimeMap.clear();\r\n-\t\t}\r\n-\t\tCalendar cal = Calendar.getInstance();\r\n-\t\tint dayOfWeek = cal.get(Calendar.DAY_OF_WEEK) -1;\r\n-\t\tString serviceID = _providerConfig.serviceIds[dayOfWeek];\r\n- \r\n-\t\tGtfsRealtimeFullUpdate tripUpdates = new GtfsRealtimeFullUpdate();\t\t\r\n-  \t\tGtfsRealtimeFullUpdate vehiclePositions = new GtfsRealtimeFullUpdate();\r\n-\r\n-\t\t VehicleDescriptor.Builder vehicleDescriptor = null;\r\n-\t\t String route, trip;\r\n-\t\t int entity = 0;\r\n-\t\t int vehicleFeedID = 0;\r\n-\t\t String stopId = \"\";\r\n-\t\t String startTime = \"\";\r\n-\t\t String stopSeq;\r\n-\t\t long predictTime = 0;\r\n-\t\t TripUpdate.Builder tripUpdate = null;\r\n-\t\t TripDescriptor.Builder tripDescriptor = null;\r\n-\t\t \r\n-\t\t \r\n-\t\t List <TripUpdate.Builder> tripUpdateArr = new ArrayList<>();\r\n-\t\t List <stopTimeUpdateRecord> records = new ArrayList<stopTimeUpdateRecord>();\r\n-\t\t routeVehiDirMap = new BiHashMap<String, String, Float>();\r\n-\t\t tripVehicleInfoMap = new BiHashMap<String, String, vehicleInfo>();\r\n-\t\t BiHashMap<String, String, TripUpdate.Builder> tripUpdateMap =  new BiHashMap<String, String, TripUpdate.Builder>();\r\n-\t\t\t \r\n-\t\t for (int i = 0; i < stopIDsArray.length(); i ++) {\r\n-\t\t\t\tJSONObject obj = stopIDsArray.getJSONObject(i);\r\n-\t\t\t\troute = obj.getString(\"route\").substring(6); \t\t\t\r\n-\t\t\t\ttrip = _providerConfig.tripIDMap.get(route, serviceID);\t\r\n-\t\t\t\tif (trip == null || trip.equals(\"\"))\r\n-\t\t\t\t\t_log.error(\"Route \"+ route+ \"dosn't exit in GTFS file\");\r\n-\t\t\t\tint stopId_int = obj.getInt(\"stop\");\r\n-\t\t\t\tstopId = Integer.toString(stopId_int);\r\n-\t\t\t\tJSONArray childArray = obj.getJSONArray(\"Ptimes\");\t\t\t \r\n-\t\t\t\t\r\n-\t\t\t\tfor (int j = 0; j < childArray.length(); j++) {\r\n-\t\t\t\t\t\r\n-\t\t\t\t\tJSONObject child = childArray.getJSONObject(j);\r\n-\t\t\t\t\tString predTimeStamp = child.getString(\"PredictionTime\");\r\n-\t\t\t\t\tpredictTime = convertTime(predTimeStamp);\r\n-\t\t\t\t\tString vehicleId = child.getString(\"VehicleId\");\r\n-\t\t\t\t\t\r\n-\t\t\t\t\tif (!tripUpdateMap.containsKey(route, vehicleId)){\r\n-\t\t\t\t\t\ttripUpdate = TripUpdate.newBuilder();\r\n-\t\t\t\t\t\tvehicleDescriptor = VehicleDescriptor.newBuilder();\r\n-\t\t\t\t\t\tvehicleDescriptor.setId(vehicleId);\r\n-\t\t\t\t\t\ttripDescriptor = TripDescriptor.newBuilder();\t\t\t\t\t\r\n-\t\t\t\t\t\ttripDescriptor.setRouteId(route);\r\n-\t\t\t\t\t\ttripDescriptor.setTripId(trip);\r\n-\t\t\t\t\t\ttripUpdate.setVehicle(vehicleDescriptor);\r\n-\t\t\t\t\t\ttripUpdate.setTrip(tripDescriptor);\t\r\n-\t\t\t\t\t\ttripUpdateMap.put(route, vehicleId, tripUpdate);\r\n-\t\t\t\t\t\ttripUpdateArr.add(tripUpdate);\t\t\t\t\t \r\n-\t\t\t\t\t}else{\r\n-\t\t\t\t\t\ttripUpdate = tripUpdateMap.get(route, vehicleId);\t\t\r\n-\t\t\t\t\t}\r\n-\t\t\t\t\t\r\n-\t\t\t\t\tStopTimeEvent.Builder arrival = StopTimeEvent.newBuilder();\r\n-\t\t\t\t\tarrival.setTime(predictTime);\r\n-\t\t\t\t\tStopTimeUpdate.Builder stopTimeUpdate = StopTimeUpdate.newBuilder();\r\n-\t\t\t\t\tstopTimeUpdate.setArrival(arrival);\r\n-\t\t\t\t\tstopTimeUpdate.setStopId(stopId);\t\t\t\t\t\r\n-\t\t\t\t\t\r\n-\t\t\t\t\tstopSeq = _providerConfig.stopSeqIDMap.get(trip, stopId);\r\n-\t\t\t\t\t if( stopSeq == null){\r\n-\t\t\t\t\t\tstopSeq = \"0\";\r\n-\t\t\t\t\t\t_log.warn(\"Error stopID: \"+ stopId+ \" is not available in GTFS files\");\r\n-\t\t\t\t\t\tSystem.out.println(\"Error: stopID: \"+ stopId+ \" is not available in GTFS files\");\r\n-\t\t\t\t\t}\r\n-\t\t\t\t\t \r\n-\t\t\t\t\tif (stopSeq.equals(\"1\")){\r\n-\t\t\t\t\t\tstartTime = convert2FormattedTime(predTimeStamp);\t\t\t\t\t\t \r\n-\t\t\t\t\t\t//System.out.println(\"stopSeq =1,  route \"+ route+ \", vehicleID = \"+ vehicleId);\r\n-\t\t\t\t\t\ttripDescriptor.setStartTime(startTime);\t\r\n-\t\t\t\t\t\ttripDescriptor.setScheduleRelationship(ScheduleRelationship.UNSCHEDULED);\r\n-\t\t\t\t\t\ttripUpdate.setTrip(tripDescriptor);\r\n-\t\t\t\t\t\t \r\n-\t\t\t\t\t\tif (routeVehicleStartTimeMap.containsKeys(route, vehicleId)){\r\n-\t\t\t\t\t\t\tStartTimes startTimes= routeVehicleStartTimeMap.get(route, vehicleId);\r\n-\t\t\t\t\t\t\tstartTimes.previousStartT = startTimes.currentStartT;\r\n-\t\t\t\t\t\t\tstartTimes.currentStartT = startTime;\r\n-\t\t\t\t\t\t} else{\r\n-\t\t\t\t\t\t\tStartTimes startTInstance = new StartTimes(startTime, \"0\");\r\n-\t\t\t\t\t\t\trouteVehicleStartTimeMap.put(route, vehicleId, startTInstance);\r\n-\t\t\t\t\t\t}\r\n-\t\t\t\t\t\t//System.out.println(\"current starttime = \"+ tripUpdate.getTrip().getStartTime());\r\n-\t\t\t\t\t}\r\n-\t\t\t\t\tstopTimeUpdate.setStopSequence(Integer.parseInt(stopSeq));\r\n-\t\t\t\t\trecords.add(new stopTimeUpdateRecord(tripUpdate, stopTimeUpdate));\r\n-\t\t\t\t\t//tripUpdate.addStopTimeUpdate(stopTimeUpdate);\t\t\t\t\t\t\t\r\n-\t\t\t\t}\t\t\t\t\r\n-\t\t }\r\n-\t\t Collections.sort(records);\r\n-\t\t \r\n-\t\t\r\n-\t\t for (int i=0; i< records.size();i++){  \r\n-\t\t\t records.get(i).tripUpdate.addStopTimeUpdate(records.get(i).stopTimeUpdate);\r\n-\t\t }\r\n-\t\t \r\n-\t\t/**\r\n-\t\t * Create a new feed entity to wrap the trip update and add it\r\n-\t\t * to the GTFS-realtime trip updates feed.\r\n-\t\t */\t\r\n-\t\t\r\n-\t\t for (int j = 0; j < tripUpdateArr.size(); j++){\r\n-\t\t\tFeedEntity.Builder tripUpdateEntity = FeedEntity.newBuilder();\r\n-\t\t\ttripUpdate = tripUpdateArr.get(j);\r\n-\t\t\t//System.out.println(\"-----size of trip Updates = \" + tripUpdate.getStopTimeUpdateList().size());\r\n-\t\t\t\r\n-\t\t\tint noStopTimes = tripUpdate.getStopTimeUpdateList().size();\r\n-\t\t\troute = tripUpdate.getTrip().getRouteId();\r\n-\t\t\ttrip = tripUpdate.getTrip().getTripId();\r\n-\t\t\tString vehicleId = tripUpdate.getVehicle().getId();\r\n-\t\t\t \r\n-\t\t\tif (tripUpdate.getStopTimeUpdate(0).getStopSequence() != 1){\r\n-\t\t\t\tStartTimes startTInstance;\r\n-\t\t\t\tif (routeVehicleStartTimeMap.containsKeys(route, vehicleId)){\r\n-\t\t\t\t\tstartTInstance = routeVehicleStartTimeMap.get(route, vehicleId);\r\n-\t\t\t\t} else {\r\n-\t\t\t\t\t//cold start\r\n-\t\t\t\t\tstartTInstance = new StartTimes(\"0\", \"0\");\t \r\n-\t\t\t\t\trouteVehicleStartTimeMap.put(route, vehicleId, startTInstance);\r\n-\t\t\t\t}\r\n-\r\n-\t\t\t\tTripDescriptor.Builder newTripDescriptor = TripDescriptor.newBuilder();\r\n-\t\t\t\tnewTripDescriptor.setTripId(trip);\r\n-\t\t\t\tnewTripDescriptor.setRouteId(route);\r\n-\t\t\t\tnewTripDescriptor.setStartTime(startTInstance.currentStartT);\r\n-\t\t\t\tnewTripDescriptor.setScheduleRelationship(ScheduleRelationship.UNSCHEDULED);\r\n-\t\t\t\ttripUpdate.setTrip(newTripDescriptor);\r\n-\r\n-\t\t\t\t}\r\n-//\t\t\tSystem.out.println(\"responseTimeStamp: \"+ responseTimeStamp + \"change it to unix: \"+ responseTimeStamp); \r\n-\t\t\tint delay;\r\n-\t\t\tlong preTime = 0;\r\n-\t\t\tfor(int h = 0; h < noStopTimes; h++){\r\n-\t\t\t\tlong timeStamp = tripUpdate.getStopTimeUpdate(h).getArrival().getTime();\r\n-\t\t\t\tif (timeStamp < preTime){\r\n-\t\t\t\t\tdelay = calcDelayTime(timeStamp );\r\n-\t\t\t\t\tif ( 60 < delay  ){\r\n-\t\t\t\t\t\t \r\n-\t\t\t\t\t\tStopTimeEvent.Builder arrival = StopTimeEvent.newBuilder();\r\n-\t\t\t\t\t\tarrival.setTime(timeStamp);\r\n-\t\t\t\t\t\t\r\n-\t\t\t\t\t\tStopTimeUpdate preStopTime = tripUpdate.getStopTimeUpdate(h-1);\r\n-\t\t\t\t\t\tStopTimeUpdate.Builder newStopTimeUpdate = StopTimeUpdate.newBuilder(preStopTime);\r\n-\t\t\t\t\t\tnewStopTimeUpdate.setArrival(arrival);\r\n-\t\t\t\t\t\ttripUpdate.setStopTimeUpdate(h-1, newStopTimeUpdate);\r\n-\t\t\t\t\t\tpreTime = timeStamp;\r\n-//\t\t\t\t\t\tSystem.out.println(\"** h = \"+ h+ \" is \"+ timeStamp+ \" routeID: \"+tripUpdate.getTrip().getRouteId()+ \" vehicle: \"+tripUpdate.getVehicle().getId());\r\n-//\t\t\t\t\t\tSystem.out.println(\"\\t DELAY:\"+ delay + \", stop time of h-1 change to \"+ tripUpdate.getStopTimeUpdate(h-1).getArrival().getTime()+ \" from \"+ preTime);\r\n-\t\t\t\t\t\t\r\n-\t\t\t\t\t} else {\r\n-//\t\t\t\t\t\tSystem.out.println(\" h = \" + h+\" routeID: \"+tripUpdate.getTrip().getRouteId()+ \" vehicle: \"+tripUpdate.getVehicle().getId());\r\n-\t\t\t\t\t\t\r\n-\t\t\t\t\t\tList <StopTimeUpdate> allStopUpdates = tripUpdate.getStopTimeUpdateList();\r\n-\t\t\t\t\t\ttripUpdate.clearStopTimeUpdate();\r\n-\t\t\t\t\t\tTripUpdate.Builder newTripUpdate = tripUpdate.clone();\r\n-\t\t\t\t\t\t \r\n-\t\t\t\t\t\t// we have to send out the old tripUpdate, but before that the rest of stopTimes should be deleted from it\t\t\r\n-\t\t\t\t\t\tnewTripUpdate.addAllStopTimeUpdate(allStopUpdates.subList(0, h));\r\n-\t\t\t\t\t\tentity ++; \r\n-\t\t\t\t\t\ttripUpdateEntity.setId(Integer.toString(entity));\r\n-\t\t\t\t\t\ttripUpdateEntity.setTripUpdate(newTripUpdate);\r\n-\t\t\t\t\t\ttripUpdates.addEntity(tripUpdateEntity.build());\r\n-\t\t\t\t\t\t//System.out.println(\"what has been sent: size = \"+ newTripUpdate.getStopTimeUpdateList().size()+ \" stoptime\");\r\n-\t\t\t\t\t\t\r\n-\t\t\t\t\t\ttripUpdate.addAllStopTimeUpdate(allStopUpdates.subList(h, noStopTimes));\r\n-\t\t\t\t\t\t//System.out.println(\"current tripUpdate size = \"+ tripUpdate.getStopTimeUpdateList().size()+ \" stoptime\");\r\n-\t\t\t\t\t\tpreTime = 0;\r\n-\t\t\t\t\t\tnoStopTimes = noStopTimes - h;\r\n-\t\t\t\t\t\th = -1; \r\n-\t\t\t\t\t\tStartTimes startTimes;\r\n-\t\t\t\t\t\tif (routeVehicleStartTimeMap.containsKeys(route, vehicleId))\r\n-\t\t\t\t\t\t\tstartTimes = routeVehicleStartTimeMap.get(route, vehicleId);\r\n-\t\t\t\t\t\telse{\r\n-\t\t\t\t\t\t\tstartTimes = new StartTimes(startTime, \"0\");\t\t\t\t\t\t \r\n-\t\t\t\t\t\t\trouteVehicleStartTimeMap.put(route, vehicleId, startTimes);\r\n-\t\t\t\t\t\t}\r\n-\t\t\t\t\t\t \r\n-\t\t\t\t\t\tString previousStartT = startTimes.previousStartT; \r\n-\t\t\t\t\t\tTripDescriptor.Builder newTripDescriptor = TripDescriptor.newBuilder();\r\n-\t\t\t\t\t\tnewTripDescriptor.setTripId(trip);\r\n-\t\t\t\t\t\tnewTripDescriptor.setRouteId(route);\r\n-\t\t\t\t\t\tnewTripDescriptor.setStartTime(previousStartT);\r\n-\t\t\t\t\t\tnewTripDescriptor.setScheduleRelationship(ScheduleRelationship.UNSCHEDULED);\r\n-\t\t\t\t\t\ttripUpdate.setTrip(newTripDescriptor);\r\n-\t\t\t\t\t\t//System.out.println(\"second startTime = \"+ tripUpdate.getTrip().getStartTime()+ \"  &&&& first startTime = \"+ newTripUpdate.getTrip().getStartTime());\r\n-\t\t\t\t\t}\r\n-\t\t\t\t}\r\n-\t\t\t\telse\r\n-\t\t\t\t\tpreTime = timeStamp;\r\n-\t\t\t}\r\n-\t\t\t \r\n-\t\t\tentity ++;\r\n-\t\t\t \r\n-\t\t\ttripUpdateEntity.setId(Integer.toString(entity));\r\n-\t\t\ttripUpdateEntity.setTripUpdate(tripUpdate);\r\n-\t\t\ttripUpdates.addEntity(tripUpdateEntity.build());\r\n-\t\t }\r\n-\t\t _tripUpdatesSink.handleFullUpdate(tripUpdates);\r\n-\t\t  \r\n-\t\t\t _log.info(\"stoIDs extracted: \" + tripUpdates.getEntities().size());\r\n-\t\t\t// System.out.println(\"stoIDs extracted: \" + tripUpdates.getEntityCount());\r\n-\t\t\t\r\n-\t\t\t \r\n-\t\t\t \r\n-\t\t\t float bearing;\r\n-\t\t\t for (int k = 0; k < vehicleArray.length(); k++) {\r\n-\t\t\t\t\tJSONObject vehicleObj = vehicleArray.getJSONObject(k);\r\n-\r\n-\t\t\t\t\t// We only operate on \"Route X\" routes and ignore other strings\r\n-\t\t\t\t\tif (vehicleObj.getString(\"route\").length() != 7) continue; \r\n-\r\n-\t\t\t\t\troute = vehicleObj.getString(\"route\").substring(6);\t\t \t\t\t\r\n-\t\t\t\t\tJSONArray vehicleLocsArray = vehicleObj .getJSONArray(\"VehicleLocation\");\r\n-\t\t\t\t\t\r\n-\t\t\t\t\tfor (int l = 0; l < vehicleLocsArray.length(); ++l) {\r\n-\t\t\t\t\t\tJSONObject child = vehicleLocsArray.getJSONObject(l);\r\n-\t\t\t\t\t\tdouble lat = child.getDouble(\"vehicleLat\");\r\n-\t\t\t\t\t\tdouble lon = child.getDouble(\"vehicleLong\");\r\n-\t\t\t\t\t\t/**\r\n-\t\t\t\t\t\t * To construct our VehiclePosition, we create a position for\r\n-\t\t\t\t\t\t * the vehicle. We add the position to a VehiclePosition\r\n-\t\t\t\t\t\t * builder, along with the trip and vehicle descriptors.\r\n-\t\t\t\t\t\t */\r\n-\t\t\t\t\t\ttripDescriptor = TripDescriptor.newBuilder();\r\n-\t\t\t\t\t\ttripDescriptor.setRouteId(route);\r\n-\t\t\t\t\t\tPosition.Builder position = Position.newBuilder();\r\n-\t\t\t\t\t\t//position.setLatitude((float) lat);\r\n-\t\t\t\t\t\t//position.setLongitude((float) lon);\r\n-\r\n-\t\t\t\t\t\tFeedEntity.Builder vehiclePositionEntity = FeedEntity\r\n-\t\t\t\t\t\t\t\t.newBuilder();\r\n-\t\t\t\t\t\t//int tripID_int = child.getInt(\"tripId\"); \r\n-\t\t\t\t\t\tString vehicleId = child.getString(\"VehicleId\");\t\r\n-\t\t\t\t\t\tif (!routeVehiDirMap.containsKey(route))\t\t\t\t\t\t\r\n-\t\t\t\t\t\t\textractHeading(route);\r\n-\r\n-\t\t\t\t\t\tvehicleInfo info = tripVehicleInfoMap.get(route, vehicleId);\r\n-\t\t\t\t\t\t\r\n-\t\t\t\t\t\t//_log.info(\"vehicles' info: \" + info); \r\n-\t\t\t\t\t\tbearing = info.bearing;\r\n-\t\t\t\t\t\t\r\n-\t\t\t\t\t\t\t\t//routeVehiDirMap.get(route, vehicleId);\t\r\n-\t\t\t\t\t\tposition.setBearing(bearing);\r\n-\t\t\t\t\t\tposition.setLatitude(info.lat);\r\n-\t\t\t\t\t\tposition.setLongitude(info.longi);\r\n-\t\t\t\t\t\tVehiclePosition.Builder vehiclePosition = VehiclePosition.newBuilder();\r\n-\t\t\t\t\t\tvehiclePosition.setPosition(position);\r\n-\t\t\t\t\t\tvehiclePosition.setTrip(tripDescriptor);\r\n-\r\n-\t\t\t\t\t\tif (info.APCPercentage <= 0) vehiclePosition.setOccupancyStatus( VehiclePosition.OccupancyStatus.EMPTY );\r\n-\t\t\t\t\t\telse if (info.APCPercentage <= 50) vehiclePosition.setOccupancyStatus( VehiclePosition.OccupancyStatus.MANY_SEATS_AVAILABLE );\r\n-\t\t\t\t\t\telse if (info.APCPercentage <= 70) vehiclePosition.setOccupancyStatus( VehiclePosition.OccupancyStatus.FEW_SEATS_AVAILABLE );\r\n-\t\t\t\t\t\telse if (info.APCPercentage <= 90) vehiclePosition.setOccupancyStatus( VehiclePosition.OccupancyStatus.STANDING_ROOM_ONLY );\r\n-\t\t\t\t\t\telse if (info.APCPercentage <= 95) vehiclePosition.setOccupancyStatus( VehiclePosition.OccupancyStatus.CRUSHED_STANDING_ROOM_ONLY );\r\n-\t\t\t\t\t\telse vehiclePosition.setOccupancyStatus( VehiclePosition.OccupancyStatus.FULL );\r\n-\r\n-\t\t\t\t\t\tvehicleDescriptor = VehicleDescriptor.newBuilder();\r\n-\t\t\t\t\t\tvehicleDescriptor.setId(vehicleId);\r\n-\t\t\t\t\t\t\r\n-\t\t\t\t\t\tvehicleFeedID ++;\r\n-\r\n-\t\t\t\t\t\tvehiclePositionEntity.setId(Integer.toString(vehicleFeedID));\r\n-\t\t\t\t\t\tvehiclePosition.setVehicle(vehicleDescriptor);\r\n-\t\t\t\t\t\tvehiclePositionEntity.setVehicle(vehiclePosition);\r\n-\t\t\t\t\t\t\r\n-\t\t\t\t\t\tvehiclePositions.addEntity(vehiclePositionEntity.build());\r\n-\t\t\t\t\t\t\r\n-\t\t\t\t\t}\r\n-\t\t \t\t}\r\n-\t\t\t _vehiclePositionsSink.handleFullUpdate(vehiclePositions);\r\n-\t\t\t _log.info(\"vehicles' location extracted: \" + vehiclePositions.getEntities().size());\t\r\n-\t\t\t //System.out.println(\"vehicles' location extracted: \" + vehiclePositions.getEntityCount());\r\n-\t}\r\n- \r\n- \r\n-\t/**\r\n-\t * @return a JSON array parsed from the data pulled from the SEPTA vehicle\r\n-\t *         data API.\r\n-\t */\r\n-\tpublic class Pair {\r\n-\t\tprivate JSONArray array1;\r\n-\t\tprivate JSONArray array2;\r\n-\r\n-\t\tpublic Pair(JSONArray array1, JSONArray array2) {\r\n-\t\t\tthis.array1 = array1;\r\n-\t\t\tthis.array2 = array2;\r\n-\r\n-\t\t}\r\n-\r\n-\t\tpublic JSONArray getArray1() {\r\n-\t\t\treturn array1;\r\n-\t\t}\r\n-\r\n-\t\tpublic JSONArray getArray2() {\r\n-\t\t\treturn array2;\r\n-\t\t}\r\n-\t}\r\n-\r\n-\tprivate Pair downloadVehicleDetails() throws IOException, JSONException {\r\n-\t\tURLConnection connection = null;\r\n-\t\ttry {\r\n-\t\t    connection = _url.openConnection();\r\n-\t\t  } catch (Exception ex) {\r\n-\t\t    _log.error(\"Error in opening feeds url\", ex);\r\n-\t\t  }\r\n-\t\t  \r\n-\t\t  connection.setConnectTimeout(10000);  // connectTimeout is time out in miliseconds\r\n-\t\t  connection.setReadTimeout(10000);\r\n-\t\t  java.io.InputStream in =  connection.getInputStream();\r\n-\r\n-\t\t  BufferedReader reader = new BufferedReader(new InputStreamReader(in, \"UTF-8\"));\r\n-\r\n-\t\t  StringBuilder builder = new StringBuilder();\r\n-\t\t  String inputLine;\r\n-\t\t  JSONArray stopIDsArray;\r\n-\t\t  JSONArray vehicleArray;\r\n-\t\t  try {\r\n-\t\t    while ((inputLine = reader.readLine()) != null)\r\n-\t\t      builder.append(inputLine).append(\"\\n\");\r\n-\r\n-\t\t    JSONObject object = (JSONObject) new JSONTokener(builder.toString())\r\n-\t\t            .nextValue();\r\n-\r\n-\t\t    String data = object.getString(\"PredictionData\");\r\n-\t\t    JSONObject child2_obj = new JSONObject(data);\r\n-\t\t    responseTimeStamp = child2_obj.getString(\"TimeStamp\");\r\n-\t\t     stopIDsArray = child2_obj.getJSONArray(\"StopPredictions\");\r\n-\t\t     vehicleArray = child2_obj.getJSONArray(\"VehicleLocationData\");\r\n-\r\n-\t\t  } catch (java.net.SocketTimeoutException ex) {\r\n-\t\t\t  _log.error(\"Error readline, server dosn't close the connection.\", ex);\r\n-\t\t    stopIDsArray = null;\r\n-\t\t    vehicleArray = null;\r\n-\t\t  }\r\n-\t\t  return new Pair(stopIDsArray, vehicleArray);\r\n-\r\n-\t \r\n-\t}\r\n-\r\n-\t/**\r\n-\t * Task that will download new vehicle data from the remote data source when\r\n-\t * executed.\r\n-\t */\r\n-\tprivate class VehiclesRefreshTask implements Runnable {\r\n-\r\n-\t\t@Override\r\n-\t\tpublic void run() {\r\n-\t\t\ttry {\r\n-\t\t\t\t_log.info(\"refreshing vehicles\");\r\n-\t\t\t\trefreshTripVehicle();\r\n-\t\t\t\t//test_refreshVehicles();\r\n-\t\t\t} catch (Exception ex) {\r\n-\t\t\t\t_log.warn(\"Error in vehicle refresh task\", ex);\r\n-\t\t\t}\r\n-\t\t}\r\n-\t}\r\n-\tprivate int calcDelayTime(long arrivalTime){\r\n-\t\tint diff;\r\n-\t\tString pattern = \"yyyy-MM-dd'T'HH:mm:ssZ\";\r\n-\t\tDateTimeFormatter dtf = DateTimeFormat.forPattern(pattern);\r\n-\t\tDateTime parsedDate = dtf.parseDateTime(responseTimeStamp);\t \r\n-\t\tdiff = (int) (arrivalTime - parsedDate.getMillis()/1000);\r\n-\t\treturn diff;\r\n-\t}\r\n-\tprivate String convert2FormattedTime(String myTimeStamp){\r\n-\t\t\r\n-\t\t   DateTimeFormatter dtf = DateTimeFormat.forPattern(\"yyyy-MM-dd'T'HH:mm:ssZ\");\r\n-\t\t   DateTime jodatime = dtf.parseDateTime(myTimeStamp);\r\n-\t\t   DateTimeFormatter dtfOut = DateTimeFormat.forPattern(\"HH:mm:ss\"); \r\n-\t\t   return dtfOut.print(jodatime);\r\n-\t   }\r\n-   \r\n-\t// This method extract time from timestamp\r\n-\tprivate long convertTime(String myTimeStamp) {\r\n-\r\n-\t\t//final SimpleDateFormat sdf = new SimpleDateFormat( \"yyyy-MM-dd'T'HH:mm:ssXXX\");\r\n-\t\tDateFormat dateFormat = new SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm:ssZ\"){ \r\n-\t\t    public Date parse(String source,ParsePosition pos) {    \r\n-\t\t        return super.parse(source.replaceFirst(\":(?=[0-9]{2}$)\",\"\"),pos);\r\n-\t\t    }\r\n-\t\t};\r\n-\t\tDate time;// = new Date();\r\n-\t\tlong result = 0;\r\n-\t\ttry {\r\n-\t\t\t//dateFormat.setTimeZone(TimeZone.getTimeZone(\"GMT\"));\r\n-\t\t\t//dateFormat.setTimeZone(dateFormat.getTimeZone());\r\n-\t\t\ttime = dateFormat.parse(myTimeStamp);\r\n-\t\t\tresult = time.getTime()/1000; \r\n-\t\t} catch (ParseException e) {\r\n-\t\t\t// TODO Auto-generated catch block\r\n-\t\t\te.printStackTrace();\r\n-\t\t}\r\n-\r\n-\t\treturn result;\r\n-\r\n-\t}\r\n-\r\n-\t \r\n-\tprivate static float getDirVal(String direction) {\r\n+    private static final Logger _log = LoggerFactory\r\n+            .getLogger(GtfsRealtimeProviderImpl.class);\r\n+    private static final int _Conn_Timeout_MS = (int) TimeUnit.SECONDS.toMillis(10); // connection time out 10s\r\n+    private ScheduledExecutorService _executor;\r\n+    private GtfsRealtimeExporterCutr _gtfsRealtimeProvider;\r\n+    private URL _url;\r\n+    private String _api_key;\r\n+\r\n+    /**\r\n+     * How often vehicle data will be downloaded, in seconds.\r\n+     */\r\n+    private int _refreshInterval = 30;\r\n+    private BullRunnerConfigExtract _providerConfig;\r\n+\r\n+    @Inject\r\n+    public void setGtfsRealtimeProvider(\r\n+            GtfsRealtimeExporterCutr gtfsRealtimeProvider) {\r\n+        _gtfsRealtimeProvider = gtfsRealtimeProvider;\r\n+    }\r\n+\r\n+\r\n+    private GtfsRealtimeSink _vehiclePositionsSink;\r\n+    private GtfsRealtimeSink _tripUpdatesSink;\r\n+\r\n+    private static float getDirVal(String direction) {\r\n         switch (direction) {\r\n-\t      case \"N\":\r\n-\t          return 0;\r\n-\t      case \"NE\":\r\n-\t          return 45;\r\n-\t      case \"E\":\r\n-\t          return 90; \r\n-\t      case \"SE\":\r\n-\t          return 135;  \r\n-\t      case \"S\":\r\n-\t          return 180;\r\n-\t      case \"SW\":\r\n-\t          return 225;\r\n-\t      case \"W\":\r\n-\t          return 270;\r\n-\t      case \"NW\":\r\n-\t          return 315;\r\n-\t      default:{\r\n-\t    \t  System.out.println(\"this dierection is not available : \"+ direction);\r\n-\t    \t  _log.error(\"this dierection is not supported : \"+ direction);\r\n-\t    \t  return 0;\r\n-\t      }\r\n-\t         \r\n-      }\r\n-\t}\r\n-\tclass vehicleInfo {\r\n-\t    public float lat;\r\n-\t    public float longi;\r\n-\t    public float bearing;\r\n-\t    public int APCPercentage;\r\n-\t}\r\n-\tprivate void extractHeading (String route) throws IOException, JSONException{\r\n-\t\tint routeID = _providerConfig.routesMap.get(route);\t\r\n-\t\r\n-\t\tString urlStr = \"\"+ \"https://usfbullrunner.com/route/\"+ routeID+\"/vehicles\";\r\n-\t\tJSONArray jsonVehicle = _providerConfig.downloadCofiguration(new URL( urlStr ));\r\n-\t\t\r\n-\t\tfor (int i= 0; i < jsonVehicle.length(); i++ ){\r\n-\t\t\t\r\n-\t\t\tJSONObject child = jsonVehicle.getJSONObject(i);\r\n-\t\t\tString heading = child.getString(\"Heading\");\r\n-\t\t\tfloat direction = getDirVal(heading);\r\n-\t\t\tString vehicleID = child.getString(\"Name\");\r\n-\t\t\trouteVehiDirMap.put(route, vehicleID, direction);\r\n-\t\t\t\r\n-\t\t\tJSONObject coordinate = child.getJSONObject(\"Coordinate\");\r\n-\t\t\tvehicleInfo info = new vehicleInfo();\r\n-\t\t\tinfo.lat = (float) coordinate.getDouble(\"Latitude\");\r\n-\t\t\tinfo.longi = (float) coordinate.getDouble(\"Longitude\");\r\n-\t\t\tinfo.bearing = direction;\r\n-\r\n-\t\t\tinfo.APCPercentage = child.getInt(\"APCPercentage\");\r\n-\t\t\t \r\n-\t\t\ttripVehicleInfoMap.put(route, vehicleID, info);\t\t \r\n-\t\t}\r\n-\t\t\r\n-\t\r\n-\t}\r\n-\tprivate class stopTimeUpdateRecord implements Comparable<stopTimeUpdateRecord> {\r\n-\t\tpublic StopTimeUpdate.Builder stopTimeUpdate;\r\n-\t\tpublic TripUpdate.Builder tripUpdate;\r\n-\t\tpublic stopTimeUpdateRecord(TripUpdate.Builder t, StopTimeUpdate.Builder s){\r\n-\t\t\ttripUpdate = t;\r\n-\t\t\tstopTimeUpdate = s;\r\n-\t\t}\r\n-\t\t@Override\r\n-\t\tpublic int compareTo(stopTimeUpdateRecord other) {\r\n-\t\t\tint currentStopSeq = this.stopTimeUpdate.getStopSequence();\r\n-\t\t\tint otherStopSeq = other.stopTimeUpdate.getStopSequence();\r\n-\t\t\r\n-\t\t\t if (currentStopSeq == otherStopSeq)\r\n-\t\t            return 0;\r\n-\t\t        else if (currentStopSeq > otherStopSeq)\r\n-\t\t            return 1;\r\n-\t\t        else\r\n-\t\t            return -1;\r\n-\t\t}\r\n-\t}\r\n-    \r\n-\tprivate class StartTimes{\r\n-\t\tpublic String currentStartT;\r\n-\t\tpublic String previousStartT;\r\n-\t\t//constructor\r\n-\t\tpublic StartTimes(String currentStartT, String previousStartT){\r\n-\t\t\tthis.currentStartT = currentStartT;\r\n-\t\t\tthis.previousStartT = previousStartT;\r\n-\t\t}\r\n-\t}\r\n+            case \"N\":\r\n+                return 0;\r\n+            case \"NE\":\r\n+                return 45;\r\n+            case \"E\":\r\n+                return 90;\r\n+            case \"SE\":\r\n+                return 135;\r\n+            case \"S\":\r\n+                return 180;\r\n+            case \"SW\":\r\n+                return 225;\r\n+            case \"W\":\r\n+                return 270;\r\n+            case \"NW\":\r\n+                return 315;\r\n+            default: {\r\n+                System.out.println(\"this dierection is not available : \" + direction);\r\n+                _log.error(\"this dierection is not supported : \" + direction);\r\n+                return 0;\r\n+            }\r\n+\r\n+        }\r\n+    }\r\n+\r\n+    /**\r\n+     * @param url\r\n+     *            the URL for the SEPTA vehicle data API.\r\n+     */\r\n+    public void setUrl(URL url) {\r\n+        _url = url;\r\n+        // System.out.println(_url.toString());\r\n+    }\r\n+\r\n+    /**\r\n+     * Find the key file in the current directory and then in the parent directory\r\n+     * If not found, throw an Error and exit\r\n+     * @throws IOException\r\n+     */\r\n+    public void setKey() throws IOException { // try to find key file and set API key\r\n+        String keyPath;\r\n+        if (Files.exists(Paths.get(\"./key.txt\"))){\r\n+            keyPath = \"./key.txt\";\r\n+        } else if (Files.exists(Paths.get(\"../key.txt\"))){\r\n+            keyPath = \"../key.txt\";\r\n+        } else {\r\n+            throw new Error(\"KEY FILE NOT FOUND! Create key.txt file in the main directory and save the API key in the file\");\r\n+        }\r\n+        BufferedReader tripsBuffer = new BufferedReader(new FileReader(keyPath));\r\n+        String key = tripsBuffer.readLine();\r\n+        _api_key = key;\r\n+    }\r\n+\r\n+    /**\r\n+     * @param refreshInterval\r\n+     *            how often vehicle data will be downloaded, in seconds.\r\n+     */\r\n+    public void setRefreshInterval(int refreshInterval) {\r\n+        _refreshInterval = refreshInterval;\r\n+    }\r\n+\r\n+    /**\r\n+     * The start method automatically starts up a recurring task that\r\n+     * periodically downloads the latest vehicle data from the SEPTA vehicle\r\n+     * stream and processes them.\r\n+     */\r\n+    @Inject\r\n+    public void setProvider(BullRunnerConfigExtract providerConfig) {\r\n+        _providerConfig = providerConfig;\r\n+    }\r\n+\r\n+    @Inject\r\n+    public void setVehiclePositionsSink(@VehiclePositions GtfsRealtimeSink vehiclePositionsSink) {\r\n+        _vehiclePositionsSink = vehiclePositionsSink;\r\n+    }\r\n+\r\n+    @Inject\r\n+    public void setTripUpdatesSink(@TripUpdates GtfsRealtimeSink tripUpdatesSink) {\r\n+        _tripUpdatesSink = tripUpdatesSink;\r\n+    }\r\n+\r\n+    @PostConstruct\r\n+    public void start() {\r\n+\r\n+        try {\r\n+            //_providerConfig .setUrl(new URL( \"http://usfbullrunner.com/region/0/routes\"));\r\n+            _providerConfig.findPaths(); // try to find path of GTFS directory\r\n+            _providerConfig.generatesRouteMap(new URL(\"https://usfbullrunner.com/region/0/routes\"));\r\n+            _providerConfig.generateTripMap();\r\n+            _providerConfig.generateServiceMap();\r\n+            _providerConfig.extractSeqId();\r\n+            _providerConfig.extractStartTime();\r\n+            _providerConfig.generateExternalIDMap();\r\n+\r\n+        } catch (Exception ex) {\r\n+            _log.warn(\"Error in retriving confirmation data!\", ex);\r\n+        }\r\n+        _log.info(\"starting GTFS-realtime service\");\r\n+        _executor = Executors.newSingleThreadScheduledExecutor();\r\n+        _executor.scheduleAtFixedRate(new VehiclesRefreshTask(), 0,\r\n+                _refreshInterval, TimeUnit.SECONDS);\r\n+    }\r\n+\r\n+    /****\r\n+     * Private Methods - Here is where the real work happens\r\n+     ****/\r\n+\r\n+    /**\r\n+     * The stop method cancels the recurring vehicle data downloader task.\r\n+     */\r\n+    @PreDestroy\r\n+    public void stop() {\r\n+        _log.info(\"stopping GTFS-realtime service\");\r\n+        _executor.shutdownNow();\r\n+    }\r\n+\r\n+    /**\r\n+     * This method downloads the latest vehicle data, processes each vehicle in\r\n+     * turn, and create a GTFS-realtime feed of trip updates and vehicle\r\n+     * positions as a result.\r\n+     */\r\n+    private void refreshTripVehicle() throws IOException, JSONException {\r\n+\r\n+        // ---- START UPDATING VEHICLE POSITIONS -----------------\r\n+        // construct VehiclePosition feed\r\n+        GtfsRealtimeFullUpdate vehiclePositions = new GtfsRealtimeFullUpdate();\r\n+        int vehicleFeedID = 0;\r\n+        // Loop through the external route id map to get vehicle locations for each route id\r\n+        Iterator IT = _providerConfig.ExternalIDMap.entrySet().iterator();\r\n+        while (IT.hasNext()) {\r\n+            Map.Entry pair = (Map.Entry) IT.next();\r\n+            String route_id = pair.getKey().toString();\r\n+            String external_id = pair.getValue().toString();\r\n+\r\n+            // get vehicle locations for this route_id:\r\n+            JSONArray vehicleArray;\r\n+            if (route_id.equals(\"C\") || route_id.equals(\"MSC Express\")){\r\n+                vehicleArray = downloadVehicleDetails_C(route_id, external_id);\r\n+            } else {\r\n+                vehicleArray = downloadVehicleDetails(external_id);\r\n+            }\r\n+            // loop through vehicleArray to build vehiclePosition for the given route\r\n+            for (int k = 0; k < vehicleArray.length(); k++) {\r\n+\r\n+\r\n+                JSONObject vehicleObj = vehicleArray.getJSONObject(k);\r\n+                // initiate feed\r\n+                TripDescriptor.Builder tripDescriptor = TripDescriptor.newBuilder();\r\n+                Position.Builder position = Position.newBuilder();\r\n+                VehicleDescriptor.Builder VehicleInfo = VehicleDescriptor.newBuilder();\r\n+                FeedEntity.Builder vehiclePositionEntity = FeedEntity.newBuilder();\r\n+\r\n+                // set values for feed\r\n+                tripDescriptor.setRouteId(route_id);\r\n+                tripDescriptor.setTripId(findTripID(route_id));\r\n+                position.setBearing((float) Math.round(100*vehicleObj.getDouble(\"headingDegrees\"))/100);\r\n+                position.setLatitude((float) vehicleObj.getDouble(\"lat\"));\r\n+                position.setLongitude((float) vehicleObj.getDouble(\"lon\"));\r\n+                position.setSpeed((float) Math.round(100*vehicleObj.getDouble(\"speed\"))/100);\r\n+                VehicleInfo.setId(vehicleObj.getString(\"name\"));\r\n+                VehicleInfo.setLabel(vehicleObj.getString(\"name\"));\r\n+                tripDescriptor.setScheduleRelationship(TripDescriptor.ScheduleRelationship.UNSCHEDULED);\r\n+\r\n+                // Build feed\r\n+                VehiclePosition.Builder VehiclePosition_route = VehiclePosition.newBuilder();\r\n+                VehiclePosition_route.setPosition(position);\r\n+                VehiclePosition_route.setTrip(tripDescriptor);\r\n+                VehiclePosition_route.setVehicle(VehicleInfo);\r\n+                VehiclePosition_route.setTimestamp(\r\n+                        Instant.parse(vehicleObj.getString(\"lastUpdated\")).getEpochSecond()\r\n+                );\r\n+                if (vehicleObj.getDouble(\"passengerLoad\") <= 0)\r\n+                    VehiclePosition_route.setOccupancyStatus(OccupancyStatus.EMPTY);\r\n+                else if (vehicleObj.getDouble(\"passengerLoad\") <= 50)\r\n+                    VehiclePosition_route.setOccupancyStatus(OccupancyStatus.MANY_SEATS_AVAILABLE);\r\n+                else if (vehicleObj.getDouble(\"passengerLoad\") <= 70)\r\n+                    VehiclePosition_route.setOccupancyStatus(OccupancyStatus.FEW_SEATS_AVAILABLE);\r\n+                else if (vehicleObj.getDouble(\"passengerLoad\") <= 90)\r\n+                    VehiclePosition_route.setOccupancyStatus(OccupancyStatus.STANDING_ROOM_ONLY);\r\n+                else if (vehicleObj.getDouble(\"passengerLoad\") <= 95)\r\n+                    VehiclePosition_route.setOccupancyStatus(OccupancyStatus.CRUSHED_STANDING_ROOM_ONLY);\r\n+                else VehiclePosition_route.setOccupancyStatus(OccupancyStatus.FULL);\r\n+\r\n+                vehicleFeedID++;\r\n+                vehiclePositionEntity.setId(Integer.toString(vehicleFeedID));\r\n+                vehiclePositionEntity.setVehicle(VehiclePosition_route);\r\n+                vehiclePositions.addEntity(vehiclePositionEntity.build());\r\n+            }\r\n+        }\r\n+        _vehiclePositionsSink.handleFullUpdate(vehiclePositions);\r\n+        _log.info(\"vehicles' location extracted: \" + vehiclePositions.getEntities().size());\r\n+        // ---- END UPDATING VEHICLE POSITIONS -----------------\r\n+        /* ---- TRIP UPDATES WAS REMOVED FROM VERSION 1.0-------------------\r\n+        * We have experienced inconsistencies in the generated trip_updates,\r\n+        * (see issue #8 (https://github.com/CUTR-at-USF/bullrunner-gtfs-realtime-generator/issues/8)\r\n+        * So we decided to remove trip_updates until we find a better solution.\r\n+        * */\r\n+    }\r\n+\r\n+\r\n+    /**\r\n+     * Method to hit the API and Get Vehicle locations for a given external route_id (Syncromatics route id)\r\n+     * @param external_route_id\r\n+     * @return JSONArray of vehicle locations\r\n+     * @throws IOException\r\n+     * @throws JSONException\r\n+     */\r\n+    private JSONArray downloadVehicleDetails(String external_route_id) throws IOException, JSONException {\r\n+        URLConnection connection = null;\r\n+        URL URL_Request = new URL(_url + \"routes/\" + external_route_id + \"/vehicles?api-key=\" + _api_key);\r\n+        try {\r\n+            connection = URL_Request.openConnection();\r\n+        } catch (Exception ex) {\r\n+            _log.error(\"Error in opening feeds url\", ex);\r\n+        }\r\n+        connection.setConnectTimeout(_Conn_Timeout_MS);\r\n+        connection.setReadTimeout(_Conn_Timeout_MS);\r\n+        InputStream in = connection.getInputStream();\r\n+\r\n+        BufferedReader reader = new BufferedReader(new InputStreamReader(in, \"UTF-8\"));\r\n+\r\n+        StringBuilder builder = new StringBuilder();\r\n+        String inputLine;\r\n+        JSONArray response;\r\n+        try {\r\n+            while ((inputLine = reader.readLine()) != null)\r\n+                builder.append(inputLine).append(\"\\n\");\r\n+            response = new JSONArray(builder.toString());\r\n+        } catch (SocketTimeoutException ex) {\r\n+            _log.error(\"Error readline, server doesn't close the connection.\", ex);\r\n+            response = null;\r\n+        }\r\n+\r\n+        return response;\r\n+    }\r\n+\r\n+    /**\r\n+     * Method to get Vehicle locations for MSC Express (has the same route_id with C)\r\n+     *     route id must be C or MSC Express\r\n+     * @param route_id\r\n+     * @param external_route_id\r\n+     * @return JSONArray of vehicle locations for given route\r\n+     * @throws IOException\r\n+     * @throws JSONException\r\n+     */\r\n+    private JSONArray downloadVehicleDetails_C(String route_id, String external_route_id) throws IOException, JSONException {\r\n+        final String Pattern_name_C = \"Route C\";\r\n+        final String Pattern_name_MSC = \"MSC Express\";\r\n+        final String Route_id_C = \"C\";\r\n+        final String Route_id_MSC = \"MSC Express\";\r\n+\r\n+        if ((!route_id.equals(Route_id_C)) & (!route_id.equals(Route_id_MSC))) {\r\n+            throw new Error(\"Method downloadVehicleDetails_C only applies to route C or MSC Express\");\r\n+        }\r\n+        URLConnection connection = null;\r\n+        URL URL_Request = new URL(_url + \"routes/\" + external_route_id + \"/vehicles?api-key=\" + _api_key);\r\n+        try {\r\n+            connection = URL_Request.openConnection();\r\n+        } catch (Exception ex) {\r\n+            _log.error(\"Error in opening feeds url\", ex);\r\n+        }\r\n+        connection.setConnectTimeout(_Conn_Timeout_MS);\r\n+        connection.setReadTimeout(_Conn_Timeout_MS);\r\n+        InputStream in = connection.getInputStream();\r\n+\r\n+        BufferedReader reader = new BufferedReader(new InputStreamReader(in, \"UTF-8\"));\r\n+\r\n+        StringBuilder builder = new StringBuilder();\r\n+        String inputLine;\r\n+        JSONArray response;\r\n+        try {\r\n+            while ((inputLine = reader.readLine()) != null)\r\n+                builder.append(inputLine).append(\"\\n\");\r\n+            response = new JSONArray(builder.toString());\r\n+        } catch (SocketTimeoutException ex) {\r\n+            _log.error(\"Error readline, server doesn't close the connection.\", ex);\r\n+            response = null;\r\n+        }\r\n+\r\n+        if (response.length() == 0)\r\n+            return null;\r\n+\r\n+        // keep only vehicles of the given route_id\r\n+        JSONArray response_out = new JSONArray();\r\n+        for (int i = 0; i < response.length(); i++){\r\n+            JSONObject vehicleObj = response.getJSONObject(i);\r\n+            String vehicle_id = vehicleObj.getString(\"id\");\r\n+            // check pattern name\r\n+            URL URL_pattern = new URL(_url + \"v1/vehicles/\" + vehicle_id + \"/arrivals?count=100&api-key=\" + _api_key);\r\n+            try {\r\n+                connection = URL_pattern.openConnection();\r\n+            } catch (Exception ex) {\r\n+                _log.error(\"Error in opening feeds url\", ex);\r\n+            }\r\n+            connection.setConnectTimeout(_Conn_Timeout_MS);\r\n+            connection.setReadTimeout(_Conn_Timeout_MS);\r\n+            in = connection.getInputStream();\r\n+\r\n+            BufferedReader reader1 = new BufferedReader(new InputStreamReader(in, \"UTF-8\"));\r\n+\r\n+            StringBuilder builder1 = new StringBuilder();\r\n+            String inputLine1;\r\n+            JSONArray response_pattern;\r\n+            try {\r\n+                while ((inputLine1 = reader1.readLine()) != null)\r\n+                    builder1.append(inputLine1).append(\"\\n\");\r\n+                response_pattern = new JSONArray(builder1.toString());\r\n+                String pattern_name = response_pattern.getJSONObject(0).getJSONObject(\"pattern\").getString(\"name\");\r\n+            } catch (SocketTimeoutException ex) {\r\n+                _log.error(\"Error readline, server doesn't close the connection.\", ex);\r\n+                response_pattern = null;\r\n+                return null;\r\n+            }\r\n+            String pattern_name = response_pattern.getJSONObject(0).getJSONObject(\"pattern\").getString(\"name\");\r\n+\r\n+            // check if pattern name matches\r\n+            if (pattern_name.equals(Pattern_name_C)){\r\n+                if (route_id.equals(Route_id_C))\r\n+                    response_out.put(vehicleObj);\r\n+            } else if (pattern_name.equals(Pattern_name_MSC)){\r\n+                if (route_id.equals(Route_id_MSC))\r","path":"src/main/java/edu/usf/cutr/gtfs_realtime/bullrunner/GtfsRealtimeProviderImpl.java","position":null,"original_position":1073,"commit_id":"dd2efd47b8ab7ad970fa77ef212973a9ab792016","original_commit_id":"94f77948356065ae93088c10aa1f79b54ec9cbe2","user":{"login":"minhhpham","id":23173735,"node_id":"MDQ6VXNlcjIzMTczNzM1","avatar_url":"https://avatars2.githubusercontent.com/u/23173735?v=4","gravatar_id":"","url":"https://api.github.com/users/minhhpham","html_url":"https://github.com/minhhpham","followers_url":"https://api.github.com/users/minhhpham/followers","following_url":"https://api.github.com/users/minhhpham/following{/other_user}","gists_url":"https://api.github.com/users/minhhpham/gists{/gist_id}","starred_url":"https://api.github.com/users/minhhpham/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/minhhpham/subscriptions","organizations_url":"https://api.github.com/users/minhhpham/orgs","repos_url":"https://api.github.com/users/minhhpham/repos","events_url":"https://api.github.com/users/minhhpham/events{/privacy}","received_events_url":"https://api.github.com/users/minhhpham/received_events","type":"User","site_admin":false},"body":"We need this to assign vehicles to the correct `route_id`. `refreshTripVehicle()` loops through `route_id` in GTFS files and request vehicle info from the API. Since the API returns both route C and MSC Express vehicle as a response to the request for route C, these 4 lines attempt to match `pattern_name` with `route_id`.","created_at":"2018-09-18T22:56:42Z","updated_at":"2018-09-18T22:56:42Z","html_url":"https://github.com/CUTR-at-USF/bullrunner-gtfs-realtime-generator/pull/26#discussion_r218620848","pull_request_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/pulls/26","author_association":"MEMBER","_links":{"self":{"href":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/pulls/comments/218620848"},"html":{"href":"https://github.com/CUTR-at-USF/bullrunner-gtfs-realtime-generator/pull/26#discussion_r218620848"},"pull_request":{"href":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/pulls/26"}},"in_reply_to_id":218459226},"pull_request":{"url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/pulls/26","id":216125656,"node_id":"MDExOlB1bGxSZXF1ZXN0MjE2MTI1NjU2","html_url":"https://github.com/CUTR-at-USF/bullrunner-gtfs-realtime-generator/pull/26","diff_url":"https://github.com/CUTR-at-USF/bullrunner-gtfs-realtime-generator/pull/26.diff","patch_url":"https://github.com/CUTR-at-USF/bullrunner-gtfs-realtime-generator/pull/26.patch","issue_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/issues/26","number":26,"state":"open","locked":false,"title":"Update Syncromatics API, add per-vehicle timestamp, remove TripUpdates","user":{"login":"barbeau","id":928045,"node_id":"MDQ6VXNlcjkyODA0NQ==","avatar_url":"https://avatars0.githubusercontent.com/u/928045?v=4","gravatar_id":"","url":"https://api.github.com/users/barbeau","html_url":"https://github.com/barbeau","followers_url":"https://api.github.com/users/barbeau/followers","following_url":"https://api.github.com/users/barbeau/following{/other_user}","gists_url":"https://api.github.com/users/barbeau/gists{/gist_id}","starred_url":"https://api.github.com/users/barbeau/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/barbeau/subscriptions","organizations_url":"https://api.github.com/users/barbeau/orgs","repos_url":"https://api.github.com/users/barbeau/repos","events_url":"https://api.github.com/users/barbeau/events{/privacy}","received_events_url":"https://api.github.com/users/barbeau/received_events","type":"User","site_admin":false},"body":"This is a continuation of https://github.com/CUTR-at-USF/bullrunner-gtfs-realtime-generator/pull/24, so @minhhpham and I can both push to the same branch.","created_at":"2018-09-17T21:50:32Z","updated_at":"2018-09-18T22:56:42Z","closed_at":null,"merged_at":null,"merge_commit_sha":"b078f2493846647b85484b629c2fd9be6cf36f20","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/pulls/26/commits","review_comments_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/pulls/26/comments","review_comment_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/pulls/comments{/number}","comments_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/issues/26/comments","statuses_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/statuses/dd2efd47b8ab7ad970fa77ef212973a9ab792016","head":{"label":"CUTR-at-USF:update-syncromatics-api","ref":"update-syncromatics-api","sha":"dd2efd47b8ab7ad970fa77ef212973a9ab792016","user":{"login":"CUTR-at-USF","id":1372220,"node_id":"MDEyOk9yZ2FuaXphdGlvbjEzNzIyMjA=","avatar_url":"https://avatars2.githubusercontent.com/u/1372220?v=4","gravatar_id":"","url":"https://api.github.com/users/CUTR-at-USF","html_url":"https://github.com/CUTR-at-USF","followers_url":"https://api.github.com/users/CUTR-at-USF/followers","following_url":"https://api.github.com/users/CUTR-at-USF/following{/other_user}","gists_url":"https://api.github.com/users/CUTR-at-USF/gists{/gist_id}","starred_url":"https://api.github.com/users/CUTR-at-USF/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/CUTR-at-USF/subscriptions","organizations_url":"https://api.github.com/users/CUTR-at-USF/orgs","repos_url":"https://api.github.com/users/CUTR-at-USF/repos","events_url":"https://api.github.com/users/CUTR-at-USF/events{/privacy}","received_events_url":"https://api.github.com/users/CUTR-at-USF/received_events","type":"Organization","site_admin":false},"repo":{"id":13509270,"node_id":"MDEwOlJlcG9zaXRvcnkxMzUwOTI3MA==","name":"bullrunner-gtfs-realtime-generator","full_name":"CUTR-at-USF/bullrunner-gtfs-realtime-generator","private":false,"owner":{"login":"CUTR-at-USF","id":1372220,"node_id":"MDEyOk9yZ2FuaXphdGlvbjEzNzIyMjA=","avatar_url":"https://avatars2.githubusercontent.com/u/1372220?v=4","gravatar_id":"","url":"https://api.github.com/users/CUTR-at-USF","html_url":"https://github.com/CUTR-at-USF","followers_url":"https://api.github.com/users/CUTR-at-USF/followers","following_url":"https://api.github.com/users/CUTR-at-USF/following{/other_user}","gists_url":"https://api.github.com/users/CUTR-at-USF/gists{/gist_id}","starred_url":"https://api.github.com/users/CUTR-at-USF/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/CUTR-at-USF/subscriptions","organizations_url":"https://api.github.com/users/CUTR-at-USF/orgs","repos_url":"https://api.github.com/users/CUTR-at-USF/repos","events_url":"https://api.github.com/users/CUTR-at-USF/events{/privacy}","received_events_url":"https://api.github.com/users/CUTR-at-USF/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/CUTR-at-USF/bullrunner-gtfs-realtime-generator","description":"Desktop application that retrieves AVL data from the USF Bull Runner's AVL system and produces Trip Updates and Vehicle Positions files in GTFS-realtime format.","fork":false,"url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator","forks_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/forks","keys_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/keys{/key_id}","collaborators_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/teams","hooks_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/hooks","issue_events_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/issues/events{/number}","events_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/events","assignees_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/assignees{/user}","branches_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/branches{/branch}","tags_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/tags","blobs_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/git/refs{/sha}","trees_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/git/trees{/sha}","statuses_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/statuses/{sha}","languages_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/languages","stargazers_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/stargazers","contributors_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/contributors","subscribers_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/subscribers","subscription_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/subscription","commits_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/commits{/sha}","git_commits_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/git/commits{/sha}","comments_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/comments{/number}","issue_comment_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/issues/comments{/number}","contents_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/contents/{+path}","compare_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/compare/{base}...{head}","merges_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/merges","archive_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/downloads","issues_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/issues{/number}","pulls_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/pulls{/number}","milestones_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/milestones{/number}","notifications_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/labels{/name}","releases_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/releases{/id}","deployments_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/deployments","created_at":"2013-10-11T20:43:39Z","updated_at":"2016-01-15T18:40:27Z","pushed_at":"2018-09-18T16:25:31Z","git_url":"git://github.com/CUTR-at-USF/bullrunner-gtfs-realtime-generator.git","ssh_url":"git@github.com:CUTR-at-USF/bullrunner-gtfs-realtime-generator.git","clone_url":"https://github.com/CUTR-at-USF/bullrunner-gtfs-realtime-generator.git","svn_url":"https://github.com/CUTR-at-USF/bullrunner-gtfs-realtime-generator","homepage":null,"size":305,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":3,"mirror_url":null,"archived":false,"open_issues_count":6,"license":{"key":"other","name":"Other","spdx_id":null,"url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":3,"open_issues":6,"watchers":0,"default_branch":"master"}},"base":{"label":"CUTR-at-USF:master","ref":"master","sha":"fa6f6be1cba79a8217569943e34f2f2dbb05b6fd","user":{"login":"CUTR-at-USF","id":1372220,"node_id":"MDEyOk9yZ2FuaXphdGlvbjEzNzIyMjA=","avatar_url":"https://avatars2.githubusercontent.com/u/1372220?v=4","gravatar_id":"","url":"https://api.github.com/users/CUTR-at-USF","html_url":"https://github.com/CUTR-at-USF","followers_url":"https://api.github.com/users/CUTR-at-USF/followers","following_url":"https://api.github.com/users/CUTR-at-USF/following{/other_user}","gists_url":"https://api.github.com/users/CUTR-at-USF/gists{/gist_id}","starred_url":"https://api.github.com/users/CUTR-at-USF/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/CUTR-at-USF/subscriptions","organizations_url":"https://api.github.com/users/CUTR-at-USF/orgs","repos_url":"https://api.github.com/users/CUTR-at-USF/repos","events_url":"https://api.github.com/users/CUTR-at-USF/events{/privacy}","received_events_url":"https://api.github.com/users/CUTR-at-USF/received_events","type":"Organization","site_admin":false},"repo":{"id":13509270,"node_id":"MDEwOlJlcG9zaXRvcnkxMzUwOTI3MA==","name":"bullrunner-gtfs-realtime-generator","full_name":"CUTR-at-USF/bullrunner-gtfs-realtime-generator","private":false,"owner":{"login":"CUTR-at-USF","id":1372220,"node_id":"MDEyOk9yZ2FuaXphdGlvbjEzNzIyMjA=","avatar_url":"https://avatars2.githubusercontent.com/u/1372220?v=4","gravatar_id":"","url":"https://api.github.com/users/CUTR-at-USF","html_url":"https://github.com/CUTR-at-USF","followers_url":"https://api.github.com/users/CUTR-at-USF/followers","following_url":"https://api.github.com/users/CUTR-at-USF/following{/other_user}","gists_url":"https://api.github.com/users/CUTR-at-USF/gists{/gist_id}","starred_url":"https://api.github.com/users/CUTR-at-USF/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/CUTR-at-USF/subscriptions","organizations_url":"https://api.github.com/users/CUTR-at-USF/orgs","repos_url":"https://api.github.com/users/CUTR-at-USF/repos","events_url":"https://api.github.com/users/CUTR-at-USF/events{/privacy}","received_events_url":"https://api.github.com/users/CUTR-at-USF/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/CUTR-at-USF/bullrunner-gtfs-realtime-generator","description":"Desktop application that retrieves AVL data from the USF Bull Runner's AVL system and produces Trip Updates and Vehicle Positions files in GTFS-realtime format.","fork":false,"url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator","forks_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/forks","keys_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/keys{/key_id}","collaborators_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/teams","hooks_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/hooks","issue_events_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/issues/events{/number}","events_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/events","assignees_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/assignees{/user}","branches_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/branches{/branch}","tags_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/tags","blobs_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/git/refs{/sha}","trees_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/git/trees{/sha}","statuses_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/statuses/{sha}","languages_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/languages","stargazers_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/stargazers","contributors_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/contributors","subscribers_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/subscribers","subscription_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/subscription","commits_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/commits{/sha}","git_commits_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/git/commits{/sha}","comments_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/comments{/number}","issue_comment_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/issues/comments{/number}","contents_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/contents/{+path}","compare_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/compare/{base}...{head}","merges_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/merges","archive_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/downloads","issues_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/issues{/number}","pulls_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/pulls{/number}","milestones_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/milestones{/number}","notifications_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/labels{/name}","releases_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/releases{/id}","deployments_url":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/deployments","created_at":"2013-10-11T20:43:39Z","updated_at":"2016-01-15T18:40:27Z","pushed_at":"2018-09-18T16:25:31Z","git_url":"git://github.com/CUTR-at-USF/bullrunner-gtfs-realtime-generator.git","ssh_url":"git@github.com:CUTR-at-USF/bullrunner-gtfs-realtime-generator.git","clone_url":"https://github.com/CUTR-at-USF/bullrunner-gtfs-realtime-generator.git","svn_url":"https://github.com/CUTR-at-USF/bullrunner-gtfs-realtime-generator","homepage":null,"size":305,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":3,"mirror_url":null,"archived":false,"open_issues_count":6,"license":{"key":"other","name":"Other","spdx_id":null,"url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":3,"open_issues":6,"watchers":0,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/pulls/26"},"html":{"href":"https://github.com/CUTR-at-USF/bullrunner-gtfs-realtime-generator/pull/26"},"issue":{"href":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/issues/26"},"comments":{"href":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/issues/26/comments"},"review_comments":{"href":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/pulls/26/comments"},"review_comment":{"href":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/pulls/26/commits"},"statuses":{"href":"https://api.github.com/repos/CUTR-at-USF/bullrunner-gtfs-realtime-generator/statuses/dd2efd47b8ab7ad970fa77ef212973a9ab792016"}},"author_association":"MEMBER"}}
{"actor":{"display_login":"minhhpham"}}