Commit 3f89c012 authored by Luc Maisonobe's avatar Luc Maisonobe
Browse files

Finalized parsing of NDM combined messages.

parent 571a160a
......@@ -19,7 +19,7 @@ package org.orekit.files.ccsds.ndm;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Supplier;
import org.orekit.errors.OrekitException;
import org.orekit.errors.OrekitMessages;
......@@ -28,7 +28,6 @@ import org.orekit.files.ccsds.utils.FileFormat;
import org.orekit.files.ccsds.utils.lexical.ParseToken;
import org.orekit.files.ccsds.utils.lexical.XmlTokenBuilder;
import org.orekit.files.ccsds.utils.parsing.AbstractMessageParser;
import org.orekit.files.ccsds.utils.parsing.ErrorState;
/** A parser for the CCSDS NDM (Navigation Data Message).
* @author Luc Maisonobe
......@@ -82,8 +81,7 @@ public class NdmParser extends AbstractMessageParser<NdmFile> {
/** {@inheritDoc} */
@Override
public void reset(final FileFormat fileFormat) {
reset(fileFormat, this::processStructureToken);
setFallback(new ErrorState());
reset(fileFormat, this::processToken);
constituentParser = null;
comments = new CommentsContainer();
constituents = new ArrayList<>();
......@@ -92,13 +90,8 @@ public class NdmParser extends AbstractMessageParser<NdmFile> {
/** {@inheritDoc} */
@Override
public NdmFile build() {
// store the previous constituent if any
storeLastConstituent();
// build the file from parsed comments and constituents
return new NdmFile(comments.getComments(), constituents);
}
/**
......@@ -114,61 +107,100 @@ public class NdmParser extends AbstractMessageParser<NdmFile> {
return comments.addComment(comment);
}
/** Store last parsed constituent.
/** Prepare parsing of a TDM constituent.
* @return always return true
*/
private void storeLastConstituent() {
if (constituentParser != null) {
constituents.add(constituentParser.build());
}
constituentParser = null;
boolean manageTdmConstituent() {
return manageConstituent(builder::buildTdmParser);
}
/** Prepare parsing of an OPM constituent.
* @return always return true
*/
boolean manageOpmConstituent() {
return manageConstituent(builder::buildOpmParser);
}
/** Prepare parsing of an OMM constituent.
* @return always return true
*/
boolean manageOmmConstituent() {
return manageConstituent(builder::buildOmmParser);
}
/** Prepare parsing of an OEM constituent.
* @return always return true
*/
boolean manageOemConstituent() {
return manageConstituent(builder::buildOemParser);
}
/** Prepare parsing of an OCM constituent.
* @return always return true
*/
boolean manageOcmConstituent() {
return manageConstituent(builder::buildOcmParser);
}
/** Prepare parsing of an APM constituent.
* @return always return true
*/
boolean manageApmConstituent() {
return manageConstituent(builder::buildApmParser);
}
/** Prepare parsing of a AEM constituent.
* @return always return true
*/
boolean manageAemConstituent() {
return manageConstituent(builder::buildAemParser);
}
/** Prepare parsing of a constituent.
* <p>
* Returning false allows the root element of the constituent to be handled by the dedicated parser
* </p>
* otherwise it is leaving the section
* @param build builder for constituent parser
* @return always return false!
* @param parserSupplier supplier for constituent parser
* @return always return true
*/
boolean manageConstituent(final Function<ParserBuilder, AbstractMessageParser<? extends NdmConstituent<?, ?>>> build) {
boolean manageConstituent(final Supplier<AbstractMessageParser<? extends NdmConstituent<?, ?>>> parserSupplier) {
// as we have started parsing constituents, we cannot accept any further comments
comments.refuseFurtherComments();
// store the previous constituent if any
storeLastConstituent();
// create a parser for the constituent
constituentParser = build.apply(builder);
constituentParser = parserSupplier.get();
constituentParser.reset(getFileFormat());
// delegate to the constituent parser the parsing of upcoming tokens
// (including the current one that will be rejected below)
anticipateNext(constituentParser.getCurrent());
setFallback(this::processStructureToken);
// reject current token, so it will be parsed by the constituent just built
return false;
return true;
}
/** Process one structure token.
/** Process one token.
* @param token token to process
* @return true if token was processed, false otherwise
*/
private boolean processStructureToken(final ParseToken token) {
private boolean processToken(final ParseToken token) {
if (getFileFormat() == FileFormat.KVN) {
// NDM combined instantiation can only be formatted as XML messages
throw new OrekitException(OrekitMessages.UNSUPPORTED_FILE_FORMAT, token.getFileName());
}
try {
return NdmStructureKey.valueOf(token.getName()).process(token, this);
} catch (IllegalArgumentException iae) {
// token has not been recognized
return false;
if (constituentParser == null) {
// we are in the global NDM structure
try {
return NdmStructureKey.valueOf(token.getName()).process(token, this);
} catch (IllegalArgumentException iae) {
// token has not been recognized
return false;
}
} else {
// we are inside one constituent
constituentParser.process(token);
if (constituentParser.wasEndTagSeen()) {
// we have seen the end tag, we must go back global structure parsing
constituents.add(constituentParser.build());
constituentParser = null;
}
return true;
}
}
......
......@@ -33,25 +33,25 @@ enum NdmStructureKey {
ndm((token, parser) -> true),
/** TDM constituent. */
CCSDS_TDM_VERSION((token, parser) -> parser.manageConstituent(builder -> builder.buildTdmParser())),
tdm((token, parser) -> parser.manageTdmConstituent()),
/** OPM constituent. */
CCSDS_OPM_VERSION((token, parser) -> parser.manageConstituent(builder -> builder.buildOpmParser())),
opm((token, parser) -> parser.manageOpmConstituent()),
/** OMM constituent. */
CCSDS_OMM_VERSION((token, parser) -> parser.manageConstituent(builder -> builder.buildOmmParser())),
omm((token, parser) -> parser.manageOmmConstituent()),
/** OEM constituent. */
CCSDS_OEM_VERSION((token, parser) -> parser.manageConstituent(builder -> builder.buildOemParser())),
oem((token, parser) -> parser.manageOemConstituent()),
/** OCM constituent. */
CCSDS_OCM_VERSION((token, parser) -> parser.manageConstituent(builder -> builder.buildOcmParser())),
ocm((token, parser) -> parser.manageOcmConstituent()),
/** APM constituent. */
CCSDS_APM_VERSION((token, parser) -> parser.manageConstituent(builder -> builder.buildApmParser())),
apm((token, parser) -> parser.manageApmConstituent()),
/** AEM constituent. */
CCSDS_AEM_VERSION((token, parser) -> parser.manageConstituent(builder -> builder.buildAemParser()));
aem((token, parser) -> parser.manageAemConstituent());
/** Processing method. */
private final TokenProcessor processor;
......
......@@ -50,7 +50,7 @@ public class XmlStructureProcessingState implements ProcessingState {
public boolean processToken(final ParseToken token) {
if (root.equals(token.getName())) {
// ignored
parser.setEndTagSeen(token.getType() == TokenType.STOP);
return true;
}
......
......@@ -68,6 +68,9 @@ public abstract class AbstractMessageParser<T> implements MessageParser<T> {
/** Format of the file ready to be parsed. */
private FileFormat format;
/** Flag for XML end tag. */
private boolean endTagSeen;
/** Simple constructor.
* @param root root element for XML files
* @param formatVersionKey key for format version
......@@ -95,11 +98,26 @@ public abstract class AbstractMessageParser<T> implements MessageParser<T> {
* @param initialState initial processing state
*/
protected void reset(final FileFormat fileFormat, final ProcessingState initialState) {
format = fileFormat;
current = initialState;
format = fileFormat;
current = initialState;
endTagSeen = false;
anticipateNext(fallback);
}
/** Set the flag for XML end tag.
* @param endTagSeen if true, the XML end tag has been seen
*/
public void setEndTagSeen(final boolean endTagSeen) {
this.endTagSeen = endTagSeen;
}
/** Check if XML end tag has been seen.
* @return true if XML end tag has been seen
*/
public boolean wasEndTagSeen() {
return endTagSeen;
}
/** Get the current processing state.
* @return current processing state
*/
......
......@@ -23,6 +23,8 @@ import org.orekit.Utils;
import org.orekit.data.DataSource;
import org.orekit.errors.OrekitException;
import org.orekit.errors.OrekitMessages;
import org.orekit.files.ccsds.ndm.adm.apm.ApmFile;
import org.orekit.files.ccsds.ndm.odm.ocm.OcmFile;
import org.orekit.files.ccsds.ndm.odm.opm.OpmFile;
/**
......@@ -68,9 +70,27 @@ public class NdmParserTest {
final DataSource source = new DataSource(name, () -> NdmParserTest.class.getResourceAsStream(name));
final NdmFile ndm = new ParserBuilder().buildNdmParser().parseMessage(source);
Assert.assertEquals(1, ndm.getComments().size());
Assert.assertEquals("NDM with only one constituent: an OPM", ndm.getComments().size());
Assert.assertEquals("NDM with only one constituent: an OPM", ndm.getComments().get(0));
Assert.assertEquals(1, ndm.getConstituents().size());
OpmFile opm = (OpmFile) ndm.getConstituents().get(0);
Assert.assertEquals("OSPREY 5", opm.getMetadata().getObjectName());
Assert.assertEquals(3000.0, opm.getData().getSpacecraftParametersBlock().getMass(), 1.0e-10);
}
@Test
public void testOpmApm() {
final String name = "/ccsds/ndm/NDM-ocm-apm.xml";
final DataSource source = new DataSource(name, () -> NdmParserTest.class.getResourceAsStream(name));
final NdmFile ndm = new ParserBuilder().buildNdmParser().parseMessage(source);
Assert.assertEquals(1, ndm.getComments().size());
Assert.assertEquals("NDM with two constituents: an OCM and an APM", ndm.getComments().get(0));
Assert.assertEquals(2, ndm.getConstituents().size());
OcmFile ocm = (OcmFile) ndm.getConstituents().get(0);
Assert.assertEquals("1998-999A", ocm.getMetadata().getInternationalDesignator());
Assert.assertEquals("WGS-84", ocm.getData().getUserDefinedBlock().getParameters().get("EARTH_MODEL"));
ApmFile apm = (ApmFile) ndm.getConstituents().get(1);
Assert.assertEquals("MARS SPIRIT", apm.getMetadata().getObjectName());
Assert.assertEquals("INSTRUMENT_A", apm.getData().getQuaternionBlock().getEndpoints().getFrameA().getName());
}
}
<?xml version="1.0" encoding="UTF-8"?>
<ndm>
<COMMENT>NDM with two constituents: an OCM and an APM</COMMENT>
<ocm id="CCSDS_OCM_VERS" version="3.0">
<header>
<CREATION_DATE>1998-11-06T09:23:57</CREATION_DATE>
<ORIGINATOR>JAXA</ORIGINATOR>
<MESSAGE_ID> OCM 201113719185</MESSAGE_ID> </header>
<body>
<segment>
<metadata>
<INTERNATIONAL_DESIGNATOR>1998-999A</INTERNATIONAL_DESIGNATOR>
<TIME_SYSTEM>UT1</TIME_SYSTEM>
<EPOCH_TZERO>1998-12-18T00:00:00.0000</EPOCH_TZERO>
<TAIMUTC_AT_TZERO units="s">36</TAIMUTC_AT_TZERO>
<UT1MUTC_AT_TZERO units="s">.357</UT1MUTC_AT_TZERO>
</metadata>
<data>
<orb>
<COMMENT>GEOCENTRIC, CARTESIAN, EARTH FIXED</COMMENT>
<ORB_BASIS>PREDICTED</ORB_BASIS >
<ORB_REF_FRAME>EFG</ORB_REF_FRAME>
<ORB_TYPE>CARTPVA</ORB_TYPE>
<orbLine>0.0 2854.5 -2916.2 -5360.7 5.90 4.86 0.52 0.0037 -0.0038 -0.0070</orbLine>
</orb>
<userDef>
<USER_DEFINED parameter="EARTH_MODEL">WGS-84</USER_DEFINED>
</userDef>
</data>
</segment>
</body>
</ocm>
<apm id="CCSDS_APM_VERS" version="1.0">
<header>
<CREATION_DATE>2004-02-14T19:23:57</CREATION_DATE>
<ORIGINATOR>JPL</ORIGINATOR>
</header>
<body>
<segment>
<metadata>
<OBJECT_NAME>MARS SPIRIT</OBJECT_NAME>
<OBJECT_ID>2004-003A</OBJECT_ID>
<CENTER_NAME>EARTH</CENTER_NAME>
<TIME_SYSTEM>UTC</TIME_SYSTEM>
</metadata>
<data>
<COMMENT>Attitude state quaternion</COMMENT>
<quaternionState>
<EPOCH>2004-02-14T14:28:15.1172</EPOCH>
<Q_FRAME_A>INSTRUMENT_A</Q_FRAME_A>
<Q_FRAME_B>ITRF-97</Q_FRAME_B>
<Q_DIR>A2B</Q_DIR>
<quaternion>
<Q1>0.03123</Q1>
<Q2>0.78543</Q2>
<Q3>0.39158</Q3>
<QC>0.47832</QC>
</quaternion>
</quaternionState>
</data>
</segment>
</body>
</apm>
</ndm>
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment