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 错误
This commit is contained in:
@@ -1,18 +1,64 @@
|
||||
/// 设备状态模型
|
||||
enum DeviceStatus { idle, running, paused, error }
|
||||
/// 设备运行状态
|
||||
///
|
||||
/// 与下位机 `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;
|
||||
final String? currentPosition;
|
||||
final int? currentStepNo;
|
||||
final String? currentStepName;
|
||||
final int? remainingSeconds;
|
||||
final double? progress;
|
||||
final bool lightingOn;
|
||||
|
||||
DeviceState({
|
||||
/// 当前运行程序名(仅运行态有效)
|
||||
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,
|
||||
@@ -20,7 +66,10 @@ class DeviceState {
|
||||
this.currentStepName,
|
||||
this.remainingSeconds,
|
||||
this.progress,
|
||||
this.lightingOn = false,
|
||||
this.lightStatus = DeviceLightStatus.off,
|
||||
this.doorStatus = DeviceDoorStatus.close,
|
||||
this.taskStatus = DeviceTaskStatus.idle,
|
||||
this.lastInfoAt,
|
||||
});
|
||||
|
||||
bool get isRunning => status == DeviceStatus.running;
|
||||
@@ -28,6 +77,18 @@ class DeviceState {
|
||||
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:
|
||||
@@ -46,7 +107,9 @@ class DeviceState {
|
||||
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')}';
|
||||
return '${hours.toString().padLeft(2, '0')}:'
|
||||
'${minutes.toString().padLeft(2, '0')}:'
|
||||
'${seconds.toString().padLeft(2, '0')}';
|
||||
}
|
||||
|
||||
DeviceState copyWith({
|
||||
@@ -57,17 +120,28 @@ class DeviceState {
|
||||
String? currentStepName,
|
||||
int? remainingSeconds,
|
||||
double? progress,
|
||||
bool? lightingOn,
|
||||
DeviceLightStatus? lightStatus,
|
||||
DeviceDoorStatus? doorStatus,
|
||||
DeviceTaskStatus? taskStatus,
|
||||
DateTime? lastInfoAt,
|
||||
bool clearProgram = false,
|
||||
bool clearProgress = false,
|
||||
}) {
|
||||
return DeviceState(
|
||||
status: status ?? this.status,
|
||||
currentProgram: currentProgram ?? this.currentProgram,
|
||||
currentProgram: clearProgram ? null : (currentProgram ?? this.currentProgram),
|
||||
currentPosition: currentPosition ?? this.currentPosition,
|
||||
currentStepNo: currentStepNo ?? this.currentStepNo,
|
||||
currentStepName: currentStepName ?? this.currentStepName,
|
||||
remainingSeconds: remainingSeconds ?? this.remainingSeconds,
|
||||
progress: progress ?? this.progress,
|
||||
lightingOn: lightingOn ?? this.lightingOn,
|
||||
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 }
|
||||
|
||||
Reference in New Issue
Block a user