Flexray,Lin添加

This commit is contained in:
“hsc”
2026-08-05 11:53:51 +08:00
parent 66bb6d36ba
commit 5a565f7361
11 changed files with 1152 additions and 0 deletions
+470
View File
@@ -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;
/// <summary>
/// 实时接收到的 FlexRay 报文缓存(按 SlotId 索引,保留最新帧)。
/// 供界面轮询读取。
/// </summary>
public static ConcurrentDictionary<int, TLIBFlexRay> RealTimeMessages { get; } = new();
#endregion
#region
/// <summary>设备类型号(TSMaster 中作为配置参考保留)</summary>
public uint DeviceType { get; }
/// <summary>设备索引</summary>
public uint DeviceIndex { get; }
/// <summary>最大通道数</summary>
public int MaxChannels { get; }
/// <summary>工程路径(FlexRay 需通过 TSMaster 工程文件加载 Cluster 配置)</summary>
public string ProjectPath { get; }
/// <summary>数据库解析器</summary>
public FlexRayDBParse DBParser => FlexRayDBParse.Instance;
/// <summary>
/// FlexRay 帧解码事件:当接收到帧并通过数据库解码后触发。
/// 参数:(通道号, 解码后的报文信息)
/// </summary>
public event Action<uint, FlexRayDBMessage>? OnFrameDecoded;
private bool _disposed;
private bool _isOpened;
private TFlexRayQueueEvent_Win32? _instanceListener;
private TFlexRayQueueEvent_Win32? _preTxListener;
#endregion
#region
/// <summary>默认构造函数(使用默认工程路径)</summary>
public FlexRay() : this(43, 0, 2, "") { }
/// <summary>
/// 带配置参数的构造函数。
/// </summary>
/// <param name="deviceType">设备类型号</param>
/// <param name="deviceIndex">设备索引</param>
/// <param name="maxChannels">最大通道数</param>
/// <param name="projectPath">TSMaster 工程路径(FlexRay 必须加载工程)</param>
public FlexRay(uint deviceType, uint deviceIndex, int maxChannels, string projectPath)
{
DeviceType = deviceType;
DeviceIndex = deviceIndex;
MaxChannels = maxChannels;
ProjectPath = projectPath;
}
#endregion
#region
/// <summary>
/// 打开设备:加载工程 → 连接 → 启动 RBS → 注册监听。
/// FlexRay 的硬件配置(波特率、集群参数等)由 TSMaster 工程统一管理,
/// 代码中无法直接设置,需通过 ShowChannelMappingWindow 或工程本身配置。
/// </summary>
/// <returns>true 表示成功</returns>
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;
}
}
/// <summary>
/// 关闭 FlexRay 设备:注销监听 → 停止 RBS → 断开连接。
/// </summary>
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;
}
}
/// <summary>
/// FlexRay 接收回调:接收原始帧 → 缓存 → 触发 OnFrameDecoded 事件。
/// </summary>
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
/// <summary>
/// 初始化 TSMaster FlexRay(必须加载工程文件)
/// </summary>
/// <param name="ProjectName">应用名</param>
/// <param name="filePath">工程路径,为空则仅初始化库</param>
/// <returns></returns>
[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));
}
}
/// <summary>
/// 释放 TSMaster
/// </summary>
[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;
}
/// <summary>
/// 注册发送前回调(FlexRay 特有,可用于发送前修改帧内容)
/// </summary>
/// <param name="preTxEvent"></param>
/// <returns></returns>
[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);
}
/// <summary>
/// 连接(含 RBS 启动)
/// </summary>
/// <returns></returns>
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;
}
/// <summary>
/// 断开(含 RBS 停止)
/// </summary>
/// <returns></returns>
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;
}
/// <summary>
/// 获取工程中 FlexRay 数据库数量
/// </summary>
/// <returns></returns>
public static int GetDBCount()
{
int count = 0;
TsMasterApi.tsdb_get_flexray_db_count(ref count);
return count;
}
/// <summary>
/// 弹出通道映射窗口(FlexRay 硬件配置推荐通过此窗口完成)
/// </summary>
/// <param name="isWait"></param>
/// <returns></returns>
[Browsable(false)]
public static int ShowChannelMappingWindow(bool isWait = false)
{
return TsMasterApi.tsapp_show_tsmaster_window("Hardware", isWait);
}
/// <summary>
/// 发送一帧 FlexRay(异步)
/// </summary>
/// <param name="msg"></param>
/// <returns></returns>
[Browsable(false)]
public static int SendMsg(TLIBFlexRay msg)
{
return tsapp_transmit_flexray_async(ref msg);
}
/// <summary>
/// 发送一帧 FlexRay(按参数构造)
/// </summary>
/// <param name="AIdxChn">通道索引(0-based</param>
/// <param name="AChannelMask">通道掩码:1=A, 2=B, 3=AB</param>
/// <param name="ADLC">有效负载长度</param>
/// <param name="ABaseCycle">基准循环号</param>
/// <param name="ASlotId">时隙号</param>
/// <param name="AData">数据数组</param>
/// <returns></returns>
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);
}
/// <summary>
/// 通过 FIFO 批量拉取已接收的 FlexRay 消息
/// </summary>
/// <param name="bufferSize">缓冲区大小</param>
/// <param name="AChn">通道号(0-based</param>
/// <param name="ATxRx">true=TX, false=RX</param>
/// <returns></returns>
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;
}
/// <summary>
/// 通过 RBS 地址获取信号值(地址格式:通道/Cluster/ECU/Frame/Signal
/// </summary>
/// <param name="address"></param>
/// <returns></returns>
public static double GetSignalValue(string address)
{
double value = 0;
TsMasterApi.tscom_flexray_rbs_get_signal_value_by_address(address, ref value);
return value;
}
/// <summary>
/// 通过 RBS 地址设置信号值
/// </summary>
/// <param name="address"></param>
/// <param name="value"></param>
/// <returns></returns>
public static int SetSignalValue(string address, double value)
{
TsMasterApi.tscom_flexray_rbs_set_signal_value_by_address(address, value);
return 0;
}
/// <summary>
/// 按名称激活 ClusterRBS 仿真)
/// </summary>
/// <param name="chnIdx">通道索引</param>
/// <param name="enable">true=激活</param>
/// <param name="clusterName">Cluster 名称</param>
/// <returns></returns>
public static int ActivateCluster(int chnIdx, bool enable, string clusterName)
{
return TsMasterApi.tscom_flexray_rbs_activate_cluster_by_name(chnIdx, enable, clusterName, false);
}
/// <summary>
/// 按名称激活 ECU(RBS 仿真)
/// </summary>
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);
}
/// <summary>
/// 按名称激活 FrameRBS 仿真)
/// </summary>
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);
}
/// <summary>
/// 开始记录日志
/// </summary>
public static int StartLogging(string filePath)
{
return TsMasterApi.tsapp_start_logging(Path.GetFullPath(filePath));
}
/// <summary>
/// 结束记录日志
/// </summary>
public static int StopLogging()
{
return TsMasterApi.tsapp_stop_logging();
}
/// <summary>
/// 获取错误提示
/// </summary>
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);
}
}
+248
View File
@@ -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
{
/// <summary>
/// FlexRay 网络(Cluster
/// </summary>
public struct flexray_network
{
[JsonInclude]
public string network_name;
[JsonInclude]
public flexray_node[] flexray_nodes;
}
/// <summary>
/// FlexRay 节点(ECU
/// </summary>
public struct flexray_node
{
[JsonInclude]
public string node_name;
[JsonInclude]
public flexray_message[] tx_flexray_Messages;
[JsonInclude]
public flexray_message[] rx_flexray_Messages;
}
/// <summary>
/// FlexRay 帧(Frame
/// </summary>
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;
}
/// <summary>
/// FlexRay 解码后的信号信息
/// </summary>
public struct FlexRaySignal
{
/// <summary>信号名称</summary>
public string name;
/// <summary>起始位</summary>
public int start_bit;
/// <summary>长度(bit</summary>
public int length;
/// <summary>因子</summary>
public double factor;
/// <summary>偏移</summary>
public double offset;
/// <summary>初始值</summary>
public double init_value;
/// <summary>信号类型</summary>
public TSignalType signal_type;
/// <summary>字节序(true=小端)</summary>
public bool is_intel;
}
/// <summary>
/// FlexRay 解码后的报文信息
/// </summary>
public class FlexRayDBMessage
{
/// <summary>时隙号</summary>
public int nSlotId;
/// <summary>周期号</summary>
public int nCycleNumber;
/// <summary>通道掩码</summary>
public int nChannelMask;
/// <summary>有效负载长度</summary>
public int nDataLength;
/// <summary>数据</summary>
public byte[] Data = Array.Empty<byte>();
}
public class FlexRayDBParse
{
private static FlexRayDBParse? _instance;
/// <summary>单例实例</summary>
public static FlexRayDBParse Instance => _instance ??= new FlexRayDBParse();
/// <summary>最大通道数(等于 MsgDatabase 的维度)</summary>
public int MaxChannels => MsgDatabase.Count;
/// <summary>
/// FlexRay 报文数据库(按通道索引,每个通道包含该通道上所有帧)。
/// </summary>
public static List<List<flexray_message>> MsgDatabase { get; set; } =
[.. Enumerable.Range(0, 12).Select(_ => new List<flexray_message>())];
/// <summary>FlexRay 网络拓扑(Cluster/ECU/Frame/Signal</summary>
public static flexray_network Network;
/// <summary>
/// 解析指定索引的 FlexRay 数据库,填充 MsgDatabase 和 Network。
/// FlexRay 数据库由工程加载,通过 tsdb_get_flexray_db_count 获取数量。
/// </summary>
/// <param name="dbIndex">数据库索引(0-based</param>
/// <param name="channel">映射的通道号</param>
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));
}
/// <summary>
/// 解析单帧属性(TX 或 RX
/// </summary>
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;
}
/// <summary>
/// 解析网络拓扑(仅填充 Network,不写入 MsgDatabase
/// </summary>
public static void rbs_parse(int dbIndex, int channel)
{
// parse 已同时填充 Network 和 MsgDatabase,此处保持与 CAN 一致的接口
parse(dbIndex, channel);
}
}
}
Binary file not shown.
Binary file not shown.
+19
View File
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Common\Common.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="Interop.TSMasterAPI">
<HintPath>Interop.TSMasterAPI.dll</HintPath>
</Reference>
</ItemGroup>
</Project>