|
|
@@ -40,11 +40,6 @@ public class DataLoggingService : IDisposable
|
|
|
/// </summary>
|
|
|
public int DeviceStatusLogIntervalSeconds { get; set; } = 300;
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// 数据保留天数
|
|
|
- /// </summary>
|
|
|
- public int DataRetentionDays { get; set; } = 30;
|
|
|
-
|
|
|
public DataLoggingService(PlcPollingService pollingService)
|
|
|
{
|
|
|
_pollingService = pollingService;
|
|
|
@@ -151,7 +146,6 @@ public class DataLoggingService : IDisposable
|
|
|
{
|
|
|
var flowTimer = 0;
|
|
|
var deviceTimer = 0;
|
|
|
- var cleanupTimer = 0;
|
|
|
|
|
|
while (!ct.IsCancellationRequested)
|
|
|
{
|
|
|
@@ -161,7 +155,6 @@ public class DataLoggingService : IDisposable
|
|
|
|
|
|
flowTimer++;
|
|
|
deviceTimer++;
|
|
|
- cleanupTimer++;
|
|
|
|
|
|
// 定期记录流量数据和模拟量
|
|
|
if (flowTimer >= FlowLogIntervalSeconds)
|
|
|
@@ -177,13 +170,7 @@ public class DataLoggingService : IDisposable
|
|
|
deviceTimer = 0;
|
|
|
await LogDeviceStatusAsync();
|
|
|
}
|
|
|
-
|
|
|
- // 每天清理一次过期数据
|
|
|
- if (cleanupTimer >= 86400)
|
|
|
- {
|
|
|
- cleanupTimer = 0;
|
|
|
- await CleanupOldDataAsync();
|
|
|
- }
|
|
|
+ // 数据清理由 DataRetentionService 统一管理(使用 SystemConfig.DataRetention 配置)
|
|
|
}
|
|
|
catch (OperationCanceledException)
|
|
|
{
|
|
|
@@ -237,6 +224,12 @@ public class DataLoggingService : IDisposable
|
|
|
try
|
|
|
{
|
|
|
var data = _pollingService.Data;
|
|
|
+ if (!data.HasValidData)
|
|
|
+ {
|
|
|
+ Log.Debug("PLC 未连接,跳过流量记录");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
var (totalIn, totalOut) = AccumulateFlow(data.InflowRate, data.OutflowRate);
|
|
|
|
|
|
var record = new FlowRecord
|
|
|
@@ -259,31 +252,65 @@ public class DataLoggingService : IDisposable
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
- /// 记录模拟量数据(液位、泵频率等)
|
|
|
+ /// 记录模拟量数据(按项目统计配置动态采集)
|
|
|
/// </summary>
|
|
|
public async Task LogAnalogDataAsync()
|
|
|
{
|
|
|
try
|
|
|
{
|
|
|
var data = _pollingService.Data;
|
|
|
+ if (!data.HasValidData)
|
|
|
+ {
|
|
|
+ Log.Debug("PLC 未连接,跳过模拟量记录");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
var now = DateTime.Now;
|
|
|
+ var profile = ProjectService.Current;
|
|
|
+
|
|
|
+ List<AnalogRecord> records;
|
|
|
|
|
|
- var records = new List<AnalogRecord>
|
|
|
+ if (profile?.Statistics?.Items is { Count: > 0 })
|
|
|
{
|
|
|
- new() { RecordTime = now, TagName = "Tank1Level", Value = data.Tank1Level },
|
|
|
- new() { RecordTime = now, TagName = "Tank2Level", Value = data.Tank2Level },
|
|
|
- new() { RecordTime = now, TagName = "Tank3Level", Value = data.Tank3Level },
|
|
|
- new() { RecordTime = now, TagName = "Tank4Level", Value = data.Tank4Level },
|
|
|
- new() { RecordTime = now, TagName = "Pump1Freq", Value = data.Pump1Freq },
|
|
|
- new() { RecordTime = now, TagName = "Pump2Freq", Value = data.Pump2Freq },
|
|
|
- new() { RecordTime = now, TagName = "Pump3Freq", Value = data.Pump3Freq },
|
|
|
- new() { RecordTime = now, TagName = "Pump4Freq", Value = data.Pump4Freq },
|
|
|
- new() { RecordTime = now, TagName = "Pump5Freq", Value = data.Pump5Freq },
|
|
|
- new() { RecordTime = now, TagName = "InflowRate", Value = data.InflowRate },
|
|
|
- new() { RecordTime = now, TagName = "OutflowRate", Value = data.OutflowRate },
|
|
|
- };
|
|
|
+ // ── 按项目统计配置采集 ──
|
|
|
+ records = new List<AnalogRecord>();
|
|
|
+ foreach (var item in profile.Statistics.Items)
|
|
|
+ {
|
|
|
+ if (!item.Enabled) continue;
|
|
|
+
|
|
|
+ // 从动态寄存器读取值(TagName 与 AnalogRegisters key 约定一致)
|
|
|
+ if (data.AnalogRegisters.TryGetValue(item.Key, out var value))
|
|
|
+ {
|
|
|
+ records.Add(new AnalogRecord
|
|
|
+ {
|
|
|
+ RecordTime = now,
|
|
|
+ TagName = item.Key,
|
|
|
+ Value = value
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // ── 兼容旧逻辑(无统计配置时使用硬编码) ──
|
|
|
+ records = new List<AnalogRecord>
|
|
|
+ {
|
|
|
+ new() { RecordTime = now, TagName = "Tank1Level", Value = data.Tank1Level },
|
|
|
+ new() { RecordTime = now, TagName = "Tank2Level", Value = data.Tank2Level },
|
|
|
+ new() { RecordTime = now, TagName = "Tank3Level", Value = data.Tank3Level },
|
|
|
+ new() { RecordTime = now, TagName = "Tank4Level", Value = data.Tank4Level },
|
|
|
+ new() { RecordTime = now, TagName = "Pump1Freq", Value = data.Pump1Freq },
|
|
|
+ new() { RecordTime = now, TagName = "Pump2Freq", Value = data.Pump2Freq },
|
|
|
+ new() { RecordTime = now, TagName = "Pump3Freq", Value = data.Pump3Freq },
|
|
|
+ new() { RecordTime = now, TagName = "Pump4Freq", Value = data.Pump4Freq },
|
|
|
+ new() { RecordTime = now, TagName = "Pump5Freq", Value = data.Pump5Freq },
|
|
|
+ new() { RecordTime = now, TagName = "InflowRate", Value = data.InflowRate },
|
|
|
+ new() { RecordTime = now, TagName = "OutflowRate", Value = data.OutflowRate },
|
|
|
+ };
|
|
|
+ }
|
|
|
|
|
|
- await _bgDb.Insertable(records).ExecuteCommandAsync();
|
|
|
+ if (records.Count > 0)
|
|
|
+ await _bgDb.Insertable(records).ExecuteCommandAsync();
|
|
|
Log.Debug("模拟量数据已记录: {Count} 个标签", records.Count);
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
@@ -300,6 +327,12 @@ public class DataLoggingService : IDisposable
|
|
|
try
|
|
|
{
|
|
|
var data = _pollingService.Data;
|
|
|
+ if (!data.HasValidData)
|
|
|
+ {
|
|
|
+ Log.Debug("PLC 未连接,跳过设备状态记录");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
var now = DateTime.Now;
|
|
|
var intervalHours = (float)(now - _lastDeviceLogTime).TotalHours;
|
|
|
_lastDeviceLogTime = now;
|
|
|
@@ -371,32 +404,40 @@ public class DataLoggingService : IDisposable
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
- /// 清理过期数据
|
|
|
+ /// 清理过期数据(使用 SystemConfig.DataRetention 配置,与 DataRetentionService 保持一致)
|
|
|
+ /// 通常由 DataRetentionService 自动调度,此方法仅供手动触发
|
|
|
/// </summary>
|
|
|
public async Task CleanupOldDataAsync()
|
|
|
{
|
|
|
try
|
|
|
{
|
|
|
- var cutoff = DateTime.Now.AddDays(-DataRetentionDays);
|
|
|
+ var config = ConfigService.GetConfig();
|
|
|
+ var flowRetention = config?.DataRetention?.FlowRecordRetentionDays ?? 360;
|
|
|
+ var analogRetention = config?.DataRetention?.AnalogRecordRetentionDays ?? 360;
|
|
|
+ var alarmRetention = config?.DataRetention?.AlarmRecordRetentionDays ?? 90;
|
|
|
+
|
|
|
+ var flowCutoff = DateTime.Now.AddDays(-flowRetention);
|
|
|
+ var analogCutoff = DateTime.Now.AddDays(-analogRetention);
|
|
|
+ var alarmCutoff = DateTime.Now.AddDays(-alarmRetention);
|
|
|
|
|
|
var flowDeleted = await _bgDb.Deleteable<FlowRecord>()
|
|
|
- .Where(r => r.RecordTime < cutoff)
|
|
|
+ .Where(r => r.RecordTime < flowCutoff)
|
|
|
.ExecuteCommandAsync();
|
|
|
|
|
|
var alarmDeleted = await _bgDb.Deleteable<AlarmRecord>()
|
|
|
- .Where(r => r.AlarmTime < cutoff && r.IsConfirmed)
|
|
|
+ .Where(r => r.AlarmTime < alarmCutoff && r.IsConfirmed)
|
|
|
.ExecuteCommandAsync();
|
|
|
|
|
|
var equipDeleted = await _bgDb.Deleteable<EquipmentStatus>()
|
|
|
- .Where(r => r.LastUpdateTime < cutoff)
|
|
|
+ .Where(r => r.LastUpdateTime < flowCutoff)
|
|
|
.ExecuteCommandAsync();
|
|
|
|
|
|
var analogDeleted = await _bgDb.Deleteable<AnalogRecord>()
|
|
|
- .Where(r => r.RecordTime < cutoff)
|
|
|
+ .Where(r => r.RecordTime < analogCutoff)
|
|
|
.ExecuteCommandAsync();
|
|
|
|
|
|
- Log.Information("数据清理完成: 删除 {Flow} 条流量记录, {Alarm} 条报警记录, {Equip} 条设备状态, {Analog} 条模拟量记录",
|
|
|
- flowDeleted, alarmDeleted, equipDeleted, analogDeleted);
|
|
|
+ Log.Information("数据清理完成: 删除 {Flow} 条流量(>{FlowDays}d), {Alarm} 条报警(>{AlarmDays}d), {Equip} 条设备, {Analog} 条模拟量(>{AnalogDays}d)",
|
|
|
+ flowDeleted, flowRetention, alarmDeleted, alarmRetention, equipDeleted, analogDeleted, analogRetention);
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|