001/* *********************************************************************** *
002 * project: org.matsim.*
003 * PlanToPlanStepBasedOnEvents.java
004 *                                                                         *
005 * *********************************************************************** *
006 *                                                                         *
007 * copyright       : (C) 2012 by the members listed in the COPYING,        *
008 *                   LICENSE and WARRANTY file.                            *
009 * email           : info at matsim dot org                                *
010 *                                                                         *
011 * *********************************************************************** *
012 *                                                                         *
013 *   This program is free software; you can redistribute it and/or modify  *
014 *   it under the terms of the GNU General Public License as published by  *
015 *   the Free Software Foundation; either version 2 of the License, or     *
016 *   (at your option) any later version.                                   *
017 *   See also COPYING, LICENSE and WARRANTY file                           *
018 *                                                                         *
019 * *********************************************************************** */
020
021package org.matsim.contrib.cadyts.car;
022
023import java.util.HashSet;
024import java.util.Set;
025
026import javax.inject.Inject;
027
028import org.apache.log4j.Logger;
029import org.matsim.api.core.v01.Id;
030import org.matsim.api.core.v01.Scenario;
031import org.matsim.api.core.v01.TransportMode;
032import org.matsim.api.core.v01.events.LinkLeaveEvent;
033import org.matsim.api.core.v01.events.VehicleLeavesTrafficEvent;
034import org.matsim.api.core.v01.events.VehicleEntersTrafficEvent;
035import org.matsim.api.core.v01.events.handler.LinkLeaveEventHandler;
036import org.matsim.api.core.v01.events.handler.VehicleLeavesTrafficEventHandler;
037import org.matsim.api.core.v01.network.Link;
038import org.matsim.api.core.v01.events.handler.VehicleEntersTrafficEventHandler;
039import org.matsim.api.core.v01.population.Person;
040import org.matsim.api.core.v01.population.Plan;
041import org.matsim.contrib.cadyts.general.CadytsConfigGroup;
042import org.matsim.contrib.cadyts.general.PlansTranslator;
043import org.matsim.core.config.ConfigUtils;
044import org.matsim.core.events.algorithms.Vehicle2DriverEventHandler;
045
046import cadyts.demand.PlanBuilder;
047
048public final class PlansTranslatorBasedOnEvents implements PlansTranslator<Link>, LinkLeaveEventHandler,
049VehicleEntersTrafficEventHandler, VehicleLeavesTrafficEventHandler {
050        // could be/remain public as long as constructor is package-private. kai, feb'20
051        // used from outside, e.g. vsp-playgrounds
052        
053        private static final Logger log = Logger.getLogger(PlansTranslatorBasedOnEvents.class);
054
055        private final Scenario scenario;
056
057        private Vehicle2DriverEventHandler delegate = new Vehicle2DriverEventHandler();
058        
059        private int iteration = -1;
060
061        // this is _only_ there for output:
062        Set<Plan> plansEverSeen = new HashSet<>();
063
064        private static final String STR_PLANSTEPFACTORY = "planStepFactory";
065        private static final String STR_ITERATION = "iteration";
066
067        private final Set<Id<Link>> calibratedLinks = new HashSet<>() ;
068
069        @Inject
070        PlansTranslatorBasedOnEvents(final Scenario scenario) {
071                this.scenario = scenario;
072                Set<String> abc = ConfigUtils.addOrGetModule(scenario.getConfig(), CadytsConfigGroup.GROUP_NAME, CadytsConfigGroup.class).getCalibratedLinks();
073                for ( String str : abc ) {
074                        this.calibratedLinks.add( Id.createLinkId(str) ) ;
075                }
076        }
077
078        private long plansFound = 0;
079        private long plansNotFound = 0;
080
081        @Override
082        public final cadyts.demand.Plan<Link> getCadytsPlan(final Plan plan) {
083                @SuppressWarnings("unchecked")
084                PlanBuilder<Link> planStepFactory = (PlanBuilder<Link>) plan.getCustomAttributes().get(STR_PLANSTEPFACTORY);
085                if (planStepFactory == null) {
086                        this.plansNotFound++;
087                        return null;
088                }
089                this.plansFound++;
090                final cadyts.demand.Plan<Link> planSteps = planStepFactory.getResult();
091                return planSteps;
092        }
093
094        @Override
095        public void reset(final int iteration) {
096                this.iteration = iteration;
097
098                log.warn("found " + this.plansFound + " out of " + (this.plansFound + this.plansNotFound) + " ("
099                                + (100. * this.plansFound / (this.plansFound + this.plansNotFound)) + "%)");
100                log.warn("(above values may both be at zero for a couple of iterations if multiple plans per agent all have no score)");
101                
102                delegate.reset(iteration);
103        }
104
105        @Override
106        public void handleEvent(VehicleEntersTrafficEvent event) {
107                if (event.getNetworkMode().equals(TransportMode.car))
108                        delegate.handleEvent(event);
109        }
110        
111        @Override
112        public void handleEvent(VehicleLeavesTrafficEvent event) {
113                delegate.handleEvent(event);
114        }
115        
116        @Override
117        public void handleEvent(LinkLeaveEvent event) {
118                
119                Id<Person> driverId = delegate.getDriverOfVehicle(event.getVehicleId());
120                
121                // if it is not a car, ignore the event
122                if (driverId == null) 
123                        return;
124                
125                // if only a subset of links is calibrated but the link is not contained, ignore the event
126                if (!calibratedLinks.contains(event.getLinkId())) 
127                        return;
128                
129                // get the "Person" behind the id:
130                Person person = this.scenario.getPopulation().getPersons().get(driverId);
131                
132                // return if the driver is not in the standard population:
133                if ( person==null ) {
134                        return ;
135                }
136                // (I think that this will be ok: they will be counted, as they will be in reality.  But we cannot influence them in the normal way because they are not part of normal replanning.  Cadyts
137                // should still work, since it operates on all the others. kai, based on https://matsim.atlassian.net/browse/MATSIM-648, nov'18
138                
139                // get the selected plan:
140                Plan selectedPlan = person.getSelectedPlan();
141                
142                // get the planStepFactory for the plan (or create one):
143                PlanBuilder<Link> tmpPlanStepFactory = getPlanStepFactoryForPlan(selectedPlan);
144                
145                if (tmpPlanStepFactory != null) {
146                                                
147                        Link link = this.scenario.getNetwork().getLinks().get(event.getLinkId());
148                                        
149                        // add the "turn" to the planStepfactory
150                        tmpPlanStepFactory.addTurn(link, (int) event.getTime());
151                }
152        }
153
154        // ###################################################################################
155        // only private functions below here (low level functionality)
156
157        private PlanBuilder<Link> getPlanStepFactoryForPlan(final Plan selectedPlan) {
158                PlanBuilder<Link> planStepFactory = null;
159
160                planStepFactory = (PlanBuilder<Link>) selectedPlan.getCustomAttributes().get(STR_PLANSTEPFACTORY);
161                Integer factoryIteration = (Integer) selectedPlan.getCustomAttributes().get(STR_ITERATION);
162                if (planStepFactory == null || factoryIteration == null || factoryIteration != this.iteration) {
163                        // attach the iteration number to the plan:
164                        selectedPlan.getCustomAttributes().put(STR_ITERATION, this.iteration);
165
166                        // construct a new PlanBulder and attach it to the plan:
167                        planStepFactory = new PlanBuilder<Link>();
168                        selectedPlan.getCustomAttributes().put(STR_PLANSTEPFACTORY, planStepFactory);
169
170                        // memorize the plan as being seen:
171                        this.plansEverSeen.add(selectedPlan);
172                }
173
174                return planStepFactory;
175        }
176
177}