use crate::binsdim0::BinsDim0; use crate::framable::FrameType; use crate::framable::FrameTypeStatic; use crate::timebin::ChooseIndicesForTimeBin; use crate::timebin::ChooseIndicesForTimeBinEvents; use crate::timebin::TimeAggregatorCommonV0Func; use crate::timebin::TimeAggregatorCommonV0Trait; use crate::timebin::TimeBinnerCommonV0Func; use crate::timebin::TimeBinnerCommonV0Trait; use crate::IsoDateTime; use crate::RangeOverlapInfo; use crate::TimeBinnableType; use crate::TimeBinnableTypeAggregator; use err::Error; use items_0::collect_s::Collectable; use items_0::collect_s::Collected; use items_0::collect_s::Collector; use items_0::collect_s::CollectorType; use items_0::collect_s::ToJsonBytes; use items_0::collect_s::ToJsonResult; use items_0::container::ByteEstimate; use items_0::framable::FrameTypeInnerStatic; use items_0::overlap::HasTimestampDeque; use items_0::scalar_ops::ScalarOps; use items_0::test::f32_iter_cmp_near; use items_0::timebin::TimeBinnable; use items_0::timebin::TimeBinned; use items_0::timebin::TimeBinner; use items_0::AppendAllFrom; use items_0::AppendEmptyBin; use items_0::Appendable; use items_0::AsAnyMut; use items_0::AsAnyRef; use items_0::Empty; use items_0::Events; use items_0::EventsNonObj; use items_0::HasNonemptyFirstBin; use items_0::MergeError; use items_0::Resettable; use items_0::TypeName; use items_0::WithLen; use netpod::is_false; use netpod::log::*; use netpod::range::evrange::NanoRange; use netpod::range::evrange::SeriesRange; use netpod::timeunits::MS; use netpod::timeunits::SEC; use netpod::BinnedRange; use netpod::BinnedRangeEnum; use netpod::TsNano; use serde::Deserialize; use serde::Serialize; use std::any; use std::any::Any; use std::collections::VecDeque; use std::fmt; use std::mem; #[allow(unused)] macro_rules! trace_init { ($($arg:tt)*) => ( if true { trace!($($arg)*); }) } #[allow(unused)] macro_rules! trace_ingest_item { ($($arg:tt)*) => ( if true { trace!($($arg)*); }) } #[allow(unused)] macro_rules! trace_ingest_event { ($($arg:tt)*) => ( if false { trace!($($arg)*); }) } #[allow(unused)] macro_rules! trace2 { ($($arg:tt)*) => ( if false { trace!($($arg)*); }) } #[allow(unused)] macro_rules! trace_binning { ($($arg:tt)*) => ( if true { trace!($($arg)*); }) } #[allow(unused)] macro_rules! debug_ingest { ($($arg:tt)*) => ( if true { trace!($($arg)*); }) } #[derive(Clone, PartialEq, Serialize, Deserialize)] pub struct EventsDim0NoPulse { pub tss: VecDeque, pub values: VecDeque, } impl From> for EventsDim0 { fn from(value: EventsDim0NoPulse) -> Self { let pulses = vec![0; value.tss.len()].into(); Self { tss: value.tss, pulses, values: value.values, } } } #[derive(Clone, PartialEq, Serialize, Deserialize)] pub struct EventsDim0 { pub tss: VecDeque, pub pulses: VecDeque, pub values: VecDeque, } impl EventsDim0 { pub fn type_name() -> &'static str { std::any::type_name::() } pub fn push_back(&mut self, ts: u64, pulse: u64, value: STY) { self.tss.push_back(ts); self.pulses.push_back(pulse); self.values.push_back(value); } pub fn push_front(&mut self, ts: u64, pulse: u64, value: STY) { self.tss.push_front(ts); self.pulses.push_front(pulse); self.values.push_front(value); } pub fn serde_id() -> &'static str { "EventsDim0" } pub fn tss(&self) -> &VecDeque { &self.tss } // only for testing at the moment pub fn private_values_ref(&self) -> &VecDeque { &self.values } pub fn private_values_mut(&mut self) -> &mut VecDeque { &mut self.values } } impl AsAnyRef for EventsDim0 where STY: ScalarOps, { fn as_any_ref(&self) -> &dyn Any { self } } impl AsAnyMut for EventsDim0 where STY: ScalarOps, { fn as_any_mut(&mut self) -> &mut dyn Any { self } } impl Empty for EventsDim0 { fn empty() -> Self { Self { tss: VecDeque::new(), pulses: VecDeque::new(), values: VecDeque::new(), } } } impl fmt::Debug for EventsDim0 where STY: fmt::Debug, { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { if false { write!( fmt, "{} {{ count {} ts {:?} vals {:?} }}", self.type_name(), self.tss.len(), self.tss.iter().map(|x| x / SEC).collect::>(), self.values, ) } else { write!( fmt, "{} {{ count {} ts {:?} .. {:?} vals {:?} .. {:?} }}", self.type_name(), self.tss.len(), self.tss.front().map(|&x| TsNano::from_ns(x)), self.tss.back().map(|&x| TsNano::from_ns(x)), self.values.front(), self.values.back(), ) } } } impl WithLen for EventsDim0 { fn len(&self) -> usize { self.tss.len() } } impl ByteEstimate for EventsDim0 { fn byte_estimate(&self) -> u64 { // TODO // Should use a better estimate for waveform and string types, // or keep some aggregated byte count on push. let n = self.len(); if n == 0 { 0 } else { // TODO use the actual size of one/some of the elements. let i = n * 2 / 3; let sty_bytes = self.values[i].byte_estimate(); (n as u64 * (8 + 8 + sty_bytes)) as u64 } } } impl Resettable for EventsDim0 { fn reset(&mut self) { self.tss.clear(); self.pulses.clear(); self.values.clear(); } } impl HasTimestampDeque for EventsDim0 { fn timestamp_min(&self) -> Option { self.tss.front().map(|x| *x) } fn timestamp_max(&self) -> Option { self.tss.back().map(|x| *x) } fn pulse_min(&self) -> Option { self.pulses.front().map(|x| *x) } fn pulse_max(&self) -> Option { self.pulses.back().map(|x| *x) } } items_0::impl_range_overlap_info_events!(EventsDim0); impl ChooseIndicesForTimeBin for EventsDim0 { fn choose_indices_unweight(&self, beg: u64, end: u64) -> (Option, usize, usize) { ChooseIndicesForTimeBinEvents::choose_unweight(beg, end, &self.tss) } fn choose_indices_timeweight(&self, beg: u64, end: u64) -> (Option, usize, usize) { ChooseIndicesForTimeBinEvents::choose_timeweight(beg, end, &self.tss) } } impl TimeBinnableType for EventsDim0 where STY: ScalarOps, { type Output = BinsDim0; type Aggregator = EventsDim0Aggregator; fn aggregator(range: SeriesRange, x_bin_count: usize, do_time_weight: bool) -> Self::Aggregator { panic!("TODO remove, should no longer be used"); let self_name = any::type_name::(); debug!( "TimeBinnableType for {self_name} aggregator() range {:?} x_bin_count {} do_time_weight {}", range, x_bin_count, do_time_weight ); Self::Aggregator::new(range, do_time_weight) } } #[derive(Debug, Serialize, Deserialize)] pub struct EventsDim0ChunkOutput { tss: VecDeque, pulses: VecDeque, values: VecDeque, scalar_type: String, } impl EventsDim0ChunkOutput {} #[derive(Debug)] pub struct EventsDim0Collector { vals: EventsDim0, range_final: bool, timed_out: bool, needs_continue_at: bool, } impl EventsDim0Collector { pub fn self_name() -> &'static str { any::type_name::() } pub fn new() -> Self { debug!("EventsDim0Collector NEW"); Self { vals: EventsDim0::empty(), range_final: false, timed_out: false, needs_continue_at: false, } } } impl WithLen for EventsDim0Collector { fn len(&self) -> usize { WithLen::len(&self.vals) } } impl ByteEstimate for EventsDim0Collector { fn byte_estimate(&self) -> u64 { ByteEstimate::byte_estimate(&self.vals) } } #[derive(Debug, Serialize, Deserialize)] pub struct EventsDim0CollectorOutput { #[serde(rename = "tsAnchor")] ts_anchor_sec: u64, #[serde(rename = "tsMs")] ts_off_ms: VecDeque, #[serde(rename = "tsNs")] ts_off_ns: VecDeque, #[serde(rename = "pulseAnchor")] pulse_anchor: u64, #[serde(rename = "pulseOff")] pulse_off: VecDeque, #[serde(rename = "values")] values: VecDeque, #[serde(rename = "rangeFinal", default, skip_serializing_if = "is_false")] range_final: bool, #[serde(rename = "timedOut", default, skip_serializing_if = "is_false")] timed_out: bool, #[serde(rename = "continueAt", default, skip_serializing_if = "Option::is_none")] continue_at: Option, } impl EventsDim0CollectorOutput { pub fn ts_anchor_sec(&self) -> u64 { self.ts_anchor_sec } pub fn ts_off_ms(&self) -> &VecDeque { &self.ts_off_ms } pub fn pulse_anchor(&self) -> u64 { self.pulse_anchor } pub fn pulse_off(&self) -> &VecDeque { &self.pulse_off } /// Note: only used for unit tests. pub fn values_to_f32(&self) -> VecDeque { self.values.iter().map(|x| x.as_prim_f32_b()).collect() } pub fn range_final(&self) -> bool { self.range_final } pub fn timed_out(&self) -> bool { self.timed_out } pub fn is_valid(&self) -> bool { if self.ts_off_ms.len() != self.ts_off_ns.len() { false } else if self.ts_off_ms.len() != self.pulse_off.len() { false } else if self.ts_off_ms.len() != self.values.len() { false } else { true } } pub fn info_str(&self) -> String { use fmt::Write; let mut out = String::new(); write!( out, "ts_off_ms {} ts_off_ns {} pulse_off {} values {}", self.ts_off_ms.len(), self.ts_off_ns.len(), self.pulse_off.len(), self.values.len(), ) .unwrap(); out } } impl AsAnyRef for EventsDim0CollectorOutput where STY: 'static, { fn as_any_ref(&self) -> &dyn Any { self } } impl AsAnyMut for EventsDim0CollectorOutput where STY: 'static, { fn as_any_mut(&mut self) -> &mut dyn Any { self } } impl TypeName for EventsDim0CollectorOutput { fn type_name(&self) -> String { any::type_name::().into() } } impl WithLen for EventsDim0CollectorOutput { fn len(&self) -> usize { self.values.len() } } impl ToJsonResult for EventsDim0CollectorOutput { fn to_json_result(&self) -> Result, Error> { let k = serde_json::to_value(self)?; Ok(Box::new(k)) } } impl Collected for EventsDim0CollectorOutput {} impl CollectorType for EventsDim0Collector { type Input = EventsDim0; type Output = EventsDim0CollectorOutput; fn ingest(&mut self, src: &mut Self::Input) { self.vals.tss.append(&mut src.tss); self.vals.pulses.append(&mut src.pulses); self.vals.values.append(&mut src.values); } fn set_range_complete(&mut self) { self.range_final = true; } fn set_timed_out(&mut self) { self.timed_out = true; self.needs_continue_at = true; } fn set_continue_at_here(&mut self) { self.needs_continue_at = true; } fn result( &mut self, range: Option, _binrange: Option, ) -> Result { debug!( "{} result() needs_continue_at {}", Self::self_name(), self.needs_continue_at ); // If we timed out, we want to hint the client from where to continue. // This is tricky: currently, client can not request a left-exclusive range. // We currently give the timestamp of the last event plus a small delta. // The amount of the delta must take into account what kind of timestamp precision the client // can parse and handle. let vals = &mut self.vals; let continue_at = if self.needs_continue_at { if let Some(ts) = vals.tss.back() { let x = Some(IsoDateTime::from_ns_u64(*ts / MS * MS + MS)); x } else { if let Some(range) = &range { match range { SeriesRange::TimeRange(x) => Some(IsoDateTime::from_ns_u64(x.beg + SEC)), SeriesRange::PulseRange(_) => { error!("TODO emit create continueAt for pulse range"); Some(IsoDateTime::from_ns_u64(0)) } } } else { Some(IsoDateTime::from_ns_u64(0)) } } } else { None }; let tss_sl = vals.tss.make_contiguous(); let pulses_sl = vals.pulses.make_contiguous(); let (ts_anchor_sec, ts_off_ms, ts_off_ns) = crate::ts_offs_from_abs(tss_sl); let (pulse_anchor, pulse_off) = crate::pulse_offs_from_abs(pulses_sl); let values = mem::replace(&mut vals.values, VecDeque::new()); if ts_off_ms.len() != ts_off_ns.len() { return Err(Error::with_msg_no_trace("collected len mismatch")); } if ts_off_ms.len() != pulse_off.len() { return Err(Error::with_msg_no_trace("collected len mismatch")); } if ts_off_ms.len() != values.len() { return Err(Error::with_msg_no_trace("collected len mismatch")); } let ret = Self::Output { ts_anchor_sec, ts_off_ms, ts_off_ns, pulse_anchor, pulse_off, values, range_final: self.range_final, timed_out: self.timed_out, continue_at, }; if !ret.is_valid() { error!("invalid:\n{}", ret.info_str()); } Ok(ret) } } impl items_0::collect_s::CollectableType for EventsDim0 { type Collector = EventsDim0Collector; fn new_collector() -> Self::Collector { Self::Collector::new() } } #[derive(Debug)] pub struct EventsDim0Aggregator { range: SeriesRange, count: u64, minmaxlst: Option<(STY, STY, STY)>, sumc: u64, sum: f32, int_ts: u64, last_ts: u64, do_time_weight: bool, events_ignored_count: u64, items_seen: usize, } impl Drop for EventsDim0Aggregator { fn drop(&mut self) { // TODO collect as stats for the request context: trace!("count {} ignored {}", self.count, self.events_ignored_count); } } impl TimeAggregatorCommonV0Trait for EventsDim0Aggregator { type Input = ::Input; type Output = ::Output; fn type_name() -> &'static str { Self::type_name() } fn common_range_current(&self) -> &SeriesRange { &self.range } fn common_ingest_unweight_range(&mut self, item: &Self::Input, r: core::ops::Range) { panic!("TODO common_ingest_unweight_range"); for (&ts, val) in item.tss.range(r.clone()).zip(item.values.range(r)) { self.apply_event_unweight(val.clone()); self.count += 1; self.last_ts = ts; } } fn common_ingest_one_before(&mut self, item: &Self::Input, j: usize) { trace_ingest_item!("{} common_ingest_one_before {:?} {:?}", Self::type_name(), j, item); self.apply_min_max_lst(item.values[j].clone()); self.last_ts = item.tss[j]; } fn common_ingest_range(&mut self, item: &Self::Input, r: core::ops::Range) { trace_ingest_item!( "{} common_ingest_range {:?} {:?} lst {:?}", Self::type_name(), r, item, self.minmaxlst ); // panic!("common_ingest_range"); let beg = self.range.beg_u64(); for (&ts, val) in item.tss.range(r.clone()).zip(item.values.range(r)) { if ts > beg { self.apply_event_time_weight(ts); } else { trace_ingest_item!("{} common_ingest_range init minmaxlst {:?}", Self::type_name(), val); self.apply_min_max_lst(val.clone()); } self.count += 1; self.last_ts = ts; } } } impl EventsDim0Aggregator { fn type_name() -> &'static str { any::type_name::() } pub fn new(range: SeriesRange, do_time_weight: bool) -> Self { trace_init!("{}::new", Self::type_name()); let int_ts = range.beg_u64(); Self { range, count: 0, minmaxlst: None, sumc: 0, sum: 0., int_ts, last_ts: 0, do_time_weight, events_ignored_count: 0, items_seen: 0, } } // TODO reduce clone.. optimize via more traits to factor the trade-offs? fn apply_min_max_lst(&mut self, val: STY) { trace_ingest_event!( "apply_min_max_lst val {:?} count {} sumc {:?} minmaxlst {:?}", val, self.count, self.sumc, self.minmaxlst, ); if let Some((min, max, lst)) = self.minmaxlst.as_mut() { if *min > val { *min = val.clone(); } if *max < val { *max = val.clone(); } *lst = val.clone(); } else { self.minmaxlst = Some((val.clone(), val.clone(), val.clone())); } } fn apply_event_unweight(&mut self, val: STY) { error!("TODO check again result_reset_unweight"); err::todo(); let vf = val.as_prim_f32_b(); self.apply_min_max_lst(val); if vf.is_nan() { } else { self.sum += vf; self.sumc += 1; } } fn apply_event_time_weight(&mut self, px: u64) { if let Some((_, _, v)) = self.minmaxlst.as_ref() { trace_ingest_event!("apply_event_time_weight with v {v:?}"); let vf = v.as_prim_f32_b(); let v2 = v.clone(); self.apply_min_max_lst(v2); self.sumc += 1; let w = (px - self.int_ts) as f32 * 1e-9; if false { trace!( "int_ts {:10} px {:8} w {:8.1} vf {:8.1} sum {:8.1}", self.int_ts / MS, px / MS, w, vf, self.sum ); } if vf.is_nan() { } else { self.sum += vf * w; } self.int_ts = px; } else { debug_ingest!("apply_event_time_weight minmaxlst None"); } } fn ingest_unweight(&mut self, item: &::Input) { TimeAggregatorCommonV0Func::ingest_unweight(self, item) } fn ingest_time_weight(&mut self, item: &::Input) { TimeAggregatorCommonV0Func::ingest_time_weight(self, item) } fn reset_values(&mut self, lst: STY, range: SeriesRange) { self.int_ts = range.beg_u64(); trace_init!("ON RESET SET int_ts {:10}", self.int_ts); self.range = range; self.count = 0; self.sum = 0.; self.sumc = 0; self.minmaxlst = Some((lst.clone(), lst.clone(), lst)); self.items_seen = 0; } fn result_reset_unweight(&mut self, range: SeriesRange) -> BinsDim0 { error!("TODO result_reset_unweight"); panic!("TODO result_reset_unweight"); if let Some((min, max, lst)) = self.minmaxlst.take() { let avg = if self.sumc > 0 { self.sum / self.sumc as f32 } else { STY::zero_b().as_prim_f32_b() }; let ret = if self.range.is_time() { BinsDim0 { ts1s: [self.range.beg_u64()].into(), ts2s: [self.range.end_u64()].into(), cnts: [self.count].into(), mins: [min].into(), maxs: [max].into(), avgs: [avg].into(), lsts: [lst.clone()].into(), dim0kind: Some(self.range.dim0kind()), } } else { error!("TODO result_reset_unweight"); err::todoval() }; self.reset_values(lst, range); ret } else { // TODO add check that nothing is different from initial values, or reset without lst. BinsDim0::empty() } } fn result_reset_time_weight(&mut self, range: SeriesRange) -> BinsDim0 { // TODO check callsite for correct expand status. trace_binning!( "result_reset_time_weight calls apply_event_time_weight range {:?} items_seen {} count {}", self.range, self.items_seen, self.count ); let range_beg = self.range.beg_u64(); let range_end = self.range.end_u64(); if self.range.is_time() { self.apply_event_time_weight(range_end); } else { error!("TODO result_reset_time_weight"); err::todoval() } if let Some((min, max, lst)) = self.minmaxlst.take() { let avg = if self.sumc > 0 { self.sum / (self.range.delta_u64() as f32 * 1e-9) } else { lst.as_prim_f32_b() }; let max = if min > max { // TODO count debug!("min > max"); min.clone() } else { max }; let avg = { let g = min.as_prim_f32_b(); if avg < g { debug!("avg < min"); g } else { avg } }; let avg = { let g = max.as_prim_f32_b(); if avg > g { debug!("avg > max"); g } else { avg } }; let ret = if self.range.is_time() { BinsDim0 { ts1s: [range_beg].into(), ts2s: [range_end].into(), cnts: [self.count].into(), mins: [min].into(), maxs: [max].into(), avgs: [avg].into(), lsts: [lst.clone()].into(), dim0kind: Some(self.range.dim0kind()), } } else { error!("TODO result_reset_time_weight"); err::todoval() }; self.reset_values(lst, range); ret } else { // TODO add check that nothing is different from initial values, or reset without lst. BinsDim0::empty() } } } impl TimeBinnableTypeAggregator for EventsDim0Aggregator { type Input = EventsDim0; type Output = BinsDim0; fn range(&self) -> &SeriesRange { &self.range } fn ingest(&mut self, item: &Self::Input) { trace_ingest_item!("{} ingest {} events", Self::type_name(), item.len()); if false { for (i, &ts) in item.tss.iter().enumerate() { trace_ingest_event!("{} ingest {:6} {:20}", Self::type_name(), i, ts); } } if self.do_time_weight { self.ingest_time_weight(item) } else { self.ingest_unweight(item) } } fn result_reset(&mut self, range: SeriesRange) -> Self::Output { trace_binning!("result_reset {:?}", range); if self.do_time_weight { self.result_reset_time_weight(range) } else { self.result_reset_unweight(range) } } } impl TimeBinnable for EventsDim0 { fn time_binner_new( &self, binrange: BinnedRangeEnum, do_time_weight: bool, emit_empty_bins: bool, ) -> Box { trace_init!( "<{} as items_0::timebin::TimeBinnable>::time_binner_new", self.type_name() ); // TODO get rid of unwrap let ret = EventsDim0TimeBinner::::new(binrange, do_time_weight, emit_empty_bins).unwrap(); Box::new(ret) } fn to_box_to_json_result(&self) -> Box { let k = serde_json::to_value(self).unwrap(); Box::new(k) as _ } } impl TypeName for EventsDim0 { fn type_name(&self) -> String { let self_name = any::type_name::(); format!("{self_name}") } } impl EventsNonObj for EventsDim0 { fn into_tss_pulses(self: Box) -> (VecDeque, VecDeque) { trace!( "{}::into_tss_pulses len {} len {}", Self::type_name(), self.tss.len(), self.pulses.len() ); (self.tss, self.pulses) } } impl Events for EventsDim0 { fn as_time_binnable_ref(&self) -> &dyn TimeBinnable { self } fn as_time_binnable_mut(&mut self) -> &mut dyn TimeBinnable { self } fn verify(&self) -> bool { let mut good = true; let n = self.tss.len(); for (&ts1, &ts2) in self.tss.iter().zip(self.tss.range(n.min(1)..n)) { if ts1 > ts2 { good = false; error!("unordered event data ts1 {} ts2 {}", ts1, ts2); break; } } good } fn output_info(&self) -> String { let n2 = self.tss.len().max(1) - 1; let min = if let Some(ts) = self.tss.get(0) { TsNano::from_ns(*ts).fmt().to_string() } else { String::from("None") }; let max = if let Some(ts) = self.tss.get(n2) { TsNano::from_ns(*ts).fmt().to_string() } else { String::from("None") }; format!( "EventsDim0OutputInfo {{ len {}, ts_min {}, ts_max {} }}", self.tss.len(), min, max, ) } fn as_collectable_mut(&mut self) -> &mut dyn Collectable { self } fn as_collectable_with_default_ref(&self) -> &dyn Collectable { self } fn as_collectable_with_default_mut(&mut self) -> &mut dyn Collectable { self } fn take_new_events_until_ts(&mut self, ts_end: u64) -> Box { // TODO improve the search let n1 = self.tss.iter().take_while(|&&x| x <= ts_end).count(); let tss = self.tss.drain(..n1).collect(); let pulses = self.pulses.drain(..n1).collect(); let values = self.values.drain(..n1).collect(); let ret = Self { tss, pulses, values }; Box::new(ret) } fn new_empty_evs(&self) -> Box { Box::new(Self::empty()) } fn drain_into_evs(&mut self, dst: &mut dyn Events, range: (usize, usize)) -> Result<(), MergeError> { // TODO as_any and as_any_mut are declared on unrelated traits. Simplify. if let Some(dst) = dst.as_any_mut().downcast_mut::() { // TODO make it harder to forget new members when the struct may get modified in the future let r = range.0..range.1; dst.tss.extend(self.tss.drain(r.clone())); dst.pulses.extend(self.pulses.drain(r.clone())); dst.values.extend(self.values.drain(r.clone())); Ok(()) } else { error!( "downcast to EventsDim0 FAILED\n\n{}\n\n{}\n\n", self.type_name(), dst.type_name() ); panic!(); Err(MergeError::NotCompatible) } } fn find_lowest_index_gt_evs(&self, ts: u64) -> Option { for (i, &m) in self.tss.iter().enumerate() { if m > ts { return Some(i); } } None } fn find_lowest_index_ge_evs(&self, ts: u64) -> Option { for (i, &m) in self.tss.iter().enumerate() { if m >= ts { return Some(i); } } None } fn find_highest_index_lt_evs(&self, ts: u64) -> Option { for (i, &m) in self.tss.iter().enumerate().rev() { if m < ts { return Some(i); } } None } fn ts_min(&self) -> Option { self.tss.front().map(|&x| x) } fn ts_max(&self) -> Option { self.tss.back().map(|&x| x) } fn partial_eq_dyn(&self, other: &dyn Events) -> bool { if let Some(other) = other.as_any_ref().downcast_ref::() { self == other } else { false } } fn serde_id(&self) -> &'static str { Self::serde_id() } fn nty_id(&self) -> u32 { STY::SUB } fn clone_dyn(&self) -> Box { Box::new(self.clone()) } fn tss(&self) -> &VecDeque { &self.tss } fn pulses(&self) -> &VecDeque { &self.pulses } fn frame_type_id(&self) -> u32 { error!("TODO frame_type_id should not be called"); // TODO make more nice panic!() } fn to_min_max_avg(&mut self) -> Box { let dst = Self { tss: mem::replace(&mut self.tss, Default::default()), pulses: mem::replace(&mut self.pulses, Default::default()), values: mem::replace(&mut self.values, Default::default()), }; Box::new(dst) } fn to_json_string(&self) -> String { // TODO redesign with mut access, rename to `into_` and take the values out. let mut tss = self.tss.clone(); let mut pulses = self.pulses.clone(); let mut values = self.values.clone(); let tss_sl = tss.make_contiguous(); let pulses_sl = pulses.make_contiguous(); let (ts_anchor_sec, ts_off_ms, ts_off_ns) = crate::ts_offs_from_abs(tss_sl); let (pulse_anchor, pulse_off) = crate::pulse_offs_from_abs(pulses_sl); let values = mem::replace(&mut values, VecDeque::new()); let ret = EventsDim0CollectorOutput { ts_anchor_sec, ts_off_ms, ts_off_ns, pulse_anchor, pulse_off, values, range_final: false, timed_out: false, continue_at: None, }; serde_json::to_string(&ret).unwrap() } fn to_json_vec_u8(&self) -> Vec { self.to_json_string().into_bytes() } fn to_cbor_vec_u8(&self) -> Vec { // TODO redesign with mut access, rename to `into_` and take the values out. let ret = EventsDim0ChunkOutput { // TODO use &mut to swap the content tss: self.tss.clone(), pulses: self.pulses.clone(), values: self.values.clone(), scalar_type: STY::scalar_type_name().into(), }; let mut buf = Vec::new(); ciborium::into_writer(&ret, &mut buf).unwrap(); buf } fn clear(&mut self) { self.tss.clear(); self.pulses.clear(); self.values.clear(); } fn to_dim0_f32_for_binning(&self) -> Box { let mut ret = EventsDim0::empty(); for (&ts, val) in self.tss.iter().zip(self.values.iter()) { ret.push(ts, 0, val.as_prim_f32_b()); } Box::new(ret) } fn to_container_events(&self) -> Box { // let tss = self.tss.iter().map(|&x| TsNano::from_ns(x)).collect(); // let vals = self.values.clone(); // let ret = crate::binning::container_events::ContainerEvents::from_constituents(tss, vals); // Box::new(ret) todo!() } } #[derive(Debug)] pub struct EventsDim0TimeBinner { binrange: BinnedRangeEnum, rix: usize, rng: Option, agg: EventsDim0Aggregator, ready: Option< as TimeBinnableTypeAggregator>::Output>, range_final: bool, emit_empty_bins: bool, } impl EventsDim0TimeBinner { fn type_name() -> &'static str { any::type_name::() } pub fn new(binrange: BinnedRangeEnum, do_time_weight: bool, emit_empty_bins: bool) -> Result { trace_init!("{}::new binrange {:?}", Self::type_name(), binrange); let rng = binrange .range_at(0) .ok_or_else(|| Error::with_msg_no_trace("empty binrange"))?; trace_init!("{}::new rng {:?}", Self::type_name(), rng); let agg = EventsDim0Aggregator::new(rng, do_time_weight); let ret = Self { binrange, rix: 0, rng: Some(agg.range().clone()), agg, ready: None, range_final: false, emit_empty_bins, }; Ok(ret) } fn next_bin_range(&mut self) -> Option { self.rix += 1; if let Some(rng) = self.binrange.range_at(self.rix) { trace_binning!("{} next_bin_range {:?}", Self::type_name(), rng); Some(rng) } else { trace_binning!("{} next_bin_range None", Self::type_name()); None } } } impl TimeBinnerCommonV0Trait for EventsDim0TimeBinner { type Input = as TimeBinnableTypeAggregator>::Input; type Output = as TimeBinnableTypeAggregator>::Output; fn type_name() -> &'static str { Self::type_name() } fn common_bins_ready_count(&self) -> usize { match &self.ready { Some(k) => k.len(), None => 0, } } fn common_range_current(&self) -> &SeriesRange { self.agg.range() } fn common_has_more_range(&self) -> bool { self.rng.is_some() } fn common_next_bin_range(&mut self) -> Option { self.next_bin_range() } fn common_set_current_range(&mut self, range: Option) { self.rng = range; } fn common_take_or_append_all_from(&mut self, item: Self::Output) { let mut item = item; match self.ready.as_mut() { Some(ready) => { ready.append_all_from(&mut item); } None => { self.ready = Some(item); } } } fn common_result_reset(&mut self, range: Option) -> Self::Output { self.agg.result_reset(range.unwrap_or_else(|| { SeriesRange::TimeRange(netpod::range::evrange::NanoRange { beg: u64::MAX, end: u64::MAX, }) })) } fn common_agg_ingest(&mut self, item: &mut Self::Input) { self.agg.ingest(item) } fn common_has_lst(&self) -> bool { self.agg.minmaxlst.is_some() } fn common_feed_lst(&mut self, item: &mut Self::Input) { if self.agg.minmaxlst.is_none() { if let Some(val) = item.values.front() { self.agg.apply_min_max_lst(val.clone()); } } } } impl TimeBinner for EventsDim0TimeBinner { fn ingest(&mut self, item: &mut dyn TimeBinnable) { trace_ingest_item!("<{} as TimeBinner>::ingest {:?}", Self::type_name(), item); TimeBinnerCommonV0Func::ingest(self, item) } fn bins_ready_count(&self) -> usize { TimeBinnerCommonV0Trait::common_bins_ready_count(self) } fn bins_ready(&mut self) -> Option> { match self.ready.take() { Some(k) => Some(Box::new(k)), None => None, } } fn push_in_progress(&mut self, push_empty: bool) { TimeBinnerCommonV0Func::push_in_progress(self, push_empty) } fn cycle(&mut self) { TimeBinnerCommonV0Func::cycle(self) } fn set_range_complete(&mut self) { self.range_final = true; } fn empty(&self) -> Box { let ret = as TimeBinnableTypeAggregator>::Output::empty(); Box::new(ret) } fn append_empty_until_end(&mut self) { // nothing to do for events } } impl Appendable for EventsDim0 where STY: ScalarOps, { fn push(&mut self, ts: u64, pulse: u64, value: STY) { self.tss.push_back(ts); self.pulses.push_back(pulse); self.values.push_back(value); } } #[cfg(test)] mod test_frame { use super::*; use crate::channelevents::ChannelEvents; use crate::framable::Framable; use crate::framable::INMEM_FRAME_ENCID; use crate::frame::decode_frame; use crate::inmem::InMemoryFrame; use items_0::streamitem::RangeCompletableItem; use items_0::streamitem::Sitemty; use items_0::streamitem::StreamItem; #[test] fn events_serialize() { taskrun::tracing_init_testing().unwrap(); let mut events = EventsDim0::empty(); events.push(123, 234, 55f32); let events = events; let events: Box = Box::new(events); let item = ChannelEvents::Events(events); let item = Ok::<_, Error>(StreamItem::DataItem(RangeCompletableItem::Data(item))); let mut buf = item.make_frame().unwrap(); let s = String::from_utf8_lossy(&buf[20..buf.len() - 4]); eprintln!("[[{s}]]"); let buflen = buf.len(); let frame = InMemoryFrame { encid: INMEM_FRAME_ENCID, tyid: 0x2500, len: (buflen - 24) as _, buf: buf.split_off(20).split_to(buflen - 20 - 4).freeze(), }; let item: Sitemty = decode_frame(&frame).unwrap(); let item = if let Ok(x) = item { x } else { panic!() }; let item = if let StreamItem::DataItem(x) = item { x } else { panic!() }; let item = if let RangeCompletableItem::Data(x) = item { x } else { panic!() }; let mut item = if let ChannelEvents::Events(x) = item { x } else { panic!() }; let item = if let Some(item) = item.as_any_mut().downcast_mut::>() { item } else { panic!() }; assert_eq!(item.tss(), &[123]); #[cfg(DISABLED)] { eprintln!("NOW WE SEE: {:?}", item); // type_name_of_val alloc::boxed::Box eprintln!("0 {:22?}", item.as_any_mut().type_id()); eprintln!("A {:22?}", std::any::TypeId::of::>()); eprintln!("B {:22?}", std::any::TypeId::of::()); eprintln!("C {:22?}", std::any::TypeId::of::<&dyn items_0::Events>()); eprintln!("D {:22?}", std::any::TypeId::of::<&mut dyn items_0::Events>()); eprintln!("E {:22?}", std::any::TypeId::of::<&mut Box>()); eprintln!("F {:22?}", std::any::TypeId::of::>>()); eprintln!("G {:22?}", std::any::TypeId::of::<&EventsDim0>()); eprintln!("H {:22?}", std::any::TypeId::of::<&mut EventsDim0>()); eprintln!("I {:22?}", std::any::TypeId::of::>>>()); //let item = item.as_mut(); //eprintln!("1 {:22?}", item.type_id()); /* let item = if let Some(item) = items_0::collect_s::Collectable::as_any_mut(item).downcast_ref::>>() { item } else { panic!() }; */ //eprintln!("Final value: {item:?}"); } } } #[cfg(test)] mod test_serde_opt { use super::*; #[derive(Serialize)] struct A { a: Option, #[serde(default)] b: Option, #[serde(default, skip_serializing_if = "Option::is_none")] c: Option, } #[test] fn test_a() { let s = serde_json::to_string(&A { a: None, b: None, c: None, }) .unwrap(); assert_eq!(s, r#"{"a":null,"b":null}"#); } } #[test] fn overlap_info_00() { let mut ev1 = EventsDim0::empty(); ev1.push(MS * 1200, 3, 1.2f32); ev1.push(MS * 3200, 3, 3.2f32); let range = SeriesRange::TimeRange(NanoRange { beg: MS * 1000, end: MS * 2000, }); assert_eq!(ev1.ends_after(&range), true); } #[test] fn overlap_info_01() { let mut ev1 = EventsDim0::empty(); ev1.push(MS * 1200, 3, 1.2f32); ev1.push(MS * 1400, 3, 3.2f32); let range = SeriesRange::TimeRange(NanoRange { beg: MS * 1000, end: MS * 2000, }); assert_eq!(ev1.ends_after(&range), false); } #[test] fn binner_00() { let mut ev1 = EventsDim0::empty(); ev1.push(MS * 1200, 3, 1.2f32); ev1.push(MS * 3200, 3, 3.2f32); let binrange = BinnedRangeEnum::from_custom(TsNano::from_ns(SEC), 0, 10); let mut binner = ev1.time_binner_new(binrange, true, false); binner.ingest(ev1.as_time_binnable_mut()); eprintln!("{:?}", binner); // TODO add actual asserts } #[test] fn binner_01() { let mut ev1 = EventsDim0::empty(); ev1.push(MS * 1200, 3, 1.2f32); ev1.push(MS * 1300, 3, 1.3); ev1.push(MS * 2100, 3, 2.1); ev1.push(MS * 2300, 3, 2.3); let binrange = BinnedRangeEnum::from_custom(TsNano::from_ns(SEC), 0, 10); let mut binner = ev1.time_binner_new(binrange, true, false); binner.ingest(ev1.as_time_binnable_mut()); eprintln!("{:?}", binner); // TODO add actual asserts } /* TODO adapt and enable #[test] fn bin_binned_01() { use binsdim0::MinMaxAvgDim0Bins; let edges = vec![SEC * 1000, SEC * 1010, SEC * 1020, SEC * 1030]; let inp0 = as NewEmpty>::empty(Shape::Scalar); let mut time_binner = inp0.time_binner_new(edges, true); let inp1 = MinMaxAvgDim0Bins:: { ts1s: vec![SEC * 1000, SEC * 1010], ts2s: vec![SEC * 1010, SEC * 1020], counts: vec![1, 1], mins: vec![3, 4], maxs: vec![10, 9], avgs: vec![7., 6.], }; assert_eq!(time_binner.bins_ready_count(), 0); time_binner.ingest(&inp1); assert_eq!(time_binner.bins_ready_count(), 1); time_binner.push_in_progress(false); assert_eq!(time_binner.bins_ready_count(), 2); // From here on, pushing any more should not change the bin count: time_binner.push_in_progress(false); assert_eq!(time_binner.bins_ready_count(), 2); // On the other hand, cycling should add one more zero-bin: time_binner.cycle(); assert_eq!(time_binner.bins_ready_count(), 3); time_binner.cycle(); assert_eq!(time_binner.bins_ready_count(), 3); let bins = time_binner.bins_ready().expect("bins should be ready"); eprintln!("bins: {:?}", bins); assert_eq!(time_binner.bins_ready_count(), 0); assert_eq!(bins.counts(), &[1, 1, 0]); // TODO use proper float-compare logic: assert_eq!(bins.mins(), &[3., 4., 0.]); assert_eq!(bins.maxs(), &[10., 9., 0.]); assert_eq!(bins.avgs(), &[7., 6., 0.]); } #[test] fn bin_binned_02() { use binsdim0::MinMaxAvgDim0Bins; let edges = vec![SEC * 1000, SEC * 1020]; let inp0 = as NewEmpty>::empty(Shape::Scalar); let mut time_binner = inp0.time_binner_new(edges, true); let inp1 = MinMaxAvgDim0Bins:: { ts1s: vec![SEC * 1000, SEC * 1010], ts2s: vec![SEC * 1010, SEC * 1020], counts: vec![1, 1], mins: vec![3, 4], maxs: vec![10, 9], avgs: vec![7., 6.], }; assert_eq!(time_binner.bins_ready_count(), 0); time_binner.ingest(&inp1); assert_eq!(time_binner.bins_ready_count(), 0); time_binner.cycle(); assert_eq!(time_binner.bins_ready_count(), 1); time_binner.cycle(); //assert_eq!(time_binner.bins_ready_count(), 2); let bins = time_binner.bins_ready().expect("bins should be ready"); eprintln!("bins: {:?}", bins); assert_eq!(time_binner.bins_ready_count(), 0); assert_eq!(bins.counts(), &[2]); assert_eq!(bins.mins(), &[3.]); assert_eq!(bins.maxs(), &[10.]); assert_eq!(bins.avgs(), &[13. / 2.]); } */ #[test] fn events_timebin_ingest_continuous_00() { let binrange = BinnedRangeEnum::Time(BinnedRange { bin_len: TsNano::from_ns(SEC * 2), bin_off: 9, bin_cnt: 20, }); let do_time_weight = true; let mut bins = EventsDim0::::empty(); bins.push(SEC * 20, 1, 20); bins.push(SEC * 23, 2, 23); let mut binner = bins .as_time_binnable_ref() .time_binner_new(binrange, do_time_weight, false); binner.ingest(&mut bins); //binner.push_in_progress(true); let ready = binner.bins_ready(); let got = ready.unwrap(); let got: &BinsDim0 = got.as_any_ref().downcast_ref().unwrap(); let mut exp = BinsDim0::empty(); // exp.push(SEC * 18, SEC * 20, 0, 0, 0, 0., None); exp.push(SEC * 20, SEC * 22, 1, 20, 20, 20., 20); assert!(f32_iter_cmp_near(got.avgs.clone(), exp.avgs.clone(), 0.0001, 0.0001)); }