use super::{
builder::{self, NoTimer},
control_interface::ControlInterface,
data_interface::{DataInterface, NoInterface},
encoding::json::OtaJob,
pal::OtaPal,
state::{Error, Events, JobEventData, SmContext, StateMachine, States},
};
use crate::jobs::StatusDetails;
pub struct OtaAgent<'a, C, DP, DS, T, ST, PAL, const TIMER_HZ: u32>
where
C: ControlInterface,
DP: DataInterface,
DS: DataInterface,
T: fugit_timer::Timer<TIMER_HZ>,
ST: fugit_timer::Timer<TIMER_HZ>,
PAL: OtaPal,
{
pub(crate) state: StateMachine<SmContext<'a, C, DP, DS, T, ST, PAL, 3, TIMER_HZ>>,
}
impl<'a, C, DP, DS, T, ST, PAL, const TIMER_HZ: u32> Drop
for OtaAgent<'a, C, DP, DS, T, ST, PAL, TIMER_HZ>
where
C: ControlInterface,
DP: DataInterface,
DS: DataInterface,
T: fugit_timer::Timer<TIMER_HZ>,
ST: fugit_timer::Timer<TIMER_HZ>,
PAL: OtaPal,
{
fn drop(&mut self) {
let sm_context = self.state.context_mut();
sm_context.ota_close().ok();
sm_context.control.cleanup().ok();
}
}
impl<'a, C, DP, T, PAL, const TIMER_HZ: u32>
OtaAgent<'a, C, DP, NoInterface, T, NoTimer, PAL, TIMER_HZ>
where
C: ControlInterface,
DP: DataInterface,
T: fugit_timer::Timer<TIMER_HZ>,
PAL: OtaPal,
{
pub fn builder(
control_interface: &'a C,
data_primary: DP,
request_timer: T,
pal: PAL,
) -> builder::OtaAgentBuilder<'a, C, DP, NoInterface, T, NoTimer, PAL, TIMER_HZ> {
builder::OtaAgentBuilder::new(control_interface, data_primary, request_timer, pal)
}
}
impl<'a, C, DP, DS, T, ST, PAL, const TIMER_HZ: u32> OtaAgent<'a, C, DP, DS, T, ST, PAL, TIMER_HZ>
where
C: ControlInterface,
DP: DataInterface,
DS: DataInterface,
T: fugit_timer::Timer<TIMER_HZ>,
ST: fugit_timer::Timer<TIMER_HZ>,
PAL: OtaPal,
{
pub fn init(&mut self) {
if matches!(self.state(), &States::Ready) {
self.state.process_event(Events::Start).ok();
} else {
self.state.process_event(Events::Resume).ok();
}
}
pub fn job_update(
&mut self,
job_name: &str,
ota_document: &OtaJob,
status_details: Option<&StatusDetails>,
) -> Result<&States, Error> {
self.state
.process_event(Events::ReceivedJobDocument(JobEventData {
job_name,
ota_document,
status_details,
}))
}
pub fn timer_callback(&mut self) -> Result<(), Error> {
let ctx = self.state.context_mut();
if ctx.request_timer.wait().is_ok() {
return self.state.process_event(Events::RequestTimer).map(drop);
}
if let Some(ref mut self_test_timer) = ctx.self_test_timer {
if self_test_timer.wait().is_ok() {
error!(
"Self test failed to complete within {} ms",
ctx.config.self_test_timeout_ms
);
ctx.pal.reset_device().ok();
}
}
Ok(())
}
pub fn process_event(&mut self) -> Result<&States, Error> {
if let Some(event) = self.state.context_mut().events.dequeue() {
self.state.process_event(event)
} else {
Ok(self.state())
}
}
pub fn handle_message(&mut self, payload: &mut [u8]) -> Result<&States, Error> {
self.state.process_event(Events::ReceivedFileBlock(payload))
}
pub fn check_for_update(&mut self) -> Result<&States, Error> {
if matches!(
self.state(),
States::WaitingForJob | States::RequestingJob | States::WaitingForFileBlock
) {
self.state.process_event(Events::RequestJobDocument)
} else {
Ok(self.state())
}
}
pub fn abort(&mut self) -> Result<&States, Error> {
self.state.process_event(Events::UserAbort)
}
pub fn suspend(&mut self) -> Result<&States, Error> {
self.state.context_mut().request_timer.cancel().ok();
self.state.process_event(Events::Suspend)
}
pub fn resume(&mut self) -> Result<&States, Error> {
self.state.process_event(Events::Resume)
}
pub fn state(&self) -> &States {
self.state.state()
}
}