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
{
///
/// ZLG CAN/CANFD 驱动服务(共享设备句柄)。
///
public sealed class ZlgCanDriveService : BindableBase
{
private readonly ILogService _log;
///
/// 共享的 ZLG 驱动实例(CAN/CANFD/LIN)。
///
public ZlgCanFd200uDriver Driver { get; }
///
/// 当前选中的配置程序(沿用原有 FreeSql 模型)。
///
public CanLinConfigPro? SelectedCanLinConfigPro { get; set; }
///
/// Dbc 消息集合(用于 UI 绑定)。
///
public ObservableCollection ListCanDbcModel { get; private set; } = new ObservableCollection();
private bool _dbcParserState;
///
/// DBC 解析状态。
///
public bool DbcParserState
{
get { return _dbcParserState; }
private set { _dbcParserState = value; RaisePropertyChanged(); }
}
private bool _openState;
///
/// 设备打开状态。
///
public bool OpenState
{
get { return _openState; }
private set { _openState = value; RaisePropertyChanged(); }
}
private ZlgCanMode _mode;
///
/// CAN 模式(单选:CAN 或 CANFD)。
///
public ZlgCanMode Mode
{
get { return _mode; }
set { _mode = value; RaisePropertyChanged(); }
}
///
/// 发送使能(与 UI 的调度表使能语义对齐)。
///
public bool SchEnable
{
get { return Driver.SchEnable; }
set { Driver.SchEnable = value; RaisePropertyChanged(); }
}
///
/// 是否启用事件驱动发送。
///
public bool IsCycleSend
{
get { return Driver.IsCycleSend; }
set { Driver.IsCycleSend = value; RaisePropertyChanged(); }
}
///
/// 是否正在循环接收(对齐 Toomoss:IsCycleRevice)。
///
public bool IsCycleRevice
{
get { return Driver.IsReceiving; }
}
///
/// 最近是否发送成功(用于 UI 指示)。
///
public bool IsSendOk
{
get { return Driver.IsSendOk; }
}
///
/// 最近是否接收成功(用于 UI 指示)。
///
public bool IsReviceOk
{
get { return Driver.IsReviceOk; }
}
///
/// 要发送的 CAN 指令数据。
///
public List CmdData { get; } = new List();
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;
private ZlgCanFdChannelOptions _channel0 = new ZlgCanFdChannelOptions();
///
/// 构造。
///
/// 日志服务。
public ZlgCanDriveService(ILogService logService)
{
_log = logService;
Driver = new ZlgCanFd200uDriver(logService);
Driver.PropertyChanged += (_, __) =>
{
OpenState = Driver.OpenState;
RaisePropertyChanged(nameof(IsCycleRevice));
RaisePropertyChanged(nameof(IsSendOk));
RaisePropertyChanged(nameof(IsReviceOk));
};
OpenState = Driver.OpenState;
Mode = ZlgCanMode.Can;
}
///
/// 初始化 CAN 配置信息,并将配置中的名称映射到 DBC 信号集合(用于 UI 显示)。
///
/// 选中的配置。
public void InitCanConfig(CanLinConfigPro selectedCanLinConfigPro)
{
SelectedCanLinConfigPro = selectedCanLinConfigPro;
if (SelectedCanLinConfigPro?.CanLinConfigContents == null) return;
foreach (var item in SelectedCanLinConfigPro.CanLinConfigContents)
{
var find = ListCanDbcModel.FindFirst(a => a.SignalName == item.SignalName);
if (find != null)
{
find.Name = item.Name;
}
}
}
///
/// 更新配置(从 DTO/DB 同步到驱动)。
///
/// 设备索引。
/// 仲裁波特率(bps)。
/// 数据波特率(bps)。
/// 终端电阻使能。
/// 是否合并接收。
/// 合并接收缓冲帧数。
public void UpdateConfig(uint deviceIndex, uint arbBaudRate, uint dataBaudRate, bool resEnable, bool mergeReceive = false, int mergeReceiveBufferFrames = 100)
{
_deviceIndex = deviceIndex;
_channel0.ArbitrationBaudRate = arbBaudRate;
_channel0.DataBaudRate = dataBaudRate;
_channel0.EnableInternalResistance = resEnable;
_channel0.EnableMergeReceive = mergeReceive;
_channel0.MergeReceiveBufferFrames = mergeReceiveBufferFrames;
}
///
/// 打开 CAN/CANFD(按 Mode)。
///
public void StartCanDrive()
{
if (OpenState)
{
return;
}
Driver.OpenAndInitCan(_deviceIndex, _channel0);
Driver.StartReceiveLoop(_channel0.EnableMergeReceive, _channel0.MergeReceiveBufferFrames);
OpenState = Driver.OpenState;
}
///
/// 使能/停止循环接收。
///
/// true=启动接收线程,false=停止接收线程。
public void SetReceiveEnabled(bool enable)
{
if (!OpenState)
{
throw new InvalidOperationException("设备未连接,无法切换循环接收。");
}
if (enable)
{
if (!Driver.IsReceiving)
{
Driver.StartReceiveLoop(_channel0.EnableMergeReceive, _channel0.MergeReceiveBufferFrames);
}
}
else
{
if (Driver.IsReceiving)
{
Driver.StopReceiveLoop();
}
}
RaisePropertyChanged(nameof(IsCycleRevice));
}
///
/// 关闭设备(会同时关闭共享的 LIN 通道)。
///
public void CloseDevice()
{
try
{
// Close 语义:关闭时必须停止循环发送与循环接收。
// - 循环发送:停止软件调度,并关闭事件驱动发送标志。
// - 循环接收:Driver.Close 内部会 StopReceiveLoop。
StopSchedule();
IsCycleSend = false;
Driver.Close();
}
finally
{
OpenState = Driver.OpenState;
DbcParserState = false;
}
}
public void SetScheduleConfigs(IEnumerable configs)
{
var list = configs?.Where(a => !string.IsNullOrWhiteSpace(a.MsgName)).ToList() ?? new List();
lock (_scheduleLock)
{
_scheduleItems = list
.Select(a => (a.MsgName!.Trim(), Math.Max(1, a.Cycle), a.OrderSend, a.SchTabIndex))
.ToList();
}
}
public void SetScheduleConfigs(IEnumerable configs)
{
var list = configs?.Where(a => !string.IsNullOrWhiteSpace(a.MsgName)).ToList() ?? new List();
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(StringComparer.Ordinal);
var cycle = new Dictionary(StringComparer.Ordinal);
var order = new Dictionary(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);
}
///
/// 加载 DBC。
///
/// DBC 路径。
public ObservableCollection StartDbc(string path)
{
if (!OpenState)
{
throw new InvalidOperationException("请先打开设备后再加载 DBC。");
}
ListCanDbcModel = Driver.StartDbc(path);
DbcParserState = ListCanDbcModel != null && ListCanDbcModel.Count > 0;
return ListCanDbcModel;
}
///
/// 加载要发送的数据(订阅数据变化事件)。
///
/// 指令数据集合。
public void LoadCmdDataToDrive(List cmdData)
{
var list = cmdData ?? new List();
CmdData.Clear();
CmdData.AddRange(list);
foreach (var item in CmdData)
{
if (string.Equals(item.ConfigName, "转速", StringComparison.Ordinal))
{
SpeedCanCmdData = item;
}
}
Driver.LoadCmdDataToDrive(CmdData, channelIndex: 0, frameType: Mode == ZlgCanMode.Can ? (byte)ZDBC.FT_CAN : (byte)ZDBC.FT_CANFD);
}
///
/// 手动发送(目前对齐原服务:仅发送转速)。
///
/// 转速。
public void SendMsgToCanDrive(double speedData)
{
if (!OpenState)
{
return;
}
if (SpeedCanCmdData != null)
{
SpeedCanCmdData.SignalCmdValue = speedData;
}
else
{
_log.Warn("未配置转速指令项(ConfigName=转速),忽略手动发送。");
}
// 若未启用事件驱动发送,则这里主动发送一次(与旧行为兼容)
if (!IsCycleSend || !SchEnable)
{
var firstMsg = CmdData.FirstOrDefault()?.MsgName;
if (!string.IsNullOrWhiteSpace(firstMsg))
{
Driver.SendOneMsgByCmdData(firstMsg, 0, Mode == ZlgCanMode.Can ? (byte)ZDBC.FT_CAN : (byte)ZDBC.FT_CANFD);
}
}
}
}
///
/// ZLG CAN 工作模式。
///
public enum ZlgCanMode
{
///
/// CAN 经典帧。
///
Can = 0,
///
/// CAN FD。
///
CanFd = 1
}
}