551 lines
18 KiB
JavaScript
551 lines
18 KiB
JavaScript
const logger = require('../utils/logger');
|
|
const database = require('../config/database');
|
|
const redis = require('../config/redis');
|
|
const healingService = require('./healingService');
|
|
const alertService = require('./alertService');
|
|
|
|
class AIAgentService {
|
|
constructor() {
|
|
this.learningRate = parseFloat(process.env.AI_LEARNING_RATE) || 0.1;
|
|
this.anomalyThreshold = parseFloat(process.env.AI_THRESHOLD_ANOMALY) || 0.8;
|
|
this.healingEnabled = process.env.AI_HEALING_ENABLED === 'true';
|
|
this.deviceModels = new Map();
|
|
this.isInitialized = false;
|
|
}
|
|
|
|
async initialize() {
|
|
try {
|
|
await this.loadDeviceModels();
|
|
await this.setupPeriodicAnalysis();
|
|
this.isInitialized = true;
|
|
logger.info('AI Agent service initialized successfully');
|
|
} catch (error) {
|
|
logger.error('Failed to initialize AI Agent service:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async loadDeviceModels() {
|
|
try {
|
|
// Load device-specific AI models from database
|
|
const devices = await database.query('SELECT id, device_type, ai_model_config FROM devices WHERE ai_model_config IS NOT NULL');
|
|
|
|
for (const device of devices) {
|
|
if (device.ai_model_config) {
|
|
const config = JSON.parse(device.ai_model_config);
|
|
this.deviceModels.set(device.id, {
|
|
deviceType: device.device_type,
|
|
config: config,
|
|
lastUpdated: new Date()
|
|
});
|
|
}
|
|
}
|
|
|
|
logger.info(`Loaded AI models for ${devices.length} devices`);
|
|
} catch (error) {
|
|
logger.error('Failed to load device models:', error);
|
|
}
|
|
}
|
|
|
|
async setupPeriodicAnalysis() {
|
|
// Run periodic analysis every 5 minutes
|
|
setInterval(async () => {
|
|
await this.runPeriodicAnalysis();
|
|
}, 5 * 60 * 1000);
|
|
}
|
|
|
|
async processDeviceData(deviceId, data, timestamp) {
|
|
try {
|
|
const startTime = Date.now();
|
|
|
|
// Analyze data for anomalies
|
|
const anomalyScore = await this.detectAnomalies(deviceId, data);
|
|
|
|
// Generate insights
|
|
const insights = await this.generateInsights(deviceId, data, timestamp);
|
|
|
|
// Check for optimization opportunities
|
|
const optimizations = await this.findOptimizations(deviceId, data);
|
|
|
|
// Update device model
|
|
await this.updateDeviceModel(deviceId, data, anomalyScore);
|
|
|
|
// Generate suggestions
|
|
const suggestions = await this.generateSuggestions(deviceId, data, insights, optimizations);
|
|
|
|
// Store analysis results
|
|
await this.storeAnalysisResults(deviceId, {
|
|
anomalyScore,
|
|
insights,
|
|
optimizations,
|
|
suggestions,
|
|
timestamp
|
|
});
|
|
|
|
const processingTime = Date.now() - startTime;
|
|
logger.logAIAction('device_data_analysis', {
|
|
deviceId,
|
|
anomalyScore,
|
|
insightsCount: insights.length,
|
|
optimizationsCount: optimizations.length,
|
|
suggestionsCount: suggestions.length
|
|
}, anomalyScore);
|
|
|
|
logger.logPerformance('ai_data_processing', processingTime, {
|
|
deviceId,
|
|
dataSize: JSON.stringify(data).length
|
|
});
|
|
|
|
} catch (error) {
|
|
logger.error('Error processing device data with AI:', error);
|
|
}
|
|
}
|
|
|
|
async detectAnomalies(deviceId, data) {
|
|
try {
|
|
// Get historical data for comparison
|
|
const historicalData = await this.getHistoricalData(deviceId, 100);
|
|
|
|
if (historicalData.length < 10) {
|
|
return 0.5; // Neutral score if insufficient data
|
|
}
|
|
|
|
// Calculate statistical measures
|
|
const values = historicalData.map(d => this.extractNumericValues(d.raw_data)).flat();
|
|
const mean = values.reduce((a, b) => a + b, 0) / values.length;
|
|
const variance = values.reduce((a, b) => a + Math.pow(b - mean, 2), 0) / values.length;
|
|
const stdDev = Math.sqrt(variance);
|
|
|
|
// Calculate anomaly score for current data
|
|
const currentValues = this.extractNumericValues(data);
|
|
const anomalyScores = currentValues.map(value => {
|
|
const zScore = Math.abs((value - mean) / stdDev);
|
|
return Math.min(zScore / 3, 1); // Normalize to 0-1
|
|
});
|
|
|
|
const avgAnomalyScore = anomalyScores.reduce((a, b) => a + b, 0) / anomalyScores.length;
|
|
|
|
// Trigger alert if anomaly score is high
|
|
if (avgAnomalyScore > this.anomalyThreshold) {
|
|
await alertService.createAlert({
|
|
deviceId,
|
|
type: 'anomaly_detected',
|
|
severity: avgAnomalyScore > 0.9 ? 'critical' : 'warning',
|
|
message: `Anomaly detected in device ${deviceId} with score ${avgAnomalyScore.toFixed(3)}`,
|
|
data: { anomalyScore: avgAnomalyScore, values: currentValues }
|
|
});
|
|
}
|
|
|
|
return avgAnomalyScore;
|
|
} catch (error) {
|
|
logger.error('Error detecting anomalies:', error);
|
|
return 0.5;
|
|
}
|
|
}
|
|
|
|
async generateInsights(deviceId, data, timestamp) {
|
|
try {
|
|
const insights = [];
|
|
|
|
// Analyze data patterns
|
|
const patterns = await this.analyzePatterns(deviceId, data);
|
|
if (patterns.length > 0) {
|
|
insights.push({
|
|
type: 'pattern_detected',
|
|
description: `Detected ${patterns.length} new patterns in device behavior`,
|
|
patterns: patterns
|
|
});
|
|
}
|
|
|
|
// Analyze performance trends
|
|
const trends = await this.analyzeTrends(deviceId, data);
|
|
if (trends.length > 0) {
|
|
insights.push({
|
|
type: 'trend_detected',
|
|
description: `Identified ${trends.length} performance trends`,
|
|
trends: trends
|
|
});
|
|
}
|
|
|
|
// Analyze efficiency
|
|
const efficiency = await this.analyzeEfficiency(deviceId, data);
|
|
if (efficiency.score < 0.7) {
|
|
insights.push({
|
|
type: 'efficiency_issue',
|
|
description: `Device efficiency is ${(efficiency.score * 100).toFixed(1)}%`,
|
|
recommendations: efficiency.recommendations
|
|
});
|
|
}
|
|
|
|
return insights;
|
|
} catch (error) {
|
|
logger.error('Error generating insights:', error);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
async findOptimizations(deviceId, data) {
|
|
try {
|
|
const optimizations = [];
|
|
|
|
// Check for energy optimization opportunities
|
|
const energyOpt = await this.checkEnergyOptimization(deviceId, data);
|
|
if (energyOpt.opportunity) {
|
|
optimizations.push({
|
|
type: 'energy_optimization',
|
|
description: energyOpt.description,
|
|
potentialSavings: energyOpt.savings,
|
|
action: energyOpt.action
|
|
});
|
|
}
|
|
|
|
// Check for performance optimization
|
|
const perfOpt = await this.checkPerformanceOptimization(deviceId, data);
|
|
if (perfOpt.opportunity) {
|
|
optimizations.push({
|
|
type: 'performance_optimization',
|
|
description: perfOpt.description,
|
|
improvement: perfOpt.improvement,
|
|
action: perfOpt.action
|
|
});
|
|
}
|
|
|
|
// Check for maintenance optimization
|
|
const maintOpt = await this.checkMaintenanceOptimization(deviceId, data);
|
|
if (maintOpt.opportunity) {
|
|
optimizations.push({
|
|
type: 'maintenance_optimization',
|
|
description: maintOpt.description,
|
|
benefit: maintOpt.benefit,
|
|
action: maintOpt.action
|
|
});
|
|
}
|
|
|
|
return optimizations;
|
|
} catch (error) {
|
|
logger.error('Error finding optimizations:', error);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
async generateSuggestions(deviceId, data, insights, optimizations) {
|
|
try {
|
|
const suggestions = [];
|
|
|
|
// Generate suggestions based on insights
|
|
for (const insight of insights) {
|
|
const suggestion = await this.createSuggestionFromInsight(deviceId, insight);
|
|
if (suggestion) {
|
|
suggestions.push(suggestion);
|
|
}
|
|
}
|
|
|
|
// Generate suggestions based on optimizations
|
|
for (const optimization of optimizations) {
|
|
const suggestion = await this.createSuggestionFromOptimization(deviceId, optimization);
|
|
if (suggestion) {
|
|
suggestions.push(suggestion);
|
|
}
|
|
}
|
|
|
|
// Generate proactive suggestions
|
|
const proactiveSuggestions = await this.generateProactiveSuggestions(deviceId, data);
|
|
suggestions.push(...proactiveSuggestions);
|
|
|
|
// Store suggestions in database
|
|
for (const suggestion of suggestions) {
|
|
await this.storeSuggestion(deviceId, suggestion);
|
|
}
|
|
|
|
return suggestions;
|
|
} catch (error) {
|
|
logger.error('Error generating suggestions:', error);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
async createSuggestionFromInsight(deviceId, insight) {
|
|
try {
|
|
const suggestion = {
|
|
deviceId,
|
|
type: 'ai_insight',
|
|
title: `Action based on ${insight.type}`,
|
|
description: insight.description,
|
|
priority: insight.type === 'efficiency_issue' ? 'high' : 'medium',
|
|
category: 'optimization',
|
|
confidence: 0.8,
|
|
actions: [],
|
|
created_at: new Date()
|
|
};
|
|
|
|
// Add specific actions based on insight type
|
|
switch (insight.type) {
|
|
case 'efficiency_issue':
|
|
suggestion.actions = insight.recommendations;
|
|
break;
|
|
case 'pattern_detected':
|
|
suggestion.actions = ['Monitor pattern evolution', 'Adjust thresholds if needed'];
|
|
break;
|
|
case 'trend_detected':
|
|
suggestion.actions = ['Review trend direction', 'Plan preventive measures'];
|
|
break;
|
|
}
|
|
|
|
return suggestion;
|
|
} catch (error) {
|
|
logger.error('Error creating suggestion from insight:', error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
async createSuggestionFromOptimization(deviceId, optimization) {
|
|
try {
|
|
const suggestion = {
|
|
deviceId,
|
|
type: 'ai_optimization',
|
|
title: optimization.description,
|
|
description: `AI detected optimization opportunity: ${optimization.description}`,
|
|
priority: 'medium',
|
|
category: 'optimization',
|
|
confidence: 0.7,
|
|
actions: [optimization.action],
|
|
metadata: {
|
|
optimizationType: optimization.type,
|
|
potentialBenefit: optimization.potentialSavings || optimization.improvement || optimization.benefit
|
|
},
|
|
created_at: new Date()
|
|
};
|
|
|
|
return suggestion;
|
|
} catch (error) {
|
|
logger.error('Error creating suggestion from optimization:', error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
async generateProactiveSuggestions(deviceId, data) {
|
|
try {
|
|
const suggestions = [];
|
|
|
|
// Check device health
|
|
const health = await this.assessDeviceHealth(deviceId, data);
|
|
if (health.score < 0.6) {
|
|
suggestions.push({
|
|
deviceId,
|
|
type: 'health_warning',
|
|
title: 'Device Health Alert',
|
|
description: `Device health is at ${(health.score * 100).toFixed(1)}%. Consider maintenance.`,
|
|
priority: 'high',
|
|
category: 'maintenance',
|
|
confidence: 0.9,
|
|
actions: ['Schedule maintenance', 'Check device logs', 'Review recent alerts'],
|
|
created_at: new Date()
|
|
});
|
|
}
|
|
|
|
// Check for predictive maintenance
|
|
const maintenance = await this.predictMaintenance(deviceId, data);
|
|
if (maintenance.needed) {
|
|
suggestions.push({
|
|
deviceId,
|
|
type: 'predictive_maintenance',
|
|
title: 'Predictive Maintenance Recommended',
|
|
description: `Maintenance recommended within ${maintenance.timeframe} days`,
|
|
priority: 'medium',
|
|
category: 'maintenance',
|
|
confidence: maintenance.confidence,
|
|
actions: ['Schedule maintenance', 'Order parts if needed'],
|
|
metadata: { timeframe: maintenance.timeframe },
|
|
created_at: new Date()
|
|
});
|
|
}
|
|
|
|
return suggestions;
|
|
} catch (error) {
|
|
logger.error('Error generating proactive suggestions:', error);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
async storeSuggestion(deviceId, suggestion) {
|
|
try {
|
|
await database.query(
|
|
`INSERT INTO ai_suggestions
|
|
(device_id, type, title, description, priority, category, confidence, actions, metadata, created_at)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
[
|
|
deviceId,
|
|
suggestion.type,
|
|
suggestion.title,
|
|
suggestion.description,
|
|
suggestion.priority,
|
|
suggestion.category,
|
|
suggestion.confidence,
|
|
JSON.stringify(suggestion.actions),
|
|
JSON.stringify(suggestion.metadata || {}),
|
|
suggestion.created_at
|
|
]
|
|
);
|
|
} catch (error) {
|
|
logger.error('Failed to store suggestion:', error);
|
|
}
|
|
}
|
|
|
|
async runPeriodicAnalysis() {
|
|
try {
|
|
logger.info('Running periodic AI analysis...');
|
|
|
|
// Get all active devices
|
|
const devices = await database.query('SELECT id FROM devices WHERE status = "active"');
|
|
|
|
for (const device of devices) {
|
|
// Get recent data for analysis
|
|
const recentData = await database.query(
|
|
'SELECT * FROM device_data WHERE device_id = ? ORDER BY timestamp DESC LIMIT 10',
|
|
[device.id]
|
|
);
|
|
|
|
if (recentData.length > 0) {
|
|
// Run comprehensive analysis
|
|
await this.runComprehensiveAnalysis(device.id, recentData);
|
|
}
|
|
}
|
|
|
|
logger.info('Periodic AI analysis completed');
|
|
} catch (error) {
|
|
logger.error('Error in periodic analysis:', error);
|
|
}
|
|
}
|
|
|
|
async runComprehensiveAnalysis(deviceId, data) {
|
|
try {
|
|
// Analyze device behavior patterns
|
|
const behaviorPatterns = await this.analyzeBehaviorPatterns(deviceId, data);
|
|
|
|
// Check for system-wide optimizations
|
|
const systemOptimizations = await this.findSystemOptimizations(deviceId, data);
|
|
|
|
// Generate long-term recommendations
|
|
const longTermRecommendations = await this.generateLongTermRecommendations(deviceId, data);
|
|
|
|
// Store comprehensive analysis
|
|
await this.storeComprehensiveAnalysis(deviceId, {
|
|
behaviorPatterns,
|
|
systemOptimizations,
|
|
longTermRecommendations,
|
|
timestamp: new Date()
|
|
});
|
|
|
|
} catch (error) {
|
|
logger.error('Error in comprehensive analysis:', error);
|
|
}
|
|
}
|
|
|
|
// Helper methods
|
|
extractNumericValues(data) {
|
|
const values = [];
|
|
const extract = (obj) => {
|
|
for (const key in obj) {
|
|
if (typeof obj[key] === 'number') {
|
|
values.push(obj[key]);
|
|
} else if (typeof obj[key] === 'object' && obj[key] !== null) {
|
|
extract(obj[key]);
|
|
}
|
|
}
|
|
};
|
|
extract(data);
|
|
return values;
|
|
}
|
|
|
|
async getHistoricalData(deviceId, limit) {
|
|
try {
|
|
return await database.query(
|
|
'SELECT raw_data FROM device_data WHERE device_id = ? ORDER BY timestamp DESC LIMIT ?',
|
|
[deviceId, limit]
|
|
);
|
|
} catch (error) {
|
|
logger.error('Error getting historical data:', error);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
async updateDeviceModel(deviceId, data, anomalyScore) {
|
|
try {
|
|
// Update device model with new data
|
|
const model = this.deviceModels.get(deviceId) || {
|
|
deviceType: 'unknown',
|
|
config: {},
|
|
lastUpdated: new Date()
|
|
};
|
|
|
|
// Update model parameters based on new data
|
|
model.lastUpdated = new Date();
|
|
model.lastAnomalyScore = anomalyScore;
|
|
|
|
this.deviceModels.set(deviceId, model);
|
|
|
|
// Store updated model in database
|
|
await database.query(
|
|
'UPDATE devices SET ai_model_config = ?, updated_at = NOW() WHERE id = ?',
|
|
[JSON.stringify(model), deviceId]
|
|
);
|
|
} catch (error) {
|
|
logger.error('Error updating device model:', error);
|
|
}
|
|
}
|
|
|
|
async storeAnalysisResults(deviceId, results) {
|
|
try {
|
|
await database.query(
|
|
`INSERT INTO ai_analysis_results
|
|
(device_id, anomaly_score, insights, optimizations, suggestions, timestamp, created_at)
|
|
VALUES (?, ?, ?, ?, ?, ?, NOW())`,
|
|
[
|
|
deviceId,
|
|
results.anomalyScore,
|
|
JSON.stringify(results.insights),
|
|
JSON.stringify(results.optimizations),
|
|
JSON.stringify(results.suggestions),
|
|
results.timestamp
|
|
]
|
|
);
|
|
} catch (error) {
|
|
logger.error('Failed to store analysis results:', error);
|
|
}
|
|
}
|
|
|
|
// Placeholder methods for complex AI operations
|
|
async analyzePatterns(deviceId, data) { return []; }
|
|
async analyzeTrends(deviceId, data) { return []; }
|
|
async analyzeEfficiency(deviceId, data) { return { score: 0.8, recommendations: [] }; }
|
|
async checkEnergyOptimization(deviceId, data) { return { opportunity: false }; }
|
|
async checkPerformanceOptimization(deviceId, data) { return { opportunity: false }; }
|
|
async checkMaintenanceOptimization(deviceId, data) { return { opportunity: false }; }
|
|
async assessDeviceHealth(deviceId, data) { return { score: 0.8 }; }
|
|
async predictMaintenance(deviceId, data) { return { needed: false }; }
|
|
async analyzeBehaviorPatterns(deviceId, data) { return []; }
|
|
async findSystemOptimizations(deviceId, data) { return []; }
|
|
async generateLongTermRecommendations(deviceId, data) { return []; }
|
|
async storeComprehensiveAnalysis(deviceId, analysis) { }
|
|
|
|
async healthCheck() {
|
|
try {
|
|
return {
|
|
status: this.isInitialized ? 'healthy' : 'not_initialized',
|
|
message: this.isInitialized ? 'AI Agent service is healthy' : 'Service not initialized',
|
|
deviceModels: this.deviceModels.size,
|
|
learningRate: this.learningRate,
|
|
anomalyThreshold: this.anomalyThreshold,
|
|
healingEnabled: this.healingEnabled
|
|
};
|
|
} catch (error) {
|
|
return {
|
|
status: 'unhealthy',
|
|
message: 'AI Agent service health check failed',
|
|
error: error.message
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = new AIAgentService();
|