Battery Adapter
The battery service expects to be handed a type that implements the SmartBattery
trait, as well as the Controller
trait defined in the battery_service::controller
module. We can create a simple adapter type that holds a reference to our ControllerCore
mutex, and then forwards the trait method calls into the core controller code.
#![allow(unused)] fn main() { use crate::controller_core::ControllerCore; #[allow(unused_imports)] use ec_common::mutex::{RawMutex, Mutex}; use core::sync::atomic::{AtomicU64, Ordering}; #[allow(unused_imports)] use battery_service::controller::{Controller, ControllerEvent}; use battery_service::device::{DynamicBatteryMsgs, StaticBatteryMsgs}; use embassy_time::Duration; use mock_battery::mock_battery::MockBatteryError; #[allow(unused_imports)] use embedded_batteries_async::smart_battery::{ SmartBattery, ManufactureDate, SpecificationInfoFields, CapacityModeValue, CapacityModeSignedValue, BatteryModeFields, BatteryStatusFields, DeciKelvin, MilliVolts }; const DEFAULT_TIMEOUT_MS: u64 = 1000; #[allow(unused)] pub struct BatteryAdapter { core_mutex: &'static Mutex<RawMutex, ControllerCore>, timeout_ms: AtomicU64 // cached timeout to work around sync/async mismatch } impl BatteryAdapter { #[allow(unused)] pub fn new(core_mutex: &'static Mutex<RawMutex, ControllerCore>) -> Self { Self { core_mutex, timeout_ms: AtomicU64::new(DEFAULT_TIMEOUT_MS) } } #[inline] fn dur_to_ms(d: Duration) -> u64 { // Use the unit that’s most convenient for you; ms is usually fine. d.as_millis() as u64 } #[inline] fn ms_to_dur(ms: u64) -> Duration { Duration::from_millis(ms as u64) } // called on Controller methods to shadow timeout value we can forward in a synchronous trait method fn sync_timeout_cache(&self, core: &mut ControllerCore) { use core::sync::atomic::Ordering; let cached = self.timeout_ms.load(Ordering::Relaxed); let current = Self::dur_to_ms(core.get_timeout()); if current != cached { core.set_timeout(Self::ms_to_dur(cached)); } } } impl embedded_batteries_async::smart_battery::ErrorType for BatteryAdapter { type Error = MockBatteryError; } impl SmartBattery for BatteryAdapter { async fn temperature(&mut self) -> Result<DeciKelvin, Self::Error> { let mut c = self.core_mutex.lock().await; c.temperature().await } async fn voltage(&mut self) -> Result<MilliVolts, Self::Error> { let mut c = self.core_mutex.lock().await; c.voltage().await } async fn remaining_capacity_alarm(&mut self) -> Result<CapacityModeValue, Self::Error> { let mut c = self.core_mutex.lock().await; c.remaining_capacity_alarm().await } async fn set_remaining_capacity_alarm(&mut self, v: CapacityModeValue) -> Result<(), Self::Error> { let mut c = self.core_mutex.lock().await; c.set_remaining_capacity_alarm(v).await } async fn remaining_time_alarm(&mut self) -> Result<u16, Self::Error> { let mut c = self.core_mutex.lock().await; c.remaining_time_alarm().await } async fn set_remaining_time_alarm(&mut self, v: u16) -> Result<(), Self::Error> { let mut c = self.core_mutex.lock().await; c.set_remaining_time_alarm(v).await } async fn battery_mode(&mut self) -> Result<BatteryModeFields, Self::Error> { let mut c = self.core_mutex.lock().await; c.battery_mode().await } async fn set_battery_mode(&mut self, v: BatteryModeFields) -> Result<(), Self::Error> { let mut c = self.core_mutex.lock().await; c.set_battery_mode(v).await } async fn at_rate(&mut self) -> Result<CapacityModeSignedValue, Self::Error> { let mut c = self.core_mutex.lock().await; c.at_rate().await } async fn set_at_rate(&mut self, _: CapacityModeSignedValue) -> Result<(), Self::Error> { let mut c = self.core_mutex.lock().await; c.set_at_rate(CapacityModeSignedValue::MilliAmpSigned(0)).await } async fn at_rate_time_to_full(&mut self) -> Result<u16, Self::Error> { let mut c = self.core_mutex.lock().await; c.at_rate_time_to_full().await } async fn at_rate_time_to_empty(&mut self) -> Result<u16, Self::Error> { let mut c = self.core_mutex.lock().await; c.at_rate_time_to_empty().await } async fn at_rate_ok(&mut self) -> Result<bool, Self::Error> { let mut c = self.core_mutex.lock().await; c.at_rate_ok().await } async fn current(&mut self) -> Result<i16, Self::Error> { let mut c = self.core_mutex.lock().await; c.current().await } async fn average_current(&mut self) -> Result<i16, Self::Error> { let mut c = self.core_mutex.lock().await; c.average_current().await } async fn max_error(&mut self) -> Result<u8, Self::Error> { let mut c = self.core_mutex.lock().await; c.max_error().await } async fn relative_state_of_charge(&mut self) -> Result<u8, Self::Error> { let mut c = self.core_mutex.lock().await; c.relative_state_of_charge().await } async fn absolute_state_of_charge(&mut self) -> Result<u8, Self::Error> { let mut c = self.core_mutex.lock().await; c.absolute_state_of_charge().await } async fn remaining_capacity(&mut self) -> Result<CapacityModeValue, Self::Error> { let mut c = self.core_mutex.lock().await; c.remaining_capacity().await } async fn full_charge_capacity(&mut self) -> Result<CapacityModeValue, Self::Error> { let mut c = self.core_mutex.lock().await; c.full_charge_capacity().await } async fn run_time_to_empty(&mut self) -> Result<u16, Self::Error> { let mut c = self.core_mutex.lock().await; c.run_time_to_empty().await } async fn average_time_to_empty(&mut self) -> Result<u16, Self::Error> { let mut c = self.core_mutex.lock().await; c.average_time_to_empty().await } async fn average_time_to_full(&mut self) -> Result<u16, Self::Error> { let mut c = self.core_mutex.lock().await; c.average_time_to_full().await } async fn charging_current(&mut self) -> Result<u16, Self::Error> { let mut c = self.core_mutex.lock().await; c.charging_current().await } async fn charging_voltage(&mut self) -> Result<u16, Self::Error> { let mut c = self.core_mutex.lock().await; c.charging_voltage().await } async fn battery_status(&mut self) -> Result<BatteryStatusFields, Self::Error> { let mut c = self.core_mutex.lock().await; c.battery_status().await } async fn cycle_count(&mut self) -> Result<u16, Self::Error> { let mut c = self.core_mutex.lock().await; c.cycle_count().await } async fn design_capacity(&mut self) -> Result<CapacityModeValue, Self::Error> { let mut c = self.core_mutex.lock().await; c.design_capacity().await } async fn design_voltage(&mut self) -> Result<u16, Self::Error> { let mut c = self.core_mutex.lock().await; c.design_voltage().await } async fn specification_info(&mut self) -> Result<SpecificationInfoFields, Self::Error> { let mut c = self.core_mutex.lock().await; c.specification_info().await } async fn manufacture_date(&mut self) -> Result<ManufactureDate, Self::Error> { let mut c = self.core_mutex.lock().await; c.manufacture_date().await } async fn serial_number(&mut self) -> Result<u16, Self::Error> { let mut c = self.core_mutex.lock().await; c.serial_number().await } async fn manufacturer_name(&mut self, v: &mut [u8]) -> Result<(), Self::Error> { let mut c = self.core_mutex.lock().await; c.manufacturer_name(v).await } async fn device_name(&mut self, v: &mut [u8]) -> Result<(), Self::Error> { let mut c = self.core_mutex.lock().await; c.device_name(v).await } async fn device_chemistry(&mut self, v: &mut [u8]) -> Result<(), Self::Error> { let mut c = self.core_mutex.lock().await; c.device_chemistry(v).await } } impl Controller for BatteryAdapter { type ControllerError = MockBatteryError; async fn initialize(&mut self) -> Result<(), Self::ControllerError> { let mut c = self.core_mutex.lock().await; self.sync_timeout_cache(&mut *c); // deref + ref safely casts away the guard c.initialize().await } async fn get_static_data(&mut self) -> Result<StaticBatteryMsgs, Self::ControllerError> { let mut c = self.core_mutex.lock().await; self.sync_timeout_cache(&mut *c); // deref + ref safely casts away the guard c.get_static_data().await } async fn get_dynamic_data(&mut self) -> Result<DynamicBatteryMsgs, Self::ControllerError> { let mut c = self.core_mutex.lock().await; self.sync_timeout_cache(&mut *c); // deref + ref safely casts away the guard c.get_dynamic_data().await } async fn get_device_event(&mut self) -> ControllerEvent { core::future::pending().await } async fn ping(&mut self) -> Result<(), Self::ControllerError> { let mut c = self.core_mutex.lock().await; self.sync_timeout_cache(&mut *c); // deref + ref safely casts away the guard c.ping().await } fn get_timeout(&self) -> Duration { // Fast path: if we can grab the mutex without waiting, read the real value. if let Ok(guard) = self.core_mutex.try_lock() { let d = guard.get_timeout(); // assumed non-async on core self.timeout_ms.store(Self::dur_to_ms(d), Ordering::Relaxed); d } else { // Fallback to cached value if the mutex is busy. Self::ms_to_dur(self.timeout_ms.load(Ordering::Relaxed)) } } fn set_timeout(&mut self, duration: Duration) { // Always update our cache immediately. self.timeout_ms.store(Self::dur_to_ms(duration), Ordering::Relaxed); // Try to apply to the real controller right away if the mutex is free. // if the mutex is busy, we'll simply use the previous cache next time. if let Ok(mut guard) = self.core_mutex.try_lock() { guard.set_timeout(duration); // assumed non-async on core } } } }
As noted, the BatteryAdapter
is nothing more than a forwarding mechanism to direct the trait methods called by the battery service into our code base. We pass it the reference to our core_mutex
which is then used to call the battery controller traits implemented there, in our ControllerCore
code.
Note that we might also have chosen to direct all but the get_static_data
/ get_dynamic_data
trait methods of BatteryAdapter
directly to the MockBatteryController
, since the ControllerCore
is going to simply forward them there anyway.