File size: 3,722 Bytes
8eeb37a
6ce4ca6
f62f94b
6ce4ca6
f62f94b
 
6ce4ca6
 
f62f94b
 
6ce4ca6
 
 
f62f94b
 
 
 
 
 
 
6ce4ca6
 
 
f62f94b
 
 
6ce4ca6
f62f94b
6ce4ca6
 
 
 
f62f94b
 
 
 
 
6ce4ca6
 
f62f94b
 
6ce4ca6
f62f94b
 
 
 
 
 
 
6ce4ca6
f62f94b
6ce4ca6
 
f62f94b
6ce4ca6
 
 
f62f94b
6ce4ca6
 
 
 
 
f62f94b
 
 
6ce4ca6
f62f94b
6ce4ca6
f62f94b
 
6ce4ca6
 
 
f62f94b
 
6ce4ca6
 
f62f94b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6ce4ca6
f62f94b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6ce4ca6
f62f94b
 
 
 
6ce4ca6
 
 
f62f94b
6ce4ca6
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
import type { Producer, RobotCommand } from '../models.js';
import { ROBOT_CONFIG } from '../config.js';
import { USBServoDriver } from './USBServoDriver.js';

export class USBProducer extends USBServoDriver implements Producer {
  private commandQueue: RobotCommand[] = [];
  private isProcessingCommands = false;

  constructor(config: any) {
    super(config, 'Producer');
  }

  async connect(): Promise<void> {
    // Connect to USB first (this triggers browser's device selection dialog)
    await this.connectToUSB();
    
    // Lock servos for software control (producer mode)
    await this.lockAllServos();
    
    // Note: Calibration is checked when operations are actually needed
  }

  async disconnect(): Promise<void> {
    // Stop command processing
    this.isProcessingCommands = false;
    this.commandQueue = [];

    await this.disconnectFromUSB();
  }

  async sendCommand(command: RobotCommand): Promise<void> {
    if (!this._status.isConnected) {
      throw new Error(`[${this.name}] Cannot send command: not connected`);
    }

    if (!this.isCalibrated) {
      throw new Error(`[${this.name}] Cannot send command: not calibrated`);
    }

    // Add command to queue for processing
    this.commandQueue.push(command);
    
    // Limit queue size to prevent memory issues
    if (this.commandQueue.length > ROBOT_CONFIG.commands.maxQueueSize) {
      this.commandQueue.shift(); // Remove oldest command
    }

    // Start processing if not already running
    if (!this.isProcessingCommands) {
      this.processCommandQueue();
    }
  }

  // Event handlers already in base class

  // Private methods
  private async processCommandQueue(): Promise<void> {
    if (this.isProcessingCommands || !this._status.isConnected) {
      return;
    }

    this.isProcessingCommands = true;

    while (this.commandQueue.length > 0 && this._status.isConnected) {
      const command = this.commandQueue.shift();
      if (command) {
        try {
          await this.executeCommand(command);
        } catch (error) {
          console.error(`[${this.name}] Command execution failed:`, error);
          // Continue processing other commands
        }
      }
    }

    this.isProcessingCommands = false;
  }

  private async executeCommand(command: RobotCommand): Promise<void> {
    if (!this.scsServoSDK || !this._status.isConnected) {
      return;
    }

    try {
      // Convert normalized values to servo positions using calibration (required)
      const servoCommands = new Map<number, number>();
      
      command.joints.forEach(joint => {
        const servoId = this.jointToServoMap[joint.name as keyof typeof this.jointToServoMap];
        if (servoId) {
          const servoPosition = this.denormalizeValue(joint.value, joint.name);
          
          // Clamp to valid servo range
          const clampedPosition = Math.max(0, Math.min(4095, Math.round(servoPosition)));
          servoCommands.set(servoId, clampedPosition);
        }
      });

      if (servoCommands.size > 0) {
        // Use batch commands when possible for better performance
        if (servoCommands.size > 1) {
          await this.scsServoSDK.syncWritePositions(servoCommands);
        } else {
          // Single servo command
          const entry = servoCommands.entries().next().value;
          if (entry) {
            const [servoId, position] = entry;
            await this.scsServoSDK.writePosition(servoId, position);
          }
        }
        
        console.debug(`[${this.name}] Sent positions to ${servoCommands.size} servos`);
      }
      
    } catch (error) {
      console.error(`[${this.name}] Failed to execute command:`, error);
      throw error;
    }
  }


}