Newer
Older
model LogisticsServiceProvider
import "Provider.gaml"
import "SupplyChain.gaml"
import "TransferredStocks.gaml"
import "Strategies.gaml"
species LogisticsServiceProvider {
Thibaut Démare
committed
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;
string costsPathStrategy <- one_of(['financial_costs','travel_time']);//'financial_costs';//
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>);
Thibaut Démare
committed
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
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];
j <- j + 1;
}
supplyChain.root <- newRoot;
}
}
Thibaut Démare
committed
/*
* 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;
}
Thibaut Démare
committed
/*
* 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
*/
Thibaut Démare
committed
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;
}
}
Thibaut Démare
committed
/*
* Browse the warehouses to get the stocks to remove and the list of warehouses which could be deleted from the supply chain
*/
Thibaut Démare
committed
list<Warehouse> uselessWarehouses <- [];
Thibaut Démare
committed
TransferredStocks stocksRemoved;
create TransferredStocks number: 1 returns: rts;
stocksRemoved <- rts[0];
Thibaut Démare
committed
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) {
(lvl1Warehouses[i] as Warehouse).occupiedSurface <- (lvl1Warehouses[i] as Warehouse).occupiedSurface - temp_stocks[j].maxQuantity;
Thibaut Démare
committed
stocksRemoved.stocksLvl1 <- stocksRemoved.stocksLvl1 + temp_stocks[j];
Thibaut Démare
committed
remove index: j from: (lvl1Warehouses[i] as Warehouse).stocks;
Thibaut Démare
committed
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
committed
useless <- false;
}
j <- j + 1;
Thibaut Démare
committed
if(useless){
uselessWarehouses <- uselessWarehouses + lvl1Warehouses[i];
remove index: i from: lvl1Warehouses;
}
else {
i <- i + 1;
}
Thibaut Démare
committed
// 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) {
(lvl2Warehouses[i] as Warehouse).occupiedSurface <- (lvl2Warehouses[i] as Warehouse).occupiedSurface - temp_stocks[j].maxQuantity;
Thibaut Démare
committed
stocksRemoved.stocksLvl2 <- stocksRemoved.stocksLvl2 + temp_stocks[j];
Thibaut Démare
committed
remove index: j from: (lvl2Warehouses[i] as Warehouse).stocks;
Thibaut Démare
committed
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
committed
useless <- false;
Thibaut Démare
committed
j <- j + 1;
Thibaut Démare
committed
if(useless){
uselessWarehouses <- uselessWarehouses + lvl2Warehouses[i];
remove index: i from: lvl2Warehouses;
Thibaut Démare
committed
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);
Thibaut Démare
committed
loop while: 0 < length(uselessSCE) {
if(building != myself.provider and building != fdm.building){
Thibaut Démare
committed
(building as RestockingBuilding).maxProcessOrdersCapacity <- (building as RestockingBuilding).maxProcessOrdersCapacity - 5;
Thibaut Démare
committed
remove index: 0 from: uselessSCE;
Thibaut Démare
committed
if(length(supplyChain.root.sons) = 0){
ask supplyChain.root {
do die;
Thibaut Démare
committed
ask supplyChain {
do die;
}
supplyChain <- nil;
Thibaut Démare
committed
return stocksRemoved;
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
list<SupplyChainElement> deleteUselessSCE(SupplyChainElement sce, FinalDestinationManager fdm, list<Warehouse> uselessWarehouses){
Thibaut Démare
committed
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]){
Thibaut Démare
committed
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){
Thibaut Démare
committed
return uselessSCE;
Thibaut Démare
committed
/*
* These methods are used to connect a new customer to the supply chain
*/
Thibaut Démare
committed
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;
Thibaut Démare
committed
sons <- [];
position <- 0;
}
// and build the supply chain with this root
create SupplyChain number:1 returns:sc {
logisticsServiceProvider <- myself;
Thibaut Démare
committed
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";
Thibaut Démare
committed
}
Thibaut Démare
committed
}
Thibaut Démare
committed
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];
Thibaut Démare
committed
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";
}
}
Thibaut Démare
committed
return fdmLeaf[0];
}
Thibaut Démare
committed
SupplyChainElement connectLvl1Warehouse(FinalDestinationManager fdm, SupplyChainElement fdmLeaf, list<Stock> stocksLvl1) {
// First we find an appropriate local warehouse
Warehouse closeWarehouse <- findWarehouseLvl1(fdm, sizeOfStockLocalWarehouse);
Thibaut Démare
committed
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];
Thibaut Démare
committed
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
fdmLeaf.fathers <- [] + sceCloseWarehouse;
ask sceCloseWarehouse {
Thibaut Démare
committed
(building as RestockingBuilding).maxProcessOrdersCapacity <- (building as RestockingBuilding).maxProcessOrdersCapacity + 5;
}
Thibaut Démare
committed
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;
Thibaut Démare
committed
}
}
Thibaut Démare
committed
return sceCloseWarehouse;
}
Thibaut Démare
committed
action connectLvl2Warehouse(FinalDestinationManager fdm, SupplyChainElement sceCloseWarehouse, list<Stock> stocksLvl2){
// We try to find a father who has an appropriate surface
SupplyChainElement sceLarge <- nil;
Thibaut Démare
committed
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) ){
Thibaut Démare
committed
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
Thibaut Démare
committed
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;
Thibaut Démare
committed
}
Thibaut Démare
committed
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;
Thibaut Démare
committed
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;
Thibaut Démare
committed
}
}
// 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 {
Thibaut Démare
committed
(building as RestockingBuilding).maxProcessOrdersCapacity <- (building as RestockingBuilding).maxProcessOrdersCapacity + 5;
}
if(use_gs){
if(use_r9){
Thibaut Démare
committed
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
committed
}
Thibaut Démare
committed
/**
* When a logistic provider has a new customer, he needs to find a new supply chain. This method build it.
Thibaut Démare
committed
*/
Thibaut Démare
committed
action getNewCustomer(FinalDestinationManager fdm, list<Stock> stocksLvl1, list<Stock> stocksLvl2){
Thibaut Démare
committed
/*
* 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
*/
Thibaut Démare
committed
SupplyChainElement sceCloseWarehouse <- connectLvl1Warehouse(fdm, fdmLeaf, stocksLvl1);
Thibaut Démare
committed
/*
* Connect the close warehouse to the large warehouse
*/
Thibaut Démare
committed
do connectLvl2Warehouse(fdm, sceCloseWarehouse, stocksLvl2);
customers <- customers + fdm;
Thibaut Démare
committed
}
/**
* 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 {
Thibaut Démare
committed
// 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;
Thibaut Démare
committed
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){
Thibaut Démare
committed
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
Thibaut Démare
committed
Warehouse findWarehouseLvl2(FinalDestinationManager fdm, int sizeOfStock){
Warehouse w <- nil;
if(adoptedStrategy = 1){
w <- world.findWarehouseLvl2Strat1(fdm, sizeOfStock, lvl1Warehouses);
}
else if(adoptedStrategy = 2){
Thibaut Démare
committed
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;
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
}