EN2 New Controllers Implementation Plan
Overview
This document outlines the implementation plan for creating controllers for 14 new EN2Models in the dwd-api-aspnet project. These models represent newly implemented EPANET input file sections that provide comprehensive coverage of EPANET functionality.
New Models to Implement
1. Pattern - Time-varying data
- Model:
EN2Models.Pattern - EPANET Section:
[PATTERNS] - Type: Single entity with array data
- Key Properties:
Id,Multipliers[],Description - Controller:
PatternController - CRUD Operations: Full CRUD
- Special Features: Array manipulation methods for multipliers
2. DataCurve - Pump and tank curves
- Model:
EN2Models.DataCurve - EPANET Section:
[CURVES] - Type: Single entity with coordinate arrays
- Key Properties:
Id,CurveType,XValues[],YValues[],Description - Controller:
DataCurveController - CRUD Operations: Full CRUD
- Special Features: Point manipulation, min/max calculations
3. TimeParameters - Simulation timing
- Model:
EN2Models.TimeParameters - EPANET Section:
[TIMES] - Type: Network defaults with optional scenario overrides
- Key Properties:
ScenarioId?(nullable),Duration,HydraulicTimestep,QualityTimestep, etc. - Controller:
TimeParametersController - CRUD Operations: Full CRUD + network defaults endpoints
- Special Features: Network-wide defaults (ScenarioId = null), scenario-specific overrides
4. Options - Analysis configuration
- Model:
EN2Models.Options - EPANET Section:
[OPTIONS] - Type: Network defaults with optional scenario overrides
- Key Properties:
ScenarioId?(nullable), various analysis options - Controller:
OptionsController - CRUD Operations: Full CRUD + network defaults endpoints
- Special Features: Network-wide defaults (ScenarioId = null), scenario-specific overrides
5. Demand - Multiple demand categories
- Model:
EN2Models.Demand - EPANET Section:
[DEMANDS] - Type: Multiple entities (list per junction)
- Key Properties:
JunctionId,DemandValue,PatternId,Category - Controller:
DemandController - CRUD Operations: Full CRUD + LIST endpoint
- Special Features: Junction-based, multiple demands per junction
6. Source - Water quality sources
- Model:
EN2Models.Source - EPANET Section:
[SOURCES] - Type: Multiple entities (list per node)
- Key Properties:
NodeId,SourceType,Strength,PatternId - Controller:
SourceController - CRUD Operations: Full CRUD + LIST endpoint
- Special Features: Node-based, multiple sources per node
7. Reaction - Chemical reactions
- Model:
EN2Models.Reaction - EPANET Section:
[REACTIONS] - Type: Multiple entities (list of reactions)
- Key Properties:
ReactionType,Coefficient,ObjectId,Order - Controller:
ReactionController - CRUD Operations: Full CRUD + LIST endpoint
- Special Features: Global and object-specific reactions
8. TankMixing - Tank mixing models
- Model:
EN2Models.TankMixing - EPANET Section:
[MIXING] - Type: Single entity per tank
- Key Properties:
TankId,MixingModel,CompartmentVolume - Controller:
TankMixingController - CRUD Operations: Full CRUD
- Special Features: Tank-based (one per tank)
9. Energy - Energy analysis
- Model:
EN2Models.Energy - EPANET Section:
[ENERGY] - Type: Network defaults with optional scenario overrides
- Key Properties:
ScenarioId?(nullable),GlobalPrice,GlobalPattern,PumpId, etc. - Controller:
EnergyController - CRUD Operations: Full CRUD + network defaults endpoints
- Special Features: Network-wide defaults (ScenarioId = null), scenario-specific overrides with pump-specific overrides
10. Emitter - Pressure-dependent demands
- Model:
EN2Models.Emitter - EPANET Section:
[EMITTERS] - Type: Multiple entities (list per junction)
- Key Properties:
JunctionId,Coefficient - Controller:
EmitterController - CRUD Operations: Full CRUD + LIST endpoint
- Special Features: Junction-based, multiple emitters per junction
11. Leakage - Pipe leakage modeling
- Model:
EN2Models.Leakage - EPANET Section:
[LEAKAGE] - Type: Multiple entities (list per pipe)
- Key Properties:
PipeId,LeakageCoefficient,Exponent - Controller:
LeakageController - CRUD Operations: Full CRUD + LIST endpoint
- Special Features: Pipe-based, multiple leakage models per pipe
12. Quality - Initial water quality
- Model:
EN2Models.Quality - EPANET Section:
[QUALITY] - Type: Network defaults with optional scenario overrides
- Key Properties:
ScenarioId?(nullable),GlobalQuality,NodeId,NodeQuality - Controller:
QualityController - CRUD Operations: Full CRUD + network defaults endpoints
- Special Features: Network-wide defaults (ScenarioId = null), scenario-specific overrides with node-specific overrides
13. Status - Link status configuration
- Model:
EN2Models.Status - EPANET Section:
[STATUS] - Type: Multiple entities (list per link)
- Key Properties:
LinkId,Status,Setting - Controller:
StatusController - CRUD Operations: Full CRUD + LIST endpoint
- Special Features: Link-based, multiple status entries per link
14. Report - Output reporting options
- Model:
EN2Models.Report - EPANET Section:
[REPORT] - Type: Network defaults with optional scenario overrides
- Key Properties:
ScenarioId?(nullable),Status,Summary,Energy, etc. - Controller:
ReportController - CRUD Operations: Full CRUD + network defaults endpoints
- Special Features: Network-wide defaults (ScenarioId = null), scenario-specific overrides
Network Defaults Design
Scenario-Based Models with Network Defaults
The following models use a network defaults with scenario overrides approach:
- TimeParameters - Simulation timing settings
- Options - Analysis configuration options
- Energy - Energy analysis parameters
- Report - Output reporting options
Design Pattern
// Network-wide defaults (ScenarioId = null)
public class TimeParameters
{
public int? ScenarioId { get; set; } // null = network default
public string Duration { get; set; }
// ... other properties
}
Usage Pattern
- Network Defaults: One record per model with
ScenarioId = null - Scenario Overrides: Optional records with specific
ScenarioIdvalues - Effective Settings: Check for scenario-specific first, fall back to network defaults
API Endpoints
// Get network defaults
GET /api/timeparameters/defaults
// Get effective settings for scenario (with fallback)
GET /api/timeparameters/scenario/{scenarioId}
// Create scenario-specific override
POST /api/timeparameters/scenario/{scenarioId}
// Update scenario-specific override
PUT /api/timeparameters/scenario/{scenarioId}
Benefits
- Better UX: New scenarios inherit sensible defaults
- Industry Standard: Matches EPANET desktop behavior
- Flexibility: Power users can customize per scenario
- Reduced Complexity: Less required configuration upfront
- Consistency: All scenarios have valid settings
Implementation Strategy
Phase 1: Database Context Updates
- Add DbSet properties for all 14 new models to
EN2PostgresContext.cs - Update scenario-based models: Change
ScenarioIdfrom[Required]to nullable (ScenarioId?) - Configure entity relationships and constraints in
OnModelCreating - Create database migration if needed
- Create network default records: Insert default records with
ScenarioId = null
Phase 2: DTO Creation
- Create response DTOs for each model following existing patterns
- Include display values for enums where applicable
- Add JSON property names for consistency
Phase 3: Controller Implementation
- Implement controllers following existing patterns from
TankController,PumpController, etc. - Use consistent naming:
{ModelName}Controller - Place all controllers in
Controllers/EN2/directory - Implement full CRUD operations:
GET- Retrieve with optional ID filterPOST- Create new entityPUT- Update existing entityDELETE- Delete entity
- Special endpoints for scenario-based models:
GET /defaults- Get network-wide defaults (ScenarioId = null)GET /scenario/{scenarioId}- Get effective settings for scenario (with fallback to defaults)POST /scenario/{scenarioId}- Create scenario-specific override
Phase 4: Special Endpoints
- Implement LIST endpoints for models that represent multiple entries:
DemandController-/api/demand/list/{junctionId}SourceController-/api/source/list/{nodeId}ReactionController-/api/reaction/listEmitterController-/api/emitter/list/{junctionId}LeakageController-/api/leakage/list/{pipeId}StatusController-/api/status/list/{linkId}
Controller Structure Template
Network-Based Models (Pattern, DataCurve, Demand, Source, etc.)
[ApiController]
[Route("api/[controller]")]
public class {ModelName}Controller : ControllerBase
{
private readonly EN2PostgresContext.EN2PostgresContext _context;
private readonly EnumMappingService _enumMappingService;
public {ModelName}Controller(EN2PostgresContext.EN2PostgresContext context, EnumMappingService enumMappingService)
{
_context = context;
_enumMappingService = enumMappingService;
}
[HttpGet]
public async Task<ActionResult<IEnumerable<{ModelName}ResponseDto>>> Get([FromQuery] string? id)
{
// Implementation - no scenario filtering
}
[HttpPost]
public async Task<ActionResult<{ModelName}ResponseDto>> Post([FromBody] {ModelName}RequestDto request)
{
// Implementation - no ScenarioId needed
}
[HttpPut("{id}")]
public async Task<IActionResult> Put(string id, [FromBody] {ModelName}RequestDto request)
{
// Implementation
}
[HttpDelete("{id}")]
public async Task<IActionResult> Delete(string id)
{
// Implementation
}
// LIST endpoint for models with multiple entries
[HttpGet("list/{parentId}")]
public async Task<ActionResult<IEnumerable<{ModelName}ResponseDto>>> GetList(string parentId)
{
// Implementation
}
}
Scenario-Based Models (TimeParameters, Options, Energy, Report)
[ApiController]
[Route("api/[controller]")]
public class {ModelName}Controller : ControllerBase
{
private readonly EN2PostgresContext.EN2PostgresContext _context;
private readonly EnumMappingService _enumMappingService;
public {ModelName}Controller(EN2PostgresContext.EN2PostgresContext context, EnumMappingService enumMappingService)
{
_context = context;
_enumMappingService = enumMappingService;
}
// Get network defaults
[HttpGet("defaults")]
public async Task<ActionResult<{ModelName}ResponseDto>> GetDefaults()
{
var defaults = await _context.{ModelName}s
.Where(x => x.ScenarioId == null)
.FirstOrDefaultAsync();
// Implementation
}
// Get effective settings for scenario (with fallback to defaults)
[HttpGet("scenario/{scenarioId}")]
public async Task<ActionResult<{ModelName}ResponseDto>> GetForScenario(int scenarioId)
{
// Try scenario-specific first
var scenario = await _context.{ModelName}s
.Where(x => x.ScenarioId == scenarioId)
.FirstOrDefaultAsync();
if (scenario != null) return Ok(scenario);
// Fall back to network defaults
var defaults = await _context.{ModelName}s
.Where(x => x.ScenarioId == null)
.FirstOrDefaultAsync();
return Ok(defaults);
}
// Create scenario-specific override
[HttpPost("scenario/{scenarioId}")]
public async Task<ActionResult<{ModelName}ResponseDto>> CreateScenarioOverride(int scenarioId, [FromBody] {ModelName}RequestDto request)
{
// Implementation - set ScenarioId = scenarioId
}
// Update scenario-specific override
[HttpPut("scenario/{scenarioId}")]
public async Task<IActionResult> UpdateScenarioOverride(int scenarioId, [FromBody] {ModelName}RequestDto request)
{
// Implementation
}
// Delete scenario-specific override
[HttpDelete("scenario/{scenarioId}")]
public async Task<IActionResult> DeleteScenarioOverride(int scenarioId)
{
// Implementation - delete scenario-specific record, fall back to defaults
}
}
DTO Structure Template
public class {ModelName}ResponseDto
{
public string Id { get; set; } = string.Empty;
// Model-specific properties
public string? DisplayValue { get; set; } // For enums
public DateTime CreatedAt { get; set; }
public DateTime? ModifiedAt { get; set; }
}
public class {ModelName}RequestDto
{
public string Id { get; set; } = string.Empty;
// Model-specific properties for creation/update
}
Key Considerations
1. Model Types Classification
- Network-Based Models: Pattern, DataCurve, Demand, Source, Reaction, TankMixing, Emitter, Leakage, Quality, Status
- Scenario-Based Models with Network Defaults: TimeParameters, Options, Energy, Report
- List Models: Demand, Source, Reaction, Emitter, Leakage, Status
2. Relationship Handling
- Scenario-based models: Use nullable ScenarioId for network defaults with scenario overrides
- Network-based models: No ScenarioId needed, tied to network elements (JunctionId, NodeId, etc.)
- Navigation properties should be included in queries where beneficial
3. Array Data Handling
- Pattern.Multipliers and DataCurve.XValues/YValues are stored as PostgreSQL arrays
- Consider special endpoints for array manipulation if needed
4. Enum Display Values
- Use EnumMappingService for consistent enum display values
- Follow existing patterns from current controllers
5. Error Handling
- Implement consistent error handling across all controllers
- Return appropriate HTTP status codes
- Include meaningful error messages
6. Validation
- Add model validation attributes where appropriate
- Implement business logic validation in controllers
Testing Strategy
- Unit Tests: Test individual controller methods
- Integration Tests: Test full CRUD operations
- API Tests: Test endpoints with various scenarios
- Data Validation: Test with valid and invalid data
Documentation
- Update API documentation for new endpoints
- Add Swagger/OpenAPI annotations
- Include example requests and responses
- Document special features and limitations
Timeline
- Phase 1: Database Context Updates (1 day)
- Phase 2: DTO Creation (1 day)
- Phase 3: Controller Implementation (3-4 days)
- Phase 4: Special Endpoints (1 day)
- Testing & Documentation: (1-2 days)
Total Estimated Time: 7-9 days
Success Criteria
- All 14 controllers implemented with full CRUD operations
- LIST endpoints implemented for appropriate models
- Consistent API design following existing patterns
- Proper error handling and validation
- Complete test coverage
- Updated documentation
- All endpoints working correctly with the database