From db6acc2741a83747f031a18e18be308e7a9f454a Mon Sep 17 00:00:00 2001 From: Tyrone CT Date: Tue, 14 Oct 2025 20:31:40 +0800 Subject: [PATCH] =?UTF-8?q?LIN=20=E8=B0=83=E5=BA=A6=E8=A1=A8=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E5=88=9D=E7=89=88=EF=BC=8C=E5=88=9D=E6=AD=A5=E5=8F=AF?= =?UTF-8?q?=E4=BB=A5=E8=BF=90=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CapMachine.Wpf/LinDrive/LDFParser.cs | 4 +- CapMachine.Wpf/LinDrive/ToomossLin.cs | 203 ++++++++++++------ CapMachine.Wpf/Services/LinDriveService.cs | 9 +- .../ViewModels/LinConfigViewModel.cs | 1 + 4 files changed, 152 insertions(+), 65 deletions(-) diff --git a/CapMachine.Wpf/LinDrive/LDFParser.cs b/CapMachine.Wpf/LinDrive/LDFParser.cs index 2f53829..06c5912 100644 --- a/CapMachine.Wpf/LinDrive/LDFParser.cs +++ b/CapMachine.Wpf/LinDrive/LDFParser.cs @@ -1,4 +1,4 @@ -using System.Runtime.InteropServices; +using System.Runtime.InteropServices; using System.Text; namespace CapMachine.Wpf.LinDrive @@ -75,6 +75,8 @@ namespace CapMachine.Wpf.LinDrive [DllImport("USB2XXX.dll")] public static extern Int32 LDF_GetRawMsg(UInt64 LDFHandle, IntPtr pLINMsg, int BufferSize); [DllImport("USB2XXX.dll")] + public static extern Int32 LDF_SyncMsgToValue(UInt64 LDFHandle, USB2LIN_EX.LIN_EX_MSG[] pLINMsg, int MsgLen); + [DllImport("USB2XXX.dll")] public static extern Int32 LDF_StopSchTable(UInt64 LDFHandle); [DllImport("USB2XXX.dll")] public static extern Int32 LDF_Release(UInt64 LDFHandle); diff --git a/CapMachine.Wpf/LinDrive/ToomossLin.cs b/CapMachine.Wpf/LinDrive/ToomossLin.cs index 398cf2e..1fbc244 100644 --- a/CapMachine.Wpf/LinDrive/ToomossLin.cs +++ b/CapMachine.Wpf/LinDrive/ToomossLin.cs @@ -1,4 +1,4 @@ -using CapMachine.Model.CANLIN; +using CapMachine.Model.CANLIN; using CapMachine.Wpf.CanDrive; using CapMachine.Wpf.Dtos; using CapMachine.Wpf.Services; @@ -28,16 +28,19 @@ namespace CapMachine.Wpf.LinDrive /// 设备Handles集合 /// public Int32[] DevHandles { get; set; } = new Int32[20]; + /// /// 设备Handles /// 设备句柄,本质为设备序号的低4字节,可以通过调用USB_ScanDevice函数获得 /// public Int32 DevHandle { get; set; } = 0; + /// /// Lin的Index /// LIN通道索引号,填0或者1,若只有一个通道LIN,则填0. /// public Byte LINIndex { get; set; } = 0; + /// /// Lin的连接State /// @@ -161,6 +164,17 @@ namespace CapMachine.Wpf.LinDrive /// public UInt64 LDFHandle { get; set; } + /// + /// LIN波特率(从LDF读取) + /// + public int LINBaudRate { get; private set; } + + /// + /// 当前正在适配器中运行的调度表名称(用于增量刷新) + /// 同一个时刻只能运行一个调度表 + /// + public string ActiveSchName { get; private set; } + /// /// LDF消息集合 /// 包括读取的实时值和数据 @@ -249,7 +263,23 @@ namespace CapMachine.Wpf.LinDrive //读取LDF文件信息 Console.WriteLine("ProtocolVersion = {0}", LDFParser.LDF_GetProtocolVersion(LDFHandle)); - Console.WriteLine("LINSpeed = {0}", LDFParser.LDF_GetLINSpeed(LDFHandle)); + LINBaudRate = LDFParser.LDF_GetLINSpeed(LDFHandle); + Console.WriteLine("LINSpeed = {0}", LINBaudRate); + + // 使用LDF中的波特率初始化LIN通道(主机模式) + var initRet = USB2LIN_EX.LIN_EX_Init(DevHandle, LINIndex, LINBaudRate, USB2LIN_EX.LIN_EX_MASTER); + if (initRet < USB2LIN_EX.LIN_EX_SUCCESS) + { + Console.WriteLine("LIN通道初始化失败!"); + LdfParserState = false; + return; + } + else + { + Console.WriteLine("LIN通道初始化成功"); + // 可选:开启适配器供电以驱动从节点(若硬件支持) + try { USB2LIN_EX.LIN_EX_CtrlPowerOut(DevHandle, LINIndex, 1); } catch { } + } //读取主机名称 StringBuilder MasterName = new StringBuilder(64); @@ -675,7 +705,6 @@ namespace CapMachine.Wpf.LinDrive } } - /// /// 定时扫描更新数据 扫描Task /// @@ -683,8 +712,9 @@ namespace CapMachine.Wpf.LinDrive /// /// 定时更新时间 + /// 定时更新调度表的周期 /// - private int UpdateCycle { get; set; } = 500; + private int UpdateCycle { get; set; } = 5000; /// /// 调度表数量 @@ -721,12 +751,13 @@ namespace CapMachine.Wpf.LinDrive }); } } - return LINScheduleConfigs; } /// /// 获取调度的帧的信号个数 + /// 消息/报文的个数 + /// 一个调度表包含多个帧/消息/报文 /// /// public int GetSchFrameCount(string SchName) @@ -755,7 +786,6 @@ namespace CapMachine.Wpf.LinDrive return SchCount; } - /// /// 获取调度表数量 /// @@ -771,55 +801,85 @@ namespace CapMachine.Wpf.LinDrive /// /// 开始调度表执行 + /// 同一个时刻只能运行一个调度表,如果有多个调度表,只能选择一个运行, + /// ********** 如果控制指令分布在不同的调度表中,需要更改LDF文件的配置,把控制的报文放在同一个调度表中 ********** /// public void StartSchedule() { - if (CmdData.Count() == 0) return; - - //设置调度表发送次数 - var SetSchRunTimeRet = USB2LIN_EX.LIN_EX_MasterSetSchRunTimes(DevHandle, LINIndex, 0xFFFFFFFF);//次数为0xFFFFFFFF表示一直发送数据 - if (SetSchRunTimeRet < USB2LIN_EX.LIN_EX_SUCCESS) - { - Console.WriteLine("Set Master Run Times failed!"); + if (LDFHandle == 0) return; + + // 1) 先将当前指令值写入对应帧(可选,用于初始化) + if (CmdData != null && CmdData.Count > 0) + { + var groupMsg = CmdData.GroupBy(x => x.MsgName); + foreach (var itemMsg in groupMsg) + { + foreach (var itemSignal in itemMsg) + { + //测试的速度数据 + //if (!string.IsNullOrEmpty(itemSignal.ConfigName) && itemSignal.ConfigName.Contains("速")) + //{ + // itemSignal.SignalCmdValue = 1000; + //} + LDFParser.LDF_SetSignalValue(LDFHandle, new StringBuilder(itemMsg.Key), new StringBuilder(itemSignal.SignalName), itemSignal.SignalCmdValue); + } + } + } + + // 2) 将要执行的调度表烧入适配器并自动运行(若不指定,则启动全部调度表) + // 优先使用 ListLINScheduleConfig 指定的调度表名;否则默认取第一个 + string selectedSch = string.Empty; + if (ListLINScheduleConfig != null && ListLINScheduleConfig.Count > 0) + { + selectedSch = ListLINScheduleConfig.FirstOrDefault(a => a.IsActive && !string.IsNullOrEmpty(a.SchTabName))?.SchTabName + ?? ListLINScheduleConfig.FirstOrDefault(a => !string.IsNullOrEmpty(a.SchTabName))?.SchTabName; + } + + if (string.IsNullOrEmpty(selectedSch)) + { + // 回退为索引0的调度表 + if (GetSchCount() > 0) + { + selectedSch = GetSchName(0); + LoggerService?.Info($"未指定调度表,默认启动调度表[{selectedSch}],注意当前调度表可能需要的调度表"); + } + } + + //当前激活的调度表 + ActiveSchName = selectedSch; + var ret = LDFParser.LDF_SetSchToTable(LDFHandle, new StringBuilder(ActiveSchName), 0); + if (ret < 0) + { + LoggerService?.Info($"启动调度表[{ActiveSchName}]失败, 返回:{ret}"); } else { - Console.WriteLine("Set Master Run Times success!"); + Console.WriteLine($"调度表[{ActiveSchName}]已启动(自动执行)"); + LoggerService?.Info($"调度表[{ActiveSchName}]已启动(自动执行)"); } - //防止有多个不同的消息名称/帧,每个帧单独处理发送 - var GroupMsg = CmdData.GroupBy(x => x.MsgName); - foreach (var itemMsg in GroupMsg) + //SchCount = GetSchCount(); + //for (int i = 0; i < SchCount; i++) + //{ + // var schName = GetSchName(i); + // var ret = LDFParser.LDF_SetSchToTable(LDFHandle, new StringBuilder(schName), 0); + // if (ret < 0) + // { + // LoggerService?.Info($"启动调度表[{schName}]失败, 返回:{ret}"); + // } + // else + // { + // Console.WriteLine($"调度表[{schName}]已启动(自动执行)"); + // } + //} + + // 3) 置位调度更新开关,启动参数更新任务 + //SchEnable = true; + if (CycleUpdateCmdTask == null || CycleUpdateCmdTask.IsCompleted) { - foreach (var itemSignal in itemMsg) - { - if (itemSignal.ConfigName.Contains("速")) - { - itemSignal.SignalCmdValue = Random.NextDouble() * 1000; - } - //主机写操作,发送数据给从机 - LDFParser.LDF_SetSignalValue(LDFHandle, new StringBuilder(itemMsg.Key), new StringBuilder(itemSignal.SignalName), itemSignal.SignalCmdValue); - } - - ////【0】参数注意 - //LDFParser.LDF_ExeFrameToBus(LDFHandle, new StringBuilder(itemMsg.Key), 0); - - //读取当前的指令帧数据,LDF_ExeFrameToBus执行后就可以读取本身的数据 - foreach (var item in ListLinLdfModel) - { - if (CmdData.Any(a => a.MsgName == item.MsgName)) - { - LDFParser.LDF_GetSignalValueStr(LDFHandle, new StringBuilder(item.MsgName), new StringBuilder(item.SignalName), ReadValueStr); - item.SignalRtValueSb = ReadValueStr; - } - } - - LDFParser.LDF_SetSchToTable(LDFHandle, new StringBuilder(itemMsg.Key), 1); + StartCycleUpdateCmd(); } - - - } @@ -847,7 +907,7 @@ namespace CapMachine.Wpf.LinDrive { if (itemSignal.ConfigName.Contains("速")) { - itemSignal.SignalCmdValue = Random.NextDouble() * 1000; + itemSignal.SignalCmdValue = 1250+ Random.NextDouble()*1000; } //主机写操作,发送数据给从机 LDFParser.LDF_SetSignalValue(LDFHandle, new StringBuilder(itemMsg.Key), new StringBuilder(itemSignal.SignalName), itemSignal.SignalCmdValue); @@ -866,8 +926,17 @@ namespace CapMachine.Wpf.LinDrive } } - LDFParser.LDF_ExeSchToBus(LDFHandle, new StringBuilder(itemMsg.Key), 1); - LDFParser.LDF_SetSchToTable(LDFHandle, new StringBuilder(itemMsg.Key), 1); + // 调度表已在适配器中自动执行,这里仅更新信号值即可 + } + + // 将更新后的信号值下发到适配器的自动调度表(刷新离线表的数据) + if (!string.IsNullOrEmpty(ActiveSchName)) + { + var retPush = LDFParser.LDF_SetSchToTable(LDFHandle, new StringBuilder(ActiveSchName), 0); + if (retPush < 0) + { + LoggerService?.Info($"刷新调度表[{ActiveSchName}]失败, 返回:{retPush}"); + } } IsSendOk = true; @@ -883,29 +952,26 @@ namespace CapMachine.Wpf.LinDrive }); } - - - - public void LinTest() + /// + /// 停止调度表自动执行 + /// + public void StopSchedule() { - GetSchCount(); - - for (int i = 0; i < SchCount; i++) + try { - StringBuilder pSchName = new StringBuilder(); - var Ret = LDFParser.LDF_GetSchName(LDFHandle, i, pSchName); - if (Ret <= 0) + IsCycleSend = false; + if (LDFHandle != 0) { - Console.WriteLine($"{pSchName.ToString()}"); - } - else - { - Console.WriteLine("执行失败"); + LDFParser.LDF_StopSchTable(LDFHandle); } } - + catch (Exception ex) + { + LoggerService?.Info($"停止调度表异常: {ex.Message}"); + } } + #endregion /// @@ -914,6 +980,17 @@ namespace CapMachine.Wpf.LinDrive public void CloseDevice() { //关闭设备 + try + { + // 停止调度并释放LDF资源 + if (LDFHandle != 0) + { + try { LDFParser.LDF_StopSchTable(LDFHandle); } catch { } + try { LDFParser.LDF_Release(LDFHandle); } catch { } + LDFHandle = 0; + } + } + catch { } USB_DEVICE.USB_CloseDevice(DevHandle); OpenState = false; LdfParserState = false; diff --git a/CapMachine.Wpf/Services/LinDriveService.cs b/CapMachine.Wpf/Services/LinDriveService.cs index 325c894..b61cfab 100644 --- a/CapMachine.Wpf/Services/LinDriveService.cs +++ b/CapMachine.Wpf/Services/LinDriveService.cs @@ -358,6 +358,7 @@ namespace CapMachine.Wpf.Services { if (ToomossLinDrive.OpenState) { + //来回取反设置 if (ToomossLinDrive.IsCycleSend == false) { if (CmdData.Count > 0) @@ -388,10 +389,11 @@ namespace CapMachine.Wpf.Services ToomossLinDrive.ListLINScheduleConfig = ListLINScheduleConfig!; ToomossLinDrive.StartSchedule(); - ToomossLinDrive.StartCycleUpdateCmd(); + //ToomossLinDrive.StartCycleUpdateCmd(); } else { + //使用软件调度发送数据 ToomossLinDrive.StartPrecisionCycleSendMsg(); //ToomossCanDrive.StartCycleSendMsg(); } @@ -407,6 +409,11 @@ namespace CapMachine.Wpf.Services else { ToomossLinDrive.IsCycleSend = false; + //如果是调度表的话,需要关闭调度表 + if (ToomossLinDrive.SchEnable) + { + ToomossLinDrive.StopSchedule(); + } } } diff --git a/CapMachine.Wpf/ViewModels/LinConfigViewModel.cs b/CapMachine.Wpf/ViewModels/LinConfigViewModel.cs index bab14d8..e01d0dd 100644 --- a/CapMachine.Wpf/ViewModels/LinConfigViewModel.cs +++ b/CapMachine.Wpf/ViewModels/LinConfigViewModel.cs @@ -1028,6 +1028,7 @@ namespace CapMachine.Wpf.ViewModels .Set(a => a.LdfPath, SelectedLINConfigExdDto.LdfPath) .Set(a => a.Cycle, SelectedLINConfigExdDto.Cycle) .Set(a => a.BaudRate, SelectedLINConfigExdDto.BaudRate) + .Set(a => a.SchEnable, SelectedLINConfigExdDto.SchEnable) .Where(a => a.Id == SelectedLINConfigExdDto.Id) .ExecuteUpdated(); }