zenoh_flow/model/record/
dataflow.rs

1//
2// Copyright (c) 2021 - 2023 ZettaScale Technology
3//
4// This program and the accompanying materials are made available under the
5// terms of the Eclipse Public License 2.0 which is available at
6// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7// which is available at https://www.apache.org/licenses/LICENSE-2.0.
8//
9// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10//
11// Contributors:
12//   ZettaScale Zenoh Team, <zenoh@zettascale.tech>
13//
14
15use crate::model::descriptor::{
16    FlattenDataFlowDescriptor, InputDescriptor, LinkDescriptor, OutputDescriptor,
17};
18use crate::model::record::connector::{ZFConnectorKind, ZFConnectorRecord};
19use crate::model::record::{LinkRecord, OperatorRecord, PortRecord, SinkRecord, SourceRecord};
20use crate::types::{NodeId, PortId, RuntimeId};
21use crate::zferror;
22use crate::zfresult::ErrorKind;
23use crate::Result as ZFResult;
24use serde::{Deserialize, Serialize};
25use std::collections::HashMap;
26use std::convert::TryFrom;
27use std::hash::{Hash, Hasher};
28use uuid::Uuid;
29
30/// A `DataFlowRecord` is an instance of a [`FlattenDataFlowDescriptor`](`FlattenDataFlowDescriptor`).
31#[derive(Serialize, Deserialize, Debug, Clone)]
32pub struct DataFlowRecord {
33    pub uuid: Uuid,
34    pub flow: String,
35    pub operators: HashMap<NodeId, OperatorRecord>,
36    pub sinks: HashMap<NodeId, SinkRecord>,
37    pub sources: HashMap<NodeId, SourceRecord>,
38    pub connectors: HashMap<NodeId, ZFConnectorRecord>,
39    pub links: Vec<LinkRecord>,
40    pub counter: u32,
41}
42
43impl DataFlowRecord {
44    /// Creates a new `DataFlowRecord` record from its YAML format.
45    ///
46    ///  # Errors
47    /// A variant error is returned if deserialization fails.
48    pub fn from_yaml(data: &str) -> ZFResult<Self> {
49        serde_yaml::from_str::<DataFlowRecord>(data)
50            .map_err(|e| zferror!(ErrorKind::ParsingError, e).into())
51    }
52
53    /// Creates a new `DataFlowRecord` from its JSON format.
54    ///
55    ///  # Errors
56    /// A variant error is returned if deserialization fails.
57    pub fn from_json(data: &str) -> ZFResult<Self> {
58        serde_json::from_str::<DataFlowRecord>(data)
59            .map_err(|e| zferror!(ErrorKind::ParsingError, e).into())
60    }
61
62    /// Returns the JSON representation of the `DataFlowRecord`.
63    ///
64    ///  # Errors
65    /// A variant error is returned if serialization fails.
66    pub fn to_json(&self) -> ZFResult<String> {
67        serde_json::to_string(&self).map_err(|e| zferror!(ErrorKind::SerializationError, e).into())
68    }
69
70    /// Returns the YAML representation of the `DataFlowRecord`.
71    ///
72    ///  # Errors
73    /// A variant error is returned if serialization fails.
74    pub fn to_yaml(&self) -> ZFResult<String> {
75        serde_yaml::to_string(&self).map_err(|e| zferror!(ErrorKind::SerializationError, e).into())
76    }
77
78    /// Returns the runtime mapping for the given node.
79    pub fn find_node_runtime(&self, id: &str) -> Option<RuntimeId> {
80        match self.operators.get(id) {
81            Some(o) => Some(o.runtime.clone()),
82            None => match self.sources.get(id) {
83                Some(s) => Some(s.runtime.clone()),
84                None => self.sinks.get(id).map(|s| s.runtime.clone()),
85            },
86        }
87    }
88
89    /// Finds the uid of the given node.
90    fn find_node_uid_by_id(&self, id: &NodeId) -> Option<u32> {
91        if let Some(o) = self.operators.get(id) {
92            return Some(o.uid);
93        }
94        if let Some(s) = self.sources.get(id) {
95            return Some(s.uid);
96        }
97        if let Some(s) = self.sinks.get(id) {
98            return Some(s.uid);
99        }
100        None
101    }
102
103    /// Find the port uid for the given couple of node, port.
104    fn find_port_id_in_node(&self, node_id: &NodeId, port_id: &PortId) -> Option<u32> {
105        let (inputs, outputs) = if let Some(op) = self.operators.get(node_id) {
106            (Some(&op.inputs), Some(&op.outputs))
107        } else if let Some(source) = self.sources.get(node_id) {
108            (None, Some(&source.outputs))
109        } else if let Some(sink) = self.sinks.get(node_id) {
110            (Some(&sink.inputs), None)
111        } else {
112            (None, None)
113        };
114
115        if let Some(outputs) = outputs {
116            if let Some(port_record) = outputs.iter().find(|&output| output.port_id == *port_id) {
117                return Some(port_record.uid);
118            }
119        }
120
121        if let Some(inputs) = inputs {
122            if let Some(port_record) = inputs.iter().find(|&input| input.port_id == *port_id) {
123                return Some(port_record.uid);
124            }
125        }
126
127        None
128    }
129
130    /// Adds the links.
131    ///
132    /// If the nodes are mapped to different machines it adds the couple of
133    /// connectors in between and creates the unique key expression
134    /// for the data to flow in Zenoh.
135    ///
136    ///  # Errors
137    /// A variant error is returned if validation fails.
138    fn add_links(&mut self, links: &[LinkDescriptor]) -> ZFResult<()> {
139        for l in links.iter() {
140            log::debug!("Adding link: {:?}…", l);
141            let from_runtime = match self.find_node_runtime(&l.from.node) {
142                Some(rt) => rt,
143                None => {
144                    log::error!("Could not find runtime for: {:?}", &l.from.node);
145                    return Err(zferror!(
146                        ErrorKind::Uncompleted,
147                        "Unable to find runtime for {}",
148                        &l.from.node
149                    )
150                    .into());
151                }
152            };
153
154            let to_runtime = match self.find_node_runtime(&l.to.node) {
155                Some(rt) => rt,
156                None => {
157                    log::error!("Could not find runtime for: {:?}", &l.to.node);
158                    return Err(zferror!(
159                        ErrorKind::Uncompleted,
160                        "Unable to find runtime for {}",
161                        &l.to.node
162                    )
163                    .into());
164                }
165            };
166
167            if from_runtime == to_runtime {
168                log::debug!("Adding link: {:?}… OK", l);
169                // link between nodes on the same runtime
170                self.links.push((l.clone(), self.counter).into());
171                self.counter += 1;
172            } else {
173                // link between node on different runtime
174                // here we have to create the connectors information
175                // and add the new links
176
177                let from_uid = self
178                    .find_node_uid_by_id(&l.from.node)
179                    .ok_or_else(|| zferror!(ErrorKind::NotFound))?;
180                let from_port_uid = self
181                    .find_port_id_in_node(&l.from.node, &l.from.output)
182                    .ok_or_else(|| zferror!(ErrorKind::NotFound))?;
183
184                // creating zenoh resource name
185                let z_resource_name = format!(
186                    "zf/data/{}/{}/{}/{}",
187                    &self.flow, &self.uuid, &from_uid, &from_port_uid
188                );
189
190                // We only create a sender if none was created for the same resource. The rationale
191                // is to avoid creating multiple publisher for the same resource in case an operator
192                // acts as a multiplexor.
193                if !self.connectors.iter().any(|(_id, c)| {
194                    c.kind == ZFConnectorKind::Sender && c.resource == z_resource_name
195                }) {
196                    // creating sender
197                    let sender_id: NodeId = format!(
198                        "sender-{}-{}-{}-{}",
199                        &self.flow, &self.uuid, &l.from.node, &l.from.output
200                    )
201                    .into();
202                    let sender = ZFConnectorRecord {
203                        kind: ZFConnectorKind::Sender,
204                        id: sender_id.clone(),
205                        resource: z_resource_name.clone(),
206                        link_id: PortRecord {
207                            uid: self.counter,
208                            port_id: l.from.output.clone(),
209                        },
210                        shared_memory_element_size: l.shared_memory_element_size,
211                        shared_memory_elements: l.shared_memory_elements,
212                        shared_memory_backoff: l.shared_memory_backoff,
213                        runtime: from_runtime,
214                    };
215                    self.counter += 1;
216
217                    // creating link between node and sender
218                    let link_sender = LinkDescriptor {
219                        from: l.from.clone(),
220                        to: InputDescriptor {
221                            node: sender_id.clone(),
222                            input: l.from.output.clone(),
223                        },
224                        shared_memory_element_size: l.shared_memory_element_size,
225                        shared_memory_elements: l.shared_memory_elements,
226                        shared_memory_backoff: l.shared_memory_backoff,
227                    };
228
229                    // storing info in the dataflow record
230                    self.connectors.insert(sender_id, sender);
231                    self.links.push((link_sender, self.counter).into());
232                    self.counter += 1;
233                }
234
235                // creating receiver
236                let receiver_id: NodeId = format!(
237                    "receiver-{}-{}-{}-{}",
238                    &self.flow, &self.uuid, &l.to.node, &l.to.input
239                )
240                .into();
241                let receiver = ZFConnectorRecord {
242                    kind: ZFConnectorKind::Receiver,
243                    id: receiver_id.clone(),
244                    resource: z_resource_name.clone(),
245                    link_id: PortRecord {
246                        uid: self.counter,
247                        port_id: l.to.input.clone(),
248                    },
249                    shared_memory_element_size: l.shared_memory_element_size,
250                    shared_memory_elements: l.shared_memory_elements,
251                    shared_memory_backoff: l.shared_memory_backoff,
252                    runtime: to_runtime,
253                };
254                self.counter += 1;
255
256                // Creating link between receiver and node
257                let link_receiver = LinkDescriptor {
258                    from: OutputDescriptor {
259                        node: receiver_id.clone(),
260                        output: l.to.input.clone(),
261                    },
262                    to: l.to.clone(),
263                    shared_memory_element_size: l.shared_memory_element_size,
264                    shared_memory_elements: l.shared_memory_elements,
265                    shared_memory_backoff: l.shared_memory_backoff,
266                };
267
268                // storing info in the data flow record
269                self.connectors.insert(receiver_id, receiver);
270                self.links.push((link_receiver, self.counter).into());
271                self.counter += 1;
272            }
273        }
274
275        Ok(())
276    }
277}
278
279impl TryFrom<(FlattenDataFlowDescriptor, Uuid)> for DataFlowRecord {
280    type Error = crate::zfresult::Error;
281
282    fn try_from(d: (FlattenDataFlowDescriptor, Uuid)) -> Result<Self, Self::Error> {
283        let (dataflow, id) = d;
284
285        let FlattenDataFlowDescriptor {
286            flow,
287            operators,
288            sources,
289            sinks,
290            links,
291            mapping,
292            global_configuration: _,
293        } = dataflow;
294
295        let mapping = mapping.map_or(HashMap::new(), |m| m);
296
297        let mut dfr = DataFlowRecord {
298            uuid: id,
299            flow,
300            operators: HashMap::with_capacity(operators.len()),
301            sinks: HashMap::with_capacity(sinks.len()),
302            sources: HashMap::with_capacity(sources.len()),
303            connectors: HashMap::new(),
304            links: Vec::new(),
305            counter: 0,
306        };
307
308        for o in operators.into_iter() {
309            // Converting inputs
310            let mut inputs: Vec<PortRecord> = vec![];
311            for i in o.inputs {
312                inputs.push((i, dfr.counter).into());
313                dfr.counter += 1;
314            }
315
316            // Converting outputs
317            let mut outputs: Vec<PortRecord> = vec![];
318            for o in o.outputs {
319                outputs.push((o, dfr.counter).into());
320                dfr.counter += 1;
321            }
322
323            let or = OperatorRecord {
324                id: o.id.clone(),
325                uid: dfr.counter,
326                inputs,
327                outputs,
328                uri: o.uri,
329                configuration: o.configuration,
330                runtime: mapping
331                    .get(&o.id)
332                    .ok_or_else(|| zferror!(ErrorKind::MissingConfiguration))
333                    .cloned()?,
334            };
335            dfr.operators.insert(o.id, or);
336            dfr.counter += 1;
337        }
338
339        for s in sources.into_iter() {
340            let mut outputs: Vec<PortRecord> = vec![];
341            for o in s.outputs {
342                outputs.push((o, dfr.counter).into());
343                dfr.counter += 1;
344            }
345
346            let sr = SourceRecord {
347                id: s.id.clone(),
348                uid: dfr.counter,
349                outputs,
350                uri: s.uri,
351                configuration: s.configuration,
352                runtime: mapping
353                    .get(&s.id)
354                    .ok_or_else(|| zferror!(ErrorKind::MissingConfiguration))
355                    .cloned()?,
356            };
357            dfr.sources.insert(s.id, sr);
358            dfr.counter += 1;
359        }
360
361        for s in sinks.into_iter() {
362            let mut inputs: Vec<PortRecord> = Vec::with_capacity(s.inputs.len());
363            for i in s.inputs {
364                inputs.push((i, dfr.counter).into());
365                dfr.counter += 1;
366            }
367
368            let sr = SinkRecord {
369                id: s.id.clone(),
370                uid: dfr.counter,
371                inputs,
372                uri: s.uri,
373                configuration: s.configuration,
374                runtime: mapping
375                    .get(&s.id)
376                    .ok_or_else(|| zferror!(ErrorKind::MissingConfiguration))
377                    .cloned()?,
378            };
379            dfr.sinks.insert(s.id, sr);
380            dfr.counter += 1;
381        }
382
383        dfr.add_links(&links)?;
384
385        Ok(dfr)
386    }
387}
388
389impl Hash for DataFlowRecord {
390    fn hash<H: Hasher>(&self, state: &mut H) {
391        self.uuid.hash(state);
392        self.flow.hash(state);
393    }
394}
395
396impl PartialEq for DataFlowRecord {
397    fn eq(&self, other: &DataFlowRecord) -> bool {
398        self.uuid == other.uuid && self.flow == other.flow
399    }
400}
401
402impl Eq for DataFlowRecord {}