Files
GreenHomeBack/src/GreenHome.Api/Controllers/TelemetryController.cs

146 lines
6.1 KiB
C#

using Microsoft.AspNetCore.Mvc;
using GreenHome.Application;
using System.Text;
namespace GreenHome.Api.Controllers;
[ApiController]
[Route("api/[controller]")]
public class TelemetryController : ControllerBase
{
private readonly ITelemetryService telemetryService;
private readonly IAlertService alertService;
private readonly IAlertConditionService alertConditionService;
public TelemetryController(ITelemetryService telemetryService, IAlertService alertService, IAlertConditionService alertConditionService)
{
this.telemetryService = telemetryService;
this.alertService = alertService;
this.alertConditionService = alertConditionService;
}
[HttpGet]
public async Task<ActionResult<PagedResult<TelemetryDto>>> List([FromQuery] int? deviceId, [FromQuery] DateTime? startUtc, [FromQuery] DateTime? endUtc, [FromQuery] DateTime? sinceUtc, [FromQuery] int page = 1, [FromQuery] int pageSize = 20, CancellationToken cancellationToken = default)
{
var filter = new TelemetryFilter { DeviceId = deviceId, StartDateUtc = startUtc, EndDateUtc = endUtc, SinceUtc = sinceUtc, Page = page, PageSize = pageSize };
var result = await telemetryService.ListAsync(filter, cancellationToken);
return Ok(result);
}
[HttpGet("AddData")]
public async Task<ActionResult<string>> Create([FromQuery] TelemetryDto telementry, [FromQuery] string? lastUpdateDate, CancellationToken cancellationToken)
{
telementry.TimestampUtc = DateTime.UtcNow;
var id = await telemetryService.AddAsync(telementry, cancellationToken);
var parts = new List<string> { $"ID:{id}" };
var deviceId = telementry.DeviceId;
if (deviceId > 0)
{
// 1. Check alerts and power status
var alertResult = await alertService.CheckAndSendAlertsAsync(deviceId, telementry, cancellationToken);
if (alertResult != null)
{
if (alertResult.Messages.Count > 0)
{
parts.Add($"A:{alertResult.Messages.Count}");
parts.Add($"R:{alertResult.Recipients}");
parts.Add($"M:{string.Join("@", alertResult.Messages)}");
}
if (alertResult.PowerStatus != null)
{
parts.Add($"P:{alertResult.PowerStatus}");
}
}
// 2. Check relay conditions update
if (!string.IsNullOrEmpty(lastUpdateDate))
{
var parsedDate = ParseLastUpdateDate(lastUpdateDate);
var relayConditions = await alertConditionService.GetUpdatedRelayConditionsAsync(deviceId, parsedDate, cancellationToken);
if (relayConditions.Count > 0)
{
parts.Add("RU:1");
parts.Add($"RL:{relayConditions.Count}");
parts.Add($"RC:{EncodeRelayConditions(relayConditions)}");
}
}
}
return Ok(string.Join("|", parts));
}
/// <summary>
/// Encode relay conditions into a compact string format for STM32 parsing.
/// Format: E{enabled}T{timeType}R{relay}S{sensor}O{op}V{value1}[,{value2}]
/// Multiple conditions separated by |, multiple rules within a condition separated by &
/// </summary>
private static string EncodeRelayConditions(IReadOnlyList<RelayConditionDto> conditions)
{
return string.Join("|", conditions.Select(c =>
{
var rules = string.Join("&", c.Rules.Select(r =>
{
var rule = $"S{r.Sensor}O{r.Op}V{r.V1}";
if (r.V2.HasValue)
rule += $",{r.V2.Value}";
return rule;
}));
return $"E{c.Enabled}T{c.TimeType}R{c.Relay}{rules}";
}));
}
/// <summary>
/// Parse date string in format: yyyy/MM/dd-HH:mm
/// </summary>
private static DateTime? ParseLastUpdateDate(string dateStr)
{
if (string.IsNullOrEmpty(dateStr))
return null;
// Try parsing as yyyy/MM/dd-HH:mm
if (DateTime.TryParseExact(dateStr, "yyyy/MM/dd-HH:mm", null, System.Globalization.DateTimeStyles.None, out var result))
{
return DateTime.SpecifyKind(result, DateTimeKind.Utc);
}
// Try parsing as yyyy-MM-dd HH:mm (ISO-like)
if (DateTime.TryParseExact(dateStr, "yyyy-MM-dd HH:mm", null, System.Globalization.DateTimeStyles.None, out var result2))
{
return DateTime.SpecifyKind(result2, DateTimeKind.Utc);
}
return null;
}
[HttpGet("minmax")]
public async Task<ActionResult<TelemetryMinMax>> MinMax([FromQuery] int deviceId, [FromQuery] DateTime? startUtc, [FromQuery] DateTime? endUtc, CancellationToken cancellationToken)
{
var result = await telemetryService.GetMinMaxAsync(deviceId, startUtc, endUtc, cancellationToken);
return Ok(result);
}
[HttpGet("days")]
public async Task<ActionResult<IReadOnlyList<DayCount>>> Days([FromQuery] int deviceId, [FromQuery] int year, [FromQuery] int month, CancellationToken cancellationToken)
{
var result = await telemetryService.GetMonthDaysAsync(deviceId, year, month, cancellationToken);
return Ok(result);
}
[HttpGet("months")]
public async Task<ActionResult<IReadOnlyList<int>>> Months([FromQuery] int deviceId, [FromQuery] int year, CancellationToken cancellationToken)
{
var result = await telemetryService.GetActiveMonthsAsync(deviceId, year, cancellationToken);
return Ok(result);
}
[HttpGet("aggregate")]
public async Task<ActionResult<IReadOnlyList<TelemetryBucketDto>>> Aggregate([FromQuery] int deviceId, [FromQuery] DateTime? startUtc, [FromQuery] DateTime? endUtc, [FromQuery] int bucketMinutes = 15, CancellationToken cancellationToken = default)
{
var result = await telemetryService.GetAggregatedAsync(deviceId, startUtc, endUtc, bucketMinutes, cancellationToken);
return Ok(result);
}
}