Corrige modelo OAMRP v3 e alinha nomenclatura com Al-Thani (2016)

Correções ao modelo:
- Adiciona conservação de fluxo em nós de inspeção (bug que permitia
  criação/destruição de unidades de fluxo, inflando cobertura para 50/50
  sem atribuição real de rotas)
- Remove restrição C6 simplificada que somava horas totais sem respeitar
  resets de inspeção, tornando 2809/2811 artificialmente ociosos

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

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

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

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Cesa-V
2026-06-16 00:50:53 -03:00
parent 7cd7458565
commit f0d72f8189
6 changed files with 2500 additions and 88 deletions

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

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

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)
============================================================================
@@ -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]
# ---------------------------------------------------------------------------
@@ -514,13 +510,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 +550,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 +562,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,19 +671,9 @@ 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)
mdl.solve(solver)
@@ -881,24 +870,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 +908,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 +1085,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,
)
@@ -1173,6 +1162,12 @@ def _haversine_km(b1: str, b2: str) -> float:
return R * 2 * math.asin(math.sqrt(a))
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,
seed: Optional[int] = 42,
@@ -1180,6 +1175,7 @@ def gerar_missoes_sinteticas(
horizonte_h: float = 744.0, # janeiro = 31 × 24
n_evam: int = 5,
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 +1183,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 +1193,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 +1242,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 +1278,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 +1326,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",
@@ -1341,6 +1358,10 @@ def main() -> None:
"--n-evam", type=int, default=5,
help="Missões obrigatórias (EVAM/hard) nas sintéticas (padrão: 5).",
)
parser.add_argument(
"--ofrag", type=Path, default=None, metavar="CSV",
help="CSV de OFRAG com prioridades por missão (missao_id, prioridade).",
)
args = parser.parse_args()
inicio = datetime.fromisoformat(args.inicio).replace(tzinfo=timezone.utc)
@@ -1358,6 +1379,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,15 +1392,15 @@ 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
]

View File

@@ -0,0 +1,232 @@
"""
Visualização interativa do resultado OAMRP — Esquadrão Arara C-105.
Gera um mapa HTML (Folium) com:
- Marcadores para cada base operacional
- Rotas por aeronave (cor distinta por EVAM)
- Missões não cumpridas em cinza tracejado
- Popup com detalhes de cada missão
Uso:
python software/visualizar_resultado.py \
--resultado db/processed/resultado_oamrp_sintetico.csv \
--saida db/processed/mapa_rotas.html
"""
import argparse
import csv
from pathlib import Path
import folium
from folium import plugins
BASE_DIR = Path(__file__).resolve().parents[1]
RESULTADO_PADRAO = BASE_DIR / "db" / "processed" / "resultado_oamrp_sintetico.csv"
MAPA_PADRAO = BASE_DIR / "db" / "processed" / "mapa_rotas.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),
}
# Nomes legíveis das bases
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é",
}
# Paleta de cores por aeronave
CORES_AERONAVE = {
"2800": "#1f77b4", # azul
"2803": "#2ca02c", # verde
"2809": "#d62728", # vermelho
"2811": "#ff7f0e", # laranja
}
COR_NAO_CUMPRIDA = "#aaaaaa"
COR_HUB = "#d62728"
COR_BASE = "#1f77b4"
def _cor_prio(prio: str) -> str:
mapa = {"1": "red", "2": "orange", "3": "yellow", "4": "lightblue", "5": "lightgray"}
return mapa.get(str(prio), "white")
def carregar_resultado(caminho: Path) -> list[dict]:
with caminho.open(newline="", encoding="utf-8-sig") as f:
return list(csv.DictReader(f))
def adicionar_bases(mapa: folium.Map) -> None:
for icao, (lat, lon) in BASES_COORDS.items():
cor = COR_HUB if icao == "SBMN" else COR_BASE
icone = folium.Icon(color="red" if icao == "SBMN" else "blue", icon="plane", prefix="fa")
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=icone,
).add_to(mapa)
def gerar_mapa(resultado: Path, saida: Path) -> None:
linhas = carregar_resultado(resultado)
mapa = folium.Map(
location=[-3.5, -62.0],
zoom_start=5,
tiles="CartoDB positron",
)
# Grupos por aeronave + grupo para não cumpridas
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"EVAM {k}", show=True)
grupos["nao_cumprida"] = folium.FeatureGroup(name="Não cumpridas", show=True)
cumpridas = [r for r in linhas if r["status"] == "cumprida"]
nao_cumpridas = [r for r in linhas if r["status"] != "cumprida"]
# Rotas cumpridas
for r in cumpridas:
orig = r["orig"]
dest = 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]
k = r["aeronave"]
cor = CORES_AERONAVE.get(k, "#333333")
# Linha de rota
folium.PolyLine(
locations=[[lat_o, lon_o], [lat_d, lon_d]],
color=cor,
weight=2.5,
opacity=0.8,
tooltip=f"EVAM {k} | {orig}{dest} | OM {r['om']} | prio {r['prioridade']}",
popup=folium.Popup(
f"<b>EVAM {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])
# Marcador de origem (pequeno círculo)
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])
# Missões não cumpridas (linha tracejada cinza)
for r in nao_cumpridas:
orig = r["orig"]
dest = 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"])
# Adiciona grupos ao mapa
for g in grupos.values():
g.add_to(mapa)
adicionar_bases(mapa)
folium.LayerControl(collapsed=False).add_to(mapa)
# Legenda HTML
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'EVAM {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))
saida.parent.mkdir(parents=True, exist_ok=True)
mapa.save(str(saida))
print(f" Mapa salvo: {saida}")
_resumir_resultado(linhas)
def _resumir_resultado(linhas: list[dict]) -> None:
total = len(linhas)
cumpridas = sum(1 for r in linhas if r["status"] == "cumprida")
print(f" Missões: {cumpridas}/{total} cumpridas")
aeronaves = {}
for r in linhas:
if r["aeronave"]:
aeronaves.setdefault(r["aeronave"], 0)
aeronaves[r["aeronave"]] += 1
for k, c in sorted(aeronaves.items()):
print(f" EVAM {k}: {c} missões")
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=MAPA_PADRAO)
args = parser.parse_args()
gerar_mapa(resultado=args.resultado, saida=args.saida)
if __name__ == "__main__":
main()