Complete IoT monitoring platform for Acuvim II power meters via ESP32. Firmware (Phases 1-7): - ESP32-WROVER-B (TTGO T-Call v1.4) with RS485 Modbus RTU - WiFi STA+AP concurrent mode with GSM/GPRS failover - Transport abstraction layer with 4 priority modes - MQTT protocol with 20 commands, LWT, QoS, exponential backoff - SD card offline buffering with JSONL rotation and non-blocking drain - OTA firmware updates with dual partition rollback protection - Watchdog timer, crash loop detection, Acuvim health monitoring - Captive portal provisioning with AP mode Console backend (Phase 8): - .NET 10 minimal API with PostgreSQL + EF Core - JWT authentication, SignalR real-time updates - MQTTnet 5.x bridge service with health monitoring - Device, telemetry, firmware, alert, group management - Rate limiting, security headers, Swagger/OpenAPI Frontend (Phase 9): - React 18 + TypeScript + Vite with Ant Design 5 - ECharts telemetry visualization, TanStack Query - SignalR live updates, device management UI - Dashboard, fleet management, firmware deployment Testing & Production (Phase 10): - 28 firmware unit tests (Modbus, JSON, config, version) - 23 xUnit backend tests (device, telemetry, command, alert) - Docker Compose with nginx, TLS MQTT, PostgreSQL - Production deployment, commissioning, and troubleshooting docs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
87 lines
3.2 KiB
C#
87 lines
3.2 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using Tau.Acuvim.Console.Models;
|
|
|
|
namespace Tau.Acuvim.Console.Data;
|
|
|
|
public class AppDbContext : DbContext
|
|
{
|
|
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
|
|
|
|
public DbSet<Device> Devices => Set<Device>();
|
|
public DbSet<TelemetryRecord> TelemetryRecords => Set<TelemetryRecord>();
|
|
public DbSet<Alert> Alerts => Set<Alert>();
|
|
public DbSet<Command> Commands => Set<Command>();
|
|
public DbSet<FirmwareVersion> FirmwareVersions => Set<FirmwareVersion>();
|
|
public DbSet<DeviceGroup> DeviceGroups => Set<DeviceGroup>();
|
|
public DbSet<User> Users => Set<User>();
|
|
|
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
|
{
|
|
modelBuilder.Entity<Device>(e =>
|
|
{
|
|
e.ToTable("devices");
|
|
e.HasKey(d => d.Id);
|
|
e.HasIndex(d => d.DeviceId).IsUnique();
|
|
e.HasIndex(d => d.Status);
|
|
e.Property(d => d.Capabilities).HasColumnType("jsonb");
|
|
e.Property(d => d.Tags).HasColumnType("jsonb");
|
|
e.HasOne(d => d.Group).WithMany(g => g.Devices).HasForeignKey(d => d.GroupId);
|
|
});
|
|
|
|
modelBuilder.Entity<TelemetryRecord>(e =>
|
|
{
|
|
e.ToTable("telemetry_records");
|
|
e.HasKey(t => t.Id);
|
|
e.HasIndex(t => new { t.DeviceId, t.Timestamp }).IsDescending(false, true);
|
|
e.HasIndex(t => t.Timestamp).IsDescending();
|
|
e.Property(t => t.Data).HasColumnType("jsonb");
|
|
e.HasOne(t => t.Device).WithMany(d => d.TelemetryRecords)
|
|
.HasForeignKey(t => t.DeviceId).HasPrincipalKey(d => d.DeviceId);
|
|
});
|
|
|
|
modelBuilder.Entity<Alert>(e =>
|
|
{
|
|
e.ToTable("alerts");
|
|
e.HasKey(a => a.Id);
|
|
e.HasIndex(a => new { a.DeviceId, a.CreatedAt }).IsDescending(false, true);
|
|
e.Property(a => a.Metadata).HasColumnType("jsonb");
|
|
e.HasOne(a => a.Device).WithMany(d => d.Alerts)
|
|
.HasForeignKey(a => a.DeviceId).HasPrincipalKey(d => d.DeviceId);
|
|
});
|
|
|
|
modelBuilder.Entity<Command>(e =>
|
|
{
|
|
e.ToTable("commands");
|
|
e.HasKey(c => c.Id);
|
|
e.HasIndex(c => c.RequestId).IsUnique();
|
|
e.HasIndex(c => new { c.DeviceId, c.CreatedAt }).IsDescending(false, true);
|
|
e.Property(c => c.Params).HasColumnType("jsonb");
|
|
e.Property(c => c.Response).HasColumnType("jsonb");
|
|
e.HasOne(c => c.Device).WithMany(d => d.Commands)
|
|
.HasForeignKey(c => c.DeviceId).HasPrincipalKey(d => d.DeviceId);
|
|
});
|
|
|
|
modelBuilder.Entity<FirmwareVersion>(e =>
|
|
{
|
|
e.ToTable("firmware_versions");
|
|
e.HasKey(f => f.Id);
|
|
e.HasIndex(f => f.Version).IsUnique();
|
|
});
|
|
|
|
modelBuilder.Entity<DeviceGroup>(e =>
|
|
{
|
|
e.ToTable("device_groups");
|
|
e.HasKey(g => g.Id);
|
|
e.HasIndex(g => g.Name).IsUnique();
|
|
});
|
|
|
|
modelBuilder.Entity<User>(e =>
|
|
{
|
|
e.ToTable("users");
|
|
e.HasKey(u => u.Id);
|
|
e.HasIndex(u => u.Username).IsUnique();
|
|
e.HasIndex(u => u.Email).IsUnique();
|
|
});
|
|
}
|
|
}
|