SSim C++ API documentation (v. 1.7.4)

bstp.cc

This is a variant of the bs.cc example that implements the same supply-chain system using a sequential style.

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