Skip to content

Commit

Permalink
Add error handling for unknown parameter type element during XTCE par…
Browse files Browse the repository at this point in the history
…sing
  • Loading branch information
medley56 committed Mar 29, 2024
1 parent 55950d6 commit 0979d6c
Show file tree
Hide file tree
Showing 6 changed files with 268 additions and 5 deletions.
1 change: 1 addition & 0 deletions docs/source/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Release notes for the `space_packet_parser` library
- Drop support for bitstring <4.0.1
- Support BooleanExpression in a ContextCalibrator
- Default read size is changed to a full file read on file-like objects
- Improve error handling for invalid/unsupported parameter types

### v4.1.1 (released)
- Allow Python 3.12
Expand Down
32 changes: 28 additions & 4 deletions space_packet_parser/xtcedef.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ class CalibrationError(Exception):
pass


class InvalidParameterTypeError(Exception):
"""Error raised when someone is using an invalid ParameterType element"""
pass


# Common comparable mixin
class AttrComparable(metaclass=ABCMeta):
"""Generic class that provides a notion of equality based on all non-callable, non-dunder attributes"""
Expand Down Expand Up @@ -2207,18 +2212,23 @@ def __init__(self, xtce_document: str or Path, ns: dict = None):
self._tag_to_type_template.items()}
self.tree = ElementTree.parse(xtce_document)

self._populate_sequence_container_cache()

def __getitem__(self, item):
return self._sequence_container_cache[item]

def _populate_sequence_container_cache(self):
"""Force populating sequence_container_cache by parsing all SequenceContainers"""
for sequence_container in self.container_set.iterfind('xtce:SequenceContainer', self.ns):
self._sequence_container_cache[
sequence_container.attrib['name']
] = self.parse_sequence_container_contents(sequence_container)

# Back-populate the list of inheritors for each container
for name, sc in self._sequence_container_cache.items():
if sc.base_container_name:
self._sequence_container_cache[sc.base_container_name].inheritors.append(name)

def __getitem__(self, item):
return self._sequence_container_cache[item]

def parse_sequence_container_contents(self, sequence_container: ElementTree.Element) -> SequenceContainer:
"""Parses the list of parameters in a SequenceContainer element, recursively parsing nested SequenceContainers
to build an entry list of parameters that flattens the nested structure to derive a sequential ordering of
Expand Down Expand Up @@ -2263,7 +2273,19 @@ def parse_sequence_container_contents(self, sequence_container: ElementTree.Elem
parameter_type_object = self._parameter_type_cache[parameter_type_name]
else:
parameter_type_element = self._find_parameter_type(parameter_type_name)
parameter_type_class = self.type_tag_to_object[parameter_type_element.tag]
try:
parameter_type_class = self.type_tag_to_object[parameter_type_element.tag]
except KeyError as e:
if ("ArrayParameterType" in parameter_type_element.tag or
"AggregateParameterType" in parameter_type_element.tag):
raise NotImplementedError(f"Unsupported parameter type {parameter_type_element.tag}. "
"Supporting this parameter type is in the roadmap but has "
"not yet been implemented.") from e
raise InvalidParameterTypeError(f"Invalid parameter type {parameter_type_element.tag}. "
"If you believe this is a valid XTCE parameter type, "
"please open a feature request as a Github issue with a "
"reference to the XTCE element description for the "
"parameter type element.") from e
parameter_type_object = parameter_type_class.from_parameter_type_xml_element(
parameter_type_element, self.ns)
self._parameter_type_cache[parameter_type_name] = parameter_type_object # Add to cache
Expand Down Expand Up @@ -2374,6 +2396,8 @@ def flatten_container(sequence_container: SequenceContainer):
aggregated_entry_list.append(entry)
return aggregated_entry_list, aggregated_restrictions

warnings.warn("The 'flattened_containers' property is deprecated to allow for dynamic container "
"inheritance matching during parsing.", DeprecationWarning)
return {
name: FlattenedContainer(*flatten_container(sc))
for name, sc in self._sequence_container_cache.items()
Expand Down
4 changes: 3 additions & 1 deletion tests/test_data/test_xtce.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?xml version='1.0' encoding='UTF-8'?>
<xtce:SpaceSystem xmlns:xtce="http://www.omg.org/space/xtce" name="Space Packet Parser">
<xtce:SpaceSystem xmlns:xtce="http://www.omg.org/space/xtce" name="Space Packet Parser"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.omg.org/spec/XTCE/20180204/SpaceSystem.xsd">
<xtce:Header date="2024-03-05T13:36:00MST" version="1.0" author="Gavin Medley"/>
<xtce:TelemetryMetaData>
<xtce:ParameterTypeSet>
Expand Down
104 changes: 104 additions & 0 deletions tests/test_data/test_xtce_invalid.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<?xml version='1.0' encoding='UTF-8'?>
<xtce:SpaceSystem xmlns:xtce="http://www.omg.org/space/xtce" name="Space Packet Parser"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.omg.org/spec/XTCE/20180204/SpaceSystem.xsd">
<xtce:Header date="2024-03-05T13:36:00MST" version="1.0" author="Gavin Medley"/>
<xtce:TelemetryMetaData>
<xtce:ParameterTypeSet>
<xtce:InvalidParameterType name="TEST_INVALID_Type" signed="false">
<xtce:UnitSet/>
<xtce:IntegerDataEncoding sizeInBits="3" encoding="unsigned"/>
</xtce:InvalidParameterType>
<xtce:IntegerParameterType name="VERSION_Type" signed="false">
<xtce:UnitSet/>
<xtce:IntegerDataEncoding sizeInBits="3" encoding="unsigned"/>
</xtce:IntegerParameterType>
<xtce:IntegerParameterType name="TYPE_Type" signed="false">
<xtce:UnitSet/>
<xtce:IntegerDataEncoding sizeInBits="1" encoding="unsigned"/>
</xtce:IntegerParameterType>
<xtce:IntegerParameterType name="SEC_HDR_FLG_Type" signed="false">
<xtce:UnitSet/>
<xtce:IntegerDataEncoding sizeInBits="1" encoding="unsigned"/>
</xtce:IntegerParameterType>
<xtce:IntegerParameterType name="PKT_APID_Type" signed="false">
<xtce:UnitSet/>
<xtce:IntegerDataEncoding sizeInBits="11" encoding="unsigned"/>
</xtce:IntegerParameterType>
<xtce:IntegerParameterType name="SEQ_FLGS_Type" signed="false">
<xtce:UnitSet/>
<xtce:IntegerDataEncoding sizeInBits="2" encoding="unsigned"/>
</xtce:IntegerParameterType>
<xtce:IntegerParameterType name="SRC_SEQ_CTR_Type" signed="false">
<xtce:UnitSet/>
<xtce:IntegerDataEncoding sizeInBits="14" encoding="unsigned"/>
</xtce:IntegerParameterType>
<xtce:IntegerParameterType name="PKT_LEN_Type" signed="false">
<xtce:UnitSet/>
<xtce:IntegerDataEncoding sizeInBits="16" encoding="unsigned"/>
</xtce:IntegerParameterType>
</xtce:ParameterTypeSet>
<xtce:ParameterSet>
<xtce:Parameter name="VERSION" parameterTypeRef="VERSION_Type">
<xtce:LongDescription>Not really used. We aren't changing the version of CCSDS that we use.
</xtce:LongDescription>
</xtce:Parameter>
<xtce:Parameter name="TYPE" parameterTypeRef="TYPE_Type">
<xtce:LongDescription>Indicates whether this packet is CMD or TLM. TLM is 0.</xtce:LongDescription>
</xtce:Parameter>
<xtce:Parameter name="SEC_HDR_FLG" parameterTypeRef="SEC_HDR_FLG_Type">
<xtce:LongDescription>Always 1 - indicates that there is a secondary header.</xtce:LongDescription>
</xtce:Parameter>
<xtce:Parameter name="PKT_APID" parameterTypeRef="PKT_APID_Type">
<xtce:LongDescription>Unique to each packet type.</xtce:LongDescription>
</xtce:Parameter>
<xtce:Parameter name="SEQ_FLGS" parameterTypeRef="SEQ_FLGS_Type">
<xtce:LongDescription>Always set to 1.</xtce:LongDescription>
</xtce:Parameter>
<xtce:Parameter name="SRC_SEQ_CTR" parameterTypeRef="SRC_SEQ_CTR_Type">
<xtce:LongDescription>Increments from 0 at reset for each packet issued of that APID. Rolls over at
14b.
</xtce:LongDescription>
</xtce:Parameter>
<xtce:Parameter name="PKT_LEN" parameterTypeRef="PKT_LEN_Type">
<xtce:LongDescription>Number of bytes of the data field following the primary header -1. (To get the
length of the whole packet, add 7)
</xtce:LongDescription>
</xtce:Parameter>
<xtce:Parameter name="INVALID" parameterTypeRef="TEST_INVALID_Type"/>
</xtce:ParameterSet>
<xtce:ContainerSet>
<xtce:SequenceContainer name="CCSDSPacket" abstract="true">
<xtce:LongDescription>Super-container for telemetry and command packets</xtce:LongDescription>
<xtce:EntryList>
<xtce:ParameterRefEntry parameterRef="VERSION"/>
<xtce:ParameterRefEntry parameterRef="TYPE"/>
<xtce:ParameterRefEntry parameterRef="SEC_HDR_FLG"/>
<xtce:ParameterRefEntry parameterRef="PKT_APID"/>
<xtce:ParameterRefEntry parameterRef="SEQ_FLGS"/>
<xtce:ParameterRefEntry parameterRef="SRC_SEQ_CTR"/>
<xtce:ParameterRefEntry parameterRef="PKT_LEN"/>
</xtce:EntryList>
</xtce:SequenceContainer>
<xtce:SequenceContainer name="CCSDSTelemetryPacket" abstract="true">
<xtce:LongDescription>Super-container for all telemetry packets</xtce:LongDescription>
<xtce:EntryList>
<xtce:ContainerRefEntry containerRef="TEST_CONTAINER"/>
</xtce:EntryList>
<xtce:BaseContainer containerRef="CCSDSPacket">
<xtce:RestrictionCriteria>
<xtce:ComparisonList>
<xtce:Comparison parameterRef="VERSION" value="0" useCalibratedValue="false"/>
<xtce:Comparison parameterRef="TYPE" value="0" useCalibratedValue="false"/>
</xtce:ComparisonList>
</xtce:RestrictionCriteria>
</xtce:BaseContainer>
</xtce:SequenceContainer>
<xtce:SequenceContainer name="TEST_CONTAINER" shortDescription="Test container">
<xtce:EntryList>
<xtce:ParameterRefEntry parameterRef="INVALID"/>
</xtce:EntryList>
</xtce:SequenceContainer>
</xtce:ContainerSet>
</xtce:TelemetryMetaData>
</xtce:SpaceSystem>
116 changes: 116 additions & 0 deletions tests/test_data/test_xtce_unsupported_array_type.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<?xml version='1.0' encoding='UTF-8'?>
<xtce:SpaceSystem xmlns:xtce="http://www.omg.org/space/xtce" name="Space Packet Parser"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.omg.org/spec/XTCE/20180204/SpaceSystem.xsd">
<xtce:Header date="2024-03-05T13:36:00MST" version="1.0" author="Gavin Medley"/>
<xtce:TelemetryMetaData>
<xtce:ParameterTypeSet>
<xtce:ArrayParameterType name="TEST_ARRAY_Type" arrayTypeRef="TYPE_Type">
<xtce:DimensionList>
<xtce:Dimension>
<xtce:StartingIndex>
<xtce:FixedValue>0</xtce:FixedValue>
</xtce:StartingIndex>
<xtce:EndingIndex>
<xtce:FixedValue>4</xtce:FixedValue>
</xtce:EndingIndex>
</xtce:Dimension>
</xtce:DimensionList>
</xtce:ArrayParameterType>
<xtce:InvalidParameterType name="TEST_INVALID_Type" signed="false">
<xtce:UnitSet/>
<xtce:IntegerDataEncoding sizeInBits="3" encoding="unsigned"/>
</xtce:InvalidParameterType>
<xtce:IntegerParameterType name="VERSION_Type" signed="false">
<xtce:UnitSet/>
<xtce:IntegerDataEncoding sizeInBits="3" encoding="unsigned"/>
</xtce:IntegerParameterType>
<xtce:IntegerParameterType name="TYPE_Type" signed="false">
<xtce:UnitSet/>
<xtce:IntegerDataEncoding sizeInBits="1" encoding="unsigned"/>
</xtce:IntegerParameterType>
<xtce:IntegerParameterType name="SEC_HDR_FLG_Type" signed="false">
<xtce:UnitSet/>
<xtce:IntegerDataEncoding sizeInBits="1" encoding="unsigned"/>
</xtce:IntegerParameterType>
<xtce:IntegerParameterType name="PKT_APID_Type" signed="false">
<xtce:UnitSet/>
<xtce:IntegerDataEncoding sizeInBits="11" encoding="unsigned"/>
</xtce:IntegerParameterType>
<xtce:IntegerParameterType name="SEQ_FLGS_Type" signed="false">
<xtce:UnitSet/>
<xtce:IntegerDataEncoding sizeInBits="2" encoding="unsigned"/>
</xtce:IntegerParameterType>
<xtce:IntegerParameterType name="SRC_SEQ_CTR_Type" signed="false">
<xtce:UnitSet/>
<xtce:IntegerDataEncoding sizeInBits="14" encoding="unsigned"/>
</xtce:IntegerParameterType>
<xtce:IntegerParameterType name="PKT_LEN_Type" signed="false">
<xtce:UnitSet/>
<xtce:IntegerDataEncoding sizeInBits="16" encoding="unsigned"/>
</xtce:IntegerParameterType>
</xtce:ParameterTypeSet>
<xtce:ParameterSet>
<xtce:Parameter name="VERSION" parameterTypeRef="VERSION_Type">
<xtce:LongDescription>Not really used. We aren't changing the version of CCSDS that we use.
</xtce:LongDescription>
</xtce:Parameter>
<xtce:Parameter name="TYPE" parameterTypeRef="TYPE_Type">
<xtce:LongDescription>Indicates whether this packet is CMD or TLM. TLM is 0.</xtce:LongDescription>
</xtce:Parameter>
<xtce:Parameter name="SEC_HDR_FLG" parameterTypeRef="SEC_HDR_FLG_Type">
<xtce:LongDescription>Always 1 - indicates that there is a secondary header.</xtce:LongDescription>
</xtce:Parameter>
<xtce:Parameter name="PKT_APID" parameterTypeRef="PKT_APID_Type">
<xtce:LongDescription>Unique to each packet type.</xtce:LongDescription>
</xtce:Parameter>
<xtce:Parameter name="SEQ_FLGS" parameterTypeRef="SEQ_FLGS_Type">
<xtce:LongDescription>Always set to 1.</xtce:LongDescription>
</xtce:Parameter>
<xtce:Parameter name="SRC_SEQ_CTR" parameterTypeRef="SRC_SEQ_CTR_Type">
<xtce:LongDescription>Increments from 0 at reset for each packet issued of that APID. Rolls over at
14b.
</xtce:LongDescription>
</xtce:Parameter>
<xtce:Parameter name="PKT_LEN" parameterTypeRef="PKT_LEN_Type">
<xtce:LongDescription>Number of bytes of the data field following the primary header -1. (To get the
length of the whole packet, add 7)
</xtce:LongDescription>
</xtce:Parameter>
<xtce:Parameter name="ARRAY" parameterTypeRef="TEST_ARRAY_Type"/>
</xtce:ParameterSet>
<xtce:ContainerSet>
<xtce:SequenceContainer name="CCSDSPacket" abstract="true">
<xtce:LongDescription>Super-container for telemetry and command packets</xtce:LongDescription>
<xtce:EntryList>
<xtce:ParameterRefEntry parameterRef="VERSION"/>
<xtce:ParameterRefEntry parameterRef="TYPE"/>
<xtce:ParameterRefEntry parameterRef="SEC_HDR_FLG"/>
<xtce:ParameterRefEntry parameterRef="PKT_APID"/>
<xtce:ParameterRefEntry parameterRef="SEQ_FLGS"/>
<xtce:ParameterRefEntry parameterRef="SRC_SEQ_CTR"/>
<xtce:ParameterRefEntry parameterRef="PKT_LEN"/>
</xtce:EntryList>
</xtce:SequenceContainer>
<xtce:SequenceContainer name="CCSDSTelemetryPacket" abstract="true">
<xtce:LongDescription>Super-container for all telemetry packets</xtce:LongDescription>
<xtce:EntryList>
<xtce:ContainerRefEntry containerRef="TEST_CONTAINER"/>
</xtce:EntryList>
<xtce:BaseContainer containerRef="CCSDSPacket">
<xtce:RestrictionCriteria>
<xtce:ComparisonList>
<xtce:Comparison parameterRef="VERSION" value="0" useCalibratedValue="false"/>
<xtce:Comparison parameterRef="TYPE" value="0" useCalibratedValue="false"/>
</xtce:ComparisonList>
</xtce:RestrictionCriteria>
</xtce:BaseContainer>
</xtce:SequenceContainer>
<xtce:SequenceContainer name="TEST_CONTAINER" shortDescription="Test container">
<xtce:EntryList>
<xtce:ParameterRefEntry parameterRef="ARRAY"/>
</xtce:EntryList>
</xtce:SequenceContainer>
</xtce:ContainerSet>
</xtce:TelemetryMetaData>
</xtce:SpaceSystem>
16 changes: 16 additions & 0 deletions tests/unit/test_xtcedef.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,20 @@
TEST_NAMESPACE = {'xtce': 'http://www.omg.org/space/xtce'}


def test_invalid_parameter_type_error(test_data_dir):
"""Test proper reporting of an invalid parameter type element"""
with open(test_data_dir / "test_xtce_invalid.xml") as x:
with pytest.raises(xtcedef.InvalidParameterTypeError):
xtcedef.XtcePacketDefinition(x, ns=TEST_NAMESPACE)


def test_unsupported_parameter_type_error(test_data_dir):
"""Test proper reporting of an unsupported parameter type element"""
with open(test_data_dir / "test_xtce_unsupported_array_type.xml") as x:
with pytest.raises(NotImplementedError):
xtcedef.XtcePacketDefinition(x, ns=TEST_NAMESPACE)


def test_attr_comparable():
"""Test abstract class that allows comparisons based on all non-callable attributes"""
class TestClass(xtcedef.AttrComparable):
Expand Down Expand Up @@ -1791,6 +1805,8 @@ def test_parsing_xtce_document(test_data_dir):
with open(test_data_dir / "test_xtce.xml") as x:
xdef = xtcedef.XtcePacketDefinition(x, ns=TEST_NAMESPACE)

xdef._populate_sequence_container_cache()

# Test Parameter Types
ptname = "USEC_Type"
pt = xdef.named_parameter_types[ptname]
Expand Down

0 comments on commit 0979d6c

Please sign in to comment.