啊鑫
2025-07-26 da107f3b89271191052d9ece45e0010bb2d7d795
修复Oracle数据库列名映射错误和空引用异常

- 修复SqlSugar实体类与Oracle表列名不匹配问题
- 为所有实体属性添加明确的ColumnName映射(IS_RESOLVED, ADDITIONAL_DATA_JSON等)
- 增强UniversalModbusManager的空值检查和连接状态验证
- 改进ModbusDataParser的错误处理和容错性
- 修复EventTypes和ErrorSeverity常量的空引用问题
已修改3个文件
173 ■■■■■ 文件已修改
Database/DatabaseEntities.cs 112 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
UniversalModbusManager.cs 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
config/ModbusDataParser.cs 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Database/DatabaseEntities.cs
@@ -5,7 +5,7 @@
    /// <summary>
    /// Modbus主数据实体 - 存储从PLC读取的数据
    /// </summary>
    [SugarTable("ModbusData")]
    [SugarTable("PRODUCTION_DATA")]
    public class ModbusDataEntity
    {
        /// <summary>
@@ -17,56 +17,56 @@
        /// <summary>
        /// 数据读取时间
        /// </summary>
        [SugarColumn(IsNullable = true, ColumnDescription = "数据读取时间")]
        [SugarColumn(ColumnName = "READ_TIME", IsNullable = true, ColumnDescription = "数据读取时间")]
        public DateTime? ReadTime { get; set; }
        /// <summary>
        /// 项目名称(来自配置)
        /// </summary>
        [SugarColumn(Length = 100, IsNullable = true, ColumnDescription = "项目名称")]
        [SugarColumn(ColumnName = "PROJECT_NAME", Length = 100, IsNullable = true, ColumnDescription = "项目名称")]
        public string? ProjectName { get; set; } 
        /// <summary>
        /// PLC IP地址
        /// </summary>
        [SugarColumn(Length = 50, IsNullable = true, ColumnDescription = "PLC IP地址")]
        [SugarColumn(ColumnName = "PLC_IP_ADDRESS", Length = 50, IsNullable = true, ColumnDescription = "PLC IP地址")]
        public string? PlcIpAddress { get; set; } 
        /// <summary>
        /// 控制信号数据(JSON格式)
        /// </summary>
        [SugarColumn(ColumnDataType = "TEXT", IsNullable = true, ColumnDescription = "控制信号数据JSON")]
        [SugarColumn(ColumnName = "CONTROL_SIGNALS_JSON", ColumnDataType = "TEXT", IsNullable = true, ColumnDescription = "控制信号数据JSON")]
        public string? ControlSignalsJson { get; set; }
        /// <summary>
        /// 产品数据(JSON格式)
        /// </summary>
        [SugarColumn(ColumnDataType = "TEXT", IsNullable = true, ColumnDescription = "产品数据JSON")]
        [SugarColumn(ColumnName = "PRODUCT_DATA_JSON", ColumnDataType = "TEXT", IsNullable = true, ColumnDescription = "产品数据JSON")]
        public string? ProductDataJson { get; set; }
        /// <summary>
        /// 测量数据(JSON格式)
        /// </summary>
        [SugarColumn(ColumnDataType = "TEXT", IsNullable = true, ColumnDescription = "测量数据JSON")]
        [SugarColumn(ColumnName = "MEASUREMENT_DATA_JSON", ColumnDataType = "TEXT", IsNullable = true, ColumnDescription = "测量数据JSON")]
        public string? MeasurementDataJson { get; set; }
        /// <summary>
        /// 原始寄存器数据(JSON格式,用于调试)
        /// </summary>
        [SugarColumn(ColumnDataType = "TEXT", IsNullable = true, ColumnDescription = "原始寄存器数据JSON")]
        [SugarColumn(ColumnName = "RAW_REGISTERS_JSON", ColumnDataType = "TEXT", IsNullable = true, ColumnDescription = "原始寄存器数据JSON")]
        public string? RawRegistersJson { get; set; }
        /// <summary>
        /// 数据创建时间
        /// </summary>
        [SugarColumn(IsNullable = true, ColumnDescription = "记录创建时间")]
        [SugarColumn(ColumnName = "CREATED_AT", IsNullable = true, ColumnDescription = "记录创建时间")]
        public DateTime? CreatedAt { get; set; } = DateTime.Now;
    }
    /// <summary>
    /// 通信日志实体 - 记录通信状态和统计信息
    /// </summary>
    [SugarTable("CommunicationLog")]
    [SugarTable("COMMUNICATION_LOG")]
    public class CommunicationLogEntity
    {
        /// <summary>
@@ -78,56 +78,56 @@
        /// <summary>
        /// 日志时间
        /// </summary>
        [SugarColumn(IsNullable = true, ColumnDescription = "日志时间")]
        [SugarColumn(ColumnName = "LOG_TIME", IsNullable = true, ColumnDescription = "日志时间")]
        public DateTime? LogTime { get; set; }
        /// <summary>
        /// 项目名称
        /// </summary>
        [SugarColumn(Length = 100, IsNullable = true, ColumnDescription = "项目名称")]
        [SugarColumn(ColumnName = "PROJECT_NAME", Length = 100, IsNullable = true, ColumnDescription = "项目名称")]
        public string? ProjectName { get; set; }
        /// <summary>
        /// PLC IP地址
        /// </summary>
        [SugarColumn(Length = 50, IsNullable = true, ColumnDescription = "PLC IP地址")]
        [SugarColumn(ColumnName = "PLC_IP_ADDRESS", Length = 50, IsNullable = true, ColumnDescription = "PLC IP地址")]
        public string? PlcIpAddress { get; set; } 
        /// <summary>
        /// 事件类型(Connected, Disconnected, DataReceived, Error等)
        /// </summary>
        [SugarColumn(Length = 50, IsNullable = true, ColumnDescription = "事件类型")]
        [SugarColumn(ColumnName = "EVENT_TYPE", Length = 50, IsNullable = true, ColumnDescription = "事件类型")]
        public string? EventType { get; set; }
        /// <summary>
        /// 事件描述
        /// </summary>
        [SugarColumn(Length = 500, IsNullable = true, ColumnDescription = "事件描述")]
        [SugarColumn(ColumnName = "EVENT_DESCRIPTION", Length = 500, IsNullable = true, ColumnDescription = "事件描述")]
        public string? EventDescription { get; set; }
        /// <summary>
        /// 是否成功
        /// </summary>
        [SugarColumn(IsNullable = true, ColumnDescription = "是否成功")]
        [SugarColumn(ColumnName = "IS_SUCCESS", IsNullable = true, ColumnDescription = "是否成功")]
        public bool? IsSuccess { get; set; }
        /// <summary>
        /// 耗时(毫秒)
        /// </summary>
        [SugarColumn(IsNullable = true, ColumnDescription = "操作耗时毫秒")]
        [SugarColumn(ColumnName = "DURATION_MS", IsNullable = true, ColumnDescription = "操作耗时毫秒")]
        public int? DurationMs { get; set; }
        /// <summary>
        /// 附加数据(JSON格式)
        /// </summary>
        [SugarColumn(ColumnDataType = "TEXT", IsNullable = true, ColumnDescription = "附加数据JSON")]
        [SugarColumn(ColumnName = "ADDITIONAL_DATA_JSON", ColumnDataType = "TEXT", IsNullable = true, ColumnDescription = "附加数据JSON")]
        public string? AdditionalDataJson { get; set; }
    }
    /// <summary>
    /// 错误日志实体 - 记录通信和处理错误
    /// </summary>
    [SugarTable("ErrorLog")]
    [SugarTable("ERROR_LOG")]
    public class ErrorLogEntity
    {
        /// <summary>
@@ -139,61 +139,61 @@
        /// <summary>
        /// 错误发生时间
        /// </summary>
        [SugarColumn(IsNullable = true, ColumnDescription = "错误发生时间")]
        [SugarColumn(ColumnName = "ERROR_TIME", IsNullable = true, ColumnDescription = "错误发生时间")]
        public DateTime? ErrorTime { get; set; }
        /// <summary>
        /// 项目名称
        /// </summary>
        [SugarColumn(Length = 100, IsNullable = true, ColumnDescription = "项目名称")]
        [SugarColumn(ColumnName = "PROJECT_NAME", Length = 100, IsNullable = true, ColumnDescription = "项目名称")]
        public string? ProjectName { get; set; } 
        /// <summary>
        /// PLC IP地址
        /// </summary>
        [SugarColumn(Length = 50, IsNullable = true, ColumnDescription = "PLC IP地址")]
        [SugarColumn(ColumnName = "PLC_IP_ADDRESS", Length = 50, IsNullable = true, ColumnDescription = "PLC IP地址")]
        public string? PlcIpAddress { get; set; }
        /// <summary>
        /// 错误类型
        /// </summary>
        [SugarColumn(Length = 100, IsNullable = true, ColumnDescription = "错误类型")]
        [SugarColumn(ColumnName = "ERROR_TYPE", Length = 100, IsNullable = true, ColumnDescription = "错误类型")]
        public string? ErrorType { get; set; }
        /// <summary>
        /// 错误消息
        /// </summary>
        [SugarColumn(Length = 1000, IsNullable = true, ColumnDescription = "错误消息")]
        [SugarColumn(ColumnName = "ERROR_MESSAGE", Length = 1000, IsNullable = true, ColumnDescription = "错误消息")]
        public string? ErrorMessage { get; set; }
        /// <summary>
        /// 异常堆栈信息
        /// </summary>
        [SugarColumn(ColumnDataType = "TEXT", IsNullable = true, ColumnDescription = "异常堆栈信息")]
        [SugarColumn(ColumnName = "STACK_TRACE", ColumnDataType = "TEXT", IsNullable = true, ColumnDescription = "异常堆栈信息")]
        public string? StackTrace { get; set; }
        /// <summary>
        /// 错误严重级别(Critical, High, Medium, Low)
        /// </summary>
        [SugarColumn(Length = 20, IsNullable = true, ColumnDescription = "错误严重级别")]
        [SugarColumn(ColumnName = "SEVERITY", Length = 20, IsNullable = true, ColumnDescription = "错误严重级别")]
        public string? Severity { get; set; } = "Medium";
        /// <summary>
        /// 重试次数
        /// </summary>
        [SugarColumn(IsNullable = true, ColumnDescription = "重试次数")]
        [SugarColumn(ColumnName = "RETRY_COUNT", IsNullable = true, ColumnDescription = "重试次数")]
        public int? RetryCount { get; set; } = 0;
        /// <summary>
        /// 是否已解决
        /// </summary>
        [SugarColumn(IsNullable = true, ColumnDescription = "是否已解决")]
        [SugarColumn(ColumnName = "IS_RESOLVED", IsNullable = true, ColumnDescription = "是否已解决")]
        public bool? IsResolved { get; set; } = false;
    }
    /// <summary>
    /// 统计数据实体 - 记录通信统计信息
    /// </summary>
    [SugarTable("Statistics")]
    [SugarTable("STATISTICS")]
    public class StatisticsEntity
    {
        /// <summary>
@@ -205,91 +205,91 @@
        /// <summary>
        /// 统计时间
        /// </summary>
        [SugarColumn(IsNullable = true, ColumnDescription = "统计时间")]
        [SugarColumn(ColumnName = "STATISTICS_TIME", IsNullable = true, ColumnDescription = "统计时间")]
        public DateTime? StatisticsTime { get; set; }
        /// <summary>
        /// 项目名称
        /// </summary>
        [SugarColumn(Length = 100, IsNullable = true, ColumnDescription = "项目名称")]
        [SugarColumn(ColumnName = "PROJECT_NAME", Length = 100, IsNullable = true, ColumnDescription = "项目名称")]
        public string? ProjectName { get; set; } 
        /// <summary>
        /// PLC IP地址
        /// </summary>
        [SugarColumn(Length = 50, IsNullable = true, ColumnDescription = "PLC IP地址")]
        [SugarColumn(ColumnName = "PLC_IP_ADDRESS", Length = 50, IsNullable = true, ColumnDescription = "PLC IP地址")]
        public string? PlcIpAddress { get; set; }
        /// <summary>
        /// 连接尝试总数
        /// </summary>
        [SugarColumn(IsNullable = true, ColumnDescription = "连接尝试总数")]
        [SugarColumn(ColumnName = "TOTAL_CONNECTION_ATTEMPTS", IsNullable = true, ColumnDescription = "连接尝试总数")]
        public int? TotalConnectionAttempts { get; set; }
        /// <summary>
        /// 连接成功总数
        /// </summary>
        [SugarColumn(IsNullable = true, ColumnDescription = "连接成功总数")]
        [SugarColumn(ColumnName = "SUCCESSFUL_CONNECTIONS", IsNullable = true, ColumnDescription = "连接成功总数")]
        public int? SuccessfulConnections { get; set; }
        /// <summary>
        /// 数据读取总数
        /// </summary>
        [SugarColumn(IsNullable = true, ColumnDescription = "数据读取总数")]
        [SugarColumn(ColumnName = "TOTAL_DATA_READS", IsNullable = true, ColumnDescription = "数据读取总数")]
        public int? TotalDataReads { get; set; }
        /// <summary>
        /// 数据读取成功总数
        /// </summary>
        [SugarColumn(IsNullable = true, ColumnDescription = "数据读取成功总数")]
        [SugarColumn(ColumnName = "SUCCESSFUL_DATA_READS", IsNullable = true, ColumnDescription = "数据读取成功总数")]
        public int? SuccessfulDataReads { get; set; }
        /// <summary>
        /// 错误总数
        /// </summary>
        [SugarColumn(IsNullable = true, ColumnDescription = "错误总数")]
        [SugarColumn(ColumnName = "TOTAL_ERRORS", IsNullable = true, ColumnDescription = "错误总数")]
        public int? TotalErrors { get; set; }
        /// <summary>
        /// 平均响应时间(毫秒)
        /// </summary>
        [SugarColumn(IsNullable = true, ColumnDescription = "平均响应时间毫秒")]
        [SugarColumn(ColumnName = "AVERAGE_RESPONSE_TIME_MS", IsNullable = true, ColumnDescription = "平均响应时间毫秒")]
        public double? AverageResponseTimeMs { get; set; }
        /// <summary>
        /// 最大响应时间(毫秒)
        /// </summary>
        [SugarColumn(IsNullable = true, ColumnDescription = "最大响应时间毫秒")]
        [SugarColumn(ColumnName = "MAX_RESPONSE_TIME_MS", IsNullable = true, ColumnDescription = "最大响应时间毫秒")]
        public int? MaxResponseTimeMs { get; set; }
        /// <summary>
        /// 最小响应时间(毫秒)
        /// </summary>
        [SugarColumn(IsNullable = true, ColumnDescription = "最小响应时间毫秒")]
        [SugarColumn(ColumnName = "MIN_RESPONSE_TIME_MS", IsNullable = true, ColumnDescription = "最小响应时间毫秒")]
        public int? MinResponseTimeMs { get; set; }
        /// <summary>
        /// 连接成功率
        /// </summary>
        [SugarColumn(IsNullable = true, ColumnDescription = "连接成功率")]
        [SugarColumn(ColumnName = "CONNECTION_SUCCESS_RATE", IsNullable = true, ColumnDescription = "连接成功率")]
        public double? ConnectionSuccessRate { get; set; }
        /// <summary>
        /// 数据读取成功率
        /// </summary>
        [SugarColumn(IsNullable = true, ColumnDescription = "数据读取成功率")]
        [SugarColumn(ColumnName = "DATA_READ_SUCCESS_RATE", IsNullable = true, ColumnDescription = "数据读取成功率")]
        public double? DataReadSuccessRate { get; set; }
        /// <summary>
        /// 统计时间范围开始
        /// </summary>
        [SugarColumn(IsNullable = true, ColumnDescription = "统计时间范围开始")]
        [SugarColumn(ColumnName = "PERIOD_START", IsNullable = true, ColumnDescription = "统计时间范围开始")]
        public DateTime? PeriodStart { get; set; }
        /// <summary>
        /// 统计时间范围结束
        /// </summary>
        [SugarColumn(IsNullable = true, ColumnDescription = "统计时间范围结束")]
        [SugarColumn(ColumnName = "PERIOD_END", IsNullable = true, ColumnDescription = "统计时间范围结束")]
        public DateTime? PeriodEnd { get; set; }
    }
@@ -298,14 +298,14 @@
    /// </summary>
    public static class EventTypes
    {
        public const string? Connected = "Connected";
        public const string? Disconnected = "Disconnected";
        public const string? DataReceived = "DataReceived";
        public const string? ConnectionError = "ConnectionError";
        public const string? DataReadError = "DataReadError";
        public const string? ConfigurationLoaded = "ConfigurationLoaded";
        public const string? SystemStarted = "SystemStarted";
        public const string? SystemStopped = "SystemStopped";
        public const string Connected = "Connected";
        public const string Disconnected = "Disconnected";
        public const string DataReceived = "DataReceived";
        public const string ConnectionError = "ConnectionError";
        public const string DataReadError = "DataReadError";
        public const string ConfigurationLoaded = "ConfigurationLoaded";
        public const string SystemStarted = "SystemStarted";
        public const string SystemStopped = "SystemStopped";
    }
    /// <summary>
@@ -313,9 +313,9 @@
    /// </summary>
    public static class ErrorSeverity
    {
        public const string? Critical = "Critical";  // 系统无法继续运行
        public const string? High = "High";          // 功能受到严重影响
        public const string? Medium = "Medium";      // 功能受到影响但可继续
        public const string? Low = "Low";            // 轻微问题,不影响主要功能
        public const string Critical = "Critical";  // 系统无法继续运行
        public const string High = "High";          // 功能受到严重影响
        public const string Medium = "Medium";      // 功能受到影响但可继续
        public const string Low = "Low";            // 轻微问题,不影响主要功能
    }
}
UniversalModbusManager.cs
@@ -501,7 +501,23 @@
        /// <param name="groupName">组名称(用于日志)</param>
        private async Task ReadFieldGroup(DynamicModbusData dynamicData, Dictionary<string, DataField> fieldGroup, string groupName)
        {
            if (_modbusClient == null) return;
            if (_modbusClient == null)
            {
                LogWarning($"Modbus客户端为null,无法读取 {groupName}");
                return;
            }
            if (!_isConnected)
            {
                LogWarning($"Modbus未连接,无法读取 {groupName}");
                return;
            }
            if (fieldGroup == null || fieldGroup.Count == 0)
            {
                LogDebug($"{groupName} 没有配置字段,跳过读取");
                return;
            }
            foreach (var kvp in fieldGroup)
            {
@@ -510,14 +526,28 @@
                try
                {
                    // 检查Modbus客户端状态
                    if (_modbusClient == null || !_modbusClient.Connected)
                    {
                        LogWarning($"Modbus客户端未连接,跳过字段 '{fieldName}'");
                        continue;
                    }
                    // 读取原始寄存器数据
                    var registers = await Task.Run(() => 
                        _modbusClient.ReadHoldingRegisters(fieldConfig.Address, fieldConfig.Length));
                    // 检查读取结果是否为null
                    if (registers == null)
                    {
                        LogWarning($"字段 '{fieldName}' 读取结果为null,地址: {fieldConfig.Address}, 长度: {fieldConfig.Length}");
                        continue;
                    }
                    // 验证数据长度
                    if (!ModbusDataParser.ValidateRegisterLength(registers, fieldConfig))
                    {
                        LogWarning($"字段 '{fieldName}' 的数据长度不足:期望{fieldConfig.Length},实际{registers.Length}");
                        LogWarning($"字段 '{fieldName}' 的数据长度不足:期望{fieldConfig.Length},实际{registers?.Length ?? 0}");
                        continue;
                    }
@@ -532,6 +562,14 @@
                catch (Exception ex)
                {
                    LogError($"读取字段 '{fieldName}' 失败: {ex.Message}");
                    // 记录详细错误信息
                    await _databaseManager?.LogErrorAsync(
                        "FieldReadError",
                        $"读取字段 '{fieldName}' 失败: {ex.Message}",
                        ex,
                        ErrorSeverity.Medium)!;
                    // 继续读取其他字段
                }
            }
config/ModbusDataParser.cs
@@ -18,9 +18,22 @@
        /// <returns>解析后的数据对象</returns>
        public static object? ParseFieldData(int[] registers, DataField fieldConfig)
        {
            if (fieldConfig == null)
            {
                Console.WriteLine("[PARSER-ERROR] 字段配置为null");
                return null;
            }
            if (registers == null || registers.Length == 0)
            {
                Console.WriteLine($"[PARSER-ERROR] 字段 '{fieldConfig.DisplayName}' 的寄存器数据为空");
                return GetDefaultValue(fieldConfig.DataType);
            }
            if (string.IsNullOrEmpty(fieldConfig.DataType))
            {
                Console.WriteLine($"[PARSER-ERROR] 字段 '{fieldConfig.DisplayName}' 的数据类型未定义");
                return null;
            }
            try
@@ -39,6 +52,7 @@
            catch (Exception ex)
            {
                Console.WriteLine($"[PARSER-ERROR] 解析字段 '{fieldConfig.DisplayName}' 时发生错误: {ex.Message}");
                Console.WriteLine($"[PARSER-ERROR] 堆栈跟踪: {ex.StackTrace}");
                return GetDefaultValue(fieldConfig.DataType);
            }
        }
@@ -272,8 +286,11 @@
        /// </summary>
        /// <param name="dataType">数据类型字符串</param>
        /// <returns>默认值对象</returns>
        private static object? GetDefaultValue(string dataType)
        private static object? GetDefaultValue(string? dataType)
        {
            if (string.IsNullOrEmpty(dataType))
                return null;
            return dataType.ToLower() switch
            {
                "byte" => (byte)0,