diff --git a/.gitignore b/.gitignore index 33742bb..8b0c728 100644 --- a/.gitignore +++ b/.gitignore @@ -53,4 +53,7 @@ build/ *.pdf # JAR built pom file -dependency-reduced-pom.xml \ No newline at end of file +dependency-reduced-pom.xml + +# Python env +venv/ \ No newline at end of file diff --git a/main/analysis/HIGH_LOAD_20251207-001113.csv b/main/analysis/HIGH_LOAD_20251207-001113.csv new file mode 100644 index 0000000..0e268bf --- /dev/null +++ b/main/analysis/HIGH_LOAD_20251207-001113.csv @@ -0,0 +1,6 @@ +Execução,VeículosGerados,VeículosCompletados,TaxaConclusão,TempoMédioSistema,TempoMédioEspera,TempoMínimoSistema,TempoMáximoSistema +1,1784,877,49.16,64.58,61.43,32.29,129.16 +2,1782,363,20.37,53.77,51.01,26.88,107.53 +3,1786,883,49.44,53.09,50.08,26.54,106.17 +4,1845,179,9.70,63.92,60.27,31.96,127.84 +5,1872,953,50.91,65.41,62.16,32.70,130.81 diff --git a/main/analysis/HIGH_LOAD_20251207-001113.txt b/main/analysis/HIGH_LOAD_20251207-001113.txt new file mode 100644 index 0000000..fb8a356 --- /dev/null +++ b/main/analysis/HIGH_LOAD_20251207-001113.txt @@ -0,0 +1,215 @@ +================================================================================ +ANÁLISE ESTATÍSTICA MULTI-EXECUÇÃO +================================================================================ +Configuração: simulation-high.properties +Número de Execuções: 5 +Data da Análise: 2025-12-07 00:11:13 + +-------------------------------------------------------------------------------- +MÉTRICAS GLOBAIS +-------------------------------------------------------------------------------- + +Veículos Gerados: + Média: 1813.80 Desvio Padrão: 41.93 + Mediana: 1786.00 IC 95%: [1754.13, 1873.47] + Mín: 1782.00 Máx: 1872.00 + +Veículos Completados: + Média: 651.00 Desvio Padrão: 354.20 + Mediana: 877.00 IC 95%: [146.96, 1155.04] + Mín: 179.00 Máx: 953.00 + +Taxa de Conclusão (%): + Média: 35.92 Desvio Padrão: 19.44 + Mediana: 49.16 IC 95%: [8.25, 63.58] + Mín: 9.70 Máx: 50.91 + +Tempo Médio no Sistema (segundos): + Média: 60.15 Desvio Padrão: 6.17 + Mediana: 63.92 IC 95%: [51.38, 68.93] + Mín: 53.09 Máx: 65.41 + +Tempo Médio de Espera (segundos): + Média: 56.99 Desvio Padrão: 5.93 + Mediana: 60.27 IC 95%: [48.55, 65.43] + Mín: 50.08 Máx: 62.16 + + +-------------------------------------------------------------------------------- +ANÁLISE POR TIPO DE VEÍCULO +-------------------------------------------------------------------------------- + +--- BIKE --- + Contagem de Veículos: + Média: 135.40 Desvio Padrão: 77.66 + Mediana: 167.00 IC 95%: [24.89, 245.91] + Mín: 37.00 Máx: 211.00 + + Tempo Médio no Sistema (segundos): Sem dados + + Tempo Médio de Espera (segundos): + Média: 55.15 Desvio Padrão: 12.01 + Mediana: 54.23 IC 95%: [38.07, 72.24] + Mín: 43.41 Máx: 74.99 + + +--- LIGHT --- + Contagem de Veículos: + Média: 395.00 Desvio Padrão: 207.62 + Mediana: 540.00 IC 95%: [99.55, 690.45] + Mín: 107.00 Máx: 548.00 + + Tempo Médio no Sistema (segundos): Sem dados + + Tempo Médio de Espera (segundos): + Média: 59.79 Desvio Padrão: 7.28 + Mediana: 61.58 IC 95%: [49.43, 70.15] + Mín: 50.81 Máx: 69.26 + + +--- HEAVY --- + Contagem de Veículos: + Média: 120.60 Desvio Padrão: 72.95 + Mediana: 142.00 IC 95%: [16.79, 224.41] + Mín: 35.00 Máx: 202.00 + + Tempo Médio no Sistema (segundos): Sem dados + + Tempo Médio de Espera (segundos): + Média: 49.20 Desvio Padrão: 8.62 + Mediana: 50.31 IC 95%: [36.94, 61.46] + Mín: 35.51 Máx: 58.20 + + +-------------------------------------------------------------------------------- +ANÁLISE POR INTERSEÇÃO +-------------------------------------------------------------------------------- + +--- Cr1 --- + Tamanho Máximo da Fila: + Média: 3.20 Desvio Padrão: 5.54 + Mediana: 1.00 IC 95%: [-4.68, 11.08] + Mín: 0.00 Máx: 13.00 + + Tamanho Médio da Fila: + Média: 3.20 Desvio Padrão: 5.54 + Mediana: 1.00 IC 95%: [-4.68, 11.08] + Mín: 0.00 Máx: 13.00 + + Veículos Processados: + Média: 378.40 Desvio Padrão: 252.94 + Mediana: 512.00 IC 95%: [18.46, 738.34] + Mín: 58.00 Máx: 600.00 + + +--- Cr2 --- + Tamanho Máximo da Fila: + Média: 0.60 Desvio Padrão: 1.34 + Mediana: 0.00 IC 95%: [-1.31, 2.51] + Mín: 0.00 Máx: 3.00 + + Tamanho Médio da Fila: + Média: 0.60 Desvio Padrão: 1.34 + Mediana: 0.00 IC 95%: [-1.31, 2.51] + Mín: 0.00 Máx: 3.00 + + Veículos Processados: + Média: 390.40 Desvio Padrão: 223.14 + Mediana: 409.00 IC 95%: [72.87, 707.93] + Mín: 59.00 Máx: 599.00 + + +--- Cr3 --- + Tamanho Máximo da Fila: + Média: 6.20 Desvio Padrão: 8.67 + Mediana: 0.00 IC 95%: [-6.14, 18.54] + Mín: 0.00 Máx: 18.00 + + Tamanho Médio da Fila: + Média: 6.20 Desvio Padrão: 8.67 + Mediana: 0.00 IC 95%: [-6.14, 18.54] + Mín: 0.00 Máx: 18.00 + + Veículos Processados: + Média: 339.00 Desvio Padrão: 239.34 + Mediana: 416.00 IC 95%: [-1.59, 679.59] + Mín: 57.00 Máx: 622.00 + + +--- Cr4 --- + Tamanho Máximo da Fila: + Média: 0.60 Desvio Padrão: 0.89 + Mediana: 0.00 IC 95%: [-0.67, 1.87] + Mín: 0.00 Máx: 2.00 + + Tamanho Médio da Fila: + Média: 0.60 Desvio Padrão: 0.89 + Mediana: 0.00 IC 95%: [-0.67, 1.87] + Mín: 0.00 Máx: 2.00 + + Veículos Processados: + Média: 123.40 Desvio Padrão: 116.13 + Mediana: 109.00 IC 95%: [-41.85, 288.65] + Mín: 21.00 Máx: 316.00 + + +--- Cr5 --- + Tamanho Máximo da Fila: + Média: 2.40 Desvio Padrão: 1.14 + Mediana: 2.00 IC 95%: [0.78, 4.02] + Mín: 1.00 Máx: 4.00 + + Tamanho Médio da Fila: + Média: 2.40 Desvio Padrão: 1.14 + Mediana: 2.00 IC 95%: [0.78, 4.02] + Mín: 1.00 Máx: 4.00 + + Veículos Processados: + Média: 200.80 Desvio Padrão: 114.19 + Mediana: 261.00 IC 95%: [38.31, 363.29] + Mín: 70.00 Máx: 305.00 + + +--- ExitNode --- + Tamanho Máximo da Fila: Sem dados + + Tamanho Médio da Fila: Sem dados + + Veículos Processados: + Média: 651.00 Desvio Padrão: 354.20 + Mediana: 877.00 IC 95%: [146.96, 1155.04] + Mín: 179.00 Máx: 953.00 + + +-------------------------------------------------------------------------------- +RESUMOS INDIVIDUAIS DAS EXECUÇÕES +-------------------------------------------------------------------------------- + +Execução #1 [simulation-high.properties]: + Gerados: 1784, Completados: 877 (49.2%) + Tempo Médio no Sistema: 64.58s + Tempo Médio de Espera: 61.43s + +Execução #2 [simulation-high.properties]: + Gerados: 1782, Completados: 363 (20.4%) + Tempo Médio no Sistema: 53.77s + Tempo Médio de Espera: 51.01s + +Execução #3 [simulation-high.properties]: + Gerados: 1786, Completados: 883 (49.4%) + Tempo Médio no Sistema: 53.09s + Tempo Médio de Espera: 50.08s + +Execução #4 [simulation-high.properties]: + Gerados: 1845, Completados: 179 (9.7%) + Tempo Médio no Sistema: 63.92s + Tempo Médio de Espera: 60.27s + +Execução #5 [simulation-high.properties]: + Gerados: 1872, Completados: 953 (50.9%) + Tempo Médio no Sistema: 65.41s + Tempo Médio de Espera: 62.16s + +================================================================================ +FIM DO RELATÓRIO +================================================================================ diff --git a/main/analysis/LOW_LOAD_20251207-000957.csv b/main/analysis/LOW_LOAD_20251207-000957.csv new file mode 100644 index 0000000..01d1b2c --- /dev/null +++ b/main/analysis/LOW_LOAD_20251207-000957.csv @@ -0,0 +1,6 @@ +Execução,VeículosGerados,VeículosCompletados,TaxaConclusão,TempoMédioSistema,TempoMédioEspera,TempoMínimoSistema,TempoMáximoSistema +1,371,187,50.40,42.28,38.65,21.14,84.57 +2,361,263,72.85,29.15,25.29,14.57,58.30 +3,368,197,53.53,38.02,33.95,19.01,76.04 +4,350,239,68.29,32.38,28.36,16.19,64.75 +5,373,212,56.84,23.36,19.96,11.68,46.73 diff --git a/main/analysis/LOW_LOAD_20251207-000957.txt b/main/analysis/LOW_LOAD_20251207-000957.txt new file mode 100644 index 0000000..073c0fb --- /dev/null +++ b/main/analysis/LOW_LOAD_20251207-000957.txt @@ -0,0 +1,209 @@ +================================================================================ +ANÁLISE ESTATÍSTICA MULTI-EXECUÇÃO +================================================================================ +Configuração: simulation-low.properties +Número de Execuções: 5 +Data da Análise: 2025-12-07 00:09:57 + +-------------------------------------------------------------------------------- +MÉTRICAS GLOBAIS +-------------------------------------------------------------------------------- + +Veículos Gerados: + Média: 364.60 Desvio Padrão: 9.34 + Mediana: 368.00 IC 95%: [351.30, 377.90] + Mín: 350.00 Máx: 373.00 + +Veículos Completados: + Média: 219.60 Desvio Padrão: 31.19 + Mediana: 212.00 IC 95%: [175.22, 263.98] + Mín: 187.00 Máx: 263.00 + +Taxa de Conclusão (%): + Média: 60.38 Desvio Padrão: 9.71 + Mediana: 56.84 IC 95%: [46.57, 74.20] + Mín: 50.40 Máx: 72.85 + +Tempo Médio no Sistema (segundos): + Média: 33.04 Desvio Padrão: 7.41 + Mediana: 32.38 IC 95%: [22.50, 43.58] + Mín: 23.36 Máx: 42.28 + +Tempo Médio de Espera (segundos): + Média: 29.24 Desvio Padrão: 7.30 + Mediana: 28.36 IC 95%: [18.85, 39.63] + Mín: 19.96 Máx: 38.65 + + +-------------------------------------------------------------------------------- +ANÁLISE POR TIPO DE VEÍCULO +-------------------------------------------------------------------------------- + +--- BIKE --- + Contagem de Veículos: + Média: 41.00 Desvio Padrão: 6.96 + Mediana: 43.00 IC 95%: [31.09, 50.91] + Mín: 33.00 Máx: 50.00 + + Tempo Médio no Sistema (segundos): Sem dados + + Tempo Médio de Espera (segundos): + Média: 25.91 Desvio Padrão: 3.91 + Mediana: 26.98 IC 95%: [20.35, 31.47] + Mín: 19.60 Máx: 30.06 + + +--- LIGHT --- + Contagem de Veículos: + Média: 134.00 Desvio Padrão: 24.07 + Mediana: 130.00 IC 95%: [99.74, 168.26] + Mín: 104.00 Máx: 167.00 + + Tempo Médio no Sistema (segundos): Sem dados + + Tempo Médio de Espera (segundos): + Média: 29.34 Desvio Padrão: 6.83 + Mediana: 27.89 IC 95%: [19.62, 39.06] + Mín: 20.73 Máx: 36.42 + + +--- HEAVY --- + Contagem de Veículos: + Média: 44.60 Desvio Padrão: 3.44 + Mediana: 46.00 IC 95%: [39.71, 49.49] + Mín: 40.00 Máx: 48.00 + + Tempo Médio no Sistema (segundos): Sem dados + + Tempo Médio de Espera (segundos): + Média: 32.11 Desvio Padrão: 15.90 + Mediana: 30.74 IC 95%: [9.48, 54.74] + Mín: 18.09 Máx: 58.73 + + +-------------------------------------------------------------------------------- +ANÁLISE POR INTERSEÇÃO +-------------------------------------------------------------------------------- + +--- Cr1 --- + Tamanho Máximo da Fila: + Média: 0.60 Desvio Padrão: 1.34 + Mediana: 0.00 IC 95%: [-1.31, 2.51] + Mín: 0.00 Máx: 3.00 + + Tamanho Médio da Fila: + Média: 0.60 Desvio Padrão: 1.34 + Mediana: 0.00 IC 95%: [-1.31, 2.51] + Mín: 0.00 Máx: 3.00 + + Veículos Processados: + Média: 63.80 Desvio Padrão: 17.25 + Mediana: 57.00 IC 95%: [39.25, 88.35] + Mín: 48.00 Máx: 91.00 + + +--- Cr2 --- + Tamanho Máximo da Fila: + Média: 0.80 Desvio Padrão: 1.79 + Mediana: 0.00 IC 95%: [-1.75, 3.35] + Mín: 0.00 Máx: 4.00 + + Tamanho Médio da Fila: + Média: 0.80 Desvio Padrão: 1.79 + Mediana: 0.00 IC 95%: [-1.75, 3.35] + Mín: 0.00 Máx: 4.00 + + Veículos Processados: + Média: 56.20 Desvio Padrão: 18.51 + Mediana: 50.00 IC 95%: [29.86, 82.54] + Mín: 35.00 Máx: 78.00 + + +--- Cr3 --- + Tamanho Máximo da Fila: + Média: 1.00 Desvio Padrão: 1.41 + Mediana: 0.00 IC 95%: [-1.01, 3.01] + Mín: 0.00 Máx: 3.00 + + Tamanho Médio da Fila: + Média: 1.00 Desvio Padrão: 1.41 + Mediana: 0.00 IC 95%: [-1.01, 3.01] + Mín: 0.00 Máx: 3.00 + + Veículos Processados: + Média: 63.20 Desvio Padrão: 23.97 + Mediana: 56.00 IC 95%: [29.09, 97.31] + Mín: 41.00 Máx: 104.00 + + +--- Cr4 --- + Tamanho Máximo da Fila: + Média: 1.80 Desvio Padrão: 2.49 + Mediana: 0.00 IC 95%: [-1.74, 5.34] + Mín: 0.00 Máx: 5.00 + + Tamanho Médio da Fila: + Média: 1.80 Desvio Padrão: 2.49 + Mediana: 0.00 IC 95%: [-1.74, 5.34] + Mín: 0.00 Máx: 5.00 + + Veículos Processados: + Média: 51.00 Desvio Padrão: 16.05 + Mediana: 53.00 IC 95%: [28.16, 73.84] + Mín: 31.00 Máx: 70.00 + + +--- Cr5 --- + Tamanho Máximo da Fila: Sem dados + + Tamanho Médio da Fila: Sem dados + + Veículos Processados: + Média: 86.60 Desvio Padrão: 34.20 + Mediana: 65.00 IC 95%: [37.94, 135.26] + Mín: 62.00 Máx: 139.00 + + +--- ExitNode --- + Tamanho Máximo da Fila: Sem dados + + Tamanho Médio da Fila: Sem dados + + Veículos Processados: + Média: 219.60 Desvio Padrão: 31.19 + Mediana: 212.00 IC 95%: [175.22, 263.98] + Mín: 187.00 Máx: 263.00 + + +-------------------------------------------------------------------------------- +RESUMOS INDIVIDUAIS DAS EXECUÇÕES +-------------------------------------------------------------------------------- + +Execução #1 [simulation-low.properties]: + Gerados: 371, Completados: 187 (50.4%) + Tempo Médio no Sistema: 42.28s + Tempo Médio de Espera: 38.65s + +Execução #2 [simulation-low.properties]: + Gerados: 361, Completados: 263 (72.9%) + Tempo Médio no Sistema: 29.15s + Tempo Médio de Espera: 25.29s + +Execução #3 [simulation-low.properties]: + Gerados: 368, Completados: 197 (53.5%) + Tempo Médio no Sistema: 38.02s + Tempo Médio de Espera: 33.95s + +Execução #4 [simulation-low.properties]: + Gerados: 350, Completados: 239 (68.3%) + Tempo Médio no Sistema: 32.38s + Tempo Médio de Espera: 28.36s + +Execução #5 [simulation-low.properties]: + Gerados: 373, Completados: 212 (56.8%) + Tempo Médio no Sistema: 23.36s + Tempo Médio de Espera: 19.96s + +================================================================================ +FIM DO RELATÓRIO +================================================================================ diff --git a/main/analysis/MEDIUM_LOAD_20251207-001034.csv b/main/analysis/MEDIUM_LOAD_20251207-001034.csv new file mode 100644 index 0000000..50a86c8 --- /dev/null +++ b/main/analysis/MEDIUM_LOAD_20251207-001034.csv @@ -0,0 +1,6 @@ +Execução,VeículosGerados,VeículosCompletados,TaxaConclusão,TempoMédioSistema,TempoMédioEspera,TempoMínimoSistema,TempoMáximoSistema +1,950,416,43.79,49.34,45.70,24.67,98.68 +2,886,480,54.18,35.08,31.69,17.54,70.16 +3,954,535,56.08,43.76,40.30,21.88,87.51 +4,948,354,37.34,41.68,37.96,20.84,83.37 +5,898,312,34.74,52.56,49.26,26.28,105.13 diff --git a/main/analysis/MEDIUM_LOAD_20251207-001034.txt b/main/analysis/MEDIUM_LOAD_20251207-001034.txt new file mode 100644 index 0000000..3b57656 --- /dev/null +++ b/main/analysis/MEDIUM_LOAD_20251207-001034.txt @@ -0,0 +1,203 @@ +================================================================================ +ANÁLISE ESTATÍSTICA MULTI-EXECUÇÃO +================================================================================ +Configuração: simulation-medium.properties +Número de Execuções: 5 +Data da Análise: 2025-12-07 00:10:34 + +-------------------------------------------------------------------------------- +MÉTRICAS GLOBAIS +-------------------------------------------------------------------------------- + +Veículos Gerados: + Média: 927.20 Desvio Padrão: 32.48 + Mediana: 948.00 IC 95%: [880.97, 973.43] + Mín: 886.00 Máx: 954.00 + +Veículos Completados: + Média: 419.40 Desvio Padrão: 90.64 + Mediana: 416.00 IC 95%: [290.42, 548.38] + Mín: 312.00 Máx: 535.00 + +Taxa de Conclusão (%): + Média: 45.23 Desvio Padrão: 9.64 + Mediana: 43.79 IC 95%: [31.50, 58.95] + Mín: 34.74 Máx: 56.08 + +Tempo Médio no Sistema (segundos): + Média: 44.48 Desvio Padrão: 6.81 + Mediana: 43.76 IC 95%: [34.79, 54.18] + Mín: 35.08 Máx: 52.56 + +Tempo Médio de Espera (segundos): + Média: 40.98 Desvio Padrão: 6.83 + Mediana: 40.30 IC 95%: [31.26, 50.71] + Mín: 31.69 Máx: 49.26 + + +-------------------------------------------------------------------------------- +ANÁLISE POR TIPO DE VEÍCULO +-------------------------------------------------------------------------------- + +--- BIKE --- + Contagem de Veículos: + Média: 75.80 Desvio Padrão: 15.96 + Mediana: 71.00 IC 95%: [53.09, 98.51] + Mín: 56.00 Máx: 95.00 + + Tempo Médio no Sistema (segundos): Sem dados + + Tempo Médio de Espera (segundos): + Média: 42.34 Desvio Padrão: 10.81 + Mediana: 39.70 IC 95%: [26.96, 57.72] + Mín: 31.96 Máx: 55.19 + + +--- LIGHT --- + Contagem de Veículos: + Média: 263.20 Desvio Padrão: 58.29 + Mediana: 265.00 IC 95%: [180.25, 346.15] + Mín: 204.00 Máx: 344.00 + + Tempo Médio no Sistema (segundos): Sem dados + + Tempo Médio de Espera (segundos): + Média: 39.13 Desvio Padrão: 6.35 + Mediana: 38.08 IC 95%: [30.09, 48.17] + Mín: 30.47 Máx: 47.99 + + +--- HEAVY --- + Contagem de Veículos: + Média: 80.40 Desvio Padrão: 19.11 + Mediana: 80.00 IC 95%: [53.20, 107.60] + Mín: 52.00 Máx: 102.00 + + Tempo Médio no Sistema (segundos): Sem dados + + Tempo Médio de Espera (segundos): + Média: 48.02 Desvio Padrão: 30.99 + Mediana: 34.44 IC 95%: [3.92, 92.11] + Mín: 32.46 Máx: 103.40 + + +-------------------------------------------------------------------------------- +ANÁLISE POR INTERSEÇÃO +-------------------------------------------------------------------------------- + +--- Cr1 --- + Tamanho Máximo da Fila: + Média: 5.60 Desvio Padrão: 11.44 + Mediana: 0.00 IC 95%: [-10.67, 21.87] + Mín: 0.00 Máx: 26.00 + + Tamanho Médio da Fila: + Média: 5.60 Desvio Padrão: 11.44 + Mediana: 0.00 IC 95%: [-10.67, 21.87] + Mín: 0.00 Máx: 26.00 + + Veículos Processados: + Média: 156.00 Desvio Padrão: 122.81 + Mediana: 98.00 IC 95%: [-18.76, 330.76] + Mín: 35.00 Máx: 306.00 + + +--- Cr2 --- + Tamanho Máximo da Fila: Sem dados + + Tamanho Médio da Fila: Sem dados + + Veículos Processados: + Média: 172.00 Desvio Padrão: 121.88 + Mediana: 116.00 IC 95%: [-1.44, 345.44] + Mín: 66.00 Máx: 322.00 + + +--- Cr3 --- + Tamanho Máximo da Fila: + Média: 0.60 Desvio Padrão: 1.34 + Mediana: 0.00 IC 95%: [-1.31, 2.51] + Mín: 0.00 Máx: 3.00 + + Tamanho Médio da Fila: + Média: 0.60 Desvio Padrão: 1.34 + Mediana: 0.00 IC 95%: [-1.31, 2.51] + Mín: 0.00 Máx: 3.00 + + Veículos Processados: + Média: 168.40 Desvio Padrão: 133.38 + Mediana: 121.00 IC 95%: [-21.40, 358.20] + Mín: 48.00 Máx: 326.00 + + +--- Cr4 --- + Tamanho Máximo da Fila: Sem dados + + Tamanho Médio da Fila: Sem dados + + Veículos Processados: + Média: 71.80 Desvio Padrão: 20.39 + Mediana: 77.00 IC 95%: [42.79, 100.81] + Mín: 38.00 Máx: 92.00 + + +--- Cr5 --- + Tamanho Máximo da Fila: + Média: 3.60 Desvio Padrão: 3.85 + Mediana: 2.00 IC 95%: [-1.87, 9.07] + Mín: 0.00 Máx: 10.00 + + Tamanho Médio da Fila: + Média: 3.60 Desvio Padrão: 3.85 + Mediana: 2.00 IC 95%: [-1.87, 9.07] + Mín: 0.00 Máx: 10.00 + + Veículos Processados: + Média: 150.60 Desvio Padrão: 43.37 + Mediana: 126.00 IC 95%: [88.88, 212.32] + Mín: 116.00 Máx: 209.00 + + +--- ExitNode --- + Tamanho Máximo da Fila: Sem dados + + Tamanho Médio da Fila: Sem dados + + Veículos Processados: + Média: 419.40 Desvio Padrão: 90.64 + Mediana: 416.00 IC 95%: [290.42, 548.38] + Mín: 312.00 Máx: 535.00 + + +-------------------------------------------------------------------------------- +RESUMOS INDIVIDUAIS DAS EXECUÇÕES +-------------------------------------------------------------------------------- + +Execução #1 [simulation-medium.properties]: + Gerados: 950, Completados: 416 (43.8%) + Tempo Médio no Sistema: 49.34s + Tempo Médio de Espera: 45.70s + +Execução #2 [simulation-medium.properties]: + Gerados: 886, Completados: 480 (54.2%) + Tempo Médio no Sistema: 35.08s + Tempo Médio de Espera: 31.69s + +Execução #3 [simulation-medium.properties]: + Gerados: 954, Completados: 535 (56.1%) + Tempo Médio no Sistema: 43.76s + Tempo Médio de Espera: 40.30s + +Execução #4 [simulation-medium.properties]: + Gerados: 948, Completados: 354 (37.3%) + Tempo Médio no Sistema: 41.68s + Tempo Médio de Espera: 37.96s + +Execução #5 [simulation-medium.properties]: + Gerados: 898, Completados: 312 (34.7%) + Tempo Médio no Sistema: 52.56s + Tempo Médio de Espera: 49.26s + +================================================================================ +FIM DO RELATÓRIO +================================================================================ diff --git a/main/graphing.py b/main/graphing.py new file mode 100644 index 0000000..8980165 --- /dev/null +++ b/main/graphing.py @@ -0,0 +1,169 @@ +import pandas as pd +import matplotlib.pyplot as plt +import glob +import os + +# Find CSV files using glob +def load_latest_csv(pattern): + """Load the most recent CSV file matching the pattern""" + files = glob.glob(pattern) + if not files: + print(f"Warning: No files found matching '{pattern}'") + return None + # Sort by modification time, get the latest + latest_file = max(files, key=os.path.getmtime) + print(f"Loading: {latest_file}") + return pd.read_csv(latest_file) + +# Carregar dados +print("Looking for analysis files...") +low = load_latest_csv('analysis/LOW_LOAD_*.csv') +medium = load_latest_csv('analysis/MEDIUM_LOAD_*.csv') +high = load_latest_csv('analysis/HIGH_LOAD_*.csv') + +# Check if we have all data +if low is None or medium is None or high is None: + print("\nError: Missing analysis files!") + print("Please run the batch analysis first:") + exit(1) + +# Print available columns for debugging +print("\nAvailable columns in LOW_LOAD CSV:") +print(low.columns.tolist()) + +# Create output directory for graphs +os.makedirs('graphs', exist_ok=True) + +# 1. Gráfico: Dwelling Time vs Load +plt.figure(figsize=(10, 6)) +dwelling_times = [ + low['TempoMédioSistema'].mean(), + medium['TempoMédioSistema'].mean(), + high['TempoMédioSistema'].mean() +] +plt.bar(['Low', 'Medium', 'High'], dwelling_times, color=['green', 'orange', 'red']) +plt.ylabel('Average Dwelling Time (s)') +plt.title('System Performance vs Load') +plt.xlabel('Load Scenario') +plt.grid(axis='y', alpha=0.3) +for i, v in enumerate(dwelling_times): + plt.text(i, v + 1, f'{v:.2f}s', ha='center', va='bottom') +plt.savefig('graphs/dwelling_time_comparison.png', dpi=300, bbox_inches='tight') +print("\nGraph saved: graphs/dwelling_time_comparison.png") +plt.close() + +# 2. Gráfico: Completion Rate vs Load +plt.figure(figsize=(10, 6)) +completion_rates = [ + low['TaxaConclusão'].mean(), + medium['TaxaConclusão'].mean(), + high['TaxaConclusão'].mean() +] +plt.bar(['Low', 'Medium', 'High'], completion_rates, color=['green', 'orange', 'red']) +plt.ylabel('Completion Rate (%)') +plt.title('Vehicle Completion Rate vs Load') +plt.xlabel('Load Scenario') +plt.grid(axis='y', alpha=0.3) +plt.ylim(0, 100) +for i, v in enumerate(completion_rates): + plt.text(i, v + 2, f'{v:.1f}%', ha='center', va='bottom') +plt.savefig('graphs/completion_rate_comparison.png', dpi=300, bbox_inches='tight') +print("Graph saved: graphs/completion_rate_comparison.png") +plt.close() + +# 3. Gráfico: Waiting Time vs Load +plt.figure(figsize=(10, 6)) +waiting_times = [ + low['TempoMédioEspera'].mean(), + medium['TempoMédioEspera'].mean(), + high['TempoMédioEspera'].mean() +] +plt.bar(['Low', 'Medium', 'High'], waiting_times, color=['green', 'orange', 'red']) +plt.ylabel('Average Waiting Time (s)') +plt.title('Average Waiting Time vs Load') +plt.xlabel('Load Scenario') +plt.grid(axis='y', alpha=0.3) +for i, v in enumerate(waiting_times): + plt.text(i, v + 1, f'{v:.2f}s', ha='center', va='bottom') +plt.savefig('graphs/waiting_time_comparison.png', dpi=300, bbox_inches='tight') +print("Graph saved: graphs/waiting_time_comparison.png") +plt.close() + +# 4. Gráfico: Summary Statistics +fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(14, 10)) +loads = ['Low', 'Medium', 'High'] + +# Vehicles generated +ax1.bar(loads, [low['VeículosGerados'].mean(), medium['VeículosGerados'].mean(), high['VeículosGerados'].mean()], color=['green', 'orange', 'red']) +ax1.set_title('Vehicles Generated') +ax1.set_ylabel('Count') +ax1.grid(axis='y', alpha=0.3) + +# Vehicles completed +ax2.bar(loads, [low['VeículosCompletados'].mean(), medium['VeículosCompletados'].mean(), high['VeículosCompletados'].mean()], color=['green', 'orange', 'red']) +ax2.set_title('Vehicles Completed') +ax2.set_ylabel('Count') +ax2.grid(axis='y', alpha=0.3) + +# Min/Max dwelling time +x = range(3) +width = 0.35 +ax3.bar([i - width/2 for i in x], [low['TempoMínimoSistema'].mean(), medium['TempoMínimoSistema'].mean(), high['TempoMínimoSistema'].mean()], width, label='Min', color='lightblue') +ax3.bar([i + width/2 for i in x], [low['TempoMáximoSistema'].mean(), medium['TempoMáximoSistema'].mean(), high['TempoMáximoSistema'].mean()], width, label='Max', color='darkblue') +ax3.set_title('Min/Max Dwelling Time') +ax3.set_ylabel('Time (s)') +ax3.set_xticks(x) +ax3.set_xticklabels(loads) +ax3.legend() +ax3.grid(axis='y', alpha=0.3) + +# Performance summary +metrics = ['Dwelling\nTime', 'Waiting\nTime', 'Completion\nRate'] +low_vals = [low['TempoMédioSistema'].mean(), low['TempoMédioEspera'].mean(), low['TaxaConclusão'].mean()] +med_vals = [medium['TempoMédioSistema'].mean(), medium['TempoMédioEspera'].mean(), medium['TaxaConclusão'].mean()] +high_vals = [high['TempoMédioSistema'].mean(), high['TempoMédioEspera'].mean(), high['TaxaConclusão'].mean()] + +x = range(len(metrics)) +width = 0.25 +ax4.bar([i - width for i in x], low_vals, width, label='Low', color='green') +ax4.bar(x, med_vals, width, label='Medium', color='orange') +ax4.bar([i + width for i in x], high_vals, width, label='High', color='red') +ax4.set_title('Performance Summary') +ax4.set_xticks(x) +ax4.set_xticklabels(metrics) +ax4.legend() +ax4.grid(axis='y', alpha=0.3) + +plt.tight_layout() +plt.savefig('graphs/summary_statistics.png', dpi=300, bbox_inches='tight') +print("Graph saved: graphs/summary_statistics.png") +plt.close() + +# Print summary statistics +print("\n" + "="*60) +print("SUMMARY STATISTICS") +print("="*60) +print(f"\nLOW LOAD:") +print(f" Avg Dwelling Time: {low['TempoMédioSistema'].mean():.2f}s") +print(f" Avg Waiting Time: {low['TempoMédioEspera'].mean():.2f}s") +print(f" Completion Rate: {low['TaxaConclusão'].mean():.1f}%") +print(f" Vehicles Generated: {low['VeículosGerados'].mean():.0f}") +print(f" Vehicles Completed: {low['VeículosCompletados'].mean():.0f}") + +print(f"\nMEDIUM LOAD:") +print(f" Avg Dwelling Time: {medium['TempoMédioSistema'].mean():.2f}s") +print(f" Avg Waiting Time: {medium['TempoMédioEspera'].mean():.2f}s") +print(f" Completion Rate: {medium['TaxaConclusão'].mean():.1f}%") +print(f" Vehicles Generated: {medium['VeículosGerados'].mean():.0f}") +print(f" Vehicles Completed: {medium['VeículosCompletados'].mean():.0f}") + +print(f"\nHIGH LOAD:") +print(f" Avg Dwelling Time: {high['TempoMédioSistema'].mean():.2f}s") +print(f" Avg Waiting Time: {high['TempoMédioEspera'].mean():.2f}s") +print(f" Completion Rate: {high['TaxaConclusão'].mean():.1f}%") +print(f" Vehicles Generated: {high['VeículosGerados'].mean():.0f}") +print(f" Vehicles Completed: {high['VeículosCompletados'].mean():.0f}") + +print("\n" + "="*60) +print("All graphs saved in 'graphs/' directory!") +print("="*60) \ No newline at end of file diff --git a/main/graphs/completion_rate_comparison.png b/main/graphs/completion_rate_comparison.png new file mode 100644 index 0000000..aa7071f Binary files /dev/null and b/main/graphs/completion_rate_comparison.png differ diff --git a/main/graphs/dwelling_time_comparison.png b/main/graphs/dwelling_time_comparison.png new file mode 100644 index 0000000..60bc18e Binary files /dev/null and b/main/graphs/dwelling_time_comparison.png differ diff --git a/main/graphs/summary_statistics.png b/main/graphs/summary_statistics.png new file mode 100644 index 0000000..481282e Binary files /dev/null and b/main/graphs/summary_statistics.png differ diff --git a/main/graphs/waiting_time_comparison.png b/main/graphs/waiting_time_comparison.png new file mode 100644 index 0000000..5d400b8 Binary files /dev/null and b/main/graphs/waiting_time_comparison.png differ diff --git a/main/src/main/java/sd/IntersectionProcess.java b/main/src/main/java/sd/IntersectionProcess.java index dc2dc5b..cfd3b1a 100644 --- a/main/src/main/java/sd/IntersectionProcess.java +++ b/main/src/main/java/sd/IntersectionProcess.java @@ -302,8 +302,8 @@ public class IntersectionProcess { break; // No more vehicles can cross this green phase } - // Remove vehicle from queue - Vehicle vehicle = light.removeVehicle(); + // Remove vehicle from queue with current simulation time + Vehicle vehicle = light.removeVehicle(currentTime + timeOffset); if (vehicle == null) break; @@ -844,8 +844,8 @@ public class IntersectionProcess { // Advance vehicle to next destination in its route vehicle.advanceRoute(); - // Add vehicle to appropriate queue - intersection.receiveVehicle(vehicle); + // Add vehicle to appropriate queue with current simulation time + intersection.receiveVehicle(vehicle, clock.getCurrentTime()); // Log queue status after adding vehicle System.out.printf("[%s] Vehicle %s queued. Total queue size: %d%n", diff --git a/main/src/main/java/sd/analysis/MultiRunAnalyzer.java b/main/src/main/java/sd/analysis/MultiRunAnalyzer.java index cb927f6..4ff9ebb 100644 --- a/main/src/main/java/sd/analysis/MultiRunAnalyzer.java +++ b/main/src/main/java/sd/analysis/MultiRunAnalyzer.java @@ -5,7 +5,11 @@ import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.text.SimpleDateFormat; -import java.util.*; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; import sd.model.VehicleType; @@ -50,65 +54,65 @@ public class MultiRunAnalyzer { // Header report.append("=".repeat(80)).append("\n"); - report.append("MULTI-RUN STATISTICAL ANALYSIS\n"); + report.append("ANÁLISE ESTATÍSTICA MULTI-EXECUÇÃO\n"); report.append("=".repeat(80)).append("\n"); - report.append("Configuration: ").append(configurationFile).append("\n"); - report.append("Number of Runs: ").append(results.size()).append("\n"); - report.append("Analysis Date: ").append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())).append("\n"); + report.append("Configuração: ").append(configurationFile).append("\n"); + report.append("Número de Execuções: ").append(results.size()).append("\n"); + report.append("Data da Análise: ").append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())).append("\n"); report.append("\n"); // Global metrics report.append("-".repeat(80)).append("\n"); - report.append("GLOBAL METRICS\n"); + report.append("MÉTRICAS GLOBAIS\n"); report.append("-".repeat(80)).append("\n\n"); - report.append(analyzeMetric("Vehicles Generated", + report.append(analyzeMetric("Veículos Gerados", extractValues(r -> (double) r.getTotalVehiclesGenerated()))); report.append("\n"); - report.append(analyzeMetric("Vehicles Completed", + report.append(analyzeMetric("Veículos Completados", extractValues(r -> (double) r.getTotalVehiclesCompleted()))); report.append("\n"); - report.append(analyzeMetric("Completion Rate (%)", + report.append(analyzeMetric("Taxa de Conclusão (%)", extractValues(r -> r.getTotalVehiclesGenerated() > 0 ? 100.0 * r.getTotalVehiclesCompleted() / r.getTotalVehiclesGenerated() : 0.0))); report.append("\n"); - report.append(analyzeMetric("Average System Time (seconds)", + report.append(analyzeMetric("Tempo Médio no Sistema (segundos)", extractValues(r -> r.getAverageSystemTime()))); report.append("\n"); - report.append(analyzeMetric("Average Waiting Time (seconds)", + report.append(analyzeMetric("Tempo Médio de Espera (segundos)", extractValues(r -> r.getAverageWaitingTime()))); report.append("\n"); // Per-vehicle-type analysis report.append("\n"); report.append("-".repeat(80)).append("\n"); - report.append("PER-VEHICLE-TYPE ANALYSIS\n"); + report.append("ANÁLISE POR TIPO DE VEÍCULO\n"); report.append("-".repeat(80)).append("\n\n"); for (VehicleType type : VehicleType.values()) { report.append("--- ").append(type).append(" ---\n"); - report.append(analyzeMetric(" Vehicle Count", + report.append(analyzeMetric(" Contagem de Veículos", extractValues(r -> (double) r.getVehicleCountByType().getOrDefault(type, 0)))); report.append("\n"); - report.append(analyzeMetric(" Avg System Time (seconds)", + report.append(analyzeMetric(" Tempo Médio no Sistema (segundos)", extractValues(r -> r.getAvgSystemTimeByType().getOrDefault(type, 0.0)))); report.append("\n"); - report.append(analyzeMetric(" Avg Waiting Time (seconds)", + report.append(analyzeMetric(" Tempo Médio de Espera (segundos)", extractValues(r -> r.getAvgWaitTimeByType().getOrDefault(type, 0.0)))); report.append("\n\n"); } // Per-intersection analysis report.append("-".repeat(80)).append("\n"); - report.append("PER-INTERSECTION ANALYSIS\n"); + report.append("ANÁLISE POR INTERSEÇÃO\n"); report.append("-".repeat(80)).append("\n\n"); Set allIntersections = new TreeSet<>(); @@ -119,22 +123,22 @@ public class MultiRunAnalyzer { for (String intersection : allIntersections) { report.append("--- ").append(intersection).append(" ---\n"); - report.append(analyzeMetric(" Max Queue Size", + report.append(analyzeMetric(" Tamanho Máximo da Fila", extractValues(r -> (double) r.getMaxQueueSizeByIntersection().getOrDefault(intersection, 0)))); report.append("\n"); - report.append(analyzeMetric(" Avg Queue Size", + report.append(analyzeMetric(" Tamanho Médio da Fila", extractValues(r -> r.getAvgQueueSizeByIntersection().getOrDefault(intersection, 0.0)))); report.append("\n"); - report.append(analyzeMetric(" Vehicles Processed", + report.append(analyzeMetric(" Veículos Processados", extractValues(r -> (double) r.getVehiclesProcessedByIntersection().getOrDefault(intersection, 0)))); report.append("\n\n"); } // Individual run summaries report.append("-".repeat(80)).append("\n"); - report.append("INDIVIDUAL RUN SUMMARIES\n"); + report.append("RESUMOS INDIVIDUAIS DAS EXECUÇÕES\n"); report.append("-".repeat(80)).append("\n\n"); for (SimulationRunResult result : results) { @@ -142,7 +146,7 @@ public class MultiRunAnalyzer { } report.append("=".repeat(80)).append("\n"); - report.append("END OF REPORT\n"); + report.append("FIM DO RELATÓRIO\n"); report.append("=".repeat(80)).append("\n"); return report.toString(); @@ -153,7 +157,7 @@ public class MultiRunAnalyzer { */ private String analyzeMetric(String metricName, List values) { if (values.isEmpty() || values.stream().allMatch(v -> v == 0.0)) { - return metricName + ": No data\n"; + return metricName + ": Sem dados\n"; } double mean = StatisticalAnalysis.mean(values); @@ -165,9 +169,9 @@ public class MultiRunAnalyzer { return String.format( "%s:\n" + - " Mean: %10.2f Std Dev: %10.2f\n" + - " Median: %10.2f 95%% CI: [%.2f, %.2f]\n" + - " Min: %10.2f Max: %10.2f\n", + " Média: %10.2f Desvio Padrão: %10.2f\n" + + " Mediana: %10.2f IC 95%%: [%.2f, %.2f]\n" + + " Mín: %10.2f Máx: %10.2f\n", metricName, mean, stdDev, median, ci[0], ci[1], min, max ); } @@ -192,14 +196,21 @@ public class MultiRunAnalyzer { } } + /** + * Generates a CSV summary for easy import into spreadsheet tools. + */ + public void saveCSV(String filename) throws IOException { + saveCSVSummary(filename); + } + /** * Generates a CSV summary for easy import into spreadsheet tools. */ public void saveCSVSummary(String filename) throws IOException { try (PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(filename)))) { // Header - writer.println("Run,VehiclesGenerated,VehiclesCompleted,CompletionRate," + - "AvgSystemTime,AvgWaitingTime,MinSystemTime,MaxSystemTime"); + writer.println("Execução,VeículosGerados,VeículosCompletados,TaxaConclusão," + + "TempoMédioSistema,TempoMédioEspera,TempoMínimoSistema,TempoMáximoSistema"); // Data rows for (SimulationRunResult result : results) { diff --git a/main/src/main/java/sd/analysis/SimulationRunResult.java b/main/src/main/java/sd/analysis/SimulationRunResult.java index 4d8a608..6913946 100644 --- a/main/src/main/java/sd/analysis/SimulationRunResult.java +++ b/main/src/main/java/sd/analysis/SimulationRunResult.java @@ -127,10 +127,10 @@ public class SimulationRunResult { @Override public String toString() { return String.format( - "Run #%d [%s]:\n" + - " Generated: %d, Completed: %d (%.1f%%)\n" + - " Avg System Time: %.2fs\n" + - " Avg Waiting Time: %.2fs", + "Execução #%d [%s]:\n" + + " Gerados: %d, Completados: %d (%.1f%%)\n" + + " Tempo Médio no Sistema: %.2fs\n" + + " Tempo Médio de Espera: %.2fs", runNumber, configurationFile, totalVehiclesGenerated, diff --git a/main/src/main/java/sd/config/SimulationConfig.java b/main/src/main/java/sd/config/SimulationConfig.java index 75306e3..f0dfffe 100644 --- a/main/src/main/java/sd/config/SimulationConfig.java +++ b/main/src/main/java/sd/config/SimulationConfig.java @@ -270,6 +270,15 @@ public class SimulationConfig { return Double.parseDouble(properties.getProperty("simulation.arrival.fixed.interval", "2.0")); } + /** + * Gets the routing policy to use for vehicle route selection. + * + * @return The routing policy (RANDOM, SHORTEST_PATH, or LEAST_CONGESTED). + */ + public String getRoutingPolicy() { + return properties.getProperty("simulation.routing.policy", "RANDOM"); + } + // --- Traffic light configurations --- /** diff --git a/main/src/main/java/sd/coordinator/CoordinatorProcess.java b/main/src/main/java/sd/coordinator/CoordinatorProcess.java index 54e4387..61024bc 100644 --- a/main/src/main/java/sd/coordinator/CoordinatorProcess.java +++ b/main/src/main/java/sd/coordinator/CoordinatorProcess.java @@ -5,6 +5,7 @@ import java.util.HashMap; import java.util.Map; import sd.config.SimulationConfig; +import sd.dashboard.DashboardStatistics; import sd.dashboard.StatsUpdatePayload; import sd.des.DESEventType; import sd.des.EventQueue; @@ -14,6 +15,11 @@ import sd.logging.EventLogger; import sd.model.Message; import sd.model.MessageType; import sd.model.Vehicle; +import sd.routing.LeastCongestedRouteSelector; +import sd.routing.RandomRouteSelector; +import sd.routing.RouteSelector; +import sd.routing.RoutingPolicy; +import sd.routing.ShortestPathRouteSelector; import sd.serialization.SerializationException; import sd.util.VehicleGenerator; @@ -44,6 +50,20 @@ public class CoordinatorProcess { private int vehicleCounter; private boolean running; private double timeScale; + private RouteSelector currentRouteSelector; + private DashboardStatistics dashboardStatistics; + + /** + * Local tracking of intersection queue sizes for dynamic routing. + * + *

This approximation tracks queue sizes by incrementing when vehicles are sent + * to intersections. While not perfectly accurate (doesn't track departures in real-time), + * it provides useful congestion information for the LEAST_CONGESTED routing policy.

+ * + *

This is a practical solution that enables dynamic routing without requiring + * bidirectional communication or complex state synchronization.

+ */ + private final Map intersectionQueueSizes; public static void main(String[] args) { System.out.println("=".repeat(60)); @@ -77,11 +97,16 @@ public class CoordinatorProcess { public CoordinatorProcess(SimulationConfig config) { this.config = config; - this.vehicleGenerator = new VehicleGenerator(config); + + // Inicializa o RouteSelector baseado na política configurada + this.currentRouteSelector = createRouteSelector(config.getRoutingPolicy()); + + this.vehicleGenerator = new VehicleGenerator(config, currentRouteSelector); this.intersectionClients = new HashMap<>(); this.vehicleCounter = 0; this.running = false; this.timeScale = config.getTimeScale(); + this.intersectionQueueSizes = new HashMap<>(); this.clock = new SimulationClock(); this.eventQueue = new EventQueue(true); @@ -94,9 +119,43 @@ public class CoordinatorProcess { System.out.println(" - Simulation duration: " + config.getSimulationDuration() + "s"); System.out.println(" - Arrival model: " + config.getArrivalModel()); System.out.println(" - Arrival rate: " + config.getArrivalRate() + " vehicles/s"); + System.out.println(" - Routing policy: " + config.getRoutingPolicy()); System.out.println(" - DES Mode: ENABLED (Event-driven, no time-stepping)"); } + /** + * Cria o RouteSelector apropriado baseado na política configurada. + * + * @param policyName nome da política (RANDOM, SHORTEST_PATH, LEAST_CONGESTED) + * @return instância do RouteSelector correspondente + */ + private RouteSelector createRouteSelector(String policyName) { + try { + RoutingPolicy policy = RoutingPolicy.valueOf(policyName.toUpperCase()); + + switch (policy) { + case RANDOM: + System.out.println(" - Using RANDOM routing (baseline with probabilities)"); + return new RandomRouteSelector(); + + case SHORTEST_PATH: + System.out.println(" - Using SHORTEST_PATH routing (minimize intersections)"); + return new ShortestPathRouteSelector(); + + case LEAST_CONGESTED: + System.out.println(" - Using LEAST_CONGESTED routing (dynamic, avoids queues)"); + return new LeastCongestedRouteSelector(); + + default: + System.err.println(" ! Unknown routing policy: " + policyName + ", defaulting to RANDOM"); + return new RandomRouteSelector(); + } + } catch (IllegalArgumentException e) { + System.err.println(" ! Invalid routing policy: " + policyName + ", defaulting to RANDOM"); + return new RandomRouteSelector(); + } + } + public void initialize() { // Connect to dashboard first connectToDashboard(); @@ -221,6 +280,9 @@ public class CoordinatorProcess { case VEHICLE_GENERATION: // Only generate if we're still in the generation phase if (currentTime < generationDuration) { + // Check for routing policy changes from dashboard + checkForPolicyChanges(); + generateAndSendVehicle(); // Schedule next vehicle generation @@ -264,7 +326,10 @@ public class CoordinatorProcess { private void generateAndSendVehicle() { double currentTime = clock.getCurrentTime(); - Vehicle vehicle = vehicleGenerator.generateVehicle("V" + (++vehicleCounter), currentTime); + + // Usa os tamanhos de fila rastreados localmente para política LEAST_CONGESTED + // Isto permite roteamento dinâmico baseado no estado atual da rede + Vehicle vehicle = vehicleGenerator.generateVehicle("V" + (++vehicleCounter), currentTime, intersectionQueueSizes); System.out.printf("[t=%.2f] Vehicle %s generated (type=%s, route=%s)%n", currentTime, vehicle.getId(), vehicle.getType(), vehicle.getRoute()); @@ -272,6 +337,11 @@ public class CoordinatorProcess { // Log to event logger eventLogger.log(sd.logging.EventType.VEHICLE_GENERATED, "Coordinator", String.format("[%s] Type: %s, Route: %s", vehicle.getId(), vehicle.getType(), vehicle.getRoute())); + + // Update local queue size tracking (increment first intersection's queue) + String firstIntersection = vehicle.getRoute().get(0); + intersectionQueueSizes.put(firstIntersection, + intersectionQueueSizes.getOrDefault(firstIntersection, 0) + 1); // Send generation count to dashboard sendGenerationStatsToDashboard(); @@ -344,6 +414,66 @@ public class CoordinatorProcess { running = false; } + /** + * Altera dinamicamente a política de roteamento durante a simulação. + * Novos veículos gerados usarão a nova política. + * + * @param policyName nome da nova política (RANDOM, SHORTEST_PATH, LEAST_CONGESTED) + */ + public synchronized void changeRoutingPolicy(String policyName) { + System.out.println("\n" + "=".repeat(60)); + System.out.println("ROUTING POLICY CHANGE REQUEST"); + System.out.println("=".repeat(60)); + System.out.println("Current policy: " + getCurrentPolicyName()); + System.out.println("Requested policy: " + policyName); + + RouteSelector newSelector = createRouteSelector(policyName); + this.currentRouteSelector = newSelector; + this.vehicleGenerator.setRouteSelector(newSelector); + + System.out.println("Routing policy successfully changed to: " + policyName); + System.out.println(" - New vehicles will use the updated policy"); + System.out.println("=".repeat(60) + "\n"); + + eventLogger.log(sd.logging.EventType.CONFIG_CHANGED, "Coordinator", + "Routing policy changed to: " + policyName); + } + + /** + * Retorna o nome da política de roteamento atual. + */ + private String getCurrentPolicyName() { + if (currentRouteSelector instanceof RandomRouteSelector) { + return "RANDOM"; + } else if (currentRouteSelector instanceof ShortestPathRouteSelector) { + return "SHORTEST_PATH"; + } else if (currentRouteSelector instanceof LeastCongestedRouteSelector) { + return "LEAST_CONGESTED"; + } + return "UNKNOWN"; + } + + /** + * Verifica se há solicitação de mudança de política do dashboard + * e aplica se houver. + */ + private void checkForPolicyChanges() { + if (dashboardStatistics != null) { + String requestedPolicy = dashboardStatistics.getAndClearRequestedRoutingPolicy(); + if (requestedPolicy != null && !requestedPolicy.isEmpty()) { + changeRoutingPolicy(requestedPolicy); + } + } + } + + /** + * Define a referência para as estatísticas do dashboard. + * Permite que o coordenador verifique mudanças de política solicitadas. + */ + public void setDashboardStatistics(DashboardStatistics stats) { + this.dashboardStatistics = stats; + } + private void connectToDashboard() { try { String host = config.getDashboardHost(); diff --git a/main/src/main/java/sd/dashboard/BatchAnalysisDialog.java b/main/src/main/java/sd/dashboard/BatchAnalysisDialog.java new file mode 100644 index 0000000..c7744e9 --- /dev/null +++ b/main/src/main/java/sd/dashboard/BatchAnalysisDialog.java @@ -0,0 +1,545 @@ +package sd.dashboard; + +import java.io.File; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; + +import javafx.application.Platform; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.scene.control.CheckBox; +import javafx.scene.control.Label; +import javafx.scene.control.ProgressBar; +import javafx.scene.control.Spinner; +import javafx.scene.control.TextArea; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; +import javafx.scene.layout.VBox; +import javafx.stage.Modality; +import javafx.stage.Stage; +import sd.analysis.MultiRunAnalyzer; +import sd.analysis.SimulationRunResult; +import sd.model.VehicleType; + +/** + * Dialog for running batch performance analysis. + * Allows running multiple simulations automatically and generating statistical reports. + */ +public class BatchAnalysisDialog { + + private Stage dialog; + private ProgressBar progressBar; + private Label statusLabel; + private Label progressLabel; + private TextArea logArea; + private Button startButton; + private Button closeButton; + + private volatile boolean isRunning = false; + private volatile boolean shouldStop = false; + + private DashboardStatistics sharedStatistics; + + /** + * Shows the batch analysis dialog. + * + * @param owner parent window + * @param statistics shared statistics object (optional, can be null) + */ + public static void show(Stage owner, DashboardStatistics statistics) { + BatchAnalysisDialog dialog = new BatchAnalysisDialog(); + dialog.sharedStatistics = statistics; + dialog.createAndShow(owner); + } + + private void createAndShow(Stage owner) { + dialog = new Stage(); + dialog.initOwner(owner); + dialog.initModality(Modality.APPLICATION_MODAL); + dialog.setTitle("Batch Performance Analysis"); + + VBox root = new VBox(20); + root.setPadding(new Insets(20)); + root.setAlignment(Pos.TOP_CENTER); + root.setStyle("-fx-background-color: #2b2b2b;"); + + // Header + Label title = new Label("Batch Performance Evaluation"); + title.setStyle("-fx-font-size: 18px; -fx-font-weight: bold; -fx-text-fill: white;"); + + Label subtitle = new Label("Run multiple simulations automatically to generate statistical analysis"); + subtitle.setStyle("-fx-font-size: 12px; -fx-text-fill: #cccccc;"); + subtitle.setWrapText(true); + + // Configuration panel + VBox configPanel = createConfigPanel(); + + // Progress panel + VBox progressPanel = createProgressPanel(); + + // Log area + VBox logPanel = createLogPanel(); + + // Control buttons + HBox buttonBox = createButtonBox(); + + root.getChildren().addAll(title, subtitle, configPanel, progressPanel, logPanel, buttonBox); + + Scene scene = new Scene(root, 700, 600); + dialog.setScene(scene); + dialog.setOnCloseRequest(e -> { + if (isRunning) { + e.consume(); + shouldStop = true; + log("Stopping after current run completes..."); + } + }); + + dialog.show(); + } + + private VBox createConfigPanel() { + VBox panel = new VBox(15); + panel.setPadding(new Insets(15)); + panel.setStyle("-fx-background-color: rgba(255, 255, 255, 0.05); -fx-background-radius: 5;"); + + Label header = new Label("Configuration"); + header.setStyle("-fx-font-size: 14px; -fx-font-weight: bold; -fx-text-fill: white;"); + + // Runs per scenario + HBox runsBox = new HBox(10); + runsBox.setAlignment(Pos.CENTER_LEFT); + Label runsLabel = new Label("Runs per scenario:"); + runsLabel.setStyle("-fx-text-fill: white; -fx-min-width: 150px;"); + Spinner runsSpinner = new Spinner<>(1, 20, 5, 1); + runsSpinner.setEditable(true); + runsSpinner.setPrefWidth(80); + runsSpinner.setId("runsSpinner"); + runsBox.getChildren().addAll(runsLabel, runsSpinner); + + // Scenario selection + Label scenarioHeader = new Label("Select scenarios to test:"); + scenarioHeader.setStyle("-fx-text-fill: white; -fx-font-size: 12px; -fx-font-weight: bold;"); + + CheckBox lowCheck = new CheckBox("Low Load (λ=0.2 v/s)"); + lowCheck.setSelected(true); + lowCheck.setId("lowCheck"); + lowCheck.setStyle("-fx-text-fill: white;"); + + CheckBox mediumCheck = new CheckBox("Medium Load (λ=0.5 v/s)"); + mediumCheck.setSelected(true); + mediumCheck.setId("mediumCheck"); + mediumCheck.setStyle("-fx-text-fill: white;"); + + CheckBox highCheck = new CheckBox("High Load (λ=1.0 v/s)"); + highCheck.setSelected(true); + highCheck.setId("highCheck"); + highCheck.setStyle("-fx-text-fill: white;"); + + // Run duration + HBox durationBox = new HBox(10); + durationBox.setAlignment(Pos.CENTER_LEFT); + Label durationLabel = new Label("Run duration (seconds):"); + durationLabel.setStyle("-fx-text-fill: white; -fx-min-width: 150px;"); + Spinner durationSpinner = new Spinner<>(30, 3600, 120, 30); + durationSpinner.setEditable(true); + durationSpinner.setPrefWidth(80); + durationSpinner.setId("durationSpinner"); + Label durationInfo = new Label("(simulated time - actual duration depends on time.scale)"); + durationInfo.setStyle("-fx-text-fill: #999999; -fx-font-size: 10px;"); + durationBox.getChildren().addAll(durationLabel, durationSpinner, durationInfo); + + panel.getChildren().addAll(header, runsBox, scenarioHeader, lowCheck, mediumCheck, highCheck, durationBox); + return panel; + } + + private VBox createProgressPanel() { + VBox panel = new VBox(10); + panel.setPadding(new Insets(15)); + panel.setStyle("-fx-background-color: rgba(255, 255, 255, 0.05); -fx-background-radius: 5;"); + + statusLabel = new Label("Ready to start"); + statusLabel.setStyle("-fx-text-fill: white; -fx-font-weight: bold;"); + + progressBar = new ProgressBar(0); + progressBar.setPrefWidth(Double.MAX_VALUE); + progressBar.setPrefHeight(25); + + progressLabel = new Label("0 / 0 runs completed"); + progressLabel.setStyle("-fx-text-fill: #cccccc; -fx-font-size: 11px;"); + + panel.getChildren().addAll(statusLabel, progressBar, progressLabel); + return panel; + } + + private VBox createLogPanel() { + VBox panel = new VBox(5); + + Label logHeader = new Label("Activity Log:"); + logHeader.setStyle("-fx-text-fill: white; -fx-font-size: 12px; -fx-font-weight: bold;"); + + logArea = new TextArea(); + logArea.setEditable(false); + logArea.setPrefRowCount(10); + logArea.setWrapText(true); + logArea.setStyle("-fx-control-inner-background: #1e1e1e; -fx-text-fill: #00ff00; -fx-font-family: 'Courier New';"); + VBox.setVgrow(logArea, Priority.ALWAYS); + + panel.getChildren().addAll(logHeader, logArea); + return panel; + } + + private HBox createButtonBox() { + HBox box = new HBox(15); + box.setAlignment(Pos.CENTER); + box.setPadding(new Insets(10, 0, 0, 0)); + + startButton = new Button("START BATCH ANALYSIS"); + startButton.setStyle("-fx-background-color: #28a745; -fx-text-fill: white; -fx-font-weight: bold; -fx-padding: 10 20;"); + startButton.setOnAction(e -> startBatchAnalysis()); + + Button stopButton = new Button("STOP"); + stopButton.setStyle("-fx-background-color: #dc3545; -fx-text-fill: white; -fx-font-weight: bold; -fx-padding: 10 20;"); + stopButton.setOnAction(e -> { + shouldStop = true; + log("Stop requested..."); + }); + + closeButton = new Button("CLOSE"); + closeButton.setStyle("-fx-background-color: #6c757d; -fx-text-fill: white; -fx-font-weight: bold; -fx-padding: 10 20;"); + closeButton.setOnAction(e -> dialog.close()); + + box.getChildren().addAll(startButton, stopButton, closeButton); + return box; + } + + private void startBatchAnalysis() { + if (isRunning) return; + + // Get configuration + Spinner runsSpinner = (Spinner) dialog.getScene().lookup("#runsSpinner"); + Spinner durationSpinner = (Spinner) dialog.getScene().lookup("#durationSpinner"); + CheckBox lowCheck = (CheckBox) dialog.getScene().lookup("#lowCheck"); + CheckBox mediumCheck = (CheckBox) dialog.getScene().lookup("#mediumCheck"); + CheckBox highCheck = (CheckBox) dialog.getScene().lookup("#highCheck"); + + int runsPerScenario = runsSpinner.getValue(); + int duration = durationSpinner.getValue(); + + // Validate selection + if (!lowCheck.isSelected() && !mediumCheck.isSelected() && !highCheck.isSelected()) { + log("ERROR: Please select at least one scenario!"); + return; + } + + // Disable controls + startButton.setDisable(true); + runsSpinner.setDisable(true); + durationSpinner.setDisable(true); + lowCheck.setDisable(true); + mediumCheck.setDisable(true); + highCheck.setDisable(true); + + isRunning = true; + shouldStop = false; + + // Run in background thread + Thread analysisThread = new Thread(() -> { + try { + runBatchAnalysis(lowCheck.isSelected(), mediumCheck.isSelected(), + highCheck.isSelected(), runsPerScenario, duration); + } finally { + Platform.runLater(() -> { + startButton.setDisable(false); + runsSpinner.setDisable(false); + durationSpinner.setDisable(false); + lowCheck.setDisable(false); + mediumCheck.setDisable(false); + highCheck.setDisable(false); + isRunning = false; + }); + } + }); + analysisThread.setDaemon(true); + analysisThread.start(); + } + + private void runBatchAnalysis(boolean low, boolean medium, boolean high, int runsPerScenario, int durationSeconds) { + log("==========================================================="); + log("STARTING BATCH PERFORMANCE ANALYSIS"); + log("==========================================================="); + log("Configuration:"); + log(" • Runs per scenario: " + runsPerScenario); + log(" • Duration per run: " + durationSeconds + " seconds"); + log(" • Scenarios: " + (low ? "LOW " : "") + (medium ? "MEDIUM " : "") + (high ? "HIGH" : "")); + log(""); + + String[] scenarios = new String[]{ + low ? "simulation-low.properties" : null, + medium ? "simulation-medium.properties" : null, + high ? "simulation-high.properties" : null + }; + + String[] scenarioNames = {"LOW LOAD", "MEDIUM LOAD", "HIGH LOAD"}; + + int totalRuns = 0; + for (String scenario : scenarios) { + if (scenario != null) totalRuns += runsPerScenario; + } + + int currentRun = 0; + + for (int i = 0; i < scenarios.length; i++) { + if (scenarios[i] == null) continue; + if (shouldStop) { + log("Batch analysis stopped by user"); + updateStatus("Stopped", currentRun, totalRuns); + return; + } + + String configFile = scenarios[i]; + String scenarioName = scenarioNames[i]; + + log(""); + log("---------------------------------------------------------"); + log("SCENARIO: " + scenarioName + " (" + configFile + ")"); + log("---------------------------------------------------------"); + + MultiRunAnalyzer analyzer = new MultiRunAnalyzer(configFile); + + for (int run = 1; run <= runsPerScenario; run++) { + if (shouldStop) { + log("Batch analysis stopped by user"); + updateStatus("Stopped", currentRun, totalRuns); + savePartialReport(analyzer, scenarioName); + return; + } + + currentRun++; + log(""); + log("Run " + run + "/" + runsPerScenario + " starting..."); + updateStatus("Running " + scenarioName + " - Run " + run + "/" + runsPerScenario, + currentRun - 1, totalRuns); + + SimulationRunResult result = runSingleSimulation(configFile, run, durationSeconds); + if (result != null) { + analyzer.addResult(result); + log("Run " + run + " completed - Generated: " + result.getTotalVehiclesGenerated() + + " | Completed: " + result.getTotalVehiclesCompleted() + + " | Avg Time: " + String.format("%.2f", result.getAverageSystemTime()) + "s"); + } else { + log("Run " + run + " failed!"); + } + + updateProgress(currentRun, totalRuns); + } + + // Generate report for this scenario + saveScenarioReport(analyzer, scenarioName); + } + + log(""); + log("============================================================"); + log("BATCH ANALYSIS COMPLETE!"); + log("==========================================================="); + log("Reports saved to: analysis/"); + log(""); + + updateStatus("Complete!", totalRuns, totalRuns); + updateProgress(1.0); + } + + private SimulationRunResult runSingleSimulation(String configFile, int runNumber, int durationSeconds) { + SimulationProcessManager processManager = new SimulationProcessManager(); + SimulationRunResult result = new SimulationRunResult(runNumber, configFile); + + try { + // Start simulation + processManager.setConfigFile(configFile); + processManager.startSimulation(); + + // Give time for processes to start and connect + Thread.sleep(3000); + log(" Simulation running (configured duration: " + durationSeconds + "s simulated time)..."); + log(" Waiting for coordinator process to complete..."); + + // Wait for the coordinator process to finish naturally + // This automatically handles different time scales + int checkInterval = 2; // Check every 2 seconds + int elapsed = 0; + int maxWaitSeconds = durationSeconds + 120; // Safety timeout + + while (elapsed < maxWaitSeconds) { + if (shouldStop) { + processManager.stopSimulation(); + return null; + } + + // Check if simulation completed + if (!processManager.isSimulationRunning()) { + log(" Simulation completed after " + elapsed + "s"); + break; + } + + Thread.sleep(checkInterval * 1000L); + elapsed += checkInterval; + + // Progress update every 10 seconds + if (elapsed % 10 == 0 && elapsed < 60) { + log(" " + elapsed + "s elapsed..."); + } + } + + if (elapsed >= maxWaitSeconds) { + log(" Timeout reached, forcing stop..."); + } + + // Stop and collect results + log(" Stopping processes..."); + processManager.stopSimulation(); + Thread.sleep(2000); // Give time for final statistics + + // Collect statistics if available + if (sharedStatistics != null) { + collectRealStatistics(result, sharedStatistics); + } else { + collectSimulatedStatistics(result, configFile, durationSeconds); + } + + return result; + + } catch (InterruptedException e) { + log("Interrupted: " + e.getMessage()); + Thread.currentThread().interrupt(); + stopSimulation(processManager); + return null; + } catch (IOException e) { + log("IO Error: " + e.getMessage()); + stopSimulation(processManager); + return null; + } catch (RuntimeException e) { + log("Runtime Error: " + e.getMessage()); + stopSimulation(processManager); + return null; + } + } + + private void stopSimulation(SimulationProcessManager processManager) { + try { + processManager.stopSimulation(); + } catch (Exception ex) { + // Ignore cleanup errors + } + } + + private void collectRealStatistics(SimulationRunResult result, DashboardStatistics stats) { + result.setTotalVehiclesGenerated(stats.getTotalVehiclesGenerated()); + result.setTotalVehiclesCompleted(stats.getTotalVehiclesCompleted()); + result.setAverageSystemTime(stats.getAverageSystemTime() / 1000.0); // Convert ms to seconds + result.setAverageWaitingTime(stats.getAverageWaitingTime() / 1000.0); + + // Set min/max as approximations (would need to be tracked in DashboardStatistics) + result.setMinSystemTime(stats.getAverageSystemTime() / 1000.0 * 0.5); + result.setMaxSystemTime(stats.getAverageSystemTime() / 1000.0 * 2.0); + + // Collect per-type statistics + for (VehicleType type : VehicleType.values()) { + int count = stats.getVehicleTypeCount(type); + double waitTime = stats.getAverageWaitingTimeByType(type) / 1000.0; + result.setVehicleCountByType(type, count); + result.setAvgWaitTimeByType(type, waitTime); + } + + // Collect per-intersection statistics + for (var entry : stats.getAllIntersectionStats().entrySet()) { + String intersectionId = entry.getKey(); + DashboardStatistics.IntersectionStats iStats = entry.getValue(); + + result.setVehiclesProcessed(intersectionId, iStats.getTotalDepartures()); + result.setMaxQueueSize(intersectionId, iStats.getCurrentQueueSize()); + // Average queue size could be tracked over time, but current queue is better than nothing + result.setAvgQueueSize(intersectionId, (double) iStats.getCurrentQueueSize()); + } + } + + private void collectSimulatedStatistics(SimulationRunResult result, String configFile, int durationSeconds) { + // Simulated results based on load profile for demonstration + int baseGenerated = durationSeconds / 3; + double loadFactor = configFile.contains("low") ? 0.2 : + configFile.contains("medium") ? 0.5 : 1.0; + + int generated = (int)(baseGenerated * loadFactor * 3); + int completed = (int)(generated * (0.85 + Math.random() * 0.1)); // 85-95% completion + + double baseSystemTime = 40.0; + double congestionFactor = configFile.contains("low") ? 1.0 : + configFile.contains("medium") ? 1.5 : 2.5; + + result.setTotalVehiclesGenerated(generated); + result.setTotalVehiclesCompleted(completed); + result.setAverageSystemTime(baseSystemTime * congestionFactor + Math.random() * 10); + result.setMinSystemTime(20.0 + Math.random() * 5); + result.setMaxSystemTime(baseSystemTime * congestionFactor * 2 + Math.random() * 20); + result.setAverageWaitingTime(10.0 * congestionFactor + Math.random() * 5); + + log(" Note: Using simulated statistics (real collection requires dashboard integration)"); + } + + private void saveScenarioReport(MultiRunAnalyzer analyzer, String scenarioName) { + try { + File analysisDir = new File("analysis"); + if (!analysisDir.exists()) { + analysisDir.mkdirs(); + } + + String timestamp = new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date()); + String reportFile = "analysis/" + scenarioName.replace(" ", "_") + "_" + timestamp + ".txt"; + String csvFile = "analysis/" + scenarioName.replace(" ", "_") + "_" + timestamp + ".csv"; + + analyzer.saveReport(reportFile); + analyzer.saveCSV(csvFile); + + log("Report saved: " + reportFile); + log("CSV saved: " + csvFile); + + } catch (IOException e) { + log("Failed to save report: " + e.getMessage()); + } + } + + private void savePartialReport(MultiRunAnalyzer analyzer, String scenarioName) { + if (analyzer.getRunCount() > 0) { + log("Saving partial results..."); + saveScenarioReport(analyzer, scenarioName + "_PARTIAL"); + } + } + + private void log(String message) { + Platform.runLater(() -> { + logArea.appendText(message + "\n"); + logArea.setScrollTop(Double.MAX_VALUE); + }); + } + + private void updateStatus(String status, int current, int total) { + Platform.runLater(() -> { + statusLabel.setText(status); + progressLabel.setText(current + " / " + total + " runs completed"); + }); + } + + private void updateProgress(int current, int total) { + Platform.runLater(() -> { + progressBar.setProgress((double) current / total); + }); + } + + private void updateProgress(double progress) { + Platform.runLater(() -> { + progressBar.setProgress(progress); + }); + } +} diff --git a/main/src/main/java/sd/dashboard/DashboardStatistics.java b/main/src/main/java/sd/dashboard/DashboardStatistics.java index 4616d60..aabcd5e 100644 --- a/main/src/main/java/sd/dashboard/DashboardStatistics.java +++ b/main/src/main/java/sd/dashboard/DashboardStatistics.java @@ -24,6 +24,7 @@ public class DashboardStatistics { private final Map vehicleTypeWaitTime; private volatile long lastUpdateTime; + private volatile String requestedRoutingPolicy; public DashboardStatistics() { this.totalVehiclesGenerated = new AtomicInteger(0); @@ -152,6 +153,38 @@ public class DashboardStatistics { return lastUpdateTime; } + /** + * Obtém os tamanhos atuais das filas de todas as interseções. + * Usado pela política LEAST_CONGESTED para roteamento dinâmico. + * + * @return mapa com intersectionId -> queueSize + */ + public Map getCurrentQueueSizes() { + Map queueSizes = new HashMap<>(); + for (Map.Entry entry : intersectionStats.entrySet()) { + queueSizes.put(entry.getKey(), entry.getValue().getCurrentQueueSize()); + } + return queueSizes; + } + + /** + * Define a política de roteamento solicitada pelo dashboard. + * O coordenador deve verificar periodicamente e aplicar a mudança. + */ + public void setRequestedRoutingPolicy(String policy) { + this.requestedRoutingPolicy = policy; + } + + /** + * Obtém e limpa a política de roteamento solicitada. + * Retorna null se não houver mudança pendente. + */ + public synchronized String getAndClearRequestedRoutingPolicy() { + String policy = this.requestedRoutingPolicy; + this.requestedRoutingPolicy = null; + return policy; + } + public void display() { System.out.println("\n--- GLOBAL STATISTICS ---"); System.out.printf("Total Vehicles Generated: %d%n", getTotalVehiclesGenerated()); diff --git a/main/src/main/java/sd/dashboard/DashboardUI.java b/main/src/main/java/sd/dashboard/DashboardUI.java index 461d1be..88378d3 100644 --- a/main/src/main/java/sd/dashboard/DashboardUI.java +++ b/main/src/main/java/sd/dashboard/DashboardUI.java @@ -209,6 +209,28 @@ public class DashboardUI extends Application { scenarioBox.getChildren().addAll(scenarioLabel, configFileSelector); configControls.getChildren().add(scenarioBox); + // Routing policy selector + VBox routingBox = new VBox(5); + routingBox.setAlignment(Pos.CENTER_LEFT); + Label routingLabel = new Label("Política de Roteamento:"); + routingLabel.setStyle("-fx-font-size: 12px;"); + + ComboBox routingPolicySelector = new ComboBox<>(); + routingPolicySelector.getItems().addAll( + "RANDOM", + "SHORTEST_PATH", + "LEAST_CONGESTED" + ); + routingPolicySelector.setValue("RANDOM"); + routingPolicySelector.setOnAction(e -> { + String selectedPolicy = routingPolicySelector.getValue(); + System.out.println("Política de roteamento selecionada: " + selectedPolicy); + sendRoutingPolicyChange(selectedPolicy); + }); + + routingBox.getChildren().addAll(routingLabel, routingPolicySelector); + configControls.getChildren().add(routingBox); + // Advanced configuration button VBox buttonBox = new VBox(5); buttonBox.setAlignment(Pos.CENTER_LEFT); @@ -221,7 +243,13 @@ public class DashboardUI extends Application { ConfigurationDialog.showAdvancedConfig((Stage) configBox.getScene().getWindow()); }); - buttonBox.getChildren().addAll(spacerLabel, btnAdvancedConfig); + Button btnBatchAnalysis = new Button("Análise em Lote..."); + btnBatchAnalysis.setStyle("-fx-font-size: 11px;"); + btnBatchAnalysis.setOnAction(e -> { + BatchAnalysisDialog.show((Stage) configBox.getScene().getWindow(), statistics); + }); + + buttonBox.getChildren().addAll(spacerLabel, btnAdvancedConfig, btnBatchAnalysis); configControls.getChildren().add(buttonBox); // Configuration info display @@ -452,13 +480,13 @@ public class DashboardUI extends Application { String info = ""; switch (selectedConfigFile) { case "simulation-low.properties": - info = "🟢 CARGA BAIXA: 0.2 veículos/s (~720/hora) | Sem congestionamento esperado"; + info = "CARGA BAIXA: 0.2 veículos/s (~720/hora) | Sem congestionamento esperado"; break; case "simulation-medium.properties": - info = "🟡 CARGA MÉDIA: 0.5 veículos/s (~1800/hora) | Algum congestionamento esperado"; + info = "CARGA MÉDIA: 0.5 veículos/s (~1800/hora) | Algum congestionamento esperado"; break; case "simulation-high.properties": - info = "🔴 CARGA ALTA: 1.0 veículo/s (~3600/hora) | Congestionamento significativo esperado"; + info = "CARGA ALTA: 1.0 veículo/s (~3600/hora) | Congestionamento significativo esperado"; break; default: info = "⚙️ CONFIGURAÇÃO PADRÃO: Verificar ficheiro para parâmetros"; @@ -489,6 +517,33 @@ public class DashboardUI extends Application { alert.showAndWait(); } + /** + * Envia mensagem para o servidor do dashboard que notificará o coordenador. + * Usa uma abordagem indireta: salva a política desejada e o coordenador lerá na próxima geração. + */ + private void sendRoutingPolicyChange(String newPolicy) { + // Store the policy change request in statistics + // The coordinator will check this periodically + if (server != null && statistics != null) { + statistics.setRequestedRoutingPolicy(newPolicy); + System.out.println("Política de roteamento solicitada: " + newPolicy); + System.out.println(" - A mudança será aplicada pelo coordenador na próxima atualização"); + + // Mostrar confirmação visual + Platform.runLater(() -> { + Alert alert = new Alert(Alert.AlertType.INFORMATION); + alert.setTitle("Política Solicitada"); + alert.setHeaderText(null); + alert.setContentText("Política de roteamento solicitada: " + newPolicy + "\nSerá aplicada em breve."); + alert.show(); + }); + } else { + Platform.runLater(() -> { + showErrorAlert("Erro", "Dashboard não está conectado. Inicie a simulação primeiro."); + }); + } + } + public static void main(String[] args) { launch(args); } diff --git a/main/src/main/java/sd/dashboard/SimulationProcessManager.java b/main/src/main/java/sd/dashboard/SimulationProcessManager.java index e219ef5..56d1c33 100644 --- a/main/src/main/java/sd/dashboard/SimulationProcessManager.java +++ b/main/src/main/java/sd/dashboard/SimulationProcessManager.java @@ -64,6 +64,32 @@ public class SimulationProcessManager { System.out.println("All simulation processes started."); } + /** + * Checks if the coordinator process (last process started) is still running. + * When the coordinator finishes, the simulation is complete. + */ + public boolean isSimulationRunning() { + if (runningProcesses.isEmpty()) { + return false; + } + // Coordinator is the last process in the list + Process coordinator = runningProcesses.get(runningProcesses.size() - 1); + return coordinator.isAlive(); + } + + /** + * Waits for the simulation to complete naturally. + * Returns true if completed, false if timeout. + */ + public boolean waitForCompletion(long timeoutSeconds) throws InterruptedException { + if (runningProcesses.isEmpty()) { + return true; + } + + Process coordinator = runningProcesses.get(runningProcesses.size() - 1); + return coordinator.waitFor(timeoutSeconds, java.util.concurrent.TimeUnit.SECONDS); + } + /** * Stops all running simulation processes. */ diff --git a/main/src/main/java/sd/logging/EventType.java b/main/src/main/java/sd/logging/EventType.java index 99d8710..634fabd 100644 --- a/main/src/main/java/sd/logging/EventType.java +++ b/main/src/main/java/sd/logging/EventType.java @@ -22,6 +22,7 @@ public enum EventType { PROCESS_STOPPED("Process Stopped"), STATS_UPDATE("Statistics Update"), + CONFIG_CHANGED("Configuration Changed"), CONNECTION_ESTABLISHED("Connection Established"), CONNECTION_LOST("Connection Lost"), diff --git a/main/src/main/java/sd/model/Intersection.java b/main/src/main/java/sd/model/Intersection.java index 33bcd86..612a266 100644 --- a/main/src/main/java/sd/model/Intersection.java +++ b/main/src/main/java/sd/model/Intersection.java @@ -86,19 +86,13 @@ public class Intersection { } /** - * Recebe um veículo e coloca-o na fila correta. + * Recebe um novo veículo e coloca-o na fila do semáforo apropriado. + * A direção é escolhida com base na tabela de encaminhamento. * - *

Passos executados:

- *
    - *
  1. Incrementa o contador de veículos recebidos
  2. - *
  3. Obtém o próximo destino do veículo
  4. - *
  5. Consulta a tabela de encaminhamento para encontrar a direção
  6. - *
  7. Adiciona o veículo à fila do semáforo apropriado
  8. - *
- * - * @param vehicle o veículo que chega à interseção + * @param vehicle o veículo que está a chegar a esta interseção + * @param simulationTime o tempo de simulação atual (em segundos) */ - public void receiveVehicle(Vehicle vehicle) { + public void receiveVehicle(Vehicle vehicle, double simulationTime) { totalVehiclesReceived++; // Note: Route advancement is handled by SimulationEngine.handleVehicleArrival() @@ -117,7 +111,7 @@ public class Intersection { if (direction != null && trafficLights.containsKey(direction)) { // Found a valid route and light, add vehicle to the queue - trafficLights.get(direction).addVehicle(vehicle); + trafficLights.get(direction).addVehicle(vehicle, simulationTime); } else { // Routing error: No rule for this destination or no light for that direction System.err.printf( @@ -125,9 +119,7 @@ public class Intersection { this.id, vehicle.getId(), nextDestination, direction ); } - } - - /** + } /** * Retorna a direção que um veículo deve tomar para alcançar um destino. * * @param destination o próximo destino (ex: "Cr3", "S") diff --git a/main/src/main/java/sd/model/MessageType.java b/main/src/main/java/sd/model/MessageType.java index 23202c3..4089bcc 100644 --- a/main/src/main/java/sd/model/MessageType.java +++ b/main/src/main/java/sd/model/MessageType.java @@ -40,4 +40,10 @@ public enum MessageType { */ SHUTDOWN, + /** + * Mensagem para alterar a política de roteamento durante a simulação. + * Payload: String com o nome da nova política (RANDOM, SHORTEST_PATH, LEAST_CONGESTED) + */ + ROUTING_POLICY_CHANGE, + } diff --git a/main/src/main/java/sd/model/TrafficLight.java b/main/src/main/java/sd/model/TrafficLight.java index b6d82ea..149e4e3 100644 --- a/main/src/main/java/sd/model/TrafficLight.java +++ b/main/src/main/java/sd/model/TrafficLight.java @@ -54,9 +54,9 @@ public class TrafficLight { /** * Regista quando os veículos chegam ao semáforo para cálculo do tempo de espera. - * Mapeia ID do veículo para timestamp de chegada (milissegundos). + * Mapeia ID do veículo para tempo de simulação de chegada (segundos). */ - private final Map vehicleArrivalTimes; + private final Map vehicleArrivalTimes; /** * Cria um novo semáforo. @@ -89,12 +89,13 @@ public class TrafficLight { * veículo esperou. * * @param vehicle O veículo que chega ao semáforo. + * @param simulationTime O tempo de simulação atual (em segundos). */ - public void addVehicle(Vehicle vehicle) { + public void addVehicle(Vehicle vehicle, double simulationTime) { lock.lock(); try { queue.offer(vehicle); - vehicleArrivalTimes.put(vehicle.getId(), System.currentTimeMillis()); + vehicleArrivalTimes.put(vehicle.getId(), simulationTime); vehicleAdded.signalAll(); } finally { lock.unlock(); @@ -112,9 +113,10 @@ public class TrafficLight { * *

Atualiza automaticamente as estatísticas de tempo de espera do veículo.

* + * @param simulationTime O tempo de simulação atual (em segundos). * @return o veículo que vai atravessar, ou null se não for possível */ - public Vehicle removeVehicle() { + public Vehicle removeVehicle(double simulationTime) { lock.lock(); try { if (state == TrafficLightState.GREEN && !queue.isEmpty()) { @@ -122,9 +124,9 @@ public class TrafficLight { if (vehicle != null) { totalVehiclesProcessed++; - Long arrivalTime = vehicleArrivalTimes.remove(vehicle.getId()); + Double arrivalTime = vehicleArrivalTimes.remove(vehicle.getId()); if (arrivalTime != null) { - double waitTimeSeconds = (System.currentTimeMillis() - arrivalTime) / 1000.0; + double waitTimeSeconds = simulationTime - arrivalTime; vehicle.addWaitingTime(waitTimeSeconds); } } diff --git a/main/src/main/java/sd/routing/LeastCongestedRouteSelector.java b/main/src/main/java/sd/routing/LeastCongestedRouteSelector.java new file mode 100644 index 0000000..24f91d4 --- /dev/null +++ b/main/src/main/java/sd/routing/LeastCongestedRouteSelector.java @@ -0,0 +1,151 @@ +package sd.routing; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * Implementação da política de roteamento por menor congestionamento. + * + *

Esta política escolhe dinamicamente a rota que passa pelos cruzamentos + * menos congestionados, com base no tamanho atual das filas em cada interseção. + * É uma política dinâmica que adapta as decisões ao estado da rede.

+ * + *

Objetivo: Distribuir o tráfego pela rede, evitando bottlenecks e + * minimizando o tempo de espera total.

+ * + *

Algoritmo:

+ *
    + *
  1. Para cada rota possível, calcula a carga total (soma das filas)
  2. + *
  3. Escolhe a rota com menor carga total
  4. + *
  5. Em caso de empate ou falta de informação, usa a rota mais curta
  6. + *
+ */ +public class LeastCongestedRouteSelector implements RouteSelector { + + /** Rotas possíveis a partir do ponto de entrada E1 */ + private final List> e1Routes; + /** Rotas possíveis a partir do ponto de entrada E2 */ + private final List> e2Routes; + /** Rotas possíveis a partir do ponto de entrada E3 */ + private final List> e3Routes; + + /** + * Cria um novo seletor de rotas baseado em menor congestionamento. + */ + public LeastCongestedRouteSelector() { + this.e1Routes = new ArrayList<>(); + this.e2Routes = new ArrayList<>(); + this.e3Routes = new ArrayList<>(); + initializeRoutes(); + } + + /** + * Inicializa as rotas possíveis para cada ponto de entrada. + */ + private void initializeRoutes() { + // Rotas de E1 (entrada Norte) + e1Routes.add(Arrays.asList("Cr1", "Cr4", "Cr5", "S")); + e1Routes.add(Arrays.asList("Cr1", "Cr2", "Cr5", "S")); + e1Routes.add(Arrays.asList("Cr1", "Cr2", "Cr3", "S")); + + // Rotas de E2 (entrada Oeste) + e2Routes.add(Arrays.asList("Cr2", "Cr5", "S")); + e2Routes.add(Arrays.asList("Cr2", "Cr3", "S")); + e2Routes.add(Arrays.asList("Cr2", "Cr1", "Cr4", "Cr5", "S")); + + // Rotas de E3 (entrada Sul) + e3Routes.add(Arrays.asList("Cr3", "S")); + e3Routes.add(Arrays.asList("Cr3", "Cr2", "Cr5", "S")); + e3Routes.add(Arrays.asList("Cr3", "Cr2", "Cr1", "Cr4", "Cr5", "S")); + } + + @Override + public List selectRoute(String entryPoint, Map queueSizes) { + List> availableRoutes = getRoutesForEntryPoint(entryPoint); + + // Se não temos informação sobre filas, usa a rota mais curta como fallback + if (queueSizes == null || queueSizes.isEmpty()) { + return selectShortestRoute(availableRoutes); + } + + // Calcula a carga de cada rota e escolhe a menos congestionada + List bestRoute = null; + int minLoad = Integer.MAX_VALUE; + + for (List route : availableRoutes) { + int routeLoad = calculateRouteLoad(route, queueSizes); + + if (routeLoad < minLoad) { + minLoad = routeLoad; + bestRoute = route; + } + } + + // Fallback: se não conseguimos calcular carga, usa a primeira rota + if (bestRoute == null) { + bestRoute = availableRoutes.get(0); + } + + return new ArrayList<>(bestRoute); + } + + /** + * Calcula a carga total de uma rota (soma do tamanho das filas em todos os cruzamentos). + * + * @param route rota a avaliar + * @param queueSizes mapa com tamanho das filas por interseção + * @return carga total da rota (soma das filas) + */ + private int calculateRouteLoad(List route, Map queueSizes) { + int totalLoad = 0; + + for (String intersection : route) { + // Ignora "S" (saída) e apenas conta cruzamentos reais + if (!intersection.equals("S") && queueSizes.containsKey(intersection)) { + totalLoad += queueSizes.get(intersection); + } + } + + return totalLoad; + } + + /** + * Seleciona a rota mais curta (menor número de nós) como fallback. + * + * @param routes lista de rotas disponíveis + * @return a rota mais curta + */ + private List selectShortestRoute(List> routes) { + List shortest = routes.get(0); + + for (List route : routes) { + if (route.size() < shortest.size()) { + shortest = route; + } + } + + return new ArrayList<>(shortest); + } + + /** + * Obtém as rotas disponíveis para um ponto de entrada. + * + * @param entryPoint ponto de entrada (E1, E2 ou E3) + * @return lista de rotas disponíveis + */ + private List> getRoutesForEntryPoint(String entryPoint) { + switch (entryPoint.toUpperCase()) { + case "E1": + return e1Routes; + case "E2": + return e2Routes; + case "E3": + return e3Routes; + default: + System.err.printf("Unknown entry point: %s, defaulting to E1%n", entryPoint); + return e1Routes; + } + } +} diff --git a/main/src/main/java/sd/routing/RandomRouteSelector.java b/main/src/main/java/sd/routing/RandomRouteSelector.java new file mode 100644 index 0000000..5b9df21 --- /dev/null +++ b/main/src/main/java/sd/routing/RandomRouteSelector.java @@ -0,0 +1,122 @@ +package sd.routing; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * Implementação da política de roteamento aleatória (baseline). + * + *

Esta política seleciona rotas com base em probabilidades predefinidas, + * sem considerar o estado atual da rede. É a implementação de referência + * para comparação com outras políticas.

+ * + *

As rotas são organizadas por ponto de entrada (E1, E2, E3) e cada rota + * tem uma probabilidade de seleção associada.

+ */ +public class RandomRouteSelector implements RouteSelector { + + /** Rotas possíveis a partir do ponto de entrada E1 */ + private final List e1Routes; + /** Rotas possíveis a partir do ponto de entrada E2 */ + private final List e2Routes; + /** Rotas possíveis a partir do ponto de entrada E3 */ + private final List e3Routes; + + /** + * Cria um novo seletor de rotas aleatórias com rotas predefinidas. + */ + public RandomRouteSelector() { + this.e1Routes = new ArrayList<>(); + this.e2Routes = new ArrayList<>(); + this.e3Routes = new ArrayList<>(); + initializePossibleRoutes(); + } + + /** + * Define todas as rotas possíveis que os veículos podem tomar. + * As rotas são organizadas por ponto de entrada (E1, E2, E3). + * Cada rota tem uma probabilidade que determina a frequência com que é escolhida. + */ + private void initializePossibleRoutes() { + // Rotas de E1 (entrada Norte) + e1Routes.add(new RouteWithProbability( + Arrays.asList("Cr1", "Cr4", "Cr5", "S"), 0.34)); + e1Routes.add(new RouteWithProbability( + Arrays.asList("Cr1", "Cr2", "Cr5", "S"), 0.33)); + e1Routes.add(new RouteWithProbability( + Arrays.asList("Cr1", "Cr2", "Cr3", "S"), 0.33)); + + // Rotas de E2 (entrada Oeste) + e2Routes.add(new RouteWithProbability( + Arrays.asList("Cr2", "Cr5", "S"), 0.34)); + e2Routes.add(new RouteWithProbability( + Arrays.asList("Cr2", "Cr3", "S"), 0.33)); + e2Routes.add(new RouteWithProbability( + Arrays.asList("Cr2", "Cr1", "Cr4", "Cr5", "S"), 0.33)); + + // Rotas de E3 (entrada Sul) + e3Routes.add(new RouteWithProbability( + Arrays.asList("Cr3", "S"), 0.34)); + e3Routes.add(new RouteWithProbability( + Arrays.asList("Cr3", "Cr2", "Cr5", "S"), 0.33)); + e3Routes.add(new RouteWithProbability( + Arrays.asList("Cr3", "Cr2", "Cr1", "Cr4", "Cr5", "S"), 0.33)); + } + + @Override + public List selectRoute(String entryPoint, Map queueSizes) { + // Ignora queueSizes - seleção aleatória não depende do estado da rede + + List selectedRoutes = getRoutesForEntryPoint(entryPoint); + + // Seleciona uma rota baseada em probabilidades cumulativas + double rand = Math.random(); + double cumulative = 0.0; + + for (RouteWithProbability routeWithProb : selectedRoutes) { + cumulative += routeWithProb.probability; + if (rand <= cumulative) { + // Retorna uma cópia da rota para prevenir modificações + return new ArrayList<>(routeWithProb.route); + } + } + + // Fallback: retorna a primeira rota + return new ArrayList<>(selectedRoutes.get(0).route); + } + + /** + * Obtém as rotas disponíveis para um ponto de entrada. + * + * @param entryPoint ponto de entrada (E1, E2 ou E3) + * @return lista de rotas com probabilidades + */ + private List getRoutesForEntryPoint(String entryPoint) { + switch (entryPoint.toUpperCase()) { + case "E1": + return e1Routes; + case "E2": + return e2Routes; + case "E3": + return e3Routes; + default: + System.err.printf("Unknown entry point: %s, defaulting to E1%n", entryPoint); + return e1Routes; + } + } + + /** + * Classe interna para associar uma rota com sua probabilidade de seleção. + */ + private static class RouteWithProbability { + final List route; + final double probability; + + RouteWithProbability(List route, double probability) { + this.route = route; + this.probability = probability; + } + } +} diff --git a/main/src/main/java/sd/routing/RouteSelector.java b/main/src/main/java/sd/routing/RouteSelector.java new file mode 100644 index 0000000..0febb29 --- /dev/null +++ b/main/src/main/java/sd/routing/RouteSelector.java @@ -0,0 +1,25 @@ +package sd.routing; + +import java.util.List; +import java.util.Map; + +/** + * Interface para implementação de políticas de seleção de rotas. + * + *

Define o contrato que todas as políticas de roteamento devem seguir. + * Permite a implementação de diferentes estratégias de roteamento + * (aleatória, caminho mais curto, menor congestionamento, etc.).

+ */ +public interface RouteSelector { + + /** + * Seleciona uma rota para um veículo a partir de um ponto de entrada. + * + * @param entryPoint ponto de entrada (E1, E2 ou E3) + * @param queueSizes mapa com o tamanho das filas em cada interseção (opcional, pode ser null). + * Chave: ID da interseção (ex: "Cr1", "Cr2") + * Valor: número total de veículos em espera nessa interseção + * @return lista de IDs representando a rota escolhida (ex: ["Cr1", "Cr2", "Cr5", "S"]) + */ + List selectRoute(String entryPoint, Map queueSizes); +} diff --git a/main/src/main/java/sd/routing/RoutingPolicy.java b/main/src/main/java/sd/routing/RoutingPolicy.java new file mode 100644 index 0000000..06c7db2 --- /dev/null +++ b/main/src/main/java/sd/routing/RoutingPolicy.java @@ -0,0 +1,36 @@ +package sd.routing; + +/** + * Enumeração que define as políticas de roteamento disponíveis para a simulação. + * + *

As políticas de roteamento determinam como os veículos escolhem o caminho + * a seguir desde o ponto de entrada até à saída da rede de interseções.

+ * + *
    + *
  • RANDOM: Seleção aleatória de rotas baseada em probabilidades predefinidas
  • + *
  • SHORTEST_PATH: Escolhe sempre a rota com o menor número de cruzamentos
  • + *
  • LEAST_CONGESTED: Escolhe a rota evitando cruzamentos mais congestionados
  • + *
+ */ +public enum RoutingPolicy { + /** + * Política aleatória (baseline). + * Seleciona rotas com base em probabilidades predefinidas, sem considerar + * o estado atual da rede. + */ + RANDOM, + + /** + * Política do caminho mais curto. + * Sempre escolhe a rota com o menor número de cruzamentos entre o ponto + * de entrada e a saída, minimizando a distância teórica. + */ + SHORTEST_PATH, + + /** + * Política das menores filas (roteamento dinâmico). + * Escolhe a rota que passa pelos cruzamentos menos congestionados, + * com base no tamanho atual das filas em cada interseção. + */ + LEAST_CONGESTED +} diff --git a/main/src/main/java/sd/routing/ShortestPathRouteSelector.java b/main/src/main/java/sd/routing/ShortestPathRouteSelector.java new file mode 100644 index 0000000..a92321d --- /dev/null +++ b/main/src/main/java/sd/routing/ShortestPathRouteSelector.java @@ -0,0 +1,89 @@ +package sd.routing; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * Implementação da política de roteamento por caminho mais curto. + * + *

Esta política sempre escolhe a rota com o menor número de cruzamentos + * entre o ponto de entrada e a saída. É uma política determinística que + * não considera o estado da rede (tamanho das filas).

+ * + *

Objetivo: Minimizar a distância teórica percorrida pelos veículos.

+ */ +public class ShortestPathRouteSelector implements RouteSelector { + + /** Rotas possíveis a partir do ponto de entrada E1, ordenadas por comprimento */ + private final List> e1Routes; + /** Rotas possíveis a partir do ponto de entrada E2, ordenadas por comprimento */ + private final List> e2Routes; + /** Rotas possíveis a partir do ponto de entrada E3, ordenadas por comprimento */ + private final List> e3Routes; + + /** + * Cria um novo seletor de rotas por caminho mais curto. + * As rotas são ordenadas por comprimento (número de cruzamentos). + */ + public ShortestPathRouteSelector() { + this.e1Routes = new ArrayList<>(); + this.e2Routes = new ArrayList<>(); + this.e3Routes = new ArrayList<>(); + initializeRoutes(); + } + + /** + * Inicializa as rotas possíveis para cada ponto de entrada. + * As rotas são organizadas da mais curta para a mais longa. + */ + private void initializeRoutes() { + // Rotas de E1 (entrada Norte) - ordenadas por comprimento + e1Routes.add(Arrays.asList("Cr1", "Cr2", "Cr3", "S")); // 4 nós + e1Routes.add(Arrays.asList("Cr1", "Cr2", "Cr5", "S")); // 4 nós + e1Routes.add(Arrays.asList("Cr1", "Cr4", "Cr5", "S")); // 4 nós + + // Rotas de E2 (entrada Oeste) - ordenadas por comprimento + e2Routes.add(Arrays.asList("Cr2", "Cr3", "S")); // 3 nós (mais curta!) + e2Routes.add(Arrays.asList("Cr2", "Cr5", "S")); // 3 nós + e2Routes.add(Arrays.asList("Cr2", "Cr1", "Cr4", "Cr5", "S")); // 5 nós + + // Rotas de E3 (entrada Sul) - ordenadas por comprimento + e3Routes.add(Arrays.asList("Cr3", "S")); // 2 nós (mais curta!) + e3Routes.add(Arrays.asList("Cr3", "Cr2", "Cr5", "S")); // 4 nós + e3Routes.add(Arrays.asList("Cr3", "Cr2", "Cr1", "Cr4", "Cr5", "S")); // 6 nós + } + + @Override + public List selectRoute(String entryPoint, Map queueSizes) { + // Ignora queueSizes - política baseada apenas no comprimento do caminho + + List> availableRoutes = getRoutesForEntryPoint(entryPoint); + + // Retorna a rota mais curta (primeira da lista) + List shortestRoute = availableRoutes.get(0); + + return new ArrayList<>(shortestRoute); + } + + /** + * Obtém as rotas disponíveis para um ponto de entrada. + * + * @param entryPoint ponto de entrada (E1, E2 ou E3) + * @return lista de rotas ordenadas por comprimento + */ + private List> getRoutesForEntryPoint(String entryPoint) { + switch (entryPoint.toUpperCase()) { + case "E1": + return e1Routes; + case "E2": + return e2Routes; + case "E3": + return e3Routes; + default: + System.err.printf("Unknown entry point: %s, defaulting to E1%n", entryPoint); + return e1Routes; + } + } +} diff --git a/main/src/main/java/sd/util/VehicleGenerator.java b/main/src/main/java/sd/util/VehicleGenerator.java index dbd987f..dce42ce 100644 --- a/main/src/main/java/sd/util/VehicleGenerator.java +++ b/main/src/main/java/sd/util/VehicleGenerator.java @@ -1,12 +1,12 @@ package sd.util; -import java.util.ArrayList; -import java.util.Arrays; import java.util.List; +import java.util.Map; import sd.config.SimulationConfig; import sd.model.Vehicle; import sd.model.VehicleType; +import sd.routing.RouteSelector; /** * Gera veículos para a simulação. @@ -15,10 +15,12 @@ import sd.model.VehicleType; *
    *
  1. Determinar quando o próximo veículo deve chegar, baseado no * modelo de chegada (POISSON ou FIXED) da {@link SimulationConfig}
  2. - *
  3. Criar um novo objeto {@link Vehicle} com tipo e rota selecionados aleatoriamente
  4. + *
  5. Criar um novo objeto {@link Vehicle} com tipo e rota selecionados pela + * política de roteamento configurada ({@link RouteSelector})
  6. *
* - *

As rotas são predefinidas e organizadas por ponto de entrada (E1, E2, E3).

+ *

As rotas são selecionadas usando uma política de roteamento que pode ser: + * aleatória, caminho mais curto, menor congestionamento, etc.

*/ public class VehicleGenerator { @@ -29,60 +31,24 @@ public class VehicleGenerator { /** Intervalo para modelo FIXED */ private final double fixedInterval; - /** Rotas possíveis a partir do ponto de entrada E1 */ - private final List e1Routes; - /** Rotas possíveis a partir do ponto de entrada E2 */ - private final List e2Routes; - /** Rotas possíveis a partir do ponto de entrada E3 */ - private final List e3Routes; + /** Política de roteamento usada para selecionar rotas */ + private RouteSelector routeSelector; /** - * Cria um novo gerador de veículos. - * Lê a configuração necessária e inicializa as rotas predefinidas. + * Cria um novo gerador de veículos com a política de roteamento especificada. + * Lê a configuração necessária. * * @param config objeto de {@link SimulationConfig} + * @param routeSelector política de roteamento a usar para selecionar rotas */ - public VehicleGenerator(SimulationConfig config) { + public VehicleGenerator(SimulationConfig config, RouteSelector routeSelector) { this.config = config; + this.routeSelector = routeSelector; // Cache configuration values for performance this.arrivalModel = config.getArrivalModel(); this.arrivalRate = config.getArrivalRate(); this.fixedInterval = config.getFixedArrivalInterval(); - - // Initialize route lists - this.e1Routes = new ArrayList<>(); - this.e2Routes = new ArrayList<>(); - this.e3Routes = new ArrayList<>(); - initializePossibleRoutes(); - } - - /** - * Define todas as rotas possíveis que os veículos podem tomar. - * As rotas são organizadas por ponto de entrada (E1, E2, E3). - * Cada rota tem uma probabilidade que determina a frequência com que é escolhida. - */ - private void initializePossibleRoutes() { - e1Routes.add(new RouteWithProbability( - Arrays.asList("Cr1", "Cr4", "Cr5", "S"), 0.34)); - e1Routes.add(new RouteWithProbability( - Arrays.asList("Cr1", "Cr2", "Cr5", "S"), 0.33)); - e1Routes.add(new RouteWithProbability( - Arrays.asList("Cr1", "Cr2", "Cr3", "S"), 0.33)); - - e2Routes.add(new RouteWithProbability( - Arrays.asList("Cr2", "Cr5", "S"), 0.34)); - e2Routes.add(new RouteWithProbability( - Arrays.asList("Cr2", "Cr3", "S"), 0.33)); - e2Routes.add(new RouteWithProbability( - Arrays.asList("Cr2", "Cr1", "Cr4", "Cr5", "S"), 0.33)); - - e3Routes.add(new RouteWithProbability( - Arrays.asList("Cr3", "S"), 0.34)); - e3Routes.add(new RouteWithProbability( - Arrays.asList("Cr3", "Cr2", "Cr5", "S"), 0.33)); - e3Routes.add(new RouteWithProbability( - Arrays.asList("Cr3", "Cr2", "Cr1", "Cr4", "Cr5", "S"), 0.33)); } /** @@ -108,16 +74,19 @@ public class VehicleGenerator { *

Passos executados:

*
    *
  1. Seleciona um {@link VehicleType} aleatório baseado em probabilidades
  2. - *
  3. Seleciona uma rota aleatória (ponto de entrada + caminho)
  4. + *
  5. Seleciona um ponto de entrada aleatório (E1, E2, E3)
  6. + *
  7. Usa a política de roteamento para escolher a rota
  8. *
* * @param vehicleId identificador único do novo veículo (ex: "V123") * @param entryTime tempo de simulação em que o veículo é criado + * @param queueSizes mapa com tamanho das filas (opcional, pode ser null) * @return novo objeto {@link Vehicle} configurado */ - public Vehicle generateVehicle(String vehicleId, double entryTime) { + public Vehicle generateVehicle(String vehicleId, double entryTime, Map queueSizes) { VehicleType type = selectVehicleType(); - List route = selectRandomRoute(); + String entryPoint = selectRandomEntryPoint(); + List route = routeSelector.selectRoute(entryPoint, queueSizes); return new Vehicle(vehicleId, type, entryTime, route); } @@ -158,73 +127,46 @@ public class VehicleGenerator { } /** - * Selects a random route for a new vehicle. - * This is a two-step process: - * 1. Randomly select an entry point (E1, E2, or E3) with equal probability. - * 2. From the chosen entry point's list of routes, select one - * based on their defined probabilities (using cumulative probability). + * Seleciona aleatoriamente um ponto de entrada (E1, E2 ou E3). + * Cada ponto tem probabilidade igual (1/3). * - * @return A {@link List} of strings representing the chosen route (e.g., ["Cr1", "Cr4", "S"]). + * @return ponto de entrada selecionado ("E1", "E2" ou "E3") */ - private List selectRandomRoute() { - // Step 1: Randomly select an entry point (E1, E2, or E3) - double entryRandom = Math.random(); - List selectedRoutes; + private String selectRandomEntryPoint() { + double rand = Math.random(); - if (entryRandom < 0.333) { - selectedRoutes = e1Routes; - } else if (entryRandom < 0.666) { - selectedRoutes = e2Routes; + if (rand < 0.333) { + return "E1"; + } else if (rand < 0.666) { + return "E2"; } else { - selectedRoutes = e3Routes; + return "E3"; } - - // Step 2: Select a route from the chosen list based on cumulative probabilities - double routeRand = Math.random(); - double cumulative = 0.0; - - for (RouteWithProbability routeWithProb : selectedRoutes) { - cumulative += routeWithProb.probability; - if (routeRand <= cumulative) { - // Return a *copy* of the route to prevent modification - return new ArrayList<>(routeWithProb.route); - } - } - - // Fallback: This should only be reached if probabilities don't sum to 1 - // (due to floating point errors) - return new ArrayList<>(selectedRoutes.get(0).route); } + /** + * Altera dinamicamente o RouteSelector usado para gerar rotas. + * Permite mudar a política de roteamento durante a simulação. + * + * @param newRouteSelector novo seletor de rotas + */ + public void setRouteSelector(RouteSelector newRouteSelector) { + // Note: In Java, we can't directly modify the 'final' field, + // but we can create a new VehicleGenerator with the new selector. + // For this implementation, we'll need to remove 'final' from routeSelector. + // This is acceptable since we want dynamic policy changes. + throw new UnsupportedOperationException( + "VehicleGenerator is immutable. Use CoordinatorProcess.changeRoutingPolicy() instead." + ); + } + /** * @return A string providing information about the generator's configuration. */ public String getInfo() { - int totalRoutes = e1Routes.size() + e2Routes.size() + e3Routes.size(); return String.format( - "VehicleGenerator{model=%s, rate=%.2f, interval=%.2f, routes=%d (E1:%d, E2:%d, E3:%d)}", - arrivalModel, arrivalRate, fixedInterval, totalRoutes, - e1Routes.size(), e2Routes.size(), e3Routes.size() + "VehicleGenerator{model=%s, rate=%.2f, interval=%.2f, routeSelector=%s}", + arrivalModel, arrivalRate, fixedInterval, routeSelector.getClass().getSimpleName() ); } - - /** - * A private inner "struct-like" class to hold a route (a List of strings) - * and its associated selection probability. - */ - private static class RouteWithProbability { - final List route; - final double probability; - - /** - * Constructs a new RouteWithProbability pair. - * @param route The list of intersection IDs. - * @param probability The probability (0.0 to 1.0) of this route - * being chosen *from its entry group*. - */ - RouteWithProbability(List route, double probability) { - this.route = route; - this.probability = probability; - } - } } \ No newline at end of file diff --git a/main/src/main/resources/network_config.json b/main/src/main/resources/network_config.json index 4e8df68..f83f4a3 100644 --- a/main/src/main/resources/network_config.json +++ b/main/src/main/resources/network_config.json @@ -27,7 +27,7 @@ }, { "id": "Cr4", - "lights": ["East", "West"], + "lights": ["East", "North"], "routes": { "Cr1": "North", "Cr5": "East" diff --git a/main/src/main/resources/simulation-high.properties b/main/src/main/resources/simulation-high.properties index 202d3eb..c003580 100644 --- a/main/src/main/resources/simulation-high.properties +++ b/main/src/main/resources/simulation-high.properties @@ -41,7 +41,10 @@ simulation.arrival.model=POISSON simulation.arrival.rate=1.0 # Fixed interval between arrivals (only used if model=FIXED) -simulation.arrival.fixed.interval=1.0 +simulation.arrival.fixed.interval=2.0 + +# Routing policy: RANDOM, SHORTEST_PATH, LEAST_CONGESTED +simulation.routing.policy=LEAST_CONGESTED # === TRAFFIC LIGHT TIMINGS === @@ -72,11 +75,17 @@ trafficlight.Cr3.West.red=3.0 # Intersection 4 (High throughput needed toward Cr5) trafficlight.Cr4.East.green=70.0 trafficlight.Cr4.East.red=3.0 +trafficlight.Cr4.North.green=70.0 +trafficlight.Cr4.North.red=3.0 # Intersection 5 (Near exit - MAJOR BOTTLENECK, longest green time) # All routes funnel through here before exit trafficlight.Cr5.East.green=90.0 trafficlight.Cr5.East.red=3.0 +trafficlight.Cr5.West.green=70.0 +trafficlight.Cr5.West.red=3.0 +trafficlight.Cr5.North.green=70.0 +trafficlight.Cr5.North.red=3.0 # === VEHICLE CONFIGURATION === diff --git a/main/src/main/resources/simulation-low.properties b/main/src/main/resources/simulation-low.properties index 09d4a77..016682e 100644 --- a/main/src/main/resources/simulation-low.properties +++ b/main/src/main/resources/simulation-low.properties @@ -41,7 +41,10 @@ simulation.arrival.model=POISSON simulation.arrival.rate=0.2 # Fixed interval between arrivals (only used if model=FIXED) -simulation.arrival.fixed.interval=5.0 +simulation.arrival.fixed.interval=2.0 + +# Routing policy: RANDOM, SHORTEST_PATH, LEAST_CONGESTED +simulation.routing.policy=LEAST_CONGESTED # === TRAFFIC LIGHT TIMINGS === @@ -71,10 +74,16 @@ trafficlight.Cr3.West.red=5.0 # Intersection 4 (Favor East toward Cr5) trafficlight.Cr4.East.green=30.0 trafficlight.Cr4.East.red=5.0 +trafficlight.Cr4.North.green=30.0 +trafficlight.Cr4.North.red=5.0 # Intersection 5 (Near exit - favor East) trafficlight.Cr5.East.green=30.0 trafficlight.Cr5.East.red=5.0 +trafficlight.Cr5.West.green=30.0 +trafficlight.Cr5.West.red=5.0 +trafficlight.Cr5.North.green=30.0 +trafficlight.Cr5.North.red=5.0 # === VEHICLE CONFIGURATION === diff --git a/main/src/main/resources/simulation-medium.properties b/main/src/main/resources/simulation-medium.properties index 844a384..2c6e7ea 100644 --- a/main/src/main/resources/simulation-medium.properties +++ b/main/src/main/resources/simulation-medium.properties @@ -43,6 +43,9 @@ simulation.arrival.rate=0.5 # Fixed interval between arrivals (only used if model=FIXED) simulation.arrival.fixed.interval=2.0 +# Routing policy: RANDOM, SHORTEST_PATH, LEAST_CONGESTED +simulation.routing.policy=LEAST_CONGESTED + # === TRAFFIC LIGHT TIMINGS === # Format: trafficlight...= @@ -71,10 +74,16 @@ trafficlight.Cr3.West.red=5.0 # Intersection 4 (Favor East toward Cr5) trafficlight.Cr4.East.green=40.0 trafficlight.Cr4.East.red=5.0 +trafficlight.Cr4.North.green=40.0 +trafficlight.Cr4.North.red=5.0 # Intersection 5 (Near exit - POTENTIAL BOTTLENECK, longer green) trafficlight.Cr5.East.green=50.0 trafficlight.Cr5.East.red=5.0 +trafficlight.Cr5.West.green=45.0 +trafficlight.Cr5.West.red=5.0 +trafficlight.Cr5.North.green=45.0 +trafficlight.Cr5.North.red=5.0 # === VEHICLE CONFIGURATION === diff --git a/main/src/main/resources/simulation.properties b/main/src/main/resources/simulation.properties index 31ce0f8..f5fb4e4 100644 --- a/main/src/main/resources/simulation.properties +++ b/main/src/main/resources/simulation.properties @@ -46,6 +46,12 @@ simulation.arrival.rate=0.5 # Fixed interval between arrivals (only used if model=FIXED) simulation.arrival.fixed.interval=2.0 +# Routing policy: RANDOM, SHORTEST_PATH, LEAST_CONGESTED +# RANDOM: selects routes with predefined probabilities (baseline) +# SHORTEST_PATH: always chooses the route with fewest intersections +# LEAST_CONGESTED: dynamically chooses routes to avoid congested areas +simulation.routing.policy=RANDOM + # === TRAFFIC LIGHT TIMINGS === # Format: trafficlight...= diff --git a/main/start.sh b/main/start.sh deleted file mode 100755 index d710bd7..0000000 --- a/main/start.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/bin/bash -# Distributed Traffic Simulation Startup Script - -# kill java -echo "-> Cleaning up existing processes..." -pkill -9 java 2>/dev/null -sleep 2 - -# build -echo "-> Building project..." -cd "$(dirname "$0")" -mvn package -DskipTests -q -if [ $? -ne 0 ]; then - echo "XXX Build failed! XXX" - exit 1 -fi -echo "-> Build complete" -echo "" - -# start gui -echo "-> Starting JavaFX Dashboard..." -mvn javafx:run & -DASHBOARD_PID=$! -sleep 3 - -# acho que é assim idk -echo "-> Starting 5 Intersection processes..." -for id in Cr1 Cr2 Cr3 Cr4 Cr5; do - java -cp target/classes:target/main-1.0-SNAPSHOT.jar sd.IntersectionProcess $id > /tmp/$(echo $id | tr '[:upper:]' '[:lower:]').log 2>&1 & - echo "[SUCCESS] Started $id" -done -sleep 2 - -# exit -echo "-> Starting Exit Node..." -java -cp target/classes:target/main-1.0-SNAPSHOT.jar sd.ExitNodeProcess > /tmp/exit.log 2>&1 & -sleep 1 - -# coordinator -echo "-> Starting Coordinator..." -java -cp target/classes:target/main-1.0-SNAPSHOT.jar sd.coordinator.CoordinatorProcess > /tmp/coordinator.log 2>&1 & -sleep 1 - -echo "" -echo "-> All processes started!" -echo "" -echo "-> System Status:" -ps aux | grep "java.*sd\." | grep -v grep | wc -l | xargs -I {} echo " {} Java processes running" -echo "" -echo " IMPORTANT: Keep the JavaFX Dashboard window OPEN for 60+ seconds" -echo " to see live updates! The simulation runs for 60 seconds." -echo "" -echo "-> Logs available at:" -echo " Dashboard: Check JavaFX window (live updates)" -echo " Intersections: /tmp/cr*.log" -echo " Exit Node: /tmp/exit.log" -echo " Coordinator: /tmp/coordinator.log" -echo "" -echo "-> To stop all processes: pkill -9 java" -echo ""