-
Notifications
You must be signed in to change notification settings - Fork 1
/
nonparallel.go
193 lines (178 loc) · 5.54 KB
/
nonparallel.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
package composite
import (
"fmt"
"github.com/alkaid/behavior/bcore"
"go.uber.org/zap"
)
// NonParallel 非并行组合基类,节点按从左到右的顺序执行其子节点
type NonParallel struct {
bcore.Composite
INonParallelWorker
needOrder bool
}
// INonParallelWorker 继承 NonParallel 时必须实现的接口
type INonParallelWorker interface {
IChildrenOrder
// SuccessMode 成功模式
// @return behavior.FinishMode
SuccessMode() bcore.FinishMode
}
type IChildrenOrder interface {
// OnOrder 为孩子节点索引排序, NonParallel.OnStart 里回调
// @param originChildrenOrder
// @return orders 排序后的索引
// @return needOrder 是否需要排序
OnOrder(brain bcore.IBrain, originChildrenOrder []int) (orders []int, needOrder bool)
}
// CurrIdx 当前处理进度index
//
// @receiver n
// @param brain
// @return int
func (n *NonParallel) CurrIdx(brain bcore.IBrain) int {
return brain.Blackboard().(bcore.IBlackboardInternal).NodeMemory(n.ID()).CurrIndex
}
func (n *NonParallel) SetCurrIdx(brain bcore.IBrain, currChild int) {
brain.Blackboard().(bcore.IBlackboardInternal).NodeMemory(n.ID()).CurrIndex = currChild
}
// CurrChildIdx 当前处理的子节点index
//
// @receiver n
// @param brain
// @return int
func (n *NonParallel) CurrChildIdx(brain bcore.IBrain) int {
nodeData := brain.Blackboard().(bcore.IBlackboardInternal).NodeMemory(n.ID())
// 无需重新排序的节点,使用单例里的索引
if !n.needOrder {
return nodeData.CurrIndex
}
// 使用保存到黑板的排序
return nodeData.ChildrenOrder[nodeData.CurrIndex]
}
// CurrChild 当前处理的子节点
//
// @receiver n
// @param brain
// @return behavior.INode
func (n *NonParallel) CurrChild(brain bcore.IBrain) bcore.INode {
return n.Children()[n.CurrChildIdx(brain)]
}
// OnOrder
//
// @implement INonParallelWorker.OnOrder
// @receiver n
// @param originChildrenOrder
// @return orders
// @return needOrder
func (n *NonParallel) OnOrder(brain bcore.IBrain, originChildrenOrder []int) (orders []int, needOrder bool) {
return originChildrenOrder, false
}
// InitNodeWorker
//
// @override Node.InitNodeWorker
// @receiver c
// @param worker
func (n *NonParallel) InitNodeWorker(worker bcore.INodeWorker) error {
err := n.Composite.InitNodeWorker(worker)
// 强转,由框架本身保证实例化时传进来的worker是自己(自己实现了 INonParallelWorker 接口,故强转不会panic)
n.INonParallelWorker = worker.(INonParallelWorker)
return err
}
// OnStart
//
// @override Node.OnStart
// @receiver n
// @param brain
func (n *NonParallel) OnStart(brain bcore.IBrain) {
n.Composite.OnStart(brain)
for _, child := range n.Children() {
if !child.IsInactive(brain) {
n.Log(brain).Error("child must be inactive", zap.String("child", child.String(brain)))
return
}
}
n.SetCurrIdx(brain, -1)
childrenOrder := make([]int, len(n.Children()))
for i := 0; i < len(n.Children()); i++ {
childrenOrder[i] = i
}
// 若子类返回需要重新排序,需要记录排序索引到黑板
childrenOrder, n.needOrder = n.INonParallelWorker.OnOrder(brain, childrenOrder)
if n.needOrder {
brain.Blackboard().(bcore.IBlackboardInternal).NodeMemory(n.ID()).ChildrenOrder = childrenOrder
}
n.processChildren(brain)
}
// OnAbort
//
// @override Node.OnAbort
// @receiver n
// @param brain
func (n *NonParallel) OnAbort(brain bcore.IBrain) {
n.Composite.OnAbort(brain)
// 向下传播给当前活跃子节点
n.CurrChild(brain).SetUpstream(brain, n)
n.CurrChild(brain).Abort(brain)
}
// OnChildFinished
//
// Sequence 碰到一个失败就Finish(false)
// Selector 碰到一个成功就Finish(true)
// 若是被父节点中断的,马上Finish(false)
//
// @override Container.OnChildFinished
// @receiver r
// @param brain
// @param child
// @param succeeded
func (n *NonParallel) OnChildFinished(brain bcore.IBrain, child bcore.INode, succeeded bool) {
n.Composite.OnChildFinished(brain, child, succeeded)
// 只要一个成功 || 只要一个失败 就结束
if (n.SuccessMode() == bcore.FinishModeOne && succeeded) || (n.SuccessMode() == bcore.FinishModeAll && !succeeded) {
n.Finish(brain, succeeded)
return
}
// 成功则处理下一个
n.processChildren(brain)
}
func (n *NonParallel) processChildren(brain bcore.IBrain) {
currChild := n.CurrIdx(brain) + 1
n.SetCurrIdx(brain, currChild)
// 说明子节点全部成功/失败,则自己也返回成功/失败
if currChild >= len(n.Children()) {
// 模式为只要一个成功就成功的情况下,走到这里所有子节点都执行了一遍都还没返回,说明失败了.反之亦然
successWhenAllProcessed := n.SuccessMode() == bcore.FinishModeAll
n.Finish(brain, successWhenAllProcessed)
return
}
// 如果是被父节点中断的,直接返回失败
if n.IsAborting(brain) {
n.Finish(brain, false)
return
}
n.CurrChild(brain).Start(brain)
}
// AbortLowerPriorityChildrenForChild
//
// @implement IComposite.AbortLowerPriorityChildrenForChild
// @receiver c
// @param childAbortBy
func (n *NonParallel) AbortLowerPriorityChildrenForChild(brain bcore.IBrain, childAbortBy bcore.INode) {
idx := -1
for i, currChild := range n.Children() {
// 找到发起中断请求的子节点的索引
if childAbortBy.ID() == currChild.ID() {
idx = i
}
// 找到正在运行的第一个右侧节点,中断掉
if idx > -1 && currChild.IsActive(brain) {
n.SetCurrIdx(brain, idx-1)
currChild.SetUpstream(brain, n)
currChild.Abort(brain)
break
}
}
}
func (n *NonParallel) OnString(brain bcore.IBrain) string {
return fmt.Sprintf("%s[%d]", n.Composite.OnString(brain), n.CurrChildIdx(brain))
}