public IHttpActionResult GetVirtualTrucks(string query = null) { try { List <Location> Items = Utilities.GetProductionPlanByCountry(query); List <ShipmentGroup> ShipmentGroups = Utilities.GetShipmentGroups(); List <VirtualTruck> Trucks = new List <VirtualTruck>(); if (Items.Any()) { ProductMachineEfficiencyKeeper EfficiencyKeeper = new ProductMachineEfficiencyKeeper(); EfficiencyKeeper.Items = Utilities.GetProductMachineEfficiencies(); //let's create first truck VirtualTruck currTruck = null; int counter = 0; foreach (Location currLoc in Items.OrderBy(i => i.TotalPallets)) { Logger.Info("Lokacja: {loc}", currLoc.L); //let's plan trucks starting from those locations, that have the fewest pallets if (currTruck != null) { //location has changed. each location to have separate truck for now Logger.Info("Dodaje samochód do lokacji: {L}, palet: {pal}", currTruck.L, currTruck.TotalPallets); currTruck.Compose(); Trucks.Add(currTruck); currTruck = null; } foreach (ProductionPlanItem p in currLoc.Parts.OrderBy(p => p.END_DATE)) { counter = 0; while (p.PAL > 0.9 && counter < 10) { Logger.Info("Operacja: {op}, przejście: {counter}", p.OPERATION_NR, counter); //while this order hasn't been consumed if (currTruck != null) { if (currTruck.Pallets2Full == 0) { //we can't add any pallet to current truck Logger.Info("Dodaje samochód do lokacji: {L}, palet: {pal}", currTruck.L, currTruck.TotalPallets); currTruck.Compose(); Trucks.Add(currTruck); currTruck = null; } } if (currTruck == null) { //we have to create new truck Logger.Info("Nowe auto do: {L}", p.LOCATION); currTruck = new VirtualTruck(); currTruck.L = p.LOCATION; } double palletCount = p.QUANTITY / p.PAL; ProductionPlanItem pi = new ProductionPlanItem(); pi = p.CloneJson(); Logger.Info("Ilość palet w operacji: {op}, pozostało na aucie: {rem}", p.PAL, currTruck.Pallets2Full); if (p.PAL < currTruck.Pallets2Full) { currTruck.Parts.Add(pi); currTruck.TotalPallets += p.PAL; p.PAL = 0; p.QUANTITY = 0; } else { //part of this operation will go to current truck and part will go to next truck pi.PAL = currTruck.Pallets2Full; pi.QUANTITY = Convert.ToInt64(pi.PAL * palletCount); currTruck.TotalPallets += pi.PAL; currTruck.Parts.Add(pi); //as we don't consume the whole operation, //we must adjust the REMAINING & CONSUMED parts (stop date, quantity, etc) p.PAL -= pi.PAL; //subtract pallets you've taken p.QUANTITY -= pi.QUANTITY; long?minutesTaken = EfficiencyKeeper.Amount2Minutes(pi.MACHINE_ID, pi.PRODUCT_ID, pi.QUANTITY); if (minutesTaken != null) { //we have the efficiency set in MES pi.STOP_DATE = pi.START_DATE.AddMinutes((double)minutesTaken); p.START_DATE = pi.STOP_DATE; //stop date of this part is beginning of next part } } counter++; } } } return(Ok(Trucks)); } else { return(NotFound()); } } catch (Exception ex) { Logger.Error("GetProductionPlanByDestinations: Błąd. Szczegóły: {Message}", ex.ToString()); return(InternalServerError(ex)); } }
public static List <Location> GetProductionPlanByCountry(string query = null) { try { List <Location> Locations = new List <Location>(); List <ProductionPlanItem> Items = GetProductionPlan(query); if (Items.Any()) { List <DividerKeeper> Dividers = new List <DividerKeeper>(); List <DividerItem> DefaultDestinations = Utilities.GetDefaultDestinations(); DateTime start = Items.Min(i => i.START_DATE); DateTime stop = Items.Max(i => i.STOP_DATE); TimeSpan span = stop - start; string ProductIds = string.Join(",", Items.Select(i => i.PRODUCT_ID).ToList().Distinct()); ProductMachineEfficiencyKeeper EfficiencyKeeper = new ProductMachineEfficiencyKeeper(); EfficiencyKeeper.Items = Utilities.GetProductMachineEfficiencies(ProductIds); List <Session> Sessions = new List <Session>(); List <ShipmentGroup> shipmentGroups = Utilities.GetShipmentGroups(); foreach (ProductionPlanItem i in Items) { //for each operation //get how to allocate it //What week is this? //info from session dates rather than from operation dates if (!Sessions.Any(s => s.SCHEDULING_ID == i.SCHEDULING_ID)) { //we need to create the session and calculate its week/year Session s = new Session(); s.SCHEDULING_ID = i.SCHEDULING_ID; s.BEGIN_DATE = i.BEGIN_DATE; s.END_DATE = i.END_DATE; s.CalcualatePeriod(); Sessions.Add(s); } i.WEEK = Sessions.FirstOrDefault(s => s.SCHEDULING_ID == i.SCHEDULING_ID).Week; i.YEAR = Sessions.FirstOrDefault(s => s.SCHEDULING_ID == i.SCHEDULING_ID).Year; if (!Dividers.Any(d => d.Week == i.WEEK && d.Year == i.YEAR)) { //create it DividerKeeper div = new DividerKeeper(); div.Week = i.WEEK; div.Year = i.YEAR; div.Items = Utilities.GetDivider(i.WEEK, i.YEAR); Dividers.Add(div); } DividerKeeper currDiv = Dividers.FirstOrDefault(d => d.Week == i.WEEK && d.Year == i.YEAR); double palletCount = i.QUANTITY / i.PAL; //number of pieces on pallet if (currDiv.Items.Any(x => x.ZfinIndex == i.PRODUCT_NR)) { } else { //this product hasn't been found in divider for week X //check futher weeks, maybe it's in week X+1 currDiv = null; foreach (DividerKeeper dk in Dividers.Where(d => (d.Week > i.WEEK && d.Year == i.YEAR) || (d.Week <i.WEEK && d.Year> i.YEAR))) { if (dk.Items.Any(y => y.ZfinIndex == i.PRODUCT_NR)) { //it's in divider for next week currDiv = dk; } } } if (currDiv != null) { foreach (LocationAmount la in currDiv.Items.FirstOrDefault(x => x.ZfinIndex == i.PRODUCT_NR).Locations) { if (i.QUANTITY > 0) { //if there's nothing left to allocate in this operation, go to next operation if (la.Amount > 0) { //there's still quantity to allocate if (!Locations.Any(l => l.L.Trim() == la.L.Trim())) { //we don't have this location started yet Location loc = new Location(); loc.L = la.L.Trim(); if (shipmentGroups.Any(s => s.Members.Any(m => m.L.Trim() == loc.L))) { loc.ShipmentGroupName = shipmentGroups.FirstOrDefault(s => s.Members.Any(m => m.L.Trim() == loc.L)).Name; } Locations.Add(loc); } Location currLoc = Locations.FirstOrDefault(l => l.L.Trim() == la.L.Trim()); ProductionPlanItem p = new ProductionPlanItem(); p = i.CloneJson(); if (la.Amount >= i.QUANTITY) { //this L needs more than operation quantity or all of it //take only operation quantity then la.Amount -= i.QUANTITY; //decrease the amount of allocation that remains to this L i.QUANTITY = 0; i.PAL = 0; } else { //there will remain some quantity for other L p.QUANTITY -= la.Amount; p.PAL = la.Amount / palletCount; la.Amount = 0; i.QUANTITY -= p.QUANTITY; i.PAL -= p.QUANTITY / palletCount; //as we don't consume the whole operation, //we must adjust the REMAINING & CONSUMED parts (stop date, quantity, etc) long?minutesTaken = EfficiencyKeeper.Amount2Minutes(i.MACHINE_ID, i.PRODUCT_ID, p.QUANTITY); if (minutesTaken != null) { //we have the efficiency set in MES p.STOP_DATE = p.START_DATE.AddMinutes((double)minutesTaken); i.START_DATE = p.STOP_DATE; //stop date of this part is beginning of next part } } p.LOCATION = la.L; p.DIVIDER_WEEK = currDiv.Week; p.DIVIDER_YEAR = currDiv.Year; currLoc.Parts.Add(p); // add this part to operations for this location } } } } else { //It's not divider-based product //take default allocation Location currLoc; if (DefaultDestinations.Any(d => d.ZfinIndex == i.PRODUCT_NR)) { LocationAmount la = DefaultDestinations.Where(d => d.ZfinIndex == i.PRODUCT_NR).FirstOrDefault().Locations.FirstOrDefault(); //default destination found if (!Locations.Any(l => l.L.Trim() == la.L.Trim())) { //we don't have this location started yet Location loc = new Location(); loc.L = la.L.Trim(); if (shipmentGroups.Any(s => s.Members.Any(m => m.L.Trim() == loc.L))) { loc.ShipmentGroupName = shipmentGroups.FirstOrDefault(s => s.Members.Any(m => m.L.Trim() == loc.L)).Name; } Locations.Add(loc); } currLoc = Locations.FirstOrDefault(l => l.L.Trim() == la.L.Trim()); } else { //default destination doesn't exist //add it to unknow collection if (!Locations.Any(l => l.L.Trim() == "LXXX")) { //we don't have this location started yet Location loc = new Location(); loc.L = "LXXX"; Locations.Add(loc); } currLoc = Locations.FirstOrDefault(l => l.L.Trim() == "LXXX"); } ProductionPlanItem p = new ProductionPlanItem(); p = i.CloneJson(); i.QUANTITY = 0; i.PAL = 0; p.LOCATION = currLoc.L; p.DIVIDER_WEEK = 0; // not divider-based p.DIVIDER_YEAR = 0; // not divider-based currLoc.Parts.Add(p); // add this part to operations for this location } } } foreach (Location l in Locations) { l.Compose(); } return(Locations); } catch (Exception ex) { Logger.Error("GetProductionPlanByDestinations: Błąd. Szczegóły: {Message}", ex.ToString()); return(null); } }