From 3933dea4790afa97700f9ac9a060652adff191b1 Mon Sep 17 00:00:00 2001 From: Athanasius Date: Fri, 2 Apr 2021 12:19:40 +0100 Subject: [PATCH] Convert monitor.event_queue to a queue.Queue. * We now use event_queue.empty() to see if it is indeed empty. * Changed AppWindow.journal_event() to also use .empty() rather than `while True`. Keeping the `'if not entry'` check just in case. This prevents it from calling monitor.get_entry() when the prior run through the loop has just emptied the queue. Thus we'll only log when it genuinely IS unexpectedly empty. * During *game* Shutdown we can send the synthetic 'Shutdown' event through, and then find and append the actual game 'Shutdown' event (i.e. this wasn't a crash, which is what the synthetic event is meant to react to). But AppWindow.journal_event() loop picks up both events in one call, so the second <> sends it working on an empty queue. Thus we log in monitor.get_entry() only if the queue is empty *and* the game is still running. * NB: Also adds 'Component' to the OTHER place montitor.state is initialised. --- EDMarketConnector.py | 2 +- monitor.py | 102 +++++++++++++++++++++++-------------------- 2 files changed, 55 insertions(+), 49 deletions(-) diff --git a/EDMarketConnector.py b/EDMarketConnector.py index 66d13235..41724cb8 100755 --- a/EDMarketConnector.py +++ b/EDMarketConnector.py @@ -891,7 +891,7 @@ class AppWindow(object): logger.debug('monitor.thread is None, assuming shutdown and returning') return - while True: + while not monitor.event_queue.empty(): entry = monitor.get_entry() if not entry: # This is expected due to some monitor.py code that appends `None` diff --git a/monitor.py b/monitor.py index 72730d3a..0abe721a 100644 --- a/monitor.py +++ b/monitor.py @@ -1,6 +1,7 @@ """Monitor for new Journal files and contents of latest.""" import json +import queue import re import threading from calendar import timegm @@ -78,7 +79,8 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below self.observer: Optional['Observer'] = None self.observed = None # a watchdog ObservedWatch, or None if polling self.thread: Optional[threading.Thread] = None - self.event_queue: List = [] # For communicating journal entries back to main thread + # For communicating journal entries back to main thread + self.event_queue: queue.Queue = queue.Queue(maxsize=0) # On startup we might be: # 1) Looking at an old journal file because the game isn't running or the user has exited to the main menu. @@ -342,11 +344,11 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below entry['StationType'] = self.stationtype entry['MarketID'] = self.station_marketid - self.event_queue.append(json.dumps(entry, separators=(', ', ':'))) + self.event_queue.put(json.dumps(entry, separators=(', ', ':'))) else: # Generate null event to update the display (with possibly out-of-date info) - self.event_queue.append(None) + self.event_queue.put(None) self.live = False # Watchdog thread -- there is a way to get this by using self.observer.emitters and checking for an attribute: @@ -396,12 +398,13 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below return # Terminate if b'"event":"Location"' in line: - logger.trace('Found "Location" event, appending to event_queue') + logger.trace('Found "Location" event, adding to event_queue') - self.event_queue.append(line) + self.event_queue.put(line) - if self.event_queue: + if not self.event_queue.empty(): if not config.shutting_down: + # logger.trace('Sending <>') self.root.event_generate('<>', when="tail") log_pos = loghandle.tell() @@ -420,11 +423,12 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below if not self.game_running(): logger.info('Detected exit from game, synthesising ShutDown event') timestamp = strftime('%Y-%m-%dT%H:%M:%SZ', gmtime()) - self.event_queue.append( + self.event_queue.put( f'{{ "timestamp":"{timestamp}", "event":"ShutDown" }}' ) if not config.shutting_down: + # logger.trace('Sending <>') self.root.event_generate('<>', when="tail") self.game_was_running = False @@ -482,6 +486,7 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below 'Raw': defaultdict(int), 'Manufactured': defaultdict(int), 'Encoded': defaultdict(int), + 'Component': defaultdict(int), 'Engineers': {}, 'Rank': {}, 'Reputation': {}, @@ -975,54 +980,55 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below logger.debug('Called whilst self.thread is None, returning') return None - if not self.event_queue: - logger.trace('Called with no event_queue') + # logger.trace('Begin') + if self.event_queue.empty() and self.game_running(): + logger.error('event_queue is empty whilst game_running, this should not happen, returning') return None - else: - entry = self.parse_entry(self.event_queue.pop(0)) + # logger.trace('event_queue NOT empty') + entry = self.parse_entry(self.event_queue.get_nowait()) + + if entry['event'] == 'Location': + logger.trace('"Location" event') + + if not self.live and entry['event'] not in (None, 'Fileheader'): + # Game not running locally, but Journal has been updated + self.live = True + if self.station: + entry = OrderedDict([ + ('timestamp', strftime('%Y-%m-%dT%H:%M:%SZ', gmtime())), + ('event', 'StartUp'), + ('Docked', True), + ('MarketID', self.station_marketid), + ('StationName', self.station), + ('StationType', self.stationtype), + ('StarSystem', self.system), + ('StarPos', self.coordinates), + ('SystemAddress', self.systemaddress), + ]) + + else: + entry = OrderedDict([ + ('timestamp', strftime('%Y-%m-%dT%H:%M:%SZ', gmtime())), + ('event', 'StartUp'), + ('Docked', False), + ('StarSystem', self.system), + ('StarPos', self.coordinates), + ('SystemAddress', self.systemaddress), + ]) if entry['event'] == 'Location': - logger.trace('"Location" event') + logger.trace('Appending "Location" event to event_queue') - if not self.live and entry['event'] not in (None, 'Fileheader'): - # Game not running locally, but Journal has been updated - self.live = True - if self.station: - entry = OrderedDict([ - ('timestamp', strftime('%Y-%m-%dT%H:%M:%SZ', gmtime())), - ('event', 'StartUp'), - ('Docked', True), - ('MarketID', self.station_marketid), - ('StationName', self.station), - ('StationType', self.stationtype), - ('StarSystem', self.system), - ('StarPos', self.coordinates), - ('SystemAddress', self.systemaddress), - ]) + self.event_queue.put(json.dumps(entry, separators=(', ', ':'))) - else: - entry = OrderedDict([ - ('timestamp', strftime('%Y-%m-%dT%H:%M:%SZ', gmtime())), - ('event', 'StartUp'), - ('Docked', False), - ('StarSystem', self.system), - ('StarPos', self.coordinates), - ('SystemAddress', self.systemaddress), - ]) + elif self.live and entry['event'] == 'Music' and entry.get('MusicTrack') == 'MainMenu': + ts = strftime('%Y-%m-%dT%H:%M:%SZ', gmtime()) + self.event_queue.put( + f'{{ "timestamp":"{ts}", "event":"ShutDown" }}' + ) - if entry['event'] == 'Location': - logger.trace('Appending "Location" event to event_queue') - - self.event_queue.append(json.dumps(entry, separators=(', ', ':'))) - - elif self.live and entry['event'] == 'Music' and entry.get('MusicTrack') == 'MainMenu': - ts = strftime('%Y-%m-%dT%H:%M:%SZ', gmtime()) - self.event_queue.append( - f'{{ "timestamp":"{ts}", "event":"ShutDown" }}' - ) - - return entry + return entry def game_running(self) -> bool: # noqa: CCR001 """