File size: 8,156 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
 
 
 
 
 
 
 
 
 
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
#!/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);
});