using CapMachine.Model.CANLIN; using CapMachine.Wpf.LinDrive; using Prism.Mvvm; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; namespace CapMachine.Wpf.Services { /// /// ZLG LIN 驱动服务(共享设备句柄)。 /// public sealed class ZlgLinDriveService : BindableBase { private readonly ILogService _log; private readonly ZlgCanDriveService _zlgCanDriveService; /// /// 当前选中的配置程序(沿用原有 FreeSql 模型)。 /// public CanLinConfigPro? SelectedCanLinConfigPro { get; set; } private bool _openState; /// /// LIN 打开状态。 /// public bool OpenState { get { return _openState; } private set { _openState = value; RaisePropertyChanged(); } } private bool _ldfParserState; /// /// LDF 解析状态(ZLG LIN 暂未接入 LDF,固定为 false)。 /// public bool LdfParserState { get { return _ldfParserState; } private set { _ldfParserState = value; RaisePropertyChanged(); } } /// /// LDF 消息集合(UI 绑定),ZLG LIN 暂未接入 LDF,默认空。 /// public ObservableCollection ListLinLdfModel { get; private set; } = new ObservableCollection(); /// /// 是否启用调度发送(与 UI 的调度表使能语义对齐)。 /// public bool SchEnable { get { return _zlgCanDriveService.Driver.SchEnable; } set { _zlgCanDriveService.Driver.SchEnable = value; RaisePropertyChanged(); } } /// /// 是否启用事件驱动发送。 /// public bool IsCycleSend { get { return _zlgCanDriveService.Driver.IsCycleSend; } set { _zlgCanDriveService.Driver.IsCycleSend = value; RaisePropertyChanged(); } } /// /// 构造。 /// /// 共享 CAN 服务。 /// 日志服务。 public ZlgLinDriveService(ZlgCanDriveService zlgCanDriveService, ILogService logService) { _zlgCanDriveService = zlgCanDriveService; _log = logService; _zlgCanDriveService.Driver.LinFrameReceived += frame => { // 未来接入 LDF 后在这里做 Sync/Decode;当前仅保留事件链路,不做假解析。 }; } /// /// 初始化 LIN 配置信息(目前仅缓存)。 /// /// 选中的配置。 public void InitLinConfig(CanLinConfigPro selectedLinConfigPro) { SelectedCanLinConfigPro = selectedLinConfigPro; } /// /// 打开 LIN(共享设备句柄)。 /// /// 设备索引。 /// 波特率。 /// 是否主节点。 public void StartLinDrive(uint deviceIndex, uint baudRate, bool isMaster) { if (OpenState) { return; } try { // 先确保设备打开(不影响 CAN 后续 Init) _zlgCanDriveService.Driver.OpenDevice(deviceIndex); // 初始化 LIN 通道 _zlgCanDriveService.Driver.OpenAndInitLin(0, new CanDrive.ZlgCan.ZlgLinChannelOptions { BaudRate = baudRate, IsMaster = isMaster, MaxLength = 8, ChecksumMode = 3 }); // 统一由 CAN 服务侧启动接收线程(设备级 merge 接收可以同时收 CAN/LIN) if (!_zlgCanDriveService.Driver.IsReceiving) { _zlgCanDriveService.Driver.StartReceiveLoop(mergeReceive: true, bufferFrames: 200); } OpenState = true; LdfParserState = false; } catch (Exception ex) { _log.Error($"ZLG LIN 打开失败:{ex.Message}"); OpenState = false; throw; } } /// /// 关闭 LIN(共享设备句柄下,当前实现以 CloseDevice 为准:关闭将同时关闭 CAN/LIN)。 /// public void CloseDevice() { // ZLG 的通道句柄都在 Driver 内部;当前 Close 会关闭所有通道,保持与旧系统“同一时刻只有一种驱动工作”的原则一致。 _zlgCanDriveService.CloseDevice(); OpenState = false; LdfParserState = false; } /// /// 加载 LDF(ZLG LIN 暂未接入 LDF 解析 DLL,因此此处明确失败,不返回模拟数据)。 /// /// LDF 路径。 public ObservableCollection StartLdf(string path) { if (string.IsNullOrWhiteSpace(path)) { throw new ArgumentException("LDF 路径为空", nameof(path)); } if (!File.Exists(path)) { throw new FileNotFoundException($"LDF 文件不存在:{path}", path); } try { var text = File.ReadAllText(path, Encoding.UTF8); // 去除单行注释,简化解析 text = Regex.Replace(text, @"//.*?$", string.Empty, RegexOptions.Multiline); var models = ParseLdfFramesAndSignals(text); ListLinLdfModel.Clear(); foreach (var item in models) { ListLinLdfModel.Add(item); } LdfParserState = true; return ListLinLdfModel; } catch (Exception ex) { _log.Error($"ZLG LIN 解析 LDF 失败:{ex.Message}"); LdfParserState = false; throw; } } private static List ParseLdfFramesAndSignals(string ldfText) { // 说明:此解析器只用于生成“帧-信号全集池”,不做位宽/缩放等语义解析。 // 目标:尽可能容错地从 Frames 区域提取 FrameName 与其包含的 SignalName 列表。 var framesBlock = TryExtractNamedBlock(ldfText, "Frames"); if (string.IsNullOrWhiteSpace(framesBlock)) { return new List(); } var result = new List(); var exists = new HashSet(StringComparer.Ordinal); // Frame 定义一般形式:FrameName : ... { ... } // 这里以非贪婪匹配提取每个 Frame 的 body var frameRegex = new Regex(@"(?s)(?[A-Za-z_][A-Za-z0-9_]*)\s*:\s*.*?\{(?.*?)\}\s*;?", RegexOptions.Compiled); var sigRegex = new Regex(@"(?m)^\s*(?[A-Za-z_][A-Za-z0-9_]*)\s*[,;]", RegexOptions.Compiled); foreach (Match fm in frameRegex.Matches(framesBlock)) { var frameName = fm.Groups["name"].Value; var body = fm.Groups["body"].Value; if (string.IsNullOrWhiteSpace(frameName) || string.IsNullOrWhiteSpace(body)) { continue; } foreach (Match sm in sigRegex.Matches(body)) { var sigName = sm.Groups["sig"].Value; if (string.IsNullOrWhiteSpace(sigName)) { continue; } // 排除明显的关键字(避免误采集) if (IsReservedKeyword(sigName)) { continue; } var key = $"{frameName}:{sigName}"; if (!exists.Add(key)) { continue; } result.Add(new LinLdfModel { MsgName = frameName, SignalName = sigName, Name = null, SignalDesc = null, SignalUnit = null, IsSeletedInfo = 0, }); } } return result; } private static string? TryExtractNamedBlock(string text, string blockName) { // 提取形如:blockName { ... } 的块内容(不包含外层大括号)。 // 基于括号深度扫描,避免正则在嵌套结构上失效。 var idx = CultureInvariantIndexOf(text, blockName); if (idx < 0) return null; var braceIdx = text.IndexOf('{', idx); if (braceIdx < 0) return null; int depth = 0; for (int i = braceIdx; i < text.Length; i++) { var ch = text[i]; if (ch == '{') depth++; else if (ch == '}') { depth--; if (depth == 0) { return text.Substring(braceIdx + 1, i - braceIdx - 1); } } } return null; } private static int CultureInvariantIndexOf(string text, string value) { return text.IndexOf(value, StringComparison.OrdinalIgnoreCase); } private static bool IsReservedKeyword(string token) { // LDF 常见关键字/区块名(用于降低误匹配概率) switch (token) { case "Frames": case "Signals": case "Signal": case "Nodes": case "Master": case "Slaves": case "Diagnostic": case "Diagnostics": case "Checksum": case "Event_triggered_frames": case "Sporadic_frames": case "Schedule_tables": case "Node_attributes": case "Node_composition": case "LIN_protocol": case "LIN_protocol_version": case "LIN_speed": case "Protocol_version": return true; default: return false; } } } }