1. PHM平台数据采集接口设计

PHM平台数据采集接口设计 学习卡片

任务说明

设计并实现一个PHM(设备健康管理)平台的数据采集接口,该接口负责接收来自不同工业设备(如振动传感器、温度传感器、PLC控制器)的监测数据,完成数据格式解析、基础质量校验,并输出统一结构化的JSON数据供下游存储与分析模块使用。本案例聚焦于接口的输入解析与标准化输出,不涉及数据持久化与前端展示。

接口协议定义

设备数据通过HTTP POST请求以JSON格式上报,请求路径为/api/v1/telemetry。每种设备类型上报的数据字段不同,接口需根据device_type字段自动选择对应的解析与校验逻辑。统一输出格式包含设备标识、时间戳、数据类型及经过标准化后的指标键值对。

振动传感器 device_type: "vib" 温度传感器 device_type: "temp" PLC控制器 device_type: "plc" 采集接口 POST /api/v1/telemetry JSON解析 字段校验 类型映射 范围检查 时间戳标准化 统一JSON输出 标准化输出 {"device_id": "VIB-001", "ts": 1710000000, "type": "vibration", "values": {"rms": 2.3, "peak": 5.1}} 校验失败返回: {"error": "invalid_value", "message": "..."}

核心实现

以下Python代码使用Flask框架实现采集接口。代码包含三种设备类型的解析器、通用校验函数及统一响应封装。输入样例覆盖三种设备类型,可直接运行验证。

# app.py
from flask import Flask, request, jsonify
from datetime import datetime
import re

app = Flask(__name__)

# ---------- 设备类型解析器 ----------
def parse_vibration(data):
    """振动数据: {device_id, ts, rms, peak}"""
    required = ['device_id', 'ts', 'rms', 'peak']
    if not all(k in data for k in required):
        return None, 'missing_field'
    try:
        rms = float(data['rms'])
        peak = float(data['peak'])
    except (TypeError, ValueError):
        return None, 'invalid_number'
    if rms < 0 or peak < 0 or peak < rms:
        return None, 'invalid_range'
    return {
        'device_id': str(data['device_id']),
        'ts': int(data['ts']),
        'type': 'vibration',
        'values': {'rms': rms, 'peak': peak}
    }, None

def parse_temperature(data):
    """温度数据: {device_id, ts, temp_c}"""
    required = ['device_id', 'ts', 'temp_c']
    if not all(k in data for k in required):
        return None, 'missing_field'
    try:
        temp = float(data['temp_c'])
    except (TypeError, ValueError):
        return None, 'invalid_number'
    if temp < -50 or temp > 200:
        return None, 'out_of_range'
    return {
        'device_id': str(data['device_id']),
        'ts': int(data['ts']),
        'type': 'temperature',
        'values': {'temp_c': temp}
    }, None

def parse_plc(data):
    """PLC数据: {device_id, ts, current_a, voltage_v}"""
    required = ['device_id', 'ts', 'current_a', 'voltage_v']
    if not all(k in data for k in required):
        return None, 'missing_field'
    try:
        current = float(data['current_a'])
        voltage = float(data['voltage_v'])
    except (TypeError, ValueError):
        return None, 'invalid_number'
    if current < 0 or voltage < 0:
        return None, 'out_of_range'
    return {
        'device_id': str(data['device_id']),
        'ts': int(data['ts']),
        'type': 'plc',
        'values': {'current_a': current, 'voltage_v': voltage}
    }, None

# 解析器注册表
PARSERS = {
    'vib': parse_vibration,
    'temp': parse_temperature,
    'plc': parse_plc
}

# ---------- 时间戳校验 ----------
def validate_ts(ts):
    """时间戳需为10位秒级Unix时间戳,且不晚于当前时间+5分钟"""
    if ts < 10**9 or ts > 10**10:
        return False
    if ts > datetime.now().timestamp() + 300:
        return False
    return True

# ---------- 路由 ----------
@app.route('/api/v1/telemetry', methods=['POST'])
def telemetry():
    data = request.get_json(silent=True)
    if not data or not isinstance(data, dict):
        return jsonify({'error': 'invalid_json'}), 400
    
    device_type = data.get('device_type')
    if device_type not in PARSERS:
        return jsonify({'error': 'unsupported_device_type'}), 400
    
    parser = PARSERS[device_type]
    parsed, err = parser(data)
    if err:
        return jsonify({'error': err}), 400
    
    if not validate_ts(parsed['ts']):
        return jsonify({'error': 'invalid_timestamp'}), 400
    
    return jsonify(parsed), 200

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)

运行与验证

依赖安装:

pip install flask

启动服务:

python app.py

验证用例:使用curl发送三种设备类型的请求,观察返回结果。

# 1. 正常振动数据
curl -X POST http://localhost:5000/api/v1/telemetry \
  -H "Content-Type: application/json" \
  -d '{"device_type":"vib","device_id":"VIB-001","ts":1710000000,"rms":2.3,"peak":5.1}'

# 预期输出: {"device_id":"VIB-001","ts":1710000000,"type":"vibration","values":{"rms":2.3,"peak":5.1}}

# 2. 温度数据超范围
curl -X POST http://localhost:5000/api/v1/telemetry \
  -H "Content-Type: application/json" \
  -d '{"device_type":"temp","device_id":"TMP-002","ts":1710000000,"temp_c":250}'

# 预期输出: {"error":"out_of_range"}

# 3. PLC数据缺少字段
curl -X POST http://localhost:5000/api/v1/telemetry \
  -H "Content-Type: application/json" \
  -d '{"device_type":"plc","device_id":"PLC-003","ts":1710000000,"current_a":5.5}'

# 预期输出: {"error":"missing_field"}

# 4. 不支持的设备类型
curl -X POST http://localhost:5000/api/v1/telemetry \
  -H "Content-Type: application/json" \
  -d '{"device_type":"sound","device_id":"SND-001","ts":1710000000}'

# 预期输出: {"error":"unsupported_device_type"}

判定标准:

  • 正常数据请求返回HTTP 200且JSON结构包含device_id、ts、type、values四个字段;
  • 字段缺失、数值非法、范围越界、时间戳异常分别返回对应的错误码与HTTP 400;
  • 所有校验逻辑均在上述代码中实现,无外部依赖。