pw_transfer: Start docs and proto - Create the pw_transfer module. - Start the pw_transfer docs. Change-Id: I2d3b4f3e719960167ee28ba4ca84eb809bf94bc1 Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/49022 Pigweed-Auto-Submit: Wyatt Hepler <hepler@google.com> Reviewed-by: Keir Mierle <keir@google.com> Reviewed-by: Alexei Frolov <frolv@google.com> Commit-Queue: Wyatt Hepler <hepler@google.com>
diff --git a/BUILD.gn b/BUILD.gn index 7b482ff..7631696 100644 --- a/BUILD.gn +++ b/BUILD.gn
@@ -293,6 +293,7 @@ "$dir_pw_tokenizer:tests", "$dir_pw_trace:tests", "$dir_pw_trace_tokenized:tests", + "$dir_pw_transfer:tests", "$dir_pw_unit_test:tests", "$dir_pw_varint:tests", ]
diff --git a/docs/BUILD.gn b/docs/BUILD.gn index 7b0b536..7f5d778 100644 --- a/docs/BUILD.gn +++ b/docs/BUILD.gn
@@ -141,6 +141,7 @@ "$dir_pw_toolchain:docs", "$dir_pw_trace:docs", "$dir_pw_trace_tokenized:docs", + "$dir_pw_transfer:docs", "$dir_pw_unit_test:docs", "$dir_pw_varint:docs", "$dir_pw_watch:docs",
diff --git a/modules.gni b/modules.gni index cc01d95..9b2625e 100644 --- a/modules.gni +++ b/modules.gni
@@ -40,6 +40,7 @@ dir_pw_cpu_exception = get_path_info("pw_cpu_exception", "abspath") dir_pw_cpu_exception_cortex_m = get_path_info("pw_cpu_exception_cortex_m", "abspath") + dir_pw_transfer = get_path_info("pw_transfer", "abspath") dir_pw_docgen = get_path_info("pw_docgen", "abspath") dir_pw_doctor = get_path_info("pw_doctor", "abspath") dir_pw_env_setup = get_path_info("pw_env_setup", "abspath")
diff --git a/pw_transfer/BUILD.gn b/pw_transfer/BUILD.gn new file mode 100644 index 0000000..5fede6f --- /dev/null +++ b/pw_transfer/BUILD.gn
@@ -0,0 +1,32 @@ +# Copyright 2021 The Pigweed Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + +import("//build_overrides/pigweed.gni") + +import("$dir_pw_docgen/docs.gni") +import("$dir_pw_protobuf_compiler/proto.gni") +import("$dir_pw_unit_test/test.gni") + +pw_proto_library("proto") { + sources = [ "transfer.proto" ] +} + +pw_test_group("tests") { + tests = [] +} + +pw_doc_group("docs") { + sources = [ "docs.rst" ] + inputs = [ "transfer.proto" ] +}
diff --git a/pw_transfer/CMakeLists.txt b/pw_transfer/CMakeLists.txt new file mode 100644 index 0000000..ac100e7 --- /dev/null +++ b/pw_transfer/CMakeLists.txt
@@ -0,0 +1,23 @@ +# Copyright 2020 The Pigweed Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + +include($ENV{PW_ROOT}/pw_build/pigweed.cmake) + +pw_auto_add_simple_module(pw_transfer + PUBLIC_DEPS + pw_assert + pw_containers + pw_status + pw_stream +)
diff --git a/pw_transfer/docs.rst b/pw_transfer/docs.rst new file mode 100644 index 0000000..2f31ed6 --- /dev/null +++ b/pw_transfer/docs.rst
@@ -0,0 +1,154 @@ +.. _module-pw_transfer: + +=========== +pw_transfer +=========== + +.. attention:: + + ``pw_transfer`` is under construction and so is its documentation. + +-------- +Protocol +-------- + +Protocol buffer definition +========================== +.. literalinclude:: transfer.proto + :language: protobuf + :lines: 14- + +Server to client transfer (read) +================================ +.. seqdiag:: + :scale: 110 + + seqdiag { + default_note_color = aliceblue; + + client -> server [ + label = "set transfer parameters", + leftnote = "transfer_id\noffset\npending_bytes\nmax_chunk_size\nchunk_delay" + ]; + + client <-- server [ + noactivate, + label = "requested bytes\n(zero or more chunks)", + rightnote = "offset\ndata\n(remaining_bytes)" + ]; + + client --> server [ + noactivate, + label = "update transfer parameters\n(as needed)", + leftnote = "offset\npending_bytes\n(max_chunk_size)\n(chunk_delay)" + ]; + + client <- server [ + noactivate, + label = "final chunk", + rightnote = "offset\ndata\nremaining_bytes=0" + ]; + + client -> server [ + noactivate, + label = "received final chunk", + leftnote = "pending_bytes=0" + ]; + + client <- server [ + label = "done" + ]; + } + +Client to server transfer (write) +================================= +.. seqdiag:: + :scale: 110 + + seqdiag { + default_note_color = aliceblue; + + client -> server [ + label = "start", + leftnote = "transfer_id" + ]; + + client <- server [ + noactivate, + label = "set transfer parameters", + rightnote = "offset\npending_bytes\nmax_chunk_size\nchunk_delay" + ]; + + client --> server [ + noactivate, + label = "requested bytes\n(zero or more chunks)", + leftnote = "offset\ndata\n(remaining_bytes)" + ]; + + client <-- server [ + noactivate, + label = "update transfer parameters\n(as needed)", + rightnote = "offset\npending_bytes\n(max_chunk_size)\n(chunk_delay)" + ]; + + client -> server [ + noactivate, + label = "final chunk", + leftnote = "offset\ndata\nremaining_bytes=0" + ]; + + client <- server [ + label = "done" + ]; + } + +Transmitter flow +================ +.. mermaid:: + + graph TD + start([Client initiates<br>transfer]) -->data_request + data_request[Receive transfer<br>parameters]-->send_chunk + + send_chunk[Send chunk]-->sent_all + + sent_all{Sent final<br>chunk?} -->|yes|wait + sent_all-->|no|sent_requested + + sent_requested{Sent all<br>pending?}-->|yes|data_request + sent_requested-->|no|send_chunk + + wait[Wait for receiver]-->is_done + + is_done{Received<br>final chunk?}-->|yes|done + is_done-->|no|data_request + + done([Transfer complete]) + +Receiver flow +============= +.. mermaid:: + + graph TD + start([Client initiates<br>transfer]) -->request_bytes + request_bytes[Set transfer<br>parameters]-->wait + + wait[Wait for chunk]-->received_chunk + + received_chunk{Received<br>chunk by<br>deadline?}-->|no|request_bytes + received_chunk-->|yes|check_chunk + + check_chunk{Correct<br>offset?} -->|yes|process_chunk + check_chunk --> |no|request_bytes + + process_chunk[Process chunk]-->final_chunk + + final_chunk{Final<br>chunk?}-->|yes|signal_completion + final_chunk{Final<br>chunk?}-->|no|received_requested + + received_requested{Received all<br>pending?}-->|yes|request_bytes + received_requested-->|no|wait + + signal_completion[Signal completion]-->done + + done([Transfer complete])
diff --git a/pw_transfer/transfer.proto b/pw_transfer/transfer.proto new file mode 100644 index 0000000..427bd17 --- /dev/null +++ b/pw_transfer/transfer.proto
@@ -0,0 +1,106 @@ +// Copyright 2021 The Pigweed Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +syntax = "proto3"; + +package pw.transfer; + +// The transfer RPC service is used to send data between the client and server. +service Transfer { + // Transfer data from the server to the client; a "download" from the client's + // perspective. + rpc Read(stream Chunk) returns (stream Chunk); + + // Transfer data from the client to the server; an "upload" from the client's + // perspective. + rpc Write(stream Chunk) returns (stream Chunk); +} + +// Represents a chunk of data sent by the transfer service. Includes fields for +// configuring the transfer parameters. +// +// Notation: (Read|Write) (→|←) +// X → Means client sending data to the server. +// X ← Means server sending data to the client. +message Chunk { + // Represents the source or destination of the data. May be ephemeral or + // stable depending on the implementation. Only ever sent by the client in the + // first request. + // + // Read → ID of transfer + // Read ← Omitted + // Write → ID of transfer + // Write ← Omitted + uint32 transfer_id = 1; + + // Used by the receiver to indicate how many bytes it can accept. The + // transmitter sends this much data, divided into chunks no larger than + // max_chunk_size_bytes. The receiver then starts another window by sending + // request_bytes again with a new offset. + // + // Read → The client requests this many bytes to be sent. + // Read ← N/A + // Write → N/A + // Write ← The server requests this many bytes to be sent. + optional uint32 pending_bytes = 2; + + // Maximum size of an individual chunk. The transmitter may send smaller + // chunks if required. + // + // Read → Set maximum size for subsequent chunks. + // Read ← N/A + // Write → N/A + // Write ← Set maximum size for subsequent chunks. + optional uint32 max_chunk_size_bytes = 3; + + // Minimum required delay between chunks. The transmitter may delay longer if + // desired. + // + // Read → Set minimum delay for subsequent chunks. + // Read ← N/A + // Write → N/A + // Write ← Set minimum delay for subsequent chunks. + optional uint32 min_delay_microseconds = 4; + + // On writes, the offset of the data. On reads, the offset at which to read. + // + // Read → Read data starting at this offset. + // Read ← Offset of the data. + // Write → Offset of the data. + // Write ← Write data starting at this offset. + uint64 offset = 5; + + // The data that was read or the data to write. + // + // Read → N/A + // Read ← Data read + // Write → Data to write + // Write ← N/A + bytes data = 6; + + // Estimated bytes remaining to read/write. Optional except for the last + // chunk, for which remaining_bytes must be set to 0. + // + // The sender can set remaining_bytes at the beginning of a read/write so that + // the receiver can track progress or cancel the transaction if the value is + // too large. + // + // Read → N/A + // Read ← Remaining bytes to read, excluding any data in this chunk. Set to + // 0 for the last chunk. + // Write → Reamining bytes to write, excluding any data in is chunk. Set to + // 0 for the last chunk. + // Write ← N/A + optional uint64 remaining_bytes = 7; +}