SSim C++ API documentation (v. 1.7.6)
bstp.cc

This is a variant of the bs.cc example that implements the same supply-chain system using a sequential style.

//
// bstp.cc
//
// this example uses sequential processes to implement a contrived
// commerce scenario where there are buyers, retailers and
// manufacturers that are interacting with each other.
//
#include <cstdlib>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <siena/ssim.h>
#include <siena/tprocess.h>
#include <sstream>
#include <string>
using namespace ssim;
using namespace std;
int get_rand( int a, int b )
{
return( a + (int)(((double)rand()/(RAND_MAX+1.0))*(double)(b-a+1)) );
}
//
// events (messages) sent between processes
//
class Order : public Event
{
public:
Order(const string& item, int quantity, int deliverToId)
: m_item(item), m_orderId(rand()), m_quantity(quantity),
m_deliverToId(deliverToId) {}
Order(const Order& ord)
: m_item(ord.m_item), m_orderId(ord.m_orderId),
m_quantity(ord.m_quantity), m_deliverToId(ord.m_deliverToId) {}
virtual ~Order() {}
const string& getItem() const
{
return m_item;
}
int getOrderId() const
{
return m_orderId;
}
int getQuantity() const
{
return m_quantity;
}
ProcessId getDeliverToId() const
{
return m_deliverToId;
}
string str() const
{
ostringstream oss;
oss << "[Order: item=" << m_item << ",orderId="
<< m_orderId << ",quantity=" << m_quantity << ",deliverToId="
<< m_deliverToId << "]";
return oss.str();
}
private:
string m_item;
int m_orderId;
int m_quantity;
ProcessId m_deliverToId;
};
class Delivery : public Event
{
public:
Delivery(const string& item, int orderId, int quantity)
: m_item(item), m_orderId(orderId), m_quantity(quantity) {}
virtual ~Delivery() {}
const string& getItem() const
{
return m_item;
}
int getOrderId() const
{
return m_orderId;
}
int getQuantity() const
{
return m_quantity;
}
string str() const
{
ostringstream oss;
oss << "[Delivery: item=" << m_item << ",orderId="
<< m_orderId << ",quantity=" << m_quantity << "]";
return oss.str();
}
private:
string m_item;
int m_orderId;
int m_quantity;
};
class Manufacturer : public TProcess
{
public:
Manufacturer() : m_id(NULL_PROCESSID) {}
virtual ~Manufacturer() {}
virtual void main()
{
while( true )
{
const Event *e = wait_for_event();
const Order *ord;
if ((ord = dynamic_cast<const Order *>(e)) != 0)
{
//
// if we have received an Order event
//
int delay = m_delayMap[ord->getItem()];
cout << "Manufacturer(" << m_id << ") received: " << ord->str() << endl;
Delivery* del = new Delivery(ord->getItem(), ord->getOrderId(),
ord->getQuantity());
// add a random extra delay
Sim::signal_event(ord->getDeliverToId(), del, delay + get_rand(0, 10));
}
else if (dynamic_cast<const Delivery *>(e) != 0)
{
cerr << "Manufacturer(" << m_id << ") received a delivery event!" << endl;
}
else
{
cerr << "Manufacturer(" << m_id << ") received unknown event" << endl;
}
}
}
void addItem(const string& item, int delay)
{
cout << "Manufacturer(" << m_id << ") -> " << item << " takes "
<< delay << " days to make" << endl;
m_delayMap[item] = delay;
}
void setId(ProcessId id)
{
m_id = id;
}
ProcessId getId() const
{
return m_id;
}
private:
ProcessId m_id;
map<string,int> m_delayMap;
};
struct item_count
{
int stock;
int held;
int ordered;
int claimed;
};
class Retailer : public TProcess
{
public:
Retailer(ProcessId manufac_id) : m_id(NULL_PROCESSID), m_mid(manufac_id) {}
virtual ~Retailer() {}
virtual void main()
{
while( true )
{
const Delivery * d;
const Order * o;
const Event *e = wait_for_event();
if ((d = dynamic_cast<const Delivery *>(e)))
{
handleDelivery(d);
}
else if ((o = dynamic_cast<const Order *>(e)))
{
handleOrder(o);
}
else
{
cerr << "Retailer(" << m_id << ") received unknown event." << endl;
}
}
}
void addItem(const string& item, int quantity )
{
cout << "Retailer(" << m_id << ") -> has " << quantity << " " << item
<< "(s)" << endl;
item_count ic;
ic.stock = quantity;
ic.held = 0;
ic.ordered = 0;
ic.claimed = 0;
m_stockMap[item] = ic;
}
ProcessId getId() const
{
return m_id;
}
void setId(ProcessId id)
{
m_id = id;
}
private:
void handleDelivery(const Delivery* del)
{
item_count ic = m_stockMap[del->getItem()];
int q_delivered = del->getQuantity();
cout << "Retailer(" << m_id << ") received: " << del->str() << endl;
/*
cout << "Retailer(" << m_id << ") s=" << ic.stock << " h="
<< ic.held << " c=" << ic.claimed << " o=" << ic.ordered
<< endl;
*/
// a delivery of X widgets arrived, we need to combine with
// the widgets that are being held and start sending deliveries
// out to the buyer.
// !!! Maybe we should implement the deliveries to buyers as
// a timeout? If nothing else, this would demonstrate that
// functionality -- CPH
ic.ordered -= q_delivered;
queue<Order*>& oqRef = m_pendingOrders[del->getItem()];
queue<Order*> newQ;
while(!oqRef.empty())
{
Order *ord = oqRef.front();
oqRef.pop();
int q_needed = ord->getQuantity();
if((ic.stock + ic.held + q_delivered) >= q_needed)
{
// we are going to fulfill this order one way or the other...
Delivery *del = new Delivery(ord->getItem(), ord->getOrderId(),
ord->getQuantity());
Sim::signal_event(ord->getDeliverToId(), del, 1);
delete ord;
// Just have to account for it, first from held
q_needed -= ic.held;
if(q_needed < 0)
{
ic.held = -1 * q_needed;
q_needed = 0;
}
else
{
ic.held = 0;
}
if(q_needed != 0)
{
// second from stock
q_needed -= ic.stock;
if(q_needed < 0)
{
ic.stock = -1 * q_needed;
q_needed = 0;
}
else
{
ic.stock = 0;
}
if( q_needed != 0 )
{
// third from delivery
if(q_delivered < q_needed)
{
cerr << "LOGIC ERROR: q_needed=" << q_needed << endl;
break;
}
q_delivered -= q_needed;
ic.claimed -= q_needed;
}
}
}
else
{ // we can't fulfill the order yet
newQ.push(ord);
}
}
m_pendingOrders[del->getItem()] = newQ;
// if q_delivered is still positive, it goes into stock
ic.stock += q_delivered;
/*
cout << "Retailer(" << m_id << ") s=" << ic.stock << " h=" << ic.held
<< " c=" << ic.claimed << " o=" << ic.ordered << endl;
*/
m_stockMap[del->getItem()] = ic;
}
void handleOrder(const Order* ord)
{
/*
* an order was received by this retailer, it needs to
* check if it can fulfill the order out of stock. If
* not, it needs to order the difference, and put
* the order in a holding area until it gets delivery.
*/
int q_needed = ord->getQuantity();
item_count ic = m_stockMap[ord->getItem()];
cout << "Retailer(" << m_id << ") received: " << ord->str() << endl;
/*
cout << "Retailer(" << m_id << ") s=" << ic.stock << " h="
<< ic.held << " c=" << ic.claimed << " o=" << ic.ordered
<< endl;
*/
if(ic.stock >= q_needed)
{
Delivery *del = new Delivery(ord->getItem(), ord->getOrderId(), q_needed);
Sim::signal_event(ord->getDeliverToId(), del, 1);
ic.stock -= q_needed;
}
else
{
// move the ones in stock to held
q_needed -= ic.stock;
ic.held += ic.stock;
ic.stock = 0;
// need to wait for a delivery, have to copy
// order because scheduler may delete order after this
// method returns.
m_pendingOrders[ord->getItem()].push(new Order(*ord));
ic.claimed += q_needed;
// if there aren't already enough on order, generate an order
if((ic.ordered - ic.claimed) < 0)
{
int q = ic.claimed - ic.ordered;
Order *ord2 = new Order(ord->getItem(), q, m_id);
Sim::signal_event(m_mid, ord2, 1);
ic.ordered += q;
}
}
// store item_count back into stock map to reflect any changes
m_stockMap[ord->getItem()] = ic;
}
ProcessId m_id;
ProcessId m_mid;
map<string,item_count> m_stockMap;
map<string,queue<Order*> > m_pendingOrders;
};
class Buyer : public TProcess
{
public:
Buyer() : m_id(NULL_PROCESSID), m_rid(NULL_PROCESSID) {}
virtual ~Buyer() {}
void addItem(const string& item, int quantity)
{
m_shoppingMap[item] = quantity;
}
virtual void main()
{
// initially make orders
map<string,int>::const_iterator i;
for(i = m_shoppingMap.begin(); i != m_shoppingMap.end(); ++i)
{
Order *ord = new Order( (*i).first, (*i).second, m_id );
m_delayMap[(*i).first] = get_rand( 0, 20 );
Sim::signal_event(m_rid, ord, m_delayMap[(*i).first]);
}
// now wait for responses
while( true )
{
const Event* e = wait_for_event();
const Delivery* del;
if ((del = dynamic_cast<const Delivery*>(e)))
{
int sendDelay = m_delayMap[del->getItem()];
cout << "Buyer(" << m_id << ") order for " << del->getQuantity()
<< " " << del->getItem() << " took "
<< (Sim::clock()-sendDelay) << " days." << endl;
m_shoppingMap[del->getItem()] -= del->getQuantity();
}
else if ((dynamic_cast<const Order *>(e)))
{
cerr << "Buyer(" << m_id << ") received an order event!" << endl;
}
else
{
cerr << "Buyer(" << m_id << ") received unknown event." << endl;
}
}
}
ProcessId getId() const
{
return m_id;
}
bool isSatisfied() const
{
map<string,int>::const_iterator i;
bool ret = true;
for(i = m_shoppingMap.begin(); i != m_shoppingMap.end(); ++i)
{
if( (*i).second != 0 )
{
cerr << "Buyer(" << m_id << ") still needs " << (*i).second << " of " << (*i).first << endl;
ret = false;
}
}
return ret;
}
void setId( ProcessId id )
{
m_id = id;
}
void setRetailerId( ProcessId id )
{
m_rid = id;
}
private:
ProcessId m_id;
ProcessId m_rid;
map<string,int> m_shoppingMap;
map<string,int> m_delayMap;
};
#define BUYER_COUNT 10
// main function creates a list of the items being delt with by the
// supply-chain, instantiates a single manufacturer and retailer and a
// number of buyers and ties them together by passing the process ids,
// and finally runs the simulation.
int main(int argc, char** argv)
{
srand(1);
set<string> items;
items.insert("HardDrive");
items.insert("Keyboard");
items.insert("Monitor");
items.insert("Mouse");
items.insert("Printer");
items.insert("Scanner");
Manufacturer m;
m.setId(Sim::create_process(&m));
Retailer r(m.getId());
r.setId(Sim::create_process(&r));
Buyer b[BUYER_COUNT];
set<string>::const_iterator i;
for(i = items.begin(); i != items.end(); ++i)
{
// name, production delay
m.addItem((*i), get_rand(1, 30));
// name, quantity in stock
r.addItem((*i), get_rand(1, 100));
for(int j = 0; j < BUYER_COUNT; ++j) {
if(i == items.begin()) {
b[j].setId(Sim::create_process(&b[j]));
b[j].setRetailerId(r.getId());
}
// name, number needed
b[j].addItem((*i), get_rand(1, 50));
}
}
bool failed = false;
// check that all buyers are satisfied
for( int j = 0; j < BUYER_COUNT; ++j )
{
if( ! b[j].isSatisfied() )
{
failed = true;
}
}
return failed;
}