Newer
Older
use serde_derive::{Deserialize, Serialize};
use serde_json::json;
use uom::si::length::nanometer;
use crate::properties::{Properties, Property, Proptype};
use crate::{
error::OpossumError,
optic_ports::OpticPorts,
};
use std::collections::HashMap;
use std::fmt::Debug;
use std::path::{Path, PathBuf};
#[derive(Debug, Default, PartialEq, Clone, Copy, Serialize, Deserialize)]
pub enum SpectrometerType {
/// an ideal energy meter
#[default]
IdealSpectrometer,
/// Ocean Optics HR2000
HR2000,
}
/// (ideal) spectrometer
///
/// It normally measures / displays the spectrum of the incoming light.
///
/// ## Optical Ports
/// - Inputs
/// - `in1`
/// - Outputs
/// - `out1`
///
/// ## Properties
/// - `name`
/// - `spectrometer type
/// `
/// During analysis, the output port contains a replica of the input port similar to a [`Dummy`](crate::nodes::Dummy) node. This way,
/// different dectector nodes can be "stacked" or used somewhere within the optical setup.
pub struct Spectrometer {
light_data: Option<LightData>,
fn create_default_props() -> Properties {
let mut props = Properties::default();
props.set("name", "spectrometer".into());
props.set(
"spectrometer type",
SpectrometerType::IdealSpectrometer.into(),
);
props.set("inverted", false.into());
props
}
impl Default for Spectrometer {
/// create an ideal spectrometer.
props: create_default_props(),
}
}
}
impl Spectrometer {
/// Creates a new [`Spectrometer`] of the given [`SpectrometerType`].
pub fn new(name: &str, spectrometer_type: SpectrometerType) -> Self {
let mut props = create_default_props();
props.set("spectrometer type", spectrometer_type.into());
}
}
/// Returns the meter type of this [`Spectrometer`].
pub fn spectrometer_type(&self) -> SpectrometerType {
let meter_type = self.props.get("spectrometer type").unwrap().prop.clone();
if let Proptype::SpectrometerType(meter_type) = meter_type {
meter_type
} else {
panic!("wrong data format")
}
pub fn set_spectrometer_type(&mut self, meter_type: SpectrometerType) {
self.props.set("spectrometer type", meter_type.into());
fn name(&self) -> &str {
if let Proptype::String(name) = &self.props.get("name").unwrap().prop {
name
} else {
self.node_type()
}
fn node_type(&self) -> &str {
"spectrometer"
}
fn inverted(&self) -> bool {
self.properties().get_bool("inverted").unwrap().unwrap()
}
fn ports(&self) -> OpticPorts {
let mut ports = OpticPorts::new();
ports.add_input("in1").unwrap();
ports.add_output("out1").unwrap();
ports
}
fn analyze(
&mut self,
incoming_data: LightResult,
_analyzer_type: &crate::analyzer::AnalyzerType,
let (src, target) = if self.inverted() {
("out1", "in1")
("in1", "out1")
};
let data = incoming_data.get(src).unwrap_or(&None);
self.light_data = data.clone();
Ok(HashMap::from([(target.into(), data.clone())]))
fn export_data(&self, report_dir: &Path) {
let mut file_path = PathBuf::from(report_dir);
file_path.push(format!("spectrum_{}.svg", self.name()));
data.export(&file_path)
}
}
fn is_detector(&self) -> bool {
true
}
fn properties(&self) -> &Properties {
&self.props
}
fn set_property(&mut self, name: &str, prop: Property) -> OpmResult<()> {
if self.props.set(name, prop).is_none() {
Err(OpossumError::Other("property not defined".into()))
} else {
Ok(())
}
}
fn report(&self) -> serde_json::Value {
let data = &self.light_data;
let mut energy_data = serde_json::Value::Null;
if let Some(LightData::Energy(e)) = data {

Udo Eisenbarth
committed
energy_data = e.spectrum.to_json();
}
json!({"type": self.node_type(),
"name": self.name(),
"energy": energy_data})
}
}
impl Debug for Spectrometer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self.light_data {
Some(data) => match data {
LightData::Energy(data_energy) => {
let spectrum_range = data_energy.spectrum.range();
write!(
f,
"Spectrum {:.3} - {:.3} nm (Type: {:?})",
spectrum_range.start.get::<nanometer>(),
spectrum_range.end.get::<nanometer>(),
self.spectrometer_type()
)
}
_ => write!(f, "no spectrum data to display"),
},
None => write!(f, "no data"),
}
}
}
impl Dottable for Spectrometer {
fn node_color(&self) -> &str {
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::{analyzer::AnalyzerType, lightdata::DataEnergy, spectrum::create_he_ne_spectrum};
let node = Spectrometer::default();
assert!(node.light_data.is_none());
SpectrometerType::IdealSpectrometer
);
assert_eq!(node.name(), "spectrometer");
assert_eq!(node.node_type(), "spectrometer");
assert_eq!(node.is_detector(), true);
assert_eq!(node.inverted(), false);
assert_eq!(node.node_color(), "lightseagreen");
assert!(node.as_group().is_err());
let meter = Spectrometer::new("test", SpectrometerType::HR2000);
assert_eq!(meter.name(), "test");
assert!(meter.light_data.is_none());
assert_eq!(meter.spectrometer_type(), SpectrometerType::HR2000);
let mut meter = Spectrometer::new("test", SpectrometerType::IdealSpectrometer);
meter.set_spectrometer_type(SpectrometerType::HR2000);
assert_eq!(meter.spectrometer_type(), SpectrometerType::HR2000);
}
#[test]
fn ports() {
let meter = Spectrometer::default();
assert_eq!(meter.ports().inputs(), vec!["in1"]);
assert_eq!(meter.ports().outputs(), vec!["out1"]);
fn inverted() {
let mut node = Spectrometer::default();
node.set_property("inverted", true.into()).unwrap();
assert_eq!(node.inverted(), true)
}
#[test]
fn analyze_ok() {
let mut node = Spectrometer::default();
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
let input_light = LightData::Energy(DataEnergy {
spectrum: create_he_ne_spectrum(1.0),
});
input.insert("in1".into(), Some(input_light.clone()));
let output = node.analyze(input, &AnalyzerType::Energy);
assert!(output.is_ok());
let output = output.unwrap();
assert!(output.contains_key("out1".into()));
assert_eq!(output.len(), 1);
let output = output.get("out1".into()).unwrap();
assert!(output.is_some());
let output = output.clone().unwrap();
assert_eq!(output, input_light);
}
#[test]
fn analyze_wrong() {
let mut node =Spectrometer::default();
let mut input = LightResult::default();
let input_light = LightData::Energy(DataEnergy {
spectrum: create_he_ne_spectrum(1.0),
});
input.insert("wrong".into(), Some(input_light.clone()));
let output = node.analyze(input, &AnalyzerType::Energy);
assert!(output.is_ok());
let output = output.unwrap();
let output = output.get("out1".into()).unwrap();
assert!(output.is_none());
}
#[test]
fn analyze_inverse() {
let mut node = Spectrometer::default();
node.set_property("inverted", true.into()).unwrap();
let mut input = LightResult::default();
let input_light = LightData::Energy(DataEnergy {
spectrum: create_he_ne_spectrum(1.0),
});
input.insert("out1".into(), Some(input_light.clone()));
let output = node.analyze(input, &AnalyzerType::Energy);
assert!(output.is_ok());
let output = output.unwrap();
assert!(output.contains_key("in1".into()));
assert_eq!(output.len(), 1);
let output = output.get("in1".into()).unwrap();
assert!(output.is_some());
let output = output.clone().unwrap();
assert_eq!(output, input_light);