diff --git a/src/components/3d-viewport/SafetyZonesRenderer.tsx b/src/components/3d-viewport/SafetyZonesRenderer.tsx index fff5a035..1223e3c3 100644 --- a/src/components/3d-viewport/SafetyZonesRenderer.tsx +++ b/src/components/3d-viewport/SafetyZonesRenderer.tsx @@ -1,8 +1,8 @@ -import type { SafetySetupSafetyZone } from "@wandelbots/wandelbots-js" +import { type GroupProps } from "@react-three/fiber" import type { Geometry } from "@wandelbots/wandelbots-api-client" +import type { SafetySetupSafetyZone } from "@wandelbots/wandelbots-js" import * as THREE from "three" import { ConvexGeometry } from "three-stdlib" -import { type GroupProps } from "@react-three/fiber" export type SafetyZonesRendererProps = { safetyZones: SafetySetupSafetyZone[] @@ -89,21 +89,19 @@ export function SafetyZonesRenderer({ return null } return ( - <> - - - - + + + ) }) })} diff --git a/src/components/jogging/JoggingActivationRequired.tsx b/src/components/jogging/JoggingActivationRequired.tsx deleted file mode 100644 index 627ffef6..00000000 --- a/src/components/jogging/JoggingActivationRequired.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { Button, Stack, useTheme } from "@mui/material" -import { observer } from "mobx-react-lite" -import type React from "react" -import { useTranslation } from "react-i18next" -import { LoadingCover } from "../LoadingCover" -import { TransparentOverlay } from "../TransparentOverlay" -import type { JoggingStore } from "./JoggingStore" - -export const JoggingActivationRequired = observer( - ({ store, children }: { store: JoggingStore; children: React.ReactNode }) => { - const { t } = useTranslation() - const theme = useTheme() - - function renderOverlay() { - if (store.activationState === "inactive" && !store.activationError) { - return ( - - - - ) - } else if (store.activationState === "loading" || store.activationError) { - return ( - - - - ) - } - } - - return ( - - {renderOverlay()} - {children} - - ) - }, -) diff --git a/src/components/jogging/JoggingCartesianTab.tsx b/src/components/jogging/JoggingCartesianTab.tsx index 5294b4f5..57cd89f2 100644 --- a/src/components/jogging/JoggingCartesianTab.tsx +++ b/src/components/jogging/JoggingCartesianTab.tsx @@ -13,7 +13,6 @@ import YAxisIcon from "../../icons/axis-y.svg" import ZAxisIcon from "../../icons/axis-z.svg" import RotationIcon from "../../icons/rotation.svg" import { useReaction } from "../utils/hooks" -import { JoggingActivationRequired } from "./JoggingActivationRequired" import { JoggingCartesianAxisControl } from "./JoggingCartesianAxisControl" import { JoggingJointLimitDetector } from "./JoggingJointLimitDetector" import { JoggingOptions } from "./JoggingOptions" @@ -67,11 +66,11 @@ export const JoggingCartesianTab = observer( opts: JoggingCartesianOpts, increment: DiscreteIncrementOption, ) { - const tcpPose = - store.jogger.motionStream.rapidlyChangingMotionState.tcp_pose + const jogger = await store.activate() + + const tcpPose = jogger.motionStream.rapidlyChangingMotionState.tcp_pose const jointPosition = - store.jogger.motionStream.rapidlyChangingMotionState.state - .joint_position + jogger.motionStream.rapidlyChangingMotionState.state.joint_position if (!tcpPose) return await store.withMotionLock(async () => { @@ -100,6 +99,7 @@ export const JoggingCartesianTab = observer( }) } finally { store.setCurrentIncrementJog(null) + await store.deactivate() } }) } @@ -107,6 +107,7 @@ export const JoggingCartesianTab = observer( async function startCartesianJogging(opts: JoggingCartesianOpts) { if (store.isLocked) return + const jogger = await store.activate() if (store.activeDiscreteIncrement) { return runIncrementalCartesianJog(opts, store.activeDiscreteIncrement) } @@ -133,7 +134,7 @@ export const JoggingCartesianTab = observer( return } - await store.jogger.stop() + await store.deactivate() } const axisList = [ @@ -183,109 +184,112 @@ export const JoggingCartesianTab = observer( justifyContent="center" sx={{ flexGrow: "1" }} > - {/* Translate or rotate toggle */} - - - {t("Jogging.Cartesian.Translation.bt")} - - - {t("Jogging.Cartesian.Rotation.bt")} - - + {/* Translate or rotate toggle */} + + + {t("Jogging.Cartesian.Translation.bt")} + + + {t("Jogging.Cartesian.Rotation.bt")} + + - - - {/* Cartesian translate jogging */} - {store.selectedCartesianMotionType === "translate" && - axisList.map((axis) => ( - - {axis.icon} - - {axis.id.toUpperCase()} - - - } - getDisplayedValue={() => - formatMM( - store.jogger.motionStream.rapidlyChangingMotionState - .tcp_pose?.position[axis.id] || 0, - ) - } - startJogging={(direction: "-" | "+") => - startCartesianJogging({ - axis: axis.id, - motionType: "translate", - direction, - }) - } - stopJogging={stopJogging} - /> - ))} + {/* Cartesian translate jogging */} + {store.selectedCartesianMotionType === "translate" && + axisList.map((axis) => ( + + {axis.icon} + + {axis.id.toUpperCase()} + + + } + getDisplayedValue={() => + formatMM( + store.jogger.motionStream.rapidlyChangingMotionState + .tcp_pose?.position[axis.id] || 0, + ) + } + startJogging={(direction: "-" | "+") => + startCartesianJogging({ + axis: axis.id, + motionType: "translate", + direction, + }) + } + stopJogging={stopJogging} + /> + ))} - {/* Cartesian rotate jogging */} - {store.selectedCartesianMotionType === "rotate" && - axisList.map((axis) => ( - - - - {axis.id.toUpperCase()} - - - } - getDisplayedValue={() => - formatDegrees( - store.jogger.motionStream.rapidlyChangingMotionState - .tcp_pose?.orientation?.[axis.id] || 0, - ) - } - startJogging={(direction: "-" | "+") => - startCartesianJogging({ - axis: axis.id, - motionType: "rotate", - direction, - }) - } - stopJogging={stopJogging} - /> - ))} - - + {/* Cartesian rotate jogging */} + {store.selectedCartesianMotionType === "rotate" && + axisList.map((axis) => ( + + + + {axis.id.toUpperCase()} + + + } + getDisplayedValue={() => + formatDegrees( + store.jogger.motionStream.rapidlyChangingMotionState + .tcp_pose?.orientation?.[axis.id] || 0, + ) + } + startJogging={(direction: "-" | "+") => + startCartesianJogging({ + axis: axis.id, + motionType: "rotate", + direction, + }) + } + stopJogging={stopJogging} + /> + ))} + {/* Show message if joint limits reached */} diff --git a/src/components/jogging/JoggingJointRotationControl.tsx b/src/components/jogging/JoggingJointRotationControl.tsx index 5c05738d..6fca3e64 100644 --- a/src/components/jogging/JoggingJointRotationControl.tsx +++ b/src/components/jogging/JoggingJointRotationControl.tsx @@ -136,7 +136,7 @@ export const JoggingJointRotationControl = externalizeComponent( > @@ -231,7 +231,7 @@ export const JoggingJointRotationControl = externalizeComponent( > diff --git a/src/components/jogging/JoggingJointTab.tsx b/src/components/jogging/JoggingJointTab.tsx index 59fab1ac..a10ae8a9 100644 --- a/src/components/jogging/JoggingJointTab.tsx +++ b/src/components/jogging/JoggingJointTab.tsx @@ -2,7 +2,6 @@ import { Divider, Stack } from "@mui/material" import { radiansToDegrees } from "@wandelbots/wandelbots-js" import { observer } from "mobx-react-lite" import type { ReactNode } from "react" -import { JoggingActivationRequired } from "./JoggingActivationRequired" import { JoggingJointLimitDetector } from "./JoggingJointLimitDetector" import { JoggingJointRotationControl } from "./JoggingJointRotationControl" import type { JoggingStore } from "./JoggingStore" @@ -14,6 +13,8 @@ export const JoggingJointTab = observer( joint: number direction: "-" | "+" }) { + await store.activate() + await store.jogger.startJointRotation({ joint: opts.joint, direction: opts.direction, @@ -36,46 +37,44 @@ export const JoggingJointTab = observer( sx={{ flexGrow: "1" }} id="JointControls" > - - - {store.jogger.motionStream.joints.map((joint) => { - const jointLimits = - store.motionGroupSpec.mechanical_joint_limits?.[joint.index] - const lowerLimitDegs = - jointLimits?.lower_limit !== undefined - ? radiansToDegrees(jointLimits.lower_limit) - : undefined - const upperLimitDegs = - jointLimits?.upper_limit !== undefined - ? radiansToDegrees(jointLimits.upper_limit) - : undefined + + {store.jogger.motionStream.joints.map((joint) => { + const jointLimits = + store.motionGroupSpec.mechanical_joint_limits?.[joint.index] + const lowerLimitDegs = + jointLimits?.lower_limit !== undefined + ? radiansToDegrees(jointLimits.lower_limit) + : undefined + const upperLimitDegs = + jointLimits?.upper_limit !== undefined + ? radiansToDegrees(jointLimits.upper_limit) + : undefined - return ( - { - const value = - store.jogger.motionStream.rapidlyChangingMotionState - .state.joint_position.joints[joint.index] - return value !== undefined - ? radiansToDegrees(value) - : undefined - }} - startJogging={(direction: "-" | "+") => - startJointJogging({ - joint: joint.index, - direction, - }) - } - stopJogging={stopJointJogging} - /> - ) - })} - - + return ( + { + const value = + store.jogger.motionStream.rapidlyChangingMotionState.state + .joint_position.joints[joint.index] + return value !== undefined + ? radiansToDegrees(value) + : undefined + }} + startJogging={(direction: "-" | "+") => + startJointJogging({ + joint: joint.index, + direction, + }) + } + stopJogging={stopJointJogging} + /> + ) + })} + diff --git a/src/components/jogging/JoggingPanel.tsx b/src/components/jogging/JoggingPanel.tsx index 12fa2720..2e989e9a 100644 --- a/src/components/jogging/JoggingPanel.tsx +++ b/src/components/jogging/JoggingPanel.tsx @@ -6,7 +6,6 @@ import { observer, useLocalObservable } from "mobx-react-lite" import { useEffect } from "react" import { externalizeComponent } from "../../externalizeComponent" import { LoadingCover } from "../LoadingCover" -import { useReaction } from "../utils/hooks" import { JoggingCartesianTab } from "./JoggingCartesianTab" import { JoggingJointTab } from "./JoggingJointTab" import { JoggingStore } from "./JoggingStore" @@ -121,38 +120,6 @@ const JoggingPanelInner = observer( children?: React.ReactNode childrenJoint?: React.ReactNode }) => { - // Jogger is only active as long as the tab is focused - useEffect(() => { - function deactivate() { - store.deactivate() - } - - function activate() { - store.activate() - } - - window.addEventListener("blur", deactivate) - window.addEventListener("focus", activate) - - return () => { - window.removeEventListener("blur", deactivate) - window.removeEventListener("focus", activate) - } - }) - - // Update jogging mode on jogger based on user selections - useReaction( - () => [ - store.currentTab.id, - store.selectedTcpId, - store.activeCoordSystemId, - store.activeDiscreteIncrement, - ], - () => { - if (store.activationState !== "inactive") store.activate() - }, - ) - function renderTabContent() { if (store.currentTab.id === "cartesian") { return ( diff --git a/src/components/jogging/JoggingStore.ts b/src/components/jogging/JoggingStore.ts index d7bcac3a..7acda07a 100644 --- a/src/components/jogging/JoggingStore.ts +++ b/src/components/jogging/JoggingStore.ts @@ -8,12 +8,7 @@ import { tryParseJson } from "@wandelbots/wandelbots-js" import { countBy } from "lodash-es" import keyBy from "lodash-es/keyBy" import uniqueId from "lodash-es/uniqueId" -import { - autorun, - makeAutoObservable, - runInAction, - type IReactionDisposer, -} from "mobx" +import { autorun, makeAutoObservable, type IReactionDisposer } from "mobx" const discreteIncrementOptions = [ { id: "0.1", mm: 0.1, degrees: 0.05 }, @@ -44,12 +39,6 @@ export type IncrementJogInProgress = { export class JoggingStore { selectedTabId: "cartesian" | "joint" | "debug" = "cartesian" - /** - * Whether the user must manually interact to activate jogging, or - * if it can be done automatically - */ - manualActivationRequired: boolean = true - /** * State of the jogging panel. Starts as "inactive" */ @@ -141,6 +130,18 @@ export class JoggingStore { ), ]) + // Setting mode control makes jogging startup slightly faster + // on physical robots + // https://wandelbots.slack.com/archives/C06VA4J59PF/p1725523765976109?thread_ts=1725464963.859559&cid=C06VA4J59PF + try { + await jogger.nova.api.controller.setDefaultMode( + jogger.motionStream.controllerId, + "MODE_CONTROL", + ) + } catch (err) { + console.error(err) + } + return new JoggingStore( jogger, motionGroupSpec, @@ -187,54 +188,18 @@ export class JoggingStore { return countBy(this.coordSystems, (cs) => cs.name) } - async deactivate(opts: { requireManualReactivation?: boolean } = {}) { - if (this.activationState === "inactive") return + async deactivate() { const websocket = this.jogger.activeWebsocket - this.activationState = "inactive" this.jogger.setJoggingMode("increment") if (websocket) { await websocket.closed() } - - // Closing websocket sometimes isn't enough to stop interference - try { - await this.jogger.nova.api.motionGroupJogging.stopJogging( - this.jogger.motionGroupId, - ) - } catch (err) { - console.error(err) - } - - if (opts.requireManualReactivation) { - runInAction(() => { - this.manualActivationRequired = true - }) - } } /** Activate the jogger with current settings */ - async activate(opts: { manual?: boolean } = {}) { - if (this.manualActivationRequired && !opts.manual) return - - runInAction(() => { - this.activationState = "loading" - this.activationError = null - }) - - // Setting mode control makes jogging startup slightly faster - // on physical robots - // https://wandelbots.slack.com/archives/C06VA4J59PF/p1725523765976109?thread_ts=1725464963.859559&cid=C06VA4J59PF - try { - await this.jogger.nova.api.controller.setDefaultMode( - this.jogger.motionStream.controllerId, - "MODE_CONTROL", - ) - } catch (err) { - console.error(err) - } - + async activate() { if (this.currentTab.id === "cartesian") { const cartesianJoggingOpts = { tcpId: this.selectedTcpId, @@ -250,25 +215,7 @@ export class JoggingStore { this.jogger.setJoggingMode("joint") } - if (this.jogger.activeWebsocket) { - try { - this.jogger.stop() - await this.jogger.activeWebsocket.nextMessage() - } catch (err) { - runInAction(() => { - this.activationState = "inactive" - this.activationError = err - }) - return - } - } - - runInAction(() => { - this.activationState = "active" - if (opts.manual) { - this.manualActivationRequired = false - } - }) + return this.jogger } loadFromLocalStorage() { diff --git a/src/i18n/locales/de/translations.json b/src/i18n/locales/de/translations.json index aafa4b5e..bf8f4b72 100644 --- a/src/i18n/locales/de/translations.json +++ b/src/i18n/locales/de/translations.json @@ -10,8 +10,6 @@ "Jogging.Joints.JointValues.lb": "Gelenkwerte", "Jogging.Increment.Continuous.dd": "Fortlaufend", "Jogging.Cartesian.Orientation.lb": "Orientierung", - "Jogging.Activate.bt": "Jogging aktivieren", - "Jogging.Activating.lb": "Jogging wird aktiviert", "Jogging.JointLimitsReached.lb": "Joint-Limit für Joint {{jointNumbers}} erreicht", "Jogging.Orientation.coordsys": "Base", "Jogging.Orientation.tool": "Tool" diff --git a/src/i18n/locales/en/translations.json b/src/i18n/locales/en/translations.json index 911b5442..d4ffbe53 100644 --- a/src/i18n/locales/en/translations.json +++ b/src/i18n/locales/en/translations.json @@ -10,7 +10,6 @@ "Jogging.Joints.JointValues.lb": "Joint values", "Jogging.Increment.Continuous.dd": "Continuous", "Jogging.Cartesian.Orientation.lb": "Orientation", - "Jogging.Activate.bt": "Activate jogging", "Jogging.Activating.lb": "Activating jogging", "Jogging.JointLimitsReached.lb": "Joint limit reached for joint {{jointNumbers}}", "Jogging.Orientation.coordsys": "Base",