4 Commits

Author SHA1 Message Date
5088ec4a0e 6个物性公式的更改 2026-04-13 10:09:30 +08:00
4ec6acde8f 更改 2026-04-09 21:56:31 +08:00
532374d1c4 CAN/LIN 导入和导出功能 2026-04-09 21:56:22 +08:00
48514b3162 实现CAN/CANFD/LIN配置导入导出功能 - 从嘉兴分支ea58e033移植 2026-04-03 22:43:10 +08:00
60 changed files with 22203 additions and 154 deletions

36
.windsurf/rules/zlgcan.md Normal file
View File

@@ -0,0 +1,36 @@
---
trigger: manual
---
### 目标根据周立功的CAN卡提供的官方资料进行封装成C#类库供使用主要是CANCAN FDLIN三个通信其中CAN和CAN FD使用DBC配置LIN使用LDF配置
### 周立功CAN卡型号USBCANFD 200U
### 官网网络资源地址
函数库简介https://manual.zlg.cn/web/#/152/5333
开发流程https://manual.zlg.cn/web/#/152/5334
数据结构定义https://manual.zlg.cn/web/#/152/5335
函数说明https://manual.zlg.cn/web/#/152/5336
设备类型号https://manual.zlg.cn/web/#/152/6361
### 官方ZLGAPI
CapMachine.Wpf\CanDrive\ZlgCan\ZLGAPI.cs
### 官方接口函数使用方法:
CapMachine.Wpf\CanDrive\ZlgCan\接口函数使用手册.pdf
### 要求:
相关的封装内容放到CapMachine.Wpf\CanDrive\ZlgCan下封装要体现工程化能力需要考虑性能后期长时间运行
功能要全面我之前用的图莫斯的CAN卡是另外一套函数在CapMachine.Wpf\CanDrive 和CapMachine.Wpf\LinDrive 中封装实现,你可以查看
官方的样例在\CapMachine\Sample\C#_USBCANFD_251215,可借鉴使用
可以联网查询信息
需要详细的注释分析过程
当前提供的CAN和LIN的基础能力我最终需要的是使用DBCCAN/CAN FD和LDFLIN配置文件进行读取数据和控制发送

View File

@@ -49,5 +49,11 @@ namespace CapMachine.Model.CANLIN
/// </summary>
[Column(Name = "DbcPath", IsNullable = false, StringLength = 500)]
public string? DbcPath { get; set; }
/// <summary>
/// 调度表是否启用
/// </summary>
[Column(Name = "SchEnable")]
public bool SchEnable { get; set; }
}
}

View File

@@ -0,0 +1,60 @@
using FreeSql.DataAnnotations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CapMachine.Model.CANLIN
{
/// <summary>
/// 调度表的配置
/// 其实这些调度表是在DBC中有的但是图莫斯的驱动没有读取到这些信息
/// 那么我们在系统层面进行操作和保存这些信息
/// </summary>
[Table(Name = "CANFdScheduleConfig")]
public class CANFdScheduleConfig
{
/// <summary>
/// 主键
/// </summary>
[Column(IsPrimary = true, IsIdentity = true)]
public long Id { get; set; }
/// <summary>
/// 消息名称
/// </summary>
[Column(Name = "MsgName")]
public string? MsgName { get; set; }
/// <summary>
/// 消息的周期
/// </summary>
[Column(Name = "Cycle")]
public int Cycle { get; set; }
/// <summary>
/// 发送方式
/// </summary>
[Column(Name = "OrderSend")]
public int OrderSend { get; set; }
/// <summary>
/// 调度表的Index
/// //约定每帧对应一个调度表预设5个调度表每个调度表对应一个帧
/// 0-4这个范围的设置Index
/// </summary>
[Column(Name = "SchTabIndex")]
public int SchTabIndex { get; set; }
/// <summary>
/// ///////////////////////////////////////////导航属性///////////////////////////////////////////////////////
/// </summary>
public long CanLinConfigProId { get; set; }
public CanLinConfigPro? CanLinConfigPro { get; set; }
}
}

View File

@@ -50,6 +50,12 @@ namespace CapMachine.Model.CANLIN
///CAN 的调度表配置模式
public List<CANScheduleConfig>? CanScheduleConfigs { get; set; }
/// <summary>
/// ///////////////////////////////////////////导航属性///////////////////////////////////////////////////////
/// </summary>
///CAN 的调度表配置模式
public List<CANFdScheduleConfig>? CanFdScheduleConfigs { get; set; }
/// <summary>
/// ///////////////////////////////////////////导航属性///////////////////////////////////////////////////////
/// </summary>

View File

@@ -57,6 +57,12 @@ namespace CapMachine.Model
[Column(Name = "CapNo", IsNullable = true, StringLength = 150)]
public string? CapNo { get; set; }
/// <summary>
/// 压缩机排量
/// </summary>
[Column(Name = "CapDisplacement", IsNullable = true, StringLength = 100)]
public string? CapDisplacement { get; set; }
/// <summary>
/// 制冷剂
/// </summary>

View File

@@ -7,6 +7,7 @@
<add key="connecting5" value="Data Source=@Address@; Attachs=ems.db; Pooling=true;Min Pool Size=1"/>
<add key="PLCScan" value="600"/>
<add key="PLCIP" value="127.0.0.1"/>
<add key="CompressorDisplacementCc" value="35CC"/>
<add key="FluidsPath" value="C:\Program Files (x86)\REFPROP\fluids"/>
<add key="Cryogen" value="R134a"/>
<add key="LocalDBPath" value="D:\MSDB\LocalDb\CapMachineDb"/>

View File

@@ -133,14 +133,19 @@ namespace CapMachine.Wpf
containerRegistry.RegisterSingleton<CanFdDriveService>();
containerRegistry.RegisterSingleton<LinDriveService>();
containerRegistry.RegisterSingleton<ZlgCanDriveService>();
containerRegistry.RegisterSingleton<ZlgLinDriveService>();
containerRegistry.RegisterSingleton<MachineRtDataService>();
containerRegistry.RegisterSingleton<DataRecordService>();
containerRegistry.RegisterSingleton<HighSpeedDataService>();
//containerRegistry.RegisterSingleton<PPCService>();
containerRegistry.RegisterSingleton<PPCService>();
containerRegistry.RegisterSingleton<SysRunService>();
containerRegistry.RegisterSingleton<ComActionService>();
containerRegistry.RegisterSingleton<CanLinConfigImExportService>();
containerRegistry.RegisterSingleton<ProRuntimeService>();
string strsqllite = System.AppDomain.CurrentDomain.BaseDirectory + @"Db\CapMachine.db";
@@ -185,6 +190,8 @@ namespace CapMachine.Wpf
containerRegistry.RegisterForNavigation<CANConfigView, CANConfigViewModel>();
containerRegistry.RegisterForNavigation<CANFDConfigView, CANFDConfigViewModel>();
containerRegistry.RegisterForNavigation<LINConfigView, LinConfigViewModel>();
containerRegistry.RegisterForNavigation<ZlgCanDriveConfigView, ZlgCanDriveConfigViewModel>();
containerRegistry.RegisterForNavigation<ZlgLinDriveConfigView, ZlgLinDriveConfigViewModel>();
//注册Dialog视图时绑定VM
@@ -198,12 +205,17 @@ namespace CapMachine.Wpf
containerRegistry.RegisterDialog<DialogExpInfoView, DialogExpInfoViewModel>();
containerRegistry.RegisterDialog<DialogUserView, DialogUserViewModel>();
containerRegistry.RegisterDialog<DialogCanRwConfigView, DialogCanRwConfigViewModel>();
containerRegistry.RegisterDialog<DialogZlgCanLinRwConfigView, DialogZlgCanLinRwConfigViewModel>();
containerRegistry.RegisterDialog<DialogCanLinConfigCreateView, DialogCanLinConfigCreateViewModel>();
containerRegistry.RegisterDialog<DialogPIDConfigView, DialogPIDConfigViewModel>();
containerRegistry.RegisterDialog<DialogLimitConfigView, DialogLimitConfigViewModel>();
containerRegistry.RegisterDialog<DialogSuperHeatCoolConfigView, DialogSuperHeatCoolConfigViewModel>();
containerRegistry.RegisterDialog<DialogLogicRuleView, DialogLogicRuleViewModel>();
containerRegistry.RegisterDialog<DialogCanLinConfigImExportView, DialogCanLinConfigImExportViewModel>();
containerRegistry.RegisterDialog<DialogCANFdSchConfigView, DialogCANFdSchConfigViewModel>();
containerRegistry.RegisterDialog<DialogCANSchConfigView, DialogCANSchConfigViewModel>();
containerRegistry.RegisterDialog<DialogZlgCANSchConfigView, DialogZlgCANSchConfigViewModel>();
containerRegistry.RegisterDialog<DialogZlgCANFDSchConfigView, DialogZlgCANFDSchConfigViewModel>();
containerRegistry.RegisterDialog<DialogLINSchConfigView, DialogLINSchConfigViewModel>();
containerRegistry.RegisterDialog<SplashScreenView, SplashScreenViewModel>();

View File

@@ -1,6 +1,8 @@
using CapMachine.Wpf.CanDrive.CanFD;
using CapMachine.Wpf.Dtos;
using CapMachine.Wpf.Models.Tag;
using CapMachine.Wpf.Services;
using ImTools;
using NPOI.OpenXmlFormats.Wordprocessing;
using Prism.Ioc;
using Prism.Mvvm;
@@ -20,20 +22,41 @@ using System.Windows.Interop;
namespace CapMachine.Wpf.CanDrive
{
/// <summary>
/// Toomoss CANFD
/// 图莫斯(ToomossCAN FD 驱动封装。
/// </summary>
/// <remarks>
/// 职责边界:
/// - 负责图莫斯 USB2XXXCANFDSDK 的设备扫描/打开/初始化/关闭;
/// - 负责 DBC 文件解析,并提供“信号值 <-> CANFD 帧”的互转(通过 SDK 的 DBCParserByFD 接口);
/// - 提供三类发送能力:
/// - 单次发送(<see cref="SendCanMsg"/>
/// - 软件侧精确周期发送(<see cref="StartPrecisionCycleSendMsg"/>,以及并行版本 <see cref="StartParallelPrecisionCycleSendMsg"/>
/// - 硬件侧调度表发送(<see cref="StartSchedule"/> / <see cref="StopSchedule"/>,以及 <see cref="UpdateSchDataByCmdDataChanged"/> 增量更新);
/// - 提供接收能力:后台轮询读取报文并同步到 DBC 信号实时值(<see cref="StartCycleReviceCanMsg"/>)。
///
/// 线程与资源:
/// - 发送/接收/调度表更新均可能在后台线程运行;
/// - 多处使用 <see cref="Marshal.AllocHGlobal"/> 分配非托管内存,必须在 finally 中释放,避免长期运行内存上涨。
///
/// 重要约束:
/// - <see cref="DBCHandle"/> 为 0 表示 DBC 未解析成功,任何 DBC_* 调用都应视为不可用;
/// - <see cref="WriteCANIndex"/> / <see cref="ReadCANIndex"/> 表示 CANFD 通道索引0=CAN1,1=CAN2
/// </remarks>
public class ToomossCanFD : BindableBase
{
private readonly IContainerProvider ContainerProvider;
/// <summary>
/// 实例化函数
/// 构造函数
/// </summary>
public ToomossCanFD(IContainerProvider containerProvider)
/// <param name="containerProvider">DI 容器。</param>
/// <param name="logService">日志服务。</param>
public ToomossCanFD(IContainerProvider containerProvider, ILogService logService)
{
ContainerProvider = containerProvider;
LoggerService = logService;
HighSpeedDataService = ContainerProvider.Resolve<HighSpeedDataService>();
LogService = ContainerProvider.Resolve<ILogService>();
//LogService = ContainerProvider.Resolve<ILogService>();
//Stopwatch.Frequency表示高精度计时器每秒的计数次数ticks/秒每毫秒的ticks数 = 每秒的ticks数 ÷ 1000
TicksPerMs = Stopwatch.Frequency / 1000.0;
}
@@ -43,11 +66,22 @@ namespace CapMachine.Wpf.CanDrive
/// </summary>
public HighSpeedDataService HighSpeedDataService { get; set; }
public ILogService LogService { get; set; }
public ILogService LoggerService { get; set; }
/// <summary>
/// 开始CAN的驱动
/// 启动 CANFD 驱动(设备准备流程)。
/// </summary>
/// <remarks>
/// 执行顺序与 SDK 依赖一致:
/// 1) 校验 DLL 是否存在;
/// 2) 扫描设备并选取句柄;
/// 3) 打开设备;
/// 4) 读取设备信息;
/// 5) 获取 CANFD 初始化配置(仲裁/数据波特率);
/// 6) 初始化 CANFD。
///
/// 注意:该方法不解析 DBCDBC 解析由 <see cref="StartDbc"/> 显式触发。
/// </remarks>
public void StartCanDrive()
{
try
@@ -61,14 +95,16 @@ namespace CapMachine.Wpf.CanDrive
}
catch (Exception ex)
{
LogService.Error(ex.Message);
LoggerService.Error(ex.Message);
System.Windows.MessageBox.Show($"{ex.Message}", "提示", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Hand);
}
}
/// <summary>
/// 开始Dbc文件写入
/// 解析并加载 DBC 文件。
/// </summary>
/// <param name="DbcPath">DBC 文件路径。</param>
/// <returns>解析得到的信号列表(用于 UI 展示与实时值刷新)。</returns>
public ObservableCollection<CanDbcModel> StartDbc(string DbcPath)
{
DBC_Parser(DbcPath);
@@ -76,43 +112,51 @@ namespace CapMachine.Wpf.CanDrive
}
/// <summary>
/// Dbc消息集合
/// DBC 解析得到的信号集合
/// </summary>
/// <remarks>
/// 接收线程会持续更新 <see cref="CanDbcModel.SignalRtValue"/>。
/// </remarks>
public ObservableCollection<CanDbcModel> ListCanFdDbcModel { get; set; } = new ObservableCollection<CanDbcModel>();
#region
/// <summary>
/// 仲裁波特率
/// 仲裁波特率Arbitration Bit Rate
/// </summary>
public uint ArbBaudRate { get; set; } = 500000;
/// <summary>
/// 数据波特率
/// 数据波特率Data Bit Rate
/// </summary>
public uint DataBaudRate { get; set; } = 2000000;
/// <summary>
/// CAN FD标准 ISO是否启用
/// 是否启用 ISO CRCCAN FD ISO 标准)。
/// </summary>
public bool ISOEnable { get; set; } = true;
/// <summary>
/// 终端电阻 是否启用
/// 终端电阻是否启用
/// </summary>
public bool ResEnable { get; set; } = true;
/// <summary>
/// 更新配置
/// 更新 CANFD 参数配置(仅更新内存变量,实际生效需重新 InitCAN
/// </summary>
public void UpdateConfig(uint arbBaudRate, uint dataBaudRate, bool iSOEnable, bool resEnable,ushort sendCycle)
/// <param name="arbBaudRate">仲裁波特率。</param>
/// <param name="dataBaudRate">数据波特率。</param>
/// <param name="iSOEnable">ISO CRC 使能。</param>
/// <param name="resEnable">终端电阻使能。</param>
/// <param name="sendCycle">软件循环发送周期ms。</param>
public void UpdateConfig(uint arbBaudRate, uint dataBaudRate, bool iSOEnable, bool resEnable, ushort sendCycle)
{
ArbBaudRate = arbBaudRate;
DataBaudRate = dataBaudRate;
ISOEnable = iSOEnable;
ResEnable = resEnable;
SendCycle=sendCycle;
SendCycle = sendCycle;
}
/// <summary>
@@ -136,8 +180,11 @@ namespace CapMachine.Wpf.CanDrive
public USB2CANFD.CANFD_BUS_ERROR CurCANFD_BUS_ERROR = new USB2CANFD.CANFD_BUS_ERROR();
/// <summary>
/// DBC Handle
/// DBC 解析成功后得到的句柄。
/// </summary>
/// <remarks>
/// 为 0 表示 DBC 未解析/解析失败。
/// </remarks>
public UInt64 DBCHandle { get; set; }
/// <summary>
@@ -151,19 +198,19 @@ namespace CapMachine.Wpf.CanDrive
public Int32 DevHandle { get; set; } = 0;
/// <summary>
/// Write CAN Index
/// 发送通道索引0=CAN1,1=CAN2
/// </summary>
public Byte WriteCANIndex { get; set; } = 0;
/// <summary>
/// Read CAN Index
/// 接收通道索引0=CAN1,1=CAN2
/// </summary>
public Byte ReadCANIndex { get; set; } = 0;
private bool _OpenState;
/// <summary>
/// 打开设备的状态
/// 设备打开状态
/// </summary>
public bool OpenState
{
@@ -173,7 +220,7 @@ namespace CapMachine.Wpf.CanDrive
private bool _DbcParserState;
/// <summary>
/// DBC解析状态
/// DBC 解析状态
/// </summary>
public bool DbcParserState
{
@@ -402,8 +449,12 @@ namespace CapMachine.Wpf.CanDrive
}
/// <summary>
/// 发送CAN数据
/// 单次发送:根据 CmdData 中的信号值构建 CANFD 报文并发送。
/// </summary>
/// <param name="CmdData">待发送的信号指令集合(按 MsgName 分组后组成帧)。</param>
/// <remarks>
/// 注意:该方法会分配临时非托管内存 msgPt并在结束前释放。
/// </remarks>
public void SendCanMsg(List<CanCmdData> CmdData)
{
var GroupMsg = CmdData.GroupBy(x => x.MsgName);
@@ -414,6 +465,7 @@ namespace CapMachine.Wpf.CanDrive
CanMsg[i].Data = new Byte[64];
}
// 非托管缓冲SDK API 以指针形式写入帧结构体
IntPtr msgPt = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(USB2CANFD.CANFD_MSG)));
int Index = 0;
//循环给MSG赋值数据
@@ -447,7 +499,7 @@ namespace CapMachine.Wpf.CanDrive
//DBCParser.DBC_SyncValueToCANFDMsg(DBCHandle, new StringBuilder("msg_speed_can"), msgPt);
//CanMsg[2] = (USB2CANFD.CANFD_MSG)Marshal.PtrToStructure(msgPt, typeof(USB2CANFD.CANFD_MSG));
//释放申请的临时缓冲区
// 释放临时非托管缓冲区,避免内存泄漏
Marshal.FreeHGlobal(msgPt);
Console.WriteLine("");
//发送CAN数据
@@ -484,8 +536,18 @@ namespace CapMachine.Wpf.CanDrive
private static readonly Random _random = new Random();
/// <summary>
/// 精确周期发送CAN数据
/// 定时扫描更新数据 扫描Task
/// </summary>
private static Task CycleUpdateCmdTask { get; set; }
/// <summary>
/// 软件侧精确周期发送 CANFD 数据。
/// </summary>
/// <remarks>
/// - 使用 <see cref="Stopwatch"/> tick 作为时间基准,减少 DateTime 抖动;
/// - 使用 Task.Delay + SpinWait 组合等待;
/// - 使用 <see cref="CycleSendCts"/> 支持外部取消。
/// </remarks>
public void StartPrecisionCycleSendMsg()
{
// 创建取消标记源 用于控制任务的取消 允许在需要时通过取消令牌来优雅停止任务
@@ -550,7 +612,7 @@ namespace CapMachine.Wpf.CanDrive
//此时重置NextExecutionTime为当前时间避免连续的延迟累积
// 严重延迟,重新校准
NextExecutionTime = Stopwatcher.ElapsedTicks;
LogService.Info("定时发送延迟过大,重新校准时间");
LoggerService.Info("定时发送延迟过大,重新校准时间");
Console.WriteLine("定时发送延迟过大,重新校准时间");
}
@@ -572,6 +634,7 @@ namespace CapMachine.Wpf.CanDrive
CanMsg[i].Data = new Byte[64];
}
// 发送构帧临时缓冲:每轮申请/释放,避免与其他线程共享同一指针导致并发问题
IntPtr msgPtSend = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(USB2CANFD.CANFD_MSG)));
int Index = 0;
@@ -590,7 +653,7 @@ namespace CapMachine.Wpf.CanDrive
//通过DBC写入数据后生成CanMsg
//将信号值填入CAN消息里面
//释放申请的临时缓冲区
// 释放非托管缓冲区(务必释放,否则长时间运行会造成内存泄漏)
Marshal.FreeHGlobal(msgPtSend);
//CanMsg[0].Flags = 5;
@@ -618,7 +681,7 @@ namespace CapMachine.Wpf.CanDrive
}
catch (Exception ex)
{
LogService.Error(ex.Message);
LoggerService.Error(ex.Message);
Console.WriteLine($"CAN周期发送异常: {ex.Message}");
// 短暂暂停避免异常情况下CPU占用过高
IsSendOk = false;
@@ -632,7 +695,7 @@ namespace CapMachine.Wpf.CanDrive
{
// 确保在任何情况下(正常退出、异常、取消)都会停止计时器
Stopwatcher.Stop();
LogService.Error(ex.Message);
LoggerService.Error(ex.Message);
// 清理其他可能的资源
Console.WriteLine("CAN周期发送任务已结束资源已清理");
IsSendOk = false;
@@ -674,8 +737,12 @@ namespace CapMachine.Wpf.CanDrive
}
/// <summary>
/// 修改停止发送的方法
/// 停止软件循环发送。
/// </summary>
/// <remarks>
/// - 置 <see cref="IsCycleSend"/> 为 false循环自然退出
/// - 触发 <see cref="CycleSendCts"/> 取消,用于尽快唤醒 Delay 并退出。
/// </remarks>
public void StopCycleSendMsg()
{
IsCycleSend = false;
@@ -765,7 +832,7 @@ namespace CapMachine.Wpf.CanDrive
if (messageStopwatch.ElapsedTicks >= nextExecutionTime + cycleInTicks)
{
nextExecutionTime = messageStopwatch.ElapsedTicks;
LogService.Info($"消息{msgName}定时发送延迟过大,重新校准时间");
LoggerService.Info($"消息{msgName}定时发送延迟过大,重新校准时间");
}
// 使用锁确保DBC操作和发送的线程安全
@@ -779,12 +846,12 @@ namespace CapMachine.Wpf.CanDrive
// 为信号赋值
foreach (var signal in signals)
{
CAN_DBCParser.DBC_SetSignalValue(DBCHandle, new StringBuilder(msgName),
DBCParserByFD.DBC_SetSignalValue(DBCHandle, new StringBuilder(msgName),
new StringBuilder(signal.SignalName), signal.SignalCmdValue);
}
// 同步值到CAN消息
CAN_DBCParser.DBC_SyncValueToCANMsg(DBCHandle, new StringBuilder(msgName), msgPtSend);
DBCParserByFD.DBC_SyncValueToCANFDMsg(DBCHandle, new StringBuilder(msgName), msgPtSend);
var canMsg = (USB2CAN.CAN_MSG)Marshal.PtrToStructure(msgPtSend, typeof(USB2CAN.CAN_MSG));
// 直接发送此消息,无需通过队列
@@ -800,7 +867,7 @@ namespace CapMachine.Wpf.CanDrive
else
{
IsSendOk = false;
LogService.Info($"消息{msgName}发送失败: {sendedNum}");
LoggerService.Info($"消息{msgName}发送失败: {sendedNum}");
}
}
finally
@@ -812,23 +879,23 @@ namespace CapMachine.Wpf.CanDrive
}
catch (TaskCanceledException)
{
LogService.Info($"消息{msgName}发送任务被取消");
LoggerService.Info($"消息{msgName}发送任务被取消");
break;
}
catch (Exception ex)
{
LogService.Info($"消息{msgName}发送异常: {ex.Message}");
LoggerService.Info($"消息{msgName}发送异常: {ex.Message}");
await Task.Delay(10, token);
}
}
}
catch (Exception ex)
{
LogService.Info($"消息{msgName}发送线程异常: {ex.Message}");
LoggerService.Info($"消息{msgName}发送线程异常: {ex.Message}");
}
finally
{
LogService.Info($"消息{msgName}发送线程已退出");
LoggerService.Info($"消息{msgName}发送线程已退出");
}
}, token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
@@ -845,11 +912,11 @@ namespace CapMachine.Wpf.CanDrive
}
catch (Exception ex)
{
LogService.Info($"并行发送监控任务异常: {ex.Message}");
LoggerService.Info($"并行发送监控任务异常: {ex.Message}");
}
finally
{
LogService.Info("并行发送任务已全部结束");
LoggerService.Info("并行发送任务已全部结束");
IsSendOk = false;
}
}, token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
@@ -859,9 +926,405 @@ namespace CapMachine.Wpf.CanDrive
#region
private bool _SchEnable;
/// <summary>
/// 调度表使能开关。
/// </summary>
/// <remarks>
/// 该开关用于控制“硬件调度表发送”及“CmdData 变化时是否增量更新调度表”。
/// </remarks>
public bool SchEnable
{
get { return _SchEnable; }
set
{
_SchEnable = value;
RaisePropertyChanged();
}
}
/// <summary>
/// 调度表的发送的报文数据
/// </summary>
private USB2CANFD.CANFD_MSG[] SchCanMsg { get; set; }
/// <summary>
/// 依据消息的分组
/// </summary>
private IEnumerable<IGrouping<string, CanCmdData>> GroupMsg { get; set; }
/// <summary>
/// 调度表集合数据
/// 总共3个调度表第一个表里面包含3帧数据第二个调度表包含6帧数据第三个调度表包含11帧数据
/// Byte[] MsgTabNum = new Byte[3] { 3, 6, 11 };
/// </summary>
private Byte[] MsgTabNum { get; set; }
/// <summary>
/// 调度表发送的次数集合
/// 第一个调度表循环发送数据第二个调度表循环发送数据第三个调度表只发送3次
/// UInt16[] SendTimes = new UInt16[3] { 0xFFFF, 0xFFFF, 3 };
/// </summary>
private UInt16[] SendTimes { get; set; }
/// <summary>
/// 预设的调度表的个数 常值
/// </summary>
private const int MsgTabCount = 5;
/// <summary>
/// 定时更新时间
/// </summary>
private int UpdateCycle { get; set; } = 100;
/// <summary>
/// CNA 调度表的配置信息
/// </summary>
public List<CANFdScheduleConfigDto> ListCANScheduleConfig { get; set; }
Random random = new Random();
/// <summary>
/// 监控数据
/// 查找问题用,平时不用
/// </summary>
//public MonitorValueLog monitorValueLog { get; set; }
/// <summary>
/// 更新数据 测试用废弃了
/// </summary>
public void UpdateValue()
{
//通过DBC进行对消息赋值
IntPtr msgPtSend = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(USB2CANFD.CANFD_MSG)));
int Index = 0;
//循环给MSG赋值数据
foreach (var itemMsg in GroupMsg)
{
foreach (var itemSignal in itemMsg)
{
itemSignal.SignalCmdValue = random.Next(0, 100);
DBCParserByFD.DBC_SetSignalValue(DBCHandle, new StringBuilder(itemMsg.Key), new StringBuilder(itemSignal.SignalName), itemSignal.SignalCmdValue);
}
DBCParserByFD.DBC_SyncValueToCANFDMsg(DBCHandle, new StringBuilder(itemMsg.Key), msgPtSend);
SchCanMsg[Index] = (USB2CANFD.CANFD_MSG)Marshal.PtrToStructure(msgPtSend, typeof(USB2CANFD.CANFD_MSG));
Index++;
}
//通过DBC写入数据后生成CanMsg
//将信号值填入CAN消息里面
//释放申请的临时缓冲区
Marshal.FreeHGlobal(msgPtSend);
////总共3个调度表第一个表里面包含3帧数据第二个调度表包含6帧数据第三个调度表包含11帧数据
//MsgTabNum = new Byte[1] { 1 };
////第一个调度表循环发送数据第二个调度表循环发送数据第三个调度表只发送3次
//SendTimes = new UInt16[1] { 0xFFFF };
//var ret = USB2CANFD.CAN_SetSchedule(DevHandle, WriteCANIndex, SchCanMsg, MsgTabNum, SendTimes, 1);//配置调度表,该函数耗时可能会比较长,但是只需要执行一次即可
var ret = USB2CANFD.CANFD_UpdateSchedule(DevHandle, WriteCANIndex, 0, 0, SchCanMsg, 1);//配置调度表,该函数耗时可能会比较长,但是只需要执行一次即可
if (ret == USB2CANFD.CANFD_SUCCESS)
{
Console.WriteLine("Update CAN Schedule Success");
}
else
{
Console.WriteLine("Update CAN Schedule Error ret = {0}", ret);
return;
}
}
/// <summary>
/// 启动硬件调度表CANFD
/// </summary>
/// <remarks>
/// 流程与 CAN 类似:
/// 1) 基于 <see cref="CmdData"/> 分组构建帧数组;
/// 2) 使用 DBC 将信号值同步到帧;
/// 3) 将调度周期写入帧 <see cref="USB2CANFD.CANFD_MSG.TimeStamp"/>
/// 4) 调用 CANFD_SetSchedule 下发;
/// 5) 调用 CANFD_StartSchedule 启动调度器 0。
/// </remarks>
public void StartSchedule()
{
if (CmdData.Count() == 0) return;
//依据报文进行分组
GroupMsg = CmdData.GroupBy(x => x.MsgName)!;
//初始化调度表要发送的消息结构
SchCanMsg = new USB2CANFD.CANFD_MSG[GroupMsg.Count()];
for (int i = 0; i < GroupMsg.Count(); i++)
{
SchCanMsg[i] = new USB2CANFD.CANFD_MSG();
SchCanMsg[i].Data = new Byte[64];
}
//通过DBC进行对消息赋值
IntPtr msgPtSend = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(USB2CANFD.CANFD_MSG)));
int Index = 0;
//循环给MSG赋值数据
foreach (var itemMsg in GroupMsg)
{
foreach (var itemSignal in itemMsg)
{
DBCParserByFD.DBC_SetSignalValue(DBCHandle, new StringBuilder(itemMsg.Key), new StringBuilder(itemSignal.SignalName), itemSignal.SignalCmdValue);
}
DBCParserByFD.DBC_SyncValueToCANFDMsg(DBCHandle, new StringBuilder(itemMsg.Key), msgPtSend);
//每个分组就是一个帧指令/消息数据
SchCanMsg[Index] = (USB2CANFD.CANFD_MSG)Marshal.PtrToStructure(msgPtSend, typeof(USB2CANFD.CANFD_MSG));
// 将“MsgName -> 帧 index”的映射写回调度表配置用于调试/定位(当前更新实现是全量更新,不依赖 MsgIndex
if (ListCANScheduleConfig.Any(a => a.MsgName == itemMsg.Key))
{
//把帧的位置给ListCANScheduleConfig方便更新时定位数据
ListCANScheduleConfig.FindFirst(a => a.MsgName == itemMsg.Key).MsgIndex = Index;
// 设备 SDK 约定TimeStamp 字段用于调度表发送周期(单位毫秒)
SchCanMsg[Index].TimeStamp = (uint)ListCANScheduleConfig.FindFirst(a => a.MsgName == itemMsg.Key).Cycle;
}
Index++;
}
//通过DBC写入数据后生成CanMsg
//将信号值填入CAN消息里面
//释放申请的临时缓冲区
Marshal.FreeHGlobal(msgPtSend);
//********就是可以设置多个调度表放那里,但是运行时同一个时刻只能运行调度表其中的一个 ********
//****** 控制报文SchCanMsg和调度器中第一个调度器中的报文集合和要更新的报文集合都是同一个变量SchCanMsg ********
// *** SchCanMsg的Index序号和ListCANScheduleConfig的MsgIndex是一样的 ***
//图莫斯的Sample总共3个调度表第一个表里面包含3帧数据第二个调度表包含6帧数据第三个调度表包含11帧数据
//预设5个调度表但是我们只用其中第一个调度表第一个调度表中包括多少消息帧由系统的控制指令的帧的分布决定SchCanMsg.Count()是所需要的控制发送的帧,都放到第一个调度表中
MsgTabNum = new Byte[MsgTabCount] { (byte)SchCanMsg.Count(), 1, 1, 1, 1 };
//0xFFFF调度表循环发送数据X调度表循环发送的次数
//设置每个调度表的发送方式,约定全部为循环发送
SendTimes = new UInt16[MsgTabCount] { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
//SchCanMsg:需要发送的消息集合MsgTabNum调度表集合数据SendTimes发送次数集合数据调度表个数MsgTabCount
var ret = USB2CANFD.CANFD_SetSchedule(DevHandle, WriteCANIndex, SchCanMsg, MsgTabNum, SendTimes, MsgTabCount);//配置调度表,该函数耗时可能会比较长,但是只需要执行一次即可
if (ret == USB2CANFD.CANFD_SUCCESS)
{
Console.WriteLine("Set CAN Schedule Success");
}
else
{
Console.WriteLine("Set CAN Schedule Error ret = {0}", ret);
LoggerService.Info($"Set CAN Schedule Error; 返回错误代码:{ret}");
return;
}
// 约定使用调度器索引 0同一时刻只能运行一个调度器且本项目把所有控制帧都集中在同一个调度器里
//CAN_MSG的TimeStamp就是这个报文发送的周期由调度器协调
ret = USB2CANFD.CANFD_StartSchedule(DevHandle, WriteCANIndex, (byte)0, (byte)100, (byte)ListCANScheduleConfig.FirstOrDefault()!.OrderSend);
if (ret == USB2CANFD.CANFD_SUCCESS)
{
Console.WriteLine($"Start CAN Schedule 1 SuccessSchTabIndex{(byte)0} - Cycle{(byte)100} - OrderSend{(byte)1}");
}
else
{
Console.WriteLine("Start CAN Schedule 1 Error ret = {0}", ret);
LoggerService.Info($"Start CAN Schedule 1 Error;");
return;
}
//foreach (var itemGroupMsg in GroupMsg)
//{
// if (itemGroupMsg == null) continue;
// if (ListCANScheduleConfig.Any(a => a.MsgName!.Contains(itemGroupMsg.Key)))
// {
// var CANScheduleConfig = ListCANScheduleConfig.FindFirst(a => a.MsgName!.Contains(itemGroupMsg.Key));
// //配置表里面包括这个报文消息内容
// ret = USB2CANFD.CAN_StartSchedule(DevHandle, WriteCANIndex, (byte)CANScheduleConfig.SchTabIndex, (byte)CANScheduleConfig.Cycle, (byte)CANScheduleConfig.OrderSend);
// if (ret == USB2CANFD.CAN_SUCCESS)
// {
// Console.WriteLine($"Start CAN Schedule 1 SuccessSchTabIndex{(byte)CANScheduleConfig.SchTabIndex} - Cycle{(byte)CANScheduleConfig.Cycle} - OrderSend{(byte)CANScheduleConfig.OrderSend}");
// }
// else
// {
// Console.WriteLine("Start CAN Schedule 1 Error ret = {0}", ret);
// LoggerService.Info($"Start CAN Schedule 1 Error;消息名称:{CANScheduleConfig.MsgName}");
// return;
// }
// }
// else
// {
// LoggerService.Info($"调度表配置未发现对应的消息报文信息;报文信息{itemGroupMsg.Key}");
// }
//}
//走到这里说明调度表执行的是OK的
//IsSendOk = true;
}
/// <summary>
/// 停止硬件调度表。
/// </summary>
/// <remarks>
/// 停止后设备侧将不再按周期自动发送。
/// </remarks>
public void StopSchedule()
{
ret = USB2CANFD.CANFD_StopSchedule(DevHandle, WriteCANIndex);//启动第一个调度表,表里面的CAN帧并行发送
if (ret == USB2CANFD.CANFD_SUCCESS)
{
IsSendOk = false;
Console.WriteLine("Stop CAN Schedule");
LoggerService.Info($"Stop CAN Schedule");
}
else
{
Console.WriteLine("Start CAN Schedule Error ret = {0}", ret);
LoggerService.Info($"Stop CAN Schedule");
return;
}
}
/// <summary>
/// 循环使用的
/// </summary>
private int CycleUpdateIndex = 0;
/// <summary>
/// 绑定/刷新驱动侧要发送的 CmdData并自动维护“值变化事件”的订阅。
/// </summary>
/// <param name="cmdData">新的指令集合。</param>
/// <remarks>
/// 注意:必须先对旧集合取消订阅,否则会造成重复触发和内存泄漏。
/// </remarks>
public void LoadCmdDataToDrive(List<CanCmdData> cmdData)
{
// Unsubscribe from events on the old CmdData items
if (CmdData != null && CmdData.Count > 0)
{
foreach (var cmd in CmdData)
{
cmd.CanCmdDataChangedHandler -= CmdData_CanCmdDataChangedHandler;
}
}
// Set the new data and subscribe to events
CmdData = cmdData;
foreach (var cmd in cmdData)
{
cmd.CanCmdDataChangedHandler += CmdData_CanCmdDataChangedHandler;
}
}
/// <summary>
/// CmdData 中任意信号值变化事件处理。
/// </summary>
/// <param name="sender">事件源。</param>
/// <param name="e">变化的 MsgName约定为报文名。</param>
private void CmdData_CanCmdDataChangedHandler(object? sender, string e)
{
UpdateSchDataByCmdDataChanged();
}
/// <summary>
/// 调度表更新互斥锁。
/// </summary>
private readonly object SchUpdateLock = new object();
/// <summary>
/// 当 CmdData 中信号值变化时,增量刷新调度表的帧数据。
/// </summary>
/// <remarks>
/// 触发条件:<see cref="IsCycleSend"/> 与 <see cref="SchEnable"/> 同时为 true。
/// 当前实现为“全量覆盖更新”CANFD_UpdateSchedule
/// </remarks>
public void UpdateSchDataByCmdDataChanged()
{
try
{
if (!IsCycleSend) return;
if (!SchEnable) return;
// 基础防御:确保 DBC/ 调度表 / 分组已经初始化
if (DBCHandle == 0 || SchCanMsg == null || GroupMsg == null)
{
return;
}
lock (SchUpdateLock)
{
// 重新构建每一帧的 Data并覆盖更新到设备调度表
IntPtr msgPtSend = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(USB2CANFD.CANFD_MSG)));
int CycleUpdateIndex = 0;
//循环给MSG赋值数据顺序是固定的跟初始时设置是一样的
foreach (var itemMsg in GroupMsg)
{
foreach (var itemSignal in itemMsg)
{
//itemSignal.SignalCmdValue = random.Next(0, 100); //仿真测试数据使用
var SetSignalValue = DBCParserByFD.DBC_SetSignalValue(DBCHandle, new StringBuilder(itemMsg.Key), new StringBuilder(itemSignal.SignalName), itemSignal.SignalCmdValue);
//monitorValueLog.UpdateValue1(SetSignalValue);
}
var SyncValueToCanMsg = DBCParserByFD.DBC_SyncValueToCANFDMsg(DBCHandle, new StringBuilder(itemMsg.Key), msgPtSend);
//monitorValueLog.UpdateValue2(SyncValueToCanMsg);
SchCanMsg[CycleUpdateIndex] = (USB2CANFD.CANFD_MSG)Marshal.PtrToStructure(msgPtSend, typeof(USB2CANFD.CANFD_MSG));
CycleUpdateIndex++;
}
//通过DBC写入数据后生成CanMsg
//将信号值填入CAN消息里面
// 释放非托管缓冲
Marshal.FreeHGlobal(msgPtSend);
//CAN_UpdateSchedule 官网解释
// ---MsgTabIndex CAN调度表索引号
// ---MsgIndex 开始更新帧起始索引,若起始索引大于调度表帧数,则将帧添加到调度表后面
// ---pCanMsg 需要更新的CAN帧指针
// ---MsgNum pCanMsgTab里面包含的有效帧数
//CAN_UpdateSchedule中的MsgIndex表示当前的调度器中的帧Index序号
//因为调度表中的帧集合和控制帧的集合和要更新的帧集合都是同一个集合SchCanMsg
// 默认调度表索引 0一次性覆盖更新所有帧MsgIndex=0, MsgNum=帧数)
var ret = USB2CANFD.CANFD_UpdateSchedule(DevHandle, WriteCANIndex, (byte)0, (byte)(0), SchCanMsg, (byte)SchCanMsg.Count());//配置调度表,该函数耗时可能会比较长,但是只需要执行一次即可
if (ret == USB2CANFD.CANFD_SUCCESS)
{
IsSendOk = true;
Console.WriteLine($"Update CAN Schedule Success -- SchTabIndex{(byte)0} -- MsgIndex{(byte)(0)}-- SchCanMsg.Count(){(SchCanMsg.Count())} ");
//monitorValueLog.UpdateValue3(ret);
}
else
{
IsSendOk = false;
//Console.WriteLine($"Update CAN Schedule Error ret = {ret} -- SchTabIndex{(byte)0} -- MsgIndex{(byte)(0)}");
//monitorValueLog.UpdateValue3(ret);
LoggerService.Info($"更新调度表失败,错误码:{ret}");
//return;
}
}
}
catch (Exception ex)
{
IsSendOk = false;
LoggerService.Info($"时间:{DateTime.Now.ToString()}-【MSG】-{ex.Message}");
}
}
#endregion
private bool _IsCycleRevice;
/// <summary>
/// 是否循环接收数据
/// 是否循环接收数据
/// </summary>
public bool IsCycleRevice
{
@@ -872,7 +1335,7 @@ namespace CapMachine.Wpf.CanDrive
private bool _IsCycleSend;
/// <summary>
/// 是否循环发送数据
/// 是否循环发送数据
/// </summary>
public bool IsCycleSend
{
@@ -881,12 +1344,12 @@ namespace CapMachine.Wpf.CanDrive
}
/// <summary>
/// 循环发送数据
/// 软件循环发送周期(毫秒)。
/// </summary>
public ushort SendCycle { get; set; } = 200;
/// <summary>
/// 循环接受数据
/// 接收轮询周期(毫秒)。
/// </summary>
public ushort ReviceCycle { get; set; } = 200;
@@ -906,7 +1369,7 @@ namespace CapMachine.Wpf.CanDrive
private bool _IsSendOk;
/// <summary>
/// 发送报文是否OK
/// 最近一次发送是否成功。
/// </summary>
public bool IsSendOk
{
@@ -924,7 +1387,7 @@ namespace CapMachine.Wpf.CanDrive
private bool _IsReviceOk;
/// <summary>
/// 接收报文是否OK
/// 最近一次接收是否成功。
/// </summary>
public bool IsReviceOk
{
@@ -941,18 +1404,22 @@ namespace CapMachine.Wpf.CanDrive
/// <summary>
/// 要发送的数据
/// 当前激活的指令集合(由 Service/UI 下发)。
/// </summary>
public List<CanCmdData> CmdData { get; set; } = new List<CanCmdData>();
/// <summary>
/// 循环获取CAN消息
/// 启动后台循环接收 CANFD 报文,并同步到 DBC 信号实时值。
/// </summary>
/// <remarks>
/// 注意:当前实现每轮接收都会分配 msgPtRead 并释放,长时间运行会造成 GC 压力和潜在碎片;
/// 若后续需要优化,可参考 CAN 版本的“接收缓冲池复用 + CloseDevice 互斥释放”。
/// </remarks>
public void StartCycleReviceCanMsg()
{
CycleReviceTask = Task.Run(async () =>
{
//var ret = USB2CANFD.CANFD_StartGetMsg(DevHandle, ReadCANIndex);
//var ret = USB2CANFDFD.CANFD_StartGetMsg(DevHandle, ReadCANIndex);
while (IsCycleRevice)
{
await Task.Delay(ReviceCycle);
@@ -990,14 +1457,14 @@ namespace CapMachine.Wpf.CanDrive
else if (CanNum == 0)
{
IsReviceOk = false;
Console.WriteLine("No CAN data!");
//Console.WriteLine("No CAN data!");
}
else
{
IsReviceOk = false;
Console.WriteLine("Get CAN data error!");
}
Console.WriteLine("");
//Console.WriteLine("");
//将CAN消息数据填充到信号里面
DBCParserByFD.DBC_SyncCANFDMsgToValue(DBCHandle, msgPtRead, CanNum);
@@ -1008,7 +1475,7 @@ namespace CapMachine.Wpf.CanDrive
//有配置的名称的,认为是有用的,则需要读取数据
//if (!string.IsNullOrEmpty(item.Name))
//{
//CAN_DBCParser.DBC_GetSignalValueStr(DBCHandle, new StringBuilder(item.MsgName), new StringBuilder(item.SignalName), ValueSb);
//DBCParserByFD.DBC_GetSignalValueStr(DBCHandle, new StringBuilder(item.MsgName), new StringBuilder(item.SignalName), ValueSb);
//double[] ValueDouble;
DBCParserByFD.DBC_GetSignalValue(DBCHandle, new StringBuilder(item.MsgName), new StringBuilder(item.SignalName), ValueDouble);
//item.SignalRtValueSb = ValueSb;
@@ -1017,7 +1484,7 @@ namespace CapMachine.Wpf.CanDrive
//}
}
//释放数据缓冲区,必须释放,否则程序运行一段时间后会报内存不足
// 释放数据缓冲区,必须释放,否则程序运行一段时间后会报内存不足
Marshal.FreeHGlobal(msgPtRead);
Thread.Sleep(10);
@@ -1031,8 +1498,8 @@ namespace CapMachine.Wpf.CanDrive
catch (Exception ex)
{
IsReviceOk = false;
LogService.Error(ex.Message);
//LogService.Info($"时间:{DateTime.Now.ToString()}-【Meter】-{ex.Message}");
LoggerService.Error(ex.Message);
//LoggerService.Info($"时间:{DateTime.Now.ToString()}-【Meter】-{ex.Message}");
}
}
@@ -1091,8 +1558,14 @@ namespace CapMachine.Wpf.CanDrive
/// <summary>
/// 关闭设备
/// 关闭设备并释放资源。
/// </summary>
/// <remarks>
/// 释放顺序要点:
/// - 关闭设备并下置状态位;
/// - 若启用调度表则先停止;
/// - 等待接收任务退出(短等待,避免 UI 卡死)。
/// </remarks>
public void CloseDevice()
{
//关闭设备
@@ -1101,6 +1574,25 @@ namespace CapMachine.Wpf.CanDrive
DbcParserState = false;
IsCycleRevice = false;
IsCycleSend = false;
if (SchEnable)
{
StopSchedule();
}
// 确保定时器发送被停止并释放资源
//try { StopTimerCycleSendMsg(); } catch { }
// 等待接收任务结束后释放非托管缓冲区,避免并发释放
try
{
var task = CycleReviceTask;
if (task != null && !task.IsCompleted)
{
task.Wait(TimeSpan.FromMilliseconds(ReviceCycle + 500));
}
}
catch { }
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,82 @@
using System;
namespace CapMachine.Wpf.CanDrive.ZlgCan
{
/// <summary>
/// ZLG CANFD 通道初始化参数。
/// </summary>
public sealed class ZlgCanFdChannelOptions
{
/// <summary>
/// 仲裁域波特率单位bps。例如 500000。
/// </summary>
public uint ArbitrationBaudRate { get; set; } = 500000;
/// <summary>
/// 数据域波特率单位bps。例如 2000000。
/// </summary>
public uint DataBaudRate { get; set; } = 2000000;
/// <summary>
/// 终端电阻。
/// </summary>
public bool EnableInternalResistance { get; set; } = true;
/// <summary>
/// 仅监听模式。
/// </summary>
public bool ListenOnly { get; set; } = false;
/// <summary>
/// 是否启用总线利用率上报。
/// </summary>
public bool EnableBusUsage { get; set; } = false;
/// <summary>
/// 总线利用率上报周期单位ms
/// </summary>
public int BusUsagePeriodMs { get; set; } = 500;
/// <summary>
/// 是否启用设备层“合并接收”ZCAN_ReceiveData
/// </summary>
public bool EnableMergeReceive { get; set; } = false;
/// <summary>
/// 合并接收缓冲区最大帧数量。
/// </summary>
public int MergeReceiveBufferFrames { get; set; } = 100;
}
/// <summary>
/// ZLG LIN 通道初始化参数。
/// </summary>
public sealed class ZlgLinChannelOptions
{
/// <summary>
/// LIN 模式true=主节点false=从节点。
/// </summary>
public bool IsMaster { get; set; } = true;
/// <summary>
/// 校验模式。
/// 1-经典校验2-增强校验3-自动。
/// </summary>
public byte ChecksumMode { get; set; } = 3;
/// <summary>
/// 最大数据长度8~64
/// </summary>
public byte MaxLength { get; set; } = 8;
/// <summary>
/// 波特率1000~20000
/// </summary>
public uint BaudRate { get; set; } = 19200;
/// <summary>
/// LIN 接收轮询等待时间ms
/// </summary>
public int ReceiveWaitMs { get; set; } = 10;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,62 @@
using System;
namespace CapMachine.Wpf.CanDrive.ZlgCan
{
/// <summary>
/// ZLG CAN ID 帮助类。
/// </summary>
public static class ZlgCanIdHelper
{
/// <summary>
/// 生成 ZLG/SocketCAN 风格的 can_id包含扩展帧/远程帧/错误帧标志位)。
/// </summary>
/// <param name="id">CAN 标识符(标准帧 11bit0~0x7FF扩展帧 29bit0~0x1FFFFFFF。</param>
/// <param name="isExtended">是否为扩展帧。</param>
/// <param name="isRemote">是否为远程帧RTR。</param>
/// <param name="isError">是否为错误帧。</param>
/// <returns>包含标志位的 can_id。</returns>
public static uint MakeCanId(uint id, bool isExtended, bool isRemote, bool isError)
{
// 兼容官方样例写法bit31 扩展帧bit30 RTRbit29 ERR
// 注意:该函数不做强约束校验(例如扩展帧 29bit上层可按需要增加校验。
uint canId = id & 0x1FFFFFFF;
if (isExtended)
{
canId |= 1u << 31;
}
if (isRemote)
{
canId |= 1u << 30;
}
if (isError)
{
canId |= 1u << 29;
}
return canId;
}
/// <summary>
/// 从 can_id 中提取 29bit 标识符。
/// </summary>
/// <param name="canId">包含标志位的 can_id。</param>
/// <returns>29bit 标识符。</returns>
public static uint GetArbitrationId(uint canId)
{
return canId & 0x1FFFFFFF;
}
/// <summary>
/// 判断 can_id 是否为扩展帧。
/// </summary>
/// <param name="canId">包含标志位的 can_id。</param>
/// <returns>是否扩展帧。</returns>
public static bool IsExtended(uint canId)
{
return (canId & (1u << 31)) != 0;
}
}
}

View File

@@ -0,0 +1,728 @@
using CapMachine.Wpf.CanDrive;
using CapMachine.Wpf.Services;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace CapMachine.Wpf.CanDrive.ZlgCan
{
/// <summary>
/// 基于周立功 ZDBCzdbc.dll的 DBC 数据库封装。
/// </summary>
/// <remarks>
/// 设计要点:
/// - zdbc.dll 为原生依赖必须放置在程序输出目录AppContext.BaseDirectory
/// - 本类封装 DBC 文件加载、消息/信号枚举、以及基于 DBCMessage 的编码/解码能力。
/// - 运行稳定性:实现 IDisposable确保 ZDBC_Release 与相关非托管内存释放。
/// </remarks>
public sealed class ZlgDbcDatabase : IDisposable
{
private readonly ILogService _log;
private readonly object _sync = new object();
private uint _dbcHandle;
private bool _disposed;
// 复用非托管缓冲,避免高频 Allocate/Free
private IntPtr _msgPtr = IntPtr.Zero;
private IntPtr _countPtr = IntPtr.Zero;
private IntPtr _canFramePtr = IntPtr.Zero;
private IntPtr _canFdFramePtr = IntPtr.Zero;
private readonly Dictionary<string, ZDBC.DBCMessage> _messageByName = new Dictionary<string, ZDBC.DBCMessage>(StringComparer.Ordinal);
private readonly Dictionary<string, uint> _messageIdByName = new Dictionary<string, uint>(StringComparer.Ordinal);
/// <summary>
/// 构造函数。
/// </summary>
/// <param name="logService">日志服务。</param>
public ZlgDbcDatabase(ILogService logService)
{
_log = logService;
}
/// <summary>
/// 是否已加载。
/// </summary>
public bool IsLoaded
{
get
{
return _dbcHandle != 0 && _dbcHandle != ZDBC.INVALID_DBC_HANDLE;
}
}
/// <summary>
/// 加载 DBC 文件。
/// </summary>
/// <param name="dbcPath">DBC 文件路径。</param>
/// <param name="enableAsyncAnalyse">是否启用异步解析(库内部线程)。</param>
/// <param name="merge">是否合并到当前数据库(支持加载多个文件)。</param>
/// <param name="protocolType">协议类型J1939=0其他=1。</param>
public void Load(string dbcPath, bool enableAsyncAnalyse = true, bool merge = false, byte protocolType = ZDBC.PROTOCOL_OTHER)
{
ThrowIfDisposed();
if (string.IsNullOrWhiteSpace(dbcPath))
{
throw new ArgumentException("dbcPath 不能为空。", nameof(dbcPath));
}
EnsureNativeDllExists("zdbc.dll");
if (!File.Exists(dbcPath))
{
throw new FileNotFoundException("DBC 文件不存在。", dbcPath);
}
var fileSize = 0L;
try
{
fileSize = new FileInfo(dbcPath).Length;
}
catch
{
}
var fullPath = dbcPath;
try
{
fullPath = Path.GetFullPath(dbcPath);
}
catch
{
}
var shortPath = TryGetShortPath(fullPath);
var candidatePaths = new List<string>();
if (!string.IsNullOrWhiteSpace(fullPath)) candidatePaths.Add(fullPath);
if (!string.IsNullOrWhiteSpace(shortPath)
&& !string.Equals(shortPath, fullPath, StringComparison.OrdinalIgnoreCase))
{
candidatePaths.Add(shortPath);
}
lock (_sync)
{
if (_dbcHandle == 0 || _dbcHandle == ZDBC.INVALID_DBC_HANDLE)
{
_dbcHandle = ZDBC.ZDBC_Init(0, enableAsyncAnalyse ? (byte)1 : (byte)0);
if (_dbcHandle == ZDBC.INVALID_DBC_HANDLE)
{
_dbcHandle = 0;
throw new InvalidOperationException("ZDBC_Init 初始化失败。");
}
}
var loadOk = false;
foreach (var path in candidatePaths)
{
var fileInfo = new ZDBC.FileInfo
{
strFilePath = BuildFixedPathBytes(path, ZDBC._MAX_FILE_PATH_ + 1),
type = protocolType,
merge = (byte)(merge ? 1 : 0)
};
IntPtr pFile = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZDBC.FileInfo)));
try
{
Marshal.StructureToPtr(fileInfo, pFile, false);
loadOk = ZDBC.ZDBC_LoadFile(_dbcHandle, pFile);
if (loadOk)
{
break;
}
}
finally
{
Marshal.FreeHGlobal(pFile);
}
}
if (!loadOk)
{
// fallback直接按内容加载绕开路径编码问题同时尝试多种编码以提升兼容性。
loadOk = TryLoadContentFallback_NoLock(fullPath, merge);
}
if (!loadOk)
{
var msg = $"ZDBC_LoadFile 失败。DbcPath={fullPath}ShortPath={shortPath ?? string.Empty}Size={fileSize}。请检查 DBC 文件格式,或将 DBC 放到纯英文路径后重试。";
throw new InvalidOperationException(msg);
}
RefreshMessageCache_NoLock();
}
}
/// <summary>
/// 枚举 DBC 内全部 Message/Signal 元信息,并转换为 UI 使用的 CanDbcModel 集合。
/// </summary>
/// <returns>信号集合。</returns>
public ObservableCollection<CanDbcModel> BuildCanDbcModels()
{
ThrowIfDisposed();
if (!IsLoaded)
{
return new ObservableCollection<CanDbcModel>();
}
lock (_sync)
{
var list = new List<CanDbcModel>();
foreach (var kv in _messageByName)
{
var msg = kv.Value;
var msgName = ByteArrayToAsciiString(msg.strName);
var msgIdStr = "0x" + msg.nID.ToString("X8");
var signals = msg.vSignals;
if (signals == null || signals.Length == 0)
{
continue;
}
var signalCount = (int)Math.Min(msg.nSignalCount, (uint)signals.Length);
for (int i = 0; i < signalCount; i++)
{
var s = signals[i];
var sigName = ByteArrayToAsciiString(s.strName);
if (string.IsNullOrWhiteSpace(sigName))
{
continue;
}
list.Add(new CanDbcModel
{
MsgName = msgName,
MsgId = msgIdStr,
SignalName = sigName,
SignalDesc = ByteArrayToAsciiString(s.strComment),
SignalUnit = ByteArrayToAsciiString(s.unit),
SignalRtValue = "--",
Publisher = string.Empty
});
}
}
return new ObservableCollection<CanDbcModel>(list);
}
}
/// <summary>
/// 根据 MsgName 获取 Message ID。
/// </summary>
/// <param name="msgName">消息名称。</param>
/// <returns>Message ID。</returns>
public uint GetMessageIdByName(string msgName)
{
ThrowIfDisposed();
if (string.IsNullOrWhiteSpace(msgName)) return 0;
lock (_sync)
{
return _messageIdByName.TryGetValue(msgName, out var id) ? id : 0;
}
}
/// <summary>
/// 根据 MsgName + 信号值集合编码为 CAN/CANFD 原始帧。
/// </summary>
/// <param name="msgName">消息名称。</param>
/// <param name="signals">信号值SignalName -> 实际值)。</param>
/// <param name="frameType">帧类型FT_CAN=0FT_CANFD=1。</param>
/// <returns>编码后的 can_id 与数据。</returns>
public (uint CanId, byte[] Data, int DataLen) EncodeToRawFrame(string msgName, IDictionary<string, double> signals, byte frameType)
{
ThrowIfDisposed();
if (!IsLoaded)
{
throw new InvalidOperationException("DBC 未加载。");
}
if (string.IsNullOrWhiteSpace(msgName))
{
throw new ArgumentException("msgName 不能为空。", nameof(msgName));
}
lock (_sync)
{
if (!_messageByName.TryGetValue(msgName, out var msg))
{
throw new InvalidOperationException($"DBC 中未找到消息:{msgName}");
}
// 在副本上修改信号,避免污染缓存
var workingMsg = msg;
if (workingMsg.vSignals == null)
{
workingMsg.vSignals = new ZDBC.DBCSignal[ZDBC._DBC_SIGNAL_MAX_COUNT_];
}
if (signals != null)
{
foreach (var kv in signals)
{
SetSignalActualValue(ref workingMsg, kv.Key, kv.Value);
}
}
EnsureMarshalBuffers_NoLock();
// 写入 msg 到非托管
Marshal.StructureToPtr(workingMsg, _msgPtr, false);
// nCount 输入输出参数,准备 1 帧
Marshal.WriteInt32(_countPtr, 1);
if (frameType == ZDBC.FT_CAN)
{
var ok = ZDBC.ZDBC_Encode(_dbcHandle, _canFramePtr, _countPtr, _msgPtr, frameType);
if (!ok)
{
throw new InvalidOperationException("ZDBC_Encode(CAN) 失败。");
}
var frame = (ZlgNativeCanFrame)Marshal.PtrToStructure(_canFramePtr, typeof(ZlgNativeCanFrame));
var len = Math.Min(8, (int)frame.can_dlc);
var data = new byte[len];
if (len > 0 && frame.data != null)
{
Array.Copy(frame.data, data, len);
}
return (frame.can_id, data, len);
}
if (frameType == ZDBC.FT_CANFD)
{
var ok = ZDBC.ZDBC_Encode(_dbcHandle, _canFdFramePtr, _countPtr, _msgPtr, frameType);
if (!ok)
{
throw new InvalidOperationException("ZDBC_Encode(CANFD) 失败。");
}
var frame = (ZlgNativeCanFdFrame)Marshal.PtrToStructure(_canFdFramePtr, typeof(ZlgNativeCanFdFrame));
var len = Math.Min(64, (int)frame.len);
var data = new byte[len];
if (len > 0 && frame.data != null)
{
Array.Copy(frame.data, data, len);
}
return (frame.can_id, data, len);
}
throw new ArgumentOutOfRangeException(nameof(frameType), "frameType 仅支持 FT_CAN=0 或 FT_CANFD=1。");
}
}
/// <summary>
/// 将原始 CAN/CANFD 帧解码为 DBCMessage并返回消息名称与信号实际值。
/// </summary>
/// <param name="canId">can_id。</param>
/// <param name="data">payload。</param>
/// <param name="frameType">帧类型FT_CAN=0FT_CANFD=1。</param>
/// <returns>解码结果。</returns>
public (string MsgName, Dictionary<string, double> Signals) DecodeRawFrame(uint canId, byte[] data, byte frameType)
{
ThrowIfDisposed();
if (!IsLoaded)
{
throw new InvalidOperationException("DBC 未加载。");
}
lock (_sync)
{
EnsureMarshalBuffers_NoLock();
if (frameType == ZDBC.FT_CAN)
{
var frame = ZlgNativeCanFrame.Create(canId, data ?? Array.Empty<byte>(), requestTxEcho: false);
Marshal.StructureToPtr(frame, _canFramePtr, false);
var ok = ZDBC.ZDBC_Decode(_dbcHandle, _msgPtr, _canFramePtr, 1, frameType);
if (!ok)
{
return (string.Empty, new Dictionary<string, double>());
}
var msg = Marshal.PtrToStructure<ZDBC.DBCMessage>(_msgPtr);
return ExtractActualSignals(msg);
}
if (frameType == ZDBC.FT_CANFD)
{
var frame = ZlgNativeCanFdFrame.Create(canId, data ?? Array.Empty<byte>(), requestTxEcho: false);
Marshal.StructureToPtr(frame, _canFdFramePtr, false);
var ok = ZDBC.ZDBC_Decode(_dbcHandle, _msgPtr, _canFdFramePtr, 1, frameType);
if (!ok)
{
return (string.Empty, new Dictionary<string, double>());
}
var msg = Marshal.PtrToStructure<ZDBC.DBCMessage>(_msgPtr);
return ExtractActualSignals(msg);
}
throw new ArgumentOutOfRangeException(nameof(frameType), "frameType 仅支持 FT_CAN=0 或 FT_CANFD=1。");
}
}
/// <inheritdoc />
public void Dispose()
{
if (_disposed) return;
_disposed = true;
lock (_sync)
{
try
{
if (_dbcHandle != 0 && _dbcHandle != ZDBC.INVALID_DBC_HANDLE)
{
ZDBC.ZDBC_Release(_dbcHandle);
}
}
catch (Exception ex)
{
_log.Warn($"ZDBC_Release 异常:{ex.Message}");
}
finally
{
_dbcHandle = 0;
}
FreeMarshalBuffers_NoLock();
_messageByName.Clear();
_messageIdByName.Clear();
}
}
private void RefreshMessageCache_NoLock()
{
_messageByName.Clear();
_messageIdByName.Clear();
if (!IsLoaded)
{
return;
}
EnsureMarshalBuffers_NoLock();
// 遍历消息
var msg = new ZDBC.DBCMessage
{
vSignals = new ZDBC.DBCSignal[ZDBC._DBC_SIGNAL_MAX_COUNT_],
strName = new byte[ZDBC._DBC_NAME_LENGTH_ + 1],
strComment = new byte[ZDBC._DBC_COMMENT_MAX_LENGTH_ + 1]
};
Marshal.StructureToPtr(msg, _msgPtr, false);
var ok = ZDBC.ZDBC_GetFirstMessage(_dbcHandle, _msgPtr);
while (ok)
{
var m = Marshal.PtrToStructure<ZDBC.DBCMessage>(_msgPtr);
var name = ByteArrayToAsciiString(m.strName);
if (!string.IsNullOrWhiteSpace(name))
{
_messageByName[name] = m;
_messageIdByName[name] = m.nID;
}
Marshal.StructureToPtr(msg, _msgPtr, false);
ok = ZDBC.ZDBC_GetNextMessage(_dbcHandle, _msgPtr);
}
}
private void EnsureMarshalBuffers_NoLock()
{
if (_msgPtr == IntPtr.Zero)
{
_msgPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZDBC.DBCMessage)));
}
if (_countPtr == IntPtr.Zero)
{
_countPtr = Marshal.AllocHGlobal(sizeof(int));
}
if (_canFramePtr == IntPtr.Zero)
{
_canFramePtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZlgNativeCanFrame)));
}
if (_canFdFramePtr == IntPtr.Zero)
{
_canFdFramePtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZlgNativeCanFdFrame)));
}
}
private void FreeMarshalBuffers_NoLock()
{
if (_canFdFramePtr != IntPtr.Zero)
{
Marshal.FreeHGlobal(_canFdFramePtr);
_canFdFramePtr = IntPtr.Zero;
}
if (_canFramePtr != IntPtr.Zero)
{
Marshal.FreeHGlobal(_canFramePtr);
_canFramePtr = IntPtr.Zero;
}
if (_countPtr != IntPtr.Zero)
{
Marshal.FreeHGlobal(_countPtr);
_countPtr = IntPtr.Zero;
}
if (_msgPtr != IntPtr.Zero)
{
Marshal.FreeHGlobal(_msgPtr);
_msgPtr = IntPtr.Zero;
}
}
private static void EnsureNativeDllExists(string dllName)
{
var baseDir = AppContext.BaseDirectory;
var full = Path.Combine(baseDir, dllName);
if (!File.Exists(full))
{
throw new FileNotFoundException($"未找到 {dllName},请将其复制到程序输出目录:{baseDir}", full);
}
}
private void SetSignalActualValue(ref ZDBC.DBCMessage msg, string signalName, double actualValue)
{
if (msg.vSignals == null || msg.vSignals.Length == 0)
{
return;
}
var count = (int)Math.Min(msg.nSignalCount, (uint)msg.vSignals.Length);
for (int i = 0; i < count; i++)
{
var s = msg.vSignals[i];
var name = ByteArrayToAsciiString(s.strName);
if (!string.Equals(name, signalName, StringComparison.Ordinal))
{
continue;
}
// 使用 ZDBC_CalcRawValue 做实际值->原始值转换
IntPtr pSgl = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZDBC.DBCSignal)));
IntPtr pVal = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(double)));
try
{
Marshal.StructureToPtr(s, pSgl, false);
Marshal.StructureToPtr(actualValue, pVal, false);
var raw = ZDBC.ZDBC_CalcRawValue(pSgl, pVal);
msg.vSignals[i].nRawvalue = raw;
}
finally
{
Marshal.FreeHGlobal(pVal);
Marshal.FreeHGlobal(pSgl);
}
return;
}
}
private (string MsgName, Dictionary<string, double> Signals) ExtractActualSignals(ZDBC.DBCMessage msg)
{
var msgName = ByteArrayToAsciiString(msg.strName);
var dict = new Dictionary<string, double>(StringComparer.Ordinal);
if (msg.vSignals == null)
{
return (msgName, dict);
}
var count = (int)Math.Min(msg.nSignalCount, (uint)msg.vSignals.Length);
for (int i = 0; i < count; i++)
{
var s = msg.vSignals[i];
var name = ByteArrayToAsciiString(s.strName);
if (string.IsNullOrWhiteSpace(name))
{
continue;
}
// 使用 ZDBC_CalcActualValue 做原始值->实际值转换
IntPtr pSgl = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZDBC.DBCSignal)));
IntPtr pRaw = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ulong)));
try
{
Marshal.StructureToPtr(s, pSgl, false);
Marshal.StructureToPtr(s.nRawvalue, pRaw, false);
var actual = ZDBC.ZDBC_CalcActualValue(pSgl, pRaw);
dict[name] = actual;
}
finally
{
Marshal.FreeHGlobal(pRaw);
Marshal.FreeHGlobal(pSgl);
}
}
return (msgName, dict);
}
private static byte[] BuildFixedPathBytes(string path, int fixedLen)
{
var bytes = Encoding.Default.GetBytes(path);
var dst = new byte[fixedLen];
Array.Clear(dst, 0, dst.Length);
Array.Copy(bytes, 0, dst, 0, Math.Min(bytes.Length, fixedLen - 1));
return dst;
}
/// <summary>
/// 当 ZDBC_LoadFile 失败时的兜底加载策略:读取文件内容并调用 ZDBC_LoadContent。
/// </summary>
/// <param name="fullPath">DBC 完整路径。</param>
/// <param name="merge">是否合并。</param>
/// <returns>加载成功返回 true。</returns>
private bool TryLoadContentFallback_NoLock(string fullPath, bool merge)
{
// zdbc.dll 入口为 ANSI 字符串,因此这里优先使用 ASCII将非 ASCII 字符替换为 ?
// 避免中文注释/特殊字符导致原生库解析失败;同时需要兼容 UTF-16 BOM否则用 ASCII 读会出现大量 \0 造成内容截断。
var encodings = new List<Encoding>();
try
{
var bytes = File.ReadAllBytes(fullPath);
if (bytes.Length >= 2)
{
// UTF-16 LE BOM: FF FE
if (bytes[0] == 0xFF && bytes[1] == 0xFE)
{
encodings.Add(Encoding.Unicode);
}
// UTF-16 BE BOM: FE FF
else if (bytes[0] == 0xFE && bytes[1] == 0xFF)
{
encodings.Add(Encoding.BigEndianUnicode);
}
}
if (bytes.Length >= 3)
{
// UTF-8 BOM: EF BB BF
if (bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF)
{
encodings.Add(Encoding.UTF8);
}
}
}
catch
{
}
// 常规兜底编码序列
encodings.Add(new UTF8Encoding(false, true));
encodings.Add(Encoding.UTF8);
encodings.Add(Encoding.Default);
encodings.Add(Encoding.ASCII);
foreach (var enc in encodings.Distinct())
{
IntPtr pContent = IntPtr.Zero;
try
{
var content = File.ReadAllText(fullPath, enc);
pContent = Marshal.StringToHGlobalAnsi(content);
var ok = ZDBC.ZDBC_LoadContent(_dbcHandle, pContent, merge ? 1u : 0u);
if (ok)
{
return true;
}
}
catch
{
}
finally
{
if (pContent != IntPtr.Zero)
{
Marshal.FreeHGlobal(pContent);
}
}
}
return false;
}
/// <summary>
/// 获取 Windows 8.3 短路径。
/// </summary>
/// <param name="lpszLongPath">长路径。</param>
/// <param name="lpszShortPath">短路径缓冲。</param>
/// <param name="cchBuffer">缓冲区大小。</param>
/// <returns>返回写入的字符数量0 表示失败。</returns>
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern uint GetShortPathName(string lpszLongPath, StringBuilder lpszShortPath, uint cchBuffer);
/// <summary>
/// 尝试将长路径转换为短路径8.3),用于兼容部分原生库对中文/特殊字符路径处理不完整的问题。
/// </summary>
/// <param name="fullPath">长路径。</param>
/// <returns>短路径;失败返回 null。</returns>
private static string? TryGetShortPath(string fullPath)
{
try
{
if (string.IsNullOrWhiteSpace(fullPath))
{
return null;
}
var sb = new StringBuilder(1024);
var ret = GetShortPathName(fullPath, sb, (uint)sb.Capacity);
if (ret == 0)
{
return null;
}
return sb.ToString();
}
catch
{
return null;
}
}
private static string ByteArrayToAsciiString(byte[]? bytes)
{
if (bytes == null || bytes.Length == 0) return string.Empty;
int len = Array.IndexOf(bytes, (byte)0);
if (len < 0) len = bytes.Length;
return Encoding.Default.GetString(bytes, 0, len).Trim();
}
private void ThrowIfDisposed()
{
if (_disposed)
{
throw new ObjectDisposedException(nameof(ZlgDbcDatabase));
}
}
}
}

View File

@@ -0,0 +1,103 @@
using System;
using System.Runtime.InteropServices;
namespace CapMachine.Wpf.CanDrive.ZlgCan
{
/// <summary>
/// 供 DBC 编解码/Marshal 使用的原生 CAN 帧结构(与 zlgcan can_frame 二进制布局兼容)。
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZlgNativeCanFrame
{
public uint can_id;
public byte can_dlc;
public byte __pad;
public byte __res0;
public byte __res1;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public byte[] data;
/// <summary>
/// 创建 CAN 帧。
/// </summary>
/// <param name="canId">can_id包含扩展帧等标志位。</param>
/// <param name="payload">数据0~8。</param>
/// <param name="requestTxEcho">是否请求发送回显。</param>
/// <returns>原生 CAN 帧。</returns>
public static ZlgNativeCanFrame Create(uint canId, byte[] payload, bool requestTxEcho)
{
var len = Math.Min(8, payload?.Length ?? 0);
var frame = new ZlgNativeCanFrame
{
can_id = canId,
can_dlc = (byte)len,
__pad = 0,
__res0 = 0,
__res1 = 0,
data = new byte[8]
};
if (len > 0 && payload != null)
{
Array.Copy(payload, frame.data, len);
}
if (requestTxEcho)
{
frame.__pad |= 0x20;
}
return frame;
}
}
/// <summary>
/// 供 DBC 编解码/Marshal 使用的原生 CANFD 帧结构(与 zlgcan canfd_frame 二进制布局兼容)。
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ZlgNativeCanFdFrame
{
public uint can_id;
public byte len;
public byte flags;
public byte __res0;
public byte __res1;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
public byte[] data;
/// <summary>
/// 创建 CANFD 帧。
/// </summary>
/// <param name="canId">can_id包含扩展帧等标志位。</param>
/// <param name="payload">数据0~64。</param>
/// <param name="requestTxEcho">是否请求发送回显。</param>
/// <returns>原生 CANFD 帧。</returns>
public static ZlgNativeCanFdFrame Create(uint canId, byte[] payload, bool requestTxEcho)
{
var len = Math.Min(64, payload?.Length ?? 0);
var frame = new ZlgNativeCanFdFrame
{
can_id = canId,
len = (byte)len,
flags = 0,
__res0 = 0,
__res1 = 0,
data = new byte[64]
};
if (len > 0 && payload != null)
{
Array.Copy(payload, frame.data, len);
}
if (requestTxEcho)
{
frame.flags |= 0x20;
}
return frame;
}
}
}

View File

@@ -73,5 +73,16 @@ namespace CapMachine.Wpf.Dtos
get { return _DbcPath; }
set { _DbcPath = value; RaisePropertyChanged(); }
}
private bool _SchEnable;
/// <summary>
/// 调度表是否启用
/// </summary>
public bool SchEnable
{
get { return _SchEnable; }
set { _SchEnable = value; RaisePropertyChanged(); }
}
}
}

View File

@@ -0,0 +1,83 @@
using CapMachine.Model.CANLIN;
using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CapMachine.Wpf.Dtos
{
/// <summary>
/// CANFdScheduleConfigDto
/// </summary>
public class CANFdScheduleConfigDto : BindableBase
{
/// <summary>
/// 主键
/// </summary>
public long Id { get; set; }
private string? _MsgName;
/// <summary>
/// 消息名称/帧名称
/// </summary>
public string? MsgName
{
get { return _MsgName; }
set { _MsgName = value; RaisePropertyChanged(); }
}
private int _Cycle;
/// <summary>
/// 周期
/// </summary>
public int Cycle
{
get { return _Cycle; }
set { _Cycle = value; RaisePropertyChanged(); }
}
private int _OrderSend;
/// <summary>
/// 发送方式
/// </summary>
public int OrderSend
{
get { return _OrderSend; }
set { _OrderSend = value; RaisePropertyChanged(); }
}
private int _SchTabIndex;
/// <summary>
/// 调度表的Index序列
/// </summary>
public int SchTabIndex
{
get { return _SchTabIndex; }
set { _SchTabIndex = value; RaisePropertyChanged(); }
}
/// <summary>
/// 在更新调度表数据时,我们有一个整体的帧数据指令集合,但是这些帧数据集合,分属于不同的调度表,
/// 这个在开始时生成整体的帧数据指令集合才会把这个MsgIndex分配上这个不需要保存到数据库中对接使用
/// </summary>
public int MsgIndex { get; set; }
/// <summary>
/// 程序的ID
/// </summary>
public long CanLinConfigProId { get; set; }
private CanLinConfigPro _CanLinConfigPro;
/// <summary>
/// 所属的程序
/// </summary>
public CanLinConfigPro CanLinConfigPro
{
get { return _CanLinConfigPro; }
set { _CanLinConfigPro = value; RaisePropertyChanged(); }
}
}
}

View File

@@ -92,6 +92,16 @@ namespace CapMachine.Wpf.Dtos
set { _CapNo = value; RaisePropertyChanged(); }
}
private string? _CapDisplacement;
/// <summary>
/// 压缩机排量
/// </summary>
public string? CapDisplacement
{
get { return _CapDisplacement; }
set { _CapDisplacement = value; RaisePropertyChanged(); }
}
private string? _RfNo;
/// <summary>
/// 制冷剂

View File

@@ -385,6 +385,42 @@ namespace CapMachine.Wpf.Models
[Name("过热度[K]")]
public double Superheat { get; set; }
/// <summary>
/// 制热量Qh[W]
/// </summary>
[Name("制热量Qh[W]")]
public double HeatingCapacity { get; set; }
/// <summary>
/// 压缩机性能系数(制热)
/// </summary>
[Name("压缩机性能系数(制热)")]
public double COPHeat { get; set; }
/// <summary>
/// 等熵效率ns[%]
/// </summary>
[Name("等熵效率ns[%]")]
public double IsentrpEff { get; set; }
/// <summary>
/// 制冷量Qc[W]
/// </summary>
[Name("制冷量Qc[W]")]
public double CoolCapacity { get; set; }
/// <summary>
/// 压缩机性能系数(制冷)
/// </summary>
[Name("压缩机性能系数(制冷)")]
public double COPCool { get; set; }
/// <summary>
/// 容积效率nv[%]
/// </summary>
[Name("容积效率nv[%]")]
public double VoltricEff { get; set; }
/// <summary>
/// 创建时间
/// </summary>

View File

@@ -77,6 +77,13 @@ namespace CapMachine.Wpf.Models
Map(m => m.Dryness).Name("干度[%]");
Map(m => m.Subcooling).Name("过冷度[K]");
Map(m => m.Superheat).Name("过热度[K]");
Map(m => m.HeatingCapacity).Name("制热量Qh[W]").TypeConverterOption.Format("0");
Map(m => m.COPHeat).Name("压缩机性能系数(制热)").TypeConverterOption.Format("0.0");
Map(m => m.IsentrpEff).Name("等熵效率ns[%]").TypeConverterOption.Format("0.0");
Map(m => m.CoolCapacity).Name("制冷量Qc[W]").TypeConverterOption.Format("0");
Map(m => m.COPCool).Name("压缩机性能系数(制冷)").TypeConverterOption.Format("0.0");
Map(m => m.VoltricEff).Name("容积效率nv[%]").TypeConverterOption.Format("0.0");
Map(m => m.CreateTime).Name("时间");

View File

@@ -0,0 +1,11 @@
using Prism.Events;
namespace CapMachine.Wpf.PrismEvent
{
/// <summary>
/// CAN/CANFD/LIN 配置变更事件。
/// </summary>
public class CanLinConfigChangedEvent : PubSubEvent<string>
{
}
}

View File

@@ -1,5 +1,6 @@
using CapMachine.Model.CANLIN;
using CapMachine.Wpf.CanDrive;
using CapMachine.Wpf.Dtos;
using CapMachine.Wpf.Models;
using CapMachine.Wpf.Models.ProModelPars;
using ImTools;
@@ -25,9 +26,9 @@ namespace CapMachine.Wpf.Services
/// <summary>
/// 实例化函数
/// </summary>
public CanFdDriveService(HighSpeedDataService highSpeedDataService, IContainerProvider containerProvider, LogicRuleService logicRuleService)
public CanFdDriveService(HighSpeedDataService highSpeedDataService, IContainerProvider containerProvider, LogicRuleService logicRuleService, ILogService logService)
{
ToomossCanFDDrive = new ToomossCanFD(containerProvider);
ToomossCanFDDrive = new ToomossCanFD(containerProvider, logService);
//高速数据服务
HighSpeedDataService = highSpeedDataService;
LogicRuleService = logicRuleService;
@@ -126,6 +127,11 @@ namespace CapMachine.Wpf.Services
/// </summary>
public List<CanCmdData> CmdData { get; set; } = new List<CanCmdData>();
/// <summary>
/// CNA 调度表的配置信息
/// </summary>
public List<CANFdScheduleConfigDto> ListCANFdScheduleConfig { get; set; }
/// <summary>
/// 增加发送的指令数据
/// </summary>
@@ -212,7 +218,7 @@ namespace CapMachine.Wpf.Services
{
if (PwLimitCanCmdData != null)
{
if (PwLimitCanCmdData.LogicRuleDto==null)
if (PwLimitCanCmdData.LogicRuleDto == null)
{
PwLimitCanCmdData.SignalCmdValue = PwLimit;
return;
@@ -264,7 +270,7 @@ namespace CapMachine.Wpf.Services
PTCFlowCanCmdData.SignalCmdValue = Flow;
return;
}
PTCFlowCanCmdData.SignalCmdValue = LogicRuleService.ApplyExpressionFast(Flow, PTCFlowCanCmdData.LogicRuleDto!);
PTCFlowCanCmdData.SignalCmdValue = LogicRuleService.ApplyExpressionFast(Flow, PTCFlowCanCmdData.LogicRuleDto!);
}
}
@@ -322,10 +328,39 @@ namespace CapMachine.Wpf.Services
{
if (CmdData.Count > 0)
{
ToomossCanFDDrive.IsCycleSend = true;
//把指令数据赋值给CAN 驱动
ToomossCanFDDrive.CmdData = CmdData;
//ToomossCanFDDrive.StartCycleSendMsg();
ToomossCanFDDrive.StartPrecisionCycleSendMsg();
if (ToomossCanFDDrive.SchEnable)
{
//使用调度表的话,需要在调度表中配置信息后才可以进行操作
var GroupMsg = ToomossCanFDDrive.CmdData.GroupBy(a => a.MsgName).ToList();
foreach (var itemMsg in GroupMsg)
{
if (!ListCANFdScheduleConfig.ToList().Any(a => a.MsgName == itemMsg.Key))
{
System.Windows.MessageBox.Show($"你使能了调度表,但是调度表中没有设置【{itemMsg.Key}】信息,请设置后再操作", "提示", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Hand);
return;
}
}
if (ListCANFdScheduleConfig == null && ListCANFdScheduleConfig!.Count() == 0)
{
System.Windows.MessageBox.Show("调度表配置为空数据,请检查!将无法发送数据", "提示", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Hand);
return;
}
ToomossCanFDDrive.ListCANScheduleConfig = ListCANFdScheduleConfig!;
ToomossCanFDDrive.StartSchedule();
//ToomossCanFDDrive.StartCycleUpdateCmd();
}
else
{
ToomossCanFDDrive.StartPrecisionCycleSendMsg();
//ToomossCanFDDrive.StartCycleSendMsg();
}
ToomossCanFDDrive.IsCycleSend = true;
}
else
{
@@ -334,7 +369,15 @@ namespace CapMachine.Wpf.Services
}
else
{
ToomossCanFDDrive.IsCycleSend = false;
if (ToomossCanFDDrive.SchEnable)
{
ToomossCanFDDrive.IsCycleSend = false;
ToomossCanFDDrive.StopSchedule();
}
else
{
ToomossCanFDDrive.IsCycleSend = false;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -70,6 +70,9 @@ namespace CapMachine.Wpf.Services
}
ShowLogicRule(msg.Par);
break;
case "CAN/CANFD/LIN配置导入导出":
ShowCanLinConfigImExport(msg.Par);
break;
default:
break;
}
@@ -102,6 +105,7 @@ namespace CapMachine.Wpf.Services
}
/// <summary>
/// 过热度和过冷度配置弹窗
/// </summary>
@@ -135,6 +139,20 @@ namespace CapMachine.Wpf.Services
});
}
/// <summary>
/// CAN/CANFD/LIN 配置导入导出弹窗。
/// </summary>
/// <param name="par">透传参数。</param>
private void ShowCanLinConfigImExport(object par)
{
DialogService.ShowDialog("DialogCanLinConfigImExportView", new DialogParameters() { { "Name", par } }, (dialogResult) =>
{
if (dialogResult.Result == ButtonResult.OK)
{
}
});
}

View File

@@ -44,10 +44,10 @@ namespace CapMachine.Wpf.Services
{
new NavigationItem("Rule","规则转换","DialogLogicRuleView"),
}),
//new NavigationItem("", "PID设置","",new ObservableCollection<NavigationItem>()
//{
// new NavigationItem("Circle","转速PID",""),
//}),
new NavigationItem("", "配置导出","",new ObservableCollection<NavigationItem>()
{
new NavigationItem("","CAN/CANFD/LIN配置导入导出","DialogCanLinConfigImExport"),
}),
//new NavigationItem("", "通信配置","",new ObservableCollection<NavigationItem>()
//{
// new NavigationItem("Circle","CAN配置","CANConfigView"),
@@ -79,6 +79,8 @@ namespace CapMachine.Wpf.Services
MenuItems.Add(new NavigationItem("", "CAN配置", "CANConfigView"));
MenuItems.Add(new NavigationItem("", "CANFD配置", "CANFDConfigView"));
MenuItems.Add(new NavigationItem("", "LIN配置", "LINConfigView"));
//MenuItems.Add(new NavigationItem("", "ZLG CAN配置", "ZlgCanDriveConfigView"));
//MenuItems.Add(new NavigationItem("", "ZLG LIN配置", "ZlgLinDriveConfigView"));
MenuItems.Add(new NavigationItem("", "工艺参数", "ProConfigView"));
MenuItems.Add(new NavigationItem("", "工艺曲线", "RealTimeChartView"));
MenuItems.Add(new NavigationItem("", "动作日志", "ActionLogView"));

View File

@@ -10,8 +10,10 @@ using Prism.Events;
using Prism.Mvvm;
using Prism.Services.Dialogs;
using SixLabors.ImageSharp.ColorSpaces;
using System.Globalization;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
namespace CapMachine.Wpf.Services
{
@@ -23,7 +25,7 @@ namespace CapMachine.Wpf.Services
/// <summary>
/// 计算扫描 Task
/// </summary>
private static Task CalcTask { get; set; }
private static Task CalcTask { get; set; } = Task.CompletedTask;
public ConfigService ConfigService { get; }
private IEventAggregator _EventAggregator { get; set; }
@@ -93,7 +95,8 @@ namespace CapMachine.Wpf.Services
//TxvFrTempTag = TagManager.DicTags.GetValueOrDefault("膨胀阀前温度[℃]")!;
//TxvFrPressTag = TagManager.DicTags.GetValueOrDefault("膨胀阀前压力[BarA]")!;
if (TagManager.TryGetShortTagByName("SUBCOOL出口温度[℃]", out ShortValueTag? TxvFrTempShortTag))
if (TagManager.TryGetShortTagByName("膨胀阀前温度[℃]", out ShortValueTag? TxvFrTempShortTag) ||
TagManager.TryGetShortTagByName("SUBCOOL出口温度[℃]", out TxvFrTempShortTag))
{
TxvFrTempTag = TxvFrTempShortTag!;
}
@@ -150,6 +153,31 @@ namespace CapMachine.Wpf.Services
DrynessTag = drynessPct!;
}
if (TagManager.TryGetShortTagByName("制热量Qh[W]", out ShortValueTag? heatingCapacityTag))
{
HeatingCapacityTag = heatingCapacityTag!;
}
if (TagManager.TryGetShortTagByName("压缩机性能系数(制热)", out ShortValueTag? copHeatTag))
{
COPHeatTag = copHeatTag!;
}
if (TagManager.TryGetShortTagByName("等熵效率ns[%]", out ShortValueTag? isentrpEffTag))
{
IsentrpEffTag = isentrpEffTag!;
}
if (TagManager.TryGetShortTagByName("制冷量Qc[W]", out ShortValueTag? coolCapacityTag))
{
CoolCapacityTag = coolCapacityTag!;
}
if (TagManager.TryGetShortTagByName("压缩机性能系数(制冷)", out ShortValueTag? copCoolTag))
{
COPCoolTag = copCoolTag!;
}
if (TagManager.TryGetShortTagByName("容积效率nv[%]", out ShortValueTag? voltricEffTag))
{
VoltricEffTag = voltricEffTag!;
}
SuperHeatCoolConfig.FluidsPath = ConfigHelper.GetValue("FluidsPath");
SuperHeatCoolConfig.Cryogen = ConfigHelper.GetValue("Cryogen");
@@ -166,14 +194,14 @@ namespace CapMachine.Wpf.Services
/// </summary>
public void SaveSuperHeatCoolConfig()
{
ConfigHelper.SetValue("FluidsPath", SuperHeatCoolConfig.FluidsPath);
ConfigHelper.SetValue("Cryogen", SuperHeatCoolConfig.Cryogen);
ConfigHelper.SetValue("FluidsPath", SuperHeatCoolConfig.FluidsPath ?? string.Empty);
ConfigHelper.SetValue("Cryogen", SuperHeatCoolConfig.Cryogen ?? string.Empty);
}
/// <summary>
/// 吸气压力
/// </summary>
public ShortValueTag InhPressTag { get; set; }
public ShortValueTag InhPressTag { get; set; } = null!;
/// <summary>
/// 转速标签
@@ -198,32 +226,44 @@ namespace CapMachine.Wpf.Services
/// <summary>
/// 吸气温度
/// </summary>
public ShortValueTag InhTempTag { get; set; }
public ShortValueTag InhTempTag { get; set; } = null!;
/// <summary>
/// 液体阀前温度
/// </summary>
public ShortValueTag TxvFrTempTag { get; set; }
public ShortValueTag TxvFrTempTag { get; set; } = null!;
/// <summary>
/// 液体阀前压力
/// </summary>
public ShortValueTag TxvFrPressTag { get; set; }
public ShortValueTag TxvFrPressTag { get; set; } = null!;
/// <summary>
/// 过热度
/// </summary>
public ShortValueTag Superheat { get; set; }
public ShortValueTag Superheat { get; set; } = null!;
/// <summary>
/// 过冷度
/// </summary>
public ShortValueTag Subcool { get; set; }
public ShortValueTag Subcool { get; set; } = null!;
/// <summary>
/// 干度(无量纲 [-]
/// </summary>
public ShortValueTag DrynessTag { get; set; }
public ShortValueTag DrynessTag { get; set; } = null!;
public ShortValueTag? HeatingCapacityTag { get; set; }
public ShortValueTag? COPHeatTag { get; set; }
public ShortValueTag? IsentrpEffTag { get; set; }
public ShortValueTag? CoolCapacityTag { get; set; }
public ShortValueTag? COPCoolTag { get; set; }
public ShortValueTag? VoltricEffTag { get; set; }
private double _DrynessTag2Value;
/// <summary>
@@ -238,28 +278,28 @@ namespace CapMachine.Wpf.Services
/// <summary>
/// 气路阀前压力BarA
/// </summary>
public ShortValueTag GasPreValvePressTag { get; set; }
public ShortValueTag GasPreValvePressTag { get; set; } = null!;
/// <summary>
/// 气路阀前温度(℃)
/// </summary>
public ShortValueTag GasPreValveTempTag { get; set; }
public ShortValueTag GasPreValveTempTag { get; set; } = null!;
/// <summary>
/// 冷媒流量kg/h
/// </summary>
public ShortValueTag VRVTag { get; set; }
public ShortValueTag VRVTag { get; set; } = null!;
/// <summary>
/// 液体流量kg/h
/// 液冷媒流量kg/h=液体流量kg/h
/// </summary>
public ShortValueTag LiqRefFlowTag { get; set; }
public ShortValueTag LiqRefFlowTag { get; set; } = null!;
/// <summary>
/// 润滑油流量kg/h
/// </summary>
public ShortValueTag LubeFlowTag { get; set; }
public ShortValueTag LubeFlowTag { get; set; } = null!;
/// <summary>
/// 风量数据-乘以系数的后的最终结果
@@ -448,25 +488,25 @@ namespace CapMachine.Wpf.Services
// 处理 err2
}
//if (TryUpdateThermodynamicSixResults(out var thermoErr))
//{
// if (!string.IsNullOrWhiteSpace(thermoErr))
// {
// Logger?.Error($"六个物性结果计算警告: {thermoErr}");
// }
//}
//else
//{
// if (!string.IsNullOrWhiteSpace(thermoErr))
// {
// Logger?.Error($"六个物性结果计算失败: {thermoErr}");
// }
//}
if (TryUpdateThermodynamicSixResults(out var thermoErr))
{
if (!string.IsNullOrWhiteSpace(thermoErr))
{
Logger?.Error($"六个物性结果计算警告: {thermoErr}");
}
}
else
{
if (!string.IsNullOrWhiteSpace(thermoErr))
{
Logger?.Error($"六个物性结果计算失败: {thermoErr}");
}
}
}
catch (Exception ex)
{
Logger.Error(String.Format("ErrSource : {0} ErrMsg : {1}", ex.StackTrace.ToString(), ex.Message.ToString()));
Logger.Error(String.Format("ErrSource : {0} ErrMsg : {1}", ex.StackTrace ?? string.Empty, ex.Message));
}
}
});
@@ -1159,6 +1199,17 @@ namespace CapMachine.Wpf.Services
{
error = string.Empty;
if (InhPressTag == null || InhTempTag == null)
{
error = "缺少吸气压力/吸气温度标签";
return false;
}
if (TxvFrPressTag == null || TxvFrTempTag == null)
{
error = "缺少膨胀阀前压力/膨胀阀前温度标签";
return false;
}
if (!TryGetCompressorPower_kW(out var w_kW, out var wErr))
{
error = wErr;
@@ -1217,6 +1268,27 @@ namespace CapMachine.Wpf.Services
COPCooling = copC;
IsentropicEfficiencyPct = etaS_pct;
if (HeatingCapacityTag != null)
{
HeatingCapacityTag.PVModel.EngValue = HeatingCapacityQh_kW * 1000.0;
}
if (COPHeatTag != null)
{
COPHeatTag.PVModel.EngValue = COPHeating;
}
if (IsentrpEffTag != null)
{
IsentrpEffTag.PVModel.EngValue = IsentropicEfficiencyPct;
}
if (CoolCapacityTag != null)
{
CoolCapacityTag.PVModel.EngValue = CoolingCapacityQc_kW * 1000.0;
}
if (COPCoolTag != null)
{
COPCoolTag.PVModel.EngValue = COPCooling;
}
if (!TryComputeVolumetricEfficiencyPct(mRef_kg_s, v1_m3kg, out var etaV_pct, out var etaVErr))
{
VolumetricEfficiencyPct = double.NaN;
@@ -1225,6 +1297,11 @@ namespace CapMachine.Wpf.Services
}
VolumetricEfficiencyPct = etaV_pct;
if (VoltricEffTag != null)
{
VoltricEffTag.PVModel.EngValue = VolumetricEfficiencyPct;
}
return true;
}
@@ -1269,6 +1346,17 @@ namespace CapMachine.Wpf.Services
mRef_kg_s = double.NaN;
error = string.Empty;
if (VRVTag == null)
{
error = "缺少总流量(冷媒流量)标签";
return false;
}
if (LubeFlowTag == null)
{
error = "缺少油流量标签";
return false;
}
double mTotal_kg_h = VRVTag.PVModel.EngValue;
double mOil_kg_h = LubeFlowTag.PVModel.EngValue;
@@ -1634,26 +1722,49 @@ namespace CapMachine.Wpf.Services
displacement_cc = double.NaN;
error = string.Empty;
const double defaultDisplacementCc = 35d;
if (ConfigService?.CurExpInfo != null &&
TryParseCompressorDisplacementTextToCc(ConfigService.CurExpInfo.CapDisplacement, out var expCc) &&
expCc > 0)
{
displacement_cc = expCc;
return true;
}
const string key = "CompressorDisplacementCc";
if (!ConfigHelper.IsExist(key))
if (ConfigHelper.IsExist(key) && TryParseCompressorDisplacementTextToCc(ConfigHelper.GetValue(key), out var cfgCc) && cfgCc > 0)
{
displacement_cc = cfgCc;
return true;
}
displacement_cc = defaultDisplacementCc;
return true;
}
private static bool TryParseCompressorDisplacementTextToCc(string? text, out double displacementCc)
{
displacementCc = double.NaN;
if (string.IsNullOrWhiteSpace(text))
{
error = $"未配置压缩机排量,请在 App.config/appSettings 增加 {key}(单位 cc";
return false;
}
string v = ConfigHelper.GetValue(key);
if (!double.TryParse(v, out displacement_cc))
var normalized = text.Trim().ToLowerInvariant();
normalized = normalized.Replace(',', '.');
var match = Regex.Match(normalized, @"[-+]?\d+(\.\d+)?");
if (!match.Success)
{
error = $"压缩机排量配置无法解析: {v}";
return false;
}
if (displacement_cc <= 0)
if (!double.TryParse(match.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out var v))
{
error = $"压缩机排量<=0: {displacement_cc}";
return false;
}
displacementCc = v;
return true;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -506,28 +506,28 @@
"BlockIndex": 44
}
},
// {
// "Id": 240,
// "Name": "膨胀阀前温度[℃]",
// "NameNoUnit": "膨胀阀前温度",
// "EnName": "TxvFrTemp",
// "Group": "采集",
// "MinValue": 0,
// "MaxValue": 10000,
// "IsMeter": false,
// "DecimalPoint": 1,
// "Precision": 10,
// "Unit": "℃",
// "DataType": "Short",
// "RWInfo": "PLCRead",
// "PVModel": {
// "Address": "VW15046",
// "EngValue": 0,
// "EngValueStr": "",
// "Block": "PV",
// "BlockIndex": 46
// }
// },
{
"Id": 240,
"Name": "膨胀阀前温度[℃]",
"NameNoUnit": "膨胀阀前温度",
"EnName": "TxvFrTemp",
"Group": "采集",
"MinValue": 0,
"MaxValue": 10000,
"IsMeter": false,
"DecimalPoint": 1,
"Precision": 10,
"Unit": "℃",
"DataType": "Short",
"RWInfo": "PLCRead",
"PVModel": {
"Address": "VW15046",
"EngValue": 0,
"EngValueStr": "",
"Block": "PV",
"BlockIndex": 46
}
},
{
"Id": 250,
"Name": "EVAP出口压力[BarA]",
@@ -1265,6 +1265,137 @@
"Block": "PV",
"BlockIndex": 112
}
},
{
"Id": 560,
"Name": "制热量Qh[W]",
"NameNoUnit": "制热量Qh",
"EnName": "HeatingCapacity",
"Group": "计算",
"MinValue": 0,
"MaxValue": 1000000,
"IsMeter": false,
"DecimalPoint": 0,
"Precision": 1,
"Unit": "W",
"DataType": "Short",
"RWInfo": "PCCalcu",
"PVModel": {
"Address": "",
"EngValue": 0,
"EngValueStr": "",
"Block": "PV",
"BlockIndex": 0
}
},
{
"Id": 570,
"Name": "压缩机性能系数(制热)",
"NameNoUnit": "压缩机性能系数(制热)",
"EnName": "COPHeat",
"Group": "计算",
"MinValue": 0,
"MaxValue": 1000,
"IsMeter": false,
"DecimalPoint": 3,
"Precision": 1,
"Unit": "",
"DataType": "Short",
"RWInfo": "PCCalcu",
"PVModel": {
"Address": "",
"EngValue": 0,
"EngValueStr": "",
"Block": "PV",
"BlockIndex": 0
}
},
{
"Id": 580,
"Name": "等熵效率ns[%]",
"NameNoUnit": "等熵效率ns",
"EnName": "IsentrpEff",
"Group": "计算",
"MinValue": 0,
"MaxValue": 100,
"IsMeter": false,
"DecimalPoint": 2,
"Precision": 1,
"Unit": "%",
"DataType": "Short",
"RWInfo": "PCCalcu",
"PVModel": {
"Address": "",
"EngValue": 0,
"EngValueStr": "",
"Block": "PV",
"BlockIndex": 0
}
},
{
"Id": 590,
"Name": "制冷量Qc[W]",
"NameNoUnit": "制冷量Qc",
"EnName": "CoolCapacity",
"Group": "计算",
"MinValue": 0,
"MaxValue": 1000000,
"IsMeter": false,
"DecimalPoint": 0,
"Precision": 1,
"Unit": "W",
"DataType": "Short",
"RWInfo": "PCCalcu",
"PVModel": {
"Address": "",
"EngValue": 0,
"EngValueStr": "",
"Block": "PV",
"BlockIndex": 0
}
},
{
"Id": 600,
"Name": "压缩机性能系数(制冷)",
"NameNoUnit": "压缩机性能系数(制冷)",
"EnName": "COPCool",
"Group": "计算",
"MinValue": 0,
"MaxValue": 1000,
"IsMeter": false,
"DecimalPoint": 3,
"Precision": 1,
"Unit": "",
"DataType": "Short",
"RWInfo": "PCCalcu",
"PVModel": {
"Address": "",
"EngValue": 0,
"EngValueStr": "",
"Block": "PV",
"BlockIndex": 0
}
},
{
"Id": 610,
"Name": "容积效率nv[%]",
"NameNoUnit": "容积效率nv",
"EnName": "VoltricEff",
"Group": "计算",
"MinValue": 0,
"MaxValue": 100,
"IsMeter": false,
"DecimalPoint": 2,
"Precision": 1,
"Unit": "%",
"DataType": "Short",
"RWInfo": "PCCalcu",
"PVModel": {
"Address": "",
"EngValue": 0,
"EngValueStr": "",
"Block": "PV",
"BlockIndex": 0
}
}
]
]

View File

@@ -1,4 +1,4 @@
using AutoMapper;
using AutoMapper;
using CapMachine.Core;
using CapMachine.Model;
using CapMachine.Model.CANLIN;
@@ -57,6 +57,7 @@ namespace CapMachine.Wpf.ViewModels
DialogService = dialogService;
EventAggregator.GetEvent<LogicRuleChangeEvent>().Subscribe(LogicRuleChangeEventCall);
EventAggregator.GetEvent<CanLinConfigChangedEvent>().Subscribe(CanLinConfigChangedEventCall);
//数据波特率
DataBaudRateCbxItems = new ObservableCollection<CbxItems>()
@@ -150,6 +151,16 @@ namespace CapMachine.Wpf.ViewModels
//InitWriteRuleCbx();
}
/// <summary>
/// CAN/LIN 配置导入后刷新当前页面。
/// </summary>
/// <param name="msg">事件消息。</param>
private void CanLinConfigChangedEventCall(string msg)
{
InitLoadCanConfigPro();
InitWriteRuleCbx();
}
/// <summary>
/// 初始化写规则下拉框
/// </summary>
@@ -938,18 +949,12 @@ namespace CapMachine.Wpf.ViewModels
/// <exception cref="NotImplementedException"></exception>
private void SchEnableCmdCall(object par)
{
var dd = SelectedCANConfigExdDto.SchEnable;
var Result = (bool)par;
if (Result)
if (SelectedCANConfigExdDto == null)
{
CanDriveService.ToomossCanDrive.SchEnable = true;
return;
}
else
{
CanDriveService.ToomossCanDrive.SchEnable = false;
}
CanDriveService.ToomossCanDrive.SchEnable = SelectedCANConfigExdDto.SchEnable;
}
@@ -1352,22 +1357,22 @@ namespace CapMachine.Wpf.ViewModels
{ "SelectCanLinConfigProId",SelectCanLinConfigPro.Id},
}, (par) =>
{
if (par.Result == ButtonResult.OK)
{
////程序名称
ListCANScheduleConfigDto = par.Parameters.GetValue<ObservableCollection<CANScheduleConfigDto>>("ReturnValue");
//把更新后的最新值给当前的主集合中
SelectCanLinConfigPro.CanScheduleConfigs = Mapper.Map<List<CANScheduleConfig>>(ListCANScheduleConfigDto.ToList());
{
if (par.Result == ButtonResult.OK)
{
////程序名称
ListCANScheduleConfigDto = par.Parameters.GetValue<ObservableCollection<CANScheduleConfigDto>>("ReturnValue");
//把更新后的最新值给当前的主集合中
SelectCanLinConfigPro.CanScheduleConfigs = Mapper.Map<List<CANScheduleConfig>>(ListCANScheduleConfigDto.ToList());
}
else if (par.Result == ButtonResult.Cancel)
{
//取消
}
else if (par.Result == ButtonResult.Cancel)
{
//取消
}
}
});
});
}
else
{

View File

@@ -54,6 +54,9 @@ namespace CapMachine.Wpf.ViewModels
//MachineDataService = machineDataService;
DialogService = dialogService;
EventAggregator.GetEvent<LogicRuleChangeEvent>().Subscribe(LogicRuleChangeEventCall);
EventAggregator.GetEvent<CanLinConfigChangedEvent>().Subscribe(CanLinConfigChangedEventCall);
//仲裁波特率
ArbBaudRateCbxItems = new ObservableCollection<CbxItems>()
{
@@ -168,6 +171,16 @@ namespace CapMachine.Wpf.ViewModels
//InitWriteRuleCbx();
}
/// <summary>
/// CAN/LIN 配置导入后刷新当前页面。
/// </summary>
/// <param name="msg">事件消息。</param>
private void CanLinConfigChangedEventCall(string msg)
{
InitLoadCanConfigPro();
InitWriteRuleCbx();
}
/// <summary>
/// 初始化写规则下拉框
/// </summary>
@@ -200,7 +213,7 @@ namespace CapMachine.Wpf.ViewModels
canLinConfigPros = FreeSql.Select<CanLinConfigPro>().Where(a => a.CANLINInfo == CANLIN.CANFD)
.Include(a => a.CANFdConfigExd)
.IncludeMany(a => a.CanLinConfigContents, then => then.Include(b => b.LogicRule))//,then=> then.Include(b=>b.LogicRule)
//.IncludeMany(a => a.CanLinConfigContents)
.IncludeMany(a => a.CanFdScheduleConfigs)//
.ToList();
ListCanLinConfigPro = new ObservableCollection<CanLinConfigPro>(canLinConfigPros);
@@ -259,6 +272,12 @@ namespace CapMachine.Wpf.ViewModels
ListReadCanLinRWConfigDto = new ObservableCollection<CanLinRWConfigDto>();
}
//调度表配置信息
if (SelectCanLinConfigPro.CanFdScheduleConfigs != null && SelectCanLinConfigPro.CanFdScheduleConfigs.Count() > 0)
{
ListCANFdScheduleConfigDto = new ObservableCollection<CANFdScheduleConfigDto>(Mapper.Map<List<CANFdScheduleConfigDto>>(SelectCanLinConfigPro.CanFdScheduleConfigs));
}
//匹配选中的SelectCanLinConfigPro.CanLinConfigContents和ListCanDbcModel
MatchSeletedAndCanDbcModel();
}
@@ -507,6 +526,8 @@ namespace CapMachine.Wpf.ViewModels
CanFdDriveService.InitCanConfig(SelectCanLinConfigPro);
InitLoadCanConfigPro();
CanFdDriveService.ToomossCanFDDrive.LoadCmdDataToDrive(CanFdDriveService.CmdData);
}
else
{
@@ -624,6 +645,18 @@ namespace CapMachine.Wpf.ViewModels
SelectCanLinConfigProConfigName = SelectCanLinConfigPro.ConfigName;
//调度表配置信息
if (SelectCanLinConfigPro.CanFdScheduleConfigs != null && SelectCanLinConfigPro.CanFdScheduleConfigs.Count() > 0)
{
ListCANFdScheduleConfigDto = new ObservableCollection<CANFdScheduleConfigDto>(Mapper.Map<List<CANFdScheduleConfigDto>>(SelectCanLinConfigPro.CanFdScheduleConfigs));
}
else
{
ListCANFdScheduleConfigDto = new ObservableCollection<CANFdScheduleConfigDto>();
}
return;
}
//先判断是否是正确的集合数据防止DataGrid的数据源刷新导致的触发事件
@@ -693,6 +726,33 @@ namespace CapMachine.Wpf.ViewModels
#endregion
private DelegateCommand _btnTestPLC;
/// <summary>
/// 临时测试的方法
/// </summary>
public DelegateCommand btnTestPLC
{
set
{
_btnTestPLC = value;
}
get
{
if (_btnTestPLC == null)
{
_btnTestPLC = new DelegateCommand(() => btnTestPLCCall());
}
return _btnTestPLC;
}
}
//临时测试的方法
private void btnTestPLCCall()
{
CanFdDriveService.ToomossCanFDDrive.UpdateSchDataByCmdDataChanged();
}
#region Dbc操作
private ObservableCollection<CanDbcModel> _ListCanDbcModel;
@@ -926,6 +986,43 @@ namespace CapMachine.Wpf.ViewModels
set { _ArbBaudRateCbxItems = value; RaisePropertyChanged(); }
}
private DelegateCommand<object> _SchEnableCmd;
/// <summary>
/// 调度表的使能 操作的指令
/// </summary>
public DelegateCommand<object> SchEnableCmd
{
set
{
_SchEnableCmd = value;
}
get
{
if (_SchEnableCmd == null)
{
_SchEnableCmd = new DelegateCommand<object>((Par) => SchEnableCmdCall(Par));
}
return _SchEnableCmd;
}
}
/// <summary>
/// 调度表的使能信息
/// </summary>
/// <param name="par"></param>
/// <exception cref="NotImplementedException"></exception>
private void SchEnableCmdCall(object par)
{
if (SelectedCANConfigExdDto == null)
{
return;
}
CanFdDriveService.ToomossCanFDDrive.SchEnable = SelectedCANConfigExdDto.SchEnable;
}
private DelegateCommand<string> _CanOpCmd;
/// <summary>
/// CAN操作的指令
@@ -1017,6 +1114,7 @@ namespace CapMachine.Wpf.ViewModels
.Set(a => a.ArbBaudRate, SelectedCANConfigExdDto.ArbBaudRate)
.Set(a => a.ISOEnable, SelectedCANConfigExdDto.ISOEnable)
.Set(a => a.ResEnable, SelectedCANConfigExdDto.ResEnable)
.Set(a => a.SchEnable, SelectedCANConfigExdDto.SchEnable)
.Where(a => a.Id == SelectedCANConfigExdDto.Id)
.ExecuteUpdated();
}
@@ -1078,6 +1176,12 @@ namespace CapMachine.Wpf.ViewModels
break;
case "CycleSend"://循环发送你
//有可能加载完毕后不手动改变状态,导致值没有传输,所以这里赋值
CanFdDriveService.ToomossCanFDDrive.SchEnable = SelectedCANConfigExdDto.SchEnable;
//无论有没有调度表都要把这个配置给CanDriveService
CanFdDriveService.ListCANFdScheduleConfig = ListCANFdScheduleConfigDto.ToList();
//循环发送数据
CanFdDriveService.CycleSendMsg();
@@ -1141,6 +1245,26 @@ namespace CapMachine.Wpf.ViewModels
set { _WriteRuleCbxItems = value; RaisePropertyChanged(); }
}
private ObservableCollection<CbxItems> _ReadRuleCbxItems;
/// <summary>
/// 读取的规格集合
/// </summary>
public ObservableCollection<CbxItems> ReadRuleCbxItems
{
get { return _ReadRuleCbxItems; }
set { _ReadRuleCbxItems = value; RaisePropertyChanged(); }
}
private ObservableCollection<CANFdScheduleConfigDto> _ListCANFdScheduleConfigDto;
/// <summary>
///调度表集合
/// </summary>
public ObservableCollection<CANFdScheduleConfigDto> ListCANFdScheduleConfigDto
{
get { return _ListCANFdScheduleConfigDto; }
set { _ListCANFdScheduleConfigDto = value; RaisePropertyChanged(); }
}
//private string _SelectedWriteName;
///// <summary>
///// 选中的写入的Name
@@ -1286,6 +1410,45 @@ namespace CapMachine.Wpf.ViewModels
System.Windows.MessageBox.Show("请选中后再进行【删除】操作?", "提示", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Hand);
}
break;
case "WriteSch"://调度表
var data = CanFdDriveService.CmdData.GroupBy(a => a.MsgName).Select(a => a.Key).ToList();
if (data != null && data.Count > 0)
{
//弹窗
DialogService.ShowDialog("DialogCANFdSchConfigView", new DialogParameters() {
{"ListMsg", CanFdDriveService.CmdData.GroupBy(a=>a.MsgName).Select(a=>a.Key).ToList() },
{ "ListCANFdScheduleConfigDto",ListCANFdScheduleConfigDto},
{ "SelectCanLinConfigProId",SelectCanLinConfigPro.Id},
}, (par) =>
{
if (par.Result == ButtonResult.OK)
{
////程序名称
ListCANFdScheduleConfigDto = par.Parameters.GetValue<ObservableCollection<CANFdScheduleConfigDto>>("ReturnValue");
//把更新后的最新值给当前的主集合中
SelectCanLinConfigPro.CanFdScheduleConfigs = Mapper.Map<List<CANFdScheduleConfig>>(ListCANFdScheduleConfigDto.ToList());
}
else if (par.Result == ButtonResult.Cancel)
{
//取消
}
});
}
else
{
System.Windows.MessageBox.Show("未发现写入的执行的命令数据,无法配置?", "提示", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Hand);
}
break;
default:
break;

View File

@@ -0,0 +1,377 @@
using AutoMapper;
using CapMachine.Core;
using CapMachine.Model.CANLIN;
using CapMachine.Wpf.Dtos;
using Prism.Commands;
using Prism.Services.Dialogs;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace CapMachine.Wpf.ViewModels
{
/// <summary>
/// CAN Fd 调度表配置
/// </summary>
public class DialogCANFdSchConfigViewModel : DialogViewModel
{
/// <summary>
/// 构造函数
/// </summary>
public DialogCANFdSchConfigViewModel(IFreeSql freeSql, IMapper mapper)
{
Title = "调度表 CAN 配置";
FreeSql = freeSql;
Mapper = mapper;
SendOrderCbxItems = new ObservableCollection<CbxItems>()
{
new CbxItems(){
Key="0",
Text="并行",
},
new CbxItems(){
Key="1",
Text="顺序",
},
};
//默认只能用1号调度器
SchTabIndexCbxItems = new ObservableCollection<CbxItems>()
{
new CbxItems(){
Key="0",
Text="0",
},
//new CbxItems(){
// Key="1",
// Text="1",
//},
//new CbxItems(){
// Key="2",
// Text="2",
//},
//new CbxItems(){
// Key="3",
// Text="3",
//},
//new CbxItems(){
// Key="4",
// Text="4",
//},
};
}
public IFreeSql FreeSql { get; }
public IMapper Mapper { get; }
private string name;
/// <summary>
/// 名称
/// </summary>
public string Name
{
get { return name; }
set { name = value; RaisePropertyChanged(); }
}
private ObservableCollection<CANFdScheduleConfigDto> _ListCANFdScheduleConfigDto = new ObservableCollection<CANFdScheduleConfigDto>();
/// <summary>
/// CAN 调度表数据集合
/// </summary>
public ObservableCollection<CANFdScheduleConfigDto> ListCANFdScheduleConfigDto
{
get { return _ListCANFdScheduleConfigDto; }
set { _ListCANFdScheduleConfigDto = value; RaisePropertyChanged(); }
}
/// <summary>
/// 消息/帧报文信息集合
/// </summary>
public List<string> ListMsg { get; set; }
private ObservableCollection<CbxItems> _MsgCbxItems;
/// <summary>
/// 消息名称 集合信息
/// </summary>
public ObservableCollection<CbxItems> MsgCbxItems
{
get { return _MsgCbxItems; }
set { _MsgCbxItems = value; RaisePropertyChanged(); }
}
/// <summary>
/// 选中的程序的Id
/// </summary>
public long SelectCanLinConfigProId { get; set; }
private ObservableCollection<CbxItems> _SendOrderCbxItems;
/// <summary>
/// 发送方式 集合信息
/// </summary>
public ObservableCollection<CbxItems> SendOrderCbxItems
{
get { return _SendOrderCbxItems; }
set { _SendOrderCbxItems = value; RaisePropertyChanged(); }
}
private string? _CurSendOrder;
/// <summary>
/// 当前发送方式
/// </summary>
public string? CurSendOrder
{
get { return _CurSendOrder; }
set { _CurSendOrder = value; RaisePropertyChanged(); }
}
private ObservableCollection<CbxItems> _SchTabIndexCbxItems;
/// <summary>
/// 调度器序号 集合信息
/// </summary>
public ObservableCollection<CbxItems> SchTabIndexCbxItems
{
get { return _SchTabIndexCbxItems; }
set { _SchTabIndexCbxItems = value; RaisePropertyChanged(); }
}
private CANFdScheduleConfigDto _CurSelectedItem;
/// <summary>
/// 选中的数据
/// </summary>
public CANFdScheduleConfigDto CurSelectedItem
{
get { return _CurSelectedItem; }
set { _CurSelectedItem = value; RaisePropertyChanged(); }
}
private DelegateCommand<object> _GridSelectionChangedCmd;
/// <summary>
/// 选中行数据命令
/// </summary>
public DelegateCommand<object> GridSelectionChangedCmd
{
set
{
_GridSelectionChangedCmd = value;
}
get
{
if (_GridSelectionChangedCmd == null)
{
_GridSelectionChangedCmd = new DelegateCommand<object>((par) => GridSelectionChangedCmdMethod(par));
}
return _GridSelectionChangedCmd;
}
}
private void GridSelectionChangedCmdMethod(object par)
{
//先判断是否是正确的集合数据防止DataGrid的数据源刷新导致的触发事件
var Selecteddata = par as CANFdScheduleConfigDto;
if (Selecteddata != null)
{
CurSelectedItem = Selecteddata;
}
}
//OpCmd
private DelegateCommand<string> _OpCmd;
/// <summary>
/// 增加方法命令
/// </summary>
public DelegateCommand<string> OpCmd
{
set
{
_OpCmd = value;
}
get
{
if (_OpCmd == null)
{
_OpCmd = new DelegateCommand<string>((Par) => OpCmdCall(Par));
}
return _OpCmd;
}
}
private void OpCmdCall(string Par)
{
switch (Par)
{
case "Add":
ListCANFdScheduleConfigDto.Add(new CANFdScheduleConfigDto
{
CanLinConfigProId = SelectCanLinConfigProId,
Cycle = 100,
SchTabIndex = 0,
OrderSend = 1,
});
break;
case "Delete":
if (CurSelectedItem != null)
{
//直接删除掉如果没有ID的话这就不需要删除了
FreeSql.Delete<CANFdScheduleConfig>(CurSelectedItem.Id).ExecuteAffrows();
ListCANFdScheduleConfigDto.Remove(CurSelectedItem);
CurSelectedItem = null;
}
else
{
MessageBox.Show("请选中后再进行【删除】操作?", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
}
break;
default:
break;
}
}
private DelegateCommand saveCmd;
/// <summary>
/// 保存命令
/// </summary>
public DelegateCommand SaveCmd
{
set
{
saveCmd = value;
}
get
{
if (saveCmd == null)
{
saveCmd = new DelegateCommand(() => SaveCmdMethod());
}
return saveCmd;
}
}
/// <summary>
/// 保存命令方法
/// </summary>
/// <exception cref="NotImplementedException"></exception>
private void SaveCmdMethod()
{
//检查空的数据
foreach (var item in ListCANFdScheduleConfigDto)
{
//整个的发送方式赋值给每个子项
item.OrderSend = CurSendOrder == "0" ? 0 : 1;
if (string.IsNullOrEmpty(item.MsgName))
{
MessageBox.Show("请确认消息名称是否正确", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
return;
}
if (item.Cycle == 0)
{
MessageBox.Show("请确认周期是否正确", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
return;
}
if (item.OrderSend >= 2)
{
MessageBox.Show("请确认发送方式是否正确", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
return;
}
}
//发送的控制帧都放到同一个调度表中,不需要检查了
////检查重复设置问题
//bool isRepeat = ListCANScheduleConfigDto.GroupBy(i => i.MsgName).Any(g => g.Count() > 1);
//if (isRepeat)
//{
// MessageBox.Show("请确认是否重复设置", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
// return;
//}
//检查数据是否正常
foreach (var item in ListCANFdScheduleConfigDto)
{
FreeSql.InsertOrUpdate<CANFdScheduleConfig>()
.SetSource(Mapper.Map<CANFdScheduleConfig>(item)).
ExecuteAffrows();
}
ListCANFdScheduleConfigDto = new ObservableCollection<CANFdScheduleConfigDto>(Mapper.Map<List<CANFdScheduleConfigDto>>(FreeSql.Select<CANFdScheduleConfig>().Where(a => a.CanLinConfigProId == SelectCanLinConfigProId).ToList()));
DialogParameters pars = new DialogParameters
{
{ "ReturnValue", ListCANFdScheduleConfigDto }
};
RaiseRequestClose(new DialogResult(ButtonResult.OK, pars));
}
private DelegateCommand cancelCmd;
/// <summary>
/// 保存命令
/// </summary>
public DelegateCommand CancelCmd
{
set
{
cancelCmd = value;
}
get
{
if (cancelCmd == null)
{
cancelCmd = new DelegateCommand(() => CancelCmdMethod());
}
return cancelCmd;
}
}
/// <summary>
/// 取消命令方法
/// </summary>
/// <exception cref="NotImplementedException"></exception>
private void CancelCmdMethod()
{
RaiseRequestClose(new DialogResult(ButtonResult.Cancel));
}
/// <summary>
/// 窗口打开时的传递的参数
/// </summary>
/// <param name="parameters"></param>
public override void OnDialogOpened(IDialogParameters parameters)
{
ListMsg = parameters.GetValue<List<string>>("ListMsg");
// 转换为CbxItems集合都是文本内容
MsgCbxItems = new ObservableCollection<CbxItems>(
ListMsg.Select(value => new CbxItems
{
Key = value,
Text = value
}));
ListCANFdScheduleConfigDto = parameters.GetValue<ObservableCollection<CANFdScheduleConfigDto>>("ListCANFdScheduleConfigDto");
//防止返回的数据为空,就无法增加了
if (ListCANFdScheduleConfigDto == null) ListCANFdScheduleConfigDto = new ObservableCollection<CANFdScheduleConfigDto>();
//Name = parameters.GetValue<string>("Name");
if (ListCANFdScheduleConfigDto.Count > 0)
{
CurSendOrder = ListCANFdScheduleConfigDto.FirstOrDefault()!.OrderSend.ToString();
}
SelectCanLinConfigProId = parameters.GetValue<long>("SelectCanLinConfigProId");
}
}
}

View File

@@ -0,0 +1,319 @@
using CapMachine.Core;
using CapMachine.Wpf.PrismEvent;
using CapMachine.Wpf.Services;
using Microsoft.Win32;
using Prism.Commands;
using Prism.Events;
using Prism.Services.Dialogs;
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Windows;
namespace CapMachine.Wpf.ViewModels
{
/// <summary>
/// CAN/CANFD/LIN 配置导入导出弹窗 ViewModel。
/// </summary>
public class DialogCanLinConfigImExportViewModel : DialogViewModel
{
/// <summary>
/// 初始化导入导出弹窗。
/// </summary>
/// <param name="canLinConfigImExportService">导入导出服务。</param>
/// <param name="eventAggregator">事件聚合器。</param>
public DialogCanLinConfigImExportViewModel(
CanLinConfigImExportService canLinConfigImExportService,
IEventAggregator eventAggregator)
{
CanLinConfigImExportService = canLinConfigImExportService;
EventAggregator = eventAggregator;
Title = "CAN/CANFD/LIN配置导入导出";
ImportModes = new ObservableCollection<ImportModeOption>
{
new ImportModeOption { Text = "覆盖导入(同名同类型先删除后导入)", Mode = CanLinImportMode.Overwrite },
new ImportModeOption { Text = "增量导入(同名同类型自动重命名)", Mode = CanLinImportMode.IncrementalRename },
};
SelectedImportMode = ImportModes.FirstOrDefault();
}
/// <summary>
/// 导入导出服务。
/// </summary>
public CanLinConfigImExportService CanLinConfigImExportService { get; }
/// <summary>
/// 事件聚合器。
/// </summary>
public IEventAggregator EventAggregator { get; }
private string _importFilePath = string.Empty;
/// <summary>
/// 导入文件路径。
/// </summary>
public string ImportFilePath
{
get { return _importFilePath; }
set { _importFilePath = value; RaisePropertyChanged(); }
}
private ObservableCollection<ImportModeOption> _importModes = new ObservableCollection<ImportModeOption>();
/// <summary>
/// 导入模式集合。
/// </summary>
public ObservableCollection<ImportModeOption> ImportModes
{
get { return _importModes; }
set { _importModes = value; RaisePropertyChanged(); }
}
private ImportModeOption? _selectedImportMode;
/// <summary>
/// 当前选中的导入模式。
/// </summary>
public ImportModeOption? SelectedImportMode
{
get { return _selectedImportMode; }
set { _selectedImportMode = value; RaisePropertyChanged(); }
}
private string _resultMessage = string.Empty;
/// <summary>
/// 操作结果消息。
/// </summary>
public string ResultMessage
{
get { return _resultMessage; }
set { _resultMessage = value; RaisePropertyChanged(); }
}
private DelegateCommand? _browseImportFileCmd;
/// <summary>
/// 选择导入文件命令。
/// </summary>
public DelegateCommand BrowseImportFileCmd
{
get
{
if (_browseImportFileCmd == null)
{
_browseImportFileCmd = new DelegateCommand(BrowseImportFile);
}
return _browseImportFileCmd;
}
}
private DelegateCommand? _importCmd;
/// <summary>
/// 导入命令。
/// </summary>
public DelegateCommand ImportCmd
{
get
{
if (_importCmd == null)
{
_importCmd = new DelegateCommand(ImportConfig);
}
return _importCmd;
}
}
private DelegateCommand? _exportCmd;
/// <summary>
/// 导出命令。
/// </summary>
public DelegateCommand ExportCmd
{
get
{
if (_exportCmd == null)
{
_exportCmd = new DelegateCommand(ExportConfig);
}
return _exportCmd;
}
}
private DelegateCommand? _closeCmd;
/// <summary>
/// 关闭命令。
/// </summary>
public DelegateCommand CloseCmd
{
get
{
if (_closeCmd == null)
{
_closeCmd = new DelegateCommand(CloseDialog);
}
return _closeCmd;
}
}
/// <summary>
/// 打开文件选择框并选择导入文件。
/// </summary>
private void BrowseImportFile()
{
try
{
var openFileDialog = new OpenFileDialog
{
Filter = "JSON文件 (*.json)|*.json|所有文件 (*.*)|*.*",
CheckFileExists = true,
CheckPathExists = true,
};
var ok = openFileDialog.ShowDialog();
if (ok == true)
{
ImportFilePath = openFileDialog.FileName;
}
}
catch (Exception ex)
{
MessageBox.Show($"选择导入文件失败:{ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
/// <summary>
/// 执行配置导入。
/// </summary>
private void ImportConfig()
{
try
{
if (string.IsNullOrWhiteSpace(ImportFilePath))
{
MessageBox.Show("请先选择导入文件", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
if (SelectedImportMode == null)
{
MessageBox.Show("请选择导入模式", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
var result = CanLinConfigImExportService.ImportFromFile(ImportFilePath, SelectedImportMode.Mode);
ResultMessage = BuildResultText(result);
if (!result.IsSuccess)
{
MessageBox.Show(ResultMessage, "导入失败", MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
EventAggregator.GetEvent<CanLinConfigChangedEvent>().Publish("import");
EventAggregator.GetEvent<LogicRuleChangeEvent>().Publish("import");
MessageBox.Show(ResultMessage, "导入成功", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
ResultMessage = $"导入失败:{ex.Message}";
MessageBox.Show(ResultMessage, "导入失败", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
/// <summary>
/// 执行配置导出。
/// </summary>
private void ExportConfig()
{
try
{
var saveFileDialog = new SaveFileDialog
{
Filter = "JSON文件 (*.json)|*.json|所有文件 (*.*)|*.*",
DefaultExt = "json",
FileName = $"CanLinConfigExport_{DateTime.Now:yyyyMMddHHmmss}.json",
};
var ok = saveFileDialog.ShowDialog();
if (ok != true)
{
return;
}
var result = CanLinConfigImExportService.ExportToFile(saveFileDialog.FileName);
ResultMessage = BuildResultText(result);
if (!result.IsSuccess)
{
MessageBox.Show(ResultMessage, "导出失败", MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
MessageBox.Show(ResultMessage, "导出成功", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
ResultMessage = $"导出失败:{ex.Message}";
MessageBox.Show(ResultMessage, "导出失败", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
/// <summary>
/// 关闭弹窗。
/// </summary>
private void CloseDialog()
{
RaiseRequestClose(new DialogResult(ButtonResult.Cancel));
}
/// <summary>
/// 构建结果文本。
/// </summary>
/// <param name="result">执行结果。</param>
/// <returns>显示文本。</returns>
private static string BuildResultText(CanLinConfigImExportResult result)
{
var builder = new StringBuilder();
builder.AppendLine(result.Message);
if (result.RenamedConfigs != null && result.RenamedConfigs.Count > 0)
{
builder.AppendLine("重命名明细:");
foreach (var line in result.RenamedConfigs)
{
builder.AppendLine($"- {line}");
}
}
return builder.ToString();
}
/// <summary>
/// 弹窗打开回调。
/// </summary>
/// <param name="parameters">弹窗参数。</param>
public override void OnDialogOpened(IDialogParameters parameters)
{
ResultMessage = string.Empty;
}
/// <summary>
/// 导入模式选项。
/// </summary>
public class ImportModeOption
{
/// <summary>
/// 文本。
/// </summary>
public string Text { get; set; } = string.Empty;
/// <summary>
/// 模式值。
/// </summary>
public CanLinImportMode Mode { get; set; }
}
}
}

View File

@@ -0,0 +1,358 @@
using AutoMapper;
using CapMachine.Core;
using CapMachine.Model.CANLIN;
using CapMachine.Wpf.Dtos;
using Prism.Commands;
using Prism.Services.Dialogs;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
namespace CapMachine.Wpf.ViewModels
{
/// <summary>
/// ZLG CAN FD 调度表配置弹窗 ViewModel。
/// 说明:
/// - 调度表项本身由“读写设置”弹窗中的“加入定时调度表”能力生成,这里主要负责:
/// 1) 设置发送方式(并行/顺序)
/// 2) 配置周期Cycle
/// 3) 删除与保存落库
/// - 保存时会使用 InsertOrUpdate 做 Upsert并在保存完成后从数据库重载作为权威数据再回传给调用方。
/// </summary>
public class DialogZlgCANFDSchConfigViewModel : DialogViewModel
{
/// <summary>
/// 构造函数。
/// </summary>
/// <param name="freeSql">FreeSql。</param>
/// <param name="mapper">AutoMapper。</param>
public DialogZlgCANFDSchConfigViewModel(IFreeSql freeSql, IMapper mapper)
{
Title = "调度表 CANFD 配置";
FreeSql = freeSql;
Mapper = mapper;
SendOrderCbxItems = new ObservableCollection<CbxItems>()
{
new CbxItems(){
Key="0",
Text="并行",
},
new CbxItems(){
Key="1",
Text="顺序",
},
};
SchTabIndexCbxItems = new ObservableCollection<CbxItems>()
{
new CbxItems(){
Key="0",
Text="0",
},
};
}
/// <summary>
/// FreeSql。
/// </summary>
public IFreeSql FreeSql { get; }
/// <summary>
/// AutoMapper。
/// </summary>
public IMapper Mapper { get; }
private ObservableCollection<CANFdScheduleConfigDto> _listCANFdScheduleConfigDto = new ObservableCollection<CANFdScheduleConfigDto>();
/// <summary>
/// CAN FD 调度表数据集合。
/// </summary>
public ObservableCollection<CANFdScheduleConfigDto> ListCANFdScheduleConfigDto
{
get { return _listCANFdScheduleConfigDto; }
set { _listCANFdScheduleConfigDto = value; RaisePropertyChanged(); }
}
/// <summary>
/// 消息名列表。
/// </summary>
public List<string>? ListMsg { get; set; }
private ObservableCollection<CbxItems> _msgCbxItems;
/// <summary>
/// 消息名称下拉框数据。
/// </summary>
public ObservableCollection<CbxItems> MsgCbxItems
{
get { return _msgCbxItems; }
set { _msgCbxItems = value; RaisePropertyChanged(); }
}
/// <summary>
/// 选中的程序 Id。
/// </summary>
public long SelectCanLinConfigProId { get; set; }
private ObservableCollection<CbxItems> _sendOrderCbxItems;
/// <summary>
/// 发送方式集合。
/// </summary>
public ObservableCollection<CbxItems> SendOrderCbxItems
{
get { return _sendOrderCbxItems; }
set { _sendOrderCbxItems = value; RaisePropertyChanged(); }
}
private string? _curSendOrder;
/// <summary>
/// 当前发送方式。
/// </summary>
public string? CurSendOrder
{
get { return _curSendOrder; }
set { _curSendOrder = value; RaisePropertyChanged(); }
}
private ObservableCollection<CbxItems> _schTabIndexCbxItems;
/// <summary>
/// 调度器序号集合。
/// </summary>
public ObservableCollection<CbxItems> SchTabIndexCbxItems
{
get { return _schTabIndexCbxItems; }
set { _schTabIndexCbxItems = value; RaisePropertyChanged(); }
}
private CANFdScheduleConfigDto _curSelectedItem;
/// <summary>
/// 当前选中项。
/// </summary>
public CANFdScheduleConfigDto CurSelectedItem
{
get { return _curSelectedItem; }
set { _curSelectedItem = value; RaisePropertyChanged(); }
}
private DelegateCommand<object> _gridSelectionChangedCmd;
/// <summary>
/// DataGrid 选中行变化。
/// </summary>
public DelegateCommand<object> GridSelectionChangedCmd
{
get
{
if (_gridSelectionChangedCmd == null)
{
_gridSelectionChangedCmd = new DelegateCommand<object>(GridSelectionChangedCmdMethod);
}
return _gridSelectionChangedCmd;
}
}
/// <summary>
/// 选中行变化处理。
/// </summary>
/// <param name="par">选中项。</param>
private void GridSelectionChangedCmdMethod(object par)
{
var selected = par as CANFdScheduleConfigDto;
if (selected != null)
{
CurSelectedItem = selected;
}
}
private DelegateCommand<string> _opCmd;
/// <summary>
/// 操作命令(删除)。
/// </summary>
public DelegateCommand<string> OpCmd
{
get
{
if (_opCmd == null)
{
_opCmd = new DelegateCommand<string>(OpCmdCall);
}
return _opCmd;
}
}
/// <summary>
/// 操作命令处理。
/// </summary>
/// <param name="par">参数。</param>
private void OpCmdCall(string par)
{
switch (par)
{
case "Add":
MessageBox.Show("调度表项由【读写设置】中的写入配置通过“加入定时调度表”生成,这里不支持新增。", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
break;
case "Delete":
if (CurSelectedItem != null)
{
FreeSql.Delete<CANFdScheduleConfig>(CurSelectedItem.Id).ExecuteAffrows();
ListCANFdScheduleConfigDto.Remove(CurSelectedItem);
CurSelectedItem = null;
}
else
{
MessageBox.Show("请选中后再进行【删除】操作?", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
}
break;
default:
break;
}
}
private DelegateCommand _saveCmd;
/// <summary>
/// 保存命令。
/// </summary>
public DelegateCommand SaveCmd
{
get
{
if (_saveCmd == null)
{
_saveCmd = new DelegateCommand(SaveCmdMethod);
}
return _saveCmd;
}
}
/// <summary>
/// 保存命令处理。
/// </summary>
private void SaveCmdMethod()
{
// 校验:确保每个调度项具备最小可用字段,避免落库无效配置。
foreach (var item in ListCANFdScheduleConfigDto)
{
// UI 上的“发送方式”是全局下拉框,这里同步写回每个子项,确保落库数据一致。
item.OrderSend = CurSendOrder == "0" ? 0 : 1;
if (string.IsNullOrEmpty(item.MsgName))
{
MessageBox.Show("消息名为空:请在【读写设置】-> 写入配置中选择报文后点击“加入定时调度表”,再回到此处设置周期。", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
return;
}
if (item.Cycle <= 0)
{
MessageBox.Show("请确认周期是否正确", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
return;
}
if (item.OrderSend >= 2)
{
MessageBox.Show("请确认发送方式是否正确", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
return;
}
}
foreach (var item in ListCANFdScheduleConfigDto)
{
// Upsert逐项落库。
FreeSql.InsertOrUpdate<CANFdScheduleConfig>()
.SetSource(Mapper.Map<CANFdScheduleConfig>(item))
.ExecuteAffrows();
}
// 保存完成后重载:
// - 避免 UI 集合与数据库实际数据不一致;
// - 同时让新增项获取真实 Id。
ListCANFdScheduleConfigDto = new ObservableCollection<CANFdScheduleConfigDto>(
Mapper.Map<List<CANFdScheduleConfigDto>>(
FreeSql.Select<CANFdScheduleConfig>()
.Where(a => a.CanLinConfigProId == SelectCanLinConfigProId)
.ToList()));
DialogParameters pars = new DialogParameters
{
{ "ReturnValue", ListCANFdScheduleConfigDto }
};
RaiseRequestClose(new DialogResult(ButtonResult.OK, pars));
}
private DelegateCommand _cancelCmd;
/// <summary>
/// 取消命令。
/// </summary>
public DelegateCommand CancelCmd
{
get
{
if (_cancelCmd == null)
{
_cancelCmd = new DelegateCommand(CancelCmdMethod);
}
return _cancelCmd;
}
}
/// <summary>
/// 取消命令处理。
/// </summary>
private void CancelCmdMethod()
{
RaiseRequestClose(new DialogResult(ButtonResult.Cancel));
}
/// <summary>
/// 弹窗打开时参数接收并初始化 UI。
/// 说明:
/// - ListMsg用于构造消息名下拉框候选
/// - ListCANFdScheduleConfigDto用于回显当前调度表
/// - SelectCanLinConfigProId用于 Save 后按配置程序 id 重载。
/// </summary>
/// <param name="parameters">参数。</param>
public override void OnDialogOpened(IDialogParameters parameters)
{
if (parameters.ContainsKey("ListMsg"))
{
ListMsg = parameters.GetValue<List<string>>("ListMsg");
if (ListMsg != null)
{
// 转换为 CbxItems 集合Key/Text 都使用消息名)。
MsgCbxItems = new ObservableCollection<CbxItems>(
ListMsg.Select(value => new CbxItems
{
Key = value,
Text = value
}));
}
}
ListCANFdScheduleConfigDto = parameters.GetValue<ObservableCollection<CANFdScheduleConfigDto>>("ListCANFdScheduleConfigDto");
if (ListCANFdScheduleConfigDto == null) ListCANFdScheduleConfigDto = new ObservableCollection<CANFdScheduleConfigDto>();
if (ListCANFdScheduleConfigDto.Count > 0)
{
// 回显:默认取第一条的 OrderSend。
CurSendOrder = ListCANFdScheduleConfigDto.FirstOrDefault()!.OrderSend.ToString();
}
else
{
// 默认:顺序发送。
CurSendOrder = "1";
}
SelectCanLinConfigProId = parameters.GetValue<long>("SelectCanLinConfigProId");
}
}
}

View File

@@ -0,0 +1,402 @@
using AutoMapper;
using CapMachine.Core;
using CapMachine.Model;
using CapMachine.Model.CANLIN;
using CapMachine.Wpf.Dtos;
using CapMachine.Wpf.Services;
using ImTools;
using Prism.Commands;
using Prism.Services.Dialogs;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace CapMachine.Wpf.ViewModels
{
/// <summary>
/// ZLG CAN 调度表配置
/// </summary>
public class DialogZlgCANSchConfigViewModel : DialogViewModel
{
/// <summary>
/// 构造函数
/// </summary>
public DialogZlgCANSchConfigViewModel(IFreeSql freeSql, IMapper mapper)
{
Title = "调度表 CAN 配置";
FreeSql = freeSql;
Mapper = mapper;
SendOrderCbxItems = new ObservableCollection<CbxItems>()
{
new CbxItems(){
Key="0",
Text="并行",
},
new CbxItems(){
Key="1",
Text="顺序",
},
};
//默认只能用1号调度器
SchTabIndexCbxItems = new ObservableCollection<CbxItems>()
{
new CbxItems(){
Key="0",
Text="0",
},
//new CbxItems(){
// Key="1",
// Text="1",
//},
//new CbxItems(){
// Key="2",
// Text="2",
//},
//new CbxItems(){
// Key="3",
// Text="3",
//},
//new CbxItems(){
// Key="4",
// Text="4",
//},
};
}
/// <summary>
/// FreeSql用于调度表项的删除/保存落库)。
/// </summary>
public IFreeSql FreeSql { get; }
/// <summary>
/// AutoMapperDTO 与实体映射)。
/// </summary>
public IMapper Mapper { get; }
private string name;
/// <summary>
/// 名称
/// </summary>
public string Name
{
get { return name; }
set { name = value; RaisePropertyChanged(); }
}
private ObservableCollection<CANScheduleConfigDto> _ListCANScheduleConfigDto = new ObservableCollection<CANScheduleConfigDto>();
/// <summary>
/// CAN 调度表数据集合
/// </summary>
public ObservableCollection<CANScheduleConfigDto> ListCANScheduleConfigDto
{
get { return _ListCANScheduleConfigDto; }
set { _ListCANScheduleConfigDto = value; RaisePropertyChanged(); }
}
/// <summary>
/// 消息/帧报文信息集合
/// </summary>
public List<string>? ListMsg { get; set; }
private ObservableCollection<CbxItems> _MsgCbxItems;
/// <summary>
/// 消息名称 集合信息
/// </summary>
public ObservableCollection<CbxItems> MsgCbxItems
{
get { return _MsgCbxItems; }
set { _MsgCbxItems = value; RaisePropertyChanged(); }
}
/// <summary>
/// 选中的程序的Id
/// </summary>
public long SelectCanLinConfigProId { get; set; }
private ObservableCollection<CbxItems> _SendOrderCbxItems;
/// <summary>
/// 发送方式 集合信息
/// </summary>
public ObservableCollection<CbxItems> SendOrderCbxItems
{
get { return _SendOrderCbxItems; }
set { _SendOrderCbxItems = value; RaisePropertyChanged(); }
}
private string? _CurSendOrder;
/// <summary>
/// 当前发送方式
/// </summary>
public string? CurSendOrder
{
get { return _CurSendOrder; }
set { _CurSendOrder = value; RaisePropertyChanged(); }
}
private ObservableCollection<CbxItems> _SchTabIndexCbxItems;
/// <summary>
/// 调度器序号 集合信息
/// </summary>
public ObservableCollection<CbxItems> SchTabIndexCbxItems
{
get { return _SchTabIndexCbxItems; }
set { _SchTabIndexCbxItems = value; RaisePropertyChanged(); }
}
private CANScheduleConfigDto _CurSelectedItem;
/// <summary>
/// 当前选中的调度表项(用于删除等操作)。
/// </summary>
public CANScheduleConfigDto CurSelectedItem
{
get { return _CurSelectedItem; }
set { _CurSelectedItem = value; RaisePropertyChanged(); }
}
private DelegateCommand<object> _GridSelectionChangedCmd;
/// <summary>
/// DataGrid 选中行变化。
/// </summary>
public DelegateCommand<object> GridSelectionChangedCmd
{
set
{
_GridSelectionChangedCmd = value;
}
get
{
if (_GridSelectionChangedCmd == null)
{
_GridSelectionChangedCmd = new DelegateCommand<object>((par) => GridSelectionChangedCmdMethod(par));
}
return _GridSelectionChangedCmd;
}
}
private void GridSelectionChangedCmdMethod(object par)
{
// 先判断是否是正确的集合数据,防止 DataGrid 的 ItemsSource 刷新导致的误触发。
var Selecteddata = par as CANScheduleConfigDto;
if (Selecteddata != null)
{
CurSelectedItem = Selecteddata;
}
}
private DelegateCommand<string> _OpCmd;
/// <summary>
/// 操作命令(删除)。
/// 说明:新增调度表项由“读写设置”弹窗内的“加入定时调度表”生成,此处仅允许删除与保存。
/// </summary>
public DelegateCommand<string> OpCmd
{
set
{
_OpCmd = value;
}
get
{
if (_OpCmd == null)
{
_OpCmd = new DelegateCommand<string>((Par) => OpCmdCall(Par));
}
return _OpCmd;
}
}
private void OpCmdCall(string Par)
{
switch (Par)
{
case "Add":
MessageBox.Show("调度表项由【读写设置】中的写入配置通过“加入定时调度表”生成,这里不支持新增。", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
break;
case "Delete":
if (CurSelectedItem != null)
{
// 直接落库删除(无论 UI 是否还有引用,都以数据库为准)。
FreeSql.Delete<CANScheduleConfig>(CurSelectedItem.Id).ExecuteAffrows();
ListCANScheduleConfigDto.Remove(CurSelectedItem);
CurSelectedItem = null;
}
else
{
MessageBox.Show("请选中后再进行【删除】操作?", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
}
break;
default:
break;
}
}
private DelegateCommand saveCmd;
/// <summary>
/// 保存命令
/// </summary>
public DelegateCommand SaveCmd
{
set
{
saveCmd = value;
}
get
{
if (saveCmd == null)
{
saveCmd = new DelegateCommand(() => SaveCmdMethod());
}
return saveCmd;
}
}
/// <summary>
/// 保存命令方法。
/// 说明:
/// - 先做必要校验MsgName/Cycle/发送方式等),避免写入无效配置;
/// - 使用 InsertOrUpdate 保证“新增/修改”统一处理;
/// - 保存完成后重新从数据库加载,作为最终权威数据,并通过 DialogResult 回传给调用方。
/// </summary>
private void SaveCmdMethod()
{
// 校验:确保每个调度项具备最小可用字段。
foreach (var item in ListCANScheduleConfigDto)
{
// UI 上的“发送方式”是全局下拉框,这里同步写回到每个子项,确保落库数据一致。
item.OrderSend = CurSendOrder == "0" ? 0 : 1;
if (string.IsNullOrEmpty(item.MsgName))
{
MessageBox.Show("消息名为空:请在【读写设置】-> 写入配置中选择报文后点击“加入定时调度表”,再回到此处设置周期。", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
return;
}
if (item.Cycle <= 0)
{
MessageBox.Show("请确认周期是否正确", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
return;
}
if (item.OrderSend >= 2)
{
MessageBox.Show("请确认发送方式是否正确", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
return;
}
}
//发送的控制帧都放到同一个调度表中,不需要检查了
////检查重复设置问题
//bool isRepeat = ListCANScheduleConfigDto.GroupBy(i => i.MsgName).Any(g => g.Count() > 1);
//if (isRepeat)
//{
// MessageBox.Show("请确认是否重复设置", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
// return;
//}
// Upsert逐项落库。
foreach (var item in ListCANScheduleConfigDto)
{
FreeSql.InsertOrUpdate<CANScheduleConfig>()
.SetSource(Mapper.Map<CANScheduleConfig>(item)).
ExecuteAffrows();
}
// 保存完成后重新加载:
// - 防止 UI 缓存与数据库实际数据不一致;
// - 同时也让新增项拿到真实 Id。
ListCANScheduleConfigDto = new ObservableCollection<CANScheduleConfigDto>(Mapper.Map<List<CANScheduleConfigDto>>(FreeSql.Select<CANScheduleConfig>().Where(a => a.CanLinConfigProId == SelectCanLinConfigProId).ToList()));
DialogParameters pars = new DialogParameters
{
{ "ReturnValue", ListCANScheduleConfigDto }
};
RaiseRequestClose(new DialogResult(ButtonResult.OK, pars));
}
private DelegateCommand cancelCmd;
/// <summary>
/// 取消命令。
/// </summary>
public DelegateCommand CancelCmd
{
set
{
cancelCmd = value;
}
get
{
if (cancelCmd == null)
{
cancelCmd = new DelegateCommand(() => CancelCmdMethod());
}
return cancelCmd;
}
}
/// <summary>
/// 取消命令方法。
/// </summary>
private void CancelCmdMethod()
{
RaiseRequestClose(new DialogResult(ButtonResult.Cancel));
}
/// <summary>
/// 弹窗打开时接收参数并初始化 UI。
/// 说明:
/// - ListMsg 用于构造消息名下拉框;
/// - ListCANScheduleConfigDto 用于回显现有调度表;
/// - SelectCanLinConfigProId 用于 Save 时按配置程序 id 过滤重载。
/// </summary>
/// <param name="parameters"></param>
public override void OnDialogOpened(IDialogParameters parameters)
{
if (parameters.ContainsKey("ListMsg"))
{
ListMsg = parameters.GetValue<List<string>>("ListMsg");
if (ListMsg != null)
{
// 转换为CbxItems集合都是文本内容
MsgCbxItems = new ObservableCollection<CbxItems>(
ListMsg.Select(value => new CbxItems
{
Key = value,
Text = value
}));
}
}
ListCANScheduleConfigDto = parameters.GetValue<ObservableCollection<CANScheduleConfigDto>>("ListCANScheduleConfigDto");
//防止返回的数据为空,就无法增加了
if (ListCANScheduleConfigDto == null) ListCANScheduleConfigDto = new ObservableCollection<CANScheduleConfigDto>();
//Name = parameters.GetValue<string>("Name");
if (ListCANScheduleConfigDto.Count > 0)
{
// 回显:默认取第一条的 OrderSend 作为当前下拉框选择。
CurSendOrder = ListCANScheduleConfigDto.FirstOrDefault()!.OrderSend.ToString();
}
else
{
// 默认:顺序发送。
CurSendOrder = "1";
}
SelectCanLinConfigProId = parameters.GetValue<long>("SelectCanLinConfigProId");
}
}
}

View File

@@ -0,0 +1,904 @@
using CapMachine.Core;
using CapMachine.Model.CANLIN;
using CapMachine.Wpf.Dtos;
using CapMachine.Wpf.Services;
using FreeSql;
using Prism.Commands;
using Prism.Services.Dialogs;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Data;
namespace CapMachine.Wpf.ViewModels
{
/// <summary>
/// ZLG CAN/LIN 读写配置三栏管理弹窗 ViewModel。
/// 左侧:写入/读取配置;右侧:信号全集候选池;统一保存落库。
/// </summary>
public class DialogZlgCanLinRwConfigViewModel : DialogViewModel
{
private readonly IFreeSql _freeSql;
private readonly ILogService _logService;
private readonly LogicRuleService _logicRuleService;
private long _canLinConfigProId;
private bool _enableHardwareCycleSchedule = true;
private bool _useCanFdSchedule;
/// <summary>
/// 是否启用“加入定时调度表ZLG auto_send”能力。
/// 说明:该能力仅适用于 ZLG CAN/CANFD 硬件 auto_sendLIN 当前使用软件调度,不允许写入 CANScheduleConfig。
/// </summary>
public bool EnableHardwareCycleSchedule
{
get { return _enableHardwareCycleSchedule; }
private set
{
_enableHardwareCycleSchedule = value;
RaisePropertyChanged();
RaisePropertyChanged(nameof(CanAddCycleTimeSch));
}
}
/// <summary>
/// 是否允许执行“加入定时调度表”操作。
/// 说明:
/// - 需要 <see cref="EnableHardwareCycleSchedule"/> 打开(调用方允许硬件调度表能力);
/// - 且弹窗处于可编辑态(<see cref="IsEditable"/>)。
/// </summary>
public bool CanAddCycleTimeSch
{
get { return EnableHardwareCycleSchedule && IsEditable; }
}
/// <summary>
/// 是否使用 CANFD 调度表(决定“加入定时调度表”写入 <see cref="CANFdScheduleConfig"/> 还是 <see cref="CANScheduleConfig"/>)。
/// </summary>
public bool UseCanFdSchedule
{
get { return _useCanFdSchedule; }
private set { _useCanFdSchedule = value; RaisePropertyChanged(); }
}
/// <summary>
/// 构造函数。
/// </summary>
/// <param name="freeSql">FreeSql。</param>
/// <param name="logService">日志。</param>
/// <param name="logicRuleService">逻辑规则服务。</param>
public DialogZlgCanLinRwConfigViewModel(IFreeSql freeSql, ILogService logService, LogicRuleService logicRuleService)
{
_freeSql = freeSql;
_logService = logService;
_logicRuleService = logicRuleService;
Title = "读写设置";
WriteConfigs = new ObservableCollection<CanLinRWConfigDto>();
ReadConfigs = new ObservableCollection<CanLinRWConfigDto>();
SignalCandidates = new ObservableCollection<SignalCandidate>();
SignalTree = new ObservableCollection<SignalFrameNode>();
SignalCandidatesView = CollectionViewSource.GetDefaultView(SignalCandidates);
SignalCandidatesView.Filter = FilterSignalCandidate;
WriteNameCbxItems = new ObservableCollection<CbxItems>()
{
new CbxItems(){ Key="转速",Text="转速"},
new CbxItems(){ Key="功率限制",Text="功率限制"},
new CbxItems(){ Key="使能",Text="使能"},
new CbxItems(){ Key="Anti_Sleep",Text="Anti_Sleep"},
new CbxItems(){ Key="PTC使能",Text="PTC使能"},
new CbxItems(){ Key="PTC功率",Text="PTC功率"},
new CbxItems(){ Key="PTC水流量",Text="PTC水流量"},
new CbxItems(){ Key="PTC水温",Text="PTC水温"},
};
ReadNameCbxItems = new ObservableCollection<CbxItems>()
{
new CbxItems(){ Key="通讯Cmp转速",Text="通讯Cmp转速"},
new CbxItems(){ Key="通讯Cmp母线电压",Text="通讯Cmp母线电压"},
new CbxItems(){ Key="通讯Cmp母线电流",Text="通讯Cmp母线电流"},
new CbxItems(){ Key="通讯Cmp逆变器温度",Text="通讯Cmp逆变器温度"},
new CbxItems(){ Key="通讯Cmp相电流",Text="通讯Cmp相电流"},
new CbxItems(){ Key="通讯Cmp功率",Text="通讯Cmp功率"},
new CbxItems(){ Key="通讯Cmp芯片温度",Text="通讯Cmp芯片温度"},
new CbxItems(){ Key="通讯PTC入水温度",Text="通讯PTC入水温度"},
new CbxItems(){ Key="通讯PTC出水温度",Text="通讯PTC出水温度"},
new CbxItems(){ Key="通讯PTC峰值电流",Text="通讯PTC峰值电流"},
new CbxItems(){ Key="通讯PTC母线电流",Text="通讯PTC母线电流"},
new CbxItems(){ Key="通讯PTC膜温",Text="通讯PTC膜温"},
new CbxItems(){ Key="通讯PTC模块温度",Text="通讯PTC模块温度"},
};
IsEditable = true;
}
/// <summary>
/// 是否允许编辑(由调用方根据 Active/打开状态决定)。
/// </summary>
public bool IsEditable { get; private set; }
/// <summary>
/// 逻辑规则集合(下拉框 ItemsSource
/// </summary>
public IReadOnlyList<LogicRuleDto> LogicRuleDtos => _logicRuleService.LogicRuleDtos;
/// <summary>
/// 写入配置“名称”下拉框集合(参考 CANConfigViewModel
/// </summary>
public ObservableCollection<CbxItems> WriteNameCbxItems { get; private set; }
/// <summary>
/// 读取配置“名称”下拉框集合(参考 CANConfigViewModel
/// </summary>
public ObservableCollection<CbxItems> ReadNameCbxItems { get; private set; }
/// <summary>
/// 写入配置集合。
/// </summary>
public ObservableCollection<CanLinRWConfigDto> WriteConfigs { get; private set; }
/// <summary>
/// 读取配置集合。
/// </summary>
public ObservableCollection<CanLinRWConfigDto> ReadConfigs { get; private set; }
/// <summary>
/// 信号候选集合(右侧池)。
/// </summary>
public ObservableCollection<SignalCandidate> SignalCandidates { get; private set; }
/// <summary>
/// 信号树(按帧分组)。
/// </summary>
public ObservableCollection<SignalFrameNode> SignalTree { get; private set; }
/// <summary>
/// 候选信号视图(含过滤)。
/// </summary>
public ICollectionView SignalCandidatesView { get; private set; }
private string? _signalFilterText;
/// <summary>
/// 信号过滤文本(按 MsgName/SignalName/Name/Desc 匹配)。
/// </summary>
public string? SignalFilterText
{
get { return _signalFilterText; }
set
{
_signalFilterText = value;
RaisePropertyChanged();
SignalCandidatesView.Refresh();
RebuildSignalTree();
}
}
/// <summary>
/// 当前选中的候选信号。
/// </summary>
public SignalCandidate? SelectedSignalCandidate { get; set; }
/// <summary>
/// 当前选中的写入配置行。
/// </summary>
public CanLinRWConfigDto? SelectedWriteConfig { get; set; }
private DelegateCommand? _addCycleTimeSch;
/// <summary>
/// 将当前选中的写入配置对应的报文加入“硬件定时调度表”ZLG auto_send
/// </summary>
/// <remarks>
/// 说明:
/// - 调度表按“报文/帧MsgFrameName”维度配置同一报文可包含多个写入信号但调度表只需要一条。
/// - 该命令会直接落库到 <see cref="CANScheduleConfig"/>,随后由主界面刷新配置程序时加载显示。
/// </remarks>
public DelegateCommand AddCycleTimeSch => _addCycleTimeSch ??= new DelegateCommand(AddCycleTimeSchMethod);
/// <summary>
/// 加入定时调度表命令处理。
/// </summary>
private void AddCycleTimeSchMethod()
{
if (!EnableHardwareCycleSchedule)
{
MessageBox.Show("当前模式不支持加入硬件定时调度表LIN 使用软件调度表)", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
return;
}
if (!IsEditable)
{
MessageBox.Show("当前状态禁止修改", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
return;
}
if (_canLinConfigProId <= 0)
{
MessageBox.Show("配置程序ID无效无法加入调度表", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
return;
}
if (SelectedWriteConfig == null)
{
MessageBox.Show("请先选中写入配置中的一行", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
return;
}
if (string.IsNullOrWhiteSpace(SelectedWriteConfig.MsgFrameName))
{
MessageBox.Show("写入配置的【消息名称】为空,无法加入调度表", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
return;
}
var msgName = SelectedWriteConfig.MsgFrameName.Trim();
try
{
var exists = UseCanFdSchedule
? _freeSql.Select<CANFdScheduleConfig>()
.Where(a => a.CanLinConfigProId == _canLinConfigProId)
.Where(a => a.MsgName == msgName)
.Any()
: _freeSql.Select<CANScheduleConfig>()
.Where(a => a.CanLinConfigProId == _canLinConfigProId)
.Where(a => a.MsgName == msgName)
.Any();
if (exists)
{
MessageBox.Show($"该报文已在定时调度表中:{msgName}", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
if (UseCanFdSchedule)
{
_freeSql.Insert<CANFdScheduleConfig>(new CANFdScheduleConfig
{
CanLinConfigProId = _canLinConfigProId,
MsgName = msgName,
Cycle = 100,
OrderSend = 1,
SchTabIndex = 0,
}).ExecuteAffrows();
}
else
{
_freeSql.Insert<CANScheduleConfig>(new CANScheduleConfig
{
CanLinConfigProId = _canLinConfigProId,
MsgName = msgName,
Cycle = 100,
OrderSend = 1,
SchTabIndex = 0,
}).ExecuteAffrows();
}
MessageBox.Show($"已加入定时调度表:{msgName}(默认周期 100ms可在调度表中修改", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
_logService.Error($"加入定时调度表失败:{msgName}{ex}");
MessageBox.Show(ex.Message, "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
/// <summary>
/// 当前选中的读取配置行。
/// </summary>
public CanLinRWConfigDto? SelectedReadConfig { get; set; }
private DelegateCommand<object>? _signalTreeSelectionChangedCmd;
/// <summary>
/// 右侧信号树选中变化(仅当选中叶子节点时回写 SelectedSignalCandidate
/// </summary>
public DelegateCommand<object> SignalTreeSelectionChangedCmd =>
_signalTreeSelectionChangedCmd ??= new DelegateCommand<object>(SignalTreeSelectionChangedCmdMethod);
private void SignalTreeSelectionChangedCmdMethod(object par)
{
if (par is SignalCandidate leaf)
{
SelectedSignalCandidate = leaf;
RaisePropertyChanged(nameof(SelectedSignalCandidate));
return;
}
if (par is SignalFrameNode)
{
// 选中父节点时不变更 SelectedSignalCandidate
return;
}
}
private DelegateCommand? _addToWriteCmd;
/// <summary>
/// 将右侧选中信号添加到写入配置。
/// </summary>
public DelegateCommand AddToWriteCmd => _addToWriteCmd ??= new DelegateCommand(AddToWriteCmdMethod);
private void AddToWriteCmdMethod()
{
if (!IsEditable)
{
MessageBox.Show("当前状态禁止修改", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
return;
}
if (SelectedSignalCandidate == null)
{
MessageBox.Show("请先在右侧信号集合中选中一条", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
return;
}
if (string.IsNullOrWhiteSpace(SelectedSignalCandidate.SignalName) || string.IsNullOrWhiteSpace(SelectedSignalCandidate.MsgName))
{
MessageBox.Show("选中的信号数据不完整MsgName/SignalName 为空)", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
return;
}
if (WriteConfigs.Any(a => string.Equals(a.SignalName, SelectedSignalCandidate.SignalName, StringComparison.Ordinal) && a.RWInfo == RW.Write))
{
MessageBox.Show("该信号已在写入配置中,无需重复添加", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
return;
}
if (ReadConfigs.Any(a => string.Equals(a.SignalName, SelectedSignalCandidate.SignalName, StringComparison.Ordinal) && a.RWInfo == RW.Read))
{
MessageBox.Show("该信号已在读取配置中,同一个信号不允许同时配置为写入与读取", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
return;
}
WriteConfigs.Add(new CanLinRWConfigDto
{
Id = 0,
RWInfo = RW.Write,
Name = string.IsNullOrWhiteSpace(SelectedSignalCandidate.Name) ? SelectedSignalCandidate.SignalName : SelectedSignalCandidate.Name,
MsgFrameName = SelectedSignalCandidate.MsgName,
SignalName = SelectedSignalCandidate.SignalName,
DefautValue = "0",
LogicRuleId = 0,
});
RebuildSignalTree();
}
private DelegateCommand? _addToReadCmd;
/// <summary>
/// 将右侧选中信号添加到读取配置。
/// </summary>
public DelegateCommand AddToReadCmd => _addToReadCmd ??= new DelegateCommand(AddToReadCmdMethod);
private void AddToReadCmdMethod()
{
if (!IsEditable)
{
MessageBox.Show("当前状态禁止修改", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
return;
}
if (SelectedSignalCandidate == null)
{
MessageBox.Show("请先在右侧信号集合中选中一条", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
return;
}
if (string.IsNullOrWhiteSpace(SelectedSignalCandidate.SignalName) || string.IsNullOrWhiteSpace(SelectedSignalCandidate.MsgName))
{
MessageBox.Show("选中的信号数据不完整MsgName/SignalName 为空)", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
return;
}
if (ReadConfigs.Any(a => string.Equals(a.SignalName, SelectedSignalCandidate.SignalName, StringComparison.Ordinal) && a.RWInfo == RW.Read))
{
MessageBox.Show("该信号已在读取配置中,无需重复添加", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
return;
}
if (WriteConfigs.Any(a => string.Equals(a.SignalName, SelectedSignalCandidate.SignalName, StringComparison.Ordinal) && a.RWInfo == RW.Write))
{
MessageBox.Show("该信号已在写入配置中,同一个信号不允许同时配置为写入与读取", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
return;
}
ReadConfigs.Add(new CanLinRWConfigDto
{
Id = 0,
RWInfo = RW.Read,
Name = string.IsNullOrWhiteSpace(SelectedSignalCandidate.Name) ? SelectedSignalCandidate.SignalName : SelectedSignalCandidate.Name,
MsgFrameName = SelectedSignalCandidate.MsgName,
SignalName = SelectedSignalCandidate.SignalName,
DefautValue = "0",
LogicRuleId = 0,
});
RebuildSignalTree();
}
private DelegateCommand? _removeWriteCmd;
/// <summary>
/// 从写入配置移除当前选中行。
/// </summary>
public DelegateCommand RemoveWriteCmd => _removeWriteCmd ??= new DelegateCommand(RemoveWriteCmdMethod);
private void RemoveWriteCmdMethod()
{
if (!IsEditable)
{
MessageBox.Show("当前状态禁止修改", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
return;
}
if (SelectedWriteConfig == null)
{
MessageBox.Show("请先选中写入列表中的一行", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
return;
}
WriteConfigs.Remove(SelectedWriteConfig);
SelectedWriteConfig = null;
RaisePropertyChanged(nameof(SelectedWriteConfig));
RebuildSignalTree();
}
private DelegateCommand? _removeReadCmd;
/// <summary>
/// 从读取配置移除当前选中行。
/// </summary>
public DelegateCommand RemoveReadCmd => _removeReadCmd ??= new DelegateCommand(RemoveReadCmdMethod);
private void RemoveReadCmdMethod()
{
if (!IsEditable)
{
MessageBox.Show("当前状态禁止修改", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
return;
}
if (SelectedReadConfig == null)
{
MessageBox.Show("请先选中读取列表中的一行", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
return;
}
ReadConfigs.Remove(SelectedReadConfig);
SelectedReadConfig = null;
RaisePropertyChanged(nameof(SelectedReadConfig));
RebuildSignalTree();
}
private DelegateCommand? _saveCmd;
/// <summary>
/// 保存并落库。
/// </summary>
public DelegateCommand SaveCmd => _saveCmd ??= new DelegateCommand(SaveCmdMethod);
private void SaveCmdMethod()
{
if (!IsEditable)
{
MessageBox.Show("当前状态禁止修改", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
return;
}
if (_canLinConfigProId <= 0)
{
MessageBox.Show("配置程序ID无效无法保存", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
return;
}
try
{
PersistRwConfigs();
var pars = new DialogParameters
{
{ "Saved", true }
};
RaiseRequestClose(new DialogResult(ButtonResult.OK, pars));
}
catch (Exception ex)
{
_logService.Error($"ZLG 读写设置保存失败:{ex}");
MessageBox.Show(ex.Message, "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private DelegateCommand? _cancelCmd;
/// <summary>
/// 取消。
/// </summary>
public DelegateCommand CancelCmd => _cancelCmd ??= new DelegateCommand(CancelCmdMethod);
private void CancelCmdMethod()
{
RaiseRequestClose(new DialogResult(ButtonResult.Cancel));
}
/// <summary>
/// 弹窗打开时接收参数。
/// </summary>
/// <param name="parameters">参数。</param>
public override void OnDialogOpened(IDialogParameters parameters)
{
_canLinConfigProId = parameters.GetValue<long>("CanLinConfigProId");
EnableHardwareCycleSchedule = parameters.ContainsKey("EnableHardwareCycleSchedule")
? parameters.GetValue<bool>("EnableHardwareCycleSchedule")
: true;
// 兼容参数命名:调用方传入的 key 为 "IsCanFdSchedule"。
UseCanFdSchedule = parameters.ContainsKey("IsCanFdSchedule")
? parameters.GetValue<bool>("IsCanFdSchedule")
: false;
IsEditable = parameters.ContainsKey("IsEditable") ? parameters.GetValue<bool>("IsEditable") : true;
RaisePropertyChanged(nameof(IsEditable));
RaisePropertyChanged(nameof(CanAddCycleTimeSch));
if (parameters.ContainsKey("WriteConfigs"))
{
var list = parameters.GetValue<ObservableCollection<CanLinRWConfigDto>>("WriteConfigs") ?? new ObservableCollection<CanLinRWConfigDto>();
WriteConfigs = list;
RaisePropertyChanged(nameof(WriteConfigs));
}
if (parameters.ContainsKey("ReadConfigs"))
{
var list = parameters.GetValue<ObservableCollection<CanLinRWConfigDto>>("ReadConfigs") ?? new ObservableCollection<CanLinRWConfigDto>();
ReadConfigs = list;
RaisePropertyChanged(nameof(ReadConfigs));
}
if (parameters.ContainsKey("SignalCandidates"))
{
var list = parameters.GetValue<ObservableCollection<SignalCandidate>>("SignalCandidates") ?? new ObservableCollection<SignalCandidate>();
SignalCandidates = list;
RaisePropertyChanged(nameof(SignalCandidates));
SignalCandidatesView = CollectionViewSource.GetDefaultView(SignalCandidates);
SignalCandidatesView.Filter = FilterSignalCandidate;
RaisePropertyChanged(nameof(SignalCandidatesView));
}
RebuildSignalTree();
if (parameters.ContainsKey("Title"))
{
Title = parameters.GetValue<string>("Title") ?? Title;
}
}
private bool FilterSignalCandidate(object obj)
{
if (obj is not SignalCandidate item)
{
return false;
}
if (string.IsNullOrWhiteSpace(SignalFilterText))
{
return true;
}
var key = SignalFilterText.Trim();
return ContainsIgnoreCase(item.MsgName, key)
|| ContainsIgnoreCase(item.SignalName, key)
|| ContainsIgnoreCase(item.Name, key)
|| ContainsIgnoreCase(item.Desc, key);
}
private void RebuildSignalTree()
{
// 依据过滤条件 + 全量候选池生成树。
// 树数据源独立于 ICollectionView避免 TreeView 过滤复杂度。
var filtered = SignalCandidates
.Where(a => FilterSignalCandidate(a))
.ToList();
var groups = filtered
.GroupBy(a => string.IsNullOrWhiteSpace(a.MsgName) ? "(未命名帧)" : a.MsgName!.Trim(), StringComparer.Ordinal)
.OrderBy(a => a.Key, StringComparer.Ordinal)
.ToList();
SignalTree.Clear();
foreach (var g in groups)
{
var signalNodes = g
.OrderBy(a => a.SignalName ?? string.Empty, StringComparer.Ordinal)
.ThenBy(a => a.Name ?? string.Empty, StringComparer.Ordinal)
.Select(a => new SignalCandidate
{
MsgName = a.MsgName,
SignalName = a.SignalName,
Name = a.Name,
Desc = a.Desc,
AddedInfo = ComputeAddedInfo(a),
})
.ToList();
var node = new SignalFrameNode
{
FrameName = g.Key,
Signals = new ObservableCollection<SignalCandidate>(signalNodes)
};
SignalTree.Add(node);
}
RaisePropertyChanged(nameof(SignalTree));
}
/// <summary>
/// 计算候选信号是否已被添加到写入/读取。
/// 0=未添加1=已添加到写入2=已添加到读取3=同时存在于写入与读取。
/// </summary>
/// <param name="candidate">候选信号。</param>
/// <returns>AddedInfo 标志值。</returns>
private int ComputeAddedInfo(SignalCandidate candidate)
{
if (candidate == null || string.IsNullOrWhiteSpace(candidate.SignalName))
{
return 0;
}
var signal = candidate.SignalName;
var inWrite = WriteConfigs.Any(a => a.RWInfo == RW.Write && string.Equals(a.SignalName, signal, StringComparison.Ordinal));
var inRead = ReadConfigs.Any(a => a.RWInfo == RW.Read && string.Equals(a.SignalName, signal, StringComparison.Ordinal));
if (inWrite && inRead) return 3;
if (inWrite) return 1;
if (inRead) return 2;
return 0;
}
private static bool ContainsIgnoreCase(string? src, string key)
{
if (string.IsNullOrEmpty(src)) return false;
return src.IndexOf(key, StringComparison.OrdinalIgnoreCase) >= 0;
}
private void PersistRwConfigs()
{
// 互斥约束:同一 SignalName 不允许同时出现在写入与读取
EnsureNoWriteReadConflict();
// 规范化 DTO空值/默认值防御)
NormalizeRwConfigs(WriteConfigs, RW.Write);
NormalizeRwConfigs(ReadConfigs, RW.Read);
// 防重复:同一 SignalName 在同一 RW 列表中只允许一条
EnsureNoDuplicateSignal(WriteConfigs, RW.Write);
EnsureNoDuplicateSignal(ReadConfigs, RW.Read);
var existing = _freeSql.Select<CanLinRWConfig>()
.Where(a => a.CanLinConfigProId == _canLinConfigProId)
.Where(a => a.RWInfo == RW.Write || a.RWInfo == RW.Read)
.ToList();
var desiredWrite = WriteConfigs
.Where(a => !string.IsNullOrWhiteSpace(a.SignalName))
.Select(a => new DesiredItem(RW.Write, a.SignalName!, a))
.ToList();
var desiredRead = ReadConfigs
.Where(a => !string.IsNullOrWhiteSpace(a.SignalName))
.Select(a => new DesiredItem(RW.Read, a.SignalName!, a))
.ToList();
var desiredAll = desiredWrite.Concat(desiredRead).ToList();
// 以 (RWInfo + SignalName) 作为“业务主键”对齐数据库:
// - desiredKeySet本次保存期望存在的 key 集合
// - existingByKey当前数据库已存在的 key -> 实体
var desiredKeySet = new HashSet<string>(desiredAll.Select(a => BuildKey(a.Rw, a.SignalName)), StringComparer.Ordinal);
var existingByKey = existing.ToDictionary(a => BuildKey(a.RWInfo, a.SignalName ?? string.Empty), a => a, StringComparer.Ordinal);
// 删除DB 中存在,但目标集合里不存在
foreach (var old in existing)
{
var key = BuildKey(old.RWInfo, old.SignalName ?? string.Empty);
if (!desiredKeySet.Contains(key))
{
// 保持与 UI 一致:用户在弹窗中移除的项,需要同步删除数据库记录。
_freeSql.Delete<CanLinRWConfig>(old.Id).ExecuteAffrows();
}
}
// Upsert按 keyRWInfo + SignalName更新或插入
foreach (var item in desiredAll)
{
var key = BuildKey(item.Rw, item.SignalName);
if (existingByKey.TryGetValue(key, out var old))
{
_freeSql.Update<CanLinRWConfig>(old.Id)
.Set(a => a.Name, item.Dto.Name)
.Set(a => a.MsgFrameName, item.Dto.MsgFrameName)
.Set(a => a.SignalName, item.Dto.SignalName)
.Set(a => a.DefautValue, item.Dto.DefautValue)
.Set(a => a.LogicRuleId, item.Dto.LogicRuleId)
.ExecuteAffrows();
}
else
{
_freeSql.Insert<CanLinRWConfig>(new CanLinRWConfig
{
CanLinConfigProId = _canLinConfigProId,
RWInfo = item.Rw,
Name = item.Dto.Name,
MsgFrameName = item.Dto.MsgFrameName,
SignalName = item.Dto.SignalName,
DefautValue = item.Dto.DefautValue,
LogicRuleId = item.Dto.LogicRuleId,
}).ExecuteAffrows();
}
}
}
private static void NormalizeRwConfigs(IEnumerable<CanLinRWConfigDto> list, RW rw)
{
foreach (var item in list)
{
item.RWInfo = rw;
if (string.IsNullOrWhiteSpace(item.SignalName))
{
continue;
}
if (string.IsNullOrWhiteSpace(item.Name))
{
item.Name = item.SignalName;
}
if (string.IsNullOrWhiteSpace(item.MsgFrameName))
{
item.MsgFrameName = string.Empty;
}
if (string.IsNullOrWhiteSpace(item.DefautValue))
{
item.DefautValue = "0";
}
if (item.LogicRuleId < 0)
{
item.LogicRuleId = 0;
}
}
}
private static void EnsureNoDuplicateSignal(IEnumerable<CanLinRWConfigDto> list, RW rw)
{
var duplicates = list
.Where(a => !string.IsNullOrWhiteSpace(a.SignalName))
.GroupBy(a => a.SignalName!, StringComparer.Ordinal)
.Where(g => g.Count() > 1)
.Select(g => g.Key)
.ToList();
if (duplicates.Count > 0)
{
throw new InvalidOperationException($"{rw} 配置中存在重复信号:{string.Join(",", duplicates)}");
}
}
private void EnsureNoWriteReadConflict()
{
var writeSet = new HashSet<string>(
WriteConfigs
.Where(a => a.RWInfo == RW.Write)
.Select(a => a.SignalName)
.Where(a => !string.IsNullOrWhiteSpace(a))
.Select(a => a!),
StringComparer.Ordinal);
var readSet = new HashSet<string>(
ReadConfigs
.Where(a => a.RWInfo == RW.Read)
.Select(a => a.SignalName)
.Where(a => !string.IsNullOrWhiteSpace(a))
.Select(a => a!),
StringComparer.Ordinal);
writeSet.IntersectWith(readSet);
if (writeSet.Count > 0)
{
throw new InvalidOperationException($"同一信号不允许同时配置为写入与读取,冲突信号:{string.Join(",", writeSet)}");
}
}
private static string BuildKey(RW rw, string signalName)
{
return $"{(int)rw}:{signalName}";
}
private readonly struct DesiredItem
{
/// <summary>
/// 读写类型。
/// </summary>
public RW Rw { get; }
/// <summary>
/// 信号名称。
/// </summary>
public string SignalName { get; }
/// <summary>
/// 原始 DTO。
/// </summary>
public CanLinRWConfigDto Dto { get; }
public DesiredItem(RW rw, string signalName, CanLinRWConfigDto dto)
{
Rw = rw;
SignalName = signalName;
Dto = dto;
}
}
/// <summary>
/// 右侧信号候选项。
/// </summary>
public class SignalCandidate
{
/// <summary>
/// 消息名称/帧名称。
/// </summary>
public string? MsgName { get; set; }
/// <summary>
/// 信号名称。
/// </summary>
public string? SignalName { get; set; }
/// <summary>
/// 配置名称(若解析层已有中文名则传入)。
/// </summary>
public string? Name { get; set; }
/// <summary>
/// 描述。
/// </summary>
public string? Desc { get; set; }
/// <summary>
/// 已添加标记。
/// 0=未添加1=已添加到写入2=已添加到读取3=同时存在于写入与读取。
/// </summary>
public int AddedInfo { get; set; }
}
/// <summary>
/// 帧节点。
/// </summary>
public class SignalFrameNode
{
/// <summary>
/// 帧名。
/// </summary>
public string FrameName { get; set; } = string.Empty;
/// <summary>
/// 帧内信号集合。
/// </summary>
public ObservableCollection<SignalCandidate> Signals { get; set; } = new ObservableCollection<SignalCandidate>();
}
}
}

View File

@@ -1,9 +1,10 @@
using AutoMapper;
using AutoMapper;
using CapMachine.Core;
using CapMachine.Model.CANLIN;
using CapMachine.Wpf.CanDrive;
using CapMachine.Wpf.Dtos;
using CapMachine.Wpf.LinDrive;
using CapMachine.Wpf.PrismEvent;
using CapMachine.Wpf.Services;
using Prism.Commands;
using Prism.Events;
@@ -55,6 +56,7 @@ namespace CapMachine.Wpf.ViewModels
//MachineDataService = machineDataService;
DialogService = dialogService;
EventAggregator.GetEvent<CanLinConfigChangedEvent>().Subscribe(CanLinConfigChangedEventCall);
//数据波特率
DataBaudRateCbxItems = new ObservableCollection<CbxItems>()
@@ -144,6 +146,16 @@ namespace CapMachine.Wpf.ViewModels
//InitWriteRuleCbx();
}
/// <summary>
/// CAN/LIN 配置导入后刷新当前页面。
/// </summary>
/// <param name="msg">事件消息。</param>
private void CanLinConfigChangedEventCall(string msg)
{
InitLoadLinConfigPro();
InitWriteRuleCbx();
}
/// <summary>
/// 初始化写规则下拉框
/// </summary>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,206 @@
<UserControl
x:Class="CapMachine.Wpf.Views.DialogCANFdSchConfigView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:local="clr-namespace:CapMachine.Wpf.Views"
xmlns:localEx="clr-namespace:CapMachine.Wpf"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:prism="http://prismlibrary.com/"
Width="1000"
Height="800"
mc:Ignorable="d">
<UserControl.Resources>
<localEx:BindingProxy x:Key="Proxy" Data="{Binding}" />
<Style x:Key="myHeaderStyle" TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="FontSize" Value="24" />
<Setter Property="Width" Value="218" />
</Style>
<Style x:Key="myHeaderStyle1" TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="FontSize" Value="16" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="BorderBrush" Value="Transparent" />
</Style>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="60" />
<RowDefinition />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal">
<Button
Margin="10,0"
Command="{Binding OpCmd}"
CommandParameter="Add"
FontWeight="Bold"
Foreground="#404040"
Style="{StaticResource MaterialDesignFlatAccentBgButton}">
<StackPanel Orientation="Horizontal">
<TextBlock
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="14"
Foreground="#404040"
Text="&#xe8c0; " />
<TextBlock
VerticalAlignment="Center"
FontSize="14"
Text="新增" />
</StackPanel>
</Button>
<!--<Button
Margin="10,0"
Command="{Binding CopyCmd}"
FontWeight="Bold"
Foreground="#404040"
Style="{StaticResource MaterialDesignFlatAccentBgButton}">
<StackPanel Orientation="Horizontal">
<TextBlock
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="14"
Foreground="#404040"
Text="&#xe9c6;" />
<TextBlock
VerticalAlignment="Center"
FontSize="14"
Text="复制" />
</StackPanel>
</Button>-->
<Button
Margin="10,0"
Command="{Binding OpCmd}"
CommandParameter="Delete"
FontWeight="Bold"
Foreground="#404040"
Style="{StaticResource MaterialDesignFlatAccentBgButton}">
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="0,0,5,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="14"
Foreground="#404040"
Text="&#xe9d9;" />
<TextBlock
VerticalAlignment="Center"
FontSize="14"
Text="删除" />
</StackPanel>
</Button>
</StackPanel>
<StackPanel
Margin="30,0"
HorizontalAlignment="Right"
Orientation="Horizontal">
<TextBlock
Margin="10,0,5,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="22"
Text="&#xe9ff;" />
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="20"
Text="报文发送方式:"
TextAlignment="Center" />
<ComboBox
Width="120"
DisplayMemberPath="Text"
FontSize="20"
ItemsSource="{Binding SendOrderCbxItems}"
SelectedValue="{Binding CurSendOrder, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="Key" />
</StackPanel>
<Grid Grid.Row="1">
<DataGrid
x:Name="MainDatagrid"
Margin="5"
AutoGenerateColumns="False"
CanUserAddRows="False"
HeadersVisibility="Column"
ItemsSource="{Binding ListCANFdScheduleConfigDto}"
SelectionMode="Extended"
SelectionUnit="FullRow">
<DataGrid.Columns>
<!--<DataGridTextColumn
Binding="{Binding Index}"
Header="序号"
IsReadOnly="True" />-->
<DataGridTemplateColumn Width="260" Header="消息名">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox
DisplayMemberPath="Text"
ItemsSource="{Binding Source={StaticResource Proxy}, Path=Data.MsgCbxItems}"
SelectedValue="{Binding MsgName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="Key" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn
Width="100"
Binding="{Binding Cycle}"
Header="周期" />
<!--<DataGridTemplateColumn Width="200" Header="发送方式">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox
DisplayMemberPath="Text"
ItemsSource="{Binding Source={StaticResource Proxy}, Path=Data.SendOrderCbxItems}"
SelectedValue="{Binding OrderSend, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="Key" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>-->
<DataGridTemplateColumn Width="160" Header="调度器序号">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox
DisplayMemberPath="Text"
ItemsSource="{Binding Source={StaticResource Proxy}, Path=Data.SchTabIndexCbxItems}"
SelectedValue="{Binding SchTabIndex, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="Key" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<!--<DataGridCheckBoxColumn Binding="{Binding IsEnable}" Header="是否启用" />-->
</DataGrid.Columns>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<prism:InvokeCommandAction Command="{Binding GridSelectionChangedCmd}" CommandParameter="{Binding ElementName=MainDatagrid, Path=SelectedItem}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</DataGrid>
</Grid>
<StackPanel
Grid.Row="2"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Button
Margin="10,10"
Command="{Binding SaveCmd}"
Content="确定"
Foreground="White" />
<Button
Margin="10,0"
Command="{Binding CancelCmd}"
Content="取消"
Foreground="White" />
</StackPanel>
</Grid>
</UserControl>

View File

@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace CapMachine.Wpf.Views
{
/// <summary>
/// DialogCANFdSchConfigView.xaml 的交互逻辑
/// </summary>
public partial class DialogCANFdSchConfigView : UserControl
{
public DialogCANFdSchConfigView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,83 @@
<UserControl
x:Class="CapMachine.Wpf.Views.DialogCanLinConfigImExportView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:CapMachine.Wpf.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Width="800"
Height="450">
<Grid Margin="15">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock
FontSize="18"
FontWeight="Bold"
Text="CAN/CANFD/LIN 配置导入导出" />
<Grid Grid.Row="1" Margin="0,12,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox
Margin="0,0,8,0"
IsReadOnly="True"
Text="{Binding ImportFilePath, UpdateSourceTrigger=PropertyChanged}" />
<Button
Grid.Column="1"
Padding="16,4"
Command="{Binding BrowseImportFileCmd}"
Content="选择导入文件" />
</Grid>
<StackPanel
Grid.Row="2"
Margin="0,12,0,0"
Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="导入模式:" />
<ComboBox
Width="320"
Margin="8,0,0,0"
DisplayMemberPath="Text"
ItemsSource="{Binding ImportModes}"
SelectedItem="{Binding SelectedImportMode}" />
</StackPanel>
<TextBox
Grid.Row="3"
Margin="0,12,0,0"
HorizontalScrollBarVisibility="Auto"
IsReadOnly="True"
Text="{Binding ResultMessage}"
VerticalScrollBarVisibility="Auto" />
<StackPanel
Grid.Row="4"
Margin="0,12,0,0"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Button
Margin="0,0,8,0"
Padding="16,4"
Command="{Binding ExportCmd}"
Content="导出" />
<Button
Margin="0,0,8,0"
Padding="16,4"
Command="{Binding ImportCmd}"
Content="导入" />
<Button
Padding="16,4"
Command="{Binding CloseCmd}"
Content="关闭" />
</StackPanel>
</Grid>
</UserControl>

View File

@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace CapMachine.Wpf.Views
{
/// <summary>
/// DialogCanLinConfigImExport.xaml 的交互逻辑
/// </summary>
public partial class DialogCanLinConfigImExportView : UserControl
{
public DialogCanLinConfigImExportView()
{
InitializeComponent();
}
}
}

View File

@@ -125,6 +125,10 @@
Binding="{Binding CapNo}"
Header="压缩机编号"
IsReadOnly="{Binding Source={StaticResource Proxy}, Path=Data.IsComplete}" />
<DataGridTextColumn
Binding="{Binding CapDisplacement}"
Header="压缩机排量"
IsReadOnly="{Binding Source={StaticResource Proxy}, Path=Data.IsComplete}" />
<DataGridTextColumn
Binding="{Binding RfNo}"
Header="制冷剂"

View File

@@ -0,0 +1,143 @@
<UserControl
x:Class="CapMachine.Wpf.Views.DialogZlgCANFDSchConfigView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:local="clr-namespace:CapMachine.Wpf.Views"
xmlns:localEx="clr-namespace:CapMachine.Wpf"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:prism="http://prismlibrary.com/"
Width="1000"
Height="800"
mc:Ignorable="d">
<UserControl.Resources>
<localEx:BindingProxy x:Key="Proxy" Data="{Binding}" />
<Style x:Key="myHeaderStyle" TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="FontSize" Value="24" />
<Setter Property="Width" Value="218" />
</Style>
<Style x:Key="myHeaderStyle1" TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="FontSize" Value="16" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="BorderBrush" Value="Transparent" />
</Style>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="60" />
<RowDefinition />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal">
<Button
Margin="10,0"
Command="{Binding OpCmd}"
CommandParameter="Delete"
FontWeight="Bold"
Foreground="#404040"
Style="{StaticResource MaterialDesignFlatAccentBgButton}">
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="0,0,5,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="14"
Foreground="#404040"
Text="&#xe9d9;" />
<TextBlock
VerticalAlignment="Center"
FontSize="14"
Text="删除" />
</StackPanel>
</Button>
</StackPanel>
<StackPanel
Margin="30,0"
HorizontalAlignment="Right"
Orientation="Horizontal">
<TextBlock
Margin="10,0,5,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="22"
Text="&#xe9ff;" />
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="20"
Text="报文发送方式:"
TextAlignment="Center" />
<ComboBox
Width="120"
DisplayMemberPath="Text"
FontSize="20"
ItemsSource="{Binding SendOrderCbxItems}"
SelectedValue="{Binding CurSendOrder, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="Key" />
</StackPanel>
<Grid Grid.Row="1">
<DataGrid
x:Name="MainDatagrid"
Margin="5"
AutoGenerateColumns="False"
CanUserAddRows="False"
HeadersVisibility="Column"
ItemsSource="{Binding ListCANFdScheduleConfigDto}"
SelectionMode="Extended"
SelectionUnit="FullRow">
<DataGrid.Columns>
<DataGridTextColumn
Width="260"
Binding="{Binding MsgName}"
Header="消息名"
IsReadOnly="True" />
<DataGridTextColumn
Width="100"
Binding="{Binding Cycle}"
Header="周期" />
<DataGridTemplateColumn Width="160" Header="调度器序号">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox
DisplayMemberPath="Text"
ItemsSource="{Binding Source={StaticResource Proxy}, Path=Data.SchTabIndexCbxItems}"
SelectedValue="{Binding SchTabIndex, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="Key" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<prism:InvokeCommandAction Command="{Binding GridSelectionChangedCmd}" CommandParameter="{Binding ElementName=MainDatagrid, Path=SelectedItem}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</DataGrid>
</Grid>
<StackPanel
Grid.Row="2"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Button
Margin="10,10"
Command="{Binding SaveCmd}"
Content="确定"
Foreground="White" />
<Button
Margin="10,0"
Command="{Binding CancelCmd}"
Content="取消"
Foreground="White" />
</StackPanel>
</Grid>
</UserControl>

View File

@@ -0,0 +1,18 @@
using System.Windows.Controls;
namespace CapMachine.Wpf.Views
{
/// <summary>
/// DialogZlgCANFDSchConfigView.xaml 的交互逻辑
/// </summary>
public partial class DialogZlgCANFDSchConfigView : UserControl
{
/// <summary>
/// 构造函数。
/// </summary>
public DialogZlgCANFDSchConfigView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,180 @@
<UserControl
x:Class="CapMachine.Wpf.Views.DialogZlgCANSchConfigView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:local="clr-namespace:CapMachine.Wpf.Views"
xmlns:localEx="clr-namespace:CapMachine.Wpf"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:prism="http://prismlibrary.com/"
Width="1000"
Height="800"
mc:Ignorable="d">
<UserControl.Resources>
<localEx:BindingProxy x:Key="Proxy" Data="{Binding}" />
<Style x:Key="myHeaderStyle" TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="FontSize" Value="24" />
<Setter Property="Width" Value="218" />
</Style>
<Style x:Key="myHeaderStyle1" TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="FontSize" Value="16" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="BorderBrush" Value="Transparent" />
</Style>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="60" />
<RowDefinition />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal">
<!--<Button
Margin="10,0"
Command="{Binding CopyCmd}"
FontWeight="Bold"
Foreground="#404040"
Style="{StaticResource MaterialDesignFlatAccentBgButton}">
<StackPanel Orientation="Horizontal">
<TextBlock
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="14"
Foreground="#404040"
Text="&#xe9c6;" />
<TextBlock
VerticalAlignment="Center"
FontSize="14"
Text="复制" />
</StackPanel>
</Button>-->
<Button
Margin="10,0"
Command="{Binding OpCmd}"
CommandParameter="Delete"
FontWeight="Bold"
Foreground="#404040"
Style="{StaticResource MaterialDesignFlatAccentBgButton}">
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="0,0,5,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="14"
Foreground="#404040"
Text="&#xe9d9;" />
<TextBlock
VerticalAlignment="Center"
FontSize="14"
Text="删除" />
</StackPanel>
</Button>
</StackPanel>
<StackPanel
Margin="30,0"
HorizontalAlignment="Right"
Orientation="Horizontal">
<TextBlock
Margin="10,0,5,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="22"
Text="&#xe9ff;" />
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="20"
Text="报文发送方式:"
TextAlignment="Center" />
<ComboBox
Width="120"
DisplayMemberPath="Text"
FontSize="20"
ItemsSource="{Binding SendOrderCbxItems}"
SelectedValue="{Binding CurSendOrder, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="Key" />
</StackPanel>
<Grid Grid.Row="1">
<DataGrid
x:Name="MainDatagrid"
Margin="5"
AutoGenerateColumns="False"
CanUserAddRows="False"
HeadersVisibility="Column"
ItemsSource="{Binding ListCANScheduleConfigDto}"
SelectionMode="Extended"
SelectionUnit="FullRow">
<DataGrid.Columns>
<!--<DataGridTextColumn
Binding="{Binding Index}"
Header="序号"
IsReadOnly="True" />-->
<DataGridTextColumn
Width="260"
Binding="{Binding MsgName}"
Header="消息名"
IsReadOnly="True" />
<DataGridTextColumn
Width="100"
Binding="{Binding Cycle}"
Header="周期" />
<!--<DataGridTemplateColumn Width="200" Header="发送方式">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox
DisplayMemberPath="Text"
ItemsSource="{Binding Source={StaticResource Proxy}, Path=Data.SendOrderCbxItems}"
SelectedValue="{Binding OrderSend, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="Key" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>-->
<DataGridTemplateColumn Width="160" Header="调度器序号">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox
DisplayMemberPath="Text"
ItemsSource="{Binding Source={StaticResource Proxy}, Path=Data.SchTabIndexCbxItems}"
SelectedValue="{Binding SchTabIndex, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="Key" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<!--<DataGridCheckBoxColumn Binding="{Binding IsEnable}" Header="是否启用" />-->
</DataGrid.Columns>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<prism:InvokeCommandAction Command="{Binding GridSelectionChangedCmd}" CommandParameter="{Binding ElementName=MainDatagrid, Path=SelectedItem}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</DataGrid>
</Grid>
<StackPanel
Grid.Row="2"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Button
Margin="10,10"
Command="{Binding SaveCmd}"
Content="确定"
Foreground="White" />
<Button
Margin="10,0"
Command="{Binding CancelCmd}"
Content="取消"
Foreground="White" />
</StackPanel>
</Grid>
</UserControl>

View File

@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace CapMachine.Wpf.Views
{
/// <summary>
/// DialogZlgCANSchConfigView.xaml 的交互逻辑
/// </summary>
public partial class DialogZlgCANSchConfigView : UserControl
{
public DialogZlgCANSchConfigView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,373 @@
<UserControl
x:Class="CapMachine.Wpf.Views.DialogZlgCanLinRwConfigView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:local="clr-namespace:CapMachine.Wpf.Views"
xmlns:localEx="clr-namespace:CapMachine.Wpf"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:prism="http://prismlibrary.com/"
xmlns:vm="clr-namespace:CapMachine.Wpf.ViewModels"
Width="1900"
Height="900"
mc:Ignorable="d">
<UserControl.Resources>
<localEx:BindingProxy x:Key="Proxy" Data="{Binding}" />
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1.3*" />
<ColumnDefinition Width="15" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="15" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<GroupBox Grid.Row="0">
<GroupBox.Header>
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="2,0,5,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="18"
Foreground="White"
Text="&#xe9f8;" />
<TextBlock FontSize="18" Foreground="White">写入配置</TextBlock>
</StackPanel>
</GroupBox.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.Resources>
<Style
x:Key="TextBoxStyle"
BasedOn="{StaticResource MaterialDesignTextBoxBase}"
TargetType="TextBox">
<Setter Property="FontSize" Value="18" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Margin" Value="10,0" />
<Setter Property="Width" Value="80" />
</Style>
<Style x:Key="TextBlockStyle" TargetType="TextBlock">
<Setter Property="Width" Value="110" />
<Setter Property="FontSize" Value="18" />
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Margin" Value="5,0" />
</Style>
</Grid.Resources>
<StackPanel Grid.Column="1" Orientation="Horizontal">
<TextBlock
Margin="10,0,2,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="20"
Text="&#xe796;" />
<TextBlock
Width="90"
Style="{StaticResource TextBlockStyle}"
Text="定时调度表:" />
<Button
Margin="10,0"
Command="{Binding AddCycleTimeSch}"
Content="加入定时调度表"
Foreground="White"
IsEnabled="{Binding CanAddCycleTimeSch}" />
</StackPanel>
<StackPanel
Grid.Row="0"
Margin="5"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Button
Margin="5,0"
Command="{Binding RemoveWriteCmd}"
Content="从写入移除"
Foreground="White"
IsEnabled="{Binding IsEditable}" />
</StackPanel>
<DataGrid
Grid.Row="1"
Margin="5"
AutoGenerateColumns="False"
CanUserAddRows="False"
HeadersVisibility="Column"
ItemsSource="{Binding WriteConfigs}"
SelectedItem="{Binding SelectedWriteConfig, Mode=TwoWay}"
SelectionMode="Single"
SelectionUnit="FullRow">
<DataGrid.Columns>
<DataGridTemplateColumn Width="180" Header="名称">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox
DisplayMemberPath="Text"
IsEditable="True"
IsEnabled="{Binding Source={StaticResource Proxy}, Path=Data.IsEditable}"
ItemsSource="{Binding Source={StaticResource Proxy}, Path=Data.WriteNameCbxItems}"
SelectedValue="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="Text" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn
Width="200"
Binding="{Binding MsgFrameName}"
Header="消息名称"
IsReadOnly="True" />
<DataGridTextColumn
Width="200"
Binding="{Binding SignalName}"
Header="信号名称"
IsReadOnly="True" />
<DataGridTextColumn
Width="120"
Binding="{Binding DefautValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Header="默认值" />
<DataGridTemplateColumn Width="200" Header="规则名称">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox
DisplayMemberPath="Name"
IsEnabled="{Binding Source={StaticResource Proxy}, Path=Data.IsEditable}"
ItemsSource="{Binding Source={StaticResource Proxy}, Path=Data.LogicRuleDtos}"
SelectedValue="{Binding LogicRuleId, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="Id" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</GroupBox>
<GroupBox Grid.Row="2">
<GroupBox.Header>
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="2,0,5,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="18"
Foreground="White"
Text="&#xe9f8;" />
<TextBlock FontSize="18" Foreground="White">读取配置</TextBlock>
</StackPanel>
</GroupBox.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel
Grid.Row="0"
Margin="5"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Button
Margin="5,0"
Command="{Binding RemoveReadCmd}"
Content="从读取移除"
Foreground="White"
IsEnabled="{Binding IsEditable}" />
</StackPanel>
<DataGrid
Grid.Row="1"
Margin="5"
AutoGenerateColumns="False"
CanUserAddRows="False"
HeadersVisibility="Column"
ItemsSource="{Binding ReadConfigs}"
SelectedItem="{Binding SelectedReadConfig, Mode=TwoWay}"
SelectionMode="Single"
SelectionUnit="FullRow">
<DataGrid.Columns>
<DataGridTemplateColumn Width="180" Header="名称">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox
DisplayMemberPath="Text"
IsEditable="True"
IsEnabled="{Binding Source={StaticResource Proxy}, Path=Data.IsEditable}"
ItemsSource="{Binding Source={StaticResource Proxy}, Path=Data.ReadNameCbxItems}"
SelectedValue="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="Text" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn
Width="200"
Binding="{Binding MsgFrameName}"
Header="消息名称"
IsReadOnly="True" />
<DataGridTextColumn
Width="200"
Binding="{Binding SignalName}"
Header="信号名称"
IsReadOnly="True" />
<DataGridTextColumn
Width="120"
Binding="{Binding DefautValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Header="默认值" />
<DataGridTemplateColumn Width="200" Header="规则名称">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox
DisplayMemberPath="Name"
IsEnabled="{Binding Source={StaticResource Proxy}, Path=Data.IsEditable}"
ItemsSource="{Binding Source={StaticResource Proxy}, Path=Data.LogicRuleDtos}"
SelectedValue="{Binding LogicRuleId, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="Id" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</GroupBox>
</Grid>
<GroupBox Grid.Column="2">
<GroupBox.Header>
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="2,0,5,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="18"
Foreground="White"
Text="&#xe9f8;" />
<TextBlock FontSize="18" Foreground="White">信号集合</TextBlock>
</StackPanel>
</GroupBox.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition />
</Grid.RowDefinitions>
<TextBox
Grid.Row="0"
Margin="5"
VerticalContentAlignment="Center"
Text="{Binding SignalFilterText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<StackPanel
Grid.Row="1"
Margin="5"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Button
Margin="5,0"
Command="{Binding AddToWriteCmd}"
Content="添加到写入"
Foreground="White"
IsEnabled="{Binding IsEditable}" />
<Button
Margin="5,0"
Command="{Binding AddToReadCmd}"
Content="添加到读取"
Foreground="White"
IsEnabled="{Binding IsEditable}" />
</StackPanel>
<TreeView
x:Name="SignalTreeView"
Grid.Row="2"
Margin="5"
ItemsSource="{Binding SignalTree}">
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="True" />
</Style>
</TreeView.ItemContainerStyle>
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type vm:DialogZlgCanLinRwConfigViewModel+SignalFrameNode}" ItemsSource="{Binding Signals}">
<TextBlock FontWeight="Bold" Text="{Binding FrameName}" />
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type vm:DialogZlgCanLinRwConfigViewModel+SignalCandidate}">
<Grid>
<Grid.Style>
<Style TargetType="Grid">
<Setter Property="Background" Value="Transparent" />
<Style.Triggers>
<DataTrigger Binding="{Binding AddedInfo}" Value="1">
<Setter Property="Background" Value="LightGreen" />
</DataTrigger>
<DataTrigger Binding="{Binding AddedInfo}" Value="2">
<Setter Property="Background" Value="SkyBlue" />
</DataTrigger>
<DataTrigger Binding="{Binding AddedInfo}" Value="3">
<Setter Property="Background" Value="Orange" />
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="180" />
<ColumnDefinition Width="160" />
<ColumnDefinition Width="120" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding MsgName}" />
<TextBlock Grid.Column="1" Text="{Binding SignalName}" />
<TextBlock Grid.Column="2" Text="{Binding Name}" />
<TextBlock
Grid.Column="3"
Text="{Binding Desc}"
TextTrimming="CharacterEllipsis" />
</Grid>
</DataTemplate>
</TreeView.Resources>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectedItemChanged">
<prism:InvokeCommandAction Command="{Binding SignalTreeSelectionChangedCmd}" CommandParameter="{Binding ElementName=SignalTreeView, Path=SelectedItem}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TreeView>
</Grid>
</GroupBox>
</Grid>
<StackPanel
Grid.Row="1"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Button
Margin="10,10"
Command="{Binding SaveCmd}"
Content="保存"
Foreground="White"
IsEnabled="{Binding IsEditable}" />
<Button
Margin="10,0"
Command="{Binding CancelCmd}"
Content="取消"
Foreground="White" />
</StackPanel>
</Grid>
</UserControl>

View File

@@ -0,0 +1,15 @@
using System.Windows.Controls;
namespace CapMachine.Wpf.Views
{
/// <summary>
/// DialogZlgCanLinRwConfigView.xaml 的交互逻辑
/// </summary>
public partial class DialogZlgCanLinRwConfigView : UserControl
{
public DialogZlgCanLinRwConfigView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,842 @@
<UserControl
x:Class="CapMachine.Wpf.Views.ZlgCanDriveConfigView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:local="clr-namespace:CapMachine.Wpf.Views"
xmlns:localEx="clr-namespace:CapMachine.Wpf"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:prism="http://prismlibrary.com/"
d:DesignHeight="980"
d:DesignWidth="1920"
prism:ViewModelLocator.AutoWireViewModel="True"
mc:Ignorable="d">
<UserControl.Resources>
<localEx:BindingProxy x:Key="Proxy" Data="{Binding}" />
</UserControl.Resources>
<Grid>
<Grid.Resources>
<Style
x:Key="TextBoxStyle"
BasedOn="{StaticResource MaterialDesignTextBoxBase}"
TargetType="TextBox">
<Setter Property="FontSize" Value="18" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Margin" Value="10,0" />
<Setter Property="Width" Value="80" />
</Style>
<Style x:Key="TextBlockStyle" TargetType="TextBlock">
<Setter Property="Width" Value="110" />
<Setter Property="FontSize" Value="18" />
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Margin" Value="5,0" />
</Style>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="6*" />
<ColumnDefinition Width="6*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="60" />
<RowDefinition />
</Grid.RowDefinitions>
<materialDesign:Card
Margin="3"
Background="{DynamicResource MaterialDesignLightBackground}"
Foreground="{DynamicResource PrimaryHueLightForegroundBrush}"
UniformCornerRadius="5">
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="10,0,0,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="20"
Text="&#xe9f8;" />
<TextBlock
Margin="5,0"
VerticalAlignment="Center"
FontSize="20"
FontWeight="Bold"
Text="{Binding DbcPathTitle}" />
<Border
Width="550"
Margin="5,8"
Padding="15,5"
Background="LightGray"
CornerRadius="5">
<TextBlock
HorizontalAlignment="Right"
VerticalAlignment="Center"
FontSize="20"
Text="{Binding CurrentDbcPath}" />
</Border>
<Button Command="{Binding LoadDbcCmd}" Foreground="White">
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="2,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="18"
Text="&#xe771; " />
<TextBlock
VerticalAlignment="Center"
FontSize="14"
Text="选择Dbc文件" />
</StackPanel>
</Button>
</StackPanel>
</materialDesign:Card>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="400" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<materialDesign:Card
Grid.Column="0"
Margin="3"
Background="{DynamicResource MaterialDesignLightBackground}"
Foreground="{DynamicResource PrimaryHueLightForegroundBrush}"
UniformCornerRadius="5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition Height="40" />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock
Margin="10,0"
VerticalAlignment="Center"
FontSize="18"
FontWeight="Bold"
Text="配置程序" />
<StackPanel Grid.Row="1" Orientation="Horizontal">
<StackPanel.Resources>
<Style
x:Key="btnStyle"
BasedOn="{StaticResource MaterialDesignFlatSecondaryLightBgButton}"
TargetType="Button">
<Setter Property="Margin" Value="5,2" />
<Setter Property="Foreground" Value="White" />
</Style>
</StackPanel.Resources>
<Button
Command="{Binding CanLinConfigProCmd}"
CommandParameter="Add"
Style="{StaticResource btnStyle}">
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="2,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="18"
Text="&#xe8c0;" />
<TextBlock
VerticalAlignment="Center"
FontSize="14"
Text="新建" />
</StackPanel>
</Button>
<Button
Command="{Binding CanLinConfigProCmd}"
CommandParameter="Edit"
Style="{StaticResource btnStyle}">
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="2,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="18"
Text="&#xe73a;" />
<TextBlock
VerticalAlignment="Center"
FontSize="14"
Text="修改" />
</StackPanel>
</Button>
<Button
Command="{Binding CanLinConfigProCmd}"
CommandParameter="Delete"
Style="{StaticResource btnStyle}">
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="2,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="18"
Text="&#xe748;" />
<TextBlock
VerticalAlignment="Center"
FontSize="14"
Text="删除" />
</StackPanel>
</Button>
<Button
Command="{Binding CanLinConfigProCmd}"
CommandParameter="Active"
Style="{StaticResource btnStyle}">
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="2,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="18"
Text="&#xe8fa;" />
<TextBlock
VerticalAlignment="Center"
FontSize="14"
Text="激活" />
</StackPanel>
</Button>
</StackPanel>
<DataGrid
x:Name="CanConfigDatagrid"
Grid.Row="2"
AutoGenerateColumns="False"
BorderBrush="Gray"
BorderThickness="1"
CanUserAddRows="False"
HeadersVisibility="Column"
IsEnabled="{Binding IsCANConfigDatagridActive}"
IsReadOnly="True"
ItemsSource="{Binding ListCanLinConfigPro}"
SelectedItem="{Binding SelectCanLinConfigPro, Mode=TwoWay}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<prism:InvokeCommandAction Command="{Binding CanConfigProGridSelectionChangedCmd}" CommandParameter="{Binding ElementName=CanConfigDatagrid, Path=SelectedItem}" />
</i:EventTrigger>
<i:EventTrigger EventName="PreviewMouseLeftButtonDown">
<prism:InvokeCommandAction Command="{Binding CanConfigProGridPreviewMouseLeftButtonDownCmd}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<DataGrid.Columns>
<DataGridTextColumn
Width="*"
Binding="{Binding ConfigName}"
Header="名称" />
<DataGridTextColumn
Width="80"
Binding="{Binding CANLINInfo}"
Header="模式" />
</DataGrid.Columns>
</DataGrid>
</Grid>
</materialDesign:Card>
<materialDesign:Card
Grid.Column="1"
Margin="3"
Background="{DynamicResource MaterialDesignLightBackground}"
Foreground="{DynamicResource PrimaryHueLightForegroundBrush}"
UniformCornerRadius="5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition Height="60" />
<RowDefinition Height="40" />
<RowDefinition Height="200" />
<RowDefinition Height="40" />
<RowDefinition Height="400" />
<RowDefinition Height="40" />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock
Margin="10,0"
VerticalAlignment="Center"
FontSize="18"
FontWeight="Bold"
Text="参数操作" />
<StackPanel
Grid.Row="1"
Margin="0,0,15,0"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Button
Margin="5,0"
Command="{Binding CanOpCmd}"
CommandParameter="Open"
Foreground="White">
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="2,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="18"
Text="&#xe743;" />
<TextBlock
VerticalAlignment="Center"
FontSize="14"
Text="{Binding ConnectButtonText}" />
</StackPanel>
</Button>
<Button
Margin="5,0"
Command="{Binding CanOpCmd}"
CommandParameter="Close"
Foreground="White">
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="2,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="18"
Text="&#xe7fb;" />
<TextBlock
VerticalAlignment="Center"
FontSize="14"
Text="{Binding CloseButtonText}" />
</StackPanel>
</Button>
<Button
Margin="5,0"
Command="{Binding CanOpCmd}"
CommandParameter="Parse"
Foreground="White">
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="2,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="18"
Text="&#xe93f;" />
<TextBlock
VerticalAlignment="Center"
FontSize="14"
Text="解析DBC" />
</StackPanel>
</Button>
<Button
Margin="5,0"
Command="{Binding CanOpCmd}"
CommandParameter="Save"
Foreground="White">
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="2,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="18"
Text="&#xe936;" />
<TextBlock
VerticalAlignment="Center"
FontSize="14"
Text="保存配置" />
</StackPanel>
</Button>
</StackPanel>
<TextBlock
Grid.Row="2"
Margin="10,0"
VerticalAlignment="Center"
FontSize="18"
FontWeight="Bold"
Text="状态和模式" />
<Grid Grid.Row="3">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="10,0,2,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="20"
Text="&#xe977;" />
<TextBlock
Width="80"
Style="{StaticResource TextBlockStyle}"
Text="报文状态:" />
<Border
Width="50"
Margin="0,12"
BorderThickness="1"
CornerRadius="5">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="Gray" />
<Style.Triggers>
<DataTrigger Binding="{Binding ZlgCanDriveService.IsSendOk}" Value="true">
<Setter Property="Background" Value="LimeGreen" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock
Margin="5"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="White"
Text="发送" />
</Border>
<Border
Width="50"
Margin="2,12"
BorderThickness="1"
CornerRadius="5">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="Gray" />
<Style.Triggers>
<DataTrigger Binding="{Binding ZlgCanDriveService.IsReviceOk}" Value="true">
<Setter Property="Background" Value="LimeGreen" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock
Margin="5"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="White"
Text="接收" />
</Border>
</StackPanel>
<StackPanel Grid.Column="1" Orientation="Horizontal">
<TextBlock
Margin="10,0,2,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="20"
Text="&#xe796;" />
<TextBlock
Width="90"
Style="{StaticResource TextBlockStyle}"
Text="启用调度表:" />
<ToggleButton
Width="40"
Margin="2,2"
Command="{Binding SchEnableCmd}"
IsChecked="{Binding CurrentSchEnable, Mode=TwoWay}"
Style="{StaticResource MaterialDesignSwitchToggleButton}"
ToolTip="启用调度表" />
</StackPanel>
<StackPanel Grid.Row="1" Orientation="Horizontal">
<TextBlock
Margin="10,0,2,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="18"
Text="&#xe8fa;" />
<TextBlock Style="{StaticResource TextBlockStyle}" Text="CAN连接状态" />
<Border
Width="50"
Margin="5,10"
Padding="5"
CornerRadius="3">
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<DataTrigger Binding="{Binding ZlgCanDriveService.OpenState}" Value="True">
<Setter Property="Background" Value="LimeGreen" />
</DataTrigger>
<DataTrigger Binding="{Binding ZlgCanDriveService.OpenState}" Value="False">
<Setter Property="Background" Value="Gray" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="White"
Text="连接" />
</Border>
</StackPanel>
<StackPanel
Grid.Row="1"
Grid.Column="1"
Orientation="Horizontal">
<TextBlock
Margin="10,0,2,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="20"
Text="&#xe796;" />
<TextBlock
Width="90"
Style="{StaticResource TextBlockStyle}"
Text="CAN/FD:" />
<ComboBox
Width="110"
Margin="5,0"
SelectedValue="{Binding SelectedModeKey, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="Tag">
<ComboBoxItem Content="CAN" Tag="Can" />
<ComboBoxItem Content="CANFD" Tag="CanFd" />
</ComboBox>
</StackPanel>
<StackPanel
Grid.Row="2"
Grid.Column="0"
Orientation="Horizontal">
<Button
Margin="15,0,5,0"
Command="{Binding CanOpCmd}"
CommandParameter="CycleSend">
<Button.Style>
<Style BasedOn="{StaticResource MaterialDesignFlatDarkBgButton}" TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding ZlgCanDriveService.IsCycleSend}" Value="true">
<Setter Property="Background" Value="LimeGreen" />
<Setter Property="Foreground" Value="White" />
</DataTrigger>
<DataTrigger Binding="{Binding ZlgCanDriveService.IsCycleSend}" Value="false">
<Setter Property="Background" Value="Gray" />
<Setter Property="Foreground" Value="White" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="2,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="18"
Text="&#xe796;" />
<TextBlock
VerticalAlignment="Center"
FontSize="14"
Text="循环发送" />
</StackPanel>
</Button>
<Button
Margin="5,0"
Command="{Binding CanOpCmd}"
CommandParameter="CycleRecive">
<Button.Style>
<Style BasedOn="{StaticResource MaterialDesignFlatDarkBgButton}" TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding ZlgCanDriveService.IsCycleRevice}" Value="true">
<Setter Property="Background" Value="LimeGreen" />
<Setter Property="Foreground" Value="White" />
</DataTrigger>
<DataTrigger Binding="{Binding ZlgCanDriveService.IsCycleRevice}" Value="false">
<Setter Property="Background" Value="Gray" />
<Setter Property="Foreground" Value="White" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="2,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="18"
Text="&#xe73a;" />
<TextBlock
VerticalAlignment="Center"
FontSize="14"
Text="循环接收" />
</StackPanel>
</Button>
</StackPanel>
</Grid>
<TextBlock
Grid.Row="4"
Margin="10,0"
VerticalAlignment="Center"
FontSize="18"
FontWeight="Bold"
Text="参数信息" />
<Grid Grid.Row="5">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="10,0,10,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="18"
Text="&#xe9f8;" />
<TextBlock Style="{StaticResource TextBlockStyle}" Text="{Binding BaudRateTitle}" />
<ComboBox
Width="100"
DisplayMemberPath="Text"
FontSize="16"
ItemsSource="{Binding ArbBaudRateCbxItems}"
SelectedValue="{Binding CurrentArbBaudRate, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="Key" />
<!--<TextBox Style="{StaticResource TextBoxStyle}" Text="{Binding SelectedCANConfigExdDto.DataBaudRate}" />-->
</StackPanel>
<StackPanel Grid.Column="1" Orientation="Horizontal">
<TextBlock
Margin="10,0,10,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="18"
Text="&#xe9f8;" />
<TextBlock Style="{StaticResource TextBlockStyle}" Text="{Binding DataBaudRateTitle}" />
<ComboBox
Width="100"
DisplayMemberPath="Text"
FontSize="16"
IsEnabled="{Binding IsCanFdMode}"
ItemsSource="{Binding DataBaudRateCbxItems}"
SelectedValue="{Binding CurrentDataBaudRate, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="Key" />
<!--<TextBox Style="{StaticResource TextBoxStyle}" Text="{Binding SelectedCANConfigExdDto.ArbBaudRate}" />-->
</StackPanel>
<StackPanel
Grid.Row="1"
Grid.Column="1"
Orientation="Horizontal">
<TextBlock
Margin="10,0,10,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="18"
Text="&#xe9f8;" />
<TextBlock
Width="auto"
Style="{StaticResource TextBlockStyle}"
Text="ISO标准" />
<ToggleButton
Width="50"
Margin="5,0,5,0"
IsChecked="{Binding CurrentISOEnable, Mode=TwoWay}"
IsEnabled="{Binding IsCanFdMode}"
Style="{StaticResource MaterialDesignSwitchToggleButton}"
ToolTip="{Binding Name}" />
</StackPanel>
<StackPanel
Grid.Row="2"
Grid.Column="1"
Orientation="Horizontal">
<TextBlock
Margin="10,0,10,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="18"
Text="&#xe9f8;" />
<TextBlock
Width="auto"
Style="{StaticResource TextBlockStyle}"
Text="终端电阻" />
<ToggleButton
Width="50"
Margin="5,0,5,0"
IsChecked="{Binding CurrentResEnable, Mode=TwoWay}"
IsEnabled="{Binding IsCanFdMode}"
Style="{StaticResource MaterialDesignSwitchToggleButton}"
ToolTip="{Binding Name}" />
</StackPanel>
<StackPanel Grid.Row="1" Orientation="Horizontal">
<TextBlock
Margin="10,0,10,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="18"
Text="&#xe9f8;" />
<TextBlock Style="{StaticResource TextBlockStyle}" Text="循环周期(ms)" />
<TextBox Style="{StaticResource TextBoxStyle}" Text="{Binding CurrentCycle, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</Grid>
<TextBlock
Grid.Row="6"
Margin="10,0"
VerticalAlignment="Center"
FontSize="18"
FontWeight="Bold"
Text="写入读取操作" />
<Grid Grid.Row="7">
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel
Margin="0,0,15,0"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Button
Margin="5,0"
Command="{Binding WriteReadConfigCmd}"
CommandParameter="OpenDialog"
Foreground="White"
IsEnabled="{Binding IsRwEditable}">
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="2,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="18"
Text="&#xe796;" />
<TextBlock
VerticalAlignment="Center"
FontSize="14"
Text="读写设置" />
</StackPanel>
</Button>
<Button
Margin="15,0,0,0"
Command="{Binding ScheduleConfigCmd}"
Foreground="White">
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="2,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="18"
Text="&#xe796;" />
<TextBlock
VerticalAlignment="Center"
FontSize="14"
Text="调度表" />
</StackPanel>
</Button>
</StackPanel>
<TextBlock
Grid.Row="1"
Margin="10"
VerticalAlignment="Center"
FontSize="16"
Text="提示:点击【读写设置】在弹窗中统一管理写入/读取配置(含规则与默认值)。"
TextWrapping="Wrap" />
</Grid>
</Grid>
</materialDesign:Card>
</Grid>
<materialDesign:Card
Grid.RowSpan="2"
Grid.Column="1"
Margin="3"
Background="{DynamicResource MaterialDesignLightBackground}"
Foreground="{DynamicResource PrimaryHueLightForegroundBrush}"
UniformCornerRadius="5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock
Margin="10,0"
VerticalAlignment="Center"
FontSize="18"
FontWeight="Bold"
Text="信号" />
<DataGrid
Grid.Row="1"
AutoGenerateColumns="False"
CanUserAddRows="False"
HeadersVisibility="Column"
IsReadOnly="True"
ItemsSource="{Binding ListCanDbcModel}"
SelectedItem="{Binding SelectedCanDbcModel, Mode=TwoWay}">
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<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 Width="120" Binding="{Binding MsgId}">
<DataGridTextColumn.Header>
<TextBlock FontWeight="Bold" Text="消息ID" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Width="200" Binding="{Binding MsgName}">
<DataGridTextColumn.Header>
<TextBlock FontWeight="Bold" Text="消息名称" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Width="140" Binding="{Binding Name}">
<DataGridTextColumn.Header>
<TextBlock FontWeight="Bold" Text="中文名称" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Width="*" Binding="{Binding SignalName}">
<DataGridTextColumn.Header>
<TextBlock FontWeight="Bold" Text="信号名称" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Width="90" Binding="{Binding SignalUnit}">
<DataGridTextColumn.Header>
<TextBlock FontWeight="Bold" Text="单位" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Width="150" Binding="{Binding SignalRtValue}">
<DataGridTextColumn.Header>
<TextBlock FontWeight="Bold" Text="实时值" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</materialDesign:Card>
</Grid>
</UserControl>

View File

@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace CapMachine.Wpf.Views
{
/// <summary>
/// ZlgCanDriveConfigView.xaml 的交互逻辑
/// </summary>
public partial class ZlgCanDriveConfigView : UserControl
{
public ZlgCanDriveConfigView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,741 @@
<UserControl
x:Class="CapMachine.Wpf.Views.ZlgLinDriveConfigView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:local="clr-namespace:CapMachine.Wpf.Views"
xmlns:localEx="clr-namespace:CapMachine.Wpf"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:prism="http://prismlibrary.com/"
d:DesignHeight="980"
d:DesignWidth="1920"
prism:ViewModelLocator.AutoWireViewModel="True"
mc:Ignorable="d">
<UserControl.Resources>
<localEx:BindingProxy x:Key="Proxy" Data="{Binding}" />
</UserControl.Resources>
<Grid>
<Grid.Resources>
<Style
x:Key="TextBoxStyle"
BasedOn="{StaticResource MaterialDesignTextBoxBase}"
TargetType="TextBox">
<Setter Property="FontSize" Value="18" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Margin" Value="10,0" />
<Setter Property="Width" Value="120" />
</Style>
<Style x:Key="TextBlockStyle" TargetType="TextBlock">
<Setter Property="Width" Value="110" />
<Setter Property="FontSize" Value="18" />
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Margin" Value="5,0" />
</Style>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="6*" />
<ColumnDefinition Width="6*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="60" />
<RowDefinition />
</Grid.RowDefinitions>
<materialDesign:Card
Margin="3"
Background="{DynamicResource MaterialDesignLightBackground}"
Foreground="{DynamicResource PrimaryHueLightForegroundBrush}"
UniformCornerRadius="5"
Grid.ColumnSpan="2">
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="10,0,10,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="30"
Text="&#xe9f8;" />
<TextBlock
Margin="5,0"
VerticalAlignment="Center"
FontSize="30"
FontWeight="Bold"
Text="LIN LDF 文件路径:" />
<Border
Width="800"
Margin="5,8"
Padding="15,5"
Background="LightGray"
CornerRadius="5">
<TextBlock
VerticalAlignment="Center"
FontSize="22"
Text="{Binding CurrentLdfPath}" />
</Border>
<Button Command="{Binding LoadLdfCmd}" Foreground="White">
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="2,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="18"
Text="&#xe771; " />
<TextBlock VerticalAlignment="Center" FontSize="14" Text="选择Ldf文件" />
</StackPanel>
</Button>
<Button
Margin="15,0,2,0"
Command="{Binding LinOpCmd}"
CommandParameter="Open"
Foreground="White">
<TextBlock FontSize="14" Text="打开" />
</Button>
<Button
Margin="2,0"
Command="{Binding LinOpCmd}"
CommandParameter="Close"
Foreground="White">
<TextBlock FontSize="14" Text="关闭" />
</Button>
<Button
Margin="2,0"
Command="{Binding LinOpCmd}"
CommandParameter="Parse"
Foreground="White">
<TextBlock FontSize="14" Text="解析" />
</Button>
<Button
Margin="2,0"
Command="{Binding LinOpCmd}"
CommandParameter="Save"
Foreground="White">
<TextBlock FontSize="14" Text="保存" />
</Button>
</StackPanel>
</materialDesign:Card>
<Grid Grid.Row="1" Grid.Column="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="400" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<materialDesign:Card
Grid.Column="0"
Margin="3"
Background="{DynamicResource MaterialDesignLightBackground}"
Foreground="{DynamicResource PrimaryHueLightForegroundBrush}"
UniformCornerRadius="5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition Height="40" />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock
Margin="10,0"
VerticalAlignment="Center"
FontSize="18"
FontWeight="Bold"
Text="配置程序" />
<StackPanel Grid.Row="1" Orientation="Horizontal">
<StackPanel.Resources>
<Style
x:Key="btnStyle"
BasedOn="{StaticResource MaterialDesignFlatSecondaryLightBgButton}"
TargetType="Button">
<Setter Property="Margin" Value="5,2" />
<Setter Property="Foreground" Value="White" />
</Style>
</StackPanel.Resources>
<Button
Command="{Binding LinConfigProCmd}"
CommandParameter="Add"
Style="{StaticResource btnStyle}">
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="2,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="18"
Text="&#xe8c0;" />
<TextBlock
VerticalAlignment="Center"
FontSize="14"
Text="新建" />
</StackPanel>
</Button>
<Button
Command="{Binding LinConfigProCmd}"
CommandParameter="Edit"
Style="{StaticResource btnStyle}">
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="2,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="18"
Text="&#xe73a;" />
<TextBlock
VerticalAlignment="Center"
FontSize="14"
Text="修改" />
</StackPanel>
</Button>
<Button
Command="{Binding LinConfigProCmd}"
CommandParameter="Delete"
Style="{StaticResource btnStyle}">
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="2,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="18"
Text="&#xe748;" />
<TextBlock
VerticalAlignment="Center"
FontSize="14"
Text="删除" />
</StackPanel>
</Button>
<Button
Command="{Binding LinConfigProCmd}"
CommandParameter="Active"
Style="{StaticResource btnStyle}">
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="2,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="18"
Text="&#xe8fa;" />
<TextBlock
VerticalAlignment="Center"
FontSize="14"
Text="激活" />
</StackPanel>
</Button>
</StackPanel>
<DataGrid
x:Name="LinConfigDatagrid"
Grid.Row="2"
AutoGenerateColumns="False"
CanUserAddRows="False"
HeadersVisibility="Column"
IsEnabled="{Binding IsLINConfigDatagridActive}"
ItemsSource="{Binding ListCanLinConfigPro}"
SelectedItem="{Binding SelectCanLinConfigPro, Mode=TwoWay}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<prism:InvokeCommandAction
Command="{Binding LinConfigProGridSelectionChangedCmd}"
CommandParameter="{Binding ElementName=LinConfigDatagrid, Path=SelectedItem}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding ConfigName}" Header="名称" Width="*" />
</DataGrid.Columns>
</DataGrid>
</Grid>
</materialDesign:Card>
<materialDesign:Card
Grid.Column="1"
Margin="3"
Background="{DynamicResource MaterialDesignLightBackground}"
Foreground="{DynamicResource PrimaryHueLightForegroundBrush}"
UniformCornerRadius="5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition Height="60" />
<RowDefinition Height="40" />
<RowDefinition Height="200" />
<RowDefinition Height="40" />
<RowDefinition Height="220" />
<RowDefinition Height="40" />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock
Margin="10,0"
VerticalAlignment="Center"
FontSize="18"
FontWeight="Bold"
Text="参数操作" />
<StackPanel
Grid.Row="1"
Margin="0,0,15,0"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Button
Margin="5,0"
Command="{Binding LinOpCmd}"
CommandParameter="Open"
Foreground="White">
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="2,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="18"
Text="&#xe743;" />
<TextBlock
VerticalAlignment="Center"
FontSize="14"
Text="{Binding ConnectButtonText}" />
</StackPanel>
</Button>
<Button
Margin="5,0"
Command="{Binding LinOpCmd}"
CommandParameter="Close"
Foreground="White">
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="2,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="18"
Text="&#xe7fb;" />
<TextBlock
VerticalAlignment="Center"
FontSize="14"
Text="{Binding CloseButtonText}" />
</StackPanel>
</Button>
<Button
Margin="5,0"
Command="{Binding LinOpCmd}"
CommandParameter="Parse"
Foreground="White">
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="2,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="18"
Text="&#xe93f;" />
<TextBlock
VerticalAlignment="Center"
FontSize="14"
Text="解析LDF" />
</StackPanel>
</Button>
<Button
Margin="5,0"
Command="{Binding LinOpCmd}"
CommandParameter="Save"
Foreground="White">
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="2,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="18"
Text="&#xe936;" />
<TextBlock
VerticalAlignment="Center"
FontSize="14"
Text="保存配置" />
</StackPanel>
</Button>
</StackPanel>
<TextBlock
Grid.Row="2"
Margin="10,0"
VerticalAlignment="Center"
FontSize="18"
FontWeight="Bold"
Text="状态" />
<Grid Grid.Row="3">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="10,0,2,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="20"
Text="&#xe977;" />
<TextBlock
Width="80"
Style="{StaticResource TextBlockStyle}"
Text="报文状态:" />
<Border
Width="50"
Margin="0,12"
BorderThickness="1"
CornerRadius="5">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="Gray" />
<Style.Triggers>
<DataTrigger Binding="{Binding ZlgLinDriveService.IsSendOk}" Value="true">
<Setter Property="Background" Value="LimeGreen" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock
Margin="5"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="White"
Text="发送" />
</Border>
<Border
Width="50"
Margin="2,12"
BorderThickness="1"
CornerRadius="5">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="Gray" />
<Style.Triggers>
<DataTrigger Binding="{Binding ZlgLinDriveService.IsReviceOk}" Value="true">
<Setter Property="Background" Value="LimeGreen" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock
Margin="5"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="White"
Text="接收" />
</Border>
</StackPanel>
<StackPanel Grid.Column="1" Orientation="Horizontal">
<TextBlock
Margin="10,0,2,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="20"
Text="&#xe796;" />
<TextBlock
Width="90"
Style="{StaticResource TextBlockStyle}"
Text="启用调度表:" />
<ToggleButton
Width="40"
Margin="2,2"
Command="{Binding SchEnableCmd}"
IsChecked="{Binding CurrentSchEnable, Mode=TwoWay}"
Style="{StaticResource MaterialDesignSwitchToggleButton}"
ToolTip="启用调度表" />
</StackPanel>
<StackPanel Grid.Row="1" Orientation="Horizontal">
<TextBlock
Margin="10,0,2,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="18"
Text="&#xe8fa;" />
<TextBlock Style="{StaticResource TextBlockStyle}" Text="LIN连接状态" />
<Border
Width="50"
Margin="5,10"
Padding="5"
CornerRadius="3">
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<DataTrigger Binding="{Binding ZlgLinDriveService.OpenState}" Value="True">
<Setter Property="Background" Value="LimeGreen" />
</DataTrigger>
<DataTrigger Binding="{Binding ZlgLinDriveService.OpenState}" Value="False">
<Setter Property="Background" Value="Gray" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="White"
Text="连接" />
</Border>
</StackPanel>
<StackPanel
Grid.Row="2"
Grid.Column="0"
Orientation="Horizontal">
<Button
Margin="15,0,5,0"
Command="{Binding LinOpCmd}"
CommandParameter="CycleSend">
<Button.Style>
<Style BasedOn="{StaticResource MaterialDesignFlatDarkBgButton}" TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding ZlgLinDriveService.IsCycleSend}" Value="true">
<Setter Property="Background" Value="LimeGreen" />
<Setter Property="Foreground" Value="White" />
</DataTrigger>
<DataTrigger Binding="{Binding ZlgLinDriveService.IsCycleSend}" Value="false">
<Setter Property="Background" Value="Gray" />
<Setter Property="Foreground" Value="White" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="2,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="18"
Text="&#xe796;" />
<TextBlock
VerticalAlignment="Center"
FontSize="14"
Text="循环发送" />
</StackPanel>
</Button>
<Button
Margin="5,0"
Command="{Binding LinOpCmd}"
CommandParameter="CycleRecive">
<Button.Style>
<Style BasedOn="{StaticResource MaterialDesignFlatDarkBgButton}" TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding ZlgLinDriveService.IsCycleRevice}" Value="true">
<Setter Property="Background" Value="LimeGreen" />
<Setter Property="Foreground" Value="White" />
</DataTrigger>
<DataTrigger Binding="{Binding ZlgLinDriveService.IsCycleRevice}" Value="false">
<Setter Property="Background" Value="Gray" />
<Setter Property="Foreground" Value="White" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="2,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="18"
Text="&#xe73a;" />
<TextBlock
VerticalAlignment="Center"
FontSize="14"
Text="循环接收" />
</StackPanel>
</Button>
</StackPanel>
</Grid>
<TextBlock
Grid.Row="4"
Margin="10,0"
VerticalAlignment="Center"
FontSize="18"
FontWeight="Bold"
Text="参数信息" />
<Grid Grid.Row="5">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="10,0,10,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="18"
Text="&#xe9f8;" />
<TextBlock Style="{StaticResource TextBlockStyle}" Text="波特率" />
<TextBox Style="{StaticResource TextBoxStyle}" Text="{Binding LinBaudRate, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
<StackPanel Grid.Column="1" Orientation="Horizontal">
<TextBlock
Margin="10,0,10,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="18"
Text="&#xe9f8;" />
<TextBlock Style="{StaticResource TextBlockStyle}" Text="循环周期(ms)" />
<TextBox Style="{StaticResource TextBoxStyle}" Text="{Binding CurrentCycle, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</Grid>
<TextBlock
Grid.Row="6"
Margin="10,0"
VerticalAlignment="Center"
FontSize="18"
FontWeight="Bold"
Text="写入读取操作" />
<Grid Grid.Row="7">
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel
Margin="0,0,15,0"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Button
Margin="5,0"
Command="{Binding OpenRwDialogCmd}"
Foreground="White"
IsEnabled="{Binding IsRwEditable}">
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="2,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="18"
Text="&#xe796;" />
<TextBlock
VerticalAlignment="Center"
FontSize="14"
Text="读写设置" />
</StackPanel>
</Button>
<Button
Margin="15,0,0,0"
Command="{Binding ScheduleConfigCmd}"
Foreground="White">
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="2,0"
VerticalAlignment="Center"
FontFamily="/Assets/Fonts/#iconfont"
FontSize="18"
Text="&#xe796;" />
<TextBlock
VerticalAlignment="Center"
FontSize="14"
Text="调度表" />
</StackPanel>
</Button>
</StackPanel>
<TextBlock
Grid.Row="1"
Margin="10"
VerticalAlignment="Center"
FontSize="16"
Text="提示:点击【读写设置】在弹窗中统一管理写入/读取配置(含规则与默认值)。"
TextWrapping="Wrap" />
</Grid>
</Grid>
</materialDesign:Card>
</Grid>
<materialDesign:Card
Grid.Row="1"
Grid.Column="1"
Margin="3"
Background="{DynamicResource MaterialDesignLightBackground}"
Foreground="{DynamicResource PrimaryHueLightForegroundBrush}"
UniformCornerRadius="5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock
Margin="10,0"
VerticalAlignment="Center"
FontSize="18"
FontWeight="Bold"
Text="信号" />
<DataGrid
Grid.Row="1"
AutoGenerateColumns="False"
CanUserAddRows="False"
HeadersVisibility="Column"
IsReadOnly="True"
ItemsSource="{Binding ZlgLinDriveService.ListLinLdfModel}">
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<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 Width="200" Binding="{Binding MsgName}">
<DataGridTextColumn.Header>
<TextBlock FontWeight="Bold" Text="帧名称" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Width="140" Binding="{Binding Name}">
<DataGridTextColumn.Header>
<TextBlock FontWeight="Bold" Text="中文名称" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Width="*" Binding="{Binding SignalName}">
<DataGridTextColumn.Header>
<TextBlock FontWeight="Bold" Text="信号名称" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Width="90" Binding="{Binding SignalUnit}">
<DataGridTextColumn.Header>
<TextBlock FontWeight="Bold" Text="单位" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Width="150" Binding="{Binding SignalRtValue}">
<DataGridTextColumn.Header>
<TextBlock FontWeight="Bold" Text="实时值" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</materialDesign:Card>
</Grid>
</UserControl>

View File

@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace CapMachine.Wpf.Views
{
/// <summary>
/// ZlgLinDriveConfigView.xaml 的交互逻辑
/// </summary>
public partial class ZlgLinDriveConfigView : UserControl
{
public ZlgLinDriveConfigView()
{
InitializeComponent();
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,6 @@
宸叉垚鍔熺敓鎴愩€?
2541 涓鍛?
0 涓敊璇?
宸茬敤鏃堕棿 00:00:15.76

6
build-errors-sln.txt Normal file
View File

@@ -0,0 +1,6 @@
宸叉垚鍔熺敓鎴愩€?
6 涓鍛?
0 涓敊璇?
宸茬敤鏃堕棿 00:00:01.52

13
build-errors.txt Normal file
View File

@@ -0,0 +1,13 @@
E:\MyTest\VS2022\CapMachine\CapMachine\CapMachine.Wpf\App.xaml.cs(214,46): error CS0246: 鏈兘鎵惧埌绫诲瀷鎴栧懡鍚嶇┖闂村悕鈥淒ialogCANFdSchConfigView鈥?鏄惁缂哄皯 using 鎸囦护鎴栫▼搴忛泦寮曠敤?) [E:\MyTest\VS2022\CapMachine\CapMachine\CapMachine.Wpf\CapMachine.Wpf_zrtyujqf_wpftmp.csproj]
E:\MyTest\VS2022\CapMachine\CapMachine\CapMachine.Wpf\App.xaml.cs(214,72): error CS0246: 鏈兘鎵惧埌绫诲瀷鎴栧懡鍚嶇┖闂村悕鈥淒ialogCANFdSchConfigViewModel鈥?鏄惁缂哄皯 using 鎸囦护鎴栫▼搴忛泦寮曠敤?) [E:\MyTest\VS2022\CapMachine\CapMachine\CapMachine.Wpf\CapMachine.Wpf_zrtyujqf_wpftmp.csproj]
E:\MyTest\VS2022\CapMachine\CapMachine\CapMachine.Wpf\ViewModels\CANFDConfigViewModel.cs(530,65): error CS1061: 鈥淭oomossCanFD鈥濇湭鍖呭惈鈥淟oadCmdDataToDrive鈥濈殑瀹氫箟锛屽苟涓旀壘涓嶅埌鍙帴鍙楃涓€涓€淭oomossCanFD鈥濈被鍨嬪弬鏁扮殑鍙闂墿灞曟柟娉曗€淟oadCmdDataToDrive鈥?鏄惁缂哄皯 using 鎸囦护鎴栫▼搴忛泦寮曠敤?) [E:\MyTest\VS2022\CapMachine\CapMachine\CapMachine.Wpf\CapMachine.Wpf_zrtyujqf_wpftmp.csproj]
E:\MyTest\VS2022\CapMachine\CapMachine\CapMachine.Wpf\ViewModels\CANFDConfigViewModel.cs(752,49): error CS1061: 鈥淭oomossCanFD鈥濇湭鍖呭惈鈥淯pdateSchDataByCmdDataChanged鈥濈殑瀹氫箟锛屽苟涓旀壘涓嶅埌鍙帴鍙楃涓€涓€淭oomossCanFD鈥濈被鍨嬪弬鏁扮殑鍙闂墿灞曟柟娉曗€淯pdateSchDataByCmdDataChanged鈥?鏄惁缂哄皯 using 鎸囦护鎴栫▼搴忛泦寮曠敤?) [E:\MyTest\VS2022\CapMachine\CapMachine\CapMachine.Wpf\CapMachine.Wpf_zrtyujqf_wpftmp.csproj]
E:\MyTest\VS2022\CapMachine\CapMachine\CapMachine.Wpf\ViewModels\CANFDConfigViewModel.cs(1021,49): error CS1061: 鈥淭oomossCanFD鈥濇湭鍖呭惈鈥淪chEnable鈥濈殑瀹氫箟锛屽苟涓旀壘涓嶅埌鍙帴鍙楃涓€涓€淭oomossCanFD鈥濈被鍨嬪弬鏁扮殑鍙闂墿灞曟柟娉曗€淪chEnable鈥?鏄惁缂哄皯 using 鎸囦护鎴栫▼搴忛泦寮曠敤?) [E:\MyTest\VS2022\CapMachine\CapMachine\CapMachine.Wpf\CapMachine.Wpf_zrtyujqf_wpftmp.csproj]
E:\MyTest\VS2022\CapMachine\CapMachine\CapMachine.Wpf\ViewModels\CANFDConfigViewModel.cs(1181,57): error CS1061: 鈥淭oomossCanFD鈥濇湭鍖呭惈鈥淪chEnable鈥濈殑瀹氫箟锛屽苟涓旀壘涓嶅埌鍙帴鍙楃涓€涓€淭oomossCanFD鈥濈被鍨嬪弬鏁扮殑鍙闂墿灞曟柟娉曗€淪chEnable鈥?鏄惁缂哄皯 using 鎸囦护鎴栫▼搴忛泦寮曠敤?) [E:\MyTest\VS2022\CapMachine\CapMachine\CapMachine.Wpf\CapMachine.Wpf_zrtyujqf_wpftmp.csproj]
E:\MyTest\VS2022\CapMachine\CapMachine\CapMachine.Wpf\ViewModels\CANFDConfigViewModel.cs(1183,39): error CS1061: 鈥淐anFdDriveService鈥濇湭鍖呭惈鈥淟istCANFdScheduleConfig鈥濈殑瀹氫箟锛屽苟涓旀壘涓嶅埌鍙帴鍙楃涓€涓€淐anFdDriveService鈥濈被鍨嬪弬鏁扮殑鍙闂墿灞曟柟娉曗€淟istCANFdScheduleConfig鈥?鏄惁缂哄皯 using 鎸囦护鎴栫▼搴忛泦寮曠敤?) [E:\MyTest\VS2022\CapMachine\CapMachine\CapMachine.Wpf\CapMachine.Wpf_zrtyujqf_wpftmp.csproj]
鐢熸垚澶辫触銆?
1233 涓鍛?
7 涓敊璇?
宸茬敤鏃堕棿 00:00:06.42