.. _module-pw_transfer: =========== pw_transfer =========== .. attention:: ``pw_transfer`` is under construction and so is its documentation. ----- Usage ----- C++ === The transfer service is defined and registered with an RPC server like any other RPC service. To know how to read data from or write data to device, a ``TransferHandler`` interface is defined (``pw_transfer/public/pw_transfer/handler.h``). Transfer handlers wrap a stream reader and/or writer with initialization and completion code. Custom transfer handler implementations should derive from ``ReadOnlyHandler``, ``WriteOnlyHandler``, or ``ReadWriteHandler`` as appropriate and override Prepare and Finalize methods if necessary. A transfer handler should be implemented and instantiated for each unique data transfer to or from a device. These handlers are then registered with the transfer service using their transfer IDs. **Example** .. code-block:: cpp #include "pw_transfer/transfer.h" namespace { // Simple transfer handler which reads data from an in-memory buffer. class SimpleBufferReadHandler : public pw::transfer::ReadOnlyHandler { public: SimpleReadTransfer(uint32_t transfer_id, pw::ConstByteSpan data) : ReadOnlyHandler(transfer_id), reader_(data) { set_reader(reader_); } private: pw::stream::MemoryReader reader_; }; // The maximum amount of data that can be sent in a single chunk, excluding // transport layer overhead. constexpr size_t kMaxChunkSizeBytes = 256; // In a write transfer, the maximum number of bytes to receive at one time, // (potentially across multiple chunks), unless specified otherwise by the // transfer handler's stream::Writer. constexpr size_t kDefaultMaxBytesToReceive = 1024; // Instantiate a static transfer service. pw::transfer::TransferService transfer_service( kMaxChunkSizeBytes, kDefaultMaxBytesToReceive); // Instantiate a handler for the the data to be transferred. constexpr uint32_t kBufferTransferId = 1; char buffer_to_transfer[256] = { /* ... */ }; SimpleBufferReadHandler buffer_handler(kBufferTransferId, buffer_to_transfer); } // namespace void InitTransfer() { // Register the handler with the transfer service, then the transfer service // with an RPC server. transfer_service.RegisterHandler(buffer_handler); GetSystemRpcServer().RegisterService(transfer_service); } Python ====== .. automodule:: pw_transfer :members: ProgressStats, Manager, Error **Example** .. code-block:: python import pw_transfer # Initialize a Pigweed RPC client; see pw_rpc docs for more info. rpc_client = CustomRpcClient() rpcs = rpc_client.channel(1).rpcs transfer_service = rpcs.pw.transfer.Transfer transfer_manager = pw_transfer.Manager(transfer_service) try: # Read transfer_id 3 from the server. data = transfer_manager.read(3) except pw_transfer.Error as err: print('Failed to read:', err.status) try: # Send some data to the server. The transfer manager does not have to be # reinitialized. transfer_manager.write(2, b'hello, world') except pw_transfer.Error as err: print('Failed to write:', err.status) -------- Protocol -------- Protocol buffer definition ========================== .. literalinclude:: transfer.proto :language: protobuf :lines: 14- Server to client transfer (read) ================================ .. image:: read.svg Client to server transfer (write) ================================= .. image:: write.svg Errors ====== At any point, either the client or server may terminate the transfer with a status code. The transfer chunk with the status is the final chunk of the transfer. The following table describes the meaning of each status code when sent by the sender or the receiver (see `Transfer roles`_). .. cpp:namespace-push:: pw::stream +-------------------------+-------------------------+-------------------------+ | Status | Sent by sender | Sent by receiver | +=========================+=========================+=========================+ | ``OK`` | (not sent) | All data was received | | | | and handled | | | | successfully. | +-------------------------+-------------------------+-------------------------+ | ``ABORTED`` | The service aborted the transfer because the | | | client restarted it. This status is passed to the | | | transfer handler, but not sent to the client | | | because it restarted the transfer. | +-------------------------+---------------------------------------------------+ | ``CANCELLED`` | The client cancelled the transfer. | +-------------------------+-------------------------+-------------------------+ | ``DATA_LOSS`` | Failed to read the data | Failed to write the | | | to send. The | received data. The | | | :cpp:class:`Reader` | :cpp:class:`Writer` | | | returned an error. | returned an error. | +-------------------------+-------------------------+-------------------------+ | ``FAILED_PRECONDITION`` | Received chunk for transfer that is not active. | +-------------------------+-------------------------+-------------------------+ | ``INVALID_ARGUMENT`` | Received a malformed packet. | +-------------------------+-------------------------+-------------------------+ | ``INTERNAL`` | An assumption of the protocol was violated. | | | Encountering ``INTERNAL`` indicates that there is | | | a bug in the service or client implementation. | +-------------------------+-------------------------+-------------------------+ | ``PERMISSION_DENIED`` | The transfer does not support the requested | | | operation (either reading or writing). | +-------------------------+-------------------------+-------------------------+ | ``RESOURCE_EXHAUSTED`` | (not sent) | Storage is full. | +-------------------------+-------------------------+-------------------------+ | ``UNAVAILABLE`` | The service is busy with other transfers and | | | cannot begin a new transfer at this time. | +-------------------------+-------------------------+-------------------------+ | ``UNIMPLEMENTED`` | Out-of-order chunk was | (not sent) | | | requested, but seeking | | | | is not supported. | | +-------------------------+-------------------------+-------------------------+ .. cpp:namespace-pop:: Transfer roles ============== Every transfer has two participants: the sender and the receiver. The sender transmits data to the receiver. The receiver controls how the data is transferred and sends the final status when the transfer is complete. In read transfers, the client is the receiver and the service is the sender. In write transfers, the client is the sender and the service is the receiver. Sender flow ----------- .. mermaid:: graph TD start([Client initiates
transfer]) -->data_request data_request[Receive transfer
parameters]-->send_chunk send_chunk[Send chunk]-->sent_all sent_all{Sent final
chunk?} -->|yes|wait sent_all-->|no|sent_requested sent_requested{Sent all
pending?}-->|yes|data_request sent_requested-->|no|send_chunk wait[Wait for receiver]-->is_done is_done{Received
final chunk?}-->|yes|done is_done-->|no|data_request done([Transfer complete]) Receiver flow ------------- .. mermaid:: graph TD start([Client initiates
transfer]) -->request_bytes request_bytes[Set transfer
parameters]-->wait wait[Wait for chunk]-->received_chunk received_chunk{Received
chunk by
deadline?}-->|no|request_bytes received_chunk-->|yes|check_chunk check_chunk{Correct
offset?} -->|yes|process_chunk check_chunk --> |no|request_bytes process_chunk[Process chunk]-->final_chunk final_chunk{Final
chunk?}-->|yes|signal_completion final_chunk{Final
chunk?}-->|no|received_requested received_requested{Received all
pending?}-->|yes|request_bytes received_requested-->|no|wait signal_completion[Signal completion]-->done done([Transfer complete])