SSim C++ API documentation (v. 1.5.0)

Main Page | Namespace List | Class Hierarchy | Class List | File List | Namespace Members | Class Members | Examples

bs.cc

This extensive example implements a simulation of a simple supply-chain system with a manufacturer, a retailer, and a number of buyers. The example consists of a header file and an implementation file.

Thanks to Matt Rutherford for creating this example, and to Cyrus Hall for updating it.

//
// bs.h
//
#include <string>
#include <map>
#include <queue>
#include <sstream>
#include <siena/ssim.h>

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

class BSEvent : public Event {
public:
    virtual ~BSEvent() { }

    virtual string str() const {
        return string("");
    }
};

class Order : public BSEvent {
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 BSEvent {
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 Process {
public:
    Manufacturer()
        : m_id(NULL_PROCESSID)
        { }

    virtual ~Manufacturer() {}

    /* Implimentations for virtuals in ssim::Process */
    virtual void init() {
        cout << "Manufacturer: init" << endl;
        Sim::self_signal_event(NULL);
    }

    virtual void process_event(const Event *e) {
        const Order* ord;
        Delivery *del;
        int delay;
        
        if ((ord = dynamic_cast<const Order *>(e)) != 0) {
            //
            // if we have received an Order event
            //
            delay = m_delayMap[ord->getItem()];

            cout << "Manufacturer(" << m_id << ") received: " << ord->str()
                 << endl;
            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;
        }
    }

    /* Manufacturer methods */
    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;
};

typedef struct _item_count {
    int stock;
    int held;
    int ordered;
    int claimed;
} item_count;


class Retailer : public Process {
public:
    Retailer(ProcessId manufac_id)
        : m_id(NULL_PROCESSID), m_mid(manufac_id)
        { }

    virtual ~Retailer() { }

    /* Implimentations for virtuals in ssim::Process */
    virtual void init() {
        cout << "Retailer: init" << endl;
    }

    virtual void process_event(const Event *e) {
        const Delivery * d;
        const Order * o;
        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 impliment 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) {
                    break;
                }
                
                // 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) {
                    break;
                }
                
                // 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;
    }

    //memeber variables
    ProcessId m_id;
    ProcessId m_mid;
    map<string,item_count> m_stockMap;
    map<string,queue<Order*> > m_pendingOrders;
};

class Buyer : public Process {
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 process_event(Event *e) {
        const Delivery* del;
        int sendDelay;
        
        if ((del = dynamic_cast<const Delivery*>(e))) {
            sendDelay = m_delayMap[del->getItem()];
            
            cout << "Buyer(" << m_id << ") order for " << del->getQuantity()
                 << " " << del->getItem() << " took "
                 << (Sim::clock()-sendDelay) << " days." << endl;
        } 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;
        }
    }

    virtual void init() {
        map<string,int>::const_iterator i;

        cout << "Buyer: init" << endl;
        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]);
        }
    }

    ProcessId getId() const {
        return m_id;
    }

    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;
};
//
// bs.cc
//
#include <cstdlib>
#include <iostream>
#include <map>
#include <set>
#include <siena/ssim.h>
#include <string>

#include "bs.h"

#define BUYER_COUNT 10

using namespace ssim;
using namespace std;

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();
}


Copyright © 2002-2004 University of Colorado.
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License". This documentation is authored and maintained by Antonio Carzaniga