Skip to content

Commit 2516bd7

Browse files
committed
feat(flow): 1.实现了SwitchAction,并完成了单元测试;2.给DummyAction添加了事件回调;
1 parent 4f6b98d commit 2516bd7

File tree

9 files changed

+771
-11
lines changed

9 files changed

+771
-11
lines changed

modules/flow/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ set(TBOX_FLOW_HEADERS
5252
actions/wrapper_action.h
5353
actions/succ_fail_action.h
5454
actions/dummy_action.h
55+
actions/switch_action.h
5556
to_graphviz.h)
5657

5758
set(TBOX_FLOW_SOURCES
@@ -71,6 +72,8 @@ set(TBOX_FLOW_SOURCES
7172
actions/repeat_action.cpp
7273
actions/composite_action.cpp
7374
actions/wrapper_action.cpp
75+
actions/dummy_action.cpp
76+
actions/switch_action.cpp
7477
to_graphviz.cpp)
7578

7679
set(TBOX_FLOW_TEST_SOURCES
@@ -90,6 +93,7 @@ set(TBOX_FLOW_TEST_SOURCES
9093
actions/composite_action_test.cpp
9194
actions/wrapper_action_test.cpp
9295
actions/succ_fail_action_test.cpp
96+
actions/switch_action_test.cpp
9397
to_graphviz_test.cpp)
9498

9599
add_library(${TBOX_LIBRARY_NAME} ${TBOX_BUILD_LIB_TYPE} ${TBOX_FLOW_SOURCES})

modules/flow/Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ HEAD_FILES = \
4747
actions/wrapper_action.h \
4848
actions/succ_fail_action.h \
4949
actions/dummy_action.h \
50+
actions/switch_action.h \
5051
to_graphviz.h \
5152

5253
CPP_SRC_FILES = \
@@ -66,6 +67,8 @@ CPP_SRC_FILES = \
6667
actions/repeat_action.cpp \
6768
actions/composite_action.cpp \
6869
actions/wrapper_action.cpp \
70+
actions/dummy_action.cpp \
71+
actions/switch_action.cpp \
6972
to_graphviz.cpp \
7073

7174
CXXFLAGS := -DMODULE_ID='"tbox.flow"' $(CXXFLAGS)
@@ -88,6 +91,7 @@ TEST_CPP_SRC_FILES = \
8891
actions/composite_action_test.cpp \
8992
actions/wrapper_action_test.cpp \
9093
actions/succ_fail_action_test.cpp \
94+
actions/switch_action_test.cpp \
9195
to_graphviz_test.cpp \
9296

9397
TEST_LDFLAGS := $(LDFLAGS) -ltbox_flow -ltbox_event -ltbox_util -ltbox_base -ldl

modules/flow/action_reason.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
#define ACTION_REASON_FAIL_ACTION 5 //!< "FailAction"
3030
#define ACTION_REASON_START_CHILD_FAIL 6 //!< "StartChildFail"
3131
#define ACTION_REASON_REPEAT_NO_TIMES 7 //!< "RepeatNoTimes"
32+
#define ACTION_REASON_SWITCH_FAIL 8 //!< "SwitchFail"
33+
#define ACTION_REASON_SWITCH_SKIP 9 //!< "SwitchSkip"
3234

3335
//! 保存 1000 以内供 Action 自用,使用者自定义的 Reason 需 >= 1000
3436

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* .============.
3+
* // M A K E / \
4+
* // C++ DEV / \
5+
* // E A S Y / \/ \
6+
* ++ ----------. \/\ .
7+
* \\ \ \ /\ /
8+
* \\ \ \ /
9+
* \\ \ \ /
10+
* -============'
11+
*
12+
* Copyright (c) 2025 Hevake and contributors, all rights reserved.
13+
*
14+
* This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox)
15+
* Use of this source code is governed by MIT license that can be found
16+
* in the LICENSE file in the root of the source tree. All contributing
17+
* project authors may be found in the CONTRIBUTORS.md file in the root
18+
* of the source tree.
19+
*/
20+
#include "dummy_action.h"
21+
22+
namespace tbox {
23+
namespace flow {
24+
25+
void DummyAction::onStart() {
26+
Action::onStart();
27+
if (start_cb_)
28+
start_cb_();
29+
}
30+
31+
void DummyAction::onStop() {
32+
if (stop_cb_)
33+
stop_cb_();
34+
Action::onStop();
35+
}
36+
37+
void DummyAction::onPause() {
38+
if (pause_cb_)
39+
pause_cb_();
40+
Action::onPause();
41+
}
42+
43+
void DummyAction::onResume() {
44+
Action::onResume();
45+
if (resume_cb_)
46+
resume_cb_();
47+
}
48+
49+
void DummyAction::onReset() {
50+
if (reset_cb_)
51+
reset_cb_();
52+
Action::onReset();
53+
}
54+
55+
}
56+
}

modules/flow/actions/dummy_action.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,30 @@ class DummyAction : public Action {
3333

3434
virtual bool isReady() const { return true; }
3535

36+
using Callback = std::function<void()>;
37+
38+
inline void setStartCallback(Callback &&cb) { start_cb_ = std::move(cb); }
39+
inline void setStopCallback(Callback &&cb) { stop_cb_ = std::move(cb); }
40+
inline void setPauseCallback(Callback &&cb) { pause_cb_ = std::move(cb); }
41+
inline void setResumeCallback(Callback &&cb) { resume_cb_ = std::move(cb); }
42+
inline void setResetCallback(Callback &&cb) { reset_cb_ = std::move(cb); }
43+
3644
inline void emitFinish(bool is_succ, const Reason &reason = Reason()) { finish(is_succ, reason); }
3745
inline void emitBlock(const Reason &reason) { block(reason); }
46+
47+
protected:
48+
virtual void onStart() override;
49+
virtual void onStop() override;
50+
virtual void onPause() override;
51+
virtual void onResume() override;
52+
virtual void onReset() override;
53+
54+
private:
55+
Callback start_cb_;
56+
Callback stop_cb_;
57+
Callback pause_cb_;
58+
Callback resume_cb_;
59+
Callback reset_cb_;
3860
};
3961

4062
}
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
/*
2+
* .============.
3+
* // M A K E / \
4+
* // C++ DEV / \
5+
* // E A S Y / \/ \
6+
* ++ ----------. \/\ .
7+
* \\ \ \ /\ /
8+
* \\ \ \ /
9+
* \\ \ \ /
10+
* -============'
11+
*
12+
* Copyright (c) 2025 Hevake and contributors, all rights reserved.
13+
*
14+
* This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox)
15+
* Use of this source code is governed by MIT license that can be found
16+
* in the LICENSE file in the root of the source tree. All contributing
17+
* project authors may be found in the CONTRIBUTORS.md file in the root
18+
* of the source tree.
19+
*/
20+
#include "switch_action.h"
21+
22+
#include <tbox/base/assert.h>
23+
#include <tbox/base/defines.h>
24+
#include <tbox/base/json.hpp>
25+
#include <tbox/util/string.h>
26+
27+
namespace tbox {
28+
namespace flow {
29+
30+
using namespace std::placeholders;
31+
32+
SwitchAction::SwitchAction(event::Loop &loop)
33+
: AssembleAction(loop, "Switch")
34+
{ }
35+
36+
SwitchAction::~SwitchAction() {
37+
CHECK_DELETE_RESET_OBJ(switch_action_);
38+
CHECK_DELETE_RESET_OBJ(default_action_);
39+
for (auto &item : case_actions_)
40+
CHECK_DELETE_OBJ(item.second);
41+
case_actions_.clear();
42+
}
43+
44+
void SwitchAction::toJson(Json &js) const {
45+
AssembleAction::toJson(js);
46+
47+
auto &js_children = js["children"];
48+
switch_action_->toJson(js_children["00.switch"]);
49+
50+
if (default_action_ != nullptr)
51+
default_action_->toJson(js_children["99.default"]);
52+
53+
int index = 1;
54+
for (auto &item : case_actions_) {
55+
std::ostringstream oss;
56+
oss << std::setw(2) << std::setfill('0') << index << '.' << item.first;
57+
item.second->toJson(js_children[oss.str()]);
58+
++index;
59+
}
60+
}
61+
62+
bool SwitchAction::setChildAs(Action *child, const std::string &role) {
63+
if (child == nullptr) {
64+
LogWarn("%d:%s[%s], child is nullptr, role:%s", id(), type().c_str(), label().c_str(), role.c_str());
65+
return false;
66+
}
67+
68+
if (role == "switch") {
69+
child->setFinishCallback(std::bind(&SwitchAction::onSwitchActionFinished, this, _1, _2, _3));
70+
child->setBlockCallback(std::bind(&SwitchAction::block, this, _1, _2));
71+
child->setParent(this);
72+
CHECK_DELETE_RESET_OBJ(switch_action_);
73+
switch_action_ = child;
74+
return true;
75+
76+
} else if (role == "default") {
77+
child->setFinishCallback(std::bind(&SwitchAction::finish, this, _1, _2, _3));
78+
child->setBlockCallback(std::bind(&SwitchAction::block, this, _1, _2));
79+
child->setParent(this);
80+
CHECK_DELETE_RESET_OBJ(default_action_);
81+
default_action_ = child;
82+
return true;
83+
84+
} else if (util::string::IsStartWith(role, "case:")) {
85+
auto result = case_actions_.emplace(role, child);
86+
if (result.second) {
87+
child->setFinishCallback(std::bind(&SwitchAction::finish, this, _1, _2, _3));
88+
child->setBlockCallback(std::bind(&SwitchAction::block, this, _1, _2));
89+
child->setParent(this);
90+
return true;
91+
}
92+
}
93+
94+
LogWarn("%d:%s[%s], unsupport role:%s", id(), type().c_str(), label().c_str(), role.c_str());
95+
return false;
96+
}
97+
98+
bool SwitchAction::isReady() const {
99+
//! switch_action没有安装,自然就没有就绪
100+
if (switch_action_ == nullptr) {
101+
LogWarn("%d:%s[%s] no switch action", id(), type().c_str(), label().c_str());
102+
return false;
103+
}
104+
105+
//! 如果default与case都没有,则表示Action没有安装好
106+
if (!default_action_ && case_actions_.empty()) {
107+
LogWarn("%d:%s[%s] both default and case action is null", id(), type().c_str(), label().c_str());
108+
return false;
109+
}
110+
111+
if (!switch_action_->isReady())
112+
return false;
113+
114+
if (default_action_ != nullptr && !default_action_->isReady())
115+
return false;
116+
117+
//! 检查所有的case_action,看看有没有哪个没有就绪
118+
for (auto &item : case_actions_) {
119+
if (!item.second->isReady())
120+
return false;
121+
}
122+
123+
return true;
124+
}
125+
126+
void SwitchAction::onStart() {
127+
AssembleAction::onStart();
128+
129+
TBOX_ASSERT(switch_action_ != nullptr);
130+
running_action_ = switch_action_;
131+
running_action_->start();
132+
}
133+
134+
void SwitchAction::onStop() {
135+
if (running_action_ != nullptr)
136+
running_action_->stop();
137+
138+
AssembleAction::onStop();
139+
}
140+
141+
void SwitchAction::onPause() {
142+
if (running_action_ != nullptr)
143+
running_action_->pause();
144+
145+
AssembleAction::onPause();
146+
}
147+
148+
void SwitchAction::onResume() {
149+
AssembleAction::onResume();
150+
151+
if (running_action_ != nullptr)
152+
running_action_->resume();
153+
}
154+
155+
void SwitchAction::onReset() {
156+
TBOX_ASSERT(switch_action_ != nullptr);
157+
switch_action_->reset();
158+
159+
if (default_action_ != nullptr)
160+
default_action_->reset();
161+
162+
for (auto &item : case_actions_)
163+
item.second->reset();
164+
165+
running_action_ = nullptr;
166+
167+
AssembleAction::onReset();
168+
}
169+
170+
void SwitchAction::onSwitchActionFinished(bool is_succ, const Reason &why, const Trace &trace) {
171+
if (state() == State::kRunning) {
172+
if (is_succ) {
173+
running_action_ = default_action_;
174+
175+
auto iter = case_actions_.find(why.message);
176+
if (iter != case_actions_.end())
177+
running_action_ = iter->second;
178+
179+
if (running_action_ != nullptr) {
180+
running_action_->start();
181+
} else {
182+
finish(false, Reason(ACTION_REASON_SWITCH_SKIP, "SwitchSkip"));
183+
}
184+
185+
} else {
186+
finish(false, Reason(ACTION_REASON_SWITCH_FAIL, "SwitchFail"));
187+
}
188+
}
189+
}
190+
191+
}
192+
}

0 commit comments

Comments
 (0)