From a55285518cc809c72255e2c4ed224d527a0ea73e Mon Sep 17 00:00:00 2001 From: Baptiste Cluzel Date: Mon, 19 Feb 2024 16:48:41 +0100 Subject: [PATCH 1/2] image: upload: resend packet if lost --- src/cli.rs | 14 +++++++++++--- src/default.rs | 6 +++--- src/image.rs | 31 ++++++++++++++++++++++++++----- src/transfer.rs | 8 ++++---- 4 files changed, 44 insertions(+), 15 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 6dd1e49..33a9b41 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -18,9 +18,17 @@ pub struct Cli { #[arg(short, long)] pub verbose: bool, - /// maximum timeout in seconds - #[arg(short, long, default_value_t = 60)] - pub timeout: u32, + /// initial timeout in seconds + #[arg(short = 'i', long = "initial_timeout", default_value_t = 60)] + pub initial_timeout_s: u32, + + /// subsequent timeout in msec + #[arg(short = 't', long = "subsequent_timeout", default_value_t = 200)] + pub subsequent_timeout_ms: u32, + + // number of retry per packet + #[arg(long, default_value_t = 4)] + pub nb_retry: u32, /// maximum length per line #[arg(short, long, default_value_t = 128)] diff --git a/src/default.rs b/src/default.rs index 568338b..e5f276b 100644 --- a/src/default.rs +++ b/src/default.rs @@ -15,10 +15,10 @@ use crate::transfer::transceive; pub fn reset(cli: &Cli) -> Result<(), Error> { info!("send reset request"); - + // open serial port let mut port = open_port(cli)?; - + // send request let body = Vec::new(); let (data, request_header) = encode_request( @@ -29,7 +29,7 @@ pub fn reset(cli: &Cli) -> Result<(), Error> { &body, next_seq_id(), )?; - let (response_header, response_body) = transceive(&mut *port, data)?; + let (response_header, response_body) = transceive(&mut *port, &data)?; // verify sequence id if response_header.seq != request_header.seq { diff --git a/src/image.rs b/src/image.rs index 7f117d2..7ac7077 100644 --- a/src/image.rs +++ b/src/image.rs @@ -3,8 +3,7 @@ use anyhow::{bail, Error, Result}; use humantime::format_duration; use indicatif::{ProgressBar, ProgressStyle}; -use log::debug; -use log::info; +use log::{debug, info, warn}; use serde_cbor; use serde_json; use sha2::{Digest, Sha256}; @@ -37,7 +36,7 @@ pub fn list(cli: &Cli) -> Result<(), Error> { &body, next_seq_id(), )?; - let (response_header, response_body) = transceive(&mut *port, data)?; + let (response_header, response_body) = transceive(&mut *port, &data)?; // verify sequence id if response_header.seq != request_header.seq { @@ -89,7 +88,10 @@ pub fn upload(cli: &Cli, filename: &PathBuf) -> Result<(), Error> { // transfer in blocks let mut off: usize = 0; let start_time = Instant::now(); + let mut sent_blocks: u32 = 0; + let mut confirmed_blocks: u32 = 0; loop { + let mut nb_retry = cli.nb_retry; let off_start = off; let mut try_length = cli.mtu; debug!("try_length: {}", try_length); @@ -151,7 +153,19 @@ pub fn upload(cli: &Cli, filename: &PathBuf) -> Result<(), Error> { } // send request - let (response_header, response_body) = transceive(&mut *port, chunk)?; + sent_blocks += 1; + let (response_header, response_body) = match transceive(&mut *port, &chunk) { + Ok(ret) => ret, + Err(e) if e.to_string() == "Operation timed out" => { + if nb_retry == 0 { + return Err(e); + } + nb_retry -= 1; + debug!("missed answer, nb_retry: {}", nb_retry); + continue; + } + Err(e) => return Err(e), + }; // verify sequence id if response_header.seq != request_header.seq { @@ -187,7 +201,7 @@ pub fn upload(cli: &Cli, filename: &PathBuf) -> Result<(), Error> { } } } - + confirmed_blocks += 1; break; } @@ -200,6 +214,10 @@ pub fn upload(cli: &Cli, filename: &PathBuf) -> Result<(), Error> { if off == data.len() { break; } + + // The first packet was sent and the device has cleared his internal flash + // We can now lower the timeout in case of failed transmission + port.set_timeout(Duration::from_millis(cli.subsequent_timeout_ms as u64))?; } pb.finish_with_message("upload complete"); @@ -207,6 +225,9 @@ pub fn upload(cli: &Cli, filename: &PathBuf) -> Result<(), Error> { let elapsed_duration = Duration::from_secs(elapsed as u64); let formatted_duration = format_duration(elapsed_duration); info!("upload took {}", formatted_duration); + if confirmed_blocks != sent_blocks { + warn!("upload packet loss {}%", 100 - confirmed_blocks * 100 / sent_blocks); + } Ok(()) } diff --git a/src/transfer.rs b/src/transfer.rs index c9b37a4..f96cb1c 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -38,7 +38,7 @@ pub fn open_port(cli: &Cli) -> Result, Error> { Ok(Box::new(TestSerialPort::new())) } else { serialport::new(&cli.device, cli.baudrate) - .timeout(Duration::from_secs(cli.timeout as u64)) + .timeout(Duration::from_secs(cli.initial_timeout_s as u64)) .open() .with_context(|| format!("failed to open serial port {}", &cli.device)) } @@ -110,7 +110,7 @@ pub fn encode_request( pub fn transceive( port: &mut dyn SerialPort, - data: Vec, + data: &Vec, ) -> Result<(NmpHdr, serde_cbor::Value), Error> { // empty input buffer let to_read = port.bytes_to_read()?; @@ -119,14 +119,14 @@ pub fn transceive( } // write request - port.write_all(&data)?; + port.write_all(data)?; // read result let mut bytes_read = 0; let mut expected_len = 0; let mut result: Vec = Vec::new(); loop { - // first wait for the chunk start marker + debug!("waiting for the chunk start marker"); if bytes_read == 0 { expect_byte(&mut *port, 6)?; expect_byte(&mut *port, 9)?; From 59483dea23a58cd00f28a373aac19fef914b0480 Mon Sep 17 00:00:00 2001 From: Baptiste Cluzel Date: Tue, 20 Feb 2024 09:55:46 +0100 Subject: [PATCH 2/2] Resolving comments --- src/cli.rs | 4 ++-- src/image.rs | 2 +- src/transfer.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 33a9b41..40f849e 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -19,11 +19,11 @@ pub struct Cli { pub verbose: bool, /// initial timeout in seconds - #[arg(short = 'i', long = "initial_timeout", default_value_t = 60)] + #[arg(short = 't', long = "initial_timeout", default_value_t = 60)] pub initial_timeout_s: u32, /// subsequent timeout in msec - #[arg(short = 't', long = "subsequent_timeout", default_value_t = 200)] + #[arg(short = 'u', long = "subsequent_timeout", default_value_t = 200)] pub subsequent_timeout_ms: u32, // number of retry per packet diff --git a/src/image.rs b/src/image.rs index 7ac7077..23444d1 100644 --- a/src/image.rs +++ b/src/image.rs @@ -215,7 +215,7 @@ pub fn upload(cli: &Cli, filename: &PathBuf) -> Result<(), Error> { break; } - // The first packet was sent and the device has cleared his internal flash + // The first packet was sent and the device has cleared its internal flash // We can now lower the timeout in case of failed transmission port.set_timeout(Duration::from_millis(cli.subsequent_timeout_ms as u64))?; } diff --git a/src/transfer.rs b/src/transfer.rs index f96cb1c..a1899a0 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -126,7 +126,7 @@ pub fn transceive( let mut expected_len = 0; let mut result: Vec = Vec::new(); loop { - debug!("waiting for the chunk start marker"); + // first wait for the chunk start marker if bytes_read == 0 { expect_byte(&mut *port, 6)?; expect_byte(&mut *port, 9)?;