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