Eclipse SUMO - Simulation of Urban MObility
Loading...
Searching...
No Matches
MSRailSignalControl.cpp
Go to the documentation of this file.
1/****************************************************************************/
2// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3// Copyright (C) 2001-2026 German Aerospace Center (DLR) and others.
4// This program and the accompanying materials are made available under the
5// terms of the Eclipse Public License 2.0 which is available at
6// https://www.eclipse.org/legal/epl-2.0/
7// This Source Code may also be made available under the following Secondary
8// Licenses when the conditions for such availability set forth in the Eclipse
9// Public License 2.0 are satisfied: GNU General Public License, version 2
10// or later which is available at
11// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13/****************************************************************************/
18// Centralized services for rail signal control (Singleton)
19/****************************************************************************/
20#include <config.h>
21
22#include <cassert>
23#include <utility>
24#include <vector>
25#include <bitset>
26#include <microsim/MSNet.h>
27#include <microsim/MSRoute.h>
28#include <microsim/MSEdge.h>
29#include <microsim/MSLane.h>
30#include "MSRailSignal.h"
32#include "MSDriveWay.h"
33#include "MSRailSignalControl.h"
34
35
36//#define DEBUG_BUILD_DEADLOCK_CHECK
37
38// ===========================================================================
39// static value definitions
40// ===========================================================================
44
45// ===========================================================================
46// method definitions
47// ===========================================================================
49 myMaxTrainLength(OptionsCont::getOptions().getFloat("railway.max-train-length"))
50{}
51
60
61void
63 delete myInstance;
64 myInstance = nullptr;
65}
66
67void
69 if (myInstance != nullptr) {
76 //myInstance->myActiveSignals.clear();
77 }
78}
79
80
83
84void
85MSRailSignalControl::vehicleStateChanged(const SUMOVehicle* const vehicle, MSNet::VehicleState to, const std::string& /*info*/) {
86 if (vehicle->isRail()) {
87 std::string dummyMsg;
88 if ((to == MSNet::VehicleState::BUILT && (!vehicle->getParameter().wasSet(VEHPARS_FORCE_REROUTE) || vehicle->hasValidRoute(dummyMsg)))
90 // @note we could delay initialization until the departure time
93 }
94 }
97 }
98 }
99}
100
101
102void
104 mySignals.push_back(signal);
105 for (const auto& links : signal->getLinks()) {
106 for (const MSLink* link : links) {
107 mySignalizedClasses |= (link->getPermissions() & myMBClasses);
108 }
109 }
110}
111
112
113void
115 //std::cout << time2string(SIMSTEP) << " addWaitRelation waits=" << waits->getID() << " foe=" << reason->getID() << "\n";
116 myWaitRelations[waits] = WaitRelation(rs, reason, constraint);
117}
118
119
120bool
122 std::set<const SUMOVehicle*> seen;
123 std::vector<WaitRelation> list;
124 const SUMOVehicle* cur = veh;
125 std::vector<MSRailSignalConstraint*> constraints;
126 std::vector<const SUMOVehicle*> constraintBlocked;
127 std::vector<const MSRailSignal*> constraintSignals;
128 //std::cout << time2string(SIMSTEP) << " haveDeadlock veh=" << veh->getID() << "\n";
129 while (seen.count(cur) == 0) {
130 auto it = myWaitRelations.find(cur);
131 if (it != myWaitRelations.end()) {
132 if (it->second.constraint != nullptr) {
133 constraints.push_back(it->second.constraint);
134 constraintBlocked.push_back(cur);
135 constraintSignals.push_back(it->second.railSignal);
136 }
137 seen.insert(cur);
138 list.push_back(it->second);
139 cur = it->second.foe;
140 } else {
141 return false;
142 }
143 }
144 if (cur == veh) {
145 const bool newDeadlock = myWrittenDeadlocks.count(seen) == 0;
146 myWrittenDeadlocks.insert(seen);
148 MSRailSignalConstraint* resolved = nullptr;
149 const SUMOVehicle* resolvedUnblocked = nullptr;
150 const MSRailSignal* resolvedSignal = nullptr;
151 if (!constraints.empty() && oc.getBool("time-to-teleport.remove-constraint")) {
152 resolved = constraints.front();
153 if (newDeadlock) {
154 std::vector<std::string> vehicles;
155 for (auto item : list) {
156 vehicles.push_back(item.foe->getID());
157 }
158 WRITE_WARNINGF("Deactivating constraint to resolve deadlock between vehicles % at time %.", toString(vehicles), time2string(SIMSTEP));
159 resolved->setActive(false);
160 resolvedUnblocked = constraintBlocked.front();
161 resolvedSignal = constraintSignals.front();
162 }
163 }
164
165 if (oc.isSet("deadlock-output")) {
166 if (newDeadlock) {
167 myWrittenDeadlocks.insert(seen);
168 std::vector<std::string> signals;
169 std::vector<std::string> vehicles;
170 std::vector<std::string> tripIDs;
171 for (auto item : list) {
172 signals.push_back(item.railSignal == nullptr ? "INSERTION" : item.railSignal->getID());
173 vehicles.push_back(item.foe->getID());
174 tripIDs.push_back(item.foe->getParameter().getParameter("tripId", item.foe->getID()));
175 }
176 // rotate signals so their are aligned with the vehicle waiting
177 // at that signal instead of the foe that is blocking the signal
178 std::rotate(signals.begin(), signals.begin() + 1, signals.end());
179 OutputDevice& od = OutputDevice::getDeviceByOption("deadlock-output");
180 if (constraints.empty()) {
182 } else {
183 od.openTag("constraintDeadlock");
184 }
186 od.writeAttr(SUMO_ATTR_SIGNALS, signals);
187 od.writeAttr("vehicles", vehicles);
188 if (!constraints.empty()) {
189 od.writeAttr("tripIds", tripIDs);
190 }
191 if (resolved != nullptr) {
192 od.openTag("resolvedConstraint");
193 od.writeAttr(SUMO_ATTR_ID, resolvedSignal->getID());
194 resolved->write(od, resolvedUnblocked->getParameter().getParameter("tripId", resolvedUnblocked->getID()));
195 od.closeTag();
196 }
197 for (auto item : list) {
198 int start = item.foe->getRoutePosition();
200 const std::vector<MSLane*>& further = dynamic_cast<const MSVehicle*>(item.foe)->getFurtherLanes();
201 start = MAX2(0, start - (int)(MSGlobals::gUsingInternalLanes && MSNet::getInstance()->hasInternalLinks() ? further.size() / 2 : further.size()));
202 }
203 const MSRoute& route = item.foe->getRoute();
204 ConstMSEdgeVector future(route.begin() + start, route.end());
207 od.closeTag();
208 }
209 od.closeTag();
210 }
211 }
212 return resolved == nullptr;
213 } else {
214 // it's a deadlock but does not involve veh
215 return false;
216 }
217}
218
219
220void
221MSRailSignalControl::loadDeadlockCheck(std::vector<const MSRailSignal*> signals) {
223 WRITE_WARNING("Deadlocks should be loaded before any vehicles");
224 }
225#ifdef DEBUG_BUILD_DEADLOCK_CHECK
226 std::cout << " loadedDeadlockCheck signals=" << toString(signals) << "\n";
227#endif
228 const int deadlockIndex = (int)myLoadedDeadlocks.size();
229 myLoadedDeadlocks.push_back(Deadlock());
230 myLoadedDeadlocks.back().signals = signals;
231 const int n = (int)signals.size();
232 for (int i = 0; i < n; i++) {
233 mySignalDeadlocks[signals[i]].push_back(std::make_pair(deadlockIndex, i));
234 }
235}
236
237
238void
240 Deadlock& dl = myLoadedDeadlocks.back();
241 const int signalIndex = (int)dl.edges.size();
242 const MSRailSignal* rs = dl.signals[signalIndex];
243 const MSJunction* j = rs->getJunction();
244 auto it = edges.begin();
245 while (it != edges.end() && (*it)->getToJunction() != j) {
246 it++;
247 }
248 if (it == edges.end()) {
249 throw ProcessError("Rail signal junction '" + j->getID() + "' not passed by loaded deadlock edges '" + toString(edges) + "'.");
250 }
251 const MSEdge* firstSignalEdge = *it;
252 // maybe there is an earlier signal, in this case we must look for driveways that start there
253 for (auto it2 = edges.begin(); it2 != it; it2++) {
254 if ((*it2)->getToJunction()->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
255 const MSLink* link = (*it2)->getLanes().front()->getLinkTo((*(it2 + 1))->getLanes().front());
256 if (link != nullptr && link->isTLSControlled()) {
257 firstSignalEdge = *it2;
258 break;
259 }
260 }
261 }
262 dl.forwardEnds.push_back(firstSignalEdge);
263 dl.edges.push_back(edges);
264#ifdef DEBUG_BUILD_DEADLOCK_CHECK
265 std::cout << " loadedDeadlockRoute signal=" << rs->getID() << " signalEdge=" << firstSignalEdge->getID() << " edges=" << toString(edges) << "\n";
266#endif
267}
268
269
270void
272 //std::cout << " addDrivewayFollower " << dw->getID() << " " << dw2->getID() << "\n";
273 myDriveWaySucc[dw].insert(dw2);
274 myDriveWayPred[dw2].insert(dw);
275}
276
277
278void
280 auto itDL = mySignalDeadlocks.find(rs);
281 if (itDL == mySignalDeadlocks.end()) {
282 return;
283 }
284#ifdef DEBUG_BUILD_DEADLOCK_CHECK
285 std::cout << " needsDeadlockChecks? rs=" << rs->getID() << " dw=" << dw->getID() << "\n";
286#endif
287 // the driveway dw ends at signal rs and could be part of a deadlock.
288 // check whether we can form a sequence of driveways that link it with all others (railsignals)
289 // findDeadlockFoes calls itself recursively until all circles have been found
290 for (const auto& pair : itDL->second) {
291 const Deadlock& dl = myLoadedDeadlocks[pair.first];
292 std::vector<const MSDriveWay*> deadlockFoes;
293 if (dl.edges.empty()) {
294 findDeadlockFoes(dw, dl, pair.second, deadlockFoes);
295 } else {
296 const MSEdge* forwardEnd = dl.forwardEnds[pair.second];
297 if (forwardEnd == &dw->getForward().back()->getEdge()) {
298 findDeadlockFoesFromRoutes(dw, dl, pair.second, deadlockFoes);
299 } else {
300 for (MSDriveWay* cand : MSDriveWay::getEndingDriveWays(forwardEnd)) {
301 findDeadlockFoesFromRoutes(cand, dl, pair.second, deadlockFoes);
302 }
303 }
304 }
305 }
306}
307
308
309void
310MSRailSignalControl::findDeadlockFoes(const MSDriveWay* dw, const Deadlock& dl, int signalIndex, std::vector<const MSDriveWay*> deadlockFoes) {
311 if (deadlockFoes.size() < dl.signals.size()) {
312 signalIndex = (signalIndex + 1) % dl.signals.size();
313 const MSRailSignal* other = dl.signals[signalIndex];
314 const MSJunction* otherJ = other->getJunction();
315#ifdef DEBUG_BUILD_DEADLOCK_CHECK
316 std::cout << " findDLfoes dw=" << dw->getID() << " foundFoes=" << toString(deadlockFoes) << " otherRS=" << other->getID() << "\n";
317#endif
318 deadlockFoes.push_back(dw);
319 for (const MSDriveWay* follower : myDriveWaySucc[dw]) {
320#ifdef DEBUG_BUILD_DEADLOCK_CHECK
321 std::cout << " follower=" << follower->getID() << " followerFoes=" << toString(follower->getFoes()) << "\n";
322#endif
323 for (MSDriveWay* foe : follower->getFoes()) {
324 if (foe->getForward().back()->getToJunction() == otherJ) {
325 findDeadlockFoes(foe, dl, signalIndex, deadlockFoes);
326 } else if (foe->isSubDriveWay() && foe->getParent()->getForward().back()->getToJunction() == otherJ && foe->getParentRemainingLength() < myMaxTrainLength) {
327 // subdriveway-parents where the remaining part is short and thus a long train might reach back onto the subdriveway
328 // @note: technically we do not need to check this in meso because train ends cannot reach into multiple segments
329 // but it wouldn't help to make meso "better" by ignoring this foe relationship
330 findDeadlockFoes(foe->getParent(), dl, signalIndex, deadlockFoes);
331 }
332 }
333 }
334 } else {
335#ifdef DEBUG_BUILD_DEADLOCK_CHECK
336 std::cout << " add deadlock check dlFoes=" << toString(deadlockFoes) << "\n";;
337#endif
338 for (const MSDriveWay* dldw : deadlockFoes) {
339 if (!dldw->isDepartDriveway()) {
340 const MSRailSignal* rs = dynamic_cast<const MSRailSignal*>(dldw->getOrigin()->getTLLogic());
341 if (std::find(dl.signals.begin(), dl.signals.end(), rs) != dl.signals.end()) {
342 // moving into dldw frees up another part of the deadlock
343 continue;
344 }
345 }
346 std::vector<const MSDriveWay*> df(deadlockFoes);
347 df.erase(std::find(df.begin(), df.end(), dldw));
348#ifdef DEBUG_BUILD_DEADLOCK_CHECK
349 std::cout << " dldw=" << dldw->getID() << " df=" << toString(df) << "\n";
350#endif
351 const_cast<MSDriveWay*>(dldw)->addDWDeadlock(df);
352 }
353 }
354}
355
356
357void
358MSRailSignalControl::findDeadlockFoesFromRoutes(const MSDriveWay* dw, const Deadlock& dl, int signalIndex, std::vector<const MSDriveWay*> deadlockFoes) {
359 // the loaded edges must match the end of the driveway route
360 const ConstMSEdgeVector& de = dl.edges[signalIndex];
361 const size_t n = de.size();
362#ifdef DEBUG_BUILD_DEADLOCK_CHECK
363 std::cout << " findDLfoesFromRoutes dw=" << dw->getID() << " foundFoes=" << toString(deadlockFoes)
364 << "\n dle=" << toString(de)
365 << "\n dwe=" << toString(ConstMSEdgeVector(dw->getRoute().end() - MIN2(n, dw->getRoute().size()), dw->getRoute().end()))
366 << "\n";
367#endif
368 if (dw->getRoute().size() >= n && std::equal(de.begin(), de.end(),
369 dw->getRoute().end() - n, dw->getRoute().end())) {
370 deadlockFoes.push_back(dw);
371 if (deadlockFoes.size() < dl.signals.size()) {
372 signalIndex = (signalIndex + 1) % dl.signals.size();
373 const MSEdge* forwardEnd = dl.forwardEnds[signalIndex];
374 for (MSDriveWay* cand : MSDriveWay::getEndingDriveWays(forwardEnd)) {
375 findDeadlockFoesFromRoutes(cand, dl, signalIndex, deadlockFoes);
376 }
377 } else {
378 findDeadlockFoes(dw, dl, signalIndex, deadlockFoes);
379 }
380 }
381}
382
383
384void
386 const MSRailSignal* rs = dynamic_cast<const MSRailSignal*>(link->getTLLogic());
387 assert(rs != nullptr);
388 myActiveSignals.insert(const_cast<MSRailSignal*>(rs));
389}
390
391
392void
395 // there are 4 states for a signal
396 // 1. approached and green
397 // 2. approached and red
398 // 3. not approached and trains could pass
399 // 4. not approached and trains coult not pass
400 //
401 // for understanding conflicts better in sumo-gui, we want to show (3) as green. This
402 // means we have to keep updating signals in state (4) until they change to (3)
403
404 //std::cout << SIMTIME << " activeSignals=" << myActiveSignals.size() << "\n";
405 for (auto it = myActiveSignals.begin(); it != myActiveSignals.end();) {
406 MSRailSignal* rs = *it;
407 const bool keepActive = rs->updateCurrentPhase();
408 // std::cout << SIMTIME << " update " << rs->getID() << " keepActive=" << keepActive << "\n";
409 if (rs->isActive()) {
411 }
412 if (!keepActive) {
413 it = myActiveSignals.erase(it);
414 } else {
415 it++;
416 }
417 }
418}
419
420
421/****************************************************************************/
long long int SUMOTime
Definition GUI.h:36
std::vector< const MSEdge * > ConstMSEdgeVector
Definition MSEdge.h:75
#define WRITE_WARNINGF(...)
Definition MsgHandler.h:287
#define WRITE_WARNING(msg)
Definition MsgHandler.h:286
std::string time2string(SUMOTime t, bool humanReadable)
convert SUMOTime to string (independently of global format setting)
Definition SUMOTime.cpp:107
#define SIMSTEP
Definition SUMOTime.h:69
const SVCPermissions SVC_UNSPECIFIED
permissions not specified
bool isRailwayOrShared(SVCPermissions permissions)
Returns whether an edge with the given permissions is a railway edge or a shared road/rail edge.
long long int SVCPermissions
bitset where each bit declares whether a certain SVC may use this edge/lane
const long long int VEHPARS_FORCE_REROUTE
@ SUMO_TAG_ROUTEPART
@ SUMO_TAG_DEADLOCK
Saved deadlock information, also for loading as an extra check.
@ SUMO_ATTR_EDGES
the edges of a route
@ SUMO_ATTR_SIGNALS
@ SUMO_ATTR_ID
@ SUMO_ATTR_TIME
trigger: the time of the step
T MIN2(T a, T b)
Definition StdDefs.h:80
T MAX2(T a, T b)
Definition StdDefs.h:86
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition ToString.h:57
static bool haveDriveWays()
Definition MSDriveWay.h:72
static const std::vector< MSDriveWay * > & getEndingDriveWays(const MSEdge *edge)
const std::vector< const MSLane * > & getForward() const
Definition MSDriveWay.h:157
const std::vector< const MSEdge * > & getRoute() const
Definition MSDriveWay.h:117
A road/street connecting two junctions.
Definition MSEdge.h:78
SVCPermissions getPermissions() const
Returns the combined permissions of all lanes of this edge.
Definition MSEdge.h:659
SumoXMLEdgeFunc getFunction() const
Returns the edge type (SumoXMLEdgeFunc)
Definition MSEdge.h:260
static bool gUseMesoSim
Definition MSGlobals.h:106
static bool gUsingInternalLanes
Information whether the simulation regards internal lanes.
Definition MSGlobals.h:81
The base class for an intersection.
Definition MSJunction.h:58
VehicleState
Definition of a vehicle state.
Definition MSNet.h:636
@ BUILT
The vehicle was built, but has not yet departed.
@ NEWROUTE
The vehicle got a new route.
static MSNet * getInstance()
Returns the pointer to the unique instance of MSNet (singleton).
Definition MSNet.cpp:201
void addVehicleStateListener(VehicleStateListener *listener)
Adds a vehicle states listener.
Definition MSNet.cpp:1385
A base class for constraints.
virtual void setActive(bool active)=0
virtual void write(OutputDevice &out, const std::string &tripId) const =0
A signal for rails.
void loadDeadlockRoute(const ConstMSEdgeVector &edges)
void notifyApproach(const MSLink *link)
switch rail signal to active
void addSignal(MSRailSignal *signal)
void vehicleStateChanged(const SUMOVehicle *const vehicle, MSNet::VehicleState to, const std::string &info="")
Called if a vehicle changes its state.
std::vector< Deadlock > myLoadedDeadlocks
void addDrivewayFollower(const MSDriveWay *dw, const MSDriveWay *dw2)
std::vector< MSRailSignal * > mySignals
list of all rail signals
void findDeadlockFoesFromRoutes(const MSDriveWay *dw, const Deadlock &dl, int signalIndex, std::vector< const MSDriveWay * > deadlockFoes)
MSRailSignalControl()
Constructor.
void loadDeadlockCheck(std::vector< const MSRailSignal * > signals)
void addWaitRelation(const SUMOVehicle *waits, const MSRailSignal *rs, const SUMOVehicle *reason, MSRailSignalConstraint *constraint=nullptr)
static MSRailSignalControl * myInstance
std::map< const SUMOVehicle *, WaitRelation > myWaitRelations
void addDWDeadlockChecks(const MSRailSignal *rs, MSDriveWay *dw)
check whether the given signal and driveway are part of a deadlock circle
static MSRailSignalControl & getInstance()
std::map< const MSDriveWay *, std::set< const MSDriveWay * > > myDriveWayPred
static SVCPermissions mySignalizedClasses
signalized classes
bool haveDeadlock(const SUMOVehicle *veh) const
whether there is a circle in the waiting-for relationships that contains the given vehicle
std::set< std::set< const SUMOVehicle * > > myWrittenDeadlocks
void updateSignals(SUMOTime t)
update active rail signals
std::set< MSRailSignal *, ComparatorNumericalIdLess > myActiveSignals
std::map< std::pair< int, int >, bool > myDriveWayCompatibility
static void clearState()
Perform resets events when quick-loading state.
static SVCPermissions myMBClasses
std::map< const MSDriveWay *, std::set< const MSDriveWay *, ComparatorNumericalIdLess > > myDriveWaySucc
std::map< const MSRailSignal *, std::vector< std::pair< int, int > > > mySignalDeadlocks
void findDeadlockFoes(const MSDriveWay *dw, const Deadlock &dl, int signalIndex, std::vector< const MSDriveWay * > deadlockFoes)
A signal for rails.
const MSJunction * getJunction() const
get the unique junction controlled by this signal
bool updateCurrentPhase()
returns the state of the signal that actually required
static void initDriveWays(const SUMOVehicle *ego, bool update)
MSRouteIterator end() const
Returns the end of the list of edges to pass.
Definition MSRoute.cpp:79
MSRouteIterator begin() const
Returns the begin of the list of edges to pass.
Definition MSRoute.cpp:73
const LinkVectorVector & getLinks() const
Returns the list of lists of all affected links.
bool isActive() const
whether this logic is the active program
bool setTrafficLightSignals(SUMOTime t) const
Applies the current signal states to controlled links.
Representation of a vehicle in the micro simulation.
Definition MSVehicle.h:77
const std::string & getID() const
Returns the id.
Definition Named.h:73
A storage for options typed value containers)
Definition OptionsCont.h:89
bool isSet(const std::string &name, bool failOnNonExistant=true) const
Returns the information whether the named option is set.
bool getBool(const std::string &name) const
Returns the boolean-value of the named option (only for Option_Bool)
static OptionsCont & getOptions()
Retrieves the options.
Static storage of an output device and its base (abstract) implementation.
OutputDevice & openTag(const std::string &xmlElement)
Opens an XML tag.
OutputDevice & writeAttr(const ATTR_TYPE &attr, const T &val, const bool isNull=false, const bool escape=false)
writes a named attribute
static OutputDevice & getDeviceByOption(const std::string &name)
Returns the device described by the option.
bool closeTag(const std::string &comment="")
Closes the most recently opened tag and optionally adds a comment.
virtual const std::string getParameter(const std::string &key, const std::string defaultValue="") const
Returns the value for a given key.
virtual const SUMOVehicleParameter & getParameter() const =0
Returns the vehicle's parameter (including departure definition)
virtual const MSEdge * getEdge() const =0
Returns the (normal) route edge the object is currently at.
Representation of a vehicle.
Definition SUMOVehicle.h:63
virtual bool isRail() const =0
virtual double getLength() const =0
Returns the vehicles's length.
virtual bool hasValidRoute(std::string &msg, ConstMSRoutePtr route=0) const =0
Validates the current or given route.
bool wasSet(long long int what) const
Returns whether the given parameter was set.
#define UNUSED_PARAMETER(x)
storage for loaded deadlocks
std::vector< const MSRailSignal * > signals
std::vector< ConstMSEdgeVector > edges