Node.js Plugin Development
The Node.js implementation provides an event-driven, asynchronous interface to the DroneEngage DataBus. It’s ideal for web-integrated solutions, real-time applications, and rapid prototyping.
Features
Event-driven Architecture - Built on Node.js EventEmitter
Async/Await Support - Modern JavaScript patterns
NPM Package - Easy installation and dependency management
Complete API Reference - Comprehensive documentation
Cross-platform - Works on Windows, macOS, and Linux
Real-time Communication - Low-latency UDP messaging
Binary Data Support - Handle images, files, and streams
Message Filtering - Subscribe to specific message types
Quick Start
Installation
cd nodejs
npm install
Basic Usage
node client.js --help # Show comprehensive help
node client.js MyModule 60000 61234 # Run with all arguments
node client.js MyModule # Uses default ports
Architecture
The Node.js library follows the same layered architecture as other implementations:
Library Components
┌─────────────────────┐
│ Your Application │
│ (client.js) │
└──────────┬──────────┘
│
↓
┌─────────────────────┐
│ CFacade_Base │ ← High-level API
│ (de_facade_base.js)│
└──────────┬──────────┘
│
↓
┌─────────────────────┐
│ CModule │ ← Module management & routing
│ (de_module.js) │ ← EventEmitter for async ops
└──────────┬──────────┘
│
↓
┌─────────────────────┐
│ CUDPClient │ ← UDP transport with chunking
│ (udpClient.js) │ ← EventEmitter for data events
└──────────┬──────────┘
│
↓
┌─────────────────────┐
│ de_comm │ ← DroneEngage Communicator
│ (Port 60000) │
└─────────────────────┘
Key Classes
CModule (EventEmitter)
Module registration and management
Message routing and filtering
Event emission for async operations
CUDPClient (EventEmitter)
UDP communication with chunking
Automatic message reassembly
Periodic module identification
CFacade_Base
Simplified API for response generation
Command handling and validation
Error handling and logging
AndruavMessageParserBase
JSON and binary message parsing
Command dispatch and routing
Message validation and filtering
Usage Examples
Basic Module Setup
const { CModule, CFacade_Base } = require('./de_module');
const { CUDPClient } = require('./udpClient');
class MyFacade extends CFacade_Base {
handleCommand(request) {
return {
status: 'success',
data: { message: 'Command processed' }
};
}
}
// Create module instance
const module = CModule.getInstance();
const facade = new MyFacade();
// Define module properties
module.defineModule('MyModule', '1.0.0', facade);
// Start communication
module.start();
Event-driven Message Handling
const module = CModule.getInstance();
// Listen for incoming messages
module.on('message', (message) => {
console.log('Received:', message);
});
// Listen for connection events
module.on('connected', () => {
console.log('Connected to DroneEngage communicator');
});
module.on('disconnected', () => {
console.log('Disconnected from communicator');
});
Binary Data Transmission
const fs = require('fs');
// Read image file
const imageData = fs.readFileSync('image.jpg');
// Send binary data
module.sendBinaryData('target_module', imageData, {
type: 'image',
format: 'jpeg'
});
Message Filtering
// Filter messages by type
module.setMessageFilter(['telemetry', 'command']);
// Custom message handler
module.on('telemetry', (data) => {
processTelemetryData(data);
});
API Reference
CModule Class
class CModule extends EventEmitter {
// Singleton access
static getInstance();
// Module management
defineModule(name, version, facade);
start();
stop();
// Messaging
sendMessage(target, message);
sendBinaryData(target, data, metadata);
// Configuration
setMessageFilter(filters);
setJsonId(jsonId);
// Events
on('message', callback);
on('connected', callback);
on('disconnected', callback);
on('error', callback);
}
CUDPClient Class
class CUDPClient extends EventEmitter {
constructor(callback);
// Network operations
initialize(port);
sendMessage(data);
close();
// Events
on('data', callback);
on('error', callback);
on('listening', callback);
}
CFacade_Base Class
class CFacade_Base {
// Abstract method to implement
handleCommand(request);
// Helper methods
generateResponse(status, data);
generateError(message, code);
validateRequest(request);
}
Package Configuration
package.json
{
"name": "droneengage-databus-nodejs",
"version": "1.0.0",
"description": "Node.js client for DroneEngage DataBus",
"main": "client.js",
"dependencies": {},
"engines": {
"node": ">=12.0.0"
},
"scripts": {
"start": "node client.js",
"test": "node test.js"
}
}
Code Style
Variables: camelCase (e.g.,
moduleName,messageData)Classes: PascalCase (e.g.,
CModule,CUDPClient)Constants: UPPER_SNAKE_CASE (e.g.,
DEFAULT_PORT,MAX_PACKET_SIZE)Files: snake_case (e.g.,
de_module.js,udp_client.js)Functions: camelCase (e.g.,
sendMessage,handleCommand)
Error Handling
Comprehensive error handling with proper event emission:
try {
await module.sendMessage(target, message);
} catch (error) {
console.error('Failed to send message:', error);
module.emit('error', error);
}
// Handle network errors
udpClient.on('error', (error) => {
console.error('UDP Client error:', error);
// Implement reconnection logic
});
Performance Considerations
Memory Management
Stream processing for large binary data
Buffer pooling for frequent allocations
Garbage collection optimization
Event Loop Optimization
Non-blocking I/O operations
Efficient event handling
Proper error propagation
Network Optimization
UDP buffer tuning
Chunk size optimization
Connection pooling
Debugging and Logging
Logging
Built-in colored console output:
const colors = require('./colors');
console.log(colors.info('Info message'));
console.log(colors.error('Error message'));
console.log(colors.success('Success message'));
Debug Mode
Enable debug output:
DEBUG=* node client.js MyModule
Integration Examples
Sensor Module Integration
const CModule = require('./de_module');
const { CMyFacade } = require('./my_facade');
const { TYPE_AndruavMessage_ServoChannel, NOTIFICATION_TYPE_INFO } = require('./messages');
class SensorFacade extends CMyFacade {
constructor() {
super();
this.sensorData = {
temperature: 0,
humidity: 0,
pressure: 0
};
}
readSensorData() {
// Simulate sensor reading
this.sensorData.temperature = Math.random() * 30 + 20;
this.sensorData.humidity = Math.random() * 40 + 30;
this.sensorData.pressure = Math.random() * 50 + 1000;
return this.sensorData;
}
sendTelemetry() {
const data = this.readSensorData();
this.sendJMSG('', {
sensor_type: 'environmental',
data: data,
timestamp: Date.now()
}, TYPE_AndruavMessage_ServoChannel, true);
}
}
// Module setup
const cModule = new CModule();
const sensorFacade = new SensorFacade();
sensorFacade.setModule(cModule);
// Define module
cModule.defineModule(
'gen', // MODULE_CLASS_GENERIC
'EnvironmentalSensor',
'123456789012',
'1.0.0',
[TYPE_AndruavMessage_ServoChannel]
);
// Add module features
cModule.addModuleFeatures('T'); // MODULE_FEATURE_SENDING_TELEMETRY
cModule.addModuleFeatures('R'); // MODULE_FEATURE_RECEIVING_TELEMETRY
// Initialize communication
cModule.init('0.0.0.0', 60000, '0.0.0.0', 61234, 8192);
// Start sensor data transmission
setInterval(() => {
sensorFacade.sendTelemetry();
}, 1000);
console.log('Environmental Sensor Module RUNNING');
Servo Control Module
const CModule = require('./de_module');
const { CMyFacade } = require('./my_facade');
const { TYPE_AndruavMessage_ServoChannel, TYPE_AndruavMessage_RemoteExecute } = require('./messages');
class ServoFacade extends CMyFacade {
constructor() {
super();
this.servoPositions = new Array(8).fill(1500); // 8 channels, center position
}
setServoChannel(channel, position) {
if (channel >= 0 && channel < 8 && position >= 1000 && position <= 2000) {
this.servoPositions[channel] = position;
// Send servo command to drone
this.sendJMSG('', {
command: 'servo',
channel: channel,
position: position
}, TYPE_AndruavMessage_ServoChannel, true);
console.log(`Servo Channel ${channel} set to ${position}μs`);
}
}
handleCommand(request) {
if (request.command === 'set_servo') {
this.setServoChannel(request.channel, request.position);
return { status: 'success', message: 'Servo command executed' };
}
return { status: 'error', message: 'Unknown command' };
}
}
// Module setup
const cModule = new CModule();
const servoFacade = new ServoFacade();
servoFacade.setModule(cModule);
// Define module
cModule.defineModule(
'gen', // MODULE_CLASS_GENERIC
'ServoController',
'123456789013',
'1.0.0',
[TYPE_AndruavMessage_ServoChannel, TYPE_AndruavMessage_RemoteExecute]
);
// Add module features
cModule.addModuleFeatures('T'); // MODULE_FEATURE_SENDING_TELEMETRY
cModule.addModuleFeatures('R'); // MODULE_FEATURE_RECEIVING_TELEMETRY
// Initialize communication
cModule.init('0.0.0.0', 60000, '0.0.0.0', 61235, 8192);
// Example: Control servos based on commands
cModule.on('message', (message) => {
if (message.type === TYPE_AndruavMessage_RemoteExecute) {
servoFacade.handleCommand(message.data);
}
});
console.log('Servo Control Module RUNNING');
See Also
https://nodejs.org/api/events.html - Node.js EventEmitter documentation