docs: consolidate and archive documentation

- Move 30+ outdated docs to docs/_archive/
- Move obsolete root files to _archive/
- Update README.md (Better Auth, current features)
- Update docs/README.md (new architecture diagram)
- Update docs/quick-reference.md (consolidated)
- Update docs/project-status.md (March 2026 state)
- Update docs/nao6-quick-reference.md (14 actions, Docker services)
- Update docs/implementation-guide.md (Better Auth, git submodule)
- Update docs/proposal.tex (timeline updates)
- Archive errors.txt, plugin_dump.json, test HTML files
This commit is contained in:
2026-03-22 16:38:28 -04:00
parent add3380307
commit cf3597881b
46 changed files with 439 additions and 1193 deletions

84
_archive/DOCUMENTATION.md Normal file
View File

@@ -0,0 +1,84 @@
# HRIStudio Documentation Overview
Clean, organized documentation for the HRIStudio platform.
## Quick Links
### Getting Started
- **[README.md](README.md)** - Main project overview and setup
- **[Quick Reference](docs/quick-reference.md)** - 5-minute setup guide
### HRIStudio Platform
- **[Project Overview](docs/project-overview.md)** - Features and architecture
- **[Database Schema](docs/database-schema.md)** - Complete database reference
- **[API Routes](docs/api-routes.md)** - tRPC API documentation
- **[Implementation Guide](docs/implementation-guide.md)** - Technical implementation
### NAO6 Robot Integration
- **[NAO6 Quick Reference](docs/nao6-quick-reference.md)** - Essential commands
- **[Integration Repository](../nao6-hristudio-integration/)** - Complete integration package
- Installation guide
- Usage instructions
- Troubleshooting
- Plugin definitions
### Experiment Design
- **[Core Blocks System](docs/core-blocks-system.md)** - Experiment building blocks
- **[Plugin System](docs/plugin-system-implementation-guide.md)** - Robot plugins
### Deployment
- **[Deployment & Operations](docs/deployment-operations.md)** - Production deployment
### Research
- **[Research Paper](docs/paper.md)** - Academic documentation
## Repository Structure
```
hristudio/ # Main web application
├── README.md # Start here
├── DOCUMENTATION.md # This file
├── src/ # Next.js application
├── docs/ # Platform documentation
└── ...
nao6-hristudio-integration/ # NAO6 integration
├── README.md # Integration overview
├── docs/ # NAO6 documentation
├── launch/ # ROS2 launch files
├── scripts/ # Utility scripts
├── plugins/ # Plugin definitions
└── examples/ # Usage examples
```
## Documentation Philosophy
- **One source of truth** - No duplicate docs
- **Clear hierarchy** - Easy to find what you need
- **Practical focus** - Real commands, not theory
- **Examples** - Working code samples
## For Researchers
Start here:
1. [README.md](README.md) - Setup HRIStudio
2. [NAO6 Quick Reference](docs/nao6-quick-reference.md) - Connect NAO robot
3. [Project Overview](docs/project-overview.md) - Understand the system
## For Developers
Start here:
1. [Implementation Guide](docs/implementation-guide.md) - Technical architecture
2. [Database Schema](docs/database-schema.md) - Data model
3. [API Routes](docs/api-routes.md) - Backend APIs
## Support
- Check documentation first
- Use NAO6 integration repo for robot-specific issues
- Main HRIStudio repo for platform issues
---
**Last Updated:** December 2024
**Version:** 1.0 (Simplified)

View File

@@ -0,0 +1,344 @@
# NAO6 Integration Handoff Document
**Date**: 2024-11-12
**Status**: ✅ Production Ready - Action Execution Pending
**Session Duration**: ~3 hours
---
## 🎯 What's Ready
### ✅ Completed
1. **Live Robot Connection** - NAO6 @ nao.local fully connected via ROS2
2. **Plugin System** - NAO6 ROS2 Integration plugin (v2.1.0) with 10 actions
3. **Database Integration** - Plugin installed, experiments seeded with NAO6 actions
4. **Web Test Interface** - `/nao-test` page working with live robot control
5. **Documentation** - 1,877 lines of comprehensive technical docs
6. **Repository Cleanup** - Consolidated into `robot-plugins` git repo (pushed to GitHub)
### 🚧 Next Step: Action Execution
**Current Gap**: Experiment designer → WebSocket → ROS2 → NAO flow not implemented
The plugin is loaded, actions are in the database, but clicking "Execute" in the wizard interface doesn't send commands to the robot yet.
---
## 🚀 Quick Start (For Next Agent)
### Terminal 1: Start NAO6 Integration
```bash
cd ~/Documents/Projects/nao6-hristudio-integration
./start-nao6.sh
```
**Expect**: Color-coded logs showing NAO Driver, ROS Bridge, ROS API running
### Terminal 2: Start HRIStudio
```bash
cd ~/Documents/Projects/hristudio
bun dev
```
**Access**: http://localhost:3000
### Verify Setup
1. **Test Page**: http://localhost:3000/nao-test
- Click "Connect" → Should turn green
- Click "Speak" → NAO should talk
- Movement buttons → NAO should move
2. **Experiment Designer**: http://localhost:3000/experiments/[id]/designer
- Check "Basic Interaction Protocol 1"
- Should see NAO6 actions in action library
- Drag actions to experiment canvas
3. **Database Check**:
```bash
bun db:seed # Should complete without errors
```
---
## 📁 Key File Locations
### NAO6 Integration Repository
```
~/Documents/Projects/nao6-hristudio-integration/
├── start-nao6.sh # START HERE - runs everything
├── nao6-plugin.json # Plugin definition (10 actions)
├── SESSION-SUMMARY.md # Complete session details
└── docs/ # Technical references
├── NAO6-ROS2-TOPICS.md (26 topics documented)
├── HRISTUDIO-ACTION-MAPPING.md (Action specs + TypeScript types)
└── INTEGRATION-SUMMARY.md (Quick reference)
```
### HRIStudio Project
```
~/Documents/Projects/hristudio/
├── robot-plugins/ # Git submodule @ github.com/soconnor0919/robot-plugins
│ └── plugins/
│ └── nao6-ros2.json # MAIN PLUGIN FILE (v2.1.0)
├── src/app/(dashboard)/nao-test/
│ └── page.tsx # Working test interface
├── src/components/experiments/designer/
│ └── ActionRegistry.ts # Loads plugin actions
└── scripts/seed-dev.ts # Seeds NAO6 plugin into DB
```
---
## 🔧 Current System State
### Database
- **2 repositories**: Core + Robot Plugins
- **4 plugins**: Core System, TurtleBot3 Burger, TurtleBot3 Waffle, **NAO6 ROS2 Integration**
- **NAO6 installed in**: "Basic Interaction Protocol 1" study
- **Experiment actions**: Step 1 has "NAO Speak Text", Step 3 has "NAO Move Head"
### ROS2 System
- **26 topics** available when `start-nao6.sh` is running
- **Key topics**: `/speech`, `/cmd_vel`, `/joint_angles`, `/joint_states`, `/bumper`, etc.
- **WebSocket**: ws://localhost:9090 (rosbridge_websocket)
### Robot
- **IP**: nao.local (134.82.159.168)
- **Credentials**: nao / robolab
- **Status**: Awake and responsive (test with ping)
---
## 🎯 Implementation Needed
### 1. Action Execution Flow
**Where to implement**:
- `src/components/trials/WizardInterface.tsx` or similar
- Connect "Execute Action" button → WebSocket → ROS2
**What it should do**:
```typescript
// When wizard clicks "Execute Action" on a NAO6 action
function executeNAO6Action(action: Action) {
// 1. Get action parameters from database
const { type, parameters } = action;
// 2. Connect to WebSocket (if not connected)
const ws = new WebSocket('ws://localhost:9090');
// 3. Map action type to ROS2 topic
const topicMapping = {
'nao6_speak': '/speech',
'nao6_move_forward': '/cmd_vel',
'nao6_move_head': '/joint_angles',
// ... etc
};
// 4. Create ROS message
const rosMessage = {
op: 'publish',
topic: topicMapping[type],
msg: formatMessageForROS(type, parameters)
};
// 5. Send to robot
ws.send(JSON.stringify(rosMessage));
// 6. Log to trial_events
logTrialEvent({
trial_id: currentTrialId,
event_type: 'action_executed',
event_data: { action, timestamp: Date.now() }
});
}
```
### 2. Message Formatting
**Reference**: See `nao6-hristudio-integration/docs/HRISTUDIO-ACTION-MAPPING.md`
**Examples**:
```typescript
function formatMessageForROS(actionType: string, params: any) {
switch(actionType) {
case 'nao6_speak':
return { data: params.text };
case 'nao6_move_forward':
return {
linear: { x: params.speed, y: 0, z: 0 },
angular: { x: 0, y: 0, z: 0 }
};
case 'nao6_move_head':
return {
joint_names: ['HeadYaw', 'HeadPitch'],
joint_angles: [params.yaw, params.pitch],
speed: params.speed,
relative: 0
};
}
}
```
### 3. WebSocket Connection Management
**Suggested approach**:
- Create `useROSBridge()` hook in `src/hooks/`
- Manage connection state, auto-reconnect
- Provide `publish()`, `subscribe()`, `callService()` methods
---
## 🧪 Testing Checklist
Before marking as complete:
- [ ] Can execute "Speak Text" action from wizard interface → NAO speaks
- [ ] Can execute "Move Forward" action → NAO walks
- [ ] Can execute "Move Head" action → NAO moves head
- [ ] Actions are logged to `trial_events` table
- [ ] Connection errors are handled gracefully
- [ ] Emergency stop works from wizard interface
- [ ] Multiple actions in sequence work
- [ ] Sensor monitoring displays in wizard interface
---
## 📚 Reference Documentation
### Primary Sources
1. **Working Example**: `src/app/(dashboard)/nao-test/page.tsx`
- Lines 67-100: WebSocket connection setup
- Lines 200-350: Action execution examples
- This is WORKING code - use it as template!
2. **Action Specifications**: `nao6-hristudio-integration/docs/HRISTUDIO-ACTION-MAPPING.md`
- Lines 1-100: Each action with parameters
- TypeScript types already defined
- WebSocket message formats included
3. **ROS2 Topics**: `nao6-hristudio-integration/docs/NAO6-ROS2-TOPICS.md`
- Complete message type definitions
- Examples for each topic
### TypeScript Types
Already defined in action mapping doc:
```typescript
interface SpeakTextAction {
action: 'nao6_speak';
parameters: {
text: string;
volume?: number;
};
}
interface MoveForwardAction {
action: 'nao6_move_forward';
parameters: {
speed: number;
duration: number;
};
}
```
---
## 🔍 Where to Look
### To understand plugin loading:
- `src/components/experiments/designer/ActionRegistry.ts`
- `src/components/experiments/designer/panels/ActionLibraryPanel.tsx`
### To see working WebSocket code:
- `src/app/(dashboard)/nao-test/page.tsx` (fully functional!)
### To find action execution trigger:
- Search for: `executeAction`, `onActionExecute`, `runAction`
- Likely in: `src/components/trials/` or `src/components/experiments/`
---
## 🚨 Important Notes
### DO NOT
- ❌ Modify `robot-plugins/` without committing/pushing (it's a git repo)
- ❌ Change plugin structure without updating seed script
- ❌ Remove `start-nao6.sh` - it's the main entry point
- ❌ Hard-code WebSocket URLs - use config/env vars
### DO
- ✅ Use existing `/nao-test` page code as reference
- ✅ Test with live robot frequently
- ✅ Log all actions to `trial_events` table
- ✅ Handle connection errors gracefully
- ✅ Add loading states for action execution
### Known Working
- ✅ WebSocket connection (`/nao-test` proves it works)
- ✅ ROS2 topics (26 topics verified)
- ✅ Plugin loading (shows in action library)
- ✅ Database integration (seed script works)
### Known NOT Working
- ❌ Action execution from experiment designer/wizard interface
- ❌ Sensor data display in wizard interface (topics exist, just not displayed)
- ❌ Camera streaming to browser
---
## 🤝 Session Handoff Summary
### What We Did
1. Connected to live NAO6 robot at nao.local
2. Documented all 26 ROS2 topics with complete specifications
3. Created clean plugin with 10 actions
4. Integrated plugin into HRIStudio database
5. Built working test interface proving WebSocket → ROS2 → NAO works
6. Cleaned up repositories (removed duplicates, committed to git)
7. Updated experiments to use NAO6 actions
8. Fixed APT repository issues
9. Created comprehensive documentation (1,877 lines)
### What's Left
**ONE THING**: Connect the experiment designer's "Execute Action" button to the WebSocket/ROS2 system.
The hard part is done. The `/nao-test` page is a fully working example of exactly what you need to do - just integrate that pattern into the wizard interface.
---
## 🎓 Key Insights
1. **We're NOT writing a WebSocket server** - using ROS2's official `rosbridge_websocket`
2. **The test page works perfectly** - copy its pattern
3. **All topics are documented** - no guesswork needed
4. **Plugin is in database** - just needs execution hookup
5. **Robot is live and responsive** - test frequently!
---
## ⚡ Quick Commands
```bash
# Start everything
cd ~/Documents/Projects/nao6-hristudio-integration && ./start-nao6.sh
cd ~/Documents/Projects/hristudio && bun dev
# Test robot
curl -X POST http://localhost:9090 -d '{"op":"publish","topic":"/speech","msg":{"data":"Test"}}'
# Reset robot
sshpass -p robolab ssh nao@nao.local \
"python2 -c 'import sys; sys.path.append(\"/opt/aldebaran/lib/python2.7/site-packages\"); import naoqi; p=naoqi.ALProxy(\"ALRobotPosture\",\"127.0.0.1\",9559); p.goToPosture(\"StandInit\", 0.5)'"
# Reseed database
cd ~/Documents/Projects/hristudio && bun db:seed
```
---
**Next Agent**: Start by reviewing `/nao-test/page.tsx` - it's the Rosetta Stone for this integration. Everything you need is already working there!
**Estimated Time**: 2-4 hours to implement action execution
**Difficulty**: Medium (pattern exists, just needs integration)
**Priority**: High (this is the final piece)
---
**Status**: 🟢 Ready for implementation
**Blocker**: None - all prerequisites met
**Dependencies**: Robot must be running (`start-nao6.sh`)

View File

@@ -0,0 +1,374 @@
# HRIStudio Project Backlog - Honors Thesis Research
## Project Overview
**Student**: Sean O'Connor
**Thesis Title**: A Web-Based Wizard-of-Oz Platform for Collaborative and Reproducible Human-Robot Interaction Research
**Timeline**: Fall 2025 - Spring 2026
**Current Date**: September 23, 2025
## Current Status Assessment
### Platform Strengths
- **Core Platform Complete**: Production-ready backend with 12 tRPC routers, 31 database tables
- **Visual Designer**: Repository-based plugin system with 26+ core blocks
- **Type Safety**: Clean TypeScript throughout, passes `bun typecheck`
- **Authentication**: Role-based access control (Admin, Researcher, Wizard, Observer)
- **Development Environment**: Comprehensive seed data and documentation
### Critical Gaps
- **Wizard Interface**: ✅ COMPLETE - Role-based views implemented (Wizard, Observer, Participant)
- **Robot Control**: Not working yet - core functionality missing (NEXT PRIORITY)
- **NAO6 Integration**: Cannot test without working robot control
- **Trial Execution**: WebSocket implementation needed for real-time functionality
### Platform Constraints
- **Device Target**: Laptop-only (no mobile/tablet optimization needed)
- **Robot Platform**: NAO6 humanoid robot
- **Study Focus**: Comparative usability study, not full platform development
## Academic Timeline
### Fall 2025 Semester
- **Current Date**: September 23, 2025
- **Goal**: Functional platform + IRB submission by end of December
### Winter Break
- **December - January 2026**: IRB approval process and final preparations
### Spring 2026 Semester
- **January - February 2026**: User study execution (10-12 participants)
- **March 2026**: Data analysis and results drafting
- **April 2026**: Thesis defense preparation and execution
- **May 2026**: Final thesis submission
## Research Study Design
### Comparison Study
- **Control Group**: Choregraphe (manufacturer software for NAO6)
- **Experimental Group**: HRIStudio platform
- **Task**: Recreate well-documented HRI experiment from literature
- **Participants**: 10-12 non-engineering researchers (Psychology, Education, etc.)
- **Metrics**: Methodological consistency, user experience, completion times, error rates
### Success Criteria
- Functional wizard interface for real-time experiment control
- Reliable NAO6 robot integration
- Reference experiment implemented in both platforms
- Platform usable by non-programmers with minimal training
- Comprehensive data collection for comparative analysis
## Development Backlog by Timeline
### Phase 1: Core Development (September 23 - October 31, 2025)
**Goal**: Get essential systems working - 5-6 weeks available
#### Week 1-2: Foundation (Sept 23 - Oct 6)
**WIZARD-001: Wizard Interface Architecture** - ✅ COMPLETE (December 2024)
- **Story**: As a wizard, I need a functional interface to control experiments
- **Tasks**:
- ✅ Design wizard interface wireframes and user flow
- ✅ Implement three-panel layout (trial controls, execution view, monitoring)
- ✅ Create role-based views (Wizard, Observer, Participant)
- ✅ Build step navigation and progress tracking
- ✅ Fix layout issues (double headers, bottom cut-off)
- **Deliverable**: Complete wizard interface with role-based views
- **Effort**: 12 days (completed)
**ROBOT-001: Robot Control Foundation** - CRITICAL (NEXT PRIORITY)
- **Story**: As a wizard, I need to send commands to NAO6 robot
- **Tasks**:
- Research and implement NAO6 WebSocket connection
- Create basic action execution engine
- Implement mock robot mode for development
- Build connection status monitoring
- Integrate with existing wizard interface
- **Deliverable**: Robot connection established with basic commands
- **Effort**: 8 days (increased due to WebSocket server implementation needed)
**WEBSOCKET-001: Real-Time Infrastructure** - CRITICAL (NEW PRIORITY)
- **Story**: As a system, I need real-time communication between clients and robots
- **Tasks**:
- Implement WebSocket server for real-time trial coordination
- Create multi-client session management (wizard, observers, participants)
- Build event broadcasting system for live trial updates
- Add robust connection recovery and fallback mechanisms
- **Deliverable**: Working real-time infrastructure for trial execution
- **Effort**: 10 days
#### Week 3-4: Core Functionality (Oct 7 - Oct 20)
**ROBOT-002: Essential NAO6 Actions** - CRITICAL
- **Story**: As a wizard, I need basic robot actions for experiments
- **Tasks**:
- Implement speech synthesis and playback
- Add basic movement commands (walk, turn, sit, stand)
- Create simple gesture library
- Add LED color control
- Implement error handling and recovery
- Integrate with WebSocket infrastructure for real-time control
- **Deliverable**: NAO6 performs essential experiment actions reliably via wizard interface
- **Effort**: 10 days (increased due to real-time integration)
**TRIAL-001: Trial Execution Engine** - HIGH PRIORITY
- **Story**: As a wizard, I need to execute experiment protocols step-by-step
- **Tasks**:
- ✅ Basic trial state machine exists (needs WebSocket integration)
- Connect existing wizard interface to real-time execution
- Enhance event logging with real-time broadcasting
- Add manual intervention controls via WebSocket
- Build trial completion and data export
- **Deliverable**: Complete trial execution with real-time data capture
- **Effort**: 8 days (integration with existing wizard interface)
#### Week 5-6: Integration & Testing (Oct 21 - Oct 31)
**INTEGRATION-001: End-to-End Workflow** - CRITICAL
- **Story**: As a researcher, I need complete workflow from design to execution
- **Tasks**:
- Connect visual designer to trial execution
- Test complete workflow: design → schedule → execute → analyze
- Fix critical bugs and performance issues
- Validate data consistency throughout pipeline
- **Deliverable**: Working end-to-end experiment workflow
- **Effort**: 8 days
### Phase 2: User Experience & Study Preparation (November 1-30, 2025)
**Goal**: Make platform usable and prepare study materials - 4 weeks available
#### Week 1-2: User Experience (Nov 1 - Nov 14)
**UX-001: Non-Programmer Interface** - HIGH PRIORITY
- **Story**: As a psychology researcher, I need intuitive tools to recreate experiments
- **Tasks**:
- Simplify visual designer for non-technical users
- Add contextual help and guided tutorials
- Implement undo/redo functionality
- Create error prevention and recovery mechanisms
- Add visual feedback for successful actions
- **Deliverable**: Interface usable by non-programmers
- **Effort**: 10 days
#### Week 3-4: Study Foundation (Nov 15 - Nov 30)
**STUDY-001: Reference Experiment** - HIGH PRIORITY
- **Story**: As a researcher, I need a validated experiment for comparison study
- **Tasks**:
- Select appropriate HRI experiment from literature
- Implement in HRIStudio visual designer
- Create equivalent Choregraphe implementation
- Validate both versions work correctly
- Document implementation decisions and constraints
- **Deliverable**: Reference experiment working in both platforms
- **Effort**: 8 days
**IRB-001: IRB Application Preparation** - CRITICAL
- **Story**: As a researcher, I need IRB approval for user study
- **Tasks**:
- Draft complete IRB application
- Create consent forms and participant materials
- Design study protocols and procedures
- Prepare risk assessment and mitigation plans
- Design data collection and privacy protection measures
- **Deliverable**: Complete IRB application ready for submission
- **Effort**: 6 days
### Phase 3: Polish & IRB Submission (December 1-31, 2025)
**Goal**: Finalize platform and submit IRB - 4 weeks available
#### Week 1-2: Platform Validation (Dec 1 - Dec 14)
**VALIDATE-001: Platform Reliability** - CRITICAL
- **Story**: As a researcher, I need confidence the platform works reliably
- **Tasks**:
- Conduct extensive testing with multiple scenarios
- Fix any critical bugs or stability issues
- Test on different laptop configurations (Mac, PC, browsers)
- Validate data collection and export functionality
- Performance optimization for laptop hardware
- **Deliverable**: Stable, reliable platform ready for study use
- **Effort**: 10 days
**TRAIN-001: Training Materials** - HIGH PRIORITY
- **Story**: As study participants, we need equivalent training for both platforms
- **Tasks**:
- Create HRIStudio training workshop materials
- Develop Choregraphe training equivalent
- Record instructional videos
- Create quick reference guides and cheat sheets
- Design hands-on practice exercises
- **Deliverable**: Complete training materials for both platforms
- **Effort**: 4 days
#### Week 3-4: Final Preparations (Dec 15-31)
**IRB-002: IRB Submission** - CRITICAL
- **Story**: As a researcher, I need IRB approval to proceed with human subjects
- **Tasks**:
- Finalize IRB application with all supporting materials
- Submit to university IRB committee
- Respond to any initial questions or clarifications
- Prepare for potential revisions or additional requirements
- **Deliverable**: IRB application submitted and under review
- **Effort**: 2 days
**PILOT-001: Internal Pilot Testing** - HIGH PRIORITY
- **Story**: As a researcher, I need to validate study methodology
- **Tasks**:
- Recruit 2-3 internal pilot participants from target demographic
- Run complete study protocol with both platforms
- Test wizard interface reliability during real sessions
- Identify and fix any procedural issues
- Refine training materials based on feedback
- Document lessons learned and methodology improvements
- **Deliverable**: Validated study methodology ready for execution
- **Effort**: 6 days
### Phase 4: Study Execution (January - February 2026)
**Goal**: Execute user study with 10-12 participants
#### Study Preparation (January 2026)
**RECRUIT-001: Participant Recruitment**
- **Story**: As a researcher, I need to recruit qualified study participants
- **Tasks**:
- Create participant screening survey
- Recruit from Psychology, Education, and other non-engineering departments
- Schedule study sessions to avoid conflicts
- Send confirmation and preparation materials
- **Deliverable**: 10-12 confirmed participants scheduled
- **Effort**: Ongoing through January
**EXECUTE-001: Study Session Management**
- **Story**: As a researcher, I need reliable execution of each study session
- **Tasks**:
- Create detailed session procedures and checklists
- Implement real-time monitoring dashboard for study staff
- Build backup procedures for technical failures
- Create automated data validation after each session
- Design post-session debriefing workflows
- **Deliverable**: Reliable study execution infrastructure
- **Effort**: 4 days
#### Data Collection (February 2026)
**DATA-001: Comprehensive Data Collection**
- **Story**: As a researcher, I need rich data for comparative analysis
- **Tasks**:
- Automatic time-tracking for all participant actions
- User interaction logging (clicks, errors, help usage)
- Screen recording of participant sessions
- Post-task survey integration
- Experiment fidelity scoring system
- **Deliverable**: Complete behavioral and performance data
- **Effort**: Built into platform, minimal additional work
### Phase 5: Analysis & Writing (March - May 2026)
**Goal**: Analyze results and complete thesis
#### Analysis Tools (March 2026)
**ANALYSIS-001: Quantitative Analysis Support**
- **Story**: As a researcher, I need tools to analyze study results
- **Tasks**:
- Implement automated experiment fidelity scoring
- Build statistical comparison tools for platform differences
- Create completion time and error rate analysis
- Generate charts and visualizations for thesis
- Export data in formats suitable for statistical software (R, SPSS)
- **Deliverable**: Analysis-ready data and initial results
- **Effort**: 5 days
#### Thesis Writing (March - May 2026)
**THESIS-001: Results and Discussion**
- **Tasks**:
- Quantitative analysis of methodological consistency
- Qualitative analysis of participant feedback
- Statistical comparison of user experience metrics
- Discussion of implications for HRI research
- **Deliverable**: Thesis chapters 4-5 (Results and Discussion)
**THESIS-002: Conclusion and Defense Preparation**
- **Tasks**:
- Synthesis of research contributions
- Limitations and future work discussion
- Defense presentation preparation
- Final thesis formatting and submission
- **Deliverable**: Complete thesis and successful defense
## Critical Success Factors
### End of December 2025 Must-Haves
1. **Functional Platform**: Wizard can execute experiments with NAO6 robot
2. **Reference Experiment**: Working implementation in both HRIStudio and Choregraphe
3. **User-Ready Interface**: Non-programmers can use with minimal training
4. **IRB Application**: Submitted and under review
5. **Training Materials**: Complete workshop materials for both platforms
6. **Pilot Validation**: Study methodology tested and refined
### Risk Mitigation Strategies
**Technical Risks**
- **Robot Hardware Failure**: Have backup NAO6 unit available, implement robust mock mode
- **Platform Stability**: Extensive testing across different laptop configurations
- **Data Loss**: Implement automatic session backup and recovery
- **Performance Issues**: Optimize for older laptop hardware
**Study Execution Risks**
- **Participant Recruitment**: Start early, have backup recruitment channels
- **Learning Curve**: Extensive pilot testing to refine training materials
- **Platform Comparison Fairness**: Ensure equivalent training quality for both platforms
- **IRB Delays**: Submit early with complete application to allow for revisions
**Timeline Risks**
- **Development Delays**: Focus on minimum viable features for research needs
- **Academic Calendar**: Align all deadlines with university schedule
- **Winter Break**: Use break time for IRB follow-up and final preparations
## Sprint Planning
### October Sprint (Core Development)
- **Total Development Days**: 28 days
- **Key Milestone**: Working wizard interface + robot control
- **Priority**: Technical foundation - everything depends on this
### November Sprint (User Experience & Study Prep)
- **Total Development Days**: 24 days
- **Key Milestone**: Non-programmer ready interface + IRB draft
- **Priority**: Usability and study preparation
### December Sprint (Polish & Launch Prep)
- **Total Development Days**: 22 days
- **Key Milestone**: IRB submitted + reliable platform
- **Priority**: Quality assurance and study readiness
### Buffer and Contingency
- **Built-in Buffer**: 10-15% buffer time in each sprint for unexpected issues
- **Parallel Workstreams**: IRB preparation can happen alongside platform development
- **Fallback Options**: Mock robot mode if hardware integration proves challenging
- **Academic Alignment**: All deadlines respect university calendar and requirements
## Success Metrics for Thesis Research
### Primary Research Outcomes
- **Methodological Consistency**: Quantitative fidelity scores comparing participant implementations to reference experiment
- **User Experience**: Task completion rates, error rates, time-to-completion, satisfaction scores
- **Accessibility**: Learning curve differences between platforms, help-seeking behavior
- **Efficiency**: Setup time, execution time, and total task completion time comparisons
### Platform Quality Gates
- Zero critical bugs during study sessions
- Sub-100ms response time for core wizard interface interactions
- 100% data collection success rate across all study sessions
- Participant satisfaction score > 4.0/5.0 for HRIStudio usability
- Successful completion of reference experiment by 90%+ of participants
### Thesis Contributions
- **Empirical Evidence**: Quantitative comparison of WoZ platform approaches
- **Design Insights**: Specific recommendations for accessible HRI research tools
- **Methodological Framework**: Validated approach for comparing research software platforms
- **Open Source Contribution**: Functional platform available for broader HRI community
This backlog prioritizes research success over platform perfection, focusing on delivering the minimum viable system needed to conduct a rigorous comparative study while maintaining the scientific integrity required for honors thesis research.

279
_archive/TRIAL_START_DEBUG.md Executable file
View File

@@ -0,0 +1,279 @@
# Trial Start Debug Guide
## ❌ **Problem**: "I can't start the trial"
This guide will help you systematically debug why the trial start functionality isn't working.
---
## 🔍 **Step 1: Verify System Setup**
### Database Connection
```bash
# Check if database is running
docker ps | grep postgres
# If not running, start it
bun run docker:up
# Check database schema is up to date
bun db:push
# Verify seed data exists
bun db:seed
```
### Build Status
```bash
# Ensure project builds without errors
bun run build
# Should complete successfully with no TypeScript errors
```
---
## 🔍 **Step 2: Browser-Based Testing**
### Access the Wizard Interface
1. Start dev server: `bun dev`
2. Open browser: `http://localhost:3000`
3. Login: `sean@soconnor.dev` / `password123`
4. Navigate: Studies → [First Study] → Trials → [First Trial] → Wizard Interface
### Check Browser Console
Open Developer Tools (F12) and look for:
**Expected Debug Messages** (when clicking "Start Trial"):
```
[WizardInterface] Starting trial: <id> Current status: scheduled
[WizardControlPanel] Start Trial clicked
```
**Error Messages to Look For**:
- Network errors (red entries in Console)
- tRPC errors (search for "trpc" or "TRPC")
- Authentication errors (401/403 status codes)
- Database errors (check Network tab)
---
## 🔍 **Step 3: Test Database Access**
### Quick API Test
Visit this URL in your browser while dev server is running:
```
http://localhost:3000/api/test-trial
```
**Expected Response**:
```json
{
"success": true,
"message": "Database connection working",
"trials": [...],
"count": 4
}
```
**If you get an error**, the database connection is broken.
### Check Specific Trial
If the above works, test with a specific trial ID:
```
http://localhost:3000/api/test-trial?id=<trial-id-from-above-response>
```
---
## 🔍 **Step 4: Verify Trial Status**
### Requirements for Starting Trial
1. **Trial must exist** - Check API response has trials
2. **Trial must be "scheduled"** - Status should be "scheduled", not "in_progress" or "completed"
3. **User must have permissions** - Must be administrator, researcher, or wizard role
4. **Experiment must have steps** - Trial needs an experiment with defined steps
### Check Trial Data
In browser console, after navigating to wizard interface:
```javascript
// Check trial data
console.log("Current trial:", window.location.pathname);
// Check user session
fetch('/api/auth/session').then(r => r.json()).then(console.log);
```
---
## 🔍 **Step 5: tRPC API Testing**
### Test tRPC Endpoint Directly
In browser console on the wizard page:
```javascript
// This should work if you're on the wizard interface page
// Replace 'TRIAL_ID' with actual trial ID from URL
fetch('/api/trpc/trials.get?batch=1&input={"0":{"json":{"id":"TRIAL_ID"}}}')
.then(r => r.json())
.then(console.log);
```
### Test Start Trial Endpoint
```javascript
// Test the start trial mutation
fetch('/api/trpc/trials.start', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
"0": {
"json": {
"id": "TRIAL_ID_HERE"
}
}
})
}).then(r => r.json()).then(console.log);
```
---
## 🔍 **Step 6: Common Issues & Fixes**
### Issue: "Start Trial" Button Doesn't Respond
- Check browser console for JavaScript errors
- Verify the button isn't disabled
- Check if `isStarting` state is stuck on `true`
### Issue: Network Error / API Not Found
- Check middleware isn't blocking tRPC routes
- Verify NextAuth session is valid
- Check if API routes are properly built
### Issue: Permission Denied
- Check user role: must be administrator, researcher, or wizard
- Verify study membership if role-based access is enabled
- Check `checkTrialAccess` function in trials router
### Issue: "Trial can only be started from scheduled status"
- Current trial status is not "scheduled"
- Find a trial with "scheduled" status or create one manually
- Check seed data created scheduled trials properly
### Issue: Database Connection Error
- Database container not running
- Environment variables missing/incorrect
- Schema not pushed or out of date
---
## 🔧 **Manual Debugging Steps**
### Create Test Trial
If no scheduled trials exist:
```sql
-- Connect to database and create a test trial
INSERT INTO trial (
id,
experiment_id,
participant_id,
status,
session_number,
scheduled_at,
created_at,
updated_at
) VALUES (
gen_random_uuid(),
'EXPERIMENT_ID_HERE',
'PARTICIPANT_ID_HERE',
'scheduled',
1,
NOW(),
NOW(),
NOW()
);
```
### Check User Permissions
```sql
-- Check user system roles
SELECT u.email, usr.role
FROM users u
LEFT JOIN user_system_roles usr ON u.id = usr.user_id
WHERE u.email = 'sean@soconnor.dev';
-- Check study memberships
SELECT u.email, sm.role, s.name as study_name
FROM users u
LEFT JOIN study_members sm ON u.id = sm.user_id
LEFT JOIN studies s ON sm.study_id = s.id
WHERE u.email = 'sean@soconnor.dev';
```
---
## 🚨 **Emergency Fixes**
### Quick Reset
```bash
# Complete reset of database and seed data
bun run docker:down
bun run docker:up
bun db:push
bun db:seed
```
### Bypass Authentication (Development Only)
In `src/server/api/routers/trials.ts`, temporarily comment out the permission check:
```typescript
// await checkTrialAccess(db, userId, input.id, [
// "owner",
// "researcher",
// "wizard",
// ]);
```
---
## 📞 **Getting Help**
If none of the above steps resolve the issue:
1. **Provide the following information**:
- Output of `/api/test-trial`
- Browser console errors (screenshots)
- Network tab showing failed requests
- Current user session info
- Trial ID you're trying to start
2. **Include environment details**:
- Operating system
- Node.js version (`node --version`)
- Bun version (`bun --version`)
- Docker status (`docker ps`)
3. **Steps you've already tried** from this guide
---
## ✅ **Success Indicators**
When trial start is working correctly, you should see:
1. **Debug logs in console**:
```
[WizardInterface] Starting trial: abc123 Current status: scheduled
[WizardControlPanel] Start Trial clicked
[WizardInterface] Trial started successfully
```
2. **UI changes**:
- "Start Trial" button disappears/disables
- Toast notification: "Trial started successfully"
- Trial status badge changes to "in progress"
- Control buttons appear (Pause, Next, Complete, Abort)
3. **Database changes**:
- Trial status changes from "scheduled" to "in_progress"
- `started_at` timestamp is set
- Trial event is logged with type "trial_started"
The trial start functionality is working when all three indicators occur successfully.

View File

@@ -0,0 +1,193 @@
# Wizard Interface - Implementation Complete ✅
## Overview
The Wizard Interface for HRIStudio has been completely implemented and is production-ready. All issues identified have been resolved, including duplicate headers, hardcoded data usage, and WebSocket integration.
## What Was Fixed
### 🔧 Duplicate Headers Removed
- **Problem**: Cards on the right side had duplicated headers when wrapped in `EntityViewSection`
- **Solution**: Removed redundant `Card` components and replaced with simple `div` elements
- **Files Modified**:
- `ParticipantInfo.tsx` - Removed Card headers, used direct div styling
- `RobotStatus.tsx` - Cleaned up duplicate title sections
- `WizardInterface.tsx` - Proper EntityViewSection usage
### 📊 Real Experiment Data Integration
- **Problem**: Using hardcoded mock data instead of actual experiment steps
- **Solution**: Integrated with `api.experiments.getSteps` to load real database content
- **Implementation**:
```typescript
const { data: experimentSteps } = api.experiments.getSteps.useQuery({
experimentId: trial.experimentId
});
```
- **Type Mapping**: Database step types ("wizard", "robot") mapped to component types ("wizard_action", "robot_action")
### 🔗 WebSocket System Integration
- **Status**: Fully operational WebSocket server at `/api/websocket`
- **Features**:
- Real-time trial status updates
- Live step transitions
- Wizard intervention logging
- Automatic reconnection with exponential backoff
- **Visual Indicators**: Connection status badges (green "Real-time", yellow "Connecting", red "Offline")
### 🛡️ Type Safety Improvements
- **Fixed**: All `any` types in ParticipantInfo demographics handling
- **Improved**: Nullish coalescing (`??`) instead of logical OR (`||`)
- **Added**: Proper type mapping for step properties
## Current System State
### ✅ Production Ready Features
- **Trial Execution**: Start, conduct, and finish trials using real experiment data
- **Step Navigation**: Execute actual protocol steps from experiment designer
- **Robot Integration**: Support for TurtleBot3 and NAO robots via plugin system
- **Real-time Monitoring**: Live event logging and status updates
- **Participant Management**: Complete demographic information display
- **Professional UI**: Consistent with platform design standards
### 📋 Seed Data Available
Run `bun db:seed` to populate test environment:
- **2 Experiments**: "Basic Interaction Protocol 1" and "Dialogue Timing Pilot"
- **8 Participants**: Complete demographics and consent status
- **Multiple Trials**: Various states (scheduled, in_progress, completed)
- **Robot Plugins**: NAO and TurtleBot3 configurations
## How to Use the WebSocket System
### 1. Automatic Connection
The wizard interface connects automatically when you access a trial:
- URL: `wss://localhost:3000/api/websocket?trialId={ID}&token={AUTH}`
- Authentication: Session-based token validation
- Reconnection: Automatic with exponential backoff
### 2. Message Types
**Outgoing (Wizard → Server)**:
- `trial_action`: Start, complete, or abort trials
- `wizard_intervention`: Log manual interventions
- `step_transition`: Advance to next protocol step
**Incoming (Server → Wizard)**:
- `trial_status`: Current trial state and step index
- `trial_action_executed`: Action confirmation
- `step_changed`: Step transition notifications
### 3. Real-time Features
- **Live Status**: Trial progress and robot status updates
- **Event Logging**: All actions logged with timestamps
- **Multi-client**: Multiple wizards can monitor same trial
- **Error Handling**: Graceful fallback to polling if WebSocket fails
## Quick Start Guide
### 1. Setup Environment
```bash
bun install # Install dependencies
bun db:push # Apply database schema
bun db:seed # Load test data
bun dev # Start development server
```
### 2. Access Wizard Interface
1. Login: `sean@soconnor.dev` / `password123`
2. Navigate: Dashboard → Studies → Select Study → Trials
3. Find trial with "scheduled" status
4. Click "Wizard Control" button
### 3. Conduct Trial
1. Verify green "Real-time" connection badge
2. Review experiment steps and participant info
3. Click "Start Trial" to begin
4. Execute steps using "Next Step" button
5. Monitor robot status and live event log
6. Click "Complete" when finished
## Testing with Seed Data
### Available Experiments
**"Basic Interaction Protocol 1"**:
- Step 1: Wizard shows object + NAO says greeting
- Step 2: Wizard waits for participant response
- Step 3: Robot LED feedback or wizard note
**"Dialogue Timing Pilot"**:
- Parallel actions (wizard gesture + robot animation)
- Conditional logic with timer-based transitions
- Complex multi-step protocol
### Robot Actions
- **NAO Say Text**: TTS with configurable parameters
- **NAO Set LED Color**: Visual feedback system
- **NAO Play Animation**: Gesture sequences
- **Wizard Fallbacks**: Manual alternatives when robots unavailable
## Architecture Highlights
### Design Patterns
- **EntityViewSection**: Consistent layout across all pages
- **Unified Components**: Maximum reusability, minimal duplication
- **Type Safety**: Strict TypeScript throughout
- **Real-time First**: WebSocket primary, polling fallback
### Performance Features
- **Smart Polling**: Reduced frequency when WebSocket connected
- **Local State**: Efficient React state management
- **Event Batching**: Optimized message handling
- **Selective Updates**: Only relevant changes broadcast
## Files Modified
### Core Components
- `src/components/trials/wizard/WizardInterface.tsx` - Main wizard control panel
- `src/components/trials/wizard/ParticipantInfo.tsx` - Demographics display
- `src/components/trials/wizard/RobotStatus.tsx` - Robot monitoring panel
### API Integration
- `src/hooks/useWebSocket.ts` - WebSocket connection management
- `src/app/api/websocket/route.ts` - Real-time server endpoint
### Documentation
- `docs/wizard-interface-guide.md` - Complete usage documentation
- `docs/wizard-interface-summary.md` - Technical implementation details
## Production Deployment
### Environment Setup
```env
DATABASE_URL=postgresql://user:pass@host:port/dbname
NEXTAUTH_SECRET=your-secret-key
NEXTAUTH_URL=https://your-domain.com
```
### WebSocket Configuration
- **Protocol**: Automatic HTTP → WebSocket upgrade
- **Security**: Role-based access control
- **Scaling**: Per-trial room isolation
- **Monitoring**: Connection status and error logging
## Success Criteria Met ✅
-**No Duplicate Headers**: Clean, professional interface
-**Real Data Integration**: Uses actual experiment steps from database
-**WebSocket Functionality**: Live real-time trial control
-**Type Safety**: Strict TypeScript throughout
-**Production Quality**: Matches platform design standards
## Next Steps (Optional Enhancements)
- [ ] Observer-only interface for read-only monitoring
- [ ] Pause/resume functionality during trials
- [ ] Enhanced analytics and visualization
- [ ] Voice control for hands-free operation
- [ ] Mobile-responsive design
---
**Status**: ✅ COMPLETE - Production Ready
**Last Updated**: December 2024
**Version**: 1.0.0
The wizard interface is now a fully functional, professional-grade control system for conducting Human-Robot Interaction studies with real-time monitoring and comprehensive data capture.

45
_archive/errors.txt Normal file
View File

@@ -0,0 +1,45 @@
scripts/seed-dev.ts(762,61): error TS2769: No overload matches this call.
Overload 1 of 2, '(value: { experimentId: string | SQL<unknown> | Placeholder<string, any>; duration?: number | SQL<unknown> | Placeholder<string, any> | null | undefined; id?: string | ... 2 more ... | undefined; ... 11 more ...; parameters?: unknown; }): PgInsertBase<...>', gave the following error.
Object literal may only specify known properties, and 'currentStepId' does not exist in type '{ experimentId: string | SQL<unknown> | Placeholder<string, any>; duration?: number | SQL<unknown> | Placeholder<string, any> | null | undefined; id?: string | SQL<...> | Placeholder<...> | undefined; ... 11 more ...; parameters?: unknown; }'.
Overload 2 of 2, '(values: { experimentId: string | SQL<unknown> | Placeholder<string, any>; duration?: number | SQL<unknown> | Placeholder<string, any> | null | undefined; id?: string | ... 2 more ... | undefined; ... 11 more ...; parameters?: unknown; }[]): PgInsertBase<...>', gave the following error.
Object literal may only specify known properties, and 'experimentId' does not exist in type '{ experimentId: string | SQL<unknown> | Placeholder<string, any>; duration?: number | SQL<unknown> | Placeholder<string, any> | null | undefined; id?: string | SQL<...> | Placeholder<...> | undefined; ... 11 more ...; parameters?: unknown; }[]'.
src/app/(dashboard)/studies/[id]/trials/[trialId]/analysis/page.tsx(99,13): error TS2322: Type '{ startedAt: Date | null; completedAt: Date | null; eventCount: any; mediaCount: any; media: { url: string; contentType: string; id: string; trialId: string; mediaType: "video" | "audio" | "image" | null; ... 8 more ...; createdAt: Date; }[]; ... 13 more ...; participant: { ...; }; }' is not assignable to type '{ id: string; status: string; startedAt: Date | null; completedAt: Date | null; duration: number | null; experiment: { name: string; studyId: string; }; participant: { participantCode: string; }; eventCount?: number | undefined; mediaCount?: number | undefined; media?: { ...; }[] | undefined; }'.
Types of property 'media' are incompatible.
Type '{ url: string; contentType: string; id: string; trialId: string; mediaType: "video" | "audio" | "image" | null; storagePath: string; fileSize: number | null; duration: number | null; format: string | null; ... 4 more ...; createdAt: Date; }[]' is not assignable to type '{ url: string; mediaType: string; format?: string | undefined; contentType?: string | undefined; }[]'.
Type '{ url: string; contentType: string; id: string; trialId: string; mediaType: "video" | "audio" | "image" | null; storagePath: string; fileSize: number | null; duration: number | null; format: string | null; ... 4 more ...; createdAt: Date; }' is not assignable to type '{ url: string; mediaType: string; format?: string | undefined; contentType?: string | undefined; }'.
Types of property 'mediaType' are incompatible.
Type 'string | null' is not assignable to type 'string'.
Type 'null' is not assignable to type 'string'.
src/lib/experiment-designer/__tests__/control-flow.test.ts(2,38): error TS2307: Cannot find module 'vitest' or its corresponding type declarations.
src/lib/experiment-designer/__tests__/control-flow.test.ts(64,16): error TS2532: Object is possibly 'undefined'.
src/lib/experiment-designer/__tests__/control-flow.test.ts(65,17): error TS2532: Object is possibly 'undefined'.
src/lib/experiment-designer/__tests__/control-flow.test.ts(70,16): error TS2532: Object is possibly 'undefined'.
src/lib/experiment-designer/__tests__/control-flow.test.ts(71,17): error TS2532: Object is possibly 'undefined'.
src/lib/experiment-designer/__tests__/control-flow.test.ts(72,17): error TS2532: Object is possibly 'undefined'.
src/lib/experiment-designer/__tests__/control-flow.test.ts(100,16): error TS2532: Object is possibly 'undefined'.
src/lib/experiment-designer/__tests__/control-flow.test.ts(101,17): error TS2532: Object is possibly 'undefined'.
src/lib/experiment-designer/__tests__/control-flow.test.ts(107,17): error TS2532: Object is possibly 'undefined'.
src/lib/experiment-designer/__tests__/control-flow.test.ts(108,17): error TS2532: Object is possibly 'undefined'.
src/lib/experiment-designer/__tests__/hashing.test.ts(2,38): error TS2307: Cannot find module 'vitest' or its corresponding type declarations.
src/lib/experiment-designer/__tests__/hashing.test.ts(65,19): error TS2741: Property 'category' is missing in type '{ id: string; type: string; name: string; parameters: { message: string; }; source: { kind: "core"; baseActionId: string; }; execution: { transport: "internal"; }; }' but required in type 'ExperimentAction'.
src/lib/experiment-designer/__tests__/hashing.test.ts(86,19): error TS2741: Property 'category' is missing in type '{ id: string; type: string; name: string; parameters: { message: string; }; source: { kind: "core"; baseActionId: string; }; execution: { transport: "internal"; }; }' but required in type 'ExperimentAction'.
src/lib/experiment-designer/__tests__/store.test.ts(2,50): error TS2307: Cannot find module 'vitest' or its corresponding type declarations.
src/lib/experiment-designer/__tests__/store.test.ts(39,16): error TS2532: Object is possibly 'undefined'.
src/lib/experiment-designer/__tests__/store.test.ts(58,16): error TS2532: Object is possibly 'undefined'.
src/lib/experiment-designer/__tests__/store.test.ts(103,16): error TS2532: Object is possibly 'undefined'.
src/lib/experiment-designer/__tests__/store.test.ts(104,16): error TS2532: Object is possibly 'undefined'.
src/lib/experiment-designer/__tests__/store.test.ts(107,16): error TS2532: Object is possibly 'undefined'.
src/lib/experiment-designer/__tests__/store.test.ts(108,16): error TS2532: Object is possibly 'undefined'.
src/lib/experiment-designer/__tests__/store.test.ts(123,15): error TS2741: Property 'category' is missing in type '{ id: string; type: string; name: string; parameters: {}; source: { kind: "core"; baseActionId: string; }; execution: { transport: "internal"; }; }' but required in type 'ExperimentAction'.
src/lib/experiment-designer/__tests__/store.test.ts(135,16): error TS18048: 'storedStep' is possibly 'undefined'.
src/lib/experiment-designer/__tests__/store.test.ts(136,16): error TS18048: 'storedStep' is possibly 'undefined'.
src/lib/experiment-designer/__tests__/store.test.ts(136,16): error TS2532: Object is possibly 'undefined'.
src/lib/experiment-designer/__tests__/validators.test.ts(2,38): error TS2307: Cannot find module 'vitest' or its corresponding type declarations.
src/lib/experiment-designer/__tests__/validators.test.ts(11,5): error TS2322: Type '"utility"' is not assignable to type 'ActionCategory'.
src/lib/experiment-designer/__tests__/validators.test.ts(14,91): error TS2353: Object literal may only specify known properties, and 'default' does not exist in type 'ActionParameter'.
src/lib/experiment-designer/__tests__/validators.test.ts(36,20): error TS2532: Object is possibly 'undefined'.
src/lib/experiment-designer/__tests__/validators.test.ts(58,17): error TS2353: Object literal may only specify known properties, and 'order' does not exist in type 'ExperimentAction'.
src/lib/experiment-designer/__tests__/validators.test.ts(78,17): error TS2353: Object literal may only specify known properties, and 'order' does not exist in type 'ExperimentAction'.
src/lib/experiment-designer/__tests__/validators.test.ts(107,17): error TS2353: Object literal may only specify known properties, and 'order' does not exist in type 'ExperimentAction'.
src/lib/experiment-designer/__tests__/validators.test.ts(119,20): error TS2532: Object is possibly 'undefined'.
src/server/services/__tests__/trial-execution.test.ts(2,56): error TS2307: Cannot find module 'bun:test' or its corresponding type declarations.

View File

@@ -0,0 +1,368 @@
# NAO6 HRIStudio Integration
**Complete integration package for NAO6 humanoid robots with the HRIStudio research platform**
## 🎯 Overview
This repository contains all components needed to integrate NAO6 robots with HRIStudio for Human-Robot Interaction research. It provides production-ready ROS2 packages, web interface plugins, control scripts, and comprehensive documentation for seamless robot operation through the HRIStudio platform.
## 📦 Repository Structure
```
nao6-hristudio-integration/
├── README.md # This file
├── launch/ # ROS2 launch configurations
│ ├── nao6_production.launch.py # Production-optimized launch
│ └── nao6_hristudio_enhanced.launch.py # Enhanced with monitoring
├── scripts/ # Utilities and automation
│ ├── test_nao_topics.py # ROS topics simulator
│ ├── test_websocket.py # WebSocket bridge tester
│ ├── verify_nao6_bridge.sh # Integration verification
│ └── seed-nao6-plugin.ts # Database seeding for HRIStudio
├── plugins/ # HRIStudio plugin definitions
│ ├── repository.json # Plugin repository metadata
│ ├── nao6-ros2-enhanced.json # Complete NAO6 plugin
│ └── README.md # Plugin documentation
├── examples/ # Usage examples and tools
│ ├── nao_control.py # Command-line robot control
│ └── start_nao6_hristudio.sh # Automated startup script
└── docs/ # Documentation
├── NAO6_INTEGRATION_COMPLETE.md # Complete integration guide
├── INSTALLATION.md # Installation instructions
├── USAGE.md # Usage examples
└── TROUBLESHOOTING.md # Common issues and solutions
```
## 🚀 Quick Start
### Prerequisites
- **NAO6 Robot** with NAOqi 2.8.7.4+
- **Ubuntu 22.04** with ROS2 Humble
- **HRIStudio Platform** (web interface)
- **Network connectivity** between computer and robot
### 1. Clone Repository
```bash
git clone <repository-url> ~/nao6-hristudio-integration
cd ~/nao6-hristudio-integration
```
### 2. Install Dependencies
```bash
# Install ROS2 packages
sudo apt update
sudo apt install ros-humble-rosbridge-suite ros-humble-naoqi-driver
# Install Python dependencies
pip install websocket-client
```
### 3. Setup NAOqi ROS2 Workspace
```bash
# Build the enhanced nao_launch package
cd ~/naoqi_ros2_ws
colcon build --packages-select nao_launch
source install/setup.bash
```
### 4. Start Integration
```bash
# Option A: Use automated startup script
./examples/start_nao6_hristudio.sh --nao-ip nao.local --password robolab
# Option B: Manual launch
ros2 launch nao_launch nao6_production.launch.py nao_ip:=nao.local password:=robolab
```
### 5. Configure HRIStudio
```bash
# Seed NAO6 plugin into HRIStudio database
cd ~/Documents/Projects/hristudio
bun run ../nao6-hristudio-integration/scripts/seed-nao6-plugin.ts
# Start HRIStudio web interface
bun dev
```
### 6. Test Integration
- Open: `http://localhost:3000/nao-test`
- Login: `sean@soconnor.dev` / `password123`
- Click "Connect" to establish WebSocket connection
- Try robot commands and verify responses
## 🎮 Available Robot Actions
The NAO6 plugin provides 9 comprehensive actions for HRIStudio experiments:
### 🗣️ Communication
- **Speak Text** - Text-to-speech with volume/speed control
- **LED Control** - Visual feedback with colors and patterns
### 🚶 Movement & Posture
- **Move Robot** - Walking, turning with safety limits
- **Set Posture** - Stand, sit, crouch positions
- **Move Head** - Gaze control and attention direction
- **Perform Gesture** - Wave, point, applause, custom animations
### 📡 Sensors & Monitoring
- **Monitor Sensors** - Touch, bumper, sonar detection
- **Check Robot Status** - Battery, joints, system health
### 🛡️ Safety & System
- **Emergency Stop** - Immediate motion termination
- **Wake Up / Rest** - Power management
## 🔧 Command-Line Tools
### Robot Control
```bash
# Direct robot control
python3 examples/nao_control.py --ip nao.local wake
python3 examples/nao_control.py --ip nao.local speak "Hello world"
python3 examples/nao_control.py --ip nao.local move 0.1 0 0
python3 examples/nao_control.py --ip nao.local pose Stand
python3 examples/nao_control.py --ip nao.local emergency
```
### Integration Testing
```bash
# Verify all components
./scripts/verify_nao6_bridge.sh
# Test WebSocket connectivity
python3 scripts/test_websocket.py
# Simulate robot topics (without hardware)
python3 scripts/test_nao_topics.py
```
## 🌐 WebSocket Communication
### Connection Details
- **URL**: `ws://localhost:9090`
- **Protocol**: rosbridge v2.0
- **Format**: JSON messages
### Sample Messages
```javascript
// Speech command
{
"op": "publish",
"topic": "/speech",
"type": "std_msgs/String",
"msg": {"data": "Hello from HRIStudio!"}
}
// Movement command
{
"op": "publish",
"topic": "/cmd_vel",
"type": "geometry_msgs/Twist",
"msg": {
"linear": {"x": 0.1, "y": 0.0, "z": 0.0},
"angular": {"x": 0.0, "y": 0.0, "z": 0.0}
}
}
// Subscribe to sensors
{
"op": "subscribe",
"topic": "/naoqi_driver/joint_states",
"type": "sensor_msgs/JointState"
}
```
## 📋 Key Topics
### Input Topics (Robot Control)
- `/speech` - Text-to-speech commands
- `/cmd_vel` - Movement control
- `/joint_angles` - Joint positioning
- `/led_control` - Visual feedback
### Output Topics (Sensor Data)
- `/naoqi_driver/joint_states` - Joint positions/velocities
- `/naoqi_driver/bumper` - Foot sensors
- `/naoqi_driver/hand_touch` - Hand touch sensors
- `/naoqi_driver/head_touch` - Head touch sensors
- `/naoqi_driver/sonar/left` - Left ultrasonic sensor
- `/naoqi_driver/sonar/right` - Right ultrasonic sensor
- `/naoqi_driver/battery` - Battery level
## 🛡️ Safety Features
### Automated Safety
- **Velocity Limits** - Maximum speed constraints (0.2 m/s linear, 0.8 rad/s angular)
- **Emergency Stop** - Immediate motion termination
- **Battery Monitoring** - Low battery warnings
- **Fall Detection** - Automatic safety responses
- **Wake-up Management** - Proper robot state handling
### Manual Safety Controls
```bash
# Emergency stop via CLI
ros2 topic pub --once /cmd_vel geometry_msgs/msg/Twist '{linear: {x: 0.0, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 0.0}}'
# Emergency stop via script
python3 examples/nao_control.py --ip nao.local emergency
# Or use HRIStudio emergency stop action
```
## 🔍 Troubleshooting
### Common Issues
**Robot not responding to commands**
```bash
# Check robot is awake
python3 examples/nao_control.py --ip nao.local status
# Wake up robot
python3 examples/nao_control.py --ip nao.local wake
# OR press chest button for 3 seconds
```
**WebSocket connection failed**
```bash
# Check rosbridge is running
ros2 node list | grep rosbridge
# Restart integration
pkill -f rosbridge && pkill -f rosapi
ros2 launch nao_launch nao6_production.launch.py nao_ip:=nao.local
```
**Network connectivity issues**
```bash
# Test basic connectivity
ping nao.local
telnet nao.local 9559
# Check robot credentials
ssh nao@nao.local # Password: robolab (institution-specific)
```
For detailed troubleshooting, see [docs/TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md)
## 📖 Documentation
### Complete Guides
- **[Installation Guide](docs/INSTALLATION.md)** - Detailed setup instructions
- **[Usage Guide](docs/USAGE.md)** - Examples and best practices
- **[Integration Complete](docs/NAO6_INTEGRATION_COMPLETE.md)** - Comprehensive overview
- **[Troubleshooting](docs/TROUBLESHOOTING.md)** - Problem resolution
### Quick References
- **Launch Files** - See `launch/` directory
- **Plugin Definitions** - See `plugins/` directory
- **Example Scripts** - See `examples/` directory
## 🎯 Research Applications
### Experiment Types
- **Social Interaction** - Gestures, speech, gaze studies
- **Human-Robot Collaboration** - Shared task experiments
- **Behavior Analysis** - Touch, proximity, response studies
- **Navigation Studies** - Movement and spatial interaction
- **Multimodal Interaction** - Combined speech, gesture, movement
### Data Capture
- **Synchronized Timestamps** - All robot actions and sensor events
- **Sensor Fusion** - Touch, vision, audio, movement data
- **Real-time Logging** - Comprehensive event capture
- **Export Capabilities** - Data analysis and visualization
## 🏆 Features & Benefits
### ✅ Production Ready
- **Tested Integration** - Verified with NAO V6.0 / NAOqi 2.8.7.4
- **Safety First** - Comprehensive safety monitoring
- **Performance Optimized** - Tuned for stable experiments
- **Error Handling** - Robust failure management
### ✅ Researcher Friendly
- **Web Interface** - No programming required for experiments
- **Visual Designer** - Drag-and-drop experiment creation
- **Real-time Control** - Live robot operation during trials
- **Multiple Roles** - Researcher, wizard, observer access
### ✅ Developer Friendly
- **Open Source** - MIT licensed components
- **Modular Design** - Extensible architecture
- **Comprehensive APIs** - ROS2 and WebSocket interfaces
- **Documentation** - Complete setup and usage guides
## 🚀 Getting Started Examples
### Basic Experiment Workflow
1. **Design** - Create experiment in HRIStudio visual designer
2. **Configure** - Set robot parameters and safety limits
3. **Execute** - Run trial with real-time robot control
4. **Analyze** - Review captured data and events
5. **Iterate** - Refine experiment based on results
### Sample Experiment: Greeting Interaction
```javascript
// HRIStudio experiment sequence
[
{"action": "nao_wake_rest", "parameters": {"action": "wake"}},
{"action": "nao_pose", "parameters": {"posture": "Stand"}},
{"action": "nao_speak", "parameters": {"text": "Hello! Welcome to our study."}},
{"action": "nao_gesture", "parameters": {"gesture": "wave"}},
{"action": "nao_sensor_monitor", "parameters": {"sensorType": "touch", "duration": 30}}
]
```
## 🤝 Contributing
### Development Setup
1. Fork this repository
2. Create feature branch: `git checkout -b feature-name`
3. Test with real NAO6 hardware
4. Submit pull request with documentation updates
### Guidelines
- Follow ROS2 conventions for launch files
- Test all changes with physical robot
- Update documentation for new features
- Ensure backward compatibility
## 📞 Support
### Resources
- **GitHub Issues** - Report bugs and request features
- **Documentation** - Complete guides in `docs/` folder
- **HRIStudio Platform** - Web interface documentation
### Requirements
- **NAO6 Robot** - NAO V6.0 with NAOqi 2.8.7.4+
- **ROS2 Humble** - Ubuntu 22.04 recommended
- **Network Setup** - Robot and computer on same network
- **HRIStudio** - Web platform for experiment design
## 📄 License
MIT License - See LICENSE file for details
## 🏅 Citation
If you use this integration in your research, please cite:
```bibtex
@software{nao6_hristudio_integration,
title={NAO6 HRIStudio Integration},
author={HRIStudio RoboLab Team},
year={2024},
url={https://github.com/hristudio/nao6-integration},
version={2.0.0}
}
```
---
**Status**: Production Ready ✅
**Tested With**: NAO V6.0 / NAOqi 2.8.7.4 / ROS2 Humble / HRIStudio v1.0
**Last Updated**: December 2024
*Advancing Human-Robot Interaction research through standardized, accessible, and reliable tools.*

927
_archive/plugin_dump.json Normal file
View File

@@ -0,0 +1,927 @@
jsonb_pretty
---------------------------------------------------------------------------------------
[ +
{ +
"id": "walk_velocity", +
"icon": "navigation", +
"name": "Walk with Velocity", +
"ros2": { +
"qos": { +
"depth": 1, +
"history": "keep_last", +
"durability": "volatile", +
"reliability": "reliable" +
}, +
"topic": "/cmd_vel", +
"messageType": "geometry_msgs/msg/Twist", +
"payloadMapping": { +
"type": "transform", +
"transformFn": "transformToTwist" +
} +
}, +
"timeout": 5000, +
"category": "movement", +
"retryable": true, +
"description": "Control robot walking with linear and angular velocities", +
"parameterSchema": { +
"type": "object", +
"required": [ +
"linear", +
"angular" +
], +
"properties": { +
"linear": { +
"type": "number", +
"default": 0, +
"maximum": 0.55, +
"minimum": -0.55, +
"description": "Forward velocity in m/s" +
}, +
"angular": { +
"type": "number", +
"default": 0, +
"maximum": 2, +
"minimum": -2, +
"description": "Angular velocity in rad/s" +
} +
} +
} +
}, +
{ +
"id": "walk_forward", +
"icon": "arrow-up", +
"name": "Walk Forward", +
"ros2": { +
"topic": "/cmd_vel", +
"messageType": "geometry_msgs/msg/Twist", +
"payloadMapping": { +
"type": "static", +
"payload": { +
"linear": { +
"x": "{{speed}}", +
"y": 0, +
"z": 0 +
}, +
"angular": { +
"x": 0, +
"y": 0, +
"z": 0 +
} +
} +
} +
}, +
"timeout": 30000, +
"category": "movement", +
"retryable": true, +
"description": "Make the robot walk forward at specified speed", +
"parameterSchema": { +
"type": "object", +
"required": [ +
"speed" +
], +
"properties": { +
"speed": { +
"type": "number", +
"default": 0.1, +
"maximum": 0.3, +
"minimum": 0.01, +
"description": "Walking speed in m/s" +
}, +
"duration": { +
"type": "number", +
"default": 0, +
"maximum": 30, +
"minimum": 0, +
"description": "Duration to walk in seconds (0 = indefinite)" +
} +
} +
} +
}, +
{ +
"id": "walk_backward", +
"icon": "arrow-down", +
"name": "Walk Backward", +
"ros2": { +
"topic": "/cmd_vel", +
"messageType": "geometry_msgs/msg/Twist", +
"payloadMapping": { +
"type": "static", +
"payload": { +
"linear": { +
"x": "-{{speed}}", +
"y": 0, +
"z": 0 +
}, +
"angular": { +
"x": 0, +
"y": 0, +
"z": 0 +
} +
} +
} +
}, +
"timeout": 30000, +
"category": "movement", +
"retryable": true, +
"description": "Make the robot walk backward at specified speed", +
"parameterSchema": { +
"type": "object", +
"required": [ +
"speed" +
], +
"properties": { +
"speed": { +
"type": "number", +
"default": 0.1, +
"maximum": 0.3, +
"minimum": 0.01, +
"description": "Walking speed in m/s" +
}, +
"duration": { +
"type": "number", +
"default": 0, +
"maximum": 30, +
"minimum": 0, +
"description": "Duration to walk in seconds (0 = indefinite)" +
} +
} +
} +
}, +
{ +
"id": "turn_left", +
"icon": "rotate-ccw", +
"name": "Turn Left", +
"ros2": { +
"topic": "/cmd_vel", +
"messageType": "geometry_msgs/msg/Twist", +
"payloadMapping": { +
"type": "static", +
"payload": { +
"linear": { +
"x": 0, +
"y": 0, +
"z": 0 +
}, +
"angular": { +
"x": 0, +
"y": 0, +
"z": "{{speed}}" +
} +
} +
} +
}, +
"timeout": 30000, +
"category": "movement", +
"retryable": true, +
"description": "Make the robot turn left at specified angular speed", +
"parameterSchema": { +
"type": "object", +
"required": [ +
"speed" +
], +
"properties": { +
"speed": { +
"type": "number", +
"default": 0.3, +
"maximum": 1, +
"minimum": 0.1, +
"description": "Angular speed in rad/s" +
}, +
"duration": { +
"type": "number", +
"default": 0, +
"maximum": 30, +
"minimum": 0, +
"description": "Duration to turn in seconds (0 = indefinite)" +
} +
} +
} +
}, +
{ +
"id": "turn_right", +
"icon": "rotate-cw", +
"name": "Turn Right", +
"ros2": { +
"topic": "/cmd_vel", +
"messageType": "geometry_msgs/msg/Twist", +
"payloadMapping": { +
"type": "static", +
"payload": { +
"linear": { +
"x": 0, +
"y": 0, +
"z": 0 +
}, +
"angular": { +
"x": 0, +
"y": 0, +
"z": "-{{speed}}" +
} +
} +
} +
}, +
"timeout": 30000, +
"category": "movement", +
"retryable": true, +
"description": "Make the robot turn right at specified angular speed", +
"parameterSchema": { +
"type": "object", +
"required": [ +
"speed" +
], +
"properties": { +
"speed": { +
"type": "number", +
"default": 0.3, +
"maximum": 1, +
"minimum": 0.1, +
"description": "Angular speed in rad/s" +
}, +
"duration": { +
"type": "number", +
"default": 0, +
"maximum": 30, +
"minimum": 0, +
"description": "Duration to turn in seconds (0 = indefinite)" +
} +
} +
} +
}, +
{ +
"id": "stop_walking", +
"icon": "square", +
"name": "Stop Walking", +
"ros2": { +
"qos": { +
"depth": 1, +
"history": "keep_last", +
"durability": "volatile", +
"reliability": "reliable" +
}, +
"topic": "/cmd_vel", +
"messageType": "geometry_msgs/msg/Twist", +
"payloadMapping": { +
"type": "static", +
"payload": { +
"linear": { +
"x": 0, +
"y": 0, +
"z": 0 +
}, +
"angular": { +
"x": 0, +
"y": 0, +
"z": 0 +
} +
} +
} +
}, +
"timeout": 3000, +
"category": "movement", +
"retryable": false, +
"description": "Immediately stop robot movement", +
"parameterSchema": { +
"type": "object", +
"required": [ +
], +
"properties": { +
} +
} +
}, +
{ +
"id": "say_text", +
"icon": "volume-2", +
"name": "Say Text", +
"ros2": { +
"qos": { +
"durability": "volatile", +
"reliability": "reliable" +
}, +
"topic": "/speech", +
"messageType": "std_msgs/msg/String", +
"payloadMapping": { +
"type": "transform", +
"transformFn": "transformToStringMessage" +
} +
}, +
"timeout": 15000, +
"category": "interaction", +
"retryable": true, +
"description": "Make the robot speak using text-to-speech", +
"parameterSchema": { +
"type": "object", +
"required": [ +
"text" +
], +
"properties": { +
"text": { +
"type": "string", +
"default": "Hello from NAO!", +
"description": "Text to speak" +
} +
} +
} +
}, +
{ +
"id": "say_with_emotion", +
"icon": "heart", +
"name": "Say Text with Emotion", +
"ros2": { +
"topic": "/speech", +
"messageType": "std_msgs/msg/String", +
"payloadMapping": { +
"type": "static", +
"payload": { +
"data": "\\rspd={{speed}}\\\\rst={{emotion}}\\{{text}}" +
} +
} +
}, +
"timeout": 15000, +
"category": "interaction", +
"retryable": true, +
"description": "Speak text with emotional expression using SSML-like markup",+
"parameterSchema": { +
"type": "object", +
"required": [ +
"text" +
], +
"properties": { +
"text": { +
"type": "string", +
"default": "Hello! I'm feeling great today!", +
"description": "Text for the robot to speak" +
}, +
"speed": { +
"type": "number", +
"default": 1, +
"maximum": 2, +
"minimum": 0.5, +
"description": "Speech speed multiplier" +
}, +
"emotion": { +
"enum": [ +
"neutral", +
"happy", +
"sad", +
"excited", +
"calm" +
], +
"type": "string", +
"default": "neutral", +
"description": "Emotional tone for speech" +
} +
} +
} +
}, +
{ +
"id": "set_volume", +
"icon": "volume-x", +
"name": "Set Volume", +
"ros2": { +
"topic": "/audio_volume", +
"messageType": "std_msgs/msg/Float32", +
"payloadMapping": { +
"type": "static", +
"payload": { +
"data": "{{volume}}" +
} +
} +
}, +
"timeout": 5000, +
"category": "interaction", +
"retryable": true, +
"description": "Adjust the robot's audio volume level", +
"parameterSchema": { +
"type": "object", +
"required": [ +
"volume" +
], +
"properties": { +
"volume": { +
"type": "number", +
"default": 0.5, +
"maximum": 1, +
"minimum": 0, +
"description": "Volume level (0.0 = silent, 1.0 = maximum)" +
} +
} +
} +
}, +
{ +
"id": "set_language", +
"icon": "globe", +
"name": "Set Language", +
"ros2": { +
"topic": "/set_language", +
"messageType": "std_msgs/msg/String", +
"payloadMapping": { +
"type": "static", +
"payload": { +
"data": "{{language}}" +
} +
} +
}, +
"timeout": 5000, +
"category": "interaction", +
"retryable": true, +
"description": "Change the robot's speech language", +
"parameterSchema": { +
"type": "object", +
"required": [ +
"language" +
], +
"properties": { +
"language": { +
"enum": [ +
"en-US", +
"en-GB", +
"fr-FR", +
"de-DE", +
"es-ES", +
"it-IT", +
"ja-JP", +
"ko-KR", +
"zh-CN" +
], +
"type": "string", +
"default": "en-US", +
"description": "Speech language" +
} +
} +
} +
}, +
{ +
"id": "move_head", +
"icon": "eye", +
"name": "Move Head", +
"ros2": { +
"topic": "/joint_angles", +
"messageType": "naoqi_bridge_msgs/msg/JointAnglesWithSpeed", +
"payloadMapping": { +
"type": "static", +
"payload": { +
"speed": "{{speed}}", +
"joint_names": [ +
"HeadYaw", +
"HeadPitch" +
], +
"joint_angles": [ +
"{{yaw}}", +
"{{pitch}}" +
] +
} +
} +
}, +
"timeout": 10000, +
"category": "movement", +
"retryable": true, +
"description": "Control head orientation (yaw and pitch)", +
"parameterSchema": { +
"type": "object", +
"required": [ +
"yaw", +
"pitch" +
], +
"properties": { +
"yaw": { +
"type": "number", +
"default": 0, +
"maximum": 2.09, +
"minimum": -2.09, +
"description": "Head yaw angle in radians" +
}, +
"pitch": { +
"type": "number", +
"default": 0, +
"maximum": 0.51, +
"minimum": -0.67, +
"description": "Head pitch angle in radians" +
}, +
"speed": { +
"type": "number", +
"default": 0.3, +
"maximum": 1, +
"minimum": 0.1, +
"description": "Movement speed (0.1 = slow, 1.0 = fast)" +
} +
} +
} +
}, +
{ +
"id": "move_arm", +
"icon": "hand", +
"name": "Move Arm", +
"ros2": { +
"topic": "/joint_angles", +
"messageType": "naoqi_bridge_msgs/msg/JointAnglesWithSpeed", +
"payloadMapping": { +
"type": "static", +
"payload": { +
"speed": "{{speed}}", +
"joint_names": [ +
"{{arm === 'left' ? 'L' : 'R'}}ShoulderPitch", +
"{{arm === 'left' ? 'L' : 'R'}}ShoulderRoll", +
"{{arm === 'left' ? 'L' : 'R'}}ElbowYaw", +
"{{arm === 'left' ? 'L' : 'R'}}ElbowRoll" +
], +
"joint_angles": [ +
"{{shoulder_pitch}}", +
"{{shoulder_roll}}", +
"{{elbow_yaw}}", +
"{{elbow_roll}}" +
] +
} +
} +
}, +
"timeout": 10000, +
"category": "movement", +
"retryable": true, +
"description": "Control arm joint positions", +
"parameterSchema": { +
"type": "object", +
"required": [ +
"arm", +
"shoulder_pitch", +
"shoulder_roll", +
"elbow_yaw", +
"elbow_roll" +
], +
"properties": { +
"arm": { +
"enum": [ +
"left", +
"right" +
], +
"type": "string", +
"default": "right", +
"description": "Which arm to control" +
}, +
"speed": { +
"type": "number", +
"default": 0.3, +
"maximum": 1, +
"minimum": 0.1, +
"description": "Movement speed (0.1 = slow, 1.0 = fast)" +
}, +
"elbow_yaw": { +
"type": "number", +
"default": 0, +
"maximum": 2.09, +
"minimum": -2.09, +
"description": "Elbow yaw angle in radians" +
}, +
"elbow_roll": { +
"type": "number", +
"default": -0.5, +
"maximum": -0.03, +
"minimum": -1.54, +
"description": "Elbow roll angle in radians" +
}, +
"shoulder_roll": { +
"type": "number", +
"default": 0.2, +
"maximum": 1.33, +
"minimum": -0.31, +
"description": "Shoulder roll angle in radians" +
}, +
"shoulder_pitch": { +
"type": "number", +
"default": 1.4, +
"maximum": 2.09, +
"minimum": -2.09, +
"description": "Shoulder pitch angle in radians" +
} +
} +
} +
}, +
{ +
"id": "set_joint_angle", +
"icon": "settings", +
"name": "Set Joint Angle", +
"ros2": { +
"topic": "/joint_angles", +
"messageType": "naoqi_bridge_msgs/msg/JointAnglesWithSpeed", +
"payloadMapping": { +
"type": "transform", +
"transformFn": "transformToJointAngles" +
} +
}, +
"timeout": 10000, +
"category": "movement", +
"retryable": true, +
"description": "Control individual joint angles", +
"parameterSchema": { +
"type": "object", +
"required": [ +
"joint_name", +
"angle" +
], +
"properties": { +
"angle": { +
"type": "number", +
"default": 0, +
"maximum": 3.14159, +
"minimum": -3.14159, +
"description": "Target angle in radians" +
}, +
"speed": { +
"type": "number", +
"default": 0.2, +
"maximum": 1, +
"minimum": 0.01, +
"description": "Movement speed (fraction of max)" +
}, +
"joint_name": { +
"enum": [ +
"HeadYaw", +
"HeadPitch", +
"LShoulderPitch", +
"LShoulderRoll", +
"LElbowYaw", +
"LElbowRoll", +
"LWristYaw", +
"RShoulderPitch", +
"RShoulderRoll", +
"RElbowYaw", +
"RElbowRoll", +
"RWristYaw", +
"LHipYawPitch", +
"LHipRoll", +
"LHipPitch", +
"LKneePitch", +
"LAnklePitch", +
"LAnkleRoll", +
"RHipRoll", +
"RHipPitch", +
"RKneePitch", +
"RAnklePitch", +
"RAnkleRoll" +
], +
"type": "string", +
"default": "HeadYaw", +
"description": "Joint to control" +
} +
} +
} +
}, +
{ +
"id": "turn_head", +
"icon": "rotate-ccw", +
"name": "Turn Head", +
"ros2": { +
"topic": "/joint_angles", +
"messageType": "naoqi_bridge_msgs/msg/JointAnglesWithSpeed", +
"payloadMapping": { +
"type": "transform", +
"transformFn": "transformToHeadMovement" +
} +
}, +
"timeout": 8000, +
"category": "movement", +
"retryable": true, +
"description": "Control head orientation", +
"parameterSchema": { +
"type": "object", +
"required": [ +
"yaw", +
"pitch" +
], +
"properties": { +
"yaw": { +
"type": "number", +
"default": 0, +
"maximum": 2.0857, +
"minimum": -2.0857, +
"description": "Head yaw angle in radians (left-right)" +
}, +
"pitch": { +
"type": "number", +
"default": 0, +
"maximum": 0.5149, +
"minimum": -0.672, +
"description": "Head pitch angle in radians (up-down)" +
}, +
"speed": { +
"type": "number", +
"default": 0.3, +
"maximum": 1, +
"minimum": 0.1, +
"description": "Movement speed fraction" +
} +
} +
} +
}, +
{ +
"id": "get_camera_image", +
"icon": "camera", +
"name": "Get Camera Image", +
"ros2": { +
"qos": { +
"durability": "volatile", +
"reliability": "reliable" +
}, +
"topic": "/camera/{camera}/image_raw", +
"messageType": "sensor_msgs/msg/Image", +
"payloadMapping": { +
"type": "transform", +
"transformFn": "getCameraImage" +
} +
}, +
"timeout": 5000, +
"category": "sensors", +
"retryable": true, +
"description": "Capture image from front or bottom camera", +
"parameterSchema": { +
"type": "object", +
"required": [ +
"camera" +
], +
"properties": { +
"camera": { +
"enum": [ +
"front", +
"bottom" +
], +
"type": "string", +
"default": "front", +
"description": "Camera to use" +
} +
} +
} +
}, +
{ +
"id": "get_joint_states", +
"icon": "activity", +
"name": "Get Joint States", +
"ros2": { +
"qos": { +
"durability": "volatile", +
"reliability": "reliable" +
}, +
"topic": "/joint_states", +
"messageType": "sensor_msgs/msg/JointState", +
"payloadMapping": { +
"type": "transform", +
"transformFn": "getJointStates" +
} +
}, +
"timeout": 3000, +
"category": "sensors", +
"retryable": true, +
"description": "Read current joint positions and velocities", +
"parameterSchema": { +
"type": "object", +
"required": [ +
], +
"properties": { +
} +
} +
}, +
{ +
"id": "get_imu_data", +
"icon": "compass", +
"name": "Get IMU Data", +
"ros2": { +
"qos": { +
"durability": "volatile", +
"reliability": "reliable" +
}, +
"topic": "/imu/torso", +
"messageType": "sensor_msgs/msg/Imu", +
"payloadMapping": { +
"type": "transform", +
"transformFn": "getImuData" +
} +
}, +
"timeout": 3000, +
"category": "sensors", +
"retryable": true, +
"description": "Read inertial measurement unit data from torso", +
"parameterSchema": { +
"type": "object", +
"required": [ +
], +
"properties": { +
} +
} +
}, +
{ +
"id": "get_bumper_status", +
"icon": "zap", +
"name": "Get Bumper Status", +
"ros2": { +
"topic": "/bumper", +
"messageType": "naoqi_bridge_msgs/msg/Bumper", +
"payloadMapping": { +
"type": "transform", +
"transformFn": "getBumperStatus" +
} +
}, +
"timeout": 3000, +
"category": "sensors", +
"retryable": true, +
"description": "Read foot bumper contact sensors", +
"parameterSchema": { +
"type": "object", +
"required": [ +
], +
"properties": { +
} +
} +
}, +
{ +
"id": "get_touch_sensors", +
"icon": "hand", +
"name": "Get Touch Sensors", +
"ros2": { +
"topic": "/{sensor_type}_touch", +
"messageType": "naoqi_bridge_msgs/msg/HandTouch", +
"payloadMapping": { +
"type": "transform", +
"transformFn": "getTouchSensors" +
} +
}, +
"timeout": 3000, +
"category": "sensors", +
"retryable": true, +
"description": "Read hand and head touch sensor states", +
"parameterSchema": { +
"type": "object", +
"required": [ +
"sensor_type" +
], +
"properties": { +
"sensor_type": { +
"enum": [ +
"hand", +
"head" +
], +
"type": "string", +
"default": "hand", +
"description": "Touch sensor type to read" +
} +
} +
} +
}, +
{ +
"id": "get_sonar_range", +
"icon": "radio", +
"name": "Get Sonar Range", +
"ros2": { +
"topic": "/sonar/{sensor}", +
"messageType": "sensor_msgs/msg/Range", +
"payloadMapping": { +
"type": "transform", +
"transformFn": "getSonarRange" +
} +
}, +
"timeout": 3000, +
"category": "sensors", +
"retryable": true, +
"description": "Read ultrasonic range sensor data", +
"parameterSchema": { +
"type": "object", +
"required": [ +
"sensor" +
], +
"properties": { +
"sensor": { +
"enum": [ +
"left", +
"right", +
"both" +
], +
"type": "string", +
"default": "both", +
"description": "Sonar sensor to read" +
} +
} +
} +
}, +
{ +
"id": "get_robot_info", +
"icon": "info", +
"name": "Get Robot Info", +
"ros2": { +
"topic": "/info", +
"messageType": "naoqi_bridge_msgs/msg/RobotInfo", +
"payloadMapping": { +
"type": "transform", +
"transformFn": "getRobotInfo" +
} +
}, +
"timeout": 3000, +
"category": "sensors", +
"retryable": true, +
"description": "Read general robot information and status", +
"parameterSchema": { +
"type": "object", +
"required": [ +
], +
"properties": { +
} +
} +
} +
]
(1 row)

86
_archive/simple-ws-test.html Executable file
View File

@@ -0,0 +1,86 @@
<!DOCTYPE html>
<html>
<head>
<title>Simple WebSocket Test</title>
<style>
body { font-family: Arial; padding: 20px; }
.status { padding: 10px; margin: 10px 0; border-radius: 5px; }
.connected { background: #d4edda; color: #155724; }
.disconnected { background: #f8d7da; color: #721c24; }
.connecting { background: #d1ecf1; color: #0c5460; }
.log { background: #f8f9fa; padding: 10px; height: 300px; overflow-y: auto; border: 1px solid #ddd; font-family: monospace; white-space: pre-wrap; }
button { padding: 8px 16px; margin: 5px; }
</style>
</head>
<body>
<h1>WebSocket Test</h1>
<div id="status" class="status disconnected">Disconnected</div>
<button onclick="connect()">Connect</button>
<button onclick="disconnect()">Disconnect</button>
<button onclick="sendTest()">Send Test</button>
<div id="log" class="log"></div>
<script>
let ws = null;
const log = document.getElementById('log');
const status = document.getElementById('status');
function updateStatus(text, className) {
status.textContent = text;
status.className = 'status ' + className;
}
function addLog(msg) {
log.textContent += new Date().toLocaleTimeString() + ': ' + msg + '\n';
log.scrollTop = log.scrollHeight;
}
function connect() {
const trialId = '931c626d-fe3f-4db3-a36c-50d6898e1b17';
const token = btoa(JSON.stringify({userId: '08594f2b-64fe-4952-947f-3edc5f144f52', timestamp: Math.floor(Date.now()/1000)}));
const url = `ws://localhost:3000/api/websocket?trialId=${trialId}&token=${token}`;
addLog('Connecting to: ' + url);
updateStatus('Connecting...', 'connecting');
ws = new WebSocket(url);
ws.onopen = function() {
addLog('✅ Connected!');
updateStatus('Connected', 'connected');
};
ws.onmessage = function(event) {
addLog('📨 Received: ' + event.data);
};
ws.onclose = function(event) {
addLog('🔌 Closed: ' + event.code + ' ' + event.reason);
updateStatus('Disconnected', 'disconnected');
};
ws.onerror = function(error) {
addLog('❌ Error: ' + error);
updateStatus('Error', 'disconnected');
};
}
function disconnect() {
if (ws) {
ws.close();
ws = null;
}
}
function sendTest() {
if (ws && ws.readyState === WebSocket.OPEN) {
const msg = JSON.stringify({type: 'heartbeat', data: {}});
ws.send(msg);
addLog('📤 Sent: ' + msg);
} else {
addLog('❌ Not connected');
}
}
</script>
</body>
</html>

297
_archive/test-websocket.html Executable file
View File

@@ -0,0 +1,297 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>HRIStudio WebSocket Test</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: #f5f5f5;
}
.container {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.status {
padding: 10px;
border-radius: 4px;
margin: 10px 0;
font-weight: bold;
}
.connected { background-color: #d4edda; color: #155724; }
.connecting { background-color: #d1ecf1; color: #0c5460; }
.disconnected { background-color: #f8d7da; color: #721c24; }
.error { background-color: #f5c6cb; color: #721c24; }
.log {
background-color: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 4px;
padding: 10px;
height: 300px;
overflow-y: auto;
font-family: monospace;
font-size: 12px;
white-space: pre-wrap;
}
button {
background-color: #007bff;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
margin: 5px;
}
button:hover { background-color: #0056b3; }
button:disabled { background-color: #6c757d; cursor: not-allowed; }
input, select {
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
margin: 5px;
}
.input-group {
margin: 10px 0;
display: flex;
align-items: center;
gap: 10px;
}
.input-group label {
min-width: 100px;
}
</style>
</head>
<body>
<div class="container">
<h1>🔌 HRIStudio WebSocket Test</h1>
<div class="input-group">
<label>Trial ID:</label>
<input type="text" id="trialId" value="931c626d-fe3f-4db3-a36c-50d6898e1b17" style="width: 300px;">
</div>
<div class="input-group">
<label>User ID:</label>
<input type="text" id="userId" value="08594f2b-64fe-4952-947f-3edc5f144f52" style="width: 300px;">
</div>
<div class="input-group">
<label>Server:</label>
<input type="text" id="serverUrl" value="ws://localhost:3000" style="width: 200px;">
</div>
<div id="status" class="status disconnected">Disconnected</div>
<div>
<button id="connectBtn" onclick="connect()">Connect</button>
<button id="disconnectBtn" onclick="disconnect()" disabled>Disconnect</button>
<button onclick="sendHeartbeat()" disabled id="heartbeatBtn">Send Heartbeat</button>
<button onclick="requestStatus()" disabled id="statusBtn">Request Status</button>
<button onclick="sendTestAction()" disabled id="actionBtn">Send Test Action</button>
<button onclick="clearLog()">Clear Log</button>
</div>
<h3>📨 Message Log</h3>
<div id="log" class="log"></div>
<h3>🎮 Send Custom Message</h3>
<div class="input-group">
<label>Type:</label>
<select id="messageType">
<option value="heartbeat">heartbeat</option>
<option value="request_trial_status">request_trial_status</option>
<option value="trial_action">trial_action</option>
<option value="wizard_intervention">wizard_intervention</option>
<option value="step_transition">step_transition</option>
</select>
<button onclick="sendCustomMessage()" disabled id="customBtn">Send</button>
</div>
<textarea id="messageData" placeholder='{"key": "value"}' rows="3" style="width: 100%; margin: 5px 0;"></textarea>
</div>
<script>
let ws = null;
let connectionAttempts = 0;
const maxRetries = 3;
const statusEl = document.getElementById('status');
const logEl = document.getElementById('log');
const connectBtn = document.getElementById('connectBtn');
const disconnectBtn = document.getElementById('disconnectBtn');
const heartbeatBtn = document.getElementById('heartbeatBtn');
const statusBtn = document.getElementById('statusBtn');
const actionBtn = document.getElementById('actionBtn');
const customBtn = document.getElementById('customBtn');
function log(message, type = 'info') {
const timestamp = new Date().toLocaleTimeString();
const prefix = type === 'sent' ? '📤' : type === 'received' ? '📨' : type === 'error' ? '❌' : '';
logEl.textContent += `[${timestamp}] ${prefix} ${message}\n`;
logEl.scrollTop = logEl.scrollHeight;
}
function updateStatus(status, className) {
statusEl.textContent = status;
statusEl.className = `status ${className}`;
}
function updateButtons(connected) {
connectBtn.disabled = connected;
disconnectBtn.disabled = !connected;
heartbeatBtn.disabled = !connected;
statusBtn.disabled = !connected;
actionBtn.disabled = !connected;
customBtn.disabled = !connected;
}
function generateToken() {
const userId = document.getElementById('userId').value;
const tokenData = {
userId: userId,
timestamp: Math.floor(Date.now() / 1000)
};
return btoa(JSON.stringify(tokenData));
}
function connect() {
if (ws && (ws.readyState === WebSocket.CONNECTING || ws.readyState === WebSocket.OPEN)) {
log('Already connected or connecting', 'error');
return;
}
const trialId = document.getElementById('trialId').value;
const serverUrl = document.getElementById('serverUrl').value;
const token = generateToken();
if (!trialId) {
log('Please enter a trial ID', 'error');
return;
}
const wsUrl = `${serverUrl}/api/websocket?trialId=${trialId}&token=${token}`;
log(`Connecting to: ${wsUrl}`);
updateStatus('Connecting...', 'connecting');
try {
ws = new WebSocket(wsUrl);
ws.onopen = function() {
connectionAttempts = 0;
updateStatus('Connected', 'connected');
updateButtons(true);
log('WebSocket connection established!');
};
ws.onmessage = function(event) {
try {
const message = JSON.parse(event.data);
log(`${message.type}: ${JSON.stringify(message.data, null, 2)}`, 'received');
} catch (e) {
log(`Raw message: ${event.data}`, 'received');
}
};
ws.onclose = function(event) {
updateStatus(`Disconnected (${event.code})`, 'disconnected');
updateButtons(false);
log(`Connection closed: ${event.code} ${event.reason}`);
// Auto-reconnect logic
if (event.code !== 1000 && connectionAttempts < maxRetries) {
connectionAttempts++;
log(`Attempting reconnection ${connectionAttempts}/${maxRetries}...`);
setTimeout(() => connect(), 2000 * connectionAttempts);
}
};
ws.onerror = function(event) {
updateStatus('Error', 'error');
updateButtons(false);
log('WebSocket error occurred', 'error');
};
} catch (error) {
log(`Failed to create WebSocket: ${error.message}`, 'error');
updateStatus('Error', 'error');
updateButtons(false);
}
}
function disconnect() {
if (ws) {
ws.close(1000, 'Manual disconnect');
ws = null;
}
connectionAttempts = maxRetries; // Prevent auto-reconnect
}
function sendMessage(type, data = {}) {
if (!ws || ws.readyState !== WebSocket.OPEN) {
log('WebSocket not connected', 'error');
return;
}
const message = { type, data };
ws.send(JSON.stringify(message));
log(`${type}: ${JSON.stringify(data, null, 2)}`, 'sent');
}
function sendHeartbeat() {
sendMessage('heartbeat');
}
function requestStatus() {
sendMessage('request_trial_status');
}
function sendTestAction() {
sendMessage('trial_action', {
actionType: 'test_action',
message: 'Hello from WebSocket test!',
timestamp: Date.now()
});
}
function sendCustomMessage() {
const type = document.getElementById('messageType').value;
let data = {};
try {
const dataText = document.getElementById('messageData').value.trim();
if (dataText) {
data = JSON.parse(dataText);
}
} catch (e) {
log('Invalid JSON in message data', 'error');
return;
}
sendMessage(type, data);
}
function clearLog() {
logEl.textContent = '';
}
// Auto-connect on page load
document.addEventListener('DOMContentLoaded', function() {
log('WebSocket test page loaded');
log('Click "Connect" to start testing the WebSocket connection');
});
// Handle page unload
window.addEventListener('beforeunload', function() {
if (ws) {
ws.close(1000, 'Page unload');
}
});
</script>
</body>
</html>

477
_archive/ws-check.html Executable file
View File

@@ -0,0 +1,477 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebSocket Connection Test | HRIStudio</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
background: #f8fafc;
color: #334155;
line-height: 1.6;
}
.container {
max-width: 800px;
margin: 2rem auto;
padding: 0 1rem;
}
.card {
background: white;
border-radius: 12px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
overflow: hidden;
margin-bottom: 1rem;
}
.card-header {
background: #1e293b;
color: white;
padding: 1rem;
font-size: 1.25rem;
font-weight: 600;
}
.card-content {
padding: 1rem;
}
.status-badge {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 1rem;
border-radius: 20px;
font-size: 0.875rem;
font-weight: 500;
margin: 0.5rem 0;
}
.status-connecting {
background: #dbeafe;
color: #1e40af;
}
.status-connected {
background: #dcfce7;
color: #166534;
}
.status-failed {
background: #fef2f2;
color: #dc2626;
}
.status-fallback {
background: #fef3c7;
color: #92400e;
}
.dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: currentColor;
}
.dot.pulse {
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.log {
background: #f1f5f9;
border: 1px solid #e2e8f0;
border-radius: 8px;
padding: 1rem;
height: 300px;
overflow-y: auto;
font-family: "Courier New", monospace;
font-size: 0.875rem;
white-space: pre-wrap;
}
.controls {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
margin: 1rem 0;
}
button {
background: #3b82f6;
color: white;
border: none;
padding: 0.5rem 1rem;
border-radius: 6px;
cursor: pointer;
font-size: 0.875rem;
font-weight: 500;
transition: background 0.2s;
}
button:hover:not(:disabled) {
background: #2563eb;
}
button:disabled {
background: #94a3b8;
cursor: not-allowed;
}
.input-group {
margin: 1rem 0;
}
.input-group label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
color: #475569;
}
input[type="text"] {
width: 100%;
padding: 0.5rem;
border: 1px solid #d1d5db;
border-radius: 6px;
font-size: 0.875rem;
}
.info-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
margin: 1rem 0;
}
.info-item {
background: #f8fafc;
padding: 0.75rem;
border-radius: 6px;
border: 1px solid #e2e8f0;
}
.info-label {
font-size: 0.75rem;
color: #64748b;
text-transform: uppercase;
letter-spacing: 0.05em;
margin-bottom: 0.25rem;
}
.info-value {
font-weight: 500;
word-break: break-all;
}
.alert {
padding: 1rem;
border-radius: 8px;
margin: 1rem 0;
border-left: 4px solid;
}
.alert-info {
background: #eff6ff;
border-color: #3b82f6;
color: #1e40af;
}
.alert-warning {
background: #fefce8;
border-color: #eab308;
color: #a16207;
}
</style>
</head>
<body>
<div class="container">
<div class="card">
<div class="card-header">
🔌 WebSocket Connection Test
</div>
<div class="card-content">
<div class="alert alert-info">
<strong>Development Mode:</strong> WebSocket connections are expected to fail in Next.js development server.
The app automatically falls back to polling for real-time updates.
</div>
<div id="status" class="status-badge status-failed">
<div class="dot"></div>
<span>Disconnected</span>
</div>
<div class="input-group">
<label for="trialId">Trial ID:</label>
<input type="text" id="trialId" value="931c626d-fe3f-4db3-a36c-50d6898e1b17">
</div>
<div class="input-group">
<label for="userId">User ID:</label>
<input type="text" id="userId" value="08594f2b-64fe-4952-947f-3edc5f144f52">
</div>
<div class="controls">
<button id="connectBtn" onclick="testConnection()">Test WebSocket Connection</button>
<button id="disconnectBtn" onclick="disconnect()" disabled>Disconnect</button>
<button onclick="clearLog()">Clear Log</button>
<button onclick="testPolling()">Test Polling Fallback</button>
</div>
<div class="info-grid">
<div class="info-item">
<div class="info-label">Connection Attempts</div>
<div class="info-value" id="attempts">0</div>
</div>
<div class="info-item">
<div class="info-label">Messages Received</div>
<div class="info-value" id="messages">0</div>
</div>
<div class="info-item">
<div class="info-label">Connection Time</div>
<div class="info-value" id="connectionTime">N/A</div>
</div>
<div class="info-item">
<div class="info-label">Last Error</div>
<div class="info-value" id="lastError">None</div>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
📋 Connection Log
</div>
<div class="card-content">
<div id="log" class="log"></div>
</div>
</div>
<div class="card">
<div class="card-header">
How This Works
</div>
<div class="card-content">
<h3 style="margin-bottom: 0.5rem;">Expected Behavior:</h3>
<ul style="margin-left: 2rem; margin-bottom: 1rem;">
<li><strong>Development:</strong> WebSocket fails, app uses polling fallback (2-second intervals)</li>
<li><strong>Production:</strong> WebSocket connects successfully, minimal polling backup</li>
</ul>
<h3 style="margin-bottom: 0.5rem;">Testing Steps:</h3>
<ol style="margin-left: 2rem;">
<li>Click "Test WebSocket Connection" - should fail with connection error</li>
<li>Click "Test Polling Fallback" - should work and show API responses</li>
<li>Check browser Network tab for ongoing tRPC polling requests</li>
<li>Open actual wizard interface to see full functionality</li>
</ol>
<div class="alert alert-warning" style="margin-top: 1rem;">
<strong>Note:</strong> This test confirms the WebSocket failure is expected in development.
Your trial runner works perfectly using the polling fallback system.
</div>
</div>
</div>
</div>
<script>
let ws = null;
let attempts = 0;
let messages = 0;
let startTime = null;
const elements = {
status: document.getElementById('status'),
log: document.getElementById('log'),
connectBtn: document.getElementById('connectBtn'),
disconnectBtn: document.getElementById('disconnectBtn'),
attempts: document.getElementById('attempts'),
messages: document.getElementById('messages'),
connectionTime: document.getElementById('connectionTime'),
lastError: document.getElementById('lastError')
};
function updateStatus(text, className, pulse = false) {
elements.status.innerHTML = `
<div class="dot ${pulse ? 'pulse' : ''}"></div>
<span>${text}</span>
`;
elements.status.className = `status-badge ${className}`;
}
function log(message, type = 'info') {
const timestamp = new Date().toLocaleTimeString();
const prefix = {
info: '',
success: '✅',
error: '❌',
warning: '⚠️',
websocket: '🔌',
polling: '🔄'
}[type] || '';
elements.log.textContent += `[${timestamp}] ${prefix} ${message}\n`;
elements.log.scrollTop = elements.log.scrollHeight;
}
function updateButtons(connecting = false, connected = false) {
elements.connectBtn.disabled = connecting || connected;
elements.disconnectBtn.disabled = !connected;
}
function generateToken() {
const userId = document.getElementById('userId').value;
return btoa(JSON.stringify({
userId: userId,
timestamp: Math.floor(Date.now() / 1000)
}));
}
function testConnection() {
const trialId = document.getElementById('trialId').value;
const token = generateToken();
if (!trialId) {
log('Please enter a trial ID', 'error');
return;
}
attempts++;
elements.attempts.textContent = attempts;
startTime = Date.now();
updateStatus('Connecting...', 'status-connecting', true);
updateButtons(true, false);
const wsUrl = `ws://localhost:3000/api/websocket?trialId=${trialId}&token=${token}`;
log(`Attempting WebSocket connection to: ${wsUrl}`, 'websocket');
log('This is expected to fail in development mode...', 'warning');
try {
ws = new WebSocket(wsUrl);
ws.onopen = function() {
const duration = Date.now() - startTime;
elements.connectionTime.textContent = `${duration}ms`;
updateStatus('Connected', 'status-connected');
updateButtons(false, true);
log('🎉 WebSocket connected successfully!', 'success');
log('This is unexpected in development mode - you may be in production', 'info');
};
ws.onmessage = function(event) {
messages++;
elements.messages.textContent = messages;
try {
const data = JSON.parse(event.data);
log(`📨 Received: ${data.type} - ${JSON.stringify(data.data)}`, 'success');
} catch (e) {
log(`📨 Received (raw): ${event.data}`, 'success');
}
};
ws.onclose = function(event) {
updateStatus('Connection Failed (Expected)', 'status-failed');
updateButtons(false, false);
if (event.code === 1006) {
log('✅ Connection failed as expected in development mode', 'success');
log('This confirms WebSocket failure behavior is working correctly', 'info');
elements.lastError.textContent = 'Expected dev failure';
} else {
log(`Connection closed: ${event.code} - ${event.reason}`, 'error');
elements.lastError.textContent = `${event.code}: ${event.reason}`;
}
updateStatus('Fallback to Polling (Normal)', 'status-fallback');
log('🔄 App will automatically use polling fallback', 'polling');
};
ws.onerror = function(error) {
log('✅ WebSocket error occurred (expected in dev mode)', 'success');
log('Error details: Connection establishment failed', 'info');
elements.lastError.textContent = 'Connection refused (expected)';
};
} catch (error) {
log(`Failed to create WebSocket: ${error.message}`, 'error');
updateStatus('Connection Failed', 'status-failed');
updateButtons(false, false);
elements.lastError.textContent = error.message;
}
}
function disconnect() {
if (ws) {
ws.close(1000, 'Manual disconnect');
ws = null;
}
updateStatus('Disconnected', 'status-failed');
updateButtons(false, false);
log('Disconnected by user', 'info');
}
function clearLog() {
elements.log.textContent = '';
messages = 0;
elements.messages.textContent = messages;
log('Log cleared', 'info');
}
async function testPolling() {
log('🔄 Testing polling fallback (tRPC API)...', 'polling');
try {
const trialId = document.getElementById('trialId').value;
const response = await fetch(`/api/trpc/trials.get?batch=1&input=${encodeURIComponent(JSON.stringify({0:{json:{id:trialId}}}))}`);
if (response.ok) {
const data = await response.json();
log('✅ Polling fallback working! API response received', 'success');
log(`Response status: ${response.status}`, 'info');
log('This is how the app gets real-time updates in development', 'polling');
if (data[0]?.result?.data) {
log(`Trial status: ${data[0].result.data.json.status}`, 'info');
}
} else {
log(`❌ Polling failed: ${response.status} ${response.statusText}`, 'error');
if (response.status === 401) {
log('You may need to sign in first', 'warning');
}
}
} catch (error) {
log(`❌ Polling error: ${error.message}`, 'error');
log('Make sure the dev server is running', 'warning');
}
}
// Initialize
document.addEventListener('DOMContentLoaded', function() {
log('WebSocket test page loaded', 'info');
log('Click "Test WebSocket Connection" to verify expected failure', 'info');
log('Click "Test Polling Fallback" to verify API connectivity', 'info');
// Auto-test on load
setTimeout(() => {
log('Running automatic connection test...', 'websocket');
testConnection();
}, 1000);
});
</script>
</body>
</html>