Files
ACP/TSMasterFlexRay/FlexRay.cs
T
2026-08-05 11:53:51 +08:00

471 lines
16 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 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);
}
}