1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
//! Pipeline system for calculating values.

#![deny(
    missing_docs,
    missing_debug_implementations,
    trivial_casts,
    trivial_numeric_casts,
    unsafe_code,
    unstable_features,
    unused_import_braces,
    unused_qualifications
)]

#[macro_use]
extern crate serde_derive;

use snafu;

pub mod bins;
pub mod error;

mod item;
mod run;
mod sortable_float;

pub use self::item::Item;
pub use crate::error::Error;
pub use crate::run::{run_bin, run_bin_with_calibration};
pub use crate::sortable_float::{Direction, HasDirection, HasReverse};
pub use decorum::{R32, R64};

use indexmap::IndexMap;
use snafu::OptionExt;
use std::fmt;

/// The scope of a bin.
#[derive(Clone)]
pub enum Scope {
    /// The top level of a scope, created in-memory.
    Base,
    /// Loaded from a file.
    File(String),
    /// Nested insid a parent scope.
    Sub {
        /// The parent scope.
        parent: Box<Scope>,
        /// The name of the child.
        name: String,
    },
}

impl Default for Scope {
    fn default() -> Self {
        Scope::Base
    }
}

impl Scope {
    /// Produce a child scope.
    pub fn enter<N: AsRef<str>>(&self, name: N) -> Self {
        Scope::Sub {
            parent: Box::new(self.clone()),
            name: name.as_ref().to_string(),
        }
    }
}

impl fmt::Debug for Scope {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Scope::Base => write!(f, "in-memory"),
            Scope::File(ref file) => write!(f, "file \"{}\"", file),
            Scope::Sub {
                ref parent,
                ref name,
            } => write!(f, "{:?}→\"{}\"", parent, name),
        }
    }
}

/// The result type used throughout this crate.
pub type Result<T, E = Error> = std::result::Result<T, E>;

/// The source of a calibration.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case", tag = "source", content = "data")]
pub enum CalibrationSource {
    /// The calibration gets loaded from a file.
    File {
        /// The path of the calibration definition file.
        path: String,
    },
    /// The calibration is directly defined in the definition.
    Embedded {
        /// The values of the calibration.
        curve: Vec<(f64, f64)>,
    },
    /// The calibration is found by an identifier.
    Identifier {
        /// The identifier of the calibration.
        id: String,
    },
}

/// An indicator on whether to stop or continue processing.
#[derive(PartialEq, Debug, Clone)]
#[must_use]
pub enum Proceed {
    /// Stop processing.
    Stop,
    /// Continue processing.
    Continue,
}

/// A trait for getting calibrations by a calibration source description.
pub trait GetCalibration {
    /// Get the calibration according to it's source description.
    fn calibration(
        &mut self,
        source: &CalibrationSource,
    ) -> Result<IndexMap<R64, R64>>;
}

/// A trait describing a single calibration.
pub trait IsCalibration {
    /// Get the calibration data.
    fn calibration(&self) -> Result<IndexMap<R64, R64>>;
}

impl<T: IsCalibration> GetCalibration for IndexMap<String, T> {
    fn calibration(
        &mut self,
        mode: &CalibrationSource,
    ) -> Result<IndexMap<R64, R64>> {
        match *mode {
            CalibrationSource::File { .. } => error::GetCalibration {
                message: "IndexMap of calibrations cannot \
                          get calibration from file"
                    .to_string(),
            }
            .fail(),
            CalibrationSource::Embedded { ref curve } => Ok(curve
                .iter()
                .map(|&(x, y)| (R64::from(x), R64::from(y)))
                .collect::<IndexMap<R64, R64>>()),
            CalibrationSource::Identifier { ref id } => Ok(self
                .get(id)
                .context(error::GetCalibration {
                    message: format!("Calibration {:?} not present.", id),
                })?
                .calibration()?),
        }
    }
}

type DateTime = ::chrono::DateTime<::chrono::offset::Utc>;

/// A single point inside a calibration.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CalibrationPoint {
    /// The coordinates of the point.
    pub coordinates: (f64, f64),
    /// The results associated with the point, e.g. calculated or added
    /// when the point was measured.
    ///
    /// This might be e.g. the weight that was applied to the measurement
    /// cell during calibration.
    ///
    /// It has no relevance for calculation of calibration values, but
    /// is only for informational purposes.
    #[serde(default, skip_serializing_if = "IndexMap::is_empty")]
    pub results: IndexMap<String, Item>,
}

/// A calibration.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Calibration {
    /// The points inside the calibration.
    pub points: Vec<CalibrationPoint>,
    /// The timestamp of the moment when the calibration was last modified.
    pub timestamp: DateTime,
}

impl Calibration {
    /// Sort the points by their x coordinate.
    pub fn sort_points(&mut self) {
        self.points.sort_by(|a, b| {
            a.coordinates.0.partial_cmp(&b.coordinates.0).unwrap()
        });
    }
}

impl IsCalibration for Calibration {
    fn calibration(&self) -> Result<IndexMap<R64, R64>> {
        let mut p = self
            .points
            .iter()
            .map(|p| {
                let (x, y) = p.coordinates;
                (R64::from(x), R64::from(y))
            })
            .collect::<IndexMap<R64, R64>>();
        p.sort_keys();
        Ok(p)
    }
}