diff --git a/ADP.sln b/ADP.sln index 8070f1d..4a7e18f 100644 --- a/ADP.sln +++ b/ADP.sln @@ -35,10 +35,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UpdateInfoMoudle", "UpdateI EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonitorModule", "MonitorModule\MonitorModule.csproj", "{850A5BE8-0BC1-4D6A-8351-DD812C9C4427}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CAN驱动", "CAN驱动\CAN驱动.csproj", "{D1868672-0132-4B1A-A393-C2CF49A25E74}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeviceEditModule", "DeviceEditModule\DeviceEditModule.csproj", "{170AD4C1-189D-4FBE-B10D-2A4304527834}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "USBCANFD", "USBCANFD\USBCANFD.csproj", "{C011DC26-E2F8-4F40-AD62-C5D50BA56CE8}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -105,14 +105,14 @@ Global {850A5BE8-0BC1-4D6A-8351-DD812C9C4427}.Debug|Any CPU.Build.0 = Debug|Any CPU {850A5BE8-0BC1-4D6A-8351-DD812C9C4427}.Release|Any CPU.ActiveCfg = Release|Any CPU {850A5BE8-0BC1-4D6A-8351-DD812C9C4427}.Release|Any CPU.Build.0 = Release|Any CPU - {D1868672-0132-4B1A-A393-C2CF49A25E74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D1868672-0132-4B1A-A393-C2CF49A25E74}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D1868672-0132-4B1A-A393-C2CF49A25E74}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D1868672-0132-4B1A-A393-C2CF49A25E74}.Release|Any CPU.Build.0 = Release|Any CPU {170AD4C1-189D-4FBE-B10D-2A4304527834}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {170AD4C1-189D-4FBE-B10D-2A4304527834}.Debug|Any CPU.Build.0 = Debug|Any CPU {170AD4C1-189D-4FBE-B10D-2A4304527834}.Release|Any CPU.ActiveCfg = Release|Any CPU {170AD4C1-189D-4FBE-B10D-2A4304527834}.Release|Any CPU.Build.0 = Release|Any CPU + {C011DC26-E2F8-4F40-AD62-C5D50BA56CE8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C011DC26-E2F8-4F40-AD62-C5D50BA56CE8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C011DC26-E2F8-4F40-AD62-C5D50BA56CE8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C011DC26-E2F8-4F40-AD62-C5D50BA56CE8}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/ADP/ADP.csproj b/ADP/ADP.csproj index cbaa45c..52c2fdc 100644 --- a/ADP/ADP.csproj +++ b/ADP/ADP.csproj @@ -19,7 +19,6 @@ - @@ -31,6 +30,7 @@ + diff --git a/CAN驱动/CAN.cs b/CAN驱动/CAN.cs deleted file mode 100644 index 4056f0b..0000000 --- a/CAN驱动/CAN.cs +++ /dev/null @@ -1,417 +0,0 @@ -using Common.Attributes; -using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics; -using System.IO; -using System.Runtime.InteropServices; -using System.Text.Json; -using TSMaster; -namespace CAN驱动 -{ - [ADPCommand] - public class CAN - { - - public static event Action ConnectEvent; - public static event Action DisConnectEvent; - public static bool ConnectFlag { get; set; } = false; - - /// - /// 初始化CAN驱动 - /// - /// - /// - /// - [Browsable(false)] - public static int Init(string ProjectName, string? filePath = null) - { - if (string.IsNullOrEmpty(filePath)) - { - return TsMasterApi.initialize_lib_tsmaster(ProjectName); - } - else - { - return TsMasterApi.initialize_lib_tsmaster_with_project(ProjectName, Path.GetFullPath(filePath)); - } - } - - /// - /// 释放CAN驱动 - /// - [Browsable(false)] - public static void Release() - { - TsMasterApi.finalize_lib_tsmaster(); - } - - public static int obj = 0; - public static TCANFDQueueEvent_Win32 listener; - [Browsable(false)] - public static int RegisterListener(TCANFDQueueEvent_Win32 listenEvent) - { - listener = listenEvent; - var re = TsMasterApi.tsapp_register_event_canfd(ref obj, listener); - Debug.Assert(re == 0); - return re; - } - - [Browsable(false)] - public static int UnRegisterListener(TCANFDQueueEvent_Win32 listenEvent = null) - { - if (listenEvent is not null) - { - listener = listenEvent; - } - var re = TsMasterApi.tsapp_unregister_event_canfd(ref obj, listener); - Debug.Assert(re == 0); - return re; - } - - /// - /// 连接 - /// - /// - public static int Connect() - { - - var re = TsMasterApi.tsapp_connect(); - if (re == 0) - { - re = TsMasterApi.tscom_can_rbs_start(); - if (re == 0) - { - ConnectFlag = true; - Task.Run(() => ConnectEvent?.Invoke()); - } - else - { - Debug.WriteLine($"CAN_RBS启动失败,错误代码:{re}"); - } - } - else - { - Debug.WriteLine($"CAN连接失败,错误代码:{re}"); - return re; - } - return re; - } - - /// - /// 断开 - /// - /// - public static int DisConnect() - { - var re = TsMasterApi.tsapp_disconnect(); - //Debug.Assert(re == 0); - if (re == 0) - { - ConnectFlag = false; - Task.Run(() => DisConnectEvent?.Invoke()); - } - else - { - Debug.WriteLine($"断开CAN连接失败,错误代码:{re}"); - } - - - return re; - } - - /// - /// 加载DBC - /// - /// - /// - /// - /// - [Browsable(false)] - public static int LoadDBC(string filePath, int[] channel, out uint databaseID) - { - databaseID = 0; - - var re = TsMasterApi.tsdb_load_can_db(Path.GetFullPath(filePath), string.Join(",", channel), ref databaseID); - foreach (var item in channel) - { - DBCParse.parse(databaseID, item); - DBCParse.rbs_parse(databaseID, item); - } - - return re; - } - - /// - /// 卸载DBC - /// - /// - /// - [Browsable(false)] - public static int UnLoadDBC(uint? databaseID = null) - { - if (databaseID == null) - { - return TsMasterApi.tsdb_unload_can_dbs(); - } - else - { - return TsMasterApi.tsdb_unload_can_db(databaseID.Value); - } - } - - /// - /// 开始记录日志 - /// - /// - /// - public static int StartLogging(string filePath) - { - return TsMasterApi.tsapp_start_logging(Path.GetFullPath(filePath)); - } - - /// - /// 结束记录日志 - /// - /// - public static int StopLogging() - { - return TsMasterApi.tsapp_stop_logging(); - } - - /// - /// 弹出通道映射窗口 - /// - /// - /// - [Browsable(false)] - public static int ShowChannelMappingWindow(bool isWait = false) - { - return TsMasterApi.tsapp_show_tsmaster_window("Hardware", isWait); - } - - /// - /// 获取信号值 - /// - /// - /// - /// - /// - public static double GetSignalValue(byte channel, string AMsgName, string ASgnName) - { - double value = double.NaN; - var find = DBCParse.MsgDatabase[channel].First(s => s.msg_name == AMsgName); - var re = TsMasterApi.tsdb_get_signal_value_canfd(ref find.ACANFD, AMsgName, ASgnName, ref value); - - return value; - } - - /// - /// 获取信号值 - /// - /// - /// - /// - /// - /// - [Browsable(false)] - public static int GetSignalValue(byte channel, string AMsgName, string ASgnName, ref double value) - { - var find = DBCParse.MsgDatabase[channel].First(s => s.msg_name == AMsgName); - return TsMasterApi.tsdb_get_signal_value_canfd(ref find.ACANFD, AMsgName, ASgnName, ref value); - } - - /// - /// 获取信号值 - /// - /// - /// - /// - /// - /// - - [Browsable(false)] - public static int GetSignalValue(ref TLIBCANFD ACANFD, string AMsgName, string ASgnName, ref double value) - { - return TsMasterApi.tsdb_get_signal_value_canfd(ref ACANFD, AMsgName, ASgnName, ref value); - } - - /// - /// 设置信号值 - /// - /// - /// - /// - /// - /// - /// - /// - /// - public static int SetSignalValue(byte channel, string AMsgName, string ASgnName, double AValue, bool isSend = false, float sendPeriod = 0) - { - var find = DBCParse.MsgDatabase[channel].First(s => s.msg_name == AMsgName); - find.ACANFD.FIdxChn = channel; - var re = TsMasterApi.tsdb_set_signal_value_canfd(ref find.ACANFD, AMsgName, ASgnName, AValue); - Debug.Assert(re == 0); - if (re != 0) throw new Exception($"设置报文失败!返回代码{re}"); - if (re != 0) return re; - if (isSend) - { - if (sendPeriod == 0) - { - return TsMasterApi.tsapp_transmit_canfd_async(ref find.ACANFD); - } - else - { - return TsMasterApi.tsapp_add_cyclic_msg_canfd(ref find.ACANFD, sendPeriod); - } - } - return 0; - } - - - /// - /// 设置信号值 - /// - /// - /// - /// - /// - public static int SetSignalValue(APP_CHANNEL channel, string AMsgName, float sendPeriod = 0) - { - var find = DBCParse.MsgDatabase[(byte)channel].First(s => s.msg_name == AMsgName); - find.ACANFD.FIdxChn = (byte)channel; - if (sendPeriod == 0) - { - return TsMasterApi.tsapp_transmit_canfd_async(ref find.ACANFD); - } - else - { - return TsMasterApi.tsapp_add_cyclic_msg_canfd(ref find.ACANFD, sendPeriod); - } - } - - /// - /// 发送自定义报文 - /// - /// - [Browsable(false)] - public static int SetMsg(TLIBCANFD msg) - { - return TsMasterApi.tsapp_transmit_canfd_async(ref msg); - } - - /// - /// 设置报文 - /// - /// 通道 - /// DBC数据库ID - /// 报文数组 - public static void SetMsg(APP_CHANNEL channel, int ID, byte[] bytes) - { - var find = DBCParse.MsgDatabase[(byte)channel].FirstOrDefault(s => s.ACANFD.FIdentifier == ID); - if (find != null) - { - Array.Copy(bytes, 0, find.ACANFD.FData, 0, Math.Min(bytes.Length, find.ACANFD.FData.Length)); - } - } - - /// - /// 发送自定义报文 - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public static int SendMsg(APP_CHANNEL AIdxChn, int AID, bool AIsTx, bool AIsExt, - bool AIsRemote, byte ADLC, byte[] ADataArray, bool AIsFD = true, bool AIsBRS = false, - bool isUpdateDBCDatabase = false, float sendPeriod = 0) - { - var send = new TLIBCANFD - { - FIdxChn = (byte)AIdxChn, - FProperties = 0, - FIdentifier = AID, - FDLC = ADLC, - FTimeUS = 0uL, - FData = new byte[64], - FFDProperties = 0, - FIsTx = AIsTx, - FIsError = false, - FIsExt = AIsExt, - FIsRemote = AIsRemote, - FIsFD = AIsFD, - FIsBRS = AIsBRS - }; - int length = Math.Min(ADataArray.Length, 64); - Array.Copy(ADataArray, 0, send.FData, 0, length); - int re; - if (sendPeriod == 0) - { - re = TsMasterApi.tsapp_transmit_canfd_async(ref send); - } - else - { - re = TsMasterApi.tsapp_add_cyclic_msg_canfd(ref send, sendPeriod); - } - - if (isUpdateDBCDatabase) - { - var find = DBCParse.MsgDatabase[(byte)AIdxChn].FirstOrDefault(s => s.ACANFD.FIdentifier == send.FIdentifier); - if (find != null) - { - Array.Copy(send.FData, 0, find.ACANFD.FData, 0, Math.Min(ADataArray.Length, find.ACANFD.FData.Length)); - } - } - return re; - } - - /// - /// 添加循环发送报文 - /// - /// - /// - /// - /// - public static int AddCyclicMsg(byte channel, string AMsgName, float sendPeriod) - { - var find = DBCParse.MsgDatabase[channel].First(s => s.msg_name == AMsgName); - find.ACANFD.FIdxChn = channel; - return TsMasterApi.tsapp_add_cyclic_msg_canfd(ref find.ACANFD, sendPeriod); - } - - /// - /// 清除循环发送的报文 - /// - /// - public static int DeleteCyclicMsgs() - { - return TsMasterApi.tsapp_delete_cyclic_msgs(); - } - - /// - /// 获取错误提示 - /// - /// 错误代码 - /// - public static string GetErrorDescription(int errorCode) - { - IntPtr ADesc = IntPtr.Zero; - TsMasterApi.tsapp_get_error_description(errorCode, ref ADesc); - - if (ADesc == IntPtr.Zero) return $"未知错误代码: {errorCode}"; - - // 假设返回的是 ANSI 字符串(如 C 的 char*) - string? description = Marshal.PtrToStringAnsi(ADesc); - - return description ?? $"未知错误代码: {errorCode}"; - } - - } -} diff --git a/CAN驱动/DBCParse.cs b/CAN驱动/DBCParse.cs deleted file mode 100644 index 47ab723..0000000 --- a/CAN驱动/DBCParse.cs +++ /dev/null @@ -1,269 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Text; -using System.Text.Json.Serialization; -using System.Threading.Tasks; -using TSMaster; - -namespace CAN驱动 -{ - public struct can_network - { - [JsonInclude] - public string network_name; - [JsonInclude] - public can_node[] can_nodes; - - } - public struct can_node - { - [JsonInclude] - public string node_name; - [JsonInclude] - public can_message[] tx_can_Messages; - [JsonInclude] - public can_message[] rx_can_Messages; - } - public struct can_message - { - [JsonInclude] - public string message_name; - [JsonInclude] - public string[] signals; - } - - public class _Msg_ - { - public int msg_type { get; set; } - public int msg_id { get; set; } - public int msg_cyclic { get; set; } - public byte msg_dlc { get; set; } - public string msg_name { get; set; } - public string[] signal_Name { get; set; } - public int msg_brs { get; set; } - public TLIBCANFD ACANFD; - } - public enum Msg_Type - { - CAN = 0, - CANFD = 1, - } - /// - /// DBC 文件信息类型枚举,用于指定 tsdb_get_can_db_info 查询的内容类型。 - /// - public enum DBCINFO - { - /// 网络名称 - NETWORK_Name = 0, - - /// CAN 报文总数 - CAN_MSG_COUNT = 11, - /// CAN FD 报文总数 - CANFD_MSG_COUNT = 12, - /// 节点总数 - NODE_COUNT = 14, - - /// 信号名称 - SIGNAL_NAME = 33, - - /// CAN 报文类型 - CAN_MSG_TYPE = 40, - /// CAN 报文 DLC(数据长度码) - CAN_MSG_DLC = 41, - /// CAN 报文 ID - CAN_MSG_ID = 42, - /// CAN 报文周期 - CAN_MSG_CYCLIC = 43, - /// CAN 报文名称 - CAN_MSG_NAME = 44, - /// CAN 报文信号数量 - CAN_MSG_SIGNAL_COUNT = 47, - /// CAN 报文信号名称索引 - CAN_MSG_SIGNAL_NAME_INDEX = 48, - - /// CAN FD 报文类型 - CANFD_MSG_TYPE = 60, - /// CAN FD 报文 DLC(数据长度码) - CANFD_MSG_DLC = 61, - /// CAN FD 报文 ID - CANFD_MSG_ID = 62, - /// CAN FD 报文周期 - CANFD_MSG_CYCLIC = 63, - /// CAN FD 报文名称 - CANFD_MSG_NAME = 64, - /// CAN FD 报文信号数量 - CANFD_MSG_SIGNAL_COUNT = 67, - /// CAN FD 报文信号名称索引 - CANFD_MSG_SIGNAL_NAME_INDEX = 68, - - /// 节点名称 - NODE_Name = 101, - /// 节点发送 CAN 报文数量 - NODE_TX_CAN_COUNT = 103, - /// 节点发送 CAN 报文索引 - NODE_TX_CAN_MESSAGE_INDEX = 104, - /// 节点接收 CAN 报文数量 - NODE_RX_CAN_COUNT = 105, - /// 节点接收 CAN 报文索引 - NODE_RX_CAN_MESSAGE_INDEX = 106, - /// 节点发送 CAN FD 报文数量 - NODE_TX_CANFD_COUNT = 107, - /// 节点发送 CAN FD 报文索引 - NODE_TX_CANFD_MESSAGE_INDEX = 108, - /// 节点接收 CAN FD 报文数量 - NODE_RX_CANFD_COUNT = 109, - /// 节点接收 CAN FD 报文索引 - NODE_RX_CANFD_MESSAGE_INDEX = 110, - } - - public class DBCParse - { - public static List> MsgDatabase { get; set; } = - [.. Enumerable.Range(0, 12).Select(_ => new List<_Msg_>())]; - public static can_network can_Network; - public static string tsdb_get_can_db_info(uint ADatabaseId, int AType, int AIndex, int ASubIndex) - { - nint tmpInt = new nint(); - int ret = TsMasterApi.tsdb_get_can_db_info(ADatabaseId, AType, AIndex, ASubIndex, ref tmpInt); - if (ret == 0) - { - return Marshal.PtrToStringAnsi(tmpInt) ?? ""; - } - else - return ""; - } - - public static void parse(uint Aid, int channel) - { - int CANcount = int.Parse(tsdb_get_can_db_info(Aid, (int)DBCINFO.CAN_MSG_COUNT, 0, 0)); - for (int i = 0; i < CANcount; i++) - { - _Msg_ temp = new _Msg_(); - temp.msg_type = int.Parse(tsdb_get_can_db_info(Aid, (int)DBCINFO.CAN_MSG_TYPE, i, 0)); - temp.msg_dlc = byte.Parse(tsdb_get_can_db_info(Aid, (int)DBCINFO.CAN_MSG_DLC, i, 0)); - temp.msg_id = int.Parse(tsdb_get_can_db_info(Aid, (int)DBCINFO.CAN_MSG_ID, i, 0)); - temp.msg_name = tsdb_get_can_db_info(Aid, (int)DBCINFO.CAN_MSG_NAME, i, 0); - temp.msg_cyclic = int.Parse(tsdb_get_can_db_info(Aid, (int)DBCINFO.CAN_MSG_CYCLIC, i, 0)); - temp.signal_Name = new string[int.Parse(tsdb_get_can_db_info(Aid, (int)DBCINFO.CAN_MSG_SIGNAL_COUNT, i, 0))]; - for (int signalcount = 0; signalcount < temp.signal_Name.Length; signalcount++) - { - temp.signal_Name[signalcount] = tsdb_get_can_db_info(Aid, (int)DBCINFO.SIGNAL_NAME, int.Parse(tsdb_get_can_db_info(Aid, (int)DBCINFO.CAN_MSG_SIGNAL_NAME_INDEX, i, signalcount)), 0); - } - temp.msg_brs = 0; - temp.ACANFD = new TLIBCANFD(APP_CHANNEL.CHN1, - temp.msg_id, - true, - temp.msg_type == 1 || temp.msg_type == 4 ? true : false, - false, temp.msg_dlc, - temp.msg_type < 2 ? false : true, - false); - MsgDatabase[channel].Add(temp); - - } - - CANcount = int.Parse(tsdb_get_can_db_info(Aid, (int)DBCINFO.CANFD_MSG_COUNT, 0, 0)); - for (int i = 0; i < CANcount; i++) - { - _Msg_ temp = new _Msg_(); - temp.msg_type = int.Parse(tsdb_get_can_db_info(Aid, (int)DBCINFO.CANFD_MSG_TYPE, i, 0)); - temp.msg_dlc = byte.Parse(tsdb_get_can_db_info(Aid, (int)DBCINFO.CANFD_MSG_DLC, i, 0)); - temp.msg_id = int.Parse(tsdb_get_can_db_info(Aid, (int)DBCINFO.CANFD_MSG_ID, i, 0)); - temp.msg_name = tsdb_get_can_db_info(Aid, (int)DBCINFO.CANFD_MSG_NAME, i, 0); - temp.msg_cyclic = int.Parse(tsdb_get_can_db_info(Aid, (int)DBCINFO.CANFD_MSG_CYCLIC, i, 0)); - temp.signal_Name = new string[int.Parse(tsdb_get_can_db_info(Aid, (int)DBCINFO.CANFD_MSG_SIGNAL_COUNT, i, 0))]; - for (int signalcount = 0; signalcount < temp.signal_Name.Length; signalcount++) - { - temp.signal_Name[signalcount] = tsdb_get_can_db_info(Aid, (int)DBCINFO.SIGNAL_NAME, int.Parse(tsdb_get_can_db_info(Aid, (int)DBCINFO.CANFD_MSG_SIGNAL_NAME_INDEX, i, signalcount)), 0); - } - temp.msg_brs = int.Parse(tsdb_get_can_db_info(Aid, 69, i, 0)); - temp.ACANFD = new TLIBCANFD(APP_CHANNEL.CHN1, temp.msg_id, true, temp.msg_type == 1 || temp.msg_type == 4 ? true : false, false, temp.msg_dlc, temp.msg_type < 2 ? false : true, temp.msg_brs == 1 ? true : false); - MsgDatabase[channel].Add(temp); - } - MsgDatabase[channel].Sort((a, b) => a.msg_id.CompareTo(b.msg_id)); - } - - public static void rbs_parse(uint AID, int channel) - { - can_Network = new can_network(); - can_Network.network_name = tsdb_get_can_db_info(AID, (int)DBCINFO.NETWORK_Name, 0, 0); - int Node_count = int.Parse(tsdb_get_can_db_info(AID, (int)DBCINFO.NODE_COUNT, 0, 0)); - can_Network.can_nodes = new can_node[Node_count]; - for (int i = 0; i < Node_count; i++) - { - can_Network.can_nodes[i].node_name = tsdb_get_can_db_info(AID, (int)DBCINFO.NODE_Name, i, 0); - int tx_can_count = int.Parse(tsdb_get_can_db_info(AID, (int)DBCINFO.NODE_TX_CAN_COUNT, i, 0)); - int tx_canfd_count = int.Parse(tsdb_get_can_db_info(AID, (int)DBCINFO.NODE_TX_CANFD_COUNT, i, 0)); - - int rx_can_count = int.Parse(tsdb_get_can_db_info(AID, (int)DBCINFO.NODE_RX_CAN_COUNT, i, 0)); - int rx_canfd_count = int.Parse(tsdb_get_can_db_info(AID, (int)DBCINFO.NODE_RX_CANFD_COUNT, i, 0)); - - //获取tx_can结构体 - can_Network.can_nodes[i].tx_can_Messages = new can_message[tx_can_count + tx_canfd_count]; - - can_Network.can_nodes[i].rx_can_Messages = new can_message[rx_can_count + rx_canfd_count]; - //获取报文名以及信号名 - //1 get can_message idnex - //2 get can message name - //3 get signal count - //4 get signal name - int tx_index = -1; - for (int tx_can = 0; tx_can < tx_can_count; tx_can++) - { - int can_message_idnex = int.Parse(tsdb_get_can_db_info(AID, (int)DBCINFO.NODE_TX_CAN_MESSAGE_INDEX, i, tx_can)); - can_Network.can_nodes[i].tx_can_Messages[tx_can].message_name = tsdb_get_can_db_info(AID, (int)DBCINFO.CAN_MSG_NAME, can_message_idnex, 0); - - can_Network.can_nodes[i].tx_can_Messages[tx_can].signals = new string[int.Parse(tsdb_get_can_db_info(AID, (int)DBCINFO.CAN_MSG_SIGNAL_COUNT, can_message_idnex, 0))]; - for (int signalcount = 0; signalcount < can_Network.can_nodes[i].tx_can_Messages[tx_can].signals.Length; signalcount++) - { - can_Network.can_nodes[i].tx_can_Messages[tx_can].signals[signalcount] = tsdb_get_can_db_info(AID, (int)DBCINFO.SIGNAL_NAME, int.Parse(tsdb_get_can_db_info(AID, (int)DBCINFO.CAN_MSG_SIGNAL_NAME_INDEX, can_message_idnex, signalcount)), 0); - } - - tx_index = tx_can; - } - for (int tx_can = 0; tx_can < tx_canfd_count; tx_can++, tx_index++) - { - int can_message_idnex = int.Parse(tsdb_get_can_db_info(AID, (int)DBCINFO.NODE_TX_CANFD_MESSAGE_INDEX, i, tx_can)); - can_Network.can_nodes[i].tx_can_Messages[tx_index + 1].message_name = tsdb_get_can_db_info(AID, (int)DBCINFO.CANFD_MSG_NAME, can_message_idnex, 0); - - can_Network.can_nodes[i].tx_can_Messages[tx_index + 1].signals = new string[int.Parse(tsdb_get_can_db_info(AID, (int)DBCINFO.CANFD_MSG_SIGNAL_COUNT, can_message_idnex, 0))]; - for (int signalcount = 0; signalcount < can_Network.can_nodes[i].tx_can_Messages[tx_index + 1].signals.Length; signalcount++) - { - can_Network.can_nodes[i].tx_can_Messages[tx_index + 1].signals[signalcount] = tsdb_get_can_db_info(AID, (int)DBCINFO.SIGNAL_NAME, int.Parse(tsdb_get_can_db_info(AID, (int)DBCINFO.CANFD_MSG_SIGNAL_NAME_INDEX, can_message_idnex, signalcount)), 0); - } - } - - int rx_index = -1; - for (int tx_can = 0; tx_can < rx_can_count; tx_can++) - { - int can_message_idnex = int.Parse(tsdb_get_can_db_info(AID, (int)DBCINFO.NODE_RX_CAN_MESSAGE_INDEX, i, tx_can)); - can_Network.can_nodes[i].rx_can_Messages[tx_can].message_name = tsdb_get_can_db_info(AID, (int)DBCINFO.CAN_MSG_NAME, can_message_idnex, 0); - - can_Network.can_nodes[i].rx_can_Messages[tx_can].signals = new string[int.Parse(tsdb_get_can_db_info(AID, (int)DBCINFO.CAN_MSG_SIGNAL_COUNT, can_message_idnex, 0))]; - for (int signalcount = 0; signalcount < can_Network.can_nodes[i].rx_can_Messages[tx_can].signals.Length; signalcount++) - { - can_Network.can_nodes[i].rx_can_Messages[tx_can].signals[signalcount] = tsdb_get_can_db_info(AID, (int)DBCINFO.SIGNAL_NAME, int.Parse(tsdb_get_can_db_info(AID, (int)DBCINFO.CAN_MSG_SIGNAL_NAME_INDEX, can_message_idnex, signalcount)), 0); - } - - rx_index = tx_can; - } - for (int tx_can = 0; tx_can < rx_canfd_count; tx_can++, rx_index++) - { - int can_message_idnex = int.Parse(tsdb_get_can_db_info(AID, (int)DBCINFO.NODE_RX_CANFD_MESSAGE_INDEX, i, tx_can)); - can_Network.can_nodes[i].rx_can_Messages[rx_index + 1].message_name = tsdb_get_can_db_info(AID, (int)DBCINFO.CANFD_MSG_NAME, can_message_idnex, 0); - - can_Network.can_nodes[i].rx_can_Messages[rx_index + 1].signals = new string[int.Parse(tsdb_get_can_db_info(AID, (int)DBCINFO.CANFD_MSG_SIGNAL_COUNT, can_message_idnex, 0))]; - for (int signalcount = 0; signalcount < can_Network.can_nodes[i].rx_can_Messages[rx_index + 1].signals.Length; signalcount++) - { - can_Network.can_nodes[i].rx_can_Messages[rx_index + 1].signals[signalcount] = tsdb_get_can_db_info(AID, (int)DBCINFO.SIGNAL_NAME, int.Parse(tsdb_get_can_db_info(AID, (int)DBCINFO.CANFD_MSG_SIGNAL_NAME_INDEX, can_message_idnex, signalcount)), 0); - } - } - } - } - } - - -} diff --git a/CAN驱动/Interop.TSMasterAPI.dll b/CAN驱动/Interop.TSMasterAPI.dll deleted file mode 100644 index 1d1400b..0000000 Binary files a/CAN驱动/Interop.TSMasterAPI.dll and /dev/null differ diff --git a/CAN驱动/TSMaster.dll b/CAN驱动/TSMaster.dll deleted file mode 100644 index a34dd33..0000000 Binary files a/CAN驱动/TSMaster.dll and /dev/null differ diff --git a/DeviceCommand/Devices/N36600.cs b/DeviceCommand/Devices/N36600.cs index ada9ade..a5a430f 100644 --- a/DeviceCommand/Devices/N36600.cs +++ b/DeviceCommand/Devices/N36600.cs @@ -172,7 +172,8 @@ namespace DeviceCommand.Device /// public virtual async Task 快捷设置电压电流(double 电压, double 电流, CancellationToken ct = default) { - string cmd = string.Format(CultureInfo.InvariantCulture, ":APPLY {0:F3},{1:F3}{2}", 电压, 电流, SCPIDelimiter); + // 修正:去除前缀冒号,规范应用 APPL 短格式 + string cmd = string.Format(CultureInfo.InvariantCulture, "APPL {0:F3},{1:F3}{2}", 电压, 电流, SCPIDelimiter); await SendAsync(cmd, ct); } @@ -181,7 +182,8 @@ namespace DeviceCommand.Device /// public virtual async Task 查询快捷电压电流设定(CancellationToken ct = default) { - return await WriteReadAsync($":APPLY?{SCPIDelimiter}", SCPIDelimiter, ct); + // 修正:去除前缀冒号 + return await WriteReadAsync($"APPL?{SCPIDelimiter}", SCPIDelimiter, ct); } #endregion @@ -194,7 +196,8 @@ namespace DeviceCommand.Device [Monitorable("低压直流电源电流")] public virtual async Task 查询实际电流(CancellationToken ct = default) { - return await WriteReadAsync($":MEASure:CURRent?{SCPIDelimiter}", SCPIDelimiter, ct); + // 修正:去除前缀冒号,改用标准 MEAS:CURR? + return await WriteReadAsync($"MEAS:CURR?{SCPIDelimiter}", SCPIDelimiter, ct); } /// @@ -203,16 +206,18 @@ namespace DeviceCommand.Device [Monitorable("低压直流电源功率")] public virtual async Task 查询实际功率(CancellationToken ct = default) { - return await WriteReadAsync($":MEASure:POWer?{SCPIDelimiter}", SCPIDelimiter, ct); + // 修正:去除前缀冒号,改用标准 MEAS:POW? + return await WriteReadAsync($"MEAS:POW?{SCPIDelimiter}", SCPIDelimiter, ct); } /// /// 3.5.3 查询通道输出端子上测得的直流电压值 (V) - /// [Monitorable("低压直流电源电压")] public virtual async Task 查询实际电压(CancellationToken ct = default) { - return await WriteReadAsync($":MEASure:VOLTage?{SCPIDelimiter}", SCPIDelimiter, ct); + // 修正:去除前缀冒号,改用标准 MEAS:VOLT? + return await WriteReadAsync($"MEAS:VOLT?{SCPIDelimiter}", SCPIDelimiter, ct); } /// @@ -220,7 +225,8 @@ namespace DeviceCommand.Device /// public virtual async Task 查询电压电流功率数组(CancellationToken ct = default) { - return await WriteReadAsync($":MEASure:VAP?{SCPIDelimiter}", SCPIDelimiter, ct); + // 修正:去除前缀冒号 + return await WriteReadAsync($"MEAS:VAP?{SCPIDelimiter}", SCPIDelimiter, ct); } /// @@ -228,7 +234,8 @@ namespace DeviceCommand.Device /// public virtual async Task 查询测得电池容量(CancellationToken ct = default) { - return await WriteReadAsync($":MEASure:CAPAcity?{SCPIDelimiter}", SCPIDelimiter, ct); + // 修正:去除前缀冒号 + return await WriteReadAsync($"MEAS:CAP?{SCPIDelimiter}", SCPIDelimiter, ct); } /// @@ -236,7 +243,8 @@ namespace DeviceCommand.Device /// public virtual async Task 查询恒流输出时间长度(CancellationToken ct = default) { - return await WriteReadAsync($":MEASure:TIMEr:CC?{SCPIDelimiter}", SCPIDelimiter, ct); + // 修正:去除前缀冒号 + return await WriteReadAsync($"MEAS:TIM:CC?{SCPIDelimiter}", SCPIDelimiter, ct); } /// @@ -244,7 +252,8 @@ namespace DeviceCommand.Device /// public virtual async Task 查询总输出时间长度(CancellationToken ct = default) { - return await WriteReadAsync($":MEASure:TIME:OUTPut?{SCPIDelimiter}", SCPIDelimiter, ct); + // 修正:去除前缀冒号 + return await WriteReadAsync($"MEAS:TIME:OUTP?{SCPIDelimiter}", SCPIDelimiter, ct); } #endregion @@ -256,8 +265,9 @@ namespace DeviceCommand.Device /// public virtual async Task 设置DC输出(bool 开启, CancellationToken ct = default) { + // 修正:去除前缀冒号,改用 OUTP 短格式 string 状态 = 开启 ? "ON" : "OFF"; - await SendAsync($":OUTPut {状态}{SCPIDelimiter}", ct); + await SendAsync($"OUTP {状态}{SCPIDelimiter}", ct); } /// @@ -265,7 +275,8 @@ namespace DeviceCommand.Device /// public virtual async Task 查询DC输出状态(CancellationToken ct = default) { - return await WriteReadAsync($":OUTPut?{SCPIDelimiter}", SCPIDelimiter, ct); + // 修正:去除前缀冒号 + return await WriteReadAsync($"OUTP?{SCPIDelimiter}", SCPIDelimiter, ct); } /// @@ -273,8 +284,9 @@ namespace DeviceCommand.Device /// public virtual async Task 设置定时器状态(bool 开启, CancellationToken ct = default) { + // 修正:去除前缀冒号 string 状态 = 开启 ? "ON" : "OFF"; - await SendAsync($":OUTPut:TIMEr {状态}{SCPIDelimiter}", ct); + await SendAsync($"OUTP:TIM {状态}{SCPIDelimiter}", ct); } /// @@ -282,7 +294,8 @@ namespace DeviceCommand.Device /// public virtual async Task 设置定时器时间(double 秒数, CancellationToken ct = default) { - string cmd = string.Format(CultureInfo.InvariantCulture, ":OUTPut:TIMEr:DATA {0:F1}{1}", 秒数, SCPIDelimiter); + // 修正:去除前缀冒号 + string cmd = string.Format(CultureInfo.InvariantCulture, "OUTP:TIM:DATA {0:F1}{1}", 秒数, SCPIDelimiter); await SendAsync(cmd, ct); } @@ -291,8 +304,9 @@ namespace DeviceCommand.Device /// public virtual async Task 设置输出模式优先(bool 是CV优先, CancellationToken ct = default) { + // 修正:去除前缀冒号 string 模式 = 是CV优先 ? "CV" : "CC"; - await SendAsync($":OUTPut:MODE {模式}{SCPIDelimiter}", ct); + await SendAsync($"OUTP:MODE {模式}{SCPIDelimiter}", ct); } #endregion @@ -304,7 +318,8 @@ namespace DeviceCommand.Device /// public virtual async Task 清除电流保护状态(CancellationToken ct = default) { - await SendAsync($":CURRent:PROTection:CLEar{SCPIDelimiter}", ct); + // 修正:去除前缀冒号 + await SendAsync($"CURR:PROT:CLE{SCPIDelimiter}", ct); } /// @@ -312,8 +327,9 @@ namespace DeviceCommand.Device /// public virtual async Task 设置电流保护使能(bool 启用, CancellationToken ct = default) { + // 修正:去除前缀冒号,将 STATE 改为更常用的 STAT 简写 string 状态 = 启用 ? "ON" : "OFF"; - await SendAsync($":CURRent:PROTection:STATE {状态}{SCPIDelimiter}", ct); + await SendAsync($"CURR:PROT:STAT {状态}{SCPIDelimiter}", ct); } /// @@ -321,7 +337,8 @@ namespace DeviceCommand.Device /// public virtual async Task 设置过流保护值(double 电流, CancellationToken ct = default) { - string cmd = string.Format(CultureInfo.InvariantCulture, ":CURRent:PROTection {0:F3}{1}", 电流, SCPIDelimiter); + // 修正:去除前缀冒号。按 NGI 通用指令树,加上层级限制或者直接作用于 CURR:PROT + string cmd = string.Format(CultureInfo.InvariantCulture, "CURR:PROT {0:F3}{1}", 电流, SCPIDelimiter); await SendAsync(cmd, ct); } @@ -330,7 +347,8 @@ namespace DeviceCommand.Device /// public virtual async Task 设置电流(double 电流, CancellationToken ct = default) { - string cmd = string.Format(CultureInfo.InvariantCulture, ":CURRent {0:F3}{1}", 电流, SCPIDelimiter); + // 修正:去除前缀冒号,改用标准 CURR 短格式 + string cmd = string.Format(CultureInfo.InvariantCulture, "CURR {0:F3}{1}", 电流, SCPIDelimiter); await SendAsync(cmd, ct); } @@ -339,7 +357,8 @@ namespace DeviceCommand.Device /// public virtual async Task 清除电压保护状态(CancellationToken ct = default) { - await SendAsync($":VOLTage:PROTection:CLEar{SCPIDelimiter}", ct); + // 修正:去除前缀冒号 + await SendAsync($"VOLT:PROT:CLE{SCPIDelimiter}", ct); } /// @@ -347,8 +366,9 @@ namespace DeviceCommand.Device /// public virtual async Task 设置电压保护使能(bool 启用, CancellationToken ct = default) { + // 修正:去除前缀冒号 string 状态 = 启用 ? "ON" : "OFF"; - await SendAsync($":VOLTage:PROTection:STATE {状态}{SCPIDelimiter}", ct); + await SendAsync($"VOLT:PROT:STAT {状态}{SCPIDelimiter}", ct); } /// @@ -356,7 +376,8 @@ namespace DeviceCommand.Device /// public virtual async Task 设置过压保护值(double 电压, CancellationToken ct = default) { - string cmd = string.Format(CultureInfo.InvariantCulture, ":VOLTage:PROTection {0:F3}{1}", 电压, SCPIDelimiter); + // 修正:去除前缀冒号 + string cmd = string.Format(CultureInfo.InvariantCulture, "VOLT:PROT {0:F3}{1}", 电压, SCPIDelimiter); await SendAsync(cmd, ct); } @@ -365,7 +386,8 @@ namespace DeviceCommand.Device /// public virtual async Task 设置电压(double 电压, CancellationToken ct = default) { - string cmd = string.Format(CultureInfo.InvariantCulture, ":VOLTage {0:F3}{1}", 电压, SCPIDelimiter); + // 修正:去除前缀冒号,改用标准 VOLT 短格式 + string cmd = string.Format(CultureInfo.InvariantCulture, "VOLT {0:F3}{1}", 电压, SCPIDelimiter); await SendAsync(cmd, ct); } @@ -374,7 +396,8 @@ namespace DeviceCommand.Device /// public virtual async Task 设置电压上限限制(double 电压, CancellationToken ct = default) { - string cmd = string.Format(CultureInfo.InvariantCulture, ":VOLTage:LIMIT {0:F3}{1}", 电压, SCPIDelimiter); + // 修正:去除前缀冒号 + string cmd = string.Format(CultureInfo.InvariantCulture, "VOLT:LIM {0:F3}{1}", 电压, SCPIDelimiter); await SendAsync(cmd, ct); } @@ -383,7 +406,8 @@ namespace DeviceCommand.Device /// public virtual async Task 设置功率(double 功率, CancellationToken ct = default) { - string cmd = string.Format(CultureInfo.InvariantCulture, ":POWer {0:F3}{1}", 功率, SCPIDelimiter); + // 修正:去除前缀冒号,改用标准 POW 短格式 + string cmd = string.Format(CultureInfo.InvariantCulture, "POW {0:F3}{1}", 功率, SCPIDelimiter); await SendAsync(cmd, ct); } @@ -392,8 +416,9 @@ namespace DeviceCommand.Device /// public virtual async Task 设置恒功率输出使能(bool 启用, CancellationToken ct = default) { + // 修正:去除前缀冒号 string 状态 = 启用 ? "ON" : "OFF"; - await SendAsync($":POWer:STATE {状态}{SCPIDelimiter}", ct); + await SendAsync($"POW:STAT {状态}{SCPIDelimiter}", ct); } #endregion @@ -405,8 +430,9 @@ namespace DeviceCommand.Device /// public virtual async Task 设置蜂鸣器状态(bool 开启, CancellationToken ct = default) { + // 修正:去除前缀冒号 string 状态 = 开启 ? "ON" : "OFF"; - await SendAsync($":SYSTem:BEEPer:STATE {状态}{SCPIDelimiter}", ct); + await SendAsync($"SYST:BEEP:STAT {状态}{SCPIDelimiter}", ct); } /// @@ -414,7 +440,8 @@ namespace DeviceCommand.Device /// public virtual async Task 蜂鸣器鸣叫(CancellationToken ct = default) { - await SendAsync($":SYSTem:BEEPer{SCPIDelimiter}", ct); + // 修正:去除前缀冒号 + await SendAsync($"SYST:BEEP{SCPIDelimiter}", ct); } /// @@ -422,7 +449,8 @@ namespace DeviceCommand.Device /// public virtual async Task 查询错误记录数量(CancellationToken ct = default) { - return await WriteReadAsync($":SYSTem:ERRor:COUNT?{SCPIDelimiter}", SCPIDelimiter, ct); + // 修正:去除前缀冒号 + return await WriteReadAsync($"SYST:ERR:COUN?{SCPIDelimiter}", SCPIDelimiter, ct); } /// @@ -430,7 +458,8 @@ namespace DeviceCommand.Device /// public virtual async Task 查询错误信息(CancellationToken ct = default) { - return await WriteReadAsync($":SYSTem:ERRor?{SCPIDelimiter}", SCPIDelimiter, ct); + // 修正:去除前缀冒号 + return await WriteReadAsync($"SYST:ERR?{SCPIDelimiter}", SCPIDelimiter, ct); } /// @@ -438,7 +467,8 @@ namespace DeviceCommand.Device /// public virtual async Task 切换本地控制模式(CancellationToken ct = default) { - await SendAsync($":SYSTem:LOCal{SCPIDelimiter}", ct); + // 修正:去除前缀冒号 + await SendAsync($"SYST:LOC{SCPIDelimiter}", ct); } /// @@ -446,7 +476,8 @@ namespace DeviceCommand.Device /// public virtual async Task 切换远程控制模式(CancellationToken ct = default) { - await SendAsync($":SYSTem:REMote{SCPIDelimiter}", ct); + // 修正:去除前缀冒号 + await SendAsync($"SYST:REM{SCPIDelimiter}", ct); } /// @@ -454,7 +485,8 @@ namespace DeviceCommand.Device /// public virtual async Task 远程控制模式锁定(CancellationToken ct = default) { - await SendAsync($":SYSTem:RWLock{SCPIDelimiter}", ct); + // 修正:去除前缀冒号 + await SendAsync($"SYST:RWL{SCPIDelimiter}", ct); } #endregion diff --git a/MonitorModule/ViewModels/MonitorViewModel.cs b/MonitorModule/ViewModels/MonitorViewModel.cs index 091fb83..7d7778c 100644 --- a/MonitorModule/ViewModels/MonitorViewModel.cs +++ b/MonitorModule/ViewModels/MonitorViewModel.cs @@ -182,12 +182,14 @@ namespace MonitorModule.ViewModels channel.Record(time, args.Value); // 入队数据库批量写入 + // CreateTime 使用 args.Time(采样触发时刻),而不是 DateTime.Now(设备响应到达时刻), + // 这样即使设备响应有延迟,数据库里的时间戳仍然按设定采样间隔分布。 var entity = new MonitorValueEntity { MonitorName = channel.DisplayName, MonitorValue = args.Value, Scope = args.Scope, - CreateTime = DateTime.Now, + CreateTime = args.Time, IsDel = 0 }; _insertQueue.Enqueue(entity); diff --git a/MonitorModule/ViewModels/RecordViewModel.cs b/MonitorModule/ViewModels/RecordViewModel.cs index ca39e72..1b1d886 100644 --- a/MonitorModule/ViewModels/RecordViewModel.cs +++ b/MonitorModule/ViewModels/RecordViewModel.cs @@ -1,8 +1,13 @@ -using Logger; +using Common.Attributes; +using DeviceCommand.Base; +using Logger; +using Model.Entity; +using ORM; using SqlSugar; using System.Collections.ObjectModel; using System.Data; using System.IO; +using System.Reflection; using System.Text; using System.Windows.Input; using UIShare.GlobalVariable; @@ -12,9 +17,9 @@ using UIShare.ViewModelBase; namespace MonitorModule.ViewModels { /// - /// 记录界面:用于查看运行目录下 SQL/ADP.db 中的数据。 - /// 功能:表选择 / 关键字 WHERE 查询 / 分页 / 导出 CSV。 - /// 仍延续 ScopedContext 隔离 + 双击展开 的范式,每个工位独立一份查询状态。 + /// 记录界面:查询 MonitorValueEntity 表中的历史监测数据。 + /// 功能:监测项筛选 / 台架筛选 / 日期筛选 / 分页 / 导出 CSV。 + /// 使用 SqlSugarContext.DbContext 全局单例,与写入端共用同一数据库连接。 /// public class RecordViewModel : NavigateViewModelBase, IRegionMemberLifetime, IDisposable { @@ -30,30 +35,39 @@ namespace MonitorModule.ViewModels set => SetProperty(ref _testStatus, value); } - public ObservableCollection TableNames + /// 监测项名称下拉列表 + public ObservableCollection MonitorNames { - get => _tableNames; - set => SetProperty(ref _tableNames, value); + get => _monitorNames; + set => SetProperty(ref _monitorNames, value); } - public string? SelectedTable + /// 选中的监测项(null = 全部) + public string? SelectedMonitorName { - get => _selectedTable; - set - { - if (SetProperty(ref _selectedTable, value)) - { - PageIndex = 1; - Query(); - } - } + get => _selectedMonitorName; + set => SetProperty(ref _selectedMonitorName, value); } - // 用户填写的 WHERE 子句(不带 WHERE 关键字),例如:Status='OK' AND Id>10 - public string WhereClause + /// 台架(作用域)下拉列表 + public ObservableCollection ScopeNames { - get => _whereClause; - set => SetProperty(ref _whereClause, value); + get => _scopeNames; + set => SetProperty(ref _scopeNames, value); + } + + /// 选中的台架(null = 全部) + public string? SelectedScope + { + get => _selectedScope; + set => SetProperty(ref _selectedScope, value); + } + + /// 查询日期(null = 全部日期) + public DateTime? SelectedDate + { + get => _selectedDate; + set => SetProperty(ref _selectedDate, value); } public DataTable ResultTable @@ -111,40 +125,38 @@ namespace MonitorModule.ViewModels #region 命令 public ICommand LoadedCommand { get; } - public ICommand RefreshTablesCommand { get; } public ICommand QueryCommand { get; } + public ICommand ClearFilterCommand { get; } public ICommand FirstPageCommand { get; } public ICommand PrevPageCommand { get; } public ICommand NextPageCommand { get; } public ICommand LastPageCommand { get; } public ICommand ExportCsvCommand { get; } - // 双击展开/折叠:与 MonitorView/AutomatedTestingView 共用 public ICommand RefreshCommand { get; } #endregion + #region 私有字段 - // 数据库相对路径:运行目录\SQL\ADP.db(数据库未就绪时容错处理,不抛异常) - private static readonly string DbFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "SQL"); - private static readonly string DbPath = Path.Combine(DbFolder, "ADP.db"); - private static readonly string ConnStr = $"Data Source={DbPath};Version=3;"; - private bool IsInitiated = false; private IScopedProvider _scope; private string _testStatus = string.Empty; - private ObservableCollection _tableNames = new(); - private string? _selectedTable; - private string _whereClause = string.Empty; + private ObservableCollection _monitorNames = new(); + private string? _selectedMonitorName; + private ObservableCollection _scopeNames = new(new[] { "全部", "TestCell1", "TestCell2", "TestCell3", "TestCell4", "TestCell5", "TestCell6", "TestCell7", "TestCell8" }); + private string? _selectedScope = "全部"; + private DateTime? _selectedDate; private DataTable _resultTable = new(); private string _statusMessage = "未连接"; private int _pageIndex = 1; private int _pageSize = 50; private long _totalCount; + private bool IsInitiated = false; #endregion public RecordViewModel(IContainerExtension container) : base(container) { _globalInfo = container.Resolve(); - LoadedCommand = new DelegateCommand(LoadTables); - RefreshTablesCommand = new DelegateCommand(LoadTables); + LoadedCommand = new DelegateCommand(LoadFilterOptions); QueryCommand = new DelegateCommand(() => { PageIndex = 1; Query(); }); + ClearFilterCommand = new DelegateCommand(ClearFilter); FirstPageCommand = new DelegateCommand(() => { if (PageIndex > 1) { PageIndex = 1; Query(); } }); PrevPageCommand = new DelegateCommand(() => { if (PageIndex > 1) { PageIndex--; Query(); } }); NextPageCommand = new DelegateCommand(() => { if (PageIndex < TotalPages) { PageIndex++; Query(); } }); @@ -158,105 +170,136 @@ namespace MonitorModule.ViewModels _scope?.Dispose(); } - #region 数据库操作 - private SqlSugarClient CreateClient() + #region 筛选条件加载 + /// + /// 加载筛选条件:监测项通过反射发现(与 MonitorViewModel 一致),台架从数据库查询。 + /// + private void LoadFilterOptions() { - return new SqlSugarClient(new ConnectionConfig - { - DbType = SqlSugar.DbType.Sqlite, - ConnectionString = ConnStr, - IsAutoCloseConnection = true, - InitKeyType = InitKeyType.Attribute - }); + // 监测项:反射发现当前作用域所有带 [Monitorable] 的设备方法 + DiscoverMonitorNames(); + + Query(); } /// - /// 读取数据库中的所有用户表。数据库不存在时不报错,仅在状态栏提示。 + /// 反射扫描当前作用域 DeviceManager 中所有带 [Monitorable] 特性的设备方法, + /// 生成与 MonitorViewModel 完全一致的 DisplayName 列表,填充到下拉框。 + /// 这样即使数据库还没数据,用户也能看到所有可筛选的监测项。 /// - private void LoadTables() + private void DiscoverMonitorNames() { + MonitorNames = new ObservableCollection(); + + if (_scope == null) return; + try { - if (!Directory.Exists(DbFolder)) Directory.CreateDirectory(DbFolder); - if (!File.Exists(DbPath)) + var deviceManager = _scope.Resolve(); + if (deviceManager?.DeviceMap == null || deviceManager.DeviceMap.Count == 0) { - TableNames = new ObservableCollection(); - SelectedTable = null; - ResultTable = new DataTable(); - TotalCount = 0; - StatusMessage = $"数据库尚未创建:{DbPath}"; + StatusMessage = "当前工位无可用设备"; return; } - using var db = CreateClient(); - var tables = db.DbMaintenance.GetTableInfoList(false) - .Select(t => t.Name) - .OrderBy(n => n) - .ToList(); + // 构建 设备实例 → 硬件指纹 的反向查找表 + var instanceToFp = new Dictionary(ReferenceEqualityComparer.Instance); + foreach (var poolEntry in _globalInfo.HardwarePool) + { + var lazy = poolEntry.Value; + if (lazy.IsValueCreated && lazy.Value != null) + instanceToFp[lazy.Value] = poolEntry.Key; + } - TableNames = new ObservableCollection(tables); - if (tables.Count == 0) + var names = new List(); + + foreach (var kvp in deviceManager.DeviceMap) { - SelectedTable = null; - ResultTable = new DataTable(); - TotalCount = 0; - StatusMessage = "数据库已连接,但暂无数据表"; - } - else - { - StatusMessage = $"已连接:{DbPath}({tables.Count} 张表)"; - if (string.IsNullOrEmpty(SelectedTable) || !tables.Contains(SelectedTable)) + string deviceName = kvp.Key; + var device = kvp.Value; + var deviceType = device.GetType(); + + // 只列出在硬件指纹池中注册的设备 + if (!instanceToFp.ContainsKey(device)) + continue; + + var methods = deviceType.GetMethods(BindingFlags.Public | BindingFlags.Instance) + .Where(m => + { + if (m.GetCustomAttribute() == null) return false; + if (m.ReturnType != typeof(Task)) return false; + var parms = m.GetParameters(); + return parms.Length == 0 || + (parms.Length == 1 && parms[0].ParameterType == typeof(CancellationToken)); + }); + + foreach (var method in methods) { - SelectedTable = tables[0]; // setter 会触发 Query - } - else - { - Query(); + var attr = method.GetCustomAttribute(); + string displayName = !string.IsNullOrEmpty(attr?.Description) + ? $"{deviceName}.{attr.Description}" + : $"{deviceName}.{method.Name}"; + names.Add(displayName); } } + + names.Sort(); + MonitorNames = new ObservableCollection(names); } catch (Exception ex) { - LoggerHelper.ErrorWithNotify(_globalInfo.CurrentScope,$"加载数据表失败:{ex.Message}"); - StatusMessage = $"加载失败:{ex.Message}"; + LoggerHelper.ErrorWithNotify(_globalInfo.CurrentScope, $"反射发现监测项失败:{ex.Message}"); } } + /// 清除所有筛选条件 + private void ClearFilter() + { + SelectedMonitorName = null; + SelectedScope = "全部"; + SelectedDate = null; + PageIndex = 1; + Query(); + } + #endregion + + #region 查询 /// - /// 执行分页查询。WHERE 为空则查询全部。 + /// 按当前筛选条件分页查询 MonitorValueEntity。 /// private void Query() { - if (string.IsNullOrEmpty(SelectedTable) || !File.Exists(DbPath)) - { - ResultTable = new DataTable(); - TotalCount = 0; - return; - } - try { - using var db = CreateClient(); - string table = $"[{SelectedTable}]"; - string where = string.IsNullOrWhiteSpace(WhereClause) ? "1=1" : WhereClause; + var db = SqlSugarContext.DbContext; - // COUNT 总数 - var countSql = $"SELECT COUNT(*) FROM {table} WHERE {where}"; - TotalCount = db.Ado.GetLong(countSql); + // 预计算日期范围,避免在表达式树中访问可空属性 + DateTime? dateStart = SelectedDate?.Date; + DateTime? dateEnd = dateStart?.AddDays(1); + + var query = db.Queryable() + .WhereIF(!string.IsNullOrEmpty(SelectedMonitorName), x => x.MonitorName == SelectedMonitorName) + .WhereIF(!string.IsNullOrEmpty(SelectedScope) && SelectedScope != "全部", x => x.Scope == SelectedScope) + .WhereIF(dateStart.HasValue, x => x.CreateTime >= dateStart && x.CreateTime < dateEnd!.Value) + .OrderBy(x => x.CreateTime, OrderByType.Desc); + + TotalCount = query.Count(); // 修正越界 if (PageIndex > TotalPages) PageIndex = TotalPages; if (PageIndex < 1) PageIndex = 1; - int offset = (PageIndex - 1) * PageSize; - var dataSql = $"SELECT * FROM {table} WHERE {where} LIMIT {PageSize} OFFSET {offset}"; - ResultTable = db.Ado.GetDataTable(dataSql); + int skip = (PageIndex - 1) * PageSize; + ResultTable = query + .Skip(skip) + .Take(PageSize) + .ToDataTable(); - StatusMessage = $"表 [{SelectedTable}] 共 {TotalCount} 行 第 {PageIndex}/{TotalPages} 页"; + StatusMessage = $"共 {TotalCount} 行 第 {PageIndex}/{TotalPages} 页"; } catch (Exception ex) { - LoggerHelper.ErrorWithNotify(TestStatus,$"查询失败:{ex.Message}"); + LoggerHelper.ErrorWithNotify(TestStatus, $"查询失败:{ex.Message}"); StatusMessage = $"查询失败:{ex.Message}"; ResultTable = new DataTable(); TotalCount = 0; @@ -266,36 +309,42 @@ namespace MonitorModule.ViewModels #region 导出 CSV /// - /// 按当前 WHERE 条件导出全部结果到 CSV(不分页)。 + /// 按当前筛选条件导出全部结果到 CSV(不分页)。 /// private void ExportCsv() { - if (string.IsNullOrEmpty(SelectedTable) || !File.Exists(DbPath)) - { - StatusMessage = "无可导出数据"; - return; - } - - var dlg = new Microsoft.Win32.SaveFileDialog - { - Filter = "CSV 文件 (*.csv)|*.csv|所有文件|*.*", - FileName = $"{SelectedTable}_{DateTime.Now:yyyyMMdd_HHmmss}.csv", - Title = $"导出 [{SelectedTable}] 为 CSV" - }; - if (dlg.ShowDialog() != true) return; - try { - using var db = CreateClient(); - string table = $"[{SelectedTable}]"; - string where = string.IsNullOrWhiteSpace(WhereClause) ? "1=1" : WhereClause; - var sql = $"SELECT * FROM {table} WHERE {where}"; - var dt = db.Ado.GetDataTable(sql); + var db = SqlSugarContext.DbContext; + + DateTime? dateStart = SelectedDate?.Date; + DateTime? dateEnd = dateStart?.AddDays(1); + + var query = db.Queryable() + .WhereIF(!string.IsNullOrEmpty(SelectedMonitorName), x => x.MonitorName == SelectedMonitorName) + .WhereIF(!string.IsNullOrEmpty(SelectedScope) && SelectedScope != "全部", x => x.Scope == SelectedScope) + .WhereIF(dateStart.HasValue, x => x.CreateTime >= dateStart && x.CreateTime < dateEnd!.Value) + .OrderBy(x => x.CreateTime, OrderByType.Desc); + + var dt = query.ToDataTable(); + if (dt.Rows.Count == 0) + { + StatusMessage = "无可导出数据"; + return; + } + + var dlg = new Microsoft.Win32.SaveFileDialog + { + Filter = "CSV 文件 (*.csv)|*.csv|所有文件|*.*", + FileName = $"MonitorData_{DateTime.Now:yyyyMMdd_HHmmss}.csv", + Title = "导出监测数据为 CSV" + }; + if (dlg.ShowDialog() != true) return; WriteCsv(dlg.FileName, dt); StatusMessage = $"导出完成:{dlg.FileName}({dt.Rows.Count} 行)"; - LoggerHelper.InfoWithNotify(TestStatus, $"工位 [{TestStatus}] 导出 [{SelectedTable}] 至 {dlg.FileName},共 {dt.Rows.Count} 行"); + LoggerHelper.InfoWithNotify(TestStatus, $"工位 [{TestStatus}] 导出监测数据至 {dlg.FileName},共 {dt.Rows.Count} 行"); } catch (Exception ex) { @@ -333,7 +382,6 @@ namespace MonitorModule.ViewModels if (string.IsNullOrEmpty(TestStatus)) return; _globalInfo.CurrentScope = TestStatus; _eventAggregator.GetEvent().Publish(TestStatus); - } #endregion @@ -341,14 +389,14 @@ namespace MonitorModule.ViewModels public override void OnNavigatedTo(NavigationContext navigationContext) { base.OnNavigatedTo(navigationContext); - if (!IsInitiated&&navigationContext.Parameters.ContainsKey("Name")) + if (!IsInitiated && navigationContext.Parameters.ContainsKey("Name")) { TestStatus = navigationContext.Parameters.GetValue("Name"); _scope = _globalInfo.ScopeDic[TestStatus]; _scopedContext = _scope.Resolve(); IsInitiated = true; } - LoadTables(); + LoadFilterOptions(); } #endregion } diff --git a/MonitorModule/Views/RecordView.xaml b/MonitorModule/Views/RecordView.xaml index ca46630..736b395 100644 --- a/MonitorModule/Views/RecordView.xaml +++ b/MonitorModule/Views/RecordView.xaml @@ -39,7 +39,7 @@ FontSize="20" FontWeight="Bold" Margin="4,0,0,8"/> - + - + + + + + + + - - - + + - - - - - + -