/// 设备运行状态 /// /// 与下位机 `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 }