BUGFIX: Elevators did not handle Requests in the correct order
[cs356-p1-elevator.git] / elevator.cpp
index 5543b02..038bc29 100644 (file)
 #include "elevator.hpp"
+#include "main.hpp"
 
-Elevator::Elevator ()
-       : _stops()
-       , _pos()
-       , _direction(IDLE)
-       , _door_status(CLOSED)
+Elevator::Elevator (int elevator_number)
+       : state_(STATE_IDLE)
+       , wait_(0)
+       , direction_(IDLE)
+       , position_()
+       , stops_()
+       , number_(elevator_number)
 {
        // Intentionally Left Empty
 }
 
-void Elevator::stop_at (int floor, enum direction _direction)
+Elevator::Elevator (int starting_floor, int elevator_number)
+       : state_(STATE_IDLE)
+       , wait_(0)
+       , direction_(IDLE)
+       , position_(starting_floor)
+       , stops_()
+       , number_(elevator_number)
 {
-       std::cout << "Adding Stop: Floor=" << floor << " Dir=" << _direction << std::endl;
+       // Intentionally Left Empty
+}
+
+bool Elevator::currently_at_stop () const
+{
+       StopList::const_iterator it;
+       Stop current(position_, direction_);
+
+       /* Calculate the number of Stops above and below our current position */
+       int stops_above = 0;
+       int stops_below = 0;
+
+       for (it = stops_.begin(); it != stops_.end(); it++)
+       {
+               if (current < *it)
+                       ++stops_above;
+
+               if (current > *it)
+                       ++stops_below;
+       }
+
+       /* Check if we are at the top. If so, only the position needs to match */
+       if (direction_ == UP && stops_above == 0)
+       {
+               for (it = stops_.begin (); it != stops_.end (); it++)
+                       if (it->getPosition() == position_)
+                               return true;
+       }
+
+       /* Check if we are at the bottom. If so, only the position needs to match */
+       if (direction_ == DOWN && stops_below == 0)
+       {
+               for (it = stops_.begin (); it != stops_.end (); it++)
+                       if (it->getPosition() == position_)
+                               return true;
+       }
+
+       /* Check if we match exactly */
+       for (it = stops_.begin (); it != stops_.end (); it++)
+               if (*it == current)
+                       return true;
+
+       /* Check if we are IDLE. If so, only the position needs to match */
+       if (direction_ == IDLE)
+       {
+               for (it = stops_.begin (); it != stops_.end (); it++)
+                       if (it->getPosition() == position_)
+                               return true;
+       }
 
-       _stops.push_back (Stop(floor, _direction));
+       /* No match */
+       return false;
 }
 
-/*
- * Will check if direction needs to be changed, then change it.
- *
- * Will move this Elevator one step in the correct direction.
- *
- * The correct direction is defined based on the current direction:
- *
- * IDLE: Move in the direction that has the most stops
- *                     - Ex: if there are 10 stops above us, and two below, move DOWN
- *
- * UP: Move upwards
- *
- * DOWN: Move downwards
- */
-void Elevator::move ()
+void Elevator::stop_at (Stop &stop)
 {
-       /* Check if we are at a stop. If we are:
-        * 1) Open the doors
-        * 2) Clear the call button
-        */
-       if (currently_at_stop ())
+       StopList::iterator it;
+
+       if (stop.getDirection() == IDLE)
+               throw bad_direction();
+#if 0
+       /* If this is an "ALL" Stop, it supercedes all others */
+       if (stop.getDirection() == ALL)
        {
-               std::cout << "At a stop, right now" << std::endl;
+               stops_.remove (stop);
+               stops_.push_back (stop);
                return;
        }
+#endif
+
+       /* Check if this stop already exists. If so, just leave */
+       for (it = stops_.begin(); it != stops_.end(); it++)
+               if (*it == stop)
+                       return;
+
+       /* The stop did not already exist, so add it */
+       stops_.push_back (stop);
+}
+
+float Elevator::distance_from (Position &pos) const
+{
+       if (position_ > pos)
+               return position_ - pos;
+
+       return pos - position_;
+}
+
+float Elevator::distance_from (Stop &s) const
+{
+       Direction d = s.getDirection();
+       Position  p = s.getPosition ();
+
+       /* If direction doesn't matter, then only position does */
+       if (d == ALL || direction_ == IDLE)
+               return distance_from (p);
+
+       /* If we're not in the same direction, then we're "really far" away */
+       if (d != direction_)
+               return INT_MAX;
+
+       /* We must be in the correct direction, so pure distance is fine */
+       return distance_from (p);
+}
+
+void Elevator::transition_move_up ()
+{
+       direction_ = UP;
+       position_ += ELEVATOR_STEP;
+
+       // TODO: Call into the GUI to update the position
+       gui_update_position_label (number_, (float)position_, direction_);
+       std::cout << "Updating the GUI with our position: " << position_ << std::endl;
+}
+
+void Elevator::transition_move_down ()
+{
+       direction_ = DOWN;
+       position_ -= ELEVATOR_STEP;
+
+       // TODO: Call into the GUI to update the position
+       gui_update_position_label (number_, (float)position_, direction_);
+       std::cout << "Updating the GUI with our position: " << position_ << std::endl;
+}
+
+void Elevator::transition_move_idle ()
+{
+       direction_ = IDLE;
+       // TODO: Call into the GUI to update the position
+       gui_update_position_label (number_, (float)position_, direction_);
+       // do not change position while IDLE
+}
+
+void Elevator::transition_open_door ()
+{
+       /* Calculate the number of Stops above and below our
+        * current position */
+       StopList::const_iterator it;
+       Stop current = Stop(position_, direction_);
+       int stops_above = 0;
+       int stops_below = 0;
+
+       for (it = stops_.begin(); it != stops_.end(); it++)
+       {
+               if (current < *it)
+                       ++stops_above;
+
+               if (current > *it)
+                       ++stops_below;
+       }
+
+       std::cout << "Elevator: " << number_ << " stopping in direction=" << direction_ << std::endl;
+
+       /* Always unpress the in-elevator button for this stop, as well
+        * as clear the stop. This is like letting people off at a floor. */
+       gui_unpress_request_button (number_, (int)position_);
+       stops_.remove (Stop(position_, ALL));
 
-       /* Check if we have any more stops to attend to. If we do not, go IDLE */
-       if (_stops.size() == 0)
+
+       /* If we are going to switch direction, clear all stops here,
+        * regardless of direction.
+        *
+        * Otherwise, just clear this stop */
+       if      (direction_ == UP && stops_above == 0)
        {
-               _direction = IDLE;
-               return;
+               stops_.remove (Stop(position_, UP));
+               stops_.remove (Stop(position_, DOWN));
+               gui_unpress_call_button ((int)position_, UP);
+               gui_unpress_call_button ((int)position_, DOWN);
+       }
+       else if (direction_ == DOWN && stops_below == 0)
+       {
+               stops_.remove (Stop(position_, UP));
+               stops_.remove (Stop(position_, DOWN));
+               gui_unpress_call_button ((int)position_, UP);
+               gui_unpress_call_button ((int)position_, DOWN);
        }
+       else if (direction_ == IDLE)
+       {
+               stops_.remove (Stop(position_, UP));
+               stops_.remove (Stop(position_, DOWN));
+               gui_unpress_call_button ((int)position_, UP);
+               gui_unpress_call_button ((int)position_, DOWN);
+       }
+       else
+       {
+               stops_.remove (Stop(position_, direction_));
+               gui_unpress_call_button ((int)position_, direction_);
+       }
+
+       // TODO: Call into the GUI to open the door
+       gui_open_door (number_, (int)position_);
+       std::cout << "Opening Door" << std::endl;
+}
+
+void Elevator::transition_close_door ()
+{
+       // TODO: Call into the GUI to close the door
+       gui_close_door (number_, (int)position_);
+       std::cout << "Closing Door" << std::endl;
+}
+
+void Elevator::transition_begin_wait ()
+{
+       wait_ = 10;
+}
+
+void Elevator::transition_continue_wait ()
+{
+       --wait_;
+}
 
-       /* Find the best direction to move in, and set our direction to that */
-       if (_direction == IDLE)
-               _direction = find_best_direction ();
+#include <string>
+static void debug (const std::string& s)
+{
+       std::cout << s << std::endl;
+}
 
-       /* Move */
-       switch (_direction)
+static std::string get_state_name (State s)
+{
+       std::string sname;
+
+       switch (s)
        {
-               case UP:
-                       _pos += elevator_step;
+               case STATE_IDLE:
+                       sname = "STATE_IDLE";
+                       break;
+               case STATE_UP:
+                       sname = "STATE_UP";
                        break;
-               case DOWN:
-                       _pos -= elevator_step;
+               case STATE_DOWN:
+                       sname = "STATE_DOWN";
+                       break;
+               case STATE_WAIT:
+                       sname = "STATE_WAIT";
+                       break;
+               case STATE_OPEN_DOOR:
+                       sname = "STATE_OPEN_DOOR";
+                       break;
+               case STATE_CLOSE_DOOR:
+                       sname = "STATE_CLOSE_DOOR";
                        break;
                default:
-                       throw bad_direction ();
+                       sname = "BAD STATE";
                        break;
        }
 
-       std::cout << "at position: " << _pos << std::endl;
+       return sname;
 }
 
-enum direction Elevator::find_best_direction ()
+static std::string get_event_name (Event e)
 {
-       /* Make sure that the current direction is IDLE */
-       if (_direction != IDLE)
-               throw bad_direction();
+       std::string ename;
 
-       std::vector<Stop>::const_iterator it;
-       int above, below;
+       switch (e)
+       {
+               case EVT_IDLE:
+                       ename = "EVT_IDLE";
+                       break;
+               case EVT_UP:
+                       ename = "EVT_UP";
+                       break;
+               case EVT_DOWN:
+                       ename = "EVT_DOWN";
+                       break;
+               case EVT_WAIT:
+                       ename = "EVT_WAIT";
+                       break;
+               case EVT_OPEN_DOOR:
+                       ename = "EVT_OPEN_DOOR";
+                       break;
+               case EVT_CLOSE_DOOR:
+                       ename = "EVT_CLOSE_DOOR";
+                       break;
+               default:
+                       ename = "BAD EVENT";
+                       break;
+       }
+
+       return ename;
+}
+
+static void bad_transition (State s, Event e)
+{
+       std::cout << "Bad State Transition: " << get_state_name (s)
+                         << " -> " << get_event_name (e) << std::endl;
+}
+
+Event Elevator::find_next_event () const
+{
+       /* Calculate the number of Stops above and below our
+        * current position */
+       StopList::const_iterator it;
+       Stop current = Stop(position_, direction_);
+       int stops_above = 0;
+       int stops_below = 0;
 
-       /* Find out how many are above and below our current position */
-       for (above=0, below=0, it = _stops.begin(); it != _stops.end(); it++)
+       for (it = stops_.begin(); it != stops_.end(); it++)
        {
-               if (_pos < (*it))
-                       above++;
+               if (current < *it)
+                       ++stops_above;
 
-               if (_pos > (*it))
-                       below++;
+               if (current > *it)
+                       ++stops_below;
        }
 
-       /* If we have nothing, then stay IDLE */
-       if (above == 0 && below == 0)
-               return IDLE;
+       /* Now figure out which state transition to make */
+       switch (state_)
+       {
+               case STATE_IDLE:
 
-       /* If they are the same, always go up first.
-        *
-        * This comes from the design meeting on 2007-10-03 */
-       if (above == below)
-               return UP;
+                       if (currently_at_stop ())
+                               return EVT_OPEN_DOOR;
+
+                       if (stops_above > 0)
+                               return EVT_UP;
+
+                       if (stops_below > 0)
+                               return EVT_DOWN;
+
+                       return EVT_IDLE;
+
+                       break;
+               case STATE_UP:
+
+                       if (currently_at_stop ())
+                               return EVT_OPEN_DOOR;
+
+                       return EVT_UP;
+
+                       break;
+               case STATE_DOWN:
+
+                       if (currently_at_stop ())
+                               return EVT_OPEN_DOOR;
+
+                       return EVT_DOWN;
+
+                       break;
+               case STATE_WAIT:
+
+                       if (wait_ > 0)
+                               return EVT_WAIT;
+
+                       return EVT_CLOSE_DOOR;
+
+                       break;
+               case STATE_OPEN_DOOR:
+
+                       return EVT_WAIT;
 
-       /* Otherwise, go in the direction that has the most stops */
-       return above > below ? UP : DOWN;
+                       break;
+               case STATE_CLOSE_DOOR:
+
+                       if (currently_at_stop ())
+                               return EVT_OPEN_DOOR;
+
+                       if (direction_ == UP && stops_above > 0)
+                               return EVT_UP;
+
+                       if (direction_ == DOWN && stops_below > 0)
+                               return EVT_DOWN;
+
+                       /* We need to switch directions */
+                       if (direction_ == UP && stops_above == 0 && stops_below > 0)
+                               return EVT_DOWN;
+
+                       if (direction_ == DOWN && stops_below == 0 && stops_above > 0)
+                               return EVT_UP;
+
+                       return EVT_IDLE;
+
+                       break;
+               default:
+                       std::cout << "find_next_event(): Bad State" << std::endl;
+                       break;
+       }
 }
 
-bool Elevator::currently_at_stop ()
+void Elevator::move ()
 {
-       std::vector<Stop>::iterator it;
-       Stop current (_pos, _direction);
+       /* Generate Events */
+       Event e = find_next_event ();
 
-       for (it = _stops.begin (); it != _stops.end (); it++)
+       std::cout << "State Transition: " << get_state_name (state_) << " with "
+                         << get_event_name (e) << std::endl;
+
+       switch (state_)
        {
-               std::cout << "cas(): current=" << current << " it=" << *it << std::endl;
-               if (*it == current)
-                       return true;
+               case STATE_IDLE:
+                       switch (e)
+                       {
+                               case EVT_UP:
+                                       state_ = STATE_UP;
+                                       transition_move_up ();
+                                       break;
+                               case EVT_DOWN:
+                                       state_ = STATE_DOWN;
+                                       transition_move_down ();
+                                       break;
+                               case EVT_IDLE:
+                                       state_ = STATE_IDLE;
+                                       transition_move_idle ();
+                                       break;
+                               case EVT_OPEN_DOOR:
+                                       state_ = STATE_OPEN_DOOR;
+                                       transition_open_door ();
+                                       break;
+                               default:
+                                       bad_transition (state_, e);
+                                       break;
+                       } // end switch (e)
+                       break;
+
+               case STATE_UP:
+                       switch (e)
+                       {
+                               case EVT_UP:
+                                       state_ = STATE_UP;
+                                       transition_move_up ();
+                                       break;
+                               case EVT_OPEN_DOOR:
+                                       state_ = STATE_OPEN_DOOR;
+                                       transition_open_door ();
+                                       break;
+                               default:
+                                       bad_transition (state_, e);
+                                       break;
+                       } // end switch (e)
+                       break;
+
+               case STATE_DOWN:
+                       switch (e)
+                       {
+                               case EVT_DOWN:
+                                       state_ = STATE_DOWN;
+                                       transition_move_down ();
+                                       break;
+                               case EVT_OPEN_DOOR:
+                                       state_ = STATE_OPEN_DOOR;
+                                       transition_open_door ();
+                                       break;
+                               default:
+                                       bad_transition (state_, e);
+                                       break;
+                       } // end switch (e)
+                       break;
+
+               case STATE_WAIT:
+                       switch (e)
+                       {
+                               case EVT_WAIT:
+                                       state_ = STATE_WAIT;
+                                       transition_continue_wait ();
+                                       break;
+                               case EVT_CLOSE_DOOR:
+                                       state_ = STATE_CLOSE_DOOR;
+                                       transition_close_door ();
+                                       break;
+                               default:
+                                       bad_transition (state_, e);
+                                       break;
+                       } // end switch (e)
+                       break;
+
+               case STATE_OPEN_DOOR:
+                       switch (e)
+                       {
+                               case EVT_WAIT:
+                                       state_ = STATE_WAIT;
+                                       transition_begin_wait ();
+                                       break;
+                               default:
+                                       bad_transition (state_, e);
+                                       break;
+                       } // end switch (e)
+                       break;
+
+               case STATE_CLOSE_DOOR:
+                       switch (e)
+                       {
+                               case EVT_OPEN_DOOR:
+                                       state_ = STATE_OPEN_DOOR;
+                                       transition_open_door ();
+                                       break;
+                               case EVT_UP:
+                                       state_ = STATE_UP;
+                                       transition_move_up ();
+                                       break;
+                               case EVT_DOWN:
+                                       state_ = STATE_DOWN;
+                                       transition_move_down ();
+                                       break;
+                               case EVT_IDLE:
+                                       state_ = STATE_IDLE;
+                                       transition_move_idle ();
+                                       break;
+                               default:
+                                       bad_transition (state_, e);
+                                       break;
+                       } // end switch (e)
+                       break;
+
+               default:
+                       std::cout << "Bad State: " << get_state_name (state_) << std::endl;
+                       break;
        }
+}
 
-       return false;
+bool Elevator::is_idle () const
+{
+       if (stops_.size() != 0)
+               return false;
+
+       return direction_ == IDLE;
 }
 
+int Elevator::getLoad () const
+{
+       return stops_.size ();
+}
 
+bool Elevator::willStopAt (int floor, Direction direction) const
+{
+       Stop s (floor, direction);
+       StopList::const_iterator it;
 
+       for (it=stops_.begin(); it != stops_.end(); it++)
+               if (*it == s)
+                       return true;
 
+       return false;
+}
 
 /* vim: set ts=4 sts=4 sw=4 noexpandtab textwidth=112: */