周立功的CAN /FD实现

This commit is contained in:
2026-02-06 12:34:34 +08:00
parent 2e8ad1cffa
commit 74338fdb3a
13 changed files with 4260 additions and 310 deletions

View File

@@ -1,12 +1,15 @@
using CapMachine.Model.CANLIN;
using CapMachine.Wpf.CanDrive;
using CapMachine.Wpf.CanDrive.ZlgCan;
using CapMachine.Wpf.Dtos;
using ImTools;
using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace CapMachine.Wpf.Services
{
@@ -109,6 +112,12 @@ namespace CapMachine.Wpf.Services
/// </summary>
public List<CanCmdData> CmdData { get; } = new List<CanCmdData>();
private readonly object _scheduleLock = new object();
private List<(string MsgName, int CycleMs, int OrderSend, int SchTabIndex)> _scheduleItems = new List<(string, int, int, int)>();
private CancellationTokenSource? _scheduleCts;
private Task? _scheduleTask;
private bool _scheduleUseConfigItems;
private CanCmdData? SpeedCanCmdData { get; set; }
private uint _deviceIndex = 0;
@@ -224,6 +233,12 @@ namespace CapMachine.Wpf.Services
{
try
{
// Close 语义:关闭时必须停止循环发送与循环接收。
// - 循环发送:停止软件调度,并关闭事件驱动发送标志。
// - 循环接收Driver.Close 内部会 StopReceiveLoop。
StopSchedule();
IsCycleSend = false;
Driver.Close();
}
finally
@@ -233,6 +248,178 @@ namespace CapMachine.Wpf.Services
}
}
public void SetScheduleConfigs(IEnumerable<CANScheduleConfigDto> configs)
{
var list = configs?.Where(a => !string.IsNullOrWhiteSpace(a.MsgName)).ToList() ?? new List<CANScheduleConfigDto>();
lock (_scheduleLock)
{
_scheduleItems = list
.Select(a => (a.MsgName!.Trim(), Math.Max(1, a.Cycle), a.OrderSend, a.SchTabIndex))
.ToList();
}
}
public void SetScheduleConfigs(IEnumerable<CANFdScheduleConfigDto> configs)
{
var list = configs?.Where(a => !string.IsNullOrWhiteSpace(a.MsgName)).ToList() ?? new List<CANFdScheduleConfigDto>();
lock (_scheduleLock)
{
_scheduleItems = list
.Select(a => (a.MsgName!.Trim(), Math.Max(1, a.Cycle), a.OrderSend, a.SchTabIndex))
.ToList();
}
}
public void StartSchedule()
{
if (!OpenState)
{
throw new InvalidOperationException("设备未连接,无法启动调度表。");
}
List<(string MsgName, int CycleMs, int OrderSend, int SchTabIndex)> items;
lock (_scheduleLock)
{
items = _scheduleItems.ToList();
}
if (items.Count == 0)
{
throw new InvalidOperationException("调度表为空,无法启动调度表。");
}
_scheduleUseConfigItems = true;
StartSoftwareScheduler(items);
}
public void StartPrecisionCycleSend(int cycleMs)
{
if (!OpenState)
{
throw new InvalidOperationException("设备未连接,无法启动循环发送。");
}
var ms = Math.Max(1, cycleMs);
var msgNames = CmdData.Where(a => !string.IsNullOrWhiteSpace(a.MsgName)).Select(a => a.MsgName!).Distinct(StringComparer.Ordinal).ToList();
if (msgNames.Count == 0)
{
throw new InvalidOperationException("CmdData 为空,无法启动循环发送。");
}
var items = msgNames.Select(n => (n, ms, 1, 0)).ToList();
_scheduleUseConfigItems = false;
StartSoftwareScheduler(items);
}
public void StopSchedule()
{
CancellationTokenSource? cts;
Task? task;
lock (_scheduleLock)
{
cts = _scheduleCts;
task = _scheduleTask;
_scheduleCts = null;
_scheduleTask = null;
}
try
{
cts?.Cancel();
if (task != null)
{
task.Wait(TimeSpan.FromSeconds(2));
}
}
catch
{
}
finally
{
cts?.Dispose();
}
}
private void StartSoftwareScheduler(List<(string MsgName, int CycleMs, int OrderSend, int SchTabIndex)> items)
{
StopSchedule();
var cts = new CancellationTokenSource();
lock (_scheduleLock)
{
_scheduleCts = cts;
}
// 统一:软件调度开启后,等同“循环发送开启”
IsCycleSend = true;
_scheduleTask = Task.Run(async () =>
{
var token = cts.Token;
// next due time for each msg
var now = DateTime.UtcNow;
var due = new Dictionary<string, DateTime>(StringComparer.Ordinal);
var cycle = new Dictionary<string, int>(StringComparer.Ordinal);
var order = new Dictionary<string, int>(StringComparer.Ordinal);
foreach (var it in items)
{
if (!due.ContainsKey(it.MsgName))
{
due[it.MsgName] = now;
cycle[it.MsgName] = Math.Max(1, it.CycleMs);
order[it.MsgName] = it.OrderSend;
}
}
while (!token.IsCancellationRequested)
{
if (!OpenState)
{
await Task.Delay(50, token).ConfigureAwait(false);
continue;
}
var utcNow = DateTime.UtcNow;
var minDue = due.Values.Min();
var delay = minDue - utcNow;
if (delay > TimeSpan.Zero)
{
var ms = (int)Math.Min(delay.TotalMilliseconds, 200);
await Task.Delay(ms, token).ConfigureAwait(false);
continue;
}
// due messages
var ready = due.Where(kv => kv.Value <= utcNow).Select(kv => kv.Key).ToList();
if (ready.Count == 0)
{
await Task.Delay(1, token).ConfigureAwait(false);
continue;
}
// 顺序/并行:这里只决定同一 tick 内的发送顺序(并行模式仍按字典序依次发)
ready.Sort(StringComparer.Ordinal);
foreach (var msg in ready)
{
if (token.IsCancellationRequested) break;
try
{
Driver.SendOneMsgByCmdData(msg, 0, Mode == ZlgCanMode.Can ? (byte)ZDBC.FT_CAN : (byte)ZDBC.FT_CANFD);
}
catch (Exception ex)
{
_log.Warn($"调度表发送失败:{msg}{ex.Message}");
}
finally
{
due[msg] = DateTime.UtcNow.AddMilliseconds(cycle[msg]);
}
}
}
}, cts.Token);
}
/// <summary>
/// 加载 DBC。
/// </summary>