Skip to main content

plato_sim_channel/
lib.rs

1//! plato-sim-channel — ChannelLayer adapter for plato-sim-bridge
2//!
3//! Bridges simulation ↔ live environments through the Ship Interconnection
4//! Protocol's ChannelLayer trait. Messages can be sent to simulation channels
5//! or live channels, with automatic mode switching.
6//!
7//! Sprint 3 Task S3-4: implement ChannelLayer for plato-sim-bridge.
8
9use std::collections::{HashMap, VecDeque};
10
11// ── Channel Trait ────────────────────────────────────────
12
13/// Channel layer: simulation ↔ live bridging.
14/// Matches plato-ship-protocol::ChannelLayer exactly.
15pub trait ChannelLayer {
16    fn bridge_send(&mut self, channel: u8, msg: &[u8]) -> bool;
17    fn bridge_recv(&mut self, channel: u8) -> Option<Vec<u8>>;
18    fn is_live(&self) -> bool;
19}
20
21// ── Channel Types ────────────────────────────────────────
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub enum ChannelMode {
25    /// Connected to live production environment
26    Live,
27    /// Running in simulation mode
28    Simulated,
29    /// Bridging: sim output feeds into live input
30    Bridging,
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34#[repr(u8)]
35pub enum ChannelKind {
36    /// Fleet coordination channel
37    Fleet = 0,
38    /// Training data channel
39    Training = 1,
40    /// Event/notification channel
41    Event = 2,
42    /// Tile sharing channel
43    Tiles = 3,
44    /// Trust signal channel
45    Trust = 4,
46    /// Custom/reserved channels 5-255
47    Custom(u8),
48}
49
50impl ChannelKind {
51    pub fn from_byte(b: u8) -> Self {
52        match b {
53            0 => ChannelKind::Fleet,
54            1 => ChannelKind::Training,
55            2 => ChannelKind::Event,
56            3 => ChannelKind::Tiles,
57            4 => ChannelKind::Trust,
58            n => ChannelKind::Custom(n),
59        }
60    }
61
62    pub fn to_byte(self) -> u8 {
63        match self {
64            ChannelKind::Fleet => 0,
65            ChannelKind::Training => 1,
66            ChannelKind::Event => 2,
67            ChannelKind::Tiles => 3,
68            ChannelKind::Trust => 4,
69            ChannelKind::Custom(n) => n,
70        }
71    }
72
73    pub fn name(&self) -> &'static str {
74        match self {
75            ChannelKind::Fleet => "fleet",
76            ChannelKind::Training => "training",
77            ChannelKind::Event => "event",
78            ChannelKind::Tiles => "tiles",
79            ChannelKind::Trust => "trust",
80            ChannelKind::Custom(_) => "custom",
81        }
82    }
83}
84
85// ── Channel Message ──────────────────────────────────────
86
87#[derive(Debug, Clone)]
88pub struct ChannelMessage {
89    pub payload: Vec<u8>,
90    pub channel: ChannelKind,
91    pub source: String,      // agent or system that sent it
92    pub timestamp: u64,      // nanosecond
93    pub sim_origin: bool,    // true if this came from simulation
94    pub quality_score: f32,  // 0.0-1.0, how good is this data
95}
96
97impl ChannelMessage {
98    pub fn new(channel: ChannelKind, source: &str, payload: &[u8]) -> Self {
99        Self {
100            payload: payload.to_vec(),
101            channel,
102            source: source.to_string(),
103            timestamp: nanos_now(),
104            sim_origin: false,
105            quality_score: 0.5,
106        }
107    }
108
109    pub fn from_sim(channel: ChannelKind, source: &str, payload: &[u8]) -> Self {
110        let mut msg = Self::new(channel, source, payload);
111        msg.sim_origin = true;
112        msg
113    }
114
115    pub fn with_quality(mut self, q: f32) -> Self {
116        self.quality_score = q.max(0.0).min(1.0);
117        self
118    }
119}
120
121// ── Channel Adapter ──────────────────────────────────────
122
123/// Bridges simulation and live environments through typed channels.
124#[derive(Debug, Clone)]
125pub struct ChannelAdapter {
126    mode: ChannelMode,
127    channels: HashMap<u8, VecDeque<ChannelMessage>>,
128    max_buffer: usize,
129    messages_sent: u64,
130    messages_received: u64,
131    messages_bridged: u64,
132}
133
134impl ChannelAdapter {
135    pub fn new(mode: ChannelMode) -> Self {
136        Self {
137            mode,
138            channels: HashMap::new(),
139            max_buffer: 256,
140            messages_sent: 0,
141            messages_received: 0,
142            messages_bridged: 0,
143        }
144    }
145
146    pub fn live() -> Self { Self::new(ChannelMode::Live) }
147    pub fn simulated() -> Self { Self::new(ChannelMode::Simulated) }
148    pub fn bridging() -> Self { Self::new(ChannelMode::Bridging) }
149
150    /// Set maximum buffer per channel
151    pub fn with_max_buffer(mut self, max: usize) -> Self {
152        self.max_buffer = max;
153        self
154    }
155
156    /// Send a typed message to a channel
157    pub fn send_typed(&mut self, kind: ChannelKind, source: &str, payload: &[u8]) -> bool {
158        let ch = kind.to_byte();
159        let cap = self.max_buffer;
160        let buf = self.channels.entry(ch).or_insert_with(VecDeque::new);
161        if buf.len() >= cap { return false; }
162        buf.push_back(ChannelMessage::new(kind, source, payload));
163        self.messages_sent += 1;
164        true
165    }
166
167    /// Send a simulation-originated message
168    pub fn send_sim(&mut self, kind: ChannelKind, source: &str, payload: &[u8]) -> bool {
169        let ch = kind.to_byte();
170        let cap = self.max_buffer;
171        let buf = self.channels.entry(ch).or_insert_with(VecDeque::new);
172        if buf.len() >= cap { return false; }
173        buf.push_back(ChannelMessage::from_sim(kind, source, payload));
174        self.messages_sent += 1;
175        self.messages_bridged += 1;
176        true
177    }
178
179    /// Receive from a channel (returns payload only)
180    pub fn recv(&mut self, kind: ChannelKind) -> Option<Vec<u8>> {
181        let ch = kind.to_byte();
182        let msg = self.channels.get_mut(&ch)?.pop_front()?;
183        self.messages_received += 1;
184        Some(msg.payload)
185    }
186
187    /// Receive full message with metadata
188    pub fn recv_full(&mut self, kind: ChannelKind) -> Option<ChannelMessage> {
189        let ch = kind.to_byte();
190        let msg = self.channels.get_mut(&ch)?.pop_front()?;
191        self.messages_received += 1;
192        Some(msg)
193    }
194
195    /// Bridge: take all sim messages from one channel, tag them, push to another
196    pub fn bridge(&mut self, from: ChannelKind, to: ChannelKind) -> usize {
197        let from_ch = from.to_byte();
198        // Take all messages from source channel
199        let all_msgs: Vec<ChannelMessage> = self.channels.remove(&from_ch)
200            .unwrap_or_default().into_iter().collect();
201        
202        // Separate sim from non-sim
203        let mut sim = Vec::new();
204        let mut non_sim = Vec::new();
205        for msg in all_msgs {
206            if msg.sim_origin { sim.push(msg); } else { non_sim.push(msg); }
207        }
208        
209        // Put non-sim messages back
210        if !non_sim.is_empty() {
211            self.channels.insert(from_ch, non_sim.into_iter().collect());
212        }
213        
214        // Move sim messages to target channel
215        let count = sim.len();
216        let target = self.channels.entry(to.to_byte()).or_insert_with(VecDeque::new);
217        for msg in sim {
218            if target.len() < self.max_buffer {
219                target.push_back(msg);
220                self.messages_bridged += 1;
221            }
222        }
223        count
224    }
225
226    /// Buffer size for a channel
227    pub fn channel_size(&self, kind: ChannelKind) -> usize {
228        self.channels.get(&kind.to_byte()).map(|b| b.len()).unwrap_or(0)
229    }
230
231    /// Total buffered across all channels
232    pub fn total_buffered(&self) -> usize {
233        self.channels.values().map(|b| b.len()).sum()
234    }
235
236    /// Switch mode
237    pub fn set_mode(&mut self, mode: ChannelMode) {
238        self.mode = mode;
239    }
240
241    /// Stats
242    pub fn stats(&self) -> ChannelStats {
243        ChannelStats {
244            mode: self.mode,
245            messages_sent: self.messages_sent,
246            messages_received: self.messages_received,
247            messages_bridged: self.messages_bridged,
248            active_channels: self.channels.len(),
249            total_buffered: self.total_buffered(),
250        }
251    }
252}
253
254#[derive(Debug, Clone, Copy)]
255pub struct ChannelStats {
256    pub mode: ChannelMode,
257    pub messages_sent: u64,
258    pub messages_received: u64,
259    pub messages_bridged: u64,
260    pub active_channels: usize,
261    pub total_buffered: usize,
262}
263
264impl ChannelLayer for ChannelAdapter {
265    fn bridge_send(&mut self, channel: u8, msg: &[u8]) -> bool {
266        let is_sim = self.mode != ChannelMode::Live;
267        let kind = ChannelKind::from_byte(channel);
268        let buf = self.channels.entry(channel).or_insert_with(VecDeque::new);
269        if buf.len() >= self.max_buffer { return false; }
270        let mut ch_msg = ChannelMessage::new(kind, "bridge", msg);
271        ch_msg.sim_origin = is_sim;
272        buf.push_back(ch_msg);
273        self.messages_sent += 1;
274        true
275    }
276
277    fn bridge_recv(&mut self, channel: u8) -> Option<Vec<u8>> {
278        let msg = self.channels.get_mut(&channel)?.pop_front()?;
279        self.messages_received += 1;
280        Some(msg.payload)
281    }
282
283    fn is_live(&self) -> bool {
284        self.mode == ChannelMode::Live
285    }
286}
287
288fn nanos_now() -> u64 {
289    use std::time::{SystemTime, UNIX_EPOCH};
290    SystemTime::now().duration_since(UNIX_EPOCH).map(|d| d.as_nanos() as u64).unwrap_or(0)
291}
292
293// ── Tests ────────────────────────────────────────────────
294
295#[cfg(test)]
296mod tests {
297    use super::*;
298
299    #[test]
300    fn test_channel_send_recv() {
301        let mut ch = ChannelAdapter::live();
302        ch.send_typed(ChannelKind::Fleet, "oracle1", b"hello");
303        let msg = ch.recv(ChannelKind::Fleet).unwrap();
304        assert_eq!(msg, b"hello");
305    }
306
307    #[test]
308    fn test_channel_recv_empty() {
309        let mut ch = ChannelAdapter::simulated();
310        assert!(ch.recv(ChannelKind::Event).is_none());
311    }
312
313    #[test]
314    fn test_channel_ordering() {
315        let mut ch = ChannelAdapter::live();
316        ch.send_typed(ChannelKind::Training, "a", b"1");
317        ch.send_typed(ChannelKind::Training, "b", b"2");
318        ch.send_typed(ChannelKind::Training, "c", b"3");
319
320        assert_eq!(ch.recv(ChannelKind::Training).unwrap(), b"1");
321        assert_eq!(ch.recv(ChannelKind::Training).unwrap(), b"2");
322    }
323
324    #[test]
325    fn test_sim_messages() {
326        let mut ch = ChannelAdapter::simulated();
327        ch.send_sim(ChannelKind::Tiles, "sim", b"tile_data");
328        let msg = ch.recv_full(ChannelKind::Tiles).unwrap();
329        assert!(msg.sim_origin);
330        assert_eq!(msg.payload, b"tile_data");
331    }
332
333    #[test]
334    fn test_bridge() {
335        let mut ch = ChannelAdapter::bridging();
336        // Send sim messages to fleet channel
337        ch.send_sim(ChannelKind::Fleet, "sim", b"sim_msg_1");
338        ch.send_sim(ChannelKind::Fleet, "sim", b"sim_msg_2");
339        // Send a live message (should NOT be bridged)
340        ch.send_typed(ChannelKind::Fleet, "live_agent", b"live_msg");
341
342        // Bridge fleet → event
343        let bridged = ch.bridge(ChannelKind::Fleet, ChannelKind::Event);
344        assert_eq!(bridged, 2); // only sim messages
345
346        // Event channel should have the 2 sim messages
347        assert_eq!(ch.channel_size(ChannelKind::Event), 2);
348        // Fleet channel should still have the live message
349        assert_eq!(ch.channel_size(ChannelKind::Fleet), 1);
350    }
351
352    #[test]
353    fn test_capacity_limit() {
354        let mut ch = ChannelAdapter::live().with_max_buffer(2);
355        assert!(ch.send_typed(ChannelKind::Event, "a", b"1"));
356        assert!(ch.send_typed(ChannelKind::Event, "a", b"2"));
357        assert!(!ch.send_typed(ChannelKind::Event, "a", b"3")); // over capacity
358    }
359
360    #[test]
361    fn test_is_live() {
362        assert!(ChannelAdapter::live().is_live());
363        assert!(!ChannelAdapter::simulated().is_live());
364        assert!(!ChannelAdapter::bridging().is_live());
365    }
366
367    #[test]
368    fn test_channel_layer_trait() {
369        let mut ch = ChannelAdapter::live();
370        assert!(ch.bridge_send(0, b"fleet_msg")); // channel 0 = fleet
371        assert!(ch.bridge_send(3, b"tile_msg")); // channel 3 = tiles
372
373        let fleet = ch.bridge_recv(0).unwrap();
374        assert_eq!(fleet, b"fleet_msg");
375
376        let tiles = ch.bridge_recv(3).unwrap();
377        assert_eq!(tiles, b"tile_msg");
378    }
379
380    #[test]
381    fn test_channel_kinds() {
382        assert_eq!(ChannelKind::from_byte(0), ChannelKind::Fleet);
383        assert_eq!(ChannelKind::from_byte(1), ChannelKind::Training);
384        assert_eq!(ChannelKind::from_byte(5), ChannelKind::Custom(5));
385        assert_eq!(ChannelKind::Fleet.to_byte(), 0);
386        assert_eq!(ChannelKind::Custom(99).to_byte(), 99);
387    }
388
389    #[test]
390    fn test_quality_score() {
391        let msg = ChannelMessage::new(ChannelKind::Event, "src", b"data").with_quality(0.95);
392        assert_eq!(msg.quality_score, 0.95);
393    }
394
395    #[test]
396    fn test_quality_clamping() {
397        let msg = ChannelMessage::new(ChannelKind::Event, "src", b"data").with_quality(1.5);
398        assert_eq!(msg.quality_score, 1.0);
399    }
400
401    #[test]
402    fn test_stats() {
403        let mut ch = ChannelAdapter::bridging();
404        ch.send_typed(ChannelKind::Fleet, "a", b"1");
405        ch.send_sim(ChannelKind::Tiles, "sim", b"2");
406        ch.recv(ChannelKind::Fleet);
407
408        let stats = ch.stats();
409        assert_eq!(stats.messages_sent, 2);
410        assert_eq!(stats.messages_received, 1);
411        assert_eq!(stats.messages_bridged, 1);
412        assert_eq!(stats.active_channels, 2);
413    }
414
415    #[test]
416    fn test_mode_switch() {
417        let mut ch = ChannelAdapter::simulated();
418        assert!(!ch.is_live());
419        ch.set_mode(ChannelMode::Live);
420        assert!(ch.is_live());
421    }
422
423    #[test]
424    fn test_multiple_channels_independent() {
425        let mut ch = ChannelAdapter::live();
426        ch.send_typed(ChannelKind::Fleet, "a", b"f1");
427        ch.send_typed(ChannelKind::Training, "a", b"t1");
428
429        assert_eq!(ch.recv(ChannelKind::Fleet).unwrap(), b"f1");
430        assert_eq!(ch.recv(ChannelKind::Training).unwrap(), b"t1");
431        assert!(ch.recv(ChannelKind::Event).is_none()); // empty channel
432    }
433
434    #[test]
435    fn test_bridge_empty() {
436        let mut ch = ChannelAdapter::bridging();
437        let bridged = ch.bridge(ChannelKind::Event, ChannelKind::Fleet);
438        assert_eq!(bridged, 0);
439    }
440}