refactor: release v1.0.5 安全精简与网站发布
web-platform-ci / Standalone decision service (no cloud credentials) (push) Has been cancelled
web-platform-ci / TypeScript, lint, unit, build (push) Has been cancelled
web-platform-ci / Playwright E2E (push) Has been cancelled
lekiwi-compatibility / cpu-compatibility (push) Has been cancelled
web-platform-ci / Standalone decision service (no cloud credentials) (push) Has been cancelled
web-platform-ci / TypeScript, lint, unit, build (push) Has been cancelled
web-platform-ci / Playwright E2E (push) Has been cancelled
lekiwi-compatibility / cpu-compatibility (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
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,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user