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 } }