diff --git a/EDMC.py b/EDMC.py index 2195adea..46e34c0c 100755 --- a/EDMC.py +++ b/EDMC.py @@ -21,6 +21,7 @@ import commodity from commodity import COMMODITY_DEFAULT import outfitting import loadout +import edshipyard import coriolis import shipyard import eddb @@ -44,8 +45,9 @@ try: # arg parsing parser = argparse.ArgumentParser(prog=appcmdname, description='Prints the current system and station (if docked) to stdout and optionally writes player status, ship locations, ship loadout and/or station data to file. Requires prior setup through the accompanying GUI app.') parser.add_argument('-v', '--version', help='print program version and exit', action='store_const', const=True) + parser.add_argument('-a', metavar='FILE', help='write ship loadout to FILE in Companion API json format') parser.add_argument('-c', metavar='FILE', help='write ship loadout to FILE in Coriolis json format') - parser.add_argument('-e', metavar='FILE', help='write ship loadout to FILE in E:D Shipyard format') + parser.add_argument('-e', metavar='FILE', help='write ship loadout to FILE in E:D Shipyard plain text format') parser.add_argument('-l', metavar='FILE', help='write ship locations to FILE in CSV format') parser.add_argument('-m', metavar='FILE', help='write station commodity market data to FILE in CSV format') parser.add_argument('-o', metavar='FILE', help='write station outfitting data to FILE in CSV format') @@ -100,10 +102,12 @@ try: if args.d: with open(args.d, 'wt') as h: h.write(json.dumps(data, ensure_ascii=False, indent=2, sort_keys=True, separators=(',', ': ')).encode('utf-8')) + if args.a: + loadout.export(data, args.a) if args.c: coriolis.export(data, args.c) if args.e: - loadout.export(data, args.e) + edshipyard.export(data, args.e) if args.l: stats.export_ships(data, args.l) if args.t: diff --git a/EDMarketConnector.py b/EDMarketConnector.py index 1c4af87e..21d0b995 100755 --- a/EDMarketConnector.py +++ b/EDMarketConnector.py @@ -44,6 +44,7 @@ import edsm import coriolis import eddb import edshipyard +import loadout import stats import prefs import plug @@ -399,7 +400,7 @@ class AppWindow: # stuff we can do when not docked plug.notify_newdata(data) if config.getint('output') & config.OUT_SHIP_EDS: - edshipyard.export(data) + loadout.export(data) if config.getint('output') & config.OUT_SHIP_CORIOLIS: coriolis.export(data) diff --git a/edshipyard.py b/edshipyard.py index 742ecbfb..eef5dfba 100644 --- a/edshipyard.py +++ b/edshipyard.py @@ -1,4 +1,4 @@ -# Export ship loadout in E:D Shipyard format +import cPickle import base64 from collections import defaultdict @@ -10,14 +10,119 @@ import StringIO import time import gzip -import companion - from config import config +import companion +import outfitting + +# Map API ship names to E:D Shipyard ship names +ship_map = dict(companion.ship_map) +ship_map['cobramkiii'] = 'Cobra Mk III' +ship_map['cobramkiv'] = 'Cobra Mk IV', +ship_map['viper'] = 'Viper' +ship_map['viper_mkiv'] = 'Viper Mk IV' +# Map API slot names to E:D Shipyard slot names +slot_map = { + 'hugehardpoint' : 'H', + 'largehardpoint' : 'L', + 'mediumhardpoint' : 'M', + 'smallhardpoint' : 'S', + 'tinyhardpoint' : 'U', + 'armour' : 'BH', + 'powerplant' : 'RB', + 'mainengines' : 'TM', + 'frameshiftdrive' : 'FH', + 'lifesupport' : 'EC', + 'powerdistributor' : 'PC', + 'radar' : 'SS', + 'fueltank' : 'FS', +} + + +# Ship masses +ships = cPickle.load(open(join(config.respath, 'ships.p'), 'rb')) + + +# Export ship loadout in E:D Shipyard plain text format def export(data, filename=None): - string = json.dumps(companion.ship(data), ensure_ascii=False, indent=2, sort_keys=True, separators=(',', ': ')) # pretty print + def class_rating(module): + if 'guidance' in module: # Missiles + return module['class'] + module['rating'] + '/' + module.get('mount', 'F')[0] + module['guidance'][0] + ' ' + elif 'mount' in module: # Hardpoints + return module['class'] + module['rating'] + '/' + module['mount'][0] + ' ' + elif 'Cabin' in module['name']: # Passenger cabins + return module['class'] + module['rating'] + '/' + module['name'][0] + ' ' + else: + return module['class'] + module['rating'] + ' ' + + querytime = config.getint('querytime') or int(time.time()) + + loadout = defaultdict(list) + mass = 0.0 + fsd = None + + for slot in sorted(data['ship']['modules']): + + v = data['ship']['modules'][slot] + try: + if not v: continue + + module = outfitting.lookup(v['module'], ship_map) + if not module: continue + + cr = class_rating(module) + mass += module.get('mass', 0) + + # Specials + if 'Fuel Tank'in module['name'] or 'Cargo Rack' in module['name']: + name = '%s (Capacity: %d)' % (module['name'], 2**int(module['class'])) + else: + name = module['name'] + + if name == 'Frame Shift Drive': + fsd = module # save for range calculation + + for s in slot_map: + if slot.lower().startswith(s): + loadout[slot_map[s]].append(cr + name) + break + else: + if slot.lower().startswith('slot'): + loadout[slot[-1]].append(cr + name) + elif __debug__ and not slot.lower().startswith('planetaryapproachsuite'): + print 'EDShipyard: Unknown slot %s' % slot + + except AssertionError as e: + if __debug__: print 'EDShipyard: %s' % e + continue # Silently skip unrecognized modules + except: + if __debug__: raise + + # Construct description + string = '[%s]\n' % ship_map.get(data['ship']['name'].lower(), data['ship']['name']) + for slot in ['H', 'L', 'M', 'S', 'U', None, 'BH', 'RB', 'TM', 'FH', 'EC', 'PC', 'SS', 'FS', None, '9', '8', '7', '6', '5', '4', '3', '2', '1']: + if not slot: + string += '\n' + elif slot in loadout: + for name in loadout[slot]: + string += '%s: %s\n' % (slot, name) + string += '---\nCargo : %d T\nFuel : %d T\n' % (data['ship']['cargo']['capacity'], data['ship']['fuel']['main']['capacity']) + + # Add mass and range + assert data['ship']['name'].lower() in companion.ship_map, data['ship']['name'] + assert companion.ship_map[data['ship']['name'].lower()] in ships, companion.ship_map[data['ship']['name'].lower()] + try: + # https://github.com/cmmcleod/coriolis/blob/master/app/js/shipyard/module-shipyard.js#L184 + mass += ships[companion.ship_map[data['ship']['name'].lower()]]['hullMass'] + string += 'Mass : %.1f T empty\n %.1f T full\n' % (mass, mass + data['ship']['fuel']['main']['capacity']+ data['ship']['cargo']['capacity']) + multiplier = pow(min(data['ship']['fuel']['main']['capacity'], fsd['maxfuel']) / fsd['fuelmul'], 1.0 / fsd['fuelpower']) * fsd['optmass'] + string += 'Range : %.2f LY unladen\n %.2f LY laden\n' % ( + multiplier / (mass + data['ship']['fuel']['main']['capacity']), + multiplier / (mass + data['ship']['fuel']['main']['capacity'] + data['ship']['cargo']['capacity'])) + except: + if __debug__: raise if filename: with open(filename, 'wt') as h: @@ -33,8 +138,6 @@ def export(data, filename=None): if h.read() == string: return # same as last time - don't write - querytime = config.getint('querytime') or int(time.time()) - # Write filename = join(config.get('outdir'), '%s.%s.txt' % (ship, time.strftime('%Y-%m-%dT%H.%M.%S', time.localtime(querytime)))) with open(filename, 'wt') as h: diff --git a/loadout.py b/loadout.py index 77d123d0..688af85d 100644 --- a/loadout.py +++ b/loadout.py @@ -1,125 +1,17 @@ -# Export ship loadout in E:D Shipyard format - -from collections import defaultdict -import cPickle +import json import os from os.path import join import re import time from config import config -import outfitting import companion -# Map API ship names to E:D Shipyard ship names -ship_map = dict(companion.ship_map) -ship_map['cobramkiii'] = 'Cobra Mk III' -ship_map['cobramkiv'] = 'Cobra Mk IV', -ship_map['viper'] = 'Viper' -ship_map['viper_mkiv'] = 'Viper Mk IV' - - -# Map API slot names to E:D Shipyard slot names -slot_map = { - 'hugehardpoint' : 'H', - 'largehardpoint' : 'L', - 'mediumhardpoint' : 'M', - 'smallhardpoint' : 'S', - 'tinyhardpoint' : 'U', - 'armour' : 'BH', - 'powerplant' : 'RB', - 'mainengines' : 'TM', - 'frameshiftdrive' : 'FH', - 'lifesupport' : 'EC', - 'powerdistributor' : 'PC', - 'radar' : 'SS', - 'fueltank' : 'FS', -} - - -# Ship masses -ships = cPickle.load(open(join(config.respath, 'ships.p'), 'rb')) - - +# Export ship loadout in Companion API json format def export(data, filename=None): - def class_rating(module): - if 'guidance' in module: # Missiles - return module['class'] + module['rating'] + '/' + module.get('mount', 'F')[0] + module['guidance'][0] + ' ' - elif 'mount' in module: # Hardpoints - return module['class'] + module['rating'] + '/' + module['mount'][0] + ' ' - elif 'Cabin' in module['name']: # Passenger cabins - return module['class'] + module['rating'] + '/' + module['name'][0] + ' ' - else: - return module['class'] + module['rating'] + ' ' - - querytime = config.getint('querytime') or int(time.time()) - - loadout = defaultdict(list) - mass = 0.0 - fsd = None - - for slot in sorted(data['ship']['modules']): - - v = data['ship']['modules'][slot] - try: - if not v: continue - - module = outfitting.lookup(v['module'], ship_map) - if not module: continue - - cr = class_rating(module) - mass += module.get('mass', 0) - - # Specials - if 'Fuel Tank'in module['name'] or 'Cargo Rack' in module['name']: - name = '%s (Capacity: %d)' % (module['name'], 2**int(module['class'])) - else: - name = module['name'] - - if name == 'Frame Shift Drive': - fsd = module # save for range calculation - - for s in slot_map: - if slot.lower().startswith(s): - loadout[slot_map[s]].append(cr + name) - break - else: - if slot.lower().startswith('slot'): - loadout[slot[-1]].append(cr + name) - elif __debug__ and not slot.lower().startswith('planetaryapproachsuite'): - print 'EDShipyard: Unknown slot %s' % slot - - except AssertionError as e: - if __debug__: print 'EDShipyard: %s' % e - continue # Silently skip unrecognized modules - except: - if __debug__: raise - - # Construct description - string = '[%s]\n' % ship_map.get(data['ship']['name'].lower(), data['ship']['name']) - for slot in ['H', 'L', 'M', 'S', 'U', None, 'BH', 'RB', 'TM', 'FH', 'EC', 'PC', 'SS', 'FS', None, '9', '8', '7', '6', '5', '4', '3', '2', '1']: - if not slot: - string += '\n' - elif slot in loadout: - for name in loadout[slot]: - string += '%s: %s\n' % (slot, name) - string += '---\nCargo : %d T\nFuel : %d T\n' % (data['ship']['cargo']['capacity'], data['ship']['fuel']['main']['capacity']) - - # Add mass and range - assert data['ship']['name'].lower() in companion.ship_map, data['ship']['name'] - assert companion.ship_map[data['ship']['name'].lower()] in ships, companion.ship_map[data['ship']['name'].lower()] - try: - # https://github.com/cmmcleod/coriolis/blob/master/app/js/shipyard/module-shipyard.js#L184 - mass += ships[companion.ship_map[data['ship']['name'].lower()]]['hullMass'] - string += 'Mass : %.1f T empty\n %.1f T full\n' % (mass, mass + data['ship']['fuel']['main']['capacity']+ data['ship']['cargo']['capacity']) - multiplier = pow(min(data['ship']['fuel']['main']['capacity'], fsd['maxfuel']) / fsd['fuelmul'], 1.0 / fsd['fuelpower']) * fsd['optmass'] - string += 'Range : %.2f LY unladen\n %.2f LY laden\n' % ( - multiplier / (mass + data['ship']['fuel']['main']['capacity']), - multiplier / (mass + data['ship']['fuel']['main']['capacity'] + data['ship']['cargo']['capacity'])) - except: - if __debug__: raise + string = json.dumps(companion.ship(data), ensure_ascii=False, indent=2, sort_keys=True, separators=(',', ': ')) # pretty print if filename: with open(filename, 'wt') as h: @@ -135,6 +27,8 @@ def export(data, filename=None): if h.read() == string: return # same as last time - don't write + querytime = config.getint('querytime') or int(time.time()) + # Write filename = join(config.get('outdir'), '%s.%s.txt' % (ship, time.strftime('%Y-%m-%dT%H.%M.%S', time.localtime(querytime)))) with open(filename, 'wt') as h: