Skip to content
Snippets Groups Projects
LogisticsServiceProvider.gaml 19.2 KiB
Newer Older
model LogisticsServiceProvider

import "Provider.gaml"
import "SupplyChain.gaml"
import "TransferredStocks.gaml"
import "Strategies.gaml"

species LogisticsServiceProvider {
	SupplyChain supplyChain <- nil;
	list<int> timeToDeliver <- [];
	list<Warehouse> lvl1Warehouses <- []; // close warehouse
	list<Warehouse> lvl2Warehouses <- []; // large warehouse
	list<FinalDestinationManager> customers <- [];
	float cumulateCosts <- 0;
	float averageCosts <- 0;
	float probaAnt <- 0.5;
	string costsPathStrategy <- one_of(['financial_costs','travel_time']);//'financial_costs';//
		adoptedStrategy <- one_of(possibleStrategies);
		provider <- one_of(Provider);
		ask provider {
			do addCustomer(myself);
		}

		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) {
				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];
			}
			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

	/*
	 * 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
		 */
		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) {
Thibaut Démare's avatar
Thibaut Démare committed
				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
Thibaut Démare's avatar
Thibaut Démare committed
					if(temp_stocks[j].lp = self){
			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) {
Thibaut Démare's avatar
Thibaut Démare committed
				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
Thibaut Démare's avatar
Thibaut Démare committed
					if(temp_stocks[j].lp = self){
			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
		 */
Thibaut Démare's avatar
Thibaut Démare committed
		list<SupplyChainElement> uselessSCE <- deleteUselessSCE(supplyChain.root, fdm, uselessWarehouses);
Thibaut Démare's avatar
Thibaut Démare committed
			ask uselessSCE[0] {
				if(building != myself.provider and building != fdm.building){
					(building as RestockingBuilding).maxProcessOrdersCapacity <- (building as RestockingBuilding).maxProcessOrdersCapacity - 5;
		if(length(supplyChain.root.sons) = 0){
			ask supplyChain.root {
				do die;
Thibaut Démare's avatar
Thibaut Démare committed
	 * A recursive method which browse the supply chain and delete the useless supply chain elements from the leafs to the root
Thibaut Démare's avatar
Thibaut Démare committed
	list<SupplyChainElement> deleteUselessSCE(SupplyChainElement sce, FinalDestinationManager fdm, list<Warehouse> uselessWarehouses){
		int i <- 0;
		list<SupplyChainElement> uselessSCE <- [];
		loop while: i < length(sce.sons) {
Thibaut Démare's avatar
Thibaut Démare committed
			uselessSCE <- uselessSCE + deleteUselessSCE(sce.sons[i], fdm, uselessWarehouses);
			if(uselessSCE contains sce.sons[i]){
		if(sce.building = fdm.building){
			remove sce from: sce.supplyChain.leafs;
		}
		if(uselessWarehouses contains sce.building or sce.building = fdm.building){
Thibaut Démare's avatar
Thibaut Démare committed
			return uselessSCE + sce;
		}


	/*
	 * 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 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";
			}
		}
	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";
				}
			}
			// 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
Thibaut Démare's avatar
Thibaut Démare committed
			sceCloseWarehouse.sons <- sceCloseWarehouse.sons + fdmLeaf;
		fdmLeaf.fathers <- [] + 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;
	action connectLvl2Warehouse(FinalDestinationManager fdm, SupplyChainElement sceCloseWarehouse, list<Stock> stocksLvl2){
		// We try to find a father who has an appropriate surface
		SupplyChainElement sceLarge <- nil;
		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) ){
				do initStock( (sceLarge.building as Warehouse), fdm, stocksLvl2, sizeOfStockLargeWarehouse);
		// 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;
		}
			(building as RestockingBuilding).maxProcessOrdersCapacity <- (building as RestockingBuilding).maxProcessOrdersCapacity + 5;
				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;
Thibaut Démare's avatar
Thibaut Démare committed
	 * 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.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);
			w <- world.findWarehouseLvl1Strat2(fdm, sizeOfStock, lvl1Warehouses, lvl2Warehouses);
			w <- world.findWarehouseLvl1Strat3(fdm, sizeOfStock, lvl2Warehouses);
		}
		else if(adoptedStrategy = 4){
			w <- world.findWarehouseLvl1Strat4(fdm, sizeOfStock, lvl2Warehouses);
	 * 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);
			w <- world.findWarehouseLvl2Strat2(fdm, sizeOfStock, lvl1Warehouses, lvl2Warehouses);
			w <- world.findWarehouseLvl2Strat3(fdm, sizeOfStock, lvl1Warehouses);
		}
		else if(adoptedStrategy = 4){
			w <- world.findWarehouseLvl2Strat4(fdm, sizeOfStock, lvl1Warehouses);
Thibaut Démare's avatar
Thibaut Démare committed
	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
	}