blanchon's picture
Update
51f51c3
#!/usr/bin/env node
/**
* Basic Producer Example - RobotHub TransportServer
*
* This example demonstrates:
* - Creating a room with workspace support
* - Connecting as a producer with workspace_id and room_id
* - Sending joint updates and state sync
* - Modern error handling and cleanup
* - Enhanced user feedback
*/
import { RoboticsProducer } from '../dist/robotics/index.js';
async function main() {
console.log('πŸ€– RobotHub TransportServer Basic Producer Example πŸ€–');
console.log('πŸ“‹ This example will create a room and demonstrate producer functionality\n');
// Create producer client
const producer = new RoboticsProducer('http://localhost:8000');
// Track connection state
let isConnected = false;
let workspaceId = null;
let roomId = null;
// Set up event callbacks with enhanced feedback
producer.onConnected(() => {
isConnected = true;
console.log('βœ… Producer connected successfully!');
});
producer.onDisconnected(() => {
isConnected = false;
console.log('πŸ‘‹ Producer disconnected');
});
producer.onError((error) => {
console.error('❌ Producer error:', error);
});
try {
// Create a room (returns both workspace_id and room_id)
console.log('πŸ“¦ Creating new room...');
const roomInfo = await producer.createRoom();
workspaceId = roomInfo.workspaceId;
roomId = roomInfo.roomId;
console.log(`βœ… Room created successfully!`);
console.log(` Workspace ID: ${workspaceId}`);
console.log(` Room ID: ${roomId}`);
// Connect as producer with both workspace_id and room_id
console.log('\nπŸ”Œ Connecting as producer...');
const success = await producer.connect(workspaceId, roomId, 'demo-producer');
if (!success) {
console.error('❌ Failed to connect as producer!');
console.log('πŸ’‘ Make sure the server is running on http://localhost:8000');
return;
}
console.log(`βœ… Connected to room successfully!`);
// Show detailed connection info
const info = producer.getConnectionInfo();
console.log('\nπŸ“Š Connection Details:');
console.log(` Workspace ID: ${info.workspace_id}`);
console.log(` Room ID: ${info.room_id}`);
console.log(` Role: ${info.role}`);
console.log(` Participant ID: ${info.participant_id}`);
console.log(` Server URL: ${info.base_url}`);
// Send initial state sync with enhanced feedback
console.log('\nπŸ“€ Sending initial robot state...');
const initialState = {
base: 0.0,
shoulder: 0.0,
elbow: 0.0,
wrist: 0.0,
gripper: 0.0
};
await producer.sendStateSync(initialState);
console.log('βœ… Initial state sent:');
Object.entries(initialState).forEach(([joint, value]) => {
console.log(` ${joint}: ${value}Β°`);
});
// Simulate realistic robot movement sequence
console.log('\nπŸ€– Simulating robot movement sequence...');
const movements = [
{
joints: [{ name: 'base', value: 30.0 }],
description: 'Rotate base to 30Β°',
delay: 1500
},
{
joints: [{ name: 'shoulder', value: 45.0 }],
description: 'Raise shoulder to 45Β°',
delay: 1200
},
{
joints: [{ name: 'elbow', value: -30.0 }],
description: 'Bend elbow to -30Β°',
delay: 1000
},
{
joints: [{ name: 'wrist', value: 15.0 }],
description: 'Turn wrist to 15Β°',
delay: 800
},
{
joints: [{ name: 'gripper', value: 0.8 }],
description: 'Close gripper to 80%',
delay: 600
},
];
for (let i = 0; i < movements.length; i++) {
const movement = movements[i];
console.log(`\n Step ${i + 1}: ${movement.description}`);
await producer.sendJointUpdate(movement.joints);
console.log(` βœ… Joint update sent: ${movement.joints.map(j => `${j.name}=${j.value}Β°`).join(', ')}`);
// Wait between movements for realistic timing
console.log(` ⏳ Waiting ${movement.delay}ms...`);
await new Promise(resolve => setTimeout(resolve, movement.delay));
}
// Send combined multi-joint update
console.log('\nπŸ“€ Sending combined multi-joint update...');
const combinedUpdate = [
{ name: 'base', value: 90.0 },
{ name: 'shoulder', value: 60.0 },
{ name: 'elbow', value: -45.0 }
];
await producer.sendJointUpdate(combinedUpdate);
console.log('βœ… Combined update sent:');
combinedUpdate.forEach(joint => {
console.log(` ${joint.name}: ${joint.value}Β°`);
});
// Send final state sync
console.log('\nπŸ“€ Sending final state synchronization...');
const finalState = {
base: 90.0,
shoulder: 60.0,
elbow: -45.0,
wrist: 15.0,
gripper: 0.8
};
await producer.sendStateSync(finalState);
console.log('βœ… Final state synchronized');
// Send heartbeat to verify connection
console.log('\nπŸ’“ Sending heartbeat...');
await producer.sendHeartbeat();
console.log('βœ… Heartbeat sent - connection healthy');
// Demonstrate emergency stop
console.log('\n🚨 Testing emergency stop functionality...');
await producer.sendEmergencyStop('Demo emergency stop - testing safety protocols');
console.log('βœ… Emergency stop sent successfully');
console.log('\nπŸŽ‰ Basic producer example completed successfully!');
// Display connection info for consumers
console.log('\nπŸ“‹ Connection Information for Consumers:');
console.log(` Workspace ID: ${workspaceId}`);
console.log(` Room ID: ${roomId}`);
console.log('\nπŸ’‘ You can use these IDs with the consumer example to connect to this room');
// Keep running to allow consumers to connect
console.log('\n⏳ Keeping producer alive for 30 seconds...');
console.log(' (Consumers can connect during this time using the IDs above)');
const keepAliveStart = Date.now();
const keepAliveDuration = 30000;
// Show countdown
const countdownInterval = setInterval(() => {
const elapsed = Date.now() - keepAliveStart;
const remaining = Math.max(0, keepAliveDuration - elapsed);
if (remaining > 0) {
process.stdout.write(`\r Time remaining: ${Math.ceil(remaining / 1000)}s `);
} else {
process.stdout.write('\r Time is up! \n');
clearInterval(countdownInterval);
}
}, 1000);
await new Promise(resolve => setTimeout(resolve, keepAliveDuration));
clearInterval(countdownInterval);
} catch (error) {
console.error('\n❌ Error occurred:', error.message);
if (error.stack) {
console.error('Stack trace:', error.stack);
}
} finally {
// Always disconnect and cleanup
console.log('\n🧹 Cleaning up resources...');
if (producer.isConnected()) {
console.log(' Disconnecting from room...');
await producer.disconnect();
console.log(' βœ… Disconnected successfully');
}
// Delete the room to clean up server resources
if (workspaceId && roomId) {
try {
console.log(' Deleting room...');
const deleted = await producer.deleteRoom(workspaceId, roomId);
if (deleted) {
console.log(` βœ… Room deleted: ${roomId}`);
} else {
console.log(` ⚠️ Room may have already been deleted: ${roomId}`);
}
} catch (error) {
console.log(` ⚠️ Could not delete room: ${error.message}`);
}
}
console.log('\nπŸ‘‹ Producer example finished. Goodbye!');
}
}
// Handle Ctrl+C gracefully
process.on('SIGINT', async () => {
console.log('\n\nπŸ›‘ Received interrupt signal (Ctrl+C)');
console.log('🧹 Shutting down gracefully...');
process.exit(0);
});
// Handle uncaught errors
process.on('unhandledRejection', (error) => {
console.error('\n❌ Unhandled promise rejection:', error);
process.exit(1);
});
main().catch((error) => {
console.error('\nπŸ’₯ Fatal error:', error);
process.exit(1);
});