using LoopViewerApp;
using Newtonsoft.Json;
using SimpleLite.RCS;
using SimpleLite.RCS.CarTypes;
using SimpleCore;
using SimpleCore.Compiler;
using SimpleCore.Library;
using SimpleCore.PropType;
using StandardScene.CarTypes;
using StandardScene.Chained.Loop;
using StandardScene.Model;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace StandardScene.Chained
{
///
/// 抽象环线任务基类
/// 核心功能:
///
/// - 支持多种启动类型:AutoLoop(自动循环)、Api、Plc、ButtonBox(外部触发)
/// - 支持多种任务类别:Loop(普通循环)、BranchPoint(分流点)、JoinPoint(汇合点)
/// - 支持流量控制:限制目标站点的最大车辆数
/// - 支持优先级调度:高优先级任务优先处理
/// - 支持配置热更新:tasklist.json 文件变更自动刷新
/// - 外部触发接口:子类可重写 OnApiTrigger/OnPlcTrigger/OnButtonTrigger 实现业务逻辑
///
///
///
/// 子类实现示例:
///
/// public class MyLoopMission : AbstractLoopMission
/// {
/// protected override ExternalTriggerResult OnApiTrigger(int currentSiteId, LoopTask task, Car car)
/// {
/// // 返回 UseTarget(目标站点) - 使用子类指定的目标
/// // 返回 UseConfigTarget() - 使用配置文件目标
/// // 返回 Fail() - 不分配任务
/// return ExternalTriggerResult.UseConfigTarget();
/// }
/// }
///
///
public abstract class AbstractLoopMission : Mission, IDisposable
{
#region 常量定义
/// 策略同步线程轮询间隔(毫秒)
private const int STRATEGY_SYNC_INTERVAL_MS = 1000;
/// 业务逻辑线程轮询间隔(毫秒)
private const int LOGIC_LOOP_INTERVAL_MS = 300;
/// 文件变更防抖延迟(毫秒)
private const int FILE_CHANGE_DEBOUNCE_MS = 50;
/// 异常恢复等待时间(毫秒)
private const int ERROR_RECOVERY_DELAY_MS = 3000;
/// 脚本异常检测触发延迟(毫秒)
private const int SCRIPT_ERROR_TRIGGER_DELAY_MS = 3000;
#endregion
#region 外部触发结果类
///
/// 外部触发处理结果类
/// 用于子类重写外部触发接口时返回处理结果,决定目标站点的分配方式
///
///
/// 使用示例:
///
/// // 方式1:使用子类指定的目标站点
/// return ExternalTriggerResult.UseTarget(200);
///
/// // 方式2:使用配置文件中的目标站点(相当于返回 true)
/// return ExternalTriggerResult.UseConfigTarget();
///
/// // 方式3:业务失败,不分配任务
/// return ExternalTriggerResult.Fail();
///
///
public class ExternalTriggerResult
{
///
/// 是否处理成功
/// true = 成功,将根据 CustomTargetSiteId 分配目标站点
/// false = 失败或跳过,不分配任务
///
public bool Success { get; set; }
///
/// 子类指定的目标站点ID
/// 有值时使用此目标站点
/// null 时使用配置文件中的 TargetStationId
///
public int? CustomTargetSiteId { get; set; }
///
/// 创建成功结果,使用子类指定的目标站点
///
/// 目标站点ID
/// 包含指定目标的成功结果
public static ExternalTriggerResult UseTarget(int targetSiteId)
{
return new ExternalTriggerResult
{
Success = true,
CustomTargetSiteId = targetSiteId
};
}
///
/// 创建成功结果,使用配置文件中的目标站点
/// 等同于业务处理成功返回 true 的效果
///
/// 使用配置目标的成功结果
public static ExternalTriggerResult UseConfigTarget()
{
return new ExternalTriggerResult
{
Success = true,
CustomTargetSiteId = null
};
}
///
/// 创建失败结果,不分配任务
/// 等同于业务处理失败返回 false 的效果
///
/// 失败结果
public static ExternalTriggerResult Fail()
{
return new ExternalTriggerResult { Success = false };
}
}
#endregion
#region 状态与字段
///
/// 环线任务状态类
///
public class AbstractLoopMissionStatus : MissionStatus
{
/// 当前任务数量
public int TaskCount { get; set; }
/// 最后一次错误信息
public string LastError { get; set; } = "/";
}
/// 任务状态对象
/* [JsonIgnore]
public override MissionStatus status { get; set; } = new LoopMissionStatus();*/
/// 策略同步线程
[JsonIgnore]
private Thread _strategyThread;
/// 策略同步线程运行标志
[JsonIgnore]
private volatile bool _strategyRunning;
/// 业务逻辑线程
[JsonIgnore]
private Thread _logicThread;
/// 业务逻辑线程运行标志
[JsonIgnore]
private volatile bool _logicRunning;
/// 任务集合同步锁
[JsonIgnore]
private readonly object _tasksLock = new object();
/// 任务列表(线程安全访问需使用 _tasksLock)
[JsonIgnore]
protected readonly List Tasks = new List();
/// 触发器适配器列表
[JsonIgnore]
private readonly List _adapters = new List();
/// 任务策略实例
private ITaskStrategy _taskStrategy;
///
/// 任务策略属性
/// 支持热更新,设置时自动订阅/取消订阅变更事件
///
public ITaskStrategy TaskStrategy
{
get => _taskStrategy;
set
{
// 取消旧策略的事件订阅
if (_taskStrategy is JsonFileTaskStrategy oldStrategy)
{
oldStrategy.Changed -= OnTaskStrategyChanged;
oldStrategy.DisposeWatcher();
}
// 设置新策略(null 时使用默认策略)
_taskStrategy = value ?? new JsonFileTaskStrategy();
// 订阅新策略的变更事件
if (_taskStrategy is JsonFileTaskStrategy newStrategy)
{
newStrategy.Changed += OnTaskStrategyChanged;
newStrategy.EnsureWatcher();
}
}
}
/// 任务列表变更事件
public event EventHandler TaskListChanged;
#endregion
#region 构造函数
///
/// 初始化抽象环线任务基类
/// 自动使用默认的 JSON 文件策略并加载初始任务配置
///
protected AbstractLoopMission()
{
TaskStrategy = new JsonFileTaskStrategy();
ApplyTaskStrategy();
}
#endregion
#region 任务管理方法
///
/// 获取当前任务列表的只读副本(线程安全)
///
/// 任务列表快照
public IReadOnlyList GetTasks()
{
lock (_tasksLock)
{
return Tasks.ToArray();
}
}
///
/// 添加任务到列表末尾
///
/// 要添加的任务
/// 新任务的索引
/// task 为 null 时抛出
public int EnqueueTask(LoopTask task)
{
if (task == null)
throw new ArgumentNullException(nameof(task));
lock (_tasksLock)
{
Tasks.Add(task);
int index = Tasks.Count - 1;
// 触发任务添加事件
TaskListChanged?.Invoke(this, new TaskListChangedEventArgs(TaskChangeType.Added, index, task));
// 更新状态中的任务计数
((AbstractLoopMissionStatus)status).TaskCount = Tasks.Count;
return index;
}
}
///
/// 更新指定索引的任务
///
/// 任务索引
/// 新的任务数据
/// 是否更新成功
public bool UpdateTask(int index, LoopTask task)
{
lock (_tasksLock)
{
if (index < 0 || index >= Tasks.Count)
return false;
Tasks[index] = task;
// 触发任务更新事件
TaskListChanged?.Invoke(this, new TaskListChangedEventArgs(TaskChangeType.Updated, index, task));
return true;
}
}
///
/// 移除指定索引的任务
///
/// 任务索引
/// 是否移除成功
public bool RemoveTask(int index)
{
lock (_tasksLock)
{
if (index < 0 || index >= Tasks.Count)
return false;
LoopTask task = Tasks[index];
Tasks.RemoveAt(index);
// 触发任务移除事件
TaskListChanged?.Invoke(this, new TaskListChangedEventArgs(TaskChangeType.Removed, index, task));
// 更新状态中的任务计数
((AbstractLoopMissionStatus)status).TaskCount = Tasks.Count;
return true;
}
}
#endregion
#region 车辆查找辅助方法
///
/// 查找恰好停在指定站点的车辆
/// 条件:车辆的 holdingLocks 只包含该站点(表示车辆已到达并停稳)
///
/// 站点ID
/// 找到的车辆,未找到返回 null
protected virtual Car FindCarArrivedAtSite(int siteId)
{
try
{
return SimpleLib.GetAllCars()
.OfType()
.FirstOrDefault(car =>
car?.status?.holdingLocks != null &&
car.status.holdingLocks.Length == 1 &&
car.status.holdingLocks[0] == siteId);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[AbstractLoopMission] FindCarArrivedAtSite({siteId}) error: {ex.Message}");
return null;
}
}
///
/// 获取当前在站或正在前往指定站点的所有车辆
/// 用于流量控制统计
///
/// 站点ID
/// 车辆集合
protected IEnumerable GetCarsAtOrHeadingToSite(int siteId)
{
try
{
return SimpleLib.GetAllCars()
.OfType()
.Where(car =>
{
if (car?.status == null)
return false;
// 检查是否在站:holdingLocks 包含该站点
if (car.status.holdingLocks?.Contains(siteId) == true)
return true;
// 检查是否前往:pendingLocks 最后一个锁定点为该站点
if (car.status.pendingLocks.LastOrDefault()==siteId)
{
return true;
}
// 检查是否前往:goalSite 标签指向该站点
/* if (car.tags?.TryGetValue("goalSite", out var destStr) == true &&
int.TryParse(destStr, out int destId) &&
destId == siteId)
return true;*/
return false;
})
.ToList();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[AbstractLoopMission] GetCarsAtOrHeadingToSite({siteId}) error: {ex.Message}");
return Array.Empty();
}
}
///
/// 统计当前在站或正在前往指定站点的车辆数量
///
/// 站点ID
/// 车辆数量
protected int CountCarsAtOrHeadingToSite(int siteId)
{
return GetCarsAtOrHeadingToSite(siteId).Count();
}
///
/// 检查车辆是否空闲(未被占用、未有目标、未执行任务)
///
/// 车辆
/// 是否空闲
protected bool IsCarIdle(Car car)
{
if (car?.tags == null)
return false;
try
{
// 检查是否存在表示忙碌状态的标签
if (car.tags.ContainsKey("occupied")) return false;
if (car.tags.ContainsKey("dest")) return false;
if (car.tags.ContainsKey("deliver")) return false;
return true;
}
catch
{
return false;
}
}
#endregion
#region 流量控制检查
///
/// 检查目标站点是否满足流量控制条件
/// 统计当前在站或正在前往该站点的车辆数量,与最大允许数比较
///
/// 目标站点ID
/// 最大允许车辆数(0或负数表示不限制)
/// true=可以发车,false=已达上限需等待
protected bool CheckTrafficControl(int targetSiteId, int maxAllowed)
{
// 不限制流量的情况
if (maxAllowed <= 0)
return true;
int currentCount = CountCarsAtOrHeadingToSite(targetSiteId);
return currentCount < maxAllowed;
}
#endregion
#region 外部触发接口(子类重写)
///
/// API 触发处理接口
/// 当任务的 StartType 为 Api 时,车辆到达当前站点后会调用此方法
/// 子类重写此方法实现 API 触发的业务逻辑
///
/// 当前站点ID
/// 匹配的任务配置
/// 到达的车辆
///
/// 处理结果:
///
/// - - 使用子类指定的目标站点
/// - - 使用配置文件中的目标站点
/// - - 业务失败,不分配任务
///
///
protected virtual ExternalTriggerResult OnApiTrigger(int currentSiteId, LoopTask task, Car car)
{
// 默认返回失败,子类需重写以启用 API 触发功能
return ExternalTriggerResult.Fail();
}
///
/// PLC 信号触发处理接口
/// 当任务的 StartType 为 Plc 时,车辆到达当前站点后会调用此方法
/// 子类重写此方法实现 PLC 触发的业务逻辑
///
/// 当前站点ID
/// 匹配的任务配置
/// 到达的车辆
/// 处理结果
protected virtual ExternalTriggerResult OnPlcTrigger(int currentSiteId, LoopTask task, Car car)
{
// 默认返回失败,子类需重写以启用 PLC 触发功能
return ExternalTriggerResult.Fail();
}
///
/// 充电条件信号触发处理接口
/// 当任务的 StartType 为 Plc 时,车辆到达当前站点后会调用此方法
/// 子类重写此方法实现 PLC 触发的业务逻辑
///
/// 当前站点ID
/// 匹配的任务配置
/// 到达的车辆
/// 处理结果
protected virtual ExternalTriggerResult OnChargeTrigger(int currentSiteId, LoopTask task, Car car)
{
// 默认返回失败,子类需重写已启用去充电触发功能
return ExternalTriggerResult.Fail();
}
///
/// 按钮盒触发处理接口
/// 当任务的 StartType 为 ButtonBox 时,车辆到达当前站点后会调用此方法
/// 子类重写此方法实现按钮盒触发的业务逻辑
///
/// 当前站点ID
/// 匹配的任务配置
/// 到达的车辆
/// 处理结果
protected virtual ExternalTriggerResult OnButtonTrigger(int currentSiteId, LoopTask task, Car car)
{
// 默认返回失败,子类需重写以启用按钮盒触发功能
return ExternalTriggerResult.Fail();
}
///
/// 根据启动类型调用对应的子类触发接口
///
/// 启动类型
/// 当前站点ID
/// 匹配的任务配置
/// 到达的车辆
/// 子类返回的处理结果
private ExternalTriggerResult InvokeExternalTrigger(TaskStartType startType, int currentSiteId, LoopTask task, Car car)
{
return startType switch
{
TaskStartType.Api => OnApiTrigger(currentSiteId, task, car),
TaskStartType.Plc => OnPlcTrigger(currentSiteId, task, car),
TaskStartType.ButtonBox => OnButtonTrigger(currentSiteId, task, car),
TaskStartType.Charge => OnChargeTrigger(currentSiteId, task, car),
_ => ExternalTriggerResult.Fail()
};
}
#endregion
#region 任务策略管理
///
/// 从任务策略刷新并应用任务列表
/// 读取配置文件内容并替换当前任务列表
///
public void ApplyTaskStrategy()
{
try
{
// 刷新策略数据
TaskStrategy?.Refresh();
var sourceTasks = TaskStrategy?.GetTasks();
if (sourceTasks == null)
return;
lock (_tasksLock)
{
// 清空并重新加载任务
Tasks.Clear();
foreach (var task in sourceTasks)
{
Tasks.Add(task);
}
// 触发任务替换事件
TaskListChanged?.Invoke(this, new TaskListChangedEventArgs(TaskChangeType.Replaced, -1, null));
// 更新状态中的任务计数
((AbstractLoopMissionStatus)status).TaskCount = Tasks.Count;
}
System.Diagnostics.Debug.WriteLine($"[AbstractLoopMission] ApplyTaskStrategy: 加载了 {Tasks.Count} 个任务");
}
catch (Exception ex)
{
// 记录错误信息
((AbstractLoopMissionStatus)status).LastError = ex.Message;
System.Diagnostics.Debug.WriteLine($"[AbstractLoopMission] ApplyTaskStrategy error: {ex}");
}
}
///
/// 任务策略配置文件变更处理
///
private void OnTaskStrategyChanged(object sender, EventArgs e)
{
try
{
ApplyTaskStrategy();
System.Diagnostics.Debug.WriteLine("[AbstractLoopMission] OnTaskStrategyChanged: 配置文件已更新");
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[AbstractLoopMission] OnTaskStrategyChanged error: {ex}");
}
}
#endregion
#region 核心业务逻辑
///
/// 业务逻辑主循环
/// 在 _logicThread 线程中执行,每 500ms 处理一次任务调度
///
protected virtual void ExecuteLogicLoop()
{
while (_logicRunning)
{
try
{
ProcessAutoLoopTasks();
Thread.Sleep(LOGIC_LOOP_INTERVAL_MS);
}
catch (ThreadAbortException)
{
break;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[AbstractLoopMission] ExecuteLogicLoop error: {ex}");
}
}
}
///
/// 处理所有任务(主调度入口)
/// 按顺序处理:自动循环任务 → 外部触发任务 → 分流点任务 → 汇合点任务 → 循环启动动作
///
protected virtual void ProcessAutoLoopTasks()
{
// 获取任务快照(避免长时间持锁)
List snapshot;
lock (_tasksLock)
{
snapshot = Tasks.ToList();
}
// 1. 处理自动循环任务(StartType=AutoLoop, Kind=Loop)
ProcessAutoLoopTypeTasks(snapshot);
// 2. 处理外部触发任务(StartType=Api/Plc/ButtonBox/charge)
ProcessExternalTriggerTasks(snapshot);
// 3. 处理自动分流点任务(StartType=AutoLoop, Kind=BranchPoint)
ProcessBranchPointTasks(snapshot);
// 4. 处理自动汇合点任务(StartType=AutoLoop, Kind=JoinPoint)
ProcessJoinPointTasks(snapshot);
// 5. 执行循环启动动作(处理站点默认目标、执行导航、异常恢复等)
LoopStartAction();
}
///
/// 处理自动循环类型任务
/// 筛选 StartType=AutoLoop 且 Kind=Loop 的任务,按优先级处理
///
/// 任务快照
private void ProcessAutoLoopTypeTasks(List snapshot)
{
// 筛选自动循环任务并按优先级排序
var autoTasks = snapshot
.Where(t => t.StartType == TaskStartType.AutoLoop && t.Kind == TaskKind.Loop)
.OrderByDescending(t => t.Priority)
.ToList();
foreach (var task in autoTasks)
{
try
{
// 查找到达当前站点的车辆
var car = FindCarArrivedAtSite(task.CurrentStationId);
if (car == null)
continue;
// 检查车辆是否可用(SelectCar 返回 1 表示可用)
if (Commons.SelectCar(car) != 1)
continue;
// 流量控制检查
if (!CheckTrafficControl(task.TargetStationId, task.TrafficControl))
{
Diagnosis.Post($"[AbstractLoopMission] AutoLoop: 目标站点 {task.TargetStationId} 流量已满,车辆 {car.name} 等待", "Loop-AutoLoop", true);
continue;
}
// 分配目标站点
AssignCarToTarget(car, task.TargetStationId);
Diagnosis.Post($"[AbstractLoopMission] AutoLoop: 车辆 {car.name} 从站点 {task.CurrentStationId} 自动分配到目标站点 {task.TargetStationId}", "Loop-AutoLoop", true);
}
catch (Exception ex)
{
Diagnosis.Post($"[AbstractLoopMission] ProcessAutoLoopTypeTasks error: {ex}", "Loop-AutoLoop", true);
}
}
}
///
/// 处理外部触发类型任务(API/PLC/ButtonBox)
/// 依次处理各类型的外部触发任务
///
/// 任务快照
private void ProcessExternalTriggerTasks(List snapshot)
{
// 依次处理 API、PLC、ButtonBox 类型任务
ProcessExternalTasksByType(snapshot, TaskStartType.Api);
ProcessExternalTasksByType(snapshot, TaskStartType.Plc);
ProcessExternalTasksByType(snapshot, TaskStartType.ButtonBox);
ProcessExternalTasksByType(snapshot, TaskStartType.Charge);
}
///
/// 按启动类型处理外部触发任务
/// 处理普通循环任务、分流点任务、汇合点任务
///
/// 任务快照
/// 启动类型
private void ProcessExternalTasksByType(List snapshot, TaskStartType startType)
{
// 筛选指定启动类型的普通循环任务
var externalTasks = snapshot
.Where(t => t.StartType == startType && t.Kind == TaskKind.Loop)
.OrderByDescending(t => t.Priority)
.ToList();
foreach (var task in externalTasks)
{
try
{
// 查找到达当前站点的车辆
var car = FindCarArrivedAtSite(task.CurrentStationId);
if (car == null)
continue;
// 检查车辆是否可用
if (Commons.SelectCar(car) != 1)
continue;
// 调用子类接口获取处理结果
ExternalTriggerResult result = InvokeExternalTrigger(startType, task.CurrentStationId, task, car);
// 子类返回失败则跳过
if (result?.Success != true)
continue;
// 确定目标站点:子类指定 > 配置文件
int targetSiteId = result.CustomTargetSiteId ?? task.TargetStationId;
// 流量控制检查
if (!CheckTrafficControl(targetSiteId, task.TrafficControl))
{
Diagnosis.Post($"[AbstractLoopMission] {startType}: 目标站点 {targetSiteId} 流量已满,车辆 {car.name} 等待", $"Loop-{startType}", true);
continue;
}
// 分配目标站点
AssignCarToTarget(car, targetSiteId);
Diagnosis.Post($"[AbstractLoopMission] {startType}: 车辆 {car.name} 从站点 {task.CurrentStationId} 分配到目标站点 {targetSiteId}", $"Loop-{startType}", true);
}
catch (Exception ex)
{
Diagnosis.Post($"[AbstractLoopMission] ProcessExternalTasksByType({startType}) error: {ex}", $"Loop-{startType}", true);
}
}
// 处理分流点类型的外部触发任务
ProcessExternalBranchTasks(snapshot, startType);
// 处理汇合点类型的外部触发任务
ProcessExternalJoinTasks(snapshot, startType);
}
///
/// 处理外部触发的分流点任务
/// 按当前站点分组,优先级排序,调用子类接口后分配目标
///
/// 任务快照
/// 启动类型
private void ProcessExternalBranchTasks(List snapshot, TaskStartType startType)
{
// 按当前站点分组
var branchGroups = snapshot
.Where(t => t.Kind == TaskKind.BranchPoint && t.StartType == startType)
.GroupBy(t => t.CurrentStationId)
.ToList();
foreach (var group in branchGroups)
{
int currentSiteId = group.Key;
// 查找到达当前站点的车辆
var car = FindCarArrivedAtSite(currentSiteId);
if (car == null || Commons.SelectCar(car) != 1)
continue;
// 按优先级排序
var sortedTasks = group.OrderByDescending(t => t.Priority).ToList();
foreach (var task in sortedTasks)
{
// 调用子类接口
ExternalTriggerResult result = InvokeExternalTrigger(startType, currentSiteId, task, car);
if (result?.Success != true)
continue;
// 确定目标:子类指定 > 按优先级选择流量允许的目标
int targetSiteId = result.CustomTargetSiteId ?? SelectBranchTarget(task, snapshot);
if (CheckTrafficControl(targetSiteId, task.TrafficControl))
{
AssignCarToTarget(car, targetSiteId);
Diagnosis.Post($"[AbstractLoopMission] {startType} BranchPoint: 车辆 {car.name} 从分流点 {currentSiteId} 分配到目标 {targetSiteId}", $"BranchPoint-{startType}", true);
break; // 分流点只分配一个目标
}
}
}
}
///
/// 处理外部触发的汇合点任务
/// 按目标站点分组(汇合点),每组中的策略按优先级排列,排列后每个策略根据流量判断,最后采用接口返回结果
///
/// 任务快照
/// 启动类型
private void ProcessExternalJoinTasks(List snapshot, TaskStartType startType)
{
// 按目标站点分组(汇合点)
var joinGroups = snapshot
.Where(t => t.Kind == TaskKind.JoinPoint && t.StartType == startType)
.GroupBy(t => t.TargetStationId)
.ToList();
foreach (var group in joinGroups)
{
int targetSiteId = group.Key;
// 按优先级降序排列组内任务
var sortedTasks = group.OrderByDescending(t => t.Priority).ToList();
// 遍历排序后的任务,根据流量和接口返回结果处理
foreach (var task in sortedTasks)
{
try
{
// 查找到达当前站点的车辆
var car = FindCarArrivedAtSite(task.CurrentStationId);
if (car == null)
continue;
// 检查车辆是否可用
if (Commons.SelectCar(car) != 1)
continue;
// 流量控制检查(优先判断,避免无效的接口调用)
if (!CheckTrafficControl(targetSiteId, task.TrafficControl))
{
Diagnosis.Post($"[AbstractLoopMission] {startType} JoinPoint: 目标站点 {targetSiteId} 流量已满,跳过任务 {task.Id}", $"JoinPoint-{startType}", true);
continue;
}
// 调用子类接口获取处理结果
ExternalTriggerResult result = InvokeExternalTrigger(startType, task.CurrentStationId, task, car);
// 子类返回失败则跳过当前任务
if (result?.Success != true)
continue;
// 确定最终目标:子类指定 > 配置文件
int finalTarget = result.CustomTargetSiteId ?? targetSiteId;
// 如果子类指定了不同的目标,需要再次检查流量
if (finalTarget != targetSiteId && !CheckTrafficControl(finalTarget, task.TrafficControl))
{
Diagnosis.Post($"[AbstractLoopMission] {startType} JoinPoint: 子类指定目标站点 {finalTarget} 流量已满,车辆 {car.name} 等待", $"JoinPoint-{startType}", true);
continue;
}
// 分配目标站点
AssignCarToTarget(car, finalTarget);
Diagnosis.Post($"[AbstractLoopMission] {startType} JoinPoint: 车辆 {car.name} 从站点 {task.CurrentStationId} 汇入目标 {finalTarget}(优先级 {task.Priority})", $"JoinPoint-{startType}", true);
}
catch (Exception ex)
{
Diagnosis.Post($"[AbstractLoopMission] ProcessExternalJoinTasks({startType}) task {task.Id} error: {ex.Message}", $"JoinPoint-{startType}", true);
}
}
}
}
///
/// 循环启动动作
/// 处理站点配置的默认目标、执行路径规划导航、检测并处理脚本异常
/// 注意:此方法不拆分,保持原有逻辑完整性
///
private void LoopStartAction()
{
try
{
foreach (var car in SimpleLib.GetAllCars())
{
// 如果车辆可用且没有目标,尝试从站点配置读取默认目标
if (Commons.SelectCar(car) == 1 && !car.tags.Contains("goalSite"))
{
var carSite = SimpleLib.GetSite(car.GetLastSite());
if (carSite != null && carSite.fields.TryGetValue("goSite", out var dst))
{
Commons.AddOrUpdateTag(car.tags, "goalSite", dst);
}
if (carSite != null && !car.tags.Contains("goalSite"))
{
var taskLine = FindBestTaskForCar((Car)car);
if (taskLine!=null && !taskLine.IsAtStartPoint && !taskLine.IsAtEndPoint)
{
Commons.AddOrUpdateTag(car.tags, "goalSite", taskLine.TargetSiteId.ToString());
}
}
}
// 如果车辆可用且有目标,执行导航
if (Commons.SelectCar(car) == 1 && car.tags.Contains("goalSite"))
{
var goalSite = SimpleLib.GetAllSites()
.FirstOrDefault(s => car.tags.IsEqual("goalSite", s.id.ToString()));
var holdingSite=car.status.holdingLocks.FirstOrDefault();
if (goalSite != null && holdingSite!=goalSite.id )
{
_=GoSite(car, goalSite);
}
else if (goalSite != null && holdingSite == goalSite.id)
{
// 已到达目标,清理 goalSite 标签
car.tags.Remove("goalSite");
car.tags.Remove("loopAssigned");
}
}
// 以下处理声明了脚本异常自恢复能力的车辆(IScriptErrorRecoverable,当前为 Kiva)
if (!(car is IScriptErrorRecoverable recoverable) || !(car is Car mcar))
continue;
// 检测脚本异常状态:车辆被占用但脚本状态为 Error 或 Bad
LadderLogic.TriggerOnce(
mcar.tags.Contains("occupied") &&
(mcar.status.programs.printStatus().Contains("Error") || mcar.status.programs.printStatus().Contains("Bad")),
SCRIPT_ERROR_TRIGGER_DELAY_MS,
() =>
{
// 获取车辆最后持有的站点锁
var lastHoldingLocks = car.status.holdingLocks.First();
var site = SimpleLib.GetSite(lastHoldingLocks);
// 标记站点不可用
site.MakeAllCarsUnavailable();
// 禁止车辆调度
mcar.NoSchedule(true);
Diagnosis.Post($"errStatus ", "NoSchedule", true);
// 重置车辆站点ID
mcar.siteID = -1;
// 更新车辆使用状态
mcar.status.usage.AddUsage("base", new CarUsage.CarUsageInfo()
{
scheduling = false,
refreshing = false
});
// 设置车辆状态为返厂检修
mcar.lstatus = "返厂检修";
// 清理车辆标签
Commons.DeleteTag(mcar.tags, "occupied");
// Commons.DeleteTag(mcar.tags, "goalSite");
// 执行车辆重置
((Car)car).Intercept((_) =>
{
recoverable.RecoverReset(lastHoldingLocks);
});
},
mcar.id
);
}
}
catch (Exception e)
{
Diagnosis.Post($"loop mission error {ExceptionFormatter.FormatEx(e)}");
}
}
///
/// 导航车辆到目标站点
/// 创建路径规划并执行移动任务
///
/// 车辆
/// 目标站点
/// 动作参数,默认为 "/"
/// 是否倒车,默认为 false
public async Task GoSite(AbstractCar car, Site targetSite, string action = "/", bool reverse = false)
{
try
{
// 创建路径规划
var plan = new SegmentPlan() { usingCar = car };
plan.fields["reverse"] = reverse.ToString();
plan.fields["action"] = action;
plan.fields["allow_destination_on_route"] = "true";
// 查找从当前位置到目标站点的路径
plan.FindRoute(SimpleLib.GetSite(car.GetLastSite()), targetSite);
// 编译并执行移动脚本(默认 Forecast)
var program = plan.Compile("move");
// 标记车辆为占用状态
car.tags.Add("occupied", $"go{targetSite.id}");
Console.WriteLine($">>Script:{program.script}");
// 等待任务完成
var tsk = program.Queue();
await tsk;
// 任务完成后清理标签
car.tags.Remove("occupied");
car.tags.Remove("goalSite");
car.tags.Remove("loopAssigned");
}
catch (Exception ex)
{
try
{
car.tags.Remove("occupied");
if (car is Car trafficCar && car.GetLastSite() > 0)
{
var currentSite = SimpleLib.GetSite(car.GetLastSite());
if (currentSite != null)
{
trafficCar.TrafficReset(currentSite, true, strict: false);
}
}
}
catch
{
}
// 记录异常并等待后重试
Diagnosis.Post(
$"{car.name}循环任务报错{ExceptionFormatter.FormatEx(ex)}--去往循环点id:{targetSite.id},name:{targetSite.name}",
$"重新执行循环任务",
true
);
Thread.Sleep(ERROR_RECOVERY_DELAY_MS);
}
}
///
/// 处理自动分流点任务
/// 筛选 StartType=AutoLoop 且 Kind=BranchPoint 的任务
/// 按当前站点分组,优先级排序,选择流量允许的目标
///
/// 任务列表
protected virtual void ProcessBranchPointTasks(List allTasks)
{
// 按当前站点分组
var branchGroups = allTasks
.Where(t => t.Kind == TaskKind.BranchPoint && t.StartType == TaskStartType.AutoLoop)
.GroupBy(t => t.CurrentStationId)
.ToList();
foreach (var group in branchGroups)
{
int currentSiteId = group.Key;
// 查找到达当前站点的车辆
var car = FindCarArrivedAtSite(currentSiteId);
if (car == null || Commons.SelectCar(car) != 1)
continue;
// 按优先级排序,选择流量允许的目标
var sortedTasks = group.OrderByDescending(t => t.Priority).ToList();
foreach (var task in sortedTasks)
{
if (CheckTrafficControl(task.TargetStationId, task.TrafficControl))
{
AssignCarToTarget(car, task.TargetStationId);
Diagnosis.Post($"[AbstractLoopMission] BranchPoint: 车辆 {car.name}-{car.id} 从分流点 {currentSiteId} 分配到目标 {task.TargetStationId}(优先级 {task.Priority})", $"BranchPoint-AutoLoop", true);
break; // 分流点只分配一个目标
}
}
}
}
///
/// 处理自动汇合点任务
/// 筛选 StartType=AutoLoop 且 Kind=JoinPoint 的任务
/// 按目标站点分组,优先级排序,选择有车在站的最高优先级任务
///
/// 任务列表
protected virtual void ProcessJoinPointTasks(List allTasks)
{
// 按目标站点分组(汇合点)
var joinGroups = allTasks
.Where(t => t.Kind == TaskKind.JoinPoint && t.StartType == TaskStartType.AutoLoop)
.GroupBy(t => t.TargetStationId)
.ToList();
foreach (var group in joinGroups)
{
int targetSiteId = group.Key;
// 检查汇合点流量控制
var firstTask = group.First();
if (!CheckTrafficControl(targetSiteId, firstTask.TrafficControl))
continue;
// 按优先级排序,选择有车在站的最高优先级任务
var sortedTasks = group.OrderByDescending(t => t.Priority).ToList();
foreach (var task in sortedTasks)
{
var car = FindCarArrivedAtSite(task.CurrentStationId);
if (car != null && Commons.SelectCar(car) == 1)
{
AssignCarToTarget(car, targetSiteId);
Diagnosis.Post($"[AbstractLoopMission] JoinPoint: 车辆 {car.name} 从站点 {task.CurrentStationId} 汇入目标 {targetSiteId}(优先级 {task.Priority})", $"JoinPoint-AutoLoop", true);
//break; // 汇合点一次只放行一辆车
}
}
}
}
///
/// 分流点目标选择
/// 按优先级选择流量允许的目标站点
///
/// 当前分流任务
/// 所有任务列表
/// 选中的目标站点ID
protected virtual int SelectBranchTarget(LoopTask branchTask, List allTasks)
{
// 查找同一当前站点的所有分流目标
var branches = allTasks
.Where(t => t.Kind == TaskKind.BranchPoint && t.CurrentStationId == branchTask.CurrentStationId)
.OrderByDescending(t => t.Priority)
.ToList();
// 按优先级选择流量允许的目标
foreach (var branch in branches)
{
if (CheckTrafficControl(branch.TargetStationId, branch.TrafficControl))
{
return branch.TargetStationId;
}
}
// 默认返回原任务目标
return branchTask.TargetStationId;
}
///
/// 汇合点通行判断
/// 检查当前任务是否有最高优先级通行权(是否有更高优先级的车在等待)
///
/// 当前汇合任务
/// 所有任务列表
/// true=可以通行,false=需要等待更高优先级的车
protected virtual bool CanJoinPass(LoopTask joinTask, List allTasks)
{
// 查找同一目标站点的所有汇合来源
var joinSources = allTasks
.Where(t => t.Kind == TaskKind.JoinPoint && t.TargetStationId == joinTask.TargetStationId)
.OrderByDescending(t => t.Priority)
.ToList();
// 检查是否有更高优先级的任务且其站点有空闲车等待
foreach (var source in joinSources)
{
if (source.Priority > joinTask.Priority)
{
var higherPriorityCar = FindCarArrivedAtSite(source.CurrentStationId);
if (higherPriorityCar != null && IsCarIdle(higherPriorityCar))
{
return false; // 有更高优先级的车等待,当前车需要让行
}
}
}
return true;
}
///
/// 为车辆分配目标站点
/// 设置车辆的 goalSite 标签和 loopAssigned 时间戳
///
/// 车辆
/// 目标站点ID
protected virtual void AssignCarToTarget(Car car, int targetSiteId)
{
if (car == null)
return;
try
{
string targetIdStr = targetSiteId.ToString();
// 设置或更新目标站点标签
if (car.tags.ContainsKey("goalSite"))
car.tags["goalSite"] = targetIdStr;
else
car.tags.Add("goalSite", targetIdStr);
// 记录分配时间(用于调试和监控)
if (!car.tags.ContainsKey("loopAssigned"))
car.tags.Add("loopAssigned", DateTime.Now.ToString("HH:mm:ss"));
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[AbstractLoopMission] AssignCarToTarget error: {ex}");
}
}
#endregion
#region 线程生命周期
///
/// 启动策略同步线程
/// 每秒同步一次配置文件,自动更新任务列表
///
public virtual void StartLoop()
{
if (_strategyRunning)
return;
_strategyRunning = true;
_strategyThread = new Thread(() =>
{
while (_strategyRunning)
{
try
{
ApplyTaskStrategy();
Thread.Sleep(STRATEGY_SYNC_INTERVAL_MS);
}
catch (ThreadAbortException)
{
break;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[AbstractLoopMission] Strategy loop error: {ex}");
}
}
})
{
Name = "AbstractLoopMission_Strategy",
IsBackground = true
};
_strategyThread.Start();
status.status = "策略同步已启动";
System.Diagnostics.Debug.WriteLine("[AbstractLoopMission] StartLoop: 策略同步线程已启动");
}
///
/// 启动业务逻辑线程
/// 每 500ms 处理一次任务调度
///
public virtual void StartLogicLoop()
{
if (_logicRunning)
return;
_logicRunning = true;
_logicThread = new Thread(ExecuteLogicLoop)
{
Name = "AbstractLoopMission_Logic",
IsBackground = true
};
_logicThread.Start();
status.status = "业务逻辑已启动";
System.Diagnostics.Debug.WriteLine("[AbstractLoopMission] StartLogicLoop: 业务逻辑线程已启动");
}
///
/// 停止策略同步线程
///
public virtual void StopLoop()
{
try
{
_strategyRunning = false;
_strategyThread?.Join(2000);
_strategyThread = null;
System.Diagnostics.Debug.WriteLine("[AbstractLoopMission] StopLoop: 策略同步线程已停止");
}
catch { }
}
///
/// 停止业务逻辑线程
///
public virtual void StopLogicLoop()
{
try
{
_logicRunning = false;
_logicThread?.Join(2000);
_logicThread = null;
System.Diagnostics.Debug.WriteLine("[AbstractLoopMission] StopLogicLoop: 业务逻辑线程已停止");
}
catch { }
}
///
/// 启动所有线程(策略同步 + 业务逻辑)
///
public virtual void StartAll()
{
// ApplyTaskStrategy();
MarkTaskStartSitesAsTerminal();
StartLoop();
StartLogicLoop();
status.status = "已启动";
}
///
/// 将任务策略中的起始站点标记为 terminal=true
///
private void MarkTaskStartSitesAsTerminal()
{
List startSiteIds;
lock (_tasksLock)
{
startSiteIds = Tasks
.Select(t => t.CurrentStationId)
.Where(id => id > 0)
.Distinct()
.ToList();
}
foreach (var siteId in startSiteIds)
{
var site = SimpleLib.GetSite(siteId);
if (site == null)
continue;
Commons.AddOrUpdateSiteField(site, "terminal", "true");
}
}
///
/// 停止所有线程
///
public virtual void StopAll()
{
StopLoop();
StopLogicLoop();
status.status = "已停止";
}
///
/// 释放资源
/// 清理触发器适配器、停止所有线程、取消策略订阅
///
public void Dispose()
{
// 清理触发器适配器
foreach (var adapter in _adapters.ToArray())
{
try
{
adapter?.Dispose();
}
catch { }
}
_adapters.Clear();
// 停止所有线程
StopAll();
// 取消策略订阅
if (_taskStrategy is JsonFileTaskStrategy jsonStrategy)
{
jsonStrategy.Changed -= OnTaskStrategyChanged;
jsonStrategy.DisposeWatcher();
}
System.Diagnostics.Debug.WriteLine("[AbstractLoopMission] Dispose: 资源已释放");
}
#endregion
#region 辅助类型
///
/// 任务列表变更事件参数
///
public class TaskListChangedEventArgs : EventArgs
{
/// 变更类型
public TaskChangeType ChangeType { get; }
/// 变更的任务索引(Replaced 时为 -1)
public int Index { get; }
/// 变更的任务(Replaced 时为 null)
public LoopTask Task { get; }
///
/// 创建任务列表变更事件参数
///
/// 变更类型
/// 任务索引
/// 任务对象
public TaskListChangedEventArgs(TaskChangeType changeType, int index, LoopTask task)
{
ChangeType = changeType;
Index = index;
Task = task;
}
}
///
/// 任务变更类型枚举
///
public enum TaskChangeType
{
/// 添加任务
Added,
/// 更新任务
Updated,
/// 移除任务
Removed,
/// 替换全部任务
Replaced
}
///
/// 循环点配置类
/// 用于定义循环点的规则和属性
///
public class LoopPoint
{
/// 唯一标识
public string Id { get; set; } = Guid.NewGuid().ToString("N");
/// 名称
public string Name { get; set; } = string.Empty;
/// 自定义标签集合
public Dictionary Tags { get; } = new Dictionary();
/// 进入规则
public IEnterRule EnterRule { get; set; }
/// 退出规则
public IExitRule ExitRule { get; set; }
/// 汇合规则
public IJoinRule JoinRule { get; set; }
/// 分流规则
public IBranchRule BranchRule { get; set; }
}
///
/// JSON 文件任务策略类
/// 从 tasklist.json 读取任务配置,支持文件变更自动刷新
///
public class JsonFileTaskStrategy : ITaskStrategy, IDisposable
{
/// JSON 文件路径
public string JsonPath { get; set; }
/// 任务缓存列表
private List _cache = new List();
/// 文件系统监听器
private FileSystemWatcher _watcher;
/// 配置变更事件
public event EventHandler Changed;
///
/// 创建 JSON 文件任务策略
///
/// JSON 文件路径(null 时使用默认路径 tasklist.json)
public JsonFileTaskStrategy(string jsonPath = null)
{
JsonPath = string.IsNullOrWhiteSpace(jsonPath)
? Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "tasklist.json")
: jsonPath;
EnsureWatcher();
Refresh();
}
///
/// 获取任务列表
///
/// 任务列表副本
public IEnumerable GetTasks()
{
return _cache.ToArray();
}
///
/// 刷新任务列表(从文件重新读取)
///
public void Refresh()
{
try
{
if (!File.Exists(JsonPath))
{
_cache = new List();
return;
}
string json = File.ReadAllText(JsonPath);
_cache = JsonConvert.DeserializeObject>(json) ?? new List();
}
catch
{
_cache = new List();
}
}
///
/// 确保文件监听器已启动
///
public void EnsureWatcher()
{
try
{
if (_watcher != null)
return;
string directory = Path.GetDirectoryName(JsonPath);
string fileName = Path.GetFileName(JsonPath);
if (string.IsNullOrEmpty(directory))
return;
_watcher = new FileSystemWatcher(directory, fileName)
{
NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size | NotifyFilters.FileName
};
_watcher.Changed += (s, e) => OnFileChanged();
_watcher.Renamed += (s, e) => OnFileChanged();
_watcher.EnableRaisingEvents = true;
}
catch { }
}
private void OnFileChanged()
{
try
{
Thread.Sleep(50);
Refresh();
Changed?.Invoke(this, EventArgs.Empty);
}
catch { }
}
public void DisposeWatcher()
{
try
{
if (_watcher != null)
{
_watcher.EnableRaisingEvents = false;
_watcher.Dispose();
_watcher = null;
}
}
catch { }
}
public void Dispose()
{
DisposeWatcher();
}
}
#endregion
#region 路径查找辅助方法
///
/// 查找从起点到终点之间的所有站点(基于轨道连接的广度优先搜索)
///
/// 起点站点ID
/// 终点站点ID
/// 从起点到终点的站点ID列表(包含起点和终点),未找到路径返回空列表
///
///
/// // 查找站点100到站点200之间的所有站点
/// var sites = GetSitesBetween(100, 200);
/// // 返回: [100, 150, 180, 200]
///
///
protected List GetSitesBetween(int startSiteId, int endSiteId)
{
if (startSiteId == endSiteId)
return new List { startSiteId };
try
{
// 构建站点邻接表
var adjacency = BuildSiteAdjacency();
// 广度优先搜索
var visited = new HashSet();
var queue = new Queue>();
queue.Enqueue(new List { startSiteId });
visited.Add(startSiteId);
while (queue.Count > 0)
{
var currentPath = queue.Dequeue();
int currentSite = currentPath[currentPath.Count - 1];
// 找到终点,返回路径
if (currentSite == endSiteId)
return currentPath;
// 获取相邻站点
if (!adjacency.TryGetValue(currentSite, out var neighbors))
continue;
foreach (var neighbor in neighbors)
{
if (visited.Contains(neighbor))
continue;
visited.Add(neighbor);
var newPath = new List(currentPath) { neighbor };
queue.Enqueue(newPath);
}
}
// 未找到路径
return new List();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[AbstractLoopMission] GetSitesBetween({startSiteId}, {endSiteId}) error: {ex.Message}");
return new List();
}
}
///
/// 查找从起点到终点之间的所有站点对象
///
/// 起点站点ID
/// 终点站点ID
/// 从起点到终点的站点对象列表
protected List GetSiteObjectsBetween(int startSiteId, int endSiteId)
{
var siteIds = GetSitesBetween(startSiteId, endSiteId);
var result = new List();
foreach (var siteId in siteIds)
{
var site = SimpleLib.GetSite(siteId);
if (site != null)
result.Add(site);
}
return result;
}
///
/// 构建站点邻接表(基于轨道连接,考虑轨道方向)
///
/// 邻接表:站点ID -> 可达的相邻站点ID列表
private Dictionary> BuildSiteAdjacency()
{
var adjacency = new Dictionary>();
foreach (var track in SimpleLib.GetAllTracks())
{
int siteA = track.siteA;
int siteB = track.siteB;
// 根据轨道方向添加邻接关系
// direction: 0=双向, 1=A->B, 2=B->A
switch (track.direction)
{
case 0: // 双向
AddAdjacency(adjacency, siteA, siteB);
AddAdjacency(adjacency, siteB, siteA);
break;
case 1: // A -> B
AddAdjacency(adjacency, siteA, siteB);
break;
case 2: // B -> A
AddAdjacency(adjacency, siteB, siteA);
break;
default:
AddAdjacency(adjacency, siteA, siteB);
AddAdjacency(adjacency, siteB, siteA);
break;
}
}
return adjacency;
}
///
/// 向邻接表添加边
///
private void AddAdjacency(Dictionary> adjacency, int from, int to)
{
if (!adjacency.ContainsKey(from))
adjacency[from] = new HashSet();
adjacency[from].Add(to);
}
///
/// 检查路径上是否有其他车辆(不包含起点和终点)
///
/// 起点站点ID
/// 终点站点ID
/// 排除的车辆(通常是当前车辆)
/// true=路径上有车辆,false=路径畅通
protected bool HasCarOnPath(int startSiteId, int endSiteId, Car excludeCar = null)
{
var pathSites = GetSitesBetween(startSiteId, endSiteId);
// 排除起点和终点
var middleSites = pathSites
.Skip(1)
.Take(pathSites.Count - 2)
.ToList();
if (middleSites.Count == 0)
return false;
foreach (var car in SimpleLib.GetAllCars().OfType())
{
if (excludeCar != null && car == excludeCar)
continue;
if (car?.status?.holdingLocks == null)
continue;
// 检查车辆是否在路径中间站点
foreach (var siteId in middleSites)
{
if (car.status.holdingLocks.Contains(siteId))
return true;
}
}
return false;
}
///
/// 获取路径上的所有车辆
///
/// 起点站点ID
/// 终点站点ID
/// 是否包含起点和终点的车辆
/// 路径上的车辆列表
protected List GetCarsOnPath(int startSiteId, int endSiteId, bool includeEndpoints = false)
{
var pathSites = GetSitesBetween(startSiteId, endSiteId);
if (!includeEndpoints && pathSites.Count > 2)
{
pathSites = pathSites
.Skip(1)
.Take(pathSites.Count - 2)
.ToList();
}
var carsOnPath = new List();
foreach (var car in SimpleLib.GetAllCars().OfType())
{
if (car?.status?.holdingLocks == null)
continue;
foreach (var siteId in pathSites)
{
if (car.status.holdingLocks.Contains(siteId))
{
carsOnPath.Add(car);
break;
}
}
}
return carsOnPath;
}
///
/// 计算两站点之间的路径长度(经过的站点数)
///
/// 起点站点ID
/// 终点站点ID
/// 路径长度(站点数),未找到路径返回 -1
protected int GetPathLength(int startSiteId, int endSiteId)
{
var path = GetSitesBetween(startSiteId, endSiteId);
return path.Count > 0 ? path.Count : -1;
}
#endregion
#region 任务策略匹配
///
/// 路径缓存(避免重复计算)
///
[JsonIgnore]
private readonly Dictionary<(int, int), List> _pathCache = new Dictionary<(int, int), List>();
///
/// 路径缓存锁
///
[JsonIgnore]
private readonly object _pathCacheLock = new object();
///
/// 根据车辆当前站点匹配最优任务策略
/// 匹配逻辑:
///
/// - 遍历所有任务策略,获取每个策略从当前点到目标点的完整路径
/// - 检查车辆当前站点是否在该路径上
/// - 返回包含当前站点且优先级最高的策略
///
///
/// 车辆
/// 最优匹配结果,无匹配返回 null
protected TaskPathMatchResult FindBestTaskForCar(Car car)
{
if (car?.status?.holdingLocks == null || car.status.holdingLocks.Length == 0)
return null;
int currentSiteId = car.status.holdingLocks[0];
return FindBestTaskForSite(currentSiteId);
}
///
/// 根据站点ID匹配最优任务策略
/// 遍历所有任务,找出包含该站点的路径,返回优先级最高的任务
///
/// 当前站点ID
/// 最优匹配结果,无匹配返回 null
protected TaskPathMatchResult FindBestTaskForSite(int siteId)
{
List snapshot;
lock (_tasksLock)
{
snapshot = Tasks.Where(T=>T.IsViaPoint).ToList();
}
var matchResults = new List();
foreach (var task in snapshot)
{
// 获取任务的完整路径(从当前点到目标点)
var fullPath = GetSitesBetweenCached(task.CurrentStationId, task.TargetStationId);
if (fullPath.Count == 0)
continue;
// 检查站点是否在路径上
int indexInPath = fullPath.IndexOf(siteId);
if (indexInPath < 0)
continue;
// 创建匹配结果
var result = new TaskPathMatchResult
{
Task = task,
CurrentSiteId = siteId,
FullPath = fullPath,
IndexInPath = indexInPath,
RemainingPath = fullPath.Skip(indexInPath).ToList(),
DistanceToTarget = fullPath.Count - indexInPath - 1,
IsAtStartPoint = (indexInPath == 0),
IsAtEndPoint = (indexInPath == fullPath.Count - 1),
IsOnMiddlePath = (indexInPath > 0 && indexInPath < fullPath.Count - 1)
};
matchResults.Add(result);
}
if (matchResults.Count == 0)
return null;
// 按匹配优先级排序:
// 1. 起点匹配优先
// 2. 任务优先级高的优先
// 3. 距离目标近的优先
var bestMatch = matchResults.Where(r=>!r.IsAtEndPoint && !r.IsAtStartPoint )
.OrderByDescending(r => r.IsAtStartPoint ? 1 : 0) // 起点匹配优先
.ThenByDescending(r => r.Task.Priority) // 任务优先级高的优先
.ThenBy(r => r.DistanceToTarget) // 距离目标近的优先
.FirstOrDefault();
Diagnosis.Post($"[AbstractLoopMission] 小车获取到的任务策略 {bestMatch.ToString()} )", $"路径中间点初始化", true);
return bestMatch;
}
///
/// 根据站点ID查找所有匹配的任务策略
/// 返回所有包含该站点的任务,按优先级排序
///
/// 站点ID
/// 匹配结果列表(按优先级降序)
protected List FindAllTasksForSite(int siteId)
{
List snapshot;
lock (_tasksLock)
{
snapshot = Tasks.ToList();
}
var matchResults = new List();
foreach (var task in snapshot)
{
// 获取任务的完整路径
var fullPath = GetSitesBetweenCached(task.CurrentStationId, task.TargetStationId);
if (fullPath.Count == 0)
continue;
// 检查站点是否在路径上
int indexInPath = fullPath.IndexOf(siteId);
if (indexInPath < 0)
continue;
var result = new TaskPathMatchResult
{
Task = task,
CurrentSiteId = siteId,
FullPath = fullPath,
IndexInPath = indexInPath,
RemainingPath = fullPath.Skip(indexInPath).ToList(),
DistanceToTarget = fullPath.Count - indexInPath - 1,
IsAtStartPoint = (indexInPath == 0),
IsAtEndPoint = (indexInPath == fullPath.Count - 1),
IsOnMiddlePath = (indexInPath > 0 && indexInPath < fullPath.Count - 1)
};
matchResults.Add(result);
}
// 按优先级排序
return matchResults
.OrderByDescending(r => r.IsAtStartPoint ? 1 : 0)
.ThenByDescending(r => r.Task.Priority)
.ThenBy(r => r.DistanceToTarget)
.ToList();
}
///
/// 获取车辆应该前往的下一个目标站点
/// 根据最优匹配策略返回目标站点ID
///
/// 车辆
/// 目标站点ID,无匹配返回 -1
protected int GetRecommendedTargetForCar(Car car)
{
var match = FindBestTaskForCar(car);
return match?.Task?.TargetStationId ?? -1;
}
///
/// 获取车辆的下一个中间站点
/// 返回当前位置的下一个路径节点
///
/// 车辆
/// 下一站点ID,无匹配返回 -1
protected int GetNextSiteForCar(Car car)
{
var match = FindBestTaskForCar(car);
if (match?.RemainingPath == null || match.RemainingPath.Count < 2)
return -1;
return match.RemainingPath[1];
}
///
/// 获取缓存的路径(避免重复计算)
///
private List GetSitesBetweenCached(int startSiteId, int endSiteId)
{
var key = (startSiteId, endSiteId);
lock (_pathCacheLock)
{
if (_pathCache.TryGetValue(key, out var cached))
return new List(cached);
var path = GetSitesBetween(startSiteId, endSiteId);
_pathCache[key] = path;
return new List(path);
}
}
///
/// 清除路径缓存
/// 当任务策略变更或地图变更时调用
///
protected void ClearPathCache()
{
lock (_pathCacheLock)
{
_pathCache.Clear();
}
}
///
/// 根据任务ID查找任务
///
/// 任务ID
/// 任务对象,未找到返回 null
protected LoopTask FindTaskById(int taskId)
{
lock (_tasksLock)
{
return Tasks.FirstOrDefault(t => t.Id == taskId);
}
}
///
/// 判断车辆是否在指定任务的路径上
///
/// 车辆
/// 任务
/// 是否在路径上
protected bool IsCarOnTaskPath(Car car, LoopTask task)
{
if (car?.status?.holdingLocks == null || car.status.holdingLocks.Length == 0)
return false;
int currentSiteId = car.status.holdingLocks[0];
var path = GetSitesBetweenCached(task.CurrentStationId, task.TargetStationId);
return path.Contains(currentSiteId);
}
#endregion
#region 任务匹配结果类型
///
/// 任务路径匹配结果
///
public class TaskPathMatchResult
{
/// 匹配的任务
public LoopTask Task { get; set; }
/// 车辆当前站点ID
public int CurrentSiteId { get; set; }
/// 完整路径(从任务起点到终点)
public List FullPath { get; set; } = new List();
/// 当前站点在路径中的索引
public int IndexInPath { get; set; }
/// 剩余路径(从当前站点到终点)
public List RemainingPath { get; set; } = new List();
/// 到目标的距离(剩余站点数)
public int DistanceToTarget { get; set; }
/// 是否在任务起点
public bool IsAtStartPoint { get; set; }
/// 是否在任务终点
public bool IsAtEndPoint { get; set; }
/// 是否在路径中间
public bool IsOnMiddlePath { get; set; }
/// 任务ID
public int TaskId => Task?.Id ?? 0;
/// 任务优先级
public int TaskPriority => Task?.Priority ?? 0;
/// 目标站点ID
public int TargetSiteId => Task?.TargetStationId ?? -1;
/// 下一个站点ID(当前站点的下一跳)
public int NextSiteId => RemainingPath.Count > 1 ? RemainingPath[1] : -1;
/// 路径进度百分比
public double ProgressPercent => FullPath.Count > 1
? (double)IndexInPath / (FullPath.Count - 1) * 100
: 0;
///
/// 获取匹配信息描述
///
public override string ToString()
{
string position = IsAtStartPoint ? "起点" : (IsAtEndPoint ? "终点" : "中间");
return $"任务[ID={TaskId}, {Task?.CurrentStationId}->{Task?.TargetStationId}] " +
$"优先级={TaskPriority}, 当前={CurrentSiteId}({position}), " +
$"进度={ProgressPercent:F1}%, 剩余={DistanceToTarget}站, " +
$"下一站={NextSiteId}";
}
}
#endregion
private LoopViewer lv;
[MethodMember(Name = "任务管理", Description = "任务管理")]
public void Print()
{
lv = new LoopViewer();
lv.Show();
}
[MethodMember(Name = "启动环线", Description = "启动环线")]
public void Start()
{
StartAll();
}
[MethodMember(Name = "停止环线", Description = "停止环线")]
public void Stop()
{
StopAll();
}
}
}