Added failed state

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

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();