383 lines
12 KiB
C#
383 lines
12 KiB
C#
|
||
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;
|
||
|
||
/// <summary>
|
||
/// 实时接收到的 LIN 报文缓存(按帧 ID 索引,保留最新帧)。
|
||
/// </summary>
|
||
public static ConcurrentDictionary<int, TLIBLIN> RealTimeMessages { get; } = new();
|
||
|
||
#endregion
|
||
|
||
#region 实例字段与属性
|
||
|
||
/// <summary>设备类型号</summary>
|
||
public uint DeviceType { get; }
|
||
/// <summary>设备索引</summary>
|
||
public uint DeviceIndex { get; }
|
||
/// <summary>最大通道数</summary>
|
||
public int MaxChannels { get; }
|
||
/// <summary>波特率(LIN 常用 19.2 kbps)</summary>
|
||
public float BaudRate { get; }
|
||
/// <summary>LIN 协议版本</summary>
|
||
public LIN_PROTOCOL Protocol { get; }
|
||
|
||
/// <summary>
|
||
/// LIN 帧解码事件:当接收到帧后触发。
|
||
/// 参数:(通道号, 帧信息)
|
||
/// </summary>
|
||
public event Action<uint, TLIBLIN>? OnLinFrameReceived;
|
||
|
||
private bool _disposed;
|
||
private bool _isOpened;
|
||
private TLINQueueEvent_Win32? _instanceListener;
|
||
|
||
#endregion
|
||
|
||
#region 构造函数
|
||
|
||
/// <summary>默认构造函数(1 通道、19.2 kbps、LIN 2.1)</summary>
|
||
public LIN() : this(43, 0, 1, 19.2f, LIN_PROTOCOL.LIN_PROTOCOL_21) { }
|
||
|
||
/// <summary>
|
||
/// 带配置参数的构造函数。
|
||
/// </summary>
|
||
/// <param name="deviceType">设备类型号</param>
|
||
/// <param name="deviceIndex">设备索引</param>
|
||
/// <param name="maxChannels">最大通道数</param>
|
||
/// <param name="baudRate">波特率(常用 19.2)</param>
|
||
/// <param name="protocol">LIN 协议版本</param>
|
||
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 实例方法 — 设备管理
|
||
|
||
/// <summary>
|
||
/// 打开设备:初始化 TSMaster 库 → 设置通道数 → 配置波特率与协议 → 连接 → 注册监听。
|
||
/// </summary>
|
||
/// <returns>true 表示成功</returns>
|
||
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;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 关闭 LIN 设备:注销监听 → 断开连接。
|
||
/// </summary>
|
||
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;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// LIN 接收回调:缓存最新帧 → 触发 OnLinFrameReceived 事件。
|
||
/// </summary>
|
||
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
|
||
|
||
/// <summary>
|
||
/// 初始化 TSMasterLIN
|
||
/// </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();
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 连接
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 断开
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置节点角色(LIN 特有:主节点/从节点)
|
||
/// LIN 是单主多从总线,只有 Master 能发起帧头调度。
|
||
/// </summary>
|
||
/// <param name="channel">通道号(0-based)</param>
|
||
/// <param name="nodeType">节点类型:T_MasterNode / T_SlaveNode</param>
|
||
/// <returns></returns>
|
||
public static int SetNodeType(byte channel, TLINNodeType nodeType)
|
||
{
|
||
return TsMasterApi.tslin_set_node_functiontype(channel, nodeType);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置当前通道为主节点
|
||
/// </summary>
|
||
public static int SetMaster(byte channel)
|
||
{
|
||
return TsMasterApi.tslin_set_node_functiontype(channel, TLINNodeType.T_MasterNode);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置当前通道为从节点
|
||
/// </summary>
|
||
public static int SetSlave(byte channel)
|
||
{
|
||
return TsMasterApi.tslin_set_node_functiontype(channel, TLINNodeType.T_SlaveNode);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 弹出通道映射窗口
|
||
/// </summary>
|
||
[Browsable(false)]
|
||
public static int ShowChannelMappingWindow(bool isWait = false)
|
||
{
|
||
return TsMasterApi.tsapp_show_tsmaster_window("Hardware", isWait);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 发送一帧 LIN(异步)。
|
||
/// LIN 发送语义:Master 发帧头(Header)后由指定节点应答数据。
|
||
/// </summary>
|
||
/// <param name="msg"></param>
|
||
/// <returns></returns>
|
||
[Browsable(false)]
|
||
public static int SendMsg(TLIBLIN msg)
|
||
{
|
||
return TsMasterApi.tsapp_transmit_lin_async(ref msg);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 发送一帧 LIN(按参数构造)。
|
||
/// </summary>
|
||
/// <param name="AIdxChn">通道索引(0-based)</param>
|
||
/// <param name="AID">帧 ID(6 位有效)</param>
|
||
/// <param name="ADLC">数据长度(1~8 字节)</param>
|
||
/// <param name="AIsTx">true=Master 同时发送数据(Master 发送帧+数据);false=仅发帧头,等待从节点应答</param>
|
||
/// <returns></returns>
|
||
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);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 发送一帧 LIN(携带数据)。
|
||
/// </summary>
|
||
/// <param name="AIdxChn">通道索引</param>
|
||
/// <param name="AID">帧 ID</param>
|
||
/// <param name="AData">数据数组(最多 8 字节)</param>
|
||
/// <param name="AIsTx">true=Master 发送数据;false=仅发帧头</param>
|
||
/// <returns></returns>
|
||
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);
|
||
}
|
||
|
||
/// <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}";
|
||
}
|
||
}
|
||
}
|