From da42ebdad12016b5a1a76aa78ff661fd88019548 Mon Sep 17 00:00:00 2001 From: Luciano Silva do Lago Date: Sun, 7 Jun 2026 20:44:13 -0300 Subject: [PATCH] =?UTF-8?q?feat:=20adiciona=20Modelo=203=20minimizando=20a?= =?UTF-8?q?eronaves=20com=20a=20considera=C3=A7=C3=A3o=20do=20tempo=20de?= =?UTF-8?q?=20solo=20(TAT)=20de=2040=20minutos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- create_nb.py | 119 +++++++++++++++++++++++ modelos.ipynb | 264 ++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 334 insertions(+), 49 deletions(-) diff --git a/create_nb.py b/create_nb.py index 0cf90e8..0635558 100644 --- a/create_nb.py +++ b/create_nb.py @@ -449,6 +449,125 @@ print("\\n== QUADRO DE HORÁRIOS DIÁRIOS ==") print(tabulate(tabela_horarios, headers=["Aeronave", "Origem", "Destino", "Tipo", "Partida", "Chegada", "Duração"], tablefmt="github")) """)) +# Cell 13: Markdown Modelo 3 +cells.append(nbf.v4.new_markdown_cell("""## 6. Modelo 3: Minimização de Aeronaves com TAT (Turnaround Time) +Nesta variação do Modelo 2, adicionamos uma restrição crítica da vida real: o Tempo de Solo ou **Turnaround Time (TAT)**. +Considerações: +- TAT estipulado: 40 minutos (0.66 horas) por cada voo (tempo necessário para desembarque, reabastecimento, verificações e embarque). +- O tempo total de dedicação de uma aeronave por ciclo passa a ser a soma do seu Tempo de Voo + 40 minutos de TAT no aeroporto de destino. +""")) + +# Cell 14: Code Modelo 3 +cells.append(nbf.v4.new_code_cell("""# 3. Modelagem Matemática Modelo 3 (Pulp) com TAT +prob3 = pulp.LpProblem("Minimizar_Aeronaves_TAT", pulp.LpMinimize) + +# Variáveis +Y3 = pulp.LpVariable.dicts("Y3", [(o, d) for o in aeroportos_malha for d in aeroportos_malha], lowBound=0, cat='Integer') +N3 = pulp.LpVariable("N3", lowBound=0, cat='Integer') + +TAT_horas = 40.0 / 60.0 + +# Função Objetivo +prob3 += N3 + 0.0001 * pulp.lpSum([Y3[(i, j)] * tempos_voo[(i, j)] for i in aeroportos_malha for j in aeroportos_malha]) + +# Restrição de fluxo +for no in aeroportos_malha: + chegadas = pulp.lpSum([D[(i, no)] + Y3[(i, no)] for i in aeroportos_malha]) + partidas = pulp.lpSum([D[(no, j)] + Y3[(no, j)] for j in aeroportos_malha]) + prob3 += chegadas == partidas + +# Restrição de tempo com TAT +# Cada voo (D ou Y) consome tempo de voo + TAT +tempo_total_operacional = pulp.lpSum([(D[(i, j)] + Y3[(i, j)]) * (tempos_voo[(i, j)] + TAT_horas) for i in aeroportos_malha for j in aeroportos_malha]) +prob3 += tempo_total_operacional <= N3 * 24.0 + +# Solução +prob3.solve(pulp.PULP_CBC_CMD(msg=False)) + +print("\\n== RESULTADO DO MODELO 3 (COM 40 MIN DE SOLO) ==") +print(f"Status: {pulp.LpStatus[prob3.status]}") +print(f"Número mínimo absoluto de aeronaves C-97 necessárias: {int(N3.varValue)}") + +tempo_voo_puro = sum([(D[(i, j)] + Y3[(i, j)].varValue) * tempos_voo[(i, j)] for i in aeroportos_malha for j in aeroportos_malha]) +total_voos_realizados = sum([(D[(i, j)] + Y3[(i, j)].varValue) for i in aeroportos_malha for j in aeroportos_malha]) +tempo_solo_total = total_voos_realizados * TAT_horas + +print(f"Tempo total de voo puro: {tempo_voo_puro:.2f} horas") +print(f"Tempo total gasto em solo (TAT): {tempo_solo_total:.2f} horas") +print(f"Tempo total operacional comprometido: {tempo_voo_puro + tempo_solo_total:.2f} horas") +if N3.varValue > 0: + print(f"Taxa de ocupação da frota (Tempo Operacional / Tempo Disponível): {((tempo_voo_puro + tempo_solo_total) / (N3.varValue * 24)) * 100:.1f}%\\n") + +# GERAR TABELA DE HORÁRIOS COM TABULATE E DATETIME +voos_para_fazer3 = [] +D_copy3 = {k: v for k, v in D.items()} + +for i in aeroportos_malha: + for j in aeroportos_malha: + total_voos3 = D[(i, j)] + int(Y3[(i, j)].varValue) + for _ in range(total_voos3): + if D_copy3[(i, j)] > 0: + tipo = 'Passageiro' + D_copy3[(i, j)] -= 1 + else: + tipo = 'Vazio (Reposicionamento)' + voos_para_fazer3.append({'origem': i, 'destino': j, 'tipo': tipo, 'tempo': tempos_voo[(i, j)]}) + +tabela_horarios3 = [] +aeronave_id3 = 1 + +while voos_para_fazer3: + voo_atual = voos_para_fazer3.pop(0) + hora_atual = datetime.datetime(2025, 1, 1, 0, 0, 0) + + tempo_delta = datetime.timedelta(hours=voo_atual['tempo']) + hora_chegada = hora_atual + tempo_delta + + tabela_horarios3.append([ + f"Aeronave {aeronave_id3}", + voo_atual['origem'], + voo_atual['destino'], + voo_atual['tipo'], + hora_atual.strftime('%H:%M'), + hora_chegada.strftime('%H:%M'), + f"{voo_atual['tempo']:.2f} h" + ]) + + # Próxima partida só depois do TAT de 40 min + hora_atual = hora_chegada + datetime.timedelta(minutes=40) + local_atual = voo_atual['destino'] + + while True: + prox_voo = None + for idx, v in enumerate(voos_para_fazer3): + if v['origem'] == local_atual: + if (hora_atual - datetime.datetime(2025, 1, 1, 0, 0, 0) + datetime.timedelta(hours=v['tempo'])).total_seconds() <= 24 * 3600: + prox_voo = voos_para_fazer3.pop(idx) + break + + if prox_voo: + tempo_delta = datetime.timedelta(hours=prox_voo['tempo']) + hora_chegada = hora_atual + tempo_delta + tabela_horarios3.append([ + f"Aeronave {aeronave_id3}", + prox_voo['origem'], + prox_voo['destino'], + prox_voo['tipo'], + hora_atual.strftime('%H:%M'), + hora_chegada.strftime('%H:%M'), + f"{prox_voo['tempo']:.2f} h" + ]) + hora_atual = hora_chegada + datetime.timedelta(minutes=40) + local_atual = prox_voo['destino'] + else: + break + + aeronave_id3 += 1 + +print("== QUADRO DE HORÁRIOS DIÁRIOS (COM 40 MIN TAT) ==") +print(tabulate(tabela_horarios3, headers=["Aeronave", "Origem", "Destino", "Tipo", "Partida", "Chegada", "Duração Voo"], tablefmt="github")) +""")) + nb.cells = cells with open('modelos.ipynb', 'w', encoding='utf-8') as f: diff --git a/modelos.ipynb b/modelos.ipynb index 265188b..f5be2ff 100644 --- a/modelos.ipynb +++ b/modelos.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "d163e704", + "id": "eeb50313", "metadata": {}, "source": [ "# Alocação de Frotas (Fleet Scheduling) - Aeronaves C-97\n", @@ -21,13 +21,13 @@ { "cell_type": "code", "execution_count": 1, - "id": "1cda871c", + "id": "3345f6bf", "metadata": { "execution": { - "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" + "iopub.execute_input": "2026-06-07T23:44:10.042075Z", + "iopub.status.busy": "2026-06-07T23:44:10.041422Z", + "iopub.status.idle": "2026-06-07T23:44:10.717819Z", + "shell.execute_reply": "2026-06-07T23:44:10.717190Z" } }, "outputs": [], @@ -53,7 +53,7 @@ }, { "cell_type": "markdown", - "id": "3aa25feb", + "id": "bb838c8d", "metadata": {}, "source": [ "## 1. Leitura e Limpeza dos Dados\n", @@ -63,13 +63,13 @@ { "cell_type": "code", "execution_count": 2, - "id": "5f12213f", + "id": "af65863f", "metadata": { "execution": { - "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" + "iopub.execute_input": "2026-06-07T23:44:10.719344Z", + "iopub.status.busy": "2026-06-07T23:44:10.719122Z", + "iopub.status.idle": "2026-06-07T23:44:11.177567Z", + "shell.execute_reply": "2026-06-07T23:44:11.176910Z" } }, "outputs": [], @@ -101,7 +101,7 @@ }, { "cell_type": "markdown", - "id": "29a370cf", + "id": "f4e097f0", "metadata": {}, "source": [ "## 2. Análise dos Esquadrões e Matrículas (Aeronaves)\n", @@ -111,13 +111,13 @@ { "cell_type": "code", "execution_count": 3, - "id": "4f559f60", + "id": "01c6de00", "metadata": { "execution": { - "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" + "iopub.execute_input": "2026-06-07T23:44:11.179085Z", + "iopub.status.busy": "2026-06-07T23:44:11.178943Z", + "iopub.status.idle": "2026-06-07T23:44:11.193498Z", + "shell.execute_reply": "2026-06-07T23:44:11.192832Z" } }, "outputs": [ @@ -176,7 +176,7 @@ }, { "cell_type": "markdown", - "id": "3d637614", + "id": "03d676ac", "metadata": {}, "source": [ "## 3. Análise Estatística das Demandas Diárias\n", @@ -187,13 +187,13 @@ { "cell_type": "code", "execution_count": 4, - "id": "ca8b936f", + "id": "d1d53c92", "metadata": { "execution": { - "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" + "iopub.execute_input": "2026-06-07T23:44:11.195008Z", + "iopub.status.busy": "2026-06-07T23:44:11.194881Z", + "iopub.status.idle": "2026-06-07T23:44:11.521316Z", + "shell.execute_reply": "2026-06-07T23:44:11.520564Z" } }, "outputs": [ @@ -424,7 +424,7 @@ }, { "cell_type": "markdown", - "id": "782a4736", + "id": "5a340e61", "metadata": {}, "source": [ "## Diagrama de Rede dos Trechos\n", @@ -434,13 +434,13 @@ { "cell_type": "code", "execution_count": 5, - "id": "9fedd535", + "id": "233d1236", "metadata": { "execution": { - "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" + "iopub.execute_input": "2026-06-07T23:44:11.522783Z", + "iopub.status.busy": "2026-06-07T23:44:11.522643Z", + "iopub.status.idle": "2026-06-07T23:44:11.652315Z", + "shell.execute_reply": "2026-06-07T23:44:11.651816Z" } }, "outputs": [ @@ -497,7 +497,7 @@ }, { "cell_type": "markdown", - "id": "551febf3", + "id": "19a69fa7", "metadata": {}, "source": [ "## 4. Fleet Assignment Model (FAM) - Nível Brasil\n", @@ -514,13 +514,13 @@ { "cell_type": "code", "execution_count": 6, - "id": "bb15ee0b", + "id": "77122760", "metadata": { "execution": { - "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" + "iopub.execute_input": "2026-06-07T23:44:11.654036Z", + "iopub.status.busy": "2026-06-07T23:44:11.653900Z", + "iopub.status.idle": "2026-06-07T23:44:11.681677Z", + "shell.execute_reply": "2026-06-07T23:44:11.681090Z" } }, "outputs": [ @@ -609,7 +609,7 @@ }, { "cell_type": "markdown", - "id": "37f9e364", + "id": "d52acdc3", "metadata": {}, "source": [ "## 5. Modelo 2: Minimização de Aeronaves\n", @@ -623,13 +623,13 @@ { "cell_type": "code", "execution_count": 7, - "id": "08433efd", + "id": "e4289b56", "metadata": { "execution": { - "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" + "iopub.execute_input": "2026-06-07T23:44:11.683322Z", + "iopub.status.busy": "2026-06-07T23:44:11.683175Z", + "iopub.status.idle": "2026-06-07T23:44:13.105611Z", + "shell.execute_reply": "2026-06-07T23:44:13.105024Z" } }, "outputs": [ @@ -653,15 +653,15 @@ "|------------|----------|-----------|--------------------------|-----------|-----------|-----------|\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 1 | SBGL | SBBR | Passageiro | 01:43 | 04:02 | 2.33 h |\n", + "| Aeronave 1 | SBBR | SBGL | Passageiro | 04:02 | 06:22 | 2.33 h |\n", + "| Aeronave 1 | SBGL | SBSJ | Passageiro | 06:22 | 07:03 | 0.69 h |\n", + "| Aeronave 1 | SBSJ | SBGL | Passageiro | 07:03 | 07:45 | 0.69 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" + "| Aeronave 3 | SBBR | SBAN | Passageiro | 00:00 | 00:18 | 0.30 h |\n", + "| Aeronave 3 | SBAN | SBSJ | Vazio (Reposicionamento) | 00:18 | 02:26 | 2.14 h |\n", + "| Aeronave 3 | SBSJ | SBBR | Passageiro | 02:26 | 04:35 | 2.15 h |\n" ] } ], @@ -832,6 +832,172 @@ "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" ] + }, + { + "cell_type": "markdown", + "id": "2daf591f", + "metadata": {}, + "source": [ + "## 6. Modelo 3: Minimização de Aeronaves com TAT (Turnaround Time)\n", + "Nesta variação do Modelo 2, adicionamos uma restrição crítica da vida real: o Tempo de Solo ou **Turnaround Time (TAT)**.\n", + "Considerações:\n", + "- TAT estipulado: 40 minutos (0.66 horas) por cada voo (tempo necessário para desembarque, reabastecimento, verificações e embarque).\n", + "- O tempo total de dedicação de uma aeronave por ciclo passa a ser a soma do seu Tempo de Voo + 40 minutos de TAT no aeroporto de destino.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "4d54330a", + "metadata": { + "execution": { + "iopub.execute_input": "2026-06-07T23:44:13.108816Z", + "iopub.status.busy": "2026-06-07T23:44:13.108593Z", + "iopub.status.idle": "2026-06-07T23:44:13.126370Z", + "shell.execute_reply": "2026-06-07T23:44:13.125849Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "== RESULTADO DO MODELO 3 (COM 40 MIN DE SOLO) ==\n", + "Status: Optimal\n", + "Número mínimo absoluto de aeronaves C-97 necessárias: 2\n", + "Tempo total de voo puro: 16.70 horas\n", + "Tempo total gasto em solo (TAT): 7.33 horas\n", + "Tempo total operacional comprometido: 24.04 horas\n", + "Taxa de ocupação da frota (Tempo Operacional / Tempo Disponível): 50.1%\n", + "\n", + "== QUADRO DE HORÁRIOS DIÁRIOS (COM 40 MIN TAT) ==\n", + "| Aeronave | Origem | Destino | Tipo | Partida | Chegada | Duração Voo |\n", + "|------------|----------|-----------|--------------------------|-----------|-----------|---------------|\n", + "| Aeronave 1 | SBGL | SBGR | Passageiro | 00:00 | 00:51 | 0.86 h |\n", + "| Aeronave 1 | SBGR | SBGL | Passageiro | 01:31 | 02:23 | 0.86 h |\n", + "| Aeronave 1 | SBGL | SBBR | Passageiro | 03:03 | 05:22 | 2.33 h |\n", + "| Aeronave 1 | SBBR | SBGL | Passageiro | 06:02 | 08:22 | 2.33 h |\n", + "| Aeronave 1 | SBGL | SBSJ | Passageiro | 09:02 | 09:43 | 0.69 h |\n", + "| Aeronave 1 | SBSJ | SBGL | Passageiro | 10:23 | 11:05 | 0.69 h |\n", + "| Aeronave 2 | SBGR | SBBR | Passageiro | 00:00 | 02:10 | 2.17 h |\n", + "| Aeronave 2 | SBBR | SBGR | Passageiro | 02:50 | 05:00 | 2.17 h |\n", + "| Aeronave 3 | SBBR | SBAN | Passageiro | 00:00 | 00:18 | 0.30 h |\n", + "| Aeronave 3 | SBAN | SBSJ | Vazio (Reposicionamento) | 00:58 | 03:06 | 2.14 h |\n", + "| Aeronave 3 | SBSJ | SBBR | Passageiro | 03:46 | 05:55 | 2.15 h |\n" + ] + } + ], + "source": [ + "# 3. Modelagem Matemática Modelo 3 (Pulp) com TAT\n", + "prob3 = pulp.LpProblem(\"Minimizar_Aeronaves_TAT\", pulp.LpMinimize)\n", + "\n", + "# Variáveis\n", + "Y3 = pulp.LpVariable.dicts(\"Y3\", [(o, d) for o in aeroportos_malha for d in aeroportos_malha], lowBound=0, cat='Integer')\n", + "N3 = pulp.LpVariable(\"N3\", lowBound=0, cat='Integer')\n", + "\n", + "TAT_horas = 40.0 / 60.0\n", + "\n", + "# Função Objetivo\n", + "prob3 += N3 + 0.0001 * pulp.lpSum([Y3[(i, j)] * tempos_voo[(i, j)] for i in aeroportos_malha for j in aeroportos_malha])\n", + "\n", + "# Restrição de fluxo\n", + "for no in aeroportos_malha:\n", + " chegadas = pulp.lpSum([D[(i, no)] + Y3[(i, no)] for i in aeroportos_malha])\n", + " partidas = pulp.lpSum([D[(no, j)] + Y3[(no, j)] for j in aeroportos_malha])\n", + " prob3 += chegadas == partidas\n", + "\n", + "# Restrição de tempo com TAT\n", + "# Cada voo (D ou Y) consome tempo de voo + TAT\n", + "tempo_total_operacional = pulp.lpSum([(D[(i, j)] + Y3[(i, j)]) * (tempos_voo[(i, j)] + TAT_horas) for i in aeroportos_malha for j in aeroportos_malha])\n", + "prob3 += tempo_total_operacional <= N3 * 24.0\n", + "\n", + "# Solução\n", + "prob3.solve(pulp.PULP_CBC_CMD(msg=False))\n", + "\n", + "print(\"\\n== RESULTADO DO MODELO 3 (COM 40 MIN DE SOLO) ==\")\n", + "print(f\"Status: {pulp.LpStatus[prob3.status]}\")\n", + "print(f\"Número mínimo absoluto de aeronaves C-97 necessárias: {int(N3.varValue)}\")\n", + "\n", + "tempo_voo_puro = sum([(D[(i, j)] + Y3[(i, j)].varValue) * tempos_voo[(i, j)] for i in aeroportos_malha for j in aeroportos_malha])\n", + "total_voos_realizados = sum([(D[(i, j)] + Y3[(i, j)].varValue) for i in aeroportos_malha for j in aeroportos_malha])\n", + "tempo_solo_total = total_voos_realizados * TAT_horas\n", + "\n", + "print(f\"Tempo total de voo puro: {tempo_voo_puro:.2f} horas\")\n", + "print(f\"Tempo total gasto em solo (TAT): {tempo_solo_total:.2f} horas\")\n", + "print(f\"Tempo total operacional comprometido: {tempo_voo_puro + tempo_solo_total:.2f} horas\")\n", + "if N3.varValue > 0:\n", + " print(f\"Taxa de ocupação da frota (Tempo Operacional / Tempo Disponível): {((tempo_voo_puro + tempo_solo_total) / (N3.varValue * 24)) * 100:.1f}%\\n\")\n", + "\n", + "# GERAR TABELA DE HORÁRIOS COM TABULATE E DATETIME\n", + "voos_para_fazer3 = []\n", + "D_copy3 = {k: v for k, v in D.items()}\n", + "\n", + "for i in aeroportos_malha:\n", + " for j in aeroportos_malha:\n", + " total_voos3 = D[(i, j)] + int(Y3[(i, j)].varValue)\n", + " for _ in range(total_voos3):\n", + " if D_copy3[(i, j)] > 0:\n", + " tipo = 'Passageiro'\n", + " D_copy3[(i, j)] -= 1\n", + " else:\n", + " tipo = 'Vazio (Reposicionamento)'\n", + " voos_para_fazer3.append({'origem': i, 'destino': j, 'tipo': tipo, 'tempo': tempos_voo[(i, j)]})\n", + "\n", + "tabela_horarios3 = []\n", + "aeronave_id3 = 1\n", + "\n", + "while voos_para_fazer3:\n", + " voo_atual = voos_para_fazer3.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_horarios3.append([\n", + " f\"Aeronave {aeronave_id3}\",\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", + " # Próxima partida só depois do TAT de 40 min\n", + " hora_atual = hora_chegada + datetime.timedelta(minutes=40)\n", + " local_atual = voo_atual['destino']\n", + " \n", + " while True:\n", + " prox_voo = None\n", + " for idx, v in enumerate(voos_para_fazer3):\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_fazer3.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_horarios3.append([\n", + " f\"Aeronave {aeronave_id3}\",\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 + datetime.timedelta(minutes=40)\n", + " local_atual = prox_voo['destino']\n", + " else:\n", + " break\n", + " \n", + " aeronave_id3 += 1\n", + "\n", + "print(\"== QUADRO DE HORÁRIOS DIÁRIOS (COM 40 MIN TAT) ==\")\n", + "print(tabulate(tabela_horarios3, headers=[\"Aeronave\", \"Origem\", \"Destino\", \"Tipo\", \"Partida\", \"Chegada\", \"Duração Voo\"], tablefmt=\"github\"))\n" + ] } ], "metadata": {