Files
CapMachine/CapMachine.Wpf/Services/ZlgLinDriveService.cs
2026-02-06 12:34:34 +08:00

318 lines
11 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
{
/// <summary>
/// ZLG LIN 驱动服务(共享设备句柄)。
/// </summary>
public sealed class ZlgLinDriveService : BindableBase
{
private readonly ILogService _log;
private readonly ZlgCanDriveService _zlgCanDriveService;
/// <summary>
/// 当前选中的配置程序(沿用原有 FreeSql 模型)。
/// </summary>
public CanLinConfigPro? SelectedCanLinConfigPro { get; set; }
private bool _openState;
/// <summary>
/// LIN 打开状态。
/// </summary>
public bool OpenState
{
get { return _openState; }
private set { _openState = value; RaisePropertyChanged(); }
}
private bool _ldfParserState;
/// <summary>
/// LDF 解析状态ZLG LIN 暂未接入 LDF固定为 false
/// </summary>
public bool LdfParserState
{
get { return _ldfParserState; }
private set { _ldfParserState = value; RaisePropertyChanged(); }
}
/// <summary>
/// LDF 消息集合UI 绑定ZLG LIN 暂未接入 LDF默认空。
/// </summary>
public ObservableCollection<LinLdfModel> ListLinLdfModel { get; private set; } = new ObservableCollection<LinLdfModel>();
/// <summary>
/// 是否启用调度发送(与 UI 的调度表使能语义对齐)。
/// </summary>
public bool SchEnable
{
get { return _zlgCanDriveService.Driver.SchEnable; }
set { _zlgCanDriveService.Driver.SchEnable = value; RaisePropertyChanged(); }
}
/// <summary>
/// 是否启用事件驱动发送。
/// </summary>
public bool IsCycleSend
{
get { return _zlgCanDriveService.Driver.IsCycleSend; }
set { _zlgCanDriveService.Driver.IsCycleSend = value; RaisePropertyChanged(); }
}
/// <summary>
/// 构造。
/// </summary>
/// <param name="zlgCanDriveService">共享 CAN 服务。</param>
/// <param name="logService">日志服务。</param>
public ZlgLinDriveService(ZlgCanDriveService zlgCanDriveService, ILogService logService)
{
_zlgCanDriveService = zlgCanDriveService;
_log = logService;
_zlgCanDriveService.Driver.LinFrameReceived += frame =>
{
// 未来接入 LDF 后在这里做 Sync/Decode当前仅保留事件链路不做假解析。
};
}
/// <summary>
/// 初始化 LIN 配置信息(目前仅缓存)。
/// </summary>
/// <param name="selectedLinConfigPro">选中的配置。</param>
public void InitLinConfig(CanLinConfigPro selectedLinConfigPro)
{
SelectedCanLinConfigPro = selectedLinConfigPro;
}
/// <summary>
/// 打开 LIN共享设备句柄
/// </summary>
/// <param name="deviceIndex">设备索引。</param>
/// <param name="baudRate">波特率。</param>
/// <param name="isMaster">是否主节点。</param>
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;
}
}
/// <summary>
/// 关闭 LIN共享设备句柄下当前实现以 CloseDevice 为准:关闭将同时关闭 CAN/LIN
/// </summary>
public void CloseDevice()
{
// ZLG 的通道句柄都在 Driver 内部;当前 Close 会关闭所有通道,保持与旧系统“同一时刻只有一种驱动工作”的原则一致。
_zlgCanDriveService.CloseDevice();
OpenState = false;
LdfParserState = false;
}
/// <summary>
/// 加载 LDFZLG LIN 暂未接入 LDF 解析 DLL因此此处明确失败不返回模拟数据
/// </summary>
/// <param name="path">LDF 路径。</param>
public ObservableCollection<LinLdfModel> 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<LinLdfModel> ParseLdfFramesAndSignals(string ldfText)
{
// 说明:此解析器只用于生成“帧-信号全集池”,不做位宽/缩放等语义解析。
// 目标:尽可能容错地从 Frames 区域提取 FrameName 与其包含的 SignalName 列表。
var framesBlock = TryExtractNamedBlock(ldfText, "Frames");
if (string.IsNullOrWhiteSpace(framesBlock))
{
return new List<LinLdfModel>();
}
var result = new List<LinLdfModel>();
var exists = new HashSet<string>(StringComparer.Ordinal);
// Frame 定义一般形式FrameName : ... { ... }
// 这里以非贪婪匹配提取每个 Frame 的 body
var frameRegex = new Regex(@"(?s)(?<name>[A-Za-z_][A-Za-z0-9_]*)\s*:\s*.*?\{(?<body>.*?)\}\s*;?", RegexOptions.Compiled);
var sigRegex = new Regex(@"(?m)^\s*(?<sig>[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;
}
}
}
}