diff --git a/cf.js b/cf.js index 8589b95ebe757cba5c535c4d753921444f4d6065..860be38f127fb8c735a25eda3d86e88e461faf84 100644 --- a/cf.js +++ b/cf.js @@ -7,14 +7,20 @@ const bodyparser = require('body-parser') // our fs tools, const fs = require('fs') const filesys = require('./filesys.js') +// and we occasionally spawn local pipes (workers) +const { + Worker, workerData +} = require('worker_threads') // will use these to figure where tf we are const os = require('os') let ifaces = os.networkInterfaces() // serve everything: https://expressjs.com/en/resources/middleware/serve-static.html app.use(express.static(__dirname)) +// accept post bodies as json, app.use(bodyparser.json()) app.use(bodyparser.urlencoded({extended: true})) + // if these don't exist, they get 'nexted' to any other 'middleware' we write app.get('/fileList', (req, res) => { try{ @@ -42,6 +48,7 @@ app.get('/fileList', (req, res) => { res.send('server-side error retrieving list') } }) + // we also handle file-saving this way, app.post('/save/contexts/:context/:file', (req, res) => { // this is probably fine for now, but I kind of want a websocket to do this kind of stuff ? @@ -68,11 +75,35 @@ app.post('/save/systems/:file', (req, res) => { }) }) +// we also want to institute some pipes: this is a holdover for a better system +// more akin to nautilus, where server-side graphs are manipulated +// for now, we just want to dive down to a usb port, probably, so this shallow link is OK +app.get('/pipeHookup/:file', (req, res) => { + // we can assume that the file is a reciprocal pipe-type in our local pipes/file.js location + // we'll open that can as a spawn, can assume it's hosting a websocket (it will tell us the port?) + // and we can send that information back up stream, + console.log('/pipes', req.params.file) + const piper = new Worker(`${__dirname}/pipes/${req.params.file}`) + piper.on('message', (msg) => { + console.log('worker msg', msg) + }) + piper.on('error', (err) => { + console.log('worker err', err) + }) + piper.on('exit', (code) => { + console.log('exit code', code) + }) + res.send({address: 'localip', port: '1024'}) + // then this (or similar) should be all we really need to add here, and we can do local-dev of the pipes in hunks/pipes/pipename.js and pipes/pipename.js + // so, do we spawn, or do we use workers ? + // awh yis it's workers, messages easy, errors also OK to catch... nextup: draw the sys, what wraps what doesn't? mostly: want to be able to refresh / reload / restart remotely +}) + +// finally, we tell the thing to listen here: let port = 8080 -// and listen, app.listen(port) -// want to announce our existence, this just logs our IPs to the console: +// this just logs the processes IP's to the termina Object.keys(ifaces).forEach(function(ifname) { var alias = 0; @@ -93,20 +124,3 @@ Object.keys(ifaces).forEach(function(ifname) { ++alias; }); }); - - -/* -let begin = () => { - // setup to dish files, - ex.get('/', (req, res) => { - console.log('req /') - res.sendFile(__dirname + '/index.html') - }) - - http.listen(8080, () => { - console.log("is listening on 8080") - }) -} - -begin() -*/ diff --git a/gogetter.js b/gogetter.js index 767eb1cb682cb1544b240f58727ee1bad5847898..2be6b76bb9fc3d69d8a0537659333b221e823500 100644 --- a/gogetter.js +++ b/gogetter.js @@ -8,7 +8,7 @@ function GoGetter() { this.recursivePathSearch = (root, debug) => { return new Promise((resolve, reject) => { jQuery.get(`/fileList?path=${root}`, (resp) => { - console.log('resp at jq', resp) + //console.log('resp at jq', resp) resolve(resp) }) }) diff --git a/hunks/hunks.js b/hunks/hunks.js index 3ad3bfaf4b5ccb7f0886807bac767281c3af02ce..eae5b6f3a3c8a8731f3954768f40c77e69c69799 100644 --- a/hunks/hunks.js +++ b/hunks/hunks.js @@ -325,6 +325,10 @@ function deepCopy(obj) { // turns out parse/stringify is the fastest, //console.log('to dc', obj) //console.log(JSON.stringify(obj)) + if(obj instanceof ImageData) { + let newData = new ImageData(obj.data, obj.width, obj.height) + return newData + } return JSON.parse(JSON.stringify(obj)) } diff --git a/hunks/image/readpng_leo.js b/hunks/image/readpng_leo.js new file mode 100644 index 0000000000000000000000000000000000000000..2426ce644dad34f3fcf591cb48bac7bb538e620b --- /dev/null +++ b/hunks/image/readpng_leo.js @@ -0,0 +1,107 @@ +/* + +hunk template + +*/ + +// these are ES6 modules +import { + Hunkify, + Input, + Output, + State +} from '../hunks.js' + +import { + html, + svg, + render +} from 'https://unpkg.com/lit-html?module'; + +export default function UploadPNG() { + // this fn attaches handles to our function-object, + Hunkify(this) + + const imageOut = new Output('RGBA', 'Image', this) + this.outputs.push(imageOut) + + this.init = () => { + this.dom = $('<div>').get(0) + } + + //import + const png_read_handler = (e) => { + const reader = new FileReader(); + + const files = e.target.files; + const file = files[0]; + + const img = new Image(); + img.file = file; + + const loader = (aImg) => (e) => { + aImg.src = e.target.result; + + aImg.onload = () => { + const canvas = document.getElementById("wackyAndTotallyUniqueID2"); + const ctx = canvas.getContext("2d"); + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.drawImage(aImg, 0, 0); + + // string other functions here + const data = getImageDataRGBA(); + // console.log("data", data); + + imageOut.put(data) + // console.log(data); + // console.log("copied", JSON.stringify(data)); + } + + }; + reader.onload = loader(img); + reader.readAsDataURL(file); + } + + // get image data: RGBA + const getImageDataRGBA = () => { //should add dpi term here + const canvas = document.getElementById("wackyAndTotallyUniqueID2"); + const ctx = canvas.getContext("2d"); + const imageRGBA = ctx.getImageData(0, 0, canvas.width, canvas.height); + return imageRGBA; + } + + this.onload = () => { + const target = $('<div>').get(0); + $(this.dom).append(target); + + const view = html ` + <style> + #wackyAndTotallyUniqueID2 { + border: 1px solid black; + margin: 5px; + } + + #wackyAndTotallyUniqueID { + position: absolute; + left: 10px; + top: 350px; + } + </style> + <div>Upload!</div> + <canvas + id="wackyAndTotallyUniqueID2" + width=300 + height=300> + </canvas> + <input + id="wackyAndTotallyUniqueID" + type="file" + accept="image/png" + @input=${(e) => png_read_handler(e)}></input> + ` + render(view, target); + } + + + this.loop = () => {} +} diff --git a/hunks/image/thresholdrgba_leo.js b/hunks/image/thresholdrgba_leo.js new file mode 100644 index 0000000000000000000000000000000000000000..fcd4d6f3805778f6a3a6c6e9c99a14b1e119d5f5 --- /dev/null +++ b/hunks/image/thresholdrgba_leo.js @@ -0,0 +1,109 @@ +/* + +hunk template + +*/ + +// these are ES6 modules +import { + Hunkify, + Input, + Output, + State +} from '../hunks.js' + +import { + html, + svg, + render +} from 'https://unpkg.com/lit-html?module'; + + +export default function Threshold() { + // this fn attaches handles to our function-object, + Hunkify(this) + + // inputs + let imageIn = new Input('RGBA', 'Image', this); + this.inputs.push(imageIn); + + // states + let threshold = new State('number', 'Threshold', .5); + this.states.push(threshold); + + // outputs + let imageOut = new Output('RGBA', 'Image', this); + this.outputs.push(imageOut); + + // Helper Functions + const thresholdRGBA = (imageRGBA, threshold) => { + console.log(imageRGBA) + const w = imageRGBA.width; + const h = imageRGBA.height; + const buf = imageRGBA.data; + const t = threshold; + + let r, g, b, a, i; + for (var row = 0; row < h; ++row) { + for (var col = 0; col < w; ++col) { + r = buf[(h - 1 - row) * w * 4 + col * 4 + 0]; + g = buf[(h - 1 - row) * w * 4 + col * 4 + 1]; + b = buf[(h - 1 - row) * w * 4 + col * 4 + 2]; + a = buf[(h - 1 - row) * w * 4 + col * 4 + 3]; + i = (r + g + b) / (3 * 255); + + let val; + if (a === 0) { + val = 255; + } else if (i > t) { + val = 255; + } else { + val = 0; + } + + buf[(h - 1 - row) * w * 4 + col * 4 + 0] = val; + buf[(h - 1 - row) * w * 4 + col * 4 + 1] = val; + buf[(h - 1 - row) * w * 4 + col * 4 + 2] = val; + buf[(h - 1 - row) * w * 4 + col * 4 + 3] = 255; + } + } + + const imgdata = new ImageData(buf, w, h) + + return imgdata; + } + + // view + // as is tradition + this.dom = {} + this.init = () => { + this.dom = $('<div>').get(0) + } + + this.onload = () => { + const target = $('<div>').get(0); + $(this.dom).append(target); + + const view = html ` + <button + @mousedown=${() => { + // console.log("imageIn", imageIn.get()); + // console.log("copied", deepCopy(imageIn)); + const newOut = thresholdRGBA(imageIn.get(), threshold.value) + imageOut.put(newOut); + }}> + Calculate + </button> + ` + + render(view, target); + } + + //loop + this.loop = () => { + if(imageIn.io() && !imageOut.io()){ + const thresholded = thresholdRGBA(imageIn.get(), threshold.value) + imageOut.put(thresholded) + } + } +} diff --git a/hunks/pipes/pipetemplate.js b/hunks/pipes/pipetemplate.js new file mode 100644 index 0000000000000000000000000000000000000000..9a7c5f3740c72ff4535a60de8d3772ae9c416149 --- /dev/null +++ b/hunks/pipes/pipetemplate.js @@ -0,0 +1,34 @@ +/* + +pipes are websocket-having devices that commune with our server, +this is a scratch / example of one such object + +*/ + +import { + Hunkify, + Input, + Output, + State +} from '../hunks.js' + +export default function Pipe() { + Hunkify(this) + let debug = true + + let statusMessage = new State('string', 'status', 'closed') + let retryButton = new State('boolean', 'retry', false) + this.states.push(statusMessage, retryButton) + + // coming merge of init and onload, however: + this.init = () => { + // hijack ajax to ask for a websocket + jQuery.get('pipeHookup/pipetemplate.js', (data) => { + console.log('pipe jquery data', data) + }) + } + + this.loop = () => { + // ws status, ws messages ... + } +} diff --git a/hunks/pipes/vfpt.js b/hunks/pipes/vfpt.js new file mode 100644 index 0000000000000000000000000000000000000000..5627185e97dd63dbe09604aaf9e8d56d1211a033 --- /dev/null +++ b/hunks/pipes/vfpt.js @@ -0,0 +1,164 @@ +/* + +very fast ~~picket ship~~ pipe transport + +*/ + +import { + Hunkify, + Input, + Output, + State +} from '../hunks.js' + +function VFPT() { + Hunkify(this) + + let debug = false + + let dtin = new Input('byteArray', 'data', this) + this.inputs.push(dtin) + + let dtout = new Output('byteArray', 'data', this) + this.outputs.push(dtout) + + // TODO is tackling state sets / updates / onupdate fn's + // this is hunk -> manager commune ... + let statusMessage = new State('string', 'status', 'closed') + let retryCountHandle = new State('number', 'retrycount', 3) + let resetRetryHandle = new State('boolean', 'retryreset', false) + let addressState = new State('string', 'address', '127.0.0.1') + let portState = new State('number', 'port', 2042) + this.states.push(statusMessage, retryCountHandle, resetRetryHandle, addressState, portState) + + // this ws is a client, + let ws = {} + let url = 'ws://127.0.0.1:2020' + this.outbuffer = new Array() + + this.init = () => { + setTimeout(startWs, 500) + } + + resetRetryHandle.change = (value) => { + retryCountHandle.set(3) + startWs() + // to actually change the value, we would do: + // resetRetryHandle.set(value) + } + + let startWs = () => { + // manager calls this once + // it is loaded and state is updated (from program) + url = 'ws://' + addressState.value + ':' + portState.value + this.log(`attempt start ws at ${url}`) + ws = new WebSocket(url) + ws.binaryType = "arraybuffer" + ws.onopen = (evt) => { + this.log('ws opened') + statusMessage.set('open') + } + ws.onerror = (evt) => { + this.log('ws error, will reset to check') + console.log('ws error:', evt) + if(debug) console.log(evt) + statusMessage.set('error') + setCheck(500) + } + ws.onclose = (evt) => { + this.log('ws close') + setCheck(500) + } + ws.onmessage = (message) => { + // this should be a buffer + if(debug) console.log('WS receives', message.data) + // tricks? + // ok, message.data is a blob, we know it's str8 up bytes, want that + // as an array + let msgAsArray = new Uint8Array(message.data) + // it's messy, yep! + let msgAsStdArray = Array.from(msgAsArray) + if(debug) console.log('WS receive, as an array:', msgAsArray); + if (dtout.ie && this.outbuffer.length === 0) { + dtout.put(msgAsStdArray) + } else { + this.outbuffer.push(msgAsStdArray) + } + } + statusMessage.set('ws initialized...') + } + + let checking = false + + let setCheck = (ms) => { + if (checking) { + // noop + } else { + setTimeout(checkWsStatus, ms) + checking = true + } + } + + let checkWsStatus = () => { + let retrycount = retryCountHandle.value - 1 + if (retrycount < 1) { + // give up + statusMessage.set('not connected') + retryCountHandle.set(0) + checking = false + } else { + retryCountHandle.set(retrycount) + checking = false + this.log('CHECKING STATUS') + switch (ws.readyState) { + case WebSocket.CONNECTING: + this.log('ws is in process of connecting...') + break + case WebSocket.OPEN: + this.log('is open') + break + case WebSocket.CLOSING: + this.log('is closing') + break + case WebSocket.CLOSED: + this.log('is closed, retrying ...') + startWs() + break + default: + throw new Error('nonsensical result at ws readystate check for ws') + break + } + } + } + + // override default change f'n + retryCountHandle.change = (value) => { + this.log('retrycount reset') + retryCountHandle.set(value) + setCheck(10) + } + + this.loop = () => { + // something like if(ws !== null && ws.isopen) + // if we have an open port, and have bytes to send downstream, + if (ws !== null && ws.readyState === 1) { + // no buffering + if (dtin.io()) { + let arr = dtin.get() + if(debug) console.log('WS transmission as array', arr) + let bytesOut = Uint8Array.from(arr) + // HERE insertion -> buffer.from() ? + if(debug) console.log("WS sending buffer", bytesOut.buffer) + ws.send(bytesOut.buffer) + } + } + + // check if we have outgoing to pass along + if (this.outbuffer.length > 0 && !dtout.io()) { + dtout.put(this.outbuffer.shift()) + } + + } +} + +export default VFPT diff --git a/hunks/comm/websocketclient.js b/hunks/pipes/websocketclient.js similarity index 100% rename from hunks/comm/websocketclient.js rename to hunks/pipes/websocketclient.js diff --git a/pipes/pipetemplate.js b/pipes/pipetemplate.js new file mode 100644 index 0000000000000000000000000000000000000000..913ae0271e9d5709c8f67cef1e3e7b6f92f8c314 --- /dev/null +++ b/pipes/pipetemplate.js @@ -0,0 +1,5 @@ +const { + parentPort +} = require('worker_threads') + +parentPort.postMessage('hello worker') diff --git a/scratch/test.png b/scratch/test.png new file mode 100644 index 0000000000000000000000000000000000000000..8d416e2339529d7e07a8fd2aeb97b6746bf65eb9 Binary files /dev/null and b/scratch/test.png differ