将 StandardScene 各插件的配置/监控窗体从 WinForms 迁移到 CycleGUI(删除 .Designer.cs/.resx,重写为 PanelBuilder 立即模式 UI,新增 CycleUiHelper 统一对话框)。 同时修复代码审核中的问题: - 后台文件写入加锁 + try/catch(ButtonBoxManager / DoorManager,对齐 LoopViewer.SaveTasks 模式) - CoderFieldsMetadata.cs 启用 #nullable enable,消除 CS8632 警告 - DummyCar 移除已废弃的 rightClickAction()/SetPosition() - CarRemoteHelper.OpenVehicleWebPage 的 Process.Start 加 try/catch - 重命名名不副实的 Mstsc()(现为打开网页) - 统一弃元命名为 _ - TrafficInterlockViewer 改用稳定 Id(GUID)做选择/编辑,替代行索引 - csproj 改用 $(CGUILibDir) 解析 CycleGUI,绝对路径收敛到 Directory.Build.props 构建:dotnet build StandardScene.sln → 0 错误,30 警告(均为历史遗留)。 注:static 单例状态重构(审核第 8 项)暂未处理,留待单独任务。
1160 lines
28 KiB
Markdown
1160 lines
28 KiB
Markdown
# StandardScene 答辩综合题 - 参考答题框架
|
||
|
||
**本文档提供的是答题思路指引,不是标准答案。鼓励基于此框架进行深度思考和创新。**
|
||
|
||
---
|
||
|
||
## 主题题答题参考
|
||
|
||
### A. 系统架构设计参考
|
||
|
||
#### A.1 核心组件与交互
|
||
|
||
**建议的架构图框架**:
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ 冲突管理层(Conflict Management) │
|
||
│ │
|
||
│ ┌──────────────────┐ ┌─────────────────┐ │
|
||
│ │ 冲突检测器 │───→│ 冲突级别评估 │ │
|
||
│ │ (Detector) │ │ (Classifier) │ │
|
||
│ └────────┬─────────┘ └────────┬────────┘ │
|
||
│ │ │ │
|
||
│ └───────┬───────────────┘ │
|
||
│ ▼ │
|
||
│ ┌─────────────────────────────────────┐ │
|
||
│ │ 策略选择引擎 │ │
|
||
│ │ (Strategy Selector) │ │
|
||
│ └────────────────┬────────────────────┘ │
|
||
│ │ │
|
||
├───────────────────┼──────────────────────────────────────┤
|
||
│ 策略执行层(Strategy Execution) │
|
||
│ │ │
|
||
│ ┌───────┴─────────────────────┬──────────────┐ │
|
||
│ ▼ ▼ ▼ ▼ ▼ │
|
||
│ 推挤 避让 切换 延迟 中止 │
|
||
│ 任务 任务 车辆 处理 任务 │
|
||
│(Push) (GiveWay) (Switch) (Wait) (Abort) │
|
||
│ │
|
||
├──────────────────────────────────────────────────────────┤
|
||
│ 任务调度层(Task Scheduling) │
|
||
│ │
|
||
│ ┌─────────────────┐ ┌──────────────────┐ │
|
||
│ │ 任务队列 │───→│ 优先级管理 │ │
|
||
│ │ (TaskQueue) │ │ (PriorityMgr) │ │
|
||
│ └─────────────────┘ └──────────────────┘ │
|
||
│ │
|
||
├──────────────────────────────────────────────────────────┤
|
||
│ 车辆管理层 / 通信层│
|
||
│ (VDA5050Car / MasterMQTTCommunication) │
|
||
│ │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
#### A.2 数据结构设计参考
|
||
|
||
**关键数据结构**:
|
||
|
||
```csharp
|
||
// 1. 冲突信息
|
||
public class ConflictInfo
|
||
{
|
||
public int ConflictId { get; set; }
|
||
public int DeliveryId { get; set; }
|
||
public int AssignedCarId { get; set; }
|
||
public int[] BlockingCarIds { get; set; }
|
||
public int ConflictSiteId { get; set; }
|
||
public ConflictType Type { get; set; }// 资源冲突/路径冲突/空间冲突
|
||
public DateTime DetectTime { get; set; }
|
||
public int WaitTime { get; set; }
|
||
public string Description { get; set; }
|
||
}
|
||
|
||
// 2. 避障历史
|
||
public class AvoidanceHistory
|
||
{
|
||
public int HistoryId { get; set; }
|
||
public int CarId { get; set; }
|
||
public int DeliveryId { get; set; }
|
||
public AvoidanceStrategy Strategy { get; set; }
|
||
public DateTime ExecuteTime { get; set; }
|
||
public bool IsSuccessful { get; set; }
|
||
public int CostDistance { get; set; } // 额外行驶距离
|
||
public int CostTime { get; set; } // 额外耗时(秒)
|
||
}
|
||
|
||
// 3. 车辆状态转移
|
||
public class VehicleStateTransition
|
||
{
|
||
public int CarId { get; set; }
|
||
public VehicleState FromState { get; set; }
|
||
public VehicleState ToState { get; set; }
|
||
public string Reason { get; set; }
|
||
public DateTime Timestamp { get; set; }
|
||
}
|
||
|
||
public enum VehicleState
|
||
{
|
||
Idle, // 空闲
|
||
Fetching, // 取货中
|
||
Transporting, // 运输中
|
||
Putting, // 放货中
|
||
AvoidingObstacle, // 避障中
|
||
GoingToGiveWay, // 前往避让点
|
||
StandbyAtGiveWay, // 在避让点待命
|
||
WaitingForTask, // 等待任务
|
||
Charging, // 充电中
|
||
Faulty // 故障
|
||
}
|
||
|
||
// 4. 路权管理
|
||
public class RightOfWayAllocation
|
||
{
|
||
public int AllocationId { get; set; }
|
||
public int PrimaryCarId { get; set; } // 有路权的车
|
||
public int[] SecondaryCarIds { get; set; } // 让路的车
|
||
public int ResourceSiteId { get; set; } // 争夺的资源
|
||
public DateTime AllocateTime { get; set; }
|
||
public int DurationSeconds { get; set; }
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### B. 代码实现参考思路
|
||
|
||
#### B.1 冲突检测器参考实现
|
||
|
||
**核心思路**:
|
||
|
||
```csharp
|
||
public bool DetectResourceConflict(AbstractDelivery d, AbstractCar car, out AbstractCar[] blockingCars)
|
||
{
|
||
blockingCars = null;
|
||
var conflictSite = d.src; // 假设冲突发生在取货点
|
||
|
||
// 1. 遍历所有其他车辆
|
||
var otherCars = SimpleLib.GetAllCars().OfType<Car>()
|
||
.Where(c => c.id != car.id)
|
||
.ToArray();
|
||
|
||
// 2. 检查是否有车要到达或已在冲突工位
|
||
var blocking = new List<AbstractCar>();
|
||
foreach (var otherCar in otherCars)
|
||
{
|
||
// 检查目标工位是否相同
|
||
if (otherCar.tags.TryGetValue("dest", out var destStr) &&
|
||
int.TryParse(destStr, out var destId) &&
|
||
destId == conflictSite)
|
||
{
|
||
blocking.Add(otherCar);
|
||
}
|
||
|
||
// 检查当前工位是否是冲突工位
|
||
if (otherCar.siteID == conflictSite)
|
||
{
|
||
blocking.Add(otherCar);
|
||
}
|
||
|
||
// 检查是否在前往冲突工位的路径中
|
||
var plan = new SegmentPlan { usingCar = otherCar };
|
||
try
|
||
{
|
||
// 如果规划的路径经过冲突工位,也算冲突
|
||
if (plan.segments?.Any(seg => seg.id == conflictSite) == true)
|
||
{
|
||
blocking.Add(otherCar);
|
||
}
|
||
}
|
||
catch { }
|
||
}
|
||
|
||
if (blocking.Count > 0)
|
||
{
|
||
blockingCars = blocking.ToArray();
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
public bool IsGiveWayAvailable(AbstractCar car, int targetSiteId, out int availableGiveWaySiteId)
|
||
{
|
||
availableGiveWaySiteId = -1;
|
||
|
||
// 1. 找到所有避让点
|
||
var giveWaySites = SimpleLib.GetAllSites()
|
||
.Where(s => s.fields.ContainsKey("giveWay") &&
|
||
s.fields["giveWay"] == "true")
|
||
.ToArray();
|
||
|
||
if (giveWaySites.Length == 0)
|
||
return false;
|
||
|
||
// 2. 检查哪些避让点是可达且未被占用的
|
||
foreach (var giveWaySite in giveWaySites)
|
||
{
|
||
// 检查避让点是否被占用
|
||
var isOccupied = SimpleLib.GetAllCars().OfType<Car>()
|
||
.Any(c => c.siteID == giveWaySite.id);
|
||
|
||
if (isOccupied)
|
||
continue;
|
||
|
||
// 检查是否可达
|
||
var plan = new SegmentPlan { usingCar = car };
|
||
try
|
||
{
|
||
var currentSite = car.GetLastSite();
|
||
if (currentSite == -1)
|
||
continue;
|
||
|
||
plan.FindRoute(
|
||
SimpleLib.GetSite(currentSite),
|
||
giveWaySite);
|
||
|
||
availableGiveWaySiteId = giveWaySite.id;
|
||
return true;
|
||
}
|
||
catch
|
||
{
|
||
// 这个避让点不可达,尝试下一个
|
||
continue;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
public ConflictLevel GetConflictLevel(ConflictInfo conflict)
|
||
{
|
||
// 1. 根据等待时间
|
||
var waitSeconds = (DateTime.Now - conflict.DetectTime).TotalSeconds;
|
||
if (waitSeconds > 300) // 等待超过5分钟
|
||
return ConflictLevel.Critical;
|
||
|
||
// 2. 根据冲突类型和严重程度
|
||
switch (conflict.Type)
|
||
{
|
||
case ConflictType.ResourceConflict:
|
||
if (conflict.BlockingCarIds.Length > 2)
|
||
return ConflictLevel.Critical;
|
||
else
|
||
return ConflictLevel.Medium;
|
||
|
||
case ConflictType.PathConflict:
|
||
return ConflictLevel.Medium;
|
||
|
||
case ConflictType.SpaceConflict:
|
||
// 没有可用避让点
|
||
return ConflictLevel.Critical;
|
||
|
||
default:
|
||
return ConflictLevel.Low;
|
||
}
|
||
}
|
||
```
|
||
|
||
#### B.2 任务重规划参考思路
|
||
|
||
**策略选择逻辑**:
|
||
|
||
```csharp
|
||
public async Task<bool> ResolveConflict(
|
||
EnhancedConflictDetector.ConflictInfo conflict,
|
||
EnhancedConflictDetector.ConflictLevel level)
|
||
{
|
||
switch (level)
|
||
{
|
||
case ConflictLevel.Low:
|
||
// 低级冲突:只需等待,通常会自动解除
|
||
return await WaitForConflictResolution(conflict);
|
||
|
||
case ConflictLevel.Medium:
|
||
// 中等冲突:尝试推挤或避让
|
||
if (await TryPushAwayTask(conflict))
|
||
{
|
||
Diagnosis.Post($"冲突{conflict.ConflictId}:通过推挤任务解决", "conflict", true);
|
||
return true;
|
||
}
|
||
|
||
if (await GoToGiveWay(conflict))
|
||
{
|
||
Diagnosis.Post($"冲突{conflict.ConflictId}:通过避让点解决", "conflict", true);
|
||
return true;
|
||
}
|
||
|
||
// 避让也失败,升级为严重冲突
|
||
return false;
|
||
|
||
case ConflictLevel.Critical:
|
||
// 严重冲突:尝试车辆切换或任务重规划
|
||
if (await TrySwitchCar(conflict))
|
||
{
|
||
Diagnosis.Post($"冲突{conflict.ConflictId}:通过切换车辆解决", "conflict", true);
|
||
return true;
|
||
}
|
||
|
||
if (await TryRescheduleDelivery(conflict))
|
||
{
|
||
Diagnosis.Post($"冲突{conflict.ConflictId}:通过重规划任务解决", "conflict", true);
|
||
return true;
|
||
}
|
||
|
||
if (await GoToGiveWay(conflict))
|
||
{
|
||
Diagnosis.Post($"冲突{conflict.ConflictId}:通过避让点解决", "conflict", true);
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
|
||
case ConflictLevel.Deadlock:
|
||
// 死锁:必须强制中止某个任务
|
||
if (ForceBreakDeadlock(conflict))
|
||
{
|
||
Diagnosis.Post($"检测到死锁:已强制中止任务", "deadlock", true);
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
private async Task<bool> TryPushAwayTask(ConflictInfo conflict)
|
||
{
|
||
var blockingCar = SimpleLib.GetCar(conflict.BlockingCarIds[0]) as Car;
|
||
var delivery = _mission.GetDeliveries().FirstOrDefault(d => d.id == conflict.DeliveryId);
|
||
|
||
if (blockingCar == null || delivery == null)
|
||
return false;
|
||
|
||
// 1. 寻找其他可做的任务
|
||
var alternativeTasks = _mission.GetDeliveries()
|
||
.Where(d => d.usingCar == null && // 未分配
|
||
d.GetStatus() == DeliveryStatus.Waiting && // 等待中
|
||
d.priority >= delivery.priority * 0.8) // 优先级相近
|
||
.ToArray();
|
||
|
||
if (alternativeTasks.Length == 0)
|
||
return false;
|
||
|
||
// 2. 计算距离和可达性
|
||
var bestTask = alternativeTasks[0];
|
||
float bestDistance = float.MaxValue;
|
||
|
||
foreach (var task in alternativeTasks)
|
||
{
|
||
try
|
||
{
|
||
var plan = new SegmentPlan { usingCar = blockingCar };
|
||
var currentSite = blockingCar.GetLastSite();
|
||
if (currentSite == -1)
|
||
continue;
|
||
|
||
var distance = plan.FindRoute(
|
||
SimpleLib.GetSite(currentSite),
|
||
SimpleLib.GetSite(task.src));
|
||
|
||
if (distance < bestDistance)
|
||
{
|
||
bestDistance = distance;
|
||
bestTask = task;
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
continue;
|
||
}
|
||
}
|
||
|
||
// 3. 如果距离在阈值内,分配该任务
|
||
if (bestDistance <= 10000) // 10000 是推挤距离阈值
|
||
{
|
||
bestTask.usingCar = blockingCar;
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
private async Task<bool> GoToGiveWay(ConflictInfo conflict)
|
||
{
|
||
var car = SimpleLib.GetCar(conflict.AssignedCarId) as Car;
|
||
if (car == null)
|
||
return false;
|
||
|
||
// 1. 检查是否有可用避让点
|
||
if (!_detector.IsGiveWayAvailable(car, conflict.ConflictSiteId, out var giveWaySiteId))
|
||
return false;
|
||
|
||
// 2. 规划路径到避让点
|
||
try
|
||
{
|
||
var plan = new SegmentPlan { usingCar = car };
|
||
var currentSite = car.GetLastSite();
|
||
if (currentSite == -1)
|
||
return false;
|
||
|
||
plan.FindRoute(
|
||
SimpleLib.GetSite(currentSite),
|
||
SimpleLib.GetSite(giveWaySiteId));
|
||
|
||
// 3. 更新车辆标签和任务状态
|
||
car.tags.Add("redirect", conflict.DeliveryId.ToString());
|
||
car.tags.Add("dest", giveWaySiteId.ToString());
|
||
|
||
// 4. 编译并执行
|
||
var program = plan.Compile($"GiveWay_{giveWaySiteId}");
|
||
await program.Queue();
|
||
|
||
car.siteID = giveWaySiteId;
|
||
return true;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Diagnosis.Log($"前往避让点失败: {ex.Message}", "error", true);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
private async Task<bool> TrySwitchCar(ConflictInfo conflict)
|
||
{
|
||
var delivery = _mission.GetDeliveries().FirstOrDefault(d => d.id == conflict.DeliveryId);
|
||
if (delivery == null || delivery.skipFetch) // 已取货,不能切换
|
||
return false;
|
||
|
||
// 1. 找到其他可用车辆
|
||
var availableCars = SimpleLib.GetAllCars().OfType<Car>()
|
||
.Where(c => Commons.SelectCar(c) >= 0) // 车辆可用
|
||
.ToArray();
|
||
|
||
if (availableCars.Length == 0)
|
||
return false;
|
||
|
||
// 2. 选择最近的车
|
||
float bestDistance = float.MaxValue;
|
||
Car bestCar = null;
|
||
|
||
foreach (var car in availableCars)
|
||
{
|
||
try
|
||
{
|
||
var plan = new SegmentPlan { usingCar = car };
|
||
var distance = plan.FindRoute(
|
||
SimpleLib.GetSite(car.GetLastSite()),
|
||
SimpleLib.GetSite(delivery.src));
|
||
|
||
if (distance < bestDistance)
|
||
{
|
||
bestDistance = distance;
|
||
bestCar = car;
|
||
}
|
||
}
|
||
catch { }
|
||
}
|
||
|
||
if (bestCar != null)
|
||
{
|
||
delivery.usingCar = bestCar;
|
||
|
||
var oldCar = SimpleLib.GetCar(conflict.AssignedCarId) as Car;
|
||
if (oldCar != null)
|
||
{
|
||
oldCar.tags.Remove("occupied");
|
||
oldCar.tags.Remove("deliver");
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
private bool ForceBreakDeadlock(ConflictInfo conflict)
|
||
{
|
||
// 1. 找到优先级最低的任务
|
||
var deliveries = _mission.GetDeliveries().Where(d => d.IsActive()).ToArray();
|
||
if (deliveries.Length == 0)
|
||
return false;
|
||
|
||
var lowestPriorityDelivery = deliveries.OrderBy(d => d.priority).First();
|
||
|
||
// 2. 中止该任务
|
||
if (lowestPriorityDelivery.Cancel())
|
||
{
|
||
Diagnosis.Log($"已中止任务{lowestPriorityDelivery.id}以破坏死锁", "deadlock", true);
|
||
|
||
// 3. 释放该任务占用的资源
|
||
if (lowestPriorityDelivery.usingCar != null)
|
||
{
|
||
lowestPriorityDelivery.usingCar.tags.Clear();
|
||
lowestPriorityDelivery.usingCar.tags.Add("idle", DateTime.Now.ToString());
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
```
|
||
|
||
#### B.3 连接恢复参考思路
|
||
|
||
```csharp
|
||
public class ResilientMQTTCommunication : MasterMQTTCommunication
|
||
{
|
||
private DateTime _lastSuccessfulConnection = DateTime.Now;
|
||
private int _connectionFailureCount = 0;
|
||
private const int MAX_RETRY_COUNT = 5;
|
||
private const int RETRY_INTERVAL_SECONDS = 10;
|
||
private Queue<(string Topic, string Payload, DateTime Timestamp)> _pendingMessages = new();
|
||
private CancellationTokenSource _healthCheckCts;
|
||
|
||
public async Task EnableAutoReconnect()
|
||
{
|
||
try
|
||
{
|
||
// 1. 监听连接状态变化
|
||
_client.ConnectedAsync += async e =>
|
||
{
|
||
_connectionFailureCount = 0;
|
||
_lastSuccessfulConnection = DateTime.Now;
|
||
Diagnosis.Post("MQTT 连接已建立", "mqtt", true);
|
||
|
||
// 2. 重连成功后,恢复订阅
|
||
SubsribeToConnectionTopic();
|
||
SubscribeToState();
|
||
SubscribeToVisualization();
|
||
|
||
// 3. 发送待发消息
|
||
await FlushPendingMessages();
|
||
|
||
return;
|
||
};
|
||
|
||
_client.DisconnectedAsync += async e =>
|
||
{
|
||
Diagnosis.Post($"MQTT 连接断开", "mqtt", true);
|
||
_connectionFailureCount += 1;
|
||
|
||
// 4. 指数退避重试
|
||
if (_connectionFailureCount <= MAX_RETRY_COUNT)
|
||
{
|
||
var backoffSeconds = (int)Math.Min(
|
||
RETRY_INTERVAL_SECONDS * Math.Pow(2, _connectionFailureCount - 1),
|
||
300// 最多等待5分钟
|
||
);
|
||
|
||
Diagnosis.Post(
|
||
$"将在 {backoffSeconds} 秒后重连 (第 {_connectionFailureCount} 次)...",
|
||
"mqtt",
|
||
true);
|
||
|
||
await Task.Delay(backoffSeconds * 1000);
|
||
await RestartClient();
|
||
}
|
||
else
|
||
{
|
||
Diagnosis.Post("连接失败次数过多,请检查网络和 MQTT broker", "mqtt", true);
|
||
}
|
||
|
||
return;
|
||
};
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Diagnosis.Log($"启用自动重连失败: {ex.Message}", "error", true);
|
||
}
|
||
}
|
||
|
||
private async Task FlushPendingMessages()
|
||
{
|
||
while (_pendingMessages.Count > 0)
|
||
{
|
||
var (topic, payload, timestamp) = _pendingMessages.Dequeue();
|
||
|
||
// 检查消息是否过期(超过5分钟)
|
||
if ((DateTime.Now - timestamp).TotalSeconds > 300)
|
||
{
|
||
Diagnosis.Log($"消息已过期,已删除: {topic}", "mqtt", true);
|
||
continue;
|
||
}
|
||
|
||
try
|
||
{
|
||
await PublishTo(payload);
|
||
Diagnosis.Log($"已补发消息到 {topic}", "mqtt", true);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Diagnosis.Log($"补发消息失败: {ex.Message}", "error", true);
|
||
// 重新加入队列
|
||
_pendingMessages.Enqueue((topic, payload, timestamp));
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
public void EnableMessagePersistence(string persistDir)
|
||
{
|
||
if (!Directory.Exists(persistDir))
|
||
Directory.CreateDirectory(persistDir);
|
||
|
||
// 1. 连接断开时,保存消息
|
||
var originalPublishTo = PublishTo;
|
||
PublishTo = async (payload) =>
|
||
{
|
||
try
|
||
{
|
||
await originalPublishTo(payload);
|
||
}
|
||
catch
|
||
{
|
||
// 保存到文件
|
||
var fileName = Path.Combine(
|
||
persistDir,
|
||
$"message_{DateTime.Now:yyyyMMdd_HHmmss_fff}.json"
|
||
);
|
||
File.WriteAllText(fileName, payload);
|
||
|
||
_pendingMessages.Enqueue(("vda5050/order", payload, DateTime.Now));
|
||
}
|
||
};
|
||
|
||
// 2. 启动时,读取待发消息
|
||
var files = Directory.GetFiles(persistDir, "message_*.json");
|
||
foreach (var file in files)
|
||
{
|
||
try
|
||
{
|
||
var payload = File.ReadAllText(file);
|
||
_pendingMessages.Enqueue(("vda5050/order", payload, File.GetCreationTime(file)));
|
||
}
|
||
catch { }
|
||
}
|
||
}
|
||
|
||
public async Task StartHealthCheck(int intervalSeconds = 30)
|
||
{
|
||
_healthCheckCts = new CancellationTokenSource();
|
||
|
||
var healthCheckTask = new Task(async () =>
|
||
{
|
||
while (!_healthCheckCts.Token.IsCancellationRequested)
|
||
{
|
||
try
|
||
{
|
||
if (!_client.IsConnected)
|
||
{
|
||
Diagnosis.Post("健康检查:连接已断开", "mqtt", true);
|
||
await RestartClient();
|
||
}
|
||
else
|
||
{
|
||
// 5. 检查最后一条消息的时间戳
|
||
var timeSinceLastMessage = DateTime.Now - _lastSuccessfulConnection;
|
||
if (timeSinceLastMessage.TotalSeconds > intervalSeconds * 3)
|
||
{
|
||
Diagnosis.Post(
|
||
$"长时间未收到消息 ({timeSinceLastMessage.TotalSeconds:0.0}s)",
|
||
"mqtt",
|
||
true);
|
||
|
||
// 可能是单向断连,尝试重连
|
||
await RestartClient();
|
||
}
|
||
}
|
||
|
||
await Task.Delay(intervalSeconds * 1000, _healthCheckCts.Token);
|
||
}
|
||
catch (OperationCanceledException)
|
||
{
|
||
break;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Diagnosis.Log($"健康检查异常: {ex.Message}", "error", true);
|
||
}
|
||
}
|
||
});
|
||
|
||
healthCheckTask.Start();
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### C. 问题分析参考答题框架
|
||
|
||
#### C.1 场景 A:优先级倒挂 - 参考分析
|
||
|
||
| 方案 | 说明 | 优点 | 缺点 | 推荐度 |
|
||
|-----|------|-----|-----|--------|
|
||
| **A)继续等待** | C1完成当前任务 | 简单,不改变系统状态 | 违反公平性原则,B永远无法执行 | ? 不推荐 |
|
||
| **B)中止C1任务** | 取消C1任务,释放资源 | 满足必须执行规则 | 丢弃已投入的工作,浪费资源,C1返空 | ?? 谨慎使用 |
|
||
| **C)分配其他车** | 给B分配其他可用车 | 保证任务继续进行 | 需要有可用车,否则无效 | ? 首选 |
|
||
| **D)推荐方案** | 动态优先级 + 等待补偿 | 公平且高效 | 实现复杂 | ? 最优 |
|
||
|
||
**推荐答案框架**:
|
||
|
||
```
|
||
场景分析:
|
||
- 任务A已在执行(已投入资源)
|
||
- 任务B等待超过阈值(优先级升级)
|
||
- 系统状态:C1被阻挡,无其他车可用
|
||
|
||
最优方案:动态优先级 + 等待补偿机制
|
||
|
||
实现步骤:
|
||
1. 监控任务等待时间
|
||
2. 当等待超过 300 秒时,自动升级优先级
|
||
3. 评估当前任务的预期完成时间
|
||
4. 如果 A 的剩余时间 > B 的升级等待时间,则中止 A
|
||
5. 否则继续执行 A,同时尝试分配其他车给 B
|
||
|
||
伪代码:
|
||
if (B.waitTime > threshold && B.priority_upgraded)
|
||
{
|
||
if (A.remaining_time > B.accumulated_wait_time)
|
||
{
|
||
// 中止 A,执行 B
|
||
A.Cancel();
|
||
// 分配 B 给其他车
|
||
}
|
||
else
|
||
{
|
||
// 继续 A,为 B 找其他车
|
||
if (FindAvailableCar(B))
|
||
AssignCar(B);
|
||
}
|
||
}
|
||
|
||
优点:
|
||
? 保证公平性(任务不会无限期等待)
|
||
? 最大化系统效率(优先完成高优先级任务)
|
||
? 最小化浪费(只在必要时中止)
|
||
|
||
缺点:
|
||
? 中止任务会导致已取货的物料需要放回
|
||
? 可能形成任务链复杂度
|
||
|
||
结论:
|
||
这是公平性和效率的良好平衡。建议采用。
|
||
```
|
||
|
||
#### C.2 场景 B:级联避让 - 参考分析
|
||
|
||
**问题树分析**:
|
||
|
||
```
|
||
级联避让问题
|
||
│
|
||
┌─────────────┼─────────────┐
|
||
│ │ │
|
||
避让点满资源浪费 避让死锁
|
||
(Space) (Time/Energy) (Deadlock)
|
||
|
||
↓ ↓ ↓
|
||
怎么办? 怎么办? 怎么办?
|
||
```
|
||
|
||
**参考答案框架**:
|
||
|
||
```
|
||
问题1:避让点满
|
||
━━━━━━━━━━━━━━━━
|
||
原因:所有车都涌向少数几个避让点
|
||
|
||
解决方案:
|
||
1. 增加避让点数量(规划优化)
|
||
2. 分布式避让(让车避让到不同方向)
|
||
3. 避让点预约制(提前锁定避让点资源)
|
||
4. 多级避让(临时避让点 → 标准避让点)
|
||
|
||
建议实现:
|
||
- 将避让点分为"A区避让"和"B区避让"
|
||
- 根据冲突位置动态分配避让点
|
||
- 如果避让点即将满,触发推挤策略
|
||
|
||
代码框架:
|
||
class DistributedGiveWayStrategy
|
||
{
|
||
public Site SelectGiveWay(Car car, int conflictSite)
|
||
{
|
||
var candidateSites = GetGiveWaySitesByRegion(conflictSite);
|
||
|
||
// 选择最近且未被占用的避让点
|
||
return candidateSites
|
||
.Where(s => !IsOccupied(s))
|
||
.OrderBy(s => Distance(car, s))
|
||
.FirstOrDefault();
|
||
}
|
||
}
|
||
|
||
问题2:资源浪费
|
||
━━━━━━━━━━━━━━━━
|
||
原因:车辆在避让点之间来回奔波
|
||
|
||
解决方案:
|
||
1. 锁定避让路径(一旦选择避让点,不再改变)
|
||
2. 并行避让(多个车同时从不同方向避让)
|
||
3. 避让点等待(在避让点等待而不是往返)
|
||
4. 能耗预测(预测避让成本,选择最低成本方案)
|
||
|
||
建议实现:
|
||
- 为每个避让操作计算成本(距离+时间+能耗)
|
||
- 选择成本最低的方案
|
||
- 避免重复避让同一车
|
||
|
||
代码框架:
|
||
class EfficientAvoidanceCalculator
|
||
{
|
||
public double CalculateAvoidanceCost(Car car, Site target)
|
||
{
|
||
var distance = GetDistance(car, target);
|
||
var time = distance / car.speed;
|
||
var energy = distance * car.powerConsumption;
|
||
|
||
return distance * 0.5 + time * 0.3 + energy * 0.2; // 加权评分
|
||
}
|
||
}
|
||
|
||
问题3:避让死锁
|
||
━━━━━━━━━━━━━━━━
|
||
原因:C1→避让1→被C2阻挡→避让2→被C3阻挡→避让1→死锁
|
||
|
||
解决方案:
|
||
1. 避让链路唯一性:避免重复访问同一避让点
|
||
2. 避让超时:如果避让操作超时,强制中止并选择其他策略
|
||
3. 避让路径验证:规划前检查避让路径是否会形成循环
|
||
4. 资源预约:预先锁定整条避让路径
|
||
|
||
建议实现:
|
||
class DeadlockFreeAvoidance
|
||
{
|
||
private Set<int> visitedSites = new(); // 已访问的避让点
|
||
|
||
public Site SelectGiveWay(Car car, int conflictSite)
|
||
{
|
||
var candidates = GetGiveWaySites()
|
||
.Where(s => !visitedSites.Contains(s.id)) // 不重复访问
|
||
.ToArray();
|
||
|
||
if (candidates.Length == 0)
|
||
{
|
||
// 所有避让点都被访问过,说明形成了死锁
|
||
return BreakDeadlock(car, conflictSite);
|
||
}
|
||
|
||
var selected = SelectBestSite(candidates);
|
||
visitedSites.Add(selected.id);
|
||
return selected;
|
||
}
|
||
}
|
||
|
||
综合解决方案:
|
||
━━━━━━━━━━━━━━━━
|
||
1. 分布式避让点 + 2. 成本优化 + 3. 死锁检测 + 4. 自动中止
|
||
|
||
流程:
|
||
┌─ 检测冲突
|
||
│
|
||
├─ 选择避让点
|
||
│ ├─ 按地区分配(分散压力)
|
||
│ ├─ 按成本排序(优化资源)
|
||
│ └─ 检查死锁风险(预防问题)
|
||
│
|
||
├─ 发送避让指令
|
||
│ └─ 设置超时(防止无限期等待)
|
||
│
|
||
└─ 监控避让过程
|
||
├─ 成功 → 继续
|
||
├─ 超时 → 尝试推挤
|
||
└─ 死锁 → 强制中止
|
||
```
|
||
|
||
---
|
||
|
||
### D. 创新性改进参考方向
|
||
|
||
#### 改进方向 1:机器学习优化任务分配
|
||
|
||
```
|
||
问题:现有算法基于简单的距离优化,不考虑:
|
||
- 历史执行成功率
|
||
- 车辆性能差异
|
||
- 工位的繁忙程度
|
||
- 时间序列的规律性
|
||
|
||
解决思路:
|
||
1. 使用强化学习(Q-Learning)
|
||
- 状态:车辆位置、任务队列、车辆状态
|
||
- 动作:选择某个车+某个任务的配对
|
||
- 奖励:任务完成速度、避免冲突、能耗效率
|
||
|
||
2. 数据收集:记录每个任务的执行历史
|
||
- 指派车型
|
||
- 执行时间
|
||
- 是否发生冲突
|
||
- 最终成功/失败
|
||
|
||
3. 模型训练:定期重训练模型以适应环境变化
|
||
- 每小时重训一次
|
||
- 积累一周数据进行离线评估
|
||
|
||
预期效果:
|
||
? 任务完成率提升 5-10%
|
||
? 平均等待时间减少 15%
|
||
? 冲突发生率降低 20%
|
||
|
||
实现复杂度:★★★☆☆
|
||
```
|
||
|
||
#### 改进方向 2:预测性避障
|
||
|
||
```
|
||
问题:现有系统是反应式(问题发生后才处理),不够主动
|
||
|
||
解决思路:
|
||
1. 路径冲突预测
|
||
- 在规划路径时,预测未来30秒内的所有车辆位置
|
||
- 如果发现潜在冲突,提前调整路线
|
||
|
||
2. 优先级预测
|
||
- 根据工位繁忙程度,预测哪些任务可能堵塞
|
||
- 提前调整这些任务的优先级
|
||
|
||
3. 避让点需求预测
|
||
- 预测接下来30秒有多少车可能需要避让
|
||
- 提前释放避让点资源
|
||
|
||
代码框架:
|
||
class PredictiveAvoidanceSystem
|
||
{
|
||
public bool PredictConflict(SegmentPlan plan, int lookAheadSeconds = 30)
|
||
{
|
||
// 1. 获取规划的路径
|
||
var path = plan.segments;
|
||
|
||
// 2. 预测车辆的未来位置
|
||
var futurePositions = new Dictionary<int, Site>();
|
||
foreach (var car in GetAllCars())
|
||
{
|
||
var predictedSite = PredictCarPosition(car, lookAheadSeconds);
|
||
futurePositions[car.id] = predictedSite;
|
||
}
|
||
|
||
// 3. 检查规划路径是否会与预测位置冲突
|
||
foreach (var segment in path)
|
||
{
|
||
if (futurePositions.Values.Any(s => s.id == segment.id))
|
||
{
|
||
return true; // 发现潜在冲突
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
private Site PredictCarPosition(Car car, int seconds)
|
||
{
|
||
// 基于车的速度和目标,预测位置
|
||
var currentSpeed = car.speed;
|
||
var distanceTraveled = currentSpeed * seconds;
|
||
|
||
// 简单预测:沿着当前路线继续前进
|
||
var pendingLocks = car.status.pendingLocks;
|
||
if (pendingLocks.Length == 0)
|
||
return SimpleLib.GetSite(car.siteID);
|
||
|
||
// 计算会在哪个工位
|
||
// ...
|
||
return pendingLocks.Last();
|
||
}
|
||
}
|
||
|
||
预期效果:
|
||
? 冲突检测提前30秒
|
||
? 避障成功率从 95% 提升到 99%
|
||
? 系统响应更主动
|
||
|
||
实现复杂度:★★★★☆
|
||
```
|
||
|
||
#### 改进方向 3:多目标优化框架
|
||
|
||
```
|
||
问题:现有系统只优化距离,忽视吞吐量、能耗、公平性等
|
||
|
||
解决思路:使用帕累托最优(Pareto Optimality)
|
||
|
||
class MultiObjectiveOptimizer
|
||
{
|
||
public List<TaskAssignment> FindParetoFrontier(
|
||
List<AbstractDelivery> tasks,
|
||
Dictionary<string, double> weights)
|
||
{
|
||
var paretoFront = new List<TaskAssignment>();
|
||
|
||
// 1. 枚举所有可能的分配方案
|
||
var allAssignments = GenerateAllAssignments(tasks);
|
||
|
||
// 2. 评估每个方案的多个目标
|
||
foreach (var assignment in allAssignments)
|
||
{
|
||
var evaluation = Evaluate(assignment);
|
||
|
||
// 3. 检查是否被现有方案支配
|
||
var isDominated = paretoFront.Any(existing =>
|
||
existing.throughput >= evaluation.throughput &&
|
||
existing.fairness >= evaluation.fairness &&
|
||
existing.energyEfficiency >= evaluation.energyEfficiency &&
|
||
existing.reliability >= evaluation.reliability
|
||
);
|
||
|
||
if (!isDominated)
|
||
{
|
||
paretoFront.Add(assignment);
|
||
}
|
||
}
|
||
|
||
// 4. 根据权重选择最终方案
|
||
return paretoFront
|
||
.OrderByDescending(a => CalculateWeightedScore(a, weights))
|
||
.ToList();
|
||
}
|
||
}
|
||
|
||
预期效果:
|
||
? 支持多种运营目标
|
||
? 提高决策的透明性和可控性
|
||
? 系统更加灵活
|
||
|
||
实现复杂度:★★★★☆
|
||
```
|
||
|
||
---
|
||
|
||
## 常见答题错误与改进
|
||
|
||
### ? 常见错误 1:忽视线程安全
|
||
|
||
**错误示例**:
|
||
```csharp
|
||
// 错误:竞态条件
|
||
if (ConflictExists(d.src)) // 检查
|
||
{
|
||
AssignDelivery(d); // 在此期间其他线程可能改变了状态
|
||
}
|
||
```
|
||
|
||
**正确做法**:
|
||
```csharp
|
||
lock (syncLock)
|
||
{
|
||
if (ConflictExists(d.src))
|
||
{
|
||
AssignDelivery(d);
|
||
}
|
||
}
|
||
```
|
||
|
||
### ? 常见错误 2:没有考虑回滚
|
||
|
||
**错误示例**:
|
||
```csharp
|
||
// 错误:分配任务后规划失败,但不回滚
|
||
delivery.usingCar = car;
|
||
try
|
||
{
|
||
plan.FindRoute(...);
|
||
}
|
||
catch { } // 异常吞没,车辆未释放
|
||
```
|
||
|
||
**正确做法**:
|
||
```csharp
|
||
try
|
||
{
|
||
plan.FindRoute(...);
|
||
delivery.usingCar = car; // 成功后才分配
|
||
}
|
||
catch
|
||
{
|
||
// 规划失败,不分配车辆
|
||
delivery.usingCar = null;
|
||
}
|
||
```
|
||
|
||
### ? 常见错误 3:避障死循环
|
||
|
||
**错误示例**:
|
||
```csharp
|
||
// 错误:可能形成死循环
|
||
while (ConflictExists(d.src))
|
||
{
|
||
TryAvoid(d); // 避障可能失败,一直循环
|
||
}
|
||
```
|
||
|
||
**正确做法**:
|
||
```csharp
|
||
// 设置重试次数上限和超时
|
||
int retries = 0;
|
||
var timeout = DateTime.Now.AddSeconds(300);
|
||
|
||
while (ConflictExists(d.src) && retries < MAX_RETRIES && DateTime.Now < timeout)
|
||
{
|
||
if (!TryAvoid(d))
|
||
break; // 避障失败,退出
|
||
retries++;
|
||
}
|
||
|
||
if (ConflictExists(d.src))
|
||
{
|
||
// 所有尝试都失败,中止任务或升级策略
|
||
BreakDeadlock(d);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 答题检查清单
|
||
|
||
在提交答案前,请检查:
|
||
|
||
- [ ] **架构设计**
|
||
- [ ] 包含所有关键组件
|
||
- [ ] 组件间交互清晰
|
||
- [ ] 支持扩展性
|
||
- [ ] 有清晰的数据流向
|
||
|
||
- [ ] **代码实现**
|
||
- [ ] 核心方法有实现(非仅伪代码)
|
||
- [ ] 考虑了异常处理
|
||
- [ ] 考虑了并发安全
|
||
- [ ] 有适当的日志输出
|
||
- [ ] 变量命名清晰
|
||
|
||
- [ ] **问题分析**
|
||
- [ ] 列举了多个解决方案
|
||
- [ ] 分析了方案的优缺点
|
||
- [ ] 给出了明确的推荐
|
||
- [ ] 考虑了现实约束
|
||
|
||
- [ ] **创新性**
|
||
- [ ] 提出了至少2项改进
|
||
- [ ] 改进方案具体可行
|
||
- [ ] 说明了预期效果
|
||
- [ ] 评估了实现成本
|
||
|
||
---
|
||
|
||
**祝你答题成功!** ??
|
||
|