1#[allow(unused_imports)]
4use {
5 crate::{
6 error::{Error, ErrorCode},
7 parser::{
8 response::{Formatter, ResponseData},
9 tokenizer::Token,
10 },
11 },
12 core::convert::{TryFrom, TryInto},
13};
14
15pub enum Db<V, UNIT> {
19 None(V),
21 Linear(UNIT),
23 Logarithmic(V, UNIT),
25}
26
27pub enum Amplitude<UNIT> {
29 None(UNIT),
31 Peak(UNIT),
35 PeakToPeak(UNIT),
39 Rms(UNIT),
43}
44
45impl<'a, UNIT> TryFrom<Token<'a>> for Amplitude<UNIT>
46where
47 UNIT: TryFrom<Token<'a>, Error = Error>,
48{
49 type Error = Error;
50
51 fn try_from(value: Token<'a>) -> Result<Self, Self::Error> {
52 fn ends_with_ignore_ascii(str: &[u8], needle: &[u8]) -> bool {
53 let (m, n) = (str.len(), needle.len());
54 m >= n && needle.eq_ignore_ascii_case(&str[m - n..])
55 }
56
57 match value {
58 Token::DecimalNumericSuffixProgramData(num, s) if ends_with_ignore_ascii(s, b"PK") => {
59 Ok(Self::Peak(<UNIT>::try_from(
60 Token::DecimalNumericSuffixProgramData(num, &s[..s.len() - 2]),
61 )?))
62 }
63 Token::DecimalNumericSuffixProgramData(num, s) if ends_with_ignore_ascii(s, b"PP") => {
64 Ok(Self::PeakToPeak(<UNIT>::try_from(
65 Token::DecimalNumericSuffixProgramData(num, &s[..s.len() - 2]),
66 )?))
67 }
68 Token::DecimalNumericSuffixProgramData(num, s) if ends_with_ignore_ascii(s, b"RMS") => {
69 Ok(Self::Rms(<UNIT>::try_from(
70 Token::DecimalNumericSuffixProgramData(num, &s[..s.len() - 3]),
71 )?))
72 }
73 _ => Ok(Self::None(<UNIT>::try_from(value)?)),
74 }
75 }
76}
77
78#[cfg(feature = "unit-angle")]
79mod angle {
80 use super::*;
81 use uom::si::angle::{degree, gon, minute, radian, revolution, second, Angle};
82
83 impl_unit![uom::si::angle::Conversion<V>, Angle, radian;
84 b"RAD" => radian,
85 b"DEG" => degree,
86 b"MNT" => minute,
87 b"SEC" => second,
88 b"REV" => revolution,
89 b"GON" => gon
90 ];
91}
92
93#[cfg(feature = "unit-capacitance")]
94mod capacitance {
95 use super::*;
96 use uom::si::capacitance::{farad, microfarad, millifarad, nanofarad, picofarad, Capacitance};
97
98 impl_unit![uom::si::capacitance::Conversion<V>, Capacitance, farad;
99 b"F" => farad,
100 b"MF" => millifarad,
101 b"UF" => microfarad,
102 b"NF" => nanofarad,
103 b"PF" => picofarad
104 ];
105}
106
107#[cfg(feature = "unit-electric-charge")]
108mod electric_charge {
109 use super::*;
110 use uom::si::electric_charge::{
111 ampere_hour, coulomb, kilocoulomb, megacoulomb, microcoulomb, milliampere_hour,
112 millicoulomb, ElectricCharge,
113 };
114
115 impl_unit![uom::si::electric_charge::Conversion<V>, ElectricCharge, coulomb;
116 b"MAC" => megacoulomb,
118 b"KC" => kilocoulomb,
119 b"C" => coulomb,
120 b"MC" => millicoulomb,
121 b"UC" => microcoulomb,
122 b"AH"|b"A.HR" => ampere_hour,
124 b"MAH"|b"MA.HR" => milliampere_hour
125 ];
126}
127
128#[cfg(feature = "unit-electric-current")]
129mod electric_current {
130 use super::*;
131 use uom::si::electric_current::{
132 ampere, kiloampere, microampere, milliampere, nanoampere, ElectricCurrent,
133 };
134
135 impl_unit![uom::si::electric_current::Conversion<V>, ElectricCurrent, ampere;
136 b"KA" => kiloampere,
137 b"A" => ampere,
138 b"MA" => milliampere,
139 b"UA" => microampere,
140 b"NA" => nanoampere
141 ];
142
143 impl_logarithmic_unit![uom::si::electric_current::Conversion<V>, ElectricCurrent;
144 b"DBA" => ampere,
145 b"DBMA" => milliampere,
146 b"DBUA" => microampere
147 ];
148}
149
150#[cfg(feature = "unit-electric-potential")]
151mod electric_potential {
152 use super::*;
153 use uom::si::electric_potential::{kilovolt, microvolt, millivolt, volt, ElectricPotential};
154
155 impl_unit![uom::si::electric_potential::Conversion<V>, ElectricPotential, volt;
156 b"KV" => kilovolt,
157 b"V" => volt,
158 b"MV" => millivolt,
159 b"UV" => microvolt
160 ];
161
162 impl_logarithmic_unit![uom::si::electric_potential::Conversion<V>, ElectricPotential;
163 b"DBV" => volt,
164 b"DBMV" => millivolt,
165 b"DBUV" => microvolt
166 ];
167}
168
169#[cfg(feature = "unit-electrical-conductance")]
170mod electrical_conductance {
171 use super::*;
172 use uom::si::electrical_conductance::{
173 kilosiemens, microsiemens, millisiemens, siemens, ElectricalConductance,
174 };
175
176 impl_unit![uom::si::electrical_conductance::Conversion<V>, ElectricalConductance, siemens;
177 b"KSIE" => kilosiemens,
178 b"SIE" => siemens,
179 b"MSIE" => millisiemens,
180 b"USIE" => microsiemens
181 ];
182}
183
184#[cfg(feature = "unit-electrical-resistance")]
185mod electrical_resistance {
186 use super::*;
187
188 use uom::si::electrical_resistance::{
189 gigaohm, kiloohm, megaohm, microohm, ohm, ElectricalResistance,
190 };
191 impl_unit![uom::si::electrical_resistance::Conversion<V>, ElectricalResistance, ohm;
192 b"GOHM" => gigaohm,
193 b"MOHM" => megaohm,
194 b"KOHM" => kiloohm,
195 b"OHM" => ohm,
196 b"UOHM" => microohm
197 ];
198}
199
200#[cfg(feature = "unit-energy")]
201mod energy {
202 use super::*;
203 use uom::si::energy::{
204 electronvolt, joule, kilojoule, megajoule, megawatt_hour, microjoule, milliwatt_hour,
205 watt_hour, Energy,
206 };
207
208 impl_unit![uom::si::energy::Conversion<V>, Energy, joule;
209 b"MAJ" => megajoule,
210 b"KJ" => kilojoule,
211 b"J" => joule,
212 b"MJ" => megajoule,
213 b"UJ" => microjoule,
214 b"MAW.HR" => megawatt_hour,
216 b"WH"|b"W.HR" => watt_hour,
217 b"MW.HR" => milliwatt_hour,
218 b"EV" => electronvolt
220 ];
221}
222
223#[cfg(feature = "unit-inductance")]
224mod inductance {
225 use super::*;
226 use uom::si::inductance::{henry, microhenry, millihenry, nanohenry, picohenry, Inductance};
227
228 impl_unit![uom::si::inductance::Conversion<V>, Inductance, henry;
229 b"H" => henry,
230 b"MH" => millihenry,
231 b"UH" => microhenry,
232 b"NH" => nanohenry,
233 b"PH" => picohenry
234 ];
235}
236
237#[cfg(feature = "unit-power")]
238mod power {
239 use super::*;
240 use uom::si::power::{kilowatt, megawatt, microwatt, milliwatt, watt, Power};
241
242 impl_unit![uom::si::power::Conversion<V>, Power, watt;
243 b"MAW" => megawatt,
244 b"KW" => kilowatt,
245 b"W" => watt,
246 b"MW" => milliwatt,
247 b"UW" => microwatt
248 ];
249
250 impl_logarithmic_unit![uom::si::power::Conversion<V>, Power;
251 b"DBW" => watt,
252 b"DBMW" | b"DBM" => milliwatt,
253 b"DBUW" => microwatt
254 ];
255}
256
257#[cfg(feature = "unit-ratio")]
258mod ratio {
259 use super::*;
260 use uom::si::ratio::{part_per_million, percent, ratio, Ratio};
261
262 impl_unit![uom::si::ratio::Conversion<V>, Ratio, ratio;
263 b"PCT" => percent,
264 b"PPM" => part_per_million
265 ];
266
267 impl_logarithmic_unit![uom::si::ratio::Conversion<V>, Ratio;
268 b"DB" => ratio
269 ];
270}
271
272#[cfg(feature = "unit-thermodynamic-temperature")]
273mod thermodynamic_temperature {
274 use super::*;
275 use uom::si::thermodynamic_temperature::{
276 degree_celsius, degree_fahrenheit, kelvin, ThermodynamicTemperature,
277 };
278
279 impl_unit![uom::si::thermodynamic_temperature::Conversion<V>, ThermodynamicTemperature, degree_celsius;
280 b"CEL" => degree_celsius,
281 b"FAR" => degree_fahrenheit,
282 b"K" => kelvin
283 ];
284}
285
286#[cfg(feature = "unit-time")]
287mod time {
288 use super::*;
289 use uom::si::time::{
290 day, hour, microsecond, millisecond, minute, nanosecond, second, year, Time,
291 };
292
293 impl_unit![uom::si::time::Conversion<V>, Time, second;
294 b"S" => second,
295 b"MS" => millisecond,
296 b"US" => microsecond,
297 b"NS" => nanosecond,
298 b"MIN" => minute,
299 b"HR" => hour,
300 b"D" => day,
301 b"ANN" => year
302 ];
303}
304
305#[cfg(feature = "unit-frequency")]
306mod frequency {
307 use super::*;
308 use uom::si::frequency::{gigahertz, hertz, kilohertz, megahertz, Frequency};
309
310 impl_unit![uom::si::frequency::Conversion<V>, Frequency, hertz;
311 b"GHZ" => gigahertz,
312 b"MHZ" | b"MAHZ" => megahertz,
313 b"KHZ" => kilohertz,
314 b"HZ" => hertz
315 ];
316}
317
318#[cfg(all(feature = "unit-electric-potential", test))]
319mod test_suffix {
320
321 extern crate std;
322
323 use crate::{
324 parser::suffix::{Amplitude, Db},
325 tree::prelude::*,
327 };
328 use core::convert::TryInto;
329 use uom::si::f32::*;
330
331 #[test]
332 fn test_suffix_amplitude() {
333 let none: Amplitude<ElectricPotential> =
334 Token::DecimalNumericSuffixProgramData(b"1.0", b"V")
335 .try_into()
336 .unwrap();
337 assert!(matches!(none, Amplitude::None(_)));
338 let peak: Amplitude<ElectricPotential> =
339 Token::DecimalNumericSuffixProgramData(b"1.0", b"VPK")
340 .try_into()
341 .unwrap();
342 assert!(matches!(peak, Amplitude::Peak(_)));
343 let peak_to_peak: Amplitude<ElectricPotential> =
344 Token::DecimalNumericSuffixProgramData(b"1.0", b"VPP")
345 .try_into()
346 .unwrap();
347 assert!(matches!(peak_to_peak, Amplitude::PeakToPeak(_)));
348 let rms: Amplitude<ElectricPotential> =
349 Token::DecimalNumericSuffixProgramData(b"1.0", b"VRMS")
350 .try_into()
351 .unwrap();
352 assert!(matches!(rms, Amplitude::Rms(_)))
353 }
354
355 #[test]
356 fn test_suffix_logarithmic() {
357 let none: Db<f32, ElectricPotential> =
358 Token::DecimalNumericProgramData(b"1.0").try_into().unwrap();
359 assert!(matches!(none, Db::None(_)));
360 let peak: Db<f32, ElectricPotential> = Token::DecimalNumericSuffixProgramData(b"1.0", b"V")
361 .try_into()
362 .unwrap();
363 assert!(matches!(peak, Db::Linear(_)));
364 let peak_to_peak: Db<f32, ElectricPotential> =
365 Token::DecimalNumericSuffixProgramData(b"1.0", b"DBV")
366 .try_into()
367 .unwrap();
368 assert!(matches!(peak_to_peak, Db::Logarithmic(_, _)));
369 }
370
371 }
377
378#[allow(unused_macros)]
379macro_rules! impl_logarithmic_unit {
380 ($conversion:path, $unit:ident; $($($suffix:literal)|+ => $subunit:ident),+) => {
381 impl<'a, U, V> TryFrom<Token<'a>> for Db<V,$unit<U, V>>
382 where
383 U: Units<V> + ?Sized,
384 V: Num + uom::Conversion<V> + TryFrom<Token<'a>,Error=Error>,
385 $unit<U, V>: TryFrom<Token<'a>,Error=Error>,
386 $(
387 $subunit: $conversion
388 ),+
389 {
390 type Error = Error;
391
392 fn try_from(value: Token<'a>) -> Result<Self, Self::Error> {
393 if let Token::DecimalNumericProgramData(_) = value {
394 Ok(Self::None(<V>::try_from(value)?))
395 } else if let Token::DecimalNumericSuffixProgramData(num, suffix) = value {
396 match suffix {
397 $(
398 s if $(s.eq_ignore_ascii_case($suffix))||+ => Ok(Self::Logarithmic(
399 <V>::try_from(Token::DecimalNumericProgramData(num))?,
400 <$unit<U,V>>::new::<$subunit>(V::one())
401 ))
402 ),+,
403 _ => Ok(Self::Linear(<$unit<U, V>>::try_from(value)?))
404 }
405 } else {
406 Err(ErrorCode::DataTypeError.into())
407 }
408 }
409 }
410
411 };
413}
414pub(crate) use impl_logarithmic_unit;
415
416#[allow(unused_macros)]
417macro_rules! impl_unit {
418 ($conversion:path, $unit:ident, $base:ident; $($($suffix:literal)|+ => $subunit:ident),+) => {
419 use uom::{num_traits::Num, si::Units, Conversion};
420
421 impl<'a, U, V> TryFrom<Token<'a>> for $unit<U, V>
422 where
423 U: Units<V> + ?Sized,
424 V: Num + uom::Conversion<V> + TryFrom<Token<'a>, Error=Error>,
425 $base: $conversion,
426 $(
427 $subunit: $conversion
428 ),+
429 {
430 type Error = Error;
431
432 fn try_from(value: Token<'a>) -> Result<Self, Self::Error> {
433 if let Token::DecimalNumericProgramData(_) = value {
434 Ok($unit::new::<$base>(<V>::try_from(value)?))
435 } else if let Token::DecimalNumericSuffixProgramData(num, suffix) = value
436 {
437 match suffix {
438 $(
439 s if $(s.eq_ignore_ascii_case($suffix))||+ => Ok($unit::new::<$subunit>(
440 <V>::try_from(Token::DecimalNumericProgramData(num))?,
441 ))
442 ),+,
443 _ => Err(ErrorCode::IllegalParameterValue.into()),
444 }
445 } else {
446 Err(ErrorCode::DataTypeError.into())
447 }
448 }
449 }
450
451 impl<U, V> ResponseData for $unit<U, V> where U: Units<V> + ?Sized, V: Num + Conversion<V> + ResponseData {
452 fn format_response_data(
453 &self,
454 formatter: &mut dyn Formatter,
455 ) -> core::result::Result<(), Error> {
456 self.value.format_response_data(formatter)
457 }
458 }
459
460 #[cfg(test)]
476 #[allow(non_snake_case)]
477 mod tests {
478
479 extern crate std;
480
481 use crate::{tree::prelude::*};
482 use core::convert::TryInto;
483 use uom::si::f32::*;
484
485 #[test]
486 fn test_suffix_correct() {
487 $(
488 $(
489 let l: $unit = Token::DecimalNumericSuffixProgramData(b"1.0", $suffix)
490 .try_into()
491 .unwrap();
492 assert_eq!(l.get::<super::$subunit>(), 1.0f32);
493 )+
494 )+
495
496 }
497
498 #[test]
499 fn test_suffix_incorrect() {
500 let l: Result<$unit, Error> = Token::DecimalNumericSuffixProgramData(b"1.0", b"POTATO")
502 .try_into();
503 assert_eq!(l, Err(Error::from(ErrorCode::IllegalParameterValue)));
504 let l: Result<$unit, Error> = Token::StringProgramData(b"STRING").try_into();
506 assert_eq!(l, Err(Error::from(ErrorCode::DataTypeError)))
507 }
509
510 #[test]
511 fn test_suffix_default() {
512 let l: $unit = Token::DecimalNumericProgramData(b"1.0").try_into().unwrap();
513 assert_eq!(l.get::< super::$base >(), 1.0f32)
514 }
515 }
516 };
517}
518pub(crate) use impl_unit;