def __init__(self, device_serial, is_emulator=True, output_dir=None, use_hierarchy_viewer=False): """ create a device :param device_serial: serial number of target device :param is_emulator: boolean, type of device, True for emulator, False for real device :return: """ self.logger = logging.getLogger("Device") self.serial = device_serial self.is_emulator = is_emulator self.adb = None self.telnet = None self.monkeyrunner = None self.view_client = None self.settings = {} self.display_info = None self.sdk_version = None self.release_version = None self.ro_debuggable = None self.ro_secure = None self.output_dir = output_dir if self.output_dir is None: self.output_dir = os.path.abspath("droidbot_out") if not os.path.exists(self.output_dir): os.mkdir(self.output_dir) self.use_hierarchy_viewer = use_hierarchy_viewer if self.is_emulator: self.adb_enabled = True self.telnet_enabled = True self.monkeyrunner_enabled = False self.view_client_enabled = True else: self.adb_enabled = True self.telnet_enabled = False self.monkeyrunner_enabled = False self.view_client_enabled = True self.is_connected = False self.connect() self.get_sdk_version() self.get_release_version() self.get_ro_secure() self.get_ro_debuggable() self.get_display_info() self.logcat = self.redirect_logcat() from state_monitor import StateMonitor self.state_monitor = StateMonitor(device=self) self.state_monitor.start() self.unlock()
def __init__(self, action_server): # action server for getting requests from AI self._action_server = action_server # current state of motion coordinator self._task = None self._first_task_seen = False # used for timeouts & extrapolating self._time_of_last_task = None self._timeout_vel_sent = False # to keep things thread safe self._lock = threading.RLock() # handles monitoring of state of drone self._state_monitor = StateMonitor() # handles communicating between tasks and LLM self._task_command_handler = TaskCommandHandler() self._idle_obstacle_avoider = IdleObstacleAvoider() self._avoid_magnitude = rospy.get_param("~obst_avoid_magnitude") self._kickout_distance = rospy.get_param('~kickout_distance') self._new_task_distance = rospy.get_param('~new_task_distance') self._safe_distance = rospy.get_param('~safe_distance') # safety self._safety_client = SafetyClient('motion_command_coordinator') self._safety_land_complete = False self._safety_land_requested = False # Create action client to request a safety landing self._action_client = actionlib.SimpleActionClient( "motion_planner_server", QuadMoveAction) try: # update rate for motion coordinator self._update_rate = rospy.get_param('~update_rate') # task timeout values self._task_timeout = rospy.Duration( rospy.get_param('~task_timeout')) # startup timeout self._startup_timeout = rospy.Duration( rospy.get_param('~startup_timeout')) except KeyError as e: rospy.logerr('Could not lookup a parameter for motion coordinator') raise
def __init__(self, device_serial, is_emulator=True, output_dir=None): """ create a device :param device_serial: serial number of target device :param is_emulator: boolean, type of device, True for emulator, False for real device :return: """ self.logger = logging.getLogger('Device') self.serial = device_serial # is_emulator 0 for real device, 1 for emulator self.is_emulator = is_emulator self.adb = None self.telnet = None self.monkeyrunner = None self.view_client = None self.settings = {} self.display_info = None self.output_dir = output_dir if self.output_dir is None: self.output_dir = os.path.abspath("droidbot_out") if not os.path.exists(self.output_dir): os.mkdir(self.output_dir) if self.is_emulator: self.adb_enabled = True self.telnet_enabled = True self.monkeyrunner_enabled = False self.view_client_enabled = True else: self.adb_enabled = True self.telnet_enabled = False self.monkeyrunner_enabled = False self.view_client_enabled = True self.is_connected = False self.connect() self.get_display_info() self.logcat = self.redirect_logcat() from state_monitor import StateMonitor self.state_monitor = StateMonitor(device=self) self.state_monitor.start()
class Device(object): """ this class describes a connected device """ def __init__(self, device_serial, is_emulator=True, output_dir=None): """ create a device :param device_serial: serial number of target device :param is_emulator: boolean, type of device, True for emulator, False for real device :return: """ self.logger = logging.getLogger("Device") self.serial = device_serial # is_emulator 0 for real device, 1 for emulator self.is_emulator = is_emulator self.adb = None self.telnet = None self.monkeyrunner = None self.view_client = None self.settings = {} self.display_info = None self.sdk_version = None self.release_version = None self.ro_debuggable = None self.ro_secure = None self.output_dir = output_dir if self.output_dir is None: self.output_dir = os.path.abspath("droidbot_out") if not os.path.exists(self.output_dir): os.mkdir(self.output_dir) if self.is_emulator: self.adb_enabled = True self.telnet_enabled = True self.monkeyrunner_enabled = False self.view_client_enabled = True else: self.adb_enabled = True self.telnet_enabled = False self.monkeyrunner_enabled = False self.view_client_enabled = True self.is_connected = False self.connect() self.get_sdk_version() self.get_release_version() self.get_ro_secure() self.get_ro_debuggable() self.get_display_info() self.logcat = self.redirect_logcat() from state_monitor import StateMonitor self.state_monitor = StateMonitor(device=self) self.state_monitor.start() self.unlock() # assert self.display_info is not None # self.check_connectivity() # print self.is_emulator, self.host, self.port def redirect_logcat(self, output_dir=None): if output_dir is None: output_dir = self.output_dir logcat_file = open("%s/logcat.log" % output_dir, "w") import subprocess subprocess.check_call(["adb", "-s", self.serial, "logcat", "-c"]) logcat = subprocess.Popen(["adb", "-s", self.serial, "logcat", "-v", "threadtime"], stdin=subprocess.PIPE, stdout=logcat_file) return logcat def check_connectivity(self): """ check if the device is available :return: Ture for available, False for not """ try: # try connecting to device self.logger.info("checking connectivity...") result = True if self.adb_enabled and self.adb and self.adb.check_connectivity(): self.logger.info("ADB is connected") else: self.logger.warning("ADB is not connected") result = False if self.telnet_enabled and self.telnet and self.telnet.check_connectivity(): self.logger.info("Telnet is connected") else: self.logger.warning("Telnet is not connected") result = False if self.monkeyrunner_enabled and self.monkeyrunner and self.monkeyrunner.check_connectivity(): self.logger.info("monkeyrunner is connected") else: self.logger.warning("monkeyrunner is not connected") result = False if self.view_client_enabled and self.view_client: self.logger.info("view_client is connected") else: self.logger.warning("view_client is not connected") result = False return result except: return False def wait_for_device(self): """ wait until the device is fully booted :return: """ self.logger.info("waiting for device") try: subprocess.check_call(["adb", "-s", self.serial, "wait-for-device"]) while True: out = subprocess.check_output(["adb", "-s", self.serial, "shell", "getprop", "init.svc.bootanim"]).split()[0] if out == "stopped": break time.sleep(3) except: self.logger.warning("error waiting for device") def connect(self): """ connect this device via adb, telnet and monkeyrunner :return: """ # wait for emulator to start self.wait_for_device() if self.adb_enabled: self.get_adb() if self.telnet_enabled: self.get_telnet() if self.monkeyrunner_enabled: self.get_monkeyrunner() if self.view_client_enabled: self.get_view_client() time.sleep(3) self.is_connected = True def disconnect(self): """ disconnect current device :return: """ self.is_connected = False if self.adb: self.adb.disconnect() if self.telnet: self.telnet.disconnect() if self.monkeyrunner: self.monkeyrunner.disconnect() if self.view_client: self.view_client.disconnect() self.logcat.terminate() self.state_monitor.stop() def get_telnet(self): """ get telnet connection of the device note that only emulator have telnet connection """ if self.telnet_enabled and self.telnet is None: from adapter.telnet import TelnetConsole, TelnetException try: self.telnet = TelnetConsole(self) except TelnetException: self.logger.warning("Cannot connect to telnet.") return self.telnet def get_adb(self): """ get adb connection of the device """ if self.adb_enabled and self.adb is None: from adapter.adb import ADB self.adb = ADB(self) return self.adb def get_monkeyrunner(self): """ get monkeyrunner connection of the device :return: """ if self.monkeyrunner_enabled and self.monkeyrunner is None: from adapter.monkey_runner import MonkeyRunner self.monkeyrunner = MonkeyRunner(self) return self.monkeyrunner def get_view_client(self): """ get view_client connection of the device :return: """ if self.view_client_enabled and self.view_client is None: from adapter.viewclient import ViewClient self.view_client = ViewClient(self, forceviewserveruse=False) return self.view_client def is_foreground(self, app): """ check if app is in foreground of device :param app: App :return: boolean """ if isinstance(app, str): package_name = app elif isinstance(app, App): package_name = app.get_package_name() else: return False top_activity_name = self.get_top_activity_name() if top_activity_name is None: return False return top_activity_name.startswith(package_name) def get_sdk_version(self): """ Get version of current SDK """ if self.sdk_version is None: self.sdk_version = self.get_adb().get_sdk_version() return self.sdk_version def get_release_version(self): """ Get version of current SDK """ if self.release_version is None: self.release_version = self.get_adb().get_release_version() return self.release_version def get_ro_secure(self): if self.ro_secure is None: self.ro_secure = self.get_adb().get_ro_secure() return self.ro_secure def get_ro_debuggable(self): if self.ro_debuggable is None: self.ro_debuggable = self.get_adb().get_release_version() return self.ro_debuggable def get_display_info(self, refresh=True): """ get device display infomation, including width, height, and density :return: dict, display_info @param refresh: if set to True, refresh the display info instead of using the old values """ if self.display_info is None or refresh: self.display_info = self.get_adb().getDisplayInfo() return self.display_info def get_width(self, refresh=False): display_info = self.get_display_info(refresh=refresh) width = 0 if "width" in display_info: width = display_info["width"] elif not refresh: width = self.get_width(refresh=True) else: self.logger.warning("get_width: width not in display_info") return width def get_height(self, refresh=False): display_info = self.get_display_info(refresh=refresh) height = 0 if "height" in display_info: height = display_info["height"] elif not refresh: height = self.get_width(refresh=True) else: self.logger.warning("get_height: height not in display_info") return height def unlock(self): """ unlock screen skip first-use tutorials etc :return: """ assert self.get_adb() is not None # unlock screen self.get_adb().unlock() # DONE skip first-use turorials, we don't have to def add_env(self, env): """ set env to the device :param env: instance of AppEnv """ self.logger.info("deploying env: %s" % env) env.deploy(self) def add_contact(self, contact_data): """ add a contact to device :param contact_data: dict of contact, should have keys like name, phone, email :return: """ assert self.get_adb() is not None contact_intent = Intent(prefix="start", action="android.intent.action.INSERT", mime_type="vnd.android.cursor.dir/contact", extra_string=contact_data) self.send_intent(intent=contact_intent) time.sleep(2) self.get_adb().press("BACK") time.sleep(2) self.get_adb().press("BACK") return True def receive_call(self, phone=DEFAULT_NUM): """ simulate a income phonecall :param phone: str, phonenum :return: """ assert self.get_telnet() is not None return self.get_telnet().run_cmd("gsm call %s" % phone) def cancel_call(self, phone=DEFAULT_NUM): """ cancel phonecall :param phone: str, phonenum :return: """ assert self.get_telnet() is not None return self.get_telnet().run_cmd("gsm cancel %s" % phone) def accept_call(self, phone=DEFAULT_NUM): """ accept phonecall :param phone: str, phonenum :return: """ assert self.get_telnet() is not None return self.get_telnet().run_cmd("gsm accept %s" % phone) def call(self, phone=DEFAULT_NUM): """ simulate a outcome phonecall :param phone: str, phonenum :return: """ call_intent = Intent(prefix='start', action="android.intent.action.CALL", data_uri="tel:%s" % phone) return self.send_intent(intent=call_intent) def send_sms(self, phone=DEFAULT_NUM, content=DEFAULT_CONTENT): """ send a SMS :param phone: str, phone number of receiver :param content: str, content of sms :return: """ send_sms_intent = Intent(prefix='start', action="android.intent.action.SENDTO", data_uri="sms:%s" % phone, extra_string={'sms_body': content}, extra_boolean={'exit_on_sent': 'true'}) self.send_intent(intent=send_sms_intent) time.sleep(2) self.get_adb().press('66') return True def receive_sms(self, phone=DEFAULT_NUM, content=DEFAULT_CONTENT): """ receive a SMS :param phone: str, phone number of sender :param content: str, content of sms :return: """ assert self.get_telnet() is not None return self.get_telnet().run_cmd("sms send %s '%s'" % (phone, content)) def set_gps(self, x, y): """ set GPS positioning to x,y :param x: float :param y: float :return: """ assert self.get_telnet() is not None return self.get_telnet().run_cmd("geo fix %s %s" % (x, y)) def set_continuous_gps(self, center_x, center_y, delta_x, delta_y): import threading gps_thread = threading.Thread( target=self.set_continuous_gps_blocked, args=(center_x, center_y, delta_x, delta_y)) gps_thread.start() return True def set_continuous_gps_blocked(self, center_x, center_y, delta_x, delta_y): """ simulate GPS on device via telnet this method is blocked @param center_x: x coordinate of GPS position @param center_y: y coordinate of GPS position @param delta_x: range of x coordinate @param delta_y: range of y coordinate """ import random while self.is_connected: x = random.random() * delta_x * 2 + center_x - delta_x y = random.random() * delta_y * 2 + center_y - delta_y self.set_gps(x, y) time.sleep(3) def get_settings(self): """ get device settings via adb """ db_name = "/data/data/com.android.providers.settings/databases/settings.db" system_settings = {} out = self.get_adb().shell("sqlite3 %s \"select * from %s\"" % (db_name, "system")) out_lines = out.splitlines() for line in out_lines: segs = line.split('|') if len(segs) != 3: continue system_settings[segs[1]] = segs[2] secure_settings = {} out = self.get_adb().shell("sqlite3 %s \"select * from %s\"" % (db_name, "secure")) out_lines = out.splitlines() for line in out_lines: segs = line.split('|') if len(segs) != 3: continue secure_settings[segs[1]] = segs[2] self.settings['system'] = system_settings self.settings['secure'] = secure_settings return self.settings def change_settings(self, table_name, name, value): """ dangerous method, by calling this, change settings.db in device be very careful for sql injection :param table_name: table name to work on, usually it is system or secure :param name: settings name to set :param value: settings value to set """ db_name = "/data/data/com.android.providers.settings/databases/settings.db" self.get_adb().shell("sqlite3 %s \"update '%s' set value='%s' where name='%s'\"" % (db_name, table_name, value, name)) return True def send_intent(self, intent): """ send an intent to device via am (ActivityManager) :param intent: instance of Intent :return: """ assert self.get_adb() is not None assert intent is not None if isinstance(intent, Intent): cmd = intent.get_cmd() else: cmd = intent return self.get_adb().shell(cmd) def send_event(self, event): """ send one event to device :param event: the event to be sent :return: """ self.logger.info("sending event: %s" % event) event.send(self) def start_app(self, app): """ start an app on the device :param app: instance of App, or str of package name :return: """ if isinstance(app, str): package_name = app elif isinstance(app, App): package_name = app.get_package_name() if app.get_main_activity(): package_name += "/%s" % app.get_main_activity() else: self.logger.warning("unsupported param " + app + " with type: ", type(app)) return intent = Intent(suffix=package_name) self.send_intent(intent) def get_top_activity_name(self): """ Get current activity """ data = self.get_adb().shell("dumpsys activity top").splitlines() regex = re.compile("\s*ACTIVITY ([A-Za-z0-9_.]+)/([A-Za-z0-9_.]+)") m = regex.search(data[1]) if m: return m.group(1) + "/" + m.group(2) return None def get_service_names(self): """ get current running services :return: list of services """ services = [] dat = self.get_adb().shell('dumpsys activity services') lines = dat.splitlines() service_re = re.compile('^.+ServiceRecord{.+ ([A-Za-z0-9_.]+)/.([A-Za-z0-9_.]+)}') for line in lines: m = service_re.search(line) if m: package = m.group(1) service = m.group(2) services.append("%s/%s" % (package, service)) return services def get_focused_window_name(self): return self.get_adb().getFocusedWindowName() def get_package_path(self, package_name): """ get installation path of a package (app) :param package_name: :return: package path of app in device """ dat = self.get_adb().shell('pm path %s' % package_name) package_path_re = re.compile('^package:(.+)$') m = package_path_re.match(dat) if m: path = m.group(1) return path.strip() return None def start_activity_via_monkey(self, package): """ use monkey to start activity @param package: package name of target activity """ cmd = 'monkey' if package: cmd += ' -p %s' % package out = self.get_adb().shell(cmd) if re.search(r"(Error)|(Cannot find 'App')", out, re.IGNORECASE | re.MULTILINE): raise RuntimeError(out) def install_app(self, app): """ install an app to device @param app: instance of App @return: """ assert isinstance(app, App) subprocess.check_call(["adb", "-s", self.serial, "uninstall", app.get_package_name()], stdout=subprocess.PIPE, stderr=subprocess.PIPE) subprocess.check_call(["adb", "-s", self.serial, "install", app.app_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE) package_info_file_name = "%s/dumpsys_package_%s.txt" % (self.output_dir, app.get_package_name()) package_info_file = open(package_info_file_name, "w") subprocess.check_call(["adb", "-s", self.serial, "shell", "dumpsys", "package", app.get_package_name()], stdout=package_info_file) package_info_file.close() def uninstall_app(self, app): assert isinstance(app, App) subprocess.check_call(["adb", "-s", self.serial, "uninstall", app.get_package_name()], stdout=subprocess.PIPE, stderr=subprocess.PIPE) def get_app_pid(self, app): package = app.get_package_name() name2pid = {} ps_out = self.get_adb().shell(["ps", "-t"]) ps_out_lines = ps_out.splitlines() ps_out_head = ps_out_lines[0].split() if ps_out_head[1] != "PID" or ps_out_head[-1] != "NAME": self.logger.warning("ps command output format error: %s" % ps_out_head) for ps_out_line in ps_out_lines[1:]: segs = ps_out_line.split() if len(segs) < 4: continue pid = int(segs[1]) name = segs[-1] name2pid[name] = pid if package in name2pid: return name2pid[package] possible_pids = [] for name in name2pid: if name.startswith(package): possible_pids.append(name2pid[name]) if len(possible_pids) > 0: return min(possible_pids) return None def push_file(self, local_file, remote_dir="/sdcard/"): """ push file/directory to target_dir :param local_file: path to file/directory in host machine :param remote_dir: path to target directory in device :return: """ if not os.path.exists(local_file): self.logger.warning("push_file file does not exist: %s" % local_file) self.get_adb().run_cmd(["push", local_file, remote_dir]) def pull_file(self, remote_file, local_file): self.get_adb().run_cmd(["pull", remote_file, local_file]) def take_screenshot(self): image = None received = self.get_adb().shell("screencap -p").replace("\r\n", "\n") import StringIO stream = StringIO.StringIO(received) try: from PIL import Image image = Image.open(stream) except IOError as e: self.logger.warning("exception in take_screenshot: %s" % e) return image def get_current_state(self): self.logger.info("getting current device state...") current_state = None try: view_client_views = self.dump_views() foreground_activity = self.get_top_activity_name() background_services = self.get_service_names() screenshot = self.take_screenshot() self.logger.info("finish getting current device state...") current_state = DeviceState(self, view_client_views=view_client_views, foreground_activity=foreground_activity, background_services=background_services, screenshot=screenshot) except Exception as e: self.logger.warning("exception in get_current_state: %s" % e) import traceback traceback.print_exc() return current_state def view_touch(self, x, y): self.get_adb().touch(x, y) def view_long_touch(self, x, y, duration=2000): """ Long touches at (x, y) @param duration: duration in ms This workaround was suggested by U{HaMi<http://stackoverflow.com/users/2571957/hami>} """ self.get_adb().longTouch(x, y, duration) def view_drag(self, (x0, y0), (x1, y1), duration): """ Sends drag event n PX (actually it's using C{input swipe} command. @param (x0, y0): starting point in PX @param (x1, y1): ending point in PX @param duration: duration of the event in ms """ self.get_adb().drag((x0, y0), (x1, y1), duration)
class Device(object): """ this class describes a connected device """ def __init__(self, device_serial, is_emulator=True, output_dir=None): """ create a device :param device_serial: serial number of target device :param is_emulator: boolean, type of device, True for emulator, False for real device :return: """ self.logger = logging.getLogger('Device') self.serial = device_serial # is_emulator 0 for real device, 1 for emulator self.is_emulator = is_emulator self.adb = None self.telnet = None self.monkeyrunner = None self.view_client = None self.settings = {} self.display_info = None self.output_dir = output_dir if self.output_dir is None: self.output_dir = os.path.abspath("droidbot_out") if not os.path.exists(self.output_dir): os.mkdir(self.output_dir) if self.is_emulator: self.adb_enabled = True self.telnet_enabled = True self.monkeyrunner_enabled = False self.view_client_enabled = True else: self.adb_enabled = True self.telnet_enabled = False self.monkeyrunner_enabled = False self.view_client_enabled = True self.is_connected = False self.connect() self.get_display_info() self.logcat = self.redirect_logcat() from state_monitor import StateMonitor self.state_monitor = StateMonitor(device=self) self.state_monitor.start() # assert self.display_info is not None # self.check_connectivity() # print self.is_emulator, self.host, self.port def redirect_logcat(self, output_dir=None): if output_dir is None: output_dir = self.output_dir logcat_file = open("%s/logcat.log" % output_dir, "w") import subprocess subprocess.check_call(["adb", "-s", self.serial, "logcat", "-c"]) logcat = subprocess.Popen(["adb", "-s", self.serial, "logcat", "-v", "threadtime"], stdin=subprocess.PIPE, stdout=logcat_file) return logcat def check_connectivity(self): """ check if the device is available :return: Ture for available, False for not """ try: # try connecting to device self.logger.info("checking connectivity...") result = True if self.adb_enabled and self.adb and self.adb.checkConnected(): self.logger.info("ADB is connected") else: self.logger.warning("ADB is not connected") result = False if self.telnet_enabled and self.telnet and self.telnet.check_connectivity(): self.logger.info("Telnet is connected") else: self.logger.warning("Telnet is not connected") result = False if self.monkeyrunner_enabled and self.monkeyrunner and self.monkeyrunner.check_connectivity(): self.logger.info("monkeyrunner is connected") else: self.logger.warning("monkeyrunner is not connected") result = False if self.view_client_enabled and self.view_client: self.logger.info("view_client is connected") else: self.logger.warning("view_client is not connected") result = False return result except: return False def wait_for_device(self): """ wait until the device is fully booted :return: """ self.logger.info("waiting for device") try: subprocess.check_call(["adb", "-s", self.serial, "wait-for-device"]) while True: out = subprocess.check_output(["adb", "-s", self.serial, "shell", "getprop", "init.svc.bootanim"]).split()[0] if out == "stopped": break time.sleep(3) except: self.logger.warning("error waiting for device") def connect(self): """ connect this device via adb, telnet and monkeyrunner :return: """ try: # wait for emulator to start self.wait_for_device() if self.adb_enabled: self.get_adb() if self.telnet_enabled: self.get_telnet() if self.monkeyrunner_enabled: self.get_monkeyrunner() if self.view_client_enabled: self.get_view_client() time.sleep(3) self.is_connected = True except connection.TelnetException: self.logger.warning("Cannot connect to telnet.") def disconnect(self): """ disconnect current device :return: """ self.is_connected = False if self.adb: self.adb.close() if self.telnet: self.telnet.disconnect() if self.monkeyrunner: self.monkeyrunner.disconnect() if self.view_client: self.view_client = None self.logcat.terminate() self.state_monitor.stop() def get_telnet(self): """ get telnet connection of the device note that only emulator have telnet connection """ if self.telnet_enabled and self.telnet is None: self.telnet = connection.TelnetConsole(self) return self.telnet def get_adb(self): """ get adb connection of the device """ if self.adb_enabled and self.adb is None: # use adbclient class in com.dtmilano.adb.adbclient self.adb, self.serial = ViewClient.connectToDeviceOrExit(verbose=True, serialno=self.serial) return self.adb def get_monkeyrunner(self): """ get monkeyrunner connection of the device :return: """ if self.monkeyrunner_enabled and self.monkeyrunner is None: self.monkeyrunner = connection.MonkeyRunner(self) return self.monkeyrunner def get_view_client(self): """ get view_client connection of the device :return: """ if self.view_client_enabled and self.view_client is None: kwargs = {'startviewserver': True, 'autodump': False, 'forceviewserveruse': True, 'ignoreuiautomatorkilled': True} self.view_client = ViewClient(self.adb, self.serial, **kwargs) return self.view_client def is_foreground(self, app): """ check if app is in foreground of device :param app: App :return: boolean """ if isinstance(app, str): package_name = app elif isinstance(app, App): package_name = app.get_package_name() else: return False focused_window_name = self.get_adb().getTopActivityName() if focused_window_name is None: return False return focused_window_name.startswith(package_name) def get_display_info(self): """ get device display infomation, including width, height, and density :return: dict, display_info """ if self.display_info is None: self.display_info = self.get_adb().getDisplayInfo() return self.display_info def device_prepare(self): """ unlock screen skip first-use tutorials etc :return: """ assert self.get_adb() is not None assert self.get_view_client() is not None # unlock screen self.get_adb().unlock() # DOWN skip first-use turorials, we don't have to def add_env(self, env): """ set env to the device :param env: instance of AppEnv """ self.logger.info("deploying env: %s" % env) env.deploy(self) def add_contact(self, contact_data): """ add a contact to device :param contact_data: dict of contact, should have keys like name, phone, email :return: """ assert self.get_adb() is not None contact_intent = Intent(prefix="start", action="android.intent.action.INSERT", mime_type="vnd.android.cursor.dir/contact", extra_string=contact_data) self.send_intent(intent=contact_intent) time.sleep(2) self.get_adb().press("BACK") time.sleep(2) self.get_adb().press("BACK") return True def receive_call(self, phone=DEFAULT_NUM): """ simulate a income phonecall :param phone: str, phonenum :return: """ assert self.get_telnet() is not None return self.get_telnet().run_cmd("gsm call %s" % phone) def cancel_call(self, phone=DEFAULT_NUM): """ cancel phonecall :param phone: str, phonenum :return: """ assert self.get_telnet() is not None return self.get_telnet().run_cmd("gsm cancel %s" % phone) def accept_call(self, phone=DEFAULT_NUM): """ accept phonecall :param phone: str, phonenum :return: """ assert self.get_telnet() is not None return self.get_telnet().run_cmd("gsm accept %s" % phone) def call(self, phone=DEFAULT_NUM): """ simulate a outcome phonecall :param phone: str, phonenum :return: """ call_intent = Intent(prefix='start', action="android.intent.action.CALL", data_uri="tel:%s" % phone) return self.send_intent(intent=call_intent) def send_sms(self, phone=DEFAULT_NUM, content=DEFAULT_CONTENT): """ send a SMS :param phone: str, phone number of receiver :param content: str, content of sms :return: """ send_sms_intent = Intent(prefix='start', action="android.intent.action.SENDTO", data_uri="sms:%s" % phone, extra_string={'sms_body': content}, extra_boolean={'exit_on_sent': 'true'}) self.send_intent(intent=send_sms_intent) time.sleep(2) self.get_adb().press('66') return True def receive_sms(self, phone=DEFAULT_NUM, content=DEFAULT_CONTENT): """ receive a SMS :param phone: str, phone number of sender :param content: str, content of sms :return: """ assert self.get_telnet() is not None return self.get_telnet().run_cmd("sms send %s '%s'" % (phone, content)) def set_gps(self, x, y): """ set GPS positioning to x,y :param x: float :param y: float :return: """ assert self.get_telnet() is not None return self.get_telnet().run_cmd("geo fix %s %s" % (x, y)) def set_continuous_gps(self, center_x, center_y, delta_x, delta_y): import threading gps_thread = threading.Thread( target=self.set_continuous_gps_blocked, args=(center_x, center_y, delta_x, delta_y)) gps_thread.start() return True def set_continuous_gps_blocked(self, center_x, center_y, delta_x, delta_y): """ simulate GPS on device via telnet this method is blocked @param center_x: x coordinate of GPS position @param center_y: y coordinate of GPS position @param delta_x: range of x coordinate @param delta_y: range of y coordinate """ import random while self.is_connected: x = random.random() * delta_x * 2 + center_x - delta_x y = random.random() * delta_y * 2 + center_y - delta_y self.set_gps(x, y) time.sleep(3) def get_settings(self): """ get device settings via adb """ db_name = "/data/data/com.android.providers.settings/databases/settings.db" system_settings = {} out = self.get_adb().shell("sqlite3 %s \"select * from %s\"" % (db_name, "system")) out_lines = out.splitlines() for line in out_lines: segs = line.split('|') if len(segs) != 3: continue system_settings[segs[1]] = segs[2] secure_settings = {} out = self.get_adb().shell("sqlite3 %s \"select * from %s\"" % (db_name, "secure")) out_lines = out.splitlines() for line in out_lines: segs = line.split('|') if len(segs) != 3: continue secure_settings[segs[1]] = segs[2] self.settings['system'] = system_settings self.settings['secure'] = secure_settings return self.settings def change_settings(self, table_name, name, value): """ dangerous method, by calling this, change settings.db in device be very careful for sql injection :param table_name: table name to work on, usually it is system or secure :param name: settings name to set :param value: settings value to set """ db_name = "/data/data/com.android.providers.settings/databases/settings.db" self.get_adb().shell("sqlite3 %s \"update '%s' set value='%s' where name='%s'\"" % (db_name, table_name, value, name)) return True def send_intent(self, intent): """ send an intent to device via am (ActivityManager) :param intent: instance of Intent :return: """ assert self.get_adb() is not None assert intent is not None cmd = intent.get_cmd() return self.get_adb().shell(cmd) def send_event(self, event): """ send one event to device :param event: the event to be sent :return: """ self.logger.info("sending event: %s" % event) event.send(self) def start_app(self, app): """ start an app on the device :param app: instance of App, or str of package name :return: """ if isinstance(app, str): package_name = app elif isinstance(app, App): package_name = app.get_package_name() if app.get_main_activity(): package_name += "/%s" % app.get_main_activity() else: self.logger.warning("unsupported param " + app + " with type: ", type(app)) return intent = Intent(suffix=package_name) self.send_intent(intent) def get_service_names(self): """ get current running services :return: list of services """ services = [] dat = self.get_adb().shell('dumpsys activity services') lines = dat.splitlines() service_re = re.compile('^.+ServiceRecord{.+ ([A-Za-z0-9_.]+)/.([A-Za-z0-9_.]+)}') for line in lines: m = service_re.search(line) if m: package = m.group(1) service = m.group(2) services.append("%s/%s" % (package, service)) return services def get_package_path(self, package_name): """ get installation path of a package (app) :param package_name: :return: package path of app in device """ dat = self.get_adb().shell('pm path %s' % package_name) package_path_re = re.compile('^package:(.+)$') m = package_path_re.match(dat) if m: path = m.group(1) return path.strip() return None def start_activity_via_monkey(self, package): """ use monkey to start activity @param package: package name of target activity """ cmd = 'monkey' if package: cmd += ' -p %s' % package out = self.get_adb().shell(cmd) if re.search(r"(Error)|(Cannot find 'App')", out, re.IGNORECASE | re.MULTILINE): raise RuntimeError(out) def install_app(self, app): """ install an app to device @param app: instance of App @return: """ assert isinstance(app, App) subprocess.check_call(["adb", "-s", self.serial, "uninstall", app.get_package_name()], stdout=subprocess.PIPE, stderr=subprocess.PIPE) subprocess.check_call(["adb", "-s", self.serial, "install", app.app_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE) package_info_file_name = "%s/dumpsys_package_%s.txt" % (self.output_dir, app.get_package_name()) package_info_file = open(package_info_file_name, "w") subprocess.check_call(["adb", "-s", self.serial, "shell", "dumpsys", "package", app.get_package_name()], stdout=package_info_file) package_info_file.close() def uninstall_app(self, app): assert isinstance(app, App) subprocess.check_call(["adb", "-s", self.serial, "uninstall", app.get_package_name()], stdout=subprocess.PIPE, stderr=subprocess.PIPE) def get_current_state(self): self.logger.info("getting current device state...") try: view_client_views = self.get_view_client().dump() foreground_activity = self.get_adb().getTopActivityName() background_services = self.get_service_names() snapshot = self.get_adb().takeSnapshot(reconnect=True) self.logger.info("finish getting current device state...") return DeviceState(self, view_client_views=view_client_views, foreground_activity=foreground_activity, background_services=background_services, snapshot=snapshot) except Exception as e: self.logger.warning(e) return None
class Device(object): """ this class describes a connected device """ def __init__(self, device_serial, is_emulator=True, output_dir=None): """ create a device :param device_serial: serial number of target device :param is_emulator: boolean, type of device, True for emulator, False for real device :return: """ self.logger = logging.getLogger('Device') self.serial = device_serial # is_emulator 0 for real device, 1 for emulator self.is_emulator = is_emulator self.adb = None self.telnet = None self.monkeyrunner = None self.view_client = None self.settings = {} self.display_info = None self.output_dir = output_dir if self.output_dir is None: self.output_dir = os.path.abspath("droidbot_out") if not os.path.exists(self.output_dir): os.mkdir(self.output_dir) if self.is_emulator: self.adb_enabled = True self.telnet_enabled = True self.monkeyrunner_enabled = False self.view_client_enabled = True else: self.adb_enabled = True self.telnet_enabled = False self.monkeyrunner_enabled = False self.view_client_enabled = True self.is_connected = False self.connect() self.get_display_info() self.logcat = self.redirect_logcat() from state_monitor import StateMonitor self.state_monitor = StateMonitor(device=self) self.state_monitor.start() # assert self.display_info is not None # self.check_connectivity() # print self.is_emulator, self.host, self.port def redirect_logcat(self, output_dir=None): if output_dir is None: output_dir = self.output_dir logcat_file = open("%s/logcat.log" % output_dir, "w") import subprocess subprocess.check_call(["adb", "-s", self.serial, "logcat", "-c"]) logcat = subprocess.Popen( ["adb", "-s", self.serial, "logcat", "-v", "threadtime"], stdin=subprocess.PIPE, stdout=logcat_file) return logcat def check_connectivity(self): """ check if the device is available :return: Ture for available, False for not """ try: # try connecting to device self.logger.info("checking connectivity...") result = True if self.adb_enabled and self.adb and self.adb.checkConnected(): self.logger.info("ADB is connected") else: self.logger.warning("ADB is not connected") result = False if self.telnet_enabled and self.telnet and self.telnet.check_connectivity( ): self.logger.info("Telnet is connected") else: self.logger.warning("Telnet is not connected") result = False if self.monkeyrunner_enabled and self.monkeyrunner and self.monkeyrunner.check_connectivity( ): self.logger.info("monkeyrunner is connected") else: self.logger.warning("monkeyrunner is not connected") result = False if self.view_client_enabled and self.view_client: self.logger.info("view_client is connected") else: self.logger.warning("view_client is not connected") result = False return result except: return False def wait_for_device(self): """ wait until the device is fully booted :return: """ self.logger.info("waiting for device") try: subprocess.check_call( ["adb", "-s", self.serial, "wait-for-device"]) while True: out = subprocess.check_output([ "adb", "-s", self.serial, "shell", "getprop", "init.svc.bootanim" ]).split()[0] if out == "stopped": break time.sleep(3) except: self.logger.warning("error waiting for device") def connect(self): """ connect this device via adb, telnet and monkeyrunner :return: """ try: # wait for emulator to start self.wait_for_device() if self.adb_enabled: self.get_adb() if self.telnet_enabled: self.get_telnet() if self.monkeyrunner_enabled: self.get_monkeyrunner() if self.view_client_enabled: self.get_view_client() time.sleep(3) self.is_connected = True except connection.TelnetException: self.logger.warning("Cannot connect to telnet.") def disconnect(self): """ disconnect current device :return: """ self.is_connected = False if self.adb: self.adb.close() if self.telnet: self.telnet.disconnect() if self.monkeyrunner: self.monkeyrunner.disconnect() if self.view_client: self.view_client = None self.logcat.terminate() self.state_monitor.stop() def get_telnet(self): """ get telnet connection of the device note that only emulator have telnet connection """ if self.telnet_enabled and self.telnet is None: self.telnet = connection.TelnetConsole(self) return self.telnet def get_adb(self): """ get adb connection of the device """ if self.adb_enabled and self.adb is None: # use adbclient class in com.dtmilano.adb.adbclient self.adb, self.serial = ViewClient.connectToDeviceOrExit( verbose=True, serialno=self.serial) return self.adb def get_monkeyrunner(self): """ get monkeyrunner connection of the device :return: """ if self.monkeyrunner_enabled and self.monkeyrunner is None: self.monkeyrunner = connection.MonkeyRunner(self) return self.monkeyrunner def get_view_client(self): """ get view_client connection of the device :return: """ if self.view_client_enabled and self.view_client is None: kwargs = { 'startviewserver': True, 'autodump': False, 'forceviewserveruse': True, 'ignoreuiautomatorkilled': True } self.view_client = ViewClient(self.adb, self.serial, **kwargs) return self.view_client def is_foreground(self, app): """ check if app is in foreground of device :param app: App :return: boolean """ if isinstance(app, str): package_name = app elif isinstance(app, App): package_name = app.get_package_name() else: return False focused_window_name = self.get_adb().getTopActivityName() if focused_window_name is None: return False return focused_window_name.startswith(package_name) def get_display_info(self): """ get device display infomation, including width, height, and density :return: dict, display_info """ if self.display_info is None: self.display_info = self.get_adb().getDisplayInfo() return self.display_info def device_prepare(self): """ unlock screen skip first-use tutorials etc :return: """ assert self.get_adb() is not None assert self.get_view_client() is not None # unlock screen self.get_adb().unlock() # DOWN skip first-use turorials, we don't have to def add_env(self, env): """ set env to the device :param env: instance of AppEnv """ self.logger.info("deploying env: %s" % env) env.deploy(self) def add_contact(self, contact_data): """ add a contact to device :param contact_data: dict of contact, should have keys like name, phone, email :return: """ assert self.get_adb() is not None contact_intent = Intent(prefix="start", action="android.intent.action.INSERT", mime_type="vnd.android.cursor.dir/contact", extra_string=contact_data) self.send_intent(intent=contact_intent) time.sleep(2) self.get_adb().press("BACK") time.sleep(2) self.get_adb().press("BACK") return True def receive_call(self, phone=DEFAULT_NUM): """ simulate a income phonecall :param phone: str, phonenum :return: """ assert self.get_telnet() is not None return self.get_telnet().run_cmd("gsm call %s" % phone) def cancel_call(self, phone=DEFAULT_NUM): """ cancel phonecall :param phone: str, phonenum :return: """ assert self.get_telnet() is not None return self.get_telnet().run_cmd("gsm cancel %s" % phone) def accept_call(self, phone=DEFAULT_NUM): """ accept phonecall :param phone: str, phonenum :return: """ assert self.get_telnet() is not None return self.get_telnet().run_cmd("gsm accept %s" % phone) def call(self, phone=DEFAULT_NUM): """ simulate a outcome phonecall :param phone: str, phonenum :return: """ call_intent = Intent(prefix='start', action="android.intent.action.CALL", data_uri="tel:%s" % phone) return self.send_intent(intent=call_intent) def send_sms(self, phone=DEFAULT_NUM, content=DEFAULT_CONTENT): """ send a SMS :param phone: str, phone number of receiver :param content: str, content of sms :return: """ send_sms_intent = Intent(prefix='start', action="android.intent.action.SENDTO", data_uri="sms:%s" % phone, extra_string={'sms_body': content}, extra_boolean={'exit_on_sent': 'true'}) self.send_intent(intent=send_sms_intent) time.sleep(2) self.get_adb().press('66') return True def receive_sms(self, phone=DEFAULT_NUM, content=DEFAULT_CONTENT): """ receive a SMS :param phone: str, phone number of sender :param content: str, content of sms :return: """ assert self.get_telnet() is not None return self.get_telnet().run_cmd("sms send %s '%s'" % (phone, content)) def set_gps(self, x, y): """ set GPS positioning to x,y :param x: float :param y: float :return: """ assert self.get_telnet() is not None return self.get_telnet().run_cmd("geo fix %s %s" % (x, y)) def set_continuous_gps(self, center_x, center_y, delta_x, delta_y): import threading gps_thread = threading.Thread(target=self.set_continuous_gps_blocked, args=(center_x, center_y, delta_x, delta_y)) gps_thread.start() return True def set_continuous_gps_blocked(self, center_x, center_y, delta_x, delta_y): """ simulate GPS on device via telnet this method is blocked @param center_x: x coordinate of GPS position @param center_y: y coordinate of GPS position @param delta_x: range of x coordinate @param delta_y: range of y coordinate """ import random while self.is_connected: x = random.random() * delta_x * 2 + center_x - delta_x y = random.random() * delta_y * 2 + center_y - delta_y self.set_gps(x, y) time.sleep(3) def get_settings(self): """ get device settings via adb """ db_name = "/data/data/com.android.providers.settings/databases/settings.db" system_settings = {} out = self.get_adb().shell("sqlite3 %s \"select * from %s\"" % (db_name, "system")) out_lines = out.splitlines() for line in out_lines: segs = line.split('|') if len(segs) != 3: continue system_settings[segs[1]] = segs[2] secure_settings = {} out = self.get_adb().shell("sqlite3 %s \"select * from %s\"" % (db_name, "secure")) out_lines = out.splitlines() for line in out_lines: segs = line.split('|') if len(segs) != 3: continue secure_settings[segs[1]] = segs[2] self.settings['system'] = system_settings self.settings['secure'] = secure_settings return self.settings def change_settings(self, table_name, name, value): """ dangerous method, by calling this, change settings.db in device be very careful for sql injection :param table_name: table name to work on, usually it is system or secure :param name: settings name to set :param value: settings value to set """ db_name = "/data/data/com.android.providers.settings/databases/settings.db" self.get_adb().shell( "sqlite3 %s \"update '%s' set value='%s' where name='%s'\"" % (db_name, table_name, value, name)) return True def send_intent(self, intent): """ send an intent to device via am (ActivityManager) :param intent: instance of Intent :return: """ assert self.get_adb() is not None assert intent is not None cmd = intent.get_cmd() return self.get_adb().shell(cmd) def send_event(self, event): """ send one event to device :param event: the event to be sent :return: """ self.logger.info("sending event: %s" % event) event.send(self) def start_app(self, app): """ start an app on the device :param app: instance of App, or str of package name :return: """ if isinstance(app, str): package_name = app elif isinstance(app, App): package_name = app.get_package_name() if app.get_main_activity(): package_name += "/%s" % app.get_main_activity() else: self.logger.warning("unsupported param " + app + " with type: ", type(app)) return intent = Intent(suffix=package_name) self.send_intent(intent) def get_service_names(self): """ get current running services :return: list of services """ services = [] dat = self.get_adb().shell('dumpsys activity services') lines = dat.splitlines() service_re = re.compile( '^.+ServiceRecord{.+ ([A-Za-z0-9_.]+)/.([A-Za-z0-9_.]+)}') for line in lines: m = service_re.search(line) if m: package = m.group(1) service = m.group(2) services.append("%s/%s" % (package, service)) return services def get_package_path(self, package_name): """ get installation path of a package (app) :param package_name: :return: package path of app in device """ dat = self.get_adb().shell('pm path %s' % package_name) package_path_re = re.compile('^package:(.+)$') m = package_path_re.match(dat) if m: path = m.group(1) return path.strip() return None def start_activity_via_monkey(self, package): """ use monkey to start activity @param package: package name of target activity """ cmd = 'monkey' if package: cmd += ' -p %s' % package out = self.get_adb().shell(cmd) if re.search(r"(Error)|(Cannot find 'App')", out, re.IGNORECASE | re.MULTILINE): raise RuntimeError(out) def install_app(self, app): """ install an app to device @param app: instance of App @return: """ assert isinstance(app, App) subprocess.check_call( ["adb", "-s", self.serial, "uninstall", app.get_package_name()], stdout=subprocess.PIPE, stderr=subprocess.PIPE) subprocess.check_call( ["adb", "-s", self.serial, "install", app.app_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE) package_info_file_name = "%s/dumpsys_package_%s.txt" % ( self.output_dir, app.get_package_name()) package_info_file = open(package_info_file_name, "w") subprocess.check_call([ "adb", "-s", self.serial, "shell", "dumpsys", "package", app.get_package_name() ], stdout=package_info_file) package_info_file.close() def uninstall_app(self, app): assert isinstance(app, App) subprocess.check_call( ["adb", "-s", self.serial, "uninstall", app.get_package_name()], stdout=subprocess.PIPE, stderr=subprocess.PIPE) def get_current_state(self): self.logger.info("getting current device state...") try: view_client_views = self.get_view_client().dump() foreground_activity = self.get_adb().getTopActivityName() background_services = self.get_service_names() snapshot = self.get_adb().takeSnapshot(reconnect=True) self.logger.info("finish getting current device state...") return DeviceState(self, view_client_views=view_client_views, foreground_activity=foreground_activity, background_services=background_services, snapshot=snapshot) except Exception as e: self.logger.warning(e) return None
def run_test(load_balancer, workload, debug): sm = StateMonitor() create_count = 0 # Keeps track of how many creates went through remove_count = 0 # Keeps track of how many removes went through with open(workload) as f: # returns (None, sm.failed) if there are any errors # otherwise runs through the workload for line in tqdm.tqdm(f, total=get_num_lines(workload)): # Parse command command, arg = line.split() if command == 'create': if debug: print('\n{} {}'.format(command, arg)) try: check_valid(sm, load_balancer.shards, debug) except Error as e: print('\nbefore create {}\n{}'.format(create_count, e)) return None, sm.failed load_balancer.add_shard(arg) try: check_valid(sm, load_balancer.shards, debug) except Error as e: print('\nafter create {}\n{}'.format(create_count, e)) return None, sm.failed create_count += 1 elif command == 'remove': if debug: print('\n{} {}'.format(command, arg)) try: check_valid(sm, load_balancer.shards, debug) except Error as e: print('\nbefore remove {}\n{}'.format(create_count, e)) return None, sm.failed load_balancer.remove_shard(arg) try: check_valid(sm, load_balancer.shards, debug) except Error as e: print('\nafter remove {}\n{}'.format(create_count, e)) return None, sm.failed remove_count += 1 elif command == 'put': load_balancer.put(arg) sm.put(arg) # Final check try: check_valid(sm, load_balancer.shards, debug) except Error as e: print('\nat the end\n{}'.format(e)) return None, sm.failed stats = sm.get_stats(load_balancer.shards, debug) return stats, sm.failed
class MotionCommandCoordinator(object): def __init__(self, action_server): # action server for getting requests from AI self._action_server = action_server # current state of motion coordinator self._task = None self._first_task_seen = False # used for timeouts & extrapolating self._time_of_last_task = None self._timeout_vel_sent = False # to keep things thread safe self._lock = threading.RLock() # handles monitoring of state of drone self._state_monitor = StateMonitor() # handles communicating between tasks and LLM self._task_command_handler = TaskCommandHandler() self._idle_obstacle_avoider = IdleObstacleAvoider() self._avoid_magnitude = rospy.get_param("~obst_avoid_magnitude") self._kickout_distance = rospy.get_param('~kickout_distance') self._new_task_distance = rospy.get_param('~new_task_distance') self._safe_distance = rospy.get_param('~safe_distance') # safety self._safety_client = SafetyClient('motion_command_coordinator') self._safety_land_complete = False self._safety_land_requested = False # Create action client to request a safety landing self._action_client = actionlib.SimpleActionClient( "motion_planner_server", QuadMoveAction) try: # update rate for motion coordinator self._update_rate = rospy.get_param('~update_rate') # task timeout values self._task_timeout = rospy.Duration( rospy.get_param('~task_timeout')) # startup timeout self._startup_timeout = rospy.Duration( rospy.get_param('~startup_timeout')) except KeyError as e: rospy.logerr('Could not lookup a parameter for motion coordinator') raise def run(self): # rate limiting of updates of motion coordinator rate = rospy.Rate(self._update_rate) # waiting for dependencies to be ready while rospy.Time.now() == rospy.Time(0) and not rospy.is_shutdown(): rospy.sleep(0.005) if rospy.is_shutdown(): raise rospy.ROSInterruptException() start_time = rospy.Time.now() if not self._action_client.wait_for_server(self._startup_timeout): raise IARCFatalSafetyException( 'Motion Coordinator could not initialize action client') self._state_monitor.wait_until_ready(self._startup_timeout - (rospy.Time.now() - start_time)) self._task_command_handler.wait_until_ready(self._startup_timeout - (rospy.Time.now() - start_time)) self._idle_obstacle_avoider.wait_until_ready(self._startup_timeout - (rospy.Time.now() - start_time)) AbstractTask().topic_buffer.wait_until_ready(self._startup_timeout - (rospy.Time.now() - start_time)) # forming bond with safety client if not self._safety_client.form_bond(): raise IARCFatalSafetyException( 'Motion Coordinator could not form bond with safety client') while not rospy.is_shutdown(): with self._lock: # Exit immediately if fatal if self._safety_client.is_fatal_active(): raise IARCFatalSafetyException( 'Safety Client is fatal active') elif self._safety_land_complete: return # Land if put into safety mode if self._safety_client.is_safety_active( ) and not self._safety_land_requested: # Request landing goal = QuadMoveGoal(movement_type="land", preempt=True) self._action_client.send_goal( goal, done_cb=self._safety_task_complete_callback) rospy.logwarn( 'motion coordinator attempting to execute safety land') self._safety_land_requested = True self._state_monitor.signal_safety_active() # set the time of last task to now if we have not seen a task yet if not self._first_task_seen: self._time_of_last_task = rospy.Time.now() self._first_task_seen = True closest_obstacle_dist = self._idle_obstacle_avoider.get_distance_to_obstacle( ) if self._task is None and self._action_server.has_new_task(): new_task = self._action_server.get_new_task() if not self._state_monitor.check_transition(new_task): rospy.logerr( 'Illegal task transition request requested in motion coordinator. Aborting requested task.' ) self._action_server.set_aborted() elif not closest_obstacle_dist >= self._new_task_distance: rospy.logerr( 'Attempt to start task too close to obstacle.' + ' Aborting requested task.') self._action_server.set_aborted() else: self._time_of_last_task = None self._task = new_task self._task_command_handler.new_task( new_task, self._get_current_transition()) if self._task is not None: task_canceled = False if self._action_server.is_canceled(): task_canceled = self._task_command_handler.cancel_task( ) if not task_canceled and closest_obstacle_dist < self._kickout_distance: # Abort current task task_canceled = self._task_command_handler.abort_task( 'Too close to obstacle') # if canceling task did not result in an error if not task_canceled: self._task_command_handler.run() task_state = self._task_command_handler.get_state() # handles state of task, motion coordinator, and action server if isinstance(task_state, task_states.TaskCanceled): self._action_server.set_canceled() rospy.logwarn('Task was canceled') self._task = None elif isinstance(task_state, task_states.TaskAborted): rospy.logwarn('Task aborted with: %s', task_state.msg) self._action_server.set_aborted() self._task = None elif isinstance(task_state, task_states.TaskFailed): rospy.logwarn('Task failed with: %s', task_state.msg) self._action_server.set_succeeded(False) self._task = None elif isinstance(task_state, task_states.TaskDone): self._action_server.set_succeeded(True) self._task = None elif not isinstance(task_state, task_states.TaskRunning): rospy.logerr( "Invalid task state returned, aborting task") self._action_server.set_aborted() self._task = None task_state = task_states.TaskAborted( msg='Invalid task state returned') # as soon as we set a task to None, start time # and send ending state to State Monitor if self._task is None: self._time_of_last_task = rospy.Time.now() self._timeout_vel_sent = False self._state_monitor.set_last_task_end_state(task_state) # No task is running, run obstacle avoider else: vel = AbstractTask.topic_buffer.get_linear_motion_profile_generator( ).expected_point_at_time( rospy.Time.now()).motion_point.twist.linear vel_vec_2d = np.array([vel.x, vel.y], dtype=np.float) avoid_vector, acceleration = self._idle_obstacle_avoider.get_safest( vel_vec_2d) avoid_twist = TwistStamped() avoid_twist.header.stamp = rospy.Time.now() avoid_twist.twist.linear.x = avoid_vector[0] avoid_twist.twist.linear.y = avoid_vector[1] self._task_command_handler.send_timeout( avoid_twist, acceleration=acceleration) rospy.logwarn_throttle( 1.0, 'Task running timeout. Running obstacle avoider') rate.sleep() # fills out the Intermediary State for the task def _get_current_transition(self): state = TransitionData() state.last_twist = self._task_command_handler.get_last_twist() state.last_task_ending_state = self._task_command_handler.get_state() state.timeout_sent = self._timeout_vel_sent return self._state_monitor.fill_out_transition(state) # callback for safety task completition def _safety_task_complete_callback(self, status, response): with self._lock: if response.success: rospy.logwarn( 'Motion Coordinator supposedly safely landed the aircraft') else: rospy.logerr('Motion Coordinator did not safely land aircraft') self._safety_land_complete = True