Skip to content

Commit

Permalink
implement FrameLimiter node
Browse files Browse the repository at this point in the history
  • Loading branch information
LeoDJ committed Feb 4, 2022
1 parent 865bc79 commit ddf65b6
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 4 deletions.
71 changes: 71 additions & 0 deletions src/common/worker/frameLimiterComponentWorker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import * as Rete from "rete";
import { NodeData, WorkerInputs } from "rete/types/core/data";
import { FrameLimiterConverter } from "../../converters/FrameLimiterConverter";
import { BackendInstanceManager } from "../backendInstanceManager";
import { ReteTask } from "../reteTask.interface";

interface FrameLimiterComponentParams {
fps: number;
}

export class FrameLimiterComponentWorker extends Rete.Component {
constructor(protected instMgr: BackendInstanceManager) {
super("Frame Limiter");
}

tasks: { [id: number]: ReteTask } = {};

[x: string]: any; // make Typescript happy (allow arbitrary member variables, as there is no definition file for Rete Tasks)
task = {
outputs: { frame: 'option' },
init: (task: ReteTask, node: NodeData) => { // gets called on engine.process
this.tasks[node.id] = task;
task.run(null); // init node instance with parameters from inputs (has to be done via the worker)
}
}

async builder(node: Rete.Node) {
// see node builder definition in webinterface/frontend/src/node-editor/components
}

initBackend = async (node: NodeData, inputs: WorkerInputs) => {
// get node parameters
const fps = (inputs['fps']?.length ? inputs['fps'][0] : node.data.fps) as number;

// check for undefined parameters
if (fps == undefined) {
return;
}

const params: FrameLimiterComponentParams = {
fps: fps
};

this.instMgr.createOrReconfigureInstance(node, params, () =>
new FrameLimiterConverter(fps)
);
}

async worker(node: NodeData, inputs: WorkerInputs, data: any) {
// TODO: do parameter getting and precalculation only once during init
if (data === null) {
this.closed = ['frame']; // stop propagating event
this.component.initBackend(node, inputs); // worker is run outside of current class context, so we need to acess initBackend via .component
}
else if (this.component.instMgr.getInstance(node)?.instance) {
let frame = await this.component.instMgr.getInstance(node).instance.convert(data.frame);
if (frame !== null) {
this.closed = []; // enable propagating event again
data.fromId = node.id;
}
else {
this.closed = ['frame']; // stop propagating event
}

}
else {
this.closed = ['frame']; // stop propagating event
}

}
}
22 changes: 22 additions & 0 deletions src/converters/FrameLimiterConverter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Frame } from "../common/frame.interface";

export class FrameLimiterConverter {
lastFrame = 0;
minimumFrameTime: number; // ms

constructor(protected fps: number) {
this.minimumFrameTime = 1000 / fps;
}

// returns null, if frame needs to be dropped
async convert(frame: Frame): Promise<Frame> {
if ((Date.now() - this.lastFrame) > this.minimumFrameTime) {
this.lastFrame = Date.now();
return frame;
}
else {
return null;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import * as Rete from "rete";
import { NodeData, WorkerInputs, WorkerOutputs } from "rete/types/core/data";
import { NumControl } from "../controls/numControl";
import { FrameSocket, NumSocket } from "../sockets/sockets";

export class FrameLimiterComponent extends Rete.Component {
constructor() {
super("Frame Limiter");
}

async builder(node: Rete.Node) {
const fps = new Rete.Input('fps', "FPS", NumSocket);
const framesIn = new Rete.Input('frameIn', "Frame Input", FrameSocket);
const frameOut = new Rete.Output('frame', "Frame", FrameSocket);

fps.addControl(new NumControl(this.editor, 'fps', undefined, undefined, 0, undefined, 0.01));

node.addInput(fps);
node.addInput(framesIn);
node.addOutput(frameOut);
}

worker(node: NodeData, inputs: WorkerInputs, outputs: WorkerOutputs) {
// do nothing for now
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,20 @@ interface NumControlProps {
ikey: string,
onChange: (val: number) => void,
readonly: boolean,
min: number,
max: number,
step: number
}

export class NumControl extends Rete.Control {
component: any;
props: NumControlProps;
vueContext: any;

constructor(emitter: NodeEditor | null, key: string, readonly?: boolean, onChange?: (val: number) => void) {
constructor(emitter: NodeEditor | null, key: string, readonly?: boolean, onChange?: (val: number) => void, min?: number, max?: number, step?: number) {
super(key);
this.component = VueNumControl;
this.props = { emitter, ikey: key, readonly, onChange };
this.props = { emitter, ikey: key, readonly, onChange, min, max, step };
}

setValue(val) {
Expand Down
17 changes: 15 additions & 2 deletions src/webinterface/frontend/src/node-editor/controls/numControl.vue
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
<template>
<input type="number" :readonly="readonly" :value="value" :placeholder="ikey" @input="change($event)" @dblclick.stop="" @pointerdown.stop="" @pointermove.stop="" @wheel.stop=""/>
<input
type="number"
:readonly="readonly"
:value="value"
:placeholder="ikey"
:min="min"
:max="max"
:step="step"
@input="change($event)"
@dblclick.stop=""
@pointerdown.stop=""
@pointermove.stop=""
@wheel.stop=""
/>
</template>

<script>
export default {
props: ["readonly", "emitter", "ikey", "onChange", "getData", "putData"],
props: ["readonly", "emitter", "ikey", "onChange", "getData", "putData", "min", "max", "step"],
data() {
return {
Expand Down
2 changes: 2 additions & 0 deletions src/webinterface/frontend/src/node-editor/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import Vue from "vue";
import { Tpm2NetInputComponent } from "./components/tpm2NetInputComponent";
import { OpcInputComponent } from "./components/opcInputComponent";
import { SerialOutputComponent } from "./components/serialOutputComponent";
import { FrameLimiterComponent } from "./components/frameLimiterComponent";

// in dev mode, UI is hosted on different port, could probably be solved more elegant
const apiUrl = Vue.config.devtools ? `http://${window.location.hostname}:8080/api/nodeEditor` : '/api/nodeEditor';
Expand Down Expand Up @@ -68,6 +69,7 @@ export default async function (container: HTMLElement) {
new OpcInputComponent(),
new Tpm2NetInputComponent(),
new MultiplexerComponent(),
new FrameLimiterComponent(),
new FrameMapComponent(),
new PixelMapComponent(),
new SplitComponent(),
Expand Down
2 changes: 2 additions & 0 deletions src/webinterface/nodeEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { PixelMapComponentWorker } from "../common/worker/pixelMapComponentWorke
import { OPCMultiOutputComponentWorker } from "../common/worker/opcMultiOutputComponentWorker";
import { Tpm2NetInputComponentWorker } from "../common/worker/tpm2NetInputComponentWorker";
import { SerialOutputComponentWorker } from "../common/worker/SerialOutputComponentWorker";
import { FrameLimiterComponentWorker } from "../common/worker/frameLimiterComponentWorker";


export class NodeEngine {
Expand All @@ -34,6 +35,7 @@ export class NodeEngine {
new OPCMultiOutputComponentWorker(this.instMgr),
new Tpm2NetInputComponentWorker(this.instMgr),
new SerialOutputComponentWorker(this.instMgr),
new FrameLimiterComponentWorker(this.instMgr),
];
this.engine = new Rete.Engine('[email protected]');
this.engine.use(TaskPlugin);
Expand Down

0 comments on commit ddf65b6

Please sign in to comment.