-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmanager.py
243 lines (210 loc) · 9.21 KB
/
manager.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
from clases.input_auto import generate_agents
from clases.agentes import Ship, Port, Route
import clases.func_params as f_p
import simpy
import heapq
import time
class Manager:
def __init__(self):
self.env = simpy.Environment()
self.ships = {}
self.ports = {}
self.routes = {}
self.filename = f_p.FILE_NAME
self.start_time = None
self.end_time = None
def search_route(self, actual_port_id, final_port, matriz_adyacencia):
N = len(matriz_adyacencia)
costos = [float('inf')] * N
costos[actual_port_id] = 0
previos = [-1] * N
visitados = [False] * N
cola_prioridad = [(0, actual_port_id)]
while cola_prioridad:
costo_actual, puerto_actual = heapq.heappop(cola_prioridad)
if visitados[puerto_actual]:
continue
visitados[puerto_actual] = True
if puerto_actual == final_port:
break
for vecino in range(N):
ruta_id = matriz_adyacencia[puerto_actual][vecino]
if ruta_id == 0:
continue
ruta_temp = self.routes[ruta_id]
costo_ruta = f_p.costo_ruta(ruta_temp)
if costo_ruta > 0 and not visitados[vecino]:
nuevo_costo = costo_actual + costo_ruta
if nuevo_costo < costos[vecino]:
costos[vecino] = nuevo_costo
previos[vecino] = puerto_actual
heapq.heappush(cola_prioridad, (nuevo_costo, vecino))
if costos[final_port] == float('inf'):
print("No hay ruta disponible entre los puertos.")
return None
# Reconstruir la ruta
ruta = []
puerto = final_port
while puerto != -1 and previos[puerto] != -1:
ruta.append(f"{previos[puerto]}-{puerto}")
puerto = previos[puerto]
ruta.reverse()
if ruta == []:
print("No hay ruta disponible entre los puertos.")
return None
return ruta
def ship_event_loop(self, ship, events):
actual_port_id = ship.port_id
events = ship.itinerary
test = events
visitados = set()
ship.start_time = self.env.now
intentos_fallidos = 0
while len(visitados) != len(test):
progreso = False
for final_port_id in events:
if final_port_id not in visitados:
final_port = self.ports[final_port_id]
if not final_port.open:
print(f"El puerto {final_port_id} está cerrado.")
continue
rutas = self.search_route(actual_port_id, final_port_id,
self.matrix)
if rutas is None:
print(f"No hay ruta disponible hacia"
f"el puerto {final_port_id}.")
continue
progreso = True # Hay una ruta disponible
for ruta in rutas:
route = self.routes[ruta]
route.ships.append(ship.ship_id)
try:
yield self.env.process(
ship.drive(final_port, route,
self.filename, self.matrix)
)
except Exception as e:
print(f"Error durante el viaje: {e}")
route.ships.remove(ship.ship_id)
route.ships.remove(ship.ship_id)
actual_port_id = final_port_id
ship.actual_port = final_port_id
visitados.add(final_port_id)
# Si no se pudo avanzar en este ciclo
if not progreso:
intentos_fallidos += 1
# Terminar si todos los puertos son inaccesibles
if intentos_fallidos >= len(events):
ship.end_time = self.env.now
return
# Reiniciar el itinerario si es cíclico
if ship.cycles and len(visitados) == len(test):
visitados = set()
ship.end_time = self.env.now
ship.end_time = self.env.now
def processes(self):
# Procesar cada barco con su itinerario asociado
for ship_id, ship in self.ships.items():
self.env.process(self.ship_event_loop(ship, ship.itinerary))
def run(self, until):
self.start_time = time.time()
self.env.run(until=until)
self.end_time = time.time()
def step_run(self, until, sleep_time):
self.start_time = time.time()
while self.env.now < until:
if not self.env._queue:
break
self.env.step()
time.sleep(sleep_time)
self.end_time = time.time()
def add_ports(self, ports_file):
for i, port in enumerate(self.ports_generator(ports_file)):
self.ports[port.port_id] = port
self.matrix = [[0 for _ in range(i + 1)] for _ in range(i + 1)]
def add_routes(self, routes_file):
for route in self.routes_generator(routes_file):
self.routes[route.route_id] = route
self.matrix[route.initial_port_id][route.final_port_id] = route.route_id
def add_ships(self, ships_file):
for ship in self.ships_generator(ships_file):
self.ships[ship.ship_id] = ship
def add(self, n_ports=None, input_file=None):
if input_file is None:
ports, routes, ships, matrix = generate_agents(self.env, n_ports, True)
self.ports = ports
self.routes = routes
self.ships = ships
self.matrix = matrix
else:
ports_file = input_file[0]
routes_file = input_file[1]
ships_file = input_file[2]
self.add_ports(ports_file)
self.add_routes(routes_file)
self.add_ships(ships_file)
self.output()
def calculate_metrics(self):
times = []
for ship in self.ships.values():
total_time = abs(ship.end_time - ship.start_time)
# total_time = abs(ship.end_time - ship.start_time)
# print(f"Barco {ship.ship_id} - "
# f"Tiempo total itinerario cumplido: {total_time}"
# f" unidades de tiempo\n")
times.append(total_time)
total_wait_time_routes = sum(ship.total_wait_time_routes for ship in self.ships.values())
total_wait_time_ports = sum(ship.total_wait_time_ports for ship in self.ships.values())
num_events = len(self.ships)
avg_wait_time_routes = total_wait_time_routes / num_events if num_events > 0 else 0
avg_wait_time_ports = total_wait_time_ports / num_events if num_events > 0 else 0
# print(f"Tiempo promedio de espera en rutas: "
# f"{avg_wait_time_routes:.2f} unidades de tiempo\n")
# print(f"Tiempo promedio de espera en puertos: "
# f"{avg_wait_time_ports:.2f} unidades de tiempo\n")
return times
# esta función solo se debe ocupar después de correr la
# simulación con self.run
def elapsed_time(self):
return self.end_time - self.start_time
def output(self):
with open(self.filename, "a") as file:
for ship in self.ships.values():
file.write(f"ship;{ship.ship_id};{ship.name};"
f"{ship.itinerary[0]};{ship.speed};0.0\n")
for port in self.ports.values():
file.write(f"port;{port.port_id};{port.name}\n")
for route in self.routes.values():
file.write(f"route;{route.initial_port_id}-"
f"{route.final_port_id};1\n")
# estas funciones generator son solo una forma "elegante" de cargar los
# .txt como instancias. NO tienen que ver con la simulación en simpy y
# no son tan importantes.
def ports_generator(self, ports_file):
with open(ports_file) as file:
file.readline()
for line in file:
data = line.strip().split(";")
yield Port(self.env, data[0],
int(data[1]), int(data[2]))
def routes_generator(self, routes_file):
with open(routes_file) as file:
file.readline()
for line in file:
data = line.strip().split(";")
yield Route(self.env, int(data[0]), int(data[1]),
int(data[2]), int(data[3]), float(data[4]),
float(data[5]), float(data[6]))
def ships_generator(self, ships_file):
with open(ships_file) as file:
file.readline()
for line in file:
data = line.strip().split(";")
name = data[0]
speed = float(data[1])
port_id = int(data[2])
cycles = int(data[3])
recharge = int(data[4])
itinerary = list(map(int, data[5].split(",")))
yield Ship(self.env, name, speed, port_id,
cycles, recharge, itinerary)