refactor: restructure Program.cs and enhance Telemetry response format
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using GreenHome.Application;
|
using GreenHome.Application;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace GreenHome.Api.Controllers;
|
namespace GreenHome.Api.Controllers;
|
||||||
|
|
||||||
@@ -29,38 +30,66 @@ public class TelemetryController : ControllerBase
|
|||||||
[HttpGet("AddData")]
|
[HttpGet("AddData")]
|
||||||
public async Task<ActionResult<string>> Create([FromQuery] TelemetryDto telementry, [FromQuery] string? lastUpdateDate, CancellationToken cancellationToken)
|
public async Task<ActionResult<string>> Create([FromQuery] TelemetryDto telementry, [FromQuery] string? lastUpdateDate, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
|
||||||
telementry.TimestampUtc = DateTime.UtcNow;
|
telementry.TimestampUtc = DateTime.UtcNow;
|
||||||
var id = await telemetryService.AddAsync(telementry, cancellationToken);
|
var id = await telemetryService.AddAsync(telementry, cancellationToken);
|
||||||
|
|
||||||
string? message;
|
var parts = new List<string> { $"ID:{id}" };
|
||||||
|
|
||||||
var deviceId = telementry.DeviceId;
|
var deviceId = telementry.DeviceId;
|
||||||
if(deviceId > 0)
|
|
||||||
|
if (deviceId > 0)
|
||||||
{
|
{
|
||||||
message = await alertService.CheckAndSendAlertsAsync(deviceId, telementry, cancellationToken);
|
// 1. Check alerts and power status
|
||||||
if(!string.IsNullOrEmpty(message))
|
var alertResult = await alertService.CheckAndSendAlertsAsync(deviceId, telementry, cancellationToken);
|
||||||
|
if (alertResult != null)
|
||||||
{
|
{
|
||||||
return Ok(message);
|
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}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if relay conditions have been updated since lastUpdateDate
|
// 2. Check relay conditions update
|
||||||
if (deviceId > 0 && !string.IsNullOrEmpty(lastUpdateDate))
|
if (!string.IsNullOrEmpty(lastUpdateDate))
|
||||||
{
|
{
|
||||||
var parsedDate = ParseLastUpdateDate(lastUpdateDate);
|
var parsedDate = ParseLastUpdateDate(lastUpdateDate);
|
||||||
// var relayConditions = await alertConditionService.GetUpdatedRelayConditionsAsync(deviceId, parsedDate, cancellationToken);
|
var relayConditions = await alertConditionService.GetUpdatedRelayConditionsAsync(deviceId, parsedDate, cancellationToken);
|
||||||
// if (relayConditions.Count > 0)
|
if (relayConditions.Count > 0)
|
||||||
// {
|
{
|
||||||
// return Ok(new { conditions = relayConditions });
|
parts.Add("RU:1");
|
||||||
// }
|
parts.Add($"RL:{relayConditions.Count}");
|
||||||
|
parts.Add($"RC:{EncodeRelayConditions(relayConditions)}");
|
||||||
var hasUpdates = await alertConditionService.HasUpdatedConditionsAsync(deviceId, parsedDate, cancellationToken);
|
}
|
||||||
if(hasUpdates)
|
}
|
||||||
return Ok("Relay");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(id + "");
|
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>
|
/// <summary>
|
||||||
|
|||||||
@@ -2,32 +2,63 @@ using FluentValidation;
|
|||||||
using GreenHome.AI.DeepSeek;
|
using GreenHome.AI.DeepSeek;
|
||||||
using GreenHome.Application;
|
using GreenHome.Application;
|
||||||
using GreenHome.Infrastructure;
|
using GreenHome.Infrastructure;
|
||||||
|
using GreenHome.Infrastructure.Data;
|
||||||
using GreenHome.Sms.Ippanel;
|
using GreenHome.Sms.Ippanel;
|
||||||
using GreenHome.VoiceCall.Avanak;
|
using GreenHome.VoiceCall.Avanak;
|
||||||
using Microsoft.AspNetCore.StaticFiles;
|
using Microsoft.AspNetCore.StaticFiles;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.FileProviders;
|
using Microsoft.Extensions.FileProviders;
|
||||||
using Scalar.AspNetCore;
|
using Scalar.AspNetCore;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
// Add services to the container.
|
// ============================================================
|
||||||
builder.Services.AddControllers();
|
// 1. Service Registration
|
||||||
builder.Services.AddEndpointsApiExplorer();
|
// ============================================================
|
||||||
builder.Services.AddOpenApi();
|
|
||||||
|
|
||||||
// Application/Infrastructure DI
|
ConfigureCoreServices(builder);
|
||||||
builder.Services.AddAutoMapper(typeof(GreenHome.Application.MappingProfile));
|
ConfigureCors(builder);
|
||||||
builder.Services.AddValidatorsFromAssemblyContaining<GreenHome.Application.DeviceDtoValidator>();
|
ConfigureExternalServices(builder);
|
||||||
|
|
||||||
// CORS Configuration
|
var app = builder.Build();
|
||||||
const string CorsPolicy = "DefaultCors";
|
|
||||||
builder.Services.AddCors(options =>
|
// ============================================================
|
||||||
|
// 2. Pipeline Configuration
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
ApplyMigrations(app);
|
||||||
|
ConfigureMiddleware(app);
|
||||||
|
ConfigureStaticFiles(app);
|
||||||
|
app.MapControllers();
|
||||||
|
|
||||||
|
app.Run();
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Service Registration Methods
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
void ConfigureCoreServices(WebApplicationBuilder builder)
|
||||||
{
|
{
|
||||||
|
builder.Services.AddControllers();
|
||||||
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
|
builder.Services.AddOpenApi();
|
||||||
|
|
||||||
|
builder.Services.AddAutoMapper(typeof(GreenHome.Application.MappingProfile));
|
||||||
|
builder.Services.AddValidatorsFromAssemblyContaining<GreenHome.Application.DeviceDtoValidator>();
|
||||||
|
|
||||||
|
builder.Services.AddInfrastructure(builder.Configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigureCors(WebApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
const string CorsPolicy = "DefaultCors";
|
||||||
|
|
||||||
|
builder.Services.AddCors(options =>
|
||||||
|
{
|
||||||
options.AddPolicy(CorsPolicy, policy =>
|
options.AddPolicy(CorsPolicy, policy =>
|
||||||
{
|
{
|
||||||
if (builder.Environment.IsDevelopment())
|
if (builder.Environment.IsDevelopment())
|
||||||
{
|
{
|
||||||
// در محیط Development همه origin ها مجاز هستند
|
|
||||||
policy
|
policy
|
||||||
.AllowAnyOrigin()
|
.AllowAnyOrigin()
|
||||||
.AllowAnyHeader()
|
.AllowAnyHeader()
|
||||||
@@ -35,7 +66,6 @@ builder.Services.AddCors(options =>
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// در محیط Production فقط دامنههای مشخص
|
|
||||||
policy
|
policy
|
||||||
.WithOrigins(
|
.WithOrigins(
|
||||||
"http://green.nabaksoft.ir",
|
"http://green.nabaksoft.ir",
|
||||||
@@ -48,46 +78,28 @@ builder.Services.AddCors(options =>
|
|||||||
.AllowCredentials();
|
.AllowCredentials();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
builder.Services.AddDbContext<GreenHome.Infrastructure.GreenHomeDbContext>(options =>
|
void ConfigureExternalServices(WebApplicationBuilder builder)
|
||||||
{
|
{
|
||||||
options.UseSqlServer(builder.Configuration.GetConnectionString("Default"));
|
builder.Services.AddIppanelSms(builder.Configuration);
|
||||||
});
|
builder.Services.AddAvanakVoiceCall(builder.Configuration);
|
||||||
builder.Services.AddScoped<GreenHome.Application.IDeviceService, GreenHome.Infrastructure.DeviceService>();
|
builder.Services.AddDeepSeek(builder.Configuration);
|
||||||
builder.Services.AddScoped<GreenHome.Application.ITelemetryService, GreenHome.Infrastructure.TelemetryService>();
|
}
|
||||||
builder.Services.AddScoped<GreenHome.Application.IDeviceSettingsService, GreenHome.Infrastructure.DeviceSettingsService>();
|
|
||||||
builder.Services.AddScoped<GreenHome.Application.IAuthService, GreenHome.Infrastructure.AuthService>();
|
|
||||||
builder.Services.AddScoped<GreenHome.Application.IAlertService, GreenHome.Infrastructure.AlertService>();
|
|
||||||
builder.Services.AddScoped<GreenHome.Application.IAlertConditionService, GreenHome.Infrastructure.AlertConditionService>();
|
|
||||||
builder.Services.AddScoped<GreenHome.Application.ISunCalculatorService, GreenHome.Infrastructure.SunCalculatorService>();
|
|
||||||
builder.Services.AddScoped<GreenHome.Application.IAIQueryService, GreenHome.Infrastructure.AIQueryService>();
|
|
||||||
builder.Services.AddScoped<GreenHome.Application.IDailyReportService, GreenHome.Infrastructure.DailyReportService>();
|
|
||||||
builder.Services.AddScoped<GreenHome.Application.IAlertLogService, GreenHome.Infrastructure.AlertLogService>();
|
|
||||||
builder.Services.AddScoped<GreenHome.Application.IUserDailyReportService, GreenHome.Infrastructure.UserDailyReportService>();
|
|
||||||
builder.Services.AddScoped<GreenHome.Application.IChecklistService, GreenHome.Infrastructure.ChecklistService>();
|
|
||||||
builder.Services.AddScoped<GreenHome.Application.IMonthlyReportService, GreenHome.Infrastructure.MonthlyReportService>();
|
|
||||||
builder.Services.AddScoped<GreenHome.Application.IDevicePostService, GreenHome.Infrastructure.DevicePostService>();
|
|
||||||
builder.Services.AddScoped<GreenHome.Application.IDeviceTokenService, GreenHome.Infrastructure.DeviceTokenService>();
|
|
||||||
|
|
||||||
// SMS Service Configuration
|
// ============================================================
|
||||||
builder.Services.AddIppanelSms(builder.Configuration);
|
// Middleware & Pipeline Methods
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
// Voice Call Service Configuration
|
void ApplyMigrations(WebApplication app)
|
||||||
builder.Services.AddAvanakVoiceCall(builder.Configuration);
|
|
||||||
|
|
||||||
// AI Service Configuration
|
|
||||||
builder.Services.AddDeepSeek(builder.Configuration);
|
|
||||||
|
|
||||||
var app = builder.Build();
|
|
||||||
|
|
||||||
// Apply pending migrations automatically
|
|
||||||
using (var scope = app.Services.CreateScope())
|
|
||||||
{
|
{
|
||||||
|
using var scope = app.Services.CreateScope();
|
||||||
var services = scope.ServiceProvider;
|
var services = scope.ServiceProvider;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var context = services.GetRequiredService<GreenHome.Infrastructure.GreenHomeDbContext>();
|
var context = services.GetRequiredService<GreenHomeDbContext>();
|
||||||
context.Database.Migrate();
|
context.Database.Migrate();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -97,28 +109,30 @@ using (var scope = app.Services.CreateScope())
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure the HTTP request pipeline.
|
void ConfigureMiddleware(WebApplication app)
|
||||||
app.MapOpenApi();
|
|
||||||
app.MapScalarApiReference();
|
|
||||||
if (!app.Environment.IsDevelopment())
|
|
||||||
{
|
{
|
||||||
|
app.MapOpenApi();
|
||||||
|
app.MapScalarApiReference();
|
||||||
|
|
||||||
|
if (!app.Environment.IsDevelopment())
|
||||||
|
{
|
||||||
app.UseHttpsRedirection();
|
app.UseHttpsRedirection();
|
||||||
|
}
|
||||||
|
|
||||||
|
app.UseCors("DefaultCors");
|
||||||
|
app.UseAuthorization();
|
||||||
}
|
}
|
||||||
|
|
||||||
app.UseCors(CorsPolicy);
|
void ConfigureStaticFiles(WebApplication app)
|
||||||
app.UseAuthorization();
|
|
||||||
|
|
||||||
var provider = new FileExtensionContentTypeProvider();
|
|
||||||
provider.Mappings[".amr"] = "audio/amr";
|
|
||||||
|
|
||||||
app.UseStaticFiles(new StaticFileOptions
|
|
||||||
{
|
{
|
||||||
|
var provider = new FileExtensionContentTypeProvider();
|
||||||
|
provider.Mappings[".amr"] = "audio/amr";
|
||||||
|
|
||||||
|
app.UseStaticFiles(new StaticFileOptions
|
||||||
|
{
|
||||||
FileProvider = new PhysicalFileProvider(
|
FileProvider = new PhysicalFileProvider(
|
||||||
Path.Combine(builder.Environment.ContentRootPath, "My_StaticFiles")),
|
Path.Combine(app.Environment.ContentRootPath, "My_StaticFiles")),
|
||||||
RequestPath = "/My_StaticFiles",
|
RequestPath = "/My_StaticFiles",
|
||||||
ContentTypeProvider = provider
|
ContentTypeProvider = provider
|
||||||
});
|
});
|
||||||
|
}
|
||||||
app.MapControllers();
|
|
||||||
|
|
||||||
app.Run();
|
|
||||||
|
|||||||
Reference in New Issue
Block a user