Subversion Repositories programming

Rev

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

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

#include "controller.h"

Controller::Controller (int num_floors, int num_elevators) : num_floors (num_floors), num_elevators (num_elevators)
{
    assert (num_floors > 2);
    assert (num_elevators > 0);

    elevator.reserve (num_elevators);

    for (int i=0; i<num_elevators; i++)
    {
        elevator.push_back (new Elevator (num_floors));
        elevator.at(i) -> set_controller (this);
    }

    // Seed the RNG
    srandom (time(NULL));
}

Controller::~Controller ()
{
}

void Controller::request_elevator (int on_floor, int direction)
{
    assert (on_floor < num_floors);
    assert (on_floor >= 0);
    assert (direction == MOVE_UP || direction == MOVE_DOWN);

    float distance = INT_MAX;
    int elevator_number;

    // check that there is not already an elevator requested
    // to this floor. If there is already one, ignore this
    // request.
    if (floor_already_requested (on_floor))
        return;

    // find elevator that is closest, AND NOT moving away
    // "push" it's button
    elevator_number = find_closest_elevator (on_floor, direction);
    elevator.at(elevator_number) -> push_button (on_floor);

    //printf ("elevator[%d] gets queued for floor: %d\n", elevator_number, on_floor);
}

/**
 * Check if there is an elevator already in the queue to stop
 * at the floor given as a parameter.
 */
bool Controller::floor_already_requested (int on_floor)
{
    assert (on_floor >= 0);
    assert (on_floor < num_floors);

    int i;

    for (i=0; i<num_elevators; i++)
        if (elevator.at(i) -> button_is_pushed (on_floor))
            return true;

    return false;
}

/**
 * Return the number of the closest elevator to the
 * floor given as a parameter.
 *
 * We only want to choose elevators that are close and
 * heading in the right direction, or the closest elevator
 * if it is sitting idle.
 */
int Controller::find_closest_elevator (int to_floor, int in_direction)
{
    assert (to_floor >= 0);
    assert (to_floor < num_floors);
    assert (in_direction == MOVE_UP || in_direction == MOVE_DOWN);

    int i;
    float distance = INT_MAX;
    float temp_distance;
    int answer = -1;

    float cur_floor;
    int   cur_direction;

    //printf ("finding closest elevator: to_floor=%d -- in_direction=%d\n", to_floor, in_direction);

    for (i=0; i<num_elevators; i++)
    {
        cur_floor = elevator.at(i) -> get_current_floor ();
        cur_direction = elevator.at(i) -> get_direction ();
        temp_distance = fabsf (cur_floor - to_floor);

        //printf ("cur_floor=%e -- direc=%d -- t_dist=%e\n", cur_floor, cur_direction, temp_distance);

        // Automatically discard elevators that are moving in the wrong
        // direction, but DO consider ones that are idle.
        if (cur_direction == MOVE_DOWN && in_direction == MOVE_UP)
        {
            //printf ("type 1: skipped elevator[%d]\n", i);
            continue;
        }

        if (cur_direction == MOVE_UP && in_direction == MOVE_DOWN)
        {
            //printf ("type 2: skipped elevator[%d]\n", i);
            continue;
        }

        if (cur_floor < to_floor && cur_direction == MOVE_UP)
        {
            //printf ("good cantidate type 1\n");
            // GOOD CANTIDATE
            if (temp_distance < distance)
            {
                answer = i;
                distance = temp_distance;
            }
        }

        if (cur_floor > to_floor && cur_direction == MOVE_DOWN)
        {
            //printf ("good cantidate type 2\n");
            // GOOD CANTIDATE
            if (temp_distance < distance)
            {
                answer = i;
                distance = temp_distance;
            }
        }

        if (cur_direction == IDLE && temp_distance < distance)
        {
            //printf ("idle cantidate\n");
            answer = i;
            distance = temp_distance;
        }
    }

    // If we failed to find an elevator, then we need to
    // pick at random.
    if (answer == -1)
    {
        // Pick a random number in the range [0,num_elevators-1]
        answer = (int) (0.0 + ((num_elevators - 1.0) * (random() / (RAND_MAX + 1.0))));

        printf ("picked at random: %d\n", answer);

        assert (answer >= 0);
        assert (answer < num_elevators);
    }

    return answer;
}

void Controller::disable_elevator (int elevator_number)
{
}

void Controller::enable_elevator (int elevator_number)
{
    // make sure elevator is enabled first
}

void Controller::start_all_elevators ()
{
    int i;

    for (i=0; i<num_elevators; i++)
        elevator.at(i) -> thread_start ();
}

void Controller::stop_all_elevators ()
{
    int i;

    for (i=0; i<num_elevators; i++)
        elevator.at(i) -> thread_stop ();
}

void Controller::pause_all_elevators ()
{
    int i;

    for (i=0; i<num_elevators; i++)
        elevator.at(i) -> thread_pause ();
}

void Controller::unpause_all_elevators ()
{
    int i;

    for (i=0; i<num_elevators; i++)
        elevator.at(i) -> thread_unpause ();
}

int Controller::which_elevator_is (Elevator *e)
{
    int i;

    for (i=0; i<num_elevators; i++)
        if (elevator.at (i) == e)
            return i;

    return -1;
}

void Controller::stop_at_floor (Elevator *e, int floor)
{
    int elevator_num = which_elevator_is (e);

    assert (elevator_num != -1);

    pause_all_elevators ();

    /*****************
     * Call the gui
     ****************/

    // Open the elevator door
    //gui->open_elevator_at (floor, elevator_num);
    gui->open_elevator_at (num_floors-(floor+1), elevator_num);

    // Untoggle all buttons on that floor
    if (floor >= 0 && floor <= 8)
        gui->unset_up_button (floor);

    if (floor >= 1 && floor <= 9)
        gui->unset_down_button (floor-1);

    // Get the floors from the user
    gui->get_floors_from_user (elevator_num);

    sleep (4); // FIXME

    gui->close_elevator_at (num_floors-(floor+1), elevator_num);

    unpause_all_elevators ();
}

void Controller::set_gui (Elevator_Window *gui)
{
    this->gui = gui;
}

void Controller::update_elevator_position (Elevator *e)
{
    std::ostringstream strstrm;
    int elevator_num = which_elevator_is (e);

    assert (elevator_num != -1);

    strstrm << e->get_current_floor ();

    gui->set_label (elevator_num, strstrm.str());
}