// Пользователь запросил генерацию листов по введенным данным // TODO: обработать правильность заполнения и контроль ошибок private void BtnPOIGenerate_Click(object sender, RoutedEventArgs e) { // Создаем новый лист TripTicket tripTicket = new TripTicket(); // Создаем хранилище листов tripLists = new List <TripTicket>(); // Помещаем новый лист в хранилище tripLists.Add(tripTicket); // Генерируем с сохранением выходных данных в ntt NextTripTicket ntt = tripTicket.Generate(); // Записываем количество пройденных км в рамках этого листа int realDistance = tripTicket.TotalRealDistance; // ИНТЕРФЕЙС: // Отображаем расчетное количество км, которое можно пройти на введенном количестве топлива tbMustDistance.Text = tripTicket.TotalDistance.ToString(); // Будем продолжать генерацию новых листов пока она требуется (ntt не равно null) while (ntt != null) { tripTicket = new TripTicket(ntt.Liters, ntt.Date, ntt.NextIndex, ntt.OdometerEndDay); ntt = tripTicket.Generate(); // Суммируем пройденный путь realDistance += tripTicket.TotalRealDistance; tripLists.Add(tripTicket); } // Показывать первый лист из хранилища currentSheet = 0; // Вызвать обновление интерфейса ShowTrackListSheet(); // ИНТЕРФЕЙС: // Отобразисть суммарное количество пройденного пути tbFullDistance.Text = realDistance.ToString(); // Показания дометра на конец чека // с добавлением пройденного пути от дома и обратно tbOdometerEnd.Text = (int.Parse(tbOdometerStart.Text) + realDistance + ((tripLists.Count - 1) * CurrentOptions.Vehicles[CurrentOptions.SelectedVehicle].HomeWay * 2)).ToString(); }
private NextTripTicket GenerateTimeAndDate(List <POI> poi) { int i = 1; NextTripTicket ntt = null; BusinessDaysCalculator businessDays = MainWindow.Instance.GlobalBusinessDaysCalculator; // Стартовая дата в чеке DateTime startTime = receiptDate; //Определяем конец рабочего дня (попросту добавляем к указанной дате время) DateTime endDT = new DateTime(startTime.Year, startTime.Month, startTime.Day, endOfWorkDay.Hours, endOfWorkDay.Minutes, 0); foreach (POI p in poi) { if (i > 1) { startTime = startTime.AddMinutes(p.prev.toNextPOI.TotalMinutes); } if (p.next == null) { break; } //ПРЕДСТАВЛЕНИЕ ДАТЫ //Конвертация даты в строку и запись в объект. (Можно делать это уже при выгрузки в XLSX p.Date = startTime.ToShortDateString(); p.FullDate = startTime; startTime = startTime.AddMinutes(random.Next(0, pause + 1)); //Добавляем задержку в точке назначения p.timeDeparture = startTime; p.timeArrive = startTime.Add(p.toNextPOI); //Проверяем укладываемся ли в рабочий день if (p.next != null) { //Если ехать в следующую точку, то надо проверить а хватит ли потом времени в рамках рабочего дня if ((startTime.AddMinutes(p.next.toNextPOI.TotalMinutes * 2) > endDT) && !p.address.IsBase) { DateTime checkDay; //Если не хватит то переключаем день на следующий do { //Меняем день до тех пор пока он не станет следующим РАБОЧИМ днем startTime = startTime.AddDays(1); checkDay = new DateTime(startTime.Year, startTime.Month, startTime.Day); } while (((startTime.DayOfWeek == DayOfWeek.Saturday || startTime.DayOfWeek == DayOfWeek.Sunday) && (businessDays.IsBusinessDay(checkDay) != 1)) || // если это выходной и не является принудительно рабочим businessDays.IsBusinessDay(checkDay) == 2); // если это любой другой день, но он объявлен нерабочим //Переводим стартовое время на начало рабочего дня startTime = new DateTime(startTime.Year, startTime.Month, startTime.Day, startOfWorkDay.Hours, startOfWorkDay.Minutes, 0); //Определяем конец рабочего дня endDT = new DateTime(startTime.Year, startTime.Month, startTime.Day, endOfWorkDay.Hours, endOfWorkDay.Minutes, 0); //startTime = startOfWorkDay; FuelResidude = Math.Round(p.FuelResidude); // Если нужно создавать новый трип лист каждый раз когда закончился день // и еще достаточно топлива, то if (MainWindow.DIVIDE_TRIPTICKETS && FuelResidude > fuelLost) { // Записываем данные для переноса на след день ntt = new NextTripTicket() { NextIndex = Id + 1, // Id следующего путевого листа Date = startTime, //Дата с которой начнется след лист Liters = p.FuelResidude, //Остаток топлива с которым начнется день NumberPOI = i //Количество уже пройденных точек (нужно для удаление лишних) }; return(ntt); } i = 0; } i++; } } return(null); }
public NextTripTicket Generate() { //Вычисление какой расход. Зимний или Летний double gasRate = gasRateWinter; if ((receiptDate.Month >= 4 && receiptDate.Month <= 9) || (receiptDate.Month == 3 && receiptDate.Month >= 15)) { gasRate = gasRateSummer; } TotalDistance = GetDistance(gasRate, gasLiters); int distance = 0; //счетчик пройденного пути int index = 0; POI start = new POI() { address = pool[0] }; //TODO: подразумевается, что база имеет индекс 0, а что если это не так!? index++; start.Id = index; poi.Add(start); //<--------------------------------------------------------------------------------------------- Добавляем первую точку. Это база! POI prev = start; //предыдущая точка для следующей найденной POI curr; //текущая точка. сюда будт получено значение в цикле (раз за разом) bool nowBase = true; //изначально мы на базе //цикл генерирует первичный маршрут, случайным образом //пока пройденный путь меньше общего пройденного пути по расходу/чеку, цикл будет добавлять точки и прибавлять //дистанцию до точек в счетчик пройденного пути while (distance <= TotalDistance - inaccuracy) { //с учетом погрешности int min = 0; if (nowBase) // Это чтобы находясь на базе случайно не поехать опять на базу { min++; } int r = random.Next(min, pool.Count()); //случайный индекс в диапазоне листа адресов (пула) //Если мы не на базе, а нам нужно каждый раз возвращаться на базу, то след адрес у нас БАЗА if (!nowBase && backToBase) { r = 0; //TODO: тут я подразумеваю, что база имеет у нас индекс 0 в листе адресов //TODO: а что если это не так? } Address adr = pool[r]; //получаем адрес по индексу int poiDistance = adr.Distance; //Если мы не на базе, а нам туда нужно каждый раз возвращаться, то дистанция будет равной //пройденной перед этим if (!nowBase && backToBase) { poiDistance = prev.address.Distance; //System.out.println("Предыдущий адрес: " + prev.address.getName()); } //System.out.println("Добавляем: "+poiDistance); //проверяем не выйдим ли мы за рамки // необходимого пути с учетом допустимой погрешности // либо мы не на базе, а нам туда каждый раз надо возвращаться, тогда у нас просто нет выбора if ((poiDistance + distance <= TotalDistance + inaccuracy) || (!nowBase && backToBase)) { distance += poiDistance; //добавляем пройденную дистанцию //Создаем точку для листа с найденным адресом, предыдущей точкой, а следующая точка пока null //Если - это не последняя точка то она будет присвоена в след витке. curr = new POI { address = adr, prev = prev }; //(adr, prev, null); index++; curr.Id = index; poi.Add(curr); if (backToBase) { //Если мы каждый раз возвращаемся на базу, то nowBase = !nowBase; //меняем значение база ли это на противоположное } //Если текущая точка не является первой (есть предыдущая), то //говорим предыдущей точке, что следующая для нее - это текущая int prevDist = 0; if (prev != null) { //В предыдущую точку добавляем дистанцию до текущей точки if (curr.address.IsBase) { //Если теущая точка база, то дистанцию до нее такаяже как и в предыдущую prevDist = prev.address.Distance; } else { prevDist = curr.address.Distance; } //Присвиваем в прыдущую точку дистанцию до текущей prev.distToNext = prevDist; //Записываем остаток топлива gasLiters = gasLiters - (gasRate * prevDist / 100); prev.FuelResidude = gasLiters; //Console.WriteLine("Осталось топилва: "+gasLiters); //Рассчитываем время из предыдущей точки в текущую, на основе дистанции <-----------------------------ВРЕМЯ!!!!! //TimeSpan timeInMinutes = GetTravelTime(averageSpeed, prevDist); prev.toNextPOI = GetTravelTime(averageSpeed, prevDist); //.setMinutesFromMidnight(timeInMinutes); //записываем в предыдущую точку время затраченной для //достижения текущей точки //В прыдыдущей точке делаем ссылку на текущую в переменную следующая. prev.next = curr; //System.out.println("Предыдущая: "+prev.address.getName()); } //TODO: Где то тут надо сгенерировать дату в точку и время //Для следующего витка цикла в предыдущую точку присваиваем ссылку на текущую prev = curr; usedPOI.Add(adr); //добавляем использованный адрес в соотв лист } //TODO: не совсем уверен нужно ли так делать: Я удаляю провернную точку из пула даже если она не подошла, //чтобы не зациклиться до бесконечности if (r != 0) { //Если адрес не база, то удаляем pool.Remove(pool[r]); } //После удаления я проверяю есть ли в пуле еще точки и если их нет, то цикл я прерываю, //т.к. не хватает точек достроить маршрутный лист if (pool.Count <= 1) { //1 - потому, что в конечном итоге база то останется //System.out.println("Перебрали все значение, но подходящего нет! Маршрутный лист недостроен."); break; } } //КОНЕЦ ЦИКЛА TotalRealDistance = distance; // По уже созданному маршруту генерируются даты-время пути. // Если требуется создавать один маршрутный на один день, // И если в один день уложиться не удалось, то // ntt веренется не null // А это значит что по данным ntt будет сгенерирован следующий лист на след день // Поэтому: NextTripTicket ntt = GenerateTimeAndDate(poi); // В пробег начального дня прибавляется путь до дома // ПЛЮС путь ОТ дома (чтоб туда обратно получилось) if (Id != 0) { OdometerStartDay = lastListOdo + homeWay * 2; } if (ntt != null) { TotalRealDistance = 0; // Обнуляем пройденный путь в рамках одного листа poi.RemoveRange(ntt.NumberPOI, poi.Count - ntt.NumberPOI); // Удаляем точки которые не будут использоваться данным листом // Пробегаемся по всем точкам чтобы... foreach (POI p in poi) { // ... суммировать весь пройденный путь только по этим точкам TotalRealDistance += p.distToNext; } ntt.OdometerEndDay = OdometerStartDay + TotalRealDistance; //return ntt; <Возвращаем ниже>// вернем данные для генерации след листа // таким образом, код который вызвал генерацию будет знать что // генерация была ограничена одинм днем и весь бензин еще не использован // и генерация должна быть продолжена с использованием выходных данных ntt } OdometerEndDay = OdometerStartDay + TotalRealDistance; TimeSpan randTime = new TimeSpan(0, random.Next(2, 15), 0); // Время Выезда по путевому StartTime = poi[0].timeDeparture - randTime; randTime = new TimeSpan(0, random.Next(2, 15), 0); int k = poi.Count - 1; // TODO: Это костыль: // Иногда последняя точка путая и время у нее равно нулю. Поэтому вернем индекс предпоследней if (poi[k].timeArrive == new DateTime(1, 1, 1, 0, 0, 0)) { k--; } // Время приезда по путевому EndTime = poi[k].timeArrive + randTime; return(ntt); // если ntt вернулось null то генерация след листа не требуется. }