UNPKG

sweetpea

Version:

Signal and Web Component Enhanced Web Apps

167 lines (133 loc) 4.58 kB
import EventEmittter from 'event-emitter'; import {PortParameter, Parameter} from 'system-parameters'; export class SystemWorker extends EventEmittter { data; // function stage; queue; buffer; input = new PortParameter({description: "Input port for the node, located on the left side." }); output = new PortParameter({portDirection: "right", description: "Output port for the node, located on the right side." }); constructor({queue, buffer, stage, data}){ super(); this.stage = stage; //NOTE: this is the emitter this.queue = queue; this.buffer = buffer; this.data = data; } subscriptions = []; // {type:'list/item/value', id:'', run} collectGarbage(){ this.subscriptions.forEach(s=>s.subscription()) } set gc(subscription){ // shorthand for component level garbage collection this.subscriptions.push( {type:'gc-standard', id:'gc-'+this.subscriptions.length, subscription} ); } async connect(){ // console.log('connect', this.constructor.name); // Let's make the code a bit more readable const actor = this; const stage = this.stage; const queue = this.queue; const buffer = this.buffer; // When message is sent to this actor this.gc = actor.on('input', input => { // console.log(`${this.constructor.name} got input packet!`, input); queue.enqueue( input ); }); // When a new order is added to the queue this.gc = queue.on('enqueue', async order => { // console.log(`${this.constructor.name} queue order!`, order); if(order === undefined) throw new Error('Enqueue Work order must be an object'); const product = await this.process(order, this.data.parameters); // console.log(`${this.constructor.name} PRODUCT`, product); // EARLY EXIT if(product === undefined || product === null){ queue.remove(order.id); return; // STOP RUN, nothing to pass along, not return value } // CONTINUE // Store the finished product in the buffer if(Array.isArray(product)){ for (const item of product) { buffer.enbuffer(item); } }else{ buffer.enbuffer(product); } queue.remove(order.id); }); // When a product is added to the buffer this.gc = buffer.on('enbuffer', product => { // Send the product out to another part of the system actor.send('output', product ); // Remove the product from the buffer since it's no longer needed buffer.remove(product); }); // Control protocol aka DATA PULL this.gc = actor.on('control', async control=>{ // console.log(`${this.constructor.name} got control packet!`, control); switch(control.event) { case 'request': await actor.transmit(control.event||1, this.data.parameters); break; case 'quiesce': actor.pause(); break; default: console.info('unknown control', control.event); } }); } // CONTROL transmit(max=Infinity){ //console.log('BUFFER TRANSMIT', this.constructor.name); this.#pause = false; let sentCount = 0; for (const product of this.buffer) { if(this.#pause) break; this.send('output', product ); sentCount++; if(sentCount>max) break; } } #pause = false; pause(){ this.#pause = true; } async connected(){ // connect to stage // subscribe } async disconnect(){ // disconnect from stage // unsubscribe } async disconnected(){ // disconnect from stage // unsubscribe } async destroy(){ // disconnect from stage console.log('Destroy!', this.stage.stats()); this.removeAllListeners(); this.collectGarbage() console.log('Destroyed!', this.stage.stats()); } async diagnostic(){ } async process(input){ } // util get parameters(){ const parameters = []; const properties = Object.getOwnPropertyNames(this); properties.forEach(name => { const value = this[name]; if (value && value.subscribe) { if (value instanceof Parameter) { const type = value.constructor.name.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase().replace(/-parameter$/,''); const label = name.replace(/([A-Z])/,' $1').split(' ').map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(' '); const entry = {name, type, label, ...value.value} parameters.push( entry ); } } }); return parameters; } } export default {SystemWorker}