Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ than one that stops, because the gap only shows up in flight.
| Integers, 1–64 bits, all three signed codings | Yes |
| IEEE-754 binary16, binary32, binary64 | Yes |
| MIL-STD-1750A | Refused — reading it is many-to-one, so there is no inverse to write |
| A base container left concrete beside its own children | Refused by name — `abstract` defaults to false in XTCE, and a container with no restriction criteria matches every packet long enough to hold it, so the generated `decode` would answer `Ambiguous` for all of them |
| Booleans, enumerations (as generated Rust enums) | Yes |
| Fixed-size text: whole-buffer, terminated, length-prefixed | Yes |
| Fixed-size binary | Yes |
Expand Down
21 changes: 21 additions & 0 deletions crates/xtce-flight/src/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,27 @@ pub fn build(plan: &Plan) -> Result<Layout, FlightError> {
});
}

// A container with no restriction criteria matches every packet long enough to hold it —
// its generated `matches` is `true` — so a decoder holding it beside any other container
// answers `Ambiguous` for every packet it is ever given. XTCE's `abstract` defaults to
// false, which makes an unmarked base container, the ordinary CCSDS primary-header shape,
// exactly that. Refused here by name rather than generated into a decoder that rejects
// every packet once it is on the spacecraft.
let unconditional = containers
.iter()
.find(|held| held.constants.is_empty())
.filter(|_| containers.len() > 1);
if let Some(unconditional) = unconditional {
return Err(FlightError::Unsupported {
element: "SequenceContainer".to_owned(),
container: unconditional.xtce_name.clone(),
reason: "this container has no restriction criteria, so it matches every packet \
long enough to hold it; beside the other containers generated here that \
makes every packet ambiguous. Mark it abstract=\"true\" if other \
containers inherit from it, or give it criteria that select it",
});
}

Ok(Layout {
containers,
enums: builder.enums,
Expand Down
48 changes: 48 additions & 0 deletions crates/xtce-flight/tests/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,54 @@ fn a_data_dependent_width_is_refused() {
);
}

/// A base left concrete matches everything, and `abstract` defaults to false.
///
/// The generated `matches` for a container with no criteria is `true`, so a decoder holding it
/// beside its own children answers `Ambiguous` for every packet. Refused at generation time
/// rather than discovered on the spacecraft.
#[test]
fn a_base_container_that_is_not_abstract_is_refused_by_name() {
let body = r#" <ParameterTypeSet>
<IntegerParameterType name="APID_T">
<IntegerDataEncoding sizeInBits="8" encoding="unsigned"/>
</IntegerParameterType>
<IntegerParameterType name="DATA_T">
<IntegerDataEncoding sizeInBits="8" encoding="unsigned"/>
</IntegerParameterType>
</ParameterTypeSet>
<ParameterSet>
<Parameter name="APID" parameterTypeRef="APID_T"/>
<Parameter name="DATA" parameterTypeRef="DATA_T"/>
</ParameterSet>
<ContainerSet>
<SequenceContainer name="Base">
<EntryList>
<ParameterRefEntry parameterRef="APID"/>
</EntryList>
</SequenceContainer>
<SequenceContainer name="Child">
<EntryList>
<ParameterRefEntry parameterRef="DATA"/>
</EntryList>
<BaseContainer containerRef="Base">
<RestrictionCriteria>
<Comparison parameterRef="APID" value="42" useCalibratedValue="false"/>
</RestrictionCriteria>
</BaseContainer>
</SequenceContainer>
</ContainerSet>"#;

let message = refusal(layout_of(body));
assert!(
message.contains("Base"),
"the refusal has to name it: {message}"
);
assert!(
message.contains("no restriction criteria"),
"unexpected refusal: {message}"
);
}

#[test]
fn restriction_criteria_become_constants_and_leave_the_struct() {
let body = r#" <ParameterTypeSet>
Expand Down
Loading