Merge pull request #1795 from SiboVG/issue-1348

[#1348] Add support for RockSim importing and exporting of Pods
This commit is contained in:
Sibo Van Gool 2022-11-07 21:21:35 +01:00 committed by GitHub
commit cd420b6499
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 1779 additions and 16 deletions

View File

@ -29,6 +29,8 @@ public class RockSimCommonConstants {
public static final String DENSITY_TYPE = "DensityType";
public static final String RADIAL_LOC = "RadialLoc";
public static final String RADIAL_ANGLE = "RadialAngle";
public static final String AUTO_CALC_RADIAL_DISTANCE = "AutoCalcRadialDistance";
public static final String AUTO_CALC_RADIAL_ANGLE = "AutoCalcRadialAngle";
public static final String LOCATION_MODE = "LocationMode";
public static final String FINISH_CODE = "FinishCode";
public static final String SERIAL_NUMBER = "SerialNo";

View File

@ -11,6 +11,7 @@ import net.sf.openrocket.rocketcomponent.InnerTube;
import net.sf.openrocket.rocketcomponent.LaunchLug;
import net.sf.openrocket.rocketcomponent.MassObject;
import net.sf.openrocket.rocketcomponent.Parachute;
import net.sf.openrocket.rocketcomponent.PodSet;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.rocketcomponent.Streamer;
import net.sf.openrocket.rocketcomponent.Transition;
@ -50,6 +51,7 @@ public class BodyTubeDTO extends BasePartDTO implements AttachableParts {
@XmlElementRefs({
@XmlElementRef(name = RockSimCommonConstants.BODY_TUBE, type = BodyTubeDTO.class),
@XmlElementRef(name = RockSimCommonConstants.BODY_TUBE, type = InnerBodyTubeDTO.class),
@XmlElementRef(name = RockSimCommonConstants.TRANSITION, type = TransitionDTO.class),
@XmlElementRef(name = RockSimCommonConstants.RING, type = CenteringRingDTO.class),
@XmlElementRef(name = RockSimCommonConstants.LAUNCH_LUG, type = LaunchLugDTO.class),
@XmlElementRef(name = RockSimCommonConstants.FIN_SET, type = FinSetDTO.class),
@ -57,7 +59,8 @@ public class BodyTubeDTO extends BasePartDTO implements AttachableParts {
@XmlElementRef(name = RockSimCommonConstants.TUBE_FIN_SET, type = TubeFinSetDTO.class),
@XmlElementRef(name = RockSimCommonConstants.STREAMER, type = StreamerDTO.class),
@XmlElementRef(name = RockSimCommonConstants.PARACHUTE, type = ParachuteDTO.class),
@XmlElementRef(name = RockSimCommonConstants.MASS_OBJECT, type = MassObjectDTO.class)})
@XmlElementRef(name = RockSimCommonConstants.MASS_OBJECT, type = MassObjectDTO.class),
@XmlElementRef(name = RockSimCommonConstants.EXTERNAL_POD, type = PodSetDTO.class)})
List<BasePartDTO> attachedParts = new ArrayList<BasePartDTO>();
/**
@ -125,6 +128,8 @@ public class BodyTubeDTO extends BasePartDTO implements AttachableParts {
addAttachedPart(new FinSetDTO((FinSet) rocketComponents));
} else if (rocketComponents instanceof TubeFinSet) {
addAttachedPart(new TubeFinSetDTO((TubeFinSet) rocketComponents));
} else if (rocketComponents instanceof PodSet) {
addAttachedPart(new PodSetDTO((PodSet) rocketComponents));
}
}
}

View File

@ -0,0 +1,110 @@
package net.sf.openrocket.file.rocksim.export;
import net.sf.openrocket.file.rocksim.RockSimCommonConstants;
import net.sf.openrocket.rocketcomponent.BodyTube;
import net.sf.openrocket.rocketcomponent.NoseCone;
import net.sf.openrocket.rocketcomponent.PodSet;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.rocketcomponent.Transition;
import net.sf.openrocket.rocketcomponent.position.AxialMethod;
import net.sf.openrocket.rocketcomponent.position.RadiusMethod;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlElementRefs;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.ArrayList;
import java.util.List;
/**
* Models the XML element for a RockSim pod.
*/
@XmlRootElement(name = RockSimCommonConstants.EXTERNAL_POD)
@XmlAccessorType(XmlAccessType.FIELD)
public class PodSetDTO extends BasePartDTO implements AttachableParts {
@XmlElement(name = RockSimCommonConstants.AUTO_CALC_RADIAL_DISTANCE)
private int autoCalcRadialDistance = 0;
@XmlElement(name = RockSimCommonConstants.AUTO_CALC_RADIAL_ANGLE)
private int autoCalcRadialAngle = 0;
@XmlElementWrapper(name = RockSimCommonConstants.ATTACHED_PARTS)
@XmlElementRefs({
@XmlElementRef(name = RockSimCommonConstants.BODY_TUBE, type = BodyTubeDTO.class),
@XmlElementRef(name = RockSimCommonConstants.NOSE_CONE, type = NoseConeDTO.class),
@XmlElementRef(name = RockSimCommonConstants.TRANSITION, type = TransitionDTO.class),
@XmlElementRef(name = RockSimCommonConstants.EXTERNAL_POD, type = PodSetDTO.class)})
List<BasePartDTO> attachedParts = new ArrayList<BasePartDTO>();
/**
* Default constructor.
*/
public PodSetDTO() {
}
/**
* Copy constructor. Fully populates this instance with values taken from the OR PodSet.
*
* @param theORPodSet
*/
public PodSetDTO(PodSet theORPodSet) {
super(theORPodSet);
// OR should always override the radial angle and distance
setAutoCalcRadialDistance(false);
setAutoCalcRadialAngle(false);
setRadialAngle(theORPodSet.getAngleOffset());
setRadialLoc(theORPodSet.getRadiusMethod().getRadius(
theORPodSet.getParent(), theORPodSet,
theORPodSet.getRadiusOffset()) * RockSimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH);
setXb(theORPodSet.getAxialOffset(AxialMethod.TOP) * RockSimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH);
for (RocketComponent child : theORPodSet.getChildren()) {
if (child instanceof PodSet) {
addAttachedPart(new PodSetDTO((PodSet) child));
} else if (child instanceof BodyTube) {
addAttachedPart(new BodyTubeDTO((BodyTube) child));
} else if (child instanceof NoseCone) {
addAttachedPart(new NoseConeDTO((NoseCone) child));
} else if (child instanceof Transition) {
addAttachedPart(new TransitionDTO((Transition) child));
}
}
}
public int getAutoCalcRadialDistance() {
return autoCalcRadialDistance;
}
public void setAutoCalcRadialDistance(boolean motorMount) {
if (motorMount) {
this.autoCalcRadialDistance = 1;
} else {
this.autoCalcRadialDistance = 0;
}
}
public int getAutoCalcRadialAngle() {
return autoCalcRadialAngle;
}
public void setAutoCalcRadialAngle(boolean motorMount) {
if (motorMount) {
this.autoCalcRadialAngle = 1;
} else {
this.autoCalcRadialAngle = 0;
}
}
@Override
public void addAttachedPart(BasePartDTO part) {
if (!attachedParts.contains(part)) {
attachedParts.add(part);
}
}
@Override
public void removeAttachedPart(BasePartDTO part) {
attachedParts.remove(part);
}
}

View File

@ -8,6 +8,7 @@ import net.sf.openrocket.file.DocumentLoadingContext;
import net.sf.openrocket.file.rocksim.RockSimCommonConstants;
import net.sf.openrocket.file.simplesax.AbstractElementHandler;
import net.sf.openrocket.file.simplesax.ElementHandler;
import net.sf.openrocket.rocketcomponent.PodSet;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import java.util.HashMap;
@ -68,23 +69,32 @@ class AttachedPartsHandler extends AbstractElementHandler {
return new RingHandler(context, component, warnings);
}
if (RockSimCommonConstants.BODY_TUBE.equals(element)) {
return new InnerBodyTubeHandler(context, component, warnings);
// Pods can have BodyTubes as attached parts, but not inner tubes. All other components can't have BodyTubes as
// attached parts.
if (component instanceof PodSet) {
return new BodyTubeHandler(context, component, warnings);
} else {
return new InnerBodyTubeHandler(context, component, warnings);
}
}
if (RockSimCommonConstants.TRANSITION.equals(element)) {
return new TransitionHandler(context, component, warnings);
}
if (RockSimCommonConstants.NOSE_CONE.equals(element)) {
return new NoseConeHandler(context, component, warnings);
}
if (RockSimCommonConstants.SUBASSEMBLY.equals(element)) {
return new SubAssemblyHandler(context, component);
}
if (RockSimCommonConstants.TUBE_FIN_SET.equals(element)) {
return new TubeFinSetHandler(context, component, warnings);
}
if (RockSimCommonConstants.EXTERNAL_POD.equals(element)) {
return new PodHandler(context, component, warnings);
}
if (RockSimCommonConstants.RING_TAIL.equals(element)) {
warnings.add("Ring tails are not currently supported. Ignoring.");
}
if (RockSimCommonConstants.EXTERNAL_POD.equals(element)) {
warnings.add("Pods are not currently supported. Ignoring.");
}
return null;
}
}

View File

@ -25,6 +25,7 @@ class BodyTubeHandler extends BaseHandler<BodyTube> {
* The OpenRocket BodyTube.
*/
private final BodyTube bodyTube;
private int isInsideTube = 0;
/**
* Constructor.
@ -80,6 +81,9 @@ class BodyTubeHandler extends BaseHandler<BodyTube> {
if (RockSimCommonConstants.MATERIAL.equals(element)) {
setMaterialName(content);
}
if (RockSimCommonConstants.IS_INSIDE_TUBE.equals(element)) {
isInsideTube = Integer.parseInt(content);
}
} catch (NumberFormatException nfe) {
warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number.");
}
@ -104,4 +108,11 @@ class BodyTubeHandler extends BaseHandler<BodyTube> {
public Material.Type getMaterialType() {
return Material.Type.BULK;
}
/**
* Returns 0 if this is a body tube, 1 if it is an inside tube.
*/
public int isInsideTube() {
return isInsideTube;
}
}

View File

@ -0,0 +1,66 @@
package net.sf.openrocket.file.rocksim.importt;
import net.sf.openrocket.aerodynamics.WarningSet;
import net.sf.openrocket.file.DocumentLoadingContext;
import net.sf.openrocket.file.rocksim.RockSimCommonConstants;
import net.sf.openrocket.file.simplesax.ElementHandler;
import net.sf.openrocket.file.simplesax.PlainTextHandler;
import net.sf.openrocket.material.Material;
import net.sf.openrocket.rocketcomponent.PodSet;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.rocketcomponent.position.RadiusMethod;
import org.xml.sax.SAXException;
import java.util.HashMap;
public class PodHandler extends PositionDependentHandler<PodSet> {
/**
* The OpenRocket BodyTube.
*/
private final PodSet podSet;
public PodHandler(DocumentLoadingContext context, RocketComponent c, WarningSet warnings) {
super(context);
if (c == null) {
throw new IllegalArgumentException("The parent component of a pod set may not be null.");
}
podSet = new PodSet();
podSet.setInstanceCount(1); // RockSim only supports one pod instance
podSet.setRadiusMethod(RadiusMethod.FREE); // RockSim radial offset is relative to the center of the parent
if (isCompatible(c, PodSet.class, warnings)) {
c.addChild(podSet);
}
}
@Override
public ElementHandler openElement(String element, HashMap<String, String> attributes, WarningSet warnings) throws SAXException {
if (RockSimCommonConstants.BODY_TUBE.equals(element)) { // RockSim pods allow body tubes, not inner tubes
return new BodyTubeHandler(context, podSet, warnings);
}
if (RockSimCommonConstants.ATTACHED_PARTS.equals(element)) {
return new AttachedPartsHandler(context, podSet);
}
return PlainTextHandler.INSTANCE;
}
@Override
public void closeElement(String element, HashMap<String, String> attributes, String content, WarningSet warnings) throws SAXException {
super.closeElement(element, attributes, content, warnings);
if (RockSimCommonConstants.RADIAL_ANGLE.equals(element)) {
podSet.setAngleOffset(Double.parseDouble(content));
}
if (RockSimCommonConstants.RADIAL_LOC.equals(element)) {
podSet.setRadiusOffset(Double.parseDouble(content) / RockSimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH);
}
}
@Override
protected PodSet getComponent() {
return podSet;
}
@Override
protected Material.Type getMaterialType() {
return Material.Type.BULK;
}
}

View File

@ -9,6 +9,8 @@ import net.sf.openrocket.aerodynamics.WarningSet;
import net.sf.openrocket.file.DocumentLoadingContext;
import net.sf.openrocket.file.rocksim.RockSimCommonConstants;
import net.sf.openrocket.file.rocksim.RockSimLocationMode;
import net.sf.openrocket.rocketcomponent.ComponentAssembly;
import net.sf.openrocket.rocketcomponent.ParallelStage;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.rocketcomponent.position.AxialMethod;
@ -70,6 +72,10 @@ public abstract class PositionDependentHandler<C extends RocketComponent> extend
* Set the axialMethod of a component.
*/
protected void setLocation() {
if ((getComponent() instanceof ComponentAssembly || getComponent() instanceof ParallelStage) &&
getComponent().getParent() == null) {
return;
}
getComponent().setAxialMethod(axialMethod);
if (axialMethod.equals(AxialMethod.BOTTOM)) {
getComponent().setAxialOffset(-1d * positionValue);

View File

@ -121,19 +121,23 @@ public class PodSet extends ComponentAssembly implements RingInstanceable {
@Override
public double getAxialOffset() {
return getAxialOffset(this.axialMethod);
}
@Override
public double getAxialOffset(AxialMethod method) {
double returnValue;
if (this.isAfter()){
// remember the implicit (this instanceof Stage)
throw new BugException("found a pod positioned via: AFTER, but is not on the centerline?!: " + this.getName() + " is " + this.getAxialMethod().name() );
} else {
returnValue = super.getAxialOffset(this.axialMethod);
returnValue = super.getAxialOffset(method);
}
if (MathUtil.EPSILON > Math.abs(returnValue)) {
returnValue = 0.0;
}
return returnValue;
}

View File

@ -31,11 +31,14 @@ import net.sf.openrocket.rocketcomponent.InnerTube;
import net.sf.openrocket.rocketcomponent.MassComponent;
import net.sf.openrocket.rocketcomponent.NoseCone;
import net.sf.openrocket.rocketcomponent.Parachute;
import net.sf.openrocket.rocketcomponent.PodSet;
import net.sf.openrocket.rocketcomponent.Rocket;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.rocketcomponent.ShockCord;
import net.sf.openrocket.rocketcomponent.Streamer;
import net.sf.openrocket.rocketcomponent.TubeCoupler;
import net.sf.openrocket.rocketcomponent.position.AxialMethod;
import net.sf.openrocket.rocketcomponent.position.RadiusMethod;
import org.junit.Assert;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
@ -94,6 +97,72 @@ public class RockSimDocumentDTOTest extends RockSimTestBase {
output.delete();
}
/**
* Tests exporting a rocket with pods, and whether importing that same file results in the same pod configuration.
*/
@Test
public void testPodsExport() throws Exception {
OpenRocketDocument originalDocument = makePodsRocket();
Rocket originalRocket = originalDocument.getRocket();
// Convert to RockSim XML
String result = new RockSimSaver().marshalToRockSim(originalDocument);
// Write to .rkt file
Path output = Files.createTempFile("podsRocket", ".rkt");
Files.write(output, result.getBytes(StandardCharsets.UTF_8));
// Read the file
RockSimLoader loader = new RockSimLoader();
InputStream stream = new FileInputStream(output.toFile());
Assert.assertNotNull("Could not open podsRocket.rkt", stream);
OpenRocketDocument importedDocument = OpenRocketDocumentFactory.createEmptyRocket();
DocumentLoadingContext context = new DocumentLoadingContext();
context.setOpenRocketDocument(importedDocument);
context.setMotorFinder(new DatabaseMotorFinder());
loader.loadFromStream(context, new BufferedInputStream(stream));
Rocket importedRocket = importedDocument.getRocket();
// Test children counts
List<RocketComponent> originalChildren = originalRocket.getAllChildren();
List<RocketComponent> importedChildren = importedRocket.getAllChildren();
assertEquals(" Number of total children doesn't match",
originalChildren.size(), importedChildren.size());
assertEquals(" Number of rocket children doesn't match", 1, importedRocket.getChildCount());
AxialStage stage = (AxialStage) importedRocket.getChild(0);
assertEquals(" Number of stage children doesn't match", 2, stage.getChildCount());
BodyTube tube = (BodyTube) stage.getChild(1);
assertEquals(" Number of body tube children doesn't match", 3, tube.getChildCount());
PodSet pod1 = (PodSet) tube.getChild(0);
assertEquals(" Number of pod 1 children doesn't match", 1, pod1.getChildCount());
PodSet pod2 = (PodSet) tube.getChild(1);
assertEquals(" Number of pod 2 children doesn't match", 2, pod2.getChildCount());
PodSet pod3 = (PodSet) tube.getChild(2);
assertEquals(" Number of pod 3 children doesn't match", 0, pod3.getChildCount());
// Test component names
for (int i = 1; i < originalChildren.size(); i++) {
assertEquals(" Child " + i + " does not match",
originalChildren.get(i).getName(), importedChildren.get(i).getName());
}
// Test pod parameters
assertEquals(-0.14, pod1.getAxialOffset(), 0.0001);
assertEquals(0.065, pod1.getRadiusOffset(), 0.0001);
assertEquals(Math.PI, pod1.getAngleOffset(), 0.0001);
assertEquals(1, pod1.getInstanceCount());
assertEquals(0.02, pod2.getAxialOffset(), 0.0001);
assertEquals(0.025, pod2.getRadiusOffset(), 0.0001);
assertEquals(- Math.PI / 2, pod2.getAngleOffset(), 0.0001);
assertEquals(1, pod2.getInstanceCount());
assertEquals(0.23, pod3.getAxialOffset(), 0.0001);
assertEquals(0.06, pod3.getRadiusOffset(), 0.0001);
assertEquals(Math.PI / 3, pod3.getAngleOffset(), 0.0001);
assertEquals(1, pod3.getInstanceCount());
stream.close();
Files.delete(output);
}
/**
* Tests exporting a design where a tube coupler has children, which is not supported by RockSim, so the children
* need to be moved outside the tube coupler.
@ -142,19 +211,91 @@ public class RockSimDocumentDTOTest extends RockSimTestBase {
Files.delete(output);
}
private OpenRocketDocument makeTubeCouplerRocket() {
private OpenRocketDocument makePodsRocket() {
OpenRocketDocument document = OpenRocketDocumentFactory.createNewRocket();
Rocket rocket = document.getRocket();
AxialStage stage = rocket.getStage(0);
// Stage children
NoseCone noseCone = new NoseCone();
noseCone.setName("Nose Cone");
stage.addChild(noseCone);
BodyTube tube = new BodyTube();
tube.setName("Body Tube");
stage.addChild(tube);
// Body tube children
PodSet pod1 = new PodSet();
pod1.setName("Pod 1");
tube.addChild(pod1);
PodSet pod2 = new PodSet();
pod2.setName("Pod 2");
tube.addChild(pod2);
PodSet pod3 = new PodSet();
pod2.setName("Pod 3");
tube.addChild(pod3);
// Pod 1 children
NoseCone noseCone1 = new NoseCone();
noseCone1.setName("Nose Cone 1");
pod1.addChild(noseCone1);
// Pod 2 children
NoseCone noseCone2 = new NoseCone();
noseCone2.setName("Nose Cone 2");
pod2.addChild(noseCone2);
BodyTube tube2 = new BodyTube();
tube2.setName("Body Tube 2");
pod2.addChild(tube2);
// Set pod parameters
pod1.setInstanceCount(1);
pod2.setInstanceCount(2);
pod3.setInstanceCount(3);
pod1.setAxialMethod(AxialMethod.ABSOLUTE);
pod1.setAxialOffset(0.01);
pod2.setAxialMethod(AxialMethod.TOP);
pod2.setAxialOffset(0.02);
pod3.setAxialMethod(AxialMethod.BOTTOM);
pod3.setAxialOffset(0.03);
pod1.setRadiusMethod(RadiusMethod.RELATIVE);
pod1.setRadiusOffset(0.015);
pod2.setRadiusMethod(RadiusMethod.FREE);
pod2.setRadiusOffset(0.025);
pod3.setRadiusMethod(RadiusMethod.RELATIVE);
pod3.setRadiusOffset(0.035);
pod1.setAngleOffset(Math.PI);
pod2.setAngleOffset(- Math.PI / 2);
pod3.setAngleOffset(Math.PI / 3);
return document;
}
private OpenRocketDocument makeTubeCouplerRocket() {
OpenRocketDocument document = OpenRocketDocumentFactory.createNewRocket();
Rocket rocket = document.getRocket();
AxialStage stage = rocket.getStage(0);
// Stage children
NoseCone noseCone = new NoseCone();
noseCone.setName("Nose Cone");
stage.addChild(noseCone);
BodyTube tube = new BodyTube();
tube.setName("Body Tube");
stage.addChild(tube);
// Body tube children
TubeCoupler coupler = new TubeCoupler();
coupler.setName("Tube coupler 1");
tube.addChild(coupler);
TubeCoupler coupler3 = new TubeCoupler();
coupler3.setName("Tube Coupler 3");
tube.addChild(coupler3);
// Tube coupler 1 children
InnerTube innerTube = new InnerTube();
innerTube.setName("Inner Tube");
coupler.addChild(innerTube);
@ -182,9 +323,8 @@ public class RockSimDocumentDTOTest extends RockSimTestBase {
MassComponent massComponent = new MassComponent();
massComponent.setName("Mass Component");
coupler.addChild(massComponent);
TubeCoupler coupler3 = new TubeCoupler();
coupler3.setName("Tube Coupler 3");
tube.addChild(coupler3);
// Tube coupler 3 children
Parachute parachute2 = new Parachute();
parachute2.setName("Parachute 2");
coupler3.addChild(parachute2);

File diff suppressed because it is too large Load Diff

View File

@ -8,10 +8,17 @@ import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import net.sf.openrocket.rocketcomponent.Bulkhead;
import net.sf.openrocket.rocketcomponent.CenteringRing;
import net.sf.openrocket.rocketcomponent.FreeformFinSet;
import net.sf.openrocket.rocketcomponent.InnerTube;
import net.sf.openrocket.rocketcomponent.MassComponent;
import net.sf.openrocket.rocketcomponent.NoseCone;
import net.sf.openrocket.rocketcomponent.Parachute;
import net.sf.openrocket.rocketcomponent.PodSet;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.rocketcomponent.Transition;
import net.sf.openrocket.rocketcomponent.TrapezoidFinSet;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.MathUtil;
import org.junit.Assert;
@ -212,7 +219,41 @@ public class RockSimLoaderTest extends BaseTestCase {
Assert.assertEquals(0.185d, stage1.getOverrideMass(), 0.001);
Assert.assertTrue(stage1.isCGOverridden());
Assert.assertEquals(0.3d, stage1.getOverrideCG().x, 0.001);
Assert.assertEquals(3, loader.getWarnings().size());
Assert.assertEquals(2, loader.getWarnings().size());
NoseCone nc = (NoseCone) stage1.getChild(0);
Assert.assertEquals(2, nc.getChildCount());
Assert.assertEquals("Clay", nc.getChild(0).getName());
RocketComponent it = nc.getChild(1);
Assert.assertEquals(InnerTube.class, it.getClass());
Assert.assertEquals("Attachment Rod", it.getName());
Assert.assertEquals(3, it.getChildCount());
RocketComponent c = it.getChild(0);
Assert.assertEquals(CenteringRing.class, c.getClass());
Assert.assertEquals("Plate", c.getName());
c = it.getChild(1);
Assert.assertEquals(CenteringRing.class, c.getClass());
Assert.assertEquals("Sleeve ", c.getName());
c = it.getChild(2);
Assert.assertEquals(Parachute.class, c.getClass());
Assert.assertEquals("Nose Cone Parachute", c.getName());
BodyTube bt1 = (BodyTube) stage1.getChild(1);
Assert.assertEquals(5, bt1.getChildCount());
Assert.assertEquals("Centering ring", bt1.getChild(0).getName());
Assert.assertEquals("Centering ring", bt1.getChild(1).getName());
c = bt1.getChild(2);
Assert.assertEquals(InnerTube.class, c.getClass());
Assert.assertEquals("Body tube", c.getName());
Assert.assertEquals("Launch lug", bt1.getChild(3).getName());
Assert.assertEquals("Pod", bt1.getChild(4).getName());
PodSet pod = (PodSet) bt1.getChild(4);
Assert.assertEquals(1, pod.getChildCount());
c = pod.getChild(0);
Assert.assertEquals(BodyTube.class, c.getClass());
Assert.assertEquals("Body tube", pod.getChild(0).getName());
Assert.assertEquals(1, stage2.getChildCount());
Assert.assertEquals("2nd Stage Tube", stage2.getChild(0).getName());
@ -221,8 +262,8 @@ public class RockSimLoaderTest extends BaseTestCase {
Assert.assertTrue(stage2.isCGOverridden());
Assert.assertEquals(0.4d, stage2.getOverrideCG().x, 0.001);
BodyTube bt = (BodyTube) stage2.getChild(0);
LaunchLug ll = (LaunchLug) bt.getChild(6);
BodyTube bt2 = (BodyTube) stage2.getChild(0);
LaunchLug ll = (LaunchLug) bt2.getChild(6);
Assert.assertEquals(1.22d, ll.getAngleOffset(), 0.001);
Assert.assertEquals(2, stage3.getChildCount());
@ -342,6 +383,97 @@ public class RockSimLoaderTest extends BaseTestCase {
Assert.assertEquals(0, bodyTube3.getChildCount());
}
@Test
public void testPodRocket() throws IOException, RocketLoadException{
RockSimLoader loader = new RockSimLoader();
OpenRocketDocument doc = loadRockSimRocket(loader, "PodTest.rkt");
Assert.assertNotNull(doc);
Rocket rocket = doc.getRocket();
Assert.assertNotNull(rocket);
Assert.assertEquals("Pod Test", doc.getRocket().getName());
Assert.assertEquals(3, loader.getWarnings().size());
InputStream stream = this.getClass().getResourceAsStream("PodTest.rkt");
Assert.assertNotNull("Could not open PodTest.rkt", stream);
doc = OpenRocketDocumentFactory.createEmptyRocket();
DocumentLoadingContext context = new DocumentLoadingContext();
context.setOpenRocketDocument(doc);
context.setMotorFinder(new DatabaseMotorFinder());
loader.loadFromStream(context, new BufferedInputStream(stream));
Assert.assertNotNull(doc);
rocket = doc.getRocket();
Assert.assertNotNull(rocket);
Assert.assertEquals(1, rocket.getStageCount());
AxialStage stage1 = (AxialStage) rocket.getChild(0);
Assert.assertEquals(3, stage1.getChildCount());
RocketComponent noseCone1 = stage1.getChild(0);
RocketComponent bodyTube1 = stage1.getChild(1);
RocketComponent transition1 = stage1.getChild(2);
Assert.assertEquals(NoseCone.class, noseCone1.getClass());
Assert.assertEquals(BodyTube.class, bodyTube1.getClass());
Assert.assertEquals(Transition.class, transition1.getClass());
Assert.assertEquals("Nose cone 1", noseCone1.getName());
Assert.assertEquals("Body tube 1", bodyTube1.getName());
Assert.assertEquals("Transition 1", transition1.getName());
Assert.assertEquals(1, noseCone1.getChildCount());
RocketComponent component = noseCone1.getChild(0);
Assert.assertEquals(MassComponent.class, component.getClass());
Assert.assertEquals("Mass object 1", component.getName());
Assert.assertEquals(2, bodyTube1.getChildCount());
RocketComponent pod2 = bodyTube1.getChild(0);
Assert.assertEquals(PodSet.class, pod2.getClass());
Assert.assertEquals("Pod 2", pod2.getName());
component = bodyTube1.getChild(1);
Assert.assertEquals(Bulkhead.class, component.getClass());
Assert.assertEquals("Bulkhead 1", component.getName());
Assert.assertEquals(3, pod2.getChildCount());
RocketComponent noseCone2 = pod2.getChild(0);
Assert.assertEquals(NoseCone.class, noseCone2.getClass());
Assert.assertEquals("Nose cone 2", noseCone2.getName());
RocketComponent bodyTube2 = pod2.getChild(1);
Assert.assertEquals(BodyTube.class, bodyTube2.getClass());
Assert.assertEquals("Body tube 2", bodyTube2.getName());
component = pod2.getChild(2);
Assert.assertEquals(Transition.class, component.getClass());
Assert.assertEquals("Transition 2", component.getName());
Assert.assertEquals(1, noseCone2.getChildCount());
component = noseCone2.getChild(0);
Assert.assertEquals(MassComponent.class, component.getClass());
Assert.assertEquals("Mass object 2", component.getName());
Assert.assertEquals(3, bodyTube2.getChildCount());
component = bodyTube2.getChild(0);
Assert.assertEquals(TrapezoidFinSet.class, component.getClass());
Assert.assertEquals("Fin set 2", component.getName());
RocketComponent pod3 = bodyTube2.getChild(1);
Assert.assertEquals(PodSet.class, pod3.getClass());
Assert.assertEquals("Pod 3", pod3.getName());
component = bodyTube2.getChild(2);
Assert.assertEquals(LaunchLug.class, component.getClass());
Assert.assertEquals("Launch lug 1", component.getName());
Assert.assertEquals(1, pod3.getChildCount());
component = pod3.getChild(0);
Assert.assertEquals(BodyTube.class, component.getClass());
Assert.assertEquals("Body tube 3", component.getName());
Assert.assertEquals(0.04, pod3.getAxialOffset(), MathUtil.EPSILON);
Assert.assertEquals(Math.PI / 2, pod3.getAngleOffset(), 0.0001);
Assert.assertEquals(0.05, pod3.getRadiusOffset(), MathUtil.EPSILON);
Assert.assertEquals(1, transition1.getChildCount());
component = transition1.getChild(0);
Assert.assertEquals(MassComponent.class, component.getClass());
Assert.assertEquals("Mass object 3", component.getName());
}
public static OpenRocketDocument loadRockSimRocket(RockSimLoader theLoader, String fileName) throws IOException, RocketLoadException {
try (InputStream stream = RockSimLoaderTest.class.getResourceAsStream(fileName)) {
Assert.assertNotNull("Could not open " + fileName, stream);