将Feature/XP.Common和Feature/XP.Hardware分支合并至Develop/XP.forHardwareAndCommon,完善XPapp注册和相关硬件类库通用类库功能。
This commit is contained in:
@@ -0,0 +1,577 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using XP.Common.Database.Interfaces;
|
||||
using XP.Common.Logging.Interfaces;
|
||||
using XP.Hardware.RaySource.Config;
|
||||
|
||||
namespace XP.Hardware.RaySource.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// 灯丝寿命管理服务实现 | Filament Lifetime Management Service Implementation
|
||||
/// 负责灯丝使用时长的记录、计算、异常恢复和预警判断
|
||||
/// Responsible for filament usage duration recording, calculation, anomaly recovery, and warning evaluation
|
||||
/// </summary>
|
||||
public class FilamentLifetimeService : IFilamentLifetimeService
|
||||
{
|
||||
private readonly IDbContext _dbContext;
|
||||
private readonly RaySourceConfig _config;
|
||||
private readonly ILoggerService _logger;
|
||||
|
||||
// 内存状态 | In-memory state
|
||||
private DateTime? _filamentStartTime;
|
||||
private string _sourceType = "";
|
||||
private string _serialNumber = "";
|
||||
private double _thresholdSeconds;
|
||||
private readonly object _filamentLock = new object();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsInitialized { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsFilamentOn => _filamentStartTime != null;
|
||||
|
||||
#region 构造函数 | Constructor
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数,注入依赖 | Constructor with dependency injection
|
||||
/// </summary>
|
||||
/// <param name="dbContext">数据库上下文 | Database context</param>
|
||||
/// <param name="config">射线源配置 | Ray source configuration</param>
|
||||
/// <param name="logger">日志服务 | Logger service</param>
|
||||
public FilamentLifetimeService(IDbContext dbContext, RaySourceConfig config, ILoggerService logger)
|
||||
{
|
||||
_dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
|
||||
_config = config ?? throw new ArgumentNullException(nameof(config));
|
||||
_logger = logger?.ForModule<FilamentLifetimeService>() ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 初始化 | Initialization
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Initialize()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 1. 读取配置 | Read configuration
|
||||
_sourceType = _config.SourceType;
|
||||
_serialNumber = _config.SerialNumber;
|
||||
_thresholdSeconds = _config.TotalLifeThreshold * 3600;
|
||||
|
||||
// 2. 验证配置(SourceType 或 SerialNumber 为空时阻止初始化)
|
||||
// Validate configuration (block initialization if SourceType or SerialNumber is empty)
|
||||
if (string.IsNullOrWhiteSpace(_sourceType) || string.IsNullOrWhiteSpace(_serialNumber))
|
||||
{
|
||||
_logger.Error(null, "灯丝寿命服务初始化失败:SourceType 或 SerialNumber 为空,SourceType={SourceType},SerialNumber={SerialNumber} | " +
|
||||
"Filament lifetime service initialization failed: SourceType or SerialNumber is empty, SourceType={SourceType}, SerialNumber={SerialNumber}",
|
||||
_sourceType ?? "", _serialNumber ?? "");
|
||||
IsInitialized = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3. 建表(CREATE TABLE IF NOT EXISTS)| Create tables
|
||||
if (!CreateTables())
|
||||
{
|
||||
IsInitialized = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 4. 异常恢复(关闭未结束记录)| Anomaly recovery (close unclosed records)
|
||||
RecoverUnclosedRecords();
|
||||
|
||||
// 5. 重算累计寿命 | Recalculate accumulated lifetime
|
||||
RecalculateTotalLifeSeconds();
|
||||
|
||||
IsInitialized = true;
|
||||
_logger.Info("灯丝寿命服务初始化成功,SourceType={SourceType},SerialNumber={SerialNumber},阈值={Threshold}小时 | " +
|
||||
"Filament lifetime service initialized successfully, SourceType={SourceType}, SerialNumber={SerialNumber}, threshold={Threshold} hours",
|
||||
_sourceType, _serialNumber, _config.TotalLifeThreshold);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "灯丝寿命服务初始化异常 | Filament lifetime service initialization exception: {Message}", ex.Message);
|
||||
IsInitialized = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建数据库表 | Create database tables
|
||||
/// </summary>
|
||||
private bool CreateTables()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 创建累计统计表 | Create lifetime statistics table
|
||||
var createStatsSql = @"
|
||||
CREATE TABLE IF NOT EXISTS RaySourceFilamentLifetimeStatistics (
|
||||
Id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
SourceType TEXT NOT NULL,
|
||||
SerialNumber TEXT NOT NULL,
|
||||
TotalLifeSeconds REAL NOT NULL DEFAULT 0,
|
||||
LastUpdateTime TEXT NOT NULL DEFAULT '1970-01-01T00:00:00Z'
|
||||
);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_lifetime_serial ON RaySourceFilamentLifetimeStatistics(SerialNumber);
|
||||
CREATE INDEX IF NOT EXISTS idx_lifetime_source_type ON RaySourceFilamentLifetimeStatistics(SourceType);";
|
||||
|
||||
var statsResult = _dbContext.ExecuteNonQuery(createStatsSql);
|
||||
if (!statsResult.IsSuccess)
|
||||
{
|
||||
_logger.Error(statsResult.Exception, "创建统计表失败:{Message} | Failed to create statistics table: {Message}", statsResult.Message);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 创建使用流水表 | Create usage logs table
|
||||
var createLogsSql = @"
|
||||
CREATE TABLE IF NOT EXISTS RaySourceFilamentUsageLogs (
|
||||
Id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
SourceType TEXT NOT NULL,
|
||||
SerialNumber TEXT NOT NULL,
|
||||
StartTime TEXT NOT NULL,
|
||||
EndTime TEXT,
|
||||
DurationSeconds REAL NOT NULL DEFAULT 0,
|
||||
Status INTEGER NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_usage_serial ON RaySourceFilamentUsageLogs(SerialNumber);";
|
||||
|
||||
var logsResult = _dbContext.ExecuteNonQuery(createLogsSql);
|
||||
if (!logsResult.IsSuccess)
|
||||
{
|
||||
_logger.Error(logsResult.Exception, "创建流水表失败:{Message} | Failed to create usage logs table: {Message}", logsResult.Message);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "建表异常 | Exception creating tables: {Message}", ex.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异常恢复:关闭未结束的流水记录 | Anomaly recovery: close unclosed usage log records
|
||||
/// </summary>
|
||||
private void RecoverUnclosedRecords()
|
||||
{
|
||||
try
|
||||
{
|
||||
var querySql = @"
|
||||
SELECT Id, StartTime FROM RaySourceFilamentUsageLogs
|
||||
WHERE SourceType = @SourceType AND SerialNumber = @SerialNumber AND EndTime IS NULL";
|
||||
|
||||
var queryParams = new Dictionary<string, object>
|
||||
{
|
||||
{ "SourceType", _sourceType },
|
||||
{ "SerialNumber", _serialNumber }
|
||||
};
|
||||
|
||||
var (queryResult, dataTable) = _dbContext.ExecuteDataTable(querySql, queryParams);
|
||||
if (!queryResult.IsSuccess || dataTable == null || dataTable.Rows.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.Warn("检测到 {Count} 条未关闭的灯丝使用记录,开始异常恢复 | " +
|
||||
"Detected {Count} unclosed filament usage records, starting anomaly recovery",
|
||||
dataTable.Rows.Count);
|
||||
|
||||
foreach (DataRow row in dataTable.Rows)
|
||||
{
|
||||
var id = Convert.ToInt64(row["Id"]);
|
||||
var startTimeStr = row["StartTime"]?.ToString() ?? "";
|
||||
|
||||
// 将未关闭记录的 EndTime 设为 StartTime,DurationSeconds 设为 0,Status 设为 1(异常中断)
|
||||
// Set unclosed record's EndTime to StartTime, DurationSeconds to 0, Status to 1 (abnormal interruption)
|
||||
var updateSql = @"
|
||||
UPDATE RaySourceFilamentUsageLogs
|
||||
SET EndTime = StartTime, DurationSeconds = 0, Status = 1
|
||||
WHERE Id = @Id";
|
||||
|
||||
var updateParams = new Dictionary<string, object>
|
||||
{
|
||||
{ "Id", id }
|
||||
};
|
||||
|
||||
_dbContext.ExecuteNonQuery(updateSql, updateParams);
|
||||
_logger.Warn("异常恢复:关闭未结束记录,SourceType={SourceType},SerialNumber={SerialNumber},StartTime={StartTime} | " +
|
||||
"Anomaly recovery: closed unclosed record, SourceType={SourceType}, SerialNumber={SerialNumber}, StartTime={StartTime}",
|
||||
_sourceType, _serialNumber, startTimeStr);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "异常恢复失败 | Anomaly recovery failed: {Message}", ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 基于流水表重算累计寿命 | Recalculate accumulated lifetime based on usage logs
|
||||
/// </summary>
|
||||
private void RecalculateTotalLifeSeconds()
|
||||
{
|
||||
try
|
||||
{
|
||||
var sumSql = @"
|
||||
SELECT COALESCE(SUM(DurationSeconds), 0) FROM RaySourceFilamentUsageLogs
|
||||
WHERE SourceType = @SourceType AND SerialNumber = @SerialNumber";
|
||||
|
||||
var sumParams = new Dictionary<string, object>
|
||||
{
|
||||
{ "SourceType", _sourceType },
|
||||
{ "SerialNumber", _serialNumber }
|
||||
};
|
||||
|
||||
var (sumResult, totalSeconds) = _dbContext.ExecuteScalar<double>(sumSql, sumParams);
|
||||
if (!sumResult.IsSuccess)
|
||||
{
|
||||
_logger.Error(sumResult.Exception, "重算累计寿命失败:查询 DurationSeconds 总和出错,{Message} | " +
|
||||
"Failed to recalculate lifetime: error querying DurationSeconds sum, {Message}", sumResult.Message);
|
||||
return;
|
||||
}
|
||||
|
||||
var nowUtc = DateTime.UtcNow.ToString("o");
|
||||
|
||||
// 更新或插入统计表 | Update or insert statistics table
|
||||
var upsertSql = @"
|
||||
INSERT INTO RaySourceFilamentLifetimeStatistics (SourceType, SerialNumber, TotalLifeSeconds, LastUpdateTime)
|
||||
VALUES (@SourceType, @SerialNumber, @TotalLifeSeconds, @LastUpdateTime)
|
||||
ON CONFLICT(SerialNumber) DO UPDATE SET TotalLifeSeconds = @TotalLifeSeconds, LastUpdateTime = @LastUpdateTime";
|
||||
|
||||
var updateParams = new Dictionary<string, object>
|
||||
{
|
||||
{ "TotalLifeSeconds", totalSeconds },
|
||||
{ "LastUpdateTime", nowUtc },
|
||||
{ "SourceType", _sourceType },
|
||||
{ "SerialNumber", _serialNumber }
|
||||
};
|
||||
|
||||
var updateResult = _dbContext.ExecuteNonQuery(upsertSql, updateParams);
|
||||
if (!updateResult.IsSuccess)
|
||||
{
|
||||
_logger.Error(updateResult.Exception, "重算累计寿命失败:更新统计表出错,{Message} | " +
|
||||
"Failed to recalculate lifetime: error updating statistics table, {Message}", updateResult.Message);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "重算累计寿命异常 | Exception recalculating lifetime: {Message}", ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 灯丝开启与关闭 | Filament Start and Stop
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool StartFilamentUsage()
|
||||
{
|
||||
if (!IsInitialized)
|
||||
{
|
||||
_logger.Warn("灯丝寿命服务未初始化,无法开始记录 | Filament lifetime service not initialized, cannot start recording");
|
||||
return false;
|
||||
}
|
||||
|
||||
lock (_filamentLock)
|
||||
{
|
||||
// 锁内再次检查,防止并发重复开始 | Double-check inside lock to prevent concurrent duplicate start
|
||||
if (_filamentStartTime != null)
|
||||
{
|
||||
_logger.Warn("灯丝已处于开启状态,忽略重复开始请求 | Filament already on, ignoring duplicate start request");
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// 1. 确保统计表中存在当前设备记录 | Ensure current device record exists in statistics table
|
||||
var ensureStatsSql = @"
|
||||
INSERT OR IGNORE INTO RaySourceFilamentLifetimeStatistics (SourceType, SerialNumber, TotalLifeSeconds, LastUpdateTime)
|
||||
VALUES (@SourceType, @SerialNumber, 0, @LastUpdateTime)";
|
||||
|
||||
var ensureParams = new Dictionary<string, object>
|
||||
{
|
||||
{ "SourceType", _sourceType },
|
||||
{ "SerialNumber", _serialNumber },
|
||||
{ "LastUpdateTime", DateTime.UtcNow.ToString("o") }
|
||||
};
|
||||
|
||||
var ensureResult = _dbContext.ExecuteNonQuery(ensureStatsSql, ensureParams);
|
||||
if (!ensureResult.IsSuccess)
|
||||
{
|
||||
_logger.Error(ensureResult.Exception, "确保统计记录存在失败:{Message},SourceType={SourceType},SerialNumber={SerialNumber} | " +
|
||||
"Failed to ensure statistics record exists: {Message}, SourceType={SourceType}, SerialNumber={SerialNumber}",
|
||||
ensureResult.Message, _sourceType, _serialNumber);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. 向流水表插入新记录 | Insert new usage log record
|
||||
var startTime = DateTime.UtcNow;
|
||||
var startTimeStr = startTime.ToString("o");
|
||||
|
||||
var insertLogSql = @"
|
||||
INSERT INTO RaySourceFilamentUsageLogs (SourceType, SerialNumber, StartTime, EndTime, DurationSeconds, Status)
|
||||
VALUES (@SourceType, @SerialNumber, @StartTime, NULL, 0, 0)";
|
||||
|
||||
var insertParams = new Dictionary<string, object>
|
||||
{
|
||||
{ "SourceType", _sourceType },
|
||||
{ "SerialNumber", _serialNumber },
|
||||
{ "StartTime", startTimeStr }
|
||||
};
|
||||
|
||||
var insertResult = _dbContext.ExecuteNonQuery(insertLogSql, insertParams);
|
||||
if (!insertResult.IsSuccess)
|
||||
{
|
||||
_logger.Error(insertResult.Exception, "插入灯丝使用流水记录失败:{Message},SourceType={SourceType},SerialNumber={SerialNumber} | " +
|
||||
"Failed to insert filament usage log: {Message}, SourceType={SourceType}, SerialNumber={SerialNumber}",
|
||||
insertResult.Message, _sourceType, _serialNumber);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3. 记录内存起始时间(仅在数据库插入成功后更新)| Record in-memory start time
|
||||
_filamentStartTime = startTime;
|
||||
|
||||
_logger.Info("灯丝使用开始,SourceType={SourceType},SerialNumber={SerialNumber},StartTime={StartTime} | " +
|
||||
"Filament usage started, SourceType={SourceType}, SerialNumber={SerialNumber}, StartTime={StartTime}",
|
||||
_sourceType, _serialNumber, startTimeStr);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "开始灯丝使用记录异常,SourceType={SourceType},SerialNumber={SerialNumber} | " +
|
||||
"Exception starting filament usage recording, SourceType={SourceType}, SerialNumber={SerialNumber}",
|
||||
_sourceType, _serialNumber);
|
||||
return false;
|
||||
}
|
||||
} // end lock
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool StopFilamentUsage()
|
||||
{
|
||||
if (!IsInitialized)
|
||||
{
|
||||
_logger.Warn("灯丝寿命服务未初始化,无法停止记录 | Filament lifetime service not initialized, cannot stop recording");
|
||||
return false;
|
||||
}
|
||||
|
||||
lock (_filamentLock)
|
||||
{
|
||||
// 锁内再次检查,防止并发重复停止 | Double-check inside lock to prevent concurrent duplicate stop
|
||||
if (_filamentStartTime == null)
|
||||
{
|
||||
_logger.Warn("灯丝已处于关闭状态,忽略重复停止请求 | Filament already off, ignoring duplicate stop request");
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// 1. 查找当前设备最近一条未关闭的记录 | Find the most recent unclosed record
|
||||
var querySql = @"
|
||||
SELECT Id, StartTime FROM RaySourceFilamentUsageLogs
|
||||
WHERE SourceType = @SourceType AND SerialNumber = @SerialNumber AND EndTime IS NULL
|
||||
ORDER BY Id DESC LIMIT 1";
|
||||
|
||||
var queryParams = new Dictionary<string, object>
|
||||
{
|
||||
{ "SourceType", _sourceType },
|
||||
{ "SerialNumber", _serialNumber }
|
||||
};
|
||||
|
||||
var (queryResult, dataTable) = _dbContext.ExecuteDataTable(querySql, queryParams);
|
||||
if (!queryResult.IsSuccess || dataTable == null || dataTable.Rows.Count == 0)
|
||||
{
|
||||
_logger.Warn("未找到当前设备的未结束流水记录,SourceType={SourceType},SerialNumber={SerialNumber} | " +
|
||||
"No unclosed usage log found, SourceType={SourceType}, SerialNumber={SerialNumber}",
|
||||
_sourceType, _serialNumber);
|
||||
return false;
|
||||
}
|
||||
|
||||
var row = dataTable.Rows[0];
|
||||
var recordId = Convert.ToInt64(row["Id"]);
|
||||
var startTimeStr = row["StartTime"]?.ToString() ?? "";
|
||||
|
||||
// 2. 计算 DurationSeconds | Calculate DurationSeconds
|
||||
var endTime = DateTime.UtcNow;
|
||||
var endTimeStr = endTime.ToString("o");
|
||||
|
||||
double durationSeconds = 0;
|
||||
if (DateTime.TryParse(startTimeStr, null, System.Globalization.DateTimeStyles.RoundtripKind, out var startTime))
|
||||
{
|
||||
durationSeconds = (endTime - startTime).TotalSeconds;
|
||||
if (durationSeconds < 0) durationSeconds = 0;
|
||||
}
|
||||
|
||||
// 3. 顺序更新流水表和统计表(ExecuteNonQuery 不支持外部事务参数,改为顺序执行)
|
||||
// Sequential update: usage log then statistics
|
||||
|
||||
// 3.1 更新流水记录 | Update usage log
|
||||
var updateLogSql = @"
|
||||
UPDATE RaySourceFilamentUsageLogs
|
||||
SET EndTime = @EndTime, DurationSeconds = @DurationSeconds, Status = 1
|
||||
WHERE Id = @Id";
|
||||
|
||||
var updateLogParams = new Dictionary<string, object>
|
||||
{
|
||||
{ "EndTime", endTimeStr },
|
||||
{ "DurationSeconds", durationSeconds },
|
||||
{ "Id", recordId }
|
||||
};
|
||||
|
||||
var updateLogResult = _dbContext.ExecuteNonQuery(updateLogSql, updateLogParams);
|
||||
if (!updateLogResult.IsSuccess)
|
||||
{
|
||||
_logger.Error(updateLogResult.Exception, "更新流水记录失败:{Message} | Failed to update usage log: {Message}", updateLogResult.Message);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3.2 累加统计表 | Accumulate statistics
|
||||
var updateStatsSql = @"
|
||||
UPDATE RaySourceFilamentLifetimeStatistics
|
||||
SET TotalLifeSeconds = TotalLifeSeconds + @DurationSeconds, LastUpdateTime = @LastUpdateTime
|
||||
WHERE SourceType = @SourceType AND SerialNumber = @SerialNumber";
|
||||
|
||||
var updateStatsParams = new Dictionary<string, object>
|
||||
{
|
||||
{ "DurationSeconds", durationSeconds },
|
||||
{ "LastUpdateTime", endTimeStr },
|
||||
{ "SourceType", _sourceType },
|
||||
{ "SerialNumber", _serialNumber }
|
||||
};
|
||||
|
||||
var updateStatsResult = _dbContext.ExecuteNonQuery(updateStatsSql, updateStatsParams);
|
||||
if (!updateStatsResult.IsSuccess)
|
||||
{
|
||||
_logger.Error(updateStatsResult.Exception, "更新统计记录失败:{Message} | Failed to update statistics: {Message}", updateStatsResult.Message);
|
||||
// 流水已更新但统计失败,记录警告(下次 RecalculateTotalLifeSeconds 可恢复)
|
||||
_logger.Warn("统计表更新失败但流水记录已关闭,可通过重算恢复 | Statistics update failed but usage log closed, recoverable via recalculation");
|
||||
}
|
||||
|
||||
// 4. 清除内存起始时间 | Clear in-memory start time
|
||||
_filamentStartTime = null;
|
||||
|
||||
// 5. 查询更新后的 TotalLifeSeconds 用于日志 | Query updated TotalLifeSeconds for logging
|
||||
var totalLifeSeconds = 0.0;
|
||||
var totalSql = @"
|
||||
SELECT TotalLifeSeconds FROM RaySourceFilamentLifetimeStatistics
|
||||
WHERE SourceType = @SourceType AND SerialNumber = @SerialNumber";
|
||||
|
||||
var totalParams = new Dictionary<string, object>
|
||||
{
|
||||
{ "SourceType", _sourceType },
|
||||
{ "SerialNumber", _serialNumber }
|
||||
};
|
||||
|
||||
var (totalResult, totalData) = _dbContext.ExecuteScalar<double>(totalSql, totalParams);
|
||||
if (totalResult.IsSuccess)
|
||||
{
|
||||
totalLifeSeconds = totalData;
|
||||
}
|
||||
|
||||
_logger.Info("灯丝使用结束,SourceType={SourceType},SerialNumber={SerialNumber},DurationSeconds={DurationSeconds},TotalLifeSeconds={TotalLifeSeconds} | " +
|
||||
"Filament usage stopped, SourceType={SourceType}, SerialNumber={SerialNumber}, DurationSeconds={DurationSeconds}, TotalLifeSeconds={TotalLifeSeconds}",
|
||||
_sourceType, _serialNumber, durationSeconds, totalLifeSeconds);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "停止灯丝使用记录异常,SourceType={SourceType},SerialNumber={SerialNumber} | " +
|
||||
"Exception stopping filament usage recording, SourceType={SourceType}, SerialNumber={SerialNumber}",
|
||||
_sourceType, _serialNumber);
|
||||
return false;
|
||||
}
|
||||
} // end lock
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 查询与计算 | Query and Calculation
|
||||
|
||||
/// <inheritdoc/>
|
||||
public double GetTotalLifeSeconds()
|
||||
{
|
||||
if (!IsInitialized)
|
||||
{
|
||||
_logger.Warn("服务未初始化,返回默认值 0 | Service not initialized, returning default value 0");
|
||||
return 0;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var sql = @"
|
||||
SELECT COALESCE(TotalLifeSeconds, 0) FROM RaySourceFilamentLifetimeStatistics
|
||||
WHERE SourceType = @SourceType AND SerialNumber = @SerialNumber";
|
||||
|
||||
var parameters = new Dictionary<string, object>
|
||||
{
|
||||
{ "SourceType", _sourceType },
|
||||
{ "SerialNumber", _serialNumber }
|
||||
};
|
||||
|
||||
var (result, totalSeconds) = _dbContext.ExecuteScalar<double>(sql, parameters);
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
_logger.Error(result.Exception, "查询累计使用秒数失败,{Message} | Failed to query total life seconds, {Message}", result.Message);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return totalSeconds;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "查询累计使用秒数异常,SourceType={SourceType},SerialNumber={SerialNumber} | " +
|
||||
"Exception querying total life seconds, SourceType={SourceType}, SerialNumber={SerialNumber}",
|
||||
_sourceType, _serialNumber);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public double GetCurrentTotalLifeSeconds()
|
||||
{
|
||||
var dbSeconds = GetTotalLifeSeconds();
|
||||
|
||||
// 如果灯丝正在运行,加上本次运行时长 | If filament is on, add current session duration
|
||||
if (_filamentStartTime.HasValue)
|
||||
{
|
||||
var currentSessionSeconds = (DateTime.UtcNow - _filamentStartTime.Value).TotalSeconds;
|
||||
dbSeconds += currentSessionSeconds;
|
||||
}
|
||||
|
||||
return dbSeconds;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public double GetThresholdSeconds()
|
||||
{
|
||||
return _thresholdSeconds;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public double GetLifetimePercentage()
|
||||
{
|
||||
// 处理阈值为 0 的边界情况,避免除零 | Handle zero threshold to avoid division by zero
|
||||
if (_thresholdSeconds <= 0)
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
|
||||
var totalSeconds = GetCurrentTotalLifeSeconds();
|
||||
return Math.Min(totalSeconds / _thresholdSeconds * 100, 100);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool ShouldShowLifetimeWarning()
|
||||
{
|
||||
return GetLifetimePercentage() >= 90;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user