1
0
Fork 0
mirror of https://github.com/graphhopper/jsprit.git synced 2020-01-24 07:45:05 +01:00

modify io to read and write shipments, add tests to test reader/writer

This commit is contained in:
oblonski 2013-10-30 08:43:01 +01:00
parent d06f6ddc4b
commit 3740225058
14 changed files with 514 additions and 62 deletions

View file

@ -228,11 +228,14 @@ public class VehicleRoutingProblem {
/**
* Adds a job which is either a service or a shipment.
*
* <p>Note that job.getId() must be unique, i.e. no job (either it is a shipment or a service) is allowed to have an already allocated id.
*
* @param job
* @return
* @throws IllegalStateException if job is neither a shipment or a service.
* @throws IllegalStateException if job is neither a shipment or a service, or jobId has already been added.
*/
public Builder addJob(Job job) {
if(jobs.containsKey(job.getId())) throw new IllegalStateException("jobList already contains a job with id " + job.getId() + ". make sure you use unique ids for your jobs (i.e. service and shipments)");
if(job instanceof Service) {
addService((Service) job);
}

View file

@ -42,18 +42,17 @@ import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblem.FleetComposition;
import basics.VehicleRoutingProblem.FleetSize;
import basics.VehicleRoutingProblemSolution;
import basics.route.DefaultTourActivityFactory;
import basics.route.Driver;
import basics.route.DriverImpl;
import basics.route.End;
import basics.route.Start;
import basics.route.TimeWindow;
import basics.route.TourActivity;
import basics.route.TourActivityFactory;
import basics.route.Vehicle;
import basics.route.VehicleImpl;
import basics.route.VehicleImpl.Builder;
import basics.route.VehicleRoute;
import basics.route.VehicleRouteBuilder;
import basics.route.VehicleTypeImpl;
public class VrpXMLReader{
@ -125,21 +124,21 @@ public class VrpXMLReader{
private Map<String, Service> serviceMap;
private Map<String, Shipment> shipmentMap;
private boolean schemaValidation = true;
private Collection<VehicleRoutingProblemSolution> solutions;
private ServiceBuilderFactory serviceBuilderFactory = new DefaultServiceBuilderFactory();
private TourActivityFactory tourActivityFactory = new DefaultTourActivityFactory();
private Collection<JobConfigReader> jobConfigReaders = new ArrayList<VrpXMLReader.JobConfigReader>();
public void addJobConfigReader(JobConfigReader reader){
jobConfigReaders.add(reader);
}
public void setTourActivityFactory(TourActivityFactory tourActivityFactory){
this.tourActivityFactory = tourActivityFactory;
}
public void setServiceBuilderFactory(ServiceBuilderFactory serviceBuilderFactory){
@ -157,6 +156,7 @@ public class VrpXMLReader{
this.vrpBuilder = vrpBuilder;
this.vehicleMap = new HashMap<String, Vehicle>();
this.serviceMap = new HashMap<String, Service>();
this.shipmentMap = new HashMap<String, Shipment>();
this.solutions = solutions;
}
@ -164,6 +164,7 @@ public class VrpXMLReader{
this.vrpBuilder = vrpBuilder;
this.vehicleMap = new HashMap<String, Vehicle>();
this.serviceMap = new HashMap<String, Service>();
this.shipmentMap = new HashMap<String, Shipment>();
this.solutions = null;
}
@ -207,6 +208,7 @@ public class VrpXMLReader{
readShipments(xmlConfig);
readServices(xmlConfig);
readSolutions(xmlConfig);
}
@ -234,28 +236,36 @@ public class VrpXMLReader{
startAct.setEndTime(Double.parseDouble(start));
End endAct = End.newInstance(vehicle.getLocationId(), vehicle.getEarliestDeparture(), vehicle.getLatestArrival());
endAct.setArrTime(Double.parseDouble(end));
VehicleRoute.Builder routeBuilder = VehicleRoute.Builder.newInstance(startAct, endAct);
routeBuilder.setDriver(driver);
routeBuilder.setVehicle(vehicle);
VehicleRouteBuilder routeBuilder = new VehicleRouteBuilder(vehicle, driver);
List<HierarchicalConfiguration> actConfigs = routeConfig.configurationsAt("act");
for(HierarchicalConfiguration actConfig : actConfigs){
String type = actConfig.getString("[@type]");
if(type == null) throw new IllegalStateException("act[@type] is missing.");
String arrTimeS = actConfig.getString("arrTime");
if(arrTimeS == null) throw new IllegalStateException("act.arrTime is missing.");
String endTimeS = actConfig.getString("endTime");
if(endTimeS == null) throw new IllegalStateException("act.endTime is missing.");
double arrTime = Double.parseDouble(arrTimeS);
double endTime = Double.parseDouble(endTimeS);
String serviceId = actConfig.getString("serviceId");
if(serviceId != null) {
Service service = getService(serviceId);
String arrTime = actConfig.getString("arrTime");
if(arrTime == null) throw new IllegalStateException("act.arrTime is missing.");
String endTime = actConfig.getString("endTime");
if(endTime == null) throw new IllegalStateException("act.endTime is missing.");
TourActivity tourActivity = tourActivityFactory.createActivity(service);
tourActivity.setArrTime(Double.parseDouble(arrTime));
tourActivity.setEndTime(Double.parseDouble(endTime));
routeBuilder.addActivity(tourActivity);
routeBuilder.addService(service, arrTime, endTime);
}
else{
String shipmentId = actConfig.getString("shipmentId");
if(shipmentId == null) throw new IllegalStateException("either serviceId or shipmentId is missing");
Shipment shipment = getShipment(shipmentId);
if(shipment == null) throw new IllegalStateException("shipment with id " + shipmentId + " does not exist.");
if(type.equals("pickupShipment")){
routeBuilder.addPickup(shipment, arrTime, endTime);
}
else if(type.equals("deliverShipment")){
routeBuilder.addDelivery(shipment, arrTime, endTime);
}
else throw new IllegalStateException("type " + type + " is not supported. Use 'pickupShipment' or 'deliverShipment' here");
}
}
@ -266,6 +276,9 @@ public class VrpXMLReader{
}
}
private Shipment getShipment(String shipmentId) {
return shipmentMap.get(shipmentId);
}
private Service getService(String serviceId) {
return serviceMap.get(serviceId);
}
@ -328,6 +341,9 @@ public class VrpXMLReader{
builder.setPickupLocation(deliveryCoord.toString());
}
}
Shipment shipment = builder.build();
vrpBuilder.addJob(shipment);
shipmentMap .put(shipment.getId(),shipment);
}
}

View file

@ -31,6 +31,7 @@ import org.w3c.dom.Element;
import basics.Job;
import basics.Service;
import basics.Shipment;
import basics.VehicleRoutingProblem;
import basics.VehicleRoutingProblemSolution;
import basics.route.TourActivity;
@ -79,7 +80,8 @@ public class VrpXMLWriter {
writeProblemType(xmlConfig);
writeVehiclesAndTheirTypes(xmlConfig);
writerServices(xmlConfig);
writeServices(xmlConfig);
writeShipments(xmlConfig);
writeSolutions(xmlConfig);
OutputFormat format = new OutputFormat();
@ -129,7 +131,16 @@ public class VrpXMLWriter {
for(TourActivity act : route.getTourActivities().getActivities()){
xmlConfig.setProperty(solutionPath + "(" + counter + ").routes.route(" + routeCounter + ").act("+actCounter+")[@type]", act.getName());
if(act instanceof JobActivity){
xmlConfig.setProperty(solutionPath + "(" + counter + ").routes.route(" + routeCounter + ").act("+actCounter+").serviceId", ((JobActivity) act).getJob().getId());
Job job = ((JobActivity) act).getJob();
if(job instanceof Service){
xmlConfig.setProperty(solutionPath + "(" + counter + ").routes.route(" + routeCounter + ").act("+actCounter+").serviceId", job.getId());
}
else if(job instanceof Shipment){
xmlConfig.setProperty(solutionPath + "(" + counter + ").routes.route(" + routeCounter + ").act("+actCounter+").shipmentId", job.getId());
}
else{
throw new IllegalStateException("cannot write solution correctly since job-type is not know. make sure you use either service or shipment, or another writer");
}
}
xmlConfig.setProperty(solutionPath + "(" + counter + ").routes.route(" + routeCounter + ").act("+actCounter+").arrTime", act.getArrTime());
xmlConfig.setProperty(solutionPath + "(" + counter + ").routes.route(" + routeCounter + ").act("+actCounter+").endTime", act.getEndTime());
@ -142,10 +153,11 @@ public class VrpXMLWriter {
}
}
private void writerServices(XMLConf xmlConfig) {
private void writeServices(XMLConf xmlConfig) {
String shipmentPathString = "services.service";
int counter = 0;
for(Job j : vrp.getJobs().values()){
if(!(j instanceof Service)) continue;
Service service = (Service) j;
xmlConfig.setProperty(shipmentPathString + "("+counter+")[@id]", service.getId());
xmlConfig.setProperty(shipmentPathString + "("+counter+")[@type]", service.getType());
@ -161,8 +173,41 @@ public class VrpXMLWriter {
counter++;
}
}
private void writeShipments(XMLConf xmlConfig) {
String shipmentPathString = "shipments.shipment";
int counter = 0;
for(Job j : vrp.getJobs().values()){
if(!(j instanceof Shipment)) continue;
Shipment shipment = (Shipment) j;
xmlConfig.setProperty(shipmentPathString + "("+counter+")[@id]", shipment.getId());
// xmlConfig.setProperty(shipmentPathString + "("+counter+")[@type]", service.getType());
if(shipment.getPickupLocation() != null) xmlConfig.setProperty(shipmentPathString + "("+counter+").pickup.locationId", shipment.getPickupLocation());
if(shipment.getPickupCoord() != null) {
xmlConfig.setProperty(shipmentPathString + "("+counter+").pickup.coord[@x]", shipment.getPickupCoord().getX());
xmlConfig.setProperty(shipmentPathString + "("+counter+").pickup.coord[@y]", shipment.getPickupCoord().getY());
}
xmlConfig.setProperty(shipmentPathString + "("+counter+").pickup.duration", shipment.getPickupServiceTime());
xmlConfig.setProperty(shipmentPathString + "("+counter+").pickup.timeWindows.timeWindow(0).start", shipment.getPickupTimeWindow().getStart());
xmlConfig.setProperty(shipmentPathString + "("+counter+").pickup.timeWindows.timeWindow(0).end", shipment.getPickupTimeWindow().getEnd());
if(shipment.getDeliveryLocation() != null) xmlConfig.setProperty(shipmentPathString + "("+counter+").delivery.locationId", shipment.getDeliveryLocation());
if(shipment.getDeliveryCoord() != null) {
xmlConfig.setProperty(shipmentPathString + "("+counter+").delivery.coord[@x]", shipment.getDeliveryCoord().getX());
xmlConfig.setProperty(shipmentPathString + "("+counter+").delivery.coord[@y]", shipment.getDeliveryCoord().getY());
}
xmlConfig.setProperty(shipmentPathString + "("+counter+").delivery.duration", shipment.getDeliveryServiceTime());
xmlConfig.setProperty(shipmentPathString + "("+counter+").delivery.timeWindows.timeWindow(0).start", shipment.getDeliveryTimeWindow().getStart());
xmlConfig.setProperty(shipmentPathString + "("+counter+").delivery.timeWindows.timeWindow(0).end", shipment.getDeliveryTimeWindow().getEnd());
xmlConfig.setProperty(shipmentPathString + "("+counter+").capacity-demand", shipment.getCapacityDemand());
counter++;
}
}
private void writeProblemType(XMLConfiguration xmlConfig){

View file

@ -32,7 +32,7 @@ public final class DeliverShipment implements DeliveryActivity{
@Override
public String getName() {
return "DeliverShipment";
return "deliverShipment";
}
@Override

View file

@ -32,7 +32,7 @@ public final class PickupShipment implements PickupActivity{
@Override
public String getName() {
return "PickupShipment";
return "pickupShipment";
}
@Override

View file

@ -62,7 +62,15 @@ public class VehicleRouteBuilder {
}
public VehicleRouteBuilder addService(Service service){
tourActivities.addActivity(serviceActivityFactory.createActivity(service));
addService(service,0.0,0.0);
return this;
}
public VehicleRouteBuilder addService(Service service, double arrTime, double endTime){
TourActivity act = serviceActivityFactory.createActivity(service);
act.setArrTime(arrTime);
act.setEndTime(endTime);
tourActivities.addActivity(act);
return this;
}
@ -74,8 +82,23 @@ public class VehicleRouteBuilder {
* @return
*/
public VehicleRouteBuilder addPickup(Shipment shipment){
addPickup(shipment,0.0,0.0);
return this;
}
/**
* Adds a the pickup of the specified shipment at specified arrival and end-time.
*
* @param shipment
* @throws IllegalStateException if method has already been called with the specified shipment.
* @return
*/
public VehicleRouteBuilder addPickup(Shipment shipment, double arrTime, double endTime){
if(openShipments.contains(shipment)) throw new IllegalStateException("shipment has already been added. cannot add it twice.");
tourActivities.addActivity(shipmentActivityFactory.createPickup(shipment));
TourActivity act = shipmentActivityFactory.createPickup(shipment);
act.setArrTime(arrTime);
act.setEndTime(endTime);
tourActivities.addActivity(act);
openShipments.add(shipment);
return this;
}
@ -88,8 +111,23 @@ public class VehicleRouteBuilder {
* @return
*/
public VehicleRouteBuilder addDelivery(Shipment shipment){
addDelivery(shipment,0.0,0.0);
return this;
}
/**
* Adds a the delivery of the specified shipment at a specified arrival and endTime.
*
* @param shipment
* @throws IllegalStateException if specified shipment has not been picked up yet (i.e. method addPickup(shipment) has not been called yet).
* @return
*/
public VehicleRouteBuilder addDelivery(Shipment shipment, double arrTime, double endTime){
if(openShipments.contains(shipment)){
tourActivities.addActivity(shipmentActivityFactory.createDelivery(shipment));
TourActivity act = shipmentActivityFactory.createDelivery(shipment);
act.setArrTime(arrTime);
act.setEndTime(endTime);
tourActivities.addActivity(act);
openShipments.remove(shipment);
}
else{ throw new IllegalStateException("cannot deliver shipment. shipment " + shipment + " needs to be picked up first."); }

View file

@ -1,24 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (C) 2013 Stefan Schroeder
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Contributors:
Stefan Schroeder - initial API and implementation
-->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.w3schools.com"
xmlns="http://www.w3schools.com" elementFormDefault="qualified">
@ -99,16 +80,10 @@
</xs:complexType>
</xs:element>
</xs:all>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="services" minOccurs="0" maxOccurs="1">
@ -138,6 +113,53 @@
</xs:complexType>
</xs:element>
<xs:element name="shipments" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:sequence>
<xs:element name="shipment" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="pickup" minOccurs="1" maxOccurs="1">
<xs:complexType>
<xs:all>
<xs:element name="locationId" type="xs:string" minOccurs="1" maxOccurs="1"/>
<xs:element name="coord" type="coordType" minOccurs="0" maxOccurs="1"/>
<xs:element name="duration" type="xs:decimal" minOccurs="0" maxOccurs="1" default="0.0"/>
<xs:element name="timeWindows" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:sequence>
<xs:element name="timeWindow" type="timeWindowType" minOccurs="1" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:all>
</xs:complexType>
</xs:element>
<xs:element name="delivery" minOccurs="1" maxOccurs="1">
<xs:complexType>
<xs:all>
<xs:element name="locationId" type="xs:string" minOccurs="1" maxOccurs="1"/>
<xs:element name="coord" type="coordType" minOccurs="0" maxOccurs="1"/>
<xs:element name="duration" type="xs:decimal" minOccurs="0" maxOccurs="1" default="0.0"/>
<xs:element name="timeWindows" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:sequence>
<xs:element name="timeWindow" type="timeWindowType" minOccurs="1" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:all>
</xs:complexType>
</xs:element>
<xs:element name="capacity-demand" type="xs:integer" minOccurs="1" maxOccurs="1"/>
</xs:sequence>
<xs:attribute name="id" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="solutions" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:sequence>
@ -157,11 +179,11 @@
<xs:element name="start" type="xs:double" minOccurs="1" maxOccurs="1"/>
<xs:element name="act" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:all>
<xs:element name="serviceId" type="xs:string" minOccurs="1" maxOccurs="1"/>
<xs:element name="arrTime" type="xs:double" minOccurs="1" maxOccurs="1"/>
<xs:element name="endTime" type="xs:double" minOccurs="1" maxOccurs="1"/>
</xs:all>
<xs:choice>
<xs:group ref="serviceActGroup"/>
<xs:group ref="shipmentActGroup"/>
</xs:choice>
<xs:attribute name="type" type="xs:string" use="required"/>
</xs:complexType>
</xs:element>
@ -188,6 +210,22 @@
</xs:complexType>
</xs:element>
<xs:group name="serviceActGroup">
<xs:sequence>
<xs:element name="serviceId" type="xs:string" minOccurs="1" maxOccurs="1"/>
<xs:element name="arrTime" type="xs:double" minOccurs="1" maxOccurs="1"/>
<xs:element name="endTime" type="xs:double" minOccurs="1" maxOccurs="1"/>
</xs:sequence>
</xs:group>
<xs:group name="shipmentActGroup">
<xs:sequence>
<xs:element name="shipmentId" type="xs:string" minOccurs="1" maxOccurs="1"/>
<xs:element name="arrTime" type="xs:double" minOccurs="1" maxOccurs="1"/>
<xs:element name="endTime" type="xs:double" minOccurs="1" maxOccurs="1"/>
</xs:sequence>
</xs:group>
<xs:complexType name="timeWindowType">
<xs:sequence>
<xs:element name="start" type="xs:double"/>