Subversion Repositories programming

Rev

Rev 344 | Rev 348 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*******************************************************************************
 * elevator.cpp
 *
 * Implementation for the Elevator class.
 *
 * Copyright 2006, Ira W. Snyder (devel@irasnyder.com)
 ******************************************************************************/

#include "elevator.h"

Elevator::Elevator (int num_floors) : num_floors(num_floors)
{
    int i;

    this->terminate = false;
    this->pause = false;
    this->direction = IDLE;
    this->current_floor = 0.0;
    this->controller = NULL;

    this->stop_at_floors.reserve (num_floors);

    for (i=0; i<num_floors; i++)
        this->stop_at_floors.push_back(bool(false));
}

Elevator::~Elevator ()
{
    this->pause = false;
    this->terminate = true;
}

int Elevator::get_direction ()
{
    return this->direction;
}

/**
 * Return the number of floors above the current one that
 * are queued to be stopped at.
 */
int Elevator::floors_above_current ()
{
    int i = ((int)current_floor) + 1;
    int count = 0;

    for (/* see above */; i<num_floors; i++)
        if (stop_at_floors.at(i))
            count++;

    return count;
}

/**
 * Return the number of floors below the current one that
 * are queued to be stopped at.
 */
int Elevator::floors_below_current ()
{
    int i;
    int count = 0;

    if (near_a_floor ())
        i = (int)current_floor - 1;
    else
        i = (int)current_floor;

    for (/* see above */; i>=0; i--)
        if (stop_at_floors.at(i))
            count++;

    return count;
}

void Elevator::push_button (int floor)
{
    assert (floor < num_floors);
    assert (floor >= 0);

    this->stop_at_floors.at(floor) = true;
}

/**
 * This function is the entry point for a thread.
 *
 * You must pass the object in, since this is a static function
 * and we aren't allowed to access the this pointer.
 */
void Elevator::thread_entry (const Elevator *me)
{
    assert (me != NULL);

    Elevator *pthis = (Elevator*)me;

    while (!pthis->terminate)
    {
        /* Implement "pausing" */
        while (pthis->pause)
        {
            usleep (ELEVATOR_TIME_PAUSE_USEC);
        }

        pthis->run_elevator_logic ();

#ifndef QUIET
        //std::cout << pthis << " Position: " << pthis->current_floor << std::endl;
#endif
        usleep (ELEVATOR_TIME_DELAY_USEC);
    }
}

void Elevator::thread_start ()
{
    boost::thread the_thread (boost::bind (this->thread_entry, this));
}

void Elevator::run_elevator_logic ()
{
    int i;

    /* Decide what to do */
    switch (direction)
    {
        case IDLE:

            /* Move towards the direction that has more floors to stop at.
             * Prefer DOWN if there is a tie. */
            if (has_floors_to_stop_at ())
            {
                direction = (floors_above_current () > floors_below_current ()) ? MOVE_UP : MOVE_DOWN;

#ifndef QUIET
                printf ("changed direction to: %s\n", (direction == MOVE_UP) ? "MOVE_UP" : "MOVE_DOWN");
#endif
            }

            break;

        case MOVE_UP:

            /* Decide if we should stop at the floor we're at */
            if (should_stop_at_current_floor ())
            {
                stop_at_floor (MOVE_UP);
            }

            /* Decide to sit idle if we have no more floors to stop at */
            if (!has_floors_to_stop_at ())
            {
#ifndef QUIET
                puts ("decides to sit idle");
#endif
                direction = IDLE;
            }
            /* Decide to switch directions if we have no more floors to stop
             * at in the current direction, but we have floors in the
             * opposite direction to stop at. */
            else if (floors_below_current () && !floors_above_current ())
            {
#ifndef QUIET
                puts ("decides to switch direction to down");
#endif
                direction = MOVE_DOWN;
            }
            /* Since nothing else applies, we should keep moving in the
             * current direction, up. */
            else
            {
                current_floor += ELEVATOR_TIME_MOVE_AMOUNT;

                // Check to make sure we don't go too high
                assert (current_floor < (num_floors * 1.0));

            }

            break;

        case MOVE_DOWN:

            /* Decide if we should stop at the floor we're at */
            if (should_stop_at_current_floor ())
            {
                stop_at_floor (MOVE_DOWN);
            }

            /* Decide to sit idle if we have no more floors to stop at */
            if (!has_floors_to_stop_at ())
            {
#ifndef QUIET
                puts ("decides to sit idle");
#endif
                direction = IDLE;
            }
            /* Decide to switch directions if we have no more floors to stop
             * at in the current direction, but we have floors in the
             * opposite direction to stop at. */
            else if (floors_above_current () && !floors_below_current ())
            {
#ifndef QUIET
                puts ("decides to switch direction to up");
#endif
                direction = MOVE_UP;
            }
            /* Since nothing else applies, we should keep moving in the
             * current direction, down. */
            else
            {
                current_floor -= ELEVATOR_TIME_MOVE_AMOUNT;

                // Check to make sure we don't go too low
                assert (current_floor > -0.1);

            }

            break;

        default:
            
            assert (false); // bad value of direction
            break;
    }

    Dispatch_Data data;
    data.type = DISPATCH_DATA_UPDATE_LABEL;
    data.elev = this;
    data.farg = current_floor;
    data.direc = direction;

    controller->push_to_gui_queue (data);

    (*dispatcher)();
}

/**
 * Call this to stop the thread from running.
 */
void Elevator::thread_stop ()
{
    this->terminate = true;
}

/**
 * Call this to pause the thread without exiting it.
 */
void Elevator::thread_pause ()
{
    this->pause = true;
}

/**
 * Call this to unpause the thread.
 */
void Elevator::thread_unpause ()
{
    this->pause = false;
}

bool Elevator::near_a_floor ()
{
    int i;

    for (i=0; i<num_floors; i++)
        if (near_floor (i))
            return true;

    return false;
}

bool Elevator::near_floor (int floor)
{
    assert (floor >= 0);
    assert (floor < num_floors);

    const float thresh  = ELEVATOR_TIME_MOVE_AMOUNT / 2.0;
    const float greater = floor + thresh;
    const float lower   = floor - thresh;

    if (current_floor > lower && current_floor < greater)
        return true;

    return false;
}

bool Elevator::has_floors_to_stop_at ()
{
    int i;

    for (i=0; i<num_floors; i++)
        if (stop_at_floors.at(i))
            return true;

    return false;
}

bool Elevator::should_stop_at_current_floor ()
{
    int i;

    for (i=0; i<num_floors; i++)
        if (near_floor (i) && stop_at_floors.at(i) == true)
            return true;

    return false;
}

bool Elevator::button_is_pushed (int floor)
{
    assert (floor < num_floors);
    assert (floor >= 0);

    return stop_at_floors.at(floor);
}

float Elevator::get_current_floor ()
{
    return current_floor;
}

void Elevator::set_controller (Controller *c)
{
    this->controller = c;
}

void Elevator::stop_at_floor (int in_direction)
{
    int the_floor = (int)(current_floor+0.5);

#ifndef QUIET
    if (in_direction == MOVE_UP)
        printf ("stop at floor %d -- while going up\n", the_floor);
    else
        printf ("stop at floor %d -- while going down\n", the_floor);
#endif
    stop_at_floors.at(the_floor) = false;

    Dispatch_Data data;
    data.type = DISPATCH_DATA_STOP_AT_FLOOR;
    data.elev = this;
    data.iarg = the_floor;
    data.direc = in_direction;

    controller->push_to_gui_queue (data);

    (*dispatcher)();
}

void Elevator::set_dispatcher (Glib::Dispatcher *d)
{
    this->dispatcher = d;
}