-module(run_sequence). -include("bt_nodes.hrl"). -export([ run/3, preempt/3 ]). %% should run each child from left to right; they will return their new states so it will need to update them when returning %% blackboard will also be changed between each child call %% if child returns: %% failure: preempt and then return {failure, NewSequenceNode, NewBB2} %% running: return {running, NewSequenceNode, NewBB}, preempt all children to the right of running! %% success: run next child with NewBB; make sure to update this child's state with the returned state %% all return success: just clear BB and then return {success, NewSequenceNode, NewBB2} -spec run(SequenceNode :: sequence(), BB :: blackboard(), Event :: bt_event()) -> {Status :: bt_status(), NewSequenceNode :: sequence(), NewBB :: blackboard()}. run(SequenceNode, BB, Event) when is_record(SequenceNode, sequence) -> Children = SequenceNode#sequence.children, {Status, NewChildren, NewBB} = run_children(Children, BB, Event), case Status of failure -> %% clear blackboard and return failure; preemption of children already done. NewBB2 = bt_blackboard:clear_namespace(SequenceNode#sequence.namespace, BB), {failure, SequenceNode#sequence{children = NewChildren}, NewBB2}; running -> %% just return; preemption of children already done. {running, SequenceNode#sequence{children = NewChildren}, NewBB}; success -> %% clear blackboard and return success; NewBB2 = bt_blackboard:clear_namespace(SequenceNode#sequence.namespace, NewBB), {success, SequenceNode#sequence{children = NewChildren}, NewBB2} end. run_children([], BB, _Event) -> %% all ran successfully! return success {success, [], BB}; run_children([Child | RestOfChildren], BB, Event) -> %% run the child {ChildStatus, NewChildNode, NewBB} = bt_runner:run(Child, BB, Event), case ChildStatus of %% failure: preempt all right children failure -> {PreemptedRestOfChildren, NewBB2} = preempt_children(RestOfChildren, NewBB, true), {failure, [NewChildNode | PreemptedRestOfChildren], NewBB2}; %% running: preempt all right children running -> {PreemptedRestOfChildren, NewBB2} = preempt_children(RestOfChildren, NewBB, true), {running, [NewChildNode | PreemptedRestOfChildren], NewBB2}; success -> %% using the new bb, run the rest of the children {Status, NewRestOfChildren, NewBB2} = run_children(RestOfChildren, NewBB, Event), {Status, [NewChildNode | NewRestOfChildren], NewBB2} end. %% is being preempted. clear namespace and call preempt on each child. -spec preempt(SequenceNode :: sequence(), BB :: blackboard(), ShouldCleanBB :: boolean()) -> {NewNode :: term(), NewBB :: blackboard()}. preempt(SequenceNode, BB, true) -> %% clear the namespace NewBB = bt_blackboard:clear_namespace(SequenceNode#sequence.namespace, BB), %% now dont preempt(SequenceNode, NewBB, false); %% don't need to clear the namespace. preempt(SequenceNode, BB, false) -> Children = SequenceNode#sequence.children, {NewChildren, NewBB} = preempt_children(Children, BB, false), {SequenceNode#sequence{children = NewChildren}, NewBB}. preempt_children([], BB, _Flag) -> {[], BB}; preempt_children([Child | RestOfChildren], BB, ShouldCleanBB) -> {NewChild, NewBB} = bt_runner:preempt(Child, BB, ShouldCleanBB), {NewRestOfChildren, NewBB2} = preempt_children(RestOfChildren, NewBB, ShouldCleanBB), {[NewChild | NewRestOfChildren], NewBB2}.