CalejoControl/docs/PROTOCOL_MAPPING_IMPLEMENTA...

21 KiB

Protocol Mapping - Phase 1 Implementation Plan

Overview

This document outlines the detailed implementation plan for Phase 1 of the Protocol Mapping UI feature, supporting Modbus, OPC UA, and other industrial protocols.

🎯 Phase 1 Goals

  • Enable basic configuration of database-to-protocol mappings through unified dashboard interface
  • Replace hardcoded protocol mappings with configurable system
  • Support multiple protocols (Modbus, OPC UA) through single Protocol Mapping tab
  • Provide protocol-specific validation within unified interface
  • Implement protocol switching within single dashboard tab

📋 Detailed Task Breakdown

Task 1: Extend Configuration Manager with Protocol Mapping Support

Priority: High Estimated Effort: 3 days

Implementation Details:

# File: src/dashboard/configuration_manager.py

class ProtocolMapping(BaseModel):
    """Protocol mapping configuration for all protocols"""
    id: str
    protocol_type: str  # modbus_tcp, opcua, custom
    station_id: str
    pump_id: str
    data_type: str  # setpoint, status, power, etc.
    protocol_address: str  # register address or OPC UA node
    db_source: str
    transformation_rules: List[Dict] = []
    
    # Protocol-specific configurations
    modbus_config: Optional[Dict] = None
    opcua_config: Optional[Dict] = None
    
class ConfigurationManager:
    def __init__(self):
        self.protocol_mappings: List[ProtocolMapping] = []
    
    def add_protocol_mapping(self, mapping: ProtocolMapping) -> bool:
        """Add a new protocol mapping with validation"""
        
    def get_protocol_mappings(self, 
                            protocol_type: str = None, 
                            station_id: str = None, 
                            pump_id: str = None) -> List[ProtocolMapping]:
        """Get mappings filtered by protocol/station/pump"""
        
    def validate_protocol_mapping(self, mapping: ProtocolMapping) -> Dict[str, Any]:
        """Validate mapping for conflicts and protocol-specific rules"""

Task 2: Create Protocol Mapping API Endpoints

Priority: High Estimated Effort: 2 days

Implementation Details:

# File: src/dashboard/api.py

@dashboard_router.get("/protocol-mappings")
async def get_protocol_mappings(
    protocol_type: Optional[str] = None,
    station_id: Optional[str] = None,
    pump_id: Optional[str] = None
):
    """Get all protocol mappings"""

@dashboard_router.post("/protocol-mappings")
async def create_protocol_mapping(mapping: ProtocolMapping):
    """Create a new protocol mapping"""

@dashboard_router.put("/protocol-mappings/{mapping_id}")
async def update_protocol_mapping(mapping_id: str, mapping: ProtocolMapping):
    """Update an existing protocol mapping"""

@dashboard_router.delete("/protocol-mappings/{mapping_id}")
async def delete_protocol_mapping(mapping_id: str):
    """Delete a protocol mapping"""

@dashboard_router.post("/protocol-mappings/validate")
async def validate_protocol_mapping(mapping: ProtocolMapping):
    """Validate a protocol mapping without saving"""

# Protocol-specific endpoints
@dashboard_router.get("/protocol-mappings/modbus")
async def get_modbus_mappings():
    """Get all Modbus mappings"""

@dashboard_router.get("/protocol-mappings/opcua")
async def get_opcua_mappings():
    """Get all OPC UA mappings"""

Task 3: Build Multi-Protocol Configuration Form UI

Priority: High Estimated Effort: 3 days

Implementation Details:

<!-- File: static/dashboard.js - Add to existing dashboard -->

// Add Protocol Mapping section to dashboard
function createProtocolMappingSection() {
    return `
    <div class="protocol-mapping-section">
        <h3>Protocol Mapping Configuration</h3>
        <div class="protocol-selector">
            <button class="protocol-btn active" onclick="selectProtocol('modbus')">Modbus</button>
            <button class="protocol-btn" onclick="selectProtocol('opcua')">OPC UA</button>
            <button class="protocol-btn" onclick="selectProtocol('custom')">Custom</button>
        </div>
        <div class="mapping-controls">
            <button onclick="showMappingForm()">Add Mapping</button>
            <button onclick="exportMappings()">Export</button>
        </div>
        <div id="mapping-grid"></div>
        <div id="mapping-form" class="modal hidden">
            <!-- Multi-protocol configuration form implementation -->
        </div>
    </div>
    `;
}

Task 4: Implement Protocol Mapping Grid View

Priority: Medium Estimated Effort: 2 days

Implementation Details:

// File: static/dashboard.js

function renderMappingGrid(mappings) {
    const grid = document.getElementById('mapping-grid');
    grid.innerHTML = `
        <table class="mapping-table">
            <thead>
                <tr>
                    <th>Protocol</th>
                    <th>Station</th>
                    <th>Pump</th>
                    <th>Data Type</th>
                    <th>Address</th>
                    <th>Database Source</th>
                    <th>Actions</th>
                </tr>
            </thead>
            <tbody>
                ${mappings.map(mapping => `
                    <tr class="protocol-${mapping.protocol_type}">
                        <td><span class="protocol-badge">${mapping.protocol_type}</span></td>
                        <td>${mapping.station_id}</td>
                        <td>${mapping.pump_id}</td>
                        <td>${mapping.data_type}</td>
                        <td>${mapping.protocol_address}</td>
                        <td>${mapping.db_source}</td>
                        <td>
                            <button onclick="editMapping('${mapping.id}')">Edit</button>
                            <button onclick="deleteMapping('${mapping.id}')">Delete</button>
                        </td>
                    </tr>
                `).join('')}
            </tbody>
        </table>
    `;
}

Task 5: Add Protocol-Specific Validation Logic

Priority: High Estimated Effort: 2 days

Implementation Details:

# File: src/dashboard/configuration_manager.py

class ConfigurationManager:
    def validate_protocol_mapping(self, mapping: ProtocolMapping) -> Dict[str, Any]:
        """Validate protocol mapping configuration"""
        errors = []
        warnings = []
        
        # Protocol-specific validation
        if mapping.protocol_type == 'modbus_tcp':
            # Modbus validation
            try:
                address = int(mapping.protocol_address)
                if not (0 <= address <= 65535):
                    errors.append("Modbus register address must be between 0 and 65535")
            except ValueError:
                errors.append("Modbus address must be a valid integer")
                
            # Check for address conflicts
            for existing in self.protocol_mappings:
                if (existing.id != mapping.id and 
                    existing.protocol_type == 'modbus_tcp' and 
                    existing.protocol_address == mapping.protocol_address):
                    errors.append(f"Modbus address {mapping.protocol_address} already used by {existing.station_id}/{existing.pump_id}")
        
        elif mapping.protocol_type == 'opcua':
            # OPC UA validation
            if not mapping.protocol_address.startswith('ns='):
                errors.append("OPC UA Node ID must start with 'ns='")
            
            # Check for node conflicts
            for existing in self.protocol_mappings:
                if (existing.id != mapping.id and 
                    existing.protocol_type == 'opcua' and 
                    existing.protocol_address == mapping.protocol_address):
                    errors.append(f"OPC UA node {mapping.protocol_address} already used by {existing.station_id}/{existing.pump_id}")
        
        return {
            'valid': len(errors) == 0,
            'errors': errors,
            'warnings': warnings
        }

Task 6: Integrate Configuration Manager with Protocol Servers

Priority: High Estimated Effort: 2 days

Implementation Details:

# File: src/protocols/modbus_server.py

class ModbusServer:
    def __init__(self, setpoint_manager, configuration_manager):
        self.setpoint_manager = setpoint_manager
        self.configuration_manager = configuration_manager
        
    async def _update_registers(self):
        """Update registers using configured mappings"""
        modbus_mappings = self.configuration_manager.get_protocol_mappings('modbus_tcp')
        for mapping in modbus_mappings:
            try:
                # Get value from database/setpoint manager
                value = await self._get_mapped_value(mapping)
                # Apply transformations
                transformed_value = self._apply_transformations(value, mapping.transformation_rules)
                # Write to register
                self._write_register(mapping.protocol_address, transformed_value, mapping.modbus_config['register_type'])
            except Exception as e:
                logger.error(f"Failed to update mapping {mapping.id}: {str(e)}")

# File: src/protocols/opcua_server.py

class OPCUAServer:
    def __init__(self, configuration_manager):
        self.configuration_manager = configuration_manager
        
    async def update_nodes(self):
        """Update OPC UA nodes using configured mappings"""
        opcua_mappings = self.configuration_manager.get_protocol_mappings('opcua')
        for mapping in opcua_mappings:
            try:
                # Get value from database/setpoint manager
                value = await self._get_mapped_value(mapping)
                # Apply transformations
                transformed_value = self._apply_transformations(value, mapping.transformation_rules)
                # Write to node
                await self._write_node(mapping.protocol_address, transformed_value)
            except Exception as e:
                logger.error(f"Failed to update mapping {mapping.id}: {str(e)}")

Task 7: Create Database Schema for Protocol Mappings

Priority: Medium Estimated Effort: 1 day

Implementation Details:

-- File: database/schema.sql

CREATE TABLE IF NOT EXISTS protocol_mappings (
    id VARCHAR(50) PRIMARY KEY,
    protocol_type VARCHAR(20) NOT NULL, -- modbus_tcp, opcua, custom
    station_id VARCHAR(50) NOT NULL,
    pump_id VARCHAR(50) NOT NULL,
    data_type VARCHAR(50) NOT NULL,
    protocol_address VARCHAR(200) NOT NULL, -- register address or OPC UA node
    db_source VARCHAR(200) NOT NULL,
    transformation_rules JSONB,
    
    -- Protocol-specific configurations
    modbus_config JSONB,
    opcua_config JSONB,
    
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (station_id, pump_id) REFERENCES pumps(station_id, pump_id)
);

CREATE INDEX idx_protocol_mappings_type ON protocol_mappings(protocol_type);
CREATE INDEX idx_protocol_mappings_station_pump ON protocol_mappings(station_id, pump_id);
CREATE INDEX idx_protocol_mappings_address ON protocol_mappings(protocol_address);

Task 8: Add Protocol-Specific Unit Tests

Priority: Medium Estimated Effort: 1.5 days

Implementation Details:

# File: tests/unit/test_protocol_mapping.py

class TestProtocolMapping(unittest.TestCase):
    def test_modbus_address_conflict_detection(self):
        """Test that Modbus address conflicts are properly detected"""
        config_manager = ConfigurationManager()
        
        mapping1 = ProtocolMapping(
            id="test1", protocol_type="modbus_tcp", station_id="STATION_001", pump_id="PUMP_001",
            data_type="setpoint", protocol_address="40001", db_source="pump_plans.speed_hz"
        )
        
        mapping2 = ProtocolMapping(
            id="test2", protocol_type="modbus_tcp", station_id="STATION_001", pump_id="PUMP_002", 
            data_type="setpoint", protocol_address="40001", db_source="pump_plans.speed_hz"
        )
        
        config_manager.add_protocol_mapping(mapping1)
        result = config_manager.validate_protocol_mapping(mapping2)
        
        self.assertFalse(result['valid'])
        self.assertIn("Modbus address 40001 already used", result['errors'][0])
    
    def test_opcua_node_validation(self):
        """Test OPC UA node validation"""
        config_manager = ConfigurationManager()
        
        mapping = ProtocolMapping(
            id="test1", protocol_type="opcua", station_id="STATION_001", pump_id="PUMP_001",
            data_type="setpoint", protocol_address="invalid_node", db_source="pump_plans.speed_hz"
        )
        
        result = config_manager.validate_protocol_mapping(mapping)
        self.assertFalse(result['valid'])
        self.assertIn("OPC UA Node ID must start with 'ns='", result['errors'][0])

Task 9: Add Single Protocol Mapping Tab to Dashboard

Priority: Low Estimated Effort: 0.5 days

Implementation Details:

// File: static/dashboard.js

// Update tab navigation - Add single Protocol Mapping tab
function updateNavigation() {
    const tabButtons = document.querySelector('.tab-buttons');
    tabButtons.innerHTML += `
        <button class="tab-button" onclick="showTab('protocol-mapping')">Protocol Mapping</button>
    `;
}

// Add Protocol Mapping tab content
function addProtocolMappingTab() {
    const tabContainer = document.querySelector('.tab-container');
    tabContainer.innerHTML += `
        <!-- Protocol Mapping Tab -->
        <div id="protocol-mapping-tab" class="tab-content">
            <h2>Protocol Mapping Configuration</h2>
            <div class="protocol-selector">
                <button class="protocol-btn active" onclick="selectProtocol('modbus')">Modbus</button>
                <button class="protocol-btn" onclick="selectProtocol('opcua')">OPC UA</button>
                <button class="protocol-btn" onclick="selectProtocol('all')">All Protocols</button>
            </div>
            <div id="protocol-mapping-content">
                <!-- Unified protocol mapping interface will be loaded here -->
            </div>
        </div>
    `;
}

// Protocol switching within the single tab
function selectProtocol(protocol) {
    // Update active protocol button
    document.querySelectorAll('.protocol-btn').forEach(btn => btn.classList.remove('active'));
    event.target.classList.add('active');
    
    // Load protocol-specific content
    loadProtocolMappings(protocol);
}

Task 10: Implement Protocol Discovery Features

Priority: Medium Estimated Effort: 2 days

Implementation Details:

# File: src/dashboard/api.py

@dashboard_router.post("/protocol-mappings/modbus/discover")
async def discover_modbus_registers():
    """Auto-discover available Modbus registers"""
    try:
        # Scan for available registers
        discovered_registers = await modbus_client.scan_registers()
        return {"success": True, "registers": discovered_registers}
    except Exception as e:
        logger.error(f"Failed to discover Modbus registers: {str(e)}")
        raise HTTPException(status_code=500, detail=f"Discovery failed: {str(e)}")

@dashboard_router.post("/protocol-mappings/opcua/browse")
async def browse_opcua_nodes():
    """Browse OPC UA server for available nodes"""
    try:
        # Browse OPC UA server
        nodes = await opcua_client.browse_nodes()
        return {"success": True, "nodes": nodes}
    except Exception as e:
        logger.error(f"Failed to browse OPC UA nodes: {str(e)}")
        raise HTTPException(status_code=500, detail=f"Browse failed: {str(e)}")

🔄 Integration Points

Existing System Integration

  1. Configuration Manager: Extend existing class with unified protocol mapping support
  2. Protocol Servers: Inject configuration manager and use configured mappings (Modbus, OPC UA)
  3. Dashboard API: Add unified protocol mapping endpoints alongside existing configuration endpoints
  4. Dashboard UI: Add single Protocol Mapping tab with protocol switching
  5. Database: Add unified table for persistent storage of all protocol mappings

Data Flow Changes

Current: Database → Setpoint Manager → Hardcoded Mapping → Protocol Servers
New:     Database → Setpoint Manager → Unified Configurable Mapping → Protocol Servers
                                    ↑
                            Unified Configuration Manager

Dashboard Integration

┌─────────────────────────────────────────────────────────────────┐
│                    DASHBOARD NAVIGATION                         │
├─────────────────────────────────────────────────────────────────┤
│ [Status] [Config] [SCADA] [Signals] [Protocol Mapping] [Logs]   │
└─────────────────────────────────────────────────────────────────┘

Within Protocol Mapping Tab:
┌─────────────────────────────────────────────────────────────────┐
│                    PROTOCOL MAPPING                             │
├─────────────────────────────────────────────────────────────────┤
│  [Modbus] [OPC UA] [All Protocols] ← Protocol Selector          │
│                                                                 │
│  Unified Mapping Grid & Configuration Forms                     │
└─────────────────────────────────────────────────────────────────┘

🧪 Testing Strategy

Test Scenarios

  1. Protocol Configuration Validation: Test address conflicts, data type compatibility across protocols
  2. Integration Testing: Test that configured mappings are applied correctly to all protocol servers
  3. Protocol-Specific Testing: Test Modbus register mapping and OPC UA node mapping separately
  4. Performance Testing: Test impact on protocol server performance

Test Data

  • Create test mappings for different protocols and scenarios
  • Test edge cases (address boundaries, data type conversions, protocol-specific rules)
  • Test cross-protocol conflict scenarios

📊 Success Metrics

Functional Requirements

  • Users can configure database-to-protocol mappings through dashboard
  • System uses configured mappings for all supported protocols
  • Protocol-specific validation prevents configuration conflicts
  • Mappings are persisted across application restarts
  • Support for multiple protocols (Modbus, OPC UA) with unified interface

Performance Requirements

  • ⏱️ Mapping configuration response time < 500ms
  • ⏱️ Protocol server update performance maintained
  • 💾 Memory usage increase < 15MB for typical multi-protocol configurations

🚨 Risk Mitigation

Technical Risks

  1. Performance Impact: Monitor protocol server update times, optimize if needed
  2. Configuration Errors: Implement comprehensive protocol-specific validation
  3. Protocol Compatibility: Ensure consistent behavior across different protocols

Implementation Risks

  1. Scope Creep: Stick to Phase 1 requirements only
  2. Integration Issues: Test thoroughly with existing protocol servers
  3. Data Loss: Implement backup/restore for mapping configurations

📅 Estimated Timeline

Total Phase 1 Effort: 18.5 days

Week Tasks Deliverables
1 Tasks 1-3 Configuration manager, API endpoints, multi-protocol UI
2 Tasks 4-6 Grid view, protocol-specific validation, server integration
3 Tasks 7-10 Database schema, tests, navigation, discovery features

🎯 Next Steps After Phase 1

  1. User Testing: Gather feedback from operators on multi-protocol interface
  2. Bug Fixing: Address any issues discovered in production
  3. Phase 2 Planning: Begin design for enhanced features (drag & drop, templates, bulk operations)

This implementation plan provides a detailed roadmap for delivering Phase 1 of the Protocol Mapping feature, supporting multiple industrial protocols with a unified interface. Each task includes specific implementation details and integration points with the existing system.