按 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 错误
85 lines
2.3 KiB
Dart
85 lines
2.3 KiB
Dart
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;
|
||
}
|
||
}
|