diff --git a/.gitignore b/.gitignore index ff9c438..0eafe05 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,7 @@ Desktop.ini # Local editor folders .vscode/ .idea/ + +# Python cache +__pycache__/ +*.py[cod] diff --git a/pre_process/comparacao_ica_66_31_c105_2023/comparacao_ica_66_31_c105_2023_vs_relatorio_ciclo.csv b/db/pre_process/comparacao_ica_66_31_c105_2023/comparacao_ica_66_31_c105_2023_vs_relatorio_ciclo.csv similarity index 100% rename from pre_process/comparacao_ica_66_31_c105_2023/comparacao_ica_66_31_c105_2023_vs_relatorio_ciclo.csv rename to db/pre_process/comparacao_ica_66_31_c105_2023/comparacao_ica_66_31_c105_2023_vs_relatorio_ciclo.csv diff --git a/pre_process/comparacao_ica_66_31_c105_2023/comparacao_ica_66_31_c105_2023_vs_relatorio_ciclo.md b/db/pre_process/comparacao_ica_66_31_c105_2023/comparacao_ica_66_31_c105_2023_vs_relatorio_ciclo.md similarity index 100% rename from pre_process/comparacao_ica_66_31_c105_2023/comparacao_ica_66_31_c105_2023_vs_relatorio_ciclo.md rename to db/pre_process/comparacao_ica_66_31_c105_2023/comparacao_ica_66_31_c105_2023_vs_relatorio_ciclo.md diff --git a/pre_process/comparacao_ica_66_31_c105_2023/comparar_ica_relatorio_ciclo.py b/db/pre_process/comparacao_ica_66_31_c105_2023/comparar_ica_relatorio_ciclo.py similarity index 100% rename from pre_process/comparacao_ica_66_31_c105_2023/comparar_ica_relatorio_ciclo.py rename to db/pre_process/comparacao_ica_66_31_c105_2023/comparar_ica_relatorio_ciclo.py diff --git a/pre_process/relatorio_ciclo_inspecoes_c105_2805_2026-06-15/preprocess_pdf.py b/db/pre_process/relatorio_ciclo_inspecoes_c105_2805_2026-06-15/preprocess_pdf.py similarity index 100% rename from pre_process/relatorio_ciclo_inspecoes_c105_2805_2026-06-15/preprocess_pdf.py rename to db/pre_process/relatorio_ciclo_inspecoes_c105_2805_2026-06-15/preprocess_pdf.py diff --git a/pre_process/relatorio_ciclo_inspecoes_c105_2805_2026-06-15/relatorio_ciclo_inspecoes_c105_2805_2026-06-15_inspecoes.csv b/db/pre_process/relatorio_ciclo_inspecoes_c105_2805_2026-06-15/relatorio_ciclo_inspecoes_c105_2805_2026-06-15_inspecoes.csv similarity index 100% rename from pre_process/relatorio_ciclo_inspecoes_c105_2805_2026-06-15/relatorio_ciclo_inspecoes_c105_2805_2026-06-15_inspecoes.csv rename to db/pre_process/relatorio_ciclo_inspecoes_c105_2805_2026-06-15/relatorio_ciclo_inspecoes_c105_2805_2026-06-15_inspecoes.csv diff --git a/pre_process/relatorio_ciclo_inspecoes_c105_2805_2026-06-15/relatorio_ciclo_inspecoes_c105_2805_2026-06-15_inspecoes.json b/db/pre_process/relatorio_ciclo_inspecoes_c105_2805_2026-06-15/relatorio_ciclo_inspecoes_c105_2805_2026-06-15_inspecoes.json similarity index 100% rename from pre_process/relatorio_ciclo_inspecoes_c105_2805_2026-06-15/relatorio_ciclo_inspecoes_c105_2805_2026-06-15_inspecoes.json rename to db/pre_process/relatorio_ciclo_inspecoes_c105_2805_2026-06-15/relatorio_ciclo_inspecoes_c105_2805_2026-06-15_inspecoes.json diff --git a/pre_process/relatorio_ciclo_inspecoes_c105_2805_2026-06-15/relatorio_ciclo_inspecoes_c105_2805_2026-06-15_texto.txt b/db/pre_process/relatorio_ciclo_inspecoes_c105_2805_2026-06-15/relatorio_ciclo_inspecoes_c105_2805_2026-06-15_texto.txt similarity index 100% rename from pre_process/relatorio_ciclo_inspecoes_c105_2805_2026-06-15/relatorio_ciclo_inspecoes_c105_2805_2026-06-15_texto.txt rename to db/pre_process/relatorio_ciclo_inspecoes_c105_2805_2026-06-15/relatorio_ciclo_inspecoes_c105_2805_2026-06-15_texto.txt diff --git a/processed/.gitkeep b/db/processed/.gitkeep similarity index 100% rename from processed/.gitkeep rename to db/processed/.gitkeep diff --git a/raw/Cópia de ESCALA DE MISSÕES 2025 - ATÉ 20 de outubro, 1812.xlsx b/db/raw/Cópia de ESCALA DE MISSÕES 2025 - ATÉ 20 de outubro, 1812.xlsx similarity index 100% rename from raw/Cópia de ESCALA DE MISSÕES 2025 - ATÉ 20 de outubro, 1812.xlsx rename to db/raw/Cópia de ESCALA DE MISSÕES 2025 - ATÉ 20 de outubro, 1812.xlsx diff --git a/raw/ESCALA DE MISSÕES 2024.xlsx b/db/raw/ESCALA DE MISSÕES 2024.xlsx similarity index 100% rename from raw/ESCALA DE MISSÕES 2024.xlsx rename to db/raw/ESCALA DE MISSÕES 2024.xlsx diff --git a/raw/ICA 66-31 2023.pdf b/db/raw/ICA 66-31 2023.pdf similarity index 100% rename from raw/ICA 66-31 2023.pdf rename to db/raw/ICA 66-31 2023.pdf diff --git a/raw/relatorio_ciclo_inspecoes_c105_2805_2026-06-15.pdf b/db/raw/relatorio_ciclo_inspecoes_c105_2805_2026-06-15.pdf similarity index 100% rename from raw/relatorio_ciclo_inspecoes_c105_2805_2026-06-15.pdf rename to db/raw/relatorio_ciclo_inspecoes_c105_2805_2026-06-15.pdf diff --git a/software/oamrp_v1.py b/software/oamrp_v1.py new file mode 100644 index 0000000..6b1eebb --- /dev/null +++ b/software/oamrp_v1.py @@ -0,0 +1,188 @@ +""" +============================================================================ + AIRCRAFT ROUTING - ESQUADRAO ARARA (Transporte C-95/C-105) +============================================================================ + Modelo MIP compacto que ATRIBUI aeronaves a missoes de transporte, + maximizando o numero (ponderado por prioridade) de missoes cumpridas, + respeitando: + - rota viavel (continuidade espacial e temporal por aeronave) + - turnaround minimo entre missoes + - limite de horas de celula ate a proxima inspecao (LRT) + - base inicial e horas acumuladas reais de cada aeronave + + Diferenca chave vs. modelo civil (Bazargan): aqui a cobertura e' "<= 1" + (missao OPCIONAL, selecionada se compensar) e nao "= 1". O objetivo e' + prontidao/cumprimento de missao, nao custo. + + Solver: CBC (vem junto com o PuLP, zero instalacao). Para instancias + maiores troque por HiGHS (ver nota no fim do arquivo). + + Autor: gerado como ponto de partida. pt-BR nos comentarios, codigo limpo. +============================================================================ +""" + +import pulp + +# =========================================================================== +# 1) DADOS DE ENTRADA (substitua pelos dados reais do ARARA) +# =========================================================================== +# 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 + +TURNAROUND_MIN_H = 1.0 # tempo minimo em solo entre 2 missoes (h) +LIMITE_HORAS_F = 100.0 # horas de celula ate inspecao obrigatoria + # (AJUSTE ao intervalo real da sua frota) + +# ----- Aeronaves (tail number, base inicial, horas acumuladas desde a ult. insp.) +# '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 +] + +# ----- 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), +] + +# =========================================================================== +# 2) PRE-PROCESSAMENTO (gera conexoes viaveis) +# =========================================================================== +miss = {m[0]: dict(orig=m[1], dest=m[2], part=m[3], cheg=m[4], prio=m[5], + dur=m[4] - m[3]) for m in MISSOES} +acft = {a[0]: dict(base=a[1], f=a[2]) for a in AERONAVES} +IDS = list(miss.keys()) +KS = list(acft.keys()) + +# Conexao i->j valida: destino de i == origem de j e ha turnaround suficiente +conex = [(i, j) for i in IDS for j in IDS + if i != j + and miss[i]["dest"] == miss[j]["orig"] + and miss[j]["part"] >= miss[i]["cheg"] + TURNAROUND_MIN_H] + +# Aeronave k pode INICIAR pela missao i se i parte da base inicial de k +def pode_iniciar(k, i): + return miss[i]["orig"] == acft[k]["base"] + +# =========================================================================== +# 3) MODELO MIP +# =========================================================================== +mdl = pulp.LpProblem("ARARA_Aircraft_Routing", pulp.LpMaximize) + +# --- Variaveis +x = {(k, i): pulp.LpVariable(f"x_{k}_{i}", cat="Binary") for k in KS for i in IDS} +s = {(k, i): pulp.LpVariable(f"s_{k}_{i}", cat="Binary") # k inicia por i + for k in KS for i in IDS if pode_iniciar(k, i)} +c = {(k, i, j): pulp.LpVariable(f"c_{k}_{i}_{j}", cat="Binary") # k voa i depois j + 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 +mdl += pulp.lpSum(miss[i]["prio"] * z[i] for i in IDS), "Missoes_cumpridas_ponderadas" + +# --- (R1) Cobertura: cada missao por NO MAXIMO uma aeronave +for i in IDS: + mdl += z[i] == pulp.lpSum(x[(k, i)] for k in KS), f"def_z_{i}" + # z[i] ja e' binaria, entao automaticamente <= 1 + +# --- (R2) Fluxo de entrada: se k voa i, ele iniciou por i OU chegou de outra missao +for k in KS: + for i in IDS: + entradas = pulp.lpSum(c[(k, jj, i)] for (jj, ii) in conex if ii == i) + ini = s[(k, i)] if (k, i) in s else 0 + mdl += ini + entradas == x[(k, i)], f"in_{k}_{i}" + +# --- (R3) Fluxo de saida: depois de i, k segue para no max uma proxima missao +for k in KS: + for i in IDS: + saidas = pulp.lpSum(c[(k, i, jj)] for (ii, jj) in conex if ii == i) + mdl += saidas <= x[(k, i)], f"out_{k}_{i}" + +# --- (R4) Cada aeronave inicia no maximo uma rota +for k in KS: + inis = [s[(k, i)] for i in IDS if (k, i) in s] + if inis: + mdl += pulp.lpSum(inis) <= 1, f"start_{k}" + +# --- (R5) Limite de horas de celula (LRT): total voado por k <= F - f_k +# Esta e' a restricao CENTRAL de manutencao (versao "orcamento de horas"). +# -> A inspecao com RESET de horas em base habilitada e' a extensao da Fase 3. +for k in KS: + mdl += pulp.lpSum(miss[i]["dur"] * x[(k, i)] for i in IDS) <= \ + LIMITE_HORAS_F - acft[k]["f"], f"horas_{k}" + +# =========================================================================== +# 4) RESOLVER +# =========================================================================== +solver = pulp.PULP_CBC_CMD(msg=False) # troque por HiGHS se instalado +mdl.solve(solver) + +# =========================================================================== +# 5) RELATORIO +# =========================================================================== +print("=" * 70) +print(f" STATUS: {pulp.LpStatus[mdl.status]}") +prio_tot = sum(miss[i]["prio"] for i in IDS) +prio_obt = int(pulp.value(mdl.objective)) +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}") +print("=" * 70) + +def rota_da_aeronave(k): + """Reconstroi a sequencia de missoes da aeronave k seguindo as conexoes.""" + inicio = [i for i in IDS if (k, i) in s and s[(k, i)].value() and s[(k, i)].value() > 0.5] + if not inicio: + return [] + seq = [inicio[0]] + while True: + atual = seq[-1] + prox = [j for (ii, jj) in conex if ii == atual + for j in [jj] if c[(k, atual, j)].value() and c[(k, atual, j)].value() > 0.5] + if not prox: + break + seq.append(prox[0]) + return seq + +for k in KS: + seq = rota_da_aeronave(k) + horas = sum(miss[i]["dur"] for i in seq) + lrt_ini = LIMITE_HORAS_F - acft[k]["f"] + if seq: + trechos = " -> ".join( + f"{i}({miss[i]['orig']}-{miss[i]['dest']} {miss[i]['part']:g}h)" for i in seq) + print(f"\n {k} [base {acft[k]['base']}, f={acft[k]['f']:g}h, LRT={lrt_ini:g}h]") + print(f" {trechos}") + print(f" horas voadas: {horas:g}h | LRT restante: {lrt_ini - horas:g}h") + else: + print(f"\n {k} [base {acft[k]['base']}, LRT={lrt_ini:g}h] -> sem missoes") + +descobertas = [i for i in IDS if i not in cob] +if descobertas: + print("\n " + "-" * 60) + print(" Missoes NAO cumpridas:", + ", ".join(f"{i}(prio {miss[i]['prio']})" for i in descobertas)) + +# --------------------------------------------------------------------------- +# NOTA - usar HiGHS (recomendado p/ instancias maiores, sem licenca): +# pip install highspy +# solver = pulp.HiGHS(msg=False) # PuLP 3.x expoe HiGHS nativo +# --------------------------------------------------------------------------- \ No newline at end of file