forked from roscopecoltran/goflow
/
network.go
941 lines (857 loc) · 25.2 KB
/
network.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
package flow
import (
"reflect"
"sync"
)
// DefaultBufferSize is the default channel buffer capacity.
var DefaultBufferSize = 0
// DefaultNetworkCapacity is the default capacity of network's processes/ports maps.
var DefaultNetworkCapacity = 32
// Default network output or input ports number
var DefaultNetworkPortsNum = 16
// port stores full port information within the network.
type port struct {
// Process name in the network
proc string
// Port name of the process
port string
// Actual channel attached
channel reflect.Value
// Runtime info
info PortInfo
}
// portName stores full port name within the network.
type portName struct {
// Process name in the network
proc string
// Port name of the process
port string
}
// connection stores information about a connection within the net.
type connection struct {
src portName
tgt portName
channel reflect.Value
buffer int
}
// iip stands for Initial Information Packet representation
// within the network.
type iip struct {
data interface{}
proc string // Target process name
port string // Target port name
}
// portMapper interface is used to obtain subnet's ports.
type portMapper interface {
getInPort(string) reflect.Value
getOutPort(string) reflect.Value
hasInPort(string) bool
hasOutPort(string) bool
listInPorts() map[string]port
listOutPorts() map[string]port
SetInPort(string, interface{}) bool
SetOutPort(string, interface{}) bool
}
// netController interface is used to run a subnet.
type netController interface {
getWait() *sync.WaitGroup
run()
}
// Graph represents a graph of processes connected with packet channels.
type Graph struct {
// Wait is used for graceful network termination.
waitGrp *sync.WaitGroup
// Net is a pointer to parent network.
Net *Graph
// procs contains the processes of the network.
procs map[string]interface{}
// inPorts maps network incoming ports to component ports.
inPorts map[string]port
// outPorts maps network outgoing ports to component ports.
outPorts map[string]port
// connections contains graph edges and channels.
connections []connection
// sendChanRefCount tracks how many sendports use the same channel
sendChanRefCount map[uintptr]uint
// sendChanMutex is used to synchronize operations on the sendChanRefCount map.
sendChanMutex sync.Locker
// iips contains initial IPs attached to the network
iips []iip
// done is used to let the outside world know when the net has finished its job
done chan struct{}
// ready is used to let the outside world know when the net is ready to accept input
ready chan struct{}
// isRunning indicates that the network is currently running
isRunning bool
}
// InitGraphState method initializes graph fields and allocates memory.
func (n *Graph) InitGraphState() {
n.waitGrp = new(sync.WaitGroup)
n.procs = make(map[string]interface{}, DefaultNetworkCapacity)
n.inPorts = make(map[string]port, DefaultNetworkPortsNum)
n.outPorts = make(map[string]port, DefaultNetworkPortsNum)
n.connections = make([]connection, 0, DefaultNetworkCapacity)
n.sendChanRefCount = make(map[uintptr]uint, DefaultNetworkCapacity)
n.sendChanMutex = new(sync.Mutex)
n.iips = make([]iip, 0, DefaultNetworkPortsNum)
n.done = make(chan struct{})
n.ready = make(chan struct{})
}
// Canvas is a generic graph that is manipulated at run-time only
type Canvas struct {
Graph
}
// NewGraph creates a new canvas graph that can be modified at run-time.
// Implements ComponentConstructor interace, so can it be used with Factory.
func NewGraph() interface{} {
net := new(Canvas)
net.InitGraphState()
return net
}
// Register an empty graph component in the registry
func init() {
Register("Graph", NewGraph)
Annotate("Graph", ComponentInfo{
Description: "A clear graph",
Icon: "cogs",
})
}
// Increments SendChanRefCount
func (n *Graph) IncSendChanRefCount(c reflect.Value) {
n.sendChanMutex.Lock()
defer n.sendChanMutex.Unlock()
ptr := c.Pointer()
cnt := n.sendChanRefCount[ptr]
cnt += 1
n.sendChanRefCount[ptr] = cnt
}
// Decrements SendChanRefCount
// It returns true if the RefCount has reached 0
func (n *Graph) DecSendChanRefCount(c reflect.Value) bool {
n.sendChanMutex.Lock()
defer n.sendChanMutex.Unlock()
ptr := c.Pointer()
cnt := n.sendChanRefCount[ptr]
if cnt == 0 {
return true //yes you may try to close a nonexistant channel, see what happens...
}
cnt -= 1
n.sendChanRefCount[ptr] = cnt
return cnt == 0
}
// Add adds a new process with a given name to the network.
// It returns true on success or panics and returns false on error.
func (n *Graph) Add(c interface{}, name string) bool {
// Check if passed interface is a valid pointer to struct
v := reflect.ValueOf(c)
if v.Kind() != reflect.Ptr || v.IsNil() {
panic("flow.Graph.Add() argument is not a valid pointer")
return false
}
v = v.Elem()
if v.Kind() != reflect.Struct {
panic("flow.Graph.Add() argument is not a valid pointer to struct")
return false
}
// Set the link to self in the proccess so that it could use it
var vNet reflect.Value
vCom := v.FieldByName("Component")
if vCom.IsValid() && vCom.Type().Name() == "Component" {
vNet = vCom.FieldByName("Net")
} else {
vGraph := v.FieldByName("Graph")
if vGraph.IsValid() && vGraph.Type().Name() == "Graph" {
vNet = vGraph.FieldByName("Net")
}
}
if vNet.IsValid() && vNet.CanSet() {
vNet.Set(reflect.ValueOf(n))
}
// Add to the map of processes
n.procs[name] = c
return true
}
// AddGraph adds a new blank graph instance to a network. That instance can
// be modified then at run-time.
func (n *Graph) AddGraph(name string) bool {
net := new(Graph)
net.InitGraphState()
return n.Add(net, name)
}
// AddNew creates a new process instance using component factory and adds it to the network.
func (n *Graph) AddNew(componentName string, processName string) bool {
proc := Factory(componentName)
return n.Add(proc, processName)
}
// Remove deletes a process from the graph. First it stops the process if running.
// Then it disconnects it from other processes and removes the connections from
// the graph. Then it drops the process itself.
func (n *Graph) Remove(processName string) bool {
if _, exists := n.procs[processName]; !exists {
return false
}
// TODO disconnect before removal
delete(n.procs, processName)
return true
}
// Rename changes a process name in all connections, external ports, IIPs and the
// graph itself.
func (n *Graph) Rename(processName, newName string) bool {
if _, exists := n.procs[processName]; !exists {
return false
}
if _, busy := n.procs[newName]; busy {
// New name is already taken
return false
}
for i, conn := range n.connections {
if conn.src.proc == processName {
n.connections[i].src.proc = newName
}
if conn.tgt.proc == processName {
n.connections[i].tgt.proc = newName
}
}
for key, port := range n.inPorts {
if port.proc == processName {
tmp := n.inPorts[key]
tmp.proc = newName
n.inPorts[key] = tmp
}
}
for key, port := range n.outPorts {
if port.proc == processName {
tmp := n.outPorts[key]
tmp.proc = newName
n.outPorts[key] = tmp
}
}
n.procs[newName] = n.procs[processName]
delete(n.procs, processName)
return true
}
// AddIIP adds an Initial Information packet to the network
func (n *Graph) AddIIP(data interface{}, processName, portName string) bool {
if _, exists := n.procs[processName]; exists {
n.iips = append(n.iips, iip{data: data, proc: processName, port: portName})
return true
}
return false
}
// RemoveIIP detaches an IIP from specific process and port
func (n *Graph) RemoveIIP(processName, portName string) bool {
for i, p := range n.iips {
if p.proc == processName && p.port == portName {
// Remove item from the slice
n.iips[len(n.iips)-1], n.iips[i], n.iips = iip{}, n.iips[len(n.iips)-1], n.iips[:len(n.iips)-1]
return true
}
}
return false
}
// Connect connects a sender to a receiver and creates a channel between them using DefaultBufferSize.
// Normally such a connection is unbuffered but you can change by setting flow.DefaultBufferSize > 0 or
// by using ConnectBuf() function instead.
// It returns true on success or panics and returns false if error occurs.
func (n *Graph) Connect(senderName, senderPort, receiverName, receiverPort string) bool {
return n.ConnectBuf(senderName, senderPort, receiverName, receiverPort, DefaultBufferSize)
}
// Connect connects a sender to a receiver using a channel with a buffer of a given size.
// It returns true on success or panics and returns false if error occurs.
func (n *Graph) ConnectBuf(senderName, senderPort, receiverName, receiverPort string, bufferSize int) bool {
// Ensure sender and receiver processes exist
sender, senderFound := n.procs[senderName]
receiver, receiverFound := n.procs[receiverName]
if !senderFound {
panic("Sender '" + senderName + "' not found")
return false
}
if !receiverFound {
panic("Receiver '" + receiverName + "' not found")
return false
}
// Ensure sender and receiver are settable
sp := reflect.ValueOf(sender)
sv := sp.Elem()
// st := sv.Type()
rp := reflect.ValueOf(receiver)
rv := rp.Elem()
// rt := rv.Type()
if !sv.CanSet() {
panic(senderName + " is not settable")
return false
}
if !rv.CanSet() {
panic(receiverName + " is not settable")
return false
}
var sport reflect.Value
// Get the actual ports and link them to the channel
// Check if sender is a net
var snet reflect.Value
if sv.Type().Name() == "Graph" {
snet = sv
} else {
snet = sv.FieldByName("Graph")
}
if snet.IsValid() {
// Sender is a net
if pm, isPm := snet.Addr().Interface().(portMapper); isPm {
sport = pm.getOutPort(senderPort)
}
} else {
// Sender is a proc
sport = sv.FieldByName(senderPort)
}
// Get the reciever port
var rport reflect.Value
// Check if receiver is a net
var rnet reflect.Value
if rv.Type().Name() == "Graph" {
rnet = rv
} else {
rnet = rv.FieldByName("Graph")
}
if rnet.IsValid() {
if pm, isPm := rnet.Addr().Interface().(portMapper); isPm {
rport = pm.getInPort(receiverPort)
}
} else {
// Receiver is a proc
rport = rv.FieldByName(receiverPort)
}
// Validate receiver port
rtport := rport.Type()
if rtport.Kind() != reflect.Chan || rtport.ChanDir()&reflect.RecvDir == 0 {
panic(receiverName + "." + receiverPort + " is not a valid input channel")
return false
}
// Validate sender port
stport := sport.Type()
var channel reflect.Value
if !rport.IsNil() {
for _, mycon := range n.connections {
if mycon.tgt.port == receiverPort && mycon.tgt.proc == receiverName {
channel = mycon.channel
break
}
}
}
if stport.Kind() == reflect.Slice {
if sport.Type().Elem().Kind() == reflect.Chan && sport.Type().Elem().ChanDir()&reflect.SendDir != 0 {
if !channel.IsValid() {
// Need to create a new channel and add it to the array
chanType := reflect.ChanOf(reflect.BothDir, sport.Type().Elem().Elem())
channel = reflect.MakeChan(chanType, bufferSize)
}
sport.Set(reflect.Append(sport, channel))
n.IncSendChanRefCount(channel)
}
} else if stport.Kind() == reflect.Chan && stport.ChanDir()&reflect.SendDir != 0 {
// Check if channel was already instantiated, if so, use it. Thus we can connect serveral endpoints and golang will pseudo-randomly chooses a receiver
// Also, this avoids crashes on <-net.Wait()
if !sport.IsNil() {
//go does not allow cast of unidir chan to bidir chan (for good reason)
//but luckily we saved it, so we look it up
if channel.IsValid() && sport.Addr() != rport.Addr() {
panic("Trying to connect an already connected source to an already connected target")
}
for _, mycon := range n.connections {
if mycon.src.port == senderPort && mycon.src.proc == senderName {
channel = mycon.channel
break
}
}
}
// either sport was nil or we did not find a previous channel instance
if !channel.IsValid() {
// Make a channel of an appropriate type
chanType := reflect.ChanOf(reflect.BothDir, stport.Elem())
channel = reflect.MakeChan(chanType, bufferSize)
}
}
if channel.IsNil() {
panic(senderName + "." + senderPort + " is not a valid output channel")
return false
}
// Check if ?port.Set() would cause panic and if so ... panic
if !sport.CanSet() {
panic(senderName + "." + senderPort + " is not settable")
}
if !rport.CanSet() {
panic(receiverName + "." + receiverPort + " is not settable")
}
// Set the channels
if sport.IsNil() {
//note that if sport is a slice, this does not run, instead see code above (== reflect.Slice)
sport.Set(channel)
n.IncSendChanRefCount(channel)
}
if rport.IsNil() {
rport.Set(channel)
}
// Add connection info
n.connections = append(n.connections, connection{
src: portName{proc: senderName,
port: senderPort},
tgt: portName{proc: receiverName,
port: receiverPort},
channel: channel,
buffer: bufferSize})
return true
}
// Unsets an port of a given process
func unsetProcPort(proc interface{}, portName string, isOut bool) bool {
v := reflect.ValueOf(proc)
var ch reflect.Value
if v.Elem().FieldByName("Graph").IsValid() {
if subnet, ok := v.Elem().FieldByName("Graph").Addr().Interface().(*Graph); ok {
if isOut {
ch = subnet.getOutPort(portName)
} else {
ch = subnet.getInPort(portName)
}
} else {
return false
}
} else {
ch = v.Elem().FieldByName(portName)
}
if !ch.IsValid() {
return false
}
ch.Set(reflect.Zero(ch.Type()))
return true
}
// Disconnect removes a connection between sender's outport and receiver's inport.
func (n *Graph) Disconnect(senderName, senderPort, receiverName, receiverPort string) bool {
var sender, receiver interface{}
var ok bool
sender, ok = n.procs[senderName]
if !ok {
return false
}
receiver, ok = n.procs[receiverName]
if !ok {
return false
}
res := unsetProcPort(sender, senderPort, true)
res = res && unsetProcPort(receiver, receiverPort, false)
return res
}
// Get returns a node contained in the network by its name.
func (n *Graph) Get(processName string) interface{} {
if proc, ok := n.procs[processName]; ok {
return proc
} else {
panic("Process with name '" + processName + "' was not found")
}
}
// getInPort returns the inport with given name as reflect.Value channel.
func (n *Graph) getInPort(name string) reflect.Value {
pName, ok := n.inPorts[name]
if !ok {
panic("flow.Graph.getInPort(): Invalid inport name: " + name)
}
return pName.channel
}
// listInPorts returns information about graph inports and their types.
func (n *Graph) listInPorts() map[string]port {
return n.inPorts
}
// getOutPort returns the outport with given name as reflect.Value channel.
func (n *Graph) getOutPort(name string) reflect.Value {
pName, ok := n.outPorts[name]
if !ok {
panic("flow.Graph.getOutPort(): Invalid outport name: " + name)
}
return pName.channel
}
// listOutPorts returns information about graph outports and their types.
func (n *Graph) listOutPorts() map[string]port {
return n.outPorts
}
// getWait returns net's wait group.
func (n *Graph) getWait() *sync.WaitGroup {
return n.waitGrp
}
// hasInPort checks if the net has an inport with given name.
func (n *Graph) hasInPort(name string) bool {
_, has := n.inPorts[name]
return has
}
// hasOutPort checks if the net has an outport with given name.
func (n *Graph) hasOutPort(name string) bool {
_, has := n.outPorts[name]
return has
}
// MapInPort adds an inport to the net and maps it to a contained proc's port.
// It returns true on success or panics and returns false on error.
func (n *Graph) MapInPort(name, procName, procPort string) bool {
ret := false
// Check if target component and port exists
var channel reflect.Value
if p, procFound := n.procs[procName]; procFound {
if i, isNet := p.(portMapper); isNet {
// Is a subnet
ret = i.hasInPort(procPort)
channel = i.getInPort(procPort)
} else {
// Is a proc
f := reflect.ValueOf(p).Elem().FieldByName(procPort)
ret = f.IsValid() && f.Kind() == reflect.Chan && (f.Type().ChanDir()&reflect.RecvDir) != 0
channel = f
}
if !ret {
panic("flow.Graph.MapInPort(): No such inport: " + procName + "." + procPort)
}
} else {
panic("flow.Graph.MapInPort(): No such process: " + procName)
}
if ret {
n.inPorts[name] = port{proc: procName, port: procPort, channel: channel}
}
return ret
}
// AnnotateInPort sets optional run-time annotation for the port utilized by
// runtimes and FBP protocol clients.
func (n *Graph) AnnotateInPort(name string, info PortInfo) bool {
port, exists := n.inPorts[name]
if !exists {
return false
}
port.info = info
return true
}
// UnmapInPort removes an existing inport mapping
func (n *Graph) UnmapInPort(name string) bool {
if _, exists := n.inPorts[name]; !exists {
return false
}
delete(n.inPorts, name)
return true
}
// MapOutPort adds an outport to the net and maps it to a contained proc's port.
// It returns true on success or panics and returns false on error.
func (n *Graph) MapOutPort(name, procName, procPort string) bool {
ret := false
// Check if target component and port exists
var channel reflect.Value
if p, procFound := n.procs[procName]; procFound {
if i, isNet := p.(portMapper); isNet {
// Is a subnet
ret = i.hasOutPort(procPort)
channel = i.getOutPort(procPort)
} else {
// Is a proc
f := reflect.ValueOf(p).Elem().FieldByName(procPort)
ret = f.IsValid() && f.Kind() == reflect.Chan && (f.Type().ChanDir()&reflect.SendDir) != 0
channel = f
}
if !ret {
panic("flow.Graph.MapOutPort(): No such outport: " + procName + "." + procPort)
}
} else {
panic("flow.Graph.MapOutPort(): No such process: " + procName)
}
if ret {
n.outPorts[name] = port{proc: procName, port: procPort, channel: channel}
}
return ret
}
// AnnotateOutPort sets optional run-time annotation for the port utilized by
// runtimes and FBP protocol clients.
func (n *Graph) AnnotateOutPort(name string, info PortInfo) bool {
port, exists := n.outPorts[name]
if !exists {
return false
}
port.info = info
return true
}
// UnmapOutPort removes an existing outport mapping
func (n *Graph) UnmapOutPort(name string) bool {
if _, exists := n.outPorts[name]; !exists {
return false
}
delete(n.outPorts, name)
return true
}
// run runs the network and waits for all processes to finish.
func (n *Graph) run() {
// Add processes to the waitgroup before starting them
n.waitGrp.Add(len(n.procs))
for _, v := range n.procs {
// Check if it is a net or proc
r := reflect.ValueOf(v).Elem()
if r.FieldByName("Graph").IsValid() {
RunNet(v)
} else {
RunProc(v)
}
}
n.isRunning = true
// Send initial IPs
for _, ip := range n.iips {
// Get the reciever port
var rport reflect.Value
found := false
// Try to find it among network inports
for _, inPort := range n.inPorts {
if inPort.proc == ip.proc && inPort.port == ip.port {
rport = inPort.channel
found = true
break
}
}
if !found {
// Try to find among connections
for _, conn := range n.connections {
if conn.tgt.proc == ip.proc && conn.tgt.port == ip.port {
rport = conn.channel
found = true
break
}
}
}
if !found {
// Try to find a proc and attach a new channel to it
for procName, proc := range n.procs {
if procName == ip.proc {
// Check if receiver is a net
rv := reflect.ValueOf(proc).Elem()
var rnet reflect.Value
if rv.Type().Name() == "Graph" {
rnet = rv
} else {
rnet = rv.FieldByName("Graph")
}
if rnet.IsValid() {
if pm, isPm := rnet.Addr().Interface().(portMapper); isPm {
rport = pm.getInPort(ip.port)
}
} else {
// Receiver is a proc
rport = rv.FieldByName(ip.port)
}
// Validate receiver port
rtport := rport.Type()
if rtport.Kind() != reflect.Chan || rtport.ChanDir()&reflect.RecvDir == 0 {
panic(ip.proc + "." + ip.port + " is not a valid input channel")
}
var channel reflect.Value
// Make a channel of an appropriate type
chanType := reflect.ChanOf(reflect.BothDir, rtport.Elem())
channel = reflect.MakeChan(chanType, DefaultBufferSize)
// Set the channel
if rport.CanSet() {
rport.Set(channel)
} else {
panic(ip.proc + "." + ip.port + " is not settable")
}
// Use the new channel to send the IIP
rport = channel
found = true
break
}
}
}
if found {
// Send data to the port
rport.Send(reflect.ValueOf(ip.data))
} else {
panic("IIP target not found: " + ip.proc + "." + ip.port)
}
}
// Let the outside world know that the network is ready
close(n.ready)
// Wait for all processes to terminate
n.waitGrp.Wait()
n.isRunning = false
// Check if there is a parent net
if n.Net != nil {
// Notify parent of finish
n.Net.waitGrp.Done()
}
}
// RunProc starts a proc added to a net at run time
func (n *Graph) RunProc(procName string) bool {
if !n.isRunning {
return false
}
proc, ok := n.procs[procName]
if !ok {
return false
}
v := reflect.ValueOf(proc).Elem()
n.waitGrp.Add(1)
if v.FieldByName("Graph").IsValid() {
RunNet(proc)
return true
} else {
ok = RunProc(proc)
if !ok {
n.waitGrp.Done()
}
return ok
}
}
// Stop terminates the network without closing any connections
func (n *Graph) Stop() {
if !n.isRunning {
return
}
for _, v := range n.procs {
// Check if it is a net or proc
r := reflect.ValueOf(v).Elem()
if r.FieldByName("Graph").IsValid() {
subnet, ok := r.FieldByName("Graph").Addr().Interface().(*Graph)
if !ok {
panic("Couldn't get graph interface")
}
subnet.Stop()
} else {
StopProc(v)
}
}
}
// StopProc stops a specific process in the net
func (n *Graph) StopProc(procName string) bool {
if !n.isRunning {
return false
}
proc, ok := n.procs[procName]
if !ok {
return false
}
v := reflect.ValueOf(proc).Elem()
if v.FieldByName("Graph").IsValid() {
subnet, ok := v.FieldByName("Graph").Addr().Interface().(*Graph)
if !ok {
panic("Couldn't get graph interface")
}
subnet.Stop()
} else {
return StopProc(proc)
}
return true
}
// Ready returns a channel that can be used to suspend the caller
// goroutine until the network is ready to accept input packets
func (n *Graph) Ready() <-chan struct{} {
return n.ready
}
// Wait returns a channel that can be used to suspend the caller
// goroutine until the network finishes its job
func (n *Graph) Wait() <-chan struct{} {
return n.done
}
// SetInPort assigns a channel to a network's inport to talk to the outer world.
// It returns true on success or false if the inport cannot be set.
func (n *Graph) SetInPort(name string, channel interface{}) bool {
res := false
// Get the component's inport associated
p := n.getInPort(name)
// Try to set it
if p.CanSet() {
p.Set(reflect.ValueOf(channel))
res = true
}
// Save it in inPorts to be used with IIPs if needed
if p, ok := n.inPorts[name]; ok {
p.channel = reflect.ValueOf(channel)
n.inPorts[name] = p
}
return res
}
// RenameInPort changes graph's inport name
func (n *Graph) RenameInPort(oldName, newName string) bool {
if _, exists := n.inPorts[oldName]; !exists {
return false
}
n.inPorts[newName] = n.inPorts[oldName]
delete(n.inPorts, oldName)
return true
}
// UnsetInPort removes an external inport from the graph
func (n *Graph) UnsetInPort(name string) bool {
port, exists := n.inPorts[name]
if !exists {
return false
}
if proc, ok := n.procs[port.proc]; ok {
unsetProcPort(proc, port.port, false)
}
delete(n.inPorts, name)
return true
}
// SetOutPort assigns a channel to a network's outport to talk to the outer world.
// It returns true on success or false if the outport cannot be set.
func (n *Graph) SetOutPort(name string, channel interface{}) bool {
res := false
// Get the component's outport associated
p := n.getOutPort(name)
// Try to set it
if p.CanSet() {
p.Set(reflect.ValueOf(channel))
res = true
}
// Save it in outPorts to be used later
if p, ok := n.outPorts[name]; ok {
p.channel = reflect.ValueOf(channel)
n.outPorts[name] = p
}
return res
}
// RenameOutPort changes graph's outport name
func (n *Graph) RenameOutPort(oldName, newName string) bool {
if _, exists := n.outPorts[oldName]; !exists {
return false
}
n.outPorts[newName] = n.outPorts[oldName]
delete(n.outPorts, oldName)
return true
}
// UnsetOutPort removes an external outport from the graph
func (n *Graph) UnsetOutPort(name string) bool {
port, exists := n.outPorts[name]
if !exists {
return false
}
if proc, ok := n.procs[port.proc]; ok {
unsetProcPort(proc, port.proc, true)
}
delete(n.outPorts, name)
return true
}
// RunNet runs the network by starting all of its processes.
// It runs Init/Finish handlers if the network implements Initializable/Finalizable interfaces.
func RunNet(i interface{}) {
// Get the contained network
net, isGraph := i.(*Graph)
if !isGraph {
v := reflect.ValueOf(i).Elem()
if v.Kind() != reflect.Struct {
panic("flow.RunNet(): argument is not a pointer to struct")
}
vGraph := v.FieldByName("Graph")
if !vGraph.IsValid() || vGraph.Type().Name() != "Graph" {
panic("flow.RunNet(): argument is not a valid graph instance")
}
net = vGraph.Addr().Interface().(*Graph)
}
// Call user init function if exists
if initable, ok := i.(Initializable); ok {
initable.Init()
}
// Run the contained processes
go func() {
net.run()
// Call user finish function if exists
if finable, ok := i.(Finalizable); ok {
finable.Finish()
}
// Close the wait channel
close(net.done)
}()
}