Thanks to Matt Rutherford for creating this example, and to Cyrus Hall for updating it.
// // bs.h // #include <string> #include <map> #include <queue> #include <sstream> #include <siena/ssim.h> 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 BSEvent : public Event { public: virtual ~BSEvent() { } virtual string str() const { return string(""); } }; class Order : public BSEvent { 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 BSEvent { 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: Manufacturer() : m_id(NULL_PROCESSID) { } virtual ~Manufacturer() {} /* Implimentations for virtuals in ssim::Process */ virtual void init() { cout << "Manufacturer: init" << endl; Sim::self_signal_event(NULL); } virtual void process_event(const Event *e) { const Order* ord; Delivery *del; int delay; if ((ord = dynamic_cast<const Order *>(e)) != 0) { // // if we have received an Order event // delay = m_delayMap[ord->getItem()]; cout << "Manufacturer(" << m_id << ") received: " << ord->str() << endl; 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; } } /* Manufacturer methods */ 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; }; typedef struct _item_count { int stock; int held; int ordered; int claimed; } item_count; class Retailer : public Process { public: Retailer(ProcessId manufac_id) : m_id(NULL_PROCESSID), m_mid(manufac_id) { } virtual ~Retailer() { } /* Implimentations for virtuals in ssim::Process */ virtual void init() { cout << "Retailer: init" << endl; } 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; } 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 impliment 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) { 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 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; } //memeber variables ProcessId m_id; ProcessId m_mid; map<string,item_count> m_stockMap; map<string,queue<Order*> > m_pendingOrders; }; class Buyer : public Process { 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 process_event(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 " << (Sim::clock()-sendDelay) << " days." << endl; } 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; cout << "Buyer: init" << endl; 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]); } } ProcessId getId() const { return m_id; } 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; };
// // bs.cc // #include <cstdlib> #include <iostream> #include <map> #include <set> #include <siena/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(); }