eflips-depot 3.0.0__py3-none-any.whl → 3.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of eflips-depot might be problematic. Click here for more details.
- eflips/depot/.DS_Store +0 -0
- eflips/depot/api/__init__.py +60 -17
- eflips/depot/api/private/depot.py +1 -1
- eflips/depot/depot.py +164 -90
- eflips/depot/processes.py +53 -34
- {eflips_depot-3.0.0.dist-info → eflips_depot-3.0.2.dist-info}/METADATA +1 -1
- {eflips_depot-3.0.0.dist-info → eflips_depot-3.0.2.dist-info}/RECORD +9 -8
- {eflips_depot-3.0.0.dist-info → eflips_depot-3.0.2.dist-info}/LICENSE.md +0 -0
- {eflips_depot-3.0.0.dist-info → eflips_depot-3.0.2.dist-info}/WHEEL +0 -0
eflips/depot/.DS_Store
ADDED
|
Binary file
|
eflips/depot/api/__init__.py
CHANGED
|
@@ -724,11 +724,7 @@ def add_evaluation_to_database(
|
|
|
724
724
|
else:
|
|
725
725
|
for process in process_log:
|
|
726
726
|
match process.status:
|
|
727
|
-
case ProcessStatus.CANCELLED:
|
|
728
|
-
raise NotImplementedError(
|
|
729
|
-
f"Cancelled processes {process.ID} are not implemented."
|
|
730
|
-
)
|
|
731
|
-
case ProcessStatus.COMPLETED:
|
|
727
|
+
case ProcessStatus.COMPLETED | ProcessStatus.CANCELLED:
|
|
732
728
|
assert (
|
|
733
729
|
len(process.starts) == 1 and len(process.ends) == 1
|
|
734
730
|
), (
|
|
@@ -754,13 +750,38 @@ def add_evaluation_to_database(
|
|
|
754
750
|
}
|
|
755
751
|
else:
|
|
756
752
|
# Duration is 0
|
|
757
|
-
|
|
758
|
-
if start_time in dict_of_events:
|
|
759
|
-
assert current_area.issink is True
|
|
753
|
+
if current_area.issink is True:
|
|
760
754
|
# Standby departure
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
755
|
+
if start_time in dict_of_events:
|
|
756
|
+
# Actual start time should be the end time of the other positive duration
|
|
757
|
+
# process starting at the same time
|
|
758
|
+
actual_start_time = dict_of_events[
|
|
759
|
+
start_time
|
|
760
|
+
]["end"]
|
|
761
|
+
else:
|
|
762
|
+
for other_process in process_log:
|
|
763
|
+
if (
|
|
764
|
+
other_process.dur > 0
|
|
765
|
+
and len(other_process.ends) != 0
|
|
766
|
+
):
|
|
767
|
+
actual_start_time = (
|
|
768
|
+
other_process.ends[0]
|
|
769
|
+
)
|
|
770
|
+
else:
|
|
771
|
+
# Invalid standby before a process in progress will be ignored
|
|
772
|
+
continue
|
|
773
|
+
|
|
774
|
+
last_standby_departure_start = actual_start_time
|
|
775
|
+
|
|
776
|
+
# If this standby event lasts actually 0 seconds, it is not a real event
|
|
777
|
+
if (
|
|
778
|
+
actual_start_time in dict_of_events.keys()
|
|
779
|
+
and dict_of_events[actual_start_time][
|
|
780
|
+
"type"
|
|
781
|
+
]
|
|
782
|
+
== "trip"
|
|
783
|
+
):
|
|
784
|
+
continue
|
|
764
785
|
dict_of_events[actual_start_time] = {
|
|
765
786
|
"type": type(process).__name__,
|
|
766
787
|
"area": current_area.ID,
|
|
@@ -769,11 +790,12 @@ def add_evaluation_to_database(
|
|
|
769
790
|
"id": process.ID,
|
|
770
791
|
}
|
|
771
792
|
|
|
772
|
-
last_standby_departure_start = actual_start_time
|
|
773
|
-
|
|
774
793
|
else:
|
|
775
794
|
# Standby arrival
|
|
776
|
-
assert current_area.issink is False
|
|
795
|
+
assert current_area.issink is False, (
|
|
796
|
+
f"A bus cannot go from Area {current_area.ID} to other areas. A Parking Area"
|
|
797
|
+
f" for standby arrival should be added."
|
|
798
|
+
)
|
|
777
799
|
dict_of_events[start_time] = {
|
|
778
800
|
"type": type(process).__name__,
|
|
779
801
|
"area": current_area.ID,
|
|
@@ -782,9 +804,30 @@ def add_evaluation_to_database(
|
|
|
782
804
|
"id": process.ID,
|
|
783
805
|
}
|
|
784
806
|
case ProcessStatus.IN_PROGRESS:
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
)
|
|
807
|
+
assert (
|
|
808
|
+
len(process.starts) == 1 and len(process.ends) == 0
|
|
809
|
+
), f"Current process {process.ID} is marked IN_PROGRESS, but has an end."
|
|
810
|
+
current_area = area_log[start_time]
|
|
811
|
+
current_slot = slot_log[start_time]
|
|
812
|
+
|
|
813
|
+
if current_area is None or current_slot is None:
|
|
814
|
+
raise ValueError(
|
|
815
|
+
f"For process {process.ID} Area and slot should not be None."
|
|
816
|
+
)
|
|
817
|
+
|
|
818
|
+
if process.dur > 0:
|
|
819
|
+
# Valid duration
|
|
820
|
+
dict_of_events[start_time] = {
|
|
821
|
+
"type": type(process).__name__,
|
|
822
|
+
"end": process.etc,
|
|
823
|
+
"area": current_area.ID,
|
|
824
|
+
"slot": current_slot,
|
|
825
|
+
"id": process.ID,
|
|
826
|
+
}
|
|
827
|
+
else:
|
|
828
|
+
raise NotImplementedError(
|
|
829
|
+
"We believe this should never happen. If it happens, handle it here."
|
|
830
|
+
)
|
|
788
831
|
case ProcessStatus.WAITING:
|
|
789
832
|
raise NotImplementedError(
|
|
790
833
|
f"Current process {process.ID} is waiting. Not implemented yet."
|
|
@@ -137,7 +137,7 @@ def depot_to_template(depot: Depot) -> Dict[str, str | Dict[str, str | int]]:
|
|
|
137
137
|
"ismandatory": True,
|
|
138
138
|
"vehicle_filter": {},
|
|
139
139
|
# True if this process can be interrupted by a dispatch. False if it cannot be interrupted
|
|
140
|
-
"cancellable_for_dispatch":
|
|
140
|
+
"cancellable_for_dispatch": process.dispatchable,
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
match process_type(process):
|
eflips/depot/depot.py
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
"""
|
|
3
|
-
Created on Fri Oct 13 11:12:00 2017
|
|
3
|
+
Created on Fri Oct 13 11:12:00 2017.
|
|
4
4
|
|
|
5
5
|
@author: P.Mundt, E.Lauth
|
|
6
6
|
|
|
7
7
|
Core components of the depot simulation model.
|
|
8
|
-
|
|
9
8
|
"""
|
|
10
9
|
|
|
11
10
|
from collections import Counter
|
|
@@ -43,6 +42,7 @@ from warnings import warn
|
|
|
43
42
|
|
|
44
43
|
class DepotWorkingData:
|
|
45
44
|
"""Data container for communication between vehicle and depot.
|
|
45
|
+
|
|
46
46
|
Attribute of a SimpleVehicle object.
|
|
47
47
|
|
|
48
48
|
Parameters:
|
|
@@ -87,7 +87,9 @@ class DepotWorkingData:
|
|
|
87
87
|
|
|
88
88
|
@property
|
|
89
89
|
def current_slot(self):
|
|
90
|
-
"""Return slot number [int] if on an area, else None.
|
|
90
|
+
"""Return slot number [int] if on an area, else None.
|
|
91
|
+
|
|
92
|
+
The lowest slot
|
|
91
93
|
number is 0. Note that slot_no is not the list index.
|
|
92
94
|
"""
|
|
93
95
|
if self.current_area is not None:
|
|
@@ -97,7 +99,8 @@ class DepotWorkingData:
|
|
|
97
99
|
|
|
98
100
|
@property
|
|
99
101
|
def current_charging_interface(self):
|
|
100
|
-
"""Return [ChargingInterface] object of current slot on current area
|
|
102
|
+
"""Return [ChargingInterface] object of current slot on current area,.
|
|
103
|
+
|
|
101
104
|
if any, else None.
|
|
102
105
|
"""
|
|
103
106
|
if self.current_area is not None and self.current_area.charging_interfaces:
|
|
@@ -108,7 +111,8 @@ class DepotWorkingData:
|
|
|
108
111
|
|
|
109
112
|
@property
|
|
110
113
|
def etc_processes(self):
|
|
111
|
-
"""Return simulation time estimate [int or EstimateValue] of when all
|
|
114
|
+
"""Return simulation time estimate [int or EstimateValue] of when all.
|
|
115
|
+
|
|
112
116
|
currently active processes will be completed.
|
|
113
117
|
"""
|
|
114
118
|
etcs = [process.etc for process in self.active_processes]
|
|
@@ -134,7 +138,8 @@ class DepotWorkingData:
|
|
|
134
138
|
|
|
135
139
|
@property
|
|
136
140
|
def etc_processes_uncertain(self):
|
|
137
|
-
"""Return estimate [int or EstimateValue] of when all currently active
|
|
141
|
+
"""Return estimate [int or EstimateValue] of when all currently active.
|
|
142
|
+
|
|
138
143
|
processes will be completed. Unlike self.etc_processes, get a rough
|
|
139
144
|
estimate if an etc value is EstimateValue.UNKNOWN.
|
|
140
145
|
"""
|
|
@@ -203,7 +208,8 @@ class BackgroundStoreGet(FilterStoreExtGet):
|
|
|
203
208
|
|
|
204
209
|
|
|
205
210
|
class BackgroundStore(FilterStoreExt):
|
|
206
|
-
"""Virtual area for background tasks that require functionalities such as
|
|
211
|
+
"""Virtual area for background tasks that require functionalities such as.
|
|
212
|
+
|
|
207
213
|
logging.
|
|
208
214
|
"""
|
|
209
215
|
|
|
@@ -216,7 +222,9 @@ class BackgroundStore(FilterStoreExt):
|
|
|
216
222
|
get = BoundClass(BackgroundStoreGet)
|
|
217
223
|
|
|
218
224
|
def select(self, ID, print_missing=True):
|
|
219
|
-
"""Return the item with *ID* in self.items.
|
|
225
|
+
"""Return the item with *ID* in self.items.
|
|
226
|
+
|
|
227
|
+
Return None if the ID is
|
|
220
228
|
not found.
|
|
221
229
|
Relies on *ID* to be unique.
|
|
222
230
|
|
|
@@ -252,7 +260,9 @@ class UnassignedTrips(SortedList):
|
|
|
252
260
|
|
|
253
261
|
|
|
254
262
|
class Depot:
|
|
255
|
-
"""Representation of a depot.
|
|
263
|
+
"""Representation of a depot.
|
|
264
|
+
|
|
265
|
+
Owns all related objects such as areas
|
|
256
266
|
and the DepotControl as attributes.
|
|
257
267
|
A depot must be created and configured using a DepotConfigurator and may
|
|
258
268
|
be empty upon instantation. Before simulation start, the validity of the
|
|
@@ -377,14 +387,16 @@ class Depot:
|
|
|
377
387
|
|
|
378
388
|
@property
|
|
379
389
|
def vacant_accessible(self):
|
|
380
|
-
"""Return the sum of slots that are unoccupied and accessible
|
|
390
|
+
"""Return the sum of slots that are unoccupied and accessible.
|
|
391
|
+
|
|
381
392
|
(not blocked) from the default entrance side on all areas.
|
|
382
393
|
"""
|
|
383
394
|
return sum(area.vacant_accessible for area in self.list_areas)
|
|
384
395
|
|
|
385
396
|
@property
|
|
386
397
|
def count(self):
|
|
387
|
-
"""Return the amount of vehicles in the depot, i.e. the amount of
|
|
398
|
+
"""Return the amount of vehicles in the depot, i.e. the amount of.
|
|
399
|
+
|
|
388
400
|
occupied slots.
|
|
389
401
|
"""
|
|
390
402
|
return self._count
|
|
@@ -397,14 +409,16 @@ class Depot:
|
|
|
397
409
|
|
|
398
410
|
@property
|
|
399
411
|
def max_count(self):
|
|
400
|
-
"""Return the maximum number of vehicles that were in the depot at the
|
|
412
|
+
"""Return the maximum number of vehicles that were in the depot at the.
|
|
413
|
+
|
|
401
414
|
same time.
|
|
402
415
|
"""
|
|
403
416
|
return self._max_count
|
|
404
417
|
|
|
405
418
|
@property
|
|
406
419
|
def maxOccupiedSlots(self):
|
|
407
|
-
"""Return the total number of slots that have been occupied up to this
|
|
420
|
+
"""Return the total number of slots that have been occupied up to this.
|
|
421
|
+
|
|
408
422
|
point of time. Only functional if the simulation is run with the GUI.
|
|
409
423
|
"""
|
|
410
424
|
totalUsedSlots = 0
|
|
@@ -414,7 +428,8 @@ class Depot:
|
|
|
414
428
|
|
|
415
429
|
@property
|
|
416
430
|
def urgent_trips(self):
|
|
417
|
-
"""Return a list of trips that are urgent: due or delayed and no
|
|
431
|
+
"""Return a list of trips that are urgent: due or delayed and no.
|
|
432
|
+
|
|
418
433
|
vehicle assigned to.
|
|
419
434
|
Relies on self.unassigned_trips to be sorted by std.
|
|
420
435
|
"""
|
|
@@ -430,7 +445,8 @@ class Depot:
|
|
|
430
445
|
|
|
431
446
|
@property
|
|
432
447
|
def overdue_trips(self):
|
|
433
|
-
"""Return a list of trips that are overdue, i.e. have not started yet
|
|
448
|
+
"""Return a list of trips that are overdue, i.e. have not started yet.
|
|
449
|
+
|
|
434
450
|
although the target departure time has passed. Includes trips that have
|
|
435
451
|
a scheduled vehicle.
|
|
436
452
|
"""
|
|
@@ -442,12 +458,14 @@ class Depot:
|
|
|
442
458
|
|
|
443
459
|
def checkin(self, vehicle):
|
|
444
460
|
"""Redirect to checkin() of the depot's depot_control.
|
|
461
|
+
|
|
445
462
|
Don't add code here.
|
|
446
463
|
"""
|
|
447
464
|
self.depot_control.checkin(vehicle)
|
|
448
465
|
|
|
449
466
|
def checkout(self, vehicle):
|
|
450
467
|
"""Redirect to checkout() of the depot's depot_control.
|
|
468
|
+
|
|
451
469
|
Don't add code here.
|
|
452
470
|
"""
|
|
453
471
|
self.depot_control.checkout(vehicle)
|
|
@@ -477,7 +495,9 @@ class BaseDispatchStrategy(ABC):
|
|
|
477
495
|
|
|
478
496
|
@staticmethod
|
|
479
497
|
def next_trips(depot):
|
|
480
|
-
"""Return depot.urgent_trips, if not empty.
|
|
498
|
+
"""Return depot.urgent_trips, if not empty.
|
|
499
|
+
|
|
500
|
+
Else return a list
|
|
481
501
|
containing only the unassigned trip with the lowest scheduled time of
|
|
482
502
|
departure (std), if any. Otherwise return an empty list.
|
|
483
503
|
Relies on depot.unassigned_trips to be sorted by std.
|
|
@@ -636,7 +656,8 @@ class BaseDispatchStrategy(ABC):
|
|
|
636
656
|
|
|
637
657
|
@staticmethod
|
|
638
658
|
def trigger_until_found(depot, trip, interval=60):
|
|
639
|
-
"""Periodically trigger the *depot*'s dispatch until a vehicle is found
|
|
659
|
+
"""Periodically trigger the *depot*'s dispatch until a vehicle is found.
|
|
660
|
+
|
|
640
661
|
for *trip*. This triggering should be used for special cases only and
|
|
641
662
|
*interval* [s] should be set with care due to a potentially high
|
|
642
663
|
impact on sim time.
|
|
@@ -666,6 +687,7 @@ class DSFirst(BaseDispatchStrategy):
|
|
|
666
687
|
@staticmethod
|
|
667
688
|
def get_pending_vehicles(depot, trip):
|
|
668
689
|
"""Return a dict of pending vehicles.
|
|
690
|
+
|
|
669
691
|
A vehicle is pending if it's at an area in a parking area group and has
|
|
670
692
|
no trip assigned to, or its assigned trip's std is later than
|
|
671
693
|
*trip*.std.
|
|
@@ -708,6 +730,7 @@ class DSFirst(BaseDispatchStrategy):
|
|
|
708
730
|
@staticmethod
|
|
709
731
|
def find_match(depot):
|
|
710
732
|
"""Find a matching vehicle for the next trip.
|
|
733
|
+
|
|
711
734
|
Is called recursively while DSFirst.try_assign is successful.
|
|
712
735
|
"""
|
|
713
736
|
|
|
@@ -762,7 +785,9 @@ class DSFirst(BaseDispatchStrategy):
|
|
|
762
785
|
|
|
763
786
|
@staticmethod
|
|
764
787
|
def try_assign(vehicle, trip, depot):
|
|
765
|
-
"""Try to match *vehicle* and *trip*.
|
|
788
|
+
"""Try to match *vehicle* and *trip*.
|
|
789
|
+
|
|
790
|
+
Return True if successful, else
|
|
766
791
|
False.
|
|
767
792
|
"""
|
|
768
793
|
urgent = trip.vehicle is None and trip.std <= trip.env.now
|
|
@@ -833,6 +858,7 @@ class DSSmart(BaseDispatchStrategy):
|
|
|
833
858
|
@staticmethod
|
|
834
859
|
def get_suitable_vehicles(depot, trip, vf):
|
|
835
860
|
"""Return a list of vehicles that are suitable for *trip*.
|
|
861
|
+
|
|
836
862
|
A vehicle is suitable if it's at an area in a parking area group and
|
|
837
863
|
has no trip assigned to, or its assigned trip's std is later than
|
|
838
864
|
*trip*.std. Furthermore, a vehicle must pass all criteria of *vf*. For
|
|
@@ -882,6 +908,7 @@ class DSSmart(BaseDispatchStrategy):
|
|
|
882
908
|
@staticmethod
|
|
883
909
|
def find_match(depot):
|
|
884
910
|
"""Find a matching vehicle for the next trip.
|
|
911
|
+
|
|
885
912
|
Is called recursively while successful.
|
|
886
913
|
"""
|
|
887
914
|
next_trips = DSSmart.next_trips(depot)
|
|
@@ -963,7 +990,8 @@ class DSSmart(BaseDispatchStrategy):
|
|
|
963
990
|
|
|
964
991
|
@staticmethod
|
|
965
992
|
def scheduling_delay(env, trip):
|
|
966
|
-
"""Return the interval [int] from now until *lead_time_match* before
|
|
993
|
+
"""Return the interval [int] from now until *lead_time_match* before.
|
|
994
|
+
|
|
967
995
|
departure time of *trip*. Return 0 if the departure time is in less
|
|
968
996
|
than *lead_time_match*.
|
|
969
997
|
"""
|
|
@@ -977,6 +1005,7 @@ class DSSmart(BaseDispatchStrategy):
|
|
|
977
1005
|
|
|
978
1006
|
class DepotControl:
|
|
979
1007
|
"""Control of vehicle movement and actions in the depot.
|
|
1008
|
+
|
|
980
1009
|
Instantiated as attribute of the corresponding depot.
|
|
981
1010
|
|
|
982
1011
|
Attributes:
|
|
@@ -990,8 +1019,10 @@ class DepotControl:
|
|
|
990
1019
|
dispatch_strategies = {DSFirst.name: DSFirst, DSSmart.name: DSSmart}
|
|
991
1020
|
|
|
992
1021
|
parking_congestion_event_cls = None
|
|
993
|
-
"""Event that succeeds if parking congestion occurs because no slot can be
|
|
994
|
-
|
|
1022
|
+
"""Event that succeeds if parking congestion occurs because no slot can be.
|
|
1023
|
+
|
|
1024
|
+
found at a parking area upon request.
|
|
1025
|
+
"""
|
|
995
1026
|
|
|
996
1027
|
def __init__(self, env, depot, dispatch_strategy_name="FIRST"):
|
|
997
1028
|
self.depot = depot
|
|
@@ -1004,7 +1035,8 @@ class DepotControl:
|
|
|
1004
1035
|
self.departure_areas = AreaGroup(self.env, [], "departure_areas")
|
|
1005
1036
|
|
|
1006
1037
|
def _complete(self):
|
|
1007
|
-
"""Actions that must take place before simulation start, but may not be
|
|
1038
|
+
"""Actions that must take place before simulation start, but may not be.
|
|
1039
|
+
|
|
1008
1040
|
possible during the depot configuration phase.
|
|
1009
1041
|
"""
|
|
1010
1042
|
# Set self.process_request based on prioritize_init_store option. Not
|
|
@@ -1017,7 +1049,7 @@ class DepotControl:
|
|
|
1017
1049
|
|
|
1018
1050
|
@property
|
|
1019
1051
|
def dispatch_strategy_name(self):
|
|
1020
|
-
"""'name' attribute of set dispatch strategy"""
|
|
1052
|
+
"""'name' attribute of set dispatch strategy."""
|
|
1021
1053
|
return self._dispatch_strategy_name
|
|
1022
1054
|
|
|
1023
1055
|
@dispatch_strategy_name.setter
|
|
@@ -1030,7 +1062,7 @@ class DepotControl:
|
|
|
1030
1062
|
|
|
1031
1063
|
@property
|
|
1032
1064
|
def dispatch_strategy(self):
|
|
1033
|
-
"""[BaseDispatchStrategy] subclass"""
|
|
1065
|
+
"""[BaseDispatchStrategy] subclass."""
|
|
1034
1066
|
return self._dispatch_strategy
|
|
1035
1067
|
|
|
1036
1068
|
@dispatch_strategy.setter
|
|
@@ -1042,7 +1074,8 @@ class DepotControl:
|
|
|
1042
1074
|
)
|
|
1043
1075
|
|
|
1044
1076
|
def checkin(self, vehicle):
|
|
1045
|
-
"""Reset and set several variables upon arrival of a vehicle and
|
|
1077
|
+
"""Reset and set several variables upon arrival of a vehicle and.
|
|
1078
|
+
|
|
1046
1079
|
trigger follow-up actions.
|
|
1047
1080
|
"""
|
|
1048
1081
|
# if vehicle.trip_at_departure is not vehicle.trip:
|
|
@@ -1088,7 +1121,9 @@ class DepotControl:
|
|
|
1088
1121
|
vehicle.dwd.plan = self.depot.default_plan.copy()
|
|
1089
1122
|
|
|
1090
1123
|
def proceed(self, vehicle):
|
|
1091
|
-
"""Main function to move a vehicle to the next area.
|
|
1124
|
+
"""Main function to move a vehicle to the next area.
|
|
1125
|
+
|
|
1126
|
+
Splits into
|
|
1092
1127
|
proceed_area and proceed_group for further actions.
|
|
1093
1128
|
"""
|
|
1094
1129
|
# an = vehicle.dwd.current_area.ID if vehicle.dwd.current_area is not None else None
|
|
@@ -1124,8 +1159,9 @@ class DepotControl:
|
|
|
1124
1159
|
current_area.trigger_get(None)
|
|
1125
1160
|
self.trigger_dispatch()
|
|
1126
1161
|
|
|
1127
|
-
|
|
1128
|
-
|
|
1162
|
+
# This was removed cause it triggered an Exception after fixing #90
|
|
1163
|
+
# if globalConstants["general"]["LOG_ATTRIBUTES"]:
|
|
1164
|
+
# current_area.logger.steplog()
|
|
1129
1165
|
|
|
1130
1166
|
def proceed_area(self, vehicle, current_area, next_area):
|
|
1131
1167
|
"""Proceed with *next_area* as a BaseArea subtype."""
|
|
@@ -1295,6 +1331,7 @@ class DepotControl:
|
|
|
1295
1331
|
|
|
1296
1332
|
def get_process_need(self, vehicle, area):
|
|
1297
1333
|
"""Check what processes a vehicle will request at an area.
|
|
1334
|
+
|
|
1298
1335
|
Return a list of processes by ID to request, which can be empty.
|
|
1299
1336
|
"""
|
|
1300
1337
|
flexprint(
|
|
@@ -1320,7 +1357,8 @@ class DepotControl:
|
|
|
1320
1357
|
return process_IDs
|
|
1321
1358
|
|
|
1322
1359
|
def run_processes(self, vehicle, process_IDs):
|
|
1323
|
-
"""Initialize and run processes marked in process_IDs for a vehicle at
|
|
1360
|
+
"""Initialize and run processes marked in process_IDs for a vehicle at.
|
|
1361
|
+
|
|
1324
1362
|
an area.
|
|
1325
1363
|
If more than one process is requested, then all processes will be
|
|
1326
1364
|
started simultaneously. Call proceed() when all processes have
|
|
@@ -1437,7 +1475,8 @@ class DepotControl:
|
|
|
1437
1475
|
self.trigger_dispatch()
|
|
1438
1476
|
|
|
1439
1477
|
def trigger_dispatch(self):
|
|
1440
|
-
"""Trigger the matching of trips and vehicles of
|
|
1478
|
+
"""Trigger the matching of trips and vehicles of.
|
|
1479
|
+
|
|
1441
1480
|
self.dispatch_strategy.
|
|
1442
1481
|
"""
|
|
1443
1482
|
self.dispatch_strategy.trigger(self.depot)
|
|
@@ -1452,8 +1491,10 @@ class DepotControl:
|
|
|
1452
1491
|
self.env.process(self.process_request(trip, filter=filter))
|
|
1453
1492
|
|
|
1454
1493
|
def register_for_dispatch(self, trip):
|
|
1455
|
-
"""Prepare processing the vehicle request for *trip* by the regular
|
|
1456
|
-
|
|
1494
|
+
"""Prepare processing the vehicle request for *trip* by the regular.
|
|
1495
|
+
|
|
1496
|
+
dispatch.
|
|
1497
|
+
"""
|
|
1457
1498
|
self.depot.pending_departures.append(trip)
|
|
1458
1499
|
self.env.process(self.schedule_for_matching(trip))
|
|
1459
1500
|
|
|
@@ -1473,6 +1514,7 @@ class DepotControl:
|
|
|
1473
1514
|
|
|
1474
1515
|
def process_request_prio_parking(self, trip, filter=lambda item: True):
|
|
1475
1516
|
"""Return a vehicle from the depot for which *filter* returns True.
|
|
1517
|
+
|
|
1476
1518
|
If there is no matching vehicle available immediately at
|
|
1477
1519
|
self.departure_areas, try to get a vehicle from depot.init_store.
|
|
1478
1520
|
If there is no matching vehicle either, issue requests at
|
|
@@ -1546,6 +1588,7 @@ class DepotControl:
|
|
|
1546
1588
|
|
|
1547
1589
|
def process_request_prio_init(self, trip, filter=lambda item: True):
|
|
1548
1590
|
"""Return a vehicle from the depot for which *filter* returns True.
|
|
1591
|
+
|
|
1549
1592
|
Try to get a vehicle from depot.init_store first. If there is no
|
|
1550
1593
|
match available, issue requests at self.departure_areas.
|
|
1551
1594
|
"""
|
|
@@ -1597,6 +1640,7 @@ def assert_after_checkout(env, vehicle):
|
|
|
1597
1640
|
|
|
1598
1641
|
def update_relations(put_event):
|
|
1599
1642
|
"""Updater to be called as callback of a successful put event for an area.
|
|
1643
|
+
|
|
1600
1644
|
Relies on a corresponding get event to succeed in the same simulation
|
|
1601
1645
|
time step.
|
|
1602
1646
|
"""
|
|
@@ -1612,7 +1656,9 @@ def update_relations(put_event):
|
|
|
1612
1656
|
|
|
1613
1657
|
|
|
1614
1658
|
class BaseArea(ABC):
|
|
1615
|
-
"""Abstract base class for an area in a depot.
|
|
1659
|
+
"""Abstract base class for an area in a depot.
|
|
1660
|
+
|
|
1661
|
+
Subclasses must inherit from
|
|
1616
1662
|
a SimPy Store subclass that has attributes 'capacity', 'items' and
|
|
1617
1663
|
'vacant'.
|
|
1618
1664
|
|
|
@@ -1647,7 +1693,6 @@ class BaseArea(ABC):
|
|
|
1647
1693
|
slot_orientation: [str] orientation of slots. Used for display purposes
|
|
1648
1694
|
in the GUI only, has no functional purpose. See
|
|
1649
1695
|
gui.area_view.DepotAreaView for documentation.
|
|
1650
|
-
|
|
1651
1696
|
"""
|
|
1652
1697
|
|
|
1653
1698
|
@abstractmethod
|
|
@@ -1700,7 +1745,8 @@ class BaseArea(ABC):
|
|
|
1700
1745
|
|
|
1701
1746
|
@property
|
|
1702
1747
|
def vacant_accessible(self):
|
|
1703
|
-
"""Helper function to distinguish between direct and line areas for the
|
|
1748
|
+
"""Helper function to distinguish between direct and line areas for the.
|
|
1749
|
+
|
|
1704
1750
|
determination of vacant accessible slots.
|
|
1705
1751
|
"""
|
|
1706
1752
|
if isinstance(self, LineArea):
|
|
@@ -1714,7 +1760,8 @@ class BaseArea(ABC):
|
|
|
1714
1760
|
|
|
1715
1761
|
@property
|
|
1716
1762
|
def charge_proc(self):
|
|
1717
|
-
"""Return the type of the Charge or subclass process available at this
|
|
1763
|
+
"""Return the type of the Charge or subclass process available at this.
|
|
1764
|
+
|
|
1718
1765
|
area. Return None if there is None. Relies on the assumption that there
|
|
1719
1766
|
is only one charging process at an area at max.
|
|
1720
1767
|
"""
|
|
@@ -1737,14 +1784,13 @@ class BaseArea(ABC):
|
|
|
1737
1784
|
|
|
1738
1785
|
@property
|
|
1739
1786
|
def scheduledVehicles(self):
|
|
1740
|
-
"""
|
|
1741
|
-
Returns list of scheduled vehicles (trips assigned).
|
|
1742
|
-
"""
|
|
1787
|
+
"""Returns list of scheduled vehicles (trips assigned)."""
|
|
1743
1788
|
return [vehicle for vehicle in self.vehicle if vehicle.trip is not None]
|
|
1744
1789
|
|
|
1745
1790
|
@property
|
|
1746
1791
|
def count_rfd(self):
|
|
1747
|
-
"""Return the number of vehicles that are ready for departure at this
|
|
1792
|
+
"""Return the number of vehicles that are ready for departure at this.
|
|
1793
|
+
|
|
1748
1794
|
area.
|
|
1749
1795
|
"""
|
|
1750
1796
|
return sum(item.dwd.isrfd for item in self.vehicles)
|
|
@@ -1752,12 +1798,14 @@ class BaseArea(ABC):
|
|
|
1752
1798
|
@property
|
|
1753
1799
|
@abstractmethod
|
|
1754
1800
|
def count_rfd_unblocked(self):
|
|
1755
|
-
"""Return the number of vehicles that are ready for departure and not
|
|
1801
|
+
"""Return the number of vehicles that are ready for departure and not.
|
|
1802
|
+
|
|
1756
1803
|
blocked from departure at this area.
|
|
1757
1804
|
"""
|
|
1758
1805
|
|
|
1759
1806
|
def istypestack(self, substitution=True):
|
|
1760
|
-
"""Return a tuple (istypestack [bool or None], vehicle_type
|
|
1807
|
+
"""Return a tuple (istypestack [bool or None], vehicle_type.
|
|
1808
|
+
|
|
1761
1809
|
[VehicleType or None]).
|
|
1762
1810
|
|
|
1763
1811
|
istypestack is True if all vehicles are of the same type. vehicle_type
|
|
@@ -1779,7 +1827,8 @@ class BaseArea(ABC):
|
|
|
1779
1827
|
return None, None
|
|
1780
1828
|
|
|
1781
1829
|
def istypestack_with(self, vehicle, substitution=True):
|
|
1782
|
-
"""Return True if current vehicles would be a typestack together with
|
|
1830
|
+
"""Return True if current vehicles would be a typestack together with.
|
|
1831
|
+
|
|
1783
1832
|
*vehicle*. Extenuate the comparison with subsitutable types if
|
|
1784
1833
|
*substitution* is True. Return None if the area is empty. Otherwise
|
|
1785
1834
|
return False.
|
|
@@ -1795,7 +1844,9 @@ class BaseArea(ABC):
|
|
|
1795
1844
|
return all(vi.vehicle_type is vehicle.vehicle_type for vi in self.vehicles)
|
|
1796
1845
|
|
|
1797
1846
|
def select(self, ID, print_missing=True):
|
|
1798
|
-
"""Return the item with *ID* in self.items.
|
|
1847
|
+
"""Return the item with *ID* in self.items.
|
|
1848
|
+
|
|
1849
|
+
Return None if the ID is
|
|
1799
1850
|
not found.
|
|
1800
1851
|
Relies on *ID* to be unique.
|
|
1801
1852
|
|
|
@@ -1840,7 +1891,8 @@ class BaseAreaPut(ABC):
|
|
|
1840
1891
|
if globalConstants["general"]["LOG_ATTRIBUTES"]:
|
|
1841
1892
|
|
|
1842
1893
|
def calc_waiting_time(request):
|
|
1843
|
-
"""Log the waiting time from init until success of *request
|
|
1894
|
+
"""Log the waiting time from init until success of *request*.
|
|
1895
|
+
|
|
1844
1896
|
in the vehicle's data logger.
|
|
1845
1897
|
"""
|
|
1846
1898
|
waiting_time = request.item.env.now - request.init_time
|
|
@@ -1893,11 +1945,12 @@ class BaseAreaGet(ABC):
|
|
|
1893
1945
|
|
|
1894
1946
|
|
|
1895
1947
|
class DirectAreaPut(BaseAreaPut, ExclusiveRequest, StorePutExt):
|
|
1896
|
-
"""Request to put *item* into the *store*.
|
|
1948
|
+
"""Request to put *item* into the *store*.
|
|
1949
|
+
|
|
1950
|
+
The request is triggered once
|
|
1897
1951
|
there is space for the item in the store.
|
|
1898
1952
|
|
|
1899
1953
|
Callbacks by BaseAreaPut are added last.
|
|
1900
|
-
|
|
1901
1954
|
"""
|
|
1902
1955
|
|
|
1903
1956
|
def __init__(self, store, item, other_requests=None):
|
|
@@ -1907,7 +1960,9 @@ class DirectAreaPut(BaseAreaPut, ExclusiveRequest, StorePutExt):
|
|
|
1907
1960
|
|
|
1908
1961
|
|
|
1909
1962
|
class DirectAreaGet(BaseAreaGet, ExclusiveRequest, FilterStoreExtGet):
|
|
1910
|
-
"""Request to get an *item* from the *store* matching the *filter*.
|
|
1963
|
+
"""Request to get an *item* from the *store* matching the *filter*.
|
|
1964
|
+
|
|
1965
|
+
The
|
|
1911
1966
|
request is triggered once there is such an item available in the store.
|
|
1912
1967
|
|
|
1913
1968
|
*filter* is a function receiving one item. It should return ``True`` for
|
|
@@ -1916,7 +1971,6 @@ class DirectAreaGet(BaseAreaGet, ExclusiveRequest, FilterStoreExtGet):
|
|
|
1916
1971
|
:class:`StoreGet`.
|
|
1917
1972
|
|
|
1918
1973
|
Callbacks by BaseAreaGet are added last.
|
|
1919
|
-
|
|
1920
1974
|
"""
|
|
1921
1975
|
|
|
1922
1976
|
def __init__(self, store, filter=lambda item: True, other_requests=None, **kwargs):
|
|
@@ -1926,11 +1980,11 @@ class DirectAreaGet(BaseAreaGet, ExclusiveRequest, FilterStoreExtGet):
|
|
|
1926
1980
|
|
|
1927
1981
|
|
|
1928
1982
|
class DirectArea(BaseArea, PositionalFilterStore):
|
|
1929
|
-
"""Depot area where vehicles don't block each other and therefore direct
|
|
1983
|
+
"""Depot area where vehicles don't block each other and therefore direct.
|
|
1984
|
+
|
|
1930
1985
|
access on all stored vehicles is possible.
|
|
1931
1986
|
|
|
1932
1987
|
See parent class descriptions for more details.
|
|
1933
|
-
|
|
1934
1988
|
"""
|
|
1935
1989
|
|
|
1936
1990
|
def __init__(
|
|
@@ -1963,20 +2017,21 @@ class DirectArea(BaseArea, PositionalFilterStore):
|
|
|
1963
2017
|
|
|
1964
2018
|
@property
|
|
1965
2019
|
def pendingVehicles(self):
|
|
1966
|
-
"""
|
|
1967
|
-
Returns list of pending vehicles (potentially blocking vehicles).
|
|
1968
|
-
"""
|
|
2020
|
+
"""Returns list of pending vehicles (potentially blocking vehicles)."""
|
|
1969
2021
|
return []
|
|
1970
2022
|
|
|
1971
2023
|
@property
|
|
1972
2024
|
def count_rfd_unblocked(self):
|
|
1973
|
-
"""Return the number of vehicles that are ready for departure and not
|
|
2025
|
+
"""Return the number of vehicles that are ready for departure and not.
|
|
2026
|
+
|
|
1974
2027
|
blocked from departure at this area.
|
|
1975
2028
|
"""
|
|
1976
2029
|
return sum(vehicle.dwd.isrfd for vehicle in self.vehicles)
|
|
1977
2030
|
|
|
1978
2031
|
def slot_no(self, item):
|
|
1979
|
-
"""Return the slot number of *item*.
|
|
2032
|
+
"""Return the slot number of *item*.
|
|
2033
|
+
|
|
2034
|
+
Return None if item is not in
|
|
1980
2035
|
self.items.
|
|
1981
2036
|
Unlike list indexing, slot number counting starts at 1. Therefore only
|
|
1982
2037
|
meant for display purposes, otherwise use items.index(vehicle).
|
|
@@ -1988,20 +2043,22 @@ class DirectArea(BaseArea, PositionalFilterStore):
|
|
|
1988
2043
|
|
|
1989
2044
|
@staticmethod
|
|
1990
2045
|
def index2slot_no(index):
|
|
1991
|
-
"""Convert *index* (correct value for indexing list self.items) to slot
|
|
1992
|
-
|
|
2046
|
+
"""Convert *index* (correct value for indexing list self.items) to slot.
|
|
2047
|
+
|
|
2048
|
+
number (for display purposes).
|
|
2049
|
+
"""
|
|
1993
2050
|
return index + 1
|
|
1994
2051
|
|
|
1995
2052
|
|
|
1996
2053
|
class LineAreaPut(BaseAreaPut, ExclusiveRequest, LineStorePut):
|
|
1997
|
-
"""Request to put *item* onto deepest accessible slot from *side* in
|
|
2054
|
+
"""Request to put *item* onto deepest accessible slot from *side* in.
|
|
2055
|
+
|
|
1998
2056
|
*store*. The request is triggered once there is accessible space for the
|
|
1999
2057
|
item in the store.
|
|
2000
2058
|
|
|
2001
2059
|
side: [str] Same as in LineStorePut.
|
|
2002
2060
|
|
|
2003
2061
|
Callbacks by BaseAreaPut are added last.
|
|
2004
|
-
|
|
2005
2062
|
"""
|
|
2006
2063
|
|
|
2007
2064
|
def __init__(self, store, item, side="default", other_requests=None):
|
|
@@ -2011,7 +2068,8 @@ class LineAreaPut(BaseAreaPut, ExclusiveRequest, LineStorePut):
|
|
|
2011
2068
|
|
|
2012
2069
|
|
|
2013
2070
|
class LineAreaGet(BaseAreaGet, ExclusiveRequest, LineFilterStoreGet):
|
|
2014
|
-
"""Request to get the first accessible *item* from *side* in *store
|
|
2071
|
+
"""Request to get the first accessible *item* from *side* in *store*.
|
|
2072
|
+
|
|
2015
2073
|
matching *filter*(item). The request is triggered once there is an
|
|
2016
2074
|
accessible item available in the store.
|
|
2017
2075
|
|
|
@@ -2024,7 +2082,6 @@ class LineAreaGet(BaseAreaGet, ExclusiveRequest, LineFilterStoreGet):
|
|
|
2024
2082
|
interface for customization.
|
|
2025
2083
|
|
|
2026
2084
|
Callbacks by BaseAreaGet are added last.
|
|
2027
|
-
|
|
2028
2085
|
"""
|
|
2029
2086
|
|
|
2030
2087
|
def __init__(
|
|
@@ -2036,14 +2093,14 @@ class LineAreaGet(BaseAreaGet, ExclusiveRequest, LineFilterStoreGet):
|
|
|
2036
2093
|
|
|
2037
2094
|
|
|
2038
2095
|
class LineArea(BaseArea, LineFilterStore):
|
|
2039
|
-
"""Depot area where vehicles are arranged in a line and can block each
|
|
2096
|
+
"""Depot area where vehicles are arranged in a line and can block each.
|
|
2097
|
+
|
|
2040
2098
|
other (feature of LineFilterStore). Therefore not all stored vehicles may
|
|
2041
2099
|
be directly accessible.
|
|
2042
2100
|
See parent class descriptions for more details.
|
|
2043
2101
|
|
|
2044
2102
|
At the moment, only areas where issink is True (e.g. typically False for a
|
|
2045
2103
|
service area, True for parking areas) can reliably be of this area type.
|
|
2046
|
-
|
|
2047
2104
|
"""
|
|
2048
2105
|
|
|
2049
2106
|
def __init__(
|
|
@@ -2086,14 +2143,13 @@ class LineArea(BaseArea, LineFilterStore):
|
|
|
2086
2143
|
|
|
2087
2144
|
@property
|
|
2088
2145
|
def pendingVehicles(self):
|
|
2089
|
-
"""
|
|
2090
|
-
Returns list of pending vehicles (potentially blocking vehicles).
|
|
2091
|
-
"""
|
|
2146
|
+
"""Returns list of pending vehicles (potentially blocking vehicles)."""
|
|
2092
2147
|
return [item for item in self.items if item is not None and item.trip is None]
|
|
2093
2148
|
|
|
2094
2149
|
@property
|
|
2095
2150
|
def count_rfd_unblocked(self):
|
|
2096
|
-
"""Return the number of vehicles that are ready for departure and not
|
|
2151
|
+
"""Return the number of vehicles that are ready for departure and not.
|
|
2152
|
+
|
|
2097
2153
|
blocked from departure at this area.
|
|
2098
2154
|
"""
|
|
2099
2155
|
return sum(
|
|
@@ -2101,7 +2157,9 @@ class LineArea(BaseArea, LineFilterStore):
|
|
|
2101
2157
|
)
|
|
2102
2158
|
|
|
2103
2159
|
def slot_no(self, item):
|
|
2104
|
-
"""Return the slot number of *item*.
|
|
2160
|
+
"""Return the slot number of *item*.
|
|
2161
|
+
|
|
2162
|
+
Return None if item is not in
|
|
2105
2163
|
self.items.
|
|
2106
2164
|
The slot closest to the front has the lowest number, which is the
|
|
2107
2165
|
reverse of items.index. Unlike list indexing, slot number counting
|
|
@@ -2114,8 +2172,10 @@ class LineArea(BaseArea, LineFilterStore):
|
|
|
2114
2172
|
return None
|
|
2115
2173
|
|
|
2116
2174
|
def index2slot_no(self, index):
|
|
2117
|
-
"""Convert *index* (correct value for indexing self.items) to slot
|
|
2118
|
-
|
|
2175
|
+
"""Convert *index* (correct value for indexing self.items) to slot.
|
|
2176
|
+
|
|
2177
|
+
number (for display purposes).
|
|
2178
|
+
"""
|
|
2119
2179
|
return self.capacity - index
|
|
2120
2180
|
|
|
2121
2181
|
|
|
@@ -2135,7 +2195,9 @@ class BaseParkingStrategy(ABC):
|
|
|
2135
2195
|
@staticmethod
|
|
2136
2196
|
@abstractmethod
|
|
2137
2197
|
def determine_store(*args, **kwargs):
|
|
2138
|
-
"""Return the most suitable area for parking a vehicle.
|
|
2198
|
+
"""Return the most suitable area for parking a vehicle.
|
|
2199
|
+
|
|
2200
|
+
A put request
|
|
2139
2201
|
to this area must be immediately successful.
|
|
2140
2202
|
"""
|
|
2141
2203
|
|
|
@@ -2215,7 +2277,6 @@ class PSSmart(BaseParkingStrategy):
|
|
|
2215
2277
|
|
|
2216
2278
|
Note: This strategy only makes sense with stores that don't restrict by
|
|
2217
2279
|
vehicle type, since it wont have any effect on typed stores.
|
|
2218
|
-
|
|
2219
2280
|
"""
|
|
2220
2281
|
|
|
2221
2282
|
name = "SMART"
|
|
@@ -2251,7 +2312,8 @@ class PSSmart(BaseParkingStrategy):
|
|
|
2251
2312
|
@staticmethod
|
|
2252
2313
|
def rate_stores(preselected_stores, vehicle):
|
|
2253
2314
|
"""
|
|
2254
|
-
Rate [preselected_stores] by accessibility for [vehicle] and
|
|
2315
|
+
Rate [preselected_stores] by accessibility for [vehicle] and.
|
|
2316
|
+
|
|
2255
2317
|
return a dict (key: result, value: list of stores having this result).
|
|
2256
2318
|
|
|
2257
2319
|
Called by DSFirst.find_match.
|
|
@@ -2271,7 +2333,8 @@ class PSSmart(BaseParkingStrategy):
|
|
|
2271
2333
|
@staticmethod
|
|
2272
2334
|
def rate_store(store, vehicle):
|
|
2273
2335
|
"""
|
|
2274
|
-
Rate [store] by accessibility for [vehicle]: 0 means directly
|
|
2336
|
+
Rate [store] by accessibility for [vehicle]: 0 means directly.
|
|
2337
|
+
|
|
2275
2338
|
accessible, higher values mean lower accessibility (lower => better).
|
|
2276
2339
|
|
|
2277
2340
|
Called by DSFirst.rate_stores.
|
|
@@ -2382,6 +2445,7 @@ class PSSmart2(BaseParkingStrategy):
|
|
|
2382
2445
|
@staticmethod
|
|
2383
2446
|
def log_best(parking_area_group, rating):
|
|
2384
2447
|
"""Add the best value of a rating to parking_area_group.pssmart2_logs.
|
|
2448
|
+
|
|
2385
2449
|
rating: [ParkRating]
|
|
2386
2450
|
"""
|
|
2387
2451
|
if globalConstants["general"]["LOG_ATTRIBUTES"]:
|
|
@@ -2408,7 +2472,6 @@ class AreaGroup(StoreConnector):
|
|
|
2408
2472
|
max_capacity_line: [int] maximum capacity among Line areas in this group
|
|
2409
2473
|
capacity_direct: [int] total cacpacity of Direct areas in this group
|
|
2410
2474
|
capacity_line: [int] total cacpacity of Line areas in this group
|
|
2411
|
-
|
|
2412
2475
|
"""
|
|
2413
2476
|
|
|
2414
2477
|
def __init__(self, env, stores, ID):
|
|
@@ -2423,13 +2486,16 @@ class AreaGroup(StoreConnector):
|
|
|
2423
2486
|
self.ID = ID
|
|
2424
2487
|
|
|
2425
2488
|
def clear(self):
|
|
2426
|
-
"""Remove all entries from self.stores and update.
|
|
2489
|
+
"""Remove all entries from self.stores and update.
|
|
2490
|
+
|
|
2491
|
+
self.depot is
|
|
2427
2492
|
unaffected.
|
|
2428
2493
|
"""
|
|
2429
2494
|
super().clear()
|
|
2430
2495
|
|
|
2431
2496
|
def check_entry_filters(self, vehicle):
|
|
2432
|
-
"""Return a list of booleans that are the results of entry permission
|
|
2497
|
+
"""Return a list of booleans that are the results of entry permission.
|
|
2498
|
+
|
|
2433
2499
|
checks at areas in self.stores. Has the same length as
|
|
2434
2500
|
self.stores.
|
|
2435
2501
|
"""
|
|
@@ -2437,7 +2503,8 @@ class AreaGroup(StoreConnector):
|
|
|
2437
2503
|
return permissions
|
|
2438
2504
|
|
|
2439
2505
|
def update_defaults(self):
|
|
2440
|
-
"""Update attributes after relevant changes such as the amount of areas
|
|
2506
|
+
"""Update attributes after relevant changes such as the amount of areas.
|
|
2507
|
+
|
|
2441
2508
|
in the group.
|
|
2442
2509
|
"""
|
|
2443
2510
|
self.default_selection_put = [True] * len(self.stores)
|
|
@@ -2478,7 +2545,9 @@ class AreaGroup(StoreConnector):
|
|
|
2478
2545
|
|
|
2479
2546
|
|
|
2480
2547
|
class ParkingAreaGroup(AreaGroup):
|
|
2481
|
-
"""Group specifically for parking areas.
|
|
2548
|
+
"""Group specifically for parking areas.
|
|
2549
|
+
|
|
2550
|
+
Provides various algorithms to
|
|
2482
2551
|
decide in which area to put a vehicle.
|
|
2483
2552
|
|
|
2484
2553
|
Parameters:
|
|
@@ -2491,7 +2560,6 @@ class ParkingAreaGroup(AreaGroup):
|
|
|
2491
2560
|
simulation start. None if there are no charging interfaces.
|
|
2492
2561
|
put_queue: [dict] Container for keeping count and time of pending put
|
|
2493
2562
|
requests to the group. Provisional, may be changed.
|
|
2494
|
-
|
|
2495
2563
|
"""
|
|
2496
2564
|
|
|
2497
2565
|
parking_strategies = {
|
|
@@ -2533,7 +2601,7 @@ class ParkingAreaGroup(AreaGroup):
|
|
|
2533
2601
|
|
|
2534
2602
|
@property
|
|
2535
2603
|
def parking_strategy_name(self):
|
|
2536
|
-
"""'name' attribute of set parking strategy"""
|
|
2604
|
+
"""'name' attribute of set parking strategy."""
|
|
2537
2605
|
return self._parking_strategy_name
|
|
2538
2606
|
|
|
2539
2607
|
@parking_strategy_name.setter
|
|
@@ -2545,7 +2613,7 @@ class ParkingAreaGroup(AreaGroup):
|
|
|
2545
2613
|
|
|
2546
2614
|
@property
|
|
2547
2615
|
def parking_strategy(self):
|
|
2548
|
-
"""[BaseParkingStrategy] subclass"""
|
|
2616
|
+
"""[BaseParkingStrategy] subclass."""
|
|
2549
2617
|
return self._parking_strategy
|
|
2550
2618
|
|
|
2551
2619
|
@parking_strategy.setter
|
|
@@ -2576,7 +2644,9 @@ class ParkingAreaGroup(AreaGroup):
|
|
|
2576
2644
|
store.parking_area_group = None
|
|
2577
2645
|
|
|
2578
2646
|
def clear(self):
|
|
2579
|
-
"""Remove all entries from self.stores and update.
|
|
2647
|
+
"""Remove all entries from self.stores and update.
|
|
2648
|
+
|
|
2649
|
+
self.depot,
|
|
2580
2650
|
self.depot.parking_area_groups and self.parking_strategy are
|
|
2581
2651
|
unaffected.
|
|
2582
2652
|
"""
|
|
@@ -2616,7 +2686,9 @@ class ParkingAreaGroup(AreaGroup):
|
|
|
2616
2686
|
max_power = None
|
|
2617
2687
|
|
|
2618
2688
|
def put(self, item, selection=None):
|
|
2619
|
-
"""Summarize put_imm and put_wait.
|
|
2689
|
+
"""Summarize put_imm and put_wait.
|
|
2690
|
+
|
|
2691
|
+
A parking strategy is applied here.
|
|
2620
2692
|
|
|
2621
2693
|
In this method strategies can be determined before calling the actual
|
|
2622
2694
|
put() methods, e.g. by modifying the *selection* parameter.
|
|
@@ -2679,11 +2751,11 @@ class ParkingAreaGroup(AreaGroup):
|
|
|
2679
2751
|
|
|
2680
2752
|
class BaseActivityPlan(ABC):
|
|
2681
2753
|
"""Base class for the guidance of a vehicle inside a depot.
|
|
2754
|
+
|
|
2682
2755
|
A copied instance is assigned to a vehicle at check-in.
|
|
2683
2756
|
|
|
2684
2757
|
Parameters:
|
|
2685
2758
|
locations: [list] of DepotArea or AreaGroup objects
|
|
2686
|
-
|
|
2687
2759
|
"""
|
|
2688
2760
|
|
|
2689
2761
|
def __init__(self, ID, locations=None, vehicle_filter=None):
|
|
@@ -2735,9 +2807,10 @@ class BaseActivityPlan(ABC):
|
|
|
2735
2807
|
|
|
2736
2808
|
|
|
2737
2809
|
class DefaultActivityPlan(BaseActivityPlan):
|
|
2738
|
-
"""Default plan applicable for every vehicle.
|
|
2739
|
-
depot configuration.
|
|
2810
|
+
"""Default plan applicable for every vehicle.
|
|
2740
2811
|
|
|
2812
|
+
One plan is mandatory for a
|
|
2813
|
+
depot configuration.
|
|
2741
2814
|
"""
|
|
2742
2815
|
|
|
2743
2816
|
def __init__(self, locations=None):
|
|
@@ -2749,9 +2822,10 @@ class DefaultActivityPlan(BaseActivityPlan):
|
|
|
2749
2822
|
|
|
2750
2823
|
|
|
2751
2824
|
class SpecificActivityPlan(BaseActivityPlan):
|
|
2752
|
-
"""Plan that is applicable to vehicles passing vehicle_filter only.
|
|
2753
|
-
configuration may include zero or more specific plans.
|
|
2825
|
+
"""Plan that is applicable to vehicles passing vehicle_filter only.
|
|
2754
2826
|
|
|
2827
|
+
A depot
|
|
2828
|
+
configuration may include zero or more specific plans.
|
|
2755
2829
|
"""
|
|
2756
2830
|
|
|
2757
2831
|
def __init__(self, ID, locations=None, vehicle_filter=None):
|
eflips/depot/processes.py
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
-
"""
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
"""
|
|
6
|
-
from enum import auto, Enum
|
|
7
|
-
import simpy
|
|
2
|
+
"""Components for processes in a depot."""
|
|
3
|
+
import math
|
|
4
|
+
import warnings
|
|
8
5
|
from abc import ABC, abstractmethod
|
|
6
|
+
from enum import auto, Enum
|
|
9
7
|
from warnings import warn
|
|
10
|
-
|
|
8
|
+
|
|
9
|
+
import simpy
|
|
11
10
|
from eflips.helperFunctions import flexprint
|
|
11
|
+
from eflips.settings import globalConstants
|
|
12
|
+
|
|
13
|
+
import eflips
|
|
12
14
|
from eflips.depot.evaluation import (
|
|
13
15
|
BatteryLog,
|
|
14
16
|
ChargeStart,
|
|
@@ -16,8 +18,6 @@ from eflips.depot.evaluation import (
|
|
|
16
18
|
ProcessFinished,
|
|
17
19
|
)
|
|
18
20
|
from eflips.depot.filters import VehicleFilter
|
|
19
|
-
import eflips
|
|
20
|
-
import math
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
class ProcessStatus(Enum):
|
|
@@ -31,9 +31,9 @@ class ProcessStatus(Enum):
|
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
class EstimateValue(Enum):
|
|
34
|
-
"""Values for process completion time estimates in addition to an int
|
|
35
|
-
value.
|
|
34
|
+
"""Values for process completion time estimates in addition to an int.
|
|
36
35
|
|
|
36
|
+
value.
|
|
37
37
|
"""
|
|
38
38
|
|
|
39
39
|
UNKNOWN = auto() # process not completed but an estimate is not possible
|
|
@@ -41,7 +41,9 @@ class EstimateValue(Enum):
|
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
class BaseDepotProcess(ABC):
|
|
44
|
-
"""Base class for processes in a depot.
|
|
44
|
+
"""Base class for processes in a depot.
|
|
45
|
+
|
|
46
|
+
Core functionalities are:
|
|
45
47
|
- wrapping the execution of a simpy process and handling of required
|
|
46
48
|
resources,
|
|
47
49
|
- providing the option to resume the process after interruption.
|
|
@@ -87,7 +89,6 @@ class BaseDepotProcess(ABC):
|
|
|
87
89
|
and won't be resumed, resulting in self.__call__ to proceed.
|
|
88
90
|
etc: [int or EstimateValue] estimated simulation time of completion. See
|
|
89
91
|
method self._estimate_time_of_completion for details.
|
|
90
|
-
|
|
91
92
|
"""
|
|
92
93
|
|
|
93
94
|
@abstractmethod
|
|
@@ -141,7 +142,7 @@ class BaseDepotProcess(ABC):
|
|
|
141
142
|
|
|
142
143
|
@property
|
|
143
144
|
def resIDs(self):
|
|
144
|
-
"""Return a list of IDs of self.required_resources"""
|
|
145
|
+
"""Return a list of IDs of self.required_resources."""
|
|
145
146
|
return [res.ID for res in self.required_resources]
|
|
146
147
|
|
|
147
148
|
@property
|
|
@@ -178,7 +179,9 @@ class BaseDepotProcess(ABC):
|
|
|
178
179
|
|
|
179
180
|
@abstractmethod
|
|
180
181
|
def _action(self, *args, **kwargs):
|
|
181
|
-
"""Generator function that characterizes the process.
|
|
182
|
+
"""Generator function that characterizes the process.
|
|
183
|
+
|
|
184
|
+
Subclasses must
|
|
182
185
|
implement this method. It must catch the exception simpy.Interrupt and
|
|
183
186
|
let it pass or implement custom follow-ups. If this method yields a
|
|
184
187
|
timeout, the duration must be set as self.dur before the yield
|
|
@@ -188,7 +191,8 @@ class BaseDepotProcess(ABC):
|
|
|
188
191
|
@staticmethod
|
|
189
192
|
@abstractmethod
|
|
190
193
|
def estimate_duration(*args, **kwargs):
|
|
191
|
-
"""Return an estimate of the process duration in seconds based on given
|
|
194
|
+
"""Return an estimate of the process duration in seconds based on given.
|
|
195
|
+
|
|
192
196
|
parameters as as if it could start immediately. Must be int, not float.
|
|
193
197
|
Possible waiting time for resources are not considered. Therefore the
|
|
194
198
|
estimate is reliable only if the process is started immediately. Static
|
|
@@ -196,8 +200,10 @@ class BaseDepotProcess(ABC):
|
|
|
196
200
|
"""
|
|
197
201
|
|
|
198
202
|
def _pre(self, *args, **kwargs):
|
|
199
|
-
"""Space for actions before calling self._pem that may not be possible
|
|
200
|
-
|
|
203
|
+
"""Space for actions before calling self._pem that may not be possible.
|
|
204
|
+
|
|
205
|
+
upon init.
|
|
206
|
+
"""
|
|
201
207
|
pass
|
|
202
208
|
|
|
203
209
|
def _post(self, *args, **kwargs):
|
|
@@ -206,6 +212,7 @@ class BaseDepotProcess(ABC):
|
|
|
206
212
|
|
|
207
213
|
def __call__(self, *args, **kwargs):
|
|
208
214
|
"""Generator that starts the execution of this process.
|
|
215
|
+
|
|
209
216
|
Note that all parameters required by self._action() have to be passed
|
|
210
217
|
as keyword arguments.
|
|
211
218
|
"""
|
|
@@ -225,7 +232,8 @@ class BaseDepotProcess(ABC):
|
|
|
225
232
|
self._post(*args, **kwargs)
|
|
226
233
|
|
|
227
234
|
def _pem(self, recall=False, *args, **kwargs):
|
|
228
|
-
"""Process execution method (pem) that wraps waiting for required
|
|
235
|
+
"""Process execution method (pem) that wraps waiting for required.
|
|
236
|
+
|
|
229
237
|
resources (if any) and self._action in a single generator. Is called
|
|
230
238
|
again for the remaining duration after interruption if self.resume is
|
|
231
239
|
True.
|
|
@@ -417,7 +425,9 @@ class BaseDepotProcess(ABC):
|
|
|
417
425
|
)
|
|
418
426
|
|
|
419
427
|
def cancel(self):
|
|
420
|
-
"""Immediately stop the current process execution.
|
|
428
|
+
"""Immediately stop the current process execution.
|
|
429
|
+
|
|
430
|
+
No restart is
|
|
421
431
|
scheduled, even if self.resume was set to True before.
|
|
422
432
|
"""
|
|
423
433
|
if hasattr(
|
|
@@ -457,7 +467,9 @@ class BaseDepotProcess(ABC):
|
|
|
457
467
|
self.finished.succeed()
|
|
458
468
|
|
|
459
469
|
def interrupt(self):
|
|
460
|
-
"""Immediately stop the current process execution.
|
|
470
|
+
"""Immediately stop the current process execution.
|
|
471
|
+
|
|
472
|
+
If self.resume is
|
|
461
473
|
True, a restart is scheduled and may be successful immediately if
|
|
462
474
|
required resources are available. If self.resume is False, same effect
|
|
463
475
|
as self.cancel.
|
|
@@ -482,7 +494,6 @@ class VehicleProcess(BaseDepotProcess, ABC):
|
|
|
482
494
|
request_immediately: [bool] if True, a vehicle will request this process to
|
|
483
495
|
start immediately after entering the area this process is available at.
|
|
484
496
|
There might still be waiting time due to resource requirements.
|
|
485
|
-
|
|
486
497
|
"""
|
|
487
498
|
|
|
488
499
|
request_immediately = True
|
|
@@ -644,7 +655,6 @@ class ChargeAbstract(VehicleProcess, ABC):
|
|
|
644
655
|
|
|
645
656
|
Attributes:
|
|
646
657
|
last_update: [int] time of last call to self.update_battery.
|
|
647
|
-
|
|
648
658
|
"""
|
|
649
659
|
|
|
650
660
|
@abstractmethod
|
|
@@ -723,7 +733,9 @@ class ChargeAbstract(VehicleProcess, ABC):
|
|
|
723
733
|
|
|
724
734
|
@classmethod
|
|
725
735
|
def get_chargedata(cls, vehicle):
|
|
726
|
-
"""Get data for charging process of class *cls*.
|
|
736
|
+
"""Get data for charging process of class *cls*.
|
|
737
|
+
|
|
738
|
+
Not robust if there
|
|
727
739
|
are multiple data sets using *cls* with different parameters and
|
|
728
740
|
vehicle filters that can return True for the same vehicle.
|
|
729
741
|
"""
|
|
@@ -738,7 +750,9 @@ class ChargeAbstract(VehicleProcess, ABC):
|
|
|
738
750
|
)
|
|
739
751
|
|
|
740
752
|
def update_battery(self, event_name, amount=None):
|
|
741
|
-
"""Update the energy level of self.vehicle.battery.
|
|
753
|
+
"""Update the energy level of self.vehicle.battery.
|
|
754
|
+
|
|
755
|
+
Can be called
|
|
742
756
|
during the execution of the process to provide an interim update.
|
|
743
757
|
|
|
744
758
|
Parameters:
|
|
@@ -780,7 +794,9 @@ class ChargeAbstract(VehicleProcess, ABC):
|
|
|
780
794
|
|
|
781
795
|
|
|
782
796
|
class Charge(ChargeAbstract):
|
|
783
|
-
"""Process of charging a vehicle's battery.
|
|
797
|
+
"""Process of charging a vehicle's battery.
|
|
798
|
+
|
|
799
|
+
Charging is constant during the
|
|
784
800
|
whole charging process with maximum power provided by the charging
|
|
785
801
|
interface.
|
|
786
802
|
|
|
@@ -788,7 +804,6 @@ class Charge(ChargeAbstract):
|
|
|
788
804
|
soc_target: [int or float or str] the charging process will stop at this
|
|
789
805
|
soc. Must be 0 < soc_target <= 1 or 'soc_max'. For soc_max,
|
|
790
806
|
vehicle.battery.soc_max will be used as soc_target.
|
|
791
|
-
|
|
792
807
|
"""
|
|
793
808
|
|
|
794
809
|
def __init__(
|
|
@@ -891,7 +906,9 @@ class Charge(ChargeAbstract):
|
|
|
891
906
|
|
|
892
907
|
except simpy.Interrupt:
|
|
893
908
|
flexprint("charge interrupted", env=self.env, switch="processes")
|
|
894
|
-
self.
|
|
909
|
+
actual_charging_duration = self.env.now - self.starts[0]
|
|
910
|
+
actual_charged_energy = (effective_power * actual_charging_duration) / 3600
|
|
911
|
+
self.update_battery("charge_interrupt", amount=actual_charged_energy)
|
|
895
912
|
|
|
896
913
|
self.charging_interface.current_power = 0
|
|
897
914
|
self.vehicle.power_logs[self.env.now] = 0
|
|
@@ -900,7 +917,8 @@ class Charge(ChargeAbstract):
|
|
|
900
917
|
|
|
901
918
|
@staticmethod
|
|
902
919
|
def estimate_duration(vehicle, charging_interface, *args, **kwargs):
|
|
903
|
-
"""Return a duration estimate [int] assuming constant charging at
|
|
920
|
+
"""Return a duration estimate [int] assuming constant charging at.
|
|
921
|
+
|
|
904
922
|
maximum power until soc_target is reached.
|
|
905
923
|
"""
|
|
906
924
|
chargedata = Charge.get_chargedata(vehicle)
|
|
@@ -952,7 +970,6 @@ class ChargeSteps(ChargeAbstract):
|
|
|
952
970
|
from the class ChargeAbstract/Charge.
|
|
953
971
|
Note that the power values must not be higher than the maximum power of
|
|
954
972
|
the charging interface.
|
|
955
|
-
|
|
956
973
|
"""
|
|
957
974
|
|
|
958
975
|
def __init__(
|
|
@@ -1169,7 +1186,8 @@ class ChargeSteps(ChargeAbstract):
|
|
|
1169
1186
|
|
|
1170
1187
|
|
|
1171
1188
|
class ChargeEquationSteps(ChargeAbstract):
|
|
1172
|
-
"""Process of charging a vehicle's battery based on a linearized function
|
|
1189
|
+
"""Process of charging a vehicle's battery based on a linearized function.
|
|
1190
|
+
|
|
1173
1191
|
for power.
|
|
1174
1192
|
|
|
1175
1193
|
Parameters:
|
|
@@ -1183,7 +1201,6 @@ class ChargeEquationSteps(ChargeAbstract):
|
|
|
1183
1201
|
significantly increase simulation runtime. Precision is capped to
|
|
1184
1202
|
intervals resulting in step durations >= 1 second.
|
|
1185
1203
|
soc_target: same as for class Charge
|
|
1186
|
-
|
|
1187
1204
|
"""
|
|
1188
1205
|
|
|
1189
1206
|
def __init__(
|
|
@@ -1343,6 +1360,10 @@ class ChargeEquationSteps(ChargeAbstract):
|
|
|
1343
1360
|
except simpy.Interrupt:
|
|
1344
1361
|
flexprint("charge interrupted", env=self.env, switch="processes")
|
|
1345
1362
|
self.update_battery("charge_interrupt")
|
|
1363
|
+
warnings.warn(
|
|
1364
|
+
"It is unclear whether interrupting a charging process of this type returns the desired "
|
|
1365
|
+
"result. Double-check the battery state after the simulation."
|
|
1366
|
+
)
|
|
1346
1367
|
|
|
1347
1368
|
self.charging_interface.current_power = 0
|
|
1348
1369
|
self.vehicle.power_logs[self.env.now] = 0
|
|
@@ -1390,7 +1411,6 @@ def exponential_power(vehicle, charging_interface, peq_params, *args, **kwargs):
|
|
|
1390
1411
|
charging_interface: [DepotChargingInterface]
|
|
1391
1412
|
peq_params: [dict] with parameters for this function in addition to what is
|
|
1392
1413
|
accessible via vehicle and charging_interface
|
|
1393
|
-
|
|
1394
1414
|
"""
|
|
1395
1415
|
SoC = vehicle.battery.soc
|
|
1396
1416
|
P_max = charging_interface.max_power
|
|
@@ -1584,7 +1604,6 @@ class Precondition(VehicleProcess):
|
|
|
1584
1604
|
Attributes:
|
|
1585
1605
|
request_immediately: is False for this class because preconditioning is
|
|
1586
1606
|
scheduled depending on departure time instead of entry time at an area.
|
|
1587
|
-
|
|
1588
1607
|
"""
|
|
1589
1608
|
|
|
1590
1609
|
request_immediately = False
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
+
eflips/depot/.DS_Store,sha256=KzHp46okh461fSINWUgzRhJ8nGo6jsHoENU0EAwl_pQ,6148
|
|
1
2
|
eflips/depot/__init__.py,sha256=n7jte8R6j_Ad4Mp4hkklKwil5r8u8Q_SbXrCC-nf5jM,1556
|
|
2
|
-
eflips/depot/api/__init__.py,sha256=
|
|
3
|
+
eflips/depot/api/__init__.py,sha256=WenYoLrAk94Jc-nD7NOD0AZ_2IR7pjsfkz_dA-Jv644,48272
|
|
3
4
|
eflips/depot/api/defaults/default_settings.json,sha256=0eUDTw_rtLQFvthP8oJL93iRXlmAOravAg-4qqGMQAY,5375
|
|
4
5
|
eflips/depot/api/private/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
-
eflips/depot/api/private/depot.py,sha256=
|
|
6
|
+
eflips/depot/api/private/depot.py,sha256=3VvSAFnLZtsraD8m685zqqi-pwfOci66pc7FRVSjxP8,16594
|
|
6
7
|
eflips/depot/api/private/util.py,sha256=2qVTGeAtAhoq-h5V7Xlli2aDniuoIZYtaCAHD0n1sl8,15261
|
|
7
8
|
eflips/depot/configuration.py,sha256=Op3hlir-dEN7yHr0kTqbYANoCBKFWK6uKOv3NJl8w_w,35678
|
|
8
|
-
eflips/depot/depot.py,sha256=
|
|
9
|
+
eflips/depot/depot.py,sha256=afIlaiX-J-M5-K_oAGMr_soL3_QjIAwrQKDaZzTwle0,105566
|
|
9
10
|
eflips/depot/evaluation.py,sha256=qqXyP4jA1zFcKuWhliQ6n25ZlGl9mJV-vtXf0yu8WN8,140842
|
|
10
11
|
eflips/depot/filters.py,sha256=ZBTiu0UJUp2NYWtvZewfb4Zd4mdmFLkrjIS-ujCgax8,15522
|
|
11
12
|
eflips/depot/input_epex_power_price.py,sha256=VPDC1zy-klQpveGIZ8941hL1P_jeNq3IHoLgFTsANig,5569
|
|
@@ -25,7 +26,7 @@ eflips/depot/layout_opt/settings.py,sha256=EUGCp4dAX22j2uF8sKqbi9a5iP8hb6QpP7t2N
|
|
|
25
26
|
eflips/depot/layout_opt/template_creation.py,sha256=H4LoFjQfbPjTt9rGvapH2tEUWcQ56kPwDucq7t6YahU,9736
|
|
26
27
|
eflips/depot/layout_opt/util.py,sha256=EYh7IN58ZjysmCFdSieQqIQ9goe1a_ZwARRHxOgjEQo,3780
|
|
27
28
|
eflips/depot/plots.py,sha256=85xInZWfJIOVm03onvppgP5yLTgQeMn-1t5aoNdavyY,2509
|
|
28
|
-
eflips/depot/processes.py,sha256=
|
|
29
|
+
eflips/depot/processes.py,sha256=vqwtaK5UfG5aJwsQL2Eo9ZOFg1z5khuNPyEC9-3M83Y,58757
|
|
29
30
|
eflips/depot/rating.py,sha256=vBUr3y31DBcA-LjZW8t6wLfpAsuB8DPkrMThtCtN908,16648
|
|
30
31
|
eflips/depot/resources.py,sha256=0SuzN8qgMmCqa7oUEXVC_XE6pCUtxTsqOfCsaM9Oh3o,13568
|
|
31
32
|
eflips/depot/settings_config.py,sha256=z7CqPdjd8QRlgZj0Zis-H13cL7LOiheRT4ctYCYGguc,2527
|
|
@@ -34,7 +35,7 @@ eflips/depot/simulation.py,sha256=ee0qTzOzG-8ybN36ie_NJallXfC7jUaS9JZvaYFziLs,10
|
|
|
34
35
|
eflips/depot/smart_charging.py,sha256=C3BYqzn2-OYY4ipXm0ETtavbAM9QXZMYULBpVoChf0E,54311
|
|
35
36
|
eflips/depot/standalone.py,sha256=VxcTzBaB67fNJUMmjPRwKXjhqTy6oQ41Coote2LvAmk,22338
|
|
36
37
|
eflips/depot/validation.py,sha256=TIuY7cQtEJI4H2VVMSuY5IIVkacEEZ67weeMuY3NSAM,7097
|
|
37
|
-
eflips_depot-3.0.
|
|
38
|
-
eflips_depot-3.0.
|
|
39
|
-
eflips_depot-3.0.
|
|
40
|
-
eflips_depot-3.0.
|
|
38
|
+
eflips_depot-3.0.2.dist-info/LICENSE.md,sha256=KB4XTk1fPHjtZCYDyPyreu6h1LVJVZXYg-5vePcWZAc,34143
|
|
39
|
+
eflips_depot-3.0.2.dist-info/METADATA,sha256=nbFn4zt1i10t3WkwSc6da-U5krwSbDFGWIOxhd9yKcM,5648
|
|
40
|
+
eflips_depot-3.0.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
41
|
+
eflips_depot-3.0.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|