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