+
+
parcel
gentoo
+
emacs
+
+
+
+
+
bundler
ray
elasticsearch
+
cassandra
toml
+
+
quarkus
+
sublime
+
+
angular
grpc
+
∩
bash
+
yaml
axum
+
+
https
+
+
zig
+
+
xgboost
grafana
+
lua
stimulus
+
express
cassandra
choo
+
https
cassandra
linux
rocket
+
+
+
+
swc
+
jwt
+
adonis
!=
saml
<=
+
emacs
axum
abap
+
+
adonis
jwt
elm
+
+
+
git
node
zorin
bbedit
mocha
+
+
β‰ˆ
+
+
+
βˆͺ
Back to Blog
Fleet Management Systems on Alpine Linux πŸš›
Alpine Linux Transportation GPS Tracking

Fleet Management Systems on Alpine Linux πŸš›

Published Jun 13, 2025

Learn how to build a fleet management system on Alpine Linux. We will track vehicles, monitor drivers, optimize routes, and manage maintenance schedules! πŸ—ΊοΈ

25 min read
0 views
Table of Contents

Fleet management is like having a command center for all your vehicles! 🎯 It helps track locations, monitor driver behavior, optimize routes, and keep vehicles running smoothly. Let’s build a comprehensive fleet management system on Alpine Linux! πŸš€

What is Fleet Management? πŸ€”

Fleet management includes:

  • Vehicle tracking - Real-time GPS location
  • Route optimization - Find the best paths
  • Driver monitoring - Safety and performance
  • Maintenance scheduling - Preventive care
  • Fuel management - Track consumption

Think of it as a smart assistant for your entire vehicle fleet! πŸš—

Installing Core Components πŸ“¦

Set up the fleet management infrastructure:

# Update package list
sudo apk update

# Install database systems
sudo apk add postgresql postgresql-client
sudo apk add redis

# Install message broker
sudo apk add rabbitmq-server

# Install web server and runtime
sudo apk add nginx
sudo apk add nodejs npm
sudo apk add python3 py3-pip python3-dev

# Install geo libraries
sudo apk add proj geos gdal
sudo apk add postgis

# Install monitoring tools
sudo apk add prometheus grafana

GPS Tracking System πŸ“

Create the vehicle tracking backend:

# Create project structure
mkdir -p ~/fleet-management/{gps,api,dashboard,analytics}
cd ~/fleet-management

# GPS data receiver
cat > gps/gps_receiver.py << 'EOF'
#!/usr/bin/env python3
import asyncio
import json
import random
import math
from datetime import datetime
import asyncpg
import aioredis
import pika

class VehicleSimulator:
    def __init__(self, vehicle_id, route_coords):
        self.vehicle_id = vehicle_id
        self.route_coords = route_coords
        self.current_index = 0
        self.speed = random.uniform(40, 60)  # km/h
        self.fuel_level = random.uniform(50, 100)  # percentage
        self.engine_temp = random.uniform(80, 95)  # celsius
        self.odometer = random.uniform(10000, 50000)  # km
        
    def get_next_position(self):
        """Simulate vehicle movement along route"""
        if self.current_index >= len(self.route_coords) - 1:
            self.current_index = 0  # Loop back to start
            
        # Get current and next point
        current = self.route_coords[self.current_index]
        next_point = self.route_coords[self.current_index + 1]
        
        # Interpolate between points
        progress = random.uniform(0.1, 0.3)
        lat = current[0] + (next_point[0] - current[0]) * progress
        lon = current[1] + (next_point[1] - current[1]) * progress
        
        # Move to next segment occasionally
        if random.random() > 0.7:
            self.current_index += 1
            
        # Update vehicle stats
        self.speed = max(0, self.speed + random.uniform(-5, 5))
        self.fuel_level = max(0, self.fuel_level - random.uniform(0.01, 0.1))
        self.engine_temp = min(120, max(70, self.engine_temp + random.uniform(-2, 2)))
        self.odometer += self.speed / 3600  # Add distance based on speed
        
        return lat, lon
        
    def get_telemetry(self):
        """Get current vehicle telemetry"""
        lat, lon = self.get_next_position()
        
        return {
            "vehicle_id": self.vehicle_id,
            "timestamp": datetime.utcnow().isoformat(),
            "location": {
                "lat": round(lat, 6),
                "lon": round(lon, 6),
                "speed": round(self.speed, 1),
                "heading": random.randint(0, 359),
                "altitude": random.randint(100, 300)
            },
            "engine": {
                "rpm": int(self.speed * 40),  # Simplified RPM calculation
                "temperature": round(self.engine_temp, 1),
                "oil_pressure": random.uniform(30, 60),
                "fuel_level": round(self.fuel_level, 1)
            },
            "vehicle": {
                "odometer": round(self.odometer, 1),
                "battery_voltage": round(random.uniform(12.5, 14.5), 1),
                "tire_pressure": [
                    random.uniform(32, 35) for _ in range(4)
                ]
            },
            "driver": {
                "id": f"DRV{random.randint(100, 999)}",
                "status": "active",
                "harsh_braking": random.random() < 0.05,
                "harsh_acceleration": random.random() < 0.05,
                "speeding": self.speed > 80
            }
        }

class GPSReceiver:
    def __init__(self):
        self.db_pool = None
        self.redis = None
        self.rabbitmq = None
        self.vehicles = {}
        
    async def init(self):
        """Initialize connections"""
        # PostgreSQL connection
        self.db_pool = await asyncpg.create_pool(
            'postgresql://fleet_user:password@localhost/fleet_db'
        )
        
        # Redis connection
        self.redis = await aioredis.create_redis_pool('redis://localhost')
        
        # RabbitMQ connection
        connection = pika.BlockingConnection(
            pika.ConnectionParameters('localhost')
        )
        self.channel = connection.channel()
        self.channel.queue_declare(queue='gps_data')
        
        # Initialize database schema
        await self.init_database()
        
    async def init_database(self):
        """Create database tables"""
        async with self.db_pool.acquire() as conn:
            await conn.execute('''
                CREATE TABLE IF NOT EXISTS vehicles (
                    id VARCHAR(50) PRIMARY KEY,
                    make VARCHAR(50),
                    model VARCHAR(50),
                    year INTEGER,
                    license_plate VARCHAR(20),
                    vin VARCHAR(50),
                    status VARCHAR(20) DEFAULT 'active'
                );
                
                CREATE TABLE IF NOT EXISTS gps_history (
                    id SERIAL PRIMARY KEY,
                    vehicle_id VARCHAR(50) REFERENCES vehicles(id),
                    timestamp TIMESTAMPTZ DEFAULT NOW(),
                    location GEOGRAPHY(POINT),
                    speed FLOAT,
                    heading INTEGER,
                    telemetry JSONB
                );
                
                CREATE INDEX idx_gps_vehicle_time ON gps_history(vehicle_id, timestamp DESC);
                
                CREATE TABLE IF NOT EXISTS trips (
                    id SERIAL PRIMARY KEY,
                    vehicle_id VARCHAR(50) REFERENCES vehicles(id),
                    driver_id VARCHAR(50),
                    start_time TIMESTAMPTZ,
                    end_time TIMESTAMPTZ,
                    start_location GEOGRAPHY(POINT),
                    end_location GEOGRAPHY(POINT),
                    distance_km FLOAT,
                    duration_minutes INTEGER,
                    fuel_used_liters FLOAT,
                    route_points JSONB
                );
                
                CREATE TABLE IF NOT EXISTS alerts (
                    id SERIAL PRIMARY KEY,
                    vehicle_id VARCHAR(50) REFERENCES vehicles(id),
                    alert_type VARCHAR(50),
                    severity VARCHAR(20),
                    message TEXT,
                    data JSONB,
                    created_at TIMESTAMPTZ DEFAULT NOW(),
                    resolved BOOLEAN DEFAULT FALSE
                );
                
                CREATE TABLE IF NOT EXISTS maintenance (
                    id SERIAL PRIMARY KEY,
                    vehicle_id VARCHAR(50) REFERENCES vehicles(id),
                    service_type VARCHAR(100),
                    scheduled_date DATE,
                    completed_date DATE,
                    mileage_at_service INTEGER,
                    cost DECIMAL(10,2),
                    notes TEXT
                );
            ''')
            
    async def process_gps_data(self, telemetry):
        """Process incoming GPS data"""
        vehicle_id = telemetry['vehicle_id']
        location = telemetry['location']
        
        # Store in database
        async with self.db_pool.acquire() as conn:
            await conn.execute('''
                INSERT INTO gps_history (vehicle_id, location, speed, heading, telemetry)
                VALUES ($1, ST_MakePoint($2, $3)::geography, $4, $5, $6)
            ''', vehicle_id, location['lon'], location['lat'], 
                location['speed'], location['heading'], json.dumps(telemetry))
        
        # Update real-time cache
        await self.redis.setex(
            f"vehicle:{vehicle_id}:location",
            60,  # TTL 60 seconds
            json.dumps(telemetry)
        )
        
        # Check for alerts
        await self.check_alerts(telemetry)
        
        # Publish to message queue
        self.channel.basic_publish(
            exchange='',
            routing_key='gps_data',
            body=json.dumps(telemetry)
        )
        
    async def check_alerts(self, telemetry):
        """Check for alert conditions"""
        vehicle_id = telemetry['vehicle_id']
        alerts = []
        
        # Speeding alert
        if telemetry['location']['speed'] > 80:
            alerts.append({
                'type': 'speeding',
                'severity': 'warning',
                'message': f"Vehicle {vehicle_id} exceeding speed limit: {telemetry['location']['speed']} km/h"
            })
            
        # Low fuel alert
        if telemetry['engine']['fuel_level'] < 20:
            alerts.append({
                'type': 'low_fuel',
                'severity': 'warning',
                'message': f"Vehicle {vehicle_id} low fuel: {telemetry['engine']['fuel_level']}%"
            })
            
        # Engine temperature alert
        if telemetry['engine']['temperature'] > 100:
            alerts.append({
                'type': 'engine_temp',
                'severity': 'critical',
                'message': f"Vehicle {vehicle_id} engine overheating: {telemetry['engine']['temperature']}Β°C"
            })
            
        # Harsh driving alerts
        if telemetry['driver']['harsh_braking'] or telemetry['driver']['harsh_acceleration']:
            alerts.append({
                'type': 'harsh_driving',
                'severity': 'info',
                'message': f"Vehicle {vehicle_id} harsh driving detected"
            })
            
        # Store alerts
        async with self.db_pool.acquire() as conn:
            for alert in alerts:
                await conn.execute('''
                    INSERT INTO alerts (vehicle_id, alert_type, severity, message, data)
                    VALUES ($1, $2, $3, $4, $5)
                ''', vehicle_id, alert['type'], alert['severity'], 
                    alert['message'], json.dumps(telemetry))
                    
    async def simulate_fleet(self):
        """Simulate a fleet of vehicles"""
        # Sample routes (latitude, longitude pairs)
        routes = [
            # Route 1: City center loop
            [(37.7749, -122.4194), (37.7849, -122.4094), (37.7949, -122.4194), 
             (37.7849, -122.4294), (37.7749, -122.4194)],
            # Route 2: Highway route
            [(37.7749, -122.4194), (37.8049, -122.4494), (37.8349, -122.4794),
             (37.8649, -122.5094), (37.8349, -122.4794), (37.7749, -122.4194)],
            # Route 3: Suburban route
            [(37.7549, -122.4394), (37.7349, -122.4594), (37.7149, -122.4794),
             (37.7349, -122.4594), (37.7549, -122.4394)]
        ]
        
        # Create vehicle simulators
        for i in range(10):
            vehicle_id = f"VEH{1000 + i}"
            route = routes[i % len(routes)]
            self.vehicles[vehicle_id] = VehicleSimulator(vehicle_id, route)
            
            # Add vehicle to database
            async with self.db_pool.acquire() as conn:
                await conn.execute('''
                    INSERT INTO vehicles (id, make, model, year, license_plate)
                    VALUES ($1, $2, $3, $4, $5)
                    ON CONFLICT (id) DO NOTHING
                ''', vehicle_id, 'Ford', 'Transit', 2022, f"ABC{1000 + i}")
                
        # Start simulation
        while True:
            tasks = []
            for vehicle in self.vehicles.values():
                telemetry = vehicle.get_telemetry()
                tasks.append(self.process_gps_data(telemetry))
                
            await asyncio.gather(*tasks)
            await asyncio.sleep(5)  # Update every 5 seconds

async def main():
    receiver = GPSReceiver()
    await receiver.init()
    await receiver.simulate_fleet()

if __name__ == "__main__":
    asyncio.run(main())
EOF

chmod +x gps/gps_receiver.py

Fleet Management API πŸ”Œ

Build the REST API for fleet operations:

# API server
cd api
npm init -y
npm install express cors body-parser pg redis socket.io amqplib
npm install bcrypt jsonwebtoken
npm install @turf/turf  # For geo calculations

cat > server.js << 'EOF'
const express = require('express');
const cors = require('cors');
const bodyParser = require('body-parser');
const { Pool } = require('pg');
const redis = require('redis');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
const socketIo = require('socket.io');
const amqp = require('amqplib');
const turf = require('@turf/turf');

const app = express();
const server = require('http').createServer(app);
const io = socketIo(server, { cors: { origin: '*' } });

// Middleware
app.use(cors());
app.use(bodyParser.json());

// Database connection
const pool = new Pool({
    user: 'fleet_user',
    host: 'localhost',
    database: 'fleet_db',
    password: 'password',
    port: 5432,
});

// Redis connection
const redisClient = redis.createClient();
redisClient.connect();

// RabbitMQ connection
let channel;
amqp.connect('amqp://localhost').then(conn => {
    return conn.createChannel();
}).then(ch => {
    channel = ch;
    return channel.assertQueue('gps_data');
}).then(() => {
    // Consume GPS data and broadcast via WebSocket
    channel.consume('gps_data', (msg) => {
        if (msg) {
            const data = JSON.parse(msg.content.toString());
            io.emit('gps_update', data);
            channel.ack(msg);
        }
    });
});

// Authentication middleware
const authenticate = (req, res, next) => {
    const token = req.headers.authorization?.split(' ')[1];
    if (!token) {
        return res.status(401).json({ error: 'No token provided' });
    }
    
    try {
        const decoded = jwt.verify(token, 'your-secret-key');
        req.user = decoded;
        next();
    } catch (error) {
        res.status(401).json({ error: 'Invalid token' });
    }
};

// Routes

// Get all vehicles
app.get('/api/vehicles', authenticate, async (req, res) => {
    try {
        const result = await pool.query(`
            SELECT v.*, 
                   ST_AsGeoJSON(h.location) as last_location,
                   h.speed as last_speed,
                   h.timestamp as last_update
            FROM vehicles v
            LEFT JOIN LATERAL (
                SELECT location, speed, timestamp
                FROM gps_history
                WHERE vehicle_id = v.id
                ORDER BY timestamp DESC
                LIMIT 1
            ) h ON true
            ORDER BY v.id
        `);
        
        const vehicles = await Promise.all(result.rows.map(async (vehicle) => {
            // Get real-time data from Redis
            const realtimeData = await redisClient.get(`vehicle:${vehicle.id}:location`);
            if (realtimeData) {
                const telemetry = JSON.parse(realtimeData);
                vehicle.realtime = telemetry;
            }
            
            if (vehicle.last_location) {
                vehicle.last_location = JSON.parse(vehicle.last_location);
            }
            
            return vehicle;
        }));
        
        res.json(vehicles);
    } catch (error) {
        console.error('Error fetching vehicles:', error);
        res.status(500).json({ error: 'Failed to fetch vehicles' });
    }
});

// Get vehicle details
app.get('/api/vehicles/:id', authenticate, async (req, res) => {
    try {
        const { id } = req.params;
        
        // Get vehicle info
        const vehicleResult = await pool.query(
            'SELECT * FROM vehicles WHERE id = $1',
            [id]
        );
        
        if (vehicleResult.rows.length === 0) {
            return res.status(404).json({ error: 'Vehicle not found' });
        }
        
        const vehicle = vehicleResult.rows[0];
        
        // Get latest position
        const positionResult = await pool.query(`
            SELECT ST_AsGeoJSON(location) as location, speed, heading, timestamp, telemetry
            FROM gps_history
            WHERE vehicle_id = $1
            ORDER BY timestamp DESC
            LIMIT 1
        `, [id]);
        
        if (positionResult.rows.length > 0) {
            vehicle.current_position = {
                ...positionResult.rows[0],
                location: JSON.parse(positionResult.rows[0].location)
            };
        }
        
        // Get today's route
        const routeResult = await pool.query(`
            SELECT ST_AsGeoJSON(location) as location, timestamp, speed
            FROM gps_history
            WHERE vehicle_id = $1 AND timestamp > NOW() - INTERVAL '24 hours'
            ORDER BY timestamp
        `, [id]);
        
        vehicle.todays_route = routeResult.rows.map(point => ({
            ...point,
            location: JSON.parse(point.location)
        }));
        
        // Get active alerts
        const alertsResult = await pool.query(`
            SELECT * FROM alerts
            WHERE vehicle_id = $1 AND resolved = false
            ORDER BY created_at DESC
        `, [id]);
        
        vehicle.active_alerts = alertsResult.rows;
        
        res.json(vehicle);
    } catch (error) {
        console.error('Error fetching vehicle details:', error);
        res.status(500).json({ error: 'Failed to fetch vehicle details' });
    }
});

// Get vehicle history
app.get('/api/vehicles/:id/history', authenticate, async (req, res) => {
    try {
        const { id } = req.params;
        const { start_date, end_date } = req.query;
        
        let query = `
            SELECT ST_AsGeoJSON(location) as location, speed, heading, timestamp
            FROM gps_history
            WHERE vehicle_id = $1
        `;
        const params = [id];
        
        if (start_date) {
            query += ' AND timestamp >= $2';
            params.push(start_date);
        }
        
        if (end_date) {
            query += ` AND timestamp <= $${params.length + 1}`;
            params.push(end_date);
        }
        
        query += ' ORDER BY timestamp DESC LIMIT 1000';
        
        const result = await pool.query(query, params);
        
        const history = result.rows.map(point => ({
            ...point,
            location: JSON.parse(point.location)
        }));
        
        res.json(history);
    } catch (error) {
        console.error('Error fetching vehicle history:', error);
        res.status(500).json({ error: 'Failed to fetch vehicle history' });
    }
});

// Calculate route statistics
app.get('/api/vehicles/:id/stats', authenticate, async (req, res) => {
    try {
        const { id } = req.params;
        const { date = new Date().toISOString().split('T')[0] } = req.query;
        
        // Get all points for the day
        const result = await pool.query(`
            SELECT ST_X(location::geometry) as lon, 
                   ST_Y(location::geometry) as lat,
                   speed, timestamp, telemetry
            FROM gps_history
            WHERE vehicle_id = $1 
                  AND DATE(timestamp) = $2
            ORDER BY timestamp
        `, [id, date]);
        
        if (result.rows.length < 2) {
            return res.json({
                date,
                total_distance: 0,
                total_time: 0,
                average_speed: 0,
                max_speed: 0,
                fuel_consumed: 0,
                idle_time: 0
            });
        }
        
        // Calculate statistics
        let totalDistance = 0;
        let maxSpeed = 0;
        let idleTime = 0;
        let fuelStart = null;
        let fuelEnd = null;
        
        for (let i = 1; i < result.rows.length; i++) {
            const prev = result.rows[i - 1];
            const curr = result.rows[i];
            
            // Calculate distance
            const from = turf.point([prev.lon, prev.lat]);
            const to = turf.point([curr.lon, curr.lat]);
            const distance = turf.distance(from, to, { units: 'kilometers' });
            totalDistance += distance;
            
            // Track max speed
            maxSpeed = Math.max(maxSpeed, curr.speed);
            
            // Track idle time
            if (curr.speed < 5) {
                const timeDiff = (new Date(curr.timestamp) - new Date(prev.timestamp)) / 1000 / 60;
                idleTime += timeDiff;
            }
            
            // Track fuel
            if (i === 1 && prev.telemetry?.engine?.fuel_level) {
                fuelStart = prev.telemetry.engine.fuel_level;
            }
            if (i === result.rows.length - 1 && curr.telemetry?.engine?.fuel_level) {
                fuelEnd = curr.telemetry.engine.fuel_level;
            }
        }
        
        const firstPoint = result.rows[0];
        const lastPoint = result.rows[result.rows.length - 1];
        const totalTime = (new Date(lastPoint.timestamp) - new Date(firstPoint.timestamp)) / 1000 / 60; // minutes
        
        res.json({
            date,
            total_distance: Math.round(totalDistance * 100) / 100,
            total_time: Math.round(totalTime),
            average_speed: totalDistance / (totalTime / 60),
            max_speed: maxSpeed,
            fuel_consumed: fuelStart && fuelEnd ? fuelStart - fuelEnd : 0,
            idle_time: Math.round(idleTime),
            stops: result.rows.filter(p => p.speed < 1).length
        });
    } catch (error) {
        console.error('Error calculating stats:', error);
        res.status(500).json({ error: 'Failed to calculate statistics' });
    }
});

// Get alerts
app.get('/api/alerts', authenticate, async (req, res) => {
    try {
        const { vehicle_id, severity, resolved } = req.query;
        
        let query = 'SELECT * FROM alerts WHERE 1=1';
        const params = [];
        
        if (vehicle_id) {
            params.push(vehicle_id);
            query += ` AND vehicle_id = $${params.length}`;
        }
        
        if (severity) {
            params.push(severity);
            query += ` AND severity = $${params.length}`;
        }
        
        if (resolved !== undefined) {
            params.push(resolved === 'true');
            query += ` AND resolved = $${params.length}`;
        }
        
        query += ' ORDER BY created_at DESC LIMIT 100';
        
        const result = await pool.query(query, params);
        res.json(result.rows);
    } catch (error) {
        console.error('Error fetching alerts:', error);
        res.status(500).json({ error: 'Failed to fetch alerts' });
    }
});

// Resolve alert
app.put('/api/alerts/:id/resolve', authenticate, async (req, res) => {
    try {
        const { id } = req.params;
        
        await pool.query(
            'UPDATE alerts SET resolved = true WHERE id = $1',
            [id]
        );
        
        res.json({ success: true });
    } catch (error) {
        console.error('Error resolving alert:', error);
        res.status(500).json({ error: 'Failed to resolve alert' });
    }
});

// Get trips
app.get('/api/trips', authenticate, async (req, res) => {
    try {
        const { vehicle_id, driver_id, date } = req.query;
        
        let query = `
            SELECT t.*, 
                   ST_AsGeoJSON(t.start_location) as start_location,
                   ST_AsGeoJSON(t.end_location) as end_location,
                   v.make, v.model, v.license_plate
            FROM trips t
            JOIN vehicles v ON t.vehicle_id = v.id
            WHERE 1=1
        `;
        const params = [];
        
        if (vehicle_id) {
            params.push(vehicle_id);
            query += ` AND t.vehicle_id = $${params.length}`;
        }
        
        if (driver_id) {
            params.push(driver_id);
            query += ` AND t.driver_id = $${params.length}`;
        }
        
        if (date) {
            params.push(date);
            query += ` AND DATE(t.start_time) = $${params.length}`;
        }
        
        query += ' ORDER BY t.start_time DESC LIMIT 100';
        
        const result = await pool.query(query, params);
        
        const trips = result.rows.map(trip => ({
            ...trip,
            start_location: trip.start_location ? JSON.parse(trip.start_location) : null,
            end_location: trip.end_location ? JSON.parse(trip.end_location) : null
        }));
        
        res.json(trips);
    } catch (error) {
        console.error('Error fetching trips:', error);
        res.status(500).json({ error: 'Failed to fetch trips' });
    }
});

// Schedule maintenance
app.post('/api/maintenance', authenticate, async (req, res) => {
    try {
        const { vehicle_id, service_type, scheduled_date, notes } = req.body;
        
        const result = await pool.query(`
            INSERT INTO maintenance (vehicle_id, service_type, scheduled_date, notes)
            VALUES ($1, $2, $3, $4)
            RETURNING *
        `, [vehicle_id, service_type, scheduled_date, notes]);
        
        res.json(result.rows[0]);
    } catch (error) {
        console.error('Error scheduling maintenance:', error);
        res.status(500).json({ error: 'Failed to schedule maintenance' });
    }
});

// Get maintenance schedule
app.get('/api/maintenance', authenticate, async (req, res) => {
    try {
        const { vehicle_id, upcoming } = req.query;
        
        let query = `
            SELECT m.*, v.make, v.model, v.license_plate
            FROM maintenance m
            JOIN vehicles v ON m.vehicle_id = v.id
            WHERE 1=1
        `;
        const params = [];
        
        if (vehicle_id) {
            params.push(vehicle_id);
            query += ` AND m.vehicle_id = $${params.length}`;
        }
        
        if (upcoming === 'true') {
            query += ' AND m.completed_date IS NULL AND m.scheduled_date >= CURRENT_DATE';
        }
        
        query += ' ORDER BY m.scheduled_date';
        
        const result = await pool.query(query, params);
        res.json(result.rows);
    } catch (error) {
        console.error('Error fetching maintenance:', error);
        res.status(500).json({ error: 'Failed to fetch maintenance' });
    }
});

// WebSocket connection for real-time updates
io.on('connection', (socket) => {
    console.log('Client connected:', socket.id);
    
    // Join vehicle-specific rooms
    socket.on('track_vehicle', (vehicleId) => {
        socket.join(`vehicle:${vehicleId}`);
    });
    
    socket.on('untrack_vehicle', (vehicleId) => {
        socket.leave(`vehicle:${vehicleId}`);
    });
    
    socket.on('disconnect', () => {
        console.log('Client disconnected:', socket.id);
    });
});

// Start server
const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
    console.log(`Fleet Management API running on port ${PORT}`);
});
EOF

Fleet Dashboard Interface πŸ–₯️

Create the web dashboard:

# Dashboard frontend
cd ../dashboard
cat > index.html << 'EOF'
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Fleet Management Dashboard</title>
    <link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" />
    <script src="https://unpkg.com/[email protected]/dist/leaflet.js"></script>
    <script src="/socket.io/socket.io.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
            background: #f5f5f5;
        }
        .header {
            background: #2c3e50;
            color: white;
            padding: 1rem 2rem;
            display: flex;
            justify-content: space-between;
            align-items: center;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }
        .container {
            display: grid;
            grid-template-columns: 300px 1fr;
            height: calc(100vh - 60px);
        }
        .sidebar {
            background: white;
            border-right: 1px solid #e0e0e0;
            overflow-y: auto;
        }
        .main {
            position: relative;
            overflow: hidden;
        }
        #map {
            width: 100%;
            height: 100%;
        }
        .vehicle-list {
            padding: 1rem;
        }
        .vehicle-item {
            background: #f8f8f8;
            padding: 1rem;
            margin-bottom: 0.5rem;
            border-radius: 8px;
            cursor: pointer;
            transition: all 0.2s;
            border: 2px solid transparent;
        }
        .vehicle-item:hover {
            background: #e8e8e8;
        }
        .vehicle-item.selected {
            border-color: #3498db;
            background: #e3f2fd;
        }
        .vehicle-status {
            display: inline-block;
            width: 10px;
            height: 10px;
            border-radius: 50%;
            margin-right: 0.5rem;
        }
        .status-active { background: #27ae60; }
        .status-idle { background: #f39c12; }
        .status-offline { background: #e74c3c; }
        .vehicle-info {
            margin-top: 0.5rem;
            font-size: 0.85rem;
            color: #666;
        }
        .stats-panel {
            position: absolute;
            top: 1rem;
            right: 1rem;
            background: white;
            padding: 1.5rem;
            border-radius: 8px;
            box-shadow: 0 2px 8px rgba(0,0,0,0.1);
            width: 300px;
            max-height: 80vh;
            overflow-y: auto;
        }
        .stat-item {
            margin-bottom: 1rem;
            padding-bottom: 1rem;
            border-bottom: 1px solid #eee;
        }
        .stat-label {
            font-size: 0.85rem;
            color: #666;
            margin-bottom: 0.25rem;
        }
        .stat-value {
            font-size: 1.5rem;
            font-weight: bold;
            color: #2c3e50;
        }
        .alert-badge {
            background: #e74c3c;
            color: white;
            padding: 0.25rem 0.5rem;
            border-radius: 12px;
            font-size: 0.75rem;
            margin-left: 0.5rem;
        }
        .vehicle-marker {
            background: #3498db;
            color: white;
            padding: 0.5rem;
            border-radius: 50%;
            text-align: center;
            font-weight: bold;
            box-shadow: 0 2px 4px rgba(0,0,0,0.2);
        }
        .route-info {
            background: #f8f8f8;
            padding: 1rem;
            border-radius: 8px;
            margin-top: 1rem;
        }
        .tabs {
            display: flex;
            gap: 1rem;
            margin-bottom: 1rem;
            border-bottom: 1px solid #e0e0e0;
        }
        .tab {
            padding: 0.5rem 1rem;
            cursor: pointer;
            border-bottom: 2px solid transparent;
        }
        .tab.active {
            color: #3498db;
            border-bottom-color: #3498db;
        }
        .tab-content {
            display: none;
        }
        .tab-content.active {
            display: block;
        }
        #alerts-list {
            max-height: 300px;
            overflow-y: auto;
        }
        .alert-item {
            background: #fff3cd;
            border: 1px solid #ffeaa7;
            padding: 0.75rem;
            margin-bottom: 0.5rem;
            border-radius: 4px;
            font-size: 0.85rem;
        }
        .alert-item.critical {
            background: #f8d7da;
            border-color: #f5c6cb;
        }
        .btn {
            background: #3498db;
            color: white;
            border: none;
            padding: 0.5rem 1rem;
            border-radius: 4px;
            cursor: pointer;
            font-size: 0.9rem;
        }
        .btn:hover {
            background: #2980b9;
        }
        .btn-small {
            padding: 0.25rem 0.5rem;
            font-size: 0.8rem;
        }
    </style>
</head>
<body>
    <header class="header">
        <h1>πŸš› Fleet Management System</h1>
        <div>
            <span id="total-vehicles">0</span> Vehicles | 
            <span id="active-vehicles">0</span> Active | 
            <span id="total-alerts">0</span> Alerts
        </div>
    </header>
    
    <div class="container">
        <aside class="sidebar">
            <div class="tabs">
                <div class="tab active" onclick="switchTab('vehicles')">Vehicles</div>
                <div class="tab" onclick="switchTab('alerts')">Alerts</div>
            </div>
            
            <div id="vehicles-tab" class="tab-content active">
                <div class="vehicle-list" id="vehicle-list">
                    <!-- Vehicles will be loaded here -->
                </div>
            </div>
            
            <div id="alerts-tab" class="tab-content">
                <div id="alerts-list">
                    <!-- Alerts will be loaded here -->
                </div>
            </div>
        </aside>
        
        <main class="main">
            <div id="map"></div>
            
            <div class="stats-panel" id="stats-panel" style="display: none;">
                <h3 id="selected-vehicle">No Vehicle Selected</h3>
                
                <div class="stat-item">
                    <div class="stat-label">Current Speed</div>
                    <div class="stat-value" id="current-speed">0 km/h</div>
                </div>
                
                <div class="stat-item">
                    <div class="stat-label">Location</div>
                    <div id="current-location">Unknown</div>
                </div>
                
                <div class="stat-item">
                    <div class="stat-label">Driver</div>
                    <div id="current-driver">Unknown</div>
                </div>
                
                <div class="stat-item">
                    <div class="stat-label">Fuel Level</div>
                    <div class="stat-value" id="fuel-level">0%</div>
                </div>
                
                <div class="stat-item">
                    <div class="stat-label">Today's Distance</div>
                    <div class="stat-value" id="todays-distance">0 km</div>
                </div>
                
                <div class="route-info">
                    <h4>Today's Route</h4>
                    <canvas id="speed-chart" width="300" height="150"></canvas>
                </div>
                
                <button class="btn" onclick="showVehicleHistory()">View History</button>
                <button class="btn" onclick="scheduleMainenance()">Schedule Maintenance</button>
            </div>
        </main>
    </div>
    
    <script>
        // Initialize map
        const map = L.map('map').setView([37.7749, -122.4194], 12);
        L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
            attribution: 'Β© OpenStreetMap contributors'
        }).addTo(map);
        
        // WebSocket connection
        const socket = io('http://localhost:3000');
        
        // Data storage
        let vehicles = {};
        let vehicleMarkers = {};
        let selectedVehicle = null;
        let routePolylines = {};
        let speedChart = null;
        
        // Custom vehicle icon
        const vehicleIcon = L.divIcon({
            className: 'vehicle-marker',
            html: '<div>πŸš›</div>',
            iconSize: [30, 30]
        });
        
        // Initialize
        async function init() {
            await loadVehicles();
            await loadAlerts();
            setupSocketListeners();
            initSpeedChart();
        }
        
        // Load vehicles
        async function loadVehicles() {
            const response = await fetch('/api/vehicles', {
                headers: { 'Authorization': 'Bearer ' + localStorage.getItem('token') }
            });
            
            const vehicleList = await response.json();
            
            document.getElementById('total-vehicles').textContent = vehicleList.length;
            
            const vehicleListEl = document.getElementById('vehicle-list');
            vehicleListEl.innerHTML = '';
            
            let activeCount = 0;
            
            vehicleList.forEach(vehicle => {
                vehicles[vehicle.id] = vehicle;
                
                // Add to sidebar
                const item = document.createElement('div');
                item.className = 'vehicle-item';
                item.onclick = () => selectVehicle(vehicle.id);
                
                const isActive = vehicle.realtime && 
                    (Date.now() - new Date(vehicle.realtime.timestamp).getTime()) < 60000;
                
                if (isActive) activeCount++;
                
                item.innerHTML = `
                    <div>
                        <span class="vehicle-status ${isActive ? 'status-active' : 'status-offline'}"></span>
                        <strong>${vehicle.id}</strong>
                        ${vehicle.realtime?.driver?.speeding ? '<span class="alert-badge">⚑</span>' : ''}
                    </div>
                    <div class="vehicle-info">
                        ${vehicle.make} ${vehicle.model} - ${vehicle.license_plate}<br>
                        ${vehicle.realtime ? 
                            `${vehicle.realtime.location.speed.toFixed(1)} km/h` : 
                            'Offline'}
                    </div>
                `;
                
                vehicleListEl.appendChild(item);
                
                // Add to map
                if (vehicle.realtime) {
                    addVehicleToMap(vehicle);
                }
            });
            
            document.getElementById('active-vehicles').textContent = activeCount;
        }
        
        // Add vehicle to map
        function addVehicleToMap(vehicle) {
            const location = vehicle.realtime.location;
            
            if (vehicleMarkers[vehicle.id]) {
                vehicleMarkers[vehicle.id].setLatLng([location.lat, location.lon]);
            } else {
                const marker = L.marker([location.lat, location.lon], { icon: vehicleIcon })
                    .bindPopup(`
                        <strong>${vehicle.id}</strong><br>
                        ${vehicle.make} ${vehicle.model}<br>
                        Speed: ${location.speed.toFixed(1)} km/h
                    `)
                    .addTo(map);
                
                vehicleMarkers[vehicle.id] = marker;
            }
        }
        
        // Select vehicle
        async function selectVehicle(vehicleId) {
            selectedVehicle = vehicleId;
            
            // Update UI
            document.querySelectorAll('.vehicle-item').forEach(item => {
                item.classList.remove('selected');
            });
            event.currentTarget.classList.add('selected');
            
            // Show stats panel
            document.getElementById('stats-panel').style.display = 'block';
            document.getElementById('selected-vehicle').textContent = vehicleId;
            
            // Load vehicle details
            const response = await fetch(`/api/vehicles/${vehicleId}`, {
                headers: { 'Authorization': 'Bearer ' + localStorage.getItem('token') }
            });
            
            const vehicle = await response.json();
            
            // Update stats
            if (vehicle.current_position) {
                const telemetry = vehicle.current_position.telemetry;
                document.getElementById('current-speed').textContent = 
                    `${telemetry.location.speed.toFixed(1)} km/h`;
                document.getElementById('current-location').textContent = 
                    `${telemetry.location.lat.toFixed(4)}, ${telemetry.location.lon.toFixed(4)}`;
                document.getElementById('current-driver').textContent = 
                    telemetry.driver.id;
                document.getElementById('fuel-level').textContent = 
                    `${telemetry.engine.fuel_level.toFixed(1)}%`;
            }
            
            // Load today's stats
            const statsResponse = await fetch(`/api/vehicles/${vehicleId}/stats`, {
                headers: { 'Authorization': 'Bearer ' + localStorage.getItem('token') }
            });
            
            const stats = await statsResponse.json();
            document.getElementById('todays-distance').textContent = 
                `${stats.total_distance.toFixed(1)} km`;
            
            // Draw route
            if (vehicle.todays_route && vehicle.todays_route.length > 1) {
                drawRoute(vehicleId, vehicle.todays_route);
                updateSpeedChart(vehicle.todays_route);
            }
            
            // Center map on vehicle
            if (vehicleMarkers[vehicleId]) {
                map.setView(vehicleMarkers[vehicleId].getLatLng(), 15);
            }
            
            // Subscribe to real-time updates
            socket.emit('track_vehicle', vehicleId);
        }
        
        // Draw route on map
        function drawRoute(vehicleId, route) {
            // Remove existing route
            if (routePolylines[vehicleId]) {
                map.removeLayer(routePolylines[vehicleId]);
            }
            
            const coordinates = route.map(point => [
                point.location.coordinates[1],
                point.location.coordinates[0]
            ]);
            
            const polyline = L.polyline(coordinates, {
                color: '#3498db',
                weight: 4,
                opacity: 0.7
            }).addTo(map);
            
            routePolylines[vehicleId] = polyline;
        }
        
        // Initialize speed chart
        function initSpeedChart() {
            const ctx = document.getElementById('speed-chart').getContext('2d');
            speedChart = new Chart(ctx, {
                type: 'line',
                data: {
                    labels: [],
                    datasets: [{
                        label: 'Speed (km/h)',
                        data: [],
                        borderColor: '#3498db',
                        tension: 0.1
                    }]
                },
                options: {
                    responsive: true,
                    maintainAspectRatio: false,
                    scales: {
                        y: {
                            beginAtZero: true,
                            max: 120
                        }
                    }
                }
            });
        }
        
        // Update speed chart
        function updateSpeedChart(route) {
            const labels = route.map(point => 
                new Date(point.timestamp).toLocaleTimeString()
            );
            const speeds = route.map(point => point.speed);
            
            speedChart.data.labels = labels;
            speedChart.data.datasets[0].data = speeds;
            speedChart.update();
        }
        
        // Load alerts
        async function loadAlerts() {
            const response = await fetch('/api/alerts?resolved=false', {
                headers: { 'Authorization': 'Bearer ' + localStorage.getItem('token') }
            });
            
            const alerts = await response.json();
            
            document.getElementById('total-alerts').textContent = alerts.length;
            
            const alertsList = document.getElementById('alerts-list');
            alertsList.innerHTML = '';
            
            alerts.forEach(alert => {
                const item = document.createElement('div');
                item.className = `alert-item ${alert.severity}`;
                item.innerHTML = `
                    <strong>${alert.vehicle_id}</strong> - ${alert.alert_type}<br>
                    ${alert.message}<br>
                    <small>${new Date(alert.created_at).toLocaleString()}</small>
                    <button class="btn btn-small" onclick="resolveAlert(${alert.id})">Resolve</button>
                `;
                alertsList.appendChild(item);
            });
        }
        
        // Resolve alert
        async function resolveAlert(alertId) {
            await fetch(`/api/alerts/${alertId}/resolve`, {
                method: 'PUT',
                headers: { 'Authorization': 'Bearer ' + localStorage.getItem('token') }
            });
            
            loadAlerts();
        }
        
        // Setup socket listeners
        function setupSocketListeners() {
            socket.on('gps_update', (data) => {
                // Update vehicle data
                if (vehicles[data.vehicle_id]) {
                    vehicles[data.vehicle_id].realtime = data;
                    addVehicleToMap(vehicles[data.vehicle_id]);
                    
                    // Update sidebar
                    loadVehicles();
                    
                    // Update selected vehicle stats
                    if (selectedVehicle === data.vehicle_id) {
                        document.getElementById('current-speed').textContent = 
                            `${data.location.speed.toFixed(1)} km/h`;
                        document.getElementById('fuel-level').textContent = 
                            `${data.engine.fuel_level.toFixed(1)}%`;
                    }
                }
            });
        }
        
        // Switch tabs
        function switchTab(tab) {
            document.querySelectorAll('.tab').forEach(t => {
                t.classList.remove('active');
            });
            document.querySelectorAll('.tab-content').forEach(c => {
                c.classList.remove('active');
            });
            
            event.target.classList.add('active');
            document.getElementById(`${tab}-tab`).classList.add('active');
        }
        
        // Show vehicle history
        function showVehicleHistory() {
            // Implement history view
            alert('Vehicle history view coming soon!');
        }
        
        // Schedule maintenance
        function scheduleMainenance() {
            // Implement maintenance scheduling
            alert('Maintenance scheduling coming soon!');
        }
        
        // Set demo token (in production, implement proper login)
        localStorage.setItem('token', 'demo-token');
        
        // Initialize app
        init();
    </script>
</body>
</html>
EOF

Route Optimization Engine πŸ—ΊοΈ

Optimize delivery routes:

# Route optimization
cat > analytics/route_optimizer.py << 'EOF'
#!/usr/bin/env python3
import numpy as np
from scipy.spatial.distance import cdist
from ortools.constraint_solver import routing_enums_pb2
from ortools.constraint_solver import pywrapcp
import asyncpg
import json

class RouteOptimizer:
    def __init__(self, db_config):
        self.db_config = db_config
        
    async def get_delivery_points(self, date):
        """Get delivery points for a specific date"""
        conn = await asyncpg.connect(**self.db_config)
        
        points = await conn.fetch('''
            SELECT id, customer_name, 
                   ST_X(location::geometry) as lon,
                   ST_Y(location::geometry) as lat,
                   delivery_window_start,
                   delivery_window_end,
                   package_weight,
                   priority
            FROM deliveries
            WHERE delivery_date = $1
            ORDER BY priority DESC
        ''', date)
        
        await conn.close()
        return points
        
    def calculate_distance_matrix(self, locations):
        """Calculate distance matrix between all points"""
        coords = np.array([[loc['lat'], loc['lon']] for loc in locations])
        
        # Calculate Euclidean distances (in production, use real road distances)
        distances = cdist(coords, coords) * 111  # Convert to km (approximation)
        
        return distances.astype(int)
        
    def optimize_routes(self, locations, num_vehicles, vehicle_capacity):
        """Optimize routes using OR-Tools"""
        # Create routing model
        manager = pywrapcp.RoutingIndexManager(
            len(locations), num_vehicles, 0
        )
        routing = pywrapcp.RoutingModel(manager)
        
        # Distance callback
        distance_matrix = self.calculate_distance_matrix(locations)
        
        def distance_callback(from_index, to_index):
            from_node = manager.IndexToNode(from_index)
            to_node = manager.IndexToNode(to_index)
            return distance_matrix[from_node][to_node]
        
        transit_callback_index = routing.RegisterTransitCallback(distance_callback)
        routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)
        
        # Capacity constraint
        def demand_callback(from_index):
            from_node = manager.IndexToNode(from_index)
            return locations[from_node].get('package_weight', 0)
        
        demand_callback_index = routing.RegisterUnaryTransitCallback(demand_callback)
        routing.AddDimensionWithVehicleCapacity(
            demand_callback_index,
            0,  # null capacity slack
            [vehicle_capacity] * num_vehicles,
            True,  # start cumul to zero
            'Capacity'
        )
        
        # Time windows
        time_dimension_name = 'Time'
        routing.AddDimension(
            transit_callback_index,
            30,  # allow waiting time
            7200,  # maximum time per vehicle
            False,  # Don't force start cumul to zero
            time_dimension_name
        )
        
        time_dimension = routing.GetDimensionOrDie(time_dimension_name)
        
        # Add time window constraints
        for location_idx, location in enumerate(locations):
            if location_idx == 0:  # Skip depot
                continue
            index = manager.NodeToIndex(location_idx)
            time_dimension.CumulVar(index).SetRange(
                int(location.get('delivery_window_start', 0)),
                int(location.get('delivery_window_end', 7200))
            )
        
        # Set search parameters
        search_parameters = pywrapcp.DefaultRoutingSearchParameters()
        search_parameters.first_solution_strategy = (
            routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC
        )
        search_parameters.local_search_metaheuristic = (
            routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH
        )
        search_parameters.time_limit.FromSeconds(30)
        
        # Solve
        solution = routing.SolveWithParameters(search_parameters)
        
        if solution:
            return self.extract_routes(manager, routing, solution, locations)
        
        return None
        
    def extract_routes(self, manager, routing, solution, locations):
        """Extract routes from solution"""
        routes = []
        
        for vehicle_id in range(routing.vehicles()):
            route = []
            index = routing.Start(vehicle_id)
            
            while not routing.IsEnd(index):
                node_index = manager.IndexToNode(index)
                route.append({
                    'location': locations[node_index],
                    'arrival_time': solution.Min(
                        routing.GetDimensionOrDie('Time').CumulVar(index)
                    )
                })
                index = solution.Value(routing.NextVar(index))
            
            if len(route) > 1:  # Exclude empty routes
                routes.append({
                    'vehicle_id': vehicle_id,
                    'route': route,
                    'total_distance': routing.GetArcCostForVehicle(
                        routing.Start(vehicle_id),
                        routing.End(vehicle_id),
                        vehicle_id
                    )
                })
        
        return routes
        
    def generate_turn_by_turn_directions(self, route):
        """Generate navigation instructions"""
        directions = []
        
        for i in range(len(route) - 1):
            current = route[i]['location']
            next_loc = route[i + 1]['location']
            
            # Calculate bearing
            lat1, lon1 = current['lat'], current['lon']
            lat2, lon2 = next_loc['lat'], next_loc['lon']
            
            bearing = self.calculate_bearing(lat1, lon1, lat2, lon2)
            distance = self.calculate_distance(lat1, lon1, lat2, lon2)
            
            # Generate instruction
            turn = self.get_turn_instruction(bearing)
            
            directions.append({
                'instruction': f"{turn} and continue for {distance:.1f} km",
                'distance': distance,
                'destination': next_loc['customer_name']
            })
        
        return directions
        
    def calculate_bearing(self, lat1, lon1, lat2, lon2):
        """Calculate bearing between two points"""
        lat1, lon1, lat2, lon2 = map(np.radians, [lat1, lon1, lat2, lon2])
        
        dlon = lon2 - lon1
        
        x = np.sin(dlon) * np.cos(lat2)
        y = np.cos(lat1) * np.sin(lat2) - np.sin(lat1) * np.cos(lat2) * np.cos(dlon)
        
        bearing = np.arctan2(x, y)
        bearing = np.degrees(bearing)
        bearing = (bearing + 360) % 360
        
        return bearing
        
    def get_turn_instruction(self, bearing):
        """Convert bearing to turn instruction"""
        directions = [
            (0, 45, "Head north"),
            (45, 135, "Turn right"),
            (135, 225, "Turn around"),
            (225, 315, "Turn left"),
            (315, 360, "Head north")
        ]
        
        for start, end, instruction in directions:
            if start <= bearing < end:
                return instruction
        
        return "Continue straight"
        
    def calculate_distance(self, lat1, lon1, lat2, lon2):
        """Calculate distance between two points (Haversine formula)"""
        R = 6371  # Earth's radius in km
        
        lat1, lon1, lat2, lon2 = map(np.radians, [lat1, lon1, lat2, lon2])
        
        dlat = lat2 - lat1
        dlon = lon2 - lon1
        
        a = np.sin(dlat/2)**2 + np.cos(lat1) * np.cos(lat2) * np.sin(dlon/2)**2
        c = 2 * np.arcsin(np.sqrt(a))
        
        return R * c

# Example usage
async def main():
    optimizer = RouteOptimizer({
        'host': 'localhost',
        'database': 'fleet_db',
        'user': 'fleet_user',
        'password': 'password'
    })
    
    # Get delivery points
    locations = await optimizer.get_delivery_points('2024-01-15')
    
    # Add depot as first location
    depot = {
        'id': 0,
        'customer_name': 'Depot',
        'lat': 37.7749,
        'lon': -122.4194,
        'package_weight': 0
    }
    locations = [depot] + list(locations)
    
    # Optimize routes
    routes = optimizer.optimize_routes(
        locations,
        num_vehicles=5,
        vehicle_capacity=1000
    )
    
    # Display results
    for route in routes:
        print(f"\nVehicle {route['vehicle_id']}:")
        print(f"Total distance: {route['total_distance']} km")
        
        for stop in route['route']:
            print(f"  - {stop['location']['customer_name']} "
                  f"(Arrival: {stop['arrival_time']} min)")

if __name__ == "__main__":
    import asyncio
    asyncio.run(main())
EOF

Maintenance Scheduler πŸ”§

Automate vehicle maintenance:

# Maintenance scheduler
cat > analytics/maintenance_scheduler.py << 'EOF'
#!/usr/bin/env python3
import asyncio
import asyncpg
from datetime import datetime, timedelta
import smtplib
from email.mime.text import MIMEText

class MaintenanceScheduler:
    def __init__(self, db_config):
        self.db_config = db_config
        self.maintenance_intervals = {
            'oil_change': {'mileage': 5000, 'days': 90},
            'tire_rotation': {'mileage': 10000, 'days': 180},
            'brake_inspection': {'mileage': 20000, 'days': 365},
            'transmission_service': {'mileage': 50000, 'days': 730},
            'annual_inspection': {'mileage': None, 'days': 365}
        }
        
    async def check_maintenance_due(self):
        """Check which vehicles need maintenance"""
        conn = await asyncpg.connect(**self.db_config)
        
        # Get all vehicles with their current mileage and last service dates
        vehicles = await conn.fetch('''
            SELECT v.id, v.make, v.model, v.license_plate,
                   h.odometer as current_mileage,
                   m.service_type,
                   m.mileage_at_service,
                   m.completed_date
            FROM vehicles v
            LEFT JOIN LATERAL (
                SELECT telemetry->>'vehicle'->>'odometer' as odometer
                FROM gps_history
                WHERE vehicle_id = v.id
                ORDER BY timestamp DESC
                LIMIT 1
            ) h ON true
            LEFT JOIN maintenance m ON v.id = m.vehicle_id
            WHERE v.status = 'active'
        ''')
        
        maintenance_due = []
        
        for vehicle in vehicles:
            for service_type, intervals in self.maintenance_intervals.items():
                if self.is_service_due(vehicle, service_type, intervals):
                    maintenance_due.append({
                        'vehicle_id': vehicle['id'],
                        'vehicle': f"{vehicle['make']} {vehicle['model']} ({vehicle['license_plate']})",
                        'service_type': service_type,
                        'current_mileage': vehicle['current_mileage'],
                        'reason': self.get_service_reason(vehicle, service_type, intervals)
                    })
        
        await conn.close()
        return maintenance_due
        
    def is_service_due(self, vehicle, service_type, intervals):
        """Check if specific service is due"""
        # Check mileage-based maintenance
        if intervals['mileage'] and vehicle['current_mileage']:
            last_service_mileage = 0
            for service in vehicle:
                if service.get('service_type') == service_type and service.get('mileage_at_service'):
                    last_service_mileage = max(last_service_mileage, service['mileage_at_service'])
            
            if vehicle['current_mileage'] - last_service_mileage >= intervals['mileage']:
                return True
        
        # Check time-based maintenance
        last_service_date = None
        for service in vehicle:
            if service.get('service_type') == service_type and service.get('completed_date'):
                if not last_service_date or service['completed_date'] > last_service_date:
                    last_service_date = service['completed_date']
        
        if last_service_date:
            days_since_service = (datetime.now().date() - last_service_date).days
            if days_since_service >= intervals['days']:
                return True
        elif intervals['days']:  # No previous service record
            return True
        
        return False
        
    def get_service_reason(self, vehicle, service_type, intervals):
        """Get reason why service is due"""
        reasons = []
        
        if intervals['mileage']:
            reasons.append(f"Every {intervals['mileage']:,} km")
        
        if intervals['days']:
            reasons.append(f"Every {intervals['days']} days")
        
        return " or ".join(reasons)
        
    async def schedule_maintenance(self, vehicle_id, service_type, scheduled_date):
        """Schedule maintenance for a vehicle"""
        conn = await asyncpg.connect(**self.db_config)
        
        await conn.execute('''
            INSERT INTO maintenance (vehicle_id, service_type, scheduled_date)
            VALUES ($1, $2, $3)
        ''', vehicle_id, service_type, scheduled_date)
        
        await conn.close()
        
    async def send_maintenance_alerts(self, maintenance_due):
        """Send email alerts for due maintenance"""
        if not maintenance_due:
            return
        
        # Group by service type
        by_service = {}
        for item in maintenance_due:
            service = item['service_type']
            if service not in by_service:
                by_service[service] = []
            by_service[service].append(item)
        
        # Create email content
        html_content = """
        <html>
        <body>
            <h2>Fleet Maintenance Alert</h2>
            <p>The following vehicles require maintenance:</p>
        """
        
        for service_type, vehicles in by_service.items():
            html_content += f"<h3>{service_type.replace('_', ' ').title()}</h3><ul>"
            for vehicle in vehicles:
                html_content += f"<li>{vehicle['vehicle']} - {vehicle['reason']}</li>"
            html_content += "</ul>"
        
        html_content += """
            <p>Please schedule maintenance appointments as soon as possible.</p>
        </body>
        </html>
        """
        
        # Send email (configure SMTP settings)
        msg = MIMEText(html_content, 'html')
        msg['Subject'] = f'Fleet Maintenance Alert - {len(maintenance_due)} vehicles need service'
        msg['From'] = '[email protected]'
        msg['To'] = '[email protected]'
        
        # In production, configure proper SMTP
        # smtp = smtplib.SMTP('localhost')
        # smtp.send_message(msg)
        # smtp.quit()
        
        print(f"Maintenance alert sent for {len(maintenance_due)} vehicles")
        
    async def run_daily_check(self):
        """Run daily maintenance check"""
        print(f"Running maintenance check at {datetime.now()}")
        
        maintenance_due = await self.check_maintenance_due()
        
        if maintenance_due:
            print(f"Found {len(maintenance_due)} vehicles needing maintenance:")
            for item in maintenance_due:
                print(f"  - {item['vehicle']}: {item['service_type']}")
            
            await self.send_maintenance_alerts(maintenance_due)
            
            # Auto-schedule maintenance for next week
            next_week = datetime.now().date() + timedelta(days=7)
            for item in maintenance_due:
                await self.schedule_maintenance(
                    item['vehicle_id'],
                    item['service_type'],
                    next_week
                )
        else:
            print("No vehicles require maintenance at this time")

# Run scheduler
async def main():
    scheduler = MaintenanceScheduler({
        'host': 'localhost',
        'database': 'fleet_db',
        'user': 'fleet_user',
        'password': 'password'
    })
    
    # Run once
    await scheduler.run_daily_check()
    
    # Or run continuously
    # while True:
    #     await scheduler.run_daily_check()
    #     await asyncio.sleep(86400)  # Check daily

if __name__ == "__main__":
    asyncio.run(main())
EOF

System Configuration πŸ”§

Set up the complete system:

# Database setup
cat > setup_database.sql << 'EOF'
-- Create database and user
CREATE DATABASE fleet_db;
CREATE USER fleet_user WITH PASSWORD 'password';
GRANT ALL PRIVILEGES ON DATABASE fleet_db TO fleet_user;

-- Connect to fleet_db
\c fleet_db

-- Enable PostGIS extension
CREATE EXTENSION IF NOT EXISTS postgis;

-- Grant permissions
GRANT ALL ON ALL TABLES IN SCHEMA public TO fleet_user;
GRANT ALL ON ALL SEQUENCES IN SCHEMA public TO fleet_user;
EOF

# System startup script
cat > start_fleet_system.sh << 'EOF'
#!/bin/sh
# Start Fleet Management System

echo "Starting Fleet Management System..."

# Start PostgreSQL
sudo rc-service postgresql start

# Start Redis
redis-server --daemonize yes

# Start RabbitMQ
sudo rc-service rabbitmq-server start

# Start GPS receiver (simulator)
cd ~/fleet-management
python3 gps/gps_receiver.py &

# Start API server
cd api && npm start &

# Start maintenance scheduler
python3 analytics/maintenance_scheduler.py &

# Serve dashboard
cd ../dashboard
python3 -m http.server 8080 &

echo "Fleet Management System started!"
echo "Dashboard: http://localhost:8080"
echo "API: http://localhost:3000"
EOF

chmod +x start_fleet_system.sh

Best Practices πŸ“Œ

  1. Data retention - Archive old GPS data
  2. Real-time accuracy - Use quality GPS hardware
  3. Driver privacy - Implement data protection
  4. Scalability - Design for fleet growth
  5. Redundancy - Backup critical systems

Troubleshooting πŸ”§

GPS Data Not Updating

# Check GPS receiver
ps aux | grep gps_receiver

# Check MQTT messages
mosquitto_sub -t "gps/#" -v

# Check database connection
psql -U fleet_user -d fleet_db -c "SELECT COUNT(*) FROM gps_history"

Performance Issues

# Optimize PostGIS queries
CREATE INDEX idx_gps_history_geography ON gps_history USING GIST(location);

# Partition large tables
CREATE TABLE gps_history_2024_01 PARTITION OF gps_history
FOR VALUES FROM ('2024-01-01') TO ('2024-02-01');

Quick Commands πŸ“‹

# Check vehicle locations
curl http://localhost:3000/api/vehicles -H "Authorization: Bearer token"

# Get vehicle route
curl http://localhost:3000/api/vehicles/VEH1001/history?date=2024-01-15

# Schedule maintenance
curl -X POST http://localhost:3000/api/maintenance \
  -H "Content-Type: application/json" \
  -d '{"vehicle_id":"VEH1001","service_type":"oil_change","scheduled_date":"2024-01-20"}'

# View real-time tracking
open http://localhost:8080

Conclusion 🎯

You’ve built a comprehensive fleet management system on Alpine Linux! From real-time GPS tracking to route optimization and automated maintenance scheduling, you now have all the tools to manage a modern vehicle fleet efficiently. This system helps reduce costs, improve safety, and maximize fleet productivity. Happy tracking! πŸš›βœ¨