Files
arara_oarmp/software/gerar_ofrag.py
Cesa-V f0d72f8189 Corrige modelo OAMRP v3 e alinha nomenclatura com Al-Thani (2016)
Correções ao modelo:
- Adiciona conservação de fluxo em nós de inspeção (bug que permitia
  criação/destruição de unidades de fluxo, inflando cobertura para 50/50
  sem atribuição real de rotas)
- Remove restrição C6 simplificada que somava horas totais sem respeitar
  resets de inspeção, tornando 2809/2811 artificialmente ociosos

Nomenclatura alinhada ao Al-Thani (2016):
- InspecaoParam.lrt_h → f_max  (F do artigo: batente máximo legal)
- Aeronave.horas_iniciais → f0  (f_k: horas acumuladas no início)
- orcamento_h() → lrt_inicial() (LRT = F_max − f0)
- horas_iniciais_aleatorias() → gerar_f0_aleatorio()
- carregar_horas_iniciais() → carregar_f0()
- CLI --horas-iniciais → --f0

Novos arquivos:
- software/gerar_ofrag.py: gera tabela de OFRAGs com prioridades 1-5
- software/visualizar_resultado.py: mapa Folium interativo de rotas
- db/processed/ofrag.csv: 50 OFRAGs sintéticas (seed 42)
- db/processed/mapa_rotas.html: mapa gerado da última rodada

Resultado com --sintetico --aleatorio --ofrag: 50/50 missões cumpridas
(antes: 30/50 sem ferry, 7/50 com bug de fluxo)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 00:50:53 -03:00

109 lines
3.5 KiB
Python

"""
Gera o arquivo de OFRAG (Ordem de Fragmentação) sintético para o Esquadrão Arara.
Cada OFRAG autoriza uma missão específica e define:
- Prioridade operacional (1=urgente … 5=baixa)
- Tipo de missão (Logística, Médico, Pessoal, Reabastecimento, Reconhecimento)
- Base de origem e destino
Uso:
python software/gerar_ofrag.py --seed 42 --n 50 --saida db/processed/ofrag.csv
"""
import argparse
import csv
import random
from datetime import datetime, timezone
from pathlib import Path
# ---------------------------------------------------------------------------
# Importa constantes e função de geração do modelo principal
# ---------------------------------------------------------------------------
import sys
sys.path.insert(0, str(Path(__file__).resolve().parent))
from oamrp_v3 import (
BASES_COORDS,
TIPOS_MISSAO,
_PESOS_PRIO,
gerar_missoes_sinteticas,
)
BASE_DIR = Path(__file__).resolve().parents[1]
OFRAG_PADRAO = BASE_DIR / "db" / "processed" / "ofrag.csv"
_TIPOS_EVAM = ["Médico", "Logística"] # EVAM normalmente carrega carga urgente
def gerar_ofrag(
n: int = 50,
seed: int = 42,
saida: Path = OFRAG_PADRAO,
) -> None:
inicio = datetime(2025, 1, 1, tzinfo=timezone.utc)
missoes, _ = gerar_missoes_sinteticas(n=n, seed=seed, inicio=inicio, n_evam=5)
rng = random.Random(seed + 1) # semente diferente para tipos
saida.parent.mkdir(parents=True, exist_ok=True)
colunas = [
"id_ofrag", "missao_id", "om", "orig", "dest",
"prioridade", "tipo_missao", "observacao",
]
with saida.open("w", newline="", encoding="utf-8-sig") as f:
w = csv.DictWriter(f, fieldnames=colunas)
w.writeheader()
for idx, m in enumerate(missoes, start=1):
if m.obrigatoria:
prio = 1
tipo = rng.choice(_TIPOS_EVAM)
obs = "EVAM — execução obrigatória"
else:
prio = rng.choices(range(1, 6), weights=_PESOS_PRIO)[0]
tipo = rng.choices(TIPOS_MISSAO, weights=[20, 15, 25, 25, 15])[0]
obs = ""
w.writerow({
"id_ofrag": f"OFRAG-2025-{idx:03d}",
"missao_id": m.id,
"om": m.om,
"orig": m.orig,
"dest": m.dest,
"prioridade": prio,
"tipo_missao": tipo,
"observacao": obs,
})
print(f" OFRAG gerado: {saida} ({len(missoes)} ordens)")
_resumir(saida)
def _resumir(saida: Path) -> None:
from collections import Counter
prios: Counter = Counter()
tipos: Counter = Counter()
with saida.open(newline="", encoding="utf-8-sig") as f:
for row in csv.DictReader(f):
prios[int(row["prioridade"])] += 1
tipos[row["tipo_missao"]] += 1
print("\n Distribuição de prioridades:")
for p in sorted(prios):
bar = "#" * prios[p]
print(f" Prio {p}: {prios[p]:3d} {bar}")
print("\n Distribuição de tipos:")
for t, c in tipos.most_common():
print(f" {t:<20} {c:3d}")
def main() -> None:
parser = argparse.ArgumentParser(description="Gerador de OFRAG sintético — Arara C-105")
parser.add_argument("--seed", type=int, default=42)
parser.add_argument("--n", type=int, default=50, help="Número de missões")
parser.add_argument("--saida", type=Path, default=OFRAG_PADRAO)
args = parser.parse_args()
gerar_ofrag(n=args.n, seed=args.seed, saida=args.saida)
if __name__ == "__main__":
main()