Ejemplo n.º 1
0
 def test_only_when_prevents_execution(self, mock_connect, mock_redis,
                                       mock_thread):
     drone = DroneController("")  # noqa: F841
     mock_connect.assert_called_once()
     drone.status = FlightStatus.PANICKED
     drone.take_off(10)
     self.assertEquals(drone.status, FlightStatus.PANICKED)
Ejemplo n.º 2
0
    def test_redis_panic_handler(self, mock_connect, mock_redis, mock_thread):
        drone = DroneController("")

        # Simulate receiving a panic message
        drone._redis_pubsub.listen.return_value = ["panic"]
        drone._redis_pubsub_listener()

        # Now, the DroneController instance should have set panicked state.
        self.assertEquals(drone.status, FlightStatus.PANICKED)
        self.assertTrue(drone.panic_event.is_set())
Ejemplo n.º 3
0
    def test_takeoff(self, mock_connect, mock_redis, mock_thread):
        vehicle = mock.Mock()
        vehicle.location.global_relative_frame.alt = 10.0
        vehicle.is_armable = True
        vehicle.armed = False
        mock_connect.side_effect = [vehicle]

        drone = DroneController("")  # noqa: F841
        mock_connect.assert_called_once()
        drone.status = FlightStatus.LANDED
        drone.take_off(10.0)
        self.assertTrue(vehicle.armed)
        self.assertTrue(drone.status, FlightStatus.FLYING)
Ejemplo n.º 4
0
    def __init__(self, fc_addr, log_file):
        """
        Create a TraversalMission and start the mission server.

        :param fc_addr: MAVProxy address of the flight controller.
        :param log_file: Name of the log file for location data.
        """
        self.enable_disk_event_logging()

        self.dc = DroneController(fc_addr)
        self.logger = DirectLogger(path=log_file)
        self.log.debug('Drone controller and logger initialized successfully')

        self.start_server()
Ejemplo n.º 5
0
    def test_kill(self, mock_connect):
        drone = DroneController("")
        vehicle = mock.MagicMock()
        mock_connect.side_effect = [vehicle]

        drone.status = FlightStatus.LANDING
        self.assertRaises(
            AvionicsException,
            drone.kill,
        )
        self.assertEqual(drone.status, FlightStatus.LANDING)

        drone.kill(i_know_what_im_doing=True)
        self.assertEqual(drone.status, FlightStatus.LANDED)
Ejemplo n.º 6
0
    def __init__(self, fc_addr, log_file):
        """
        Create a WaypointGotoMission and start the mission server.

        :param fc_addr: MAVProxy address of the flight controller.
        :param log_file: Name of the log file for location data.
        """
        self.enable_disk_event_logging()

        self.dc = DroneController(fc_addr)
        self.location_logger = DirectLogger(path=log_file)
        self.log.debug('Drone controller and logger initialized successfully')

        self.cancel_tick = self.start_location_log()
        self.log.info('Waypoint-goto mission initialization complete')

        self.start_server()
Ejemplo n.º 7
0
    def __init__(self, fc_addr, log_file):
        """
        Create a HoverMission, which is started as soon as the class is instantiated.

        :param fc_addr: MAVProxy address of the flight controller.
        :param log_file: Name of log file for location data.
        """
        self.enable_disk_event_logging()

        self.dc = DroneController(fc_addr)
        self.location_logger = DirectLogger(path=log_file)
        self.log.debug('Drone controller and logger initialized successfully')

        self.cancel_tick = self.start_location_log()
        self.log.info('Hover mission initialization complete')

        self.start_server()
Ejemplo n.º 8
0
    def test_redis(self, mock_poll, mock_connect, mock_redis, mock_thread):
        mock_thread_instance = mock.Mock()
        mock_thread.return_value = mock_thread_instance
        drone = DroneController("")

        # Confirm Redis was initialized in SUBSCRIBE mode
        mock_redis.assert_called_once()
        drone._redis.pubsub.assert_called_once()
        drone._redis_pubsub.subscribe.assert_called_once()

        # Make sure we started a thread for the Redis listener.
        mock_thread.assert_called_once()
        drone._redis_pubsub_thread.start.assert_called_once()
Ejemplo n.º 9
0
    def test_goto(self, mock_poll, mock_connect, mock_redis, mock_thread):
        vehicle = self.mock_vehicle_with(mock_poll, mock_connect)
        set_home(vehicle, 0.0, 0.0, 5.0)

        drone = DroneController("")
        drone.take_off(30.0)
        move_vehicle_to(vehicle, 0., 0., 30.)
        drone.goto((0.0001, 0), 30.0, airspeed=3)
        drone.vehicle.simple_goto.assert_called_once()
        move_vehicle_to(vehicle, 0.0001, 0., 35.0)
        self.assertAlmostEqual(drone.distance_to((0.0001, 0., 35.0)), 0.0)
Ejemplo n.º 10
0
    def test_panic_lands(self, mock_poll, mock_connect, mock_redis,
                         mock_thread):
        vehicle = self.mock_vehicle_with(mock_poll, mock_connect)
        drone = DroneController("")
        drone.take_off(30.0)
        drone.panic()

        self.assertEquals(vehicle.mode, VehicleMode("LAND"))
Ejemplo n.º 11
0
    def test_land(self, mock_poll, mock_connect, mock_redis, mock_thread):
        self.mock_vehicle_with(mock_poll, mock_connect)

        drone = DroneController("")
        mock_connect.assert_called_once()
        drone.take_off(10.0)
        drone.land()
        self.assertFalse(drone._vehicle.armed)
        self.assertEquals(drone.status, FlightStatus.LANDED)
Ejemplo n.º 12
0
    def test_goto_validation(self, mock_poll, mock_connect, mock_redis,
                             mock_thread):
        vehicle = self.mock_vehicle_with(mock_poll, mock_connect)
        move_vehicle_to(vehicle, 0.0, 0.0, 5.0)
        set_home(vehicle, 0.0, 0.0, 5.0)

        drone = DroneController("")
        drone.take_off(30.0)
        self.assertRaises(RicePublicRelationsException, lambda: drone.goto(
            (10.0, 0.), 30.0))
        self.assertRaises(RicePublicRelationsException, lambda: drone.goto(
            (0.0, 0.0), -1.0))
Ejemplo n.º 13
0
    def __init__(self):
        # Port on which the HTTP server should listen
        self.port = int(os.environ.get('SKYSERVE_PORT', 5000))

        # Skyengine instance to interface with the drone.
        self.drone = DroneController(os.environ.get('FC_ADDR'))

        # Capturing images from the Pi camera, if available
        self.camera_factory = CameraFactory

        # Querying registered mission services
        self.redis = StrictRedis()

        # Skyserve version
        self.version = __version__

        # Publish this Skyserve instance to the network for discovery
        discovery.publish_drone(port=self.port)

        # Verify the instantiated context before continuing
        self._verify_ctx()
Ejemplo n.º 14
0
class WaypointGotoMission(Mission):

    port = 4002
    mission_id = 'waypoint-goto'

    def __init__(self, fc_addr, log_file):
        """
        Create a WaypointGotoMission and start the mission server.

        :param fc_addr: MAVProxy address of the flight controller.
        :param log_file: Name of the log file for location data.
        """
        self.enable_disk_event_logging()

        self.dc = DroneController(fc_addr)
        self.location_logger = DirectLogger(path=log_file)
        self.log.debug('Drone controller and logger initialized successfully')

        self.cancel_tick = self.start_location_log()
        self.log.info('Waypoint-goto mission initialization complete')

        self.start_server()

    @tick(interval=0.5)
    def start_location_log(self):
        """
        Start periodically logging the drone GPS location to disk.
        """
        location = self.dc.read_gps()
        message = LocationMessage(
            timestamp=time.time(),
            lat=location.lat,
            lon=location.lon,
            alt=location.alt,
        )

        self.location_logger.log(message)

    @callback(
        endpoint='/start-mission',
        description=
        'Gives the drone a series of waypoints and starts the mission.',
        required_params=('waypoints', 'hover_duration'),
        public=True,
    )
    def start_mission(self, data, *args, **kwargs):
        """

        :param data: Required to be of the form:
                     [{
                         'lat': ...,  # Target latitude
                         'lon': ...,  # Target longitude
                         'alt': ...,  # Target altitude
                     }]
        """
        try:
            hover_duration = data['hover_duration']
            waypoints = [
                Waypoint(point['lat'], point['lon'], point['alt'])
                for point in data['waypoints']
            ]
            start_alt = waypoints[0].alt

            self.log.debug(
                'Taking off to altitude: {alt}'.format(alt=start_alt))
            self.dc.take_off(start_alt)
            self.log.debug('Take off complete')

            for waypoint in waypoints:
                self.log.debug('Navigating to waypoint: ({lat}, {lat})'.format(
                    lat=waypoint.lat,
                    lon=waypoint.lon,
                ))
                self.dc.goto(coords=(waypoint.lat, waypoint.lon),
                             altitude=waypoint.alt,
                             airspeed=5)
                self.log.debug('Navigation to waypoint complete')

                location = self.dc.read_gps()
                self.log.debug(
                    'Arrived! Current location: ({lat}, {lon})'.format(
                        lat=location.lat,
                        lon=location.lon,
                    ))

                self.log.debug('Hovering for {hover_duration} seconds'.format(
                    hover_duration=hover_duration, ))
                time.sleep(hover_duration)

            self.log.info('Navigation to all waypoints complete. Landing now.')
            self.dc.land()
            self.log.info('Landed!')
        except FlightAbortedException:
            self.log.error(
                'Flight aborted due to panic; aborting remaining tasks.')

    @panic
    def panic(self, *args, **kwargs):
        self.log.info('Mission panicked! Landing immediately.')
        self.dc.panic()
Ejemplo n.º 15
0
class TraversalMission(Mission):
    """
    This mission will sweep the area of a designated region. It will determine the closet corner and
    begin there, then sweeping back and forth long ways along the region, from all inputted altitudes.
    """

    mission_id = 'airquality-mission'
    port = 4004

    def __init__(self, fc_addr, log_file):
        """
        Create a TraversalMission and start the mission server.

        :param fc_addr: MAVProxy address of the flight controller.
        :param log_file: Name of the log file for location data.
        """
        self.enable_disk_event_logging()

        self.dc = DroneController(fc_addr)
        self.logger = DirectLogger(path=log_file)
        self.log.debug('Drone controller and logger initialized successfully')

        self.start_server()


    @tick(interval=1)
    def start_log(self):
        """
        Start periodically logging the drone GPS location, speed, and direction.
        """
        gps_pos = self.dc.read_gps()
        self.logger.log(TraversalMessage(
            timestamp=time.time(),
            location=gps_pos,
            speed=self.dc.vehicle.airspeed,
            heading=self.dc.vehicle.heading,
            PM0_3 = air_quality_data['PM0.3']
			PM0_5 = air_quality_data['PM0.5']
			PM1_0 = air_quality_data['PM1.0']
			PM2_5 = air_quality_data['PM2.5']
			PM5_0 = air_quality_data['PM5.0']
			PM50  = air_quality_data['PM50']
        ))


    @callback(
        endpoint='/start-mission',
        description='Start the traversal mission: sweep the region starting from '
                    'nearest corner while reporting location, speed, and heading.',
        required_params=('corners', 'alt'),
        public=True,
    )
    def start_mission(self, data, *args, **kwargs):
        """
        Client invoked endpoint to begin the traversal mission

        :param data: Required to be of the form:
                     {
                         'corners': ..., #Name of set of corners matching desired region
                         'alt': [...] #List of altitudes in meters
                     }
        """
        altitudes = data['alt']
        corners = data['corners']
        corner_global = corners_names[corners]
    
        # make the corner lat/lon points into instances of Coordinate and compute the distances from 
        # each of them to the last one
        corner_coords = [Coordinate(c[0],c[1]) for c in corner_global]
        dist = [corner_coords[-1].distance_to(corner_coords[i]) for i in range(3)]
    
        # use the distances in order to pair the corner indeces with the neighbors
        dist_copy = dist[:]
        dist_copy.sort()    
        neighbor_close = dist.index(min(dist))
        neighbor_far = dist.index(dist_copy[1])
        neighbor_diag = dist.index(dist_copy[2])
        sides = {}
        sides['short'] = {3: neighbor_close, neighbor_close: 3, neighbor_far: neighbor_diag, neighbor_diag: neighbor_far}
        sides['long'] = {3: neighbor_far, neighbor_far: 3, neighbor_close: neighbor_diag, neighbor_diag: neighbor_close}
        self.log.debug('Short side pairings = (3,{q}), ({w},{e})'.format(
            q=neighbor_close,
            w=neighbor_far,
            e=neighbor_diag,
        ))

        # offset all the corners to be 10 meters inward from each direction
        for side in sides:
            for a in sides[side]:
                b = sides[side][a]
                corner_coords[a] = corner_coords[a].offset_toward_target(corner_coords[b], 10)

        # find closest corner to drone
        current_location = self.dc.read_gps()
        current_location_coord = Coordinate(current_location.lat, current_location.lon)
        calc_dist = [current_location_coord.distance_to(corner) for corner in corner_coords]
        closest_corner = calc_dist.index(min(calc_dist))
        second_corner = sides['long'][closest_corner]
        closest_corner_coord = corner_coords[closest_corner]
        second_corner_coord = corner_coords[second_corner]

        self.log.debug('Current location is ({a},{b}) and closest corner is ({c},{d})'.format(
            a=current_location.lat, 
            b=current_location.lon,
            c=closest_corner_coord.lat,
            d=closest_corner_coord.lon,
            ))
    
        # calculate the coordinates for the traversal
        traverse_coords = [closest_corner_coord, second_corner_coord]
        shortest_side_dist = min([closest_corner_coord.distance_to(corner_coords[sides['short'][closest_corner]]),
                                  second_corner_coord.distance_to(corner_coords[sides['short'][second_corner]])])
        num_of_sweeps = int(round(shortest_side_dist / 10))
        if num_of_sweeps != 0:
            dist_sweep = shortest_side_dist / float(num_of_sweeps)
        else:
            dist_sweep = 0
        self.log.debug('Number of sweeps: {a} Distance per sweep: {b}'.format(a=num_of_sweeps,b=dist_sweep))
        
        for i in range(num_of_sweeps):
            if (i % 2) == 0 :
                new_coord1 = traverse_coords[-1].offset_toward_target(corner_coords[sides['short'][second_corner]], dist_sweep)
                new_coord2 = traverse_coords[-2].offset_toward_target(corner_coords[sides['short'][closest_corner]], dist_sweep)
            else:
                new_coord2 = traverse_coords[-2].offset_toward_target(corner_coords[sides['short'][second_corner]], dist_sweep)
                new_coord1 = traverse_coords[-1].offset_toward_target(corner_coords[sides['short'][closest_corner]], dist_sweep)
            self.log.debug('New traverse coordinates: {a}, {b} on iter {i}'.format(a=new_coord1,b=new_coord2, i=i))
            traverse_coords.extend([new_coord1,new_coord2])
    
        # use the coordinates to make waypoints at all altitudes
        waypoints = []
        for i, alt in enumerate(altitudes):
            if (i%2) == 0:
                for coord in traverse_coords:
                    waypoints.append(coord_to_waypoint(coord, alt))
            else:
                for coord in reversed(traverse_coords):
                    waypoints.append(coord_to_waypoint(coord, alt))

        self.log.debug('All waypoints calculated!')
    

        #begin flight
        self.start_log()
        try: 
            self.log.debug('Taking off to starting altitude {}m'.format(altitudes[0]))
            self.dc.take_off(altitudes[0])
            self.log.debug('Take off complete')

            for waypoint in waypoints:
                self.log.debug('Navigating to waypoint: ({lat}, {lon})'.format(
                    lat=waypoint.lat,
                    lon=waypoint.lon,
                ))
                self.dc.goto(coords=(waypoint.lat, waypoint.lon), altitude=waypoint.alt, airspeed=2)
                self.log.debug('Navigation to waypoint complete')

                location = self.dc.read_gps()
                self.log.debug('Arrived! Current location: ({lat}, {lon})'.format(
                    lat=location.lat,
                    lon=location.lon,
                ))

            self.log.info('Navigation to all waypoints complete. Landing now.')
            self.dc.land()
            self.log.info('Landed!')
        except FlightAbortedException:
            self.log.error('Flight aborted due to panic; aborting remaining tasks.')


    @panic
    def panic(self, *args, **kwargs):
        self.log.info('Mission panicked! Landing immediately.')
        self.dc.panic()
Ejemplo n.º 16
0
 def test_panic_rejects_other_movement(self, mock_poll, mock_connect,
                                       mock_redis, mock_thread):
     vehicle = self.mock_vehicle_with(mock_poll, mock_connect)
     set_home(vehicle, 0.0, 0.0, 5.0)
     drone = DroneController("")
     drone.take_off(30.0)
     drone.panic()
     # Attempt some movement of the drone. These should be ignored.
     drone.goto((0.0001, 0.0001), 30)
     drone.goto_relative((0.0001, 0.0001), 30)
     drone.move_by((100.0, 100.0), 30)
     # Drone should not have actually ever issued any instruction to the FC
     # to move.
     vehicle.simple_goto.assert_not_called()
Ejemplo n.º 17
0
class HoverMission(Mission):
    """
    A mission to take off, hover, and land.
    """

    port = 4004
    mission_id = 'hover'

    def __init__(self, fc_addr, log_file):
        """
        Create a HoverMission, which is started as soon as the class is instantiated.

        :param fc_addr: MAVProxy address of the flight controller.
        :param log_file: Name of log file for location data.
        """
        self.enable_disk_event_logging()

        self.dc = DroneController(fc_addr)
        self.location_logger = DirectLogger(path=log_file)
        self.log.debug('Drone controller and logger initialized successfully')

        self.cancel_tick = self.start_location_log()
        self.log.info('Hover mission initialization complete')

        self.start_server()

    @tick(interval=0.5)
    def start_location_log(self):
        """
        Start periodically logging the drone GPS location to disk.
        """
        location = self.dc.vehicle.location.global_frame
        message = LocationMessage(
            timestamp=time.time(),
            lat=location.lat,
            lon=location.lon,
            alt=location.alt,
        )

        self.log.debug('Recording current location: ({lat}, {lon})'.format(
            lat=location.lat,
            lon=location.lon,
        ))

        self.location_logger.log(message)

    @callback(
        endpoint='/start-mission',
        description='Gives the drone an altitude and hover time.',
        required_params=('alt', 'hover_time'),
        public=True,
    )
    def start_mission(self, data, *args, **kwargs):
        """
        Client-invoked endpoint to begin the hover mission.

        :param data: Required to be of the form:
                     {
                         'alt': ...,  # Target altitude (m)
                         'hover_time': ..., # Hover time (s)
                     }
        """
        alt = data['alt']
        hover_time = data['hover_time']

        try:
            self.log.debug('Taking off to altitude: {alt}'.format(alt=alt))
            self.dc.take_off(alt)
            self.log.debug('Take off complete')

            self.log.debug('Hovering for: {hover_time} seconds'.format(
                hover_time=hover_time))
            time.sleep(hover_time)

            self.log.info('Hovering complete; begin landing')
            self.dc.land()
            self.log.info('Landed!')
        except FlightAbortedException:
            self.log.warn('Flight aborted due to emergency panic!')

    @panic
    def panic(self, *args, **kwargs):
        self.log.warn('Panic request received')
        self.dc.panic()