Files
flutterApp/lib/features/home/presentation/widgets/common/tracepoint.dart

304 lines
7.2 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.

import 'dart:async';
import 'dart:convert';
import 'package:flutter/foundation.dart';
// ======================== 枚举定义 ========================
/// 轨迹模式枚举
enum TPMode {
NAVIGATION, // 导航模式: 绘制规划已完成路径和当前路径
LOCATION, // 定位模式: 绘制当前点
TRACK, // 轨迹模式: 实时绘制历史轨迹
}
/// 导航模式下的操作动作枚举
enum TPAction {
UPDATE, // 更新当前点
ADD, // 添加完成点
}
// ======================== 尝试锁实现 ========================
class TryLock {
bool _locked = false;
bool tryLock() {
if (_locked) return false;
_locked = true;
return true;
}
void release() {
_locked = false;
}
}
// ======================== 环形队列实现 ========================
class CircQueue<T> {
final int capacity;
final List<T?> buffer;
int head = 0;
int tail = 0;
bool isFull = false;
final bool deepCopy;
final int maxMemoryBytes;
int currentMemoryBytes = 0;
CircQueue(this.capacity, {this.deepCopy = true, this.maxMemoryBytes = 1024 * 1024}) : buffer = List<T?>.filled(capacity, null);
void clear() {
head = tail;
isFull = false;
currentMemoryBytes = 0;
}
bool isEmpty() {
return head == tail && !isFull;
}
bool isFullFn() {
return isFull;
}
int _estimateSize(T item) {
try {
return utf8.encode(jsonEncode(item)).length;
} catch (e) {
return 0;
}
}
T? _deepClone(T? item) {
if (!deepCopy || item == null) return item;
try {
return jsonDecode(jsonEncode(item)) as T;
} catch (e) {
return item;
}
}
bool enter(T item) {
final itemSize = _estimateSize(item);
if (isFullFn() || (currentMemoryBytes + itemSize > maxMemoryBytes)) {
return false;
}
final clone = _deepClone(item);
buffer[tail] = clone;
tail = (tail + 1) % capacity;
currentMemoryBytes += itemSize;
if (tail == head) isFull = true;
return true;
}
T? out() {
if (isEmpty()) return null;
final item = buffer[head];
if (item != null) {
currentMemoryBytes -= _estimateSize(item);
}
head = (head + 1) % capacity;
isFull = false;
return _deepClone(item);
}
bool discard(int len) {
for (int i = 0; i < len; i++) {
if (isEmpty()) return false;
final index = (tail - 1 + capacity) % capacity;
final item = buffer[index];
if (item != null) {
currentMemoryBytes -= _estimateSize(item);
}
tail = index;
isFull = false;
}
return true;
}
int getDepth() {
return isFull ? capacity : (tail + capacity - head) % capacity;
}
Future<bool> send(T item, {int timeoutMs = 0}) async {
final start = DateTime.now().millisecondsSinceEpoch;
while (!enter(item)) {
if (timeoutMs > 0 && DateTime.now().millisecondsSinceEpoch - start >= timeoutMs) {
return false;
}
await Future.delayed(const Duration(milliseconds: 1));
}
return true;
}
Future<T?> recv({int timeoutMs = 0}) async {
final start = DateTime.now().millisecondsSinceEpoch;
while (true) {
final item = out();
if (item != null) return item;
if (timeoutMs > 0 && DateTime.now().millisecondsSinceEpoch - start >= timeoutMs) {
return null;
}
await Future.delayed(const Duration(milliseconds: 1));
}
}
List<T?> toList() {
final result = <T?>[];
int i = head;
int count = getDepth();
while (count-- > 0) {
result.add(_deepClone(buffer[i]));
i = (i + 1) % capacity;
}
return result;
}
}
// ======================== 轨迹点核心类 ========================
class TracePoint<T> {
late final CircQueue<T> _queue;
List<T> _tracePoint = [];
int _completePointIndex = 0;
late final TryLock _lock;
TPMode _mode = TPMode.LOCATION;
// 回调事件(对应JS原版emit)
void Function(T)? onCurrentPointUpdated;
void Function(T)? onCompletePointAdded;
// 测试专用方法
bool tryLock() => _lock.tryLock();
void release() => _lock.release();
TracePoint({int queueCapacity = 5, int maxMemoryBytes = 1024 * 1024}) {
_queue = CircQueue<T>(queueCapacity, deepCopy: true, maxMemoryBytes: maxMemoryBytes);
_lock = TryLock();
reset();
}
/// 复位所有状态(和JS完全一致)
void reset() {
_queue.clear();
_tracePoint.clear();
_completePointIndex = 0;
_lock.release();
}
/// 设置工作模式
void setMode(TPMode mode) {
reset();
_mode = mode;
}
TPMode getMode() {
return _mode;
}
/// 添加/更新点(核心API,和JS逻辑完全一致)
void upsert(T point, [TPAction act = TPAction.UPDATE]) {
if (point == null) {
debugPrint('upsert: point is null');
return;
}
switch (_mode) {
case TPMode.NAVIGATION:
if (act == TPAction.UPDATE) {
_updateCurrentPoint(point);
} else {
_addCompletePoint(point);
}
break;
case TPMode.LOCATION:
_updateCurrentPoint(point);
break;
case TPMode.TRACK:
_addCompletePoint(point);
break;
}
}
/// 获取轨迹列表(线程安全拷贝)
List<T>? getTracePoint() {
if (_lock.tryLock()) {
final trace = List<T>.from(_tracePoint);
_lock.release();
return trace;
} else {
debugPrint('getTracePoint: lock!!!');
return null;
}
}
/// 更新当前点(导航/定位模式共用)
void _updateCurrentPoint(T point) {
if (!_lock.tryLock()) {
debugPrint('updateCurrentPoint: lock!!!, discard current point');
return;
}
try {
switch (_mode) {
case TPMode.NAVIGATION:
if (_tracePoint.isEmpty) {
debugPrint('updateCurrentPoint: wait first completed point, discard');
} else {
_tracePoint = _tracePoint.sublist(0, _completePointIndex);
_tracePoint.add(point);
onCurrentPointUpdated?.call(point);
}
break;
case TPMode.LOCATION:
_tracePoint = [point];
onCurrentPointUpdated?.call(point);
break;
case TPMode.TRACK:
break;
}
} finally {
_lock.release();
}
}
/// 添加完成点(导航/轨迹模式共用)
void _addCompletePoint(T point) {
if (_lock.tryLock()) {
try {
// 先处理队列积压
while (!_queue.isEmpty()) {
final queuedPoint = _queue.out();
if (queuedPoint != null) {
_addPoint(queuedPoint);
}
}
_addPoint(point);
onCompletePointAdded?.call(point);
} catch (e) {
debugPrint('addCompletePoint failure: $e');
} finally {
_lock.release();
}
} else {
// 锁失败 → 入队列等待(JS原版核心逻辑)
_queue.enter(point);
debugPrint('addCompletePoint: lock!!!, enter queue');
}
}
/// 内部真正执行添加点逻辑(和JS完全一致)
void _addPoint(T point) {
final hasCurrentPoint = _tracePoint.isNotEmpty && _completePointIndex < _tracePoint.length;
if (hasCurrentPoint) {
_tracePoint = _tracePoint.sublist(0, _completePointIndex);
_tracePoint.insert(_completePointIndex, point);
} else {
_tracePoint.add(point);
}
_completePointIndex++;
}
}