@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.
@@ -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(SubLabel, { className: css({
15
- ...(type === 'input' //
16
- ? { marginLeft: 'xs' }
17
- : { marginRight: 'xs' }),
18
- }), text: type === 'input' ? `[${queueSize}] ${name}` : name, break: "none" })] }, id))) }));
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, useNodesInitialized, useNodesState, useReactFlow, useStore, } from '@xyflow/react';
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 PipelineCanvasBody = ({ pipeline, ...flexProps }) => {
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 nodesInitialized = useNodesInitialized();
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
- setNodes([...layouted.nodes]);
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
- }, [pipeline?.edges, pipeline?.nodes, setEdges, setNodes]);
156
+ setShouldFitAndResize(true);
157
+ }, [pipeline?.edges, pipeline?.nodes, setEdges, setNodes, pipelineState]);
112
158
  React.useEffect(() => {
113
- if (nodesInitialized && !isAutoLayoutingFinished) {
114
- const adjustedNodes = adjustNodes(nodes);
115
- setNodes([...adjustedNodes]);
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
- }, [nodesInitialized, isAutoLayoutingFinished]);
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 }) }));
@@ -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
@@ -1,2 +1,3 @@
1
1
  export { parsePipeline } from './services/pipeline.js';
2
+ export { parsePipelineState } from './services/pipeline-state.js';
2
3
  export { PipelineCanvas } from './components/PipelineCanvas.js';
@@ -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
+ }
@@ -43,6 +43,8 @@ export type ParsedHandle = {
43
43
  type: 'input' | 'output';
44
44
  blocking: boolean;
45
45
  queueSize: number;
46
+ maxQueueSize?: number;
47
+ fps?: number;
46
48
  };
47
49
  export type ParsedNode = Node<{
48
50
  id: string;
package/package.json CHANGED
@@ -1,64 +1,64 @@
1
1
  {
2
- "name": "@luxonis/depthai-pipeline-lib",
3
- "version": "1.6.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
- }
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
  }