/**
* Copyright (C) 2011 Brian Ferris <bdferris@onebusaway.org>
*
* 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.onebusaway.api.actions.siri;
import org.onebusaway.api.actions.OneBusAwayApiActionSupport;
import org.onebusaway.siri.model.MonitoredVehicleJourney;
import org.onebusaway.siri.model.ServiceDelivery;
import org.onebusaway.siri.model.Siri;
import org.onebusaway.siri.model.VehicleActivity;
import org.onebusaway.siri.model.VehicleLocation;
import org.onebusaway.siri.model.VehicleMonitoringDelivery;
import org.onebusaway.transit_data.model.ListBean;
import org.onebusaway.transit_data.model.TripStopTimeBean;
import org.onebusaway.transit_data.model.VehicleStatusBean;
import org.onebusaway.transit_data.model.trips.TripBean;
import org.onebusaway.transit_data.model.trips.TripDetailsBean;
import org.onebusaway.transit_data.model.trips.TripDetailsQueryBean;
import org.onebusaway.transit_data.model.trips.TripStatusBean;
import org.onebusaway.transit_data.model.trips.TripsForRouteQueryBean;
import org.onebusaway.transit_data.services.TransitDataService;
import com.opensymphony.xwork2.ModelDriven;
import com.opensymphony.xwork2.conversion.annotations.TypeConversion;
import org.apache.struts2.interceptor.ServletRequestAware;
import org.apache.struts2.rest.DefaultHttpHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
/**
* For a given vehicle or set of vehicles, returns the location. Can select
* vehicles by id, trip, or route.
*/
public class VehicleMonitoringAction extends OneBusAwayApiActionSupport
implements ModelDriven<Object>, ServletRequestAware {
private static final long serialVersionUID = 1L;
private Object _response;
private HttpServletRequest _request;
@Autowired
private TransitDataService _transitDataService;
private Date _time;
@TypeConversion(converter = "org.onebusaway.api.actions.siri.Iso8601DateTimeConverter")
public void setTime(Date time) {
_time = time;
}
/**
* This is the default action, corresponding to a SIRI
* VehicleMonitoringRequest
*
* @return
* @throws IOException
*/
public DefaultHttpHeaders index() throws IOException {
String agencyId = _request.getParameter("OperatorRef");
if (_time == null)
_time = new Date();
String detailLevel = _request.getParameter("VehicleMonitoringDetailLevel");
boolean onwardCalls = false;
if (detailLevel != null) {
onwardCalls = detailLevel.equals("calls");
}
String vehicleId = _request.getParameter("VehicleRef");
// single trip, by vehicle
if (vehicleId != null) {
String vehicleIdWithAgency = agencyId + "_" + vehicleId;
VehicleStatusBean vehicle = _transitDataService.getVehicleForAgency(
vehicleIdWithAgency, _time.getTime());
ArrayList<VehicleActivity> activities = new ArrayList<VehicleActivity>();
if (vehicle != null) {
if (!(vehicle.getPhase().equals("DEADHEAD_AFTER")
|| vehicle.getPhase().equals("DEADHEAD_BEFORE") || vehicle.getPhase().equals(
"DEADHEAD_DURING"))) {
activities.add(createActivity(vehicle, onwardCalls));
}
}
_response = generateSiriResponse(_time, activities);
return new DefaultHttpHeaders();
}
String directionId = _request.getParameter("DirectionRef");
// by trip (may be more than one trip)
String tripId = _request.getParameter("VehicleJourneyRef");
if (tripId != null) {
TripBean tripBean = _transitDataService.getTrip(agencyId + "_" + tripId);
if (tripBean == null) {
throw new IllegalArgumentException("No such trip: " + tripId);
}
TripDetailsQueryBean query = new TripDetailsQueryBean();
query.setTripId(tripId);
ListBean<TripDetailsBean> trips = _transitDataService.getTripDetails(query);
ArrayList<VehicleActivity> activities = new ArrayList<VehicleActivity>();
for (TripDetailsBean trip : trips.getList()) {
if (directionId != null
&& !trip.getTrip().getDirectionId().equals(directionId)) {
continue;
}
if (trip.getStatus().isPredicted() == false) {
/* only show trips with realtime info */
continue;
}
VehicleActivity activity = createActivity(trip, onwardCalls);
if (activity != null) {
activities.add(activity);
}
}
_response = generateSiriResponse(_time, activities);
return new DefaultHttpHeaders();
}
String routeId = _request.getParameter("LineRef");
// multiple trips by route
if (routeId != null) {
TripsForRouteQueryBean query = new TripsForRouteQueryBean();
query.setRouteId(agencyId + "_" + routeId);
query.setTime(_time.getTime());
ListBean<TripDetailsBean> trips = _transitDataService.getTripsForRoute(query);
ArrayList<VehicleActivity> activities = new ArrayList<VehicleActivity>();
for (TripDetailsBean trip : trips.getList()) {
if (directionId != null
&& !trip.getTrip().getDirectionId().equals(directionId)) {
continue;
}
if (trip.getStatus().isPredicted() == false) {
/* only show trips with realtime info */
continue;
}
VehicleActivity activity = createActivity(trip, onwardCalls);
if (activity != null) {
activities.add(activity);
}
}
_response = generateSiriResponse(_time, activities);
return new DefaultHttpHeaders();
}
/* All vehicles */
ListBean<VehicleStatusBean> vehicles = _transitDataService.getAllVehiclesForAgency(
agencyId, _time.getTime());
ArrayList<VehicleActivity> activities = new ArrayList<VehicleActivity>();
for (VehicleStatusBean v : vehicles.getList()) {
VehicleActivity activity = createActivity(v, onwardCalls);
if (activity != null) {
activities.add(activity);
}
}
_response = generateSiriResponse(_time, activities);
return new DefaultHttpHeaders();
}
private VehicleActivity createActivity(VehicleStatusBean vehicleStatus,
boolean onwardCalls) {
if (vehicleStatus.getPhase().equals("DEADHEAD_AFTER")
|| vehicleStatus.getPhase().equals("DEADHEAD_BEFORE")
|| vehicleStatus.getPhase().equals("DEADHEAD_DURING")) {
return null;
}
VehicleActivity activity = new VehicleActivity();
Calendar time = Calendar.getInstance();
time.setTime(new Date(vehicleStatus.getLastUpdateTime()));
activity.RecordedAtTime = time;
TripBean tripBean = vehicleStatus.getTrip();
if (tripBean != null) {
TripDetailsQueryBean query = new TripDetailsQueryBean();
query.setTime(time.getTimeInMillis());
query.setTripId(tripBean.getId());
query.setVehicleId(vehicleStatus.getVehicleId());
query.getInclusion().setIncludeTripStatus(true);
TripStatusBean tripStatus = vehicleStatus.getTripStatus();
query.setServiceDate(tripStatus.getServiceDate());
TripDetailsBean tripDetails = _transitDataService.getSingleTripDetails(query);
activity.MonitoredVehicleJourney = SiriUtils.getMonitoredVehicleJourney(
tripDetails, new Date(tripStatus.getServiceDate()),
vehicleStatus.getVehicleId());
if (onwardCalls) {
List<TripStopTimeBean> stopTimes = tripDetails.getSchedule().getStopTimes();
long serviceDateMillis = tripStatus.getServiceDate();
double distance = tripStatus.getDistanceAlongTrip();
if (Double.isNaN(distance)) {
distance = tripStatus.getScheduledDistanceAlongTrip();
}
activity.MonitoredVehicleJourney.OnwardCalls = SiriUtils.getOnwardCalls(
stopTimes, serviceDateMillis, distance, tripStatus.getNextStop());
}
} else {
activity.MonitoredVehicleJourney = new MonitoredVehicleJourney();
}
activity.MonitoredVehicleJourney.Monitored = true;
activity.MonitoredVehicleJourney.VehicleRef = vehicleStatus.getVehicleId();
activity.MonitoredVehicleJourney.ProgressRate = SiriUtils.getProgressRateForStatus(vehicleStatus.getStatus());
VehicleLocation location = new VehicleLocation();
location.Latitude = vehicleStatus.getLocation().getLat();
location.Longitude = vehicleStatus.getLocation().getLon();
activity.MonitoredVehicleJourney.VehicleLocation = location;
return activity;
}
/** Generate a siri response for a set of VehicleActivities */
private Siri generateSiriResponse(Date time,
ArrayList<VehicleActivity> activities) {
Siri siri = new Siri();
siri.ServiceDelivery = new ServiceDelivery();
GregorianCalendar calendar = new GregorianCalendar();
calendar.setTime(time);
siri.ServiceDelivery.ResponseTimestamp = calendar;
siri.ServiceDelivery.VehicleMonitoringDelivery = new VehicleMonitoringDelivery();
siri.ServiceDelivery.VehicleMonitoringDelivery.ResponseTimestamp = siri.ServiceDelivery.ResponseTimestamp;
siri.ServiceDelivery.VehicleMonitoringDelivery.ValidUntil = (Calendar) calendar.clone();
siri.ServiceDelivery.VehicleMonitoringDelivery.ValidUntil.add(
Calendar.MINUTE, 1);
siri.ServiceDelivery.VehicleMonitoringDelivery.deliveries = activities;
return siri;
}
/**
* Create a VehicleActivity for a given vehicle's trip.
*/
private VehicleActivity createActivity(TripDetailsBean trip,
boolean onwardCalls) {
VehicleActivity activity = new VehicleActivity();
TripStatusBean status = trip.getStatus();
if (status.getPhase().equals("DEADHEAD_AFTER")
|| status.getPhase().equals("DEADHEAD_BEFORE")
|| status.getPhase().equals("DEADHEAD_DURING")) {
return null;
}
Calendar time = Calendar.getInstance();
time.setTime(new Date(status.getLastUpdateTime()));
activity.RecordedAtTime = time;
activity.MonitoredVehicleJourney = SiriUtils.getMonitoredVehicleJourney(
trip, new Date(status.getServiceDate()), status.getVehicleId());
activity.MonitoredVehicleJourney.Monitored = true;
activity.MonitoredVehicleJourney.VehicleRef = status.getVehicleId();
activity.MonitoredVehicleJourney.ProgressRate = status.getStatus();
VehicleLocation location = new VehicleLocation();
location.Latitude = status.getLocation().getLat();
location.Longitude = status.getLocation().getLon();
activity.MonitoredVehicleJourney.VehicleLocation = location;
if (onwardCalls) {
List<TripStopTimeBean> stopTimes = trip.getSchedule().getStopTimes();
long serviceDateMillis = status.getServiceDate();
double distance = status.getDistanceAlongTrip();
if (Double.isNaN(distance)) {
distance = status.getScheduledDistanceAlongTrip();
}
activity.MonitoredVehicleJourney.OnwardCalls = SiriUtils.getOnwardCalls(
stopTimes, serviceDateMillis, distance, status.getNextStop());
}
return activity;
}
@Override
public Object getModel() {
return _response;
}
@Override
public void setServletRequest(HttpServletRequest request) {
this._request = request;
}
public void setService(TransitDataService service) {
this._transitDataService = service;
}
public TransitDataService getService() {
return _transitDataService;
}
}