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 AddAsync(TelemetryDto dto, CancellationToken cancellationToken) { var entity = mapper.Map(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> 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 { Items = mapper.Map>(items), TotalCount = total, Page = filter.Page, PageSize = filter.PageSize }; } public async Task 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> 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> 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; } }