SSim C++ API documentation (v. 1.3.2)

Main Page   Namespace List   Class Hierarchy   Compound List   File List   Namespace Members   Compound 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.

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

#define ORDER_EVENT_TYPE 1
#define DELIVER_EVENT_TYPE 2

int get_rand( int a, int b )
{
        return( a + (int)(((double)rand()/(RAND_MAX+1.0))*(double)(b-a+1)) );
}

using namespace ssim;
using namespace std;

class BSEvent : public Event
{
public:
        BSEvent( EventType type ) : Event( type )
        {
        }

        virtual ~BSEvent(){}

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

class Order : public BSEvent
{
        string m_item;
        int m_orderId;
        int m_quantity;
        ProcessId m_deliverToId;
public:
        Order( const string& item, int quantity, int deliverToId )
                : BSEvent( ORDER_EVENT_TYPE ), 
                m_item( item ), m_orderId( rand() ), m_quantity( quantity ),
                m_deliverToId( deliverToId )
        {
        }

        Order( const Order& ord )
                : BSEvent( ORDER_EVENT_TYPE ),
                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: type=" << type << 
                        ",item=" << m_item << 
                        ",orderId=" << m_orderId << 
                        ",quantity=" << m_quantity <<
                        ",deliverToId=" << m_deliverToId <<
                        "]";
                return oss.str();
        }
};

class Delivery : public BSEvent
{
        string m_item;
        int m_orderId;
        int m_quantity;
public:
        Delivery( const string& item, int orderId, int quantity )
                : BSEvent( DELIVER_EVENT_TYPE ), 
                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: type=" <<  type <<
                        ",item=" << m_item << 
                        ",orderId=" << m_orderId << 
                        ",quantity=" << m_quantity <<
                        "]";
                return oss.str();
        }
};

class Manufacturer : public ReactiveProcess
{
        ProcessId m_id;
        map<string,int> m_delayMap;
public:
        Manufacturer() : m_id( NULL_PROCESSID )
        {
                register_response( ORDER_EVENT_TYPE, 
                                static_cast<EventResponse>( &Manufacturer::handleOrder ) );
        }

        virtual ~Manufacturer() {}

        void addItem( const string& item, int delay )
        {
                cout << "Manufacturer(" << m_id << ") -> " << item << " takes " << delay << " days to make" << endl;
                m_delayMap[item] = delay;
        }

        ProcessId getId() const
        {
                return m_id;
        }

        virtual void default_response( const Event* msg )
        {
                const BSEvent* bse = static_cast<const BSEvent*>( msg );
                cerr << "Manufacturer(" << m_id << ") received unknown event: " << bse->str() << endl;
        }

        void handleOrder( const Event* msg )
        {
                const Order* ord = static_cast<const Order*>( msg );
                cout << "Manufacturer(" << m_id << ") received: " << ord->str() << endl;
                int delay = m_delayMap[ord->getItem()];
                Delivery *del = new Delivery( ord->getItem(), ord->getOrderId(), ord->getQuantity() );
                // add a random extra delay
                Sim::signal_event( del, ord->getDeliverToId(), 
                                delay + get_rand( 0, 10 ) );
        }

        virtual void init()
        {
                cout << "Manufacturer: init" << endl;
                Sim::set_timeout( 0 );
        }

        void setId( ProcessId id )
        {
                m_id = id;
        }
};

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

class Retailer : public ReactiveProcess
{
        ProcessId m_id;
        ProcessId m_mid;
        map<string,item_count> m_stockMap;
        map< string,queue<Order*> > m_pendingOrders;

public:
        Retailer( ProcessId mid ) : m_id( NULL_PROCESSID ), m_mid( mid )
        {
                register_response( DELIVER_EVENT_TYPE, 
                        static_cast<EventResponse>( &Retailer::handleDelivery ) );
                register_response( ORDER_EVENT_TYPE, 
                        static_cast<EventResponse>( &Retailer::handleOrder ) );
        }

        virtual ~Retailer(){}

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

        virtual void default_response( const Event* msg )
        {
                const BSEvent* bse = static_cast<const BSEvent*>( msg );
                cerr << "Retailer(" << m_id << ") received unknown event: " << bse->str() << endl;
        }

        void handleDelivery( const Event* msg )
        {
                const Delivery* del = static_cast<const Delivery*>( msg );
                cout << "Retailer(" << m_id << ") received: " << del->str() << endl;
                item_count ic = m_stockMap[del->getItem()];
                /*
                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.
                ic.ordered -= del->getQuantity();
                int q_delivered = del->getQuantity();

                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( del, ord->getDeliverToId(), 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 Event* msg )
        {
                const Order* ord = static_cast<const Order*>( msg );
                cout << "Retailer(" << m_id << ") received: " << ord->str() << endl;
                /*
                 * 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 << ") 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( del, ord->getDeliverToId(), 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( ord2, m_mid, 1 );
                                ic.ordered += q;
                        }
                }
                // store item_count back into stock map to reflect any changes
                /*
                cout << "Retailer(" << m_id << ") s=" << ic.stock << " h=" << ic.held <<
                        " c=" << ic.claimed << " o=" << ic.ordered << endl;
                */
                m_stockMap[ord->getItem()] = ic;
        }

        virtual void init()
        {
                cout << "Retailer: init" << endl;
                Sim::set_timeout( 0 );
        }

        ProcessId getId() const
        {
                return m_id;
        }

        void setId( ProcessId id )
        {
                m_id = id;
        }
};

class Buyer : public ReactiveProcess
{
        ProcessId m_id;
        ProcessId m_rid;
        map<string,int> m_shoppingMap;
        map<string,int> m_delayMap;

public:
        Buyer() : m_id( NULL_PROCESSID ), m_rid( NULL_PROCESSID )
        {
                register_response( DELIVER_EVENT_TYPE, 
                        static_cast<EventResponse>( &Buyer::handleDelivery ) );
        }

        virtual ~Buyer(){}

        void addItem( const string& item, int quantity )
        {
                m_shoppingMap[item] = quantity;
        }

        virtual void default_response( const Event* msg )
        {
                const BSEvent* bse = static_cast<const BSEvent*>( msg );
                cerr << "Buyer(" << m_id << ") received unknown event: " << bse->str() << endl;
        }

        void handleDelivery( const Event* msg )
        {
                const Delivery* del = static_cast<const Delivery*>( msg );
                int sendDelay = m_delayMap[del->getItem()];
                cout << "Buyer(" << m_id << ") order for " << del->getQuantity() << " " << del->getItem() << 
                        " took " << (Sim::clock()-sendDelay) << " days." << endl;
        }

        virtual void init()
        {
                cout << "Buyer: init" << endl;
                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( ord, m_rid, m_delayMap[i->first]);
                }
        }

        ProcessId getId() const
        {
                return m_id;
        }

        void setId( ProcessId id )
        {
                m_id = id;
        }

        void setRetailerId( ProcessId id )
        {
                m_rid = id;
        }
};

//
// bs.cc
//
#include <cstdlib>
#include <iostream>
#include <map>
#include <set>
#include <ssim/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 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