Existing Bugs :
- The SimulatedRobotWithCommunicationDelay class's set_position method creates a new thread but doesn't start it. The thread_update.start() line is missing.
- The SimulatedRobotWithCommunicationDelay class's set_navigation_command method creates a new thread but doesn't start it. The thread_update.start() line is missing.
- The SimulatedRobotWithCommunicationDelay class's set_navigation_command method uses the self._robot variable without locking it first, which could cause synchronization issues in a multithreaded environment.
- The MissionController class's _poll_position method calls itself recursively without an exit condition, which could lead to a stack overflow error.
- The MissionController class's _poll_position method doesn't use a lock when accessing the current_waypoint_idx variable, which could cause synchronization issues in a multithreaded environment.
Changelog for fixing issues and refactoring code for readability and maintainability.
To address these issues, you can add the following modifications to the code:
BUG FIXES :
- In the SimulatedRobotWithCommunicationDelay class's set_position method, add
Thread(target=self._update_position, args=(position,)).start()
- In the SimulatedRobotWithCommunicationDelay class's set_navigation_command method, add
Thread(target=self._update_position, args=(waypoint,)).start()
- In the SimulatedRobotWithCommunicationDelay class's set_navigation_command method, add a lock before accessing the self._robot variable by defining lock = Lock() in the class constructor and using with lock: when accessing the self._robot variable.
- In the MissionController class's _poll_position method, add a condition
is_mission_completed
to the recursive call by checking if the mission has been completed. That is to check if the current waypoint index has reached the last waypoint in the trajectory. - In the MissionController class's _poll_position method, add a lock before accessing the current_waypoint_idx variable by defining lock = Lock() in the class constructor and using with lock: when accessing the current_waypoint_idx variable.
REFACTORIZATION for readability and maintainability
mission_controller.py
- Calling seperate method
_start_polling_position
to start the polling thread for better maintainability. - Initialize trajectory to None within constructor and set current_waypoint_idx to 0 whenever a new trajectory is set in
set_trajectory
method - Added a
is_mission_completed
method to encapsulate the logic for checking if the mission is completed. - Added a check in the _poll_position method to avoid infinite recursion in case the trajectory is not set.
SimulatedRobotWithCommunicationDelay
- Added
_update_position_with_delay
and called it as the target withinset_navigation_command
to start a new thread and secure it with a lock
INPUT VALIDATION: Added below input validation
mission_controller.py
- Check if robot is instance of SimulatedRobot or SimulatedRobotWithCommunicationDelay in the constructor
- Check if trajectory is a numpy array
SimulatedRobot
- Check if
update_position_callback
is callable and not None in the constructor - Check if waypoint, initial_position and position are a numy array
SAMPLE OUTPUT
Creating SimulatedRobot!
Creating MissionController!
Sending waypoint 0
Commanding robot to move to [2. 0.]
Sending waypoint 1
Commanding robot to move to [3. 0.]
Commanding robot to move to [2. 0.]
Commanding robot to move to [3. 0.]
Robot is now at [2. 0.]
Robot is now at [3. 0.]
Test complete