This extensive example implements a simulation of a simple supply-chain system with a manufacturer, a retailer, and a number of buyers.
This uses reactive processes.
Thanks to Matt Rutherford for creating this example, and to Cyrus Hall for updating it.
#include <cstdlib>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#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)) );
}
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;
}
{
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;
};
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 Process
{
public:
virtual ~Manufacturer() {}
virtual void process_event(
const Event *e)
{
const Order* ord;
if ((ord = dynamic_cast<const Order *>(e)) != 0)
{
int delay = m_delayMap[ord->getItem()];
cout << "Manufacturer(" << m_id << ") received: " << ord->str() << endl;
Delivery* del = new Delivery(ord->getItem(), ord->getOrderId(),
ord->getQuantity());
}
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;
}
{
m_id = id;
}
{
return m_id;
}
private:
map<string,int> m_delayMap;
};
struct item_count
{
int stock;
int held;
int ordered;
int claimed;
};
{
public:
virtual ~Retailer() {}
virtual void process_event(
const Event *e)
{
const Delivery * d;
const Order * o;
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;
}
{
return m_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;
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)
{
Delivery *del = new Delivery(ord->getItem(), ord->getOrderId(),
ord->getQuantity());
delete ord;
q_needed -= ic.held;
if(q_needed < 0)
{
ic.held = -1 * q_needed;
q_needed = 0;
}
else
{
ic.held = 0;
}
if(q_needed != 0)
{
q_needed -= ic.stock;
if(q_needed < 0)
{
ic.stock = -1 * q_needed;
q_needed = 0;
}
else
{
ic.stock = 0;
}
if(q_needed != 0)
{
if(q_delivered < q_needed)
{
cerr << "LOGIC ERROR: q_needed=" << q_needed << endl;
break;
}
q_delivered -= q_needed;
ic.claimed -= q_needed;
}
}
}
else
{
newQ.push(ord);
}
}
m_pendingOrders[del->getItem()] = newQ;
ic.stock += q_delivered;
m_stockMap[del->getItem()] = ic;
}
void handleOrder(const Order* ord)
{
int q_needed = ord->getQuantity();
item_count ic = m_stockMap[ord->getItem()];
cout << "Retailer(" << m_id << ") received: " << ord->str() << endl;
if(ic.stock >= q_needed)
{
Delivery *del = new Delivery(ord->getItem(), ord->getOrderId(), q_needed);
ic.stock -= q_needed;
}
else
{
q_needed -= ic.stock;
ic.held += ic.stock;
ic.stock = 0;
m_pendingOrders[ord->getItem()].push(new Order(*ord));
ic.claimed += q_needed;
if((ic.ordered - ic.claimed) < 0)
{
int q = ic.claimed - ic.ordered;
Order *ord2 = new Order(ord->getItem(), q, m_id);
ic.ordered += q;
}
}
m_stockMap[ord->getItem()] = ic;
}
map<string,item_count> m_stockMap;
map<string,queue<Order*> > m_pendingOrders;
};
{
public:
virtual ~Buyer() {}
void addItem(const string& item, int quantity)
{
m_shoppingMap[item] = quantity;
}
virtual void process_event(
const Event *e)
{
const Delivery* del;
int sendDelay;
if ((del = dynamic_cast<const Delivery*>(e)))
{
sendDelay = m_delayMap[del->getItem()];
cout << "Buyer(" << m_id << ") order for " << del->getQuantity()
<< " " << del->getItem() << " took "
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;
}
}
virtual void init()
{
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 );
}
}
{
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;
}
{
m_id = id;
}
{
m_rid = id;
}
private:
map<string,int> m_shoppingMap;
map<string,int> m_delayMap;
};
#define BUYER_COUNT 10
{
public:
unsigned busy_count, term_count;
ErrorHandler() : busy_count(0), term_count(0) {}
void clear() throw ()
{
cout << "ErrorHandler::clear() called" << endl;
}
{
busy_count++;
}
{
term_count++;
}
};
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;
Retailer r(m.getId());
Buyer b[BUYER_COUNT];
set<string>::const_iterator i;
for(i = items.begin(); i != items.end(); ++i)
{
m.addItem((*i), get_rand(1, 30));
r.addItem((*i), get_rand(1, 100));
for(int j = 0; j < BUYER_COUNT; ++j) {
if(i == items.begin()) {
b[j].setRetailerId(r.getId());
}
b[j].addItem((*i), get_rand(1, 50));
}
}
ErrorHandler eh;
if( eh.busy_count )
{
cout << "WARNING: there were " << eh.busy_count << " dropped events." << endl;
}
if( eh.term_count )
{
cout << "WARNING: there were " << eh.term_count << " events sent to terminated processed." << endl;
}
bool failed = false;
for( int j = 0; j < BUYER_COUNT; ++j )
{
if( ! b[j].isSatisfied() )
{
failed = true;
}
}
return failed;
}