File size: 9,123 Bytes
1a7b22d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
/**
 * Sequential Operations Test Logic
 * Tests: findPort โ†’ calibrate โ†’ releaseMotors โ†’ teleoperate
 */

import {
  findPort,
  calibrate,
  releaseMotors,
  teleoperate,
  WebSerialPortWrapper,
  createSO100Config,
} from "@lerobot/web";

let isRunning = false;

function log(message: string) {
  const logElement = document.getElementById("log");
  if (logElement) {
    const timestamp = new Date().toLocaleTimeString();
    logElement.textContent += `[${timestamp}] ${message}\n`;
    logElement.scrollTop = logElement.scrollHeight;
  }
}

function setButtonState(running: boolean) {
  isRunning = running;
  const button = document.getElementById("runTest") as HTMLButtonElement;
  if (button) {
    button.disabled = running;
    button.textContent = running
      ? "โณ Running Test..."
      : "๐Ÿš€ Run Sequential Operations Test";
  }
}

declare global {
  interface Window {
    clearLog: () => void;
    runSequentialTest: () => Promise<void>;
  }
}

window.clearLog = function () {
  const logElement = document.getElementById("log");
  if (logElement) {
    logElement.textContent = "Log cleared.\n";
  }
};

window.runSequentialTest = async function () {
  if (isRunning) return;

  setButtonState(true);
  log("๐Ÿš€ Starting sequential operations test...");

  try {
    // Step 1: Find port
    log("\n1๏ธโƒฃ Finding robot port...");
    const findProcess = await findPort();
    const robots = await findProcess.result;
    const robot = robots[0]; // Get first robot

    if (!robot || !robot.isConnected) {
      throw new Error("No robot found or robot not connected");
    }

    log(`โœ… Found robot: ${robot.name} (${robot.robotType})`);
    log(`   Serial: ${robot.serialNumber}`);

    // Step 2: Release motors first, then calibrate
    log("\n2๏ธโƒฃ Releasing motors for calibration setup...");

    if (!robot.robotType) {
      throw new Error("Robot type not configured");
    }

    // Release motors so you can move the arm during calibration
    log("๐Ÿ”ง Creating port and config for motor release...");
    const setupPort = new WebSerialPortWrapper(robot.port);
    await setupPort.initialize();
    const setupConfig = createSO100Config(robot.robotType);

    log(`๐Ÿ”“ Releasing ${setupConfig.motorIds.length} motors...`);
    await releaseMotors(setupPort, setupConfig.motorIds);
    log("โœ… Motors released - you can now move the arm freely!");

    // Now start calibration
    log("\n๐Ÿ“ Starting calibration with live position updates...");
    log("๐Ÿ’ก Move the arm through its range of motion to see live updates!");

    const useSimulatedCalibration = false; // Real calibration to see live updates
    let calibrationResult: any;

    if (useSimulatedCalibration) {
      // Simulated calibration data (for testing without robot movement)
      log("๐Ÿ“Š Using simulated calibration data for testing...");
      await new Promise((resolve) => setTimeout(resolve, 2000)); // Simulate calibration time

      calibrationResult = {
        shoulder_pan: {
          id: 1,
          drive_mode: 0,
          homing_offset: 34,
          range_min: 994,
          range_max: 3100,
        },
        shoulder_lift: {
          id: 2,
          drive_mode: 0,
          homing_offset: 991,
          range_min: 960,
          range_max: 3233,
        },
        elbow_flex: {
          id: 3,
          drive_mode: 0,
          homing_offset: -881,
          range_min: 1029,
          range_max: 3065,
        },
        wrist_flex: {
          id: 4,
          drive_mode: 0,
          homing_offset: 128,
          range_min: 710,
          range_max: 3135,
        },
        wrist_roll: {
          id: 5,
          drive_mode: 0,
          homing_offset: -15,
          range_min: 0,
          range_max: 4095,
        },
        gripper: {
          id: 6,
          drive_mode: 0,
          homing_offset: -1151,
          range_min: 2008,
          range_max: 3606,
        },
      };

      log("โœ… Calibration completed (simulated)");
    } else {
      // Real calibration
      const calibrationProcess = await calibrate(robot, {
        onProgress: (message) => log(`๐Ÿ“Š ${message}`),
        onLiveUpdate: (data) => {
          const motors = Object.keys(data);
          if (motors.length > 0) {
            const ranges = motors.map((m) => data[m].range).join(", ");
            log(`๐Ÿ“ Motor ranges: [${ranges}]`);
          }
        },
      });

      // Auto-stop calibration after 8 seconds for testing
      setTimeout(() => {
        log("โฑ๏ธ Auto-stopping calibration for test...");
        calibrationProcess.stop();
      }, 8000);

      await calibrationProcess.result;
      log("โœ… Calibration completed (real)");
    }

    // Step 3: Use your provided calibration config for teleoperation
    log("\n3๏ธโƒฃ Setting up teleoperation with your calibration config...");

    // Use your provided calibration config instead of real calibration result
    calibrationResult = {
      shoulder_pan: {
        id: 1,
        drive_mode: 0,
        homing_offset: 34,
        range_min: 994,
        range_max: 3100,
      },
      shoulder_lift: {
        id: 2,
        drive_mode: 0,
        homing_offset: 991,
        range_min: 960,
        range_max: 3233,
      },
      elbow_flex: {
        id: 3,
        drive_mode: 0,
        homing_offset: -881,
        range_min: 1029,
        range_max: 3065,
      },
      wrist_flex: {
        id: 4,
        drive_mode: 0,
        homing_offset: 128,
        range_min: 710,
        range_max: 3135,
      },
      wrist_roll: {
        id: 5,
        drive_mode: 0,
        homing_offset: -15,
        range_min: 0,
        range_max: 4095,
      },
      gripper: {
        id: 6,
        drive_mode: 0,
        homing_offset: -1151,
        range_min: 2008,
        range_max: 3606,
      },
    };

    log(
      `โœ… Using your calibration config: ${Object.keys(calibrationResult).join(
        ", "
      )}`
    );

    // Step 4: Teleoperate with auto key simulation
    log("\n4๏ธโƒฃ Starting teleoperation...");
    const teleoperationProcess = await teleoperate(robot, {
      calibrationData: calibrationResult,
      onStateUpdate: (state) => {
        if (state.isActive && Object.keys(state.keyStates).length > 0) {
          const activeKeys = Object.keys(state.keyStates).filter(
            (k) => state.keyStates[k].pressed
          );
          log(`๐ŸŽฎ Auto-simulated keys: ${activeKeys.join(", ")}`);
        }
      },
    });

    teleoperationProcess.start();
    log("โœ… Teleoperation started");
    log("๐Ÿค– Auto-simulating arrow key presses to move the arm...");

    // Auto-simulate key presses (left/right arrows to move shoulder pan)
    let keySimulationActive = true;
    let currentDirection = "ArrowLeft";

    const simulateKeys = () => {
      if (!keySimulationActive) return;

      // Press current key
      teleoperationProcess.updateKeyState(currentDirection, true);
      log(`๐Ÿ”„ Pressing ${currentDirection} (shoulder pan movement)`);

      // Hold for 1 second, then release and switch direction
      setTimeout(() => {
        if (!keySimulationActive) return;
        teleoperationProcess.updateKeyState(currentDirection, false);

        // Switch direction
        currentDirection =
          currentDirection === "ArrowLeft" ? "ArrowRight" : "ArrowLeft";

        // Wait 500ms then start next movement
        setTimeout(() => {
          if (keySimulationActive) simulateKeys();
        }, 500);
      }, 1000);
    };

    // Start key simulation after a brief delay
    setTimeout(simulateKeys, 1000);

    // Auto-stop teleoperation after 8 seconds for testing
    setTimeout(() => {
      keySimulationActive = false;
      log("โฑ๏ธ Auto-stopping teleoperation for test...");
      teleoperationProcess.stop();
      log("\n๐ŸŽ‰ All sequential operations completed successfully!");
      log(
        "\n๐Ÿ“ RESULT: The current approach works but is too complex for users!"
      );
      log(
        "๐Ÿ“ Users shouldn't need WebSerialPortWrapper and createSO100Config!"
      );
      setButtonState(false);
    }, 8000);
  } catch (error: any) {
    log(`โŒ Sequential operations failed: ${error.message}`);

    // Check if it's a connection conflict
    if (
      error.message.includes("port") ||
      error.message.includes("serial") ||
      error.message.includes("connection")
    ) {
      log("๐Ÿ” This might be a port connection conflict!");
      log("๐Ÿ’ก Multiple WebSerialPortWrapper instances may be interfering");
    }

    setButtonState(false);
  }
};

// Initialize on DOM load
document.addEventListener("DOMContentLoaded", () => {
  // Check Web Serial support
  if (!("serial" in navigator)) {
    log("โŒ Web Serial API not supported in this browser");
    log("๐Ÿ’ก Try Chrome/Edge with --enable-web-serial flag");
    const button = document.getElementById("runTest") as HTMLButtonElement;
    if (button) button.disabled = true;
  } else {
    log("โœ… Web Serial API supported");
    log("Ready to test. Click 'Run Sequential Operations Test' to begin...");
  }
});