xref: /aosp_15_r20/tools/netsim/rust/daemon/src/devices/devices_handler.rs (revision cf78ab8cffb8fc9207af348f23af247fb04370a6)
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 // devices_handler.rs
16 //
17 // Provides the API for the frontend and backend to interact with devices.
18 //
19 // The DeviceManager struct is a singleton for the devices collection.
20 //
21 // Additional functions are
22 // -- inactivity instant
23 // -- vending device identifiers
24 
25 use super::chip;
26 use super::chip::ChipIdentifier;
27 use super::device::DeviceIdentifier;
28 use crate::devices::device::{AddChipResult, Device};
29 use crate::events;
30 use crate::events::{
31     ChipAdded, ChipRemoved, DeviceAdded, DevicePatched, DeviceRemoved, Event, Events, ShutDown,
32 };
33 use crate::ffi::ffi_response_writable::CxxServerResponseWriter;
34 use crate::ffi::CxxServerResponseWriterWrapper;
35 use crate::http_server::server_response::ResponseWritable;
36 use crate::wireless;
37 use cxx::{CxxString, CxxVector};
38 use http::Request;
39 use http::Version;
40 use log::{info, warn};
41 use netsim_proto::common::ChipKind as ProtoChipKind;
42 use netsim_proto::configuration::Controller;
43 use netsim_proto::frontend::patch_device_request::PatchDeviceFields as ProtoPatchDeviceFields;
44 use netsim_proto::frontend::CreateDeviceRequest;
45 use netsim_proto::frontend::CreateDeviceResponse;
46 use netsim_proto::frontend::DeleteChipRequest;
47 use netsim_proto::frontend::ListDeviceResponse;
48 use netsim_proto::frontend::PatchDeviceRequest;
49 use netsim_proto::frontend::SubscribeDeviceRequest;
50 use netsim_proto::model::chip_create::Chip as ProtoBuiltin;
51 use netsim_proto::model::Chip as ProtoChip;
52 use netsim_proto::model::Device as ProtoDevice;
53 use netsim_proto::model::Orientation as ProtoOrientation;
54 use netsim_proto::model::Position as ProtoPosition;
55 use netsim_proto::startup::DeviceInfo as ProtoDeviceInfo;
56 use netsim_proto::stats::{NetsimDeviceStats as ProtoDeviceStats, NetsimRadioStats};
57 use protobuf::well_known_types::timestamp::Timestamp;
58 use protobuf::Message;
59 use protobuf::MessageField;
60 use protobuf_json_mapping::merge_from_str;
61 use protobuf_json_mapping::print_to_string;
62 use protobuf_json_mapping::print_to_string_with_options;
63 use protobuf_json_mapping::PrintOptions;
64 use std::collections::{BTreeMap, HashMap};
65 use std::pin::Pin;
66 use std::sync::atomic::{AtomicU32, Ordering};
67 use std::sync::mpsc::Receiver;
68 use std::sync::Arc;
69 use std::sync::Mutex;
70 use std::sync::OnceLock;
71 use std::sync::RwLock;
72 use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
73 
74 // The amount of seconds netsimd will wait until the first device has attached.
75 static IDLE_SECS_FOR_SHUTDOWN: u64 = 15;
76 
77 const INITIAL_DEVICE_ID: u32 = 1;
78 const JSON_PRINT_OPTION: PrintOptions = PrintOptions {
79     enum_values_int: false,
80     proto_field_name: false,
81     always_output_default_values: true,
82     _future_options: (),
83 };
84 
85 static POSE_MANAGER: OnceLock<Arc<PoseManager>> = OnceLock::new();
86 
get_pose_manager() -> Arc<PoseManager>87 fn get_pose_manager() -> Arc<PoseManager> {
88     POSE_MANAGER.get_or_init(|| Arc::new(PoseManager::new())).clone()
89 }
90 
91 pub struct PoseManager {
92     positions: RwLock<HashMap<DeviceIdentifier, ProtoPosition>>,
93     orientations: RwLock<HashMap<DeviceIdentifier, ProtoOrientation>>,
94 }
95 
96 impl PoseManager {
new() -> Self97     pub fn new() -> Self {
98         PoseManager {
99             positions: RwLock::new(HashMap::new()),
100             orientations: RwLock::new(HashMap::new()),
101         }
102     }
103 
add(&self, device_id: DeviceIdentifier)104     pub fn add(&self, device_id: DeviceIdentifier) {
105         self.positions.write().unwrap().insert(device_id, ProtoPosition::new());
106         self.orientations.write().unwrap().insert(device_id, ProtoOrientation::new());
107     }
108 
remove(&self, device_id: &DeviceIdentifier)109     pub fn remove(&self, device_id: &DeviceIdentifier) {
110         self.positions.write().unwrap().remove(device_id);
111         self.orientations.write().unwrap().remove(device_id);
112     }
113 
reset(&self, device_id: DeviceIdentifier)114     pub fn reset(&self, device_id: DeviceIdentifier) {
115         self.positions.write().unwrap().insert(device_id, ProtoPosition::new());
116         self.orientations.write().unwrap().insert(device_id, ProtoOrientation::new());
117     }
118 
set_position(&self, device_id: DeviceIdentifier, position: &ProtoPosition)119     pub fn set_position(&self, device_id: DeviceIdentifier, position: &ProtoPosition) {
120         self.positions.write().unwrap().insert(device_id, position.clone());
121     }
get_position(&self, device_id: &DeviceIdentifier) -> Option<ProtoPosition>122     pub fn get_position(&self, device_id: &DeviceIdentifier) -> Option<ProtoPosition> {
123         self.positions.read().unwrap().get(device_id).cloned()
124     }
set_orientation(&self, device_id: DeviceIdentifier, orientation: &ProtoOrientation)125     pub fn set_orientation(&self, device_id: DeviceIdentifier, orientation: &ProtoOrientation) {
126         self.orientations.write().unwrap().insert(device_id, orientation.clone());
127     }
get_orientation(&self, device_id: &DeviceIdentifier) -> Option<ProtoOrientation>128     pub fn get_orientation(&self, device_id: &DeviceIdentifier) -> Option<ProtoOrientation> {
129         self.orientations.read().unwrap().get(device_id).cloned()
130     }
131 }
132 
133 static DEVICE_MANAGER: OnceLock<Arc<DeviceManager>> = OnceLock::new();
134 
get_manager() -> Arc<DeviceManager>135 fn get_manager() -> Arc<DeviceManager> {
136     DEVICE_MANAGER.get_or_init(|| Arc::new(DeviceManager::new())).clone()
137 }
138 
139 // TODO: last_modified atomic
140 /// The Device resource is a singleton that manages all devices.
141 struct DeviceManager {
142     // BTreeMap allows ListDevice to output devices in order of identifiers.
143     devices: RwLock<BTreeMap<DeviceIdentifier, Device>>,
144     ids: AtomicU32,
145     last_modified: RwLock<Duration>,
146 }
147 
148 impl DeviceManager {
new() -> Self149     fn new() -> Self {
150         DeviceManager {
151             devices: RwLock::new(BTreeMap::new()),
152             ids: AtomicU32::new(INITIAL_DEVICE_ID),
153             last_modified: RwLock::new(
154                 SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards"),
155             ),
156         }
157     }
158 
next_id(&self) -> DeviceIdentifier159     fn next_id(&self) -> DeviceIdentifier {
160         DeviceIdentifier(self.ids.fetch_add(1, Ordering::SeqCst))
161     }
162 
update_timestamp(&self)163     fn update_timestamp(&self) {
164         *self.last_modified.write().unwrap() =
165             SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards");
166     }
167 
168     /// Get or create a device.
169     /// Returns a (device_id, device_name) pair.
get_or_create_device( &self, guid: Option<&str>, name: Option<&str>, builtin: bool, device_info: ProtoDeviceInfo, ) -> (DeviceIdentifier, String)170     fn get_or_create_device(
171         &self,
172         guid: Option<&str>,
173         name: Option<&str>,
174         builtin: bool,
175         device_info: ProtoDeviceInfo,
176     ) -> (DeviceIdentifier, String) {
177         // Hold a lock while checking and updating devices.
178         let mut guard = self.devices.write().unwrap();
179         // Check if a device with the same guid already exists and if so, return it
180         if let Some(guid) = guid {
181             if let Some(existing_device) = guard.values().find(|d| d.guid == *guid) {
182                 if existing_device.builtin != builtin {
183                     warn!("builtin mismatch for device {} during add_chip", existing_device.name);
184                 }
185                 return (existing_device.id, existing_device.name.clone());
186             }
187         }
188         // A new device needs to be created and inserted
189         let id = self.next_id();
190         let default = format!("device-{}", id);
191         let name = name.unwrap_or(&default);
192         guard.insert(id, Device::new(id, guid.unwrap_or(&default), name, builtin));
193         drop(guard);
194         // Update last modified timestamp for devices
195         self.update_timestamp();
196         let device_stats = ProtoDeviceStats {
197             device_id: Some(id.0),
198             kind: Some(device_info.kind).filter(|s| !s.is_empty()),
199             version: Some(device_info.version).filter(|s| !s.is_empty()),
200             sdk_version: Some(device_info.sdk_version).filter(|s| !s.is_empty()),
201             variant: Some(device_info.variant).filter(|s| !s.is_empty()),
202             build_id: Some(device_info.build_id).filter(|s| !s.is_empty()),
203             arch: Some(device_info.arch).filter(|s| !s.is_empty()),
204             ..Default::default()
205         };
206         let event =
207             Event::DeviceAdded(DeviceAdded { id, name: String::from(name), builtin, device_stats });
208         events::publish(event);
209         (id, String::from(name))
210     }
211 }
212 
213 /// Returns a Result<AddChipResult, String> after adding chip to resource.
214 /// add_chip is called by the transport layer when a new chip is attached.
215 ///
216 /// The guid is a transport layer identifier for the device (host:port)
217 /// that is adding the chip.
218 ///
219 /// TODO: Replace the parameter of add_chip with a single protobuf
add_chip( device_guid: &str, device_name: &str, chip_create_params: &chip::CreateParams, wireless_create_params: &wireless::CreateParam, device_info: ProtoDeviceInfo, ) -> Result<AddChipResult, String>220 pub fn add_chip(
221     device_guid: &str,
222     device_name: &str,
223     chip_create_params: &chip::CreateParams,
224     wireless_create_params: &wireless::CreateParam,
225     device_info: ProtoDeviceInfo,
226 ) -> Result<AddChipResult, String> {
227     let chip_kind = chip_create_params.kind;
228     let manager = get_manager();
229     let (device_id, _) = manager.get_or_create_device(
230         Some(device_guid),
231         Some(device_name),
232         chip_kind == ProtoChipKind::BLUETOOTH_BEACON,
233         device_info,
234     );
235     get_pose_manager().add(device_id);
236 
237     // Create
238     let chip_id = chip::next_id();
239     let wireless_adaptor = wireless::new(wireless_create_params, chip_id);
240 
241     // This is infrequent, so we can afford to do another lookup for the device.
242     let _ = manager
243         .devices
244         .write()
245         .unwrap()
246         .get_mut(&device_id)
247         .ok_or(format!("Device not found for device_id: {}", device_id))?
248         .add_chip(chip_create_params, chip_id, wireless_adaptor);
249 
250     // Update last modified timestamp for devices
251     manager.update_timestamp();
252 
253     // Update Capture resource
254     let event = Event::ChipAdded(ChipAdded {
255         chip_id,
256         chip_kind,
257         device_name: device_name.to_string(),
258         builtin: chip_kind == ProtoChipKind::BLUETOOTH_BEACON,
259     });
260     events::publish(event);
261     Ok(AddChipResult { device_id, chip_id })
262 }
263 
264 /// AddChipResult for C++ to handle
265 pub struct AddChipResultCxx {
266     device_id: u32,
267     chip_id: u32,
268     is_error: bool,
269 }
270 
271 impl AddChipResultCxx {
get_device_id(&self) -> u32272     pub fn get_device_id(&self) -> u32 {
273         self.device_id
274     }
275 
get_chip_id(&self) -> u32276     pub fn get_chip_id(&self) -> u32 {
277         self.chip_id
278     }
279 
is_error(&self) -> bool280     pub fn is_error(&self) -> bool {
281         self.is_error
282     }
283 }
284 
285 /// An AddChip function for Rust Device API.
286 /// The backend gRPC code will be invoking this method.
287 #[allow(clippy::too_many_arguments)]
add_chip_cxx( device_guid: &str, device_name: &str, chip_kind: &CxxString, chip_address: &str, chip_name: &str, chip_manufacturer: &str, chip_product_name: &str, bt_properties: &CxxVector<u8>, kind: &str, version: &str, sdk_version: &str, build_id: &str, variant: &str, arch: &str, ) -> Box<AddChipResultCxx>288 pub fn add_chip_cxx(
289     device_guid: &str,
290     device_name: &str,
291     chip_kind: &CxxString,
292     chip_address: &str,
293     chip_name: &str,
294     chip_manufacturer: &str,
295     chip_product_name: &str,
296     bt_properties: &CxxVector<u8>,
297     kind: &str,
298     version: &str,
299     sdk_version: &str,
300     build_id: &str,
301     variant: &str,
302     arch: &str,
303 ) -> Box<AddChipResultCxx> {
304     let _bt_properties_proto = Controller::parse_from_bytes(bt_properties.as_slice());
305     #[cfg(not(test))]
306     let (chip_kind_enum, wireless_create_param) = match chip_kind.to_string().as_str() {
307         "BLUETOOTH" => (
308             ProtoChipKind::BLUETOOTH,
309             wireless::CreateParam::Bluetooth(wireless::bluetooth::CreateParams {
310                 address: chip_address.to_string(),
311                 bt_properties: _bt_properties_proto
312                     .as_ref()
313                     .map_or(None, |p| Some(MessageField::some(p.clone()))),
314             }),
315         ),
316         "WIFI" => {
317             (ProtoChipKind::WIFI, wireless::CreateParam::Wifi(wireless::wifi::CreateParams {}))
318         }
319         "UWB" => (
320             ProtoChipKind::UWB,
321             wireless::CreateParam::Uwb(wireless::uwb::CreateParams {
322                 address: chip_address.to_string(),
323             }),
324         ),
325         _ => {
326             return Box::new(AddChipResultCxx {
327                 device_id: u32::MAX,
328                 chip_id: u32::MAX,
329                 is_error: true,
330             })
331         }
332     };
333     #[cfg(test)]
334     let (chip_kind_enum, wireless_create_param) = match chip_kind.to_string().as_str() {
335         "BLUETOOTH" => (
336             ProtoChipKind::BLUETOOTH,
337             wireless::CreateParam::Mock(wireless::mocked::CreateParams {
338                 chip_kind: ProtoChipKind::BLUETOOTH,
339             }),
340         ),
341         "WIFI" => (
342             ProtoChipKind::WIFI,
343             wireless::CreateParam::Mock(wireless::mocked::CreateParams {
344                 chip_kind: ProtoChipKind::WIFI,
345             }),
346         ),
347         "UWB" => (
348             ProtoChipKind::UWB,
349             wireless::CreateParam::Mock(wireless::mocked::CreateParams {
350                 chip_kind: ProtoChipKind::UWB,
351             }),
352         ),
353         _ => {
354             return Box::new(AddChipResultCxx {
355                 device_id: u32::MAX,
356                 chip_id: u32::MAX,
357                 is_error: true,
358             })
359         }
360     };
361     let chip_create_params = chip::CreateParams {
362         kind: chip_kind_enum,
363         address: chip_address.to_string(),
364         name: if chip_name.is_empty() { None } else { Some(chip_name.to_string()) },
365         manufacturer: chip_manufacturer.to_string(),
366         product_name: chip_product_name.to_string(),
367     };
368     let device_info = ProtoDeviceInfo {
369         kind: kind.to_string(),
370         version: version.to_string(),
371         sdk_version: sdk_version.to_string(),
372         build_id: build_id.to_string(),
373         variant: variant.to_string(),
374         arch: arch.to_string(),
375         ..Default::default()
376     };
377 
378     match add_chip(
379         device_guid,
380         device_name,
381         &chip_create_params,
382         &wireless_create_param,
383         device_info,
384     ) {
385         Ok(result) => Box::new(AddChipResultCxx {
386             device_id: result.device_id.0,
387             chip_id: result.chip_id.0,
388             is_error: false,
389         }),
390         Err(_) => {
391             Box::new(AddChipResultCxx { device_id: u32::MAX, chip_id: u32::MAX, is_error: true })
392         }
393     }
394 }
395 
396 /// Remove a chip from a device.
397 ///
398 /// Called when the packet transport for the chip shuts down.
remove_chip(device_id: DeviceIdentifier, chip_id: ChipIdentifier) -> Result<(), String>399 pub fn remove_chip(device_id: DeviceIdentifier, chip_id: ChipIdentifier) -> Result<(), String> {
400     let manager = get_manager();
401     let mut guard = manager.devices.write().unwrap();
402     let device =
403         guard.get(&device_id).ok_or(format!("RemoveChip device id {device_id} not found"))?;
404     let radio_stats = device.remove_chip(&chip_id)?;
405 
406     let mut device_id_to_remove = None;
407     if device.chips.read().unwrap().is_empty() {
408         device_id_to_remove = Some(device_id);
409         let device = guard
410             .remove(&device_id)
411             .ok_or(format!("RemoveChip device id {device_id} not found"))?;
412         events::publish(Event::DeviceRemoved(DeviceRemoved {
413             id: device.id,
414             name: device.name,
415             builtin: device.builtin,
416         }));
417     }
418 
419     let remaining_nonbuiltin_devices = guard.values().filter(|device| !device.builtin).count();
420     drop(guard);
421 
422     if let Some(device_id) = device_id_to_remove {
423         get_pose_manager().remove(&device_id);
424     }
425 
426     events::publish(Event::ChipRemoved(ChipRemoved {
427         chip_id,
428         device_id,
429         remaining_nonbuiltin_devices,
430         radio_stats,
431     }));
432 
433     manager.update_timestamp();
434     Ok(())
435 }
436 
delete_chip(request: &DeleteChipRequest) -> Result<(), String>437 pub fn delete_chip(request: &DeleteChipRequest) -> Result<(), String> {
438     let chip_id = ChipIdentifier(request.id);
439 
440     let device_id = get_manager()
441         .devices
442         .read()
443         .unwrap()
444         .iter()
445         .find(|(_, device)| device.chips.read().unwrap().contains_key(&chip_id))
446         .map(|(id, _)| *id)
447         .ok_or(format!("failed to delete chip: could not find chip with id {}", request.id))?;
448 
449     remove_chip(device_id, chip_id)
450 }
451 
452 /// A RemoveChip function for Rust Device API.
453 /// The backend gRPC code will be invoking this method.
remove_chip_cxx(device_id: u32, chip_id: u32)454 pub fn remove_chip_cxx(device_id: u32, chip_id: u32) {
455     let _ = remove_chip(DeviceIdentifier(device_id), ChipIdentifier(chip_id));
456 }
457 
458 /// Create a device from a CreateDeviceRequest.
459 /// Uses a default name if none is provided.
460 /// Returns an error if the device already exists.
create_device(create_device_request: &CreateDeviceRequest) -> Result<ProtoDevice, String>461 pub fn create_device(create_device_request: &CreateDeviceRequest) -> Result<ProtoDevice, String> {
462     let new_device = &create_device_request.device;
463     let manager = get_manager();
464     // Check if specified device name is already mapped.
465     if new_device.name != String::default()
466         && manager.devices.read().unwrap().values().any(|d| d.guid == new_device.name)
467     {
468         return Err(String::from("failed to create device: device already exists"));
469     }
470 
471     if new_device.chips.is_empty() {
472         return Err(String::from("failed to create device: device must contain at least 1 chip"));
473     }
474     new_device.chips.iter().try_for_each(|chip| match chip.chip {
475         Some(ProtoBuiltin::BleBeacon(_)) => Ok(()),
476         Some(_) => Err(format!("failed to create device: chip {} was not a built-in", chip.name)),
477         None => Err(format!("failed to create device: chip {} was missing a radio", chip.name)),
478     })?;
479 
480     let device_name = (new_device.name != String::default()).then_some(new_device.name.as_str());
481     let device_info =
482         ProtoDeviceInfo { kind: "BLUETOOTH_BEACON".to_string(), ..Default::default() };
483     let (device_id, device_name) =
484         manager.get_or_create_device(device_name, device_name, true, device_info.clone());
485 
486     new_device.chips.iter().try_for_each(|chip| {
487         {
488             let chip_create_params = chip::CreateParams {
489                 kind: chip.kind.enum_value_or_default(),
490                 address: chip.address.clone(),
491                 name: if chip.name.is_empty() { None } else { Some(chip.name.to_string()) },
492                 manufacturer: chip.manufacturer.clone(),
493                 product_name: chip.product_name.clone(),
494             };
495             let wireless_create_params =
496                 wireless::CreateParam::BleBeacon(wireless::ble_beacon::CreateParams {
497                     device_name: device_name.clone(),
498                     chip_proto: chip.clone(),
499                 });
500 
501             add_chip(
502                 &device_name,
503                 &device_name,
504                 &chip_create_params,
505                 &wireless_create_params,
506                 device_info.clone(),
507             )
508         }
509         .map(|_| ())
510     })?;
511 
512     let manager = get_manager();
513     let guard = manager.devices.read().unwrap();
514     let device = guard.get(&device_id).expect("could not find test bluetooth beacon device");
515     let device_proto = device.get(get_pose_manager())?;
516     Ok(device_proto)
517 }
518 
519 struct ProtoChipDisplay(ProtoChip);
520 
521 // Due to the low readability of debug formatter for ProtoChip, we implemented our own fmt.
522 impl std::fmt::Display for ProtoChipDisplay {
fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result523     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
524         let chip = &self.0;
525         if let Ok(kind) = chip.kind.enum_value() {
526             match kind {
527                 ProtoChipKind::BLUETOOTH => {
528                     chip.bt().low_energy.clone().map(|v| {
529                         write!(
530                             f,
531                             "{{ id: {}, kind: BLUETOOTH_LOW_ENERGY, state: {:?} }}",
532                             self.0.id, v.state
533                         )
534                     });
535                     chip.bt().classic.clone().map(|v| {
536                         write!(
537                             f,
538                             "{{ id: {}, kind: BLUETOOTH_CLASSIC, state: {:?} }}",
539                             chip.id, v.state
540                         )
541                     });
542                 }
543                 ProtoChipKind::BLUETOOTH_BEACON => {
544                     chip.ble_beacon().bt.low_energy.clone().map(|v| {
545                         write!(f, "{{ id: {}, kind: BLE_BEACON, state: {:?} }}", chip.id, v.state)
546                     });
547                     chip.ble_beacon().bt.classic.clone().map(|v| {
548                         write!(
549                             f,
550                             "{{ id: {}, kind: BLUETOOTH_CLASSIC_BEACON, state: {:?} }}",
551                             chip.id, v.state
552                         )
553                     });
554                 }
555                 ProtoChipKind::WIFI => {
556                     write!(f, "{{ id: {}, kind: WIFI, state: {:?} }}", chip.id, chip.wifi().state)?
557                 }
558                 ProtoChipKind::UWB => {
559                     write!(f, "{{ id: {}, kind: UWB, state: {:?} }}", chip.id, chip.uwb().state)?
560                 }
561                 _ => (),
562             }
563         }
564         Ok(())
565     }
566 }
567 
568 struct PatchDeviceFieldsDisplay(DeviceIdentifier, ProtoPatchDeviceFields);
569 
570 // Due to the low readability of debug formatter for ProtoPatchDeviceFields, we implemented our own fmt.
571 impl std::fmt::Display for PatchDeviceFieldsDisplay {
fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result572     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
573         write!(f, "PatchDevice: ")?;
574         let mut fields = Vec::<String>::new();
575         fields.push(format!("id: {}", self.0));
576         if let Some(name) = &self.1.name {
577             fields.push(format!("name: {}", name));
578         }
579         if let Some(visible) = &self.1.visible {
580             fields.push(format!("visible: {}", visible));
581         }
582         if let Some(position) = &self.1.position.0 {
583             fields.push(format!("position: {{ {} }}", position));
584         }
585         if let Some(orientation) = &self.1.orientation.0 {
586             fields.push(format!("orientation: {{ {} }}", orientation));
587         }
588         if !self.1.chips.is_empty() {
589             let mut chip_field = Vec::<String>::new();
590             for chip in &self.1.chips {
591                 chip_field.push(format!("{}", ProtoChipDisplay(chip.clone())));
592             }
593             fields.push(format!("chips: {{ {} }}", chip_field.join(", ")));
594         }
595         write!(f, "{}", fields.join(", "))
596     }
597 }
598 
599 // lock the devices, find the id and call the patch function
patch_device(patch_device_request: PatchDeviceRequest) -> Result<(), String>600 pub fn patch_device(patch_device_request: PatchDeviceRequest) -> Result<(), String> {
601     let manager = get_manager();
602     let proto_device = patch_device_request
603         .device
604         .into_option()
605         .ok_or("Missing PatchDevice in PatchDeviceRequest".to_string())?;
606     match (patch_device_request.id, proto_device.name.clone()) {
607         (Some(id), _) => {
608             let id = DeviceIdentifier(id);
609             match manager.devices.read().unwrap().get(&id) {
610                 Some(device) => {
611                     let result = device.patch(&proto_device, get_pose_manager());
612                     let name = device.name.clone();
613                     if result.is_ok() {
614                         // Update last modified timestamp for manager
615                         manager.update_timestamp();
616 
617                         // Log patched fields
618                         log::info!("{}", PatchDeviceFieldsDisplay(id, proto_device));
619 
620                         // Publish Device Patched event
621                         events::publish(Event::DevicePatched(DevicePatched { id, name }));
622                     }
623                     result
624                 }
625                 None => Err(format!("No such device with id {id}")),
626             }
627         }
628         (_, Some(name)) => {
629             let mut multiple_matches = false;
630             let mut target: Option<&Device> = None;
631             let devices = manager.devices.read().unwrap();
632             for device in devices.values() {
633                 if device.name.contains(&name) {
634                     if device.name == name {
635                         let result = device.patch(&proto_device, get_pose_manager());
636                         let id = device.id;
637                         let name = device.name.clone();
638                         if result.is_ok() {
639                             // Update last modified timestamp for manager
640                             manager.update_timestamp();
641 
642                             // Log patched fields
643                             log::info!("{}", PatchDeviceFieldsDisplay(id, proto_device));
644 
645                             // Publish Device Patched event
646                             events::publish(Event::DevicePatched(DevicePatched { id, name }));
647                         }
648                         return result;
649                     }
650                     multiple_matches = target.is_some();
651                     target = Some(device);
652                 }
653             }
654             if multiple_matches {
655                 return Err(format!(
656                     "Multiple ambiguous matches were found with substring {}",
657                     name
658                 ));
659             }
660             match target {
661                 Some(device) => {
662                     let result = device.patch(&proto_device, get_pose_manager());
663                     let id = device.id;
664                     let name = device.name.clone();
665                     if result.is_ok() {
666                         // Update last modified timestamp for devices
667                         manager.update_timestamp();
668 
669                         // Log patched fields
670                         log::info!("{}", PatchDeviceFieldsDisplay(id, proto_device));
671 
672                         // Publish Device Patched event
673                         events::publish(Event::DevicePatched(DevicePatched { id, name }));
674                     }
675                     result
676                 }
677                 None => Err(format!("No such device with name {}", name)),
678             }
679         }
680         (_, _) => Err("Both id and name are not provided".to_string()),
681     }
682 }
683 
684 // Parse from input json string to proto
patch_device_json(id_option: Option<DeviceIdentifier>, patch_json: &str) -> Result<(), String>685 fn patch_device_json(id_option: Option<DeviceIdentifier>, patch_json: &str) -> Result<(), String> {
686     let mut patch_device_request = PatchDeviceRequest::new();
687     if merge_from_str(&mut patch_device_request, patch_json).is_ok() {
688         if patch_device_request.id.is_none() {
689             patch_device_request.id = id_option.map(|id| id.0);
690         }
691         patch_device(patch_device_request)
692     } else {
693         Err(format!("Incorrect format of patch json {}", patch_json))
694     }
695 }
696 
distance(a: &ProtoPosition, b: &ProtoPosition) -> f32697 fn distance(a: &ProtoPosition, b: &ProtoPosition) -> f32 {
698     ((b.x - a.x).powf(2.0) + (b.y - a.y).powf(2.0) + (b.z - a.z).powf(2.0)).sqrt()
699 }
700 
701 #[allow(dead_code)]
get_distance(id: &ChipIdentifier, other_id: &ChipIdentifier) -> Result<f32, String>702 fn get_distance(id: &ChipIdentifier, other_id: &ChipIdentifier) -> Result<f32, String> {
703     let device_id = crate::devices::chip::get_chip(id)
704         .ok_or(format!("No such device with chip_id {id}"))?
705         .device_id;
706     let other_device_id = crate::devices::chip::get_chip(other_id)
707         .ok_or(format!("No such device with chip_id {other_id}"))?
708         .device_id;
709 
710     let pose_manager = get_pose_manager();
711     let a = pose_manager
712         .get_position(&device_id)
713         .ok_or(format!("no position for device {device_id}"))?;
714     let b = pose_manager
715         .get_position(&other_device_id)
716         .ok_or(format!("no position for device {other_device_id}"))?;
717     Ok(distance(&a, &b))
718 }
719 
720 /// A GetDistance function for Rust Device API.
721 /// The backend gRPC code will be invoking this method.
get_distance_cxx(a: u32, b: u32) -> f32722 pub fn get_distance_cxx(a: u32, b: u32) -> f32 {
723     match get_distance(&ChipIdentifier(a), &ChipIdentifier(b)) {
724         Ok(distance) => distance,
725         Err(err) => {
726             warn!("get_distance Error: {err}");
727             0.0
728         }
729     }
730 }
731 
732 /// Function to obtain ProtoDevice given a ChipIdentifier
get_device(chip_id: &ChipIdentifier) -> anyhow::Result<netsim_proto::model::Device>733 pub fn get_device(chip_id: &ChipIdentifier) -> anyhow::Result<netsim_proto::model::Device> {
734     let device_id = match chip::get_chip(chip_id) {
735         Some(chip) => chip.device_id,
736         None => return Err(anyhow::anyhow!("Can't find chip for chip_id: {chip_id}")),
737     };
738     get_manager()
739         .devices
740         .read()
741         .unwrap()
742         .get(&device_id)
743         .ok_or(anyhow::anyhow!("Can't find device for device_id: {device_id}"))?
744         .get(get_pose_manager())
745         .map_err(|e| anyhow::anyhow!("{e:?}"))
746 }
747 
reset_all() -> Result<(), String>748 pub fn reset_all() -> Result<(), String> {
749     let manager = get_manager();
750     // Perform reset for all manager
751     let mut device_ids = Vec::new();
752     for device in manager.devices.read().unwrap().values() {
753         device.reset()?;
754         device_ids.push(device.id);
755     }
756     for device_id in device_ids {
757         get_pose_manager().reset(device_id);
758     }
759     // Update last modified timestamp for manager
760     manager.update_timestamp();
761     events::publish(Event::DeviceReset);
762     Ok(())
763 }
764 
handle_device_create(writer: ResponseWritable, create_json: &str)765 fn handle_device_create(writer: ResponseWritable, create_json: &str) {
766     let mut response = CreateDeviceResponse::new();
767 
768     let mut get_result = || {
769         let mut create_device_request = CreateDeviceRequest::new();
770         merge_from_str(&mut create_device_request, create_json)
771             .map_err(|_| format!("create device: invalid json: {}", create_json))?;
772         let device_proto = create_device(&create_device_request)?;
773         response.device = MessageField::some(device_proto);
774         print_to_string(&response).map_err(|_| String::from("failed to convert device to json"))
775     };
776 
777     match get_result() {
778         Ok(response) => writer.put_ok("text/json", &response, vec![]),
779         Err(err) => writer.put_error(404, err.as_str()),
780     }
781 }
782 
783 /// Performs PatchDevice to patch a single device
handle_device_patch(writer: ResponseWritable, id: Option<DeviceIdentifier>, patch_json: &str)784 fn handle_device_patch(writer: ResponseWritable, id: Option<DeviceIdentifier>, patch_json: &str) {
785     match patch_device_json(id, patch_json) {
786         Ok(()) => writer.put_ok("text/plain", "Device Patch Success", vec![]),
787         Err(err) => writer.put_error(404, err.as_str()),
788     }
789 }
790 
handle_chip_delete(writer: ResponseWritable, delete_json: &str)791 fn handle_chip_delete(writer: ResponseWritable, delete_json: &str) {
792     let get_result = || {
793         let mut delete_chip_request = DeleteChipRequest::new();
794         merge_from_str(&mut delete_chip_request, delete_json)
795             .map_err(|_| format!("delete chip: invalid json: {}", delete_json))?;
796         delete_chip(&delete_chip_request)
797     };
798 
799     match get_result() {
800         Ok(()) => writer.put_ok("text/plain", "Chip Delete Success", vec![]),
801         Err(err) => writer.put_error(404, err.as_str()),
802     }
803 }
804 
list_device() -> anyhow::Result<ListDeviceResponse, String>805 pub fn list_device() -> anyhow::Result<ListDeviceResponse, String> {
806     // Instantiate ListDeviceResponse and add DeviceManager
807     let mut response = ListDeviceResponse::new();
808     let manager = get_manager();
809 
810     for device in manager.devices.read().unwrap().values() {
811         if let Ok(device_proto) = device.get(get_pose_manager()) {
812             response.devices.push(device_proto);
813         }
814     }
815 
816     // Add Last Modified Timestamp into ListDeviceResponse
817     response.last_modified = Some(Timestamp {
818         seconds: manager.last_modified.read().unwrap().as_secs() as i64,
819         nanos: manager.last_modified.read().unwrap().subsec_nanos() as i32,
820         ..Default::default()
821     })
822     .into();
823     Ok(response)
824 }
825 
826 /// Performs ListDevices to get the list of DeviceManager and write to writer.
handle_device_list(writer: ResponseWritable)827 fn handle_device_list(writer: ResponseWritable) {
828     let response = list_device().unwrap();
829     // Perform protobuf-json-mapping with the given protobuf
830     if let Ok(json_response) = print_to_string_with_options(&response, &JSON_PRINT_OPTION) {
831         writer.put_ok("text/json", &json_response, vec![])
832     } else {
833         writer.put_error(404, "proto to JSON mapping failure")
834     }
835 }
836 
837 /// Performs ResetDevice for all devices
handle_device_reset(writer: ResponseWritable)838 fn handle_device_reset(writer: ResponseWritable) {
839     match reset_all() {
840         Ok(()) => writer.put_ok("text/plain", "Device Reset Success", vec![]),
841         Err(err) => writer.put_error(404, err.as_str()),
842     }
843 }
844 
845 /// Performs SubscribeDevice
handle_device_subscribe(writer: ResponseWritable, subscribe_json: &str)846 fn handle_device_subscribe(writer: ResponseWritable, subscribe_json: &str) {
847     // Check if the provided last_modified timestamp is prior to the current last_modified
848     let mut subscribe_device_request = SubscribeDeviceRequest::new();
849     if merge_from_str(&mut subscribe_device_request, subscribe_json).is_ok() {
850         let timestamp_proto = subscribe_device_request.last_modified;
851         let provided_last_modified =
852             Duration::new(timestamp_proto.seconds as u64, timestamp_proto.nanos as u32);
853         if provided_last_modified < *get_manager().last_modified.read().unwrap() {
854             info!("Immediate return for SubscribeDevice");
855             handle_device_list(writer);
856             return;
857         }
858     }
859 
860     let event_rx = events::subscribe();
861     // Timeout after 15 seconds with no event received
862     match event_rx.recv_timeout(Duration::from_secs(15)) {
863         Ok(Event::DeviceAdded(_))
864         | Ok(Event::DeviceRemoved(_))
865         | Ok(Event::ChipAdded(_))
866         | Ok(Event::ChipRemoved(_))
867         | Ok(Event::DevicePatched(_))
868         | Ok(Event::DeviceReset) => handle_device_list(writer),
869         Err(err) => writer.put_error(404, format!("{err:?}").as_str()),
870         _ => writer.put_error(404, "disconnecting due to unrelated event"),
871     }
872 }
873 
874 /// The Rust device handler used directly by Http frontend or handle_device_cxx for LIST, GET, and PATCH
handle_device(request: &Request<Vec<u8>>, param: &str, writer: ResponseWritable)875 pub fn handle_device(request: &Request<Vec<u8>>, param: &str, writer: ResponseWritable) {
876     // Route handling
877     if request.uri() == "/v1/devices" {
878         // Routes with ID not specified
879         match request.method().as_str() {
880             "GET" => {
881                 handle_device_list(writer);
882             }
883             "PUT" => {
884                 handle_device_reset(writer);
885             }
886             "SUBSCRIBE" => {
887                 let body = request.body();
888                 let subscribe_json = String::from_utf8(body.to_vec()).unwrap();
889                 handle_device_subscribe(writer, subscribe_json.as_str());
890             }
891             "PATCH" => {
892                 let body = request.body();
893                 let patch_json = String::from_utf8(body.to_vec()).unwrap();
894                 handle_device_patch(writer, None, patch_json.as_str());
895             }
896             "POST" => {
897                 let body = &request.body();
898                 let create_json = String::from_utf8(body.to_vec()).unwrap();
899                 handle_device_create(writer, create_json.as_str());
900             }
901             "DELETE" => {
902                 let body = &request.body();
903                 let delete_json = String::from_utf8(body.to_vec()).unwrap();
904                 handle_chip_delete(writer, delete_json.as_str());
905             }
906             _ => writer.put_error(404, "Not found."),
907         }
908     } else {
909         // Routes with ID specified
910         match request.method().as_str() {
911             "PATCH" => {
912                 let id = match param.parse::<u32>() {
913                     Ok(num) => DeviceIdentifier(num),
914                     Err(_) => {
915                         writer.put_error(404, "Incorrect Id type for devices, ID should be u32.");
916                         return;
917                     }
918                 };
919                 let body = request.body();
920                 let patch_json = String::from_utf8(body.to_vec()).unwrap();
921                 handle_device_patch(writer, Some(id), patch_json.as_str());
922             }
923             _ => writer.put_error(404, "Not found."),
924         }
925     }
926 }
927 
928 /// Device handler cxx for grpc server to call
handle_device_cxx( responder: Pin<&mut CxxServerResponseWriter>, method: String, param: String, body: String, )929 pub fn handle_device_cxx(
930     responder: Pin<&mut CxxServerResponseWriter>,
931     method: String,
932     param: String,
933     body: String,
934 ) {
935     let mut builder = Request::builder().method(method.as_str());
936     if param.is_empty() {
937         builder = builder.uri("/v1/devices");
938     } else {
939         builder = builder.uri(format!("/v1/devices/{}", param));
940     }
941     builder = builder.version(Version::HTTP_11);
942     let request = match builder.body(body.as_bytes().to_vec()) {
943         Ok(request) => request,
944         Err(err) => {
945             warn!("{err:?}");
946             return;
947         }
948     };
949     handle_device(
950         &request,
951         param.as_str(),
952         &mut CxxServerResponseWriterWrapper { writer: responder },
953     )
954 }
955 
956 /// return enum type for check_device_event
957 #[derive(Debug, PartialEq)]
958 enum DeviceWaitStatus {
959     LastDeviceRemoved,
960     DeviceAdded,
961     Timeout,
962     IgnoreEvent,
963 }
964 
965 /// listening to events
check_device_event( events_rx: &Receiver<Event>, timeout_time: Option<Instant>, ) -> DeviceWaitStatus966 fn check_device_event(
967     events_rx: &Receiver<Event>,
968     timeout_time: Option<Instant>,
969 ) -> DeviceWaitStatus {
970     let wait_time = timeout_time.map_or(Duration::from_secs(u64::MAX), |t| t - Instant::now());
971     match events_rx.recv_timeout(wait_time) {
972         Ok(Event::ChipRemoved(ChipRemoved { remaining_nonbuiltin_devices: 0, .. })) => {
973             DeviceWaitStatus::LastDeviceRemoved
974         }
975         // DeviceAdded (event from CreateDevice)
976         // ChipAdded (event from add_chip or add_chip_cxx)
977         Ok(Event::DeviceAdded(DeviceAdded { builtin: false, .. }))
978         | Ok(Event::ChipAdded(ChipAdded { builtin: false, .. })) => DeviceWaitStatus::DeviceAdded,
979         Err(_) => DeviceWaitStatus::Timeout,
980         _ => DeviceWaitStatus::IgnoreEvent,
981     }
982 }
983 
984 /// wait loop logic for devices
985 /// the function will publish a ShutDown event when
986 /// 1. Initial timeout before first device is added
987 /// 2. Last Chip Removed from netsimd
988 ///    this function should NOT be invoked if running in no-shutdown mode
spawn_shutdown_publisher(events_rx: Receiver<Event>)989 pub fn spawn_shutdown_publisher(events_rx: Receiver<Event>) {
990     spawn_shutdown_publisher_with_timeout(events_rx, IDLE_SECS_FOR_SHUTDOWN, events::get_events());
991 }
992 
993 // separate function for testability
spawn_shutdown_publisher_with_timeout( events_rx: Receiver<Event>, timeout_duration_s: u64, events_tx: Arc<Mutex<Events>>, )994 fn spawn_shutdown_publisher_with_timeout(
995     events_rx: Receiver<Event>,
996     timeout_duration_s: u64,
997     events_tx: Arc<Mutex<Events>>,
998 ) {
999     let _ =
1000         std::thread::Builder::new().name("device_event_subscriber".to_string()).spawn(move || {
1001             let publish_event =
1002                 |e: Event| events_tx.lock().expect("Failed to acquire lock on events").publish(e);
1003 
1004             let mut timeout_time = Some(Instant::now() + Duration::from_secs(timeout_duration_s));
1005             loop {
1006                 match check_device_event(&events_rx, timeout_time) {
1007                     DeviceWaitStatus::LastDeviceRemoved => {
1008                         publish_event(Event::ShutDown(ShutDown {
1009                             reason: "last device disconnected".to_string(),
1010                         }));
1011                         return;
1012                     }
1013                     DeviceWaitStatus::DeviceAdded => {
1014                         timeout_time = None;
1015                     }
1016                     DeviceWaitStatus::Timeout => {
1017                         publish_event(Event::ShutDown(ShutDown {
1018                             reason: format!(
1019                                 "no devices connected within {IDLE_SECS_FOR_SHUTDOWN}s"
1020                             ),
1021                         }));
1022                         return;
1023                     }
1024                     DeviceWaitStatus::IgnoreEvent => continue,
1025                 }
1026             }
1027         });
1028 }
1029 
1030 /// Return vector containing current radio chip stats from all devices
get_radio_stats() -> Vec<NetsimRadioStats>1031 pub fn get_radio_stats() -> Vec<NetsimRadioStats> {
1032     let mut result: Vec<NetsimRadioStats> = Vec::new();
1033     // TODO: b/309805437 - optimize logic using get_stats for WirelessAdaptor
1034     for (device_id, device) in get_manager().devices.read().unwrap().iter() {
1035         for chip in device.chips.read().unwrap().values() {
1036             for mut radio_stats in chip.get_stats() {
1037                 radio_stats.set_device_id(device_id.0);
1038                 result.push(radio_stats);
1039             }
1040         }
1041     }
1042     result
1043 }
1044 
1045 #[cfg(test)]
1046 mod tests {
1047     use crate::events;
1048     use netsim_common::util::netsim_logger::init_for_test;
1049     use netsim_proto::frontend::patch_device_request::PatchDeviceFields as ProtoPatchDeviceFields;
1050     use netsim_proto::model::{DeviceCreate as ProtoDeviceCreate, Orientation as ProtoOrientation};
1051     use protobuf_json_mapping::print_to_string;
1052     use std::{sync::Once, thread};
1053 
1054     use super::*;
1055 
1056     static TEST_DEVICE_KIND: &str = "TEST_DEVICE";
1057 
1058     // This allows Log init method to be invoked once when running all tests.
1059     static INIT: Once = Once::new();
1060 
1061     /// Logger setup function that is only run once, even if called multiple times.
logger_setup()1062     fn logger_setup() {
1063         INIT.call_once(|| {
1064             init_for_test();
1065         });
1066     }
1067 
1068     /// TestChipParameters struct to invoke add_chip
1069     /// This struct contains parameters required to invoke add_chip.
1070     /// This will eventually be invoked by the facades.
1071     struct TestChipParameters {
1072         device_guid: String,
1073         device_name: String,
1074         chip_kind: ProtoChipKind,
1075         chip_name: String,
1076         chip_manufacturer: String,
1077         chip_product_name: String,
1078         device_info: ProtoDeviceInfo,
1079     }
1080 
1081     impl TestChipParameters {
add_chip(&self) -> Result<AddChipResult, String>1082         fn add_chip(&self) -> Result<AddChipResult, String> {
1083             let chip_create_params = chip::CreateParams {
1084                 kind: self.chip_kind,
1085                 address: "".to_string(),
1086                 name: Some(self.chip_name.clone()),
1087                 manufacturer: self.chip_manufacturer.clone(),
1088                 product_name: self.chip_product_name.clone(),
1089             };
1090             let wireless_create_params =
1091                 wireless::CreateParam::Mock(wireless::mocked::CreateParams {
1092                     chip_kind: self.chip_kind,
1093                 });
1094             super::add_chip(
1095                 &self.device_guid,
1096                 &self.device_name,
1097                 &chip_create_params,
1098                 &wireless_create_params,
1099                 self.device_info.clone(),
1100             )
1101         }
1102 
get_or_create_device(&self) -> DeviceIdentifier1103         fn get_or_create_device(&self) -> DeviceIdentifier {
1104             let manager = get_manager();
1105             manager
1106                 .get_or_create_device(
1107                     Some(&self.device_guid),
1108                     Some(&self.device_name),
1109                     false,
1110                     self.device_info.clone(),
1111                 )
1112                 .0
1113         }
1114     }
1115 
1116     /// helper function for test cases to instantiate ProtoPosition
new_position(x: f32, y: f32, z: f32) -> ProtoPosition1117     fn new_position(x: f32, y: f32, z: f32) -> ProtoPosition {
1118         ProtoPosition { x, y, z, ..Default::default() }
1119     }
1120 
new_orientation(yaw: f32, pitch: f32, roll: f32) -> ProtoOrientation1121     fn new_orientation(yaw: f32, pitch: f32, roll: f32) -> ProtoOrientation {
1122         ProtoOrientation { yaw, pitch, roll, ..Default::default() }
1123     }
1124 
test_chip_1_bt() -> TestChipParameters1125     fn test_chip_1_bt() -> TestChipParameters {
1126         TestChipParameters {
1127             device_guid: format!("guid-fs-1-{:?}", thread::current().id()),
1128             device_name: format!("test-device-name-1-{:?}", thread::current().id()),
1129             chip_kind: ProtoChipKind::BLUETOOTH,
1130             chip_name: "bt_chip_name".to_string(),
1131             chip_manufacturer: "netsim".to_string(),
1132             chip_product_name: "netsim_bt".to_string(),
1133             device_info: ProtoDeviceInfo {
1134                 kind: TEST_DEVICE_KIND.to_string(),
1135                 ..Default::default()
1136             },
1137         }
1138     }
1139 
test_chip_1_wifi() -> TestChipParameters1140     fn test_chip_1_wifi() -> TestChipParameters {
1141         TestChipParameters {
1142             device_guid: format!("guid-fs-1-{:?}", thread::current().id()),
1143             device_name: format!("test-device-name-1-{:?}", thread::current().id()),
1144             chip_kind: ProtoChipKind::WIFI,
1145             chip_name: "wifi_chip_name".to_string(),
1146             chip_manufacturer: "netsim".to_string(),
1147             chip_product_name: "netsim_wifi".to_string(),
1148             device_info: ProtoDeviceInfo {
1149                 kind: TEST_DEVICE_KIND.to_string(),
1150                 ..Default::default()
1151             },
1152         }
1153     }
1154 
test_chip_2_bt() -> TestChipParameters1155     fn test_chip_2_bt() -> TestChipParameters {
1156         TestChipParameters {
1157             device_guid: format!("guid-fs-2-{:?}", thread::current().id()),
1158             device_name: format!("test-device-name-2-{:?}", thread::current().id()),
1159             chip_kind: ProtoChipKind::BLUETOOTH,
1160             chip_name: "bt_chip_name".to_string(),
1161             chip_manufacturer: "netsim".to_string(),
1162             chip_product_name: "netsim_bt".to_string(),
1163             device_info: ProtoDeviceInfo {
1164                 kind: TEST_DEVICE_KIND.to_string(),
1165                 ..Default::default()
1166             },
1167         }
1168     }
1169 
reset(id: DeviceIdentifier) -> Result<(), String>1170     fn reset(id: DeviceIdentifier) -> Result<(), String> {
1171         get_pose_manager().reset(id);
1172 
1173         let manager = get_manager();
1174         let mut devices = manager.devices.write().unwrap();
1175         match devices.get_mut(&id) {
1176             Some(device) => device.reset(),
1177             None => Err(format!("No such device with id {id}")),
1178         }
1179     }
1180 
spawn_shutdown_publisher_test_setup(timeout: u64) -> (Arc<Mutex<Events>>, Receiver<Event>)1181     fn spawn_shutdown_publisher_test_setup(timeout: u64) -> (Arc<Mutex<Events>>, Receiver<Event>) {
1182         let mut events = events::test::new();
1183         let events_rx = events::test::subscribe(&mut events);
1184         spawn_shutdown_publisher_with_timeout(events_rx, timeout, events.clone());
1185 
1186         let events_rx2 = events::test::subscribe(&mut events);
1187 
1188         (events, events_rx2)
1189     }
1190 
1191     #[test]
test_spawn_shutdown_publisher_last_chip_removed()1192     fn test_spawn_shutdown_publisher_last_chip_removed() {
1193         let (mut events, events_rx) = spawn_shutdown_publisher_test_setup(IDLE_SECS_FOR_SHUTDOWN);
1194 
1195         events::test::publish(
1196             &mut events,
1197             Event::ChipRemoved(ChipRemoved {
1198                 remaining_nonbuiltin_devices: 0,
1199                 ..Default::default()
1200             }),
1201         );
1202 
1203         // receive our own ChipRemoved
1204         assert!(matches!(events_rx.recv(), Ok(Event::ChipRemoved(ChipRemoved { .. }))));
1205         // receive the ShutDown emitted by the function under test
1206         assert!(matches!(events_rx.recv(), Ok(Event::ShutDown(ShutDown { .. }))));
1207     }
1208 
1209     #[test]
test_spawn_shutdown_publisher_chip_removed_which_is_not_last_chip()1210     fn test_spawn_shutdown_publisher_chip_removed_which_is_not_last_chip() {
1211         let (mut events, events_rx) = spawn_shutdown_publisher_test_setup(IDLE_SECS_FOR_SHUTDOWN);
1212         events::test::publish(
1213             &mut events,
1214             Event::ChipRemoved(ChipRemoved {
1215                 chip_id: ChipIdentifier(1),
1216                 remaining_nonbuiltin_devices: 1,
1217                 ..Default::default()
1218             }),
1219         );
1220 
1221         // give other thread time to generate a ShutDown if it was going to
1222         std::thread::sleep(std::time::Duration::from_secs(1));
1223 
1224         // only the 2nd ChipRemoved should generate a ShutDown as it is marked the last one
1225         events::test::publish(
1226             &mut events,
1227             Event::ChipRemoved(ChipRemoved {
1228                 chip_id: ChipIdentifier(0),
1229                 remaining_nonbuiltin_devices: 0,
1230                 ..Default::default()
1231             }),
1232         );
1233 
1234         // receive our own ChipRemoved
1235         assert!(matches!(events_rx.recv(), Ok(Event::ChipRemoved(ChipRemoved { .. }))));
1236         // receive our own ChipRemoved (with no shutdown)
1237         assert!(matches!(events_rx.recv(), Ok(Event::ChipRemoved(ChipRemoved { .. }))));
1238         // only then receive the ShutDown emitted by the function under test
1239         assert!(matches!(events_rx.recv(), Ok(Event::ShutDown(ShutDown { .. }))));
1240     }
1241 
1242     #[test]
test_spawn_shutdown_publisher_last_chip_removed_with_duplicate_event()1243     fn test_spawn_shutdown_publisher_last_chip_removed_with_duplicate_event() {
1244         let (mut events, events_rx) = spawn_shutdown_publisher_test_setup(IDLE_SECS_FOR_SHUTDOWN);
1245         events::test::publish(
1246             &mut events,
1247             Event::ChipRemoved(ChipRemoved {
1248                 chip_id: ChipIdentifier(0),
1249                 remaining_nonbuiltin_devices: 0,
1250                 ..Default::default()
1251             }),
1252         );
1253 
1254         // give other thread time to generate a ShutDown if it was going to
1255         std::thread::sleep(std::time::Duration::from_secs(1));
1256 
1257         // this is a duplicate event and we already sent that all chips were removed
1258         // this is for strict comparison with test_spawn_shutdown_publisher_chip_removed_which_is_not_last_chip
1259         // to validate that if the first event has remaining_nonbuiltin_devices 0
1260         // we would receive ChipRemoved, ShutDown, ChipRemoved
1261         // but if first ChipRemoved has remaining_nonbuiltin_devices,
1262         // we instead receive ChipRemoved, ChipRemoved, ShutDown
1263         events::test::publish(
1264             &mut events,
1265             Event::ChipRemoved(ChipRemoved {
1266                 chip_id: ChipIdentifier(0),
1267                 remaining_nonbuiltin_devices: 0,
1268                 ..Default::default()
1269             }),
1270         );
1271 
1272         // receive our own ChipRemoved
1273         assert!(matches!(events_rx.recv(), Ok(Event::ChipRemoved(_))));
1274         // receive the ShutDown emitted by the function under test
1275         assert!(matches!(events_rx.recv(), Ok(Event::ShutDown(_))));
1276         // receive our own erroneous ChipRemoved which occurs after we said all chips were removed
1277         // this is just for strict comparison with test_spawn_shutdown_publisher_chip_removed_which_is_not_last_chip
1278         assert!(matches!(events_rx.recv(), Ok(Event::ChipRemoved(_))));
1279         // should timeout now (no further events as we expect shutdown publisher thread to have stopped)
1280         assert!(events_rx.recv_timeout(Duration::from_secs(2)).is_err());
1281     }
1282 
1283     #[test]
test_spawn_shutdown_publisher_timeout()1284     fn test_spawn_shutdown_publisher_timeout() {
1285         let (_, events_rx) = spawn_shutdown_publisher_test_setup(1u64);
1286 
1287         // receive the ShutDown emitted by the function under test
1288         assert!(matches!(events_rx.recv_timeout(Duration::from_secs(2)), Ok(Event::ShutDown(_))));
1289     }
1290 
1291     #[test]
test_spawn_shutdown_publisher_timeout_is_canceled_if_a_chip_is_added()1292     fn test_spawn_shutdown_publisher_timeout_is_canceled_if_a_chip_is_added() {
1293         let (mut events, events_rx) = spawn_shutdown_publisher_test_setup(1u64);
1294 
1295         events::test::publish(
1296             &mut events,
1297             Event::ChipAdded(ChipAdded {
1298                 chip_id: ChipIdentifier(0),
1299                 chip_kind: ProtoChipKind::BLUETOOTH,
1300                 ..Default::default()
1301             }),
1302         );
1303         assert!(matches!(events_rx.recv(), Ok(Event::ChipAdded(_))));
1304 
1305         // should NO longer receive the ShutDown emitted by the function under test
1306         // based on timeout removed when chip added
1307         assert!(events_rx.recv_timeout(Duration::from_secs(2)).is_err());
1308 
1309         events::test::publish(
1310             &mut events,
1311             Event::ChipRemoved(ChipRemoved {
1312                 chip_id: ChipIdentifier(0),
1313                 remaining_nonbuiltin_devices: 0,
1314                 ..Default::default()
1315             }),
1316         );
1317         // receive our own ChipRemoved
1318         assert!(matches!(events_rx.recv(), Ok(Event::ChipRemoved(_))));
1319         // receive the ShutDown emitted by the function under test
1320         assert!(matches!(events_rx.recv(), Ok(Event::ShutDown(_))));
1321     }
1322 
1323     #[test]
test_distance()1324     fn test_distance() {
1325         // Pythagorean quadruples
1326         let a = new_position(0.0, 0.0, 0.0);
1327         let mut b = new_position(1.0, 2.0, 2.0);
1328         assert_eq!(distance(&a, &b), 3.0);
1329         b = new_position(2.0, 3.0, 6.0);
1330         assert_eq!(distance(&a, &b), 7.0);
1331     }
1332 
1333     #[test]
test_add_chip()1334     fn test_add_chip() {
1335         // Initializing Logger
1336         logger_setup();
1337 
1338         // Adding a chip
1339         let chip_params = test_chip_1_bt();
1340         let chip_result = chip_params.add_chip().unwrap();
1341         match get_manager().devices.read().unwrap().get(&chip_result.device_id) {
1342             Some(device) => {
1343                 let chips = device.chips.read().unwrap();
1344                 let chip = chips.get(&chip_result.chip_id).unwrap();
1345                 assert_eq!(chip_params.chip_kind, chip.kind);
1346                 assert_eq!(
1347                     chip_params.chip_manufacturer,
1348                     chip.manufacturer.read().unwrap().to_string()
1349                 );
1350                 assert_eq!(chip_params.chip_name, chip.name);
1351                 assert_eq!(
1352                     chip_params.chip_product_name,
1353                     chip.product_name.read().unwrap().to_string()
1354                 );
1355                 assert_eq!(chip_params.device_name, device.name);
1356             }
1357             None => unreachable!(),
1358         }
1359     }
1360 
1361     #[test]
test_get_or_create_device()1362     fn test_get_or_create_device() {
1363         // Initializing Logger
1364         logger_setup();
1365 
1366         // Creating a device and getting device
1367         let bt_chip_params = test_chip_1_bt();
1368         let device_id_1 = bt_chip_params.get_or_create_device();
1369         let wifi_chip_params = test_chip_1_wifi();
1370         let device_id_2 = wifi_chip_params.get_or_create_device();
1371         assert_eq!(device_id_1, device_id_2);
1372         assert!(get_manager().devices.read().unwrap().get(&device_id_1).is_some())
1373     }
1374 
1375     #[test]
test_patch_device_json()1376     fn test_patch_device_json() {
1377         // Initializing Logger
1378         logger_setup();
1379 
1380         // Patching device position and orientation by id
1381         let chip_params = test_chip_1_bt();
1382         let chip_result = chip_params.add_chip().unwrap();
1383         let mut patch_device_request = PatchDeviceRequest::new();
1384         let mut proto_device = ProtoPatchDeviceFields::new();
1385         let request_position = new_position(1.1, 2.2, 3.3);
1386         let request_orientation = new_orientation(4.4, 5.5, 6.6);
1387         proto_device.name = Some(chip_params.device_name);
1388         proto_device.visible = Some(false);
1389         proto_device.position = Some(request_position.clone()).into();
1390         proto_device.orientation = Some(request_orientation.clone()).into();
1391         patch_device_request.device = Some(proto_device.clone()).into();
1392         let patch_json = print_to_string(&patch_device_request).unwrap();
1393         patch_device_json(Some(chip_result.device_id), patch_json.as_str()).unwrap();
1394         match get_manager().devices.read().unwrap().get(&chip_result.device_id) {
1395             Some(device) => {
1396                 assert!(!device.visible.load(Ordering::SeqCst));
1397             }
1398             None => unreachable!(),
1399         }
1400 
1401         match get_pose_manager().get_position(&chip_result.device_id) {
1402             Some(position) => {
1403                 assert_eq!(position.x, request_position.x);
1404                 assert_eq!(position.y, request_position.y);
1405                 assert_eq!(position.z, request_position.z);
1406             }
1407             None => unreachable!(),
1408         }
1409 
1410         match get_pose_manager().get_orientation(&chip_result.device_id) {
1411             Some(orientation) => {
1412                 assert_eq!(orientation.yaw, request_orientation.yaw);
1413                 assert_eq!(orientation.pitch, request_orientation.pitch);
1414                 assert_eq!(orientation.roll, request_orientation.roll);
1415             }
1416             None => unreachable!(),
1417         }
1418 
1419         // Patch device by name with substring match
1420         proto_device.name = format!("test-device-name-1-{:?}", thread::current().id()).into();
1421         patch_device_request.device = Some(proto_device).into();
1422         let patch_json = print_to_string(&patch_device_request).unwrap();
1423         assert!(patch_device_json(None, patch_json.as_str()).is_ok());
1424     }
1425 
1426     #[test]
test_patch_error()1427     fn test_patch_error() {
1428         // Initializing Logger
1429         logger_setup();
1430 
1431         // Patch Error Testing
1432         let bt_chip_params = test_chip_1_bt();
1433         let bt_chip2_params = test_chip_2_bt();
1434         let bt_chip_result = bt_chip_params.add_chip().unwrap();
1435         bt_chip2_params.add_chip().unwrap();
1436 
1437         // Incorrect value type
1438         let error_json = format!(
1439             "{{\"device\": {{\"name\": \"test-device-name-1-{:?}\", \"position\": 1.1}}}}",
1440             thread::current().id()
1441         );
1442         let patch_result = patch_device_json(Some(bt_chip_result.device_id), error_json.as_str());
1443         assert!(patch_result.is_err());
1444         assert_eq!(
1445             patch_result.unwrap_err(),
1446             format!("Incorrect format of patch json {}", error_json)
1447         );
1448 
1449         // Incorrect key
1450         let error_json = format!(
1451             "{{\"device\": {{\"name\": \"test-device-name-1-{:?}\", \"hello\": \"world\"}}}}",
1452             thread::current().id()
1453         );
1454         let patch_result = patch_device_json(Some(bt_chip_result.device_id), error_json.as_str());
1455         assert!(patch_result.is_err());
1456         assert_eq!(
1457             patch_result.unwrap_err(),
1458             format!("Incorrect format of patch json {}", error_json)
1459         );
1460 
1461         // Incorrect Id
1462         let error_json = r#"{"device": {"name": "test-device-name-1"}}"#;
1463         let patch_result =
1464             patch_device_json(Some(DeviceIdentifier(INITIAL_DEVICE_ID - 1)), error_json);
1465         assert!(patch_result.is_err());
1466         assert_eq!(
1467             patch_result.unwrap_err(),
1468             format!("No such device with id {}", INITIAL_DEVICE_ID - 1)
1469         );
1470 
1471         // Incorrect name
1472         let error_json = r#"{"device": {"name": "wrong-name"}}"#;
1473         let patch_result = patch_device_json(None, error_json);
1474         assert!(patch_result.is_err());
1475         assert_eq!(patch_result.unwrap_err(), "No such device with name wrong-name");
1476 
1477         // Multiple ambiguous matching
1478         let error_json = r#"{"device": {"name": "test-device"}}"#;
1479         let patch_result = patch_device_json(None, error_json);
1480         assert!(patch_result.is_err());
1481         assert_eq!(
1482             patch_result.unwrap_err(),
1483             "Multiple ambiguous matches were found with substring test-device"
1484         );
1485     }
1486 
1487     #[test]
test_adding_two_chips()1488     fn test_adding_two_chips() {
1489         // Initializing Logger
1490         logger_setup();
1491 
1492         // Adding two chips of the same device
1493         let bt_chip_params = test_chip_1_bt();
1494         let wifi_chip_params = test_chip_1_wifi();
1495         let bt_chip_result = bt_chip_params.add_chip().unwrap();
1496         let wifi_chip_result = wifi_chip_params.add_chip().unwrap();
1497         assert_eq!(bt_chip_result.device_id, wifi_chip_result.device_id);
1498         let manager = get_manager();
1499         let devices = manager.devices.read().unwrap();
1500         let device = devices.get(&bt_chip_result.device_id).unwrap();
1501         assert_eq!(device.id, bt_chip_result.device_id);
1502         assert_eq!(device.name, bt_chip_params.device_name);
1503         assert_eq!(device.chips.read().unwrap().len(), 2);
1504         for chip in device.chips.read().unwrap().values() {
1505             assert!(chip.id == bt_chip_result.chip_id || chip.id == wifi_chip_result.chip_id);
1506             if chip.id == bt_chip_result.chip_id {
1507                 assert_eq!(chip.kind, ProtoChipKind::BLUETOOTH);
1508             } else if chip.id == wifi_chip_result.chip_id {
1509                 assert_eq!(chip.kind, ProtoChipKind::WIFI);
1510             } else {
1511                 unreachable!();
1512             }
1513         }
1514     }
1515 
1516     #[test]
test_reset()1517     fn test_reset() {
1518         // Initializing Logger
1519         logger_setup();
1520 
1521         // Patching Device and Resetting scene
1522         let chip_params = test_chip_1_bt();
1523         let chip_result = chip_params.add_chip().unwrap();
1524         let mut patch_device_request = PatchDeviceRequest::new();
1525         let mut proto_device = ProtoPatchDeviceFields::new();
1526         let request_position = new_position(10.0, 20.0, 30.0);
1527         let request_orientation = new_orientation(1.0, 2.0, 3.0);
1528         proto_device.name = Some(chip_params.device_name);
1529         proto_device.visible = Some(false);
1530         proto_device.position = Some(request_position).into();
1531         proto_device.orientation = Some(request_orientation).into();
1532         patch_device_request.device = Some(proto_device).into();
1533         patch_device_json(
1534             Some(chip_result.device_id),
1535             print_to_string(&patch_device_request).unwrap().as_str(),
1536         )
1537         .unwrap();
1538         match get_manager().devices.read().unwrap().get(&chip_result.device_id) {
1539             Some(device) => {
1540                 assert!(!device.visible.load(Ordering::SeqCst));
1541             }
1542             None => unreachable!(),
1543         }
1544 
1545         match get_pose_manager().get_position(&chip_result.device_id) {
1546             Some(position) => {
1547                 assert_eq!(position.x, 10.0);
1548             }
1549             None => unreachable!(),
1550         }
1551 
1552         match get_pose_manager().get_orientation(&chip_result.device_id) {
1553             Some(orientation) => {
1554                 assert_eq!(orientation.yaw, 1.0);
1555             }
1556             None => unreachable!(),
1557         }
1558 
1559         reset(chip_result.device_id).unwrap();
1560         match get_manager().devices.read().unwrap().get(&chip_result.device_id) {
1561             Some(device) => {
1562                 assert!(device.visible.load(Ordering::SeqCst));
1563             }
1564             None => unreachable!(),
1565         }
1566 
1567         match get_pose_manager().get_position(&chip_result.device_id) {
1568             Some(position) => {
1569                 assert_eq!(position.x, 0.0);
1570                 assert_eq!(position.y, 0.0);
1571                 assert_eq!(position.z, 0.0);
1572             }
1573             None => unreachable!(),
1574         }
1575 
1576         match get_pose_manager().get_orientation(&chip_result.device_id) {
1577             Some(orientation) => {
1578                 assert_eq!(orientation.yaw, 0.0);
1579                 assert_eq!(orientation.pitch, 0.0);
1580                 assert_eq!(orientation.roll, 0.0);
1581             }
1582             None => unreachable!(),
1583         }
1584     }
1585 
1586     #[test]
test_remove_chip()1587     fn test_remove_chip() {
1588         // Initializing Logger
1589         logger_setup();
1590 
1591         // Add 2 chips of same device and 1 chip of different device
1592         let bt_chip_params = test_chip_1_bt();
1593         let wifi_chip_params = test_chip_1_wifi();
1594         let bt_chip_2_params = test_chip_2_bt();
1595         let bt_chip_result = bt_chip_params.add_chip().unwrap();
1596         let wifi_chip_result = wifi_chip_params.add_chip().unwrap();
1597         let bt_chip_2_result = bt_chip_2_params.add_chip().unwrap();
1598 
1599         // Remove a bt chip of first device
1600         remove_chip(bt_chip_result.device_id, bt_chip_result.chip_id).unwrap();
1601         match get_manager().devices.read().unwrap().get(&bt_chip_result.device_id) {
1602             Some(device) => {
1603                 assert_eq!(device.chips.read().unwrap().len(), 1);
1604                 assert_eq!(
1605                     device.chips.read().unwrap().get(&wifi_chip_result.chip_id).unwrap().kind,
1606                     ProtoChipKind::WIFI
1607                 );
1608             }
1609             None => unreachable!(),
1610         }
1611 
1612         // Remove a wifi chip of first device
1613         remove_chip(wifi_chip_result.device_id, wifi_chip_result.chip_id).unwrap();
1614         assert!(!get_manager().devices.read().unwrap().contains_key(&wifi_chip_result.device_id));
1615 
1616         // Remove a bt chip of second device
1617         remove_chip(bt_chip_2_result.device_id, bt_chip_2_result.chip_id).unwrap();
1618         assert!(!get_manager().devices.read().unwrap().contains_key(&bt_chip_2_result.device_id));
1619     }
1620 
1621     #[test]
test_remove_chip_error()1622     fn test_remove_chip_error() {
1623         // Initializing Logger
1624         logger_setup();
1625 
1626         // Add 2 chips of same device and 1 chip of different device
1627         let bt_chip_params = test_chip_1_bt();
1628         let bt_chip_result = bt_chip_params.add_chip().unwrap();
1629 
1630         // Invoke remove_chip with incorrect chip_id.
1631         match remove_chip(bt_chip_result.device_id, ChipIdentifier(9999)) {
1632             Ok(_) => unreachable!(),
1633             Err(err) => assert_eq!(err, "RemoveChip chip id 9999 not found"),
1634         }
1635 
1636         // Invoke remove_chip with incorrect device_id
1637         match remove_chip(DeviceIdentifier(9999), bt_chip_result.chip_id) {
1638             Ok(_) => unreachable!(),
1639             Err(err) => assert_eq!(err, "RemoveChip device id 9999 not found"),
1640         }
1641         assert!(get_manager().devices.read().unwrap().contains_key(&bt_chip_result.device_id));
1642     }
1643 
1644     #[test]
test_get_distance()1645     fn test_get_distance() {
1646         // Initializing Logger
1647         logger_setup();
1648 
1649         // Add 2 chips of different devices
1650         let bt_chip_params = test_chip_1_bt();
1651         let bt_chip_2_params = test_chip_2_bt();
1652         let bt_chip_result = bt_chip_params.add_chip().unwrap();
1653         let bt_chip_2_result = bt_chip_2_params.add_chip().unwrap();
1654 
1655         // Patch the first chip
1656         let mut patch_device_request = PatchDeviceRequest::new();
1657         let mut proto_device = ProtoPatchDeviceFields::new();
1658         let request_position = new_position(1.0, 1.0, 1.0);
1659         proto_device.name = Some(bt_chip_params.device_name);
1660         proto_device.position = Some(request_position.clone()).into();
1661         patch_device_request.device = Some(proto_device.clone()).into();
1662         let patch_json = print_to_string(&patch_device_request).unwrap();
1663         patch_device_json(Some(bt_chip_result.device_id), patch_json.as_str()).unwrap();
1664 
1665         // Patch the second chip
1666         let mut patch_device_request = PatchDeviceRequest::new();
1667         let mut proto_device = ProtoPatchDeviceFields::new();
1668         let request_position = new_position(1.0, 4.0, 5.0);
1669         proto_device.name = Some(bt_chip_2_params.device_name);
1670         proto_device.position = Some(request_position.clone()).into();
1671         patch_device_request.device = Some(proto_device.clone()).into();
1672         let patch_json = print_to_string(&patch_device_request).unwrap();
1673         patch_device_json(Some(bt_chip_2_result.device_id), patch_json.as_str()).unwrap();
1674 
1675         // Verify the get_distance performs the correct computation of
1676         // sqrt((1-1)**2 + (4-1)**2 + (5-1)**2)
1677         assert_eq!(Ok(5.0), get_distance(&bt_chip_result.chip_id, &bt_chip_2_result.chip_id))
1678     }
1679 
1680     #[allow(dead_code)]
list_request() -> Request<Vec<u8>>1681     fn list_request() -> Request<Vec<u8>> {
1682         Request::builder()
1683             .method("GET")
1684             .uri("/v1/devices")
1685             .version(Version::HTTP_11)
1686             .body(Vec::<u8>::new())
1687             .unwrap()
1688     }
1689 
1690     use netsim_proto::model::chip::{
1691         ble_beacon::AdvertiseData, ble_beacon::AdvertiseSettings, BleBeacon, Chip,
1692     };
1693     use netsim_proto::model::chip_create::{BleBeaconCreate, Chip as BuiltChipProto};
1694     use netsim_proto::model::Chip as ChipProto;
1695     use netsim_proto::model::ChipCreate as ProtoChipCreate;
1696     use protobuf::{EnumOrUnknown, MessageField};
1697 
get_test_create_device_request(device_name: Option<String>) -> CreateDeviceRequest1698     fn get_test_create_device_request(device_name: Option<String>) -> CreateDeviceRequest {
1699         let beacon_proto = BleBeaconCreate {
1700             settings: MessageField::some(AdvertiseSettings { ..Default::default() }),
1701             adv_data: MessageField::some(AdvertiseData { ..Default::default() }),
1702             ..Default::default()
1703         };
1704 
1705         let chip_proto = ProtoChipCreate {
1706             name: String::from("test-beacon-chip"),
1707             kind: ProtoChipKind::BLUETOOTH_BEACON.into(),
1708             chip: Some(BuiltChipProto::BleBeacon(beacon_proto)),
1709             ..Default::default()
1710         };
1711 
1712         let device_proto = ProtoDeviceCreate {
1713             name: device_name.unwrap_or_default(),
1714             chips: vec![chip_proto],
1715             ..Default::default()
1716         };
1717 
1718         CreateDeviceRequest { device: MessageField::some(device_proto), ..Default::default() }
1719     }
1720 
1721     #[test]
test_create_device_succeeds()1722     fn test_create_device_succeeds() {
1723         logger_setup();
1724 
1725         let request = get_test_create_device_request(Some(format!(
1726             "bob-the-beacon-{:?}",
1727             thread::current().id()
1728         )));
1729 
1730         let device_proto = create_device(&request);
1731         assert!(device_proto.is_ok());
1732         let device_proto = device_proto.unwrap();
1733         assert_eq!(request.device.name, device_proto.name);
1734         assert_eq!(1, device_proto.chips.len());
1735         assert_eq!(request.device.chips[0].name, device_proto.chips[0].name);
1736     }
1737 
1738     #[test]
test_create_chipless_device_fails()1739     fn test_create_chipless_device_fails() {
1740         logger_setup();
1741 
1742         let request = CreateDeviceRequest {
1743             device: MessageField::some(ProtoDeviceCreate { ..Default::default() }),
1744             ..Default::default()
1745         };
1746 
1747         let device_proto = create_device(&request);
1748         assert!(device_proto.is_err(), "{}", device_proto.unwrap());
1749     }
1750 
1751     #[test]
test_create_radioless_device_fails()1752     fn test_create_radioless_device_fails() {
1753         logger_setup();
1754 
1755         let request = CreateDeviceRequest {
1756             device: MessageField::some(ProtoDeviceCreate {
1757                 chips: vec![ProtoChipCreate::default()],
1758                 ..Default::default()
1759             }),
1760             ..Default::default()
1761         };
1762 
1763         let device_proto = create_device(&request);
1764         assert!(device_proto.is_err(), "{}", device_proto.unwrap());
1765     }
1766 
1767     #[test]
test_get_beacon_device()1768     fn test_get_beacon_device() {
1769         logger_setup();
1770 
1771         let request = get_test_create_device_request(Some(format!(
1772             "bob-the-beacon-{:?}",
1773             thread::current().id()
1774         )));
1775 
1776         let device_proto = create_device(&request);
1777         assert!(device_proto.is_ok(), "{}", device_proto.unwrap_err());
1778         let device_proto = device_proto.unwrap();
1779         assert_eq!(1, device_proto.chips.len());
1780         assert!(device_proto.chips[0].chip.is_some());
1781         assert!(matches!(device_proto.chips[0].chip, Some(Chip::BleBeacon(_))));
1782     }
1783 
1784     #[test]
test_create_device_default_name()1785     fn test_create_device_default_name() {
1786         logger_setup();
1787 
1788         let request = get_test_create_device_request(None);
1789 
1790         let device_proto = create_device(&request);
1791         assert!(device_proto.is_ok(), "{}", device_proto.unwrap_err());
1792         let device_proto = device_proto.unwrap();
1793         assert_eq!(format!("device-{}", device_proto.id), device_proto.name);
1794     }
1795 
1796     #[test]
test_create_existing_device_fails()1797     fn test_create_existing_device_fails() {
1798         logger_setup();
1799 
1800         let request = get_test_create_device_request(Some(format!(
1801             "existing-device-{:?}",
1802             thread::current().id()
1803         )));
1804 
1805         let device_proto = create_device(&request);
1806         assert!(device_proto.is_ok(), "{}", device_proto.unwrap_err());
1807 
1808         // Attempt to create the device again. This should fail because the devices have the same name.
1809         let device_proto = create_device(&request);
1810         assert!(device_proto.is_err());
1811     }
1812 
1813     #[test]
test_patch_beacon_device()1814     fn test_patch_beacon_device() {
1815         logger_setup();
1816 
1817         let request = get_test_create_device_request(Some(format!(
1818             "bob-the-beacon-{:?}",
1819             thread::current().id()
1820         )));
1821 
1822         let device_proto = create_device(&request);
1823         assert!(device_proto.is_ok(), "{}", device_proto.unwrap_err());
1824         let device_proto = device_proto.unwrap();
1825         let manager = get_manager();
1826         let mut devices = manager.devices.write().unwrap();
1827         let device = devices
1828             .get_mut(&DeviceIdentifier(device_proto.id))
1829             .expect("could not find test bluetooth beacon device");
1830         let patch_result = device.patch(
1831             &ProtoPatchDeviceFields {
1832                 name: Some(device_proto.name.clone()),
1833                 chips: vec![ChipProto {
1834                     name: request.device.chips[0].name.clone(),
1835                     kind: EnumOrUnknown::new(ProtoChipKind::BLUETOOTH_BEACON),
1836                     chip: Some(Chip::BleBeacon(BleBeacon {
1837                         bt: MessageField::some(Default::default()),
1838                         ..Default::default()
1839                     })),
1840                     ..Default::default()
1841                 }],
1842                 ..Default::default()
1843             },
1844             get_pose_manager(),
1845         );
1846         assert!(patch_result.is_ok(), "{}", patch_result.unwrap_err());
1847         let patched_device = device.get(get_pose_manager());
1848         assert!(patched_device.is_ok(), "{}", patched_device.unwrap_err());
1849         let patched_device = patched_device.unwrap();
1850         assert_eq!(1, patched_device.chips.len());
1851         assert!(matches!(patched_device.chips[0].chip, Some(Chip::BleBeacon(_))));
1852     }
1853 
1854     #[test]
test_remove_beacon_device_succeeds()1855     fn test_remove_beacon_device_succeeds() {
1856         logger_setup();
1857 
1858         let create_request = get_test_create_device_request(None);
1859         let device_proto = create_device(&create_request);
1860         assert!(device_proto.is_ok(), "{}", device_proto.unwrap_err());
1861 
1862         let device_proto = device_proto.unwrap();
1863         let chip_id = {
1864             let manager = get_manager();
1865             let devices = manager.devices.read().unwrap();
1866             let device = devices.get(&DeviceIdentifier(device_proto.id)).unwrap();
1867             let chips = device.chips.read().unwrap();
1868             chips.first_key_value().map(|(id, _)| *id).unwrap()
1869         };
1870 
1871         let delete_request = DeleteChipRequest { id: chip_id.0, ..Default::default() };
1872         let delete_result = delete_chip(&delete_request);
1873         assert!(delete_result.is_ok(), "{}", delete_result.unwrap_err());
1874 
1875         assert!(!get_manager()
1876             .devices
1877             .read()
1878             .unwrap()
1879             .contains_key(&DeviceIdentifier(device_proto.id)))
1880     }
1881 
1882     #[test]
test_remove_beacon_device_fails()1883     fn test_remove_beacon_device_fails() {
1884         logger_setup();
1885 
1886         let create_request = get_test_create_device_request(None);
1887         let device_proto = create_device(&create_request);
1888         assert!(device_proto.is_ok(), "{}", device_proto.unwrap_err());
1889 
1890         let device_proto = device_proto.unwrap();
1891         let chip_id = get_manager()
1892             .devices
1893             .read()
1894             .unwrap()
1895             .get(&DeviceIdentifier(device_proto.id))
1896             .unwrap()
1897             .chips
1898             .read()
1899             .unwrap()
1900             .first_key_value()
1901             .map(|(id, _)| *id)
1902             .unwrap();
1903 
1904         let delete_request = DeleteChipRequest { id: chip_id.0, ..Default::default() };
1905         let delete_result = delete_chip(&delete_request);
1906         assert!(delete_result.is_ok(), "{}", delete_result.unwrap_err());
1907 
1908         let delete_result = delete_chip(&delete_request);
1909         assert!(delete_result.is_err());
1910     }
1911 
1912     #[test]
test_check_device_event_initial_timeout()1913     fn test_check_device_event_initial_timeout() {
1914         logger_setup();
1915 
1916         let mut events = events::test::new();
1917         let events_rx = events::test::subscribe(&mut events);
1918         assert_eq!(
1919             check_device_event(&events_rx, Some(std::time::Instant::now())),
1920             DeviceWaitStatus::Timeout
1921         );
1922     }
1923 
1924     #[test]
test_check_device_event_last_device_removed()1925     fn test_check_device_event_last_device_removed() {
1926         logger_setup();
1927 
1928         let mut events = events::test::new();
1929         let events_rx = events::test::subscribe(&mut events);
1930         events::test::publish(
1931             &mut events,
1932             Event::ChipRemoved(ChipRemoved {
1933                 remaining_nonbuiltin_devices: 0,
1934                 ..Default::default()
1935             }),
1936         );
1937         assert_eq!(check_device_event(&events_rx, None), DeviceWaitStatus::LastDeviceRemoved);
1938     }
1939 
1940     #[test]
test_check_device_event_device_chip_added()1941     fn test_check_device_event_device_chip_added() {
1942         logger_setup();
1943 
1944         let mut events = events::test::new();
1945         let events_rx = events::test::subscribe(&mut events);
1946         events::test::publish(
1947             &mut events,
1948             Event::DeviceAdded(DeviceAdded {
1949                 id: DeviceIdentifier(0),
1950                 name: "".to_string(),
1951                 builtin: false,
1952                 device_stats: ProtoDeviceStats::new(),
1953             }),
1954         );
1955         assert_eq!(check_device_event(&events_rx, None), DeviceWaitStatus::DeviceAdded);
1956         events::test::publish(
1957             &mut events,
1958             Event::ChipAdded(ChipAdded { builtin: false, ..Default::default() }),
1959         );
1960         assert_eq!(check_device_event(&events_rx, None), DeviceWaitStatus::DeviceAdded);
1961     }
1962 
1963     #[test]
test_check_device_event_ignore_event()1964     fn test_check_device_event_ignore_event() {
1965         logger_setup();
1966 
1967         let mut events = events::test::new();
1968         let events_rx = events::test::subscribe(&mut events);
1969         events::test::publish(
1970             &mut events,
1971             Event::DevicePatched(DevicePatched { id: DeviceIdentifier(0), name: "".to_string() }),
1972         );
1973         assert_eq!(check_device_event(&events_rx, None), DeviceWaitStatus::IgnoreEvent);
1974         events::test::publish(
1975             &mut events,
1976             Event::ChipRemoved(ChipRemoved {
1977                 remaining_nonbuiltin_devices: 1,
1978                 ..Default::default()
1979             }),
1980         );
1981         assert_eq!(check_device_event(&events_rx, None), DeviceWaitStatus::IgnoreEvent);
1982     }
1983 
1984     #[test]
test_check_device_event_ignore_chip_added_for_builtin()1985     fn test_check_device_event_ignore_chip_added_for_builtin() {
1986         logger_setup();
1987 
1988         let mut events = events::test::new();
1989         let events_rx = events::test::subscribe(&mut events);
1990         events::test::publish(
1991             &mut events,
1992             Event::ChipAdded(ChipAdded { builtin: true, ..Default::default() }),
1993         );
1994         assert_eq!(check_device_event(&events_rx, None), DeviceWaitStatus::IgnoreEvent);
1995     }
1996 }
1997