SSim C++ API documentation (v. 1.7.5)

bstp.cc

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

//
// 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;
}