// // 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)); } } Sim::run_simulation(); 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; }