feat: adiciona quadro de horários formatado com tabulate ao Modelo 2

This commit is contained in:
2026-06-07 20:41:17 -03:00
parent 1cab2bbe36
commit e478cc72e0
4 changed files with 223 additions and 44 deletions

View File

@@ -2,7 +2,7 @@
"cells": [
{
"cell_type": "markdown",
"id": "07d7e46d",
"id": "d163e704",
"metadata": {},
"source": [
"# Alocação de Frotas (Fleet Scheduling) - Aeronaves C-97\n",
@@ -21,13 +21,13 @@
{
"cell_type": "code",
"execution_count": 1,
"id": "3f9fa4c6",
"id": "1cda871c",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-07T23:37:16.312196Z",
"iopub.status.busy": "2026-06-07T23:37:16.311487Z",
"iopub.status.idle": "2026-06-07T23:37:16.933888Z",
"shell.execute_reply": "2026-06-07T23:37:16.933301Z"
"iopub.execute_input": "2026-06-07T23:41:13.265917Z",
"iopub.status.busy": "2026-06-07T23:41:13.265674Z",
"iopub.status.idle": "2026-06-07T23:41:13.946892Z",
"shell.execute_reply": "2026-06-07T23:41:13.946177Z"
}
},
"outputs": [],
@@ -53,7 +53,7 @@
},
{
"cell_type": "markdown",
"id": "6054dba5",
"id": "3aa25feb",
"metadata": {},
"source": [
"## 1. Leitura e Limpeza dos Dados\n",
@@ -63,13 +63,13 @@
{
"cell_type": "code",
"execution_count": 2,
"id": "da258d7f",
"id": "5f12213f",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-07T23:37:16.935502Z",
"iopub.status.busy": "2026-06-07T23:37:16.935296Z",
"iopub.status.idle": "2026-06-07T23:37:17.297220Z",
"shell.execute_reply": "2026-06-07T23:37:17.296482Z"
"iopub.execute_input": "2026-06-07T23:41:13.949035Z",
"iopub.status.busy": "2026-06-07T23:41:13.948756Z",
"iopub.status.idle": "2026-06-07T23:41:14.387479Z",
"shell.execute_reply": "2026-06-07T23:41:14.386681Z"
}
},
"outputs": [],
@@ -101,7 +101,7 @@
},
{
"cell_type": "markdown",
"id": "2424db2d",
"id": "29a370cf",
"metadata": {},
"source": [
"## 2. Análise dos Esquadrões e Matrículas (Aeronaves)\n",
@@ -111,13 +111,13 @@
{
"cell_type": "code",
"execution_count": 3,
"id": "8b308e12",
"id": "4f559f60",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-07T23:37:17.298752Z",
"iopub.status.busy": "2026-06-07T23:37:17.298630Z",
"iopub.status.idle": "2026-06-07T23:37:17.310776Z",
"shell.execute_reply": "2026-06-07T23:37:17.310206Z"
"iopub.execute_input": "2026-06-07T23:41:14.388969Z",
"iopub.status.busy": "2026-06-07T23:41:14.388784Z",
"iopub.status.idle": "2026-06-07T23:41:14.401597Z",
"shell.execute_reply": "2026-06-07T23:41:14.401016Z"
}
},
"outputs": [
@@ -176,7 +176,7 @@
},
{
"cell_type": "markdown",
"id": "4909bfef",
"id": "3d637614",
"metadata": {},
"source": [
"## 3. Análise Estatística das Demandas Diárias\n",
@@ -187,13 +187,13 @@
{
"cell_type": "code",
"execution_count": 4,
"id": "b38abd07",
"id": "ca8b936f",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-07T23:37:17.312196Z",
"iopub.status.busy": "2026-06-07T23:37:17.312078Z",
"iopub.status.idle": "2026-06-07T23:37:17.621631Z",
"shell.execute_reply": "2026-06-07T23:37:17.620923Z"
"iopub.execute_input": "2026-06-07T23:41:14.403000Z",
"iopub.status.busy": "2026-06-07T23:41:14.402870Z",
"iopub.status.idle": "2026-06-07T23:41:14.726896Z",
"shell.execute_reply": "2026-06-07T23:41:14.726161Z"
}
},
"outputs": [
@@ -424,7 +424,7 @@
},
{
"cell_type": "markdown",
"id": "6bab0068",
"id": "782a4736",
"metadata": {},
"source": [
"## Diagrama de Rede dos Trechos\n",
@@ -434,13 +434,13 @@
{
"cell_type": "code",
"execution_count": 5,
"id": "6198127c",
"id": "9fedd535",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-07T23:37:17.623083Z",
"iopub.status.busy": "2026-06-07T23:37:17.622958Z",
"iopub.status.idle": "2026-06-07T23:37:17.740324Z",
"shell.execute_reply": "2026-06-07T23:37:17.739229Z"
"iopub.execute_input": "2026-06-07T23:41:14.728337Z",
"iopub.status.busy": "2026-06-07T23:41:14.728199Z",
"iopub.status.idle": "2026-06-07T23:41:14.849420Z",
"shell.execute_reply": "2026-06-07T23:41:14.848842Z"
}
},
"outputs": [
@@ -497,7 +497,7 @@
},
{
"cell_type": "markdown",
"id": "35633067",
"id": "551febf3",
"metadata": {},
"source": [
"## 4. Fleet Assignment Model (FAM) - Nível Brasil\n",
@@ -514,13 +514,13 @@
{
"cell_type": "code",
"execution_count": 6,
"id": "c9a2018d",
"id": "bb15ee0b",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-07T23:37:17.742588Z",
"iopub.status.busy": "2026-06-07T23:37:17.742459Z",
"iopub.status.idle": "2026-06-07T23:37:17.764890Z",
"shell.execute_reply": "2026-06-07T23:37:17.764350Z"
"iopub.execute_input": "2026-06-07T23:41:14.850971Z",
"iopub.status.busy": "2026-06-07T23:41:14.850776Z",
"iopub.status.idle": "2026-06-07T23:41:14.876417Z",
"shell.execute_reply": "2026-06-07T23:41:14.875894Z"
}
},
"outputs": [
@@ -609,7 +609,7 @@
},
{
"cell_type": "markdown",
"id": "666ba740",
"id": "37f9e364",
"metadata": {},
"source": [
"## 5. Modelo 2: Minimização de Aeronaves\n",
@@ -623,13 +623,13 @@
{
"cell_type": "code",
"execution_count": 7,
"id": "7b9b8019",
"id": "08433efd",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-07T23:37:17.766458Z",
"iopub.status.busy": "2026-06-07T23:37:17.766338Z",
"iopub.status.idle": "2026-06-07T23:37:19.072202Z",
"shell.execute_reply": "2026-06-07T23:37:19.071522Z"
"iopub.execute_input": "2026-06-07T23:41:14.877926Z",
"iopub.status.busy": "2026-06-07T23:41:14.877797Z",
"iopub.status.idle": "2026-06-07T23:41:16.250728Z",
"shell.execute_reply": "2026-06-07T23:41:16.250087Z"
}
},
"outputs": [
@@ -646,7 +646,22 @@
"Taxa de ocupação da frota (Tempo Voo / Tempo Disponível): 69.6%\n",
"\n",
"Voos de Reposicionamento Vazio Gerados (Y) para fechar o ciclo diário:\n",
" -> De SBAN para SBSJ: 1 voo(s) vazio(s) | Tempo de voo: 2.1h\n"
" -> De SBAN para SBSJ: 1 voo(s) vazio(s) | Tempo de voo: 2.1h\n",
"\n",
"== QUADRO DE HORÁRIOS DIÁRIOS ==\n",
"| Aeronave | Origem | Destino | Tipo | Partida | Chegada | Duração |\n",
"|------------|----------|-----------|--------------------------|-----------|-----------|-----------|\n",
"| Aeronave 1 | SBGL | SBGR | Passageiro | 00:00 | 00:51 | 0.86 h |\n",
"| Aeronave 1 | SBGR | SBGL | Passageiro | 00:51 | 01:43 | 0.86 h |\n",
"| Aeronave 1 | SBGL | SBSJ | Passageiro | 01:43 | 02:24 | 0.69 h |\n",
"| Aeronave 1 | SBSJ | SBGL | Passageiro | 02:24 | 03:06 | 0.69 h |\n",
"| Aeronave 1 | SBGL | SBBR | Passageiro | 03:06 | 05:26 | 2.33 h |\n",
"| Aeronave 1 | SBBR | SBGL | Passageiro | 05:26 | 07:45 | 2.33 h |\n",
"| Aeronave 2 | SBGR | SBBR | Passageiro | 00:00 | 02:10 | 2.17 h |\n",
"| Aeronave 2 | SBBR | SBGR | Passageiro | 02:10 | 04:20 | 2.17 h |\n",
"| Aeronave 3 | SBAN | SBSJ | Vazio (Reposicionamento) | 00:00 | 02:08 | 2.14 h |\n",
"| Aeronave 3 | SBSJ | SBBR | Passageiro | 02:08 | 04:17 | 2.15 h |\n",
"| Aeronave 3 | SBBR | SBAN | Passageiro | 04:17 | 04:35 | 0.30 h |\n"
]
}
],
@@ -739,7 +754,83 @@
" print(f\" -> De {i} para {j}: {int(Y[(i, j)].varValue)} voo(s) vazio(s) | Tempo de voo: {tempos_voo[(i,j)]*Y[(i,j)].varValue:.1f}h\")\n",
" gerou_vazio = True\n",
"if not gerou_vazio:\n",
" print(\" Nenhum voo de reposicionamento vazio foi necessário! A malha já é circular/balanceada.\")\n"
" print(\" Nenhum voo de reposicionamento vazio foi necessário! A malha já é circular/balanceada.\")\n",
"\n",
"# GERAR TABELA DE HORÁRIOS COM TABULATE E DATETIME\n",
"from tabulate import tabulate\n",
"import datetime\n",
"\n",
"# 1. Construir a lista de todos os voos a serem realizados\n",
"voos_para_fazer = []\n",
"\n",
"# Cópia para saber quantos voos de passageiros faltam alocar\n",
"D_copy = {k: v for k, v in D.items()}\n",
"\n",
"for i in aeroportos_malha:\n",
" for j in aeroportos_malha:\n",
" total_voos = D[(i, j)] + int(Y[(i, j)].varValue)\n",
" for _ in range(total_voos):\n",
" if D_copy[(i, j)] > 0:\n",
" tipo = 'Passageiro'\n",
" D_copy[(i, j)] -= 1\n",
" else:\n",
" tipo = 'Vazio (Reposicionamento)'\n",
" voos_para_fazer.append({'origem': i, 'destino': j, 'tipo': tipo, 'tempo': tempos_voo[(i, j)]})\n",
"\n",
"# 2. Simular a alocação sequencial de horários\n",
"tabela_horarios = []\n",
"aeronave_id = 1\n",
"\n",
"while voos_para_fazer:\n",
" voo_atual = voos_para_fazer.pop(0)\n",
" hora_atual = datetime.datetime(2025, 1, 1, 0, 0, 0)\n",
" \n",
" tempo_delta = datetime.timedelta(hours=voo_atual['tempo'])\n",
" hora_chegada = hora_atual + tempo_delta\n",
" \n",
" tabela_horarios.append([\n",
" f\"Aeronave {aeronave_id}\",\n",
" voo_atual['origem'],\n",
" voo_atual['destino'],\n",
" voo_atual['tipo'],\n",
" hora_atual.strftime('%H:%M'),\n",
" hora_chegada.strftime('%H:%M'),\n",
" f\"{voo_atual['tempo']:.2f} h\"\n",
" ])\n",
" \n",
" hora_atual = hora_chegada\n",
" local_atual = voo_atual['destino']\n",
" \n",
" # Continuar traçando o caminho para esta aeronave até que não caiba mais nas 24h\n",
" while True:\n",
" prox_voo = None\n",
" for idx, v in enumerate(voos_para_fazer):\n",
" if v['origem'] == local_atual:\n",
" if (hora_atual - datetime.datetime(2025, 1, 1, 0, 0, 0) + datetime.timedelta(hours=v['tempo'])).total_seconds() <= 24 * 3600:\n",
" prox_voo = voos_para_fazer.pop(idx)\n",
" break\n",
" \n",
" if prox_voo:\n",
" tempo_delta = datetime.timedelta(hours=prox_voo['tempo'])\n",
" hora_chegada = hora_atual + tempo_delta\n",
" tabela_horarios.append([\n",
" f\"Aeronave {aeronave_id}\",\n",
" prox_voo['origem'],\n",
" prox_voo['destino'],\n",
" prox_voo['tipo'],\n",
" hora_atual.strftime('%H:%M'),\n",
" hora_chegada.strftime('%H:%M'),\n",
" f\"{prox_voo['tempo']:.2f} h\"\n",
" ])\n",
" hora_atual = hora_chegada\n",
" local_atual = prox_voo['destino']\n",
" else:\n",
" break\n",
" \n",
" aeronave_id += 1\n",
"\n",
"print(\"\\n== QUADRO DE HORÁRIOS DIÁRIOS ==\")\n",
"print(tabulate(tabela_horarios, headers=[\"Aeronave\", \"Origem\", \"Destino\", \"Tipo\", \"Partida\", \"Chegada\", \"Duração\"], tablefmt=\"github\"))\n"
]
}
],