Compare commits
9 Commits
db989eacff
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 3e513c7ef4 | |||
| 9d71b1f21b | |||
| 1a44b84d67 | |||
| 8d1101b235 | |||
| b2dc2b341b | |||
| d3dcc6a30b | |||
| ea1896a544 | |||
| 7a5e82bf79 | |||
| 4f314111d7 |
50
APP.md
50
APP.md
@@ -1,50 +0,0 @@
|
|||||||
Você é um especialista em UI/UX usando Streamlit.
|
|
||||||
|
|
||||||
[OBJETIVO]
|
|
||||||
|
|
||||||
- Construir uma interface gráfica para o arquivo main.py.
|
|
||||||
|
|
||||||
[REGRAS]
|
|
||||||
|
|
||||||
- A interface deve ser clean, moderna e intuitiva.
|
|
||||||
- Não crie gráficos que não sejam úteis.
|
|
||||||
|
|
||||||
[WORKSPACE]
|
|
||||||
|
|
||||||
- Você está em um ambiente Linux;
|
|
||||||
- O shell é fish;
|
|
||||||
- Coloque 'rtk' antes de todos os comandos;
|
|
||||||
- O ambiente venv foi criado usando 'uv';
|
|
||||||
- Rode scripts python com 'uv run ...';
|
|
||||||
- Adicione bibliotecas com 'uv add ...';
|
|
||||||
|
|
||||||
[PROJETO]
|
|
||||||
|
|
||||||
- Faça a interface toda em inglês;
|
|
||||||
- Crie uma aba chamada 'Data' com uma tabela do csv após filtragem e tratamento das variáveis;
|
|
||||||
- Crie uma aba chamada 'Fleet' com a tabela df_frota;
|
|
||||||
- Crie uma aba chamada 'Demand' com a tabela tabela_pax;
|
|
||||||
- Crie uma aba chamada 'Solver' com sliders para os parâmetros do problema:
|
|
||||||
- Frota de aeronaves;
|
|
||||||
- Período de operação;
|
|
||||||
- Local de origem;
|
|
||||||
- Local de destino;
|
|
||||||
- Número de aeronaves de cada tipo;
|
|
||||||
- Número de dias;
|
|
||||||
- Número de voos;
|
|
||||||
- Número de passageiros;
|
|
||||||
- Número de tripulantes;
|
|
||||||
- Número de bagagens;
|
|
||||||
- Número de aeronaves de cada tipo;
|
|
||||||
- Dentro da aba Solver coloque também, antes dos slides, o problema em 'matematiques' usando latex;
|
|
||||||
- No final da aba Solver, coloque um botão para resolver e uma caixa de texto com a saída do solver;
|
|
||||||
- Crie uma aba chamada 'Mapa' com um mapa da localização dos aeroportos na solução, com um slider por dia do ano;
|
|
||||||
- Crie uma aba chamada 'Resultados' com os resultados do solver, em tabelas. Coloque também o resultado não otimizado;
|
|
||||||
- Eu preciso saber também a quantiade de pernoites de uma aeronave em cada aeroporto.
|
|
||||||
- Desenhe a rota, ao selecionar a rota, um pop-up deve aparecer com informações sobre a rota (incluindo a aeronave e quantidade de pax e dias fora de sede);
|
|
||||||
- Mude a cor da rota caso seja uma rota de algum dia fora de sede.
|
|
||||||
- Crie um novo arquivo para este projeto, chamado main_app.py
|
|
||||||
- Utilize somente o Modelo VII
|
|
||||||
- Deixe o código bem documento usando doxygen
|
|
||||||
- Programe como um programado Senior usando Lean Code
|
|
||||||
- Antes de prosseguir, me mostre uma comparação para este projeto: Streamlit ou Shiny e eu decidirei qual o melhor.
|
|
||||||
Binary file not shown.
35
README.md
35
README.md
@@ -1,19 +1,23 @@
|
|||||||
# Fleet Assignment Optimization - Força Aérea Brasileira
|
# Fleet Assignment Optimization - Força Aérea Brasileira
|
||||||
|
|
||||||
## Integrantes do Grupo
|
## Integrantes do Grupo
|
||||||
|
|
||||||
|
- 1T Willia
|
||||||
- 1T Lago
|
- 1T Lago
|
||||||
- 1T Figueiredo
|
- 1T Figueiredo
|
||||||
- 1T Fialho
|
- 1T Fialho
|
||||||
|
|
||||||
## Sobre o Projeto
|
## Sobre o Projeto
|
||||||
O presente trabalho aborda o problema clássico de **Fleet Assignment** (Alocação de Frota) aplicado ao contexto do transporte logístico aéreo militar.
|
|
||||||
|
|
||||||
O sistema foi desenvolvido como uma aplicação interativa (Dashboard Web) acoplada a um modelo matemático avançado. O objetivo é designar de forma ótima os diferentes modelos de aeronaves de transporte (C-97, C-95M, C-105, KC-390, KC-30, C-99A, C-98 e C-98A) de seus respectivos Esquadrões para atender à matriz de passageiros solicitada.
|
O presente trabalho aborda o problema clássico de **Fleet Assignment** (Alocação de Frota) aplicado ao contexto do transporte logístico aéreo militar.
|
||||||
|
|
||||||
|
O sistema foi desenvolvido como uma aplicação interativa (Dashboard Web) acoplada a um modelo matemático avançado. O objetivo é designar de forma ótima os diferentes modelos de aeronaves de transporte (C-97, C-95M, C-105, KC-390, KC-30, C-99A, C-98 e C-98A) de seus respectivos Esquadrões para atender à matriz de passageiros solicitada.
|
||||||
|
|
||||||
A arquitetura equilibra a maximização do atendimento de passageiros com a **minimização dos custos logísticos** (consumo de combustível em litros e penalidades de pernoite em bases externas), tudo isso balizado por limites de alcance, velocidades, tamanho físico das frotas disponíveis nas bases (Emissores) e capacidade de passageiros de cada aeronave.
|
A arquitetura equilibra a maximização do atendimento de passageiros com a **minimização dos custos logísticos** (consumo de combustível em litros e penalidades de pernoite em bases externas), tudo isso balizado por limites de alcance, velocidades, tamanho físico das frotas disponíveis nas bases (Emissores) e capacidade de passageiros de cada aeronave.
|
||||||
|
|
||||||
### O Modelo Matemático
|
### O Modelo Matemático
|
||||||
O núcleo de otimização foi construído utilizando Programação Linear Inteira Mista (MILP) processada pelo solver **SCIP** (implementado com *Google OR-Tools*). A modelagem contempla:
|
|
||||||
|
O núcleo de otimização foi construído utilizando Programação Linear Inteira Mista (MILP) processada pelo solver **SCIP** (implementado com _Google OR-Tools_). A modelagem contempla:
|
||||||
|
|
||||||
- **Função Objetivo:** $\min \sum_{t} \sum_{r} (c_{r} \cdot x_{r,t}) + \sum_{t} \sum_{d} (M \cdot s_{d,t})$
|
- **Função Objetivo:** $\min \sum_{t} \sum_{r} (c_{r} \cdot x_{r,t}) + \sum_{t} \sum_{d} (M \cdot s_{d,t})$
|
||||||
O algoritmo minimiza o custo total de combustível das missões ($c_r$), somado à grande penalidade ($M$) associada às variáveis de folga ($s_{d,t}$), forçando a malha a alocar missões para não deixar passageiros para trás, sempre que exequível.
|
O algoritmo minimiza o custo total de combustível das missões ($c_r$), somado à grande penalidade ($M$) associada às variáveis de folga ($s_{d,t}$), forçando a malha a alocar missões para não deixar passageiros para trás, sempre que exequível.
|
||||||
@@ -23,7 +27,9 @@ O núcleo de otimização foi construído utilizando Programação Linear Inteir
|
|||||||
- **Limites de Estoque:** As decolagens originadas de uma localidade jamais podem exceder a propriedade física do $\text{MaxFleet}$ distribuída para o esquadrão emissor.
|
- **Limites de Estoque:** As decolagens originadas de uma localidade jamais podem exceder a propriedade física do $\text{MaxFleet}$ distribuída para o esquadrão emissor.
|
||||||
|
|
||||||
### Resultados Obtidos
|
### Resultados Obtidos
|
||||||
|
|
||||||
A ferramenta é capaz de rodar a otimização global e extrair métricas gerenciais comparativas:
|
A ferramenta é capaz de rodar a otimização global e extrair métricas gerenciais comparativas:
|
||||||
|
|
||||||
- **Baseline Fuel (Não-Otimizado):** O sistema isola qual seria o consumo nominal caso as demandas fossem atendidas de modo bruto.
|
- **Baseline Fuel (Não-Otimizado):** O sistema isola qual seria o consumo nominal caso as demandas fossem atendidas de modo bruto.
|
||||||
- **Optimized Fuel Consumption:** Retorna o custo de combustível estrito gerado pelo motor SCIP.
|
- **Optimized Fuel Consumption:** Retorna o custo de combustível estrito gerado pelo motor SCIP.
|
||||||
- **Total Savings:** Revela à chefia o percentual quantitativo de economia gerada pela roteirização matemática contra o plano base.
|
- **Total Savings:** Revela à chefia o percentual quantitativo de economia gerada pela roteirização matemática contra o plano base.
|
||||||
@@ -32,7 +38,9 @@ A ferramenta é capaz de rodar a otimização global e extrair métricas gerenci
|
|||||||
---
|
---
|
||||||
|
|
||||||
## 📂 Arquitetura do Projeto
|
## 📂 Arquitetura do Projeto
|
||||||
|
|
||||||
O projeto foi estruturado utilizando padrões modernos de software:
|
O projeto foi estruturado utilizando padrões modernos de software:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
/
|
/
|
||||||
├── app/
|
├── app/
|
||||||
@@ -54,36 +62,39 @@ O projeto foi estruturado utilizando padrões modernos de software:
|
|||||||
## 🛠️ Guia de Instalação e Execução
|
## 🛠️ Guia de Instalação e Execução
|
||||||
|
|
||||||
### 1. Instalar o Python (3.12+)
|
### 1. Instalar o Python (3.12+)
|
||||||
|
|
||||||
Abra seu terminal/prompt e utilize o gerenciador de pacotes nativo do seu Sistema Operacional:
|
Abra seu terminal/prompt e utilize o gerenciador de pacotes nativo do seu Sistema Operacional:
|
||||||
|
|
||||||
* **Windows (Winget):** `winget install Python.Python.3.12`
|
- **Windows (Winget):** `winget install Python.Python.3.12`
|
||||||
* **Ubuntu/Debian (APT):** `sudo apt update && sudo apt install python3 python3-pip python3-venv`
|
- **Ubuntu/Debian (APT):** `sudo apt update && sudo apt install python3 python3-pip python3-venv`
|
||||||
* **Fedora (DNF):** `sudo dnf install python3`
|
- **Fedora (DNF):** `sudo dnf install python3`
|
||||||
* **Arch Linux (Pacman):** `sudo pacman -S python`
|
- **Arch Linux (Pacman):** `sudo pacman -S python`
|
||||||
* **macOS (Homebrew):** `brew install python`
|
- **macOS (Homebrew):** `brew install python`
|
||||||
|
|
||||||
### 2. Instalar o Gerenciador `uv`
|
### 2. Instalar o Gerenciador `uv`
|
||||||
|
|
||||||
O projeto adota o `uv` (gerenciador de ambiente e dependências ultrarrápido em Rust):
|
O projeto adota o `uv` (gerenciador de ambiente e dependências ultrarrápido em Rust):
|
||||||
|
|
||||||
* **macOS / Linux:**
|
- **macOS / Linux:**
|
||||||
```bash
|
```bash
|
||||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||||
```
|
```
|
||||||
* **Windows (PowerShell):**
|
- **Windows (PowerShell):**
|
||||||
```powershell
|
```powershell
|
||||||
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
|
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. Configurar Ambiente e Executar
|
### 3. Configurar Ambiente e Executar
|
||||||
|
|
||||||
Abra o terminal na pasta raiz deste projeto (onde este `README.md` e o arquivo `pyproject.toml` se encontram) e siga os passos abaixo:
|
Abra o terminal na pasta raiz deste projeto (onde este `README.md` e o arquivo `pyproject.toml` se encontram) e siga os passos abaixo:
|
||||||
|
|
||||||
1. **Sincronizar Dependências (Instalar tudo e criar a VENV automaticamente):**
|
1. **Sincronizar Dependências (Instalar tudo e criar a VENV automaticamente):**
|
||||||
```bash
|
```bash
|
||||||
rtk uv sync
|
uv sync
|
||||||
```
|
```
|
||||||
2. **Ligar o Servidor Web e a Interface:**
|
2. **Ligar o Servidor Web e a Interface:**
|
||||||
```bash
|
```bash
|
||||||
rtk uv run streamlit run app/dashboard.py
|
uv run streamlit run app/dashboard.py
|
||||||
```
|
```
|
||||||
|
|
||||||
> **Acesso:** Assim que o servidor subir, o Python acionará automaticamente a abertura de uma nova guia no seu **navegador web padrão** acessando o painel de operações (normalmente via `http://localhost:8501`).
|
> **Acesso:** Assim que o servidor subir, o Python acionará automaticamente a abertura de uma nova guia no seu **navegador web padrão** acessando o painel de operações (normalmente via `http://localhost:8501`).
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ df_results = load_results()
|
|||||||
def run_solver_subprocess(time_limit_sec, output_placeholder):
|
def run_solver_subprocess(time_limit_sec, output_placeholder):
|
||||||
"""Spawns the optimization worker and streams logs to the UI."""
|
"""Spawns the optimization worker and streams logs to the UI."""
|
||||||
process = subprocess.Popen(
|
process = subprocess.Popen(
|
||||||
["uv", "run", "python", "-u", "solver_worker.py", str(time_limit_sec)],
|
["uv", "run", "python", "-u", "src/fleet_assignment/optimizer.py", str(time_limit_sec)],
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.STDOUT,
|
stderr=subprocess.STDOUT,
|
||||||
text=True,
|
text=True,
|
||||||
@@ -126,7 +126,7 @@ with tab1:
|
|||||||
with col_title:
|
with col_title:
|
||||||
st.header("Passenger Demands")
|
st.header("Passenger Demands")
|
||||||
with col_reset:
|
with col_reset:
|
||||||
if st.button("🔄 Reset DB to Raw CSV", type="secondary", use_container_width=True):
|
if st.button("🔄 Reset DB to Raw CSV", type="secondary", width="stretch"):
|
||||||
init_db(demands_raw, df_fleet, force_reset=True)
|
init_db(demands_raw, df_fleet, force_reset=True)
|
||||||
st.success("Database Reset!")
|
st.success("Database Reset!")
|
||||||
st.rerun()
|
st.rerun()
|
||||||
@@ -137,12 +137,12 @@ with tab1:
|
|||||||
edited_demand = st.data_editor(
|
edited_demand = st.data_editor(
|
||||||
df_display_demand,
|
df_display_demand,
|
||||||
num_rows="dynamic",
|
num_rows="dynamic",
|
||||||
use_container_width=True,
|
width="stretch",
|
||||||
hide_index=True,
|
hide_index=True,
|
||||||
key="demand_editor"
|
key="demand_editor"
|
||||||
)
|
)
|
||||||
|
|
||||||
if st.button("💾 Save Demand Changes", type="primary", use_container_width=True):
|
if st.button("💾 Save Demand Changes", type="primary", width="stretch"):
|
||||||
with sqlite3.connect(DB_PATH) as conn:
|
with sqlite3.connect(DB_PATH) as conn:
|
||||||
conn.execute("DROP TABLE IF EXISTS demandas")
|
conn.execute("DROP TABLE IF EXISTS demandas")
|
||||||
conn.execute('''
|
conn.execute('''
|
||||||
@@ -177,12 +177,12 @@ with tab2:
|
|||||||
st.info("💡 Edit specs directly below to simulate larger fleets, different fuel burn, or cargo capacity.")
|
st.info("💡 Edit specs directly below to simulate larger fleets, different fuel burn, or cargo capacity.")
|
||||||
edited_fleet = st.data_editor(
|
edited_fleet = st.data_editor(
|
||||||
df_display_fleet,
|
df_display_fleet,
|
||||||
use_container_width=True,
|
width="stretch",
|
||||||
hide_index=True,
|
hide_index=True,
|
||||||
key="fleet_editor"
|
key="fleet_editor"
|
||||||
)
|
)
|
||||||
|
|
||||||
if st.button("💾 Save Fleet Changes", type="primary", use_container_width=True):
|
if st.button("💾 Save Fleet Changes", type="primary", width="stretch"):
|
||||||
rename_back = {
|
rename_back = {
|
||||||
'Modelo': 'Modelo', 'Squadron': 'Emissor', 'Fleet Size': 'Tamanho da Frota',
|
'Modelo': 'Modelo', 'Squadron': 'Emissor', 'Fleet Size': 'Tamanho da Frota',
|
||||||
'Fuel Usage (km/l)': 'Consumo (km/l)', 'Mean Speed (km/h)': 'Velocidade Média (km/h)',
|
'Fuel Usage (km/l)': 'Consumo (km/l)', 'Mean Speed (km/h)': 'Velocidade Média (km/h)',
|
||||||
@@ -207,20 +207,24 @@ with tab2:
|
|||||||
# --- TAB 3: SOLVER ---
|
# --- TAB 3: SOLVER ---
|
||||||
with tab3:
|
with tab3:
|
||||||
st.header("Mathematical Formulation")
|
st.header("Mathematical Formulation")
|
||||||
st.latex(r"\min \sum_{t} \sum_{r} (c_{r} \cdot x_{r,t}) + \sum_{t} \sum_{d} (M \cdot s_{d,t})")
|
st.latex(r"\min \sum_{m,e,r,t} (c_{m,e,r} \cdot x_{m,e,r,t})")
|
||||||
st.latex(r"\text{s.t.} \quad \sum_{m,e} (\text{cap}_{m} \cdot x_{m,e,r,t}) + s_{r,t} \ge \text{PAX}_{r,t} \quad \forall r, t")
|
st.latex(r"\text{s.t.} \quad \sum_{m,e} (\text{cap}_{m} \cdot x_{m,e,r,t}) \ge \text{PAX}_{r,t} \quad \forall r, t")
|
||||||
st.latex(r"\sum_{t_{start} \le T \le t_{start} + \text{pernoites}} x_{m,e,r,t_{start}} \le \text{MaxFleet}_{m,e} \quad \forall m, e, T")
|
st.latex(r"\sum_{t_{start} \le T \le t_{start} + \text{overnights}} x_{m,e,r,t_{start}} \le \text{MaxFleet}_{m,e} \quad \forall m, e, T")
|
||||||
|
st.latex(r"\text{FlightTime}_{m,r} \cdot x_{m,e,r,t} \le \text{MaxDailyHours} \quad \forall m,e,r,t")
|
||||||
|
st.latex(r"\text{MissionTime}_{m,r} \le \text{MaxDaysOut} \quad \forall m,r")
|
||||||
|
|
||||||
st.markdown(r"""
|
st.markdown(r"""
|
||||||
**Dictionary of Variables:**
|
**Dictionary of Variables & Constraints:**
|
||||||
- $x_{m,e,r,t}$: Decision Variable (Integer). Number of aircraft of model $m$, from squadron $e$, allocated to route $r$ on day $t$.
|
- $x_{m,e,r,t}$: Decision Variable (Integer). Number of aircraft of model $m$, from squadron $e$, allocated to route $r$ on day $t$.
|
||||||
- $c_r$: Estimated total fuel cost for the route $r$ mission, including overnight stay penalties if applicable.
|
- $c_{m,e,r}$: Total fuel cost function: $c_{m,e,r} = \left( \frac{\text{Dist}_r \cdot \text{RangePenalty}}{\text{FuelConsumption}_m} \right) + (\text{overnights} \cdot \text{DailyPenalty}_m)$.
|
||||||
- $s_{r,t}$: Slack Variable. Represents the passenger demand that could **not** be met on that day due to fleet limitations.
|
- $\text{RangePenalty}$: Applies a **25% penalty** multiplier ($1.25$) to the fuel burn if any leg of the mission exceeds the aircraft's maximum range.
|
||||||
- $M$: High Penalty (Big M). Imposed on the system for each unit of slack activated, forcing the solver to meet demand whenever possible.
|
- $\text{DailyPenalty}_m$: Equivalent to an extra daily fuel expenditure quota for each day the aircraft spends out of its base.
|
||||||
- $\text{cap}_m$: Maximum passenger capacity (seats) of aircraft $m$.
|
- $\text{cap}_m$: Maximum passenger capacity (seats) of aircraft $m$.
|
||||||
- $\text{PAX}_{r,t}$: Actual number of Air Force passengers needing to fly on route $r$ on day $t$.
|
- $\text{PAX}_{r,t}$: Actual number of Air Force passengers needing to fly on route $r$ on day $t$.
|
||||||
- $\text{MaxFleet}_{m,e}$: Total physical aircraft available in squadron $e$ for model $m$.
|
- $\text{MaxFleet}_{m,e}$: Total physical aircraft available in squadron $e$ for model $m$.
|
||||||
- $T$: Temporal inspection window. Ensures aircraft blockade during overnight stays, preventing fleet duplication.
|
- $T$: Temporal inspection window. Ensures aircraft blockade during overnight stays, preventing fleet duplication.
|
||||||
|
- $\text{MaxDailyHours}$: Maximum allowed flight time per day per aircraft (typically $12$ hours).
|
||||||
|
- $\text{MaxDaysOut}$: Maximum allowable time for a mission before returning to base (hard limit of $96$ hours / 4 days).
|
||||||
""")
|
""")
|
||||||
|
|
||||||
if st.button("🚀 Run Global Optimization", type="primary"):
|
if st.button("🚀 Run Global Optimization", type="primary"):
|
||||||
@@ -229,7 +233,8 @@ with tab3:
|
|||||||
|
|
||||||
if run_solver_subprocess(300, log_placeholder):
|
if run_solver_subprocess(300, log_placeholder):
|
||||||
st.success("Solver Finished! Results saved to CSV.")
|
st.success("Solver Finished! Results saved to CSV.")
|
||||||
st.session_state['new_results'] = True
|
load_results.clear()
|
||||||
|
st.rerun()
|
||||||
else:
|
else:
|
||||||
st.error("Solver Failed or was aborted.")
|
st.error("Solver Failed or was aborted.")
|
||||||
|
|
||||||
@@ -237,9 +242,6 @@ with tab3:
|
|||||||
with tab4:
|
with tab4:
|
||||||
st.header("Interactive Flight Map")
|
st.header("Interactive Flight Map")
|
||||||
|
|
||||||
if st.session_state.get('new_results'):
|
|
||||||
df_results = load_results()
|
|
||||||
|
|
||||||
view_state = pdk.ViewState(latitude=-15.78, longitude=-47.92, zoom=3.5, pitch=45)
|
view_state = pdk.ViewState(latitude=-15.78, longitude=-47.92, zoom=3.5, pitch=45)
|
||||||
layers = []
|
layers = []
|
||||||
|
|
||||||
@@ -259,7 +261,7 @@ with tab4:
|
|||||||
col_prev, col_drop, col_next = st.columns([1, 8, 1])
|
col_prev, col_drop, col_next = st.columns([1, 8, 1])
|
||||||
with col_prev:
|
with col_prev:
|
||||||
st.markdown("<br/>", unsafe_allow_html=True)
|
st.markdown("<br/>", unsafe_allow_html=True)
|
||||||
if st.button("⬅️", disabled=current_idx==0, use_container_width=True):
|
if st.button("⬅️", disabled=current_idx==0, width="stretch"):
|
||||||
st.session_state.current_map_date = dias_disponiveis[current_idx - 1]
|
st.session_state.current_map_date = dias_disponiveis[current_idx - 1]
|
||||||
st.rerun()
|
st.rerun()
|
||||||
with col_drop:
|
with col_drop:
|
||||||
@@ -269,7 +271,7 @@ with tab4:
|
|||||||
st.rerun()
|
st.rerun()
|
||||||
with col_next:
|
with col_next:
|
||||||
st.markdown("<br/>", unsafe_allow_html=True)
|
st.markdown("<br/>", unsafe_allow_html=True)
|
||||||
if st.button("➡️", disabled=current_idx>=len(dias_disponiveis)-1, use_container_width=True):
|
if st.button("➡️", disabled=current_idx>=len(dias_disponiveis)-1, width="stretch"):
|
||||||
st.session_state.current_map_date = dias_disponiveis[current_idx + 1]
|
st.session_state.current_map_date = dias_disponiveis[current_idx + 1]
|
||||||
st.rerun()
|
st.rerun()
|
||||||
|
|
||||||
@@ -342,7 +344,7 @@ with tab4:
|
|||||||
}, inplace=True)
|
}, inplace=True)
|
||||||
|
|
||||||
st.subheader(f"Flight Schedule for {st.session_state.current_map_date}")
|
st.subheader(f"Flight Schedule for {st.session_state.current_map_date}")
|
||||||
st.dataframe(df_day_display, use_container_width=True)
|
st.dataframe(df_day_display, width="stretch")
|
||||||
|
|
||||||
# --- TAB 5: RESULTS ---
|
# --- TAB 5: RESULTS ---
|
||||||
with tab5:
|
with tab5:
|
||||||
@@ -372,7 +374,7 @@ with tab5:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
if not df_results.empty:
|
if not df_results.empty:
|
||||||
st.dataframe(df_results, use_container_width=True)
|
st.dataframe(df_results, width="stretch")
|
||||||
|
|
||||||
if 'Pernoites' in df_results.columns:
|
if 'Pernoites' in df_results.columns:
|
||||||
st.subheader("Days Out of Base (Pernoites) Breakdown")
|
st.subheader("Days Out of Base (Pernoites) Breakdown")
|
||||||
|
|||||||
@@ -17,11 +17,10 @@ import sqlite3
|
|||||||
# Ensure src module is visible if run standalone
|
# Ensure src module is visible if run standalone
|
||||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
||||||
|
|
||||||
from src.fleet_assignment.config import DB_PATH, RESULTS_FILE, COST_FILE
|
from src.fleet_assignment.config import DB_PATH, RESULTS_FILE, COST_FILE, JSON_AEROPORTOS, JSON_EMISSORES
|
||||||
|
|
||||||
def run_solver(time_limit_sec=300):
|
def run_solver(time_limit_sec=300):
|
||||||
print(f"--- Starting Global Solver Worker (Limit: {time_limit_sec}s) ---")
|
print(f"--- Starting Global Solver Worker (Limit: {time_limit_sec}s) ---")
|
||||||
JSON_AEROPORTOS = "airports.json"
|
|
||||||
|
|
||||||
conn = sqlite3.connect(DB_PATH)
|
conn = sqlite3.connect(DB_PATH)
|
||||||
df_frota_db = pd.read_sql_query("SELECT * FROM frota", conn)
|
df_frota_db = pd.read_sql_query("SELECT * FROM frota", conn)
|
||||||
@@ -34,7 +33,7 @@ def run_solver(time_limit_sec=300):
|
|||||||
pax_demanda_completa = demands_db.groupby(['Data Apenas', 'Localidade Decolagem', 'Localidade Pouso'])['PAX'].sum().reset_index()
|
pax_demanda_completa = demands_db.groupby(['Data Apenas', 'Localidade Decolagem', 'Localidade Pouso'])['PAX'].sum().reset_index()
|
||||||
demands = pax_demanda_completa[pax_demanda_completa['PAX'] > 0].copy()
|
demands = pax_demanda_completa[pax_demanda_completa['PAX'] > 0].copy()
|
||||||
|
|
||||||
with open('emissores.json', 'r') as f:
|
with open(JSON_EMISSORES, 'r') as f:
|
||||||
emissores_data = json.load(f)[0]
|
emissores_data = json.load(f)[0]
|
||||||
icaos_emissores = list(emissores_data.values())
|
icaos_emissores = list(emissores_data.values())
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user