From 55bf8d85b909e4c821695b1f5444ad9a0796724c Mon Sep 17 00:00:00 2001 From: Hevake Lee Date: Tue, 23 Sep 2025 21:36:01 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E7=BC=96=E5=86=9919-action-tree-buildin-ac?= =?UTF-8?q?tions.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 19-action-tree-buildin-actions.md | 461 ++++++++++++++++++++++++++++-- 1 file changed, 437 insertions(+), 24 deletions(-) diff --git a/19-action-tree-buildin-actions.md b/19-action-tree-buildin-actions.md index 823a415..77a617c 100644 --- a/19-action-tree-buildin-actions.md +++ b/19-action-tree-buildin-actions.md @@ -4,28 +4,441 @@ ![动作树类图](images/067-action-classes.png) ## 1. 基类动作 -### Action -### EventAction -### AssembleAction -### CompositeAction - -## 2. 组合动作(枝干) -### SequenceAction -### ParalleAction -### IfElseAction -### IfThenAction -### LoopAction -### LoopIfAction -### RepeatAction -### WrapperAction -### SwitchAction - -## 3. 执行动作(子叶) -### FunctionAction -### SleepAction -### SuccAction -### FailAction -### ExecuteCmdAction -### ExecuteInThreadAction -### DummyAction +### Action(动作基类) +它是所有动作的基类。 +它有以以5种状态: + +- `Idle`,未启动; +- `Running`,执行中; +- `Pause`,暂停中; +- `Finished`,已结束(自发的); +- `Stoped`,已被停止(被动的); + +其状态可以通过`state()`方法获得; + +如果`Action`是自发地结束,那么它会有2个结果:成功、失败; +可通过`result()`方法获取结果; + +此外,它还对外主要提供以下几个操作方法: + +- `start()`,启动动作; +- `stop()`,停止动作; +- `pause()`,暂停动作; +- `resume()`,恢复动作; +- `reset()`,重置动作; + +以及两个超时相关的方法: + +- `setTimeout()`,设置动作的超时时长; +- `resetTimeout()`,清除动作的超时时长; + +其对子类提供了两个方法: + +- `finish()`,结束动作; +- `block()`,暂停动作,并抛出异常; + +定义了以下虚函数,由子类根据业务需要进行实现: + +- `onStart()`,动作启动时要执行的动作; +- `onStop()`,动作被停止时要执行的动作; +- `onPause()`,动作被暂停时要执行的动作; +- `onResume()`,动作被恢复时要执行的动作; +- `onReset()`,动作被重置时要执行的动作; +- `onFinished()`,动作结束时要执行的动作; +- `onFinal()`,动作在终止或被停止时要执行的动作; +- `onBlock()`,动作阻塞时要执行的动作。默认向上传递; +- `onTimeout()`,动作在超时时要执行的动作。默认finish(false); + +需要说明的是: + +- `onStart()`与`onStop()`通常是必须要重写的,其它则可以根据需要决定; +- 除了`onFinal()`与`onTimeout()`在重写时不需要调Action被重写的函数外,其它都需要。否则会报警告; + +还有其它辅助方法: + +- `set_label()`,设置动作标签; + +具体如何通过继承`Action`来创建一个自己的动作类,可以参考`SleepAction`与`FunctionAction`的实现。 + +### EventAction(事件动作基类) +它继承于`Action`,与`EventPublisher`配合使用,是一种能够监听由`EventPublisher`发布的外部事件的动作。 +当它被start 或 resume 的时候,它就开始订阅EventPublisher发布的事件。当它被stop,pause,stop或自行finish时,就取消事件的订阅。在它处于活动状态下,就能够接受`EventPublisher`发布的事件。 + +它不能独自使用,必须被派生。 +其派生类要实现`onEvent()`方法,以指明接收到事件的时候该如何处理。 +例如: + +``` +bool MyEventAction::onEvent(tbox::flow::Event e) { + if (e.id == 400012) { + ... + return true; //! 表示该事件被本Action处理,不再进行传递 + } + + return false; //! 表示该事件被本Action忽略,继续传递给下一个EventAction处理 +} +``` + +### AssembleAction(组装动作基类) +它是组装动作的基类。 +它定义了组装子动作的几个接口: + +- `setChild()`,设置子动作。常用于只能设置一个子动作的情况,比如:`LoopAction`,`Composite`,`WrapperAction`; +- `setChildAs()`,设置子动作为指定的角色。用于设置多个子动作,比如:`IfElseAction`,`LoopIfAction`; +- `addChild()`,追加子动作。常用于需要多次添加多个子动作的情况,比如:`SequenceAction`; +- `addChildAs()`,追加子动作为指定的角色。用于设置多个子动作,但同一个角色会有多个子动作,比如:`IfThenAction`; + +还提供了设置最终动作完成时要执行的动作的方法:`setFinalCallback()`。 +这个方法非常有用,常用于在整个组合动作完成之后(无论是正常还是异常,主动还是被动结束的)进行清理的动作; + +它有一个常见的派生类:`SerialAssembleAction`序列型组装动作。它还有另一个派生类:`ParallelAction`并列动作。后面详述; + +#### SerialAssembleAction(序列型组装动作基类) +它继承于`AssembleAction`,是一种用于同一时刻只会有一个子动作处于执行状态的组合动作。 +下面的组合动作,除了`ParalleAction`,均是继承于它。 + +它为子类提供了四个常用方法,供子类使用: + +- `startThisAction()`,启动指定的子动作; +- `stopCurrAction()`,停止当前的子动作; +- `handleChildFinishEvent()`,处理子动作完成的事件; +- `onLastChildFinshed()`,处理最后的子动作完成的事件; + +具体使用方法,可以参考`IfElseAction`的实现。 + +### CompositeAction(组合动作基类) + +## 2. 执行动作(子叶) +### FunctionAction(函数动作) +执行指定函数,并将返回值作为结果的动作。 +该动作常用于: + +- 作为判定条件; +- 作为执行的动作; + +它通常不独立使用,通常作为子动作添加到组合动作中。 + +使用方法: +``` +auto &loop = *ctx().loop(); + +//! 构造中指定函数 +auto func_1_action = new tbox::flow::FunctionAction(loop, [] { return true; }); + +//! 构造中不指定函数,在setFunc()中指定 +auto func_2_action = new tbox::flow::FunctionAction(loop); +func_2_action->setFunc([] { LogTag(); return true; }); + +//! 带Reason的函数 +auto func_3_action = new tbox::flow::FunctionAction(loop); +func_3_action->setFunc([] (tbox::flow::Reason &r) { r.code = 1000; return false; }); +``` + +由于`FunctionAction`自身无法体现它的功能,在导出GraphViz的时候看不出它的功能。建议在创建了`FunctionAction`对象之后,通过其`set_label()`方法设置其标签。 + +### SleepAction(延时动作) +顾明思义,延时指定时长。 + +### SuccAction(固定成功动作) +直接返回成功的动作。类拟于: +``` +return true; +``` + +### FailAction(固定失败动作) +直接返回失败的动作。类拟于: +``` +return false; +``` + +### ExecuteCmdAction(执行System指令动作) +委托子线程执行system命令的动作。 +该动作常用于执行类似于curl,wget,zip,unzip或者执行指定脚本等过程。 + +使用示例: +``` +auto &loop = *ctx().loop(); +auto &thread_executor = *ctx().thread_pool(); + +auto unzip_cmd_action = new tbox::flow::ExecuteCmdAction(loop, thread_executor, "unzip /tmp/fw-20150923.zip"); +``` + +### ExecuteInThreadAction(在子线程中执行函数的动作) +委托子线程执行指定函数的动作。 +该动作常用于包装执行保存数据、进行CPU密集运算、执行第三方阻塞性函数的操作; + +使用示例: +``` +auto &loop = *ctx().loop(); +auto &thread_executor = *ctx().thread_pool(); + +auto save_data_action = new tbox::flow::ExecuteInThreadAction(loop, thread_executor); +save_data_action->setFunc( + [filepath, content] { + return tbox::util::fs::WriteStringToTextFile(filepath, content); + } +); +``` + +### DummyAction(桩动作) +该动作有点类似于调试断点,使用场景较少。 + +## 3. 组合动作(枝干) +### SequenceAction(顺序组合动作) +它有三种模式: + +- AllFinish(默认),依次执行,直至所有结束(无论成功或失败) +- AnyFail,任一失败则结束,常用于下一个动作依赖上一个动作成功的顺序动作 +- AnySucc,任一成功则结束,常用于备选方案,上一个动作不成功,则尝试后面的动作 + +它实现类似以下的动作: +``` +bool is_succ = true; +for (child_action : child_action_vec) { + is_succ = child_action(); + if ((mode == AnySucc && is_succ) || + (mode == AnyFail && !is_succ)) + break; +} +return is_succ; +``` + +使用示例: +``` +auto &loop = *ctx().loop(); + +auto seq_action = new tbox::flow::SequenceAction(loop); +auto step_1_action = new tbox::flow::FunctionAction(loop, []( LogDbg("Hello"); return true; )); +auto step_2_action = new tbox::flow::SleepAction(loop, std::chrono::seconds(2)); +auto step_3_action = new tbox::flow::FunctionAction(loop, []( LogDbg("cpp-tbox"); return true; )); + +seq_action->addChild(step_1_action); +seq_action->addChild(step_2_action); +seq_action->addChild(step_3_action); + +seq_action->start(); +``` + +### ParalleAction(并行组合动作) +与`SequenceAction`相对,它是并行动作。它同时启动其下的所有子动作。 +它有三种模式: + +- AllFinish,所有的子动作都结束了才结束; +- AnyFail,只要其中有一个失败了,就结束; +- AnySucc,只要其中有一个成功了,就结束; + +使用方式与`SequenceAction`几乎一样。 + +### IfElseAction(IfElse条件分支组合动作) +实现if-else逻辑的动作。 + +``` +if (cond_action()) { + return then_action(); +} else { + return else_action(); +} +``` +或 +``` +if (cond_action()) { + return then_action(); +} else { + return true; +} +``` +或 +``` +if (cond_action()) { + return true; +} else { + return else_action(); +} +``` + +使用示例: +``` +auto &loop = *ctx().loop(); + +auto if_else_action = new tbox::flow::IfElseAction(loop); +auto cond_action = new tbox::flow::FunctionAction(loop, []( return true; )); +auto then_action = new tbox::flow::FunctionAction(loop, []( LogDbg("Then"); return true; )); +auto else_action = new tbox::flow::FunctionAction(loop, []( LogDbg("Else"); return true; )); + +if_else_action->setChildAs(cond_action, "if"); +if_else_action->setChildAs(then_action, "then"); +if_else_action->setChildAs(else_action, "else"); + +if_else_action->start(); +``` + +### IfThenAction(IfThen条件分支组合动作) +它是`IfElseAction`的扩展,可实现连续的条件判断。 + +类似于: +``` +if (cond_1_action()) { + return then_1_action; +} else if (cond_2_action) { + return then_2_action; +} else if (cond_xx_cond) { + ... +} else { + return false; +} +``` + +使用示例: +``` +auto &loop = *ctx().loop(); + +auto if_then_action = new tbox::flow::IfThenAction(loop); +auto cond_1_action = new tbox::flow::FunctionAction(loop, []( return false; )); +auto then_1_action = new tbox::flow::FunctionAction(loop, []( LogDbg("Then_1"); return true; )); +auto cond_2_action = new tbox::flow::FunctionAction(loop, []( return false; )); +auto then_2_action = new tbox::flow::FunctionAction(loop, []( LogDbg("Then_2"); return true; )); +auto else_action = new tbox::flow::FunctionAction(loop, []( LogDbg("Else"); return true; )); + +if_then_action->addChildAs(cond_1_action, "if"); +if_then_action->addChildAs(then_1_action, "then"); +if_then_action->addChildAs(cond_2_action, "if"); +if_then_action->addChildAs(then_2_action, "then"); +if_then_action->addChildAs(new tbox::flow::SuccAction(loop), "if"); +if_then_action->addChildAs(else_action, "else"); + +if_then_action->start(); +``` + +### LoopAction(循环组合动作) +它反复执行其子动作。 +它有三种模式: + +- Forever,永远不会结束; +- UntilFail,直到失败了才结束; +- UntilSucc,直到成功了才结束; + +类似于以下的功能: +``` +while (true) { + bool is_succ = child_action(); + if ((mode == UntilSucc && is_succ) || + (mode == UntilFail && !is_succ)) + return is_succ; +} +``` + +使用示例: +``` +int count = 10; + +auto &loop = *ctx().loop(); + +auto loop_action = new tbox::flow::LoopAction(loop, tbox::flow::LoopAction::Mode::kUntilFail); +auto exec_action = new tbox::flow::FunctionAction(loop, [&]( LogDbg("count:%d", --count); return count > 0; )); + +loop_action->setChild(exec_action); + +loop_action->start(); +``` + +### LoopIfAction(条件循环组合动作) +该动用是对`LoopAction`的补充,是否继续循环由单独的Action来判定。 +它实现类似于: +``` +while (cond_action()) { + exec_action(); +} +``` + +使用示例: +``` +int count = 10; + +auto &loop = *ctx().loop(); + +auto loop_if_action = new tbox::flow::LoopAction(loop, tbox::flow::LoopAction::Mode::kUntilFail); +auto cond_action = new tbox::flow::FunctionAction(loop, [&]( return --count > 0; )); +auto exec_action = new tbox::flow::FunctionAction(loop, [&]( LogDbg("count:%d", count); return true; )); + +loop_if_action->setChildAs(cond_action, "if"); +loop_if_action->setChildAs(exec_action, "exec"); + +loop_if_action->start(); +``` + +### RepeatAction(重复组合动作) +实现重复指定次数的动作。 +它有三种模式: + +- NoBreak,不提前结束; +- BreakFail,如果失败,则提前结束; +- BreakSucc,如果成功,则提前结束。常用于动作重试,并指定了尝试次数; + +实现类似以下功能: +``` +for (int i = 0; i < times; ++i) { + bool is_succ = child_action(); + if ((mode == BreakSucc && is_succ) || + (mode == BreakFail && !is_succ)) + return is_succ; +} +return true; +``` +使用示例: +``` +auto &loop = *ctx().loop(); + +auto repeat_action = new tbox::flow::LoopAction(loop, 10, tbox::flow::LoopAction::Mode::kBreakSucc); +auto exec_action = new tbox::flow::FunctionAction(loop, [&]( LogDbg("Repeat"); return false; )); + +repeat_action->setChild(exec_action); + +repeat_action->start(); +``` + +### WrapperAction(结果包装组合动作) +如果不希望某个动作的结果影响到流程执行,可使用`WrapperAction`对它进行包装。 +它有四种模式: + +- Normal,透传。即子动作返回什么,它就返回什么; +- Invert,取反,即子动作返回成功,它就返回失败。子动作返回失败,它就返回成功; +- AlwaySucc,固定为成功,不管子动作返回什么; +- AlwayFail,固定为失败,不管子动作返回什么; + +### SwitchAction(Switch分支组合动作) +用于实现switch-case逻辑。 +类似于: +``` +switch (cond_action()) { + case xxx: return xxx_action(); + case yyy: return yyy_action(); + default: return default_action(); +} +return false; +``` + +使用示例: +``` +auto &loop = *ctx().loop(); + +auto switch_action = new tbox::flow::SwitchAction(loop); + +auto cond_action = new tbox::flow::FunctionAction( + [](tbox::flow::Action::Reason &r) { + r.message = "case:B"; + return true; + } +); + +auto a_action = new tbox::flow::FunctionAction(loop, []{ LogDbg("A"); return true; }); +auto b_action = new tbox::flow::FunctionAction(loop, []{ LogDbg("B"); return true; }); +auto default_action = new tbox::flow::FunctionAction(loop, []{ LogDbg("Default"); return true; }); + +switch_action->setChildAs(cond_action, "switch"); +switch_action->setChildAs(a_action, "case:A"); +switch_action->setChildAs(b_action, "case:B"); +switch_action->setChildAs(default_action, "default"); + +switch_action->start(); +``` -- Gitee From f3abbc3fc561c37e0b3ca7d6eac3f68e7926edce Mon Sep 17 00:00:00 2001 From: Hevake Lee Date: Tue, 23 Sep 2025 22:32:28 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 19-action-tree-buildin-actions.md | 122 +++++++++++++++++++++++++----- 1 file changed, 101 insertions(+), 21 deletions(-) diff --git a/19-action-tree-buildin-actions.md b/19-action-tree-buildin-actions.md index 77a617c..9fdfdae 100644 --- a/19-action-tree-buildin-actions.md +++ b/19-action-tree-buildin-actions.md @@ -1,6 +1,6 @@ -# ActionTree自建动作 +# ActionTree内建动作 -## 类图 +## 动作类图 ![动作树类图](images/067-action-classes.png) ## 1. 基类动作 @@ -33,7 +33,7 @@ - `setTimeout()`,设置动作的超时时长; - `resetTimeout()`,清除动作的超时时长; -其对子类提供了两个方法: +对子类提供了两个方法: - `finish()`,结束动作; - `block()`,暂停动作,并抛出异常; @@ -53,48 +53,92 @@ 需要说明的是: - `onStart()`与`onStop()`通常是必须要重写的,其它则可以根据需要决定; -- 除了`onFinal()`与`onTimeout()`在重写时不需要调Action被重写的函数外,其它都需要。否则会报警告; +- 除了`onFinal()`与`onTimeout()`在重写时不需要调Action被重写的函数外,其它都需要。否则会报警告且工作异常; + +如: +``` +void MyAction::onStart() { + Action::onStart(); //! 注意:放在前面 + ... +} + +void MyAction::onStop() { + ... + Action::onStop(); //! 注意:放在后面 +} + +void MyAction::onPause() { + ... + Action::onPause(); //! 注意:放在后面 +} + +void MyAction::onResume() { + Action::onResume(); //! 注意:放在前面 + ... +} + +void MyAction::onReset() { + ... + Action::onReset(); //! 注意:放在后面 +} + +void MyAction::onBlock(const Reason &r, const Trace &t) { + ... + Action::onBlock(r, t); //! 放后面 +} + +void MyAction::onFinished(bool is_succ, const Reason &r, const Trace &t) { + ... + Action::onFinished(is_succ, r, t); //! 放后面 +} +``` +不仅是`Action`有这个要求,其它被派生的动作类,如:`EventAction`,`CompositeAction`,`AssembleAction` 等也是如此。 还有其它辅助方法: -- `set_label()`,设置动作标签; +- `set_label()`,设置动作标签,以便在日志以及GraphViz中可以展示功能; +- `toJson()`,导出内部数据到JSON对象; +- `isReady()`,检查参数以及子对象是否准备好; +- `setParent()`,建立动作之间的父子关系; +- `vars()`,获取白板对象,用于多动作之间传递中间数据; 具体如何通过继承`Action`来创建一个自己的动作类,可以参考`SleepAction`与`FunctionAction`的实现。 ### EventAction(事件动作基类) -它继承于`Action`,与`EventPublisher`配合使用,是一种能够监听由`EventPublisher`发布的外部事件的动作。 -当它被start 或 resume 的时候,它就开始订阅EventPublisher发布的事件。当它被stop,pause,stop或自行finish时,就取消事件的订阅。在它处于活动状态下,就能够接受`EventPublisher`发布的事件。 +它继承于`Action`与`EventSubscriber`,与`EventPublisher`配合使用,是一种能够监听由`EventPublisher`发布的外部事件的动作。 +在它处于活动状态下,就能够接受`EventPublisher`发布的事件。具体表现为,当它被 start 或 resume 的时候,它就开始订阅`EventPublisher`发布的事件。当它被 stop, pause, stop 或自行 finish, block 时,就取消事件的订阅。 它不能独自使用,必须被派生。 -其派生类要实现`onEvent()`方法,以指明接收到事件的时候该如何处理。 +其派生类要实现`onEvent()`方法,以指明接收到事件的时候该如何处理。 例如: ``` bool MyEventAction::onEvent(tbox::flow::Event e) { if (e.id == 400012) { ... - return true; //! 表示该事件被本Action处理,不再进行传递 + return true; } - return false; //! 表示该事件被本Action忽略,继续传递给下一个EventAction处理 + return false; } ``` +这里要注意返回值。返回true,表示该事件被本Action处理,不再进行传递。返回false,表示该事件被本Action忽略,继续传递给下一个EventAction处理。 ### AssembleAction(组装动作基类) 它是组装动作的基类。 它定义了组装子动作的几个接口: - `setChild()`,设置子动作。常用于只能设置一个子动作的情况,比如:`LoopAction`,`Composite`,`WrapperAction`; -- `setChildAs()`,设置子动作为指定的角色。用于设置多个子动作,比如:`IfElseAction`,`LoopIfAction`; -- `addChild()`,追加子动作。常用于需要多次添加多个子动作的情况,比如:`SequenceAction`; -- `addChildAs()`,追加子动作为指定的角色。用于设置多个子动作,但同一个角色会有多个子动作,比如:`IfThenAction`; +- `setChildAs()`,设置子动作为指定的角色。用于设置多个子动作,每个子动作担当不同的角色,比如:`IfElseAction`,`LoopIfAction`; +- `addChild()`,追加子动作。常用于添加多次添加同种角色的多个子动作,比如:`SequenceAction`; +- `addChildAs()`,追加子动作为指定的角色。用于设置多个子动作,但多个子动作可能会担当同一个角色,比如:`IfThenAction`; 还提供了设置最终动作完成时要执行的动作的方法:`setFinalCallback()`。 -这个方法非常有用,常用于在整个组合动作完成之后(无论是正常还是异常,主动还是被动结束的)进行清理的动作; +这个方法非常有用,常用于在整个组合动作完成(无论是正常还是异常,主动还是被动结束的)之后进行清理的动作; 它有一个常见的派生类:`SerialAssembleAction`序列型组装动作。它还有另一个派生类:`ParallelAction`并列动作。后面详述; -#### SerialAssembleAction(序列型组装动作基类) +### SerialAssembleAction(序列型组装动作基类) 它继承于`AssembleAction`,是一种用于同一时刻只会有一个子动作处于执行状态的组合动作。 下面的组合动作,除了`ParalleAction`,均是继承于它。 @@ -108,13 +152,14 @@ bool MyEventAction::onEvent(tbox::flow::Event e) { 具体使用方法,可以参考`IfElseAction`的实现。 ### CompositeAction(组合动作基类) +它用于对一个动作树进行封装。 ## 2. 执行动作(子叶) ### FunctionAction(函数动作) 执行指定函数,并将返回值作为结果的动作。 该动作常用于: -- 作为判定条件; +- 作为判定的条件; - 作为执行的动作; 它通常不独立使用,通常作为子动作添加到组合动作中。 @@ -140,6 +185,18 @@ func_3_action->setFunc([] (tbox::flow::Reason &r) { r.code = 1000; return false; ### SleepAction(延时动作) 顾明思义,延时指定时长。 +使用示例: +``` +auto &loop = *ctx().loop(); + +//! 固定延迟时长 +auto const_sleep_action = new tbox::flow::SleepAction(loop, std::chrono::seconds(5)); + +//! 动态延迟时长,可以运时决定 +auto get_time_func = [] { return std::chrono::seconds(3); }; +auto dynamic_sleep_action = new tbox::flow::SleepAction(loop, get_time_func); +``` + ### SuccAction(固定成功动作) 直接返回成功的动作。类拟于: ``` @@ -154,14 +211,19 @@ return false; ### ExecuteCmdAction(执行System指令动作) 委托子线程执行system命令的动作。 -该动作常用于执行类似于curl,wget,zip,unzip或者执行指定脚本等过程。 +该动作常用于执行类似于 curl, wget, zip, unzip 或者执行指定脚本等过程的场景。 使用示例: ``` auto &loop = *ctx().loop(); auto &thread_executor = *ctx().thread_pool(); +//! 在构造函数中指定命令 auto unzip_cmd_action = new tbox::flow::ExecuteCmdAction(loop, thread_executor, "unzip /tmp/fw-20150923.zip"); + +//! 不在构造函数中指定命令,稍后通过setCmd()进行指定 +auto curl_cmd_action = new tbox::flow::ExecuteCmdAction(loop, thread_executor); +curl_cmd_action->setCmd("curl -o /home/user/downloads/file.zip https://example.com/file.zip"); ``` ### ExecuteInThreadAction(在子线程中执行函数的动作) @@ -219,6 +281,7 @@ seq_action->addChild(step_3_action); seq_action->start(); ``` +执行的结果:先输出“Hello”,5秒之后后输出“cpp-tbox”; ### ParalleAction(并行组合动作) 与`SequenceAction`相对,它是并行动作。它同时启动其下的所有子动作。 @@ -272,6 +335,7 @@ if_else_action->setChildAs(else_action, "else"); if_else_action->start(); ``` +执行的结果:输出“Then”。因为`cond_action`返回的是true。 ### IfThenAction(IfThen条件分支组合动作) 它是`IfElseAction`的扩展,可实现连续的条件判断。 @@ -305,10 +369,11 @@ if_then_action->addChildAs(then_1_action, "then"); if_then_action->addChildAs(cond_2_action, "if"); if_then_action->addChildAs(then_2_action, "then"); if_then_action->addChildAs(new tbox::flow::SuccAction(loop), "if"); -if_then_action->addChildAs(else_action, "else"); +if_then_action->addChildAs(else_action, "true"); if_then_action->start(); ``` +执行的结果:输出“Else”。因为`cond_1_action`与`cond_2_action`返回的都是false。所以`then_1_action`与`then_2_action`都不会执行。 ### LoopAction(循环组合动作) 它反复执行其子动作。 @@ -330,7 +395,7 @@ while (true) { 使用示例: ``` -int count = 10; +int count = 3; auto &loop = *ctx().loop(); @@ -368,7 +433,8 @@ loop_if_action->start(); ``` ### RepeatAction(重复组合动作) -实现重复指定次数的动作。 +实现重复指定次数的动作,是对`LoopAction`的扩展。 + 它有三种模式: - NoBreak,不提前结束; @@ -385,17 +451,19 @@ for (int i = 0; i < times; ++i) { } return true; ``` + 使用示例: ``` auto &loop = *ctx().loop(); -auto repeat_action = new tbox::flow::LoopAction(loop, 10, tbox::flow::LoopAction::Mode::kBreakSucc); +auto repeat_action = new tbox::flow::LoopAction(loop, 3, tbox::flow::LoopAction::Mode::kBreakSucc); auto exec_action = new tbox::flow::FunctionAction(loop, [&]( LogDbg("Repeat"); return false; )); repeat_action->setChild(exec_action); repeat_action->start(); ``` +执行结果:打印3次"Repeat"。 ### WrapperAction(结果包装组合动作) 如果不希望某个动作的结果影响到流程执行,可使用`WrapperAction`对它进行包装。 @@ -406,6 +474,16 @@ repeat_action->start(); - AlwaySucc,固定为成功,不管子动作返回什么; - AlwayFail,固定为失败,不管子动作返回什么; +使用示例: +``` +auto &loop = *ctx().loop(); + +auto your_action = new YourAction(loop); + +//! 对your_action的结果取反 +auto invert_wrapper_action = new tbox::flow::WrapperAction(loop, your_action, tbox::flow::WrapperAction::Mode::kInvert); +``` + ### SwitchAction(Switch分支组合动作) 用于实现switch-case逻辑。 类似于: @@ -442,3 +520,5 @@ switch_action->setChildAs(default_action, "default"); switch_action->start(); ``` +执行结果:打印"B",因为`cond_action`的`FunctionAction`函数中将r.message设置成了"case:B",与`b_action`匹配。 + -- Gitee From 02eb745890e15a35aff96fb56f09ead7c11d7900 Mon Sep 17 00:00:00 2001 From: Hevake Lee Date: Tue, 23 Sep 2025 22:43:34 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=AC=94=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 19-action-tree-buildin-actions.md | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/19-action-tree-buildin-actions.md b/19-action-tree-buildin-actions.md index 9fdfdae..e0daf92 100644 --- a/19-action-tree-buildin-actions.md +++ b/19-action-tree-buildin-actions.md @@ -126,6 +126,7 @@ bool MyEventAction::onEvent(tbox::flow::Event e) { ### AssembleAction(组装动作基类) 它是组装动作的基类。 + 它定义了组装子动作的几个接口: - `setChild()`,设置子动作。常用于只能设置一个子动作的情况,比如:`LoopAction`,`Composite`,`WrapperAction`; @@ -325,9 +326,9 @@ if (cond_action()) { auto &loop = *ctx().loop(); auto if_else_action = new tbox::flow::IfElseAction(loop); -auto cond_action = new tbox::flow::FunctionAction(loop, []( return true; )); -auto then_action = new tbox::flow::FunctionAction(loop, []( LogDbg("Then"); return true; )); -auto else_action = new tbox::flow::FunctionAction(loop, []( LogDbg("Else"); return true; )); +auto cond_action = new tbox::flow::FunctionAction(loop, []{ return true; }); +auto then_action = new tbox::flow::FunctionAction(loop, []{ LogDbg("Then"); return true; }); +auto else_action = new tbox::flow::FunctionAction(loop, []{ LogDbg("Else"); return true; }); if_else_action->setChildAs(cond_action, "if"); if_else_action->setChildAs(then_action, "then"); @@ -358,11 +359,11 @@ if (cond_1_action()) { auto &loop = *ctx().loop(); auto if_then_action = new tbox::flow::IfThenAction(loop); -auto cond_1_action = new tbox::flow::FunctionAction(loop, []( return false; )); -auto then_1_action = new tbox::flow::FunctionAction(loop, []( LogDbg("Then_1"); return true; )); -auto cond_2_action = new tbox::flow::FunctionAction(loop, []( return false; )); -auto then_2_action = new tbox::flow::FunctionAction(loop, []( LogDbg("Then_2"); return true; )); -auto else_action = new tbox::flow::FunctionAction(loop, []( LogDbg("Else"); return true; )); +auto cond_1_action = new tbox::flow::FunctionAction(loop, []{ return false; }); +auto then_1_action = new tbox::flow::FunctionAction(loop, []{ LogDbg("Then_1"); return true; }); +auto cond_2_action = new tbox::flow::FunctionAction(loop, []{ return false; }); +auto then_2_action = new tbox::flow::FunctionAction(loop, []{ LogDbg("Then_2"); return true; }); +auto else_action = new tbox::flow::FunctionAction(loop, []{ LogDbg("Else"); return true; }); if_then_action->addChildAs(cond_1_action, "if"); if_then_action->addChildAs(then_1_action, "then"); @@ -400,7 +401,7 @@ int count = 3; auto &loop = *ctx().loop(); auto loop_action = new tbox::flow::LoopAction(loop, tbox::flow::LoopAction::Mode::kUntilFail); -auto exec_action = new tbox::flow::FunctionAction(loop, [&]( LogDbg("count:%d", --count); return count > 0; )); +auto exec_action = new tbox::flow::FunctionAction(loop, [&]{ LogDbg("count:%d", --count); return count > 0; }); loop_action->setChild(exec_action); @@ -423,8 +424,8 @@ int count = 10; auto &loop = *ctx().loop(); auto loop_if_action = new tbox::flow::LoopAction(loop, tbox::flow::LoopAction::Mode::kUntilFail); -auto cond_action = new tbox::flow::FunctionAction(loop, [&]( return --count > 0; )); -auto exec_action = new tbox::flow::FunctionAction(loop, [&]( LogDbg("count:%d", count); return true; )); +auto cond_action = new tbox::flow::FunctionAction(loop, [&]{ return --count > 0; }); +auto exec_action = new tbox::flow::FunctionAction(loop, [&]{ LogDbg("count:%d", count); return true; }); loop_if_action->setChildAs(cond_action, "if"); loop_if_action->setChildAs(exec_action, "exec"); @@ -457,7 +458,7 @@ return true; auto &loop = *ctx().loop(); auto repeat_action = new tbox::flow::LoopAction(loop, 3, tbox::flow::LoopAction::Mode::kBreakSucc); -auto exec_action = new tbox::flow::FunctionAction(loop, [&]( LogDbg("Repeat"); return false; )); +auto exec_action = new tbox::flow::FunctionAction(loop, [&]{ LogDbg("Repeat"); return false; }); repeat_action->setChild(exec_action); -- Gitee From f8d347485469de0a4cedd44c0ceabad4a83f1927 Mon Sep 17 00:00:00 2001 From: Hevake Lee Date: Tue, 23 Sep 2025 22:44:37 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=AC=94=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 19-action-tree-buildin-actions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/19-action-tree-buildin-actions.md b/19-action-tree-buildin-actions.md index e0daf92..6ab592e 100644 --- a/19-action-tree-buildin-actions.md +++ b/19-action-tree-buildin-actions.md @@ -272,9 +272,9 @@ return is_succ; auto &loop = *ctx().loop(); auto seq_action = new tbox::flow::SequenceAction(loop); -auto step_1_action = new tbox::flow::FunctionAction(loop, []( LogDbg("Hello"); return true; )); +auto step_1_action = new tbox::flow::FunctionAction(loop, []{ LogDbg("Hello"); return true; }); auto step_2_action = new tbox::flow::SleepAction(loop, std::chrono::seconds(2)); -auto step_3_action = new tbox::flow::FunctionAction(loop, []( LogDbg("cpp-tbox"); return true; )); +auto step_3_action = new tbox::flow::FunctionAction(loop, []{ LogDbg("cpp-tbox"); return true; }); seq_action->addChild(step_1_action); seq_action->addChild(step_2_action); -- Gitee