@wandelbots/wandelbots-js-react-components 2.10.3 → 2.11.0-pr.feat-collision-scene.263.cb851a8

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.
Files changed (58) hide show
  1. package/dist/components/3d-viewport/collider/ColliderCollection.d.ts +11 -0
  2. package/dist/components/3d-viewport/collider/ColliderCollection.d.ts.map +1 -0
  3. package/dist/components/3d-viewport/collider/ColliderElement.d.ts +10 -0
  4. package/dist/components/3d-viewport/collider/ColliderElement.d.ts.map +1 -0
  5. package/dist/components/3d-viewport/collider/CollisionMotionGroup.d.ts +14 -0
  6. package/dist/components/3d-viewport/collider/CollisionMotionGroup.d.ts.map +1 -0
  7. package/dist/components/3d-viewport/collider/CollisionScene.d.ts +16 -0
  8. package/dist/components/3d-viewport/collider/CollisionScene.d.ts.map +1 -0
  9. package/dist/components/3d-viewport/collider/colliderShapeToBufferGeometry.d.ts +4 -0
  10. package/dist/components/3d-viewport/collider/colliderShapeToBufferGeometry.d.ts.map +1 -0
  11. package/dist/components/robots/DHRobot.d.ts.map +1 -1
  12. package/dist/components/safetyBar/ControllerTypeIndicator.d.ts +9 -0
  13. package/dist/components/safetyBar/ControllerTypeIndicator.d.ts.map +1 -0
  14. package/dist/components/safetyBar/IndicatorWithExplanation.d.ts +15 -0
  15. package/dist/components/safetyBar/IndicatorWithExplanation.d.ts.map +1 -0
  16. package/dist/components/safetyBar/OperationModeIndicator.d.ts +9 -0
  17. package/dist/components/safetyBar/OperationModeIndicator.d.ts.map +1 -0
  18. package/dist/components/safetyBar/SafetyBar.d.ts +11 -0
  19. package/dist/components/safetyBar/SafetyBar.d.ts.map +1 -0
  20. package/dist/components/safetyBar/SafetyStateIndicator.d.ts +9 -0
  21. package/dist/components/safetyBar/SafetyStateIndicator.d.ts.map +1 -0
  22. package/dist/components/utils/dhParameter.d.ts +7 -0
  23. package/dist/components/utils/dhParameter.d.ts.map +1 -0
  24. package/dist/components/utils/errorHandling.d.ts +1 -0
  25. package/dist/components/utils/errorHandling.d.ts.map +1 -1
  26. package/dist/index.cjs +36 -35
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.d.ts +1 -0
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +4165 -3759
  31. package/dist/index.js.map +1 -1
  32. package/package.json +15 -16
  33. package/src/components/3d-viewport/collider/ColliderCollection.tsx +34 -0
  34. package/src/components/3d-viewport/collider/ColliderElement.tsx +32 -0
  35. package/src/components/3d-viewport/collider/CollisionMotionGroup.tsx +88 -0
  36. package/src/components/3d-viewport/collider/CollisionScene.tsx +59 -0
  37. package/src/components/3d-viewport/collider/colliderShapeToBufferGeometry.ts +36 -0
  38. package/src/components/robots/DHRobot.tsx +22 -44
  39. package/src/components/safetyBar/ControllerTypeIndicator.tsx +59 -0
  40. package/src/components/safetyBar/IndicatorWithExplanation.tsx +117 -0
  41. package/src/components/safetyBar/OperationModeIndicator.tsx +76 -0
  42. package/src/components/safetyBar/SafetyBar.tsx +43 -0
  43. package/src/components/safetyBar/SafetyStateIndicator.tsx +131 -0
  44. package/src/components/safetyBar/icons/controller-type-physical.svg +1 -0
  45. package/src/components/safetyBar/icons/controller-type-virtual.svg +1 -0
  46. package/src/components/safetyBar/icons/operation-mode-automatic.svg +1 -0
  47. package/src/components/safetyBar/icons/operation-mode-error.svg +1 -0
  48. package/src/components/safetyBar/icons/operation-mode-manual.svg +1 -0
  49. package/src/components/safetyBar/icons/safety-state-error.svg +1 -0
  50. package/src/components/safetyBar/icons/safety-state-estop.svg +1 -0
  51. package/src/components/safetyBar/icons/safety-state-manual-action-required.svg +1 -0
  52. package/src/components/safetyBar/icons/safety-state-normal.svg +1 -0
  53. package/src/components/safetyBar/icons/safety-state-stop.svg +1 -0
  54. package/src/components/utils/dhParameter.ts +58 -0
  55. package/src/components/utils/errorHandling.ts +4 -0
  56. package/src/i18n/locales/de/translations.json +27 -1
  57. package/src/i18n/locales/en/translations.json +27 -1
  58. package/src/index.ts +1 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wandelbots/wandelbots-js-react-components",
3
- "version": "2.10.3",
3
+ "version": "2.11.0-pr.feat-collision-scene.263.cb851a8",
4
4
  "description": "React UI toolkit for building applications on top of the Wandelbots platform",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -55,16 +55,16 @@
55
55
  "@rollup/plugin-node-resolve": "^16.0.0",
56
56
  "@rollup/plugin-terser": "^0.4.4",
57
57
  "@rollup/plugin-typescript": "^12.1.2",
58
- "@storybook/addon-docs": "^8.5.8",
59
- "@storybook/addon-essentials": "^8.5.8",
60
- "@storybook/addon-interactions": "^8.5.8",
61
- "@storybook/blocks": "^8.5.8",
62
- "@storybook/react": "^8.5.8",
63
- "@storybook/csf-tools": "^8.5.8",
64
- "@storybook/react-vite": "^8.5.8",
65
- "@storybook/test": "^8.5.8",
58
+ "@storybook/addon-docs": "^8.6.2",
59
+ "@storybook/addon-essentials": "^8.6.2",
60
+ "@storybook/addon-interactions": "^8.6.2",
61
+ "@storybook/blocks": "^8.6.2",
62
+ "@storybook/react": "^8.6.2",
63
+ "@storybook/csf-tools": "^8.6.2",
64
+ "@storybook/react-vite": "^8.6.2",
65
+ "@storybook/test": "^8.6.2",
66
66
  "@storybook/test-runner": "^0.21.3",
67
- "@storybook/types": "^8.5.8",
67
+ "@storybook/types": "^8.6.2",
68
68
  "@svgr/rollup": "^8.1.0",
69
69
  "@types/lodash-es": "^4.17.12",
70
70
  "@types/react": "^18.3.12",
@@ -90,7 +90,7 @@
90
90
  "rollup-plugin-peer-deps-external": "^2.2.4",
91
91
  "rollup-plugin-postcss": "^4.0.2",
92
92
  "semantic-release": "^24.2.3",
93
- "storybook": "^8.5.8",
93
+ "storybook": "^8.6.2",
94
94
  "storybook-dark-mode": "^4.0.2",
95
95
  "storybook-preset-inline-svg": "^1.0.1",
96
96
  "ts-dedent": "^2.2.0",
@@ -100,10 +100,9 @@
100
100
  "vite-plugin-svgr": "^4.3.0",
101
101
  "vitest": "^3.0.7",
102
102
  "wait-on": "^8.0.2",
103
- "@wandelbots/wandelbots-js": "^1.16.0"
103
+ "@wandelbots/wandelbots-js": "^1.17.1"
104
104
  },
105
105
  "peerDependencies": {
106
- "@wandelbots/wandelbots-js": "^1.12.0",
107
106
  "@emotion/react": "^11.11.1",
108
107
  "@emotion/styled": "^11.11.0",
109
108
  "@mui/icons-material": "^6",
@@ -113,7 +112,7 @@
113
112
  "@react-three/fiber": "~8.18.0",
114
113
  "react": "~18.3.1",
115
114
  "react-dom": "~18.3.1",
116
- "three": ">=0.173",
115
+ "three": ">=0.174",
117
116
  "three-stdlib": ">=2"
118
117
  },
119
118
  "peerDependenciesMeta": {
@@ -123,13 +122,13 @@
123
122
  },
124
123
  "dependencies": {
125
124
  "@monaco-editor/react": "^4.7.0",
126
- "@shikijs/monaco": "^3.0.0",
125
+ "@shikijs/monaco": "^3.1.0",
127
126
  "i18next-browser-languagedetector": "^8.0.4",
128
127
  "lodash-es": "^4.17.21",
129
128
  "mobx": "^6.13.6",
130
129
  "mobx-react-lite": "^4.1.0",
131
130
  "react-error-boundary": "^5.0.0",
132
131
  "react-i18next": "^15.4.1",
133
- "shiki": "^3.0.0"
132
+ "shiki": "^3.1.0"
134
133
  }
135
134
  }
@@ -0,0 +1,34 @@
1
+ import type { GroupProps } from "@react-three/fiber"
2
+ import type { Collider } from "@wandelbots/wandelbots-js"
3
+ import ColliderElement from "./ColliderElement"
4
+
5
+ export type MeshChildrenProvider = (
6
+ key: string,
7
+ collider: Collider,
8
+ ) => React.ReactNode
9
+
10
+ type ColliderCollectionProps = {
11
+ name?: string
12
+ colliders: Record<string, Collider>
13
+ meshChildrenProvider: MeshChildrenProvider
14
+ } & GroupProps
15
+
16
+ export default function ColliderCollection({
17
+ name,
18
+ colliders,
19
+ meshChildrenProvider,
20
+ ...props
21
+ }: ColliderCollectionProps) {
22
+ return (
23
+ <group name={name} {...props}>
24
+ {Object.entries(colliders).map(([colliderKey, collider]) => (
25
+ <ColliderElement
26
+ key={colliderKey}
27
+ name={colliderKey}
28
+ collider={collider}
29
+ children={meshChildrenProvider(colliderKey, collider)}
30
+ />
31
+ ))}
32
+ </group>
33
+ )
34
+ }
@@ -0,0 +1,32 @@
1
+ import type { Collider } from "@wandelbots/wandelbots-js"
2
+ import type React from "react"
3
+ import * as THREE from "three"
4
+ import { colliderShapeToBufferGeometry } from "./colliderShapeToBufferGeometry"
5
+
6
+ type ColliderElementProps = {
7
+ name?: string
8
+ collider: Collider
9
+ children?: React.ReactNode
10
+ }
11
+
12
+ export default function ColliderElement({
13
+ name,
14
+ collider,
15
+ children,
16
+ }: ColliderElementProps) {
17
+ const position = collider.pose?.position ?? [0, 0, 0]
18
+ const rotation = collider.pose?.orientation ?? [0, 0, 0]
19
+ if (collider.margin) {
20
+ console.warn(`${name} margin is not supported`)
21
+ }
22
+ return (
23
+ <mesh
24
+ name={name}
25
+ position={new THREE.Vector3(position[0], position[1], position[2]).divideScalar(1000)}
26
+ rotation={new THREE.Euler(rotation[0], rotation[1], rotation[2], "XYZ")}
27
+ geometry={colliderShapeToBufferGeometry(collider.shape)}
28
+ >
29
+ {children}
30
+ </mesh>
31
+ )
32
+ }
@@ -0,0 +1,88 @@
1
+ import type {
2
+ CollisionMotionGroup,
3
+ DHParameter,
4
+ MotionGroupStateResponse,
5
+ } from "@wandelbots/wandelbots-js"
6
+ import * as THREE from "three"
7
+ import { DHRobot } from "../../robots/DHRobot"
8
+ import { getDHTransform } from "../../utils/dhParameter"
9
+ import ColliderCollection, {
10
+ type MeshChildrenProvider,
11
+ } from "./ColliderCollection"
12
+
13
+ type CollisionMotionGroupElementProps = {
14
+ name?: string
15
+ motionGroup: CollisionMotionGroup
16
+ rapidlyChangingMotionState: MotionGroupStateResponse
17
+ dhParameters: DHParameter[]
18
+ mountingPosition?: THREE.Vector3
19
+
20
+ meshChildrenProvider: MeshChildrenProvider
21
+ }
22
+
23
+ export default function CollisionMotionGroupElement({
24
+ name,
25
+ motionGroup,
26
+ rapidlyChangingMotionState,
27
+ dhParameters,
28
+ mountingPosition,
29
+ meshChildrenProvider,
30
+ }: CollisionMotionGroupElementProps) {
31
+ const joints = rapidlyChangingMotionState.state.joint_position?.joints
32
+ const jointTransform = new THREE.Matrix4()
33
+ const jointTransforms = getJointTransforms()
34
+ const toolTransform = jointTransform
35
+
36
+ console.log(joints, jointTransforms)
37
+ if (!joints) {
38
+ return null
39
+ }
40
+
41
+ function getJointTransforms() {
42
+ jointTransform.identity()
43
+
44
+ return [
45
+ jointTransform.clone(),
46
+ ...joints?.map((joint, jointIndex) => {
47
+ jointTransform.multiply(getDHTransform(dhParameters[jointIndex], joint))
48
+ return {
49
+ position: new THREE.Vector3().applyMatrix4(jointTransform),
50
+ rotation: new THREE.Euler().setFromRotationMatrix(jointTransform),
51
+ }
52
+ }),
53
+ ]
54
+ }
55
+
56
+ return (
57
+ <group
58
+ name={name}
59
+ position={mountingPosition ?? new THREE.Vector3(0, 0, 0)}
60
+ >
61
+ <DHRobot
62
+ dhParameters={dhParameters}
63
+ rapidlyChangingMotionState={rapidlyChangingMotionState}
64
+ />
65
+ {motionGroup.link_chain &&
66
+ motionGroup.link_chain.map((chainSegment, jointIndex) => (
67
+ <group name="robot" {...(jointTransforms ?? [])[jointIndex]}>
68
+ <ColliderCollection
69
+ colliders={chainSegment}
70
+ meshChildrenProvider={meshChildrenProvider}
71
+ />
72
+ </group>
73
+ ))}
74
+ {motionGroup.tool && (
75
+ <group
76
+ name="tool"
77
+ position={new THREE.Vector3(0, 0, 0).applyMatrix4(toolTransform)}
78
+ rotation={new THREE.Euler().setFromRotationMatrix(toolTransform)}
79
+ >
80
+ <ColliderCollection
81
+ colliders={motionGroup.tool}
82
+ meshChildrenProvider={meshChildrenProvider}
83
+ />
84
+ </group>
85
+ )}
86
+ </group>
87
+ )
88
+ }
@@ -0,0 +1,59 @@
1
+ import type {
2
+ CollisionScene,
3
+ DHParameter,
4
+ MotionGroupStateResponse,
5
+ } from "@wandelbots/wandelbots-js"
6
+ import * as THREE from "three"
7
+ import ColliderCollection, {
8
+ type MeshChildrenProvider,
9
+ } from "./ColliderCollection"
10
+ import CollisionMotionGroupElement from "./CollisionMotionGroup"
11
+
12
+ type MotionGroupData = {
13
+ state: MotionGroupStateResponse
14
+ dhParameters: DHParameter[]
15
+ mountingPosition: THREE.Vector3
16
+ }
17
+
18
+ type CollisionSceneElementProps = {
19
+ scene: CollisionScene
20
+ meshChildrenProvider: MeshChildrenProvider
21
+ motionGroupStates: Record<string, MotionGroupData>
22
+ }
23
+
24
+ export default function CollisionSceneElement({
25
+ scene,
26
+ motionGroupStates,
27
+ meshChildrenProvider,
28
+ }: CollisionSceneElementProps) {
29
+ const colliders = scene.colliders
30
+ const motionGroups = scene.motion_groups ?? {}
31
+ return (
32
+ <group>
33
+ {colliders && (
34
+ <ColliderCollection
35
+ meshChildrenProvider={meshChildrenProvider}
36
+ colliders={colliders}
37
+ />
38
+ )}
39
+ {Object.entries(motionGroups)
40
+ .filter(
41
+ ([motionGroupKey, _]) =>
42
+ motionGroupStates[motionGroupKey] !== undefined,
43
+ )
44
+ .map(([motionGroupKey, motionGroup]) => (
45
+ <CollisionMotionGroupElement
46
+ key={motionGroupKey}
47
+ name={motionGroupKey}
48
+ motionGroup={motionGroup}
49
+ mountingPosition={
50
+ motionGroupStates[motionGroupKey].mountingPosition
51
+ }
52
+ dhParameters={motionGroupStates[motionGroupKey].dhParameters}
53
+ meshChildrenProvider={meshChildrenProvider}
54
+ rapidlyChangingMotionState={motionGroupStates[motionGroupKey].state}
55
+ />
56
+ ))}
57
+ </group>
58
+ )
59
+ }
@@ -0,0 +1,36 @@
1
+ import type { ColliderShape } from "@wandelbots/wandelbots-js"
2
+ import * as THREE from "three"
3
+ import { ConvexGeometry } from "three-stdlib"
4
+
5
+ export function colliderShapeToBufferGeometry(
6
+ shape: ColliderShape,
7
+ ): THREE.BufferGeometry {
8
+ const shapeType = shape.shape_type
9
+ switch (shapeType) {
10
+ case "convex_hull":
11
+ return new ConvexGeometry(
12
+ shape.vertices.map(
13
+ (vertex) => new THREE.Vector3(vertex[0] / 1000, vertex[1] / 1000, vertex[2] / 1000),
14
+ ),
15
+ )
16
+ case "box":
17
+ return new THREE.BoxGeometry(shape.size_x / 1000, shape.size_y / 1000, shape.size_z / 1000)
18
+ case "sphere":
19
+ return new THREE.SphereGeometry(shape.radius / 1000)
20
+ case "capsule":
21
+ return new THREE.CapsuleGeometry(shape.radius / 1000, shape.cylinder_height / 1000)
22
+ case "cylinder":
23
+ return new THREE.CylinderGeometry(
24
+ shape.radius / 1000,
25
+ shape.radius / 1000,
26
+ shape.height / 1000,
27
+ )
28
+ case "rectangle": {
29
+ return new THREE.BoxGeometry(shape.size_x / 1000, shape.size_y / 1000, 0)
30
+ }
31
+ default: {
32
+ console.warn(`${shape.shape_type} is not supported`)
33
+ return new THREE.BufferGeometry()
34
+ }
35
+ }
36
+ }
@@ -1,8 +1,8 @@
1
1
  import { Line } from "@react-three/drei"
2
- import type { DHParameter } from "@wandelbots/wandelbots-api-client"
3
2
  import type * as THREE from "three"
4
- import { Matrix4, Quaternion, Vector3 } from "three"
3
+ import { Matrix4 } from "three"
5
4
  import type { LineGeometry } from "three/examples/jsm/lines/LineGeometry.js"
5
+ import { getDHLine, getDHLines } from "../utils/dhParameter"
6
6
  import RobotAnimator from "./RobotAnimator"
7
7
  import type { DHRobotProps } from "./SupportedRobot"
8
8
 
@@ -16,36 +16,11 @@ export function DHRobot({
16
16
  }: DHRobotProps) {
17
17
  // reused in every update
18
18
  const accumulatedMatrix = new Matrix4()
19
-
20
- // Updates accumulatedMatrix with every execution
21
- // Reset the matrix to identity if you start a new position update
22
- function getLinePoints(
23
- dhParameter: DHParameter,
24
- jointRotation: number,
25
- ): {
26
- a: THREE.Vector3
27
- b: THREE.Vector3
28
- } {
29
- const position = new Vector3()
30
- const quaternion = new Quaternion()
31
- const scale = new Vector3()
32
- accumulatedMatrix.decompose(position, quaternion, scale)
33
- const prevPosition = position.clone() // Update the previous position
34
-
35
- const matrix = new Matrix4()
36
- .makeRotationY(
37
- dhParameter.theta! +
38
- jointRotation * (dhParameter.reverse_rotation_direction ? -1 : 1),
39
- ) // Rotate around Z
40
- .multiply(new Matrix4().makeTranslation(0, dhParameter.d! / 1000, 0)) // Translate along Z
41
- .multiply(new Matrix4().makeTranslation(dhParameter.a! / 1000, 0, 0)) // Translate along X
42
- .multiply(new Matrix4().makeRotationX(dhParameter.alpha!)) // Rotate around X
43
-
44
- // Accumulate transformations
45
- accumulatedMatrix.multiply(matrix)
46
- accumulatedMatrix.decompose(position, quaternion, scale)
47
- return { a: prevPosition, b: position }
48
- }
19
+ const dhLines = getDHLines(
20
+ accumulatedMatrix,
21
+ dhParameters,
22
+ rapidlyChangingMotionState.state.joint_position.joints,
23
+ )
49
24
 
50
25
  function setJointLineRotation(
51
26
  jointIndex: number,
@@ -57,16 +32,18 @@ export function DHRobot({
57
32
  return
58
33
  }
59
34
 
60
- const dh_parameter = dhParameters[jointIndex]
61
- if (!dh_parameter) {
35
+ const dhParameter = dhParameters[jointIndex]
36
+ if (!dhParameter) {
62
37
  return
63
38
  }
64
39
 
65
- const { a, b } = getLinePoints(dh_parameter, jointValue)
40
+ const dhLine = getDHLine(accumulatedMatrix, dhParameter, jointValue)
66
41
  const lineGeometry = line.geometry as LineGeometry
67
- lineGeometry.setPositions([a.toArray(), b.toArray()].flat())
42
+ lineGeometry.setPositions(
43
+ [dhLine.start.toArray(), dhLine.end.toArray()].flat(),
44
+ )
68
45
 
69
- mesh.position.set(b.x, b.y, b.z)
46
+ mesh.position.set(dhLine.end.x, dhLine.end.y, dhLine.end.z)
70
47
  }
71
48
 
72
49
  function setRotation(joints: THREE.Object3D[], jointValues: number[]) {
@@ -93,22 +70,23 @@ export function DHRobot({
93
70
  <sphereGeometry args={[0.01, 32, 32]} />
94
71
  <meshStandardMaterial color={"black"} depthTest={true} />
95
72
  </mesh>
73
+
96
74
  {dhParameters!.map((param, index) => {
97
- const { a, b } = getLinePoints(
98
- param,
99
- rapidlyChangingMotionState.state.joint_position.joints[index] ??
100
- 0,
101
- )
102
75
  const jointName = `dhrobot_J0${index}`
103
76
  return (
104
77
  <group name={jointName} key={jointName}>
105
78
  <Line
106
79
  name={CHILD_LINE}
107
- points={[a, b]}
80
+ points={[dhLines[index].start, dhLines[index].end]}
108
81
  color={"white"}
109
82
  lineWidth={5}
83
+ segments
110
84
  />
111
- <mesh name={CHILD_MESH} key={"mesh_" + index} position={b}>
85
+ <mesh
86
+ name={CHILD_MESH}
87
+ key={"mesh_" + index}
88
+ position={dhLines[index].end}
89
+ >
112
90
  <sphereGeometry args={[0.01, 32, 32]} />
113
91
  <meshStandardMaterial color={"black"} depthTest={true} />
114
92
  </mesh>
@@ -0,0 +1,59 @@
1
+ import { useTheme } from "@mui/material"
2
+ import { observer } from "mobx-react-lite"
3
+ import { Trans, useTranslation } from "react-i18next"
4
+ import ControllerTypePhysicalIcon from "./icons/controller-type-physical.svg"
5
+ import ControllerTypeVirtualIcon from "./icons/controller-type-virtual.svg"
6
+ import { IndicatorWithExplanation } from "./IndicatorWithExplanation"
7
+
8
+ interface ControllerTypeIndicatorProps {
9
+ isVirtual: boolean
10
+ motionGroupId: string
11
+ }
12
+
13
+ export const ControllerTypeIndicator = observer(
14
+ ({ isVirtual, motionGroupId }: ControllerTypeIndicatorProps) => {
15
+ const theme = useTheme()
16
+ const { t } = useTranslation()
17
+
18
+ if (isVirtual) {
19
+ return (
20
+ <IndicatorWithExplanation
21
+ id="motion-group-virtual"
22
+ icon={ControllerTypeVirtualIcon}
23
+ color={theme.palette.tertiary.main}
24
+ name={t("SafetyBar.ControllerType.Virtual.lb")}
25
+ explanation={
26
+ <Trans
27
+ i18nKey="SafetyBar.MotionGroup.Virtual.Explanation.lb"
28
+ values={{
29
+ motionGroupId,
30
+ }}
31
+ >
32
+ Motion group <code>{motionGroupId}</code> refers to a virtual
33
+ robot with no physical counterpart. It can be freely manipulated
34
+ without special safety precautions.
35
+ </Trans>
36
+ }
37
+ />
38
+ )
39
+ }
40
+
41
+ return (
42
+ <IndicatorWithExplanation
43
+ id="motion-group-physical"
44
+ icon={ControllerTypePhysicalIcon}
45
+ color={theme.palette.primary.main}
46
+ name={t("SafetyBar.ControllerType.Physical.lb")}
47
+ explanation={
48
+ <Trans
49
+ i18nKey="SafetyBar.MotionGroup.Physical.Explanation.lb"
50
+ values={{
51
+ motionGroupId,
52
+ }}
53
+ components={{ code: <code />, strong: <strong /> }}
54
+ />
55
+ }
56
+ />
57
+ )
58
+ },
59
+ )
@@ -0,0 +1,117 @@
1
+ import {
2
+ Popover,
3
+ Stack,
4
+ SvgIcon,
5
+ Typography,
6
+ type TypographyProps,
7
+ } from "@mui/material"
8
+ import { observer, useLocalObservable } from "mobx-react-lite"
9
+ import type { ElementType, ReactNode } from "react"
10
+
11
+ export type IndicatorWithExplanationProps = {
12
+ id: string
13
+ icon: ElementType
14
+ color: TypographyProps["color"]
15
+ title?: ReactNode
16
+ name: ReactNode
17
+ explanation: ReactNode
18
+ literalValue?: string
19
+ }
20
+
21
+ export const IndicatorWithExplanation = observer(
22
+ ({
23
+ id,
24
+ icon,
25
+ color,
26
+ title,
27
+ name,
28
+ explanation,
29
+ literalValue,
30
+ }: IndicatorWithExplanationProps) => {
31
+ const state = useLocalObservable(() => ({
32
+ anchorEl: null as HTMLElement | null,
33
+
34
+ get isPopoverOpen() {
35
+ return !!state.anchorEl
36
+ },
37
+
38
+ openPopover(event: React.MouseEvent<HTMLElement>) {
39
+ state.anchorEl = event.currentTarget
40
+ },
41
+
42
+ closePopover() {
43
+ state.anchorEl = null
44
+ },
45
+ }))
46
+
47
+ return (
48
+ <>
49
+ <Stack
50
+ aria-owns={state.isPopoverOpen ? id : undefined}
51
+ aria-haspopup="true"
52
+ onMouseEnter={state.openPopover}
53
+ onMouseLeave={state.closePopover}
54
+ sx={{
55
+ cursor: "pointer",
56
+ }}
57
+ >
58
+ <SvgIcon
59
+ component={icon}
60
+ sx={{
61
+ color,
62
+ }}
63
+ />
64
+ </Stack>
65
+ <Popover
66
+ id={id}
67
+ sx={{
68
+ pointerEvents: "none",
69
+ }}
70
+ open={state.isPopoverOpen}
71
+ anchorEl={state.anchorEl}
72
+ anchorOrigin={{
73
+ vertical: "bottom",
74
+ horizontal: "left",
75
+ }}
76
+ transformOrigin={{
77
+ vertical: "top",
78
+ horizontal: "left",
79
+ }}
80
+ onClose={state.closePopover}
81
+ disableRestoreFocus
82
+ >
83
+ <Stack
84
+ gap="1rem"
85
+ padding={2}
86
+ maxWidth="450px"
87
+ sx={{
88
+ wordWrap: "break-word",
89
+ }}
90
+ >
91
+ <div>
92
+ {title && (
93
+ <Typography component="span" fontSize="14px">
94
+ {`${title}: `}
95
+ </Typography>
96
+ )}
97
+ <Typography
98
+ component="span"
99
+ color={color}
100
+ fontWeight="bold"
101
+ fontSize="14px"
102
+ >
103
+ {name}
104
+ </Typography>
105
+ </div>
106
+
107
+ <Typography fontSize="14px">{explanation}</Typography>
108
+
109
+ {literalValue && (
110
+ <Typography fontSize="12px">{literalValue}</Typography>
111
+ )}
112
+ </Stack>
113
+ </Popover>
114
+ </>
115
+ )
116
+ },
117
+ )
@@ -0,0 +1,76 @@
1
+ import { useTheme } from "@mui/material"
2
+ import type { RobotControllerStateOperationModeEnum } from "@wandelbots/wandelbots-js"
3
+ import { observer } from "mobx-react-lite"
4
+ import { Trans, useTranslation } from "react-i18next"
5
+ import OperationModeAutomaticIcon from "./icons/operation-mode-automatic.svg"
6
+ import OperationModeErrorIcon from "./icons/operation-mode-error.svg"
7
+ import OperationModeManualIcon from "./icons/operation-mode-manual.svg"
8
+ import { IndicatorWithExplanation } from "./IndicatorWithExplanation"
9
+
10
+ interface OperationModeIndicatorProps {
11
+ operationMode: RobotControllerStateOperationModeEnum
12
+ }
13
+
14
+ export const OperationModeIndicator = observer(
15
+ ({ operationMode }: OperationModeIndicatorProps) => {
16
+ const { t } = useTranslation()
17
+ const theme = useTheme()
18
+
19
+ switch (operationMode) {
20
+ case "OPERATION_MODE_AUTO":
21
+ return (
22
+ <IndicatorWithExplanation
23
+ id="operation-mode-auto"
24
+ icon={OperationModeAutomaticIcon}
25
+ title={t("SafetyBar.OperationMode.ti")}
26
+ name={t("SafetyBar.OperationMode.Automatic.ti")}
27
+ color={"rgba(255, 255, 255, 0.57)"}
28
+ explanation={
29
+ <Trans i18nKey="SafetyBar.OperationMode.Auto.Explanation.lb">
30
+ The robot controller is in automatic operation mode. Automated
31
+ movement without manual confirmation is possible in this mode.
32
+ </Trans>
33
+ }
34
+ />
35
+ )
36
+ case "OPERATION_MODE_MANUAL":
37
+ case "OPERATION_MODE_MANUAL_T1":
38
+ case "OPERATION_MODE_MANUAL_T2": {
39
+ return (
40
+ <IndicatorWithExplanation
41
+ id="operation-mode-manual"
42
+ icon={OperationModeManualIcon}
43
+ color={theme.palette.warning.main}
44
+ title={t("SafetyBar.OperationMode.ti")}
45
+ name={t("SafetyBar.OperationMode.Manual.lb")}
46
+ explanation={
47
+ <Trans i18nKey="SafetyBar.OperationMode.Manual.Explanation.lb">
48
+ The robot controller is in manual operation mode. On a physical
49
+ robot, this generally indicates that an enabling switch must be
50
+ held for the robot to move successfully.
51
+ </Trans>
52
+ }
53
+ literalValue={operationMode}
54
+ />
55
+ )
56
+ }
57
+ default:
58
+ return (
59
+ <IndicatorWithExplanation
60
+ id="operation-mode-error"
61
+ icon={OperationModeErrorIcon}
62
+ color={theme.palette.warning.main}
63
+ title={t("SafetyBar.OperationMode.ti")}
64
+ name={t("SafetyBar.OperationMode.Error.lb")}
65
+ explanation={
66
+ <Trans i18nKey="SafetyBar.OperationMode.Error.Explanation.lb">
67
+ The robot controller has entered an unexpected operation mode.
68
+ Motion group movement may not be possible.
69
+ </Trans>
70
+ }
71
+ literalValue={operationMode}
72
+ />
73
+ )
74
+ }
75
+ },
76
+ )