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:
Kevin Ruland 2012-04-11 17:36:50 +00:00
parent 68eaf21ef0
commit edcd0008b1
7 changed files with 559 additions and 365 deletions

View File

@ -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.

View File

@ -26,4 +26,6 @@ public interface ComponentPresetDao {
public void setFavorite( ComponentPreset preset, boolean favorite );
public List<ComponentPreset> find( String manufacturer, String partNo );
}

View File

@ -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);

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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);
}
}
}