CAN驱动的重构V1

CAN报文的展示和处理
This commit is contained in:
2025-04-30 11:23:01 +08:00
parent 96597c95f2
commit 16eaa230d8
9 changed files with 760 additions and 110 deletions

View File

@@ -132,6 +132,8 @@ namespace CapMachine.Wpf
containerRegistry.RegisterSingleton<LogicRuleService>(); containerRegistry.RegisterSingleton<LogicRuleService>();
containerRegistry.RegisterSingleton<ConfigService>(); containerRegistry.RegisterSingleton<ConfigService>();
containerRegistry.RegisterSingleton<AlarmService>(); containerRegistry.RegisterSingleton<AlarmService>();
containerRegistry.RegisterSingleton<HightDriveMsgService>();
////注册设备服务 ////注册设备服务
//containerRegistry.RegisterSingleton<MachineDataService>(); //containerRegistry.RegisterSingleton<MachineDataService>();
containerRegistry.RegisterSingleton<CanDriveService>(); containerRegistry.RegisterSingleton<CanDriveService>();

View File

@@ -33,6 +33,10 @@ namespace CapMachine.Wpf.CanDrive
{ {
ContainerProvider = containerProvider; ContainerProvider = containerProvider;
HighSpeedDataService = ContainerProvider.Resolve<HighSpeedDataService>(); HighSpeedDataService = ContainerProvider.Resolve<HighSpeedDataService>();
//Stopwatch.Frequency表示高精度计时器每秒的计数次数ticks/秒每毫秒的ticks数 = 每秒的ticks数 ÷ 1000
TicksPerMs = Stopwatch.Frequency / 1000.0;
} }
/// <summary> /// <summary>
@@ -54,6 +58,11 @@ namespace CapMachine.Wpf.CanDrive
/// </summary> /// </summary>
public HighSpeedDataService HighSpeedDataService { get; set; } public HighSpeedDataService HighSpeedDataService { get; set; }
/// <summary>
/// 驱动消息数据处理
/// </summary>
public HightDriveMsgService HightDriveMsgService { get; set; }
/// <summary> /// <summary>
/// 开始Dbc文件写入 /// 开始Dbc文件写入
/// </summary> /// </summary>
@@ -288,7 +297,6 @@ namespace CapMachine.Wpf.CanDrive
{ {
Console.WriteLine("Get CAN Speed Success!"); Console.WriteLine("Get CAN Speed Success!");
} }
} }
/// <summary> /// <summary>
@@ -456,6 +464,24 @@ namespace CapMachine.Wpf.CanDrive
set { _IsCycleSend = value; RaisePropertyChanged(); } set { _IsCycleSend = value; RaisePropertyChanged(); }
} }
private bool _IsSendOk;
/// <summary>
/// 发送报文是否OK
/// </summary>
public bool IsSendOk
{
get { return _IsSendOk; }
set
{
if (_IsSendOk != value)
{
RaisePropertyChanged();
_IsSendOk = value;
}
}
}
/// <summary> /// <summary>
/// 循环发送数据 /// 循环发送数据
@@ -486,6 +512,7 @@ namespace CapMachine.Wpf.CanDrive
/// <summary> /// <summary>
/// 循环发送数据 /// 循环发送数据
/// 这个是通过延迟处理的,发送的周期时间可能不精准
/// </summary> /// </summary>
public void StartCycleSendMsg() public void StartCycleSendMsg()
{ {
@@ -543,6 +570,192 @@ namespace CapMachine.Wpf.CanDrive
} }
#region
// 添加取消标记源字段用于停止任务
private CancellationTokenSource CycleSendCts;
/// <summary>
/// 计算每毫秒对应的ticks数只需计算一次
/// </summary>
private double TicksPerMs;
// 类成员变量定义 精确记时用
private readonly Stopwatch Stopwatcher = new Stopwatch();
private long NextExecutionTime;
// 计算需要等待的时间
private long CurrentTime;
private long DelayTicks;
private int DelayMs;
private static readonly Random _random = new Random();
/// <summary>
/// 精确周期发送CAN数据
/// </summary>
public void StartPrecisionCycleSendMsg()
{
// 创建取消标记源 用于控制任务的取消 允许在需要时通过取消令牌来优雅停止任务
var cancellationTokenSource = new CancellationTokenSource();
var token = cancellationTokenSource.Token;
// 保存取消标记,以便在停止时使用
CycleSendCts = cancellationTokenSource;//将取消标记源保存到类的成员变量CycleSendCts这样在外部调用停止方法时可以访问它
NextExecutionTime = 0;//初始化NextExecutionTime为0这个变量用于记录下一次执行的目标时间点
CycleSendTask = Task.Factory.StartNew(async () =>
{
try
{
// 设置当前线程为高优先级
Thread.CurrentThread.Priority = ThreadPriority.AboveNormal;
// 初始化完成后开始计时
Stopwatcher.Restart();
// 预先计算固定值
long CycleInTicks = (long)(SendCycle * TicksPerMs);
//临时测试用
long lastTicks = Stopwatcher.ElapsedTicks;
//IsCycleSend
while (IsCycleSend && !token.IsCancellationRequested)
{
try
{
// 计算下一次执行时间点 将当前设置的发送周期SendCycle(毫秒)转换为Stopwatch的计时单位(tick)累加到NextExecutionTime上
NextExecutionTime += CycleInTicks; // 转换为Stopwatch计时单位
// 获取当前时间点以Stopwatch的tick为单位
CurrentTime = Stopwatcher.ElapsedTicks;
//计算需要等待的时间,即目标时间点(NextExecutionTime)与当前时间点(CurrentTime)的差值
DelayTicks = NextExecutionTime - CurrentTime;
// 如果还有等待时间,则等待,只有在目标时间点还未到达时才执行等待
if (DelayTicks > 0)
{
////此时是需要等待的,那么需要等待多久呢, 将需等待的tick数转换回毫秒
DelayMs = (int)(DelayTicks / TicksPerMs);
//20这个数据是预估和测试的可能跟Windows抖动误差就是20ms左右当然可以不用这个IF()判断直接SpinWait.SpinUntil(() => Stopwatcher.ElapsedTicks >= NextExecutionTime);但是会导致当前独占一个CPU核心线程
//所以设置一个20的阈值20ms以下的延迟使用SpinWait.SpinUntil进行自旋等待20ms以上的延迟使用Task.Delay进行异步等待让CPU不至于一直的独占
if (DelayMs <= 20)
{
SpinWait.SpinUntil(() => Stopwatcher.ElapsedTicks >= NextExecutionTime);
}
else
{
////使用Task.Delay进行异步等待大部分等待时间通过这种方式完成避免线程阻塞
await Task.Delay(DelayMs - 20, token);
//// 使用SpinWait.SpinUntil进行精确的微调等待。自旋等待会占用CPU资源但能提供更高的定时精度确保在精确的时间点执行
////上面的Task.Delay可能会因为系统调度等原因导致实际执行时间稍晚于预期因此在这里使用SpinWait.SpinUntil来确保在精确的时间点执行
SpinWait.SpinUntil(() => Stopwatcher.ElapsedTicks >= NextExecutionTime);
}
}
// 如果已经超过了计划时间,立即执行并重新校准
if (Stopwatcher.ElapsedTicks >= NextExecutionTime + CycleInTicks)
{
//检测是否发生了严重延迟(超过一个周期)。如果当前时间已经超过了下一次计划时间,则说明系统负载过高或其他原因导致无法按时执行,
//此时重置NextExecutionTime为当前时间避免连续的延迟累积
// 严重延迟,重新校准
NextExecutionTime = Stopwatcher.ElapsedTicks;
Console.WriteLine("定时发送延迟过大,重新校准时间");
}
// 使用Stopwatch记录实际的执行间隔而不是DateTime
Console.WriteLine($"--实际间隔(ms): {(Stopwatcher.ElapsedTicks - lastTicks) / TicksPerMs:F3}, 目标: {SendCycle}");
lastTicks = Stopwatcher.ElapsedTicks;
//Console.WriteLine($"--当前时间(毫秒): {DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}");
// 执行发送CAN逻辑
{
var GroupMsg = CmdData.GroupBy(x => x.MsgName);
USB2CAN.CAN_MSG[] CanMsg = new USB2CAN.CAN_MSG[GroupMsg.Count()];
for (int i = 0; i < GroupMsg.Count(); i++)
{
CanMsg[i] = new USB2CAN.CAN_MSG();
CanMsg[i].Data = new Byte[64];
}
IntPtr msgPtSend = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(USB2CAN.CAN_MSG)));
int Index = 0;
//循环给MSG赋值数据
foreach (var itemMsg in GroupMsg)
{
foreach (var itemSignal in itemMsg)
{
CAN_DBCParser.DBC_SetSignalValue(DBCHandle, new StringBuilder(itemMsg.Key), new StringBuilder(itemSignal.SignalName), itemSignal.SignalCmdValue);
}
CAN_DBCParser.DBC_SyncValueToCANMsg(DBCHandle, new StringBuilder(itemMsg.Key), msgPtSend);
CanMsg[Index] = (USB2CAN.CAN_MSG)Marshal.PtrToStructure(msgPtSend, typeof(USB2CAN.CAN_MSG));
Index++;
}
//通过DBC写入数据后生成CanMsg
//将信号值填入CAN消息里面
//释放申请的临时缓冲区
Marshal.FreeHGlobal(msgPtSend);
//发送CAN数据
int SendedNum = USB2CAN.CAN_SendMsg(DevHandle, WriteCANIndex, CanMsg, (uint)CanMsg.Length);
if (SendedNum >= 0)
{
//Console.WriteLine("Success send frames:{0}", SendedNum);
IsSendOk=true;
}
else
{
//Console.WriteLine("Send CAN data failed! {0}", SendedNum);
IsSendOk = false;
}
}
}
catch (TaskCanceledException)
{
// 任务被取消,正常退出
break;
}
catch (Exception ex)
{
Console.WriteLine($"CAN周期发送异常: {ex.Message}");
// 短暂暂停避免异常情况下CPU占用过高
await Task.Delay(10, token);
}
}
}
catch (Exception ex)
{
// 确保在任何情况下(正常退出、异常、取消)都会停止计时器
Stopwatcher.Stop();
// 清理其他可能的资源
Console.WriteLine("CAN周期发送任务已结束资源已清理");
}
finally
{
// 确保在任何情况下(正常退出、异常、取消)都会停止计时器
Stopwatcher.Stop();
}
}, token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
}
/// <summary>
/// 修改停止发送的方法
/// </summary>
public void StopCycleSendMsg()
{
IsCycleSend = false;
CycleSendCts?.Cancel();
}
#endregion
/// <summary> /// <summary>
/// 循环获取CAN消息 /// 循环获取CAN消息
/// </summary> /// </summary>
@@ -585,6 +798,15 @@ namespace CapMachine.Wpf.CanDrive
MsgData = BitConverter.ToString(CanMsgBuffer[i].Data), MsgData = BitConverter.ToString(CanMsgBuffer[i].Data),
Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")
}); });
//报文给高速报文处理服务
await HightDriveMsgService.AddMessageAsync(new Models.HighSpeed.CommMsg()
{
Category = "CAN",
MsgInfo = "0x" + CanMsgBuffer[i].ID.ToString("X8"),
MsgData = BitConverter.ToString(CanMsgBuffer[i].Data),
Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")
});
} }
} }
else if (CanNum == 0) else if (CanNum == 0)

View File

@@ -717,6 +717,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="LocalDll\" /> <Folder Include="LocalDll\" />
<Folder Include="Models\HightDriveMsg\" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Resource Include="Assets\Images\favicon.ico"> <Resource Include="Assets\Images\favicon.ico">

View File

@@ -95,7 +95,7 @@ namespace CapMachine.Wpf.Models
PauseTotalSecTime = 0; PauseTotalSecTime = 0;
//结束时间信息 //结束时间信息
EndTimeInfo = ProStartDt.AddSeconds(PauseTotalSecTime).ToString("yyyy-MM-dd HH:mm:ss"); EndTimeInfo = ProStartDt.AddSeconds(TotalProSec).ToString("yyyy-MM-dd HH:mm:ss");
} }
/// <summary> /// <summary>
@@ -120,7 +120,7 @@ namespace CapMachine.Wpf.Models
//统计的暂停的时间 //统计的暂停的时间
var PauseTime = (PauseEndDt - PauseStartDt).TotalSeconds; var PauseTime = (PauseEndDt - PauseStartDt).TotalSeconds;
PauseTotalSecTime = PauseTotalSecTime + (int)Math.Round(PauseTime); PauseTotalSecTime = PauseTotalSecTime + (int)Math.Round(PauseTime);
EndTimeInfo = ProStartDt.AddSeconds(PauseTotalSecTime).ToString("yyyy-MM-dd HH:mm:ss"); EndTimeInfo = ProStartDt.AddSeconds(TotalProSec+ PauseTotalSecTime).ToString("yyyy-MM-dd HH:mm:ss");
CycleTimer.Start(); CycleTimer.Start();

View File

@@ -231,7 +231,8 @@ namespace CapMachine.Wpf.Services
{ {
ToomossCanDrive.IsCycleSend = true; ToomossCanDrive.IsCycleSend = true;
ToomossCanDrive.CmdData = CmdData; ToomossCanDrive.CmdData = CmdData;
ToomossCanDrive.StartCycleSendMsg(); //ToomossCanDrive.StartCycleSendMsg();
ToomossCanDrive.StartPrecisionCycleSendMsg();
} }
else else
{ {

View File

@@ -29,7 +29,7 @@ namespace CapMachine.Wpf.Services
/// </summary> /// </summary>
public class HighSpeedDataService : BindableBase public class HighSpeedDataService : BindableBase
{ {
public HighSpeedDataService(ConfigService configService, ILogService logService, IFreeSql FreeSql,IContainerProvider containerProvider) public HighSpeedDataService(ConfigService configService, ILogService logService, IFreeSql FreeSql, IContainerProvider containerProvider)
{ {
ConfigService = configService; ConfigService = configService;
LogService = logService; LogService = logService;
@@ -112,13 +112,13 @@ namespace CapMachine.Wpf.Services
// new CommMsg() { Category = "CAN5",MsgInfo = "0x000003E3",Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"),MsgData = "33 00 00 00 37 00 04 00"}, // new CommMsg() { Category = "CAN5",MsgInfo = "0x000003E3",Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"),MsgData = "33 00 00 00 37 00 04 00"},
//}; //};
/// <summary> /// <summary>
/// 周期定时器 /// 周期定时器
/// </summary> /// </summary>
private System.Timers.Timer CycleTimer { get; set; } private System.Timers.Timer CycleTimer { get; set; }
private bool _IsEnable=true; private bool _IsEnable = true;
/// <summary> /// <summary>
/// 是否启用 /// 是否启用
/// </summary> /// </summary>
@@ -208,7 +208,7 @@ namespace CapMachine.Wpf.Services
} }
#endregion #endregion
#region CSV Helper CSV #region CSV Helper CSV

View File

@@ -0,0 +1,424 @@
using CapMachine.Wpf.CanDrive;
using CapMachine.Wpf.Models.HighSpeed;
using Prism.Ioc;
using Prism.Mvvm;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Channels;
using System.Threading.Tasks;
using System.Windows.Threading;
namespace CapMachine.Wpf.Services
{
/// <summary>
/// 高速驱动消息服务
/// 负载高速的驱动数据的消息和转发
/// </summary>
public class HightDriveMsgService : BindableBase
{
#region
private readonly ILogService LogService;
/// <summary>
/// 接收数据Channel
/// </summary>
private readonly Channel<CommMsg> ReceiveChannel;
/// <summary>
/// 转发数据Channel
/// </summary>
private readonly Channel<CommMsg> ForwardChannel;
/// <summary>
/// 取消令牌
/// </summary>
private CancellationTokenSource TaskCancellationTokenSource;
/// <summary>
/// 数据接受处理任务
/// </summary>
private Task ProcessTask;
/// <summary>
/// 数据转发处理任务
/// </summary>
private Task ForwardTask;
/// <summary>
/// 显示的消息集合
/// </summary>
public ObservableCollection<CommMsg> DisplayMessages { get; set; } = new ObservableCollection<CommMsg>();
/// <summary>
/// 最大显示消息数量
/// </summary>
public int MaxDisplayCount { get; set; } = 200;
/// <summary>
/// 是否启用CAN报文显示
/// </summary>
private bool _IsDisplayEnabled=true;
public bool IsDisplayEnabled
{
get { return _IsDisplayEnabled; }
set
{
SetProperty(ref _IsDisplayEnabled, value);
if (!value)
{
// 不显示时清空数据节省内存
App.Current.Dispatcher.Invoke(() => DisplayMessages.Clear());
}
}
}
/// <summary>
/// 是否启用CAN数据转发
/// </summary>
private bool _IsForwardEnabled;
public bool IsForwardEnabled
{
get { return _IsForwardEnabled; }
set { SetProperty(ref _IsForwardEnabled, value); }
}
#endregion
#region
/// <summary>
/// 构造函数
/// </summary>
/// <param name="containerProvider">容器服务提供者</param>
/// <param name="logService">日志服务</param>
public HightDriveMsgService(ILogService logService)
{
LogService = logService;
// 创建接收Channel设置容量和溢出策略
ReceiveChannel = Channel.CreateBounded<CommMsg>(new BoundedChannelOptions(100000)
{
FullMode = BoundedChannelFullMode.DropOldest, // 满时删除旧数据
SingleReader = true, // 单一读取者
SingleWriter = false // 允许多个写入者
});
// 创建转发Channel
ForwardChannel = Channel.CreateBounded<CommMsg>(new BoundedChannelOptions(100000)
{
FullMode = BoundedChannelFullMode.DropOldest,
SingleReader = true,
SingleWriter = false
});
// 初始化服务
Initialize();
}
/// <summary>
/// 初始化服务
/// </summary>
private void Initialize()
{
TaskCancellationTokenSource = new CancellationTokenSource();
// 启动数据处理任务
ProcessTask = Task.Run(ProcessMessagesAsync);
// 启动数据转发任务
ForwardTask = Task.Run(ForwardMessagesAsync);
//LogService.Info("HightDriveMsgService初始化完成");
}
#endregion
#region
/// <summary>
/// 添加CAN消息进行处理
/// </summary>
/// <param name="message">CAN消息</param>
/// <returns>是否添加成功</returns>
public async Task<bool> AddMessageAsync(CommMsg message)
{
if (message == null) return false;
try
{
if (IsDisplayEnabled)
{
// 添加到接收Channel
await ReceiveChannel.Writer.WriteAsync(message, TaskCancellationTokenSource.Token);
}
// 如果启用转发也添加到转发Channel 需要判断目标的CAN是否已经打开如果打开的话则转发数据
if (IsForwardEnabled)
{
await ForwardChannel.Writer.WriteAsync(message, TaskCancellationTokenSource.Token);
}
return true;
}
catch (OperationCanceledException)
{
return false;
}
catch (Exception ex)
{
LogService.Error($"添加CAN消息失败: {ex.Message}");
return false;
}
}
/// <summary>
/// 批量添加CAN消息
/// </summary>
/// <param name="messages">消息集合</param>
/// <returns>成功添加的消息数量</returns>
public async Task<int> AddMessagesAsync(IEnumerable<CommMsg> messages)
{
if (messages == null) return 0;
int successCount = 0;
foreach (var message in messages)
{
if (await AddMessageAsync(message))
{
successCount++;
}
}
return successCount;
}
/// <summary>
/// 设置转发目标CAN设备
/// </summary>
/// <param name="targetDevice">目标设备</param>
public void SetForwardTarget(ToomossCan targetDevice)
{
//_targetCanDevice = targetDevice;
LogService.Info($"设置转发目标设备: {(targetDevice != null ? "" : "")}");
}
/// <summary>
/// 清空显示消息
/// </summary>
public void ClearDisplayMessages()
{
App.Current.Dispatcher.Invoke(() => DisplayMessages.Clear());
}
#endregion
#region
/// <summary>
/// 批量处理消息提高效率
/// </summary>
private List<CommMsg> MessageBatch { get; set; } = new List<CommMsg>();
/// <summary>
/// 接受的批次数量
/// </summary>
private int ReciveBatchCount { get; set; }
/// <summary>
/// 转发的批次数量
/// </summary>
private int ForwardBatchCount { get; set; }
/// <summary>
/// 异步处理接收到的消息
/// </summary>
private async Task ProcessMessagesAsync()
{
try
{
while (await ReceiveChannel.Reader.WaitToReadAsync(TaskCancellationTokenSource.Token))
{
// 清空列表但保留容量
MessageBatch.Clear();
// 读取最多100条消息或直到没有更多消息
ReciveBatchCount = 0;
while (ReciveBatchCount < 30 && ReceiveChannel.Reader.TryRead(out CommMsg message))
{
MessageBatch.Add(message);
ReciveBatchCount++;
}
if (MessageBatch.Count > 0 && IsDisplayEnabled)
{
// 在UI线程更新显示
await App.Current.Dispatcher.InvokeAsync(() =>
{
foreach (var msg in MessageBatch)
{
DisplayMessages.Add(msg);
// 控制显示消息数量
while (DisplayMessages.Count > MaxDisplayCount)
{
DisplayMessages.RemoveAt(0);
}
}
}, DispatcherPriority.Background);
}
// 避免CPU过度使用
if (ReciveBatchCount == 0)
{
await Task.Delay(20);
}
}
}
catch (OperationCanceledException ex)
{
// 服务被取消,正常退出
LogService.Error($" 服务被取消,正常退出: {ex.Message}");
}
catch (Exception ex)
{
LogService.Error($"消息处理任务异常: {ex.Message}");
}
}
/// <summary>
/// 异步转发消息
/// </summary>
private async Task ForwardMessagesAsync()
{
try
{
while (await ForwardChannel.Reader.WaitToReadAsync(TaskCancellationTokenSource.Token))
{
//// 只有在启用转发且目标设备可用时才处理 && _targetCanDevice != null && _targetCanDevice.OpenState
//if (IsForwardEnabled)
//{
// // 批量收集转发消息
// List<USB2CAN.CAN_MSG> canMessages = new List<USB2CAN.CAN_MSG>();
// ForwardBatchCount = 0;
// // 一次最多处理50条消息
// while (ForwardBatchCount < 50 && ForwardChannel.Reader.TryRead(out CommMsg message))
// {
// if (message.Category == "CAN" && !string.IsNullOrEmpty(message.MsgInfo))
// {
// try
// {
// // 转换为CAN_MSG格式
// USB2CAN.CAN_MSG canMsg = new USB2CAN.CAN_MSG();
// canMsg.Data = new byte[64];
// // 解析CAN ID
// string msgIdHex = message.MsgInfo.Replace("0x", "");
// canMsg.ID = Convert.ToUInt32(msgIdHex, 16);
// // 解析数据
// string[] dataBytes = message.MsgData.Split('-');
// canMsg.DataLen = (byte)dataBytes.Length;
// for (int i = 0; i < dataBytes.Length && i < 64; i++)
// {
// canMsg.Data[i] = Convert.ToByte(dataBytes[i], 16);
// }
// canMessages.Add(canMsg);
// }
// catch (Exception ex)
// {
// LogService.Error($"转换CAN消息格式失败: {ex.Message}");
// }
// }
// ForwardBatchCount++;
// }
// // 批量发送转发消息
// if (canMessages.Count > 0)
// {
// //// 在这里调用设备API发送消息
// //int sendResult = USB2CAN.CAN_SendMsg(
// // _targetCanDevice.DevHandle,
// // _targetCanDevice.WriteCANIndex,
// // canMessages.ToArray(),
// // (uint)canMessages.Count);
// //if (sendResult < 0)
// //{
// // LogService.Error($"CAN消息转发失败: 错误码={sendResult}");
// //}
// //else
// //{
// // LogService.Debug($"成功转发{sendResult}条CAN消息");
// //}
// }
//}
//else
//{
// // 不转发时,清空队列减少内存占用
// while (ForwardChannel.Reader.TryRead(out _)) { }
// await Task.Delay(10);
//}
}
}
catch (OperationCanceledException)
{
// 服务被取消,正常退出
}
catch (Exception ex)
{
LogService.Error($"消息转发任务异常: {ex.Message}");
}
}
#endregion
#region IDisposable
/// <summary>
/// 释放资源
/// </summary>
public void Dispose()
{
try
{
// 发送取消信号
TaskCancellationTokenSource?.Cancel();
// 等待任务完成
Task.WaitAll(new[] { ProcessTask, ForwardTask }, 1000);
}
catch { /* 忽略取消异常 */ }
finally
{
// 释放资源
TaskCancellationTokenSource?.Dispose();
TaskCancellationTokenSource = null;
// 完成写入器,表示不再接受新数据
ReceiveChannel.Writer.Complete();
ForwardChannel.Writer.Complete();
// 清空显示数据
App.Current.Dispatcher.Invoke(() => DisplayMessages.Clear());
LogService.Info("HightDriveMsgService资源已释放");
}
}
#endregion
}
}

View File

@@ -38,7 +38,7 @@ namespace CapMachine.Wpf.ViewModels
public CANConfigViewModel(IDialogService dialogService, IFreeSql freeSql, public CANConfigViewModel(IDialogService dialogService, IFreeSql freeSql,
IEventAggregator eventAggregator, IRegionManager regionManager, SysRunService sysRunService, IEventAggregator eventAggregator, IRegionManager regionManager, SysRunService sysRunService,
ComActionService actionService, LogicRuleService logicRuleService, ComActionService actionService, LogicRuleService logicRuleService,
ConfigService configService, CanDriveService canDriveService, ConfigService configService, CanDriveService canDriveService,HightDriveMsgService hightDriveMsgService,
IMapper mapper, MachineRtDataService machineRtDataService) IMapper mapper, MachineRtDataService machineRtDataService)
{ {
//LogService = logService; //LogService = logService;
@@ -50,6 +50,7 @@ namespace CapMachine.Wpf.ViewModels
LogicRuleService = logicRuleService; LogicRuleService = logicRuleService;
ConfigService = configService; ConfigService = configService;
CanDriveService = canDriveService; CanDriveService = canDriveService;
HightDriveMsgService = hightDriveMsgService;
Mapper = mapper; Mapper = mapper;
this.MachineRtDataService = machineRtDataService; this.MachineRtDataService = machineRtDataService;
@@ -96,6 +97,7 @@ namespace CapMachine.Wpf.ViewModels
public LogicRuleService LogicRuleService { get; } public LogicRuleService LogicRuleService { get; }
public ConfigService ConfigService { get; } public ConfigService ConfigService { get; }
public CanDriveService CanDriveService { get; } public CanDriveService CanDriveService { get; }
public HightDriveMsgService HightDriveMsgService { get; }
public IMapper Mapper { get; } public IMapper Mapper { get; }
private MachineRtDataService MachineRtDataService { get; } private MachineRtDataService MachineRtDataService { get; }

View File

@@ -910,114 +910,112 @@
</Grid> </Grid>
<Grid Grid.Column="2" Margin="3"> <Grid Grid.Column="2" Margin="3">
<!--<Grid.RowDefinitions> <TabControl>
<RowDefinition Height="60" /> <TabItem Header="DBC数据表">
<RowDefinition /> <DataGrid
</Grid.RowDefinitions>--> x:Name="DbcDatagrid"
Margin="0,2,0,0"
AutoGenerateColumns="False"
BorderBrush="Black"
BorderThickness="1"
CanUserAddRows="False"
IsReadOnly="True"
ItemsSource="{Binding ListCanDbcModel}"
SelectionMode="Extended"
SelectionUnit="FullRow">
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem
Command="{Binding Source={StaticResource Proxy}, Path=Data.DataGridMenuCmd}"
CommandParameter="Write"
Header="添加到写入操作" />
<MenuItem
Command="{Binding Source={StaticResource Proxy}, Path=Data.DataGridMenuCmd}"
CommandParameter="Read"
Header="添加到读取操作" />
</ContextMenu>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding IsSeletedInfo}" Value="1">
<Setter Property="Background" Value="LightGreen" />
</DataTrigger>
<DataTrigger Binding="{Binding IsSeletedInfo}" Value="2">
<Setter Property="Background" Value="SkyBlue" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
<!--<materialDesign:Card <DataGrid.Columns>
Margin="0,1,0,3" <DataGridTextColumn Binding="{Binding MsgId}">
Background="{DynamicResource MaterialDesignLightBackground}" <DataGridTextColumn.Header>
Foreground="{DynamicResource PrimaryHueLightForegroundBrush}" <TextBlock FontWeight="Bold" Text="消息ID" />
UniformCornerRadius="5"> </DataGridTextColumn.Header>
<StackPanel Margin="5" Orientation="Horizontal"> </DataGridTextColumn>
<Button
Margin="5,0"
Content="连接CAN"
Foreground="White" />
<Button
Margin="5,0"
Content="关闭CAN"
Foreground="White" />
<Button
Margin="5,0"
Content="保存配置"
Foreground="White" />
</StackPanel>
</materialDesign:Card>-->
<DataGrid <DataGridTextColumn Binding="{Binding MsgName}">
x:Name="DbcDatagrid" <DataGridTextColumn.Header>
AutoGenerateColumns="False" <TextBlock FontWeight="Bold" Text="消息名称" />
BorderBrush="Black" </DataGridTextColumn.Header>
BorderThickness="1" </DataGridTextColumn>
CanUserAddRows="False"
IsReadOnly="True"
ItemsSource="{Binding ListCanDbcModel}"
SelectionMode="Extended"
SelectionUnit="FullRow">
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem
Command="{Binding Source={StaticResource Proxy}, Path=Data.DataGridMenuCmd}"
CommandParameter="Write"
Header="添加到写入操作" />
<MenuItem
Command="{Binding Source={StaticResource Proxy}, Path=Data.DataGridMenuCmd}"
CommandParameter="Read"
Header="添加到读取操作" />
</ContextMenu>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding IsSeletedInfo}" Value="1">
<Setter Property="Background" Value="LightGreen" />
</DataTrigger>
<DataTrigger Binding="{Binding IsSeletedInfo}" Value="2">
<Setter Property="Background" Value="SkyBlue" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
<DataGrid.Columns> <DataGridTextColumn Binding="{Binding SignalName}">
<DataGridTextColumn Binding="{Binding MsgId}"> <DataGridTextColumn.Header>
<DataGridTextColumn.Header> <TextBlock FontWeight="Bold" Text="信号名称" />
<TextBlock FontWeight="Bold" Text="消息ID" /> </DataGridTextColumn.Header>
</DataGridTextColumn.Header> </DataGridTextColumn>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding MsgName}"> <!--<DataGridTextColumn Binding="{Binding SignalDesc}">
<DataGridTextColumn.Header> <DataGridTextColumn.Header>
<TextBlock FontWeight="Bold" Text="消息名称" /> <TextBlock FontWeight="Bold" Text="信号描述" />
</DataGridTextColumn.Header> </DataGridTextColumn.Header>
</DataGridTextColumn> </DataGridTextColumn>-->
<DataGridTextColumn Binding="{Binding SignalName}"> <!--<DataGridTextColumn Binding="{Binding SignalUnit}">
<DataGridTextColumn.Header> <DataGridTextColumn.Header>
<TextBlock FontWeight="Bold" Text="信号名称" /> <TextBlock FontWeight="Bold" Text="信号单位" />
</DataGridTextColumn.Header> </DataGridTextColumn.Header>
</DataGridTextColumn> </DataGridTextColumn>-->
<!--<DataGridTextColumn Binding="{Binding SignalDesc}"> <DataGridTextColumn Binding="{Binding SignalRtValue}">
<DataGridTextColumn.Header> <DataGridTextColumn.Header>
<TextBlock FontWeight="Bold" Text="信号描述" /> <TextBlock FontWeight="Bold" Text="实时值" />
</DataGridTextColumn.Header> </DataGridTextColumn.Header>
</DataGridTextColumn>--> </DataGridTextColumn>
<!--<DataGridTextColumn Binding="{Binding SignalUnit}"> </DataGrid.Columns>
<DataGridTextColumn.Header> <i:Interaction.Triggers>
<TextBlock FontWeight="Bold" Text="信号单位" /> <i:EventTrigger EventName="SelectionChanged">
</DataGridTextColumn.Header> <prism:InvokeCommandAction Command="{Binding DbcGridSelectionChangedCmd}" CommandParameter="{Binding ElementName=DbcDatagrid, Path=SelectedItem}" />
</DataGridTextColumn>--> </i:EventTrigger>
</i:Interaction.Triggers>
<DataGridTextColumn Binding="{Binding SignalRtValue}"> </DataGrid>
<DataGridTextColumn.Header> </TabItem>
<TextBlock FontWeight="Bold" Text="实时值" /> <TabItem Header="CAN报文">
</DataGridTextColumn.Header> <ListView
</DataGridTextColumn> ItemsSource="{Binding HightDriveMsgService.DisplayMessages}"
ScrollViewer.IsDeferredScrollingEnabled="True"
VirtualizingPanel.CacheLength="1,1"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.VirtualizationMode="Recycling">
<ListView.View>
<GridView>
<GridViewColumn DisplayMemberBinding="{Binding MsgInfo}" Header="报文ID" />
<GridViewColumn DisplayMemberBinding="{Binding Category}" Header="报文名称" />
<GridViewColumn DisplayMemberBinding="{Binding MsgData}" Header="报文数据" />
<GridViewColumn DisplayMemberBinding="{Binding Time}" Header="报文时间" />
</GridView>
</ListView.View>
</ListView>
</TabItem>
</TabControl>
</DataGrid.Columns>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<prism:InvokeCommandAction Command="{Binding DbcGridSelectionChangedCmd}" CommandParameter="{Binding ElementName=DbcDatagrid, Path=SelectedItem}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</DataGrid>
</Grid> </Grid>