// 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'}
Edit
Delete
`;
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;