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