From b1b19f9cba8d51457b4343b4a244753376c8bec0 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Fri, 9 Sep 2022 14:23:18 +0200 Subject: [PATCH] added cfdp module to common --- common.py | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 135 insertions(+), 4 deletions(-) diff --git a/common.py b/common.py index f96025c..e5a8723 100644 --- a/common.py +++ b/common.py @@ -1,11 +1,25 @@ import logging import sys +from pathlib import Path +from typing import Optional, Sequence -from tmtccmd.com_if import ComInterface +from spacepackets import SpacePacket, SpacePacketHeader, PacketTypes +from spacepackets.cfdp import TransmissionModes, PduType, DirectiveType +from spacepackets.cfdp.pdu.helper import GenericPduPacket, PduHolder, PduFactory +from spacepackets.util import UnsignedByteField +from tmtccmd.cfdp import ( + RemoteEntityCfgTable, + HostFilestore, + RemoteEntityCfg, + LocalEntityCfg, + CfdpUserBase, +) +from tmtccmd.cfdp.handler import SourceHandler, DestHandler +from tmtccmd.cfdp.request import PutRequest, PutRequestCfg from tmtccmd.logging import get_current_time_string from tmtccmd.pus.pus_11_tc_sched import Subservices as Pus11Subservices from tmtccmd.tc.queue import DefaultPusQueueHelper -from tmtccmd.util import FileSeqCountProvider, PusFileSeqCountProvider +from tmtccmd.util import FileSeqCountProvider, PusFileSeqCountProvider, ProvidesSeqCount from tmtccmd.util.tmtc_printer import FsfwTmTcPrinter try: @@ -38,7 +52,6 @@ from tmtccmd.tc import ( ProcedureHelper, FeedWrapper, TcProcedureType, - QueueEntryHelper, TcQueueEntryType, SendCbParams, ) @@ -46,13 +59,131 @@ from tmtccmd.tc.pus_5_event import pack_generic_service_5_test_into from tmtccmd.tm import SpecificApidHandlerBase, CcsdsTmHandler from tmtccmd.logging.pus import RawTmtcTimedLogWrapper from tmtccmd.config import CoreServiceList, SetupWrapper, SetupParams, ArgParserWrapper -from common_tmtc.config import __version__ from common_tmtc.pus_tm.factory_hook import pus_factory_hook LOGGER = get_console_logger() +class CfdpCcsdsWrapper: + def __init__( + self, + cfg: LocalEntityCfg, + user: CfdpUserBase, + cfdp_seq_cnt_provider: ProvidesSeqCount, + remote_cfg: Sequence[RemoteEntityCfg], + ccsds_seq_cnt_provider: ProvidesSeqCount, + ccsds_apid: int, + ): + self.handler = CfdpHandler(cfg, user, cfdp_seq_cnt_provider, remote_cfg) + self.ccsds_seq_cnt_provider = ccsds_seq_cnt_provider + self.ccsds_apid = ccsds_apid + + def pull_next_dest_packet(self) -> Optional[SpacePacket]: + """Retrieves the next PDU to send and wraps it into a space packet""" + next_packet = self.handler.pull_next_dest_packet() + if next_packet is None: + return next_packet + sp_header = SpacePacketHeader( + packet_type=PacketTypes.TC, + apid=self.ccsds_apid, + seq_count=self.ccsds_seq_cnt_provider.get_and_increment(), + data_len=next_packet.packet_len - 1, + ) + return SpacePacket(sp_header, None, next_packet.pack()) + + def confirm_dest_packet_sent(self): + self.handler.confirm_dest_packet_sent() + + def pass_packet(self, packet: SpacePacket): + # Unwrap the user data and pass it to the handler + pdu_raw = packet.user_data + pdu_base = PduFactory.from_raw(pdu_raw) + self.handler.pass_packet(pdu_base) + + +class CfdpHandler: + def __init__( + self, + cfg: LocalEntityCfg, + user: CfdpUserBase, + seq_cnt_provider: ProvidesSeqCount, + remote_cfg: Sequence[RemoteEntityCfg], + ): + vfs = HostFilestore() + super().__init__(vfs) + self.dest_id = UnsignedByteField(EXAMPLE_APID, 2) + self.remote_cfg_table = RemoteEntityCfgTable() + self.remote_cfg_table.add_remote_entities(remote_cfg) + self.dest_handler = DestHandler(cfg, user, self.remote_cfg_table) + self.source_handler = SourceHandler(cfg, seq_cnt_provider, user) + + def put_request_file( + self, + source_path: Path, + dest_path: Path, + trans_mode: TransmissionModes, + closure_requested: bool, + ): + put_request_cfg = PutRequestCfg( + destination_id=self.dest_id, + source_file=source_path, + dest_file=dest_path.as_posix(), + trans_mode=trans_mode, + closure_requested=closure_requested, + ) + put_request = PutRequest(put_request_cfg) + self.source_handler.put_request( + put_request, self.remote_cfg_table.get_remote_entity(self.dest_id) + ) + + def pull_next_dest_packet(self) -> Optional[PduHolder]: + res = self.dest_handler.state_machine() + if res.states.packet_ready: + return self.dest_handler.pdu_holder + return None + + def confirm_dest_packet_sent(self): + self.dest_handler.confirm_packet_sent_advance_fsm() + + def pass_packet(self, packet: GenericPduPacket): + """This function routes the packets based on PDU type and directive type if applicable. + + The routing is based on section 4.5 of the CFDP standard whcih specifies the PDU forwarding + procedure. + """ + if packet.pdu_type == PduType.FILE_DATA: + self.dest_handler.pass_packet(packet) + else: + if packet.directive_type in [ + DirectiveType.METADATA_PDU, + DirectiveType.EOF_PDU, + DirectiveType.PROMPT_PDU, + ]: + # Section b) of 4.5.3: These PDUs should always be targeted towards the file + # receiver a.k.a. the destination handler + self.dest_handler.pass_packet(packet) + elif packet.directive_type in [ + DirectiveType.FINISHED_PDU, + DirectiveType.NAK_PDU, + DirectiveType.KEEP_ALIVE_PDU, + ]: + # Section c) of 4.5.3: These PDUs should always be targeted towards the file sender + # a.k.a. the source handler + self.source_handler.pass_packet(packet) + elif packet.directive_type == DirectiveType.ACK_PDU: + # Section a): Recipient depends on the type of PDU that is being acknowledged. + # We can simply extract the PDU type from the raw stream. If it is an EOF PDU, + # this packet is passed to the source handler. For a finished PDU, it is + # passed to the destination handler + pdu_holder = PduHolder(packet) + ack_pdu = pdu_holder.to_ack_pdu() + if ack_pdu.directive_code_of_acked_pdu == DirectiveType.EOF_PDU: + self.source_handler.pass_packet(packet) + elif ack_pdu.directive_code_of_acked_pdu == DirectiveType.FINISHED_PDU: + self.dest_handler.pass_packet(packet) + + class PusHandler(SpecificApidHandlerBase): def __init__( self,