Spaces:
Running
Running
| // Fan-out adapter: drives the on-screen puppet and the real robot in | |
| // lock-step (same targets, same durations, so they stay visually in | |
| // sync). The command log lives here in embed mode — one entry per | |
| // program action regardless of how many robots execute it. | |
| import type { CommandLog } from "../util/log"; | |
| import type { | |
| LoadedEmotion, | |
| LoadedSound, | |
| PoseTargetDeg, | |
| RobotAdapter, | |
| } from "./adapter"; | |
| export class MultiplexAdapter implements RobotAdapter { | |
| readonly kind = "multi" as const; | |
| constructor( | |
| private children: RobotAdapter[], | |
| private log?: CommandLog, | |
| ) {} | |
| async goto(target: PoseTargetDeg, durationSec: number, signal?: AbortSignal): Promise<void> { | |
| this.log?.push("goto", { target, durationSec }); | |
| await Promise.all(this.children.map((c) => c.goto(target, durationSec, signal))); | |
| } | |
| async playEmotion(emotion: LoadedEmotion, signal?: AbortSignal): Promise<void> { | |
| this.log?.push("playEmotion", { id: emotion.id }); | |
| await Promise.all(this.children.map((c) => c.playEmotion(emotion, signal))); | |
| } | |
| async playSound(sound: LoadedSound, signal?: AbortSignal): Promise<void> { | |
| this.log?.push("playSound", { id: sound.id }); | |
| await Promise.all(this.children.map((c) => c.playSound(sound, signal))); | |
| } | |
| antennaTouched(): boolean { | |
| return this.children.some((c) => c.antennaTouched()); | |
| } | |
| async stopAll(): Promise<void> { | |
| this.log?.push("stopAll"); | |
| await Promise.all(this.children.map((c) => c.stopAll())); | |
| } | |
| async returnHome(): Promise<void> { | |
| this.log?.push("returnHome"); | |
| await Promise.all(this.children.map((c) => c.returnHome())); | |
| } | |
| dispose(): void { | |
| for (const c of this.children) c.dispose(); | |
| } | |
| } | |