feat: add PowerOutageLog entity, service, migration and API endpoints

This commit is contained in:
2026-06-01 18:16:13 +03:30
parent 9fe575ae07
commit 97a946ebea
5 changed files with 1778 additions and 3 deletions

View File

@@ -8,13 +8,16 @@ namespace GreenHome.Api.Controllers;
public class PowerOutageController : ControllerBase
{
private readonly IAlertService _alertService;
private readonly IPowerOutageLogService _powerOutageLogService;
private readonly ILogger<PowerOutageController> _logger;
public PowerOutageController(
IAlertService alertService,
IPowerOutageLogService powerOutageLogService,
ILogger<PowerOutageController> logger)
{
_alertService = alertService;
_powerOutageLogService = powerOutageLogService;
_logger = logger;
}
@@ -40,9 +43,9 @@ public class PowerOutageController : ControllerBase
_logger.LogInformation("Power outage alert processed for device {DeviceId}", deviceId);
return Ok(new {
success = true,
message = "هشدار قطع برق با موفقیت ارسال شد"
return Ok(new {
success = true,
message = "هشدار قطع برق با موفقیت ارسال شد"
});
}
catch (InvalidOperationException ex)
@@ -56,5 +59,175 @@ public class PowerOutageController : ControllerBase
return StatusCode(500, new { error = "خطا در ارسال هشدار قطع برق" });
}
}
/// <summary>
/// دریافت لیست رویدادهای قطع و وصل برق یک دستگاه
/// </summary>
/// <param name="deviceId">شناسه دستگاه</param>
/// <param name="startUtc">شروع بازه (UTC)</param>
/// <param name="endUtc">پایان بازه (UTC)</param>
/// <param name="eventType">نوع رویداد: 1 = وصل, 2 = قطع (اختیاری)</param>
/// <param name="page">شماره صفحه</param>
/// <param name="pageSize">تعداد در هر صفحه</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>لیست رویدادها</returns>
[HttpGet("logs")]
public async Task<ActionResult<PagedResult<PowerOutageLogDto>>> GetLogs(
[FromQuery] int deviceId,
[FromQuery] DateTime? startUtc,
[FromQuery] DateTime? endUtc,
[FromQuery] int? eventType,
[FromQuery] int page = 1,
[FromQuery] int pageSize = 20,
CancellationToken cancellationToken = default)
{
try
{
if (deviceId <= 0)
{
return BadRequest(new { error = "شناسه دستگاه نامعتبر است" });
}
var filter = new PowerOutageFilter
{
EventType = eventType,
StartDateUtc = startUtc,
EndDateUtc = endUtc,
Page = page,
PageSize = pageSize
};
var result = await _powerOutageLogService.ListAsync(deviceId, filter, cancellationToken);
return Ok(result);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting power outage logs: DeviceId={DeviceId}", deviceId);
return StatusCode(500, new { error = "خطا در دریافت لیست رویدادهای برق" });
}
}
/// <summary>
/// دریافت خلاصه قطعی‌های برق یک دستگاه در یک بازه زمانی
/// </summary>
/// <param name="deviceId">شناسه دستگاه</param>
/// <param name="startUtc">شروع بازه (UTC)</param>
/// <param name="endUtc">پایان بازه (UTC)</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>خلاصه قطعی‌ها شامل تعداد قطعی‌ها و مجموع مدت زمان قطعی</returns>
[HttpGet("summary")]
public async Task<ActionResult<PowerOutageSummaryDto>> GetSummary(
[FromQuery] int deviceId,
[FromQuery] DateTime? startUtc,
[FromQuery] DateTime? endUtc,
CancellationToken cancellationToken = default)
{
try
{
if (deviceId <= 0)
{
return BadRequest(new { error = "شناسه دستگاه نامعتبر است" });
}
var filter = new PowerOutageFilter
{
StartDateUtc = startUtc,
EndDateUtc = endUtc,
PageSize = int.MaxValue // Get all records for summary
};
var result = await _powerOutageLogService.ListAsync(deviceId, filter, cancellationToken);
var outages = result.Items.Where(x => x.CurrentPower == 0).ToList();
var connections = result.Items.Where(x => x.CurrentPower == 1).ToList();
// Calculate total downtime by pairing disconnects with subsequent connects
long totalDowntimeSeconds = 0;
var pairedOutages = new List<PowerOutagePairDto>();
foreach (var outage in outages)
{
// Find the next connect event after this outage
var nextConnect = connections
.Where(c => c.TimestampUtc > outage.TimestampUtc)
.OrderBy(c => c.TimestampUtc)
.FirstOrDefault();
if (nextConnect != null)
{
var duration = (nextConnect.TimestampUtc - outage.TimestampUtc).TotalSeconds;
totalDowntimeSeconds += (long)duration;
pairedOutages.Add(new PowerOutagePairDto
{
DisconnectedAt = outage.TimestampUtc,
ConnectedAt = nextConnect.TimestampUtc,
DurationSeconds = (long)duration
});
}
else
{
// Still disconnected (no subsequent connect)
pairedOutages.Add(new PowerOutagePairDto
{
DisconnectedAt = outage.TimestampUtc,
ConnectedAt = null,
DurationSeconds = null
});
}
}
var summary = new PowerOutageSummaryDto
{
DeviceId = deviceId,
TotalOutageCount = outages.Count,
TotalDowntimeSeconds = totalDowntimeSeconds,
StartDateUtc = startUtc,
EndDateUtc = endUtc,
Outages = pairedOutages
};
return Ok(summary);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting power outage summary: DeviceId={DeviceId}", deviceId);
return StatusCode(500, new { error = "خطا در دریافت خلاصه قطعی‌های برق" });
}
}
/// <summary>
/// مهاجرت داده‌های قدیمی از جدول Telemetry به PowerOutageLogs
/// این اندپوینت یکبار مصرف است - بعد از اجرا می‌توانید آن را غیرفعال کنید
/// </summary>
/// <param name="deviceId">اختیاری - اگر داده شود فقط برای همان دستگاه مهاجرت می‌کند</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>تعداد رکوردهای مهاجرت داده شده</returns>
[HttpPost("migrate")]
public async Task<ActionResult> MigrateFromTelemetry(
[FromQuery] int? deviceId,
CancellationToken cancellationToken)
{
try
{
_logger.LogInformation("Starting migration from Telemetry to PowerOutageLogs...");
var count = await _powerOutageLogService.MigrateFromTelemetryAsync(deviceId, cancellationToken);
_logger.LogInformation("Migration completed: {Count} records inserted", count);
return Ok(new
{
success = true,
message = $"مهاجرت با موفقیت انجام شد. {count} رکورد به جدول PowerOutageLogs اضافه شد.",
recordsInserted = count
});
}
catch (Exception ex)
{
_logger.LogError(ex, "Error during migration from Telemetry to PowerOutageLogs");
return StatusCode(500, new { error = $"خطا در مهاجرت داده‌ها: {ex.Message}" });
}
}
}