Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Read Point from Period and not Series elements #1426

Merged
merged 11 commits into from
Dec 23, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -52,31 +52,48 @@ protected override IIncomingMessageSeries ParseTransaction(JsonElement transacti
var meteringPointLocationId = transactionElement.GetProperty(MeteringPointIdentificationElementName)
.GetProperty(ValueElementName)
.ToString();

var meteringPointType =
transactionElement.GetProperty(MeteringPointTypeElementName).GetProperty(ValueElementName).ToString();

var registrationDateAndOrTime =
transactionElement.GetProperty(RegistrationDateAndOrTimeElementName).ToString();

var productNumber =
transactionElement.GetProperty(ProductNumberElementName).ToString();

var productUnitType =
transactionElement.GetProperty(ProductUnitTypeElementName).GetProperty(ValueElementName).ToString();

var periodElement = transactionElement.GetProperty(PeriodElementName);
var resolution = periodElement.GetProperty(ResolutionElementName).ToString();
var startDateAndOrTimeDateTime = periodElement.GetProperty(TimeIntervalElementName).GetProperty(StartElementName).GetProperty(ValueElementName).ToString();
var endDateAndOrTimeDateTime = periodElement.GetProperty(TimeIntervalElementName).GetProperty(EndElementName).GetProperty(ValueElementName).ToString();

var energyObservations = new List<EnergyObservation>();
JsonElement? pointsElements = transactionElement.TryGetProperty(PointElementName, out var pointsElement)
JsonElement? pointsElements = periodElement.TryGetProperty(PointElementName, out var pointsElement)
? pointsElement
: null;

if (pointsElements != null)
{
foreach (var pointElement in pointsElements.Value.EnumerateArray())
{
energyObservations.Add(new EnergyObservation(
pointElement.GetProperty(PositionElementName).GetProperty(ValueElementName).ToString(),
pointElement.GetProperty(QualityElementName).GetProperty(ValueElementName).ToString(),
pointElement.GetProperty(QuantityElementName).ToString()));
var position = pointElement.GetProperty(PositionElementName).GetProperty(ValueElementName).ToString();
string? quality = null;
string? quantity = null;

if (pointElement.TryGetProperty(QualityElementName, out var qualityElement))
{
quality = qualityElement.GetProperty(ValueElementName).ToString();
}

if (pointElement.TryGetProperty(QuantityElementName, out var quantityElement))
{
quantity = quantityElement.ToString();
}

energyObservations.Add(new EnergyObservation(position, quantity, quality));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

using System.Collections.ObjectModel;
using System.Globalization;
using System.Text;
using System.Xml;
using Energinet.DataHub.EDI.BuildingBlocks.Domain.Models;
Expand Down Expand Up @@ -62,6 +62,19 @@ public static IncomingMarketMessageStream CreateIncomingMessage(
receiverNumber,
schema ?? "urn:ediel.org:measure:notifyvalidatedmeasuredata:0:1");
}
else if (format == DocumentFormat.Json)
{
content = GetJson(
senderActorNumber,
series,
messageType,
processType,
businessType,
messageId,
senderRole,
receiverNumber,
schema ?? "NotifyValidatedMeasureData_MarketDocument");
}
else
{
throw new ArgumentOutOfRangeException(nameof(format), format, "Unsupported document format");
Expand Down Expand Up @@ -119,13 +132,7 @@ private static string GetEbix(
<ns0:MeteringPointDomainLocation>
<ns0:Identification schemeAgencyIdentifier=""9"">571313000000002000</ns0:Identification>
</ns0:MeteringPointDomainLocation>
{string.Join("\n", GetEnergyObservations(s.Resolution).Select(e => $@"
<ns0:IntervalEnergyObservation>
<ns0:Position>{e.Position}</ns0:Position>
<ns0:EnergyQuantity>{e.Quantity}</ns0:EnergyQuantity>
<ns0:QuantityQuality listAgencyIdentifier=""260"">E01</ns0:QuantityQuality>
</ns0:IntervalEnergyObservation>
"))}
{EnergyObservationEbixBuilder(GetEnergyObservations())}
</ns0:PayloadEnergyTimeSeries>
"))}
</ns0:DK_MeteredDataTimeSeries>");
Expand Down Expand Up @@ -176,30 +183,186 @@ private static string GetXml(
<cim:end>{s.PeriodEnd.ToString("yyyy-MM-ddTHH:mm'Z'", null)}</cim:end>
</cim:timeInterval>

{string.Join("\n", GetEnergyObservations(s.Resolution).Select(e => $@"
<cim:Point>
<cim:position>{e.Position}</cim:position>
<cim:quantity>{e.Quantity}</cim:quantity>
<cim:quality>A03</cim:quality>
</cim:Point>
"))}
{EnergyObservationXmlBuilder(GetEnergyObservations())}
</cim:Period>
</cim:Series>
"))}
</cim:NotifyValidatedMeasureData_MarketDocument>");
return doc.OuterXml;
}

private static ReadOnlyCollection<(int Position, int Quantity)> GetEnergyObservations(Resolution resolution)
private static string GetJson(
ActorNumber senderActorNumber,
IReadOnlyCollection<(string TransactionId, Instant PeriodStart, Instant PeriodEnd, Resolution Resolution)>
series,
string messageType,
string processType,
string businessType,
string messageId,
string senderRole,
string receiverNumber,
string schema) =>
$$"""
{
"{{schema}}": {
"mRID": "{{messageId}}",
"businessSector.type": {
"value": "{{businessType}}"
},
"createdDateTime": "2022-12-17T09:30:47Z",
"process.processType": {
"value": "{{processType}}"
},
"receiver_MarketParticipant.mRID": {
"codingScheme": "A10",
"value": "{{receiverNumber}}"
},
"receiver_MarketParticipant.marketRole.type": {
"value": "DGL"
},
"sender_MarketParticipant.mRID": {
"codingScheme": "A10",
"value": "{{senderActorNumber.Value}}"
},
"sender_MarketParticipant.marketRole.type": {
"value": "{{senderRole}}"
},
"type": {
"value": "{{messageType}}"
},
"Series": [
{{string.Join(",\n", series.Select(s =>
$$"""
{
"mRID": "{{s.TransactionId}}",
"marketEvaluationPoint.mRID": {
"codingScheme": "A10",
"value": "579999993331812345"
},
"marketEvaluationPoint.type": {
"value": "E17"
},
"originalTransactionIDReference_Series.mRID": "C1875000",
"product": "8716867000030",
"quantity_Measure_Unit.name": {
"value": "KWH"
},
"registration_DateAndOrTime.dateTime": "2022-12-17T07:30:00Z",
"Period": {
"resolution": "{{s.Resolution.Code}}",
"timeInterval": {
"start": {
"value": "{{s.PeriodStart.ToString("yyyy-MM-ddTHH:mm'Z'", null)}}"
},
"end": {
"value": "{{s.PeriodEnd.ToString("yyyy-MM-ddTHH:mm'Z'", null)}}"
}
},
"Point": [
{{EnergyObservationJsonBuilder(GetEnergyObservations())}}
]
}
}
"""))}}
]
}
}
""";

private static IReadOnlyCollection<(int Position, string? Quality, decimal? Quantity)> GetEnergyObservations() =>
[
(1, null, null),
(2, "A03", null),
(3, null, 123.456m),
(4, "A03", 654.321m),
];

private static string EnergyObservationJsonBuilder(
IReadOnlyCollection<(int Position, string? Quality, decimal? Quantity)> observations)
{
return string.Join(
",\n",
observations.Select(
e =>
{
var builder = new StringBuilder();
builder.Append($"{{ \"position\": {{ \"value\": {e.Position} }}");

if (e.Quality != null)
{
builder.Append($", \"quality\": {{ \"value\": \"{e.Quality}\" }}");
}

if (e.Quantity.HasValue)
{
builder.Append($", \"quantity\": {e.Quantity.Value.ToString(CultureInfo.InvariantCulture)}");
}

builder.Append(" }");
return builder.ToString();
}));
}

private static string EnergyObservationXmlBuilder(
IReadOnlyCollection<(int Position, string? Quality, decimal? Quantity)> observations)
{
var observations = new List<(int Position, int Quantity)>();
var intervalsPerDay = resolution == Resolution.QuarterHourly ? 96 : 24;
return string.Join(
"\n",
observations.Select(
e =>
{
var builder = new StringBuilder();
builder.AppendLine("<cim:Point>");
builder.AppendLine($"<cim:position>{e.Position}</cim:position>");

for (var i = 1; i <= intervalsPerDay; i++)
{
observations.Add((i, 1000 + i));
}
if (e.Quantity.HasValue)
{
builder.AppendLine(
$"<cim:quantity>{e.Quantity.Value.ToString(CultureInfo.InvariantCulture)}</cim:quantity>");
}

if (e.Quality != null)
{
builder.AppendLine($"<cim:quality>{e.Quality}</cim:quality>");
}

builder.AppendLine("</cim:Point>");

return builder.ToString();
}));
}

private static string EnergyObservationEbixBuilder(
IReadOnlyCollection<(int Position, string? Quality, decimal? Quantity)> observations)
{
return string.Join(
"\n",
observations.Select(
e =>
{
var builder = new StringBuilder();
builder.AppendLine("<ns0:IntervalEnergyObservation>");
builder.AppendLine($"<ns0:Position>{e.Position}</ns0:Position>");

if (e.Quantity.HasValue)
{
builder.AppendLine(
$"<ns0:EnergyQuantity>{e.Quantity.Value.ToString(CultureInfo.InvariantCulture)}</ns0:EnergyQuantity>");
}
else
{
builder.AppendLine("<ns0:QuantityMissing>true</ns0:QuantityMissing>");
}

if (e.Quality != null)
{
builder.AppendLine(
"<ns0:QuantityQuality listAgencyIdentifier=\"260\">E01</ns0:QuantityQuality>");
}

builder.AppendLine("</ns0:IntervalEnergyObservation>");

return observations.AsReadOnly();
return builder.ToString();
}));
}
}
Loading
Loading