47 using State =
typename Traits::State;
50 write(
"digraph fsm {");
51 write(
" rankdir=LR;");
52 write(
" compound=true;");
53 write(
" node [shape=box, style=rounded];");
54 write(
" any [shape=point, xlabel=\"any\"];");
55 write(
" refused [shape=octagon, style=solid];");
60 constexpr auto& specs = Traits::states;
65 void emit_state(State s,
int indent) {
66 if (detail::is_composite(Traits::states, s)) {
68 lb.str(
"subgraph cluster_");
69 M::put_state_name(lb, s);
74 M::put_state_name(lb, s);
77 for (
size_t i = 0; i < M::state_count; ++i) {
78 const auto& sp = Traits::states[i];
79 if (sp.parent.k == parent_kind::child && sp.parent.s == s)
80 emit_state(sp.state.s, indent + 1);
87 M::put_state_name(lb, s);
92 void line(
int indent) {
94 for (
int i = 0; i < indent; ++i) lb.str(
" ");
98 for (
size_t i = 0; i < M::state_count; ++i) {
99 const auto& sp = specs[i];
107 auto endpoint = [&lb](State s) {
108 M::put_state_name(lb, detail::leaf_of(Traits::states, s));
110 auto cluster_attr = [&lb](
const char* attr, State s) {
114 M::put_state_name(lb, s);
118 for (
size_t i = 0; i < M::row_count; ++i) {
119 const auto& r = Traits::rows[i];
120 const bool from_comp =
122 bool to_comp =
false;
126 else endpoint(r.from.s);
130 to_comp = detail::is_composite(specs, r.to.s);
136 else endpoint(r.from.s);
143 lb.str(
" [label=\"");
144 M::put_event_name(lb, r.on.e);
145 if (r.when !=
nullptr) lb.str(
" [guard]");
146 if (r.then !=
nullptr) lb.str(
" /action");
149 M::put_reason_name(lb, r.to.r);
152 if (r.label !=
nullptr) {
158 if (from_comp) cluster_attr(
"ltail", r.from.s);
167 for (
size_t i = 0; i < M::state_count; ++i) {
168 const auto& sp = specs[i];
172 const bool comp = detail::is_composite(specs, sp.state.s);
175 endpoint(sp.state.s);
177 endpoint(sp.state.s);
178 lb.str(
" [style=dashed, label=\"after ");
182 lb.u64(sp.deadline.ms);
185 M::put_event_name(lb, sp.deadline.event);
188 cluster_attr(
"ltail", sp.state.s);
189 cluster_attr(
"lhead", sp.state.s);