Adiciona missoes sinteticas, ferry (Fase 3) e consolida versoes anteriores

- Gerador sintetico cross-base (50 missoes, 10 bases, haversine/C-105)
- Fase 3: arcos de ferry em construir_rede(); ferry consome horas de celula
- oamrp_v2.py e resultados CSV anteriores adicionados ao repositorio
- README, docs/about.md e changelog atualizados ate v0.6

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Cesa-V
2026-06-15 23:51:31 -03:00
parent 328a066849
commit 7cd7458565
9 changed files with 782 additions and 37 deletions

1
.gitignore vendored
View File

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

2
LOG.md
View File

@@ -30,3 +30,5 @@ Legenda de autores:
| 2026-06-15 | 21:00 | VTO | Criou modelo OAMRP v3 — Fase 1: rede espaço-tempo | `software/oamrp_v3.py`; `db/processed/resultado_oamrp_v3.csv` | Reescrita do zero com abstrações No/Arco e variável unificada y[k,arco]; C1C6; TAT=1,5h; f0_{k,i} editável; top-10 bases por frequência; missões EVAM hard; 50/50 missões cobertas em jan/2025. | | 2026-06-15 | 21:00 | VTO | Criou modelo OAMRP v3 — Fase 1: rede espaço-tempo | `software/oamrp_v3.py`; `db/processed/resultado_oamrp_v3.csv` | Reescrita do zero com abstrações No/Arco e variável unificada y[k,arco]; C1C6; TAT=1,5h; f0_{k,i} editável; top-10 bases por frequência; missões EVAM hard; 50/50 missões cobertas em jan/2025. |
| 2026-06-15 | 22:00 | VTO | Calibrou inspeções válidas (Q1) e respondeu Q2Q4 | `software/oamrp_v3.py` | Removidas seq 18 e 23 do catálogo; EVAM marcado como M_obr (cobertura=1); L2 definido como minimizar folga de horas ao fim do horizonte; 10 bases por frequência. | | 2026-06-15 | 22:00 | VTO | Calibrou inspeções válidas (Q1) e respondeu Q2Q4 | `software/oamrp_v3.py` | Removidas seq 18 e 23 do catálogo; EVAM marcado como M_obr (cobertura=1); L2 definido como minimizar folga de horas ao fim do horizonte; 10 bases por frequência. |
| 2026-06-15 | 22:30 | VTO | Implementou Fase 2: inspeções na rede espaço-tempo | `software/oamrp_v3.py` | Adicionados nós INSP_{k}_{seq} e arcos missao_insp/insp_missao; variáveis H_{k,i,nid} para rastreamento de horas com reset; C9 downtime; C11 slot único via disjuntivas. | | 2026-06-15 | 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. |

View File

@@ -182,3 +182,19 @@ db/processed/registro_voo_2025_consolidado.csv
``` ```
O consolidado trata `ROTA` como marcador de transicao, nao como localidade operacional, e remove a linha original 793 da aeronave 2806 dos artefatos processados. O consolidado trata `ROTA` como marcador de transicao, nao como localidade operacional, e remove a linha original 793 da aeronave 2806 dos artefatos processados.
## Modelo OAMRP v2
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
```
Para executar:
```powershell
python software/oamrp_v2.py
```
A rodada inicial de janeiro/2025 foi resolvida como otima pelo CBC, com 50 de 61 missoes cumpridas.

View File

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

View File

@@ -0,0 +1,51 @@
status,aeronave,ordem,id,om,orig,dest,partida_utc,chegada_utc,dur_h,prioridade,periodo,aeronave_real_2025,linhas_origem
cumprida,2800,1,SIM_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,,
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_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_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_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_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_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_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_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_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_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_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 14 SBMN SBTS 2025-01-11T09:41:20.400000Z 2025-01-11T12:08:20.400000Z 2.45 1 DIURNO
3 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
4 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
5 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
6 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
7 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
8 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
9 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
10 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
11 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
12 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
13 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
14 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
15 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
16 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
17 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
18 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
19 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
20 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
21 nao_cumprida SIM_015_SBMN_SBPV 15 SBMN SBPV 2025-01-12T01:42:18Z 2025-01-12T03:21:54Z 1.66 1 DIURNO
22 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
23 nao_cumprida SIM_017_SBSN_SBMN 17 SBSN SBMN 2025-01-13T06:27:18Z 2025-01-13T07:41:42Z 1.24 1 DIURNO
24 nao_cumprida SIM_018_SBSN_SBBV 18 SBSN SBBV 2025-01-14T02:28:12Z 2025-01-14T04:36:36Z 2.14 1 DIURNO
25 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
26 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
27 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
28 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
29 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
30 nao_cumprida SIM_025_SWBC_SBMN 25 SWBC SBMN 2025-01-18T08:18:36Z 2025-01-18T09:11:24Z 0.88 1 DIURNO
31 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
32 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
33 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
34 nao_cumprida SIM_029_SBTT_SBSN 29 SBTT SBSN 2025-01-20T02:58:12Z 2025-01-20T05:46:12Z 2.80 1 DIURNO
35 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
36 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
37 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
38 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
39 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
40 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
41 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
42 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
43 nao_cumprida SIM_040_SBSN_SBMN 40 SBSN SBMN 2025-01-24T07:16:48Z 2025-01-24T08:30:36Z 1.23 1 DIURNO
44 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
45 nao_cumprida SIM_043_SBMN_SBPV 43 SBMN SBPV 2025-01-26T17:47:06Z 2025-01-26T19:21:54Z 1.58 1 DIURNO
46 nao_cumprida SIM_044_SBBE_SBPV 44 SBBE SBPV 2025-01-27T08:03:00Z 2025-01-27T12:37:48Z 4.58 1 DIURNO
47 nao_cumprida SIM_046_SBMN_SBUA 46 SBMN SBUA 2025-01-28T01:26:06Z 2025-01-28T03:14:42Z 1.81 1 DIURNO
48 nao_cumprida SIM_047_SWBC_SBTS 47 SWBC SBTS 2025-01-28T22:14:31.200000Z 2025-01-29T00:03:07.200000Z 1.81 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 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

@@ -172,3 +172,30 @@ O relatorio de validacao esta em:
```text ```text
db/pre_process/registro_voo_2025/validacao_registro_voo_2025.md db/pre_process/registro_voo_2025/validacao_registro_voo_2025.md
``` ```
## Modelo OAMRP v2
O arquivo `software/oamrp_v2.py` e a primeira versao do modelo usando o registro de voo 2025 consolidado.
Entradas principais:
- `db/processed/registro_voo_2025_consolidado.csv`;
- janela padrao de `2025-01-01` ate `2025-02-01` exclusivo;
- aeronaves reais `2800`, `2803`, `2809` e `2811`;
- base inicial provisoria `SBMN`;
- base de manutencao `SBMN`;
- prioridade uniforme: todas as missoes recebem peso 1.
Saida principal:
```text
db/processed/resultado_oamrp_jan_2025.csv
```
Resultado da primeira execucao:
- status do solver: `Optimal`;
- missoes cumpridas: 50/61;
- prioridade obtida: 50/61.
Limitacao atual: o limite de horas foi deixado alto para validar primeiro a continuidade espacial/temporal. A calibracao com dados reais de manutencao ainda deve ser feita em etapa posterior.

View File

@@ -87,6 +87,20 @@ Formato:
- A remocao foi aplicada no script de pre-processamento para manter reprodutibilidade. - A remocao foi aplicada no script de pre-processamento para manter reprodutibilidade.
## v0.6 - VTO - 2026-06
### Adicionado
- Gerador de missões sintéticas cross-base (`gerar_missoes_sinteticas()`): 50 missões entre 10 bases com durações calculadas via haversine ÷ velocidade C-105 (430 km/h); flag `--sintetico`, `--n-missoes`, `--n-evam` na CLI.
- Fase 3: arcos de ferry em `construir_rede()` — arcos `inicio`, `missao_missao`, `missao_insp` e `insp_missao` agora permitem bases distintas com tempo de ferry calculado automaticamente.
- Ferry consome horas de célula no relógio de inspeção (C6: `carga = v.carga_h + a.horas_ferry`).
- Relatório imprime segmentos `[FERRY base1->base2 Xh]` na linha do tempo de cada aeronave.
- Resultado `db/processed/resultado_oamrp_sintetico.csv` para 50 missões sintéticas.
### Alterado
- Dicionário `BASES_COORDS` com coordenadas (lat, lon) das 10 bases operacionais do C-105.
## v0.5 - VTO - 2026-06 ## v0.5 - VTO - 2026-06
### Adicionado ### Adicionado

375
software/oamrp_v2.py Normal file
View File

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

View File

@@ -240,13 +240,12 @@ def construir_rede(
- sumidouro_k : ponto de chegada de cada aeronave k - sumidouro_k : ponto de chegada de cada aeronave k
Tipos de arcos: Tipos de arcos:
- inicio : origem_k → missao_m (m.orig == base_k) - inicio : origem_k → missao_m (com ou sem ferry inicial)
- missao_missao : missao_m → missao_m' (dest==orig e gap ≥ TAT) - missao_missao : missao_m → missao_m' (dest==orig e gap ≥ TAT)
- missao_insp : missao_m → inspecao_k_i (m.dest == SBMN) - ferry : missao_m → missao_m' (dest≠orig, gap ≥ TAT + tempo_ferry)
- insp_missao : inspecao_k_i → missao_m' (m'.orig == SBMN, t_dep ≥ DT_i+TAT) - missao_insp : missao_m → inspecao_k_i (m.dest == SBMN, ou com ferry)
- insp_missao : inspecao_k_i → missao_m' (m'.orig == SBMN ou com ferry)
- fim : missao_m / inspecao_k_i → sumidouro_k - fim : missao_m / inspecao_k_i → sumidouro_k
Fase 3 adicionará arcos de ferry entre bases distintas.
""" """
nos: dict[str, No] = {} nos: dict[str, No] = {}
arcos: list[Arco] = [] arcos: list[Arco] = []
@@ -288,11 +287,22 @@ def construir_rede(
aeronave_id=a.id, aeronave_id=a.id,
) )
# --- Arcos início: origem_k → missao_m --- # Helper: tempo de ferry entre bases (0 se mesma base)
def _ferry_h(b1: str, b2: str) -> float:
if b1 == b2:
return 0.0
if b1 not in BASES_COORDS or b2 not in BASES_COORDS:
return float("inf")
return _haversine_km(b1, b2) / _VELOCIDADE_CRUZEIRO_KMH
# --- Arcos início: origem_k → missao_m (com ou sem ferry) ---
for a in aeronaves: for a in aeronaves:
src_id = f"SRC_{a.id}" src_id = f"SRC_{a.id}"
for m in missoes: for m in missoes:
if m.orig != a.base_inicial: fh = _ferry_h(a.base_inicial, m.orig)
if fh == float("inf"):
continue
if m.t_dep < fh: # missão começa antes do ferry terminar
continue continue
arc_id = f"INI_{a.id}_{m.id}" arc_id = f"INI_{a.id}_{m.id}"
arcos.append( arcos.append(
@@ -300,19 +310,21 @@ def construir_rede(
id=arc_id, id=arc_id,
orig_id=src_id, orig_id=src_id,
dest_id=f"M_{m.id}", dest_id=f"M_{m.id}",
tipo="inicio", tipo="inicio" if fh == 0.0 else "ferry",
aeronave_id=a.id, aeronave_id=a.id,
horas_ferry=fh,
) )
) )
# --- Arcos missao → missao --- # --- Arcos missao → missao (direto ou com ferry) ---
for mi in missoes: for mi in missoes:
for mj in missoes: for mj in missoes:
if mi.id == mj.id: if mi.id == mj.id:
continue continue
if mi.dest != mj.orig: fh = _ferry_h(mi.dest, mj.orig)
if fh == float("inf"):
continue continue
if mj.t_dep < mi.t_arr + tat_h: if mj.t_dep < mi.t_arr + tat_h + fh:
continue continue
arc_id = f"MM_{mi.id}_{mj.id}" arc_id = f"MM_{mi.id}_{mj.id}"
arcos.append( arcos.append(
@@ -320,7 +332,8 @@ def construir_rede(
id=arc_id, id=arc_id,
orig_id=f"M_{mi.id}", orig_id=f"M_{mi.id}",
dest_id=f"M_{mj.id}", dest_id=f"M_{mj.id}",
tipo="missao_missao", tipo="missao_missao" if fh == 0.0 else "ferry",
horas_ferry=fh,
) )
) )
@@ -355,19 +368,22 @@ def construir_rede(
aeronave_id=a.id, aeronave_id=a.id,
) )
# SRC_k → INSP_k_i (aeronave começa o horizonte em SBMN e vai direto) # SRC_k → INSP_k_i (com ou sem ferry até SBMN)
if a.base_inicial == BASE_MANUTENCAO: fh_src = _ferry_h(a.base_inicial, BASE_MANUTENCAO)
if fh_src != float("inf"):
arcos.append(Arco( arcos.append(Arco(
id=f"SRC_INSP_{a.id}_{seq}", id=f"SRC_INSP_{a.id}_{seq}",
orig_id=f"SRC_{a.id}", orig_id=f"SRC_{a.id}",
dest_id=insp_nid, dest_id=insp_nid,
tipo="inicio", tipo="inicio",
aeronave_id=a.id, aeronave_id=a.id,
horas_ferry=fh_src,
)) ))
# M_m → INSP_k_i: missões que chegam em SBMN # M_m → INSP_k_i: missões de qualquer base (ferry até SBMN)
for m in missoes: for m in missoes:
if m.dest != BASE_MANUTENCAO: fh = _ferry_h(m.dest, BASE_MANUTENCAO)
if fh == float("inf"):
continue continue
arcos.append(Arco( arcos.append(Arco(
id=f"MINSP_{a.id}_{seq}_{m.id}", id=f"MINSP_{a.id}_{seq}_{m.id}",
@@ -375,13 +391,15 @@ def construir_rede(
dest_id=insp_nid, dest_id=insp_nid,
tipo="missao_insp", tipo="missao_insp",
aeronave_id=a.id, aeronave_id=a.id,
horas_ferry=fh,
)) ))
# INSP_k_i → M_m': missões que partem de SBMN após o downtime mínimo # INSP_k_i → M_m': missões de qualquer base após downtime (ferry de SBMN)
for m in missoes: for m in missoes:
if m.orig != BASE_MANUTENCAO: fh = _ferry_h(BASE_MANUTENCAO, m.orig)
if fh == float("inf"):
continue continue
if m.t_dep < insp.dt_h + tat_h: # muito cedo mesmo no melhor caso if m.t_dep < insp.dt_h + tat_h + fh:
continue continue
arcos.append(Arco( arcos.append(Arco(
id=f"INSPM_{a.id}_{seq}_{m.id}", id=f"INSPM_{a.id}_{seq}_{m.id}",
@@ -389,6 +407,7 @@ def construir_rede(
dest_id=f"M_{m.id}", dest_id=f"M_{m.id}",
tipo="insp_missao", tipo="insp_missao",
aeronave_id=a.id, aeronave_id=a.id,
horas_ferry=fh,
)) ))
# INSP_k_i → SNK_k (aeronave encerra o horizonte após inspeção) # INSP_k_i → SNK_k (aeronave encerra o horizonte após inspeção)
@@ -566,7 +585,8 @@ def resolver(
h_prev = _H_prev(k, seq, a.orig_id) h_prev = _H_prev(k, seq, a.orig_id)
if h_prev is None: if h_prev is None:
continue continue
carga = nos[v_nid].carga_h # dur_h para missões, 0 para inspeções # horas acumuladas = missão + ferry (C6: traslado consome horas de célula)
carga = nos[v_nid].carga_h + a.horas_ferry
y_var = y[(k, a.id)] y_var = y[(k, a.id)]
mdl += H_v >= h_prev + carga - M_h * (1 - y_var), f"Hlo_{k}_{seq}_{a.id}" mdl += H_v >= h_prev + carga - M_h * (1 - y_var), f"Hlo_{k}_{seq}_{a.id}"
mdl += H_v <= h_prev + carga + M_h * (1 - y_var), f"Hhi_{k}_{seq}_{a.id}" mdl += H_v <= h_prev + carga + M_h * (1 - y_var), f"Hhi_{k}_{seq}_{a.id}"
@@ -847,7 +867,7 @@ def imprimir_relatorio(
total = len(missoes_nos) total = len(missoes_nos)
print("=" * 72) print("=" * 72)
print(" OAMRP V3 — Fase 1 (C1C6) | Esquadrao Arara C-105") print(" OAMRP V3 — Fase 3 (C1C11 + ferry) | Esquadrao Arara C-105")
print("=" * 72) print("=" * 72)
print(f" Status solver : {pulp.LpStatus[sol['modelo'].status]}") print(f" Status solver : {pulp.LpStatus[sol['modelo'].status]}")
print(f" Missoes : {len(cobertas)}/{total} cumpridas") print(f" Missoes : {len(cobertas)}/{total} cumpridas")
@@ -881,22 +901,47 @@ def imprimir_relatorio(
print(f" {k}: {oc:.1f}h (sem restricao de inspecao)") print(f" {k}: {oc:.1f}h (sem restricao de inspecao)")
print() print()
arcos_por_id = {a.id: a for a in sol["arcos"]}
y = sol["y"]
for k in sol["ks"]: for k in sol["ks"]:
rota = rota_da_aeronave(sol, k) rota = rota_da_aeronave(sol, k)
insps = inspecoes_da_aeronave(sol, k) insps = inspecoes_da_aeronave(sol, k)
missoes_rota = [n for n in rota if n.tipo == "missao"] missoes_rota = [n for n in rota if n.tipo == "missao"]
horas = sum(n.carga_h for n in missoes_rota) horas_missao = sum(n.carga_h for n in missoes_rota)
# horas de ferry: soma horas_ferry dos arcos ativos para este k
horas_ferry_total = sum(
a.horas_ferry
for a in sol["arcos"]
if (k, a.id) in y
and y[(k, a.id)].value() is not None
and y[(k, a.id)].value() > 0.5
and a.horas_ferry > 0.0
)
horas_celula = horas_missao + horas_ferry_total
orcamento = acft[k].orcamento_h(catalogo) orcamento = acft[k].orcamento_h(catalogo)
folga = orcamento - horas folga = orcamento - horas_celula
print( print(
f" Aeronave {k}: {len(missoes_rota)} missoes | {horas:.1f}h voadas | " 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"folga {folga:.1f}h | {len(insps)} inspecao(oes)"
) )
# Linha do tempo unificada: missões e inspeções ordenadas por t_ini # Linha do tempo: missões, inspeções e ferries ordenados por t_ini
eventos: list[tuple[float, str]] = [] eventos: list[tuple[float, str]] = []
for no in rota: # rastrear nó anterior para identificar ferry
nos_rota_ids = [n.id for n in rota]
for idx, no in enumerate(rota):
if no.tipo == "missao": if no.tipo == "missao":
m = no.missao m = no.missao
# detecta ferry antes desta missão
if idx > 0:
prev = rota[idx - 1]
if prev.tipo in ("missao", "origem") and prev.base_out != no.base_in:
fh = _haversine_km(prev.base_out, no.base_in) / _VELOCIDADE_CRUZEIRO_KMH
eventos.append((
no.t_ini - fh,
f" [FERRY {prev.base_out}->{no.base_in} {fh:.1f}h]"
))
eventos.append(( eventos.append((
no.t_ini, no.t_ini,
f" {m.partida_utc:%d/%m %H:%M}Z " f" {m.partida_utc:%d/%m %H:%M}Z "
@@ -906,15 +951,23 @@ def imprimir_relatorio(
seq = no.insp_seq seq = no.insp_seq
s_val = sol["s_insp"][(k, seq)].value() or 0.0 s_val = sol["s_insp"][(k, seq)].value() or 0.0
insp = catalogo[seq] insp = catalogo[seq]
if idx > 0:
prev = rota[idx - 1]
if prev.tipo == "missao" and prev.base_out != BASE_MANUTENCAO:
fh = _haversine_km(prev.base_out, BASE_MANUTENCAO) / _VELOCIDADE_CRUZEIRO_KMH
eventos.append((
s_val - fh,
f" [FERRY {prev.base_out}->{BASE_MANUTENCAO} {fh:.1f}h (retorno insp)]"
))
eventos.append(( eventos.append((
s_val, s_val,
f" [INSP seq {seq} {insp.sigla}] " f" [INSP seq {seq} {insp.sigla}] "
f"s={s_val:.1f}h DT={insp.dt_h:.1f}h fim={s_val + insp.dt_h:.1f}h" f"s={s_val:.1f}h DT={insp.dt_h:.1f}h fim={s_val + insp.dt_h:.1f}h"
)) ))
for _, linha in sorted(eventos)[:15]: for _, linha in sorted(eventos)[:20]:
print(linha) print(linha)
if len(eventos) > 15: if len(eventos) > 20:
print(f" ... mais {len(eventos) - 15} eventos") print(f" ... mais {len(eventos) - 20} eventos")
print() print()
nao = [nid for nid in missoes_nos if nid not in cobertas] nao = [nid for nid in missoes_nos if nid not in cobertas]
@@ -1087,6 +1140,124 @@ def derivar_bases(
return sorted(top) return sorted(top)
# ---------------------------------------------------------------------------
# Missões sintéticas
# ---------------------------------------------------------------------------
# Coordenadas (lat, lon) das 10 bases operacionais do C-105
BASES_COORDS: dict[str, tuple[float, float]] = {
"SBMN": (-3.15, -59.99), # Manaus — Ponta Pelada (hub)
"SBBE": (-1.38, -48.48), # Belém
"SBBV": ( 2.84, -60.69), # Boa Vista
"SBSN": (-2.42, -54.79), # Santarém
"SBPV": (-8.71, -63.90), # Porto Velho
"SBTS": (-4.25, -69.94), # Tabatinga
"SBTT": (-3.38, -64.72), # Tefé
"SBUA": (-0.15, -67.05), # São Gabriel da Cachoeira
"SWBC": (-0.98, -62.92), # Barcelos
"SBMY": (-5.81, -61.28), # Manicoré
}
_VELOCIDADE_CRUZEIRO_KMH = 430.0 # C-105 Spartan (típico)
_VARIACAO_DUR = 0.10 # ± 10 % de variação aleatória na duração
def _haversine_km(b1: str, b2: str) -> float:
import math
lat1, lon1 = BASES_COORDS[b1]
lat2, lon2 = BASES_COORDS[b2]
R = 6371.0
dlat = math.radians(lat2 - lat1)
dlon = math.radians(lon2 - lon1)
a = math.sin(dlat/2)**2 + math.cos(math.radians(lat1)) * math.cos(math.radians(lat2)) * math.sin(dlon/2)**2
return R * 2 * math.asin(math.sqrt(a))
def gerar_missoes_sinteticas(
n: int = 50,
seed: Optional[int] = 42,
inicio: Optional[datetime] = None,
horizonte_h: float = 744.0, # janeiro = 31 × 24
n_evam: int = 5,
bases: Optional[list[str]] = None,
) -> tuple[list[Missao], datetime]:
"""
Gera n missões aleatórias cross-base (orig ≠ dest) entre as bases listadas.
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).
"""
import math
if inicio is None:
inicio = datetime(2025, 1, 1, tzinfo=timezone.utc)
bases = list(bases or BASES_COORDS.keys())
rng = random.Random(seed)
pares = [(o, d) for o in bases for d in bases if o != d]
missoes: list[Missao] = []
# sorteia pares com reposição para garantir n missões
escolhidos = [pares[rng.randrange(len(pares))] for _ in range(n)]
# garante que SBMN aparece em orig ou dest com frequência (hub real)
for i in range(0, n, 3):
outro = rng.choice([b for b in bases if b != "SBMN"])
if rng.random() < 0.5:
escolhidos[i] = ("SBMN", outro)
else:
escolhidos[i] = (outro, "SBMN")
evam_indices = set(rng.sample(range(n), min(n_evam, n)))
# distribui decolagens uniformemente ao longo do horizonte com gap mínimo de 2h
t_deps = sorted(rng.uniform(0, horizonte_h - 10) for _ in range(n))
for idx, (t_dep_h, (orig, dest)) in enumerate(zip(t_deps, escolhidos)):
dist_km = _haversine_km(orig, dest)
dur_base = dist_km / _VELOCIDADE_CRUZEIRO_KMH
dur_h = round(dur_base * (1 + rng.uniform(-_VARIACAO_DUR, _VARIACAO_DUR)), 2)
dur_h = max(0.5, dur_h)
t_dep_h = round(t_dep_h, 3)
t_arr_h = round(t_dep_h + dur_h, 3)
if t_arr_h > horizonte_h:
t_arr_h = horizonte_h
t_dep_h = max(0.0, t_arr_h - dur_h)
partida = datetime.fromtimestamp(
inicio.timestamp() + t_dep_h * 3600, tz=timezone.utc
)
chegada = datetime.fromtimestamp(
inicio.timestamp() + t_arr_h * 3600, tz=timezone.utc
)
obrigatoria = idx in evam_indices
cod = f"EVAM{idx+1:03d}" if obrigatoria else f"SIM{idx+1:03d}"
om_str = str(idx + 1)
missoes.append(Missao(
id=f"SIM_{idx+1:03d}_{orig}_{dest}",
orig=orig,
dest=dest,
t_dep=t_dep_h,
t_arr=t_arr_h,
dur_h=dur_h,
prioridade=1,
obrigatoria=obrigatoria,
partida_utc=partida,
chegada_utc=chegada,
periodo="DIURNO",
linhas_origem="",
aeronave_real="",
om=om_str,
codigo_missao=cod,
))
return missoes, inicio
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Horas iniciais # Horas iniciais
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@@ -1158,20 +1329,46 @@ def main() -> None:
"--hard-pattern", default=HARD_MISSAO_PADRAO, "--hard-pattern", default=HARD_MISSAO_PADRAO,
help="Regex no campo 'missao' do CSV que marca missões obrigatórias (Q2).", help="Regex no campo 'missao' do CSV que marca missões obrigatórias (Q2).",
) )
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).",
)
parser.add_argument(
"--n-evam", type=int, default=5,
help="Missões obrigatórias (EVAM/hard) nas sintéticas (padrão: 5).",
)
args = parser.parse_args() args = parser.parse_args()
inicio = datetime.fromisoformat(args.inicio).replace(tzinfo=timezone.utc) inicio = datetime.fromisoformat(args.inicio).replace(tzinfo=timezone.utc)
fim = datetime.fromisoformat(args.fim).replace(tzinfo=timezone.utc) fim = datetime.fromisoformat(args.fim).replace(tzinfo=timezone.utc)
catalogo = carregar_inspecoes(args.inspecoes) catalogo = carregar_inspecoes(args.inspecoes)
top_n = args.n_bases if args.n_bases > 0 else None
bases = derivar_bases(args.csv, inicio, fim, top_n=top_n) if args.sintetico:
bases_validas = set(bases) if top_n else None horizonte_h = (fim - inicio).total_seconds() / 3600
missoes, _ = carregar_missoes( bases = list(BASES_COORDS.keys())
args.csv, inicio, fim, missoes, horizonte_dt = gerar_missoes_sinteticas(
bases_validas=bases_validas, n=args.n_missoes,
hard_pattern=args.hard_pattern, seed=args.seed,
) inicio=inicio,
horizonte_h=horizonte_h,
n_evam=args.n_evam,
bases=bases,
)
print(f" Modo sintético: {len(missoes)} missões geradas entre {len(bases)} bases.")
else:
top_n = args.n_bases if args.n_bases > 0 else None
bases = derivar_bases(args.csv, inicio, fim, top_n=top_n)
bases_validas = set(bases) if top_n else None
missoes, horizonte_dt = carregar_missoes(
args.csv, inicio, fim,
bases_validas=bases_validas,
hard_pattern=args.hard_pattern,
)
f0 = carregar_horas_iniciais( f0 = carregar_horas_iniciais(
fonte=args.horas_iniciais, fonte=args.horas_iniciais,