1use std::collections::{HashMap, HashSet};
2use std::net::IpAddr;
3
4use serde::Serialize;
5
6use tokio::sync::broadcast::{self, Receiver};
7use tokio::task::JoinHandle;
8
9use tosca::device::{DeviceEnvironment, DeviceKind};
10use tosca::events::{Events as ToscaEvents, EventsDescription};
11use tosca::route::RouteConfigs;
12
13use crate::error::{Error, ErrorKind, Result};
14use crate::events::{Events, EventsRunner};
15use crate::request::{Request, RequestInfo, create_requests};
16
17pub(crate) fn build_device_address(scheme: &str, address: &IpAddr, port: u16) -> String {
18 format!("{scheme}://{address}:{port}")
19}
20
21#[derive(Debug, PartialEq, Clone, Serialize)]
26pub struct NetworkInformation {
27 pub name: String,
29 pub addresses: HashSet<IpAddr>,
31 #[serde(skip_serializing_if = "Option::is_none")]
35 pub wifi_mac: Option<[u8; 6]>,
36 #[serde(skip_serializing_if = "Option::is_none")]
40 pub ethernet_mac: Option<[u8; 6]>,
41 pub port: u16,
43 pub properties: HashMap<String, String>,
45 pub last_reachable_address: String,
49}
50
51impl NetworkInformation {
52 #[must_use]
54 pub const fn new(
55 name: String,
56 addresses: HashSet<IpAddr>,
57 port: u16,
58 properties: HashMap<String, String>,
59 last_reachable_address: String,
60 ) -> Self {
61 Self {
62 name,
63 addresses,
64 wifi_mac: None,
65 ethernet_mac: None,
66 port,
67 properties,
68 last_reachable_address,
69 }
70 }
71
72 #[must_use]
74 pub const fn wifi_mac(mut self, mac: [u8; 6]) -> Self {
75 self.wifi_mac = Some(mac);
76 self
77 }
78
79 #[must_use]
81 pub const fn ethernet_mac(mut self, mac: [u8; 6]) -> Self {
82 self.ethernet_mac = Some(mac);
83 self
84 }
85}
86
87#[derive(Debug, PartialEq, Serialize)]
91pub struct Description {
92 pub kind: DeviceKind,
94 pub environment: DeviceEnvironment,
96 pub main_route: String,
98}
99
100impl Description {
101 #[must_use]
103 pub const fn new(kind: DeviceKind, environment: DeviceEnvironment, main_route: String) -> Self {
104 Self {
105 kind,
106 environment,
107 main_route,
108 }
109 }
110}
111
112#[derive(Debug, Serialize)]
114pub struct Device {
115 network_info: NetworkInformation,
117 description: Description,
119 requests: HashMap<String, Request>,
121 #[serde(skip)]
125 pub(crate) events: Option<Events>,
126 #[serde(skip)]
128 pub(crate) event_handle: Option<JoinHandle<()>>,
129}
130
131impl PartialEq for Device {
132 fn eq(&self, other: &Self) -> bool {
133 self.network_info == other.network_info
134 && self.description == other.description
135 && self.requests == other.requests
136 }
137}
138
139impl Device {
140 #[must_use]
146 pub fn new(
147 network_info: NetworkInformation,
148 description: Description,
149 route_configs: RouteConfigs,
150 ) -> Self {
151 let requests = create_requests(
152 route_configs,
153 &network_info.last_reachable_address,
154 &description.main_route,
155 description.environment,
156 );
157
158 Self {
163 network_info,
164 description,
165 requests,
166 events: None,
167 event_handle: None,
168 }
169 }
170
171 #[must_use]
173 pub const fn network_info(&self) -> &NetworkInformation {
174 &self.network_info
175 }
176
177 #[must_use]
179 pub const fn description(&self) -> &Description {
180 &self.description
181 }
182
183 #[must_use]
187 #[inline]
188 pub fn events_metadata(&self) -> Option<&EventsDescription> {
189 self.events.as_ref().map(|events| &events.description)
190 }
191
192 #[must_use]
195 #[inline]
196 pub fn requests_info(&self) -> Vec<RequestInfo<'_>> {
197 self.requests
198 .iter()
199 .map(|(route, sender)| RequestInfo::new(route, sender))
200 .collect()
201 }
202
203 #[must_use]
205 #[inline]
206 pub fn requests_count(&self) -> usize {
207 self.requests.len()
208 }
209
210 #[must_use]
214 #[inline]
215 pub fn request(&self, route: &str) -> Option<&Request> {
216 self.requests.get(route)
217 }
218
219 #[must_use]
221 pub const fn has_events(&self) -> bool {
222 self.events.is_some()
223 }
224
225 #[must_use]
229 pub const fn is_event_receiver_running(&self) -> bool {
230 self.event_handle.is_some()
231 }
232
233 #[inline]
256 pub async fn start_event_receiver(
257 &mut self,
258 id: usize,
259 buffer_size: usize,
260 ) -> Result<Receiver<ToscaEvents>> {
261 if self.event_handle.is_some() {
262 return Err(Error::new(
263 ErrorKind::Events,
264 format!("Event receiver already started for device with id `{id}`"),
265 ));
266 }
267
268 let Some(ref events) = self.events else {
269 return Err(Error::new(
270 ErrorKind::Events,
271 format!("The device with `{id}` does not support events"),
272 ));
273 };
274
275 let (tx, _) = broadcast::channel(buffer_size);
276
277 let handle = EventsRunner::run_device_subscriber(events, id, tx.clone()).await?;
278 self.event_handle = Some(handle);
279
280 Ok(tx.subscribe())
281 }
282
283 pub(crate) const fn init(
284 network_info: NetworkInformation,
285 description: Description,
286 requests: HashMap<String, Request>,
287 events: Option<Events>,
288 ) -> Self {
289 Self {
290 network_info,
291 description,
292 requests,
293 events,
294 event_handle: None,
295 }
296 }
297}
298
299#[derive(Debug, PartialEq, Serialize)]
301pub struct Devices(pub(crate) Vec<Device>);
302
303impl Default for Devices {
304 fn default() -> Self {
305 Self::new()
306 }
307}
308
309impl IntoIterator for Devices {
310 type Item = Device;
311 type IntoIter = std::vec::IntoIter<Self::Item>;
312
313 fn into_iter(self) -> Self::IntoIter {
314 self.0.into_iter()
315 }
316}
317
318impl<'a> IntoIterator for &'a Devices {
319 type Item = &'a Device;
320 type IntoIter = std::slice::Iter<'a, Device>;
321
322 fn into_iter(self) -> Self::IntoIter {
323 self.iter()
324 }
325}
326
327impl<'a> IntoIterator for &'a mut Devices {
328 type Item = &'a mut Device;
329 type IntoIter = std::slice::IterMut<'a, Device>;
330 fn into_iter(self) -> Self::IntoIter {
331 self.0.iter_mut()
332 }
333}
334
335impl Devices {
336 #[must_use]
338 pub const fn new() -> Self {
339 Self(Vec::new())
340 }
341
342 #[must_use]
344 pub const fn from_devices(devices: Vec<Device>) -> Self {
345 Self(devices)
346 }
347
348 #[inline]
350 pub fn add(&mut self, device: Device) {
351 self.0.push(device);
352 }
353
354 #[must_use]
356 #[inline]
357 pub fn is_empty(&self) -> bool {
358 self.0.is_empty()
359 }
360
361 #[must_use]
363 #[inline]
364 pub fn len(&self) -> usize {
365 self.0.len()
366 }
367
368 #[must_use]
370 #[inline]
371 pub fn get(&self, index: usize) -> Option<&Device> {
372 self.0.get(index)
373 }
374
375 #[inline]
377 pub fn iter(&self) -> std::slice::Iter<'_, Device> {
378 self.0.iter()
379 }
380
381 #[inline]
383 pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, Device> {
384 self.0.iter_mut()
385 }
386}
387
388#[cfg(test)]
389pub(crate) mod tests {
390 use std::collections::{HashMap, HashSet};
391
392 use tosca::device::{DeviceEnvironment, DeviceKind};
393 use tosca::hazards::{Hazard, Hazards};
394 use tosca::parameters::Parameters;
395 use tosca::route::{Route, RouteConfigs};
396
397 use super::{Description, Device, Devices, NetworkInformation, build_device_address};
398
399 fn create_network_info(address: &str, port: u16) -> NetworkInformation {
400 let ip_address = address.parse().unwrap();
401
402 let complete_address = build_device_address("http", &ip_address, port);
403
404 let mut addresses = HashSet::new();
405 addresses.insert(ip_address);
406 addresses.insert("172.0.0.1".parse().unwrap());
407
408 let mut properties = HashMap::new();
409 properties.insert("scheme".into(), "http".into());
410
411 NetworkInformation::new(
412 "device-name1._tosca._tcp.local.".into(),
413 addresses,
414 port,
415 properties,
416 complete_address,
417 )
418 .wifi_mac([0x02, 0x11, 0x22, 0x33, 0x44, 0x55])
419 .ethernet_mac([0x06, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE])
420 }
421
422 fn create_description(device_kind: DeviceKind, main_route: &str) -> Description {
423 Description::new(device_kind, DeviceEnvironment::Os, main_route.into())
424 }
425
426 pub(crate) fn create_light() -> Device {
427 let network_info = create_network_info("192.168.1.174", 5000);
428 let description = create_description(DeviceKind::Light, "light/");
429
430 let light_on_route = Route::put("On", "/on")
431 .description("Turn light on.")
432 .with_hazard(Hazard::ElectricEnergyConsumption);
433
434 let light_off_route = Route::put("Off", "/off")
435 .description("Turn light off.")
436 .with_hazard(Hazard::LogEnergyConsumption);
437
438 let toggle_route = Route::get("Toggle", "/toggle")
439 .description("Toggle a light.")
440 .with_hazards(
441 Hazards::new()
442 .insert(Hazard::FireHazard)
443 .insert(Hazard::ElectricEnergyConsumption),
444 )
445 .with_parameters(Parameters::new().rangeu64("brightness", (0, 20, 1)));
446
447 let route_configs = RouteConfigs::new()
448 .insert(light_on_route.serialize_data())
449 .insert(light_off_route.serialize_data())
450 .insert(toggle_route.serialize_data());
451
452 Device::new(network_info, description, route_configs)
453 }
454
455 pub(crate) fn create_unknown() -> Device {
456 let network_info = create_network_info("192.168.1.176", 5500);
457 let description = create_description(DeviceKind::Unknown, "ip-camera/");
458
459 let camera_stream_route = Route::get("Stream", "/stream")
460 .description("View camera stream.")
461 .with_hazards(
462 Hazards::new()
463 .insert(Hazard::ElectricEnergyConsumption)
464 .insert(Hazard::VideoDisplay)
465 .insert(Hazard::VideoRecordAndStore),
466 );
467
468 let screenshot_route = Route::get("Take screenshot", "/take-screenshot")
469 .description("Take a screenshot.")
470 .with_hazards(
471 Hazards::new()
472 .insert(Hazard::ElectricEnergyConsumption)
473 .insert(Hazard::TakeDeviceScreenshots)
474 .insert(Hazard::TakePictures),
475 );
476
477 let route_configs = RouteConfigs::new()
478 .insert(camera_stream_route.serialize_data())
479 .insert(screenshot_route.serialize_data());
480
481 Device::new(network_info, description, route_configs)
482 }
483
484 #[test]
485 fn check_devices() {
486 let devices_vector = vec![create_light(), create_unknown()];
487
488 let devices_from_vector = Devices::from_devices(devices_vector);
489
490 let mut devices = Devices::new();
491
492 assert!(devices.is_empty());
494
495 devices.add(create_light());
496 devices.add(create_unknown());
497
498 assert_eq!(devices_from_vector, devices);
500
501 assert!(!devices.is_empty());
503
504 assert_eq!(devices.len(), 2);
506
507 assert_eq!(devices.get(1000), None);
509
510 assert_eq!(devices.get(1), Some(&create_unknown()));
512 }
513}