26bb5634bd
web-platform-ci / Standalone decision service (no cloud credentials) (push) Waiting to run
web-platform-ci / TypeScript, lint, unit, build (push) Waiting to run
web-platform-ci / Playwright E2E (push) Waiting to run
lekiwi-compatibility / cpu-compatibility (push) Waiting to run
165 lines
5.6 KiB
TypeScript
165 lines
5.6 KiB
TypeScript
import type { RefObject } from 'react';
|
|
import type { MainThreadPhysicsAdapter } from '../../simulation/PhysicsAdapter';
|
|
import type { ActuatorParameters, SimulationSession } from '../../simulation/SimulationSession';
|
|
import type { ProjectManifest } from '../../project/types';
|
|
import type { PickPlaceTaskConfig } from '../../mobile/agent/PickPlaceScene';
|
|
import type { DataRecorderConfig } from '../../telemetry/DataRecorder';
|
|
import type { InteractionMode } from '../../viewer/MuJoCoViewer';
|
|
import type { useAppStore } from '../../stores/useAppStore';
|
|
import { downloadBytes } from '../../project/cachedFiles';
|
|
import { diagnostic, type useWorkbenchFeedback } from './useWorkbenchFeedback';
|
|
|
|
/** UI 意图委托,不拥有物理时钟或控制权算法。 */
|
|
export function useSimulationActions({
|
|
state,
|
|
adapterRef,
|
|
manifestRef,
|
|
agentSceneSessionRef,
|
|
lastAgentConfigRef,
|
|
prepareAgentScene,
|
|
ignoreJointLimits,
|
|
setIgnoreJointLimits,
|
|
setGeneratedMjcf,
|
|
notify,
|
|
}: {
|
|
state: Pick<
|
|
ReturnType<typeof useAppStore.getState>,
|
|
| 'paused'
|
|
| 'snapshot'
|
|
| 'selectedEntry'
|
|
| 'setPaused'
|
|
| 'setSnapshot'
|
|
| 'setSpeed'
|
|
| 'setMode'
|
|
| 'setDiagnostic'
|
|
>;
|
|
adapterRef: RefObject<MainThreadPhysicsAdapter>;
|
|
manifestRef: RefObject<ProjectManifest | null>;
|
|
agentSceneSessionRef: RefObject<SimulationSession | undefined>;
|
|
lastAgentConfigRef: RefObject<PickPlaceTaskConfig>;
|
|
prepareAgentScene(config: PickPlaceTaskConfig): Promise<SimulationSession>;
|
|
ignoreJointLimits: boolean;
|
|
setIgnoreJointLimits(value: boolean): void;
|
|
setGeneratedMjcf(value: string): void;
|
|
notify: ReturnType<typeof useWorkbenchFeedback>['notify'];
|
|
}) {
|
|
const togglePause = () => {
|
|
const value = !state.paused;
|
|
state.setPaused(value);
|
|
adapterRef.current.setPaused(value);
|
|
};
|
|
const reset = () => {
|
|
if (
|
|
agentSceneSessionRef.current &&
|
|
agentSceneSessionRef.current === adapterRef.current.session
|
|
) {
|
|
void prepareAgentScene(lastAgentConfigRef.current).catch(() => undefined);
|
|
return;
|
|
}
|
|
adapterRef.current.setPaused(true);
|
|
adapterRef.current.reset();
|
|
state.setSnapshot(adapterRef.current.snapshot() ?? undefined);
|
|
state.setPaused(true);
|
|
};
|
|
const singleStep = () => {
|
|
adapterRef.current.singleStep();
|
|
state.setSnapshot(adapterRef.current.snapshot() ?? undefined);
|
|
};
|
|
const changeSpeed = (value: number) => {
|
|
state.setSpeed(value);
|
|
adapterRef.current.setSpeed(value);
|
|
};
|
|
const jointPoseEditingDisabled = Boolean(
|
|
state.snapshot?.robot ||
|
|
state.snapshot?.mobileRobotId ||
|
|
(state.snapshot?.controlOwner && state.snapshot.controlOwner !== 'manual'),
|
|
);
|
|
const mode = (value: InteractionMode) => {
|
|
if (value === 'joint' && jointPoseEditingDisabled) return;
|
|
state.setMode(value);
|
|
};
|
|
const resetJoints = () => {
|
|
adapterRef.current.resetJoints();
|
|
state.setPaused(true);
|
|
state.setSnapshot(adapterRef.current.snapshot() ?? undefined);
|
|
};
|
|
const toggleJointLimits = () => {
|
|
const next = !ignoreJointLimits;
|
|
adapterRef.current.setIgnoreJointLimits(next);
|
|
setIgnoreJointLimits(next);
|
|
state.setSnapshot(adapterRef.current.snapshot() ?? undefined);
|
|
};
|
|
const setActuator = (id: number, value: number) => {
|
|
adapterRef.current.setActuator(id, value);
|
|
state.setSnapshot(adapterRef.current.snapshot() ?? undefined);
|
|
};
|
|
const setActuatorParameters = (id: number, parameters: ActuatorParameters) => {
|
|
if (!adapterRef.current.setActuatorParameters(id, parameters)) return;
|
|
state.setSnapshot(adapterRef.current.snapshot() ?? undefined);
|
|
try {
|
|
setGeneratedMjcf(new TextDecoder().decode(adapterRef.current.exportMjcf()));
|
|
} catch (error) {
|
|
console.warn('[MuJoCo] 无法刷新驱动器参数源码', error);
|
|
}
|
|
};
|
|
const setJoint = (id: number, value: number) => {
|
|
adapterRef.current.setJointPosition(id, value);
|
|
state.setPaused(true);
|
|
state.setSnapshot(adapterRef.current.snapshot() ?? undefined);
|
|
};
|
|
const configureDataRecorder = (patch: Partial<DataRecorderConfig>) => {
|
|
try {
|
|
adapterRef.current.configureDataRecorder(patch);
|
|
state.setSnapshot(adapterRef.current.snapshot() ?? undefined);
|
|
} catch (error) {
|
|
state.setDiagnostic(diagnostic('仿真', error, state.selectedEntry));
|
|
}
|
|
};
|
|
const startDataRecording = () => {
|
|
adapterRef.current.startDataRecording();
|
|
state.setSnapshot(adapterRef.current.snapshot() ?? undefined);
|
|
};
|
|
const stopDataRecording = () => {
|
|
adapterRef.current.stopDataRecording();
|
|
state.setSnapshot(adapterRef.current.snapshot() ?? undefined);
|
|
};
|
|
const clearDataRecording = () => {
|
|
adapterRef.current.clearDataRecording();
|
|
state.setSnapshot(adapterRef.current.snapshot() ?? undefined);
|
|
};
|
|
const exportDataRecording = (format: 'csv' | 'json') => {
|
|
try {
|
|
const stem =
|
|
(manifestRef.current?.name ?? 'simulation')
|
|
.replace(/\.(?:zip|xml|urdf)$/i, '')
|
|
.replace(/[^\p{L}\p{N}._-]+/gu, '_') || 'simulation';
|
|
downloadBytes(
|
|
adapterRef.current.exportDataRecording(format),
|
|
`${stem}-telemetry.${format}`,
|
|
format === 'csv' ? 'text/csv' : 'application/json',
|
|
);
|
|
notify(`遥测 ${format.toUpperCase()} 已导出`, `${stem}-telemetry.${format}`);
|
|
} catch (error) {
|
|
state.setDiagnostic(diagnostic('仿真', error, state.selectedEntry));
|
|
}
|
|
};
|
|
return {
|
|
togglePause,
|
|
reset,
|
|
singleStep,
|
|
changeSpeed,
|
|
jointPoseEditingDisabled,
|
|
mode,
|
|
resetJoints,
|
|
toggleJointLimits,
|
|
setActuator,
|
|
setActuatorParameters,
|
|
setJoint,
|
|
configureDataRecorder,
|
|
startDataRecording,
|
|
stopDataRecording,
|
|
clearDataRecording,
|
|
exportDataRecording,
|
|
};
|
|
}
|