| | |
| | | using GSModbus.Config; |
| | | using SqlSugar; |
| | | using System.Text.Json; |
| | | using Oracle.ManagedDataAccess.Client; |
| | | |
| | | namespace GSModbus.Database |
| | | { |
| | |
| | | { |
| | | try |
| | | { |
| | | OnDatabaseLog("开始初始化数据库连接..."); |
| | | |
| | | // 对Oracle特殊处理,避免SqlSugar语法问题 |
| | | if (_config.Type.ToLower() == "oracle") |
| | | { |
| | | InitializeOracleDirectly(); |
| | | } |
| | | else |
| | | { |
| | | InitializeWithSqlSugar(); |
| | | } |
| | | |
| | | OnDatabaseLog("数据库管理器初始化完成"); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | OnDatabaseError($"初始化数据库失败: {ex.Message}"); |
| | | throw; |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 直接初始化Oracle数据库 |
| | | /// </summary> |
| | | private void InitializeOracleDirectly() |
| | | { |
| | | try |
| | | { |
| | | OnDatabaseLog("使用Oracle原生方式初始化..."); |
| | | |
| | | // 先测试连接 |
| | | using (var testConnection = new OracleConnection(_config.ConnectionString)) |
| | | { |
| | | testConnection.Open(); |
| | | OnDatabaseLog("Oracle数据库连接测试成功"); |
| | | testConnection.Close(); |
| | | } |
| | | |
| | | // 创建SqlSugar客户端(只用于数据操作,不用于连接测试) |
| | | var dbType = GetDbType(_config.Type); |
| | | _db = new SqlSugarClient(new SqlSugar.ConnectionConfig() |
| | | { |
| | | ConnectionString = _config.ConnectionString, |
| | | DbType = dbType, |
| | | IsAutoCloseConnection = true, |
| | | MoreSettings = new ConnMoreSettings() |
| | | { |
| | | IsAutoRemoveDataCache = true |
| | | } |
| | | }); |
| | | |
| | | OnDatabaseLog("SqlSugar客户端创建成功"); |
| | | |
| | | // 自动创建表(使用直接SQL方式) |
| | | if (_config.AutoCreateTables) |
| | | { |
| | | CreateTablesIfNotExists(); |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | OnDatabaseError($"Oracle数据库初始化失败: {ex.Message}"); |
| | | throw; |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 使用SqlSugar初始化其他数据库 |
| | | /// </summary> |
| | | private void InitializeWithSqlSugar() |
| | | { |
| | | // 根据数据库类型创建SqlSugarClient |
| | | var dbType = GetDbType(_config.Type); |
| | | |
| | |
| | | { |
| | | CreateTablesIfNotExists(); |
| | | } |
| | | |
| | | OnDatabaseLog("数据库管理器初始化完成"); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | OnDatabaseError($"初始化数据库失败: {ex.Message}"); |
| | | throw; |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | |
| | | { |
| | | OnDatabaseLog("开始检查和创建数据库表..."); |
| | | |
| | | // 创建ModbusData表 |
| | | // 对Oracle使用直接SQL建表,避免SqlSugar语法问题 |
| | | if (GetDbType(_config.Type) == DbType.Oracle) |
| | | { |
| | | CreateOracleTablesDirectly(); |
| | | } |
| | | else |
| | | { |
| | | // 其他数据库使用SqlSugar CodeFirst |
| | | _db.CodeFirst.SetStringDefaultLength(200).InitTables(typeof(ModbusDataEntity)); |
| | | OnDatabaseLog($"ModbusData表检查完成,表名: {_config.Tables.ModbusDataTable}"); |
| | | |
| | | // 创建CommunicationLog表 |
| | | _db.CodeFirst.SetStringDefaultLength(200).InitTables(typeof(CommunicationLogEntity)); |
| | | OnDatabaseLog($"CommunicationLog表检查完成,表名: {_config.Tables.CommunicationLogTable}"); |
| | | |
| | | // 创建ErrorLog表 |
| | | _db.CodeFirst.SetStringDefaultLength(200).InitTables(typeof(ErrorLogEntity)); |
| | | OnDatabaseLog($"ErrorLog表检查完成,表名: {_config.Tables.ErrorLogTable}"); |
| | | |
| | | // 创建Statistics表 |
| | | _db.CodeFirst.SetStringDefaultLength(200).InitTables(typeof(StatisticsEntity)); |
| | | OnDatabaseLog($"Statistics表检查完成,表名: {_config.Tables.StatisticsTable}"); |
| | | } |
| | | |
| | | OnDatabaseLog("所有数据库表检查完成"); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | OnDatabaseError($"创建数据库表失败: {ex.Message}"); |
| | | throw; |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 直接使用SQL为Oracle创建表 |
| | | /// </summary> |
| | | private void CreateOracleTablesDirectly() |
| | | { |
| | | try |
| | | { |
| | | OnDatabaseLog("开始使用原生Oracle SQL创建表..."); |
| | | |
| | | using (var connection = new OracleConnection(_config.ConnectionString)) |
| | | { |
| | | connection.Open(); |
| | | OnDatabaseLog("Oracle连接已打开,开始执行建表脚本..."); |
| | | |
| | | // 1. 创建ModbusData表(主要数据表) |
| | | var createModbusDataTable = $@" |
| | | BEGIN |
| | | EXECUTE IMMEDIATE 'CREATE TABLE {_config.Tables.ModbusDataTable} ( |
| | | ID NUMBER PRIMARY KEY, |
| | | READ_TIME TIMESTAMP NOT NULL, |
| | | PROJECT_NAME VARCHAR2(100) NOT NULL, |
| | | PLC_IP_ADDRESS VARCHAR2(50) NOT NULL, |
| | | CONTROL_SIGNALS_JSON CLOB, |
| | | PRODUCT_DATA_JSON CLOB, |
| | | MEASUREMENT_DATA_JSON CLOB, |
| | | RAW_REGISTERS_JSON CLOB, |
| | | CREATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL |
| | | )'; |
| | | EXCEPTION |
| | | WHEN OTHERS THEN |
| | | IF SQLCODE != -955 THEN -- -955 = table already exists |
| | | RAISE; |
| | | END IF; |
| | | END;"; |
| | | |
| | | using (var cmd = new OracleCommand(createModbusDataTable, connection)) |
| | | { |
| | | cmd.ExecuteNonQuery(); |
| | | } |
| | | OnDatabaseLog($"ModbusData表检查完成,表名: {_config.Tables.ModbusDataTable}"); |
| | | |
| | | // 2. 创建CommunicationLog表 |
| | | var createCommLogTable = $@" |
| | | BEGIN |
| | | EXECUTE IMMEDIATE 'CREATE TABLE {_config.Tables.CommunicationLogTable} ( |
| | | ID NUMBER PRIMARY KEY, |
| | | LOG_TIME TIMESTAMP NOT NULL, |
| | | PROJECT_NAME VARCHAR2(100) NOT NULL, |
| | | PLC_IP_ADDRESS VARCHAR2(50) NOT NULL, |
| | | EVENT_TYPE VARCHAR2(50) NOT NULL, |
| | | EVENT_DESCRIPTION VARCHAR2(500), |
| | | IS_SUCCESS NUMBER(1) DEFAULT 1, |
| | | DURATION_MS NUMBER, |
| | | ADDITIONAL_DATA_JSON CLOB |
| | | )'; |
| | | EXCEPTION |
| | | WHEN OTHERS THEN |
| | | IF SQLCODE != -955 THEN |
| | | RAISE; |
| | | END IF; |
| | | END;"; |
| | | |
| | | using (var cmd = new OracleCommand(createCommLogTable, connection)) |
| | | { |
| | | cmd.ExecuteNonQuery(); |
| | | } |
| | | OnDatabaseLog($"CommunicationLog表检查完成,表名: {_config.Tables.CommunicationLogTable}"); |
| | | |
| | | // 3. 创建ErrorLog表 |
| | | var createErrorLogTable = $@" |
| | | BEGIN |
| | | EXECUTE IMMEDIATE 'CREATE TABLE {_config.Tables.ErrorLogTable} ( |
| | | ID NUMBER PRIMARY KEY, |
| | | ERROR_TIME TIMESTAMP NOT NULL, |
| | | PROJECT_NAME VARCHAR2(100) NOT NULL, |
| | | PLC_IP_ADDRESS VARCHAR2(50) NOT NULL, |
| | | ERROR_TYPE VARCHAR2(100) NOT NULL, |
| | | ERROR_MESSAGE VARCHAR2(1000) NOT NULL, |
| | | STACK_TRACE CLOB, |
| | | SEVERITY VARCHAR2(20) DEFAULT ''Medium'', |
| | | RETRY_COUNT NUMBER DEFAULT 0, |
| | | IS_RESOLVED NUMBER(1) DEFAULT 0 |
| | | )'; |
| | | EXCEPTION |
| | | WHEN OTHERS THEN |
| | | IF SQLCODE != -955 THEN |
| | | RAISE; |
| | | END IF; |
| | | END;"; |
| | | |
| | | using (var cmd = new OracleCommand(createErrorLogTable, connection)) |
| | | { |
| | | cmd.ExecuteNonQuery(); |
| | | } |
| | | OnDatabaseLog($"ErrorLog表检查完成,表名: {_config.Tables.ErrorLogTable}"); |
| | | |
| | | // 4. 创建Statistics表 |
| | | var createStatsTable = $@" |
| | | BEGIN |
| | | EXECUTE IMMEDIATE 'CREATE TABLE {_config.Tables.StatisticsTable} ( |
| | | ID NUMBER PRIMARY KEY, |
| | | STATISTICS_TIME TIMESTAMP NOT NULL, |
| | | PROJECT_NAME VARCHAR2(100) NOT NULL, |
| | | PLC_IP_ADDRESS VARCHAR2(50) NOT NULL, |
| | | TOTAL_CONNECTION_ATTEMPTS NUMBER NOT NULL, |
| | | SUCCESSFUL_CONNECTIONS NUMBER NOT NULL, |
| | | TOTAL_DATA_READS NUMBER NOT NULL, |
| | | SUCCESSFUL_DATA_READS NUMBER NOT NULL, |
| | | TOTAL_ERRORS NUMBER NOT NULL, |
| | | AVERAGE_RESPONSE_TIME_MS NUMBER NOT NULL, |
| | | MAX_RESPONSE_TIME_MS NUMBER NOT NULL, |
| | | MIN_RESPONSE_TIME_MS NUMBER NOT NULL, |
| | | CONNECTION_SUCCESS_RATE NUMBER NOT NULL, |
| | | DATA_READ_SUCCESS_RATE NUMBER NOT NULL, |
| | | PERIOD_START TIMESTAMP NOT NULL, |
| | | PERIOD_END TIMESTAMP NOT NULL |
| | | )'; |
| | | EXCEPTION |
| | | WHEN OTHERS THEN |
| | | IF SQLCODE != -955 THEN |
| | | RAISE; |
| | | END IF; |
| | | END;"; |
| | | |
| | | using (var cmd = new OracleCommand(createStatsTable, connection)) |
| | | { |
| | | cmd.ExecuteNonQuery(); |
| | | } |
| | | OnDatabaseLog($"Statistics表检查完成,表名: {_config.Tables.StatisticsTable}"); |
| | | |
| | | connection.Close(); |
| | | OnDatabaseLog("所有Oracle表创建完成,连接已关闭"); |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | OnDatabaseError($"Oracle建表失败: {ex.Message}"); |
| | | throw; |
| | | } |
| | | } |
| | |
| | | |
| | | #endregion |
| | | |
| | | #region 连接测试方法 |
| | | |
| | | /// <summary> |
| | | /// 测试数据库连接 |
| | | /// </summary> |
| | | /// <returns>连接测试结果</returns> |
| | | public async Task<(bool Success, string Message)> TestConnectionAsync() |
| | | { |
| | | try |
| | | { |
| | | if (!_config.Enabled) |
| | | { |
| | | return (false, "数据库功能已禁用"); |
| | | } |
| | | |
| | | if (string.IsNullOrEmpty(_config.ConnectionString)) |
| | | { |
| | | return (false, "数据库连接字符串为空"); |
| | | } |
| | | |
| | | // 对于Oracle数据库,使用原生连接方式测试 |
| | | if (_config.Type.ToLower() == "oracle") |
| | | { |
| | | return await TestOracleConnectionAsync(); |
| | | } |
| | | |
| | | // 创建临时连接进行测试 |
| | | var dbType = GetDbType(_config.Type); |
| | | using var testDb = new SqlSugarClient(new SqlSugar.ConnectionConfig() |
| | | { |
| | | ConnectionString = _config.ConnectionString, |
| | | DbType = dbType, |
| | | IsAutoCloseConnection = true |
| | | }); |
| | | |
| | | // 执行简单查询测试连接 |
| | | string testQuery = dbType switch |
| | | { |
| | | DbType.SqlServer => "SELECT 1", |
| | | DbType.MySql => "SELECT 1", |
| | | DbType.Sqlite => "SELECT 1", |
| | | DbType.PostgreSQL => "SELECT 1", |
| | | _ => "SELECT 1" |
| | | }; |
| | | |
| | | var result = await testDb.Ado.GetScalarAsync(testQuery); |
| | | |
| | | if (result != null) |
| | | { |
| | | return (true, $"连接成功!数据库类型: {_config.Type}"); |
| | | } |
| | | else |
| | | { |
| | | return (false, "连接测试查询返回空结果"); |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return (false, $"连接失败: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 测试Oracle数据库连接的专用方法 |
| | | /// </summary> |
| | | private async Task<(bool Success, string Message)> TestOracleConnectionAsync() |
| | | { |
| | | try |
| | | { |
| | | using var connection = new Oracle.ManagedDataAccess.Client.OracleConnection(_config.ConnectionString); |
| | | await connection.OpenAsync(); |
| | | |
| | | using var command = connection.CreateCommand(); |
| | | command.CommandText = "SELECT SYSDATE FROM DUAL"; |
| | | var result = await command.ExecuteScalarAsync(); |
| | | |
| | | if (result != null) |
| | | { |
| | | await connection.CloseAsync(); |
| | | return (true, $"Oracle连接成功!当前时间: {result}"); |
| | | } |
| | | else |
| | | { |
| | | return (false, "Oracle连接测试查询返回空结果"); |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return (false, $"Oracle连接失败: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 静态方法:测试数据库连接(用于配置验证) |
| | | /// </summary> |
| | | /// <param name="config">数据库配置</param> |
| | | /// <returns>连接测试结果</returns> |
| | | public static async Task<(bool Success, string Message)> TestConnectionAsync(DatabaseConfig config) |
| | | { |
| | | if (config == null) |
| | | { |
| | | return (false, "数据库配置为空"); |
| | | } |
| | | |
| | | if (!config.Enabled) |
| | | { |
| | | return (false, "数据库功能已禁用"); |
| | | } |
| | | |
| | | if (string.IsNullOrEmpty(config.ConnectionString)) |
| | | { |
| | | return (false, "数据库连接字符串为空"); |
| | | } |
| | | |
| | | try |
| | | { |
| | | // 对于Oracle,直接使用原生连接 |
| | | if (config.Type.ToLower() == "oracle") |
| | | { |
| | | using var connection = new OracleConnection(config.ConnectionString); |
| | | await connection.OpenAsync(); |
| | | |
| | | using var command = connection.CreateCommand(); |
| | | command.CommandText = "SELECT SYSDATE FROM DUAL"; |
| | | var result = await command.ExecuteScalarAsync(); |
| | | |
| | | if (result != null) |
| | | { |
| | | await connection.CloseAsync(); |
| | | return (true, $"Oracle连接成功!当前时间: {result}"); |
| | | } |
| | | else |
| | | { |
| | | return (false, "Oracle连接测试查询返回空结果"); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | // 其他数据库使用SqlSugar |
| | | var tempManager = new DatabaseManager(config, "TestConnection", "127.0.0.1"); |
| | | try |
| | | { |
| | | return await tempManager.TestConnectionAsync(); |
| | | } |
| | | finally |
| | | { |
| | | tempManager.Dispose(); |
| | | } |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return (false, $"连接失败: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region 辅助方法 |
| | | |
| | | /// <summary> |