Spaces:
Running
Running
File size: 4,387 Bytes
8eeb37a 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 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 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
import type { Consumer, RobotCommand } from '../models.js';
import { USBServoDriver } from './USBServoDriver.js';
import { ROBOT_CONFIG } from '../config.js';
export class USBConsumer extends USBServoDriver implements Consumer {
private commandCallbacks: ((command: RobotCommand) => void)[] = [];
private pollingInterval: ReturnType<typeof setInterval> | null = null;
private lastPositions: Record<number, number> = {};
private errorCount = 0;
constructor(config: any) {
super(config, 'Consumer');
}
async connect(): Promise<void> {
// Connect to USB first (this triggers browser's device selection dialog)
await this.connectToUSB();
// Unlock servos for manual movement (consumer mode)
await this.unlockAllServos();
// Note: Calibration is checked when operations are actually needed
}
async disconnect(): Promise<void> {
await this.stopListening();
await this.disconnectFromUSB();
}
async startListening(): Promise<void> {
if (!this._status.isConnected || this.pollingInterval !== null) {
return;
}
if (!this.isCalibrated) {
throw new Error('Cannot start listening: not calibrated');
}
console.log(`[${this.name}] Starting position listening...`);
this.errorCount = 0;
this.pollingInterval = setInterval(async () => {
try {
await this.pollAndBroadcastPositions();
this.errorCount = 0;
} catch (error) {
this.errorCount++;
console.warn(`[${this.name}] Polling error (${this.errorCount}):`, error);
if (this.errorCount >= ROBOT_CONFIG.polling.maxPollingErrors) {
console.warn(`[${this.name}] Too many polling errors, slowing down...`);
await this.stopListening();
setTimeout(() => this.startListening(), ROBOT_CONFIG.polling.errorBackoffRate);
}
}
}, ROBOT_CONFIG.polling.consumerPollingRate);
}
async stopListening(): Promise<void> {
if (this.pollingInterval !== null) {
clearInterval(this.pollingInterval);
this.pollingInterval = null;
console.log(`[${this.name}] Stopped position listening`);
}
}
// Event handlers already in base class
onCommand(callback: (command: RobotCommand) => void): () => void {
this.commandCallbacks.push(callback);
return () => {
const index = this.commandCallbacks.indexOf(callback);
if (index >= 0) {
this.commandCallbacks.splice(index, 1);
}
};
}
// Private methods
private async pollAndBroadcastPositions(): Promise<void> {
if (!this.scsServoSDK || !this._status.isConnected) {
return;
}
try {
// Read positions for all servos
const servoIds = Object.values(this.jointToServoMap);
const positions = await this.scsServoSDK.syncReadPositions(servoIds);
const jointsWithChanges: { name: string; value: number }[] = [];
// Check for position changes and convert to normalized values
Object.entries(this.jointToServoMap).forEach(([jointName, servoId]) => {
const currentPosition = positions.get(servoId);
const lastPosition = this.lastPositions[servoId];
if (currentPosition !== undefined &&
(lastPosition === undefined ||
Math.abs(currentPosition - lastPosition) > ROBOT_CONFIG.performance.jointUpdateThreshold)) {
this.lastPositions[servoId] = currentPosition;
// Convert to normalized value using calibration (required)
const normalizedValue = this.normalizeValue(currentPosition, jointName);
jointsWithChanges.push({
name: jointName,
value: normalizedValue
});
}
});
// Broadcast changes if any
if (jointsWithChanges.length > 0) {
const command: RobotCommand = {
timestamp: Date.now(),
joints: jointsWithChanges
};
this.notifyCommand(command);
}
} catch (error) {
throw error; // Re-throw for error handling in polling loop
}
}
private notifyCommand(command: RobotCommand): void {
this.commandCallbacks.forEach(callback => {
try {
callback(command);
} catch (error) {
console.error(`[${this.name}] Error in command callback:`, error);
}
});
}
} |