This uses reactive processes.
Thanks to Matt Rutherford for creating this example, and to Cyrus Hall for updating it.
00001 // 00002 // bs.cc 00003 // 00004 // this example uses reactive processes to implement a contrived 00005 // commerce scenario where there are buyers, retailers and 00006 // manufacturers that are interacting with each other. 00007 // 00008 #include <cstdlib> 00009 #include <iostream> 00010 #include <map> 00011 #include <queue> 00012 #include <set> 00013 #include <siena/ssim.h> 00014 #include <sstream> 00015 #include <string> 00016 00017 using namespace ssim; 00018 using namespace std; 00019 00020 int get_rand( int a, int b ) 00021 { 00022 return( a + (int)(((double)rand()/(RAND_MAX+1.0))*(double)(b-a+1)) ); 00023 } 00024 00025 // 00026 // events (messages) sent between processes 00027 // 00028 class Order : public Event 00029 { 00030 public: 00031 Order(const string& item, int quantity, int deliverToId) 00032 : m_item(item), m_orderId(rand()), m_quantity(quantity), 00033 m_deliverToId(deliverToId) {} 00034 00035 Order(const Order& ord) 00036 : m_item(ord.m_item), m_orderId(ord.m_orderId), 00037 m_quantity(ord.m_quantity), m_deliverToId(ord.m_deliverToId) {} 00038 00039 virtual ~Order() {} 00040 00041 const string& getItem() const 00042 { 00043 return m_item; 00044 } 00045 00046 int getOrderId() const 00047 { 00048 return m_orderId; 00049 } 00050 00051 int getQuantity() const 00052 { 00053 return m_quantity; 00054 } 00055 00056 ProcessId getDeliverToId() const 00057 { 00058 return m_deliverToId; 00059 } 00060 00061 string str() const 00062 { 00063 ostringstream oss; 00064 oss << "[Order: item=" << m_item << ",orderId=" 00065 << m_orderId << ",quantity=" << m_quantity << ",deliverToId=" 00066 << m_deliverToId << "]"; 00067 return oss.str(); 00068 } 00069 00070 private: 00071 string m_item; 00072 int m_orderId; 00073 int m_quantity; 00074 ProcessId m_deliverToId; 00075 }; 00076 00077 class Delivery : public Event 00078 { 00079 public: 00080 Delivery(const string& item, int orderId, int quantity) 00081 : m_item(item), m_orderId(orderId), m_quantity(quantity) {} 00082 00083 virtual ~Delivery() {} 00084 00085 const string& getItem() const 00086 { 00087 return m_item; 00088 } 00089 00090 int getOrderId() const 00091 { 00092 return m_orderId; 00093 } 00094 00095 int getQuantity() const 00096 { 00097 return m_quantity; 00098 } 00099 00100 string str() const 00101 { 00102 ostringstream oss; 00103 oss << "[Delivery: item=" << m_item << ",orderId=" 00104 << m_orderId << ",quantity=" << m_quantity << "]"; 00105 return oss.str(); 00106 } 00107 00108 private: 00109 string m_item; 00110 int m_orderId; 00111 int m_quantity; 00112 }; 00113 00114 class Manufacturer : public Process 00115 { 00116 public: 00117 Manufacturer() : m_id(NULL_PROCESSID) {} 00118 00119 virtual ~Manufacturer() {} 00120 00121 virtual void process_event(const Event *e) 00122 { 00123 const Order* ord; 00124 00125 if ((ord = dynamic_cast<const Order *>(e)) != 0) 00126 { 00127 // 00128 // if we have received an Order event 00129 // 00130 int delay = m_delayMap[ord->getItem()]; 00131 00132 cout << "Manufacturer(" << m_id << ") received: " << ord->str() << endl; 00133 Delivery* del = new Delivery(ord->getItem(), ord->getOrderId(), 00134 ord->getQuantity()); 00135 // add a random extra delay 00136 Sim::signal_event(ord->getDeliverToId(), del, delay + get_rand(0, 10)); 00137 } 00138 else if (dynamic_cast<const Delivery *>(e) != 0) 00139 { 00140 cerr << "Manufacturer(" << m_id << ") received a delivery event!" << endl; 00141 } 00142 else 00143 { 00144 cerr << "Manufacturer(" << m_id << ") received unknown event" << endl; 00145 } 00146 } 00147 00148 void addItem(const string& item, int delay) 00149 { 00150 cout << "Manufacturer(" << m_id << ") -> " << item << " takes " 00151 << delay << " days to make" << endl; 00152 m_delayMap[item] = delay; 00153 } 00154 00155 void setId(ProcessId id) 00156 { 00157 m_id = id; 00158 } 00159 00160 ProcessId getId() const 00161 { 00162 return m_id; 00163 } 00164 00165 private: 00166 ProcessId m_id; 00167 map<string,int> m_delayMap; 00168 }; 00169 00170 struct item_count 00171 { 00172 int stock; 00173 int held; 00174 int ordered; 00175 int claimed; 00176 }; 00177 00178 class Retailer : public Process 00179 { 00180 public: 00181 Retailer(ProcessId manufac_id) : m_id(NULL_PROCESSID), m_mid(manufac_id) {} 00182 00183 virtual ~Retailer() {} 00184 00185 virtual void process_event(const Event *e) 00186 { 00187 const Delivery * d; 00188 const Order * o; 00189 if ((d = dynamic_cast<const Delivery *>(e))) 00190 { 00191 handleDelivery(d); 00192 } 00193 else if ((o = dynamic_cast<const Order *>(e))) 00194 { 00195 handleOrder(o); 00196 } 00197 else 00198 { 00199 cerr << "Retailer(" << m_id << ") received unknown event." << endl; 00200 } 00201 } 00202 00203 void addItem(const string& item, int quantity ) 00204 { 00205 cout << "Retailer(" << m_id << ") -> has " << quantity << " " << item 00206 << "(s)" << endl; 00207 item_count ic; 00208 ic.stock = quantity; 00209 ic.held = 0; 00210 ic.ordered = 0; 00211 ic.claimed = 0; 00212 m_stockMap[item] = ic; 00213 } 00214 00215 ProcessId getId() const 00216 { 00217 return m_id; 00218 } 00219 00220 void setId(ProcessId id) 00221 { 00222 m_id = id; 00223 } 00224 00225 private: 00226 void handleDelivery(const Delivery* del) 00227 { 00228 item_count ic = m_stockMap[del->getItem()]; 00229 int q_delivered = del->getQuantity(); 00230 00231 cout << "Retailer(" << m_id << ") received: " << del->str() << endl; 00232 /* 00233 cout << "Retailer(" << m_id << ") s=" << ic.stock << " h=" 00234 << ic.held << " c=" << ic.claimed << " o=" << ic.ordered 00235 << endl; 00236 */ 00237 00238 // a delivery of X widgets arrived, we need to combine with 00239 // the widgets that are being held and start sending deliveries 00240 // out to the buyer. 00241 // !!! Maybe we should implement the deliveries to buyers as 00242 // a timeout? If nothing else, this would demonstrate that 00243 // functionality -- CPH 00244 00245 ic.ordered -= q_delivered; 00246 00247 queue<Order*>& oqRef = m_pendingOrders[del->getItem()]; 00248 queue<Order*> newQ; 00249 while(!oqRef.empty()) 00250 { 00251 Order *ord = oqRef.front(); 00252 oqRef.pop(); 00253 00254 int q_needed = ord->getQuantity(); 00255 if((ic.stock + ic.held + q_delivered) >= q_needed) 00256 { 00257 // we are going to fulfill this order one way or the other... 00258 Delivery *del = new Delivery(ord->getItem(), ord->getOrderId(), 00259 ord->getQuantity()); 00260 Sim::signal_event(ord->getDeliverToId(), del, 1); 00261 delete ord; 00262 00263 // Just have to account for it, first from held 00264 q_needed -= ic.held; 00265 if(q_needed < 0) 00266 { 00267 ic.held = -1 * q_needed; 00268 q_needed = 0; 00269 } 00270 else 00271 { 00272 ic.held = 0; 00273 } 00274 00275 if(q_needed != 0) 00276 { 00277 // second from stock 00278 q_needed -= ic.stock; 00279 if(q_needed < 0) 00280 { 00281 ic.stock = -1 * q_needed; 00282 q_needed = 0; 00283 } 00284 else 00285 { 00286 ic.stock = 0; 00287 } 00288 00289 if(q_needed != 0) 00290 { 00291 // third from delivery 00292 if(q_delivered < q_needed) 00293 { 00294 cerr << "LOGIC ERROR: q_needed=" << q_needed << endl; 00295 break; 00296 } 00297 00298 q_delivered -= q_needed; 00299 ic.claimed -= q_needed; 00300 } 00301 } 00302 } 00303 else 00304 { // we can't fulfill the order yet 00305 newQ.push(ord); 00306 } 00307 } 00308 m_pendingOrders[del->getItem()] = newQ; 00309 00310 // if q_delivered is still positive, it goes into stock 00311 ic.stock += q_delivered; 00312 00313 /* 00314 cout << "Retailer(" << m_id << ") s=" << ic.stock << " h=" << ic.held 00315 << " c=" << ic.claimed << " o=" << ic.ordered << endl; 00316 */ 00317 m_stockMap[del->getItem()] = ic; 00318 } 00319 00320 void handleOrder(const Order* ord) 00321 { 00322 /* 00323 * an order was received by this retailer, it needs to 00324 * check if it can fulfill the order out of stock. If 00325 * not, it needs to order the difference, and put 00326 * the order in a holding area until it gets delivery. 00327 */ 00328 int q_needed = ord->getQuantity(); 00329 item_count ic = m_stockMap[ord->getItem()]; 00330 00331 cout << "Retailer(" << m_id << ") received: " << ord->str() << endl; 00332 /* 00333 cout << "Retailer(" << m_id << ") s=" << ic.stock << " h=" 00334 << ic.held << " c=" << ic.claimed << " o=" << ic.ordered 00335 << endl; 00336 */ 00337 if(ic.stock >= q_needed) 00338 { 00339 Delivery *del = new Delivery(ord->getItem(), ord->getOrderId(), q_needed); 00340 Sim::signal_event(ord->getDeliverToId(), del, 1); 00341 ic.stock -= q_needed; 00342 } 00343 else 00344 { 00345 // move the ones in stock to held 00346 q_needed -= ic.stock; 00347 ic.held += ic.stock; 00348 ic.stock = 0; 00349 00350 // need to wait for a delivery, have to copy 00351 // order because scheduler may delete order after this 00352 // method returns. 00353 m_pendingOrders[ord->getItem()].push(new Order(*ord)); 00354 ic.claimed += q_needed; 00355 // if there aren't already enough on order, generate an order 00356 if((ic.ordered - ic.claimed) < 0) 00357 { 00358 int q = ic.claimed - ic.ordered; 00359 Order *ord2 = new Order(ord->getItem(), q, m_id); 00360 Sim::signal_event(m_mid, ord2, 1); 00361 ic.ordered += q; 00362 } 00363 } 00364 // store item_count back into stock map to reflect any changes 00365 m_stockMap[ord->getItem()] = ic; 00366 } 00367 00368 ProcessId m_id; 00369 ProcessId m_mid; 00370 map<string,item_count> m_stockMap; 00371 map<string,queue<Order*> > m_pendingOrders; 00372 }; 00373 00374 class Buyer : public Process 00375 { 00376 public: 00377 Buyer() : m_id(NULL_PROCESSID), m_rid(NULL_PROCESSID) {} 00378 00379 virtual ~Buyer() {} 00380 00381 void addItem(const string& item, int quantity) 00382 { 00383 m_shoppingMap[item] = quantity; 00384 } 00385 00386 virtual void process_event(const Event *e) 00387 { 00388 const Delivery* del; 00389 int sendDelay; 00390 00391 if ((del = dynamic_cast<const Delivery*>(e))) 00392 { 00393 sendDelay = m_delayMap[del->getItem()]; 00394 00395 cout << "Buyer(" << m_id << ") order for " << del->getQuantity() 00396 << " " << del->getItem() << " took " 00397 << (Sim::clock()-sendDelay) << " days." << endl; 00398 m_shoppingMap[del->getItem()] -= del->getQuantity(); 00399 } 00400 else if ((dynamic_cast<const Order *>(e))) 00401 { 00402 cerr << "Buyer(" << m_id << ") received an order event!" << endl; 00403 } 00404 else 00405 { 00406 cerr << "Buyer(" << m_id << ") received unknown event." << endl; 00407 } 00408 } 00409 00410 virtual void init() 00411 { 00412 map<string,int>::const_iterator i; 00413 00414 for(i = m_shoppingMap.begin(); i != m_shoppingMap.end(); ++i) 00415 { 00416 Order *ord = new Order( (*i).first, (*i).second, m_id ); 00417 m_delayMap[(*i).first] = get_rand( 0, 20 ); 00418 Sim::signal_event(m_rid, ord, m_delayMap[(*i).first]); 00419 } 00420 } 00421 00422 ProcessId getId() const 00423 { 00424 return m_id; 00425 } 00426 00427 bool isSatisfied() const 00428 { 00429 map<string,int>::const_iterator i; 00430 bool ret = true; 00431 for(i = m_shoppingMap.begin(); i != m_shoppingMap.end(); ++i) 00432 { 00433 if( (*i).second != 0 ) 00434 { 00435 cerr << "Buyer(" << m_id << ") still needs " << (*i).second << " of " << (*i).first << endl; 00436 ret = false; 00437 } 00438 } 00439 return ret; 00440 } 00441 00442 void setId( ProcessId id ) 00443 { 00444 m_id = id; 00445 } 00446 00447 void setRetailerId( ProcessId id ) 00448 { 00449 m_rid = id; 00450 } 00451 00452 private: 00453 ProcessId m_id; 00454 ProcessId m_rid; 00455 map<string,int> m_shoppingMap; 00456 map<string,int> m_delayMap; 00457 }; 00458 00459 #define BUYER_COUNT 10 00460 00461 class ErrorHandler : public SimErrorHandler 00462 { 00463 public: 00464 unsigned busy_count, term_count; 00465 00466 ErrorHandler() : busy_count(0), term_count(0) {} 00467 00468 void clear() throw () 00469 { 00470 cout << "ErrorHandler::clear() called" << endl; 00471 } 00472 00473 void handle_busy(ProcessId pid, const Event * e) throw() 00474 { 00475 busy_count++; 00476 } 00477 00478 void handle_terminated(ProcessId pid, const Event * e) throw() 00479 { 00480 term_count++; 00481 } 00482 }; 00483 00484 // main function creates a list of the items being delt with by the 00485 // supply-chain, instantiates a single manufacturer and retailer and a 00486 // number of buyers and ties them together by passing the process ids, 00487 // and finally runs the simulation. 00488 int main(int argc, char** argv) 00489 { 00490 srand(1); 00491 00492 set<string> items; 00493 items.insert("HardDrive"); 00494 items.insert("Keyboard"); 00495 items.insert("Monitor"); 00496 items.insert("Mouse"); 00497 items.insert("Printer"); 00498 items.insert("Scanner"); 00499 00500 Manufacturer m; 00501 m.setId(Sim::create_process(&m)); 00502 00503 Retailer r(m.getId()); 00504 r.setId(Sim::create_process(&r)); 00505 00506 Buyer b[BUYER_COUNT]; 00507 00508 set<string>::const_iterator i; 00509 for(i = items.begin(); i != items.end(); ++i) 00510 { 00511 // name, production delay 00512 m.addItem((*i), get_rand(1, 30)); 00513 00514 // name, quantity in stock 00515 r.addItem((*i), get_rand(1, 100)); 00516 00517 for(int j = 0; j < BUYER_COUNT; ++j) { 00518 if(i == items.begin()) { 00519 b[j].setId(Sim::create_process(&b[j])); 00520 b[j].setRetailerId(r.getId()); 00521 } 00522 // name, number needed 00523 b[j].addItem((*i), get_rand(1, 50)); 00524 } 00525 } 00526 00527 ErrorHandler eh; 00528 Sim::set_error_handler( &eh ); 00529 Sim::run_simulation(); 00530 00531 if( eh.busy_count ) 00532 { 00533 cout << "WARNING: there were " << eh.busy_count << " dropped events." << endl; 00534 } 00535 if( eh.term_count ) 00536 { 00537 cout << "WARNING: there were " << eh.term_count << " events sent to terminated processed." << endl; 00538 } 00539 bool failed = false; 00540 // check that all buyers are satisfied 00541 for( int j = 0; j < BUYER_COUNT; ++j ) 00542 { 00543 if( ! b[j].isSatisfied() ) 00544 { 00545 failed = true; 00546 } 00547 } 00548 return failed; 00549 } 00550