mobile-eda/lib/data/incremental/incremental_save.dart

979 lines
24 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.

/// 增量保存模块
///
/// 实现操作日志Command Pattern支持
/// - 快照 + 增量日志混合存储
/// - 撤销/重做功能
/// - 断点恢复
///
/// @version 0.1.0
/// @date 2026-03-07
import 'dart:convert';
import 'dart:typed_data';
// ============================================================================
// 操作类型枚举
// ============================================================================
/// 操作类型
enum OperationType {
componentAdd,
componentMove,
componentRotate,
componentDelete,
netAdd,
netConnect,
netDelete,
traceAdd,
traceDelete,
viaAdd,
propertyChange,
snapshot,
}
// ============================================================================
// 命令接口
// ============================================================================
/// 命令接口 (Command Pattern)
abstract class Command {
/// 执行命令
void execute();
/// 撤销命令
void undo();
/// 获取操作类型
OperationType get type;
/// 获取时间戳
DateTime get timestamp;
/// 序列化为 Map
Map<String, dynamic> toJson();
/// 从 Map 反序列化
factory Command.fromJson(Map<String, dynamic> json);
}
// ============================================================================
// 具体命令实现
// ============================================================================
/// 添加元件命令
class AddComponentCommand implements Command {
@override
final OperationType type = OperationType.componentAdd;
@override
final DateTime timestamp;
final Map<String, dynamic> component;
final String? parentId;
// 撤销时需要的信息
String? _createdId;
AddComponentCommand({
required this.component,
this.parentId,
DateTime? timestamp,
}) : timestamp = timestamp ?? DateTime.now();
@override
void execute() {
// 实际执行由 Editor 处理
_createdId = component['id'];
}
@override
void undo() {
// 删除创建的元件
}
@override
Map<String, dynamic> toJson() {
return {
'type': 'componentAdd',
'timestamp': timestamp.millisecondsSinceEpoch,
'component': component,
'parentId': parentId,
'createdId': _createdId,
};
}
factory Command.fromJson(Map<String, dynamic> json) {
switch (json['type']) {
case 'componentAdd':
return AddComponentCommand(
component: json['component'] as Map<String, dynamic>,
parentId: json['parentId'] as String?,
timestamp: DateTime.fromMillisecondsSinceEpoch(json['timestamp'] as int),
);
case 'componentMove':
return MoveComponentCommand.fromJson(json);
case 'componentRotate':
return RotateComponentCommand.fromJson(json);
case 'componentDelete':
return DeleteComponentCommand.fromJson(json);
case 'netAdd':
return AddNetCommand.fromJson(json);
case 'propertyChange':
return PropertyChangeCommand.fromJson(json);
case 'snapshot':
return SnapshotCommand.fromJson(json);
default:
throw FormatException('Unknown command type: ${json['type']}');
}
}
}
/// 移动元件命令
class MoveComponentCommand implements Command {
@override
final OperationType type = OperationType.componentMove;
@override
final DateTime timestamp;
final String componentId;
final num oldX;
final num oldY;
final num newX;
final num newY;
MoveComponentCommand({
required this.componentId,
required this.oldX,
required this.oldY,
required this.newX,
required this.newY,
DateTime? timestamp,
}) : timestamp = timestamp ?? DateTime.now();
@override
void execute() {
// 移动到新位置 (已经在执行时应用)
}
@override
void undo() {
// 移回旧位置
}
@override
Map<String, dynamic> toJson() {
return {
'type': 'componentMove',
'timestamp': timestamp.millisecondsSinceEpoch,
'componentId': componentId,
'oldX': oldX,
'oldY': oldY,
'newX': newX,
'newY': newY,
};
}
factory MoveComponentCommand.fromJson(Map<String, dynamic> json) {
return MoveComponentCommand(
componentId: json['componentId'] as String,
oldX: json['oldX'] as num,
oldY: json['oldY'] as num,
newX: json['newX'] as num,
newY: json['newY'] as num,
timestamp: DateTime.fromMillisecondsSinceEpoch(json['timestamp'] as int),
);
}
}
/// 旋转元件命令
class RotateComponentCommand implements Command {
@override
final OperationType type = OperationType.componentRotate;
@override
final DateTime timestamp;
final String componentId;
final int oldRotation;
final int newRotation;
RotateComponentCommand({
required this.componentId,
required this.oldRotation,
required this.newRotation,
DateTime? timestamp,
}) : timestamp = timestamp ?? DateTime.now();
@override
void execute() {
// 旋转到新角度
}
@override
void undo() {
// 旋转回旧角度
}
@override
Map<String, dynamic> toJson() {
return {
'type': 'componentRotate',
'timestamp': timestamp.millisecondsSinceEpoch,
'componentId': componentId,
'oldRotation': oldRotation,
'newRotation': newRotation,
};
}
factory RotateComponentCommand.fromJson(Map<String, dynamic> json) {
return RotateComponentCommand(
componentId: json['componentId'] as String,
oldRotation: json['oldRotation'] as int,
newRotation: json['newRotation'] as int,
timestamp: DateTime.fromMillisecondsSinceEpoch(json['timestamp'] as int),
);
}
}
/// 删除元件命令
class DeleteComponentCommand implements Command {
@override
final OperationType type = OperationType.componentDelete;
@override
final DateTime timestamp;
final String componentId;
final Map<String, dynamic> component;
DeleteComponentCommand({
required this.componentId,
required this.component,
DateTime? timestamp,
}) : timestamp = timestamp ?? DateTime.now();
@override
void execute() {
// 删除元件
}
@override
void undo() {
// 恢复元件
}
@override
Map<String, dynamic> toJson() {
return {
'type': 'componentDelete',
'timestamp': timestamp.millisecondsSinceEpoch,
'componentId': componentId,
'component': component,
};
}
factory DeleteComponentCommand.fromJson(Map<String, dynamic> json) {
return DeleteComponentCommand(
componentId: json['componentId'] as String,
component: json['component'] as Map<String, dynamic>,
timestamp: DateTime.fromMillisecondsSinceEpoch(json['timestamp'] as int),
);
}
}
/// 添加网络命令
class AddNetCommand implements Command {
@override
final OperationType type = OperationType.netAdd;
@override
final DateTime timestamp;
final Map<String, dynamic> net;
AddNetCommand({
required this.net,
DateTime? timestamp,
}) : timestamp = timestamp ?? DateTime.now();
@override
void execute() {
// 添加网络
}
@override
void undo() {
// 删除网络
}
@override
Map<String, dynamic> toJson() {
return {
'type': 'netAdd',
'timestamp': timestamp.millisecondsSinceEpoch,
'net': net,
};
}
factory AddNetCommand.fromJson(Map<String, dynamic> json) {
return AddNetCommand(
net: json['net'] as Map<String, dynamic>,
timestamp: DateTime.fromMillisecondsSinceEpoch(json['timestamp'] as int),
);
}
}
/// 属性变更命令
class PropertyChangeCommand implements Command {
@override
final OperationType type = OperationType.propertyChange;
@override
final DateTime timestamp;
final String objectId;
final String propertyName;
final dynamic oldValue;
final dynamic newValue;
PropertyChangeCommand({
required this.objectId,
required this.propertyName,
required this.oldValue,
required this.newValue,
DateTime? timestamp,
}) : timestamp = timestamp ?? DateTime.now();
@override
void execute() {
// 应用新值
}
@override
void undo() {
// 恢复旧值
}
@override
Map<String, dynamic> toJson() {
return {
'type': 'propertyChange',
'timestamp': timestamp.millisecondsSinceEpoch,
'objectId': objectId,
'propertyName': propertyName,
'oldValue': oldValue,
'newValue': newValue,
};
}
factory PropertyChangeCommand.fromJson(Map<String, dynamic> json) {
return PropertyChangeCommand(
objectId: json['objectId'] as String,
propertyName: json['propertyName'] as String,
oldValue: json['oldValue'],
newValue: json['newValue'],
timestamp: DateTime.fromMillisecondsSinceEpoch(json['timestamp'] as int),
);
}
}
/// 快照命令
class SnapshotCommand implements Command {
@override
final OperationType type = OperationType.snapshot;
@override
final DateTime timestamp;
final Map<String, dynamic> fullState;
SnapshotCommand({
required this.fullState,
DateTime? timestamp,
}) : timestamp = timestamp ?? DateTime.now();
@override
void execute() {
// 保存完整状态
}
@override
void undo() {
// 快照不支持撤销
}
@override
Map<String, dynamic> toJson() {
return {
'type': 'snapshot',
'timestamp': timestamp.millisecondsSinceEpoch,
'fullState': fullState,
};
}
factory SnapshotCommand.fromJson(Map<String, dynamic> json) {
return SnapshotCommand(
fullState: json['fullState'] as Map<String, dynamic>,
timestamp: DateTime.fromMillisecondsSinceEpoch(json['timestamp'] as int),
);
}
}
// ============================================================================
// 操作历史记录
// ============================================================================
/// 操作历史记录管理器
class OperationHistory {
final List<Command> _undoStack = [];
final List<Command> _redoStack = [];
/// 最大历史记录数 (移动端建议50-100)
final int maxStackSize;
/// 快照间隔 (每 N 次操作生成一个快照)
final int snapshotInterval;
/// 当前操作计数 (用于快照生成)
int _operationCount = 0;
OperationHistory({
this.maxStackSize = 50,
this.snapshotInterval = 100,
});
/// 添加操作到历史记录
void push(Command command) {
// 执行命令
command.execute();
// 添加到撤销栈
_undoStack.add(command);
_operationCount++;
// 清空重做栈 (新的操作使重做无效)
_redoStack.clear();
// 检查是否需要生成快照
if (_operationCount % snapshotInterval == 0) {
_generateSnapshot();
}
// 限制栈大小
if (_undoStack.length > maxStackSize) {
_undoStack.removeAt(0);
}
}
/// 撤销操作
Command? undo() {
if (_undoStack.isEmpty) {
return null;
}
final command = _undoStack.removeLast();
command.undo();
_redoStack.add(command);
return command;
}
/// 重做操作
Command? redo() {
if (_redoStack.isEmpty) {
return null;
}
final command = _redoStack.removeLast();
command.execute();
_undoStack.add(command);
return command;
}
/// 是否可以撤销
bool get canUndo => _undoStack.isNotEmpty;
/// 是否可以重做
bool get canRedo => _redoStack.isNotEmpty;
/// 获取撤销栈大小
int get undoStackSize => _undoStack.length;
/// 获取重做栈大小
int get redoStackSize => _redoStack.length;
/// 清除历史记录
void clear() {
_undoStack.clear();
_redoStack.clear();
_operationCount = 0;
}
/// 生成快照
void _generateSnapshot() {
// 快照由外部提供完整状态
// 这里只生成标记
}
/// 从快照恢复
void restoreFromSnapshot(Map<String, dynamic> snapshot) {
clear();
// 恢复状态由外部处理
}
/// 序列化为 JSON
Map<String, dynamic> toJson() {
return {
'undoStack': _undoStack.map((c) => c.toJson()).toList(),
'redoStack': _redoStack.map((c) => c.toJson()).toList(),
'operationCount': _operationCount,
};
}
/// 从 JSON 反序列化
factory OperationHistory.fromJson(Map<String, dynamic> json) {
final history = OperationHistory(
maxStackSize: json['maxStackSize'] as int? ?? 50,
snapshotInterval: json['snapshotInterval'] as int? ?? 100,
);
final undoList = json['undoStack'] as List? ?? [];
for (final cmdJson in undoList) {
history._undoStack.add(Command.fromJson(cmdJson as Map<String, dynamic>));
}
final redoList = json['redoStack'] as List? ?? [];
for (final cmdJson in redoList) {
history._redoStack.add(Command.fromJson(cmdJson as Map<String, dynamic>));
}
history._operationCount = json['operationCount'] as int? ?? 0;
return history;
}
}
// ============================================================================
// 增量保存管理器
// ============================================================================
/// 增量保存管理器
///
/// 管理快照和增量日志的混合存储
class IncrementalSaveManager {
/// 操作历史记录
final OperationHistory history;
/// 当前设计状态
Map<String, dynamic>? _currentState;
/// 最后一个快照
Map<String, dynamic>? _lastSnapshot;
/// 快照后的操作日志
final List<Command> _deltaLog = [];
/// 自动保存间隔 (毫秒)
final int autoSaveInterval;
/// 是否自动保存
bool _autoSaveEnabled = false;
IncrementalSaveManager({
OperationHistory? history,
this.autoSaveInterval = 30000, // 30 秒
}) : history = history ?? OperationHistory();
/// 设置当前状态
void setCurrentState(Map<String, dynamic> state) {
_currentState = state;
}
/// 记录操作
void recordOperation(Command command) {
history.push(command);
_deltaLog.add(command);
// 标记为脏数据
if (_currentState != null) {
_currentState!['metadata'] ??= {};
_currentState!['metadata']['isDirty'] = true;
}
}
/// 创建快照
void createSnapshot(Map<String, dynamic> fullState) {
_lastSnapshot = Map<String, dynamic>.from(fullState);
_deltaLog.clear();
// 记录快照命令
history.push(SnapshotCommand(fullState: fullState));
// 清除脏标记
fullState['metadata'] ??= {};
fullState['metadata']['isDirty'] = false;
fullState['metadata']['lastSavedAt'] = DateTime.now().millisecondsSinceEpoch;
}
/// 保存 (快照 + 增量)
IncrementalSaveData save() {
return IncrementalSaveData(
snapshot: _lastSnapshot,
deltaLog: _deltaLog.map((c) => c.toJson()).toList(),
timestamp: DateTime.now(),
);
}
/// 从保存数据恢复
Map<String, dynamic>? restore(IncrementalSaveData saveData) {
if (saveData.snapshot != null) {
_currentState = Map<String, dynamic>.from(saveData.snapshot!);
}
// 应用增量操作
for (final cmdJson in saveData.deltaLog) {
final command = Command.fromJson(cmdJson);
command.execute();
_applyCommandToState(command);
}
return _currentState;
}
/// 应用命令到状态
void _applyCommandToState(Command command) {
// 根据命令类型更新状态
switch (command.type) {
case OperationType.componentAdd:
_applyAddComponent(command as AddComponentCommand);
break;
case OperationType.componentMove:
_applyMoveComponent(command as MoveComponentCommand);
break;
case OperationType.componentRotate:
_applyRotateComponent(command as RotateComponentCommand);
break;
case OperationType.componentDelete:
_applyDeleteComponent(command as DeleteComponentCommand);
break;
case OperationType.netAdd:
_applyAddNet(command as AddNetCommand);
break;
case OperationType.propertyChange:
_applyPropertyChange(command as PropertyChangeCommand);
break;
case OperationType.snapshot:
_applySnapshot(command as SnapshotCommand);
break;
default:
break;
}
}
void _applyAddComponent(AddComponentCommand command) {
if (_currentState == null) return;
final tables = _currentState!['tables'] as Map<String, dynamic>?;
if (tables == null) return;
final components = tables['components'] as List?;
if (components != null) {
components.add(command.component);
}
}
void _applyMoveComponent(MoveComponentCommand command) {
if (_currentState == null) return;
final tables = _currentState!['tables'] as Map<String, dynamic>?;
if (tables == null) return;
final components = tables['components'] as List?;
if (components != null) {
for (final comp in components) {
if (comp is Map && comp['id'] == command.componentId) {
comp['position'] ??= {};
comp['position']['x'] = command.newX;
comp['position']['y'] = command.newY;
break;
}
}
}
}
void _applyRotateComponent(RotateComponentCommand command) {
if (_currentState == null) return;
final tables = _currentState!['tables'] as Map<String, dynamic>?;
if (tables == null) return;
final components = tables['components'] as List?;
if (components != null) {
for (final comp in components) {
if (comp is Map && comp['id'] == command.componentId) {
comp['position'] ??= {};
comp['position']['rotation'] = command.newRotation;
break;
}
}
}
}
void _applyDeleteComponent(DeleteComponentCommand command) {
if (_currentState == null) return;
final tables = _currentState!['tables'] as Map<String, dynamic>?;
if (tables == null) return;
final components = tables['components'] as List?;
if (components != null) {
components.removeWhere((comp) =>
comp is Map && comp['id'] == command.componentId
);
}
}
void _applyAddNet(AddNetCommand command) {
if (_currentState == null) return;
final tables = _currentState!['tables'] as Map<String, dynamic>?;
if (tables == null) return;
final nets = tables['nets'] as List?;
if (nets != null) {
nets.add(command.net);
}
}
void _applyPropertyChange(PropertyChangeCommand command) {
if (_currentState == null) return;
// 查找对象并更新属性
final tables = _currentState!['tables'] as Map<String, dynamic>?;
if (tables == null) return;
for (final table in tables.values) {
if (table is List) {
for (final obj in table) {
if (obj is Map && obj['id'] == command.objectId) {
obj[command.propertyName] = command.newValue;
return;
}
}
}
}
}
void _applySnapshot(SnapshotCommand command) {
_currentState = Map<String, dynamic>.from(command.fullState);
_lastSnapshot = Map<String, dynamic>.from(command.fullState);
}
/// 启用自动保存
void enableAutoSave() {
_autoSaveEnabled = true;
}
/// 禁用自动保存
void disableAutoSave() {
_autoSaveEnabled = false;
}
/// 获取当前状态
Map<String, dynamic>? get currentState => _currentState;
/// 获取最后快照
Map<String, dynamic>? get lastSnapshot => _lastSnapshot;
/// 是否有未保存的更改
bool get isDirty => _deltaLog.isNotEmpty;
/// 获取增量日志大小
int get deltaLogSize => _deltaLog.length;
}
// ============================================================================
// 保存数据
// ============================================================================
/// 增量保存数据
class IncrementalSaveData {
/// 完整快照 (可能为 null如果是纯增量保存)
final Map<String, dynamic>? snapshot;
/// 增量操作日志
final List<Map<String, dynamic>> deltaLog;
/// 保存时间戳
final DateTime timestamp;
IncrementalSaveData({
this.snapshot,
this.deltaLog = const [],
required this.timestamp,
});
/// 序列化为 JSON 字符串
String toJsonString() {
return jsonEncode({
'snapshot': snapshot,
'deltaLog': deltaLog,
'timestamp': timestamp.millisecondsSinceEpoch,
});
}
/// 从 JSON 字符串反序列化
factory IncrementalSaveData.fromJsonString(String jsonString) {
final json = jsonDecode(jsonString) as Map<String, dynamic>;
return IncrementalSaveData(
snapshot: json['snapshot'] as Map<String, dynamic>?,
deltaLog: (json['deltaLog'] as List? ?? [])
.map((e) => e as Map<String, dynamic>)
.toList(),
timestamp: DateTime.fromMillisecondsSinceEpoch(json['timestamp'] as int),
);
}
/// 序列化为字节 (用于文件存储)
Uint8List toBytes() {
final jsonString = toJsonString();
return Uint8List.fromList(utf8.encode(jsonString));
}
/// 从字节反序列化
factory IncrementalSaveData.fromBytes(Uint8List bytes) {
final jsonString = utf8.decode(bytes);
return IncrementalSaveData.fromJsonString(jsonString);
}
}
// ============================================================================
// 断点恢复
// ============================================================================
/// 断点恢复管理器
class CheckpointManager {
/// 检查点列表
final List<Checkpoint> _checkpoints = [];
/// 最大检查点数
final int maxCheckpoints;
CheckpointManager({this.maxCheckpoints = 10});
/// 创建检查点
void createCheckpoint(
String name,
Map<String, dynamic> state,
String reason,
) {
final checkpoint = Checkpoint(
name: name,
state: Map<String, dynamic>.from(state),
reason: reason,
timestamp: DateTime.now(),
);
_checkpoints.add(checkpoint);
// 限制检查点数量
if (_checkpoints.length > maxCheckpoints) {
_checkpoints.removeAt(0);
}
}
/// 获取最新检查点
Checkpoint? getLatestCheckpoint() {
if (_checkpoints.isEmpty) {
return null;
}
return _checkpoints.last;
}
/// 获取指定名称的检查点
Checkpoint? getCheckpoint(String name) {
for (final checkpoint in _checkpoints) {
if (checkpoint.name == name) {
return checkpoint;
}
}
return null;
}
/// 从检查点恢复
Map<String, dynamic>? restoreFromCheckpoint(String name) {
final checkpoint = getCheckpoint(name);
return checkpoint?.state;
}
/// 从最新检查点恢复
Map<String, dynamic>? restoreFromLatest() {
final checkpoint = getLatestCheckpoint();
return checkpoint?.state;
}
/// 清除所有检查点
void clear() {
_checkpoints.clear();
}
/// 获取检查点列表
List<Checkpoint> get checkpoints => List.unmodifiable(_checkpoints);
}
/// 检查点
class Checkpoint {
final String name;
final Map<String, dynamic> state;
final String reason;
final DateTime timestamp;
Checkpoint({
required this.name,
required this.state,
required this.reason,
required this.timestamp,
});
/// 序列化为 JSON
Map<String, dynamic> toJson() {
return {
'name': name,
'state': state,
'reason': reason,
'timestamp': timestamp.millisecondsSinceEpoch,
};
}
/// 从 JSON 反序列化
factory Checkpoint.fromJson(Map<String, dynamic> json) {
return Checkpoint(
name: json['name'] as String,
state: json['state'] as Map<String, dynamic>,
reason: json['reason'] as String,
timestamp: DateTime.fromMillisecondsSinceEpoch(json['timestamp'] as int),
);
}
}
// ============================================================================
// 公共 API
// ============================================================================
/// 创建增量保存管理器
IncrementalSaveManager createIncrementalSaveManager({
int maxHistorySize = 50,
int snapshotInterval = 100,
int autoSaveInterval = 30000,
}) {
final history = OperationHistory(
maxStackSize: maxHistorySize,
snapshotInterval: snapshotInterval,
);
return IncrementalSaveManager(
history: history,
autoSaveInterval: autoSaveInterval,
);
}
/// 创建断点恢复管理器
CheckpointManager createCheckpointManager({int maxCheckpoints = 10}) {
return CheckpointManager(maxCheckpoints: maxCheckpoints);
}