using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using HMI.Core.Util;
using log4net;
using Plugin.DeviceDebug.Entity;
namespace Plugin.DeviceDebug.Model
{
///
/// 数据访问层 - 管理调试会话和操作审计的持久化
///
public class DeviceDebugModel
{
private static readonly ILog log = LogManager.GetLogger(typeof(DeviceDebugModel));
private SqliteHelper? _sqliteHelper;
private string _dbPath = string.Empty;
///
/// 初始化数据库,创建必要的表结构
///
public void InitDatabase(string dbPath)
{
try
{
_dbPath = dbPath;
// 确保目录存在
string? dir = Path.GetDirectoryName(dbPath);
if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
// 如果数据库文件不存在,创建它
if (!File.Exists(dbPath))
{
SqliteHelper.CreateDatabase(dbPath);
log.Info($"创建调试数据库: {dbPath}");
}
_sqliteHelper = new SqliteHelper(dbPath);
// 创建 debug_session 表
string createSessionTable = @"
CREATE TABLE IF NOT EXISTS debug_session (
session_id TEXT PRIMARY KEY,
device_id TEXT NOT NULL,
operator_id TEXT NOT NULL,
operator_name TEXT,
effective_version TEXT,
state INTEGER NOT NULL DEFAULT 0,
reservation_ref TEXT,
created_at TEXT NOT NULL,
closed_at TEXT
)";
// 创建 debug_operation 表
string createOperationTable = @"
CREATE TABLE IF NOT EXISTS debug_operation (
operation_id TEXT PRIMARY KEY,
session_id TEXT NOT NULL,
command_type TEXT NOT NULL,
parameters TEXT,
outcome TEXT NOT NULL,
device_readback TEXT,
error_code TEXT,
error_message TEXT,
timestamp TEXT NOT NULL,
FOREIGN KEY (session_id) REFERENCES debug_session(session_id)
)";
_sqliteHelper.ExecuteSql(createSessionTable);
_sqliteHelper.ExecuteSql(createOperationTable);
log.Info("调试数据库表初始化完成");
}
catch (Exception ex)
{
log.Error($"初始化调试数据库失败: {ex.Message}", ex);
throw;
}
}
#region 会话 CRUD
///
/// 创建会话
///
public void CreateSession(DebugSession session)
{
try
{
if (_sqliteHelper == null)
throw new InvalidOperationException("数据库未初始化,请先调用 InitDatabase");
string sql = $@"
INSERT INTO debug_session (
session_id, device_id, operator_id, operator_name,
effective_version, state, reservation_ref, created_at, closed_at
) VALUES (
'{EscapeSql(session.SessionId)}',
'{EscapeSql(session.DeviceId)}',
'{EscapeSql(session.OperatorId)}',
'{EscapeSql(session.OperatorName)}',
'{EscapeSql(session.EffectiveVersion)}',
{(int)session.State},
'{EscapeSql(session.ReservationRef)}',
'{session.CreatedAt:yyyy-MM-dd HH:mm:ss}',
{(session.ClosedAt.HasValue ? $"'{session.ClosedAt.Value:yyyy-MM-dd HH:mm:ss}'" : "NULL")}
)";
_sqliteHelper.ExecuteSql(sql);
log.Debug($"创建会话: {session.SessionId}, 设备: {session.DeviceId}");
}
catch (Exception ex)
{
log.Error($"创建会话失败: {ex.Message}", ex);
throw;
}
}
///
/// 获取会话
///
public DebugSession? GetSession(string sessionId)
{
try
{
if (_sqliteHelper == null)
throw new InvalidOperationException("数据库未初始化,请先调用 InitDatabase");
string sql = $"SELECT * FROM debug_session WHERE session_id = '{EscapeSql(sessionId)}'";
DataTable dt = _sqliteHelper.GetDataTable(sql);
if (dt.Rows.Count == 0)
return null;
return MapToDebugSession(dt.Rows[0]);
}
catch (Exception ex)
{
log.Error($"获取会话失败: {ex.Message}", ex);
return null;
}
}
///
/// 更新会话
///
public void UpdateSession(DebugSession session)
{
try
{
if (_sqliteHelper == null)
throw new InvalidOperationException("数据库未初始化,请先调用 InitDatabase");
string sql = $@"
UPDATE debug_session SET
device_id = '{EscapeSql(session.DeviceId)}',
operator_id = '{EscapeSql(session.OperatorId)}',
operator_name = '{EscapeSql(session.OperatorName)}',
effective_version = '{EscapeSql(session.EffectiveVersion)}',
state = {(int)session.State},
reservation_ref = '{EscapeSql(session.ReservationRef)}',
closed_at = {(session.ClosedAt.HasValue ? $"'{session.ClosedAt.Value:yyyy-MM-dd HH:mm:ss}'" : "NULL")}
WHERE session_id = '{EscapeSql(session.SessionId)}'";
_sqliteHelper.ExecuteSql(sql);
log.Debug($"更新会话: {session.SessionId}, 状态: {session.State}");
}
catch (Exception ex)
{
log.Error($"更新会话失败: {ex.Message}", ex);
throw;
}
}
///
/// 获取设备的所有活跃会话(未关闭的)
///
public List GetActiveSessionsByDevice(string deviceId)
{
try
{
if (_sqliteHelper == null)
throw new InvalidOperationException("数据库未初始化,请先调用 InitDatabase");
string sql = $@"
SELECT * FROM debug_session
WHERE device_id = '{EscapeSql(deviceId)}'
AND state != {(int)DebugSessionState.Closed}
ORDER BY created_at DESC";
DataTable dt = _sqliteHelper.GetDataTable(sql);
return dt.Rows.Cast().Select(MapToDebugSession).ToList();
}
catch (Exception ex)
{
log.Error($"获取设备活跃会话失败: {ex.Message}", ex);
return new List();
}
}
///
/// 获取所有会话
///
public List GetAllSessions()
{
try
{
if (_sqliteHelper == null)
throw new InvalidOperationException("数据库未初始化,请先调用 InitDatabase");
string sql = "SELECT * FROM debug_session ORDER BY created_at DESC";
DataTable dt = _sqliteHelper.GetDataTable(sql);
return dt.Rows.Cast().Select(MapToDebugSession).ToList();
}
catch (Exception ex)
{
log.Error($"获取所有会话失败: {ex.Message}", ex);
return new List();
}
}
#endregion
#region 审计 CRUD
///
/// 记录操作
///
public void RecordOperation(DebugOperation operation)
{
try
{
if (_sqliteHelper == null)
throw new InvalidOperationException("数据库未初始化,请先调用 InitDatabase");
string sql = $@"
INSERT INTO debug_operation (
operation_id, session_id, command_type, parameters,
outcome, device_readback, error_code, error_message, timestamp
) VALUES (
'{EscapeSql(operation.OperationId)}',
'{EscapeSql(operation.SessionId)}',
'{EscapeSql(operation.CommandType)}',
'{EscapeSql(operation.Parameters)}',
'{EscapeSql(operation.Outcome)}',
'{EscapeSql(operation.DeviceReadback)}',
'{EscapeSql(operation.ErrorCode)}',
'{EscapeSql(operation.ErrorMessage)}',
'{operation.Timestamp:yyyy-MM-dd HH:mm:ss}'
)";
_sqliteHelper.ExecuteSql(sql);
log.Debug($"记录操作: {operation.OperationId}, 类型: {operation.CommandType}, 结果: {operation.Outcome}");
}
catch (Exception ex)
{
log.Error($"记录操作失败: {ex.Message}", ex);
throw;
}
}
///
/// 获取会话的所有操作
///
public List GetOperationsBySession(string sessionId)
{
try
{
if (_sqliteHelper == null)
throw new InvalidOperationException("数据库未初始化,请先调用 InitDatabase");
string sql = $@"
SELECT * FROM debug_operation
WHERE session_id = '{EscapeSql(sessionId)}'
ORDER BY timestamp DESC";
DataTable dt = _sqliteHelper.GetDataTable(sql);
return dt.Rows.Cast().Select(MapToDebugOperation).ToList();
}
catch (Exception ex)
{
log.Error($"获取会话操作失败: {ex.Message}", ex);
return new List();
}
}
///
/// 获取设备最近的操作
///
public List GetRecentOperations(string deviceId, int count)
{
try
{
if (_sqliteHelper == null)
throw new InvalidOperationException("数据库未初始化,请先调用 InitDatabase");
string sql = $@"
SELECT o.* FROM debug_operation o
INNER JOIN debug_session s ON o.session_id = s.session_id
WHERE s.device_id = '{EscapeSql(deviceId)}'
ORDER BY o.timestamp DESC
LIMIT {count}";
DataTable dt = _sqliteHelper.GetDataTable(sql);
return dt.Rows.Cast().Select(MapToDebugOperation).ToList();
}
catch (Exception ex)
{
log.Error($"获取最近操作失败: {ex.Message}", ex);
return new List();
}
}
#endregion
#region 辅助方法
///
/// 将 DataRow 映射为 DebugSession
///
private DebugSession MapToDebugSession(DataRow row)
{
return new DebugSession
{
SessionId = row["session_id"].ToString() ?? string.Empty,
DeviceId = row["device_id"].ToString() ?? string.Empty,
OperatorId = row["operator_id"].ToString() ?? string.Empty,
OperatorName = row["operator_name"]?.ToString() ?? string.Empty,
EffectiveVersion = row["effective_version"]?.ToString() ?? string.Empty,
State = (DebugSessionState)Convert.ToInt32(row["state"]),
ReservationRef = row["reservation_ref"]?.ToString() ?? string.Empty,
CreatedAt = DateTime.Parse(row["created_at"].ToString() ?? DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")),
ClosedAt = row["closed_at"] != DBNull.Value && !string.IsNullOrEmpty(row["closed_at"].ToString())
? DateTime.Parse(row["closed_at"].ToString()!)
: null
};
}
///
/// 将 DataRow 映射为 DebugOperation
///
private DebugOperation MapToDebugOperation(DataRow row)
{
return new DebugOperation
{
OperationId = row["operation_id"].ToString() ?? string.Empty,
SessionId = row["session_id"].ToString() ?? string.Empty,
CommandType = row["command_type"].ToString() ?? string.Empty,
Parameters = row["parameters"]?.ToString() ?? string.Empty,
Outcome = row["outcome"].ToString() ?? string.Empty,
DeviceReadback = row["device_readback"]?.ToString() ?? string.Empty,
ErrorCode = row["error_code"]?.ToString() ?? string.Empty,
ErrorMessage = row["error_message"]?.ToString() ?? string.Empty,
Timestamp = DateTime.Parse(row["timestamp"].ToString() ?? DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"))
};
}
///
/// SQL 字符串转义(防止注入)
///
private string EscapeSql(string value)
{
return value.Replace("'", "''");
}
#endregion
}
}