NERDDISCO commited on
Commit
0f7f2db
Β·
1 Parent(s): 24f0634

docs: update conventions

Browse files
Files changed (1) hide show
  1. docs/conventions.md +243 -13
docs/conventions.md CHANGED
@@ -15,6 +15,9 @@
15
  - **Serial API Separation**: Always use `serialport` package for Node.js and Web Serial API for browsers - never mix or bridge these incompatible APIs
16
  - **Minimal Console Output**: Only show essential information - reduce cognitive load for users
17
  - **Hardware-First Testing**: Always validate with real hardware, not just simulation
 
 
 
18
 
19
  ## Project Goals
20
 
@@ -285,22 +288,29 @@ lerobot/
285
  ##### 5. UI Framework Integration
286
 
287
  - **Node.js**: CLI-based interaction (inquirer, chalk)
288
- - **Web**: React components with real-time hardware data binding
289
- - **Critical Challenges Solved**:
290
- - **React.StrictMode**: Disabled for hardware interfaces (`src/demo/main.tsx`)
291
- - **Concurrent Access**: Single controlled serial operation via custom hooks
292
  - **Real-time Updates**: Hardware callbacks β†’ React state updates
293
  - **Professional UI**: shadcn Dialog, Card, Button components for robotics interfaces
294
  - **Architecture Pattern**:
 
295
  ```typescript
296
- // Custom hook for hardware state management
297
- function useCalibration(robot: ConnectedRobot) {
298
- const [controller, setController] =
299
- useState<WebCalibrationController | null>(null);
300
- // Stable dependencies to prevent infinite re-renders
301
- const startCalibration = useCallback(async () => {
302
- /* ... */
303
- }, [dependencies]);
 
 
 
 
 
 
304
  }
305
  ```
306
 
@@ -634,4 +644,224 @@ const STS3215_REGISTERS = {
634
  - **Loop Time**: < 5ms (when not reading positions)
635
  - **User Experience**: "Buttery smooth", "fucking working and super perfect"
636
 
637
- **Golden Rule**: When you achieve smooth control, NEVER change the step size, delays, or update patterns without extensive testing. These values were optimized through real hardware testing.\*\*
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  - **Serial API Separation**: Always use `serialport` package for Node.js and Web Serial API for browsers - never mix or bridge these incompatible APIs
16
  - **Minimal Console Output**: Only show essential information - reduce cognitive load for users
17
  - **Hardware-First Testing**: Always validate with real hardware, not just simulation
18
+ - **Library/Demo Separation**: Standard library handles hardware communication, demos handle storage/UI concerns - never mix these responsibilities
19
+ - **No Code Duplication**: Use shared utils, never reimplement the same functionality across files
20
+ - **Direct Library Usage**: End users call library functions directly (e.g., `calibrate()`, `teleoperate()`) - avoid unnecessary abstraction layers
21
 
22
  ## Project Goals
23
 
 
288
  ##### 5. UI Framework Integration
289
 
290
  - **Node.js**: CLI-based interaction (inquirer, chalk)
291
+ - **Web**: React components with direct library function usage
292
+ - **Critical Patterns**:
293
+ - **Direct Library Usage**: Call `calibrate()`, `teleoperate()` directly in components
294
+ - **Controlled Hardware Access**: Single controlled serial operation via refs
295
  - **Real-time Updates**: Hardware callbacks β†’ React state updates
296
  - **Professional UI**: shadcn Dialog, Card, Button components for robotics interfaces
297
  - **Architecture Pattern**:
298
+
299
  ```typescript
300
+ // Direct library usage in components (NO custom hooks)
301
+ function CalibrationPanel({ robot }) {
302
+ const [calibrationState, setCalibrationState] = useState();
303
+ const calibrationProcessRef = useRef(null);
304
+
305
+ useEffect(() => {
306
+ const initCalibration = async () => {
307
+ const process = await calibrate(robot, {
308
+ onLiveUpdate: setCalibrationState,
309
+ });
310
+ calibrationProcessRef.current = process;
311
+ };
312
+ initCalibration();
313
+ }, [robot]);
314
  }
315
  ```
316
 
 
644
  - **Loop Time**: < 5ms (when not reading positions)
645
  - **User Experience**: "Buttery smooth", "fucking working and super perfect"
646
 
647
+ **Golden Rule**: When you achieve smooth control, NEVER change the step size, delays, or update patterns without extensive testing. These values were optimized through real hardware testing.
648
+
649
+ ## Clean Library Architecture (Critical Lessons)
650
+
651
+ ### Standard Library Design Principles
652
+
653
+ **End users should be able to use the library with minimal effort and excellent UX:**
654
+
655
+ ```typescript
656
+ // βœ… PERFECT: Clean, self-contained library functions
657
+ const calibrationProcess = await calibrate(robotConnection, options);
658
+ const result = await calibrationProcess.result;
659
+
660
+ const teleoperationProcess = await teleoperate(robotConnection, options);
661
+ teleoperationProcess.start();
662
+ ```
663
+
664
+ **❌ WRONG: Custom hooks and abstraction layers**
665
+
666
+ ```typescript
667
+ // Never create hooks like useTeleoperation, useCalibration
668
+ // End users shouldn't need React to use robotics functions
669
+ ```
670
+
671
+ ### Library vs Demo Separation (CRITICAL)
672
+
673
+ **Library Responsibilities:**
674
+
675
+ - Hardware communication protocols
676
+ - Robot control logic
677
+ - Calibration algorithms
678
+ - Motor communication utilities
679
+ - Device-agnostic interfaces
680
+
681
+ **Demo Responsibilities:**
682
+
683
+ - localStorage/storage management
684
+ - UI state management
685
+ - JSON file export/import
686
+ - User interface components
687
+ - Application-specific workflows
688
+
689
+ **❌ WRONG: Mixing concerns**
690
+
691
+ ```typescript
692
+ // NEVER put localStorage in standard library
693
+ export function teleoperate(robot, options) {
694
+ const calibration = localStorage.getItem("calibration"); // ❌ Demo concern in library
695
+ }
696
+ ```
697
+
698
+ **βœ… CORRECT: Clean separation**
699
+
700
+ ```typescript
701
+ // Library: Pure hardware function
702
+ export function teleoperate(robot, options) {
703
+ // options.calibrationData passed from demo
704
+ }
705
+
706
+ // Demo: Handles storage
707
+ const calibrationData = getUnifiedRobotData(robot.serialNumber)?.calibration;
708
+ const process = await teleoperate(robot, { calibrationData });
709
+ ```
710
+
711
+ ### Utils Structure (No Code Duplication)
712
+
713
+ **Proper utils organization prevents reimplementation:**
714
+
715
+ ```
716
+ src/lerobot/web/utils/
717
+ β”œβ”€β”€ sts3215-protocol.ts # Protocol constants
718
+ β”œβ”€β”€ sign-magnitude.ts # Encoding/decoding
719
+ β”œβ”€β”€ serial-port-wrapper.ts # Web Serial wrapper
720
+ β”œβ”€β”€ motor-communication.ts # Core motor operations
721
+ └── motor-calibration.ts # Calibration functions
722
+ ```
723
+
724
+ **❌ WRONG: Duplicate implementations**
725
+
726
+ - Multiple files with same motor communication code
727
+ - Calibration logic copied across files
728
+ - Protocol constants scattered everywhere
729
+
730
+ **βœ… CORRECT: Single source of truth**
731
+
732
+ - Shared utilities with clear responsibilities
733
+ - Import from utils, never reimplement
734
+ - Kebab-case naming for consistency
735
+
736
+ ### Types Organization
737
+
738
+ **Types belong in dedicated directories, not mixed with business logic:**
739
+
740
+ ```
741
+ src/lerobot/web/types/
742
+ β”œβ”€β”€ robot-connection.ts # Core connection types
743
+ └── robot-config.ts # Hardware configuration types
744
+ ```
745
+
746
+ **❌ WRONG: Types in business logic files**
747
+
748
+ ```typescript
749
+ // Never export types from find_port.ts, calibrate.ts, etc.
750
+ import type { RobotConnection } from "./find_port.js"; // ❌ Bad architecture
751
+ ```
752
+
753
+ **βœ… CORRECT: Proper type imports**
754
+
755
+ ```typescript
756
+ import type { RobotConnection } from "./types/robot-connection.js"; // βœ… Clean
757
+ ```
758
+
759
+ ### Device-Agnostic Architecture
760
+
761
+ **Standard library must support multiple robot types without hardcoding:**
762
+
763
+ **βœ… CORRECT: Configuration-driven**
764
+
765
+ ```typescript
766
+ // Generic library function
767
+ export async function teleoperate(robotConnection, options) {
768
+ const config = createRobotConfig(robotConnection.robotType); // Device-specific
769
+ // ... generic logic using config
770
+ }
771
+
772
+ // Device-specific configuration
773
+ export function createSO100Config(type) {
774
+ return {
775
+ motorIds: [1, 2, 3, 4, 5, 6],
776
+ keyboardControls: SO100_KEYBOARD_CONTROLS,
777
+ // ... other device specifics
778
+ };
779
+ }
780
+ ```
781
+
782
+ **❌ WRONG: Hardcoded device values**
783
+
784
+ ```typescript
785
+ // Never hardcode in generic library
786
+ const KEYBOARD_CONTROLS = { w: "elbow_flex" }; // ❌ SO-100 specific in generic code
787
+ ```
788
+
789
+ ### Browser Keyboard Timing (Critical for Teleoperation)
790
+
791
+ **Browser keyboard repeat pattern:**
792
+
793
+ 1. Initial keydown β†’ immediate
794
+ 2. ~500ms delay (browser default)
795
+ 3. Rapid repeating
796
+
797
+ **βœ… CORRECT: Account for browser delays**
798
+
799
+ ```typescript
800
+ private readonly KEY_TIMEOUT = 600; // ms - longer than browser repeat delay
801
+ ```
802
+
803
+ **❌ WRONG: Too short timeout**
804
+
805
+ ```typescript
806
+ private readonly KEY_TIMEOUT = 100; // ❌ Causes pause during browser repeat delay
807
+ ```
808
+
809
+ ### State Update Callbacks (UI Responsiveness)
810
+
811
+ **Library must notify UI of state changes, especially when stopping:**
812
+
813
+ **βœ… CORRECT: Always notify on state changes**
814
+
815
+ ```typescript
816
+ stop(): void {
817
+ this.isActive = false;
818
+ // ... cleanup ...
819
+
820
+ // CRITICAL: Notify UI immediately
821
+ if (this.onStateUpdate) {
822
+ this.onStateUpdate(this.getState());
823
+ }
824
+ }
825
+ ```
826
+
827
+ ### Component Architecture (No Custom Hardware Hooks)
828
+
829
+ **Use library functions directly in components, just like calibration:**
830
+
831
+ **βœ… CORRECT: Direct library usage**
832
+
833
+ ```typescript
834
+ // In component
835
+ const [teleoperationState, setTeleoperationState] =
836
+ useState<TeleoperationState>();
837
+ const teleoperationProcessRef = useRef<TeleoperationProcess | null>(null);
838
+
839
+ useEffect(() => {
840
+ const initTeleoperation = async () => {
841
+ const process = await teleoperate(robot, {
842
+ onStateUpdate: setTeleoperationState,
843
+ });
844
+ teleoperationProcessRef.current = process;
845
+ };
846
+ initTeleoperation();
847
+ }, [robot]);
848
+ ```
849
+
850
+ **❌ WRONG: Custom hooks**
851
+
852
+ ```typescript
853
+ // Never create these - adds unnecessary complexity
854
+ const { start, stop, motorConfigs } = useTeleoperation(robot);
855
+ const { startCalibration, isActive } = useCalibration(robot);
856
+ ```
857
+
858
+ ### Architecture Success Metrics
859
+
860
+ **When architecture is correct:**
861
+
862
+ - βœ… End users can use library functions directly without React
863
+ - βœ… Adding new robot types requires zero changes to existing code
864
+ - βœ… Demo and library have zero shared dependencies
865
+ - βœ… No code duplication across files
866
+ - βœ… Types are properly organized and importable
867
+ - βœ… UI updates immediately reflect hardware state changes