├── .eslintignore ├── .eslintrc ├── .gitignore ├── .npmignore ├── .npmrc ├── .project ├── .snyk ├── .travis.yml ├── 1.14.0 ├── LICENSE ├── README.md ├── appveyor.yml ├── components ├── breader.js ├── bwriter.js ├── collate.js ├── concat.js ├── copier.js ├── delay.js ├── discard.js ├── display.js ├── httpserver.js ├── lbal.js ├── passthru.js ├── randdelay.js ├── reader.js ├── recvr.js ├── repl.js ├── reverse.js ├── rrmerge.js ├── substreamsensitivemerge.js ├── writer.js ├── wsrecv.js └── wsresp.js ├── core ├── Enum.js ├── IIPConnection.js ├── IP.js ├── InputPort.js ├── Network.js ├── OutputPort.js ├── Process.js ├── ProcessConnection.js ├── runtimes │ └── FiberRuntime │ │ └── index.js ├── trace.js └── utils.js ├── docs ├── Fbptest11.png ├── Fbptestws.png └── JSFBP.png ├── examples ├── components │ ├── checksequencewithinsubstreams.js │ ├── compare.js │ ├── copier_closing.js │ ├── copier_nonlooper.js │ ├── gendata.js │ ├── gendatawithbreaks.js │ ├── genss.js │ └── mockmocksender.js ├── data │ ├── collate_output │ ├── dfile │ ├── mfile │ ├── text.txt │ └── zzzs.txt ├── fbptest01.js ├── fbptest02.js ├── fbptest03.js ├── fbptest04.js ├── fbptest05.js ├── fbptest06.js ├── fbptest07.js ├── fbptest08.js ├── fbptest09.js ├── fbptest10.js ├── fbptest11.js ├── fbptest12.js ├── fbptest13.js ├── fbptest14.js ├── fbptests.bat ├── fbptests.sh ├── fbptestvl.js ├── httpserver │ ├── fbphttpserver.js │ ├── myproc.js │ └── myresponse.js ├── testsubstreamsensitivesplitting.js ├── update.js ├── update_c.js └── websocketchat │ ├── fbptestwschat.js │ ├── index.html │ ├── wsbroadcast.js │ └── wssimproc.js ├── index.js ├── package-lock.json ├── package.json ├── renovate.json └── test ├── .eslintrc ├── components ├── 000000-1.png ├── breader.js ├── bwriter.js ├── collate.js ├── copier.js ├── delay.js ├── randdelay.js └── repl.js ├── core ├── Enum.js ├── InputPort.js ├── Network.js ├── network.fbp └── runtimes │ └── fiber-runtime.js ├── mocks ├── MockReceiver.js ├── MockSender.js └── TypeReceiver.js └── test_helper.js /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parserOptions": { 3 | "ecmaVersion": 5, 4 | "sourceType": "module", 5 | "ecmaFeatures": { 6 | "impliedStrict": true 7 | }, 8 | "root" :true 9 | }, 10 | "env": { 11 | "node": true 12 | }, 13 | "extends": [ 14 | "defaults" 15 | ], 16 | "rules": { 17 | "no-console": 0, 18 | "no-constant-condition": 0 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # node stuff 2 | node_modules/ 3 | 4 | npm-debug.log 5 | test/components/goodbye-world.txt 6 | examples/data/text_new.txt 7 | .idea/ 8 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Node template 3 | node_modules 4 | test 5 | examples 6 | 7 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jpaulm/jsfbp/841bd9f99d897a3e8fa80e435b3a3f62a25b2985/.npmrc -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | jsfbp 4 | JavaScript Implementation of Flow-Based Programming (FBP), which is a particular form of dataflow programming based on bounded buffers, information packets with defined lifetimes, named ports, and separate definition of connections. 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.snyk: -------------------------------------------------------------------------------- 1 | # Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. 2 | version: v1.14.1 3 | ignore: {} 4 | # patches apply the minimum changes required to fix a vulnerability 5 | patch: 6 | SNYK-JS-LODASH-450202: 7 | - lodash: 8 | patched: '2019-07-03T21:01:27.622Z' 9 | - snyk > snyk-nuget-plugin > lodash: 10 | patched: '2019-07-05T21:01:32.322Z' 11 | - snyk > lodash: 12 | patched: '2019-07-05T21:01:32.322Z' 13 | - snyk > inquirer > lodash: 14 | patched: '2019-07-05T21:01:32.322Z' 15 | - snyk > snyk-mvn-plugin > lodash: 16 | patched: '2019-07-05T21:01:32.322Z' 17 | - snyk > snyk-nodejs-lockfile-parser > lodash: 18 | patched: '2019-07-05T21:01:32.322Z' 19 | - snyk > @snyk/dep-graph > lodash: 20 | patched: '2019-07-05T21:01:32.322Z' 21 | - snyk > snyk-config > lodash: 22 | patched: '2019-07-05T21:01:32.322Z' 23 | - snyk > snyk-go-plugin > graphlib > lodash: 24 | patched: '2019-07-05T21:01:32.322Z' 25 | - snyk > snyk-nodejs-lockfile-parser > graphlib > lodash: 26 | patched: '2019-07-05T21:01:32.322Z' 27 | - snyk > @snyk/dep-graph > graphlib > lodash: 28 | patched: '2019-07-05T21:01:32.322Z' 29 | - snyk > snyk-php-plugin > @snyk/composer-lockfile-parser > lodash: 30 | patched: '2019-07-05T21:01:32.322Z' 31 | SNYK-JS-HTTPSPROXYAGENT-469131: 32 | - snyk > proxy-agent > https-proxy-agent: 33 | patched: '2019-10-03T21:01:26.691Z' 34 | - snyk > proxy-agent > pac-proxy-agent > https-proxy-agent: 35 | patched: '2019-10-03T21:01:26.691Z' 36 | SNYK-JS-TREEKILL-536781: 37 | - snyk > snyk-sbt-plugin > tree-kill: 38 | patched: '2019-12-11T21:01:37.849Z' 39 | SNYK-JS-LODASH-567746: 40 | - lodash: 41 | patched: '2020-05-01T03:24:44.772Z' 42 | - snyk > lodash: 43 | patched: '2020-05-01T03:24:44.772Z' 44 | - snyk > @snyk/dep-graph > lodash: 45 | patched: '2020-05-01T03:24:44.772Z' 46 | - snyk > inquirer > lodash: 47 | patched: '2020-05-01T03:24:44.772Z' 48 | - snyk > snyk-config > lodash: 49 | patched: '2020-05-01T03:24:44.772Z' 50 | - snyk > snyk-mvn-plugin > lodash: 51 | patched: '2020-05-01T03:24:44.772Z' 52 | - snyk > snyk-nodejs-lockfile-parser > lodash: 53 | patched: '2020-05-01T03:24:44.772Z' 54 | - snyk > snyk-nuget-plugin > lodash: 55 | patched: '2020-05-01T03:24:44.772Z' 56 | - snyk > @snyk/dep-graph > graphlib > lodash: 57 | patched: '2020-05-01T03:24:44.772Z' 58 | - snyk > snyk-go-plugin > graphlib > lodash: 59 | patched: '2020-05-01T03:24:44.772Z' 60 | - snyk > snyk-nodejs-lockfile-parser > graphlib > lodash: 61 | patched: '2020-05-01T03:24:44.772Z' 62 | - snyk > @snyk/snyk-cocoapods-plugin > @snyk/dep-graph > lodash: 63 | patched: '2020-05-01T03:24:44.772Z' 64 | - snyk > snyk-nuget-plugin > dotnet-deps-parser > lodash: 65 | patched: '2020-05-01T03:24:44.772Z' 66 | - snyk > snyk-php-plugin > @snyk/composer-lockfile-parser > lodash: 67 | patched: '2020-05-01T03:24:44.772Z' 68 | - snyk > @snyk/snyk-cocoapods-plugin > @snyk/dep-graph > graphlib > lodash: 69 | patched: '2020-05-01T03:24:44.772Z' 70 | - snyk > @snyk/snyk-cocoapods-plugin > @snyk/cocoapods-lockfile-parser > @snyk/ruby-semver > lodash: 71 | patched: '2020-05-01T03:24:44.772Z' 72 | - snyk > @snyk/snyk-cocoapods-plugin > @snyk/cocoapods-lockfile-parser > @snyk/dep-graph > graphlib > lodash: 73 | patched: '2020-05-01T03:24:44.772Z' 74 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "node" 4 | - "12.22.6" 5 | 6 | cache: npm 7 | install: npm ci 8 | 9 | script: npm test 10 | -------------------------------------------------------------------------------- /1.14.0: -------------------------------------------------------------------------------- 1 | + mocha@6.2.0 2 | updated 1 package and audited 12995 packages in 5.249s 3 | found 0 vulnerabilities 4 | 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 J. Paul Morrison 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![npm version](https://badge.fury.io/js/jsfbp.svg)](https://badge.fury.io/js/jsfbp) 2 | [![Build Status](https://travis-ci.org/jpaulm/jsfbp.svg?branch=master)](https://travis-ci.org/jpaulm/jsfbp) 3 | [![Known Vulnerabilities](https://snyk.io/test/github/jpaulm/jsfbp/badge.svg?targetFile=package.json)](https://snyk.io/test/github/jpaulm/jsfbp?targetFile=package.json) 4 | 5 | jsfbp 6 | === 7 | 8 | ### JavaScript Implementation of FBP - no longer supported. Please take a copy by Oct. 1, 2021 if you need one! 9 | 10 | General web site on Flow-Based Programming: https://jpaulm.github.io/fbp/ . 11 | 12 | 13 | 14 | "Classical" FBP "green thread" implementation written in JavaScript, using Node-Fibers - . 15 | 16 | JSFBP takes advantage of JavaScript's concept of functions as first-degree objects to allow applications to be built using "green threads". JSFBP makes use of an internal "Future Events Queue" which supports the green threads, and provides quite good performance (see below) - the JavaScript events queue is only used for JavaScript asynchronous functions, as before. 17 | 18 | 19 | 20 | **JSFBP is no longer supported, as Node-Fibers is being discontinued (I assume early 2021).** Here is the relevant quote from https://github.com/laverdet/node-fibers : 21 | 22 |
"The author of this project recommends you avoid its use if possible. The original version of this module targeted nodejs v0.1.x in early 2011 when JavaScript on the server looked a lot different. Since then async/await, Promises, and Generators were standardized and the ecosystem as a whole has moved in that direction."
23 | 24 | 25 | # Installing Fibers 26 | 27 | As suggested in https://github.com/laverdet/node-fibers/issues/new , make sure your version of `nodejs` is an even one. 28 | 29 | Go into command mode, and enter `npm install fibers`. 30 | 31 | If this command has trouble finding Python, install Python 2.7.10, then run `npm --add-python-to-path='true' --debug install --global windows-build-tools`. Don't know if this is still necessary! 32 | 33 | # General 34 | 35 | To run test cases, position in your command shell to `GitHub/jsfbp` and type in `node examples/fbptestxx`, where `xx` is the test case number. 36 | 37 | Test cases so far: 38 | 39 | - `fbptest01` - 3 processes: 40 | - `gendata` (generates ascending numeric values) 41 | - `copier` (copies) 42 | - `recvr` (displays incoming values to console) 43 | 44 | ![jsfbp](https://cloud.githubusercontent.com/assets/312871/12333111/52f268b8-bac0-11e5-963d-08061734dc68.png "Simple Test Network") 45 | 46 | - `fbptest02` - `gendata` replaced with `reader` 47 | - `fbptest03` - `gendata` and `reader` both feeding into `copier.IN` 48 | - `fbptest04` - `gendata` feeding `repl` which sends 3 copies of input IP (as specified in network), each copy going to a separate element of array port `OUT`; all 3 copies then feeding into `recvr.IN` 49 | - `fbptest05` - Two copies of `reader` running concurrently, one feeds directly into `rrmerge` ("round robin" merge) input port element 0; other one into `copier` and then into `rrmerge` input port element 1; from `rrmerge.OUT` to `recvr.IN` 50 | - `fbptest06` - The output streams of the `repl` (in `fbptest04`) are fed to the input array port of `rrmerge`, and from its `OUT` to `recvr.IN` 51 | - `fbptest07` - Creates a deadlock condition - the status of each Process is displayed 52 | - `fbptest08` - reads text, reverses it twice and outputs it 53 | - `fbptest09` - `copier` in `fbptest01` is replaced with a version of `copier` which terminates prematurely and closes its input port, bringing the network down (ungracefully!) 54 | - `fbptest10` - `copier` in `fbptest01` is replaced with a non-looping version of `copier` 55 | - `fbptest11` - Load balancer (`lbal`) feeding 3 instances of a random delay component (`randdelay`) 56 | 57 | ![fbptest11](https://cloud.githubusercontent.com/assets/312871/12333117/5ae111a0-bac0-11e5-8167-0bb4bfb24f25.png "Diagram of fbptest11 above") 58 | 59 | - `fbptest12` - `reader OUT -> IN copier OUT -> IN writer` 60 | - `fbptest13` - Simple network to demonstrate functioning of random delay component (`randdelay`) 61 | - `fbptest14` - Network demonstrating parallelism using two instances of `reader` and two fixed delay components (`delay`) 62 | - `fbptestvl` - Volume test (see below): `gendata` -> `copier` -> `discard` 63 | - `testsubstreamsensitivesplitting.js` - Test substream-sensitive logic in `lbal`, feeding `substreamsensitivemerge.js` 64 | 65 | "Update" networks 66 | ---------- 67 | - `update` - "Update" run, demonstrating use of `collate.js` 68 | - `update_c` - Same as `update.js` but routing output to a `compare` process, rather than to `display` 69 | 70 | The following diagram shows `update` and `update_c` in one diagram using the DrawFBP Enclosure function - this is not really a valid DrawFBP diagram, so no port names are shown: 71 | 72 | ![update_combined](https://cloud.githubusercontent.com/assets/312871/12332149/efc37f52-baba-11e5-8775-d1516a0cc568.png "Diagram showing update.js and update_c.js") 73 | 74 | Here is `update_c` by itself, with component and port names marked in - it contains all the information needed to generate a running JSFBP network (the file and report icons do not generate any code): 75 | 76 | ![update_c](https://cloud.githubusercontent.com/assets/312871/12379403/ffb3d6ea-bd27-11e5-9f56-1a8e4758dc1d.png "Diagram showing update_c.js") 77 | 78 | WebSockets 79 | ---- 80 | 81 | - `fbptestws` - Schematic web socket server (simple Process shown can be replaced by any structure of Processes, provided interfaces are adhered to) 82 | 83 | ![fbptestws](https://cloud.githubusercontent.com/assets/312871/12344772/0433b4a8-bb0f-11e5-8084-082b9a7b9c22.png "Diagram of fbptestws above") 84 | 85 | Some of these have tracing set on, depending on what testing was being done when they were promoted! 86 | 87 | These tests (except for `fbptestws`) can be run sequentially by running `fbptests.bat`. 88 | 89 | # Components 90 | 91 | - `breader` - reads from a binary file specified by FILE IIP and sends one IP per byte in the file. Starts sending IPs as soon as first byte is read. 92 | - `bwriter` - takes a stream of IPs containing bytes and writes them to a file from its FILE IIP. Starts writing as soon as the first IP comes in. 93 | - `collate` - collates from 1 to any number of sorted input streams, generating merged stream with bracket IPs inserted (sort fields assumed to be contiguous starting at 1st byte; all streams assumed to be sorted on same fields, in ascending sequence) 94 | - `concat` - concatenates all the streams that are sent to its array input port (size determined in network definition) 95 | - `copier` - copies its input stream to its output stream 96 | - `copier_closing` - forces close of input port after 20 IPs 97 | - `copier_nonlooper` - same as `copier`, except that it is written as a non-looper (it has been modified to call the FBP services from lower in the process's stack) 98 | - `discard` - discard (drop) all incoming IPs 99 | - `display` - display all incoming IPs, including bracket IPs 100 | - `gendata` - sends as many IPs to its output port as are specified by its COUNT IIP (each just contains the current count) 101 | - `lbal` - load balancer - sends output to output port array element with smallest number of IPs in transit 102 | - `randdelay` - sends incoming IPs to output port after random number of millisecs (between 0 and 400) 103 | - `reader` - does an asynchronous read on the file specified by its FILE IIP 104 | - `recvr` - receives its incoming stream and displays the contents on the console 105 | - `repl` - replicates the incoming IPs to the streams specified by an array output port (it does not handle tree structures) 106 | - `reverse` - reverses the string contained in each incoming IP 107 | - `rrmerge` - "round robin" merge 108 | - `substreamsensitivemerge.js` - merges multiple input streams, but keeps IPs in correct sequence within each substream, although sequence of substreams is not guaranteed 109 | - `writer` - does an asynchronous write to the file specified by its FILE IIP 110 | 111 | - `wsrecv` - general web socket "receive" component for web socket server - outputs substream 112 | - `wsresp` - general web socket "respond" component sending data from web socket server to client - takes substream as input 113 | - `wssimproc` - "simulated" processing for web socket server - actually just outputs 3 names 114 | 115 | 116 | # API 117 | 118 | ## For application developers 119 | 120 | Networks can be generated programmatically or by loading in an FBP file. 121 | 122 | ### Programmatically 123 | 124 | 1. Get access to JSFBP: `var fbp = require('fbp')` 125 | 2. Create a new network: `var network = new fbp.Network();` 126 | 3. Define your network: 127 | - Add processes: `network.defProc(...)` Note: when several processes use the same component, `defProc` takes the process name as a second argument. 128 | - Connect output ports to input ports: `network.connect(...)` 129 | - Specify IIPs: `network.initialize(...)` 130 | 4. Create a new runtime: `var fiberRuntime = new fbp.FiberRuntime();` 131 | 5. Run it! 132 | 133 | ``` 134 | network.run(fiberRuntime, {trace: true/false}, function success() { 135 | console.log("Finished!"); 136 | }); 137 | ``` 138 | 139 | ### Via an FBP file 140 | 141 | 1. Generate an `.fbp` file that complies with the specification under [parsefbp](https://github.com/jpaulm/parsefbp). 142 | 2. Get access to JSFBP: `var fbp = require('fbp')` 143 | 3. Load the contents of the `.fbp` file into a String: `fs.readFile(__dirname + '/network.fbp' ...);` 144 | 4. Create a new network: `var network = new fbp.Network.createFromGraph(fileContents);` If you're using components 145 | that are local to your application, use a second parameter giving the directory that contains your components. 146 | 5. Create a new runtime: `var fiberRuntime = new fbp.FiberRuntime();` 147 | 6. Run it! 148 | ``` 149 | network.run(fiberRuntime, {trace: true/false}, function success() { 150 | console.log("Finished!"); 151 | }); 152 | ``` 153 | 154 | 155 | Activating `trace` can be desired in debugging scenarios. 156 | 157 | ### Useful methods 158 | 159 | - `Network#defProc(component[, name])` Creates a process from a component, defined by the first parameter. 160 | 161 | - The first parameter can be a function or a string. When a string is used, the component is loaded according to three 162 | possibilities: 163 | - If the component string starts with `'./'` then the component is assumed to be one of he JSFBP components and is loaded. 164 | For example: `'./components/copier.js'` 165 | - If the component string starts with `'/'` then the component is assumed to be local to the application. If your network has 166 | local components, then the network needs to have been instantiated with a `{ componentRoot: 'dir' }` object so that 167 | it knows where to find the components. 168 | - If the component string contains a `/`, then it is assumed to be of the form `'package/component'`. Thus `package` is loaded 169 | and then `component` is retrieved from it. If `package` is `'jsfbp'`, then it is loaded from the JSFBP `components` directory. 170 | - Otherwise, the string is assumed to be a node module that _is_ an FBP component and it is simply 171 | loaded via `require`. 172 | 173 | - The second parameter is an optional name for the Process. If not provided, it will be inferred from the `component`. 174 | 175 | ## For component developers 176 | 177 | Component headers: 178 | `'use strict';` 179 | 180 | In most cases you do not need to *require()* any JSFBP-related scripts or libraries as a component developer. Everything you need is injected into the component's function as its context `this` (the process object) and as a parameter (the runtime object). 181 | Some utility functions are stored in `core/utils.js`. Import them if you really need them. 182 | You should generally refrain from accessing runtime-related code (e.g. Fibers) to ensure the greatest compatibility. 183 | 184 | Component services 185 | 186 | - In what follows, the `this` is only valid if the function is called from the component level; if called from a subroutine, pass in `this` as a parameter. 187 | 188 | - `var ip = this.createIP(contents);` - create an IP containing `contents` 189 | - `var ip = this.createIPBracket(this.IPTypes.OPEN|this.IPTypes.CLOSE[, contents])` - create an open or close bracket IP 190 | - **Be sure** to include IP: `var IP = require('IP')` to gain access to the IP constants. 191 | - `this.dropIP(ip);` - drop IP 192 | 193 | - `var inport = this.openInputPort('IN');` - create InputPort variable 194 | - `var array = this.openInputPortArray('IN');` - create input array port array 195 | - `var outport = this.openOutputPort('OUT');` - create OutputPort variable 196 | - `var array = this.openOutputPortArray('OUT');` - create output array port array 197 | 198 | - `var ip = inport.receive();` - returns null if end of stream 199 | - `var ip = array[i].receive();` - receive to element of port array 200 | - `outport.send(ip);` - returns -1 if send unable to deliver 201 | - `array[i].send(ip);` - send from element of port array 202 | - `inport.close();` - close input port (or array port element) 203 | 204 | - `runtime.runAsyncCallback()` - used when doing asynchronous I/O in component; when using this function, include `runtime` in component header, e.g. `module.exports = function xxx(runtime) { ...` 205 | 206 | Example: 207 | ``` 208 | runtime.runAsyncCallback(function (done) { 209 | // your asynchronous 210 | ... 211 | // call done (possibly asynchronously) when you're done! 212 | done(); 213 | }); 214 | ``` 215 | - `Utils.getElementWithSmallestBacklog(array);` - used by `lbal` - not for general use 216 | - **Be sure** to include Utils: `var Utils = require('core/utils')`. 217 | 218 | - `Utils.findInputPortElementWithData(array);` - used by `substreamsensitivemerge` - not for general use 219 | - **Be sure** to include Utils: `var Utils = require('core/utils')`. 220 | 221 | # Install & Run 222 | 223 | We use `node-fibers` which is known to work with `Node.js 10.16.0` (as of 25.07.2019). 224 | 225 | 1. Install [Node.js](http://nodejs.org/download/) 226 | 2. Clone or download this project 227 | 3. Execute `npm install` 228 | 229 | If you get an MSB4019 or similar error messages involving `utf-8-validate` and `bufferutil` (some dependencies deep down the dependency tree), you can just ignore them, given the optional nature of these components' compilation. 230 | 231 | 4. Run `node examples/fbptestxx.js`, where `fbptestxx` is any of the tests listed above. If tracing is desired, change the value of the `trace` variable at the bottom of `fbptestxx.js` to `true`. 232 | 5. All these tests can be run sequentially by running `examples/fbptests.bat`, or by running `examples/fbptests.sh` under `bash`. 233 | 234 | *Important* - BitDefender Antivirus 2016 anti-ransomware feature seems to interfere with `git`- we suggest you leave it turned off while working with `git`. 235 | 236 | ## Full install 237 | 238 | If you wish to eliminate the errors mentioned in point #3 under *Install*, you will need to install Python 2.x and Visual Studio Express for Desktop 2013. This doesn't seem to guarantee an error-free `npm install`, however. Still `jsfbp` works fine, even with these errors. 239 | 240 | 1. Install [Node.js](http://nodejs.org/download/) 241 | 2. Install Python 2.x 242 | 3. Install Visual Studio Express for Desktop 2013 (click on http://go.microsoft.com/fwlink/?LinkId=532500&clcid=0x409 ) 243 | 4. Clone or download this project 244 | 5. Open a _new_ shell (The shell should not have been opened from before the Visual Studio installation because then the PATH and other environment variables are not yet updated.) 245 | 6. Optionally prepend Python 2.x to your PATH if you haven't already done so 246 | - e.g. `SET PATH=C:\path\to\python2-directory\;%PATH%` 247 | 7. Execute `npm install` 248 | 8. Run `node examples/fbptestxx.js`, where `fbptestxx` is any of the tests listed above. If tracing is desired, change the value of the `trace` variable at the bottom of `fbptestxx.js` to `true`. 249 | 9. Install requires the following `npm` packages: `parsefbp`, `fibers`, `mocha`, `chai`, `lodash` and `mocha-fibers` - you may have to do `npm` installs for some or all of these. 250 | 10. All these tests can be run sequentially by running `examples/fbptests.bat`, or by running `examples/fbptests.sh` under `bash`. 251 | 252 | *Important* - BitDefender Antivirus 2016 anti-ransomware feature seems to interfere with `git`- we suggest you leave it turned off while working with `git`. 253 | 254 | # Testing with Mocha 255 | 256 | The folder called `test` contains a number of Mocha tests. 257 | 258 | 1. Run `npm test` to execute a series of tests (all the `fbptestxx.js` tests in sequence). 259 | 2. Alternatively, you can directly execute `node ./node_modules/mocha/bin/mocha --recursive --require test/test_helper.js` in case you need to adjust the path to Node's binary or pass further parameters to Mocha. 260 | 261 | # Testing Sample HTTP Server 262 | 263 | Run `node examples/httpserver/fbphttpserver.js`, which is a simple HTTP server which is similar to the one in the sample at: 264 | 265 | NOTE: The HTTP server components are currently all custom components, based on the components used in the simple web socket chat server described below. 266 | 267 | # Testing Simple Web Socket Chat Server 268 | 269 | Run `node examples/websocketchat/fbptestwschat.js`, which is a simple web socket chat server which responds to any request by broadcasting it to all connected clients. It is similar to the chat sample at: http://socket.io/get-started/chat/ except for serving the client HTML. 270 | 271 | `examples/websocketchat/index.html` is intended as a simple chat client for testing with `fbptestwschat.js`. If Firefox doesn't work for you, Chrome and Safari will work. 272 | 273 | Just enter any string into the input field, and click on `Send`, and it will broadcast it to all clients that are connected. 274 | 275 | Click on the `Stop WS` button, and the network will come down. 276 | 277 | Tracing 278 | --- 279 | 280 | Here is a sample section of the trace output for `fbptest08.js`: 281 | ``` 282 | recvr recv OK: externally to the processes. These black box processes can be rec 283 | onnected endlessly 284 | data: externally to the processes. These black box processes can be reconnected 285 | endlessly 286 | recvr IP dropped with: externally to the processes. These black box processes ca 287 | n be reconnected endlessly 288 | recvr recv from recvr.IN 289 | Yield/return: state of future events queue: 290 | - reverse2 - status: ACTIVE 291 | --- 292 | --- 293 | reverse2 send OK 294 | reverse2 IP dropped with: si PBF .yllanretni degnahc eb ot gnivah tuohtiw snoit 295 | acilppa tnereffid mrof ot 296 | reverse2 recv from reverse2.IN 297 | reverse2 recv OK: .detneiro-tnenopmoc yllarutan suht 298 | reverse2 send to reverse2.OUT: thus naturally component-oriented. 299 | Yield/return: state of future events queue: 300 | - recvr - status: ACTIVE 301 | --- 302 | --- 303 | recvr recv OK: to form different applications without having to be changed inter 304 | nally. FBP is 305 | ``` 306 | 307 | Performance 308 | --- 309 | 310 | The volume test case (`fbptestvl`) with 100,000,000 IPs running through three processes took 164 seconds, on my machine 311 | which has 4 AMD Phenom(tm) II X4 925 processors. 312 | 313 | Since there are two connections, giving a total of 200,000,000 send/receive pairs, this works out to approx. 314 | 0.82 microsecs per send/receive pair. Of course, as it is JavaScript, this test only uses 1 core intensively, 315 | although there is some matching activity on the other cores (why...?!) 316 | 317 | 318 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # Test against this version of Node.js 2 | environment: 3 | nodejs_version: "0.10" 4 | 5 | init: 6 | - git config --global core.autocrlf true 7 | # Install scripts. (runs after repo cloning) 8 | install: 9 | # Get the latest stable version of Node.js or io.js 10 | - ps: Install-Product node $env:nodejs_version 11 | # install modules 12 | - npm install 13 | 14 | # Post-install test scripts. 15 | test_script: 16 | # Output useful info for debugging. 17 | - node --version 18 | - npm --version 19 | # run tests 20 | - npm test 21 | 22 | # Don't actually build. 23 | build: off 24 | -------------------------------------------------------------------------------- /components/breader.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /** 3 | * This component reads a file and sends one IP per byte of the file to OUT 4 | * The IPs are streamed out as the file is read, so the network can start flowing as soon as reading begins. 5 | * This should make things a lot faster (and less memory hungry) for large files. 6 | * File name is given by the FILE inport 7 | */ 8 | 9 | var fs = require('fs'); 10 | var trace = require('../core/trace'); 11 | 12 | var READ_SIZE = 4; 13 | 14 | module.exports = function reader(runtime) { 15 | var inport = this.openInputPort('FILE'); 16 | var ip = inport.receive(); 17 | var fname = ip.contents; 18 | this.dropIP(ip); 19 | 20 | trace("Opening file: " + fname); 21 | var openResult = runtime.runAsyncCallback(openFile(fname, 'r', this)); 22 | 23 | var fileDescriptor = openResult[1]; 24 | if (fileDescriptor == undefined) { 25 | console.log("OPEN error: " + openResult); 26 | return; 27 | } 28 | trace("Got fd: " + fileDescriptor); 29 | 30 | var outport = this.openOutputPort('OUT'); 31 | trace("Starting read"); 32 | outport.send(this.createIPBracket(this.IPTypes.OPEN)); 33 | readFile(runtime, this, fileDescriptor, outport); 34 | 35 | fs.closeSync(fileDescriptor); 36 | outport.send(this.createIPBracket(this.IPTypes.CLOSE)); 37 | 38 | }; 39 | 40 | function readFile(runtime, proc, fileDescriptor, outport) { 41 | do { 42 | var readResult = runtime.runAsyncCallback(readData(fileDescriptor, READ_SIZE)); 43 | if (readResult[0]) { 44 | console.error(readResult[0]); 45 | return; 46 | } 47 | var bytesRead = readResult[1]; 48 | var data = readResult[2]; 49 | 50 | for (var i = 0; i < bytesRead; i++) { 51 | var byte = data[i]; 52 | trace("Got byte: " + byte); 53 | outport.send(proc.createIP(byte)); 54 | } 55 | } while (bytesRead === READ_SIZE); 56 | } 57 | 58 | function openFile(path, flags) { 59 | return function (done) { 60 | fs.open(path, flags, function (err, fd) { 61 | done([err, fd]); 62 | }); 63 | } 64 | } 65 | 66 | function readData(fd, size) { 67 | return function (done) { 68 | //fs.read(fd,new Buffer(size), 0, size, null, function(err, bytesRead, buffer) { 69 | fs.read(fd,Buffer.alloc(size), 0, size, null, function(err, bytesRead, buffer) { 70 | done([err, bytesRead, buffer]); 71 | }); 72 | } 73 | } 74 | 75 | -------------------------------------------------------------------------------- /components/bwriter.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /** 3 | * This component writes a binary file based on IPs that it receives. 4 | * The IPs are written out as the IPs come in, so the writing goes in pace with data flowing in. 5 | * This should make things a lot faster (and less memory hungry) for large files. 6 | * File name is given by the FILE inport 7 | */ 8 | 9 | var fs = require('fs'); 10 | var trace = require('../core/trace'); 11 | 12 | module.exports = function reader(runtime) { 13 | var filePort = this.openInputPort('FILE'); 14 | var ip = filePort.receive(); 15 | var fname = ip.contents; 16 | this.dropIP(ip); 17 | 18 | trace("Opening file: " + fname); 19 | var openResult = runtime.runAsyncCallback(openFile(fname, 'w', this)); 20 | 21 | var fileDescriptor = openResult[1]; 22 | if (fileDescriptor == undefined) { 23 | console.log("OPEN error: " + openResult); 24 | return; 25 | } 26 | trace("Got fd: " + fileDescriptor); 27 | 28 | var inPort = this.openInputPort('IN'); 29 | trace("Starting read"); 30 | var bracket = inPort.receive(); 31 | if(bracket.type != this.IPTypes.OPEN) { 32 | console.log("ERROR: Received non OPEN bracket"); 33 | console.log(bracket); 34 | return; 35 | } 36 | this.dropIP(bracket); 37 | 38 | writeFile(runtime, this, fileDescriptor, inPort); 39 | 40 | fs.closeSync(fileDescriptor); 41 | }; 42 | 43 | function writeFile(runtime, proc, fileDescriptor, inPort) { 44 | do { 45 | var inIP = inPort.receive(); 46 | if(inIP.type == proc.IPTypes.NORMAL) { 47 | var writeResult = runtime.runAsyncCallback(writeData(fileDescriptor, inIP.contents)); 48 | if (writeResult[0]) { 49 | console.error(writeResult[0]); 50 | return; 51 | } 52 | if(writeResult[1] !== 1) { 53 | console.error("Insufficient data written!"); 54 | return; 55 | } 56 | } 57 | proc.dropIP(inIP); 58 | } while (inIP.type != proc.IPTypes.CLOSE); 59 | } 60 | 61 | function openFile(path, flags) { 62 | return function (done) { 63 | fs.open(path, flags, function (err, fd) { 64 | done([err, fd]); 65 | }); 66 | } 67 | } 68 | 69 | function writeData(fd, byte) { 70 | return function (done) { 71 | var writeBuffer = Buffer.alloc(1); 72 | writeBuffer[0] = byte; 73 | fs.write(fd, writeBuffer, 0, 1, null, function(err, written) { 74 | done([err, written]); 75 | }); 76 | } 77 | } 78 | 79 | -------------------------------------------------------------------------------- /components/collate.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * This component collates 1 to n input streams based on some number of contiguous key values; it 5 | * assumes all incoming streams are sorted on the same keys, in ascending order 6 | * The keys start in the first byte of each incoming IP 7 | * Key lengths are specified in the CTLFIELDS IIP, separated by commas 8 | * 9 | */ 10 | 11 | module.exports = function collate() { 12 | var ctlfieldsPort = this.openInputPort('CTLFIELDS'); 13 | var inportArray = this.openInputPortArray('IN'); 14 | var outport = this.openOutputPort('OUT'); 15 | 16 | var ctlfieldsIP = ctlfieldsPort.receive(); 17 | var ctlfields = ctlfieldsIP.contents.split(','); 18 | var ctlfieldlens = ctlfields.map(function (str) { 19 | return parseInt(str); 20 | }); 21 | var prev = null; 22 | var hold = null; 23 | 24 | this.dropIP(ctlfieldsIP); 25 | 26 | var totalFieldLength = ctlfieldlens.reduce(function (acc, n) { 27 | return acc + n; 28 | }, 0); 29 | 30 | var portCount = inportArray.length; 31 | var ips = []; 32 | 33 | inportArray.forEach(function (port, index) { 34 | ips[index] = port.receive(); 35 | if (ips[index] === null) { 36 | portCount--; 37 | } 38 | }); 39 | 40 | for (var i = 0; i < ctlfields.length; i++) { 41 | var p = this.createIPBracket(this.IPTypes.OPEN); 42 | outport.send(p); 43 | } 44 | 45 | while (portCount) { 46 | var lowestIndex = 0; 47 | hold = "\uffff"; 48 | ips.forEach(function (ip, portIndex) { 49 | if (ip !== null) { 50 | var key = ip.contents.substring(0, totalFieldLength); 51 | if (key < hold) { 52 | lowestIndex = portIndex; 53 | hold = key; 54 | } 55 | } 56 | }); 57 | 58 | sendOutput(lowestIndex, this); 59 | 60 | ips[lowestIndex] = inportArray[lowestIndex].receive(); 61 | if (ips[lowestIndex] === null) { 62 | portCount--; 63 | } 64 | } 65 | 66 | ctlfields.forEach(function() { 67 | var p = this.createIPBracket(this.IPTypes.CLOSE); 68 | outport.send(p); 69 | }.bind(this)); 70 | 71 | 72 | function sendOutput(x, proc) { 73 | if (prev != null) { 74 | var level = findLevel(); 75 | for (i = 0; i < level; i++) { 76 | var p2 = proc.createIPBracket(proc.IPTypes.CLOSE); 77 | outport.send(p2); 78 | } 79 | for (i = 0; i < level; i++) { 80 | p2 = proc.createIPBracket(proc.IPTypes.OPEN); 81 | outport.send(p2); 82 | } 83 | } 84 | outport.send(ips[x]); 85 | prev = hold; 86 | } 87 | 88 | function findLevel() { 89 | var j = 0; 90 | //console.log(ctlfields); 91 | for (var i = 0; i < ctlfields.length; i++) { 92 | var h1 = hold.substring(j, j + ctlfieldlens[i]); 93 | var p1 = prev.substring(j, j + ctlfieldlens[i]); 94 | //console.log(h1 + ':' + p1); 95 | if (h1.localeCompare(p1) != 0) { 96 | return ctlfields.length - i; 97 | } 98 | j += ctlfieldlens[i]; 99 | } 100 | return 0; 101 | } 102 | }; 103 | -------------------------------------------------------------------------------- /components/concat.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function concat() { 4 | var array = this.openInputPortArray('IN'); 5 | var outport = this.openOutputPort('OUT'); 6 | var ip = null; 7 | 8 | for (var i = 0; i < array.length; i++) { 9 | while (true) { 10 | ip = array[i].receive(); 11 | if (ip === null) { 12 | break; 13 | } 14 | outport.send(ip); 15 | } 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /components/copier.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function copier() { 4 | var inport = this.openInputPort('IN'); 5 | var outport = this.openOutputPort('OUT'); 6 | while (true) { 7 | var ip = inport.receive(); 8 | if (ip === null) { 9 | break; 10 | } 11 | outport.send(ip); 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /components/delay.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function delay(runtime) { 4 | // var proc = fbp.getCurrentProc(); 5 | var inport = this.openInputPort('IN'); 6 | var intvlport = this.openInputPort('INTVL'); 7 | var outport = this.openOutputPort('OUT'); 8 | var intvl_ip = intvlport.receive(); 9 | var intvl = intvl_ip.contents; 10 | this.dropIP(intvl_ip); 11 | 12 | while (true) { 13 | var ip = inport.receive(); 14 | if (ip === null) { 15 | break; 16 | } 17 | //fbp.setCallbackPending(true); 18 | //console.log('start wait for ' + Math.round(intvl * 100) / 100 + ' msecs: ' + ip.contents); 19 | runtime.runAsyncCallback(genSleepFun(this, intvl)); 20 | //var name = outport.name.substring(0, outport.name.indexOf(".")); 21 | //console.log(name + ' end sleep ' ); 22 | //fbp.setCallbackPending(false); 23 | outport.send(ip); 24 | } 25 | }; 26 | 27 | function genSleepFun(proc, ms) { 28 | return function (done) { 29 | //console.log(proc.name + ' start sleep: ' + ms + ' msecs'); 30 | 31 | setTimeout(function () { 32 | done(); 33 | }, ms); 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /components/discard.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function discard() { 4 | var inport = this.openInputPort('IN'); 5 | while (true) { 6 | var ip = inport.receive(); 7 | if (ip === null) { 8 | break; 9 | } 10 | //var data = ip.contents; 11 | //console.log('data: ' + data); 12 | this.dropIP(ip); 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /components/display.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * display has an optional output port 5 | */ 6 | 7 | module.exports = function display() { 8 | var inport = this.openInputPort('IN'); 9 | var outport = this.openOutputPort('OUT', 'OPTIONAL'); 10 | while (true) { 11 | var ip = inport.receive(); 12 | if (ip === null) { 13 | break; 14 | } 15 | var data = ip.contents; 16 | if (ip.type == this.IPTypes.OPEN) 17 | console.log('OPEN: ' + data); 18 | else if (ip.type == this.IPTypes.CLOSE) 19 | console.log('CLOSE: ' + data); 20 | else 21 | console.log('data: ' + data); 22 | outport.send(ip); 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /components/httpserver.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var http = require('http'); 4 | 5 | module.exports = function httpserver(runtime) { 6 | var inport = this.openInputPort('PORTNO'); 7 | var outport = this.openOutputPort('OUT'); 8 | 9 | var ip = inport.receive(); 10 | var portno = ip.contents; 11 | var server = http.createServer(handleServerRequest); 12 | 13 | runtime.runAsyncCallback(genListenFun(runtime, server, portno)); 14 | 15 | while (true) { 16 | var result = runtime.runAsyncCallback(genReceiveFun(runtime, server, portno, this)); 17 | 18 | for (var i = 0; i < result.length; ++i) { 19 | var r = result[i]; 20 | outport.send(this.createIPBracket(this.IPTypes.OPEN)); 21 | outport.send(this.createIP(r[0])); 22 | outport.send(this.createIP(r[1])); 23 | outport.send(this.createIPBracket(this.IPTypes.CLOSE)); 24 | } 25 | } 26 | }; 27 | 28 | // TODO move globals into function: 29 | var rx = null; 30 | 31 | var wq = []; 32 | 33 | function handleServerRequest(req, res) { 34 | wq.push([req, res]); 35 | 36 | if (rx !== null) { 37 | var q = wq; 38 | wq = []; 39 | rx(q); 40 | } 41 | } 42 | 43 | function genListenFun(runtime, server, portno) { 44 | return function (done) { 45 | // In next tick (TODO use process.nextTick() instead): 46 | setTimeout(function () { 47 | done(); 48 | }, 0); 49 | 50 | server.listen(portno, function () { 51 | console.log('server listen cb'); 52 | }); 53 | }; 54 | } 55 | 56 | function genReceiveFun() { 57 | return function (done) { 58 | rx = function (q) { 59 | done(q); 60 | } 61 | }; 62 | } 63 | -------------------------------------------------------------------------------- /components/lbal.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Utils = require('../core/Utils'); 4 | 5 | module.exports = function lbal() { 6 | var inport = this.openInputPort('IN'); 7 | var array = this.openOutputPortArray('OUT'); 8 | var sel = -1; 9 | var substream_level = 0; 10 | while (true) { 11 | var ip = inport.receive(); 12 | if (ip === null) { 13 | break; 14 | } 15 | 16 | if (substream_level == 0) { 17 | sel = Utils.getElementWithSmallestBacklog(array, sel); 18 | } 19 | if (ip.type == this.IPTypes.OPEN) 20 | substream_level++; 21 | else if (ip.type == this.IPTypes.CLOSE) 22 | substream_level--; 23 | 24 | array[sel].send(ip); 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /components/passthru.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function passthru() { 4 | var inport = this.openInputPort('IN'); 5 | var outport = this.openOutputPort('OUT'); 6 | while (true) { 7 | var ip = inport.receive(); 8 | if (ip === null) { 9 | break; 10 | } 11 | outport.send(ip); 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /components/randdelay.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function randdelay(runtime) { 4 | var inport = this.openInputPort('IN'); 5 | var intvlport = this.openInputPort('INTVL'); 6 | var outport = this.openOutputPort('OUT'); 7 | var intvl_ip = intvlport.receive(); 8 | var intvl = intvl_ip.contents; 9 | this.dropIP(intvl_ip); 10 | 11 | while (true) { 12 | var ip = inport.receive(); 13 | if (ip === null) { 14 | break; 15 | } 16 | runtime.runAsyncCallback(genSleepFun(this, Math.random() * intvl)); 17 | outport.send(ip); 18 | } 19 | }; 20 | 21 | function genSleepFun(proc, ms) { 22 | return function (done) { 23 | //console.log(proc.name + ' start sleep: ' + Math.round(ms * 100) / 100 + ' msecs'); 24 | 25 | setTimeout(function () { 26 | done(); 27 | }, ms); 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /components/reader.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | 5 | // Reader based on Bruno Jouhier's code 6 | module.exports = function reader(runtime) { 7 | var inport = this.openInputPort('FILE'); 8 | var ip = inport.receive(); 9 | var fname = ip.contents; 10 | this.dropIP(ip); 11 | 12 | var result = runtime.runAsyncCallback(myReadFile(fname, "utf8", this)); 13 | 14 | if (result[0] == undefined) { 15 | console.log(result[1]); 16 | return; 17 | } 18 | 19 | var outport = this.openOutputPort('OUT'); 20 | var array = result[0].split('\n'); 21 | array.forEach(function(item){ 22 | var ip = this.createIP(item); 23 | outport.send(ip); 24 | }.bind(this)); 25 | 26 | }; 27 | 28 | function myReadFile(path, options) { 29 | return function (done) { 30 | fs.readFile(path, options, function (err, data) { 31 | done([data, err]); 32 | }); 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /components/recvr.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function recvr() { 4 | var inport = this.openInputPort('IN'); 5 | while (true) { 6 | var ip = inport.receive(); 7 | if (ip === null) { 8 | break; 9 | } 10 | if (ip.type == this.IPTypes.OPEN) 11 | console.log('open'); 12 | else if (ip.type == this.IPTypes.CLOSE) 13 | console.log('close'); 14 | else 15 | console.log('data: ' + ip.contents); 16 | this.dropIP(ip); 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /components/repl.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function repl() { 4 | var inport = this.openInputPort('IN'); 5 | var array = this.openOutputPortArray('OUT'); 6 | 7 | while (true) { 8 | var ip = inport.receive(); 9 | if (ip === null) { 10 | break; 11 | } 12 | for (var i = 0; i < array.length; i++) { 13 | if(ip.type === this.IPTypes.NORMAL) { 14 | array[i].send(this.createIP(ip.contents)); 15 | } else { 16 | array[i].send(this.createIPBracket(ip.type, ip.contents)); 17 | } 18 | } 19 | this.dropIP(ip); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /components/reverse.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function reverse() { 4 | var inport = this.openInputPort('IN'); 5 | var outport = this.openOutputPort('OUT'); 6 | while (true) { 7 | var ip = inport.receive(); 8 | if (ip === null) { 9 | break; 10 | } 11 | var s = ip.contents; 12 | outport.send(this.createIP(reverseString(s))); 13 | this.dropIP(ip); 14 | } 15 | }; 16 | 17 | // Thanks to Scott Gartner 18 | // Link to answer on StackOverflow: http://stackoverflow.com/a/17374133 19 | // Link to user profile page of Scott Gartner: http://stackoverflow.com/users/324657/scott-gartner 20 | // License: CC BY-SA 3.0 with attribution required: http://creativecommons.org/licenses/by-sa/3.0/ 21 | function reverseString(str) { 22 | var charArray = []; 23 | for (var i = 0; i < str.length; i++) { 24 | if (i + 1 < str.length) { 25 | var value = str.charCodeAt(i); 26 | var nextValue = str.charCodeAt(i + 1); 27 | if ((value >= 0xD800 && value <= 0xDBFF 28 | && (nextValue & 0xFC00) == 0xDC00) // Surrogate pair 29 | || (nextValue >= 0x0300 && nextValue <= 0x036F)) // Combining marks 30 | { 31 | charArray.unshift(str.substring(i, i + 2)); 32 | i++; // Skip the other half 33 | continue; 34 | } 35 | } 36 | 37 | // Otherwise we just have a rogue surrogate marker or a plain old character. 38 | charArray.unshift(str[i]); 39 | } 40 | 41 | return charArray.join(''); 42 | } 43 | -------------------------------------------------------------------------------- /components/rrmerge.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function rrmerge() { 4 | var array = this.openInputPortArray('IN'); 5 | var outport = this.openOutputPort('OUT'); 6 | var ip = null; 7 | while (true) { 8 | for (var i = 0; i < array.length; i++) { 9 | ip = array[i].receive(); 10 | if (ip !== null) { 11 | outport.send(ip); 12 | } 13 | } 14 | if (ip === null) { 15 | break; 16 | } 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /components/substreamsensitivemerge.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // substreamsensitivemerge.js 4 | 5 | var Utils = require('../core/Utils'); 6 | 7 | module.exports = function substreamsensitivemerge() { 8 | 9 | var inportArray = this.openInputPortArray('IN'); 10 | var outport = this.openOutputPort('OUT'); 11 | var substream_level = 0; 12 | 13 | var ip; 14 | var elemno = -1; 15 | 16 | while (true) { 17 | if (substream_level != 0) { 18 | ip = inportArray[elemno].receive(); 19 | if (ip == null) 20 | break; 21 | } else { 22 | while (true) { 23 | elemno = Utils.findInputPortElementWithData(inportArray); 24 | // console.log("Merge elemno:" + elemno); 25 | if (elemno == -1) // all elements drained 26 | return; 27 | ip = inportArray[elemno].receive(); 28 | if (ip != null) 29 | break; 30 | } 31 | } 32 | 33 | if (ip.type == this.IPTypes.OPEN) 34 | substream_level++; 35 | else if (ip.type == this.IPTypes.CLOSE) 36 | substream_level--; 37 | 38 | outport.send(ip); 39 | } 40 | }; 41 | -------------------------------------------------------------------------------- /components/writer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | 5 | module.exports = function writer(runtime) { 6 | var inport = this.openInputPort('FILE'); 7 | var dataport = this.openInputPort('IN'); 8 | var ip = inport.receive(); 9 | var fname = ip.contents; 10 | this.dropIP(ip); 11 | var string = ''; 12 | while (true) { 13 | ip = dataport.receive(); 14 | if (ip === null) { 15 | break; 16 | } 17 | string += ip.contents + '\n'; 18 | this.dropIP(ip); 19 | } 20 | 21 | var result = runtime.runAsyncCallback(myWriteFile(fname, string, "utf8", this)); 22 | console.log('write complete: ' + this.name); 23 | if (result != null) { 24 | console.log(result); 25 | } 26 | }; 27 | 28 | function myWriteFile(path, data, options, proc) { 29 | return function (done) { 30 | console.log('write started: ' + proc.name); 31 | fs.writeFile(path, data, options, function (err) { 32 | done(err); 33 | }); 34 | console.log('write pending: ' + proc.name); 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /components/wsrecv.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var WebSocketServer = require('ws').Server; 4 | 5 | module.exports = function wsrecv(runtime) { 6 | var inport = this.openInputPort('PORTNO'); 7 | var outport = this.openOutputPort('OUT'); 8 | var wssout = this.openOutputPort('WSSOUT'); 9 | 10 | var ip = inport.receive(); 11 | var portno = ip.contents; 12 | var wss = new WebSocketServer({port: portno}); 13 | wssout.send(this.createIP(wss)); 14 | 15 | var ws = null; 16 | while (true) { 17 | var result = runtime.runAsyncCallback(genWsReceiveFun(runtime, wss, ws, this)); 18 | console.log('wsrecv callback complete: ' + this.name); 19 | //console.log(result); 20 | if (result[1].endsWith('@kill')) { 21 | break; 22 | } 23 | 24 | console.log(result); 25 | outport.send(this.createIPBracket(this.IPTypes.OPEN)); 26 | outport.send(this.createIP(result[0])); 27 | outport.send(this.createIP(result[1])); 28 | outport.send(this.createIPBracket(this.IPTypes.CLOSE)); 29 | } 30 | 31 | wss.close(); 32 | this.dropIP(ip); 33 | }; 34 | 35 | function genWsReceiveFun(runtime, wss, ws, proc) { 36 | return function (done) { 37 | wss.on('connection', function connection(ws) { 38 | ws.on('message', function incoming(message) { 39 | console.log('running callback for: ' + proc.name); 40 | done([ws, message]); 41 | }); 42 | ws.send('connected!'); 43 | }); 44 | console.log('wsrecv pending: ' + proc.name); 45 | }; 46 | } 47 | 48 | String.prototype.endsWith = function (s) { 49 | return this.length >= s.length && this.substr(this.length - s.length) == s; 50 | }; 51 | -------------------------------------------------------------------------------- /components/wsresp.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function wsresp() { 4 | var ip; 5 | var inport = this.openInputPort('IN'); 6 | while (true) { 7 | ip = inport.receive(); // shd be open bracket 8 | if (ip === null) { 9 | break; 10 | } 11 | //console.log(ip); 12 | this.dropIP(ip); 13 | ip = inport.receive(); // shd be connection 14 | //console.log(ip); 15 | var ws = ip.contents; 16 | this.dropIP(ip); 17 | while (true) { 18 | ip = inport.receive(); 19 | //console.log(ip); 20 | if (ip.type == this.IPTypes.CLOSE) { 21 | this.dropIP(ip); 22 | break; 23 | } 24 | var msg = ip.contents; 25 | this.dropIP(ip); 26 | ws.send(msg); 27 | } 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /core/Enum.js: -------------------------------------------------------------------------------- 1 | module.exports = function (constants) { 2 | var _map = {}; 3 | var enumTable = { 4 | __lookup: function (constantValue) { 5 | return _map[constantValue] || null; 6 | } 7 | }; 8 | 9 | var counter = 1; 10 | constants.forEach(function (name) { 11 | if (name === '__lookup') { 12 | throw 'You must not specify a enum constant named "__lookup"! This name is reserved for the lookup function.'; 13 | } 14 | enumTable[name] = counter; 15 | _map[counter] = name; 16 | counter++; 17 | }); 18 | 19 | return Object.freeze(enumTable); 20 | }; 21 | -------------------------------------------------------------------------------- /core/IIPConnection.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (data) { 4 | this.contents = data; 5 | this.closed = false; 6 | }; 7 | -------------------------------------------------------------------------------- /core/IP.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Enum = require('./Enum'); 4 | 5 | var IP = module.exports = function IP(contents) { 6 | this.owner = null; 7 | this.type = IP.Types.NORMAL; 8 | this.contents = contents; 9 | }; 10 | 11 | IP.Types = new Enum([ 12 | "NORMAL", 13 | "OPEN", 14 | "CLOSE" 15 | ]); 16 | 17 | ["NORMAL", "OPEN", "CLOSE"].forEach(function(type) { 18 | Object.defineProperty(IP, type, { 19 | get: function() { 20 | console.error("Accessing IP types from IP object directly is deprecated. Please use IP.Types." + type); 21 | return IP.Types[type] 22 | } 23 | }); 24 | }); 25 | 26 | -------------------------------------------------------------------------------- /core/InputPort.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Fiber = require('fibers') 4 | , IIPConnection = require('./IIPConnection') 5 | , ProcessStatus = require('./Process').Status 6 | , trace = require('./trace'); 7 | 8 | var InputPort = module.exports = function () { 9 | this.name = null; 10 | this.conn = null; // either ProcessConnection or IIPConnection 11 | //this.closed = false; 12 | }; 13 | 14 | InputPort.prototype.setRuntime = function (runtime) { 15 | this._runtime = runtime; 16 | }; 17 | 18 | InputPort.prototype.receive = function () { 19 | var proc = Fiber.current.fbpProc; 20 | var conn = this.conn; 21 | 22 | if (conn instanceof IIPConnection) { 23 | if(conn.closed) { 24 | trace('tried to read from closed IIPConnection to ' + this.name); 25 | return null; 26 | } 27 | trace('recv IIP from port ' + this.name + ': ' + conn.contents); 28 | //var ip = new exports.IP(conn + ''); 29 | var ip = proc.createIP(conn.contents); 30 | conn.closed = true; 31 | ip.user = proc; 32 | //console.log(conn); 33 | return ip; 34 | } 35 | 36 | trace('Requesting IP from ' + this.name); 37 | 38 | while (true) { 39 | if (conn.usedslots == 0) { 40 | if (conn.closed) { 41 | trace('recv EOS from ' + this.name); 42 | return null; 43 | } 44 | proc.yield(ProcessStatus.WAITING_TO_RECEIVE); 45 | } 46 | else 47 | break; 48 | } 49 | //if (conn.usedslots == conn.array.length) 50 | for (var i = 0; i < conn.up.length; i++) { 51 | if (conn.up[i].status == ProcessStatus.WAITING_TO_SEND) { 52 | conn.up[i].status = ProcessStatus.READY_TO_EXECUTE; 53 | this._runtime.pushToQueue(conn.up[i]); 54 | } 55 | } 56 | 57 | ip = conn.array[conn.nxtget]; 58 | conn.array[conn.nxtget] = null; 59 | conn.nxtget++; 60 | if (conn.nxtget > conn.array.length - 1) 61 | conn.nxtget = 0; 62 | var cont = ip.contents; 63 | trace('Received: ' + proc.IPTypes.__lookup(ip.type) + (cont !== null) ? ", " + cont : ""); 64 | conn.usedslots--; 65 | ip.owner = proc; 66 | proc.ownedIPs++; 67 | return ip; 68 | }; 69 | 70 | InputPort.prototype.close = function () { 71 | var proc = Fiber.current.fbpProc; 72 | var conn = this.conn; 73 | conn.closed = true; 74 | console.log(proc.name + ': ' + conn.usedslots + ' IPs dropped because of close on ' + conn.name); 75 | while (true) { 76 | conn.array[conn.nxtget] = null; 77 | conn.nxtget++; 78 | if (conn.nxtget > conn.array.length - 1) 79 | conn.nxtget = 0; 80 | conn.usedslots--; 81 | if (conn.usedslots <= 0) 82 | break; 83 | } 84 | for (var i = 0; i < conn.up.length; i++) { 85 | if (conn.up[i].status == ProcessStatus.WAITING_TO_SEND) 86 | this._runtime.pushToQueue(conn.up[i]); 87 | } 88 | }; 89 | -------------------------------------------------------------------------------- /core/Network.js: -------------------------------------------------------------------------------- 1 | var IIPConnection = require('./IIPConnection') 2 | , InputPort = require('./InputPort') 3 | , OutputPort = require('./OutputPort') 4 | , path = require('path') 5 | , Process = require('./Process') 6 | , ProcessConnection = require('./ProcessConnection') 7 | , parseFBP = require('parsefbp') 8 | , trace = require('./trace') 9 | , _ = require('lodash'); 10 | 11 | var Network = module.exports = function (options) { 12 | this._processes = {}; 13 | if(options) { 14 | this.componentRoot = options.componentRoot; 15 | } 16 | }; 17 | 18 | /* 19 | * This function provides support for loading 20 | * - components that come _with_ this module -> './components/copier.js' 21 | * - components that are inside a package -> 'package/component' 22 | * - components that are simply a node module -> 'component' 23 | * - components that are local to the application trying to load them 24 | */ 25 | function loadComponent(componentName, localRoot) { 26 | var moduleLocation = componentName; 27 | var componentField; 28 | if (componentName.match('^[.]{1,2}/')) { 29 | moduleLocation = path.resolve(path.join(__dirname, '..', componentName)); 30 | } else if (componentName.indexOf('/') >= 0) { 31 | moduleLocation = componentName.slice(0, componentName.indexOf('/')); 32 | componentField = componentName.slice(componentName.indexOf('/') + 1); 33 | if (moduleLocation === 'jsfbp') { 34 | moduleLocation = path.resolve(path.join(__dirname, '..', 'components', componentField + '.js')); 35 | componentField = undefined; 36 | } else if (moduleLocation === '') { 37 | moduleLocation = path.join(localRoot, componentField); 38 | componentField = undefined; 39 | } 40 | } 41 | trace("Trying to load: " +require.resolve(moduleLocation)); 42 | var component = require(moduleLocation); 43 | 44 | if (componentField) { 45 | return component[componentField] 46 | } else { 47 | return component; 48 | } 49 | } 50 | 51 | function getPort(connectionEnd) { 52 | var port = connectionEnd.port; 53 | if ('index' in connectionEnd) { 54 | port += '[' + connectionEnd.index + ']'; 55 | } 56 | return port; 57 | } 58 | 59 | Network.createFromGraph = function (graphString, localRoot) { 60 | var graphDefinition = parseFBP(graphString, {caseSensitive: true}); 61 | 62 | var network = new Network({componentRoot: localRoot}); 63 | var processes = {}; 64 | 65 | Object.keys(graphDefinition.processes).forEach(function (processName) { 66 | var processDefinition = graphDefinition.processes[processName]; 67 | processes[processName] = network.defProc(processDefinition.component, processName); 68 | }); 69 | 70 | graphDefinition.connections.forEach(function (connection) { 71 | var target = connection.tgt; 72 | if ('data' in connection) { 73 | network.initialize(processes[target.process], getPort(target), connection.data); 74 | } else { 75 | var source = connection.src; 76 | network.connect(processes[source.process], getPort(source), processes[target.process], getPort(target)); 77 | } 78 | 79 | }); 80 | return network; 81 | }; 82 | 83 | Network.prototype.getProcessByName = function (processName) { 84 | return this._processes[processName]; 85 | }; 86 | 87 | Network.prototype.run = function (runtime, options, callback) { 88 | options = options || {}; 89 | 90 | _.forEach(this._processes, function (process) { 91 | _.invokeMap(process.inports, 'setRuntime', runtime); 92 | _.invokeMap(process.outports, 'setRuntime', runtime); 93 | }); 94 | runtime.run(_.values(this._processes), options, callback || function () {}); 95 | }; 96 | 97 | Network.prototype.defProc = function (func, name) { 98 | if (typeof func === "string") { 99 | func = loadComponent(func, this.componentRoot || ''); 100 | } 101 | if (!func) { 102 | throw new Error("No function passed to defProc: " + name); 103 | } 104 | if (!name) { 105 | name = func.name; 106 | if(!name) { 107 | throw new Error("No name passed to defProc:" + func); 108 | } 109 | } 110 | 111 | 112 | if (this._processes[name]) { 113 | throw new Error("Duplicate name specified in defProc:" + func); 114 | } 115 | 116 | var proc = new Process(name, func); 117 | 118 | proc.trace('defined'); 119 | 120 | this._processes[name] = proc; 121 | return proc; 122 | }; 123 | 124 | Network.prototype.initialize = function (proc, port, string) { 125 | var inport = new InputPort(); 126 | inport.name = proc.name + "." + port; 127 | inport.conn = new IIPConnection(string); 128 | proc.inports[port] = inport; 129 | }; 130 | 131 | Network.prototype.connect = function (upproc, upport, downproc, downport, capacity) { 132 | if (capacity == undefined) { 133 | capacity = 10; 134 | } 135 | var outport = upproc.outports[upport]; 136 | if (outport) { 137 | console.log('Cannot connect one output port (' + outport.name + ') to multiple input ports'); 138 | return; 139 | } 140 | 141 | 142 | outport = new OutputPort(); 143 | outport.name = upproc.name + "." + upport; 144 | 145 | var inport = downproc.inports[downport]; 146 | 147 | if (inport == null) { 148 | inport = new InputPort(); 149 | inport.name = downproc.name + "." + downport; 150 | 151 | var cnxt = new ProcessConnection(capacity); 152 | cnxt.name = downproc.name + "." + downport; 153 | inport.conn = cnxt; 154 | } else { 155 | cnxt = inport.conn; 156 | } 157 | 158 | outport.conn = cnxt; 159 | 160 | upproc.outports[upport] = outport; 161 | downproc.inports[downport] = inport; 162 | 163 | cnxt.up[cnxt.up.length] = upproc; 164 | cnxt.down = downproc; 165 | cnxt.upstreamProcsUnclosed++; 166 | }; 167 | 168 | Network.prototype.sinitialize = function (sinport, string) { 169 | var i = sinport.lastIndexOf('.'); 170 | var procname = sinport.substring(0, i); 171 | var port = sinport.substring(i + 1); 172 | var proc = this._processes[procname]; 173 | 174 | this.initialize(proc, port, string); 175 | }; 176 | 177 | Network.prototype.sconnect = function (soutport, sinport, capacity) { 178 | 179 | var i = soutport.lastIndexOf('.'); 180 | var procname = soutport.substring(0, i); 181 | var upport = soutport.substring(i + 1); 182 | var upproc = this._processes[procname]; 183 | i = sinport.lastIndexOf('.'); 184 | procname = sinport.substring(0, i); 185 | var downport = sinport.substring(i + 1); 186 | var downproc = this._processes[procname]; 187 | 188 | this.connect(upproc, upport, downproc, downport, capacity); 189 | }; 190 | -------------------------------------------------------------------------------- /core/OutputPort.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Fiber = require('fibers') 3 | , ProcessStatus = require('./Process').Status 4 | , trace = require('./trace'); 5 | 6 | var OutputPort = module.exports = function () { 7 | this.name = null; 8 | this.conn = null; 9 | this.closed = false; 10 | }; 11 | 12 | OutputPort.prototype.setRuntime = function (runtime) { 13 | this._runtime = runtime; 14 | }; 15 | 16 | OutputPort.prototype.send = function (ip) { 17 | var proc = Fiber.current.fbpProc; 18 | var conn = this.conn; 19 | var cont = ip.contents; 20 | if (ip.type != proc.IPTypes.NORMAL) { 21 | cont = proc.IPTypes.__lookup(ip.type) + ", " + cont; 22 | } 23 | trace('send to ' + this.name + ': ' + cont); 24 | 25 | if (ip.owner != proc) { 26 | console.log(proc.name + ' IP being sent not owned by this Process: ' + cont); 27 | return; 28 | } 29 | if (conn.closed) { 30 | console.log(proc.name + ' sending to closed connection: ' + conn.name); 31 | return -1; 32 | } 33 | while (true) { 34 | if (conn.down.status == ProcessStatus.WAITING_TO_RECEIVE || 35 | conn.down.status == ProcessStatus.NOT_INITIALIZED || 36 | conn.down.status == ProcessStatus.DORMANT || 37 | conn.down.status == ProcessStatus.WAITING_TO_FIPE) { 38 | conn.down.status = ProcessStatus.READY_TO_EXECUTE; 39 | this._runtime.pushToQueue(conn.down); 40 | } 41 | if (conn.usedslots == conn.array.length) { 42 | proc.yield(ProcessStatus.WAITING_TO_SEND, ProcessStatus.WAITING_TO_SEND); 43 | } 44 | else { 45 | break; 46 | } 47 | } 48 | conn.array[conn.nxtput] = ip; 49 | conn.nxtput++; 50 | if (conn.nxtput > conn.array.length - 1) { 51 | conn.nxtput = 0; 52 | } 53 | conn.usedslots++; 54 | proc.ownedIPs--; 55 | trace('send OK: ' + cont); 56 | 57 | return 0; 58 | }; 59 | -------------------------------------------------------------------------------- /core/Process.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | var Fiber = require('fibers') 5 | , Enum = require('./Enum') 6 | , IP = require('./IP') 7 | , trace = require('./trace'); 8 | 9 | var Process = module.exports = function (name, func) { 10 | this.name = name; 11 | this.func = func; 12 | this.fiber = null; 13 | this.inports = {}; 14 | this.outports = {}; 15 | this._status = Process.Status.NOT_INITIALIZED; 16 | this.ownedIPs = 0; 17 | this.cbpending = false; 18 | this.yielded = false; 19 | this.result = null; // [data, err] 20 | 21 | this.trace('Created with status: ' + Process.Status.__lookup(this._status),this.name); 22 | Object.defineProperty(this, 'status', { 23 | get: function() { return this._status; }, 24 | set: function(status) { 25 | this.trace('Transition from ' + Process.Status.__lookup(this._status) + ' to ' + Process.Status.__lookup(status)); 26 | this._status = status; 27 | } 28 | }) 29 | }; 30 | 31 | Process.Status = Enum([ 32 | 'NOT_INITIALIZED', 33 | 'ACTIVE', // (includes waiting on callback ...) 34 | 'WAITING_TO_RECEIVE', 35 | 'WAITING_TO_FIPE', 36 | 'WAITING_TO_SEND', 37 | 'READY_TO_EXECUTE', 38 | 'DORMANT', 39 | 'CLOSED', 40 | 'DONE' 41 | ]); 42 | 43 | Process.prototype.IPTypes = IP.Types; 44 | 45 | Process.prototype.trace = trace; 46 | 47 | /* 48 | * Given a set of ports an a base name XXX, returns all the ports in the set that 49 | * have the name XXX[] 50 | */ 51 | function getPortArray(ports, name) { 52 | var re = new RegExp(name + '\\[\\d+\\]'); 53 | 54 | return Object.keys(ports) 55 | .filter(function(portName) { 56 | return re.test(portName); 57 | }) 58 | .sort() 59 | .map(function(portName) { 60 | return ports[portName]; 61 | }); 62 | } 63 | 64 | 65 | Process.prototype.getStatusString = function () { 66 | return Process.Status.__lookup(this.status); 67 | }; 68 | 69 | Process.prototype.createIP = function (data) { 70 | var ip = new IP(data); 71 | this.ownedIPs++; 72 | ip.owner = this; 73 | this.trace("Normal IP created: " + ip.contents); 74 | return ip; 75 | }; 76 | 77 | Process.prototype.createIPBracket = function (bktType, x) { 78 | if (x == undefined) { 79 | x = null; 80 | } 81 | var ip = new IP(x); 82 | ip.type = bktType; 83 | this.ownedIPs++; 84 | ip.owner = this; 85 | this.trace("Bracket IP created: " + this.IPTypes.__lookup(ip.type) + ", " + ip.contents); 86 | 87 | return ip; 88 | }; 89 | 90 | Process.prototype.dropIP = function (ip) { 91 | var cont = ip.contents; 92 | if (ip.type != this.IPTypes.NORMAL) { 93 | cont = this.IPTypes.__lookup(ip.type) + ", " + cont; 94 | } 95 | this.trace('IP dropped with: ' + cont); 96 | 97 | if (ip.owner != this) { 98 | console.log(this.name + ' IP being dropped not owned by this Process: ' + cont); 99 | return; 100 | } 101 | this.ownedIPs--; 102 | ip.owner = null; 103 | }; 104 | 105 | Process.prototype.openInputPort = function (name) { 106 | var port = this.inports[name]; 107 | if(port) { 108 | return port; 109 | } else { 110 | console.log('Port ' + this.name + '.' + name + ' not found'); 111 | return null; 112 | } 113 | }; 114 | 115 | Process.prototype.openInputPortArray = function (name) { 116 | var array = getPortArray(this.inports, name); 117 | 118 | if (array.length === 0) { 119 | console.log('Port ' + this.name + '.' + name + ' not found'); 120 | return null; 121 | } 122 | 123 | return array; 124 | }; 125 | 126 | Process.prototype.openOutputPort = function (name, opt) { 127 | var port = this.outports[name]; 128 | if(port) { 129 | return port; 130 | } else { 131 | if (opt != 'OPTIONAL') { 132 | console.log('Port ' + this.name + '.' + name + ' not found'); 133 | } 134 | return null; 135 | } 136 | }; 137 | 138 | Process.prototype.openOutputPortArray = function (name) { 139 | var array = getPortArray(this.outports, name); 140 | 141 | if (array.length === 0) { 142 | console.log('Port ' + this.name + '.' + name + ' not found'); 143 | return null; 144 | } 145 | 146 | return array; 147 | }; 148 | 149 | /** 150 | * Yield the fiber that is running this process 151 | * 152 | * @param preStatus Process status will be set to this before yielding. If not set or set to `null`, the status is not changed 153 | * @param postStatus Process status will be set to this after yielding. If not set, the status will be changed to ACTIVE 154 | */ 155 | Process.prototype.yield = function (preStatus, postStatus) { 156 | if(preStatus !== undefined || preStatus !== null) { 157 | this.status = preStatus; 158 | } 159 | this.yielded = true; 160 | this.trace("Yielding with: " + Process.Status.__lookup(preStatus)); 161 | Fiber.yield(); 162 | if(postStatus !== undefined) { 163 | this.status = postStatus 164 | } else { 165 | this.status = Process.Status.ACTIVE; 166 | } 167 | this.yielded = false; 168 | }; 169 | -------------------------------------------------------------------------------- /core/ProcessConnection.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (size) { 4 | this.name = null; 5 | this.nxtget = 0; 6 | this.nxtput = 0; 7 | this.down = null; // downstream process 8 | this.usedslots = 0; 9 | this.array = []; 10 | this.up = []; // list of upstream processes 11 | this.upstreamProcsUnclosed = 0; 12 | for (var i = 0; i < size; i++) { 13 | this.array[i] = null; 14 | } 15 | this.closed = false; 16 | }; 17 | -------------------------------------------------------------------------------- /core/runtimes/FiberRuntime/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Fiber = require('fibers'), 4 | IIPConnection = require('../../IIPConnection'), 5 | Process = require('../../Process'), 6 | _ = require('lodash'), 7 | trace = require('../../trace'), 8 | Enum = require('../../Enum'); 9 | 10 | Fiber.prototype.fbpProc = null; 11 | 12 | var FiberRuntime = module.exports = function () { 13 | this._queue = []; 14 | this._count = null; 15 | }; 16 | 17 | 18 | // TOOD Better description of parameter and maybe function name as well 19 | FiberRuntime.prototype.pushToQueue = function (item) { 20 | this._queue.push(item); 21 | }; 22 | 23 | FiberRuntime.prototype._close = function (proc) { 24 | proc.trace('closing'); 25 | proc.status = Process.Status.CLOSED; 26 | // console.log('cl' + count); 27 | this._count--; 28 | _.forEach(proc.outports, function (outPort) { 29 | var conn = outPort.conn; 30 | if (conn.down.status == Process.Status.WAITING_TO_RECEIVE 31 | || conn.down.status == Process.Status.NOT_INITIALIZED) { 32 | conn.down.status = Process.Status.READY_TO_EXECUTE; 33 | this._queue.push(conn.down); 34 | } 35 | conn.upstreamProcsUnclosed--; 36 | if ((conn.upstreamProcsUnclosed) <= 0) { 37 | conn.closed = true; 38 | } 39 | }.bind(this)); 40 | 41 | _.forEach(proc.inports, function (inport) { 42 | var conn = inport.conn; 43 | if (conn instanceof IIPConnection) { 44 | return; 45 | } 46 | conn.up.forEach(function (up) { 47 | if (up.status == Process.Status.CLOSED) { 48 | up.status = Process.Status.DONE; 49 | this._queue.push(up); 50 | } 51 | }.bind(this)); 52 | }.bind(this)); 53 | 54 | if (proc.ownedIPs != 0) { 55 | console.log(proc.name + ' closed without disposing of all IPs'); 56 | } 57 | proc.trace('closed'); 58 | }; 59 | 60 | FiberRuntime.prototype.getCurrentProc = function () { 61 | return Fiber.current.fbpProc; 62 | }; 63 | 64 | FiberRuntime.prototype.queueCallback = function (proc, result) { 65 | proc.trace('queueCallback'); 66 | if (result != undefined) { 67 | proc.result = result; 68 | } 69 | this._queue.push(proc); 70 | }; 71 | 72 | FiberRuntime.prototype.runAsyncCallback = function (cb) { 73 | var proc = this.getCurrentProc(); 74 | proc.yielded = true; 75 | proc.cbpending = true; 76 | 77 | var self = this; 78 | 79 | cb(function (result) { 80 | proc.yielded = false; 81 | proc.cbpending = false; 82 | proc.result = result; 83 | self.queueCallback(proc); 84 | }); 85 | 86 | return Fiber.yield(); 87 | }; 88 | 89 | FiberRuntime.prototype.run = function (processes, options, callback) { 90 | this._processList = _.keyBy(processes, 'name'); 91 | this._count = _.size(this._processList); 92 | 93 | global.trace = global.trace || Boolean(options.trace); 94 | 95 | var self = this; 96 | 97 | Fiber(function () { 98 | var startTime = new Date(); 99 | var time = startTime.toLocaleString(); 100 | console.log('Start time: ' + time); 101 | 102 | self._actualRun.call(self); 103 | 104 | console.log('Elapsed time in millisecs: ' + (Date.now() - startTime)); 105 | 106 | callback(null); 107 | }).run(); 108 | }; 109 | 110 | FiberRuntime.prototype._createFiber = function (process) { 111 | trace('creating new fiber for ' + process.name); 112 | 113 | process.fiber = new Fiber(process.func.bind(process, this)); 114 | process.fiber.fbpProc = process; 115 | process.status = Process.Status.ACTIVE; 116 | 117 | return process; 118 | }; 119 | 120 | FiberRuntime.prototype._hasDeadLock = function () { 121 | // We have a deadlock if no processes in the list are ACTIVE or have a callback pending 122 | return !_.some(this._processList, function (process) { 123 | return process.cbpending || process.status == Process.Status.ACTIVE 124 | }); 125 | }; 126 | 127 | FiberRuntime.prototype._genInitialQueue = function () { 128 | var self = this; 129 | var queue = []; 130 | 131 | // A process is selfstarting if its incoming ports are only connected to IIPs 132 | _.forEach(self._processList, function (process) { 133 | var selfstarting = true; 134 | _.forEach(process.inports, function (inport) { 135 | selfstarting = selfstarting && (inport.conn instanceof IIPConnection); 136 | }); 137 | 138 | if (selfstarting) { 139 | queue.push(process); 140 | } 141 | }); 142 | 143 | return queue; 144 | }; 145 | 146 | 147 | var ProcState = Enum([ 148 | "UPSTREAM_CLOSED", 149 | "NO_DATA", 150 | "DATA_AVAILABLE" 151 | ]); 152 | FiberRuntime.prototype._procState = function (proc) { 153 | var allDrained = true; 154 | var hasData = false; 155 | 156 | _.forEach(proc.inports, function(port) { 157 | var connection = port.conn; 158 | if (connection instanceof IIPConnection) { 159 | return; 160 | } 161 | 162 | allDrained = allDrained && connection.usedslots == 0 && connection.closed; 163 | hasData = hasData || connection.usedslots > 0; 164 | }); 165 | 166 | return allDrained ? ProcState.UPSTREAM_CLOSED 167 | : !hasData ? ProcState.NO_DATA 168 | : ProcState.DATA_AVAILABLE; 169 | }; 170 | 171 | // Fibre running scheduler 172 | FiberRuntime.prototype._actualRun = function () { 173 | this._queue = this._genInitialQueue(); 174 | var runtime = this; 175 | 176 | _.forEach(this._processList, function (process) { 177 | _.invokeMap(process.inports, 'setRuntime', runtime); 178 | _.invokeMap(process.outports, 'setRuntime', runtime); 179 | }); 180 | 181 | while (true) { 182 | this._tick(); 183 | 184 | if (this._count <= 0) { 185 | break; 186 | } 187 | 188 | if (this._hasDeadLock()) { 189 | console.log('Deadlock detected'); 190 | _.forEach(this._processList, function (process) { 191 | console.log('- Process status: ' + process.getStatusString() + ' - ' + process.name); 192 | }); 193 | throw 'DEADLOCK'; 194 | } 195 | sleep(50); 196 | } 197 | }; 198 | 199 | FiberRuntime.prototype._showQueueState = function (x) { 200 | var queue = this._queue; 201 | trace("Yield/return: state of future events queue: "); 202 | trace("--- This Process"); 203 | trace("- " + x.name + " - status: " + x.getStatusString()); 204 | trace("--- Queue"); 205 | _.forEach(queue, function(process) { 206 | trace("- " + process.name + " - status: " + process.getStatusString()); 207 | }); 208 | if(_.size(this._processList) > _.size(queue)) { 209 | trace("--- Other Processes"); 210 | _.forEach(_.difference(_.values(this._processList), queue.concat(x)), function (process) { 211 | trace("- " + process.name + " - status: " + process.getStatusString()); 212 | }); 213 | } 214 | trace("--- "); 215 | }; 216 | 217 | FiberRuntime.prototype._tick = function () { 218 | 219 | var x = this._queue.shift(); 220 | 221 | while (x != undefined) { 222 | 223 | if (x.status != Process.Status.DONE) { 224 | if (x.fiber == null) { 225 | x = this._createFiber(x); 226 | } else { 227 | x.status = Process.Status.ACTIVE; 228 | } 229 | 230 | this._showQueueState(x); 231 | 232 | while (true) { 233 | var procState = this._procState(x); 234 | if (x.status == Process.Status.DORMANT && ProcState.UPSTREAM_CLOSED == procState) { 235 | this._close(x); 236 | break; 237 | } else if (x.status != Process.Status.CLOSED) { 238 | if (!x.cbpending) { 239 | x.status = Process.Status.ACTIVE; 240 | 241 | x.trace('Start process run'); 242 | // -------------------------- 243 | x.fiber.run(x.result); 244 | // --------------------------- 245 | x.trace('End process run'); 246 | 247 | } 248 | procState = this._procState(x); 249 | x.data = null; 250 | 251 | if (x.yielded) { 252 | break; 253 | } else if (!x.cbpending) { 254 | 255 | if (ProcState.UPSTREAM_CLOSED == procState) { 256 | this._close(x); 257 | break; 258 | } else if (ProcState.NO_DATA == procState) { 259 | x.status = Process.Status.DORMANT; 260 | _.forEach(x.inports, function (port) { 261 | if (port.conn instanceof IIPConnection) { 262 | port.conn.closed = false; 263 | } 264 | }); 265 | break; 266 | } 267 | } 268 | } 269 | } 270 | } 271 | x = this._queue.shift(); 272 | } 273 | }; 274 | 275 | function sleep(ms) { 276 | var fiber = Fiber.current; 277 | setTimeout(function () { 278 | fiber.run(); 279 | }, ms); 280 | Fiber.yield(); 281 | } 282 | -------------------------------------------------------------------------------- /core/trace.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by danrumney on 5/27/16. 3 | */ 4 | 5 | var Fiber = require('fibers'); 6 | 7 | module.exports = function(message) { 8 | if(global.trace) { 9 | var calledAs = ""; 10 | if(this && this.name) { 11 | calledAs = this.name; 12 | } 13 | var fiberProc = "no-fiber"; 14 | if(Fiber.current) { 15 | if(Fiber.current.fbpProc) { 16 | fiberProc = Fiber.current.fbpProc.name; 17 | } else { 18 | fiberProc = "runtime"; 19 | } 20 | } 21 | 22 | var tag = "[" + fiberProc + "->" + calledAs + "]"; 23 | if(fiberProc === calledAs) { 24 | tag = "[" + calledAs + "]"; 25 | } else if (!calledAs) { 26 | tag = "[" + fiberProc + "]"; 27 | } 28 | 29 | console.log(tag + ' ' + message); 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /core/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Fiber = require('fibers'), 4 | ProcessStatus = require('./Process').Status, 5 | trace = require('./trace'); 6 | 7 | function getInportWithData(inportArray) { 8 | var allDrained = true; 9 | 10 | var inportElementWithData = inportArray.findIndex(function (inport) { 11 | var conn = inport ? inport.conn : false; 12 | if (!conn) { 13 | return false; 14 | } 15 | else if (conn.usedslots > 0) { // connection has data 16 | return true; 17 | } 18 | 19 | allDrained = allDrained && conn.closed; // no data but not all closed, so suspend 20 | return false; 21 | }); 22 | 23 | if (inportElementWithData >= 0) { 24 | trace('findIPE_with_data - found: ' + inportElementWithData); 25 | } 26 | else if(allDrained) { 27 | trace('findIPE_with_data: all drained'); 28 | } else { 29 | inportElementWithData = null; 30 | } 31 | 32 | return { 33 | inportElementWithData: inportElementWithData, 34 | allDrained: allDrained 35 | }; 36 | } 37 | 38 | module.exports.getElementWithSmallestBacklog = function (array, elem) { 39 | var number = Number.MAX_VALUE; 40 | var element = elem; 41 | if (element == -1) { 42 | element = 0; 43 | } 44 | var j = element; 45 | for (var i = 0; i < array.length; i++) { 46 | if (array[j] == null || array[j] == undefined) 47 | continue; 48 | if (number > array[j].conn.usedslots) { 49 | number = array[j].conn.usedslots; 50 | element = j; 51 | } 52 | j = (j + 1) % array.length; 53 | } 54 | return element; 55 | }; 56 | 57 | module.exports.findInputPortElementWithData = function (array) { 58 | var proc = Fiber.current.fbpProc; 59 | 60 | trace('findIPE_with_data '); 61 | 62 | while (true) { 63 | var inportWitData = getInportWithData(array); 64 | 65 | if(inportWitData.inportElementWithData !== null) { 66 | return inportWitData.inportElementWithData; 67 | } 68 | 69 | proc.yield(ProcessStatus.WAITING_TO_FIPE); 70 | } 71 | }; 72 | -------------------------------------------------------------------------------- /docs/Fbptest11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jpaulm/jsfbp/841bd9f99d897a3e8fa80e435b3a3f62a25b2985/docs/Fbptest11.png -------------------------------------------------------------------------------- /docs/Fbptestws.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jpaulm/jsfbp/841bd9f99d897a3e8fa80e435b3a3f62a25b2985/docs/Fbptestws.png -------------------------------------------------------------------------------- /docs/JSFBP.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jpaulm/jsfbp/841bd9f99d897a3e8fa80e435b3a3f62a25b2985/docs/JSFBP.png -------------------------------------------------------------------------------- /examples/components/checksequencewithinsubstreams.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This is is an ad hoc check program, checking that the IPs within each 3 | * substream are in descending order, and the right number in each substream - 4 | * assuming they were generated by GenSS... 5 | */ 6 | 7 | 'use strict'; 8 | 9 | // checksequencewithinsubstreams.js 10 | 11 | module.exports = function checksequencewithinsubstreams() { 12 | var inport = this.openInputPort('IN'); 13 | var outport = this.openOutputPort('OUT'); // optional 14 | var seq = -2; 15 | var count = 0; 16 | while (true) { 17 | var ip = inport.receive(); 18 | if (ip === null) { 19 | break; 20 | } 21 | if (ip.type == this.IPTypes.OPEN) { 22 | if (seq != -2) { 23 | console.log("Sequence error"); 24 | return; 25 | } 26 | seq = -1; 27 | count = 5; 28 | } 29 | else if (ip.type == this.IPTypes.CLOSE) { 30 | if (seq < 0) { 31 | console.log("Stream out of sequence - case 2"); 32 | return; 33 | } 34 | if (count != 0) { 35 | console.log("Wrong number of IPs in substream"); 36 | return; 37 | } 38 | seq = -2; 39 | } 40 | else { 41 | var s = ip.contents; 42 | var i = s.indexOf("abcd"); 43 | var j = parseInt(s.substring(0, i)); 44 | if (seq == -1) { 45 | //console.log("1st of substream " + j + ": " + s); 46 | seq = j; 47 | } 48 | else { 49 | if (j != seq - 1) { 50 | console.log("Stream out of sequence - case 3"); 51 | return; 52 | } 53 | seq = j; 54 | } 55 | count--; 56 | } 57 | 58 | if (outport != null) 59 | outport.send(ip); 60 | else 61 | this.dropIP(ip); 62 | } 63 | }; 64 | -------------------------------------------------------------------------------- /examples/components/compare.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * This component simply compares 2 files, and outputs an 'OK' to the console if they match exactly; 5 | * otherwise it outputs a 'FAIL' 6 | * 7 | * It is a general component, but it seems appropriate to include it in examples/components 8 | * 9 | * Amusingly, I had forgotten that the input streams have to be drained - otherwise the compare 10 | * process will keep on getting reinvoked indefinitely. The drainInputs logic has now been added. 11 | */ 12 | 13 | module.exports = function compare() { 14 | 15 | var inportArray = this.openInputPortArray('IN'); 16 | 17 | while (true) { 18 | var ip0 = inportArray[0].receive(); 19 | var ip1 = inportArray[1].receive(); 20 | if (ip0 == null && ip1 == null) { 21 | console.log('OK'); 22 | return; 23 | } 24 | 25 | if (ip0 == null || ip1 == null) { 26 | if (ip0 == null) { 27 | console.log(ip1.type + ',' + ip1.contents); 28 | } 29 | if (ip1 == null) { 30 | console.log(ip0.type + ',' + ip0.contents); 31 | } 32 | console.log('FAIL1'); 33 | drainInputs(this); 34 | return; 35 | } 36 | if (ip0.type != ip1.type) { 37 | console.log(ip0.type + ',' + ip0.contents); 38 | console.log(ip1.type + ',' + ip1.contents); 39 | console.log('FAIL2'); 40 | drainInputs(this); 41 | return; 42 | } 43 | if (ip0.type == this.IPTypes.NORMAL && 44 | ip0.contents.trim().localeCompare(ip1.contents.trim()) != 0) { 45 | console.log(ip0.type + ',' + ip0.contents); 46 | console.log(ip1.type + ',' + ip1.contents); 47 | console.log('FAIL3'); 48 | drainInputs(this); 49 | return; 50 | } 51 | this.dropIP(ip0); 52 | this.dropIP(ip1); 53 | } 54 | 55 | function drainInputs(proc) { 56 | if (ip0 != null) { 57 | proc.dropIP(ip0); 58 | } 59 | ip0 = inportArray[0].receive(); 60 | while (ip0 != null) { 61 | proc.dropIP(ip0); 62 | ip0 = inportArray[0].receive(); 63 | } 64 | 65 | if (ip1 != null) { 66 | proc.dropIP(ip1); 67 | } 68 | ip1 = inportArray[1].receive(); 69 | while (ip1 != null) { 70 | proc.dropIP(ip1); 71 | ip1 = inportArray[1].receive(); 72 | } 73 | } 74 | }; 75 | -------------------------------------------------------------------------------- /examples/components/copier_closing.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function copier_closing() { 4 | var inport = this.openInputPort('IN'); 5 | var outport = this.openOutputPort('OUT'); 6 | var count = 0; 7 | while (true) { 8 | var ip = inport.receive(); 9 | if (ip === null) { 10 | break; 11 | } 12 | count++; 13 | if (count === 20) { 14 | inport.close(); 15 | this.dropIP(ip); 16 | return; 17 | } 18 | outport.send(ip); 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /examples/components/copier_nonlooper.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | //var InputPort = require('../core/InputPort') 4 | // , OutputPort = require('../core/OutputPort') 5 | 6 | // same as copier, but written as a non-looper 7 | 8 | // do service calls from internal subroutine 9 | 10 | module.exports = function copier_nonlooper() { 11 | var inport = this.openInputPort('IN'); 12 | var outport = this.openOutputPort('OUT'); 13 | //var ip = inport.receive(); 14 | //var i = ip.contents; 15 | //outport.send(ip); 16 | subrtn(inport, outport); 17 | }; 18 | 19 | function subrtn(inport, outport) { 20 | var ip = inport.receive(); 21 | outport.send(ip); 22 | } 23 | -------------------------------------------------------------------------------- /examples/components/gendata.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function gendata() { 4 | var inport = this.openInputPort('COUNT'); 5 | var outport = this.openOutputPort('OUT'); 6 | var ip = inport.receive(); 7 | var count = ip.contents; 8 | this.dropIP(ip); 9 | //console.log(count); 10 | for (var i = 0; i < count; i++) { 11 | ip = this.createIP(i + 'abcd'); 12 | //console.log(outport); 13 | if (-1 == outport.send(ip)) { 14 | return; 15 | } 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /examples/components/gendatawithbreaks.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * This testing component uses the first byte of each incoming IP to generate open or close 5 | * brackets, or "normal" data IPs; the real data starts at the second byte 6 | * 7 | */ 8 | 9 | module.exports = function gendatawithbreaks() { 10 | var inport = this.openInputPort('IN'); 11 | var outport = this.openOutputPort('OUT'); 12 | while (true) { 13 | var ip = inport.receive(); 14 | if (ip === null) { 15 | break; 16 | } 17 | var c = ip.contents; 18 | this.dropIP(ip); 19 | var type = c.substring(0, 1); 20 | if (type == 'O') 21 | ip = this.createIPBracket(this.IPTypes.OPEN); 22 | else if (type == 'C') 23 | ip = this.createIPBracket(this.IPTypes.CLOSE); 24 | else { 25 | c = c.substring(1); 26 | ip = this.createIP(c); 27 | } 28 | outport.send(ip); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /examples/components/genss.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // generate substreams of length 5 4 | module.exports = function genss() { 5 | var inport = this.openInputPort('COUNT'); 6 | var outport = this.openOutputPort('OUT'); 7 | var ip = inport.receive(); 8 | var count = ip.contents; 9 | this.dropIP(ip); 10 | //console.log(count); 11 | var p = this.createIPBracket(this.IPTypes.OPEN); 12 | outport.send(p); 13 | 14 | for (var i = 0; i < count; i++) { 15 | ip = this.createIP((count - i) + 'abcd'); 16 | if (-1 == outport.send(ip)) { 17 | return; 18 | } 19 | if (i < count - 1) { 20 | if (i % 5 == 5 - 1) { 21 | p = this.createIPBracket(this.IPTypes.CLOSE); 22 | outport.send(p); 23 | 24 | p = this.createIPBracket(this.IPTypes.OPEN); 25 | outport.send(p); 26 | } 27 | } 28 | } 29 | p = this.createIPBracket(this.IPTypes.CLOSE); 30 | outport.send(p); 31 | }; 32 | -------------------------------------------------------------------------------- /examples/components/mockmocksender.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | //var fbp = require('..') 4 | // , InputPort = require('../core/InputPort') 5 | // , IP = require('../core/IP') 6 | // , OutputPort = require('../core/OutputPort'); 7 | 8 | module.exports = function mockmocksender() { 9 | var inport = this.openInputPort('PARMS'); 10 | var outport = this.openOutputPort('OUT'); 11 | var ip = inport.receive(); 12 | var parms = ip.contents; 13 | var parmsarray = parms.split(','); // increment, suffix, count 14 | this.dropIP(ip); 15 | //console.log(count); 16 | var value = 0; 17 | for (var i = 0; i < parmsarray[2]; i++) { 18 | value += Number(parmsarray[0]); 19 | ip = this.createIP(value + ' ' + parmsarray[1]); 20 | if (-1 == outport.send(ip)) { 21 | return; 22 | } 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /examples/data/collate_output: -------------------------------------------------------------------------------- 1 | O 2 | O 3 | O 4 | 111AA11111 M 5 | 111AA11111 D 6 | 111AA11111 D 7 | 111AA11111 D 8 | C 9 | O 10 | 111AA22222 M 11 | 111AA22222 D 12 | 111AA22222 D 13 | C 14 | C 15 | O 16 | O 17 | 111BB44444 M 18 | 111BB44444 D 19 | 111BB44444 D 20 | C 21 | O 22 | 111BB55555 D 23 | C 24 | C 25 | C 26 | O 27 | O 28 | O 29 | 222AA11111 M 30 | C 31 | O 32 | 222AA22222 M 33 | 222AA22222 D 34 | 222AA22222 D 35 | C 36 | C 37 | O 38 | O 39 | 222BB22222 M 40 | 222BB22222 D 41 | 222BB22222 D 42 | C 43 | O 44 | 222BB33333 M 45 | C 46 | O 47 | 222BB44444 M 48 | 222BB44444 D 49 | C 50 | C 51 | O 52 | O 53 | 222CC11111 M 54 | 222CC11111 D 55 | 222CC11111 D 56 | C 57 | C 58 | O 59 | O 60 | 222DD11111 D 61 | C 62 | O 63 | 222DD22222 D 64 | C 65 | C 66 | C -------------------------------------------------------------------------------- /examples/data/dfile: -------------------------------------------------------------------------------- 1 | 111AA11111 D 2 | 111AA11111 D 3 | 111AA11111 D 4 | 111AA22222 D 5 | 111AA22222 D 6 | 111BB44444 D 7 | 111BB44444 D 8 | 111BB55555 D 9 | 222AA22222 D 10 | 222AA22222 D 11 | 222BB22222 D 12 | 222BB22222 D 13 | 222BB44444 D 14 | 222CC11111 D 15 | 222CC11111 D 16 | 222DD11111 D 17 | 222DD22222 D -------------------------------------------------------------------------------- /examples/data/mfile: -------------------------------------------------------------------------------- 1 | 111AA11111 M 2 | 111AA22222 M 3 | 111BB44444 M 4 | 222AA11111 M 5 | 222AA22222 M 6 | 222BB22222 M 7 | 222BB33333 M 8 | 222BB44444 M 9 | 222CC11111 M -------------------------------------------------------------------------------- /examples/data/text.txt: -------------------------------------------------------------------------------- 1 | In computer programming, Flow-Based Programming (FBP) is a programming paradigm that 2 | uses a "data factory" metaphor for designing and building applications. FBP defines 3 | applications as networks of "black box" processes, which exchange data across 4 | predefined connections by message passing, where the connections are specified 5 | externally to the processes. These black box processes can be reconnected endlessly 6 | to form different applications without having to be changed internally. FBP is 7 | thus naturally component-oriented. -------------------------------------------------------------------------------- /examples/data/zzzs.txt: -------------------------------------------------------------------------------- 1 | zzz 2 | zzz 3 | zzz 4 | zzz 5 | zzz 6 | zzz 7 | zzz 8 | zzz 9 | zzz 10 | zzz -------------------------------------------------------------------------------- /examples/fbptest01.js: -------------------------------------------------------------------------------- 1 | var fbp = require('..'); 2 | 3 | // --- define network --- 4 | var network = new fbp.Network(); 5 | 6 | network.defProc('./examples/components/gendata.js', 'Gen'); 7 | network.defProc('./components/copier.js', 'Copy'); 8 | network.defProc('./components/recvr.js', 'Recvr'); 9 | 10 | //network.initialize(gendata, 'COUNT', '2000'); 11 | //network.connect(gendata, 'OUT', copier, 'IN', 5); 12 | //network.connect(copier, 'OUT', recvr, 'IN', 5); 13 | 14 | network.sinitialize('Gen.COUNT', '2000'); 15 | network.sconnect('Gen.OUT', 'Copy.IN', 5); 16 | network.sconnect('Copy.OUT', 'Recvr.IN', 5); 17 | 18 | // --- run --- 19 | var fiberRuntime = new fbp.FiberRuntime(); 20 | network.run(fiberRuntime, { trace: false }); 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /examples/fbptest02.js: -------------------------------------------------------------------------------- 1 | var fbp = require('..') 2 | , path = require('path'); 3 | 4 | // --- define network --- 5 | var network = new fbp.Network(); 6 | 7 | var reader = network.defProc('./components/reader.js', 'Read'); 8 | var copier = network.defProc('./components/copier.js', 'Copy'); 9 | var recvr = network.defProc('./components/recvr.js', 'Recvr'); 10 | 11 | network.initialize(reader, 'FILE', path.resolve(__dirname, 'data/text.txt')); 12 | network.connect(reader, 'OUT', copier, 'IN', 1); 13 | network.connect(copier, 'OUT', recvr, 'IN', 1); 14 | 15 | // --- run --- 16 | var fiberRuntime = new fbp.FiberRuntime(); 17 | network.run(fiberRuntime, {trace: false}); 18 | -------------------------------------------------------------------------------- /examples/fbptest03.js: -------------------------------------------------------------------------------- 1 | var fbp = require('..') 2 | , path = require('path'); 3 | 4 | // --- define network --- 5 | var network = new fbp.Network(); 6 | 7 | var gendata = network.defProc('./examples/components/gendata.js', 'Gen'); 8 | var reader = network.defProc('./components/reader.js', 'Read'); 9 | var copier = network.defProc('./components/copier.js', 'Copy'); 10 | var recvr = network.defProc('./components/recvr.js', 'Recvr'); 11 | 12 | network.initialize(gendata, 'COUNT', '20'); 13 | network.connect(gendata, 'OUT', copier, 'IN', 5); 14 | network.initialize(reader, 'FILE', path.resolve(__dirname, 'data/text.txt')); 15 | network.connect(reader, 'OUT', copier, 'IN', 5); 16 | network.connect(copier, 'OUT', recvr, 'IN', 5); 17 | 18 | // --- run --- 19 | var fiberRuntime = new fbp.FiberRuntime(); 20 | network.run(fiberRuntime, {trace: false}); 21 | -------------------------------------------------------------------------------- /examples/fbptest04.js: -------------------------------------------------------------------------------- 1 | var fbp = require('..'); 2 | 3 | // --- define network --- 4 | var network = new fbp.Network(); 5 | 6 | var gendata = network.defProc('./examples/components/gendata.js', 'Gen'); 7 | var repl = network.defProc('./components/repl.js', 'Repl'); 8 | var recvr = network.defProc('./components/recvr.js', 'Recvr'); 9 | 10 | network.initialize(gendata, 'COUNT', '20'); 11 | network.connect(gendata, 'OUT', repl, 'IN', 5); 12 | network.connect(repl, 'OUT[0]', recvr, 'IN', 5); 13 | network.connect(repl, 'OUT[1]', recvr, 'IN', 5); 14 | network.connect(repl, 'OUT[2]', recvr, 'IN', 5); 15 | 16 | // --- run --- 17 | var fiberRuntime = new fbp.FiberRuntime(); 18 | network.run(fiberRuntime, {trace: false}); 19 | -------------------------------------------------------------------------------- /examples/fbptest05.js: -------------------------------------------------------------------------------- 1 | var fbp = require('..') 2 | , path = require('path'); 3 | 4 | // --- define network --- 5 | var network = new fbp.Network(); 6 | 7 | 8 | var reader = network.defProc('./components/reader.js', "Read"); 9 | var reader2 = network.defProc('./components/reader.js', 'Read2'); 10 | var copier = network.defProc('./components/copier.js', 'Copy'); 11 | var recvr = network.defProc('./components/recvr.js', 'Recvr'); 12 | var rrmerge = network.defProc('./components/rrmerge.js', 'RRMerge'); 13 | 14 | network.initialize(reader, 'FILE', path.resolve(__dirname, 'data/text.txt')); 15 | network.connect(reader, 'OUT', copier, 'IN', 2); 16 | network.initialize(reader2, 'FILE', path.resolve(__dirname, 'data/zzzs.txt')); 17 | network.connect(reader2, 'OUT', rrmerge, 'IN[0]', 2); 18 | network.connect(copier, 'OUT', rrmerge, 'IN[1]', 2); 19 | network.connect(rrmerge, 'OUT', recvr, 'IN', 2); 20 | 21 | // --- run --- 22 | var fiberRuntime = new fbp.FiberRuntime(); 23 | network.run(fiberRuntime, {trace: false}); 24 | -------------------------------------------------------------------------------- /examples/fbptest06.js: -------------------------------------------------------------------------------- 1 | var fbp = require('..'); 2 | 3 | // --- define network --- 4 | var network = new fbp.Network(); 5 | 6 | var gendata = network.defProc('./examples/components/gendata', 'Gen'); 7 | var repl = network.defProc('./components/repl.js', 'Repl'); 8 | var rrmerge = network.defProc('./components/rrmerge', 'RRMerge'); 9 | var recvr = network.defProc('./components/recvr', 'Recvr'); 10 | 11 | network.initialize(gendata, 'COUNT', '20'); 12 | network.connect(gendata, 'OUT', repl, 'IN', 5); 13 | network.connect(repl, 'OUT[0]', rrmerge, 'IN[0]', 5); 14 | network.connect(repl, 'OUT[1]', rrmerge, 'IN[1]', 5); 15 | network.connect(repl, 'OUT[2]', rrmerge, 'IN[2]', 5); 16 | network.connect(rrmerge, 'OUT', recvr, 'IN', 5); 17 | 18 | // --- run --- 19 | var fiberRuntime = new fbp.FiberRuntime(); 20 | network.run(fiberRuntime, {trace: false}); 21 | -------------------------------------------------------------------------------- /examples/fbptest07.js: -------------------------------------------------------------------------------- 1 | var fbp = require('..'); 2 | 3 | // --- define network --- 4 | var network = new fbp.Network(); 5 | 6 | var gendata = network.defProc('./examples/components/gendata', 'Gen'); 7 | var repl = network.defProc('./components/repl', 'Repl'); 8 | var concat = network.defProc('./components/concat', 'Concat'); 9 | var recvr = network.defProc('./components/recvr', 'Recvr'); 10 | 11 | network.initialize(gendata, 'COUNT', '20'); 12 | network.connect(gendata, 'OUT', repl, 'IN', 5); 13 | network.connect(repl, 'OUT[0]', concat, 'IN[0]', 5); 14 | network.connect(repl, 'OUT[1]', concat, 'IN[1]', 5); 15 | network.connect(repl, 'OUT[2]', concat, 'IN[2]', 5); 16 | network.connect(concat, 'OUT', recvr, 'IN', 5); 17 | 18 | // --- run --- 19 | var fiberRuntime = new fbp.FiberRuntime(); 20 | network.run(fiberRuntime, { 21 | trace: false 22 | }); 23 | -------------------------------------------------------------------------------- /examples/fbptest08.js: -------------------------------------------------------------------------------- 1 | var fbp = require('..') 2 | , path = require('path'); 3 | 4 | // --- define network --- 5 | var network = new fbp.Network(); 6 | 7 | var reader = network.defProc('./components/reader', 'Read'); 8 | var reverse = network.defProc('./components/reverse', 'Rev'); 9 | var reverse2 = network.defProc('./components/reverse', 'Rev2'); 10 | var recvr = network.defProc('./components/recvr', 'Recvr'); 11 | 12 | network.initialize(reader, 'FILE', path.resolve(__dirname, 'data/text.txt')); 13 | network.connect(reader, 'OUT', reverse, 'IN', 5); 14 | network.connect(reverse, 'OUT', reverse2, 'IN', 5); 15 | network.connect(reverse2, 'OUT', recvr, 'IN', 1); 16 | 17 | // --- run --- 18 | var fiberRuntime = new fbp.FiberRuntime(); 19 | network.run(fiberRuntime, {trace: true}); 20 | -------------------------------------------------------------------------------- /examples/fbptest09.js: -------------------------------------------------------------------------------- 1 | var fbp = require('..'); 2 | 3 | // --- define network --- 4 | var network = new fbp.Network(); 5 | 6 | var gendata = network.defProc('./examples/components/gendata', 'Gen'); 7 | var copier = network.defProc('./examples/components/copier_closing', 'CC'); 8 | var recvr = network.defProc('./components/recvr', 'Recvr'); 9 | 10 | network.initialize(gendata, 'COUNT', '200'); 11 | network.connect(gendata, 'OUT', copier, 'IN', 5); 12 | network.connect(copier, 'OUT', recvr, 'IN', 1); 13 | 14 | // --- run --- 15 | var fiberRuntime = new fbp.FiberRuntime(); 16 | network.run(fiberRuntime, {trace: false}); 17 | -------------------------------------------------------------------------------- /examples/fbptest10.js: -------------------------------------------------------------------------------- 1 | var fbp = require('..'); 2 | 3 | // --- define network --- 4 | var network = new fbp.Network(); 5 | 6 | var gendata = network.defProc('./examples/components/gendata', 'Gen'); 7 | var copier = network.defProc('./examples/components/copier_nonlooper', 'CNL'); 8 | var recvr = network.defProc('./components/recvr', 'Recvr'); 9 | 10 | network.initialize(gendata, 'COUNT', '200'); 11 | network.connect(gendata, 'OUT', copier, 'IN', 10); 12 | network.connect(copier, 'OUT', recvr, 'IN', 5); 13 | 14 | // --- run --- 15 | var fiberRuntime = new fbp.FiberRuntime(); 16 | network.run(fiberRuntime, {trace: false}); 17 | -------------------------------------------------------------------------------- /examples/fbptest11.js: -------------------------------------------------------------------------------- 1 | var fbp = require('..'); 2 | 3 | // --- define network --- 4 | var network = new fbp.Network(); 5 | 6 | var gendata = network.defProc('./examples/components/gendata', 'Gen'); 7 | var lbal = network.defProc('./components/lbal', 'LBal'); 8 | var randdelay0 = network.defProc('./components/randdelay', 'RD0'); 9 | var randdelay1 = network.defProc('./components/randdelay', 'RD1'); 10 | var randdelay2 = network.defProc('./components/randdelay', 'RD2'); 11 | var recvr = network.defProc('./components/recvr', 'Recvr'); 12 | 13 | network.initialize(gendata, 'COUNT', '20'); 14 | network.initialize(randdelay0, 'INTVL', '5000'); // random between 0 and 5000 msecs 15 | network.initialize(randdelay1, 'INTVL', '5000'); 16 | network.initialize(randdelay2, 'INTVL', '5000'); 17 | network.connect(gendata, 'OUT', lbal, 'IN', 5); 18 | network.connect(lbal, 'OUT[0]', randdelay0, 'IN', 5); 19 | network.connect(lbal, 'OUT[1]', randdelay1, 'IN', 5); 20 | network.connect(lbal, 'OUT[2]', randdelay2, 'IN', 5); 21 | 22 | network.connect(randdelay0, 'OUT', recvr, 'IN', 5); 23 | network.connect(randdelay1, 'OUT', recvr, 'IN', 5); 24 | network.connect(randdelay2, 'OUT', recvr, 'IN', 5); 25 | 26 | // --- run --- 27 | var fiberRuntime = new fbp.FiberRuntime(); 28 | network.run(fiberRuntime, {trace: false}); 29 | -------------------------------------------------------------------------------- /examples/fbptest12.js: -------------------------------------------------------------------------------- 1 | var fbp = require('..') 2 | , path = require('path'); 3 | 4 | // --- define network --- 5 | var network = new fbp.Network(); 6 | 7 | var reader = network.defProc('./components/reader', 'Read'); 8 | var copier = network.defProc('./components/copier', 'Copy'); 9 | var writer = network.defProc('./components/writer', 'Write'); 10 | 11 | network.initialize(reader, 'FILE', path.resolve(__dirname, 'data/text.txt')); 12 | network.connect(reader, 'OUT', copier, 'IN', 1); 13 | network.initialize(writer, 'FILE', path.resolve(__dirname, 'data/text_new.txt')); 14 | network.connect(copier, 'OUT', writer, 'IN', 1); 15 | 16 | // --- run --- 17 | var fiberRuntime = new fbp.FiberRuntime(); 18 | network.run(fiberRuntime, {trace: false}); 19 | -------------------------------------------------------------------------------- /examples/fbptest13.js: -------------------------------------------------------------------------------- 1 | var fbp = require('..'); 2 | 3 | // --- define network --- 4 | var network = new fbp.Network(); 5 | 6 | var gendata = network.defProc('./examples/components/gendata', 'Gen'); 7 | var randdelay = network.defProc('./components/randdelay', 'RD'); 8 | var recvr = network.defProc('./components/recvr', 'Recvr'); 9 | 10 | network.initialize(gendata, 'COUNT', '20'); 11 | network.initialize(randdelay, 'INTVL', '2000'); // random between 0 and 5000 msecs 12 | network.connect(gendata, 'OUT', randdelay, 'IN', 5); 13 | network.connect(randdelay, 'OUT', recvr, 'IN', 5); 14 | 15 | // --- run --- 16 | var fiberRuntime = new fbp.FiberRuntime(); 17 | network.run(fiberRuntime, {trace: false}); 18 | -------------------------------------------------------------------------------- /examples/fbptest14.js: -------------------------------------------------------------------------------- 1 | var fbp = require('..'); 2 | 3 | // --- define network --- 4 | var network = new fbp.Network(); 5 | var mms0 = network.defProc('./examples/components/mockmocksender', 'mms0'); 6 | var delay0 = network.defProc('./components/delay', 'delay0'); 7 | var mms1 = network.defProc('./examples/components/mockmocksender', 'mms1'); 8 | var delay1 = network.defProc('./components/delay', 'delay1'); 9 | var recvr = network.defProc('./components/recvr', 'Recvr'); 10 | 11 | network.initialize(delay0, 'INTVL', '100'); // 100 msecs 12 | network.initialize(delay1, 'INTVL', '250'); // 250 msecs 13 | network.initialize(mms0, 'PARMS', '100,(a),4'); 14 | network.connect(mms0, 'OUT', delay0, 'IN', 5); 15 | network.initialize(mms1, 'PARMS', '250,(b),4'); 16 | network.connect(mms1, 'OUT', delay1, 'IN', 5); 17 | network.connect(delay0, 'OUT', recvr, 'IN', 5); 18 | network.connect(delay1, 'OUT', recvr, 'IN', 5); 19 | // --- run --- 20 | var fiberRuntime = new fbp.FiberRuntime(); 21 | network.run(fiberRuntime, {trace: false}); 22 | -------------------------------------------------------------------------------- /examples/fbptests.bat: -------------------------------------------------------------------------------- 1 | forfiles /P . /M fbptest*.js /c "cmd /c node @path & echo. & echo. & echo. & echo Test @fname run! && pause && cls" -------------------------------------------------------------------------------- /examples/fbptests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | for file in ./fbptest*.js ; do 4 | if [ -e "$file" ] ; then 5 | echo "Running $file" 6 | node "$file" > /dev/null 7 | fi 8 | done -------------------------------------------------------------------------------- /examples/fbptestvl.js: -------------------------------------------------------------------------------- 1 | var fbp = require('..'); 2 | 3 | // --- define network --- 4 | var network = new fbp.Network(); 5 | 6 | var gendata = network.defProc('./examples/components/gendata.js', 'Gen'); 7 | var copier = network.defProc('./components/copier.js', 'Copy'); 8 | var disc = network.defProc('./components/discard.js', 'Disc'); 9 | // var recvr = fbp.defProc(require('../components/recvr.js'), 'recvr'); // equivalent 10 | 11 | network.initialize(gendata, 'COUNT', '100000000'); 12 | network.connect(gendata, 'OUT', copier, 'IN', 5); 13 | network.connect(copier, 'OUT', disc, 'IN', 5); 14 | 15 | // --- run --- 16 | var fiberRuntime = new fbp.FiberRuntime(); 17 | network.run(fiberRuntime, {trace: false}); 18 | -------------------------------------------------------------------------------- /examples/httpserver/fbphttpserver.js: -------------------------------------------------------------------------------- 1 | var fbp = require('../..'); 2 | 3 | // --- define network --- 4 | var network = new fbp.Network(); 5 | 6 | var receiver = network.defProc(require('../../components/httpserver')); 7 | var myproc = network.defProc(require('./myproc')); 8 | var send = network.defProc(require('./myresponse')); 9 | 10 | network.initialize(receiver, 'PORTNO', '8080'); 11 | network.connect(receiver, 'OUT', myproc, 'IN', 6); 12 | network.connect(myproc, 'OUT', send, 'IN', 6); 13 | 14 | // --- run --- 15 | var fiberRuntime = new fbp.FiberRuntime(); 16 | network.run(fiberRuntime, {trace: true}); 17 | -------------------------------------------------------------------------------- /examples/httpserver/myproc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function myproc() { 4 | var inport = this.openInputPort('IN'); 5 | var outport = this.openOutputPort('OUT'); 6 | while (true) { 7 | var ip = inport.receive(); 8 | if (ip == null) { 9 | break; 10 | } 11 | // not null, so this IP was open bracket 12 | outport.send(ip); //send it on 13 | ip = inport.receive(); // request 14 | var req = ip.contents; 15 | this.dropIP(ip); 16 | outport.send(this.createIP('Response from URL: ' + req.url + '\n')); 17 | ip = inport.receive(); // res 18 | outport.send(ip); // send it on 19 | ip = inport.receive(); // close bracket 20 | outport.send(ip); // send it on... 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /examples/httpserver/myresponse.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function myresp() { 4 | var ip; 5 | var inport = this.openInputPort('IN'); 6 | 7 | while (true) { 8 | ip = inport.receive(); // shd be open bracket 9 | if (ip === null) { 10 | break; 11 | } 12 | this.dropIP(ip); 13 | ip = inport.receive(); // shd be response string 14 | var message = ip.contents; 15 | this.dropIP(ip); 16 | ip = inport.receive(); // shd be res object 17 | var res = ip.contents; 18 | this.dropIP(ip); 19 | res.end(message); 20 | ip = inport.receive(); // shd be close bracket 21 | this.dropIP(ip); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /examples/testsubstreamsensitivesplitting.js: -------------------------------------------------------------------------------- 1 | // testsubstreamsensitivesplitting.js 2 | 3 | var fbp = require('..'); 4 | 5 | // this network is to test interaction between lbal (Load balancer) and substreams - 6 | // the substreams may be rearranged, but within a substreams the IPs must preserve their 7 | // number and sequence 8 | 9 | // Warning: given the small connection sizes, this network should deadlock - see comment in javafbp, but 10 | // for some reason, this isn't happening - more research needs to take place! 11 | 12 | // --- define network --- 13 | var network = new fbp.Network(); 14 | 15 | var genss = network.defProc('./examples/components/genss.js', 'Gen'); 16 | var lbal = network.defProc('./components/lbal.js', 'LBal'); 17 | var passthru0 = network.defProc('./components/delay.js', 'passthru0'); 18 | var passthru1 = network.defProc('./components/delay.js', 'passthru1'); 19 | var passthru2 = network.defProc('./components/passthru.js', 'passthru2'); 20 | 21 | 22 | var makeMergeSubstreamSensitive = true; 23 | 24 | network.initialize(genss, 'COUNT', '100'); 25 | network.connect(genss, 'OUT', lbal, 'IN', 4); 26 | network.initialize(passthru0, 'INTVL', '400'); 27 | network.connect(lbal, 'OUT[0]', passthru0, 'IN', 1); 28 | network.initialize(passthru1, 'INTVL', '400'); 29 | network.connect(lbal, 'OUT[1]', passthru1, 'IN', 1); 30 | network.initialize(passthru2, 'INTVL', '400'); 31 | network.connect(lbal, 'OUT[2]', passthru2, 'IN', 1); 32 | 33 | var recvr = network.defProc('./components/recvr.js'); 34 | 35 | /* 36 | * 3 passthru's feeding one port -> pretty mixed up data 37 | */ 38 | if (!makeMergeSubstreamSensitive) { 39 | network.connect(passthru0, 'OUT', recvr, 'IN', 1); 40 | network.connect(passthru1, 'OUT', recvr, 'IN', 1); 41 | network.connect(passthru2, 'OUT', recvr, 'IN', 1); 42 | } 43 | else { 44 | 45 | /* using substreamsensitivemerge 46 | * 47 | */ 48 | 49 | var ssmerge = network.defProc('./components/substreamsensitivemerge.js'); 50 | var csws = network.defProc('./examples/components/checksequencewithinsubstreams.js'); 51 | var passthruF = network.defProc('./components/delay.js', 'passthruF'); 52 | 53 | network.connect(passthru0, 'OUT', ssmerge, 'IN[0]', 8); 54 | network.connect(passthru1, 'OUT', ssmerge, 'IN[1]', 8); 55 | network.connect(passthru2, 'OUT', ssmerge, 'IN[2]', 8); 56 | network.connect(ssmerge, 'OUT', csws, 'IN'); 57 | network.connect(csws, 'OUT', passthruF, 'IN'); 58 | network.initialize(passthruF, 'INTVL', '400'); 59 | network.connect(passthruF, 'OUT', recvr, 'IN'); 60 | 61 | } 62 | 63 | 64 | // --- run --- 65 | var fiberRuntime = new fbp.FiberRuntime(); 66 | network.run(fiberRuntime, {trace: false}); 67 | -------------------------------------------------------------------------------- /examples/update.js: -------------------------------------------------------------------------------- 1 | var fbp = require('..') 2 | , path = require('path'); 3 | 4 | // --- define network --- 5 | var network = new fbp.Network(); 6 | 7 | var readerm = network.defProc('./components/reader', 'readerm'); 8 | var readerd = network.defProc('./components/reader', 'readerd'); 9 | var collate = network.defProc('./components/collate', 'coll'); 10 | var display = network.defProc('./components/display', 'disp'); 11 | 12 | network.initialize(readerm, 'FILE', path.resolve(__dirname, 'data/mfile')); 13 | network.connect(readerm, 'OUT', collate, 'IN[0]'); 14 | network.initialize(readerd, 'FILE', path.resolve(__dirname, 'data/dfile')); 15 | network.connect(readerd, 'OUT', collate, 'IN[1]'); 16 | network.initialize(collate, 'CTLFIELDS', '3, 2, 5'); 17 | network.connect(collate, 'OUT', display, 'IN'); 18 | 19 | // --- run --- 20 | var fiberRuntime = new fbp.FiberRuntime(); 21 | network.run(fiberRuntime, {trace: false}); 22 | -------------------------------------------------------------------------------- /examples/update_c.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This is the same as update.js, except that its output is also compared with 3 | * a file of expected results; since brackets cannot be held in a file, 4 | * the first character of each record in the expected results file is used to 5 | * decide whether the IP is an open or close bracket, or a normal data IP 6 | */ 7 | 8 | var fbp = require('..') 9 | , path = require('path'); 10 | 11 | // --- define network --- 12 | var network = new fbp.Network(); 13 | 14 | var readerm = network.defProc('./components/reader', 'readerm'); 15 | var readerd = network.defProc('./components/reader', 'readerd'); 16 | var readerc = network.defProc('./components/reader', 'readerc'); 17 | var collate = network.defProc('./components/collate', 'coll'); 18 | var compare = network.defProc('./examples/components/compare', 'comp'); 19 | var gendatawithbreaks = network.defProc('./examples/components/gendatawithbreaks', 'GDWB'); 20 | //var display = network.defProc('./components/display'); 21 | 22 | network.initialize(readerm, 'FILE', path.resolve(__dirname, 'data/mfile')); 23 | network.connect(readerm, 'OUT', collate, 'IN[0]'); 24 | network.initialize(readerd, 'FILE', path.resolve(__dirname, 'data/dfile')); 25 | network.connect(readerd, 'OUT', collate, 'IN[1]'); 26 | network.initialize(collate, 'CTLFIELDS', '3, 2, 5'); 27 | network.connect(collate, 'OUT', compare, 'IN[0]'); 28 | network.initialize(readerc, 'FILE', path.resolve(__dirname, 'data/collate_output')); 29 | network.connect(readerc, 'OUT', gendatawithbreaks, 'IN'); 30 | network.connect(gendatawithbreaks, 'OUT', compare, 'IN[1]'); 31 | 32 | // --- run --- 33 | var fiberRuntime = new fbp.FiberRuntime(); 34 | network.run(fiberRuntime, {trace: false}); 35 | -------------------------------------------------------------------------------- /examples/websocketchat/fbptestwschat.js: -------------------------------------------------------------------------------- 1 | var fbp = require('../..'); 2 | 3 | // --- define network --- 4 | var network = new fbp.Network(); 5 | 6 | var receiver = network.defProc(require('../../components/wsrecv')); 7 | var simproc = network.defProc(require('./wssimproc')); 8 | var send = network.defProc(require('./wsbroadcast')); 9 | 10 | network.initialize(receiver, 'PORTNO', '9003'); 11 | network.connect(receiver, 'WSSOUT', send, 'WSSIN', 6); 12 | network.connect(receiver, 'OUT', simproc, 'IN', 6); 13 | network.connect(simproc, 'OUT', send, 'IN', 6); 14 | 15 | // --- run --- 16 | var fiberRuntime = new fbp.FiberRuntime(); 17 | network.run(fiberRuntime, {trace: true}); 18 | -------------------------------------------------------------------------------- /examples/websocketchat/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | WebSockets Example - Chat1 5 | 6 | 7 | 30 | 85 | 86 | 87 |

JEE7 WebSocket Example - Chat1

88 | 89 | 90 | 91 | 92 |
    93 | 94 |
95 | 96 | 97 | < 98 | -------------------------------------------------------------------------------- /examples/websocketchat/wsbroadcast.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function wsbroadcast() { 4 | var ip; 5 | var inport = this.openInputPort('IN'); 6 | var wssin = this.openInputPort('WSSIN'); 7 | 8 | ip = wssin.receive(); // shd be wss 9 | var wss = ip.contents; 10 | this.dropIP(ip); 11 | 12 | while (true) { 13 | ip = inport.receive(); // shd be open bracket 14 | if (ip === null) { 15 | break; 16 | } 17 | //console.log(ip); 18 | this.dropIP(ip); 19 | ip = inport.receive(); // shd be orig connection 20 | //console.log(ip); 21 | this.dropIP(ip); 22 | while (true) { 23 | ip = inport.receive(); 24 | //console.log(ip); 25 | if (ip.type == this.IPTypes.CLOSE) { 26 | this.dropIP(ip); 27 | break; 28 | } 29 | var msg = ip.contents; 30 | this.dropIP(ip); 31 | wss.clients.forEach(function (client) { 32 | client.send(msg); 33 | }); 34 | } 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /examples/websocketchat/wssimproc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function wssimproc() { 4 | var inport = this.openInputPort('IN'); 5 | var outport = this.openOutputPort('OUT'); 6 | while (true) { 7 | var ip = inport.receive(); 8 | if (ip == null) { 9 | break; 10 | } 11 | // not null, so this IP was open bracket 12 | outport.send(ip); //send it on 13 | ip = inport.receive(); // connection 14 | outport.send(ip); // send it on 15 | ip = inport.receive(); // data IP - drop and emit modified message 16 | var name = "Anonymous"; 17 | var text = ip.contents; 18 | var i = text.indexOf(":"); 19 | if (i > -1) { 20 | name = text.substring(0, i); 21 | text = text.substring(i + 1); 22 | } 23 | this.dropIP(ip); 24 | outport.send(this.createIP(name + ' wrote:')); 25 | outport.send(this.createIP(text)); 26 | ip = inport.receive(); // close bracket 27 | outport.send(ip); // send it on... 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var IP = require('./core/IP'); 4 | module.exports = { 5 | Network: require('./core/Network'), 6 | FiberRuntime: require('./core/runtimes/FiberRuntime'), 7 | IPTypes: IP.Types 8 | }; 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jsfbp", 3 | "version": "3.0.1", 4 | "description": "FBP implementation written using node-fibers ", 5 | "repository": { 6 | "type": "git", 7 | "url": "git+https://github.com/jpaulm/jsfbp.git" 8 | }, 9 | "engines": { 10 | "node": ">= 12.4.0" 11 | }, 12 | "keywords": [ 13 | "fbp", 14 | "flow-based-programming", 15 | "node-fibers" 16 | ], 17 | "author": "J. Paul Morrison", 18 | "contributors": [ 19 | "Dan Rumney " 20 | ], 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/jpaulm/jsfbp/issues" 24 | }, 25 | "homepage": "https://github.com/jpaulm/jsfbp", 26 | "dependencies": { 27 | "create-npmrc": "^2.0.2", 28 | "detect-libc": "^1.0.3", 29 | 30 | "fibers": "^5.0.0", 31 | "lodash": "^4.17.21", 32 | "parsefbp": "^0.3.0", 33 | 34 | "snyk": "^1.518.0" 35 | 36 | 37 | }, 38 | "main": "index.js", 39 | "devDependencies": { 40 | "chai": "4.3.4", 41 | "eslint": "7.32.0", 42 | "eslint-config-defaults": "9.0.0", 43 | "mocha": "8.4.0", 44 | "ws": "7.5.5", 45 | "dummy-module": "*" 46 | }, 47 | "directories": { 48 | "doc": "docs" 49 | }, 50 | "scripts": { 51 | "test": "mocha --recursive -R spec --require test/test_helper.js test/components test/core", 52 | "lint": "eslint components core examples test index.js", 53 | "snyk-protect": "snyk protect", 54 | "prepare": "npm run snyk-protect" 55 | }, 56 | "snyk": true 57 | } 58 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ], 5 | "automerge": true, 6 | "major": { 7 | "automerge": false 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "mocha":true 4 | }, 5 | "globals": { 6 | "expect": false, 7 | "MockSender": false, 8 | "MockReceiver": false, 9 | "TypeReceiver": false, 10 | "TestFiber": false 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/components/000000-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jpaulm/jsfbp/841bd9f99d897a3e8fa80e435b3a3f62a25b2985/test/components/000000-1.png -------------------------------------------------------------------------------- /test/components/breader.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fbp = require('../..'); 4 | 5 | describe('breader', function () { 6 | it('should read a file and output its contents as bytes', function (done) { 7 | var network = new fbp.Network(); 8 | 9 | var result = []; 10 | 11 | var breader = network.defProc('./components/breader.js', 'breader'); 12 | var receiver = network.defProc(MockReceiver.generator(result), 'receiver'); 13 | 14 | network.initialize(breader, 'FILE', __dirname+'/000000-1.png'); 15 | network.connect(breader, 'OUT', receiver, 'IN'); 16 | 17 | var fiberRuntime = new fbp.FiberRuntime(); 18 | network.run(fiberRuntime, {trace: false}, function() { 19 | expect(result).to.deep.equal([ 20 | 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 21 | 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x7e, 0x9b, 22 | 0x55, 0x00, 0x00, 0x00, 0x0a, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0x63, 0x62, 0x00, 0x00, 0x00, 23 | 0x06, 0x00, 0x03, 0x36, 0x37, 0x7c, 0xa8, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 24 | 0x42, 0x60, 0x82 25 | ]); 26 | done(); 27 | }); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /test/components/bwriter.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fbp = require('../..'); 4 | var fs = require('fs'); 5 | 6 | var _ = require('lodash'); 7 | var EOL = require('os').EOL; 8 | 9 | var eolBytes = _.invokeMap(EOL.split(''), 'charCodeAt', 0); 10 | 11 | describe('bwriter', function () { 12 | it('should write incoming IPs to a file', function (done) { 13 | var network = new fbp.Network(); 14 | 15 | var sender = network.defProc(MockSender.generator(["IP.OPEN", 16 | 71, 17 | 111, 18 | 111, 19 | 100, 20 | 98, 21 | 121, 22 | 101, 23 | 0x20, 24 | 87, 25 | 111, 26 | 114, 27 | 108, 28 | 100] 29 | .concat(eolBytes) 30 | .concat("IP.CLOSE")), 'sender'); 31 | var writer = network.defProc('./components/bwriter.js', 'bwriter'); 32 | 33 | network.initialize(writer, 'FILE', __dirname+'/goodbye-world.txt'); 34 | network.connect(sender, 'OUT', writer, 'IN'); 35 | 36 | network.run(new fbp.FiberRuntime(), {trace: false}, function () { 37 | fs.readFile(__dirname+'/goodbye-world.txt', 'utf-8', function(err, data) { 38 | if(err) { 39 | return done(err); 40 | } 41 | expect(data).to.equal("Goodbye World"+EOL); 42 | done(); 43 | }); 44 | }); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /test/components/collate.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fbp = require('../..'); 4 | 5 | describe('collate', function () { 6 | it('should collate based on a single field', function () { 7 | var network = new fbp.Network(); 8 | 9 | var result = []; 10 | 11 | var sender0 = network.defProc(MockSender.generator(['1,m1', '2,m2', '3,m3']), "sender0"); 12 | var sender1 = network.defProc(MockSender.generator(['1,d11', '1,d12', '2,d21', '3,d31', '3,d32', '3,d33', '4,d41']), "sender1"); 13 | var collate = network.defProc('./components/collate.js', "collate"); 14 | var receiver = network.defProc(MockReceiver.generator(result), "receiver"); 15 | 16 | network.initialize(collate, 'CTLFIELDS', '1'); 17 | network.connect(sender0, 'OUT', collate, 'IN[0]', 5); 18 | network.connect(sender1, 'OUT', collate, 'IN[1]', 5); 19 | network.connect(collate, 'OUT', receiver, 'IN'); 20 | 21 | var fiberRuntime = new fbp.FiberRuntime(); 22 | network.run(fiberRuntime, {trace: false}); 23 | 24 | expect(result).to.deep.equal(['1,m1', '1,d11', '1,d12', '2,m2', '2,d21', '3,m3', '3,d31', '3,d32', '3,d33', '4,d41']); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /test/components/copier.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fbp = require('../..'); 4 | 5 | describe('copier', function () { 6 | it('should copy multiple IPs', function (done) { 7 | var network = new fbp.Network(); 8 | 9 | var result = []; 10 | 11 | var sender = network.defProc(MockSender.generator([1, 2, 3, 4, 5]), "sender"); 12 | var copier = network.defProc('./components/copier.js', "copier"); 13 | var receiver = network.defProc(MockReceiver.generator(result), "receiver"); 14 | 15 | network.connect(sender, 'OUT', copier, 'IN'); 16 | network.connect(copier, 'OUT', receiver, 'IN'); 17 | 18 | network.run(new fbp.FiberRuntime(), {trace: false}, function () { 19 | expect(result).to.deep.equal([1, 2, 3, 4, 5]); 20 | done(); 21 | }); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/components/delay.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fbp = require('../..'); 4 | 5 | describe('delay', function () { 6 | it('should exactly delay a single IP', function (done) { 7 | var DELAY = 1000; 8 | var DELAY_MAX_DIFF = 150; 9 | this.timeout(DELAY + DELAY_MAX_DIFF); 10 | 11 | var network = new fbp.Network(); 12 | 13 | var result = []; 14 | 15 | var sender = network.defProc(MockSender.generator([42]), "sender"); 16 | var delay = network.defProc('./components/delay.js', "delay"); 17 | var receiver = network.defProc(MockReceiver.generator(result), "receiver"); 18 | 19 | network.initialize(delay, 'INTVL', DELAY); 20 | network.connect(sender, 'OUT', delay, 'IN', 5); 21 | network.connect(delay, 'OUT', receiver, 'IN'); 22 | 23 | var startTime = Date.now(); 24 | 25 | network.run(new fbp.FiberRuntime(), {trace: false}, function () { 26 | expect(result).to.deep.equal([42]); 27 | var diffTime = Date.now() - startTime; 28 | expect(Math.abs(diffTime - DELAY)).to.be.below(DELAY_MAX_DIFF); 29 | done(); 30 | }); 31 | }); 32 | 33 | it('should exactly delay multiple IPs', function (done) { 34 | var DELAY = 1000; 35 | var DELAY_MAX_DIFF = 300; 36 | var TOTAL_DELAY = DELAY * 3; 37 | this.timeout(TOTAL_DELAY + DELAY_MAX_DIFF); 38 | 39 | var network = new fbp.Network(); 40 | 41 | var result = []; 42 | 43 | var sender = network.defProc(MockSender.generator([1, 2, 3]), "sender"); 44 | var delay = network.defProc('./components/delay.js', "delay"); 45 | var receiver = network.defProc(MockReceiver.generator(result), "receiver"); 46 | 47 | network.initialize(delay, 'INTVL', DELAY); 48 | network.connect(sender, 'OUT', delay, 'IN', 5); 49 | network.connect(delay, 'OUT', receiver, 'IN'); 50 | 51 | var startTime = Date.now(); 52 | 53 | network.run(new fbp.FiberRuntime(), {trace: false}, function () { 54 | expect(result).to.deep.equal([1, 2, 3]); 55 | var diffTime = Date.now() - startTime; 56 | expect(Math.abs(diffTime - TOTAL_DELAY)).to.be.below(DELAY_MAX_DIFF); 57 | done(); 58 | }); 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /test/components/randdelay.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fbp = require('../..'); 4 | 5 | describe('randdelay', function () { 6 | it('should randomly delay a single IP', function (done) { 7 | var DELAY = 1000; 8 | var DELAY_MAX_DIFF = 150; 9 | this.timeout(DELAY + DELAY_MAX_DIFF); 10 | 11 | var network = new fbp.Network(); 12 | 13 | var result = []; 14 | 15 | var sender = network.defProc(MockSender.generator([42]), "sender"); 16 | var delay = network.defProc('./components/randdelay.js', "randdelay"); 17 | var receiver = network.defProc(MockReceiver.generator(result), "receiver"); 18 | 19 | network.initialize(delay, 'INTVL', DELAY); 20 | network.connect(sender, 'OUT', delay, 'IN'); 21 | network.connect(delay, 'OUT', receiver, 'IN'); 22 | 23 | var startTime = Date.now(); 24 | 25 | network.run(new fbp.FiberRuntime(), {trace: false}, function () { 26 | expect(result).to.deep.equal([42]); 27 | var diffTime = Date.now() - startTime; 28 | expect(Math.abs(diffTime - DELAY)).to.be.within(0, DELAY); 29 | done(); 30 | }); 31 | }); 32 | 33 | it('should randomly delay multiple IPs', function (done) { 34 | var DELAY = 1000; 35 | var DELAY_MAX_DIFF = 300; 36 | var DELAY_TOTAL = DELAY * 5; 37 | this.timeout(DELAY_TOTAL + DELAY_MAX_DIFF); 38 | 39 | var network = new fbp.Network(); 40 | 41 | var result = []; 42 | var startTime; 43 | 44 | var i = 0; 45 | var mockReceiver = MockReceiver.generator(result, function receive() { 46 | i++; 47 | var diffTime = Date.now() - startTime; 48 | expect(Math.abs(diffTime - DELAY)).to.be.within(0, DELAY * i + DELAY_MAX_DIFF); 49 | console.log("DELAY " + diffTime); 50 | }); 51 | 52 | var sender = network.defProc(MockSender.generator([1, 2, 3, 4, 5]), "sender"); 53 | var delay = network.defProc('./components/randdelay.js', "delay"); 54 | var receiver = network.defProc(mockReceiver, "receiver"); 55 | 56 | network.initialize(delay, 'INTVL', DELAY); 57 | network.connect(sender, 'OUT', delay, 'IN'); 58 | network.connect(delay, 'OUT', receiver, 'IN'); 59 | 60 | startTime = Date.now(); 61 | 62 | network.run(new fbp.FiberRuntime(), {trace: false}, function () { 63 | expect(result).to.have.members([1, 2, 3, 4, 5]); 64 | done(); 65 | }); 66 | }); 67 | }); 68 | -------------------------------------------------------------------------------- /test/components/repl.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fbp = require('../..'); 4 | 5 | describe('repl', function () { 6 | it('should replicate multiple IPs', function (done) { 7 | var network = new fbp.Network(); 8 | 9 | var result1 = []; 10 | var result2 = []; 11 | 12 | var sender = network.defProc(MockSender.generator([1, 2, 3, 4, 5]), "sender"); 13 | var repl = network.defProc('./components/repl.js', "repl"); 14 | var receiver1 = network.defProc(MockReceiver.generator(result1), "receiver1"); 15 | var receiver2 = network.defProc(MockReceiver.generator(result2), "receiver2"); 16 | 17 | network.connect(sender, 'OUT', repl, 'IN'); 18 | network.connect(repl, 'OUT[0]', receiver1, 'IN'); 19 | network.connect(repl, 'OUT[1]', receiver2, 'IN'); 20 | 21 | network.run(new fbp.FiberRuntime(), {trace: false}, function () { 22 | expect(result1).to.deep.equal([1, 2, 3, 4, 5]); 23 | expect(result2).to.deep.equal([1, 2, 3, 4, 5]); 24 | done(); 25 | }); 26 | }); 27 | 28 | it('should replicate brackets', function (done) { 29 | var network = new fbp.Network(); 30 | 31 | var result1 = []; 32 | var result2 = []; 33 | 34 | var sender = network.defProc(MockSender.generator(["IP.OPEN", 7, 6, 5, "IP.CLOSE"]), 'sender'); 35 | var repl = network.defProc('./components/repl.js', "repl"); 36 | var receiver1 = network.defProc(MockReceiver.generator(result1), "receiver1"); 37 | var receiver2 = network.defProc(TypeReceiver.generator(result2), "receiver2"); 38 | 39 | network.connect(sender, 'OUT', repl, 'IN'); 40 | network.connect(repl, 'OUT[0]', receiver1, 'IN'); 41 | network.connect(repl, 'OUT[1]', receiver2, 'IN'); 42 | 43 | network.run(new fbp.FiberRuntime(), {trace: false}, function () { 44 | expect(result1).to.deep.equal([7, 6, 5]); 45 | expect(result2).to.deep.equal([fbp.IPTypes.OPEN, fbp.IPTypes.NORMAL, fbp.IPTypes.NORMAL, fbp.IPTypes.NORMAL, fbp.IPTypes.CLOSE]); 46 | done(); 47 | }); 48 | }) 49 | }); 50 | -------------------------------------------------------------------------------- /test/core/Enum.js: -------------------------------------------------------------------------------- 1 | var Enum = require('../../core/Enum'); 2 | 3 | describe('Enum', function () { 4 | it('should create an enum and provide a lookup function', function () { 5 | var myEnum = Enum(['a', 'b', 'c']); 6 | 7 | expect(myEnum).to.have.ownProperty('a'); 8 | expect(myEnum).to.have.ownProperty('b') 9 | expect(myEnum).to.have.ownProperty('c'); 10 | 11 | expect(myEnum.__lookup(myEnum.a)).to.equal('a'); 12 | expect(myEnum.__lookup(myEnum.b)).to.equal('b'); 13 | expect(myEnum.__lookup(myEnum.c)).to.equal('c'); 14 | }); 15 | 16 | it('should not be writable', function () { 17 | var myEnum = Enum(['a']); 18 | 19 | var prevValue = myEnum.a; 20 | myEnum.a = -1; 21 | expect(myEnum.a).to.equal(prevValue); 22 | 23 | var prevLookupFunction = myEnum.__lookup; 24 | myEnum.__lookup = 100; 25 | expect(myEnum.__lookup).to.equal(prevLookupFunction); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /test/core/InputPort.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by danrumney on 5/27/16. 3 | */ 4 | 5 | var InputPort = require('../../core/InputPort'); 6 | var IIPConnection = require('../../core/IIPConnection'); 7 | var ProcessConnection = require('../../core/ProcessConnection'); 8 | var IP = require('../../core/IP'); 9 | var FiberRuntime = require('../../core/runtimes/FiberRuntime'); 10 | 11 | 12 | var getTestInPort = function (connection) { 13 | var fiberRuntime = new FiberRuntime(); 14 | var inPort = new InputPort(); 15 | inPort.name = "Test InPort"; 16 | inPort.setRuntime(fiberRuntime); 17 | inPort.conn = connection; 18 | return inPort; 19 | }; 20 | 21 | describe('InputPort', function() { 22 | 23 | 24 | it('can receive IIPs', function(done) { 25 | var inPort = getTestInPort(new IIPConnection("test")); 26 | 27 | global.tracing= true; 28 | 29 | TestFiber(function(mockProcess) { 30 | var ip = inPort.receive(); 31 | 32 | expect(ip.contents).to.be.equal('test'); 33 | expect(ip.owner).to.be.equal(mockProcess); 34 | expect(ip.type).to.be.equal(IP.NORMAL); 35 | 36 | done(); 37 | }); 38 | }); 39 | 40 | it('returns null from a closed connection', function(done) { 41 | var inPort = getTestInPort(new ProcessConnection(1)); 42 | inPort.conn.closed = true; 43 | 44 | global.tracing= true; 45 | 46 | TestFiber(function() { 47 | var ip = inPort.receive(); 48 | 49 | expect(ip).to.be.null; 50 | 51 | done(); 52 | }); 53 | }) 54 | }); 55 | -------------------------------------------------------------------------------- /test/core/Network.js: -------------------------------------------------------------------------------- 1 | var Network = require('../../core/Network'); 2 | var fbp = require('../..'); 3 | var fs = require('fs'); 4 | 5 | describe('Network', function () { 6 | it('can load FBP components from this module\'s core via path', function () { 7 | var network = new Network(); 8 | var process = network.defProc('./components/copier.js', 'copier'); 9 | 10 | expect(process.func).to.be.a('function'); 11 | }); 12 | 13 | it('can load FBP components from this module\'s core via package/component', function () { 14 | var network = new Network(); 15 | var process = network.defProc('jsfbp/copier', "copier"); 16 | 17 | expect(process.func).to.be.a('function'); 18 | }); 19 | 20 | it('can load FBP components directly from modules', function () { 21 | var network = new Network(); 22 | var process = network.defProc('dummy-module', "dummy"); 23 | 24 | expect(process.func).to.be.an('object'); 25 | }); 26 | 27 | it('can load FBP components that are part of a larger package', function () { 28 | var network = new Network(); 29 | var process = network.defProc('dummy-module/getP3', 'getP3'); 30 | 31 | expect(process.func).to.be.an('function'); 32 | }); 33 | 34 | it('can be created from an FBP file', function (done) { 35 | fs.readFile(__dirname + '/network.fbp', function (err, graph) { 36 | if (err) { 37 | return done(err); 38 | } 39 | 40 | var network = Network.createFromGraph(graph.toString()); 41 | 42 | var fiberRuntime = new fbp.FiberRuntime(); 43 | network.run(fiberRuntime, {trace: false}); 44 | 45 | var receiverProcess = network.getProcessByName("receiver"); 46 | var result = receiverProcess.func.getResult(); 47 | 48 | expect(result).to.deep.equal(['1,m1', '1,d11', '1,d12', '2,m2', '2,d21', '3,m3', '3,d31', '3,d32', '3,d33', '4,d41']); 49 | 50 | done(); 51 | }); 52 | }); 53 | 54 | it('correctly identifies empty IIPs', function () { 55 | expect(function () { 56 | Network.createFromGraph("'' -> IN RECVR(jsfbp/recvr)"); 57 | }).not.to.throw(Error); 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /test/core/network.fbp: -------------------------------------------------------------------------------- 1 | '["1,m1", "2,m2", "3,m3"]' -> DATA sender0(./test/mocks/MockSender.js) 2 | '["1,d11", "1,d12", "2,d21", "3,d31", "3,d32", "3,d33", "4,d41"]' -> DATA sender1(./test/mocks/MockSender.js) 3 | '1' -> CTLFIELDS collate(jsfbp/collate) 4 | sender0 OUT -> (5) IN[0] collate 5 | sender1 OUT -> (5) IN[1] collate 6 | collate OUT -> IN receiver(./test/mocks/MockReceiver.js) 7 | -------------------------------------------------------------------------------- /test/core/runtimes/fiber-runtime.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fbp = require('../../..'); 4 | 5 | describe('FiberRuntime', function () { 6 | it('should be able to run a simple sender-->receiver setup', function () { 7 | var network = new fbp.Network(); 8 | 9 | var result = []; 10 | var sender = network.defProc(new MockSender.generator([1, 2, 3, 4, 5]), "sender"); 11 | var recvr = network.defProc(new MockReceiver.generator(result), "receiver"); 12 | 13 | network.connect(sender, 'OUT', recvr, 'IN', 5); 14 | 15 | network.run(new fbp.FiberRuntime(), {trace: false}); 16 | expect(result).to.deep.equal([1, 2, 3, 4, 5]); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /test/mocks/MockReceiver.js: -------------------------------------------------------------------------------- 1 | 2 | var MockReceiverGenerator = function (outputArray) { 3 | if(!outputArray) { 4 | outputArray = []; 5 | } 6 | var MockReceiver = function () { 7 | var inport = this.openInputPort('IN'); 8 | var ip; 9 | while ((ip = inport.receive()) !== null) { 10 | if (ip.type === this.IPTypes.NORMAL) { 11 | outputArray.push(ip.contents); 12 | } 13 | this.dropIP(ip); 14 | } 15 | }; 16 | 17 | MockReceiver.getResult = function() { 18 | return outputArray; 19 | }; 20 | return MockReceiver; 21 | }; 22 | 23 | var MockReceiver = MockReceiverGenerator(); 24 | MockReceiver.generator = MockReceiverGenerator; 25 | 26 | 27 | module.exports = MockReceiver; 28 | -------------------------------------------------------------------------------- /test/mocks/MockSender.js: -------------------------------------------------------------------------------- 1 | 2 | var MockSenderGenerator = function (inputArray) { 3 | initByPort = false; 4 | if(!inputArray) { 5 | var initByPort = true; 6 | } 7 | return function () { 8 | if(initByPort) { 9 | var inport = this.openInputPort('DATA'); 10 | var inputJSON = inport.receive(); 11 | inputArray = JSON.parse(inputJSON.contents); 12 | this.dropIP(inputJSON); 13 | } 14 | var outport = this.openOutputPort('OUT'); 15 | var proc = this; 16 | inputArray.forEach(function (item) { 17 | if(item === "IP.OPEN" || item === "IP.CLOSE") { 18 | var bracket = item.split('.')[1]; 19 | outport.send(proc.createIPBracket(proc.IPTypes[bracket])); 20 | } else { 21 | outport.send(proc.createIP(item)); 22 | } 23 | }); 24 | }; 25 | }; 26 | 27 | MockSender = MockSenderGenerator(); 28 | 29 | module.exports = MockSender; 30 | MockSender.generator = MockSenderGenerator; 31 | -------------------------------------------------------------------------------- /test/mocks/TypeReceiver.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Receives IPs and records their types 3 | */ 4 | var TypeReceiverGenerator = function (outputArray) { 5 | if(!outputArray) { 6 | outputArray = []; 7 | } 8 | var TypeReceiver = function () { 9 | var inport = this.openInputPort('IN'); 10 | var ip; 11 | while ((ip = inport.receive()) !== null) { 12 | outputArray.push(ip.type); 13 | 14 | this.dropIP(ip); 15 | } 16 | }; 17 | 18 | TypeReceiver.getResult = function() { 19 | return outputArray; 20 | }; 21 | return TypeReceiver; 22 | }; 23 | 24 | var TypeReceiver = TypeReceiverGenerator(); 25 | TypeReceiver.generator = TypeReceiverGenerator; 26 | 27 | 28 | module.exports = TypeReceiver; 29 | -------------------------------------------------------------------------------- /test/test_helper.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var chai = require('chai'); 4 | var Fiber = require('fibers'); 5 | var Process = require('../core/Process'); 6 | 7 | global.expect = chai.expect; 8 | 9 | global.MockSender = require('./mocks/MockSender'); 10 | 11 | global.MockReceiver = require('./mocks/MockReceiver'); 12 | global.TypeReceiver = require('./mocks/TypeReceiver'); 13 | 14 | 15 | global.TestFiber = function(action) { 16 | Fiber(function() { 17 | var mockProcess = new Process("test", function() {console.log("Test Process");}); 18 | 19 | Fiber.current.fbpProc = mockProcess; 20 | action(mockProcess); 21 | }).run(); 22 | }; 23 | --------------------------------------------------------------------------------