123 lines
5.0 KiB
C#
123 lines
5.0 KiB
C#
using System;
|
|
using System.Threading.Tasks;
|
|
using System.Text;
|
|
using Compliance.Domain.Models;
|
|
using Compliance.DTOs;
|
|
using Compliance.Infrastructure.Repositories;
|
|
|
|
namespace Compliance.Services.ValidationRules
|
|
{
|
|
public class MeasurementSystemValidationRule(IDistributorRepository distributorRepository) : IValidationRule
|
|
{
|
|
private readonly IDistributorRepository _distributorRepository = distributorRepository;
|
|
private const string RULE_NAME = "Measurement System Validation";
|
|
|
|
// Art. 238 - Maximum calibration periods
|
|
private const int MAX_CALIBRATION_YEARS_REGULAR = 10;
|
|
private const int MAX_CALIBRATION_YEARS_ELECTRONIC = 5;
|
|
private const int MAX_CALIBRATION_YEARS_SMART = 3;
|
|
|
|
// Art. 239 - Accuracy requirements
|
|
private const decimal MAX_ERROR_PERCENTAGE = 2.5m;
|
|
private const decimal SMART_METER_ERROR_PERCENTAGE = 1.0m;
|
|
|
|
public int Priority => 1; // High priority as measurements affect other validations
|
|
|
|
public async Task<ValidationResult> ValidateAsync(
|
|
BillComplianceRequest request,
|
|
DistributorInformation distributorInfo,
|
|
Client clientInfo)
|
|
{
|
|
var measurementInfo = await _distributorRepository.GetMeasurementSystemInfoAsync(
|
|
request.MeterNumber) ?? throw new InvalidOperationException("Measurement system info not found");
|
|
|
|
var errors = new List<string>();
|
|
|
|
ValidateCalibration(measurementInfo, errors);
|
|
ValidateAccuracy(measurementInfo, errors);
|
|
ValidateSmartMeterRequirements(measurementInfo, errors);
|
|
ValidateSeals(measurementInfo, errors);
|
|
ValidateLocation(measurementInfo, errors);
|
|
|
|
return errors.Count == 0
|
|
? ValidationResult.Success(RULE_NAME)
|
|
: ValidationResult.Failure(RULE_NAME, errors);
|
|
}
|
|
|
|
private static void ValidateCalibration(MeasurementSystemInfo info, List<string> errors)
|
|
{
|
|
var maxYears = info.IsSmartMeter ? MAX_CALIBRATION_YEARS_SMART :
|
|
info.MeterType.Contains("Electronic", StringComparison.OrdinalIgnoreCase)
|
|
? MAX_CALIBRATION_YEARS_ELECTRONIC
|
|
: MAX_CALIBRATION_YEARS_REGULAR;
|
|
|
|
var calibrationAge = (DateTime.Now - info.LastCalibrationDate).TotalDays / 365.25;
|
|
|
|
if (calibrationAge > maxYears)
|
|
{
|
|
errors.Add($"Meter calibration expired. Last calibration: {info.LastCalibrationDate:d}. " +
|
|
$"Maximum period for {info.MeterType}: {maxYears} years");
|
|
}
|
|
|
|
// Validate component calibrations
|
|
foreach (var component in info.ComponentCalibrations)
|
|
{
|
|
var componentAge = (DateTime.Now - component.Value).TotalDays / 365.25;
|
|
if (componentAge > maxYears)
|
|
{
|
|
errors.Add($"Component {component.Key} calibration expired. " +
|
|
$"Last calibration: {component.Value:d}");
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void ValidateAccuracy(MeasurementSystemInfo info, List<string> errors)
|
|
{
|
|
var maxError = info.IsSmartMeter ? SMART_METER_ERROR_PERCENTAGE : MAX_ERROR_PERCENTAGE;
|
|
|
|
if (info.MaximumError > maxError)
|
|
{
|
|
errors.Add($"Meter accuracy ({info.MaximumError}%) exceeds maximum allowed error " +
|
|
$"({maxError}%) for {(info.IsSmartMeter ? "smart" : "regular")} meters");
|
|
}
|
|
}
|
|
|
|
private static void ValidateSmartMeterRequirements(MeasurementSystemInfo info, List<string> errors)
|
|
{
|
|
if (!info.IsSmartMeter) return;
|
|
|
|
var requiredProtocols = new[] { "DLMS/COSEM", "ANSI C12.19" };
|
|
foreach (var protocol in requiredProtocols)
|
|
{
|
|
if (!info.CommunicationProtocols.Contains(protocol))
|
|
{
|
|
errors.Add($"Smart meter missing required protocol: {protocol}");
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void ValidateSeals(MeasurementSystemInfo info, List<string> errors)
|
|
{
|
|
if (!info.HasSeal)
|
|
{
|
|
errors.Add("Meter missing required security seal");
|
|
}
|
|
else if (string.IsNullOrEmpty(info.SealNumber))
|
|
{
|
|
errors.Add("Security seal number not documented");
|
|
}
|
|
}
|
|
|
|
private static void ValidateLocation(MeasurementSystemInfo info, List<string> errors)
|
|
{
|
|
if (string.IsNullOrEmpty(info.Location))
|
|
{
|
|
errors.Add("Meter location not documented");
|
|
}
|
|
else if (!info.Location.Contains("accessible", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
errors.Add("Meter must be installed in an accessible location");
|
|
}
|
|
}
|
|
}
|
|
} |