Spaces:
Sleeping
Sleeping
File size: 9,667 Bytes
8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 8aedc84 51f51c3 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 |
#!/usr/bin/env node
/**
* Basic Consumer Example - RobotHub TransportServer
*
* This example demonstrates:
* - Connecting to an existing room as a consumer using workspace_id and room_id
* - Receiving joint updates and state sync
* - Setting up callbacks with enhanced feedback
* - Getting current state and connection info
* - Modern error handling and resource management
*/
import { RoboticsConsumer } from '../dist/robotics/index.js';
import { createInterface } from 'readline';
async function promptUser(question) {
const rl = createInterface({
input: process.stdin,
output: process.stdout
});
return new Promise((resolve) => {
rl.question(question, (answer) => {
rl.close();
resolve(answer.trim());
});
});
}
async function main() {
console.log('π€ RobotHub TransportServer Basic Consumer Example π€');
console.log('π This example will connect to an existing room and listen for updates\n');
// Get connection details from user
console.log('π Please provide the connection details:');
const workspaceId = await promptUser('Enter workspace ID: ');
if (!workspaceId) {
console.error('β Workspace ID is required!');
console.log('π‘ You can get this from the producer example output');
return;
}
const roomId = await promptUser('Enter room ID: ');
if (!roomId) {
console.error('β Room ID is required!');
console.log('π‘ You can get this from the producer example output');
return;
}
console.log('\nβ
Connection details received:');
console.log(` Workspace ID: ${workspaceId}`);
console.log(` Room ID: ${roomId}`);
// Create consumer client
const consumer = new RoboticsConsumer('http://localhost:8000');
// Track received data and connection state
let updateCount = 0;
let stateCount = 0;
let isConnected = false;
const receivedUpdates = [];
const receivedStates = [];
// Set up callbacks with enhanced feedback
consumer.onJointUpdate((joints) => {
updateCount++;
console.log(`\nπ₯ [${updateCount}] Joint update received: ${joints.length} joint(s)`);
// Show joint details with better formatting
joints.forEach(joint => {
const speedInfo = joint.speed ? ` (speed: ${joint.speed})` : '';
console.log(` π§ ${joint.name}: ${joint.value}Β°${speedInfo}`);
});
receivedUpdates.push({ timestamp: new Date(), joints });
});
consumer.onStateSync((state) => {
stateCount++;
console.log(`\nπ [${stateCount}] State sync received: ${Object.keys(state).length} joint(s)`);
// Show state details with better formatting
if (Object.keys(state).length > 0) {
Object.entries(state).forEach(([name, value]) => {
console.log(` βοΈ ${name}: ${value}Β°`);
});
} else {
console.log(' (No joint data)');
}
receivedStates.push({ timestamp: new Date(), state });
});
consumer.onError((errorMsg) => {
console.error('\nβ Consumer error:', errorMsg);
});
consumer.onConnected(() => {
isConnected = true;
console.log('\nβ
Consumer connected successfully!');
});
consumer.onDisconnected(() => {
isConnected = false;
console.log('\nπ Consumer disconnected');
});
try {
// Connect to the room with workspace_id and room_id
console.log(`\nπ Connecting to room...`);
console.log(` Workspace ID: ${workspaceId}`);
console.log(` Room ID: ${roomId}`);
const success = await consumer.connect(workspaceId, roomId, 'demo-consumer');
if (!success) {
console.error('\nβ Failed to connect to room!');
console.log('π‘ Possible reasons:');
console.log(' - Room does not exist');
console.log(' - Server is not running on http://localhost:8000');
console.log(' - Incorrect workspace_id or room_id');
console.log(' - Network connectivity issues');
return;
}
console.log(`\nβ
Successfully connected to room!`);
// Show detailed connection info
const info = consumer.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}`);
// Get initial state with enhanced feedback
console.log('\nπ Retrieving initial robot state...');
try {
const initialState = await consumer.getStateSyncAsync();
console.log(`β
Initial state retrieved: ${Object.keys(initialState).length} joint(s)`);
if (Object.keys(initialState).length > 0) {
console.log(' Current joint positions:');
Object.entries(initialState).forEach(([name, value]) => {
console.log(` βοΈ ${name}: ${value}Β°`);
});
} else {
console.log(' π No joints configured yet (empty state)');
console.log(' π‘ Producer can send initial state to populate this');
}
} catch (error) {
console.log(`β οΈ Could not retrieve initial state: ${error.message}`);
}
// Listen for updates with enhanced status reporting
console.log('\nπ Listening for real-time updates...');
console.log(' Duration: 60 seconds');
console.log(' π‘ Run the producer example to send updates to this consumer');
const startTime = Date.now();
const duration = 60000; // 60 seconds
// Show periodic status with detailed information
const statusInterval = setInterval(() => {
const elapsed = Date.now() - startTime;
const remaining = Math.max(0, duration - elapsed);
if (remaining > 0) {
console.log(`\nπ Status Update (${Math.floor(remaining / 1000)}s remaining):`);
console.log(` Connection: ${isConnected ? 'β
Connected' : 'β Disconnected'}`);
console.log(` Joint updates received: ${updateCount}`);
console.log(` State syncs received: ${stateCount}`);
console.log(` Total messages: ${updateCount + stateCount}`);
if (receivedUpdates.length > 0) {
const lastUpdate = receivedUpdates[receivedUpdates.length - 1];
const timeSinceLastUpdate = Date.now() - lastUpdate.timestamp.getTime();
console.log(` Last update: ${Math.floor(timeSinceLastUpdate / 1000)}s ago`);
}
}
}, 15000); // Every 15 seconds
// Wait for the duration
await new Promise(resolve => setTimeout(resolve, duration));
clearInterval(statusInterval);
// Show comprehensive final summary
console.log('\nπ Session Summary:');
console.log(` Duration: 60 seconds`);
console.log(` Total joint updates: ${updateCount}`);
console.log(` Total state syncs: ${stateCount}`);
console.log(` Total messages: ${updateCount + stateCount}`);
if (updateCount > 0) {
console.log(` Average update rate: ${(updateCount / 60).toFixed(2)} updates/second`);
}
// Get final state for comparison
console.log('\nπ Retrieving final robot state...');
try {
const finalState = await consumer.getStateSyncAsync();
console.log(`β
Final state retrieved: ${Object.keys(finalState).length} joint(s)`);
if (Object.keys(finalState).length > 0) {
console.log(' Final joint positions:');
Object.entries(finalState).forEach(([name, value]) => {
console.log(` βοΈ ${name}: ${value}Β°`);
});
} else {
console.log(' π Final state is empty');
}
} catch (error) {
console.log(`β οΈ Could not retrieve final state: ${error.message}`);
}
// Show data history if available
if (receivedUpdates.length > 0) {
console.log('\nπ Update History (last 5):');
const recentUpdates = receivedUpdates.slice(-5);
recentUpdates.forEach((update, index) => {
const timeStr = update.timestamp.toLocaleTimeString();
console.log(` ${index + 1}. [${timeStr}] ${update.joints.length} joint(s) updated`);
});
}
if (receivedStates.length > 0) {
console.log('\nπ State Sync History (last 3):');
const recentStates = receivedStates.slice(-3);
recentStates.forEach((stateUpdate, index) => {
const timeStr = stateUpdate.timestamp.toLocaleTimeString();
const jointCount = Object.keys(stateUpdate.state).length;
console.log(` ${index + 1}. [${timeStr}] ${jointCount} joint(s) synchronized`);
});
}
console.log('\nπ Consumer example completed successfully!');
} 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 (consumer.isConnected()) {
console.log(' Disconnecting from room...');
await consumer.disconnect();
console.log(' β
Disconnected successfully');
}
console.log('\nπ Consumer 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);
}); |