Added persistence of ComponentPresets in the ORK file. Bumped ORK file version number to 1.5 when saving designs containing ComponentPresets. Added a digest string to ComponentPreset which is used during reading and writing ORK files to verify the correct ComponentPreset is used.
This commit is contained in:
parent
68eaf21ef0
commit
edcd0008b1
@ -38,3 +38,5 @@ The following file format versions exist:
|
||||
deploymentvelocity attributes to <flightdata> element. The motor
|
||||
digesting algorithm was changed. Adds <separationevent> and
|
||||
<separationdelay> elements to stage components (except sustainer).
|
||||
|
||||
1.5: Introduced with OpenRocket 12.xx. Added ComponentPresets.
|
@ -26,4 +26,6 @@ public interface ComponentPresetDao {
|
||||
|
||||
public void setFavorite( ComponentPreset preset, boolean favorite );
|
||||
|
||||
public List<ComponentPreset> find( String manufacturer, String partNo );
|
||||
|
||||
}
|
@ -129,6 +129,17 @@ public class ComponentPresetDatabase extends Database<ComponentPreset> implement
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ComponentPreset> find(String manufacturer, String partNo) {
|
||||
List<ComponentPreset> presets = new ArrayList<ComponentPreset>();
|
||||
for( ComponentPreset preset : list ) {
|
||||
if ( preset.getManufacturer().getSimpleName().equals(manufacturer) && preset.getPartNo().equals(partNo) ) {
|
||||
presets.add(preset);
|
||||
}
|
||||
}
|
||||
return presets;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFavorite( ComponentPreset preset, boolean favorite ) {
|
||||
preset.setFavorite(favorite);
|
||||
|
@ -184,6 +184,9 @@ public class OpenRocketSaver extends RocketSaver {
|
||||
*/
|
||||
private int calculateNecessaryFileVersion(OpenRocketDocument document, StorageOptions opts) {
|
||||
/*
|
||||
* File version 1.5 is requires for:
|
||||
* - saving designs using ComponentPrests
|
||||
*
|
||||
* File version 1.4 is required for:
|
||||
* - saving simulation data
|
||||
* - saving motor data
|
||||
@ -195,6 +198,22 @@ public class OpenRocketSaver extends RocketSaver {
|
||||
* Otherwise use version 1.0.
|
||||
*/
|
||||
|
||||
// Search the rocket for any ComponentPrests
|
||||
{
|
||||
Rocket r = document.getRocket();
|
||||
Iterator<RocketComponent> componentIterator = r.iterator();
|
||||
boolean usesComponentPreset = false;
|
||||
while ( !usesComponentPreset && componentIterator.hasNext() ) {
|
||||
RocketComponent c = componentIterator.next();
|
||||
if ( c.getPresetComponent() != null ) {
|
||||
usesComponentPreset = true;
|
||||
}
|
||||
}
|
||||
if ( usesComponentPreset ) {
|
||||
return FILE_VERSION_DIVISOR + 5;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if design has simulations defined (version 1.4)
|
||||
if (document.getSimulationCount() > 0) {
|
||||
return FILE_VERSION_DIVISOR + 4;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -9,6 +9,7 @@ import net.sf.openrocket.file.RocketSaver;
|
||||
import net.sf.openrocket.material.Material;
|
||||
import net.sf.openrocket.motor.Motor;
|
||||
import net.sf.openrocket.motor.ThrustCurveMotor;
|
||||
import net.sf.openrocket.preset.ComponentPreset;
|
||||
import net.sf.openrocket.rocketcomponent.ComponentAssembly;
|
||||
import net.sf.openrocket.rocketcomponent.MotorMount;
|
||||
import net.sf.openrocket.rocketcomponent.Rocket;
|
||||
@ -19,15 +20,22 @@ import net.sf.openrocket.util.LineStyle;
|
||||
|
||||
|
||||
public class RocketComponentSaver {
|
||||
|
||||
|
||||
protected RocketComponentSaver() {
|
||||
// Prevent instantiation from outside the package
|
||||
}
|
||||
|
||||
|
||||
protected void addParams(net.sf.openrocket.rocketcomponent.RocketComponent c, List<String> elements) {
|
||||
elements.add("<name>" + RocketSaver.escapeXML(c.getName()) + "</name>");
|
||||
|
||||
|
||||
|
||||
ComponentPreset preset = c.getPresetComponent();
|
||||
if ( preset != null ) {
|
||||
elements.add("<preset type=\"" + preset.getType() +
|
||||
"\" manufacturer=\"" + preset.getManufacturer().getSimpleName() +
|
||||
"\" partno=\"" + preset.getPartNo() + "\" digest=\"" + preset.getDigest() +"\"/>");
|
||||
}
|
||||
|
||||
|
||||
// Save color and line style if significant
|
||||
if (!(c instanceof Rocket || c instanceof ComponentAssembly)) {
|
||||
Color color = c.getColor();
|
||||
@ -35,23 +43,23 @@ public class RocketComponentSaver {
|
||||
elements.add("<color red=\"" + color.getRed() + "\" green=\"" + color.getGreen()
|
||||
+ "\" blue=\"" + color.getBlue() + "\"/>");
|
||||
}
|
||||
|
||||
|
||||
LineStyle style = c.getLineStyle();
|
||||
if (style != null) {
|
||||
// Type names currently equivalent to the enum names except for case.
|
||||
elements.add("<linestyle>" + style.name().toLowerCase(Locale.ENGLISH) + "</linestyle>");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Save position unless "AFTER"
|
||||
if (c.getRelativePosition() != RocketComponent.Position.AFTER) {
|
||||
// The type names are currently equivalent to the enum names except for case.
|
||||
String type = c.getRelativePosition().name().toLowerCase(Locale.ENGLISH);
|
||||
elements.add("<position type=\"" + type + "\">" + c.getPositionValue() + "</position>");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Overrides
|
||||
boolean overridden = false;
|
||||
if (c.isMassOverridden()) {
|
||||
@ -66,26 +74,26 @@ public class RocketComponentSaver {
|
||||
elements.add("<overridesubcomponents>" + c.getOverrideSubcomponents()
|
||||
+ "</overridesubcomponents>");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Comment
|
||||
if (c.getComment().length() > 0) {
|
||||
elements.add("<comment>" + RocketSaver.escapeXML(c.getComment()) + "</comment>");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
protected final String materialParam(Material mat) {
|
||||
return materialParam("material", mat);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
protected final String materialParam(String tag, Material mat) {
|
||||
String str = "<" + tag;
|
||||
|
||||
|
||||
switch (mat.getType()) {
|
||||
case LINE:
|
||||
str += " type=\"line\"";
|
||||
@ -99,27 +107,27 @@ public class RocketComponentSaver {
|
||||
default:
|
||||
throw new BugException("Unknown material type: " + mat.getType());
|
||||
}
|
||||
|
||||
|
||||
return str + " density=\"" + mat.getDensity() + "\">" + RocketSaver.escapeXML(mat.getName()) + "</" + tag + ">";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
protected final List<String> motorMountParams(MotorMount mount) {
|
||||
if (!mount.isMotorMount())
|
||||
return Collections.emptyList();
|
||||
|
||||
|
||||
String[] motorConfigIDs = ((RocketComponent) mount).getRocket().getMotorConfigurationIDs();
|
||||
List<String> elements = new ArrayList<String>();
|
||||
|
||||
|
||||
elements.add("<motormount>");
|
||||
|
||||
|
||||
for (String id : motorConfigIDs) {
|
||||
Motor motor = mount.getMotor(id);
|
||||
|
||||
|
||||
// Nothing is stored if no motor loaded
|
||||
if (motor == null)
|
||||
continue;
|
||||
|
||||
|
||||
elements.add(" <motor configid=\"" + id + "\">");
|
||||
if (motor.getMotorType() != Motor.Type.UNKNOWN) {
|
||||
elements.add(" <type>" + motor.getMotorType().name().toLowerCase(Locale.ENGLISH) + "</type>");
|
||||
@ -133,27 +141,27 @@ public class RocketComponentSaver {
|
||||
elements.add(" <designation>" + RocketSaver.escapeXML(motor.getDesignation()) + "</designation>");
|
||||
elements.add(" <diameter>" + motor.getDiameter() + "</diameter>");
|
||||
elements.add(" <length>" + motor.getLength() + "</length>");
|
||||
|
||||
|
||||
// Motor delay
|
||||
if (mount.getMotorDelay(id) == Motor.PLUGGED) {
|
||||
elements.add(" <delay>none</delay>");
|
||||
} else {
|
||||
elements.add(" <delay>" + mount.getMotorDelay(id) + "</delay>");
|
||||
}
|
||||
|
||||
|
||||
elements.add(" </motor>");
|
||||
}
|
||||
|
||||
|
||||
elements.add(" <ignitionevent>"
|
||||
+ mount.getIgnitionEvent().name().toLowerCase(Locale.ENGLISH).replace("_", "")
|
||||
+ "</ignitionevent>");
|
||||
|
||||
|
||||
elements.add(" <ignitiondelay>" + mount.getIgnitionDelay() + "</ignitiondelay>");
|
||||
elements.add(" <overhang>" + mount.getMotorOverhang() + "</overhang>");
|
||||
|
||||
|
||||
elements.add("</motormount>");
|
||||
|
||||
|
||||
return elements;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,13 @@
|
||||
package net.sf.openrocket.preset;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.sf.openrocket.material.Material;
|
||||
@ -9,6 +16,7 @@ import net.sf.openrocket.rocketcomponent.BodyTube;
|
||||
import net.sf.openrocket.rocketcomponent.ExternalComponent.Finish;
|
||||
import net.sf.openrocket.unit.UnitGroup;
|
||||
import net.sf.openrocket.util.BugException;
|
||||
import net.sf.openrocket.util.TextUtil;
|
||||
|
||||
|
||||
/**
|
||||
@ -21,37 +29,38 @@ import net.sf.openrocket.util.BugException;
|
||||
*/
|
||||
// FIXME - Implement clone.
|
||||
public class ComponentPreset implements Comparable<ComponentPreset> {
|
||||
|
||||
|
||||
private final TypedPropertyMap properties = new TypedPropertyMap();
|
||||
|
||||
|
||||
private boolean favorite = false;
|
||||
|
||||
private String digest = "";
|
||||
|
||||
public enum Type {
|
||||
BODY_TUBE,
|
||||
NOSE_CONE;
|
||||
|
||||
Type[] compatibleTypes;
|
||||
|
||||
|
||||
Type () {
|
||||
compatibleTypes = new Type[1];
|
||||
compatibleTypes[0] = this;
|
||||
}
|
||||
|
||||
|
||||
Type( Type ... t ) {
|
||||
|
||||
|
||||
compatibleTypes = new Type[t.length+1];
|
||||
compatibleTypes[0] = this;
|
||||
for( int i=0; i<t.length; i++ ) {
|
||||
compatibleTypes[i+1] = t[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Type[] getCompatibleTypes() {
|
||||
return compatibleTypes;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public final static TypedKey<Manufacturer> MANUFACTURER = new TypedKey<Manufacturer>("Manufacturer", Manufacturer.class);
|
||||
public final static TypedKey<String> PARTNO = new TypedKey<String>("PartNo",String.class);
|
||||
public final static TypedKey<Type> TYPE = new TypedKey<Type>("Type",Type.class);
|
||||
@ -63,7 +72,7 @@ public class ComponentPreset implements Comparable<ComponentPreset> {
|
||||
public final static TypedKey<Double> THICKNESS = new TypedKey<Double>("Thickness", Double.class, UnitGroup.UNITS_LENGTH);
|
||||
public final static TypedKey<Boolean> FILLED = new TypedKey<Boolean>("Filled", Boolean.class);
|
||||
public final static TypedKey<Double> MASS = new TypedKey<Double>("Mass", Double.class, UnitGroup.UNITS_MASS);
|
||||
|
||||
|
||||
public final static Map<String, TypedKey<?>> keyMap = new HashMap<String, TypedKey<?>>();
|
||||
static {
|
||||
keyMap.put(MANUFACTURER.getName(), MANUFACTURER);
|
||||
@ -78,15 +87,15 @@ public class ComponentPreset implements Comparable<ComponentPreset> {
|
||||
keyMap.put(FILLED.getName(), FILLED);
|
||||
keyMap.put(MASS.getName(), MASS);
|
||||
}
|
||||
|
||||
|
||||
public static ComponentPreset create( TypedPropertyMap props ) throws InvalidComponentPresetException {
|
||||
|
||||
|
||||
ComponentPreset preset = new ComponentPreset();
|
||||
// First do validation.
|
||||
if ( !props.containsKey(TYPE)) {
|
||||
throw new InvalidComponentPresetException("No Type specified " + props.toString() );
|
||||
}
|
||||
|
||||
|
||||
if (!props.containsKey(MANUFACTURER)) {
|
||||
throw new InvalidComponentPresetException("No Manufacturer specified " + props.toString() );
|
||||
}
|
||||
@ -96,25 +105,25 @@ public class ComponentPreset implements Comparable<ComponentPreset> {
|
||||
}
|
||||
|
||||
preset.properties.putAll(props);
|
||||
|
||||
|
||||
// Should check for various bits of each of the types.
|
||||
Type t = props.get(TYPE);
|
||||
switch ( t ) {
|
||||
case BODY_TUBE: {
|
||||
|
||||
|
||||
if ( !props.containsKey(LENGTH) ) {
|
||||
throw new InvalidComponentPresetException( "No Length specified for body tube preset " + props.toString());
|
||||
}
|
||||
|
||||
|
||||
BodyTube bt = new BodyTube();
|
||||
|
||||
|
||||
bt.setLength(props.get(LENGTH));
|
||||
|
||||
|
||||
// Need to verify contains 2 of OD, thickness, ID. Compute the third.
|
||||
boolean hasOd = props.containsKey(OUTER_DIAMETER);
|
||||
boolean hasId = props.containsKey(INNER_DIAMETER);
|
||||
boolean hasThickness = props.containsKey(THICKNESS);
|
||||
|
||||
|
||||
if ( hasOd ) {
|
||||
double outerRadius = props.get(OUTER_DIAMETER)/2.0;
|
||||
double thickness = 0;
|
||||
@ -140,7 +149,7 @@ public class ComponentPreset implements Comparable<ComponentPreset> {
|
||||
preset.properties.put(OUTER_DIAMETER, bt.getOuterRadius() *2.0);
|
||||
preset.properties.put(INNER_DIAMETER, bt.getInnerRadius() *2.0);
|
||||
preset.properties.put(THICKNESS, bt.getThickness());
|
||||
|
||||
|
||||
// Need to translate Mass to Density.
|
||||
if ( props.containsKey(MASS) ) {
|
||||
String materialName = "TubeCustom";
|
||||
@ -150,35 +159,57 @@ public class ComponentPreset implements Comparable<ComponentPreset> {
|
||||
Material m = Material.newMaterial(Material.Type.BULK, materialName, props.get(MASS)/bt.getComponentVolume(), false);
|
||||
preset.properties.put(MATERIAL, m);
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
case NOSE_CONE: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
preset.computeDigest();
|
||||
|
||||
return preset;
|
||||
|
||||
}
|
||||
|
||||
// Private constructor to encourage use of factory.
|
||||
private ComponentPreset() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convenience method to retrieve the Type of this ComponentPreset.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Type getType() {
|
||||
return properties.get(TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to retrieve the Manufacturer of this ComponentPreset.
|
||||
* @return
|
||||
*/
|
||||
public Manufacturer getManufacturer() {
|
||||
return properties.get(MANUFACTURER);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convenience method to retrieve the PartNo of this ComponentPreset.
|
||||
* @return
|
||||
*/
|
||||
public String getPartNo() {
|
||||
return properties.get(PARTNO);
|
||||
}
|
||||
|
||||
|
||||
public String getDigest() {
|
||||
return digest;
|
||||
}
|
||||
|
||||
public boolean has(Object key) {
|
||||
return properties.containsKey(key);
|
||||
}
|
||||
|
||||
|
||||
public <T> T get(TypedKey<T> key) {
|
||||
T value = properties.get(key);
|
||||
if (value == null) {
|
||||
@ -186,7 +217,7 @@ public class ComponentPreset implements Comparable<ComponentPreset> {
|
||||
}
|
||||
return (T) value;
|
||||
}
|
||||
|
||||
|
||||
public boolean isFavorite() {
|
||||
return favorite;
|
||||
}
|
||||
@ -200,7 +231,7 @@ public class ComponentPreset implements Comparable<ComponentPreset> {
|
||||
int manuCompare = this.getManufacturer().getSimpleName().compareTo(p2.getManufacturer().getSimpleName());
|
||||
if ( manuCompare != 0 )
|
||||
return manuCompare;
|
||||
|
||||
|
||||
int partNoCompare = this.getPartNo().compareTo(p2.getPartNo());
|
||||
return partNoCompare;
|
||||
}
|
||||
@ -209,9 +240,63 @@ public class ComponentPreset implements Comparable<ComponentPreset> {
|
||||
public String toString() {
|
||||
return get(MANUFACTURER).toString() + " " + get(PARTNO);
|
||||
}
|
||||
|
||||
|
||||
public String preferenceKey() {
|
||||
return get(MANUFACTURER).toString() + "|" + get(PARTNO);
|
||||
}
|
||||
|
||||
|
||||
private void computeDigest() {
|
||||
|
||||
try {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
DataOutputStream os = new DataOutputStream(bos);
|
||||
|
||||
List<TypedKey<?>> keys = new ArrayList<TypedKey<?>>( properties.keySet());
|
||||
|
||||
Collections.sort(keys, new Comparator<TypedKey<?>>() {
|
||||
@Override
|
||||
public int compare( TypedKey<?> a, TypedKey<?> b ) {
|
||||
return a.getName().compareTo(b.getName());
|
||||
}
|
||||
});
|
||||
|
||||
for ( TypedKey<?> key : keys ) {
|
||||
|
||||
Object value = properties.get(key);
|
||||
|
||||
os.writeBytes(key.getName());
|
||||
|
||||
if ( key.getType() == Double.class ) {
|
||||
Double d = (Double) value;
|
||||
os.writeDouble(d);
|
||||
} else if (key.getType() == String.class ) {
|
||||
String s = (String) value;
|
||||
os.writeBytes(s);
|
||||
} else if (key.getType() == Manufacturer.class ) {
|
||||
String s = ((Manufacturer)value).getSimpleName();
|
||||
os.writeBytes(s);
|
||||
} else if ( key.getType() == Finish.class ) {
|
||||
String s = ((Finish)value).name();
|
||||
os.writeBytes(s);
|
||||
} else if ( key.getType() == Type.class ) {
|
||||
String s = ((Type)value).name();
|
||||
os.writeBytes(s);
|
||||
} else if ( key.getType() == Boolean.class ) {
|
||||
Boolean b = (Boolean) value;
|
||||
os.writeBoolean(b);
|
||||
} else if ( key.getType() == Material.class ) {
|
||||
double d = ((Material)value).getDensity();
|
||||
os.writeDouble(d);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MessageDigest md5 = MessageDigest.getInstance("MD5");
|
||||
digest = TextUtil.hexString(md5.digest( bos.toByteArray() ));
|
||||
}
|
||||
catch ( Exception e ) {
|
||||
throw new BugException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user