1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
|
from enum import Enum
import os
import platform
from PyQt5.QtWidgets import QApplication
import yaml
import appdirs
from .defaults import defaultPreferencesDict, defaultPropellants
appVersion = (0, 4, 0)
appVersionStr = '.'.join(map(str, appVersion))
class fileTypes(Enum):
PREFERENCES = 1
PROPELLANTS = 2
MOTOR = 3
def futureVersion(verA, verB): # Returns true if a is newer than b
major = verA[0] > verB[0]
minor = verA[0] == verB[0] and verA[1] > verB[1]
fix = verA[0] == verB[0] and verA[1] == verB[1] and verA[2] > verB[2]
return major or minor or fix
def saveFile(path, data, dataType):
output = {
'version': appVersion,
'type': dataType,
'data': data
}
with open(path, 'w') as saveLocation:
yaml.dump(output, saveLocation)
def loadFile(path, dataType):
with open(path, 'r') as readLocation:
fileData = yaml.load(readLocation, Loader=yaml.Loader)
if 'data' not in fileData or 'type' not in fileData or 'version' not in fileData:
raise ValueError('File did not contain the required fields. It may be corrupted or from an old version.')
if fileData['type'] != dataType:
raise TypeError('Loaded data type did not match expected type.')
if fileData['version'] == appVersion: # Check if the file is from the current version
return fileData['data'] # If so, the data is current and can be returned
# If the data is from a future version, it can't be loaded
if futureVersion(fileData['version'], appVersion):
new = '.'.join(str(num) for num in fileData['version'])
old = '.'.join(str(num) for num in appVersion)
raise ValueError("Data is from a future version (" + new + " vs " + old + ") and can't be loaded.")
# Otherwise it is from a past version and will be migrated
return doMigration(fileData)['data']
def getConfigPath(): # Returns the path that files like preferences and propellant library should be in
if platform.system() == 'Darwin': # On OSX, the configuration files should live in the library
path = appdirs.user_data_dir('openMotor', 'openMotor')
if not os.path.isdir(path): # Create directory if it doesn't exist
os.mkdir(path)
return path + '/'
# On other platforms they can live in this directory
return ''
def passthrough(data):
return data
def tabularizePropellant(data):
newProp = {}
newProp['name'] = data['name']
newProp['density'] = data['density']
newProp['tabs'] = [{}]
newProp['tabs'][-1]['a'] = data['a']
newProp['tabs'][-1]['n'] = data['n']
newProp['tabs'][-1]['k'] = data['k']
newProp['tabs'][-1]['t'] = data['t']
newProp['tabs'][-1]['m'] = data['m']
newProp['tabs'][-1]['minPressure'] = 0
newProp['tabs'][-1]['maxPressure'] = 1.0342e+7
return newProp
def migratePref_0_3_0_to_0_4_0(data):
data['general']['igniterPressure'] = defaultPreferencesDict()['general']['igniterPressure']
data['units']['(m*Pa)/s'] = '(in*psi)/s'
data['units']['m/(s*Pa)'] = 'thou/(s*psi)'
return data
def migrateProp_0_3_0_to_0_4_0(data):
for i in range(0, len(data)):
data[i] = tabularizePropellant(data[i])
# Add default propellants in if they don't replace existing ones
for propellant in defaultPropellants():
if propellant['name'] not in [cProp['name'] for cProp in data]:
data.append(propellant)
return data
def migrateMotor_0_3_0_to_0_4_0(data):
data['propellant'] = tabularizePropellant(data['propellant'])
data['config']['igniterPressure'] = defaultPreferencesDict()['general']['igniterPressure']
return data
def migratePref_0_2_0_to_0_3_0(data):
defPref = defaultPreferencesDict()
data['general']['maxPressure'] = defPref['general']['maxPressure']
data['general']['maxMassFlux'] = defPref['general']['maxMassFlux']
data['general']['minPortThroat'] = defPref['general']['minPortThroat']
return data
def migrateMotor_0_2_0_to_0_3_0(data):
if QApplication.instance().preferencesManager:
config = QApplication.instance().preferencesManager.preferences.getDict()['general']
else:
config = defaultPreferencesDict()['general']
data['config'] = config
data['nozzle']['divAngle'] = 15
data['nozzle']['convAngle'] = 55
data['nozzle']['throatLength'] = 0.35 * data['nozzle']['throat']
return data
migrations = {
(0, 3, 0): {
'to': (0, 4, 0),
fileTypes.PREFERENCES: migratePref_0_3_0_to_0_4_0,
fileTypes.PROPELLANTS: migrateProp_0_3_0_to_0_4_0,
fileTypes.MOTOR: migrateMotor_0_3_0_to_0_4_0
},
(0, 2, 0): {
'to': (0, 3, 0),
fileTypes.PREFERENCES: migratePref_0_2_0_to_0_3_0,
fileTypes.PROPELLANTS: passthrough,
fileTypes.MOTOR: migrateMotor_0_2_0_to_0_3_0
},
(0, 1, 0): {
'to': (0, 2, 0),
fileTypes.PREFERENCES: passthrough,
fileTypes.PROPELLANTS: passthrough,
fileTypes.MOTOR: passthrough
}
}
def doMigration(fileData):
print('Doing a migration of a ' + str(fileData["type"]) + ' from ' + str(fileData["version"]))
while fileData["version"] != appVersion:
migration = migrations[fileData["version"]]
print("\tUpgrading " + str(fileData["version"]) + " to " + str(migration["to"]))
fileData["data"] = migration[fileData["type"]](fileData["data"])
fileData["version"] = migration["to"]
return fileData
|