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