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>
This commit is contained in:
108
software/gerar_ofrag.py
Normal file
108
software/gerar_ofrag.py
Normal file
@@ -0,0 +1,108 @@
|
||||
"""
|
||||
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()
|
||||
Reference in New Issue
Block a user