Subversion Repositories programming

Rev

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


#include "elevator.h"

// FIXME
// FIXME
// FIXME
void debug_puts (const char* s)
{
#ifndef NO_DEBUG_PUTS
    puts (s);
#endif
}
// FIXME
// FIXME
// FIXME

Elevator::Elevator (int num_floors) : num_floors(num_floors)
{
    this->terminate = false;
    this->pause = false;
    this->direction = IDLE;
    this->current_floor = 1.0;
    this->controller = NULL;

    this->stop_at_floors = std::vector<bool> (num_floors+1, 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+1; i++)
        if (stop_at_floors[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[i])
            count++;

    return count;
}

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

    this->stop_at_floors[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)
{
    Elevator *pthis = (Elevator*)me;

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

        pthis->run_elevator_logic ();

        std::cout << pthis << " Position: " << pthis->current_floor << std::endl;
        usleep (ELEVATOR_TIME_DELAY_USEC);
    }
}

void Elevator::thread_start ()
{
    boost::thread t1 (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;
                printf ("changed direction to: %s\n", (direction == MOVE_UP) ? "MOVE_UP" : "MOVE_DOWN");
            }

            break;

        case MOVE_UP:

            /* Decide if we should stop at the floor we're at */
            if (should_stop_at_current_floor ())
            {
                debug_puts ("stop at floor -- while going up");
                stop_at_floors[(int)(current_floor+0.5)] = false;
                have_user_enter_buttons ();
            }

            /* Decide to sit idle if we have no more floors to stop at */
            if (!has_floors_to_stop_at ())
            {
                debug_puts ("decides to sit idle");
                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 ())
            {
                debug_puts ("decides to switch direction to down");
                direction = MOVE_DOWN;
            }
            /* Since nothing else applies, we should keep moving in the
             * current direction, up. */
            else
            {
                current_floor += ELEVATOR_TIME_MOVE_AMOUNT;
                debug_puts ("moving up");

                // TODO: Add a check here to make sure we don't go too high

            }

            break;

        case MOVE_DOWN:

            /* Decide if we should stop at the floor we're at */
            if (should_stop_at_current_floor ())
            {
                debug_puts ("stop at floor -- while going down");
                stop_at_floors[(int)(current_floor)] = false;
                have_user_enter_buttons ();
            }

            /* Decide to sit idle if we have no more floors to stop at */
            if (!has_floors_to_stop_at ())
            {
                debug_puts ("decides to sit idle");
                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 ())
            {
                debug_puts ("decides to switch direction to up");
                direction = MOVE_UP;
            }
            /* Since nothing else applies, we should keep moving in the
             * current direction, down. */
            else
            {
                current_floor -= ELEVATOR_TIME_MOVE_AMOUNT;
                debug_puts ("moving down");

                // TODO: Add a check here to make sure we don't go too low

            }

            break;

        default:

            debug_puts ("bad value of direction");
            break;
    }
}

/**
 * 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=1; i<=num_floors; i++)
        if (near_floor (i))
            return true;

    return false;
}

bool Elevator::near_floor (int floor)
{
    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=1; i<=num_floors; i++)
        if (stop_at_floors[i])
            return true;

    return false;
}

bool Elevator::should_stop_at_current_floor ()
{
    int i;

    for (i=1; i<=num_floors; i++)
        if (near_floor (i) && stop_at_floors[i] == true)
            return true;

    return false;
}

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

    return stop_at_floors[floor];
}

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

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

void Elevator::have_user_enter_buttons ()
{
    controller->pause_all_elevators ();
    sleep (1);

    std::string s;

    std::cout << "Enter floors to stop at for elevator (space seperated): ";
    getline (std::cin, s);
    
    typedef std::vector<std::string> split_vector_type;
    split_vector_type SplitVec;
    boost::split (SplitVec, s, boost::is_any_of(" "));

    std::cout << "found " << SplitVec.size() << " strings" << std::endl;

    int i, val;
    for (i=0; i<SplitVec.size(); i++)
    {
        val = atoi (SplitVec[i].c_str());

        if (val > 0 && val <= num_floors)
            this->push_button (val);
    }

    controller->unpause_all_elevators ();
}