diff --git a/ACP.sln b/ACP.sln
index 34931f0..cee7287 100644
--- a/ACP.sln
+++ b/ACP.sln
@@ -45,6 +45,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ACP", "ACP\ACP.csproj", "{C
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TSMasterCAN", "TSMasterCAN\TSMasterCAN.csproj", "{B45DC94C-2CBB-4119-8787-B52A9E7EC58B}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TSMasterFlexRay", "TSMasterFlexRay\TSMasterFlexRay.csproj", "{68B228BA-52B4-4C01-B260-235F5444ADA7}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TSMasterLIN", "TSMasterLIN\TSMasterLIN.csproj", "{75F658D1-5DC4-4638-8959-2E7A87212B1D}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -131,6 +135,14 @@ Global
{B45DC94C-2CBB-4119-8787-B52A9E7EC58B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B45DC94C-2CBB-4119-8787-B52A9E7EC58B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B45DC94C-2CBB-4119-8787-B52A9E7EC58B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {68B228BA-52B4-4C01-B260-235F5444ADA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {68B228BA-52B4-4C01-B260-235F5444ADA7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {68B228BA-52B4-4C01-B260-235F5444ADA7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {68B228BA-52B4-4C01-B260-235F5444ADA7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {75F658D1-5DC4-4638-8959-2E7A87212B1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {75F658D1-5DC4-4638-8959-2E7A87212B1D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {75F658D1-5DC4-4638-8959-2E7A87212B1D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {75F658D1-5DC4-4638-8959-2E7A87212B1D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/TSMasterFlexRay/FlexRay.cs b/TSMasterFlexRay/FlexRay.cs
new file mode 100644
index 0000000..8d3a6a7
--- /dev/null
+++ b/TSMasterFlexRay/FlexRay.cs
@@ -0,0 +1,470 @@
+
+using Common.Attributes;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Text.Json;
+using TSMaster;
+
+namespace TSMasterFlexRay
+{
+
+ [ACPCommand]
+ public class FlexRay : IDisposable
+ {
+ #region 静态成员
+
+ public static event Action? ConnectEvent;
+ public static event Action? DisConnectEvent;
+ public static bool ConnectFlag { get; set; } = false;
+
+ ///
+ /// 实时接收到的 FlexRay 报文缓存(按 SlotId 索引,保留最新帧)。
+ /// 供界面轮询读取。
+ ///
+ public static ConcurrentDictionary RealTimeMessages { get; } = new();
+
+ #endregion
+
+ #region 实例字段与属性
+
+ /// 设备类型号(TSMaster 中作为配置参考保留)
+ public uint DeviceType { get; }
+ /// 设备索引
+ public uint DeviceIndex { get; }
+ /// 最大通道数
+ public int MaxChannels { get; }
+ /// 工程路径(FlexRay 需通过 TSMaster 工程文件加载 Cluster 配置)
+ public string ProjectPath { get; }
+
+ /// 数据库解析器
+ public FlexRayDBParse DBParser => FlexRayDBParse.Instance;
+
+ ///
+ /// FlexRay 帧解码事件:当接收到帧并通过数据库解码后触发。
+ /// 参数:(通道号, 解码后的报文信息)
+ ///
+ public event Action? OnFrameDecoded;
+
+ private bool _disposed;
+ private bool _isOpened;
+ private TFlexRayQueueEvent_Win32? _instanceListener;
+ private TFlexRayQueueEvent_Win32? _preTxListener;
+
+ #endregion
+
+ #region 构造函数
+
+ /// 默认构造函数(使用默认工程路径)
+ public FlexRay() : this(43, 0, 2, "") { }
+
+ ///
+ /// 带配置参数的构造函数。
+ ///
+ /// 设备类型号
+ /// 设备索引
+ /// 最大通道数
+ /// TSMaster 工程路径(FlexRay 必须加载工程)
+ public FlexRay(uint deviceType, uint deviceIndex, int maxChannels, string projectPath)
+ {
+ DeviceType = deviceType;
+ DeviceIndex = deviceIndex;
+ MaxChannels = maxChannels;
+ ProjectPath = projectPath;
+ }
+
+ #endregion
+
+ #region 实例方法 — 设备管理
+
+ ///
+ /// 打开设备:加载工程 → 连接 → 启动 RBS → 注册监听。
+ /// FlexRay 的硬件配置(波特率、集群参数等)由 TSMaster 工程统一管理,
+ /// 代码中无法直接设置,需通过 ShowChannelMappingWindow 或工程本身配置。
+ ///
+ /// true 表示成功
+ public bool 打开设备()
+ {
+ if (_isOpened) return true;
+ if (ConnectFlag) { _isOpened = true; return true; }
+
+ try
+ {
+ // 1. 通过工程文件初始化(FlexRay 必须依赖工程)
+ var re = Init("TSMasterFlexRay", ProjectPath);
+ if (re != 0)
+ {
+ Debug.WriteLine($"TSMaster FlexRay 工程加载失败,错误代码:{re}");
+ return false;
+ }
+
+ // 2. 使能接收 FIFO(可选,用于批量拉取模式)
+ TsMasterApi.tsfifo_enable_receive_fifo();
+
+ // 3. 连接硬件
+ re = Connect();
+ if (re != 0)
+ {
+ Debug.WriteLine($"FlexRay 连接失败,错误代码:{re}");
+ return false;
+ }
+
+ // 4. 注册接收监听
+ _instanceListener = new TFlexRayQueueEvent_Win32(OnFlexRayReceived);
+ RegisterListener(_instanceListener);
+
+ _isOpened = true;
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine($"打开 FlexRay 设备异常:{ex.Message}");
+ return false;
+ }
+ }
+
+ ///
+ /// 关闭 FlexRay 设备:注销监听 → 停止 RBS → 断开连接。
+ ///
+ public void 关闭FlexRay设备()
+ {
+ if (!_isOpened && !ConnectFlag) return;
+
+ try
+ {
+ if (_preTxListener != null)
+ {
+ UnRegisterPreTxListener(_preTxListener);
+ _preTxListener = null;
+ }
+ if (_instanceListener != null)
+ {
+ UnRegisterListener(_instanceListener);
+ _instanceListener = null;
+ }
+ DisConnect();
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine($"关闭FlexRay设备异常:{ex.Message}");
+ }
+ finally
+ {
+ _isOpened = false;
+ }
+ }
+
+ ///
+ /// FlexRay 接收回调:接收原始帧 → 缓存 → 触发 OnFrameDecoded 事件。
+ ///
+ private void OnFlexRayReceived(ref int AObj, ref TLIBFlexRay AData)
+ {
+ if (_disposed) return;
+
+ // 始终缓存最新帧(按 SlotId)
+ RealTimeMessages[AData.FSlotId] = AData;
+
+ if (OnFrameDecoded == null) return;
+
+ try
+ {
+ var decoded = new FlexRayDBMessage
+ {
+ nSlotId = AData.FSlotId,
+ nCycleNumber = AData.FCycleNumber,
+ nChannelMask = AData.FChannelMask,
+ nDataLength = AData.FActualPayloadLength,
+ Data = new byte[AData.FActualPayloadLength]
+ };
+ Array.Copy(AData.FData, decoded.Data, AData.FActualPayloadLength);
+
+ OnFrameDecoded?.Invoke(AData.FIdxChn, decoded);
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine($"FlexRay 帧解码异常:{ex.Message}");
+ }
+ }
+
+ #endregion
+
+ #region IDisposable
+
+ public void Dispose()
+ {
+ if (_disposed) return;
+ _disposed = true;
+ 关闭FlexRay设备();
+ GC.SuppressFinalize(this);
+ }
+
+ #endregion
+
+ ///
+ /// 初始化 TSMaster FlexRay(必须加载工程文件)
+ ///
+ /// 应用名
+ /// 工程路径,为空则仅初始化库
+ ///
+ [Browsable(false)]
+ public static int Init(string ProjectName, string? filePath = null)
+ {
+ if (string.IsNullOrEmpty(filePath))
+ {
+ return TsMasterApi.initialize_lib_tsmaster(ProjectName);
+ }
+ else
+ {
+ return TsMasterApi.initialize_lib_tsmaster_with_project(ProjectName, Path.GetFullPath(filePath));
+ }
+ }
+
+ ///
+ /// 释放 TSMaster
+ ///
+ [Browsable(false)]
+ public static void Release()
+ {
+ TsMasterApi.finalize_lib_tsmaster();
+ }
+
+ [Browsable(false)]
+ public static int RegisterListener(TFlexRayQueueEvent_Win32 listenEvent)
+ {
+ int obj = 0;
+ var re = TsMasterApi.tsapp_register_event_flexray(ref obj, listenEvent);
+ Debug.Assert(re == 0);
+ return re;
+ }
+
+ [Browsable(false)]
+ public static int UnRegisterListener(TFlexRayQueueEvent_Win32 listenEvent)
+ {
+ int obj = 0;
+ var re = TsMasterApi.tsapp_unregister_event_flexray(ref obj, listenEvent);
+ Debug.Assert(re == 0);
+ return re;
+ }
+
+ ///
+ /// 注册发送前回调(FlexRay 特有,可用于发送前修改帧内容)
+ ///
+ ///
+ ///
+ [Browsable(false)]
+ public static int RegisterPreTxListener(TFlexRayQueueEvent_Win32 preTxEvent)
+ {
+ int obj = 0;
+ return TsMasterApi.tsapp_register_pretx_event_flexray(ref obj, preTxEvent);
+ }
+
+ [Browsable(false)]
+ public static int UnRegisterPreTxListener(TFlexRayQueueEvent_Win32 preTxEvent)
+ {
+ int obj = 0;
+ return TsMasterApi.tsapp_unregister_pretx_event_flexray(ref obj, preTxEvent);
+ }
+
+ ///
+ /// 连接(含 RBS 启动)
+ ///
+ ///
+ public static int Connect()
+ {
+ var re = TsMasterApi.tsapp_connect();
+ if (re == 0)
+ {
+ // FlexRay RBS 需先 enable 再 start
+ TsMasterApi.tscom_flexray_rbs_enable(true);
+ re = TsMasterApi.tscom_flexray_rbs_start();
+ if (re == 0)
+ {
+ ConnectFlag = true;
+ Task.Run(() => ConnectEvent?.Invoke());
+ }
+ else
+ {
+ Debug.WriteLine($"FlexRay RBS 启动失败,错误代码:{re}");
+ }
+ }
+ else
+ {
+ Debug.WriteLine($"FlexRay 连接失败,错误代码:{re}");
+ }
+ return re;
+ }
+
+ ///
+ /// 断开(含 RBS 停止)
+ ///
+ ///
+ public static int DisConnect()
+ {
+ var re = TsMasterApi.tsapp_disconnect();
+ if (re == 0)
+ {
+ TsMasterApi.tscom_flexray_rbs_enable(false);
+ TsMasterApi.tscom_flexray_rbs_stop();
+ ConnectFlag = false;
+ Task.Run(() => DisConnectEvent?.Invoke());
+ }
+ else
+ {
+ Debug.WriteLine($"断开 FlexRay 连接失败,错误代码:{re}");
+ }
+ return re;
+ }
+
+ ///
+ /// 获取工程中 FlexRay 数据库数量
+ ///
+ ///
+ public static int GetDBCount()
+ {
+ int count = 0;
+ TsMasterApi.tsdb_get_flexray_db_count(ref count);
+ return count;
+ }
+
+ ///
+ /// 弹出通道映射窗口(FlexRay 硬件配置推荐通过此窗口完成)
+ ///
+ ///
+ ///
+ [Browsable(false)]
+ public static int ShowChannelMappingWindow(bool isWait = false)
+ {
+ return TsMasterApi.tsapp_show_tsmaster_window("Hardware", isWait);
+ }
+
+ ///
+ /// 发送一帧 FlexRay(异步)
+ ///
+ ///
+ ///
+ [Browsable(false)]
+ public static int SendMsg(TLIBFlexRay msg)
+ {
+ return tsapp_transmit_flexray_async(ref msg);
+ }
+
+ ///
+ /// 发送一帧 FlexRay(按参数构造)
+ ///
+ /// 通道索引(0-based)
+ /// 通道掩码:1=A, 2=B, 3=AB
+ /// 有效负载长度
+ /// 基准循环号
+ /// 时隙号
+ /// 数据数组
+ ///
+ public static int SendMsg(byte AIdxChn, byte AChannelMask, byte ADLC,
+ byte ABaseCycle, ushort ASlotId, byte[] AData)
+ {
+ var send = new TLIBFlexRay(AIdxChn, AChannelMask, ADLC, ABaseCycle, ASlotId, AData);
+ return tsapp_transmit_flexray_async(ref send);
+ }
+
+ ///
+ /// 通过 FIFO 批量拉取已接收的 FlexRay 消息
+ ///
+ /// 缓冲区大小
+ /// 通道号(0-based)
+ /// true=TX, false=RX
+ ///
+ public static TLIBFlexRay[] ReceiveMsgs(ref int bufferSize, byte AChn, bool ATxRx)
+ {
+ TLIBFlexRay[] buffer = new TLIBFlexRay[bufferSize];
+ TsMasterApi.tsfifo_receive_flexray_msgs_list(ref buffer, ref bufferSize, AChn, ATxRx);
+ return buffer;
+ }
+
+ ///
+ /// 通过 RBS 地址获取信号值(地址格式:通道/Cluster/ECU/Frame/Signal)
+ ///
+ ///
+ ///
+ public static double GetSignalValue(string address)
+ {
+ double value = 0;
+ TsMasterApi.tscom_flexray_rbs_get_signal_value_by_address(address, ref value);
+ return value;
+ }
+
+ ///
+ /// 通过 RBS 地址设置信号值
+ ///
+ ///
+ ///
+ ///
+ public static int SetSignalValue(string address, double value)
+ {
+ TsMasterApi.tscom_flexray_rbs_set_signal_value_by_address(address, value);
+ return 0;
+ }
+
+ ///
+ /// 按名称激活 Cluster(RBS 仿真)
+ ///
+ /// 通道索引
+ /// true=激活
+ /// Cluster 名称
+ ///
+ public static int ActivateCluster(int chnIdx, bool enable, string clusterName)
+ {
+ return TsMasterApi.tscom_flexray_rbs_activate_cluster_by_name(chnIdx, enable, clusterName, false);
+ }
+
+ ///
+ /// 按名称激活 ECU(RBS 仿真)
+ ///
+ public static int ActivateECU(int chnIdx, bool enable, string clusterName, string ecuName)
+ {
+ return TsMasterApi.tscom_flexray_rbs_activate_ecu_by_name(chnIdx, enable, clusterName, ecuName, false);
+ }
+
+ ///
+ /// 按名称激活 Frame(RBS 仿真)
+ ///
+ public static int ActivateFrame(int chnIdx, bool enable, string clusterName, string ecuName, string frameName)
+ {
+ return TsMasterApi.tscom_flexray_rbs_activate_frame_by_name(chnIdx, enable, clusterName, ecuName, frameName);
+ }
+
+ ///
+ /// 开始记录日志
+ ///
+ public static int StartLogging(string filePath)
+ {
+ return TsMasterApi.tsapp_start_logging(Path.GetFullPath(filePath));
+ }
+
+ ///
+ /// 结束记录日志
+ ///
+ public static int StopLogging()
+ {
+ return TsMasterApi.tsapp_stop_logging();
+ }
+
+ ///
+ /// 获取错误提示
+ ///
+ public static string GetErrorDescription(int errorCode)
+ {
+ IntPtr ADesc = IntPtr.Zero;
+ TsMasterApi.tsapp_get_error_description(errorCode, ref ADesc);
+ if (ADesc == IntPtr.Zero) return $"未知错误代码: {errorCode}";
+ string? description = Marshal.PtrToStringAnsi(ADesc);
+ return description ?? $"未知错误代码: {errorCode}";
+ }
+
+ // FlexRay 发送函数(TSMaster.dll 中 StdCall 导出,需手动声明)
+ [DllImport(".\\TSMaster.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
+ public static extern int tsapp_transmit_flexray_async(ref TLIBFlexRay AMsg);
+ }
+}
diff --git a/TSMasterFlexRay/FlexRayDBParse.cs b/TSMasterFlexRay/FlexRayDBParse.cs
new file mode 100644
index 0000000..5e4300f
--- /dev/null
+++ b/TSMasterFlexRay/FlexRayDBParse.cs
@@ -0,0 +1,248 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Text.Json.Serialization;
+using TSMaster;
+
+namespace TSMasterFlexRay
+{
+ ///
+ /// FlexRay 网络(Cluster)
+ ///
+ public struct flexray_network
+ {
+ [JsonInclude]
+ public string network_name;
+ [JsonInclude]
+ public flexray_node[] flexray_nodes;
+ }
+
+ ///
+ /// FlexRay 节点(ECU)
+ ///
+ public struct flexray_node
+ {
+ [JsonInclude]
+ public string node_name;
+ [JsonInclude]
+ public flexray_message[] tx_flexray_Messages;
+ [JsonInclude]
+ public flexray_message[] rx_flexray_Messages;
+ }
+
+ ///
+ /// FlexRay 帧(Frame)
+ ///
+ public struct flexray_message
+ {
+ [JsonInclude]
+ public string message_name;
+ [JsonInclude]
+ public int slot_id;
+ [JsonInclude]
+ public int channel_mask;
+ [JsonInclude]
+ public int base_cycle;
+ [JsonInclude]
+ public int rep_cycle;
+ [JsonInclude]
+ public bool is_startup_frame;
+ [JsonInclude]
+ public int dlc;
+ [JsonInclude]
+ public string[] signals;
+ }
+
+ ///
+ /// FlexRay 解码后的信号信息
+ ///
+ public struct FlexRaySignal
+ {
+ /// 信号名称
+ public string name;
+ /// 起始位
+ public int start_bit;
+ /// 长度(bit)
+ public int length;
+ /// 因子
+ public double factor;
+ /// 偏移
+ public double offset;
+ /// 初始值
+ public double init_value;
+ /// 信号类型
+ public TSignalType signal_type;
+ /// 字节序(true=小端)
+ public bool is_intel;
+ }
+
+ ///
+ /// FlexRay 解码后的报文信息
+ ///
+ public class FlexRayDBMessage
+ {
+ /// 时隙号
+ public int nSlotId;
+ /// 周期号
+ public int nCycleNumber;
+ /// 通道掩码
+ public int nChannelMask;
+ /// 有效负载长度
+ public int nDataLength;
+ /// 数据
+ public byte[] Data = Array.Empty();
+ }
+
+ public class FlexRayDBParse
+ {
+ private static FlexRayDBParse? _instance;
+ /// 单例实例
+ public static FlexRayDBParse Instance => _instance ??= new FlexRayDBParse();
+
+ /// 最大通道数(等于 MsgDatabase 的维度)
+ public int MaxChannels => MsgDatabase.Count;
+
+ ///
+ /// FlexRay 报文数据库(按通道索引,每个通道包含该通道上所有帧)。
+ ///
+ public static List> MsgDatabase { get; set; } =
+ [.. Enumerable.Range(0, 12).Select(_ => new List())];
+
+ /// FlexRay 网络拓扑(Cluster/ECU/Frame/Signal)
+ public static flexray_network Network;
+
+ ///
+ /// 解析指定索引的 FlexRay 数据库,填充 MsgDatabase 和 Network。
+ /// FlexRay 数据库由工程加载,通过 tsdb_get_flexray_db_count 获取数量。
+ ///
+ /// 数据库索引(0-based)
+ /// 映射的通道号
+ public static void parse(int dbIndex, int channel)
+ {
+ int ASignalCount = 0;
+ int AFrameCount = 0;
+ int AECUcount = 0;
+ long ASupportMask = 0;
+ long AFlags = 0;
+ IntPtr temp1 = IntPtr.Zero;
+ IntPtr temp2 = IntPtr.Zero;
+
+ // 1. 获取 Cluster 属性
+ int ret = TsMasterApi.tsdb_get_flexray_db_properties_by_index_verbose(
+ dbIndex, ref ASignalCount, ref AFrameCount, ref AECUcount,
+ ref ASupportMask, ref AFlags, ref temp1, ref temp2);
+
+ string clusterName = Marshal.PtrToStringAnsi(temp1) ?? "";
+ Network = new flexray_network
+ {
+ network_name = clusterName,
+ flexray_nodes = new flexray_node[AECUcount]
+ };
+
+ // 2. 遍历所有 ECU
+ for (int i = 0; i < AECUcount; i++)
+ {
+ int ATXFramecount = 0;
+ int ARXFramecount = 0;
+
+ TsMasterApi.tsdb_get_flexray_ecu_properties_by_index_verbose(
+ dbIndex, i, ref ATXFramecount, ref ARXFramecount, ref temp1, ref temp2);
+
+ string ecuName = Marshal.PtrToStringAnsi(temp1) ?? "";
+ Network.flexray_nodes[i].node_name = ecuName;
+ Network.flexray_nodes[i].tx_flexray_Messages = new flexray_message[ATXFramecount];
+ Network.flexray_nodes[i].rx_flexray_Messages = new flexray_message[ARXFramecount];
+
+ // TX Frame
+ for (int tx = 0; tx < ATXFramecount; tx++)
+ {
+ var msg = ParseFrame(dbIndex, i, tx, true);
+ Network.flexray_nodes[i].tx_flexray_Messages[tx] = msg;
+ MsgDatabase[channel].Add(msg);
+ }
+
+ // RX Frame
+ for (int rx = 0; rx < ARXFramecount; rx++)
+ {
+ var msg = ParseFrame(dbIndex, i, rx, false);
+ Network.flexray_nodes[i].rx_flexray_Messages[rx] = msg;
+ }
+ }
+
+ MsgDatabase[channel].Sort((a, b) => a.slot_id.CompareTo(b.slot_id));
+ }
+
+ ///
+ /// 解析单帧属性(TX 或 RX)
+ ///
+ private static flexray_message ParseFrame(int dbIndex, int ecuIndex, int frameIndex, bool isTx)
+ {
+ int AFRChannelMask = 0;
+ int AFRBaseCycle = 0;
+ int ARepCycle = 0;
+ bool isStartupFrame = false;
+ int ASlotID = 0;
+ long CycleMask = 0;
+ int AFRSignalCount = 0;
+ int AFRDLC = 0;
+ IntPtr temp1 = IntPtr.Zero;
+ IntPtr temp2 = IntPtr.Zero;
+
+ TsMasterApi.tsdb_get_flexray_frame_properties_by_index_verbose(
+ dbIndex, ecuIndex, frameIndex, isTx,
+ ref AFRChannelMask, ref AFRBaseCycle, ref ARepCycle, ref isStartupFrame,
+ ref ASlotID, ref CycleMask, ref AFRSignalCount, ref AFRDLC,
+ ref temp1, ref temp2);
+
+ string frameName = Marshal.PtrToStringAnsi(temp1) ?? "";
+ var msg = new flexray_message
+ {
+ message_name = frameName,
+ slot_id = ASlotID,
+ channel_mask = AFRChannelMask,
+ base_cycle = AFRBaseCycle,
+ rep_cycle = ARepCycle,
+ is_startup_frame = isStartupFrame,
+ dlc = AFRDLC,
+ signals = new string[AFRSignalCount]
+ };
+
+ // 遍历信号
+ for (int s = 0; s < AFRSignalCount; s++)
+ {
+ TSignalType Asignaltype = (TSignalType)0;
+ TFlexRayCompuMethod ACompuMethod = (TFlexRayCompuMethod)0;
+ bool AIsIntel = false;
+ int AStartBit = 0;
+ int AUpdateBit = 0;
+ int ALength = 0;
+ double AFactor = 0;
+ double AOffset = 0;
+ double AInitValue = 0;
+
+ TsMasterApi.tsdb_get_flexray_signal_properties_by_index_verbose(
+ dbIndex, ecuIndex, frameIndex, s, isTx,
+ ref Asignaltype, ref ACompuMethod, ref AIsIntel,
+ ref AStartBit, ref AUpdateBit, ref ALength,
+ ref AFactor, ref AOffset, ref AInitValue,
+ ref temp1, ref temp2);
+
+ string sigName = Marshal.PtrToStringAnsi(temp1) ?? "";
+ msg.signals[s] = sigName;
+ }
+
+ return msg;
+ }
+
+ ///
+ /// 解析网络拓扑(仅填充 Network,不写入 MsgDatabase)
+ ///
+ public static void rbs_parse(int dbIndex, int channel)
+ {
+ // parse 已同时填充 Network 和 MsgDatabase,此处保持与 CAN 一致的接口
+ parse(dbIndex, channel);
+ }
+ }
+}
diff --git a/TSMasterFlexRay/Interop.TSMasterAPI.dll b/TSMasterFlexRay/Interop.TSMasterAPI.dll
new file mode 100644
index 0000000..1d1400b
Binary files /dev/null and b/TSMasterFlexRay/Interop.TSMasterAPI.dll differ
diff --git a/TSMasterFlexRay/TSMaster.dll b/TSMasterFlexRay/TSMaster.dll
new file mode 100644
index 0000000..a34dd33
Binary files /dev/null and b/TSMasterFlexRay/TSMaster.dll differ
diff --git a/TSMasterFlexRay/TSMasterFlexRay.csproj b/TSMasterFlexRay/TSMasterFlexRay.csproj
new file mode 100644
index 0000000..3c6396c
--- /dev/null
+++ b/TSMasterFlexRay/TSMasterFlexRay.csproj
@@ -0,0 +1,19 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+ Interop.TSMasterAPI.dll
+
+
+
+
diff --git a/TSMasterLIN/Interop.TSMasterAPI.dll b/TSMasterLIN/Interop.TSMasterAPI.dll
new file mode 100644
index 0000000..1d1400b
Binary files /dev/null and b/TSMasterLIN/Interop.TSMasterAPI.dll differ
diff --git a/TSMasterLIN/LIN.cs b/TSMasterLIN/LIN.cs
new file mode 100644
index 0000000..3094a42
--- /dev/null
+++ b/TSMasterLIN/LIN.cs
@@ -0,0 +1,382 @@
+
+using Common.Attributes;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Text.Json;
+using TSMaster;
+
+namespace TSMasterLIN
+{
+ [ACPCommand]
+ public class LIN : IDisposable
+ {
+ #region 静态成员
+
+ public static event Action? ConnectEvent;
+ public static event Action? DisConnectEvent;
+ public static bool ConnectFlag { get; set; } = false;
+
+ ///
+ /// 实时接收到的 LIN 报文缓存(按帧 ID 索引,保留最新帧)。
+ ///
+ public static ConcurrentDictionary RealTimeMessages { get; } = new();
+
+ #endregion
+
+ #region 实例字段与属性
+
+ /// 设备类型号
+ public uint DeviceType { get; }
+ /// 设备索引
+ public uint DeviceIndex { get; }
+ /// 最大通道数
+ public int MaxChannels { get; }
+ /// 波特率(LIN 常用 19.2 kbps)
+ public float BaudRate { get; }
+ /// LIN 协议版本
+ public LIN_PROTOCOL Protocol { get; }
+
+ ///
+ /// LIN 帧解码事件:当接收到帧后触发。
+ /// 参数:(通道号, 帧信息)
+ ///
+ public event Action? OnLinFrameReceived;
+
+ private bool _disposed;
+ private bool _isOpened;
+ private TLINQueueEvent_Win32? _instanceListener;
+
+ #endregion
+
+ #region 构造函数
+
+ /// 默认构造函数(1 通道、19.2 kbps、LIN 2.1)
+ public LIN() : this(43, 0, 1, 19.2f, LIN_PROTOCOL.LIN_PROTOCOL_21) { }
+
+ ///
+ /// 带配置参数的构造函数。
+ ///
+ /// 设备类型号
+ /// 设备索引
+ /// 最大通道数
+ /// 波特率(常用 19.2)
+ /// LIN 协议版本
+ public LIN(uint deviceType, uint deviceIndex, int maxChannels, float baudRate, LIN_PROTOCOL protocol)
+ {
+ DeviceType = deviceType;
+ DeviceIndex = deviceIndex;
+ MaxChannels = maxChannels;
+ BaudRate = baudRate;
+ Protocol = protocol;
+ }
+
+ #endregion
+
+ #region 实例方法 — 设备管理
+
+ ///
+ /// 打开设备:初始化 TSMaster 库 → 设置通道数 → 配置波特率与协议 → 连接 → 注册监听。
+ ///
+ /// true 表示成功
+ public bool 打开设备()
+ {
+ if (_isOpened) return true;
+ if (ConnectFlag) { _isOpened = true; return true; }
+
+ try
+ {
+ // 1. 初始化 TSMaster 库
+ var re = Init("TSMasterLIN");
+ if (re != 0)
+ {
+ Debug.WriteLine($"TSMaster LIN 初始化失败,错误代码:{re}");
+ return false;
+ }
+
+ // 2. 设置通道数
+ TsMasterApi.tsapp_set_lin_channel_count(MaxChannels);
+
+ // 3. 配置各通道波特率与协议
+ for (int ch = 0; ch < MaxChannels; ch++)
+ {
+ TsMasterApi.tsapp_configure_baudrate_lin(ch, BaudRate, (int)Protocol);
+ }
+
+ // 4. 连接硬件
+ re = Connect();
+ if (re != 0)
+ {
+ Debug.WriteLine($"LIN 连接失败,错误代码:{re}");
+ return false;
+ }
+
+ // 5. 注册接收监听
+ _instanceListener = new TLINQueueEvent_Win32(OnLinReceived);
+ RegisterListener(_instanceListener);
+
+ _isOpened = true;
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine($"打开 LIN 设备异常:{ex.Message}");
+ return false;
+ }
+ }
+
+ ///
+ /// 关闭 LIN 设备:注销监听 → 断开连接。
+ ///
+ public void 关闭LIN设备()
+ {
+ if (!_isOpened && !ConnectFlag) return;
+
+ try
+ {
+ if (_instanceListener != null)
+ {
+ UnRegisterListener(_instanceListener);
+ _instanceListener = null;
+ }
+ DisConnect();
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine($"关闭LIN设备异常:{ex.Message}");
+ }
+ finally
+ {
+ _isOpened = false;
+ }
+ }
+
+ ///
+ /// LIN 接收回调:缓存最新帧 → 触发 OnLinFrameReceived 事件。
+ ///
+ private void OnLinReceived(ref int AObj, ref TLIBLIN AData)
+ {
+ if (_disposed) return;
+
+ // 始终缓存最新报文(按 ID)
+ RealTimeMessages[AData.FIdentifier] = AData;
+
+ OnLinFrameReceived?.Invoke(AData.FIdxChn, AData);
+ }
+
+ #endregion
+
+ #region IDisposable
+
+ public void Dispose()
+ {
+ if (_disposed) return;
+ _disposed = true;
+ 关闭LIN设备();
+ GC.SuppressFinalize(this);
+ }
+
+ #endregion
+
+ ///
+ /// 初始化 TSMasterLIN
+ ///
+ ///
+ /// 工程路径,为空则仅初始化库
+ ///
+ [Browsable(false)]
+ public static int Init(string ProjectName, string? filePath = null)
+ {
+ if (string.IsNullOrEmpty(filePath))
+ {
+ return TsMasterApi.initialize_lib_tsmaster(ProjectName);
+ }
+ else
+ {
+ return TsMasterApi.initialize_lib_tsmaster_with_project(ProjectName, Path.GetFullPath(filePath));
+ }
+ }
+
+ ///
+ /// 释放 TSMaster
+ ///
+ [Browsable(false)]
+ public static void Release()
+ {
+ TsMasterApi.finalize_lib_tsmaster();
+ }
+
+ public static int obj = 0;
+ public static TLINQueueEvent_Win32? listener;
+
+ [Browsable(false)]
+ public static int RegisterListener(TLINQueueEvent_Win32 listenEvent)
+ {
+ listener = listenEvent;
+ var re = TsMasterApi.tsapp_register_event_lin(ref obj, listener);
+ Debug.Assert(re == 0);
+ return re;
+ }
+
+ [Browsable(false)]
+ public static int UnRegisterListener(TLINQueueEvent_Win32? listenEvent = null)
+ {
+ if (listenEvent is not null)
+ {
+ listener = listenEvent;
+ }
+ var re = TsMasterApi.tsapp_unregister_event_lin(ref obj, listener);
+ Debug.Assert(re == 0);
+ return re;
+ }
+
+ ///
+ /// 连接
+ ///
+ ///
+ public static int Connect()
+ {
+ var re = TsMasterApi.tsapp_connect();
+ if (re == 0)
+ {
+ ConnectFlag = true;
+ Task.Run(() => ConnectEvent?.Invoke());
+ }
+ else
+ {
+ Debug.WriteLine($"LIN 连接失败,错误代码:{re}");
+ }
+ return re;
+ }
+
+ ///
+ /// 断开
+ ///
+ ///
+ public static int DisConnect()
+ {
+ var re = TsMasterApi.tsapp_disconnect();
+ if (re == 0)
+ {
+ ConnectFlag = false;
+ Task.Run(() => DisConnectEvent?.Invoke());
+ }
+ else
+ {
+ Debug.WriteLine($"断开 LIN 连接失败,错误代码:{re}");
+ }
+ return re;
+ }
+
+ ///
+ /// 设置节点角色(LIN 特有:主节点/从节点)
+ /// LIN 是单主多从总线,只有 Master 能发起帧头调度。
+ ///
+ /// 通道号(0-based)
+ /// 节点类型:T_MasterNode / T_SlaveNode
+ ///
+ public static int SetNodeType(byte channel, TLINNodeType nodeType)
+ {
+ return TsMasterApi.tslin_set_node_functiontype(channel, nodeType);
+ }
+
+ ///
+ /// 设置当前通道为主节点
+ ///
+ public static int SetMaster(byte channel)
+ {
+ return TsMasterApi.tslin_set_node_functiontype(channel, TLINNodeType.T_MasterNode);
+ }
+
+ ///
+ /// 设置当前通道为从节点
+ ///
+ public static int SetSlave(byte channel)
+ {
+ return TsMasterApi.tslin_set_node_functiontype(channel, TLINNodeType.T_SlaveNode);
+ }
+
+ ///
+ /// 弹出通道映射窗口
+ ///
+ [Browsable(false)]
+ public static int ShowChannelMappingWindow(bool isWait = false)
+ {
+ return TsMasterApi.tsapp_show_tsmaster_window("Hardware", isWait);
+ }
+
+ ///
+ /// 发送一帧 LIN(异步)。
+ /// LIN 发送语义:Master 发帧头(Header)后由指定节点应答数据。
+ ///
+ ///
+ ///
+ [Browsable(false)]
+ public static int SendMsg(TLIBLIN msg)
+ {
+ return TsMasterApi.tsapp_transmit_lin_async(ref msg);
+ }
+
+ ///
+ /// 发送一帧 LIN(按参数构造)。
+ ///
+ /// 通道索引(0-based)
+ /// 帧 ID(6 位有效)
+ /// 数据长度(1~8 字节)
+ /// true=Master 同时发送数据(Master 发送帧+数据);false=仅发帧头,等待从节点应答
+ ///
+ public static int SendMsg(byte AIdxChn, int AID, byte ADLC, bool AIsTx)
+ {
+ var send = new TLIBLIN((APP_CHANNEL)AIdxChn, (byte)AID, ADLC, AIsTx);
+ return TsMasterApi.tsapp_transmit_lin_async(ref send);
+ }
+
+ ///
+ /// 发送一帧 LIN(携带数据)。
+ ///
+ /// 通道索引
+ /// 帧 ID
+ /// 数据数组(最多 8 字节)
+ /// true=Master 发送数据;false=仅发帧头
+ ///
+ public static int SendMsg(byte AIdxChn, int AID, byte[] AData, bool AIsTx = true)
+ {
+ byte dlc = (byte)Math.Min(AData.Length, 8);
+ var send = new TLIBLIN((APP_CHANNEL)AIdxChn, (byte)AID, dlc, AIsTx);
+ Array.Copy(AData, 0, send.FData, 0, dlc);
+ return TsMasterApi.tsapp_transmit_lin_async(ref send);
+ }
+
+ ///
+ /// 开始记录日志
+ ///
+ public static int StartLogging(string filePath)
+ {
+ return TsMasterApi.tsapp_start_logging(Path.GetFullPath(filePath));
+ }
+
+ ///
+ /// 结束记录日志
+ ///
+ public static int StopLogging()
+ {
+ return TsMasterApi.tsapp_stop_logging();
+ }
+
+ ///
+ /// 获取错误提示
+ ///
+ public static string GetErrorDescription(int errorCode)
+ {
+ IntPtr ADesc = IntPtr.Zero;
+ TsMasterApi.tsapp_get_error_description(errorCode, ref ADesc);
+ if (ADesc == IntPtr.Zero) return $"未知错误代码: {errorCode}";
+ string? description = Marshal.PtrToStringAnsi(ADesc);
+ return description ?? $"未知错误代码: {errorCode}";
+ }
+ }
+}
diff --git a/TSMasterLIN/TSMaster.dll b/TSMasterLIN/TSMaster.dll
new file mode 100644
index 0000000..a34dd33
Binary files /dev/null and b/TSMasterLIN/TSMaster.dll differ
diff --git a/TSMasterLIN/TSMasterLIN.csproj b/TSMasterLIN/TSMasterLIN.csproj
new file mode 100644
index 0000000..3c6396c
--- /dev/null
+++ b/TSMasterLIN/TSMasterLIN.csproj
@@ -0,0 +1,19 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+ Interop.TSMasterAPI.dll
+
+
+
+
diff --git a/UIShare/UIShare.csproj b/UIShare/UIShare.csproj
index 0bb28bb..3167d6d 100644
--- a/UIShare/UIShare.csproj
+++ b/UIShare/UIShare.csproj
@@ -25,5 +25,7 @@
+
+