实践项目
# 实践项目
# 1. 项目概述
本章将通过四个典型的物联网应用场景,展示完整的物联网系统设计和实现过程。每个项目都包含需求分析、架构设计、技术选型、核心代码实现和部署指南。
# 2. 学习目标
- 掌握不同领域的物联网应用特点
- 学会根据业务需求设计技术架构
- 理解端到端的系统实现过程
- 积累实际项目开发经验
- 了解不同场景下的技术选型策略
# 3. 项目架构对比
graph TB
subgraph "智能家居系统"
A1["家庭网关"] --> A2["智能设备"]
A1 --> A3["移动应用"]
A2 --> A4["传感器"]
A2 --> A5["执行器"]
end
subgraph "工业监控系统"
B1["工业网关"] --> B2["生产设备"]
B1 --> B3["监控中心"]
B2 --> B4["传感器阵列"]
B2 --> B5["控制系统"]
end
subgraph "车联网系统"
C1["车载终端"] --> C2["车辆传感器"]
C1 --> C3["云端平台"]
C2 --> C4["行驶数据"]
C2 --> C5["环境感知"]
end
subgraph "农业物联网"
D1["农业网关"] --> D2["环境监测"]
D1 --> D3["智能控制"]
D2 --> D4["土壤传感器"]
D2 --> D5["气象站"]
end
# 项目一:智能家居系统
# 1. 项目需求分析
# 1.1 功能需求
- 环境监控: 温度、湿度、空气质量、光照强度
- 安全防护: 门窗监控、人体感应、烟雾报警
- 智能控制: 灯光、空调、窗帘、家电控制
- 场景联动: 回家模式、离家模式、睡眠模式
- 远程管理: 手机APP远程监控和控制
# 1.2 非功能需求
- 可靠性: 99.9%系统可用性
- 实时性: 控制指令响应时间<500ms
- 安全性: 数据加密传输,用户认证
- 扩展性: 支持新设备接入
- 易用性: 简洁直观的用户界面
# 2. 系统架构设计
graph TB
subgraph "智能家居系统架构"
subgraph "设备层"
A1["温湿度传感器"]
A2["门窗传感器"]
A3["人体感应器"]
A4["智能开关"]
A5["智能插座"]
A6["智能摄像头"]
end
subgraph "网关层"
B1["家庭网关"]
B2["协议转换"]
B3["本地存储"]
B4["边缘计算"]
end
subgraph "网络层"
C1["WiFi"]
C2["ZigBee"]
C3["蓝牙"]
C4["4G/5G"]
end
subgraph "云端平台"
D1["设备管理"]
D2["数据处理"]
D3["规则引擎"]
D4["用户管理"]
end
subgraph "应用层"
E1["移动APP"]
E2["Web控制台"]
E3["语音助手"]
E4["第三方集成"]
end
end
A1 --> B1
A2 --> B1
A3 --> B1
A4 --> B1
A5 --> B1
A6 --> B1
B1 --> C1
B1 --> C2
B1 --> C3
C4 --> D1
D1 --> D2
D2 --> D3
D3 --> D4
D4 --> E1
D4 --> E2
D4 --> E3
D4 --> E4
# 3. 技术选型
# 3.1 硬件平台
# 技术选型配置
hardware:
gateway:
platform: "Raspberry Pi 4"
os: "Ubuntu 20.04 LTS"
storage: "32GB SD Card + 1TB SSD"
connectivity: ["WiFi", "Ethernet", "ZigBee", "Bluetooth"]
sensors:
temperature_humidity: "DHT22"
door_window: "Reed Switch"
motion: "PIR Sensor"
air_quality: "MQ-135"
light: "BH1750"
actuators:
smart_switch: "ESP32 + Relay"
smart_socket: "ESP8266 + Relay"
led_strip: "WS2812B"
servo_motor: "SG90"
software:
gateway:
runtime: "Node.js 16+"
database: "SQLite + InfluxDB"
message_queue: "MQTT (Mosquitto)"
web_framework: "Express.js"
cloud:
backend: "Spring Boot + Spring Cloud"
database: "MySQL + Redis + InfluxDB"
message_queue: "Apache Kafka"
container: "Docker + Kubernetes"
frontend:
mobile: "React Native"
web: "React + Ant Design"
charts: "ECharts"
# 4. 核心代码实现
# 4.1 设备数据采集
// 示例:传感器数据采集服务
const mqtt = require('mqtt');
const { InfluxDB, Point } = require('@influxdata/influxdb-client');
const config = require('./config');
class SensorDataCollector {
constructor() {
this.mqttClient = mqtt.connect(config.mqtt.broker);
this.influxDB = new InfluxDB({
url: config.influxdb.url,
token: config.influxdb.token
});
this.writeApi = this.influxDB.getWriteApi(
config.influxdb.org,
config.influxdb.bucket
);
this.setupMqttHandlers();
}
setupMqttHandlers() {
this.mqttClient.on('connect', () => {
console.log('Connected to MQTT broker');
// 订阅所有传感器主题
this.mqttClient.subscribe('sensors/+/+', (err) => {
if (err) {
console.error('Failed to subscribe to sensors:', err);
} else {
console.log('Subscribed to sensor topics');
}
});
});
this.mqttClient.on('message', (topic, message) => {
this.handleSensorData(topic, message);
});
this.mqttClient.on('error', (error) => {
console.error('MQTT error:', error);
});
}
handleSensorData(topic, message) {
try {
const topicParts = topic.split('/');
const deviceId = topicParts[1];
const sensorType = topicParts[2];
const data = JSON.parse(message.toString());
// 数据验证
if (!this.validateSensorData(sensorType, data)) {
console.warn(`Invalid sensor data for ${deviceId}:${sensorType}`, data);
return;
}
// 存储到InfluxDB
this.storeSensorData(deviceId, sensorType, data);
// 实时数据处理
this.processRealTimeData(deviceId, sensorType, data);
// 规则引擎处理
this.triggerRuleEngine(deviceId, sensorType, data);
} catch (error) {
console.error('Error handling sensor data:', error);
}
}
validateSensorData(sensorType, data) {
const validators = {
temperature: (d) => typeof d.value === 'number' && d.value >= -40 && d.value <= 80,
humidity: (d) => typeof d.value === 'number' && d.value >= 0 && d.value <= 100,
motion: (d) => typeof d.detected === 'boolean',
door: (d) => typeof d.open === 'boolean',
air_quality: (d) => typeof d.ppm === 'number' && d.ppm >= 0,
light: (d) => typeof d.lux === 'number' && d.lux >= 0
};
const validator = validators[sensorType];
return validator ? validator(data) : false;
}
storeSensorData(deviceId, sensorType, data) {
const point = new Point('sensor_data')
.tag('device_id', deviceId)
.tag('sensor_type', sensorType)
.timestamp(new Date(data.timestamp || Date.now()));
// 根据传感器类型添加字段
switch (sensorType) {
case 'temperature':
point.floatField('temperature', data.value);
break;
case 'humidity':
point.floatField('humidity', data.value);
break;
case 'motion':
point.booleanField('detected', data.detected);
break;
case 'door':
point.booleanField('open', data.open);
break;
case 'air_quality':
point.floatField('ppm', data.ppm);
break;
case 'light':
point.floatField('lux', data.lux);
break;
}
this.writeApi.writePoint(point);
}
processRealTimeData(deviceId, sensorType, data) {
// 发送实时数据到WebSocket客户端
const realTimeData = {
deviceId,
sensorType,
data,
timestamp: Date.now()
};
// 通过WebSocket广播
global.wsServer?.broadcast('sensor_data', realTimeData);
// 更新设备状态缓存
this.updateDeviceStatusCache(deviceId, sensorType, data);
}
updateDeviceStatusCache(deviceId, sensorType, data) {
const redis = require('./redis');
const cacheKey = `device:${deviceId}:${sensorType}`;
const cacheData = {
...data,
timestamp: Date.now()
};
redis.setex(cacheKey, 3600, JSON.stringify(cacheData)); // 缓存1小时
}
triggerRuleEngine(deviceId, sensorType, data) {
const ruleEngine = require('./ruleEngine');
ruleEngine.evaluate({
deviceId,
sensorType,
data,
timestamp: Date.now()
});
}
async getHistoricalData(deviceId, sensorType, startTime, endTime) {
const queryApi = this.influxDB.getQueryApi(config.influxdb.org);
const query = `
from(bucket: "${config.influxdb.bucket}")
|> range(start: ${startTime}, stop: ${endTime})
|> filter(fn: (r) => r._measurement == "sensor_data")
|> filter(fn: (r) => r.device_id == "${deviceId}")
|> filter(fn: (r) => r.sensor_type == "${sensorType}")
|> sort(columns: ["_time"])
`;
const result = [];
return new Promise((resolve, reject) => {
queryApi.queryRows(query, {
next(row, tableMeta) {
const o = tableMeta.toObject(row);
result.push({
timestamp: o._time,
value: o._value,
field: o._field
});
},
error(error) {
reject(error);
},
complete() {
resolve(result);
}
});
});
}
close() {
this.writeApi.close();
this.mqttClient.end();
}
}
module.exports = SensorDataCollector;
# 4.2 智能控制服务
// 示例:智能控制服务
class SmartControlService {
constructor(mqttClient, ruleEngine) {
this.mqttClient = mqttClient;
this.ruleEngine = ruleEngine;
this.deviceStates = new Map();
this.scenes = new Map();
this.initializeScenes();
}
initializeScenes() {
// 回家模式
this.scenes.set('home', {
name: '回家模式',
actions: [
{ deviceId: 'living_room_light', action: 'turn_on', brightness: 80 },
{ deviceId: 'entrance_light', action: 'turn_on', brightness: 100 },
{ deviceId: 'air_conditioner', action: 'set_temperature', temperature: 24 },
{ deviceId: 'curtains', action: 'open', position: 50 }
]
});
// 离家模式
this.scenes.set('away', {
name: '离家模式',
actions: [
{ deviceId: 'all_lights', action: 'turn_off' },
{ deviceId: 'air_conditioner', action: 'turn_off' },
{ deviceId: 'tv', action: 'turn_off' },
{ deviceId: 'security_system', action: 'arm' }
]
});
// 睡眠模式
this.scenes.set('sleep', {
name: '睡眠模式',
actions: [
{ deviceId: 'bedroom_light', action: 'turn_off' },
{ deviceId: 'living_room_light', action: 'turn_off' },
{ deviceId: 'tv', action: 'turn_off' },
{ deviceId: 'air_conditioner', action: 'set_temperature', temperature: 22 },
{ deviceId: 'curtains', action: 'close' }
]
});
// 观影模式
this.scenes.set('movie', {
name: '观影模式',
actions: [
{ deviceId: 'living_room_light', action: 'dim', brightness: 20 },
{ deviceId: 'tv', action: 'turn_on' },
{ deviceId: 'sound_system', action: 'turn_on', volume: 60 },
{ deviceId: 'curtains', action: 'close' }
]
});
}
// 执行设备控制命令
async executeCommand(deviceId, command, parameters = {}) {
try {
const commandData = {
deviceId,
command,
parameters,
timestamp: Date.now(),
requestId: this.generateRequestId()
};
// 发送MQTT命令
const topic = `commands/${deviceId}`;
this.mqttClient.publish(topic, JSON.stringify(commandData));
// 记录命令历史
await this.logCommand(commandData);
// 更新设备状态预期值
this.updateExpectedState(deviceId, command, parameters);
return {
success: true,
requestId: commandData.requestId,
message: 'Command sent successfully'
};
} catch (error) {
console.error('Failed to execute command:', error);
return {
success: false,
error: error.message
};
}
}
// 执行场景
async executeScene(sceneId, userId) {
const scene = this.scenes.get(sceneId);
if (!scene) {
throw new Error(`Scene ${sceneId} not found`);
}
console.log(`Executing scene: ${scene.name}`);
const results = [];
for (const action of scene.actions) {
try {
const result = await this.executeCommand(
action.deviceId,
action.action,
action
);
results.push({ ...action, result });
// 添加延迟以避免设备过载
await this.delay(200);
} catch (error) {
console.error(`Failed to execute action for ${action.deviceId}:`, error);
results.push({ ...action, error: error.message });
}
}
// 记录场景执行历史
await this.logSceneExecution(sceneId, userId, results);
return {
sceneId,
sceneName: scene.name,
results,
timestamp: Date.now()
};
}
// 智能联动规则
setupAutomationRules() {
// 人体感应自动开灯
this.ruleEngine.addRule({
id: 'motion_light_on',
name: '人体感应开灯',
condition: (data) => {
return data.sensorType === 'motion' &&
data.data.detected === true &&
this.isNightTime();
},
action: async (data) => {
const lightId = this.getLightForMotionSensor(data.deviceId);
if (lightId) {
await this.executeCommand(lightId, 'turn_on', { brightness: 60 });
}
}
});
// 门窗开启时关闭空调
this.ruleEngine.addRule({
id: 'window_ac_off',
name: '开窗关空调',
condition: (data) => {
return data.sensorType === 'door' &&
data.data.open === true &&
this.isAirConditionerOn();
},
action: async (data) => {
await this.executeCommand('air_conditioner', 'turn_off');
// 发送通知
this.sendNotification('空调已自动关闭,因为检测到门窗开启');
}
});
// 温度过高自动开启空调
this.ruleEngine.addRule({
id: 'high_temp_ac_on',
name: '高温开空调',
condition: (data) => {
return data.sensorType === 'temperature' &&
data.data.value > 28 &&
!this.isAirConditionerOn() &&
this.isHomeOccupied();
},
action: async (data) => {
await this.executeCommand('air_conditioner', 'turn_on');
await this.executeCommand('air_conditioner', 'set_temperature', { temperature: 24 });
this.sendNotification('空调已自动开启,当前温度过高');
}
});
// 空气质量差时开启净化器
this.ruleEngine.addRule({
id: 'poor_air_purifier_on',
name: '空气质量差开净化器',
condition: (data) => {
return data.sensorType === 'air_quality' &&
data.data.ppm > 500;
},
action: async (data) => {
await this.executeCommand('air_purifier', 'turn_on', { mode: 'auto' });
this.sendNotification('空气净化器已自动开启,当前空气质量较差');
}
});
}
// 设备状态管理
updateDeviceState(deviceId, state) {
const currentState = this.deviceStates.get(deviceId) || {};
const newState = { ...currentState, ...state, lastUpdate: Date.now() };
this.deviceStates.set(deviceId, newState);
// 广播状态更新
global.wsServer?.broadcast('device_state', {
deviceId,
state: newState
});
}
getDeviceState(deviceId) {
return this.deviceStates.get(deviceId) || {};
}
// 辅助方法
generateRequestId() {
return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
updateExpectedState(deviceId, command, parameters) {
const currentState = this.getDeviceState(deviceId);
const expectedState = { ...currentState };
switch (command) {
case 'turn_on':
expectedState.power = true;
if (parameters.brightness) {
expectedState.brightness = parameters.brightness;
}
break;
case 'turn_off':
expectedState.power = false;
break;
case 'set_temperature':
expectedState.targetTemperature = parameters.temperature;
break;
case 'dim':
expectedState.brightness = parameters.brightness;
break;
}
this.updateDeviceState(deviceId, expectedState);
}
isNightTime() {
const hour = new Date().getHours();
return hour >= 18 || hour <= 6;
}
isAirConditionerOn() {
const state = this.getDeviceState('air_conditioner');
return state.power === true;
}
isHomeOccupied() {
// 检查是否有人在家(基于最近的人体感应数据)
const motionSensors = ['living_room_motion', 'bedroom_motion', 'kitchen_motion'];
const recentTime = Date.now() - 30 * 60 * 1000; // 30分钟内
return motionSensors.some(sensorId => {
const state = this.getDeviceState(sensorId);
return state.lastDetection && state.lastDetection > recentTime;
});
}
getLightForMotionSensor(motionSensorId) {
const mapping = {
'living_room_motion': 'living_room_light',
'bedroom_motion': 'bedroom_light',
'kitchen_motion': 'kitchen_light',
'bathroom_motion': 'bathroom_light'
};
return mapping[motionSensorId];
}
async logCommand(commandData) {
// 记录到数据库
const db = require('./database');
await db.query(
'INSERT INTO command_history (device_id, command, parameters, timestamp, request_id) VALUES (?, ?, ?, ?, ?)',
[commandData.deviceId, commandData.command, JSON.stringify(commandData.parameters),
new Date(commandData.timestamp), commandData.requestId]
);
}
async logSceneExecution(sceneId, userId, results) {
const db = require('./database');
await db.query(
'INSERT INTO scene_history (scene_id, user_id, results, timestamp) VALUES (?, ?, ?, ?)',
[sceneId, userId, JSON.stringify(results), new Date()]
);
}
sendNotification(message) {
// 发送推送通知
global.notificationService?.send({
title: '智能家居通知',
message,
timestamp: Date.now()
});
}
delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
module.exports = SmartControlService;
# 4.3 移动应用界面
// 示例:React Native移动应用主界面
import React, { useState, useEffect } from 'react';
import {
View,
Text,
StyleSheet,
ScrollView,
TouchableOpacity,
Switch,
Alert,
RefreshControl
} from 'react-native';
import { Card, Button, Slider, Icon } from 'react-native-elements';
import { useWebSocket } from '../hooks/useWebSocket';
import { deviceService } from '../services/deviceService';
interface Device {
id: string;
name: string;
type: string;
status: 'online' | 'offline';
state: any;
}
interface SensorData {
temperature?: number;
humidity?: number;
airQuality?: number;
light?: number;
}
const SmartHomeScreen: React.FC = () => {
const [devices, setDevices] = useState<Device[]>([]);
const [sensorData, setSensorData] = useState<SensorData>({});
const [loading, setLoading] = useState(false);
const [refreshing, setRefreshing] = useState(false);
const { isConnected, sendMessage, subscribe } = useWebSocket({
url: 'ws://your-gateway-ip:8080/ws',
autoConnect: true
});
useEffect(() => {
loadDevices();
setupWebSocketSubscriptions();
}, []);
const loadDevices = async () => {
try {
setLoading(true);
const deviceList = await deviceService.getDevices();
setDevices(deviceList);
} catch (error) {
Alert.alert('错误', '加载设备列表失败');
} finally {
setLoading(false);
}
};
const setupWebSocketSubscriptions = () => {
// 订阅传感器数据
subscribe('sensor_data', (data: any) => {
setSensorData(prev => ({
...prev,
[data.sensorType]: data.data.value || data.data
}));
});
// 订阅设备状态变化
subscribe('device_state', (data: any) => {
setDevices(prev => prev.map(device =>
device.id === data.deviceId
? { ...device, state: data.state }
: device
));
});
};
const handleDeviceControl = async (deviceId: string, command: string, parameters?: any) => {
try {
await deviceService.sendCommand(deviceId, command, parameters);
} catch (error) {
Alert.alert('错误', '设备控制失败');
}
};
const handleSceneExecution = async (sceneId: string) => {
try {
await deviceService.executeScene(sceneId);
Alert.alert('成功', '场景执行完成');
} catch (error) {
Alert.alert('错误', '场景执行失败');
}
};
const onRefresh = async () => {
setRefreshing(true);
await loadDevices();
setRefreshing(false);
};
const renderSensorCard = () => (
<Card containerStyle={styles.card}>
<Text style={styles.cardTitle}>环境监测</Text>
<View style={styles.sensorGrid}>
<View style={styles.sensorItem}>
<Icon name="thermometer" type="font-awesome-5" color="#ff6b6b" />
<Text style={styles.sensorValue}>
{sensorData.temperature?.toFixed(1) || '--'}°C
</Text>
<Text style={styles.sensorLabel}>温度</Text>
</View>
<View style={styles.sensorItem}>
<Icon name="tint" type="font-awesome-5" color="#4ecdc4" />
<Text style={styles.sensorValue}>
{sensorData.humidity?.toFixed(1) || '--'}%
</Text>
<Text style={styles.sensorLabel}>湿度</Text>
</View>
<View style={styles.sensorItem}>
<Icon name="wind" type="font-awesome-5" color="#45b7d1" />
<Text style={styles.sensorValue}>
{sensorData.airQuality?.toFixed(0) || '--'}
</Text>
<Text style={styles.sensorLabel}>空气质量</Text>
</View>
<View style={styles.sensorItem}>
<Icon name="sun" type="font-awesome-5" color="#f9ca24" />
<Text style={styles.sensorValue}>
{sensorData.light?.toFixed(0) || '--'}
</Text>
<Text style={styles.sensorLabel}>光照(lux)</Text>
</View>
</View>
</Card>
);
const renderSceneCard = () => (
<Card containerStyle={styles.card}>
<Text style={styles.cardTitle}>场景控制</Text>
<View style={styles.sceneGrid}>
<TouchableOpacity
style={[styles.sceneButton, { backgroundColor: '#ff6b6b' }]}
onPress={() => handleSceneExecution('home')}
>
<Icon name="home" type="font-awesome-5" color="white" size={24} />
<Text style={styles.sceneText}>回家</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.sceneButton, { backgroundColor: '#4ecdc4' }]}
onPress={() => handleSceneExecution('away')}
>
<Icon name="lock" type="font-awesome-5" color="white" size={24} />
<Text style={styles.sceneText}>离家</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.sceneButton, { backgroundColor: '#45b7d1' }]}
onPress={() => handleSceneExecution('sleep')}
>
<Icon name="moon" type="font-awesome-5" color="white" size={24} />
<Text style={styles.sceneText}>睡眠</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.sceneButton, { backgroundColor: '#f9ca24' }]}
onPress={() => handleSceneExecution('movie')}
>
<Icon name="film" type="font-awesome-5" color="white" size={24} />
<Text style={styles.sceneText}>观影</Text>
</TouchableOpacity>
</View>
</Card>
);
const renderDeviceCard = (device: Device) => {
const isOn = device.state?.power === true;
const brightness = device.state?.brightness || 0;
return (
<Card key={device.id} containerStyle={styles.card}>
<View style={styles.deviceHeader}>
<View>
<Text style={styles.deviceName}>{device.name}</Text>
<Text style={styles.deviceType}>{device.type}</Text>
</View>
<View style={styles.deviceStatus}>
<View style={[
styles.statusDot,
{ backgroundColor: device.status === 'online' ? '#52c41a' : '#ff4d4f' }
]} />
<Text style={styles.statusText}>{device.status}</Text>
</View>
</View>
<View style={styles.deviceControls}>
{device.type === 'light' && (
<>
<View style={styles.controlRow}>
<Text>开关</Text>
<Switch
value={isOn}
onValueChange={(value) =>
handleDeviceControl(device.id, value ? 'turn_on' : 'turn_off')
}
/>
</View>
{isOn && (
<View style={styles.controlRow}>
<Text>亮度: {brightness}%</Text>
<Slider
style={styles.slider}
value={brightness}
minimumValue={0}
maximumValue={100}
step={1}
onSlidingComplete={(value) =>
handleDeviceControl(device.id, 'set_brightness', { brightness: value })
}
/>
</View>
)}
</>
)}
{device.type === 'air_conditioner' && (
<>
<View style={styles.controlRow}>
<Text>开关</Text>
<Switch
value={isOn}
onValueChange={(value) =>
handleDeviceControl(device.id, value ? 'turn_on' : 'turn_off')
}
/>
</View>
{isOn && (
<View style={styles.controlRow}>
<Text>温度: {device.state?.targetTemperature || 24}°C</Text>
<View style={styles.tempControls}>
<Button
title="-"
buttonStyle={styles.tempButton}
onPress={() =>
handleDeviceControl(device.id, 'set_temperature', {
temperature: (device.state?.targetTemperature || 24) - 1
})
}
/>
<Button
title="+"
buttonStyle={styles.tempButton}
onPress={() =>
handleDeviceControl(device.id, 'set_temperature', {
temperature: (device.state?.targetTemperature || 24) + 1
})
}
/>
</View>
</View>
)}
</>
)}
{device.type === 'socket' && (
<View style={styles.controlRow}>
<Text>开关</Text>
<Switch
value={isOn}
onValueChange={(value) =>
handleDeviceControl(device.id, value ? 'turn_on' : 'turn_off')
}
/>
</View>
)}
</View>
</Card>
);
};
return (
<ScrollView
style={styles.container}
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
}
>
{/* 连接状态 */}
<View style={styles.connectionStatus}>
<View style={[
styles.statusDot,
{ backgroundColor: isConnected ? '#52c41a' : '#ff4d4f' }
]} />
<Text style={styles.connectionText}>
{isConnected ? '已连接' : '连接中...'}
</Text>
</View>
{/* 传感器数据 */}
{renderSensorCard()}
{/* 场景控制 */}
{renderSceneCard()}
{/* 设备列表 */}
<Text style={styles.sectionTitle}>设备控制</Text>
{devices.map(renderDeviceCard)}
</ScrollView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
padding: 16
},
connectionStatus: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 16,
padding: 12,
backgroundColor: 'white',
borderRadius: 8
},
statusDot: {
width: 8,
height: 8,
borderRadius: 4,
marginRight: 8
},
connectionText: {
fontSize: 14,
color: '#666'
},
card: {
borderRadius: 12,
marginBottom: 16,
elevation: 3,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4
},
cardTitle: {
fontSize: 18,
fontWeight: 'bold',
marginBottom: 16,
color: '#333'
},
sensorGrid: {
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'space-between'
},
sensorItem: {
width: '48%',
alignItems: 'center',
padding: 16,
backgroundColor: '#f8f9fa',
borderRadius: 8,
marginBottom: 8
},
sensorValue: {
fontSize: 24,
fontWeight: 'bold',
marginVertical: 8,
color: '#333'
},
sensorLabel: {
fontSize: 12,
color: '#666'
},
sceneGrid: {
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'space-between'
},
sceneButton: {
width: '48%',
aspectRatio: 1,
borderRadius: 12,
justifyContent: 'center',
alignItems: 'center',
marginBottom: 8
},
sceneText: {
color: 'white',
fontSize: 14,
fontWeight: 'bold',
marginTop: 8
},
sectionTitle: {
fontSize: 20,
fontWeight: 'bold',
marginBottom: 16,
color: '#333'
},
deviceHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 16
},
deviceName: {
fontSize: 16,
fontWeight: 'bold',
color: '#333'
},
deviceType: {
fontSize: 12,
color: '#666',
marginTop: 2
},
deviceStatus: {
flexDirection: 'row',
alignItems: 'center'
},
statusText: {
fontSize: 12,
color: '#666',
marginLeft: 4
},
deviceControls: {
gap: 12
},
controlRow: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center'
},
slider: {
flex: 1,
marginLeft: 16
},
tempControls: {
flexDirection: 'row',
gap: 8
},
tempButton: {
width: 40,
height: 40,
borderRadius: 20
}
});
export default SmartHomeScreen;
# 项目二:工业监控系统
# 1. 项目需求分析
# 1.1 功能需求
- 设备监控: 生产设备状态、运行参数、故障诊断
- 数据采集: 温度、压力、振动、电流等传感器数据
- 报警管理: 实时告警、故障预警、维护提醒
- 生产统计: 产量统计、效率分析、质量监控
- 远程控制: 设备启停、参数调整、维护模式
# 1.2 系统特点
- 高可靠性: 7×24小时不间断运行
- 实时性: 毫秒级数据采集和响应
- 安全性: 工业级安全防护
- 扩展性: 支持大规模设备接入
- 兼容性: 支持多种工业协议
# 2. 系统架构设计
graph TB
subgraph "工业监控系统架构"
subgraph "现场设备层"
A1["PLC控制器"]
A2["传感器阵列"]
A3["执行器"]
A4["工业摄像头"]
A5["RFID读写器"]
end
subgraph "数据采集层"
B1["工业网关"]
B2["协议转换"]
B3["数据预处理"]
B4["边缘计算"]
end
subgraph "通信网络层"
C1["工业以太网"]
C2["Modbus"]
C3["OPC UA"]
C4["4G/5G"]
end
subgraph "数据处理层"
D1["实时数据库"]
D2["历史数据库"]
D3["流处理引擎"]
D4["规则引擎"]
end
subgraph "应用服务层"
E1["设备管理"]
E2["数据分析"]
E3["报警服务"]
E4["报表服务"]
end
subgraph "展示层"
F1["监控大屏"]
F2["Web控制台"]
F3["移动APP"]
F4["第三方系统"]
end
end
A1 --> B1
A2 --> B1
A3 --> B1
A4 --> B1
A5 --> B1
B1 --> C1
B2 --> C2
B3 --> C3
B4 --> C4
C1 --> D1
C2 --> D2
C3 --> D3
C4 --> D4
D1 --> E1
D2 --> E2
D3 --> E3
D4 --> E4
E1 --> F1
E2 --> F2
E3 --> F3
E4 --> F4
# 3. 核心代码实现
# 3.1 工业协议适配器
// 示例:Modbus协议适配器
@Component
public class ModbusProtocolAdapter implements ProtocolAdapter {
private static final Logger logger = LoggerFactory.getLogger(ModbusProtocolAdapter.class);
@Autowired
private DeviceDataService deviceDataService;
@Autowired
private AlarmService alarmService;
private final Map<String, ModbusMaster> masterConnections = new ConcurrentHashMap<>();
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(10);
@Override
public void initialize(DeviceConfig config) {
try {
ModbusMaster master = createModbusMaster(config);
masterConnections.put(config.getDeviceId(), master);
// 启动数据采集任务
startDataCollection(config);
logger.info("Modbus adapter initialized for device: {}", config.getDeviceId());
} catch (Exception e) {
logger.error("Failed to initialize Modbus adapter for device: {}",
config.getDeviceId(), e);
throw new ProtocolAdapterException("Modbus adapter initialization failed", e);
}
}
private ModbusMaster createModbusMaster(DeviceConfig config) throws Exception {
ModbusFactory factory = new ModbusFactory();
if ("TCP".equalsIgnoreCase(config.getConnectionType())) {
// TCP连接
IpParameters params = new IpParameters();
params.setHost(config.getHost());
params.setPort(config.getPort());
params.setEncapsulated(false);
return factory.createTcpMaster(params, true);
} else if ("RTU".equalsIgnoreCase(config.getConnectionType())) {
// RTU串口连接
SerialParameters params = new SerialParameters();
params.setCommPortId(config.getSerialPort());
params.setBaudRate(config.getBaudRate());
params.setDataBits(config.getDataBits());
params.setStopBits(config.getStopBits());
params.setParity(config.getParity());
return factory.createRtuMaster(params);
} else {
throw new IllegalArgumentException("Unsupported connection type: " +
config.getConnectionType());
}
}
private void startDataCollection(DeviceConfig config) {
scheduler.scheduleAtFixedRate(() -> {
try {
collectDeviceData(config);
} catch (Exception e) {
logger.error("Data collection failed for device: {}",
config.getDeviceId(), e);
handleConnectionError(config.getDeviceId(), e);
}
}, 0, config.getCollectionInterval(), TimeUnit.MILLISECONDS);
}
private void collectDeviceData(DeviceConfig config) throws Exception {
ModbusMaster master = masterConnections.get(config.getDeviceId());
if (master == null) {
throw new IllegalStateException("Modbus master not found for device: " +
config.getDeviceId());
}
List<RegisterConfig> registers = config.getRegisters();
Map<String, Object> dataPoints = new HashMap<>();
for (RegisterConfig register : registers) {
try {
Object value = readRegister(master, config.getSlaveId(), register);
dataPoints.put(register.getName(), value);
} catch (Exception e) {
logger.warn("Failed to read register {} from device {}: {}",
register.getName(), config.getDeviceId(), e.getMessage());
// 记录读取失败的寄存器
dataPoints.put(register.getName(), null);
}
}
// 处理采集到的数据
processCollectedData(config.getDeviceId(), dataPoints);
}
private Object readRegister(ModbusMaster master, int slaveId, RegisterConfig register)
throws Exception {
switch (register.getType()) {
case HOLDING_REGISTER:
return readHoldingRegister(master, slaveId, register);
case INPUT_REGISTER:
return readInputRegister(master, slaveId, register);
case COIL:
return readCoil(master, slaveId, register);
case DISCRETE_INPUT:
return readDiscreteInput(master, slaveId, register);
default:
throw new IllegalArgumentException("Unsupported register type: " +
register.getType());
}
}
private Object readHoldingRegister(ModbusMaster master, int slaveId, RegisterConfig register)
throws Exception {
ReadHoldingRegistersRequest request = new ReadHoldingRegistersRequest(
slaveId, register.getAddress(), register.getLength());
ReadHoldingRegistersResponse response =
(ReadHoldingRegistersResponse) master.send(request);
if (response.isException()) {
throw new ModbusException("Modbus exception: " + response.getExceptionMessage());
}
return convertRegisterValue(response.getShortData(), register.getDataType(),
register.getScale(), register.getOffset());
}
private Object readInputRegister(ModbusMaster master, int slaveId, RegisterConfig register)
throws Exception {
ReadInputRegistersRequest request = new ReadInputRegistersRequest(
slaveId, register.getAddress(), register.getLength());
ReadInputRegistersResponse response =
(ReadInputRegistersResponse) master.send(request);
if (response.isException()) {
throw new ModbusException("Modbus exception: " + response.getExceptionMessage());
}
return convertRegisterValue(response.getShortData(), register.getDataType(),
register.getScale(), register.getOffset());
}
private Object readCoil(ModbusMaster master, int slaveId, RegisterConfig register)
throws Exception {
ReadCoilsRequest request = new ReadCoilsRequest(
slaveId, register.getAddress(), register.getLength());
ReadCoilsResponse response = (ReadCoilsResponse) master.send(request);
if (response.isException()) {
throw new ModbusException("Modbus exception: " + response.getExceptionMessage());
}
return response.getBooleanData()[0]; // 假设只读取一个线圈
}
private Object readDiscreteInput(ModbusMaster master, int slaveId, RegisterConfig register)
throws Exception {
ReadDiscreteInputsRequest request = new ReadDiscreteInputsRequest(
slaveId, register.getAddress(), register.getLength());
ReadDiscreteInputsResponse response =
(ReadDiscreteInputsResponse) master.send(request);
if (response.isException()) {
throw new ModbusException("Modbus exception: " + response.getExceptionMessage());
}
return response.getBooleanData()[0]; // 假设只读取一个输入
}
private Object convertRegisterValue(short[] data, DataType dataType,
double scale, double offset) {
switch (dataType) {
case INT16:
return (int) (data[0] * scale + offset);
case UINT16:
return (int) ((data[0] & 0xFFFF) * scale + offset);
case INT32:
int int32Value = (data[0] << 16) | (data[1] & 0xFFFF);
return (int) (int32Value * scale + offset);
case UINT32:
long uint32Value = ((long) (data[0] & 0xFFFF) << 16) | (data[1] & 0xFFFF);
return (long) (uint32Value * scale + offset);
case FLOAT32:
int floatBits = (data[0] << 16) | (data[1] & 0xFFFF);
return Float.intBitsToFloat(floatBits) * scale + offset;
case BOOLEAN:
return data[0] != 0;
default:
throw new IllegalArgumentException("Unsupported data type: " + dataType);
}
}
private void processCollectedData(String deviceId, Map<String, Object> dataPoints) {
try {
// 创建设备数据对象
DeviceData deviceData = DeviceData.builder()
.deviceId(deviceId)
.timestamp(Instant.now())
.dataPoints(dataPoints)
.build();
// 保存数据
deviceDataService.saveDeviceData(deviceData);
// 检查告警条件
checkAlarmConditions(deviceId, dataPoints);
// 发送实时数据
publishRealTimeData(deviceData);
} catch (Exception e) {
logger.error("Failed to process collected data for device: {}", deviceId, e);
}
}
private void checkAlarmConditions(String deviceId, Map<String, Object> dataPoints) {
for (Map.Entry<String, Object> entry : dataPoints.entrySet()) {
String parameterName = entry.getKey();
Object value = entry.getValue();
if (value instanceof Number) {
double numericValue = ((Number) value).doubleValue();
alarmService.checkThresholdAlarm(deviceId, parameterName, numericValue);
}
}
}
private void publishRealTimeData(DeviceData deviceData) {
// 发布到消息队列或WebSocket
// 这里可以使用Spring的事件机制或消息队列
}
private void handleConnectionError(String deviceId, Exception error) {
// 记录连接错误
logger.error("Connection error for device {}: {}", deviceId, error.getMessage());
// 创建连接告警
alarmService.createConnectionAlarm(deviceId, error.getMessage());
// 尝试重连
scheduleReconnection(deviceId);
}
private void scheduleReconnection(String deviceId) {
scheduler.schedule(() -> {
try {
// 重新初始化连接
DeviceConfig config = deviceConfigService.getDeviceConfig(deviceId);
initialize(config);
logger.info("Reconnection successful for device: {}", deviceId);
} catch (Exception e) {
logger.error("Reconnection failed for device: {}", deviceId, e);
// 继续重试
scheduleReconnection(deviceId);
}
}, 30, TimeUnit.SECONDS); // 30秒后重试
}
@Override
public void writeData(String deviceId, String parameterName, Object value) throws Exception {
ModbusMaster master = masterConnections.get(deviceId);
if (master == null) {
throw new IllegalStateException("Modbus master not found for device: " + deviceId);
}
DeviceConfig config = deviceConfigService.getDeviceConfig(deviceId);
RegisterConfig register = config.getRegisterByName(parameterName);
if (register == null) {
throw new IllegalArgumentException("Register not found: " + parameterName);
}
writeRegister(master, config.getSlaveId(), register, value);
}
private void writeRegister(ModbusMaster master, int slaveId, RegisterConfig register,
Object value) throws Exception {
switch (register.getType()) {
case HOLDING_REGISTER:
writeHoldingRegister(master, slaveId, register, value);
break;
case COIL:
writeCoil(master, slaveId, register, value);
break;
default:
throw new IllegalArgumentException("Cannot write to register type: " +
register.getType());
}
}
private void writeHoldingRegister(ModbusMaster master, int slaveId, RegisterConfig register,
Object value) throws Exception {
short[] data = convertValueToRegister(value, register.getDataType(),
register.getScale(), register.getOffset());
if (data.length == 1) {
WriteSingleRegisterRequest request = new WriteSingleRegisterRequest(
slaveId, register.getAddress(), data[0]);
WriteSingleRegisterResponse response =
(WriteSingleRegisterResponse) master.send(request);
if (response.isException()) {
throw new ModbusException("Modbus exception: " + response.getExceptionMessage());
}
} else {
WriteMultipleRegistersRequest request = new WriteMultipleRegistersRequest(
slaveId, register.getAddress(), data);
WriteMultipleRegistersResponse response =
(WriteMultipleRegistersResponse) master.send(request);
if (response.isException()) {
throw new ModbusException("Modbus exception: " + response.getExceptionMessage());
}
}
}
private void writeCoil(ModbusMaster master, int slaveId, RegisterConfig register,
Object value) throws Exception {
boolean boolValue = (Boolean) value;
WriteSingleCoilRequest request = new WriteSingleCoilRequest(
slaveId, register.getAddress(), boolValue);
WriteSingleCoilResponse response = (WriteSingleCoilResponse) master.send(request);
if (response.isException()) {
throw new ModbusException("Modbus exception: " + response.getExceptionMessage());
}
}
private short[] convertValueToRegister(Object value, DataType dataType,
double scale, double offset) {
double adjustedValue = ((Number) value).doubleValue() / scale - offset;
switch (dataType) {
case INT16:
return new short[] { (short) Math.round(adjustedValue) };
case UINT16:
return new short[] { (short) (Math.round(adjustedValue) & 0xFFFF) };
case INT32:
int int32Value = (int) Math.round(adjustedValue);
return new short[] {
(short) (int32Value >> 16),
(short) (int32Value & 0xFFFF)
};
case FLOAT32:
float floatValue = (float) adjustedValue;
int floatBits = Float.floatToIntBits(floatValue);
return new short[] {
(short) (floatBits >> 16),
(short) (floatBits & 0xFFFF)
};
default:
throw new IllegalArgumentException("Unsupported data type for writing: " + dataType);
}
}
@Override
public void disconnect(String deviceId) {
ModbusMaster master = masterConnections.remove(deviceId);
if (master != null) {
master.destroy();
logger.info("Modbus connection closed for device: {}", deviceId);
}
}
@PreDestroy
public void shutdown() {
scheduler.shutdown();
masterConnections.values().forEach(ModbusMaster::destroy);
masterConnections.clear();
}
}
# 3.2 实时告警系统
// 示例:实时告警系统
@Service
public class IndustrialAlarmService {
private static final Logger logger = LoggerFactory.getLogger(IndustrialAlarmService.class);
@Autowired
private AlarmRuleRepository alarmRuleRepository;
@Autowired
private AlarmRecordRepository alarmRecordRepository;
@Autowired
private NotificationService notificationService;
@Autowired
private WebSocketService webSocketService;
private final Map<String, AlarmState> deviceAlarmStates = new ConcurrentHashMap<>();
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(5);
@PostConstruct
public void initialize() {
// 加载告警规则
loadAlarmRules();
// 启动告警状态检查任务
startAlarmStateMonitoring();
}
public void checkThresholdAlarm(String deviceId, String parameterName, double value) {
List<AlarmRule> rules = getAlarmRules(deviceId, parameterName);
for (AlarmRule rule : rules) {
if (rule.isEnabled()) {
evaluateAlarmRule(rule, deviceId, parameterName, value);
}
}
}
private void evaluateAlarmRule(AlarmRule rule, String deviceId,
String parameterName, double value) {
boolean alarmTriggered = false;
String alarmMessage = "";
switch (rule.getCondition()) {
case GREATER_THAN:
if (value > rule.getThreshold()) {
alarmTriggered = true;
alarmMessage = String.format("%s 超过上限: %.2f > %.2f",
parameterName, value, rule.getThreshold());
}
break;
case LESS_THAN:
if (value < rule.getThreshold()) {
alarmTriggered = true;
alarmMessage = String.format("%s 低于下限: %.2f < %.2f",
parameterName, value, rule.getThreshold());
}
break;
case EQUALS:
if (Math.abs(value - rule.getThreshold()) < 0.001) {
alarmTriggered = true;
alarmMessage = String.format("%s 等于告警值: %.2f",
parameterName, value);
}
break;
case RANGE_OUT:
if (value < rule.getMinThreshold() || value > rule.getMaxThreshold()) {
alarmTriggered = true;
alarmMessage = String.format("%s 超出范围: %.2f (范围: %.2f - %.2f)",
parameterName, value,
rule.getMinThreshold(), rule.getMaxThreshold());
}
break;
}
if (alarmTriggered) {
handleAlarmTriggered(rule, deviceId, parameterName, value, alarmMessage);
} else {
handleAlarmCleared(rule, deviceId, parameterName, value);
}
}
private void handleAlarmTriggered(AlarmRule rule, String deviceId, String parameterName,
double value, String message) {
String alarmKey = generateAlarmKey(deviceId, parameterName, rule.getId());
AlarmState currentState = deviceAlarmStates.get(alarmKey);
if (currentState == null || currentState.getStatus() != AlarmStatus.ACTIVE) {
// 新告警或告警重新触发
AlarmRecord alarmRecord = createAlarmRecord(rule, deviceId, parameterName,
value, message);
// 保存告警记录
alarmRecordRepository.save(alarmRecord);
// 更新告警状态
AlarmState newState = AlarmState.builder()
.alarmId(alarmRecord.getId())
.deviceId(deviceId)
.parameterName(parameterName)
.ruleId(rule.getId())
.status(AlarmStatus.ACTIVE)
.triggerTime(Instant.now())
.triggerValue(value)
.message(message)
.build();
deviceAlarmStates.put(alarmKey, newState);
// 发送告警通知
sendAlarmNotification(alarmRecord, rule);
// 实时推送告警
publishAlarmEvent(alarmRecord);
logger.warn("Alarm triggered: {} - {}", deviceId, message);
} else {
// 更新现有告警的最后触发时间和值
currentState.setLastTriggerTime(Instant.now());
currentState.setTriggerValue(value);
}
}
private void handleAlarmCleared(AlarmRule rule, String deviceId, String parameterName,
double value) {
String alarmKey = generateAlarmKey(deviceId, parameterName, rule.getId());
AlarmState currentState = deviceAlarmStates.get(alarmKey);
if (currentState != null && currentState.getStatus() == AlarmStatus.ACTIVE) {
// 告警恢复
currentState.setStatus(AlarmStatus.CLEARED);
currentState.setClearTime(Instant.now());
currentState.setClearValue(value);
// 更新数据库记录
AlarmRecord alarmRecord = alarmRecordRepository.findById(currentState.getAlarmId())
.orElse(null);
if (alarmRecord != null) {
alarmRecord.setStatus(AlarmStatus.CLEARED);
alarmRecord.setClearTime(Instant.now());
alarmRecord.setClearValue(value);
alarmRecordRepository.save(alarmRecord);
// 发送恢复通知
sendAlarmClearNotification(alarmRecord, rule);
// 实时推送恢复事件
publishAlarmClearEvent(alarmRecord);
logger.info("Alarm cleared: {} - {} 恢复正常", deviceId, parameterName);
}
// 移除告警状态
deviceAlarmStates.remove(alarmKey);
}
}
private String generateAlarmKey(String deviceId, String parameterName, Long ruleId) {
return String.format("%s:%s:%d", deviceId, parameterName, ruleId);
}
private AlarmRecord createAlarmRecord(AlarmRule rule, String deviceId,
String parameterName, double value, String message) {
return AlarmRecord.builder()
.deviceId(deviceId)
.parameterName(parameterName)
.ruleId(rule.getId())
.ruleName(rule.getName())
.severity(rule.getSeverity())
.triggerValue(value)
.threshold(rule.getThreshold())
.condition(rule.getCondition())
.message(message)
.status(AlarmStatus.ACTIVE)
.triggerTime(Instant.now())
.build();
}
private void sendAlarmNotification(AlarmRecord alarmRecord, AlarmRule rule) {
// 根据告警级别和规则配置发送不同类型的通知
NotificationRequest notification = NotificationRequest.builder()
.title("工业设备告警")
.message(alarmRecord.getMessage())
.severity(alarmRecord.getSeverity())
.deviceId(alarmRecord.getDeviceId())
.timestamp(alarmRecord.getTriggerTime())
.build();
// 发送邮件、短信、微信等通知
notificationService.sendNotification(notification, rule.getNotificationChannels());
}
private void publishAlarmEvent(AlarmRecord alarmRecord) {
// 通过WebSocket实时推送告警事件
AlarmEvent event = AlarmEvent.builder()
.type("ALARM_TRIGGERED")
.alarmRecord(alarmRecord)
.timestamp(Instant.now())
.build();
webSocketService.broadcast("/topic/alarms", event);
}
@PreDestroy
public void shutdown() {
scheduler.shutdown();
}
}
# 4. 车联网系统
# 4.1 系统架构
graph TB
subgraph "车载终端层"
A[OBD设备] --> B[车载网关]
C[传感器] --> B
D[GPS模块] --> B
E[摄像头] --> B
end
subgraph "通信层"
B --> F[4G/5G网络]
B --> G[WiFi]
B --> H[蓝牙]
end
subgraph "云端平台"
F --> I[消息网关]
G --> I
I --> J[设备管理]
I --> K[数据处理]
I --> L[位置服务]
I --> M[告警服务]
end
subgraph "应用层"
J --> N[车队管理]
K --> O[驾驶行为分析]
L --> P[轨迹追踪]
M --> Q[紧急救援]
end
subgraph "用户界面"
N --> R[管理后台]
O --> S[移动APP]
P --> T[Web监控]
Q --> U[告警中心]
end
# 4.2 车辆数据采集
// 示例:车载数据采集系统
class VehicleDataCollector {
constructor(config) {
this.config = config;
this.sensors = new Map();
this.dataBuffer = [];
this.isCollecting = false;
this.uploadInterval = config.uploadInterval || 30000; // 30秒
this.initializeSensors();
this.setupDataUpload();
}
initializeSensors() {
// 初始化OBD传感器
this.sensors.set('obd', new OBDSensor({
port: this.config.obdPort,
baudRate: 38400,
protocols: ['CAN', 'ISO9141', 'KWP2000']
}));
// 初始化GPS传感器
this.sensors.set('gps', new GPSSensor({
port: this.config.gpsPort,
updateRate: 1000 // 1秒更新一次
}));
// 初始化加速度传感器
this.sensors.set('accelerometer', new AccelerometerSensor({
sensitivity: 'high',
sampleRate: 100 // 100Hz
}));
// 初始化陀螺仪
this.sensors.set('gyroscope', new GyroscopeSensor({
range: 2000, // ±2000°/s
sampleRate: 100
}));
}
async startCollection() {
if (this.isCollecting) {
console.log('Data collection already started');
return;
}
this.isCollecting = true;
console.log('Starting vehicle data collection...');
// 启动各个传感器
for (const [name, sensor] of this.sensors) {
try {
await sensor.start();
sensor.on('data', (data) => this.handleSensorData(name, data));
console.log(`${name} sensor started successfully`);
} catch (error) {
console.error(`Failed to start ${name} sensor:`, error);
}
}
// 启动数据收集循环
this.collectionLoop();
}
handleSensorData(sensorType, data) {
const timestamp = Date.now();
const vehicleData = {
vehicleId: this.config.vehicleId,
timestamp,
sensorType,
data: this.processSensorData(sensorType, data)
};
// 添加到数据缓冲区
this.dataBuffer.push(vehicleData);
// 检查是否需要立即上传(紧急情况)
if (this.isEmergencyData(vehicleData)) {
this.uploadEmergencyData(vehicleData);
}
}
processSensorData(sensorType, rawData) {
switch (sensorType) {
case 'obd':
return this.processOBDData(rawData);
case 'gps':
return this.processGPSData(rawData);
case 'accelerometer':
return this.processAccelerometerData(rawData);
case 'gyroscope':
return this.processGyroscopeData(rawData);
default:
return rawData;
}
}
processOBDData(data) {
return {
speed: data.speed || 0,
rpm: data.rpm || 0,
engineLoad: data.engineLoad || 0,
coolantTemp: data.coolantTemp || 0,
fuelLevel: data.fuelLevel || 0,
throttlePosition: data.throttlePosition || 0,
engineRunTime: data.engineRunTime || 0,
distanceTraveled: data.distanceTraveled || 0,
fuelConsumption: data.fuelConsumption || 0,
diagnosticCodes: data.diagnosticCodes || []
};
}
processGPSData(data) {
return {
latitude: data.latitude,
longitude: data.longitude,
altitude: data.altitude || 0,
speed: data.speed || 0,
heading: data.heading || 0,
accuracy: data.accuracy || 0,
satellites: data.satellites || 0,
timestamp: data.timestamp
};
}
processAccelerometerData(data) {
const magnitude = Math.sqrt(data.x * data.x + data.y * data.y + data.z * data.z);
return {
x: data.x,
y: data.y,
z: data.z,
magnitude,
isHardBraking: magnitude > 8.0, // 急刹车检测
isHardAcceleration: data.x > 4.0, // 急加速检测
isSharpTurn: Math.abs(data.y) > 6.0 // 急转弯检测
};
}
processGyroscopeData(data) {
return {
x: data.x, // 俯仰角速度
y: data.y, // 横滚角速度
z: data.z, // 偏航角速度
isRapidRotation: Math.abs(data.z) > 100 // 快速转向检测
};
}
isEmergencyData(vehicleData) {
const { sensorType, data } = vehicleData;
switch (sensorType) {
case 'accelerometer':
return data.isHardBraking || data.isHardAcceleration || data.magnitude > 15.0;
case 'obd':
return data.diagnosticCodes.length > 0 || data.coolantTemp > 110;
case 'gyroscope':
return data.isRapidRotation;
default:
return false;
}
}
async uploadEmergencyData(vehicleData) {
try {
const response = await fetch(`${this.config.serverUrl}/api/vehicles/emergency`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.config.apiToken}`
},
body: JSON.stringify({
...vehicleData,
priority: 'emergency'
})
});
if (response.ok) {
console.log('Emergency data uploaded successfully');
} else {
console.error('Failed to upload emergency data:', response.statusText);
}
} catch (error) {
console.error('Error uploading emergency data:', error);
}
}
setupDataUpload() {
setInterval(() => {
if (this.dataBuffer.length > 0) {
this.uploadBatchData();
}
}, this.uploadInterval);
}
async uploadBatchData() {
if (this.dataBuffer.length === 0) return;
const batchData = [...this.dataBuffer];
this.dataBuffer = []; // 清空缓冲区
try {
const response = await fetch(`${this.config.serverUrl}/api/vehicles/data/batch`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.config.apiToken}`
},
body: JSON.stringify({
vehicleId: this.config.vehicleId,
data: batchData,
timestamp: Date.now()
})
});
if (response.ok) {
console.log(`Uploaded ${batchData.length} data points successfully`);
} else {
console.error('Failed to upload batch data:', response.statusText);
// 重新添加到缓冲区
this.dataBuffer.unshift(...batchData);
}
} catch (error) {
console.error('Error uploading batch data:', error);
// 重新添加到缓冲区
this.dataBuffer.unshift(...batchData);
}
}
async stopCollection() {
this.isCollecting = false;
// 停止所有传感器
for (const [name, sensor] of this.sensors) {
try {
await sensor.stop();
console.log(`${name} sensor stopped`);
} catch (error) {
console.error(`Error stopping ${name} sensor:`, error);
}
}
// 上传剩余数据
if (this.dataBuffer.length > 0) {
await this.uploadBatchData();
}
console.log('Vehicle data collection stopped');
}
collectionLoop() {
if (!this.isCollecting) return;
// 定期检查传感器状态
setTimeout(() => {
this.checkSensorHealth();
this.collectionLoop();
}, 5000); // 每5秒检查一次
}
checkSensorHealth() {
for (const [name, sensor] of this.sensors) {
if (!sensor.isHealthy()) {
console.warn(`${name} sensor health check failed, attempting restart...`);
sensor.restart().catch(error => {
console.error(`Failed to restart ${name} sensor:`, error);
});
}
}
}
}
// 使用示例
const collector = new VehicleDataCollector({
vehicleId: 'VEHICLE_001',
obdPort: '/dev/ttyUSB0',
gpsPort: '/dev/ttyUSB1',
serverUrl: 'https://api.vehicle-iot.com',
apiToken: 'your-api-token',
uploadInterval: 30000
});
collector.startCollection();