// Comparision of the CertIds private static void CompareCertIds(UsagePointAdapterTRuDI usagePoint, List <Exception> exceptions) { var smgwCertIds = usagePoint.Smgw.CertIds; List <byte> certificateCertIds = new List <byte>(); foreach (Certificate cert in usagePoint.Certificates) { certificateCertIds.Add((byte)cert.CertId); } if (smgwCertIds.Count == certificateCertIds.Count) { bool match = false; foreach (byte sId in smgwCertIds) { foreach (byte cId in certificateCertIds) { if (sId == cId) { match = true; break; } } if (!match) { exceptions.Add(new InvalidOperationException($"Das Smart Meter Gateway-Zertifikat mit der ID {sId} konnte nicht gefunden werden.")); } } } else { exceptions.Add(new InvalidOperationException("Die Anzahl der \"CertIds\" im Element \"SMGW\" entspricht nicht der Nummer der Zertifikate.")); } }
// Check if the delivered supplier xml has an completely enrolled calendar private static void ValidateSupplierModelCompletelyEnrolledCalendar(UsagePointAdapterTRuDI model, UsagePointLieferant supplier, List <Exception> exceptions) { var period = supplier.AnalysisProfile.BillingPeriod; var profiles = supplier.AnalysisProfile.TariffChangeTrigger.TimeTrigger.SpecialDayProfiles; var sdpCheckList = new Dictionary <DateTime, int>(); var timestamp = period.Start; var hasCounted = false; while (timestamp <= period.GetEnd().AddDays(-1)) { sdpCheckList.Add(timestamp, 0); timestamp = timestamp.AddDays(1); } foreach (var profile in profiles) { if (sdpCheckList.ContainsKey(profile.SpecialDayDate.GetDate())) { sdpCheckList[profile.SpecialDayDate.GetDate()] += 1; hasCounted = true; } } foreach (var item in sdpCheckList) { if (item.Value == 0 && hasCounted) { exceptions.Add(new InvalidOperationException($"Tagesprofil für Tag {item.Key:dd.MM.yyy} nicht vorhanden.")); } } }
// Validation of additional requirements public static void ValidateContext(UsagePointAdapterTRuDI usagePoint, AdapterContext ctx) { var exceptions = new List <Exception>(); if (ctx != null) { ValidateMeterReadingCount(usagePoint, ctx, exceptions); ValidateMaximumTimeSpan(ctx, exceptions); if (ctx.Contract.TafId == TafId.Taf2) { ValidateTaf2RegisterDurations(usagePoint, exceptions); } if (ctx.Contract.TafId == TafId.Taf7) { ValidateTaf7MeterReadingsAreOriginalValueLists(usagePoint, exceptions); ValidateTaf7PeriodsInInterval(usagePoint, exceptions); } } CompareCertIds(usagePoint, exceptions); ValidateCertificateRawData(usagePoint, exceptions); ValidateIntervalBlockConsistence(usagePoint, exceptions); if (exceptions.Any()) { throw new AggregateException("Context error:>", exceptions); } }
// Validate of all the neccessary ids of the model xml match with the ids in the supplier xml private static void ValidateTaf7ModelSupplierCompatibility(UsagePointAdapterTRuDI model, UsagePointLieferant supplier, List <Exception> exceptions) { if (model.UsagePointId != supplier.UsagePointId) { exceptions.Add(new InvalidOperationException($"TAF-7: Die ID der Messlokation \"{model.UsagePointId}\" stimmt nicht mit der ID der Messlokation \"{supplier.UsagePointId}\" aus der Tarifdatei des Lieferanten überein.")); } if (model.InvoicingParty.InvoicingPartyId != supplier.InvoicingParty.InvoicingPartyId) { exceptions.Add(new InvalidOperationException($"TAF-7: Die ID des Rechnungsstellers \"{model.InvoicingParty.InvoicingPartyId}\" stimmt nicht mit der ID des Rechnungssteller \"{supplier.InvoicingParty.InvoicingPartyId}\" aus der Tarifdatei des Lieferanten überein.")); } if (model.ServiceCategory.Kind != supplier.ServiceCategory.Kind) { exceptions.Add(new InvalidOperationException($"TAF-7: Die Service-Kategory \"{model.ServiceCategory.Kind}\" stimmt nicht mit der Service-Kategory \"{supplier.ServiceCategory.Kind}\" aus der Tarifdatei des Lieferanten überein.")); } if (string.Compare(model.Smgw.SmgwId, supplier.Smgw.SmgwId, StringComparison.OrdinalIgnoreCase) != 0) { exceptions.Add(new InvalidOperationException($"TAF-7: Die ID des Smart Meter Gateway \"{model.Smgw.SmgwId}\" stimmt nicht mit der ID \"{supplier.Smgw.SmgwId}\" aus der Tarifdatei des Lieferanten überein.")); } if (model.TariffName != supplier.TariffName) { exceptions.Add(new InvalidOperationException($"TAF-7: Der Tarifname \"{model.TariffName}\" stimmt nicht mit dem Tariffnamen \"{supplier.TariffName}\" aus der Tarifdatei des Lieferanten überein.")); } }
// Taf-7: Validate if all meter readings are original value lists private static void ValidateTaf7MeterReadingsAreOriginalValueLists(UsagePointAdapterTRuDI model, List <Exception> exceptions) { foreach (MeterReading reading in model.MeterReadings) { if (!reading.IsOriginalValueList()) { exceptions.Add(new InvalidOperationException("TAF-7: The MeterReading is not an Original Value List.")); } } }
// Check of the meterReadingIds are unique private static void ValidateMeterReadingIds(UsagePointAdapterTRuDI usagePoint, List <Exception> exceptions) { for (int i = 0; i < usagePoint.MeterReadings.Count; i++) { var reading = usagePoint.MeterReadings[i]; for (int j = i + 1; j < usagePoint.MeterReadings.Count; j++) { if (reading.MeterReadingId == usagePoint.MeterReadings[j].MeterReadingId) { exceptions.Add(new InvalidOperationException("Das Element \"MeterReadingId\" enthält keine eindeutigen Werte.")); } } } }
// Check if the raw data of the certificates are valid certificates private static void ValidateCertificateRawData(UsagePointAdapterTRuDI usagePoint, List <Exception> exceptions) { foreach (Certificate certificate in usagePoint.Certificates) { try { var cert = certificate.GetCert(); } catch (Exception e) { exceptions.Add(new InvalidOperationException($"Zertifikat konnte nicht gelesen werden: ID={certificate.CertId}, Typ={certificate.CertType}", e)); } } }
// Check if the count of MeterReadings are Valid due to the TAF private static void ValidateMeterReadingCount(UsagePointAdapterTRuDI usagePoint, AdapterContext ctx, List <Exception> exceptions) { var meterReadingCount = usagePoint.MeterReadings.Count; if (ctx.BillingPeriod != null && ctx.BillingPeriod.End.HasValue) { if (ctx.Contract.TafId == TafId.Taf1 && meterReadingCount < 2) { exceptions.Add(new InvalidOperationException("TAF-1 benötigt mindestens 2 Elemente von Typ \"MeterReading\".")); } if ((ctx.Contract.TafId == TafId.Taf2) && meterReadingCount < 5) { exceptions.Add(new InvalidOperationException("TAF-2 benötigt mindestens 5 Elemente vom Typ \"MeterReading\".")); } } }
// Validation of additional requirements with additional supplier xml (only Taf-7) public static void ValidateContext(UsagePointAdapterTRuDI usagePoint, UsagePointLieferant supplierModel, AdapterContext ctx) { ValidateContext(usagePoint, ctx); var exceptions = new List <Exception>(); ValidateTaf7ModelSupplierCompatibility(usagePoint, supplierModel, exceptions); ValidateSupplierModelTariffStageCount(supplierModel, exceptions); ValidateSpecialDayProfilesWithinBillingPeriod(supplierModel, exceptions); ValidateSupplierModelCompletelyEnrolledCalendar(usagePoint, supplierModel, exceptions); ValidateTarifStageOccurence(supplierModel, exceptions); ValidateSupplierModelDayProfileOccurence(supplierModel, exceptions); if (exceptions.Any()) { throw new AggregateException("Taf-7 Context error:>", exceptions); } }
// The public method for the validation of the han adapter model public static UsagePointAdapterTRuDI ValidateHanAdapterModel(UsagePointAdapterTRuDI usagePoint) { var exceptions = new List <Exception>(); CommonModelValidation(usagePoint, exceptions); if (usagePoint.Customer == null) { exceptions.Add(new InvalidOperationException("Das Element \"UsagePoint\" muss das Element \"Customer\" enthalten.")); } if (usagePoint.Certificates.Count < 1) { exceptions.Add(new InvalidOperationException("Das Element \"UsagePoint\" muss das Element \"Certificate\" enthalten.")); } else { foreach (var cert in usagePoint.Certificates) { ValidateCertificate(cert, exceptions); } } foreach (var meterReading in usagePoint.MeterReadings) { ValidateMeterReading(meterReading, exceptions); } if (usagePoint.LogEntries.Count >= 1) { foreach (var logEntry in usagePoint.LogEntries) { ValidateLogEntry(logEntry, exceptions); } } if (exceptions.Any()) { throw new AggregateException("Han Adapter Model error:>", exceptions); } return(usagePoint); }
// Taf-2: Validate if the durations in the different MeterReadings match private static void ValidateTaf2RegisterDurations(UsagePointAdapterTRuDI usagePoint, List <Exception> exceptions) { var interval = usagePoint.MeterReadings.FirstOrDefault(mr => !mr.IsOriginalValueList())?.GetMeterReadingInterval(); if (interval == null) { return; } foreach (MeterReading reading in usagePoint.MeterReadings) { if (!reading.IsOriginalValueList()) { var intervalTest = reading.GetMeterReadingInterval(); if (interval.Duration != intervalTest.Duration) { exceptions.Add(new InvalidOperationException("TAF-2: Die Dauer der einzelnen Ablesungen stimmen nicht überein.")); } } } }
// Check if there is a gap between multiple IntervalBlocks or if the overlap private static void ValidateIntervalBlockConsistence(UsagePointAdapterTRuDI usagePoint, List <Exception> exceptions) { foreach (MeterReading reading in usagePoint.MeterReadings) { var intervalBlocks = reading.IntervalBlocks.OrderBy(ib => ib.Interval.Start).ToList(); for (int i = 0; i < intervalBlocks.Count; i++) { if (i < intervalBlocks.Count - 1) { if (intervalBlocks[i].Interval.GetEnd() < intervalBlocks[i + 1].Interval.Start) { exceptions.Add(new InvalidOperationException("Lücke zwischen zwei Intervallblöcken.")); } else if (intervalBlocks[i].Interval.GetEnd() > intervalBlocks[i + 1].Interval.Start) { exceptions.Add(new InvalidOperationException("Es wurden zwei überlappende Intervallblöcke gefunden.")); } } } } }
// Taf-7: Validate if all IntervalReadings are within the interval of the IntervalBlock private static void ValidateTaf7PeriodsInInterval(UsagePointAdapterTRuDI model, List <Exception> exceptions) { var originalValueLists = model.MeterReadings.Where(mr => mr.IsOriginalValueList()); foreach (var reading in originalValueLists) { foreach (var ib in reading.IntervalBlocks) { var intervalBlockEnd = ib.Interval.GetEnd(); foreach (var ir in ib.IntervalReadings) { if (ir.CaptureTime.ToUniversalTime() < ib.Interval.Start.ToUniversalTime()) { exceptions.Add(new InvalidOperationException($"TAF-7: IntervalReading befindet sich nicht innerhalb des Zeitbereichs eines IntervalBlocks: Start des IntervalBlocks: {ib.Interval.Start}, Zeitpunkt des IntervalReading: {ir.TimePeriod.Start}, Kennziffer: {reading.ReadingType.ObisCode}")); } else if (ir.CaptureTime.ToUniversalTime() > intervalBlockEnd.ToUniversalTime()) { exceptions.Add(new InvalidOperationException($"TAF-7: IntervalReading befindet sich nicht innerhalb des Zeitbereichs eines IntervalBlocks: Ende des IntervalBlocks: {intervalBlockEnd}, Zeitpunkt des IntervalReading: {ir.TimePeriod.GetEnd()}, Kennziffer: {reading.ReadingType.ObisCode}")); } } } } }
/// <inheritdoc /> /// <summary> /// Calculates the derived register for Taf1. /// </summary> /// <param name="device">Date from the SMGW. There should be just original value lists.</param> /// <param name="supplier">The calculation data from the supplier.</param> /// <returns>An ITaf2Data instance. The object contains the calculated data.</returns> public TafAdapterData Calculate(UsagePointAdapterTRuDI device, UsagePointLieferant supplier) { this.originalValueLists = device.MeterReadings.Where(mr => mr.IsOriginalValueList()).Select(mr => new OriginalValueList(mr, device.ServiceCategory.Kind ?? Kind.Electricity)).ToList(); if (!this.originalValueLists.Any()) { throw new InvalidOperationException("Es ist keine originäre Messwertliste verfügbar."); } this.ValidateOriginalValueLists(originalValueLists, supplier, device.MeterReadings.Count); var registers = supplier.GetRegister(); this.UpdateReadingTypeFromOriginalValueList(registers); var accountingPeriod = new Taf1Data(registers, supplier.AnalysisProfile.TariffStages); accountingPeriod.SetDate(supplier.AnalysisProfile.BillingPeriod.Start, supplier.AnalysisProfile.BillingPeriod.GetEnd()); this.SetTotalBillingPeriod(accountingPeriod); ValidateBillingPeriod(); var dayProfiles = supplier.AnalysisProfile.TariffChangeTrigger.TimeTrigger.DayProfiles; foreach (OriginalValueList ovl in originalValueLists) { var startReading = ovl.MeterReading.GetIntervalReadingFromDate(billingPeriodStart); var endReading = ovl.MeterReading.GetIntervalReadingFromDate(billingPeriodEnd); if (startReading == null || !IsStatusValid(startReading)) { throw new InvalidOperationException($"Zu dem Zeitpunkt {billingPeriodStart} ist kein Wert vorhanden oder der Status kritisch oder fatal."); } if (endReading == null || !IsStatusValid(endReading)) { throw new InvalidOperationException($"Zu dem Zeitpunkt {billingPeriodEnd} ist kein Wert vorhanden oder der Status kritisch oder fatal."); } var dayProfile = this.GetDayProfileNumber(dayProfiles, new ObisId(ovl.MeterReading.ReadingType.ObisCode), supplier.AnalysisProfile); var tariffStages = supplier.AnalysisProfile.TariffStages; var tariffId = this.GetTariffId(tariffStages, dayProfiles, dayProfile); CheckValidSupplierFile(supplier, dayProfile, tariffId); var result = this.GetSection(supplier, ovl.MeterReading, startReading, endReading, tariffId); accountingPeriod.Add(result); accountingPeriod.AddInitialReading(new Reading() { Amount = accountingPeriod.AccountingSections.First(s => s.Reading.ObisCode == ovl.MeterReading.ReadingType.ObisCode).Reading.Amount, ObisCode = new ObisId(ovl.MeterReading.ReadingType.ObisCode) }); } return(new TafAdapterData(typeof(Taf2SummaryView), typeof(Taf2DetailView), accountingPeriod)); }
public static UsagePointAdapterTRuDI ParseHanAdapterModel(IEnumerable <XElement> elements) { UsagePointAdapterTRuDI usagePoint = null; var exceptions = new List <Exception>(); var logEventAlreadyExists = false; var dayIdAlreadyExists = false; string generatorVersion = null; foreach (XElement e in elements) { switch (e.Name.LocalName) { case "UsagePoint": usagePoint = new UsagePointAdapterTRuDI() { GeneratorVersion = generatorVersion }; break; case "GeneratorVersion": generatorVersion = e.Value; break; case "usagePointId": usagePoint.UsagePointId = e.Value; break; case "tariffName": usagePoint.TariffName = e.Value; break; case "Customer": usagePoint.Customer = new Customer(); break; case "customerId": usagePoint.Customer.CustomerId = e.Value; break; case "InvoicingParty": usagePoint.InvoicingParty = new InvoicingParty(); break; case "invoicingPartyId": usagePoint.InvoicingParty.InvoicingPartyId = e.Value; break; case "ServiceCategory": usagePoint.ServiceCategory = new ServiceCategory(); break; case "kind": usagePoint.ServiceCategory.Kind = (Kind)Convert.ToInt32(e.Value); break; case "SMGW": usagePoint.Smgw = new SMGW(); break; case "certId": if (e.Parent.Name.LocalName == "SMGW") { usagePoint.Smgw.CertIds.Add(Convert.ToByte(e.Value)); } else if (e.Parent.Name.LocalName == "Certificate") { usagePoint.Certificates.LastOrDefault().CertId = Convert.ToByte(e.Value); } break; case "smgwId": usagePoint.Smgw.SmgwId = e.Value; break; case "Certificate": usagePoint.Certificates.Add(new Certificate()); break; case "certType": usagePoint.Certificates.LastOrDefault().CertType = (CertType)Convert.ToByte(e.Value); break; case "parentCertId": usagePoint.Certificates.LastOrDefault().ParentCertId = Convert.ToByte(e.Value); break; case "certContent": if (e.Value.ValidateHexString()) { usagePoint.Certificates.LastOrDefault().HexStringToByteArray(e.Value); } else { exceptions.Add(new InvalidOperationException("Das Element \"certContent\" enthält keinen gültigen Wert.")); } break; case "LogEntry": usagePoint.LogEntries.Add(new LogEntry()); logEventAlreadyExists = false; break; case "recordNumber": usagePoint.LogEntries.LastOrDefault().RecordNumber = Convert.ToUInt32(e.Value); break; case "LogEvent": if (logEventAlreadyExists) { exceptions.Add(new InvalidOperationException("The current LogEntry has already a LogEvent instance")); } else { usagePoint.LogEntries.LastOrDefault().LogEvent = new LogEvent(); logEventAlreadyExists = true; } break; case "level": usagePoint.LogEntries.LastOrDefault().LogEvent.Level = (Level)Convert.ToByte(e.Value); break; case "text": usagePoint.LogEntries.LastOrDefault().LogEvent.Text = e.Value; break; case "outcome": usagePoint.LogEntries.LastOrDefault().LogEvent.Outcome = (Outcome)Convert.ToByte(e.Value); break; case "timestamp": usagePoint.LogEntries.LastOrDefault().LogEvent.Timestamp = Convert.ToDateTime(e.Value); break; case "MeterReading": usagePoint.MeterReadings.Add(new MeterReading()); usagePoint.MeterReadings.LastOrDefault().UsagePoint = usagePoint; break; case "Meter": usagePoint.MeterReadings.LastOrDefault().Meters.Add(new Meter()); break; case "meterId": usagePoint.MeterReadings.LastOrDefault().Meters.Last().MeterId = e.Value; break; case "meterReadingId": usagePoint.MeterReadings.LastOrDefault().MeterReadingId = e.Value; break; case "ReadingType": usagePoint.MeterReadings.LastOrDefault().ReadingType = new ReadingType { MeterReading = usagePoint.MeterReadings.LastOrDefault(), }; break; case "powerOfTenMultiplier": if (e.Parent.Name.LocalName == "ReadingType") { usagePoint.MeterReadings.LastOrDefault() .ReadingType.PowerOfTenMultiplier = (PowerOfTenMultiplier)Convert.ToInt16(e.Value); } break; case "uom": usagePoint.MeterReadings.LastOrDefault() .ReadingType.Uom = (Uom)Convert.ToUInt16(e.Value); break; case "scaler": usagePoint.MeterReadings.LastOrDefault() .ReadingType.Scaler = Convert.ToSByte(e.Value); break; case "obisCode": switch (e.Parent.Name.LocalName) { case "TariffStage": usagePoint.AnalysisProfile.TariffStages.LastOrDefault().ObisCode = e.Value; break; case "ReadingType": usagePoint.MeterReadings.LastOrDefault() .ReadingType.ObisCode = e.Value; break; } break; case "qualifiedLogicalName": usagePoint.MeterReadings.LastOrDefault() .ReadingType.QualifiedLogicalName = e.Value; break; case "measurementPeriod": usagePoint.MeterReadings.LastOrDefault().ReadingType.MeasurementPeriod = uint.Parse(e.Value); break; case "IntervalBlock": usagePoint.MeterReadings.LastOrDefault() .IntervalBlocks.Add(new IntervalBlock()); usagePoint.MeterReadings.LastOrDefault() .IntervalBlocks.LastOrDefault() .MeterReading = usagePoint.MeterReadings.LastOrDefault(); break; case "interval": usagePoint.MeterReadings.LastOrDefault() .IntervalBlocks.LastOrDefault() .Interval = new Interval(); break; case "duration": switch (e.Parent.Name.LocalName) { case "interval": usagePoint.MeterReadings.LastOrDefault() .IntervalBlocks.LastOrDefault() .Interval.Duration = Convert.ToUInt32(e.Value); break; case "timePeriod": usagePoint.MeterReadings.LastOrDefault() .IntervalBlocks.LastOrDefault() .IntervalReadings.LastOrDefault() .TimePeriod.Duration = Convert.ToUInt32(e.Value); break; case "billingPeriod": usagePoint.AnalysisProfile.BillingPeriod.Duration = Convert.ToUInt32(e.Value); break; } break; case "start": switch (e.Parent.Name.LocalName) { case "interval": usagePoint.MeterReadings.LastOrDefault() .IntervalBlocks.LastOrDefault() .Interval.Start = Convert.ToDateTime(e.Value); break; case "timePeriod": usagePoint.MeterReadings.LastOrDefault() .IntervalBlocks.LastOrDefault() .IntervalReadings.LastOrDefault() .TimePeriod.Start = Convert.ToDateTime(e.Value); break; case "billingPeriod": usagePoint.AnalysisProfile.BillingPeriod.Start = Convert.ToDateTime(e.Value); break; } break; case "IntervalReading": usagePoint.MeterReadings.LastOrDefault() .IntervalBlocks.LastOrDefault() .IntervalReadings.Add(new IntervalReading()); usagePoint.MeterReadings.LastOrDefault() .IntervalBlocks.LastOrDefault() .IntervalReadings.LastOrDefault() .IntervalBlock = usagePoint.MeterReadings.LastOrDefault() .IntervalBlocks.LastOrDefault(); break; case "value": usagePoint.MeterReadings.LastOrDefault() .IntervalBlocks.LastOrDefault() .IntervalReadings.LastOrDefault().Value = Convert.ToInt64(e.Value); break; case "timePeriod": usagePoint.MeterReadings.LastOrDefault() .IntervalBlocks.LastOrDefault() .IntervalReadings.LastOrDefault().TimePeriod = new Interval(); break; case "targetTime": usagePoint.MeterReadings.LastOrDefault() .IntervalBlocks.LastOrDefault() .IntervalReadings.LastOrDefault().TargetTime = Convert.ToDateTime(e.Value); usagePoint.MeterReadings.LastOrDefault().IsTargetTimeUsed = true; break; case "signature": usagePoint.MeterReadings.LastOrDefault() .IntervalBlocks.LastOrDefault() .IntervalReadings.LastOrDefault().Signature = e.Value; break; case "meterSig": usagePoint.MeterReadings.LastOrDefault() .IntervalBlocks.LastOrDefault() .IntervalReadings.LastOrDefault().Signature = e.Value; break; case "statusFNN": if (e.Value.ValidateHexString()) { usagePoint.MeterReadings.LastOrDefault() .IntervalBlocks.LastOrDefault() .IntervalReadings.LastOrDefault().StatusFNN = new StatusFNN(e.Value); } else { exceptions.Add(new InvalidOperationException("Das Element \"statusFNN\" enthält einen ungültigen Wert.")); } break; case "statusPTB": usagePoint.MeterReadings.LastOrDefault() .IntervalBlocks.LastOrDefault() .IntervalReadings.LastOrDefault().StatusPTB = (StatusPTB)Convert.ToByte(e.Value); break; case "AnalysisProfile": usagePoint.AnalysisProfile = new AnalysisProfile(); break; case "tariffUseCase": usagePoint.AnalysisProfile.TariffUseCase = (TafId)Convert.ToUInt16(e.Value); break; case "tariffId": usagePoint.AnalysisProfile.TariffId = e.Value; break; case "defaultTariffNumber": usagePoint.AnalysisProfile.DefaultTariffNumber = Convert.ToUInt16(e.Value); break; case "billingPeriod": usagePoint.AnalysisProfile.BillingPeriod = new Interval(); break; case "TariffStage": usagePoint.AnalysisProfile.TariffStages.Add(new TariffStage()); break; case "tariffNumber": if (e.Parent.Name.LocalName == "TariffStage") { usagePoint.AnalysisProfile.TariffStages.LastOrDefault().TariffNumber = Convert.ToUInt16(e.Value); } else if (e.Parent.Name.LocalName == "DayTimeProfile") { usagePoint.AnalysisProfile.TariffChangeTrigger.TimeTrigger.DayProfiles.LastOrDefault() .DayTimeProfiles.LastOrDefault().TariffNumber = Convert.ToUInt16(e.Value); } break; case "description": usagePoint.AnalysisProfile.TariffStages.LastOrDefault().Description = e.Value; break; case "TariffChangeTrigger": usagePoint.AnalysisProfile.TariffChangeTrigger = new TariffChangeTrigger(); break; case "TimeTrigger": usagePoint.AnalysisProfile.TariffChangeTrigger.TimeTrigger = new TimeTrigger(); break; case "DayProfile": usagePoint.AnalysisProfile.TariffChangeTrigger.TimeTrigger.DayProfiles.Add(new DayProfile()); dayIdAlreadyExists = false; break; case "dayId": if (e.Parent.Name.LocalName == "DayProfile") { if (dayIdAlreadyExists) { exceptions.Add(new InvalidOperationException("Es ist nur ein Element \"dayId\" innerhalb des Elements \"DayProfile\" erlaubt.")); } else { usagePoint.AnalysisProfile.TariffChangeTrigger.TimeTrigger.DayProfiles.LastOrDefault() .DayId = Convert.ToUInt16(e.Value); dayIdAlreadyExists = true; } } else if (e.Parent.Name.LocalName == "SpecialDayProfile") { var id = Convert.ToUInt16(e.Value); usagePoint.AnalysisProfile.TariffChangeTrigger.TimeTrigger.SpecialDayProfiles .LastOrDefault().DayId = id; try { usagePoint.AnalysisProfile.TariffChangeTrigger.TimeTrigger.SpecialDayProfiles .LastOrDefault().DayProfile = usagePoint.AnalysisProfile.TariffChangeTrigger.TimeTrigger .DayProfiles.Single(dp => dp.DayId == id); } catch (Exception ex) { exceptions.Add(ex); } } break; case "DayTimeProfile": usagePoint.AnalysisProfile.TariffChangeTrigger.TimeTrigger.DayProfiles.LastOrDefault() .DayTimeProfiles.Add(new DayTimeProfile()); usagePoint.AnalysisProfile.TariffChangeTrigger.TimeTrigger.DayProfiles.LastOrDefault() .DayTimeProfiles.LastOrDefault().DayProfile = usagePoint.AnalysisProfile .TariffChangeTrigger.TimeTrigger.DayProfiles.LastOrDefault(); break; case "startTime": usagePoint.AnalysisProfile.TariffChangeTrigger.TimeTrigger.DayProfiles.LastOrDefault() .DayTimeProfiles.LastOrDefault().StartTime = new TimeVarType(); break; case "hour": usagePoint.AnalysisProfile.TariffChangeTrigger.TimeTrigger.DayProfiles.LastOrDefault() .DayTimeProfiles.LastOrDefault().StartTime.Hour = Convert.ToByte(e.Value); break; case "minute": usagePoint.AnalysisProfile.TariffChangeTrigger.TimeTrigger.DayProfiles.LastOrDefault() .DayTimeProfiles.LastOrDefault().StartTime.Minute = Convert.ToByte(e.Value); break; case "second": usagePoint.AnalysisProfile.TariffChangeTrigger.TimeTrigger.DayProfiles.LastOrDefault() .DayTimeProfiles.LastOrDefault().StartTime.Second = Convert.ToByte(e.Value); break; case "hundreds": usagePoint.AnalysisProfile.TariffChangeTrigger.TimeTrigger.DayProfiles.LastOrDefault() .DayTimeProfiles.LastOrDefault().StartTime.Hundreds = Convert.ToByte(e.Value); break; case "SpecialDayProfile": usagePoint.AnalysisProfile.TariffChangeTrigger.TimeTrigger.SpecialDayProfiles.Add(new SpecialDayProfile()); break; case "specialDayDate": usagePoint.AnalysisProfile.TariffChangeTrigger.TimeTrigger.SpecialDayProfiles.LastOrDefault() .SpecialDayDate = new DayVarType(); break; case "year": usagePoint.AnalysisProfile.TariffChangeTrigger.TimeTrigger.SpecialDayProfiles.LastOrDefault() .SpecialDayDate.Year = Convert.ToUInt16(e.Value); break; case "month": usagePoint.AnalysisProfile.TariffChangeTrigger.TimeTrigger.SpecialDayProfiles.LastOrDefault() .SpecialDayDate.Month = Convert.ToByte(e.Value); break; case "day_of_month": usagePoint.AnalysisProfile.TariffChangeTrigger.TimeTrigger.SpecialDayProfiles.LastOrDefault() .SpecialDayDate.DayOfMonth = Convert.ToByte(e.Value); break; case "FirmwareComponent": usagePoint.Smgw.FirmwareComponents.Add(new FirmwareComponent()); break; case "name": usagePoint.Smgw.FirmwareComponents.Last().Name = e.Value; break; case "version": usagePoint.Smgw.FirmwareComponents.Last().Version = e.Value; break; case "checksum": usagePoint.Smgw.FirmwareComponents.Last().Checksum = e.Value; break; case "FirmwareVersion": usagePoint.Smgw.FirmwareVersion = e.Value; break; case "VendorConfig": case "tafProfile": case "statusVendor": case "measurementTimeMeter": // Ignored here: not displayed by TRuDI. break; // LogEvent case "type": case "eventId": case "vendorId": case "eventSubId": case "subjectIdentity": case "userIdentity": case "eventKindVendor": case "secondsIndex": case "evidence": break; default: if (usagePoint == null) { throw new InvalidOperationException("Keine gültige Datei."); } exceptions.Add(new InvalidOperationException($"Das Element \"{e.Name.LocalName}\" wird nicht unterstützt.")); break; } } if (exceptions.Any()) { throw new AggregateException("Han Adapter Parsing error:>", exceptions); } foreach (var meterReading in usagePoint.MeterReadings) { meterReading.IntervalBlocks.Sort((a, b) => a.Interval.Start.ToUniversalTime().CompareTo(b.Interval.Start.ToUniversalTime())); var measurementPeriod = (int)meterReading.GetMeasurementPeriod().TotalSeconds; foreach (var block in meterReading.IntervalBlocks) { block.IntervalReadings.Sort((a, b) => { if (a.TargetTime != null && b.TargetTime != null) { return(a.TargetTime.Value.ToUniversalTime() .CompareTo(b.TargetTime.Value.ToUniversalTime())); } return(a.TimePeriod.Start.ToUniversalTime() .CompareTo(b.TimePeriod.Start.ToUniversalTime())); }); if (meterReading.IsOriginalValueList()) { foreach (var ir in block.IntervalReadings) { if (ir.TargetTime == null) { if (measurementPeriod > 0) { ir.TargetTime = ModelExtensions.GetAlignedTimestamp( ir.TimePeriod.Start, measurementPeriod, 3); } else { ir.TargetTime = ir.TimePeriod.Start; } } } } } } if (usagePoint?.LogEntries != null && usagePoint.LogEntries.Any()) { usagePoint.LogEntries.Sort((a, b) => { if (a.RecordNumber != null && b.RecordNumber != null) { return(a.RecordNumber.Value.CompareTo(b.RecordNumber.Value)); } return(a.LogEvent.Timestamp.ToUniversalTime() .CompareTo(b.LogEvent.Timestamp.ToUniversalTime())); }); } return(usagePoint); }
/// <inheritdoc /> /// <summary> /// Calculates the derived registers for Taf2. /// </summary> /// <param name="device">Data from the SMGW. There should be just original value lists.</param> /// <param name="supplier">The calculation data from the supplier.</param> /// <returns>An ITaf2Data instance. The object contains the calculated data.</returns> public TafAdapterData Calculate(UsagePointAdapterTRuDI device, UsagePointLieferant supplier) { this.originalValueLists = device.MeterReadings.Where(mr => mr.IsOriginalValueList()).Select(mr => new OriginalValueList(mr, device.ServiceCategory.Kind ?? Kind.Electricity)).ToList(); if (!this.originalValueLists.Any()) { throw new InvalidOperationException("Es ist keine originäre Messwertliste verfügbar."); } var registers = supplier.GetRegister(); this.UpdateReadingTypeFromOriginalValueList(registers); var accountingPeriod = new Taf2Data(registers, supplier.AnalysisProfile.TariffStages); var dayProfiles = supplier.AnalysisProfile.TariffChangeTrigger.TimeTrigger.DayProfiles; foreach (var ovl in this.originalValueLists) { var bpStart = ovl.MeterReading.GetFirstReadingTimestamp(supplier.AnalysisProfile.BillingPeriod.Start, supplier.AnalysisProfile.BillingPeriod.GetEnd()); var bpEnd = ovl.MeterReading.GetLastReadingTimestamp(supplier.AnalysisProfile.BillingPeriod.Start, supplier.AnalysisProfile.BillingPeriod.GetEnd()); if (bpEnd == null || bpStart == null || bpStart == bpEnd) { throw new InvalidOperationException("Keine Messwerte innerhalb des Abbrechnungszeitraums gefunden."); } accountingPeriod.SetDates(bpStart.Value, bpEnd.Value); this.SetTotalBillingPeriod(accountingPeriod); var validDayProfiles = dayProfiles.GetValidDayProfilesForMeterReading(ovl.Obis, supplier.AnalysisProfile.TariffStages); var specialDayProfiles = this.GetSpecialDayProfiles(supplier, validDayProfiles, accountingPeriod.Begin, accountingPeriod.End); long latestReading = 0; ushort latestTariffId = 63; // Check if the lists validDayProfiles and special DayProfiles are null or empty if (!this.CheckUsedLists(validDayProfiles, specialDayProfiles)) { continue; } // Calculation of the single days (latestReading and latestTariffId are needed for days across gap detection) foreach (SpecialDayProfile profile in specialDayProfiles) { var currentDayData = this.GetDayData(profile, dayProfiles, ovl.MeterReading, supplier, latestReading, latestTariffId); if (!(currentDayData.day.Start == DateTime.MinValue) || currentDayData.day.MeasuringRanges.Count != 0) { accountingPeriod.Add(currentDayData.day); } latestReading = currentDayData.latestReading; latestTariffId = currentDayData.tariffId; } // Set the initial overall meter reading accountingPeriod.AddInitialReading(new Reading() { Amount = accountingPeriod.AccountingSections.First().Reading.Amount, ObisCode = ovl.Obis }); } accountingPeriod.OrderSections(); return(new TafAdapterData(typeof(Taf2SummaryView), typeof(Taf2DetailView), accountingPeriod)); }