File size: 4,084 Bytes
6ce4ca6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3165745
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
import type { USBCalibrationManager } from './USBCalibrationManager.js';
import type { JointCalibration } from '../models.js';
import { ROBOT_CONFIG } from '../config.js';

export class CalibrationState {
  private manager: USBCalibrationManager;
  
  // Reactive state using Svelte 5 runes - direct state management
  private _isCalibrating = $state(false);
  private _progress = $state(0);
  private _isCalibrated = $state(false);
  private _needsCalibration = $state(true);
  private _jointValues = $state<Record<string, number>>({});
  private _jointCalibrations = $state<Record<string, JointCalibration>>({});
  
  constructor(manager: USBCalibrationManager) {
    this.manager = manager;
    
    // Initialize reactive state
    manager.jointNames_.forEach(name => {
      this._jointValues[name] = 0;
      this._jointCalibrations[name] = { isCalibrated: false };
    });
    
    // Subscribe to manager changes
    this.setupManagerSubscription();
    
    // Initial state sync
    this.syncManagerState();
  }
  
  // Reactive getters - now use internal reactive state
  get isCalibrating(): boolean { return this._isCalibrating; }
  get progress(): number { return this._progress; }
  get isCalibrated(): boolean { return this._isCalibrated; }
  get needsCalibration(): boolean { return this._needsCalibration; }
  get jointValues(): Record<string, number> { return this._jointValues; }
  get jointCalibrations(): Record<string, JointCalibration> { return this._jointCalibrations; }
  
  // Get current value for a specific joint
  getCurrentValue(jointName: string): number | undefined {
    return this._jointValues[jointName];
  }
  
  // Get calibration for a specific joint
  getJointCalibration(jointName: string): JointCalibration | undefined {
    return this._jointCalibrations[jointName];
  }
  
  // Get range for a specific joint
  getJointRange(jointName: string): number {
    const calibration = this._jointCalibrations[jointName];
    if (!calibration?.minServoValue || !calibration?.maxServoValue) return 0;
    return Math.abs(calibration.maxServoValue - calibration.minServoValue);
  }
  
  private updateInterval: Timer | null = null;
  private managerUnsubscribe: (() => void) | null = null;

  private setupManagerSubscription(): void {
    // Use centralized config for UI update frequency
    this.updateInterval = setInterval(() => {
      this.syncManagerState();
    }, ROBOT_CONFIG.polling.uiUpdateRate); // Centralized UI update rate
    
    // Also listen to manager calibration changes for immediate updates
    const unsubscribe = this.manager.onCalibrationChange(() => {
      console.debug('[CalibrationState] Manager calibration changed, syncing state');
      this.syncManagerState();
    });
    
    // Store unsubscribe function for cleanup
    this.managerUnsubscribe = unsubscribe;
  }

  private syncManagerState(): void {
    // Sync manager state to reactive state
    this._isCalibrating = this.manager.calibrationState.isCalibrating;
    this._progress = this.manager.calibrationState.progress;
    this._isCalibrated = this.manager.isCalibrated;
    this._needsCalibration = this.manager.needsCalibration;
    
    // Update joint values and calibrations
    this.manager.jointNames_.forEach(name => {
      const currentValue = this.manager.getCurrentRawValue(name);
      if (currentValue !== undefined) {
        this._jointValues[name] = currentValue;
      }
      
      const calibration = this.manager.getJointCalibration(name);
      if (calibration) {
        // Create new object to ensure reactivity
        this._jointCalibrations[name] = { ...calibration };
      }
    }); 
  }

  // Cleanup method
  destroy(): void {
    if (this.updateInterval) {
      clearInterval(this.updateInterval);
      this.updateInterval = null;
    }
    if (this.managerUnsubscribe) {
      this.managerUnsubscribe();
      this.managerUnsubscribe = null;
    }
  }
  
  // Format servo value for display
  formatServoValue(value: number | undefined): string {
    return value !== undefined ? value.toString() : '---';
  }
}