// Simplified Protocol Mapping Functions // Uses human-readable signal names and tags instead of complex IDs let currentProtocolFilter = 'all'; let editingSignalId = null; let allTags = new Set(); // Simplified Signal Management Functions async function loadAllSignals() { try { const response = await fetch('/api/v1/dashboard/protocol-signals'); const data = await response.json(); if (data.success) { displaySignals(data.signals); updateTagCloud(data.signals); } else { showSimplifiedAlert('Failed to load signals', 'error'); } } catch (error) { console.error('Error loading signals:', error); showSimplifiedAlert('Error loading signals', 'error'); } } function displaySignals(signals) { // Try both possible table body IDs let tbody = document.getElementById('protocol-signals-body'); if (!tbody) { tbody = document.getElementById('protocol-mappings-body'); } // Check if the table body element exists if (!tbody) { console.warn('protocol signals/mappings table body element not found - table may not be available'); return; } tbody.innerHTML = ''; if (signals.length === 0) { tbody.innerHTML = 'No protocol signals found'; return; } signals.forEach(signal => { const row = document.createElement('tr'); row.innerHTML = ` ${signal.signal_name} ${signal.protocol_type} ${signal.tags.map(tag => `${tag}`).join('')} ${signal.protocol_address} ${signal.db_source} ${signal.enabled ? 'Enabled' : 'Disabled'} `; tbody.appendChild(row); }); } function updateTagCloud(signals) { const tagCloud = document.getElementById('tag-cloud'); if (!tagCloud) return; // Collect all tags const tagCounts = {}; signals.forEach(signal => { signal.tags.forEach(tag => { tagCounts[tag] = (tagCounts[tag] || 0) + 1; }); }); // Create tag cloud tagCloud.innerHTML = ''; Object.entries(tagCounts).forEach(([tag, count]) => { const tagElement = document.createElement('span'); tagElement.className = 'tag-cloud-item'; tagElement.textContent = tag; tagElement.title = `${count} signal(s)`; tagElement.onclick = () => filterByTag(tag); tagCloud.appendChild(tagElement); }); } function filterByTag(tag) { const filterInput = document.getElementById('tag-filter'); if (filterInput) { filterInput.value = tag; applyFilters(); } } async function applyFilters() { const tagFilter = document.getElementById('tag-filter')?.value || ''; const protocolFilter = document.getElementById('protocol-filter')?.value || 'all'; const nameFilter = document.getElementById('name-filter')?.value || ''; const params = new URLSearchParams(); if (tagFilter) params.append('tags', tagFilter); if (protocolFilter !== 'all') params.append('protocol_type', protocolFilter); if (nameFilter) params.append('signal_name_contains', nameFilter); try { const response = await fetch(`/api/v1/dashboard/protocol-signals?${params}`); const data = await response.json(); if (data.success) { displaySignals(data.signals); } } catch (error) { console.error('Error applying filters:', error); } } // Modal Functions function showAddSignalModal() { editingSignalId = null; document.getElementById('modal-title').textContent = 'Add Protocol Signal'; document.getElementById('signal-form').reset(); document.getElementById('protocol-address-help').textContent = ''; document.getElementById('signal-modal').style.display = 'block'; } function showEditSignalModal(signal) { editingSignalId = signal.signal_id; document.getElementById('modal-title').textContent = 'Edit Protocol Signal'; // Populate form document.getElementById('signal_name').value = signal.signal_name; document.getElementById('tags').value = signal.tags.join(', '); document.getElementById('protocol_type').value = signal.protocol_type; document.getElementById('protocol_address').value = signal.protocol_address; document.getElementById('db_source').value = signal.db_source; document.getElementById('preprocessing_enabled').checked = signal.preprocessing_enabled || false; updateProtocolFields(); document.getElementById('signal-modal').style.display = 'block'; } function closeSignalModal() { document.getElementById('signal-modal').style.display = 'none'; editingSignalId = null; } function updateProtocolFields() { const protocolType = document.getElementById('protocol_type').value; const helpText = document.getElementById('protocol-address-help'); switch (protocolType) { case 'modbus_tcp': case 'modbus_rtu': helpText.textContent = 'Modbus address format: 40001 (holding register), 30001 (input register), 10001 (coil), 00001 (discrete input)'; break; case 'opcua': helpText.textContent = 'OPC UA NodeId format: ns=2;s=MyVariable or ns=2;i=1234'; break; case 'rest_api': helpText.textContent = 'REST API endpoint format: /api/v1/data/endpoint'; break; default: helpText.textContent = ''; } } // Form Submission async function saveSignal(event) { event.preventDefault(); const formData = getSignalFormData(); try { let response; if (editingSignalId) { response = await fetch(`/api/v1/dashboard/protocol-signals/${editingSignalId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(formData) }); } else { response = await fetch('/api/v1/dashboard/protocol-signals', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(formData) }); } const data = await response.json(); if (data.success) { showSimplifiedAlert(`Protocol signal ${editingSignalId ? 'updated' : 'created'} successfully!`, 'success'); closeSignalModal(); loadAllSignals(); } else { showSimplifiedAlert(`Failed to save signal: ${data.detail || 'Unknown error'}`, 'error'); } } catch (error) { console.error('Error saving signal:', error); showSimplifiedAlert('Error saving signal', 'error'); } } function getSignalFormData() { const tagsInput = document.getElementById('tags').value; const tags = tagsInput.split(',').map(tag => tag.trim()).filter(tag => tag); return { signal_name: document.getElementById('signal_name').value, tags: tags, protocol_type: document.getElementById('protocol_type').value, protocol_address: document.getElementById('protocol_address').value, db_source: document.getElementById('db_source').value, preprocessing_enabled: document.getElementById('preprocessing_enabled').checked }; } // Signal Management async function editSignal(signalId) { try { const response = await fetch(`/api/v1/dashboard/protocol-signals/${signalId}`); const data = await response.json(); if (data.success) { showEditSignalModal(data.signal); } else { showSimplifiedAlert('Signal not found', 'error'); } } catch (error) { console.error('Error loading signal:', error); showSimplifiedAlert('Error loading signal', 'error'); } } async function deleteSignal(signalId) { if (!confirm('Are you sure you want to delete this signal?')) { return; } try { const response = await fetch(`/api/v1/dashboard/protocol-signals/${signalId}`, { method: 'DELETE' }); const data = await response.json(); if (data.success) { showSimplifiedAlert('Signal deleted successfully!', 'success'); loadAllSignals(); } else { showSimplifiedAlert(`Failed to delete signal: ${data.detail || 'Unknown error'}`, 'error'); } } catch (error) { console.error('Error deleting signal:', error); showSimplifiedAlert('Error deleting signal', 'error'); } } // Discovery Integration function autoPopulateSignalForm(discoveryData) { console.log('Auto-populating signal form with:', discoveryData); // First, open the "Add New Signal" modal console.log('Opening Add Signal modal...'); showAddSignalModal(); // Wait for modal to be fully loaded and visible const waitForModal = setInterval(() => { const modal = document.getElementById('signal-modal'); const isModalVisible = modal && modal.style.display !== 'none'; if (isModalVisible) { clearInterval(waitForModal); populateModalFields(discoveryData); } }, 50); // Timeout after 2 seconds setTimeout(() => { clearInterval(waitForModal); const modal = document.getElementById('signal-modal'); if (modal && modal.style.display !== 'none') { populateModalFields(discoveryData); } else { console.error('Modal did not open within timeout period'); showSimplifiedAlert('Could not open signal form. Please try opening it manually.', 'error'); } }, 2000); } function populateModalFields(discoveryData) { console.log('Populating modal fields with:', discoveryData); // Try to find the appropriate form let form = document.getElementById('signal-form'); let modal = document.getElementById('signal-modal'); if (!form) { form = document.getElementById('mapping-form'); modal = document.getElementById('mapping-modal'); } if (!form || !modal) { console.warn('No signal or mapping form found - cannot auto-populate'); showSimplifiedAlert('No signal form found - please open the add signal/mapping modal first', 'error'); return; } // Show the modal if it's hidden if (modal.style.display === 'none') { modal.style.display = 'block'; console.log('✓ Opened modal'); } // Find fields within the modal context to avoid duplicate ID issues const modalContent = modal.querySelector('.modal-content'); // Debug: Check if fields exist console.log('Available fields in modal:'); console.log('- protocol_type:', modalContent.querySelector('#protocol_type')); console.log('- mapping_protocol_type:', modalContent.querySelector('#mapping_protocol_type')); console.log('- protocol_address:', modalContent.querySelector('#protocol_address')); console.log('- db_source:', modalContent.querySelector('#db_source')); // Populate signal name (try different field names) const signalNameField = modalContent.querySelector('#signal_name') || modalContent.querySelector('#mapping_id'); if (signalNameField && discoveryData.signal_name) { signalNameField.value = discoveryData.signal_name; console.log('✓ Set signal name to:', discoveryData.signal_name); } // Populate tags (only in simplified template) const tagsField = modalContent.querySelector('#tags'); if (tagsField && discoveryData.tags) { tagsField.value = discoveryData.tags.join(', '); console.log('✓ Set tags to:', discoveryData.tags); } // Populate protocol type - try both possible IDs let protocolTypeField = modalContent.querySelector('#protocol_type'); if (!protocolTypeField) { protocolTypeField = modalContent.querySelector('#mapping_protocol_type'); } if (protocolTypeField && discoveryData.protocol_type) { protocolTypeField.value = discoveryData.protocol_type; console.log('✓ Set protocol_type to:', discoveryData.protocol_type); // Trigger protocol field updates protocolTypeField.dispatchEvent(new Event('change')); } // Populate protocol address const protocolAddressField = modalContent.querySelector('#protocol_address'); if (protocolAddressField && discoveryData.protocol_address) { protocolAddressField.value = discoveryData.protocol_address; console.log('✓ Set protocol_address to:', discoveryData.protocol_address); } // Populate database source const dbSourceField = modalContent.querySelector('#db_source'); if (dbSourceField && discoveryData.db_source) { dbSourceField.value = discoveryData.db_source; console.log('✓ Set db_source to:', discoveryData.db_source); } // Show success message showSimplifiedAlert(`Signal form populated with discovery data. Please review and save.`, 'success'); } // Utility Functions function showSimplifiedAlert(message, type = 'info') { const alertsDiv = document.getElementById('protocol-mapping-alerts'); // Check if alerts div exists if (!alertsDiv) { console.warn('protocol-mapping-alerts element not found - cannot show alert:', message); return; } const alertDiv = document.createElement('div'); alertDiv.className = `alert ${type === 'error' ? 'error' : 'success'}`; alertDiv.textContent = message; alertsDiv.innerHTML = ''; alertsDiv.appendChild(alertDiv); // Auto-remove after 5 seconds setTimeout(() => { if (alertDiv.parentNode) { alertDiv.remove(); } }, 5000); } // Initialize document.addEventListener('DOMContentLoaded', function() { // Try both possible form IDs let signalForm = document.getElementById('signal-form'); if (!signalForm) { // Look for form inside mapping-modal const mappingModal = document.getElementById('mapping-modal'); if (mappingModal) { signalForm = mappingModal.querySelector('form'); } } if (signalForm) { signalForm.addEventListener('submit', saveSignal); } // Load initial data loadAllSignals(); }); // Expose functions to global scope for discovery integration window.autoPopulateSignalForm = populateModalFields; window.loadAllSignals = loadAllSignals;