model LogisticsServiceProvider import "Provider.gaml" import "SupplyChain.gaml" import "TransferredStocks.gaml" import "Strategies.gaml" species LogisticsServiceProvider { int timeShifting <- rnd(23); int adoptedStrategy; SupplyChain supplyChain <- nil; list<int> timeToDeliver <- []; list<Warehouse> lvl1Warehouses <- []; // close warehouse list<Warehouse> lvl2Warehouses <- []; // large warehouse list<FinalDestinationManager> customers <- []; Provider provider; float cumulateCosts <- 0; float averageCosts <- 0; float threshold <- 0.3; float probaAnt <- 0.5; string costsPathStrategy <- one_of(['financial_costs','travel_time']);//'financial_costs';// init { adoptedStrategy <- one_of(possibleStrategies); provider <- one_of(Provider); ask provider { do addCustomer(myself); } timeToDeliver <- []; if(use_gs){ // Add a new node event for corresponding sender if(use_r1){ gs_add_node gs_sender_id:"actor" gs_node_id:name; } if(use_r2){ gs_add_node gs_sender_id:"neighborhood_all" gs_node_id:name; gs_add_node_attribute gs_sender_id:"neighborhood_all" gs_node_id:name gs_attribute_name:"type" gs_attribute_value:"logistic_provider"; } if(use_r5){ gs_add_node gs_sender_id:"neighborhood_logistic_provider" gs_node_id:name; } if(use_r7){ gs_add_node gs_sender_id:"neighborhood_logistic_final" gs_node_id:name; } } } reflex testRestockNeeded when: supplyChain != nil and (((time/3600.0) + timeShifting) mod nbStepsbetweenTRN) = 0.0 and (time/3600.0) > 0 { ask supplyChain.leafs { do recursiveTests([] as list<Order>); } } action updateSupplyChainProvider { // Select (or not) a new provider Provider LHP <- nil; Provider AntP <- nil; ask Provider { if(self.port = "LE HAVRE"){ LHP <- self; } else{ AntP <- self; } } bool update <- false; if(flip(probaAnt)){ if(AntP != provider){ ask provider { do lostCustomer(myself); } provider <- AntP; update <- true; ask provider { do addCustomer(myself); } } } else { if(LHP != provider){ ask provider { do lostCustomer(myself); } provider <- LHP; update <- true; ask provider { do addCustomer(myself); } } } // Do I need to update ? if(update and supplyChain != nil){ // Update the supply chain according to this new provider create SupplyChainElement number:1 returns:rt { supplyChain <- myself.supplyChain; building <- myself.provider; sons <- []; position <- 0; } SupplyChainElement newRoot <- rt[0]; int j <- 0; loop while: j<length(supplyChain.root.sons) { int i <- 0; loop while: i < length(supplyChain.root.sons[j].fathers) { if(supplyChain.root.sons[j].fathers[i].building = supplyChain.root.building){ supplyChain.root.sons[j].fathers[i] <- newRoot; newRoot.sons <- newRoot.sons + supplyChain.root.sons[j]; } i <- i + 1; } j <- j + 1; } supplyChain.root <- newRoot; } } /* * This method looks for the warehouse of his supply chain which need to be restocked */ action manageLostStock(AwaitingStock aws) { list<Warehouse> lw; if(aws.position = 1){ lw <- lvl2Warehouses; } else { lw <- lvl1Warehouses; } int i <- 0; bool notfound <- true; loop while: i < length(lw) and notfound { Warehouse w <- lw[i]; int j <- 0; loop while: j < length(w.stocks) and notfound { if(aws.stock.product = w.stocks[j].product and w.stocks[j].lp = self and aws.stock.fdm = w.stocks[j].fdm){ create Commodity number:1 returns:returnedAgent; Commodity commodity <- returnedAgent[0]; commodity.stock <- aws.stock; commodity.volume <- aws.stock.quantity; commodity.finalDestination <- w; commodity.stepOrderMade <- aws.stepOrderMade; aws.building.leavingCommodities <- aws.building.leavingCommodities + commodity; ask forwardingAgent { commodity.paths <- compute_shortest_path(aws.building, w, myself.costsPathStrategy, commodity);//'financial_costs'//travel_time } notfound <- false; } j <- j + 1; } i <- i + 1; } } /* * These methods are used to remove a customer and disconnect him of the supply chain */ /** * When a logistic provider loose a customer (a FinalDestinationManager) he must update the stock on its warehouses */ TransferredStocks lostCustomer(FinalDestinationManager fdm){ int k <- 0; bool notfound <- true; loop while: k < length(customers) and notfound { if(fdm = customers[k]){ remove index: k from: customers; notfound <- false; } else{ k <- k + 1; } } /* * Browse the warehouses to get the stocks to remove and the list of warehouses which could be deleted from the supply chain */ int i <- 0; list<Warehouse> uselessWarehouses <- []; TransferredStocks stocksRemoved; create TransferredStocks number: 1 returns: rts; stocksRemoved <- rts[0]; loop while: i < length(lvl1Warehouses) { int j <- 0; list<Stock> temp_stocks <- (lvl1Warehouses[i] as Warehouse).stocks; bool useless <- true; loop while: j < length(temp_stocks) { if(temp_stocks[j].fdm = fdm and temp_stocks[j].lp = self){ (lvl1Warehouses[i] as Warehouse).occupiedSurface <- (lvl1Warehouses[i] as Warehouse).occupiedSurface - temp_stocks[j].maxQuantity; stocksRemoved.stocksLvl1 <- stocksRemoved.stocksLvl1 + temp_stocks[j]; remove index: j from: (lvl1Warehouses[i] as Warehouse).stocks; } else { // if the current stock is managed by the current LP, but does not belong to the FDM, then it means that this warehouse is useful if(temp_stocks[j].lp = self){ useless <- false; } j <- j + 1; } } if(useless){ uselessWarehouses <- uselessWarehouses + lvl1Warehouses[i]; remove index: i from: lvl1Warehouses; } else { i <- i + 1; } } // I wanted to make a mathod in order to not duplicate the following code, unfortunately, I can't return both the list of useless warehouse and of stocks... // It is ugly... I know and I feel ashamed! i <- 0; loop while: i < length(lvl2Warehouses) { int j <- 0; list<Stock> temp_stocks <- (lvl2Warehouses[i] as Warehouse).stocks; bool useless <- true; loop while: j < length(temp_stocks) { if(temp_stocks[j].fdm = fdm and temp_stocks[j].lp = self){ (lvl2Warehouses[i] as Warehouse).occupiedSurface <- (lvl2Warehouses[i] as Warehouse).occupiedSurface - temp_stocks[j].maxQuantity; stocksRemoved.stocksLvl2 <- stocksRemoved.stocksLvl2 + temp_stocks[j]; remove index: j from: (lvl2Warehouses[i] as Warehouse).stocks; } else { // if the current stock is managed by the current LP, but does not belong to the FDM, then it means that this warehouse is useful if(temp_stocks[j].lp = self){ useless <- false; } j <- j + 1; } } if(useless){ uselessWarehouses <- uselessWarehouses + lvl2Warehouses[i]; remove index: i from: lvl2Warehouses; } else { i <- i + 1; } } /* * Delete the useless SupplyChainElement and the supply chain itself if there is no more customer */ list<SupplyChainElement> uselessSCE <- deleteUselessSCE(supplyChain.root, fdm, uselessWarehouses); loop while: 0 < length(uselessSCE) { ask uselessSCE[0] { if(building != myself.provider and building != fdm.building){ (building as RestockingBuilding).maxProcessOrdersCapacity <- (building as RestockingBuilding).maxProcessOrdersCapacity - 5; } do die; } remove index: 0 from: uselessSCE; } if(length(supplyChain.root.sons) = 0){ ask supplyChain.root { do die; } ask supplyChain { do die; } supplyChain <- nil; } return stocksRemoved; } /* * A recursive method which browse the supply chain and delete the useless supply chain elements from the leafs to the root */ list<SupplyChainElement> deleteUselessSCE(SupplyChainElement sce, FinalDestinationManager fdm, list<Warehouse> uselessWarehouses){ int i <- 0; list<SupplyChainElement> uselessSCE <- []; loop while: i < length(sce.sons) { uselessSCE <- uselessSCE + deleteUselessSCE(sce.sons[i], fdm, uselessWarehouses); if(uselessSCE contains sce.sons[i]){ remove index: i from: sce.sons; } else { i <- i + 1; } } if(sce.building = fdm.building){ remove sce from: sce.supplyChain.leafs; } if(uselessWarehouses contains sce.building or sce.building = fdm.building){ return uselessSCE + sce; } return uselessSCE; } /* * These methods are used to connect a new customer to the supply chain */ action connectRoot { // Build the root of this almost-tree create SupplyChainElement number:1 returns:rt { building <- myself.provider; (building as RestockingBuilding).maxProcessOrdersCapacity <- (building as RestockingBuilding).maxProcessOrdersCapacity + 5; sons <- []; position <- 0; } // and build the supply chain with this root create SupplyChain number:1 returns:sc { logisticsServiceProvider <- myself; root <- rt[0]; } supplyChain <- first(sc); first(rt).supplyChain <- supplyChain; if(use_gs){ if(use_r9){ gs_add_node_attribute gs_sender_id:"supply_chain" gs_node_id:provider.name gs_attribute_name:"type" gs_attribute_value:"provider"; } } } SupplyChainElement connectCustomer(FinalDestinationManager fdm) { create SupplyChainElement number:1 returns:fdmLeaf { self.building <- fdm.building; self.sons <- []; self.supplyChain <- myself.supplyChain; position <- 3; } supplyChain.leafs <- supplyChain.leafs + fdmLeaf[0]; if(use_gs){ if(use_r9){ gs_add_node_attribute gs_sender_id:"supply_chain" gs_node_id:fdm.name gs_attribute_name:"type" gs_attribute_value:"final_dest"; } } return fdmLeaf[0]; } SupplyChainElement connectLvl1Warehouse(FinalDestinationManager fdm, SupplyChainElement fdmLeaf, list<Stock> stocksLvl1) { // First we find an appropriate local warehouse Warehouse closeWarehouse <- findWarehouseLvl1(fdm, sizeOfStockLocalWarehouse); do initStock(closeWarehouse, fdm, stocksLvl1, sizeOfStockLocalWarehouse); SupplyChainElement sceCloseWarehouse <- nil; if(!(lvl1Warehouses contains closeWarehouse)){ lvl1Warehouses <- lvl1Warehouses + closeWarehouse; // We must create a SCE corresponding to this warehouse create SupplyChainElement number:1 returns:sceBuild { self.supplyChain <- myself.supplyChain; position <- 2; building <- closeWarehouse; sons <- [] + fdmLeaf; fathers <- []; } sceCloseWarehouse <- sceBuild[0]; if(use_gs){ if(use_r9){ gs_add_node_attribute gs_sender_id:"supply_chain" gs_node_id:sceCloseWarehouse.building.name gs_attribute_name:"type" gs_attribute_value:"close_warehouse"; } } } else{ // The selected warehouse exists already in the supply chain. We must find it bool found <- false; int i <- 0; loop while: i<length( (supplyChain.root as SupplyChainElement).sons) and sceCloseWarehouse = nil { int j <- 0; loop while: j<length( (supplyChain.root.sons[i] as SupplyChainElement).sons) and sceCloseWarehouse = nil { if(closeWarehouse = ((supplyChain.root.sons[i] as SupplyChainElement).sons[j] as SupplyChainElement).building){ sceCloseWarehouse <- (supplyChain.root.sons[i] as SupplyChainElement).sons[j]; } j <- j + 1; } i <- i + 1; } // We must update the fathers of the leaf and the sons of the close warehouse sceCloseWarehouse.sons <- sceCloseWarehouse.sons + fdmLeaf; } fdmLeaf.fathers <- [] + sceCloseWarehouse; ask sceCloseWarehouse { (building as RestockingBuilding).maxProcessOrdersCapacity <- (building as RestockingBuilding).maxProcessOrdersCapacity + 5; } if(use_gs){ if(use_r9){ gs_add_edge gs_sender_id:"supply_chain" gs_edge_id:(fdm.name + sceCloseWarehouse.building.name) gs_node_id_from:fdm.name gs_node_id_to:sceCloseWarehouse.building.name gs_is_directed:false; float p <- 0.0; using topology(road_network){ p <- (fdm.building.location distance_to sceCloseWarehouse.building.location); } gs_add_edge_attribute gs_sender_id:"supply_chain" gs_edge_id:(fdm.building.name + sceCloseWarehouse.building.name) gs_attribute_name:"length" gs_attribute_value:p; } } return sceCloseWarehouse; } action connectLvl2Warehouse(FinalDestinationManager fdm, SupplyChainElement sceCloseWarehouse, list<Stock> stocksLvl2){ // We try to find a father who has an appropriate surface SupplyChainElement sceLarge <- nil; bool found <- false; int i <- 0; loop while: i<length(supplyChain.root.sons) and !found { sceLarge <- supplyChain.root.sons[i]; if( ((sceLarge.building as Building).totalSurface - (sceLarge.building as Building).occupiedSurface ) >= ((fdm.building as Building).occupiedSurface * sizeOfStockLargeWarehouse) ){ found <- true; do initStock( (sceLarge.building as Warehouse), fdm, stocksLvl2, sizeOfStockLargeWarehouse); } i <- i + 1; } // If we have not found it in the large warehouses if(!found){ // we must create one SCE // we find an appropriate large warehouse Warehouse largeWarehouse <- findWarehouseLvl2(fdm, sizeOfStockLargeWarehouse); if(! (lvl2Warehouses contains largeWarehouse)){// Should always be true, isn't it? otherwise, we would have found a SCE... lvl2Warehouses <- lvl2Warehouses + largeWarehouse; } do initStock(largeWarehouse, fdm, stocksLvl2, sizeOfStockLargeWarehouse); // and create a SCE create SupplyChainElement number:1 returns:sceBuild { self.supplyChain <- myself.supplyChain; self.position <- 1; self.building <- largeWarehouse; self.sons <- []; self.fathers <- [] + myself.supplyChain.root; } sceLarge <- sceBuild[0]; supplyChain.root.sons <- supplyChain.root.sons + sceLarge; if(use_gs){ if(use_r9){ gs_add_node_attribute gs_sender_id:"supply_chain" gs_node_id:sceLarge.building.name gs_attribute_name:"type" gs_attribute_value:"large_warehouse"; gs_add_edge gs_sender_id:"supply_chain" gs_edge_id:(sceLarge.building.name + provider.name) gs_node_id_from:sceLarge.building.name gs_node_id_to:provider.name gs_is_directed:false; float p <- 0.0; using topology(road_network){ p <- (sceLarge.building.location distance_to provider.location); } gs_add_edge_attribute gs_sender_id:"supply_chain" gs_edge_id:(sceLarge.building.name + provider.name) gs_attribute_name:"length" gs_attribute_value:p; } } } // and then this father become the real father of this close warehouse found <- false; i <- 0; loop while: i<length(sceCloseWarehouse.fathers) and !found { if(sceCloseWarehouse.fathers[i] = sceLarge){ found <- true; } i <- i + 1; } if(!found){ sceCloseWarehouse.fathers <- sceCloseWarehouse.fathers + sceLarge; } found <- false; i <- 0; loop while: i<length(sceLarge.sons) and !found { if(sceLarge.sons[i] = sceCloseWarehouse){ found <- true; } i <- i + 1; } if(!found){ sceLarge.sons <- sceLarge.sons + sceCloseWarehouse; } ask sceLarge { (building as RestockingBuilding).maxProcessOrdersCapacity <- (building as RestockingBuilding).maxProcessOrdersCapacity + 5; } if(use_gs){ if(use_r9){ gs_add_edge gs_sender_id:"supply_chain" gs_edge_id:(sceCloseWarehouse.building.name + sceLarge.building.name) gs_node_id_from:sceCloseWarehouse.building.name gs_node_id_to:sceLarge.building.name gs_is_directed:false; float p <- 0.0; using topology(road_network){ p <- (sceCloseWarehouse.building.location distance_to sceLarge.building.location); } gs_add_edge_attribute gs_sender_id:"supply_chain" gs_edge_id:(sceCloseWarehouse.building.name + sceLarge.building.name) gs_attribute_name:"length" gs_attribute_value:p; } } } /** * When a logistic provider has a new customer, he needs to find a new supply chain. This method build it. */ action getNewCustomer(FinalDestinationManager fdm, list<Stock> stocksLvl1, list<Stock> stocksLvl2){ /* * Initiate the supply chain with just the provider as root */ if(supplyChain = nil){ do connectRoot; } /* * The new customer become a new leaf of the "almost-tree" supply chain. */ SupplyChainElement fdmLeaf <- connectCustomer(fdm); /* * connect this leaf to a close warehouse */ SupplyChainElement sceCloseWarehouse <- connectLvl1Warehouse(fdm, fdmLeaf, stocksLvl1); /* * Connect the close warehouse to the large warehouse */ do connectLvl2Warehouse(fdm, sceCloseWarehouse, stocksLvl2); customers <- customers + fdm; } /** * We assume that the warehouse have already a stock when we initialize a new supply chain */ action initStock(Warehouse warehouse, FinalDestinationManager f, list<Stock> stocks, int sizeOfStock){ if(stocks = nil or length(stocks) = 0){ loop stockFdm over: (f.building as Building).stocks { // We create the stock agent create Stock number:1 returns:s { self.product <- stockFdm.product; self.quantity <- rnd(stockFdm.maxQuantity * sizeOfStock); self.maxQuantity <- stockFdm.maxQuantity * sizeOfStock; self.status <- 0; self.fdm <- f; self.lp <- myself; self.building <- warehouse; } // and add it to the list of stocks in the warehouse warehouse.stocks <- warehouse.stocks + s[0]; // Finally we update the occupied surface warehouse.occupiedSurface <- warehouse.occupiedSurface + (s[0] as Stock).maxQuantity; } } else { loop stock over: stocks { warehouse.occupiedSurface <- warehouse.occupiedSurface + stock.maxQuantity; stock.fdm <- f; stock.lp <- self; stock.building <- warehouse; stock.status <- 0; } warehouse.stocks <- warehouse.stocks + stocks; } } /** * Return a warehouse of first level in the supply chain */ Warehouse findWarehouseLvl1(FinalDestinationManager fdm, int sizeOfStock){ Warehouse w <- nil; if(adoptedStrategy = 1){ w <- world.findWarehouseLvl1Strat1(fdm, sizeOfStock, lvl2Warehouses); } else if(adoptedStrategy = 2){ w <- world.findWarehouseLvl1Strat2(fdm, sizeOfStock, lvl1Warehouses, lvl2Warehouses); } else if(adoptedStrategy = 3){ w <- world.findWarehouseLvl1Strat3(fdm, sizeOfStock, lvl2Warehouses); } else if(adoptedStrategy = 4){ w <- world.findWarehouseLvl1Strat4(fdm, sizeOfStock, lvl2Warehouses); } return w; } /** * Return a warehouse of third level in the supply chain */ Warehouse findWarehouseLvl2(FinalDestinationManager fdm, int sizeOfStock){ Warehouse w <- nil; if(adoptedStrategy = 1){ w <- world.findWarehouseLvl2Strat1(fdm, sizeOfStock, lvl1Warehouses); } else if(adoptedStrategy = 2){ w <- world.findWarehouseLvl2Strat2(fdm, sizeOfStock, lvl1Warehouses, lvl2Warehouses); } else if(adoptedStrategy = 3){ w <- world.findWarehouseLvl2Strat3(fdm, sizeOfStock, lvl1Warehouses); } else if(adoptedStrategy = 4){ w <- world.findWarehouseLvl2Strat4(fdm, sizeOfStock, lvl1Warehouses); } return w; } aspect base { Provider LHP <- nil; Provider AntP <- nil; ask Provider { if(self.port = "LE HAVRE"){ LHP <- self; } else{ AntP <- self; } } if(AntP = provider){ draw shape+3°px color: rgb(14, 234, 2) ; //vert } else { draw shape+3°px color: rgb(12, 0, 236) ; // bleu } } aspect simple_base { draw shape+3°px color: rgb(66, 219, 108) ; //vert } }