# Svc::FileUplink Component

## 1 Introduction

`FileUplink` is an active F Prime component.
It manages uplink of files to the software deployment.

## 2 Requirements

Requirement | Description | Rationale | Verification Method
---- | ---- | ---- | ----
FPRIME-FU-001 | `FileUplink` shall receive file packets, assemble them into files, and store the files in the on-board non-volatile storage. | This requirement provides the capability to uplink files to the spacecraft. | Unit Test, System Test
FPRIME-FU-002 | `FileUplink` shall announce the completion of uplinked files.| This requirement provides the capability to inform other components of newly uplinked files | Unit Test, System Test

## 3 Design

### 3.1 Assumptions

The design of `FileUplink` assumes the following:

1. File uplink occurs by dividing files into packets
of type [`Fw::FilePacket`](../../../Fw/FilePacket/docs/sdd.md).

2. File access is sandboxed to a directory configured via `configure(directory)`.
All uplinked file paths are validated against the sandbox directory before writing.

   > [!WARNING]
   > The sandbox is **fail-open**: until `configure(directory)` is called, the sandbox defaults to
   > `/`, which permits writing to any absolute path accessible to the process. This default is
   > intentionally insecure for backwards compatibility. Security-conscious deployments **must**
   > call `configure(directory)` during topology setup to restrict file access. Note that the
   > stock `FileHandling` subtopology does **not** configure a sandbox.

3. In the nominal case of file uplink

    a. Files are received one at a time.
All packets of one file are received before receiving any
packets of the next file.

    b. Within a file, packets are received in order.

    c. When the file is successfully uplinked (including verification of a valid set of packets), the file name is announced via the `fileAnnounce` port.

### 3.2 Block Description Diagram (BDD)

![`FileUplink` BDD](img/FileUplink.drawio.png "FileUplink")

### 3.3 Ports

#### 3.3.1 Role Ports

Name | Type | Role
-----| ---- | ----
`timeCaller` | `Fw::Time` | TimeGet
`tlmOut` | [`Fw::Tlm`](../../../Fw/Tlm/docs/sdd.md) | Telemetry
`eventOut` | [`Fw::LogEvent`](../../../Fw/Log/docs/sdd.md) | LogEvent

#### 3.3.2 Component-Specific Ports

Name | Type | Kind | Purpose
---- | ---- | ---- | ----
<a name="bufferSendIn">`bufferSendIn`</a> | [`Fw::BufferSend`](../../../Fw/Buffer/docs/sdd.md) | async input | Receives buffers containing file packets.
<a name="bufferSendOut">`bufferSendOut`</a> | [`Fw::BufferSend`](../../../Fw/Buffer/docs/sdd.md) | output | Returns buffers for deallocation.
<a name="pingIn">`pingIn`</a> | [`Svc::Ping`](../../../Svc/Ping/docs/sdd.md) | async input | Receives ping calls from [`Svc::Health`](../../../Svc/Health/docs/sdd.md) for aliveness check
<a name="pingOut">`pingOut`</a> | [`Svc::Ping`](../../../Svc/Ping/docs/sdd.md) | output | Returns ping request to [`Svc::Health`](../../../Svc/Health/docs/sdd.md) to respond to liveness check
<a name="fileAnnounce">`fileAnnounce`</a> | [`Svc::FileAnnounce`](../../../Svc/Ports/FilePorts/FileAnnounce.fpp) | output | Announces the receipt of an uplinked file

### 3.4 State

`FileUplink` maintains the following state:

* <a name="receiveMode">*receiveMode*</a>:
One of START or DATA, recording the type of the next packet that
`FileUplink` expects to receive.
The initial value is START.

* <a name="lastSequenceIndex">*lastSequenceIndex*</a>:
An integer recording the sequence index of the last packet received.
The initial value is zero.

* <a name="writeFileDescriptor">*file*</a>:
An object representing the file, if any, that is currently open for
writing. The underlying OS file is an `Os::SandboxedFile`, which restricts
write locations to a configured sandbox directory.

* <a name="lastPacketWriteStatus">*lastPacketWriteStatus*</a>:
The status of the last file write, used when deciding whether a
duplicate packet may be skipped.

### 3.5 The bufferSendIn Port

`FileUplink` asynchronously receives buffers on
[`bufferSendIn`](#bufferSendIn).
Each buffer contains a file packet.
When `FileUplink` receives a buffer, it (a) determines the type
of the file packet in the buffer; (b) takes action as
specified in section below corresponding to the packet type; and (c) 
invokes [`bufferSendOut`](#bufferSendOut)
to return the buffer for deallocation.

#### 3.5.1 START Packets

Upon receipt of a START packet, `FileUplink` does the following:

1. If [*receiveMode*](#receiveMode) is not START,
then close
[*file*](#writeFileDescriptor)
and issue an *InvalidReceiveMode* warning.

2. Open [*file*](#writeFileDescriptor) for writing.

3. If step 2 succeeded, then set
[*lastSequenceIndex*](#lastSequenceIndex)
to zero and go to DATA mode; otherwise issue a
*FileOpenError* warning and go to START mode.

#### 3.5.2 DATA Packets

Upon receipt of a DATA packet *P*, `FileUplink` does the following,
where *I* is the 
[sequence index](../../../Fw/FilePacket/docs/sdd.md) 
of *P*:

1. If 
[*receiveMode*](#receiveMode)
is not DATA, then issue an *InvalidReceiveMode* warning 
and go to START mode.

2. Otherwise

    a. If [*lastPacketWriteStatus*](#lastPacketWriteStatus) is OK and
*I* is equal to *lastSequenceIndex*, then issue a *PacketDuplicate*
warning and skip the packet.

    b. If *I* is not equal to *lastSequenceIndex + 1*, then issue a 
*PacketOutOfOrder*
warning reporting *lastSequenceIndex* and *I*.

    c. If the packet offset and size are in bounds for the current file, then

    1. Using [*file*](#writeFileDescriptor), write the file data in the 
packet at offset specified in the packet.

    2. If there was an error writing the file, then issue a
*FileWriteError* warning. Record the write status in
[*lastPacketWriteStatus*](#lastPacketWriteStatus).

    d. Otherwise issue a *PacketOutOfBounds* warning.

#### 3.5.3 END Packets

Upon receipt of an END packet *P*, `FileUplink` does the following:

1. If [*receiveMode*](#receiveMode) is *DATA*,
then do the following, where *I* is the sequence index of *P*:

    a. If *I* is not equal to *lastSequenceIndex + 1*, 
then issue a *PacketOutOfOrder* warning reporting 
*lastSequenceIndex* and *I*.

    b. Use [*file*](#writeFileDescriptor) to do the following:

    1. Use the method described in &sect; 4.1.2 of the
[CCSDS File Delivery Protocol (CFDP) Recommended Standard](https://public.ccsds.org/Pubs/727x0b4s.pdf)
to compute the checksum value for the file.

    2. Compare the value computed in the previous step against the
checksum value in the packet.
If the two values are different, then issue a *BadChecksum* warning,
increment the *FilesReceivedFailed* telemetry channel, and do **not**
issue the *FileReceived* event or the `fileAnnounce` output.
Otherwise (the checksums match) issue the *FileReceived* event and,
if connected, the `fileAnnounce` output, and increment the
*FilesReceived* telemetry channel.

    c. Close the file.

2. Otherwise issue an *InvalidReceiveMode* warning.

3. Set *lastSequenceIndex* to zero and go to START mode.

#### 3.5.4 CANCEL Packets

Upon receipt of a cancel packet *P*, `FileUplink` does the following:

1. Set *lastSequenceIndex* to zero.

2. If *receiveMode* is not START, then close
[*file*](#writeFileDescriptor).

3. Issue an *UplinkCanceled* event.

4. Go to START mode.

## 4 Dictionary

See [FileUplink.fpp](../FileUplink.fpp) for a list of events and telemetry.

## 5 Checklists

Checklist |
-------- |
[Design](Checklist/design.xlsx) |
[Code](Checklist/code.xlsx) |
[Unit Test](Checklist/unit_test.xls) |

## 6 Unit Testing

Unit tests are located in `Svc/FileUplink/test/ut`. To run them:

```bash
fprime-util check
```

