Current section
Files
Jump to
Current section
Files
src/bt_engine/bt_util.erl
-module(bt_util).
-export([to_file/1, to_jsonable_term/1]).
-include("bt_nodes.hrl").
%% Usage:
%% ---
%% bt_util:to_jsonable_term(bt_yaml_parser:parse("src/samples/trees/rackloop.yml", "single_rackloop", [{"BId", 3}])).
%% io:format("~s ~n", [jsx:encode(bt_util:to_jsonable_term(bt_yaml_parser:parse("src/samples/trees/rackloop.yml", "single_rackloop", [{"BId", 3}])))]).
to_jsonable_term({RootNode, BB}) ->
node_to_tree(<<"null">>, RootNode, BB).
to_file(ParseResult) ->
Data = jsx:encode(to_jsonable_term(ParseResult)),
{ok, F} = file:open("treedata.js", [write]),
file:write(F, "var treeData = ["),
file:write(F, Data),
file:write(F, "];"),
file:close(F).
node_to_tree(ParentName, #sequence{name = SequenceName, children = Children, metadata = Metadata}, BB) ->
Name = list_to_binary(lists:flatten(SequenceName)),
Map = #{name => Name, parent => ParentName, status => get_status(Metadata), type => sequence},
case length(Children) of
0 -> Map;
_ -> maps:put(children, lists:map(fun(X) -> node_to_tree(Name, X, BB) end, Children), Map)
end;
node_to_tree(ParentName, #decorator{name = DecoratorName, child = Child, metadata = Metadata, args = Args, module = Module, state = State}, BB) ->
Name = list_to_binary(lists:flatten(DecoratorName)),
Attributes = get_attributes(Args, BB),
Map = #{name => Name, parent => ParentName, status => get_status(Metadata), attributes => add_other_attributes(Attributes, Module, get_exception(Metadata), State), type => decorator},
maps:put(children, [node_to_tree(Name, Child, BB)], Map);
node_to_tree(ParentName, #task{name = TaskName, metadata = Metadata, args = Args, module = Module, state = State}, BB) ->
Name = list_to_binary(lists:flatten(TaskName)),
Attributes = get_attributes(Args, BB),
#{name => Name, parent => ParentName, status => get_status(Metadata), attributes => add_other_attributes(Attributes, Module, get_exception(Metadata), State), type => task}.
get_status(undefined) ->
unknown;
get_status(#bt_metadata{status = Status}) ->
Status.
get_exception(undefined) ->
undefined;
get_exception(#bt_metadata{exception = Exception}) ->
Exception.
get_attributes(Args, BB) when is_map(Args) ->
ArgsProplist = maps:to_list(Args),
EvalFn = fun(Value, BBTemp) ->
%% if theres a crash (eg. in evaluating a promise) then just return ??? only
%% this is because this function is only used in making a jsonable term and shouldn't really crash.
V = try bt_blackboard:eval_with_default_fn(Value, BBTemp, fun (_BBKey) -> "???" end) of
Res -> Res
catch
_:_ -> "???"
end,
%% need to serialize it so its jsonable
to_list(V)
end,
WithValues = [
%% jsx doesn't like strings ?? both keys and values?
%% https://github.com/talentdeficit/jsx/issues/81
{list_to_atom(Key), list_to_atom(EvalFn(Value, BB))} ||
{Key, Value} <- ArgsProplist
],
maps:from_list(WithValues).
add_module_and_exception_attributes(AttributesMap0, Module, Exception) ->
AttributesMap1 = maps:put(node, Module, AttributesMap0),
case Exception of
undefined -> AttributesMap1;
_ -> maps:put(exception, list_to_binary(to_list(Exception)), AttributesMap1)
end.
add_state_attributes(AttributesMap, State, _Module) when State =:= undefined orelse State =:= #{} orelse State =:= {} ->
AttributesMap;
add_state_attributes(AttributesMap, State, Module) ->
case lager:pr(State, Module) of
{'$lager_record', _, TupleList} ->
WithListedValues = [{Key, to_list(Value)} || {Key, Value} <- TupleList],
StateMap = maps:from_list(WithListedValues),
maps:merge(AttributesMap, #{"state" => StateMap});
_ ->
%% just convert state to list and add
maps:merge(AttributesMap, #{"state" => to_list(State)})
end.
add_other_attributes(AttributesMap0, Module, Exception, State) ->
AttributesMap1 = add_module_and_exception_attributes(AttributesMap0, Module, Exception),
add_state_attributes(AttributesMap1, State, Module).
to_list(Tuple) ->
lists:flatten(io_lib:format("~p", [Tuple])).