From fb4bc6d9d7318359a407bee7545676620df92a14 Mon Sep 17 00:00:00 2001 From: Tyrone CT Date: Wed, 15 Oct 2025 11:13:37 +0800 Subject: [PATCH] =?UTF-8?q?LIN=20=E5=80=BC=E6=9B=B4=E6=96=B0=E5=92=8CLIN?= =?UTF-8?q?=E6=8E=A5=E5=8F=97=E4=BC=98=E5=8C=96=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CapMachine.Wpf/LinDrive/LinCmdData.cs | 22 +- CapMachine.Wpf/LinDrive/LinLdfModel.cs | 26 ++- CapMachine.Wpf/LinDrive/ToomossLin.cs | 201 +++++++++++++++--- CapMachine.Wpf/Services/LinDriveService.cs | 8 +- .../ViewModels/LinConfigViewModel.cs | 4 +- .../Views/DialogLINSchConfigView.xaml | 11 +- 6 files changed, 227 insertions(+), 45 deletions(-) diff --git a/CapMachine.Wpf/LinDrive/LinCmdData.cs b/CapMachine.Wpf/LinDrive/LinCmdData.cs index 9b89c56..cd67e86 100644 --- a/CapMachine.Wpf/LinDrive/LinCmdData.cs +++ b/CapMachine.Wpf/LinDrive/LinCmdData.cs @@ -1,4 +1,4 @@ -using CapMachine.Wpf.Dtos; +using CapMachine.Wpf.Dtos; using System; using System.Collections.Generic; using System.Linq; @@ -27,11 +27,29 @@ namespace CapMachine.Wpf.LinDrive /// public string? SignalName { get; set; } + /// + /// 指令数据改变Handler + /// 改变发送消息名称/帧名称 + /// + public event EventHandler? LinCmdDataChangedHandler; + + private double _SignalCmdValue; /// /// 指令值 /// 没有的话,则给默认值 /// - public double SignalCmdValue { get; set; } + public double SignalCmdValue + { + get { return _SignalCmdValue; } + set + { + if (_SignalCmdValue != value) + { + _SignalCmdValue = value; + LinCmdDataChangedHandler?.Invoke(this, MsgName!); + } + } + } ///// ///// 逻辑规则Id diff --git a/CapMachine.Wpf/LinDrive/LinLdfModel.cs b/CapMachine.Wpf/LinDrive/LinLdfModel.cs index 9ec3e57..7b10672 100644 --- a/CapMachine.Wpf/LinDrive/LinLdfModel.cs +++ b/CapMachine.Wpf/LinDrive/LinLdfModel.cs @@ -1,4 +1,4 @@ -using Prism.Mvvm; +using Prism.Mvvm; using System; using System.Collections.Generic; using System.Linq; @@ -62,7 +62,7 @@ namespace CapMachine.Wpf.LinDrive } } - private StringBuilder _SignalRtValueSb = new StringBuilder(10); + private StringBuilder _SignalRtValueSb = new StringBuilder(16); /// /// 信号实时值 StringBuilder /// @@ -71,12 +71,24 @@ namespace CapMachine.Wpf.LinDrive get { return _SignalRtValueSb; } set { - //if (_SignalRtValueSb != value) - //{ - SignalRtValue = value.ToString(); - _SignalRtValueSb = value; - //} + if (value == null) + { + if (_SignalRtValue != string.Empty) + { + _SignalRtValueSb.Clear(); + SignalRtValue = string.Empty; + } + return; + } + // 复制内容到内部可变缓冲区,避免多个模型共享同一个 StringBuilder 实例 + var str = value.ToString(); + if (!string.Equals(_SignalRtValue, str, StringComparison.Ordinal)) + { + _SignalRtValueSb.Clear(); + _SignalRtValueSb.Append(str); + SignalRtValue = str; + } } } diff --git a/CapMachine.Wpf/LinDrive/ToomossLin.cs b/CapMachine.Wpf/LinDrive/ToomossLin.cs index 6c8841c..0308339 100644 --- a/CapMachine.Wpf/LinDrive/ToomossLin.cs +++ b/CapMachine.Wpf/LinDrive/ToomossLin.cs @@ -77,12 +77,33 @@ namespace CapMachine.Wpf.LinDrive /// public HighSpeedDataService HighSpeedDataService { get; set; } + /// + /// 接收优化上下文:缓存帧与信号的 StringBuilder,避免循环中重复分配 + /// + private class SignalReadCtx + { + public string SignalName { get; set; } + public StringBuilder SignalNameSB { get; set; } + public LinLdfModel ModelRef { get; set; } + } + + private class FrameReadCtx + { + public string MsgName { get; set; } + public StringBuilder MsgNameSB { get; set; } + public bool IsMasterFrame { get; set; } + public List Signals { get; set; } = new List(); + } + + private List RecvFrameCtxs = new List(); + /// /// 开始LDF文件写入 /// public ObservableCollection StartLdf(string LdfPath) { LDF_Parser(LdfPath); + BuildRecvFrameCtxs(); return ListLinLdfModel; } @@ -186,6 +207,30 @@ namespace CapMachine.Wpf.LinDrive /// public List CmdData { get; set; } = new List(); + /// + /// 加载要发送的数据(订阅数据变化事件) + /// 一般是激活后才注册事件 + /// + /// + public void LoadCmdDataToDrive(List cmdData) + { + // 取消订阅旧数据源事件 + if (CmdData != null && CmdData.Count > 0) + { + foreach (var cmd in CmdData) + { + cmd.LinCmdDataChangedHandler -= CmdData_LinCmdDataChangedHandler; + } + } + + // 设置新数据源并订阅事件 + CmdData = cmdData ?? new List(); + foreach (var cmd in CmdData) + { + cmd.LinCmdDataChangedHandler += CmdData_LinCmdDataChangedHandler; + } + } + /// /// 当前激活调度表下,启用的帧/报文名称集合(来自 ListLINScheduleConfig 的 IsMsgActived)。 /// 为空(null) 表示不过滤,使用所有 CmdData 中的帧;非空则仅对包含在集合内的帧做信号更新与下发。 @@ -337,10 +382,49 @@ namespace CapMachine.Wpf.LinDrive } } + // 构建接收优化上下文,避免每次循环 GroupBy 与临时对象分配 + BuildRecvFrameCtxs(); + //解析成功 LdfParserState = true; } + /// + /// 构建接收优化上下文 + /// + private void BuildRecvFrameCtxs() + { + if (ListLinLdfModel == null || ListLinLdfModel.Count == 0) + { + RecvFrameCtxs = new List(); + return; + } + + var grouped = ListLinLdfModel.GroupBy(x => x.MsgName); + var list = new List(); + foreach (var g in grouped) + { + var first = g.First(); + var frame = new FrameReadCtx + { + MsgName = g.Key, + MsgNameSB = new StringBuilder(g.Key), + IsMasterFrame = (first.IsMasterFrame != null && first.IsMasterFrame.Contains("是")) + }; + foreach (var model in g) + { + frame.Signals.Add(new SignalReadCtx + { + SignalName = model.SignalName, + SignalNameSB = new StringBuilder(model.SignalName), + ModelRef = model + }); + } + list.Add(frame); + } + RecvFrameCtxs = list; + } + /// /// 发送LIN数据 @@ -388,32 +472,26 @@ namespace CapMachine.Wpf.LinDrive await Task.Delay(ReviceCycle); try { - var GroupMsg = ListLinLdfModel.GroupBy(x => x.MsgName); - foreach (var itemMsg in GroupMsg) + var frames = RecvFrameCtxs; + if (frames == null || frames.Count == 0) { - var data = itemMsg.FirstOrDefault(); - //非主机发送的指令帧就可以读取数据,因为函数 LDF_ExeFrameToBus【执行帧到总线,若该帧的发布者是主机,那么就是主机写数据,否则就是主机读数据】 - //那么主机发送和读取都是同一个函数。会导致跟Master主机也会在这里执行写入,导致冲突,出现错误。所以做一个排出 - if (data != null && !data.IsMasterFrame!.Contains("是")) - { - //主机读操作,读取从机返回的数据值 - LDFParser.LDF_ExeFrameToBus(LDFHandle, new StringBuilder(itemMsg.Key), 1); - foreach (var itemSignal in itemMsg) - { - //LDFParser.LDF_ExeFrameToBus(LDFHandle, new StringBuilder(itemMsg.Key), 1); - LDFParser.LDF_GetSignalValueStr(LDFHandle, new StringBuilder(itemMsg.Key), new StringBuilder(itemSignal.SignalName), ReadValueStr); - itemSignal.SignalRtValueSb = ReadValueStr; - } - //报文给高速记录的服务 - //HighSpeedDataService.AppendOrUpdateMsg(new Models.HighSpeed.CommMsg() - //{ - // Category = "LIN", - // MsgInfo = "0x" + CanMsgBuffer[i].ID.ToString("X8"), - // MsgData = BitConverter.ToString(CanMsgBuffer[i].Data), - // Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") - //}); - } + IsReviceOk = true; + continue; + } + foreach (var frame in frames) + { + // 仅对从机发布的帧执行读取 + if (frame.IsMasterFrame) + continue; + + // 主机读操作,读取从机返回的数据值 + LDFParser.LDF_ExeFrameToBus(LDFHandle, frame.MsgNameSB, 1); + foreach (var sig in frame.Signals) + { + LDFParser.LDF_GetSignalValueStr(LDFHandle, frame.MsgNameSB, sig.SignalNameSB, ReadValueStr); + sig.ModelRef.SignalRtValueSb = ReadValueStr; + } } IsReviceOk = true; @@ -697,6 +775,11 @@ namespace CapMachine.Wpf.LinDrive #region 调度器发送报文 + /// + /// 指令数据变化时,更新调度表的线程锁 + /// + private readonly object SchUpdateLock = new object(); + private bool _SchEnable; /// /// 调度表使能 @@ -881,12 +964,10 @@ namespace CapMachine.Wpf.LinDrive LoggerService?.Info($"调度表[{ActiveSchName}]已启动(自动执行)"); } - // 4) 置位调度更新开关,启动参数更新任务 + // 4) 置位调度更新开关(事件驱动刷新,不再自动启动周期刷新任务) //SchEnable = true; - if (CycleUpdateCmdTask == null || CycleUpdateCmdTask.IsCompleted) - { - StartCycleUpdateCmd(); - } + // 不再自动启动周期更新任务,改为数据变化事件驱动刷新 + // if (CycleUpdateCmdTask == null || CycleUpdateCmdTask.IsCompleted) { StartCycleUpdateCmd(); } } @@ -963,6 +1044,67 @@ namespace CapMachine.Wpf.LinDrive }); } + /// + /// 指令数据发生变化事件回调 + /// + /// + /// 消息/帧名称 + private void CmdData_LinCmdDataChangedHandler(object? sender, string e) + { + UpdateSchDataByCmdDataChanged(e); + } + + /// + /// 指令数据变化时,按消息名增量刷新调度表数据 + /// + /// 发生变化的帧名称 + private void UpdateSchDataByCmdDataChanged(string changedMsgName) + { + try + { + // 与CAN保持一致:仅在循环发送开启且调度表使能时才更新 + if (!IsCycleSend) return; + if (!SchEnable) return; + + // 基础防御 + if (LDFHandle == 0) return; + if (string.IsNullOrEmpty(ActiveSchName)) return; + if (string.IsNullOrEmpty(changedMsgName)) return; + + // 若配置了激活帧过滤,则不在集合内的帧不更新 + if (ActiveMsgNames != null && ActiveMsgNames.Count > 0 && !ActiveMsgNames.Contains(changedMsgName)) + return; + + lock (SchUpdateLock) + { + // 仅更新对应消息/帧的信号值 + var relatedCmds = CmdData.Where(x => string.Equals(x.MsgName, changedMsgName, StringComparison.Ordinal)); + foreach (var cmd in relatedCmds) + { + LDFParser.LDF_SetSignalValue(LDFHandle, new StringBuilder(changedMsgName), new StringBuilder(cmd.SignalName), cmd.SignalCmdValue); + } + + // 将更新后的信号值推送到适配器当前运行的调度表(离线表刷新) + var retPush = LDFParser.LDF_SetSchToTable(LDFHandle, new StringBuilder(ActiveSchName), 0); + if (retPush < 0) + { + IsSendOk = false; + LoggerService?.Info($"刷新调度表[{ActiveSchName}]失败, 返回:{retPush}"); + } + else + { + IsSendOk = true; + //Console.WriteLine($"Update LIN Schedule Success -- Sch:[{ActiveSchName}] Msg:[{changedMsgName}]"); + } + } + } + catch (Exception ex) + { + IsSendOk = false; + LoggerService?.Info($"时间:{DateTime.Now.ToString()}-【LIN_SCH】-{ex.Message}"); + } + } + /// /// 停止调度表自动执行 /// @@ -971,6 +1113,7 @@ namespace CapMachine.Wpf.LinDrive try { IsCycleSend = false; + SchEnable = false; ActiveMsgNames = null; // 清空激活帧过滤集合 if (LDFHandle != 0) { diff --git a/CapMachine.Wpf/Services/LinDriveService.cs b/CapMachine.Wpf/Services/LinDriveService.cs index b61cfab..98ecb76 100644 --- a/CapMachine.Wpf/Services/LinDriveService.cs +++ b/CapMachine.Wpf/Services/LinDriveService.cs @@ -1,4 +1,4 @@ -using CapMachine.Model.CANLIN; +using CapMachine.Model.CANLIN; using CapMachine.Wpf.Dtos; using CapMachine.Wpf.LinDrive; using ImTools; @@ -363,8 +363,12 @@ namespace CapMachine.Wpf.Services { if (CmdData.Count > 0) { - //ToomossLinDrive.IsCycleSend = true; + ////订阅 CmdData 的数据变化事件,确保基于数据变化刷新调度表 + //ToomossLinDrive.LoadCmdDataToDrive(CmdData); + ToomossLinDrive.CmdData = CmdData; + + //ToomossLinDrive.IsCycleSend = true; //ToomossLinDrive.StartPrecisionCycleSendMsg(); //ToomossLinDrive.StartCycleSendMsg(); diff --git a/CapMachine.Wpf/ViewModels/LinConfigViewModel.cs b/CapMachine.Wpf/ViewModels/LinConfigViewModel.cs index e01d0dd..1f4bb60 100644 --- a/CapMachine.Wpf/ViewModels/LinConfigViewModel.cs +++ b/CapMachine.Wpf/ViewModels/LinConfigViewModel.cs @@ -1,4 +1,4 @@ -using AutoMapper; +using AutoMapper; using CapMachine.Core; using CapMachine.Model.CANLIN; using CapMachine.Wpf.CanDrive; @@ -486,6 +486,8 @@ namespace CapMachine.Wpf.ViewModels LinDriveService.InitLinConfig(SelectCanLinConfigPro); InitLoadLinConfigPro(); + // 订阅 CmdData 的数据变化事件,用于基于数据变化刷新调度表 + LinDriveService.ToomossLinDrive.LoadCmdDataToDrive(LinDriveService.CmdData); } else { diff --git a/CapMachine.Wpf/Views/DialogLINSchConfigView.xaml b/CapMachine.Wpf/Views/DialogLINSchConfigView.xaml index 5cade3f..fe4efd7 100644 --- a/CapMachine.Wpf/Views/DialogLINSchConfigView.xaml +++ b/CapMachine.Wpf/Views/DialogLINSchConfigView.xaml @@ -167,13 +167,16 @@ - + - + IsEnabled="{Binding CanEdit}" /> +