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