Skip to main content

bacnet_rs/object/
engineering_units.rs

1//! BACnet Engineering Units
2//!
3//! This module defines the complete set of engineering units as specified in
4//! ANSI/ASHRAE Standard 135-2024.
5//!
6//! The enum includes all standard BACnet engineering units with their exact
7//! numeric values for protocol compatibility. Units are ordered by ID.
8
9macro_rules! generate_engineering_units {
10    ($(#[$doc:meta])* $name:ident { $($variant:ident = $value:expr => $bacnet_name:literal $unit_symbol:literal,)+ }) => {
11        pastey::paste! {
12            $(#[$doc])*
13            #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
14            pub enum $name {
15                $($variant,)*
16                Reserved([<$name Value>]),
17                Custom([<$name Value>]),
18            }
19
20            // Default should be NoUnits
21            impl Default for $name {
22                fn default() -> Self {
23                    $name::NoUnits
24                }
25            }
26
27            #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
28            pub struct [<$name Value>] { value: u32 }
29
30            impl [<$name Value>] {
31                fn new(value: u32) -> Self {
32                    Self { value }
33                }
34
35                pub fn value(&self) -> u32 {
36                    self.value
37                }
38            }
39
40            impl From<$name> for u32 {
41                fn from(value: $name) -> Self {
42                    match value {
43                        $($name::$variant => $value,)*
44                        $name::Reserved(value) => value.value(),
45                        $name::Custom(value) => value.value(),
46                    }
47                }
48            }
49
50            impl From<u32> for $name {
51                fn from(value: u32) -> Self {
52                    match value {
53                        $($value => $name::$variant,)*
54                        value if (0..=255).contains(&value) || (47808..=49999).contains(&value) || (65536..).contains(&value) => {
55                            $name::Reserved([<$name Value>]::new(value))
56                        }
57                        value if (256..=47807).contains(&value) || (50000..=65535).contains(&value) => {
58                            $name::Custom([<$name Value>]::new(value))
59                        }
60                        _ => unreachable!(),
61                    }
62                }
63            }
64
65            impl $name {
66                pub fn bacnet_name(&self) -> String {
67                    match self {
68                        $($name::$variant => $bacnet_name.to_string(),)*
69                        $name::Reserved(v) => format!("Reserved({})", v.value()),
70                        $name::Custom(v) => format!("Custom({})", v.value()),
71                    }
72                }
73
74                pub fn unit_symbol(&self) -> &str {
75                    match self {
76                        $($name::$variant => $unit_symbol,)*
77                        _ => "",
78                    }
79                }
80            }
81        }
82    };
83}
84
85generate_engineering_units! {
86    /// BACnet Engineering Units enumeration
87    ///
88    /// Represents all engineering units defined in the BACnet standard
89    /// (ANSI/ASHRAE Standard 135-2024).
90    /// Values 0-255 and 47808-49999 are reserved for definition by ASHRAE.
91    /// Values 256-47807 and 50000-65535 may be used by others and are represented
92    /// by the `Custom(EngineeringUnitsValue)` variant.
93    EngineeringUnits {
94        // 0-9: Area and electrical
95        SquareMeters = 0 => "square-meters" "m²",
96        SquareFeet = 1 => "square-feet" "ft²",
97        Milliamperes = 2 => "milliamperes" "mA",
98        Amperes = 3 => "amperes" "A",
99        Ohms = 4 => "ohms" "Ω",
100        Volts = 5 => "volts" "V",
101        Kilovolts = 6 => "kilovolts" "kV",
102        Megavolts = 7 => "megavolts" "MV",
103        VoltAmperes = 8 => "volt-amperes" "VA",
104        KilovoltAmperes = 9 => "kilovolt-amperes" "kVA",
105
106        // 10-19: Power and energy
107        MegavoltAmperes = 10 => "megavolt-amperes" "MVA",
108        VoltAmperesReactive = 11 => "volt-amperes-reactive" "var",
109        KilovoltAmperesReactive = 12 => "kilovolt-amperes-reactive" "kvar",
110        MegavoltAmperesReactive = 13 => "megavolt-amperes-reactive" "Mvar",
111        DegreesPhase = 14 => "degrees-phase" "°",
112        PowerFactor = 15 => "power-factor" "cos φ",
113        Joules = 16 => "joules" "J",
114        Kilojoules = 17 => "kilojoules" "kJ",
115        WattHours = 18 => "watt-hours" "W·h",
116        KilowattHours = 19 => "kilowatt-hours" "kW·h",
117
118        // 20-29: Energy and humidity
119        Btus = 20 => "btus" "Btu",
120        Therms = 21 => "therms" "thm",
121        TonHours = 22 => "ton-hours" "ton·h",
122        JoulesPerKilogramDryAir = 23 => "joules-per-kilogram-dry-air" "J/kg dry air",
123        BtusPerPoundDryAir = 24 => "btus-per-pound-dry-air" "Btu/lb dry air",
124        CyclesPerHour = 25 => "cycles-per-hour" "cph",
125        CyclesPerMinute = 26 => "cycles-per-minute" "cpm",
126        Hertz = 27 => "hertz" "Hz",
127        GramsOfWaterPerKilogramDryAir = 28 => "grams-of-water-per-kilogram-dry-air" "g/kg dry air",
128        PercentRelativeHumidity = 29 => "percent-relative-humidity" "% RH",
129
130        // 30-39: Length, illumination, mass
131        Millimeters = 30 => "millimeters" "mm",
132        Meters = 31 => "meters" "m",
133        Inches = 32 => "inches" "in",
134        Feet = 33 => "feet" "ft",
135        WattsPerSquareFoot = 34 => "watts-per-square-foot" "W/ft²",
136        WattsPerSquareMeter = 35 => "watts-per-square-meter" "W/m²",
137        Lumens = 36 => "lumens" "lm",
138        Luxes = 37 => "luxes" "lx",
139        FootCandles = 38 => "foot-candles" "fc",
140        Kilograms = 39 => "kilograms" "kg",
141
142        // 40-49: Mass and power
143        PoundsMass = 40 => "pounds-mass" "lbm",
144        Tons = 41 => "tons" "ton",
145        KilogramsPerSecond = 42 => "kilograms-per-second" "kg/s",
146        KilogramsPerMinute = 43 => "kilograms-per-minute" "kg/min",
147        KilogramsPerHour = 44 => "kilograms-per-hour" "kg/h",
148        PoundsMassPerMinute = 45 => "pounds-mass-per-minute" "lbm/min",
149        PoundsMassPerHour = 46 => "pounds-mass-per-hour" "lbm/h",
150        Watts = 47 => "watts" "W",
151        Kilowatts = 48 => "kilowatts" "kW",
152        Megawatts = 49 => "megawatts" "MW",
153
154        // 50-59: Power and pressure
155        BtusPerHour = 50 => "btus-per-hour" "Btu/h",
156        Horsepower = 51 => "horsepower" "hp",
157        TonsRefrigeration = 52 => "tons-refrigeration" "ton ref",
158        Pascals = 53 => "pascals" "Pa",
159        Kilopascals = 54 => "kilopascals" "kPa",
160        Bars = 55 => "bars" "bar",
161        PoundsForcePerSquareInch = 56 => "pounds-force-per-square-inch" "psi",
162        CentimetersOfWater = 57 => "centimeters-of-water" "cmH₂O",
163        InchesOfWater = 58 => "inches-of-water" "inH₂O",
164        MillimetersOfMercury = 59 => "millimeters-of-mercury" "mmHg",
165
166        // 60-69: Pressure and temperature
167        CentimetersOfMercury = 60 => "centimeters-of-mercury" "cmHg",
168        InchesOfMercury = 61 => "inches-of-mercury" "inHg",
169        DegreesCelsius = 62 => "degrees-celsius" "°C",
170        Kelvin = 63 => "kelvin" "K",
171        DegreesFahrenheit = 64 => "degrees-fahrenheit" "°F",
172        DegreeDaysCelsius = 65 => "degree-days-celsius" "°C·day",
173        DegreeDaysFahrenheit = 66 => "degree-days-fahrenheit" "°F·day",
174        Years = 67 => "years" "yr",
175        Months = 68 => "months" "mo",
176        Weeks = 69 => "weeks" "wk",
177
178        // 70-79: Time and velocity
179        Days = 70 => "days" "day",
180        Hours = 71 => "hours" "h",
181        Minutes = 72 => "minutes" "min",
182        Seconds = 73 => "seconds" "s",
183        MetersPerSecond = 74 => "meters-per-second" "m/s",
184        KilometersPerHour = 75 => "kilometers-per-hour" "km/h",
185        FeetPerSecond = 76 => "feet-per-second" "ft/s",
186        FeetPerMinute = 77 => "feet-per-minute" "ft/min",
187        MilesPerHour = 78 => "miles-per-hour" "mph",
188        CubicFeet = 79 => "cubic-feet" "ft³",
189
190        // 80-89: Volume and flow
191        CubicMeters = 80 => "cubic-meters" "m³",
192        ImperialGallons = 81 => "imperial-gallons" "gal (UK)",
193        Liters = 82 => "liters" "L",
194        UsGallons = 83 => "us-gallons" "gal (US)",
195        CubicFeetPerMinute = 84 => "cubic-feet-per-minute" "cfm",
196        CubicMetersPerSecond = 85 => "cubic-meters-per-second" "m³/s",
197        ImperialGallonsPerMinute = 86 => "imperial-gallons-per-minute" "gpm (UK)",
198        LitersPerSecond = 87 => "liters-per-second" "L/s",
199        LitersPerMinute = 88 => "liters-per-minute" "L/min",
200        UsGallonsPerMinute = 89 => "us-gallons-per-minute" "gpm (US)",
201
202        // 90-99: Angular, temperature rates, misc
203        DegreesAngular = 90 => "degrees-angular" "°",
204        DegreesCelsiusPerHour = 91 => "degrees-celsius-per-hour" "°C/h",
205        DegreesCelsiusPerMinute = 92 => "degrees-celsius-per-minute" "°C/min",
206        DegreesFahrenheitPerHour = 93 => "degrees-fahrenheit-per-hour" "°F/h",
207        DegreesFahrenheitPerMinute = 94 => "degrees-fahrenheit-per-minute" "°F/min",
208        NoUnits = 95 => "no-units" "",
209        PartsPerMillion = 96 => "parts-per-million" "ppm",
210        PartsPerBillion = 97 => "parts-per-billion" "ppb",
211        Percent = 98 => "percent" "%",
212        PercentPerSecond = 99 => "percent-per-second" "%/s",
213
214        // 100-109: Rates and currency
215        PerMinute = 100 => "per-minute" "/min",
216        PerSecond = 101 => "per-second" "/s",
217        PsiPerDegreeFahrenheit = 102 => "psi-per-degree-fahrenheit" "psi/°F",
218        Radians = 103 => "radians" "rad",
219        RevolutionsPerMinute = 104 => "revolutions-per-minute" "rpm",
220        Currency1 = 105 => "currency1" "",
221        Currency2 = 106 => "currency2" "",
222        Currency3 = 107 => "currency3" "",
223        Currency4 = 108 => "currency4" "",
224        Currency5 = 109 => "currency5" "",
225
226        // 110-119: Currency, area, misc
227        Currency6 = 110 => "currency6" "",
228        Currency7 = 111 => "currency7" "",
229        Currency8 = 112 => "currency8" "",
230        Currency9 = 113 => "currency9" "",
231        Currency10 = 114 => "currency10" "",
232        SquareInches = 115 => "square-inches" "in²",
233        SquareCentimeters = 116 => "square-centimeters" "cm²",
234        BtusPerPound = 117 => "btus-per-pound" "Btu/lb",
235        Centimeters = 118 => "centimeters" "cm",
236        PoundsMassPerSecond = 119 => "pounds-mass-per-second" "lbm/s",
237
238        // 120-129: Delta temps, resistance, voltage, energy
239        DeltaDegreesFahrenheit = 120 => "delta-degrees-fahrenheit" "Δ°F",
240        DeltaKelvin = 121 => "delta-kelvin" "ΔK",
241        Kiloohms = 122 => "kilohms" "kΩ",
242        Megaohms = 123 => "megohms" "MΩ",
243        Millivolts = 124 => "millivolts" "mV",
244        KilojoulesPerKilogram = 125 => "kilojoules-per-kilogram" "kJ/kg",
245        Megajoules = 126 => "megajoules" "MJ",
246        JoulesPerKelvin = 127 => "joules-per-kelvin" "J/K",
247        JoulesPerKilogramKelvin = 128 => "joules-per-kilogram-per-kelvin" "J/(kg·K)",
248        Kilohertz = 129 => "kilohertz" "kHz",
249
250        // 130-139: Frequency, rates, pressure, flow, energy density
251        Megahertz = 130 => "megahertz" "MHz",
252        PerHour = 131 => "per-hour" "/h",
253        Milliwatts = 132 => "milliwatts" "mW",
254        Hectopascals = 133 => "hectopascals" "hPa",
255        Millibars = 134 => "millibars" "mbar",
256        CubicMetersPerHour = 135 => "cubic-meters-per-hour" "m³/h",
257        LitersPerHour = 136 => "liters-per-hour" "L/h",
258        KilowattHoursPerSquareMeter = 137 => "kilowatt-hours-per-square-meter" "kWh/m²",
259        KilowattHoursPerSquareFoot = 138 => "kilowatt-hours-per-square-foot" "kWh/ft²",
260        MegajoulesPerSquareMeter = 139 => "megajoules-per-square-meter" "MJ/m²",
261
262        // 140-149: Energy density, flow, obscuration, resistance, energy
263        MegajoulesPerSquareFoot = 140 => "megajoules-per-square-foot" "MJ/ft²",
264        WattsPerSquareMeterPerKelvin = 141 => "watts-per-square-meter-per-kelvin" "W/(m²·K)",
265        CubicFeetPerSecond = 142 => "cubic-feet-per-second" "ft³/s",
266        PercentObscurationPerFoot = 143 => "percent-obscuration-per-foot" "%/ft",
267        PercentObscurationPerMeter = 144 => "percent-obscuration-per-meter" "%/m",
268        Milliohms = 145 => "milliohms" "mΩ",
269        MegawattHours = 146 => "megawatt-hours" "MW·h",
270        KiloBtus = 147 => "kilo-btus" "kBtu",
271        MegaBtus = 148 => "mega-btus" "MBtu",
272        KilojoulesPerKilogramDryAir = 149 => "kilojoules-per-kilogram-dry-air" "kJ/kg dry air",
273
274        // 150-159: Energy, force, mass flow, time
275        MegajoulesPerKilogramDryAir = 150 => "megajoules-per-kilogram-dry-air" "MJ/kg dry air",
276        KilojoulesPerKelvin = 151 => "kilojoules-per-kelvin" "kJ/K",
277        MegajoulesPerKelvin = 152 => "megajoules-per-kelvin" "MJ/K",
278        Newton = 153 => "newton" "N",
279        GramsPerSecond = 154 => "grams-per-second" "g/s",
280        GramsPerMinute = 155 => "grams-per-minute" "g/min",
281        TonsPerHour = 156 => "tons-per-hour" "ton/h",
282        KiloBtusPerHour = 157 => "kilo-btus-per-hour" "kBtu/h",
283        HundredthsSeconds = 158 => "hundredths-seconds" "cs",
284        Milliseconds = 159 => "milliseconds" "ms",
285
286        // 160-169: Torque, velocity, flow, acceleration, electromagnetic
287        NewtonMeters = 160 => "newton-meters" "N·m",
288        MillimetersPerSecond = 161 => "millimeters-per-second" "mm/s",
289        MillimetersPerMinute = 162 => "millimeters-per-minute" "mm/min",
290        MetersPerMinute = 163 => "meters-per-minute" "m/min",
291        MetersPerHour = 164 => "meters-per-hour" "m/h",
292        CubicMetersPerMinute = 165 => "cubic-meters-per-minute" "m³/min",
293        MetersPerSecondPerSecond = 166 => "meters-per-second-per-second" "m/s²",
294        AmperesPerMeter = 167 => "amperes-per-meter" "A/m",
295        AmperesPerSquareMeter = 168 => "amperes-per-square-meter" "A/m²",
296        AmpereSquareMeters = 169 => "ampere-square-meters" "A·m²",
297
298        // 170-179: Electromagnetic
299        Farads = 170 => "farads" "F",
300        Henrys = 171 => "henrys" "H",
301        OhmMeters = 172 => "ohm-meters" "Ω·m",
302        Siemens = 173 => "siemens" "S",
303        SiemensPerMeter = 174 => "siemens-per-meter" "S/m",
304        Teslas = 175 => "teslas" "T",
305        VoltsPerKelvin = 176 => "volts-per-kelvin" "V/K",
306        VoltsPerMeter = 177 => "volts-per-meter" "V/m",
307        Webers = 178 => "webers" "Wb",
308        Candelas = 179 => "candelas" "cd",
309
310        // 180-189: Photometric, thermal, mechanical
311        CandelasPerSquareMeter = 180 => "candelas-per-square-meter" "cd/m²",
312        KelvinPerHour = 181 => "kelvin-per-hour" "K/h",
313        KelvinPerMinute = 182 => "kelvin-per-minute" "K/min",
314        JouleSeconds = 183 => "joule-seconds" "J·s",
315        RadiansPerSecond = 184 => "radians-per-second" "rad/s",
316        SquareMetersPerNewton = 185 => "square-meters-per-newton" "m²/N",
317        KilogramsPerCubicMeter = 186 => "kilograms-per-cubic-meter" "kg/m³",
318        NewtonSeconds = 187 => "newton-seconds" "N·s",
319        NewtonsPerMeter = 188 => "newtons-per-meter" "N/m",
320        WattsPerMeterPerKelvin = 189 => "watts-per-meter-per-kelvin" "W/(m·K)",
321
322        // 190-199: Conductance, flow, length, mass, volume, acoustics
323        Microsiemens = 190 => "microsiemens" "µS",
324        CubicFeetPerHour = 191 => "cubic-feet-per-hour" "cfh",
325        UsGallonsPerHour = 192 => "us-gallons-per-hour" "gph (US)",
326        Kilometers = 193 => "kilometers" "km",
327        Micrometers = 194 => "micrometers" "µm",
328        Grams = 195 => "grams" "g",
329        Milligrams = 196 => "milligrams" "mg",
330        Milliliters = 197 => "milliliters" "mL",
331        MillilitersPerSecond = 198 => "milliliters-per-second" "mL/s",
332        Decibels = 199 => "decibels" "dB",
333
334        // 200-209: Acoustics, reactive energy, water, concentration
335        DecibelsMillivolt = 200 => "decibels-millivolt" "dBmV",
336        DecibelsVolt = 201 => "decibels-volt" "dBV",
337        Millisiemens = 202 => "millisiemens" "mS",
338        WattHoursReactive = 203 => "watt-reactive-hours" "var·h",
339        KilowattHoursReactive = 204 => "kilowatt-reactive-hours" "kvar·h",
340        MegawattHoursReactive = 205 => "megawatt-reactive-hours" "Mvar·h",
341        MillimetersOfWater = 206 => "millimeters-of-water" "mmH₂O",
342        PerMille = 207 => "per-mille" "‰",
343        GramsPerGram = 208 => "grams-per-gram" "g/g",
344        KilogramsPerKilogram = 209 => "kilograms-per-kilogram" "kg/kg",
345
346        // 210-219: Concentration
347        GramsPerKilogram = 210 => "grams-per-kilogram" "g/kg",
348        MilligramsPerGram = 211 => "milligrams-per-gram" "mg/g",
349        MilligramsPerKilogram = 212 => "milligrams-per-kilogram" "mg/kg",
350        GramsPerMilliliter = 213 => "grams-per-milliliter" "g/mL",
351        GramsPerLiter = 214 => "grams-per-liter" "g/L",
352        MilligramsPerLiter = 215 => "milligrams-per-liter" "mg/L",
353        MicrogramsPerLiter = 216 => "micrograms-per-liter" "µg/L",
354        GramsPerCubicMeter = 217 => "grams-per-cubic-meter" "g/m³",
355        MilligramsPerCubicMeter = 218 => "milligrams-per-cubic-meter" "mg/m³",
356        MicrogramsPerCubicMeter = 219 => "micrograms-per-cubic-meter" "µg/m³",
357
358        // 220-229: Concentration, radiation
359        NanogramsPerCubicMeter = 220 => "nanograms-per-cubic-meter" "ng/m³",
360        GramsPerCubicCentimeter = 221 => "grams-per-cubic-centimeter" "g/cm³",
361        Becquerels = 222 => "becquerels" "Bq",
362        Kilobecquerels = 223 => "kilobecquerels" "kBq",
363        Megabecquerels = 224 => "megabecquerels" "MBq",
364        Gray = 225 => "gray" "Gy",
365        Milligray = 226 => "milligray" "mGy",
366        Microgray = 227 => "microgray" "µGy",
367        Sieverts = 228 => "sieverts" "Sv",
368        Millisieverts = 229 => "millisieverts" "mSv",
369
370        // 230-239: Radiation, environmental, misc
371        Microsieverts = 230 => "microsieverts" "µSv",
372        MicrosievertsPerHour = 231 => "microsieverts-per-hour" "µSv/h",
373        DecibelsA = 232 => "decibels-a" "dBA",
374        NephelometricTurbidityUnit = 233 => "nephelometric-turbidity-unit" "NTU",
375        Ph = 234 => "pH" "pH",
376        GramsPerSquareMeter = 235 => "grams-per-square-meter" "g/m²",
377        MinutesPerKelvin = 236 => "minutes-per-kelvin" "min/K",
378        OhmMeterSquaredPerMeter = 237 => "ohm-meter-squared-per-meter" "Ω·m²/m",
379        AmpereSeconds = 238 => "ampere-seconds" "A·s",
380        VoltAmpereHours = 239 => "volt-ampere-hours" "VAh",
381
382        // 240-254: Electrical energy, flow, misc
383        KilovoltAmpereHours = 240 => "kilovolt-ampere-hours" "kVAh",
384        MegavoltAmpereHours = 241 => "megavolt-ampere-hours" "MVAh",
385        VoltAmpereHoursReactive = 242 => "volt-ampere-hours-reactive" "varh",
386        KilovoltAmpereHoursReactive = 243 => "kilovolt-ampere-hours-reactive" "kvarh",
387        MegavoltAmpereHoursReactive = 244 => "megavolt-ampere-hours-reactive" "Mvarh",
388        VoltSquareHours = 245 => "volt-square-hours" "V²·h",
389        AmpereSquareHours = 246 => "ampere-square-hours" "A²·h",
390        JoulesPerHour = 247 => "joules-per-hour" "J/h",
391        CubicFeetPerDay = 248 => "cubic-feet-per-day" "cfd",
392        CubicMetersPerDay = 249 => "cubic-meters-per-day" "m³/day",
393        WattHoursPerCubicMeter = 250 => "watt-hours-per-cubic-meter" "Wh/m³",
394        JoulesPerCubicMeter = 251 => "joules-per-cubic-meter" "J/m³",
395        MolePercent = 252 => "mole-percent" "mol%",
396        PascalSeconds = 253 => "pascal-seconds" "Pa·s",
397        MillionStandardCubicFeetPerMinute = 254 => "million-standard-cubic-feet-per-minute" "MMscfm",
398
399        // 47808-47812: Extended flow and mass
400        StandardCubicFeetPerDay = 47808 => "standard-cubic-feet-per-day" "scfd",
401        MillionStandardCubicFeetPerDay = 47809 => "million-standard-cubic-feet-per-day" "MMscfd",
402        ThousandCubicFeetPerDay = 47810 => "thousand-cubic-feet-per-day" "kcfd",
403        ThousandStandardCubicFeetPerDay = 47811 => "thousand-standard-cubic-feet-per-day" "kscfd",
404        PoundsMassPerDay = 47812 => "pounds-mass-per-day" "lbm/day",
405
406        // 47814-47824: Radiation, brewing, rates
407        Millirems = 47814 => "millirems" "mrem",
408        MilliremsPerHour = 47815 => "millirems-per-hour" "mrem/h",
409        DegreesLovibond = 47816 => "degrees-lovibond" "",
410        AlcoholByVolume = 47817 => "alcohol-by-volume" "",
411        InternationalBitteringUnits = 47818 => "international-bittering-units" "",
412        EuropeanBitternessUnits = 47819 => "european-bitterness-units" "",
413        DegreesPlato = 47820 => "degrees-plato" "",
414        SpecificGravity = 47821 => "specific-gravity" "",
415        EuropeanBrewingConvention = 47822 => "european-brewing-convention" "",
416        PerDay = 47823 => "per-day" "",
417        PerMillisecond = 47824 => "per-millisecond" "",
418
419        // 47825-47835: Distance, mass
420        Yards = 47825 => "yards" "",
421        Miles = 47826 => "miles" "",
422        NauticalMiles = 47827 => "nautical-miles" "",
423        Nanograms = 47828 => "nanograms" "ng",
424        Micrograms = 47829 => "micrograms" "µg",
425        MetricTonnes = 47830 => "metric-tonnes" "",
426        ShortTons = 47831 => "short-tons" "",
427        LongTons = 47832 => "long-tons" "",
428        GramsPerHour = 47833 => "grams-per-hour" "",
429        GramsPerDay = 47834 => "grams-per-day" "",
430        KilogramsPerDay = 47835 => "kilograms-per-day" "",
431
432        // 47836-47847: Mass flow rates (tons)
433        ShortTonsPerSecond = 47836 => "short-tons-per-second" "",
434        ShortTonsPerMinute = 47837 => "short-tons-per-minute" "",
435        ShortTonsPerHour = 47838 => "short-tons-per-hour" "",
436        ShortTonsPerDay = 47839 => "short-tons-per-day" "",
437        MetricTonnesPerSecond = 47840 => "metric-tonnes-per-second" "",
438        MetricTonnesPerMinute = 47841 => "metric-tonnes-per-minute" "",
439        MetricTonnesPerHour = 47842 => "metric-tonnes-per-hour" "",
440        MetricTonnesPerDay = 47843 => "metric-tonnes-per-day" "",
441        LongTonsPerSecond = 47844 => "long-tons-per-second" "",
442        LongTonsPerMinute = 47845 => "long-tons-per-minute" "",
443        LongTonsPerHour = 47846 => "long-tons-per-hour" "",
444        LongTonsPerDay = 47847 => "long-tons-per-day" "",
445
446        // 47848-47868: Energy rates (BTU, joule, kilojoule, megajoule)
447        BtusPerSecond = 47848 => "btus-per-second" "",
448        BtusPerMinute = 47849 => "btus-per-minute" "",
449        BtusPerDay = 47850 => "btus-per-day" "",
450        KiloBtusPerSecond = 47851 => "kilo-btus-per-second" "",
451        KiloBtusPerMinute = 47852 => "kilo-btus-per-minute" "",
452        KiloBtusPerDay = 47853 => "kilo-btus-per-day" "",
453        MegaBtusPerSecond = 47854 => "mega-btus-per-second" "",
454        MegaBtusPerMinute = 47855 => "mega-btus-per-minute" "",
455        MegaBtusPerHour = 47856 => "mega-btus-per-hour" "",
456        MegaBtusPerDay = 47857 => "mega-btus-per-day" "",
457        JoulesPerSecond = 47858 => "joules-per-second" "",
458        JoulesPerMinute = 47859 => "joules-per-minute" "",
459        JoulesPerDay = 47860 => "joules-per-day" "",
460        KilojoulesPerSecond = 47861 => "kilojoules-per-second" "",
461        KilojoulesPerMinute = 47862 => "kilojoules-per-minute" "",
462        KilojoulesPerHour = 47863 => "kilojoules-per-hour" "",
463        KilojoulesPerDay = 47864 => "kilojoules-per-day" "",
464        MegajoulesPerSecond = 47865 => "megajoules-per-second" "",
465        MegajoulesPerMinute = 47866 => "megajoules-per-minute" "",
466        MegajoulesPerHour = 47867 => "megajoules-per-hour" "",
467        MegajoulesPerDay = 47868 => "megajoules-per-day" "",
468
469        // 47869-47880: Temperature rates, flow rates
470        DegreesCelsiusPerDay = 47869 => "degrees-celsius-per-day" "",
471        KelvinPerDay = 47870 => "kelvin-per-day" "",
472        DegreesFahrenheitPerDay = 47871 => "degrees-fahrenheit-per-day" "",
473        DeltaDegreesCelsius = 47872 => "delta-degrees-celsius" "",
474        MillionCubicFeetPerMinute = 47873 => "million-cubic-feet-per-minute" "",
475        MillionCubicFeetPerDay = 47874 => "million-cubic-feet-per-day" "",
476        ImperialGallonsPerSecond = 47875 => "imperial-gallons-per-second" "",
477        ImperialGallonsPerHour = 47876 => "imperial-gallons-per-hour" "",
478        ImperialGallonsPerDay = 47877 => "imperial-gallons-per-day" "",
479        LitersPerDay = 47878 => "liters-per-day" "",
480        UsGallonsPerSecond = 47879 => "us-gallons-per-second" "",
481        UsGallonsPerDay = 47880 => "us-gallons-per-day" "",
482
483        // 47881-47897: Percent rates, concentration
484        PercentPerMinute = 47881 => "percent-per-minute" "",
485        PercentPerHour = 47882 => "percent-per-hour" "",
486        PercentPerDay = 47883 => "percent-per-day" "",
487        PerMillion = 47884 => "per-million" "",
488        PerBillion = 47885 => "per-billion" "",
489        MicrogramsPerGram = 47886 => "micrograms-per-gram" "",
490        NanogramsPerGram = 47887 => "nanograms-per-gram" "",
491        MicrogramsPerKilogram = 47888 => "micrograms-per-kilogram" "",
492        NanogramsPerKilogram = 47889 => "nanograms-per-kilogram" "",
493        MilligramsPerMilliliter = 47890 => "milligrams-per-milliliter" "",
494        MicrogramsPerMilliliter = 47891 => "micrograms-per-milliliter" "",
495        NanogramsPerMilliliter = 47892 => "nanograms-per-milliliter" "",
496        KilogramsPerLiter = 47893 => "kilograms-per-liter" "",
497        NanogramsPerLiter = 47894 => "nanograms-per-liter" "",
498        MilligramsPerCubicCentimeter = 47895 => "milligrams-per-cubic-centimeter" "",
499        MicrogramsPerCubicCentimeter = 47896 => "micrograms-per-cubic-centimeter" "",
500        NanogramsPerCubicCentimeter = 47897 => "nanograms-per-cubic-centimeter" "",
501
502        // 47898-47911: Efficiency, force, conductance
503        BtuPerHourPerWatt = 47898 => "btu-per-hour-per-watt" "",
504        BtuPerWattHourSeasonal = 47899 => "btu-per-watt-hour-seasonal" "",
505        CoefficientOfPerformance = 47900 => "coefficient-of-performance" "",
506        CoefficientOfPerformanceSeasonal = 47901 => "coefficient-of-performance-seasonal" "",
507        KilowattPerTonRefrigeration = 47902 => "kilowatt-per-ton-refrigeration" "",
508        LumensPerWatt = 47903 => "lumens-per-watt" "",
509        PoundForceFeet = 47904 => "pound-force-feet" "",
510        PoundForceInches = 47905 => "pound-force-inches" "",
511        OunceForceInches = 47906 => "ounce-force-inches" "",
512        PoundsForcePerSquareInchAbsolute = 47907 => "pounds-force-per-square-inch-absolute" "",
513        PoundsForcePerSquareInchGauge = 47908 => "pounds-force-per-square-inch-gauge" "",
514        MicrosiemensPerCentimeter = 47909 => "microsiemens-per-centimeter" "",
515        MillisiemensPerCentimeter = 47910 => "millisiemens-per-centimeter" "",
516        MillisiemensPerMeter = 47911 => "millisiemens-per-meter" "",
517
518        // 47912-47923: Volume, corrosion, pulses
519        MillionsOfUsGallons = 47912 => "millions-of-us-gallons" "",
520        MillionsOfImperialGallons = 47913 => "millions-of-imperial-gallons" "",
521        MillilitersPerMinute = 47914 => "milliliters-per-minute" "",
522        MilsPerYear = 47915 => "mils-per-year" "",
523        MillimetersPerYear = 47916 => "millimeters-per-year" "",
524        PulsesPerMinute = 47917 => "pulses-per-minute" "",
525        ActiveEnergyPulseValue = 47918 => "active-energy-pulse-value" "",
526        ReactiveEnergyPulseValue = 47919 => "reactive-energy-pulse-value" "",
527        ApparentEnergyPulseValue = 47920 => "apparent-energy-pulse-value" "",
528        VoltSquaredHourPulseValue = 47921 => "volt-squared-hour-pulse-value" "",
529        AmpereSquaredHourPulseValue = 47922 => "ampere-squared-hour-pulse-value" "",
530        CubicMeterPulseValue = 47923 => "cubic-meter-pulse-value" "",
531
532        // 47924-47936: Large power/energy, data rates
533        Gigawatts = 47924 => "gigawatts" "",
534        Gigajoules = 47925 => "gigajoules" "GJ",
535        Terajoules = 47926 => "terajoules" "TJ",
536        GigawattHours = 47927 => "gigawatt-hours" "GW·h",
537        GigawattReactiveHours = 47928 => "gigawatt-reactive-hours" "Gvar·h",
538        BitsPerSecond = 47929 => "bits-per-second" "",
539        KilobitsPerSecond = 47930 => "kilobits-per-second" "",
540        MegabitsPerSecond = 47931 => "megabits-per-second" "",
541        GigabitsPerSecond = 47932 => "gigabits-per-second" "",
542        BytesPerSecond = 47933 => "bytes-per-second" "",
543        KilobytesPerSecond = 47934 => "kilobytes-per-second" "",
544        MegabytesPerSecond = 47935 => "megabytes-per-second" "",
545        GigabytesPerSecond = 47936 => "gigabytes-per-second" "",
546
547        // 47937-47956: Custom volume and flow
548        Volume1 = 47937 => "volume1" "",
549        Volume2 = 47938 => "volume2" "",
550        Volume3 = 47939 => "volume3" "",
551        Volume4 = 47940 => "volume4" "",
552        Volume5 = 47941 => "volume5" "",
553        Volume6 = 47942 => "volume6" "",
554        Volume7 = 47943 => "volume7" "",
555        Volume8 = 47944 => "volume8" "",
556        Volume9 = 47945 => "volume9" "",
557        Volume10 = 47946 => "volume10" "",
558        VolumetricFlow1 = 47947 => "volumetric-flow1" "",
559        VolumetricFlow2 = 47948 => "volumetric-flow2" "",
560        VolumetricFlow3 = 47949 => "volumetric-flow3" "",
561        VolumetricFlow4 = 47950 => "volumetric-flow4" "",
562        VolumetricFlow5 = 47951 => "volumetric-flow5" "",
563        VolumetricFlow6 = 47952 => "volumetric-flow6" "",
564        VolumetricFlow7 = 47953 => "volumetric-flow7" "",
565        VolumetricFlow8 = 47954 => "volumetric-flow8" "",
566        VolumetricFlow9 = 47955 => "volumetric-flow9" "",
567        VolumetricFlow10 = 47956 => "volumetric-flow10" "",
568
569        // 47958-47981: Site units, particles, radiation, degree-time, sub-second time
570        SiteUnit1 = 47958 => "site-unit1" "",
571        SiteUnit2 = 47959 => "site-unit2" "",
572        SiteUnit3 = 47960 => "site-unit3" "",
573        SiteUnit4 = 47961 => "site-unit4" "",
574        SiteUnit5 = 47962 => "site-unit5" "",
575        SiteUnit6 = 47963 => "site-unit6" "",
576        SiteUnit7 = 47964 => "site-unit7" "",
577        SiteUnit8 = 47965 => "site-unit8" "",
578        SiteUnit9 = 47966 => "site-unit9" "",
579        SiteUnit10 = 47967 => "site-unit10" "",
580        ParticlesPerCubicFoot = 47968 => "particles-per-cubic-foot" "",
581        ParticlesPerCubicMeter = 47969 => "particles-per-cubic-meter" "",
582        PicocuriesPerLiter = 47970 => "picocuries-per-liter" "",
583        BecquerelsPerCubicMeter = 47971 => "becquerels-per-cubic-meter" "",
584        GrainsOfWaterPerPoundDryAir = 47972 => "grains-of-water-per-pound-dry-air" "",
585        DegreeHoursCelsius = 47973 => "degree-hours-celsius" "",
586        DegreeHoursFahrenheit = 47974 => "degree-hours-fahrenheit" "",
587        DegreeMinutesCelsius = 47975 => "degree-minutes-celsius" "",
588        DegreeMinutesFahrenheit = 47976 => "degree-minutes-fahrenheit" "",
589        DegreeSecondsCelsius = 47977 => "degree-seconds-celsius" "",
590        DegreeSecondsFahrenheit = 47978 => "degree-seconds-fahrenheit" "",
591        Microseconds = 47979 => "microseconds" "",
592        Nanoseconds = 47980 => "nanoseconds" "",
593        Picoseconds = 47981 => "picoseconds" "",
594    }
595}
596
597#[cfg(test)]
598mod tests {
599    use super::*;
600
601    #[test]
602    fn test_from_u32() {
603        // Test standard ASHRAE units
604        assert_eq!(EngineeringUnits::from(95), EngineeringUnits::NoUnits);
605        assert_eq!(EngineeringUnits::from(62), EngineeringUnits::DegreesCelsius);
606        assert_eq!(
607            EngineeringUnits::from(64),
608            EngineeringUnits::DegreesFahrenheit
609        );
610
611        // Test custom units (256-47807)
612        let custom1 = EngineeringUnits::from(1000);
613        assert!(matches!(custom1, EngineeringUnits::Custom(_)));
614        assert_eq!(u32::from(custom1), 1000);
615
616        // Test concrete unit by ID
617        let per_minute = EngineeringUnits::from(100);
618        assert!(matches!(per_minute, EngineeringUnits::PerMinute));
619        assert_eq!(u32::from(per_minute), 100);
620    }
621
622    #[test]
623    fn test_to_u32() {
624        assert_eq!(u32::from(EngineeringUnits::NoUnits), 95);
625        assert_eq!(u32::from(EngineeringUnits::DegreesCelsius), 62);
626        assert_eq!(
627            u32::from(EngineeringUnits::Custom(EngineeringUnitsValue::new(1000))),
628            1000
629        );
630    }
631
632    #[test]
633    fn test_roundtrip() {
634        // Standard units should roundtrip
635        let celsius = EngineeringUnits::DegreesCelsius;
636        assert_eq!(EngineeringUnits::from(u32::from(celsius)), celsius);
637
638        // Custom units should roundtrip
639        let custom = EngineeringUnits::from(12345);
640        assert_eq!(EngineeringUnits::from(u32::from(custom)), custom);
641    }
642
643    #[test]
644    fn test_default() {
645        assert_eq!(EngineeringUnits::default(), EngineeringUnits::NoUnits);
646    }
647
648    #[test]
649    fn test_bacnet_name() {
650        assert_eq!(
651            EngineeringUnits::DegreesCelsius.bacnet_name(),
652            "degrees-celsius"
653        );
654        assert_eq!(EngineeringUnits::NoUnits.bacnet_name(), "no-units");
655        assert_eq!(EngineeringUnits::from(1000).bacnet_name(), "Custom(1000)");
656    }
657
658    #[test]
659    fn test_unit_symbol() {
660        assert_eq!(EngineeringUnits::DegreesCelsius.unit_symbol(), "°C");
661        assert_eq!(EngineeringUnits::NoUnits.unit_symbol(), "");
662        assert_eq!(EngineeringUnits::from(1000).unit_symbol(), "");
663    }
664
665    #[test]
666    fn test_newly_added_units() {
667        // Verify the 13 previously missing units
668        assert_eq!(u32::from(EngineeringUnits::PerMinute), 100);
669        assert_eq!(u32::from(EngineeringUnits::PerSecond), 101);
670        assert_eq!(u32::from(EngineeringUnits::Nanograms), 47828);
671        assert_eq!(u32::from(EngineeringUnits::Micrograms), 47829);
672        assert_eq!(u32::from(EngineeringUnits::KelvinPerDay), 47870);
673        assert_eq!(u32::from(EngineeringUnits::ImperialGallonsPerHour), 47876);
674        assert_eq!(u32::from(EngineeringUnits::ImperialGallonsPerDay), 47877);
675        assert_eq!(u32::from(EngineeringUnits::MicrogramsPerGram), 47886);
676        assert_eq!(u32::from(EngineeringUnits::NanogramsPerGram), 47887);
677        assert_eq!(u32::from(EngineeringUnits::Gigajoules), 47925);
678        assert_eq!(u32::from(EngineeringUnits::Terajoules), 47926);
679        assert_eq!(u32::from(EngineeringUnits::GigawattHours), 47927);
680        assert_eq!(u32::from(EngineeringUnits::GigawattReactiveHours), 47928);
681    }
682
683    #[test]
684    fn test_corrected_names() {
685        // Verify name corrections match ASHRAE 135-2024
686        assert_eq!(EngineeringUnits::Kiloohms.bacnet_name(), "kilohms");
687        assert_eq!(
688            EngineeringUnits::JoulesPerKilogramKelvin.bacnet_name(),
689            "joules-per-kilogram-per-kelvin"
690        );
691        assert_eq!(EngineeringUnits::Microsiemens.bacnet_name(), "microsiemens");
692        assert_eq!(
693            EngineeringUnits::WattHoursReactive.bacnet_name(),
694            "watt-reactive-hours"
695        );
696        assert_eq!(
697            EngineeringUnits::KilowattHoursReactive.bacnet_name(),
698            "kilowatt-reactive-hours"
699        );
700        assert_eq!(
701            EngineeringUnits::MegawattHoursReactive.bacnet_name(),
702            "megawatt-reactive-hours"
703        );
704        assert_eq!(EngineeringUnits::Ph.bacnet_name(), "pH");
705    }
706}