周立功的CAN /FD实现
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user