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