18 KiB
Executable File
HRIStudio Plugin System Implementation Guide
Overview
This guide provides step-by-step instructions for implementing the HRIStudio Plugin System integration. You have access to two repositories:
- HRIStudio Main Repository - Contains the core platform
- Plugin Repository - Contains robot plugin definitions and web interface
Your task is to create a plugin store within HRIStudio and modify the plugin repository to ensure seamless integration.
Architecture Overview
HRIStudio Platform
├── Plugin Store (Frontend)
├── Plugin Manager (Backend)
├── Plugin Registry (Database)
└── ROS2 Integration Layer
└── Plugin Repository (External)
├── Repository Metadata
├── Plugin Definitions
└── Web Interface
Phase 1: Plugin Store Frontend Implementation
1.1 Create Plugin Store Page
Location: src/app/(dashboard)/plugins/page.tsx
Create a new page that displays available plugins from registered repositories.
// Key features to implement:
- Repository management (add/remove plugin repositories)
- Plugin browsing with categories and search
- Plugin details modal/page
- Installation status tracking
- Trust level indicators (Official, Verified, Community)
UI Requirements:
- Use existing HRIStudio design system (shadcn/ui)
- Follow established patterns from studies/experiments pages
- Include plugin cards with thumbnails, descriptions, and metadata
- Implement filtering by category, platform (ROS2), trust level
- Add search functionality
1.2 Plugin Repository Management
Location: src/components/plugins/repository-manager.tsx
// Features to implement:
- Add repository by URL
- Validate repository structure
- Display repository metadata (name, trust level, plugin count)
- Enable/disable repositories
- Remove repositories
- Repository status indicators (online, offline, error)
1.3 Plugin Installation Interface
Location: src/components/plugins/plugin-installer.tsx
// Features to implement:
- Plugin installation progress
- Dependency checking
- Version compatibility validation
- Installation success/error handling
- Plugin configuration interface
Phase 2: Plugin Manager Backend Implementation
2.1 Database Schema Extensions
Location: src/server/db/schema/plugins.ts
Add these tables to the existing schema:
-- Plugin repositories
CREATE TABLE plugin_repositories (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(255) NOT NULL,
url TEXT NOT NULL UNIQUE,
trust_level VARCHAR(20) NOT NULL CHECK (trust_level IN ('official', 'verified', 'community')),
enabled BOOLEAN DEFAULT true,
last_synced TIMESTAMP,
metadata JSONB DEFAULT '{}',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Installed plugins
CREATE TABLE installed_plugins (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
repository_id UUID NOT NULL REFERENCES plugin_repositories(id) ON DELETE CASCADE,
plugin_id VARCHAR(255) NOT NULL, -- robotId from plugin definition
name VARCHAR(255) NOT NULL,
version VARCHAR(50) NOT NULL,
configuration JSONB DEFAULT '{}',
enabled BOOLEAN DEFAULT true,
installed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
installed_by UUID NOT NULL REFERENCES users(id),
UNIQUE(repository_id, plugin_id)
);
-- Plugin usage in studies
CREATE TABLE study_plugins (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
study_id UUID NOT NULL REFERENCES studies(id) ON DELETE CASCADE,
installed_plugin_id UUID NOT NULL REFERENCES installed_plugins(id),
configuration JSONB DEFAULT '{}',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(study_id, installed_plugin_id)
);
2.2 tRPC Routes Implementation
Location: src/server/api/routers/plugins.ts
export const pluginsRouter = createTRPCRouter({
// Repository management
addRepository: protectedProcedure
.input(z.object({
url: z.string().url(),
name: z.string().optional()
}))
.mutation(async ({ ctx, input }) => {
// Validate repository structure
// Add to database
// Sync plugins
}),
listRepositories: protectedProcedure
.query(async ({ ctx }) => {
// Return user's accessible repositories
}),
syncRepository: protectedProcedure
.input(z.object({ repositoryId: z.string().uuid() }))
.mutation(async ({ ctx, input }) => {
// Fetch repository.json
// Update plugin definitions
// Handle errors
}),
// Plugin management
listAvailablePlugins: protectedProcedure
.input(z.object({
repositoryId: z.string().uuid().optional(),
search: z.string().optional(),
category: z.string().optional(),
platform: z.string().optional()
}))
.query(async ({ ctx, input }) => {
// Fetch plugins from repositories
// Apply filters
// Return plugin metadata
}),
installPlugin: protectedProcedure
.input(z.object({
repositoryId: z.string().uuid(),
pluginId: z.string(),
configuration: z.record(z.any()).optional()
}))
.mutation(async ({ ctx, input }) => {
// Validate plugin compatibility
// Install plugin
// Create plugin instance
}),
listInstalledPlugins: protectedProcedure
.query(async ({ ctx }) => {
// Return user's installed plugins
}),
getPluginActions: protectedProcedure
.input(z.object({ pluginId: z.string() }))
.query(async ({ ctx, input }) => {
// Return plugin action definitions
// For use in experiment designer
})
});
2.3 Plugin Registry Service
Location: src/lib/plugins/registry.ts
export class PluginRegistry {
// Fetch and validate repository metadata
async fetchRepository(url: string): Promise<RepositoryMetadata>
// Sync plugins from repository
async syncRepository(repositoryId: string): Promise<void>
// Load plugin definition
async loadPlugin(repositoryId: string, pluginId: string): Promise<PluginDefinition>
// Validate plugin compatibility
async validatePlugin(plugin: PluginDefinition): Promise<ValidationResult>
// Install plugin
async installPlugin(repositoryId: string, pluginId: string, config?: any): Promise<void>
}
Phase 3: Plugin Repository Modifications
3.1 Schema Enhancements
Location: Plugin Repository - docs/schema.md
Update the plugin schema to include HRIStudio-specific fields:
{
"robotId": "string (required)",
"name": "string (required)",
"description": "string (optional)",
"platform": "string (required)",
"version": "string (required)",
// Add these HRIStudio-specific fields:
"pluginApiVersion": "string (required) - Plugin API version",
"hriStudioVersion": "string (required) - Minimum HRIStudio version",
"trustLevel": "string (enum: official|verified|community)",
"category": "string (required) - Plugin category for UI organization",
// Enhanced action schema:
"actions": [
{
"id": "string (required) - Unique action identifier",
"name": "string (required) - Display name",
"description": "string (optional)",
"category": "string (required) - movement|interaction|sensors|logic",
"icon": "string (optional) - Lucide icon name",
"timeout": "number (optional) - Default timeout in milliseconds",
"retryable": "boolean (optional) - Can this action be retried on failure",
"parameterSchema": {
"type": "object",
"properties": {
// Zod-compatible parameter definitions
},
"required": ["array of required parameter names"]
},
"ros2": {
// Existing ROS2 configuration
}
}
]
}
3.2 TurtleBot3 Plugin Update
Location: Plugin Repository - plugins/turtlebot3-burger.json
Add the missing HRIStudio fields to the existing plugin:
{
"robotId": "turtlebot3-burger",
"name": "TurtleBot3 Burger",
"description": "A compact, affordable, programmable, ROS2-based mobile robot for education and research",
"platform": "ROS2",
"version": "2.0.0",
// Add these new fields:
"pluginApiVersion": "1.0",
"hriStudioVersion": ">=0.1.0",
"trustLevel": "official",
"category": "mobile-robot",
// Update actions with HRIStudio fields:
"actions": [
{
"id": "move_velocity", // Changed from actionId
"name": "Set Velocity", // Changed from title
"description": "Control the robot's linear and angular velocity",
"category": "movement", // New field
"icon": "navigation", // New field
"timeout": 30000, // New field
"retryable": true, // New field
"parameterSchema": {
// Convert existing parameters to HRIStudio format
"type": "object",
"properties": {
"linear": {
"type": "number",
"minimum": -0.22,
"maximum": 0.22,
"default": 0,
"description": "Forward/backward velocity in m/s"
},
"angular": {
"type": "number",
"minimum": -2.84,
"maximum": 2.84,
"default": 0,
"description": "Rotational velocity in rad/s"
}
},
"required": ["linear", "angular"]
},
// Keep existing ros2 config
"ros2": {
"messageType": "geometry_msgs/msg/Twist",
"topic": "/cmd_vel",
"payloadMapping": {
"type": "transform",
"transformFn": "transformToTwist"
},
"qos": {
"reliability": "reliable",
"durability": "volatile",
"history": "keep_last",
"depth": 1
}
}
}
]
}
3.3 Repository Metadata Update
Location: Plugin Repository - repository.json
Add HRIStudio-specific metadata:
{
"id": "hristudio-official",
"name": "HRIStudio Official Robot Plugins",
"description": "Official collection of robot plugins maintained by the HRIStudio team",
// Add API versioning:
"apiVersion": "1.0",
"pluginApiVersion": "1.0",
// Add plugin categories:
"categories": [
{
"id": "mobile-robots",
"name": "Mobile Robots",
"description": "Wheeled and tracked mobile platforms"
},
{
"id": "manipulators",
"name": "Manipulators",
"description": "Robotic arms and end effectors"
},
{
"id": "humanoids",
"name": "Humanoid Robots",
"description": "Human-like robots for social interaction"
},
{
"id": "drones",
"name": "Aerial Vehicles",
"description": "Quadcopters and fixed-wing UAVs"
}
],
// Keep existing fields...
"compatibility": {
"hristudio": {
"min": "0.1.0",
"recommended": "0.1.0"
},
"ros2": {
"distributions": ["humble", "iron"],
"recommended": "iron"
}
}
}
Phase 4: Integration Implementation
4.1 Experiment Designer Integration
Location: HRIStudio - src/components/experiments/designer/EnhancedBlockDesigner.tsx
Add plugin-based action loading to the block designer:
// In the block registry, load actions from installed plugins:
const loadPluginActions = async (studyId: string) => {
const installedPlugins = await api.plugins.getStudyPlugins.query({ studyId });
for (const plugin of installedPlugins) {
const actions = await api.plugins.getPluginActions.query({
pluginId: plugin.id
});
// Register actions in block registry
actions.forEach(action => {
blockRegistry.register({
id: `${plugin.id}.${action.id}`,
name: action.name,
description: action.description,
category: action.category,
icon: action.icon || 'bot',
shape: 'action',
color: getCategoryColor(action.category),
parameters: convertToZodSchema(action.parameterSchema),
metadata: {
pluginId: plugin.id,
robotId: plugin.robotId,
ros2Config: action.ros2
}
});
});
}
};
4.2 Trial Execution Integration
Location: HRIStudio - src/lib/plugins/execution.ts
Create plugin execution interface:
export class PluginExecutor {
private installedPlugins = new Map<string, InstalledPlugin>();
private rosConnections = new Map<string, RosConnection>();
async executePluginAction(
pluginId: string,
actionId: string,
parameters: Record<string, any>
): Promise<ActionResult> {
const plugin = this.installedPlugins.get(pluginId);
if (!plugin) {
throw new Error(`Plugin ${pluginId} not found`);
}
const action = plugin.actions.find(a => a.id === actionId);
if (!action) {
throw new Error(`Action ${actionId} not found in plugin ${pluginId}`);
}
// Validate parameters against schema
const validation = this.validateParameters(action.parameterSchema, parameters);
if (!validation.success) {
throw new Error(`Parameter validation failed: ${validation.error}`);
}
// Execute via ROS2 if configured
if (action.ros2) {
return this.executeRos2Action(plugin, action, parameters);
}
// Execute via REST API if configured
if (action.rest) {
return this.executeRestAction(plugin, action, parameters);
}
throw new Error(`No execution method configured for action ${actionId}`);
}
private async executeRos2Action(
plugin: InstalledPlugin,
action: PluginAction,
parameters: Record<string, any>
): Promise<ActionResult> {
const connection = this.getRosConnection(plugin.id);
// Transform parameters according to payload mapping
const payload = this.transformPayload(action.ros2.payloadMapping, parameters);
// Publish to topic or call service
if (action.ros2.topic) {
return this.publishToTopic(connection, action.ros2, payload);
} else if (action.ros2.service) {
return this.callService(connection, action.ros2, payload);
} else if (action.ros2.action) {
return this.executeAction(connection, action.ros2, payload);
}
throw new Error('No ROS2 communication method specified');
}
}
4.3 Plugin Store Navigation
Location: HRIStudio - src/components/layout/navigation/SidebarNav.tsx
Add plugin store to the navigation:
const navigationItems = [
{
title: "Dashboard",
href: "/",
icon: LayoutDashboard,
description: "Overview and quick actions"
},
{
title: "Studies",
href: "/studies",
icon: FolderOpen,
description: "Research projects and team collaboration"
},
{
title: "Experiments",
href: "/experiments",
icon: FlaskConical,
description: "Protocol design and validation"
},
{
title: "Participants",
href: "/participants",
icon: Users,
description: "Participant management and consent"
},
{
title: "Trials",
href: "/trials",
icon: Play,
description: "Experiment execution and monitoring"
},
// Add plugin store:
{
title: "Plugin Store",
href: "/plugins",
icon: Package,
description: "Robot plugins and integrations"
},
{
title: "Admin",
href: "/admin",
icon: Settings,
description: "System administration",
roles: ["administrator"]
}
];
4.4 Plugin Configuration in Studies
Location: HRIStudio - src/app/(dashboard)/studies/[studyId]/settings/page.tsx
Add plugin configuration to study settings:
const StudySettingsPage = ({ studyId }: { studyId: string }) => {
const installedPlugins = api.plugins.listInstalledPlugins.useQuery();
const studyPlugins = api.plugins.getStudyPlugins.useQuery({ studyId });
return (
<PageLayout title="Study Settings">
<Tabs defaultValue="general">
<TabsList>
<TabsTrigger value="general">General</TabsTrigger>
<TabsTrigger value="team">Team</TabsTrigger>
<TabsTrigger value="plugins">Robot Plugins</TabsTrigger>
<TabsTrigger value="permissions">Permissions</TabsTrigger>
</TabsList>
<TabsContent value="plugins">
<Card>
<CardHeader>
<CardTitle>Robot Plugins</CardTitle>
<CardDescription>
Configure which robot plugins are available for this study
</CardDescription>
</CardHeader>
<CardContent>
<PluginConfiguration
studyId={studyId}
availablePlugins={installedPlugins.data || []}
enabledPlugins={studyPlugins.data || []}
/>
</CardContent>
</Card>
</TabsContent>
</Tabs>
</PageLayout>
);
};
Phase 5: Testing and Validation
5.1 Plugin Repository Testing
Create test scripts to validate:
- Repository structure and schema compliance
- Plugin definition validation
- Web interface functionality
- API endpoint responses
5.2 HRIStudio Integration Testing
Test the complete flow:
- Add plugin repository to HRIStudio
- Install a plugin from the repository
- Configure plugin for a study
- Use plugin actions in experiment designer
- Execute plugin actions during trial
5.3 End-to-End Testing
Create automated tests that:
- Validate plugin installation process
- Test ROS2 communication via rosbridge
- Verify parameter validation and transformation
- Test error handling and recovery
Deployment Checklist
Plugin Repository
- Update plugin schema documentation
- Enhance existing plugin definitions
- Test web interface with new schema
- Deploy to GitHub Pages or hosting platform
- Validate HTTPS access and CORS headers
HRIStudio Platform
- Implement database schema migrations
- Create plugin store frontend pages
- Implement plugin management tRPC routes
- Integrate plugins with experiment designer
- Add plugin execution to trial system
- Update navigation to include plugin store
- Add plugin configuration to study settings
Integration Testing
- Test repository discovery and syncing
- Validate plugin installation workflow
- Test plugin action execution
- Verify ROS2 integration works end-to-end
- Test error handling and user feedback
This implementation will create a complete plugin ecosystem for HRIStudio, allowing researchers to easily discover, install, and use robot plugins in their studies.