Compare commits

...

9 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
15 changed files with 4497 additions and 217 deletions

16
LOG.md
View File

@@ -32,3 +32,19 @@ Legenda de autores:
| 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,57 +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:
```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
```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
```
O consolidado trata `ROTA` como marcador de transicao, nao como localidade operacional, e remove a linha original 793 da aeronave 2806 dos artefatos processados.
Parâmetros relevantes do modelo:
## Modelo OAMRP v2
| 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 |
O arquivo `software/oamrp_v2.py` executa a primeira rodada do modelo com dados reais consolidados de 2025. Por padrao, ele usa janeiro/2025, as aeronaves `2800`, `2803`, `2809` e `2811`, e grava:
---
```text
db/processed/resultado_oamrp_jan_2025.csv
```
## Histórico dos modelos
Para executar:
### 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
```
A rodada inicial de janeiro/2025 foi resolvida como otima pelo CBC, com 50 de 61 missoes cumpridas.
---
### 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/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
```

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

@@ -1,51 +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_014_SBMN_SBTS,14,SBMN,SBTS,2025-01-11T09:41:20.400000Z,2025-01-11T12:08:20.400000Z,2.45,1,DIURNO,,
cumprida,2800,2,SIM_034_SBTS_SBMN,34,SBTS,SBMN,2025-01-23T00:14:45.600000Z,2025-01-23T02:54:57.600000Z,2.67,1,DIURNO,,
cumprida,2803,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,2803,2,SIM_024_SBMY_SBBV,24,SBMY,SBBV,2025-01-17T22:04:04.800000Z,2025-01-18T00:30:28.800000Z,2.44,1,DIURNO,,
cumprida,2803,3,SIM_038_SBBV_SBBE,38,SBBV,SBBE,2025-01-23T23:08:38.400000Z,2025-01-24T02:18:50.400000Z,3.17,1,DIURNO,,
cumprida,2803,4,SIM_041_SBBE_SBTS,41,SBBE,SBTS,2025-01-24T12:09:03.600000Z,2025-01-24T17:48:03.600000Z,5.65,1,DIURNO,,
cumprida,2803,5,SIM_045_SBTS_SBMN,45,SBTS,SBMN,2025-01-28T01:21:25.200000Z,2025-01-28T03:43:01.200000Z,2.36,1,DIURNO,,
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_003_SBMN_SBPV,3,SBMN,SBPV,2025-01-04T06:06:21.600000Z,2025-01-04T07:48:57.600000Z,1.71,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_005_SBSN_SBTS,5,SBSN,SBTS,2025-01-05T05:32:09.600000Z,2025-01-05T09:50:09.600000Z,4.30,1,DIURNO,,
nao_cumprida,,,SIM_006_SBSN_SBBE,6,SBSN,SBBE,2025-01-05T06:29:20.400000Z,2025-01-05T08:16:44.400000Z,1.79,1,DIURNO,,
nao_cumprida,,,SIM_007_SBTS_SBMN,7,SBTS,SBMN,2025-01-05T08:52:04.800000Z,2025-01-05T11:40:04.800000Z,2.80,1,DIURNO,,
nao_cumprida,,,SIM_008_SBBE_SBTS,8,SBBE,SBTS,2025-01-05T16:11:09.600000Z,2025-01-05T21:53:09.600000Z,5.70,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,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,1,DIURNO,,
nao_cumprida,,,SIM_013_SBMN_SBBV,13,SBMN,SBBV,2025-01-09T23:25:44.400000Z,2025-01-10T01:05:20.400000Z,1.66,1,DIURNO,,
nao_cumprida,,,SIM_015_SBMN_SBPV,15,SBMN,SBPV,2025-01-12T01:42:18Z,2025-01-12T03:21:54Z,1.66,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,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,1,DIURNO,,
nao_cumprida,,,SIM_020_SWBC_SBTS,20,SWBC,SBTS,2025-01-16T02:14:27.600000Z,2025-01-16T04:25:51.600000Z,2.19,1,DIURNO,,
nao_cumprida,,,SIM_021_SBMN_SBPV,21,SBMN,SBPV,2025-01-16T06:26:09.600000Z,2025-01-16T08:12:21.600000Z,1.77,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_023_SBBV_SWBC,23,SBBV,SWBC,2025-01-17T11:36:32.400000Z,2025-01-17T12:47:20.400000Z,1.18,1,DIURNO,,
nao_cumprida,,,SIM_025_SWBC_SBMN,25,SWBC,SBMN,2025-01-18T08:18:36Z,2025-01-18T09:11:24Z,0.88,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,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_028_SBMN_SBTT,28,SBMN,SBTT,2025-01-19T05:45:50.400000Z,2025-01-19T07:06:14.400000Z,1.34,1,DIURNO,,
nao_cumprida,,,SIM_029_SBTT_SBSN,29,SBTT,SBSN,2025-01-20T02:58:12Z,2025-01-20T05:46:12Z,2.80,1,DIURNO,,
nao_cumprida,,,SIM_030_SWBC_SBSN,30,SWBC,SBSN,2025-01-20T15:51:10.800000Z,2025-01-20T18:05:34.800000Z,2.24,1,DIURNO,,
nao_cumprida,,,SIM_031_SBMN_SBTT,31,SBMN,SBTT,2025-01-21T19:19:40.800000Z,2025-01-21T20:35:52.800000Z,1.27,1,DIURNO,,
nao_cumprida,,,SIM_032_SBMN_SBBE,32,SBMN,SBBE,2025-01-21T19:28:22.800000Z,2025-01-21T22:36:46.800000Z,3.14,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_036_SBPV_SWBC,36,SBPV,SWBC,2025-01-23T18:49:19.200000Z,2025-01-23T21:00:07.200000Z,2.18,1,DIURNO,,
nao_cumprida,,,SIM_037_SBBV_SBMN,37,SBBV,SBMN,2025-01-23T20:18:28.800000Z,2025-01-23T21:50:52.800000Z,1.54,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,1,DIURNO,,
nao_cumprida,,,SIM_042_SBBE_SBSN,42,SBBE,SBSN,2025-01-25T14:54:14.400000Z,2025-01-25T16:35:02.400000Z,1.68,1,DIURNO,,
nao_cumprida,,,SIM_043_SBMN_SBPV,43,SBMN,SBPV,2025-01-26T17:47:06Z,2025-01-26T19:21:54Z,1.58,1,DIURNO,,
nao_cumprida,,,SIM_044_SBBE_SBPV,44,SBBE,SBPV,2025-01-27T08:03:00Z,2025-01-27T12:37:48Z,4.58,1,DIURNO,,
nao_cumprida,,,SIM_046_SBMN_SBUA,46,SBMN,SBUA,2025-01-28T01:26:06Z,2025-01-28T03:14:42Z,1.81,1,DIURNO,,
nao_cumprida,,,SIM_047_SWBC_SBTS,47,SWBC,SBTS,2025-01-28T22:14:31.200000Z,2025-01-29T00:03:07.200000Z,1.81,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,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_014_SBMN_SBTS SIM_007_SBTS_SBMN 14 7 SBMN SBTS SBTS SBMN 2025-01-11T09:41:20.400000Z 2025-01-05T08:52:04.800000Z 2025-01-11T12:08:20.400000Z 2025-01-05T11:40:04.800000Z 2.45 2.80 1 2 DIURNO
3 cumprida 2800 2 SIM_034_SBTS_SBMN SIM_008_SBBE_SBTS 34 8 SBTS SBBE SBMN SBTS 2025-01-23T00:14:45.600000Z 2025-01-05T16:11:09.600000Z 2025-01-23T02:54:57.600000Z 2025-01-05T21:53:09.600000Z 2.67 5.70 1 4 DIURNO
4 cumprida 2803 2800 1 3 SIM_001_SBMN_SBMY SIM_015_SBMN_SBPV 1 15 SBMN SBMY SBPV 2025-01-01T02:22:55.200000Z 2025-01-12T01:42:18Z 2025-01-01T03:09:07.200000Z 2025-01-12T03:21:54Z 0.77 1.66 1 DIURNO
5 cumprida 2803 2800 2 4 SIM_024_SBMY_SBBV SIM_021_SBMN_SBPV 24 21 SBMY SBMN SBBV SBPV 2025-01-17T22:04:04.800000Z 2025-01-16T06:26:09.600000Z 2025-01-18T00:30:28.800000Z 2025-01-16T08:12:21.600000Z 2.44 1.77 1 DIURNO
6 cumprida 2803 2800 3 5 SIM_038_SBBV_SBBE SIM_037_SBBV_SBMN 38 37 SBBV SBBE SBMN 2025-01-23T23:08:38.400000Z 2025-01-23T20:18:28.800000Z 2025-01-24T02:18:50.400000Z 2025-01-23T21:50:52.800000Z 3.17 1.54 1 3 DIURNO
7 cumprida 2803 2800 4 6 SIM_041_SBBE_SBTS SIM_042_SBBE_SBSN 41 42 SBBE SBTS SBSN 2025-01-24T12:09:03.600000Z 2025-01-25T14:54:14.400000Z 2025-01-24T17:48:03.600000Z 2025-01-25T16:35:02.400000Z 5.65 1.68 1 5 DIURNO
8 cumprida 2803 2800 5 7 SIM_045_SBTS_SBMN 45 SBTS SBMN 2025-01-28T01:21:25.200000Z 2025-01-28T03:43:01.200000Z 2.36 1 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
nao_cumprida SIM_003_SBMN_SBPV 3 SBMN SBPV 2025-01-04T06:06:21.600000Z 2025-01-04T07:48:57.600000Z 1.71 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_005_SBSN_SBTS SIM_009_SBMY_SBTS 5 9 SBSN SBMY SBTS 2025-01-05T05:32:09.600000Z 2025-01-05T20:05:16.800000Z 2025-01-05T09:50:09.600000Z 2025-01-05T22:20:52.800000Z 4.30 2.26 1 2 DIURNO
nao_cumprida SIM_006_SBSN_SBBE 6 SBSN SBBE 2025-01-05T06:29:20.400000Z 2025-01-05T08:16:44.400000Z 1.79 1 DIURNO
nao_cumprida SIM_007_SBTS_SBMN 7 SBTS SBMN 2025-01-05T08:52:04.800000Z 2025-01-05T11:40:04.800000Z 2.80 1 DIURNO
nao_cumprida SIM_008_SBBE_SBTS 8 SBBE SBTS 2025-01-05T16:11:09.600000Z 2025-01-05T21:53:09.600000Z 5.70 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 1 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 1 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 1 2 DIURNO
25 nao_cumprida SIM_015_SBMN_SBPV SIM_014_SBMN_SBTS 15 14 SBMN SBPV SBTS 2025-01-12T01:42:18Z 2025-01-11T09:41:20.400000Z 2025-01-12T03:21:54Z 2025-01-11T12:08:20.400000Z 1.66 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 1 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 1 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 1 4 DIURNO
nao_cumprida SIM_021_SBMN_SBPV 21 SBMN SBPV 2025-01-16T06:26:09.600000Z 2025-01-16T08:12:21.600000Z 1.77 1 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_023_SBBV_SWBC SIM_024_SBMY_SBBV 23 24 SBBV SBMY SWBC SBBV 2025-01-17T11:36:32.400000Z 2025-01-17T22:04:04.800000Z 2025-01-17T12:47:20.400000Z 2025-01-18T00:30:28.800000Z 1.18 2.44 1 DIURNO
33 nao_cumprida SIM_025_SWBC_SBMN SIM_026_SBUA_SBTT 25 26 SWBC SBUA SBMN SBTT 2025-01-18T08:18:36Z 2025-01-18T20:18:46.800000Z 2025-01-18T09:11:24Z 2025-01-18T21:21:10.800000Z 0.88 1.04 1 3 DIURNO
nao_cumprida SIM_026_SBUA_SBTT 26 SBUA SBTT 2025-01-18T20:18:46.800000Z 2025-01-18T21:21:10.800000Z 1.04 1 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_028_SBMN_SBTT SIM_029_SBTT_SBSN 28 29 SBMN SBTT SBTT SBSN 2025-01-19T05:45:50.400000Z 2025-01-20T02:58:12Z 2025-01-19T07:06:14.400000Z 2025-01-20T05:46:12Z 1.34 2.80 1 2 DIURNO
36 nao_cumprida SIM_029_SBTT_SBSN SIM_030_SWBC_SBSN 29 30 SBTT SWBC SBSN 2025-01-20T02:58:12Z 2025-01-20T15:51:10.800000Z 2025-01-20T05:46:12Z 2025-01-20T18:05:34.800000Z 2.80 2.24 1 2 DIURNO
37 nao_cumprida SIM_030_SWBC_SBSN SIM_031_SBMN_SBTT 30 31 SWBC SBMN SBSN SBTT 2025-01-20T15:51:10.800000Z 2025-01-21T19:19:40.800000Z 2025-01-20T18:05:34.800000Z 2025-01-21T20:35:52.800000Z 2.24 1.27 1 2 DIURNO
38 nao_cumprida SIM_031_SBMN_SBTT SIM_032_SBMN_SBBE 31 32 SBMN SBTT SBBE 2025-01-21T19:19:40.800000Z 2025-01-21T19:28:22.800000Z 2025-01-21T20:35:52.800000Z 2025-01-21T22:36:46.800000Z 1.27 3.14 1 5 DIURNO
nao_cumprida SIM_032_SBMN_SBBE 32 SBMN SBBE 2025-01-21T19:28:22.800000Z 2025-01-21T22:36:46.800000Z 3.14 1 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_036_SBPV_SWBC SIM_038_SBBV_SBBE 36 38 SBPV SBBV SWBC SBBE 2025-01-23T18:49:19.200000Z 2025-01-23T23:08:38.400000Z 2025-01-23T21:00:07.200000Z 2025-01-24T02:18:50.400000Z 2.18 3.17 1 2 DIURNO
nao_cumprida SIM_037_SBBV_SBMN 37 SBBV SBMN 2025-01-23T20:18:28.800000Z 2025-01-23T21:50:52.800000Z 1.54 1 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 1 4 DIURNO
45 nao_cumprida SIM_042_SBBE_SBSN SIM_043_SBMN_SBPV 42 43 SBBE SBMN SBSN SBPV 2025-01-25T14:54:14.400000Z 2025-01-26T17:47:06Z 2025-01-25T16:35:02.400000Z 2025-01-26T19:21:54Z 1.68 1.58 1 4 DIURNO
46 nao_cumprida SIM_043_SBMN_SBPV SIM_044_SBBE_SBPV 43 44 SBMN SBBE SBPV 2025-01-26T17:47:06Z 2025-01-27T08:03:00Z 2025-01-26T19:21:54Z 2025-01-27T12:37:48Z 1.58 4.58 1 3 DIURNO
47 nao_cumprida SIM_044_SBBE_SBPV SIM_046_SBMN_SBUA 44 46 SBBE SBMN SBPV SBUA 2025-01-27T08:03:00Z 2025-01-28T01:26:06Z 2025-01-27T12:37:48Z 2025-01-28T03:14:42Z 4.58 1.81 1 3 DIURNO
48 nao_cumprida SIM_046_SBMN_SBUA SIM_047_SWBC_SBTS 46 47 SBMN SWBC SBUA SBTS 2025-01-28T01:26:06Z 2025-01-28T22:14:31.200000Z 2025-01-28T03:14:42Z 2025-01-29T00:03:07.200000Z 1.81 1 2 DIURNO
nao_cumprida SIM_047_SWBC_SBTS 47 SWBC SBTS 2025-01-28T22:14:31.200000Z 2025-01-29T00:03:07.200000Z 1.81 1 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 1 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

@@ -1,51 +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,RV2025_OM1_E2_L5_C5,1,SBMN,SBMN,2025-01-02T14:30:00Z,2025-01-02T15:35:00Z,1.08,1,NOTURNO,2809,5
cumprida,2800,2,RV2025_OM5_E2_L13_C13,5,SBMN,SBMN,2025-01-07T14:15:00Z,2025-01-07T16:15:00Z,2.00,1,DIURNO,2803,13
cumprida,2803,1,RV2025_OM7_E1_L15_C15,7,SBMN,SBMN,2025-01-07T10:40:00Z,2025-01-07T12:30:00Z,1.83,1,DIURNO,2809,15
cumprida,2803,2,RV2025_OM8_E1_L17_C17,8,SBMN,SBMN,2025-01-08T10:35:00Z,2025-01-08T12:35:00Z,2.00,1,DIURNO,2803,17
cumprida,2803,3,RV2025_OM12_E4_L28_C29,12,SBMN,SBMN,2025-01-11T21:05:00Z,2025-01-12T00:00:00Z,2.92,1,DIURNO;NOTURNO,2809,28;29
cumprida,2803,4,RV2025_OM15_E1_L34_C34,15,SBMN,SBMN,2025-01-12T11:50:00Z,2025-01-12T13:30:00Z,1.67,1,DIURNO,2803,34
cumprida,2809,1,RV2025_OM1_E1_L4_C4,1,SBMN,SBMN,2025-01-02T11:55:00Z,2025-01-02T13:05:00Z,1.17,1,NOTURNO,2809,4
cumprida,2811,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,2811,2,RV2025_OM4_E2_L10_C10,4,SBMN,SBMN,2025-01-06T15:40:00Z,2025-01-06T16:45:00Z,1.08,1,DIURNO,2803,10
cumprida,2811,3,RV2025_OM9_E1_L20_C20,9,SBMN,SBMN,2025-01-09T12:30:00Z,2025-01-09T15:55:00Z,3.42,1,DIURNO,2803,20
cumprida,2811,4,RV2025_OM9_E3_L22_C22,9,SBMN,SBMN,2025-01-09T20:40:00Z,2025-01-09T22:15:00Z,1.58,1,DIURNO,2803,22
cumprida,2811,5,RV2025_OM14_E1_L32_C32,14,SBMN,SBMN,2025-01-12T16:55:00Z,2025-01-12T20:25:00Z,3.50,1,DIURNO,2809,32
cumprida,2811,6,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,7,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
nao_cumprida,,,RV2025_OM2_E3_L6_C6,2,SBMN,SBMN,2025-01-02T17:00:00Z,2025-01-02T18:05:00Z,1.08,1,NOTURNO,2809,6
nao_cumprida,,,RV2025_OM2_E4_L7_C7,2,SBMN,SBMN,2025-01-02T20:00:00Z,2025-01-02T21:10:00Z,1.17,1,NOTURNO,2809,7
nao_cumprida,,,RV2025_OM5_E1_L11_C11,5,SBMN,SBMN,2025-01-06T19:10:00Z,2025-01-06T21:20:00Z,2.17,1,DIURNO,2803,11
nao_cumprida,,,RV2025_OM6_E1_L12_C12,6,SBMN,SBMN,2025-01-06T19:30:00Z,2025-01-06T21:25:00Z,1.92,1,DIURNO,2811,12
nao_cumprida,,,RV2025_OM6_E2_L14_C14,6,SBMN,SBMN,2025-01-07T11:20:00Z,2025-01-07T13:20:00Z,2.00,1,DIURNO,2811,14
nao_cumprida,,,RV2025_OM7_E2_L16_C16,7,SBMN,SBMN,2025-01-07T14:00:00Z,2025-01-07T15:45:00Z,1.75,1,DIURNO,2809,16
nao_cumprida,,,RV2025_OM8_E2_L18_C18,8,SBMN,SBMN,2025-01-08T14:00:00Z,2025-01-08T16:00:00Z,2.00,1,DIURNO,2803,18
nao_cumprida,,,RV2025_OM10_E1_L19_C19,10,SBMN,SBMN,2025-01-08T22:30:00Z,2025-01-08T23:15:00Z,0.75,1,NOTURNO,2803,19
nao_cumprida,,,RV2025_OM9_E2_L21_C21,9,SBMN,SBMN,2025-01-09T18:10:00Z,2025-01-09T20:00:00Z,1.83,1,DIURNO,2803,21
nao_cumprida,,,RV2025_OM11_E1_L23_C23,11,SBMN,SBMN,2025-01-10T12:10:00Z,2025-01-10T13:15:00Z,1.08,1,DIURNO,2803,23
nao_cumprida,,,RV2025_OM11_E2_L24_C24,11,SBMN,SBMN,2025-01-10T14:25:00Z,2025-01-10T15:25:00Z,1.00,1,DIURNO,2803,24
nao_cumprida,,,RV2025_OM13_E1_L30_C30,13,SBMN,SBMN,2025-01-12T12:20:00Z,2025-01-12T13:10:00Z,0.83,1,DIURNO,2809,30
nao_cumprida,,,RV2025_OM15_E2_L35_C35,15,SBMN,SBMN,2025-01-12T14:25:00Z,2025-01-12T16:00:00Z,1.58,1,DIURNO,2803,35
nao_cumprida,,,RV2025_OM13_E2_L31_C31,13,SBMN,SBMN,2025-01-12T14:40:00Z,2025-01-12T15:25:00Z,0.75,1,DIURNO,2809,31
nao_cumprida,,,RV2025_OM15_E3_L36_C36,15,SBMN,SBMN,2025-01-12T18:30:00Z,2025-01-12T20:35:00Z,2.08,1,DIURNO,2803,36
nao_cumprida,,,RV2025_OM14_E2_L33_C37,14,SBMN,SBMN,2025-01-12T22:45:00Z,2025-01-13T02:05:00Z,3.33,1,NOTURNO,2809,33;37
nao_cumprida,,,RV2025_OM16_E1_L38_C38,16,SBMN,SBMN,2025-01-15T12:00:00Z,2025-01-15T15:05:00Z,3.08,1,DIURNO,2803,38
nao_cumprida,,,RV2025_OM17_E2_L41_C41,17,SBMN,SBMN,2025-01-15T14:45:00Z,2025-01-15T15:40:00Z,0.92,1,DIURNO,2809,41
nao_cumprida,,,RV2025_OM16_E2_L39_C39,16,SBMN,SBMN,2025-01-15T17:35:00Z,2025-01-15T20:35:00Z,3.00,1,DIURNO,2803,39
nao_cumprida,,,RV2025_OM18_E1_L46_C46,18,SBMN,SBMN,2025-01-21T12:05:00Z,2025-01-21T13:40:00Z,1.58,1,DIURNO,2800,46
nao_cumprida,,,RV2025_OM18_E2_L47_C47,18,SBMN,SBMN,2025-01-21T14:45:00Z,2025-01-21T16:15:00Z,1.50,1,DIURNO,2800,47
nao_cumprida,,,RV2025_OM18_E3_L48_C48,18,SBMN,SBMN,2025-01-21T18:40:00Z,2025-01-21T20:40:00Z,2.00,1,DIURNO,2800,48
nao_cumprida,,,RV2025_OM19_E1_L49_C49,19,SBMN,SBMN,2025-01-22T14:55:00Z,2025-01-22T16:00:00Z,1.08,1,DIURNO,2800,49
nao_cumprida,,,RV2025_OM20_E1_L51_C51,20,SBMN,SBMN,2025-01-22T14:55:00Z,2025-01-22T16:00:00Z,1.08,1,DIURNO,2803,51
nao_cumprida,,,RV2025_OM21_E1_L53_C53,21,SBMN,SBMN,2025-01-22T14:55:00Z,2025-01-22T16:00:00Z,1.08,1,DIURNO,2809,53
nao_cumprida,,,RV2025_OM22_E1_L55_C55,22,SBMN,SBMN,2025-01-22T14:55:00Z,2025-01-22T15:55:00Z,1.00,1,DIURNO,2811,55
nao_cumprida,,,RV2025_OM19_E2_L50_C50,19,SBMN,SBMN,2025-01-22T20:50:00Z,2025-01-22T21:25:00Z,0.58,1,DIURNO,2800,50
nao_cumprida,,,RV2025_OM20_E2_L52_C52,20,SBMN,SBMN,2025-01-22T20:50:00Z,2025-01-22T21:25:00Z,0.58,1,DIURNO,2803,52
nao_cumprida,,,RV2025_OM21_E2_L54_C54,21,SBMN,SBMN,2025-01-22T20:50:00Z,2025-01-22T21:25:00Z,0.58,1,DIURNO,2809,54
nao_cumprida,,,RV2025_OM22_E2_L56_C56,22,SBMN,SBMN,2025-01-22T20:50:00Z,2025-01-22T21:25:00Z,0.58,1,DIURNO,2811,56
nao_cumprida,,,RV2025_OM27_E1_L68_C68,27,SBMN,SBMN,2025-01-28T12:05:00Z,2025-01-28T13:05:00Z,1.00,1,DIURNO,2803,68
nao_cumprida,,,RV2025_OM25_E1_L64_C64,25,SBMN,SBMN,2025-01-28T13:25:00Z,2025-01-28T14:30:00Z,1.08,1,DIURNO,2809,64
nao_cumprida,,,RV2025_OM27_E2_L69_C69,27,SBMN,SBMN,2025-01-28T14:35:00Z,2025-01-28T15:30:00Z,0.92,1,DIURNO,2803,69
nao_cumprida,,,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
nao_cumprida,,,RV2025_OM28_E1_L71_C71,28,SBMN,SBMN,2025-01-29T18:15:00Z,2025-01-29T19:25:00Z,1.17,1,DIURNO,2809,71
nao_cumprida,,,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,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 RV2025_OM1_E2_L5_C5 SIM_007_SBMN_SBCO 1 7 SBMN SBMN SBCO 2025-01-02T14:30:00Z 2025-01-09T02:09:28.800000Z 2025-01-02T15:35:00Z 2025-01-09T07:58:40.800000Z 1.08 5.82 1 NOTURNO DIURNO 2809 5
3 cumprida 2800 2 RV2025_OM5_E2_L13_C13 SIM_010_SBCY_SBMN 5 10 SBMN SBCY SBMN 2025-01-07T14:15:00Z 2025-01-11T23:29:27.600000Z 2025-01-07T16:15:00Z 2025-01-12T03:09:39.600000Z 2.00 3.67 1 3 DIURNO 2803 13
4 cumprida 2803 2800 1 3 RV2025_OM7_E1_L15_C15 SIM_012_SWCA_SBTT 7 12 SBMN SWCA SBMN SBTT 2025-01-07T10:40:00Z 2025-01-12T14:52:01.200000Z 2025-01-07T12:30:00Z 2025-01-12T16:14:49.200000Z 1.83 1.38 1 3 DIURNO 2809 15
5 cumprida 2803 2800 2 4 RV2025_OM8_E1_L17_C17 SIM_019_SBMN_SWCA 8 19 SBMN SBMN SWCA 2025-01-08T10:35:00Z 2025-01-29T04:57:54Z 2025-01-08T12:35:00Z 2025-01-29T06:36:54Z 2.00 1.65 1 DIURNO 2803 17
6 cumprida 2803 2800 3 5 RV2025_OM12_E4_L28_C29 SIM_020_SWCA_SBCO 12 20 SBMN SWCA SBMN SBCO 2025-01-11T21:05:00Z 2025-01-31T03:09:10.800000Z 2025-01-12T00:00:00Z 2025-01-31T08:39:46.800000Z 2.92 5.51 1 2 DIURNO;NOTURNO DIURNO 2809 28;29
7 cumprida inspecao 2803 2800 4 RV2025_OM15_E1_L34_C34 INSP_2800_3 15 INSP 2A SBMN SBMN 2025-01-12T11:50:00Z 2025-01-12T16:14:49.200000Z 2025-01-12T13:30:00Z 2025-01-16T16:14:49.200000Z 1.67 96.00 1 DIURNO 2803 34
8 cumprida 2809 2803 1 RV2025_OM1_E1_L4_C4 SIM_006_SWBC_SBAN 1 6 SBMN SWBC SBMN SBAN 2025-01-02T11:55:00Z 2025-01-06T21:08:24Z 2025-01-02T13:05:00Z 2025-01-07T00:56:24Z 1.17 3.80 1 2 NOTURNO DIURNO 2809 4
9 cumprida 2811 2809 1 RV2025_OM3_E1_L8_C8 SIM_001_SBBE_SBMN 3 1 SBMN SBBE SBMN 2025-01-03T14:45:00Z 2025-01-02T07:53:34.800000Z 2025-01-03T16:10:00Z 2025-01-02T11:07:22.800000Z 1.42 3.23 1 2 DIURNO 2809 8
10 cumprida 2811 2809 2 RV2025_OM4_E2_L10_C10 SIM_002_SBPV_SBOI 4 2 SBMN SBPV SBMN SBOI 2025-01-06T15:40:00Z 2025-01-03T03:03:10.800000Z 2025-01-06T16:45:00Z 2025-01-03T09:37:58.800000Z 1.08 6.58 1 3 DIURNO 2803 10
11 cumprida 2811 2809 3 RV2025_OM9_E1_L20_C20 SIM_005_SBMY_SBYS 9 5 SBMN SBMY SBMN SBYS 2025-01-09T12:30:00Z 2025-01-04T19:37:19.200000Z 2025-01-09T15:55:00Z 2025-01-05T01:06:43.200000Z 3.42 5.49 1 DIURNO 2803 20
12 cumprida 2811 2809 4 RV2025_OM9_E3_L22_C22 SIM_008_SBPV_SBMN 9 8 SBMN SBPV SBMN 2025-01-09T20:40:00Z 2025-01-09T23:11:34.800000Z 2025-01-09T22:15:00Z 2025-01-10T00:51:10.800000Z 1.58 1.66 1 2 DIURNO 2803 22
13 cumprida 2811 2809 5 RV2025_OM14_E1_L32_C32 SIM_011_SBSN_SBVH 14 11 SBMN SBSN SBMN SBVH 2025-01-12T16:55:00Z 2025-01-12T01:26:24Z 2025-01-12T20:25:00Z 2025-01-12T04:15:36Z 3.50 2.82 1 DIURNO 2809 32
14 cumprida 2811 2809 6 RV2025_OM17_E1_L40_C40 SIM_016_SBTF_SBMN 17 16 SBMN SBTF SBMN 2025-01-15T13:30:00Z 2025-01-24T15:25:55.200000Z 2025-01-15T14:25:00Z 2025-01-24T16:40:19.200000Z 0.92 1.24 1 3 DIURNO 2809 40
15 cumprida inspecao 2811 2809 7 RV2025_OM17_E3_L42_C45 INSP_2809_3 17 INSP 2A SBMN SBMN 2025-01-15T22:50:00Z 2025-01-05T01:06:43.200000Z 2025-01-15T23:50:00Z 2025-01-09T01:06:43.200000Z 1.00 96.00 1 NOTURNO 2809 42;43;44;45
16 nao_cumprida cumprida 2811 1 RV2025_OM2_E3_L6_C6 SIM_003_SBMN_SBUY 2 3 SBMN SBMN SBUY 2025-01-02T17:00:00Z 2025-01-03T09:50:20.400000Z 2025-01-02T18:05:00Z 2025-01-03T16:17:56.400000Z 1.08 6.46 1 2 NOTURNO DIURNO 2809 6
17 nao_cumprida cumprida 2811 2 RV2025_OM2_E4_L7_C7 SIM_004_SWCA_SBMN 2 4 SBMN SWCA SBMN 2025-01-02T20:00:00Z 2025-01-04T03:01:19.200000Z 2025-01-02T21:10:00Z 2025-01-04T04:51:43.200000Z 1.17 1.84 1 3 NOTURNO DIURNO 2809 7
18 nao_cumprida cumprida 2811 3 RV2025_OM5_E1_L11_C11 SIM_009_SBUY_SBCC 5 9 SBMN SBUY SBMN SBCC 2025-01-06T19:10:00Z 2025-01-11T12:27:50.400000Z 2025-01-06T21:20:00Z 2025-01-11T17:17:02.400000Z 2.17 4.82 1 DIURNO 2803 11
19 nao_cumprida cumprida 2811 4 RV2025_OM6_E1_L12_C12 SIM_013_SBUY_SBMN 6 13 SBMN SBUY SBMN 2025-01-06T19:30:00Z 2025-01-15T01:13:04.800000Z 2025-01-06T21:25:00Z 2025-01-15T07:31:40.800000Z 1.92 6.31 1 3 DIURNO 2811 12
20 nao_cumprida cumprida 2811 5 RV2025_OM6_E2_L14_C14 SIM_014_SBBE_SBUA 6 14 SBMN SBBE SBMN SBUA 2025-01-07T11:20:00Z 2025-01-20T05:25:19.200000Z 2025-01-07T13:20:00Z 2025-01-20T10:31:55.200000Z 2.00 5.11 1 2 DIURNO 2811 14
21 nao_cumprida cumprida 2811 6 RV2025_OM7_E2_L16_C16 SIM_015_SBBE_SBTS 7 15 SBMN SBBE SBMN SBTS 2025-01-07T14:00:00Z 2025-01-21T05:22:01.200000Z 2025-01-07T15:45:00Z 2025-01-21T10:28:37.200000Z 1.75 5.11 1 4 DIURNO 2809 16
22 nao_cumprida cumprida 2811 7 RV2025_OM8_E2_L18_C18 SIM_017_SWBC_SBCC 8 17 SBMN SWBC SBMN SBCC 2025-01-08T14:00:00Z 2025-01-27T03:48:10.800000Z 2025-01-08T16:00:00Z 2025-01-27T07:24:46.800000Z 2.00 3.61 1 2 DIURNO 2803 18
23 nao_cumprida cumprida 2811 8 RV2025_OM10_E1_L19_C19 SIM_018_SBMY_SBPV 10 18 SBMN SBMY SBMN SBPV 2025-01-08T22:30:00Z 2025-01-28T01:55:19.200000Z 2025-01-08T23:15:00Z 2025-01-28T03:00:07.200000Z 0.75 1.08 1 2 NOTURNO DIURNO 2803 19
nao_cumprida RV2025_OM9_E2_L21_C21 9 SBMN SBMN 2025-01-09T18:10:00Z 2025-01-09T20:00:00Z 1.83 1 DIURNO 2803 21
nao_cumprida RV2025_OM11_E1_L23_C23 11 SBMN SBMN 2025-01-10T12:10:00Z 2025-01-10T13:15:00Z 1.08 1 DIURNO 2803 23
nao_cumprida RV2025_OM11_E2_L24_C24 11 SBMN SBMN 2025-01-10T14:25:00Z 2025-01-10T15:25:00Z 1.00 1 DIURNO 2803 24
nao_cumprida RV2025_OM13_E1_L30_C30 13 SBMN SBMN 2025-01-12T12:20:00Z 2025-01-12T13:10:00Z 0.83 1 DIURNO 2809 30
nao_cumprida RV2025_OM15_E2_L35_C35 15 SBMN SBMN 2025-01-12T14:25:00Z 2025-01-12T16:00:00Z 1.58 1 DIURNO 2803 35
nao_cumprida RV2025_OM13_E2_L31_C31 13 SBMN SBMN 2025-01-12T14:40:00Z 2025-01-12T15:25:00Z 0.75 1 DIURNO 2809 31
nao_cumprida RV2025_OM15_E3_L36_C36 15 SBMN SBMN 2025-01-12T18:30:00Z 2025-01-12T20:35:00Z 2.08 1 DIURNO 2803 36
nao_cumprida RV2025_OM14_E2_L33_C37 14 SBMN SBMN 2025-01-12T22:45:00Z 2025-01-13T02:05:00Z 3.33 1 NOTURNO 2809 33;37
nao_cumprida RV2025_OM16_E1_L38_C38 16 SBMN SBMN 2025-01-15T12:00:00Z 2025-01-15T15:05:00Z 3.08 1 DIURNO 2803 38
nao_cumprida RV2025_OM17_E2_L41_C41 17 SBMN SBMN 2025-01-15T14:45:00Z 2025-01-15T15:40:00Z 0.92 1 DIURNO 2809 41
nao_cumprida RV2025_OM16_E2_L39_C39 16 SBMN SBMN 2025-01-15T17:35:00Z 2025-01-15T20:35:00Z 3.00 1 DIURNO 2803 39
nao_cumprida RV2025_OM18_E1_L46_C46 18 SBMN SBMN 2025-01-21T12:05:00Z 2025-01-21T13:40:00Z 1.58 1 DIURNO 2800 46
nao_cumprida RV2025_OM18_E2_L47_C47 18 SBMN SBMN 2025-01-21T14:45:00Z 2025-01-21T16:15:00Z 1.50 1 DIURNO 2800 47
nao_cumprida RV2025_OM18_E3_L48_C48 18 SBMN SBMN 2025-01-21T18:40:00Z 2025-01-21T20:40:00Z 2.00 1 DIURNO 2800 48
nao_cumprida RV2025_OM19_E1_L49_C49 19 SBMN SBMN 2025-01-22T14:55:00Z 2025-01-22T16:00:00Z 1.08 1 DIURNO 2800 49
nao_cumprida RV2025_OM20_E1_L51_C51 20 SBMN SBMN 2025-01-22T14:55:00Z 2025-01-22T16:00:00Z 1.08 1 DIURNO 2803 51
nao_cumprida RV2025_OM21_E1_L53_C53 21 SBMN SBMN 2025-01-22T14:55:00Z 2025-01-22T16:00:00Z 1.08 1 DIURNO 2809 53
nao_cumprida RV2025_OM22_E1_L55_C55 22 SBMN SBMN 2025-01-22T14:55:00Z 2025-01-22T15:55:00Z 1.00 1 DIURNO 2811 55
nao_cumprida RV2025_OM19_E2_L50_C50 19 SBMN SBMN 2025-01-22T20:50:00Z 2025-01-22T21:25:00Z 0.58 1 DIURNO 2800 50
nao_cumprida RV2025_OM20_E2_L52_C52 20 SBMN SBMN 2025-01-22T20:50:00Z 2025-01-22T21:25:00Z 0.58 1 DIURNO 2803 52
nao_cumprida RV2025_OM21_E2_L54_C54 21 SBMN SBMN 2025-01-22T20:50:00Z 2025-01-22T21:25:00Z 0.58 1 DIURNO 2809 54
nao_cumprida RV2025_OM22_E2_L56_C56 22 SBMN SBMN 2025-01-22T20:50:00Z 2025-01-22T21:25:00Z 0.58 1 DIURNO 2811 56
nao_cumprida RV2025_OM27_E1_L68_C68 27 SBMN SBMN 2025-01-28T12:05:00Z 2025-01-28T13:05:00Z 1.00 1 DIURNO 2803 68
nao_cumprida RV2025_OM25_E1_L64_C64 25 SBMN SBMN 2025-01-28T13:25:00Z 2025-01-28T14:30:00Z 1.08 1 DIURNO 2809 64
nao_cumprida RV2025_OM27_E2_L69_C69 27 SBMN SBMN 2025-01-28T14:35:00Z 2025-01-28T15:30:00Z 0.92 1 DIURNO 2803 69
nao_cumprida 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
nao_cumprida RV2025_OM28_E1_L71_C71 28 SBMN SBMN 2025-01-29T18:15:00Z 2025-01-29T19:25:00Z 1.17 1 DIURNO 2809 71
nao_cumprida RV2025_OM29_E1_L72_C72 29 SBMN SBMN 2025-01-29T22:50:00Z 2025-01-30T00:00:00Z 1.17 1 NOTURNO 2809 72

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:

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
@@ -87,6 +112,70 @@ Formato:
- 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

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()

View File

@@ -30,7 +30,7 @@
Removidas: seq 18 (INSP 100FH) e seq 23 (INSP 50FH)
Horas iniciais f0_{k,i}:
--horas-iniciais <arquivo.json ou string JSON>
--f0 <arquivo.json ou string JSON>
--aleatorio [--seed N]
padrão: 0,0 para todas (aeronave recém-inspecionada — conservador)
============================================================================
@@ -43,7 +43,7 @@ import csv
import json
import random
from dataclasses import dataclass, field
from datetime import datetime, timezone
from datetime import date, datetime, timedelta, timezone
from pathlib import Path
from typing import Literal, Optional
@@ -120,7 +120,7 @@ class InspecaoParam:
sigla: str
descricao: str
nivel: str
lrt_h: float
f_max: float # F do Al-Thani: intervalo máximo legal entre manutenções
cal_dias: Optional[float]
dt_h: float
@@ -129,20 +129,16 @@ class InspecaoParam:
class Aeronave:
id: str
base_inicial: str
horas_iniciais: dict[int, float] = field(default_factory=dict)
f0: dict[int, float] = field(default_factory=dict) # f_k do Al-Thani: horas acumuladas no início
def orcamento_h(self, catalogo: dict[int, InspecaoParam]) -> float:
"""
Orçamento de horas disponíveis (C6 — Fase 1 simplificado).
Retorna min(LRT_i f0_{k,i}) sobre todas as inspeções ativas.
"""
def lrt_inicial(self, catalogo: dict[int, InspecaoParam]) -> float:
"""Retorna min(F_max_i f0_{k,i}): LRT disponível no início do horizonte."""
if not catalogo:
return 0.0
margens = [
insp.lrt_h - self.horas_iniciais.get(seq, 0.0)
return max(0.0, min(
insp.f_max - self.f0.get(seq, 0.0)
for seq, insp in catalogo.items()
]
return max(0.0, min(margens))
))
def insp_mais_restritiva(
self, catalogo: dict[int, InspecaoParam]
@@ -151,9 +147,9 @@ class Aeronave:
return None
seq_min = min(
catalogo,
key=lambda s: catalogo[s].lrt_h - self.horas_iniciais.get(s, 0.0),
key=lambda s: catalogo[s].f_max - self.f0.get(s, 0.0),
)
return seq_min, self.horas_iniciais.get(seq_min, 0.0), catalogo[seq_min]
return seq_min, self.f0.get(seq_min, 0.0), catalogo[seq_min]
# ---------------------------------------------------------------------------
@@ -432,6 +428,7 @@ def resolver(
arcos: list[Arco],
aeronaves: list[Aeronave],
catalogo: dict[int, InspecaoParam],
**kwargs,
) -> dict:
ks = [a.id for a in aeronaves]
acft = {a.id: a for a in aeronaves}
@@ -477,9 +474,7 @@ def resolver(
y[(k, a.id)] for a in arcs if (k, a.id) in y
)
# --- C1: Cobertura e definição de z ---
# Missões normais: Σ_k y_entrada ≤ 1 (cobertura opcional)
# Missões obrigatórias (M_obr / EVAM): Σ_k y_entrada == 1
# --- C1: Cobertura — Set Partitioning: toda missão coberta por exatamente 1 aeronave ---
for nid, n in missoes_nos.items():
fluxo_entrada = pulp.lpSum(
y[(k, a.id)]
@@ -488,10 +483,7 @@ def resolver(
if (k, a.id) in y
)
mdl += fluxo_entrada == z[nid], f"def_z_{nid}"
if n.missao.obrigatoria:
mdl += z[nid] == 1, f"hard_{nid}"
else:
mdl += z[nid] <= 1, f"cob_{nid}"
mdl += z[nid] == 1, f"part_{nid}"
# --- C2: Conservação de fluxo por (aeronave, nó) ---
for k in ks:
@@ -514,13 +506,16 @@ def resolver(
elif n.tipo == "missao":
# Missão: fluxo de entrada = fluxo de saída para esta aeronave
mdl += entrada_k == saida_k, f"fluxo_{k}_{nid}"
elif n.tipo == "inspecao" and n.aeronave_id == k:
# Inspeção exclusiva de k: conservação de fluxo (evita criação/destruição)
mdl += entrada_k == saida_k, f"fluxo_{k}_{nid}"
# --- Fase 2: variáveis e restrições C6-reset, C9, C11 ---
insp_nos = {nid: n for nid, n in nos.items() if n.tipo == "inspecao"}
# Big-M: usa LRT máximo (para horas) e horizonte temporal (para tempo)
M_h = max((insp.lrt_h for insp in catalogo.values()), default=1.0) + 100.0
M_h = max((insp.f_max for insp in catalogo.values()), default=1.0) + 100.0
M_t = (
max((n.t_fim for n in missoes_nos.values()), default=0.0) + 200.0
)
@@ -551,7 +546,7 @@ def resolver(
(k, seq, nid): pulp.LpVariable(
f"H_{k}_{seq}_{nid}",
lowBound=0,
upBound=catalogo[seq].lrt_h,
upBound=catalogo[seq].f_max,
)
for k in ks
for seq in catalogo
@@ -563,7 +558,7 @@ def resolver(
"""Retorna o valor/variável de H_{k,seq} ao sair do nó u."""
n = nos[u_nid]
if n.tipo == "origem":
return acft[k].horas_iniciais.get(seq, 0.0)
return acft[k].f0.get(seq, 0.0)
# Nó de reset para este clock: saída é 0
if n.tipo == "inspecao" and n.insp_seq == seq and n.aeronave_id == k:
return 0.0
@@ -672,27 +667,17 @@ def resolver(
f"c11b_{k1}_{seq1}_{k2}_{seq2}",
)
# --- C6 (Fase 1 — mantido como redundância explícita para verificação) ---
for k in ks:
orcamento = acft[k].orcamento_h(catalogo)
mdl += (
pulp.lpSum(
n.carga_h * y[(k, a.id)]
for nid, n in missoes_nos.items()
for a in arcos_entrando[nid]
if (k, a.id) in y
)
<= orcamento,
f"orcamento_{k}",
)
# C6 Fase 1 simplificada foi removida: a propagação de H[k,seq,nid] com reset
# já impõe o limite LRT por ciclo corretamente. A versão simplificada somava
# horas totais sem respeitar resets de inspeção, super-restringindo o modelo.
solver = pulp.PULP_CBC_CMD(msg=False)
time_limit = kwargs.get("time_limit", 120)
solver = pulp.PULP_CBC_CMD(msg=False, timeLimit=time_limit)
mdl.solve(solver)
status_l1 = mdl.status
prio_otima = int(round(pulp.value(mdl.objective) or 0))
# L2: com prioridade fixada, minimiza folga de horas ao fim do horizonte.
# folga_k = orcamento_k horas_voadas_k
# Minimizar Σ_k folga_k ⟺ maximizar Σ_k horas_voadas_k (orcamento é constante).
# L2: com prioridade fixada, maximiza horas voadas.
mdl += (
pulp.lpSum(n.missao.prioridade * z[nid] for nid, n in missoes_nos.items())
== prio_otima,
@@ -707,7 +692,10 @@ def resolver(
)
mdl.sense = pulp.LpMaximize
mdl.setObjective(horas_voadas)
mdl.solve(solver)
mdl.solve(pulp.PULP_CBC_CMD(msg=False, timeLimit=time_limit))
# PuLP redefine mdl.status após setObjective; preservar o status real (L1).
if mdl.status == 0 and status_l1 == 1:
mdl.status = status_l1
return {
"modelo": mdl,
@@ -798,6 +786,8 @@ def escrever_resultado(sol: dict, caminho: Path) -> None:
missoes_nos = sol["missoes_nos"]
linhas = []
horizonte_dt = sol.get("horizonte_dt")
cobertas_ids: set[str] = set()
for k in sol["ks"]:
missoes_rota = [n for n in rota_da_aeronave(sol, k) if n.tipo == "missao"]
@@ -821,6 +811,29 @@ def escrever_resultado(sol: dict, caminho: Path) -> None:
"linhas_origem": m.linhas_origem,
})
# Inspeções executadas por esta aeronave
if horizonte_dt is not None:
for seq, s_val, insp in inspecoes_da_aeronave(sol, k):
ini_dt = horizonte_dt + timedelta(hours=s_val)
fim_dt = horizonte_dt + timedelta(hours=s_val + insp.dt_h)
linhas.append({
"status": "inspecao",
"aeronave": k,
"ordem": "",
"id": f"INSP_{k}_{seq}",
"om": insp.sigla,
"orig": "SBMN",
"dest": "SBMN",
"partida_utc": ini_dt.isoformat().replace("+00:00", "Z"),
"chegada_utc": fim_dt.isoformat().replace("+00:00", "Z"),
"dur_h": f"{insp.dt_h:.2f}",
"prioridade": "",
"periodo": "",
"aeronave_real_2025": "",
"linhas_origem": "",
})
for nid, n in missoes_nos.items():
if nid in cobertas_ids:
continue
@@ -881,24 +894,24 @@ def imprimir_relatorio(
insp = catalogo[seq]
cal = f"{insp.cal_dias:.0f}d" if insp.cal_dias else ""
print(
f" seq {seq:2d} {insp.sigla:<18} LRT={insp.lrt_h:>6.0f}h "
f" seq {seq:2d} {insp.sigla:<18} F_max={insp.f_max:>6.0f}h "
f"CAL={cal:>5} DT={insp.dt_h:.1f}h [{insp.nivel}]"
)
print("\n Orcamento por aeronave:")
for k in sol["ks"]:
a = acft[k]
oc = a.orcamento_h(catalogo)
lrt_ini = a.lrt_inicial(catalogo)
info = a.insp_mais_restritiva(catalogo)
if info:
seq_min, f0, insp = info
seq_min, f0_val, insp = info
print(
f" {k}: {oc:.1f}h "
f" {k}: LRT_ini={lrt_ini:.1f}h "
f"(limitado por seq {seq_min} {insp.sigla}, "
f"f0={f0:.1f}h, LRT={insp.lrt_h:.0f}h)"
f"f0={f0_val:.1f}h, F_max={insp.f_max:.0f}h)"
)
else:
print(f" {k}: {oc:.1f}h (sem restricao de inspecao)")
print(f" {k}: LRT_ini={lrt_ini:.1f}h (sem restricao de inspecao)")
print()
arcos_por_id = {a.id: a for a in sol["arcos"]}
@@ -919,12 +932,12 @@ def imprimir_relatorio(
and a.horas_ferry > 0.0
)
horas_celula = horas_missao + horas_ferry_total
orcamento = acft[k].orcamento_h(catalogo)
folga = orcamento - horas_celula
lrt_ini = acft[k].lrt_inicial(catalogo)
folga = lrt_ini - horas_celula
print(
f" Aeronave {k}: {len(missoes_rota)} missoes | "
f"{horas_missao:.1f}h voadas + {horas_ferry_total:.1f}h ferry = {horas_celula:.1f}h celula | "
f"folga {folga:.1f}h | {len(insps)} inspecao(oes)"
f"LRT_ini={lrt_ini:.1f}h folga={folga:.1f}h | {len(insps)} inspecao(oes)"
)
# Linha do tempo: missões, inspeções e ferries ordenados por t_ini
eventos: list[tuple[float, str]] = []
@@ -1096,7 +1109,7 @@ def carregar_inspecoes(caminho: Path) -> dict[int, InspecaoParam]:
sigla=item["sigla_mnt"],
descricao=item["descricao_mnt"],
nivel=nivel_desc,
lrt_h=float(lrt),
f_max=float(lrt),
cal_dias=cal_dias,
dt_h=dt_h,
)
@@ -1144,8 +1157,9 @@ def derivar_bases(
# Missões sintéticas
# ---------------------------------------------------------------------------
# Coordenadas (lat, lon) das 10 bases operacionais do C-105
# Coordenadas (lat, lon) das bases operacionais do C-105
BASES_COORDS: dict[str, tuple[float, float]] = {
# Amazônia (bases primárias)
"SBMN": (-3.15, -59.99), # Manaus — Ponta Pelada (hub)
"SBBE": (-1.38, -48.48), # Belém
"SBBV": ( 2.84, -60.69), # Boa Vista
@@ -1156,6 +1170,24 @@ BASES_COORDS: dict[str, tuple[float, float]] = {
"SBUA": (-0.15, -67.05), # São Gabriel da Cachoeira
"SWBC": (-0.98, -62.92), # Barcelos
"SBMY": (-5.81, -61.28), # Manicoré
# Bases adicionais (escala de voo real)
"SBMQ": ( 0.05, -51.07), # Macapá
"SBOI": (-3.77, -38.53), # Fortaleza (Pinto Martins)
"SBVH": (-12.69, -60.10), # Vilhena
"SBBR": (-15.87, -47.92), # Brasília
"SBGL": (-22.81, -43.25), # Rio de Janeiro (Galeão)
"SBCY": (-15.65, -56.12), # Cuiabá
"SBCC": (-10.86, -51.80), # Canarana
"SBYS": (-23.00, -49.71), # Ourinhos
"SBCO": (-22.17, -47.88), # Cosmópolis
"SBSM": (-29.71, -53.69), # Santa Maria
"SBLO": (-23.33, -51.13), # Londrina
"SBAN": (-15.59, -56.10), # Anápolis
"SWEI": (-7.63, -72.68), # Eirunepé
"SWCA": (-8.85, -63.85), # Cacoal
"SWKO": (-11.50, -61.45), # Rolim de Moura
"SBTF": (-3.38, -64.72), # Tefé (alias SBTT)
"SBUY": (-29.78, -57.04), # Uruguaiana
}
_VELOCIDADE_CRUZEIRO_KMH = 430.0 # C-105 Spartan (típico)
@@ -1173,13 +1205,117 @@ def _haversine_km(b1: str, b2: str) -> float:
return R * 2 * math.asin(math.sqrt(a))
def carregar_escala(caminho: Path, inicio: datetime) -> tuple[list[Missao], datetime]:
"""
Lê a Escala de Voo Modelo 1 (CSV com separador ';') e retorna missões para o solver.
Formato esperado (2 linhas de cabeçalho):
DATA ; ETAPA ; DEP ; ARR ; HOR_DEP ; HOR_ARR ; DUR ; SEGMTO ; MISSÃO ; OFRAG
Cada linha vira uma Missao. Bases sem coordenadas conhecidas são descartadas.
A prioridade é derivada do tipo de missão:
69TV → prio 1 | 50TT/05TF → prio 3 | outros → prio 2
"""
_MES = {
"jan": 1, "fev": 2, "mar": 3, "abr": 4, "mai": 5, "jun": 6,
"jul": 7, "ago": 8, "set": 9, "out": 10, "nov": 11, "dez": 12,
}
def _parse_hora(s: str) -> tuple[int, int]:
parts = s.strip().split(":")
return int(parts[0]) % 24, int(parts[1])
def _prio(missao_id: str) -> int:
m = missao_id.upper()
if "69TV" in m:
return 1
if any(x in m for x in ("50TT", "05TF", "TT", "TF")):
return 3
return 2
missoes: list[Missao] = []
ano = inicio.year
data_atual: date | None = None
with caminho.open(newline="", encoding="latin-1") as f:
reader = csv.reader(f, delimiter=";")
next(reader) # cabeçalho 1
next(reader) # cabeçalho 2
for i, row in enumerate(reader):
if len(row) < 10:
continue
data_str, _, dep, arr, h_dep, h_arr, _, _, missao_id, ofrag = (
row[0].strip(), row[1].strip(), row[2].strip(), row[3].strip(),
row[4].strip(), row[5].strip(), row[6].strip(), row[7].strip(),
row[8].strip(), row[9].strip(),
)
if data_str:
partes = data_str.split("/")
if len(partes) == 2:
data_atual = date(ano, _MES.get(partes[1].lower(), 1), int(partes[0]))
if data_atual is None or not dep or not arr:
continue
if dep not in BASES_COORDS or arr not in BASES_COORDS:
continue
try:
hd, md = _parse_hora(h_dep)
ha, ma = _parse_hora(h_arr)
except Exception:
continue
t_dep_dt = datetime(data_atual.year, data_atual.month, data_atual.day,
hd, md, tzinfo=timezone.utc)
t_arr_dt = datetime(data_atual.year, data_atual.month, data_atual.day,
ha, ma, tzinfo=timezone.utc)
if t_arr_dt <= t_dep_dt: # chegada no dia seguinte
t_arr_dt += timedelta(days=1)
t_dep_h = (t_dep_dt - inicio).total_seconds() / 3600
t_arr_h = (t_arr_dt - inicio).total_seconds() / 3600
if t_dep_h < 0 or t_arr_h <= t_dep_h:
continue
missoes.append(Missao(
id=f"ESC_{i:03d}_{dep}_{arr}",
om=ofrag,
orig=dep,
dest=arr,
t_dep=t_dep_h,
t_arr=t_arr_h,
dur_h=t_arr_h - t_dep_h,
prioridade=_prio(missao_id),
obrigatoria=False,
partida_utc=t_dep_dt,
chegada_utc=t_arr_dt,
periodo="",
linhas_origem="",
aeronave_real="",
codigo_missao=missao_id,
))
if not missoes:
raise ValueError(f"Nenhuma missão válida carregada de {caminho}")
horizonte_dt = inicio
return missoes, horizonte_dt
TIPOS_MISSAO = ["Logística", "Médico", "Pessoal", "Reabastecimento", "Reconhecimento"]
# Pesos de prioridade: prio 1 (urgente) é mais frequente que prio 5 (baixa)
_PESOS_PRIO = [35, 30, 20, 10, 5]
def gerar_missoes_sinteticas(
n: int = 50,
n: int = 20,
seed: Optional[int] = 42,
inicio: Optional[datetime] = None,
horizonte_h: float = 744.0, # janeiro = 31 × 24
n_evam: int = 5,
n_evam: int = 4,
bases: Optional[list[str]] = None,
ofrag_path: Optional[Path] = None,
) -> tuple[list[Missao], datetime]:
"""
Gera n missões aleatórias cross-base (orig ≠ dest) entre as bases listadas.
@@ -1187,6 +1323,7 @@ def gerar_missoes_sinteticas(
Duração estimada via distância haversine ÷ velocidade de cruzeiro do C-105,
com variação aleatória de ± VARIACAO_DUR.
n_evam missões são marcadas como obrigatórias (hard).
Se ofrag_path for fornecido, prioridades são lidas do CSV de OFRAG.
"""
import math
@@ -1196,6 +1333,15 @@ def gerar_missoes_sinteticas(
bases = list(bases or BASES_COORDS.keys())
rng = random.Random(seed)
# Carrega prioridades do CSV de OFRAG, se fornecido
ofrag_prios: dict[str, int] = {}
ofrag_tipos: dict[str, str] = {}
if ofrag_path and ofrag_path.exists():
with ofrag_path.open(newline="", encoding="utf-8-sig") as f:
for row in csv.DictReader(f):
ofrag_prios[row["missao_id"]] = int(row["prioridade"])
ofrag_tipos[row["missao_id"]] = row.get("tipo_missao", "")
pares = [(o, d) for o in bases for d in bases if o != d]
missoes: list[Missao] = []
@@ -1236,15 +1382,24 @@ def gerar_missoes_sinteticas(
obrigatoria = idx in evam_indices
cod = f"EVAM{idx+1:03d}" if obrigatoria else f"SIM{idx+1:03d}"
om_str = str(idx + 1)
missao_id = f"SIM_{idx+1:03d}_{orig}_{dest}"
# Prioridade: EVAM sempre prio 1 (urgente); demais lidas do OFRAG ou geradas
if obrigatoria:
prio = 1
elif missao_id in ofrag_prios:
prio = ofrag_prios[missao_id]
else:
prio = rng.choices(range(1, 6), weights=_PESOS_PRIO)[0]
missoes.append(Missao(
id=f"SIM_{idx+1:03d}_{orig}_{dest}",
id=missao_id,
orig=orig,
dest=dest,
t_dep=t_dep_h,
t_arr=t_arr_h,
dur_h=dur_h,
prioridade=1,
prioridade=prio,
obrigatoria=obrigatoria,
partida_utc=partida,
chegada_utc=chegada,
@@ -1263,34 +1418,36 @@ def gerar_missoes_sinteticas(
# ---------------------------------------------------------------------------
def horas_iniciais_aleatorias(
def gerar_f0_aleatorio(
aeronaves: list[str],
catalogo: dict[int, InspecaoParam],
seed: Optional[int] = None,
) -> dict[str, dict[int, float]]:
"""Sorteia f0_{k,i} entre 10% e 90% de F_max_i para cada aeronave e inspeção."""
rng = random.Random(seed)
return {
k: {
seq: round(rng.uniform(0.1 * insp.lrt_h, 0.9 * insp.lrt_h), 1)
seq: round(rng.uniform(0.1 * insp.f_max, 0.9 * insp.f_max), 1)
for seq, insp in catalogo.items()
}
for k in aeronaves
}
def carregar_horas_iniciais(
def carregar_f0(
fonte: Optional[str],
aleatorio: bool,
aeronaves: list[str],
catalogo: dict[int, InspecaoParam],
seed: Optional[int] = None,
) -> dict[str, dict[int, float]]:
"""Retorna dict[aeronave_id -> dict[seq -> f0]] com horas iniciais acumuladas."""
if fonte:
p = Path(fonte)
raw = json.loads(p.read_text(encoding="utf-8")) if p.exists() else json.loads(fonte)
return {k: {int(s): float(v) for s, v in d.items()} for k, d in raw.items()}
if aleatorio:
return horas_iniciais_aleatorias(aeronaves, catalogo, seed=seed)
return gerar_f0_aleatorio(aeronaves, catalogo, seed=seed)
return {k: {seq: 0.0 for seq in catalogo} for k in aeronaves}
@@ -1309,8 +1466,8 @@ def main() -> None:
parser.add_argument("--fim", default=DATA_FIM_PADRAO)
parser.add_argument("--resultado", type=Path, default=CSV_RESULTADO_PADRAO)
parser.add_argument(
"--horas-iniciais", metavar="JSON",
help='f0_{k,i}: arquivo ou string JSON. Ex: {"2800": {"23": 35.0}}',
"--f0", metavar="JSON", dest="f0",
help='f0_{k,i} (horas acumuladas iniciais): arquivo ou string JSON. Ex: {"2800": {"2": 150.0}}',
)
parser.add_argument(
"--aleatorio", action="store_true",
@@ -1329,17 +1486,34 @@ def main() -> None:
"--hard-pattern", default=HARD_MISSAO_PADRAO,
help="Regex no campo 'missao' do CSV que marca missões obrigatórias (Q2).",
)
parser.add_argument(
"--escala", type=Path, default=None, metavar="CSV",
help="Escala de Voo Modelo 1 (CSV ';') com missões reais.",
)
parser.add_argument(
"--sintetico", action="store_true",
help="Usa missões sintéticas cross-base em vez do CSV real.",
)
parser.add_argument(
"--n-missoes", type=int, default=50,
help="Número de missões sintéticas a gerar (padrão: 50).",
"--n-missoes", type=int, default=20,
help="Número de missões sintéticas a gerar (padrão: 20).",
)
parser.add_argument(
"--n-evam", type=int, default=5,
help="Missões obrigatórias (EVAM/hard) nas sintéticas (padrão: 5).",
"--n-evam", type=int, default=4,
help="Missões obrigatórias (EVAM/hard) nas sintéticas (padrão: 4).",
)
parser.add_argument(
"--ofrag", type=Path, default=None, metavar="CSV",
help="CSV de OFRAG com prioridades por missão (missao_id, prioridade).",
)
parser.add_argument(
"--time-limit", type=int, default=120, dest="time_limit",
help="Limite de tempo em segundos para o solver CBC (padrão: 120).",
)
parser.add_argument(
"--insp-seqs", type=int, nargs="+", default=None, dest="insp_seqs",
metavar="SEQ",
help="Filtra catálogo para apenas estes seq de inspeção (ex: --insp-seqs 3).",
)
args = parser.parse_args()
@@ -1347,8 +1521,14 @@ def main() -> None:
fim = datetime.fromisoformat(args.fim).replace(tzinfo=timezone.utc)
catalogo = carregar_inspecoes(args.inspecoes)
if args.insp_seqs:
catalogo = {s: v for s, v in catalogo.items() if s in args.insp_seqs}
if args.sintetico:
if args.escala:
missoes, horizonte_dt = carregar_escala(args.escala, inicio)
bases = sorted({m.orig for m in missoes} | {m.dest for m in missoes})
print(f" Escala de voo: {len(missoes)} missões carregadas de {args.escala.name}")
elif args.sintetico:
horizonte_h = (fim - inicio).total_seconds() / 3600
bases = list(BASES_COORDS.keys())
missoes, horizonte_dt = gerar_missoes_sinteticas(
@@ -1358,6 +1538,7 @@ def main() -> None:
horizonte_h=horizonte_h,
n_evam=args.n_evam,
bases=bases,
ofrag_path=args.ofrag,
)
print(f" Modo sintético: {len(missoes)} missões geradas entre {len(bases)} bases.")
else:
@@ -1370,20 +1551,21 @@ def main() -> None:
hard_pattern=args.hard_pattern,
)
f0 = carregar_horas_iniciais(
fonte=args.horas_iniciais,
f0_map = carregar_f0(
fonte=args.f0,
aleatorio=args.aleatorio,
aeronaves=args.aeronaves,
catalogo=catalogo,
seed=args.seed,
)
aeronaves = [
Aeronave(id=k, base_inicial=args.base_inicial, horas_iniciais=f0.get(k, {}))
Aeronave(id=k, base_inicial=args.base_inicial, f0=f0_map.get(k, {}))
for k in args.aeronaves
]
nos, arcos = construir_rede(missoes, aeronaves, catalogo)
sol = resolver(nos, arcos, aeronaves, catalogo)
sol = resolver(nos, arcos, aeronaves, catalogo, time_limit=args.time_limit)
sol["horizonte_dt"] = horizonte_dt
escrever_resultado(sol, args.resultado)
imprimir_relatorio(sol, bases, args.resultado)

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()