mt_sea/
lib.rs

1pub mod client;
2pub mod coordinator;
3pub mod net;
4pub mod ship;
5
6use mt_net::{ActionPlan, BagMsg, Rules, VariableHuman};
7use rkyv::{
8    Archive, Deserialize, Serialize,
9    api::high::{HighSerializer, HighValidator},
10    bytecheck::CheckBytes,
11    de::Pool,
12    rancor::Strategy,
13    ser::allocator::ArenaHandle,
14    util::AlignedVec,
15};
16
17#[derive(Debug, Clone, PartialEq, Archive, Serialize, Deserialize, Hash, Eq, PartialOrd, Ord)]
18pub enum ShipKind {
19    Rat(String),
20    Wind(String),
21}
22
23pub type ShipName = i128;
24
25#[derive(Debug, Clone, Serialize, Deserialize, Archive, PartialEq, Eq)]
26pub struct NetworkShipAddress {
27    ip: [u8; 4],
28    port: u16,
29    ship: ShipName,
30    pub kind: ShipKind,
31}
32
33#[derive(Debug, Archive, Clone, Default, Serialize, Deserialize)]
34pub enum Action {
35    #[default]
36    Sail,
37    Shoot {
38        target: Vec<NetworkShipAddress>,
39        id: u32,
40    },
41    Catch {
42        source: NetworkShipAddress,
43        id: u32,
44    },
45}
46
47#[derive(Clone, Debug)]
48pub struct Variable {
49    pub ship: ShipName,
50    pub strategy: Option<Action>,
51}
52
53pub fn get_strategies(
54    haystack: &Rules,
55    rat_ship: &str,
56    variable: String,
57    indirect_parent_rat: Option<&str>,
58) -> Vec<ActionPlan> {
59    match haystack.raw().get(&variable) {
60        None => vec![ActionPlan::default()],
61        Some(plans) => {
62            // directly because rule was set
63            let directly = plans
64                .iter()
65                .filter(|plan| plan.ship == rat_ship)
66                .filter_map(|el| el.strategy.clone())
67                .collect::<Vec<_>>();
68
69            // as other part of a rule
70            let mut indirect = plans
71                .iter()
72                .filter_map(|plan| {
73                    if let Some(partner) = indirect_parent_rat {
74                        if plan.ship == partner {
75                            Some(plan)
76                        } else {
77                            None
78                        }
79                    } else {
80                        Some(plan)
81                    }
82                })
83                .filter_map(|plan| match plan.strategy.as_ref()? {
84                    ActionPlan::Sail => None,
85                    ActionPlan::Shoot { target, id } => target
86                        .iter()
87                        .find(|shoot_target| *shoot_target == rat_ship)
88                        .map(|_| ActionPlan::Catch {
89                            source: plan.ship.clone(),
90                            id: *id,
91                        }),
92                    ActionPlan::Catch { source, id } => {
93                        if source == rat_ship {
94                            Some(ActionPlan::Shoot {
95                                target: vec![source.clone()],
96                                id: *id,
97                            })
98                        } else {
99                            None
100                        }
101                    }
102                })
103                .collect::<Vec<_>>();
104
105            indirect.extend(directly);
106            indirect
107        }
108    }
109}
110
111#[async_trait::async_trait]
112pub trait Ship: Send + Sync + 'static {
113    /// Indicate a trigger point and ask the link pilot what to do with the variable.
114    async fn ask_for_action(&self, variable_name: &str) -> anyhow::Result<(Action, bool)>;
115
116    // async fn wait_for_action(&self) -> anyhow::Result<crate::Action>;
117
118    async fn wait_for_wind(&self) -> anyhow::Result<Vec<WindData>>;
119
120    fn get_cannon(&self) -> &impl Cannon;
121}
122
123#[derive(Archive, Serialize, Deserialize, Debug, Clone, Copy, Default)]
124pub enum VariableType {
125    #[default]
126    StaticOnly, // statically supported but no dynamic conversion implemented
127    U8,
128    I32,
129    F32,
130    F64,
131}
132
133impl From<u8> for VariableType {
134    fn from(value: u8) -> Self {
135        match value {
136            1 => Self::U8,
137            2 => Self::I32,
138            3 => Self::F32,
139            4 => Self::F64,
140            _ => Self::default(),
141        }
142    }
143}
144
145impl From<VariableType> for u8 {
146    fn from(value: VariableType) -> Self {
147        match value {
148            VariableType::StaticOnly => 0,
149            VariableType::U8 => 1,
150            VariableType::I32 => 2,
151            VariableType::F32 => 3,
152            VariableType::F64 => 4,
153        }
154    }
155}
156
157use rkyv::rancor::Error as RkyvError;
158
159// Trait for types that can be Sent (Serialized).
160// Requires Sized, Send, Sync, 'static, and the specific rkyv Serialize bound.
161pub trait Sendable: Sized + Send + Sync + 'static
162where
163    Self: for<'b> Serialize<HighSerializer<AlignedVec, ArenaHandle<'b>, RkyvError>>,
164{
165}
166// Blanket implementation for Sendable. Any type meeting the bounds is Sendable.
167impl<T> Sendable for T
168where
169    T: Sized + Send + Sync + 'static,
170    T: for<'b> Serialize<HighSerializer<AlignedVec, ArenaHandle<'b>, RkyvError>>,
171{
172}
173
174#[async_trait::async_trait]
175pub trait Cannon: Send + Sync + 'static {
176    /// Initialize a 1:1 connection to the target. Ports are shared using the sea network internally.
177
178    /// Dump the data to the target.
179    async fn shoot<'b, T: Sendable>(
180        &self,
181        targets: &'b [crate::NetworkShipAddress],
182        id: u32,
183        data: &T,
184        variable_type: VariableType,
185        variable_name: &str,
186    ) -> anyhow::Result<()>;
187
188    /// Catch the dumped data from the source.
189    /// The returning Vec can contain previously missed entities of T from existing sync connections.
190    /// The first item of T is the newest, followed by incremental older ones.
191    async fn catch<T>(&self, id: u32) -> anyhow::Result<Vec<T>>
192    where
193        T: Send,
194        T: Archive,
195        T::Archived: for<'a> CheckBytes<HighValidator<'a, rkyv::rancor::Error>>
196            + Deserialize<T, Strategy<Pool, rkyv::rancor::Error>>;
197
198    async fn catch_dyn(&self, id: u32) -> anyhow::Result<Vec<(String, VariableType, String)>>;
199}
200
201#[derive(Clone, Debug, Default, Copy, Archive, Serialize, Deserialize, PartialEq)]
202pub struct TimeMsg {
203    pub sec: i32,
204    pub nanosec: u32,
205}
206
207#[derive(Clone, Debug, Default, PartialEq, Archive, Serialize, Deserialize)]
208pub struct Header {
209    pub seq: u32,
210    pub stamp: TimeMsg,
211    pub frame_id: String,
212}
213
214pub type WindData = BagMsg;
215
216#[async_trait::async_trait]
217pub trait Coordinator: Send + Sync + 'static {
218    async fn rat_action_request_queue(
219        &self,
220        ship: String,
221    ) -> anyhow::Result<tokio::sync::broadcast::Receiver<String>>;
222
223    async fn blow_wind(&self, ship: String, data: Vec<WindData>) -> anyhow::Result<()>;
224
225    async fn rat_action_send(
226        &self,
227        ship: String,
228        action: ActionPlan,
229        lock_until_ack: bool,
230    ) -> anyhow::Result<()>;
231}