This extensive example implements a simulation of a simple supply-chain system with a manufacturer, a retailer, and a number of buyers. The example consists of a header file and an implementation file.
Thanks to Matt Rutherford for creating this example.
// // bs.h // #include <string> #include <ssim/ssim.h> #include <sstream> #include <queue> #define ORDER_EVENT_TYPE 1 #define DELIVER_EVENT_TYPE 2 int get_rand( int a, int b ) { return( a + (int)(((double)rand()/(RAND_MAX+1.0))*(double)(b-a+1)) ); } using namespace ssim; using namespace std; class BSEvent : public Event { public: BSEvent( EventType type ) : Event( type ) { } virtual ~BSEvent(){} virtual string str() const { return string( "" ); } }; class Order : public BSEvent { string m_item; int m_orderId; int m_quantity; ProcessId m_deliverToId; public: Order( const string& item, int quantity, int deliverToId ) : BSEvent( ORDER_EVENT_TYPE ), m_item( item ), m_orderId( rand() ), m_quantity( quantity ), m_deliverToId( deliverToId ) { } Order( const Order& ord ) : BSEvent( ORDER_EVENT_TYPE ), 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: type=" << type << ",item=" << m_item << ",orderId=" << m_orderId << ",quantity=" << m_quantity << ",deliverToId=" << m_deliverToId << "]"; return oss.str(); } }; class Delivery : public BSEvent { string m_item; int m_orderId; int m_quantity; public: Delivery( const string& item, int orderId, int quantity ) : BSEvent( DELIVER_EVENT_TYPE ), 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: type=" << type << ",item=" << m_item << ",orderId=" << m_orderId << ",quantity=" << m_quantity << "]"; return oss.str(); } }; class Manufacturer : public ReactiveProcess { ProcessId m_id; map<string,int> m_delayMap; public: Manufacturer() : m_id( NULL_PROCESSID ) { register_response( ORDER_EVENT_TYPE, static_cast<EventResponse>( &Manufacturer::handleOrder ) ); } virtual ~Manufacturer() {} void addItem( const string& item, int delay ) { cout << "Manufacturer(" << m_id << ") -> " << item << " takes " << delay << " days to make" << endl; m_delayMap[item] = delay; } ProcessId getId() const { return m_id; } virtual void default_response( const Event* msg ) { const BSEvent* bse = static_cast<const BSEvent*>( msg ); cerr << "Manufacturer(" << m_id << ") received unknown event: " << bse->str() << endl; } void handleOrder( const Event* msg ) { const Order* ord = static_cast<const Order*>( msg ); cout << "Manufacturer(" << m_id << ") received: " << ord->str() << endl; int delay = m_delayMap[ord->getItem()]; Delivery *del = new Delivery( ord->getItem(), ord->getOrderId(), ord->getQuantity() ); // add a random extra delay Sim::signal_event( del, ord->getDeliverToId(), delay + get_rand( 0, 10 ) ); } virtual void init() { cout << "Manufacturer: init" << endl; Sim::set_timeout( 0 ); } void setId( ProcessId id ) { m_id = id; } }; typedef struct _item_count { int stock; int held; int ordered; int claimed; } item_count; class Retailer : public ReactiveProcess { ProcessId m_id; ProcessId m_mid; map<string,item_count> m_stockMap; map< string,queue<Order*> > m_pendingOrders; public: Retailer( ProcessId mid ) : m_id( NULL_PROCESSID ), m_mid( mid ) { register_response( DELIVER_EVENT_TYPE, static_cast<EventResponse>( &Retailer::handleDelivery ) ); register_response( ORDER_EVENT_TYPE, static_cast<EventResponse>( &Retailer::handleOrder ) ); } virtual ~Retailer(){} 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; } virtual void default_response( const Event* msg ) { const BSEvent* bse = static_cast<const BSEvent*>( msg ); cerr << "Retailer(" << m_id << ") received unknown event: " << bse->str() << endl; } void handleDelivery( const Event* msg ) { const Delivery* del = static_cast<const Delivery*>( msg ); cout << "Retailer(" << m_id << ") received: " << del->str() << endl; item_count ic = m_stockMap[del->getItem()]; /* 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. ic.ordered -= del->getQuantity(); int q_delivered = del->getQuantity(); 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( del, ord->getDeliverToId(), 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 ) { break; } // 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 ) { break; } // 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 Event* msg ) { const Order* ord = static_cast<const Order*>( msg ); cout << "Retailer(" << m_id << ") received: " << ord->str() << endl; /* * 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 << ") 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( del, ord->getDeliverToId(), 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( ord2, m_mid, 1 ); ic.ordered += q; } } // store item_count back into stock map to reflect any changes /* cout << "Retailer(" << m_id << ") s=" << ic.stock << " h=" << ic.held << " c=" << ic.claimed << " o=" << ic.ordered << endl; */ m_stockMap[ord->getItem()] = ic; } virtual void init() { cout << "Retailer: init" << endl; Sim::set_timeout( 0 ); } ProcessId getId() const { return m_id; } void setId( ProcessId id ) { m_id = id; } }; class Buyer : public ReactiveProcess { ProcessId m_id; ProcessId m_rid; map<string,int> m_shoppingMap; map<string,int> m_delayMap; public: Buyer() : m_id( NULL_PROCESSID ), m_rid( NULL_PROCESSID ) { register_response( DELIVER_EVENT_TYPE, static_cast<EventResponse>( &Buyer::handleDelivery ) ); } virtual ~Buyer(){} void addItem( const string& item, int quantity ) { m_shoppingMap[item] = quantity; } virtual void default_response( const Event* msg ) { const BSEvent* bse = static_cast<const BSEvent*>( msg ); cerr << "Buyer(" << m_id << ") received unknown event: " << bse->str() << endl; } void handleDelivery( const Event* msg ) { const Delivery* del = static_cast<const Delivery*>( msg ); int sendDelay = m_delayMap[del->getItem()]; cout << "Buyer(" << m_id << ") order for " << del->getQuantity() << " " << del->getItem() << " took " << (Sim::clock()-sendDelay) << " days." << endl; } virtual void init() { cout << "Buyer: init" << endl; 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( ord, m_rid, m_delayMap[i->first]); } } ProcessId getId() const { return m_id; } void setId( ProcessId id ) { m_id = id; } void setRetailerId( ProcessId id ) { m_rid = id; } };
// // bs.cc // #include <cstdlib> #include <iostream> #include <map> #include <set> #include <ssim/ssim.h> #include <string> #include "bs.h" #define BUYER_COUNT 10 using namespace ssim; using namespace std; 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 ) ); } } Sim::run_simulation(); }