1
0
mirror of https://github.com/EDCD/EDMarketConnector.git synced 2025-04-14 16:27:13 +03:00

Throttle EDDN output when sending replay

This commit is contained in:
Jonathan Harris 2017-03-11 17:26:40 +00:00
parent dfa87f40d3
commit d4dddb6239
3 changed files with 75 additions and 39 deletions

View File

@ -43,7 +43,7 @@ import companion
import commodity
from commodity import COMMODITY_CSV
import td
from eddn import eddn
import eddn
import edsm
import coriolis
import eddb
@ -76,6 +76,7 @@ class AppWindow:
self.holdofftime = config.getint('querytime') + companion.holdoff
self.session = companion.Session()
self.edsm = edsm.EDSM()
self.eddn = eddn.EDDN(self)
self.w = master
self.w.title(applongname)
@ -304,7 +305,7 @@ class AppWindow:
self.status['text'] = 'Warning: Storing passwords as text' # Shouldn't happen unless no secure storage on Linux
# Try to obtain exclusive lock on journal cache, even if we don't need it yet
if not eddn.load():
if not self.eddn.load():
self.status['text'] = 'Error: Is another copy of this app already running?' # Shouldn't happen - don't bother localizing
# callback after the Preferences dialog is applied
@ -496,13 +497,13 @@ class AppWindow:
if not old_status:
self.status['text'] = _('Sending data to EDDN...')
self.w.update_idletasks()
eddn.export_commodities(data)
eddn.export_outfitting(data)
self.eddn.export_commodities(data)
self.eddn.export_outfitting(data)
if has_shipyard and not data['lastStarport'].get('ships'):
# API is flakey about shipyard info - silently retry if missing (<1s is usually sufficient - 5s for margin).
self.w.after(int(SERVER_RETRY * 1000), self.retry_for_shipyard)
else:
eddn.export_shipyard(data)
self.eddn.export_shipyard(data)
if not old_status:
self.status['text'] = ''
@ -578,7 +579,7 @@ class AppWindow:
if __debug__:
print 'Retry for shipyard - ' + (data['commander'].get('docked') and (data['lastStarport'].get('ships') and 'Success' or 'Failure') or 'Undocked!')
if data['commander'].get('docked'): # might have undocked while we were waiting for retry in which case station data is unreliable
eddn.export_shipyard(data)
self.eddn.export_shipyard(data)
except:
pass
@ -704,10 +705,7 @@ class AppWindow:
if 'StarPos' not in entry:
entry['StarPos'] = list(monitor.coordinates)
self.status['text'] = _('Sending data to EDDN...')
self.w.update_idletasks()
eddn.export_journal_entry(monitor.cmdr, monitor.is_beta, entry)
self.status['text'] = ''
self.eddn.export_journal_entry(monitor.cmdr, monitor.is_beta, entry)
elif (config.getint('output') & config.OUT_MKT_EDDN and monitor.cmdr and
entry['event'] == 'MarketSell' and entry.get('BlackMarket')):
@ -723,7 +721,7 @@ class AppWindow:
self.status['text'] = _('Sending data to EDDN...')
self.w.update_idletasks()
eddn.export_blackmarket(monitor.cmdr, monitor.is_beta, msg)
self.eddn.export_blackmarket(monitor.cmdr, monitor.is_beta, msg)
self.status['text'] = ''
except requests.exceptions.RequestException as e:
@ -756,7 +754,7 @@ class AppWindow:
try:
data = self.session.query()
except companion.VerificationRequired:
return prefs.AuthenticationDialog(self.parent, partial(self.verify, self.shipyard_url))
return prefs.AuthenticationDialog(self.w, partial(self.verify, self.shipyard_url))
except companion.ServerError as e:
self.status['text'] = str(e)
return
@ -850,7 +848,7 @@ class AppWindow:
self.w.withdraw() # Following items can take a few seconds, so hide the main window while they happen
hotkeymgr.unregister()
monitor.close()
eddn.close()
self.eddn.close()
self.updater.close()
self.session.close()
config.close()

88
eddn.py
View File

@ -25,48 +25,56 @@ from companion import category_map
timeout= 10 # requests timeout
module_re = re.compile('^Hpt_|^Int_|_Armour_')
replayfile = None # For delayed messages
class _EDDN:
class EDDN:
### UPLOAD = 'http://localhost:8081/upload/' # testing
UPLOAD = 'http://eddn-gateway.elite-markets.net:8080/upload/'
REPLAYPERIOD = 400 # Roughly two messages per second, accounting for send delays [ms]
REPLAYFLUSH = 20 # Update log on disk roughly every 10 seconds
def __init__(self):
def __init__(self, parent):
self.parent = parent
self.session = requests.Session()
self.replayfile = None # For delayed messages
self.replaylog = []
def load(self):
# Try to obtain exclusive access to the journal cache
global replayfile
filename = join(config.app_dir, 'replay.jsonl')
try:
try:
# Try to open existing file
self.replayfile = open(filename, 'r+')
replayfile = open(filename, 'r+')
except:
if exists(filename):
raise # Couldn't open existing file
else:
self.replayfile = open(filename, 'w+') # Create file
replayfile = open(filename, 'w+') # Create file
if platform != 'win32': # open for writing is automatically exclusive on Windows
lockf(self.replayfile, LOCK_EX|LOCK_NB)
lockf(replayfile, LOCK_EX|LOCK_NB)
except:
if __debug__: print_exc()
if self.replayfile:
self.replayfile.close()
self.replayfile = None
if replayfile:
replayfile.close()
replayfile = None
return False
self.replaylog = [line.strip() for line in replayfile]
return True
def flush(self):
if self.replayfile or self.load():
self.replayfile.seek(0, SEEK_SET)
for line in self.replayfile:
self.send(*json.loads(line, object_pairs_hook=OrderedDict))
self.replayfile.truncate(0)
replayfile.seek(0, SEEK_SET)
replayfile.truncate()
for line in self.replaylog:
replayfile.write('%s\n' % line)
replayfile.flush()
def close(self):
if self.replayfile:
self.replayfile.close()
global replayfile
if replayfile:
replayfile.close()
replayfile = None
def send(self, cmdr, msg):
msg['header'] = {
@ -85,6 +93,38 @@ class _EDDN:
print ('Content:\n%s' % r.text).encode('utf-8')
r.raise_for_status()
def sendreplay(self):
if not replayfile:
return # Probably closing app
if not self.replaylog:
self.parent.status['text'] = ''
return
if len(self.replaylog) == 1:
self.parent.status['text'] = _('Sending data to EDDN...')
else:
self.parent.status['text'] = '%s [%d]' % (_('Sending data to EDDN...').replace('...',''), len(self.replaylog))
self.parent.w.update_idletasks()
try:
self.send(*json.loads(self.replaylog[0], object_pairs_hook=OrderedDict))
self.replaylog.pop(0)
if not len(self.replaylog) % self.REPLAYFLUSH:
self.flush()
except EnvironmentError as e:
if __debug__: print_exc()
self.parent.status['text'] = unicode(e)
except requests.exceptions.RequestException as e:
if __debug__: print_exc()
self.parent.status['text'] = _("Error: Can't connect to EDDN")
return # stop sending
except Exception as e:
if __debug__: print_exc()
self.parent.status['text'] = unicode(e)
return # stop sending
self.parent.w.after(self.REPLAYPERIOD, self.sendreplay)
def export_commodities(self, data):
commodities = []
for commodity in data['lastStarport'].get('commodities') or []:
@ -142,25 +182,23 @@ class _EDDN:
'$schemaRef' : 'http://schemas.elite-markets.net/eddn/journal/1' + (is_beta and '/test' or ''),
'message' : entry
}
if self.replayfile or self.load():
if replayfile or self.load():
# Store the entry
self.replayfile.seek(0, SEEK_END)
self.replayfile.write('%s\n' % json.dumps([cmdr.encode('utf-8'), msg]))
self.replayfile.flush()
self.replaylog.append(json.dumps([cmdr.encode('utf-8'), msg]))
replayfile.write('%s\n' % self.replaylog[-1])
if entry['event'] == 'Docked' or not (config.getint('output') & config.OUT_SYS_DELAY):
# Try to send this and previous entries
self.flush()
self.sendreplay()
else:
# Can't access replay file! Send immediately.
self.parent.status['text'] = _('Sending data to EDDN...')
self.parent.w.update_idletasks()
self.send(cmdr, msg)
self.parent.status['text'] = ''
def export_blackmarket(self, cmdr, is_beta, msg):
self.send(cmdr, {
'$schemaRef' : 'http://schemas.elite-markets.net/eddn/blackmarket/1' + (is_beta and '/test' or ''),
'message' : msg
})
# singleton
eddn = _EDDN()

View File

@ -11,7 +11,7 @@ from ttkHyperlinkLabel import HyperlinkLabel
import myNotebook as nb
from config import applongname, config
from eddn import eddn
import eddn
from hotkey import hotkeymgr
from l10n import Translations
from monitor import monitor