1# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo 2# Copyright (C) 2017-2019 German Aerospace Center (DLR) and others. 3# This program and the accompanying materials 4# are made available under the terms of the Eclipse Public License v2.0 5# which accompanies this distribution, and is available at 6# http://www.eclipse.org/legal/epl-v20.html 7# SPDX-License-Identifier: EPL-2.0 8 9# @file _config.py 10# @author Leonhard Luecken 11# @date 2017-04-09 12# @version $Id$ 13 14 15from collections import defaultdict 16import os 17import sys 18import xml.etree.ElementTree as ET 19 20if 'SUMO_HOME' in os.environ: 21 tools = os.path.join(os.environ['SUMO_HOME'], 'tools') 22 sys.path.append(tools) 23else: 24 sys.exit("please declare environment variable 'SUMO_HOME'") 25 26from simpla._platoonmode import PlatoonMode # noqa 27import simpla._reporting as rp # noqa 28from simpla import SimplaException # noqa 29 30warn = rp.Warner("Config") 31report = rp.Reporter("Config") 32 33 34def initDefaults(): 35 ''' 36 Init default values for the configuration parameters. 37 They are overriden by specification in a configuration file (see load() method). 38 ''' 39 global CONTROL_RATE, VEH_SELECTORS, MAX_PLATOON_GAP, CATCHUP_DIST, PLATOON_SPLIT_TIME 40 global VTYPE_FILE, PLATOON_VTYPES, LC_MODE, SPEEDFACTOR, SWITCH_IMPATIENCE_FACTOR 41 42 # Rate for updating the platoon manager checks and advices 43 CONTROL_RATE = 1.0 44 45 # specify substring for vtypes, that should be controlled by platoon manager 46 VEH_SELECTORS = [""] 47 48 # Distance in meters below which a vehicle joins a leading platoon 49 MAX_PLATOON_GAP = 15.0 50 51 # Distance in meters below which a vehicle tries to catch up with a platoon in front 52 CATCHUP_DIST = 50.0 53 54 # Latency time in secs. until a platoon is split if vehicles exceed PLATOON_SPLIT_DISTANCE to their 55 # leaders within a platoon (or if they are not the direct follower), 56 # or drive on different lanes than their leader within the platoon 57 PLATOON_SPLIT_TIME = 3.0 58 59 # The switch impatience factor determines the magnitude of the effect 60 # that an increasing waiting time has on the active speed factor of a vehicle: 61 # activeSpeedFactor = modeSpecificSpeedFactor/(1+impatienceFactor*waitingTime) 62 SWITCH_IMPATIENCE_FACTOR = 0.1 63 64 # Lanechange modes for the different platooning modes 65 LC_MODE = { 66 # for solitary mode use default mode 67 PlatoonMode.NONE: 0b1001010101, 68 # for platoon leader use default mode 69 PlatoonMode.LEADER: 0b1001010101, 70 # for platoon follower do not change lanes, except for traci commands 71 # of for strategic reasons (these override traci) 72 PlatoonMode.FOLLOWER: 0b1000000010, 73 # for platoon catchup as for follower 74 PlatoonMode.CATCHUP: 0b1000000010, 75 # for platoon catchup follower as for follower 76 PlatoonMode.CATCHUP_FOLLOWER: 0b1000000010 77 } 78 79 # speedfactors for the different platooning modes 80 SPEEDFACTOR = { 81 PlatoonMode.NONE: None, # is not altered 82 PlatoonMode.LEADER: 1.0, 83 PlatoonMode.FOLLOWER: 1.0, 84 PlatoonMode.CATCHUP: 1.1, 85 PlatoonMode.CATCHUP_FOLLOWER: None # is set to the same as for catchup mode below if not explicitely set 86 } 87 88 # file with vtype maps for platoon types 89 VTYPE_FILE = "" 90 91 # map of original to platooning vTypes 92 PLATOON_VTYPES = defaultdict(dict) 93 94 95# perform initialization 96initDefaults() 97 98 99def loadVTypeMap(fn): 100 '''loadVTypeMap(string) -> dict 101 102 Reads lines of the form 'origMode:leadMode:followMode:catchupMode:catchupFollowMode' (last three elements 103 can be omitted) from a given file and write corresponding key:value pairs to PLATOON_VTYPES 104 ''' 105 global PLATOON_VTYPES 106 107 with open(fn, "r") as f: 108 # if rp.VERBOSITY >= 2: 109 if rp.VERBOSITY >= 2: 110 report("Loading vehicle type mappings from file '%s'..." % fn, True) 111 splits = [l.split(":") for l in f.readlines()] 112 NrBadLines = 0 113 for j, spl in enumerate(splits): 114 if len(spl) >= 2 and len(spl) <= 5: 115 stripped = list(map(lambda x: x.strip(), spl)) 116 origType = stripped[0] 117 if origType == "": 118 raise SimplaException("Original vType must be specified in line %s of vType file '%s'!" % (j, fn)) 119 if rp.VERBOSITY >= 2: 120 report("original type: '%s'" % origType, True) 121 122 leadType = stripped[1] 123 if leadType == "": 124 raise SimplaException( 125 "Platoon leader vType must be specified in line %s of vType file '%s'!" % (j, fn)) 126 if rp.VERBOSITY >= 2: 127 report("platoon leader type: '%s'" % leadType, True) 128 129 if (len(stripped) >= 3 and stripped[2] != ""): 130 followerType = stripped[2] 131 if rp.VERBOSITY >= 2: 132 report("platoon follower type: '%s'" % followerType, True) 133 else: 134 followerType = None 135 136 if (len(stripped) >= 4 and stripped[3] != ""): 137 catchupType = stripped[3] 138 if rp.VERBOSITY >= 2: 139 report("catchup type: '%s'" % catchupType, True) 140 else: 141 catchupType = None 142 143 if len(stripped) >= 5 and stripped[4] != "": 144 catchupFollowerType = stripped[4] 145 if rp.VERBOSITY >= 2: 146 report("catchup follower type: '%s'" % catchupFollowerType, True) 147 else: 148 catchupFollowerType = None 149 150 PLATOON_VTYPES[origType][PlatoonMode.NONE] = origType 151 PLATOON_VTYPES[origType][PlatoonMode.LEADER] = leadType 152 PLATOON_VTYPES[origType][PlatoonMode.FOLLOWER] = followerType 153 PLATOON_VTYPES[origType][PlatoonMode.CATCHUP] = catchupType 154 PLATOON_VTYPES[origType][PlatoonMode.CATCHUP_FOLLOWER] = catchupFollowerType 155 else: 156 NrBadLines += 1 157 if NrBadLines > 0: 158 if rp.VERBOSITY >= 1: 159 warn(("vType file '%s' contained %d lines that were not parsed into a colon-separated " + 160 "sequence of strings!") % (fn, NrBadLines)) 161 162 163def load(filename): 164 '''load(string) 165 166 This loads configuration parameters from a file and overwrites default values. 167 ''' 168 global CONTROL_RATE, VEH_SELECTORS, MAX_PLATOON_GAP, CATCHUP_DIST, PLATOON_SPLIT_TIME 169 global VTYPE_FILE, PLATOON_VTYPES, LC_MODE, SPEEDFACTOR, SWITCH_IMPATIENCE_FACTOR 170 171 configDir = os.path.dirname(filename) 172 configElements = ET.parse(filename).getroot().getchildren() 173 parsedTags = [] 174 for e in configElements: 175 parsedTags.append(e.tag) 176 if e.tag == "verbosity": 177 if hasAttributes(e): 178 verbosity = int(list(e.attrib.values())[0]) 179 if verbosity in range(5): 180 rp.VERBOSITY = verbosity 181 else: 182 if rp.VERBOSITY >= 1: 183 warn("Verbosity must be one of %s! Ignoring given value: %s" % 184 (str(list(range(5))), verbosity), True) 185 elif e.tag == "controlRate": 186 if hasAttributes(e): 187 rate = float(list(e.attrib.values())[0]) 188 if rate <= 0.: 189 if rp.VERBOSITY >= 1: 190 warn("Parameter controlRate must be positive. Ignoring given value: %s" % (rate), True) 191 else: 192 CONTROL_RATE = float(rate) 193 elif e.tag == "vehicleSelectors": 194 if hasAttributes(e): 195 VEH_SELECTORS = list(map(lambda x: x.strip(), list(e.attrib.values())[0].split(","))) 196 elif e.tag == "maxPlatoonGap": 197 if hasAttributes(e): 198 maxgap = float(list(e.attrib.values())[0]) 199 if maxgap <= 0: 200 if rp.VERBOSITY >= 1: 201 warn("Parameter maxPlatoonGap must be positive. Ignoring given value: %s" % (maxgap), True) 202 else: 203 MAX_PLATOON_GAP = maxgap 204 elif e.tag == "catchupDist": 205 if hasAttributes(e): 206 dist = float(list(e.attrib.values())[0]) 207 if maxgap <= 0: 208 if rp.VERBOSITY >= 1: 209 warn("Parameter catchupDist must be positive. Ignoring given value: %s" % (dist), True) 210 else: 211 CATCHUP_DIST = dist 212 elif e.tag == "switchImpatienceFactor": 213 if hasAttributes(e): 214 impfact = float(list(e.attrib.values())[0]) 215 if impfact < 0: 216 if rp.VERBOSITY >= 1: 217 warn("Parameter switchImpatienceFactor must be non-negative. Ignoring given value: %s" % 218 (impfact), True) 219 else: 220 SWITCH_IMPATIENCE_FACTOR = impfact 221 elif e.tag == "platoonSplitTime": 222 if hasAttributes(e): 223 splittime = float(list(e.attrib.values())[0]) 224 if splittime < 0: 225 if rp.VERBOSITY >= 1: 226 warn("Parameter platoonSplitTime must be non-negative. Ignoring given value: %s" % 227 (splittime), True) 228 else: 229 PLATOON_SPLIT_TIME = splittime 230 elif e.tag == "lcMode": 231 if hasAttributes(e): 232 if ("leader" in e.attrib): 233 if isValidLCMode(int(e.attrib["leader"])): 234 LC_MODE[PlatoonMode.LEADER] = int(e.attrib["leader"]) 235 if ("follower" in e.attrib): 236 if isValidLCMode(int(e.attrib["follower"])): 237 LC_MODE[PlatoonMode.FOLLOWER] = int(e.attrib["follower"]) 238 if ("catchup" in e.attrib): 239 if isValidLCMode(int(e.attrib["catchup"])): 240 LC_MODE[PlatoonMode.CATCHUP] = int(e.attrib["catchup"]) 241 if ("catchupFollower" in e.attrib): 242 if isValidLCMode(int(e.attrib["catchupFollower"])): 243 LC_MODE[PlatoonMode.CATCHUP_FOLLOWER] = int(e.attrib["catchupFollower"]) 244 if ("original" in e.attrib): 245 if isValidLCMode(int(e.attrib["original"])): 246 LC_MODE[PlatoonMode.NONE] = int(e.attrib["original"]) 247 elif e.tag == "speedFactor": 248 if hasAttributes(e): 249 if ("leader" in e.attrib): 250 if isValidSpeedFactor(float(e.attrib["leader"])): 251 SPEEDFACTOR[PlatoonMode.LEADER] = float(e.attrib["leader"]) 252 if ("follower" in e.attrib): 253 if isValidSpeedFactor(float(e.attrib["follower"])): 254 SPEEDFACTOR[PlatoonMode.FOLLOWER] = float(e.attrib["follower"]) 255 if ("catchup" in e.attrib): 256 if isValidSpeedFactor(float(e.attrib["catchup"])): 257 SPEEDFACTOR[PlatoonMode.CATCHUP] = float(e.attrib["catchup"]) 258 if ("catchupFollower" in e.attrib): 259 if isValidSpeedFactor(float(e.attrib["catchupFollower"])): 260 SPEEDFACTOR[PlatoonMode.CATCHUP_FOLLOWER] = float(e.attrib["catchupFollower"]) 261 if ("original" in e.attrib): 262 if isValidSpeedFactor(float(e.attrib["original"])): 263 SPEEDFACTOR[PlatoonMode.NONE] = float(e.attrib["original"]) 264 elif e.tag == "vTypeMapFile": 265 if hasAttributes(e): 266 fn = os.path.join(configDir, list(e.attrib.values())[0]) 267 if not os.path.isfile(fn): 268 raise SimplaException("Given vTypeMapFile '%s' does not exist." % fn) 269 VTYPE_FILE = fn 270 elif e.tag == "vTypeMap": 271 if hasAttributes(e): 272 if "original" not in e.attrib: 273 warn("vTypeMap must specify original type. Ignoring malformed vTypeMap element.", True) 274 else: 275 origType = e.attrib["original"] 276 PLATOON_VTYPES[origType][PlatoonMode.NONE] = origType 277 if ("leader" in e.attrib): 278 leaderType = e.attrib["leader"] 279 PLATOON_VTYPES[origType][PlatoonMode.LEADER] = leaderType 280 # report("Registering vtype map '%s':'%s'"%(origType,leaderType), True) 281 if ("follower" in e.attrib): 282 followerType = e.attrib["follower"] 283 PLATOON_VTYPES[origType][PlatoonMode.FOLLOWER] = followerType 284 # report("Registering vtype map '%s':'%s'"%(origType,followerType), True) 285 if ("catchup" in e.attrib): 286 catchupType = e.attrib["catchup"] 287 PLATOON_VTYPES[origType][PlatoonMode.CATCHUP] = catchupType 288 # report("Registering vtype map '%s':'%s'"%(origType,followerType), True) 289 if ("catchupFollower" in e.attrib): 290 catchupFollowerType = e.attrib["catchupFollower"] 291 PLATOON_VTYPES[origType][PlatoonMode.CATCHUP_FOLLOWER] = catchupFollowerType 292 # report("Registering vtype map '%s':'%s'"%(origType,followerType), True) 293 elif rp.VERBOSITY >= 1: 294 warn("Encountered unknown configuration parameter '%s'!" % e.tag, True) 295 296 if "vTypeMapFile" in parsedTags: 297 # load vType mapping from file 298 loadVTypeMap(VTYPE_FILE) 299 300 if SPEEDFACTOR[PlatoonMode.CATCHUP_FOLLOWER] is None: 301 # if unset, set speedfactor for catchupfollower mode to the same as in catchup mode 302 SPEEDFACTOR[PlatoonMode.CATCHUP_FOLLOWER] = SPEEDFACTOR[PlatoonMode.CATCHUP] 303 304 305def hasAttributes(element): 306 ''' 307 check if xml element has at least one attribute 308 ''' 309 # print("Checking element {tag}:\n{attributes}".format(tag=element.tag, attributes=str(element.attrib))) 310 if len(element.attrib) == 0: 311 warn("No attributes found for tag '%s'." % element.tag, True) 312 return False 313 else: 314 return True 315 316 317def isValidLCMode(mode): 318 if 0 <= mode <= 1023: 319 return True 320 else: 321 warn("Given lane change mode '%d' lies out of admissible range [0,255]. Using default mode instead." % ( 322 mode), True) 323 return False 324 325 326def isValidSpeedFactor(value): 327 if 0 < value: 328 return True 329 else: 330 warn("Given speedFactor %s is invalid. Using default value." % (value), True) 331 return False 332