feat: add PowerOutageLog entity, service, migration and API endpoints
This commit is contained in:
@@ -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}" });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user