Processa registro de voo 2025
This commit is contained in:
@@ -21,6 +21,10 @@
|
||||
============================================================================
|
||||
"""
|
||||
|
||||
import csv
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
import pulp
|
||||
|
||||
# ===========================================================================
|
||||
@@ -28,7 +32,8 @@ import pulp
|
||||
# ===========================================================================
|
||||
# Bases (codigos curtos ficticios). 'S' marca bases HABILITADAS a inspecao.
|
||||
# Na FAB o conjunto de bases habilitadas e' restrito -> isso aperta o routing.
|
||||
BASES_INSPECAO = {"BR", "GL"} # ex.: Brasilia (hub do esq.) e Galeao
|
||||
# MN representa Manaus, base operacional do Esquadrao Arara nesta simulacao.
|
||||
BASES_INSPECAO = {"MN"} # Manaus
|
||||
|
||||
TURNAROUND_MIN_H = 1.0 # tempo minimo em solo entre 2 missoes (h)
|
||||
LIMITE_HORAS_F = 100.0 # horas de celula ate inspecao obrigatoria
|
||||
@@ -38,31 +43,85 @@ LIMITE_HORAS_F = 100.0 # horas de celula ate inspecao obrigatori
|
||||
# 'horas_acum' = f_k. LRT inicial de cada aeronave = LIMITE_HORAS_F - f_k.
|
||||
AERONAVES = [
|
||||
# id base horas_acum
|
||||
("FAB-2301", "BR", 12.0),
|
||||
("FAB-2302", "BR", 71.0), # ja gastou muito -> pouco LRT disponivel
|
||||
("FAB-2303", "GL", 30.0),
|
||||
("FAB-2304", "BR", 95.0), # quase no limite -> mal consegue voar
|
||||
("FAB-2301", "MN", 12.0),
|
||||
("FAB-2302", "MN", 71.0), # ja gastou muito -> pouco LRT disponivel
|
||||
("FAB-2303", "MN", 30.0),
|
||||
("FAB-2304", "MN", 95.0), # quase no limite -> mal consegue voar
|
||||
]
|
||||
|
||||
# ----- Missoes de transporte: (id, origem, destino, partida_h, chegada_h, prioridade)
|
||||
# partida/chegada em horas decimais dentro do horizonte (ex.: 0..72 = 3 dias).
|
||||
# prioridade: peso da missao (missao critica pesa mais).
|
||||
MISSOES = [
|
||||
# id orig dest part cheg prio
|
||||
("M01", "BR", "MN", 6.0, 10.0, 5), # logistica Amazonia (alta prio)
|
||||
("M02", "MN", "BR", 12.0, 16.0, 5),
|
||||
("M03", "BR", "RF", 7.0, 10.0, 3),
|
||||
("M04", "RF", "BR", 11.5, 14.5, 3),
|
||||
("M05", "BR", "CG", 8.0, 10.0, 2),
|
||||
("M06", "CG", "BR", 11.0, 13.0, 2),
|
||||
("M07", "GL", "BR", 6.5, 8.0, 4),
|
||||
("M08", "BR", "BE", 9.0, 12.5, 4), # Belem
|
||||
("M09", "BE", "BR", 14.0, 17.5, 4),
|
||||
("M10", "BR", "GL", 15.0, 16.5, 1),
|
||||
("M11", "BR", "MN", 30.0, 34.0, 5), # dia 2
|
||||
("M12", "MN", "BR", 36.0, 40.0, 5),
|
||||
# ----- Missoes de transporte
|
||||
# Entrada recomendada: data/hora UTC, como viria de uma planilha.
|
||||
# Para usar uma planilha, exporte para CSV com as colunas:
|
||||
# id,orig,dest,partida_utc,chegada_utc,prioridade
|
||||
# e preencha CSV_MISSOES_PATH com o caminho do arquivo.
|
||||
CSV_MISSOES_PATH = None
|
||||
|
||||
MISSOES_UTC = [
|
||||
# id orig dest partida UTC chegada UTC prio
|
||||
("M01", "MN", "BR", "2026-07-01T06:00Z", "2026-07-01T10:00Z", 5),
|
||||
("M02", "BR", "MN", "2026-07-01T12:00Z", "2026-07-01T16:00Z", 5),
|
||||
("M03", "MN", "BE", "2026-07-01T06:30Z", "2026-07-01T09:00Z", 4),
|
||||
("M04", "BE", "MN", "2026-07-01T11:00Z", "2026-07-01T13:30Z", 4),
|
||||
("M05", "MN", "PV", "2026-07-01T07:00Z", "2026-07-01T09:00Z", 3),
|
||||
("M06", "PV", "MN", "2026-07-01T10:30Z", "2026-07-01T12:30Z", 3),
|
||||
("M07", "MN", "BV", "2026-07-01T08:00Z", "2026-07-01T09:30Z", 3),
|
||||
("M08", "BV", "MN", "2026-07-01T11:00Z", "2026-07-01T12:30Z", 3),
|
||||
("M09", "MN", "GL", "2026-07-01T18:00Z", "2026-07-01T23:00Z", 4),
|
||||
("M10", "GL", "MN", "2026-07-02T01:00Z", "2026-07-02T06:00Z", 4),
|
||||
("M11", "MN", "CG", "2026-07-01T20:00Z", "2026-07-01T23:00Z", 2),
|
||||
("M12", "CG", "MN", "2026-07-02T01:00Z", "2026-07-02T04:00Z", 2),
|
||||
("M13", "MN", "RF", "2026-07-02T06:00Z", "2026-07-02T11:00Z", 4),
|
||||
("M14", "RF", "MN", "2026-07-02T13:00Z", "2026-07-02T18:00Z", 4),
|
||||
("M15", "MN", "NT", "2026-07-02T08:00Z", "2026-07-02T12:30Z", 3),
|
||||
("M16", "NT", "MN", "2026-07-02T14:00Z", "2026-07-02T18:30Z", 3),
|
||||
("M17", "MN", "BR", "2026-07-03T00:00Z", "2026-07-03T04:00Z", 5),
|
||||
("M18", "BR", "MN", "2026-07-03T06:00Z", "2026-07-03T10:00Z", 5),
|
||||
("M19", "MN", "BE", "2026-07-03T02:00Z", "2026-07-03T04:30Z", 4),
|
||||
("M20", "BE", "MN", "2026-07-03T06:00Z", "2026-07-03T08:30Z", 4),
|
||||
]
|
||||
|
||||
def parse_utc(valor):
|
||||
"""Converte texto ISO UTC da planilha para datetime com timezone."""
|
||||
texto = str(valor).strip()
|
||||
if texto.endswith("Z"):
|
||||
texto = texto[:-1] + "+00:00"
|
||||
dt = datetime.fromisoformat(texto)
|
||||
if dt.tzinfo is None:
|
||||
dt = dt.replace(tzinfo=timezone.utc)
|
||||
return dt.astimezone(timezone.utc)
|
||||
|
||||
def carregar_missoes_csv(caminho):
|
||||
with Path(caminho).open(newline="", encoding="utf-8-sig") as arq:
|
||||
leitor = csv.DictReader(arq)
|
||||
return [
|
||||
(
|
||||
linha["id"],
|
||||
linha["orig"],
|
||||
linha["dest"],
|
||||
linha["partida_utc"],
|
||||
linha["chegada_utc"],
|
||||
int(linha["prioridade"]),
|
||||
)
|
||||
for linha in leitor
|
||||
]
|
||||
|
||||
def converter_missoes_para_horas(missoes_utc):
|
||||
datas_partida = [parse_utc(m[3]) for m in missoes_utc]
|
||||
horizonte_inicio = min(datas_partida)
|
||||
missoes_horas = []
|
||||
for mid, orig, dest, partida_utc, chegada_utc, prio in missoes_utc:
|
||||
partida = parse_utc(partida_utc)
|
||||
chegada = parse_utc(chegada_utc)
|
||||
if chegada <= partida:
|
||||
raise ValueError(f"Missao {mid}: chegada_utc deve ser depois de partida_utc")
|
||||
part_h = (partida - horizonte_inicio).total_seconds() / 3600
|
||||
cheg_h = (chegada - horizonte_inicio).total_seconds() / 3600
|
||||
missoes_horas.append((mid, orig, dest, part_h, cheg_h, prio))
|
||||
return missoes_horas, horizonte_inicio
|
||||
|
||||
MISSOES_FONTE = carregar_missoes_csv(CSV_MISSOES_PATH) if CSV_MISSOES_PATH else MISSOES_UTC
|
||||
MISSOES, HORIZONTE_INICIO_UTC = converter_missoes_para_horas(MISSOES_FONTE)
|
||||
|
||||
# ===========================================================================
|
||||
# 2) PRE-PROCESSAMENTO (gera conexoes viaveis)
|
||||
# ===========================================================================
|
||||
@@ -95,7 +154,7 @@ c = {(k, i, j): pulp.LpVariable(f"c_{k}_{i}_{j}", cat="Binary") # k voa i de
|
||||
for k in KS for (i, j) in conex}
|
||||
z = {i: pulp.LpVariable(f"z_{i}", cat="Binary") for i in IDS} # missao coberta
|
||||
|
||||
# --- Objetivo: maximizar prioridade total das missoes cumpridas
|
||||
# --- Objetivo primario: maximizar prioridade total das missoes cumpridas
|
||||
mdl += pulp.lpSum(miss[i]["prio"] * z[i] for i in IDS), "Missoes_cumpridas_ponderadas"
|
||||
|
||||
# --- (R1) Cobertura: cada missao por NO MAXIMO uma aeronave
|
||||
@@ -133,6 +192,24 @@ for k in KS:
|
||||
# 4) RESOLVER
|
||||
# ===========================================================================
|
||||
solver = pulp.PULP_CBC_CMD(msg=False) # troque por HiGHS se instalado
|
||||
|
||||
# Etapa 1: maximiza o cumprimento ponderado das missoes.
|
||||
mdl.solve(solver)
|
||||
prio_otima = int(pulp.value(mdl.objective))
|
||||
|
||||
# Etapa 2: mantendo a prioridade otima, minimiza o LRT restante ponderado.
|
||||
# Como as horas totais voadas podem ser constantes quando todas as missoes sao
|
||||
# cumpridas, o peso prioriza consumir LRT das aeronaves mais proximas da
|
||||
# manutencao, sem sacrificar nenhuma missao prioritaria da primeira etapa.
|
||||
lrt_restante = {
|
||||
k: (LIMITE_HORAS_F - acft[k]["f"]) -
|
||||
pulp.lpSum(miss[i]["dur"] * x[(k, i)] for i in IDS)
|
||||
for k in KS
|
||||
}
|
||||
pesos_lrt = {k: 1 / (LIMITE_HORAS_F - acft[k]["f"]) for k in KS}
|
||||
mdl += pulp.lpSum(miss[i]["prio"] * z[i] for i in IDS) == prio_otima, "fixa_prioridade_otima"
|
||||
mdl.sense = pulp.LpMinimize
|
||||
mdl.setObjective(pulp.lpSum(pesos_lrt[k] * lrt_restante[k] for k in KS))
|
||||
mdl.solve(solver)
|
||||
|
||||
# ===========================================================================
|
||||
@@ -140,11 +217,17 @@ mdl.solve(solver)
|
||||
# ===========================================================================
|
||||
print("=" * 70)
|
||||
print(f" STATUS: {pulp.LpStatus[mdl.status]}")
|
||||
print(f" Horizonte inicia em UTC: {HORIZONTE_INICIO_UTC.isoformat().replace('+00:00', 'Z')}")
|
||||
prio_tot = sum(miss[i]["prio"] for i in IDS)
|
||||
prio_obt = int(pulp.value(mdl.objective))
|
||||
prio_obt = prio_otima
|
||||
cob = [i for i in IDS if z[i].value() and z[i].value() > 0.5]
|
||||
print(f" Missoes cumpridas: {len(cob)}/{len(IDS)} "
|
||||
f"| Prioridade obtida: {prio_obt}/{prio_tot}")
|
||||
lrt_total = sum((LIMITE_HORAS_F - acft[k]["f"]) -
|
||||
sum(miss[i]["dur"] * x[(k, i)].value() for i in IDS)
|
||||
for k in KS)
|
||||
print(f" LRT restante total: {lrt_total:g}h")
|
||||
print(" Criterio secundario: menor LRT restante ponderado p/ manutencao")
|
||||
print("=" * 70)
|
||||
|
||||
def rota_da_aeronave(k):
|
||||
@@ -185,4 +268,4 @@ if descobertas:
|
||||
# NOTA - usar HiGHS (recomendado p/ instancias maiores, sem licenca):
|
||||
# pip install highspy
|
||||
# solver = pulp.HiGHS(msg=False) # PuLP 3.x expoe HiGHS nativo
|
||||
# ---------------------------------------------------------------------------
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user