Skip to content

Commit

Permalink
Fanin fanout (#20)
Browse files Browse the repository at this point in the history
* [WIP] Fanout

* 中間ノードでFanout funcの呼び出しは不可

* Fanin

* modify checklist

* update
  • Loading branch information
ddddddO authored Mar 19, 2023
1 parent a09158c commit 9470d31
Show file tree
Hide file tree
Showing 9 changed files with 231 additions and 4 deletions.
47 changes: 47 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,53 @@ func main() {
- [x] finish

## Miscellaneous

### FanIn/FanOut
> **Warning**<br>
> Continued calls to Fanin/Fanout func will result in panic.<br>
> e.g. ❌t.Fanout(n1).Fanout(n2) / ❌Fanin(t.Fanout(n1))
1. Fanin/Fanout func usage
```go
package main

import (
"fmt"
"os"

g "github.com/ddddddO/gdag"
)

func main() {
dag := g.DAG("Fanin/Fanout")
task1 := g.Task("task1")
task2 := g.Task("task2")
task3 := g.Task("task3")
task4 := g.Task("task4")

// block1
dag.Fanout(task1, task2, task3).Con(task4)
task5 := task4.Con(g.Task("task5"))
task6 := task4.Con(g.Task("task6"))
task7 := task4.Con(g.Task("task7"))
g.Fanin(task5, task6, task7).Con(g.Task("end"))

// block2
// dag.Fanout(task1, task2, task3).Con(task4).Fanout(g.Task("task5"), g.Task("task6"), g.Task("task7")).Con(g.Task("end"))

uml, err := dag.UML()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fmt.Println(uml)
}
```
- `block1` and `block2` are same result.

2. Result
![](./_example/fanin_fanout/uml.svg)

### short name methods

```go
Expand Down
33 changes: 33 additions & 0 deletions _example/fanin_fanout/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package main

import (
"fmt"
"os"

g "github.com/ddddddO/gdag"
)

func main() {
dag := g.DAG("Fanin/Fanout")
task1 := g.Task("task1")
task2 := g.Task("task2")
task3 := g.Task("task3")
task4 := g.Task("task4")

// block1
dag.Fanout(task1, task2, task3).Con(task4)
task5 := task4.Con(g.Task("task5"))
task6 := task4.Con(g.Task("task6"))
task7 := task4.Con(g.Task("task7"))
g.Fanin(task5, task6, task7).Con(g.Task("end"))

// block2
// dag.Fanout(task1, task2, task3).Con(task4).Fanout(g.Task("task5"), g.Task("task6"), g.Task("task7")).Con(g.Task("end"))

uml, err := dag.UML()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fmt.Println(uml)
}
25 changes: 25 additions & 0 deletions _example/fanin_fanout/uml.pu
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
@startuml
rectangle "Fanin/Fanout" as 1
usecase "task1" as 2
usecase "task4" as 5
usecase "task5" as 7
usecase "end" as 11
usecase "task6" as 8
usecase "task7" as 9
usecase "task2" as 3
usecase "task3" as 4

1 --> 2
2 --> 5
5 --> 7
7 --> 11
5 --> 8
8 --> 11
5 --> 9
9 --> 11
1 --> 3
3 --> 5
1 --> 4
4 --> 5

@enduml
48 changes: 48 additions & 0 deletions _example/fanin_fanout/uml.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 13 additions & 2 deletions _example/system_failure_response/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,12 @@ func main() {
end := g.Task("終了")

flow.Con(checkEvent).Con(checkMatter)
checkMatter.Con(checkBusinessImpact).Con(recoveryResponse)
checkMatter.Con(checkCause).Con(recoveryResponse)

checkMatter.Fanout(checkBusinessImpact, checkCause).Con(recoveryResponse)
// Fanout method is same below.
// checkMatter.Con(checkBusinessImpact).Con(recoveryResponse)
// checkMatter.Con(checkCause).Con(recoveryResponse)

recoveryResponse.Con(permanentResponse)
permanentResponse.Con(faultAnalysis)
permanentResponse.Con(recurrencePreventionMeasures)
Expand All @@ -34,4 +38,11 @@ func main() {
os.Exit(1)
}
fmt.Println(uml)

// mermaid, err := flow.Mermaid()
// if err != nil {
// fmt.Fprintln(os.Stderr, err)
// os.Exit(1)
// }
// fmt.Println(mermaid)
}
3 changes: 3 additions & 0 deletions checklist.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ func (*checkListGenerator) renderRow(node *Node) string {
if node.isDone() {
return fmt.Sprintf("- [x] %s\n", node.text)
} else {
if node.nodeType == intermediate {
return ""
}
return fmt.Sprintf("- [ ] %s\n", node.text)
}
}
11 changes: 11 additions & 0 deletions mermaid.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ func (*mermaidGenerator) renderComponent(node *Node) string {
ret := ""
// TODO: mermaid用に修正するかどうか。リファクタは必要
switch node.nodeType {
case intermediate:
break
case rectangle:
s := fmt.Sprintf("%d(\"%s\")", node.index, node.text)
if len(node.colorMermaid) != 0 {
Expand Down Expand Up @@ -76,8 +78,17 @@ func (mg *mermaidGenerator) generateRelations(start *Node) string {

func (mg *mermaidGenerator) generateRelation(node *Node, out string) string {
edge := fmt.Sprintf("%d --> ", node.index)
if node.nodeType == intermediate {
edge = fmt.Sprintf("%d --> ", node.parent.index)
}

ret := out
for _, d := range node.downstream {
if d.nodeType == intermediate {
ret += mg.generateRelation(d, "")
continue
}

key := fmt.Sprintf("%d-%d", node.index, d.index)
if _, ok := mg.uniqueRelations[key]; ok {
continue
Expand Down
42 changes: 40 additions & 2 deletions node.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@ type Node struct {
color string // done: #DarkGray
colorMermaid string // done: doneColor

parent *Node // TODO: 現状、中間ノードのためにおいてる
downstream []*Node
}

type nodeType string

const (
rectangle = nodeType("rectangle")
usecase = nodeType("usecase")
intermediate = nodeType("intermediate node")
rectangle = nodeType("rectangle")
usecase = nodeType("usecase")
)

func DAG(text string) *Node {
Expand Down Expand Up @@ -51,6 +53,13 @@ func Done(nodes ...*Node) {
}

func (upstream *Node) Con(current *Node) *Node {
if upstream.nodeType == intermediate {
for i := range upstream.downstream {
upstream.downstream[i].downstream = append(upstream.downstream[i].downstream, current)
}
return current
}

for _, d := range upstream.downstream {
if current.index == d.index {
return d
Expand All @@ -61,6 +70,35 @@ func (upstream *Node) Con(current *Node) *Node {
return current
}

func Fanin(nodes ...*Node) *Node {
for i := range nodes {
nodes[i].invalid()
}

intermediateNode := newNode(intermediate, "not output")
intermediateNode.downstream = nodes
return intermediateNode
}

func (upstream *Node) Fanout(nodes ...*Node) *Node {
upstream.invalid()

intermediateNode := newNode(intermediate, "not output")
intermediateNode.downstream = nodes

intermediateNode.parent = upstream
upstream.downstream = append(upstream.downstream, intermediateNode)
return intermediateNode
}

// TODO: refactor
func (n *Node) invalid() {
if n.nodeType == intermediate {
// TODO: 後ほど、Fanout(n1, n2).Fanout(n3, n4)みたいに、中間ノードでもチェイン出来るようにしたい
panic("Fanin/Fanout calls are not supported for intermediate node.")
}
}

func (current *Node) Note(note string) *Node {
current.note = note
return current
Expand Down
11 changes: 11 additions & 0 deletions plantuml.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ func (ug *umlGenerator) generateComponent(node *Node) string {
func (*umlGenerator) renderComponent(node *Node) string {
ret := ""
switch node.nodeType {
case intermediate:
break
case rectangle, usecase:
s := fmt.Sprintf("%s \"%s\" as %d", node.nodeType, node.text, node.index)
if len(node.color) != 0 {
Expand All @@ -67,8 +69,17 @@ func (ug *umlGenerator) generateRelations(start *Node) string {

func (ug *umlGenerator) generateRelation(node *Node, out string) string {
edge := fmt.Sprintf("%d --> ", node.index)
if node.nodeType == intermediate {
edge = fmt.Sprintf("%d --> ", node.parent.index)
}

ret := out
for _, d := range node.downstream {
if d.nodeType == intermediate {
ret += ug.generateRelation(d, "")
continue
}

key := fmt.Sprintf("%d-%d", node.index, d.index)
if _, ok := ug.uniqueRelations[key]; ok {
continue
Expand Down

0 comments on commit 9470d31

Please sign in to comment.