1*cf78ab8cSAndroid Build Coastguard Worker // Copyright 2023 Google LLC
2*cf78ab8cSAndroid Build Coastguard Worker //
3*cf78ab8cSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*cf78ab8cSAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*cf78ab8cSAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*cf78ab8cSAndroid Build Coastguard Worker //
7*cf78ab8cSAndroid Build Coastguard Worker // https://www.apache.org/licenses/LICENSE-2.0
8*cf78ab8cSAndroid Build Coastguard Worker //
9*cf78ab8cSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*cf78ab8cSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*cf78ab8cSAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*cf78ab8cSAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*cf78ab8cSAndroid Build Coastguard Worker // limitations under the License.
14*cf78ab8cSAndroid Build Coastguard Worker
15*cf78ab8cSAndroid Build Coastguard Worker use bytes::Bytes;
16*cf78ab8cSAndroid Build Coastguard Worker use futures::{channel::mpsc::UnboundedSender, sink::SinkExt, StreamExt};
17*cf78ab8cSAndroid Build Coastguard Worker use pica::{Handle, Pica};
18*cf78ab8cSAndroid Build Coastguard Worker
19*cf78ab8cSAndroid Build Coastguard Worker use netsim_proto::model::chip::Radio as ProtoRadio;
20*cf78ab8cSAndroid Build Coastguard Worker use netsim_proto::model::Chip as ProtoChip;
21*cf78ab8cSAndroid Build Coastguard Worker use netsim_proto::stats::{netsim_radio_stats, NetsimRadioStats as ProtoRadioStats};
22*cf78ab8cSAndroid Build Coastguard Worker
23*cf78ab8cSAndroid Build Coastguard Worker use crate::devices::chip::ChipIdentifier;
24*cf78ab8cSAndroid Build Coastguard Worker use crate::uwb::ranging_estimator::{SharedState, UwbRangingEstimator};
25*cf78ab8cSAndroid Build Coastguard Worker use crate::wireless::packet::handle_response;
26*cf78ab8cSAndroid Build Coastguard Worker
27*cf78ab8cSAndroid Build Coastguard Worker use std::sync::atomic::{AtomicBool, AtomicI32, Ordering};
28*cf78ab8cSAndroid Build Coastguard Worker use std::sync::{Arc, Mutex, OnceLock};
29*cf78ab8cSAndroid Build Coastguard Worker use std::thread;
30*cf78ab8cSAndroid Build Coastguard Worker
31*cf78ab8cSAndroid Build Coastguard Worker use super::{WirelessAdaptor, WirelessAdaptorImpl};
32*cf78ab8cSAndroid Build Coastguard Worker
33*cf78ab8cSAndroid Build Coastguard Worker // TODO(b/331267949): Construct Manager struct for each wireless_adaptor module
34*cf78ab8cSAndroid Build Coastguard Worker static PICA_HANDLE_TO_STATE: OnceLock<SharedState> = OnceLock::new();
35*cf78ab8cSAndroid Build Coastguard Worker
get_pica_handle_to_state() -> &'static SharedState36*cf78ab8cSAndroid Build Coastguard Worker fn get_pica_handle_to_state() -> &'static SharedState {
37*cf78ab8cSAndroid Build Coastguard Worker PICA_HANDLE_TO_STATE.get_or_init(SharedState::new)
38*cf78ab8cSAndroid Build Coastguard Worker }
39*cf78ab8cSAndroid Build Coastguard Worker
40*cf78ab8cSAndroid Build Coastguard Worker static PICA: OnceLock<Arc<Mutex<Pica>>> = OnceLock::new();
41*cf78ab8cSAndroid Build Coastguard Worker
get_pica() -> Arc<Mutex<Pica>>42*cf78ab8cSAndroid Build Coastguard Worker fn get_pica() -> Arc<Mutex<Pica>> {
43*cf78ab8cSAndroid Build Coastguard Worker PICA.get_or_init(|| {
44*cf78ab8cSAndroid Build Coastguard Worker Arc::new(Mutex::new(Pica::new(
45*cf78ab8cSAndroid Build Coastguard Worker Box::new(UwbRangingEstimator::new(get_pica_handle_to_state().clone())),
46*cf78ab8cSAndroid Build Coastguard Worker None,
47*cf78ab8cSAndroid Build Coastguard Worker )))
48*cf78ab8cSAndroid Build Coastguard Worker })
49*cf78ab8cSAndroid Build Coastguard Worker .clone()
50*cf78ab8cSAndroid Build Coastguard Worker }
51*cf78ab8cSAndroid Build Coastguard Worker
52*cf78ab8cSAndroid Build Coastguard Worker static PICA_RUNTIME: OnceLock<Arc<tokio::runtime::Runtime>> = OnceLock::new();
53*cf78ab8cSAndroid Build Coastguard Worker
get_pica_runtime() -> Arc<tokio::runtime::Runtime>54*cf78ab8cSAndroid Build Coastguard Worker fn get_pica_runtime() -> Arc<tokio::runtime::Runtime> {
55*cf78ab8cSAndroid Build Coastguard Worker PICA_RUNTIME.get_or_init(|| Arc::new(tokio::runtime::Runtime::new().unwrap())).clone()
56*cf78ab8cSAndroid Build Coastguard Worker }
57*cf78ab8cSAndroid Build Coastguard Worker
58*cf78ab8cSAndroid Build Coastguard Worker /// Parameters for creating UWB chips
59*cf78ab8cSAndroid Build Coastguard Worker pub struct CreateParams {
60*cf78ab8cSAndroid Build Coastguard Worker #[allow(dead_code)]
61*cf78ab8cSAndroid Build Coastguard Worker pub address: String,
62*cf78ab8cSAndroid Build Coastguard Worker }
63*cf78ab8cSAndroid Build Coastguard Worker
64*cf78ab8cSAndroid Build Coastguard Worker /// UWB struct will keep track of pica_id
65*cf78ab8cSAndroid Build Coastguard Worker pub struct Uwb {
66*cf78ab8cSAndroid Build Coastguard Worker pica_id: Handle,
67*cf78ab8cSAndroid Build Coastguard Worker uci_stream_writer: UnboundedSender<Vec<u8>>,
68*cf78ab8cSAndroid Build Coastguard Worker state: AtomicBool,
69*cf78ab8cSAndroid Build Coastguard Worker tx_count: AtomicI32,
70*cf78ab8cSAndroid Build Coastguard Worker rx_count: Arc<AtomicI32>,
71*cf78ab8cSAndroid Build Coastguard Worker }
72*cf78ab8cSAndroid Build Coastguard Worker
73*cf78ab8cSAndroid Build Coastguard Worker impl Drop for Uwb {
drop(&mut self)74*cf78ab8cSAndroid Build Coastguard Worker fn drop(&mut self) {
75*cf78ab8cSAndroid Build Coastguard Worker get_pica_handle_to_state().remove(&self.pica_id);
76*cf78ab8cSAndroid Build Coastguard Worker }
77*cf78ab8cSAndroid Build Coastguard Worker }
78*cf78ab8cSAndroid Build Coastguard Worker
79*cf78ab8cSAndroid Build Coastguard Worker impl WirelessAdaptor for Uwb {
handle_request(&self, packet: &Bytes)80*cf78ab8cSAndroid Build Coastguard Worker fn handle_request(&self, packet: &Bytes) {
81*cf78ab8cSAndroid Build Coastguard Worker // TODO(b/330788870): Increment tx_count
82*cf78ab8cSAndroid Build Coastguard Worker self.uci_stream_writer
83*cf78ab8cSAndroid Build Coastguard Worker .unbounded_send(packet.clone().into())
84*cf78ab8cSAndroid Build Coastguard Worker .expect("UciStream Receiver Disconnected");
85*cf78ab8cSAndroid Build Coastguard Worker let _ = self.tx_count.fetch_add(1, Ordering::SeqCst);
86*cf78ab8cSAndroid Build Coastguard Worker }
87*cf78ab8cSAndroid Build Coastguard Worker
reset(&self)88*cf78ab8cSAndroid Build Coastguard Worker fn reset(&self) {
89*cf78ab8cSAndroid Build Coastguard Worker self.state.store(true, Ordering::SeqCst);
90*cf78ab8cSAndroid Build Coastguard Worker self.tx_count.store(0, Ordering::SeqCst);
91*cf78ab8cSAndroid Build Coastguard Worker self.rx_count.store(0, Ordering::SeqCst);
92*cf78ab8cSAndroid Build Coastguard Worker }
93*cf78ab8cSAndroid Build Coastguard Worker
get(&self) -> ProtoChip94*cf78ab8cSAndroid Build Coastguard Worker fn get(&self) -> ProtoChip {
95*cf78ab8cSAndroid Build Coastguard Worker let mut chip_proto = ProtoChip::new();
96*cf78ab8cSAndroid Build Coastguard Worker let uwb_proto = ProtoRadio {
97*cf78ab8cSAndroid Build Coastguard Worker state: self.state.load(Ordering::SeqCst).into(),
98*cf78ab8cSAndroid Build Coastguard Worker tx_count: self.tx_count.load(Ordering::SeqCst),
99*cf78ab8cSAndroid Build Coastguard Worker rx_count: self.rx_count.load(Ordering::SeqCst),
100*cf78ab8cSAndroid Build Coastguard Worker ..Default::default()
101*cf78ab8cSAndroid Build Coastguard Worker };
102*cf78ab8cSAndroid Build Coastguard Worker chip_proto.mut_uwb().clone_from(&uwb_proto);
103*cf78ab8cSAndroid Build Coastguard Worker chip_proto
104*cf78ab8cSAndroid Build Coastguard Worker }
105*cf78ab8cSAndroid Build Coastguard Worker
patch(&self, chip: &ProtoChip)106*cf78ab8cSAndroid Build Coastguard Worker fn patch(&self, chip: &ProtoChip) {
107*cf78ab8cSAndroid Build Coastguard Worker if !chip.has_uwb() {
108*cf78ab8cSAndroid Build Coastguard Worker return;
109*cf78ab8cSAndroid Build Coastguard Worker }
110*cf78ab8cSAndroid Build Coastguard Worker if let Some(patch_state) = chip.uwb().state {
111*cf78ab8cSAndroid Build Coastguard Worker self.state.store(patch_state, Ordering::SeqCst);
112*cf78ab8cSAndroid Build Coastguard Worker }
113*cf78ab8cSAndroid Build Coastguard Worker }
114*cf78ab8cSAndroid Build Coastguard Worker
get_stats(&self, duration_secs: u64) -> Vec<ProtoRadioStats>115*cf78ab8cSAndroid Build Coastguard Worker fn get_stats(&self, duration_secs: u64) -> Vec<ProtoRadioStats> {
116*cf78ab8cSAndroid Build Coastguard Worker let mut stats_proto = ProtoRadioStats::new();
117*cf78ab8cSAndroid Build Coastguard Worker stats_proto.set_duration_secs(duration_secs);
118*cf78ab8cSAndroid Build Coastguard Worker stats_proto.set_kind(netsim_radio_stats::Kind::UWB);
119*cf78ab8cSAndroid Build Coastguard Worker let chip_proto = self.get();
120*cf78ab8cSAndroid Build Coastguard Worker if chip_proto.has_uwb() {
121*cf78ab8cSAndroid Build Coastguard Worker stats_proto.set_tx_count(chip_proto.uwb().tx_count);
122*cf78ab8cSAndroid Build Coastguard Worker stats_proto.set_rx_count(chip_proto.uwb().rx_count);
123*cf78ab8cSAndroid Build Coastguard Worker }
124*cf78ab8cSAndroid Build Coastguard Worker vec![stats_proto]
125*cf78ab8cSAndroid Build Coastguard Worker }
126*cf78ab8cSAndroid Build Coastguard Worker }
127*cf78ab8cSAndroid Build Coastguard Worker
uwb_start()128*cf78ab8cSAndroid Build Coastguard Worker pub fn uwb_start() {
129*cf78ab8cSAndroid Build Coastguard Worker // TODO: Provide TcpStream as UWB connector
130*cf78ab8cSAndroid Build Coastguard Worker let _ = thread::Builder::new().name("pica_service".to_string()).spawn(move || {
131*cf78ab8cSAndroid Build Coastguard Worker log::info!("PICA STARTED");
132*cf78ab8cSAndroid Build Coastguard Worker let _guard = get_pica_runtime().enter();
133*cf78ab8cSAndroid Build Coastguard Worker futures::executor::block_on(pica::run(&get_pica()))
134*cf78ab8cSAndroid Build Coastguard Worker });
135*cf78ab8cSAndroid Build Coastguard Worker }
136*cf78ab8cSAndroid Build Coastguard Worker
new(_create_params: &CreateParams, chip_id: ChipIdentifier) -> WirelessAdaptorImpl137*cf78ab8cSAndroid Build Coastguard Worker pub fn new(_create_params: &CreateParams, chip_id: ChipIdentifier) -> WirelessAdaptorImpl {
138*cf78ab8cSAndroid Build Coastguard Worker let (uci_stream_sender, uci_stream_receiver) = futures::channel::mpsc::unbounded();
139*cf78ab8cSAndroid Build Coastguard Worker let (uci_sink_sender, uci_sink_receiver) = futures::channel::mpsc::unbounded();
140*cf78ab8cSAndroid Build Coastguard Worker let _guard = get_pica_runtime().enter();
141*cf78ab8cSAndroid Build Coastguard Worker let pica_id = get_pica()
142*cf78ab8cSAndroid Build Coastguard Worker .lock()
143*cf78ab8cSAndroid Build Coastguard Worker .unwrap()
144*cf78ab8cSAndroid Build Coastguard Worker .add_device(Box::pin(uci_stream_receiver), Box::pin(uci_sink_sender.sink_err_into()))
145*cf78ab8cSAndroid Build Coastguard Worker .unwrap();
146*cf78ab8cSAndroid Build Coastguard Worker get_pica_handle_to_state().insert(pica_id, chip_id);
147*cf78ab8cSAndroid Build Coastguard Worker
148*cf78ab8cSAndroid Build Coastguard Worker let rx_count = Arc::new(AtomicI32::new(0));
149*cf78ab8cSAndroid Build Coastguard Worker let uwb = Uwb {
150*cf78ab8cSAndroid Build Coastguard Worker pica_id,
151*cf78ab8cSAndroid Build Coastguard Worker uci_stream_writer: uci_stream_sender,
152*cf78ab8cSAndroid Build Coastguard Worker state: AtomicBool::new(true),
153*cf78ab8cSAndroid Build Coastguard Worker tx_count: AtomicI32::new(0),
154*cf78ab8cSAndroid Build Coastguard Worker rx_count: rx_count.clone(),
155*cf78ab8cSAndroid Build Coastguard Worker };
156*cf78ab8cSAndroid Build Coastguard Worker
157*cf78ab8cSAndroid Build Coastguard Worker // Spawn a future for obtaining packet from pica and invoking handle_response_rust
158*cf78ab8cSAndroid Build Coastguard Worker get_pica_runtime().spawn(async move {
159*cf78ab8cSAndroid Build Coastguard Worker let mut uci_sink_receiver = uci_sink_receiver;
160*cf78ab8cSAndroid Build Coastguard Worker while let Some(packet) = uci_sink_receiver.next().await {
161*cf78ab8cSAndroid Build Coastguard Worker handle_response(chip_id, &Bytes::from(packet));
162*cf78ab8cSAndroid Build Coastguard Worker rx_count.fetch_add(1, Ordering::SeqCst);
163*cf78ab8cSAndroid Build Coastguard Worker }
164*cf78ab8cSAndroid Build Coastguard Worker });
165*cf78ab8cSAndroid Build Coastguard Worker Box::new(uwb)
166*cf78ab8cSAndroid Build Coastguard Worker }
167*cf78ab8cSAndroid Build Coastguard Worker
168*cf78ab8cSAndroid Build Coastguard Worker #[cfg(test)]
169*cf78ab8cSAndroid Build Coastguard Worker mod tests {
170*cf78ab8cSAndroid Build Coastguard Worker
171*cf78ab8cSAndroid Build Coastguard Worker use super::*;
172*cf78ab8cSAndroid Build Coastguard Worker
new_uwb_wireless_adaptor() -> WirelessAdaptorImpl173*cf78ab8cSAndroid Build Coastguard Worker fn new_uwb_wireless_adaptor() -> WirelessAdaptorImpl {
174*cf78ab8cSAndroid Build Coastguard Worker new(&CreateParams { address: "test".to_string() }, ChipIdentifier(0))
175*cf78ab8cSAndroid Build Coastguard Worker }
176*cf78ab8cSAndroid Build Coastguard Worker
patch_chip_proto() -> ProtoChip177*cf78ab8cSAndroid Build Coastguard Worker fn patch_chip_proto() -> ProtoChip {
178*cf78ab8cSAndroid Build Coastguard Worker let mut chip_proto = ProtoChip::new();
179*cf78ab8cSAndroid Build Coastguard Worker let uwb_proto = ProtoRadio { state: false.into(), ..Default::default() };
180*cf78ab8cSAndroid Build Coastguard Worker chip_proto.mut_uwb().clone_from(&uwb_proto);
181*cf78ab8cSAndroid Build Coastguard Worker chip_proto
182*cf78ab8cSAndroid Build Coastguard Worker }
183*cf78ab8cSAndroid Build Coastguard Worker
184*cf78ab8cSAndroid Build Coastguard Worker #[test]
test_uwb_get()185*cf78ab8cSAndroid Build Coastguard Worker fn test_uwb_get() {
186*cf78ab8cSAndroid Build Coastguard Worker let wireless_adaptor = new_uwb_wireless_adaptor();
187*cf78ab8cSAndroid Build Coastguard Worker assert!(wireless_adaptor.get().has_uwb());
188*cf78ab8cSAndroid Build Coastguard Worker }
189*cf78ab8cSAndroid Build Coastguard Worker
190*cf78ab8cSAndroid Build Coastguard Worker #[test]
test_uwb_patch_and_reset()191*cf78ab8cSAndroid Build Coastguard Worker fn test_uwb_patch_and_reset() {
192*cf78ab8cSAndroid Build Coastguard Worker let wireless_adaptor = new_uwb_wireless_adaptor();
193*cf78ab8cSAndroid Build Coastguard Worker wireless_adaptor.patch(&patch_chip_proto());
194*cf78ab8cSAndroid Build Coastguard Worker let binding = wireless_adaptor.get();
195*cf78ab8cSAndroid Build Coastguard Worker let radio = binding.uwb();
196*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(radio.state, Some(false));
197*cf78ab8cSAndroid Build Coastguard Worker wireless_adaptor.reset();
198*cf78ab8cSAndroid Build Coastguard Worker let binding = wireless_adaptor.get();
199*cf78ab8cSAndroid Build Coastguard Worker let radio = binding.uwb();
200*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(radio.rx_count, 0);
201*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(radio.tx_count, 0);
202*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(radio.state, Some(true));
203*cf78ab8cSAndroid Build Coastguard Worker }
204*cf78ab8cSAndroid Build Coastguard Worker
205*cf78ab8cSAndroid Build Coastguard Worker #[test]
test_get_stats()206*cf78ab8cSAndroid Build Coastguard Worker fn test_get_stats() {
207*cf78ab8cSAndroid Build Coastguard Worker let wireless_adaptor = new_uwb_wireless_adaptor();
208*cf78ab8cSAndroid Build Coastguard Worker let radio_stat_vec = wireless_adaptor.get_stats(0);
209*cf78ab8cSAndroid Build Coastguard Worker let radio_stat = radio_stat_vec.first().unwrap();
210*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(radio_stat.kind(), netsim_radio_stats::Kind::UWB);
211*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(radio_stat.duration_secs(), 0);
212*cf78ab8cSAndroid Build Coastguard Worker }
213*cf78ab8cSAndroid Build Coastguard Worker }
214