Files
kuaishai2/lib/features/device/models/device_state.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

148 lines
4.4 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.
/// 设备运行状态
///
/// 与下位机 `device_info.task_status` 字段对齐:
/// - `running` 运行中
/// - `pause` 暂停
/// - `idle` 空闲
enum DeviceTaskStatus { running, pause, idle }
/// 设备门状态
///
/// 与下位机 `device_info.door_status` 字段对齐:
/// - `open` 开
/// - `close` 关
enum DeviceDoorStatus { open, close }
/// 设备灯光状态
///
/// 与下位机 `device_info.light_status` / `light_control.status` 字段对齐:
/// - `on` 开
/// - `off` 关
enum DeviceLightStatus { on, off }
/// 上位机内部统一的设备状态模型
///
/// 既承载 [DeviceState](运行态监控),也承载下位机 `device_info` 上报
/// 的门状态 / 灯光状态 / 任务状态,供 UI 各处订阅展示与联动。
class DeviceState {
/// 本机内部识别的运行状态(用于驱动运行/暂停/完成流程)
final DeviceStatus status;
/// 当前运行程序名(仅运行态有效)
final String? currentProgram;
/// 当前步骤的孔位(例如 A1
final String? currentPosition;
/// 当前步骤号
final int? currentStepNo;
/// 当前步骤名
final String? currentStepName;
/// 当前步骤剩余时间(秒)
final int? remainingSeconds;
/// 总进度 [0, 1]
final double? progress;
/// 灯光状态(来自 device_info主动权在下位机
final DeviceLightStatus lightStatus;
/// 门状态(来自 device_info
final DeviceDoorStatus doorStatus;
/// 任务状态(来自 device_info
final DeviceTaskStatus taskStatus;
/// 上次 device_info 上报的时间,便于 UI 显示「通讯正常 / 已断开」
final DateTime? lastInfoAt;
const DeviceState({
this.status = DeviceStatus.idle,
this.currentProgram,
this.currentPosition,
this.currentStepNo,
this.currentStepName,
this.remainingSeconds,
this.progress,
this.lightStatus = DeviceLightStatus.off,
this.doorStatus = DeviceDoorStatus.close,
this.taskStatus = DeviceTaskStatus.idle,
this.lastInfoAt,
});
bool get isRunning => status == DeviceStatus.running;
bool get isPaused => status == DeviceStatus.paused;
bool get isIdle => status == DeviceStatus.idle;
bool get hasError => status == DeviceStatus.error;
bool get lightingOn => lightStatus == DeviceLightStatus.on;
bool get doorOpen => doorStatus == DeviceDoorStatus.open;
/// 下位机超过该时间未上报则视为通讯异常
static const Duration infoStaleAfter = Duration(seconds: 10);
bool get infoStale {
final ts = lastInfoAt;
if (ts == null) return true;
return DateTime.now().difference(ts) > infoStaleAfter;
}
String statusText() {
switch (status) {
case DeviceStatus.running:
return '运行中';
case DeviceStatus.paused:
return '已暂停';
case DeviceStatus.error:
return '错误';
case DeviceStatus.idle:
return '未运行';
}
}
String formatRemainingTime() {
if (remainingSeconds == null) return '--:--:--';
final hours = remainingSeconds! ~/ 3600;
final minutes = (remainingSeconds! % 3600) ~/ 60;
final seconds = remainingSeconds! % 60;
return '${hours.toString().padLeft(2, '0')}:'
'${minutes.toString().padLeft(2, '0')}:'
'${seconds.toString().padLeft(2, '0')}';
}
DeviceState copyWith({
DeviceStatus? status,
String? currentProgram,
String? currentPosition,
int? currentStepNo,
String? currentStepName,
int? remainingSeconds,
double? progress,
DeviceLightStatus? lightStatus,
DeviceDoorStatus? doorStatus,
DeviceTaskStatus? taskStatus,
DateTime? lastInfoAt,
bool clearProgram = false,
bool clearProgress = false,
}) {
return DeviceState(
status: status ?? this.status,
currentProgram: clearProgram ? null : (currentProgram ?? this.currentProgram),
currentPosition: currentPosition ?? this.currentPosition,
currentStepNo: currentStepNo ?? this.currentStepNo,
currentStepName: currentStepName ?? this.currentStepName,
remainingSeconds: remainingSeconds ?? this.remainingSeconds,
progress: clearProgress ? null : (progress ?? this.progress),
lightStatus: lightStatus ?? this.lightStatus,
doorStatus: doorStatus ?? this.doorStatus,
taskStatus: taskStatus ?? this.taskStatus,
lastInfoAt: lastInfoAt ?? this.lastInfoAt,
);
}
}
/// 简单的状态枚举(与 [DeviceState.status] 配合使用)
enum DeviceStatus { idle, running, paused, error }