172 lines
6.4 KiB
C#
172 lines
6.4 KiB
C#
using System.Globalization;
|
|
using AutoMapper;
|
|
using GreenHome.Application;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace GreenHome.Infrastructure;
|
|
|
|
public sealed class TelemetryService : ITelemetryService
|
|
{
|
|
private readonly GreenHomeDbContext dbContext;
|
|
private readonly IMapper mapper;
|
|
private static readonly PersianCalendar PersianCalendar = new PersianCalendar();
|
|
|
|
public TelemetryService(GreenHomeDbContext dbContext, IMapper mapper)
|
|
{
|
|
this.dbContext = dbContext;
|
|
this.mapper = mapper;
|
|
}
|
|
|
|
public async Task<int> AddAsync(TelemetryDto dto, CancellationToken cancellationToken)
|
|
{
|
|
var entity = mapper.Map<Domain.TelemetryRecord>(dto);
|
|
if (!string.IsNullOrEmpty(dto.DeviceName))
|
|
{
|
|
var device = await dbContext.Devices.FirstOrDefaultAsync(d => d.DeviceName == dto.DeviceName, cancellationToken);
|
|
if (device != null)
|
|
{
|
|
entity.DeviceId = device.Id;
|
|
dto.DeviceId = device.Id; // Update DTO for alert service
|
|
}
|
|
}
|
|
if (dto.Id!=0)
|
|
{
|
|
var device = await dbContext.Devices.FirstOrDefaultAsync(d => d.Id == dto.Id, cancellationToken);
|
|
if (device != null)
|
|
{
|
|
entity.DeviceId = device.Id;
|
|
}
|
|
}
|
|
// ذخیره زمان سرور در لحظه ثبت
|
|
entity.ServerTimestampUtc = DateTime.Now;
|
|
|
|
var dt = dto.TimestampUtc.ToLocalTime();
|
|
var py = PersianCalendar.GetYear(dt);
|
|
var pm = PersianCalendar.GetMonth(dt);
|
|
var pd = PersianCalendar.GetDayOfMonth(dt);
|
|
entity.PersianYear = py;
|
|
entity.PersianMonth = pm;
|
|
entity.PersianDate = $"{py:0000}/{pm:00}/{pd:00}";
|
|
|
|
if(entity.Lux < 0)
|
|
{
|
|
var lastItem = await dbContext.TelemetryRecords
|
|
.Where(x => x.DeviceId == entity.DeviceId)
|
|
.OrderByDescending(x => x.TimestampUtc)
|
|
.FirstOrDefaultAsync(cancellationToken);
|
|
|
|
entity.Lux = lastItem?.Lux ?? 0;
|
|
}
|
|
|
|
dbContext.TelemetryRecords.Add(entity);
|
|
await dbContext.SaveChangesAsync(cancellationToken);
|
|
return entity.Id;
|
|
}
|
|
|
|
public async Task<PagedResult<TelemetryDto>> ListAsync(TelemetryFilter filter, CancellationToken cancellationToken)
|
|
{
|
|
var query = dbContext.TelemetryRecords.AsNoTracking().AsQueryable();
|
|
if (filter.DeviceId.HasValue)
|
|
{
|
|
query = query.Where(x => x.DeviceId == filter.DeviceId.Value);
|
|
}
|
|
if (filter.StartDateUtc.HasValue)
|
|
{
|
|
//var start = filter.StartDateUtc.Value.Date.AddDays(1);
|
|
|
|
query = query.Where(x => x.ServerTimestampUtc >= filter.StartDateUtc.Value.ToLocalTime());
|
|
}
|
|
|
|
if (filter.EndDateUtc.HasValue)
|
|
{
|
|
query = query.Where(x => x.ServerTimestampUtc < filter.EndDateUtc.Value.ToLocalTime());
|
|
}
|
|
|
|
if(filter.Page <= 0) filter.Page = 1;
|
|
if(filter.PageSize <= 0) filter.PageSize = 1000000; // No limit
|
|
var total = await query.CountAsync(cancellationToken);
|
|
var skip = (filter.Page - 1) * filter.PageSize;
|
|
var items = await query
|
|
.OrderByDescending(x => x.ServerTimestampUtc)
|
|
.Skip(skip)
|
|
.Take(filter.PageSize)
|
|
.ToListAsync(cancellationToken);
|
|
|
|
if(items.Any(x => x.Lux < 0))
|
|
{
|
|
for (int i = 1; i < items.Count; i++)
|
|
{
|
|
if (items[i].Lux < 0)
|
|
items[i].Lux = items[i - 1].Lux;
|
|
}
|
|
}
|
|
|
|
return new PagedResult<TelemetryDto>
|
|
{
|
|
Items = mapper.Map<IReadOnlyList<TelemetryDto>>(items),
|
|
TotalCount = total,
|
|
Page = filter.Page,
|
|
PageSize = filter.PageSize
|
|
};
|
|
}
|
|
|
|
public async Task<TelemetryMinMax> GetMinMaxAsync(int deviceId, DateTime? startUtc, DateTime? endUtc, CancellationToken cancellationToken)
|
|
{
|
|
var query = dbContext.TelemetryRecords.AsNoTracking().Where(x => x.DeviceId == deviceId);
|
|
if (startUtc.HasValue) query = query.Where(x => x.TimestampUtc >= startUtc.Value);
|
|
if (endUtc.HasValue) query = query.Where(x => x.TimestampUtc <= endUtc.Value);
|
|
|
|
var result = await query
|
|
.Select(x => new
|
|
{
|
|
x.TemperatureC,
|
|
x.HumidityPercent,
|
|
x.SoilPercent,
|
|
x.GasPPM,
|
|
x.Lux
|
|
})
|
|
.ToListAsync(cancellationToken);
|
|
|
|
if (result.Count == 0)
|
|
{
|
|
return new TelemetryMinMax();
|
|
}
|
|
|
|
return new TelemetryMinMax
|
|
{
|
|
MinTemperatureC = result.Min(x => x.TemperatureC),
|
|
MaxTemperatureC = result.Max(x => x.TemperatureC),
|
|
MinHumidityPercent = result.Min(x => x.HumidityPercent),
|
|
MaxHumidityPercent = result.Max(x => x.HumidityPercent),
|
|
MinSoilPercent = result.Min(x => x.SoilPercent),
|
|
MaxSoilPercent = result.Max(x => x.SoilPercent),
|
|
MinGasPPM = result.Min(x => x.GasPPM),
|
|
MaxGasPPM = result.Max(x => x.GasPPM),
|
|
MinLux = result.Min(x => x.Lux),
|
|
MaxLux = result.Max(x => x.Lux)
|
|
};
|
|
}
|
|
|
|
public async Task<IReadOnlyList<DayCount>> GetMonthDaysAsync(int deviceId, int persianYear, int persianMonth, CancellationToken cancellationToken)
|
|
{
|
|
var query = dbContext.TelemetryRecords.AsNoTracking()
|
|
.Where(x => x.DeviceId == deviceId && x.PersianYear == persianYear && x.PersianMonth == persianMonth)
|
|
.GroupBy(x => x.PersianDate)
|
|
.Select(g => new DayCount { PersianDate = g.Key, Count = g.Count() })
|
|
.OrderBy(x => x.PersianDate);
|
|
|
|
return await query.ToListAsync(cancellationToken);
|
|
}
|
|
|
|
public async Task<IReadOnlyList<int>> GetActiveMonthsAsync(int deviceId, int persianYear, CancellationToken cancellationToken)
|
|
{
|
|
var months = await dbContext.TelemetryRecords.AsNoTracking()
|
|
.Where(x => x.DeviceId == deviceId && x.PersianYear == persianYear)
|
|
.Select(x => x.PersianMonth)
|
|
.Distinct()
|
|
.OrderBy(x => x)
|
|
.ToListAsync(cancellationToken);
|
|
return months;
|
|
}
|
|
}
|