mirror of
https://github.com/soconnor0919/hristudio.git
synced 2026-03-23 19:27:51 -04:00
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:
84
_archive/DOCUMENTATION.md
Normal file
84
_archive/DOCUMENTATION.md
Normal 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)
|
||||
344
_archive/HANDOFF-NAO6-INTEGRATION.md
Normal file
344
_archive/HANDOFF-NAO6-INTEGRATION.md
Normal 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`)
|
||||
374
_archive/THESIS_PROJECT_BACKLOG.md
Executable file
374
_archive/THESIS_PROJECT_BACKLOG.md
Executable 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
279
_archive/TRIAL_START_DEBUG.md
Executable 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.
|
||||
193
_archive/WIZARD_INTERFACE_README.md
Executable file
193
_archive/WIZARD_INTERFACE_README.md
Executable 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
45
_archive/errors.txt
Normal 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.
|
||||
368
_archive/nao6_integration_README.md
Normal file
368
_archive/nao6_integration_README.md
Normal 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
927
_archive/plugin_dump.json
Normal 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
86
_archive/simple-ws-test.html
Executable 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
297
_archive/test-websocket.html
Executable 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
477
_archive/ws-check.html
Executable 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>
|
||||
Reference in New Issue
Block a user