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