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