1# -*- coding: utf-8 -*-
2# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3# Copyright (C) 2011-2019 German Aerospace Center (DLR) and others.
4# This program and the accompanying materials
5# are made available under the terms of the Eclipse Public License v2.0
6# which accompanies this distribution, and is available at
7# http://www.eclipse.org/legal/epl-v20.html
8# SPDX-License-Identifier: EPL-2.0
9
10# @file    _simulation.py
11# @author  Daniel Krajzewicz
12# @author  Jakob Erdmann
13# @author  Michael Behrisch
14# @date    2011-03-15
15# @version $Id$
16
17from __future__ import absolute_import
18import struct
19import collections
20import warnings
21from . import constants as tc
22from .domain import Domain
23from .storage import Storage
24
25Stage = collections.namedtuple('Stage', ['stageType', 'vType', 'line', 'destStop', 'edges', 'travelTime', 'cost',
26                                         'length', 'intended', 'depart', 'departPos', 'arrivalPos', 'description'])
27
28
29def _readStage(result):
30    # compound size and type
31    assert(result.read("!i")[0] == 13)
32    stageType = result.readTypedInt()
33    vType = result.readTypedString()
34    line = result.readTypedString()
35    destStop = result.readTypedString()
36    edges = result.readTypedStringList()
37    travelTime = result.readTypedDouble()
38    cost = result.readTypedDouble()
39    length = result.readTypedDouble()
40    intended = result.readTypedString()
41    depart = result.readTypedDouble()
42    departPos = result.readTypedDouble()
43    arrivalPos = result.readTypedDouble()
44    description = result.readTypedString()
45    return Stage(stageType, vType, line, destStop, edges, travelTime, cost,
46                 length, intended, depart, departPos, arrivalPos, description)
47
48
49_RETURN_VALUE_FUNC = {tc.VAR_TIME: Storage.readDouble,
50                      tc.VAR_TIME_STEP: Storage.readInt,
51                      tc.VAR_LOADED_VEHICLES_NUMBER: Storage.readInt,
52                      tc.VAR_LOADED_VEHICLES_IDS: Storage.readStringList,
53                      tc.VAR_DEPARTED_VEHICLES_NUMBER: Storage.readInt,
54                      tc.VAR_DEPARTED_VEHICLES_IDS: Storage.readStringList,
55                      tc.VAR_ARRIVED_VEHICLES_NUMBER: Storage.readInt,
56                      tc.VAR_ARRIVED_VEHICLES_IDS: Storage.readStringList,
57                      tc.VAR_PARKING_STARTING_VEHICLES_NUMBER: Storage.readInt,
58                      tc.VAR_PARKING_STARTING_VEHICLES_IDS: Storage.readStringList,
59                      tc.VAR_PARKING_ENDING_VEHICLES_NUMBER: Storage.readInt,
60                      tc.VAR_PARKING_ENDING_VEHICLES_IDS: Storage.readStringList,
61                      tc.VAR_STOP_STARTING_VEHICLES_NUMBER: Storage.readInt,
62                      tc.VAR_STOP_STARTING_VEHICLES_IDS: Storage.readStringList,
63                      tc.VAR_STOP_ENDING_VEHICLES_NUMBER: Storage.readInt,
64                      tc.VAR_STOP_ENDING_VEHICLES_IDS: Storage.readStringList,
65                      tc.VAR_COLLIDING_VEHICLES_NUMBER: Storage.readInt,
66                      tc.VAR_COLLIDING_VEHICLES_IDS: Storage.readStringList,
67                      tc.VAR_EMERGENCYSTOPPING_VEHICLES_NUMBER: Storage.readInt,
68                      tc.VAR_EMERGENCYSTOPPING_VEHICLES_IDS: Storage.readStringList,
69                      tc.VAR_MIN_EXPECTED_VEHICLES: Storage.readInt,
70                      tc.VAR_BUS_STOP_WAITING: Storage.readInt,
71                      tc.VAR_TELEPORT_STARTING_VEHICLES_NUMBER: Storage.readInt,
72                      tc.VAR_TELEPORT_STARTING_VEHICLES_IDS: Storage.readStringList,
73                      tc.VAR_TELEPORT_ENDING_VEHICLES_NUMBER: Storage.readInt,
74                      tc.VAR_TELEPORT_ENDING_VEHICLES_IDS: Storage.readStringList,
75                      tc.VAR_DELTA_T: Storage.readDouble,
76                      tc.VAR_NET_BOUNDING_BOX: Storage.readShape}
77
78
79class SimulationDomain(Domain):
80
81    def __init__(self):
82        Domain.__init__(self, "simulation", tc.CMD_GET_SIM_VARIABLE, tc.CMD_SET_SIM_VARIABLE,
83                        tc.CMD_SUBSCRIBE_SIM_VARIABLE, tc.RESPONSE_SUBSCRIBE_SIM_VARIABLE,
84                        tc.CMD_SUBSCRIBE_SIM_CONTEXT, tc.RESPONSE_SUBSCRIBE_SIM_CONTEXT,
85                        _RETURN_VALUE_FUNC)
86
87    def getTime(self):
88        """getTime() -> double
89
90        Returns the current simulation time in s.
91        """
92        return self._getUniversal(tc.VAR_TIME)
93
94    def step(self, time=0.):
95        """step(double) -> None
96        Make a simulation step and simulate up to the given sim time (in seconds).
97        If the given value is 0 or absent, exactly one step is performed.
98        Values smaller than or equal to the current sim time result in no action.
99        """
100        return self._connection.simulationStep(time)
101
102    def getCurrentTime(self):
103        """getCurrentTime() -> integer
104
105        Returns the current simulation time in ms.
106        """
107        # we should raise the awareness by removing the DeprecationWarning category below after 1.0
108        warnings.warn("getCurrentTime is deprecated, please use getTime which returns floating point seconds",
109                      DeprecationWarning, stacklevel=2)
110        return self._getUniversal(tc.VAR_TIME_STEP)
111
112    def getLoadedNumber(self):
113        """getLoadedNumber() -> integer
114
115        Returns the number of vehicles which were loaded in this time step.
116        """
117        return self._getUniversal(tc.VAR_LOADED_VEHICLES_NUMBER)
118
119    def getLoadedIDList(self):
120        """getLoadedIDList() -> list(string)
121
122        Returns a list of ids of vehicles which were loaded in this time step.
123        """
124        return self._getUniversal(tc.VAR_LOADED_VEHICLES_IDS)
125
126    def getDepartedNumber(self):
127        """getDepartedNumber() -> integer
128
129        Returns the number of vehicles which departed (were inserted into the road network) in this time step.
130        """
131        return self._getUniversal(tc.VAR_DEPARTED_VEHICLES_NUMBER)
132
133    def getDepartedIDList(self):
134        """getDepartedIDList() -> list(string)
135
136        Returns a list of ids of vehicles which departed (were inserted into the road network) in this time step.
137        """
138        return self._getUniversal(tc.VAR_DEPARTED_VEHICLES_IDS)
139
140    def getArrivedNumber(self):
141        """getArrivedNumber() -> integer
142
143        Returns the number of vehicles which arrived (have reached their destination and are removed from the road
144        network) in this time step.
145        """
146        return self._getUniversal(tc.VAR_ARRIVED_VEHICLES_NUMBER)
147
148    def getArrivedIDList(self):
149        """getArrivedIDList() -> list(string)
150
151        Returns a list of ids of vehicles which arrived (have reached their destination and are removed from the road
152        network) in this time step.
153        """
154        return self._getUniversal(tc.VAR_ARRIVED_VEHICLES_IDS)
155
156    def getParkingStartingVehiclesNumber(self):
157        """getParkingStartingVehiclesNumber() -> integer
158
159        .
160        """
161        return self._getUniversal(tc.VAR_PARKING_STARTING_VEHICLES_NUMBER)
162
163    def getParkingStartingVehiclesIDList(self):
164        """getParkingStartingVehiclesIDList() -> list(string)
165
166        .
167        """
168        return self._getUniversal(tc.VAR_PARKING_STARTING_VEHICLES_IDS)
169
170    def getParkingEndingVehiclesNumber(self):
171        """getParkingEndingVehiclesNumber() -> integer
172
173        .
174        """
175        return self._getUniversal(tc.VAR_PARKING_ENDING_VEHICLES_NUMBER)
176
177    def getParkingEndingVehiclesIDList(self):
178        """getParkingEndingVehiclesIDList() -> list(string)
179
180        .
181        """
182        return self._getUniversal(tc.VAR_PARKING_ENDING_VEHICLES_IDS)
183
184    def getStopStartingVehiclesNumber(self):
185        """getStopStartingVehiclesNumber() -> integer
186
187        .
188        """
189        return self._getUniversal(tc.VAR_STOP_STARTING_VEHICLES_NUMBER)
190
191    def getStopStartingVehiclesIDList(self):
192        """getStopStartingVehiclesIDList() -> list(string)
193
194        .
195        """
196        return self._getUniversal(tc.VAR_STOP_STARTING_VEHICLES_IDS)
197
198    def getStopEndingVehiclesNumber(self):
199        """getStopEndingVehiclesNumber() -> integer
200
201        .
202        """
203        return self._getUniversal(tc.VAR_STOP_ENDING_VEHICLES_NUMBER)
204
205    def getStopEndingVehiclesIDList(self):
206        """getStopEndingVehiclesIDList() -> list(string)
207
208        .
209        """
210        return self._getUniversal(tc.VAR_STOP_ENDING_VEHICLES_IDS)
211
212    def getCollidingVehiclesNumber(self):
213        """getCollidingVehiclesNumber() -> integer
214        Return number of vehicles involved in a collision (typically 2 per
215        collision).
216        """
217        return self._getUniversal(tc.VAR_COLLIDING_VEHICLES_NUMBER)
218
219    def getCollidingVehiclesIDList(self):
220        """getCollidingVehiclesIDList() -> list(string)
221        Return Ids of vehicles involved in a collision (typically 2 per
222        collision).
223        """
224        return self._getUniversal(tc.VAR_COLLIDING_VEHICLES_IDS)
225
226    def getEmergencyStoppingVehiclesNumber(self):
227        """getEmergencyStoppingVehiclesNumber() -> integer
228        Return number of vehicles that performed an emergency stop in the last step
229        """
230        return self._getUniversal(tc.VAR_EMERGENCYSTOPPING_VEHICLES_NUMBER)
231
232    def getEmergencyStoppingVehiclesIDList(self):
233        """getEmergencyStoppingVehiclesIDList() -> list(string)
234        Return Ids of vehicles that peformed an emergency stop in the last step
235        """
236        return self._getUniversal(tc.VAR_EMERGENCYSTOPPING_VEHICLES_IDS)
237
238    def getMinExpectedNumber(self):
239        """getMinExpectedNumber() -> integer
240
241        Returns the number of vehicles which are in the net plus the
242        ones still waiting to start. This number may be smaller than
243        the actual number of vehicles still to come because of delayed
244        route file parsing. If the number is 0 however, it is
245        guaranteed that all route files have been parsed completely
246        and all vehicles have left the network.
247        """
248        return self._getUniversal(tc.VAR_MIN_EXPECTED_VEHICLES)
249
250    def getBusStopWaiting(self, stopID):
251        """getBusStopWaiting() -> integer
252        Get the total number of waiting persons at the named bus stop.
253        """
254        return self._getUniversal(tc.VAR_BUS_STOP_WAITING, stopID)
255
256    def getStartingTeleportNumber(self):
257        """getStartingTeleportNumber() -> integer
258
259        Returns the number of vehicles which started to teleport in this time step.
260        """
261        return self._getUniversal(tc.VAR_TELEPORT_STARTING_VEHICLES_NUMBER)
262
263    def getStartingTeleportIDList(self):
264        """getStartingTeleportIDList() -> list(string)
265
266        Returns a list of ids of vehicles which started to teleport in this time step.
267        """
268        return self._getUniversal(tc.VAR_TELEPORT_STARTING_VEHICLES_IDS)
269
270    def getEndingTeleportNumber(self):
271        """getEndingTeleportNumber() -> integer
272
273        Returns the number of vehicles which ended to be teleported in this time step.
274        """
275        return self._getUniversal(tc.VAR_TELEPORT_ENDING_VEHICLES_NUMBER)
276
277    def getEndingTeleportIDList(self):
278        """getEndingTeleportIDList() -> list(string)
279
280        Returns a list of ids of vehicles which ended to be teleported in this time step.
281        """
282        return self._getUniversal(tc.VAR_TELEPORT_ENDING_VEHICLES_IDS)
283
284    def getDeltaT(self):
285        """getDeltaT() -> double
286        Returns the length of one simulation step in seconds
287        """
288        return self._getUniversal(tc.VAR_DELTA_T)
289
290    def getNetBoundary(self):
291        """getNetBoundary() -> ((double, double), (double, double))
292
293        The boundary box of the simulation network.
294        """
295        return self._getUniversal(tc.VAR_NET_BOUNDING_BOX)
296
297    def convert2D(self, edgeID, pos, laneIndex=0, toGeo=False):
298        posType = tc.POSITION_2D
299        if toGeo:
300            posType = tc.POSITION_LON_LAT
301        self._connection._beginMessage(tc.CMD_GET_SIM_VARIABLE, tc.POSITION_CONVERSION,
302                                       "", 1 + 4 + 1 + 4 + len(edgeID) + 8 + 1 + 1 + 1)
303        self._connection._string += struct.pack("!Bi", tc.TYPE_COMPOUND, 2)
304        self._connection._packString(edgeID, tc.POSITION_ROADMAP)
305        self._connection._string += struct.pack("!dBBB",
306                                                pos, laneIndex, tc.TYPE_UBYTE, posType)
307        return self._connection._checkResult(tc.CMD_GET_SIM_VARIABLE, tc.POSITION_CONVERSION, "").read("!dd")
308
309    def convert3D(self, edgeID, pos, laneIndex=0, toGeo=False):
310        posType = tc.POSITION_3D
311        if toGeo:
312            posType = tc.POSITION_LON_LAT_ALT
313        self._connection._beginMessage(tc.CMD_GET_SIM_VARIABLE, tc.POSITION_CONVERSION,
314                                       "", 1 + 4 + 1 + 4 + len(edgeID) + 8 + 1 + 1 + 1)
315        self._connection._string += struct.pack("!Bi", tc.TYPE_COMPOUND, 2)
316        self._connection._packString(edgeID, tc.POSITION_ROADMAP)
317        self._connection._string += struct.pack("!dBBB",
318                                                pos, laneIndex, tc.TYPE_UBYTE, posType)
319        return self._connection._checkResult(tc.CMD_GET_SIM_VARIABLE, tc.POSITION_CONVERSION, "").read("!ddd")
320
321    def convertRoad(self, x, y, isGeo=False, vClass="ignoring"):
322        posType = tc.POSITION_2D
323        if isGeo:
324            posType = tc.POSITION_LON_LAT
325        self._connection._beginMessage(
326            tc.CMD_GET_SIM_VARIABLE, tc.POSITION_CONVERSION, "", 1 + 4 + 1 + 8 + 8 + 1 + 1 + 1 + 4 + len(vClass))
327        self._connection._string += struct.pack("!Bi", tc.TYPE_COMPOUND, 3)
328        self._connection._string += struct.pack("!Bdd", posType, x, y)
329        self._connection._string += struct.pack("!BB", tc.TYPE_UBYTE, tc.POSITION_ROADMAP)
330        self._connection._packString(vClass)
331        result = self._connection._checkResult(
332            tc.CMD_GET_SIM_VARIABLE, tc.POSITION_CONVERSION, "")
333        return result.readString(), result.readDouble(), result.read("!B")[0]
334
335    def convertGeo(self, x, y, fromGeo=False):
336        fromType = tc.POSITION_2D
337        toType = tc.POSITION_LON_LAT
338        if fromGeo:
339            fromType = tc.POSITION_LON_LAT
340            toType = tc.POSITION_2D
341        self._connection._beginMessage(
342            tc.CMD_GET_SIM_VARIABLE, tc.POSITION_CONVERSION, "", 1 + 4 + 1 + 8 + 8 + 1 + 1)
343        self._connection._string += struct.pack("!Bi", tc.TYPE_COMPOUND, 2)
344        self._connection._string += struct.pack("!Bdd", fromType, x, y)
345        self._connection._string += struct.pack("!BB", tc.TYPE_UBYTE, toType)
346        return self._connection._checkResult(tc.CMD_GET_SIM_VARIABLE, tc.POSITION_CONVERSION, "").read("!dd")
347
348    def getDistance2D(self, x1, y1, x2, y2, isGeo=False, isDriving=False):
349        """getDistance2D(double, double, double, double, boolean, boolean) -> double
350
351        Returns the distance between the two coordinate pairs (x1,y1) and (x2,y2)
352
353        If isGeo=True, coordinates are interpreted as longitude and latitude rather
354        than cartesian coordinates in meters.
355
356        If isDriving=True, the coordinates are mapped onto the road network and the
357        length of the shortest route in the network is returned. Otherwise, the
358        straight-line distance is returned.
359        """
360        posType = tc.POSITION_2D
361        if isGeo:
362            posType = tc.POSITION_LON_LAT
363        distType = tc.REQUEST_AIRDIST
364        if isDriving:
365            distType = tc.REQUEST_DRIVINGDIST
366        self._connection._beginMessage(
367            tc.CMD_GET_SIM_VARIABLE, tc.DISTANCE_REQUEST, "", 1 + 4 + 1 + 8 + 8 + 1 + 8 + 8 + 1)
368        self._connection._string += struct.pack("!Bi", tc.TYPE_COMPOUND, 3)
369        self._connection._string += struct.pack("!Bdd", posType, x1, y1)
370        self._connection._string += struct.pack(
371            "!BddB", posType, x2, y2, distType)
372        return self._connection._checkResult(tc.CMD_GET_SIM_VARIABLE, tc.DISTANCE_REQUEST, "").readDouble()
373
374    def getDistanceRoad(self, edgeID1, pos1, edgeID2, pos2, isDriving=False):
375        """getDistanceRoad(string, double, string, double, boolean) -> double
376
377        Reads two positions on the road network and an indicator whether the air or the driving distance shall be
378        computed. Returns the according distance.
379        """
380        distType = tc.REQUEST_AIRDIST
381        if isDriving:
382            distType = tc.REQUEST_DRIVINGDIST
383        self._connection._beginMessage(tc.CMD_GET_SIM_VARIABLE, tc.DISTANCE_REQUEST, "",
384                                       1 + 4 + 1 + 4 + len(edgeID1) + 8 + 1 + 1 + 4 + len(edgeID2) + 8 + 1 + 1)
385        self._connection._string += struct.pack("!Bi", tc.TYPE_COMPOUND, 3)
386        self._connection._packString(edgeID1, tc.POSITION_ROADMAP)
387        self._connection._string += struct.pack("!dB", pos1, 0)
388        self._connection._packString(edgeID2, tc.POSITION_ROADMAP)
389        self._connection._string += struct.pack("!dBB", pos2, 0, distType)
390        return self._connection._checkResult(tc.CMD_GET_SIM_VARIABLE, tc.DISTANCE_REQUEST, "").readDouble()
391
392    def findRoute(self, fromEdge, toEdge, vType="", depart=-1., routingMode=0):
393        self._connection._beginMessage(tc.CMD_GET_SIM_VARIABLE, tc.FIND_ROUTE, "",
394                                       (1 + 4 + 1 + 4 + len(fromEdge) + 1 + 4 + len(toEdge) + 1 + 4 + len(vType) +
395                                        1 + 8 + 1 + 4))
396        self._connection._string += struct.pack("!Bi", tc.TYPE_COMPOUND, 5)
397        self._connection._packString(fromEdge)
398        self._connection._packString(toEdge)
399        self._connection._packString(vType)
400        self._connection._string += struct.pack("!BdBi", tc.TYPE_DOUBLE, depart, tc.TYPE_INTEGER, routingMode)
401        return _readStage(self._connection._checkResult(tc.CMD_GET_SIM_VARIABLE, tc.FIND_ROUTE, ""))
402
403    def findIntermodalRoute(self, fromEdge, toEdge, modes="", depart=-1., routingMode=0, speed=-1.,
404                            walkFactor=-1., departPos=0., arrivalPos=tc.INVALID_DOUBLE_VALUE, departPosLat=0.,
405                            pType="", vType="", destStop=""):
406        self._connection._beginMessage(tc.CMD_GET_SIM_VARIABLE, tc.FIND_INTERMODAL_ROUTE, "",
407                                       1 + 4 + 1 + 4 + len(fromEdge) + 1 + 4 + len(toEdge) + 1 + 4 + len(modes) +
408                                       1 + 8 + 1 + 4 + 1 + 8 + 1 + 8 + 1 + 8 + 1 + 8 + 1 + 8 + 1 + 4 + len(pType) +
409                                       1 + 4 + len(vType) + 1 + 4 + len(destStop))
410        self._connection._string += struct.pack("!Bi", tc.TYPE_COMPOUND, 13)
411        self._connection._packString(fromEdge)
412        self._connection._packString(toEdge)
413        self._connection._packString(modes)
414        self._connection._string += struct.pack("!BdBi", tc.TYPE_DOUBLE, depart, tc.TYPE_INTEGER, routingMode)
415        self._connection._string += struct.pack("!BdBd", tc.TYPE_DOUBLE, speed, tc.TYPE_DOUBLE, walkFactor)
416        self._connection._string += struct.pack("!BdBd", tc.TYPE_DOUBLE, departPos, tc.TYPE_DOUBLE, arrivalPos)
417        self._connection._string += struct.pack("!Bd", tc.TYPE_DOUBLE, departPosLat)
418        self._connection._packString(pType)
419        self._connection._packString(vType)
420        self._connection._packString(destStop)
421        answer = self._connection._checkResult(tc.CMD_GET_SIM_VARIABLE, tc.FIND_INTERMODAL_ROUTE, "")
422        result = []
423        for c in range(answer.readInt()):
424            answer.read("!B")                   # Type
425            result.append(_readStage(answer))
426        return result
427
428    def clearPending(self, routeID=""):
429        self._connection._beginMessage(tc.CMD_SET_SIM_VARIABLE, tc.CMD_CLEAR_PENDING_VEHICLES, "",
430                                       1 + 4 + len(routeID))
431        self._connection._packString(routeID)
432        self._connection._sendExact()
433
434    def saveState(self, fileName):
435        self._connection._beginMessage(tc.CMD_SET_SIM_VARIABLE, tc.CMD_SAVE_SIMSTATE, "",
436                                       1 + 4 + len(fileName))
437        self._connection._packString(fileName)
438        self._connection._sendExact()
439
440    def subscribe(self, varIDs=(tc.VAR_DEPARTED_VEHICLES_IDS,), begin=0, end=2**31 - 1):
441        """subscribe(list(integer), double, double) -> None
442
443        Subscribe to one or more simulation values for the given interval.
444        """
445        Domain.subscribe(self, "", varIDs, begin, end)
446
447    def getSubscriptionResults(self):
448        """getSubscriptionResults() -> dict(integer: <value_type>)
449
450        Returns the subscription results for the last time step.
451        It is not possible to retrieve older subscription results than the ones
452        from the last time step.
453        """
454        return Domain.getSubscriptionResults(self, "")
455
456
457SimulationDomain()
458