public void Generar() { // Busco en el historial de viajes los que correspondan al mismo tipo de día TipoDeDia tipoDeDia = ObtenerTipoDeDia(_fechaAGenerar); List <DateTime> diasDelMismoTipo = ObtenerDiasDelMismoTipo(tipoDeDia); // Proceso cada recorrido individualmente List <RecorridoBE> recorridos = Recorrido.ListarTodos(); List <CalculoDeRecorrido> calculosDeRecorrido = new List <CalculoDeRecorrido>(); foreach (RecorridoBE recorrido in recorridos) { CalculoDeRecorrido calcRecorrido = new CalculoDeRecorrido { Recorrido = recorrido }; CalcularIntervalos(calcRecorrido, diasDelMismoTipo); GenerarViajesYPlanillas(calcRecorrido); calculosDeRecorrido.Add(calcRecorrido); } bool hayVehiculos = ChequearDisponibilidadDeVehiculos(out int vehiculosFaltantes); bool hayChoferes = ChequearDisponibilidadDeChoferes(out int choferesFaltantes); if (!hayVehiculos || !hayChoferes) { GenerarAlertaDeInsuficiencia(vehiculosFaltantes, choferesFaltantes); RecalcularFrecuencias(calculosDeRecorrido); } AsignarVehiculos(); DividirEnDosTurnos(); AsignarChoferes(); PlanillaHoraria.GuardarMultiples(_planillasGeneradas); }
private void GenerarViajesYPlanillas(CalculoDeRecorrido calcRecorrido) { // Una vez que termino con el cálculo de cada intervalo genero viajes // El primer viaje del día es a las 4 AM DateTime inicioProximoViaje = new DateTime(1, 1, 1, 4, 0, 0); RecorridoBE recorrido = calcRecorrido.Recorrido; int descansoChoferes = int.Parse(ConfigurationManager.AppSettings["descansoChoferes"]); foreach (int intervalo in calcRecorrido.FrecuenciaPorIntervalo.Keys) { while (inicioProximoViaje.Hour - inicioProximoViaje.Hour % 2 == intervalo) { ViajeBE viajeIda = new ViajeBE { HoraSalida = inicioProximoViaje, HoraEstimadaLlegada = inicioProximoViaje.Add(calcRecorrido.DuracionPorIntervalo[intervalo]), EsIda = true, TerminalOrigen = recorrido.TerminalInicio, TerminalDestino = recorrido.TerminalFin }; // Busco la planilla cuyo último viaje termine en la terminal de salida antes de que // salga este micro (más el descanso de los choferes) PlanillaHorariaBE planillaIda = _planillasGeneradas.Where(p => p.Recorrido == recorrido && p.Viajes.Last().TerminalDestino == recorrido.TerminalInicio && p.Viajes.Last().HoraEstimadaLlegada.AddMinutes(descansoChoferes) < inicioProximoViaje).FirstOrDefault(); if (planillaIda == null) { planillaIda = new PlanillaHorariaBE { Fecha = _fechaAGenerar, Recorrido = recorrido, Viajes = new List <ViajeBE>() }; _planillasGeneradas.Add(planillaIda); } planillaIda.Viajes.Add(viajeIda); // Hago lo mismo con el viaje de vuelta ViajeBE viajeVuelta = new ViajeBE { HoraSalida = inicioProximoViaje, HoraEstimadaLlegada = inicioProximoViaje.Add(calcRecorrido.DuracionPorIntervalo[intervalo]), EsIda = false, TerminalOrigen = recorrido.TerminalFin, TerminalDestino = recorrido.TerminalInicio, }; PlanillaHorariaBE planillaVuelta = _planillasGeneradas.Where(p => p.Recorrido == recorrido && p.Viajes.Last().TerminalDestino == recorrido.TerminalFin && p.Viajes.Last().HoraEstimadaLlegada.AddMinutes(descansoChoferes) < inicioProximoViaje).FirstOrDefault(); if (planillaVuelta == null) { planillaVuelta = new PlanillaHorariaBE { Fecha = _fechaAGenerar, Recorrido = recorrido, Viajes = new List <ViajeBE>() }; _planillasGeneradas.Add(planillaVuelta); } planillaVuelta.Viajes.Add(viajeVuelta); inicioProximoViaje = inicioProximoViaje.AddMinutes(calcRecorrido.FrecuenciaPorIntervalo[intervalo]); } } }
private void CalcularIntervalos(CalculoDeRecorrido calcRecorrido, List <DateTime> dias) { List <PlanillaHorariaBE> planillas = PlanillaHoraria.ObtenerPlanilas(calcRecorrido.Recorrido, dias); calcRecorrido.FrecuenciaPorIntervalo = new Dictionary <int, int>(); calcRecorrido.DuracionPorIntervalo = new Dictionary <int, TimeSpan>(); // Agrupo las planillas por día var planillasPorDia = planillas.GroupBy(p => p.Fecha).Select(g => new { Fecha = g.Key, Planillas = g.ToList() }).OrderBy(x => x.Fecha).ToList(); int diasDeCalculo = planillasPorDia.Count; // Un intervalo representa un período de 2 hs. El intervalo 4 es el período de 4 a 6 hs por ej. // Empieza en 4 porque el primer viaje del día siempre es a las 4 AM for (int intervalo = 4; intervalo <= 22; intervalo += 2) { decimal sumaDeIndices = 0; TimeSpan sumaDuracion = new TimeSpan(); int frecuenciaDiaAnterior = -1; int cantidadViajes = 0; for (int dia = 0; dia < diasDeCalculo; dia++) { // Viajes de este intervalo de cada planilla List <ViajeBE> viajesDeIntervalo = planillasPorDia[dia].Planillas.SelectMany(p => p.Viajes) .Where(v => v.HoraSalida.Hour - v.HoraSalida.Hour % 2 == intervalo).OrderBy(v => v.HoraSalida).ToList(); cantidadViajes += viajesDeIntervalo.Count; decimal sumaCompletitud = 0; for (int iViaje = 0; iViaje < viajesDeIntervalo.Count; iViaje++) { ViajeBE viaje = viajesDeIntervalo[iViaje]; // Completitud del viaje if (viaje.Completitud == CompletitudViaje.Vacio) { sumaCompletitud += 0.5M; } else if (viaje.Completitud == CompletitudViaje.Moderado || viaje.Completitud == CompletitudViaje.Nulo) { sumaCompletitud += 1; } else if (viaje.Completitud == CompletitudViaje.Lleno) { sumaCompletitud += 1.5M; } // Duración del viaje if (viaje.HoraRealLlegada.HasValue && viaje.HoraRealLlegada.Value < viaje.HoraSalida) { // Este es el caso cuando llega al día siguiente viaje.HoraRealLlegada = viaje.HoraRealLlegada.Value.AddDays(1); } DateTime horaLlegada = viaje.HoraRealLlegada ?? viaje.HoraEstimadaLlegada; sumaDuracion = sumaDuracion.Add(horaLlegada - viaje.HoraSalida); // Frecuencia del día anterior if (dia == 0 && frecuenciaDiaAnterior < 0) { DateTime horaProximoViaje = viajesDeIntervalo.Where(v => v.HoraSalida > viaje.HoraSalida) .OrderBy(v => v.HoraSalida).Select(v => v.HoraSalida).FirstOrDefault(); if (horaProximoViaje != default) { frecuenciaDiaAnterior = (int)(horaProximoViaje - viaje.HoraSalida).TotalMinutes; } else if (intervalo <= 20) { // No hay proximo viaje en este intervalo, busco en el siguiente horaProximoViaje = planillasPorDia[dia].Planillas.SelectMany(p => p.Viajes) .Where(v => v.HoraSalida.Hour - v.HoraSalida.Hour % 2 == (intervalo + 2)) .OrderBy(v => v.HoraSalida).Select(v => v.HoraSalida).FirstOrDefault(); frecuenciaDiaAnterior = (int)(horaProximoViaje - viaje.HoraSalida).TotalMinutes; } else { // No hay siguiente intervalo, tomo la frecuencia respecto al anterior. DateTime horaViajeAnterior = planillasPorDia[dia].Planillas .SelectMany(p => p.Viajes) .Where(v => v.HoraSalida.Hour - v.HoraSalida.Hour % 2 == (intervalo - 2)) .OrderBy(v => v.HoraSalida).Select(v => v.HoraSalida).LastOrDefault(); frecuenciaDiaAnterior = (int)(viaje.HoraSalida - horaViajeAnterior).TotalMinutes; } } } decimal promedioCompletitudIntervalo = sumaCompletitud / viajesDeIntervalo.Count; decimal indiceDeAjuste = promedioCompletitudIntervalo * (diasDeCalculo - dia) / diasDeCalculo; sumaDeIndices += indiceDeAjuste; } int nuevaFrecuencia; TimeSpan nuevaDuracion; if (diasDeCalculo > 0 && cantidadViajes > 0) { decimal divisorDeFrecuencia = sumaDeIndices / diasDeCalculo; nuevaFrecuencia = (int)Math.Round(frecuenciaDiaAnterior / divisorDeFrecuencia); nuevaFrecuencia = Math.Min(nuevaFrecuencia, 60); // No puede haber una frecuencia mayor a 60 minutos nuevaDuracion = new TimeSpan(sumaDuracion.Ticks / cantidadViajes); // Redondeo al minuto más cercano nuevaDuracion = TimeSpan.FromMinutes(Math.Round(nuevaDuracion.TotalMinutes)); } else { // Si no hay datos, tomo la frecuencia y duración por defecto nuevaFrecuencia = int.Parse(ConfigurationManager.AppSettings["frecuenciaDefault"]); nuevaDuracion = new TimeSpan(0, int.Parse(ConfigurationManager.AppSettings["duracionDefault"]), 0); } calcRecorrido.FrecuenciaPorIntervalo.Add(intervalo, nuevaFrecuencia); calcRecorrido.DuracionPorIntervalo.Add(intervalo, nuevaDuracion); } }