// Simplified Discovery Integration // Updated for simplified signal names + tags architecture console.log('=== DISCOVERY.JS FILE LOADED - START ==='); class SimplifiedProtocolDiscovery { constructor() { this.currentScanId = 'simplified-scan-123'; this.isScanning = false; } init() { console.log('Discovery.js: init() called'); try { this.bindDiscoveryEvents(); console.log('Discovery.js: bindDiscoveryEvents() completed successfully'); } catch (error) { console.error('Discovery.js: Error in init():', error); } } bindDiscoveryEvents() { console.log('Binding discovery events...'); // Discovery scan button const startScanBtn = document.getElementById('start-discovery-scan'); console.log('Start scan button:', startScanBtn); if (startScanBtn) { startScanBtn.addEventListener('click', () => { console.log('Start Discovery Scan button clicked!'); this.startDiscoveryScan(); }); } else { console.error('Start scan button not found!'); } // Check if discovery results container exists const resultsContainer = document.getElementById('discovery-results'); console.log('Discovery results container during init:', resultsContainer); // Auto-fill signal form from discovery console.log('Setting up global click event listener for use-signal-btn'); try { const self = this; // Capture 'this' context document.addEventListener('click', function(e) { console.log('Global click event fired, target:', e.target.tagName, 'classes:', e.target.className); console.log('Target dataset:', e.target.dataset); if (e.target.classList.contains('use-signal-btn')) { console.log('Use This Signal button clicked!'); console.log('Signal index from dataset:', e.target.dataset.signalIndex); self.useDiscoveredEndpoint(e.target.dataset.signalIndex); } else { console.log('Clicked element is not a use-signal-btn'); } }); console.log('Global click event listener set up successfully'); } catch (error) { console.error('Error setting up event listener:', error); } } async useDiscoveredEndpoint(signalIndex) { console.log('Using discovered endpoint with index:', signalIndex); // Get the actual discovered endpoints from the mock scan const discoveredEndpoints = await this.mockDiscoveryScan('192.168.1.0/24'); // Map signal index to endpoint const endpoint = discoveredEndpoints[signalIndex]; if (!endpoint) { this.showNotification(`Endpoint with index ${signalIndex} not found`, 'error'); return; } // Convert to simplified signal format const signalData = this.convertEndpointToSignal(endpoint); // Auto-populate the signal form with retry logic this.autoPopulateSignalFormWithRetry(signalData); this.showNotification(`Endpoint ${endpoint.device_name} selected for signal creation`, 'success'); } autoPopulateSignalFormWithRetry(signalData, retryCount = 0) { console.log('Attempting to auto-populate signal form, attempt:', retryCount + 1); if (typeof window.autoPopulateSignalForm === 'function') { console.log('Found window.autoPopulateSignalForm, calling it...'); window.autoPopulateSignalForm(signalData); } else { console.error('autoPopulateSignalForm function not found'); // Retry after a delay if we haven't exceeded max retries if (retryCount < 5) { console.log(`Retrying in 500ms... (${retryCount + 1}/5)`); setTimeout(() => { this.autoPopulateSignalFormWithRetry(signalData, retryCount + 1); }, 500); } else { console.error('Max retries exceeded, autoPopulateSignalForm function still not found'); this.showNotification('Error: Could not open signal form. Please ensure the protocol mapping system is loaded.', 'error'); } } } convertEndpointToSignal(endpoint) { // Generate human-readable signal name const signalName = `${endpoint.device_name} ${endpoint.data_point}`; // Generate meaningful tags const tags = [ `device:${endpoint.device_name.toLowerCase().replace(/[^a-z0-9]/g, '_')}`, `protocol:${endpoint.protocol_type}`, `data_point:${endpoint.data_point.toLowerCase().replace(/[^a-z0-9]/g, '_')}`, 'discovered:true' ]; // Add device-specific tags if (endpoint.device_name.toLowerCase().includes('pump')) { tags.push('equipment:pump'); } if (endpoint.device_name.toLowerCase().includes('sensor')) { tags.push('equipment:sensor'); } if (endpoint.device_name.toLowerCase().includes('controller')) { tags.push('equipment:controller'); } // Add protocol-specific tags if (endpoint.protocol_type === 'modbus_tcp') { tags.push('interface:modbus'); } else if (endpoint.protocol_type === 'opcua') { tags.push('interface:opcua'); } // Generate database source const dbSource = `measurements.${endpoint.device_name.toLowerCase().replace(/[^a-z0-9]/g, '_')}_${endpoint.data_point.toLowerCase().replace(/[^a-z0-9]/g, '_')}`; return { signal_name: signalName, tags: tags, protocol_type: endpoint.protocol_type, protocol_address: endpoint.protocol_address, db_source: dbSource }; } // Start discovery scan async startDiscoveryScan() { console.log('Starting discovery scan...'); // Update UI const startBtn = document.getElementById('start-discovery-scan'); const stopBtn = document.getElementById('stop-discovery-scan'); const statusDiv = document.getElementById('discovery-status'); if (startBtn) startBtn.disabled = true; if (stopBtn) stopBtn.disabled = false; if (statusDiv) { statusDiv.innerHTML = '
Discovery scan in progress...
'; } try { // Run discovery const results = await this.discoverAndSuggestSignals(); // Update status if (statusDiv) { statusDiv.innerHTML = `
Discovery complete. Found ${results.length} devices.
`; } this.showNotification(`Discovery complete. Found ${results.length} devices.`, 'success'); } catch (error) { console.error('Discovery scan failed:', error); if (statusDiv) { statusDiv.innerHTML = '
Discovery scan failed
'; } this.showNotification('Discovery scan failed', 'error'); } finally { // Reset UI if (startBtn) startBtn.disabled = false; if (stopBtn) stopBtn.disabled = true; } } // Advanced discovery features async discoverAndSuggestSignals(networkRange = '192.168.1.0/24') { console.log(`Starting discovery scan on ${networkRange}`); this.isScanning = true; try { // Mock discovery results const discoveredEndpoints = await this.mockDiscoveryScan(networkRange); // Convert to suggested signals const suggestedSignals = discoveredEndpoints.map(endpoint => this.convertEndpointToSignal(endpoint) ); this.displayDiscoveryResults(suggestedSignals); this.isScanning = false; return suggestedSignals; } catch (error) { console.error('Discovery scan failed:', error); this.showNotification('Discovery scan failed', 'error'); this.isScanning = false; return []; } } async mockDiscoveryScan(networkRange) { // Simulate network discovery delay await new Promise(resolve => setTimeout(resolve, 2000)); // Return mock discovered endpoints return [ { device_id: 'discovered_001', protocol_type: 'modbus_tcp', device_name: 'Booster Pump', address: '192.168.1.110', port: 502, data_point: 'Flow Rate', protocol_address: '30002' }, { device_id: 'discovered_002', protocol_type: 'modbus_tcp', device_name: 'Level Sensor', address: '192.168.1.111', port: 502, data_point: 'Tank Level', protocol_address: '30003' }, { device_id: 'discovered_003', protocol_type: 'opcua', device_name: 'PLC Controller', address: '192.168.1.112', port: 4840, data_point: 'System Status', protocol_address: 'ns=2;s=SystemStatus' } ]; } displayDiscoveryResults(suggestedSignals) { console.log('Displaying discovery results:', suggestedSignals); const resultsContainer = document.getElementById('discovery-results'); if (!resultsContainer) { console.error('Discovery results container not found!'); this.showNotification('Discovery results container not found', 'error'); return; } console.log('Found discovery results container:', resultsContainer); resultsContainer.innerHTML = '

Discovery Results

'; suggestedSignals.forEach((signal, index) => { const signalCard = document.createElement('div'); signalCard.className = 'discovery-result-card'; signalCard.innerHTML = `
${signal.signal_name}
${signal.tags.map(tag => `${tag}`).join('')}
Protocol: ${signal.protocol_type} Address: ${signal.protocol_address}
`; resultsContainer.appendChild(signalCard); }); // Add event listeners for use buttons console.log('Adding event listener to results container'); console.log('Results container:', resultsContainer); console.log('Results container ID:', resultsContainer.id); console.log('Number of use-signal-btn elements:', resultsContainer.querySelectorAll('.use-signal-btn').length); const clickHandler = (e) => { console.log('Discovery results container clicked:', e.target); console.log('Button classes:', e.target.classList); console.log('Button tag name:', e.target.tagName); if (e.target.classList.contains('use-signal-btn')) { console.log('Use This Signal button clicked!'); const signalIndex = parseInt(e.target.dataset.signalIndex); console.log('Signal index:', signalIndex); const signal = suggestedSignals[signalIndex]; console.log('Signal data:', signal); // Use the global function directly if (typeof window.autoPopulateSignalForm === 'function') { window.autoPopulateSignalForm(signal); } else { console.error('autoPopulateSignalForm function not found!'); } } else { console.log('Clicked element is not a use-signal-btn'); } }; resultsContainer.addEventListener('click', clickHandler); console.log('Event listener added to results container'); // Add "Apply All" button const applyAllButton = document.createElement('button'); applyAllButton.className = 'apply-all-btn'; applyAllButton.textContent = 'Apply All as Protocol Signals'; applyAllButton.style.marginTop = '15px'; applyAllButton.style.padding = '10px 20px'; applyAllButton.style.background = '#28a745'; applyAllButton.style.color = 'white'; applyAllButton.style.border = 'none'; applyAllButton.style.borderRadius = '4px'; applyAllButton.style.cursor = 'pointer'; applyAllButton.style.fontWeight = 'bold'; applyAllButton.onclick = () => { this.applyAllAsProtocolSignals(suggestedSignals); }; resultsContainer.appendChild(applyAllButton); } // Apply all discovered signals as protocol signals async applyAllAsProtocolSignals(signals) { console.log('Applying all discovered signals as protocol signals:', signals); let successCount = 0; let errorCount = 0; let duplicateCount = 0; // First, check which signals already exist const existingSignals = await this.getExistingSignals(); const existingSignalNames = new Set(existingSignals.map(s => s.signal_name)); for (const signal of signals) { // Skip if signal with same name already exists if (existingSignalNames.has(signal.signal_name)) { console.log(`⚠ Skipping duplicate signal: ${signal.signal_name}`); duplicateCount++; continue; } try { const response = await fetch('/api/v1/dashboard/protocol-signals', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(signal) }); const data = await response.json(); if (data.success) { successCount++; console.log(`✓ Created signal: ${signal.signal_name}`); // Add to existing set to prevent duplicates in same batch existingSignalNames.add(signal.signal_name); } else { errorCount++; console.error(`✗ Failed to create signal: ${signal.signal_name}`, data.detail); // Check if it's a duplicate error if (data.detail && data.detail.includes('already exists')) { duplicateCount++; } } } catch (error) { errorCount++; console.error(`✗ Error creating signal: ${signal.signal_name}`, error); } } // Show results let message = `Created ${successCount} signals successfully.`; if (errorCount > 0) { message += ` ${errorCount} failed.`; } if (duplicateCount > 0) { message += ` ${duplicateCount} duplicates skipped.`; } const notificationType = errorCount > 0 ? 'warning' : (successCount > 0 ? 'success' : 'info'); this.showNotification(message, notificationType); // Refresh the protocol signals display if (typeof window.loadAllSignals === 'function') { window.loadAllSignals(); } } // Get existing signals to check for duplicates async getExistingSignals() { try { const response = await fetch('/api/v1/dashboard/protocol-signals'); const data = await response.json(); if (data.success) { return data.signals || []; } else { console.error('Failed to get existing signals:', data.detail); return []; } } catch (error) { console.error('Error getting existing signals:', error); return []; } } // Tag-based signal search async searchSignalsByTags(tags) { try { const params = new URLSearchParams(); tags.forEach(tag => params.append('tags', tag)); const response = await fetch(`/api/v1/dashboard/protocol-signals?${params}`); const data = await response.json(); if (data.success) { return data.signals; } else { console.error('Failed to search signals by tags:', data.detail); return []; } } catch (error) { console.error('Error searching signals by tags:', error); return []; } } // Clear all existing signals (for testing) async clearAllSignals() { if (!confirm('Are you sure you want to delete ALL protocol signals? This action cannot be undone.')) { return; } try { const existingSignals = await this.getExistingSignals(); let deletedCount = 0; for (const signal of existingSignals) { try { const response = await fetch(`/api/v1/dashboard/protocol-signals/${signal.signal_id}`, { method: 'DELETE' }); const data = await response.json(); if (data.success) { deletedCount++; console.log(`✓ Deleted signal: ${signal.signal_name}`); } else { console.error(`✗ Failed to delete signal: ${signal.signal_name}`, data.detail); } } catch (error) { console.error(`✗ Error deleting signal: ${signal.signal_name}`, error); } } this.showNotification(`Deleted ${deletedCount} signals successfully.`, 'success'); // Refresh the protocol signals display if (typeof window.loadAllSignals === 'function') { window.loadAllSignals(); } } catch (error) { console.error('Error clearing signals:', error); this.showNotification('Error clearing signals', 'error'); } } // Signal name suggestions based on device type generateSignalNameSuggestions(deviceName, dataPoint) { const baseName = `${deviceName} ${dataPoint}`; const suggestions = [ baseName, `${dataPoint} of ${deviceName}`, `${deviceName} ${dataPoint} Reading`, `${dataPoint} Measurement - ${deviceName}` ]; // Add context-specific suggestions if (dataPoint.toLowerCase().includes('speed')) { suggestions.push(`${deviceName} Motor Speed`); suggestions.push(`${deviceName} RPM`); } if (dataPoint.toLowerCase().includes('temperature')) { suggestions.push(`${deviceName} Temperature`); suggestions.push(`Temperature at ${deviceName}`); } if (dataPoint.toLowerCase().includes('pressure')) { suggestions.push(`${deviceName} Pressure`); suggestions.push(`Pressure Reading - ${deviceName}`); } return suggestions; } // Tag suggestions based on device and protocol generateTagSuggestions(deviceName, protocolType, dataPoint) { const suggestions = new Set(); // Device type tags if (deviceName.toLowerCase().includes('pump')) { suggestions.add('equipment:pump'); suggestions.add('fluid:water'); } if (deviceName.toLowerCase().includes('sensor')) { suggestions.add('equipment:sensor'); suggestions.add('type:measurement'); } if (deviceName.toLowerCase().includes('controller')) { suggestions.add('equipment:controller'); suggestions.add('type:control'); } // Protocol tags suggestions.add(`protocol:${protocolType}`); if (protocolType === 'modbus_tcp' || protocolType === 'modbus_rtu') { suggestions.add('interface:modbus'); } else if (protocolType === 'opcua') { suggestions.add('interface:opcua'); } // Data point tags suggestions.add(`data_point:${dataPoint.toLowerCase().replace(/[^a-z0-9]/g, '_')}`); if (dataPoint.toLowerCase().includes('speed')) { suggestions.add('unit:rpm'); suggestions.add('type:setpoint'); } if (dataPoint.toLowerCase().includes('temperature')) { suggestions.add('unit:celsius'); suggestions.add('type:measurement'); } if (dataPoint.toLowerCase().includes('pressure')) { suggestions.add('unit:psi'); suggestions.add('type:measurement'); } if (dataPoint.toLowerCase().includes('status')) { suggestions.add('type:status'); suggestions.add('format:boolean'); } // Discovery tag suggestions.add('discovered:true'); return Array.from(suggestions); } showNotification(message, type = 'info') { const notification = document.createElement('div'); notification.className = `discovery-notification ${type}`; notification.textContent = message; document.body.appendChild(notification); // Auto-remove after 5 seconds setTimeout(() => { if (notification.parentNode) { notification.remove(); } }, 5000); } } // Global instance // Expose to window for global access window.SimplifiedProtocolDiscovery = SimplifiedProtocolDiscovery; const simplifiedDiscovery = new SimplifiedProtocolDiscovery(); window.simplifiedDiscovery = simplifiedDiscovery; // Initialize when DOM is loaded console.log('Discovery.js loaded - setting up DOMContentLoaded listener'); // Check if DOM is already loaded if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', function() { console.log('DOMContentLoaded event fired - Initializing SimplifiedProtocolDiscovery...'); window.simplifiedDiscovery.init(); console.log('SimplifiedProtocolDiscovery initialized successfully'); }); } else { console.log('DOM already loaded - Initializing SimplifiedProtocolDiscovery immediately...'); window.simplifiedDiscovery.init(); console.log('SimplifiedProtocolDiscovery initialized successfully'); } console.log('=== DISCOVERY.JS FILE LOADED - END ===');