Skip to main content

mx_remote/types/
unit.rs

1// Author: Lars Op den Kamp (lars@opdenkamp-it.nl)
2// Copyright (c) 2026 Op den Kamp IT Solutions
3
4//! A unit's health and IP configuration, and the commands that change them.
5
6use std::net::Ipv4Addr;
7
8use crate::wire::{DeviceUid, UnitCommandFlag, UnitCommandKind, UnitFlag};
9
10/// The most temperature sensors a unit reports.
11pub const UNIT_MAX_TEMPERATURES: usize = 6;
12
13/// A unit's health and IP configuration, as it last reported them.
14///
15/// A unit reports this about itself with its periodic broadcast, and within a
16/// few seconds of a change to its flags, its status or its addresses.
17#[derive(Clone, Debug, Default, PartialEq, Eq)]
18pub struct UnitStatus {
19    /// The report's flags.
20    pub flags: UnitFlag,
21    /// Seconds since the unit booted.
22    pub uptime: u32,
23    /// How often the unit has booted, when `flags` has
24    /// [`BOOT_COUNT`](UnitFlag::BOOT_COUNT).
25    pub boot_count: u32,
26    /// How often the unit has rebooted.
27    pub reboot_count: u32,
28    /// The encoder's health, a mask of the unit's encoder status bits.
29    pub encoder_health: u32,
30    /// The decoder's health, a mask of the unit's decoder status and state
31    /// bits.
32    pub decoder_health: u32,
33    /// The IP configuration in use.
34    pub network: UnitNetConfig,
35    /// The fan speed in RPM, when `flags` has
36    /// [`FAN_SPEED`](UnitFlag::FAN_SPEED).
37    pub fan_rpm: u16,
38    /// The fan duty cycle, in percent.
39    pub fan_duty: u8,
40    /// The unit's system status.
41    pub status: u8,
42    /// Seconds before a pending IP configuration is reverted.
43    pub net_revert_s: u8,
44    /// Temperatures, in degrees Celsius, at most
45    /// [`UNIT_MAX_TEMPERATURES`] of them.
46    pub temperatures: Vec<i8>,
47    /// The message of the current system status.
48    pub status_message: String,
49}
50
51impl UnitStatus {
52    /// How often the unit has booted, `None` while it does not report it.
53    pub const fn boot_count(&self) -> Option<u32> {
54        if !self.flags.has(UnitFlag::BOOT_COUNT) {
55            return None;
56        }
57        Some(self.boot_count)
58    }
59
60    /// The fan speed in RPM, `None` while the unit does not report it.
61    pub const fn fan_rpm(&self) -> Option<u16> {
62        if !self.flags.has(UnitFlag::FAN_SPEED) {
63            return None;
64        }
65        Some(self.fan_rpm)
66    }
67
68    /// Whether an IP configuration changed through the mesh waits for the
69    /// mesh controller to confirm it.
70    pub const fn network_pending(&self) -> bool {
71        self.flags.has(UnitFlag::NET_PENDING)
72    }
73}
74
75/// An IP configuration: the addresses a unit uses, or is told to use.
76#[derive(Clone, Copy, Debug, PartialEq, Eq)]
77pub struct UnitNetConfig {
78    /// The address.
79    pub ip: Ipv4Addr,
80    /// The netmask.
81    pub netmask: Ipv4Addr,
82    /// The gateway.
83    pub gateway: Ipv4Addr,
84    /// The primary DNS server.
85    pub dns1: Ipv4Addr,
86    /// The secondary DNS server.
87    pub dns2: Ipv4Addr,
88}
89
90impl Default for UnitNetConfig {
91    fn default() -> Self {
92        Self {
93            ip: Ipv4Addr::UNSPECIFIED,
94            netmask: Ipv4Addr::UNSPECIFIED,
95            gateway: Ipv4Addr::UNSPECIFIED,
96            dns1: Ipv4Addr::UNSPECIFIED,
97            dns2: Ipv4Addr::UNSPECIFIED,
98        }
99    }
100}
101
102impl UnitNetConfig {
103    /// Whether the address is one a unit can hold and the netmask is a run of
104    /// ones from the top.
105    pub fn is_valid(&self) -> bool {
106        let ip = self.ip;
107        let mask = u32::from(self.netmask);
108        !ip.is_unspecified()
109            && !ip.is_broadcast()
110            && !ip.is_multicast()
111            && mask != 0
112            && mask.leading_ones() + mask.trailing_zeros() == 32
113    }
114}
115
116/// A command for one unit, as a peer sent it.
117#[derive(Clone, Copy, Debug, PartialEq, Eq)]
118pub struct UnitCommand {
119    /// The unit the command is for.
120    pub target: DeviceUid,
121    /// What it asks.
122    pub command: UnitCommandKind,
123    /// The command's flags.
124    pub flags: UnitCommandFlag,
125    /// The IP configuration a [`NET_CONFIG`](UnitCommandKind::NET_CONFIG)
126    /// carries, when the frame holds one.
127    pub network: Option<UnitNetConfig>,
128}