diff --git a/docs/api_reference.md b/docs/api_reference.md new file mode 100644 index 0000000..7dab682 --- /dev/null +++ b/docs/api_reference.md @@ -0,0 +1,931 @@ +# WiFi-DensePose API Reference + +## Table of Contents + +1. [Overview](#overview) +2. [Authentication](#authentication) +3. [Base URL and Versioning](#base-url-and-versioning) +4. [Request/Response Format](#requestresponse-format) +5. [Error Handling](#error-handling) +6. [Rate Limiting](#rate-limiting) +7. [Pose Estimation API](#pose-estimation-api) +8. [System Management API](#system-management-api) +9. [Health Check API](#health-check-api) +10. [WebSocket API](#websocket-api) +11. [Data Models](#data-models) +12. [SDK Examples](#sdk-examples) + +## Overview + +The WiFi-DensePose API provides comprehensive access to WiFi-based human pose estimation capabilities. The API follows REST principles and supports both synchronous HTTP requests and real-time WebSocket connections. + +### Key Features + +- **RESTful Design**: Standard HTTP methods and status codes +- **Real-time Streaming**: WebSocket support for live pose data +- **Authentication**: JWT-based authentication with role-based access +- **Rate Limiting**: Configurable rate limits to prevent abuse +- **Comprehensive Documentation**: OpenAPI/Swagger documentation +- **Error Handling**: Detailed error responses with actionable messages + +### API Capabilities + +- Real-time pose estimation from WiFi CSI data +- Historical pose data retrieval and analysis +- System health monitoring and diagnostics +- Multi-zone occupancy tracking +- Activity recognition and analytics +- System configuration and calibration + +## Authentication + +### JWT Authentication + +The API uses JSON Web Tokens (JWT) for authentication. Include the token in the `Authorization` header: + +```http +Authorization: Bearer +``` + +### Obtaining a Token + +```bash +# Login to get JWT token +curl -X POST http://localhost:8000/api/v1/auth/login \ + -H "Content-Type: application/json" \ + -d '{ + "username": "your-username", + "password": "your-password" + }' +``` + +**Response:** +```json +{ + "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + "token_type": "bearer", + "expires_in": 86400 +} +``` + +### Token Refresh + +```bash +# Refresh expired token +curl -X POST http://localhost:8000/api/v1/auth/refresh \ + -H "Authorization: Bearer " +``` + +### Public Endpoints + +Some endpoints are publicly accessible without authentication: +- `GET /api/v1/health/*` - Health check endpoints +- `GET /api/v1/version` - Version information +- `GET /docs` - API documentation + +## Base URL and Versioning + +### Base URL +``` +http://localhost:8000/api/v1 +``` + +### API Versioning +The API uses URL path versioning. Current version is `v1`. + +### Content Types +- **Request**: `application/json` +- **Response**: `application/json` +- **WebSocket**: `application/json` messages + +## Request/Response Format + +### Standard Response Format + +```json +{ + "data": {}, + "timestamp": "2025-01-07T10:00:00Z", + "status": "success" +} +``` + +### Error Response Format + +```json +{ + "error": { + "code": "VALIDATION_ERROR", + "message": "Invalid request parameters", + "details": { + "field": "confidence_threshold", + "issue": "Value must be between 0.0 and 1.0" + } + }, + "timestamp": "2025-01-07T10:00:00Z", + "status": "error" +} +``` + +## Error Handling + +### HTTP Status Codes + +| Code | Description | +|------|-------------| +| 200 | Success | +| 201 | Created | +| 400 | Bad Request | +| 401 | Unauthorized | +| 403 | Forbidden | +| 404 | Not Found | +| 409 | Conflict | +| 422 | Validation Error | +| 429 | Rate Limited | +| 500 | Internal Server Error | +| 503 | Service Unavailable | + +### Error Codes + +| Code | Description | +|------|-------------| +| `VALIDATION_ERROR` | Request validation failed | +| `AUTHENTICATION_ERROR` | Authentication failed | +| `AUTHORIZATION_ERROR` | Insufficient permissions | +| `RESOURCE_NOT_FOUND` | Requested resource not found | +| `RATE_LIMIT_EXCEEDED` | Rate limit exceeded | +| `HARDWARE_ERROR` | Hardware communication error | +| `PROCESSING_ERROR` | Pose processing error | +| `CALIBRATION_ERROR` | System calibration error | + +## Rate Limiting + +### Default Limits +- **Authenticated users**: 1000 requests per hour +- **Anonymous users**: 100 requests per hour +- **WebSocket connections**: 10 concurrent per user + +### Rate Limit Headers +```http +X-RateLimit-Limit: 1000 +X-RateLimit-Remaining: 999 +X-RateLimit-Reset: 1641556800 +``` + +### Rate Limit Response +```json +{ + "error": { + "code": "RATE_LIMIT_EXCEEDED", + "message": "Rate limit exceeded. Try again in 60 seconds." + } +} +``` + +## Pose Estimation API + +### Get Current Pose Estimation + +Get real-time pose estimation from WiFi signals. + +```http +GET /api/v1/pose/current +``` + +**Query Parameters:** +- `zone_ids` (array, optional): Specific zones to analyze +- `confidence_threshold` (float, optional): Minimum confidence (0.0-1.0) +- `max_persons` (integer, optional): Maximum persons to detect (1-50) +- `include_keypoints` (boolean, optional): Include keypoint data (default: true) +- `include_segmentation` (boolean, optional): Include segmentation masks (default: false) + +**Example Request:** +```bash +curl "http://localhost:8000/api/v1/pose/current?confidence_threshold=0.7&max_persons=5" \ + -H "Authorization: Bearer " +``` + +**Response:** +```json +{ + "timestamp": "2025-01-07T10:00:00Z", + "frame_id": "frame_12345", + "persons": [ + { + "person_id": "person_001", + "confidence": 0.85, + "bounding_box": { + "x": 100, + "y": 150, + "width": 80, + "height": 180 + }, + "keypoints": [ + { + "name": "nose", + "x": 140, + "y": 160, + "confidence": 0.9 + } + ], + "zone_id": "zone_001", + "activity": "standing", + "timestamp": "2025-01-07T10:00:00Z" + } + ], + "zone_summary": { + "zone_001": 1, + "zone_002": 0 + }, + "processing_time_ms": 45.2 +} +``` + +### Analyze Pose Data + +Trigger pose analysis with custom parameters. + +```http +POST /api/v1/pose/analyze +``` + +**Request Body:** +```json +{ + "zone_ids": ["zone_001", "zone_002"], + "confidence_threshold": 0.8, + "max_persons": 10, + "include_keypoints": true, + "include_segmentation": false +} +``` + +**Response:** Same format as current pose estimation. + +### Get Zone Occupancy + +Get current occupancy for a specific zone. + +```http +GET /api/v1/pose/zones/{zone_id}/occupancy +``` + +**Path Parameters:** +- `zone_id` (string): Zone identifier + +**Example Request:** +```bash +curl "http://localhost:8000/api/v1/pose/zones/zone_001/occupancy" \ + -H "Authorization: Bearer " +``` + +**Response:** +```json +{ + "zone_id": "zone_001", + "current_occupancy": 3, + "max_occupancy": 10, + "persons": [ + { + "person_id": "person_001", + "confidence": 0.85, + "activity": "standing" + } + ], + "timestamp": "2025-01-07T10:00:00Z" +} +``` + +### Get Zones Summary + +Get occupancy summary for all zones. + +```http +GET /api/v1/pose/zones/summary +``` + +**Response:** +```json +{ + "timestamp": "2025-01-07T10:00:00Z", + "total_persons": 5, + "zones": { + "zone_001": { + "occupancy": 3, + "max_occupancy": 10, + "status": "normal" + }, + "zone_002": { + "occupancy": 2, + "max_occupancy": 8, + "status": "normal" + } + }, + "active_zones": 2 +} +``` + +### Get Historical Data + +Retrieve historical pose estimation data. + +```http +POST /api/v1/pose/historical +``` + +**Request Body:** +```json +{ + "start_time": "2025-01-07T00:00:00Z", + "end_time": "2025-01-07T23:59:59Z", + "zone_ids": ["zone_001"], + "aggregation_interval": 300, + "include_raw_data": false +} +``` + +**Response:** +```json +{ + "query": { + "start_time": "2025-01-07T00:00:00Z", + "end_time": "2025-01-07T23:59:59Z", + "zone_ids": ["zone_001"], + "aggregation_interval": 300 + }, + "data": [ + { + "timestamp": "2025-01-07T00:00:00Z", + "average_occupancy": 2.5, + "max_occupancy": 5, + "total_detections": 150 + } + ], + "total_records": 288 +} +``` + +### Get Detected Activities + +Get recently detected activities. + +```http +GET /api/v1/pose/activities +``` + +**Query Parameters:** +- `zone_id` (string, optional): Filter by zone +- `limit` (integer, optional): Maximum activities (1-100, default: 10) + +**Response:** +```json +{ + "activities": [ + { + "activity": "walking", + "person_id": "person_001", + "zone_id": "zone_001", + "confidence": 0.9, + "timestamp": "2025-01-07T10:00:00Z", + "duration_seconds": 15.5 + } + ], + "total_count": 1, + "zone_id": "zone_001" +} +``` + +### Calibrate System + +Start system calibration process. + +```http +POST /api/v1/pose/calibrate +``` + +**Response:** +```json +{ + "calibration_id": "cal_12345", + "status": "started", + "estimated_duration_minutes": 5, + "message": "Calibration process started" +} +``` + +### Get Calibration Status + +Check calibration progress. + +```http +GET /api/v1/pose/calibration/status +``` + +**Response:** +```json +{ + "is_calibrating": true, + "calibration_id": "cal_12345", + "progress_percent": 60, + "current_step": "phase_sanitization", + "estimated_remaining_minutes": 2, + "last_calibration": "2025-01-06T15:30:00Z" +} +``` + +### Get Pose Statistics + +Get pose estimation statistics. + +```http +GET /api/v1/pose/stats +``` + +**Query Parameters:** +- `hours` (integer, optional): Hours of data to analyze (1-168, default: 24) + +**Response:** +```json +{ + "period": { + "start_time": "2025-01-06T10:00:00Z", + "end_time": "2025-01-07T10:00:00Z", + "hours": 24 + }, + "statistics": { + "total_detections": 1500, + "average_confidence": 0.82, + "unique_persons": 25, + "average_processing_time_ms": 47.3, + "zones": { + "zone_001": { + "detections": 800, + "average_occupancy": 3.2 + } + } + } +} +``` + +## System Management API + +### System Status + +Get current system status. + +```http +GET /api/v1/system/status +``` + +**Response:** +```json +{ + "status": "running", + "uptime_seconds": 86400, + "services": { + "hardware": "healthy", + "pose_estimation": "healthy", + "streaming": "healthy" + }, + "configuration": { + "domain": "healthcare", + "max_persons": 10, + "confidence_threshold": 0.7 + }, + "timestamp": "2025-01-07T10:00:00Z" +} +``` + +### Start System + +Start the pose estimation system. + +```http +POST /api/v1/system/start +``` + +**Request Body:** +```json +{ + "configuration": { + "domain": "healthcare", + "environment_id": "room_001", + "calibration_required": true + } +} +``` + +### Stop System + +Stop the pose estimation system. + +```http +POST /api/v1/system/stop +``` + +### Restart System + +Restart the system with new configuration. + +```http +POST /api/v1/system/restart +``` + +### Get Configuration + +Get current system configuration. + +```http +GET /api/v1/config +``` + +### Update Configuration + +Update system configuration. + +```http +PUT /api/v1/config +``` + +**Request Body:** +```json +{ + "detection": { + "confidence_threshold": 0.8, + "max_persons": 8 + }, + "analytics": { + "enable_fall_detection": true + } +} +``` + +## Health Check API + +### Comprehensive Health Check + +Get detailed system health information. + +```http +GET /api/v1/health +``` + +**Response:** +```json +{ + "status": "healthy", + "timestamp": "2025-01-07T10:00:00Z", + "uptime_seconds": 86400, + "components": { + "hardware": { + "name": "Hardware Service", + "status": "healthy", + "message": "All routers connected", + "last_check": "2025-01-07T10:00:00Z", + "uptime_seconds": 86400, + "metrics": { + "connected_routers": 3, + "csi_data_rate": 30.5 + } + }, + "pose": { + "name": "Pose Service", + "status": "healthy", + "message": "Processing normally", + "last_check": "2025-01-07T10:00:00Z", + "metrics": { + "processing_rate": 29.8, + "average_latency_ms": 45.2 + } + } + }, + "system_metrics": { + "cpu": { + "percent": 65.2, + "count": 8 + }, + "memory": { + "total_gb": 16.0, + "available_gb": 8.5, + "percent": 46.9 + }, + "disk": { + "total_gb": 500.0, + "free_gb": 350.0, + "percent": 30.0 + } + } +} +``` + +### Readiness Check + +Check if system is ready to serve requests. + +```http +GET /api/v1/ready +``` + +**Response:** +```json +{ + "ready": true, + "timestamp": "2025-01-07T10:00:00Z", + "checks": { + "hardware_ready": true, + "pose_ready": true, + "stream_ready": true, + "memory_available": true, + "disk_space_available": true + }, + "message": "System is ready" +} +``` + +### Liveness Check + +Simple liveness check for load balancers. + +```http +GET /api/v1/live +``` + +**Response:** +```json +{ + "status": "alive", + "timestamp": "2025-01-07T10:00:00Z" +} +``` + +### System Metrics + +Get detailed system metrics. + +```http +GET /api/v1/metrics +``` + +### Version Information + +Get application version information. + +```http +GET /api/v1/version +``` + +**Response:** +```json +{ + "name": "WiFi-DensePose API", + "version": "1.0.0", + "environment": "production", + "debug": false, + "timestamp": "2025-01-07T10:00:00Z" +} +``` + +## WebSocket API + +### Connection + +Connect to WebSocket endpoint: + +```javascript +const ws = new WebSocket('ws://localhost:8000/ws/pose/stream'); +``` + +### Authentication + +Send authentication message after connection: + +```javascript +ws.send(JSON.stringify({ + type: 'auth', + token: 'your-jwt-token' +})); +``` + +### Subscribe to Pose Updates + +```javascript +ws.send(JSON.stringify({ + type: 'subscribe', + channel: 'pose_updates', + filters: { + zone_ids: ['zone_001'], + min_confidence: 0.7 + } +})); +``` + +### Pose Data Message + +```json +{ + "type": "pose_data", + "channel": "pose_updates", + "data": { + "timestamp": "2025-01-07T10:00:00Z", + "frame_id": "frame_12345", + "persons": [ + { + "person_id": "person_001", + "confidence": 0.85, + "bounding_box": { + "x": 100, + "y": 150, + "width": 80, + "height": 180 + }, + "zone_id": "zone_001" + } + ] + } +} +``` + +### System Events + +Subscribe to system events: + +```javascript +ws.send(JSON.stringify({ + type: 'subscribe', + channel: 'system_events' +})); +``` + +### Event Message + +```json +{ + "type": "system_event", + "channel": "system_events", + "data": { + "event_type": "fall_detected", + "person_id": "person_001", + "zone_id": "zone_001", + "confidence": 0.95, + "timestamp": "2025-01-07T10:00:00Z" + } +} +``` + +## Data Models + +### PersonPose + +```json +{ + "person_id": "string", + "confidence": 0.85, + "bounding_box": { + "x": 100, + "y": 150, + "width": 80, + "height": 180 + }, + "keypoints": [ + { + "name": "nose", + "x": 140, + "y": 160, + "confidence": 0.9, + "visible": true + } + ], + "segmentation": { + "mask": "base64-encoded-mask", + "body_parts": ["torso", "left_arm", "right_arm"] + }, + "zone_id": "zone_001", + "activity": "standing", + "timestamp": "2025-01-07T10:00:00Z" +} +``` + +### Keypoint Names + +Standard keypoint names following COCO format: +- `nose`, `left_eye`, `right_eye`, `left_ear`, `right_ear` +- `left_shoulder`, `right_shoulder`, `left_elbow`, `right_elbow` +- `left_wrist`, `right_wrist`, `left_hip`, `right_hip` +- `left_knee`, `right_knee`, `left_ankle`, `right_ankle` + +### Activity Types + +Supported activity classifications: +- `standing`, `sitting`, `walking`, `running`, `lying_down` +- `falling`, `jumping`, `bending`, `reaching`, `waving` + +### Zone Configuration + +```json +{ + "zone_id": "zone_001", + "name": "Living Room", + "coordinates": { + "x": 0, + "y": 0, + "width": 500, + "height": 300 + }, + "max_occupancy": 10, + "alerts_enabled": true, + "privacy_level": "high" +} +``` + +## SDK Examples + +### Python SDK + +```python +from wifi_densepose import WiFiDensePoseClient + +# Initialize client +client = WiFiDensePoseClient( + base_url="http://localhost:8000", + api_key="your-api-key" +) + +# Get current poses +poses = client.get_current_poses( + confidence_threshold=0.7, + max_persons=5 +) + +# Get historical data +history = client.get_historical_data( + start_time="2025-01-07T00:00:00Z", + end_time="2025-01-07T23:59:59Z", + zone_ids=["zone_001"] +) + +# Subscribe to real-time updates +def pose_callback(poses): + print(f"Received {len(poses)} poses") + +client.subscribe_to_poses(callback=pose_callback) +``` + +### JavaScript SDK + +```javascript +import { WiFiDensePoseClient } from 'wifi-densepose-js'; + +// Initialize client +const client = new WiFiDensePoseClient({ + baseUrl: 'http://localhost:8000', + apiKey: 'your-api-key' +}); + +// Get current poses +const poses = await client.getCurrentPoses({ + confidenceThreshold: 0.7, + maxPersons: 5 +}); + +// Subscribe to WebSocket updates +client.subscribeToPoses({ + onData: (poses) => { + console.log(`Received ${poses.length} poses`); + }, + onError: (error) => { + console.error('WebSocket error:', error); + } +}); +``` + +### cURL Examples + +```bash +# Get current poses +curl -X GET "http://localhost:8000/api/v1/pose/current?confidence_threshold=0.7" \ + -H "Authorization: Bearer " \ + -H "Content-Type: application/json" + +# Start system +curl -X POST "http://localhost:8000/api/v1/system/start" \ + -H "Authorization: Bearer " \ + -H "Content-Type: application/json" \ + -d '{ + "configuration": { + "domain": "healthcare", + "environment_id": "room_001" + } + }' + +# Get zone occupancy +curl -X GET "http://localhost:8000/api/v1/pose/zones/zone_001/occupancy" \ + -H "Authorization: Bearer " +``` + +--- + +For more information, see: +- [User Guide](user_guide.md) +- [Deployment Guide](deployment.md) +- [Troubleshooting Guide](troubleshooting.md) +- [Interactive API Documentation](http://localhost:8000/docs) \ No newline at end of file diff --git a/docs/deployment.md b/docs/deployment.md new file mode 100644 index 0000000..27ea1ac --- /dev/null +++ b/docs/deployment.md @@ -0,0 +1,1103 @@ +# WiFi-DensePose Deployment Guide + +## Table of Contents + +1. [Overview](#overview) +2. [Prerequisites](#prerequisites) +3. [Docker Deployment](#docker-deployment) +4. [Kubernetes Deployment](#kubernetes-deployment) +5. [Cloud Deployment](#cloud-deployment) +6. [Production Configuration](#production-configuration) +7. [Scaling and Load Balancing](#scaling-and-load-balancing) +8. [Monitoring and Observability](#monitoring-and-observability) +9. [Security Considerations](#security-considerations) +10. [Backup and Recovery](#backup-and-recovery) +11. [Troubleshooting](#troubleshooting) + +## Overview + +This guide covers deploying WiFi-DensePose in production environments, from single-node Docker deployments to large-scale Kubernetes clusters. The system is designed for high availability, scalability, and security. + +### Deployment Options + +- **Docker Compose**: Single-node development and small production deployments +- **Kubernetes**: Multi-node production deployments with auto-scaling +- **Cloud Platforms**: AWS, GCP, Azure with managed services +- **Edge Deployment**: IoT gateways and edge computing devices + +### Architecture Components + +``` +┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ +│ Load Balancer │ │ WiFi Routers │ │ Monitoring │ +│ (Nginx) │ │ (CSI Source) │ │ (Prometheus) │ +└─────────────────┘ └─────────────────┘ └─────────────────┘ + │ │ │ + ▼ ▼ ▼ +┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ +│ WiFi-DensePose │ │ Database │ │ Redis │ +│ API Servers │◄──►│ (PostgreSQL) │ │ (Cache) │ +│ (3+ replicas) │ │ (TimescaleDB) │ │ │ +└─────────────────┘ └─────────────────┘ └─────────────────┘ +``` + +## Prerequisites + +### System Requirements + +#### Minimum Requirements +- **CPU**: 4 cores, 2.4GHz +- **Memory**: 8GB RAM +- **Storage**: 100GB SSD +- **Network**: 1Gbps Ethernet + +#### Recommended Requirements +- **CPU**: 8+ cores, 3.0GHz +- **Memory**: 16GB+ RAM +- **Storage**: 500GB+ NVMe SSD +- **Network**: 10Gbps Ethernet +- **GPU**: NVIDIA GPU with 8GB+ VRAM (optional) + +### Software Dependencies + +#### Container Runtime +```bash +# Docker (20.10+) +curl -fsSL https://get.docker.com -o get-docker.sh +sudo sh get-docker.sh + +# Docker Compose (2.0+) +sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose +sudo chmod +x /usr/local/bin/docker-compose +``` + +#### Kubernetes (for K8s deployment) +```bash +# kubectl +curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" +sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl + +# Helm (3.0+) +curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash +``` + +## Docker Deployment + +### Single-Node Docker Compose + +#### 1. Download Configuration + +```bash +# Clone repository +git clone https://github.com/ruvnet/wifi-densepose.git +cd wifi-densepose + +# Copy environment template +cp .env.example .env +``` + +#### 2. Configure Environment + +Edit `.env` file: + +```bash +# Application Settings +APP_NAME=WiFi-DensePose API +VERSION=1.0.0 +ENVIRONMENT=production +DEBUG=false + +# Server Settings +HOST=0.0.0.0 +PORT=8000 +WORKERS=4 + +# Security (CHANGE THESE!) +SECRET_KEY=your-super-secret-key-change-this +JWT_SECRET=your-jwt-secret-change-this + +# Database +DATABASE_URL=postgresql://postgres:password@postgres:5432/wifi_densepose +REDIS_URL=redis://:password@redis:6379/0 + +# Hardware +WIFI_INTERFACE=wlan0 +CSI_BUFFER_SIZE=1000 +HARDWARE_POLLING_INTERVAL=0.1 + +# Features +ENABLE_AUTHENTICATION=true +ENABLE_RATE_LIMITING=true +ENABLE_WEBSOCKETS=true +``` + +#### 3. Deploy with Docker Compose + +```bash +# Start all services +docker-compose up -d + +# Check service status +docker-compose ps + +# View logs +docker-compose logs -f wifi-densepose + +# Scale API servers +docker-compose up -d --scale wifi-densepose=3 +``` + +#### 4. Verify Deployment + +```bash +# Health check +curl http://localhost:8000/api/v1/health + +# API documentation +open http://localhost:8000/docs +``` + +### Production Docker Compose + +Create `docker-compose.prod.yml`: + +```yaml +version: '3.8' + +services: + nginx: + image: nginx:alpine + ports: + - "80:80" + - "443:443" + volumes: + - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro + - ./nginx/ssl:/etc/nginx/ssl:ro + depends_on: + - wifi-densepose + restart: unless-stopped + + wifi-densepose: + image: wifi-densepose:latest + build: + context: . + target: production + environment: + - ENVIRONMENT=production + - WORKERS=4 + env_file: + - .env + volumes: + - ./data:/app/data + - ./logs:/app/logs + - ./models:/app/models + depends_on: + - postgres + - redis + restart: unless-stopped + deploy: + replicas: 3 + resources: + limits: + cpus: '2.0' + memory: 4G + reservations: + cpus: '0.5' + memory: 1G + + postgres: + image: postgres:15-alpine + environment: + POSTGRES_DB: wifi_densepose + POSTGRES_USER: postgres + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + volumes: + - postgres_data:/var/lib/postgresql/data + - ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro + restart: unless-stopped + deploy: + resources: + limits: + cpus: '1.0' + memory: 2G + + redis: + image: redis:7-alpine + command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD} + volumes: + - redis_data:/data + restart: unless-stopped + + prometheus: + image: prom/prometheus:latest + ports: + - "9090:9090" + volumes: + - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro + - prometheus_data:/prometheus + restart: unless-stopped + + grafana: + image: grafana/grafana:latest + ports: + - "3000:3000" + environment: + GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_PASSWORD} + volumes: + - grafana_data:/var/lib/grafana + - ./monitoring/grafana:/etc/grafana/provisioning:ro + restart: unless-stopped + +volumes: + postgres_data: + redis_data: + prometheus_data: + grafana_data: +``` + +## Kubernetes Deployment + +### 1. Prepare Kubernetes Cluster + +#### Create Namespace + +```bash +kubectl create namespace wifi-densepose +kubectl config set-context --current --namespace=wifi-densepose +``` + +#### Install Required Operators + +```bash +# Prometheus Operator +helm repo add prometheus-community https://prometheus-community.github.io/helm-charts +helm install prometheus prometheus-community/kube-prometheus-stack \ + --namespace monitoring --create-namespace + +# Ingress Controller +helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx +helm install ingress-nginx ingress-nginx/ingress-nginx \ + --namespace ingress-nginx --create-namespace +``` + +### 2. Configure Secrets and ConfigMaps + +#### Create Secrets + +```bash +# Database secrets +kubectl create secret generic postgres-secret \ + --from-literal=POSTGRES_DB=wifi_densepose \ + --from-literal=POSTGRES_USER=postgres \ + --from-literal=POSTGRES_PASSWORD=your-secure-password + +# Redis secrets +kubectl create secret generic redis-secret \ + --from-literal=REDIS_PASSWORD=your-redis-password + +# Application secrets +kubectl create secret generic wifi-densepose-secrets \ + --from-literal=SECRET_KEY=your-super-secret-key \ + --from-literal=JWT_SECRET=your-jwt-secret \ + --from-literal=DATABASE_URL=postgresql://postgres:password@postgres:5432/wifi_densepose \ + --from-literal=REDIS_URL=redis://:password@redis:6379/0 + +# TLS certificates +kubectl create secret tls tls-secret \ + --cert=path/to/tls.crt \ + --key=path/to/tls.key +``` + +#### Create ConfigMaps + +```bash +# Application configuration +kubectl create configmap wifi-densepose-config \ + --from-literal=ENVIRONMENT=production \ + --from-literal=LOG_LEVEL=INFO \ + --from-literal=WORKERS=4 \ + --from-literal=ENABLE_AUTHENTICATION=true \ + --from-literal=ENABLE_RATE_LIMITING=true + +# Nginx configuration +kubectl create configmap nginx-config \ + --from-file=nginx.conf=./k8s/nginx.conf + +# PostgreSQL initialization +kubectl create configmap postgres-init \ + --from-file=init.sql=./k8s/init.sql +``` + +### 3. Deploy Persistent Volumes + +```yaml +# k8s/pvc.yaml +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: wifi-densepose-data-pvc + namespace: wifi-densepose +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 100Gi + storageClassName: fast-ssd + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: wifi-densepose-models-pvc + namespace: wifi-densepose +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 50Gi + storageClassName: fast-ssd + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: postgres-data-pvc + namespace: wifi-densepose +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 200Gi + storageClassName: fast-ssd + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: redis-data-pvc + namespace: wifi-densepose +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 20Gi + storageClassName: fast-ssd +``` + +### 4. Deploy Application + +```bash +# Apply all Kubernetes manifests +kubectl apply -f k8s/pvc.yaml +kubectl apply -f k8s/deployment.yaml +kubectl apply -f k8s/service.yaml +kubectl apply -f k8s/ingress.yaml + +# Check deployment status +kubectl get pods -w +kubectl get services +kubectl get ingress +``` + +### 5. Configure Ingress + +```yaml +# k8s/ingress.yaml +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: wifi-densepose-ingress + namespace: wifi-densepose + annotations: + kubernetes.io/ingress.class: nginx + cert-manager.io/cluster-issuer: letsencrypt-prod + nginx.ingress.kubernetes.io/ssl-redirect: "true" + nginx.ingress.kubernetes.io/proxy-body-size: "50m" + nginx.ingress.kubernetes.io/rate-limit: "100" +spec: + tls: + - hosts: + - api.wifi-densepose.com + secretName: wifi-densepose-tls + rules: + - host: api.wifi-densepose.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: wifi-densepose-service + port: + number: 80 +``` + +## Cloud Deployment + +### AWS Deployment + +#### 1. EKS Cluster Setup + +```bash +# Install eksctl +curl --silent --location "https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_$(uname -s)_amd64.tar.gz" | tar xz -C /tmp +sudo mv /tmp/eksctl /usr/local/bin + +# Create EKS cluster +eksctl create cluster \ + --name wifi-densepose \ + --region us-west-2 \ + --nodegroup-name workers \ + --node-type m5.xlarge \ + --nodes 3 \ + --nodes-min 1 \ + --nodes-max 10 \ + --managed +``` + +#### 2. RDS Database + +```bash +# Create RDS PostgreSQL instance +aws rds create-db-instance \ + --db-instance-identifier wifi-densepose-db \ + --db-instance-class db.r5.large \ + --engine postgres \ + --engine-version 15.4 \ + --allocated-storage 100 \ + --storage-type gp2 \ + --storage-encrypted \ + --master-username postgres \ + --master-user-password your-secure-password \ + --vpc-security-group-ids sg-xxxxxxxxx \ + --db-subnet-group-name default \ + --backup-retention-period 7 \ + --multi-az +``` + +#### 3. ElastiCache Redis + +```bash +# Create ElastiCache Redis cluster +aws elasticache create-cache-cluster \ + --cache-cluster-id wifi-densepose-redis \ + --cache-node-type cache.r5.large \ + --engine redis \ + --num-cache-nodes 1 \ + --security-group-ids sg-xxxxxxxxx \ + --subnet-group-name default +``` + +### GCP Deployment + +#### 1. GKE Cluster Setup + +```bash +# Create GKE cluster +gcloud container clusters create wifi-densepose \ + --zone us-central1-a \ + --machine-type n1-standard-4 \ + --num-nodes 3 \ + --enable-autoscaling \ + --min-nodes 1 \ + --max-nodes 10 \ + --enable-autorepair \ + --enable-autoupgrade +``` + +#### 2. Cloud SQL + +```bash +# Create Cloud SQL PostgreSQL instance +gcloud sql instances create wifi-densepose-db \ + --database-version POSTGRES_15 \ + --tier db-n1-standard-2 \ + --region us-central1 \ + --storage-size 100GB \ + --storage-type SSD \ + --backup-start-time 02:00 +``` + +### Azure Deployment + +#### 1. AKS Cluster Setup + +```bash +# Create resource group +az group create --name wifi-densepose-rg --location eastus + +# Create AKS cluster +az aks create \ + --resource-group wifi-densepose-rg \ + --name wifi-densepose-aks \ + --node-count 3 \ + --node-vm-size Standard_D4s_v3 \ + --enable-addons monitoring \ + --generate-ssh-keys +``` + +#### 2. Azure Database for PostgreSQL + +```bash +# Create PostgreSQL server +az postgres server create \ + --resource-group wifi-densepose-rg \ + --name wifi-densepose-db \ + --location eastus \ + --admin-user postgres \ + --admin-password your-secure-password \ + --sku-name GP_Gen5_2 \ + --storage-size 102400 +``` + +## Production Configuration + +### Environment Variables + +```bash +# Production environment file +cat > .env.prod << EOF +# Application +APP_NAME=WiFi-DensePose API +VERSION=1.0.0 +ENVIRONMENT=production +DEBUG=false + +# Server +HOST=0.0.0.0 +PORT=8000 +WORKERS=4 + +# Security +SECRET_KEY=${SECRET_KEY} +JWT_SECRET=${JWT_SECRET} +JWT_ALGORITHM=HS256 +JWT_EXPIRE_HOURS=24 + +# Database +DATABASE_URL=${DATABASE_URL} +DATABASE_POOL_SIZE=20 +DATABASE_MAX_OVERFLOW=30 +DATABASE_POOL_TIMEOUT=30 + +# Redis +REDIS_URL=${REDIS_URL} +REDIS_POOL_SIZE=10 + +# Hardware +WIFI_INTERFACE=wlan0 +CSI_BUFFER_SIZE=2000 +HARDWARE_POLLING_INTERVAL=0.05 + +# Pose Processing +POSE_CONFIDENCE_THRESHOLD=0.7 +POSE_PROCESSING_BATCH_SIZE=64 +POSE_MAX_PERSONS=20 + +# Features +ENABLE_AUTHENTICATION=true +ENABLE_RATE_LIMITING=true +ENABLE_WEBSOCKETS=true +ENABLE_REAL_TIME_PROCESSING=true + +# Monitoring +ENABLE_METRICS=true +METRICS_PORT=8080 +LOG_LEVEL=INFO + +# Performance +ENABLE_GPU=true +MIXED_PRECISION=true +OPTIMIZE_FOR_INFERENCE=true +EOF +``` + +### Database Configuration + +#### PostgreSQL Optimization + +```sql +-- postgresql.conf optimizations +shared_buffers = 256MB +effective_cache_size = 1GB +maintenance_work_mem = 64MB +checkpoint_completion_target = 0.9 +wal_buffers = 16MB +default_statistics_target = 100 +random_page_cost = 1.1 +effective_io_concurrency = 200 +work_mem = 4MB +min_wal_size = 1GB +max_wal_size = 4GB + +-- TimescaleDB extension +CREATE EXTENSION IF NOT EXISTS timescaledb; + +-- Create hypertables for time-series data +SELECT create_hypertable('csi_data', 'timestamp'); +SELECT create_hypertable('pose_detections', 'timestamp'); +SELECT create_hypertable('system_metrics', 'timestamp'); + +-- Create indexes +CREATE INDEX idx_pose_detections_person_id ON pose_detections (person_id); +CREATE INDEX idx_pose_detections_zone_id ON pose_detections (zone_id); +CREATE INDEX idx_csi_data_router_id ON csi_data (router_id); +``` + +### Redis Configuration + +```bash +# redis.conf optimizations +maxmemory 2gb +maxmemory-policy allkeys-lru +save 900 1 +save 300 10 +save 60 10000 +appendonly yes +appendfsync everysec +``` + +## Scaling and Load Balancing + +### Horizontal Pod Autoscaler + +```yaml +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: wifi-densepose-hpa + namespace: wifi-densepose +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: wifi-densepose + minReplicas: 3 + maxReplicas: 20 + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 70 + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: 80 + behavior: + scaleDown: + stabilizationWindowSeconds: 300 + policies: + - type: Percent + value: 10 + periodSeconds: 60 + scaleUp: + stabilizationWindowSeconds: 60 + policies: + - type: Percent + value: 50 + periodSeconds: 60 +``` + +### Vertical Pod Autoscaler + +```yaml +apiVersion: autoscaling.k8s.io/v1 +kind: VerticalPodAutoscaler +metadata: + name: wifi-densepose-vpa + namespace: wifi-densepose +spec: + targetRef: + apiVersion: apps/v1 + kind: Deployment + name: wifi-densepose + updatePolicy: + updateMode: "Auto" + resourcePolicy: + containerPolicies: + - containerName: wifi-densepose + maxAllowed: + cpu: 4 + memory: 8Gi + minAllowed: + cpu: 500m + memory: 1Gi +``` + +### Load Balancer Configuration + +#### Nginx Configuration + +```nginx +upstream wifi_densepose_backend { + least_conn; + server wifi-densepose-1:8000 max_fails=3 fail_timeout=30s; + server wifi-densepose-2:8000 max_fails=3 fail_timeout=30s; + server wifi-densepose-3:8000 max_fails=3 fail_timeout=30s; +} + +server { + listen 80; + listen 443 ssl http2; + server_name api.wifi-densepose.com; + + # SSL configuration + ssl_certificate /etc/nginx/ssl/tls.crt; + ssl_certificate_key /etc/nginx/ssl/tls.key; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512; + + # Rate limiting + limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s; + limit_req zone=api burst=20 nodelay; + + # Gzip compression + gzip on; + gzip_types text/plain application/json application/javascript text/css; + + location / { + proxy_pass http://wifi_densepose_backend; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # WebSocket support + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + # Timeouts + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + } + + location /health { + access_log off; + proxy_pass http://wifi_densepose_backend; + } +} +``` + +## Monitoring and Observability + +### Prometheus Configuration + +```yaml +# monitoring/prometheus.yml +global: + scrape_interval: 15s + evaluation_interval: 15s + +rule_files: + - "wifi_densepose_rules.yml" + +scrape_configs: + - job_name: 'wifi-densepose' + static_configs: + - targets: ['wifi-densepose:8080'] + metrics_path: /metrics + scrape_interval: 10s + + - job_name: 'postgres' + static_configs: + - targets: ['postgres-exporter:9187'] + + - job_name: 'redis' + static_configs: + - targets: ['redis-exporter:9121'] + + - job_name: 'nginx' + static_configs: + - targets: ['nginx-exporter:9113'] +``` + +### Grafana Dashboards + +```json +{ + "dashboard": { + "title": "WiFi-DensePose Monitoring", + "panels": [ + { + "title": "Request Rate", + "type": "graph", + "targets": [ + { + "expr": "rate(http_requests_total[5m])", + "legendFormat": "{{method}} {{status}}" + } + ] + }, + { + "title": "Response Time", + "type": "graph", + "targets": [ + { + "expr": "histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))", + "legendFormat": "95th percentile" + } + ] + }, + { + "title": "Pose Detection Rate", + "type": "graph", + "targets": [ + { + "expr": "rate(pose_detections_total[5m])", + "legendFormat": "Detections per second" + } + ] + } + ] + } +} +``` + +### Alerting Rules + +```yaml +# monitoring/wifi_densepose_rules.yml +groups: + - name: wifi-densepose + rules: + - alert: HighErrorRate + expr: rate(http_requests_total{status=~"5.."}[5m]) > 0.1 + for: 5m + labels: + severity: critical + annotations: + summary: High error rate detected + description: "Error rate is {{ $value }} errors per second" + + - alert: HighResponseTime + expr: histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) > 1 + for: 5m + labels: + severity: warning + annotations: + summary: High response time detected + description: "95th percentile response time is {{ $value }} seconds" + + - alert: PoseDetectionDown + expr: rate(pose_detections_total[5m]) == 0 + for: 2m + labels: + severity: critical + annotations: + summary: Pose detection stopped + description: "No pose detections in the last 2 minutes" +``` + +## Security Considerations + +### Network Security + +```yaml +# Network policies +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: wifi-densepose-netpol + namespace: wifi-densepose +spec: + podSelector: + matchLabels: + app: wifi-densepose + policyTypes: + - Ingress + - Egress + ingress: + - from: + - namespaceSelector: + matchLabels: + name: ingress-nginx + ports: + - protocol: TCP + port: 8000 + egress: + - to: + - podSelector: + matchLabels: + component: postgres + ports: + - protocol: TCP + port: 5432 + - to: + - podSelector: + matchLabels: + component: redis + ports: + - protocol: TCP + port: 6379 +``` + +### Pod Security Standards + +```yaml +apiVersion: v1 +kind: Pod +spec: + securityContext: + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 1000 + fsGroup: 1000 + seccompProfile: + type: RuntimeDefault + containers: + - name: wifi-densepose + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: + - ALL +``` + +### Secrets Management + +```bash +# Using external secrets operator +helm repo add external-secrets https://charts.external-secrets.io +helm install external-secrets external-secrets/external-secrets \ + --namespace external-secrets-system \ + --create-namespace + +# AWS Secrets Manager integration +kubectl apply -f - < "${BACKUP_DIR}/${BACKUP_FILE}" + +# Compress backup +gzip "${BACKUP_DIR}/${BACKUP_FILE}" + +# Upload to S3 +aws s3 cp "${BACKUP_DIR}/${BACKUP_FILE}.gz" s3://wifi-densepose-backups/ + +# Clean old backups (keep last 30 days) +find ${BACKUP_DIR} -name "*.gz" -mtime +30 -delete +``` + +### Disaster Recovery + +```yaml +# Velero backup configuration +apiVersion: velero.io/v1 +kind: Schedule +metadata: + name: wifi-densepose-backup + namespace: velero +spec: + schedule: "0 2 * * *" + template: + includedNamespaces: + - wifi-densepose + storageLocation: default + volumeSnapshotLocations: + - default + ttl: 720h0m0s +``` + +## Troubleshooting + +### Common Issues + +#### 1. Pod Startup Issues + +```bash +# Check pod status +kubectl get pods -n wifi-densepose + +# Check pod logs +kubectl logs -f deployment/wifi-densepose -n wifi-densepose + +# Describe pod for events +kubectl describe pod -n wifi-densepose +``` + +#### 2. Database Connection Issues + +```bash +# Test database connectivity +kubectl run -it --rm debug --image=postgres:15-alpine --restart=Never -- \ + psql -h postgres -U postgres -d wifi_densepose + +# Check database logs +kubectl logs -f deployment/postgres -n wifi-densepose +``` + +#### 3. Performance Issues + +```bash +# Check resource usage +kubectl top pods -n wifi-densepose +kubectl top nodes + +# Check HPA status +kubectl get hpa -n wifi-densepose + +# Check metrics +curl http://localhost:8080/metrics +``` + +### Debug Commands + +```bash +# Port forward for local debugging +kubectl port-forward service/wifi-densepose-service 8000:80 -n wifi-densepose + +# Execute commands in pod +kubectl exec -it deployment/wifi-densepose -n wifi-densepose -- /bin/bash + +# Check service endpoints +kubectl get endpoints -n wifi-densepose + +# View ingress status +kubectl describe ingress wifi-densepose-ingress -n wifi-densepose +``` + +--- + +For more information, see: +- [User Guide](user_guide.md) +- [API Reference](api_reference.md) +- [Troubleshooting Guide](troubleshooting.md) \ No newline at end of file diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md new file mode 100644 index 0000000..db21c56 --- /dev/null +++ b/docs/troubleshooting.md @@ -0,0 +1,1058 @@ +# WiFi-DensePose Troubleshooting Guide + +## Table of Contents + +1. [Overview](#overview) +2. [Quick Diagnostics](#quick-diagnostics) +3. [Installation Issues](#installation-issues) +4. [Hardware and Network Issues](#hardware-and-network-issues) +5. [Pose Detection Issues](#pose-detection-issues) +6. [Performance Issues](#performance-issues) +7. [API and WebSocket Issues](#api-and-websocket-issues) +8. [Database and Storage Issues](#database-and-storage-issues) +9. [Authentication and Security Issues](#authentication-and-security-issues) +10. [Deployment Issues](#deployment-issues) +11. [Monitoring and Logging](#monitoring-and-logging) +12. [Common Error Messages](#common-error-messages) +13. [Support and Resources](#support-and-resources) + +## Overview + +This guide helps diagnose and resolve common issues with WiFi-DensePose. Issues are organized by category with step-by-step troubleshooting procedures. + +### Before You Start + +1. **Check System Status**: Always start with a health check +2. **Review Logs**: Check application and system logs for errors +3. **Verify Configuration**: Ensure environment variables are correct +4. **Test Connectivity**: Verify network and hardware connections + +### Diagnostic Tools + +```bash +# System health check +curl http://localhost:8000/api/v1/health + +# Check system information +python -c "import wifi_densepose; wifi_densepose.print_system_info()" + +# View logs +docker-compose logs -f wifi-densepose +kubectl logs -f deployment/wifi-densepose -n wifi-densepose +``` + +## Quick Diagnostics + +### System Health Check + +```bash +#!/bin/bash +# quick-health-check.sh + +echo "=== WiFi-DensePose Health Check ===" + +# Check if service is running +if curl -s http://localhost:8000/api/v1/health > /dev/null; then + echo "✅ API service is responding" +else + echo "❌ API service is not responding" +fi + +# Check database connection +if curl -s http://localhost:8000/api/v1/health | grep -q "postgres.*healthy"; then + echo "✅ Database connection is healthy" +else + echo "❌ Database connection issues detected" +fi + +# Check hardware status +if curl -s http://localhost:8000/api/v1/health | grep -q "hardware.*healthy"; then + echo "✅ Hardware service is healthy" +else + echo "❌ Hardware service issues detected" +fi + +# Check pose detection +if curl -s http://localhost:8000/api/v1/pose/current > /dev/null; then + echo "✅ Pose detection is working" +else + echo "❌ Pose detection issues detected" +fi + +echo "=== End Health Check ===" +``` + +### Log Analysis + +```bash +# Check for common error patterns +grep -i "error\|exception\|failed" /var/log/wifi-densepose.log | tail -20 + +# Check hardware warnings +grep -i "hardware\|router\|csi" /var/log/wifi-densepose.log | tail -10 + +# Check pose processing issues +grep -i "pose\|detection\|confidence" /var/log/wifi-densepose.log | tail -10 +``` + +## Installation Issues + +### Package Installation Problems + +#### Issue: `pip install wifi-densepose` fails + +**Symptoms:** +- Package not found on PyPI +- Dependency conflicts +- Build errors + +**Solutions:** + +1. **Update pip and setuptools:** +```bash +pip install --upgrade pip setuptools wheel +``` + +2. **Install with specific Python version:** +```bash +python3.9 -m pip install wifi-densepose +``` + +3. **Install from source:** +```bash +git clone https://github.com/ruvnet/wifi-densepose.git +cd wifi-densepose +pip install -e . +``` + +4. **Resolve dependency conflicts:** +```bash +pip install --no-deps wifi-densepose +pip install -r requirements.txt +``` + +#### Issue: Missing system dependencies + +**Symptoms:** +- OpenCV import errors +- PyTorch installation failures +- Build tool errors + +**Solutions:** + +1. **Ubuntu/Debian:** +```bash +sudo apt update +sudo apt install -y build-essential cmake +sudo apt install -y libopencv-dev python3-opencv +sudo apt install -y python3.9-dev python3.9-venv +``` + +2. **CentOS/RHEL:** +```bash +sudo yum groupinstall -y "Development Tools" +sudo yum install -y opencv-devel python39-devel +``` + +3. **macOS:** +```bash +brew install cmake opencv python@3.9 +``` + +### Docker Installation Issues + +#### Issue: Docker build fails + +**Symptoms:** +- Build context too large +- Network timeouts +- Permission errors + +**Solutions:** + +1. **Optimize build context:** +```bash +# Add to .dockerignore +echo "data/" >> .dockerignore +echo "logs/" >> .dockerignore +echo "*.pyc" >> .dockerignore +echo "__pycache__/" >> .dockerignore +``` + +2. **Build with specific target:** +```bash +docker build --target production -t wifi-densepose:latest . +``` + +3. **Fix permission issues:** +```bash +sudo usermod -aG docker $USER +newgrp docker +``` + +## Hardware and Network Issues + +### Router Connection Problems + +#### Issue: Router not responding + +**Symptoms:** +- "Router main_router is unhealthy" warnings +- No CSI data received +- Connection timeouts + +**Diagnostic Steps:** + +1. **Check network connectivity:** +```bash +ping 192.168.1.1 # Replace with your router IP +telnet 192.168.1.1 22 # Check SSH access +``` + +2. **Verify router configuration:** +```bash +ssh admin@192.168.1.1 +# Check if CSI extraction is enabled +cat /etc/config/wireless | grep csi +``` + +3. **Test CSI data stream:** +```bash +# Listen for CSI data +nc -l 5500 # Default CSI port +``` + +**Solutions:** + +1. **Restart router service:** +```bash +ssh admin@192.168.1.1 +/etc/init.d/csi-tools restart +``` + +2. **Reconfigure CSI extraction:** +```bash +# On router +echo "csi_enable=1" >> /etc/config/wireless +echo "csi_rate=30" >> /etc/config/wireless +wifi reload +``` + +3. **Update router firmware:** +```bash +# Flash OpenWRT with CSI patches +sysupgrade -v openwrt-csi-enabled.bin +``` + +#### Issue: CSI data quality problems + +**Symptoms:** +- Low signal strength +- High noise levels +- Inconsistent data rates + +**Solutions:** + +1. **Optimize antenna placement:** + - Ensure 3×3 MIMO configuration + - Position antennas for optimal coverage + - Avoid interference sources + +2. **Adjust CSI parameters:** +```bash +# Increase sampling rate +echo "csi_rate=50" >> /etc/config/wireless + +# Filter noise +echo "csi_filter=1" >> /etc/config/wireless +``` + +3. **Calibrate environment:** +```bash +curl -X POST http://localhost:8000/api/v1/pose/calibrate +``` + +### Network Configuration Issues + +#### Issue: Firewall blocking connections + +**Symptoms:** +- Connection refused errors +- Timeouts on specific ports +- Intermittent connectivity + +**Solutions:** + +1. **Configure firewall rules:** +```bash +# Ubuntu/Debian +sudo ufw allow 8000/tcp # API port +sudo ufw allow 5500/tcp # CSI data port +sudo ufw allow 8080/tcp # Metrics port + +# CentOS/RHEL +sudo firewall-cmd --permanent --add-port=8000/tcp +sudo firewall-cmd --permanent --add-port=5500/tcp +sudo firewall-cmd --reload +``` + +2. **Check iptables rules:** +```bash +sudo iptables -L -n | grep -E "8000|5500" +``` + +3. **Disable firewall temporarily for testing:** +```bash +sudo ufw disable # Ubuntu +sudo systemctl stop firewalld # CentOS +``` + +## Pose Detection Issues + +### No Pose Detections + +#### Issue: System running but no poses detected + +**Symptoms:** +- API returns empty pose arrays +- Zero detection count in metrics +- No activity in pose logs + +**Diagnostic Steps:** + +1. **Check CSI data reception:** +```bash +curl http://localhost:8000/api/v1/system/status | jq '.hardware' +``` + +2. **Verify confidence threshold:** +```bash +curl http://localhost:8000/api/v1/config | jq '.detection.confidence_threshold' +``` + +3. **Test with lower threshold:** +```bash +curl -X PUT http://localhost:8000/api/v1/config \ + -H "Content-Type: application/json" \ + -d '{"detection": {"confidence_threshold": 0.3}}' +``` + +**Solutions:** + +1. **Recalibrate system:** +```bash +curl -X POST http://localhost:8000/api/v1/pose/calibrate +``` + +2. **Check environment setup:** + - Ensure people are in detection area + - Verify router placement and coverage + - Check for interference sources + +3. **Adjust detection parameters:** +```bash +curl -X PUT http://localhost:8000/api/v1/config \ + -H "Content-Type: application/json" \ + -d '{ + "detection": { + "confidence_threshold": 0.5, + "max_persons": 10, + "enable_tracking": true + } + }' +``` + +### Poor Detection Accuracy + +#### Issue: Low confidence scores or false positives + +**Symptoms:** +- Confidence scores below 0.7 +- Ghost detections +- Missed detections + +**Solutions:** + +1. **Improve environment conditions:** + - Remove metallic objects that cause reflections + - Ensure stable WiFi signal strength + - Minimize movement of non-human objects + +2. **Retrain or update models:** +```bash +# Download latest models +curl -O https://models.wifi-densepose.com/latest/densepose_model.pth +mv densepose_model.pth /app/models/ +``` + +3. **Adjust processing parameters:** +```python +# In configuration +{ + "pose_processing": { + "batch_size": 32, + "nms_threshold": 0.5, + "keypoint_threshold": 0.3 + } +} +``` + +### Zone Detection Issues + +#### Issue: Incorrect zone assignments + +**Symptoms:** +- People detected in wrong zones +- Zone boundaries not respected +- Inconsistent zone occupancy + +**Solutions:** + +1. **Verify zone configuration:** +```bash +curl http://localhost:8000/api/v1/zones | jq '.' +``` + +2. **Recalibrate zone boundaries:** +```bash +curl -X PUT http://localhost:8000/api/v1/zones/zone_001 \ + -H "Content-Type: application/json" \ + -d '{ + "coordinates": { + "x": 0, "y": 0, + "width": 500, "height": 300 + } + }' +``` + +3. **Test zone detection:** +```bash +curl "http://localhost:8000/api/v1/pose/zones/zone_001/occupancy" +``` + +## Performance Issues + +### High CPU Usage + +#### Issue: CPU usage consistently above 80% + +**Symptoms:** +- Slow response times +- High system load +- Processing delays + +**Diagnostic Steps:** + +1. **Check CPU usage by component:** +```bash +top -p $(pgrep -f wifi-densepose) +htop -p $(pgrep -f python) +``` + +2. **Monitor processing metrics:** +```bash +curl http://localhost:8080/metrics | grep cpu +``` + +**Solutions:** + +1. **Optimize processing parameters:** +```bash +# Reduce batch size +export POSE_PROCESSING_BATCH_SIZE=16 + +# Lower frame rate +export STREAM_FPS=15 + +# Reduce worker count +export WORKERS=2 +``` + +2. **Enable GPU acceleration:** +```bash +export ENABLE_GPU=true +export CUDA_VISIBLE_DEVICES=0 +``` + +3. **Scale horizontally:** +```bash +# Docker Compose +docker-compose up -d --scale wifi-densepose=3 + +# Kubernetes +kubectl scale deployment wifi-densepose --replicas=5 +``` + +### High Memory Usage + +#### Issue: Memory usage growing over time + +**Symptoms:** +- Out of memory errors +- Gradual memory increase +- System swapping + +**Solutions:** + +1. **Configure memory limits:** +```bash +# Docker +docker run --memory=4g wifi-densepose + +# Kubernetes +resources: + limits: + memory: 4Gi +``` + +2. **Optimize buffer sizes:** +```bash +export CSI_BUFFER_SIZE=500 +export POSE_HISTORY_LIMIT=1000 +``` + +3. **Enable garbage collection:** +```python +import gc +gc.set_threshold(700, 10, 10) +``` + +### Slow Response Times + +#### Issue: API responses taking >1 second + +**Symptoms:** +- High latency in API calls +- Timeout errors +- Poor user experience + +**Solutions:** + +1. **Enable caching:** +```bash +export REDIS_URL=redis://localhost:6379/0 +export ENABLE_CACHING=true +``` + +2. **Optimize database queries:** +```sql +-- Add indexes +CREATE INDEX idx_pose_detections_timestamp ON pose_detections (timestamp); +CREATE INDEX idx_csi_data_timestamp ON csi_data (timestamp); +``` + +3. **Use connection pooling:** +```bash +export DATABASE_POOL_SIZE=20 +export DATABASE_MAX_OVERFLOW=30 +``` + +## API and WebSocket Issues + +### API Not Responding + +#### Issue: HTTP 500 errors or connection refused + +**Symptoms:** +- Cannot connect to API +- Internal server errors +- Service unavailable + +**Diagnostic Steps:** + +1. **Check service status:** +```bash +curl -I http://localhost:8000/api/v1/health +systemctl status wifi-densepose +``` + +2. **Check port availability:** +```bash +netstat -tlnp | grep 8000 +lsof -i :8000 +``` + +**Solutions:** + +1. **Restart service:** +```bash +# Docker +docker-compose restart wifi-densepose + +# Systemd +sudo systemctl restart wifi-densepose + +# Kubernetes +kubectl rollout restart deployment/wifi-densepose +``` + +2. **Check configuration:** +```bash +# Verify environment variables +env | grep -E "HOST|PORT|DATABASE_URL" +``` + +3. **Review logs for errors:** +```bash +tail -f /var/log/wifi-densepose.log +``` + +### WebSocket Connection Issues + +#### Issue: WebSocket connections failing or dropping + +**Symptoms:** +- Connection refused on WebSocket endpoint +- Frequent disconnections +- No real-time updates + +**Solutions:** + +1. **Test WebSocket connectivity:** +```javascript +const ws = new WebSocket('ws://localhost:8000/ws/pose/stream'); +ws.onopen = () => console.log('Connected'); +ws.onerror = (error) => console.error('Error:', error); +``` + +2. **Check proxy configuration:** +```nginx +# Nginx WebSocket support +location /ws/ { + proxy_pass http://backend; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; +} +``` + +3. **Increase connection limits:** +```bash +export WEBSOCKET_MAX_CONNECTIONS=100 +export WEBSOCKET_TIMEOUT=300 +``` + +### Authentication Issues + +#### Issue: JWT token errors + +**Symptoms:** +- 401 Unauthorized errors +- Token expired messages +- Authentication failures + +**Solutions:** + +1. **Verify token validity:** +```bash +# Decode JWT token +echo "eyJ..." | base64 -d +``` + +2. **Check token expiration:** +```bash +curl -H "Authorization: Bearer " \ + http://localhost:8000/api/v1/auth/verify +``` + +3. **Refresh token:** +```bash +curl -X POST http://localhost:8000/api/v1/auth/refresh \ + -H "Authorization: Bearer " +``` + +## Database and Storage Issues + +### Database Connection Errors + +#### Issue: Cannot connect to PostgreSQL + +**Symptoms:** +- "Connection refused" errors +- Database timeout errors +- Service startup failures + +**Diagnostic Steps:** + +1. **Check database status:** +```bash +# Docker +docker-compose logs postgres + +# Direct connection test +psql -h localhost -U postgres -d wifi_densepose +``` + +2. **Verify connection string:** +```bash +echo $DATABASE_URL +``` + +**Solutions:** + +1. **Restart database:** +```bash +docker-compose restart postgres +sudo systemctl restart postgresql +``` + +2. **Check database configuration:** +```sql +-- Check connections +SELECT * FROM pg_stat_activity; + +-- Check database size +SELECT pg_size_pretty(pg_database_size('wifi_densepose')); +``` + +3. **Fix connection limits:** +```sql +-- Increase max connections +ALTER SYSTEM SET max_connections = 200; +SELECT pg_reload_conf(); +``` + +### Storage Space Issues + +#### Issue: Disk space running low + +**Symptoms:** +- "No space left on device" errors +- Database write failures +- Log rotation issues + +**Solutions:** + +1. **Check disk usage:** +```bash +df -h +du -sh /app/data /app/logs /app/models +``` + +2. **Clean old data:** +```bash +# Remove old logs +find /app/logs -name "*.log" -mtime +7 -delete + +# Clean old pose data +psql -c "DELETE FROM pose_detections WHERE timestamp < NOW() - INTERVAL '30 days';" +``` + +3. **Configure log rotation:** +```bash +# /etc/logrotate.d/wifi-densepose +/app/logs/*.log { + daily + rotate 7 + compress + delaycompress + missingok + notifempty +} +``` + +## Authentication and Security Issues + +### SSL/TLS Certificate Issues + +#### Issue: HTTPS certificate errors + +**Symptoms:** +- Certificate validation failures +- Browser security warnings +- SSL handshake errors + +**Solutions:** + +1. **Check certificate validity:** +```bash +openssl x509 -in /etc/ssl/certs/wifi-densepose.crt -text -noout +``` + +2. **Renew Let's Encrypt certificate:** +```bash +certbot renew --nginx +``` + +3. **Update certificate in Kubernetes:** +```bash +kubectl create secret tls tls-secret \ + --cert=path/to/tls.crt \ + --key=path/to/tls.key +``` + +### Rate Limiting Issues + +#### Issue: Requests being rate limited + +**Symptoms:** +- HTTP 429 errors +- "Rate limit exceeded" messages +- Blocked API access + +**Solutions:** + +1. **Check rate limit status:** +```bash +curl -I http://localhost:8000/api/v1/pose/current +# Look for X-RateLimit-* headers +``` + +2. **Adjust rate limits:** +```bash +export RATE_LIMIT_REQUESTS=1000 +export RATE_LIMIT_WINDOW=3600 +``` + +3. **Implement authentication for higher limits:** +```bash +curl -H "Authorization: Bearer " \ + http://localhost:8000/api/v1/pose/current +``` + +## Deployment Issues + +### Docker Compose Issues + +#### Issue: Services not starting properly + +**Symptoms:** +- Container exit codes +- Dependency failures +- Network connectivity issues + +**Solutions:** + +1. **Check service dependencies:** +```bash +docker-compose ps +docker-compose logs +``` + +2. **Rebuild containers:** +```bash +docker-compose down +docker-compose build --no-cache +docker-compose up -d +``` + +3. **Fix network issues:** +```bash +docker network ls +docker network inspect wifi-densepose_default +``` + +### Kubernetes Deployment Issues + +#### Issue: Pods not starting + +**Symptoms:** +- Pods in Pending/CrashLoopBackOff state +- Image pull errors +- Resource constraints + +**Solutions:** + +1. **Check pod status:** +```bash +kubectl get pods -n wifi-densepose +kubectl describe pod -n wifi-densepose +``` + +2. **Check resource availability:** +```bash +kubectl top nodes +kubectl describe node +``` + +3. **Fix image issues:** +```bash +# Check image availability +docker pull wifi-densepose:latest + +# Update deployment +kubectl set image deployment/wifi-densepose \ + wifi-densepose=wifi-densepose:latest +``` + +## Monitoring and Logging + +### Log Analysis + +#### Common log patterns to monitor: + +1. **Error patterns:** +```bash +grep -E "ERROR|CRITICAL|Exception" /var/log/wifi-densepose.log +``` + +2. **Performance patterns:** +```bash +grep -E "slow|timeout|latency" /var/log/wifi-densepose.log +``` + +3. **Hardware patterns:** +```bash +grep -E "router|hardware|csi" /var/log/wifi-densepose.log +``` + +### Metrics Collection + +#### Key metrics to monitor: + +1. **System metrics:** + - CPU usage + - Memory usage + - Disk I/O + - Network traffic + +2. **Application metrics:** + - Request rate + - Response time + - Error rate + - Pose detection rate + +3. **Hardware metrics:** + - CSI data rate + - Signal strength + - Router connectivity + +## Common Error Messages + +### Error: "Router main_router is unhealthy" + +**Cause:** Router connectivity or CSI extraction issues + +**Solution:** +1. Check router network connectivity +2. Verify CSI extraction configuration +3. Restart router CSI service +4. Check firewall rules + +### Error: "Database connection failed" + +**Cause:** PostgreSQL connectivity issues + +**Solution:** +1. Check database service status +2. Verify connection string +3. Check network connectivity +4. Review database logs + +### Error: "CUDA out of memory" + +**Cause:** GPU memory exhaustion + +**Solution:** +1. Reduce batch size +2. Enable mixed precision +3. Clear GPU cache +4. Use CPU processing + +### Error: "Rate limit exceeded" + +**Cause:** Too many API requests + +**Solution:** +1. Implement request throttling +2. Use authentication for higher limits +3. Cache responses +4. Optimize request patterns + +### Error: "Pose detection timeout" + +**Cause:** Processing taking too long + +**Solution:** +1. Optimize processing parameters +2. Scale processing resources +3. Check hardware performance +4. Review model complexity + +## Support and Resources + +### Getting Help + +1. **Documentation:** + - [User Guide](user_guide.md) + - [API Reference](api_reference.md) + - [Deployment Guide](deployment.md) + +2. **Community Support:** + - GitHub Issues: https://github.com/ruvnet/wifi-densepose/issues + - Discord Server: https://discord.gg/wifi-densepose + - Stack Overflow: Tag `wifi-densepose` + +3. **Professional Support:** + - Enterprise support available + - Custom deployment assistance + - Performance optimization consulting + +### Diagnostic Information to Collect + +When reporting issues, include: + +1. **System Information:** +```bash +# System details +uname -a +python --version +docker --version + +# WiFi-DensePose version +python -c "import wifi_densepose; print(wifi_densepose.__version__)" +``` + +2. **Configuration:** +```bash +# Environment variables (sanitized) +env | grep -E "WIFI|POSE|DATABASE" | sed 's/=.*/=***/' +``` + +3. **Logs:** +```bash +# Recent logs +tail -100 /var/log/wifi-densepose.log + +# Error logs +grep -E "ERROR|CRITICAL" /var/log/wifi-densepose.log | tail -20 +``` + +4. **Health Status:** +```bash +curl http://localhost:8000/api/v1/health | jq '.' +``` + +### Emergency Procedures + +#### System Recovery + +1. **Stop all services:** +```bash +docker-compose down +kubectl delete deployment wifi-densepose +``` + +2. **Backup critical data:** +```bash +pg_dump wifi_densepose > backup.sql +cp -r /app/data /backup/ +``` + +3. **Restore from backup:** +```bash +psql wifi_densepose < backup.sql +cp -r /backup/data /app/ +``` + +4. **Restart with minimal configuration:** +```bash +# Use safe defaults +export DEBUG=true +export MOCK_HARDWARE=true +docker-compose up -d +``` + +--- + +For additional support, contact the WiFi-DensePose team or consult the community resources listed above. \ No newline at end of file diff --git a/ui/style.css b/ui/style.css index f273b43..bc07f79 100644 --- a/ui/style.css +++ b/ui/style.css @@ -1304,4 +1304,317 @@ canvas { .implementation-note p { color: var(--color-text-secondary); margin-bottom: 0; -} \ No newline at end of file +} +/* Additional styles for modular UI components */ + +/* Header info bar */ +.header-info { + display: flex; + gap: var(--space-16); + justify-content: center; + margin-top: var(--space-16); + font-size: var(--font-size-sm); +} + +.api-version, +.api-environment, +.overall-health { + padding: var(--space-4) var(--space-12); + border-radius: var(--radius-full); + background: var(--color-secondary); + font-weight: var(--font-weight-medium); +} + +.api-environment.env-development { + background: rgba(var(--color-info-rgb), 0.1); + color: var(--color-info); +} + +.api-environment.env-production { + background: rgba(var(--color-success-rgb), 0.1); + color: var(--color-success); +} + +.overall-health.status-healthy { + background: rgba(var(--color-success-rgb), 0.1); + color: var(--color-success); +} + +.overall-health.status-degraded { + background: rgba(var(--color-warning-rgb), 0.1); + color: var(--color-warning); +} + +.overall-health.status-unhealthy { + background: rgba(var(--color-error-rgb), 0.1); + color: var(--color-error); +} + +/* Dashboard panels */ +.live-status-panel, +.system-metrics-panel, +.features-panel, +.live-stats-panel { + background: var(--color-surface); + border: 1px solid var(--color-border); + border-radius: var(--radius-lg); + padding: var(--space-24); + margin-bottom: var(--space-24); +} + +.status-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: var(--space-16); + margin-top: var(--space-16); +} + +.component-status { + display: flex; + flex-direction: column; + gap: var(--space-4); + padding: var(--space-12); + border: 1px solid var(--color-border); + border-radius: var(--radius-base); +} + +.component-status.status-healthy { + border-color: var(--color-success); + background: rgba(var(--color-success-rgb), 0.05); +} + +.component-status.status-degraded { + border-color: var(--color-warning); + background: rgba(var(--color-warning-rgb), 0.05); +} + +.component-status.status-unhealthy { + border-color: var(--color-error); + background: rgba(var(--color-error-rgb), 0.05); +} + +.component-name { + font-weight: var(--font-weight-semibold); +} + +.status-text { + font-size: var(--font-size-sm); + text-transform: uppercase; +} + +.status-message { + font-size: var(--font-size-xs); + color: var(--color-text-secondary); +} + +/* Metrics display */ +.metrics-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: var(--space-16); + margin-top: var(--space-16); +} + +.metric-item { + display: flex; + flex-direction: column; + gap: var(--space-8); +} + +.metric-label { + font-size: var(--font-size-sm); + color: var(--color-text-secondary); +} + +.progress-bar { + height: 8px; + background: var(--color-secondary); + border-radius: var(--radius-full); + overflow: hidden; +} + +.progress-fill { + height: 100%; + transition: width 0.3s ease; +} + +.progress-fill.normal { + background: var(--color-success); +} + +.progress-fill.warning { + background: var(--color-warning); +} + +.progress-fill.critical { + background: var(--color-error); +} + +/* Features status */ +.features-status { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); + gap: var(--space-12); + margin-top: var(--space-16); +} + +.feature-item { + display: flex; + align-items: center; + justify-content: space-between; + padding: var(--space-8) var(--space-12); + border: 1px solid var(--color-border); + border-radius: var(--radius-base); + font-size: var(--font-size-sm); +} + +.feature-item.enabled { + background: rgba(var(--color-success-rgb), 0.05); + border-color: var(--color-success); +} + +.feature-item.disabled { + opacity: 0.6; +} + +.feature-status { + font-weight: var(--font-weight-semibold); +} + +/* Live statistics */ +.stats-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); + gap: var(--space-16); + margin-top: var(--space-16); +} + +.stat-item { + text-align: center; +} + +.stat-item .stat-label { + display: block; + font-size: var(--font-size-sm); + color: var(--color-text-secondary); + margin-bottom: var(--space-4); +} + +.person-count, +.avg-confidence, +.detection-count { + font-size: var(--font-size-2xl); + font-weight: var(--font-weight-bold); + color: var(--color-primary); +} + +/* Zones display */ +.zones-panel { + margin-top: var(--space-24); +} + +.zones-summary { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); + gap: var(--space-8); + margin-top: var(--space-12); +} + +.zone-item { + display: flex; + align-items: center; + justify-content: space-between; + padding: var(--space-8) var(--space-12); + background: var(--color-secondary); + border-radius: var(--radius-base); + font-size: var(--font-size-sm); +} + +.zone-name { + font-weight: var(--font-weight-medium); +} + +.zone-count { + background: var(--color-primary); + color: white; + padding: var(--space-2) var(--space-8); + border-radius: var(--radius-full); + font-weight: var(--font-weight-semibold); +} + +/* Error container */ +.error-container { + background: rgba(var(--color-error-rgb), 0.1); + border: 1px solid var(--color-error); + color: var(--color-error); + padding: var(--space-12) var(--space-16); + border-radius: var(--radius-base); + margin-bottom: var(--space-16); +} + +/* Global error toast */ +.error-toast { + position: fixed; + bottom: var(--space-24); + right: var(--space-24); + background: var(--color-error); + color: white; + padding: var(--space-12) var(--space-20); + border-radius: var(--radius-base); + box-shadow: var(--shadow-lg); + transform: translateY(100%); + opacity: 0; + transition: all 0.3s ease; + z-index: 1000; +} + +.error-toast.show { + transform: translateY(0); + opacity: 1; +} + +/* Tab badge */ +.tab-badge { + display: inline-block; + margin-left: var(--space-8); + padding: var(--space-2) var(--space-6); + background: var(--color-error); + color: white; + border-radius: var(--radius-full); + font-size: var(--font-size-xs); + font-weight: var(--font-weight-semibold); +} + +/* Help text */ +.help-text { + font-size: var(--font-size-sm); + color: var(--color-text-secondary); + font-style: italic; + margin-bottom: var(--space-16); +} + +/* Array status */ +.array-status { + display: flex; + gap: var(--space-16); + margin-top: var(--space-16); + padding: var(--space-12); + background: var(--color-secondary); + border-radius: var(--radius-base); +} + +.array-info { + display: flex; + align-items: center; + gap: var(--space-8); +} + +.info-label { + font-size: var(--font-size-sm); + color: var(--color-text-secondary); +} + +.info-value { + font-weight: var(--font-weight-semibold); + color: var(--color-primary); +} diff --git a/ui/tests/test-runner.html b/ui/tests/test-runner.html new file mode 100644 index 0000000..037177f --- /dev/null +++ b/ui/tests/test-runner.html @@ -0,0 +1,228 @@ + + + + + + WiFi DensePose UI Tests + + + +
+

WiFi DensePose UI Test Suite

+

Comprehensive testing for the modular UI components and API integration

+
+ +
+ + + + +
+ +
+

Test Summary

+
+
+
0
+
Total Tests
+
+
+
0
+
Passed
+
+
+
0
+
Failed
+
+
+
0
+
Pending
+
+
+
+ +
+
API Configuration Tests
+
+
+ +
+
API Service Tests
+
+
+ +
+
WebSocket Service Tests
+
+
+ +
+
Pose Service Tests
+
+
+ +
+
Health Service Tests
+
+
+ +
+
UI Component Tests
+
+
+ +
+
Integration Tests
+
+
+ + + + + + \ No newline at end of file diff --git a/ui/tests/test-runner.js b/ui/tests/test-runner.js new file mode 100644 index 0000000..7d35977 --- /dev/null +++ b/ui/tests/test-runner.js @@ -0,0 +1,476 @@ +// Test Runner for WiFi DensePose UI + +import { API_CONFIG, buildApiUrl, buildWsUrl } from '../config/api.config.js'; +import { apiService } from '../services/api.service.js'; +import { wsService } from '../services/websocket.service.js'; +import { poseService } from '../services/pose.service.js'; +import { healthService } from '../services/health.service.js'; +import { TabManager } from '../components/TabManager.js'; + +class TestRunner { + constructor() { + this.tests = []; + this.results = { + total: 0, + passed: 0, + failed: 0, + pending: 0 + }; + this.output = []; + } + + // Add a test + test(name, category, testFn) { + this.tests.push({ + name, + category, + fn: testFn, + status: 'pending' + }); + } + + // Run all tests + async runAllTests() { + this.clearResults(); + this.log('Starting test suite...\n'); + + for (const test of this.tests) { + await this.runSingleTest(test); + } + + this.updateSummary(); + this.log(`\nTest suite completed. ${this.results.passed}/${this.results.total} tests passed.`); + } + + // Run tests by category + async runTestsByCategory(category) { + this.clearResults(); + const categoryTests = this.tests.filter(test => test.category === category); + + this.log(`Starting ${category} tests...\n`); + + for (const test of categoryTests) { + await this.runSingleTest(test); + } + + this.updateSummary(); + this.log(`\n${category} tests completed. ${this.results.passed}/${this.results.total} tests passed.`); + } + + // Run a single test + async runSingleTest(test) { + this.log(`Running: ${test.name}...`); + + try { + const startTime = Date.now(); + await test.fn(); + const duration = Date.now() - startTime; + + test.status = 'pass'; + this.results.passed++; + this.log(` ✓ PASS (${duration}ms)`); + + } catch (error) { + test.status = 'fail'; + test.error = error.message; + this.results.failed++; + this.log(` ✗ FAIL: ${error.message}`); + + } finally { + this.results.total++; + this.updateTestDisplay(test); + } + } + + // Assertion helpers + assert(condition, message) { + if (!condition) { + throw new Error(message || 'Assertion failed'); + } + } + + assertEqual(actual, expected, message) { + if (actual !== expected) { + throw new Error(message || `Expected ${expected}, got ${actual}`); + } + } + + assertNotEqual(actual, unexpected, message) { + if (actual === unexpected) { + throw new Error(message || `Expected not to equal ${unexpected}`); + } + } + + assertThrows(fn, message) { + try { + fn(); + throw new Error(message || 'Expected function to throw'); + } catch (error) { + // Expected + } + } + + async assertRejects(promise, message) { + try { + await promise; + throw new Error(message || 'Expected promise to reject'); + } catch (error) { + // Expected + } + } + + // Logging + log(message) { + this.output.push(message); + const outputElement = document.getElementById('testOutput'); + if (outputElement) { + outputElement.style.display = 'block'; + outputElement.textContent = this.output.join('\n'); + outputElement.scrollTop = outputElement.scrollHeight; + } + } + + // Clear results + clearResults() { + this.results = { total: 0, passed: 0, failed: 0, pending: 0 }; + this.output = []; + + // Reset test statuses + this.tests.forEach(test => { + test.status = 'pending'; + delete test.error; + }); + + // Clear UI + this.updateSummary(); + this.tests.forEach(test => this.updateTestDisplay(test)); + + const outputElement = document.getElementById('testOutput'); + if (outputElement) { + outputElement.style.display = 'none'; + outputElement.textContent = ''; + } + } + + // Update test display + updateTestDisplay(test) { + const container = document.getElementById(`${test.category}Tests`); + if (!container) return; + + let testElement = container.querySelector(`[data-test="${test.name}"]`); + if (!testElement) { + testElement = document.createElement('div'); + testElement.className = 'test-case'; + testElement.setAttribute('data-test', test.name); + testElement.innerHTML = ` +
${test.name}
+
pending
+ `; + container.appendChild(testElement); + } + + const statusElement = testElement.querySelector('.test-status'); + statusElement.className = `test-status ${test.status}`; + statusElement.textContent = test.status; + + if (test.error) { + statusElement.title = test.error; + } + } + + // Update summary + updateSummary() { + document.getElementById('totalTests').textContent = this.results.total; + document.getElementById('passedTests').textContent = this.results.passed; + document.getElementById('failedTests').textContent = this.results.failed; + document.getElementById('pendingTests').textContent = this.tests.length - this.results.total; + } +} + +// Create test runner instance +const testRunner = new TestRunner(); + +// Mock DOM elements for testing +function createMockContainer() { + const container = document.createElement('div'); + container.innerHTML = ` + +
+
+ `; + return container; +} + +// API Configuration Tests +testRunner.test('API_CONFIG contains required endpoints', 'apiConfig', () => { + testRunner.assert(API_CONFIG.ENDPOINTS, 'ENDPOINTS should exist'); + testRunner.assert(API_CONFIG.ENDPOINTS.POSE, 'POSE endpoints should exist'); + testRunner.assert(API_CONFIG.ENDPOINTS.HEALTH, 'HEALTH endpoints should exist'); + testRunner.assert(API_CONFIG.ENDPOINTS.STREAM, 'STREAM endpoints should exist'); +}); + +testRunner.test('buildApiUrl constructs correct URLs', 'apiConfig', () => { + const url = buildApiUrl('/api/v1/pose/current', { zone_id: 'zone1', limit: 10 }); + testRunner.assert(url.includes('/api/v1/pose/current'), 'URL should contain endpoint'); + testRunner.assert(url.includes('zone_id=zone1'), 'URL should contain zone_id parameter'); + testRunner.assert(url.includes('limit=10'), 'URL should contain limit parameter'); +}); + +testRunner.test('buildApiUrl handles path parameters', 'apiConfig', () => { + const url = buildApiUrl('/api/v1/pose/zones/{zone_id}/occupancy', { zone_id: 'zone1' }); + testRunner.assert(url.includes('/api/v1/pose/zones/zone1/occupancy'), 'URL should replace path parameter'); + testRunner.assert(!url.includes('{zone_id}'), 'URL should not contain placeholder'); +}); + +testRunner.test('buildWsUrl constructs WebSocket URLs', 'apiConfig', () => { + const url = buildWsUrl('/api/v1/stream/pose', { token: 'test-token' }); + testRunner.assert(url.startsWith('ws://') || url.startsWith('wss://'), 'URL should be WebSocket protocol'); + testRunner.assert(url.includes('/api/v1/stream/pose'), 'URL should contain endpoint'); + testRunner.assert(url.includes('token=test-token'), 'URL should contain token parameter'); +}); + +// API Service Tests +testRunner.test('apiService has required methods', 'apiService', () => { + testRunner.assert(typeof apiService.get === 'function', 'get method should exist'); + testRunner.assert(typeof apiService.post === 'function', 'post method should exist'); + testRunner.assert(typeof apiService.put === 'function', 'put method should exist'); + testRunner.assert(typeof apiService.delete === 'function', 'delete method should exist'); +}); + +testRunner.test('apiService can set auth token', 'apiService', () => { + const token = 'test-token-123'; + apiService.setAuthToken(token); + testRunner.assertEqual(apiService.authToken, token, 'Auth token should be set'); +}); + +testRunner.test('apiService builds correct headers', 'apiService', () => { + apiService.setAuthToken('test-token'); + const headers = apiService.getHeaders(); + testRunner.assert(headers['Content-Type'], 'Content-Type header should exist'); + testRunner.assert(headers['Authorization'], 'Authorization header should exist'); + testRunner.assertEqual(headers['Authorization'], 'Bearer test-token', 'Authorization header should be correct'); +}); + +testRunner.test('apiService handles interceptors', 'apiService', () => { + let requestIntercepted = false; + let responseIntercepted = false; + + apiService.addRequestInterceptor(() => { + requestIntercepted = true; + return { url: 'test', options: {} }; + }); + + apiService.addResponseInterceptor(() => { + responseIntercepted = true; + return new Response('{}'); + }); + + testRunner.assert(apiService.requestInterceptors.length > 0, 'Request interceptor should be added'); + testRunner.assert(apiService.responseInterceptors.length > 0, 'Response interceptor should be added'); +}); + +// WebSocket Service Tests +testRunner.test('wsService has required methods', 'websocketService', () => { + testRunner.assert(typeof wsService.connect === 'function', 'connect method should exist'); + testRunner.assert(typeof wsService.disconnect === 'function', 'disconnect method should exist'); + testRunner.assert(typeof wsService.send === 'function', 'send method should exist'); + testRunner.assert(typeof wsService.onMessage === 'function', 'onMessage method should exist'); +}); + +testRunner.test('wsService generates unique connection IDs', 'websocketService', () => { + const id1 = wsService.generateId(); + const id2 = wsService.generateId(); + testRunner.assertNotEqual(id1, id2, 'Connection IDs should be unique'); + testRunner.assert(id1.startsWith('ws_'), 'Connection ID should have correct prefix'); +}); + +testRunner.test('wsService manages connection state', 'websocketService', () => { + const initialConnections = wsService.getActiveConnections(); + testRunner.assert(Array.isArray(initialConnections), 'Active connections should be an array'); +}); + +// Pose Service Tests +testRunner.test('poseService has required methods', 'poseService', () => { + testRunner.assert(typeof poseService.getCurrentPose === 'function', 'getCurrentPose method should exist'); + testRunner.assert(typeof poseService.getZoneOccupancy === 'function', 'getZoneOccupancy method should exist'); + testRunner.assert(typeof poseService.startPoseStream === 'function', 'startPoseStream method should exist'); + testRunner.assert(typeof poseService.subscribeToPoseUpdates === 'function', 'subscribeToPoseUpdates method should exist'); +}); + +testRunner.test('poseService subscription management', 'poseService', () => { + let callbackCalled = false; + const unsubscribe = poseService.subscribeToPoseUpdates(() => { + callbackCalled = true; + }); + + testRunner.assert(typeof unsubscribe === 'function', 'Subscribe should return unsubscribe function'); + testRunner.assert(poseService.poseSubscribers.length > 0, 'Subscriber should be added'); + + unsubscribe(); + testRunner.assertEqual(poseService.poseSubscribers.length, 0, 'Subscriber should be removed'); +}); + +testRunner.test('poseService handles pose updates', 'poseService', () => { + let receivedUpdate = null; + + poseService.subscribeToPoseUpdates(update => { + receivedUpdate = update; + }); + + const testUpdate = { type: 'pose_update', data: { persons: [] } }; + poseService.notifyPoseSubscribers(testUpdate); + + testRunner.assertEqual(receivedUpdate, testUpdate, 'Update should be received by subscriber'); +}); + +// Health Service Tests +testRunner.test('healthService has required methods', 'healthService', () => { + testRunner.assert(typeof healthService.getSystemHealth === 'function', 'getSystemHealth method should exist'); + testRunner.assert(typeof healthService.checkReadiness === 'function', 'checkReadiness method should exist'); + testRunner.assert(typeof healthService.startHealthMonitoring === 'function', 'startHealthMonitoring method should exist'); + testRunner.assert(typeof healthService.subscribeToHealth === 'function', 'subscribeToHealth method should exist'); +}); + +testRunner.test('healthService subscription management', 'healthService', () => { + let callbackCalled = false; + const unsubscribe = healthService.subscribeToHealth(() => { + callbackCalled = true; + }); + + testRunner.assert(typeof unsubscribe === 'function', 'Subscribe should return unsubscribe function'); + testRunner.assert(healthService.healthSubscribers.length > 0, 'Subscriber should be added'); + + unsubscribe(); + testRunner.assertEqual(healthService.healthSubscribers.length, 0, 'Subscriber should be removed'); +}); + +testRunner.test('healthService status checking', 'healthService', () => { + // Set mock health status + healthService.lastHealthStatus = { status: 'healthy' }; + testRunner.assert(healthService.isSystemHealthy(), 'System should be healthy'); + + healthService.lastHealthStatus = { status: 'unhealthy' }; + testRunner.assert(!healthService.isSystemHealthy(), 'System should not be healthy'); + + healthService.lastHealthStatus = null; + testRunner.assertEqual(healthService.isSystemHealthy(), null, 'System health should be null when no status'); +}); + +// UI Component Tests +testRunner.test('TabManager can be instantiated', 'uiComponent', () => { + const container = createMockContainer(); + const tabManager = new TabManager(container); + testRunner.assert(tabManager instanceof TabManager, 'TabManager should be instantiated'); +}); + +testRunner.test('TabManager initializes tabs', 'uiComponent', () => { + const container = createMockContainer(); + const tabManager = new TabManager(container); + tabManager.init(); + + testRunner.assert(tabManager.tabs.length > 0, 'Tabs should be found'); + testRunner.assert(tabManager.tabContents.length > 0, 'Tab contents should be found'); +}); + +testRunner.test('TabManager handles tab switching', 'uiComponent', () => { + const container = createMockContainer(); + const tabManager = new TabManager(container); + tabManager.init(); + + let tabChangeEvent = null; + tabManager.onTabChange((newTab, oldTab) => { + tabChangeEvent = { newTab, oldTab }; + }); + + // Switch to hardware tab + const hardwareTab = container.querySelector('[data-tab="hardware"]'); + tabManager.switchTab(hardwareTab); + + testRunner.assertEqual(tabManager.getActiveTab(), 'hardware', 'Active tab should be updated'); + testRunner.assert(tabChangeEvent, 'Tab change event should be fired'); + testRunner.assertEqual(tabChangeEvent.newTab, 'hardware', 'New tab should be correct'); +}); + +testRunner.test('TabManager can enable/disable tabs', 'uiComponent', () => { + const container = createMockContainer(); + const tabManager = new TabManager(container); + tabManager.init(); + + tabManager.setTabEnabled('hardware', false); + const hardwareTab = container.querySelector('[data-tab="hardware"]'); + testRunner.assert(hardwareTab.disabled, 'Tab should be disabled'); + testRunner.assert(hardwareTab.classList.contains('disabled'), 'Tab should have disabled class'); +}); + +testRunner.test('TabManager can show/hide tabs', 'uiComponent', () => { + const container = createMockContainer(); + const tabManager = new TabManager(container); + tabManager.init(); + + tabManager.setTabVisible('hardware', false); + const hardwareTab = container.querySelector('[data-tab="hardware"]'); + testRunner.assertEqual(hardwareTab.style.display, 'none', 'Tab should be hidden'); +}); + +// Integration Tests +testRunner.test('Services can be imported together', 'integration', () => { + testRunner.assert(apiService, 'API service should be available'); + testRunner.assert(wsService, 'WebSocket service should be available'); + testRunner.assert(poseService, 'Pose service should be available'); + testRunner.assert(healthService, 'Health service should be available'); +}); + +testRunner.test('Services maintain separate state', 'integration', () => { + // Set different states + apiService.setAuthToken('api-token'); + poseService.subscribeToPoseUpdates(() => {}); + healthService.subscribeToHealth(() => {}); + + // Verify independence + testRunner.assertEqual(apiService.authToken, 'api-token', 'API service should maintain its token'); + testRunner.assert(poseService.poseSubscribers.length > 0, 'Pose service should have subscribers'); + testRunner.assert(healthService.healthSubscribers.length > 0, 'Health service should have subscribers'); +}); + +testRunner.test('Configuration is consistent across services', 'integration', () => { + // All services should use the same configuration + testRunner.assert(API_CONFIG.BASE_URL, 'Base URL should be configured'); + testRunner.assert(API_CONFIG.ENDPOINTS, 'Endpoints should be configured'); + testRunner.assert(API_CONFIG.WS_CONFIG, 'WebSocket config should be available'); +}); + +// Event listeners for UI +document.addEventListener('DOMContentLoaded', () => { + document.getElementById('runAllTests').addEventListener('click', () => { + testRunner.runAllTests(); + }); + + document.getElementById('runUnitTests').addEventListener('click', () => { + const unitCategories = ['apiConfig', 'apiService', 'websocketService', 'poseService', 'healthService', 'uiComponent']; + testRunner.clearResults(); + + (async () => { + for (const category of unitCategories) { + await testRunner.runTestsByCategory(category); + } + testRunner.updateSummary(); + })(); + }); + + document.getElementById('runIntegrationTests').addEventListener('click', () => { + testRunner.runTestsByCategory('integration'); + }); + + document.getElementById('clearResults').addEventListener('click', () => { + testRunner.clearResults(); + }); + + // Initialize test display + testRunner.tests.forEach(test => testRunner.updateTestDisplay(test)); + testRunner.updateSummary(); +}); + +export { testRunner }; \ No newline at end of file