-
Udo Eisenbarth authoredUdo Eisenbarth authored
energy_meter.rs 6.62 KiB
#![warn(missing_docs)]
use serde_derive::{Deserialize, Serialize};
use serde_json::{json, Number};
use crate::dottable::Dottable;
use crate::lightdata::LightData;
use crate::properties::{Properties, Property, Proptype};
use crate::{
error::OpossumError,
optic_ports::OpticPorts,
optical::{LightResult, Optical},
};
use std::collections::HashMap;
use std::fmt::Debug;
type Result<T> = std::result::Result<T, OpossumError>;
#[non_exhaustive]
#[derive(Debug, Default, PartialEq, Clone, Copy, Serialize, Deserialize)]
/// Type of the [`EnergyMeter`]. This is currently not used.
pub enum Metertype {
/// an ideal energy meter
#[default]
IdealEnergyMeter,
/// an ideal power meter (currently not used)
IdealPowerMeter,
}
/// (ideal) energy / power meter.
///
/// It normally measures the total energy of the incoming light regardless of the wavelength, position, angle, polarization etc...
///
/// ## Optical Ports
/// - Inputs
/// - `in1`
/// - Outputs
/// - `out1`
///
/// 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 in between arbitrary optic nodes.
pub struct EnergyMeter {
light_data: Option<LightData>,
//meter_type: Metertype,
props: Properties,
}
fn create_default_props() -> Properties {
let mut props = Properties::default();
props.set("name", "energy meter".into());
props.set("inverted", false.into());
props.set("meter type", Metertype::default().into());
props
}
impl Default for EnergyMeter {
fn default() -> Self {
Self {
light_data: Default::default(),
//meter_type: Default::default(),
props: create_default_props(),
}
}
}
impl EnergyMeter {
/// Creates a new [`EnergyMeter`] of the given [`Metertype`].
pub fn new(name: &str, meter_type: Metertype) -> Self {
let mut props = create_default_props();
props.set("name", name.into());
props.set("meter type", meter_type.into());
EnergyMeter {
light_data: None,
props,
}
}
/// Returns the meter type of this [`EnergyMeter`].
pub fn meter_type(&self) -> Metertype {
let meter_type=self.props.get("meter type").unwrap().prop.clone();
if let Proptype::Metertype(meter_type)=meter_type {
meter_type
} else {
panic!("wrong data format")
}
}
/// Sets the meter type of this [`EnergyMeter`].
pub fn set_meter_type(&mut self, meter_type: Metertype) {
self.props.set("meter type", meter_type.into());
}
}
impl Optical for EnergyMeter {
fn set_name(&mut self, name: &str) {
self.props.set("name", name.into());
}
fn name(&self) -> &str {
if let Some(value) = self.props.get("name") {
if let Proptype::String(name) = &value.prop {
return name;
}
}
panic!("wrong format");
}
fn node_type(&self) -> &str {
"energy meter"
}
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,
) -> Result<LightResult> {
if let Some(data) = incoming_data.get("in1") {
self.light_data = data.clone();
Ok(HashMap::from([("out1".into(), data.clone())]))
} else {
Ok(HashMap::from([("out2".into(), None)]))
}
}
fn is_detector(&self) -> bool {
true
}
fn properties(&self) -> &Properties {
&self.props
}
fn set_property(&mut self, name: &str, prop: Property) -> Result<()> {
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 {
energy_data =
serde_json::Value::Number(Number::from_f64(e.spectrum.total_energy()).unwrap())
}
json!({"type": self.node_type(),
"name": self.name(),
"energy": energy_data})
}
}
impl Debug for EnergyMeter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self.light_data {
Some(data) => write!(f, "{} (Type: {:?})", data, self.meter_type()),
None => write!(f, "no data"),
}
}
}
impl Dottable for EnergyMeter {
fn node_color(&self) -> &str {
"whitesmoke"
}
}
#[cfg(test)]
mod test {
use crate::{analyzer::AnalyzerType, lightdata::DataEnergy, spectrum::create_he_ne_spectrum};
use super::*;
#[test]
fn new() {
let meter = EnergyMeter::new("test", Metertype::IdealEnergyMeter);
assert!(meter.light_data.is_none());
assert_eq!(meter.meter_type(), Metertype::IdealEnergyMeter);
}
#[test]
fn default() {
let meter = EnergyMeter::default();
assert!(meter.light_data.is_none());
assert_eq!(meter.meter_type(), Metertype::IdealEnergyMeter);
assert_eq!(meter.node_type(), "energy meter");
assert_eq!(meter.is_detector(), true);
assert_eq!(meter.node_color(), "whitesmoke");
assert_eq!(meter.name(), "energy meter");
}
#[test]
fn meter_type() {
let meter = EnergyMeter::new("test", Metertype::IdealEnergyMeter);
assert_eq!(meter.meter_type(), Metertype::IdealEnergyMeter);
}
#[test]
fn set_meter_type() {
let mut meter = EnergyMeter::new("test", Metertype::IdealEnergyMeter);
meter.set_meter_type(Metertype::IdealPowerMeter);
assert_eq!(meter.meter_type(), Metertype::IdealPowerMeter);
}
#[test]
fn ports() {
let meter = EnergyMeter::new("test", Metertype::IdealEnergyMeter);
let ports = meter.ports();
assert_eq!(ports.inputs(), vec!["in1"]);
assert_eq!(ports.outputs(), vec!["out1"]);
}
#[test]
fn analyze() {
let mut meter = EnergyMeter::new("test", Metertype::IdealEnergyMeter);
let mut input = LightResult::default();
input.insert(
"in1".into(),
Some(LightData::Energy(DataEnergy {
spectrum: create_he_ne_spectrum(1.0),
})),
);
let result = meter.analyze(input, &AnalyzerType::Energy);
assert!(result.is_ok());
}
}