304 lines
7.2 KiB
Dart
304 lines
7.2 KiB
Dart
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++;
|
||
}
|
||
}
|