Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
qs_*.*
bak
bin/
*.exe
*~
101 changes: 86 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# GoFBP

This repo holds the beginning of an FBP implementation in Go
This repo contains an FBP implementation in Go. It conforms pretty closely to the scheduling logic in JavaFBP, C#FBP and C++FBP (all on https://github.com/jpaulm ).

There may well be further internal changes, but I am hoping that the "external" APIs (network and component definitions) are now firm.

Current tag: **v1.0.6** .

## General

General web site for "classical" FBP:
Expand All @@ -12,8 +14,10 @@ General web site for "classical" FBP:
In computer programming, flow-based programming (FBP) is a programming paradigm that defines applications as networks of "black box" processes, which exchange data across predefined connections by message passing, where the connections are specified externally to the processes. These black box processes can be reconnected endlessly to form different applications without having to be changed internally. FBP is thus naturally component-oriented.

FBP is a particular form of dataflow programming based on bounded buffers, information packets with defined lifetimes, named ports, and separate definition of connections.

DrawFBP (https://github.com/jpaulm/drawfbp) can now generate working GoFBP network and subnet definitions from a network diagram (as well as JavaFBP, C#FBP, JSON and free-form notations).

GoFBP Network Definition Syntax and Component API:
## GoFBP Network Definition Syntax and Component API:
* https://jpaulm.github.io/fbp/gosyntax.htm

## Features (these are common to all FBP implementations on GitHub/jpaulm):
Expand All @@ -25,19 +29,62 @@ GoFBP Network Definition Syntax and Component API:
- "automatic" in- and out-ports - notation is port name = "*"
- GoFBP (and the other FBP implementations on https://github.com/jpaulm distingish between "invocation" and "activation" of processes: a process is invoked once, but may be activated multiple times (if it does a return before all its input ports have been drained)

## Running your app with GoFBP

In the `go.mod` file in your root, add the statement

```
require github.com/jpaulm/gofbp latest
```
and then run the command `go mod tidy` - this will change the word `latest` to the latest version, and store it back in your `go.mod` file.

If you need parameter values other than `false`, you will have to access a `params.xml` file, as shown below.

Command to run your network, e.g.:

```
go run Merge.go
```

A number of test cases are in the `testing` folder, and can be run as follows:

```
cd testing
go test
```
To run an individual network, e.g. `TestIntSender` in `testing\testintsender_test.go`,
run
```
cd testing
go test -run IntSender
```

### Notes:

- One comment about running with subnets: the subnet or subnets should be in a different folder from the code that invokes it. If they are in the same folder, apparently both the invoking code and the subnet need to be named individually in a `go run` command... this is not required when running under VSCode, or from an `.exe` file.

## Tracing

An XML file has been provided in the root, called `params.xml`. So far there is only one parameter:
An XML file has been provided in the root, called `params.xml`. If you need values other than all `false`, you will need to reference this using the `LoadXMLParams` and `SetParams` methods - see https://jpaulm.github.io/fbp/gosyntax.htm .

<pre>
&lt;?xml version="1.0"?&gt;
&lt;tracing&gt;true|false&lt;/tracing&gt;
&lt;tracelocks&gt;true|false&lt;/tracelocks&gt;
</pre>
Format of the tracing definitions file:

```
<?xml version="1.0"?>
<runparams>
<tracing>false</tracing>
<tracelocks>false</tracelocks>
<tracepkts>false</tracepkts> (traces packet creates, createbrackets and discards)
<generate-gIds>true</generate-gIds>
</runparams>
```
The `generate-gIds` parameter is only used to assist in debugging deadlocks, so can default most of the time - see below.

## Subnets
These are described in Chap. 7 in "Flow-Based Programming": Composite Components - https://jpaulmorrison.com/fbp/compos.shtml , although we haven't implemented dynamic subnets (yet)!

**Note:** If a subnet `.go` file is in the same folder as the network `.go` file invoking it, the `NewProc` call should be written without the folder - e.g. `net.NewProc("Run___Subnet", &Subnet1{})`, but currently DrawFBP cannot detect this situation, so, when using notation `GoFBP`, this call will not be generated correctly.

## Test Cases
The following test cases are now working - thanks to Egon Elbre and Emil Valeev for all their help!

Expand All @@ -56,9 +103,11 @@ The following test cases are now working - thanks to Egon Elbre and Emil Valeev
- test subnet (SubIn and SubOut)

- force deadlock (separate test file) - this is designed to crash, and in fact will give a message if it does *not* crash!

- simple web sockets test


To run them, position to your `GitHub\gofbp` directory, and do any of the following:
To run them, position to your `GitHub\gofbp\testing` directory (note the additional folder, as of tag `v1.0.3` plus), and do any of the following:

- `go test -run Merge -count=1`
- `go test -run Concat -count=1`
Expand All @@ -69,9 +118,11 @@ To run them, position to your `GitHub\gofbp` directory, and do any of the follow
- `go test -run WriteToConsUsingNL -count=1` (note the activated/deactivated messages)
- `go test -run ForceDeadlock -count=1`
- `go test -run InfQueueAsMain -count=1` (note the "automatic" ports between WriteFile and ReadFile)
- `go test -run LoadBal -count=1` (this does load balancing, and uses two DelayedReceiver processes)
- `go test -run Subnet1 -count=1`
- `go test -run Subnet2 -count=1`
- `go test -run Subnet3 -count=1`
- `go test -run TestWebSocket -count=1` - for more instructions, see # Test WebSockets (below)


**Note**: ForceDeadlock is constructed differently so that it can "crash" without disrupting the flow of tests: the network definition has to be compiled "on the fly", so it is actually in `testdata`, while the test itself contains the code to compile and run the test.
Expand Down Expand Up @@ -130,14 +181,34 @@ The following components are available:
- `readfile.go`
- `writefile.go`

"websocket" folder (for instructions, see below):
- `ws_request.go`
- `ws_respond.go`
- `ws_ans_req.go` (sample component - properly belongs in test suite)

**Test Websockets**

- position to your `GitHub\gofbp\testing` directory in DOS
- run `go test -run TestWebSocket -count=1`

- in File Explorer, locate `GitHub\gofbp\scripts\chat2.html`
- open with Firefox or Chrome
- enter `namelist` in the Command box, hit enter or Send
- you should see `Server: Line1`, `Server: Line2`, `Server: Line3` show up in the box below "Send"
- you can repeat this multiple times, and the 3 output lines will be appended each time to the current display
- click on `Stop WS` - you will see `End of dialog` next to `Status`

- you're done... except that, right now, it's not coming down cleanly, so you will have to close the DOS session manually. Hopefully, this will be fixed soon!


**To dos**

- More and better documentation
- Convert `panic`s to more standard Go error handling
- Way too much logging - have to make that optional - put remaining logging under switch control
- Way too much logging - have to make that optional - put remaining logging under switch control - *done!*
- Add subnet handling - *done!*
- Generate GoFBP networks from DrawFBP - https://github.com/jpaulm/drawfbp
- Add Load Balancing component
- Add sample code showing use of substreams
- Generate GoFBP networks from DrawFBP - https://github.com/jpaulm/drawfbp - *done!*
- Add Load Balancing component - *done!*
- Add sample code showing use of substreams - *done!*
- "Automatic" ports - *done!*
- Add Lua interface - see https://jpaulm.github.io/fbp/thlua.html
- Add Lua interface - similar to https://jpaulm.github.io/fbp/thlua.html

1 change: 1 addition & 0 deletions analyze_deadlock_compile.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
go build -o analyze_deadlock.exe utils\analyze_deadlock.go
25 changes: 25 additions & 0 deletions cmd/merge.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package main //change package name, or delete statement, if desired

// Network generated by DrawFBP

import (
"github.com/jpaulm/gofbp/components/testrtn"
"github.com/jpaulm/gofbp/core"
)

func main() {
params, err := core.LoadXMLParams("params.xml")
if err != nil {
panic(err)
}
net := core.NewNetwork("Merge")
net.SetParams(params)
sender2 := net.NewProc("Sender2", &testrtn.Sender{})
write___to_console := net.NewProc("Write___To_Console", &testrtn.WriteToConsole{})
sender1 := net.NewProc("Sender1", &testrtn.Sender{})
net.Connect(sender1, "OUT", write___to_console, "IN", 6)
net.Initialize("15", sender1, "COUNT")
net.Initialize("10", sender2, "COUNT")
net.Connect(sender2, "OUT", write___to_console, "IN", 6)
net.Run()
}
11 changes: 11 additions & 0 deletions component.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package gofbp

type Component interface {
Setup(*Process)
Execute(*Process)
}

type ComponentWithMustRun interface {
Component
MustRun() bool
}
4 changes: 4 additions & 0 deletions components/io/readfile.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/*Package io implements gofbp I/O*/
package io

import (
Expand All @@ -10,16 +11,19 @@ import (
"github.com/jpaulm/gofbp/core"
)

//ReadFile type defines ipt and opt
type ReadFile struct {
ipt core.InputConn
opt core.OutputConn
}

//Setup method opens readFile
func (readFile *ReadFile) Setup(p *core.Process) {
readFile.ipt = p.OpenInPort("FILENAME")
readFile.opt = p.OpenOutPort("OUT")
}

//Execute method starts Process
func (readFile *ReadFile) Execute(p *core.Process) {

icpkt := p.Receive(readFile.ipt)
Expand Down
20 changes: 12 additions & 8 deletions components/io/writefile.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,39 @@ import (
"github.com/jpaulm/gofbp/core"
)

// WriteFile type defines iptIP, ipt, and opt
type WriteFile struct {
iptIp core.InputConn
iptIP core.InputConn
ipt core.InputConn
opt core.OutputConn
}

//Setup method initializes Process
func (writeFile *WriteFile) Setup(p *core.Process) {
writeFile.iptIp = p.OpenInPort("FILENAME")
writeFile.iptIP = p.OpenInPort("FILENAME")
writeFile.ipt = p.OpenInPort("IN")
writeFile.opt = p.OpenOutPortOptional("OUT")
}

//MustRun method
func (WriteFile) MustRun() {}

//Execute method starts Process
func (writeFile *WriteFile) Execute(p *core.Process) {

icpkt := p.Receive(writeFile.iptIp)
icpkt := p.Receive(writeFile.iptIP)
fname, ok := icpkt.Contents.(string)
if !ok {
panic("Parameter (file name) not a string")
}
p.Discard(icpkt)
p.Close(writeFile.iptIp)
p.Close(writeFile.iptIP)

f, err := os.Create(fname)
if err != nil {
panic("Unable to open file: " + fname)
}
defer fmt.Println(p.Name+": File", fname, "written")
defer f.Close()

for {
Expand All @@ -43,20 +48,19 @@ func (writeFile *WriteFile) Execute(p *core.Process) {
break
}

data := []byte(pkt.Contents.(string) + "\n")

_, err2 := f.Write(data)
data := fmt.Sprint(pkt.Contents)
_, err2 := fmt.Fprintln(f, data)

if err2 != nil {
panic("Unable to write file: " + fname)
}

f.Sync()
if !writeFile.opt.IsConnected() {
p.Discard(pkt)
} else {
p.Send(writeFile.opt, pkt)
}

}
fmt.Println(p.Name+": File", fname, "written")
}
1 change: 1 addition & 0 deletions components/subnets/sssubnet1.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/*Package subnets implements FBP subnet processing*/
package subnets

import (
Expand Down
2 changes: 2 additions & 0 deletions components/subnets/subnet1.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,5 @@ func (subnet *Subnet1) Execute(p *core.Process) {

net.Run()
}

// NAME->0(proc1 SubIn)1.OUT -> IN.0(Proc2 WriteToConsole1)1.OUT -> NAME.0(proc3 SubOut);
4 changes: 3 additions & 1 deletion components/testrtn/concatstr.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package testrtn

import "github.com/jpaulm/gofbp/core"
import (
"github.com/jpaulm/gofbp/core"
)

type ConcatStr struct {
ipt core.InputArrayConn
Expand Down
2 changes: 0 additions & 2 deletions components/testrtn/counter.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ func (counter *Counter) Setup(p *core.Process) {
func (Counter) MustRun() {}

func (counter *Counter) Execute(p *core.Process) {


count := 0

Expand All @@ -43,5 +42,4 @@ func (counter *Counter) Execute(p *core.Process) {
pkt := p.Create(strconv.Itoa(count))
p.Send(counter.cnt, pkt)


}
2 changes: 0 additions & 2 deletions components/testrtn/discard.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ func (discard *Discard) Setup(p *core.Process) {
//func (Discard) MustRun() {}

func (discard *Discard) Execute(p *core.Process) {


for {
var pkt = p.Receive(discard.ipt)
Expand All @@ -30,5 +29,4 @@ func (discard *Discard) Execute(p *core.Process) {

}


}
37 changes: 37 additions & 0 deletions components/testrtn/intsender.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*Package testrtn tests gofbp code.*/
package testrtn

import (
//"fmt"
"strconv"

"github.com/jpaulm/gofbp/core"
)

/*Sender type defines ipt and opt for process send*/
type IntSender struct {
ipt core.InputConn
opt core.OutputConn
}

/*Setup function initializes a source process.*/
func (intsender *IntSender) Setup(p *core.Process) {
intsender.ipt = p.OpenInPort("COUNT")
intsender.opt = p.OpenOutPort("OUT")
}

/*Execute function launches a source process.*/
func (intsender *IntSender) Execute(p *core.Process) {

icpkt := p.Receive(intsender.ipt)
j, _ := strconv.Atoi(icpkt.Contents.(string))
p.Discard(icpkt)
p.Close(intsender.ipt)

var pkt *core.Packet
for i := 0; i < j; i++ {
pkt = p.Create(i)
p.Send(intsender.opt, pkt)
}

}
2 changes: 0 additions & 2 deletions components/testrtn/kick.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,8 @@ func (kick *Kick) Setup(p *core.Process) {
}

func (kick *Kick) Execute(p *core.Process) {


var pkt = p.Create("Kicker IP")
p.Send(kick.opt, pkt)


}
Loading