Skip to main content

dsfb_semiconductor/interface/
mod.rs

1use crate::grammar::layer::{build_grammar_states, GrammarState};
2use crate::input::alarm_stream::{AlarmSample, AlarmStream};
3use crate::input::residual_stream::{ResidualSample, ResidualStream};
4use crate::policy::{derive_policy, PolicyDecision};
5use crate::semantics::{match_semantics, minimal_heuristics_bank, Heuristic, SemanticMatch};
6use crate::sign::{build_feature_signs, FeatureSignPoint};
7use crate::syntax::{build_motifs, Motif, MotifTimelinePoint};
8use std::sync::RwLock;
9
10pub trait DSFBObserver {
11    fn ingest(&self, residual: &ResidualSample);
12    fn output(&self) -> Vec<PolicyDecision>;
13}
14
15pub trait FabDataSource {
16    fn residual_stream(&self) -> Vec<ResidualSample>;
17}
18
19#[derive(Debug, Default)]
20pub struct ReadOnlyDsfbObserver {
21    residuals: RwLock<Vec<ResidualSample>>,
22    alarms: RwLock<Vec<AlarmSample>>,
23}
24
25#[derive(Debug, Clone)]
26pub struct ObserverArtifacts {
27    pub signs: Vec<FeatureSignPoint>,
28    pub motifs: Vec<Motif>,
29    pub motif_timeline: Vec<MotifTimelinePoint>,
30    pub grammar_states: Vec<GrammarState>,
31    pub heuristics: Vec<Heuristic>,
32    pub semantic_matches: Vec<SemanticMatch>,
33    pub policy_decisions: Vec<PolicyDecision>,
34}
35
36impl ReadOnlyDsfbObserver {
37    pub fn new() -> Self {
38        Self::default()
39    }
40
41    pub fn with_alarm_stream(alarm_stream: AlarmStream) -> Self {
42        Self {
43            residuals: RwLock::new(Vec::new()),
44            alarms: RwLock::new(alarm_stream.samples().to_vec()),
45        }
46    }
47
48    pub fn ingest_alarm(&self, alarm: &AlarmSample) {
49        self.alarms.write().unwrap().push(alarm.clone());
50        self.alarms.write().unwrap().sort_by(|left, right| {
51            left.timestamp
52                .total_cmp(&right.timestamp)
53                .then_with(|| left.source.cmp(&right.source))
54        });
55    }
56
57    pub fn residual_stream(&self) -> ResidualStream {
58        ResidualStream::new(self.residuals.read().unwrap().clone())
59    }
60
61    pub fn alarm_stream(&self) -> AlarmStream {
62        AlarmStream::new(self.alarms.read().unwrap().clone())
63    }
64
65    pub fn layered_output(&self) -> ObserverArtifacts {
66        let residual_stream = self.residual_stream();
67        let signs = build_feature_signs(&residual_stream);
68        let syntax = build_motifs(&signs);
69        let grammar_states = build_grammar_states(&signs, &syntax.timeline);
70        let heuristics = minimal_heuristics_bank();
71        let semantic_matches = match_semantics(&syntax.timeline, &grammar_states, &heuristics);
72        let policy_decisions = derive_policy(&semantic_matches, &grammar_states);
73        ObserverArtifacts {
74            signs,
75            motifs: syntax.motifs,
76            motif_timeline: syntax.timeline,
77            grammar_states,
78            heuristics,
79            semantic_matches,
80            policy_decisions,
81        }
82    }
83}
84
85impl DSFBObserver for ReadOnlyDsfbObserver {
86    fn ingest(&self, residual: &ResidualSample) {
87        self.residuals.write().unwrap().push(residual.clone());
88        self.residuals.write().unwrap().sort_by(|left, right| {
89            left.timestamp
90                .total_cmp(&right.timestamp)
91                .then_with(|| left.feature_id.cmp(&right.feature_id))
92        });
93    }
94
95    fn output(&self) -> Vec<PolicyDecision> {
96        self.layered_output().policy_decisions
97    }
98}
99
100impl FabDataSource for ResidualStream {
101    fn residual_stream(&self) -> Vec<ResidualSample> {
102        self.samples().to_vec()
103    }
104}
105
106#[cfg(test)]
107mod tests {
108    use super::*;
109
110    #[test]
111    fn observer_replay_is_deterministic() {
112        let observer = ReadOnlyDsfbObserver::new();
113        let sample = ResidualSample {
114            timestamp: 1.0,
115            feature_id: "S059".into(),
116            value: 2.0,
117        };
118        observer.ingest(&sample);
119        observer.ingest(&ResidualSample {
120            timestamp: 2.0,
121            feature_id: "S059".into(),
122            value: 3.0,
123        });
124        observer.ingest(&ResidualSample {
125            timestamp: 3.0,
126            feature_id: "S059".into(),
127            value: 4.2,
128        });
129        let first = observer.layered_output();
130        let second = observer.layered_output();
131        assert_eq!(first.policy_decisions, second.policy_decisions);
132        assert_eq!(first.semantic_matches, second.semantic_matches);
133        assert_eq!(first.grammar_states, second.grammar_states);
134    }
135
136    #[test]
137    fn residual_stream_implements_fab_data_source_read_only() {
138        let source = ResidualStream::new(vec![ResidualSample {
139            timestamp: 1.0,
140            feature_id: "S059".into(),
141            value: 0.5,
142        }]);
143        let exported = source.residual_stream();
144        assert_eq!(exported.len(), 1);
145        assert_eq!(exported[0].feature_id, "S059");
146        assert_eq!(source.samples().len(), 1);
147    }
148}