- Add 100 blog posts covering AI, development, and tech topics - Add .env.example for environment configuration - Add accessibility and lighthouse audit scripts - Remove obsolete SEO reports and temporary files - Remove dev-dist build artifacts and backup files - Remove unused portrait images (moved/consolidated elsewhere) - Update contact form and component improvements Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
824 lines
23 KiB
Markdown
824 lines
23 KiB
Markdown
# Edge Computing für IoT
|
|
|
|
**Meta-Description:** Edge Computing für IoT-Anwendungen. Lokale Datenverarbeitung, ML Inference, Fog Computing und Edge-Cloud Architektur.
|
|
|
|
**Keywords:** Edge Computing, IoT, Fog Computing, ML Inference, Local Processing, Edge Gateway, TensorFlow Lite
|
|
|
|
---
|
|
|
|
## Einführung
|
|
|
|
**Edge Computing** bringt Rechenleistung dorthin, wo Daten entstehen. Für IoT bedeutet das: **niedrige Latenz, Offline-Fähigkeit, Datenschutz** und reduzierte Bandbreite durch lokale Verarbeitung direkt am Gerät oder Gateway.
|
|
|
|
---
|
|
|
|
## Edge Computing Architecture
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ EDGE COMPUTING ARCHITECTURE │
|
|
├─────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ Cloud Layer: │
|
|
│ ┌─────────────────────────────────────────────────────┐ │
|
|
│ │ Cloud Platform (AWS IoT / Azure IoT / GCP IoT) │ │
|
|
│ │ - Long-term Storage │ │
|
|
│ │ - Complex Analytics │ │
|
|
│ │ - ML Model Training │ │
|
|
│ │ - Global Dashboards │ │
|
|
│ └─────────────────────────────────────────────────────┘ │
|
|
│ ↑↓ Aggregated Data │
|
|
│ Edge Layer (Fog): │
|
|
│ ┌─────────────────────────────────────────────────────┐ │
|
|
│ │ Edge Gateway / Server │ │
|
|
│ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ │
|
|
│ │ │ Local DB │ │ ML Infer- │ │ Rules │ │ │
|
|
│ │ │ (InfluxDB)│ │ ence │ │ Engine │ │ │
|
|
│ │ └───────────┘ └───────────┘ └───────────┘ │ │
|
|
│ │ - Data Aggregation │ │
|
|
│ │ - Real-time Processing │ │
|
|
│ │ - Local Decisions │ │
|
|
│ └─────────────────────────────────────────────────────┘ │
|
|
│ ↑↓ Filtered Data │
|
|
│ Device Layer: │
|
|
│ ┌─────────────────────────────────────────────────────┐ │
|
|
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌───────┐ │ │
|
|
│ │ │ Sensor │ │ Camera │ │ Actuator│ │ PLC │ │ │
|
|
│ │ │ │ │ (CV) │ │ │ │ │ │ │
|
|
│ │ └─────────┘ └─────────┘ └─────────┘ └───────┘ │ │
|
|
│ │ - Data Collection │ │
|
|
│ │ - Simple Processing │ │
|
|
│ └─────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ Latency: Cloud ~100ms | Edge ~10ms | Device ~1ms │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Edge Gateway mit Node.js
|
|
|
|
```typescript
|
|
// edge-gateway/index.ts
|
|
import mqtt from 'mqtt';
|
|
import { InfluxDBClient } from './lib/influxdb';
|
|
import { RulesEngine } from './lib/rules-engine';
|
|
import { MLInference } from './lib/ml-inference';
|
|
|
|
interface EdgeConfig {
|
|
mqttBroker: string;
|
|
cloudEndpoint: string;
|
|
localDb: {
|
|
url: string;
|
|
token: string;
|
|
org: string;
|
|
bucket: string;
|
|
};
|
|
syncInterval: number; // Millisekunden
|
|
offlineBufferSize: number;
|
|
}
|
|
|
|
class EdgeGateway {
|
|
private localMqtt: mqtt.MqttClient;
|
|
private cloudMqtt: mqtt.MqttClient | null = null;
|
|
private influx: InfluxDBClient;
|
|
private rules: RulesEngine;
|
|
private ml: MLInference;
|
|
private offlineBuffer: any[] = [];
|
|
private isCloudConnected = false;
|
|
|
|
constructor(private config: EdgeConfig) {
|
|
this.influx = new InfluxDBClient(config.localDb);
|
|
this.rules = new RulesEngine();
|
|
this.ml = new MLInference();
|
|
}
|
|
|
|
async start(): Promise<void> {
|
|
// Local MQTT Broker verbinden
|
|
this.localMqtt = mqtt.connect(this.config.mqttBroker);
|
|
|
|
this.localMqtt.on('connect', () => {
|
|
console.log('Connected to local MQTT broker');
|
|
this.localMqtt.subscribe('sensors/#');
|
|
this.localMqtt.subscribe('devices/#');
|
|
});
|
|
|
|
this.localMqtt.on('message', (topic, payload) => {
|
|
this.handleLocalMessage(topic, payload.toString());
|
|
});
|
|
|
|
// Cloud Connection (mit Retry)
|
|
this.connectToCloud();
|
|
|
|
// Periodische Cloud-Synchronisation
|
|
setInterval(() => this.syncToCloud(), this.config.syncInterval);
|
|
|
|
// ML Models laden
|
|
await this.ml.loadModels();
|
|
|
|
console.log('Edge Gateway started');
|
|
}
|
|
|
|
private async handleLocalMessage(topic: string, payload: string): Promise<void> {
|
|
try {
|
|
const data = JSON.parse(payload);
|
|
const timestamp = new Date();
|
|
|
|
// 1. Lokale Speicherung
|
|
await this.storeLocally(topic, data, timestamp);
|
|
|
|
// 2. Echtzeit-Verarbeitung
|
|
const processed = await this.processData(topic, data);
|
|
|
|
// 3. Rules Engine
|
|
const actions = this.rules.evaluate(topic, processed);
|
|
await this.executeActions(actions);
|
|
|
|
// 4. ML Inference (wenn relevant)
|
|
if (this.shouldRunInference(topic, data)) {
|
|
const prediction = await this.ml.predict(data);
|
|
await this.handlePrediction(topic, prediction);
|
|
}
|
|
|
|
// 5. Cloud-Queue (aggregierte Daten)
|
|
this.queueForCloud(topic, processed, timestamp);
|
|
|
|
} catch (error) {
|
|
console.error('Error processing message:', error);
|
|
}
|
|
}
|
|
|
|
private async storeLocally(
|
|
topic: string,
|
|
data: any,
|
|
timestamp: Date
|
|
): Promise<void> {
|
|
const measurement = topic.split('/')[0];
|
|
const deviceId = topic.split('/')[1];
|
|
|
|
await this.influx.writePoint({
|
|
measurement,
|
|
tags: { device_id: deviceId },
|
|
fields: data,
|
|
timestamp
|
|
});
|
|
}
|
|
|
|
private async processData(topic: string, data: any): Promise<any> {
|
|
// Daten normalisieren, filtern, aggregieren
|
|
const processed = { ...data };
|
|
|
|
// Outlier Detection
|
|
if (data.temperature !== undefined) {
|
|
if (data.temperature < -50 || data.temperature > 100) {
|
|
processed.temperature_valid = false;
|
|
processed.temperature_original = data.temperature;
|
|
delete processed.temperature;
|
|
}
|
|
}
|
|
|
|
// Unit Conversion (wenn nötig)
|
|
// ...
|
|
|
|
return processed;
|
|
}
|
|
|
|
private queueForCloud(topic: string, data: any, timestamp: Date): void {
|
|
// Nur relevante Daten für Cloud
|
|
const cloudData = {
|
|
topic,
|
|
data: this.aggregateForCloud(data),
|
|
timestamp: timestamp.toISOString(),
|
|
gatewayId: process.env.GATEWAY_ID
|
|
};
|
|
|
|
if (this.isCloudConnected) {
|
|
this.sendToCloud(cloudData);
|
|
} else {
|
|
// Offline Buffer
|
|
this.offlineBuffer.push(cloudData);
|
|
|
|
// Buffer Limit
|
|
if (this.offlineBuffer.length > this.config.offlineBufferSize) {
|
|
this.offlineBuffer.shift(); // Älteste Daten verwerfen
|
|
}
|
|
}
|
|
}
|
|
|
|
private aggregateForCloud(data: any): any {
|
|
// Nur wichtige Felder für Cloud
|
|
const { temperature, humidity, battery, state } = data;
|
|
return { temperature, humidity, battery, state };
|
|
}
|
|
|
|
private async syncToCloud(): Promise<void> {
|
|
if (!this.isCloudConnected || this.offlineBuffer.length === 0) {
|
|
return;
|
|
}
|
|
|
|
console.log(`Syncing ${this.offlineBuffer.length} messages to cloud`);
|
|
|
|
// Batch Upload
|
|
const batch = this.offlineBuffer.splice(0, 100);
|
|
|
|
try {
|
|
await this.sendToCloud({
|
|
type: 'batch',
|
|
messages: batch
|
|
});
|
|
} catch (error) {
|
|
// Bei Fehler: zurück in Buffer
|
|
this.offlineBuffer.unshift(...batch);
|
|
console.error('Cloud sync failed:', error);
|
|
}
|
|
}
|
|
|
|
private sendToCloud(data: any): void {
|
|
this.cloudMqtt?.publish(
|
|
`gateways/${process.env.GATEWAY_ID}/data`,
|
|
JSON.stringify(data)
|
|
);
|
|
}
|
|
|
|
private async executeActions(actions: any[]): Promise<void> {
|
|
for (const action of actions) {
|
|
console.log('Executing action:', action);
|
|
|
|
switch (action.type) {
|
|
case 'publish':
|
|
this.localMqtt.publish(action.topic, JSON.stringify(action.payload));
|
|
break;
|
|
|
|
case 'alert':
|
|
await this.sendAlert(action.message, action.severity);
|
|
break;
|
|
|
|
case 'device_command':
|
|
this.localMqtt.publish(
|
|
`devices/${action.deviceId}/command`,
|
|
JSON.stringify(action.command)
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private connectToCloud(): void {
|
|
this.cloudMqtt = mqtt.connect(this.config.cloudEndpoint, {
|
|
clientId: `gateway-${process.env.GATEWAY_ID}`,
|
|
username: process.env.CLOUD_USER,
|
|
password: process.env.CLOUD_PASS,
|
|
reconnectPeriod: 10000
|
|
});
|
|
|
|
this.cloudMqtt.on('connect', () => {
|
|
console.log('Connected to cloud');
|
|
this.isCloudConnected = true;
|
|
|
|
// Subscribe to cloud commands
|
|
this.cloudMqtt?.subscribe(`gateways/${process.env.GATEWAY_ID}/command`);
|
|
});
|
|
|
|
this.cloudMqtt.on('close', () => {
|
|
console.log('Cloud connection closed');
|
|
this.isCloudConnected = false;
|
|
});
|
|
|
|
this.cloudMqtt.on('message', (topic, payload) => {
|
|
this.handleCloudCommand(JSON.parse(payload.toString()));
|
|
});
|
|
}
|
|
|
|
private handleCloudCommand(command: any): void {
|
|
console.log('Cloud command received:', command);
|
|
|
|
switch (command.type) {
|
|
case 'update_config':
|
|
this.updateConfig(command.config);
|
|
break;
|
|
|
|
case 'update_rules':
|
|
this.rules.updateRules(command.rules);
|
|
break;
|
|
|
|
case 'update_model':
|
|
this.ml.updateModel(command.modelId, command.modelUrl);
|
|
break;
|
|
|
|
case 'device_command':
|
|
this.localMqtt.publish(
|
|
`devices/${command.deviceId}/command`,
|
|
JSON.stringify(command.payload)
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## ML Inference am Edge
|
|
|
|
```typescript
|
|
// lib/ml-inference.ts
|
|
import * as tf from '@tensorflow/tfjs-node';
|
|
|
|
interface Model {
|
|
id: string;
|
|
model: tf.LayersModel;
|
|
inputShape: number[];
|
|
labels: string[];
|
|
}
|
|
|
|
class MLInference {
|
|
private models: Map<string, Model> = new Map();
|
|
|
|
async loadModels(): Promise<void> {
|
|
// Anomaly Detection Model
|
|
const anomalyModel = await tf.loadLayersModel(
|
|
'file://./models/anomaly/model.json'
|
|
);
|
|
this.models.set('anomaly', {
|
|
id: 'anomaly',
|
|
model: anomalyModel,
|
|
inputShape: [1, 10], // 10 Features
|
|
labels: ['normal', 'anomaly']
|
|
});
|
|
|
|
// Predictive Maintenance Model
|
|
const maintenanceModel = await tf.loadLayersModel(
|
|
'file://./models/maintenance/model.json'
|
|
);
|
|
this.models.set('maintenance', {
|
|
id: 'maintenance',
|
|
model: maintenanceModel,
|
|
inputShape: [1, 24], // 24 Stunden Historie
|
|
labels: ['ok', 'warning', 'critical']
|
|
});
|
|
|
|
console.log(`Loaded ${this.models.size} ML models`);
|
|
}
|
|
|
|
async predict(
|
|
modelId: string,
|
|
input: number[]
|
|
): Promise<{ label: string; confidence: number }> {
|
|
const modelInfo = this.models.get(modelId);
|
|
if (!modelInfo) {
|
|
throw new Error(`Model ${modelId} not found`);
|
|
}
|
|
|
|
// Input Tensor erstellen
|
|
const inputTensor = tf.tensor2d([input], modelInfo.inputShape as [number, number]);
|
|
|
|
// Inference
|
|
const prediction = modelInfo.model.predict(inputTensor) as tf.Tensor;
|
|
const probabilities = await prediction.data();
|
|
|
|
// Cleanup
|
|
inputTensor.dispose();
|
|
prediction.dispose();
|
|
|
|
// Beste Prediction finden
|
|
const maxIndex = probabilities.indexOf(Math.max(...Array.from(probabilities)));
|
|
|
|
return {
|
|
label: modelInfo.labels[maxIndex],
|
|
confidence: probabilities[maxIndex]
|
|
};
|
|
}
|
|
|
|
async detectAnomaly(
|
|
sensorData: {
|
|
temperature: number;
|
|
humidity: number;
|
|
pressure: number;
|
|
vibration: number;
|
|
}
|
|
): Promise<{ isAnomaly: boolean; confidence: number }> {
|
|
// Feature Engineering
|
|
const features = [
|
|
sensorData.temperature,
|
|
sensorData.humidity,
|
|
sensorData.pressure,
|
|
sensorData.vibration,
|
|
// Normalisierte Werte
|
|
(sensorData.temperature - 20) / 30,
|
|
(sensorData.humidity - 50) / 50,
|
|
// Interaktionen
|
|
sensorData.temperature * sensorData.humidity / 1000,
|
|
// Placeholder für historische Features
|
|
0, 0, 0
|
|
];
|
|
|
|
const result = await this.predict('anomaly', features);
|
|
|
|
return {
|
|
isAnomaly: result.label === 'anomaly',
|
|
confidence: result.confidence
|
|
};
|
|
}
|
|
|
|
async updateModel(modelId: string, modelUrl: string): Promise<void> {
|
|
console.log(`Updating model ${modelId} from ${modelUrl}`);
|
|
|
|
try {
|
|
const newModel = await tf.loadLayersModel(modelUrl);
|
|
const existing = this.models.get(modelId);
|
|
|
|
if (existing) {
|
|
existing.model.dispose();
|
|
existing.model = newModel;
|
|
}
|
|
|
|
console.log(`Model ${modelId} updated successfully`);
|
|
} catch (error) {
|
|
console.error(`Failed to update model ${modelId}:`, error);
|
|
}
|
|
}
|
|
}
|
|
|
|
export { MLInference };
|
|
```
|
|
|
|
---
|
|
|
|
## Rules Engine
|
|
|
|
```typescript
|
|
// lib/rules-engine.ts
|
|
|
|
interface Rule {
|
|
id: string;
|
|
name: string;
|
|
condition: RuleCondition;
|
|
actions: RuleAction[];
|
|
enabled: boolean;
|
|
priority: number;
|
|
}
|
|
|
|
interface RuleCondition {
|
|
type: 'simple' | 'compound';
|
|
field?: string;
|
|
operator?: '>' | '<' | '==' | '!=' | '>=' | '<=';
|
|
value?: any;
|
|
logic?: 'AND' | 'OR';
|
|
conditions?: RuleCondition[];
|
|
}
|
|
|
|
interface RuleAction {
|
|
type: 'publish' | 'alert' | 'device_command' | 'log';
|
|
[key: string]: any;
|
|
}
|
|
|
|
class RulesEngine {
|
|
private rules: Rule[] = [];
|
|
|
|
constructor() {
|
|
this.loadDefaultRules();
|
|
}
|
|
|
|
private loadDefaultRules(): void {
|
|
this.rules = [
|
|
// High Temperature Alert
|
|
{
|
|
id: 'high-temp-alert',
|
|
name: 'High Temperature Alert',
|
|
condition: {
|
|
type: 'simple',
|
|
field: 'temperature',
|
|
operator: '>',
|
|
value: 35
|
|
},
|
|
actions: [
|
|
{
|
|
type: 'alert',
|
|
message: 'High temperature detected',
|
|
severity: 'warning'
|
|
},
|
|
{
|
|
type: 'publish',
|
|
topic: 'alerts/temperature',
|
|
payload: { alert: 'high_temperature' }
|
|
}
|
|
],
|
|
enabled: true,
|
|
priority: 1
|
|
},
|
|
|
|
// Low Battery Warning
|
|
{
|
|
id: 'low-battery',
|
|
name: 'Low Battery Warning',
|
|
condition: {
|
|
type: 'simple',
|
|
field: 'battery',
|
|
operator: '<',
|
|
value: 20
|
|
},
|
|
actions: [
|
|
{
|
|
type: 'alert',
|
|
message: 'Device battery low',
|
|
severity: 'info'
|
|
}
|
|
],
|
|
enabled: true,
|
|
priority: 2
|
|
},
|
|
|
|
// Motion → Light On
|
|
{
|
|
id: 'motion-light',
|
|
name: 'Motion Activated Light',
|
|
condition: {
|
|
type: 'compound',
|
|
logic: 'AND',
|
|
conditions: [
|
|
{ type: 'simple', field: 'occupancy', operator: '==', value: true },
|
|
{ type: 'simple', field: 'ambient_light', operator: '<', value: 100 }
|
|
]
|
|
},
|
|
actions: [
|
|
{
|
|
type: 'device_command',
|
|
deviceId: 'hallway_light',
|
|
command: { state: 'ON', brightness: 80 }
|
|
}
|
|
],
|
|
enabled: true,
|
|
priority: 1
|
|
}
|
|
];
|
|
}
|
|
|
|
evaluate(topic: string, data: any): RuleAction[] {
|
|
const actions: RuleAction[] = [];
|
|
|
|
// Regeln nach Priorität sortieren
|
|
const sortedRules = [...this.rules]
|
|
.filter(r => r.enabled)
|
|
.sort((a, b) => a.priority - b.priority);
|
|
|
|
for (const rule of sortedRules) {
|
|
if (this.evaluateCondition(rule.condition, data)) {
|
|
console.log(`Rule triggered: ${rule.name}`);
|
|
actions.push(...rule.actions);
|
|
}
|
|
}
|
|
|
|
return actions;
|
|
}
|
|
|
|
private evaluateCondition(condition: RuleCondition, data: any): boolean {
|
|
if (condition.type === 'simple') {
|
|
return this.evaluateSimple(condition, data);
|
|
}
|
|
|
|
if (condition.type === 'compound' && condition.conditions) {
|
|
const results = condition.conditions.map(c =>
|
|
this.evaluateCondition(c, data)
|
|
);
|
|
|
|
return condition.logic === 'AND'
|
|
? results.every(r => r)
|
|
: results.some(r => r);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private evaluateSimple(condition: RuleCondition, data: any): boolean {
|
|
const value = data[condition.field!];
|
|
|
|
if (value === undefined) return false;
|
|
|
|
switch (condition.operator) {
|
|
case '>': return value > condition.value;
|
|
case '<': return value < condition.value;
|
|
case '>=': return value >= condition.value;
|
|
case '<=': return value <= condition.value;
|
|
case '==': return value === condition.value;
|
|
case '!=': return value !== condition.value;
|
|
default: return false;
|
|
}
|
|
}
|
|
|
|
updateRules(newRules: Rule[]): void {
|
|
this.rules = newRules;
|
|
console.log(`Updated ${newRules.length} rules`);
|
|
}
|
|
|
|
addRule(rule: Rule): void {
|
|
this.rules.push(rule);
|
|
}
|
|
|
|
removeRule(ruleId: string): void {
|
|
this.rules = this.rules.filter(r => r.id !== ruleId);
|
|
}
|
|
|
|
getRules(): Rule[] {
|
|
return this.rules;
|
|
}
|
|
}
|
|
|
|
export { RulesEngine };
|
|
```
|
|
|
|
---
|
|
|
|
## Edge-Cloud Synchronisation
|
|
|
|
```typescript
|
|
// lib/edge-cloud-sync.ts
|
|
|
|
interface SyncConfig {
|
|
syncInterval: number;
|
|
batchSize: number;
|
|
retentionLocal: number; // Tage
|
|
compressionEnabled: boolean;
|
|
}
|
|
|
|
class EdgeCloudSync {
|
|
private config: SyncConfig;
|
|
private pendingSync: any[] = [];
|
|
private lastSyncTime: Date | null = null;
|
|
|
|
constructor(config: SyncConfig) {
|
|
this.config = config;
|
|
}
|
|
|
|
// Daten für Cloud-Sync vorbereiten
|
|
prepareForSync(data: any[]): any {
|
|
// Aggregation
|
|
const aggregated = this.aggregate(data);
|
|
|
|
// Kompression (optional)
|
|
if (this.config.compressionEnabled) {
|
|
return this.compress(aggregated);
|
|
}
|
|
|
|
return aggregated;
|
|
}
|
|
|
|
private aggregate(data: any[]): any {
|
|
// Nach Device gruppieren
|
|
const byDevice = new Map<string, any[]>();
|
|
|
|
data.forEach(item => {
|
|
const deviceId = item.device_id;
|
|
if (!byDevice.has(deviceId)) {
|
|
byDevice.set(deviceId, []);
|
|
}
|
|
byDevice.get(deviceId)!.push(item);
|
|
});
|
|
|
|
// Aggregierte Statistiken pro Device
|
|
const result: any[] = [];
|
|
|
|
byDevice.forEach((items, deviceId) => {
|
|
const temps = items
|
|
.map(i => i.temperature)
|
|
.filter(t => t !== undefined);
|
|
|
|
const humidities = items
|
|
.map(i => i.humidity)
|
|
.filter(h => h !== undefined);
|
|
|
|
result.push({
|
|
device_id: deviceId,
|
|
period_start: items[0].timestamp,
|
|
period_end: items[items.length - 1].timestamp,
|
|
sample_count: items.length,
|
|
temperature: temps.length > 0 ? {
|
|
min: Math.min(...temps),
|
|
max: Math.max(...temps),
|
|
avg: temps.reduce((a, b) => a + b, 0) / temps.length
|
|
} : null,
|
|
humidity: humidities.length > 0 ? {
|
|
min: Math.min(...humidities),
|
|
max: Math.max(...humidities),
|
|
avg: humidities.reduce((a, b) => a + b, 0) / humidities.length
|
|
} : null
|
|
});
|
|
});
|
|
|
|
return result;
|
|
}
|
|
|
|
private compress(data: any): Buffer {
|
|
const zlib = require('zlib');
|
|
const json = JSON.stringify(data);
|
|
return zlib.gzipSync(json);
|
|
}
|
|
|
|
// Conflict Resolution bei Cloud-Sync
|
|
resolveConflict(localData: any, cloudData: any): any {
|
|
// Last-Write-Wins Strategie
|
|
const localTime = new Date(localData.updated_at);
|
|
const cloudTime = new Date(cloudData.updated_at);
|
|
|
|
if (localTime > cloudTime) {
|
|
return { ...cloudData, ...localData, conflict_resolved: true };
|
|
}
|
|
return { ...localData, ...cloudData, conflict_resolved: true };
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Edge Deployment
|
|
|
|
```yaml
|
|
# docker-compose.edge.yml
|
|
version: '3.8'
|
|
services:
|
|
edge-gateway:
|
|
build: ./edge-gateway
|
|
restart: always
|
|
environment:
|
|
- GATEWAY_ID=${GATEWAY_ID}
|
|
- MQTT_BROKER=mqtt://mosquitto:1883
|
|
- CLOUD_ENDPOINT=${CLOUD_MQTT_URL}
|
|
- INFLUX_URL=http://influxdb:8086
|
|
volumes:
|
|
- ./models:/app/models
|
|
- ./config:/app/config
|
|
depends_on:
|
|
- mosquitto
|
|
- influxdb
|
|
|
|
mosquitto:
|
|
image: eclipse-mosquitto:2
|
|
ports:
|
|
- "1883:1883"
|
|
volumes:
|
|
- mosquitto-data:/mosquitto/data
|
|
|
|
influxdb:
|
|
image: influxdb:2.7
|
|
ports:
|
|
- "8086:8086"
|
|
volumes:
|
|
- influxdb-data:/var/lib/influxdb2
|
|
environment:
|
|
- DOCKER_INFLUXDB_INIT_MODE=setup
|
|
- DOCKER_INFLUXDB_INIT_USERNAME=admin
|
|
- DOCKER_INFLUXDB_INIT_PASSWORD=edgepassword
|
|
- DOCKER_INFLUXDB_INIT_ORG=edge
|
|
- DOCKER_INFLUXDB_INIT_BUCKET=sensors
|
|
|
|
volumes:
|
|
mosquitto-data:
|
|
influxdb-data:
|
|
```
|
|
|
|
---
|
|
|
|
## Edge vs Cloud Comparison
|
|
|
|
| Aspect | Edge Computing | Cloud Computing |
|
|
|--------|---------------|-----------------|
|
|
| **Latency** | 1-10 ms | 50-200 ms |
|
|
| **Bandwidth** | Minimal (local) | High (all data) |
|
|
| **Offline** | Fully operational | Limited |
|
|
| **Privacy** | Data stays local | Data in cloud |
|
|
| **Compute** | Limited | Unlimited |
|
|
| **Storage** | Limited | Unlimited |
|
|
| **Cost** | Hardware upfront | Pay-per-use |
|
|
| **ML Training** | Not practical | Ideal |
|
|
| **ML Inference** | Real-time | Batch |
|
|
|
|
---
|
|
|
|
## Fazit
|
|
|
|
Edge Computing für IoT bietet:
|
|
|
|
1. **Low Latency**: Lokale Entscheidungen in Millisekunden
|
|
2. **Offline-Fähigkeit**: Funktioniert ohne Cloud
|
|
3. **Datenschutz**: Sensible Daten bleiben lokal
|
|
4. **Bandbreite**: Nur aggregierte Daten zur Cloud
|
|
|
|
Die optimale Architektur kombiniert Edge und Cloud.
|
|
|
|
---
|
|
|
|
## Bildprompts
|
|
|
|
1. "Edge computing diagram with local processing near sensors"
|
|
2. "IoT gateway device processing data streams locally"
|
|
3. "Fog computing layers between devices and cloud"
|
|
|
|
---
|
|
|
|
## Quellen
|
|
|
|
- [AWS IoT Greengrass](https://aws.amazon.com/greengrass/)
|
|
- [Azure IoT Edge](https://azure.microsoft.com/en-us/products/iot-edge)
|
|
- [TensorFlow Lite for Edge](https://www.tensorflow.org/lite)
|
|
- [InfluxDB Edge Replication](https://www.influxdata.com/blog/simplify-industrial-iot-use-influxdb-edge-replication/)
|