Files
kuaishai2/lib/features/device/services/task_payload.dart
Developer 819889684f feat(device): 实现下位机 JSON 协议(data model 对齐)
按 docs/下位机交互数据模型.md 重构串口协议层:

协议层
- 新增 DeviceMessage 模型,对应 message_id/type/ack/need_ack/data
- 新增 JsonProtocolService,4 字节大端长度前缀 + UTF-8 JSON 帧
- 删除原二进制协议(serial_protocol.dart)

服务层
- 新增 DeviceMessageService,集中收发并按 type 分发
- 重写 SerialRunner 为 JsonSerialRunner,使用 create_task/control 消息

数据模型
- DeviceState 增加 doorStatus/lightStatus/taskStatus/lastInfoAt
- 新增 DeviceInfoNotifier 订阅 device_info 上行
- 灯光按钮接通 light_control 消息

测试
- 新增 device_protocol_test.dart(14 用例)
- 修复 models_test.dart 残留的 Step mixSpeed/blowSpeed 错误
2026-06-04 13:00:21 +08:00

85 lines
2.3 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'dart:convert';
import '../../programs/models/program.dart';
import '../../programs/models/step.dart';
import 'device_message.dart';
/// 下位机启动任务负载
///
/// 将上位机 [Program] + [Step] 列表转换为下位机协议要求的 `create_task` 数据。
/// 协议示例:
/// ```json
/// {
/// "steps": [
/// {"no": 1, "slot": 1, "name": "混合", "mixtime": 60, "pulltime": 30, "volume": 100, "speed": 5}
/// ],
/// "temperature": 50,
/// "airflowtime": 60
/// }
/// ```
class TaskPayload {
final List<Step> steps;
final int temperature;
final int airflowTime;
const TaskPayload({
required this.steps,
required this.temperature,
required this.airflowTime,
});
/// 从程序和步骤列表构造负载
factory TaskPayload.fromProgram(Program program, List<Step> steps) {
return TaskPayload(
steps: List<Step>.from(steps)
..sort((a, b) => a.stepNo.compareTo(b.stepNo)),
temperature: program.temperature,
airflowTime: program.airflowTime,
);
}
/// 转换为下位机协议 Map
Map<String, dynamic> toJson() {
return {
'steps': steps.map((s) => _stepToJson(s)).toList(),
'temperature': temperature,
'airflowtime': airflowTime,
};
}
/// 序列化为 JSON 字符串
String encode() => jsonEncode(toJson());
/// 包裹为下位机 [DeviceMessage]
DeviceMessage toMessage(String messageId, {bool needAck = true}) {
return DeviceMessage.request(
messageId: messageId,
type: DeviceMessageType.createTask,
data: toJson(),
needAck: needAck,
);
}
Map<String, dynamic> _stepToJson(Step s) {
return {
'no': s.stepNo,
'slot': _slotFromPosition(s.position),
'name': s.name,
'mixtime': s.mixTime,
'pulltime': s.magnetTime,
'volume': s.volume,
'speed': s.speed,
};
}
/// 将孔位(如 "A1")转换为下位机使用的整数编号
/// 协议约定A1=1, A2=2, ..., A6=6, B1=7, ..., D6=24
static int _slotFromPosition(String position) {
if (position.length < 2) return 1;
final row = position.codeUnitAt(0) - 'A'.codeUnitAt(0);
final col = int.tryParse(position.substring(1)) ?? 1;
if (row < 0 || row > 3 || col < 1 || col > 6) return 1;
return row * 6 + col;
}
}