# `ccsds_tc_services`

The `ccsds_tc_services` library provides service-aware helpers that sit above raw `ccsds_frames` telecommand transfer frame terms.

The current implementation covers:

- extracting telecommand segments from CCSDS telecommand transfer frames

- encoding telecommand segments back into telecommand transfer frames

- tracking segmented telecommand reassembly state per spacecraft identifier and virtual channel identifier

- keeping pending segmented data separately per MAP identifier inside each virtual channel

- detecting telecommand frame-sequence discontinuities during reassembly

- applying configurable discontinuity recovery policies during reassembly

- returning explicit recovery event streams from the richer frame reassembly predicates

- returning provenance-aware reassembled service-unit terms that preserve the ordered per-frame mission-specific segment-header suffixes contributing to a complete telecommand service unit

- grouping complete telecommand service units into MAP-aware dispatch buckets so callers can route them by MAP identifier without rebuilding that bookkeeping

## API documentation

Open the ../../apis/library_index.html#ccsds_tc_services link in a web browser.

## Loading

To load all entities in this library, load the `loader.lgt` file:

    | ?- logtalk_load(ccsds_tc_services(loader)).

## Testing

To test this library predicates, load the `tester.lgt` file:

    | ?- logtalk_load(ccsds_tc_services(tester)).

## Representation

Telecommand segments extracted by this library are represented using the compound term:

    tc_segment(SequenceFlags, MapId, HeaderSuffix, Data)

Where:

- `SequenceFlags` is one of `continuation`, `first`, `last`, or `unsegmented`

- `MapId` is the multiplexer access point identifier in the range 0-63

- `HeaderSuffix` is the list of mission-specific segment-header octets that follow the standard first octet

- `Data` is the segment data as a list of bytes

The compact term `tc_segment(SequenceFlags, MapId, Data)` is also accepted by predicates such as `valid_segment/1` and `insert_tc_segment/3` as shorthand for an empty header suffix.

Reassembled service units that are reconstructed from multiple transfer frames use the empty list as `HeaderSuffix`, as there is no single mission-specific suffix value that unambiguously represents the merged result.

When callers need that provenance instead of the compact compatibility view, the companion reassembly predicates return terms of the form:

    tc_reassembled_segment(MapId, HeaderSuffixes, Data)

Where `HeaderSuffixes` is the ordered list of the per-frame mission-specific segment-header suffixes that contributed to the complete service unit.

MAP-aware dispatch helpers group complete telecommand service units into terms of the form:

    map_dispatch(MapId, ServiceUnits)

Where `ServiceUnits` is a list of complete telecommand service-unit terms for a single `MapId`, preserving the original service-unit order inside each bucket and the first-seen order of the MAP buckets.

Telecommand reassembly state is represented using the compound term:

    tc_reassembly_state(Channels)

Where `Channels` is a list of terms of the form:

    tc_reassembly_channel(SpacecraftId, VirtualChannelId, ExpectedSequenceNumber, PendingSegments)

The `PendingSegments` list stores the currently buffered segmented data by MAP identifier using terms of the form:

    tc_pending_segment(MapId, PendingData)

Pending buffered fragments can be queried using `pending_fragments/2`, which returns flattened terms of the form:

    pending_fragment(SpacecraftId, VirtualChannelId, MapId, PendingData)

Discontinuity recovery policies are represented by the atoms:

    throw
    drop
    resynchronize

The policies have the following meaning when a frame sequence discontinuity is found for a telecommand virtual channel:

- `throw`: raise a `domain_error/2` exception

- `drop`: discard all pending MAP fragments for that virtual channel and skip the discontinuous frame payload

- `resynchronize`: discard all pending MAP fragments for that virtual channel and still process the current frame payload

Recovery event streams are returned by `reassemble_tc_frame/6` and `reassemble_tc_frames/6` as lists of terms of the form:

    dropped_fragment(SpacecraftId, VirtualChannelId, MapId, Reason, PendingData)
    skipped_frame(SpacecraftId, VirtualChannelId, Discontinuity)
    resynchronized(SpacecraftId, VirtualChannelId, Discontinuity)

Where `Discontinuity` is represented by:

    discontinuity(ExpectedSequenceNumber, SequenceNumber)

Provenance-aware reassembly is provided by the companion predicates `reassemble_tc_frame_with_provenance/5-6` and `reassemble_tc_frames_with_provenance/5-6`.

MAP-aware dispatch is provided by `dispatch_service_units_by_map/2-3`, which accept both compact telecommand segment terms and provenance-aware reassembled telecommand service-unit terms.

## Scope

This first milestone interprets the standard telecommand MAP and sequence semantics from the first segment-header octet, where the high two bits encode the sequence flags and the low six bits encode the MAP identifier. Transfer frames with longer mission-specific segment headers are also supported: additional segment-header octets are exposed in extracted segment terms as the `HeaderSuffix` component and are preserved when updating frames.

## Examples

To extract a telecommand segment from a telecommand transfer frame term:

    | ?- ccsds_tc_services::extract_tc_segment(tc_transfer_frame(0, 1, 0, 42, 3, 7, segment_header([0xC5]), [1,2,3], none), Segment).

To update a telecommand transfer frame from a telecommand segment term:

    | ?- ccsds_tc_services::insert_tc_segment(tc_segment(first, 42, [1,2,3]), tc_transfer_frame(0, 1, 0, 42, 3, 7, none, [], none), UpdatedFrame).

To inspect the mission-specific segment-header suffix from an extracted telecommand segment term:

    | ?- ccsds_tc_services::segment_header_suffix(tc_segment(first, 5, [0xAA,0xBB], [1,2,3]), HeaderSuffix).

Mission-specific segment-header suffix octets are preserved when present in the input frame term:

    | ?- ccsds_tc_services::insert_tc_segment(tc_segment(first, 42, [1,2,3]), tc_transfer_frame(0, 1, 0, 42, 3, 7, segment_header([0x00,0xAA,0xBB]), [], none), UpdatedFrame).

To reassemble a segmented telecommand service unit across a sequence of frames:

    | ?- ccsds_tc_services::reassemble_tc_frames([tc_transfer_frame(0, 1, 0, 42, 3, 7, segment_header([0x45]), [1,2], none), tc_transfer_frame(0, 1, 0, 42, 3, 8, segment_header([0x85]), [3,4], none)], tc_reassembly_state([]), Segments, UpdatedState).

To reassemble a segmented telecommand service unit across a sequence of frames and preserve the ordered per-frame mission-specific header-suffix provenance:

    | ?- ccsds_tc_services::reassemble_tc_frames_with_provenance([tc_transfer_frame(0, 1, 0, 42, 3, 7, segment_header([0x45,0xAA]), [1,2], none), tc_transfer_frame(0, 1, 0, 42, 3, 8, segment_header([0x85,0xBB]), [3,4], none)], tc_reassembly_state([]), ReassembledSegments, UpdatedState, Events).

To group complete telecommand service units into MAP-aware dispatch buckets:

    | ?- ccsds_tc_services::dispatch_service_units_by_map([tc_segment(unsegmented, 7, [], [0x01]), tc_reassembled_segment(6, [[0xBB]], [0x03]), tc_reassembled_segment(7, [[0xAA]], [0x02])], Dispatches).

To resynchronize on a discontinuous telecommand frame while receiving explicit recovery events:

    | ?- ccsds_tc_services::reassemble_tc_frame(tc_transfer_frame(0, 1, 0, 42, 3, 9, segment_header([0xC6]), [9], none), resynchronize, tc_reassembly_state([tc_reassembly_channel(42, 3, 8, [tc_pending_segment(5, [1])])]), Segments, UpdatedState, Events).