• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 pica::{Handle, Pica};
18 
19 use netsim_proto::model::chip::Radio as ProtoRadio;
20 use netsim_proto::model::Chip as ProtoChip;
21 use netsim_proto::stats::{netsim_radio_stats, NetsimRadioStats as ProtoRadioStats};
22 
23 use crate::devices::chip::ChipIdentifier;
24 use crate::get_runtime;
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, OnceLock};
30 use std::thread;
31 
32 use super::{WirelessChip, WirelessChipImpl};
33 
34 // TODO(b/331267949): Construct Manager struct for each wireless_chip module
35 static PICA_HANDLE_TO_STATE: OnceLock<SharedState> = OnceLock::new();
36 
get_pica_handle_to_state() -> &'static SharedState37 fn get_pica_handle_to_state() -> &'static SharedState {
38     PICA_HANDLE_TO_STATE.get_or_init(SharedState::new)
39 }
40 
41 static PICA: OnceLock<Arc<Mutex<Pica>>> = OnceLock::new();
42 
get_pica() -> Arc<Mutex<Pica>>43 fn get_pica() -> Arc<Mutex<Pica>> {
44     PICA.get_or_init(|| {
45         Arc::new(Mutex::new(Pica::new(
46             Box::new(UwbRangingEstimator::new(get_pica_handle_to_state().clone())),
47             None,
48         )))
49     })
50     .clone()
51 }
52 
53 /// Parameters for creating UWB chips
54 pub struct CreateParams {
55     #[allow(dead_code)]
56     pub address: String,
57 }
58 
59 /// UWB struct will keep track of pica_id
60 pub struct Uwb {
61     pica_id: Handle,
62     uci_stream_writer: UnboundedSender<Vec<u8>>,
63     state: AtomicBool,
64     tx_count: AtomicI32,
65     rx_count: Arc<AtomicI32>,
66 }
67 
68 impl Drop for Uwb {
drop(&mut self)69     fn drop(&mut self) {
70         get_pica_handle_to_state().remove(&self.pica_id);
71     }
72 }
73 
74 impl WirelessChip for Uwb {
handle_request(&self, packet: &Bytes)75     fn handle_request(&self, packet: &Bytes) {
76         // TODO(b/330788870): Increment tx_count
77         self.uci_stream_writer
78             .unbounded_send(packet.clone().into())
79             .expect("UciStream Receiver Disconnected");
80         let _ = self.tx_count.fetch_add(1, Ordering::SeqCst);
81     }
82 
reset(&self)83     fn reset(&self) {
84         self.state.store(true, Ordering::SeqCst);
85         self.tx_count.store(0, Ordering::SeqCst);
86         self.rx_count.store(0, Ordering::SeqCst);
87     }
88 
get(&self) -> ProtoChip89     fn get(&self) -> ProtoChip {
90         let mut chip_proto = ProtoChip::new();
91         let uwb_proto = ProtoRadio {
92             state: self.state.load(Ordering::SeqCst).into(),
93             tx_count: self.tx_count.load(Ordering::SeqCst),
94             rx_count: self.rx_count.load(Ordering::SeqCst),
95             ..Default::default()
96         };
97         chip_proto.mut_uwb().clone_from(&uwb_proto);
98         chip_proto
99     }
100 
patch(&self, chip: &ProtoChip)101     fn patch(&self, chip: &ProtoChip) {
102         if !chip.has_uwb() {
103             return;
104         }
105         if let Some(patch_state) = chip.uwb().state {
106             self.state.store(patch_state, Ordering::SeqCst);
107         }
108     }
109 
get_stats(&self, duration_secs: u64) -> Vec<ProtoRadioStats>110     fn get_stats(&self, duration_secs: u64) -> Vec<ProtoRadioStats> {
111         let mut stats_proto = ProtoRadioStats::new();
112         stats_proto.set_duration_secs(duration_secs);
113         stats_proto.set_kind(netsim_radio_stats::Kind::UWB);
114         let chip_proto = self.get();
115         if chip_proto.has_uwb() {
116             stats_proto.set_tx_count(chip_proto.uwb().tx_count);
117             stats_proto.set_rx_count(chip_proto.uwb().rx_count);
118         }
119         vec![stats_proto]
120     }
121 }
122 
uwb_start()123 pub fn uwb_start() {
124     // TODO: Provide TcpStream as UWB connector
125     let _ = thread::Builder::new().name("pica_service".to_string()).spawn(move || {
126         log::info!("PICA STARTED");
127         let _guard = get_runtime().enter();
128         futures::executor::block_on(pica::run(&get_pica()))
129     });
130 }
131 
add_chip(_create_params: &CreateParams, chip_id: ChipIdentifier) -> WirelessChipImpl132 pub fn add_chip(_create_params: &CreateParams, chip_id: ChipIdentifier) -> WirelessChipImpl {
133     let (uci_stream_sender, uci_stream_receiver) = futures::channel::mpsc::unbounded();
134     let (uci_sink_sender, uci_sink_receiver) = futures::channel::mpsc::unbounded();
135     let _guard = get_runtime().enter();
136     let pica_id = get_pica()
137         .lock()
138         .unwrap()
139         .add_device(Box::pin(uci_stream_receiver), Box::pin(uci_sink_sender.sink_err_into()))
140         .unwrap();
141     get_pica_handle_to_state().insert(pica_id, chip_id);
142 
143     let rx_count = Arc::new(AtomicI32::new(0));
144     let uwb = Uwb {
145         pica_id,
146         uci_stream_writer: uci_stream_sender,
147         state: AtomicBool::new(true),
148         tx_count: AtomicI32::new(0),
149         rx_count: rx_count.clone(),
150     };
151 
152     // Spawn a future for obtaining packet from pica and invoking handle_response_rust
153     get_runtime().spawn(async move {
154         let mut uci_sink_receiver = uci_sink_receiver;
155         while let Some(packet) = uci_sink_receiver.next().await {
156             handle_response(chip_id, &Bytes::from(packet));
157             rx_count.fetch_add(1, Ordering::SeqCst);
158         }
159     });
160     Box::new(uwb)
161 }
162 
163 #[cfg(test)]
164 mod tests {
165 
166     use super::*;
167 
add_uwb_wireless_chip() -> WirelessChipImpl168     fn add_uwb_wireless_chip() -> WirelessChipImpl {
169         add_chip(&CreateParams { address: "test".to_string() }, ChipIdentifier(0))
170     }
171 
patch_chip_proto() -> ProtoChip172     fn patch_chip_proto() -> ProtoChip {
173         let mut chip_proto = ProtoChip::new();
174         let uwb_proto = ProtoRadio { state: false.into(), ..Default::default() };
175         chip_proto.mut_uwb().clone_from(&uwb_proto);
176         chip_proto
177     }
178 
179     #[test]
test_uwb_get()180     fn test_uwb_get() {
181         let wireless_chip = add_uwb_wireless_chip();
182         assert!(wireless_chip.get().has_uwb());
183     }
184 
185     #[test]
test_uwb_patch_and_reset()186     fn test_uwb_patch_and_reset() {
187         let wireless_chip = add_uwb_wireless_chip();
188         wireless_chip.patch(&patch_chip_proto());
189         let binding = wireless_chip.get();
190         let radio = binding.uwb();
191         assert_eq!(radio.state, Some(false));
192         wireless_chip.reset();
193         let binding = wireless_chip.get();
194         let radio = binding.uwb();
195         assert_eq!(radio.rx_count, 0);
196         assert_eq!(radio.tx_count, 0);
197         assert_eq!(radio.state, Some(true));
198     }
199 
200     #[test]
test_get_stats()201     fn test_get_stats() {
202         let wireless_chip = add_uwb_wireless_chip();
203         let radio_stat_vec = wireless_chip.get_stats(0);
204         let radio_stat = radio_stat_vec.first().unwrap();
205         assert_eq!(radio_stat.kind(), netsim_radio_stats::Kind::UWB);
206         assert_eq!(radio_stat.duration_secs(), 0);
207     }
208 }
209