312 lines
7.1 KiB
Dart
312 lines
7.1 KiB
Dart
import 'dart:convert';
|
||
import 'dart:async';
|
||
|
||
// ======================== 枚举定义 ========================
|
||
/// 轨迹模式枚举
|
||
enum TPMode {
|
||
NAVIGATION, // 导航模式:绘制规划已完成路径和当前路径
|
||
LOCATION, // 定位模式:仅绘制当前点(永远只有1个)
|
||
TRACK, // 轨迹模式:实时绘制历史轨迹
|
||
}
|
||
|
||
/// 导航模式下的操作动作枚举
|
||
enum TPAction {
|
||
UPDATE, // 更新当前点
|
||
ADD, // 添加完成点
|
||
}
|
||
|
||
// ======================== 尝试锁实现 ========================
|
||
class TryLock {
|
||
bool _locked = false;
|
||
|
||
bool tryLock() {
|
||
if (_locked) return false;
|
||
_locked = true;
|
||
return true;
|
||
}
|
||
|
||
void release() {
|
||
_locked = false;
|
||
}
|
||
|
||
bool get isLocked => _locked;
|
||
}
|
||
|
||
// ======================== 环形队列实现 ========================
|
||
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;
|
||
|
||
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();
|
||
}
|
||
|
||
/// 复位所有状态
|
||
void reset() {
|
||
_queue.clear();
|
||
_tracePoint.clear();
|
||
_completePointIndex = 0;
|
||
_lock.release();
|
||
}
|
||
|
||
/// 设置模式
|
||
void setMode(TPMode mode) {
|
||
reset();
|
||
_mode = mode;
|
||
}
|
||
|
||
TPMode getMode() {
|
||
return _mode;
|
||
}
|
||
|
||
/// 添加/更新轨迹点
|
||
void upsert(T point, [TPAction act = TPAction.UPDATE]) {
|
||
// 严格按模式分发
|
||
switch (_mode) {
|
||
case TPMode.NAVIGATION:
|
||
if (act == TPAction.UPDATE) {
|
||
_updateCurrentPoint(point);
|
||
} else {
|
||
_addCompletePoint(point);
|
||
}
|
||
break;
|
||
|
||
case TPMode.LOCATION:
|
||
// LOCATION 永远只保留最新一个点
|
||
_updateCurrentPoint(point);
|
||
break;
|
||
|
||
case TPMode.TRACK:
|
||
// TRACK 每个点都直接添加,不进队列
|
||
_addCompletePoint(point);
|
||
break;
|
||
}
|
||
}
|
||
|
||
/// 获取轨迹列表
|
||
List<T>? getTracePoint() {
|
||
if (_lock.tryLock()) {
|
||
final trace = List<T>.from(_tracePoint);
|
||
_lock.release();
|
||
return trace;
|
||
} else {
|
||
print('getTracePoint: lock!!!');
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// 更新当前点(LOCATION 永远只有1个)
|
||
void _updateCurrentPoint(T point) {
|
||
if (_lock.tryLock()) {
|
||
try {
|
||
switch (_mode) {
|
||
case TPMode.NAVIGATION:
|
||
if (_tracePoint.isNotEmpty) {
|
||
_tracePoint = _tracePoint.sublist(0, _completePointIndex);
|
||
_tracePoint.add(point);
|
||
onCurrentPointUpdated?.call(point);
|
||
}
|
||
break;
|
||
|
||
case TPMode.LOCATION:
|
||
// 核心:永远清空,只保留最新一个
|
||
_tracePoint.clear();
|
||
_tracePoint.add(point);
|
||
onCurrentPointUpdated?.call(point);
|
||
break;
|
||
|
||
default:
|
||
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) {
|
||
print('addCompletePoint failure: $e');
|
||
} finally {
|
||
_lock.release();
|
||
}
|
||
} else {
|
||
if (_mode != TPMode.TRACK) {
|
||
// TRACK 模式不加队列,直接丢弃旧的保证流畅
|
||
_queue.enter(point);
|
||
}
|
||
print('addCompletePoint: lock!!!');
|
||
}
|
||
}
|
||
|
||
/// 内部添加点
|
||
void _addPoint(T point) {
|
||
if (_mode == TPMode.LOCATION) {
|
||
// LOCATION 强制只保留1个
|
||
_tracePoint.clear();
|
||
_tracePoint.add(point);
|
||
_completePointIndex = 1;
|
||
return;
|
||
}
|
||
|
||
final hasCurrent = _tracePoint.isNotEmpty && _completePointIndex < _tracePoint.length;
|
||
|
||
if (hasCurrent) {
|
||
_tracePoint = _tracePoint.sublist(0, _completePointIndex);
|
||
_tracePoint.add(point);
|
||
} else {
|
||
_tracePoint.add(point);
|
||
}
|
||
|
||
_completePointIndex++;
|
||
}
|
||
}
|