1pub mod builtin;
2pub mod custom_builder;
3pub mod file_builder;
4pub mod text_builder;
5pub mod ros_nav;
6pub mod ros_core;
7
8use crate::get_pb;
9
10use crate::runtime::action::keeper::{ActionImpl, ActionKeeper};
11use crate::runtime::action::{Action, ActionName, Impl, ImplAsync, ImplRemote};
12use crate::runtime::blackboard::BlackBoard;
13use crate::runtime::builder::custom_builder::CustomForesterBuilder;
14use crate::runtime::builder::file_builder::FileForesterBuilder;
15use crate::runtime::builder::text_builder::TextForesterBuilder;
16use crate::runtime::env::RtEnv;
17
18use crate::runtime::forester::{serv, Forester};
19use crate::runtime::rtree::builder::RtNodeBuilder;
20use crate::runtime::rtree::rnode::RNodeId;
21use crate::runtime::rtree::{RuntimeTree, RuntimeTreeStarter};
22use crate::runtime::{RtOk, RtResult, RuntimeError};
23use crate::tracer::Tracer;
24use crate::tree::project::{FileName, TreeName};
25
26use std::collections::HashMap;
27use std::path::PathBuf;
28use std::sync::{Arc, Mutex};
29use crate::runtime::env::daemon::{DaemonName, Daemon};
30use crate::runtime::env::daemon::context::DaemonContext;
31
32pub enum ForesterBuilder {
85 Files {
86 cfb: CommonForesterBuilder,
87 delegate: FileForesterBuilder,
88 error: Option<String>,
89 },
90 Text {
91 cfb: CommonForesterBuilder,
92 delegate: TextForesterBuilder,
93 error: Option<String>,
94 },
95 Code {
96 cfb: CommonForesterBuilder,
97 delegate: CustomForesterBuilder,
98 error: Option<String>,
99 },
100}
101
102impl ForesterBuilder {
103 pub fn from_fs() -> ForesterBuilder {
104 ForesterBuilder::Files {
105 cfb: CommonForesterBuilder::new(),
106 delegate: FileForesterBuilder::new(),
107 error: None,
108 }
109 }
110 pub fn from_text() -> ForesterBuilder {
111 ForesterBuilder::Text {
112 cfb: CommonForesterBuilder::new(),
113 delegate: TextForesterBuilder::new(),
114 error: None,
115 }
116 }
117 pub fn from_code() -> ForesterBuilder {
118 ForesterBuilder::Code {
119 cfb: CommonForesterBuilder::new(),
120 delegate: CustomForesterBuilder::new(),
121 error: None,
122 }
123 }
124
125 pub fn root(&mut self, root: PathBuf) {
127 match self {
128 ForesterBuilder::Files { delegate, .. } => {
129 delegate.root(root);
130 }
131 ForesterBuilder::Text { error, .. } | ForesterBuilder::Code { error, .. } => {
132 let _ = error.insert("This type of builder does not accept root folder. Only `from_file_system` builder accept it.".to_string());
133 }
134 }
135 }
136 pub fn main_file(&mut self, main_file: FileName) {
138 match self {
139 ForesterBuilder::Files { delegate, .. } => {
140 delegate.main_file(main_file);
141 }
142 ForesterBuilder::Text { error, .. } | ForesterBuilder::Code { error, .. } => {
143 let _ = error.insert("This type of builder does not accept main_file. Only `from_file_system` builder accept it.".to_string());
144 }
145 }
146 }
147 pub fn main_tree(&mut self, main_tree: TreeName) {
149 match self {
150 ForesterBuilder::Files { delegate, .. } => {
151 delegate.main_tree(main_tree);
152 }
153 ForesterBuilder::Text { error, .. } | ForesterBuilder::Code { error, .. } => {
154 let _ = error.insert("This type of builder does not accept main_tree. Only `from_file_system` builder accept it.".to_string());
155 }
156 }
157 }
158
159 pub fn text(&mut self, txt: String) {
165 match self {
166 ForesterBuilder::Text { delegate, .. } => {
167 delegate.text(txt);
168 }
169 ForesterBuilder::Files { error, .. } | ForesterBuilder::Code { error, .. } => {
170 let _ = error.insert("This type of builder does not accept code as text. Only `from_text` builder accept it.".to_string());
171 }
172 }
173 }
174 pub fn add_rt_node(&mut self, node_b: RtNodeBuilder) -> RNodeId {
176 match self {
177 ForesterBuilder::Code { delegate, .. } => delegate.add_rt_node(node_b),
178 ForesterBuilder::Files { error, .. } | ForesterBuilder::Text { error, .. } => {
179 let _ = error.insert("This type of builder does not accept code as text. Only `from_text` builder accept it.".to_string());
180 0
181 }
182 }
183 }
184
185
186 pub fn register_daemon(&mut self, daemon: Daemon) {
188 self.cfb().register_daemon(daemon);
189 }
190
191 pub fn register_named_daemon(&mut self, name: DaemonName, daemon: Daemon) {
194 self.cfb().register_named_daemon(name, daemon);
195 }
196
197
198 pub fn register_sync_action<A>(&mut self, name: &str, action: A)
200 where
201 A: Impl + 'static,
202 {
203 self.cfb().register_sync_action(name, action);
204 }
205 pub fn register_async_action<A>(&mut self, name: &str, action: A)
207 where
208 A: ImplAsync + 'static,
209 {
210 self.cfb().register_async_action(name, action);
211 }
212
213 pub fn register_remote_action<A>(&mut self, name: &str, action: A)
215 where
216 A: ImplRemote + 'static,
217 {
218 self.cfb().register_remote_action(name, action);
219 }
220 pub fn http_serv(&mut self, port: u16) {
222 self.cfb().http_serv(port)
223 }
224
225 pub fn tracer(&mut self, tr: Tracer) {
227 self.cfb().tracer(tr);
228 }
229 pub fn bb_load(&mut self, bb: String) {
231 self.cfb().bb_load(bb);
232 }
233
234 pub fn rt_env(&mut self, env: RtEnv) {
237 self.cfb().rt_env(env);
238 }
239
240 pub fn build(self) -> RtResult<Forester> {
242 self.build_with(|| ActionImpl::Absent)
243 }
244
245 pub fn build_with<T>(self, default_action: T) -> RtResult<Forester>
247 where
248 T: Fn() -> ActionImpl,
249 {
250 self.error()?;
251
252 let (
253 tree,
254 actions,
255 action_names,
256 daemons,
257 tr,
258 env,
259 bb_load,
260 root,
261 port
262 ) = match self {
263 ForesterBuilder::Files { delegate, cfb, .. } => {
264 let root = delegate.root.clone();
265 let project = delegate.build()?;
266 let RuntimeTreeStarter {
267 tree,
268 std_actions,
269 actions,
270 } = RuntimeTree::build(project)?;
271 let mut impl_actions = cfb.actions;
272
273 for (action_name, file_name) in std_actions.iter() {
274 impl_actions.insert(
275 action_name.clone(),
276 builtin::pick_action(action_name, file_name)?,
277 );
278 }
279 (
280 tree,
281 impl_actions,
282 actions,
283 cfb.daemons,
284 cfb.tracer,
285 cfb.env,
286 cfb.bb_load,
287 root,
288 cfb.port,
289 )
290 }
291 ForesterBuilder::Text { delegate, cfb, .. } => {
292 let project = delegate.build()?;
293 let RuntimeTreeStarter {
294 tree,
295 std_actions,
296 actions,
297 } = RuntimeTree::build(project)?;
298 let mut impl_actions = cfb.actions;
299 for (action_name, file_name) in std_actions.iter() {
300 let action = builtin::pick_action(action_name, file_name)?;
301 impl_actions.insert(action_name.clone(), action);
302 }
303 (
304 tree,
305 impl_actions,
306 actions,
307 cfb.daemons,
308 cfb.tracer,
309 cfb.env,
310 cfb.bb_load,
311 None,
312 cfb.port,
313 )
314 }
315 ForesterBuilder::Code { delegate, cfb, .. } => {
316 let (tree, actions) = delegate.build()?;
317 (
318 tree,
319 cfb.actions,
320 actions,
321 cfb.daemons,
322 cfb.tracer,
323 cfb.env,
324 cfb.bb_load,
325 None,
326 cfb.port,
327 )
328 }
329 };
330
331 let bb =
332 if let Some(bb_load_dump) = bb_load {
333 BlackBoard::load(&get_pb(&PathBuf::from(bb_load_dump), &root)?)?
334 } else { BlackBoard::default() };
335
336 let mut env = if let Some(e) = env {
337 e
338 } else {
339 RtEnv::try_new()?
340 };
341
342
343 let bb = Arc::new(Mutex::new(bb));
344 let tracer = Arc::new(Mutex::new(tr));
345
346 let context = DaemonContext::new(bb.clone(), tracer.clone());
347 for daemon_cfg in daemons.into_iter() {
348 match daemon_cfg {
349 DaemonTaskCfg::Unnamed(d) => {
350 env.start_daemon(d, context.clone())?;
351 }
352 DaemonTaskCfg::Named(name, d) => {
353 env.start_named_daemon(name, d, context.clone())?;
354 }
355 }
356 }
357
358 let env = Arc::new(Mutex::new(env));
359 let serv = if port.is_some() {
360 Some(serv::start(env.clone(), port, bb.clone(), tracer.clone())?)
361 } else {
362 None
363 };
364
365 let keeper = ActionKeeper::new_with(actions, action_names, default_action)?;
366
367 Forester::new(tree, bb, tracer, keeper, env, serv)
368 }
369
370 fn cfb(&mut self) -> &mut CommonForesterBuilder {
371 match self {
372 ForesterBuilder::Files { cfb, .. }
373 | ForesterBuilder::Text { cfb, .. }
374 | ForesterBuilder::Code { cfb, .. } => cfb,
375 }
376 }
377 fn error(&self) -> RtOk {
378 match &self {
379 ForesterBuilder::Files { error: Some(v), .. }
380 | ForesterBuilder::Text { error: Some(v), .. }
381 | ForesterBuilder::Code { error: Some(v), .. } => {
382 Err(RuntimeError::Unexpected(v.to_string()))
383 }
384 _ => Ok(()),
385 }
386 }
387}
388
389pub struct CommonForesterBuilder {
390 env: Option<RtEnv>,
391 tracer: Tracer,
392 bb_load: Option<String>,
393 actions: HashMap<ActionName, Action>,
394 daemons: Vec<DaemonTaskCfg>,
395 port: ServerPort,
396}
397
398impl CommonForesterBuilder {
399 pub fn new() -> Self {
400 Self {
401 env: None,
402 tracer: Tracer::noop(),
403 bb_load: None,
404 actions: HashMap::new(),
405 daemons: Vec::new(),
406 port: ServerPort::None,
407 }
408 }
409
410 pub fn register_daemon(&mut self, daemon: Daemon) {
412 self.daemons.push(DaemonTaskCfg::Unnamed(daemon));
413 }
414
415 pub fn register_named_daemon(&mut self, name: DaemonName, daemon: Daemon)
418 {
419 self.daemons.push(DaemonTaskCfg::Named(name, daemon));
420 }
421
422
423 pub fn register_sync_action<A>(&mut self, name: &str, action: A)
425 where
426 A: Impl + 'static,
427 {
428 self.actions
429 .insert(name.to_string(), Action::Sync(Box::new(action)));
430 }
431 pub fn register_async_action<A>(&mut self, name: &str, action: A)
433 where
434 A: ImplAsync + 'static,
435 {
436 self.actions
437 .insert(name.to_string(), Action::Async(Arc::new(action)));
438 }
439 pub fn register_remote_action<A>(&mut self, name: &str, action: A)
441 where
442 A: ImplRemote + 'static,
443 {
444 self.actions
445 .insert(name.to_string(), Action::Remote(Box::new(action)));
446 }
447 pub fn http_serv(&mut self, port: u16) {
449 self.port = ServerPort::Static(port)
450 }
451
452 pub fn tracer(&mut self, tr: Tracer) {
454 self.tracer = tr;
455 }
456 pub fn bb_load(&mut self, bb: String) {
458 self.bb_load = Some(bb);
459 }
460
461 pub fn rt_env(&mut self, env: RtEnv) {
464 self.env = Some(env);
465 }
466}
467
468#[derive(Debug, Clone)]
470pub enum ServerPort {
471 None,
472 Static(u16),
473}
474
475impl Default for ServerPort {
476 fn default() -> Self {
477 ServerPort::None
478 }
479}
480
481impl ServerPort {
482 fn is_some(&self) -> bool {
483 match self {
484 ServerPort::None => false,
485 _ => true,
486 }
487 }
488 fn get(&self) -> u16 {
489 match self {
490 ServerPort::None => 0,
491 ServerPort::Static(v) => *v,
492 }
493 }
494}
495
496pub enum DaemonTaskCfg {
497 Unnamed(Daemon),
498 Named(DaemonName, Daemon),
499}