Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Automotive Sdv expertise. Covers 6 topics: Cloud Vehicle Integration, Containerized Vehicle Apps, Digital Twin Vehicles, Ota Update Systems, Vehicle App Stores.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 682% | 0% |
| case-14 | ✗→✓ | ▲ Improved | 1481% | 0% |
| case-16 | ✗→✓ | ▲ Improved | 2154% | 0% |
| case-18 | ✗→✓ | ▲ Improved | 1287% | 0% |
| case-19 | ✗→✓ | ▲ Improved | 1507% | 0% |
Expert knowledge of vehicle-to-cloud connectivity (MQTT, AMQP, HTTP/2), telemetry streaming, remote diagnostics, cloud-based fleet management, and API gateways.
python#!/usr/bin/env python3 """ Vehicle telemetry client using MQTT. Streams vehicle data to cloud platform with offline buffering. """ import json import time import sqlite3 from dataclasses import dataclass, asdict from datetime import datetime from typing import Optional, List import paho.mqtt.client as mqtt import can @dataclass class TelemetryMessage: """Vehicle telemetry data point.""" vin: str timestamp: str message_type: str data: dict class VehicleTelemetryClient: """ MQTT-based telemetry client. Features: - Real-time telemetry streaming - Offline buffering with SQLite - Automatic reconnection - QoS levels for reliability - Compression for bandwidth optimization """ def __init__(self, config_path: str = "/etc/vehicle/telemetry-config.json"): self.config = self._load_config(config_path) self.vin = self._get_vin() self.mqtt_client = None self.can_bus = None self.offline_buffer = OfflineBuffer() self.connected = False def _load_config(self, path: str) -> dict: """Load configuration.""" with open(path, 'r') as f: return json.load(f) def _get_vin(self) -> str: """Get vehicle VIN.""" with open('/sys/firmware/devicetree/base/serial-number', 'r') as f: return f.read().strip() def connect(self): """Connect to MQTT broker.""" self.mqtt_client = mqtt.Client( client_id=f"vehicle-{self.vin}", clean_session=False, # Maintain session across reconnects protocol=mqtt.MQTTv5 ) # Set credentials self.mqtt_client.username_pw_set( self.config['mqtt_username'], self.config['mqtt_password'] ) # Configure TLS if self.config.get('mqtt_tls', True): self.mqtt_client.tls_set( ca_certs=self.config['mqtt_ca_cert'], certfile=self.config.get('mqtt_client_cert'), keyfile=self.config.get('mqtt_client_key') ) # Set callbacks self.mqtt_client.on_connect = self._on_connect self.mqtt_client.on_disconnect = self._on_disconnect self.mqtt_client.on_message = self._on_message self.mqtt_client.on_publish = self._on_publish # Set last will (notify cloud if vehicle disconnects unexpectedly) self.mqtt_client.will_set( f"vehicles/{self.vin}/status", payload=json.dumps({ "status": "offline", "timestamp": datetime.utcnow().isoformat() }), qos=1, retain=True ) # Connect print(f"[Telemetry] Connecting to {self.config['mqtt_broker']}:{self.config['mqtt_port']}") self.mqtt_client.connect( self.config['mqtt_broker'], self.config['mqtt_port'], keepalive=60 ) # Start network loop in background self.mqtt_client.loop_start() def _on_connect(self, client, userdata, flags, rc, properties=None): """Handle MQTT connection.""" if rc == 0: print("[Telemetry] Connected to MQTT broker") self.connected = True # Publish online status self.mqtt_client.publish( f"vehicles/{self.vin}/status", payload=json.dumps({ "status": "online", "timestamp": datetime.utcnow().isoformat(), "sw_version": self._get_software_version() }), qos=1, retain=True ) # Subscribe to command topics self.mqtt_client.subscribe(f"vehicles/{self.vin}/commands/#", qos=1) # Send buffered messages self._flush_offline_buffer() else: print(f"[Telemetry] Connection failed: {rc}") self.connected = False def _on_disconnect(self, client, userdata, rc): """Handle MQTT disconnection.""" print(f"[Telemetry] Disconnected from broker: {rc}") self.connected = False if rc != 0: print("[Telemetry] Unexpected disconnect, will reconnect") def _on_message(self, client, userdata, msg): """Handle incoming command messages.""" print(f"[Telemetry] Received command: {msg.topic}") try: payload = json.loads(msg.payload.decode()) self._handle_command(msg.topic, payload) except Exception as e: print(f"[Telemetry] Error processing command: {e}") def _on_publish(self, client, userdata, mid): """Handle successful publish.""" # Remove from offline buffer if it was buffered pass def _handle_command(self, topic: str, payload: dict): """Handle remote commands from cloud.""" command_type = topic.split('/')[-1] if command_type == "diagnostics": # Trigger diagnostic data collection print("[Telemetry] Starting diagnostic data collection") self._collect_diagnostics() elif command_type == "update": # Trigger OTA update check print("[Telemetry] Checking for updates") # Integration with OTA system elif command_type == "lock": # Remote lock command print("[Telemetry] Remote lock requested") self._remote_lock() elif command_type == "honk": # Remote horn activation print("[Telemetry] Remote honk requested") self._remote_honk() def publish_telemetry(self, message_type: str, data: dict, qos: int = 0): """ Publish telemetry message. Args: message_type: Type of telemetry (battery, location, speed, etc.) data: Telemetry data qos: MQTT QoS level (0, 1, or 2) """ msg = TelemetryMessage( vin=self.vin, timestamp=datetime.utcnow().isoformat(), message_type=message_type, data=data ) topic = f"vehicles/{self.vin}/telemetry/{message_type}" payload = json.dumps(asdict(msg)) if self.connected: result = self.mqtt_client.publish(topic, payload, qos=qos) if result.rc == mqtt.MQTT_ERR_SUCCESS: print(f"[Telemetry] Published {message_type}") else: print(f"[Telemetry] Publish failed: {result.rc}") # Buffer for later self.offline_buffer.store(topic, payload, qos) else: # Store in offline buffer self.offline_buffer.store(topic, payload, qos) print(f"[Telemetry] Buffered {message_type} (offline)") def _flush_offline_buffer(self): """Send buffered messages when connection restored.""" messages = self.offline_buffer.retrieve_all() print(f"[Telemetry] Flushing {len(messages)} buffered messages") for msg in messages: self.mqtt_client.publish(msg['topic'], msg['payload'], qos=msg['qos']) self.offline_buffer.delete(msg['id']) def start_can_monitoring(self): """Start monitoring CAN bus and streaming telemetry.""" print("[Telemetry] Starting CAN bus monitoring") # Connect to CAN bus self.can_bus = can.interface.Bus(channel='can0', bustype='socketcan') # Define telemetry intervals intervals = { 'battery': 60, # Every minute 'location': 300, # Every 5 minutes 'speed': 10, # Every 10 seconds 'diagnostics': 3600, # Every hour } last_publish = {k: 0 for k in intervals.keys()} while True: # Read CAN messages msg = self.can_bus.recv(timeout=1.0) if msg is None: continue current_time = time.time() # Process specific CAN IDs if msg.arbitration_id == 0x123: # Battery telemetry if current_time - last_publish['battery'] >= intervals['battery']: battery_data = self._parse_battery_can(msg.data) self.publish_telemetry('battery', battery_data, qos=1) last_publish['battery'] = current_time elif msg.arbitration_id == 0x456: # Speed/location if current_time - last_publish['speed'] >= intervals['speed']: speed_data = self._parse_speed_can(msg.data) self.publish_telemetry('speed', speed_data, qos=0) last_publish['speed'] = current_time # Periodic location publish if current_time - last_publish['location'] >= intervals['location']: location_data = self._get_gps_location() self.publish_telemetry('location', location_data, qos=1) last_publish['location'] = current_time def _parse_battery_can(self, data: bytes) -> dict: """Parse battery telemetry from CAN message.""" return { 'soc': int.from_bytes(data[0:2], 'big') / 100, # State of charge % 'voltage': int.from_bytes(data[2:4], 'big') / 10, # Volts 'current': int.from_bytes(data[4:6], 'big', signed=True) / 10, # Amps 'temperature': int.from_bytes(data[6:8], 'big') / 10 - 40, # Celsius } def _parse_speed_can(self, data: bytes) -> dict: """Parse speed telemetry from CAN message.""" return { 'speed': int.from_bytes(data[0:2], 'big') / 100, # km/h 'odometer': int.from_bytes(data[2:6], 'big') / 10, # km } def _get_gps_location(self) -> dict: """Get GPS location from GNSS receiver.""" # Read from gpsd or similar return { 'latitude': 37.7749, 'longitude': -122.4194, 'altitude': 16.0, 'heading': 270.0, 'accuracy': 3.5 } def _get_software_version(self) -> str: """Get vehicle software version.""" with open('/etc/vehicle/version', 'r') as f: return f.read().strip() def _collect_diagnostics(self): """Collect comprehensive diagnostic data.""" diagnostics = { 'dtcs': [], # Diagnostic Trouble Codes 'ecu_status': {}, 'battery_health': {}, 'sensor_status': {}, } # Publish diagnostic report self.publish_telemetry('diagnostics', diagnostics, qos=1) def _remote_lock(self): """Execute remote lock command.""" # Send CAN command to lock doors pass def _remote_honk(self): """Execute remote horn activation.""" # Send CAN command to honk pass def disconnect(self): """Disconnect from MQTT broker.""" if self.mqtt_client: self.mqtt_client.loop_stop() self.mqtt_client.disconnect() if self.can_bus: self.can_bus.shutdown() class OfflineBuffer: """SQLite-based offline message buffer.""" def __init__(self, db_path: str = "/var/lib/vehicle/telemetry-buffer.db"): self.db_path = db_path self._init_db() def _init_db(self): """Initialize SQLite database.""" conn = sqlite3.connect(self.db_path) cursor = conn.cursor() cursor.execute(''' CREATE TABLE IF NOT EXISTS buffer ( id INTEGER PRIMARY KEY AUTOINCREMENT, topic TEXT NOT NULL, payload TEXT NOT NULL, qos INTEGER NOT NULL, timestamp REAL NOT NULL ) ''') conn.commit() conn.close() def store(self, topic: str, payload: str, qos: int): """Store message in buffer.""" conn = sqlite3.connect(self.db_path) cursor = conn.cursor() cursor.execute( 'INSERT INTO buffer (topic, payload, qos, timestamp) VALUES (?, ?, ?, ?)', (topic, payload, qos, time.time()) ) conn.commit() conn.close() def retrieve_all(self) -> List[dict]: """Retrieve all buffered messages.""" conn = sqlite3.connect(self.db_path) cursor = conn.cursor() cursor.execute('SELECT id, topic, payload, qos FROM buffer ORDER BY timestamp') rows = cursor.fetchall() conn.close() return [ {'id': row[0], 'topic': row[1], 'payload': row[2], 'qos': row[3]} for row in rows ] def delete(self, msg_id: int): """Delete message from buffer.""" conn = sqlite3.connect(self.db_path) cursor = conn.cursor() cursor.execute('DELETE FROM buffer WHERE id = ?', (msg_id,)) conn.commit() conn.close() def main(): """Main telemetry client loop.""" client = VehicleTelemetryClient() try: client.connect() client.start_can_monitoring() except KeyboardInterrupt: print("\n[Telemetry] Shutting down") client.disconnect() if __name__ == "__main__": main()
python#!/usr/bin/env python3 """ Cloud backend for vehicle fleet management using AWS IoT Core. """ import json import boto3 from datetime import datetime from typing import Dict, List from aws_iot_device_sdk import mqtt as mqtt5 import redis class FleetManagementBackend: """ Fleet management backend. Services: - Receive telemetry from vehicles - Store in time-series database - Real-time analytics - Remote command dispatch """ def __init__(self): # AWS IoT Core client self.iot_client = boto3.client('iot-data', region_name='us-west-2') # DynamoDB for vehicle state self.dynamodb = boto3.resource('dynamodb', region_name='us-west-2') self.vehicle_table = self.dynamodb.Table('VehicleState') # Timestream for telemetry self.timestream = boto3.client('timestream-write', region_name='us-west-2') self.ts_database = 'VehicleTelemetry' self.ts_table = 'TelemetryData' # Redis for real-time data self.redis = redis.Redis(host='localhost', port=6379, decode_responses=True) def process_telemetry(self, vin: str, message_type: str, data: dict): """ Process incoming telemetry message. Args: vin: Vehicle identification number message_type: Type of telemetry data: Telemetry data """ print(f"[Fleet] Processing {message_type} from {vin}") # Update vehicle state in DynamoDB self._update_vehicle_state(vin, message_type, data) # Store in Timestream for historical analytics self._store_timestream(vin, message_type, data) # Cache in Redis for real-time queries self._cache_redis(vin, message_type, data) # Trigger alerts if necessary self._check_alerts(vin, message_type, data) def _update_vehicle_state(self, vin: str, message_type: str, data: dict): """Update vehicle state in DynamoDB.""" self.vehicle_table.update_item( Key={'vin': vin}, UpdateExpression=f'SET {message_type} = :data, last_update = :timestamp', ExpressionAttributeValues={ ':data': data, ':timestamp': datetime.utcnow().isoformat() } ) def _store_timestream(self, vin: str, message_type: str, data: dict): """Store telemetry in AWS Timestream.""" records = [] for key, value in data.items(): records.append({ 'Time': str(int(datetime.utcnow().timestamp() * 1000)), 'TimeUnit': 'MILLISECONDS', 'Dimensions': [ {'Name': 'vin', 'Value': vin}, {'Name': 'message_type', 'Value': message_type}, ], 'MeasureName': key, 'MeasureValue': str(value), 'MeasureValueType': 'DOUBLE' if isinstance(value, float) else 'BIGINT' }) try: self.timestream.write_records( DatabaseName=self.ts_database, TableName=self.ts_table, Records=records ) except Exception as e: print(f"[Fleet] Timestream write error: {e}") def _cache_redis(self, vin: str, message_type: str, data: dict): """Cache latest telemetry in Redis.""" key = f"vehicle:{vin}:{message_type}" self.redis.setex(key, 3600, json.dumps(data)) # 1 hour TTL def _check_alerts(self, vin: str, message_type: str, data: dict): """Check for alert conditions.""" if message_type == 'battery': # Low battery alert if data.get('soc', 100) < 20: self._send_alert(vin, 'low_battery', f"Battery at {data['soc']}%") # High temperature alert if data.get('temperature', 0) > 50: self._send_alert(vin, 'high_temperature', f"Battery temp: {data['temperature']}°C") elif message_type == 'diagnostics': # DTC alert if data.get('dtcs'): self._send_alert(vin, 'diagnostic_codes', f"DTCs: {', '.join(data['dtcs'])}") def _send_alert(self, vin: str, alert_type: str, message: str): """Send alert to monitoring system.""" print(f"[Fleet] ALERT - {vin}: {alert_type} - {message}") # Send to SNS topic sns = boto3.client('sns', region_name='us-west-2') sns.publish( TopicArn='arn:aws:sns:us-west-2:123456789012:vehicle-alerts', Subject=f"Vehicle Alert: {alert_type}", Message=f"VIN: {vin}\nType: {alert_type}\nMessage: {message}" ) def send_command(self, vin: str, command: str, params: dict = None): """ Send command to vehicle. Args: vin: Vehicle identification number command: Command type (lock, unlock, honk, update) params: Command parameters """ topic = f"vehicles/{vin}/commands/{command}" payload = json.dumps(params or {}) print(f"[Fleet] Sending command to {vin}: {command}") try: self.iot_client.publish( topic=topic, qos=1, payload=payload ) # Log command self._log_command(vin, command, params) except Exception as e: print(f"[Fleet] Command send error: {e}") def _log_command(self, vin: str, command: str, params: dict): """Log command to DynamoDB.""" commands_table = self.dynamodb.Table('VehicleCommands') commands_table.put_item( Item={ 'vin': vin, 'timestamp': datetime.utcnow().isoformat(), 'command': command, 'params': params or {}, 'status': 'sent' } ) def get_vehicle_state(self, vin: str) -> Dict: """Get current vehicle state.""" response = self.vehicle_table.get_item(Key={'vin': vin}) return response.get('Item', {}) def get_fleet_status(self) -> List[Dict]: """Get status of entire fleet.""" response = self.vehicle_table.scan() return response.get('Items', []) def query_telemetry_history(self, vin: str, metric: str, start_time: datetime, end_time: datetime) -> List[Dict]: """Query historical telemetry from Timestream.""" query_client = boto3.client('timestream-query', region_name='us-west-2') query = f""" SELECT time, measure_value::double as value FROM "{self.ts_database}"."{self.ts_table}" WHERE vin = '{vin}' AND measure_name = '{metric}' AND time BETWEEN from_iso8601_timestamp('{start_time.isoformat()}') AND from_iso8601_timestamp('{end_time.isoformat()}') ORDER BY time DESC """ try: response = query_client.query(QueryString=query) results = [] for row in response['Rows']: results.append({ 'time': row['Data'][0]['ScalarValue'], 'value': float(row['Data'][1]['ScalarValue']) }) return results except Exception as e: print(f"[Fleet] Query error: {e}") return []
python#!/usr/bin/env python3 """ Fleet management API gateway. RESTful API for vehicle operations and telemetry queries. """ from fastapi import FastAPI, HTTPException, Depends, Security from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials from pydantic import BaseModel from datetime import datetime from typing import List, Optional app = FastAPI(title="Fleet Management API", version="2.0.0") security = HTTPBearer() # Initialize backend backend = FleetManagementBackend() class CommandRequest(BaseModel): """Remote command request.""" command: str params: Optional[dict] = None class TelemetryQuery(BaseModel): """Telemetry query parameters.""" metric: str start_time: datetime end_time: datetime @app.get("/api/v1/vehicles") async def list_vehicles(credentials: HTTPAuthorizationCredentials = Security(security)): """List all vehicles in fleet.""" fleet = backend.get_fleet_status() return {"vehicles": fleet, "count": len(fleet)} @app.get("/api/v1/vehicles/{vin}") async def get_vehicle(vin: str, credentials: HTTPAuthorizationCredentials = Security(security)): """Get vehicle state.""" state = backend.get_vehicle_state(vin) if not state: raise HTTPException(status_code=404, detail="Vehicle not found") return state @app.post("/api/v1/vehicles/{vin}/commands") async def send_command( vin: str, request: CommandRequest, credentials: HTTPAuthorizationCredentials = Security(security) ): """Send command to vehicle.""" backend.send_command(vin, request.command, request.params) return {"message": "Command sent", "vin": vin, "command": request.command} @app.post("/api/v1/vehicles/{vin}/telemetry/query") async def query_telemetry( vin: str, query: TelemetryQuery, credentials: HTTPAuthorizationCredentials = Security(security) ): """Query historical telemetry data.""" results = backend.query_telemetry_history( vin, query.metric, query.start_time, query.end_time ) return {"vin": vin, "metric": query.metric, "data": results} if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8080)
hcl# File: terraform/main.tf # AWS IoT Core infrastructure for vehicle fleet terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } } } provider "aws" { region = var.aws_region } # IoT Core Thing Type for Vehicles resource "aws_iot_thing_type" "vehicle" { name = "VehicleType" properties { description = "Connected vehicle" searchable_attributes = ["vin", "model", "year"] } } # IoT Policy for Vehicle resource "aws_iot_policy" "vehicle_policy" { name = "VehiclePolicy" policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = [ "iot:Connect" ] Resource = "arn:aws:iot:${var.aws_region}:${data.aws_caller_identity.current.account_id}:client/vehicle-*" }, { Effect = "Allow" Action = [ "iot:Publish" ] Resource = "arn:aws:iot:${var.aws_region}:${data.aws_caller_identity.current.account_id}:topic/vehicles/*/telemetry/*" }, { Effect = "Allow" Action = [ "iot:Subscribe" ] Resource = "arn:aws:iot:${var.aws_region}:${data.aws_caller_identity.current.account_id}:topicfilter/vehicles/*/commands/*" }, { Effect = "Allow" Action = [ "iot:Receive" ] Resource = "arn:aws:iot:${var.aws_region}:${data.aws_caller_identity.current.account_id}:topic/vehicles/*/commands/*" } ] }) } # DynamoDB Table for Vehicle State resource "aws_dynamodb_table" "vehicle_state" { name = "VehicleState" billing_mode = "PAY_PER_REQUEST" hash_key = "vin" attribute { name = "vin" type = "S" } ttl { attribute_name = "ttl" enabled = true } tags = { Name = "vehicle-state" } } # Timestream Database for Telemetry resource "aws_timestreamwrite_database" "telemetry" { database_name = "VehicleTelemetry" } resource "aws_timestreamwrite_table" "telemetry_data" { database_name = aws_timestreamwrite_database.telemetry.database_name table_name = "TelemetryData" retention_properties { memory_store_retention_period_in_hours = 24 magnetic_store_retention_period_in_days = 365 } } # SNS Topic for Alerts resource "aws_sns_topic" "vehicle_alerts" { name = "vehicle-alerts" } # IoT Rule to Route Telemetry resource "aws_iot_topic_rule" "telemetry_rule" { name = "VehicleTelemetryRule" enabled = true sql = "SELECT * FROM 'vehicles/+/telemetry/#'" sql_version = "2016-03-23" timestream { database_name = aws_timestreamwrite_database.telemetry.database_name table_name = aws_timestreamwrite_table.telemetry_data.table_name role_arn = aws_iam_role.iot_timestream_role.arn dimension { name = "vin" value = "${topic(2)}" } dimension { name = "message_type" value = "${topic(4)}" } } dynamodb_v2 { role_arn = aws_iam_role.iot_dynamodb_role.arn put_item { table_name = aws_dynamodb_table.vehicle_state.name } } } # IAM Roles resource "aws_iam_role" "iot_timestream_role" { name = "IoTTimestreamRole" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Principal = { Service = "iot.amazonaws.com" } Action = "sts:AssumeRole" } ] }) } resource "aws_iam_role_policy" "iot_timestream_policy" { name = "IoTTimestreamPolicy" role = aws_iam_role.iot_timestream_role.id policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = [ "timestream:WriteRecords" ] Resource = aws_timestreamwrite_table.telemetry_data.arn }, { Effect = "Allow" Action = [ "timestream:DescribeEndpoints" ] Resource = "*" } ] }) } data "aws_caller_identity" "current" {} variable "aws_region" { default = "us-west-2" }
Expert knowledge of container runtimes for automotive (Docker, Podman, containerd), manifest formats, resource limits, inter-container communication, and orchestration (Kubernetes, K3s).
bash#!/bin/bash # File: setup-vehicle-container-runtime.sh # Setup containerd for automotive use set -e echo "[Setup] Installing containerd for vehicle platform" # Install containerd apt-get update apt-get install -y containerd # Configure containerd for automotive mkdir -p /etc/containerd cat > /etc/containerd/config.toml <<EOF version = 2 # Root directory for containerd root = "/var/lib/containerd" state = "/run/containerd" # OCI runtime [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc] runtime_type = "io.containerd.runc.v2" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] SystemdCgroup = true # Resource limits [plugins."io.containerd.grpc.v1.cri".containerd] default_runtime_name = "runc" # Registry configuration [plugins."io.containerd.grpc.v1.cri".registry] [plugins."io.containerd.grpc.v1.cri".registry.mirrors] [plugins."io.containerd.grpc.v1.cri".registry.mirrors."vehicle-registry.local:5000"] endpoint = ["http://vehicle-registry.local:5000"] # CNI plugins for networking [plugins."io.containerd.grpc.v1.cri".cni] bin_dir = "/opt/cni/bin" conf_dir = "/etc/cni/net.d" EOF # Enable and start containerd systemctl enable containerd systemctl start containerd # Install CNI plugins mkdir -p /opt/cni/bin curl -L https://github.com/containernetworking/plugins/releases/download/v1.3.0/cni-plugins-linux-arm64-v1.3.0.tgz | \ tar -C /opt/cni/bin -xz # Configure CNI networking mkdir -p /etc/cni/net.d cat > /etc/cni/net.d/10-vehicle-bridge.conf <<EOF { "cniVersion": "1.0.0", "name": "vehicle-bridge", "type": "bridge", "bridge": "veh0", "isGateway": true, "ipMasq": true, "ipam": { "type": "host-local", "subnet": "10.88.0.0/16", "routes": [ { "dst": "0.0.0.0/0" } ] } } EOF echo "[Setup] Container runtime ready"
yaml# Vehicle app manifest (OCI-compatible) # File: spotify-app.yaml apiVersion: vehicle.io/v1 kind: VehicleApp metadata: name: spotify namespace: infotainment labels: category: media vendor: spotify safety-critical: "false" spec: # Container specification container: image: vehicle-registry.local:5000/spotify/automotive:2.1.4 imagePullPolicy: IfNotPresent # Resource limits resources: requests: memory: "256Mi" cpu: "250m" storage: "1Gi" limits: memory: "512Mi" cpu: "500m" storage: "2Gi" network: bandwidth: "10Mbps" # Security context securityContext: runAsUser: 1000 runAsGroup: 1000 readOnlyRootFilesystem: true allowPrivilegeEscalation: false capabilities: drop: - ALL add: - NET_BIND_SERVICE # For network access # Environment variables env: - name: API_KEY valueFrom: secretRef: name: spotify-credentials key: api-key - name: VEHICLE_VIN valueFrom: fieldRef: fieldPath: metadata.vin # Volume mounts volumeMounts: - name: cache mountPath: /app/cache - name: config mountPath: /app/config readOnly: true - name: dbus-socket mountPath: /var/run/dbus # Volumes volumes: - name: cache emptyDir: sizeLimit: 500Mi - name: config configMap: name: spotify-config - name: dbus-socket hostPath: path: /var/run/dbus type: Socket # Networking networking: ports: - name: http containerPort: 8080 protocol: TCP hostNetwork: false dnsPolicy: ClusterFirst # Lifecycle hooks lifecycle: postStart: exec: command: ["/app/scripts/post-start.sh"] preStop: exec: command: ["/app/scripts/graceful-shutdown.sh"] # Health checks livenessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 10 periodSeconds: 30 readinessProbe: httpGet: path: /ready port: 8080 initialDelaySeconds: 5 periodSeconds: 10 # Service integration services: - name: media-player type: dbus interface: org.mpris.MediaPlayer2 - name: steering-controls type: vehicle-bus permissions: [read] # Safety constraints safety: disableWhileDriving: false touchLimit: true requireVoiceControl: false criticalityLevel: low # Update strategy updateStrategy: type: RollingUpdate rollingUpdate: maxUnavailable: 1
bash#!/bin/bash # File: install-k3s-automotive.sh # Install K3s (lightweight Kubernetes) for vehicle platform set -e echo "[K3s] Installing K3s for vehicle platform" # Install K3s with automotive-specific configuration curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="server \ --disable traefik \ --disable servicelb \ --disable local-storage \ --write-kubeconfig-mode 644 \ --kubelet-arg=max-pods=50 \ --kubelet-arg=eviction-hard=memory.available<100Mi \ --kubelet-arg=eviction-soft=memory.available<200Mi \ --kubelet-arg=eviction-soft-grace-period=memory.available=1m \ --kube-controller-manager-arg=node-monitor-period=10s \ --kube-controller-manager-arg=node-monitor-grace-period=30s" sh - # Wait for K3s to start sleep 10 # Install vehicle-specific CRDs cat <<EOF | kubectl apply -f - apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: vehicleapps.vehicle.io spec: group: vehicle.io versions: - name: v1 served: true storage: true schema: openAPIV3Schema: type: object properties: spec: type: object properties: container: type: object safety: type: object services: type: array scope: Namespaced names: plural: vehicleapps singular: vehicleapp kind: VehicleApp shortNames: - vapp EOF # Create namespaces kubectl create namespace infotainment kubectl create namespace adas kubectl create namespace diagnostics # Install resource quotas cat <<EOF | kubectl apply -f - apiVersion: v1 kind: ResourceQuota metadata: name: infotainment-quota namespace: infotainment spec: hard: requests.cpu: "2" requests.memory: "4Gi" persistentvolumeclaims: "10" EOF echo "[K3s] Installation complete"
python#!/usr/bin/env python3 """ Vehicle app deployment tool using containerd API. """ import subprocess import json import yaml from pathlib import Path from typing import Dict class VehicleAppDeployer: """Deploy containerized apps to vehicle platform.""" def __init__(self, runtime: str = "containerd"): self.runtime = runtime self.namespace = "vehicle-apps" def deploy_app(self, manifest_path: str): """ Deploy app from manifest. Args: manifest_path: Path to app manifest YAML """ with open(manifest_path, 'r') as f: manifest = yaml.safe_load(f) app_name = manifest['metadata']['name'] print(f"[Deploy] Deploying {app_name}") # Pull container image self._pull_image(manifest['spec']['container']['image']) # Create namespace self._create_namespace(manifest['metadata']['namespace']) # Create container container_id = self._create_container(manifest) # Start container self._start_container(container_id) print(f"[Deploy] {app_name} deployed successfully (ID: {container_id})") def _pull_image(self, image: str): """Pull container image.""" print(f"[Deploy] Pulling image: {image}") cmd = [ "ctr", "-n", self.namespace, "image", "pull", image ] subprocess.run(cmd, check=True) def _create_namespace(self, namespace: str): """Create containerd namespace.""" # Containerd namespaces are created automatically on first use pass def _create_container(self, manifest: Dict) -> str: """Create container from manifest.""" spec = manifest['spec'] metadata = manifest['metadata'] app_name = metadata['name'] image = spec['container']['image'] # Build OCI runtime spec runtime_spec = self._build_runtime_spec(spec) # Write spec to file spec_path = f"/tmp/{app_name}-spec.json" with open(spec_path, 'w') as f: json.dump(runtime_spec, f, indent=2) # Create container cmd = [ "ctr", "-n", self.namespace, "container", "create", "--runtime", "io.containerd.runc.v2", "--config", spec_path, image, app_name ] result = subprocess.run(cmd, capture_output=True, text=True) if result.returncode != 0: raise RuntimeError(f"Container creation failed: {result.stderr}") return app_name def _build_runtime_spec(self, spec: Dict) -> Dict: """Build OCI runtime specification.""" container = spec['container'] resources = container.get('resources', {}) # Parse resource limits memory_limit = self._parse_memory( resources.get('limits', {}).get('memory', '512Mi') ) cpu_quota = self._parse_cpu( resources.get('limits', {}).get('cpu', '500m') ) # Build OCI spec oci_spec = { "ociVersion": "1.0.2", "process": { "terminal": False, "user": { "uid": container.get('securityContext', {}).get('runAsUser', 1000), "gid": container.get('securityContext', {}).get('runAsGroup', 1000) }, "env": self._build_env(container.get('env', [])), "cwd": "/app", "capabilities": { "bounding": ["CAP_NET_BIND_SERVICE"], "effective": ["CAP_NET_BIND_SERVICE"], "inheritable": ["CAP_NET_BIND_SERVICE"], "permitted": ["CAP_NET_BIND_SERVICE"] }, "rlimits": [ { "type": "RLIMIT_NOFILE", "hard": 1024, "soft": 1024 } ], "noNewPrivileges": True }, "root": { "path": "rootfs", "readonly": container.get('securityContext', {}).get( 'readOnlyRootFilesystem', True ) }, "mounts": self._build_mounts(spec.get('volumes', [])), "linux": { "namespaces": [ {"type": "pid"}, {"type": "network"}, {"type": "ipc"}, {"type": "uts"}, {"type": "mount"} ], "resources": { "memory": { "limit": memory_limit }, "cpu": { "quota": cpu_quota, "period": 100000 } }, "cgroupsPath": f"/vehicle-apps/{spec['container']['image'].split('/')[-1]}" } } return oci_spec def _build_env(self, env_vars: list) -> list: """Build environment variable list.""" env = ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"] for var in env_vars: name = var['name'] value = var.get('value') if value: env.append(f"{name}={value}") elif 'valueFrom' in var: # Resolve from secret/configmap # Simplified implementation env.append(f"{name}=<resolved-value>") return env def _build_mounts(self, volumes: list) -> list: """Build mount list.""" mounts = [ { "destination": "/proc", "type": "proc", "source": "proc" }, { "destination": "/dev", "type": "tmpfs", "source": "tmpfs", "options": ["nosuid", "strictatime", "mode=755", "size=65536k"] }, { "destination": "/dev/pts", "type": "devpts", "source": "devpts", "options": ["nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620"] }, { "destination": "/sys", "type": "sysfs", "source": "sysfs", "options": ["nosuid", "noexec", "nodev", "ro"] } ] # Add application-specific mounts for volume in volumes: if volume.get('emptyDir'): mounts.append({ "destination": "/app/cache", "type": "tmpfs", "source": "tmpfs", "options": ["nosuid", "nodev"] }) elif volume.get('hostPath'): mounts.append({ "destination": "/var/run/dbus", "type": "bind", "source": volume['hostPath']['path'], "options": ["rbind", "ro"] }) return mounts def _start_container(self, container_id: str): """Start container.""" print(f"[Deploy] Starting container: {container_id}") cmd = [ "ctr", "-n", self.namespace, "task", "start", "-d", container_id ] subprocess.run(cmd, check=True) def _parse_memory(self, mem_str: str) -> int: """Parse memory string to bytes.""" units = {'Ki': 1024, 'Mi': 1024**2, 'Gi': 1024**3} for unit, multiplier in units.items(): if mem_str.endswith(unit): return int(mem_str[:-2]) * multiplier return int(mem_str) def _parse_cpu(self, cpu_str: str) -> int: """Parse CPU string to quota.""" if cpu_str.endswith('m'): millicores = int(cpu_str[:-1]) return millicores * 100 # quota in microseconds else: cores = float(cpu_str) return int(cores * 100000) def list_apps(self): """List deployed apps.""" cmd = [ "ctr", "-n", self.namespace, "container", "ls" ] result = subprocess.run(cmd, capture_output=True, text=True) print(result.stdout) def stop_app(self, app_name: str): """Stop running app.""" print(f"[Deploy] Stopping app: {app_name}") # Stop task cmd = [ "ctr", "-n", self.namespace, "task", "kill", app_name, "SIGTERM" ] subprocess.run(cmd, check=False) def remove_app(self, app_name: str): """Remove app.""" print(f"[Deploy] Removing app: {app_name}") # Delete container cmd = [ "ctr", "-n", self.namespace, "container", "rm", app_name ] subprocess.run(cmd, check=True) def main(): """Main deployment script.""" deployer = VehicleAppDeployer() # Deploy app deployer.deploy_app("spotify-app.yaml") # List apps deployer.list_apps() if __name__ == "__main__": main()
python#!/usr/bin/env python3 """ D-Bus service for inter-container communication. Allows sandboxed apps to communicate with vehicle services. """ import dbus import dbus.service from dbus.mainloop.glib import DBusGMainLoop from gi.repository import GLib class VehicleServiceBus(dbus.service.Object): """ D-Bus service for vehicle platform. Provides APIs for apps to: - Get vehicle state - Control audio - Receive user input (steering controls) - Display notifications """ def __init__(self): bus_name = dbus.service.BusName('com.vehicle.Platform', bus=dbus.SystemBus()) dbus.service.Object.__init__(self, bus_name, '/com/vehicle/Platform') @dbus.service.method('com.vehicle.Platform', in_signature='', out_signature='a{sv}') def GetVehicleState(self): """Get current vehicle state.""" return { 'speed': dbus.Double(65.0), # km/h 'batterySoc': dbus.Double(75.0), # % 'range': dbus.Double(300.0), # km 'gear': dbus.String('D'), 'isMoving': dbus.Boolean(True) } @dbus.service.method('com.vehicle.Platform', in_signature='s', out_signature='b') def PlayAudio(self, url): """Play audio from URL.""" print(f"[D-Bus] Playing audio: {url}") # Integrate with vehicle audio system return True @dbus.service.signal('com.vehicle.Platform', signature='s') def SteeringControlEvent(self, action): """Signal for steering control events.""" print(f"[D-Bus] Steering control: {action}") @dbus.service.method('com.vehicle.Platform', in_signature='ss', out_signature='b') def ShowNotification(self, title, message): """Display notification in vehicle cluster.""" print(f"[D-Bus] Notification: {title} - {message}") # Send to instrument cluster return True def main(): """Start D-Bus service.""" DBusGMainLoop(set_as_default=True) service = VehicleServiceBus() print("[D-Bus] Vehicle service bus started") # Run main loop loop = GLib.MainLoop() loop.run() if __name__ == "__main__": main()
Expert knowledge of digital twin architecture, real-time synchronization, simulation for testing, predictive maintenance, virtual testing environments, and CI/CD for vehicle software.
python#!/usr/bin/env python3 """ Digital Twin Engine for vehicle simulation and synchronization. """ import json import time from dataclasses import dataclass, asdict from datetime import datetime from typing import Dict, List, Optional import numpy as np from scipy.integrate import odeint import paho.mqtt.client as mqtt @dataclass class VehicleState: """Complete vehicle state.""" vin: str timestamp: float # Powertrain battery_soc: float # State of charge (%) battery_voltage: float # Volts battery_current: float # Amps battery_temperature: float # Celsius motor_speed: float # RPM motor_torque: float # Nm # Dynamics speed: float # km/h acceleration: float # m/s^2 position: tuple # (latitude, longitude) heading: float # degrees # Environment ambient_temperature: float # Celsius road_grade: float # % # Diagnostics odometer: float # km energy_consumed: float # kWh regeneration_energy: float # kWh class VehiclePhysicsModel: """ Physics-based vehicle simulation model. Models: - Battery dynamics (charge/discharge, thermal) - Electric motor efficiency - Vehicle dynamics (acceleration, drag, rolling resistance) - Energy consumption """ def __init__(self, vehicle_params: dict): self.params = vehicle_params # Vehicle parameters self.mass = vehicle_params['mass'] # kg self.drag_coefficient = vehicle_params['drag_coefficient'] # Cd self.frontal_area = vehicle_params['frontal_area'] # m^2 self.rolling_resistance = vehicle_params['rolling_resistance'] # Crr self.wheel_radius = vehicle_params['wheel_radius'] # m # Battery parameters self.battery_capacity = vehicle_params['battery_capacity'] # kWh self.battery_resistance = vehicle_params['battery_resistance'] # Ohms self.battery_thermal_mass = vehicle_params['battery_thermal_mass'] # J/K # Motor parameters self.motor_max_power = vehicle_params['motor_max_power'] # kW self.motor_efficiency_map = vehicle_params['motor_efficiency_map'] # Constants self.air_density = 1.225 # kg/m^3 self.gravity = 9.81 # m/s^2 def simulate_step(self, state: VehicleState, throttle: float, dt: float) -> VehicleState: """ Simulate one time step. Args: state: Current vehicle state throttle: Throttle input (-1 to 1, negative for regen) dt: Time step (seconds) Returns: Updated vehicle state """ # Convert speed to m/s speed_mps = state.speed / 3.6 # Calculate forces drag_force = 0.5 * self.air_density * self.drag_coefficient * \ self.frontal_area * speed_mps**2 rolling_resistance_force = self.rolling_resistance * self.mass * \ self.gravity * np.cos(np.radians(state.road_grade)) grade_force = self.mass * self.gravity * np.sin(np.radians(state.road_grade)) # Calculate required power if throttle > 0: # Acceleration motor_torque = throttle * self._calculate_max_torque(state.motor_speed) motor_power = motor_torque * state.motor_speed * 2 * np.pi / 60 / 1000 # kW # Motor efficiency efficiency = self._get_motor_efficiency(motor_power, state.motor_speed) battery_power = motor_power / efficiency else: # Regenerative braking regen_power = abs(throttle) * self.motor_max_power * 0.7 # 70% max regen battery_power = -regen_power * 0.85 # 85% regen efficiency motor_power = -regen_power # Battery dynamics battery_current = battery_power * 1000 / state.battery_voltage voltage_drop = battery_current * self.battery_resistance state.battery_voltage = self._calculate_ocv(state.battery_soc) - voltage_drop # Energy consumed/regenerated energy_delta = battery_power * dt / 3600 # kWh state.battery_soc -= (energy_delta / self.battery_capacity) * 100 if battery_power > 0: state.energy_consumed += energy_delta else: state.regeneration_energy += abs(energy_delta) # Battery thermal model heat_generation = battery_current**2 * self.battery_resistance # Watts cooling_rate = (state.battery_temperature - state.ambient_temperature) * 50 # W temp_delta = (heat_generation - cooling_rate) * dt / self.battery_thermal_mass state.battery_temperature += temp_delta # Vehicle dynamics net_force = (motor_power * 1000 / max(speed_mps, 0.1)) - \ drag_force - rolling_resistance_force - grade_force acceleration = net_force / self.mass speed_mps += acceleration * dt speed_mps = max(0, speed_mps) # Can't go backwards state.speed = speed_mps * 3.6 # Convert to km/h state.acceleration = acceleration # Update position (simplified) distance_delta = speed_mps * dt / 1000 # km state.odometer += distance_delta # Update motor state state.motor_speed = speed_mps / (2 * np.pi * self.wheel_radius) * 60 # RPM state.motor_torque = motor_power * 1000 / (state.motor_speed * 2 * np.pi / 60) \ if state.motor_speed > 0 else 0 state.battery_current = battery_current state.timestamp = time.time() return state def _calculate_max_torque(self, motor_speed: float) -> float: """Calculate maximum available torque at given speed.""" if motor_speed < 3000: return 400 # Nm (constant torque region) else: # Constant power region max_power = self.motor_max_power * 1000 # W return max_power / (motor_speed * 2 * np.pi / 60) def _get_motor_efficiency(self, power: float, speed: float) -> float: """Get motor efficiency from efficiency map.""" # Simplified efficiency model # In production, use lookup table if power < 0.1 * self.motor_max_power: return 0.85 elif power < 0.5 * self.motor_max_power: return 0.93 elif power < 0.8 * self.motor_max_power: return 0.95 else: return 0.92 def _calculate_ocv(self, soc: float) -> float: """Calculate open-circuit voltage from SOC.""" # Simplified polynomial fit # In production, use actual OCV curve return 300 + 100 * (soc / 100) class DigitalTwin: """ Digital twin of vehicle with bi-directional synchronization. Features: - Real-time sync with physical vehicle - Physics-based simulation - Predictive analytics - Virtual testing """ def __init__(self, vin: str, vehicle_params: dict, cloud_config: dict): self.vin = vin self.physics_model = VehiclePhysicsModel(vehicle_params) self.cloud_config = cloud_config # Initialize state self.state = VehicleState( vin=vin, timestamp=time.time(), battery_soc=100.0, battery_voltage=400.0, battery_current=0.0, battery_temperature=25.0, motor_speed=0.0, motor_torque=0.0, speed=0.0, acceleration=0.0, position=(0.0, 0.0), heading=0.0, ambient_temperature=25.0, road_grade=0.0, odometer=0.0, energy_consumed=0.0, regeneration_energy=0.0 ) # MQTT connection self.mqtt_client = None self.sync_enabled = True self.simulation_mode = False # Analytics self.state_history = [] self.predictions = {} def connect_to_cloud(self): """Connect to cloud platform for synchronization.""" self.mqtt_client = mqtt.Client(client_id=f"twin-{self.vin}") self.mqtt_client.username_pw_set( self.cloud_config['mqtt_username'], self.cloud_config['mqtt_password'] ) self.mqtt_client.on_connect = self._on_connect self.mqtt_client.on_message = self._on_message self.mqtt_client.connect( self.cloud_config['mqtt_broker'], self.cloud_config['mqtt_port'] ) self.mqtt_client.loop_start() def _on_connect(self, client, userdata, flags, rc): """Handle MQTT connection.""" print(f"[Twin] Connected to cloud (VIN: {self.vin})") # Subscribe to physical vehicle telemetry client.subscribe(f"vehicles/{self.vin}/telemetry/#") # Subscribe to commands client.subscribe(f"twins/{self.vin}/commands/#") def _on_message(self, client, userdata, msg): """Handle incoming messages.""" topic_parts = msg.topic.split('/') if 'telemetry' in topic_parts: # Update from physical vehicle self._sync_from_physical(json.loads(msg.payload)) elif 'commands' in topic_parts: # Command from cloud command = topic_parts[-1] self._handle_command(command, json.loads(msg.payload)) def _sync_from_physical(self, telemetry: dict): """Synchronize state from physical vehicle.""" if not self.sync_enabled: return print(f"[Twin] Syncing from physical vehicle") # Update state from telemetry message_type = telemetry.get('message_type') data = telemetry.get('data', {}) if message_type == 'battery': self.state.battery_soc = data.get('soc', self.state.battery_soc) self.state.battery_voltage = data.get('voltage', self.state.battery_voltage) self.state.battery_current = data.get('current', self.state.battery_current) self.state.battery_temperature = data.get('temperature', self.state.battery_temperature) elif message_type == 'speed': self.state.speed = data.get('speed', self.state.speed) self.state.odometer = data.get('odometer', self.state.odometer) elif message_type == 'location': self.state.position = (data.get('latitude', 0), data.get('longitude', 0)) self.state.heading = data.get('heading', self.state.heading) # Publish updated twin state self._publish_twin_state() def _publish_twin_state(self): """Publish digital twin state to cloud.""" if self.mqtt_client and self.mqtt_client.is_connected(): topic = f"twins/{self.vin}/state" payload = json.dumps(asdict(self.state)) self.mqtt_client.publish(topic, payload, qos=0) def run_simulation(self, drive_cycle: List[dict], dt: float = 0.1): """ Run simulation with given drive cycle. Args: drive_cycle: List of {'time': t, 'throttle': x, 'grade': y} dt: Time step (seconds) """ print(f"[Twin] Starting simulation (duration: {drive_cycle[-1]['time']}s)") self.simulation_mode = True self.state_history = [] for i, cycle_point in enumerate(drive_cycle[:-1]): next_point = drive_cycle[i + 1] # Interpolate throttle throttle = cycle_point['throttle'] self.state.road_grade = cycle_point.get('grade', 0) # Simulate self.state = self.physics_model.simulate_step(self.state, throttle, dt) # Store history self.state_history.append(asdict(self.state)) # Publish periodically if i % 10 == 0: self._publish_twin_state() time.sleep(dt) self.simulation_mode = False print(f"[Twin] Simulation complete") # Analyze results self._analyze_simulation() def _analyze_simulation(self): """Analyze simulation results.""" if not self.state_history: return final_state = self.state_history[-1] print(f"\n[Twin] Simulation Results:") print(f" Distance: {final_state['odometer']:.2f} km") print(f" Energy consumed: {final_state['energy_consumed']:.2f} kWh") print(f" Regeneration: {final_state['regeneration_energy']:.2f} kWh") print(f" Final SOC: {final_state['battery_soc']:.1f}%") print(f" Efficiency: {final_state['odometer'] / final_state['energy_consumed']:.2f} km/kWh") def predict_range(self) -> float: """ Predict remaining range based on current state and driving pattern. Returns: Predicted range in km """ # Simple model: current SOC * typical efficiency # In production, use ML model trained on historical data typical_efficiency = 5.0 # km/kWh remaining_energy = self.state.battery_soc / 100 * self.physics_model.battery_capacity predicted_range = remaining_energy * typical_efficiency print(f"[Twin] Predicted range: {predicted_range:.1f} km") return predicted_range def predict_maintenance(self) -> Dict: """ Predict maintenance needs using anomaly detection. Returns: Dictionary of predicted maintenance items """ predictions = { 'battery_health': 95.0, # % 'estimated_degradation': 5.0, # % 'cycles_remaining': 1500, 'recommendations': [] } # Check battery temperature trends if len(self.state_history) > 100: recent_temps = [s['battery_temperature'] for s in self.state_history[-100:]] avg_temp = np.mean(recent_temps) if avg_temp > 45: predictions['recommendations'].append( "Battery running hot. Check cooling system." ) # Check energy consumption anomalies if self.state.energy_consumed > 0: efficiency = self.state.odometer / self.state.energy_consumed if efficiency < 4.0: # Below expected predictions['recommendations'].append( "Low efficiency detected. Check tire pressure and alignment." ) return predictions def _handle_command(self, command: str, params: dict): """Handle commands from cloud.""" print(f"[Twin] Received command: {command}") if command == 'simulate': # Run simulation with provided drive cycle drive_cycle = params.get('drive_cycle', []) self.run_simulation(drive_cycle) elif command == 'predict_range': # Predict range range_km = self.predict_range() self.mqtt_client.publish( f"twins/{self.vin}/predictions/range", json.dumps({'range_km': range_km}) ) elif command == 'predict_maintenance': # Predict maintenance predictions = self.predict_maintenance() self.mqtt_client.publish( f"twins/{self.vin}/predictions/maintenance", json.dumps(predictions) ) def export_state_history(self, filename: str): """Export state history to file for analysis.""" with open(filename, 'w') as f: json.dump(self.state_history, f, indent=2) print(f"[Twin] State history exported to {filename}") # Example usage def main(): """Example digital twin usage.""" # Vehicle parameters (Tesla Model 3 Long Range approximate) vehicle_params = { 'mass': 1847, # kg 'drag_coefficient': 0.23, 'frontal_area': 2.22, # m^2 'rolling_resistance': 0.01, 'wheel_radius': 0.368, # m (18" wheels) 'battery_capacity': 82, # kWh 'battery_resistance': 0.05, # Ohms 'battery_thermal_mass': 50000, # J/K 'motor_max_power': 258, # kW (combined front+rear) 'motor_efficiency_map': {} } cloud_config = { 'mqtt_broker': 'mqtt.example.com', 'mqtt_port': 8883, 'mqtt_username': 'twin-client', 'mqtt_password': 'password' } # Create digital twin twin = DigitalTwin('VIN123456789', vehicle_params, cloud_config) twin.connect_to_cloud() # Example drive cycle (WLTP-like) drive_cycle = [] for t in range(0, 1800, 10): # 30 minutes if t < 600: throttle = 0.3 # City driving elif t < 1200: throttle = 0.5 # Highway else: throttle = 0.2 # Slow down drive_cycle.append({ 'time': t, 'throttle': throttle, 'grade': 0 }) # Run simulation twin.run_simulation(drive_cycle, dt=10.0) # Predictions twin.predict_range() twin.predict_maintenance() # Export data twin.export_state_history('twin_history.json') if __name__ == "__main__": main()
yaml# Azure Digital Twins model definition (DTDL) # File: vehicle-model.json { "@context": "dtmi:dtdl:context;2", "@id": "dtmi:com:example:Vehicle;1", "@type": "Interface", "displayName": "Electric Vehicle", "contents": [ { "@type": "Property", "name": "vin", "schema": "string", "description": "Vehicle Identification Number" }, { "@type": "Telemetry", "name": "battery", "schema": { "@type": "Object", "fields": [ { "name": "soc", "schema": "double" }, { "name": "voltage", "schema": "double" }, { "name": "current", "schema": "double" }, { "name": "temperature", "schema": "double" } ] } }, { "@type": "Telemetry", "name": "location", "schema": "geopoint" }, { "@type": "Telemetry", "name": "speed", "schema": "double" }, { "@type": "Command", "name": "predictRange", "response": { "name": "rangeResult", "schema": "double" } }, { "@type": "Command", "name": "simulateDriveCycle", "request": { "name": "driveCycleData", "schema": "string" } } ] }
yaml# File: .github/workflows/digital-twin-ci.yml name: Digital Twin CI/CD on: push: branches: [main, develop] pull_request: branches: [main] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 with: python-version: '3.9' - name: Install dependencies run: | pip install -r requirements.txt pip install pytest pytest-cov - name: Run unit tests run: | pytest tests/ --cov=digital_twin --cov-report=xml - name: Run physics model validation run: | python tests/validate_physics.py - name: Upload coverage uses: codecov/codecov-action@v3 with: file: ./coverage.xml simulate: runs-on: ubuntu-latest needs: test steps: - uses: actions/checkout@v3 - name: Run simulation tests run: | python digital_twin.py --mode simulation \ --drive-cycle tests/wltp_cycle.json \ --output results/ - name: Validate results run: | python tests/validate_simulation.py results/ - name: Upload artifacts uses: actions/upload-artifact@v3 with: name: simulation-results path: results/ deploy: runs-on: ubuntu-latest needs: [test, simulate] if: github.ref == 'refs/heads/main' steps: - uses: actions/checkout@v3 - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v2 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: us-west-2 - name: Deploy to AWS IoT run: | aws iot create-thing --thing-name digital-twin-${{ github.sha }} aws lambda update-function-code \ --function-name vehicle-digital-twin \ --zip-file fileb://digital_twin.zip
Expert knowledge of OTA (Over-The-Air) update architecture, A/B partitioning, differential updates, rollback mechanisms, secure boot chain, and update orchestration across ECUs.
python#!/usr/bin/env python3 """ Uptane-compliant OTA update client for automotive ECUs. Implements Director and Image repository verification. """ import hashlib import json import os import subprocess import time from dataclasses import dataclass from pathlib import Path from typing import Dict, List, Optional import requests from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import padding, rsa from cryptography.hazmat.backends import default_backend @dataclass class UpdateMetadata: """Update metadata from Director repository.""" version: str targets: Dict[str, dict] signatures: List[dict] expires: str @classmethod def from_json(cls, data: dict): return cls( version=data["signed"]["version"], targets=data["signed"]["targets"], signatures=data["signatures"], expires=data["signed"]["expires"] ) class UptaneClient: """ Uptane OTA update client. Supports: - Dual Director/Image repository verification - Signature validation with threshold - Differential update download - A/B partition switching - Automatic rollback on boot failure """ def __init__(self, config_path: str = "/etc/ota/uptane-config.json"): self.config = self._load_config(config_path) self.ecu_serial = self._get_ecu_serial() self.current_version = self._get_current_version() self.director_url = self.config["director_url"] self.image_repo_url = self.config["image_repo_url"] self.root_keys = self._load_root_keys() def _load_config(self, path: str) -> dict: """Load Uptane configuration.""" with open(path, 'r') as f: return json.load(f) def _get_ecu_serial(self) -> str: """Get unique ECU serial number.""" # Read from secure storage (TPM, EEPROM, etc.) serial_path = Path("/sys/firmware/devicetree/base/serial-number") if serial_path.exists(): return serial_path.read_text().strip() return os.uname().nodename # Fallback def _get_current_version(self) -> str: """Get currently running software version.""" version_path = Path("/etc/ota/current-version") if version_path.exists(): return version_path.read_text().strip() return "unknown" def _load_root_keys(self) -> List[rsa.RSAPublicKey]: """Load root public keys for signature verification.""" keys = [] for key_file in self.config["root_keys"]: with open(key_file, 'rb') as f: key = serialization.load_pem_public_key( f.read(), backend=default_backend() ) keys.append(key) return keys def verify_signature(self, data: bytes, signature: bytes, public_key: rsa.RSAPublicKey) -> bool: """Verify RSA signature on data.""" try: public_key.verify( signature, data, padding.PSS( mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH ), hashes.SHA256() ) return True except Exception as e: print(f"Signature verification failed: {e}") return False def check_for_updates(self) -> Optional[UpdateMetadata]: """ Check Director repository for available updates. Returns: UpdateMetadata if update available, None otherwise """ print(f"[OTA] Checking for updates (ECU: {self.ecu_serial}, " f"Version: {self.current_version})") # Fetch Director metadata url = f"{self.director_url}/metadata/{self.ecu_serial}/targets.json" try: response = requests.get(url, timeout=30) response.raise_for_status() metadata = UpdateMetadata.from_json(response.json()) # Verify signatures (threshold: 2/3) verified_sigs = 0 for sig_data in metadata.signatures: sig_bytes = bytes.fromhex(sig_data["sig"]) # Verify against root keys for key in self.root_keys: if self.verify_signature( json.dumps(response.json()["signed"]).encode(), sig_bytes, key ): verified_sigs += 1 break if verified_sigs < self.config.get("signature_threshold", 2): raise ValueError(f"Insufficient signatures: {verified_sigs}") # Check if update is newer for target_name, target_info in metadata.targets.items(): if target_info["custom"].get("ecu_identifier") == self.ecu_serial: target_version = target_info["custom"]["version"] if target_version != self.current_version: print(f"[OTA] Update available: {target_version}") return metadata print("[OTA] No updates available") return None except Exception as e: print(f"[OTA] Error checking for updates: {e}") return None def download_update(self, metadata: UpdateMetadata) -> Optional[Path]: """ Download update image from Image repository. Uses differential updates if available. Returns: Path to downloaded update file """ for target_name, target_info in metadata.targets.items(): if target_info["custom"].get("ecu_identifier") != self.ecu_serial: continue # Check for differential update if "delta_from" in target_info["custom"]: if target_info["custom"]["delta_from"] == self.current_version: print("[OTA] Downloading differential update...") url = f"{self.image_repo_url}/targets/{target_name}.delta" else: print("[OTA] Delta not applicable, downloading full image") url = f"{self.image_repo_url}/targets/{target_name}" else: print("[OTA] Downloading full update image...") url = f"{self.image_repo_url}/targets/{target_name}" # Download with progress download_path = Path(f"/tmp/ota-update-{target_name}") try: with requests.get(url, stream=True, timeout=300) as r: r.raise_for_status() total_size = int(r.headers.get('content-length', 0)) downloaded = 0 with open(download_path, 'wb') as f: for chunk in r.iter_content(chunk_size=8192): f.write(chunk) downloaded += len(chunk) progress = (downloaded / total_size * 100) if total_size else 0 print(f"\r[OTA] Download progress: {progress:.1f}%", end='') print() # New line after progress # Verify hash expected_hash = target_info["hashes"]["sha256"] actual_hash = self._compute_sha256(download_path) if actual_hash != expected_hash: raise ValueError(f"Hash mismatch: {actual_hash} != {expected_hash}") print(f"[OTA] Download complete: {download_path}") return download_path except Exception as e: print(f"[OTA] Download failed: {e}") if download_path.exists(): download_path.unlink() return None return None def _compute_sha256(self, file_path: Path) -> str: """Compute SHA256 hash of file.""" sha256 = hashlib.sha256() with open(file_path, 'rb') as f: for chunk in iter(lambda: f.read(8192), b''): sha256.update(chunk) return sha256.hexdigest() def install_update(self, update_path: Path) -> bool: """ Install update to inactive partition. Uses A/B partition switching for atomic updates. Returns: True if installation successful """ print("[OTA] Installing update...") try: # Determine inactive partition current_slot = self._get_current_boot_slot() inactive_slot = 'b' if current_slot == 'a' else 'a' inactive_partition = f"/dev/mmcblk0p{2 if inactive_slot == 'b' else 1}" print(f"[OTA] Current slot: {current_slot}, " f"Installing to: {inactive_slot}") # Write update to inactive partition cmd = f"dd if={update_path} of={inactive_partition} bs=4M status=progress" subprocess.run(cmd, shell=True, check=True) # Set boot slot to inactive (will become active on next boot) self._set_boot_slot(inactive_slot) # Mark for boot attempt counter (rollback if boot fails 3 times) self._set_boot_counter(3) print(f"[OTA] Update installed to slot {inactive_slot}") return True except Exception as e: print(f"[OTA] Installation failed: {e}") return False def _get_current_boot_slot(self) -> str: """Get currently booted partition slot.""" # Read from U-Boot environment or kernel command line with open("/proc/cmdline", 'r') as f: cmdline = f.read() if "root=/dev/mmcblk0p1" in cmdline: return 'a' elif "root=/dev/mmcblk0p2" in cmdline: return 'b' return 'a' # Default def _set_boot_slot(self, slot: str): """Set boot slot for next reboot.""" # Write to U-Boot environment cmd = f"fw_setenv boot_slot {slot}" subprocess.run(cmd, shell=True, check=True) def _set_boot_counter(self, count: int): """Set boot attempt counter for rollback protection.""" cmd = f"fw_setenv boot_counter {count}" subprocess.run(cmd, shell=True, check=True) def apply_update(self) -> bool: """ Apply update by rebooting into new partition. Returns: True if reboot initiated """ print("[OTA] Applying update (rebooting)...") try: subprocess.run(["reboot"], check=True) return True except Exception as e: print(f"[OTA] Reboot failed: {e}") return False def verify_boot_success(self): """ Verify successful boot after update. Called by systemd service on boot. Confirms update success or triggers rollback. """ boot_counter_path = Path("/proc/device-tree/chosen/u-boot,boot-counter") if boot_counter_path.exists(): counter = int(boot_counter_path.read_text().strip()) if counter > 0: # Boot successful, commit update print("[OTA] Boot successful, committing update") self._set_boot_counter(0) # Clear counter # Update current version new_version = self._detect_version() Path("/etc/ota/current-version").write_text(new_version) else: print("[OTA] Already committed") else: print("[OTA] Boot counter not found (no pending update)") def _detect_version(self) -> str: """Detect software version from running system.""" version_files = [ "/etc/ota/version", "/etc/os-release" ] for path in version_files: if Path(path).exists(): content = Path(path).read_text() # Extract version from os-release for line in content.split('\n'): if line.startswith("VERSION="): return line.split('=')[1].strip('"') return "unknown" def main(): """OTA update client main loop.""" client = UptaneClient() # Check on boot client.verify_boot_success() # Periodic update check while True: metadata = client.check_for_updates() if metadata: update_path = client.download_update(metadata) if update_path: if client.install_update(update_path): print("[OTA] Update ready, will apply on next reboot") # Optionally auto-reboot or wait for user confirmation # client.apply_update() # Check every hour time.sleep(3600) if __name__ == "__main__": main()
bash# U-Boot environment for A/B partitioning # File: /etc/fw_env.config # Device Offset Size Erasesize /dev/mtd1 0x0000 0x1000 0x1000 /dev/mtd2 0x0000 0x1000 0x1000
bash# U-Boot bootcmd script for A/B boot # File: boot.cmd # Read boot slot from environment setenv bootslot ${boot_slot} if test -z "${bootslot}"; then setenv bootslot a fi # Read boot counter setenv bootcount ${boot_counter} if test -z "${bootcount}"; then setenv bootcount 0 fi # Check rollback condition if test ${bootcount} -eq 0; then echo "Booting from slot ${bootslot}" else # Decrement boot counter setexpr bootcount ${bootcount} - 1 setenv boot_counter ${bootcount} saveenv # If counter reaches 0, rollback to other slot if test ${bootcount} -eq 0; then echo "Boot failed 3 times, rolling back" if test "${bootslot}" = "a"; then setenv bootslot b else setenv bootslot a fi setenv boot_slot ${bootslot} saveenv fi fi # Set root partition if test "${bootslot}" = "a"; then setenv bootargs console=ttyS0,115200 root=/dev/mmcblk0p1 rw rootwait load mmc 0:1 ${kernel_addr_r} /boot/Image load mmc 0:1 ${fdt_addr_r} /boot/dtb else setenv bootargs console=ttyS0,115200 root=/dev/mmcblk0p2 rw rootwait load mmc 0:2 ${kernel_addr_r} /boot/Image load mmc 0:2 ${fdt_addr_r} /boot/dtb fi # Boot kernel booti ${kernel_addr_r} - ${fdt_addr_r}
python#!/usr/bin/env python3 """ Generate binary differential updates for OTA. Uses bsdiff algorithm for efficient bandwidth usage. """ import argparse import hashlib import json import subprocess import sys from pathlib import Path def generate_delta(old_image: Path, new_image: Path, output: Path): """ Generate binary diff between two images. Args: old_image: Path to old firmware image new_image: Path to new firmware image output: Path to output delta file """ print(f"Generating delta: {old_image} -> {new_image}") # Use bsdiff for binary diffing cmd = ["bsdiff", str(old_image), str(new_image), str(output)] try: subprocess.run(cmd, check=True) except FileNotFoundError: print("Error: bsdiff not found. Install with: apt-get install bsdiff") sys.exit(1) except subprocess.CalledProcessError as e: print(f"Error generating delta: {e}") sys.exit(1) # Calculate sizes and compression ratio old_size = old_image.stat().st_size new_size = new_image.stat().st_size delta_size = output.stat().st_size ratio = (1 - delta_size / new_size) * 100 print(f"Old image size: {old_size / 1024 / 1024:.2f} MB") print(f"New image size: {new_size / 1024 / 1024:.2f} MB") print(f"Delta size: {delta_size / 1024 / 1024:.2f} MB") print(f"Bandwidth savings: {ratio:.1f}%") # Generate metadata metadata = { "old_version": old_image.stem, "new_version": new_image.stem, "old_hash": compute_sha256(old_image), "new_hash": compute_sha256(new_image), "delta_hash": compute_sha256(output), "delta_size": delta_size, "compression_ratio": ratio } metadata_path = output.with_suffix('.json') with open(metadata_path, 'w') as f: json.dump(metadata, f, indent=2) print(f"Metadata written to: {metadata_path}") def compute_sha256(path: Path) -> str: """Compute SHA256 hash of file.""" sha256 = hashlib.sha256() with open(path, 'rb') as f: for chunk in iter(lambda: f.read(8192), b''): sha256.update(chunk) return sha256.hexdigest() def apply_delta(old_image: Path, delta: Path, output: Path): """ Apply delta patch to old image. Args: old_image: Path to old firmware image delta: Path to delta file output: Path to output patched image """ print(f"Applying delta: {old_image} + {delta} -> {output}") cmd = ["bspatch", str(old_image), str(output), str(delta)] try: subprocess.run(cmd, check=True) print(f"Patched image created: {output}") except FileNotFoundError: print("Error: bspatch not found. Install with: apt-get install bsdiff") sys.exit(1) except subprocess.CalledProcessError as e: print(f"Error applying delta: {e}") sys.exit(1) def main(): parser = argparse.ArgumentParser(description="OTA differential update generator") subparsers = parser.add_subparsers(dest='command', help='Command to execute') # Generate delta gen_parser = subparsers.add_parser('generate', help='Generate delta update') gen_parser.add_argument('--old', type=Path, required=True, help='Old image') gen_parser.add_argument('--new', type=Path, required=True, help='New image') gen_parser.add_argument('--output', type=Path, required=True, help='Output delta file') # Apply delta apply_parser = subparsers.add_parser('apply', help='Apply delta update') apply_parser.add_argument('--old', type=Path, required=True, help='Old image') apply_parser.add_argument('--delta', type=Path, required=True, help='Delta file') apply_parser.add_argument('--output', type=Path, required=True, help='Output image') args = parser.parse_args() if args.command == 'generate': generate_delta(args.old, args.new, args.output) elif args.command == 'apply': apply_delta(args.old, args.delta, args.output) else: parser.print_help() if __name__ == "__main__": main()
ini# File: /etc/systemd/system/ota-update.service [Unit] Description=OTA Update Client After=network-online.target Wants=network-online.target [Service] Type=simple ExecStart=/usr/bin/python3 /usr/local/bin/uptane-client.py Restart=always RestartSec=60 User=ota Group=ota # Security hardening PrivateTmp=yes ProtectSystem=strict ProtectHome=yes ReadWritePaths=/tmp /var/lib/ota NoNewPrivileges=yes CapabilityBoundingSet=CAP_NET_BIND_SERVICE [Install] WantedBy=multi-user.target
ini# File: /etc/systemd/system/ota-boot-verify.service [Unit] Description=OTA Boot Verification DefaultDependencies=no After=local-fs.target Before=sysinit.target [Service] Type=oneshot ExecStart=/usr/bin/python3 -c "from uptane_client import UptaneClient; UptaneClient().verify_boot_success()" RemainAfterExit=yes [Install] WantedBy=sysinit.target
yaml# File: /etc/mender/mender.conf { "ServerURL": "https://ota.example.com", "TenantToken": "TENANT_TOKEN_HERE", "InventoryPollIntervalSeconds": 28800, "UpdatePollIntervalSeconds": 1800, "RetryPollIntervalSeconds": 300, "RootfsPartA": "/dev/mmcblk0p2", "RootfsPartB": "/dev/mmcblk0p3", "ServerCertificate": "/etc/mender/server.crt", "DeviceTypeFile": "/var/lib/mender/device_type" }
bash# Mender artifact creation script #!/bin/bash # File: create-mender-artifact.sh set -e IMAGE_FILE=$1 ARTIFACT_NAME=$2 DEVICE_TYPE=$3 if [ -z "$IMAGE_FILE" ] || [ -z "$ARTIFACT_NAME" ] || [ -z "$DEVICE_TYPE" ]; then echo "Usage: $0 <image-file> <artifact-name> <device-type>" exit 1 fi # Create Mender artifact mender-artifact write rootfs-image \ --file "$IMAGE_FILE" \ --artifact-name "$ARTIFACT_NAME" \ --device-type "$DEVICE_TYPE" \ --output-path "${ARTIFACT_NAME}.mender" echo "Mender artifact created: ${ARTIFACT_NAME}.mender" # Sign artifact (optional) if [ -f "private.key" ]; then mender-artifact sign "${ARTIFACT_NAME}.mender" \ --key private.key \ --output-path "${ARTIFACT_NAME}.signed.mender" echo "Signed artifact created: ${ARTIFACT_NAME}.signed.mender" fi
Expert knowledge of automotive app platforms, app lifecycle management, sandboxing, permissions model, revenue sharing, 3rd-party developer SDKs, and app certification.
python#!/usr/bin/env python3 """ Vehicle App Store Backend API. Handles: - App catalog management - User authentication and purchases - App installation and updates - Developer portal integration """ from datetime import datetime, timedelta from enum import Enum from typing import List, Optional from fastapi import FastAPI, HTTPException, Depends, Security from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials from pydantic import BaseModel, Field from sqlalchemy import create_engine, Column, String, Integer, Float, DateTime, Boolean, JSON from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker, Session import jwt import hashlib # Database models Base = declarative_base() class AppCategory(str, Enum): """App categories.""" NAVIGATION = "navigation" MEDIA = "media" PRODUCTIVITY = "productivity" GAMES = "games" UTILITIES = "utilities" ADAS = "adas" DIAGNOSTICS = "diagnostics" class AppStatus(str, Enum): """App certification status.""" PENDING = "pending" APPROVED = "approved" REJECTED = "rejected" SUSPENDED = "suspended" class App(Base): """Application model.""" __tablename__ = "apps" id = Column(String, primary_key=True) name = Column(String, nullable=False) developer_id = Column(String, nullable=False) category = Column(String, nullable=False) version = Column(String, nullable=False) description = Column(String) price = Column(Float, default=0.0) rating = Column(Float, default=0.0) downloads = Column(Integer, default=0) status = Column(String, default=AppStatus.PENDING.value) manifest_url = Column(String) icon_url = Column(String) screenshots = Column(JSON, default=list) permissions = Column(JSON, default=list) min_platform_version = Column(String) created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) class User(Base): """User model.""" __tablename__ = "users" id = Column(String, primary_key=True) vin = Column(String, unique=True, nullable=False) # Vehicle VIN email = Column(String) purchased_apps = Column(JSON, default=list) installed_apps = Column(JSON, default=list) created_at = Column(DateTime, default=datetime.utcnow) class Purchase(Base): """Purchase transaction model.""" __tablename__ = "purchases" id = Column(String, primary_key=True) user_id = Column(String, nullable=False) app_id = Column(String, nullable=False) amount = Column(Float) transaction_date = Column(DateTime, default=datetime.utcnow) status = Column(String, default="completed") # Pydantic schemas class AppCreate(BaseModel): """App creation schema.""" name: str developer_id: str category: AppCategory version: str description: str price: float = 0.0 manifest_url: str icon_url: str screenshots: List[str] = [] permissions: List[str] = [] min_platform_version: str = "1.0.0" class AppResponse(BaseModel): """App response schema.""" id: str name: str developer_id: str category: str version: str description: str price: float rating: float downloads: int status: str icon_url: str screenshots: List[str] permissions: List[str] created_at: datetime class Config: from_attributes = True class PurchaseRequest(BaseModel): """Purchase request schema.""" app_id: str payment_method: str = "credit_card" class InstallRequest(BaseModel): """App installation request.""" app_id: str # Initialize FastAPI app = FastAPI(title="Vehicle App Store API", version="1.0.0") security = HTTPBearer() # Database setup DATABASE_URL = "postgresql://appstore:password@localhost/vehicle_appstore" engine = create_engine(DATABASE_URL) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) # JWT secret JWT_SECRET = "your-secret-key-change-in-production" def get_db(): """Database session dependency.""" db = SessionLocal() try: yield db finally: db.close() def verify_token(credentials: HTTPAuthorizationCredentials = Security(security)) -> dict: """Verify JWT token and extract user info.""" try: token = credentials.credentials payload = jwt.decode(token, JWT_SECRET, algorithms=["HS256"]) return payload except jwt.ExpiredSignatureError: raise HTTPException(status_code=401, detail="Token expired") except jwt.InvalidTokenError: raise HTTPException(status_code=401, detail="Invalid token") @app.post("/auth/login") async def login(vin: str, db: Session = Depends(get_db)): """ Authenticate vehicle by VIN. In production, this would verify VIN against manufacturer database. """ user = db.query(User).filter(User.vin == vin).first() if not user: # Create new user user = User(id=hashlib.sha256(vin.encode()).hexdigest()[:16], vin=vin) db.add(user) db.commit() # Generate JWT token = jwt.encode( { "user_id": user.id, "vin": user.vin, "exp": datetime.utcnow() + timedelta(days=30) }, JWT_SECRET, algorithm="HS256" ) return {"access_token": token, "token_type": "bearer"} @app.get("/apps", response_model=List[AppResponse]) async def list_apps( category: Optional[AppCategory] = None, search: Optional[str] = None, db: Session = Depends(get_db) ): """ List available apps. Filters by category and search query. Only returns approved apps. """ query = db.query(App).filter(App.status == AppStatus.APPROVED.value) if category: query = query.filter(App.category == category.value) if search: query = query.filter(App.name.ilike(f"%{search}%")) apps = query.order_by(App.rating.desc(), App.downloads.desc()).limit(50).all() return apps @app.get("/apps/{app_id}", response_model=AppResponse) async def get_app(app_id: str, db: Session = Depends(get_db)): """Get app details.""" app_obj = db.query(App).filter(App.id == app_id).first() if not app_obj: raise HTTPException(status_code=404, detail="App not found") return app_obj @app.post("/apps", response_model=AppResponse) async def submit_app( app_data: AppCreate, user_info: dict = Depends(verify_token), db: Session = Depends(get_db) ): """ Submit new app for certification. Developer portal endpoint. """ app_id = hashlib.sha256( f"{app_data.name}{app_data.developer_id}{datetime.utcnow()}".encode() ).hexdigest()[:16] app_obj = App( id=app_id, name=app_data.name, developer_id=app_data.developer_id, category=app_data.category.value, version=app_data.version, description=app_data.description, price=app_data.price, manifest_url=app_data.manifest_url, icon_url=app_data.icon_url, screenshots=app_data.screenshots, permissions=app_data.permissions, min_platform_version=app_data.min_platform_version, status=AppStatus.PENDING.value ) db.add(app_obj) db.commit() db.refresh(app_obj) return app_obj @app.post("/apps/{app_id}/purchase") async def purchase_app( app_id: str, purchase_data: PurchaseRequest, user_info: dict = Depends(verify_token), db: Session = Depends(get_db) ): """ Purchase app. Handles payment processing and license activation. """ app_obj = db.query(App).filter(App.id == app_id).first() if not app_obj: raise HTTPException(status_code=404, detail="App not found") user = db.query(User).filter(User.id == user_info["user_id"]).first() # Check if already purchased if app_id in user.purchased_apps: return {"message": "App already purchased", "app_id": app_id} # Process payment (integrate with payment gateway) # For demo purposes, assume payment succeeds purchase_id = hashlib.sha256( f"{user.id}{app_id}{datetime.utcnow()}".encode() ).hexdigest()[:16] purchase = Purchase( id=purchase_id, user_id=user.id, app_id=app_id, amount=app_obj.price, status="completed" ) # Update user purchased apps user.purchased_apps.append(app_id) # Increment app downloads app_obj.downloads += 1 db.add(purchase) db.commit() return { "message": "Purchase successful", "purchase_id": purchase_id, "app_id": app_id, "amount": app_obj.price } @app.post("/apps/{app_id}/install") async def install_app( app_id: str, user_info: dict = Depends(verify_token), db: Session = Depends(get_db) ): """ Install app to vehicle. Returns manifest URL for vehicle runtime to download. """ user = db.query(User).filter(User.id == user_info["user_id"]).first() # Check if purchased if app_id not in user.purchased_apps: raise HTTPException(status_code=403, detail="App not purchased") app_obj = db.query(App).filter(App.id == app_id).first() if not app_obj: raise HTTPException(status_code=404, detail="App not found") # Add to installed apps if app_id not in user.installed_apps: user.installed_apps.append(app_id) db.commit() return { "message": "Installation initiated", "app_id": app_id, "manifest_url": app_obj.manifest_url, "version": app_obj.version } @app.get("/apps/{app_id}/updates") async def check_app_updates( app_id: str, current_version: str, user_info: dict = Depends(verify_token), db: Session = Depends(get_db) ): """ Check for app updates. Returns latest version if newer than current. """ app_obj = db.query(App).filter(App.id == app_id).first() if not app_obj: raise HTTPException(status_code=404, detail="App not found") if app_obj.version != current_version: return { "update_available": True, "latest_version": app_obj.version, "manifest_url": app_obj.manifest_url, "changelog": f"Updated to version {app_obj.version}" } return {"update_available": False} @app.get("/user/apps") async def get_user_apps( user_info: dict = Depends(verify_token), db: Session = Depends(get_db) ): """Get user's purchased and installed apps.""" user = db.query(User).filter(User.id == user_info["user_id"]).first() purchased = db.query(App).filter(App.id.in_(user.purchased_apps)).all() installed = db.query(App).filter(App.id.in_(user.installed_apps)).all() return { "purchased": [AppResponse.from_orm(app) for app in purchased], "installed": [AppResponse.from_orm(app) for app in installed] } @app.post("/apps/{app_id}/rate") async def rate_app( app_id: str, rating: float = Field(..., ge=1.0, le=5.0), user_info: dict = Depends(verify_token), db: Session = Depends(get_db) ): """Rate an app (1-5 stars).""" user = db.query(User).filter(User.id == user_info["user_id"]).first() if app_id not in user.purchased_apps: raise HTTPException(status_code=403, detail="Must own app to rate") app_obj = db.query(App).filter(App.id == app_id).first() # Update rolling average (simplified) # In production, store individual ratings app_obj.rating = (app_obj.rating + rating) / 2 db.commit() return {"message": "Rating submitted", "new_rating": app_obj.rating} if __name__ == "__main__": import uvicorn Base.metadata.create_all(bind=engine) uvicorn.run(app, host="0.0.0.0", port=8000)
yaml# App manifest - describes app metadata and requirements # File: app-manifest.yaml apiVersion: vehicle.app/v1 kind: VehicleApp metadata: name: spotify-automotive version: 2.1.4 displayName: Spotify for Cars developer: id: spotify-inc name: Spotify AB email: automotive@spotify.com website: https://www.spotify.com spec: description: | Stream millions of songs and podcasts directly in your vehicle. Integrates with vehicle audio system and steering wheel controls. category: media price: 0.0 # Free with subscription inAppPurchases: true # Platform requirements platform: minVersion: "2.0.0" targetVersion: "3.1.0" architecture: ["arm64", "x86_64"] # Permissions required permissions: - AUDIO_PLAYBACK - INTERNET - BLUETOOTH - VEHICLE_SPEED # For audio ducking when speed > 100mph - STEERING_CONTROLS - LOCATION # For personalized recommendations # Resource limits resources: memory: 512Mi cpu: 500m storage: 2Gi bandwidth: 10Mbps # Container configuration container: image: registry.spotify.com/automotive/spotify:2.1.4 entrypoint: ["/usr/bin/spotify-app"] environment: - name: API_KEY valueFrom: secretRef: name: spotify-api-credentials key: api-key # Service exposure services: - name: media-player protocol: dbus interface: org.mpris.MediaPlayer2 path: /org/mpris/MediaPlayer2/spotify # UI configuration ui: icon: https://cdn.spotify.com/automotive/icon-512.png screenshots: - https://cdn.spotify.com/automotive/screenshot1.png - https://cdn.spotify.com/automotive/screenshot2.png launchMode: fullscreen displayInLauncher: true # Integration points integrations: voiceAssistant: enabled: true wakeWords: ["play music", "open spotify"] steeringControls: enabled: true actions: [play, pause, next, previous, volume] clusterDisplay: enabled: true showNowPlaying: true # Safety constraints safety: disableWhileDriving: false touchInteractionLimit: true # Limit UI interaction while moving voiceControlRequired: false # Analytics and telemetry telemetry: enabled: true endpoint: https://telemetry.spotify.com/automotive dataTypes: - usage_statistics - crash_reports - performance_metrics # Update strategy updatePolicy: automatic: true canRollback: true maxDowntime: 30s
go// Vehicle app sandbox runtime using containerd // File: app-runtime.go package main import ( "context" "fmt" "log" "os" "path/filepath" "syscall" "github.com/containerd/containerd" "github.com/containerd/containerd/cio" "github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/oci" "gopkg.in/yaml.v3" ) // AppManifest represents vehicle app configuration type AppManifest struct { APIVersion string `yaml:"apiVersion"` Kind string `yaml:"kind"` Metadata struct { Name string `yaml:"name"` Version string `yaml:"version"` DisplayName string `yaml:"displayName"` } `yaml:"metadata"` Spec struct { Permissions []string `yaml:"permissions"` Resources struct { Memory string `yaml:"memory"` CPU string `yaml:"cpu"` Storage string `yaml:"storage"` Bandwidth string `yaml:"bandwidth"` } `yaml:"resources"` Container struct { Image string `yaml:"image"` Entrypoint []string `yaml:"entrypoint"` Environment []struct { Name string `yaml:"name"` Value string `yaml:"value,omitempty"` } `yaml:"environment"` } `yaml:"container"` Safety struct { DisableWhileDriving bool `yaml:"disableWhileDriving"` TouchInteractionLimit bool `yaml:"touchInteractionLimit"` } `yaml:"safety"` } `yaml:"spec"` } // AppRuntime manages vehicle app lifecycle type AppRuntime struct { client *containerd.Client namespace string } // NewAppRuntime creates a new app runtime func NewAppRuntime() (*AppRuntime, error) { client, err := containerd.New("/run/containerd/containerd.sock") if err != nil { return nil, fmt.Errorf("failed to connect to containerd: %w", err) } return &AppRuntime{ client: client, namespace: "vehicle-apps", }, nil } // InstallApp installs an app from manifest func (r *AppRuntime) InstallApp(manifestPath string) error { // Load manifest manifest, err := r.loadManifest(manifestPath) if err != nil { return fmt.Errorf("failed to load manifest: %w", err) } log.Printf("Installing app: %s v%s", manifest.Metadata.Name, manifest.Metadata.Version) ctx := namespaces.WithNamespace(context.Background(), r.namespace) // Pull container image image, err := r.client.Pull(ctx, manifest.Spec.Container.Image, containerd.WithPullUnpack) if err != nil { return fmt.Errorf("failed to pull image: %w", err) } log.Printf("Pulled image: %s", image.Name()) // Create container with security constraints container, err := r.createSecureContainer(ctx, manifest, image) if err != nil { return fmt.Errorf("failed to create container: %w", err) } log.Printf("App installed successfully: %s", container.ID()) return nil } // createSecureContainer creates a sandboxed container with security policies func (r *AppRuntime) createSecureContainer( ctx context.Context, manifest *AppManifest, image containerd.Image, ) (containerd.Container, error) { appName := manifest.Metadata.Name // Define security policies based on permissions opts := []oci.SpecOpts{ oci.WithImageConfig(image), oci.WithEnv(r.buildEnvironment(manifest)), // Resource limits oci.WithMemoryLimit(r.parseMemory(manifest.Spec.Resources.Memory)), oci.WithCPUQuota(500000, 100000), // 500m CPU // Security constraints oci.WithNoNewPrivileges, oci.WithPrivileged(false), // Read-only root filesystem oci.WithRootFSReadonly(), // Drop all capabilities by default oci.WithCapabilities([]string{}), // Add capabilities based on permissions oci.WithAddedCapabilities(r.getCapabilities(manifest.Spec.Permissions)), // Mount tmpfs for writable directories oci.WithMounts([]oci.Mount{ { Type: "tmpfs", Source: "tmpfs", Destination: "/tmp", Options: []string{"nosuid", "noexec", "nodev"}, }, { Type: "bind", Source: filepath.Join("/var/lib/vehicle-apps", appName, "data"), Destination: "/data", Options: []string{"rbind", "rw"}, }, }), // Namespace isolation oci.WithLinuxNamespace(oci.LinuxNamespace{ Type: "pid", }), oci.WithLinuxNamespace(oci.LinuxNamespace{ Type: "network", }), oci.WithLinuxNamespace(oci.LinuxNamespace{ Type: "ipc", }), } // Permission-specific constraints if !r.hasPermission(manifest.Spec.Permissions, "INTERNET") { // Block network access opts = append(opts, oci.WithHostNamespace(oci.NetworkNamespace)) } // Create container container, err := r.client.NewContainer( ctx, appName, containerd.WithImage(image), containerd.WithNewSnapshot(appName+"-snapshot", image), containerd.WithNewSpec(opts...), ) return container, err } // StartApp starts an installed app func (r *AppRuntime) StartApp(appName string, manifest *AppManifest) error { ctx := namespaces.WithNamespace(context.Background(), r.namespace) container, err := r.client.LoadContainer(ctx, appName) if err != nil { return fmt.Errorf("failed to load container: %w", err) } // Check safety constraints if manifest.Spec.Safety.DisableWhileDriving { if r.isVehicleMoving() { return fmt.Errorf("app disabled while driving") } } // Create task task, err := container.NewTask(ctx, cio.NewCreator(cio.WithStdio)) if err != nil { return fmt.Errorf("failed to create task: %w", err) } // Start task if err := task.Start(ctx); err != nil { return fmt.Errorf("failed to start task: %w", err) } log.Printf("App started: %s (PID: %d)", appName, task.Pid()) return nil } // StopApp stops a running app func (r *AppRuntime) StopApp(appName string) error { ctx := namespaces.WithNamespace(context.Background(), r.namespace) container, err := r.client.LoadContainer(ctx, appName) if err != nil { return fmt.Errorf("failed to load container: %w", err) } task, err := container.Task(ctx, nil) if err != nil { return fmt.Errorf("failed to get task: %w", err) } // Graceful shutdown if err := task.Kill(ctx, syscall.SIGTERM); err != nil { return fmt.Errorf("failed to send SIGTERM: %w", err) } // Wait for exit (with timeout) status, err := task.Wait(ctx) if err != nil { return fmt.Errorf("failed to wait for exit: %w", err) } <-status log.Printf("App stopped: %s", appName) return nil } // UninstallApp removes an installed app func (r *AppRuntime) UninstallApp(appName string) error { ctx := namespaces.WithNamespace(context.Background(), r.namespace) // Stop if running _ = r.StopApp(appName) // Delete container container, err := r.client.LoadContainer(ctx, appName) if err != nil { return fmt.Errorf("failed to load container: %w", err) } if err := container.Delete(ctx, containerd.WithSnapshotCleanup); err != nil { return fmt.Errorf("failed to delete container: %w", err) } log.Printf("App uninstalled: %s", appName) return nil } // Helper functions func (r *AppRuntime) loadManifest(path string) (*AppManifest, error) { data, err := os.ReadFile(path) if err != nil { return nil, err } var manifest AppManifest if err := yaml.Unmarshal(data, &manifest); err != nil { return nil, err } return &manifest, nil } func (r *AppRuntime) buildEnvironment(manifest *AppManifest) []string { env := []string{} for _, e := range manifest.Spec.Container.Environment { env = append(env, fmt.Sprintf("%s=%s", e.Name, e.Value)) } return env } func (r *AppRuntime) parseMemory(mem string) uint64 { // Simple parser for Mi/Gi units // In production, use proper parsing return 512 * 1024 * 1024 // 512Mi } func (r *AppRuntime) getCapabilities(permissions []string) []string { caps := []string{} for _, perm := range permissions { switch perm { case "INTERNET": caps = append(caps, "CAP_NET_BIND_SERVICE") case "BLUETOOTH": caps = append(caps, "CAP_NET_ADMIN") case "AUDIO_PLAYBACK": // Allow access to audio devices } } return caps } func (r *AppRuntime) hasPermission(permissions []string, perm string) bool { for _, p := range permissions { if p == perm { return true } } return false } func (r *AppRuntime) isVehicleMoving() bool { // Check vehicle speed from CAN bus // Placeholder implementation return false } func main() { runtime, err := NewAppRuntime() if err != nil { log.Fatalf("Failed to create runtime: %v", err) } // Example: Install and start app if err := runtime.InstallApp("spotify-manifest.yaml"); err != nil { log.Fatalf("Failed to install app: %v", err) } // Load manifest for safety checks manifest, _ := runtime.loadManifest("spotify-manifest.yaml") if err := runtime.StartApp("spotify-automotive", manifest); err != nil { log.Fatalf("Failed to start app: %v", err) } log.Println("App runtime running...") select {} // Keep running }
typescript// Vehicle App Developer SDK // File: vehicle-app-sdk.ts /** * Vehicle App SDK for TypeScript/JavaScript apps * Provides APIs for vehicle integration */ export enum Permission { AUDIO_PLAYBACK = 'AUDIO_PLAYBACK', INTERNET = 'INTERNET', BLUETOOTH = 'BLUETOOTH', VEHICLE_SPEED = 'VEHICLE_SPEED', LOCATION = 'LOCATION', STEERING_CONTROLS = 'STEERING_CONTROLS', CLIMATE_CONTROL = 'CLIMATE_CONTROL', } export interface VehicleState { speed: number; // km/h gear: string; isMoving: boolean; batteryLevel: number; // percentage range: number; // km } export class VehicleAppSDK { private readonly appId: string; private readonly permissions: Permission[]; private ws: WebSocket | null = null; constructor(appId: string, permissions: Permission[]) { this.appId = appId; this.permissions = permissions; } /** * Initialize SDK connection to vehicle platform */ async initialize(): Promise<void> { console.log(`Initializing SDK for app: ${this.appId}`); // Connect to vehicle platform via WebSocket this.ws = new WebSocket('ws://vehicle-platform:8080/apps/ws'); return new Promise((resolve, reject) => { this.ws!.onopen = () => { console.log('Connected to vehicle platform'); // Send authentication this.ws!.send(JSON.stringify({ type: 'auth', appId: this.appId, permissions: this.permissions, })); resolve(); }; this.ws!.onerror = (error) => { reject(new Error(`WebSocket error: ${error}`)); }; }); } /** * Get current vehicle state */ async getVehicleState(): Promise<VehicleState> { this.checkPermission(Permission.VEHICLE_SPEED); return new Promise((resolve, reject) => { const requestId = Date.now().toString(); this.ws!.send(JSON.stringify({ type: 'request', requestId, method: 'getVehicleState', })); const handler = (event: MessageEvent) => { const data = JSON.parse(event.data); if (data.requestId === requestId) { this.ws!.removeEventListener('message', handler); resolve(data.result); } }; this.ws!.addEventListener('message', handler); // Timeout after 5 seconds setTimeout(() => { this.ws!.removeEventListener('message', handler); reject(new Error('Request timeout')); }, 5000); }); } /** * Subscribe to vehicle state changes */ onVehicleStateChange(callback: (state: VehicleState) => void): void { this.checkPermission(Permission.VEHICLE_SPEED); this.ws!.addEventListener('message', (event) => { const data = JSON.parse(event.data); if (data.type === 'vehicleStateUpdate') { callback(data.state); } }); // Subscribe this.ws!.send(JSON.stringify({ type: 'subscribe', topic: 'vehicleState', })); } /** * Control audio playback */ async playAudio(url: string): Promise<void> { this.checkPermission(Permission.AUDIO_PLAYBACK); await this.sendRequest('playAudio', { url }); } /** * Subscribe to steering control events */ onSteeringControl(callback: (action: string) => void): void { this.checkPermission(Permission.STEERING_CONTROLS); this.ws!.addEventListener('message', (event) => { const data = JSON.parse(event.data); if (data.type === 'steeringControl') { callback(data.action); // 'next', 'previous', 'play', 'pause' } }); this.ws!.send(JSON.stringify({ type: 'subscribe', topic: 'steeringControls', })); } /** * Display notification in vehicle cluster */ async showNotification(title: string, message: string): Promise<void> { await this.sendRequest('showNotification', { title, message }); } /** * Get current location */ async getLocation(): Promise<{ latitude: number; longitude: number }> { this.checkPermission(Permission.LOCATION); const result = await this.sendRequest('getLocation', {}); return result as { latitude: number; longitude: number }; } private checkPermission(permission: Permission): void { if (!this.permissions.includes(permission)) { throw new Error(`Permission not granted: ${permission}`); } } private async sendRequest(method: string, params: any): Promise<any> { return new Promise((resolve, reject) => { const requestId = Date.now().toString(); this.ws!.send(JSON.stringify({ type: 'request', requestId, method, params, })); const handler = (event: MessageEvent) => { const data = JSON.parse(event.data); if (data.requestId === requestId) { this.ws!.removeEventListener('message', handler); if (data.error) { reject(new Error(data.error)); } else { resolve(data.result); } } }; this.ws!.addEventListener('message', handler); setTimeout(() => { this.ws!.removeEventListener('message', handler); reject(new Error('Request timeout')); }, 5000); }); } } // Example app using SDK async function exampleApp() { const sdk = new VehicleAppSDK('spotify-automotive', [ Permission.AUDIO_PLAYBACK, Permission.STEERING_CONTROLS, Permission.VEHICLE_SPEED, ]); await sdk.initialize(); // Get vehicle state const state = await sdk.getVehicleState(); console.log(`Vehicle speed: ${state.speed} km/h`); // Subscribe to steering controls sdk.onSteeringControl((action) => { console.log(`Steering control: ${action}`); if (action === 'play') { sdk.playAudio('https://cdn.spotify.com/track/123.mp3'); } }); // Monitor vehicle state sdk.onVehicleStateChange((state) => { if (state.speed > 100) { // Duck audio volume at high speeds console.log('High speed detected, reducing volume'); } }); }
Expert knowledge of SDV middleware stacks (COVESA VSS, Eclipse SDV, AUTOSAR Adaptive), data models (VSS/VISS), pub-sub brokers, and service mesh for vehicles.
yaml# Vehicle Signal Specification (VSS) tree # File: vehicle-signals.vspec Vehicle: description: Top-level vehicle type: branch Vehicle.Speed: datatype: float type: sensor unit: km/h description: Vehicle speed min: 0 max: 300 Vehicle.Powertrain: type: branch description: Powertrain signals Vehicle.Powertrain.Battery: type: branch description: Battery system Vehicle.Powertrain.Battery.StateOfCharge: datatype: float type: sensor unit: percent description: Battery state of charge min: 0 max: 100 Vehicle.Powertrain.Battery.Voltage: datatype: float type: sensor unit: V description: Battery voltage min: 0 max: 800 Vehicle.Powertrain.Battery.Current: datatype: float type: sensor unit: A description: Battery current (positive = charging) min: -400 max: 400 Vehicle.Powertrain.Battery.Temperature: datatype: float type: sensor unit: celsius description: Battery temperature min: -40 max: 80 Vehicle.Cabin: type: branch description: Cabin signals Vehicle.Cabin.HVAC: type: branch description: HVAC system Vehicle.Cabin.HVAC.IsAirConditioningActive: datatype: boolean type: actuator description: Air conditioning status Vehicle.Cabin.HVAC.AmbientAirTemperature: datatype: float type: sensor unit: celsius description: Ambient temperature Vehicle.Body: type: branch description: Body signals Vehicle.Body.Doors: type: branch instances: - Row1Left - Row1Right - Row2Left - Row2Right Vehicle.Body.Doors.IsLocked: datatype: boolean type: actuator description: Door lock status Vehicle.Body.Doors.IsOpen: datatype: boolean type: sensor description: Door open status Vehicle.ADAS: type: branch description: ADAS signals Vehicle.ADAS.CruiseControl: type: branch description: Cruise control Vehicle.ADAS.CruiseControl.IsActive: datatype: boolean type: actuator description: Cruise control active Vehicle.ADAS.CruiseControl.SpeedSet: datatype: float type: actuator unit: km/h description: Set cruise control speed
rust// VSS data broker implementation // File: vss-broker/src/main.rs use tokio; use std::collections::HashMap; use std::sync::{Arc, RwLock}; use serde::{Deserialize, Serialize}; use warp::Filter; #[derive(Debug, Clone, Serialize, Deserialize)] struct VehicleSignal { path: String, value: SignalValue, timestamp: u64, } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(untagged)] enum SignalValue { Float(f64), Int(i64), Bool(bool), String(String), } #[derive(Clone)] struct DataBroker { signals: Arc<RwLock<HashMap<String, VehicleSignal>>>, subscribers: Arc<RwLock<HashMap<String, Vec<tokio::sync::mpsc::UnboundedSender<VehicleSignal>>>>>, } impl DataBroker { fn new() -> Self { DataBroker { signals: Arc::new(RwLock::new(HashMap::new())), subscribers: Arc::new(RwLock::new(HashMap::new())), } } /// Get signal value fn get(&self, path: &str) -> Option<VehicleSignal> { let signals = self.signals.read().unwrap(); signals.get(path).cloned() } /// Set signal value fn set(&self, signal: VehicleSignal) { let path = signal.path.clone(); // Store signal { let mut signals = self.signals.write().unwrap(); signals.insert(path.clone(), signal.clone()); } // Notify subscribers self.notify_subscribers(&path, signal); } /// Subscribe to signal updates fn subscribe(&self, path: String) -> tokio::sync::mpsc::UnboundedReceiver<VehicleSignal> { let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); let mut subscribers = self.subscribers.write().unwrap(); subscribers.entry(path).or_insert_with(Vec::new).push(tx); rx } /// Notify subscribers of signal update fn notify_subscribers(&self, path: &str, signal: VehicleSignal) { let subscribers = self.subscribers.read().unwrap(); if let Some(subs) = subscribers.get(path) { for tx in subs { let _ = tx.send(signal.clone()); } } // Also notify wildcard subscribers (path.*) let parts: Vec<&str> = path.split('.').collect(); for i in 0..parts.len() { let wildcard_path = format!("{}.*", parts[..i].join(".")); if let Some(subs) = subscribers.get(&wildcard_path) { for tx in subs { let _ = tx.send(signal.clone()); } } } } /// Batch get signals fn get_batch(&self, paths: Vec<String>) -> HashMap<String, VehicleSignal> { let signals = self.signals.read().unwrap(); let mut result = HashMap::new(); for path in paths { if let Some(signal) = signals.get(&path) { result.insert(path, signal.clone()); } } result } } #[tokio::main] async fn main() { let broker = DataBroker::new(); let broker = Arc::new(broker); // REST API routes let broker_filter = warp::any().map(move || broker.clone()); // GET /api/signals/{path} let get_signal = warp::path!("api" / "signals" / String) .and(warp::get()) .and(broker_filter.clone()) .map(|path: String, broker: Arc<DataBroker>| { match broker.get(&path) { Some(signal) => warp::reply::json(&signal), None => warp::reply::json(&serde_json::json!({ "error": "Signal not found" })), } }); // POST /api/signals let set_signal = warp::path!("api" / "signals") .and(warp::post()) .and(warp::body::json()) .and(broker_filter.clone()) .map(|signal: VehicleSignal, broker: Arc<DataBroker>| { broker.set(signal.clone()); warp::reply::json(&serde_json::json!({ "status": "ok", "path": signal.path })) }); // WebSocket /api/subscribe let subscribe = warp::path!("api" / "subscribe") .and(warp::ws()) .and(broker_filter.clone()) .map(|ws: warp::ws::Ws, broker: Arc<DataBroker>| { ws.on_upgrade(move |socket| handle_websocket(socket, broker)) }); let routes = get_signal .or(set_signal) .or(subscribe); println!("[VSS Broker] Starting on 0.0.0.0:8080"); warp::serve(routes) .run(([0, 0, 0, 0], 8080)) .await; } async fn handle_websocket( websocket: warp::ws::WebSocket, broker: Arc<DataBroker> ) { use futures::{StreamExt, SinkExt}; let (mut tx, mut rx) = websocket.split(); // Handle incoming subscription requests while let Some(result) = rx.next().await { let msg = match result { Ok(msg) => msg, Err(e) => { eprintln!("WebSocket error: {}", e); break; } }; if let Ok(text) = msg.to_str() { // Parse subscription request if let Ok(req) = serde_json::from_str::<serde_json::Value>(text) { if let Some(path) = req["path"].as_str() { let path = path.to_string(); let mut signal_rx = broker.subscribe(path.clone()); // Send updates tokio::spawn(async move { while let Some(signal) = signal_rx.recv().await { let json = serde_json::to_string(&signal).unwrap(); if tx.send(warp::ws::Message::text(json)).await.is_err() { break; } } }); } } } } }
python#!/usr/bin/env python3 """ VISS (Vehicle Information Service Specification) server. RESTful and WebSocket API for VSS data access. """ from fastapi import FastAPI, WebSocket, HTTPException from fastapi.responses import JSONResponse from pydantic import BaseModel from typing import Optional, List, Dict, Any import asyncio import json from datetime import datetime app = FastAPI(title="VISS Server", version="2.0.0") class VehicleSignal(BaseModel): """Vehicle signal data model.""" path: str value: Any timestamp: Optional[int] = None class GetRequest(BaseModel): """VISS GET request.""" action: str = "get" path: str requestId: str class SetRequest(BaseModel): """VISS SET request.""" action: str = "set" path: str value: Any requestId: str class SubscribeRequest(BaseModel): """VISS SUBSCRIBE request.""" action: str = "subscribe" path: str subscriptionId: str # In-memory signal storage (replace with Redis in production) signals_db: Dict[str, VehicleSignal] = {} # WebSocket subscriptions subscriptions: Dict[str, List[WebSocket]] = {} @app.get("/vss/api/v2/{path:path}") async def get_signal_rest(path: str): """ Get signal value via REST API. Example: GET /vss/api/v2/Vehicle/Speed """ if path not in signals_db: raise HTTPException(status_code=404, detail="Signal not found") signal = signals_db[path] return JSONResponse({ "action": "get", "path": path, "value": signal.value, "timestamp": signal.timestamp or int(datetime.utcnow().timestamp() * 1000), "requestId": "rest-" + str(int(datetime.utcnow().timestamp())) }) @app.put("/vss/api/v2/{path:path}") async def set_signal_rest(path: str, value: Any): """ Set signal value via REST API. Example: PUT /vss/api/v2/Vehicle/Cabin/HVAC/IsAirConditioningActive Body: {"value": true} """ signal = VehicleSignal( path=path, value=value, timestamp=int(datetime.utcnow().timestamp() * 1000) ) signals_db[path] = signal # Notify subscribers await notify_subscribers(path, signal) return JSONResponse({ "action": "set", "path": path, "timestamp": signal.timestamp, "requestId": "rest-" + str(int(datetime.utcnow().timestamp())) }) @app.websocket("/vss/api/v2") async def websocket_endpoint(websocket: WebSocket): """ VISS WebSocket endpoint for real-time communication. Supports: - GET: Get signal value - SET: Set signal value - SUBSCRIBE: Subscribe to signal updates - UNSUBSCRIBE: Unsubscribe from signal """ await websocket.accept() try: while True: # Receive request data = await websocket.receive_text() request = json.loads(data) action = request.get("action") if action == "get": await handle_get(websocket, request) elif action == "set": await handle_set(websocket, request) elif action == "subscribe": await handle_subscribe(websocket, request) elif action == "unsubscribe": await handle_unsubscribe(websocket, request) else: await websocket.send_json({ "error": {"number": 400, "message": "Invalid action"}, "requestId": request.get("requestId", "unknown") }) except Exception as e: print(f"[VISS] WebSocket error: {e}") finally: # Clean up subscriptions for path, subs in subscriptions.items(): if websocket in subs: subs.remove(websocket) async def handle_get(websocket: WebSocket, request: dict): """Handle GET request.""" path = request["path"] if path not in signals_db: await websocket.send_json({ "action": "get", "error": {"number": 404, "message": "Signal not found"}, "requestId": request["requestId"] }) return signal = signals_db[path] await websocket.send_json({ "action": "get", "path": path, "value": signal.value, "timestamp": signal.timestamp, "requestId": request["requestId"] }) async def handle_set(websocket: WebSocket, request: dict): """Handle SET request.""" path = request["path"] value = request["value"] signal = VehicleSignal( path=path, value=value, timestamp=int(datetime.utcnow().timestamp() * 1000) ) signals_db[path] = signal # Notify subscribers await notify_subscribers(path, signal) await websocket.send_json({ "action": "set", "path": path, "timestamp": signal.timestamp, "requestId": request["requestId"] }) async def handle_subscribe(websocket: WebSocket, request: dict): """Handle SUBSCRIBE request.""" path = request["path"] subscription_id = request["subscriptionId"] # Add to subscriptions if path not in subscriptions: subscriptions[path] = [] subscriptions[path].append(websocket) # Send confirmation await websocket.send_json({ "action": "subscribe", "subscriptionId": subscription_id, "timestamp": int(datetime.utcnow().timestamp() * 1000) }) async def handle_unsubscribe(websocket: WebSocket, request: dict): """Handle UNSUBSCRIBE request.""" subscription_id = request["subscriptionId"] # Remove from subscriptions for path, subs in subscriptions.items(): if websocket in subs: subs.remove(websocket) await websocket.send_json({ "action": "unsubscribe", "subscriptionId": subscription_id, "timestamp": int(datetime.utcnow().timestamp() * 1000) }) async def notify_subscribers(path: str, signal: VehicleSignal): """Notify all subscribers of signal update.""" if path in subscriptions: notification = { "action": "subscription", "path": path, "value": signal.value, "timestamp": signal.timestamp } # Send to all subscribers dead_sockets = [] for websocket in subscriptions[path]: try: await websocket.send_json(notification) except Exception: dead_sockets.append(websocket) # Remove dead sockets for ws in dead_sockets: subscriptions[path].remove(ws) # Populate some initial data @app.on_event("startup") async def populate_initial_data(): """Populate initial vehicle signals.""" initial_signals = [ VehicleSignal(path="Vehicle.Speed", value=0.0), VehicleSignal(path="Vehicle.Powertrain.Battery.StateOfCharge", value=80.0), VehicleSignal(path="Vehicle.Powertrain.Battery.Voltage", value=400.0), VehicleSignal(path="Vehicle.Powertrain.Battery.Current", value=0.0), VehicleSignal(path="Vehicle.Cabin.HVAC.IsAirConditioningActive", value=False), VehicleSignal(path="Vehicle.Body.Doors.Row1Left.IsLocked", value=True), VehicleSignal(path="Vehicle.ADAS.CruiseControl.IsActive", value=False), ] for signal in initial_signals: signal.timestamp = int(datetime.utcnow().timestamp() * 1000) signals_db[signal.path] = signal print("[VISS] Initial signals populated") if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8080)
yaml# Service mesh configuration for vehicle platform # File: vehicle-service-mesh.yaml apiVersion: v1 kind: ConfigMap metadata: name: service-mesh-config namespace: vehicle-platform data: mesh-config.yaml: | # Global mesh configuration defaultConfig: discoveryAddress: mesh-pilot.vehicle-platform.svc:15010 tracing: zipkin: address: zipkin.vehicle-platform.svc:9411 proxyMetadata: ISTIO_META_DNS_CAPTURE: "true" ISTIO_META_DNS_AUTO_ALLOCATE: "true" # Access logging accessLogFile: /dev/stdout accessLogEncoding: JSON # mTLS configuration enableAutoMtls: true # Trust domain trustDomain: vehicle.local # Service discovery defaultServiceExportTo: - "*" --- apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: vss-broker spec: hosts: - vss-broker.vehicle-platform.svc http: - match: - uri: prefix: /api/signals route: - destination: host: vss-broker.vehicle-platform.svc port: number: 8080 timeout: 5s retries: attempts: 3 perTryTimeout: 2s --- apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: vss-broker spec: host: vss-broker.vehicle-platform.svc trafficPolicy: loadBalancer: simple: ROUND_ROBIN connectionPool: tcp: maxConnections: 100 http: http1MaxPendingRequests: 50 http2MaxRequests: 100 outlierDetection: consecutiveErrors: 5 interval: 30s baseEjectionTime: 60s maxEjectionPercent: 50 --- apiVersion: security.istio.io/v1beta1 kind: PeerAuthentication metadata: name: default namespace: vehicle-platform spec: mtls: mode: STRICT --- apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: vss-access-control namespace: vehicle-platform spec: selector: matchLabels: app: vss-broker action: ALLOW rules: - from: - source: principals: ["cluster.local/ns/infotainment/sa/spotify"] to: - operation: methods: ["GET"] paths: ["/api/signals/Vehicle/Speed"]
python#!/usr/bin/env python3 """ Eclipse Kuksa.VAL integration for VSS data access. """ import asyncio from kuksa_client.grpc import VSSClient from kuksa_client.grpc.aio import VSSClientAsync async def main(): """Example Kuksa.VAL client.""" # Connect to Kuksa databroker async with VSSClientAsync('127.0.0.1', 55555) as client: print("[Kuksa] Connected to data broker") # Set signal value await client.set_current_values({ 'Vehicle.Speed': 65.5, 'Vehicle.Powertrain.Battery.StateOfCharge': 75.0 }) print("[Kuksa] Signals set") # Get signal value response = await client.get_current_values([ 'Vehicle.Speed', 'Vehicle.Powertrain.Battery.StateOfCharge' ]) for entry in response.entries: print(f"[Kuksa] {entry.path} = {entry.value.double}") # Subscribe to signal async for updates in client.subscribe_current_values([ 'Vehicle.Speed' ]): for update in updates.updates: print(f"[Kuksa] Update: {update.entry.path} = {update.entry.value.double}") if __name__ == "__main__": asyncio.run(main())
Other measured skills in the registry, with their headline benchmark lift.