diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..3534d5d --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +.git +.github +.gitignore +.dockerignore +docker-compose.yml +README.md +LICENSE +docs/ \ No newline at end of file diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml new file mode 100644 index 0000000..88749b0 --- /dev/null +++ b/.github/workflows/validate.yml @@ -0,0 +1,247 @@ +name: Validate Plugins + +on: + push: + branches: ["main", "develop"] + pull_request: + branches: ["main"] + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '18' + cache: 'npm' + + - name: Install dependencies + run: | + npm install -g ajv-cli + npm install -g jsonlint + + - name: Validate JSON syntax + run: | + echo "Validating repository.json..." + jsonlint repository.json + + echo "Validating plugins/index.json..." + jsonlint plugins/index.json + + echo "Validating plugin files..." + for file in plugins/*.json; do + if [ "$file" != "plugins/index.json" ]; then + echo "Validating $file..." + jsonlint "$file" + fi + done + + - name: Check plugin index consistency + run: | + echo "Checking plugin index consistency..." + node -e " + const fs = require('fs'); + const index = JSON.parse(fs.readFileSync('plugins/index.json', 'utf8')); + const files = fs.readdirSync('plugins').filter(f => f.endsWith('.json') && f !== 'index.json'); + + console.log('Index files:', index); + console.log('Actual files:', files); + + const missing = files.filter(f => !index.includes(f)); + const extra = index.filter(f => !files.includes(f)); + + if (missing.length > 0) { + console.error('Files missing from index:', missing); + process.exit(1); + } + + if (extra.length > 0) { + console.error('Extra files in index:', extra); + process.exit(1); + } + + console.log('Plugin index is consistent ✓'); + " + + - name: Validate plugin schemas + run: | + echo "Validating plugin schemas..." + node -e " + const fs = require('fs'); + + // Basic plugin schema validation + function validatePlugin(pluginPath) { + const plugin = JSON.parse(fs.readFileSync(pluginPath, 'utf8')); + const errors = []; + + // Required fields + const required = ['robotId', 'name', 'platform', 'version', 'pluginApiVersion', 'hriStudioVersion', 'trustLevel', 'category']; + for (const field of required) { + if (!plugin[field]) { + errors.push(\`Missing required field: \${field}\`); + } + } + + // Validate trustLevel enum + if (plugin.trustLevel && !['official', 'verified', 'community'].includes(plugin.trustLevel)) { + errors.push(\`Invalid trustLevel: \${plugin.trustLevel}\`); + } + + // Validate actions + if (plugin.actions && Array.isArray(plugin.actions)) { + plugin.actions.forEach((action, i) => { + if (!action.id) errors.push(\`Action \${i}: missing id\`); + if (!action.name) errors.push(\`Action \${i}: missing name\`); + if (!action.category) errors.push(\`Action \${i}: missing category\`); + if (action.category && !['movement', 'interaction', 'sensors', 'logic'].includes(action.category)) { + errors.push(\`Action \${i}: invalid category \${action.category}\`); + } + if (!action.parameterSchema) errors.push(\`Action \${i}: missing parameterSchema\`); + }); + } + + return errors; + } + + const index = JSON.parse(fs.readFileSync('plugins/index.json', 'utf8')); + let hasErrors = false; + + for (const pluginFile of index) { + console.log(\`Validating \${pluginFile}...\`); + const errors = validatePlugin(\`plugins/\${pluginFile}\`); + + if (errors.length > 0) { + console.error(\`Errors in \${pluginFile}:\`); + errors.forEach(error => console.error(\` - \${error}\`)); + hasErrors = true; + } else { + console.log(\` ✓ Valid\`); + } + } + + if (hasErrors) { + process.exit(1); + } + + console.log('All plugins are valid ✓'); + " + + - name: Check asset references + run: | + echo "Checking asset references..." + node -e " + const fs = require('fs'); + const path = require('path'); + + function checkAssets(pluginPath) { + const plugin = JSON.parse(fs.readFileSync(pluginPath, 'utf8')); + const errors = []; + + if (plugin.assets) { + const checkAsset = (assetPath, description) => { + if (assetPath && assetPath.startsWith('assets/')) { + const fullPath = assetPath; + if (!fs.existsSync(fullPath)) { + errors.push(\`\${description}: \${assetPath} not found\`); + } + } + }; + + checkAsset(plugin.assets.thumbnailUrl, 'Thumbnail'); + + if (plugin.assets.images) { + checkAsset(plugin.assets.images.main, 'Main image'); + checkAsset(plugin.assets.images.logo, 'Logo'); + + if (plugin.assets.images.angles) { + Object.entries(plugin.assets.images.angles).forEach(([angle, path]) => { + checkAsset(path, \`\${angle} angle image\`); + }); + } + } + } + + return errors; + } + + const index = JSON.parse(fs.readFileSync('plugins/index.json', 'utf8')); + let hasErrors = false; + + for (const pluginFile of index) { + console.log(\`Checking assets for \${pluginFile}...\`); + const errors = checkAssets(\`plugins/\${pluginFile}\`); + + if (errors.length > 0) { + console.error(\`Asset errors in \${pluginFile}:\`); + errors.forEach(error => console.error(\` - \${error}\`)); + hasErrors = true; + } else { + console.log(\` ✓ All assets found\`); + } + } + + if (hasErrors) { + console.warn('Some assets are missing - this may cause display issues'); + } else { + console.log('All assets are available ✓'); + } + " + + - name: Validate repository metadata + run: | + echo "Validating repository metadata..." + node -e " + const fs = require('fs'); + const repo = JSON.parse(fs.readFileSync('repository.json', 'utf8')); + const index = JSON.parse(fs.readFileSync('plugins/index.json', 'utf8')); + + // Check plugin count matches + const actualCount = index.length; + const reportedCount = repo.stats?.plugins || 0; + + if (actualCount !== reportedCount) { + console.error(\`Plugin count mismatch: reported \${reportedCount}, actual \${actualCount}\`); + process.exit(1); + } + + console.log(\`Plugin count is correct: \${actualCount} ✓\`); + + // Check required repository fields + const required = ['id', 'name', 'apiVersion', 'pluginApiVersion', 'trust']; + for (const field of required) { + if (!repo[field]) { + console.error(\`Missing required repository field: \${field}\`); + process.exit(1); + } + } + + console.log('Repository metadata is valid ✓'); + " + + - name: Test web interface + run: | + echo "Testing web interface..." + # Start a simple HTTP server + python3 -m http.server 8000 & + SERVER_PID=$! + + # Wait for server to start + sleep 2 + + # Test that index.html loads + curl -f http://localhost:8000/index.html > /dev/null + + # Test that repository.json is accessible + curl -f http://localhost:8000/repository.json > /dev/null + + # Test that plugins/index.json is accessible + curl -f http://localhost:8000/plugins/index.json > /dev/null + + # Clean up + kill $SERVER_PID + + echo "Web interface test passed ✓" diff --git a/README.md b/README.md index 2d55c4c..f25e385 100644 --- a/README.md +++ b/README.md @@ -1,92 +1,275 @@ # HRIStudio Robot Plugins Repository -This repository contains robot plugins for use with HRIStudio. Each plugin provides a standardized interface for controlling and interacting with different types of robots. +Official collection of robot plugins for the HRIStudio platform, providing standardized interfaces for controlling and interacting with different types of robots in Human-Robot Interaction research. + +## Overview + +This repository contains robot plugins that enable HRIStudio to work with various robot platforms including mobile robots, humanoid robots, manipulators, and drones. Each plugin provides a standardized interface for robot control, sensor data collection, and experiment execution. + +## Available Plugins + +### Mobile Robots +- **TurtleBot3 Burger** (`turtlebot3-burger`) - Compact educational robot platform +- **TurtleBot3 Waffle** (`turtlebot3-waffle`) - Extended TurtleBot3 with camera and additional sensors + +### Humanoid Robots +- **NAO Humanoid** (`nao-humanoid`) - SoftBank Robotics NAO for social interaction research + +## Quick Start + +### Using Plugins in HRIStudio + +1. **Add Repository**: In HRIStudio Admin panel, add this repository URL +2. **Install Plugins**: Browse and install plugins for your study +3. **Design Experiments**: Use plugin actions in the experiment designer +4. **Run Trials**: Execute experiments with real-time robot control + +### Local Development + +```bash +# Clone the repository +git clone https://github.com/soconnor0919/robot-plugins.git +cd robot-plugins + +# Install development dependencies (optional) +npm install + +# Start development server +./validate.sh serve + +# Validate all plugins +./validate.sh validate + +# Create a new plugin +./validate.sh create my-robot +``` ## Repository Structure ``` -repository.json # Repository metadata and configuration -index.html # Web interface for viewing repository information -plugins/ # Directory containing all plugin files - index.json # List of available plugins - plugin1.json # Individual plugin definition - plugin2.json # Individual plugin definition - ... -assets/ # Optional directory for repository assets - repository-icon.png # Repository icon - repository-logo.png # Repository logo - repository-banner.png # Repository banner +robot-plugins/ +├── repository.json # Repository metadata +├── index.html # Web interface +├── plugins/ # Plugin definitions +│ ├── index.json # Plugin list +│ ├── turtlebot3-burger.json +│ ├── turtlebot3-waffle.json +│ └── nao-humanoid.json +├── assets/ # Visual assets +│ ├── repository-*.png # Repository branding +│ ├── turtlebot3-burger/ # Robot images +│ ├── turtlebot3-waffle/ +│ └── nao-humanoid/ +├── docs/ # Documentation +│ ├── schema.md # Plugin schema reference +│ └── plugins.md # Plugin development guide +├── scripts/ # Development tools +│ └── validate-plugin.js # Plugin validator +└── .github/workflows/ # CI/CD pipelines +``` + +## Plugin Development + +### Creating a New Plugin + +1. **Generate Template**: + ```bash + ./validate.sh create my-robot + ``` + +2. **Edit Plugin Definition**: Update `plugins/my-robot.json` with robot details + +3. **Add Assets**: Place robot images in `assets/my-robot/` + +4. **Validate Plugin**: + ```bash + ./validate.sh validate + ``` + +5. **Update Index**: + ```bash + ./validate.sh update-index + ``` + +### Plugin Schema + +Each plugin must include: + +```json +{ + "robotId": "unique-robot-id", + "name": "Robot Display Name", + "platform": "ROS2|NAOqi|Custom", + "version": "1.0.0", + "pluginApiVersion": "1.0", + "hriStudioVersion": ">=0.1.0", + "trustLevel": "official|verified|community", + "category": "mobile-robot|humanoid-robot|manipulator|drone", + "actions": [...] +} +``` + +### Action Definitions + +Actions define robot operations available in experiments: + +```json +{ + "id": "action_name", + "name": "Action Display Name", + "category": "movement|interaction|sensors|logic", + "parameterSchema": { + "type": "object", + "properties": {...}, + "required": [...] + }, + "ros2": { + "messageType": "geometry_msgs/msg/Twist", + "topic": "/cmd_vel" + } +} +``` + +## Development Tools + +### Validation Script + +```bash +# Validate all plugins and repository +./validate.sh validate + +# Run full test suite +./validate.sh test + +# Build for production +./validate.sh build + +# Start development server +./validate.sh serve [port] +``` + +### Node.js Scripts + +```bash +# Validate specific plugin +node scripts/validate-plugin.js validate plugins/my-robot.json + +# Validate all plugins +npm run validate + +# Update plugin index +npm run update-index + +# Show repository statistics +npm run stats ``` ## Web Interface -The repository includes a built-in web interface (`index.html`) that provides a user-friendly way to view repository information. When hosting your repository, this interface will automatically: +The repository includes a built-in web interface accessible at the repository URL. It provides: -- Display repository name, description, and metadata -- Show repository statistics (plugin count) -- List author information and compatibility details -- Display repository tags and categories -- Show repository assets (icon, banner, logo) - -The web interface is automatically available when you host your repository, making it easy for users to browse repository information before adding it to HRIStudio. - -## Repository Configuration - -The `repository.json` file contains the repository's metadata and configuration: - -```json -{ - "id": "unique-repository-id", - "name": "Repository Name", - "description": "Repository description", - "urls": { - "repository": "https://example.com/repository", - "git": "https://github.com/user/repo.git" - }, - "official": false, - "trust": "community", - "author": { - "name": "Author Name", - "organization": "Organization Name", - "url": "https://example.com" - }, - "compatibility": { - "hristudio": { - "min": "1.0.0", - "recommended": "1.1.0" - }, - "ros2": { - "distributions": ["humble", "iron"], - "recommended": "iron" - } - }, - "assets": { - "icon": "assets/repository-icon.png", - "banner": "assets/repository-banner.png", - "logo": "assets/repository-logo.png" - }, - "stats": { - "plugins": 0 - }, - "tags": ["robots", "simulation", "education"] -} -``` - -## Plugin Structure - -Each plugin is defined in a JSON file within the `plugins` directory. The `plugins/index.json` file contains a list of all available plugin files. - -For detailed information about plugin structure and requirements, see the [Plugin Documentation](docs/plugins.md). +- Repository information and statistics +- Plugin catalog with search and filtering +- Individual plugin details and documentation +- Asset preview and download links +- Installation instructions for HRIStudio ## Contributing -1. Fork or clone this repository -2. Create your plugin branch -3. Add your plugin JSON file to the `plugins` directory -4. Update `plugins/index.json` to include your plugin -5. Test your changes locally -6. Submit your changes +### Adding a Plugin + +1. **Fork** this repository +2. **Create** your plugin using the template +3. **Add** comprehensive robot assets +4. **Validate** your plugin thoroughly +5. **Submit** a pull request + +### Plugin Requirements + +- Valid JSON syntax and schema compliance +- Complete action definitions with parameter schemas +- High-quality robot images (thumbnail, main, angles) +- Accurate robot specifications +- Working communication protocol configuration + +### Review Process + +All plugins undergo review for: +- Technical correctness +- Schema compliance +- Asset quality +- Documentation completeness +- Security considerations + +## Integration with HRIStudio + +### Repository Registration + +Administrators can add this repository in HRIStudio: + +1. Navigate to **Admin > Plugin Repositories** +2. Add repository URL: `https://repo.hristudio.com` +3. Set trust level and enable synchronization +4. Plugins become available for installation + +### Study Installation + +Researchers can install plugins for studies: + +1. Go to **Study > Plugins** +2. Browse available plugins from registered repositories +3. Install required plugins for your research +4. Configure plugin settings as needed + +### Experiment Design + +Plugin actions appear in the experiment designer: + +1. Drag actions from the **Block Library** +2. Configure parameters in the **Properties Panel** +3. Connect actions to create experiment flow +4. Test and validate your protocol + +### Trial Execution + +During live trials: + +1. HRIStudio establishes robot connections +2. Wizard controls actions in real-time +3. All robot commands are logged +4. Sensor data is captured automatically + +## API Compatibility + +This repository supports: +- **Plugin API Version**: 1.0 +- **HRIStudio Version**: 0.1.0+ +- **Schema Version**: Latest + +## Trust Levels + +Plugins are classified by trust level: + +- **Official**: Maintained by HRIStudio team or robot manufacturers +- **Verified**: Third-party plugins reviewed and tested +- **Community**: User-contributed plugins (use with caution) + +## Support + +- **Documentation**: [Plugin Development Guide](docs/plugins.md) +- **Schema Reference**: [Schema Documentation](docs/schema.md) +- **Issues**: [GitHub Issues](https://github.com/soconnor0919/robot-plugins/issues) +- **Email**: support@hristudio.com ## License -This repository is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. \ No newline at end of file +This repository is licensed under the MIT License. See [LICENSE](LICENSE) for details. + +Individual plugins may have different licenses - please check each plugin's documentation. + +## Acknowledgments + +- ROBOTIS for TurtleBot3 platform support +- SoftBank Robotics for NAO platform documentation +- ROS2 community for standardized messaging +- HRIStudio research community for feedback and testing \ No newline at end of file diff --git a/assets/nao-humanoid/back.png b/assets/nao-humanoid/back.png new file mode 100644 index 0000000..9925d00 Binary files /dev/null and b/assets/nao-humanoid/back.png differ diff --git a/assets/nao-humanoid/front.png b/assets/nao-humanoid/front.png new file mode 100644 index 0000000..9925d00 Binary files /dev/null and b/assets/nao-humanoid/front.png differ diff --git a/assets/nao-humanoid/logo.png b/assets/nao-humanoid/logo.png new file mode 100644 index 0000000..9925d00 Binary files /dev/null and b/assets/nao-humanoid/logo.png differ diff --git a/assets/nao-humanoid/main.jpg b/assets/nao-humanoid/main.jpg new file mode 100644 index 0000000..9925d00 Binary files /dev/null and b/assets/nao-humanoid/main.jpg differ diff --git a/assets/nao-humanoid/side.png b/assets/nao-humanoid/side.png new file mode 100644 index 0000000..9925d00 Binary files /dev/null and b/assets/nao-humanoid/side.png differ diff --git a/assets/nao-humanoid/thumb.png b/assets/nao-humanoid/thumb.png new file mode 100644 index 0000000..9925d00 Binary files /dev/null and b/assets/nao-humanoid/thumb.png differ diff --git a/assets/style.css b/assets/style.css index 6e58682..570e0f8 100644 --- a/assets/style.css +++ b/assets/style.css @@ -793,13 +793,18 @@ img { } .plugin-details-header { - padding: 1.5rem; border-bottom: 1px solid hsl(var(--border)); } +.plugin-details-header-content { + display: grid; + gap: 1.5rem; + grid-template-columns: auto 1fr; +} + .plugin-details-icon { - width: 4rem; - height: 4rem; + width: 5rem; + height: 5rem; flex-shrink: 0; border-radius: calc(var(--radius) - 0.25rem); overflow: hidden; @@ -811,7 +816,24 @@ img { width: 100%; height: 100%; object-fit: contain; - padding: 0.5rem; + padding: 0.75rem; +} + +@media (max-width: 640px) { + .plugin-details-header-content { + grid-template-columns: 1fr; + text-align: center; + } + + .plugin-details-icon { + width: 4rem; + height: 4rem; + margin: 0 auto; + } + + .plugin-details-header .flex-wrap { + justify-content: center; + } } /* Plugin Images */ @@ -1077,4 +1099,113 @@ code { width: 100%; height: 200px; } +} + +/* Image Zoom Modal */ +.zoom-modal { + position: fixed; + inset: 0; + z-index: 50; + display: none; + align-items: center; + justify-content: center; + padding: 1.5rem; + background: hsl(var(--background) / 0.8); + backdrop-filter: blur(8px); +} + +.zoom-modal[data-state="open"] { + display: flex; + animation: modal-in 0.3s ease-out; +} + +.zoom-modal-content { + position: relative; + width: 100%; + max-width: 90vw; + max-height: 90vh; + border-radius: var(--radius); + overflow: hidden; + background: hsl(var(--card)); + box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25); +} + +.zoom-modal-image { + width: 100%; + height: 100%; + object-fit: contain; +} + +.zoom-modal-close { + position: absolute; + top: 1rem; + right: 1rem; + width: 2rem; + height: 2rem; + border-radius: 9999px; + display: flex; + align-items: center; + justify-content: center; + background: hsl(var(--card)); + border: 1px solid hsl(var(--border)); + color: hsl(var(--foreground)); + cursor: pointer; + transition: all 0.15s ease; +} + +.zoom-modal-close:hover { + background: hsl(var(--accent) / 0.1); + color: hsl(var(--accent)); +} + +/* Update image gallery styles */ +.image-gallery { + display: grid; + gap: 1rem; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); +} + +.image-gallery-item { + position: relative; + aspect-ratio: 1; + border-radius: var(--radius); + overflow: hidden; + border: 1px solid hsl(var(--border)); + background: hsl(var(--muted)); + cursor: zoom-in; + transition: all 0.15s ease; +} + +.image-gallery-item:hover { + border-color: hsl(var(--primary)); + transform: scale(1.02); +} + +.image-gallery-item img { + width: 100%; + height: 100%; + object-fit: cover; +} + +.image-gallery-label { + position: absolute; + bottom: 0; + left: 0; + right: 0; + padding: 0.5rem 1rem; + background: linear-gradient(to top, rgba(0, 0, 0, 0.7), transparent); + color: white; + font-size: 0.75rem; + font-weight: 500; +} + +@keyframes modal-in { + from { + opacity: 0; + transform: scale(0.95); + } + to { + opacity: 1; + transform: scale(1); + } } \ No newline at end of file diff --git a/assets/turtlebot3-waffle/front.png b/assets/turtlebot3-waffle/front.png new file mode 100644 index 0000000..e15dd2d Binary files /dev/null and b/assets/turtlebot3-waffle/front.png differ diff --git a/assets/turtlebot3-waffle/logo.png b/assets/turtlebot3-waffle/logo.png new file mode 100644 index 0000000..a5be8aa Binary files /dev/null and b/assets/turtlebot3-waffle/logo.png differ diff --git a/assets/turtlebot3-waffle/main.jpg b/assets/turtlebot3-waffle/main.jpg new file mode 100644 index 0000000..08c1574 Binary files /dev/null and b/assets/turtlebot3-waffle/main.jpg differ diff --git a/assets/turtlebot3-waffle/side.png b/assets/turtlebot3-waffle/side.png new file mode 100644 index 0000000..4386959 Binary files /dev/null and b/assets/turtlebot3-waffle/side.png differ diff --git a/assets/turtlebot3-waffle/thumb.png b/assets/turtlebot3-waffle/thumb.png new file mode 100644 index 0000000..a5be8aa Binary files /dev/null and b/assets/turtlebot3-waffle/thumb.png differ diff --git a/assets/turtlebot3-waffle/top.png b/assets/turtlebot3-waffle/top.png new file mode 100644 index 0000000..df25471 Binary files /dev/null and b/assets/turtlebot3-waffle/top.png differ diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..5616c27 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,17 @@ +services: + web: + image: nginx:alpine + ports: + - "8080:80" + volumes: + - .:/usr/share/nginx/html:ro + environment: + - NGINX_HOST=localhost + - NGINX_PORT=80 + command: > + /bin/sh -c "nginx -g 'daemon off;'" + healthcheck: + test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:80"] + interval: 10s + timeout: 5s + retries: 3 \ No newline at end of file diff --git a/docs/plugins.md b/docs/plugins.md new file mode 100644 index 0000000..bd8afd2 --- /dev/null +++ b/docs/plugins.md @@ -0,0 +1,340 @@ +# HRIStudio Robot Plugins + +This document explains how robot plugins work in HRIStudio and provides guidance for plugin developers. + +## Overview + +HRIStudio uses a plugin-based architecture to support different robot platforms. Each plugin defines: + +- Robot capabilities and specifications +- Available actions for experiment design +- ROS2 integration details +- Communication protocols + +## Plugin Structure + +### Core Components + +1. **Robot Definition**: Basic information about the robot platform +2. **Action Library**: Operations that can be performed during experiments +3. **ROS2 Integration**: Message types, topics, and communication setup +4. **Assets**: Images, models, and visual resources + +### Plugin Lifecycle + +1. **Repository Registration**: Plugin repositories are added to HRIStudio +2. **Plugin Discovery**: Available plugins are fetched from repositories +3. **Study Installation**: Plugins are installed for specific studies +4. **Experiment Integration**: Actions become available in the experiment designer +5. **Trial Execution**: Actions are executed during live trials + +## Action System + +### Action Types + +Actions are the building blocks of experiments. HRIStudio supports four main categories: + +#### Movement Actions +- Robot locomotion and positioning +- Navigation and path planning +- Velocity control and stopping + +#### Interaction Actions +- Speech synthesis and audio playback +- Gesture and animation control +- Display and lighting effects + +#### Sensor Actions +- Data collection from sensors +- Environmental monitoring +- State queries and feedback + +#### Logic Actions +- Conditional operations +- Loops and iteration +- Variable manipulation + +### Parameter Schema + +Each action defines parameters using JSON Schema format: + +```json +{ + "type": "object", + "properties": { + "speed": { + "type": "number", + "minimum": 0, + "maximum": 1.0, + "default": 0.5, + "description": "Movement speed as fraction of maximum" + } + }, + "required": ["speed"] +} +``` + +### ROS2 Integration + +Actions can integrate with ROS2 through: + +- **Topics**: Publishing messages for robot control +- **Services**: Synchronous request/response operations +- **Actions**: Long-running operations with feedback + +## Plugin Development + +### Basic Plugin Structure + +```json +{ + "robotId": "my-robot", + "name": "My Robot", + "description": "Custom robot for research", + "platform": "ROS2", + "version": "1.0.0", + "pluginApiVersion": "1.0", + "hriStudioVersion": ">=0.1.0", + "trustLevel": "community", + "category": "mobile-robot", + + "manufacturer": { + "name": "Research Lab", + "website": "https://example.com" + }, + + "assets": { + "thumbnailUrl": "assets/my-robot/thumb.png" + }, + + "actions": [] +} +``` + +### Creating Actions + +Each action needs: + +1. **Unique ID**: Snake_case identifier +2. **Clear Name**: Human-readable title +3. **Category**: One of the four main types +4. **Parameters**: JSON Schema definition +5. **ROS2 Config**: Communication details + +Example action: + +```json +{ + "id": "move_forward", + "name": "Move Forward", + "description": "Move the robot forward by a specified distance", + "category": "movement", + "icon": "arrow-up", + "timeout": 30000, + "retryable": true, + + "parameterSchema": { + "type": "object", + "properties": { + "distance": { + "type": "number", + "minimum": 0.1, + "maximum": 5.0, + "default": 1.0, + "description": "Distance to move in meters" + } + }, + "required": ["distance"] + }, + + "ros2": { + "messageType": "geometry_msgs/msg/Twist", + "topic": "/cmd_vel", + "payloadMapping": { + "type": "transform", + "transformFn": "transformToTwist" + } + } +} +``` + +### Asset Management + +Plugins should include visual assets: + +- **Thumbnail**: 200x150px preview image +- **Main Image**: High-resolution robot photo +- **Angle Views**: Front, side, top perspectives +- **Logo**: Manufacturer or robot series logo + +Assets are served relative to the repository URL. + +### Testing Plugins + +Before publishing, test your plugin: + +1. Validate JSON syntax and schema +2. Verify all asset URLs are accessible +3. Test ROS2 message formats +4. Check parameter validation +5. Ensure timeout values are reasonable + +## Integration with HRIStudio + +### Repository Management + +Administrators can add plugin repositories in HRIStudio: + +1. Navigate to Admin > Plugin Repositories +2. Add repository URL +3. Set trust level and enable sync +4. Plugins become available for installation + +### Study-Level Installation + +Researchers install plugins for specific studies: + +1. Go to Study > Plugins +2. Browse available plugins +3. Install required plugins +4. Configure plugin settings + +### Experiment Design + +Installed plugin actions appear in the experiment designer: + +1. Drag actions from the block library +2. Configure parameters in the properties panel +3. Connect actions to create experiment flow +4. Test and validate the experiment protocol + +### Trial Execution + +During trials, HRIStudio: + +1. Establishes ROS2 connections +2. Validates action parameters +3. Sends commands to robots +4. Monitors execution status +5. Logs all events for analysis + +## Best Practices + +### Plugin Design + +- Use clear, descriptive action names +- Provide comprehensive parameter validation +- Include helpful descriptions for all parameters +- Choose appropriate timeout values +- Make actions atomic and focused + +### ROS2 Integration + +- Follow ROS2 naming conventions +- Use appropriate QoS settings +- Handle connection failures gracefully +- Implement proper error reporting +- Document message format requirements + +### Asset Management + +- Use consistent image dimensions +- Optimize file sizes for web delivery +- Provide high-quality thumbnails +- Include multiple viewing angles +- Ensure assets load quickly + +### Documentation + +- Document all action behaviors +- Provide usage examples +- Explain parameter effects +- Include troubleshooting tips +- Maintain version compatibility notes + +## Repository Hosting + +### File Structure + +``` +repository/ +├── repository.json # Repository metadata +├── index.html # Web interface +├── plugins/ +│ ├── index.json # Plugin list +│ └── robot-name.json # Individual plugins +└── assets/ + ├── repository-icon.png + └── robot-name/ + ├── thumb.png + ├── main.jpg + └── angles/ +``` + +### Hosting Requirements + +- HTTPS enabled +- CORS headers configured +- Static file serving +- Reliable uptime +- Fast response times + +### Version Management + +- Use semantic versioning +- Maintain backward compatibility +- Document breaking changes +- Provide migration guides +- Archive old versions + +## Security Considerations + +### Trust Levels + +- **Official**: Signed and verified plugins +- **Verified**: Community plugins that passed review +- **Community**: User-contributed, use with caution + +### Validation + +- All plugins are validated against schema +- Parameters are sanitized before execution +- ROS2 messages are type-checked +- Network communications are monitored + +### Permissions + +- Plugins run with limited permissions +- Robot access is study-scoped +- Actions can be disabled by administrators +- Audit logs track all plugin activities + +## Troubleshooting + +### Common Issues + +1. **Plugin Not Loading**: Check JSON syntax and schema compliance +2. **Assets Not Found**: Verify asset URLs and file paths +3. **ROS2 Connection Failed**: Check topic names and message types +4. **Action Timeout**: Increase timeout or check robot connectivity +5. **Parameter Validation**: Ensure all required parameters are provided + +### Debug Tools + +- Use browser developer tools for network issues +- Check HRIStudio logs for plugin errors +- Validate JSON files with online tools +- Test ROS2 connections independently +- Monitor robot topics and services + +## Contributing + +To contribute to the official plugin repository: + +1. Fork the repository +2. Create a new plugin file +3. Add assets and documentation +4. Test thoroughly +5. Submit a pull request + +For questions or support, contact the HRIStudio development team. \ No newline at end of file diff --git a/docs/schema.md b/docs/schema.md index dee191e..4d8a2fb 100644 --- a/docs/schema.md +++ b/docs/schema.md @@ -1,227 +1,243 @@ -# Plugin Schema Documentation +# HRIStudio Plugin Schema Documentation -This document describes the schema for HRIStudio robot plugins. +This document describes the schema for HRIStudio robot plugins, including both repository metadata and individual plugin definitions. -## Repository Metadata +## Repository Metadata (`repository.json`) -The repository itself is defined by a `repository.json` file with the following structure: +The repository metadata file defines the plugin repository and its capabilities: ```json { - "id": string, - "name": string, - "description": string?, + "id": "string (required) - Unique repository identifier", + "name": "string (required) - Display name", + "description": "string (optional) - Repository description", + "apiVersion": "string (required) - Repository API version", + "pluginApiVersion": "string (required) - Plugin API version supported", "urls": { - "repository": string (URL), - "git": string (URL)? + "repository": "string (URL, required) - Repository base URL", + "git": "string (URL, optional) - Git repository URL" }, - "official": boolean, + "official": "boolean (required) - Whether this is an official repository", + "trust": "string (enum: official|verified|community, required)", "author": { - "name": string, - "email": string (email)?, - "url": string (URL)?, - "organization": string? + "name": "string (required)", + "email": "string (email, optional)", + "url": "string (URL, optional)", + "organization": "string (optional)" }, "maintainers": [ { - "name": string, - "email": string (email)?, - "url": string (URL)? + "name": "string (required)", + "email": "string (email, optional)", + "url": "string (URL, optional)" } - ]?, - "homepage": string (URL)?, - "license": string, - "defaultBranch": string, - "lastUpdated": string (ISO date), - "trust": "official" | "verified" | "community", - "assets": { - "icon": string?, - "logo": string?, - "banner": string? - }, + ], + "homepage": "string (URL, optional)", + "license": "string (required) - License identifier", + "defaultBranch": "string (required) - Default Git branch", + "lastUpdated": "string (ISO date, required)", + "categories": [ + { + "id": "string (required) - Category identifier", + "name": "string (required) - Display name", + "description": "string (required) - Category description" + } + ], "compatibility": { "hristudio": { - "min": string, - "recommended": string? + "min": "string (semver, required) - Minimum HRIStudio version", + "recommended": "string (semver, optional) - Recommended version" }, "ros2": { - "distributions": string[], - "recommended": string? - }? + "distributions": "string[] (optional) - Supported ROS2 distributions", + "recommended": "string (optional) - Recommended distribution" + } }, - "tags": string[], + "assets": { + "icon": "string (path, optional) - Repository icon", + "logo": "string (path, optional) - Repository logo", + "banner": "string (path, optional) - Repository banner" + }, + "tags": "string[] (required) - Repository tags", "stats": { - "plugins": number - }? + "plugins": "number (required) - Number of plugins" + } } ``` ## Plugin Schema -Each plugin is defined in a JSON file with the following top-level structure: +Each plugin is defined in a JSON file with HRIStudio-specific extensions: + +### Core Properties ```json { - "robotId": string, - "name": string, - "description": string?, - "platform": string, - "version": string, - "manufacturer": object, - "documentation": object, - "assets": object, - "specs": object, - "ros2Config": object, - "actions": array + "robotId": "string (required) - Unique robot identifier", + "name": "string (required) - Display name", + "description": "string (optional) - Robot description", + "platform": "string (required) - Robot platform (e.g., 'ROS2')", + "version": "string (required) - Plugin version (semver)", + "pluginApiVersion": "string (required) - Plugin API version", + "hriStudioVersion": "string (required) - Minimum HRIStudio version", + "trustLevel": "string (enum: official|verified|community, required)", + "category": "string (required) - Plugin category identifier" } ``` -## Core Properties - -### Required Properties - -- `robotId`: Unique identifier for the robot (e.g., "turtlebot3-burger") -- `name`: Display name of the robot -- `platform`: Robot platform/framework (e.g., "ROS2") -- `version`: Plugin version (semver format) - -### Optional Properties - -- `description`: Detailed description of the robot - -## Manufacturer Information +### Manufacturer Information ```json "manufacturer": { - "name": string, - "website": string (URL)?, - "support": string (URL)? + "name": "string (required)", + "website": "string (URL, optional)", + "support": "string (URL, optional)" } ``` -## Documentation Links +### Documentation Links ```json "documentation": { - "mainUrl": string (URL), - "apiReference": string (URL)?, - "wikiUrl": string (URL)?, - "videoUrl": string (URL)? + "mainUrl": "string (URL, required)", + "apiReference": "string (URL, optional)", + "wikiUrl": "string (URL, optional)", + "videoUrl": "string (URL, optional)" } ``` -## Assets Configuration +### Assets Configuration ```json "assets": { - "thumbnailUrl": string, - "logo": string?, + "thumbnailUrl": "string (path, required)", "images": { - "main": string, + "main": "string (path, required)", "angles": { - "front": string?, - "side": string?, - "top": string? - } + "front": "string (path, optional)", + "side": "string (path, optional)", + "top": "string (path, optional)" + }, + "logo": "string (path, optional)" }, "model": { - "format": "URDF" | "glTF" | "other", - "url": string (URL) - }? + "format": "string (enum: URDF|glTF|STL, optional)", + "url": "string (URL, optional)" + } } ``` -## Robot Specifications +### Robot Specifications ```json "specs": { "dimensions": { - "length": number, - "width": number, - "height": number, - "weight": number + "length": "number (meters, required)", + "width": "number (meters, required)", + "height": "number (meters, required)", + "weight": "number (kg, required)" }, - "capabilities": string[], - "maxSpeed": number, - "batteryLife": number + "capabilities": "string[] (required) - List of robot capabilities", + "maxSpeed": "number (m/s, optional)", + "batteryLife": "number (hours, optional)" } ``` -## ROS2 Configuration +### ROS2 Configuration ```json "ros2Config": { - "namespace": string, - "nodePrefix": string, + "namespace": "string (required) - Default ROS2 namespace", + "nodePrefix": "string (required) - Node name prefix", "defaultTopics": { - [key: string]: string + "[topicName]": "string (topic path) - Default topic mappings" } } ``` -## Actions +## Action Definitions -Each action in the `actions` array follows this structure: +Actions define the operations that can be performed with the robot. Each action follows this HRIStudio-specific schema: ```json { - "actionId": string, - "type": "move" | "speak" | "wait" | "input" | "gesture" | "record" | "condition" | "loop", - "title": string, - "description": string, - "icon": string?, - "parameters": { - "type": "object", + "id": "string (required) - Unique action identifier (snake_case)", + "name": "string (required) - Display name for UI", + "description": "string (optional) - Action description", + "category": "string (enum: movement|interaction|sensors|logic, required)", + "icon": "string (optional) - Lucide icon name", + "timeout": "number (milliseconds, optional) - Default timeout", + "retryable": "boolean (optional) - Whether action can be retried on failure", + "parameterSchema": { + "type": "object (required)", "properties": { - [key: string]: { - "type": string, - "title": string, - "description": string?, - "default": any?, - "minimum": number?, - "maximum": number?, - "enum": string[]?, - "unit": string? + "[paramName]": { + "type": "string (JSON Schema type, required)", + "minimum": "number (optional) - For numeric types", + "maximum": "number (optional) - For numeric types", + "default": "any (optional) - Default value", + "description": "string (required) - Parameter description", + "enum": "string[] (optional) - For enum types" } }, - "required": string[] + "required": "string[] (required) - List of required parameters" }, "ros2": { - "messageType": string, - "topic": string?, - "service": string?, - "action": string?, + "messageType": "string (required) - ROS2 message type", + "topic": "string (optional) - Topic name for publishers", + "service": "string (optional) - Service name for service calls", + "action": "string (optional) - Action name for action calls", "payloadMapping": { - "type": "direct" | "transform", - "map": object?, - "transformFn": string? + "type": "string (enum: transform|static, required)", + "transformFn": "string (optional) - Transform function name", + "payload": "object (optional) - Static payload for static type" }, "qos": { - "reliability": "reliable" | "best_effort", - "durability": "volatile" | "transient_local", - "history": "keep_last" | "keep_all", - "depth": number? - }? + "reliability": "string (enum: reliable|best_effort, optional)", + "durability": "string (enum: volatile|transient_local, optional)", + "history": "string (enum: keep_last|keep_all, optional)", + "depth": "number (optional) - Queue depth for keep_last" + } } } ``` -## QoS Settings +## Action Categories -When specifying ROS2 QoS settings: +HRIStudio organizes actions into these standard categories: -- `reliability`: Message delivery guarantee - - `reliable`: Guaranteed delivery - - `best_effort`: Fast but may drop messages +- **movement**: Robot locomotion and positioning +- **interaction**: Communication and social behaviors +- **sensors**: Data collection and environmental sensing +- **logic**: Control flow and decision making -- `durability`: Message persistence - - `volatile`: Only delivered to active subscribers - - `transient_local`: Stored for late-joining subscribers +## Trust Levels -- `history`: Message queue behavior - - `keep_last`: Store up to N messages (specify with depth) - - `keep_all`: Store all messages +Plugins are classified by trust level: -## Example +- **official**: Maintained by HRIStudio team or robot manufacturer +- **verified**: Third-party plugins that have been reviewed and tested +- **community**: User-contributed plugins without formal verification -See the TurtleBot3 Burger plugin for a complete example implementation. \ No newline at end of file +## Validation Requirements + +### Required Fields +All plugins must include: +- Core properties (robotId, name, platform, version) +- HRIStudio metadata (pluginApiVersion, hriStudioVersion, trustLevel, category) +- At least one action definition +- Valid manufacturer information +- Asset thumbnailUrl + +### Naming Conventions +- `robotId`: lowercase with hyphens (e.g., "turtlebot3-burger") +- `action.id`: snake_case (e.g., "move_velocity") +- `category`: predefined enum values only + +### Version Requirements +- Plugin version must follow semantic versioning (semver) +- HRIStudio version must use semver range syntax (e.g., ">=0.1.0") + +## Example Implementation + +See `plugins/turtlebot3-burger.json` for a complete reference implementation demonstrating all schema features and best practices. \ No newline at end of file diff --git a/index.html b/index.html index dfcf48b..0353cd1 100644 --- a/index.html +++ b/index.html @@ -196,120 +196,100 @@
Plugin description goes here.
+
-
- Actions content will go here.