use ommui_string_patterns::{
freetextstring, freetextstring_maybe_empty, idstring,
idstring_maybe_empty,
};
use std::collections::BTreeMap;
use {
chrono, ommui_file_loading, ErrorKind, LoadFromPathPattern, Result,
Uuid,
};
pub mod handle;
mod loadable_device;
pub use self::loadable_device::LoadableDevice;
pub trait UnitRepresentationConvertible {
fn to_representation(
&self,
representation: &UnitRepresentationDescription,
) -> Self;
fn from_representation(
&self,
representation: &UnitRepresentationDescription,
) -> Self;
}
impl UnitRepresentationConvertible for f64 {
fn to_representation(
&self,
representation: &UnitRepresentationDescription,
) -> Self {
(self - representation.offset) / representation.factor
}
fn from_representation(
&self,
representation: &UnitRepresentationDescription,
) -> Self {
self * representation.factor + representation.offset
}
}
pub type Translation = BTreeMap<String, String>;
pub type DirectoryListing = BTreeMap<String, DirectoryListingEntry>;
pub type MeasurementResults = BTreeMap<String, f64>;
pub type Icon = String;
impl LoadFromPathPattern for DirectoryListing {
fn load_from_path_pattern(pattern: &str) -> Result<DirectoryListing> {
Ok(ommui_file_loading::load_directory_listing(pattern)?
.into_iter()
.map(|k| (k, DirectoryListingEntry::default()))
.collect())
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct DeviceId {
#[serde(with = "freetextstring")]
pub serial_number: String,
pub device_uuid: Uuid,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct DeviceDescription {
#[serde(rename = "_caption", with = "freetextstring")]
pub caption: String,
#[serde(with = "freetextstring")]
pub manufacturer: String,
#[serde(with = "freetextstring")]
pub model_number: String,
#[serde(with = "freetextstring")]
pub revision: String,
#[serde(with = "idstring")]
pub icon_id: String,
pub uuid: Uuid,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ProfileDescription {
#[serde(with = "idstring")]
pub sampling_id: String,
#[serde(rename = "_caption", with = "freetextstring")]
pub caption: String,
#[serde(rename = "_description", with = "freetextstring")]
pub description: String,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub parameters: BTreeMap<String, ProfileParameterDescription>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub results: Vec<ResultReference>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub curves: Vec<ProfileCurveDescription>,
pub translatable: bool,
#[serde(default, skip_serializing_if = "Uuid::is_nil")]
pub parent: Uuid,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ProfileCurveDescription {
pub axes: BTreeMap<String, ProfileAxisDescription>,
#[serde(with = "idstring")]
pub curve_id: String,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ProfileAxisDescription {
#[serde(with = "idstring")]
pub representation: String,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub enum ParameterMode {
ChangeableWithDefaultValue,
ChangeableWithRememberedValue,
ChangeableWithFixedValue,
HiddenWithFixedValue,
}
impl ParameterMode {
pub fn is_hidden(&self) -> bool {
match *self {
ParameterMode::ChangeableWithDefaultValue
| ParameterMode::ChangeableWithRememberedValue
| ParameterMode::ChangeableWithFixedValue => false,
ParameterMode::HiddenWithFixedValue => true,
}
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase", untagged)]
pub enum Parameter {
Numeric(f64),
Boolean(bool),
Enumeration(String),
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ProfileParameterDescription {
pub value: Parameter,
#[serde(
with = "idstring_maybe_empty",
default,
skip_serializing_if = "String::is_empty"
)]
pub representation: String,
pub mode: ParameterMode,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct SampleMetadata {
pub profile: Uuid,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub parameters: BTreeMap<String, Parameter>,
pub uuid: Uuid,
#[serde(
with = "freetextstring",
default,
skip_serializing_if = "String::is_empty"
)]
pub identifier: String,
pub source: Source,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct MeasurementMetadata {
pub active: bool,
pub timestamp: chrono::DateTime<chrono::offset::Utc>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub properties: BTreeMap<String, Parameter>,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct SamplingDescription {
#[serde(rename = "_caption", with = "freetextstring")]
pub caption: String,
pub results: BTreeMap<String, ResultDescription>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub curves: BTreeMap<String, CurveDescription>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub parameters: BTreeMap<String, ParameterDescription>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub properties: BTreeMap<String, ParameterDescription>,
#[serde(with = "idstring")]
pub icon_id: String,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ResultDescription {
#[serde(rename = "_caption", with = "freetextstring")]
pub caption: String,
#[serde(rename = "_shortCaption", with = "freetextstring")]
pub short_caption: String,
pub unit: UnitReference,
pub resolution: f64,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct BooleanParameterDescription {
#[serde(rename = "_caption", with = "freetextstring")]
caption: String,
#[serde(rename = "_shortCaption", with = "freetextstring")]
short_caption: String,
#[serde(rename = "_valueCaptionTrue", with = "freetextstring")]
value_caption_true: String,
#[serde(rename = "_valueCaptionFalse", with = "freetextstring")]
value_caption_false: String,
affects_sampling_job: bool,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct NumericParameterDescription {
#[serde(rename = "_caption", with = "freetextstring")]
caption: String,
#[serde(rename = "_shortCaption", with = "freetextstring")]
short_caption: String,
unit: UnitReference,
resolution: f64,
maximum: f64,
minimum: f64,
affects_sampling_job: bool,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct EnumerationParameterDescription {
#[serde(rename = "_caption", with = "freetextstring")]
caption: String,
#[serde(rename = "_shortCaption", with = "freetextstring")]
short_caption: String,
enumeration_values: BTreeMap<String, EnumerationValueDescription>,
affects_sampling_job: bool,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename_all = "PascalCase", tag = "type")]
pub enum ParameterDescription {
#[serde(rename_all = "camelCase")]
Numeric(NumericParameterDescription),
#[serde(rename_all = "camelCase")]
Boolean(BooleanParameterDescription),
#[serde(rename_all = "camelCase")]
Enumeration(EnumerationParameterDescription),
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct EnumerationValueDescription {
#[serde(rename = "_caption", with = "freetextstring")]
pub caption: String,
#[serde(rename = "_shortCaption", with = "freetextstring")]
pub short_caption: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub value: Option<f64>,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct Curve {
pub points: Vec<Vec<f64>>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub markers: BTreeMap<String, Vec<f64>>,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct CurveDescription {
#[serde(rename = "_caption", with = "freetextstring")]
pub caption: String,
#[serde(rename = "_shortCaption", with = "freetextstring")]
pub short_caption: String,
pub coordinate_system: CoordinateSystemDescription,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub markers: BTreeMap<String, CurveMarkerDescription>,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct Cartesian2dCoordinateSystemDescription {
x: AxisDescription,
y: AxisDescription,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct PolarCoordinateSystemDescription {
angle: AxisDescription,
radius: AxisDescription,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename_all = "PascalCase", tag = "type")]
pub enum CoordinateSystemDescription {
#[serde(rename_all = "camelCase")]
Cartesian2d(Cartesian2dCoordinateSystemDescription),
#[serde(rename_all = "camelCase")]
Polar(PolarCoordinateSystemDescription),
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct AxisDescription {
#[serde(rename = "_caption", with = "freetextstring")]
pub caption: String,
#[serde(rename = "_shortCaption", with = "freetextstring")]
pub short_caption: String,
pub unit: UnitReference,
pub resolution: f64,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct CurveMarkerDescription {
#[serde(rename = "_caption", with = "freetextstring")]
pub caption: String,
}
#[derive(Default, Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct DirectoryListingEntry {
#[serde(
with = "freetextstring",
default,
skip_serializing_if = "String::is_empty"
)]
pub description: String,
#[serde(
with = "freetextstring_maybe_empty",
default,
skip_serializing_if = "String::is_empty"
)]
pub identifier: String,
#[serde(default, skip_serializing_if = "Uuid::is_nil")]
pub profile: Uuid,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub enum Source {
Measurement,
Demo,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct UnitDescription {
pub representations: BTreeMap<String, UnitRepresentationDescription>,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct UnitRepresentationDescription {
#[serde(rename = "_caption", with = "freetextstring_maybe_empty")]
pub caption: String,
#[serde(
rename = "_shortCaption",
with = "freetextstring_maybe_empty"
)]
pub short_caption: String,
pub factor: f64,
pub offset: f64,
pub measurement_system: MeasurementSystem,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename_all = "PascalCase", deny_unknown_fields)]
pub enum MeasurementSystem {
Metric,
Imperial,
Any,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct UnitReference {
#[serde(with = "idstring")]
pub unit_id: String,
pub representations: Vec<String>,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ResultReference {
#[serde(with = "idstring")]
pub result_id: String,
#[serde(with = "idstring")]
pub representation: String,
}
#[cfg(test)]
mod tests {
use serde_json::from_str;
use super::*;
#[test]
fn test_device_id() {
let json = json!({
"serialNumber": "SN1234",
"deviceUuid": "e7ab4013-fadd-440b-9072-9dd88b220b59",
})
.to_string();
let deserialized: DeviceId = from_str(&json).unwrap();
let d = DeviceId {
serial_number: "SN1234".to_string(),
device_uuid: Uuid::parse_str(
"e7ab4013-fadd-440b-9072-9dd88b220b59",
)
.unwrap(),
};
assert_eq!(d, deserialized);
}
#[test]
#[should_panic]
#[allow(unused_variables)]
fn test_device_id_with_invalid_serialnumber() {
let json = json!({
"serialNumber": " SN600",
"deviceUuid": "e7ab4013-fadd-440b-9072-9dd88b220b59",
})
.to_string();
let deserialized: DeviceId = from_str(&json).unwrap();
}
#[test]
#[should_panic]
#[allow(unused_variables)]
fn test_device_id_with_invalid_uuid() {
let json = json!({
"serialNumber": "SN600",
"deviceUuid": "abcdefgh",
})
.to_string();
let deserialized: DeviceId = from_str(&json).unwrap();
}
#[test]
fn test_device_description() {
let json = json!({
"_caption": "My Super Device",
"manufacturer": "My Super Company",
"modelNumber": "SDX1000",
"revision": "v1.0",
"iconId": "myIcon",
"uuid": "2b8fb031-adab-48f4-81ff-479090858909",
})
.to_string();
let deserialized: DeviceDescription = from_str(&json).unwrap();
let d = DeviceDescription {
caption: "My Super Device".to_string(),
manufacturer: "My Super Company".to_owned(),
model_number: "SDX1000".to_owned(),
revision: "v1.0".to_owned(),
icon_id: "myIcon".to_owned(),
uuid: Uuid::parse_str("2b8fb031-adab-48f4-81ff-479090858909")
.unwrap(),
};
assert_eq!(d, deserialized);
}
}