gNB-EMC-dashboard

gNB-EMS Dashboard Developer Guide

Version 1.1 - Multi-Board Architecture


Table of Contents

  1. Acknowledgements
  2. Setting up / Getting started
    2.1. Frontend Setup
    2.2. Backend Setup
  3. Architecture Overview
    3.1. Multi-Board Architecture
    3.2. System Components
    3.3. Data Flow
  4. Design
    4.1. Frontend Components
    4.2. Backend Components
    4.3. Board Factory System
    4.4. Network Scanning System
  5. Implementation
    5.1. Node status monitoring
    5.2. Network Discovery & Scanning
    5.3. Dashboard visualization
    5.4. Node control (start/stop)
    5.5. Real-time data updates
    5.6. Persistent node configuration
    5.7. Multi-Board Support
  6. API Documentation
  7. Development Workflows
    7.1. Adding New Board Types
    7.2. Testing & Debugging
    7.3. Deployment
  8. Design considerations
  9. Appendix: Requirements

Acknowledgements


Setting up / Getting started

Frontend Setup

  1. Navigate to the frontend directory
     cd frontend
    
  2. Install dependencies:
    npm install
    
  3. Install additional mapping dependencies:
    npm install leaflet
    
  4. Start dev server:
    npm start
    

    ✅ Dashboard will be available at http://localhost:3000

Backend Setup

Automatic Setup (Recommended):

  1. Ensure Python 3.9+ is installed:
    python3 --version
    
  2. Navigate to project root and run:
    python3 WebDashboard.py
    

    ✅ The system automatically installs dependencies and starts the Flask server

Manual Setup (If needed):

  1. Install dependencies manually:
    cd backend/dependencies/flask_pkgs
    pip3 install *.whl --no-deps
    cd ../pexpect_pkgs
    pip3 install *.whl --no-deps
    cd ../pytest_pkgs
    pip3 install *.whl --no-deps
    
  2. Run Flask API with board selection:
    python3 WebDashboard.py --edgeq  # Explicit EdgeQ board
    python3 WebDashboard.py         # Auto-detect board type
    

Architecture Overview

Multi-Board Architecture

The dashboard implements an extensible multi-board architecture supporting different 5G gNB hardware platforms:

┌─────────────────────────────────────────────────────────────────┐
│                    Frontend (React SPA)                         │
├─────────────────────────────────────────────────────────────────┤
│  Components: HomePage | NodeDashboard | Map | Sidebar           │
│  Features: Network Scanner | Real-time Updates | Controls       │
└─────────────────────────────────────────────────────────────────┘
                                 │ HTTP/REST API
┌─────────────────────────────────────────────────────────────────┐
│                 Backend (Flask + Multi-Board)                   │
├─────────────────────────────────────────────────────────────────┤
│  WebDashboard.py (Entry Point + Auto-Setup)                     │
│  ├── BoardFactory (Auto-Detection & Board Creation)             │
│  ├── Flask.py (REST API Routes)                                 │
│  └── ConfigManager (Board-Specific Configuration)               │
└─────────────────────────────────────────────────────────────────┘
                                 │
┌─────────────────────────────────────────────────────────────────┐
│                      Board Implementations                      │
├─────────────────────────────────────────────────────────────────┤
│  BaseBoard (Abstract)                                           │
│  ├── EdgeQBoard (EdgeQ Implementation)                          │
│  ├── [Future Board Types]                                       │
│  └── Board-Specific: Attributes | Commands | Configs            │
└─────────────────────────────────────────────────────────────────┘
                                 │
┌─────────────────────────────────────────────────────────────────┐
│                        Hardware Layer                           │
├─────────────────────────────────────────────────────────────────┤
│  EdgeQ gNB Hardware | Other 5G Platforms | MANET Devices        │
└─────────────────────────────────────────────────────────────────┘

System Components

Frontend Components

Backend Components

Data Flow

  1. Initialization:
    • WebDashboard.py detects board type and initializes appropriate board instance
    • Flask server starts with board-specific configuration
    • Frontend connects and begins polling
  2. Real-time Monitoring:
    • Frontend polls /api/attributes and /api/node_status (5s)
    • Board-specific attribute classes collect hardware data
    • JSON responses sent to frontend for visualization
  3. Network Discovery:
    • Frontend NetworkScanner performs dual-sweep scanning (Health + MANET APIs every 20s)
    • Discovered devices categorized by type (gNB/MANET)
    • Users can add discovered devices to saved nodes
  4. Node Control:
    • Frontend sends control commands via /api/setup_script
    • Backend uses board-specific setup commands
    • Real-time feedback provided to user

Design

Frontend Components

App.js - Main Application

// Key state management
const [allNodeData, setAllNodeData] = useState([]);
const [autoDiscoveredNodes, setAutoDiscoveredNodes] = useState([]);
const [isNetworkScanning, setIsNetworkScanning] = useState(false);
const [subnet, setSubnet] = useState('192.168.1');

NetworkScanner - Device Discovery

Backend Components

BoardFactory - Multi-Board Support

class BoardFactory:
    AVAILABLE_BOARDS = {
        'edgeq': EdgeQBoard
        # Future board types added here
    }
    
    @staticmethod
    def create_board(board_type: str = None):
        if board_type is None:
            board_type = BoardFactory.detect_board_type()
        return BoardFactory.AVAILABLE_BOARDS[board_type.lower()]()

BaseBoard - Abstract Interface

class BaseBoard(ABC):
    @abstractmethod
    def get_board_name(self) -> str: pass
    
    @abstractmethod  
    def get_board_config(self) -> Dict[str, Any]: pass
    
    @abstractmethod
    def create_attributes(self): pass
    
    @abstractmethod
    def ensure_config_exists(self) -> bool: pass

EdgeQBoard - Concrete Implementation

Board Factory System

The board factory system enables support for multiple hardware platforms:

  1. Board Creation: Factory pattern creates appropriate board instances
  2. Unified Interface: All boards implement the same abstract interface
  3. Extensibility: New boards can be added without modifying existing code

Network Scanning System

The network discovery system performs intelligent device detection:

  1. Health API Scan: Detects gNB nodes at http://[ip]:5000/api/health
  2. MANET API Scan: Detects mesh devices at http://[ip]/status?content=temp
  3. Batch Processing: Scans multiple IPs concurrently with rate limiting
  4. Smart Categorization: Distinguishes between gNB and MANET devices
  5. Integration: Seamlessly integrates discovered devices into node management

Implementation

Node status monitoring

Frontend Implementation:

  1. App.js manages multiple polling intervals:
    • Attributes: 5-second interval for performance metrics
    • Status: 5-second interval for operational state
    • MANET: 20-second interval for mesh connectivity
    • Network Scanning: 20-second automatic discovery
  2. State Management:
    // Real-time updates flow via state
    useEffect(() => {
      const attributeInterval = setInterval(async () => {
        // Update CPU, RAM, etc. for all nodes
      }, 5000);
    }, [allNodeData]);
    

Backend Implementation: Board-specific attribute classes collect real-time data:

# EdgeQ example
class EdgeQCpuUsage(BaseAttribute):
    def refresh(self):
        # EdgeQ-specific CPU monitoring
        pass

Network Discovery & Scanning

Frontend NetworkScanner:

class NetworkScanner {
  async scanSubnet(subnet, onProgress, onNodeFound) {
    // Dual-sweep: Health API + MANET API
    await this.performSweep(allIPs, 'health', onProgress, onNodeFound);
    await this.performSweep(allIPs, 'manet', onProgress, onNodeFound);
  }
}

Integration with Sidebar:

Dashboard visualization

Performance Charts:

Interactive Map:

Node control (start/stop)

Frontend Control Flow:

const handleNodeControl = async (action) => {
  setLoading(true);
  try {
    const response = await fetch('/api/setup_script', {
      method: 'POST',
      body: JSON.stringify({ action })
    });
    // Handle response and update UI
  } finally {
    setLoading(false);
  }
};

Backend Control Flow:

  1. Board factory determines appropriate commands
  2. Board-specific setup commands executed
  3. Real-time output streamed to response
  4. Timeout handling for long operations

Real-time data updates

Data Flow Architecture:

Persistent node configuration

Client-Side Persistence:

// Save to localStorage
const saveNodes = (nodes) => {
  localStorage.setItem('nodes', JSON.stringify(nodes));
};

// Load on app startup
const loadNodes = () => {
  const saved = localStorage.getItem('nodes');
  return saved ? JSON.parse(saved) : [];
};

Configuration includes:

Multi-Board Support

Adding New Board Types:

  1. Create new board class inheriting from BaseBoard
  2. Implement required abstract methods
  3. Add to BoardFactory.AVAILABLE_BOARDS
  4. Add command-line argument support
  5. Test with new hardware platform

Board-Specific Features:


API Documentation

The backend Flask service provides REST API endpoints for monitoring and controlling gNB nodes. This API enables external applications and services to integrate with the gNB Dashboard functionality.

Interactive Swagger Documentation (Flasgger)

For comprehensive interactive API documentation with “Try it out” functionality, see the Flasgger Documentation System:

📖 Complete Setup Guide

Quick Start:

  1. Navigate to docs_generator/
  2. Double-click start_docs.bat (Windows) or run python swagger_docs.py
  3. Access interactive documentation at: http://localhost:8080/docs/

Features:

Base URL

http://<gnb-board-ip>:5000/api

Endpoints

GET /api/board-info

Get current board information and configuration details.

Response Schema:

Name JSON Attribute Type Remarks
Board Type board_type string Type of board hardware (e.g., “EdgeQ”)
Config Path config_path string Absolute path to configuration file on board
Log Directory log_directory string Directory where system logs are stored
Available Files available_files array[string] List of downloadable file keys
Timeouts timeouts object Configuration timeout values
Raptor Status Timeout timeouts.raptor_status number Timeout for status checks in seconds
Setup Max Wait timeouts.setup_max_wait number Maximum wait time for setup operations

Example:

curl -X GET http://192.168.1.100:5000/api/board-info

GET /api/attributes

Get comprehensive system attributes and real-time metrics.

Response Schema:

Name JSON Attribute Type Remarks
gNB ID gnb_id string Unique identifier for the gNodeB
gNB ID Length gnb_id_length string Length specification for gNodeB identifier
NR Band nr_band string 5G NR frequency band (e.g., “n78”)
Subcarrier Spacing scs string Subcarrier spacing configuration
TX Power tx_power string Transmission power level in dBm
Downlink Frequency frequency_down_link string Downlink center frequency in Hz
gNB IP Address ip_address_gnb string IP address of the gNodeB interface
NGC IP Address ip_address_ngc string Next Generation Core IP address
NGU IP Address ip_address_ngu string NG User plane interface IP address
Mobile Country Code MCC string Mobile Country Code for network identification
Mobile Network Code MNC string Mobile Network Code for operator identification
Cell ID cell_id string Unique cell identifier within the network
NR TAC nr_tac string 5G NR Tracking Area Code
Slice Service Type sst string Network slice service type identifier
Slice Differentiator sd string Network slice differentiator value
Profile profile string Active configuration profile name
CPU Usage cpu_usage number Current CPU utilization percentage (0-100)
CPU Usage History cpu_usage_history array[number] Historical CPU usage data points
CPU Temperature cpu_temp number Current CPU temperature in Celsius
RAM Usage ram_usage number Current RAM utilization percentage (0-100)
RAM Usage History ram_usage_history array[number] Historical RAM usage data points
RAM Total ram_total number Total system RAM in megabytes
Drive Total drive_total number Total disk space in gigabytes
Drive Used drive_used number Used disk space in gigabytes
Drive Free drive_free number Available disk space in gigabytes
Board Date board_date string Current system date on the board
Board Time board_time string Current system time on the board
Core Connection core_connection string Status of connection to core network

Example:

curl -X GET http://192.168.1.100:5000/api/attributes

GET /api/node_status

Get current operational status of the gNB node.

Response Schema:

Name JSON Attribute Type Remarks
Node Status node_status string Current operational state: OFF, INITIALISING, or RUNNING

Example:

curl -X GET http://192.168.1.100:5000/api/node_status

POST /api/setup_script

Execute node control commands for managing gNB operations.

⏱️ Expected Execution Times:

Request Body Schema:

Name JSON Attribute Type Remarks
Action action string Command to execute: “start”, “stop”, “status”, or “setupv2”

Response Schema (Success):

Name JSON Attribute Type Remarks
Action action string Echo of the executed action command
Status status string Result status: “ok” for successful start/setup, “completed” for stop/status
Output output string Command execution output and logs from log file
Log File log_file string Path to the generated log file
Exit Code exit_code number Process exit code (0 = success)

Response Schema (Error):

Name JSON Attribute Type Remarks
Action action string Echo of the attempted action command
Error error string Error type: “timeout”, “process_terminated_unexpectedly”, etc.
Details details string Detailed error description and context
Output output string Partial command output before failure
Exit Code exit_code number Process exit code (-1 for timeouts, actual code for process failures)

Examples:

# Start the gNB node (takes ~2 minutes)
curl -X POST http://192.168.1.100:5000/api/setup_script \
  -H "Content-Type: application/json" \
  -d '{"action": "start"}'

# Stop the gNB node (takes 5-10 seconds)
curl -X POST http://192.168.1.100:5000/api/setup_script \
  -H "Content-Type: application/json" \
  -d '{"action": "stop"}'

Important Notes:

POST /api/config

Update gNB configuration parameters dynamically.

Request Body Schema:

Name JSON Attribute Type Remarks
Field field string Configuration parameter name to update (see valid fields below)
Value value string New value for the configuration parameter

Valid Configuration Fields:

Field Name Type Category Description Example Value
gNBId string Radio gNodeB identifier “001”
gNBIdLength string Radio Length specification for gNodeB ID “24”
band string Radio 5G NR frequency band “n78”
scs string Radio Subcarrier spacing configuration “30kHz”
txMaxPower string Radio Maximum transmission power in dBm “23.0”
dl_centre_freq string Radio Downlink center frequency in Hz “3500000000”
gnbIP string Core gNodeB IP address “192.168.1.50”
n3_local_ip string Core N3 interface local IP address “192.168.1.51”
n2_local_ip string Core N2 interface local IP address “192.168.1.52”
n3_remote_ip string Core N3 interface local IP address “192.168.1.10”
n2_remote_ip string Core N2 interface local IP address “192.168.1.11”
MCC string Core Mobile Country Code “001”
MNC string Core Mobile Network Code “01”
cellId string Core Cell identifier “1”
nrTAC string Core 5G NR Tracking Area Code “1”
sst string Core Network slice service type “1”
sd string Core Network slice differentiator “000001”

Response Schema:

Name JSON Attribute Type Remarks
Status status string Operation result: “success” or “error”
Message message string Descriptive message about the operation result

Examples:

# Update gNB IP configuration
curl -X POST http://192.168.1.100:5000/api/config \
  -H "Content-Type: application/json" \
  -d '{"field": "gnbIP", "value": "192.168.1.50"}'

# Update gNB ID Length
curl -X POST http://192.168.1.100:5000/api/config \
  -H "Content-Type: application/json" \
  -d '{"field": "gNBIdLength", "value": "28"}'

# Update NR Band
curl -X POST http://192.168.1.100:5000/api/config \
  -H "Content-Type: application/json" \
  -d '{"field": "band", "value": "n77"}'

Important Notes:

GET /api/download/

Download board-specific files and logs.

Parameters:

Response: File download (binary content) or error message

Error Response Schema:

Name JSON Attribute Type Remarks
Error error string Error description for invalid file keys
Available Files available_files array[string] List of valid file keys that can be downloaded

Example:

curl -X GET http://192.168.1.100:5000/api/download/log_file \
  -o downloaded_log.txt

Error Handling

All endpoints return appropriate HTTP status codes:

Standard Error Response Schema:

Name JSON Attribute Type Remarks
Error error string Brief error description
Details details string Additional error context and troubleshooting information
Status status string Always “error” for error responses

Rate Limiting

The API is designed for dashboard polling and control operations:

Integration Examples

Python Integration

import requests
import json

# Monitor node attributes
response = requests.get('http://192.168.1.100:5000/api/attributes')
if response.status_code == 200:
    data = response.json()
    print(f"CPU Usage: {data['cpu_usage']}%")
    print(f"RAM Usage: {data['ram_usage']}%")

# Start node
payload = {"action": "start"}
response = requests.post(
    'http://192.168.1.100:5000/api/setup_script',
    headers={'Content-Type': 'application/json'},
    data=json.dumps(payload)
)

JavaScript Integration

// Fetch node status
async function getNodeStatus() {
  try {
    const response = await fetch('http://192.168.1.100:5000/api/node_status');
    const data = await response.json();
    console.log('Node Status:', data.node_status);
  } catch (error) {
    console.error('Error fetching status:', error);
  }
}

// Update configuration
async function updateConfig(field, value) {
  try {
    const response = await fetch('http://192.168.1.100:5000/api/config', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ field, value })
    });
    const result = await response.json();
    console.log('Config update:', result);
  } catch (error) {
    console.error('Error updating config:', error);
  }
}

Architecture Diagram

Development Workflows

Adding New Board Types

Step 1: Create Board Implementation

# backend/boards/new_board.py
from .base_board import BaseBoard

class NewBoard(BaseBoard):
    def get_board_name(self) -> str:
        return "NewBoard"
    
    def get_board_config(self) -> Dict[str, Any]:
        return {
            "config_file_path": "/path/to/config",
            "setup_commands": {...},
            # Board-specific configuration
        }
    
    def create_attributes(self):
        return {
            'cpu_usage': NewBoardCpuUsage(),
            'custom_attr': NewBoardCustomAttr(),
            # Board-specific attributes
        }

Step 2: Register with BoardFactory

# backend/board_factory.py
from boards.new_board import NewBoard

class BoardFactory:
    AVAILABLE_BOARDS = {
        'edgeq': EdgeQBoard,
        'newboard': NewBoard  # Add new board
    }

Step 3: Add Detection Logic

@staticmethod
def detect_board_type():
    if os.path.exists("/opt/newboard/bin/control"):
        return "newboard"
    # Existing detection logic...

Step 4: Create Board-Specific Attributes

# backend/logic/newboard_attributes/NewBoardCpuUsage.py
from logic.shared_attributes.BaseAttribute import BaseAttribute

class NewBoardCpuUsage(BaseAttribute):
    def __init__(self):
        super().__init__()
        self.value = 0.0
        
    def refresh(self):
        # Board-specific CPU monitoring implementation
        pass

Step 5: Add Command-Line Support

# In board_factory.py parse_board_from_args()
board_group.add_argument('--newboard', action='store_true',
                        help='Use NewBoard configuration')

Testing & Debugging

Testing Overview:

The dashboard includes comprehensive testing for both frontend and backend components:

Frontend Testing:

Backend Testing:

📖 Complete Testing Documentation

Quick Testing Commands:

# Frontend testing
cd frontend
npm test

# Backend testing
cd backend
python3 -m pytest tests/

# Debug network scanning
# Check browser console for NetworkScanner logs
console.log("NetworkScanner: Starting scan...");

# Debug API endpoints
curl http://localhost:5000/api/board-info
curl http://localhost:5000/api/attributes

Testing Logs:

Important Notes:

Deployment

Development Environment:

# Frontend development
cd frontend && npm start

# Backend development with hot reload
cd backend && python3 WebDashboard.py --edgeq

Production Deployment:

  1. Frontend Build:
    cd frontend
    npm start
    
  2. Backend Package:
    # Ensure all dependencies are in backend/dependencies/
    ls backend/dependencies/
    # flask_pkgs/ pexpect_pkgs/ pytest_pkgs/
    
  3. Board Configuration:
    • Verify board-specific paths exist on target hardware
    • Test board detection and attribute collection
    • Validate setup commands work correctly
  4. Network Configuration:
    # Configure firewalls for required ports
    sudo ufw allow 3000/tcp  # Frontend
    sudo ufw allow 5000/tcp  # Backend API
    

Release Checklist:


Design considerations

Architecture Decisions

  1. Multi-Board Extensibility vs. Complexity
    • Choice: Factory pattern with abstract base classes
    • Rationale: Supports future hardware without breaking existing code
    • Trade-off: Increased complexity for single-board deployments
  2. Network Discovery vs. Manual Configuration
    • Choice: Dual-sweep automatic scanning with manual override
    • Rationale: Reduces operator workload while maintaining flexibility
    • Trade-off: Network overhead from scanning operations
  3. Real-time Performance vs. Resource Usage
    • Choice: Staggered polling intervals (5s/15s)
    • Rationale: Balances responsiveness with system load
    • Trade-off: Some latency in status updates
  4. Client-side vs. Server-side State Management
    • Choice: React state with localStorage persistence
    • Rationale: Responsive UI with offline capability
    • Trade-off: State synchronization complexity
  5. REST API vs. WebSockets
    • Choice: RESTful polling for most operations
    • Rationale: Simpler implementation and debugging
    • Trade-off: Higher network overhead than WebSockets

Performance Considerations

Frontend Optimizations:

Backend Optimizations:

Network Optimizations:

Security Considerations

API Security:

Network Security:

Board Access:

Scalability Considerations

Horizontal Scaling:

Vertical Scaling:

Future Extensibility:


Appendix: Requirements

Product scope

Target Users:

Value Proposition:

User stories

Priority Role Feature Benefit
*** Network Engineer See all node statuses Quickly spot failures across the network
*** Operator Start/stop nodes Remote control without physical access
*** Support Engineer View CPU/RAM/disk metrics Diagnose performance issues efficiently
*** Operator Network device discovery Automatically find new equipment
** Network Engineer Historical trends Identify recurring issues and patterns
** Operator Add/remove nodes Flexible dashboard configuration
** Support Engineer MANET GPS tracking Visualize mesh network topology
** System Integrator API access Integrate with existing systems
* Support Engineer Download logs Advanced troubleshooting capabilities
* Network Engineer Multi-board support Manage heterogeneous networks

Use cases

Use Case: UC01 - Monitor Node Status

Main Success Scenario (MSS):

  1. User opens the dashboard homepage
  2. System displays all configured nodes with their current status
  3. System continually updates the status automatically every 3 seconds
  4. Use case ends

Extensions:

Use Case: UC02 - Discover Network Devices

MSS:

  1. User configures network subnet in sidebar
  2. User triggers network scan or system performs automatic scan
  3. System performs dual-sweep scan (Health API + MANET API)
  4. System displays discovered devices categorized by type
  5. User adds desired devices to saved nodes
  6. Use case ends

Extensions:

Use Case: UC03 - Assign MANET to Node

MSS:

  1. User performs network scan that discovers MANET devices
  2. User clicks add button next to MANET device
  3. System opens MANET assignment dialog
  4. User selects existing gNB node for assignment
  5. System assigns MANET IP to selected node
  6. Use case ends

Extensions:

Use Case: UC04 - View Node Details

MSS:

  1. User clicks on a node card on the homepage
  2. System navigates to the node dashboard view
  3. System displays detailed metrics, controls, and real-time charts
  4. System updates metrics automatically every 1-3 seconds
  5. Use case ends

Extensions:

Use Case: UC05 - Control Node Operations

MSS:

  1. User navigates to a node’s dashboard
  2. User clicks the “Turn On” or “Turn Off” button
  3. System sends board-specific command to the node
  4. System displays operation progress with real-time output
  5. System updates the node status when operation completes
  6. Use case ends

Extensions:

Use Case: UC06 - Add Node to Dashboard

MSS:

  1. User enters a node IP in the sidebar form
  2. User clicks “Add” or presses Enter
  3. System creates new NodeInfo instance with board detection
  4. System adds the node to the tracked list
  5. System begins polling the node status and attributes
  6. Use case ends

Extensions:

Non-Functional Requirements

  1. Performance Requirements
    • Real-time updates with < 5 seconds latency
    • Support for ≥ 20 concurrent nodes
    • Network scanning completes within 20 seconds
    • Dashboard responsive on 1920x1080 displays
  2. Reliability Requirements
    • 90% uptime for monitoring functions
    • Graceful recovery from network errors
    • Automatic reconnection for lost connections
    • Data integrity during system failures
  3. Usability Requirements
    • Intuitive interface requiring < 30 minutes training
    • Responsive design supporting desktop and tablet
    • Consistent UI patterns across all pages
    • Comprehensive error messages and help text
  4. Compatibility Requirements
    • Modern web browsers (Chrome, Firefox, Safari, Edge)
    • Python 3.9+ for backend systems
    • Linux-based gNB hardware platforms
    • Network protocols: HTTP/HTTPS, TCP/IP
  5. Security Requirements
    • Input validation on all API endpoints
    • Secure communication protocols
    • Access logging for audit trails
    • No hardcoded credentials
  6. Maintainability Requirements
    • Modular architecture supporting new board types
    • Comprehensive API documentation
    • Automated dependency management
    • Clear separation of concerns

Glossary

Technical Terms:

System Terms:

API Terms:


References