use crossbeam::queue::ArrayQueue;
use std::collections::HashMap;
use crate::event::{RREvent, RawRREvent};
use crate::group::RapidRecorderGroup;
use crate::group_handle::RapidRecorderGroupHandle;
use crate::latest_reading_holder::LatestReadingHolder;
use crate::named_usize::ValidRapidRecorderNamedUsize;
pub mod defaults;
pub mod event;
pub mod group;
pub mod group_handle;
pub mod latest_reading_holder;
pub mod named_usize;
pub mod prelude {
pub use crate::RapidRecorder;
pub use crate::defaults::{DefaultIndexDimmension, DefaultSamplingFrequency};
pub use crate::group::RapidRecorderGroup;
pub use crate::impl_rapid_recorder_named_usize;
pub use crate::named_usize::ValidRapidRecorderNamedUsize;
}
pub enum RRDuplicateEventIdHandling {
KeepOnlyFirst,
KeepOnlyLast,
KeepBoth,
}
pub struct RapidRecorder<
IndexDimmension: ValidRapidRecorderNamedUsize,
ReadingName: ValidRapidRecorderNamedUsize,
> {
buffer: ArrayQueue<RawRREvent>,
latest_readings: LatestReadingHolder,
phantom: std::marker::PhantomData<IndexDimmension>,
phantom2: std::marker::PhantomData<ReadingName>,
}
impl<IterationIndex: ValidRapidRecorderNamedUsize, ReadingName: ValidRapidRecorderNamedUsize>
RapidRecorder<IterationIndex, ReadingName>
{
pub fn new(max_history_length: usize, max_reading_types: usize) -> Self {
#[cfg(debug_assertions)]
{
use crate::named_usize::validate_enum_for_recorder;
let actual_count = match validate_enum_for_recorder::<ReadingName>() {
Ok(count) => count,
Err(err) => panic!("ReadingName enum validation failed: {}", err),
};
if max_reading_types != actual_count {
panic!(
"max_reading_types ({}) doesn't match the actual number of enum variants ({}).\n\
You should pass the exact number of variants in your enum to avoid memory safety issues.",
max_reading_types, actual_count
);
}
}
Self {
buffer: ArrayQueue::new(max_history_length),
latest_readings: LatestReadingHolder::new(max_reading_types),
phantom: std::marker::PhantomData,
phantom2: std::marker::PhantomData,
}
}
pub fn add_group<SampleRate: ValidRapidRecorderNamedUsize>(
&self,
group: RapidRecorderGroup<SampleRate, IterationIndex>,
) -> RapidRecorderGroupHandle<'_, SampleRate, ReadingName, IterationIndex> {
RapidRecorderGroupHandle::new(group, self)
}
pub fn raw_history(&self) -> &ArrayQueue<RawRREvent> {
&self.buffer
}
pub fn convenient_pop(&self) -> Option<RREvent<ReadingName, IterationIndex>> {
self.buffer.pop().map(|e| e.to_rr_event())
}
pub fn sorted_history(
&self,
) -> HashMap<IterationIndex, Vec<RREvent<ReadingName, IterationIndex>>> {
let mut organized: HashMap<IterationIndex, Vec<RREvent<ReadingName, IterationIndex>>> =
HashMap::new();
while let Some(event) = self.buffer.pop() {
let rr_event = event.to_rr_event();
organized
.entry(rr_event.iteration_index)
.or_default()
.push(rr_event);
}
for events in organized.values_mut() {
events.sort_by_key(|e| e.id);
}
organized
}
pub fn sorted_history_with_duplicate_handling(
&self,
duplicate_event_id_handling: RRDuplicateEventIdHandling,
) -> HashMap<IterationIndex, Vec<RREvent<ReadingName, IterationIndex>>> {
let mut organized: HashMap<IterationIndex, Vec<RREvent<ReadingName, IterationIndex>>> =
HashMap::new();
while let Some(event) = self.buffer.pop() {
let rr_event = event.to_rr_event();
organized
.entry(rr_event.iteration_index)
.or_default()
.push(rr_event);
}
for events in organized.values_mut() {
match duplicate_event_id_handling {
RRDuplicateEventIdHandling::KeepOnlyFirst => {
let mut seen_ids = std::collections::HashSet::new();
events.retain(|e| seen_ids.insert(e.id));
}
RRDuplicateEventIdHandling::KeepOnlyLast => {
let mut seen_ids = std::collections::HashSet::new();
events.reverse();
events.retain(|e| seen_ids.insert(e.id));
events.reverse();
}
RRDuplicateEventIdHandling::KeepBoth => {
}
}
events.sort_by_key(|e| e.id);
}
organized
}
pub fn _add_reading(&self, variable_name: usize, value: f64) {
self.latest_readings.set_value(variable_name, value);
}
#[inline(always)]
pub fn _save_event(&self, index_type: usize, id: usize) {
let (readings, changed): (Vec<f64>, Vec<bool>) = self.latest_readings.snapshot();
let event = RawRREvent {
record_id: id,
id_type: index_type,
readings,
changed,
};
let _ = self.buffer.push(event);
}
}