Path: blob/main/src/netload/NLJunctionControlBuilder.cpp
169665 views
/****************************************************************************/1// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo2// Copyright (C) 2001-2025 German Aerospace Center (DLR) and others.3// This program and the accompanying materials are made available under the4// terms of the Eclipse Public License 2.0 which is available at5// https://www.eclipse.org/legal/epl-2.0/6// This Source Code may also be made available under the following Secondary7// Licenses when the conditions for such availability set forth in the Eclipse8// Public License 2.0 are satisfied: GNU General Public License, version 29// or later which is available at10// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html11// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later12/****************************************************************************/13/// @file NLJunctionControlBuilder.cpp14/// @author Daniel Krajzewicz15/// @author Jakob Erdmann16/// @author Sascha Krieg17/// @author Michael Behrisch18/// @date Mon, 9 Jul 200119///20// Builder of microsim-junctions and tls21/****************************************************************************/22#include <config.h>2324#include <map>25#include <string>26#include <vector>27#include <list>28#include <algorithm>29#include <utils/xml/SUMOXMLDefinitions.h>30#include <utils/common/UtilExceptions.h>31#include <utils/common/ToString.h>32#include <microsim/MSGlobals.h>33#include <microsim/MSNet.h>34#include <microsim/MSJunctionLogic.h>35#include <microsim/MSNoLogicJunction.h>36#include <microsim/MSRightOfWayJunction.h>37#include <microsim/MSInternalJunction.h>38#include <microsim/MSJunctionControl.h>39#include <microsim/MSEventControl.h>40#include <microsim/traffic_lights/MSTrafficLightLogic.h>41#include <microsim/traffic_lights/MSSimpleTrafficLightLogic.h>42#include <microsim/traffic_lights/MSRailSignal.h>43#include <microsim/traffic_lights/MSRailCrossing.h>44#include <microsim/traffic_lights/MSSOTLPolicyBasedTrafficLightLogic.h>45#include <microsim/traffic_lights/MSSOTLPlatoonPolicy.h>46#include <microsim/traffic_lights/MSSOTLRequestPolicy.h>47#include <microsim/traffic_lights/MSSOTLPhasePolicy.h>48#include <microsim/traffic_lights/MSSOTLMarchingPolicy.h>49#include <microsim/traffic_lights/MSSwarmTrafficLightLogic.h>50#include <microsim/traffic_lights/MSDeterministicHiLevelTrafficLightLogic.h>51#include <microsim/traffic_lights/MSSOTLWaveTrafficLightLogic.h>52#include <microsim/traffic_lights/MSDelayBasedTrafficLightLogic.h>53#include <microsim/traffic_lights/MSOffTrafficLightLogic.h>54#include <microsim/traffic_lights/MSTLLogicControl.h>55#include "NLBuilder.h"56#include "NLJunctionControlBuilder.h"57#include "microsim/traffic_lights/NEMAController.h"585960// ===========================================================================61// static members62// ===========================================================================63const int NLJunctionControlBuilder::NO_REQUEST_SIZE = -1;6465// ===========================================================================66// method definitions67// ===========================================================================68NLJunctionControlBuilder::NLJunctionControlBuilder(MSNet& net, NLDetectorBuilder& db) :69myNet(net),70myDetectorBuilder(db),71myOffset(0),72myJunctions(new MSJunctionControl()),73myNetIsLoaded(false) {74myLogicControl = new MSTLLogicControl();75}767778NLJunctionControlBuilder::~NLJunctionControlBuilder() {79delete myLogicControl;80delete myJunctions;81}828384void85NLJunctionControlBuilder::openJunction(const std::string& id,86const std::string& key,87const SumoXMLNodeType type,88const Position pos,89const PositionVector& shape,90const std::vector<MSLane*>& incomingLanes,91const std::vector<MSLane*>& internalLanes,92const std::string& name) {93myActiveInternalLanes = internalLanes;94myActiveIncomingLanes = incomingLanes;95myActiveID = id;96myActiveKey = key;97myType = type;98myPosition.set(pos);99myShape = shape;100myActiveName = name;101myAdditionalParameter.clear();102}103104105void106NLJunctionControlBuilder::closeJunction(const std::string& basePath) {107if (myCurrentHasError) {108// had an error before...109return;110}111if (myRequestSize != NO_REQUEST_SIZE && myRequestItemNumber != myRequestSize) {112throw InvalidArgument("The description for the junction logic '" + myActiveKey + "' is malicious.");113}114if (myJunctions == nullptr) {115throw ProcessError(TL("Information about the number of nodes was missing."));116}117MSJunction* junction = nullptr;118switch (myType) {119case SumoXMLNodeType::NOJUNCTION:120case SumoXMLNodeType::DEAD_END:121case SumoXMLNodeType::DEAD_END_DEPRECATED:122case SumoXMLNodeType::DISTRICT:123case SumoXMLNodeType::TRAFFIC_LIGHT_NOJUNCTION:124if (!myActiveLogic.empty()) {125WRITE_WARNINGF(TL("Ignoring junction logic for junction '%'."), myActiveID)126}127junction = buildNoLogicJunction();128break;129case SumoXMLNodeType::TRAFFIC_LIGHT:130case SumoXMLNodeType::TRAFFIC_LIGHT_RIGHT_ON_RED:131case SumoXMLNodeType::RIGHT_BEFORE_LEFT:132case SumoXMLNodeType::LEFT_BEFORE_RIGHT:133case SumoXMLNodeType::PRIORITY:134case SumoXMLNodeType::PRIORITY_STOP:135case SumoXMLNodeType::ALLWAY_STOP:136case SumoXMLNodeType::ZIPPER:137junction = buildLogicJunction(new MSBitsetLogic(myRequestSize, myActiveLogic, myActiveFoes, myActiveConts));138break;139case SumoXMLNodeType::INTERNAL:140if (MSGlobals::gUsingInternalLanes) {141if (!myActiveLogic.empty()) {142WRITE_WARNINGF(TL("Ignoring junction logic for junction '%'."), myActiveID)143}144junction = buildInternalJunction();145}146break;147case SumoXMLNodeType::RAIL_SIGNAL:148case SumoXMLNodeType::RAIL_CROSSING:149myOffset = 0;150myActiveKey = myActiveID;151myActiveProgram = "0";152myLogicType = myType == SumoXMLNodeType::RAIL_SIGNAL ? TrafficLightType::RAIL_SIGNAL : TrafficLightType::RAIL_CROSSING;153closeTrafficLightLogic(basePath);154junction = buildLogicJunction(new MSBitsetLogic(myRequestSize, myActiveLogic, myActiveFoes, myActiveConts));155break;156default:157throw InvalidArgument("False junction logic type.");158}159if (junction != nullptr) {160if (!myJunctions->add(myActiveID, junction)) {161throw InvalidArgument("Another junction with the id '" + myActiveID + "' exists.");162}163junction->updateParameters(myAdditionalParameter);164}165}166167168MSJunctionControl*169NLJunctionControlBuilder::build() const {170MSJunctionControl* js = myJunctions;171myJunctions = nullptr;172return js;173}174175176MSJunction*177NLJunctionControlBuilder::buildNoLogicJunction() {178return new MSNoLogicJunction(myActiveID, myType, myPosition, myShape, myActiveName,179myActiveIncomingLanes, myActiveInternalLanes);180}181182183MSJunction*184NLJunctionControlBuilder::buildLogicJunction(MSJunctionLogic* const logic) {185return new MSRightOfWayJunction(myActiveID, myType, myPosition, myShape, myActiveName,186myActiveIncomingLanes, myActiveInternalLanes, logic);187}188189190MSJunction*191NLJunctionControlBuilder::buildInternalJunction() {192// build the junction193return new MSInternalJunction(myActiveID, myType, myPosition, myShape, myActiveIncomingLanes,194myActiveInternalLanes);195}196197198MSTLLogicControl::TLSLogicVariants&199NLJunctionControlBuilder::getTLLogic(const std::string& id) const {200return getTLLogicControlToUse().get(id);201}202203204void205NLJunctionControlBuilder::closeTrafficLightLogic(const std::string& basePath) {206if (myActiveProgram == "off") {207if (myAbsDuration > 0) {208throw InvalidArgument("The off program for TLS '" + myActiveKey + "' has phases.");209}210MSOffTrafficLightLogic* off = new MSOffTrafficLightLogic(getTLLogicControlToUse(), myActiveKey);211if (!getTLLogicControlToUse().add(myActiveKey, myActiveProgram, off)) {212throw InvalidArgument("Another logic with id '" + myActiveKey + "' and programID '" + myActiveProgram + "' exists.");213}214return;215}216SUMOTime firstEventOffset = 0;217int step = 0;218MSSimpleTrafficLightLogic::Phases::const_iterator i = myActivePhases.begin();219MSTrafficLightLogic* existing = getTLLogicControlToUse().get(myActiveKey, myActiveProgram);220if (existing != nullptr && (existing->getLogicType() == TrafficLightType::RAIL_SIGNAL || existing->getLogicType() == TrafficLightType::RAIL_CROSSING)) {221existing->updateParameters(myAdditionalParameter);222return;223} else {224if (myLogicType != TrafficLightType::RAIL_SIGNAL && myLogicType != TrafficLightType::RAIL_CROSSING) {225if (myAbsDuration == 0) {226if (existing == nullptr) {227throw InvalidArgument("TLS program '" + myActiveProgram + "' for TLS '" + myActiveKey + "' has a duration of 0.");228} else {229// only modify the offset of an existing logic230myAbsDuration = existing->getDefaultCycleTime();231i = existing->getPhases().begin();232}233} else if (existing != nullptr) {234throw InvalidArgument("Another logic with id '" + myActiveKey + "' and programID '" + myActiveProgram + "' exists.");235}236// compute the initial step and first switch time of the tls-logic237// a positive offset delays all phases by x (advance by absDuration - x) while a negative offset advances all phases by x seconds238// @note The implementation of % for negative values is implementation defined in ISO1998239SUMOTime offset; // the time to run the traffic light in advance240if (myOffset >= 0) {241offset = (myNet.getCurrentTimeStep() + myAbsDuration - (myOffset % myAbsDuration)) % myAbsDuration;242} else {243offset = (myNet.getCurrentTimeStep() + ((-myOffset) % myAbsDuration)) % myAbsDuration;244}245while (offset >= (*i)->duration) {246step++;247offset -= (*i)->duration;248++i;249}250firstEventOffset = (*i)->duration - offset + myNet.getCurrentTimeStep();251if (existing != nullptr) {252existing->changeStepAndDuration(getTLLogicControlToUse(),253myNet.getCurrentTimeStep(), step, (*i)->duration - offset);254// parameters that are used when initializing a logic will not take255// effect but parameters that are checked at runtime can be used256// here (i.e. device.glosa.range)257myLogicParams[existing] = myAdditionalParameter;258if (myAdditionalParameter.count(MESegment::OVERRIDE_TLS_PENALTIES) != 0) {259// value must be available when calling setMesoTypes and before setting the rest in postLoadInitialization260existing->setParameter(MESegment::OVERRIDE_TLS_PENALTIES, myAdditionalParameter[MESegment::OVERRIDE_TLS_PENALTIES]);261}262return;263}264}265}266267if (myActiveProgram == "") {268myActiveProgram = "default";269}270MSTrafficLightLogic* tlLogic = nullptr;271// build the tls-logic in dependence to its type272switch (myLogicType) {273case TrafficLightType::SWARM_BASED:274firstEventOffset = DELTA_T; //this is needed because swarm needs to update the pheromone on the lanes at every step275tlLogic = new MSSwarmTrafficLightLogic(getTLLogicControlToUse(), myActiveKey, myActiveProgram, myActivePhases, step, firstEventOffset, myAdditionalParameter);276break;277case TrafficLightType::HILVL_DETERMINISTIC:278tlLogic = new MSDeterministicHiLevelTrafficLightLogic(getTLLogicControlToUse(), myActiveKey, myActiveProgram, myActivePhases, step, firstEventOffset, myAdditionalParameter);279break;280case TrafficLightType::SOTL_REQUEST:281tlLogic = new MSSOTLPolicyBasedTrafficLightLogic(getTLLogicControlToUse(), myActiveKey, myActiveProgram, myLogicType, myActivePhases, step, firstEventOffset, myAdditionalParameter, new MSSOTLRequestPolicy(myAdditionalParameter));282break;283case TrafficLightType::SOTL_PLATOON:284tlLogic = new MSSOTLPolicyBasedTrafficLightLogic(getTLLogicControlToUse(), myActiveKey, myActiveProgram, myLogicType, myActivePhases, step, firstEventOffset, myAdditionalParameter, new MSSOTLPlatoonPolicy(myAdditionalParameter));285break;286case TrafficLightType::SOTL_WAVE:287tlLogic = new MSSOTLWaveTrafficLightLogic(getTLLogicControlToUse(), myActiveKey, myActiveProgram, myActivePhases, step, firstEventOffset, myAdditionalParameter);288break;289case TrafficLightType::SOTL_PHASE:290tlLogic = new MSSOTLPolicyBasedTrafficLightLogic(getTLLogicControlToUse(), myActiveKey, myActiveProgram, myLogicType, myActivePhases, step, firstEventOffset, myAdditionalParameter, new MSSOTLPhasePolicy(myAdditionalParameter));291break;292case TrafficLightType::SOTL_MARCHING:293tlLogic = new MSSOTLPolicyBasedTrafficLightLogic(getTLLogicControlToUse(), myActiveKey, myActiveProgram, myLogicType, myActivePhases, step, firstEventOffset, myAdditionalParameter, new MSSOTLMarchingPolicy(myAdditionalParameter));294break;295case TrafficLightType::ACTUATED:296// @note it is unclear how to apply the given offset in the context297// of variable-length phases298tlLogic = new MSActuatedTrafficLightLogic(getTLLogicControlToUse(),299myActiveKey, myActiveProgram, myOffset,300myActivePhases, step, (*i)->minDuration + myNet.getCurrentTimeStep(),301myAdditionalParameter, basePath, myActiveConditions, myActiveAssignments, myActiveFunctions);302break;303case TrafficLightType::NEMA:304tlLogic = new NEMALogic(getTLLogicControlToUse(),305myActiveKey, myActiveProgram, myOffset,306myActivePhases, step, (*i)->minDuration + myNet.getCurrentTimeStep(),307myAdditionalParameter, basePath);308break;309case TrafficLightType::DELAYBASED:310tlLogic = new MSDelayBasedTrafficLightLogic(getTLLogicControlToUse(),311myActiveKey, myActiveProgram, myOffset,312myActivePhases, step, (*i)->minDuration + myNet.getCurrentTimeStep(),313myAdditionalParameter, basePath);314break;315case TrafficLightType::STATIC:316tlLogic = new MSSimpleTrafficLightLogic(getTLLogicControlToUse(),317myActiveKey, myActiveProgram, myOffset,318TrafficLightType::STATIC,319myActivePhases, step, firstEventOffset,320myAdditionalParameter);321break;322case TrafficLightType::RAIL_SIGNAL:323tlLogic = new MSRailSignal(getTLLogicControlToUse(),324myActiveKey, myActiveProgram, myNet.getCurrentTimeStep(),325myAdditionalParameter);326break;327case TrafficLightType::RAIL_CROSSING:328tlLogic = new MSRailCrossing(getTLLogicControlToUse(),329myActiveKey, myActiveProgram, myNet.getCurrentTimeStep(),330myAdditionalParameter);331break;332case TrafficLightType::OFF:333tlLogic = new MSOffTrafficLightLogic(getTLLogicControlToUse(), myActiveKey);334break;335case TrafficLightType::INVALID:336throw ProcessError(TLF("Invalid traffic light type '%'", toString(myLogicType)));337}338myActivePhases.clear();339if (tlLogic != nullptr) {340if (getTLLogicControlToUse().add(myActiveKey, myActiveProgram, tlLogic)) {341if (myNetIsLoaded) {342myAdditionalLogics.push_back(tlLogic);343} else if (myLogicType == TrafficLightType::RAIL_SIGNAL) {344// special case: intialize earlier because signals are already used when345// loading train routes in additional files346myRailSignals.push_back(tlLogic);347} else {348myNetworkLogics.push_back(tlLogic);349}350} else {351WRITE_ERRORF(TL("Another logic with id '%' and programID '%' exists."), myActiveKey, myActiveProgram);352}353}354}355356357void358NLJunctionControlBuilder::initJunctionLogic(const std::string& id) {359myActiveKey = id;360myActiveProgram = "";361myActiveLogic.clear();362myActiveFoes.clear();363myActiveConts.reset();364myRequestSize = NO_REQUEST_SIZE; // seems not to be used365myRequestItemNumber = 0;366myCurrentHasError = false;367}368369370void371NLJunctionControlBuilder::addLogicItem(int request,372const std::string& response,373const std::string& foes,374bool cont) {375if (myCurrentHasError) {376// had an error377return;378}379if (request >= SUMO_MAX_CONNECTIONS) {380// bad request381myCurrentHasError = true;382throw InvalidArgument("Junction logic '" + myActiveKey + "' is larger than allowed; recheck the network.");383}384if (myRequestSize == NO_REQUEST_SIZE) {385// initialize386myRequestSize = (int)response.size();387}388if (static_cast<int>(response.size()) != myRequestSize) {389myCurrentHasError = true;390throw InvalidArgument("Invalid response size " + toString(response.size()) +391" in Junction logic '" + myActiveKey + "' (expected " + toString(myRequestSize) + ")");392}393if (static_cast<int>(foes.size()) != myRequestSize) {394myCurrentHasError = true;395throw InvalidArgument("Invalid foes size " + toString(foes.size()) +396" in Junction logic '" + myActiveKey + "' (expected " + toString(myRequestSize) + ")");397}398// assert that the logicitems come ordered by their request index399assert((int)myActiveLogic.size() == request);400assert((int)myActiveFoes.size() == request);401// add the read response for the given request index402myActiveLogic.push_back(std::bitset<SUMO_MAX_CONNECTIONS>(response));403// add the read junction-internal foes for the given request index404myActiveFoes.push_back(std::bitset<SUMO_MAX_CONNECTIONS>(foes));405// add whether the vehicle may drive a little bit further406myActiveConts.set(request, cont);407// increse number of set information408myRequestItemNumber++;409}410411412void413NLJunctionControlBuilder::initTrafficLightLogic(const std::string& id, const std::string& programID,414TrafficLightType type, SUMOTime offset) {415myActiveKey = id;416myActiveProgram = programID;417myActivePhases.clear();418myActiveConditions.clear();419myActiveAssignments.clear();420myActiveFunctions.clear();421myAbsDuration = 0;422myRequestSize = NO_REQUEST_SIZE;423myLogicType = type;424myOffset = offset;425myAdditionalParameter.clear();426}427428429void430NLJunctionControlBuilder::addPhase(MSPhaseDefinition* phase) {431// build and add the phase definition to the list432myActivePhases.push_back(phase);433// add phase duration to the absolute duration434myAbsDuration += phase->duration;435}436437438bool439NLJunctionControlBuilder::addCondition(const std::string& id, const std::string& value) {440if (myActiveConditions.count(id) == 0) {441myActiveConditions[id] = value;442return true;443} else {444return false;445}446}447448449void450NLJunctionControlBuilder::addAssignment(const std::string& id, const std::string& check, const std::string& value) {451if (myActiveFunction.id == "") {452myActiveAssignments.push_back(std::make_tuple(id, check, value));453} else {454myActiveFunction.assignments.push_back(std::make_tuple(id, check, value));455}456}457458459void460NLJunctionControlBuilder::addFunction(const std::string& id, int nArgs) {461myActiveFunction.id = id;462myActiveFunction.nArgs = nArgs;463}464465466void467NLJunctionControlBuilder::closeFunction() {468myActiveFunctions[myActiveFunction.id] = myActiveFunction;469myActiveFunction.id = "";470myActiveFunction.assignments.clear();471}472473474MSTLLogicControl*475NLJunctionControlBuilder::buildTLLogics() {476if (!myLogicControl->closeNetworkReading()) {477throw ProcessError(TL("Traffic lights could not be built."));478}479for (MSTrafficLightLogic* const logic : myRailSignals) {480logic->init(myDetectorBuilder);481}482MSTLLogicControl* ret = myLogicControl;483myNetIsLoaded = true;484myLogicControl = nullptr;485return ret;486}487488489void490NLJunctionControlBuilder::addParam(const std::string& key,491const std::string& value) {492myAdditionalParameter[key] = value;493}494495496MSTLLogicControl&497NLJunctionControlBuilder::getTLLogicControlToUse() const {498if (myLogicControl != nullptr) {499return *myLogicControl;500}501return myNet.getTLSControl();502}503504505const std::string&506NLJunctionControlBuilder::getActiveKey() const {507return myActiveKey;508}509510511const std::string&512NLJunctionControlBuilder::getActiveSubKey() const {513return myActiveProgram;514}515516517void518NLJunctionControlBuilder::postLoadInitialization() {519for (MSTrafficLightLogic* const logic : myNetworkLogics) {520logic->init(myDetectorBuilder);521}522for (MSTrafficLightLogic* const logic : myAdditionalLogics) {523logic->init(myDetectorBuilder);524}525// delay parameter loading until initialization526for (auto item : myLogicParams) {527item.first->updateParameters(item.second);528}529}530531532MSJunction*533NLJunctionControlBuilder::retrieve(const std::string id) {534if (myJunctions != nullptr) {535return myJunctions->get(id);536} else {537return nullptr;538}539}540541542/****************************************************************************/543544545