class WorkerBase(ABC): def __init__(self, args, id, last_known_state, websocket_handler, route_manager_daytime, route_manager_nighttime, devicesettings, db_wrapper, timer, pogoWindowManager, NoOcr=True): # self.thread_pool = ThreadPool(processes=2) self._route_manager_daytime = route_manager_daytime self._route_manager_nighttime = route_manager_nighttime self._route_manager_last_time = None self._websocket_handler = websocket_handler self._communicator = Communicator(websocket_handler, id, args.websocket_command_timeout) self._id = id self._applicationArgs = args self._last_known_state = last_known_state self._work_mutex = Lock() self.loop = None self.loop_started = Event() self.loop_tid = None self._async_io_looper_thread = None self._location_count = 0 self._timer = timer self._lastScreenshotTaken = 0 self._stop_worker_event = Event() self._db_wrapper = db_wrapper self._redErrorCount = 0 self._lastScreenHash = None self._lastScreenHashCount = 0 self._devicesettings = devicesettings self._resocalc = Resocalculator self._screen_x = 0 self._screen_y = 0 self._lastStart = "" self._pogoWindowManager = pogoWindowManager self.current_location = self._last_known_state.get( "last_location", None) if self.current_location is None: self.current_location = Location(0.0, 0.0) self.last_location = Location(0.0, 0.0) self.last_processed_location = Location(0.0, 0.0) @abstractmethod def _pre_work_loop(self): """ Work to be done before the main while true work-loop Start off asyncio loops etc in here :return: """ pass @abstractmethod def _health_check(self): """ Health check before a location is grabbed. Internally, a self._start_pogo call is already executed since that usually includes a topmost check :return: """ pass @abstractmethod def _pre_location_update(self): """ Override to run stuff like update injections settings in MITM worker Runs before walk/teleport to the location previously grabbed :return: """ pass @abstractmethod def _move_to_location(self): """ Location has previously been grabbed, the overriden function will be called. You may teleport or walk by your choosing Any post walk/teleport delays/sleeps have to be run in the derived, override method :return: """ pass @abstractmethod def _post_move_location_routine(self, timestamp): """ Routine called after having moved to a new location. MITM worker e.g. has to wait_for_data :param timestamp: :return: """ @abstractmethod def _start_pogo(self): """ Routine to start pogo. Return the state as a boolean do indicate a successful start :return: """ pass @abstractmethod def _cleanup(self): """ Cleanup any threads you started in derived classes etc self.stop_worker() and self.loop.stop() will be called afterwards :return: """ @abstractmethod def _valid_modes(self): """ Return a list of valid modes for the health checks :return: """ def _start_asyncio_loop(self): self.loop = asyncio.new_event_loop() asyncio.set_event_loop(self.loop) self.loop_tid = current_thread() self.loop.call_soon(self.loop_started.set) self.loop.run_forever() def _add_task_to_loop(self, coro): f = functools.partial(self.loop.create_task, coro) if current_thread() == self.loop_tid: return f( ) # We can call directly if we're not going between threads. else: # We're in a non-event loop thread so we use a Future # to get the task from the event loop thread once # it's ready. return self.loop.call_soon_threadsafe(f) def start_worker(self): # async_result = self.thread_pool.apply_async(self._main_work_thread, ()) t_main_work = Thread(target=self._main_work_thread) t_main_work.daemon = False t_main_work.start() # do some other stuff in the main process while not self._stop_worker_event.isSet(): time.sleep(1) t_main_work.join() log.info("Worker %s stopping gracefully" % str(self._id)) # async_result.get() return self._last_known_state def stop_worker(self): self._stop_worker_event.set() log.warning("Worker %s stop called" % str(self._id)) def _internal_pre_work(self): current_thread().name = self._id self._work_mutex.acquire() try: self._turn_screen_on_and_start_pogo() except WebsocketWorkerRemovedException: log.error("Timeout during init of worker %s" % str(self._id)) # no cleanup required here? TODO: signal websocket server somehow self._stop_worker_event.set() return self._work_mutex.release() self._async_io_looper_thread = Thread(name=str(self._id) + '_asyncio_' + self._id, target=self._start_asyncio_loop) self._async_io_looper_thread.daemon = False self._async_io_looper_thread.start() self.loop_started.wait() self._pre_work_loop() def _internal_health_check(self): # check if pogo is topmost and start if necessary log.debug( "_internal_health_check: Calling _start_pogo routine to check if pogo is topmost" ) self._work_mutex.acquire() log.debug("_internal_health_check: worker lock acquired") log.debug("Checking if we need to restart pogo") # Restart pogo every now and then... if self._devicesettings.get("restart_pogo", 80) > 0: # log.debug("main: Current time - lastPogoRestart: %s" % str(curTime - lastPogoRestart)) # if curTime - lastPogoRestart >= (args.restart_pogo * 60): if self._location_count > self._devicesettings.get( "restart_pogo", 80): log.error("scanned " + str(self._devicesettings.get("restart_pogo", 80)) + " locations, restarting pogo") pogo_started = self._restart_pogo() self._location_count = 0 else: pogo_started = self._start_pogo() else: pogo_started = self._start_pogo() self._work_mutex.release() log.debug("_internal_health_check: worker lock released") return pogo_started def _internal_cleanup(self): # set the event just to make sure - in case of exceptions for example self._stop_worker_event.set() log.info("Internal cleanup of %s started" % str(self._id)) self._cleanup() log.info("Internal cleanup of %s signalling end to websocketserver" % str(self._id)) self._route_manager_daytime.unregister_worker(self._id) if self._route_manager_nighttime is not None: self._route_manager_nighttime.unregister_worker(self._id) log.info("Stopping Route") # self.stop_worker() if self._async_io_looper_thread is not None: log.info("Stopping worker's asyncio loop") self.loop.call_soon_threadsafe(self.loop.stop) self._async_io_looper_thread.join() if self._timer is not None: log.info("Stopping switch timer") self._timer.stop_switch() self._communicator.cleanup_websocket(self) log.info("Internal cleanup of %s finished" % str(self._id)) def _main_work_thread(self): # TODO: signal websocketserver the removal try: self._internal_pre_work() except (InternalStopWorkerException, WebsocketWorkerRemovedException, WebsocketWorkerTimeoutException) \ as e: log.error( "Failed initializing worker %s, connection terminated exceptionally" % str(self._id)) return while not self._stop_worker_event.isSet(): while self._timer.get_switch( ) and self._route_manager_nighttime is None: time.sleep(1) # check if stop_worker_event is set again since sleep may have taken ages ;) if self._stop_worker_event.is_set(): break try: # TODO: consider getting results of health checks and aborting the entire worker? self._internal_health_check() self._health_check() except (InternalStopWorkerException, WebsocketWorkerRemovedException, WebsocketWorkerTimeoutException) \ as e: log.error( "Websocket connection to %s lost while running healthchecks, " "connection terminated exceptionally" % str(self._id)) break try: settings = self._internal_grab_next_location() if settings is None and self._timer.get_switch(): continue except (InternalStopWorkerException, WebsocketWorkerRemovedException, WebsocketWorkerTimeoutException) \ as e: log.warning( "Worker of %s does not support mode that's to be run, " "connection terminated exceptionally" % str(self._id)) break try: log.debug('Checking if new location is valid') valid = self._check_location_is_valid() if not valid: break except (InternalStopWorkerException, WebsocketWorkerRemovedException, WebsocketWorkerTimeoutException) \ as e: log.warning("Worker %s get non valid coords!" % str(self._id)) break try: self._pre_location_update() except (InternalStopWorkerException, WebsocketWorkerRemovedException, WebsocketWorkerTimeoutException) \ as e: log.warning( "Worker of %s stopping because of stop signal in pre_location_update, " "connection terminated exceptionally" % str(self._id)) break try: log.debug( 'main worker %s: LastLat: %s, LastLng: %s, CurLat: %s, CurLng: %s' % (str(self._id), self.last_location.lat, self.last_location.lng, self.current_location.lat, self.current_location.lng)) time_snapshot, process_location = self._move_to_location() except (InternalStopWorkerException, WebsocketWorkerRemovedException, WebsocketWorkerTimeoutException) \ as e: log.warning( "Worker %s failed moving to new location, stopping worker, " "connection terminated exceptionally" % str(self._id)) break if process_location: self._add_task_to_loop(self._update_position_file()) self._location_count += 1 if self._applicationArgs.last_scanned: log.info('main: Set new scannedlocation in Database') # self.update_scanned_location(currentLocation.lat, currentLocation.lng, curTime) self._add_task_to_loop( self.update_scanned_location(self.current_location.lat, self.current_location.lng, time_snapshot)) try: self._post_move_location_routine(time_snapshot) except (InternalStopWorkerException, WebsocketWorkerRemovedException, WebsocketWorkerTimeoutException) \ as e: log.warning( "Worker %s failed running post_move_location_routine, stopping worker" % str(self._id)) break log.info("Worker %s finished iteration, continuing work" % str(self._id)) self._internal_cleanup() async def _update_position_file(self): log.debug("Updating .position file") if self.current_location is not None: with open(self._id + '.position', 'w') as outfile: outfile.write( str(self.current_location.lat) + ", " + str(self.current_location.lng)) async def update_scanned_location(self, latitude, longitude, timestamp): try: self._db_wrapper.set_scanned_location(str(latitude), str(longitude), str(timestamp)) except Exception as e: log.error("Failed updating scanned location: %s" % str(e)) return def _get_currently_valid_routemanager(self): valid_modes = self._valid_modes() switch_mode = self._timer.get_switch() if (switch_mode and self._route_manager_nighttime is not None and self._route_manager_nighttime.mode in valid_modes): if self._route_manager_last_time != self._route_manager_nighttime: self._route_manager_daytime.unregister_worker(self._id) # TODO: check if result is positive/negative? self._route_manager_nighttime.register_worker(self._id) self._route_manager_last_time = self._route_manager_nighttime return self._route_manager_nighttime elif switch_mode is True and self._route_manager_nighttime is None: if self._route_manager_last_time is not None: self._route_manager_daytime.unregister_worker(self._id) self._route_manager_last_time = None return None elif not switch_mode and self._route_manager_daytime.mode in valid_modes: if self._route_manager_last_time != self._route_manager_daytime: if self._route_manager_nighttime is not None: self._route_manager_nighttime.unregister_worker(self._id) self._route_manager_daytime.register_worker(self._id) self._route_manager_last_time = self._route_manager_daytime return self._route_manager_daytime else: # log.fatal("Raising internal worker exception") raise InternalStopWorkerException def _internal_grab_next_location(self): # TODO: consider adding runWarningThreadEvent.set() self.last_location = self.current_location self._last_known_state["last_location"] = self.last_location log.debug("Requesting next location from routemanager") # requesting a location is blocking (iv_mitm will wait for a prioQ item), we really need to clean # the workers up... routemanager = self._get_currently_valid_routemanager() if routemanager is None: return None else: self.current_location = routemanager.get_next_location() return routemanager.settings def _init_routine(self): if self._applicationArgs.initial_restart is False: self._turn_screen_on_and_start_pogo() else: if not self._start_pogo(): while not self._restart_pogo(): log.warning("failed starting pogo") # TODO: stop after X attempts def _check_location_is_valid(self): if self.current_location is None: log.info('Current Location is None') while self._timer.get_switch(): log.info('Sleeping - Route is finished') time.sleep(30) elif self.current_location is not None: log.debug('Coords are valid') return True def _turn_screen_on_and_start_pogo(self): if not self._communicator.isScreenOn(): self._communicator.startApp("de.grennith.rgc.remotegpscontroller") log.warning("Turning screen on") self._communicator.turnScreenOn() time.sleep(self._devicesettings.get("post_turn_screen_on_delay", 2)) # check if pogo is running and start it if necessary log.warning("turnScreenOnAndStartPogo: (Re-)Starting Pogo") self._start_pogo() def _check_screen_on(self): if not self._communicator.isScreenOn(): self._communicator.startApp("de.grennith.rgc.remotegpscontroller") log.warning("Turning screen on") self._communicator.turnScreenOn() time.sleep(self._devicesettings.get("post_turn_screen_on_delay", 2)) def _stop_pogo(self): attempts = 0 stop_result = self._communicator.stopApp("com.nianticlabs.pokemongo") pogoTopmost = self._communicator.isPogoTopmost() while pogoTopmost: attempts += 1 if attempts > 10: return False stop_result = self._communicator.stopApp( "com.nianticlabs.pokemongo") time.sleep(1) pogoTopmost = self._communicator.isPogoTopmost() return stop_result def _reboot(self): try: start_result = self._communicator.reboot() except WebsocketWorkerRemovedException as e: log.error( "Could not reboot due to client already having disconnected") start_result = False time.sleep(5) self._db_wrapper.save_last_reboot(self._id) self.stop_worker() return start_result def _start_pogodroid(self): start_result = self._communicator.startApp("com.mad.pogodroid") time.sleep(5) return start_result def _stopPogoDroid(self): stopResult = self._communicator.stopApp("com.mad.pogodroid") return stopResult def _restart_pogo(self, clear_cache=True): successful_stop = self._stop_pogo() self._db_wrapper.save_last_restart(self._id) log.debug("restartPogo: stop pogo resulted in %s" % str(successful_stop)) if successful_stop: if clear_cache: self._communicator.clearAppCache("com.nianticlabs.pokemongo") time.sleep(1) return self._start_pogo() else: return False def _restartPogoDroid(self): successfulStop = self._stopPogoDroid() time.sleep(1) log.debug("restartPogoDroid: stop pogodriud resulted in %s" % str(successfulStop)) if successfulStop: return self._start_pogodroid() else: return False def _reopenRaidTab(self): log.debug("_reopenRaidTab: Taking screenshot...") log.info( "reopenRaidTab: Attempting to retrieve screenshot before checking raidtab" ) if not self._takeScreenshot(): log.debug("_reopenRaidTab: Failed getting screenshot...") log.error( "reopenRaidTab: Failed retrieving screenshot before checking for closebutton" ) return log.debug("_reopenRaidTab: Checking close except nearby...") pathToPass = os.path.join(self._applicationArgs.temp_path, 'screenshot%s.png' % str(self._id)) log.debug("Path: %s" % str(pathToPass)) self._pogoWindowManager.checkCloseExceptNearbyButton( pathToPass, self._id, self._communicator, 'True') log.debug("_reopenRaidTab: Getting to raidscreen...") self._getToRaidscreen(3) time.sleep(1) def _takeScreenshot(self, delayAfter=0.0, delayBefore=0.0): log.debug("Taking screenshot...") time.sleep(delayBefore) compareToTime = time.time() - self._lastScreenshotTaken log.debug("Last screenshot taken: %s" % str(self._lastScreenshotTaken)) if self._applicationArgs.use_media_projection: take_screenshot = self._communicator.getScreenshot( os.path.join(self._applicationArgs.temp_path, 'screenshot%s.png' % str(self._id))) else: take_screenshot = self._communicator.get_screenshot_single( os.path.join(self._applicationArgs.temp_path, 'screenshot%s.png' % str(self._id))) if self._lastScreenshotTaken and compareToTime < 0.5: log.debug( "takeScreenshot: screenshot taken recently, returning immediately" ) log.debug("Screenshot taken recently, skipping") return True # TODO: screenshot.png needs identifier in name elif not take_screenshot: log.error("takeScreenshot: Failed retrieving screenshot") log.debug("Failed retrieving screenshot") return False else: log.debug("Success retrieving screenshot") self._lastScreenshotTaken = time.time() time.sleep(delayAfter) return True def _checkPogoFreeze(self): log.debug("Checking if pogo froze") if not self._takeScreenshot(): log.debug("_checkPogoFreeze: failed retrieving screenshot") return from utils.image_utils import getImageHash screenHash = getImageHash( os.path.join(self._applicationArgs.temp_path, 'screenshot%s.png' % str(self._id))) log.debug("checkPogoFreeze: Old Hash: " + str(self._lastScreenHash)) log.debug("checkPogoFreeze: New Hash: " + str(screenHash)) if hamming_dist(str(self._lastScreenHash), str(screenHash)) < 4 and str( self._lastScreenHash) != '0': log.debug( "checkPogoFreeze: New und old Screenshoot are the same - no processing" ) self._lastScreenHashCount += 1 log.debug("checkPogoFreeze: Same Screen Count: " + str(self._lastScreenHashCount)) if self._lastScreenHashCount >= 100: self._lastScreenHashCount = 0 self._restart_pogo() else: self._lastScreenHash = screenHash self._lastScreenHashCount = 0 log.debug("_checkPogoFreeze: done") def _check_pogo_main_screen(self, maxAttempts, again=False): log.debug( "_check_pogo_main_screen: Trying to get to the Mainscreen with %s max attempts..." % str(maxAttempts)) pogoTopmost = self._communicator.isPogoTopmost() if not pogoTopmost: return False self._checkPogoFreeze() if not self._takeScreenshot( delayBefore=self._applicationArgs.post_screenshot_delay): if again: log.error( "_check_pogo_main_screen: failed getting a screenshot again" ) return False attempts = 0 if os.path.isdir( os.path.join(self._applicationArgs.temp_path, 'screenshot%s.png' % str(self._id))): log.error( "_check_pogo_main_screen: screenshot.png is not a file/corrupted" ) return False log.info("_check_pogo_main_screen: checking mainscreen") buttoncheck = self._pogoWindowManager.lookForButton( os.path.join(self._applicationArgs.temp_path, 'screenshot%s.png' % str(self._id)), 2.20, 3.01, self._communicator) if buttoncheck: log.info('Found button on screen') self._takeScreenshot( delayBefore=self._applicationArgs.post_screenshot_delay) while not self._pogoWindowManager.checkpogomainscreen( os.path.join(self._applicationArgs.temp_path, 'screenshot%s.png' % str(self._id)), self._id): log.error("_check_pogo_main_screen: not on Mainscreen...") if attempts > maxAttempts: # could not reach raidtab in given maxAttempts log.error( "_check_pogo_main_screen: Could not get to Mainscreen within %s attempts" % str(maxAttempts)) return False self._checkPogoFreeze() # not using continue since we need to get a screen before the next round... found = self._pogoWindowManager.lookForButton( os.path.join(self._applicationArgs.temp_path, 'screenshot%s.png' % str(self._id)), 2.20, 3.01, self._communicator) if found: log.info("_check_pogo_main_screen: Found button (small)") if not found and self._pogoWindowManager.checkCloseExceptNearbyButton( os.path.join(self._applicationArgs.temp_path, 'screenshot%s.png' % str(self._id)), self._id, self._communicator, closeraid=True): log.info( "_check_pogo_main_screen: Found (X) button (except nearby)" ) found = True if not found and self._pogoWindowManager.lookForButton( os.path.join(self._applicationArgs.temp_path, 'screenshot%s.png' % str(self._id)), 1.05, 2.20, self._communicator): log.info("_check_pogo_main_screen: Found button (big)") found = True log.info( "_check_pogo_main_screen: Previous checks found popups: %s" % str(found)) if not found: self._takeScreenshot() attempts += 1 log.info("_check_pogo_main_screen: done") return True def _checkPogoButton(self): log.debug("checkPogoButton: Trying to find buttons") pogoTopmost = self._communicator.isPogoTopmost() if not pogoTopmost: return False self._checkPogoFreeze() if not self._takeScreenshot( delayBefore=self._applicationArgs.post_screenshot_delay): # TODO: again? # if again: # log.error("checkPogoButton: failed getting a screenshot again") # return False # TODO: throw? log.debug("checkPogoButton: Failed getting screenshot") return False attempts = 0 if os.path.isdir( os.path.join(self._applicationArgs.temp_path, 'screenshot%s.png' % str(self._id))): log.error( "checkPogoButton: screenshot.png is not a file/corrupted") return False log.info("checkPogoButton: checking for buttons") found = self._pogoWindowManager.lookForButton( os.path.join(self._applicationArgs.temp_path, 'screenshot%s.png' % str(self._id)), 2.20, 3.01, self._communicator) if found: log.info("checkPogoButton: Found button (small)") log.info("checkPogoButton: done") return True log.info("checkPogoButton: done") return False def _checkPogoClose(self): log.debug("checkPogoClose: Trying to find closeX") pogoTopmost = self._communicator.isPogoTopmost() if not pogoTopmost: return False self._checkPogoFreeze() if not self._takeScreenshot( delayBefore=self._applicationArgs.post_screenshot_delay): # TODO: go again? # if again: # log.error("checkPogoClose: failed getting a screenshot again") # return False # TODO: consider throwing? log.debug("checkPogoClose: Could not get screenshot") return False attempts = 0 if os.path.isdir( os.path.join(self._applicationArgs.temp_path, 'screenshot%s.png' % str(self._id))): log.error("checkPogoClose: screenshot.png is not a file/corrupted") return False log.info("checkPogoClose: checking for CloseX") found = self._pogoWindowManager.checkCloseExceptNearbyButton( os.path.join(self._applicationArgs.temp_path, 'screenshot%s.png' % str(self._id)), self._id, self._communicator) if found: log.info("checkPogoClose: Found (X) button (except nearby)") log.info("checkPogoClose: done") return True log.info("checkPogoClose: done") return False def _getToRaidscreen(self, maxAttempts, again=False): # check for any popups (including post login OK) log.debug( "getToRaidscreen: Trying to get to the raidscreen with %s max attempts..." % str(maxAttempts)) pogoTopmost = self._communicator.isPogoTopmost() if not pogoTopmost: return False self._checkPogoFreeze() if not self._takeScreenshot( delayBefore=self._applicationArgs.post_screenshot_delay): if again: log.error("getToRaidscreen: failed getting a screenshot again") return False self._getToRaidscreen(maxAttempts, True) log.debug("getToRaidscreen: Got screenshot, checking GPS") attempts = 0 if os.path.isdir( os.path.join(self._applicationArgs.temp_path, 'screenshot%s.png' % str(self._id))): log.error( "getToRaidscreen: screenshot.png is not a file/corrupted") return False # TODO: replace self._id with device ID while self._pogoWindowManager.isGpsSignalLost( os.path.join(self._applicationArgs.temp_path, 'screenshot%s.png' % str(self._id)), self._id): log.debug("getToRaidscreen: GPS signal lost") time.sleep(1) self._takeScreenshot() log.warning("getToRaidscreen: GPS signal error") self._redErrorCount += 1 if self._redErrorCount > 3: log.error( "getToRaidscreen: Red error multiple times in a row, restarting" ) self._redErrorCount = 0 self._restart_pogo() return False self._redErrorCount = 0 log.debug("getToRaidscreen: checking raidscreen") while not self._pogoWindowManager.checkRaidscreen( os.path.join(self._applicationArgs.temp_path, 'screenshot%s.png' % str(self._id)), self._id, self._communicator): log.debug("getToRaidscreen: not on raidscreen...") if attempts > maxAttempts: # could not reach raidtab in given maxAttempts log.error( "getToRaidscreen: Could not get to raidtab within %s attempts" % str(maxAttempts)) return False self._checkPogoFreeze() # not using continue since we need to get a screen before the next round... found = self._pogoWindowManager.lookForButton( os.path.join(self._applicationArgs.temp_path, 'screenshot%s.png' % str(self._id)), 2.20, 3.01, self._communicator) if found: log.info("getToRaidscreen: Found button (small)") if not found and self._pogoWindowManager.checkCloseExceptNearbyButton( os.path.join(self._applicationArgs.temp_path, 'screenshot%s.png' % str(self._id)), self._id, self._communicator): log.info("getToRaidscreen: Found (X) button (except nearby)") found = True if not found and self._pogoWindowManager.lookForButton( os.path.join(self._applicationArgs.temp_path, 'screenshot%s.png' % str(self._id)), 1.05, 2.20, self._communicator): log.info("getToRaidscreen: Found button (big)") found = True log.info("getToRaidscreen: Previous checks found popups: %s" % str(found)) if not found: log.info( "getToRaidscreen: Previous checks found nothing. Checking nearby open" ) if self._pogoWindowManager.checkNearby( os.path.join(self._applicationArgs.temp_path, 'screenshot%s.png' % str(self._id)), self._id, self._communicator): return self._takeScreenshot( delayBefore=self._applicationArgs.post_screenshot_delay ) if not self._takeScreenshot( delayBefore=self._applicationArgs.post_screenshot_delay): return False attempts += 1 log.debug("getToRaidscreen: done") return True def _get_screen_size(self): screen = self._communicator.getscreensize().split(' ') self._screen_x = screen[0] self._screen_y = screen[1] log.debug('Get Screensize of %s: X: %s, Y: %s' % (str(self._id), str(self._screen_x), str(self._screen_y))) self._resocalc.get_x_y_ratio(self, self._screen_x, self._screen_y)
class WorkerBase(ABC): def __init__(self, args, id, last_known_state, websocket_handler, route_manager_daytime, route_manager_nighttime, devicesettings, db_wrapper, NoOcr=False): self.thread_pool = ThreadPool(processes=2) self._route_manager_daytime = route_manager_daytime self._route_manager_nighttime = route_manager_nighttime self._websocket_handler = websocket_handler self._communicator = Communicator(websocket_handler, id, args.websocket_command_timeout) self._id = id self._applicationArgs = args self._last_known_state = last_known_state self._lastScreenshotTaken = 0 self._stop_worker_event = Event() self._db_wrapper = db_wrapper self._redErrorCount = 0 self._lastScreenHash = None self._lastScreenHashCount = 0 self._devicesettings = devicesettings if not NoOcr: from ocr.pogoWindows import PogoWindows self._pogoWindowManager = PogoWindows(self._communicator, args.temp_path) def start_worker(self): # asyncio.ensure_future(self._main_work_thread()) async_result = self.thread_pool.apply_async(self._main_work_thread, ()) # tuple of args for foo # return_val = async_result.get() # get the return value from your function. # do some other stuff in the main process while not self._stop_worker_event.isSet(): time.sleep(1) async_result.get() return self._last_known_state def stop_worker(self): self._stop_worker_event.set() @abstractmethod def _main_work_thread(self): log.debug("Base called") pass @abstractmethod def _start_pogo(self): pass # routine to start pogo. Needs to be overriden since e.g. OCR worker uses screenshots for startup def _initRoutine(self): if self._applicationArgs.initial_restart is False: self._turnScreenOnAndStartPogo() else: if not self._start_pogo(): while not self._restartPogo(): log.warning("failed starting pogo") def _turnScreenOnAndStartPogo(self): if not self._communicator.isScreenOn(): self._communicator.startApp("de.grennith.rgc.remotegpscontroller") log.warning("Turning screen on") self._communicator.turnScreenOn() time.sleep(self._applicationArgs.post_turn_screen_on_delay) # check if pogo is running and start it if necessary log.warning("turnScreenOnAndStartPogo: (Re-)Starting Pogo") self._restartPogo() def _stopPogo(self): attempts = 0 stopResult = self._communicator.stopApp("com.nianticlabs.pokemongo") pogoTopmost = self._communicator.isPogoTopmost() while pogoTopmost: attempts += 1 if attempts > 10: return False stopResult = self._communicator.stopApp("com.nianticlabs.pokemongo") time.sleep(1) pogoTopmost = self._communicator.isPogoTopmost() return stopResult def _restartPogo(self, clear_cache=True): successfulStop = self._stopPogo() log.debug("restartPogo: stop pogo resulted in %s" % str(successfulStop)) if successfulStop: if clear_cache: self._communicator.clearAppCache("com.nianticlabs.pokemongo") time.sleep(1) return self._start_pogo() else: return False def _reopenRaidTab(self): log.debug("_reopenRaidTab: Taking screenshot...") log.info("reopenRaidTab: Attempting to retrieve screenshot before checking raidtab") if not self._takeScreenshot(): log.debug("_reopenRaidTab: Failed getting screenshot...") log.error("reopenRaidTab: Failed retrieving screenshot before checking for closebutton") return log.debug("_reopenRaidTab: Checking close except nearby...") pathToPass = os.path.join(self._applicationArgs.temp_path, 'screenshot%s.png' % str(self._id)) log.debug("Path: %s" % str(pathToPass)) self._pogoWindowManager.checkCloseExceptNearbyButton(pathToPass, self._id, 'True') log.debug("_reopenRaidTab: Getting to raidscreen...") self._getToRaidscreen(3) time.sleep(1) def _takeScreenshot(self, delayAfter=0.0, delayBefore=0.0): log.debug("Taking screenshot...") time.sleep(delayBefore) compareToTime = time.time() - self._lastScreenshotTaken log.debug("Last screenshot taken: %s" % str(self._lastScreenshotTaken)) if self._lastScreenshotTaken and compareToTime < 0.5: log.debug("takeScreenshot: screenshot taken recently, returning immediately") log.debug("Screenshot taken recently, skipping") return True # TODO: screenshot.png needs identifier in name elif not self._communicator.getScreenshot(os.path.join(self._applicationArgs.temp_path, 'screenshot%s.png' % str(self._id))): log.error("takeScreenshot: Failed retrieving screenshot") log.debug("Failed retrieving screenshot") return False else: log.debug("Success retrieving screenshot") self._lastScreenshotTaken = time.time() time.sleep(delayAfter) return True def _checkPogoFreeze(self): log.debug("Checking if pogo froze") if not self._takeScreenshot(): log.debug("_checkPogoFreeze: failed retrieving screenshot") return from utils.image_utils import getImageHash screenHash = getImageHash(os.path.join(self._applicationArgs.temp_path, 'screenshot%s.png' % str(self._id))) log.debug("checkPogoFreeze: Old Hash: " + str(self._lastScreenHash)) log.debug("checkPogoFreeze: New Hash: " + str(screenHash)) if hamming_dist(str(self._lastScreenHash), str(screenHash)) < 4 and str(self._lastScreenHash) != '0': log.debug("checkPogoFreeze: New und old Screenshoot are the same - no processing") self._lastScreenHashCount += 1 log.debug("checkPogoFreeze: Same Screen Count: " + str(self._lastScreenHashCount)) if self._lastScreenHashCount >= 100: self._lastScreenHashCount = 0 self._restartPogo() else: self._lastScreenHash = screenHash self._lastScreenHashCount = 0 log.debug("_checkPogoFreeze: done") def _getToRaidscreen(self, maxAttempts, again=False): # check for any popups (including post login OK) log.debug("getToRaidscreen: Trying to get to the raidscreen with %s max attempts..." % str(maxAttempts)) pogoTopmost = self._communicator.isPogoTopmost() if not pogoTopmost: return False self._checkPogoFreeze() if not self._takeScreenshot(delayBefore=self._applicationArgs.post_screenshot_delay): if again: log.error("getToRaidscreen: failed getting a screenshot again") return False self._getToRaidscreen(maxAttempts, True) log.debug("getToRaidscreen: Got screenshot, checking GPS") attempts = 0 if os.path.isdir(os.path.join(self._applicationArgs.temp_path, 'screenshot%s.png' % str(self._id))): log.error("getToRaidscreen: screenshot.png is not a file/corrupted") return False # TODO: replace self._id with device ID while self._pogoWindowManager.isGpsSignalLost(os.path.join(self._applicationArgs.temp_path, 'screenshot%s.png' % str(self._id)), self._id): log.debug("getToRaidscreen: GPS signal lost") time.sleep(1) self._takeScreenshot() log.warning("getToRaidscreen: GPS signal error") self._redErrorCount += 1 if self._redErrorCount > 3: log.error("getToRaidscreen: Red error multiple times in a row, restarting") self._redErrorCount = 0 self._restartPogo() return False self._redErrorCount = 0 log.debug("getToRaidscreen: checking raidscreen") while not self._pogoWindowManager.checkRaidscreen(os.path.join(self._applicationArgs.temp_path, 'screenshot%s.png' % str(self._id)), self._id): log.debug("getToRaidscreen: not on raidscreen...") if attempts > maxAttempts: # could not reach raidtab in given maxAttempts log.error("getToRaidscreen: Could not get to raidtab within %s attempts" % str(maxAttempts)) return False self._checkPogoFreeze() # not using continue since we need to get a screen before the next round... found = self._pogoWindowManager.lookForButton(os.path.join(self._applicationArgs.temp_path, 'screenshot%s.png' % str(self._id)), 2.20, 3.01) if found: log.info("getToRaidscreen: Found button (small)") if not found and self._pogoWindowManager.checkCloseExceptNearbyButton( os.path.join(self._applicationArgs.temp_path, 'screenshot%s.png' % str(self._id)), self._id): log.info("getToRaidscreen: Found (X) button (except nearby)") found = True if not found and self._pogoWindowManager.lookForButton(os.path.join(self._applicationArgs.temp_path, 'screenshot%s.png' % str(self._id)), 1.05, 2.20): log.info("getToRaidscreen: Found button (big)") found = True log.info("getToRaidscreen: Previous checks found popups: %s" % str(found)) if not found: log.info("getToRaidscreen: Previous checks found nothing. Checking nearby open") if self._pogoWindowManager.checkNearby(os.path.join(self._applicationArgs.temp_path, 'screenshot%s.png' % str(self._id)), self._id): return self._takeScreenshot(delayBefore=self._applicationArgs.post_screenshot_delay) if not self._takeScreenshot(delayBefore=self._applicationArgs.post_screenshot_delay): return False attempts += 1 log.debug("getToRaidscreen: done") return True