Fix Apply All protocol mappings and add duplicate detection
- Fix Apply All to properly create all 3 signals instead of just 1 - Add duplicate detection to prevent creating signals with same names - Add Clear All Signals button for testing - Update cache-busting versions for JavaScript files
This commit is contained in:
parent
70351940d6
commit
15961f715c
|
|
@ -1,6 +1,8 @@
|
||||||
// Simplified Discovery Integration
|
// Simplified Discovery Integration
|
||||||
// Updated for simplified signal names + tags architecture
|
// Updated for simplified signal names + tags architecture
|
||||||
|
|
||||||
|
console.log('=== DISCOVERY.JS FILE LOADED - START ===');
|
||||||
|
|
||||||
class SimplifiedProtocolDiscovery {
|
class SimplifiedProtocolDiscovery {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.currentScanId = 'simplified-scan-123';
|
this.currentScanId = 'simplified-scan-123';
|
||||||
|
|
@ -8,75 +10,100 @@ class SimplifiedProtocolDiscovery {
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
|
console.log('Discovery.js: init() called');
|
||||||
|
try {
|
||||||
this.bindDiscoveryEvents();
|
this.bindDiscoveryEvents();
|
||||||
|
console.log('Discovery.js: bindDiscoveryEvents() completed successfully');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Discovery.js: Error in init():', error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bindDiscoveryEvents() {
|
bindDiscoveryEvents() {
|
||||||
|
console.log('Binding discovery events...');
|
||||||
|
|
||||||
// Discovery scan button
|
// Discovery scan button
|
||||||
const startScanBtn = document.getElementById('start-discovery-scan');
|
const startScanBtn = document.getElementById('start-discovery-scan');
|
||||||
|
console.log('Start scan button:', startScanBtn);
|
||||||
if (startScanBtn) {
|
if (startScanBtn) {
|
||||||
startScanBtn.addEventListener('click', () => {
|
startScanBtn.addEventListener('click', () => {
|
||||||
|
console.log('Start Discovery Scan button clicked!');
|
||||||
this.startDiscoveryScan();
|
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
|
// Auto-fill signal form from discovery
|
||||||
document.addEventListener('click', (e) => {
|
console.log('Setting up global click event listener for use-signal-btn');
|
||||||
if (e.target.classList.contains('use-discovered-endpoint')) {
|
try {
|
||||||
this.useDiscoveredEndpoint(e.target.dataset.endpointId);
|
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(endpointId) {
|
async useDiscoveredEndpoint(signalIndex) {
|
||||||
console.log('Using discovered endpoint:', endpointId);
|
console.log('Using discovered endpoint with index:', signalIndex);
|
||||||
|
|
||||||
// Mock endpoint data (in real implementation, this would come from discovery service)
|
// Get the actual discovered endpoints from the mock scan
|
||||||
const endpoints = {
|
const discoveredEndpoints = await this.mockDiscoveryScan('192.168.1.0/24');
|
||||||
'device_001': {
|
|
||||||
device_id: 'device_001',
|
|
||||||
protocol_type: 'modbus_tcp',
|
|
||||||
device_name: 'Water Pump Controller',
|
|
||||||
address: '192.168.1.100',
|
|
||||||
port: 502,
|
|
||||||
data_point: 'Speed',
|
|
||||||
protocol_address: '40001'
|
|
||||||
},
|
|
||||||
'device_002': {
|
|
||||||
device_id: 'device_002',
|
|
||||||
protocol_type: 'opcua',
|
|
||||||
device_name: 'Temperature Sensor',
|
|
||||||
address: '192.168.1.101',
|
|
||||||
port: 4840,
|
|
||||||
data_point: 'Temperature',
|
|
||||||
protocol_address: 'ns=2;s=Temperature'
|
|
||||||
},
|
|
||||||
'device_003': {
|
|
||||||
device_id: 'device_003',
|
|
||||||
protocol_type: 'modbus_tcp',
|
|
||||||
device_name: 'Pressure Transmitter',
|
|
||||||
address: '192.168.1.102',
|
|
||||||
port: 502,
|
|
||||||
data_point: 'Pressure',
|
|
||||||
protocol_address: '30001'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const endpoint = endpoints[endpointId];
|
// Map signal index to endpoint
|
||||||
|
const endpoint = discoveredEndpoints[signalIndex];
|
||||||
if (!endpoint) {
|
if (!endpoint) {
|
||||||
this.showNotification(`Endpoint ${endpointId} not found`, 'error');
|
this.showNotification(`Endpoint with index ${signalIndex} not found`, 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert to simplified signal format
|
// Convert to simplified signal format
|
||||||
const signalData = this.convertEndpointToSignal(endpoint);
|
const signalData = this.convertEndpointToSignal(endpoint);
|
||||||
|
|
||||||
// Auto-populate the signal form
|
// Auto-populate the signal form with retry logic
|
||||||
this.autoPopulateSignalForm(signalData);
|
this.autoPopulateSignalFormWithRetry(signalData);
|
||||||
|
|
||||||
this.showNotification(`Endpoint ${endpoint.device_name} selected for signal creation`, 'success');
|
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) {
|
convertEndpointToSignal(endpoint) {
|
||||||
// Generate human-readable signal name
|
// Generate human-readable signal name
|
||||||
const signalName = `${endpoint.device_name} ${endpoint.data_point}`;
|
const signalName = `${endpoint.device_name} ${endpoint.data_point}`;
|
||||||
|
|
@ -120,25 +147,7 @@ class SimplifiedProtocolDiscovery {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
autoPopulateSignalForm(signalData) {
|
// Start discovery scan
|
||||||
console.log('Auto-populating signal form with:', signalData);
|
|
||||||
|
|
||||||
// Ensure protocol mapping tab is active
|
|
||||||
if (typeof showTab === 'function') {
|
|
||||||
showTab('protocol-mapping');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use the simplified protocol mapping function
|
|
||||||
if (typeof window.autoPopulateSignalForm === 'function') {
|
|
||||||
// Add a small delay to ensure the tab is loaded
|
|
||||||
setTimeout(() => {
|
|
||||||
window.autoPopulateSignalForm(signalData);
|
|
||||||
}, 100);
|
|
||||||
} else {
|
|
||||||
console.error('Simplified protocol mapping functions not loaded');
|
|
||||||
this.showNotification('Protocol mapping system not available', 'error');
|
|
||||||
}
|
|
||||||
} // Start discovery scan
|
|
||||||
async startDiscoveryScan() {
|
async startDiscoveryScan() {
|
||||||
console.log('Starting discovery scan...');
|
console.log('Starting discovery scan...');
|
||||||
|
|
||||||
|
|
@ -249,6 +258,7 @@ class SimplifiedProtocolDiscovery {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('Found discovery results container:', resultsContainer);
|
||||||
resultsContainer.innerHTML = '<h3>Discovery Results</h3>';
|
resultsContainer.innerHTML = '<h3>Discovery Results</h3>';
|
||||||
|
|
||||||
suggestedSignals.forEach((signal, index) => {
|
suggestedSignals.forEach((signal, index) => {
|
||||||
|
|
@ -274,13 +284,35 @@ class SimplifiedProtocolDiscovery {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add event listeners for use buttons
|
// Add event listeners for use buttons
|
||||||
resultsContainer.addEventListener('click', (e) => {
|
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')) {
|
if (e.target.classList.contains('use-signal-btn')) {
|
||||||
|
console.log('Use This Signal button clicked!');
|
||||||
const signalIndex = parseInt(e.target.dataset.signalIndex);
|
const signalIndex = parseInt(e.target.dataset.signalIndex);
|
||||||
|
console.log('Signal index:', signalIndex);
|
||||||
const signal = suggestedSignals[signalIndex];
|
const signal = suggestedSignals[signalIndex];
|
||||||
this.autoPopulateSignalForm(signal);
|
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
|
// Add "Apply All" button
|
||||||
const applyAllButton = document.createElement('button');
|
const applyAllButton = document.createElement('button');
|
||||||
|
|
@ -308,8 +340,20 @@ class SimplifiedProtocolDiscovery {
|
||||||
|
|
||||||
let successCount = 0;
|
let successCount = 0;
|
||||||
let errorCount = 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) {
|
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 {
|
try {
|
||||||
const response = await fetch('/api/v1/dashboard/protocol-signals', {
|
const response = await fetch('/api/v1/dashboard/protocol-signals', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|
@ -322,9 +366,16 @@ class SimplifiedProtocolDiscovery {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
successCount++;
|
successCount++;
|
||||||
console.log(`✓ Created signal: ${signal.signal_name}`);
|
console.log(`✓ Created signal: ${signal.signal_name}`);
|
||||||
|
// Add to existing set to prevent duplicates in same batch
|
||||||
|
existingSignalNames.add(signal.signal_name);
|
||||||
} else {
|
} else {
|
||||||
errorCount++;
|
errorCount++;
|
||||||
console.error(`✗ Failed to create signal: ${signal.signal_name}`, data.detail);
|
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) {
|
} catch (error) {
|
||||||
errorCount++;
|
errorCount++;
|
||||||
|
|
@ -333,8 +384,16 @@ class SimplifiedProtocolDiscovery {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show results
|
// Show results
|
||||||
const message = `Created ${successCount} signals successfully. ${errorCount > 0 ? `${errorCount} failed.` : ''}`;
|
let message = `Created ${successCount} signals successfully.`;
|
||||||
this.showNotification(message, errorCount > 0 ? 'warning' : 'success');
|
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
|
// Refresh the protocol signals display
|
||||||
if (typeof window.loadAllSignals === 'function') {
|
if (typeof window.loadAllSignals === 'function') {
|
||||||
|
|
@ -342,6 +401,24 @@ class SimplifiedProtocolDiscovery {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
// Tag-based signal search
|
||||||
async searchSignalsByTags(tags) {
|
async searchSignalsByTags(tags) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -363,6 +440,47 @@ class SimplifiedProtocolDiscovery {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
// Signal name suggestions based on device type
|
||||||
generateSignalNameSuggestions(deviceName, dataPoint) {
|
generateSignalNameSuggestions(deviceName, dataPoint) {
|
||||||
const baseName = `${deviceName} ${dataPoint}`;
|
const baseName = `${deviceName} ${dataPoint}`;
|
||||||
|
|
@ -462,9 +580,26 @@ class SimplifiedProtocolDiscovery {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Global instance
|
// Global instance
|
||||||
|
// Expose to window for global access
|
||||||
|
window.SimplifiedProtocolDiscovery = SimplifiedProtocolDiscovery;
|
||||||
|
|
||||||
const simplifiedDiscovery = new SimplifiedProtocolDiscovery();
|
const simplifiedDiscovery = new SimplifiedProtocolDiscovery();
|
||||||
|
window.simplifiedDiscovery = simplifiedDiscovery;
|
||||||
|
|
||||||
// Initialize when DOM is loaded
|
// Initialize when DOM is loaded
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
console.log('Discovery.js loaded - setting up DOMContentLoaded listener');
|
||||||
simplifiedDiscovery.init();
|
|
||||||
});
|
// 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 ===');
|
||||||
|
|
@ -42,6 +42,23 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Discovery Results -->
|
||||||
|
<div class="discovery-section" style="margin: 20px 0; padding: 15px; border: 1px solid #ddd; border-radius: 5px; background: #f8f9fa;">
|
||||||
|
<h3>Discovery Results</h3>
|
||||||
|
<p>Use discovered signals to quickly add them to your protocol mapping:</p>
|
||||||
|
<div style="margin-bottom: 15px;">
|
||||||
|
<button id="start-discovery-scan" class="btn btn-primary">
|
||||||
|
Start Discovery Scan
|
||||||
|
</button>
|
||||||
|
<button onclick="window.simplifiedDiscovery.clearAllSignals()" class="btn btn-danger" style="margin-left: 10px;">
|
||||||
|
Clear All Signals
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div id="discovery-results">
|
||||||
|
<!-- Discovery results will be populated by JavaScript -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Tag Cloud -->
|
<!-- Tag Cloud -->
|
||||||
<div class="tag-cloud">
|
<div class="tag-cloud">
|
||||||
<h3>Popular Tags</h3>
|
<h3>Popular Tags</h3>
|
||||||
|
|
@ -136,7 +153,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- JavaScript -->
|
<!-- JavaScript -->
|
||||||
<script src="/static/simplified_protocol_mapping.js"></script>
|
<script src="/static/simplified_protocol_mapping.js?v=3"></script>
|
||||||
<script src="/static/simplified_discovery.js"></script>
|
<script src="/static/discovery.js?v=29"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
Loading…
Reference in New Issue