@luxonis/depthai-pipeline-lib 1.6.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/components/Node.js +10 -6
- package/dist/src/components/PipelineCanvas.d.ts +4 -1
- package/dist/src/components/PipelineCanvas.js +64 -11
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +1 -0
- package/dist/src/services/pipeline-state.d.ts +42 -0
- package/dist/src/services/pipeline-state.js +13 -0
- package/dist/src/services/pipeline.d.ts +2 -0
- package/package.json +62 -62
|
@@ -6,16 +6,20 @@ import { css } from '../styled-system/css/css.mjs';
|
|
|
6
6
|
import { DOCS_BASE_URL, NodesWithLinks, } from '../services/pipeline.js';
|
|
7
7
|
const NodeHandles = props => {
|
|
8
8
|
const { handles, type } = props;
|
|
9
|
-
return (_jsx(Flex, { full: true, direction: "column", align: type === 'input' ? 'start' : 'end', children: handles.map(({ type: handleType, id, blocking, queueSize, name }) => (_jsxs(Flex, { position: "relative", align: "end", direction: handleType === 'input' ? 'row' : 'row-reverse', children: [_jsx(Handle, { type: handleType === 'input' ? 'target' : 'source', position: handleType === 'input' ? Position.Left : Position.Right, id: name, isConnectable: false, className: css({
|
|
9
|
+
return (_jsx(Flex, { full: true, direction: "column", align: type === 'input' ? 'start' : 'end', children: handles.map(({ type: handleType, id, blocking, queueSize, name, maxQueueSize, fps }) => (_jsxs(Flex, { position: "relative", align: "end", direction: handleType === 'input' ? 'row' : 'row-reverse', children: [_jsx(Handle, { type: handleType === 'input' ? 'target' : 'source', position: handleType === 'input' ? Position.Left : Position.Right, id: name, isConnectable: false, className: css({
|
|
10
10
|
width: 'custom.handle.dot!',
|
|
11
11
|
height: 'custom.handle.dot!',
|
|
12
12
|
backgroundColor: blocking ? 'dark.warning!' : 'dark.success!',
|
|
13
13
|
border: 'none!',
|
|
14
|
-
}) }), _jsx(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
}) }), _jsx(NodeHandlesSubLabel, { type: type, name: name, queueSize: queueSize, maxQueueSize: maxQueueSize, fps: fps })] }, id))) }));
|
|
15
|
+
};
|
|
16
|
+
const NodeHandlesSubLabel = props => {
|
|
17
|
+
const { type, name, queueSize, maxQueueSize, fps } = props;
|
|
18
|
+
return (_jsx(SubLabel, { className: css({
|
|
19
|
+
...(type === 'input' //
|
|
20
|
+
? { marginLeft: 'xs' }
|
|
21
|
+
: { marginRight: 'xs' }),
|
|
22
|
+
}), text: `[${fps ? `${fps.toFixed(1)} | ` : ''}${maxQueueSize ? `${maxQueueSize}/${queueSize}` : queueSize}] ${name}`, break: "none" }));
|
|
19
23
|
};
|
|
20
24
|
export const PipelineNode = props => {
|
|
21
25
|
const { data: node } = props;
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { FlexProps } from '@luxonis/common-fe-components';
|
|
3
|
-
import type { Pipeline } from '../services/pipeline.js';
|
|
3
|
+
import type { ParsedNode, Pipeline } from '../services/pipeline.js';
|
|
4
|
+
import type { PipelineState } from '../services/pipeline-state.js';
|
|
4
5
|
export type PipelineCanvasProps = FlexProps & {
|
|
5
6
|
pipeline: Pipeline | null;
|
|
7
|
+
pipelineState: PipelineState[] | null;
|
|
6
8
|
};
|
|
9
|
+
export declare const updateNodesOnPipelineStateChange: (nodes: ParsedNode[], pipelineState: PipelineState[]) => ParsedNode[];
|
|
7
10
|
export declare const PipelineCanvas: React.FC<PipelineCanvasProps>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import React from 'react';
|
|
3
|
-
import { ReactFlow, ReactFlowProvider, useEdgesState,
|
|
3
|
+
import { ReactFlow, ReactFlowProvider, useEdgesState, useNodesState, useReactFlow, useStore, } from '@xyflow/react';
|
|
4
4
|
import { Flex, Header } from '@luxonis/common-fe-components';
|
|
5
5
|
import Dagre from '@dagrejs/dagre';
|
|
6
6
|
import { PipelineNode } from './Node.js';
|
|
@@ -86,15 +86,53 @@ const adjustNodes = (nodes) => {
|
|
|
86
86
|
});
|
|
87
87
|
return [...positionedGroups, ...positionedNodes];
|
|
88
88
|
};
|
|
89
|
-
const
|
|
89
|
+
export const updateNodesOnPipelineStateChange = (nodes, pipelineState) => {
|
|
90
|
+
const parsedNodes = [];
|
|
91
|
+
for (const node of nodes) {
|
|
92
|
+
const nodeState = pipelineState.find(state => state.id.toString() === node.id.toString());
|
|
93
|
+
const inputHandles = node.data.handles.input;
|
|
94
|
+
const outputHandles = node.data.handles.output;
|
|
95
|
+
const newInputHandles = [];
|
|
96
|
+
for (const inputHandle of inputHandles) {
|
|
97
|
+
const obj = { ...inputHandle };
|
|
98
|
+
const inputState = nodeState?.inputs[inputHandle.name];
|
|
99
|
+
if (inputState) {
|
|
100
|
+
obj.maxQueueSize = inputState.numQueued;
|
|
101
|
+
obj.fps = inputState.timing.fps;
|
|
102
|
+
}
|
|
103
|
+
newInputHandles.push(obj);
|
|
104
|
+
}
|
|
105
|
+
const newOutputHandles = [];
|
|
106
|
+
for (const outputHandle of outputHandles) {
|
|
107
|
+
const obj = { ...outputHandle };
|
|
108
|
+
const outputState = nodeState?.outputs[outputHandle.name];
|
|
109
|
+
if (outputState) {
|
|
110
|
+
obj.maxQueueSize = outputState.numQueued;
|
|
111
|
+
obj.fps = outputState.timing.fps;
|
|
112
|
+
}
|
|
113
|
+
newOutputHandles.push(obj);
|
|
114
|
+
}
|
|
115
|
+
parsedNodes.push({
|
|
116
|
+
...node,
|
|
117
|
+
data: {
|
|
118
|
+
...node.data,
|
|
119
|
+
handles: {
|
|
120
|
+
input: newInputHandles,
|
|
121
|
+
output: newOutputHandles,
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
return parsedNodes;
|
|
127
|
+
};
|
|
128
|
+
const PipelineCanvasBody = ({ pipeline, pipelineState, ...flexProps }) => {
|
|
90
129
|
const { fitView, setViewport, getViewport } = useReactFlow();
|
|
91
130
|
const autoArrangeRef = React.useRef(true);
|
|
92
131
|
const widthSelector = (state) => state.width;
|
|
93
132
|
const heightSelector = (state) => state.height;
|
|
94
133
|
const reactFlowWidth = useStore(widthSelector);
|
|
95
134
|
const reactFlowHeight = useStore(heightSelector);
|
|
96
|
-
const
|
|
97
|
-
const [isAutoLayoutingFinished, setIsAutoLayoutingFinished] = React.useState(false);
|
|
135
|
+
const [shouldFitAndResize, setShouldFitAndResize] = React.useState(false);
|
|
98
136
|
React.useEffect(() => {
|
|
99
137
|
void fitView();
|
|
100
138
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -106,17 +144,24 @@ const PipelineCanvasBody = ({ pipeline, ...flexProps }) => {
|
|
|
106
144
|
return;
|
|
107
145
|
}
|
|
108
146
|
const layouted = getLayoutedElements(pipeline?.nodes ?? [], pipeline?.edges ?? []);
|
|
109
|
-
|
|
147
|
+
const adjustedNodes = adjustNodes([...layouted.nodes]);
|
|
148
|
+
if (pipelineState) {
|
|
149
|
+
const updatedNodes = updateNodesOnPipelineStateChange(adjustedNodes, pipelineState);
|
|
150
|
+
setNodes([...updatedNodes]);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
setNodes([...adjustedNodes]);
|
|
154
|
+
}
|
|
110
155
|
setEdges([...layouted.edges]);
|
|
111
|
-
|
|
156
|
+
setShouldFitAndResize(true);
|
|
157
|
+
}, [pipeline?.edges, pipeline?.nodes, setEdges, setNodes, pipelineState]);
|
|
112
158
|
React.useEffect(() => {
|
|
113
|
-
if (
|
|
114
|
-
const
|
|
115
|
-
setNodes([...
|
|
116
|
-
setIsAutoLayoutingFinished(true);
|
|
159
|
+
if (pipelineState && nodes.length > 0) {
|
|
160
|
+
const updatedNodes = updateNodesOnPipelineStateChange(nodes, pipelineState);
|
|
161
|
+
setNodes([...updatedNodes]);
|
|
117
162
|
}
|
|
118
163
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
119
|
-
}, [
|
|
164
|
+
}, [pipelineState]);
|
|
120
165
|
const setViewportAndFit = async (x, y, zoom, nds) => {
|
|
121
166
|
await setViewport({ x, y, zoom });
|
|
122
167
|
await fitView({ nodes: nds });
|
|
@@ -131,6 +176,14 @@ const PipelineCanvasBody = ({ pipeline, ...flexProps }) => {
|
|
|
131
176
|
}
|
|
132
177
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
133
178
|
}, [nodes, edges]);
|
|
179
|
+
React.useEffect(() => {
|
|
180
|
+
if (shouldFitAndResize) {
|
|
181
|
+
const viewport = getViewport();
|
|
182
|
+
void setViewportAndFit(viewport.x, viewport.y, viewport.zoom, nodes);
|
|
183
|
+
setShouldFitAndResize(false);
|
|
184
|
+
}
|
|
185
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
186
|
+
}, [shouldFitAndResize]);
|
|
134
187
|
return (_jsxs(Flex, { align: "center", justify: "center", full: true, height: "container.xs", rounded: "common", border: "base", ...flexProps, children: [!pipeline && _jsx(Header, { text: "Loading pipeline..." }), pipeline && (_jsx(ReactFlow, { nodes: nodes, edges: edges, onNodeDragStart: () => (autoArrangeRef.current = false), onNodesChange: onNodesChange, fitView: true, nodeTypes: { generic: PipelineNode }, minZoom: 0.4 }))] }));
|
|
135
188
|
};
|
|
136
189
|
export const PipelineCanvas = props => (_jsx(ReactFlowProvider, { children: _jsx(PipelineCanvasBody, { ...props }) }));
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { parsePipeline } from './services/pipeline.js';
|
|
2
2
|
export type { Pipeline, RawPipelinePayload } from './services/pipeline.js';
|
|
3
|
+
export { parsePipelineState } from './services/pipeline-state.js';
|
|
4
|
+
export type { PipelineState, RawPipelineStatePayload } from './services/pipeline-state.js';
|
|
3
5
|
export { PipelineCanvas } from './components/PipelineCanvas.js';
|
|
4
6
|
export type { PipelineCanvasProps } from './components/PipelineCanvas.js';
|
package/dist/src/index.js
CHANGED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export type RawPipelineStatePayload = {
|
|
2
|
+
nodeStates: RawPipelineState[];
|
|
3
|
+
};
|
|
4
|
+
export type IOStates = {
|
|
5
|
+
[key: string]: {
|
|
6
|
+
numQueued: number;
|
|
7
|
+
timing: TimingWithFps;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
type Timing = {
|
|
11
|
+
averageMicrosRecent: number;
|
|
12
|
+
maxMicros: number;
|
|
13
|
+
maxMicrosRecent: number;
|
|
14
|
+
medianMicrosRecent: number;
|
|
15
|
+
minMicros: number;
|
|
16
|
+
minMicrosRecent: number;
|
|
17
|
+
stdDevMicrosRecent: number;
|
|
18
|
+
};
|
|
19
|
+
type TimingWithFps = {
|
|
20
|
+
durationStats: Timing;
|
|
21
|
+
fps: number;
|
|
22
|
+
};
|
|
23
|
+
export type RawPipelineState = [
|
|
24
|
+
number,
|
|
25
|
+
{
|
|
26
|
+
events: any[];
|
|
27
|
+
inputStates: IOStates;
|
|
28
|
+
inputsGetTiming: TimingWithFps;
|
|
29
|
+
mainLoopTiming: TimingWithFps;
|
|
30
|
+
otherTimings: object;
|
|
31
|
+
outputStates: IOStates;
|
|
32
|
+
outputsSendTiming: TimingWithFps;
|
|
33
|
+
state: number;
|
|
34
|
+
}
|
|
35
|
+
];
|
|
36
|
+
export type PipelineState = {
|
|
37
|
+
id: number;
|
|
38
|
+
inputs: IOStates;
|
|
39
|
+
outputs: IOStates;
|
|
40
|
+
};
|
|
41
|
+
export declare function parsePipelineState(rawPayload: RawPipelineStatePayload): PipelineState[];
|
|
42
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function parsePipelineState(rawPayload) {
|
|
2
|
+
const { nodeStates } = rawPayload;
|
|
3
|
+
const parsedNodeStates = [];
|
|
4
|
+
for (const [nodeId, nodeState] of nodeStates) {
|
|
5
|
+
const currentNode = {
|
|
6
|
+
id: nodeId,
|
|
7
|
+
inputs: nodeState.inputStates,
|
|
8
|
+
outputs: nodeState.outputStates,
|
|
9
|
+
};
|
|
10
|
+
parsedNodeStates.push(currentNode);
|
|
11
|
+
}
|
|
12
|
+
return parsedNodeStates;
|
|
13
|
+
}
|
package/package.json
CHANGED
|
@@ -1,64 +1,64 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
2
|
+
"name": "@luxonis/depthai-pipeline-lib",
|
|
3
|
+
"version": "1.8.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"main": "./dist/src/index.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "./build.sh",
|
|
9
|
+
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
|
10
|
+
"gen:styles": "panda codegen",
|
|
11
|
+
"prepublishOnly": "npm run build"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@dagrejs/dagre": "^1.1.3",
|
|
15
|
+
"@luxonis/common-fe-components": "1.25.9",
|
|
16
|
+
"@xyflow/react": "^12.0.4",
|
|
17
|
+
"postcss-import": "^16.1.0",
|
|
18
|
+
"postcss-nested": "^6.2.0",
|
|
19
|
+
"postcss-preset-env": "^10.0.0",
|
|
20
|
+
"rehype-sanitize": "^6.0.0"
|
|
21
|
+
},
|
|
22
|
+
"peerDependencies": {
|
|
23
|
+
"react": "^18.3.1 || ^19.0.0",
|
|
24
|
+
"react-dom": "^18.3.1 || ^19.0.0"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@pandacss/dev": "^0.53.0",
|
|
28
|
+
"@stylistic/eslint-plugin": "^2.6.1",
|
|
29
|
+
"@types/react": "^18.3.1 || ^19.0.0",
|
|
30
|
+
"@types/react-dom": "^18.3.1 || ^19.0.0",
|
|
31
|
+
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
|
32
|
+
"@typescript-eslint/parser": "^6.21.0",
|
|
33
|
+
"eslint": "^8.56.0",
|
|
34
|
+
"eslint-config-prettier": "^9.1.0",
|
|
35
|
+
"eslint-import-resolver-typescript": "^3.6.1",
|
|
36
|
+
"eslint-interactive": "^10.8.0",
|
|
37
|
+
"eslint-plugin-cypress": "^3.3.0",
|
|
38
|
+
"eslint-plugin-functional": "^6.0.0",
|
|
39
|
+
"eslint-plugin-github": "^4.10.1",
|
|
40
|
+
"eslint-plugin-import": "^2.29.1",
|
|
41
|
+
"eslint-plugin-prettier": "^5.1.3",
|
|
42
|
+
"eslint-plugin-react": "^7.33.2",
|
|
43
|
+
"eslint-plugin-react-hooks": "^4.6.0",
|
|
44
|
+
"eslint-plugin-react-refresh": "^0.4.5",
|
|
45
|
+
"postcss": "^8.4.31",
|
|
46
|
+
"prettier": "^3.2.5",
|
|
47
|
+
"prettier-eslint": "^16.3.0",
|
|
48
|
+
"typescript": "^5.2.2"
|
|
49
|
+
},
|
|
50
|
+
"overrides": {
|
|
51
|
+
"@radix-ui/react-alert-dialog": "1.0.5"
|
|
52
|
+
},
|
|
53
|
+
"files": [
|
|
54
|
+
"dist/src/*",
|
|
55
|
+
"dist/*.css"
|
|
56
|
+
],
|
|
57
|
+
"exports": {
|
|
58
|
+
".": "./dist/src/index.js",
|
|
59
|
+
"./styles": "./dist/index.css"
|
|
60
|
+
},
|
|
61
|
+
"publishConfig": {
|
|
62
|
+
"access": "public"
|
|
63
|
+
}
|
|
64
64
|
}
|