Added failed state

This commit is contained in:
Dreaded_X 2023-09-16 01:20:52 +02:00
parent 7763269b06
commit 01860e2e7a
Signed by: Dreaded_X
GPG Key ID: FA5F485356B0D2D4
4 changed files with 178 additions and 86 deletions

25
updater/Cargo.lock generated
View File

@ -749,6 +749,30 @@ version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
[[package]]
name = "impl-tools"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d82c305b1081f1a99fda262883c788e50ab57d36c00830bdd7e0a82894ad965c"
dependencies = [
"autocfg",
"impl-tools-lib",
"proc-macro-error",
"syn 2.0.33",
]
[[package]]
name = "impl-tools-lib"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85d3946d886eaab0702fa0c6585adcced581513223fa9df7ccfabbd9fa331a88"
dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn 2.0.33",
]
[[package]]
name = "inout"
version = "0.1.3"
@ -1197,6 +1221,7 @@ dependencies = [
"embedded-storage",
"embedded-tls",
"heapless 0.7.16",
"impl-tools",
"nourl",
"rand_core",
"reqwless",

View File

@ -45,6 +45,7 @@ embedded-tls = { version = "0.15.0", default-features = false, features = [
] }
reqwless = { version = "0.5.0", features = ["defmt"] }
static_cell = { version = "1.2.0", features = ["nightly"] }
impl-tools = "0.10.0"
[patch.crates-io]
embassy-net = { git = "https://github.com/embassy-rs/embassy" }

107
updater/src/error.rs Normal file
View File

@ -0,0 +1,107 @@
use core::fmt::{Display, Write};
use heapless::String;
use defmt::{Format, Formatter};
use embassy_boot::FirmwareUpdaterError;
use embassy_net::{dns, tcp::ConnectError};
use embedded_io_async::ReadExactError;
use embedded_storage::nor_flash::NorFlashError;
use embedded_tls::TlsError;
use rust_mqtt::packet::v5::reason_codes::ReasonCode;
impl_tools::impl_scope! {
#[derive(Debug)]
pub enum Error<FE: NorFlashError + defmt::Format> {
Mqtt(ReasonCode),
Dns(dns::Error),
Connect(ConnectError),
Tls(TlsError),
Reqwless(reqwless::Error),
FirmwareUpdater(FirmwareUpdaterError),
FlashError(FE),
UnexpectedEof,
}
impl Self {
pub fn string(&self) -> String<256> {
let mut error = String::new();
core::write!(error, "{}", self).expect("Formatting the error should not fail");
error
}
}
impl From<ReasonCode> for Self {
fn from(error: ReasonCode) -> Self {
Self::Mqtt(error)
}
}
impl From<dns::Error> for Self {
fn from(error: dns::Error) -> Self {
Self::Dns(error)
}
}
impl From<ConnectError> for Self {
fn from(error: ConnectError) -> Self {
Self::Connect(error)
}
}
impl From<TlsError> for Self {
fn from(error: TlsError) -> Self {
Self::Tls(error)
}
}
impl From<reqwless::Error> for Self {
fn from(error: reqwless::Error) -> Self {
Self::Reqwless(error)
}
}
impl From<FirmwareUpdaterError> for Self {
fn from(error: FirmwareUpdaterError) -> Self {
Self::FirmwareUpdater(error)
}
}
impl From<ReadExactError<reqwless::Error>> for Self {
fn from(error: ReadExactError<reqwless::Error>) -> Self {
match error {
ReadExactError::UnexpectedEof => Self::UnexpectedEof,
ReadExactError::Other(error) => Self::Reqwless(error),
}
}
}
impl Format for Self {
fn format(&self, f: Formatter) {
match self {
Error::Mqtt(error) => defmt::write!(f, "Mqtt: {}", error),
Error::Dns(error) => defmt::write!(f, "Dns: {}", error),
Error::Connect(error) => defmt::write!(f, "Connect: {}", error),
Error::Tls(error) => defmt::write!(f, "Tls: {}", error),
Error::Reqwless(error) => defmt::write!(f, "Reqwless: {}", error),
Error::FirmwareUpdater(error) => defmt::write!(f, "FirmwareUpdater: {}", error),
Error::FlashError(error) => defmt::write!(f, "FlashError: {:?}", error),
Error::UnexpectedEof => defmt::write!(f, "UnexpectedEof"),
}
}
}
impl Display for Self {
fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
match self {
Error::Mqtt(error) => core::write!(f, "Mqtt: {}", error),
Error::Dns(error) => core::write!(f, "Dns: {:?}", error),
Error::Connect(error) => core::write!(f, "Connect: {:?}", error),
Error::Tls(error) => core::write!(f, "Tls: {:?}", error),
Error::Reqwless(error) => core::write!(f, "Reqwless: {:?}", error),
Error::FirmwareUpdater(error) => core::write!(f, "FirmwareUpdater: {:?}", error),
Error::FlashError(error) => core::write!(f, "FlashError: {:?}", error),
Error::UnexpectedEof => core::write!(f, "UnexpectedEof"),
}
}
}
}

View File

@ -4,17 +4,12 @@
#![feature(never_type)]
use defmt::*;
use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterError};
use embassy_net::{
dns::{self, DnsQueryType},
driver::Driver,
tcp::{ConnectError, TcpSocket},
Stack,
};
use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater};
use embassy_net::{dns::DnsQueryType, driver::Driver, tcp::TcpSocket, Stack};
use embassy_time::{Duration, Timer};
use embedded_io_async::{Read, ReadExactError, Write};
use embedded_storage::nor_flash::{NorFlash, NorFlashError};
use embedded_tls::{Aes128GcmSha256, NoVerify, TlsConfig, TlsConnection, TlsContext, TlsError};
use embedded_io_async::{Read, Write};
use embedded_storage::nor_flash::NorFlash;
use embedded_tls::{Aes128GcmSha256, NoVerify, TlsConfig, TlsConnection, TlsContext};
use heapless::Vec;
use nourl::Url;
use rand_core::{CryptoRng, RngCore};
@ -24,11 +19,15 @@ use reqwless::{
};
use rust_mqtt::{
client::{client::MqttClient, client_config::ClientConfig},
packet::v5::{publish_packet::QualityOfService, reason_codes::ReasonCode},
packet::v5::publish_packet::QualityOfService,
};
use serde::Serialize;
use static_cell::make_static;
mod error;
pub use crate::error::Error;
#[derive(Serialize)]
#[serde(rename_all = "snake_case", tag = "status")]
enum Status<'a> {
@ -38,94 +37,24 @@ enum Status<'a> {
Erasing,
Writing { progress: u32 },
Verifying,
UpdateFailed { error: &'a str },
UpdateComplete,
}
impl Status<'_> {
fn json(&self) -> Vec<u8, 128> {
fn json(&self) -> Vec<u8, 512> {
serde_json_core::to_vec(self)
.expect("This buffers size should be large enough to contain the serialized status")
}
}
#[derive(Debug)]
pub enum Error<FE: NorFlashError> {
Mqtt(ReasonCode),
Dns(dns::Error),
Connect(ConnectError),
Tls(TlsError),
Reqwless(reqwless::Error),
FirmwareUpdater(FirmwareUpdaterError),
FlashError(FE),
UnexpectedEof,
}
impl<FE: NorFlashError> From<ReasonCode> for Error<FE> {
fn from(error: ReasonCode) -> Self {
Self::Mqtt(error)
}
}
impl<FE: NorFlashError> From<dns::Error> for Error<FE> {
fn from(error: dns::Error) -> Self {
Self::Dns(error)
}
}
impl<FE: NorFlashError> From<ConnectError> for Error<FE> {
fn from(error: ConnectError) -> Self {
Self::Connect(error)
}
}
impl<FE: NorFlashError> From<TlsError> for Error<FE> {
fn from(error: TlsError) -> Self {
Self::Tls(error)
}
}
impl<FE: NorFlashError> From<reqwless::Error> for Error<FE> {
fn from(error: reqwless::Error) -> Self {
Self::Reqwless(error)
}
}
impl<FE: NorFlashError> From<FirmwareUpdaterError> for Error<FE> {
fn from(error: FirmwareUpdaterError) -> Self {
Self::FirmwareUpdater(error)
}
}
impl<FE: NorFlashError> From<ReadExactError<reqwless::Error>> for Error<FE> {
fn from(error: ReadExactError<reqwless::Error>) -> Self {
match error {
ReadExactError::UnexpectedEof => Self::UnexpectedEof,
ReadExactError::Other(error) => Self::Reqwless(error),
}
}
}
impl<FE: NorFlashError + defmt::Format> Format for Error<FE> {
fn format(&self, f: Formatter) {
match self {
Error::Mqtt(error) => defmt::write!(f, "Mqtt: {}", error),
Error::Dns(error) => defmt::write!(f, "Dns: {}", error),
Error::Connect(error) => defmt::write!(f, "Connect: {}", error),
Error::Tls(error) => defmt::write!(f, "Tls: {}", error),
Error::Reqwless(error) => defmt::write!(f, "Reqwless: {}", error),
Error::FirmwareUpdater(error) => defmt::write!(f, "FirmwareUpdater: {}", error),
Error::FlashError(error) => defmt::write!(f, "FlashError: {:?}", error),
Error::UnexpectedEof => defmt::write!(f, "UnexpectedEof"),
}
}
}
// TODO: Make this the owner of the blocking firmware updater
// TODO: When fixed, use the async firmware updater
pub struct Updater<'a, DFU, STATE>
where
DFU: NorFlash,
STATE: NorFlash,
DFU::Error: Format,
{
updater: BlockingFirmwareUpdater<'a, DFU, STATE>,
@ -135,7 +64,12 @@ where
public_key: &'static [u8],
}
impl<'a, DFU: NorFlash, STATE: NorFlash> Updater<'a, DFU, STATE> {
impl<'a, DFU, STATE> Updater<'a, DFU, STATE>
where
DFU: NorFlash,
STATE: NorFlash,
DFU::Error: Format,
{
pub fn new(
updater: BlockingFirmwareUpdater<'a, DFU, STATE>,
topic_status: &'static str,
@ -186,6 +120,29 @@ impl<'a, DFU: NorFlash, STATE: NorFlash> Updater<'a, DFU, STATE> {
rng: &mut (impl RngCore + CryptoRng),
client: &mut MqttClient<'_, impl Write + Read, MAX_PROPERTIES, impl RngCore>,
url: Url<'_>,
) -> Result<!, Error<DFU::Error>> {
let result = self._update(stack, rng, client, url).await;
if let Err(err) = &result {
let status = Status::UpdateFailed {
error: &err.string(),
}
.json();
client
.send_message(self.topic_status, &status, QualityOfService::QoS1, false)
.await?;
}
result
}
async fn _update<const MAX_PROPERTIES: usize>(
&mut self,
stack: &'static Stack<impl Driver>,
rng: &mut (impl RngCore + CryptoRng),
client: &mut MqttClient<'_, impl Write + Read, MAX_PROPERTIES, impl RngCore>,
url: Url<'_>,
) -> Result<!, Error<DFU::Error>> {
info!("Preparing for OTA...");
let status = Status::PreparingUpdate.json();
@ -250,7 +207,9 @@ impl<'a, DFU: NorFlash, STATE: NorFlash> Updater<'a, DFU, STATE> {
break;
}
debug!("Writing chunk: {}", read);
writer.write(size, &buffer.0[..read]).map_err(Error::FlashError)?;
writer
.write(size, &buffer.0[..read])
.map_err(Error::FlashError)?;
size += read as u32;
let status = Status::Writing { progress: size }.json();