package svg
import (
"bytes"
"encoding/json"
"fmt"
"html/template"
"strings"
"go-common/app/admin/main/aegis/model/net"
"go-common/library/log"
)
var tpl = `
流程网描述图
`
var (
_labelFlow = `%s [label="%s",shape=circle]`
_labelTransition = `%s [label="%s",shape=box,color=lightgrey,style=filled;]`
_labelToken = `%d [label="%s",shape=Mdiamond]`
_labelDirection = `%s->%s`
)
//NetView .
type NetView struct {
*template.Template
Dot *Dot
Data struct {
NetDesc string
DotCode string
Actions []string
ActionDesc map[string]string
}
}
//NewNetView .
func NewNetView() *NetView {
return &NetView{}
}
//SetDot .
func (nv *NetView) SetDot(dot *Dot) {
nv.Dot = dot
nv.Data.DotCode = dot.String()
nv.Data.Actions = append(dot.nodes[:], dot.edges...)
nv.Data.ActionDesc = dot.mapactions
ntpl := strings.Replace(tpl, "DotCode", nv.Data.DotCode, 1)
nv.Template = template.New("流程网概览")
nv.Template, _ = nv.Template.Parse(ntpl)
}
/*
节点用record表示,节点绑定的令牌用在同一个record里面
变迁用diamond表示,若变迁绑定了令牌,则用图包含变迁和令牌
*/
//Dot .
type Dot struct {
*bytes.Buffer
nodes []string
edges []string
mapactions map[string]string
flows map[int64]*net.Flow
trans map[int64]*net.Transition
dirs map[int64]*net.Direction
tokens map[string][]*net.TokenBindDetail
}
//NewDot .
func NewDot() *Dot {
d := &Dot{
mapactions: make(map[string]string),
flows: make(map[int64]*net.Flow),
trans: make(map[int64]*net.Transition),
dirs: make(map[int64]*net.Direction),
tokens: make(map[string][]*net.TokenBindDetail),
}
return d
}
//StartDot .
func (d *Dot) StartDot() *Dot {
d.Buffer = bytes.NewBufferString(`digraph net{rankdir=LR;`)
return d
}
//End .
func (d *Dot) End() string {
d.WriteString("}")
return d.String()
}
//AddTokenBinds .
func (d *Dot) AddTokenBinds(tbs ...*net.TokenBindDetail) *Dot {
for _, tb := range tbs {
key := fmt.Sprintf("%d_%d", tb.Type, tb.ElementID)
if tks, ok := d.tokens[key]; ok {
d.tokens[key] = append(tks, tb)
} else {
d.tokens[key] = []*net.TokenBindDetail{tb}
}
}
return d
}
//AddFlow .
func (d *Dot) AddFlow(flows ...*net.Flow) *Dot {
for _, flow := range flows {
d.flows[flow.ID] = flow
node := fmt.Sprintf(_labelFlow, flow.Name, flow.ChName)
//
nodeid := fmt.Sprintf("node%d", len(d.nodes)+1)
d.nodes = append(d.nodes, nodeid)
bs, _ := json.Marshal(flow)
d.mapactions[nodeid] = string(bs)
//便利token,查找绑定
if tks, ok := d.tokens[fmt.Sprintf("1_%d", flow.ID)]; ok {
d.WriteString("subgraph cluster_" + flow.Name + " {")
d.WriteString(node + ";")
for _, tk := range tks {
node := fmt.Sprintf(_labelToken, tk.ID, tk.ChName)
d.WriteString(node + ";")
nodeid := fmt.Sprintf("node%d", len(d.nodes)+1)
d.nodes = append(d.nodes, nodeid)
bs, _ := json.Marshal(tk)
d.mapactions[nodeid] = string(bs)
}
d.WriteString("}")
} else {
d.WriteString(node + ";")
}
}
return d
}
//AddTransitions .
func (d *Dot) AddTransitions(trans ...*net.Transition) *Dot {
for _, tran := range trans {
d.trans[tran.ID] = tran
node := fmt.Sprintf(_labelTransition, tran.Name, tran.ChName)
nodeid := fmt.Sprintf("node%d", len(d.nodes)+1)
d.nodes = append(d.nodes, nodeid)
bs, _ := json.Marshal(tran)
d.mapactions[nodeid] = string(bs)
//便利token,查找绑定
if tks, ok := d.tokens[fmt.Sprintf("2_%d", tran.ID)]; ok {
d.WriteString("subgraph cluster_" + tran.Name + " {")
d.WriteString(node + ";")
for _, tk := range tks {
node := fmt.Sprintf(_labelToken, tk.ID, tk.ChName)
d.WriteString(node + ";")
nodeid := fmt.Sprintf("node%d", len(d.nodes)+1)
d.nodes = append(d.nodes, nodeid)
bs, _ := json.Marshal(tk)
d.mapactions[nodeid] = string(bs)
}
d.WriteString("}")
} else {
d.WriteString(node + ";")
}
}
return d
}
//AddDirections .
func (d *Dot) AddDirections(dirs ...*net.Direction) *Dot {
for _, dir := range dirs {
var (
start, end string
flow *net.Flow
trans *net.Transition
)
flow = d.flows[dir.FlowID]
trans = d.trans[dir.TransitionID]
if flow == nil || trans == nil {
log.Error("invalid direction(%+v)", dir)
continue
}
if dir.Direction == 1 {
start, end = flow.Name, trans.Name
}
if dir.Direction == 2 {
start, end = trans.Name, flow.Name
}
edge := fmt.Sprintf(_labelDirection, start, end)
d.WriteString(edge + ";")
edgeid := fmt.Sprintf("edge%d", len(d.edges)+1)
bs, _ := json.Marshal(dir)
d.mapactions[edgeid] = string(bs)
d.edges = append(d.edges, edgeid)
}
return d
}
var (
flow1 = &net.Flow{ID: 1, ChName: "节点1", Name: "flow1"}
flow2 = &net.Flow{ID: 2, ChName: "节点2", Name: "flow2"}
flow3 = &net.Flow{ID: 3, ChName: "节点3", Name: "flow3"}
flow4 = &net.Flow{ID: 4, ChName: "节点4", Name: "flow4"}
flow5 = &net.Flow{ID: 5, ChName: "节点5", Name: "flow5"}
flow6 = &net.Flow{ID: 6, ChName: "节点6", Name: "flow6"}
flow7 = &net.Flow{ID: 7, ChName: "节点7", Name: "flow7"}
tran1 = &net.Transition{ID: 1, ChName: "变迁1", Name: "tran1"}
tran2 = &net.Transition{ID: 2, ChName: "变迁2", Name: "tran2"}
tran3 = &net.Transition{ID: 3, ChName: "变迁3", Name: "tran3"}
dir1 = &net.Direction{ID: 1, FlowID: 1, TransitionID: 1, Direction: 1}
dir2 = &net.Direction{ID: 2, FlowID: 2, TransitionID: 1, Direction: 2}
dir3 = &net.Direction{ID: 3, FlowID: 2, TransitionID: 2, Direction: 1}
dir4 = &net.Direction{ID: 4, FlowID: 2, TransitionID: 3, Direction: 1}
dir5 = &net.Direction{ID: 5, FlowID: 3, TransitionID: 2, Direction: 2}
dir6 = &net.Direction{ID: 6, FlowID: 4, TransitionID: 2, Direction: 2}
dir7 = &net.Direction{ID: 7, FlowID: 5, TransitionID: 3, Direction: 2}
dir8 = &net.Direction{ID: 8, FlowID: 6, TransitionID: 2, Direction: 2}
dir9 = &net.Direction{ID: 9, FlowID: 7, TransitionID: 3, Direction: 2}
tk1 = &net.TokenBindDetail{
ID: 1,
Type: 1,
ElementID: 1,
ChName: "待审核",
}
tk2 = &net.TokenBindDetail{
ID: 2,
Type: 2,
ElementID: 1,
ChName: "通过",
}
)
func DebugSVG() (nv *NetView) {
dot := NewDot()
dot.StartDot().AddTokenBinds(tk1, tk2).
AddFlow(flow1, flow2, flow3, flow4, flow5, flow6, flow7).
AddTransitions(tran1, tran2, tran3).
AddDirections(dir1, dir2, dir3, dir4, dir5, dir6, dir7, dir8, dir9).
End()
nv = NewNetView()
nv.SetDot(dot)
return nv
}