Compare commits

...

11 Commits

Author SHA1 Message Date
Cesa-V
fe2ba12e49 Inclui @vitorcesavc nos autores (README e docs/authors.md)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 18:03:31 -03:00
Cesa-V
b368201d5d Documenta historico v1/v2/v3 no README com fases, limitacoes e nomenclatura
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 11:14:57 -03:00
Cesa-V
e71a0f2518 Atualiza README para v0.10: Set Partitioning, cenario insp grande e tabela de parametros
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 11:11:23 -03:00
Cesa-V
a34ed6b340 Migra C1 de Set Covering para Set Partitioning e documenta limitacoes (v0.10)
- C1 alterado: z[nid] <= 1 -> z[nid] == 1 para todas as missoes; toda missao
  deve ser coberta por exatamente 1 aeronave (sem missoes descobertas).
- n_missoes padrao reduzido de 50 para 20 (n_evam de 5 para 4): garante
  factibilidade do MIP com Set Partitioning no envelope de 4 aeronaves.
- Time limit padrao do solver CBC fixado em 120 s (era ilimitado): evita
  travamento ao tentar provar infeasibility.
- Corrigido display "Not Solved": status de L1 preservado quando PuLP
  redefine mdl.status apos setObjective() em L2.
- f0_cenario_insp_grande.json revisado: apenas 2803 com f0=595h em INSP 2A
  (F_max=600h, downtime=4 dias); demais matriculas com f0=0.
- Gerado planejamento_insp_grande.html: Gantt exibe INSP 2A visivelmente;
  20/20 missoes cobertas; CBC Optimal em < 15 s.
- changelog.md e LOG.md atualizados com limitacoes detalhadas para
  contribuidores: escopo validado, bugs de display, riscos numericos do
  big-M com LRT < 5h e comportamento de 2811 ociosa.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 11:09:24 -03:00
Cesa-V
6433055e9e Atualiza LOG e changelog para sessao 2026-06-16 (v0.8 e v0.9)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 09:39:30 -03:00
Cesa-V
83a2dac624 Corrige Gantt, adiciona escala de voo real e ajusta solver (v0.9)
- visualizar_resultado: barras Gantt via go.Scatter lines (renderizacao
  garantida), yaxis range fixo para FAB 2800 visivel, largura minima 2h
- oamrp_v3: parser --escala para Escala de Voo Modelo 1 (CSV real),
  coordenadas de 26 bases, time-limit default 5min, imports timedelta/date

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 09:34:14 -03:00
Cesa-V
3756f961a8 Adiciona visualizacao combinada (Gantt + mapa + tabela) e exporta inspecoes no CSV (v0.8)
oamrp_v3.py: exporta linhas status=inspecao no CSV resultado com datas UTC reais
visualizar_resultado.py: refatorado para gerar um unico HTML (planejamento.html)
com cabecalho, Gantt Plotly (divisoes diarias, FAB labels), mapa Folium em iframe
e tabela de voos com filtros por aeronave/status/prioridade/base e busca livre

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 01:48:36 -03:00
Cesa-V
4d8554df38 Atualiza documentacao para refletir sessao 2026-06-16 (v0.7)
- LOG.md: entradas para bugs corrigidos (C2 inspeção, C6 removida),
  renomeação Al-Thani, OFRAG, visualização e rodada 50/50
- docs/changelog.md: versão v0.7 com correções, adições e resultado validado
- docs/about.md: seção v3 com rede espaço-tempo, nomenclatura Al-Thani,
  restrições C1-C11 e scripts auxiliares; v1 mantido como referência histórica
- README.md: estado atual aponta oamrp_v3 como modelo ativo, com comandos

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 01:00:48 -03:00
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
Cesa-V
7cd7458565 Adiciona missoes sinteticas, ferry (Fase 3) e consolida versoes anteriores
- Gerador sintetico cross-base (50 missoes, 10 bases, haversine/C-105)
- Fase 3: arcos de ferry em construir_rede(); ferry consome horas de celula
- oamrp_v2.py e resultados CSV anteriores adicionados ao repositorio
- README, docs/about.md e changelog atualizados ate v0.6

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 23:51:31 -03:00
Cesa-V
328a066849 Implementa OAMRP v3 com rede espaco-tempo e restricoes de inspecao (Fases 1-2)
Reescrita do zero em software/oamrp_v3.py: abstracao No/Arco, variavel
unificada y[k,arco], C1-C6 (Fase 1) e C7-C11 com relogio de horas,
downtime e disjuntivas de slot unico (Fase 2). TAT corrigido para 1,5h;
top-10 bases; EVAM obrigatoria; L2 = maximizar horas voadas.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 23:29:44 -03:00
19 changed files with 6468 additions and 39 deletions

1
.gitignore vendored
View File

@@ -9,6 +9,7 @@ Desktop.ini
# Local editor folders
.vscode/
.idea/
.claude/
# Python cache
__pycache__/

119
HANDOFF_arara_oamrp.md Normal file
View File

@@ -0,0 +1,119 @@
# HANDOFF — ARARA OAMRP (Aircraft Routing C-105 / FAB)
> Documento de transferência para o **Claude Code**.
> Projeto local: `arara_oamrp/`. Cole/abra este arquivo no Claude Code e siga as instruções de leitura antes de validar a formulação e gerar código.
---
## 0. Enquadramento do problema
Modelo de **Aircraft Routing** para o Esquadrão ARARA (C-105), no estilo **OAMRP** (Al-Thani et al., 2016): sequenciamento **compacto** em **rede espaço-tempo**.
**NÃO** é Set Partitioning + Column Generation (modelo civil Bazargan). Desvios deliberados em relação ao caso civil:
1. **Cobertura ≤ 1** (seleção de missões), não particionamento `= 1`.
2. **Objetivo de maximização** de cobertura ponderada por prioridade, não minimização de custo.
3. Possibilidade de subconjunto de missões **obrigatórias (hard)**.
**Stack:** Python + PuLP (CBC para começar) → HiGHS como evolução.
---
## 1. Calibração confirmada (respostas do Vitor)
| Item | Decisão |
|---|---|
| Frota | **4 aeronaves**, homogêneas (mesmo LRT, mesma performance) |
| Horizonte | **1 mês** |
| Discretização | **Estilo OAMRP** — eventos de voo com horários explícitos |
| TAT (turnaround) | **01h30** |
| Base de inspeção | Apenas **SBMN** (somente níveis **Base** e **Orgânico**; **Parque desconsiderado**) |
| Bases de operação | **10 bases da FAB** (derivar/confirmar a partir do CSV) |
| Traslado (ferry) | **Permitido** e **consome horas de célula** |
| Reset de horas | Inspeção **zera apenas aquela inspeção**; inspeções **não simultâneas** na mesma aeronave (podem ser sequenciais se requisitos cumpridos) |
| Prioridade | Escala **1 a 5** |
| Missões por dia | Uma cauda pode encadear **várias missões no mesmo dia** |
| Horas iniciais (f_k) | Criar **campos editáveis**; permitir **default aleatório** |
### Objetivo (lexicográfico)
- **L1 — Primário:** maximizar Σ (prioridade × missões cumpridas).
- **L2 — Secundário:** "entregar aeronave com menor LRT" *(ver Q3 — pendente de definição)*.
- **Restrição dura derivada do 6.1:** **no máximo 1 aeronave em inspeção ao mesmo tempo** → modelada como restrição de recurso (C11), não como objetivo.
---
## 2. Formulação matemática proposta (validar antes de codar)
### Conjuntos
- `K` — aeronaves (caudas), |K| = 4.
- `B` — bases (10). `B_insp ⊆ B = {SBMN}`.
- `M` — missões candidatas. `M_obr ⊆ M` — obrigatórias *(Q2)*.
- `T` — tipos de inspeção válidos *(Q1)*.
### Parâmetros
- `f⁰_{k,i}` — horas acumuladas iniciais da cauda `k` no relógio da inspeção `i` (editável / aleatório).
- `LRT_i` — intervalo (horas de célula) da inspeção `i`.
- `CAL_i` — limite calendárico (dias) da inspeção `i`.
- `DT_i` — tempo indisponível (downtime) da inspeção `i`.
- `h_m` — horas de voo da missão `m` (do CSV, por par O-D).
- `o_m, d_m` — origem/destino de `m`.
- `[e_m, l_m]` — janela de decolagem de `m`.
- `p_m ∈ {1..5}` — prioridade de `m`.
- `hferry_{b,b'}` — horas de traslado entre bases (do CSV/distância).
- `home_k` — base inicial da cauda `k`.
- `TAT = 1.5 h`.
### Variáveis de decisão
- `x_{k,m} ∈ {0,1}` — cauda `k` cumpre missão `m`.
- `y` — arcos de sequenciamento na rede espaço-tempo (missão→missão, ferry, inspeção) por cauda.
- `t_dep_{k,m}`, `t_arr_{k,m}` — tempos (contínuos).
- `z_{k,i}` (+ início `s_{k,i}`) — cauda `k` executa inspeção `i`.
- `F_{k,i}(·)` — estado de horas acumuladas no relógio `i` ao longo da rota (contínuo, zera na inspeção `i`).
### Restrições
- **C1 Cobertura:** Σ_k x_{k,m} ≤ 1 ∀m; **= 1** ∀m ∈ M_obr.
- **C2 Continuidade (fluxo):** cada cauda forma um caminho único ordenado no tempo, iniciando em `home_k`; grau de entrada = grau de saída nos nós.
- **C3 Continuidade espacial:** encadear `m→m'` exige `d_m = o_{m'}`, senão exige arco de **ferry**.
- **C4 Continuidade temporal + TAT:** `t_dep(próx) ≥ t_arr(ant) + TAT` (+ tempo de ferry, se houver reposicionamento).
- **C5 Janela:** `e_m ≤ t_dep_{k,m} ≤ l_m`.
- **C6 Orçamento de horas com reset:** ao longo da rota, horas acumuladas no relógio `i``LRT_i`; voo/ferry somam horas; inspeção `i` zera o relógio `i`.
- **C7 Limite calendárico:** dias desde o último reset de `i``CAL_i`.
- **C8 Local de inspeção:** inspeção `i` só ocorre com a cauda em `B_insp` (SBMN); a cauda precisa rotear até lá.
- **C9 Downtime:** durante `DT_i` a cauda fica indisponível; atividades seguintes começam após.
- **C10 Não-simultaneidade na mesma cauda:** duas inspeções da mesma cauda não se sobrepõem (podem ser back-to-back).
- **C11 Slot único de inspeção (recurso):** em qualquer instante, Σ_k [cauda em inspeção] ≤ 1.
### Função objetivo (lexicográfica)
1. **max** Σ_m p_m · (Σ_k x_{k,m})
2. **então** otimizar L2 *(Q3)*.
---
## 3. Plano incremental de construção
1. **Modelo base** — cobertura ponderada + orçamento de horas (C1C6).
2. **Inspeção com reset** em SBMN — C7C11 + downtime/calendário.
3. **Realismo militar** — prioridade hard (M_obr), voos de posicionamento, janelas.
4. **Replanejamento + visualização** — re-otimização e dashboard.
---
## 4. Instruções para o Claude Code (executar na pasta `arara_oamrp/`)
1. **Ler contexto:** abrir o arquivo de CONTEXTO/LOG e tudo em `docs/` para entender o estado atual.
2. **Auditar e podar:** o ChatGPT inseriu **acréscimos desnecessários** — revisar e remover o que fugir do OAMRP compacto, mas antes pergunte. Confirmar que **não** há resquício de Set Partitioning + Column Generation.
3. **Ler inspeções:** `relatorio_ciclo_inspecoes_c105_2805_2026-06-15` (JSON) → extrair, para cada inspeção válida *(Q1: seq 2,3,4,6,7,18,19,20,22,23)*, os campos `LRT_i` (horas), `CAL_i` (limite calendárico) e `DT_i` (tempo indisponível).
4. **Ler voos:** `db/processed/registro_voo_2025_consolidado.csv` → derivar as **10 bases**, as **horas de voo por par O-D** (`h_m`) e os tempos de **ferry**.
5. **Horas iniciais:** criar campos editáveis para `f⁰_{k,i}`, com opção de **default aleatório**.
6. **Validar a formulação** da Seção 2 com o Vitor antes de gerar PuLP. Construção **incremental**, confirmando cada etapa.
---
## 5. Pendências a resolver (perguntar ao Vitor no Claude Code)
- **Q1 — Inspeções válidas:** confirmar a lista de seq **{2, 3, 4, 6, 7, 18, 19, 20, 22, 23}**.
- **Q2 — Obrigatórias:** SAR/EVAM entram como **hard** (M_obr, cobertura = 1) ou apenas como prioridade 5? *(o dataset hoje só traz prioridade 15)*
- **Q3 — Objetivo secundário "entregar aeronave com menor LRT":** definir o sentido exato. Duas leituras possíveis:
- **(a)** minimizar a **folga de horas ao fim do horizonte** (usar bem as horas disponíveis: não deixar cauda ociosa com muita célula sobrando que poderia ter voado);
- **(b)** **priorizar o emprego** das caudas mais próximas da inspeção primeiro (consumir antes quem está mais perto do limite).
- **Q4 — Bases:** validar as 10 bases que o Claude Code propuser a partir do CSV.

22
LOG.md
View File

@@ -26,3 +26,25 @@ Legenda de autores:
| 2026-06-15 | 19:56 | VTO | Consolidou transicoes `ROTA` do registro de voo 2025 | `db/pre_process/registro_voo_2025/preprocess_planilha_2025.py`; `db/processed/registro_voo_2025_consolidado.csv` | `ROTA` tratado como marcador de transicao; 1421 linhas originais viraram 1268 trechos consolidados; 135 trechos com `ROTA` foram convertidos para origem/destino reais. |
| 2026-06-15 | 19:56 | VTO | Validou o registro de voo 2025 consolidado | `db/pre_process/registro_voo_2025/validacao_registro_voo_2025.md`; `db/processed/registro_voo_2025_consolidado.csv` | Sem duplicidades, sem divergencias de duracao e sem sobreposicoes temporais; pendentes 1 registro sem OM, 1 trecho com pousos zero e 3 quebras de continuidade espacial. |
| 2026-06-15 | 19:56 | VTO | Removeu registro isolado da aeronave 2806 | `db/pre_process/registro_voo_2025/preprocess_planilha_2025.py`; `db/processed/registro_voo_2025.csv`; `db/processed/registro_voo_2025_consolidado.csv` | Linha original 793 excluida dos artefatos processados; registro final passou para 1420 linhas e consolidado para 1267 trechos; nao ha mais registro sem OM. |
| 2026-06-15 | 19:56 | VTO | Criou modelo OAMRP v2 com dados reais de 2025 | `software/oamrp_v2.py`; `db/processed/resultado_oamrp_jan_2025.csv`; `README.md`; `docs/about.md`; `docs/changelog.md` | Rodada inicial de janeiro/2025 resolvida como otima pelo CBC; prioridade uniforme 1; base de manutencao SBMN; 50 de 61 missoes cumpridas. |
| 2026-06-15 | 21:00 | VTO | Criou modelo OAMRP v3 — Fase 1: rede espaço-tempo | `software/oamrp_v3.py`; `db/processed/resultado_oamrp_v3.csv` | Reescrita do zero com abstrações No/Arco e variável unificada y[k,arco]; C1C6; TAT=1,5h; f0_{k,i} editável; top-10 bases por frequência; missões EVAM hard; 50/50 missões cobertas em jan/2025. |
| 2026-06-15 | 22:00 | VTO | Calibrou inspeções válidas (Q1) e respondeu Q2Q4 | `software/oamrp_v3.py` | Removidas seq 18 e 23 do catálogo; EVAM marcado como M_obr (cobertura=1); L2 definido como minimizar folga de horas ao fim do horizonte; 10 bases por frequência. |
| 2026-06-15 | 22:30 | VTO | Implementou Fase 2: inspeções na rede espaço-tempo | `software/oamrp_v3.py` | Adicionados nós INSP_{k}_{seq} e arcos missao_insp/insp_missao; variáveis H_{k,i,nid} para rastreamento de horas com reset; C9 downtime; C11 slot único via disjuntivas. |
| 2026-06-15 | 23:00 | VTO | Substituiu dados reais por missões sintéticas cross-base | `software/oamrp_v3.py`; `db/processed/resultado_oamrp_sintetico.csv` | Dados reais de jan/2025 eram todos SBMN→SBMN (voos locais), não exercitando roteamento; gerador sintético cria 50 missões cross-base entre 10 bases com distâncias reais (haversine) e velocidade C-105; flag --sintetico na CLI. |
| 2026-06-15 | 23:30 | VTO | Implementou Fase 3: arcos de ferry (C3 relaxado) | `software/oamrp_v3.py` | Arcos inicio/missao_missao/missao_insp/insp_missao agora permitem bases diferentes com tempo de ferry (haversine/430 km/h); ferry consome horas de célula no relógio de inspeção (C6); relatório imprime segmentos de ferry na linha do tempo. |
| 2026-06-16 | 10:00 | VTO | Corrigiu bug de conservação de fluxo em nós de inspeção | `software/oamrp_v3.py` | Nós INSP_{k}_{seq} não tinham restrição entrada_k==saida_k; solver criava/destruía unidades de fluxo nesses nós, inflando cobertura para 50/50 no relatório com apenas 16 missões reais nas rotas; corrigido adicionando C2 para nós de inspeção. |
| 2026-06-16 | 10:05 | VTO | Removeu restrição C6 Fase 1 simplificada | `software/oamrp_v3.py` | Restrição somava horas totais sem respeitar resets de inspeção, tornando EVAMs 2809/2811 artificialmente ociosas; variáveis H[k,seq,nid] já cobrem isso corretamente com reset nos nós de inspeção. |
| 2026-06-16 | 10:10 | VTO | Adicionou horas iniciais aleatórias e prioridades via OFRAG | `software/oamrp_v3.py`; `software/gerar_ofrag.py`; `db/processed/ofrag.csv` | Flag --aleatorio sorteia f0 entre 10%90% de F_max por aeronave; gerar_ofrag.py cria tabela com id_ofrag, missao_id, prioridade (15), tipo_missao; --ofrag na CLI carrega prioridades ao gerar missões sintéticas. |
| 2026-06-16 | 10:20 | VTO | Alinhou nomenclatura com Al-Thani (2016) | `software/oamrp_v3.py` | lrt_h→f_max (batente fixo F); horas_iniciais→f0 (acumulado inicial f_k); orcamento_h()→lrt_inicial() (LRT=F_maxf0); horas_iniciais_aleatorias()→gerar_f0_aleatorio(); carregar_horas_iniciais()→carregar_f0(); CLI --horas-iniciais→--f0. |
| 2026-06-16 | 10:30 | VTO | Criou visualização interativa de rotas | `software/visualizar_resultado.py`; `db/processed/mapa_rotas.html` | Mapa Folium com rotas por aeronave (cor distinta), missões não cumpridas tracejadas em cinza, popup com detalhes, legenda de prioridades. |
| 2026-06-16 | 10:35 | VTO | Rodada sintética validada: 50/50 missões cumpridas | `db/processed/resultado_oamrp_sintetico.csv` | Comando: --sintetico --seed 42 --aleatorio --ofrag; solver CBC retornou Optimal; todas as 4 EVAMs voaram com ferry; commit f0d72f8 enviado ao remote. |
| 2026-06-16 | 14:00 | VTO | Criou visualização combinada (Gantt + mapa + tabela) | `software/visualizar_resultado.py`; `db/processed/planejamento_cenario_esquadrao.html` | HTML único com título "Planejamento de Diagonal de Manutenção"; Gantt via go.Scatter lines (renderização garantida); mapa Folium em iframe; tabela de voos com filtros JS; commit 3756f96. |
| 2026-06-16 | 15:00 | VTO | Criou cenários JSON de f0 para testes com inspeções | `db/processed/f0_cenario_esquadrao.json`; `db/processed/f0_cenario_insp_grande.json` | f0_cenario_esquadrao simula dados reais do esquadrão (LRT 32h/215h/12h/28h); f0_cenario_insp_grande força aeronaves próximas ao limite. |
| 2026-06-16 | 20:00 | VTO | Migrou modelo de Set Covering para Set Partitioning (C1) | `software/oamrp_v3.py` | Restrição C1 alterada de z<=1 para z==1 para todas as missões; eliminada distinção entre missões hard/opcionais; toda missão deve ser coberta por exatamente 1 aeronave. |
| 2026-06-16 | 20:05 | VTO | Reduziu missões sintéticas padrão de 50 para 20 | `software/oamrp_v3.py` | Set Partitioning com 50 missões tornava o MIP inviável ou muito lento (travamento do solver); 20 missões garantem factibilidade no envelope de 4 aeronaves em janeiro; n_evam reduzido de 5 para 4. |
| 2026-06-16 | 20:10 | VTO | Adicionou time limit padrão de 120 s ao solver CBC | `software/oamrp_v3.py` | Solver travava indefinidamente sem time limit ao tentar provar infeasibility; limite de 120 s garante retorno com melhor solução encontrada. |
| 2026-06-16 | 20:15 | VTO | Corrigiu display do status do solver (Not Solved) | `software/oamrp_v3.py` | PuLP redefine mdl.status para 0 ("Not Solved") ao chamar setObjective() antes de L2; status de L1 agora preservado quando L2 reporta 0. |
| 2026-06-16 | 20:20 | VTO | Revisou f0_cenario_insp_grande.json | `db/processed/f0_cenario_insp_grande.json` | Apenas 2803 com f0=595h em INSP 2A (F_max=600h, downtime=4 dias, LRT_ini=5h); demais matrículas com f0=0; configuração anterior com 4 aeronaves apertadas simultaneamente tornava o MIP inviável. |
| 2026-06-16 | 20:30 | VTO | Gerou visualização do cenário inspeção grande (v0.10) | `db/processed/planejamento_insp_grande.html`; `db/processed/resultado_oamrp_v3.csv` | 20/20 missões cobertas; Gantt exibe INSP 2A (4 dias) visivelmente; 2 inspeções no horizonte de janeiro; solver CBC retornou Optimal em < 15 s. |
| 2026-06-16 | 16:00 | VTO | Corrigiu Gantt: FAB 2800 invisível e barras não renderizavam | `software/visualizar_resultado.py` | yaxis range=[-0.5, n-0.5] para FAB 2800 (y=0) visível; shapes substituídas por go.Scatter mode="lines" width=64 (renderização garantida); largura mínima de 2h por barra. |
| 2026-06-16 | 16:30 | VTO | Adicionou parser de Escala de Voo Modelo 1 e bases extras | `software/oamrp_v3.py`; `db/raw/ESCALA DE VOO MODELO 1.csv` | Flag --escala carrega missões reais do CSV com separador ";"; coordenadas de 26 bases (era 10); time-limit default alterado para 300s; imports timedelta/date consolidados; commit 83a2dac. |

120
README.md
View File

@@ -45,7 +45,7 @@ arara_oarmp/
Usamos tags curtas para identificar quem fez cada alteração relevante:
- `VTO`: Vitor Cesa.
- `VTO`: Vitor Cesa ([@vitorcesavc](https://git.ppgao.ita.br/vitorcesavc)).
- `GNR`: Generoso.
- `JOM`: João Marcos.
@@ -144,41 +144,107 @@ git push
- Preferir nomes de arquivo descritivos, com tipo do documento, aeronave, matrícula e data quando possível.
- Registrar no `LOG.md` qualquer decisão que afete os dados.
## Estado atual
## Estado atual (v0.10)
Já existe um pré-processamento do relatório de ciclo de inspeções da aeronave C-105 matrícula 2805, com data de relatório `2026-06-15`.
O modelo ativo é `software/oamrp_v3.py`, que implementa o OAMRP sobre uma rede espaço-tempo com arcos de ferry, múltiplos tipos de inspeção e Set Partitioning para cobertura de missões.
Também existe um primeiro protótipo em `software/oamrp_v1.py`, que formula um modelo MIP compacto em PuLP para atribuir aeronaves a missões de transporte, respeitando continuidade espacial/temporal, turnaround mínimo e limite de horas até inspeção.
**Última rodada validada (2026-06-16, commit `a34ed6b`):**
- 20/20 missões sintéticas cobertas, solver CBC Optimal em < 15 s
- Cenário com INSP 2A (4 dias em solo) visível no Gantt da matrícula 2803
- Set Partitioning: toda missão coberta por exatamente 1 aeronave
Arquivos principais:
### Formulação de cobertura
O modelo usa **Set Partitioning** (`z[m] = 1` para toda missão `m`): cada missão deve ser atribuída a exatamente uma aeronave. Versões anteriores usavam Set Covering (`z[m] ≤ 1`), que permitia missões descobertas. A transição foi documentada em `docs/changelog.md` (v0.10).
> **Atenção para contribuidores:** Set Partitioning foi validado apenas para o cenário sintético padrão (20 missões, seed 42, 4 aeronaves, somente 2803 com LRT apertado). Com mais missões ou múltiplas aeronaves com LRT < 5 h simultâneas, o MIP pode ser infeasível ou ultrapassar o time limit. Consulte as limitações detalhadas em `docs/changelog.md`.
### Arquivos principais
```text
db/raw/relatorio_ciclo_inspecoes_c105_2805_2026-06-15.pdf
db/pre_process/relatorio_ciclo_inspecoes_c105_2805_2026-06-15/preprocess_pdf.py
db/pre_process/relatorio_ciclo_inspecoes_c105_2805_2026-06-15/relatorio_ciclo_inspecoes_c105_2805_2026-06-15_texto.txt
db/pre_process/relatorio_ciclo_inspecoes_c105_2805_2026-06-15/relatorio_ciclo_inspecoes_c105_2805_2026-06-15_inspecoes.json
db/pre_process/relatorio_ciclo_inspecoes_c105_2805_2026-06-15/relatorio_ciclo_inspecoes_c105_2805_2026-06-15_inspecoes.csv
software/oamrp_v1.py
software/oamrp_v3.py — modelo MIP principal (Fases 13 + Set Partitioning)
software/gerar_ofrag.py — gerador da tabela OFRAG
software/visualizar_resultado.py — Gantt + mapa + tabela interativa
db/processed/ofrag.csv — ordens de missão com prioridades 15
db/processed/f0_cenario_insp_grande.json — f0 calibrado para forçar INSP 2A em 2803
db/processed/resultado_oamrp_v3.csv — resultado da última rodada
db/processed/planejamento_insp_grande.html — Gantt com inspeção de 4 dias visível
```
Esse processamento extraiu 18 inspeções e separou campos como inspeção, referência, vencimento, TSO, letra, nível, duração, intervalo por horas de voo, meses contínuos e pousos.
## Atualizacao - registro de voo 2025
### Como rodar
A `Planilha 2025.xlsx` foi pre-processada a partir de `db/raw/` sem alterar o arquivo original.
Cenário padrão (inspeção grande visível no Gantt):
Arquivos gerados:
```powershell
python software/oamrp_v3.py --sintetico --seed 42 --f0 db/processed/f0_cenario_insp_grande.json --time-limit 60
python software/visualizar_resultado.py --resultado db/processed/resultado_oamrp_v3.csv --saida db/processed/planejamento_insp_grande.html
```
Parâmetros relevantes do modelo:
| Parâmetro | Padrão | Descrição |
|---|---|---|
| `--sintetico` | | Usa missões sintéticas cross-base em vez do CSV real |
| `--n-missoes` | 20 | Número de missões sintéticas |
| `--seed` | 42 | Semente aleatória (reprodutibilidade) |
| `--f0` | | JSON com horas acumuladas iniciais por aeronave/inspeção |
| `--time-limit` | 300 | Limite de tempo do solver CBC (segundos) |
| `--aleatorio` | | Sorteia f0 aleatório entre 10%90% de F_max |
---
## Histórico dos modelos
### v1 — Protótipo inicial (`software/oamrp_v1.py`)
Primeiro protótipo do modelo MIP em PuLP. Estrutura simples de atribuição aeronave-missão sem rede espaço-tempo, sem inspeções e sem ferry. Serviu para validar a configuração do ambiente e a integração com o solver CBC.
- **Formulação:** atribuição direta, variáveis binárias por (aeronave, missão)
- **Restrições:** cobertura básica e continuidade de base
- **Sem:** janelas de tempo, inspeções, ferry, prioridades
- **Status:** protótipo histórico, não usar em produção
---
### v2 — Dados reais de 2025 (`software/oamrp_v2.py`)
Primeira rodada com dados reais do registro de voo de janeiro/2025, pré-processados a partir da planilha original. Introduziu janelas de tempo (decolagem e chegada do CSV), TAT entre missões e prioridade uniforme.
- **Dados de entrada:** `db/processed/registro_voo_2025_consolidado.csv` (1 267 trechos, 1 420 linhas originais)
- **Resultado:** 50 de 61 missões cumpridas em janeiro/2025 (`db/processed/resultado_oamrp_jan_2025.csv`)
- **Limitação identificada:** os dados reais de jan/2025 eram predominantemente voos locais SBMNSBMN, não exercitando roteamento cross-base nem inspeções
```powershell
python software/oamrp_v2.py
```
---
### v3 — Rede espaço-tempo completa (`software/oamrp_v3.py`) ← **modelo ativo**
Reescrita completa baseada em Al-Thani, Ben Ahmed & Haouari (2016). Introduziu a formulação por rede espaço-tempo com nós No e arcos Arco, inspeções com relógio de horas acumuladas e reset, arcos de ferry entre bases e missões sintéticas cross-base para validação de roteamento.
**Fases implementadas:**
| Fase | Restrições | Descrição |
|---|---|---|
| 1 | C1C6 | Rede espaço-tempo, cobertura (Set Partitioning), conservação de fluxo, TAT |
| 2 | C7C11 | Nós de inspeção, relógio H[k,i,n] com reset, downtime, slot único |
| 3 | Ferry | Arcos de ferry entre quaisquer bases (haversine ÷ 430 km/h), consome horas de célula |
**Nomenclatura alinhada com Al-Thani (2016):**
- `F_max` batente fixo (intervalo máximo legal entre manutenções, ex: 300 h)
- `f0` horas acumuladas no início do horizonte (estado inicial da aeronave)
- `LRT = F_max f0` tempo legal restante (decresce com o voo)
- `H[k, seq, nid]` acumulado em tempo real, rastreado na rede (equivale a u_j do artigo)
**Missões sintéticas:** como os dados reais de 2025 não exercitavam roteamento, o modelo usa um gerador sintético cross-base (`--sintetico`) com distâncias reais (haversine) e velocidade de cruzeiro do C-105 (430 km/h).
**Pré-processamentos disponíveis:**
```text
db/pre_process/registro_voo_2025/preprocess_planilha_2025.py
db/pre_process/registro_voo_2025/registro_voo_2025_limpo.csv
db/pre_process/registro_voo_2025/registro_voo_2025_limpo.xlsx
db/pre_process/registro_voo_2025/registro_voo_2025_consolidado.csv
db/pre_process/registro_voo_2025/missoes_oamrp_2025.csv
db/pre_process/registro_voo_2025/missoes_oamrp_2025_consolidado.csv
db/pre_process/registro_voo_2025/resumo_registro_voo_2025.json
db/pre_process/registro_voo_2025/validacao_registro_voo_2025.md
db/processed/registro_voo_2025.csv
db/processed/registro_voo_2025_consolidado.csv
db/raw/relatorio_ciclo_inspecoes_c105_2805_2026-06-15.pdf — ciclo de inspeções C-105/2805
db/processed/registro_voo_2025.csv — registro de voo 2025 limpo (1 420 trechos)
db/processed/registro_voo_2025_consolidado.csv — consolidado para roteamento
```
O consolidado trata `ROTA` como marcador de transicao, nao como localidade operacional, e remove a linha original 793 da aeronave 2806 dos artefatos processados.

View File

@@ -0,0 +1,6 @@
{
"2800": {"2": 268, "3": 268, "4": 568, "6": 142, "7": 142, "19": 1800, "20": 2100, "22": 680},
"2803": {"2": 85, "3": 85, "4": 285, "6": 85, "7": 185, "19": 800, "20": 1200, "22": 350},
"2809": {"2": 155, "3": 455, "4": 155, "6": 288, "7": 288, "19": 1200, "20": 1800, "22": 500},
"2811": {"2": 120, "3": 572, "4": 572, "6": 120, "7": 220, "19": 1500, "20": 2200, "22": 720}
}

View File

@@ -0,0 +1,6 @@
{
"2800": {"2": 0, "3": 0, "4": 0, "6": 0, "7": 0, "19": 0, "20": 0, "22": 0},
"2803": {"2": 0, "3": 595, "4": 0, "6": 0, "7": 0, "19": 0, "20": 0, "22": 0},
"2809": {"2": 0, "3": 0, "4": 0, "6": 0, "7": 0, "19": 0, "20": 0, "22": 0},
"2811": {"2": 0, "3": 0, "4": 0, "6": 0, "7": 0, "19": 0, "20": 0, "22": 0}
}

1999
db/processed/mapa_rotas.html Normal file

File diff suppressed because it is too large Load Diff

51
db/processed/ofrag.csv Normal file
View File

@@ -0,0 +1,51 @@
id_ofrag,missao_id,om,orig,dest,prioridade,tipo_missao,observacao
OFRAG-2025-001,SIM_001_SBMN_SBMY,1,SBMN,SBMY,1,Reabastecimento,
OFRAG-2025-002,SIM_002_SBBE_SBTT,2,SBBE,SBTT,1,Pessoal,
OFRAG-2025-003,SIM_003_SBMN_SBPV,3,SBMN,SBPV,1,Médico,EVAM — execução obrigatória
OFRAG-2025-004,SIM_004_SBMN_SBUA,4,SBMN,SBUA,1,Logística,EVAM — execução obrigatória
OFRAG-2025-005,SIM_005_SBSN_SBTS,5,SBSN,SBTS,2,Reabastecimento,
OFRAG-2025-006,SIM_006_SBSN_SBBE,6,SBSN,SBBE,2,Pessoal,
OFRAG-2025-007,SIM_007_SBTS_SBMN,7,SBTS,SBMN,2,Reconhecimento,
OFRAG-2025-008,SIM_008_SBBE_SBTS,8,SBBE,SBTS,4,Reconhecimento,
OFRAG-2025-009,SIM_009_SBMY_SBTS,9,SBMY,SBTS,2,Logística,
OFRAG-2025-010,SIM_010_SBTT_SBMN,10,SBTT,SBMN,1,Logística,
OFRAG-2025-011,SIM_011_SBBE_SBSN,11,SBBE,SBSN,1,Logística,
OFRAG-2025-012,SIM_012_SWBC_SBSN,12,SWBC,SBSN,3,Logística,
OFRAG-2025-013,SIM_013_SBMN_SBBV,13,SBMN,SBBV,2,Reconhecimento,
OFRAG-2025-014,SIM_014_SBMN_SBTS,14,SBMN,SBTS,1,Logística,
OFRAG-2025-015,SIM_015_SBMN_SBPV,15,SBMN,SBPV,1,Médico,EVAM — execução obrigatória
OFRAG-2025-016,SIM_016_SBPV_SBMN,16,SBPV,SBMN,1,Logística,
OFRAG-2025-017,SIM_017_SBSN_SBMN,17,SBSN,SBMN,4,Reabastecimento,
OFRAG-2025-018,SIM_018_SBSN_SBBV,18,SBSN,SBBV,1,Reconhecimento,
OFRAG-2025-019,SIM_019_SBBV_SBMN,19,SBBV,SBMN,2,Logística,
OFRAG-2025-020,SIM_020_SWBC_SBTS,20,SWBC,SBTS,4,Pessoal,
OFRAG-2025-021,SIM_021_SBMN_SBPV,21,SBMN,SBPV,1,Médico,EVAM — execução obrigatória
OFRAG-2025-022,SIM_022_SBMN_SBBV,22,SBMN,SBBV,1,Reabastecimento,
OFRAG-2025-023,SIM_023_SBBV_SWBC,23,SBBV,SWBC,3,Reconhecimento,
OFRAG-2025-024,SIM_024_SBMY_SBBV,24,SBMY,SBBV,1,Pessoal,
OFRAG-2025-025,SIM_025_SWBC_SBMN,25,SWBC,SBMN,3,Logística,
OFRAG-2025-026,SIM_026_SBUA_SBTT,26,SBUA,SBTT,3,Reabastecimento,
OFRAG-2025-027,SIM_027_SBTS_SBMY,27,SBTS,SBMY,1,Pessoal,
OFRAG-2025-028,SIM_028_SBMN_SBTT,28,SBMN,SBTT,2,Logística,
OFRAG-2025-029,SIM_029_SBTT_SBSN,29,SBTT,SBSN,2,Reconhecimento,
OFRAG-2025-030,SIM_030_SWBC_SBSN,30,SWBC,SBSN,2,Logística,
OFRAG-2025-031,SIM_031_SBMN_SBTT,31,SBMN,SBTT,2,Reabastecimento,
OFRAG-2025-032,SIM_032_SBMN_SBBE,32,SBMN,SBBE,5,Reabastecimento,
OFRAG-2025-033,SIM_033_SBBV_SBSN,33,SBBV,SBSN,1,Reconhecimento,
OFRAG-2025-034,SIM_034_SBTS_SBMN,34,SBTS,SBMN,2,Logística,
OFRAG-2025-035,SIM_035_SBTT_SBMN,35,SBTT,SBMN,1,Logística,
OFRAG-2025-036,SIM_036_SBPV_SWBC,36,SBPV,SWBC,3,Logística,
OFRAG-2025-037,SIM_037_SBBV_SBMN,37,SBBV,SBMN,3,Logística,
OFRAG-2025-038,SIM_038_SBBV_SBBE,38,SBBV,SBBE,2,Pessoal,
OFRAG-2025-039,SIM_039_SBSN_SBMN,39,SBSN,SBMN,1,Logística,
OFRAG-2025-040,SIM_040_SBSN_SBMN,40,SBSN,SBMN,4,Reconhecimento,
OFRAG-2025-041,SIM_041_SBBE_SBTS,41,SBBE,SBTS,1,Reabastecimento,
OFRAG-2025-042,SIM_042_SBBE_SBSN,42,SBBE,SBSN,5,Reconhecimento,
OFRAG-2025-043,SIM_043_SBMN_SBPV,43,SBMN,SBPV,4,Médico,
OFRAG-2025-044,SIM_044_SBBE_SBPV,44,SBBE,SBPV,3,Pessoal,
OFRAG-2025-045,SIM_045_SBTS_SBMN,45,SBTS,SBMN,4,Reabastecimento,
OFRAG-2025-046,SIM_046_SBMN_SBUA,46,SBMN,SBUA,3,Reconhecimento,
OFRAG-2025-047,SIM_047_SWBC_SBTS,47,SWBC,SBTS,2,Reabastecimento,
OFRAG-2025-048,SIM_048_SBSN_SBUA,48,SBSN,SBUA,1,Médico,
OFRAG-2025-049,SIM_049_SBMN_SBMY,49,SBMN,SBMY,4,Pessoal,
OFRAG-2025-050,SIM_050_SBTT_SBPV,50,SBTT,SBPV,1,Logística,EVAM — execução obrigatória
1 id_ofrag missao_id om orig dest prioridade tipo_missao observacao
2 OFRAG-2025-001 SIM_001_SBMN_SBMY 1 SBMN SBMY 1 Reabastecimento
3 OFRAG-2025-002 SIM_002_SBBE_SBTT 2 SBBE SBTT 1 Pessoal
4 OFRAG-2025-003 SIM_003_SBMN_SBPV 3 SBMN SBPV 1 Médico EVAM — execução obrigatória
5 OFRAG-2025-004 SIM_004_SBMN_SBUA 4 SBMN SBUA 1 Logística EVAM — execução obrigatória
6 OFRAG-2025-005 SIM_005_SBSN_SBTS 5 SBSN SBTS 2 Reabastecimento
7 OFRAG-2025-006 SIM_006_SBSN_SBBE 6 SBSN SBBE 2 Pessoal
8 OFRAG-2025-007 SIM_007_SBTS_SBMN 7 SBTS SBMN 2 Reconhecimento
9 OFRAG-2025-008 SIM_008_SBBE_SBTS 8 SBBE SBTS 4 Reconhecimento
10 OFRAG-2025-009 SIM_009_SBMY_SBTS 9 SBMY SBTS 2 Logística
11 OFRAG-2025-010 SIM_010_SBTT_SBMN 10 SBTT SBMN 1 Logística
12 OFRAG-2025-011 SIM_011_SBBE_SBSN 11 SBBE SBSN 1 Logística
13 OFRAG-2025-012 SIM_012_SWBC_SBSN 12 SWBC SBSN 3 Logística
14 OFRAG-2025-013 SIM_013_SBMN_SBBV 13 SBMN SBBV 2 Reconhecimento
15 OFRAG-2025-014 SIM_014_SBMN_SBTS 14 SBMN SBTS 1 Logística
16 OFRAG-2025-015 SIM_015_SBMN_SBPV 15 SBMN SBPV 1 Médico EVAM — execução obrigatória
17 OFRAG-2025-016 SIM_016_SBPV_SBMN 16 SBPV SBMN 1 Logística
18 OFRAG-2025-017 SIM_017_SBSN_SBMN 17 SBSN SBMN 4 Reabastecimento
19 OFRAG-2025-018 SIM_018_SBSN_SBBV 18 SBSN SBBV 1 Reconhecimento
20 OFRAG-2025-019 SIM_019_SBBV_SBMN 19 SBBV SBMN 2 Logística
21 OFRAG-2025-020 SIM_020_SWBC_SBTS 20 SWBC SBTS 4 Pessoal
22 OFRAG-2025-021 SIM_021_SBMN_SBPV 21 SBMN SBPV 1 Médico EVAM — execução obrigatória
23 OFRAG-2025-022 SIM_022_SBMN_SBBV 22 SBMN SBBV 1 Reabastecimento
24 OFRAG-2025-023 SIM_023_SBBV_SWBC 23 SBBV SWBC 3 Reconhecimento
25 OFRAG-2025-024 SIM_024_SBMY_SBBV 24 SBMY SBBV 1 Pessoal
26 OFRAG-2025-025 SIM_025_SWBC_SBMN 25 SWBC SBMN 3 Logística
27 OFRAG-2025-026 SIM_026_SBUA_SBTT 26 SBUA SBTT 3 Reabastecimento
28 OFRAG-2025-027 SIM_027_SBTS_SBMY 27 SBTS SBMY 1 Pessoal
29 OFRAG-2025-028 SIM_028_SBMN_SBTT 28 SBMN SBTT 2 Logística
30 OFRAG-2025-029 SIM_029_SBTT_SBSN 29 SBTT SBSN 2 Reconhecimento
31 OFRAG-2025-030 SIM_030_SWBC_SBSN 30 SWBC SBSN 2 Logística
32 OFRAG-2025-031 SIM_031_SBMN_SBTT 31 SBMN SBTT 2 Reabastecimento
33 OFRAG-2025-032 SIM_032_SBMN_SBBE 32 SBMN SBBE 5 Reabastecimento
34 OFRAG-2025-033 SIM_033_SBBV_SBSN 33 SBBV SBSN 1 Reconhecimento
35 OFRAG-2025-034 SIM_034_SBTS_SBMN 34 SBTS SBMN 2 Logística
36 OFRAG-2025-035 SIM_035_SBTT_SBMN 35 SBTT SBMN 1 Logística
37 OFRAG-2025-036 SIM_036_SBPV_SWBC 36 SBPV SWBC 3 Logística
38 OFRAG-2025-037 SIM_037_SBBV_SBMN 37 SBBV SBMN 3 Logística
39 OFRAG-2025-038 SIM_038_SBBV_SBBE 38 SBBV SBBE 2 Pessoal
40 OFRAG-2025-039 SIM_039_SBSN_SBMN 39 SBSN SBMN 1 Logística
41 OFRAG-2025-040 SIM_040_SBSN_SBMN 40 SBSN SBMN 4 Reconhecimento
42 OFRAG-2025-041 SIM_041_SBBE_SBTS 41 SBBE SBTS 1 Reabastecimento
43 OFRAG-2025-042 SIM_042_SBBE_SBSN 42 SBBE SBSN 5 Reconhecimento
44 OFRAG-2025-043 SIM_043_SBMN_SBPV 43 SBMN SBPV 4 Médico
45 OFRAG-2025-044 SIM_044_SBBE_SBPV 44 SBBE SBPV 3 Pessoal
46 OFRAG-2025-045 SIM_045_SBTS_SBMN 45 SBTS SBMN 4 Reabastecimento
47 OFRAG-2025-046 SIM_046_SBMN_SBUA 46 SBMN SBUA 3 Reconhecimento
48 OFRAG-2025-047 SIM_047_SWBC_SBTS 47 SWBC SBTS 2 Reabastecimento
49 OFRAG-2025-048 SIM_048_SBSN_SBUA 48 SBSN SBUA 1 Médico
50 OFRAG-2025-049 SIM_049_SBMN_SBMY 49 SBMN SBMY 4 Pessoal
51 OFRAG-2025-050 SIM_050_SBTT_SBPV 50 SBTT SBPV 1 Logística EVAM — execução obrigatória

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,62 @@
status,aeronave_modelo,ordem,id,om,orig,dest,partida_utc,chegada_utc,dur_h,prioridade,periodo,aeronave_real_2025,linhas_origem
cumprida,2800,1,RV2025_OM1_E1_L4_C4,1,SBMN,SBMY,2025-01-02T11:55:00Z,2025-01-02T13:05:00Z,1.17,1,NOTURNO,2809,4
cumprida,2800,2,RV2025_OM1_E2_L5_C5,1,SBMY,SBMN,2025-01-02T14:30:00Z,2025-01-02T15:35:00Z,1.08,1,NOTURNO,2809,5
cumprida,2800,3,RV2025_OM6_E1_L12_C12,6,SBMN,SBBV,2025-01-06T19:30:00Z,2025-01-06T21:25:00Z,1.92,1,DIURNO,2811,12
cumprida,2800,4,RV2025_OM6_E2_L14_C14,6,SBBV,SBMN,2025-01-07T11:20:00Z,2025-01-07T13:20:00Z,2.00,1,DIURNO,2811,14
cumprida,2800,5,RV2025_OM8_E1_L17_C17,8,SBMN,SBPV,2025-01-08T10:35:00Z,2025-01-08T12:35:00Z,2.00,1,DIURNO,2803,17
cumprida,2800,6,RV2025_OM8_E2_L18_C18,8,SBPV,SBMN,2025-01-08T14:00:00Z,2025-01-08T16:00:00Z,2.00,1,DIURNO,2803,18
cumprida,2800,7,RV2025_OM10_E1_L19_C19,10,SBMN,SBMN,2025-01-08T22:30:00Z,2025-01-08T23:15:00Z,0.75,1,NOTURNO,2803,19
cumprida,2800,8,RV2025_OM11_E1_L23_C23,11,SBMN,SWKO,2025-01-10T12:10:00Z,2025-01-10T13:15:00Z,1.08,1,DIURNO,2803,23
cumprida,2800,9,RV2025_OM11_E2_L24_C24,11,SWKO,SBMN,2025-01-10T14:25:00Z,2025-01-10T15:25:00Z,1.00,1,DIURNO,2803,24
cumprida,2800,10,RV2025_OM13_E1_L30_C30,13,SBMN,SWKO,2025-01-12T12:20:00Z,2025-01-12T13:10:00Z,0.83,1,DIURNO,2809,30
cumprida,2800,11,RV2025_OM13_E2_L31_C31,13,SWKO,SBMN,2025-01-12T14:40:00Z,2025-01-12T15:25:00Z,0.75,1,DIURNO,2809,31
cumprida,2800,12,RV2025_OM14_E1_L32_C32,14,SBMN,SBBE,2025-01-12T16:55:00Z,2025-01-12T20:25:00Z,3.50,1,DIURNO,2809,32
cumprida,2800,13,RV2025_OM14_E2_L33_C37,14,SBBE,SBMN,2025-01-12T22:45:00Z,2025-01-13T02:05:00Z,3.33,1,NOTURNO,2809,33;37
cumprida,2800,14,RV2025_OM16_E1_L38_C38,16,SBMN,SBTT,2025-01-15T12:00:00Z,2025-01-15T15:05:00Z,3.08,1,DIURNO,2803,38
cumprida,2800,15,RV2025_OM16_E2_L39_C39,16,SBTT,SBMN,2025-01-15T17:35:00Z,2025-01-15T20:35:00Z,3.00,1,DIURNO,2803,39
cumprida,2800,16,RV2025_OM17_E3_L42_C45,17,SBMN,SBMN,2025-01-15T22:50:00Z,2025-01-15T23:50:00Z,1.00,1,NOTURNO,2809,42;43;44;45
cumprida,2800,17,RV2025_OM18_E1_L46_C46,18,SBMN,SBSN,2025-01-21T12:05:00Z,2025-01-21T13:40:00Z,1.58,1,DIURNO,2800,46
cumprida,2800,18,RV2025_OM18_E2_L47_C47,18,SBSN,SBTS,2025-01-21T14:45:00Z,2025-01-21T16:15:00Z,1.50,1,DIURNO,2800,47
cumprida,2800,19,RV2025_OM18_E3_L48_C48,18,SBTS,SBMN,2025-01-21T18:40:00Z,2025-01-21T20:40:00Z,2.00,1,DIURNO,2800,48
cumprida,2800,20,RV2025_OM19_E1_L49_C49,19,SBMN,SBMN,2025-01-22T14:55:00Z,2025-01-22T16:00:00Z,1.08,1,DIURNO,2800,49
cumprida,2800,21,RV2025_OM21_E2_L54_C54,21,SBMN,SBMN,2025-01-22T20:50:00Z,2025-01-22T21:25:00Z,0.58,1,DIURNO,2809,54
cumprida,2800,22,RV2025_OM23_E1_L57_C57,23,SBMN,SBCZ,2025-01-24T12:10:00Z,2025-01-24T15:55:00Z,3.75,1,DIURNO,2800,57
cumprida,2800,23,RV2025_OM23_E2_L58_C58,23,SBCZ,SBMN,2025-01-24T17:05:00Z,2025-01-24T20:55:00Z,3.83,1,DIURNO,2800,58
cumprida,2800,24,RV2025_OM25_E1_L64_C64,25,SBMN,SBMN,2025-01-28T13:25:00Z,2025-01-28T14:30:00Z,1.08,1,DIURNO,2809,64
cumprida,2800,25,RV2025_OM26_E1_L65_C70,26,SBMN,SBMN,2025-01-28T23:10:00Z,2025-01-29T00:50:00Z,1.67,1,NOTURNO,2809,65;66;67;70
cumprida,2800,26,RV2025_OM28_E1_L71_C71,28,SBMN,SBMN,2025-01-29T18:15:00Z,2025-01-29T19:25:00Z,1.17,1,DIURNO,2809,71
cumprida,2803,1,RV2025_OM2_E3_L6_C6,2,SBMN,SBMY,2025-01-02T17:00:00Z,2025-01-02T18:05:00Z,1.08,1,NOTURNO,2809,6
cumprida,2803,2,RV2025_OM2_E4_L7_C7,2,SBMY,SBMN,2025-01-02T20:00:00Z,2025-01-02T21:10:00Z,1.17,1,NOTURNO,2809,7
cumprida,2803,3,RV2025_OM5_E1_L11_C11,5,SBMN,SBPV,2025-01-06T19:10:00Z,2025-01-06T21:20:00Z,2.17,1,DIURNO,2803,11
cumprida,2803,4,RV2025_OM5_E2_L13_C13,5,SBPV,SBMN,2025-01-07T14:15:00Z,2025-01-07T16:15:00Z,2.00,1,DIURNO,2803,13
cumprida,2803,5,RV2025_OM9_E1_L20_C20,9,SBMN,SBBE,2025-01-09T12:30:00Z,2025-01-09T15:55:00Z,3.42,1,DIURNO,2803,20
cumprida,2803,6,RV2025_OM9_E2_L21_C21,9,SBBE,SBSN,2025-01-09T18:10:00Z,2025-01-09T20:00:00Z,1.83,1,DIURNO,2803,21
cumprida,2803,7,RV2025_OM15_E2_L35_C35,15,SBSN,SBTS,2025-01-12T14:25:00Z,2025-01-12T16:00:00Z,1.58,1,DIURNO,2803,35
cumprida,2803,8,RV2025_OM15_E3_L36_C36,15,SBTS,SBMN,2025-01-12T18:30:00Z,2025-01-12T20:35:00Z,2.08,1,DIURNO,2803,36
cumprida,2803,9,RV2025_OM20_E1_L51_C51,20,SBMN,SBMN,2025-01-22T14:55:00Z,2025-01-22T16:00:00Z,1.08,1,DIURNO,2803,51
cumprida,2803,10,RV2025_OM20_E2_L52_C52,20,SBMN,SBMN,2025-01-22T20:50:00Z,2025-01-22T21:25:00Z,0.58,1,DIURNO,2803,52
cumprida,2803,11,RV2025_OM29_E1_L72_C72,29,SBMN,SBMN,2025-01-29T22:50:00Z,2025-01-30T00:00:00Z,1.17,1,NOTURNO,2809,72
cumprida,2809,1,RV2025_OM3_E1_L8_C8,3,SBMN,SBMN,2025-01-03T14:45:00Z,2025-01-03T16:10:00Z,1.42,1,DIURNO,2809,8
cumprida,2809,2,RV2025_OM17_E2_L41_C41,17,SBMN,SBMN,2025-01-15T14:45:00Z,2025-01-15T15:40:00Z,0.92,1,DIURNO,2809,41
cumprida,2809,3,RV2025_OM21_E1_L53_C53,21,SBMN,SBMN,2025-01-22T14:55:00Z,2025-01-22T16:00:00Z,1.08,1,DIURNO,2809,53
cumprida,2809,4,RV2025_OM19_E2_L50_C50,19,SBMN,SBMN,2025-01-22T20:50:00Z,2025-01-22T21:25:00Z,0.58,1,DIURNO,2800,50
cumprida,2809,5,RV2025_OM24_E1_L59_C59,24,SBMN,SNRH,2025-01-24T12:30:00Z,2025-01-24T15:35:00Z,3.08,1,DIURNO,2803,59
cumprida,2809,6,RV2025_OM24_E2_L60_C60,24,SNRH,SBTT,2025-01-24T16:45:00Z,2025-01-24T17:40:00Z,0.92,1,DIURNO,2803,60
cumprida,2809,7,RV2025_OM24_E3_L61_C61,24,SBTT,SWEI,2025-01-25T14:05:00Z,2025-01-25T14:50:00Z,0.75,1,DIURNO,2803,61
cumprida,2809,8,RV2025_OM24_E4_L62_C62,24,SWEI,SBTF,2025-01-25T16:45:00Z,2025-01-25T18:40:00Z,1.92,1,DIURNO,2803,62
cumprida,2811,1,RV2025_OM17_E1_L40_C40,17,SBMN,SBMN,2025-01-15T13:30:00Z,2025-01-15T14:25:00Z,0.92,1,DIURNO,2809,40
cumprida,2811,2,RV2025_OM22_E1_L55_C55,22,SBMN,SBMN,2025-01-22T14:55:00Z,2025-01-22T15:55:00Z,1.00,1,DIURNO,2811,55
cumprida,2811,3,RV2025_OM22_E2_L56_C56,22,SBMN,SBMN,2025-01-22T20:50:00Z,2025-01-22T21:25:00Z,0.58,1,DIURNO,2811,56
cumprida,2811,4,RV2025_OM27_E1_L68_C68,27,SBMN,SBMY,2025-01-28T12:05:00Z,2025-01-28T13:05:00Z,1.00,1,DIURNO,2803,68
cumprida,2811,5,RV2025_OM27_E2_L69_C69,27,SBMY,SBMN,2025-01-28T14:35:00Z,2025-01-28T15:30:00Z,0.92,1,DIURNO,2803,69
nao_cumprida,,,RV2025_OM4_E1_L9_C9,4,SBAN,SWBC,2025-01-06T12:40:00Z,2025-01-06T13:50:00Z,1.17,1,DIURNO,2803,9
nao_cumprida,,,RV2025_OM4_E2_L10_C10,4,SWBC,SBMN,2025-01-06T15:40:00Z,2025-01-06T16:45:00Z,1.08,1,DIURNO,2803,10
nao_cumprida,,,RV2025_OM7_E1_L15_C15,7,SBMN,SBBV,2025-01-07T10:40:00Z,2025-01-07T12:30:00Z,1.83,1,DIURNO,2809,15
nao_cumprida,,,RV2025_OM7_E2_L16_C16,7,SBMN,SBBV,2025-01-07T14:00:00Z,2025-01-07T15:45:00Z,1.75,1,DIURNO,2809,16
nao_cumprida,,,RV2025_OM9_E3_L22_C22,9,SBSN,SBMN,2025-01-09T20:40:00Z,2025-01-09T22:15:00Z,1.58,1,DIURNO,2803,22
nao_cumprida,,,RV2025_OM12_E1_L25_C25,12,SBMN,SBUA,2025-01-11T12:50:00Z,2025-01-11T15:00:00Z,2.17,1,DIURNO,2809,25
nao_cumprida,,,RV2025_OM12_E2_L26_C26,12,SBUA,SBYA,2025-01-11T17:20:00Z,2025-01-11T18:10:00Z,0.83,1,DIURNO,2809,26
nao_cumprida,,,RV2025_OM12_E3_L27_C27,12,SBYA,SBTT,2025-01-11T18:45:00Z,2025-01-11T20:10:00Z,1.42,1,DIURNO,2809,27
nao_cumprida,,,RV2025_OM12_E4_L28_C29,12,SBTT,SBTT,2025-01-11T21:05:00Z,2025-01-12T00:00:00Z,2.92,1,DIURNO;NOTURNO,2809,28;29
nao_cumprida,,,RV2025_OM15_E1_L34_C34,15,SBMN,SBSN,2025-01-12T11:50:00Z,2025-01-12T13:30:00Z,1.67,1,DIURNO,2803,34
nao_cumprida,,,RV2025_OM24_E5_L63_C63,24,SBTF,SBMN,2025-01-25T19:20:00Z,2025-01-25T21:00:00Z,1.67,1,DIURNO,2803,63
1 status aeronave_modelo ordem id om orig dest partida_utc chegada_utc dur_h prioridade periodo aeronave_real_2025 linhas_origem
2 cumprida 2800 1 RV2025_OM1_E1_L4_C4 1 SBMN SBMY 2025-01-02T11:55:00Z 2025-01-02T13:05:00Z 1.17 1 NOTURNO 2809 4
3 cumprida 2800 2 RV2025_OM1_E2_L5_C5 1 SBMY SBMN 2025-01-02T14:30:00Z 2025-01-02T15:35:00Z 1.08 1 NOTURNO 2809 5
4 cumprida 2800 3 RV2025_OM6_E1_L12_C12 6 SBMN SBBV 2025-01-06T19:30:00Z 2025-01-06T21:25:00Z 1.92 1 DIURNO 2811 12
5 cumprida 2800 4 RV2025_OM6_E2_L14_C14 6 SBBV SBMN 2025-01-07T11:20:00Z 2025-01-07T13:20:00Z 2.00 1 DIURNO 2811 14
6 cumprida 2800 5 RV2025_OM8_E1_L17_C17 8 SBMN SBPV 2025-01-08T10:35:00Z 2025-01-08T12:35:00Z 2.00 1 DIURNO 2803 17
7 cumprida 2800 6 RV2025_OM8_E2_L18_C18 8 SBPV SBMN 2025-01-08T14:00:00Z 2025-01-08T16:00:00Z 2.00 1 DIURNO 2803 18
8 cumprida 2800 7 RV2025_OM10_E1_L19_C19 10 SBMN SBMN 2025-01-08T22:30:00Z 2025-01-08T23:15:00Z 0.75 1 NOTURNO 2803 19
9 cumprida 2800 8 RV2025_OM11_E1_L23_C23 11 SBMN SWKO 2025-01-10T12:10:00Z 2025-01-10T13:15:00Z 1.08 1 DIURNO 2803 23
10 cumprida 2800 9 RV2025_OM11_E2_L24_C24 11 SWKO SBMN 2025-01-10T14:25:00Z 2025-01-10T15:25:00Z 1.00 1 DIURNO 2803 24
11 cumprida 2800 10 RV2025_OM13_E1_L30_C30 13 SBMN SWKO 2025-01-12T12:20:00Z 2025-01-12T13:10:00Z 0.83 1 DIURNO 2809 30
12 cumprida 2800 11 RV2025_OM13_E2_L31_C31 13 SWKO SBMN 2025-01-12T14:40:00Z 2025-01-12T15:25:00Z 0.75 1 DIURNO 2809 31
13 cumprida 2800 12 RV2025_OM14_E1_L32_C32 14 SBMN SBBE 2025-01-12T16:55:00Z 2025-01-12T20:25:00Z 3.50 1 DIURNO 2809 32
14 cumprida 2800 13 RV2025_OM14_E2_L33_C37 14 SBBE SBMN 2025-01-12T22:45:00Z 2025-01-13T02:05:00Z 3.33 1 NOTURNO 2809 33;37
15 cumprida 2800 14 RV2025_OM16_E1_L38_C38 16 SBMN SBTT 2025-01-15T12:00:00Z 2025-01-15T15:05:00Z 3.08 1 DIURNO 2803 38
16 cumprida 2800 15 RV2025_OM16_E2_L39_C39 16 SBTT SBMN 2025-01-15T17:35:00Z 2025-01-15T20:35:00Z 3.00 1 DIURNO 2803 39
17 cumprida 2800 16 RV2025_OM17_E3_L42_C45 17 SBMN SBMN 2025-01-15T22:50:00Z 2025-01-15T23:50:00Z 1.00 1 NOTURNO 2809 42;43;44;45
18 cumprida 2800 17 RV2025_OM18_E1_L46_C46 18 SBMN SBSN 2025-01-21T12:05:00Z 2025-01-21T13:40:00Z 1.58 1 DIURNO 2800 46
19 cumprida 2800 18 RV2025_OM18_E2_L47_C47 18 SBSN SBTS 2025-01-21T14:45:00Z 2025-01-21T16:15:00Z 1.50 1 DIURNO 2800 47
20 cumprida 2800 19 RV2025_OM18_E3_L48_C48 18 SBTS SBMN 2025-01-21T18:40:00Z 2025-01-21T20:40:00Z 2.00 1 DIURNO 2800 48
21 cumprida 2800 20 RV2025_OM19_E1_L49_C49 19 SBMN SBMN 2025-01-22T14:55:00Z 2025-01-22T16:00:00Z 1.08 1 DIURNO 2800 49
22 cumprida 2800 21 RV2025_OM21_E2_L54_C54 21 SBMN SBMN 2025-01-22T20:50:00Z 2025-01-22T21:25:00Z 0.58 1 DIURNO 2809 54
23 cumprida 2800 22 RV2025_OM23_E1_L57_C57 23 SBMN SBCZ 2025-01-24T12:10:00Z 2025-01-24T15:55:00Z 3.75 1 DIURNO 2800 57
24 cumprida 2800 23 RV2025_OM23_E2_L58_C58 23 SBCZ SBMN 2025-01-24T17:05:00Z 2025-01-24T20:55:00Z 3.83 1 DIURNO 2800 58
25 cumprida 2800 24 RV2025_OM25_E1_L64_C64 25 SBMN SBMN 2025-01-28T13:25:00Z 2025-01-28T14:30:00Z 1.08 1 DIURNO 2809 64
26 cumprida 2800 25 RV2025_OM26_E1_L65_C70 26 SBMN SBMN 2025-01-28T23:10:00Z 2025-01-29T00:50:00Z 1.67 1 NOTURNO 2809 65;66;67;70
27 cumprida 2800 26 RV2025_OM28_E1_L71_C71 28 SBMN SBMN 2025-01-29T18:15:00Z 2025-01-29T19:25:00Z 1.17 1 DIURNO 2809 71
28 cumprida 2803 1 RV2025_OM2_E3_L6_C6 2 SBMN SBMY 2025-01-02T17:00:00Z 2025-01-02T18:05:00Z 1.08 1 NOTURNO 2809 6
29 cumprida 2803 2 RV2025_OM2_E4_L7_C7 2 SBMY SBMN 2025-01-02T20:00:00Z 2025-01-02T21:10:00Z 1.17 1 NOTURNO 2809 7
30 cumprida 2803 3 RV2025_OM5_E1_L11_C11 5 SBMN SBPV 2025-01-06T19:10:00Z 2025-01-06T21:20:00Z 2.17 1 DIURNO 2803 11
31 cumprida 2803 4 RV2025_OM5_E2_L13_C13 5 SBPV SBMN 2025-01-07T14:15:00Z 2025-01-07T16:15:00Z 2.00 1 DIURNO 2803 13
32 cumprida 2803 5 RV2025_OM9_E1_L20_C20 9 SBMN SBBE 2025-01-09T12:30:00Z 2025-01-09T15:55:00Z 3.42 1 DIURNO 2803 20
33 cumprida 2803 6 RV2025_OM9_E2_L21_C21 9 SBBE SBSN 2025-01-09T18:10:00Z 2025-01-09T20:00:00Z 1.83 1 DIURNO 2803 21
34 cumprida 2803 7 RV2025_OM15_E2_L35_C35 15 SBSN SBTS 2025-01-12T14:25:00Z 2025-01-12T16:00:00Z 1.58 1 DIURNO 2803 35
35 cumprida 2803 8 RV2025_OM15_E3_L36_C36 15 SBTS SBMN 2025-01-12T18:30:00Z 2025-01-12T20:35:00Z 2.08 1 DIURNO 2803 36
36 cumprida 2803 9 RV2025_OM20_E1_L51_C51 20 SBMN SBMN 2025-01-22T14:55:00Z 2025-01-22T16:00:00Z 1.08 1 DIURNO 2803 51
37 cumprida 2803 10 RV2025_OM20_E2_L52_C52 20 SBMN SBMN 2025-01-22T20:50:00Z 2025-01-22T21:25:00Z 0.58 1 DIURNO 2803 52
38 cumprida 2803 11 RV2025_OM29_E1_L72_C72 29 SBMN SBMN 2025-01-29T22:50:00Z 2025-01-30T00:00:00Z 1.17 1 NOTURNO 2809 72
39 cumprida 2809 1 RV2025_OM3_E1_L8_C8 3 SBMN SBMN 2025-01-03T14:45:00Z 2025-01-03T16:10:00Z 1.42 1 DIURNO 2809 8
40 cumprida 2809 2 RV2025_OM17_E2_L41_C41 17 SBMN SBMN 2025-01-15T14:45:00Z 2025-01-15T15:40:00Z 0.92 1 DIURNO 2809 41
41 cumprida 2809 3 RV2025_OM21_E1_L53_C53 21 SBMN SBMN 2025-01-22T14:55:00Z 2025-01-22T16:00:00Z 1.08 1 DIURNO 2809 53
42 cumprida 2809 4 RV2025_OM19_E2_L50_C50 19 SBMN SBMN 2025-01-22T20:50:00Z 2025-01-22T21:25:00Z 0.58 1 DIURNO 2800 50
43 cumprida 2809 5 RV2025_OM24_E1_L59_C59 24 SBMN SNRH 2025-01-24T12:30:00Z 2025-01-24T15:35:00Z 3.08 1 DIURNO 2803 59
44 cumprida 2809 6 RV2025_OM24_E2_L60_C60 24 SNRH SBTT 2025-01-24T16:45:00Z 2025-01-24T17:40:00Z 0.92 1 DIURNO 2803 60
45 cumprida 2809 7 RV2025_OM24_E3_L61_C61 24 SBTT SWEI 2025-01-25T14:05:00Z 2025-01-25T14:50:00Z 0.75 1 DIURNO 2803 61
46 cumprida 2809 8 RV2025_OM24_E4_L62_C62 24 SWEI SBTF 2025-01-25T16:45:00Z 2025-01-25T18:40:00Z 1.92 1 DIURNO 2803 62
47 cumprida 2811 1 RV2025_OM17_E1_L40_C40 17 SBMN SBMN 2025-01-15T13:30:00Z 2025-01-15T14:25:00Z 0.92 1 DIURNO 2809 40
48 cumprida 2811 2 RV2025_OM22_E1_L55_C55 22 SBMN SBMN 2025-01-22T14:55:00Z 2025-01-22T15:55:00Z 1.00 1 DIURNO 2811 55
49 cumprida 2811 3 RV2025_OM22_E2_L56_C56 22 SBMN SBMN 2025-01-22T20:50:00Z 2025-01-22T21:25:00Z 0.58 1 DIURNO 2811 56
50 cumprida 2811 4 RV2025_OM27_E1_L68_C68 27 SBMN SBMY 2025-01-28T12:05:00Z 2025-01-28T13:05:00Z 1.00 1 DIURNO 2803 68
51 cumprida 2811 5 RV2025_OM27_E2_L69_C69 27 SBMY SBMN 2025-01-28T14:35:00Z 2025-01-28T15:30:00Z 0.92 1 DIURNO 2803 69
52 nao_cumprida RV2025_OM4_E1_L9_C9 4 SBAN SWBC 2025-01-06T12:40:00Z 2025-01-06T13:50:00Z 1.17 1 DIURNO 2803 9
53 nao_cumprida RV2025_OM4_E2_L10_C10 4 SWBC SBMN 2025-01-06T15:40:00Z 2025-01-06T16:45:00Z 1.08 1 DIURNO 2803 10
54 nao_cumprida RV2025_OM7_E1_L15_C15 7 SBMN SBBV 2025-01-07T10:40:00Z 2025-01-07T12:30:00Z 1.83 1 DIURNO 2809 15
55 nao_cumprida RV2025_OM7_E2_L16_C16 7 SBMN SBBV 2025-01-07T14:00:00Z 2025-01-07T15:45:00Z 1.75 1 DIURNO 2809 16
56 nao_cumprida RV2025_OM9_E3_L22_C22 9 SBSN SBMN 2025-01-09T20:40:00Z 2025-01-09T22:15:00Z 1.58 1 DIURNO 2803 22
57 nao_cumprida RV2025_OM12_E1_L25_C25 12 SBMN SBUA 2025-01-11T12:50:00Z 2025-01-11T15:00:00Z 2.17 1 DIURNO 2809 25
58 nao_cumprida RV2025_OM12_E2_L26_C26 12 SBUA SBYA 2025-01-11T17:20:00Z 2025-01-11T18:10:00Z 0.83 1 DIURNO 2809 26
59 nao_cumprida RV2025_OM12_E3_L27_C27 12 SBYA SBTT 2025-01-11T18:45:00Z 2025-01-11T20:10:00Z 1.42 1 DIURNO 2809 27
60 nao_cumprida RV2025_OM12_E4_L28_C29 12 SBTT SBTT 2025-01-11T21:05:00Z 2025-01-12T00:00:00Z 2.92 1 DIURNO;NOTURNO 2809 28;29
61 nao_cumprida RV2025_OM15_E1_L34_C34 15 SBMN SBSN 2025-01-12T11:50:00Z 2025-01-12T13:30:00Z 1.67 1 DIURNO 2803 34
62 nao_cumprida RV2025_OM24_E5_L63_C63 24 SBTF SBMN 2025-01-25T19:20:00Z 2025-01-25T21:00:00Z 1.67 1 DIURNO 2803 63

View File

@@ -0,0 +1,51 @@
status,aeronave,ordem,id,om,orig,dest,partida_utc,chegada_utc,dur_h,prioridade,periodo,aeronave_real_2025,linhas_origem
cumprida,2800,1,SIM_007_SBTS_SBMN,7,SBTS,SBMN,2025-01-05T08:52:04.800000Z,2025-01-05T11:40:04.800000Z,2.80,2,DIURNO,,
cumprida,2800,2,SIM_008_SBBE_SBTS,8,SBBE,SBTS,2025-01-05T16:11:09.600000Z,2025-01-05T21:53:09.600000Z,5.70,4,DIURNO,,
cumprida,2800,3,SIM_015_SBMN_SBPV,15,SBMN,SBPV,2025-01-12T01:42:18Z,2025-01-12T03:21:54Z,1.66,1,DIURNO,,
cumprida,2800,4,SIM_021_SBMN_SBPV,21,SBMN,SBPV,2025-01-16T06:26:09.600000Z,2025-01-16T08:12:21.600000Z,1.77,1,DIURNO,,
cumprida,2800,5,SIM_037_SBBV_SBMN,37,SBBV,SBMN,2025-01-23T20:18:28.800000Z,2025-01-23T21:50:52.800000Z,1.54,3,DIURNO,,
cumprida,2800,6,SIM_042_SBBE_SBSN,42,SBBE,SBSN,2025-01-25T14:54:14.400000Z,2025-01-25T16:35:02.400000Z,1.68,5,DIURNO,,
cumprida,2800,7,SIM_045_SBTS_SBMN,45,SBTS,SBMN,2025-01-28T01:21:25.200000Z,2025-01-28T03:43:01.200000Z,2.36,4,DIURNO,,
cumprida,2803,1,SIM_006_SBSN_SBBE,6,SBSN,SBBE,2025-01-05T06:29:20.400000Z,2025-01-05T08:16:44.400000Z,1.79,2,DIURNO,,
cumprida,2803,2,SIM_023_SBBV_SWBC,23,SBBV,SWBC,2025-01-17T11:36:32.400000Z,2025-01-17T12:47:20.400000Z,1.18,3,DIURNO,,
cumprida,2803,3,SIM_028_SBMN_SBTT,28,SBMN,SBTT,2025-01-19T05:45:50.400000Z,2025-01-19T07:06:14.400000Z,1.34,2,DIURNO,,
cumprida,2809,1,SIM_036_SBPV_SWBC,36,SBPV,SWBC,2025-01-23T18:49:19.200000Z,2025-01-23T21:00:07.200000Z,2.18,3,DIURNO,,
cumprida,2811,1,SIM_001_SBMN_SBMY,1,SBMN,SBMY,2025-01-01T02:22:55.200000Z,2025-01-01T03:09:07.200000Z,0.77,1,DIURNO,,
cumprida,2811,2,SIM_003_SBMN_SBPV,3,SBMN,SBPV,2025-01-04T06:06:21.600000Z,2025-01-04T07:48:57.600000Z,1.71,1,DIURNO,,
cumprida,2811,3,SIM_005_SBSN_SBTS,5,SBSN,SBTS,2025-01-05T05:32:09.600000Z,2025-01-05T09:50:09.600000Z,4.30,2,DIURNO,,
cumprida,2811,4,SIM_025_SWBC_SBMN,25,SWBC,SBMN,2025-01-18T08:18:36Z,2025-01-18T09:11:24Z,0.88,3,DIURNO,,
cumprida,2811,5,SIM_041_SBBE_SBTS,41,SBBE,SBTS,2025-01-24T12:09:03.600000Z,2025-01-24T17:48:03.600000Z,5.65,1,DIURNO,,
nao_cumprida,,,SIM_002_SBBE_SBTT,2,SBBE,SBTT,2025-01-03T00:34:55.200000Z,2025-01-03T04:33:07.200000Z,3.97,1,DIURNO,,
nao_cumprida,,,SIM_004_SBMN_SBUA,4,SBMN,SBUA,2025-01-04T12:04:51.600000Z,2025-01-04T13:55:51.600000Z,1.85,1,DIURNO,,
nao_cumprida,,,SIM_009_SBMY_SBTS,9,SBMY,SBTS,2025-01-05T20:05:16.800000Z,2025-01-05T22:20:52.800000Z,2.26,2,DIURNO,,
nao_cumprida,,,SIM_010_SBTT_SBMN,10,SBTT,SBMN,2025-01-05T21:25:37.200000Z,2025-01-05T22:33:25.200000Z,1.13,1,DIURNO,,
nao_cumprida,,,SIM_011_SBBE_SBSN,11,SBBE,SBSN,2025-01-07T12:04:04.800000Z,2025-01-07T13:40:40.800000Z,1.61,1,DIURNO,,
nao_cumprida,,,SIM_012_SWBC_SBSN,12,SWBC,SBSN,2025-01-08T16:32:16.800000Z,2025-01-08T18:52:40.800000Z,2.34,3,DIURNO,,
nao_cumprida,,,SIM_013_SBMN_SBBV,13,SBMN,SBBV,2025-01-09T23:25:44.400000Z,2025-01-10T01:05:20.400000Z,1.66,2,DIURNO,,
nao_cumprida,,,SIM_014_SBMN_SBTS,14,SBMN,SBTS,2025-01-11T09:41:20.400000Z,2025-01-11T12:08:20.400000Z,2.45,1,DIURNO,,
nao_cumprida,,,SIM_016_SBPV_SBMN,16,SBPV,SBMN,2025-01-12T18:25:08.400000Z,2025-01-12T20:11:56.400000Z,1.78,1,DIURNO,,
nao_cumprida,,,SIM_017_SBSN_SBMN,17,SBSN,SBMN,2025-01-13T06:27:18Z,2025-01-13T07:41:42Z,1.24,4,DIURNO,,
nao_cumprida,,,SIM_018_SBSN_SBBV,18,SBSN,SBBV,2025-01-14T02:28:12Z,2025-01-14T04:36:36Z,2.14,1,DIURNO,,
nao_cumprida,,,SIM_019_SBBV_SBMN,19,SBBV,SBMN,2025-01-15T07:32:56.400000Z,2025-01-15T09:12:32.400000Z,1.66,2,DIURNO,,
nao_cumprida,,,SIM_020_SWBC_SBTS,20,SWBC,SBTS,2025-01-16T02:14:27.600000Z,2025-01-16T04:25:51.600000Z,2.19,4,DIURNO,,
nao_cumprida,,,SIM_022_SBMN_SBBV,22,SBMN,SBBV,2025-01-16T12:38:16.800000Z,2025-01-16T14:04:40.800000Z,1.44,1,DIURNO,,
nao_cumprida,,,SIM_024_SBMY_SBBV,24,SBMY,SBBV,2025-01-17T22:04:04.800000Z,2025-01-18T00:30:28.800000Z,2.44,1,DIURNO,,
nao_cumprida,,,SIM_026_SBUA_SBTT,26,SBUA,SBTT,2025-01-18T20:18:46.800000Z,2025-01-18T21:21:10.800000Z,1.04,3,DIURNO,,
nao_cumprida,,,SIM_027_SBTS_SBMY,27,SBTS,SBMY,2025-01-19T05:22:55.200000Z,2025-01-19T07:42:07.200000Z,2.32,1,DIURNO,,
nao_cumprida,,,SIM_029_SBTT_SBSN,29,SBTT,SBSN,2025-01-20T02:58:12Z,2025-01-20T05:46:12Z,2.80,2,DIURNO,,
nao_cumprida,,,SIM_030_SWBC_SBSN,30,SWBC,SBSN,2025-01-20T15:51:10.800000Z,2025-01-20T18:05:34.800000Z,2.24,2,DIURNO,,
nao_cumprida,,,SIM_031_SBMN_SBTT,31,SBMN,SBTT,2025-01-21T19:19:40.800000Z,2025-01-21T20:35:52.800000Z,1.27,2,DIURNO,,
nao_cumprida,,,SIM_032_SBMN_SBBE,32,SBMN,SBBE,2025-01-21T19:28:22.800000Z,2025-01-21T22:36:46.800000Z,3.14,5,DIURNO,,
nao_cumprida,,,SIM_033_SBBV_SBSN,33,SBBV,SBSN,2025-01-22T22:56:56.400000Z,2025-01-23T00:56:56.400000Z,2.00,1,DIURNO,,
nao_cumprida,,,SIM_034_SBTS_SBMN,34,SBTS,SBMN,2025-01-23T00:14:45.600000Z,2025-01-23T02:54:57.600000Z,2.67,2,DIURNO,,
nao_cumprida,,,SIM_035_SBTT_SBMN,35,SBTT,SBMN,2025-01-23T02:00:57.600000Z,2025-01-23T03:12:21.600000Z,1.19,1,DIURNO,,
nao_cumprida,,,SIM_038_SBBV_SBBE,38,SBBV,SBBE,2025-01-23T23:08:38.400000Z,2025-01-24T02:18:50.400000Z,3.17,2,DIURNO,,
nao_cumprida,,,SIM_039_SBSN_SBMN,39,SBSN,SBMN,2025-01-24T02:44:38.400000Z,2025-01-24T03:59:02.400000Z,1.24,1,DIURNO,,
nao_cumprida,,,SIM_040_SBSN_SBMN,40,SBSN,SBMN,2025-01-24T07:16:48Z,2025-01-24T08:30:36Z,1.23,4,DIURNO,,
nao_cumprida,,,SIM_043_SBMN_SBPV,43,SBMN,SBPV,2025-01-26T17:47:06Z,2025-01-26T19:21:54Z,1.58,4,DIURNO,,
nao_cumprida,,,SIM_044_SBBE_SBPV,44,SBBE,SBPV,2025-01-27T08:03:00Z,2025-01-27T12:37:48Z,4.58,3,DIURNO,,
nao_cumprida,,,SIM_046_SBMN_SBUA,46,SBMN,SBUA,2025-01-28T01:26:06Z,2025-01-28T03:14:42Z,1.81,3,DIURNO,,
nao_cumprida,,,SIM_047_SWBC_SBTS,47,SWBC,SBTS,2025-01-28T22:14:31.200000Z,2025-01-29T00:03:07.200000Z,1.81,2,DIURNO,,
nao_cumprida,,,SIM_048_SBSN_SBUA,48,SBSN,SBUA,2025-01-29T19:44:38.400000Z,2025-01-29T22:51:14.400000Z,3.11,1,DIURNO,,
nao_cumprida,,,SIM_049_SBMN_SBMY,49,SBMN,SBMY,2025-01-30T17:21:57.600000Z,2025-01-30T18:08:09.600000Z,0.77,4,DIURNO,,
nao_cumprida,,,SIM_050_SBTT_SBPV,50,SBTT,SBPV,2025-01-31T12:02:13.200000Z,2025-01-31T13:22:01.200000Z,1.33,1,DIURNO,,
1 status aeronave ordem id om orig dest partida_utc chegada_utc dur_h prioridade periodo aeronave_real_2025 linhas_origem
2 cumprida 2800 1 SIM_007_SBTS_SBMN 7 SBTS SBMN 2025-01-05T08:52:04.800000Z 2025-01-05T11:40:04.800000Z 2.80 2 DIURNO
3 cumprida 2800 2 SIM_008_SBBE_SBTS 8 SBBE SBTS 2025-01-05T16:11:09.600000Z 2025-01-05T21:53:09.600000Z 5.70 4 DIURNO
4 cumprida 2800 3 SIM_015_SBMN_SBPV 15 SBMN SBPV 2025-01-12T01:42:18Z 2025-01-12T03:21:54Z 1.66 1 DIURNO
5 cumprida 2800 4 SIM_021_SBMN_SBPV 21 SBMN SBPV 2025-01-16T06:26:09.600000Z 2025-01-16T08:12:21.600000Z 1.77 1 DIURNO
6 cumprida 2800 5 SIM_037_SBBV_SBMN 37 SBBV SBMN 2025-01-23T20:18:28.800000Z 2025-01-23T21:50:52.800000Z 1.54 3 DIURNO
7 cumprida 2800 6 SIM_042_SBBE_SBSN 42 SBBE SBSN 2025-01-25T14:54:14.400000Z 2025-01-25T16:35:02.400000Z 1.68 5 DIURNO
8 cumprida 2800 7 SIM_045_SBTS_SBMN 45 SBTS SBMN 2025-01-28T01:21:25.200000Z 2025-01-28T03:43:01.200000Z 2.36 4 DIURNO
9 cumprida 2803 1 SIM_006_SBSN_SBBE 6 SBSN SBBE 2025-01-05T06:29:20.400000Z 2025-01-05T08:16:44.400000Z 1.79 2 DIURNO
10 cumprida 2803 2 SIM_023_SBBV_SWBC 23 SBBV SWBC 2025-01-17T11:36:32.400000Z 2025-01-17T12:47:20.400000Z 1.18 3 DIURNO
11 cumprida 2803 3 SIM_028_SBMN_SBTT 28 SBMN SBTT 2025-01-19T05:45:50.400000Z 2025-01-19T07:06:14.400000Z 1.34 2 DIURNO
12 cumprida 2809 1 SIM_036_SBPV_SWBC 36 SBPV SWBC 2025-01-23T18:49:19.200000Z 2025-01-23T21:00:07.200000Z 2.18 3 DIURNO
13 cumprida 2811 1 SIM_001_SBMN_SBMY 1 SBMN SBMY 2025-01-01T02:22:55.200000Z 2025-01-01T03:09:07.200000Z 0.77 1 DIURNO
14 cumprida 2811 2 SIM_003_SBMN_SBPV 3 SBMN SBPV 2025-01-04T06:06:21.600000Z 2025-01-04T07:48:57.600000Z 1.71 1 DIURNO
15 cumprida 2811 3 SIM_005_SBSN_SBTS 5 SBSN SBTS 2025-01-05T05:32:09.600000Z 2025-01-05T09:50:09.600000Z 4.30 2 DIURNO
16 cumprida 2811 4 SIM_025_SWBC_SBMN 25 SWBC SBMN 2025-01-18T08:18:36Z 2025-01-18T09:11:24Z 0.88 3 DIURNO
17 cumprida 2811 5 SIM_041_SBBE_SBTS 41 SBBE SBTS 2025-01-24T12:09:03.600000Z 2025-01-24T17:48:03.600000Z 5.65 1 DIURNO
18 nao_cumprida SIM_002_SBBE_SBTT 2 SBBE SBTT 2025-01-03T00:34:55.200000Z 2025-01-03T04:33:07.200000Z 3.97 1 DIURNO
19 nao_cumprida SIM_004_SBMN_SBUA 4 SBMN SBUA 2025-01-04T12:04:51.600000Z 2025-01-04T13:55:51.600000Z 1.85 1 DIURNO
20 nao_cumprida SIM_009_SBMY_SBTS 9 SBMY SBTS 2025-01-05T20:05:16.800000Z 2025-01-05T22:20:52.800000Z 2.26 2 DIURNO
21 nao_cumprida SIM_010_SBTT_SBMN 10 SBTT SBMN 2025-01-05T21:25:37.200000Z 2025-01-05T22:33:25.200000Z 1.13 1 DIURNO
22 nao_cumprida SIM_011_SBBE_SBSN 11 SBBE SBSN 2025-01-07T12:04:04.800000Z 2025-01-07T13:40:40.800000Z 1.61 1 DIURNO
23 nao_cumprida SIM_012_SWBC_SBSN 12 SWBC SBSN 2025-01-08T16:32:16.800000Z 2025-01-08T18:52:40.800000Z 2.34 3 DIURNO
24 nao_cumprida SIM_013_SBMN_SBBV 13 SBMN SBBV 2025-01-09T23:25:44.400000Z 2025-01-10T01:05:20.400000Z 1.66 2 DIURNO
25 nao_cumprida SIM_014_SBMN_SBTS 14 SBMN SBTS 2025-01-11T09:41:20.400000Z 2025-01-11T12:08:20.400000Z 2.45 1 DIURNO
26 nao_cumprida SIM_016_SBPV_SBMN 16 SBPV SBMN 2025-01-12T18:25:08.400000Z 2025-01-12T20:11:56.400000Z 1.78 1 DIURNO
27 nao_cumprida SIM_017_SBSN_SBMN 17 SBSN SBMN 2025-01-13T06:27:18Z 2025-01-13T07:41:42Z 1.24 4 DIURNO
28 nao_cumprida SIM_018_SBSN_SBBV 18 SBSN SBBV 2025-01-14T02:28:12Z 2025-01-14T04:36:36Z 2.14 1 DIURNO
29 nao_cumprida SIM_019_SBBV_SBMN 19 SBBV SBMN 2025-01-15T07:32:56.400000Z 2025-01-15T09:12:32.400000Z 1.66 2 DIURNO
30 nao_cumprida SIM_020_SWBC_SBTS 20 SWBC SBTS 2025-01-16T02:14:27.600000Z 2025-01-16T04:25:51.600000Z 2.19 4 DIURNO
31 nao_cumprida SIM_022_SBMN_SBBV 22 SBMN SBBV 2025-01-16T12:38:16.800000Z 2025-01-16T14:04:40.800000Z 1.44 1 DIURNO
32 nao_cumprida SIM_024_SBMY_SBBV 24 SBMY SBBV 2025-01-17T22:04:04.800000Z 2025-01-18T00:30:28.800000Z 2.44 1 DIURNO
33 nao_cumprida SIM_026_SBUA_SBTT 26 SBUA SBTT 2025-01-18T20:18:46.800000Z 2025-01-18T21:21:10.800000Z 1.04 3 DIURNO
34 nao_cumprida SIM_027_SBTS_SBMY 27 SBTS SBMY 2025-01-19T05:22:55.200000Z 2025-01-19T07:42:07.200000Z 2.32 1 DIURNO
35 nao_cumprida SIM_029_SBTT_SBSN 29 SBTT SBSN 2025-01-20T02:58:12Z 2025-01-20T05:46:12Z 2.80 2 DIURNO
36 nao_cumprida SIM_030_SWBC_SBSN 30 SWBC SBSN 2025-01-20T15:51:10.800000Z 2025-01-20T18:05:34.800000Z 2.24 2 DIURNO
37 nao_cumprida SIM_031_SBMN_SBTT 31 SBMN SBTT 2025-01-21T19:19:40.800000Z 2025-01-21T20:35:52.800000Z 1.27 2 DIURNO
38 nao_cumprida SIM_032_SBMN_SBBE 32 SBMN SBBE 2025-01-21T19:28:22.800000Z 2025-01-21T22:36:46.800000Z 3.14 5 DIURNO
39 nao_cumprida SIM_033_SBBV_SBSN 33 SBBV SBSN 2025-01-22T22:56:56.400000Z 2025-01-23T00:56:56.400000Z 2.00 1 DIURNO
40 nao_cumprida SIM_034_SBTS_SBMN 34 SBTS SBMN 2025-01-23T00:14:45.600000Z 2025-01-23T02:54:57.600000Z 2.67 2 DIURNO
41 nao_cumprida SIM_035_SBTT_SBMN 35 SBTT SBMN 2025-01-23T02:00:57.600000Z 2025-01-23T03:12:21.600000Z 1.19 1 DIURNO
42 nao_cumprida SIM_038_SBBV_SBBE 38 SBBV SBBE 2025-01-23T23:08:38.400000Z 2025-01-24T02:18:50.400000Z 3.17 2 DIURNO
43 nao_cumprida SIM_039_SBSN_SBMN 39 SBSN SBMN 2025-01-24T02:44:38.400000Z 2025-01-24T03:59:02.400000Z 1.24 1 DIURNO
44 nao_cumprida SIM_040_SBSN_SBMN 40 SBSN SBMN 2025-01-24T07:16:48Z 2025-01-24T08:30:36Z 1.23 4 DIURNO
45 nao_cumprida SIM_043_SBMN_SBPV 43 SBMN SBPV 2025-01-26T17:47:06Z 2025-01-26T19:21:54Z 1.58 4 DIURNO
46 nao_cumprida SIM_044_SBBE_SBPV 44 SBBE SBPV 2025-01-27T08:03:00Z 2025-01-27T12:37:48Z 4.58 3 DIURNO
47 nao_cumprida SIM_046_SBMN_SBUA 46 SBMN SBUA 2025-01-28T01:26:06Z 2025-01-28T03:14:42Z 1.81 3 DIURNO
48 nao_cumprida SIM_047_SWBC_SBTS 47 SWBC SBTS 2025-01-28T22:14:31.200000Z 2025-01-29T00:03:07.200000Z 1.81 2 DIURNO
49 nao_cumprida SIM_048_SBSN_SBUA 48 SBSN SBUA 2025-01-29T19:44:38.400000Z 2025-01-29T22:51:14.400000Z 3.11 1 DIURNO
50 nao_cumprida SIM_049_SBMN_SBMY 49 SBMN SBMY 2025-01-30T17:21:57.600000Z 2025-01-30T18:08:09.600000Z 0.77 4 DIURNO
51 nao_cumprida SIM_050_SBTT_SBPV 50 SBTT SBPV 2025-01-31T12:02:13.200000Z 2025-01-31T13:22:01.200000Z 1.33 1 DIURNO

View File

@@ -0,0 +1,23 @@
status,aeronave,ordem,id,om,orig,dest,partida_utc,chegada_utc,dur_h,prioridade,periodo,aeronave_real_2025,linhas_origem
cumprida,2800,1,SIM_007_SBMN_SBCO,7,SBMN,SBCO,2025-01-09T02:09:28.800000Z,2025-01-09T07:58:40.800000Z,5.82,1,DIURNO,,
cumprida,2800,2,SIM_010_SBCY_SBMN,10,SBCY,SBMN,2025-01-11T23:29:27.600000Z,2025-01-12T03:09:39.600000Z,3.67,3,DIURNO,,
cumprida,2800,3,SIM_012_SWCA_SBTT,12,SWCA,SBTT,2025-01-12T14:52:01.200000Z,2025-01-12T16:14:49.200000Z,1.38,3,DIURNO,,
cumprida,2800,4,SIM_019_SBMN_SWCA,19,SBMN,SWCA,2025-01-29T04:57:54Z,2025-01-29T06:36:54Z,1.65,1,DIURNO,,
cumprida,2800,5,SIM_020_SWCA_SBCO,20,SWCA,SBCO,2025-01-31T03:09:10.800000Z,2025-01-31T08:39:46.800000Z,5.51,2,DIURNO,,
inspecao,2800,,INSP_2800_3,INSP 2A,SBMN,SBMN,2025-01-12T16:14:49.200000Z,2025-01-16T16:14:49.200000Z,96.00,,,,
cumprida,2803,1,SIM_006_SWBC_SBAN,6,SWBC,SBAN,2025-01-06T21:08:24Z,2025-01-07T00:56:24Z,3.80,2,DIURNO,,
cumprida,2809,1,SIM_001_SBBE_SBMN,1,SBBE,SBMN,2025-01-02T07:53:34.800000Z,2025-01-02T11:07:22.800000Z,3.23,2,DIURNO,,
cumprida,2809,2,SIM_002_SBPV_SBOI,2,SBPV,SBOI,2025-01-03T03:03:10.800000Z,2025-01-03T09:37:58.800000Z,6.58,3,DIURNO,,
cumprida,2809,3,SIM_005_SBMY_SBYS,5,SBMY,SBYS,2025-01-04T19:37:19.200000Z,2025-01-05T01:06:43.200000Z,5.49,1,DIURNO,,
cumprida,2809,4,SIM_008_SBPV_SBMN,8,SBPV,SBMN,2025-01-09T23:11:34.800000Z,2025-01-10T00:51:10.800000Z,1.66,2,DIURNO,,
cumprida,2809,5,SIM_011_SBSN_SBVH,11,SBSN,SBVH,2025-01-12T01:26:24Z,2025-01-12T04:15:36Z,2.82,1,DIURNO,,
cumprida,2809,6,SIM_016_SBTF_SBMN,16,SBTF,SBMN,2025-01-24T15:25:55.200000Z,2025-01-24T16:40:19.200000Z,1.24,3,DIURNO,,
inspecao,2809,,INSP_2809_3,INSP 2A,SBMN,SBMN,2025-01-05T01:06:43.200000Z,2025-01-09T01:06:43.200000Z,96.00,,,,
cumprida,2811,1,SIM_003_SBMN_SBUY,3,SBMN,SBUY,2025-01-03T09:50:20.400000Z,2025-01-03T16:17:56.400000Z,6.46,2,DIURNO,,
cumprida,2811,2,SIM_004_SWCA_SBMN,4,SWCA,SBMN,2025-01-04T03:01:19.200000Z,2025-01-04T04:51:43.200000Z,1.84,3,DIURNO,,
cumprida,2811,3,SIM_009_SBUY_SBCC,9,SBUY,SBCC,2025-01-11T12:27:50.400000Z,2025-01-11T17:17:02.400000Z,4.82,1,DIURNO,,
cumprida,2811,4,SIM_013_SBUY_SBMN,13,SBUY,SBMN,2025-01-15T01:13:04.800000Z,2025-01-15T07:31:40.800000Z,6.31,3,DIURNO,,
cumprida,2811,5,SIM_014_SBBE_SBUA,14,SBBE,SBUA,2025-01-20T05:25:19.200000Z,2025-01-20T10:31:55.200000Z,5.11,2,DIURNO,,
cumprida,2811,6,SIM_015_SBBE_SBTS,15,SBBE,SBTS,2025-01-21T05:22:01.200000Z,2025-01-21T10:28:37.200000Z,5.11,4,DIURNO,,
cumprida,2811,7,SIM_017_SWBC_SBCC,17,SWBC,SBCC,2025-01-27T03:48:10.800000Z,2025-01-27T07:24:46.800000Z,3.61,2,DIURNO,,
cumprida,2811,8,SIM_018_SBMY_SBPV,18,SBMY,SBPV,2025-01-28T01:55:19.200000Z,2025-01-28T03:00:07.200000Z,1.08,2,DIURNO,,
1 status aeronave ordem id om orig dest partida_utc chegada_utc dur_h prioridade periodo aeronave_real_2025 linhas_origem
2 cumprida 2800 1 SIM_007_SBMN_SBCO 7 SBMN SBCO 2025-01-09T02:09:28.800000Z 2025-01-09T07:58:40.800000Z 5.82 1 DIURNO
3 cumprida 2800 2 SIM_010_SBCY_SBMN 10 SBCY SBMN 2025-01-11T23:29:27.600000Z 2025-01-12T03:09:39.600000Z 3.67 3 DIURNO
4 cumprida 2800 3 SIM_012_SWCA_SBTT 12 SWCA SBTT 2025-01-12T14:52:01.200000Z 2025-01-12T16:14:49.200000Z 1.38 3 DIURNO
5 cumprida 2800 4 SIM_019_SBMN_SWCA 19 SBMN SWCA 2025-01-29T04:57:54Z 2025-01-29T06:36:54Z 1.65 1 DIURNO
6 cumprida 2800 5 SIM_020_SWCA_SBCO 20 SWCA SBCO 2025-01-31T03:09:10.800000Z 2025-01-31T08:39:46.800000Z 5.51 2 DIURNO
7 inspecao 2800 INSP_2800_3 INSP 2A SBMN SBMN 2025-01-12T16:14:49.200000Z 2025-01-16T16:14:49.200000Z 96.00
8 cumprida 2803 1 SIM_006_SWBC_SBAN 6 SWBC SBAN 2025-01-06T21:08:24Z 2025-01-07T00:56:24Z 3.80 2 DIURNO
9 cumprida 2809 1 SIM_001_SBBE_SBMN 1 SBBE SBMN 2025-01-02T07:53:34.800000Z 2025-01-02T11:07:22.800000Z 3.23 2 DIURNO
10 cumprida 2809 2 SIM_002_SBPV_SBOI 2 SBPV SBOI 2025-01-03T03:03:10.800000Z 2025-01-03T09:37:58.800000Z 6.58 3 DIURNO
11 cumprida 2809 3 SIM_005_SBMY_SBYS 5 SBMY SBYS 2025-01-04T19:37:19.200000Z 2025-01-05T01:06:43.200000Z 5.49 1 DIURNO
12 cumprida 2809 4 SIM_008_SBPV_SBMN 8 SBPV SBMN 2025-01-09T23:11:34.800000Z 2025-01-10T00:51:10.800000Z 1.66 2 DIURNO
13 cumprida 2809 5 SIM_011_SBSN_SBVH 11 SBSN SBVH 2025-01-12T01:26:24Z 2025-01-12T04:15:36Z 2.82 1 DIURNO
14 cumprida 2809 6 SIM_016_SBTF_SBMN 16 SBTF SBMN 2025-01-24T15:25:55.200000Z 2025-01-24T16:40:19.200000Z 1.24 3 DIURNO
15 inspecao 2809 INSP_2809_3 INSP 2A SBMN SBMN 2025-01-05T01:06:43.200000Z 2025-01-09T01:06:43.200000Z 96.00
16 cumprida 2811 1 SIM_003_SBMN_SBUY 3 SBMN SBUY 2025-01-03T09:50:20.400000Z 2025-01-03T16:17:56.400000Z 6.46 2 DIURNO
17 cumprida 2811 2 SIM_004_SWCA_SBMN 4 SWCA SBMN 2025-01-04T03:01:19.200000Z 2025-01-04T04:51:43.200000Z 1.84 3 DIURNO
18 cumprida 2811 3 SIM_009_SBUY_SBCC 9 SBUY SBCC 2025-01-11T12:27:50.400000Z 2025-01-11T17:17:02.400000Z 4.82 1 DIURNO
19 cumprida 2811 4 SIM_013_SBUY_SBMN 13 SBUY SBMN 2025-01-15T01:13:04.800000Z 2025-01-15T07:31:40.800000Z 6.31 3 DIURNO
20 cumprida 2811 5 SIM_014_SBBE_SBUA 14 SBBE SBUA 2025-01-20T05:25:19.200000Z 2025-01-20T10:31:55.200000Z 5.11 2 DIURNO
21 cumprida 2811 6 SIM_015_SBBE_SBTS 15 SBBE SBTS 2025-01-21T05:22:01.200000Z 2025-01-21T10:28:37.200000Z 5.11 4 DIURNO
22 cumprida 2811 7 SIM_017_SWBC_SBCC 17 SWBC SBCC 2025-01-27T03:48:10.800000Z 2025-01-27T07:24:46.800000Z 3.61 2 DIURNO
23 cumprida 2811 8 SIM_018_SBMY_SBPV 18 SBMY SBPV 2025-01-28T01:55:19.200000Z 2025-01-28T03:00:07.200000Z 1.08 2 DIURNO

View File

@@ -49,16 +49,72 @@ db/pre_process/relatorio_ciclo_inspecoes_c105_2805_2026-06-15/relatorio_ciclo_in
db/pre_process/relatorio_ciclo_inspecoes_c105_2805_2026-06-15/relatorio_ciclo_inspecoes_c105_2805_2026-06-15_inspecoes.csv
```
## Modelo OAMRP inicial
## Modelo OAMRP v3 (modelo atual)
O arquivo `software/oamrp_v1.py` contém um protótipo de modelo MIP em PuLP para o Aircraft Routing do Esquadrão Arara. O modelo usa dados sintéticos/substituíveis de aeronaves e missões e busca maximizar o cumprimento ponderado de missões opcionais.
O arquivo `software/oamrp_v3.py` é o modelo principal. Implementa o *Operational Aircraft Maintenance Routing Problem* (Al-Thani, Ben Ahmed, Haouari, 2016, Transportation Research Part C, vol. 72, pp. 2944) sobre uma rede espaço-tempo com arcos de ferry.
Restrições representadas no protótipo:
### Rede espaço-tempo
- continuidade espacial e temporal por aeronave;
- turnaround mínimo entre missões;
- limite de horas de célula até a próxima inspeção;
- base inicial e horas acumuladas reais de cada aeronave como parâmetros de entrada.
A rede tem quatro tipos de nó:
| Tipo | Descrição |
| --- | --- |
| `origem` | Nó de partida de cada aeronave (base inicial, t=0) |
| `missao` | Uma missão específica; `aeronave_id=None` (qualquer aeronave pode voar) |
| `inspecao` | Nó de manutenção; exclusivo de uma aeronave (`aeronave_id=k`) |
| `sumidouro` | Nó de chegada ao fim do horizonte |
Os arcos são: `inicio`, `missao_missao`, `missao_insp`, `insp_missao`, `missao_fim` e `ferry` (quando bases forem distintas). Ferry usa velocidade 430 km/h e distância haversine.
### Nomenclatura (Al-Thani, 2016)
| Símbolo | Significado | Campo no código |
| --- | --- | --- |
| F | Intervalo máximo legal entre manutenções | `InspecaoParam.f_max` |
| f_k | Horas acumuladas no início | `Aeronave.f0` |
| LRT = F f_k | Tempo legal restante (decresce) | `lrt_inicial()` |
| H[k,i,nid] | Horas acumuladas em tempo real (cresce, reseta na inspeção) | `H[k,seq,nid]` |
### Restrições implementadas
- **C1**: cada missão coberta por no máximo uma aeronave.
- **C2**: conservação de fluxo em nós de missão e inspeção.
- **C3**: continuidade espacial (base de chegada = base de partida do próximo arco; ferry gerado automaticamente).
- **C4**: continuidade temporal (partida ≥ chegada + TAT 1,5h).
- **C5**: missões EVAM (`obrigatoria=True`) devem ser cumpridas.
- **C6**: `H[k,i,nid] ≤ f_max` para cada tipo de inspeção, com reset após nó de inspeção.
- **C9**: downtime de inspeção (aeronave fica indisponível durante o slot de manutenção).
- **C11**: slot único de inspeção por base — no máximo uma aeronave em manutenção a cada instante.
### Objetivos
- **L1**: maximizar cobertura ponderada `Σ prioridade_m × z_m` (missões cobertas).
- **L2**: maximizar horas voadas, dado o L1 fixado.
### Execução típica
```powershell
python software/oamrp_v3.py --sintetico --seed 42 --aleatorio --ofrag db/processed/ofrag.csv
```
Gera missões sintéticas cross-base, sorteia `f0` aleatório e carrega prioridades do OFRAG.
### Scripts auxiliares
| Script | Função |
| --- | --- |
| `software/gerar_ofrag.py` | Gera `db/processed/ofrag.csv` com ordens de missão (OFRAG), prioridades 15 e tipo |
| `software/visualizar_resultado.py` | Lê o CSV de resultado e gera `db/processed/mapa_rotas.html` (mapa Folium interativo) |
### Resultado validado (2026-06-16)
Rodada sintética com 50 missões, seed 42, horas iniciais aleatórias e OFRAG: 50/50 missões cumpridas, solver CBC Optimal, todas as 4 EVAMs operacionais.
---
## Modelo OAMRP v1 (protótipo inicial)
O arquivo `software/oamrp_v1.py` contém o protótipo inicial, substituído pelo v3. Mantido no repositório para referência histórica.
Campos extraídos e estruturados:
@@ -172,3 +228,30 @@ O relatorio de validacao esta em:
```text
db/pre_process/registro_voo_2025/validacao_registro_voo_2025.md
```
## Modelo OAMRP v2
O arquivo `software/oamrp_v2.py` e a primeira versao do modelo usando o registro de voo 2025 consolidado.
Entradas principais:
- `db/processed/registro_voo_2025_consolidado.csv`;
- janela padrao de `2025-01-01` ate `2025-02-01` exclusivo;
- aeronaves reais `2800`, `2803`, `2809` e `2811`;
- base inicial provisoria `SBMN`;
- base de manutencao `SBMN`;
- prioridade uniforme: todas as missoes recebem peso 1.
Saida principal:
```text
db/processed/resultado_oamrp_jan_2025.csv
```
Resultado da primeira execucao:
- status do solver: `Optimal`;
- missoes cumpridas: 50/61;
- prioridade obtida: 50/61.
Limitacao atual: o limite de horas foi deixado alto para validar primeiro a continuidade espacial/temporal. A calibracao com dados reais de manutencao ainda deve ser feita em etapa posterior.

View File

@@ -2,11 +2,11 @@
## Colaboradores
| Tag | Nome | Papel inicial |
| --- | --- | --- |
| `VTO` | Vitor Cesa | Organização do repositório, estrutura inicial, pré-processamento e documentação |
| `GNR` | Generoso | Colaborador |
| `JOM` | João Marcos | Colaborador e origem inicial do relatório de inspeções |
| Tag | Nome | Gitea | Papel inicial |
| --- | --- | --- | --- |
| `VTO` | Vitor Cesa | [@vitorcesavc](https://git.ppgao.ita.br/vitorcesavc) | Organização do repositório, estrutura inicial, pré-processamento e documentação |
| `GNR` | Generoso | — | Colaborador |
| `JOM` | João Marcos | — | Colaborador e origem inicial do relatório de inspeções |
## Instituição e contexto

View File

@@ -14,6 +14,31 @@ Formato:
---
## v0.10 — VTO — 2026-06
### Alterado
- **Set Covering → Set Partitioning (C1):** a restrição de cobertura de missões foi alterada de `Σ_k y_entrada ≤ 1` (Set Covering — missão opcional) para `Σ_k y_entrada = 1` (Set Partitioning — toda missão obrigatória). Isso reflete a premissa operacional de que cada missão deve ser realizada por exatamente uma aeronave, sem possibilidade de missões descobertas. A distinção anterior entre missões "hard" (EVAM) e "opcionais" foi eliminada.
- **Número padrão de missões sintéticas reduzido de 50 para 20** (`--n-missoes`, `n_evam` de 5 para 4): Set Partitioning é mais restritivo e, com janelas de tempo fixas e restrições de inspeção simultâneas, 50 missões tornavam o MIP difícil de resolver ou inviável. Com 20 missões, a factibilidade é garantida na margem operacional de 4 aeronaves.
- **Time limit padrão do solver CBC fixado em 120 s** (era ilimitado). O solver agora nunca trava indefinidamente; retorna a melhor solução encontrada dentro do prazo. O CLI mantém `--time-limit` para ajuste.
- **Correção de display do status do solver:** PuLP redefinía `mdl.status` para "Not Solved" ao reutilizar o modelo em L2 (após `setObjective`). O status de L1 é agora preservado quando L2 reporta 0, evitando exibição enganosa.
- **Cenário `f0_cenario_insp_grande.json` revisado:** apenas a matrícula 2803 recebe `f0=595h` para INSP 2A (F_max=600h, downtime=4 dias), forçando a inspeção aparecer no Gantt. As demais matrículas partem com `f0=0` (plena disponibilidade), evitando que o problema se torne inviável por múltiplos relógios apertados simultaneamente.
### Adicionado
- `db/processed/planejamento_insp_grande.html`: visualização Gantt + mapa + tabela para o cenário com inspeção de 4 dias (INSP 2A — INSP CHECK 2A, 96 h de solo).
### Limitações conhecidas (para contribuidores)
1. **Escopo validado restrito:** Set Partitioning foi validado apenas para o cenário sintético com 20 missões, seed 42, 4 aeronaves e somente uma matrícula (2803) com LRT apertado. Fora desse envelope, o MIP pode ser infeasible ou ultrapassar o time limit sem solução.
2. **Missões sintéticas incluem bases fora da Amazônia** (SBBR, SBGL, SBYS etc.) geradas aleatoriamente pelo seed. Para estudos de caso realistas, o gerador deve ser restrito ao conjunto de bases do Esquadrão Arara.
3. **Folga negativa no relatório:** quando uma aeronave realiza inspeção dentro do horizonte, o campo `folga` exibido (`LRT_ini horas_celula_total`) pode ser negativo porque não desconta o reset do relógio pós-inspeção. Isso é um bug de display; a restrição `H ≤ F_max` está correta no modelo.
4. **LRT muito curto (< 5 h) pode gerar instabilidade numérica no big-M:** o cenário foi configurado com LRT_ini=5 h para 2803, que está próximo do limiar onde o big-M deixa de ser eficaz. Valores menores podem permitir que o solver ignore a restrição por tolerância numérica (ε ≈ 1 e-4 do CBC).
5. **Aeronave 2811 fica ociosa neste cenário:** o solver atribui todas as 20 missões a 3 aeronaves. Isso é matematicamente ótimo, mas pode não ser desejável operacionalmente. Considerar restrição de equilíbrio de carga em versões futuras.
6. **Status "Not Solved" após L2:** a correção atual usa o status de L1 como proxy. Se L1 retornar apenas uma solução feasível (não ótima, por time limit), o modelo pode ser reportado como "Optimal" quando não é. Monitorar o campo `prio_otima` para validar.
---
## v0.2 — VTO — 2026-06
### Adicionado
@@ -86,3 +111,116 @@ Formato:
### Observacoes
- A remocao foi aplicada no script de pre-processamento para manter reprodutibilidade.
## v0.9 — VTO — 2026-06
### Adicionado
- Flag `--escala` na CLI: carrega missões reais da Escala de Voo Modelo 1 (CSV com separador `;`), com parser de data DD/MMM, horários UTC e prioridade derivada do código de missão (69TV→1, 50TT/05TF→3).
- `BASES_COORDS` expandido de 10 para 26 bases (inclui SBMQ, SBOI, SBBR, SBGL, SBCY, SWEI, SWCA, SWKO, SBUY e outras rotas reais).
- Cenários de f0 em JSON: `f0_cenario_esquadrao.json` (dados realistas do esquadrão) e `f0_cenario_insp_grande.json`.
### Corrigido
- Gantt: FAB 2800 (y=0) estava invisível — corrigido com `yaxis range=[-0.5, n-0.5]`.
- Gantt: barras não renderizavam (`shapes type="rect"`) — substituídas por `go.Scatter mode="lines"` que renderiza garantido no eixo de datas.
- Largura mínima de 2h por barra no Gantt para voos curtos ficarem visíveis.
### Alterado
- `--time-limit` default alterado de 60s para 300s (5 minutos).
- Imports `timedelta` e `date` consolidados no topo do módulo (era importado localmente em funções).
### Resultado validado
Rodada `--sintetico --n-missoes 30 --f0 f0_cenario_esquadrao.json` → 30/30 Optimal; FAB 2809 com 1 inspeção (CHECK 300) visível no Gantt.
---
## v0.8 — VTO — 2026-06
### Adicionado
- Visualização combinada em HTML único: Gantt (Plotly) + mapa (Folium em iframe) + tabela de voos com filtros JS interativos.
- Título "Planejamento de Diagonal de Manutenção" no HTML gerado.
- Exportação de inspeções no CSV de resultado com timestamps UTC reais.
- Filtros na tabela: aeronave, status, prioridade, base e busca livre; ordenação por coluna.
- Flag `--time-limit` na CLI do solver (padrão era 60s).
---
## v0.7 — VTO — 2026-06
### Corrigido
- Bug crítico de conservação de fluxo em nós de inspeção (`oamrp_v3.py`): o solver criava unidades de fluxo fantasma nesses nós, fazendo o relatório exibir 50/50 enquanto o CSV real tinha apenas 16 missões cobertas. Corrigido adicionando C2 para nós `tipo == "inspecao"`.
- Bug de restrição C6 Fase 1 simplificada removida: somava horas totais sem respeitar resets de inspeção, tornando EVAMs 2809 e 2811 artificialmente ociosas. As variáveis `H[k,seq,nid]` com reset já impõem o limite corretamente.
### Adicionado
- Flag `--aleatorio` na CLI: sorteia `f0` entre 10% e 90% de `F_max` por aeronave e tipo de inspeção.
- Tabela OFRAG em `db/processed/ofrag.csv` com 50 ordens sintéticas (prioridades 15, tipo de missão, OM).
- Script `software/gerar_ofrag.py` para gerar ou regen a tabela OFRAG de forma reprodutível.
- Flag `--ofrag` na CLI: carrega prioridades do CSV ao gerar missões sintéticas.
- Visualização interativa `db/processed/mapa_rotas.html` via Folium, com uma camada por aeronave, missões não cumpridas tracejadas e popup com detalhes.
- Script `software/visualizar_resultado.py` para gerar o mapa a partir do CSV de resultado.
### Alterado
- Nomenclatura alinhada com Al-Thani (2016): `lrt_h``f_max`; `horas_iniciais``f0`; `orcamento_h()``lrt_inicial()`; `horas_iniciais_aleatorias()``gerar_f0_aleatorio()`; `carregar_horas_iniciais()``carregar_f0()`; CLI `--horas-iniciais``--f0`.
- Relatório imprime `F_max=`, `f0=` e `LRT_ini=` para cada aeronave, alinhado com a formulação do artigo de referência.
### Resultado validado
Rodada `--sintetico --seed 42 --aleatorio --ofrag db/processed/ofrag.csv` → 50/50 missões cumpridas, solver CBC Optimal, todas as 4 EVAMs com voos e ferry.
---
## v0.6 - VTO - 2026-06
### Adicionado
- Gerador de missões sintéticas cross-base (`gerar_missoes_sinteticas()`): 50 missões entre 10 bases com durações calculadas via haversine ÷ velocidade C-105 (430 km/h); flag `--sintetico`, `--n-missoes`, `--n-evam` na CLI.
- Fase 3: arcos de ferry em `construir_rede()` — arcos `inicio`, `missao_missao`, `missao_insp` e `insp_missao` agora permitem bases distintas com tempo de ferry calculado automaticamente.
- Ferry consome horas de célula no relógio de inspeção (C6: `carga = v.carga_h + a.horas_ferry`).
- Relatório imprime segmentos `[FERRY base1->base2 Xh]` na linha do tempo de cada aeronave.
- Resultado `db/processed/resultado_oamrp_sintetico.csv` para 50 missões sintéticas.
### Alterado
- Dicionário `BASES_COORDS` com coordenadas (lat, lon) das 10 bases operacionais do C-105.
## v0.5 - VTO - 2026-06
### Adicionado
- Modelo `software/oamrp_v3.py` — reescrita do zero com rede espaço-tempo (No/Arco) e variável unificada `y[k,arco]`.
- Fase 1 (C1C6): fluxo de conservação, cobertura de missões, restrição de voo ativo, orçamento de horas por inspeção.
- Fase 2 (C7C11): nós e arcos de inspeção na rede; variáveis `H[k,i,nid]` para relógio de horas com reset; C9 downtime; C11 slot único via variáveis disjuntivas `oo`.
- Documento `HANDOFF_arara_oamrp.md` com formulação matemática, decisões de calibração (Q1Q5) e plano de build incremental.
- Resultado `db/processed/resultado_oamrp_v3.csv` para janeiro/2025.
### Alterado
- TAT corrigido para 1,5h (era 1,0h em v2).
- Top-10 bases por frequência no registro de voo 2025 (Q4).
- Missões EVAM marcadas como obrigatórias (Q2).
- Objetivo L2 definido como maximizar horas voadas (= minimizar folga ao fim do horizonte mensal) (Q3).
### Corrigido
- Bug de escopo de arco: arcos de início/fim agora carregam `aeronave_id` e só entram na variável `y` do aircraft correto.
- Bug `NameError tat_h` em C9b: substituído pela constante global `TAT_H`.
- Bug `AttributeError` em `escrever_resultado`: filtro por `n.tipo == "missao"` antes de acessar `n.missao`.
## v0.4 - VTO - 2026-06
### Adicionado
- Modelo `software/oamrp_v2.py` para rodar o OAMRP com o registro de voo 2025 consolidado.
- Resultado inicial `db/processed/resultado_oamrp_jan_2025.csv`.
### Observacoes
- Rodada padrao usa janeiro/2025, aeronaves reais `2800`, `2803`, `2809` e `2811`, base inicial provisoria `SBMN`, base de manutencao `SBMN` e prioridade uniforme 1.
- Primeira execucao retornou solucao otima com 50 de 61 missoes cumpridas.

108
software/gerar_ofrag.py Normal file
View 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()

375
software/oamrp_v2.py Normal file
View File

@@ -0,0 +1,375 @@
"""
============================================================================
AIRCRAFT ROUTING - ESQUADRAO ARARA - V2
============================================================================
Primeira versao do modelo OAMRP usando dados reais pre-processados do
registro de voo 2025.
Diferencas em relacao ao v1:
- le o CSV consolidado gerado a partir da Planilha 2025;
- filtra uma janela de datas, com janeiro/2025 como padrao;
- usa aeronaves reais observadas no registro: 2800, 2803, 2809 e 2811;
- gera um CSV de resultado em db/processed/.
Esta versao ainda e uma rodada exploratoria. Todas as missoes recebem
prioridade 1 e a base de manutencao considerada e SBMN.
============================================================================
"""
from __future__ import annotations
import argparse
import csv
from dataclasses import dataclass
from datetime import datetime, timezone
from pathlib import Path
import pulp
BASE_DIR = Path(__file__).resolve().parents[1]
CSV_REGISTRO_PADRAO = BASE_DIR / "db" / "processed" / "registro_voo_2025_consolidado.csv"
CSV_RESULTADO_PADRAO = BASE_DIR / "db" / "processed" / "resultado_oamrp_jan_2025.csv"
DATA_INICIO_PADRAO = "2025-01-01"
DATA_FIM_PADRAO = "2025-02-01" # exclusivo
BASE_MANUTENCAO = "SBMN"
BASE_INICIAL = BASE_MANUTENCAO
TURNAROUND_MIN_H = 1.0
LIMITE_HORAS_F = 300.0
PRIORIDADE_PADRAO = 1
AERONAVES = [
# id base inicial horas acumuladas desde ultima inspecao
("2800", BASE_INICIAL, 0.0),
("2803", BASE_INICIAL, 0.0),
("2809", BASE_INICIAL, 0.0),
("2811", BASE_INICIAL, 0.0),
]
@dataclass(frozen=True)
class Missao:
id: str
orig: str
dest: str
partida_utc: datetime
chegada_utc: datetime
prioridade: int
periodo: str
linhas_origem: str
aeronave_real: str
om: str
@property
def dur_h(self) -> float:
return (self.chegada_utc - self.partida_utc).total_seconds() / 3600
def parse_utc(valor: str) -> datetime:
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 parse_data_inicio(valor: str) -> datetime:
return datetime.fromisoformat(valor).replace(tzinfo=timezone.utc)
def carregar_missoes(caminho: Path, inicio: datetime, fim: datetime) -> list[Missao]:
missoes: list[Missao] = []
with caminho.open(newline="", encoding="utf-8-sig") as arquivo:
leitor = csv.DictReader(arquivo)
for linha in leitor:
partida = parse_utc(linha["partida_utc"])
chegada = parse_utc(linha["chegada_utc"])
if not (inicio <= partida < fim):
continue
if chegada <= partida:
raise ValueError(f"Missao {linha['id_registro']}: chegada antes da partida")
missoes.append(
Missao(
id=linha["id_registro"],
orig=linha["localidade_dep"],
dest=linha["localidade_arr"],
partida_utc=partida,
chegada_utc=chegada,
prioridade=PRIORIDADE_PADRAO,
periodo=linha.get("periodo", ""),
linhas_origem=linha.get("linhas_origem", ""),
aeronave_real=linha.get("aeronave", ""),
om=linha.get("om", ""),
)
)
return sorted(missoes, key=lambda m: (m.partida_utc, m.id))
def horas_relativas(missoes: list[Missao]) -> tuple[dict[str, dict[str, object]], datetime]:
horizonte_inicio = min(m.partida_utc for m in missoes)
dados = {}
for missao in missoes:
dados[missao.id] = {
"orig": missao.orig,
"dest": missao.dest,
"part": (missao.partida_utc - horizonte_inicio).total_seconds() / 3600,
"cheg": (missao.chegada_utc - horizonte_inicio).total_seconds() / 3600,
"prio": missao.prioridade,
"dur": missao.dur_h,
"obj": missao,
}
return dados, horizonte_inicio
def resolver(missoes: list[Missao]) -> dict[str, object]:
if not missoes:
raise ValueError("Nenhuma missao encontrada na janela informada")
miss, horizonte_inicio = horas_relativas(missoes)
acft = {a[0]: {"base": a[1], "f": a[2]} for a in AERONAVES}
ids = list(miss.keys())
ks = list(acft.keys())
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
]
def pode_iniciar(k: str, i: str) -> bool:
return miss[i]["orig"] == acft[k]["base"]
mdl = pulp.LpProblem("ARARA_OAMRP_v2", pulp.LpMaximize)
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")
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")
for k in ks
for (i, j) in conex
}
z = {i: pulp.LpVariable(f"z_{i}", cat="Binary") for i in ids}
mdl += pulp.lpSum(miss[i]["prio"] * z[i] for i in ids), "missoes_cumpridas_ponderadas"
for i in ids:
mdl += z[i] == pulp.lpSum(x[(k, i)] for k in ks), f"def_z_{i}"
for k in ks:
for i in ids:
entradas = pulp.lpSum(c[(k, jj, i)] for (jj, ii) in conex if ii == i)
inicio = s[(k, i)] if (k, i) in s else 0
mdl += inicio + entradas == x[(k, i)], f"in_{k}_{i}"
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}"
for k in ks:
inicios = [s[(k, i)] for i in ids if (k, i) in s]
if inicios:
mdl += pulp.lpSum(inicios) <= 1, f"start_{k}"
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}"
solver = pulp.PULP_CBC_CMD(msg=False)
mdl.solve(solver)
prio_otima = int(round(pulp.value(mdl.objective) or 0))
# Criterio secundario: entre solucoes com a mesma prioridade, minimiza horas
# totais atribuidas. Neste v2 isso evita voos extras irrelevantes se houver
# empate em prioridade.
mdl += pulp.lpSum(miss[i]["prio"] * z[i] for i in ids) == prio_otima, "fixa_prioridade"
mdl.sense = pulp.LpMinimize
mdl.setObjective(pulp.lpSum(miss[i]["dur"] * x[(k, i)] for k in ks for i in ids))
mdl.solve(solver)
return {
"modelo": mdl,
"miss": miss,
"acft": acft,
"ids": ids,
"ks": ks,
"conex": conex,
"x": x,
"s": s,
"c": c,
"z": z,
"horizonte_inicio": horizonte_inicio,
"prioridade_otima": prio_otima,
}
def rota_da_aeronave(sol: dict[str, object], k: str) -> list[str]:
ids = sol["ids"]
conex = sol["conex"]
s = sol["s"]
c = sol["c"]
inicio = [
i
for i in ids
if (k, i) in s and s[(k, i)].value() is not None and s[(k, i)].value() > 0.5
]
if not inicio:
return []
seq = [inicio[0]]
while True:
atual = seq[-1]
prox = [
j
for (ii, j) in conex
if ii == atual and c[(k, atual, j)].value() is not None and c[(k, atual, j)].value() > 0.5
]
if not prox:
break
seq.append(prox[0])
return seq
def escrever_resultado(sol: dict[str, object], caminho: Path) -> None:
caminho.parent.mkdir(parents=True, exist_ok=True)
miss = sol["miss"]
z = sol["z"]
linhas = []
for k in sol["ks"]:
for ordem, mid in enumerate(rota_da_aeronave(sol, k), start=1):
obj = miss[mid]["obj"]
linhas.append(
{
"aeronave_modelo": k,
"ordem": ordem,
"id": mid,
"om": obj.om,
"orig": obj.orig,
"dest": obj.dest,
"partida_utc": obj.partida_utc.isoformat().replace("+00:00", "Z"),
"chegada_utc": obj.chegada_utc.isoformat().replace("+00:00", "Z"),
"dur_h": f"{obj.dur_h:.2f}",
"prioridade": obj.prioridade,
"periodo": obj.periodo,
"aeronave_real_2025": obj.aeronave_real,
"linhas_origem": obj.linhas_origem,
"status": "cumprida",
}
)
for mid in sol["ids"]:
if not z[mid].value() or z[mid].value() <= 0.5:
obj = miss[mid]["obj"]
linhas.append(
{
"aeronave_modelo": "",
"ordem": "",
"id": mid,
"om": obj.om,
"orig": obj.orig,
"dest": obj.dest,
"partida_utc": obj.partida_utc.isoformat().replace("+00:00", "Z"),
"chegada_utc": obj.chegada_utc.isoformat().replace("+00:00", "Z"),
"dur_h": f"{obj.dur_h:.2f}",
"prioridade": obj.prioridade,
"periodo": obj.periodo,
"aeronave_real_2025": obj.aeronave_real,
"linhas_origem": obj.linhas_origem,
"status": "nao_cumprida",
}
)
colunas = [
"status",
"aeronave_modelo",
"ordem",
"id",
"om",
"orig",
"dest",
"partida_utc",
"chegada_utc",
"dur_h",
"prioridade",
"periodo",
"aeronave_real_2025",
"linhas_origem",
]
with caminho.open("w", newline="", encoding="utf-8-sig") as arquivo:
escritor = csv.DictWriter(arquivo, fieldnames=colunas)
escritor.writeheader()
escritor.writerows(linhas)
def imprimir_relatorio(sol: dict[str, object], resultado_csv: Path) -> None:
miss = sol["miss"]
z = sol["z"]
ids = sol["ids"]
cobertas = [i for i in ids if z[i].value() is not None and z[i].value() > 0.5]
prioridade_total = sum(int(miss[i]["prio"]) for i in ids)
prioridade_obtida = sum(int(miss[i]["prio"]) for i in cobertas)
print("=" * 72)
print(" OAMRP V2 - Registro de voo 2025 consolidado")
print("=" * 72)
print(f" Status solver: {pulp.LpStatus[sol['modelo'].status]}")
print(f" Horizonte inicia em UTC: {sol['horizonte_inicio'].isoformat().replace('+00:00', 'Z')}")
print(f" Missoes cumpridas: {len(cobertas)}/{len(ids)}")
print(f" Prioridade obtida: {prioridade_obtida}/{prioridade_total}")
print(f" Resultado CSV: {resultado_csv}")
for k in sol["ks"]:
seq = rota_da_aeronave(sol, k)
horas = sum(float(miss[i]["dur"]) for i in seq)
print(f"\n Aeronave {k}: {len(seq)} missoes | {horas:.1f}h")
for mid in seq[:12]:
obj = miss[mid]["obj"]
print(
f" {obj.partida_utc:%d/%m %H:%M}Z "
f"{obj.orig}->{obj.dest} OM {obj.om} ({mid})"
)
if len(seq) > 12:
print(f" ... mais {len(seq) - 12} missoes")
nao_cumpridas = [i for i in ids if i not in cobertas]
if nao_cumpridas:
print("\n Missoes nao cumpridas (primeiras 20):")
for mid in nao_cumpridas[:20]:
obj = miss[mid]["obj"]
print(f" {obj.partida_utc:%d/%m %H:%M}Z {obj.orig}->{obj.dest} OM {obj.om} ({mid})")
if len(nao_cumpridas) > 20:
print(f" ... mais {len(nao_cumpridas) - 20} missoes")
def main() -> None:
parser = argparse.ArgumentParser(description="Resolve OAMRP v2 com registro de voo real.")
parser.add_argument("--csv", type=Path, default=CSV_REGISTRO_PADRAO)
parser.add_argument("--inicio", default=DATA_INICIO_PADRAO)
parser.add_argument("--fim", default=DATA_FIM_PADRAO)
parser.add_argument("--resultado", type=Path, default=CSV_RESULTADO_PADRAO)
args = parser.parse_args()
inicio = parse_data_inicio(args.inicio)
fim = parse_data_inicio(args.fim)
missoes = carregar_missoes(args.csv, inicio, fim)
sol = resolver(missoes)
escrever_resultado(sol, args.resultado)
imprimir_relatorio(sol, args.resultado)
if __name__ == "__main__":
main()

1574
software/oamrp_v3.py Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,625 @@
"""
Visualização interativa do resultado OAMRP — Esquadrão Arara C-105.
Gera um único HTML com título "Planejamento de Diagonal de Manutenção" contendo:
- Gantt (Plotly): calendário aeronave × tempo com voos e inspeções
- Mapa (Folium): rotas por aeronave, missões não cumpridas, bases
Uso:
python software/visualizar_resultado.py \
--resultado db/processed/resultado_oamrp_sintetico.csv \
--saida db/processed/planejamento.html
"""
import argparse
import csv
import io
from datetime import datetime, timedelta, timezone
from html import escape
from pathlib import Path
import folium
BASE_DIR = Path(__file__).resolve().parents[1]
RESULTADO_PADRAO = BASE_DIR / "db" / "processed" / "resultado_oamrp_sintetico.csv"
SAIDA_PADRAO = BASE_DIR / "db" / "processed" / "planejamento.html"
# Coordenadas das bases (lat, lon)
BASES_COORDS: dict[str, tuple[float, float]] = {
"SBMN": (-3.15, -59.99),
"SBBE": (-1.38, -48.48),
"SBBV": ( 2.84, -60.69),
"SBSN": (-2.42, -54.79),
"SBPV": (-8.71, -63.90),
"SBTS": (-4.25, -69.94),
"SBTT": (-3.38, -64.72),
"SBUA": (-0.15, -67.05),
"SWBC": (-0.98, -62.92),
"SBMY": (-5.81, -61.28),
}
BASES_NOMES = {
"SBMN": "Manaus / Ponta Pelada (Hub)",
"SBBE": "Belém",
"SBBV": "Boa Vista",
"SBSN": "Santarém",
"SBPV": "Porto Velho",
"SBTS": "Tabatinga",
"SBTT": "Tefé",
"SBUA": "São Gabriel da Cachoeira",
"SWBC": "Barcelos",
"SBMY": "Manicoré",
}
CORES_AERONAVE = {
"2800": "#1f77b4",
"2803": "#2ca02c",
"2809": "#d62728",
"2811": "#ff7f0e",
}
COR_NAO_CUMPRIDA = "#aaaaaa"
_GANTT_CORES = {
"cumprida": {
"1": "#d62728",
"2": "#ff7f0e",
"3": "#2ca02c",
"4": "#1f77b4",
"5": "#9467bd",
"": "#1f77b4",
},
"inspecao": "#8c564b",
"nao_cumprida": "#aaaaaa",
}
def _cor_prio(prio: str) -> str:
return {"1": "red", "2": "orange", "3": "yellow", "4": "lightblue", "5": "lightgray"}.get(str(prio), "white")
def _cor_gantt(linha: dict) -> str:
status = linha["status"]
if status == "inspecao":
return _GANTT_CORES["inspecao"]
if status == "nao_cumprida":
return _GANTT_CORES["nao_cumprida"]
return _GANTT_CORES["cumprida"].get(linha.get("prioridade", ""), "#1f77b4")
def carregar_resultado(caminho: Path) -> list[dict]:
with caminho.open(newline="", encoding="utf-8-sig") as f:
return list(csv.DictReader(f))
def _resumir_resultado(linhas: list[dict]) -> None:
cumpridas = sum(1 for r in linhas if r["status"] == "cumprida")
total_missoes = sum(1 for r in linhas if r["status"] in ("cumprida", "nao_cumprida"))
print(f" Missões: {cumpridas}/{total_missoes} cumpridas")
aeronaves: dict[str, int] = {}
for r in linhas:
if r["aeronave"] and r["status"] == "cumprida":
aeronaves.setdefault(r["aeronave"], 0)
aeronaves[r["aeronave"]] += 1
for k, c in sorted(aeronaves.items()):
print(f" FAB {k}: {c} missões")
# ---------------------------------------------------------------------------
# Mapa Folium → string HTML
# ---------------------------------------------------------------------------
def _construir_mapa(linhas: list[dict]) -> str:
mapa = folium.Map(location=[-3.5, -62.0], zoom_start=5, tiles="CartoDB positron")
aeronaves_vistas = sorted({r["aeronave"] for r in linhas if r["aeronave"]})
grupos: dict[str, folium.FeatureGroup] = {}
for k in aeronaves_vistas:
grupos[k] = folium.FeatureGroup(name=f"FAB {k}", show=True)
grupos["nao_cumprida"] = folium.FeatureGroup(name="Não cumpridas", show=True)
for r in linhas:
if r["status"] != "cumprida":
continue
orig, dest = r["orig"], r["dest"]
if orig not in BASES_COORDS or dest not in BASES_COORDS:
continue
k = r["aeronave"]
cor = CORES_AERONAVE.get(k, "#333333")
lat_o, lon_o = BASES_COORDS[orig]
lat_d, lon_d = BASES_COORDS[dest]
folium.PolyLine(
locations=[[lat_o, lon_o], [lat_d, lon_d]],
color=cor, weight=2.5, opacity=0.8,
tooltip=f"FAB {k} | {orig}{dest} | OM {r['om']} | prio {r['prioridade']}",
popup=folium.Popup(
f"<b>FAB {k}</b> — Missão #{r['ordem']}<br>"
f"{orig}{dest}<br>"
f"OM: {r['om']} | Prio: {r['prioridade']}<br>"
f"Partida: {r['partida_utc'][:16].replace('T',' ')}Z<br>"
f"Chegada: {r['chegada_utc'][:16].replace('T',' ')}Z<br>"
f"Duração: {r['dur_h']}h",
max_width=250,
),
).add_to(grupos[k])
folium.CircleMarker(
location=[lat_o, lon_o], radius=4, color=cor,
fill=True, fill_color=_cor_prio(r["prioridade"]), fill_opacity=0.9,
tooltip=f"{orig} | prio {r['prioridade']}",
).add_to(grupos[k])
for r in linhas:
if r["status"] == "nao_cumprida":
orig, dest = r["orig"], r["dest"]
if orig not in BASES_COORDS or dest not in BASES_COORDS:
continue
lat_o, lon_o = BASES_COORDS[orig]
lat_d, lon_d = BASES_COORDS[dest]
folium.PolyLine(
locations=[[lat_o, lon_o], [lat_d, lon_d]],
color=COR_NAO_CUMPRIDA, weight=1.5, opacity=0.5, dash_array="6 4",
tooltip=f"NÃO CUMPRIDA | {orig}{dest} | OM {r['om']} | prio {r['prioridade']}",
).add_to(grupos["nao_cumprida"])
for g in grupos.values():
g.add_to(mapa)
for icao, (lat, lon) in BASES_COORDS.items():
folium.Marker(
location=[lat, lon],
tooltip=f"<b>{icao}</b><br>{BASES_NOMES.get(icao, '')}",
popup=folium.Popup(f"<b>{icao}</b><br>{BASES_NOMES.get(icao, '')}", max_width=200),
icon=folium.Icon(color="red" if icao == "SBMN" else "blue", icon="plane", prefix="fa"),
).add_to(mapa)
folium.LayerControl(collapsed=False).add_to(mapa)
legenda_html = """
<div style="position:fixed;bottom:30px;left:30px;z-index:1000;background:white;
padding:12px 16px;border-radius:8px;box-shadow:2px 2px 6px rgba(0,0,0,0.3);
font-family:sans-serif;font-size:12px;">
<b>Esquadrão Arara — OAMRP v3</b><br><br>
<b>Aeronaves:</b><br>
"""
for k, cor in CORES_AERONAVE.items():
legenda_html += (
f' <span style="display:inline-block;width:16px;height:4px;'
f'background:{cor};margin-right:6px;vertical-align:middle;"></span>'
f'FAB {k}<br>'
)
legenda_html += """
<br><b>Prioridade (preenchimento):</b><br>
<span style="display:inline-block;width:12px;height:12px;border-radius:50%;
background:red;margin-right:6px;"></span>Prio 1 (urgente)<br>
<span style="display:inline-block;width:12px;height:12px;border-radius:50%;
background:orange;margin-right:6px;"></span>Prio 2<br>
<span style="display:inline-block;width:12px;height:12px;border-radius:50%;
background:yellow;margin-right:6px;"></span>Prio 3<br>
<span style="display:inline-block;width:12px;height:12px;border-radius:50%;
background:lightblue;margin-right:6px;"></span>Prio 45<br>
<br>
<span style="border-bottom:2px dashed #aaa;display:inline-block;width:20px;
margin-right:6px;"></span>Não cumprida
</div>
"""
mapa.get_root().html.add_child(folium.Element(legenda_html))
return mapa.get_root().render()
# ---------------------------------------------------------------------------
# Gantt Plotly → string HTML (só o div, sem <html>)
# ---------------------------------------------------------------------------
def _construir_gantt(linhas: list[dict]) -> str:
try:
import plotly.graph_objects as go
except ImportError:
return "<p><em>plotly não instalado — instale com: pip install plotly</em></p>"
aeronaves_ord = sorted(
{r["aeronave"] for r in linhas if r["aeronave"]},
key=lambda k: int(k) if k.isdigit() else 0,
)
y_pos = {k: i for i, k in enumerate(aeronaves_ord)}
fig = go.Figure()
for i, k in enumerate(aeronaves_ord):
fig.add_hrect(
y0=i - 0.45, y1=i + 0.45,
fillcolor="#f0f0f0" if i % 2 == 0 else "white",
layer="below", line_width=0,
)
shapes = []
bar_traces = []
for r in linhas:
k = r["aeronave"]
if not k or k not in y_pos:
continue
try:
ini = r["partida_utc"].replace("Z", "+00:00")
fim = r["chegada_utc"].replace("Z", "+00:00")
except Exception:
continue
ini_dt = datetime.fromisoformat(ini)
fim_dt = datetime.fromisoformat(fim)
if (fim_dt - ini_dt).total_seconds() < 2 * 3600:
fim_dt = ini_dt + timedelta(hours=2)
yi = y_pos[k]
cor = _cor_gantt(r)
status = r["status"]
if status == "inspecao":
txt = (
f"<b>FAB {k}</b> — Inspeção {r['om']}<br>"
f"Início: {ini[:16].replace('T',' ')}Z<br>"
f"Fim: {fim[:16].replace('T',' ')}Z<br>"
f"Duração: {r['dur_h']}h"
)
else:
txt = (
f"<b>FAB {k}</b> — Missão #{r['ordem']}<br>"
f"{r['orig']}{r['dest']}<br>"
f"OM: {r['om']} | Prio: {r['prioridade']}<br>"
f"Partida: {ini[:16].replace('T',' ')}Z<br>"
f"Chegada: {fim[:16].replace('T',' ')}Z<br>"
f"Duração: {r['dur_h']}h"
)
# Barra como linha horizontal grossa — renderiza garantido no eixo de datas
bar_traces.append(go.Scatter(
x=[ini_dt, fim_dt],
y=[yi, yi],
mode="lines",
line=dict(color=cor, width=64),
hovertext=[txt, txt],
hoverinfo="text",
showlegend=False,
))
# Divisões diárias
datas_utc = [
r[campo].replace("Z", "+00:00")
for r in linhas
for campo in ("partida_utc", "chegada_utc")
if r.get(campo)
]
if datas_utc:
t_min = datetime.fromisoformat(min(datas_utc))
t_max = datetime.fromisoformat(max(datas_utc))
dia = datetime(t_min.year, t_min.month, t_min.day, tzinfo=timezone.utc)
while dia <= t_max + timedelta(days=1):
shapes.append(dict(
type="line", xref="x", yref="paper",
x0=dia.isoformat(), x1=dia.isoformat(),
y0=0, y1=1,
line=dict(color="#bbbbbb", width=1, dash="dot"),
layer="above",
))
dia += timedelta(days=1)
# Legenda
for nome, cor in [
("Prio 1", _GANTT_CORES["cumprida"]["1"]),
("Prio 2", _GANTT_CORES["cumprida"]["2"]),
("Prio 3", _GANTT_CORES["cumprida"]["3"]),
("Prio 4", _GANTT_CORES["cumprida"]["4"]),
("Prio 5", _GANTT_CORES["cumprida"]["5"]),
("Inspeção", _GANTT_CORES["inspecao"]),
]:
fig.add_trace(go.Scatter(
x=[None], y=[None], mode="markers",
marker=dict(size=12, color=cor, symbol="square"),
name=nome, showlegend=True,
))
for t in bar_traces:
fig.add_trace(t)
fig.update_layout(
shapes=shapes,
xaxis=dict(
title="Data / Hora (UTC)", type="date",
tickformat="%d/%m", dtick=86400000,
showgrid=True, gridcolor="#dddddd",
),
yaxis=dict(
title="Aeronave",
tickvals=list(range(len(aeronaves_ord))),
ticktext=[f"FAB {k}" for k in aeronaves_ord],
showgrid=False,
range=[-0.5, len(aeronaves_ord) - 0.5],
),
height=max(300, 120 + 80 * len(aeronaves_ord)),
plot_bgcolor="white",
paper_bgcolor="white",
legend=dict(title="Legenda", orientation="v", x=1.01, y=1, xanchor="left"),
margin=dict(l=100, r=160, t=10, b=60),
hovermode="closest",
)
return fig.to_html(full_html=False, include_plotlyjs="cdn")
# ---------------------------------------------------------------------------
# Tabela de voos com filtros
# ---------------------------------------------------------------------------
def _construir_tabela(linhas: list[dict]) -> str:
import json
# Monta lista de registros para a tabela (missões + inspeções, sem nao_cumprida sem aeronave)
registros = []
for r in linhas:
status = r["status"]
k = r["aeronave"]
if status == "nao_cumprida" and not k:
# mantém como linha sem aeronave
k = ""
registros.append({
"status": status,
"aeronave": f"FAB {k}" if k and k != "" else "",
"ordem": r.get("ordem", ""),
"om": r.get("om", ""),
"orig": r.get("orig", ""),
"dest": r.get("dest", ""),
"partida": r.get("partida_utc", "")[:16].replace("T", " ") + "Z" if r.get("partida_utc") else "",
"chegada": r.get("chegada_utc", "")[:16].replace("T", " ") + "Z" if r.get("chegada_utc") else "",
"dur_h": r.get("dur_h", ""),
"prioridade": r.get("prioridade", ""),
})
dados_json = json.dumps(registros, ensure_ascii=False)
opcoes_aeronave = sorted({r["aeronave"] for r in registros if r["aeronave"] != ""})
opcoes_status = ["cumprida", "nao_cumprida", "inspecao"]
opcoes_prio = sorted({r["prioridade"] for r in registros if r["prioridade"]})
opcoes_base = sorted({b for r in registros for b in (r["orig"], r["dest"]) if b})
def opts(valores: list[str], label: str) -> str:
html = f'<option value="">— {label} —</option>'
for v in valores:
html += f'<option value="{escape(v)}">{escape(v)}</option>'
return html
return f"""
<style>
.filtros {{
display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 14px; align-items: flex-end;
}}
.filtros label {{ font-size: 0.78rem; color: #555; display: flex; flex-direction: column; gap: 3px; }}
.filtros select, .filtros input {{
border: 1px solid #ccc; border-radius: 4px; padding: 5px 8px;
font-size: 0.85rem; min-width: 130px; background: white;
}}
.filtros button {{
padding: 6px 14px; border: none; border-radius: 4px; cursor: pointer;
font-size: 0.85rem; background: #1a3a5c; color: white;
align-self: flex-end;
}}
.filtros button:hover {{ background: #254f80; }}
#contador {{ font-size: 0.82rem; color: #666; margin-bottom: 10px; }}
#tabela-voos {{ width: 100%; border-collapse: collapse; font-size: 0.85rem; }}
#tabela-voos thead th {{
background: #1a3a5c; color: white; padding: 8px 10px;
text-align: left; white-space: nowrap; cursor: pointer; user-select: none;
}}
#tabela-voos thead th:hover {{ background: #254f80; }}
#tabela-voos thead th .sort-icon {{ margin-left: 4px; opacity: 0.5; }}
#tabela-voos tbody tr:nth-child(even) {{ background: #f7f8fa; }}
#tabela-voos tbody tr:hover {{ background: #e8f0fb; }}
#tabela-voos td {{ padding: 7px 10px; border-bottom: 1px solid #eee; white-space: nowrap; }}
.badge {{
display: inline-block; padding: 2px 8px; border-radius: 10px;
font-size: 0.75rem; font-weight: 600; text-transform: uppercase;
}}
.badge-cumprida {{ background: #d4edda; color: #155724; }}
.badge-nao_cumprida {{ background: #f8d7da; color: #721c24; }}
.badge-inspecao {{ background: #e2d9f3; color: #4a235a; }}
.prio-1 {{ color: #d62728; font-weight: 700; }}
.prio-2 {{ color: #ff7f0e; font-weight: 700; }}
.prio-3 {{ color: #2ca02c; font-weight: 700; }}
</style>
<div class="filtros">
<label>Aeronave
<select id="f-aeronave">{opts(opcoes_aeronave, "Todas")}</select>
</label>
<label>Status
<select id="f-status">{opts(opcoes_status, "Todos")}</select>
</label>
<label>Prioridade
<select id="f-prio">{opts(opcoes_prio, "Todas")}</select>
</label>
<label>Base (orig/dest)
<select id="f-base">{opts(opcoes_base, "Todas")}</select>
</label>
<label>Busca (OM, rota…)
<input id="f-texto" type="text" placeholder="ex: SBMN, EVAM…">
</label>
<button onclick="limparFiltros()">Limpar</button>
</div>
<div id="contador"></div>
<table id="tabela-voos">
<thead>
<tr>
<th onclick="ordenar(0)">Aeronave <span class="sort-icon">↕</span></th>
<th onclick="ordenar(1)">Status <span class="sort-icon">↕</span></th>
<th onclick="ordenar(2)">#Missão <span class="sort-icon">↕</span></th>
<th onclick="ordenar(3)">OM <span class="sort-icon">↕</span></th>
<th onclick="ordenar(4)">Origem <span class="sort-icon">↕</span></th>
<th onclick="ordenar(5)">Destino <span class="sort-icon">↕</span></th>
<th onclick="ordenar(6)">Partida (UTC) <span class="sort-icon">↕</span></th>
<th onclick="ordenar(7)">Chegada (UTC) <span class="sort-icon">↕</span></th>
<th onclick="ordenar(8)">Duração (h) <span class="sort-icon">↕</span></th>
<th onclick="ordenar(9)">Prio <span class="sort-icon">↕</span></th>
</tr>
</thead>
<tbody id="corpo-tabela"></tbody>
</table>
<script>
const DADOS = {dados_json};
let _colOrdem = -1, _colAsc = true;
function badgeStatus(s) {{
const label = {{cumprida:'Cumprida', nao_cumprida:'Não cumprida', inspecao:'Inspeção'}}[s] || s;
return `<span class="badge badge-${{s}}">${{label}}</span>`;
}}
function prio(p) {{
if (!p) return '';
const cls = ['1','2','3'].includes(p) ? `prio-${{p}}` : '';
return `<span class="${{cls}}">${{p}}</span>`;
}}
function linhasFiltradas() {{
const fa = document.getElementById('f-aeronave').value;
const fs = document.getElementById('f-status').value;
const fp = document.getElementById('f-prio').value;
const fb = document.getElementById('f-base').value;
const ft = document.getElementById('f-texto').value.toLowerCase();
return DADOS.filter(r => {{
if (fa && r.aeronave !== fa) return false;
if (fs && r.status !== fs) return false;
if (fp && r.prioridade !== fp) return false;
if (fb && r.orig !== fb && r.dest !== fb) return false;
if (ft && !JSON.stringify(r).toLowerCase().includes(ft)) return false;
return true;
}});
}}
function renderizar() {{
let rows = linhasFiltradas();
if (_colOrdem >= 0) {{
const chaves = ['aeronave','status','ordem','om','orig','dest','partida','chegada','dur_h','prioridade'];
const c = chaves[_colOrdem];
rows = [...rows].sort((a,b) => {{
const va = a[c] ?? '', vb = b[c] ?? '';
return _colAsc ? String(va).localeCompare(String(vb), 'pt', {{numeric:true}})
: String(vb).localeCompare(String(va), 'pt', {{numeric:true}});
}});
}}
document.getElementById('contador').textContent =
`${{rows.length}} registro${{rows.length !== 1 ? 's' : ''}} exibido${{rows.length !== 1 ? 's' : ''}} de ${{DADOS.length}}`;
document.getElementById('corpo-tabela').innerHTML = rows.map(r => `
<tr>
<td>${{r.aeronave}}</td>
<td>${{badgeStatus(r.status)}}</td>
<td>${{r.ordem}}</td>
<td>${{r.om}}</td>
<td>${{r.orig}}</td>
<td>${{r.dest}}</td>
<td>${{r.partida}}</td>
<td>${{r.chegada}}</td>
<td>${{r.dur_h}}</td>
<td>${{prio(r.prioridade)}}</td>
</tr>`).join('');
}}
function ordenar(col) {{
if (_colOrdem === col) _colAsc = !_colAsc;
else {{ _colOrdem = col; _colAsc = true; }}
renderizar();
}}
function limparFiltros() {{
['f-aeronave','f-status','f-prio','f-base'].forEach(id => document.getElementById(id).value = '');
document.getElementById('f-texto').value = '';
renderizar();
}}
['f-aeronave','f-status','f-prio','f-base'].forEach(id =>
document.getElementById(id).addEventListener('change', renderizar));
document.getElementById('f-texto').addEventListener('input', renderizar);
renderizar();
</script>"""
# ---------------------------------------------------------------------------
# HTML combinado
# ---------------------------------------------------------------------------
def gerar_planejamento(resultado: Path, saida: Path) -> None:
linhas = carregar_resultado(resultado)
gantt_div = _construir_gantt(linhas)
mapa_html = _construir_mapa(linhas)
tabela_div = _construir_tabela(linhas)
html = f"""<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="utf-8">
<title>Planejamento de Diagonal de Manutenção</title>
<style>
* {{ box-sizing: border-box; margin: 0; padding: 0; }}
body {{ font-family: 'Segoe UI', Arial, sans-serif; background: #f0f2f5; color: #222; }}
header {{
background: #1a3a5c; color: white;
padding: 18px 32px;
border-bottom: 4px solid #c8a400;
}}
header h1 {{ font-size: 1.5rem; font-weight: 700; letter-spacing: 0.03em; }}
header p {{ font-size: 0.85rem; opacity: 0.75; margin-top: 4px; }}
.container {{ padding: 24px 32px; max-width: 1600px; margin: 0 auto; }}
.section {{
background: white; border-radius: 8px;
box-shadow: 0 1px 4px rgba(0,0,0,0.12);
margin-bottom: 28px; overflow: hidden;
}}
.section-title {{
background: #f7f8fa; border-bottom: 1px solid #e0e0e0;
padding: 12px 20px; font-size: 0.95rem; font-weight: 600;
color: #1a3a5c; text-transform: uppercase; letter-spacing: 0.05em;
}}
.section-body {{ padding: 16px; }}
iframe.mapa {{
width: 100%; height: 560px; border: none; border-radius: 4px; display: block;
}}
</style>
</head>
<body>
<header>
<h1>Planejamento de Diagonal de Manutenção</h1>
<p>Esquadrão Arara · C-105 · OAMRP v3</p>
</header>
<div class="container">
<div class="section">
<div class="section-title">Calendário de Emprego das Aeronaves</div>
<div class="section-body">{gantt_div}</div>
</div>
<div class="section">
<div class="section-title">Rotas Operacionais</div>
<iframe class="mapa" srcdoc="{escape(mapa_html)}"></iframe>
</div>
<div class="section">
<div class="section-title">Planejamento de Voos</div>
<div class="section-body">{tabela_div}</div>
</div>
</div>
</body>
</html>"""
saida.parent.mkdir(parents=True, exist_ok=True)
saida.write_text(html, encoding="utf-8")
print(f" Planejamento salvo: {saida}")
_resumir_resultado(linhas)
def main() -> None:
parser = argparse.ArgumentParser(description="Visualização OAMRP — Arara C-105")
parser.add_argument("--resultado", type=Path, default=RESULTADO_PADRAO)
parser.add_argument("--saida", type=Path, default=SAIDA_PADRAO)
args = parser.parse_args()
gerar_planejamento(resultado=args.resultado, saida=args.saida)
if __name__ == "__main__":
main()